feat(platform): add vehicle pool quick views

This commit is contained in:
lingniu
2026-07-05 23:41:32 +08:00
parent 07336e08bc
commit fbd9982427
3 changed files with 164 additions and 0 deletions

View File

@@ -782,6 +782,49 @@ export function Vehicles({
onClick: copyCustomerServicePackage
}
];
const offlineVehicleCount = Math.max((summary?.totalVehicles ?? pagination.total) - (summary?.onlineVehicles ?? 0), 0);
const vehiclePoolQuickViews = [
{
title: '在线运营',
value: `${(summary?.onlineVehicles ?? 0).toLocaleString()} 在线`,
detail: '进入地图查看在线车辆分布、最近位置和运营范围。',
action: '打开地图',
color: (summary?.onlineVehicles ?? 0) > 0 ? 'green' as const : 'orange' as const,
onClick: () => onOpenMap?.({ ...serviceScopeFilters, online: 'online' })
},
{
title: '离线断链',
value: `${offlineVehicleCount.toLocaleString()} 离线`,
detail: '筛出离线车辆,结合最后上报时间判断断链影响。',
action: '排查离线',
color: offlineVehicleCount > 0 ? 'orange' as const : 'green' as const,
onClick: () => applyFilters({ ...filters, online: 'offline' })
},
{
title: '无来源车辆',
value: `${(summary?.noDataVehicles ?? 0).toLocaleString()} 无来源`,
detail: '优先确认平台转发、订阅和车辆身份是否可归并。',
action: '平台转发',
color: (summary?.noDataVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const,
onClick: () => applyFilters({ ...filters, serviceStatus: 'no_data' })
},
{
title: '身份维护',
value: `${(summary?.unboundVehicles ?? 0).toLocaleString()} 未绑定`,
detail: '补齐车牌、手机号与 VIN 关系,保证客户能按车辆查数据。',
action: '维护绑定',
color: (summary?.unboundVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const,
onClick: () => applyFilters({ ...filters, bindingStatus: 'unbound' })
},
{
title: '报表交付',
value: `${rows.length.toLocaleString()} 当前页`,
detail: '导出当前车辆池清单,作为客户报表和后续轨迹统计入口。',
action: '导出清单',
color: rows.length > 0 ? 'blue' as const : 'grey' as const,
onClick: exportVehicles
}
];
const vehicleDecisionItems = [
{
title: '在线车辆',
@@ -1270,6 +1313,32 @@ export function Vehicles({
))}
</div>
</section>
<section className="vp-vehicle-pool-quick-views" aria-label="车辆池快捷视图">
<div className="vp-vehicle-pool-quick-copy">
<Space wrap>
<Tag color="blue"></Tag>
<Tag color={filters.keyword?.trim() ? 'green' : 'grey'}>{filters.keyword?.trim() ? '单车范围' : '车辆池'}</Tag>
</Space>
<strong>线线</strong>
<span></span>
</div>
<div className="vp-vehicle-pool-quick-grid">
{vehiclePoolQuickViews.map((item) => (
<button
key={item.title}
type="button"
className="vp-vehicle-pool-quick-item"
onClick={item.onClick}
aria-label={`车辆池快捷视图 ${item.title} ${item.value} ${item.action}`}
>
<Tag color={item.color}>{item.title}</Tag>
<strong>{item.value}</strong>
<span>{item.detail}</span>
<em>{item.action}</em>
</button>
))}
</div>
</section>
<Card bordered title="车辆服务台">
<div className="vp-vehicle-service-desk">
<div className="vp-vehicle-service-summary">

View File

@@ -4877,6 +4877,92 @@ button.vp-realtime-command-item:focus-visible {
line-height: 20px;
}
.vp-vehicle-pool-quick-views {
margin-bottom: 16px;
border: 1px solid rgba(255, 136, 0, 0.18);
border-radius: var(--vp-radius);
background: #ffffff;
display: grid;
grid-template-columns: minmax(280px, 0.55fr) minmax(0, 1.45fr);
overflow: hidden;
}
.vp-vehicle-pool-quick-copy {
padding: 18px;
border-right: 1px solid var(--vp-border);
background: #fffaf3;
display: grid;
gap: 12px;
align-content: center;
}
.vp-vehicle-pool-quick-copy strong {
color: var(--vp-text);
font-size: 20px;
font-weight: 700;
line-height: 28px;
word-break: break-word;
}
.vp-vehicle-pool-quick-copy span {
color: var(--vp-text-muted);
font-size: 13px;
line-height: 20px;
}
.vp-vehicle-pool-quick-grid {
padding: 14px;
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 10px;
}
.vp-vehicle-pool-quick-item {
min-height: 142px;
padding: 12px;
border: 1px solid var(--vp-border);
border-radius: var(--vp-radius);
background: #fffefd;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
display: grid;
gap: 8px;
align-content: start;
transition: border-color 0.16s ease, box-shadow 0.16s ease, background 0.16s ease;
}
.vp-vehicle-pool-quick-item:hover,
.vp-vehicle-pool-quick-item:focus-visible {
border-color: rgba(255, 136, 0, 0.42);
background: #fffaf3;
box-shadow: 0 0 0 3px rgba(255, 136, 0, 0.08);
outline: none;
}
.vp-vehicle-pool-quick-item strong {
color: var(--vp-text);
font-size: 20px;
font-weight: 700;
line-height: 26px;
word-break: break-word;
}
.vp-vehicle-pool-quick-item span {
color: var(--vp-text-muted);
font-size: 12px;
line-height: 18px;
}
.vp-vehicle-pool-quick-item em {
color: var(--vp-primary);
font-size: 13px;
font-style: normal;
font-weight: 700;
line-height: 20px;
}
.vp-vehicle-lookup-strip {
display: grid;
gap: 14px;
@@ -11828,6 +11914,8 @@ button.vp-realtime-command-item:focus-visible {
.vp-alert-closure-steps,
.vp-vehicle-asset-console,
.vp-vehicle-asset-console-grid,
.vp-vehicle-pool-quick-views,
.vp-vehicle-pool-quick-grid,
.vp-vehicle-service-desk,
.vp-vehicle-service-grid,
.vp-vehicle-lookup-head,

View File

@@ -2682,6 +2682,13 @@ test('shows vehicle service result summary on vehicle list filters', async () =>
expect(screen.getByRole('button', { name: '车辆资产运营台 在线车辆 73 在线 打开实时' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆资产运营台 待处理 13 待处理 异常优先' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆资产运营台 服务交付 2 当前页 复制交付包' })).toBeInTheDocument();
expect(screen.getByText('车辆池快捷视图')).toBeInTheDocument();
expect(screen.getByText('按客户服务场景一键切换车辆池:在线运营、离线断链、无来源、身份维护和报表交付。')).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆池快捷视图 在线运营 73 在线 打开地图' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆池快捷视图 离线断链 108 离线 排查离线' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆池快捷视图 无来源车辆 4 无来源 平台转发' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆池快捷视图 身份维护 9 未绑定 维护绑定' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆池快捷视图 报表交付 2 当前页 导出清单' })).toBeInTheDocument();
expect(screen.getByText('车辆服务台')).toBeInTheDocument();
expect(screen.getByText('客户视角')).toBeInTheDocument();
expect(screen.getByText('客户查车路径')).toBeInTheDocument();