feat(platform): add history delivery task board
This commit is contained in:
@@ -1036,6 +1036,84 @@ export function History({
|
||||
</Space>
|
||||
</Card>
|
||||
) : null}
|
||||
<Card bordered title="历史数据交付任务板" style={{ marginTop: 16 }}>
|
||||
<div className="vp-history-delivery-grid">
|
||||
{[
|
||||
{
|
||||
title: '轨迹回放',
|
||||
tag: validLocations.length > 0 ? '可回放' : '待查询',
|
||||
tagColor: validLocations.length > 0 ? 'green' as const : 'orange' as const,
|
||||
value: `${validLocations.length.toLocaleString()} 个有效点`,
|
||||
detail: currentVehicleKeyword
|
||||
? '围绕当前车辆生成可播放轨迹,用于定位复盘和客户问询。'
|
||||
: '先输入 VIN、车牌或手机号,再生成单车轨迹。',
|
||||
primaryText: '播放轨迹',
|
||||
secondaryText: '复制摘要',
|
||||
disabled: validLocations.length === 0,
|
||||
onPrimary: () => openQueryTab('location'),
|
||||
onSecondary: copyTrajectorySummary
|
||||
},
|
||||
{
|
||||
title: '位置导出',
|
||||
tag: locations.total > 0 ? '可交付' : '无数据',
|
||||
tagColor: locations.total > 0 ? 'green' as const : 'grey' as const,
|
||||
value: `${locations.total.toLocaleString()} 条位置`,
|
||||
detail: '导出当前筛选范围的位置历史,支撑客户定位、BI 核对和离线复盘。',
|
||||
primaryText: '导出位置',
|
||||
secondaryText: '车辆服务',
|
||||
disabled: locations.items.length === 0,
|
||||
secondaryDisabled: !currentVehicleKeyword,
|
||||
onPrimary: exportLocations,
|
||||
onSecondary: () => currentVehicleKeyword && onOpenVehicle(currentVehicleKeyword, currentProtocol)
|
||||
},
|
||||
{
|
||||
title: '原始证据',
|
||||
tag: (rawFrames.total ?? 0) > 0 ? '有证据' : '待加载',
|
||||
tagColor: (rawFrames.total ?? 0) > 0 ? 'blue' as const : 'orange' as const,
|
||||
value: `${(rawFrames.total ?? 0).toLocaleString()} 帧`,
|
||||
detail: '保留接入侧原始记录,用于解释位置、里程和字段值的来源。',
|
||||
primaryText: '看原始',
|
||||
secondaryText: '导出RAW',
|
||||
disabled: false,
|
||||
secondaryDisabled: rawFrames.items.length === 0,
|
||||
onPrimary: () => openQueryTab('raw'),
|
||||
onSecondary: exportRawFrames
|
||||
},
|
||||
{
|
||||
title: '字段裁剪',
|
||||
tag: selectedFieldCount > 0 ? `${selectedFieldCount} 个字段` : '全量字段',
|
||||
tagColor: rawFieldRows.length > 0 ? 'green' as const : 'blue' as const,
|
||||
value: `${rawFieldRows.length.toLocaleString()} 个字段`,
|
||||
detail: selectedFieldCount > 0
|
||||
? '按配置字段返回明细,减少接口体积并加快复核。'
|
||||
: '需要字段级核对时,切到字段明细或填写字段裁剪。',
|
||||
primaryText: '字段明细',
|
||||
secondaryText: '导出字段',
|
||||
disabled: false,
|
||||
secondaryDisabled: rawFieldRows.length === 0,
|
||||
onPrimary: () => openQueryTab('fields'),
|
||||
onSecondary: exportRawFields
|
||||
}
|
||||
].map((task) => (
|
||||
<div key={task.title} className="vp-history-delivery-item">
|
||||
<div className="vp-history-delivery-head">
|
||||
<Typography.Text strong>{task.title}</Typography.Text>
|
||||
<Tag color={task.tagColor}>{task.tag}</Tag>
|
||||
</div>
|
||||
<strong>{task.value}</strong>
|
||||
<Typography.Text type="secondary">{task.detail}</Typography.Text>
|
||||
<Space wrap>
|
||||
<Button size="small" theme="solid" type="primary" disabled={task.disabled} onClick={task.onPrimary}>
|
||||
{task.primaryText}
|
||||
</Button>
|
||||
<Button size="small" disabled={task.secondaryDisabled} onClick={task.onSecondary}>
|
||||
{task.secondaryText}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<Card bordered title={mode === 'query' ? '历史数据质量' : '轨迹运营影响'} style={{ marginTop: 16 }}>
|
||||
<div className="vp-trajectory-impact-board">
|
||||
<div className="vp-trajectory-impact-summary">
|
||||
|
||||
@@ -2328,6 +2328,38 @@ button.vp-realtime-command-item:focus-visible {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.vp-history-delivery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.vp-history-delivery-item {
|
||||
min-height: 184px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--vp-border);
|
||||
border-radius: var(--vp-radius);
|
||||
background: #fbfcff;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.vp-history-delivery-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.vp-history-delivery-item strong {
|
||||
color: var(--vp-text);
|
||||
font-size: 20px;
|
||||
line-height: 26px;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.vp-playback-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 260px;
|
||||
@@ -3338,6 +3370,7 @@ button.vp-realtime-command-item:focus-visible {
|
||||
.vp-trajectory-impact-grid,
|
||||
.vp-history-query-workbench,
|
||||
.vp-history-query-grid,
|
||||
.vp-history-delivery-grid,
|
||||
.vp-source-fusion-grid,
|
||||
.vp-source-fusion-facts,
|
||||
.vp-vehicle-map-layout,
|
||||
|
||||
@@ -6488,6 +6488,12 @@ test('shows trajectory playback workspace from history locations', async () => {
|
||||
render(<App />);
|
||||
|
||||
expect(await screen.findByRole('heading', { name: '轨迹回放' })).toBeInTheDocument();
|
||||
expect(screen.getByText('历史数据交付任务板')).toBeInTheDocument();
|
||||
expect(screen.getByText('2 个有效点')).toBeInTheDocument();
|
||||
expect(screen.getByText('围绕当前车辆生成可播放轨迹,用于定位复盘和客户问询。')).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.getByText('定位覆盖')).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user