feat(platform): open analysis from vehicle detail

This commit is contained in:
lingniu
2026-07-03 23:37:12 +08:00
parent 37ca94ab5f
commit 532b0d9aec
4 changed files with 57 additions and 22 deletions

View File

@@ -13,6 +13,7 @@ import { Vehicles } from './pages/Vehicles';
export default function App() {
const [activePage, setActivePage] = useState<PageKey>('dashboard');
const [activeVin, setActiveVin] = useState('LB9A32A24R0LS1426');
const [analysisVin, setAnalysisVin] = useState('LB9A32A24R0LS1426');
const [linkIssueCount, setLinkIssueCount] = useState<number | null>(null);
const refreshOpsHealth = useCallback((showError = true) => {
@@ -45,13 +46,31 @@ export default function App() {
setActivePage('detail');
};
const openHistoryForVehicle = (vin: string) => {
const nextVin = vin.trim();
if (!nextVin) {
return;
}
setAnalysisVin(nextVin);
setActivePage('history');
};
const openMileageForVehicle = (vin: string) => {
const nextVin = vin.trim();
if (!nextVin) {
return;
}
setAnalysisVin(nextVin);
setActivePage('mileage');
};
const pages: Record<PageKey, JSX.Element> = {
dashboard: <Dashboard onOpenVehicle={openVehicle} onOpenQuality={() => setActivePage('quality')} />,
vehicles: <Vehicles onOpenVehicle={openVehicle} />,
realtime: <Realtime onOpenVehicle={openVehicle} />,
detail: <VehicleDetail vin={activeVin} />,
history: <History onOpenVehicle={openVehicle} />,
mileage: <Mileage onOpenVehicle={openVehicle} />,
detail: <VehicleDetail vin={activeVin} onOpenHistory={openHistoryForVehicle} onOpenMileage={openMileageForVehicle} />,
history: <History initialVin={analysisVin} onOpenVehicle={openVehicle} />,
mileage: <Mileage initialVin={analysisVin} onOpenVehicle={openVehicle} />,
quality: <Quality onHealthLoaded={(health) => setLinkIssueCount(health.linkHealth.filter((item) => item.status !== 'ok').length)} />
};

View File

@@ -26,8 +26,8 @@ function canOpenVehicle(vin?: string) {
return Boolean(value && value !== 'unknown');
}
export function History({ onOpenVehicle }: { onOpenVehicle: (vin: string) => void }) {
const [filters, setFilters] = useState<HistoryFilters>(defaultFilters);
export function History({ initialVin, onOpenVehicle }: { initialVin: string; onOpenVehicle: (vin: string) => void }) {
const [filters, setFilters] = useState<HistoryFilters>({ ...defaultFilters, vin: initialVin || defaultFilters.vin });
const [locations, setLocations] = useState<Page<HistoryLocationRow>>(defaultPage);
const [rawFrames, setRawFrames] = useState<Page<RawFrameRow>>(defaultPage);
const [selectedRaw, setSelectedRaw] = useState<RawFrameRow | null>(null);
@@ -84,15 +84,18 @@ export function History({ onOpenVehicle }: { onOpenVehicle: (vin: string) => voi
};
const reset = () => {
setFilters(defaultFilters);
loadLocations(defaultFilters, 1, locationPagination.pageSize);
loadRawFrames(defaultFilters, 1, rawPagination.pageSize);
const nextFilters = { ...defaultFilters, vin: initialVin || defaultFilters.vin };
setFilters(nextFilters);
loadLocations(nextFilters, 1, locationPagination.pageSize);
loadRawFrames(nextFilters, 1, rawPagination.pageSize);
};
useEffect(() => {
loadLocations(defaultFilters, 1, locationPagination.pageSize);
loadRawFrames(defaultFilters, 1, rawPagination.pageSize);
}, []);
const nextFilters = { ...defaultFilters, vin: initialVin || defaultFilters.vin };
setFilters(nextFilters);
loadLocations(nextFilters, 1, locationPagination.pageSize);
loadRawFrames(nextFilters, 1, rawPagination.pageSize);
}, [initialVin]);
const rawFieldCount = useMemo(() => Object.keys(selectedRaw?.parsedFields ?? {}).length, [selectedRaw]);
@@ -100,7 +103,7 @@ export function History({ onOpenVehicle }: { onOpenVehicle: (vin: string) => voi
<div className="vp-page">
<PageHeader title="历史查询" description="按车辆查询位置历史和 RAW 帧历史,协议作为来源过滤和诊断维度" />
<Card bordered>
<Form initValues={filters} layout="horizontal" onSubmit={(values) => submit(values)}>
<Form key={`${filters.vin ?? ''}-${filters.protocol ?? ''}-${filters.includeFields ? 'fields' : 'light'}`} initValues={filters} layout="horizontal" onSubmit={(values) => submit(values)}>
<Form.Input field="vin" label="VIN" placeholder="输入 VIN" style={{ width: 260 }} />
<Form.Select field="protocol" label="协议" placeholder="全部协议" style={{ width: 190 }}>
<Select.Option value="GB32960">GB32960</Select.Option>

View File

@@ -32,12 +32,12 @@ function canOpenVehicle(vin?: string) {
return Boolean(value && value !== 'unknown');
}
export function Mileage({ onOpenVehicle }: { onOpenVehicle: (vin: string) => void }) {
export function Mileage({ initialVin, onOpenVehicle }: { initialVin: string; onOpenVehicle: (vin: string) => void }) {
const [rows, setRows] = useState<DailyMileageRow[]>([]);
const [summary, setSummary] = useState<MileageSummary>(emptySummary);
const [loading, setLoading] = useState(true);
const [summaryLoading, setSummaryLoading] = useState(true);
const [filters, setFilters] = useState<Record<string, string>>({});
const [filters, setFilters] = useState<Record<string, string>>({ vin: initialVin });
const [pagination, setPagination] = useState({ currentPage: 1, pageSize: 20, total: 0 });
const loadSummary = (values: Record<string, string> = filters) => {
@@ -60,15 +60,17 @@ export function Mileage({ onOpenVehicle }: { onOpenVehicle: (vin: string) => voi
};
useEffect(() => {
loadSummary({});
load({}, 1, pagination.pageSize);
}, []);
const nextFilters = { vin: initialVin };
setFilters(nextFilters);
loadSummary(nextFilters);
load(nextFilters, 1, pagination.pageSize);
}, [initialVin]);
return (
<div className="vp-page">
<PageHeader title="里程分析" description="每日里程、区间里程和异常差值分析" />
<Card bordered>
<Form layout="horizontal" onSubmit={(values) => {
<Form key={filters.vin ?? ''} initValues={filters} layout="horizontal" onSubmit={(values) => {
const nextFilters = values as Record<string, string>;
setFilters(nextFilters);
loadSummary(nextFilters);
@@ -85,9 +87,10 @@ export function Mileage({ onOpenVehicle }: { onOpenVehicle: (vin: string) => voi
<Space>
<Button htmlType="submit" theme="solid" type="primary"></Button>
<Button onClick={() => {
setFilters({});
loadSummary({});
load({}, 1, pagination.pageSize);
const nextFilters = { vin: initialVin };
setFilters(nextFilters);
loadSummary(nextFilters);
load(nextFilters, 1, pagination.pageSize);
}}></Button>
</Space>
</Form>

View File

@@ -11,7 +11,15 @@ type VehicleQuery = {
protocol?: string;
};
export function VehicleDetail({ vin }: { vin: string }) {
export function VehicleDetail({
vin,
onOpenHistory,
onOpenMileage
}: {
vin: string;
onOpenHistory: (vin: string) => void;
onOpenMileage: (vin: string) => void;
}) {
const [query, setQuery] = useState<VehicleQuery>({ vin });
const [detail, setDetail] = useState<VehicleDetailData | null>(null);
const [loading, setLoading] = useState(false);
@@ -66,6 +74,8 @@ export function VehicleDetail({ vin }: { vin: string }) {
<Space>
<Button icon={<IconSearch />} htmlType="submit" theme="solid" type="primary"></Button>
<Button icon={<IconRefresh />} onClick={() => load(query)} loading={loading}></Button>
<Button onClick={() => onOpenHistory(query.vin)}></Button>
<Button onClick={() => onOpenMileage(query.vin)}></Button>
</Space>
</Form>
</Card>