|
@@ -29,10 +29,10 @@
|
|
|
<TechTabPane label="干线" name="trunkLine" class="menu-scroll-view">
|
|
<TechTabPane label="干线" name="trunkLine" class="menu-scroll-view">
|
|
|
<MenuItem v-for="item in menuData" :key="item.id" :node="item" :level="0"
|
|
<MenuItem v-for="item in menuData" :key="item.id" :node="item" :level="0"
|
|
|
@node-click="handleMenuClick">
|
|
@node-click="handleMenuClick">
|
|
|
- <template #label="{ node }">
|
|
|
|
|
- <span v-if="node.children && node.children.length > 0">{{ node.label }}</span>
|
|
|
|
|
- <span v-else>{{ node.label }} 绿波带</span>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
+ <template #label="{ node }">
|
|
|
|
|
+ <span v-if="node.children && node.children.length > 0">{{ node.label }}</span>
|
|
|
|
|
+ <span v-else>{{ node.label }} 绿波带</span>
|
|
|
|
|
+ </template>
|
|
|
</MenuItem>
|
|
</MenuItem>
|
|
|
</TechTabPane>
|
|
</TechTabPane>
|
|
|
<TechTabPane label="特勤" name="specialDuty">
|
|
<TechTabPane label="特勤" name="specialDuty">
|
|
@@ -64,6 +64,9 @@ import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
|
|
|
import MenuItem from '@/components/ui/MenuItem.vue';
|
|
import MenuItem from '@/components/ui/MenuItem.vue';
|
|
|
import ButtonGroup from '@/components/ui/ButtonGroup.vue';
|
|
import ButtonGroup from '@/components/ui/ButtonGroup.vue';
|
|
|
import { makeTrafficTimeSpaceData } from '@/mock/data';
|
|
import { makeTrafficTimeSpaceData } from '@/mock/data';
|
|
|
|
|
+import testVideo1 from '@/assets/videos/video1.mp4';
|
|
|
|
|
+import testVideo2 from '@/assets/videos/video2.mp4';
|
|
|
|
|
+import testImg1 from '@/assets/test_img1.png';
|
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
@@ -226,12 +229,12 @@ export default {
|
|
|
mounted() {
|
|
mounted() {
|
|
|
// 组件挂载时检查路由
|
|
// 组件挂载时检查路由
|
|
|
this.checkRouteParams();
|
|
this.checkRouteParams();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 初始显示顶部图表(如果没有路由参数覆盖的话)
|
|
// 初始显示顶部图表(如果没有路由参数覆盖的话)
|
|
|
if (Object.keys(this.$route.query).length === 0) {
|
|
if (Object.keys(this.$route.query).length === 0) {
|
|
|
- this.showTopChartDalogs();
|
|
|
|
|
|
|
+ this.showTopChartDalogs();
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
// 模式切换
|
|
// 模式切换
|
|
@@ -297,7 +300,7 @@ export default {
|
|
|
} else if (this.activeLeftTab === 'trunkLine') { // 干线
|
|
} else if (this.activeLeftTab === 'trunkLine') { // 干线
|
|
|
// TODO: 干线Tab的顶部图表
|
|
// TODO: 干线Tab的顶部图表
|
|
|
} else if (this.activeLeftTab === 'specialDuty') { // 特勤
|
|
} else if (this.activeLeftTab === 'specialDuty') { // 特勤
|
|
|
- // TODO: 特勤Tab的顶部图表
|
|
|
|
|
|
|
+ this.openDutyDetailDialog();
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
// 显示总览弹窗组
|
|
// 显示总览弹窗组
|
|
@@ -453,9 +456,9 @@ export default {
|
|
|
showSpecialDutyDalogs(nodeData) {
|
|
showSpecialDutyDalogs(nodeData) {
|
|
|
console.log('显示干线弹窗组', nodeData.id, nodeData.label);
|
|
console.log('显示干线弹窗组', nodeData.id, nodeData.label);
|
|
|
},
|
|
},
|
|
|
- // === 新增:解析路由参数并执行对应操作 ===
|
|
|
|
|
|
|
+ // === 解析路由参数并执行对应操作 ===
|
|
|
checkRouteParams() {
|
|
checkRouteParams() {
|
|
|
- // 【修正】统一参数接收:特勤接收 id,路口接收 intersectionName 和 plan
|
|
|
|
|
|
|
+ // 统一参数接收:特勤接收 id,路口接收 intersectionName 和 plan
|
|
|
const { tab, action, id, } = this.$route.query;
|
|
const { tab, action, id, } = this.$route.query;
|
|
|
|
|
|
|
|
if (!tab) return; // 如果没有传递 tab 参数,说明是正常访问,不处理
|
|
if (!tab) return; // 如果没有传递 tab 参数,说明是正常访问,不处理
|
|
@@ -465,14 +468,14 @@ export default {
|
|
|
this.activeLeftTab = 'specialDuty'; // 切换到左侧【特勤】Tab
|
|
this.activeLeftTab = 'specialDuty'; // 切换到左侧【特勤】Tab
|
|
|
this.handleTabClick('specialDuty'); // 手动触发 Tab 切换事件,更新顶部图表
|
|
this.handleTabClick('specialDuty'); // 手动触发 Tab 切换事件,更新顶部图表
|
|
|
|
|
|
|
|
- // 【修正】这里判断的条件改为 id
|
|
|
|
|
|
|
+ // 这里判断的条件改为 id
|
|
|
if (action === 'open-dialog' && id) {
|
|
if (action === 'open-dialog' && id) {
|
|
|
this.$nextTick(() => {
|
|
this.$nextTick(() => {
|
|
|
this.openDutyDetailDialog(id); // 打开特勤弹窗
|
|
this.openDutyDetailDialog(id); // 打开特勤弹窗
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 2. 处理“关键路口”跳转
|
|
// 2. 处理“关键路口”跳转
|
|
|
else if (tab === 'crossing') {
|
|
else if (tab === 'crossing') {
|
|
|
this.activeLeftTab = 'crossing'; // 切换到左侧【路口】Tab
|
|
this.activeLeftTab = 'crossing'; // 切换到左侧【路口】Tab
|
|
@@ -490,24 +493,134 @@ export default {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- // === 新增:特勤详情弹窗 (你需要根据实际组件名替换) ===
|
|
|
|
|
- openDutyDetailDialog(dutyId) {
|
|
|
|
|
|
|
+ // === 特勤详情弹窗 (你需要根据实际组件名替换) ===
|
|
|
|
|
+ async openDutyDetailDialog(dutyId) {
|
|
|
console.log('准备打开特勤线路详情,ID:', dutyId);
|
|
console.log('准备打开特勤线路详情,ID:', dutyId);
|
|
|
- // 这里仿照你原有的开窗逻辑,打开对应的组件
|
|
|
|
|
|
|
+ // 1. 获取数据
|
|
|
|
|
+ const panelData = await this.fetchSpecialTaskData();
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 呼出弹窗
|
|
|
this.$refs.layout.openDialog({
|
|
this.$refs.layout.openDialog({
|
|
|
- id: 'special-duty-detail-' + dutyId,
|
|
|
|
|
- title: '勤务执行详情',
|
|
|
|
|
- component: 'SecurityRoutePanelSwitch', // 注意:请替换为你项目中真实的特勤弹窗组件名
|
|
|
|
|
- width: 1200,
|
|
|
|
|
- height: 600,
|
|
|
|
|
- center: true,
|
|
|
|
|
- showClose: true,
|
|
|
|
|
- data: {
|
|
|
|
|
- id: dutyId
|
|
|
|
|
|
|
+ id: 'special-task-dialog',
|
|
|
|
|
+ title: ' ', // 留空以隐藏默认标题,使用自定义 Header
|
|
|
|
|
+ width: 1400, // 弹窗宽一点,容纳 3 列
|
|
|
|
|
+ height: 700,
|
|
|
|
|
+ center: false,
|
|
|
|
|
+ showClose: false,
|
|
|
|
|
+ noPadding: true, // 去除默认内边距,让内部组件自己控制
|
|
|
|
|
+ position: {x: 200, y: 150},
|
|
|
|
|
+ // 挂载主体组件和数据
|
|
|
|
|
+ component: 'SpecialTaskMonitorPanel',
|
|
|
|
|
+ data: { panelData: panelData },
|
|
|
|
|
+
|
|
|
|
|
+ // 挂载自定义 Header 和数据
|
|
|
|
|
+ headerComponent: 'TaskMonitorHeader',
|
|
|
|
|
+ headerProps: {
|
|
|
|
|
+ taskData: panelData.taskInfo,
|
|
|
|
|
+ onEndTask: () => {
|
|
|
|
|
+ console.log('点击了结束任务');
|
|
|
|
|
+ this.$refs.layout.handleDialogClose('special-task-dialog');
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
|
|
+ // 模拟从后端拉取数据
|
|
|
|
|
+ async fetchSpecialTaskData() {
|
|
|
|
|
+ // 模拟 API 请求延迟
|
|
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
|
|
+
|
|
|
|
|
+ // 这是后端返回的完整数据结构
|
|
|
|
|
+ return {
|
|
|
|
|
+ // 1. 头部任务信息
|
|
|
|
|
+ taskInfo: {
|
|
|
|
|
+ name: '北京路演唱会特勤路线',
|
|
|
|
|
+ time: '12:00-14:00',
|
|
|
|
|
+ manager: '张飞',
|
|
|
|
|
+ level: '一级',
|
|
|
|
|
+ status: '进行中',
|
|
|
|
|
+ statusColor: '#ff4d4f' // 红色状态灯
|
|
|
|
|
+ },
|
|
|
|
|
+ // 2. 视频流列表 (支持有源和无源)
|
|
|
|
|
+ videos: [
|
|
|
|
|
+ { id: 1, url: testVideo1 }, // 有视频
|
|
|
|
|
+ { id: 2, url: testVideo2 }, // 无视频,展示占位
|
|
|
|
|
+ { id: 3, url: testVideo1 },
|
|
|
|
|
+ { id: 4, url: testVideo2 } // 第4个,用于测试轮播翻页
|
|
|
|
|
+ ],
|
|
|
|
|
+ // 3. 路口控制卡片列表
|
|
|
|
|
+ intersections: [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'INT_01',
|
|
|
|
|
+ name: '京原路与北宫路交叉口1',
|
|
|
|
|
+ statusColor: '#ffaa00', // 黄色状态灯
|
|
|
|
|
+ stage: 3,
|
|
|
|
|
+ mode: '步进',
|
|
|
|
|
+ timeLeft: 30,
|
|
|
|
|
+ btnText: '立即解锁',
|
|
|
|
|
+ btnType: 'normal',
|
|
|
|
|
+ phases: [
|
|
|
|
|
+ { id: 1, icon: '↑', img: testImg1, active: false },
|
|
|
|
|
+ { id: 2, icon: '↰', img: testImg1, active: false },
|
|
|
|
|
+ { id: 3, icon: '↑', img: testImg1, active: true }, // 当前激活相位
|
|
|
|
|
+ { id: 4, icon: '↰', img: testImg1, active: false }
|
|
|
|
|
+ ],
|
|
|
|
|
+ // 传给你原有的 IntersectionMapVideos 组件的数据
|
|
|
|
|
+ mapData: {
|
|
|
|
|
+ armsConfig: {
|
|
|
|
|
+ N: { lanes: ['L', 'S', 'R'], cameraType: 1 },
|
|
|
|
|
+ S: { lanes: ['L', 'S', 'R'], cameraType: 1 },
|
|
|
|
|
+ E: { lanes: ['L', 'S', 'R'], cameraType: 2 },
|
|
|
|
|
+ W: { lanes: ['L', 'S', 'R'], cameraType: 2 }
|
|
|
|
|
+ },
|
|
|
|
|
+ signals: {
|
|
|
|
|
+ ns: { isGreen: true, time: 30, phaseName: '南北直行' },
|
|
|
|
|
+ ew: { isGreen: false, time: 45, phaseName: '东西直行' }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ videoUrls: {
|
|
|
|
|
+ nw: testVideo1,
|
|
|
|
|
+ ne: testVideo2,
|
|
|
|
|
+ sw: testVideo1,
|
|
|
|
|
+ se: testVideo2
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ // 为了演示,这里复制上面的数据作为第2、3、4个路口
|
|
|
|
|
+ ...Array.from({ length: 3 }).map((_, i) => ({
|
|
|
|
|
+ id: `INT_0${i + 2}`,
|
|
|
|
|
+ name: `京原路与北宫路交叉口${i + 2}`,
|
|
|
|
|
+ statusColor: '#00e5ff',
|
|
|
|
|
+ stage: 2, mode: '系统', timeLeft: 15,
|
|
|
|
|
+ btnText: '立即执行', btnType: 'primary',
|
|
|
|
|
+ phases: [
|
|
|
|
|
+ { id: 1, icon: '↑', img: testImg1, active: true },
|
|
|
|
|
+ { id: 2, icon: '↰', img: testImg1, active: false },
|
|
|
|
|
+ { id: 3, icon: '↑', img: testImg1, active: false },
|
|
|
|
|
+ { id: 4, icon: '↰', img: testImg1, active: false }
|
|
|
|
|
+ ],
|
|
|
|
|
+ mapData: {
|
|
|
|
|
+ armsConfig: {
|
|
|
|
|
+ N: { lanes: ['L', 'S', 'R'], cameraType: 1 },
|
|
|
|
|
+ S: { lanes: ['L', 'S', 'R'], cameraType: 1 },
|
|
|
|
|
+ E: { lanes: ['L', 'S', 'R'], cameraType: 2 },
|
|
|
|
|
+ W: { lanes: ['L', 'S', 'R'], cameraType: 2 }
|
|
|
|
|
+ },
|
|
|
|
|
+ signals: {
|
|
|
|
|
+ ns: { isGreen: true, time: 30, phaseName: '南北直行' },
|
|
|
|
|
+ ew: { isGreen: false, time: 45, phaseName: '东西直行' }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ videoUrls: {
|
|
|
|
|
+ nw: testVideo1,
|
|
|
|
|
+ ne: testVideo2,
|
|
|
|
|
+ sw: testVideo2,
|
|
|
|
|
+ se: testVideo1
|
|
|
|
|
+ }
|
|
|
|
|
+ }))
|
|
|
|
|
+ ]
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|