refine Semi UI vehicle directory

This commit is contained in:
lingniu
2026-07-18 06:47:39 +08:00
parent 4bca0482c1
commit 4b3bb3b3dd
8 changed files with 475 additions and 184 deletions

View File

@@ -135,13 +135,18 @@ test('shows deduplicated Semi vehicle candidates and opens the selected VIN', as
plate: initialRealtime.plate,
phone: '13800000000',
oem: '测试品牌',
protocol: 'JT808',
protocols: ['JT808'],
missingProtocols: [],
sourceStatus: [],
sourceCount: 1,
onlineSourceCount: 1,
online: true,
lastSeen: initialRealtime.lastSeen,
locationText: '广东省广州市',
bindingScore: 100
bindingScore: 100,
bindingStatus: 'bound'
};
const vehicles = vi.spyOn(api, 'vehicles').mockResolvedValue({
const vehicles = vi.spyOn(api, 'vehicleCoverage').mockResolvedValue({
items: [candidate, { ...candidate }],
total: 2,
limit: 12,
@@ -154,7 +159,7 @@ test('shows deduplicated Semi vehicle candidates and opens the selected VIN', as
const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);
expect(screen.getByRole('heading', { name: '车辆快速定位', level: 5 })).toBeInTheDocument();
expect(await screen.findByRole('heading', { name: '车辆快速定位', level: 5 })).toBeInTheDocument();
expect(screen.getByLabelText('查车操作')).toHaveClass('v2-workspace-command-bar', 'v2-vehicle-command-bar');
expect(screen.getByRole('heading', { name: '车辆定位', level: 5 }).closest('.semi-card')).toHaveClass('v2-vehicle-query-panel', 'v2-workspace-filter-panel');
const input = screen.getByRole('textbox', { name: '搜索车辆' });
@@ -164,15 +169,16 @@ test('shows deduplicated Semi vehicle candidates and opens the selected VIN', as
await waitFor(() => expect(vehicles).toHaveBeenCalledTimes(1));
const initialQuery = vehicles.mock.calls[0]?.[0];
expect(initialQuery).toBeDefined();
expect(initialQuery!.get('limit')).toBe('12');
expect(screen.getByRole('heading', { name: '最近上报车辆', level: 5 })).toBeInTheDocument();
expect(await screen.findByText('当前显示 1 辆')).toBeInTheDocument();
expect(screen.getAllByRole('listitem', { name: `打开 ${initialRealtime.plate} 车辆档案` })).toHaveLength(1);
expect(initialQuery!.get('limit')).toBe('10');
expect(screen.getByRole('heading', { name: '授权车辆目录', level: 5 })).toBeInTheDocument();
expect(await screen.findByText('本页 1 辆')).toBeInTheDocument();
expect(view.container.querySelector('.v2-vehicle-directory-table.semi-table-wrapper')).toBeInTheDocument();
expect(screen.getByRole('button', { name: `打开 ${initialRealtime.vin} 车辆档案` })).toBeInTheDocument();
fireEvent.focus(input);
expect(input).toHaveAttribute('aria-expanded', 'true');
fireEvent.change(input, { target: { value: initialRealtime.plate } });
await waitFor(() => expect(vehicles).toHaveBeenCalledTimes(2));
expect(screen.getByRole('heading', { name: '匹配车辆快捷入口', level: 5 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '匹配车辆', level: 5 })).toBeInTheDocument();
const option = await screen.findByRole('option', { name: `${initialRealtime.plate} ${initialRealtime.vin} JT808 选择` });
expect(view.container.querySelector('#v2-vehicle-search-options.v2-vehicle-candidate-list')).toBeInTheDocument();
expect(view.container.querySelector('#v2-vehicle-search-options')).toHaveAttribute('aria-busy', 'false');
@@ -196,19 +202,25 @@ test('defaults to a compact mobile vehicle directory and keeps eight recent vehi
plate: `粤A${String(index).padStart(5, '0')}`,
phone: '',
oem: '测试品牌',
protocol: index % 2 ? 'JT808' : 'GB32960',
protocols: [index % 2 ? 'JT808' : 'GB32960'],
missingProtocols: [],
sourceStatus: [],
sourceCount: 1,
onlineSourceCount: index < 6 ? 1 : 0,
online: index < 6,
lastSeen: `2026-07-16 10:00:${String(index).padStart(2, '0')}`,
locationText: '广东省广州市',
bindingScore: 100
bindingScore: 100,
bindingStatus: 'bound'
}));
vi.spyOn(api, 'vehicles').mockResolvedValue({ items, total: 10, limit: 12, offset: 0 });
vi.spyOn(api, 'vehicleCoverage').mockResolvedValue({ items, total: 10, limit: 8, offset: 0 });
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);
expect(await screen.findByText('当前显示 8 辆')).toBeInTheDocument();
expect(await screen.findByText('本页 8 辆')).toBeInTheDocument();
expect(screen.getAllByRole('listitem', { name: /打开 .* 车辆档案/ })).toHaveLength(8);
expect(view.container.querySelector('.v2-vehicle-directory-table')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-vehicle-query-panel')).toHaveClass('is-mobile-collapsed');
const toggle = screen.getByRole('button', { name: '修改车辆定位10 辆授权车辆' });
expect(toggle).toHaveAttribute('aria-expanded', 'false');
@@ -217,6 +229,50 @@ test('defaults to a compact mobile vehicle directory and keeps eight recent vehi
expect(screen.getByRole('textbox', { name: '搜索车辆' })).toBeVisible();
});
test('paginates the complete desktop authorized vehicle directory', async () => {
const firstPage = Array.from({ length: 10 }, (_, index) => ({
vin: `LTEST${String(index).padStart(12, '0')}`,
plate: `粤A${String(index).padStart(5, '0')}`,
phone: '',
oem: '测试品牌',
protocols: [index % 2 ? 'JT808' : 'GB32960'],
missingProtocols: [],
sourceStatus: [],
sourceCount: 1,
onlineSourceCount: index < 6 ? 1 : 0,
online: index < 6,
lastSeen: `2026-07-16 10:00:${String(index).padStart(2, '0')}`,
locationText: '广东省广州市',
bindingScore: 100,
bindingStatus: 'bound'
}));
const lastVehicle = { ...firstPage[0], vin: 'LTEST000000000010', plate: '粤A00010' };
const vehicles = vi.spyOn(api, 'vehicleCoverage').mockImplementation((params) => {
const query = params ?? new URLSearchParams();
return Promise.resolve({
items: query.get('offset') === '10' ? [lastVehicle] : firstPage,
total: 11,
limit: 10,
offset: Number(query.get('offset') ?? 0)
});
});
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);
expect(await screen.findByText('粤A00000')).toBeInTheDocument();
expect(view.container.querySelector('.v2-vehicle-directory-table.semi-table-wrapper')).toBeInTheDocument();
expect(screen.getByText('共 11 辆 · 本页 10 辆')).toBeInTheDocument();
expect(document.querySelector('.v2-table-pagination .semi-page-item-small')).toHaveTextContent('1/2');
fireEvent.click(screen.getByRole('button', { name: 'Next' }));
await waitFor(() => expect(vehicles.mock.calls[vehicles.mock.calls.length - 1]?.[0]?.get('offset')).toBe('10'));
expect(await screen.findByText('粤A00010')).toBeInTheDocument();
expect(screen.getByText('共 11 辆 · 本页 1 辆')).toBeInTheDocument();
expect(document.querySelector('.v2-table-pagination .semi-page-item-small')).toHaveTextContent('2/2');
});
test('keeps the monitor return on the vehicle page and its nested investigation actions', async () => {
vi.spyOn(api, 'vehicleDetail').mockResolvedValue(detail);
vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [initialRealtime], total: 1, limit: 1, offset: 0 });