feat(platform): add customer vehicle lookup path

This commit is contained in:
lingniu
2026-07-05 03:25:14 +08:00
parent 46d2421511
commit 3097fd59eb
3 changed files with 167 additions and 0 deletions

View File

@@ -273,6 +273,43 @@ function vehicleIdentityMaintenanceText({
return lines.join('\n');
}
function vehicleCustomerLookupText({
filters,
rows,
total,
summary
}: {
filters: Record<string, string>;
rows: VehicleCoverageRow[];
total: number;
summary: VehicleCoverageSummary | null;
}) {
const serviceMode = filters.keyword?.trim() ? '单车服务' : '车辆池';
const scopeHash = buildAppHash({ page: 'vehicles', keyword: filters.keyword, protocol: filters.protocol, filters });
const singleVehicleFilters = filters.keyword?.trim()
? { keyword: filters.keyword.trim(), ...(filters.protocol ? { protocol: filters.protocol } : {}) }
: {};
const mapFilters = { ...filters, online: filters.online || 'online' };
const lines = [
'【客户查车路径】',
`服务模式:${serviceMode}`,
`当前筛选:${vehicleFilterSummary(filters).join('') || '全部车辆'}`,
`当前页:${rows.length.toLocaleString()} 辆 / 当前筛选 ${total.toLocaleString()}`,
`在线车辆:${(summary?.onlineVehicles ?? 0).toLocaleString()};待绑定:${(summary?.unboundVehicles ?? 0).toLocaleString()};暂无来源:${(summary?.noDataVehicles ?? 0).toLocaleString()};档案不完整:${(summary?.archiveIncompleteVehicles ?? 0).toLocaleString()}`,
'',
serviceMode === '单车服务'
? '推荐路径:先看车辆档案和实时监控,再按需要进入轨迹回放、里程统计、历史数据导出。'
: '推荐路径:先筛在线/离线/无来源/待绑定车辆,再进入地图态势或导出处置清单。',
`车辆服务:${appURL(scopeHash)}`,
`实时监控:${appURL(buildAppHash({ page: 'realtime', ...singleVehicleFilters }))}`,
`车辆地图:${appURL(buildAppHash({ page: 'map', keyword: filters.keyword, protocol: filters.protocol, filters: mapFilters }))}`,
`轨迹回放:${appURL(buildAppHash({ page: 'history', ...singleVehicleFilters, filters: { tab: 'location' } }))}`,
`里程统计:${appURL(buildAppHash({ page: 'mileage', ...singleVehicleFilters }))}`,
`历史数据:${appURL(buildAppHash({ page: 'history', ...singleVehicleFilters, filters: { tab: 'raw', includeFields: 'true' } }))}`
];
return lines.join('\n');
}
type VehicleActionRecommendation = {
label: string;
color: 'green' | 'orange' | 'red';
@@ -557,6 +594,31 @@ export function Vehicles({
}
copyText(vehicleIdentityMaintenanceText({ filters, rows, total: pagination.total }), '车辆身份维护清单');
};
const copyCustomerLookupPath = () => {
copyText(vehicleCustomerLookupText({ filters, rows, total: pagination.total, summary }), '客户查车路径');
};
const customerLookupSteps = [
{
label: '车辆身份',
value: filters.keyword?.trim() || 'VIN / 车牌 / 手机号',
detail: '确认车辆归属、车牌、手机号和 OEM客户问题先落到一辆车。'
},
{
label: '在线状态',
value: `${(summary?.onlineVehicles ?? 0).toLocaleString()} 在线`,
detail: '判断车辆是否仍在上报,离线车辆进入断链和来源证据排查。'
},
{
label: '轨迹与位置',
value: filters.keyword?.trim() ? '可回放' : '先选车辆',
detail: '单车进入轨迹回放,车辆池进入地图态势查看位置分布。'
},
{
label: '数据交付',
value: `${rows.length.toLocaleString()} 当前页`,
detail: '导出车辆清单再进入历史页交付轨迹、RAW 和字段证据。'
}
];
const serviceDeskActions = useMemo(() => [
{
label: '实时监控',
@@ -789,6 +851,34 @@ export function Vehicles({
</div>
</div>
</Card>
<Card bordered title="客户查车路径" style={{ marginTop: 16 }}>
<div className="vp-vehicle-lookup-strip">
<div className="vp-vehicle-lookup-head">
<div>
<Space wrap>
<Tag color={filters.keyword?.trim() ? 'green' : 'blue'}>{filters.keyword?.trim() ? '单车服务' : '车辆池服务'}</Tag>
<Tag color="grey">{vehicleFilterSummary(filters).join(' / ') || '全部车辆'}</Tag>
</Space>
<strong>{filters.keyword?.trim() ? '围绕一辆车完成监控、回放、统计和导出' : '从车辆池筛出客户要看的车,再进入单车服务'}</strong>
<span>32960808MQTT </span>
</div>
<Space wrap>
<Button size="small" theme="solid" type="primary" disabled={!onOpenMap} onClick={() => onOpenMap?.({ ...serviceScopeFilters, online: 'online' })}></Button>
<Button size="small" disabled={!onOpenHistory || !filters.keyword?.trim()} onClick={() => onOpenHistory?.({ ...serviceScopeFilters, tab: 'location' })}></Button>
<Button size="small" icon={<IconCopy />} onClick={copyCustomerLookupPath}></Button>
</Space>
</div>
<div className="vp-vehicle-lookup-grid">
{customerLookupSteps.map((step) => (
<div key={step.label} className="vp-vehicle-lookup-item">
<Tag color="blue">{step.label}</Tag>
<strong>{step.value}</strong>
<span>{step.detail}</span>
</div>
))}
</div>
</div>
</Card>
<Card bordered title="车辆服务决策板" style={{ marginTop: 16 }}>
<div className="vp-vehicle-decision-grid">
{vehicleDecisionItems.map((item) => (