Bläddra i källkod

路口详情-当前阶段: 跟随扫描线自动高亮 + 单行布局 + 支持全部阶段

  - onScanTick 中根据扫描线位置自动切换 currentStage 高亮(非手动模式), 触发蓝色蒙层跟随 + 自动滚动
  - phaseStages 从 slice(0,4) 改为取全部绿灯相位, 修复 8/16/32 阶段切换到第 4 阶段后停止的问题
  - 当前阶段网格从预留 2 行改为 1 行高度, 超 4 阶段纵向滚动
画安 2 veckor sedan
förälder
incheckning
50aacbb176
1 ändrade filer med 19 tillägg och 10 borttagningar
  1. 19 10
      src/components/ui/CrossingDetailPanel.vue

+ 19 - 10
src/components/ui/CrossingDetailPanel.vue

@@ -374,6 +374,15 @@ export default {
             // 更新实时方案圆饼图的已走时长
             this.updateRealtimeDonut(activeTime);
 
+            // 非手动模式下,根据扫描线位置自动切换当前阶段高亮
+            if (!this.isManualMode && this.phaseStages.length > 0) {
+                const idx = this.phaseStages.findIndex(s => activeTime >= s.start && activeTime < s.end);
+                if (idx >= 0) {
+                    const newStage = String(idx + 1);
+                    if (this.currentStage !== newStage) this.currentStage = newStage;
+                }
+            }
+
             // 双相位图模式:检测周期 wrap-around,触发 refetch 让后端把刚结束的周期作为 lastCycle 返回
             if (this.isDual) {
                 if (this._prevTick != null && activeTime < this._prevTick - 1) {
@@ -393,22 +402,22 @@ export default {
                 this._refetching = false;
             }
         },
-        // 从 phaseData 解析4个阶段的绿灯时长,构建圆饼图数据
+        // 从 phaseData 解析所有阶段的绿灯时长,构建圆饼图数据
         buildDonutFromPhaseData() {
             const phaseData = this.mockPhaseData || [];
-            const stageColors = ['#3b82f6', '#a855f7', '#14b8a6', '#f59e0b'];
+            const stageColors = ['#3b82f6', '#a855f7', '#14b8a6', '#f59e0b', '#ec4899', '#06b6d4', '#84cc16', '#f97316'];
             const stageLabels = ['1-南北直行', '2-南北左转', '3-东西直行', '4-东西左转'];
 
-            // 提取 track 0 的绿灯相位(每阶段的第一个 green)
+            // 提取 track 0 的绿灯相位
             const greenPhases = phaseData.filter(p => p[0] === 0 && p[5] === 'green');
-            const stages = greenPhases.slice(0, 4).map((p, i) => {
-                const total = p[8] || Math.floor(this.cycleLength / 4);
+            const stages = greenPhases.map((p, i) => {
+                const total = p[8] || Math.floor(this.cycleLength / greenPhases.length);
                 return {
                     label: stageLabels[i] || `阶段${i + 1}`,
                     value: total,           // 阶段总时长
                     start: p[1],            // 阶段开始时间(从绿灯起始)
                     end: p[1] + total,      // 阶段结束时间
-                    color: stageColors[i]
+                    color: stageColors[i % stageColors.length]
                 };
             });
             this.phaseStages = stages;
@@ -450,8 +459,8 @@ export default {
                 }))
             ];
         },
-        // 观察首个 stage-item-wrapper, 算出 2 行高度(含行间 gap) 写到 CSS 变量。
-        // grid 的 height 锁在 2 行高度: 不足 8 个时下排留白; 超 8 个时按行滚动。
+        // 观察首个 stage-item-wrapper, 算出 1 行高度写到 CSS 变量。
+        // grid 的 height 锁在 1 行高度: 超 4 个时按行滚动。
         // 防 ResizeObserver loop 警告: rAF 推到下一帧 + 值比较切断"测量→撑大→再测"循环。
         _observeFirstStageItem() {
             const grid = this.$refs.stageGrid;
@@ -472,7 +481,7 @@ export default {
                     if (rowH <= 0) return;
                     const cs = window.getComputedStyle(grid);
                     const gap = parseFloat(cs.rowGap || cs.gap || '0') || 0;
-                    const reservedH = rowH * 2 + gap;
+                    const reservedH = rowH;
                     if (this._lastRowH === rowH && this._lastReservedH === reservedH) return;
                     this._lastRowH = rowH;
                     this._lastReservedH = reservedH;
@@ -1143,7 +1152,7 @@ export default {
     opacity: 0;
 }
 
-/* 4 列固定网格; 始终锁 2 行 + 行间 gap (不足 8 阶段时第 2 行留白); 超 8 滚动 */
+/* 4 列固定网格; 锁 1 行高度; 超 4 阶段纵向滚动 */
 .current-stage {
     margin-bottom: clamp(4px, calc(var(--s) * 10px), 20px);
     display: grid;