feat(platform): open analysis from vehicle detail
This commit is contained in:
@@ -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)} />
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user