CrossingPanel.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <div class="crossing-panel">
  3. <div class="intersection-video-wrap">
  4. <IntersectionMapVideos :mapData="intersectionData" :videoUrls="currentRoute.cornerVideos" />
  5. </div>
  6. <div class="signal-timing-wrap">
  7. <SignalTimingChart :cycleLength="cycleLength" :currentTime="currentSec" :phaseData="mockPhaseData" :showScanLine="dataReady" :autoScan="dataReady" @scan-tick="onScanTick" />
  8. </div>
  9. </div>
  10. </template>
  11. <script>
  12. import SignalTimingChart from '@/components/ui/SignalTimingChart.vue';
  13. import IntersectionMapVideos from '@/components/ui/IntersectionMapVideos.vue';
  14. import { apiGetCrossingPanelData } from '@/api';
  15. export default {
  16. name: 'CrossingPanel',
  17. components: {
  18. SignalTimingChart,
  19. IntersectionMapVideos
  20. },
  21. props: {
  22. },
  23. data() {
  24. return {
  25. dataReady: false,
  26. followPhase: false,
  27. intersectionData: {},
  28. currentRoute: {},
  29. cycleLength: 140,
  30. currentSec: 15,
  31. mockPhaseData: []
  32. }
  33. },
  34. async mounted() {
  35. const nodeId = this.$attrs.id || this.id;
  36. const data = await apiGetCrossingPanelData(nodeId);
  37. if (data) {
  38. this.currentRoute = data.currentRoute || {};
  39. this.intersectionData = data.intersectionData || {};
  40. this.mockPhaseData = data.phaseData || [];
  41. if (data.cycleLength) this.cycleLength = data.cycleLength;
  42. if (data.currentTime !== undefined) this.currentSec = data.currentTime;
  43. this.$nextTick(() => { this.dataReady = true; });
  44. }
  45. },
  46. methods: {
  47. onScanTick(activeTime) {
  48. if (!this.mockPhaseData || this.mockPhaseData.length === 0) return;
  49. const phase = this.mockPhaseData.find(p => p[0] === 0 && activeTime >= p[1] && activeTime < p[2]);
  50. if (!phase) return;
  51. const type = phase[5];
  52. const iconValue = phase[6];
  53. const direction = phase[7];
  54. const phaseName = phase[3];
  55. const endTime = phase[2];
  56. const remaining = Math.max(0, Math.round(endTime - activeTime));
  57. const nsGreen = (type === 'green' && direction === 'ns');
  58. const ewGreen = (type === 'green' && direction === 'ew');
  59. let activeArrowTypes = [];
  60. if ((nsGreen || ewGreen) && iconValue) {
  61. const icons = iconValue.split(',');
  62. icons.forEach(ic => {
  63. if (ic.includes('UTURN')) activeArrowTypes.push('U');
  64. if (ic.includes('TURN') && !ic.includes('UTURN')) activeArrowTypes.push('L');
  65. if (ic.includes('STRAIGHT')) activeArrowTypes.push('S');
  66. });
  67. activeArrowTypes = [...new Set(activeArrowTypes)];
  68. }
  69. const pedAllRed = !(type === 'green' && (phaseName === 'P1' || phaseName === 'P3'));
  70. this.$set(this.intersectionData, 'signals', {
  71. pedAllRed,
  72. ns: {
  73. phaseName: nsGreen ? ({ P1: '南北直行', P2: '南北左转' }[phaseName] || '南北') : (this.intersectionData.signals && this.intersectionData.signals.ns && this.intersectionData.signals.ns.phaseName || '南北'),
  74. time: remaining,
  75. isGreen: nsGreen,
  76. activeArrowTypes: nsGreen ? activeArrowTypes : []
  77. },
  78. ew: {
  79. phaseName: ewGreen ? ({ P3: '东西直行', P4: '东西左转' }[phaseName] || '东西') : (this.intersectionData.signals && this.intersectionData.signals.ew && this.intersectionData.signals.ew.phaseName || '东西'),
  80. time: remaining,
  81. isGreen: ewGreen,
  82. activeArrowTypes: ewGreen ? activeArrowTypes : []
  83. }
  84. });
  85. }
  86. }
  87. }
  88. </script>
  89. <style scoped>
  90. .crossing-panel {
  91. display: flex;
  92. flex-direction: column;
  93. width: 100%;
  94. height: 100%;
  95. min-height: 0;
  96. }
  97. .intersection-video-wrap {
  98. width: 100%;
  99. flex: 2; /* 地图比较重要,分给它 2 份的高度 */
  100. min-height: 0;
  101. }
  102. .signal-timing-wrap {
  103. flex: 1;
  104. min-height: 0;
  105. --s: 1;
  106. width: 100%;
  107. height: 80px;
  108. min-width: 0;
  109. background-color: transparent;
  110. box-sizing: border-box;
  111. position: relative;
  112. display: flex;
  113. flex-direction: column;
  114. overflow: hidden;
  115. padding: calc(var(--s) * 10px) 0 0 0;
  116. }
  117. .header {
  118. display: flex;
  119. justify-content: space-between;
  120. align-items: center;
  121. margin-bottom: calc(var(--s) * 15px);
  122. color: #e0e6f1;
  123. flex-shrink: 0;
  124. }
  125. .title-area {
  126. font-size: calc(var(--s) * 16px);
  127. }
  128. .main-title {
  129. font-size: calc(var(--s) * 18px);
  130. font-weight: bold;
  131. margin-right: calc(var(--s) * 10px);
  132. }
  133. .sub-info {
  134. font-size: calc(var(--s) * 12px);
  135. opacity: 0.8;
  136. }
  137. .checkbox-area {
  138. font-size: calc(var(--s) * 12px);
  139. display: flex;
  140. align-items: center;
  141. cursor: pointer;
  142. opacity: 0.7;
  143. user-select: none;
  144. }
  145. .checkbox-area:hover {
  146. opacity: 1;
  147. }
  148. .checkbox-mock {
  149. width: calc(var(--s) * 14px);
  150. height: calc(var(--s) * 14px);
  151. border: 1px solid rgba(255, 255, 255, 0.5);
  152. margin-right: calc(var(--s) * 6px);
  153. border-radius: 2px;
  154. display: flex;
  155. align-items: center;
  156. justify-content: center;
  157. }
  158. .checkbox-mock.is-checked {
  159. background-color: #4da8ff;
  160. border-color: #4da8ff;
  161. }
  162. .chart-container {
  163. width: 100%;
  164. min-width: 0;
  165. flex: 1;
  166. min-height: 80px;
  167. overflow: hidden;
  168. }
  169. .loading-overlay {
  170. flex: 1;
  171. display: flex;
  172. align-items: center;
  173. justify-content: center;
  174. color: #758599;
  175. font-size: 14px;
  176. }
  177. </style>