Selaa lähdekoodia

feat(StatusMonitoring): 勤务/干线圆点按类型分流弹窗 + 悬浮提示

  总览/路口地图会渲染勤务路线、干线协调的折线圆点,但点击/hover 一律被当成
  真实路口处理:点击走 showCrossingDetailDialogs(mock 对未知 id 兜底造数据,
  弹出标题为「勤务路线路口-x-x-x」的伪路口详情),hover 弹出空的路口总览卡。

  改为在 handleMapCrossingClick / handleMapCrossingMouseover 顶部按 marker 类型
  (mapData.name)优先分流,置于按-Tab 分流之前,一处覆盖总览/路口等所有 Tab:
  - 勤务圆点 → showSpecialDutyDalogs(用 taskId)→ 特勤详情弹窗
  - 干线圆点 → findTrunkMenuNode 匹配后 showTrunkLineDalogs → 干线绿波弹窗
  - 真实路口 name 为控制方式,永不命中,原逻辑零改动

  hover 新增轻量悬浮卡 RouteHoverTip:勤务按 taskId 查 tableData 取真实任务名
  (如「科技论坛会」)与状态、干线显示真实干线名 + 「干线协调」。

  - 新增 src/components/ui/RouteHoverTip.vue,并在 DashboardLayout 注册
  - TongzhouTrafficMap 勤务圆点 config 补 dutyState(hover 状态退化用)
画安 1 viikko sitten
vanhempi
commit
93d6f2be1b

+ 2 - 2
src/components/TongzhouTrafficMap.vue

@@ -866,8 +866,8 @@ export default {
 
             const isPastDot = dotDist <= splitDist;
             const dotConfig = isPastDot
-              ? { ...config, color: '#5A5A5A', id: `MOCK-D-${lineIdx}-${segmentIdx}-${idx}`, road: `勤务路线路口-${lineIdx}-${segmentIdx}-${idx}`, taskId: line.taskId }
-              : { ...config, id: `MOCK-D-${lineIdx}-${segmentIdx}-${idx}`, road: `勤务路线路口-${lineIdx}-${segmentIdx}-${idx}`, taskId: line.taskId };
+              ? { ...config, color: '#5A5A5A', id: `MOCK-D-${lineIdx}-${segmentIdx}-${idx}`, road: `勤务路线路口-${lineIdx}-${segmentIdx}-${idx}`, taskId: line.taskId, dutyState: line.dutyState }
+              : { ...config, id: `MOCK-D-${lineIdx}-${segmentIdx}-${idx}`, road: `勤务路线路口-${lineIdx}-${segmentIdx}-${idx}`, taskId: line.taskId, dutyState: line.dutyState };
             const dotType = isPastDot && markerType === 'normal' ? 'passed' : markerType;
             const marker = this.createTrafficLightMarker([lng, lat], dotConfig, dotType);
             if (marker) overlays.push(marker);

+ 57 - 0
src/components/ui/RouteHoverTip.vue

@@ -0,0 +1,57 @@
+<template>
+  <div class="route-hover-tip">
+    <span class="rt-dot" :style="{ background: dotColor }"></span>
+    <span class="rt-text">{{ text }}</span>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'RouteHoverTip',
+  props: {
+    // '勤务路线' | '干线协调'
+    kind: { type: String, default: '' },
+    // 勤务:待执行/执行中/已完成;干线:干线协调
+    status: { type: String, default: '' },
+  },
+  computed: {
+    dotColor() {
+      const map = {
+        // 真实勤务任务状态
+        '未开始': '#3B82F6',
+        '进行中': '#13C373',
+        '已完成': '#7A7A7A',
+        // dutyState 退化文案
+        '待执行': '#3B82F6',
+        '执行中': '#13C373',
+        // 干线
+        '干线协调': '#13C373',
+      };
+      return map[this.status] || '#3B82F6';
+    },
+    text() {
+      // 干线没有逐点状态,展示类型;勤务展示任务状态
+      return this.kind === '干线协调' ? '干线协调' : this.status;
+    }
+  }
+};
+</script>
+
+<style scoped>
+.route-hover-tip {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #e0e8f0;
+  font-size: 14px;
+}
+.rt-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  flex-shrink: 0;
+}
+.rt-text {
+  white-space: nowrap;
+}
+</style>

+ 2 - 0
src/layouts/DashboardLayout.vue

@@ -111,6 +111,7 @@ import ChangePassword from '@/components/ui/ChangePassword.vue';
 import CrossingMultiView from '@/components/ui/CrossingMultiView.vue';
 import CrossingDetailHeader from '@/components/ui/CrossingDetailHeader.vue';
 import OfflineTip from '@/components/ui/OfflineTip.vue';
+import RouteHoverTip from '@/components/ui/RouteHoverTip.vue';
 import DetectorTable from '@/components/ui/DetectorTable.vue';
 import CameraVideoDialog from '@/components/ui/CameraVideoDialog.vue';
 import SchemeStageEditDialog from '@/components/ui/SchemeStageEditDialog.vue';
@@ -145,6 +146,7 @@ export default {
         CrossingMultiView,
         CrossingDetailHeader,
         OfflineTip,
+        RouteHoverTip,
         DetectorTable,
         CameraVideoDialog,
         SchemeStageEditDialog

+ 50 - 0
src/views/StatusMonitoring.vue

@@ -293,6 +293,11 @@ export default {
                 return;
             }
             console.log(nodeData);
+            // 路线 marker(勤务/干线):显示轻量悬浮提示,不弹路口总览卡
+            if (mapData.name === '勤务路线' || mapData.name === '干线协调') {
+                this.showRouteHoverTip(mapData, nodeData);
+                return;
+            }
             if (this.activeLeftTab === 'overview') { // 总览
                 this.showOverviewDalogs(nodeData);
             } else if (this.activeLeftTab === 'crossing') { // 路口
@@ -306,12 +311,45 @@ export default {
             const id = mapData.id || (mapData.position[0] + mapData.position[1]);
             // 关闭离线提示小弹窗
             this.$refs.layout.handleDialogClose('offline_tip_' + id);
+            // 关闭勤务/干线悬浮提示
+            this.$refs.layout.handleDialogClose('route_tip_' + id);
             if (this.activeLeftTab === 'overview') { // 总览
                 this.$refs.layout.handleDialogClose('crossing3_' + id);
             } else if (this.activeLeftTab === 'crossing') { // 路口
                 this.$refs.layout.handleDialogClose('crossing3_' + id);
             }
         },
+        // 勤务/干线路线 marker 的轻量悬浮提示(名称走标题,状态走 body)
+        showRouteHoverTip(mapData, nodeData) {
+            const isDuty = mapData.name === '勤务路线';
+            const dutyLabelMap = { pending: '待执行', active: '执行中', done: '已完成' };
+            // 勤务:按 taskId 查真实勤务任务(名称/状态),与特勤列表/详情一致;查不到再退化
+            const task = isDuty
+                ? (this.tableData || []).find(t => String(t.id) === String(mapData.taskId))
+                : null;
+            // 名称:干线用真实干线名(road),勤务用真实任务名
+            const name = isDuty
+                ? (task?.name || (mapData.taskId != null ? `勤务任务 ${mapData.taskId}` : '勤务路线'))
+                : (mapData.road || '干线协调');
+            const status = isDuty
+                ? (task?.status || dutyLabelMap[mapData.dutyState] || '勤务路线')
+                : '干线协调';
+            this.$refs.layout.openDialog({
+                id: 'route_tip_' + nodeData.id,
+                title: name,
+                component: 'RouteHoverTip',
+                width: 240,
+                height: 96,
+                center: false,
+                showClose: false,
+                noPadding: false,
+                draggable: false,
+                resizable: false,
+                enableDblclickExpand: false,
+                position: { x: (nodeData.pixelX || 950) + 10, y: nodeData.pixelY || 430 },
+                data: { kind: mapData.name, status },
+            });
+        },
         // 处理地图点击事件
         handleMapCrossingClick(mapData, lnglat, pixel) {
             console.log('父组件接收到了地图路口点击事件:', mapData);
@@ -334,6 +372,18 @@ export default {
                 pixelX: pixel ? Math.round(pixel.x / scale) : 950,
                 pixelY: pixel ? Math.round(pixel.y / scale) : 430,
             }
+            // 非真实路口的路线 marker(勤务/干线):按类型直达对应弹窗,绕过路口详情(覆盖总览/路口等所有 Tab)
+            if (mapData.name === '勤务路线') {
+                this.showSpecialDutyDalogs({ ...nodeData, id: mapData.taskId != null ? mapData.taskId : nodeData.id });
+                return;
+            }
+            if (mapData.name === '干线协调') {
+                const matched = this.findTrunkMenuNode(mapData.id);
+                this.showTrunkLineDalogs(matched
+                    ? { id: matched.id, label: matched.label, intersections: matched.intersections, distances: matched.distances }
+                    : { ...nodeData });
+                return;
+            }
             // 干线marker点击时,从菜单数据中匹配对应干线
             if (this.activeLeftTab === 'trunkLine' && mapData.id) {
                 const matched = this.findTrunkMenuNode(mapData.id);