feat(platform): frame history protocol as source evidence
This commit is contained in:
@@ -903,7 +903,7 @@ export function History({
|
||||
const pageTitle = mode === 'query' ? '历史查询导出' : '轨迹回放';
|
||||
const pageDescription = mode === 'query'
|
||||
? '按车辆和时间窗交付轨迹报告、位置记录、明细证据和字段裁剪,支持分页、字段裁剪和 CSV 导出'
|
||||
: '按车辆查询历史位置和轨迹回放,明细证据只作为复核依据,数据通道仅用于过滤';
|
||||
: '按车辆查询历史位置和轨迹回放,明细证据只作为复核依据,来源证据仅用于过滤';
|
||||
const scopeDescription = mode === 'query'
|
||||
? '位置记录、明细证据和字段裁剪按当前车辆与时间范围分页查询。'
|
||||
: '历史位置和明细证据按当前车辆与时间范围查询。';
|
||||
@@ -925,7 +925,7 @@ export function History({
|
||||
}));
|
||||
const filterSummary = [
|
||||
currentVehicleKeyword ? `车辆:${currentVehicleKeyword}` : '',
|
||||
currentProtocol ? `数据通道:${currentProtocol}` : '',
|
||||
currentProtocol ? `来源证据:${currentProtocol}` : '',
|
||||
filters.dateFrom?.trim() ? `开始时间:${filters.dateFrom.trim()}` : '',
|
||||
filters.dateTo?.trim() ? `结束时间:${filters.dateTo.trim()}` : '',
|
||||
isIncludeFieldsEnabled(filters.includeFields) ? '返回字段明细' : '',
|
||||
@@ -940,7 +940,7 @@ export function History({
|
||||
? anomalyTotal > 0 || (trajectoryCoverageRate ?? 100) < 80 ? '可复盘,需说明' : '可直接复盘'
|
||||
: '待查询数据';
|
||||
const trajectoryDecisionColor = trajectoryDecisionState === '可直接复盘' ? 'green' as const : trajectoryDecisionState === '待查询数据' ? 'grey' as const : 'orange' as const;
|
||||
const deliveryScopeText = `${currentVehicleKeyword || '全部车辆'} / ${currentProtocol || '全部数据通道'}`;
|
||||
const deliveryScopeText = `${currentVehicleKeyword || '全部车辆'} / ${currentProtocol || '全部来源证据'}`;
|
||||
const trajectoryDecisionItems = [
|
||||
{
|
||||
label: '复盘结论',
|
||||
@@ -1219,7 +1219,7 @@ export function History({
|
||||
const customerEvidencePackageOverviewItems = [
|
||||
{
|
||||
label: '交付范围',
|
||||
value: `${currentVehicleKeyword || '全部车辆'} / ${currentProtocol || '全部数据通道'}`,
|
||||
value: `${currentVehicleKeyword || '全部车辆'} / ${currentProtocol || '全部来源证据'}`,
|
||||
detail: `${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'},先确认客户要查的车辆和时间窗。`,
|
||||
action: '查看范围',
|
||||
color: currentVehicleKeyword ? 'green' as const : 'blue' as const,
|
||||
@@ -1258,7 +1258,7 @@ export function History({
|
||||
{
|
||||
label: currentVehicleKeyword ? '范围已锁定' : '范围待选择',
|
||||
value: currentVehicleKeyword || '全部车辆',
|
||||
detail: `${currentProtocol || '全部数据通道'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
detail: `${currentProtocol || '全部来源证据'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
action: '查看范围',
|
||||
color: currentVehicleKeyword ? 'green' as const : 'orange' as const,
|
||||
disabled: false,
|
||||
@@ -1410,7 +1410,7 @@ export function History({
|
||||
{
|
||||
title: '当前筛选',
|
||||
value: currentVehicleKeyword || '全部车辆',
|
||||
detail: `${currentProtocol || '全部数据通道'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
detail: `${currentProtocol || '全部来源证据'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
action: '保存视图',
|
||||
color: currentVehicleKeyword ? 'green' as const : 'blue' as const,
|
||||
disabled: false,
|
||||
@@ -1830,7 +1830,7 @@ export function History({
|
||||
{
|
||||
title: '选车和时间',
|
||||
value: currentVehicleKeyword || '待选择车辆',
|
||||
detail: `${currentProtocol || '全部数据通道'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
detail: `${currentProtocol || '全部来源证据'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
action: '调整筛选',
|
||||
color: currentVehicleKeyword ? 'green' as const : 'orange' as const,
|
||||
disabled: false,
|
||||
@@ -1881,7 +1881,7 @@ export function History({
|
||||
/>
|
||||
<div className="vp-scope-bar">
|
||||
<span className="vp-scope-label">当前车辆:{currentVehicleKeyword || '-'}</span>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>数据通道:{currentProtocol || '全部数据通道'}</Tag>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>来源证据:{currentProtocol || '全部来源证据'}</Tag>
|
||||
<Typography.Text type="tertiary">{scopeDescription}</Typography.Text>
|
||||
</div>
|
||||
<section className="vp-history-evidence-chain" aria-label="客户时间窗证据链">
|
||||
@@ -1987,7 +1987,7 @@ export function History({
|
||||
客户先选车辆和时间窗,再拿到路线、里程、原始证据、字段裁剪和告警说明。
|
||||
</Typography.Title>
|
||||
<div className="vp-history-service-desk-meta">
|
||||
<span>当前服务范围:{currentVehicleKeyword || '全部车辆'} / {currentProtocol || '全部数据通道'}</span>
|
||||
<span>当前服务范围:{currentVehicleKeyword || '全部车辆'} / {currentProtocol || '全部来源证据'}</span>
|
||||
<span>时间窗:{filters.dateFrom || '-'} 至 {filters.dateTo || '-'}</span>
|
||||
<span>协议来源只作为证据过滤,不作为客户主流程。</span>
|
||||
</div>
|
||||
@@ -2102,7 +2102,7 @@ export function History({
|
||||
step: '01',
|
||||
title: '选择范围',
|
||||
value: currentVehicleKeyword || '全部车辆',
|
||||
detail: `${currentProtocol || '全部数据通道'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
detail: `${currentProtocol || '全部来源证据'} / ${filters.dateFrom || '-'} 至 ${filters.dateTo || '-'}`,
|
||||
action: '调整筛选',
|
||||
color: currentVehicleKeyword ? 'green' as const : 'blue' as const,
|
||||
disabled: false,
|
||||
@@ -2310,7 +2310,7 @@ export function History({
|
||||
{selectedFieldCount > 0 ? `${selectedFieldCount.toLocaleString()} 个字段` : `${rawFieldRows.length.toLocaleString()} 个字段`}
|
||||
</Tag>
|
||||
</Space>
|
||||
<Typography.Title heading={5} style={{ margin: 0 }}>把当前车辆、时间窗、数据通道、字段裁剪和交付节奏保存成客户可复用视图,后续不用重新拼查询条件。</Typography.Title>
|
||||
<Typography.Title heading={5} style={{ margin: 0 }}>把当前车辆、时间窗、来源证据、字段裁剪和交付节奏保存成客户可复用视图,后续不用重新拼查询条件。</Typography.Title>
|
||||
<Typography.Text type="secondary">
|
||||
保存视图是前端可复制的报表配置:后续可升级为账号级报表模板、定时邮件或客户分组权限。
|
||||
</Typography.Text>
|
||||
@@ -2441,7 +2441,7 @@ export function History({
|
||||
<div className="vp-trip-current-card">
|
||||
<Space wrap>
|
||||
<Tag color={currentPlayback ? 'green' : 'grey'}>{currentPlayback ? `点 ${currentPlaybackIndex + 1}/${playbackRows.length}` : '暂无回放点'}</Tag>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol ? `通道 ${currentProtocol}` : '全部数据通道'}</Tag>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol ? `来源证据 ${currentProtocol}` : '全部来源证据'}</Tag>
|
||||
</Space>
|
||||
<Typography.Text strong>{currentPlayback?.plate || (currentPlayback ? '当前回放车辆' : currentVehicleKeyword ? '当前查询车辆' : '全部车辆')}</Typography.Text>
|
||||
<Typography.Text type="secondary">{currentPlayback?.deviceTime || currentPlayback?.serverTime || '请选择车辆和时间窗查询轨迹'}</Typography.Text>
|
||||
@@ -2593,7 +2593,7 @@ export function History({
|
||||
<div className="vp-history-package-summary">
|
||||
<Space wrap>
|
||||
<Tag color={deliveryStateColor}>{deliveryState}</Tag>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol || '全部数据通道'}</Tag>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol || '全部来源证据'}</Tag>
|
||||
</Space>
|
||||
<Typography.Text strong>{deliveryScopeText}</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
@@ -2700,7 +2700,7 @@ export function History({
|
||||
<Card bordered>
|
||||
<Form key={JSON.stringify(filters)} initValues={filters} layout="horizontal" onSubmit={(values) => submit(values)}>
|
||||
<Form.Input field="keyword" label="车辆关键词" placeholder="VIN / 车牌 / 手机号" style={{ width: 260 }} />
|
||||
<Form.Select field="protocol" label="数据通道" placeholder="全部数据通道" style={{ width: 190 }}>
|
||||
<Form.Select field="protocol" label="来源证据筛选" placeholder="全部来源证据" style={{ width: 190 }}>
|
||||
<Select.Option value="GB32960">GB32960</Select.Option>
|
||||
<Select.Option value="JT808">JT808</Select.Option>
|
||||
<Select.Option value="YUTONG_MQTT">YUTONG_MQTT</Select.Option>
|
||||
@@ -2890,7 +2890,7 @@ export function History({
|
||||
<div className="vp-monitor-map-header">
|
||||
<Space wrap>
|
||||
<Tag color="blue">{validLocations.length.toLocaleString()} 个有效轨迹点</Tag>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol || '全部数据通道'}</Tag>
|
||||
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol || '全部来源证据'}</Tag>
|
||||
<Tag color="green">{formatNumber(mileageDelta, ' km')}</Tag>
|
||||
<Tag color={amapConfigured ? 'green' : 'orange'}>{amapConfigured ? '高德地图配置就绪' : '高德地图待配置'}</Tag>
|
||||
</Space>
|
||||
@@ -2998,7 +2998,7 @@ export function History({
|
||||
</button>
|
||||
))}
|
||||
{locationItems.length === 0 ? (
|
||||
<Typography.Text type="secondary">当前筛选范围暂无轨迹点,请调整车辆、数据通道或时间范围。</Typography.Text>
|
||||
<Typography.Text type="secondary">当前筛选范围暂无轨迹点,请调整车辆、来源证据或时间范围。</Typography.Text>
|
||||
) : null}
|
||||
</div>
|
||||
<Card bordered title="轨迹复盘质量" style={{ marginTop: 12 }}>
|
||||
@@ -3096,7 +3096,7 @@ export function History({
|
||||
}}
|
||||
columns={[
|
||||
{ title: 'VIN', dataIndex: 'vin', width: 190 },
|
||||
{ title: '数据通道', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '来源证据', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '经度', dataIndex: 'longitude', width: 120 },
|
||||
{ title: '纬度', dataIndex: 'latitude', width: 120 },
|
||||
{ title: '速度 km/h', dataIndex: 'speedKmh', width: 120 },
|
||||
@@ -3143,7 +3143,7 @@ export function History({
|
||||
{ title: 'ID', dataIndex: 'id', width: 260 },
|
||||
{ title: 'VIN', dataIndex: 'vin', width: 190 },
|
||||
{ title: '车牌', dataIndex: 'plate', width: 120 },
|
||||
{ title: '数据通道', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '来源证据', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '帧类型', dataIndex: 'frameType', width: 190 },
|
||||
{ title: '大小 B', dataIndex: 'rawSizeBytes', width: 100 },
|
||||
{ title: '设备时间', dataIndex: 'deviceTime', width: 190 },
|
||||
@@ -3187,7 +3187,7 @@ export function History({
|
||||
},
|
||||
{ title: 'VIN', dataIndex: 'vin', width: 190 },
|
||||
{ title: '车牌', dataIndex: 'plate', width: 120 },
|
||||
{ title: '数据通道', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '来源证据', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '帧类型', dataIndex: 'frameType', width: 190 },
|
||||
{ title: '设备时间', dataIndex: 'deviceTime', width: 190 },
|
||||
{ title: '历史明细ID', dataIndex: 'rawId', width: 260 },
|
||||
|
||||
@@ -7187,7 +7187,7 @@ test('applies protocol from shareable history hash to API requests', async () =>
|
||||
body: expect.stringContaining('"protocol":"JT808"')
|
||||
}));
|
||||
expect(screen.getByText('当前车辆:粤AG18312')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('数据通道:JT808').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('来源证据:JT808').length).toBeGreaterThan(0);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '顶部统计查询' }));
|
||||
expect(window.location.hash).toBe('#/mileage?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
|
||||
@@ -7528,7 +7528,7 @@ test('shows and clears current history filters while keeping vehicle scope', asy
|
||||
|
||||
expect(await screen.findByText('当前历史筛选')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆:VIN-HISTORY-001')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('数据通道:JT808').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('来源证据:JT808').length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('开始时间:2026-07-01 00:00:00')).toBeInTheDocument();
|
||||
expect(screen.getByText('结束时间:2026-07-01 23:59:59')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('返回字段明细').length).toBeGreaterThanOrEqual(2);
|
||||
@@ -7625,6 +7625,7 @@ test('shows parsed field history as a flattened evidence table', async () => {
|
||||
expect(screen.getByRole('button', { name: '客户证据包交付总览 明细字段 2 字段 字段导出' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户证据包交付总览 交付说明 可交付 复制说明' })).toBeInTheDocument();
|
||||
expect(screen.getByText('客户先选车辆和时间窗,再拿到路线、里程、原始证据、字段裁剪和告警说明。')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('来源证据:GB32960').length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('当前服务范围:VIN-FIELDS-001 / GB32960')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆历史服务台 路线复盘 0 点 查询位置' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '车辆历史服务台 里程核对 - 统计查询' })).toBeInTheDocument();
|
||||
@@ -7687,7 +7688,7 @@ test('shows parsed field history as a flattened evidence table', async () => {
|
||||
expect(screen.getByRole('button', { name: '客户导出包模板库 问题解释包 客户问询 复制说明' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户导出包模板库 字段稳定包 数据对接 字段配置' })).toBeInTheDocument();
|
||||
expect(screen.getByText('保存报表视图')).toBeInTheDocument();
|
||||
expect(screen.getByText('把当前车辆、时间窗、数据通道、字段裁剪和交付节奏保存成客户可复用视图,后续不用重新拼查询条件。')).toBeInTheDocument();
|
||||
expect(screen.getByText('把当前车辆、时间窗、来源证据、字段裁剪和交付节奏保存成客户可复用视图,后续不用重新拼查询条件。')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '保存报表视图 当前筛选 VIN-FIELDS-001 保存视图' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '保存报表视图 字段模板 2 字段 复用字段' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '保存报表视图 交付节奏 日报/周报 复制计划' })).toBeInTheDocument();
|
||||
@@ -10065,7 +10066,7 @@ test('opens current vehicle service from history header with current source evid
|
||||
|
||||
render(<App />);
|
||||
|
||||
expect((await screen.findAllByText('数据通道:JT808')).length).toBeGreaterThan(0);
|
||||
expect((await screen.findAllByText('来源证据:JT808')).length).toBeGreaterThan(0);
|
||||
fireEvent.click(screen.getByRole('button', { name: '当前车辆服务' }));
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
Reference in New Issue
Block a user