4 İşlemeler 5aa6fc8419 ... 93d6f2be1b

Yazar SHA1 Mesaj Tarih
  画安 93d6f2be1b feat(StatusMonitoring): 勤务/干线圆点按类型分流弹窗 + 悬浮提示 1 ay önce
  画安 8501906290 fix(SignalTimingChart): 相位图横向拖动不再触发外层弹窗拖动 1 ay önce
  画安 22d184fc2c fix(SmartDialog): 合成 resize 不再把已拖动弹窗拉回视口内 1 ay önce
  画安 edb348b522 修改404跳转到home 1 ay önce

+ 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>

+ 1 - 1
src/components/ui/SignalTimingChart.vue

@@ -4,7 +4,7 @@
          - stageCount ≤ 4: 内宽 = wrap 宽度, 与容器同宽不滚
          - stageCount > 4: 内宽 = wrap × (realMax / S4End), 多出部分横滚露出 S5+
        注意: 不用 :style 绑定, 因为 Vue 响应式刷 DOM 是异步的, 会让 initChart 抓到旧宽度. -->
-  <div ref="scrollWrap" class="chart-scroll">
+  <div ref="scrollWrap" class="chart-scroll" data-no-drag>
     <div ref="chartRef" class="chart-container"></div>
   </div>
 </template>

+ 11 - 0
src/components/ui/SmartDialog.vue

@@ -88,6 +88,9 @@ export default {
     this.currentScale = this.getRealScale();
     this.w = this._parseSize(this.defaultWidth, window.innerWidth);
     this.h = this._parseSize(this.defaultHeight, window.innerHeight);
+    // 记录上次窗口尺寸,用于区分"真实 resize"与"合成 resize"(如 openDialog 广播)
+    this._lastWinW = window.innerWidth;
+    this._lastWinH = window.innerHeight;
   },
   mounted() {
     window.addEventListener('resize', this._onWindowResize);
@@ -149,6 +152,14 @@ export default {
     
     _onWindowResize() {
       setTimeout(() => {
+        // 仅在窗口尺寸真正变化时才重算/夹取位置。
+        // openDialog 等会派发"合成 resize"(尺寸未变),此时不应把已被用户拖动的弹窗强行拉回视口内。
+        if (window.innerWidth === this._lastWinW && window.innerHeight === this._lastWinH) {
+          return;
+        }
+        this._lastWinW = window.innerWidth;
+        this._lastWinH = window.innerHeight;
+
         // 重新获取当前最新比例
         const newScale = this.getRealScale();
         const scaleRatio = newScale / this.currentScale;

+ 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

+ 1 - 1
src/views/NotFound.vue

@@ -20,7 +20,7 @@ export default {
   name: 'NotFound',
   methods: {
     goBack() {
-      this.$router.push('/login');
+      this.$router.push('/home');
     }
   }
 }

+ 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);