|
|
@@ -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;
|