浏览代码

当前阶段输入框后添加百分比显示,根据各阶段时间占总时间自动计算

画安 2 周之前
父节点
当前提交
4a7c0be3b4
共有 1 个文件被更改,包括 97 次插入57 次删除
  1. 97 57
      src/components/ui/CrossingDetailPanel.vue

+ 97 - 57
src/components/ui/CrossingDetailPanel.vue

@@ -12,7 +12,8 @@
                     </div>
                 </div>
 
-                <SignalTimingChart :cycleLength="cycleLength" :currentTime="currentSec" :phaseData="mockPhaseData" :showScanLine="dataReady" :autoScan="dataReady" @scan-tick="onScanTick" />
+                <SignalTimingChart :cycleLength="cycleLength" :currentTime="currentSec" :phaseData="mockPhaseData"
+                    :showScanLine="dataReady" :autoScan="dataReady" @scan-tick="onScanTick" />
             </div>
         </div>
 
@@ -23,9 +24,8 @@
                         <div class="control-label-wrap">
                             <span class="control-label">控制方式</span>
                             <div class="control-operation">
-                                <div class="operation-btn" 
-                                     :class="{ 'is-active': isManualMode }"
-                                     @click="toggleManualMode">
+                                <div class="operation-btn" :class="{ 'is-active': isManualMode }"
+                                    @click="toggleManualMode">
                                     {{ isManualMode ? '退出手动控制' : '手动控制' }}
                                 </div>
                             </div>
@@ -42,27 +42,25 @@
                                 <span class="control-label">控制方案</span>
                                 <DropdownSelect v-model="currentScheme" :options="schemeOptions" size="auto" />
                             </div>
-                            
+
                             <div class="current-stage">
                                 <div class="current-stage-warp">
                                     <div class="current-stage-label">当前阶段:</div>
                                     <div v-for="(item, index) in currentStageList" :key="index" class="stage-item-wrapper">
-                                        <div 
-                                            class="phase-box" 
-                                            :class="{ 'is-active': item.value === currentStage }"
-                                            @click="currentStage = item.value"
-                                        >
+                                        <div class="phase-box" :class="{ 'is-active': item.value === currentStage }"
+                                            @click="currentStage = item.value">
                                             <img :src="item.img" alt="stage" class="phase-image" />
                                         </div>
-                                        
-                                        <input 
-                                            type="number" 
-                                            v-model.number="item.time" 
-                                            class="stage-input"
-                                            :disabled="!canEditStage"
-                                            :title="canEditStage ? '修改阶段时间' : '当前控制方式不可修改'"
-                                        />
-                                        <span class="unit">s</span>
+
+                                        <div class="bottom-controls">
+                                            <div class="input-unit-wrapper">
+                                                <input type="number" v-model.number="item.time" class="stage-input"
+                                                    :disabled="!canEditStage"
+                                                    :title="canEditStage ? '修改阶段时间' : '当前控制方式不可修改'" />
+                                                <span class="unit">s</span>
+                                            </div>
+                                            <span class="percent">{{ stagePercent(item.time) }}</span>
+                                        </div>
                                     </div>
                                 </div>
                             </div>
@@ -71,24 +69,14 @@
                             <div class="donut-row" v-if="!showLockTime">
                                 <div class="donut-item">
                                     <div class="donut-title">实时方案(执行方案3)</div>
-                                    <PlanDonutChart
-                                        :chartData="realtimeDonutData"
-                                        :centerValue="String(realtimeRemaining)"
-                                        centerLabel="剩余时长"
-                                        :showTotal="true"
-                                        :totalValue="cycleLength"
-                                        :scale="panelScale"
-                                    />
+                                    <PlanDonutChart :chartData="realtimeDonutData"
+                                        :centerValue="String(realtimeRemaining)" centerLabel="剩余时长" :showTotal="true"
+                                        :totalValue="cycleLength" :scale="panelScale" />
                                 </div>
                                 <div class="donut-item">
                                     <div class="donut-title">下周期方案</div>
-                                    <PlanDonutChart
-                                        :chartData="nextCycleDonutData"
-                                        :centerValue="String(cycleLength)"
-                                        centerLabel="总时长"
-                                        :showTotal="false"
-                                        :scale="panelScale"
-                                    />
+                                    <PlanDonutChart :chartData="nextCycleDonutData" :centerValue="String(cycleLength)"
+                                        centerLabel="总时长" :showTotal="false" :scale="panelScale" />
                                 </div>
                             </div>
 
@@ -107,8 +95,8 @@
                                         <div class="lock-time-option">
                                             <label>
                                                 <input type="radio" v-model="lockTimeType" value="timer" /> 放行
-                                                <DropdownSelect placeholder="锁定时间" v-model="currentLocktime" :options="locktimeOptions" size="auto"
-                                                    @click.native.prevent />
+                                                <DropdownSelect placeholder="锁定时间" v-model="currentLocktime"
+                                                    :options="locktimeOptions" size="auto" @click.native.prevent />
                                                 秒解锁
                                             </label>
                                         </div>
@@ -186,7 +174,7 @@ export default {
             phaseStages: [],
             currentLocktime: 50,
             locktimeOptions: [],
-            currentStage: '1', 
+            currentStage: '1',
             // 补充了 time 属性,用于双向绑定输入框的时间
             currentStageList: []
         }
@@ -227,6 +215,11 @@ export default {
         if (this._ro) this._ro.disconnect();
     },
     methods: {
+        stagePercent(time) {
+            const total = this.currentStageList.reduce((s, item) => s + (item.time || 0), 0);
+            if (!total) return '0%';
+            return Math.round(time / total * 100) + '%';
+        },
         onScanTick(activeTime) {
             if (!this.mockPhaseData || this.mockPhaseData.length === 0) return;
             // 只看第一轨道(trackIdx=0)的相位
@@ -380,7 +373,7 @@ export default {
                 this.showLockTime = false;
             }
         },
-        
+
         // 模拟:根据控制方式改变下拉方案的数据
         updateSchemeDataByMethod(method) {
             if (method === 'system') {
@@ -400,14 +393,14 @@ export default {
         },
 
         // 取消按钮
-        onCancel() { 
+        onCancel() {
             this.isManualMode = false;
             this.showLockTime = false;
             // 可以在此添加回滚初始数据的逻辑
         },
 
         // 需求5:点击确认按钮提交 + 表单验证
-        onConfirm() { 
+        onConfirm() {
             // 验证1:临时方案必须检查时间是否有效
             if (this.currentMethod === 'temp') {
                 const isInvalid = this.currentStageList.some(item => !item.time || item.time <= 0);
@@ -437,7 +430,7 @@ export default {
             };
 
             console.log('提交的数据:', submitData);
-            
+
             // 提交完成后可根据业务决定是否退出手动模式
             // this.isManualMode = false;
         }
@@ -610,9 +603,11 @@ export default {
     cursor: pointer;
     user-select: none;
 }
+
 .operation-btn:hover {
     text-decoration: underline;
 }
+
 .operation-btn.is-active {
     text-decoration: underline;
 }
@@ -692,14 +687,16 @@ export default {
 }
 
 .current-stage-warp {
+    width: 100%;
     display: flex;
     align-items: center;
-    justify-content: center;
+    justify-content: space-around;
     flex-wrap: wrap;
     gap: clamp(4px, calc(var(--s) * 8px), 10px);
     padding: clamp(6px, calc(var(--s) * 12px), 32px);
     color: #ffffff;
 }
+
 .current-stage-label {
     font-size: clamp(9px, calc(var(--s) * 14px), 14px);
     width: auto;
@@ -708,7 +705,7 @@ export default {
 
 .stage-input {
     width: clamp(32px, calc(var(--s) * 65px), 65px);
-    border: 1px solid rgba(161,190,255,0.7);
+    border: 1px solid rgba(161, 190, 255, 0.7);
     background-color: transparent;
     padding: clamp(2px, calc(var(--s) * 5px), 5px);
     color: #ffffff;
@@ -717,8 +714,8 @@ export default {
 
 .phase-box {
     position: relative;
-    width: clamp(30px, calc(var(--s) * 65px), 65px);
-    height: clamp(30px, calc(var(--s) * 65px), 65px);
+    width: clamp(30px, calc(var(--s) * 90px), 90px);
+    height: clamp(30px, calc(var(--s) * 90px), 90px);
     background: #E6F0FF;
     border-radius: 4px;
     display: flex;
@@ -774,11 +771,13 @@ export default {
     color: #d1d5db;
     border: 1px solid rgba(130, 150, 190, 0.4);
 }
+
 .btn-cancel:hover {
     color: #ffffff;
     border-color: rgba(130, 150, 190, 0.8);
     background-color: rgba(255, 255, 255, 0.05);
 }
+
 .btn-cancel:active {
     background-color: rgba(255, 255, 255, 0.1);
 }
@@ -788,11 +787,13 @@ export default {
     color: #ffffff;
     border: 1px solid #3b74ff;
 }
+
 .btn-confirm:hover {
     background-color: #5a8bff;
     border-color: #5a8bff;
     box-shadow: 0 2px 8px rgba(59, 116, 255, 0.3);
 }
+
 .btn-confirm:active {
     background-color: #265bed;
     border-color: #265bed;
@@ -829,41 +830,78 @@ export default {
 .stage-item-wrapper {
     display: flex;
     flex-direction: column;
-    align-items: center;
+    align-items: stretch;
     gap: clamp(2px, calc(var(--s) * 4px), 6px);
     position: relative;
 }
 
+.bottom-controls {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: clamp(4px, calc(var(--s) * 6px), 8px); /* 输入框和百分比的间距 */
+}
+
+/* 新增包裹层的相对定位 */
+.input-unit-wrapper {
+    position: relative;
+    display: inline-block;
+}
+
 .stage-input {
     width: clamp(32px, calc(var(--s) * 65px), 65px);
-    border: 1px solid rgba(161,190,255,0.7);
+    border: 1px solid rgba(161, 190, 255, 0.7);
     background-color: transparent;
     padding: clamp(2px, calc(var(--s) * 5px), 5px);
+    /* 给右侧留出空间,防止数字过长被 s 挡住 */
+    padding-right: clamp(10px, calc(var(--s) * 16px), 16px);
     color: #ffffff;
     text-align: center;
     border-radius: 4px;
 }
 
-.stage-input:disabled {
-    border-color: rgba(255, 255, 255, 0.2);
-    color: rgba(255, 255, 255, 0.5);
-    background-color: rgba(0, 0, 0, 0.2);
-}
-
-.stage-item-wrapper .unit {
+/* 修改 s 单位的定位方式为垂直居中 */
+.input-unit-wrapper .unit {
     position: absolute;
-    bottom: clamp(2px, calc(var(--s) * 6px), 6px);
+    top: 50%;
+    transform: translateY(-50%);
     right: clamp(3px, calc(var(--s) * 6px), 8px);
     color: #77A1FF;
     font-size: clamp(8px, calc(var(--s) * 12px), 12px);
     pointer-events: none;
 }
 
+/* 微调百分比的间距,让排版更紧凑 */
+.stage-item-wrapper .percent {
+    color: rgba(255, 255, 255, 0.5);
+    font-size: clamp(8px, calc(var(--s) * 11px), 11px);
+    white-space: nowrap;
+}
+
+.stage-input {
+    width: clamp(32px, calc(var(--s) * 65px), 65px);
+    border: 1px solid rgba(161, 190, 255, 0.7);
+    background-color: transparent;
+    padding: clamp(2px, calc(var(--s) * 5px), 5px);
+    color: #ffffff;
+    text-align: center;
+    border-radius: 4px;
+}
+
+.stage-input:disabled {
+    border-color: rgba(255, 255, 255, 0.2);
+    color: rgba(255, 255, 255, 0.5);
+    background-color: rgba(0, 0, 0, 0.2);
+}
+
 /* 弹窗过渡动画 */
-.fade-enter-active, .fade-leave-active {
+.fade-enter-active,
+.fade-leave-active {
     transition: opacity 0.3s, transform 0.3s;
 }
-.fade-enter, .fade-leave-to {
+
+.fade-enter,
+.fade-leave-to {
     opacity: 0;
     transform: translateY(-10px);
 }
@@ -888,10 +926,12 @@ export default {
     gap: clamp(4px, calc(var(--s) * 16px), 16px);
     width: 100%;
 }
+
 .donut-item {
     flex: 1;
     min-width: 0;
 }
+
 .donut-title {
     font-size: clamp(11px, calc(var(--s) * 13px), 14px);
     color: #a0aec0;