工艺规程增强
This commit is contained in:
@@ -161,7 +161,7 @@ redisson:
|
|||||||
# 客户端名称
|
# 客户端名称
|
||||||
clientName: ${klp.name}
|
clientName: ${klp.name}
|
||||||
# 最小空闲连接数
|
# 最小空闲连接数
|
||||||
connectionMinimumIdleSize: 32
|
connectionMinimumIdleSize: 4
|
||||||
# 连接池大小
|
# 连接池大小
|
||||||
connectionPoolSize: 64
|
connectionPoolSize: 64
|
||||||
# 连接空闲超时,单位:毫秒
|
# 连接空闲超时,单位:毫秒
|
||||||
|
|||||||
13
klp-ui/src/api/wms/processAnomaly.js
Normal file
13
klp-ui/src/api/wms/processAnomaly.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function listProcessAnomaly(query) {
|
||||||
|
return request({ url: '/wms/processAnomaly/list', method: 'get', params: query })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listAllProcessAnomaly(query) {
|
||||||
|
return request({ url: '/wms/processAnomaly/listAll', method: 'get', params: query })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function batchAddProcessAnomaly(data) {
|
||||||
|
return request({ url: '/wms/processAnomaly/batchAdd', method: 'post', data })
|
||||||
|
}
|
||||||
17
klp-ui/src/api/wms/processCoilRecord.js
Normal file
17
klp-ui/src/api/wms/processCoilRecord.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function listProcessCoilRecord(query) {
|
||||||
|
return request({ url: '/wms/processCoilRecord/list', method: 'get', params: query })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listAllProcessCoilRecord(query) {
|
||||||
|
return request({ url: '/wms/processCoilRecord/listAll', method: 'get', params: query })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function countProcessCoilRecord(versionId) {
|
||||||
|
return request({ url: '/wms/processCoilRecord/count', method: 'get', params: { versionId } })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function upsertProcessCoilRecord(data) {
|
||||||
|
return request({ url: '/wms/processCoilRecord/upsert', method: 'post', data })
|
||||||
|
}
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<!-- ③ 带钢板形 热力图 -->
|
<!-- ③ 带钢板形 -->
|
||||||
<el-tab-pane label="带钢板形" name="flatness3d">
|
<el-tab-pane label="带钢板形" name="flatness3d">
|
||||||
<div v-if="!selectedRow" class="no-data-hint">请在上方选择钢卷</div>
|
<div v-if="!selectedRow" class="no-data-hint">请在上方选择钢卷</div>
|
||||||
<div v-else-if="realtimeLoading" class="no-data-hint">加载中…</div>
|
<div v-else-if="realtimeLoading" class="no-data-hint">加载中…</div>
|
||||||
@@ -203,8 +203,14 @@ import {
|
|||||||
getExcoilList,
|
getExcoilList,
|
||||||
getExcoilCount,
|
getExcoilCount,
|
||||||
getTimingSegByEncoilId,
|
getTimingSegByEncoilId,
|
||||||
getTimingRealtimeData
|
getTimingRealtimeData,
|
||||||
|
getPresetSetupByCoilId
|
||||||
} from '@/api/l2/timing'
|
} from '@/api/l2/timing'
|
||||||
|
import { listProcessSpecVersion } from '@/api/wms/processSpecVersion'
|
||||||
|
import { listProcessPlan, addProcessPlan } from '@/api/wms/processPlan'
|
||||||
|
import { listProcessPlanParam, addProcessPlanParam, updateProcessPlanParam } from '@/api/wms/processPlanParam'
|
||||||
|
import { upsertProcessCoilRecord } from '@/api/wms/processCoilRecord'
|
||||||
|
import { batchAddProcessAnomaly } from '@/api/wms/processAnomaly'
|
||||||
|
|
||||||
// 趋势参数树结构,对应 PLTCM_PRO_SEG 列名
|
// 趋势参数树结构,对应 PLTCM_PRO_SEG 列名
|
||||||
const TREND_GROUPS = [
|
const TREND_GROUPS = [
|
||||||
@@ -265,10 +271,26 @@ const SHAPE_SCALAR_COLS = [
|
|||||||
{ col: 'IRBEND', title: '中间辊弯辊力 [kN]' }
|
{ col: 'IRBEND', title: '中间辊弯辊力 [kN]' }
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
// 参数单位映射
|
||||||
* 从数据数组计算合理的 Y 轴范围(带 15% 上下边距)。
|
const TREND_UNIT_MAP = {
|
||||||
* echarts 默认 scale 在数据变化幅度极小时会把轴拉到很大范围,导致曲线看起来是横线。
|
PORTENS: 'N/mm²', ENLTENS: 'N/mm²', TLTENS: 'N/mm²', PLTENS: 'N/mm²',
|
||||||
*/
|
CXLTENS: 'N/mm²', TRIMTENS: 'N/mm²', TRTENS: 'N/mm²', TELTENS: 'N/mm²',
|
||||||
|
PORSPEED: 'm/min', PLSPEED: 'm/min', TRIMSPEED: 'm/min',
|
||||||
|
MILLENTRYSPEED: 'm/min', MILLEXITSPEED: 'm/min',
|
||||||
|
TLMESH1: 'mm', TLMESH2: 'mm', TLMESH3: 'mm',
|
||||||
|
TLELONG: '%',
|
||||||
|
TK1TEMP: '℃', TK2TEMP: '℃', TK3TEMP: '℃', RINSETEMP: '℃'
|
||||||
|
}
|
||||||
|
|
||||||
|
// PLTCM_PRO_SEG 列 → PLTCM_PRESET_SETUP 设定值列
|
||||||
|
const TREND_PRESET_MAP = {
|
||||||
|
PORTENS: 'POR_TEN', ENLTENS: 'CEL_TEN', TLTENS: 'TLV_TEN',
|
||||||
|
PLTENS: 'CPL_TEN', CXLTENS: 'CXL_TEN', TRIMTENS: 'TRIM_TEN',
|
||||||
|
TRTENS: 'TR_TEN', TELTENS: 'TEL_TEN',
|
||||||
|
TLMESH1: 'TLV_MESH_1', TLMESH2: 'TLV_MESH_2', TLMESH3: 'TLV_MESH_3',
|
||||||
|
TLELONG: 'TLV_ELONG', PLSPEED: 'CPL_MAX_SPEED'
|
||||||
|
}
|
||||||
|
|
||||||
function calcYRange(vals) {
|
function calcYRange(vals) {
|
||||||
const nums = vals.filter(v => v != null && isFinite(Number(v))).map(Number)
|
const nums = vals.filter(v => v != null && isFinite(Number(v))).map(Number)
|
||||||
if (!nums.length) return {}
|
if (!nums.length) return {}
|
||||||
@@ -279,37 +301,48 @@ function calcYRange(vals) {
|
|||||||
return { min: parseFloat((min - base * 0.2).toFixed(4)), max: parseFloat((max + base * 0.2).toFixed(4)) }
|
return { min: parseFloat((min - base * 0.2).toFixed(4)), max: parseFloat((max + base * 0.2).toFixed(4)) }
|
||||||
}
|
}
|
||||||
const pad = (max - min) * 0.15
|
const pad = (max - min) * 0.15
|
||||||
return {
|
return { min: parseFloat((min - pad).toFixed(4)), max: parseFloat((max + pad).toFixed(4)) }
|
||||||
min: parseFloat((min - pad).toFixed(4)),
|
|
||||||
max: parseFloat((max + pad).toFixed(4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeLine(title, xData, yData) {
|
/**
|
||||||
const range = calcYRange(yData)
|
* 生成折线图 option。
|
||||||
|
* extras: [{ name, data, color, dash }] — 上下限或参考线
|
||||||
|
*/
|
||||||
|
function makeLine(title, xData, yData, extras = []) {
|
||||||
|
const allVals = [yData, ...extras.map(e => e.data)].flat()
|
||||||
|
const range = calcYRange(allVals)
|
||||||
|
const hasExtras = extras.length > 0
|
||||||
|
const mainSeries = {
|
||||||
|
name: title, type: 'line', smooth: false, symbol: 'none',
|
||||||
|
lineStyle: { width: 1.5, color: '#409EFF' }, data: yData, z: 3
|
||||||
|
}
|
||||||
|
const extraSeries = extras.map(e => ({
|
||||||
|
name: e.name,
|
||||||
|
type: 'line', smooth: false, symbol: 'none',
|
||||||
|
lineStyle: { width: 1, color: e.color || '#E6A23C', type: e.dash !== false ? 'dashed' : 'solid' },
|
||||||
|
data: e.data, z: 2
|
||||||
|
}))
|
||||||
return {
|
return {
|
||||||
title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 4, left: 8 },
|
title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 4, left: 8 },
|
||||||
|
legend: hasExtras
|
||||||
|
? { data: [title, ...extras.map(e => e.name)], top: 4, right: 4,
|
||||||
|
textStyle: { fontSize: 9 }, itemWidth: 14, itemHeight: 8 }
|
||||||
|
: { show: false },
|
||||||
tooltip: { trigger: 'axis' },
|
tooltip: { trigger: 'axis' },
|
||||||
grid: { top: 36, bottom: 28, left: 8, right: 16, containLabel: true },
|
grid: { top: hasExtras ? 44 : 36, bottom: 28, left: 8, right: 16, containLabel: true },
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category', data: xData,
|
type: 'category', data: xData,
|
||||||
name: 'pos(m)', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 }
|
name: 'pos(m)', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 }
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value', min: range.min, max: range.max,
|
||||||
min: range.min,
|
nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 }
|
||||||
max: range.max,
|
|
||||||
nameTextStyle: { fontSize: 10 },
|
|
||||||
axisLabel: { fontSize: 10 }
|
|
||||||
},
|
},
|
||||||
dataZoom: [
|
dataZoom: [
|
||||||
{ type: 'inside', xAxisIndex: 0, zoomOnMouseWheel: true, moveOnMouseMove: true },
|
{ type: 'inside', xAxisIndex: 0, zoomOnMouseWheel: true, moveOnMouseMove: true },
|
||||||
{ type: 'inside', yAxisIndex: 0, zoomOnMouseWheel: false, moveOnMouseWheel: true }
|
{ type: 'inside', yAxisIndex: 0, zoomOnMouseWheel: false, moveOnMouseWheel: true }
|
||||||
],
|
],
|
||||||
series: [{
|
series: [mainSeries, ...extraSeries]
|
||||||
name: title, type: 'line', smooth: false, symbol: 'none',
|
|
||||||
lineStyle: { width: 1 }, data: yData
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,6 +376,7 @@ export default {
|
|||||||
expandedGroups: { '张力': true, '速度': true, '拉矫机': true, '酸洗段': true },
|
expandedGroups: { '张力': true, '速度': true, '拉矫机': true, '酸洗段': true },
|
||||||
selectedTrendParam: null,
|
selectedTrendParam: null,
|
||||||
trendChartInst: null,
|
trendChartInst: null,
|
||||||
|
presetData: null,
|
||||||
// 查找
|
// 查找
|
||||||
searchType: 'coil',
|
searchType: 'coil',
|
||||||
searchCoilId: '',
|
searchCoilId: '',
|
||||||
@@ -355,6 +389,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this._clickRev = 0
|
||||||
this.loadExcoilCount()
|
this.loadExcoilCount()
|
||||||
this.loadExcoilList()
|
this.loadExcoilList()
|
||||||
},
|
},
|
||||||
@@ -382,23 +417,31 @@ export default {
|
|||||||
this.loadExcoilList()
|
this.loadExcoilList()
|
||||||
},
|
},
|
||||||
async handleRowClick(row) {
|
async handleRowClick(row) {
|
||||||
|
// 快速点击防重:每次点击递增版本号,旧的 sync 任务检测到版本号变更后自动放弃
|
||||||
|
const clickRev = ++this._clickRev
|
||||||
this.selectedRow = row
|
this.selectedRow = row
|
||||||
this.segData = null
|
this.segData = null
|
||||||
this.gaugeRows = null
|
this.gaugeRows = null
|
||||||
this.shapeRows = null
|
this.shapeRows = null
|
||||||
|
this.presetData = null
|
||||||
this.selectedTrendParam = null
|
this.selectedTrendParam = null
|
||||||
this.disposeAllCharts()
|
this.disposeAllCharts()
|
||||||
|
|
||||||
// PLTCM_PRO_SEG 用 ENCOILID 查(主键前缀)
|
|
||||||
const encoilId = row.ENCOILID || row.encoilid
|
const encoilId = row.ENCOILID || row.encoilid
|
||||||
// V_VBDA_GAUGE / V_VBDA_SHAPE 用 EXCOILID 作为 MATID
|
|
||||||
const excoilId = row.EXCOILID || row.excoilid
|
const excoilId = row.EXCOILID || row.excoilid
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
encoilId ? this.loadSeg(encoilId) : Promise.resolve(),
|
encoilId ? this.loadSeg(encoilId) : Promise.resolve(),
|
||||||
|
encoilId ? this.loadPreset(encoilId) : Promise.resolve(),
|
||||||
excoilId ? this.loadRealtime(excoilId) : Promise.resolve()
|
excoilId ? this.loadRealtime(excoilId) : Promise.resolve()
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// 如果期间又点击了其他行则放弃后续操作
|
||||||
|
if (this._clickRev !== clickRev) return
|
||||||
|
|
||||||
|
// 后台静默同步到规程(不阻塞 UI)
|
||||||
|
this.autoSyncToActiveSpec(excoilId || encoilId, clickRev)
|
||||||
|
|
||||||
await this.$nextTick()
|
await this.$nextTick()
|
||||||
// 加载完成后自动选中第一个趋势参数
|
// 加载完成后自动选中第一个趋势参数
|
||||||
if (this.activeTab === 'trend' && this.segData) {
|
if (this.activeTab === 'trend' && this.segData) {
|
||||||
@@ -434,6 +477,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async loadPreset(coilId) {
|
||||||
|
try {
|
||||||
|
const res = await getPresetSetupByCoilId(coilId)
|
||||||
|
this.presetData = res?.data?.data || null
|
||||||
|
} catch (_) {
|
||||||
|
this.presetData = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// ── 趋势参数树 ──────────────────────────────
|
// ── 趋势参数树 ──────────────────────────────
|
||||||
toggleGroup(label) {
|
toggleGroup(label) {
|
||||||
this.$set(this.expandedGroups, label, !this.expandedGroups[label])
|
this.$set(this.expandedGroups, label, !this.expandedGroups[label])
|
||||||
@@ -457,9 +509,31 @@ export default {
|
|||||||
window.addEventListener('resize', resizeFn)
|
window.addEventListener('resize', resizeFn)
|
||||||
this._trendResizeFn = resizeFn
|
this._trendResizeFn = resizeFn
|
||||||
}
|
}
|
||||||
|
const col = this.selectedTrendParam.col
|
||||||
const x = this.segX()
|
const x = this.segX()
|
||||||
const yData = this.seg(this.selectedTrendParam.col)
|
const yData = this.seg(col)
|
||||||
this.trendChartInst.setOption(makeLine(this.selectedTrendParam.label, x, yData), true)
|
// 实测最大值 / 最小值:来自 PLTCM_PRO_SEG 的段内统计 MAX / MIN
|
||||||
|
const maxData = this.seg(col + 'MAX')
|
||||||
|
const minData = this.seg(col + 'MIN')
|
||||||
|
const extras = []
|
||||||
|
if (maxData.some(v => v != null)) {
|
||||||
|
extras.push({ name: '最大值', data: maxData, color: '#F56C6C', dash: true })
|
||||||
|
}
|
||||||
|
if (minData.some(v => v != null)) {
|
||||||
|
extras.push({ name: '最小值', data: minData, color: '#67C23A', dash: true })
|
||||||
|
}
|
||||||
|
// 设定值:来自 PLTCM_PRESET_SETUP(若有映射)
|
||||||
|
const presetCol = TREND_PRESET_MAP[col]
|
||||||
|
if (presetCol && this.presetData) {
|
||||||
|
const setVal = this.presetData[presetCol] !== undefined
|
||||||
|
? this.presetData[presetCol]
|
||||||
|
: this.presetData[presetCol.toLowerCase()]
|
||||||
|
if (setVal != null && Number(setVal) !== 0) {
|
||||||
|
const sv = Number(Number(setVal).toFixed(3))
|
||||||
|
extras.push({ name: '设定值', data: new Array(x.length).fill(sv), color: '#E6A23C', dash: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.trendChartInst.setOption(makeLine(this.selectedTrendParam.label, x, yData, extras), true)
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Tab 切换 ────────────────────────────────
|
// ── Tab 切换 ────────────────────────────────
|
||||||
@@ -533,14 +607,38 @@ export default {
|
|||||||
const rows = this.gaugeRows
|
const rows = this.gaugeRows
|
||||||
if (!rows || !rows.length) return
|
if (!rows || !rows.length) return
|
||||||
const xData = xLocData(rows)
|
const xData = xLocData(rows)
|
||||||
const refs = ['chartGauge1', 'chartGauge2', 'chartGauge3', 'chartGauge4']
|
// THICK0~THICK4 对应的参考列
|
||||||
const charts = refs.map((ref, i) => {
|
const refColMap = { THICK0: 'THICK0REF', THICK1: 'THICK1REF', THICK4: 'THICK4REF', THICK5: 'THICK5REF' }
|
||||||
|
const chartRefs = ['chartGauge1', 'chartGauge2', 'chartGauge3', 'chartGauge4']
|
||||||
|
const charts = chartRefs.map((ref, i) => {
|
||||||
const { col, title } = GAUGE_COLS[i]
|
const { col, title } = GAUGE_COLS[i]
|
||||||
const yData = rows.map(r => {
|
const yData = rows.map(r => {
|
||||||
const v = getRowVal(r, col)
|
const v = getRowVal(r, col)
|
||||||
return v == null ? null : parseFloat(v.toFixed(4))
|
return v == null ? null : parseFloat(v.toFixed(4))
|
||||||
})
|
})
|
||||||
return this.makeChart(ref, makeLine(title, xData, yData))
|
const extras = []
|
||||||
|
const refCol = refColMap[col]
|
||||||
|
if (refCol) {
|
||||||
|
const refData = rows.map(r => {
|
||||||
|
const rv = getRowVal(r, refCol)
|
||||||
|
return rv == null ? null : parseFloat(rv.toFixed(4))
|
||||||
|
})
|
||||||
|
// 上限 = REF + TOPLIMIT;下限 = REF + BOTLIMIT(TOPLIMIT/BOTLIMIT 单位与测厚仪一致)
|
||||||
|
const upData = rows.map((r, j) => {
|
||||||
|
const rv = getRowVal(r, refCol)
|
||||||
|
const tl = getRowVal(r, 'TOPLIMIT')
|
||||||
|
return rv == null ? null : parseFloat((rv + (tl ?? 3)).toFixed(4))
|
||||||
|
})
|
||||||
|
const loData = rows.map((r, j) => {
|
||||||
|
const rv = getRowVal(r, refCol)
|
||||||
|
const bl = getRowVal(r, 'BOTLIMIT')
|
||||||
|
return rv == null ? null : parseFloat((rv + (bl ?? -3)).toFixed(4))
|
||||||
|
})
|
||||||
|
if (refData.some(v => v != null)) extras.push({ name: '目标值', data: refData, color: '#909399', dash: false })
|
||||||
|
if (upData.some(v => v != null)) extras.push({ name: '上限', data: upData, color: '#F56C6C', dash: true })
|
||||||
|
if (loData.some(v => v != null)) extras.push({ name: '下限', data: loData, color: '#67C23A', dash: true })
|
||||||
|
}
|
||||||
|
return this.makeChart(ref, makeLine(title, xData, yData, extras))
|
||||||
})
|
})
|
||||||
this.chartInstances = charts.filter(Boolean)
|
this.chartInstances = charts.filter(Boolean)
|
||||||
this.setupResize()
|
this.setupResize()
|
||||||
@@ -739,6 +837,181 @@ export default {
|
|||||||
this.loadExcoilList()
|
this.loadExcoilList()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ── 自动同步到生效规程 ────────────────────────
|
||||||
|
async autoSyncToActiveSpec(coilId, rev) {
|
||||||
|
if (!this.presetData && !this.segData) return
|
||||||
|
const guard = () => rev !== undefined && this._clickRev !== rev
|
||||||
|
try {
|
||||||
|
// ① 查找生效版本
|
||||||
|
const verRes = await listProcessSpecVersion({ isActive: 1, pageNum: 1, pageSize: 10 })
|
||||||
|
if (guard()) return
|
||||||
|
const activeVer = (verRes.rows || []).find(v => v.isActive === 1)
|
||||||
|
if (!activeVer) return
|
||||||
|
const versionId = activeVer.versionId
|
||||||
|
|
||||||
|
// ② 构建本次写入条目
|
||||||
|
const items = this.buildSpecSyncItems()
|
||||||
|
if (!items.length) return
|
||||||
|
|
||||||
|
// ③ 加载已有 plan 点位
|
||||||
|
const plansRes = await listProcessPlan({ versionId, pageNum: 1, pageSize: 500 })
|
||||||
|
if (guard()) return
|
||||||
|
const planMap = {}
|
||||||
|
;(plansRes.rows || []).forEach(p => { planMap[p.pointCode] = p })
|
||||||
|
|
||||||
|
// ④ 逐条 upsert 点位 & 参数,收集异常
|
||||||
|
const anomalies = []
|
||||||
|
const detectedAt = new Date().toISOString()
|
||||||
|
// 提取本次钢卷的 enCoilId(入口卷号)
|
||||||
|
const enCoilId = this.selectedRow ? (this.selectedRow.ENCOILID || this.selectedRow.encoilid || null) : null
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if (guard()) return
|
||||||
|
|
||||||
|
// 确保 plan 点位存在
|
||||||
|
let planId
|
||||||
|
const ep = planMap[item.pointCode]
|
||||||
|
if (ep) {
|
||||||
|
planId = ep.planId
|
||||||
|
} else {
|
||||||
|
const r = await addProcessPlan({
|
||||||
|
versionId,
|
||||||
|
segmentType: 'PROCESS',
|
||||||
|
segmentName: item.groupLabel,
|
||||||
|
pointName: item.pointName,
|
||||||
|
pointCode: item.pointCode,
|
||||||
|
sortOrder: 0
|
||||||
|
})
|
||||||
|
if (guard()) return
|
||||||
|
planId = r.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查已存储参数
|
||||||
|
const prRes = await listProcessPlanParam({ planId, pageNum: 1, pageSize: 100 })
|
||||||
|
if (guard()) return
|
||||||
|
const stored = (prRes.rows || []).find(p => p.paramCode === item.paramCode)
|
||||||
|
|
||||||
|
// 异常检测:与已有上下限比对
|
||||||
|
if (stored) {
|
||||||
|
const sUp = stored.upperLimit != null ? Number(stored.upperLimit) : null
|
||||||
|
const sLo = stored.lowerLimit != null ? Number(stored.lowerLimit) : null
|
||||||
|
const aUp = item.upperLimit
|
||||||
|
const aLo = item.lowerLimit
|
||||||
|
const overTypes = []
|
||||||
|
if (sUp != null && aUp != null && aUp > sUp) overTypes.push('OVER_MAX')
|
||||||
|
if (sLo != null && aLo != null && aLo < sLo) overTypes.push('UNDER_MIN')
|
||||||
|
if (overTypes.length) {
|
||||||
|
anomalies.push({
|
||||||
|
versionId,
|
||||||
|
planId,
|
||||||
|
paramId: stored.paramId || null,
|
||||||
|
coilId,
|
||||||
|
enCoilId,
|
||||||
|
paramCode: item.paramCode,
|
||||||
|
paramName: item.paramName,
|
||||||
|
unit: item.unit,
|
||||||
|
anomalyType: overTypes.length === 2 ? 'BOTH' : overTypes[0],
|
||||||
|
storedTarget: stored.targetValue != null ? Number(stored.targetValue) : null,
|
||||||
|
storedUpper: sUp,
|
||||||
|
storedLower: sLo,
|
||||||
|
actualTarget: item.targetValue,
|
||||||
|
actualMax: aUp,
|
||||||
|
actualMin: aLo,
|
||||||
|
deviationMax: sUp != null && aUp != null ? parseFloat((aUp - sUp).toFixed(4)) : null,
|
||||||
|
deviationMin: sLo != null && aLo != null ? parseFloat((aLo - sLo).toFixed(4)) : null,
|
||||||
|
detectedAt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入/更新参数
|
||||||
|
// target_value 始终覆盖(反映最新L1设定)
|
||||||
|
// upper/lower 仅首次写入(null 时写入作为基线)
|
||||||
|
// actualSrcId / presetSrcId 仅首次写入
|
||||||
|
if (stored) {
|
||||||
|
await updateProcessPlanParam({
|
||||||
|
...stored,
|
||||||
|
targetValue: item.targetValue ?? stored.targetValue,
|
||||||
|
upperLimit: stored.upperLimit ?? item.upperLimit,
|
||||||
|
lowerLimit: stored.lowerLimit ?? item.lowerLimit,
|
||||||
|
unit: item.unit || stored.unit,
|
||||||
|
actualSrcId: stored.actualSrcId || enCoilId,
|
||||||
|
presetSrcId: stored.presetSrcId || coilId
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await addProcessPlanParam({
|
||||||
|
planId,
|
||||||
|
paramCode: item.paramCode,
|
||||||
|
paramName: item.paramName,
|
||||||
|
targetValue: item.targetValue,
|
||||||
|
upperLimit: item.upperLimit,
|
||||||
|
lowerLimit: item.lowerLimit,
|
||||||
|
unit: item.unit,
|
||||||
|
actualSrcId: enCoilId,
|
||||||
|
presetSrcId: coilId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guard()) return
|
||||||
|
|
||||||
|
// ⑤ 写入钢卷服役记录(幂等 upsert)
|
||||||
|
await upsertProcessCoilRecord({
|
||||||
|
versionId,
|
||||||
|
coilId,
|
||||||
|
enCoilId,
|
||||||
|
hasAnomaly: anomalies.length > 0 ? 1 : 0,
|
||||||
|
anomalyCnt: anomalies.length,
|
||||||
|
processTime: detectedAt
|
||||||
|
})
|
||||||
|
|
||||||
|
// ⑥ 持久化异常到数据库
|
||||||
|
if (anomalies.length) {
|
||||||
|
await batchAddProcessAnomaly(anomalies)
|
||||||
|
console.log(`[规程同步] 检测到 ${anomalies.length} 个参数异常,已写入数据库`)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[规程同步] 后台同步失败:', e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 从当前 segData + presetData 构建写入条目 */
|
||||||
|
buildSpecSyncItems() {
|
||||||
|
const items = []
|
||||||
|
for (const group of TREND_GROUPS) {
|
||||||
|
for (const item of group.children) {
|
||||||
|
const col = item.col
|
||||||
|
const maxArr = this.segData ? this.seg(col + 'MAX').filter(v => v != null) : []
|
||||||
|
const minArr = this.segData ? this.seg(col + 'MIN').filter(v => v != null) : []
|
||||||
|
const presetCol = TREND_PRESET_MAP[col]
|
||||||
|
|
||||||
|
let targetValue = null
|
||||||
|
if (presetCol && this.presetData) {
|
||||||
|
const sv = this.presetData[presetCol] !== undefined
|
||||||
|
? this.presetData[presetCol]
|
||||||
|
: this.presetData[presetCol.toLowerCase()]
|
||||||
|
if (sv != null && Number(sv) !== 0) targetValue = parseFloat(Number(sv).toFixed(4))
|
||||||
|
}
|
||||||
|
const upperLimit = maxArr.length ? parseFloat(Math.max(...maxArr).toFixed(4)) : null
|
||||||
|
const lowerLimit = minArr.length ? parseFloat(Math.min(...minArr).toFixed(4)) : null
|
||||||
|
|
||||||
|
if (targetValue == null && upperLimit == null && lowerLimit == null) continue
|
||||||
|
items.push({
|
||||||
|
groupLabel: group.label,
|
||||||
|
pointCode: col,
|
||||||
|
pointName: item.label,
|
||||||
|
paramCode: col,
|
||||||
|
paramName: item.label,
|
||||||
|
targetValue,
|
||||||
|
upperLimit,
|
||||||
|
lowerLimit,
|
||||||
|
unit: TREND_UNIT_MAP[col] || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
},
|
||||||
|
|
||||||
calcLengthPerTon(row) {
|
calcLengthPerTon(row) {
|
||||||
const len = parseFloat(row.EXIT_LENGTH || row.exit_length)
|
const len = parseFloat(row.EXIT_LENGTH || row.exit_length)
|
||||||
const wt = parseFloat(row.MEAS_EXIT_WEIGHT || row.meas_exit_weight)
|
const wt = parseFloat(row.MEAS_EXIT_WEIGHT || row.meas_exit_weight)
|
||||||
@@ -748,7 +1021,8 @@ export default {
|
|||||||
formatDate(v) {
|
formatDate(v) {
|
||||||
if (!v) return '—'
|
if (!v) return '—'
|
||||||
return String(v).replace('T', ' ').substring(0, 19)
|
return String(v).replace('T', ' ').substring(0, 19)
|
||||||
}
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -957,4 +1231,5 @@ export default {
|
|||||||
color: #c0c4cc;
|
color: #c0c4cc;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,75 +1,127 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="spec-version-page" v-loading="pageLoading">
|
<div class="spec-page" v-loading="pageLoading">
|
||||||
<!-- 头部 -->
|
<!-- 主体:左规程列表 + 右版本面板 -->
|
||||||
<div class="page-header">
|
<div class="master-detail">
|
||||||
<span class="page-title">规程版本管理</span>
|
<!-- 左:规程列表 -->
|
||||||
<el-button type="primary" size="small" icon="el-icon-plus" style="margin-left:auto"
|
<div class="master-panel">
|
||||||
@click="openSpecDialog()">新建规程</el-button>
|
<!-- 筛选区 -->
|
||||||
|
<div class="filter-area">
|
||||||
|
<div class="filter-row">
|
||||||
|
<span class="filter-label">规程类型</span>
|
||||||
|
<dict-select
|
||||||
|
class="filter-select"
|
||||||
|
renderType="radio"
|
||||||
|
v-model="queryParams.specType"
|
||||||
|
dict-type="wms_process_spec_type"
|
||||||
|
:kisv="false"
|
||||||
|
:editable="true"
|
||||||
|
@change="loadSpecs"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="filter-row">
|
||||||
<div>
|
<span class="filter-label">产线</span>
|
||||||
<el-form>
|
<dict-select
|
||||||
<el-form-item label="规程类型">
|
class="filter-select"
|
||||||
<dict-select @change="loadSpecs" renderType="radio" v-model="queryParams.specType"
|
renderType="radio"
|
||||||
dict-type="wms_process_spec_type" :kisv="false" :editable="true"></dict-select>
|
v-model="queryParams.lineId"
|
||||||
</el-form-item>
|
dict-type="wms_process_spec_line"
|
||||||
<el-form-item label="产线">
|
:kisv="false"
|
||||||
<dict-select @change="loadSpecs" renderType="radio" v-model="queryParams.lineId"
|
:editable="true"
|
||||||
dict-type="wms_process_spec_line" :kisv="false" :editable="true"></dict-select>
|
@change="loadSpecs"
|
||||||
</el-form-item>
|
/>
|
||||||
</el-form>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- 规程列表 -->
|
<!-- 列表头 -->
|
||||||
<div class="section-wrapper">
|
<div class="panel-hd">
|
||||||
<div class="section-title">规程列表</div>
|
<span class="panel-title">规程列表</span>
|
||||||
|
<span class="total-badge">{{ total }} 条</span>
|
||||||
<el-table :data="specList" size="small" highlight-current-row @row-click="onSpecRowClick"
|
<el-button type="primary" size="mini" icon="el-icon-plus" style="margin-left:auto" @click="openSpecDialog()">新建</el-button>
|
||||||
:row-class-name="tableRowClassName">
|
</div>
|
||||||
<el-table-column label="规程编码" prop="specCode" width="150" />
|
<el-table
|
||||||
<el-table-column label="规程名称" prop="specName" />
|
:data="specList"
|
||||||
<el-table-column label="创建时间" prop="createTime" width="180" />
|
size="small"
|
||||||
<el-table-column label="操作" align="right" width="180">
|
highlight-current-row
|
||||||
|
@row-click="onSpecRowClick"
|
||||||
|
:row-class-name="tableRowClassName"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<el-table-column label="编码" prop="specCode" width="110" show-overflow-tooltip />
|
||||||
|
<el-table-column label="名称" prop="specName" show-overflow-tooltip />
|
||||||
|
<el-table-column label="创建时间" prop="createTime" width="100" show-overflow-tooltip>
|
||||||
|
<template slot-scope="{ row }">{{ (row.createTime || '').substring(0, 10) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="" align="right" width="80">
|
||||||
<template slot-scope="{ row }">
|
<template slot-scope="{ row }">
|
||||||
<el-button type="text" size="mini" @click.stop="openSpecDialog(row)">编辑</el-button>
|
<el-button type="text" size="mini" @click.stop="openSpecDialog(row)">编辑</el-button>
|
||||||
<el-button type="text" size="mini" class="btn-danger" @click.stop="removeSpec(row)">删除</el-button>
|
<el-button type="text" size="mini" class="btn-danger" @click.stop="removeSpec(row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||||
@pagination="loadSpecs" />
|
@pagination="loadSpecs" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 版本列表 -->
|
<!-- 右:版本面板 -->
|
||||||
<div class="section-wrapper" v-if="currentSpec" v-loading="versionLoading">
|
<div class="detail-panel">
|
||||||
<div class="section-title">
|
<template v-if="currentSpec">
|
||||||
版本列表 - {{ currentSpec.specName }}
|
<div class="panel-hd" v-loading="versionLoading">
|
||||||
|
<div class="detail-title-row">
|
||||||
|
<span class="panel-title">{{ currentSpec.specName }}</span>
|
||||||
|
<span class="spec-code-tag">{{ currentSpec.specCode }}</span>
|
||||||
|
</div>
|
||||||
<el-button type="primary" size="mini" icon="el-icon-plus" @click="openVersionDialog()">新建版本</el-button>
|
<el-button type="primary" size="mini" icon="el-icon-plus" @click="openVersionDialog()">新建版本</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-table :data="versionList" size="small" highlight-current-row @row-click="onVersionRowClick">
|
|
||||||
<el-table-column label="版本号" prop="versionCode" />
|
<div v-if="!versionLoading && !versionList.length" class="empty-versions">
|
||||||
<el-table-column label="状态" prop="status" />
|
<i class="el-icon-document" style="font-size:32px;color:#dcdfe6;margin-bottom:8px;display:block" />
|
||||||
<el-table-column label="创建时间" prop="createTime" />
|
<span>暂无版本,点击「新建版本」开始</span>
|
||||||
<el-table-column label="生效" align="center">
|
</div>
|
||||||
<template slot-scope="{ row }">
|
|
||||||
<el-switch :value="row.isActive === 1" active-color="#5F7BA0" @click.native.stop
|
<div class="version-list" v-loading="versionLoading">
|
||||||
@change="handleActiveChange(row, $event)" />
|
<div
|
||||||
</template>
|
v-for="v in versionList"
|
||||||
</el-table-column>
|
:key="v.versionId"
|
||||||
<el-table-column label="操作" align="right">
|
class="version-card"
|
||||||
<template slot-scope="{ row }">
|
@click="goPlanSpec(v)"
|
||||||
<el-button type="text" size="mini" @click.stop="openVersionDialog(row)">编辑</el-button>
|
>
|
||||||
<el-button type="text" size="mini" class="btn-danger" @click.stop="removeVersion(row)">删除</el-button>
|
<div class="vc-left">
|
||||||
</template>
|
<div class="vc-top">
|
||||||
</el-table-column>
|
<span class="vc-code">{{ v.versionCode }}</span>
|
||||||
</el-table>
|
<el-tag :type="statusType(v.status)" size="mini" effect="plain" class="vc-status">
|
||||||
<el-empty v-if="!versionList.length && !versionLoading" description="暂无版本,请新建" style="padding:40px 0" />
|
{{ statusLabel(v.status) }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="v.isActive === 1" type="success" size="mini" effect="dark" class="vc-active">当前生效</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="vc-meta">
|
||||||
|
创建于 {{ (v.createTime || '').substring(0, 16) || '—' }}
|
||||||
|
<span v-if="v.updateTime && v.updateTime !== v.createTime" style="margin-left:8px">
|
||||||
|
· 更新于 {{ (v.updateTime || '').substring(0, 16) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="vc-right" @click.stop>
|
||||||
|
<el-switch
|
||||||
|
:value="v.isActive === 1"
|
||||||
|
active-color="#5F7BA0"
|
||||||
|
@change="handleActiveChange(v, $event)"
|
||||||
|
/>
|
||||||
|
<el-button type="text" size="mini" @click="openVersionDialog(v)">编辑</el-button>
|
||||||
|
<el-button type="text" size="mini" class="btn-danger" @click="removeVersion(v)">删除</el-button>
|
||||||
|
<el-button type="text" size="mini" class="btn-view" @click="goPlanSpec(v)">方案 →</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-else class="detail-empty">
|
||||||
|
<i class="el-icon-d-arrow-left" style="font-size:24px;color:#c0c4cc;margin-bottom:12px;display:block" />
|
||||||
|
<span>请在左侧选择一个规程查看其版本</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="empty-hint">请选择一个规程查看其版本</div>
|
|
||||||
|
|
||||||
<!-- 新建/编辑规程 -->
|
<!-- 新建/编辑规程 -->
|
||||||
<el-dialog :title="specTitle" :visible.sync="specOpen" width="500px" append-to-body @close="specForm = {}">
|
<el-dialog :title="specTitle" :visible.sync="specOpen" width="460px" append-to-body @close="specForm = {}">
|
||||||
<el-form ref="specFormRef" :model="specForm" :rules="specRules" label-width="88px" size="small">
|
<el-form ref="specFormRef" :model="specForm" :rules="specRules" label-width="88px" size="small">
|
||||||
<el-form-item label="规程编码" prop="specCode">
|
<el-form-item label="规程编码" prop="specCode">
|
||||||
<el-input v-model="specForm.specCode" placeholder="请输入规程编码" maxlength="64" />
|
<el-input v-model="specForm.specCode" placeholder="请输入规程编码" maxlength="64" />
|
||||||
@@ -78,7 +130,7 @@
|
|||||||
<el-input v-model="specForm.specName" placeholder="请输入规程名称" maxlength="200" />
|
<el-input v-model="specForm.specName" placeholder="请输入规程名称" maxlength="200" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注">
|
<el-form-item label="备注">
|
||||||
<el-input v-model="specForm.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
|
<el-input v-model="specForm.remark" type="textarea" :rows="2" maxlength="500" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
@@ -88,21 +140,21 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 新建/编辑版本 -->
|
<!-- 新建/编辑版本 -->
|
||||||
<el-dialog :title="versionTitle" :visible.sync="versionOpen" width="500px" append-to-body @close="versionForm = {}">
|
<el-dialog :title="versionTitle" :visible.sync="versionOpen" width="460px" append-to-body @close="versionForm = {}">
|
||||||
<el-form ref="versionFormRef" :model="versionForm" :rules="versionRules" label-width="88px" size="small">
|
<el-form ref="versionFormRef" :model="versionForm" :rules="versionRules" label-width="88px" size="small">
|
||||||
<el-form-item label="版本号" prop="versionCode">
|
<el-form-item label="版本号" prop="versionCode">
|
||||||
<el-input v-model="versionForm.versionCode" placeholder="如 V1.0" maxlength="64" />
|
<el-input v-model="versionForm.versionCode" placeholder="如 V1.0" maxlength="64" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-select v-model="versionForm.status" style="width:100%">
|
<el-select v-model="versionForm.status" style="width:100%">
|
||||||
<el-option v-for="s in statusOptions" :key="s" :label="s" :value="s" />
|
<el-option v-for="s in STATUS_OPTIONS" :key="s.value" :label="s.label" :value="s.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="保存后生效">
|
<el-form-item label="设为生效">
|
||||||
<el-switch v-model="versionForm.isActive" :active-value="1" :inactive-value="0" />
|
<el-switch v-model="versionForm.isActive" :active-value="1" :inactive-value="0" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注">
|
<el-form-item label="备注">
|
||||||
<el-input v-model="versionForm.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
|
<el-input v-model="versionForm.remark" type="textarea" :rows="2" maxlength="500" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
@@ -114,7 +166,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listProcessSpec, getProcessSpec, addProcessSpec, updateProcessSpec, delProcessSpec } from '@/api/wms/processSpec'
|
import { listProcessSpec, addProcessSpec, updateProcessSpec, delProcessSpec } from '@/api/wms/processSpec'
|
||||||
import {
|
import {
|
||||||
listProcessSpecVersion,
|
listProcessSpecVersion,
|
||||||
addProcessSpecVersion,
|
addProcessSpecVersion,
|
||||||
@@ -123,28 +175,26 @@ import {
|
|||||||
activateProcessSpecVersion
|
activateProcessSpecVersion
|
||||||
} from '@/api/wms/processSpecVersion'
|
} from '@/api/wms/processSpecVersion'
|
||||||
|
|
||||||
|
const STATUS_OPTIONS = [
|
||||||
|
{ value: 'DRAFT', label: '草稿' },
|
||||||
|
{ value: 'PUBLISHED', label: '已发布' },
|
||||||
|
{ value: 'OBSOLETE', label: '已作废' }
|
||||||
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SpecVersionManage',
|
name: 'SpecVersionManage',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
STATUS_OPTIONS,
|
||||||
pageLoading: false,
|
pageLoading: false,
|
||||||
specList: [],
|
specList: [],
|
||||||
currentSpec: null,
|
currentSpec: null,
|
||||||
currentSpecId: null,
|
currentSpecId: null,
|
||||||
versionList: [],
|
versionList: [],
|
||||||
versionLoading: false,
|
versionLoading: false,
|
||||||
statusOptions: ['DRAFT', 'PUBLISHED', 'OBSOLETE'],
|
|
||||||
|
|
||||||
// 分页相关
|
|
||||||
total: 0,
|
total: 0,
|
||||||
queryParams: {
|
queryParams: { pageNum: 1, pageSize: 10, specType: '', lineId: '' },
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
specType: '',
|
|
||||||
lineId: ''
|
|
||||||
},
|
|
||||||
|
|
||||||
// 规程相关
|
|
||||||
specOpen: false,
|
specOpen: false,
|
||||||
specTitle: '',
|
specTitle: '',
|
||||||
specSubmitLoading: false,
|
specSubmitLoading: false,
|
||||||
@@ -154,7 +204,6 @@ export default {
|
|||||||
specName: [{ required: true, message: '规程名称不能为空', trigger: 'blur' }]
|
specName: [{ required: true, message: '规程名称不能为空', trigger: 'blur' }]
|
||||||
},
|
},
|
||||||
|
|
||||||
// 版本相关
|
|
||||||
versionOpen: false,
|
versionOpen: false,
|
||||||
versionTitle: '',
|
versionTitle: '',
|
||||||
versionSubmitLoading: false,
|
versionSubmitLoading: false,
|
||||||
@@ -169,12 +218,17 @@ export default {
|
|||||||
this.loadSpecs()
|
this.loadSpecs()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 表格行样式
|
|
||||||
tableRowClassName({ row }) {
|
tableRowClassName({ row }) {
|
||||||
return row.specId === this.currentSpecId ? 'current-row' : ''
|
return row.specId === this.currentSpecId ? 'current-row' : ''
|
||||||
},
|
},
|
||||||
|
statusType(status) {
|
||||||
|
return { DRAFT: '', PUBLISHED: 'success', OBSOLETE: 'info' }[status] || ''
|
||||||
|
},
|
||||||
|
statusLabel(status) {
|
||||||
|
const hit = STATUS_OPTIONS.find(s => s.value === status)
|
||||||
|
return hit ? hit.label : (status || '—')
|
||||||
|
},
|
||||||
|
|
||||||
// 加载规程列表
|
|
||||||
loadSpecs() {
|
loadSpecs() {
|
||||||
this.pageLoading = true
|
this.pageLoading = true
|
||||||
listProcessSpec(this.queryParams).then(res => {
|
listProcessSpec(this.queryParams).then(res => {
|
||||||
@@ -182,7 +236,7 @@ export default {
|
|||||||
this.specList = res.rows || []
|
this.specList = res.rows || []
|
||||||
if (this.specList.length > 0 && !this.currentSpec) {
|
if (this.specList.length > 0 && !this.currentSpec) {
|
||||||
this.selectSpec(this.specList[0])
|
this.selectSpec(this.specList[0])
|
||||||
} else {
|
} else if (!this.specList.length) {
|
||||||
this.currentSpec = null
|
this.currentSpec = null
|
||||||
this.currentSpecId = null
|
this.currentSpecId = null
|
||||||
this.versionList = []
|
this.versionList = []
|
||||||
@@ -190,19 +244,13 @@ export default {
|
|||||||
}).catch(e => console.error(e)).finally(() => { this.pageLoading = false })
|
}).catch(e => console.error(e)).finally(() => { this.pageLoading = false })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 选择规程
|
|
||||||
selectSpec(spec) {
|
selectSpec(spec) {
|
||||||
this.currentSpec = spec
|
this.currentSpec = spec
|
||||||
this.currentSpecId = spec.specId
|
this.currentSpecId = spec.specId
|
||||||
this.loadVersions()
|
this.loadVersions()
|
||||||
},
|
},
|
||||||
|
onSpecRowClick(row) { this.selectSpec(row) },
|
||||||
|
|
||||||
// 点击规程行
|
|
||||||
onSpecRowClick(row) {
|
|
||||||
this.selectSpec(row)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 加载版本列表
|
|
||||||
loadVersions() {
|
loadVersions() {
|
||||||
if (!this.currentSpecId) return
|
if (!this.currentSpecId) return
|
||||||
this.versionLoading = true
|
this.versionLoading = true
|
||||||
@@ -211,27 +259,15 @@ export default {
|
|||||||
}).catch(e => console.error(e)).finally(() => { this.versionLoading = false })
|
}).catch(e => console.error(e)).finally(() => { this.versionLoading = false })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 点击版本行
|
|
||||||
onVersionRowClick(row) {
|
|
||||||
this.goPlanSpec(row)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 跳转到方案详情
|
|
||||||
goPlanSpec(row) {
|
goPlanSpec(row) {
|
||||||
const basePath = this.$route.path.replace(/\/[^/]*$/, '')
|
|
||||||
console.log(basePath)
|
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: `/process/processSpec/planSpec`,
|
path: `/process/processSpec/planSpec`,
|
||||||
query: { specId: this.currentSpecId, versionId: String(row.versionId), versionCode: row.versionCode }
|
query: { specId: this.currentSpecId, versionId: String(row.versionId), versionCode: row.versionCode }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 生效切换
|
|
||||||
handleActiveChange(row, val) {
|
handleActiveChange(row, val) {
|
||||||
if (!val) {
|
if (!val) { this.$message.info('请激活其他版本来替换当前生效版本'); return }
|
||||||
this.$message.info('请激活其他版本来替换当前生效版本')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$modal.confirm('确认将版本"' + row.versionCode + '"设为当前生效版本?').then(() => {
|
this.$modal.confirm('确认将版本"' + row.versionCode + '"设为当前生效版本?').then(() => {
|
||||||
return activateProcessSpecVersion(row.versionId)
|
return activateProcessSpecVersion(row.versionId)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -240,17 +276,12 @@ export default {
|
|||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 规程对话框
|
|
||||||
openSpecDialog(row) {
|
openSpecDialog(row) {
|
||||||
this.specForm = row
|
this.specForm = row ? { ...row } : { specCode: undefined, specName: undefined, remark: undefined }
|
||||||
? { ...row }
|
|
||||||
: { specCode: undefined, specName: undefined, remark: undefined }
|
|
||||||
this.specTitle = row ? '编辑规程' : '新建规程'
|
this.specTitle = row ? '编辑规程' : '新建规程'
|
||||||
this.specOpen = true
|
this.specOpen = true
|
||||||
this.$nextTick(() => this.$refs.specFormRef && this.$refs.specFormRef.clearValidate())
|
this.$nextTick(() => this.$refs.specFormRef && this.$refs.specFormRef.clearValidate())
|
||||||
},
|
},
|
||||||
|
|
||||||
// 提交规程
|
|
||||||
submitSpec() {
|
submitSpec() {
|
||||||
this.$refs.specFormRef.validate(ok => {
|
this.$refs.specFormRef.validate(ok => {
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
@@ -263,23 +294,18 @@ export default {
|
|||||||
}).catch(e => console.error(e)).finally(() => { this.specSubmitLoading = false })
|
}).catch(e => console.error(e)).finally(() => { this.specSubmitLoading = false })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除规程
|
|
||||||
removeSpec(row) {
|
removeSpec(row) {
|
||||||
this.$modal.confirm('确认删除规程"' + row.specName + '"?').then(() => {
|
this.$modal.confirm('确认删除规程"' + row.specName + '"?').then(() => {
|
||||||
return delProcessSpec(row.specId)
|
return delProcessSpec(row.specId)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$modal.msgSuccess('删除成功')
|
this.$modal.msgSuccess('删除成功')
|
||||||
if (this.currentSpecId === row.specId) {
|
if (this.currentSpecId === row.specId) {
|
||||||
this.currentSpec = null
|
this.currentSpec = null; this.currentSpecId = null; this.versionList = []
|
||||||
this.currentSpecId = null
|
|
||||||
this.versionList = []
|
|
||||||
}
|
}
|
||||||
this.loadSpecs()
|
this.loadSpecs()
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 版本对话框
|
|
||||||
openVersionDialog(row) {
|
openVersionDialog(row) {
|
||||||
this.versionForm = row
|
this.versionForm = row
|
||||||
? { ...row }
|
? { ...row }
|
||||||
@@ -288,8 +314,6 @@ export default {
|
|||||||
this.versionOpen = true
|
this.versionOpen = true
|
||||||
this.$nextTick(() => this.$refs.versionFormRef && this.$refs.versionFormRef.clearValidate())
|
this.$nextTick(() => this.$refs.versionFormRef && this.$refs.versionFormRef.clearValidate())
|
||||||
},
|
},
|
||||||
|
|
||||||
// 提交版本
|
|
||||||
submitVersion() {
|
submitVersion() {
|
||||||
this.$refs.versionFormRef.validate(ok => {
|
this.$refs.versionFormRef.validate(ok => {
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
@@ -304,8 +328,6 @@ export default {
|
|||||||
}).catch(e => console.error(e)).finally(() => { this.versionSubmitLoading = false })
|
}).catch(e => console.error(e)).finally(() => { this.versionSubmitLoading = false })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除版本
|
|
||||||
removeVersion(row) {
|
removeVersion(row) {
|
||||||
this.$modal.confirm('确认删除版本"' + row.versionCode + '"?').then(() => {
|
this.$modal.confirm('确认删除版本"' + row.versionCode + '"?').then(() => {
|
||||||
return delProcessSpecVersion(row.versionId)
|
return delProcessSpecVersion(row.versionId)
|
||||||
@@ -319,101 +341,160 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.spec-version-page {
|
.spec-page {
|
||||||
padding: 16px 20px;
|
padding: 12px 16px;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
/* ── 主体布局 ── */
|
||||||
|
.master-detail {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 左:规程列表 ── */
|
||||||
|
.master-panel {
|
||||||
|
width: 380px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 筛选区 */
|
||||||
|
.filter-area {
|
||||||
|
padding: 10px 12px 8px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.filter-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
margin-bottom: 16px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
}
|
}
|
||||||
|
.filter-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 52px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.filter-select { flex: 1; }
|
||||||
|
|
||||||
.page-title {
|
.panel-hd {
|
||||||
font-size: 15px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.panel-title {
|
||||||
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
.total-badge {
|
||||||
.section-wrapper {
|
font-size: 12px;
|
||||||
background: #fff;
|
color: #909399;
|
||||||
border-radius: 4px;
|
margin-left: 6px;
|
||||||
padding: 12px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #ebeef5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
/* ── 右:版本面板 ── */
|
||||||
|
.detail-panel {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.detail-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.spec-code-tag {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
background: #f0f2f5;
|
||||||
|
padding: 1px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.detail-empty, .empty-versions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 0;
|
||||||
|
color: #909399;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
}
|
||||||
color: #606266;
|
|
||||||
margin-bottom: 12px;
|
/* ── 版本卡片 ── */
|
||||||
|
.version-list {
|
||||||
|
padding: 10px 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.version-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color .15s, box-shadow .15s;
|
||||||
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
.version-card:hover {
|
||||||
.empty-hint {
|
border-color: #5F7BA0;
|
||||||
text-align: center;
|
box-shadow: 0 2px 8px rgba(95,123,160,.12);
|
||||||
padding: 60px 0;
|
}
|
||||||
color: #909399;
|
.vc-left { display: flex; flex-direction: column; gap: 4px; }
|
||||||
|
.vc-top { display: flex; align-items: center; gap: 6px; }
|
||||||
|
.vc-code {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
.vc-status, .vc-active { border-radius: 10px !important; }
|
||||||
.el-table {
|
.vc-meta { font-size: 11px; color: #c0c4cc; }
|
||||||
border-radius: 0;
|
.vc-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
.btn-view { color: #5F7BA0 !important; }
|
||||||
|
|
||||||
::v-deep .el-table .current-row {
|
/* ── 表格行高亮 ── */
|
||||||
background: #f0f7ff !important;
|
::v-deep .el-table .current-row { background: #f0f7ff !important; }
|
||||||
}
|
::v-deep .el-table .current-row td { background: #f0f7ff !important; }
|
||||||
|
|
||||||
|
/* ── 按钮主色覆盖 ── */
|
||||||
::v-deep .el-button--primary {
|
::v-deep .el-button--primary {
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
background: #5F7BA0 !important;
|
background: #5F7BA0 !important;
|
||||||
border-color: #5F7BA0 !important;
|
border-color: #5F7BA0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-button--primary:hover,
|
::v-deep .el-button--primary:hover,
|
||||||
::v-deep .el-button--primary:focus {
|
::v-deep .el-button--primary:focus {
|
||||||
background: #4d6a8e !important;
|
background: #4d6a8e !important;
|
||||||
border-color: #4d6a8e !important;
|
border-color: #4d6a8e !important;
|
||||||
}
|
}
|
||||||
|
::v-deep .el-button--primary.is-disabled { opacity: .5; }
|
||||||
::v-deep .el-button--primary:active {
|
::v-deep .el-button--text { background: transparent !important; border-color: transparent !important; }
|
||||||
background: #4a6585 !important;
|
::v-deep .el-button--text.btn-danger { color: #f56c6c !important; }
|
||||||
border-color: #4a6585 !important;
|
.btn-danger { color: #f56c6c; }
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-button--primary.is-disabled {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger) {
|
|
||||||
color: #606266 !important;
|
|
||||||
background: #fff !important;
|
|
||||||
border-color: #dcdfe6 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger):hover {
|
|
||||||
color: #5F7BA0 !important;
|
|
||||||
border-color: #5F7BA0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-button--text {
|
|
||||||
background: transparent !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-button--text.btn-danger {
|
|
||||||
color: #f56c6c !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger {
|
|
||||||
color: #f56c6c;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,22 +2,29 @@
|
|||||||
<div class="plan-spec-page" v-loading="pageLoading">
|
<div class="plan-spec-page" v-loading="pageLoading">
|
||||||
<!-- 头部 -->
|
<!-- 头部 -->
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<el-button type="text" icon="el-icon-arrow-left" @click="goBack">返回</el-button>
|
<el-button type="text" icon="el-icon-arrow-left" class="back-btn" @click="goBack">返回</el-button>
|
||||||
|
<div class="header-divider" />
|
||||||
<span class="page-title">方案详情</span>
|
<span class="page-title">方案详情</span>
|
||||||
<span v-if="versionCode" class="version-badge">版本 {{ versionCode }}</span>
|
<span v-if="versionCode" class="version-badge">
|
||||||
</div>
|
<i class="el-icon-price-tag" style="margin-right:3px" />版本 {{ versionCode }}
|
||||||
|
</span>
|
||||||
|
<div style="flex:1" />
|
||||||
<!-- 可配置 / 不可配置 切换 -->
|
<!-- 可配置 / 不可配置 切换 -->
|
||||||
<div class="config-tabs">
|
<div class="config-tabs">
|
||||||
<span
|
<span :class="['config-tab', { active: configMode === 'configurable' }]" @click="configMode = 'configurable'">可配置</span>
|
||||||
:class="['config-tab', { active: configMode === 'configurable' }]"
|
<span :class="['config-tab', { active: configMode === 'readonly' }]" @click="configMode = 'readonly'">不可配置</span>
|
||||||
@click="configMode = 'configurable'"
|
|
||||||
>可配置</span>
|
|
||||||
<span
|
|
||||||
:class="['config-tab', { active: configMode === 'readonly' }]"
|
|
||||||
@click="configMode = 'readonly'"
|
|
||||||
>不可配置</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 全局异常提示条 -->
|
||||||
|
<el-alert
|
||||||
|
v-if="allAnomalies.length"
|
||||||
|
:title="`检测到 ${allAnomalies.length} 项实际生产偏差(来自最近一次实绩分析),请选择对应点位查看详情`"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
style="margin-bottom:10px"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="main-layout">
|
<div class="main-layout">
|
||||||
<!-- 左侧段分组 -->
|
<!-- 左侧段分组 -->
|
||||||
@@ -96,11 +103,7 @@
|
|||||||
<template slot-scope="{ row }">{{ segLabel(row.segmentType) }} › {{ row.segmentName || '—' }}</template>
|
<template slot-scope="{ row }">{{ segLabel(row.segmentType) }} › {{ row.segmentName || '—' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="点位名称" prop="pointName" show-overflow-tooltip />
|
<el-table-column label="点位名称" prop="pointName" show-overflow-tooltip />
|
||||||
<el-table-column label="实际值ID" prop="actualValueId" show-overflow-tooltip />
|
<el-table-column label="点位编码" prop="pointCode" show-overflow-tooltip />
|
||||||
<el-table-column label="L1设定值ID" prop="l1SetValueId" show-overflow-tooltip />
|
|
||||||
<el-table-column label="设定值" prop="targetValue" align="center" />
|
|
||||||
<el-table-column label="下限" prop="lowerLimit" align="center" />
|
|
||||||
<el-table-column label="上限" prop="upperLimit" align="center" />
|
|
||||||
<el-table-column label="操作" align="right">
|
<el-table-column label="操作" align="right">
|
||||||
<template slot-scope="{ row }">
|
<template slot-scope="{ row }">
|
||||||
<el-button type="text" size="mini" @click.stop="openPlanDialog(row)">编辑</el-button>
|
<el-button type="text" size="mini" @click.stop="openPlanDialog(row)">编辑</el-button>
|
||||||
@@ -117,12 +120,32 @@
|
|||||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openParamDialog()">新建参数</el-button>
|
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openParamDialog()">新建参数</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-table v-loading="paramLoading" :data="paramList" size="small">
|
<el-table v-loading="paramLoading" :data="paramList" size="small">
|
||||||
<el-table-column label="参数编码" prop="paramCode" show-overflow-tooltip />
|
<el-table-column label="参数编码" prop="paramCode" width="110" show-overflow-tooltip />
|
||||||
<el-table-column label="参数名称" prop="paramName" show-overflow-tooltip />
|
<el-table-column label="参数名称" prop="paramName" show-overflow-tooltip />
|
||||||
<el-table-column label="设定值" prop="targetValue" align="center" />
|
<el-table-column label="实际值ID" prop="actualSrcId" width="140" show-overflow-tooltip>
|
||||||
<el-table-column label="下限" prop="lowerLimit" align="center" />
|
<template slot-scope="{ row }">{{ row.actualSrcId || '—' }}</template>
|
||||||
<el-table-column label="上限" prop="upperLimit" align="center" />
|
</el-table-column>
|
||||||
<el-table-column label="单位" prop="unit" align="center" />
|
<el-table-column label="L1设定值ID" prop="presetSrcId" width="140" show-overflow-tooltip>
|
||||||
|
<template slot-scope="{ row }">{{ row.presetSrcId || '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="设定值" prop="targetValue" align="right" width="80" />
|
||||||
|
<el-table-column label="最小值" prop="lowerLimit" align="right" width="80" />
|
||||||
|
<el-table-column label="最大值" prop="upperLimit" align="right" width="80" />
|
||||||
|
<el-table-column label="单位" prop="unit" align="center" width="60" />
|
||||||
|
<el-table-column label="更新时间" align="center" width="136">
|
||||||
|
<template slot-scope="{ row }">{{ (row.updateTime || row.createTime || '').substring(0, 16) || '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="实际状态" align="center" width="90">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="paramAnomalyMap[row.paramCode]" class="anomaly-badge">
|
||||||
|
<i class="el-icon-warning-outline" /> 异常
|
||||||
|
</span>
|
||||||
|
<span v-else-if="row.upperLimit != null || row.lowerLimit != null" class="normal-badge">
|
||||||
|
<i class="el-icon-circle-check" /> 正常
|
||||||
|
</span>
|
||||||
|
<span v-else class="no-data-badge">—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="right">
|
<el-table-column label="操作" align="right">
|
||||||
<template slot-scope="{ row }">
|
<template slot-scope="{ row }">
|
||||||
<el-button type="text" size="mini" @click="openParamDialog(null, row)">编辑</el-button>
|
<el-button type="text" size="mini" @click="openParamDialog(null, row)">编辑</el-button>
|
||||||
@@ -130,10 +153,166 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 偏差分析区块 -->
|
||||||
|
<template v-if="planAnomalies.length">
|
||||||
|
<div class="anomaly-section-header">
|
||||||
|
<i class="el-icon-warning" style="color:#E6A23C;margin-right:4px" />
|
||||||
|
实际生产偏差分析
|
||||||
|
<el-tag type="warning" size="mini" effect="plain" style="margin-left:8px">{{ planAnomalies.length }} 项异常</el-tag>
|
||||||
|
<el-button type="text" size="mini" style="margin-left:auto" @click="anomalyExpanded = !anomalyExpanded">
|
||||||
|
{{ anomalyExpanded ? '收起' : '展开' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-show="anomalyExpanded">
|
||||||
|
<el-table :data="planAnomalies" size="small" border>
|
||||||
|
<el-table-column label="参数" prop="paramName" width="110" show-overflow-tooltip />
|
||||||
|
<el-table-column label="规程设定值" align="right" width="96">
|
||||||
|
<template slot-scope="{ row }">{{ row.storedTarget != null ? row.storedTarget : '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="规程最大值" align="right" width="96">
|
||||||
|
<template slot-scope="{ row }">{{ row.storedUpper != null ? row.storedUpper : '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="规程最小值" align="right" width="96">
|
||||||
|
<template slot-scope="{ row }">{{ row.storedLower != null ? row.storedLower : '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="实际最大值" align="right" width="96">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span :class="(row.anomalyType === 'OVER_MAX' || row.anomalyType === 'BOTH') ? 'val-over' : ''">
|
||||||
|
{{ row.actualMax != null ? row.actualMax : '—' }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="实际最小值" align="right" width="96">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span :class="(row.anomalyType === 'UNDER_MIN' || row.anomalyType === 'BOTH') ? 'val-under' : ''">
|
||||||
|
{{ row.actualMin != null ? row.actualMin : '—' }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最大偏差" align="right" width="88">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="row.deviationMax != null" class="val-over">+{{ row.deviationMax }}</span>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最小偏差" align="right" width="88">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="row.deviationMin != null" class="val-under">{{ row.deviationMin }}</span>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="来源钢卷" prop="coilId" show-overflow-tooltip width="120" />
|
||||||
|
<el-table-column label="检测时间" prop="detectedAt" width="140" show-overflow-tooltip>
|
||||||
|
<template slot-scope="{ row }">{{ (row.detectedAt || '').substring(0, 16) || '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="异常类型" align="center" width="120">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-tag v-if="row.anomalyType === 'OVER_MAX' || row.anomalyType === 'BOTH'" size="mini" type="danger" style="margin:1px">超上限</el-tag>
|
||||||
|
<el-tag v-if="row.anomalyType === 'UNDER_MIN' || row.anomalyType === 'BOTH'" size="mini" type="warning" style="margin:1px">低于下限</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 对比图 -->
|
||||||
|
<div ref="anomalyChart" class="anomaly-chart" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 服役钢卷记录 -->
|
||||||
|
<div class="coil-record-section" v-loading="coilRecordLoading">
|
||||||
|
<div class="coil-record-hd">
|
||||||
|
<i class="el-icon-data-line" style="margin-right:5px;color:#5F7BA0" />
|
||||||
|
服役钢卷记录
|
||||||
|
<span class="coil-total-badge">共 {{ coilRecordTotal }} 根</span>
|
||||||
|
<span v-if="coilRecords.filter(r => r.hasAnomaly).length" class="coil-anomaly-badge">
|
||||||
|
其中 {{ coilRecords.filter(r => r.hasAnomaly).length }} 根有异常
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="coilRecords" size="small" border>
|
||||||
|
<el-table-column label="出口钢卷号" prop="coilId" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column label="入口钢卷号" prop="enCoilId" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column label="检测时间" min-width="140">
|
||||||
|
<template slot-scope="{ row }">{{ (row.processTime || '').substring(0, 16) || '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="参数异常情况" align="center" min-width="160">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<template v-if="row.hasAnomaly">
|
||||||
|
<el-tag type="danger" size="mini" effect="plain" style="margin-right:4px">
|
||||||
|
<i class="el-icon-warning-outline" /> {{ row.anomalyCnt }} 项参数超限
|
||||||
|
</el-tag>
|
||||||
|
<el-button type="text" size="mini" style="color:#F56C6C;padding:0"
|
||||||
|
@click="jumpToAnomaly(row.coilId)">查看</el-button>
|
||||||
|
</template>
|
||||||
|
<span v-else class="normal-badge"><i class="el-icon-circle-check" /> 全部正常</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" align="center" min-width="80">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-tag :type="row.hasAnomaly ? 'danger' : 'success'" size="mini" effect="dark">
|
||||||
|
{{ row.hasAnomaly ? '异常' : '正常' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination
|
||||||
|
v-show="coilRecordTotal > 0"
|
||||||
|
:total="coilRecordTotal"
|
||||||
|
:page.sync="coilRecordPage"
|
||||||
|
:limit.sync="coilRecordPageSize"
|
||||||
|
@pagination="loadCoilRecords"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 钢卷异常明细 dialog -->
|
||||||
|
<el-dialog
|
||||||
|
:title="`钢卷 ${coilAnomalyCoilId} — 异常明细`"
|
||||||
|
:visible.sync="coilAnomalyDialog"
|
||||||
|
width="900px"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<el-table :data="coilAnomalyList" size="small" border>
|
||||||
|
<el-table-column label="参数名称" prop="paramName" min-width="110" show-overflow-tooltip />
|
||||||
|
<el-table-column label="异常类型" align="center" width="120">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-tag v-if="row.anomalyType === 'OVER_MAX' || row.anomalyType === 'BOTH'" size="mini" type="danger" style="margin:1px">超上限</el-tag>
|
||||||
|
<el-tag v-if="row.anomalyType === 'UNDER_MIN' || row.anomalyType === 'BOTH'" size="mini" type="warning" style="margin:1px">低于下限</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="规程设定值" prop="storedTarget" align="right" width="100" />
|
||||||
|
<el-table-column label="规程上限" prop="storedUpper" align="right" width="90" />
|
||||||
|
<el-table-column label="规程下限" prop="storedLower" align="right" width="90" />
|
||||||
|
<el-table-column label="实际最大值" align="right" width="100">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span :class="(row.anomalyType === 'OVER_MAX' || row.anomalyType === 'BOTH') ? 'val-over' : ''">{{ row.actualMax }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="实际最小值" align="right" width="100">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span :class="(row.anomalyType === 'UNDER_MIN' || row.anomalyType === 'BOTH') ? 'val-under' : ''">{{ row.actualMin }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最大偏差" align="right" width="90">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="row.deviationMax != null" class="val-over">+{{ row.deviationMax }}</span>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最小偏差" align="right" width="90">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="row.deviationMin != null" class="val-under">{{ row.deviationMin }}</span>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="单位" prop="unit" align="center" width="60" />
|
||||||
|
</el-table>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button size="small" @click="coilAnomalyDialog = false">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 方案点位 dialog -->
|
<!-- 方案点位 dialog -->
|
||||||
<el-dialog :title="planTitle" :visible.sync="planOpen" width="520px" append-to-body @close="planForm = {}">
|
<el-dialog :title="planTitle" :visible.sync="planOpen" width="520px" append-to-body @close="planForm = {}">
|
||||||
<el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="90px" size="small">
|
<el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="90px" size="small">
|
||||||
@@ -151,11 +330,11 @@
|
|||||||
<el-form-item label="点位编码" prop="pointCode">
|
<el-form-item label="点位编码" prop="pointCode">
|
||||||
<el-input v-model="planForm.pointCode" maxlength="64" />
|
<el-input v-model="planForm.pointCode" maxlength="64" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="实际值ID" prop="actualValueId">
|
<el-form-item label="实际值ID" prop="actualSrcId">
|
||||||
<el-input v-model="planForm.actualValueId" maxlength="64" />
|
<el-input v-model="planForm.actualSrcId" maxlength="64" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="L1设定值ID" prop="l1SetValueId">
|
<el-form-item label="L1设定值ID" prop="presetSrcId">
|
||||||
<el-input v-model="planForm.l1SetValueId" maxlength="64" />
|
<el-input v-model="planForm.presetSrcId" maxlength="64" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="设定值" prop="targetValue">
|
<el-form-item label="设定值" prop="targetValue">
|
||||||
<el-input v-model="planForm.targetValue" />
|
<el-input v-model="planForm.targetValue" />
|
||||||
@@ -289,9 +468,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
import { listProcessPlan, addProcessPlan, updateProcessPlan, delProcessPlan } from '@/api/wms/processPlan'
|
import { listProcessPlan, addProcessPlan, updateProcessPlan, delProcessPlan } from '@/api/wms/processPlan'
|
||||||
import { listProcessPlanParam, addProcessPlanParam, updateProcessPlanParam, delProcessPlanParam } from '@/api/wms/processPlanParam'
|
import { listProcessPlanParam, addProcessPlanParam, updateProcessPlanParam, delProcessPlanParam } from '@/api/wms/processPlanParam'
|
||||||
|
import { listAllProcessAnomaly } from '@/api/wms/processAnomaly'
|
||||||
|
import { listProcessCoilRecord } from '@/api/wms/processCoilRecord'
|
||||||
|
|
||||||
/** 表单内可选段类型(新建/编辑仍支持全部枚举) */
|
/** 表单内可选段类型(新建/编辑仍支持全部枚举) */
|
||||||
const SEGMENT_FORM_OPTIONS = [
|
const SEGMENT_FORM_OPTIONS = [
|
||||||
@@ -352,6 +534,19 @@ export default {
|
|||||||
paramCode: [{ required: true, message: '参数编码不能为空', trigger: 'blur' }],
|
paramCode: [{ required: true, message: '参数编码不能为空', trigger: 'blur' }],
|
||||||
paramName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }]
|
paramName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }]
|
||||||
},
|
},
|
||||||
|
// 偏差分析
|
||||||
|
allAnomalies: [],
|
||||||
|
anomalyExpanded: true,
|
||||||
|
anomalyChartInst: null,
|
||||||
|
// 服役钢卷记录
|
||||||
|
coilRecords: [],
|
||||||
|
coilRecordTotal: 0,
|
||||||
|
coilRecordPage: 1,
|
||||||
|
coilRecordPageSize: 20,
|
||||||
|
coilRecordLoading: false,
|
||||||
|
// 钢卷异常明细弹窗
|
||||||
|
coilAnomalyDialog: false,
|
||||||
|
coilAnomalyCoilId: '',
|
||||||
// 导入相关数据
|
// 导入相关数据
|
||||||
importOpen: false,
|
importOpen: false,
|
||||||
file: null,
|
file: null,
|
||||||
@@ -437,6 +632,22 @@ export default {
|
|||||||
extra.sort((a, b) => String(a.label).localeCompare(String(b.label), 'zh-CN'))
|
extra.sort((a, b) => String(a.label).localeCompare(String(b.label), 'zh-CN'))
|
||||||
return [...SEGMENT_FORM_OPTIONS, ...extra]
|
return [...SEGMENT_FORM_OPTIONS, ...extra]
|
||||||
},
|
},
|
||||||
|
/** 当前选中点位下的异常条目 */
|
||||||
|
planAnomalies() {
|
||||||
|
if (!this.selectedPlan) return []
|
||||||
|
return this.allAnomalies.filter(a => a.paramCode === this.selectedPlan.pointCode ||
|
||||||
|
this.paramList.some(p => p.paramCode === a.paramCode))
|
||||||
|
},
|
||||||
|
/** paramCode → anomaly 的快速索引(用于状态列) */
|
||||||
|
paramAnomalyMap() {
|
||||||
|
const map = {}
|
||||||
|
this.allAnomalies.forEach(a => { map[a.paramCode] = a })
|
||||||
|
return map
|
||||||
|
},
|
||||||
|
coilAnomalyList() {
|
||||||
|
if (!this.coilAnomalyCoilId) return []
|
||||||
|
return this.allAnomalies.filter(a => a.coilId === this.coilAnomalyCoilId)
|
||||||
|
},
|
||||||
filteredPlans() {
|
filteredPlans() {
|
||||||
const type = this.activeSegmentType
|
const type = this.activeSegmentType
|
||||||
const sub = this.activeSegmentName
|
const sub = this.activeSegmentName
|
||||||
@@ -464,6 +675,18 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route: { immediate: true, handler() { this.syncFromRoute() } },
|
$route: { immediate: true, handler() { this.syncFromRoute() } },
|
||||||
|
planAnomalies: {
|
||||||
|
handler(list) {
|
||||||
|
if (list.length && this.anomalyExpanded) {
|
||||||
|
this.$nextTick(() => this.renderAnomalyChart())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
anomalyExpanded(val) {
|
||||||
|
if (val && this.planAnomalies.length) {
|
||||||
|
this.$nextTick(() => this.renderAnomalyChart())
|
||||||
|
}
|
||||||
|
},
|
||||||
segmentTypeTabOptions: {
|
segmentTypeTabOptions: {
|
||||||
handler() {
|
handler() {
|
||||||
const a = this.activeSegmentType
|
const a = this.activeSegmentType
|
||||||
@@ -495,9 +718,121 @@ export default {
|
|||||||
this.versionId = q.versionId || undefined
|
this.versionId = q.versionId || undefined
|
||||||
this.versionCode = q.versionCode || ''
|
this.versionCode = q.versionCode || ''
|
||||||
this.specId = q.specId || undefined
|
this.specId = q.specId || undefined
|
||||||
if (this.versionId) this.loadPlans()
|
if (this.versionId) {
|
||||||
|
this.loadPlans()
|
||||||
|
this.loadAnomalies()
|
||||||
|
this.loadCoilRecords()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadAnomalies() {
|
||||||
|
if (!this.versionId) return
|
||||||
|
listAllProcessAnomaly({ versionId: this.versionId }).then(res => {
|
||||||
|
this.allAnomalies = res.data || []
|
||||||
|
}).catch(() => { this.allAnomalies = [] })
|
||||||
|
},
|
||||||
|
loadCoilRecords() {
|
||||||
|
if (!this.versionId) return
|
||||||
|
this.coilRecordLoading = true
|
||||||
|
listProcessCoilRecord({ versionId: this.versionId, pageNum: this.coilRecordPage, pageSize: this.coilRecordPageSize })
|
||||||
|
.then(res => {
|
||||||
|
this.coilRecords = res.rows || []
|
||||||
|
this.coilRecordTotal = res.total || 0
|
||||||
|
}).catch(() => {}).finally(() => { this.coilRecordLoading = false })
|
||||||
|
},
|
||||||
|
renderAnomalyChart() {
|
||||||
|
const el = this.$refs.anomalyChart
|
||||||
|
if (!el || !this.planAnomalies.length) return
|
||||||
|
if (this.anomalyChartInst && !this.anomalyChartInst.isDisposed()) {
|
||||||
|
this.anomalyChartInst.dispose()
|
||||||
|
}
|
||||||
|
this.anomalyChartInst = echarts.init(el)
|
||||||
|
const items = this.planAnomalies
|
||||||
|
const names = items.map(a => a.paramName)
|
||||||
|
// 范围对比:规程范围 vs 实际范围,使用自定义 bar 叠加实现
|
||||||
|
const specRangeData = items.map(a => {
|
||||||
|
const lo = a.storedLower ?? a.storedTarget ?? 0
|
||||||
|
const hi = a.storedUpper ?? a.storedTarget ?? 0
|
||||||
|
return [lo, hi]
|
||||||
|
})
|
||||||
|
const actualRangeData = items.map(a => {
|
||||||
|
const lo = a.actualMin ?? 0
|
||||||
|
const hi = a.actualMax ?? 0
|
||||||
|
return [lo, hi]
|
||||||
|
})
|
||||||
|
// 把真实值编入 data,让 ECharts 正确推断 y 轴范围
|
||||||
|
// data item: [categoryIndex, lo, hi]
|
||||||
|
const specSeriesData = specRangeData.map(([lo, hi], i) => [i, lo, hi])
|
||||||
|
const actualSeriesData = actualRangeData.map(([lo, hi], i) => [i, lo, hi])
|
||||||
|
|
||||||
|
const allVals = [...specRangeData, ...actualRangeData].flat().filter(v => v != null && isFinite(v))
|
||||||
|
const yMin = allVals.length ? Math.min(...allVals) : 0
|
||||||
|
const yMax = allVals.length ? Math.max(...allVals) : 1
|
||||||
|
const pad = (yMax - yMin) * 0.15 || 1
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
title: { text: '规程范围 vs 实际范围对比', textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 6, left: 8 },
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter(params) {
|
||||||
|
const idx = params[0].dataIndex
|
||||||
|
const name = names[idx]
|
||||||
|
const spec = specRangeData[idx]
|
||||||
|
const actual = actualRangeData[idx]
|
||||||
|
return `${name}<br/>规程范围:${spec[0]} ~ ${spec[1]}<br/>实际范围:${actual[0]} ~ ${actual[1]}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: { data: ['规程范围', '实际范围'], top: 6, right: 8, textStyle: { fontSize: 11 } },
|
||||||
|
grid: { top: 44, bottom: 36, left: 12, right: 20, containLabel: true },
|
||||||
|
xAxis: { type: 'category', data: names, axisLabel: { fontSize: 11, rotate: names.length > 5 ? 30 : 0 } },
|
||||||
|
yAxis: { type: 'value', min: yMin - pad, max: yMax + pad, axisLabel: { fontSize: 11 } },
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '规程范围',
|
||||||
|
type: 'custom',
|
||||||
|
renderItem(params, api) {
|
||||||
|
const idx = api.value(0)
|
||||||
|
const lo = api.value(1)
|
||||||
|
const hi = api.value(2)
|
||||||
|
const start = api.coord([idx, lo])
|
||||||
|
const end = api.coord([idx, hi])
|
||||||
|
const w = api.size([1, 0])[0] * 0.3
|
||||||
|
return {
|
||||||
|
type: 'rect',
|
||||||
|
shape: { x: start[0] - w / 2, y: end[1], width: w, height: Math.max(start[1] - end[1], 2) },
|
||||||
|
style: { fill: 'rgba(95,123,160,0.35)', stroke: '#5F7BA0', lineWidth: 1 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: specSeriesData,
|
||||||
|
encode: { x: 0, y: [1, 2] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '实际范围',
|
||||||
|
type: 'custom',
|
||||||
|
renderItem(params, api) {
|
||||||
|
const idx = api.value(0)
|
||||||
|
const lo = api.value(1)
|
||||||
|
const hi = api.value(2)
|
||||||
|
const start = api.coord([idx, lo])
|
||||||
|
const end = api.coord([idx, hi])
|
||||||
|
const w = api.size([1, 0])[0] * 0.18
|
||||||
|
return {
|
||||||
|
type: 'rect',
|
||||||
|
shape: { x: start[0] - w / 2, y: end[1], width: w, height: Math.max(start[1] - end[1], 2) },
|
||||||
|
style: { fill: 'rgba(245,108,108,0.45)', stroke: '#F56C6C', lineWidth: 1.5 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: actualSeriesData,
|
||||||
|
encode: { x: 0, y: [1, 2] }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.anomalyChartInst.setOption(option)
|
||||||
},
|
},
|
||||||
goBack() { this.$router.go(-1) },
|
goBack() { this.$router.go(-1) },
|
||||||
|
jumpToAnomaly(coilId) {
|
||||||
|
this.coilAnomalyCoilId = coilId
|
||||||
|
this.coilAnomalyDialog = true
|
||||||
|
},
|
||||||
selectSegmentType(val) {
|
selectSegmentType(val) {
|
||||||
this.activeSegmentType = val === undefined || val === null ? '' : val
|
this.activeSegmentType = val === undefined || val === null ? '' : val
|
||||||
this.activeSegmentName = ''
|
this.activeSegmentName = ''
|
||||||
@@ -549,8 +884,8 @@ export default {
|
|||||||
segmentName: undefined,
|
segmentName: undefined,
|
||||||
pointName: undefined,
|
pointName: undefined,
|
||||||
pointCode: undefined,
|
pointCode: undefined,
|
||||||
actualValueId: undefined,
|
actualSrcId: undefined,
|
||||||
l1SetValueId: undefined,
|
presetSrcId: undefined,
|
||||||
targetValue: undefined,
|
targetValue: undefined,
|
||||||
lowerLimit: undefined,
|
lowerLimit: undefined,
|
||||||
upperLimit: undefined,
|
upperLimit: undefined,
|
||||||
@@ -1061,26 +1396,43 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
color: #5F7BA0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 16px;
|
||||||
|
background: #dcdfe6;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version-badge {
|
.version-badge {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #909399;
|
color: #5F7BA0;
|
||||||
background: #f0f2f5;
|
background: #edf2f8;
|
||||||
padding: 2px 8px;
|
padding: 2px 10px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
border: 1px solid #c8d8ea;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-tabs {
|
.config-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
margin-bottom: 14px;
|
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #dcdfe6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -1206,6 +1558,28 @@ export default {
|
|||||||
|
|
||||||
.btn-danger { color: #f56c6c; }
|
.btn-danger { color: #f56c6c; }
|
||||||
|
|
||||||
|
/* ── 偏差分析 ── */
|
||||||
|
.anomaly-section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 0 8px;
|
||||||
|
margin-top: 14px;
|
||||||
|
border-top: 2px solid #fdf6ec;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #E6A23C;
|
||||||
|
}
|
||||||
|
.anomaly-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 260px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.anomaly-badge { font-size: 12px; color: #F56C6C; font-weight: 600; }
|
||||||
|
.normal-badge { font-size: 12px; color: #67C23A; }
|
||||||
|
.no-data-badge { font-size: 12px; color: #c0c4cc; }
|
||||||
|
.val-over { color: #F56C6C; font-weight: 600; }
|
||||||
|
.val-under { color: #E6A23C; font-weight: 600; }
|
||||||
|
|
||||||
/* 导入对话框样式 */
|
/* 导入对话框样式 */
|
||||||
.import-container {
|
.import-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -1261,4 +1635,35 @@ export default {
|
|||||||
.import-error {
|
.import-error {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── 服役钢卷记录 ── */
|
||||||
|
.coil-record-section {
|
||||||
|
margin-top: 16px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.coil-record-hd {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
.coil-total-badge {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #606266;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
.coil-anomaly-badge {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #f56c6c;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.klp.controller;
|
||||||
|
|
||||||
|
import com.klp.common.core.controller.BaseController;
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.core.domain.R;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.domain.bo.WmsProcessAnomalyBo;
|
||||||
|
import com.klp.domain.vo.WmsProcessAnomalyVo;
|
||||||
|
import com.klp.service.IWmsProcessAnomalyService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺参数异常记录
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/wms/processAnomaly")
|
||||||
|
public class WmsProcessAnomalyController extends BaseController {
|
||||||
|
|
||||||
|
private final IWmsProcessAnomalyService wmsProcessAnomalyService;
|
||||||
|
|
||||||
|
/** 分页列表(规程方案页按版本/参数/钢卷查询) */
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo<WmsProcessAnomalyVo> list(WmsProcessAnomalyBo bo, PageQuery pageQuery) {
|
||||||
|
return wmsProcessAnomalyService.queryPageList(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 不分页列表(用于规程方案页全量加载当前版本异常) */
|
||||||
|
@GetMapping("/listAll")
|
||||||
|
public R<List<WmsProcessAnomalyVo>> listAll(WmsProcessAnomalyBo bo) {
|
||||||
|
return R.ok(wmsProcessAnomalyService.queryList(bo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量新增异常记录(实绩页检测到异常后调用) */
|
||||||
|
@PostMapping("/batchAdd")
|
||||||
|
public R<Void> batchAdd(@RequestBody List<WmsProcessAnomalyBo> boList) {
|
||||||
|
wmsProcessAnomalyService.batchInsert(boList);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.klp.controller;
|
||||||
|
|
||||||
|
import com.klp.common.core.controller.BaseController;
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.core.domain.R;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.domain.bo.WmsProcessCoilRecordBo;
|
||||||
|
import com.klp.domain.vo.WmsProcessCoilRecordVo;
|
||||||
|
import com.klp.service.IWmsProcessCoilRecordService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本钢卷服役记录
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/wms/processCoilRecord")
|
||||||
|
public class WmsProcessCoilRecordController extends BaseController {
|
||||||
|
|
||||||
|
private final IWmsProcessCoilRecordService wmsProcessCoilRecordService;
|
||||||
|
|
||||||
|
/** 分页列表(规程版本页查钢卷历史) */
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo<WmsProcessCoilRecordVo> list(WmsProcessCoilRecordBo bo, PageQuery pageQuery) {
|
||||||
|
return wmsProcessCoilRecordService.queryPageList(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 不分页列表 */
|
||||||
|
@GetMapping("/listAll")
|
||||||
|
public R<List<WmsProcessCoilRecordVo>> listAll(WmsProcessCoilRecordBo bo) {
|
||||||
|
return R.ok(wmsProcessCoilRecordService.queryList(bo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 版本下服役钢卷总数 */
|
||||||
|
@GetMapping("/count")
|
||||||
|
public R<Long> count(@RequestParam Long versionId) {
|
||||||
|
return R.ok(wmsProcessCoilRecordService.countByVersion(versionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增或更新(幂等接口,前端每次点击钢卷行时调用) */
|
||||||
|
@PostMapping("/upsert")
|
||||||
|
public R<Void> upsert(@RequestBody WmsProcessCoilRecordBo bo) {
|
||||||
|
wmsProcessCoilRecordService.upsert(bo);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
84
klp-wms/src/main/java/com/klp/domain/WmsProcessAnomaly.java
Normal file
84
klp-wms/src/main/java/com/klp/domain/WmsProcessAnomaly.java
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package com.klp.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺参数异常记录 wms_process_anomaly
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("wms_process_anomaly")
|
||||||
|
public class WmsProcessAnomaly {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "anomaly_id")
|
||||||
|
private Long anomalyId;
|
||||||
|
|
||||||
|
/** 规程版本ID */
|
||||||
|
private Long versionId;
|
||||||
|
|
||||||
|
/** 方案点位ID */
|
||||||
|
private Long planId;
|
||||||
|
|
||||||
|
/** 参数ID */
|
||||||
|
private Long paramId;
|
||||||
|
|
||||||
|
/** 出口钢卷号 */
|
||||||
|
private String coilId;
|
||||||
|
|
||||||
|
/** 入口钢卷号 */
|
||||||
|
private String enCoilId;
|
||||||
|
|
||||||
|
/** 参数编码 */
|
||||||
|
private String paramCode;
|
||||||
|
|
||||||
|
/** 参数名称 */
|
||||||
|
private String paramName;
|
||||||
|
|
||||||
|
/** 单位 */
|
||||||
|
private String unit;
|
||||||
|
|
||||||
|
/** 异常类型: OVER_MAX / UNDER_MIN / BOTH */
|
||||||
|
private String anomalyType;
|
||||||
|
|
||||||
|
/** 规程存储的设定值 */
|
||||||
|
private BigDecimal storedTarget;
|
||||||
|
|
||||||
|
/** 规程存储的上限 */
|
||||||
|
private BigDecimal storedUpper;
|
||||||
|
|
||||||
|
/** 规程存储的下限 */
|
||||||
|
private BigDecimal storedLower;
|
||||||
|
|
||||||
|
/** 本次L1实际设定值 */
|
||||||
|
private BigDecimal actualTarget;
|
||||||
|
|
||||||
|
/** 本次实际最大值 */
|
||||||
|
private BigDecimal actualMax;
|
||||||
|
|
||||||
|
/** 本次实际最小值 */
|
||||||
|
private BigDecimal actualMin;
|
||||||
|
|
||||||
|
/** 最大值偏差 actual_max - stored_upper */
|
||||||
|
private BigDecimal deviationMax;
|
||||||
|
|
||||||
|
/** 最小值偏差 actual_min - stored_lower */
|
||||||
|
private BigDecimal deviationMin;
|
||||||
|
|
||||||
|
/** 检测时间 */
|
||||||
|
private Date detectedAt;
|
||||||
|
|
||||||
|
private String createBy;
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@TableLogic
|
||||||
|
private Integer delFlag;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.klp.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.klp.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本钢卷服役记录 wms_process_coil_record
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("wms_process_coil_record")
|
||||||
|
public class WmsProcessCoilRecord extends BaseEntity {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "record_id")
|
||||||
|
private Long recordId;
|
||||||
|
|
||||||
|
/** 规程版本ID */
|
||||||
|
private Long versionId;
|
||||||
|
|
||||||
|
/** 出口钢卷号 */
|
||||||
|
private String coilId;
|
||||||
|
|
||||||
|
/** 入口钢卷号 */
|
||||||
|
private String enCoilId;
|
||||||
|
|
||||||
|
/** 是否存在异常 0否1是 */
|
||||||
|
private Integer hasAnomaly;
|
||||||
|
|
||||||
|
/** 异常参数数量 */
|
||||||
|
private Integer anomalyCnt;
|
||||||
|
|
||||||
|
/** 检测/服役时间 */
|
||||||
|
private Date processTime;
|
||||||
|
|
||||||
|
@TableLogic
|
||||||
|
private Integer delFlag;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -59,6 +59,16 @@ public class WmsProcessPlanParam extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String unit;
|
private String unit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际值来源钢卷号(首次写入时的 ENCOILID)
|
||||||
|
*/
|
||||||
|
private String actualSrcId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* L1 预设值来源钢卷号(首次写入时的 COILID)
|
||||||
|
*/
|
||||||
|
private String presetSrcId;
|
||||||
|
|
||||||
@TableLogic
|
@TableLogic
|
||||||
private Integer delFlag;
|
private Integer delFlag;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.klp.domain.bo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺参数异常记录业务对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WmsProcessAnomalyBo {
|
||||||
|
|
||||||
|
private Long versionId;
|
||||||
|
private Long planId;
|
||||||
|
private Long paramId;
|
||||||
|
private String coilId;
|
||||||
|
private String enCoilId;
|
||||||
|
private String paramCode;
|
||||||
|
private String paramName;
|
||||||
|
private String unit;
|
||||||
|
private String anomalyType;
|
||||||
|
private BigDecimal storedTarget;
|
||||||
|
private BigDecimal storedUpper;
|
||||||
|
private BigDecimal storedLower;
|
||||||
|
private BigDecimal actualTarget;
|
||||||
|
private BigDecimal actualMax;
|
||||||
|
private BigDecimal actualMin;
|
||||||
|
private BigDecimal deviationMax;
|
||||||
|
private BigDecimal deviationMin;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
|
||||||
|
private Date detectedAt;
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.klp.domain.bo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.klp.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本钢卷服役记录业务对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class WmsProcessCoilRecordBo extends BaseEntity {
|
||||||
|
|
||||||
|
private Long recordId;
|
||||||
|
|
||||||
|
private Long versionId;
|
||||||
|
|
||||||
|
private String coilId;
|
||||||
|
|
||||||
|
private String enCoilId;
|
||||||
|
|
||||||
|
private Integer hasAnomaly;
|
||||||
|
|
||||||
|
private Integer anomalyCnt;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
|
||||||
|
private Date processTime;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -39,5 +39,9 @@ public class WmsProcessPlanParamBo extends BaseEntity {
|
|||||||
|
|
||||||
private String unit;
|
private String unit;
|
||||||
|
|
||||||
|
private String actualSrcId;
|
||||||
|
|
||||||
|
private String presetSrcId;
|
||||||
|
|
||||||
private String remark;
|
private String remark;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.klp.domain.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺参数异常记录视图对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WmsProcessAnomalyVo {
|
||||||
|
|
||||||
|
private Long anomalyId;
|
||||||
|
private Long versionId;
|
||||||
|
private Long planId;
|
||||||
|
private Long paramId;
|
||||||
|
private String coilId;
|
||||||
|
private String enCoilId;
|
||||||
|
private String paramCode;
|
||||||
|
private String paramName;
|
||||||
|
private String unit;
|
||||||
|
private String anomalyType;
|
||||||
|
private BigDecimal storedTarget;
|
||||||
|
private BigDecimal storedUpper;
|
||||||
|
private BigDecimal storedLower;
|
||||||
|
private BigDecimal actualTarget;
|
||||||
|
private BigDecimal actualMax;
|
||||||
|
private BigDecimal actualMin;
|
||||||
|
private BigDecimal deviationMax;
|
||||||
|
private BigDecimal deviationMin;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date detectedAt;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.klp.domain.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本钢卷服役记录视图对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WmsProcessCoilRecordVo {
|
||||||
|
|
||||||
|
private Long recordId;
|
||||||
|
private Long versionId;
|
||||||
|
private String coilId;
|
||||||
|
private String enCoilId;
|
||||||
|
private Integer hasAnomaly;
|
||||||
|
private Integer anomalyCnt;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date processTime;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -41,6 +41,12 @@ public class WmsProcessPlanParamVo {
|
|||||||
@ExcelProperty(value = "单位")
|
@ExcelProperty(value = "单位")
|
||||||
private String unit;
|
private String unit;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "实际值来源钢卷号")
|
||||||
|
private String actualSrcId;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "L1设定值来源钢卷号")
|
||||||
|
private String presetSrcId;
|
||||||
|
|
||||||
@ExcelProperty(value = "备注")
|
@ExcelProperty(value = "备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.klp.mapper;
|
||||||
|
|
||||||
|
import com.klp.common.core.mapper.BaseMapperPlus;
|
||||||
|
import com.klp.domain.WmsProcessAnomaly;
|
||||||
|
import com.klp.domain.vo.WmsProcessAnomalyVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺参数异常记录 Mapper
|
||||||
|
*/
|
||||||
|
public interface WmsProcessAnomalyMapper extends BaseMapperPlus<WmsProcessAnomalyMapper, WmsProcessAnomaly, WmsProcessAnomalyVo> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.klp.mapper;
|
||||||
|
|
||||||
|
import com.klp.common.core.mapper.BaseMapperPlus;
|
||||||
|
import com.klp.domain.WmsProcessCoilRecord;
|
||||||
|
import com.klp.domain.vo.WmsProcessCoilRecordVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本钢卷服役记录 Mapper
|
||||||
|
*/
|
||||||
|
public interface WmsProcessCoilRecordMapper extends BaseMapperPlus<WmsProcessCoilRecordMapper, WmsProcessCoilRecord, WmsProcessCoilRecordVo> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.klp.service;
|
||||||
|
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.domain.bo.WmsProcessAnomalyBo;
|
||||||
|
import com.klp.domain.vo.WmsProcessAnomalyVo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺参数异常记录 Service
|
||||||
|
*/
|
||||||
|
public interface IWmsProcessAnomalyService {
|
||||||
|
|
||||||
|
TableDataInfo<WmsProcessAnomalyVo> queryPageList(WmsProcessAnomalyBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
List<WmsProcessAnomalyVo> queryList(WmsProcessAnomalyBo bo);
|
||||||
|
|
||||||
|
/** 批量插入异常记录 */
|
||||||
|
void batchInsert(List<WmsProcessAnomalyBo> boList);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.klp.service;
|
||||||
|
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.domain.bo.WmsProcessCoilRecordBo;
|
||||||
|
import com.klp.domain.vo.WmsProcessCoilRecordVo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本钢卷服役记录 Service
|
||||||
|
*/
|
||||||
|
public interface IWmsProcessCoilRecordService {
|
||||||
|
|
||||||
|
TableDataInfo<WmsProcessCoilRecordVo> queryPageList(WmsProcessCoilRecordBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
List<WmsProcessCoilRecordVo> queryList(WmsProcessCoilRecordBo bo);
|
||||||
|
|
||||||
|
/** 按版本统计服役钢卷总数(含异常数) */
|
||||||
|
long countByVersion(Long versionId);
|
||||||
|
|
||||||
|
/** 新增或更新(按 version_id + coil_id 唯一键) */
|
||||||
|
void upsert(WmsProcessCoilRecordBo bo);
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.klp.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.common.utils.StringUtils;
|
||||||
|
import com.klp.domain.WmsProcessAnomaly;
|
||||||
|
import com.klp.domain.bo.WmsProcessAnomalyBo;
|
||||||
|
import com.klp.domain.vo.WmsProcessAnomalyVo;
|
||||||
|
import com.klp.mapper.WmsProcessAnomalyMapper;
|
||||||
|
import com.klp.service.IWmsProcessAnomalyService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺参数异常记录 Service 实现
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Service
|
||||||
|
public class WmsProcessAnomalyServiceImpl implements IWmsProcessAnomalyService {
|
||||||
|
|
||||||
|
private final WmsProcessAnomalyMapper baseMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<WmsProcessAnomalyVo> queryPageList(WmsProcessAnomalyBo bo, PageQuery pageQuery) {
|
||||||
|
Page<WmsProcessAnomalyVo> result = baseMapper.selectVoPage(pageQuery.build(), buildQueryWrapper(bo));
|
||||||
|
return TableDataInfo.build(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<WmsProcessAnomalyVo> queryList(WmsProcessAnomalyBo bo) {
|
||||||
|
return baseMapper.selectVoList(buildQueryWrapper(bo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void batchInsert(List<WmsProcessAnomalyBo> boList) {
|
||||||
|
if (boList == null || boList.isEmpty()) return;
|
||||||
|
Date now = new Date();
|
||||||
|
List<WmsProcessAnomaly> entities = boList.stream().map(bo -> {
|
||||||
|
WmsProcessAnomaly e = BeanUtil.toBean(bo, WmsProcessAnomaly.class);
|
||||||
|
if (e.getDetectedAt() == null) e.setDetectedAt(now);
|
||||||
|
e.setCreateTime(now);
|
||||||
|
return e;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
entities.forEach(baseMapper::insert);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LambdaQueryWrapper<WmsProcessAnomaly> buildQueryWrapper(WmsProcessAnomalyBo bo) {
|
||||||
|
LambdaQueryWrapper<WmsProcessAnomaly> lqw = Wrappers.lambdaQuery();
|
||||||
|
lqw.eq(bo.getVersionId() != null, WmsProcessAnomaly::getVersionId, bo.getVersionId());
|
||||||
|
lqw.eq(bo.getPlanId() != null, WmsProcessAnomaly::getPlanId, bo.getPlanId());
|
||||||
|
lqw.eq(StringUtils.isNotBlank(bo.getCoilId()), WmsProcessAnomaly::getCoilId, bo.getCoilId());
|
||||||
|
lqw.eq(StringUtils.isNotBlank(bo.getParamCode()), WmsProcessAnomaly::getParamCode, bo.getParamCode());
|
||||||
|
lqw.eq(StringUtils.isNotBlank(bo.getAnomalyType()), WmsProcessAnomaly::getAnomalyType, bo.getAnomalyType());
|
||||||
|
lqw.orderByDesc(WmsProcessAnomaly::getDetectedAt);
|
||||||
|
return lqw;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.klp.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.domain.WmsProcessCoilRecord;
|
||||||
|
import com.klp.domain.bo.WmsProcessCoilRecordBo;
|
||||||
|
import com.klp.domain.vo.WmsProcessCoilRecordVo;
|
||||||
|
import com.klp.mapper.WmsProcessCoilRecordMapper;
|
||||||
|
import com.klp.service.IWmsProcessCoilRecordService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本钢卷服役记录 Service 实现
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Service
|
||||||
|
public class WmsProcessCoilRecordServiceImpl implements IWmsProcessCoilRecordService {
|
||||||
|
|
||||||
|
private final WmsProcessCoilRecordMapper baseMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<WmsProcessCoilRecordVo> queryPageList(WmsProcessCoilRecordBo bo, PageQuery pageQuery) {
|
||||||
|
Page<WmsProcessCoilRecordVo> result = baseMapper.selectVoPage(pageQuery.build(), buildQueryWrapper(bo));
|
||||||
|
return TableDataInfo.build(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<WmsProcessCoilRecordVo> queryList(WmsProcessCoilRecordBo bo) {
|
||||||
|
return baseMapper.selectVoList(buildQueryWrapper(bo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countByVersion(Long versionId) {
|
||||||
|
return baseMapper.selectCount(Wrappers.<WmsProcessCoilRecord>lambdaQuery()
|
||||||
|
.eq(WmsProcessCoilRecord::getVersionId, versionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void upsert(WmsProcessCoilRecordBo bo) {
|
||||||
|
LambdaQueryWrapper<WmsProcessCoilRecord> lqw = Wrappers.lambdaQuery();
|
||||||
|
lqw.eq(WmsProcessCoilRecord::getVersionId, bo.getVersionId());
|
||||||
|
lqw.eq(WmsProcessCoilRecord::getCoilId, bo.getCoilId());
|
||||||
|
WmsProcessCoilRecord existing = baseMapper.selectOne(lqw);
|
||||||
|
if (existing != null) {
|
||||||
|
existing.setHasAnomaly(bo.getHasAnomaly());
|
||||||
|
existing.setAnomalyCnt(bo.getAnomalyCnt());
|
||||||
|
existing.setProcessTime(bo.getProcessTime());
|
||||||
|
existing.setEnCoilId(bo.getEnCoilId());
|
||||||
|
baseMapper.updateById(existing);
|
||||||
|
} else {
|
||||||
|
WmsProcessCoilRecord record = BeanUtil.toBean(bo, WmsProcessCoilRecord.class);
|
||||||
|
baseMapper.insert(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LambdaQueryWrapper<WmsProcessCoilRecord> buildQueryWrapper(WmsProcessCoilRecordBo bo) {
|
||||||
|
LambdaQueryWrapper<WmsProcessCoilRecord> lqw = Wrappers.lambdaQuery();
|
||||||
|
lqw.eq(bo.getVersionId() != null, WmsProcessCoilRecord::getVersionId, bo.getVersionId());
|
||||||
|
lqw.eq(bo.getCoilId() != null, WmsProcessCoilRecord::getCoilId, bo.getCoilId());
|
||||||
|
lqw.eq(bo.getHasAnomaly() != null, WmsProcessCoilRecord::getHasAnomaly, bo.getHasAnomaly());
|
||||||
|
lqw.orderByDesc(WmsProcessCoilRecord::getProcessTime);
|
||||||
|
return lqw;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.klp.mapper.WmsProcessAnomalyMapper">
|
||||||
|
|
||||||
|
<resultMap type="com.klp.domain.WmsProcessAnomaly" id="WmsProcessAnomalyResult">
|
||||||
|
<result property="anomalyId" column="anomaly_id"/>
|
||||||
|
<result property="versionId" column="version_id"/>
|
||||||
|
<result property="planId" column="plan_id"/>
|
||||||
|
<result property="paramId" column="param_id"/>
|
||||||
|
<result property="coilId" column="coil_id"/>
|
||||||
|
<result property="enCoilId" column="en_coil_id"/>
|
||||||
|
<result property="paramCode" column="param_code"/>
|
||||||
|
<result property="paramName" column="param_name"/>
|
||||||
|
<result property="unit" column="unit"/>
|
||||||
|
<result property="anomalyType" column="anomaly_type"/>
|
||||||
|
<result property="storedTarget" column="stored_target"/>
|
||||||
|
<result property="storedUpper" column="stored_upper"/>
|
||||||
|
<result property="storedLower" column="stored_lower"/>
|
||||||
|
<result property="actualTarget" column="actual_target"/>
|
||||||
|
<result property="actualMax" column="actual_max"/>
|
||||||
|
<result property="actualMin" column="actual_min"/>
|
||||||
|
<result property="deviationMax" column="deviation_max"/>
|
||||||
|
<result property="deviationMin" column="deviation_min"/>
|
||||||
|
<result property="detectedAt" column="detected_at"/>
|
||||||
|
<result property="createBy" column="create_by"/>
|
||||||
|
<result property="createTime" column="create_time"/>
|
||||||
|
<result property="delFlag" column="del_flag"/>
|
||||||
|
<result property="remark" column="remark"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.klp.mapper.WmsProcessCoilRecordMapper">
|
||||||
|
|
||||||
|
<resultMap type="com.klp.domain.WmsProcessCoilRecord" id="WmsProcessCoilRecordResult">
|
||||||
|
<result property="recordId" column="record_id"/>
|
||||||
|
<result property="versionId" column="version_id"/>
|
||||||
|
<result property="coilId" column="coil_id"/>
|
||||||
|
<result property="enCoilId" column="en_coil_id"/>
|
||||||
|
<result property="hasAnomaly" column="has_anomaly"/>
|
||||||
|
<result property="anomalyCnt" column="anomaly_cnt"/>
|
||||||
|
<result property="processTime" column="process_time"/>
|
||||||
|
<result property="createBy" column="create_by"/>
|
||||||
|
<result property="createTime" column="create_time"/>
|
||||||
|
<result property="updateBy" column="update_by"/>
|
||||||
|
<result property="updateTime" column="update_time"/>
|
||||||
|
<result property="delFlag" column="del_flag"/>
|
||||||
|
<result property="remark" column="remark"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
<result property="lowerLimit" column="lower_limit"/>
|
<result property="lowerLimit" column="lower_limit"/>
|
||||||
<result property="upperLimit" column="upper_limit"/>
|
<result property="upperLimit" column="upper_limit"/>
|
||||||
<result property="unit" column="unit"/>
|
<result property="unit" column="unit"/>
|
||||||
|
<result property="actualSrcId" column="actual_src_id"/>
|
||||||
|
<result property="presetSrcId" column="preset_src_id"/>
|
||||||
<result property="createBy" column="create_by"/>
|
<result property="createBy" column="create_by"/>
|
||||||
<result property="createTime" column="create_time"/>
|
<result property="createTime" column="create_time"/>
|
||||||
<result property="updateBy" column="update_by"/>
|
<result property="updateBy" column="update_by"/>
|
||||||
|
|||||||
210
script/sql/mysql/process.sql
Normal file
210
script/sql/mysql/process.sql
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
create table wms_process_plan
|
||||||
|
(
|
||||||
|
plan_id bigint not null comment '主键'
|
||||||
|
primary key,
|
||||||
|
version_id bigint not null comment '规程版本ID',
|
||||||
|
segment_type varchar(32) not null comment '段类型(INLET/PROCESS/OUTLET)',
|
||||||
|
segment_name varchar(100) null comment '段名称',
|
||||||
|
point_name varchar(200) not null comment '点位名称',
|
||||||
|
point_code varchar(64) not null comment '点位编码',
|
||||||
|
sort_order int default 0 not null comment '排序',
|
||||||
|
create_by varchar(64) null comment '创建人',
|
||||||
|
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
|
||||||
|
update_by varchar(64) null comment '更新人',
|
||||||
|
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
del_flag tinyint default 0 not null comment '删除标志(0正常2删除)',
|
||||||
|
remark varchar(500) null comment '备注',
|
||||||
|
constraint uk_plan_version_point_code
|
||||||
|
unique (version_id, point_code)
|
||||||
|
)
|
||||||
|
comment '方案点位表';
|
||||||
|
|
||||||
|
create index idx_plan_sort
|
||||||
|
on wms_process_plan (version_id, sort_order);
|
||||||
|
|
||||||
|
create index idx_plan_version
|
||||||
|
on wms_process_plan (version_id);
|
||||||
|
|
||||||
|
create table wms_process_plan_param
|
||||||
|
(
|
||||||
|
param_id bigint not null comment '主键'
|
||||||
|
primary key,
|
||||||
|
plan_id bigint not null comment '方案点位ID',
|
||||||
|
param_code varchar(64) not null comment '参数编码',
|
||||||
|
param_name varchar(200) not null comment '参数名称',
|
||||||
|
target_value decimal(24, 6) null comment '设定值(L1预设值)',
|
||||||
|
lower_limit decimal(24, 6) null comment '下限(首次检测实际最小值)',
|
||||||
|
upper_limit decimal(24, 6) null comment '上限(首次检测实际最大值)',
|
||||||
|
unit varchar(32) null comment '单位',
|
||||||
|
actual_src_id varchar(64) null comment '实际值来源钢卷号(首次写入时的ENCOILID)',
|
||||||
|
preset_src_id varchar(64) null comment 'L1设定值来源钢卷号(首次写入时的COILID)',
|
||||||
|
create_by varchar(64) null comment '创建人',
|
||||||
|
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
|
||||||
|
update_by varchar(64) null comment '更新人',
|
||||||
|
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
del_flag tinyint default 0 not null comment '删除标志(0正常2删除)',
|
||||||
|
remark varchar(500) null comment '备注',
|
||||||
|
constraint uk_plan_param_code
|
||||||
|
unique (plan_id, param_code)
|
||||||
|
)
|
||||||
|
comment '方案参数表';
|
||||||
|
|
||||||
|
create index idx_plan_param_plan
|
||||||
|
on wms_process_plan_param (plan_id);
|
||||||
|
|
||||||
|
create table wms_process_spec
|
||||||
|
(
|
||||||
|
spec_id bigint auto_increment comment '主键'
|
||||||
|
primary key,
|
||||||
|
spec_code varchar(64) not null comment '规程编号',
|
||||||
|
spec_name varchar(200) not null comment '规程名称',
|
||||||
|
spec_type varchar(32) default 'PROCESS' not null comment '类型(PROCESS=工艺规程,STANDARD=标准)',
|
||||||
|
line_id bigint not null comment '产线ID',
|
||||||
|
product_type varchar(100) null comment '产品类型',
|
||||||
|
is_enabled tinyint default 1 not null comment '是否启用(0否1是)',
|
||||||
|
create_by varchar(64) null comment '创建人',
|
||||||
|
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
|
||||||
|
update_by varchar(64) null comment '更新人',
|
||||||
|
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
del_flag tinyint default 0 not null comment '删除标志(0正常2删除,与全局逻辑删除配置一致)',
|
||||||
|
remark varchar(500) null comment '备注',
|
||||||
|
constraint uk_wms_process_spec_code
|
||||||
|
unique (spec_code)
|
||||||
|
)
|
||||||
|
comment '规程主表';
|
||||||
|
|
||||||
|
create index idx_wms_process_spec_line
|
||||||
|
on wms_process_spec (line_id);
|
||||||
|
|
||||||
|
create index idx_wms_process_spec_type
|
||||||
|
on wms_process_spec (spec_type);
|
||||||
|
|
||||||
|
create table wms_process_spec_version
|
||||||
|
(
|
||||||
|
version_id bigint not null comment '主键'
|
||||||
|
primary key,
|
||||||
|
spec_id bigint not null comment '规程主表ID',
|
||||||
|
version_code varchar(64) not null comment '版本号',
|
||||||
|
is_active tinyint default 0 not null comment '是否当前生效(0否1是)',
|
||||||
|
status varchar(32) default 'DRAFT' not null comment '状态(DRAFT草稿/PUBLISHED已发布/OBSOLETE作废等)',
|
||||||
|
create_by varchar(64) null comment '创建人',
|
||||||
|
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
|
||||||
|
update_by varchar(64) null comment '更新人',
|
||||||
|
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
del_flag tinyint default 0 not null comment '删除标志(0正常2删除)',
|
||||||
|
remark varchar(500) null comment '备注',
|
||||||
|
constraint uk_spec_version_code
|
||||||
|
unique (spec_id, version_code)
|
||||||
|
)
|
||||||
|
comment '规程版本表';
|
||||||
|
|
||||||
|
create index idx_spec_version_active
|
||||||
|
on wms_process_spec_version (spec_id, is_active);
|
||||||
|
|
||||||
|
create index idx_spec_version_spec
|
||||||
|
on wms_process_spec_version (spec_id);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────────────────────
|
||||||
|
-- 钢卷服役记录表:记录哪些钢卷经过了某版本规程的检测/生产
|
||||||
|
-- ─────────────────────────────────────────────────────────────
|
||||||
|
create table wms_process_coil_record
|
||||||
|
(
|
||||||
|
record_id bigint not null comment '主键'
|
||||||
|
primary key,
|
||||||
|
version_id bigint not null comment '规程版本ID',
|
||||||
|
coil_id varchar(64) not null comment '出口钢卷号(EXCOILID)',
|
||||||
|
en_coil_id varchar(64) null comment '入口钢卷号(ENCOILID)',
|
||||||
|
has_anomaly tinyint default 0 not null comment '本次是否检测到参数异常(0否1是)',
|
||||||
|
anomaly_cnt int default 0 not null comment '异常参数数量',
|
||||||
|
process_time datetime null comment '检测时间',
|
||||||
|
create_by varchar(64) null comment '创建人',
|
||||||
|
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
|
||||||
|
update_by varchar(64) null comment '更新人',
|
||||||
|
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
del_flag tinyint default 0 not null comment '删除标志(0正常2删除)',
|
||||||
|
remark varchar(500) null comment '备注',
|
||||||
|
constraint uk_coil_version
|
||||||
|
unique (version_id, coil_id)
|
||||||
|
)
|
||||||
|
comment '版本钢卷服役记录表';
|
||||||
|
|
||||||
|
create index idx_coil_record_version
|
||||||
|
on wms_process_coil_record (version_id);
|
||||||
|
|
||||||
|
create index idx_coil_record_coil
|
||||||
|
on wms_process_coil_record (coil_id);
|
||||||
|
|
||||||
|
create index idx_coil_record_anomaly
|
||||||
|
on wms_process_coil_record (version_id, has_anomaly);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────────────────────
|
||||||
|
-- 工艺参数异常记录表:持久化每次检测到的超限/欠限异常
|
||||||
|
-- ─────────────────────────────────────────────────────────────
|
||||||
|
create table wms_process_anomaly
|
||||||
|
(
|
||||||
|
anomaly_id bigint not null comment '主键'
|
||||||
|
primary key,
|
||||||
|
version_id bigint not null comment '规程版本ID',
|
||||||
|
plan_id bigint not null comment '方案点位ID',
|
||||||
|
param_id bigint null comment '参数ID(wms_process_plan_param)',
|
||||||
|
coil_id varchar(64) not null comment '触发异常的出口钢卷号',
|
||||||
|
en_coil_id varchar(64) null comment '入口钢卷号',
|
||||||
|
param_code varchar(64) not null comment '参数编码',
|
||||||
|
param_name varchar(200) null comment '参数名称',
|
||||||
|
unit varchar(32) null comment '单位',
|
||||||
|
anomaly_type varchar(32) not null comment '异常类型: OVER_MAX/UNDER_MIN/BOTH',
|
||||||
|
stored_target decimal(24, 6) null comment '规程中存储的设定值',
|
||||||
|
stored_upper decimal(24, 6) null comment '规程中存储的上限',
|
||||||
|
stored_lower decimal(24, 6) null comment '规程中存储的下限',
|
||||||
|
actual_target decimal(24, 6) null comment '本次L1实际设定值',
|
||||||
|
actual_max decimal(24, 6) null comment '本次实际最大值',
|
||||||
|
actual_min decimal(24, 6) null comment '本次实际最小值',
|
||||||
|
deviation_max decimal(24, 6) null comment '最大值偏差(actual_max - stored_upper, 正值表示超上限)',
|
||||||
|
deviation_min decimal(24, 6) null comment '最小值偏差(actual_min - stored_lower, 负值表示低于下限)',
|
||||||
|
detected_at datetime null comment '异常检测时间',
|
||||||
|
create_by varchar(64) null comment '创建人',
|
||||||
|
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
|
||||||
|
del_flag tinyint default 0 not null comment '删除标志(0正常2删除)',
|
||||||
|
remark varchar(500) null comment '备注'
|
||||||
|
)
|
||||||
|
comment '工艺参数异常记录表';
|
||||||
|
|
||||||
|
create index idx_anomaly_version
|
||||||
|
on wms_process_anomaly (version_id);
|
||||||
|
|
||||||
|
create index idx_anomaly_coil
|
||||||
|
on wms_process_anomaly (coil_id);
|
||||||
|
|
||||||
|
create index idx_anomaly_param
|
||||||
|
on wms_process_anomaly (version_id, param_code);
|
||||||
|
|
||||||
|
create index idx_anomaly_detected
|
||||||
|
on wms_process_anomaly (detected_at);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────────────────────
|
||||||
|
-- 存量数据迁移(为 wms_process_plan_param 新增列打补丁)
|
||||||
|
-- 已有库执行此 ALTER,新建库直接用上方 CREATE TABLE 即可
|
||||||
|
-- ─────────────────────────────────────────────────────────────
|
||||||
|
-- ALTER TABLE wms_process_plan_param
|
||||||
|
-- ADD COLUMN actual_src_id varchar(64) null comment '实际值来源钢卷号(首次写入时的ENCOILID)' AFTER unit,
|
||||||
|
-- ADD COLUMN preset_src_id varchar(64) null comment 'L1设定值来源钢卷号(首次写入时的COILID)' AFTER actual_src_id;
|
||||||
|
|
||||||
|
create table wms_process_task
|
||||||
|
(
|
||||||
|
task_id bigint auto_increment comment '工艺任务ID'
|
||||||
|
primary key,
|
||||||
|
plan_id bigint not null comment '关联生产计划ID',
|
||||||
|
process_id bigint not null comment '所需工艺ID',
|
||||||
|
product_id bigint not null comment '对应产品ID',
|
||||||
|
task_quantity int not null comment '任务数量',
|
||||||
|
task_status varchar(20) default 'pending' null comment '任务状态:pending-待处理/processing-处理中/completed-已完成',
|
||||||
|
sequence int not null comment '工艺顺序',
|
||||||
|
del_flag tinyint(1) default 0 not null comment '删除标志(0=正常,1=已删除)',
|
||||||
|
remark varchar(255) null comment '备注',
|
||||||
|
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
|
||||||
|
create_by varchar(50) null comment '创建人',
|
||||||
|
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
update_by varchar(50) null comment '更新人'
|
||||||
|
)
|
||||||
|
comment '工艺任务表(生产计划所需工艺任务)' row_format = DYNAMIC;
|
||||||
|
|
||||||
Reference in New Issue
Block a user