From 6049d8c22b15a6dea2a6da9180f9167ca5f9b470 Mon Sep 17 00:00:00 2001 From: lingniu Date: Thu, 16 Jul 2026 08:10:05 +0800 Subject: [PATCH] fix(access): hide unresolved identity queue from viewers --- .../apps/web/src/v2/pages/AccessPage.test.tsx | 2 ++ .../apps/web/src/v2/pages/AccessPage.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.test.tsx index 1fd5c52b..9eab27b5 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.test.tsx @@ -87,11 +87,13 @@ test('does not request or render admin-only thresholds for a read-only session', render(); expect(await screen.findByText('粤A00001')).toBeInTheDocument(); + expect(mocks.accessUnresolvedIdentities).not.toHaveBeenCalled(); expect(mocks.accessThresholds).not.toHaveBeenCalled(); expect(screen.queryByText(/在线判定阈值/)).not.toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: /刷新/ })); await waitFor(() => expect(mocks.accessSummary).toHaveBeenCalledTimes(2)); + expect(mocks.accessUnresolvedIdentities).not.toHaveBeenCalled(); expect(mocks.accessThresholds).not.toHaveBeenCalled(); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); }); diff --git a/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx index b928afa4..c19203f4 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx @@ -103,7 +103,7 @@ export default function AccessPage() { const vehicleScope = useMemo(() => queryScopeKey(baseQuery), [baseQuery]); const summaryQuery = useQuery({ queryKey: ['access-summary'], queryFn: ({ signal }) => api.accessSummary({}, signal), staleTime: 15_000, gcTime: QUERY_MEMORY.summaryGcTime }); const vehiclesQuery = useQuery>({ queryKey: ['access-vehicles', vehicleScope, limit, offset], queryFn: ({ signal }) => api.accessVehicles({ ...baseQuery, limit, offset }, signal), placeholderData: retainPreviousPageWithinScope>(vehicleScope), gcTime: QUERY_MEMORY.highVolumeGcTime }); - const unresolvedQuery = useQuery({ queryKey: ['access-unresolved-identities', criteria.keyword, criteria.protocol], queryFn: ({ signal }) => api.accessUnresolvedIdentities({ keyword: criteria.keyword || undefined, protocol: criteria.protocol || undefined, limit: 20, offset: 0 }, signal), staleTime: 15_000, gcTime: QUERY_MEMORY.summaryGcTime }); + const unresolvedQuery = useQuery({ queryKey: ['access-unresolved-identities', criteria.keyword, criteria.protocol], queryFn: ({ signal }) => api.accessUnresolvedIdentities({ keyword: criteria.keyword || undefined, protocol: criteria.protocol || undefined, limit: 20, offset: 0 }, signal), enabled: editable, staleTime: 15_000, gcTime: QUERY_MEMORY.summaryGcTime }); const thresholdQuery = useQuery({ queryKey: ['access-thresholds'], queryFn: ({ signal }) => api.accessThresholds(signal), enabled: editable, staleTime: 60_000, gcTime: QUERY_MEMORY.summaryGcTime }); useEffect(() => { if (thresholdQuery.data && !thresholdDraft) setThresholdDraft({ version: thresholdQuery.data.version, defaultThresholdSec: thresholdQuery.data.defaultThresholdSec, delayThresholdSec: thresholdQuery.data.delayThresholdSec, longOfflineSec: thresholdQuery.data.longOfflineSec, protocols: thresholdQuery.data.protocols }); }, [thresholdDraft, thresholdQuery.data]); const updateThreshold = useMutation({ mutationFn: api.updateAccessThresholds, onSuccess: async (config) => { setThresholdDraft({ version: config.version, defaultThresholdSec: config.defaultThresholdSec, delayThresholdSec: config.delayThresholdSec, longOfflineSec: config.longOfflineSec, protocols: config.protocols }); await Promise.all([queryClient.invalidateQueries({ queryKey: ['access-summary'] }), queryClient.invalidateQueries({ queryKey: ['access-vehicles'] })]); } }); @@ -112,7 +112,7 @@ export default function AccessPage() { const apply = (next: Filters) => { setDraft(next); setCriteria(next); setOffset(0); setSelectedVIN(''); syncURL(next); }; const submit = (event: FormEvent) => { event.preventDefault(); apply(draft); }; const page = Math.floor(offset / limit) + 1; const totalPages = Math.max(1, Math.ceil((vehiclesQuery.data?.total ?? 0) / limit)); const summary = summaryQuery.data; - const refresh = () => Promise.all([summaryQuery.refetch(), vehiclesQuery.refetch(), unresolvedQuery.refetch(), ...(editable ? [thresholdQuery.refetch()] : [])]); + const refresh = () => Promise.all([summaryQuery.refetch(), vehiclesQuery.refetch(), ...(editable ? [unresolvedQuery.refetch(), thresholdQuery.refetch()] : [])]); return

车辆接入管理

以主车辆为对象,对照应接协议、实际接入和各协议最新上报时间

数据时间 {summary?.asOf ? formatAccessTime(summary.asOf) : '—'}
@@ -123,8 +123,8 @@ export default function AccessPage() { ].map(([label, value, tone, connectionState]) => )} {vehiclesQuery.isError ? vehiclesQuery.refetch()} /> : null}
车辆协议接入差异优先展示应接与实接差异;时间为各协议最后接收时间
{PROTOCOLS.map((item) => )}{rows.map((row) => setSelectedVIN(row.vin)} onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') setSelectedVIN(row.vin); }}>{PROTOCOLS.map((protocol) => )})}
车辆品牌 / 车型应接协议{item}综合状态
{row.plate || '未绑定车牌'}{row.vin}{row.oem || '品牌未维护'}{row.model || row.company || '车型未维护'}{row.expectedProtocols.length} 项{row.expectedProtocols.join(' / ')}
{vehiclesQuery.isFetching ?
正在更新车辆接入状态…
: null}{!vehiclesQuery.isFetching && !rows.length ?
当前筛选条件没有车辆
: null}
第 {page} / {totalPages} 页,共 {(vehiclesQuery.data?.total ?? 0).toLocaleString('zh-CN')} 辆主车辆
{selected ? setSelectedVIN('')} /> : null}
- {unresolvedQuery.isError ? unresolvedQuery.refetch()} /> : null} - + {editable && unresolvedQuery.isError ? unresolvedQuery.refetch()} /> : null} + {editable ? : null} {editable && thresholdQuery.isError ? thresholdQuery.refetch()} /> : null} {editable ? thresholdDraft && updateThreshold.mutate(thresholdDraft)} /> : null}
;