feat(prediction): 工艺参数沙盘 — PI 累积曲线 + 最大速度安全区间条
预测页酸洗模型计算结果加 SVG 可视化: - PI 累积曲线(入口零点 → 5 槽累计 PI,含目标线 dashed 标注) - 速度安全区间条(20-180 m/min 区间内 max_speed 位置 + 数值标签) 零依赖,纯 SVG 内联。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -176,7 +176,57 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="calcResult.warning" class="warn-box mb12">{{ calcResult.warning }}</div>
|
||||
<div class="sec-title">各槽酸洗详情</div>
|
||||
|
||||
<div class="sec-title">速度安全区间</div>
|
||||
<div class="speed-bar-wrap">
|
||||
<div class="speed-bar-track">
|
||||
<div class="speed-bar-fill" :style="{ width: speedPct + '%' }"></div>
|
||||
<div class="speed-bar-tick" style="left:0%;">20</div>
|
||||
<div class="speed-bar-tick" style="left:100%;transform:translateX(-100%);">180</div>
|
||||
<div class="speed-bar-marker" :style="{ left: speedPct + '%' }">
|
||||
<div class="speed-bar-marker-label">{{ calcResult.max_speed }} m/min</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-title mt8">PI 累积曲线</div>
|
||||
<svg class="pi-chart" viewBox="0 0 400 180" preserveAspectRatio="none">
|
||||
<!-- 网格 -->
|
||||
<g stroke="rgba(255,255,255,0.06)" stroke-width="1">
|
||||
<line v-for="y in [0,25,50,75,100]" :key="'g'+y"
|
||||
x1="36" :y1="160 - y*1.3" x2="392" :y2="160 - y*1.3"/>
|
||||
</g>
|
||||
<!-- Y 轴刻度 -->
|
||||
<g fill="#7a8794" font-size="9" font-family="monospace">
|
||||
<text v-for="y in [0,25,50,75,100]" :key="'yl'+y"
|
||||
x="30" :y="163 - y*1.3" text-anchor="end">{{ y }}</text>
|
||||
</g>
|
||||
<!-- X 轴槽标签 -->
|
||||
<g fill="#7a8794" font-size="10" font-family="monospace" text-anchor="middle">
|
||||
<text v-for="i in 5" :key="'xl'+i" :x="36 + i*71.2" y="174">{{ i }}#</text>
|
||||
</g>
|
||||
<!-- 目标线 -->
|
||||
<line :x1="36" :y1="160 - calc.target_pi*1.3" :x2="392" :y2="160 - calc.target_pi*1.3"
|
||||
stroke="#f0a500" stroke-width="1" stroke-dasharray="4 3"/>
|
||||
<text :x="392" :y="160 - calc.target_pi*1.3 - 3"
|
||||
fill="#f0a500" font-size="9" text-anchor="end">目标 {{ calc.target_pi }}%</text>
|
||||
<!-- 曲线 -->
|
||||
<polyline fill="none" stroke="#00c8ff" stroke-width="2" :points="chartPoints"/>
|
||||
<!-- 数据点 + 标签 -->
|
||||
<g>
|
||||
<g v-for="(pi, i) in calcResult.pi_per_tank" :key="'p'+i">
|
||||
<circle :cx="36 + (i+1)*71.2" :cy="160 - pi*1.3" r="3.5" fill="#00c8ff"/>
|
||||
<text :x="36 + (i+1)*71.2" :y="160 - pi*1.3 - 7"
|
||||
fill="#00c8ff" font-size="9" text-anchor="middle">{{ pi }}</text>
|
||||
</g>
|
||||
</g>
|
||||
<!-- 入口零点 -->
|
||||
<circle cx="36" cy="160" r="2.5" fill="#7a8794"/>
|
||||
<polyline fill="none" stroke="#00c8ff" stroke-width="2" stroke-dasharray="2 2"
|
||||
:points="`36,160 ${36+71.2},${160 - calcResult.pi_per_tank[0]*1.3}`"/>
|
||||
</svg>
|
||||
|
||||
<div class="sec-title mt8">各槽酸洗详情</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr><th>酸槽</th><th>停留时间 (s)</th><th>累计PI (%)</th><th>进度</th></tr>
|
||||
@@ -356,6 +406,17 @@ export default {
|
||||
acidHistory() {
|
||||
return this.calibHistory.filter(h => h.model === 'acid_speed').slice(0, 10)
|
||||
},
|
||||
speedPct() {
|
||||
if (!this.calcResult) return 0
|
||||
const v = this.calcResult.max_speed
|
||||
return Math.max(0, Math.min(100, ((v - 20) / 160) * 100))
|
||||
},
|
||||
chartPoints() {
|
||||
if (!this.calcResult) return ''
|
||||
return this.calcResult.pi_per_tank
|
||||
.map((pi, i) => `${36 + (i+1)*71.2},${160 - pi*1.3}`)
|
||||
.join(' ')
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.fetchL1Data()
|
||||
@@ -534,6 +595,54 @@ export default {
|
||||
border-left: 2px solid $border;
|
||||
padding-left: 8px;
|
||||
}
|
||||
.pi-chart {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
display: block;
|
||||
background: rgba(0,0,0,.18);
|
||||
border: 1px solid $border;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.speed-bar-wrap { padding: 6px 8px 22px; }
|
||||
.speed-bar-track {
|
||||
position: relative;
|
||||
height: 8px;
|
||||
background: rgba(255,255,255,.06);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.speed-bar-fill {
|
||||
position: absolute;
|
||||
left: 0; top: 0; bottom: 0;
|
||||
background: linear-gradient(90deg, #28a745, #00c8ff);
|
||||
border-radius: 4px;
|
||||
transition: width .25s;
|
||||
}
|
||||
.speed-bar-tick {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
font-size: 10px;
|
||||
color: $text-muted;
|
||||
font-family: monospace;
|
||||
}
|
||||
.speed-bar-marker {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
width: 2px;
|
||||
height: 14px;
|
||||
background: #f0a500;
|
||||
transform: translateX(-1px);
|
||||
transition: left .25s;
|
||||
}
|
||||
.speed-bar-marker-label {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 10px;
|
||||
color: #f0a500;
|
||||
font-family: monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.calib-predict-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user