From a46072a5e7dc1a4cf5e95b85449aee51bca88770 Mon Sep 17 00:00:00 2001 From: lingniu Date: Mon, 6 Jul 2026 06:29:33 +0800 Subject: [PATCH] feat(platform): add customer alert action queue --- .../apps/web/src/pages/Quality.tsx | 70 +++++++++++++++ .../apps/web/src/styles/global.css | 86 +++++++++++++++++++ .../apps/web/src/test/App.test.tsx | 14 ++- 3 files changed, 169 insertions(+), 1 deletion(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Quality.tsx b/vehicle-data-platform/apps/web/src/pages/Quality.tsx index d60d6b3e..f37bfe46 100644 --- a/vehicle-data-platform/apps/web/src/pages/Quality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Quality.tsx @@ -1447,6 +1447,47 @@ export function Quality({ onClick: copyAlertRecoveryReceipt } ]; + const focusEscalation = focusIssue ? escalationClock(focusIssue) : null; + const customerTodayQueueItems = [ + { + label: '首要车辆', + value: focusIssue?.vehicleLabel || '暂无待处置', + detail: focusIssue + ? `${qualityProtocolLabel(focusIssue.protocol)} / ${qualityIssueLabel(focusIssue.issueType)} / ${focusIssue.actionLabel}` + : '当前没有进入优先队列的车辆,保持实时监控。', + color: focusIssue?.priority === 'P0' ? 'red' as const : focusIssue ? 'orange' as const : 'green' as const, + action: '车辆服务', + disabled: !focusIssue || !focusLookup?.key, + onClick: () => focusIssue && onOpenVehicle(focusLookup?.key ?? '', focusIssue.protocol) + }, + { + label: 'SLA状态', + value: focusEscalation?.status || '暂无', + detail: focusEscalation ? `${focusEscalation.detail} / 截止 ${focusEscalation.deadline}` : '没有待升级告警。', + color: focusEscalation?.color || 'green' as const, + action: '复制交接', + disabled: false, + onClick: copyNotificationHandoff + }, + { + label: '通知对象', + value: primaryPolicy?.target || '责任人待配置', + detail: primaryPolicy ? `${primaryPolicy.name} / ${primaryPolicy.channel}` : '需要配置客户通知对象、渠道和升级策略。', + color: primaryPolicy ? 'blue' as const : 'orange' as const, + action: '通知闭环', + disabled: false, + onClick: () => onOpenNotificationRules?.() + }, + { + label: '恢复验收', + value: primaryPolicy?.acceptanceCriteria ? '有标准' : '待补充', + detail: primaryPolicy?.acceptanceCriteria || '车辆服务状态恢复,实时、轨迹、历史和里程证据可查询。', + color: primaryPolicy?.acceptanceCriteria ? 'green' as const : 'orange' as const, + action: '复制回执', + disabled: false, + onClick: copyAlertRecoveryReceipt + } + ]; return (
@@ -1487,6 +1528,35 @@ export function Quality({ ))}
+
+
+ + 今日客户处置队列 + 0 ? 'red' : priorityP1Count > 0 ? 'orange' : 'green'}> + P0 {priorityP0Count.toLocaleString()} / P1 {priorityP1Count.toLocaleString()} + + {focusEscalation?.status || '暂无升级'} + + 把待通知车辆压缩成客户能执行的首要动作:先处理 P0 焦点车,再按 SLA 升级、证据复核和恢复验收闭环。 +
+
+ {customerTodayQueueItems.map((item) => ( + + ))} +
+
diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css index ab9aa81c..8ff4f8eb 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -8500,6 +8500,90 @@ button.vp-realtime-command-item:focus-visible { word-break: break-word; } +.vp-alert-today-queue { + margin-bottom: 16px; + border: 1px solid rgba(37, 99, 235, 0.18); + border-radius: var(--vp-radius); + background: #fff; + display: grid; + grid-template-columns: minmax(280px, 0.44fr) minmax(0, 1.56fr); + overflow: hidden; + box-shadow: var(--vp-shadow-sm); +} + +.vp-alert-today-queue-summary { + padding: 18px; + border-right: 1px solid var(--vp-border); + background: #eff6ff; + display: grid; + gap: 10px; + align-content: center; +} + +.vp-alert-today-queue-summary strong { + color: var(--vp-text); + font-size: 18px; + line-height: 26px; + word-break: break-word; +} + +.vp-alert-today-queue-grid { + padding: 14px; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; +} + +.vp-alert-today-queue-item { + min-height: 138px; + padding: 12px; + border: 1px solid rgba(37, 99, 235, 0.16); + border-radius: 8px; + background: #fbfdff; + color: inherit; + cursor: pointer; + font: inherit; + text-align: left; + display: grid; + gap: 8px; + align-content: start; +} + +.vp-alert-today-queue-item:hover, +.vp-alert-today-queue-item:focus-visible { + border-color: rgba(37, 99, 235, 0.42); + background: #eff6ff; + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.08); + outline: none; +} + +.vp-alert-today-queue-item:disabled { + cursor: not-allowed; + opacity: 0.68; +} + +.vp-alert-today-queue-item strong { + color: var(--vp-text); + font-size: 17px; + line-height: 24px; + font-weight: 800; + word-break: break-word; +} + +.vp-alert-today-queue-item span { + color: var(--vp-text-muted); + font-size: 12px; + line-height: 18px; + word-break: break-word; +} + +.vp-alert-today-queue-item em { + color: var(--vp-accent); + font-size: 12px; + font-style: normal; + font-weight: 700; +} + .vp-alert-action-conclusion { margin-top: 16px; margin-bottom: 16px; @@ -15316,6 +15400,8 @@ button.vp-realtime-command-item:focus-visible { .vp-alert-flow, .vp-alert-closure-workbench, .vp-alert-closure-workbench-grid, + .vp-alert-today-queue, + .vp-alert-today-queue-grid, .vp-alert-action-conclusion, .vp-alert-action-conclusion-grid, .vp-alert-customer-service-desk, 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 950b63bf..915aaedb 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -5044,6 +5044,12 @@ test('shows alert rule and notification policy workspace on quality page', async expect(screen.getByRole('button', { name: '客户告警闭环时间线 5 恢复验收 有标准 复制回执' })).toBeInTheDocument(); expect(screen.getByText('客户告警闭环台')).toBeInTheDocument(); expect(screen.getByText('先判断影响车辆,再通知责任人,随后打开实时、轨迹、原始记录完成处置,最后按恢复标准验收。')).toBeInTheDocument(); + expect(screen.getByText('今日客户处置队列')).toBeInTheDocument(); + expect(screen.getByText('把待通知车辆压缩成客户能执行的首要动作:先处理 P0 焦点车,再按 SLA 升级、证据复核和恢复验收闭环。')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '今日客户处置队列 首要车辆 暂无待处置 车辆服务' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '今日客户处置队列 SLA状态 暂无 复制交接' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '今日客户处置队列 通知对象 接入运维 + 业务责任人 通知闭环' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '今日客户处置队列 恢复验收 有标准 复制回执' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户告警闭环台 1 影响车辆 3 辆' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户告警闭环台 2 通知升级 P0 实时中断' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户告警闭环台 3 处置证据 实时/轨迹/原始记录' })).toBeInTheDocument(); @@ -5227,6 +5233,12 @@ test('shows actionable priority queue on quality page', async () => { render(); expect(await screen.findByText('处置优先队列')).toBeInTheDocument(); + expect(screen.getByText('今日客户处置队列')).toBeInTheDocument(); + expect(screen.getByText('把待通知车辆压缩成客户能执行的首要动作:先处理 P0 焦点车,再按 SLA 升级、证据复核和恢复验收闭环。')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '今日客户处置队列 首要车辆 粤A优先1 / VIN-P0-001 车辆服务' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /今日客户处置队列 SLA状态 .* 复制交接/ })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '今日客户处置队列 通知对象 接入运维 + 业务责任人 通知闭环' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '今日客户处置队列 恢复验收 有标准 复制回执' })).toBeInTheDocument(); expect(screen.getByText('车辆风险处置任务板')).toBeInTheDocument(); expect(screen.getAllByText('粤A优先1 / VIN-P0-001').length).toBeGreaterThan(0); expect(screen.getAllByText('实时确认').length).toBeGreaterThan(0); @@ -6686,7 +6698,7 @@ test('quality page surfaces escalation clock for priority issues', async () => { render(); expect(await screen.findByText('告警升级时钟')).toBeInTheDocument(); - expect(await screen.findByText('超 SLA')).toBeInTheDocument(); + await waitFor(() => expect(screen.getAllByText('超 SLA').length).toBeGreaterThan(0)); expect(screen.getByText('SLA 正常')).toBeInTheDocument(); expect(screen.getByText('VIN-QUALITY-OVERDUE')).toBeInTheDocument(); });