Selaa lähdekoodia

将Dialog管理逻辑从各视图抽离到DashboardLayout统一管理;新建 dialogManager mixin,抽取activeDialogs/openDialog/handleDialogClose/clearDialogs共用逻辑;DashboardLayout 引入 mixin 并统一渲染SmartDialog,支持 headerComponent 自定义 header;;三个视图(StatusMonitoring/DataAnalysis/SpecialSituationMonitoring)删除重复的弹窗模板和方法,改用$refs.layout 调用;CrossingListPanel 的 $emit('crossing-view-detail')改为 onViewDetail 回调 prop;SecurityRoutePanelSwitchSmall 的 $emit('close-dialog')改为 onClose 回调 prop;路口弹窗关闭时通过 onClose 回调联动关闭两个RingDonutChart 弹窗;

画安 1 kuukausi sitten
vanhempi
commit
38722ab5a5

+ 9 - 1
src/components/ui/CrossingListPanel.vue

@@ -53,6 +53,12 @@ export default {
         SignalTimingChart,
         TechPagination
     },
+    props: {
+        onViewDetail: {
+            type: Function,
+            default: null
+        }
+    },
     data() {
         return {
             loading: false, // 控制加载遮罩层的显示与隐藏
@@ -196,7 +202,9 @@ export default {
                 console.log('执行重启:', row.name);
             } else if (type === 'view') {
                 console.log('查看详情:', row.name);
-                this.$emit('crossing-view-detail', row);
+                if (this.onViewDetail) {
+                    this.onViewDetail(row);
+                }
             }
         }
     }

+ 7 - 1
src/components/ui/SecurityRoutePanelSwitchSmall.vue

@@ -11,7 +11,7 @@
         </div>
         <div>
           <span>2026.2.18执行</span>
-          <span class="close-btn" @click="$emit('close-dialog');">x</span>
+          <span class="close-btn" @click="onClose && onClose()">x</span>
         </div>
       </div>
     </div>
@@ -87,6 +87,12 @@ export default {
   components: {
     IntersectionMap,
   },
+  props: {
+    onClose: {
+      type: Function,
+      default: null
+    }
+  },
   data() {
     return {
       intersectionData: {}, 

+ 67 - 1
src/layouts/DashboardLayout.vue

@@ -40,18 +40,77 @@
             </main>
         </div>
 
-        <slot name="dialogs"></slot>
+        <!-- 弹窗层:由 Layout 统一渲染 -->
+        <SmartDialog
+            ref="dialogs"
+            v-for="dialog in activeDialogs"
+            :key="dialog.id"
+            :id="dialog.id"
+            :visible.sync="dialog.visible"
+            :title="dialog.title"
+            :defaultWidth="dialog.width || 400"
+            :defaultHeight="dialog.height || 300"
+            :center="dialog.center !== false"
+            :position="dialog.position"
+            :showClose="dialog.showClose"
+            :enableDblclickExpand="dialog.enableDblclickExpand"
+            :noPadding="dialog.noPadding"
+            @close="handleDialogClose(dialog.id)"
+            @expand="handleDialogExpand(dialog)">
+
+            <template #header v-if="dialog.headerComponent">
+                <component :is="dialog.headerComponent" v-bind="dialog.headerProps" />
+            </template>
+
+            <component :is="dialog.componentName" v-bind="dialog.data" />
+        </SmartDialog>
 
     </div>
 </template>
 
 <script>
 import BottomDock from '@/components/ui/BottomDock.vue';
+import SmartDialog from '@/components/ui/SmartDialog.vue';
+import dialogManager from '@/mixins/dialogManager';
+
+// 注册所有可能在弹窗中使用的内容组件
+import DeviceStatusPanel from '@/components/ui/DeviceStatusPanel.vue';
+import SecurityRoutePanel from '@/components/ui/SecurityRoutePanel.vue';
+import IntersectionMapVideos from '@/components/ui/IntersectionMapVideos.vue';
+import TrafficTimeSpace from '@/components/ui/TrafficTimeSpace.vue';
+import RingDonutChart from '@/components/ui/RingDonutChart.vue';
+import CrossingPanel from '@/components/ui/CrossingPanel.vue';
+import CrossingDetailPanel from '@/components/ui/CrossingDetailPanel.vue';
+import CrossingListPanel from '@/components/ui/CrossingListPanel.vue';
+import SecurityRoutePanelSwitch from '@/components/ui/SecurityRoutePanelSwitch.vue';
+import SecurityRoutePanelSwitchSmall from '@/components/ui/SecurityRoutePanelSwitchSmall.vue';
 
 export default {
     name: 'DashboardLayout',
+    mixins: [dialogManager],
     components: {
         BottomDock,
+        SmartDialog,
+        DeviceStatusPanel,
+        SecurityRoutePanel,
+        IntersectionMapVideos,
+        TrafficTimeSpace,
+        RingDonutChart,
+        CrossingPanel,
+        CrossingDetailPanel,
+        CrossingListPanel,
+        SecurityRoutePanelSwitch,
+        SecurityRoutePanelSwitchSmall,
+    },
+    provide() {
+        return {
+            dialogManager: {
+                openDialog: this.openDialog,
+                closeDialog: this.handleDialogClose,
+                clearDialogs: this.clearDialogs,
+                getDialogs: () => this.activeDialogs,
+            }
+        };
     },
     props: {
         // 接收外部传入的 class,用于动态切换 CSS 网格布局
@@ -65,6 +124,13 @@ export default {
         return {
             title: '交通信号控制平台',
         }
+    },
+    methods: {
+        handleDialogExpand(dialog) {
+            if (dialog.data && typeof dialog.data.onExpand === 'function') {
+                dialog.data.onExpand(dialog.data);
+            }
+        }
     }
 }
 </script>

+ 99 - 0
src/mixins/dialogManager.js

@@ -0,0 +1,99 @@
+/**
+ * Dialog 管理 Mixin
+ * 提供 activeDialogs 数组管理、openDialog / handleDialogClose / clearDialogs 方法
+ */
+export default {
+    data() {
+        return {
+            activeDialogs: []
+        };
+    },
+    methods: {
+        /**
+         * 打开一个弹窗
+         * @param {Object} config - 弹窗配置
+         * @param {string|number} config.id - 唯一标识
+         * @param {string} config.title - 标题
+         * @param {string} config.component - 内部组件名
+         * @param {number} [config.width=450] - 宽度
+         * @param {number} [config.height=300] - 高度
+         * @param {boolean} [config.center=true] - 是否居中
+         * @param {Object} [config.position] - 自定义坐标 {x, y}
+         * @param {boolean} [config.showClose=true] - 是否显示关闭按钮
+         * @param {boolean} [config.noPadding=false] - 是否无边距
+         * @param {boolean} [config.enableDblclickExpand=false] - 是否启用双击展开
+         * @param {string} [config.headerComponent] - 自定义 header 组件名
+         * @param {Object} [config.headerProps] - header 组件的 props
+         * @param {Object} [config.data] - 传给内部组件的业务数据
+         */
+        openDialog(config) {
+            // 防止重复打开同一个弹窗
+            const existingDialog = this.activeDialogs.find(
+                d => String(d.id) === String(config.id)
+            );
+
+            if (existingDialog) {
+                existingDialog.visible = true;
+
+                this.$nextTick(() => {
+                    if (this.$refs.dialogs) {
+                        const dialogRefs = Array.isArray(this.$refs.dialogs)
+                            ? this.$refs.dialogs
+                            : [this.$refs.dialogs];
+
+                        const targetComponent = dialogRefs.find(
+                            vm => String(vm.id) === String(config.id)
+                        );
+
+                        if (targetComponent) {
+                            targetComponent.bringToFront();
+                            if (typeof targetComponent.playShake === 'function') {
+                                targetComponent.playShake();
+                            }
+                        }
+                    }
+                });
+
+                return;
+            }
+
+            this.activeDialogs.push({
+                id: config.id,
+                title: config.title,
+                componentName: config.component,
+                visible: true,
+                width: config.width || 450,
+                height: config.height || 300,
+                center: config.center !== false,
+                position: config.position || null,
+                noPadding: config.noPadding !== false,
+                enableDblclickExpand: config.enableDblclickExpand !== false,
+                showClose: config.showClose !== false,
+                headerComponent: config.headerComponent || null,
+                headerProps: config.headerProps || {},
+                data: config.data || {},
+                onClose: config.onClose || null
+            });
+
+            window.dispatchEvent(new Event('resize'));
+        },
+
+        /**
+         * 关闭并移除弹窗
+         */
+        handleDialogClose(dialogId) {
+            const dialog = this.activeDialogs.find(d => d.id === dialogId);
+            if (dialog && typeof dialog.onClose === 'function') {
+                dialog.onClose();
+            }
+            this.activeDialogs = this.activeDialogs.filter(d => d.id !== dialogId);
+        },
+
+        /**
+         * 清空所有弹窗
+         */
+        clearDialogs() {
+            this.activeDialogs = [];
+        }
+    }
+};

+ 2 - 76
src/views/DataAnalysis.vue

@@ -1,5 +1,5 @@
 <template>
-    <DashboardLayout>
+    <DashboardLayout ref="layout">
         <!-- 天气 -->
         <template #header-left>
             <WeatherWidget />
@@ -49,16 +49,6 @@
             
         </template>
 
-        <template #dialogs>
-            <SmartDialog ref="dialogs" v-for="dialog in activeDialogs" :key="dialog.id" :id="dialog.id" :visible.sync="dialog.visible"
-                :title="dialog.title" :defaultWidth="dialog.width || 400" :defaultHeight="dialog.height || 300"
-                :center="dialog.center !== false" :position="dialog.position" :showClose="dialog.showClose"
-                @close="handleDialogClose(dialog.id)">
-
-                <component :is="dialog.componentName" v-bind="dialog.data"></component>
-            </SmartDialog>
-        </template>
-
     </DashboardLayout>
 </template>
 
@@ -69,9 +59,7 @@ import DateTimeWidget from '@/components/ui/DateTimeWidget.vue';
 import TechTabs from '@/components/ui/TechTabs.vue';
 import TechTabPane from '@/components/ui/TechTabPane.vue';
 import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
-import SmartDialog from '@/components/ui/SmartDialog.vue';
 import MenuItem from '@/components/ui/MenuItem.vue';
-import TrafficTimeSpace from '@/components/ui/TrafficTimeSpace.vue';
 
 import { makeTrafficTimeSpaceData } from '@/mock/data';
 
@@ -84,15 +72,11 @@ export default {
         DateTimeWidget,
         TechTabs,
         TechTabPane,
-        SmartDialog,
         TongzhouTrafficMap,
         MenuItem,
-        TrafficTimeSpace
     },
     data() {
         return {
-            // 弹窗相关数据
-            activeDialogs: [],
             // 左侧边栏数据
             activeLeftTab: 'overview',
             menuData: [
@@ -231,69 +215,11 @@ export default {
             // 这里可以根据 nodeData 的经纬度来控制地图组件的视角
             this.testOpenSecurityRoute(nodeData);
         },
-        openDialog(config) {
-            // 1. 检查这个弹窗是否已经在数组中了
-            const existingDialog = this.activeDialogs.find(
-                d => String(d.id) === String(config.id)
-            );
-
-            if (existingDialog) {
-                existingDialog.visible = true;
-
-                this.$nextTick(() => {
-                    if (this.$refs.dialogs) {
-                        const dialogRefs = Array.isArray(this.$refs.dialogs)
-                            ? this.$refs.dialogs
-                            : [this.$refs.dialogs];
-
-                        const targetComponent = dialogRefs.find(
-                            vm => String(vm.id) === String(config.id)
-                        );
-
-                        if (targetComponent) {
-                            targetComponent.bringToFront();
-                            if (typeof targetComponent.playShake === 'function') {
-                                targetComponent.playShake();
-                            }
-                        } else {
-                            console.warn('没找到对应弹窗:', config.id);
-                        }
-                    }
-                });
-
-                return;
-            }
-
-            // 2. 如果不存在,则 push 一个新的弹窗对象进去
-            this.activeDialogs.push({
-                id: config.id,                     // 唯一标识 (例如路口ID 'node-101')
-                title: config.title,               // 弹窗左上角标题
-                componentName: config.component,   // 要加载的内部组件名
-                visible: true,                     // 默认可见
-                width: config.width || 450,        // 自定义宽度
-                height: config.height || 300,      // 自定义高度
-                center: config.center !== false,   // 是否居中显示
-                position: config.position || null, // 自定义坐标 {x, y}
-                showClose: config.showClose !== false, // 是否显示关闭按钮
-                data: config.data || {}            // 传给内部组件的业务数据
-            });
-
-            window.dispatchEvent(new Event('resize'));
-        },
-
-        /**
-         * 关闭弹窗的回调
-         */
-        handleDialogClose(dialogId) {
-            // 性能优化:当用户点击 ✕ 关闭弹窗时,将其从数组中彻底移除,销毁内部组件释放内存
-            this.activeDialogs = this.activeDialogs.filter(d => d.id !== dialogId);
-        },
-
         // ================= 测试用例:模拟各种点击行为 =================
 
         // 模拟 1:打开特勤安保路线面板
         testOpenSecurityRoute(data) {
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: data.id, // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
                 title: data.label,
                 component: 'TrafficTimeSpace',

+ 8 - 59
src/views/SpecialSituationMonitoring.vue

@@ -1,5 +1,5 @@
 <template>
-    <DashboardLayout>
+    <DashboardLayout ref="layout">
         <!-- 天气 -->
         <template #header-left>
             <WeatherWidget />
@@ -49,16 +49,6 @@
             
         </template>
 
-        <template #dialogs>
-            <SmartDialog v-for="dialog in activeDialogs" :key="dialog.id" :id="dialog.id" :visible.sync="dialog.visible"
-                :title="dialog.title" :defaultWidth="dialog.width || 400" :defaultHeight="dialog.height || 300"
-                :center="dialog.center !== false" :position="dialog.position" :showClose="dialog.showClose"
-                @close="handleDialogClose(dialog.id)">
-
-                <component :is="dialog.componentName" v-bind="dialog.data"></component>
-            </SmartDialog>
-        </template>
-
     </DashboardLayout>
 </template>
 
@@ -69,10 +59,7 @@ import DateTimeWidget from '@/components/ui/DateTimeWidget.vue';
 import TechTabs from '@/components/ui/TechTabs.vue';
 import TechTabPane from '@/components/ui/TechTabPane.vue';
 import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
-import SmartDialog from '@/components/ui/SmartDialog.vue';
 import MenuItem from '@/components/ui/MenuItem.vue';
-import SecurityRoutePanelSwitch from '@/components/ui/SecurityRoutePanelSwitch.vue';
-import SecurityRoutePanelSwitchSmall from '@/components/ui/SecurityRoutePanelSwitchSmall.vue';
 
 
 export default {
@@ -83,16 +70,11 @@ export default {
         DateTimeWidget,
         TechTabs,
         TechTabPane,
-        SmartDialog,
         TongzhouTrafficMap,
         MenuItem,
-        SecurityRoutePanelSwitch,
-        SecurityRoutePanelSwitchSmall
     },
     data() {
         return {
-            // 弹窗相关数据
-            activeDialogs: [],
             // 左侧边栏数据
             activeLeftTab: 'overview',
             menuData: [
@@ -232,66 +214,33 @@ export default {
             this.testOpenSecurityRoute();
             this.testOpenSecurityRoute2();
         },
-        openDialog(config) {
-            // 1. 防止重复打开同一个弹窗 (根据 id 判断)
-            const existingDialog = this.activeDialogs.find(d => d.id === config.id);
-
-            if (existingDialog) {
-                // 如果已经存在,只是将其设为可见 (SmartDialog 内部会自动把它 bringToFront 置顶)
-                existingDialog.visible = true;
-                return;
-            }
-
-            // 2. 如果不存在,则 push 一个新的弹窗对象进去
-            this.activeDialogs.push({
-                id: config.id,                     // 唯一标识 (例如路口ID 'node-101')
-                title: config.title,               // 弹窗左上角标题
-                componentName: config.component,   // 要加载的内部组件名
-                visible: true,                     // 默认可见
-                width: config.width || 450,        // 自定义宽度
-                height: config.height || 300,      // 自定义高度
-                center: config.center !== false,   // 是否居中显示
-                position: config.position || null, // 自定义坐标 {x, y}
-                showClose: config.showClose !== false, // 是否显示关闭按钮
-                data: config.data || {}            // 传给内部组件的业务数据
-            });
-
-            window.dispatchEvent(new Event('resize'));
-        },
-
-        /**
-         * 关闭弹窗的回调
-         */
-        handleDialogClose(dialogId) {
-            // 性能优化:当用户点击 ✕ 关闭弹窗时,将其从数组中彻底移除,销毁内部组件释放内存
-            this.activeDialogs = this.activeDialogs.filter(d => d.id !== dialogId);
-        },
-
         // ================= 测试用例:模拟各种点击行为 =================
 
         // 模拟 1:打开特勤安保路线面板
         testOpenSecurityRoute() {
-            this.openDialog({
-                id: 'dev-security-route', // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
+            this.$refs.layout.openDialog({
+                id: 'dev-security-route',
                 title: '',
                 component: 'SecurityRoutePanelSwitch',
                 width: 1000,
                 height: 500,
                 center: true,
-                // position: { x: 400, y: 450 },
             });
         },
 
         // 模拟 2:打开特勤安保路线小面板
         testOpenSecurityRoute2() {
-            this.openDialog({
-                id: 'dev-security-route-small', // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
+            this.$refs.layout.openDialog({
+                id: 'dev-security-route-small',
                 title: '',
                 component: 'SecurityRoutePanelSwitchSmall',
                 width: 550,
                 height: 300,
                 center: false,
                 position: { x: 1400, y: 550 },
+                data: {
+                    onClose: () => this.$refs.layout.closeDialog('dev-security-route-small')
+                }
             });
         },
 

+ 29 - 93
src/views/StatusMonitoring.vue

@@ -1,5 +1,5 @@
 <template>
-    <DashboardLayout layoutClass="special-situation-monitoring">
+    <DashboardLayout ref="layout" layoutClass="special-situation-monitoring">
         <template #header-left>
 
         </template>
@@ -49,38 +49,17 @@
 
         </template>
 
-        <template #dialogs>
-            <SmartDialog v-for="dialog in activeDialogs" :key="dialog.id" :id="dialog.id" :visible.sync="dialog.visible"
-                :title="dialog.title" :defaultWidth="dialog.width || 400" :defaultHeight="dialog.height || 300"
-                :center="dialog.center !== false" :position="dialog.position" :showClose="dialog.showClose"
-                :enableDblclickExpand="dialog.enableDblclickExpand" :noPadding="dialog.noPadding"
-                @close="handleDialogClose(dialog.id)" @expand="handleDoubleClickExpend(dialog.data)">
-
-                <component :is="dialog.componentName" v-bind="dialog.data" @crossing-view-detail="handleCrossingViewDetail"></component>
-            </SmartDialog>
-        </template>
-
     </DashboardLayout>
 </template>
 
 <script>
 import DashboardLayout from '@/layouts/DashboardLayout.vue';
-import WeatherWidget from '@/components/ui/WeatherWidget.vue';
 import DateTimeWidget from '@/components/ui/DateTimeWidget.vue';
 import TechTabs from '@/components/ui/TechTabs.vue';
 import TechTabPane from '@/components/ui/TechTabPane.vue';
 import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
-import SmartDialog from '@/components/ui/SmartDialog.vue';
-import DeviceStatusPanel from '@/components/ui/DeviceStatusPanel.vue';
-import SecurityRoutePanel from '@/components/ui/SecurityRoutePanel.vue';
-import IntersectionMapVideos from '@/components/ui/IntersectionMapVideos.vue';
-import TrafficTimeSpace from '@/components/ui/TrafficTimeSpace.vue';
 import MenuItem from '@/components/ui/MenuItem.vue';
-import RingDonutChart from '@/components/ui/RingDonutChart.vue';
-import CrossingPanel from '@/components/ui/CrossingPanel.vue';
-import CrossingDetailPanel from '@/components/ui/CrossingDetailPanel.vue';
 import ButtonGroup from '@/components/ui/ButtonGroup.vue';
-import CrossingListPanel from '@/components/ui/CrossingListPanel.vue';
 import { getIntersectionData, makeTrafficTimeSpaceData } from '@/mock/data';
 
 
@@ -88,27 +67,15 @@ export default {
     name: "HomePage",
     components: {
         DashboardLayout,
-        WeatherWidget,
         DateTimeWidget,
         TechTabs,
         TechTabPane,
-        SmartDialog,
         TongzhouTrafficMap,
         MenuItem,
-        DeviceStatusPanel,
-        SecurityRoutePanel,
-        IntersectionMapVideos,
-        TrafficTimeSpace,
-        RingDonutChart,
-        CrossingPanel,
-        CrossingDetailPanel,
         ButtonGroup,
-        CrossingListPanel
     },
     data() {
         return {
-            // 弹窗相关数据
-            activeDialogs: [],
             // 左侧边栏数据
             activeLeftTab: 'overview',
             menuData: [
@@ -245,7 +212,7 @@ export default {
 
     },
     mounted() {
-        // this.openDialog({
+        // this.$refs.layout.openDialog({
         //         id: 'test', // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
         //         title: 'dddd',
         //         component: 'CrossingListPanel',
@@ -268,7 +235,7 @@ export default {
         // 处理tab点击
         handleTabClick(nodeData) {
             console.log('父组件接收到了tab点击事件:', nodeData);
-            this.activeDialogs = []; // 清空全部弹窗
+            this.$refs.layout.clearDialogs(); // 清空全部弹窗
         },
         // 处理菜单点击
         handleMenuClick(nodeData) {
@@ -291,54 +258,13 @@ export default {
             }
 
         },
-        // 处理弹窗双击事件
+        // 处理弹窗双击展开(通过 onExpand 回调从 Layout 传入)
         handleDoubleClickExpend(nodeData) {
             console.log('处理弹窗双击事件', nodeData);
-            // 根据Tab来显示不同的弹窗内容
-            if (this.activeLeftTab === 'overview') { // 总览
-
-            } else if (this.activeLeftTab === 'crossing') { // 路口
+            if (this.activeLeftTab === 'crossing') {
                 this.showCrossingDetailDialogs(nodeData);
-            } else if (this.activeLeftTab === 'trunkLine') { // 干线
-
-            } else if (this.activeLeftTab === 'specialDuty') { // 特勤
-
             }
         },
-        openDialog(config) {
-            // 1. 防止重复打开同一个弹窗 (根据 id 判断)
-            const existingDialog = this.activeDialogs.find(d => d.id === config.id);
-
-            if (existingDialog) {
-                // 如果已经存在,只是将其设为可见 (SmartDialog 内部会自动把它 bringToFront 置顶)
-                existingDialog.visible = true;
-                return;
-            }
-
-            // 2. 如果不存在,则 push 一个新的弹窗对象进去
-            this.activeDialogs.push({
-                id: config.id,                     // 唯一标识 (例如路口ID 'node-101')
-                title: config.title,               // 弹窗左上角标题
-                componentName: config.component,   // 要加载的内部组件名
-                visible: true,                     // 默认可见
-                width: config.width || 450,        // 自定义宽度
-                height: config.height || 300,      // 自定义高度
-                center: config.center !== false,   // 是否居中显示
-                position: config.position || null, // 自定义坐标 {x, y}
-                noPadding: config.noPadding !== false, // 无边距
-                enableDblclickExpand: config.enableDblclickExpand !== false, // 是否启用双击
-                showClose: config.showClose !== false, // 是否显示关闭按钮
-                data: config.data || {}            // 传给内部组件的业务数据
-            });
-        },
-
-        /**
-         * 关闭弹窗的回调
-         */
-        handleDialogClose(dialogId) {
-            // 性能优化:当用户点击 ✕ 关闭弹窗时,将其从数组中彻底移除,销毁内部组件释放内存
-            this.activeDialogs = this.activeDialogs.filter(d => d.id !== dialogId);
-        },
         // 显示总览弹窗组
         showOverviewDalogs(nodeData) {
             console.log('显示干线弹窗组', nodeData.id, nodeData.label);
@@ -349,7 +275,7 @@ export default {
 
             // 列表模式弹窗
             if (this.currentView === 'list-mode') {
-                this.openDialog({
+                this.$refs.layout.openDialog({
                     id: 'crossing-list' + nodeData.id, // 这里的 ID 可以根据实际业务场景动态生成
                     title: nodeData.label,
                     component: 'CrossingListPanel',
@@ -360,12 +286,14 @@ export default {
                     // position: { x: 750, y: 130 },
                     noPadding: false,
                     enableDblclickExpand: false,
-                    data: {}
+                    data: {
+                        onViewDetail: (rowData) => this.handleCrossingViewDetail(rowData)
+                    }
                 });
                 return;
             }
 
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'crossing_' + nodeData.id, // 这里的 ID 可以根据实际业务场景动态生成
                 title: '',
                 component: 'RingDonutChart',
@@ -385,7 +313,7 @@ export default {
                 }
             });
 
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'crossing2_' + nodeData.id, // 这里的 ID 可以根据实际业务场景动态生成
                 title: '',
                 component: 'RingDonutChart',
@@ -408,7 +336,7 @@ export default {
             });
 
             // 路口弹窗
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'crossing3_' + nodeData.id, // 这里的 ID 可以根据实际业务场景动态生成
                 title: nodeData.label,
                 component: 'CrossingPanel',
@@ -418,7 +346,14 @@ export default {
                 showClose: true,
                 position: { x: 950, y: 430 },
                 noPadding: false,
-                data: nodeData
+                data: {
+                    ...nodeData,
+                    onExpand: (data) => this.handleDoubleClickExpend(data)
+                },
+                onClose: () => {
+                    this.$refs.layout.handleDialogClose('crossing_' + nodeData.id);
+                    this.$refs.layout.handleDialogClose('crossing2_' + nodeData.id);
+                }
             });
 
 
@@ -426,7 +361,7 @@ export default {
 
         showCrossingDetailDialogs(nodeData) {
             console.log('显示干线弹窗组', nodeData.id, nodeData.label);
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'crossing_detail' + nodeData.id, // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
                 title: nodeData.label || nodeData.name,
                 component: 'CrossingDetailPanel',
@@ -449,7 +384,7 @@ export default {
         showTrunkLineDalogs(nodeData) {
             console.log('显示干线弹窗组', nodeData.id, nodeData.label);
 
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: nodeData.id, // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
                 title: nodeData.label,
                 component: 'TrafficTimeSpace',
@@ -470,7 +405,7 @@ export default {
 
         // 模拟 1:打开设备状态面板
         testOpenDeviceStatus() {
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'device-status-node-101', // 这里的 ID 可以根据实际业务场景动态生成,例如 'node-101' 代表某个路口
                 title: '',
                 component: 'DeviceStatusPanel', // 对应 components 里注册的名字
@@ -481,7 +416,7 @@ export default {
                 showClose: false, // 是否显示关闭按钮
             });
 
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'device-status-node-102', // 这里的 ID 可以根据实际业务场景动态生成,例如 'node-101' 代表某个路口
                 title: '',
                 component: 'DeviceStatusPanel', // 对应 components 里注册的名字
@@ -495,7 +430,7 @@ export default {
 
         // 模拟 2:打开特勤安保路线面板
         testOpenSecurityRoute() {
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'dev-security-route', // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
                 title: '特勤安保路线 未开始 一级',
                 component: 'SecurityRoutePanel',
@@ -509,7 +444,7 @@ export default {
         // 模拟 3:打开本地协调控制面板
         testOpenSecurityRoute2() {
             const dialogId = 'dev-security-route2';
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: dialogId,
                 title: '长安街-府右街口 本地协调控制',
                 component: 'IntersectionMapVideos',
@@ -531,7 +466,8 @@ export default {
 
             // 异步获取数据后更新弹窗
             getIntersectionData().then(mapData => {
-                const dialog = this.activeDialogs.find(d => d.id === dialogId);
+                const dialogs = this.$refs.layout.getDialogs();
+                const dialog = dialogs.find(d => d.id === dialogId);
                 if (dialog) {
                     this.$set(dialog.data, 'mapData', mapData);
                 }
@@ -541,7 +477,7 @@ export default {
         // 模拟 4:打开新干线协调控制面板
         testOpenTrafficTimeSpace() {
             const tsData = makeTrafficTimeSpaceData();
-            this.openDialog({
+            this.$refs.layout.openDialog({
                 id: 'dev-traffic-time-space',
                 title: '新干线协调控制 早高峰',
                 component: 'TrafficTimeSpace',