feat(platform): refine customer service navigation
This commit is contained in:
@@ -20,9 +20,9 @@ export type PageKey = 'dashboard' | 'vehicles' | 'map' | 'realtime' | 'detail' |
|
||||
|
||||
const navGroups = [
|
||||
{
|
||||
title: '车辆监控',
|
||||
title: '实时运营',
|
||||
audience: '客户',
|
||||
description: '实时看车在哪里、是否在线、是否异常',
|
||||
description: '地图看车、在线监控、异常优先',
|
||||
items: [
|
||||
{ itemKey: 'dashboard', text: '运营总览', icon: <IconHome /> },
|
||||
{ itemKey: 'map', text: '实时地图', icon: <IconMapPin /> },
|
||||
@@ -30,22 +30,22 @@ const navGroups = [
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '车辆资产',
|
||||
title: '车辆服务',
|
||||
audience: '客户',
|
||||
description: '按 VIN、车牌、手机号管理车辆资产',
|
||||
description: '按 VIN、车牌、手机号管理车辆',
|
||||
items: [
|
||||
{ itemKey: 'vehicles', text: '车辆中心', icon: <IconServer /> },
|
||||
{ itemKey: 'detail', text: '车辆档案', icon: <IconSetting /> }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '轨迹与统计',
|
||||
title: '轨迹里程',
|
||||
audience: '客户',
|
||||
description: '按时间窗回放轨迹、核对里程、导出数据',
|
||||
description: '回放轨迹、核对里程、查询历史',
|
||||
items: [
|
||||
{ itemKey: 'history', text: '轨迹回放', icon: <IconMapPin /> },
|
||||
{ itemKey: 'mileage', text: '里程统计', icon: <IconBarChartHStroked /> },
|
||||
{ itemKey: 'history-query', text: '历史查询/导出', icon: <IconHistogram /> }
|
||||
{ itemKey: 'history-query', text: '历史数据', icon: <IconHistogram /> }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -58,9 +58,9 @@ const navGroups = [
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '内部运维',
|
||||
title: '接入运维',
|
||||
audience: '内部',
|
||||
description: '客户主流程外查看接入链路和运行质量',
|
||||
description: '客户主流程外查看链路质量',
|
||||
items: [
|
||||
{ itemKey: 'ops-quality', text: '运维质量', icon: <IconPulse /> }
|
||||
]
|
||||
@@ -98,6 +98,13 @@ function alertButtonClassName(linkIssueCount: number | null, activeRuleCount?: n
|
||||
return linkHealthClassName(linkIssueCount);
|
||||
}
|
||||
|
||||
function customerStatusTitle(title?: string) {
|
||||
if (!title) {
|
||||
return '';
|
||||
}
|
||||
return title === '来源不完整' ? '数据通道不完整' : title;
|
||||
}
|
||||
|
||||
export function AppShell({
|
||||
activePage,
|
||||
linkIssueCount,
|
||||
@@ -145,7 +152,7 @@ export function AppShell({
|
||||
{ label: '监控', ariaLabel: '顶部实时监控', page: 'realtime' as const },
|
||||
{ label: '轨迹', ariaLabel: '顶部轨迹回放', page: 'history' as const },
|
||||
{ label: '里程', ariaLabel: '顶部里程统计', page: 'mileage' as const },
|
||||
{ label: '导出', ariaLabel: '顶部数据导出', page: 'history-query' as const },
|
||||
{ label: '历史', ariaLabel: '顶部历史数据', page: 'history-query' as const },
|
||||
{ label: '告警', ariaLabel: '顶部告警事件', page: 'alert-events' as const }
|
||||
];
|
||||
|
||||
@@ -157,7 +164,7 @@ export function AppShell({
|
||||
<span className="vp-brand-mark" />
|
||||
<span className="vp-brand-copy">
|
||||
<span>车辆服务中台</span>
|
||||
<span>地图监控 / 轨迹回放 / 告警通知</span>
|
||||
<span>实时地图 / 轨迹里程 / 告警通知</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="vp-nav-groups">
|
||||
@@ -197,12 +204,12 @@ export function AppShell({
|
||||
{currentVehicleLabel ? <Tag color="grey">{currentVehicleLabel}</Tag> : null}
|
||||
{currentVehicleStatus ? (
|
||||
<Tag color={currentVehicleStatus.severity === 'ok' ? 'green' : currentVehicleStatus.severity === 'error' ? 'red' : 'orange'}>
|
||||
当前车辆:{currentVehicleStatus.title}
|
||||
当前车辆:{customerStatusTitle(currentVehicleStatus.title)}
|
||||
</Tag>
|
||||
) : null}
|
||||
{currentVehicleConsistency ? (
|
||||
<Tag color={currentVehicleConsistency.severity === 'ok' ? 'green' : currentVehicleConsistency.severity === 'error' ? 'red' : 'orange'}>
|
||||
一致性:{currentVehicleConsistency.title || `${currentVehicleConsistency.onlineSourceCount}/${currentVehicleConsistency.sourceCount} 来源`}
|
||||
一致性:{customerStatusTitle(currentVehicleConsistency.title) || `${currentVehicleConsistency.onlineSourceCount}/${currentVehicleConsistency.sourceCount} 数据通道`}
|
||||
</Tag>
|
||||
) : null}
|
||||
<Tag color={amapConfigured ? 'green' : 'orange'}>{amapConfigured ? '高德地图就绪' : '高德地图待配置'}</Tag>
|
||||
|
||||
@@ -418,10 +418,10 @@ export function Dashboard({
|
||||
onClick: () => onOpenMileage({})
|
||||
},
|
||||
{
|
||||
title: '数据导出',
|
||||
title: '历史数据',
|
||||
value: `${formatCount(summary?.frameToday)} 今日数据`,
|
||||
detail: '按车辆、时间和字段裁剪查询历史位置与原始记录。',
|
||||
action: '历史查询/导出',
|
||||
action: '查询导出',
|
||||
onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' })
|
||||
}
|
||||
];
|
||||
@@ -456,9 +456,9 @@ export function Dashboard({
|
||||
},
|
||||
{
|
||||
step: '05',
|
||||
title: '导出交付',
|
||||
title: '查询导出',
|
||||
detail: '导出历史位置、原始记录、字段明细和告警证据。',
|
||||
action: '数据导出',
|
||||
action: '历史数据',
|
||||
onClick: () => openTimeMonitorRaw()
|
||||
}
|
||||
];
|
||||
@@ -751,7 +751,7 @@ export function Dashboard({
|
||||
detail: '按车辆回看位置、速度、里程和断点,用于定位异常复盘。',
|
||||
actions: [
|
||||
{ label: '打开轨迹回放', onClick: () => onOpenHistory() },
|
||||
{ label: '查原始记录', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }
|
||||
{ label: '查历史明细', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -759,7 +759,7 @@ export function Dashboard({
|
||||
value: `${formatCount(summary?.frameToday)} 今日帧`,
|
||||
meta: '字段裁剪与导出',
|
||||
color: 'blue' as const,
|
||||
detail: '查询位置历史、原始记录和字段明细,为车辆服务提供可追溯依据。',
|
||||
detail: '查询位置历史、历史明细和字段明细,为车辆服务提供可追溯依据。',
|
||||
actions: [
|
||||
{ label: '查询历史数据', onClick: () => onOpenHistory({ tab: 'location' }) },
|
||||
{ label: '查询解析字段', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }
|
||||
@@ -1281,7 +1281,7 @@ export function Dashboard({
|
||||
title: '交付数据',
|
||||
value: `${formatCount(summary?.frameToday)} 今日数据`,
|
||||
detail: '按车辆、时间、字段裁剪导出位置历史、原始记录和字段明细。',
|
||||
action: '数据导出',
|
||||
action: '查询导出',
|
||||
color: 'blue' as const,
|
||||
onClick: openTimeMonitorRaw
|
||||
},
|
||||
@@ -1314,7 +1314,7 @@ export function Dashboard({
|
||||
`实时地图:${appURL(buildAppHash({ page: 'map', filters: { online: 'online' } }))}`,
|
||||
`轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`,
|
||||
`里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`,
|
||||
`数据导出:${appURL(buildAppHash({ page: 'history-query', keyword: scope.keyword, protocol: scope.protocol, filters: rawFilters }))}`,
|
||||
`历史数据:${appURL(buildAppHash({ page: 'history-query', keyword: scope.keyword, protocol: scope.protocol, filters: rawFilters }))}`,
|
||||
`告警事件:${appURL(buildAppHash({ page: 'alert-events', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`
|
||||
];
|
||||
copyText(lines.join('\n'), '客户服务说明');
|
||||
@@ -1340,7 +1340,7 @@ export function Dashboard({
|
||||
<Button theme="solid" type="primary" onClick={() => onOpenMap({ online: 'online' })}>打开实时地图</Button>
|
||||
<Button theme="light" type="primary" onClick={() => onOpenHistory()}>轨迹回放</Button>
|
||||
<Button theme="light" type="primary" onClick={() => onOpenMileage({})}>里程统计</Button>
|
||||
<Button theme="light" type="primary" onClick={openTimeMonitorRaw}>历史查询/导出</Button>
|
||||
<Button theme="light" type="primary" onClick={openTimeMonitorRaw}>历史数据</Button>
|
||||
<Button theme="light" onClick={copyCustomerServiceGuide}>复制客户说明</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
@@ -21,18 +21,18 @@ test('renders vehicle platform shell', () => {
|
||||
|
||||
render(<App />);
|
||||
expect(screen.getByText('车辆服务中台')).toBeInTheDocument();
|
||||
expect(screen.getByText('地图监控 / 轨迹回放 / 告警通知')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆监控')).toBeInTheDocument();
|
||||
expect(screen.getByText('实时看车在哪里、是否在线、是否异常')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆资产')).toBeInTheDocument();
|
||||
expect(screen.getByText('按 VIN、车牌、手机号管理车辆资产')).toBeInTheDocument();
|
||||
expect(screen.getByText('轨迹与统计')).toBeInTheDocument();
|
||||
expect(screen.getByText('按时间窗回放轨迹、核对里程、导出数据')).toBeInTheDocument();
|
||||
expect(screen.getByText('实时地图 / 轨迹里程 / 告警通知')).toBeInTheDocument();
|
||||
expect(screen.getByText('实时运营')).toBeInTheDocument();
|
||||
expect(screen.getByText('地图看车、在线监控、异常优先')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('按 VIN、车牌、手机号管理车辆')).toBeInTheDocument();
|
||||
expect(screen.getByText('轨迹里程')).toBeInTheDocument();
|
||||
expect(screen.getByText('回放轨迹、核对里程、查询历史')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('告警通知').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('内部运维')).toBeInTheDocument();
|
||||
expect(screen.getByText('接入运维')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('客户').length).toBeGreaterThanOrEqual(3);
|
||||
expect(screen.getByText('内部')).toBeInTheDocument();
|
||||
expect(screen.getByText('客户主流程外查看接入链路和运行质量')).toBeInTheDocument();
|
||||
expect(screen.getByText('客户主流程外查看链路质量')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('运营总览').length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
@@ -205,7 +205,7 @@ test('exposes AMap operations shortcuts when map key is configured', async () =>
|
||||
expect(window.location.hash).toBe('#/realtime');
|
||||
fireEvent.click(screen.getByRole('button', { name: '顶部轨迹回放' }));
|
||||
expect(window.location.hash.startsWith('#/history')).toBe(true);
|
||||
fireEvent.click(screen.getByRole('button', { name: '顶部数据导出' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '顶部历史数据' }));
|
||||
expect(window.location.hash.startsWith('#/history-query')).toBe(true);
|
||||
expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('tab')).toBe('raw');
|
||||
expect(await screen.findByRole('heading', { name: '历史查询导出' })).toBeInTheDocument();
|
||||
@@ -809,13 +809,13 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
|
||||
expect(screen.getByRole('button', { name: '车辆服务指挥台 数据交付与导出 历史导出' })).toBeInTheDocument();
|
||||
expect(screen.getByText('客户常用服务')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('车辆地图').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('数据导出').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('历史数据').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('车辆服务路径')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆服务路径 定位车辆 车辆地图' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆服务路径 查看状态 实时监控' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆服务路径 复盘轨迹 轨迹回放' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆服务路径 统计里程 里程统计' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆服务路径 导出交付 数据导出' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆服务路径 查询导出 历史数据' })).toBeInTheDocument();
|
||||
expect(screen.getByText('今日车辆服务任务板')).toBeInTheDocument();
|
||||
expect(screen.getByText('先看全域在线')).toBeInTheDocument();
|
||||
expect(screen.getByText('再看重点车辆')).toBeInTheDocument();
|
||||
@@ -927,7 +927,7 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
|
||||
cleanup();
|
||||
|
||||
await renderDashboard();
|
||||
fireEvent.click(screen.getByRole('button', { name: '客户工作流 数据导出' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '客户工作流 历史数据' }));
|
||||
expect(window.location.hash.startsWith('#/history-query')).toBe(true);
|
||||
expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('tab')).toBe('raw');
|
||||
expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
|
||||
@@ -2821,9 +2821,9 @@ test('shows resolved vehicle service status after topbar search', async () => {
|
||||
fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤AG18312' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));
|
||||
|
||||
expect(await screen.findByText('当前车辆:来源不完整')).toBeInTheDocument();
|
||||
expect(await screen.findByText('当前车辆:数据通道不完整')).toBeInTheDocument();
|
||||
expect(screen.getByText('粤AG18312 / LB9A32A24R0LS1426')).toBeInTheDocument();
|
||||
expect(screen.getByText('一致性:来源不完整')).toBeInTheDocument();
|
||||
expect(screen.getByText('一致性:数据通道不完整')).toBeInTheDocument();
|
||||
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicle-service/overview?keyword=%E7%B2%A4AG18312'), undefined);
|
||||
expect(fetchMock).not.toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/resolve'), undefined);
|
||||
});
|
||||
@@ -6121,7 +6121,7 @@ test('shows vehicle context when opening detail from shareable hash', async () =
|
||||
render(<App />);
|
||||
|
||||
expect(await screen.findByText('粤AG18312 / VIN001')).toBeInTheDocument();
|
||||
expect(screen.getByText('当前车辆:来源不完整')).toBeInTheDocument();
|
||||
expect(screen.getByText('当前车辆:数据通道不完整')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('refreshes vehicle context when hash changes to another detail vehicle', async () => {
|
||||
|
||||
Reference in New Issue
Block a user