feat(platform): show unresolved vehicle lookup state
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
import type { QualityIssueRow } from '../api/types';
|
import type { QualityIssueRow } from '../api/types';
|
||||||
import { qualityIssueVehicleLookup } from './vehicleLookup';
|
import { isLikelyVIN, qualityIssueVehicleLookup } from './vehicleLookup';
|
||||||
|
|
||||||
function issue(row: Partial<QualityIssueRow>): QualityIssueRow {
|
function issue(row: Partial<QualityIssueRow>): QualityIssueRow {
|
||||||
return {
|
return {
|
||||||
@@ -39,3 +39,13 @@ describe('qualityIssueVehicleLookup', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isLikelyVIN', () => {
|
||||||
|
test('accepts a 17-character VIN', () => {
|
||||||
|
expect(isLikelyVIN('LA9GG68L2PBAF4773')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects phone-like lookup keys', () => {
|
||||||
|
expect(isLikelyVIN('64646848247')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ export type VehicleLookup = {
|
|||||||
label: string;
|
label: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function isLikelyVIN(value?: string) {
|
||||||
|
return /^[A-HJ-NPR-Z0-9]{17}$/i.test(value?.trim() ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
export function qualityIssueVehicleLookup(row: QualityIssueRow): VehicleLookup {
|
export function qualityIssueVehicleLookup(row: QualityIssueRow): VehicleLookup {
|
||||||
const vin = row.vin?.trim();
|
const vin = row.vin?.trim();
|
||||||
if (vin && vin !== 'unknown') {
|
if (vin && vin !== 'unknown') {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { api } from '../api/client';
|
|||||||
import type { QualityIssueRow, VehicleDetail as VehicleDetailData, VehicleSourceStatus } from '../api/types';
|
import type { QualityIssueRow, VehicleDetail as VehicleDetailData, VehicleSourceStatus } from '../api/types';
|
||||||
import { PageHeader } from '../components/PageHeader';
|
import { PageHeader } from '../components/PageHeader';
|
||||||
import { StatusTag } from '../components/StatusTag';
|
import { StatusTag } from '../components/StatusTag';
|
||||||
|
import { isLikelyVIN } from '../domain/vehicleLookup';
|
||||||
|
|
||||||
type VehicleQuery = {
|
type VehicleQuery = {
|
||||||
keyword: string;
|
keyword: string;
|
||||||
@@ -73,6 +74,8 @@ export function VehicleDetail({
|
|||||||
const summary = detail?.realtimeSummary;
|
const summary = detail?.realtimeSummary;
|
||||||
const latest = detail?.realtime[0];
|
const latest = detail?.realtime[0];
|
||||||
const resolvedVIN = detail?.vin || summary?.vin || identity?.vin || latest?.vin || query.keyword;
|
const resolvedVIN = detail?.vin || summary?.vin || identity?.vin || latest?.vin || query.keyword;
|
||||||
|
const hasResolvedVIN = isLikelyVIN(resolvedVIN);
|
||||||
|
const displayVIN = hasResolvedVIN ? resolvedVIN : '-';
|
||||||
const protocols = useMemo(() => detail?.sources ?? [], [detail?.sources]);
|
const protocols = useMemo(() => detail?.sources ?? [], [detail?.sources]);
|
||||||
const latestRaw = detail?.raw.items[0];
|
const latestRaw = detail?.raw.items[0];
|
||||||
const qualityCount = detail?.quality.total ?? 0;
|
const qualityCount = detail?.quality.total ?? 0;
|
||||||
@@ -98,8 +101,8 @@ export function VehicleDetail({
|
|||||||
<Space>
|
<Space>
|
||||||
<Button icon={<IconSearch />} htmlType="submit" theme="solid" type="primary">查询车辆</Button>
|
<Button icon={<IconSearch />} htmlType="submit" theme="solid" type="primary">查询车辆</Button>
|
||||||
<Button icon={<IconRefresh />} onClick={() => load(query)} loading={loading}>刷新</Button>
|
<Button icon={<IconRefresh />} onClick={() => load(query)} loading={loading}>刷新</Button>
|
||||||
<Button onClick={() => onOpenHistory(resolvedVIN)}>查看历史</Button>
|
<Button disabled={!hasResolvedVIN} onClick={() => onOpenHistory(resolvedVIN)}>查看历史</Button>
|
||||||
<Button onClick={() => onOpenMileage(resolvedVIN)}>查看里程</Button>
|
<Button disabled={!hasResolvedVIN} onClick={() => onOpenMileage(resolvedVIN)}>查看里程</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -110,7 +113,8 @@ export function VehicleDetail({
|
|||||||
row
|
row
|
||||||
data={[
|
data={[
|
||||||
{ key: '查询关键词', value: query.keyword },
|
{ key: '查询关键词', value: query.keyword },
|
||||||
{ key: 'VIN', value: resolvedVIN },
|
{ key: 'VIN', value: displayVIN },
|
||||||
|
{ key: '解析状态', value: <Tag color={hasResolvedVIN ? 'green' : 'orange'}>{hasResolvedVIN ? '已解析 VIN' : '未解析到 VIN'}</Tag> },
|
||||||
{ key: '车牌', value: summary?.plate || identity?.plate || latest?.plate || '-' },
|
{ key: '车牌', value: summary?.plate || identity?.plate || latest?.plate || '-' },
|
||||||
{ key: '手机号', value: summary?.phone || identity?.phone || '-' },
|
{ key: '手机号', value: summary?.phone || identity?.phone || '-' },
|
||||||
{ key: 'OEM', value: summary?.oem || identity?.oem || '-' },
|
{ key: 'OEM', value: summary?.oem || identity?.oem || '-' },
|
||||||
|
|||||||
Reference in New Issue
Block a user