feat(platform): organize console around vehicle service

This commit is contained in:
lingniu
2026-07-04 19:13:28 +08:00
parent cbe7882d51
commit 75d232e773
4 changed files with 121 additions and 20 deletions

View File

@@ -18,18 +18,33 @@ import { isAMapConfigured } from '../config/appConfig';
export type PageKey = 'dashboard' | 'vehicles' | 'map' | 'realtime' | 'detail' | 'history' | 'history-query' | 'mileage' | 'alert-events' | 'quality' | 'notification-rules' | 'ops-quality';
const navItems = [
{ itemKey: 'dashboard', text: '运营驾驶舱', icon: <IconHome /> },
{ itemKey: 'map', text: '实时地图', icon: <IconMapPin /> },
{ itemKey: 'vehicles', text: '车辆中心', icon: <IconServer /> },
{ itemKey: 'realtime', text: '实时数据', icon: <IconActivity /> },
{ itemKey: 'detail', text: '车辆档案', icon: <IconSetting /> },
{ itemKey: 'history', text: '轨迹回放', icon: <IconMapPin /> },
{ itemKey: 'history-query', text: '历史查询', icon: <IconHistogram /> },
{ itemKey: 'mileage', text: '统计查询', icon: <IconBarChartHStroked /> },
{ itemKey: 'alert-events', text: '告警事件', icon: <IconHistogram /> },
{ itemKey: 'notification-rules', text: '通知规则', icon: <IconBell /> },
{ itemKey: 'ops-quality', text: '运维质量', icon: <IconPulse /> }
const navGroups = [
{
title: '车辆工作台',
items: [
{ itemKey: 'dashboard', text: '运营驾驶舱', icon: <IconHome /> },
{ itemKey: 'map', text: '实时地图', icon: <IconMapPin /> },
{ itemKey: 'vehicles', text: '车辆中心', icon: <IconServer /> },
{ itemKey: 'realtime', text: '实时监控', icon: <IconActivity /> },
{ itemKey: 'detail', text: '车辆档案', icon: <IconSetting /> }
]
},
{
title: '查询分析',
items: [
{ itemKey: 'history', text: '轨迹回放', icon: <IconMapPin /> },
{ itemKey: 'history-query', text: '历史查询', icon: <IconHistogram /> },
{ itemKey: 'mileage', text: '统计查询', icon: <IconBarChartHStroked /> }
]
},
{
title: '告警运维',
items: [
{ itemKey: 'alert-events', text: '告警事件', icon: <IconHistogram /> },
{ itemKey: 'notification-rules', text: '通知规则', icon: <IconBell /> },
{ itemKey: 'ops-quality', text: '运维质量', icon: <IconPulse /> }
]
}
];
function linkHealthClassName(count: number | null) {
@@ -113,15 +128,22 @@ export function AppShell({
<span className="vp-brand-mark" />
<span className="vp-brand-copy">
<span></span>
<span> / / / </span>
<span> / </span>
</span>
</div>
<Nav
selectedKeys={[selectedPage]}
items={navItems}
onSelect={({ itemKey }) => onChange(itemKey as PageKey)}
style={{ maxWidth: '100%' }}
/>
<div className="vp-nav-groups">
{navGroups.map((group) => (
<div key={group.title} className="vp-nav-group">
<div className="vp-nav-section-title">{group.title}</div>
<Nav
selectedKeys={[selectedPage]}
items={group.items}
onSelect={({ itemKey }) => onChange(itemKey as PageKey)}
style={{ maxWidth: '100%' }}
/>
</div>
))}
</div>
</aside>
<main className="vp-main">
<header className="vp-topbar">
@@ -150,7 +172,7 @@ export function AppShell({
) : null}
<Tag color="blue"></Tag>
{platformRelease ? <Tag color="blue"> {platformRelease}</Tag> : null}
<Tag color="grey">32960 / 808 / MQTT </Tag>
<Tag color="grey"></Tag>
<Tag color={amapConfigured ? 'green' : 'orange'}>{amapConfigured ? '地图就绪' : '地图待配置'}</Tag>
<Button size="small" aria-label="顶部地图态势" onClick={() => onChange('map')}></Button>
<Button size="small" aria-label="顶部实时监控" onClick={() => onChange('realtime')}></Button>

View File

@@ -360,6 +360,28 @@ export function Dashboard({
onClick: () => onOpenMileage()
}
];
const vehicleServicePrinciples = [
{
title: '一车一档',
value: formatCount(serviceSummary?.totalVehicles),
detail: '以 VIN 为主对象沉淀车牌、手机号、OEM、绑定状态和来源覆盖。'
},
{
title: '一车一实时',
value: formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles),
detail: '实时状态合并位置、在线、速度、SOC、里程和来源新鲜度。'
},
{
title: '一车一轨迹',
value: formatCount(commandLocatedCount),
detail: '轨迹回放围绕车辆展开,协议来源只作为证据过滤条件。'
},
{
title: '一车一统计',
value: formatCount(serviceSummary?.totalVehicles),
detail: '里程等运营指标按车辆口径闭合,再回溯到来源证据。'
}
];
const workflowSteps = [
{
title: '接入巡检',
@@ -438,6 +460,17 @@ export function Dashboard({
<Tag color={(summary?.kafkaLag ?? 0) > 0 ? 'orange' : 'green'}>Kafka Lag {formatLag(summary?.kafkaLag)}</Tag>
</Space>
</Card>
<Card bordered title="一车一服务模型" style={{ marginBottom: 16 }}>
<div className="vp-service-model-grid">
{vehicleServicePrinciples.map((item) => (
<div key={item.title} className="vp-service-model-item">
<Tag color="blue">{item.title}</Tag>
<div className="vp-service-model-value">{item.value}</div>
<Typography.Text type="secondary">{item.detail}</Typography.Text>
</div>
))}
</div>
</Card>
<Card bordered title="全链路值班闭环" style={{ marginBottom: 16 }}>
<div className="vp-operation-flow">
{workflowSteps.map((item, index) => (

View File

@@ -69,6 +69,23 @@ body {
white-space: nowrap;
}
.vp-nav-groups {
display: grid;
gap: 12px;
}
.vp-nav-group {
min-width: 0;
}
.vp-nav-section-title {
padding: 0 12px 6px;
color: var(--vp-text-subtle);
font-size: 12px;
font-weight: 600;
line-height: 18px;
}
.vp-main {
min-width: 0;
}
@@ -878,6 +895,30 @@ body {
gap: 12px;
}
.vp-service-model-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
.vp-service-model-item {
min-height: 132px;
padding: 14px;
border: 1px solid var(--vp-border);
border-radius: var(--vp-radius);
background: #fbfcff;
display: grid;
gap: 10px;
align-content: start;
}
.vp-service-model-value {
color: var(--vp-text);
font-size: 26px;
font-weight: 700;
line-height: 32px;
}
.vp-operation-step {
min-height: 176px;
padding: 14px;
@@ -1031,6 +1072,7 @@ body {
.vp-evidence-grid,
.vp-action-grid,
.vp-operation-flow,
.vp-service-model-grid,
.vp-capability-grid,
.vp-conclusion-grid,
.vp-monitor-layout,

View File

@@ -12,6 +12,10 @@ afterEach(() => {
test('renders vehicle platform shell', () => {
render(<App />);
expect(screen.getByText('车辆服务中台')).toBeInTheDocument();
expect(screen.getByText('一车一服务 / 多源归并')).toBeInTheDocument();
expect(screen.getByText('车辆工作台')).toBeInTheDocument();
expect(screen.getByText('查询分析')).toBeInTheDocument();
expect(screen.getByText('告警运维')).toBeInTheDocument();
expect(screen.getAllByText('运营驾驶舱').length).toBeGreaterThanOrEqual(1);
});