|
|
@@ -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',
|