feat(platform): route vehicle searches to detail
This commit is contained in:
@@ -8,20 +8,31 @@ import { Realtime } from './pages/Realtime';
|
|||||||
import { VehicleDetail } from './pages/VehicleDetail';
|
import { VehicleDetail } from './pages/VehicleDetail';
|
||||||
import { Vehicles } from './pages/Vehicles';
|
import { Vehicles } from './pages/Vehicles';
|
||||||
|
|
||||||
const pages: Record<PageKey, JSX.Element> = {
|
|
||||||
dashboard: <Dashboard />,
|
|
||||||
vehicles: <Vehicles />,
|
|
||||||
realtime: <Realtime />,
|
|
||||||
detail: <VehicleDetail />,
|
|
||||||
history: <History />,
|
|
||||||
mileage: <Mileage />,
|
|
||||||
quality: <Quality />
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [activePage, setActivePage] = useState<PageKey>('dashboard');
|
const [activePage, setActivePage] = useState<PageKey>('dashboard');
|
||||||
|
const [activeVin, setActiveVin] = useState('LB9A32A24R0LS1426');
|
||||||
|
|
||||||
|
const openVehicle = (vin: string) => {
|
||||||
|
const nextVin = vin.trim();
|
||||||
|
if (!nextVin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setActiveVin(nextVin);
|
||||||
|
setActivePage('detail');
|
||||||
|
};
|
||||||
|
|
||||||
|
const pages: Record<PageKey, JSX.Element> = {
|
||||||
|
dashboard: <Dashboard />,
|
||||||
|
vehicles: <Vehicles onOpenVehicle={openVehicle} />,
|
||||||
|
realtime: <Realtime />,
|
||||||
|
detail: <VehicleDetail vin={activeVin} />,
|
||||||
|
history: <History />,
|
||||||
|
mileage: <Mileage />,
|
||||||
|
quality: <Quality />
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppShell activePage={activePage} onChange={setActivePage}>
|
<AppShell activePage={activePage} onChange={setActivePage} onVehicleSearch={openVehicle}>
|
||||||
{pages[activePage]}
|
{pages[activePage]}
|
||||||
</AppShell>
|
</AppShell>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Badge, Button, Input, Nav, Space, Tag, Typography } from '@douyinfe/semi-ui';
|
import { Badge, Button, Input, Nav, Space, Tag, Toast, Typography } from '@douyinfe/semi-ui';
|
||||||
import {
|
import {
|
||||||
IconActivity,
|
IconActivity,
|
||||||
IconBarChartHStroked,
|
IconBarChartHStroked,
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
IconSetting
|
IconSetting
|
||||||
} from '@douyinfe/semi-icons';
|
} from '@douyinfe/semi-icons';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
export type PageKey = 'dashboard' | 'vehicles' | 'realtime' | 'detail' | 'history' | 'mileage' | 'quality';
|
export type PageKey = 'dashboard' | 'vehicles' | 'realtime' | 'detail' | 'history' | 'mileage' | 'quality';
|
||||||
|
|
||||||
@@ -26,12 +27,25 @@ const navItems = [
|
|||||||
export function AppShell({
|
export function AppShell({
|
||||||
activePage,
|
activePage,
|
||||||
onChange,
|
onChange,
|
||||||
|
onVehicleSearch,
|
||||||
children
|
children
|
||||||
}: {
|
}: {
|
||||||
activePage: PageKey;
|
activePage: PageKey;
|
||||||
onChange: (page: PageKey) => void;
|
onChange: (page: PageKey) => void;
|
||||||
|
onVehicleSearch: (keyword: string) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
|
const [keyword, setKeyword] = useState('');
|
||||||
|
|
||||||
|
const search = () => {
|
||||||
|
const value = keyword.trim();
|
||||||
|
if (!value) {
|
||||||
|
Toast.warning('请输入 VIN / 车牌 / 手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onVehicleSearch(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="vp-app">
|
<div className="vp-app">
|
||||||
<div className="vp-shell">
|
<div className="vp-shell">
|
||||||
@@ -50,8 +64,15 @@ export function AppShell({
|
|||||||
<main className="vp-main">
|
<main className="vp-main">
|
||||||
<header className="vp-topbar">
|
<header className="vp-topbar">
|
||||||
<Space spacing={12}>
|
<Space spacing={12}>
|
||||||
<Input prefix={<IconSearch />} placeholder="搜索 VIN / 车牌 / 手机号" style={{ width: 320 }} />
|
<Input
|
||||||
<Button theme="solid" type="primary">查询车辆</Button>
|
prefix={<IconSearch />}
|
||||||
|
placeholder="搜索 VIN / 车牌 / 手机号"
|
||||||
|
value={keyword}
|
||||||
|
onChange={setKeyword}
|
||||||
|
onEnterPress={search}
|
||||||
|
style={{ width: 320 }}
|
||||||
|
/>
|
||||||
|
<Button theme="solid" type="primary" onClick={search}>查询车辆</Button>
|
||||||
</Space>
|
</Space>
|
||||||
<Space spacing={12}>
|
<Space spacing={12}>
|
||||||
<Tag color="blue">生产环境</Tag>
|
<Tag color="blue">生产环境</Tag>
|
||||||
|
|||||||
@@ -11,12 +11,8 @@ type VehicleQuery = {
|
|||||||
protocol?: string;
|
protocol?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultQuery: VehicleQuery = {
|
export function VehicleDetail({ vin }: { vin: string }) {
|
||||||
vin: 'LB9A32A24R0LS1426'
|
const [query, setQuery] = useState<VehicleQuery>({ vin });
|
||||||
};
|
|
||||||
|
|
||||||
export function VehicleDetail() {
|
|
||||||
const [query, setQuery] = useState<VehicleQuery>(defaultQuery);
|
|
||||||
const [detail, setDetail] = useState<VehicleDetailData | null>(null);
|
const [detail, setDetail] = useState<VehicleDetailData | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@@ -38,8 +34,10 @@ export function VehicleDetail() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load(defaultQuery);
|
const nextQuery = { vin };
|
||||||
}, []);
|
setQuery(nextQuery);
|
||||||
|
load(nextQuery);
|
||||||
|
}, [vin]);
|
||||||
|
|
||||||
const identity = detail?.identity;
|
const identity = detail?.identity;
|
||||||
const latest = detail?.realtime[0];
|
const latest = detail?.realtime[0];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Card, Form, Select, SideSheet, Space, Table, TextArea, Toast } from '@douyinfe/semi-ui';
|
import { Button, Card, Form, Select, Space, Table, Toast } from '@douyinfe/semi-ui';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { api } from '../api/client';
|
import { api } from '../api/client';
|
||||||
import type { VehicleRow } from '../api/types';
|
import type { VehicleRow } from '../api/types';
|
||||||
@@ -6,10 +6,9 @@ import { DataEmpty } from '../components/DataEmpty';
|
|||||||
import { PageHeader } from '../components/PageHeader';
|
import { PageHeader } from '../components/PageHeader';
|
||||||
import { StatusTag } from '../components/StatusTag';
|
import { StatusTag } from '../components/StatusTag';
|
||||||
|
|
||||||
export function Vehicles() {
|
export function Vehicles({ onOpenVehicle }: { onOpenVehicle: (vin: string) => void }) {
|
||||||
const [rows, setRows] = useState<VehicleRow[]>([]);
|
const [rows, setRows] = useState<VehicleRow[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [selected, setSelected] = useState<VehicleRow | null>(null);
|
|
||||||
|
|
||||||
const load = (values?: Record<string, string>) => {
|
const load = (values?: Record<string, string>) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -35,9 +34,9 @@ export function Vehicles() {
|
|||||||
{ title: '最后在线', dataIndex: 'lastSeen', width: 170 },
|
{ title: '最后在线', dataIndex: 'lastSeen', width: 170 },
|
||||||
{ title: '位置', dataIndex: 'locationText' },
|
{ title: '位置', dataIndex: 'locationText' },
|
||||||
{ title: '绑定分', dataIndex: 'bindingScore', width: 90 },
|
{ title: '绑定分', dataIndex: 'bindingScore', width: 90 },
|
||||||
{ title: '操作', render: (_: unknown, row: VehicleRow) => <Button onClick={() => setSelected(row)}>详情</Button> }
|
{ title: '操作', render: (_: unknown, row: VehicleRow) => <Button onClick={() => onOpenVehicle(row.vin)}>车辆服务</Button> }
|
||||||
],
|
],
|
||||||
[]
|
[onOpenVehicle]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -64,9 +63,6 @@ export function Vehicles() {
|
|||||||
<Table loading={loading} rowKey="vin" dataSource={rows} columns={columns} pagination={false} />
|
<Table loading={loading} rowKey="vin" dataSource={rows} columns={columns} pagination={false} />
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
<SideSheet title="车辆详情" visible={Boolean(selected)} onCancel={() => setSelected(null)} width={520}>
|
|
||||||
<TextArea value={JSON.stringify(selected, null, 2)} autosize readOnly />
|
|
||||||
</SideSheet>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user