feat(platform): center navigation on vehicle service
This commit is contained in:
@@ -16,12 +16,12 @@ export type PageKey = 'dashboard' | 'vehicles' | 'realtime' | 'detail' | 'histor
|
||||
|
||||
const navItems = [
|
||||
{ itemKey: 'dashboard', text: '总览工作台', icon: <IconHome /> },
|
||||
{ itemKey: 'vehicles', text: '车辆台账', icon: <IconServer /> },
|
||||
{ itemKey: 'realtime', text: '实时状态', icon: <IconActivity /> },
|
||||
{ itemKey: 'vehicles', text: '车辆服务', icon: <IconServer /> },
|
||||
{ itemKey: 'realtime', text: '实时车辆', icon: <IconActivity /> },
|
||||
{ itemKey: 'detail', text: '车辆详情', icon: <IconSetting /> },
|
||||
{ itemKey: 'history', text: '历史查询', icon: <IconMapPin /> },
|
||||
{ itemKey: 'mileage', text: '里程分析', icon: <IconBarChartHStroked /> },
|
||||
{ itemKey: 'quality', text: '数据质量', icon: <IconHistogram /> }
|
||||
{ itemKey: 'history', text: '历史数据', icon: <IconMapPin /> },
|
||||
{ itemKey: 'mileage', text: '里程统计', icon: <IconBarChartHStroked /> },
|
||||
{ itemKey: 'quality', text: '质量治理', icon: <IconHistogram /> }
|
||||
];
|
||||
|
||||
function linkHealthClassName(count: number | null) {
|
||||
@@ -61,7 +61,7 @@ export function AppShell({
|
||||
<aside className="vp-sidebar">
|
||||
<div className="vp-brand">
|
||||
<span className="vp-brand-mark" />
|
||||
<span>车辆数据中台</span>
|
||||
<span>车辆服务中台</span>
|
||||
</div>
|
||||
<Nav
|
||||
selectedKeys={[activePage]}
|
||||
@@ -85,6 +85,7 @@ export function AppShell({
|
||||
</Space>
|
||||
<Space spacing={12}>
|
||||
<Tag color="blue">生产环境</Tag>
|
||||
<Tag color="grey">多源接入 / 一个车辆服务</Tag>
|
||||
<Button size="small" className={linkHealthClassName(linkIssueCount)} onClick={() => onChange('quality')}>
|
||||
{linkIssueCount == null ? '链路监控' : linkIssueCount > 0 ? `链路 ${linkIssueCount} 项关注` : '链路正常'}
|
||||
</Button>
|
||||
|
||||
@@ -62,7 +62,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality }: { onOpenVehicle: (vi
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="总览工作台" description="车辆在线、协议分布、数据质量和链路健康的统一入口" />
|
||||
<PageHeader title="总览工作台" description="以车辆服务为中心汇总在线状态、数据来源覆盖、质量问题和链路健康" />
|
||||
<Spin spinning={loading}>
|
||||
<div className="vp-kpi-grid">
|
||||
{kpis.map((item) => (
|
||||
@@ -74,12 +74,12 @@ export function Dashboard({ onOpenVehicle, onOpenQuality }: { onOpenVehicle: (vi
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Col span={14}>
|
||||
<Card title="协议在线分布" bordered>
|
||||
<Card title="数据来源在线分布" bordered>
|
||||
<Table
|
||||
pagination={false}
|
||||
dataSource={summary?.protocols ?? []}
|
||||
columns={[
|
||||
{ title: '协议', dataIndex: 'protocol' },
|
||||
{ title: '数据来源', dataIndex: 'protocol' },
|
||||
{ title: '在线', dataIndex: 'online' },
|
||||
{ title: '总数', dataIndex: 'total' },
|
||||
{
|
||||
|
||||
@@ -101,11 +101,11 @@ export function History({ initialVin, onOpenVehicle }: { initialVin: string; onO
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="历史查询" description="按车辆查询位置历史和 RAW 帧历史,协议作为来源过滤和诊断维度" />
|
||||
<PageHeader title="历史数据" description="按车辆查询位置历史和 RAW 帧历史,数据来源只作为过滤和诊断维度" />
|
||||
<Card bordered>
|
||||
<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 }}>
|
||||
<Form.Select field="protocol" label="数据来源" placeholder="全部来源" style={{ width: 190 }}>
|
||||
<Select.Option value="GB32960">GB32960</Select.Option>
|
||||
<Select.Option value="JT808">JT808</Select.Option>
|
||||
<Select.Option value="YUTONG_MQTT">YUTONG_MQTT</Select.Option>
|
||||
@@ -145,7 +145,7 @@ export function History({ initialVin, onOpenVehicle }: { initialVin: string; onO
|
||||
}}
|
||||
columns={[
|
||||
{ title: 'VIN', dataIndex: 'vin', width: 190 },
|
||||
{ title: '协议', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '数据来源', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '经度', dataIndex: 'longitude', width: 120 },
|
||||
{ title: '纬度', dataIndex: 'latitude', width: 120 },
|
||||
{ title: '速度 km/h', dataIndex: 'speedKmh', width: 120 },
|
||||
@@ -179,7 +179,7 @@ export function History({ initialVin, onOpenVehicle }: { initialVin: string; onO
|
||||
{ title: 'ID', dataIndex: 'id', width: 260 },
|
||||
{ title: 'VIN', dataIndex: 'vin', width: 190 },
|
||||
{ title: '车牌', dataIndex: 'plate', width: 120 },
|
||||
{ title: '协议', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '数据来源', dataIndex: 'protocol', width: 120 },
|
||||
{ title: '帧类型', dataIndex: 'frameType', width: 190 },
|
||||
{ title: '大小 B', dataIndex: 'rawSizeBytes', width: 100 },
|
||||
{ title: '设备时间', dataIndex: 'deviceTime', width: 190 },
|
||||
|
||||
@@ -68,7 +68,7 @@ export function Mileage({ initialVin, onOpenVehicle }: { initialVin: string; onO
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="里程分析" description="每日里程、区间里程和异常差值分析" />
|
||||
<PageHeader title="里程统计" description="按车辆汇总每日里程、区间里程和异常差值分析" />
|
||||
<Card bordered>
|
||||
<Form key={filters.vin ?? ''} initValues={filters} layout="horizontal" onSubmit={(values) => {
|
||||
const nextFilters = values as Record<string, string>;
|
||||
@@ -77,7 +77,7 @@ export function Mileage({ initialVin, onOpenVehicle }: { initialVin: string; onO
|
||||
load(nextFilters, 1, pagination.pageSize);
|
||||
}}>
|
||||
<Form.Input field="vin" label="关键词" placeholder="VIN / 车牌 / 手机号 / OEM" style={{ width: 260 }} />
|
||||
<Form.Select field="protocol" label="协议" placeholder="全部协议" style={{ width: 180 }}>
|
||||
<Form.Select field="protocol" label="数据来源" placeholder="全部来源" style={{ width: 180 }}>
|
||||
<Select.Option value="GB32960">GB32960</Select.Option>
|
||||
<Select.Option value="JT808">JT808</Select.Option>
|
||||
<Select.Option value="YUTONG_MQTT">YUTONG_MQTT</Select.Option>
|
||||
|
||||
@@ -111,7 +111,7 @@ export function Quality({
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="数据质量" description="断链、VIN 缺失、字段缺失和链路健康的排查入口" />
|
||||
<PageHeader title="质量治理" description="围绕车辆服务排查断链、VIN 缺失、字段缺失和链路健康" />
|
||||
<div className="vp-kpi-grid">
|
||||
{[
|
||||
{ label: '问题车辆', value: summary.issueVehicleCount.toLocaleString() },
|
||||
@@ -145,7 +145,7 @@ export function Quality({
|
||||
loadIssues(nextFilters, 1, pagination.pageSize);
|
||||
}} style={{ marginBottom: 12 }}>
|
||||
<Form.Input field="keyword" label="关键词" placeholder="手机号 / 来源地址 / 车牌" style={{ width: 260 }} />
|
||||
<Form.Select field="protocol" label="协议" placeholder="全部协议" style={{ width: 160 }}>
|
||||
<Form.Select field="protocol" label="数据来源" placeholder="全部来源" style={{ width: 160 }}>
|
||||
<Select.Option value="JT808">JT808</Select.Option>
|
||||
</Form.Select>
|
||||
<Space>
|
||||
@@ -174,7 +174,7 @@ export function Quality({
|
||||
{ title: '车牌', dataIndex: 'plate' },
|
||||
{ title: '手机号', dataIndex: 'phone' },
|
||||
{ title: '来源地址', dataIndex: 'sourceEndpoint' },
|
||||
{ title: '协议', dataIndex: 'protocol' },
|
||||
{ title: '数据来源', dataIndex: 'protocol' },
|
||||
{ title: '问题', dataIndex: 'issueType' },
|
||||
{ title: '级别', render: (_: unknown, row: QualityIssueRow) => <Tag color={row.severity === 'error' ? 'red' : 'orange'}>{row.severity}</Tag> },
|
||||
{ title: '最后时间', dataIndex: 'lastSeen' },
|
||||
|
||||
@@ -38,7 +38,7 @@ export function Realtime({ onOpenVehicle }: { onOpenVehicle: (vin: string) => vo
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="实时状态" description="以车辆为主对象查看最新位置、在线来源和核心实时数据" />
|
||||
<PageHeader title="实时车辆" description="以车辆为主对象查看最新位置、在线来源和核心实时数据" />
|
||||
<Card bordered>
|
||||
<Form layout="horizontal" onSubmit={(values) => {
|
||||
const nextFilters = values as Record<string, string>;
|
||||
@@ -46,7 +46,7 @@ export function Realtime({ onOpenVehicle }: { onOpenVehicle: (vin: string) => vo
|
||||
load(nextFilters, 1, pagination.pageSize);
|
||||
}} style={{ marginBottom: 12 }}>
|
||||
<Form.Input field="vin" label="关键词" placeholder="VIN / 车牌 / 手机号 / OEM" style={{ width: 260 }} />
|
||||
<Form.Select field="protocol" label="协议" placeholder="全部协议" style={{ width: 180 }}>
|
||||
<Form.Select field="protocol" label="数据来源" placeholder="全部来源" style={{ width: 180 }}>
|
||||
<Select.Option value="GB32960">GB32960</Select.Option>
|
||||
<Select.Option value="JT808">JT808</Select.Option>
|
||||
<Select.Option value="YUTONG_MQTT">YUTONG_MQTT</Select.Option>
|
||||
|
||||
@@ -57,7 +57,7 @@ export function Vehicles({ onOpenVehicle }: { onOpenVehicle: (vin: string) => vo
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="车辆台账" description="以 VIN 为主对象维护车辆身份、多源覆盖、在线状态和绑定状态" />
|
||||
<PageHeader title="车辆服务" description="以 VIN 为主对象维护车辆身份、数据来源覆盖、在线状态和绑定状态" />
|
||||
<Card bordered>
|
||||
<Form layout="horizontal" onSubmit={(values) => {
|
||||
const nextFilters = values as Record<string, string>;
|
||||
@@ -65,7 +65,7 @@ export function Vehicles({ onOpenVehicle }: { onOpenVehicle: (vin: string) => vo
|
||||
load(nextFilters, 1, pagination.pageSize);
|
||||
}}>
|
||||
<Form.Input field="keyword" label="关键词" placeholder="VIN / 车牌 / 手机号 / OEM" style={{ width: 260 }} />
|
||||
<Form.Select field="protocol" label="协议" placeholder="全部协议" style={{ width: 180 }}>
|
||||
<Form.Select field="protocol" label="数据来源" placeholder="全部来源" style={{ width: 180 }}>
|
||||
<Select.Option value="GB32960">GB32960</Select.Option>
|
||||
<Select.Option value="JT808">JT808</Select.Option>
|
||||
<Select.Option value="YUTONG_MQTT">YUTONG_MQTT</Select.Option>
|
||||
|
||||
@@ -4,6 +4,6 @@ import App from '../App';
|
||||
|
||||
test('renders vehicle platform shell', () => {
|
||||
render(<App />);
|
||||
expect(screen.getByText('车辆数据中台')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆服务中台')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('总览工作台').length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user