|
|
@@ -1,191 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="container" ref="Container">
|
|
|
- <div class="intersection" ref="intersectionBox">
|
|
|
- <div class="video-1" :style="videoStyles.v1"><XgVideoPlayer :src="video1" /></div>
|
|
|
- <div class="video-2" :style="videoStyles.v2"><XgVideoPlayer :src="video2" /></div>
|
|
|
- <div class="video-3" :style="videoStyles.v3"><XgVideoPlayer :src="video1" /></div>
|
|
|
- <div class="video-4" :style="videoStyles.v4"><XgVideoPlayer :src="video2" /></div>
|
|
|
-
|
|
|
- <IntersectionMap :mapData="intersectionData" />
|
|
|
- </div>
|
|
|
- <div class="signaltiming" ref="Signaltiming">
|
|
|
- <SignalTimingChart
|
|
|
- :loading="loading"
|
|
|
- :cycle-length="signalTimingData.cycleLength"
|
|
|
- :current-time="signalTimingData.currentTime"
|
|
|
- :phase-data="signalTimingData.phaseData" />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-
|
|
|
-import SignalTimingChart from '@/components/ui/SignalTimingChart.vue';
|
|
|
-import IntersectionMap from '@/components/ui/IntersectionMap.vue';
|
|
|
-import { apiGetSignalTiming, apiGetIntersectionData } from '@/api';
|
|
|
-import video1 from '@/assets/videos/video1.mp4';
|
|
|
-import video2 from '@/assets/videos/video2.mp4';
|
|
|
-import XgVideoPlayer from '@/components/ui/XgVideoPlayer.vue';
|
|
|
-
|
|
|
-export default {
|
|
|
- name: "IntersectionSignalMonitoring",
|
|
|
- components: {
|
|
|
- SignalTimingChart,
|
|
|
- IntersectionMap,
|
|
|
- XgVideoPlayer
|
|
|
- },
|
|
|
- props: {
|
|
|
- nodeData: {
|
|
|
- type: Object,
|
|
|
- default: () => ({})
|
|
|
- }
|
|
|
- },
|
|
|
- data() {
|
|
|
- return {
|
|
|
- signalTimingData: {},
|
|
|
- loading: false,
|
|
|
- intersectionData: {},
|
|
|
- timer: null,
|
|
|
- mapWidth: 600,
|
|
|
- mapHeight: 600,
|
|
|
- video1,
|
|
|
- video2,
|
|
|
- videoStyles: { v1: {}, v2: {}, v3: {}, v4: {} }
|
|
|
- };
|
|
|
- },
|
|
|
- computed: {
|
|
|
-
|
|
|
- },
|
|
|
- created() {
|
|
|
- },
|
|
|
- async mounted() {
|
|
|
- this.measureIntersectionBox();
|
|
|
-
|
|
|
- this._roaPending = false;
|
|
|
- this._resizeObserver = new ResizeObserver(() => {
|
|
|
- if (!this._roaPending) {
|
|
|
- this._roaPending = true;
|
|
|
- requestAnimationFrame(() => {
|
|
|
- this._roaPending = false;
|
|
|
- this.measureIntersectionBox();
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- this._resizeObserver.observe(this.$refs.Container);
|
|
|
-
|
|
|
- this.loading = true;
|
|
|
- this.signalTimingData = await apiGetSignalTiming(this.nodeData.id);
|
|
|
- this.intersectionData = await apiGetIntersectionData(this.nodeData.id) || {};
|
|
|
- this.loading = false;
|
|
|
-
|
|
|
- this.startSimulationTimer();
|
|
|
- },
|
|
|
- beforeDestroy() {
|
|
|
- if (this._resizeObserver) this._resizeObserver.disconnect();
|
|
|
- if (this.timer) clearInterval(this.timer);
|
|
|
- },
|
|
|
- methods: {
|
|
|
- measureIntersectionBox() {
|
|
|
- const container = this.$refs.Container;
|
|
|
- const signaltiming = this.$refs.Signaltiming;
|
|
|
- const box = this.$refs.intersectionBox;
|
|
|
- if (!container) return;
|
|
|
- // 容器总高度 - padding(上下各15) - gap(12) - signaltiming高度(300) = intersection可用高度
|
|
|
- const containerH = container.clientHeight;
|
|
|
- const containerW = container.clientWidth;
|
|
|
- const signalH = signaltiming ? signaltiming.clientHeight : 300;
|
|
|
- const padding = 30; // 上下 padding 各 15px
|
|
|
- const gap = 12;
|
|
|
- this.mapWidth = containerW - padding;
|
|
|
- this.mapHeight = containerH - padding - gap - signalH;
|
|
|
-
|
|
|
- // 根据 Konva 画布的缩放比例动态计算视频位置
|
|
|
- if (box) {
|
|
|
- const boxW = box.clientWidth;
|
|
|
- const boxH = box.clientHeight;
|
|
|
- const designSize = 900; // IntersectionMap 设计尺寸
|
|
|
- const scale = Math.min(boxW / designSize, boxH / designSize);
|
|
|
-
|
|
|
- // Konva 画布实际尺寸
|
|
|
- const canvasH = designSize * scale;
|
|
|
-
|
|
|
- // 视频尺寸(固定高度280,宽度按16:9比例计算)
|
|
|
- const vh = Math.round(280 * scale);
|
|
|
- const vw = Math.round(280 * 16 / 9 * scale);
|
|
|
- // 马路半幅宽(缩放后)
|
|
|
- const halfRoadScaled = Math.round(160 * scale);
|
|
|
- // 水平偏移 = box宽度/2 - 视频宽度 - 马路半幅宽
|
|
|
- const hOffset = Math.round(boxW / 2 - vw - halfRoadScaled - 3); // 3px微调
|
|
|
- // 垂直偏移 = 画布顶部在容器中的偏移
|
|
|
- const vOffset = Math.round((boxH - canvasH) / 2);
|
|
|
-
|
|
|
- const size = { width: vw + 'px', height: vh + 'px' };
|
|
|
- this.videoStyles = {
|
|
|
- v1: { ...size, left: hOffset + 'px', top: vOffset + 'px' },
|
|
|
- v2: { ...size, right: hOffset + 'px', top: vOffset + 'px' },
|
|
|
- v3: { ...size, left: hOffset + 'px', bottom: vOffset + 'px' },
|
|
|
- v4: { ...size, right: hOffset + 'px', bottom: vOffset + 'px' }
|
|
|
- };
|
|
|
- }
|
|
|
- },
|
|
|
- startSimulationTimer() {
|
|
|
- this.timer = setInterval(() => {
|
|
|
- // 创建数据的副本以触发 Vue 的响应式更新
|
|
|
- let newData = JSON.parse(JSON.stringify(this.intersectionData));
|
|
|
-
|
|
|
- let ns = newData.signals.ns;
|
|
|
- let ew = newData.signals.ew;
|
|
|
-
|
|
|
- // 南北向倒计时
|
|
|
- ns.time--;
|
|
|
- if (ns.time < 0) {
|
|
|
- ns.time = 38;
|
|
|
- ns.isGreen = !ns.isGreen; // 切换红绿状态
|
|
|
- }
|
|
|
-
|
|
|
- // 东西向倒计时
|
|
|
- ew.time--;
|
|
|
- if (ew.time < 0) {
|
|
|
- ew.time = 38;
|
|
|
- ew.isGreen = !ew.isGreen; // 切换红绿状态
|
|
|
- }
|
|
|
-
|
|
|
- // 将新数据赋值回去
|
|
|
- this.intersectionData = newData;
|
|
|
- }, 1000);
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.container {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- background-color: #212842;
|
|
|
- padding: 15px;
|
|
|
- overflow: hidden;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- box-sizing: border-box;
|
|
|
-}
|
|
|
-.container .intersection {
|
|
|
- flex: 1;
|
|
|
- min-height: 0;
|
|
|
- width: 100%;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-.container .intersection video {
|
|
|
- position: absolute;
|
|
|
- z-index: 10;
|
|
|
- object-fit: fill;
|
|
|
-}
|
|
|
-.container .signaltiming {
|
|
|
- margin-top: 12px;
|
|
|
- width: 100%;
|
|
|
- min-width: 0;
|
|
|
- height: 180px;
|
|
|
- flex-shrink: 0;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-</style>
|