feat(platform): add vehicle service desk
This commit is contained in:
@@ -383,15 +383,19 @@ async function copyVehicleShareURL() {
|
||||
export function Vehicles({
|
||||
onOpenVehicle,
|
||||
onOpenQuality,
|
||||
onOpenMap,
|
||||
onOpenRealtime,
|
||||
onOpenHistory,
|
||||
onOpenMileage,
|
||||
onFiltersChange,
|
||||
initialFilters = {}
|
||||
}: {
|
||||
onOpenVehicle: (vin: string, protocol?: string) => void;
|
||||
onOpenQuality?: (filters: Record<string, string>) => void;
|
||||
onOpenMap?: (filters: Record<string, string>) => void;
|
||||
onOpenRealtime?: (filters: Record<string, string>) => void;
|
||||
onOpenHistory?: (filters: Record<string, string>) => void;
|
||||
onOpenMileage?: (filters: Record<string, string>) => void;
|
||||
onFiltersChange?: (filters: Record<string, string>) => void;
|
||||
initialFilters?: Record<string, string>;
|
||||
}) {
|
||||
@@ -490,6 +494,12 @@ export function Vehicles({
|
||||
const filterSummary = vehicleFilterSummary(filters);
|
||||
const pageVerdictStats = useMemo(() => currentPageVerdictStats(rows), [rows]);
|
||||
const pageActionStats = useMemo(() => currentPageActionStats(rows), [rows]);
|
||||
const serviceScopeFilters = useMemo(() => {
|
||||
const nextFilters: Record<string, string> = {};
|
||||
if (filters.keyword?.trim()) nextFilters.keyword = filters.keyword.trim();
|
||||
if (filters.protocol?.trim()) nextFilters.protocol = filters.protocol.trim();
|
||||
return nextFilters;
|
||||
}, [filters.keyword, filters.protocol]);
|
||||
|
||||
const load = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
|
||||
setLoading(true);
|
||||
@@ -547,6 +557,48 @@ export function Vehicles({
|
||||
}
|
||||
copyText(vehicleIdentityMaintenanceText({ filters, rows, total: pagination.total }), '车辆身份维护清单');
|
||||
};
|
||||
const serviceDeskActions = useMemo(() => [
|
||||
{
|
||||
label: '实时监控',
|
||||
value: `${(summary?.onlineVehicles ?? 0).toLocaleString()} 在线`,
|
||||
detail: '查看车辆在线、速度、最后上报和来源状态。',
|
||||
color: 'green' as const,
|
||||
disabled: !onOpenRealtime,
|
||||
onClick: () => onOpenRealtime?.({ ...serviceScopeFilters, online: 'online' })
|
||||
},
|
||||
{
|
||||
label: '车辆地图',
|
||||
value: '地图态势',
|
||||
detail: '把当前筛选车辆带入实时地图,优先看位置分布。',
|
||||
color: 'blue' as const,
|
||||
disabled: !onOpenMap,
|
||||
onClick: () => onOpenMap?.({ ...serviceScopeFilters, online: 'online' })
|
||||
},
|
||||
{
|
||||
label: '轨迹回放',
|
||||
value: filters.keyword?.trim() ? '单车轨迹' : '选择车辆',
|
||||
detail: filters.keyword?.trim() ? '按当前车辆进入历史轨迹复盘。' : '先选择车辆后进入精确轨迹回放。',
|
||||
color: filters.keyword?.trim() ? 'blue' as const : 'grey' as const,
|
||||
disabled: !onOpenHistory || !filters.keyword?.trim(),
|
||||
onClick: () => onOpenHistory?.({ ...serviceScopeFilters, tab: 'location' })
|
||||
},
|
||||
{
|
||||
label: '里程统计',
|
||||
value: filters.keyword?.trim() ? '区间核对' : '车辆口径',
|
||||
detail: filters.keyword?.trim() ? '按当前车辆核对区间里程和日里程。' : '输入车辆后查看里程闭合统计。',
|
||||
color: filters.keyword?.trim() ? 'green' as const : 'grey' as const,
|
||||
disabled: !onOpenMileage || !filters.keyword?.trim(),
|
||||
onClick: () => onOpenMileage?.(serviceScopeFilters)
|
||||
},
|
||||
{
|
||||
label: '数据导出',
|
||||
value: `${rows.length.toLocaleString()} 当前页`,
|
||||
detail: '导出当前页车辆清单,后续可继续导出轨迹和原始记录。',
|
||||
color: rows.length > 0 ? 'blue' as const : 'grey' as const,
|
||||
disabled: rows.length === 0,
|
||||
onClick: exportVehicles
|
||||
}
|
||||
], [exportVehicles, filters.keyword, onOpenHistory, onOpenMap, onOpenMileage, onOpenRealtime, rows.length, serviceScopeFilters, summary?.onlineVehicles]);
|
||||
|
||||
useEffect(() => {
|
||||
setFilters(initialFilters);
|
||||
@@ -654,7 +706,42 @@ export function Vehicles({
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="车辆服务" description="以 VIN 为主对象维护车辆身份、车辆覆盖、在线状态和绑定状态" />
|
||||
<PageHeader title="车辆服务" description="围绕车辆完成实时监控、地图查看、轨迹回放、里程统计和数据导出,协议来源只作为证据过滤条件" />
|
||||
<Card bordered title="车辆服务台">
|
||||
<div className="vp-vehicle-service-desk">
|
||||
<div className="vp-vehicle-service-summary">
|
||||
<Space wrap>
|
||||
<Tag color="blue">客户视角</Tag>
|
||||
<Tag color={filters.keyword?.trim() ? 'green' : 'grey'}>{filters.keyword?.trim() ? '单车服务' : '车辆池'}</Tag>
|
||||
</Space>
|
||||
<strong>{filters.keyword?.trim() || '全部车辆'}</strong>
|
||||
<span>
|
||||
当前筛选覆盖 {(summary?.totalVehicles ?? pagination.total).toLocaleString()} 辆车,在线 {(summary?.onlineVehicles ?? 0).toLocaleString()} 辆。
|
||||
客户先看车辆能不能服务,再进入地图、轨迹、里程和导出。
|
||||
</span>
|
||||
<Space wrap>
|
||||
<Button size="small" theme="solid" type="primary" disabled={!onOpenRealtime} onClick={() => onOpenRealtime?.({ ...serviceScopeFilters, online: 'online' })}>查看在线车辆</Button>
|
||||
<Button size="small" disabled={!onOpenMap} onClick={() => onOpenMap?.({ ...serviceScopeFilters, online: 'online' })}>打开地图</Button>
|
||||
<Button size="small" onClick={copyDispatchList}>复制服务清单</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div className="vp-vehicle-service-grid">
|
||||
{serviceDeskActions.map((item) => (
|
||||
<button
|
||||
key={item.label}
|
||||
className="vp-vehicle-service-action"
|
||||
type="button"
|
||||
disabled={item.disabled}
|
||||
onClick={item.onClick}
|
||||
>
|
||||
<Tag color={item.color}>{item.label}</Tag>
|
||||
<strong>{item.value}</strong>
|
||||
<span>{item.detail}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card bordered>
|
||||
<Form key={JSON.stringify(filters)} initValues={filters} layout="horizontal" onSubmit={(values) => {
|
||||
const nextFilters = values as Record<string, string>;
|
||||
|
||||
Reference in New Issue
Block a user