| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- <template>
- <DashboardLayout>
- <!-- 天气 -->
- <template #header-left>
- <WeatherWidget />
- </template>
- <!-- 日期 -->
- <template #header-right>
- <DateTimeWidget />
- </template>
- <template #left>
- <!-- 左侧Tab菜单栏 -->
- <TechTabs v-model="activeLeftTab" type="underline">
- <TechTabPane label="总览" name="overview" class="menu-scroll-view">
- <MenuItem theme="tech" v-for="item in menuData" :key="item.id" :node="item" :level="0"
- @node-click="handleMenuClick" />
- </TechTabPane>
- <TechTabPane label="路口" name="crossing" class="menu-scroll-view">
- <MenuItem theme="tech" v-for="item in menuData" :key="item.id" :node="item" :level="0"
- @node-click="handleMenuClick" />
- </TechTabPane>
- <TechTabPane label="干线" name="trunkLine">
- <MenuItem v-for="item in menuData" :key="item.id" :node="item" :level="0"
- @node-click="handleMenuClick" />
- </TechTabPane>
- <TechTabPane label="特勤" name="specialDuty">
- <MenuItem v-for="item in menuData" :key="item.id" :node="item" :level="0"
- @node-click="handleMenuClick" />
- </TechTabPane>
- </TechTabs>
- </template>
- <template #right>
- </template>
- <template #center>
- <!-- 地图 -->
- <TongzhouTrafficMap amapKey="db2da7e3e248c3b2077d53fc809be63f"
- securityJsCode="a7413c674852c5eaf01d90813c5b7ef6" />
- </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>
- <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 { getIntersectionData, makeTrafficTimeSpaceData } from '@/mock/data';
- export default {
- name: "HomePage",
- components: {
- DashboardLayout,
- WeatherWidget,
- DateTimeWidget,
- TechTabs,
- TechTabPane,
- SmartDialog,
- TongzhouTrafficMap,
- MenuItem,
- DeviceStatusPanel,
- SecurityRoutePanel,
- IntersectionMapVideos,
- TrafficTimeSpace
- },
- data() {
- return {
- // 弹窗相关数据
- activeDialogs: [],
- // 左侧边栏数据
- activeLeftTab: 'overview',
- menuData: [
- {
- id: 'root-1',
- label: '主控中心',
- icon: 'el-icon-monitor', // 这里可以替换为你项目用的图标类名,比如 iconfont
- children: [
- {
- id: 'team-1',
- label: '北京市交警总队',
- children:
- [
- {
- id: 'dist-1',
- label: '通州区',
- children: [
- {
- id: 'street-1',
- label: '中仓街道',
- children: [
- { id: 'node-1', label: '新华东街 - 新华南路' },
- { id: 'node-2', label: '玉带河东街 - 车站路' },
- { id: 'node-3', label: '赵登禹大街 - 新华东街' }
- ]
- },
- {
- id: 'street-2',
- label: '新华街道',
- children: [
- { id: 'node-4', label: '新华南北街交叉口' },
- { id: 'node-5', label: '通胡大街 - 紫运中路' },
- { id: 'node-6', label: '芙蓉东路 - 通胡大街' }
- ]
- },
- {
- id: 'street-3',
- label: '北苑街道',
- children: [
- { id: 'node-7', label: '北苑路口' },
- { id: 'node-8', label: '新华西街 - 北苑南路' },
- { id: 'node-9', label: '新城南街 - 新华西街' }
- ]
- },
- {
- id: 'street-4',
- label: '玉桥街道',
- children: [
- { id: 'node-10', label: '玉桥西路 - 玉桥西里中街' },
- { id: 'node-11', label: '运河西大街 - 玉桥中路' },
- { id: 'node-12', label: '梨园南街 - 运河西大街' }
- ]
- },
- {
- id: 'street-5',
- label: '杨庄街道',
- children: [
- { id: 'node-13', label: '怡乐中街 - 九棵树西路' },
- { id: 'node-14', label: '翠屏西路 - 怡乐中街' },
- { id: 'node-15', label: '杨庄路 - 新华西街' }
- ]
- },
- {
- id: 'street-6',
- label: '九棵树街道',
- children: [
- { id: 'node-16', label: '九棵树东路 - 九棵树西路' },
- { id: 'node-17', label: '云景东路 - 九棵树东路' },
- { id: 'node-18', label: '群芳南街 - 云景东路' }
- ]
- },
- {
- id: 'street-7',
- label: '临河里街道',
- children: [
- { id: 'node-19', label: '梨园中街 - 九棵树东路' },
- { id: 'node-20', label: '临河里路 - 梨园中街' },
- { id: 'node-21', label: '万盛南街 - 临河里路' }
- ]
- },
- {
- id: 'street-8',
- label: '潞邑街道',
- children: [
- { id: 'node-22', label: '潞苑北大街 - 潞邑西路' },
- { id: 'node-23', label: '潞苑南大街 - 潞邑三路' },
- { id: 'node-24', label: '东六环 - 潞苑北大街' }
- ]
- },
- {
- id: 'street-9',
- label: '通运街道',
- children: [
- { id: 'node-25', label: '通胡大街 - 东六环' },
- { id: 'node-26', label: '运河东大街 - 通胡大街' },
- { id: 'node-27', label: '紫运中路 - 运河东大街' }
- ]
- },
- {
- id: 'street-10',
- label: '潞源街道',
- children: [
- { id: 'node-28', label: '宋梁路 - 运河东大街' },
- { id: 'node-29', label: '东六环 - 运河东大街' },
- { id: 'node-30', label: '潞源北街 - 宋梁路' }
- ]
- },
- {
- id: 'street-11',
- label: '文景街道',
- children: [
- { id: 'node-31', label: '环球大道 - 九棵树东路' },
- { id: 'node-32', label: '颐瑞东路 - 环球大道' },
- { id: 'node-33', label: '万盛南街 - 颐瑞东路' }
- ]
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- };
- },
- created() {
- },
- mounted() {
- },
- methods: {
- handleMenuClick(nodeData) {
- console.log('父组件接收到了最底层路口点击事件:', nodeData);
- // 这里可以根据 nodeData 的经纬度来控制地图组件的视角
- this.testOpenDeviceStatus();
- this.testOpenSecurityRoute();
- this.testOpenSecurityRoute2();
- this.testOpenTrafficTimeSpace();
- },
- 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 || {} // 传给内部组件的业务数据
- });
- },
- /**
- * 关闭弹窗的回调
- */
- handleDialogClose(dialogId) {
- // 性能优化:当用户点击 ✕ 关闭弹窗时,将其从数组中彻底移除,销毁内部组件释放内存
- this.activeDialogs = this.activeDialogs.filter(d => d.id !== dialogId);
- },
- // ================= 测试用例:模拟各种点击行为 =================
- // 模拟 1:打开设备状态面板
- testOpenDeviceStatus() {
- this.openDialog({
- id: 'device-status-node-101', // 这里的 ID 可以根据实际业务场景动态生成,例如 'node-101' 代表某个路口
- title: '',
- component: 'DeviceStatusPanel', // 对应 components 里注册的名字
- width: 300,
- height: 200,
- center: false,
- position: { x: 400, y: 200 }, // 直接指定坐标,SmartDialog 内部会自动转换成 left/top
- showClose: false, // 是否显示关闭按钮
- });
- this.openDialog({
- id: 'device-status-node-102', // 这里的 ID 可以根据实际业务场景动态生成,例如 'node-101' 代表某个路口
- title: '',
- component: 'DeviceStatusPanel', // 对应 components 里注册的名字
- width: 300,
- height: 200,
- center: false,
- position: { x: 1600, y: 100 }, // 直接指定坐标,SmartDialog 内部会自动转换成 left/top
- showClose: false, // 是否显示关闭按钮
- });
- },
- // 模拟 2:打开特勤安保路线面板
- testOpenSecurityRoute() {
- this.openDialog({
- id: 'dev-security-route', // 这里的 ID 可以根据实际业务场景动态生成,例如 'dev-security-route' 代表特勤安保路线弹窗
- title: '特勤安保路线 未开始 一级',
- component: 'SecurityRoutePanel',
- width: 540,
- height: 300,
- center: false,
- position: { x: 400, y: 450 },
- });
- },
- // 模拟 3:打开本地协调控制面板
- testOpenSecurityRoute2() {
- const dialogId = 'dev-security-route2';
- this.openDialog({
- id: dialogId,
- title: '长安街-府右街口 本地协调控制',
- component: 'IntersectionMapVideos',
- width: 300,
- height: 200,
- center: false,
- position: { x: 1100, y: 200 },
- data: {
- mapData: {},
- intersectionName: '长安街-府右街口',
- videos: [
- { id: 'cam-1', name: '信号机视频', url: 'https://example.com/video1' },
- { id: 'cam-2', name: '路口全景', url: 'https://example.com/video2' },
- { id: 'cam-3', name: '人行横道', url: 'https://example.com/video3' },
- ]
- }
- });
- // 异步获取数据后更新弹窗
- getIntersectionData().then(mapData => {
- const dialog = this.activeDialogs.find(d => d.id === dialogId);
- if (dialog) {
- this.$set(dialog.data, 'mapData', mapData);
- }
- });
- },
- // 模拟 4:打开新干线协调控制面板
- testOpenTrafficTimeSpace() {
- const tsData = makeTrafficTimeSpaceData();
- this.openDialog({
- id: 'dev-traffic-time-space',
- title: '新干线协调控制 早高峰',
- component: 'TrafficTimeSpace',
- width: 300,
- height: 300,
- center: false,
- position: { x: 1400, y: 500 },
- data: {
- intersections: tsData.intersections,
- distances: tsData.distances,
- waveData: tsData.waveData,
- greenData: tsData.greenData,
- }
- });
- }
- }
- }
- </script>
- <style scoped></style>
|