From eccede509f3a02293bd4badbb027f615b8642378 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 22:02:29 +0800 Subject: [PATCH] feat(platform): add delivery time presets --- .../apps/web/src/pages/Dashboard.tsx | 58 +++++++++++++++++++ .../apps/web/src/styles/global.css | 53 +++++++++++++++++ .../apps/web/src/test/App.test.tsx | 5 ++ 3 files changed, 116 insertions(+) diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index 265e875a..8df75e17 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -125,6 +125,25 @@ function previousDateString(value: string) { return `${year}-${month}-${day}`; } +function offsetDateString(value: string, offsetDays: number) { + const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim()); + if (!match) return value; + const date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]) + offsetDays); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +} + +function weekStartDateString(value: string) { + const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim()); + if (!match) return value; + const date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])); + const day = date.getDay(); + const mondayOffset = day === 0 ? -6 : 1 - day; + return offsetDateString(value, mondayOffset); +} + function dayRangeText(dateFrom?: string, dateTo?: string) { if (!dateFrom && !dateTo) return '未限定时间'; if (!dateFrom || !dateTo) return '单边时间窗'; @@ -2002,6 +2021,29 @@ export function Dashboard({ onClick: openTimeMonitorRaw } ]; + const deliveryToday = timeMonitorScopeValue.dateTo || todayDateString(); + const customerTimeRangePresets = [ + { + label: '最近一天', + range: `${previousDateString(deliveryToday)} 至 ${deliveryToday}`, + filters: { ...timeMonitorScopeValue, dateFrom: previousDateString(deliveryToday), dateTo: deliveryToday } + }, + { + label: '最近三天', + range: `${offsetDateString(deliveryToday, -3)} 至 ${deliveryToday}`, + filters: { ...timeMonitorScopeValue, dateFrom: offsetDateString(deliveryToday, -3), dateTo: deliveryToday } + }, + { + label: '本周', + range: `${weekStartDateString(deliveryToday)} 至 ${deliveryToday}`, + filters: { ...timeMonitorScopeValue, dateFrom: weekStartDateString(deliveryToday), dateTo: deliveryToday } + }, + { + label: '自定义复盘', + range: '当前范围', + filters: timeMonitorScopeValue + } + ]; const deliveryScopeText = `${timeMonitorScopeValue.keyword || '全部车辆'} / ${timeMonitorScopeValue.protocol || '全部数据通道'} / ${timeMonitorScopeValue.dateFrom || '-'} 至 ${timeMonitorScopeValue.dateTo || '-'}`; const customerDeliveryWorkbenchItems = [ { @@ -2140,6 +2182,22 @@ export function Dashboard({ 当前交付范围 {deliveryScopeText} +
+ 常用时间范围 +
+ {customerTimeRangePresets.map((item) => ( + + ))} +
+
{customerDeliveryWorkbenchItems.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 a696c888..98845fc2 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -1425,6 +1425,59 @@ body { word-break: break-word; } +.vp-customer-time-presets { + display: grid; + gap: 8px; +} + +.vp-customer-time-presets > span { + color: var(--vp-text-muted); + font-size: 12px; + line-height: 18px; +} + +.vp-customer-time-presets > div { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.vp-customer-time-presets button { + min-width: 0; + padding: 9px 10px; + border: 1px solid var(--vp-border); + border-radius: 8px; + background: #ffffff; + color: inherit; + cursor: pointer; + font: inherit; + text-align: left; + display: grid; + gap: 3px; + transition: border-color 0.16s ease, box-shadow 0.16s ease, background 0.16s ease; +} + +.vp-customer-time-presets button:hover, +.vp-customer-time-presets button:focus-visible { + border-color: rgba(22, 100, 255, 0.42); + background: #f7fbff; + box-shadow: 0 0 0 3px rgba(22, 100, 255, 0.08); + outline: none; +} + +.vp-customer-time-presets strong { + color: var(--vp-text); + font-size: 13px; + line-height: 18px; +} + +.vp-customer-time-presets small { + color: var(--vp-text-muted); + font-size: 11px; + line-height: 16px; + word-break: break-word; +} + .vp-customer-delivery-workbench-grid { min-width: 0; display: grid; 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 665ac8b3..f4c72257 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -910,6 +910,11 @@ test('dashboard frames history query and export as a customer delivery workbench expect(screen.getByText('先锁定车辆和时间窗,再选择轨迹、里程、历史明细或告警说明,最终交付一份客户能看懂的数据包。')).toBeInTheDocument(); expect(screen.getByText('当前交付范围')).toBeInTheDocument(); expect(screen.getAllByText('全部车辆 / 全部数据通道 / 2026-07-04 至 2026-07-05').length).toBeGreaterThan(0); + expect(screen.getByText('常用时间范围')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户常用时间范围 最近一天 2026-07-04 至 2026-07-05 应用' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户常用时间范围 最近三天 2026-07-02 至 2026-07-05 应用' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户常用时间范围 本周 2026-06-29 至 2026-07-05 应用' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户常用时间范围 自定义复盘 当前范围 应用' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户查询导出交付台 轨迹回放 4 活跃 回放轨迹' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户查询导出交付台 里程统计 1,033 车辆 统计查询' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户查询导出交付台 历史明细 1,286,320 帧 历史导出' })).toBeInTheDocument();