同步规程同步代码和录入监测代码

This commit is contained in:
2026-05-23 19:34:52 +08:00
parent 6b58f37616
commit 35ad50a79d
29 changed files with 2357 additions and 329 deletions

View File

@@ -198,92 +198,12 @@
<el-button size="mini" @click="handleFindReset">重置</el-button>
</div>
<!-- 规程关联 -->
<div class="spec-bind-block">
<div class="panel-title">规程关联</div>
<template v-if="selectedRow">
<div class="bind-status">
<span class="bind-label">已关联版本</span>
<span v-if="coilBindingLoading" class="bind-val muted">加载中</span>
<el-tag v-else-if="coilBinding" type="success" size="mini" effect="plain">
{{ coilBinding.versionCode }}
</el-tag>
<span v-else class="bind-val muted">未关联</span>
</div>
<el-button
size="mini"
:type="coilBinding ? 'default' : 'primary'"
icon="el-icon-link"
style="width:100%;margin-top:8px"
@click="openSpecBindDialog"
>{{ coilBinding ? '重新关联' : '关联规程版本' }}</el-button>
</template>
<span v-else class="bind-val muted" style="font-size:12px">请先选择钢卷</span>
</div>
</div>
</div>
</div>
<quality-report-dialog ref="qualityReport" />
<!-- 规程版本选择弹窗 -->
<el-dialog
title="关联规程版本"
:visible.sync="specBindDialog"
width="580px"
append-to-body
@closed="specBindSelectedId = null"
>
<div class="spec-bind-toolbar">
<el-switch
v-model="specBindShowAll"
active-text="全部版本"
inactive-text="仅生效版本"
style="margin-right:8px"
/>
<span class="spec-bind-hint"> {{ specVersionsForDialog.length }} </span>
</div>
<el-table
v-loading="specVersionLoading"
:data="specVersionsForDialog"
size="mini"
border
highlight-current-row
max-height="340"
style="margin-top:10px"
@row-click="row => specBindSelectedId = row.versionId"
>
<el-table-column width="36" align="center">
<template slot-scope="{ row }">
<i
v-if="specBindSelectedId === row.versionId"
class="el-icon-check"
style="color:#409eff;font-weight:700"
/>
</template>
</el-table-column>
<el-table-column label="规程名称" prop="specName" min-width="140" show-overflow-tooltip />
<el-table-column label="版本号" prop="versionCode" width="90" />
<el-table-column label="状态" align="center" width="80">
<template slot-scope="{ row }">
<el-tag v-if="row.isActive === 1" type="success" size="mini" effect="dark">生效中</el-tag>
<el-tag v-else-if="row.status === 'PUBLISHED'" size="mini" effect="plain">已发布</el-tag>
<el-tag v-else type="info" size="mini" effect="plain">草稿</el-tag>
</template>
</el-table-column>
<el-table-column label="规程编码" prop="specCode" width="130" show-overflow-tooltip />
</el-table>
<div slot="footer">
<el-button size="small" @click="specBindDialog = false">取消</el-button>
<el-button
size="small"
type="primary"
:disabled="!specBindSelectedId"
:loading="specBindLoading"
@click="confirmSpecBind"
>确认关联</el-button>
</div>
</el-dialog>
</div>
</template>
@@ -299,12 +219,6 @@ import {
getTimingRealtimeData,
getPresetSetupByCoilId
} from '@/api/l2/timing'
import { listProcessSpecVersion, getProcessSpecVersion } from '@/api/wms/processSpecVersion'
import { listProcessCoilRecord, upsertProcessCoilRecord } from '@/api/wms/processCoilRecord'
import { listProcessSpec } from '@/api/wms/processSpec'
import { listProcessPlan } from '@/api/wms/processPlan'
import { listProcessPlanParam } from '@/api/wms/processPlanParam'
import { batchAddProcessAnomaly } from '@/api/wms/processAnomaly'
// 趋势参数树结构,对应 PLTCM_PRO_SEG 列名
const TREND_GROUPS = [
@@ -492,32 +406,6 @@ export default {
topTableHeight: 'calc(40vh - 80px)',
chartInstances: [],
resizeHandler: null,
// ── 规程关联 ──
coilBinding: null, // 当前选中钢卷已有的关联记录 { versionId, versionCode, ... }
coilBindingLoading: false,
specBindDialog: false,
specVersionLoading: false,
specVersionRawList: [], // 所有 wms_process_spec_version
specList: [], // 所有 wms_process_spec用于显示规程名
specBindSelectedId: null, // 弹窗内选中的 versionId
specBindShowAll: false, // true=显示全部版本false=只显示生效版本
specBindLoading: false
}
},
computed: {
/** 弹窗中展示的版本列表(含规程名,支持只看生效版本) */
specVersionsForDialog() {
const specMap = {}
this.specList.forEach(s => { specMap[s.specId] = s })
let list = this.specVersionRawList.map(v => ({
...v,
specCode: specMap[v.specId] ? specMap[v.specId].specCode : '—',
specName: specMap[v.specId] ? specMap[v.specId].specName : '—',
}))
if (!this.specBindShowAll) {
list = list.filter(v => v.isActive === 1)
}
return list
}
},
created() {
@@ -558,9 +446,7 @@ export default {
this.shapeRows = null
this.presetData = null
this.selectedTrendParam = null
this.coilBinding = null
this.disposeAllCharts()
this.loadCoilBinding()
const encoilId = row.ENCOILID || row.encoilid
const excoilId = row.EXCOILID || row.excoilid
@@ -979,7 +865,6 @@ export default {
this.gaugeRows = null
this.shapeRows = null
this.selectedTrendParam = null
this.coilBinding = null
this.disposeAllCharts()
this.pagination.page = 1
this.loadExcoilCount()
@@ -997,177 +882,6 @@ export default {
this.$refs.qualityReport.open(row, segData, gaugeRows, shapeRows, presetData)
},
// ── 规程关联 ─────────────────────────────────────────
/** 加载当前选中钢卷的已有规程关联记录 */
async loadCoilBinding() {
if (!this.selectedRow) return
const coilId = this.selectedRow.EXCOILID || this.selectedRow.excoilid
if (!coilId) return
this.coilBindingLoading = true
try {
const res = await listProcessCoilRecord({ coilId, pageNum: 1, pageSize: 1 })
const rec = (res.rows || [])[0]
if (rec) {
// 补充版本号显示
try {
const verRes = await getProcessSpecVersion(rec.versionId)
this.coilBinding = { ...rec, versionCode: verRes.data?.versionCode || String(rec.versionId) }
} catch {
this.coilBinding = { ...rec, versionCode: String(rec.versionId) }
}
} else {
this.coilBinding = null
}
} catch {
this.coilBinding = null
} finally {
this.coilBindingLoading = false
}
},
/** 打开选择规程版本的弹窗 */
async openSpecBindDialog() {
this.specBindSelectedId = this.coilBinding ? this.coilBinding.versionId : null
this.specBindShowAll = !this.coilBinding // 若已关联默认显示全部;否则只看生效版本
this.specBindDialog = true
this.specVersionLoading = true
try {
const [verRes, specRes] = await Promise.all([
listProcessSpecVersion({ pageNum: 1, pageSize: 500 }),
listProcessSpec({ pageNum: 1, pageSize: 200 })
])
this.specVersionRawList = verRes.rows || []
this.specList = specRes.rows || []
} finally {
this.specVersionLoading = false
}
},
/** 确认关联,并自动检测 L1 实绩与规程参数限值的偏差,将异常落库 */
async confirmSpecBind() {
if (!this.specBindSelectedId) return
this.specBindLoading = true
try {
const coilId = this.selectedRow.EXCOILID || this.selectedRow.excoilid
const enCoilId = this.selectedRow.ENCOILID || this.selectedRow.encoilid || null
const versionId = this.specBindSelectedId
// 1. 先写入基础关联记录(异常数稍后更新)
await upsertProcessCoilRecord({
versionId,
coilId,
enCoilId,
hasAnomaly: 0,
anomalyCnt: 0,
processTime: new Date().toISOString()
})
// 2. 异常检测(仅当存在 SEG 数据时执行)
let anomalyCnt = 0
if (this.segData) {
try {
// 2.1 获取该版本下所有方案点位
const planRes = await listProcessPlan({ versionId })
const plans = planRes.rows || []
// 2.2 汇总所有点位的参数
const allParams = []
for (const plan of plans) {
const paramRes = await listProcessPlanParam({ planId: plan.planId })
;(paramRes.rows || []).forEach(p => allParams.push({ ...p, planId: plan.planId }))
}
// 2.3 逐参数比对,构建异常列表
const anomalies = []
for (const param of allParams) {
const col = (param.paramCode || '').toUpperCase()
if (!col) continue
// 从 SEG 系列数据取段内最大/最小值数组,再求全卷极值
const maxArr = this.seg(col + 'MAX').filter(v => v != null)
const minArr = this.seg(col + 'MIN').filter(v => v != null)
if (!maxArr.length && !minArr.length) continue
const actualMax = maxArr.length ? Math.max(...maxArr) : null
const actualMin = minArr.length ? Math.min(...minArr) : null
// 从预设数据取实际设定值(如有对应映射)
let actualTarget = null
const presetCol = TREND_PRESET_MAP[col]
if (presetCol && this.presetData) {
const raw = this.presetData[presetCol] !== undefined
? this.presetData[presetCol]
: this.presetData[presetCol.toLowerCase()]
if (raw != null) actualTarget = parseFloat(Number(raw).toFixed(4))
}
const upper = param.upperLimit != null ? Number(param.upperLimit) : null
const lower = param.lowerLimit != null ? Number(param.lowerLimit) : null
if (upper == null && lower == null) continue // 未配置限值,跳过
const overMax = upper != null && actualMax != null && actualMax > upper
const underMin = lower != null && actualMin != null && actualMin < lower
if (!overMax && !underMin) continue
const anomalyType = (overMax && underMin) ? 'BOTH' : (overMax ? 'OVER_MAX' : 'UNDER_MIN')
anomalies.push({
versionId,
planId: param.planId,
paramId: param.paramId,
coilId,
enCoilId,
paramCode: param.paramCode,
paramName: param.paramName,
unit: param.unit || TREND_UNIT_MAP[col] || null,
anomalyType,
storedTarget: param.targetValue != null ? Number(param.targetValue) : null,
storedUpper: upper,
storedLower: lower,
actualTarget,
actualMax,
actualMin,
deviationMax: overMax ? parseFloat((actualMax - upper).toFixed(4)) : null,
deviationMin: underMin ? parseFloat((actualMin - lower).toFixed(4)) : null,
detectedAt: new Date().toISOString()
})
}
anomalyCnt = anomalies.length
// 2.4 批量写入异常记录
if (anomalies.length > 0) {
await batchAddProcessAnomaly(anomalies)
}
// 2.5 若有异常则更新关联记录的计数字段
if (anomalyCnt > 0) {
await upsertProcessCoilRecord({
versionId,
coilId,
enCoilId,
hasAnomaly: 1,
anomalyCnt,
processTime: new Date().toISOString()
})
}
} catch (e) {
console.warn('[规程关联] 异常检测失败,跳过', e)
}
}
const msg = anomalyCnt > 0
? `关联成功,检测到 ${anomalyCnt} 个参数异常`
: '关联成功,无异常'
this.$message[anomalyCnt > 0 ? 'warning' : 'success'](msg)
this.specBindDialog = false
await this.loadCoilBinding()
} catch {
this.$message.error('关联失败,请重试')
} finally {
this.specBindLoading = false
}
},
calcLengthPerTon(row) {
const len = parseFloat(row.EXIT_LENGTH || row.exit_length)
@@ -1389,45 +1103,5 @@ export default {
font-size: 13px;
}
/* ── 规程关联区块 ── */
.spec-bind-block {
border-top: 1px solid #ebeef5;
padding-top: 12px;
display: flex;
flex-direction: column;
gap: 6px;
}
.bind-status {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
}
.bind-label {
font-size: 12px;
color: #909399;
white-space: nowrap;
}
.bind-val {
font-size: 12px;
color: #303133;
font-weight: 500;
&.muted { color: #c0c4cc; font-weight: normal; }
}
/* ── 弹窗内工具栏 ── */
.spec-bind-toolbar {
display: flex;
align-items: center;
gap: 12px;
}
.spec-bind-hint {
font-size: 12px;
color: #909399;
}
</style>