import { IconRefresh, IconSearch } from '@douyinfe/semi-icons'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useDeferredValue, useEffect, useMemo, useState } from 'react'; import { api } from '../../api/client'; import type { ReconciliationIssue } from '../../api/types'; import { InlineError } from '../shared/AsyncState'; import { QUERY_MEMORY } from '../queryPolicy'; const PAGE_SIZE = 50; const activeStatuses = new Set(['pending', 'confirmed_source_a', 'confirmed_source_b']); const reviewStatuses = [ { value: 'pending', label: '待处理' }, { value: 'confirmed_source_a', label: '确认来源 A' }, { value: 'confirmed_source_b', label: '确认来源 B' }, { value: 'no_action', label: '无需处理' }, { value: 'fixed', label: '已修复' } ]; function statusLabel(status: string) { return { pending: '待处理', confirmed_source_a: '确认来源 A', confirmed_source_b: '确认来源 B', no_action: '无需处理', fixed: '已修复', recovered: '已恢复' }[status] ?? status; } function severityLabel(severity: string) { return { critical: '严重', major: '重要', minor: '一般' }[severity] ?? severity; } function ruleLabel(rule: string) { return { DUPLICATE_PLATE: '重复车牌', DUPLICATE_PHONE: '重复终端', UNBOUND_SOURCE: '来源未绑定', SOURCE_MISSING: '主车无来源', POSITION_DRIFT: '位置漂移', MILEAGE_REVERSE: '里程倒退', MILEAGE_JUMP: '里程跳变', MILEAGE_SOURCE_DIVERGENCE: '里程来源差异', FLEET_COUNT_MISMATCH: '车辆总数不一致', BUSINESS_SCOPE_UNBOUND: '业务车辆未绑定', AUTH_SCOPE_BUSINESS_MISMATCH: '授权与业务范围不一致' }[rule] ?? rule; } function fmt(value?: string) { if (!value) return '—'; const parsed = new Date(value.replace(' ', 'T')); return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString('zh-CN', { hour12: false }); } function evidenceValue(value: unknown) { if (value == null || value === '') return '—'; if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return String(value); return JSON.stringify(value, null, 2); } function ReconciliationDetail({ issue, onClose }: { issue: ReconciliationIssue; onClose: () => void }) { const queryClient = useQueryClient(); const [status, setStatus] = useState(issue.status === 'recovered' ? 'pending' : issue.status); const [note, setNote] = useState(issue.resolutionNote ?? ''); useEffect(() => { setStatus(issue.status === 'recovered' ? 'pending' : issue.status); setNote(issue.resolutionNote ?? ''); }, [issue.id, issue.resolutionNote, issue.status]); const save = useMutation({ mutationFn: () => api.updateReconciliationIssue(issue.id, { version: issue.version, status, note: note.trim() }), onSuccess: async (updated) => { queryClient.setQueryData(['reconciliation-detail', issue.id], updated); await Promise.all([ queryClient.invalidateQueries({ queryKey: ['reconciliation-summary'] }), queryClient.invalidateQueries({ queryKey: ['reconciliation-issues'] }) ]); } }); const requiresNote = status !== 'pending'; return