diff --git a/vehicle-data-platform/apps/web/src/layout/AppShell.tsx b/vehicle-data-platform/apps/web/src/layout/AppShell.tsx
index 179f9ca2..be92ceb2 100644
--- a/vehicle-data-platform/apps/web/src/layout/AppShell.tsx
+++ b/vehicle-data-platform/apps/web/src/layout/AppShell.tsx
@@ -75,6 +75,14 @@ const customerPrimaryPath = [
{ label: '告警通知', page: 'alert-events' as const }
];
+const customerTaskPath = [
+ { label: '地图看车', question: '车辆在哪里', page: 'map' as const },
+ { label: '轨迹回放', question: '这段时间怎么跑', page: 'history' as const },
+ { label: '统计查询', question: '里程怎么算', page: 'mileage' as const },
+ { label: '数据导出', question: '证据怎么给客户', page: 'history-query' as const },
+ { label: '告警通知', question: '异常谁处理', page: 'alert-events' as const }
+];
+
function linkHealthClassName(count: number | null) {
if (count == null) {
return '';
@@ -193,6 +201,26 @@ export function AppShell({
))}
+
+
+ 客户任务分发台
+ 输入 VIN / 车牌 / 手机号后,地图、轨迹、统计、导出和告警都围绕同一辆车服务。
+
+
+ {customerTaskPath.map((item) => (
+
+ ))}
+
+
{navGroups.map((group) => (
diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css
index 993dc348..7d356fcd 100644
--- a/vehicle-data-platform/apps/web/src/styles/global.css
+++ b/vehicle-data-platform/apps/web/src/styles/global.css
@@ -126,6 +126,74 @@ body {
outline: none;
}
+.vp-customer-task-dispatch {
+ padding-top: 10px;
+ border-top: 1px solid rgba(22, 100, 255, 0.12);
+ display: grid;
+ gap: 8px;
+}
+
+.vp-customer-task-dispatch-head {
+ display: grid;
+ gap: 3px;
+}
+
+.vp-customer-task-dispatch-head strong {
+ color: var(--vp-text);
+ font-size: 13px;
+ line-height: 18px;
+}
+
+.vp-customer-task-dispatch-head span {
+ color: var(--vp-text-muted);
+ font-size: 11px;
+ line-height: 16px;
+}
+
+.vp-customer-task-dispatch-actions {
+ display: grid;
+ gap: 6px;
+}
+
+.vp-customer-task-dispatch-action {
+ min-height: 42px;
+ padding: 7px 8px;
+ border: 1px solid var(--vp-border);
+ border-radius: 7px;
+ background: #fff;
+ color: var(--vp-text);
+ cursor: pointer;
+ font: inherit;
+ text-align: left;
+ display: grid;
+ gap: 1px;
+}
+
+.vp-customer-task-dispatch-action strong {
+ font-size: 12px;
+ line-height: 17px;
+}
+
+.vp-customer-task-dispatch-action span {
+ color: var(--vp-text-muted);
+ font-size: 11px;
+ line-height: 15px;
+}
+
+.vp-customer-task-dispatch-action:hover,
+.vp-customer-task-dispatch-action:focus-visible,
+.vp-customer-task-dispatch-action-active {
+ border-color: rgba(22, 100, 255, 0.38);
+ background: #ffffff;
+ outline: none;
+}
+
+.vp-customer-task-dispatch-action-active strong,
+.vp-customer-task-dispatch-action:hover strong,
+.vp-customer-task-dispatch-action:focus-visible strong {
+ color: var(--vp-primary);
+}
+
.vp-nav-groups {
display: grid;
gap: 12px;
diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx
index c5caf8af..812c2a1f 100644
--- a/vehicle-data-platform/apps/web/src/test/App.test.tsx
+++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx
@@ -24,6 +24,13 @@ test('renders vehicle platform shell', () => {
expect(screen.getByText('实时地图 / 轨迹统计 / 告警通知')).toBeInTheDocument();
expect(screen.getByText('客户主线')).toBeInTheDocument();
expect(screen.getByText('先看车,再查时间窗,最后导出和通知。')).toBeInTheDocument();
+ expect(screen.getByText('客户任务分发台')).toBeInTheDocument();
+ expect(screen.getByText('输入 VIN / 车牌 / 手机号后,地图、轨迹、统计、导出和告警都围绕同一辆车服务。')).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.getByRole('button', { name: '客户主线 轨迹回放' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '客户主线 统计查询' })).toBeInTheDocument();