feat(platform): aggregate realtime by vehicle

This commit is contained in:
lingniu
2026-07-03 23:09:28 +08:00
parent 71b714bbf7
commit 37f9d7a307
12 changed files with 291 additions and 13 deletions

View File

@@ -1,12 +1,13 @@
import { Button, Card, Form, Select, Space, Table, Tabs, Toast } from '@douyinfe/semi-ui';
import { Button, Card, Form, Select, Space, Table, Tabs, Tag, Toast } from '@douyinfe/semi-ui';
import { useEffect, useState } from 'react';
import { api } from '../api/client';
import type { RealtimeLocationRow } from '../api/types';
import type { VehicleRealtimeRow } from '../api/types';
import { DataEmpty } from '../components/DataEmpty';
import { PageHeader } from '../components/PageHeader';
import { StatusTag } from '../components/StatusTag';
export function Realtime() {
const [rows, setRows] = useState<RealtimeLocationRow[]>([]);
const [rows, setRows] = useState<VehicleRealtimeRow[]>([]);
const [loading, setLoading] = useState(true);
const [filters, setFilters] = useState<Record<string, string>>({});
const [pagination, setPagination] = useState({ currentPage: 1, pageSize: 50, total: 0 });
@@ -16,7 +17,8 @@ export function Realtime() {
const params = new URLSearchParams({ limit: String(pageSize), offset: String((page - 1) * pageSize) });
if (values?.vin) params.set('vin', values.vin);
if (values?.protocol) params.set('protocol', values.protocol);
api.realtimeLocations(params)
if (values?.online) params.set('online', values.online);
api.vehicleRealtime(params)
.then((nextPage) => {
setRows(nextPage.items);
setPagination({ currentPage: page, pageSize, total: nextPage.total });
@@ -31,19 +33,23 @@ export function Realtime() {
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>;
setFilters(nextFilters);
load(nextFilters, 1, pagination.pageSize);
}} style={{ marginBottom: 12 }}>
<Form.Input field="vin" label="关键词" placeholder="VIN / 车牌" style={{ width: 260 }} />
<Form.Input field="vin" label="关键词" placeholder="VIN / 车牌 / 手机号 / OEM" style={{ width: 260 }} />
<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>
</Form.Select>
<Form.Select field="online" label="在线" placeholder="全部" style={{ width: 130 }}>
<Select.Option value="online">线</Select.Option>
<Select.Option value="offline">线</Select.Option>
</Form.Select>
<Space>
<Button htmlType="submit" theme="solid" type="primary"></Button>
<Button onClick={() => {
@@ -59,7 +65,7 @@ export function Realtime() {
) : (
<Table
loading={loading}
rowKey={(row?: RealtimeLocationRow) => `${row?.vin ?? ''}-${row?.protocol ?? ''}`}
rowKey="vin"
dataSource={rows}
pagination={{
currentPage: pagination.currentPage,
@@ -72,10 +78,23 @@ export function Realtime() {
columns={[
{ title: 'VIN', dataIndex: 'vin' },
{ title: '车牌', dataIndex: 'plate' },
{ title: '协议', dataIndex: 'protocol' },
{
title: '来源',
width: 260,
render: (_: unknown, row: VehicleRealtimeRow) => (
<Space spacing={4} wrap>
{row.protocols.map((protocol) => (
<Tag key={protocol} color={protocol === row.primaryProtocol ? 'blue' : 'grey'}>{protocol}</Tag>
))}
</Space>
)
},
{ title: '覆盖', width: 90, render: (_: unknown, row: VehicleRealtimeRow) => `${row.onlineSourceCount}/${row.sourceCount}` },
{ title: '在线', width: 90, render: (_: unknown, row: VehicleRealtimeRow) => <StatusTag status={row.online ? 'ok' : 'offline'} /> },
{ title: '速度 km/h', dataIndex: 'speedKmh' },
{ title: 'SOC %', dataIndex: 'socPercent' },
{ title: '总里程 km', dataIndex: 'totalMileageKm' },
{ title: '最新来源', dataIndex: 'primaryProtocol' },
{ title: '最后时间', dataIndex: 'lastSeen' }
]}
/>
@@ -87,7 +106,7 @@ export function Realtime() {
<span
key={row.vin}
className="vp-map-dot"
title={`${row.plate} ${row.protocol}`}
title={`${row.plate} ${row.primaryProtocol}`}
style={{ left: `${18 + index * 24}%`, top: `${26 + (index % 3) * 18}%` }}
/>
))}