Просмотр исходного кода

路口详情:检测器弹窗在多窗口布局下保持位置/尺寸/层级正确

  - SmartDialog 加 bringToFrontOnMousedown prop(默认 true),onRootMousedown 按开关决定是否升 z;新增
  defaultWidth/Height watcher,prop 变化即时重算 w/h
  - dialogManager.openDialog existing 分支:bringToFrontOnMousedown=false 的容器型 dialog 重入不再升 z;同时允许更新
  position/width/height/center,让父容器 rect 重算后能即时同步
  - DashboardLayout 把 bringToFrontOnMousedown 传给 SmartDialog
  - StatusMonitoring/TrunkCoordination/SpecialSituationMonitoring 的 openCrossingMultiView 配置加
  bringToFrontOnMousedown:false,避免 multi-view 容器在内部点击或重入更新时盖住其上的检测器弹窗
  - IntersectionMapVideos.handleResize 在检测器模式下重调 openDetectorDialog,让弹窗跟随 .detail-panel-right rect
  变化即时调整位置和尺寸
画安 недель назад: 3
Родитель
Сommit
68fd1f3bcc

+ 6 - 0
src/components/ui/IntersectionMapVideos.vue

@@ -292,6 +292,12 @@ export default {
         this.toggleVisible = true;
         this.toggleScale = Math.min(1, Math.max(0.55, containerWidth / 600));
       }
+
+      // multi-view 布局变化(增删面板/expand 切换)会让 .detail-panel-right 重排,
+      // 检测器弹窗若已开着,跟随重新定位+尺寸。重入 openDialog 走 existing 分支只更新位置尺寸。
+      if (this.displayMode === 'detector') {
+        this.openDetectorDialog();
+      }
     },
 
     createRoadArm(x, y, rotation) {

+ 18 - 2
src/components/ui/SmartDialog.vue

@@ -1,5 +1,5 @@
 <template>
-  <div v-show="visible" class="smart-dialog" :style="dialogStyle" @mousedown="bringToFront" @dblclick="handleDoubleClick" :class="{ 'no-padding': noPadding }">
+  <div v-show="visible" class="smart-dialog" :style="dialogStyle" @mousedown="onRootMousedown" @dblclick="handleDoubleClick" :class="{ 'no-padding': noPadding }">
     <div class="dialog-header" :class="{ 'is-draggable': draggable }" @mousedown="startDrag" v-if="title">
       <div class="title-content">
         <slot name="header">
@@ -46,7 +46,10 @@ export default {
     defaultHeight: { type: [Number, String], default: 250 },
     minWidth: { type: Number, default: 200 },
     minHeight: { type: Number, default: 150 },
-    enableDblclickExpand: { type: Boolean, default: false }, 
+    enableDblclickExpand: { type: Boolean, default: false },
+    // 任何 mousedown 是否自动把本弹窗顶到最前。默认 true,
+    // 容器型外层弹窗(如 multi-view)传 false,避免内部点击带它一起升 z 把覆盖在它之上的子弹窗盖掉。
+    bringToFrontOnMousedown: { type: Boolean, default: true },
   },
   data() {
     return {
@@ -101,6 +104,13 @@ export default {
           this.calculatePosition();
         }
       }
+    },
+    // defaultWidth/Height 变化(如父容器 rect 重算后传入新尺寸)即时反应
+    defaultWidth(val) {
+      this.w = this._parseSize(val, window.innerWidth);
+    },
+    defaultHeight(val) {
+      this.h = this._parseSize(val, window.innerHeight);
     }
   },
   methods: {
@@ -165,6 +175,12 @@ export default {
       globalZIndex++;
       this.zIndex = globalZIndex;
     },
+
+    /** 根节点 mousedown 入口:仅在 bringToFrontOnMousedown=true 时把本弹窗顶到最前。
+     *  显式调用(如 openDialog 走 existing 分支)不受此开关影响。 */
+    onRootMousedown() {
+      if (this.bringToFrontOnMousedown) this.bringToFront();
+    },
     
     calculatePosition() {
       this.$nextTick(() => {

+ 1 - 0
src/layouts/DashboardLayout.vue

@@ -68,6 +68,7 @@
             :resizable="dialog.resizable"
             :minWidth="dialog.minWidth"
             :minHeight="dialog.minHeight"
+            :bringToFrontOnMousedown="dialog.bringToFrontOnMousedown"
             @close="handleDialogClose(dialog.id)"
             @expand="handleDialogExpand(dialog)">
 

+ 20 - 2
src/mixins/dialogManager.js

@@ -33,13 +33,26 @@ export default {
             );
 
             if (existingDialog) {
-                // 用 $set 更新 data/title,保证 Vue 2 响应式
+                // 用 $set 更新 data/title/位置尺寸,保证 Vue 2 响应式
                 if (config.data !== undefined) {
                     this.$set(existingDialog, 'data', config.data);
                 }
                 if (config.title !== undefined) {
                     this.$set(existingDialog, 'title', config.title);
                 }
+                // 允许重入时调整定位/尺寸(如父容器布局变化后重新计算 rect 后传入)
+                if (config.position !== undefined) {
+                    this.$set(existingDialog, 'position', config.position);
+                }
+                if (config.width !== undefined) {
+                    this.$set(existingDialog, 'width', config.width);
+                }
+                if (config.height !== undefined) {
+                    this.$set(existingDialog, 'height', config.height);
+                }
+                if (config.center !== undefined) {
+                    this.$set(existingDialog, 'center', config.center);
+                }
                 existingDialog.visible = true;
 
                 this.$nextTick(() => {
@@ -53,7 +66,11 @@ export default {
                         );
 
                         if (targetComponent) {
-                            targetComponent.bringToFront();
+                            // 容器型 dialog(bringToFrontOnMousedown=false)走"被动容器"语义,
+                            // 重入更新 data 时也不顶 z,避免覆盖在它之上的子 dialog 被遮挡。
+                            if (existingDialog.bringToFrontOnMousedown !== false) {
+                                targetComponent.bringToFront();
+                            }
                             if (typeof targetComponent.playShake === 'function') {
                                 targetComponent.playShake();
                             }
@@ -80,6 +97,7 @@ export default {
                 minWidth: config.minWidth || 200,
                 minHeight: config.minHeight || 150,
                 showClose: config.showClose !== false,
+                bringToFrontOnMousedown: config.bringToFrontOnMousedown !== false,
                 headerComponent: config.headerComponent || null,
                 headerProps: config.headerProps || {},
                 data: config.data || {},

+ 1 - 0
src/views/SpecialSituationMonitoring.vue

@@ -442,6 +442,7 @@ export default {
                 noPadding: true,
                 enableDblclickExpand: false,
                 draggable: false,
+                bringToFrontOnMousedown: false,
                 data: {
                     crossings: [...this.crossingSelections],
                     maxSlots: this.maxCrossingSlots,

+ 1 - 0
src/views/StatusMonitoring.vue

@@ -564,6 +564,7 @@ export default {
                 noPadding: true,
                 enableDblclickExpand: false,
                 draggable: false,
+                bringToFrontOnMousedown: false,
                 data: {
                     crossings: [...this.crossingSelections],
                     maxSlots: this.maxCrossingSlots,

+ 1 - 0
src/views/TrunkCoordination.vue

@@ -446,6 +446,7 @@ export default {
                 noPadding: true,
                 enableDblclickExpand: false,
                 draggable: false,
+                bringToFrontOnMousedown: false,
                 data: {
                     crossings: [...this.crossingSelections],
                     maxSlots: this.maxCrossingSlots,