diff --git a/vehicle-data-platform/apps/web/src/domain/vehicleLookup.test.ts b/vehicle-data-platform/apps/web/src/domain/vehicleLookup.test.ts index 864a9b89..15c86dbf 100644 --- a/vehicle-data-platform/apps/web/src/domain/vehicleLookup.test.ts +++ b/vehicle-data-platform/apps/web/src/domain/vehicleLookup.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'vitest'; import type { QualityIssueRow } from '../api/types'; -import { qualityIssueVehicleLookup } from './vehicleLookup'; +import { isLikelyVIN, qualityIssueVehicleLookup } from './vehicleLookup'; function issue(row: Partial): QualityIssueRow { 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); + }); +}); diff --git a/vehicle-data-platform/apps/web/src/domain/vehicleLookup.ts b/vehicle-data-platform/apps/web/src/domain/vehicleLookup.ts index f854eb52..92f9a9ab 100644 --- a/vehicle-data-platform/apps/web/src/domain/vehicleLookup.ts +++ b/vehicle-data-platform/apps/web/src/domain/vehicleLookup.ts @@ -5,6 +5,10 @@ export type VehicleLookup = { label: string; }; +export function isLikelyVIN(value?: string) { + return /^[A-HJ-NPR-Z0-9]{17}$/i.test(value?.trim() ?? ''); +} + export function qualityIssueVehicleLookup(row: QualityIssueRow): VehicleLookup { const vin = row.vin?.trim(); if (vin && vin !== 'unknown') { diff --git a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx index adf9bace..d274ede1 100644 --- a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx +++ b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx @@ -5,6 +5,7 @@ import { api } from '../api/client'; import type { QualityIssueRow, VehicleDetail as VehicleDetailData, VehicleSourceStatus } from '../api/types'; import { PageHeader } from '../components/PageHeader'; import { StatusTag } from '../components/StatusTag'; +import { isLikelyVIN } from '../domain/vehicleLookup'; type VehicleQuery = { keyword: string; @@ -73,6 +74,8 @@ export function VehicleDetail({ const summary = detail?.realtimeSummary; const latest = detail?.realtime[0]; 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 latestRaw = detail?.raw.items[0]; const qualityCount = detail?.quality.total ?? 0; @@ -98,8 +101,8 @@ export function VehicleDetail({ - - + + @@ -110,7 +113,8 @@ export function VehicleDetail({ row data={[ { key: '查询关键词', value: query.keyword }, - { key: 'VIN', value: resolvedVIN }, + { key: 'VIN', value: displayVIN }, + { key: '解析状态', value: {hasResolvedVIN ? '已解析 VIN' : '未解析到 VIN'} }, { key: '车牌', value: summary?.plate || identity?.plate || latest?.plate || '-' }, { key: '手机号', value: summary?.phone || identity?.phone || '-' }, { key: 'OEM', value: summary?.oem || identity?.oem || '-' },