- Layout: 恢复顶部横向导航,图标换为 SVG 几何图形 - Login: 西门子风格重设计,去掉渐变/圆角/placeholder - 全局清除所有输入框 placeholder 说明文字 - 修复 Equipment.vue 重复 style 属性
547 lines
23 KiB
Vue
547 lines
23 KiB
Vue
<template>
|
||
<div>
|
||
<div class="flex-between" style="margin-bottom:4px;">
|
||
<div class="sec-title" style="margin-bottom:0;">🧪 酸洗工艺段模型</div>
|
||
<div class="flex-row">
|
||
<span class="kv-label">刷新时间:{{ lastRefresh }}</span>
|
||
<span :class="['badge', l1Online ? 'badge-green' : 'badge-yellow']" style="margin-left:8px;">
|
||
{{ l1Online ? 'L1在线' : '无L1数据' }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 酸槽 1-3 ─── -->
|
||
<div class="sec-title">酸槽 1#–3#</div>
|
||
<div class="grid-3">
|
||
<div v-for="i in [0,1,2]" :key="i" class="card">
|
||
<div class="card-header">
|
||
{{ i+1 }}# 酸槽
|
||
<span :class="['badge', tankBadge(tanks[i])]" style="margin-left:auto;">{{ tankStatus(tanks[i]) }}</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="kv-grid">
|
||
<span class="kv-label">HCl 浓度</span>
|
||
<span class="kv-value">{{ tanks[i].conc != null ? tanks[i].conc : '—' }} <span class="kv-unit">g/L</span></span>
|
||
<span class="kv-label">温度</span>
|
||
<span class="kv-value">{{ tanks[i].temp != null ? tanks[i].temp : '—' }} <span class="kv-unit">°C</span></span>
|
||
<span class="kv-label">Fe²⁺ 含量</span>
|
||
<span class="kv-value">{{ tanks[i].fe2 != null ? tanks[i].fe2 : '—' }} <span class="kv-unit">g/L</span></span>
|
||
<span class="kv-label">停留时间</span>
|
||
<span class="kv-value">{{ tanks[i].rt != null ? tanks[i].rt : '—' }} <span class="kv-unit">s</span></span>
|
||
</div>
|
||
<div class="mt8" v-if="tanks[i].eff != null">
|
||
<div class="flex-between" style="margin-bottom:4px;">
|
||
<span class="kv-label" style="font-size:11px;">酸洗效率(模型估算)</span>
|
||
<span style="font-size:11px;color:#00c8ff;">{{ tanks[i].eff }}%</span>
|
||
</div>
|
||
<div class="prog-bar-wrap">
|
||
<div class="prog-bar-fill" :style="{ width: tanks[i].eff + '%', background: effColor(tanks[i].eff) }"></div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="kv-label" style="margin-top:8px;text-align:center;">等待 L1 数据</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 酸槽 4-6 ─── -->
|
||
<div class="sec-title mt8">酸槽 4#–6#</div>
|
||
<div class="grid-3">
|
||
<div v-for="i in [3,4,5]" :key="i" class="card">
|
||
<div class="card-header">
|
||
{{ i+1 }}# 酸槽
|
||
<span :class="['badge', tankBadge(tanks[i])]" style="margin-left:auto;">{{ tankStatus(tanks[i]) }}</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="kv-grid">
|
||
<span class="kv-label">HCl 浓度</span>
|
||
<span class="kv-value">{{ tanks[i].conc != null ? tanks[i].conc : '—' }} <span class="kv-unit">g/L</span></span>
|
||
<span class="kv-label">温度</span>
|
||
<span class="kv-value">{{ tanks[i].temp != null ? tanks[i].temp : '—' }} <span class="kv-unit">°C</span></span>
|
||
<span class="kv-label">Fe²⁺ 含量</span>
|
||
<span class="kv-value">{{ tanks[i].fe2 != null ? tanks[i].fe2 : '—' }} <span class="kv-unit">g/L</span></span>
|
||
<span class="kv-label">停留时间</span>
|
||
<span class="kv-value">{{ tanks[i].rt != null ? tanks[i].rt : '—' }} <span class="kv-unit">s</span></span>
|
||
</div>
|
||
<div class="mt8" v-if="tanks[i].eff != null">
|
||
<div class="flex-between" style="margin-bottom:4px;">
|
||
<span class="kv-label" style="font-size:11px;">酸洗效率(模型估算)</span>
|
||
<span style="font-size:11px;color:#00c8ff;">{{ tanks[i].eff }}%</span>
|
||
</div>
|
||
<div class="prog-bar-wrap">
|
||
<div class="prog-bar-fill" :style="{ width: tanks[i].eff + '%', background: effColor(tanks[i].eff) }"></div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="kv-label" style="margin-top:8px;text-align:center;">等待 L1 数据</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 漂洗段 ─── -->
|
||
<div class="sec-title mt8">漂洗段(5级逆流)</div>
|
||
<div class="card">
|
||
<div class="card-header">漂洗段状态</div>
|
||
<div class="card-body">
|
||
<div v-if="l1Online" class="table-scroll">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr><th>漂洗级</th><th>pH值</th><th>温度 (°C)</th><th>流量 (m³/h)</th><th>电导率 (μS/cm)</th><th>状态</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="(r, idx) in rinse" :key="idx">
|
||
<td class="td-num">{{ idx+1 }}# 漂洗</td>
|
||
<td>{{ r.ph != null ? r.ph : '—' }}</td>
|
||
<td class="td-num">{{ r.temp != null ? r.temp : '—' }}</td>
|
||
<td class="td-num">{{ r.flow != null ? r.flow : '—' }}</td>
|
||
<td class="td-num">{{ r.conductivity != null ? r.conductivity : '—' }}</td>
|
||
<td><span class="badge badge-green">正常</span></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div v-else style="text-align:center;padding:20px;color:var(--text-muted);">
|
||
漂洗段数据来自 L1 实时报文,当前无 L1 连接
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 模型计算面板 ─── -->
|
||
<div class="section-row mt8">
|
||
<div class="card" style="flex:1;">
|
||
<div class="card-header">⚙️ 最优速度计算(实时自动)</div>
|
||
<div class="card-body">
|
||
<div class="grid-2" style="gap:12px;margin-bottom:14px;">
|
||
<div class="form-field">
|
||
<div class="kv-label">带钢厚度 (mm)</div>
|
||
<input v-model.number="calc.thickness" type="number" class="kv-input" step="0.1" min="2.0" max="4.5" @change="doCalc" />
|
||
</div>
|
||
<div class="form-field">
|
||
<div class="kv-label">带钢宽度 (mm)</div>
|
||
<input v-model.number="calc.width" type="number" class="kv-input" step="10" min="800" max="1250" @change="doCalc" />
|
||
</div>
|
||
<div class="form-field">
|
||
<div class="kv-label">钢种</div>
|
||
<select v-model="calc.steel_grade" class="kv-input" @change="doCalc">
|
||
<option v-for="g in steelGrades" :key="g" :value="g">{{ g }}</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-field">
|
||
<div class="kv-label">目标酸洗指数 (%)</div>
|
||
<input v-model.number="calc.target_pi" type="number" class="kv-input" step="1" min="70" max="100" @change="doCalc" />
|
||
</div>
|
||
<div class="form-field">
|
||
<div class="kv-label">氧化铁皮重量 (g/m²)</div>
|
||
<input v-model.number="calc.scale_weight" type="number" class="kv-input" step="0.5" @change="doCalc" />
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sec-title">各槽酸液参数
|
||
<span class="kv-label" style="font-size:10px;margin-left:8px;">
|
||
{{ l1Online ? '已从 L1 报文同步' : '手动输入(L1 上线后自动同步)' }}
|
||
</span>
|
||
</div>
|
||
<div class="grid-3" style="gap:8px;margin-bottom:8px;">
|
||
<div v-for="i in 6" :key="i" class="form-field">
|
||
<div class="kv-label">{{ i }}# 槽浓度 (g/L)</div>
|
||
<input v-model.number="calc.acid_conc_list[i-1]" type="number" class="kv-input" step="5" @change="doCalc" />
|
||
</div>
|
||
</div>
|
||
<div class="grid-3" style="gap:8px;margin-bottom:14px;">
|
||
<div v-for="i in 6" :key="'t'+i" class="form-field">
|
||
<div class="kv-label">{{ i }}# 槽温度 (°C)</div>
|
||
<input v-model.number="calc.acid_temp_list[i-1]" type="number" class="kv-input" step="1" @change="doCalc" />
|
||
</div>
|
||
</div>
|
||
<div v-if="calculating" style="text-align:center;color:var(--text-muted);padding:8px;">计算中...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 计算结果 -->
|
||
<div class="card" style="flex:1;" v-if="calcResult">
|
||
<div class="card-header">计算结果
|
||
<span :class="['badge', riskBadge(calcResult.under_pickling_risk)]" style="margin-left:auto;">
|
||
欠酸洗风险:{{ calcResult.under_pickling_risk }}
|
||
</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="grid-2" style="gap:10px;margin-bottom:14px;">
|
||
<div class="metric-box">
|
||
<div class="mb-label">最大允许速度</div>
|
||
<div class="mb-value">{{ calcResult.max_speed }}</div>
|
||
<div class="mb-unit">m/min</div>
|
||
</div>
|
||
<div class="metric-box">
|
||
<div class="mb-label">综合酸洗指数</div>
|
||
<div class="mb-value">{{ calcResult.total_pi }}</div>
|
||
<div class="mb-unit">%</div>
|
||
</div>
|
||
</div>
|
||
<div v-if="calcResult.warning" class="warn-box mb12">{{ calcResult.warning }}</div>
|
||
<div class="sec-title">各槽酸洗详情</div>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr><th>酸槽</th><th>停留时间 (s)</th><th>累计PI (%)</th><th>进度</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="(pi, idx) in calcResult.pi_per_tank" :key="idx">
|
||
<td class="td-num">{{ idx+1 }}# 槽</td>
|
||
<td class="td-num">{{ calcResult.residence_time_per_tank[idx] }}</td>
|
||
<td class="td-num">{{ pi }}</td>
|
||
<td>
|
||
<div class="prog-bar-wrap" style="width:80px;display:inline-block;">
|
||
<div class="prog-bar-fill" :style="{ width: pi + '%', background: effColor(pi) }"></div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="card" style="flex:1;display:flex;align-items:center;justify-content:center;" v-else>
|
||
<div style="text-align:center;color:var(--text-muted);padding:40px;">
|
||
<div style="font-size:11px;letter-spacing:2px;color:var(--text-muted);margin-bottom:12px;">[ LOADING ]</div>
|
||
<div>{{ calculating ? '模型计算中...' : '正在加载...' }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 模型校准 ─── -->
|
||
<div class="card mt8">
|
||
<div class="card-header">
|
||
酸洗速度模型校准
|
||
<span class="ch-badge" :style="{ background: kCalColor(calib.current_kcal) }">
|
||
K_cal = {{ calib.current_kcal }}
|
||
</span>
|
||
<button class="btn btn-outline" style="margin-left:auto;padding:2px 10px;font-size:11px;"
|
||
@click="resetCalib('acid_speed')">重置系数</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="calib-layout">
|
||
<!-- 录入表单 -->
|
||
<div class="calib-form">
|
||
<div class="calib-hint">当模型预测速度与实际可用速度存在偏差时,录入实测数据进行修正。</div>
|
||
<div class="flex-col" style="gap:8px;margin-top:10px;">
|
||
<div class="form-field">
|
||
<div class="kv-label">实测最高合格速度 (m/min)</div>
|
||
<input v-model.number="calib.actual_speed" type="number" class="kv-input" step="1" min="20" max="180" />
|
||
</div>
|
||
<div class="form-field">
|
||
<div class="kv-label">该速度下质量结果</div>
|
||
<select v-model="calib.quality_ok" class="kv-input">
|
||
<option :value="true">合格(酸洗充分)</option>
|
||
<option :value="false">欠酸洗(出现缺陷)</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-field">
|
||
<div class="kv-label">备注(可选)</div>
|
||
<input v-model="calib.note" type="text" class="kv-input" />
|
||
</div>
|
||
<div v-if="calcResult" class="calib-predict-row">
|
||
<span class="kv-label">模型预测速度</span>
|
||
<span class="kv-value">{{ calcResult.max_speed }} <span class="kv-unit">m/min</span></span>
|
||
<span class="kv-label" style="margin-left:16px;">偏差</span>
|
||
<span class="kv-value" :style="{ color: Math.abs(calib.actual_speed - calcResult.max_speed) > 10 ? '#f0a500' : '#28a745' }">
|
||
{{ calib.actual_speed && calcResult ? (calib.actual_speed - calcResult.max_speed > 0 ? '+' : '') + (calib.actual_speed - calcResult.max_speed).toFixed(1) : '—' }} m/min
|
||
</span>
|
||
</div>
|
||
<button class="btn btn-primary fw" :disabled="calib.loading || !calib.actual_speed" @click="submitCalib">
|
||
{{ calib.loading ? '提交中...' : '提交修正数据' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<!-- 历史记录 -->
|
||
<div class="calib-history">
|
||
<div class="sec-title" style="margin-bottom:8px;">修正记录</div>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr><th>时间</th><th>K 前</th><th>K 后</th><th>实测速度</th><th>质量</th><th>备注</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="h in acidHistory" :key="h.ts">
|
||
<td class="td-muted">{{ h.ts.slice(5,16) }}</td>
|
||
<td class="td-num">{{ h.k_before }}</td>
|
||
<td class="td-num" :style="{ color: h.k_after > h.k_before ? '#28a745' : '#f0a500' }">{{ h.k_after }}</td>
|
||
<td class="td-num">{{ h.input.actual_speed }} m/min</td>
|
||
<td><span :class="['badge', h.input.quality_ok ? 'badge-green' : 'badge-red']">{{ h.input.quality_ok ? '合格' : '欠酸洗' }}</span></td>
|
||
<td class="td-muted">{{ h.note || '—' }}</td>
|
||
</tr>
|
||
<tr v-if="!acidHistory.length">
|
||
<td colspan="6" class="td-muted" style="text-align:center;padding:14px;">暂无修正记录</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 当前过程参数(L1实时)─── -->
|
||
<div class="card mt8">
|
||
<div class="card-header">当前过程参数(L1 实时)</div>
|
||
<div class="card-body">
|
||
<div v-if="l1Online" class="grid-5">
|
||
<div class="metric-box">
|
||
<div class="mb-label">当前线速</div>
|
||
<div class="mb-value">{{ current.speed }}</div>
|
||
<div class="mb-unit">m/min</div>
|
||
</div>
|
||
<div class="metric-box">
|
||
<div class="mb-label">入口张力</div>
|
||
<div class="mb-value">{{ current.tension_inlet }}</div>
|
||
<div class="mb-unit">kN</div>
|
||
</div>
|
||
<div class="metric-box">
|
||
<div class="mb-label">出口张力</div>
|
||
<div class="mb-value">{{ current.tension_outlet }}</div>
|
||
<div class="mb-unit">kN</div>
|
||
</div>
|
||
<div class="metric-box">
|
||
<div class="mb-label">1#槽酸液温度</div>
|
||
<div class="mb-value">{{ current.acid_temp }}</div>
|
||
<div class="mb-unit">°C</div>
|
||
</div>
|
||
<div class="metric-box">
|
||
<div class="mb-label">当前卷号</div>
|
||
<div class="mb-value" style="font-size:14px;">{{ current.coil_no || '—' }}</div>
|
||
<div class="mb-unit">在线</div>
|
||
</div>
|
||
</div>
|
||
<div v-else style="text-align:center;padding:24px;color:var(--text-muted);">
|
||
<div style="font-size:11px;letter-spacing:2px;color:var(--text-muted);margin-bottom:8px;">[ NO SIGNAL ]</div>
|
||
<div>当前无 L1 实时数据。机组启动并接入 UDP 报文后自动显示。</div>
|
||
<div style="font-size:11px;margin-top:6px;">L2 监听地址:0.0.0.0:9000</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { predictAcidSpeed, getMessageLogs, getCalibration, calibrateAcidSpeed, resetCalibration } from '@/api'
|
||
|
||
const STEEL_GRADES = ['Q195','Q215','Q235','SPHC','SPHD','SPHE','SS400','SAPH440','QSTE420TM']
|
||
|
||
export default {
|
||
name: 'ProcessModel',
|
||
data() {
|
||
return {
|
||
lastRefresh: '--:--:--',
|
||
l1Online: false,
|
||
tanks: Array.from({ length: 6 }, () => ({ conc: null, temp: null, fe2: null, rt: null, eff: null })),
|
||
rinse: Array.from({ length: 5 }, () => ({ ph: null, temp: null, flow: null, conductivity: null })),
|
||
current: { speed: null, tension_inlet: null, tension_outlet: null, acid_temp: null, coil_no: null },
|
||
steelGrades: STEEL_GRADES,
|
||
calc: {
|
||
thickness: 3.0,
|
||
width: 1000,
|
||
steel_grade: 'Q235',
|
||
target_pi: 95,
|
||
scale_weight: 8.5,
|
||
acid_conc_list: [200, 188, 175, 162, 148, 135],
|
||
acid_temp_list: [80, 78, 76, 75, 74, 72],
|
||
},
|
||
calculating: false,
|
||
calcResult: null,
|
||
_timer: null,
|
||
calib: {
|
||
current_kcal: 1.0,
|
||
actual_speed: null,
|
||
quality_ok: true,
|
||
note: '',
|
||
loading: false,
|
||
},
|
||
calibHistory: [],
|
||
}
|
||
},
|
||
computed: {
|
||
acidHistory() {
|
||
return this.calibHistory.filter(h => h.model === 'acid_speed').slice(0, 10)
|
||
},
|
||
},
|
||
async mounted() {
|
||
await this.fetchL1Data()
|
||
await this.doCalc()
|
||
await this.loadCalibration()
|
||
this._timer = setInterval(() => this.fetchL1Data(), 5000)
|
||
},
|
||
beforeDestroy() {
|
||
clearInterval(this._timer)
|
||
},
|
||
methods: {
|
||
async fetchL1Data() {
|
||
try {
|
||
// 拉最新一条 PC03 过程数据报文
|
||
const res = await getMessageLogs({ msg_type: 'PC03', page_size: 1 })
|
||
const logs = res.data?.items || []
|
||
if (logs.length && logs[0].parsed_data) {
|
||
const d = typeof logs[0].parsed_data === 'string'
|
||
? JSON.parse(logs[0].parsed_data) : logs[0].parsed_data
|
||
this.l1Online = true
|
||
this.current.speed = d.speed ?? null
|
||
this.current.tension_inlet = d.tension_inlet ?? null
|
||
this.current.tension_outlet = d.tension_outlet ?? null
|
||
this.current.acid_temp = d.acid_temp ?? null
|
||
this.current.coil_no = d.coil_no ?? null
|
||
// 如果报文中有各槽浓度/温度,同步到计算参数
|
||
if (d.acid_conc_list) this.calc.acid_conc_list = d.acid_conc_list
|
||
if (d.acid_temp_list) this.calc.acid_temp_list = d.acid_temp_list
|
||
// 更新槽显示(用计算参数中的值,真实值未来扩展报文后替换)
|
||
this.syncTanksFromCalc()
|
||
await this.doCalc()
|
||
} else {
|
||
this.l1Online = false
|
||
}
|
||
} catch (e) {
|
||
this.l1Online = false
|
||
}
|
||
this.lastRefresh = new Date().toTimeString().slice(0, 8)
|
||
},
|
||
syncTanksFromCalc() {
|
||
this.tanks = this.calc.acid_conc_list.map((conc, i) => {
|
||
const temp = this.calc.acid_temp_list[i]
|
||
const eff = +(Math.min(98, 50 + (conc / 200) * 35 + (temp - 60) / 25 * 15)).toFixed(1)
|
||
return { conc, temp, fe2: null, rt: null, eff }
|
||
})
|
||
},
|
||
async doCalc() {
|
||
this.calculating = true
|
||
try {
|
||
const res = await predictAcidSpeed({
|
||
thickness: this.calc.thickness,
|
||
width: this.calc.width,
|
||
steel_grade: this.calc.steel_grade,
|
||
acid_conc_list: this.calc.acid_conc_list,
|
||
acid_temp_list: this.calc.acid_temp_list,
|
||
scale_weight: this.calc.scale_weight,
|
||
target_pi: this.calc.target_pi,
|
||
})
|
||
this.calcResult = res.data
|
||
// 用计算结果的停留时间更新槽显示
|
||
if (res.data.residence_time_per_tank) {
|
||
res.data.residence_time_per_tank.forEach((rt, i) => {
|
||
if (this.tanks[i]) this.tanks[i].rt = rt
|
||
})
|
||
}
|
||
} catch (e) {
|
||
// silent — user will see calcResult stay null
|
||
} finally {
|
||
this.calculating = false
|
||
}
|
||
},
|
||
tankBadge(t) {
|
||
if (t.conc == null) return 'badge-yellow'
|
||
if (t.conc < 100 || t.temp < 65) return 'badge-red'
|
||
if (t.conc < 140 || t.temp < 72) return 'badge-yellow'
|
||
return 'badge-green'
|
||
},
|
||
tankStatus(t) {
|
||
if (t.conc == null) return '待接入'
|
||
if (t.conc < 100 || t.temp < 65) return '报警'
|
||
if (t.conc < 140 || t.temp < 72) return '预警'
|
||
return '正常'
|
||
},
|
||
riskBadge(r) {
|
||
if (r === 'HIGH') return 'badge-red'
|
||
if (r === 'MEDIUM') return 'badge-yellow'
|
||
return 'badge-green'
|
||
},
|
||
effColor(v) {
|
||
if (v >= 80) return '#28a745'
|
||
if (v >= 60) return '#f0a500'
|
||
return '#da3633'
|
||
},
|
||
async loadCalibration() {
|
||
try {
|
||
const res = await getCalibration()
|
||
this.calib.current_kcal = res.data?.acid_speed_kcal ?? 1.0
|
||
this.calibHistory = res.data?.history || []
|
||
} catch (e) { /* silent */ }
|
||
},
|
||
async submitCalib() {
|
||
if (!this.calib.actual_speed) return
|
||
this.calib.loading = true
|
||
try {
|
||
await calibrateAcidSpeed({
|
||
thickness: this.calc.thickness,
|
||
width: this.calc.width,
|
||
steel_grade: this.calc.steel_grade,
|
||
acid_conc_list: this.calc.acid_conc_list,
|
||
acid_temp_list: this.calc.acid_temp_list,
|
||
scale_weight: this.calc.scale_weight,
|
||
actual_max_speed: this.calib.actual_speed,
|
||
actual_quality_ok: this.calib.quality_ok,
|
||
note: this.calib.note,
|
||
})
|
||
this.$message.success('修正数据已提交,模型系数已更新')
|
||
this.calib.actual_speed = null
|
||
this.calib.note = ''
|
||
await this.loadCalibration()
|
||
await this.doCalc()
|
||
} catch (e) {
|
||
this.$message.error('提交失败:' + (e.response?.data?.detail || e.message))
|
||
} finally {
|
||
this.calib.loading = false
|
||
}
|
||
},
|
||
async resetCalib(model) {
|
||
try {
|
||
await this.$confirm('确认将校准系数重置为 1.0?', '重置确认', { type: 'warning' })
|
||
await resetCalibration(model)
|
||
this.$message.success('已重置')
|
||
await this.loadCalibration()
|
||
await this.doCalc()
|
||
} catch (e) { /* cancelled */ }
|
||
},
|
||
kCalColor(k) {
|
||
const d = Math.abs(k - 1.0)
|
||
if (d < 0.05) return '#1a3a1f'
|
||
if (d < 0.15) return '#3a2a00'
|
||
return '#3a1a1a'
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import '@/assets/styles/variables';
|
||
.form-field { display: flex; flex-direction: column; gap: 5px; }
|
||
.warn-box {
|
||
background: rgba(240,165,0,.1);
|
||
border: 1px solid $accent-yellow;
|
||
border-radius: 4px;
|
||
padding: 8px 12px;
|
||
color: $accent-yellow;
|
||
font-size: 12px;
|
||
}
|
||
.mb12 { margin-bottom: 12px; }
|
||
.mt8 { margin-top: 8px; }
|
||
|
||
.calib-layout {
|
||
display: flex;
|
||
gap: 20px;
|
||
align-items: flex-start;
|
||
}
|
||
.calib-form {
|
||
flex: 0 0 300px;
|
||
}
|
||
.calib-history {
|
||
flex: 1;
|
||
overflow-x: auto;
|
||
}
|
||
.calib-hint {
|
||
font-size: 11px;
|
||
color: $text-muted;
|
||
line-height: 1.6;
|
||
border-left: 2px solid $border;
|
||
padding-left: 8px;
|
||
}
|
||
.calib-predict-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 6px 10px;
|
||
background: rgba(255,255,255,.03);
|
||
border-radius: 4px;
|
||
border: 1px solid $border;
|
||
}
|
||
</style>
|