feat(commandCenter):kpi新增呼单乘客、今日完单,样式调整
Co-Authored-By: ai-coding <ai-coding@devgenie.local>
| | |
| | | label: '车辆位置', |
| | | active: store.layerVisibility.vehicle, |
| | | toggle: () => store.toggleLayer('vehicle', !store.layerVisibility.vehicle), |
| | | iconHtml: '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><circle cx="12" cy="8" r="4"/><path d="M12 14c-4 0-8 2-8 4v2h16v-2c0-2-4-4-8-4z"/></svg>', |
| | | iconHtml: '', |
| | | }, |
| | | { |
| | | key: 'passengerCall', |
| | | label: '呼单乘客', |
| | | active: store.layerVisibility.passengerCall, |
| | | toggle: () => store.toggleLayer('passengerCall', !store.layerVisibility.passengerCall), |
| | | iconHtml: '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><circle cx="9" cy="7" r="3"/><path d="M9 12c-3.5 0-7 1.5-7 3.5V17h14v-1.5c0-2-3.5-3.5-7-3.5z"/><circle cx="17" cy="9" r="2.5"/><path d="M17 13c-1.5 0-3 .5-3.5 1.1.7.6 1.2 1.3 1.3 2.1H22v-1c0-1.2-2.5-2.2-5-2.2z"/></svg>', |
| | | iconHtml: '<span style="font-size:14px;">👤</span>', |
| | | }, |
| | | { |
| | | key: 'point', |
| | | label: '运营点位', |
| | | active: store.layerVisibility.point, |
| | | toggle: () => store.toggleLayer('point', !store.layerVisibility.point), |
| | | iconHtml: '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>', |
| | | iconHtml: '<span style="font-size:14px;">📍</span>', |
| | | }, |
| | | { |
| | | key: 'fence', |
| | | label: '运营区域', |
| | | active: store.layerVisibility.fence, |
| | | toggle: () => store.toggleLayer('fence', !store.layerVisibility.fence), |
| | | iconHtml: '<svg viewBox="0 0 24 24" width="14" height="14"><rect x="3" y="5" width="18" height="14" rx="2" fill="currentColor" fill-opacity="0.15" stroke="currentColor" stroke-width="1.5"/></svg>', |
| | | iconHtml: '<span style="font-size:13px;font-weight:600;">区</span>', |
| | | }, |
| | | ]) |
| | | |
| | |
| | | flex-shrink: 0; |
| | | |
| | | &--vehicle { |
| | | background: #7c4dff; |
| | | background: #2563eb; |
| | | color: #fff; |
| | | } |
| | | |
| | | &--passengerCall { |
| | | background: #ff9800; |
| | | color: #fff; |
| | | background: #f59e0b; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | &--point { |
| | | background: #e8e8e8; |
| | | color: #555; |
| | | background: transparent; |
| | | } |
| | | |
| | | &--fence { |
| | | background: #e3f2fd; |
| | | color: #1890ff; |
| | | background: #fff; |
| | | border: 2px solid #2563eb; |
| | | color: #2563eb; |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | defineProps<{ isOnline: boolean }>() |
| | | |
| | | const emit = defineEmits<{ filterVehicle: [group: MonitorGroup | null] }>() |
| | | const emit = defineEmits<{ |
| | | filterVehicle: [group: MonitorGroup | null] |
| | | togglePassengerCall: [active: boolean] |
| | | }>() |
| | | |
| | | const store = useCommandCenterStore() |
| | | const kpi = computed(() => store.kpi) |
| | | |
| | | const activeGroup = ref<MonitorGroup | null>(null) |
| | | const passengerCallActive = ref(false) |
| | | |
| | | function handleKpiClick(group: MonitorGroup) { |
| | | // 点击车辆分组时,取消呼单乘客激活态 |
| | | if (passengerCallActive.value) { |
| | | passengerCallActive.value = false |
| | | emit('togglePassengerCall', false) |
| | | } |
| | | |
| | | if (activeGroup.value === group) { |
| | | activeGroup.value = null |
| | | emit('filterVehicle', null) |
| | |
| | | activeGroup.value = group |
| | | emit('filterVehicle', group) |
| | | } |
| | | } |
| | | |
| | | function handlePassengerCallClick() { |
| | | // 点击呼单乘客时,取消车辆分组筛选 |
| | | if (activeGroup.value) { |
| | | activeGroup.value = null |
| | | emit('filterVehicle', null) |
| | | } |
| | | |
| | | passengerCallActive.value = !passengerCallActive.value |
| | | emit('togglePassengerCall', passengerCallActive.value) |
| | | } |
| | | </script> |
| | | |
| | |
| | | :active="activeGroup === 'ABNORMAL'" |
| | | @click="handleKpiClick('ABNORMAL')" |
| | | /> |
| | | <KpiCard |
| | | title="呼单乘客" |
| | | :value="store.passengerCalls.length" |
| | | color="#f59e0b" |
| | | subtitle="等待派车" |
| | | :active="passengerCallActive" |
| | | @click="handlePassengerCallClick" |
| | | /> |
| | | <KpiCard |
| | | title="今日完单" |
| | | :value="kpi?.todayCompletedOrders ?? 0" |
| | | color="#2563eb" |
| | | subtitle="基础统计" |
| | | /> |
| | | </div> |
| | | <div class="status-header__right"> |
| | | <Clock /> |
| | |
| | | /** KPI 车辆过滤 */ |
| | | function handleFilterVehicle(group: MonitorGroup | null) { |
| | | vehicleLayer?.filterByGroup(group) |
| | | if (group) { |
| | | // 点击车辆分组时,隐藏呼单乘客图层,确保车辆图层可见 |
| | | store.toggleLayer('passengerCall', false) |
| | | store.toggleLayer('vehicle', true) |
| | | } |
| | | else { |
| | | // 取消筛选时,恢复呼单乘客图层 |
| | | store.toggleLayer('passengerCall', true) |
| | | } |
| | | } |
| | | |
| | | /** 呼单乘客模式切换:隐藏车辆,显示呼单乘客信息 */ |
| | | function handleTogglePassengerCall(active: boolean) { |
| | | if (active) { |
| | | // 隐藏车辆图层,确保呼单乘客图层可见 |
| | | store.toggleLayer('vehicle', false) |
| | | store.toggleLayer('passengerCall', true) |
| | | } |
| | | else { |
| | | // 恢复车辆图层和呼单乘客图层 |
| | | store.toggleLayer('vehicle', true) |
| | | store.toggleLayer('passengerCall', true) |
| | | } |
| | | } |
| | | |
| | | onMounted(async () => { |
| | |
| | | <div v-if="!isOnline" class="command-center__offline-banner"> |
| | | 连接已断开,正在尝试重连... |
| | | </div> |
| | | <StatusHeader :is-online="isOnline" @filter-vehicle="handleFilterVehicle" /> |
| | | <StatusHeader :is-online="isOnline" @filter-vehicle="handleFilterVehicle" @toggle-passenger-call="handleTogglePassengerCall" /> |
| | | <div class="command-center__body"> |
| | | <div class="command-center__city-selector"> |
| | | <NSelect |
| | |
| | | |
| | | const marker = new AMap.Marker({ |
| | | position: [call.startLon, call.startLat], |
| | | content: '<div style="width:20px;height:20px;font-size:16px;line-height:20px;text-align:center;">🙋</div>', |
| | | offset: new AMap.Pixel(-10, -10), |
| | | content: '<div style="width:26px;height:26px;border-radius:50%;background:#f59e0b;display:flex;align-items:center;justify-content:center;font-size:14px;line-height:1;">👤</div>', |
| | | offset: new AMap.Pixel(-13, -13), |
| | | zIndex: LAYER_Z_INDEX.PASSENGER_CALL, |
| | | }) |
| | | marker._callData = call |