Merge remote-tracking branch 'origin/0.8.X' into 0.8.X
This commit is contained in:
@@ -178,75 +178,78 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 参数对比表 -->
|
<!-- 参数分段 Tab -->
|
||||||
<div v-if="!detailLoading && detailParams.length === 0" class="detail-empty">
|
<div class="seg-tabs">
|
||||||
暂无参数数据
|
<div class="seg-tab-bar">
|
||||||
</div>
|
<button
|
||||||
<template v-else>
|
v-for="seg in detailSegmentTabs"
|
||||||
<div
|
:key="seg.segmentType"
|
||||||
v-for="group in detailParamGroups"
|
:class="['seg-tab-btn', { 'is-active': detailActiveTab === seg.segmentType }]"
|
||||||
:key="group.planId"
|
@click="detailActiveTab = seg.segmentType"
|
||||||
class="param-group"
|
|
||||||
>
|
|
||||||
<div class="param-group-hd">
|
|
||||||
<span class="pg-segment">{{ group.segmentName || group.segmentType || '—' }}</span>
|
|
||||||
<span class="pg-point">{{ group.pointName }}
|
|
||||||
<span v-if="group.pointCode" class="pg-code">{{ group.pointCode }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<el-table
|
|
||||||
:data="group.params"
|
|
||||||
size="mini"
|
|
||||||
border
|
|
||||||
:row-class-name="paramRowClass"
|
|
||||||
>
|
>
|
||||||
<el-table-column label="参数名称" prop="paramName" min-width="120" show-overflow-tooltip />
|
{{ seg.segmentLabel }}
|
||||||
<el-table-column label="编码" prop="paramCode" width="90" show-overflow-tooltip />
|
<span v-if="seg.anomalyCount > 0" class="seg-badge">{{ seg.anomalyCount }}</span>
|
||||||
<el-table-column label="单位" prop="unit" width="56" align="center">
|
</button>
|
||||||
<template slot-scope="{ row }">{{ row.unit || '—' }}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="设定目标" width="84" align="right">
|
|
||||||
<template slot-scope="{ row }">
|
|
||||||
<span :class="row._hasAnomaly ? 'val-anomaly' : ''">{{ fmtNum(row.targetValue) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="下限" width="72" align="right">
|
|
||||||
<template slot-scope="{ row }">{{ fmtNum(row.lowerLimit) }}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="上限" width="72" align="right">
|
|
||||||
<template slot-scope="{ row }">{{ fmtNum(row.upperLimit) }}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="实际最大" width="84" align="right">
|
|
||||||
<template slot-scope="{ row }">
|
|
||||||
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'UPPER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMax) }}</span>
|
|
||||||
<span v-else class="dim">—</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="实际最小" width="84" align="right">
|
|
||||||
<template slot-scope="{ row }">
|
|
||||||
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'LOWER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMin) }}</span>
|
|
||||||
<span v-else class="dim">—</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="偏差" width="72" align="right">
|
|
||||||
<template slot-scope="{ row }">
|
|
||||||
<span v-if="row._hasAnomaly" class="val-danger">
|
|
||||||
{{ fmtNum(row._anomalyType === 'UPPER' ? row._deviationMax : row._deviationMin) }}
|
|
||||||
</span>
|
|
||||||
<span v-else class="dim">—</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="状态" width="72" align="center">
|
|
||||||
<template slot-scope="{ row }">
|
|
||||||
<el-tag v-if="row._hasAnomaly" type="danger" size="mini" effect="plain">
|
|
||||||
{{ row._anomalyType === 'UPPER' ? '超上限' : row._anomalyType === 'LOWER' ? '低于下限' : '异常' }}
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-else type="success" size="mini" effect="plain">正常</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
<template v-for="seg in detailSegmentTabs">
|
||||||
|
<div v-show="detailActiveTab === seg.segmentType" :key="seg.segmentType" class="seg-tab-pane">
|
||||||
|
<div v-if="!detailLoading && seg.groups.length === 0" class="detail-empty">暂无参数数据</div>
|
||||||
|
<div v-for="group in seg.groups" :key="group.planId" class="param-group">
|
||||||
|
<div class="param-group-hd">
|
||||||
|
<span class="pg-point">{{ group.pointName }}</span>
|
||||||
|
<span v-if="group.pointCode" class="pg-code">{{ group.pointCode }}</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="group.params" size="mini" border :row-class-name="paramRowClass">
|
||||||
|
<el-table-column label="参数名称" prop="paramName" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column label="编码" prop="paramCode" width="100" show-overflow-tooltip />
|
||||||
|
<el-table-column label="单位" prop="unit" width="68" align="center">
|
||||||
|
<template slot-scope="{ row }">{{ row.unit || '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="设定目标" width="84" align="right">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span :class="row._hasAnomaly ? 'val-anomaly' : ''">{{ fmtNum(row.targetValue) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="下限" width="72" align="right">
|
||||||
|
<template slot-scope="{ row }">{{ fmtNum(row.lowerLimit) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="上限" width="72" align="right">
|
||||||
|
<template slot-scope="{ row }">{{ fmtNum(row.upperLimit) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="实际最大" width="84" align="right">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'UPPER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMax) }}</span>
|
||||||
|
<span v-else class="dim">—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="实际最小" width="84" align="right">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'LOWER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMin) }}</span>
|
||||||
|
<span v-else class="dim">—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="偏差" width="72" align="right">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span v-if="row._hasAnomaly" class="val-danger">
|
||||||
|
{{ fmtNum(row._anomalyType === 'UPPER' ? row._deviationMax : row._deviationMin) }}
|
||||||
|
</span>
|
||||||
|
<span v-else class="dim">—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" width="76" align="center">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-tag v-if="row._hasAnomaly" type="danger" size="mini" effect="plain">
|
||||||
|
{{ row._anomalyType === 'UPPER' ? '超上限' : row._anomalyType === 'LOWER' ? '低于下限' : '异常' }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else type="success" size="mini" effect="plain">正常</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|
||||||
@@ -391,9 +394,41 @@
|
|||||||
import { listProcessCoilRecord, batchRebindCoilRecord } from '@/api/wms/processCoilRecord'
|
import { listProcessCoilRecord, batchRebindCoilRecord } from '@/api/wms/processCoilRecord'
|
||||||
import { listProcessSpec } from '@/api/wms/processSpec'
|
import { listProcessSpec } from '@/api/wms/processSpec'
|
||||||
import { listProcessSpecVersion } from '@/api/wms/processSpecVersion'
|
import { listProcessSpecVersion } from '@/api/wms/processSpecVersion'
|
||||||
import { listProcessPlan } from '@/api/wms/processPlan'
|
import { listProcessPlan, addProcessPlan } from '@/api/wms/processPlan'
|
||||||
import { listProcessPlanParam } from '@/api/wms/processPlanParam'
|
import { listProcessPlanParam, addProcessPlanParam } from '@/api/wms/processPlanParam'
|
||||||
import { listAllProcessAnomaly } from '@/api/wms/processAnomaly'
|
import { listAllProcessAnomaly } from '@/api/wms/processAnomaly'
|
||||||
|
import { getPresetSetupByCoilId } from '@/api/l2/timing'
|
||||||
|
|
||||||
|
// 三段 L1 预设设定值的结构定义(对应 PLTCM_PRESET_SETUP 表)
|
||||||
|
const PRESET_PLANS = [
|
||||||
|
{
|
||||||
|
segmentType: 'INLET', segmentName: '入口段', pointName: '入口设定值', pointCode: 'PRESET_INLET', sortOrder: 10,
|
||||||
|
params: [
|
||||||
|
{ paramCode: 'POR_TEN', paramName: '开卷机单位张力', unit: 'N/mm²' },
|
||||||
|
{ paramCode: 'CEL_TEN', paramName: '入口活套单位张力', unit: 'N/mm²' },
|
||||||
|
{ paramCode: 'FLAT_MESH_1', paramName: '矫直机1#辊插入量', unit: 'mm' },
|
||||||
|
{ paramCode: 'FLAT_MESH_2', paramName: '矫直机2#辊插入量', unit: 'mm' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
segmentType: 'PROCESS', segmentName: '工艺段', pointName: '工艺设定值', pointCode: 'PRESET_PROCESS', sortOrder: 20,
|
||||||
|
params: [
|
||||||
|
{ paramCode: 'TLV_TEN', paramName: '拉弯矫直机单位张力', unit: 'N/mm²' },
|
||||||
|
{ paramCode: 'TLV_ELONG', paramName: '拉弯矫直机延伸率', unit: '%' },
|
||||||
|
{ paramCode: 'TLV_MESH_1', paramName: '弯曲辊1#弯辊量', unit: 'mm' },
|
||||||
|
{ paramCode: 'TLV_MESH_2', paramName: '弯曲辊2#弯辊量', unit: 'mm' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
segmentType: 'OUTLET', segmentName: '出口段', pointName: '出口设定值', pointCode: 'PRESET_OUTLET', sortOrder: 30,
|
||||||
|
params: [
|
||||||
|
{ paramCode: 'TR_TEN', paramName: '酸洗出口张力辊张力', unit: 'N/mm²' },
|
||||||
|
{ paramCode: 'TRIM_TEN', paramName: '切边段单位张力', unit: 'N/mm²' },
|
||||||
|
{ paramCode: 'TEL_TEN', paramName: '联机活套单位张力', unit: 'N/mm²' },
|
||||||
|
{ paramCode: 'CXL_TEN', paramName: '出口活套单位张力', unit: 'N/mm²' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const VERSION_STATUS = [
|
const VERSION_STATUS = [
|
||||||
{ value: 'DRAFT', label: '草稿' },
|
{ value: 'DRAFT', label: '草稿' },
|
||||||
@@ -422,6 +457,7 @@ export default {
|
|||||||
detailLoading: false,
|
detailLoading: false,
|
||||||
detailRow: null,
|
detailRow: null,
|
||||||
detailParams: [],
|
detailParams: [],
|
||||||
|
detailActiveTab: 'INLET',
|
||||||
|
|
||||||
bindDialogVisible: false,
|
bindDialogVisible: false,
|
||||||
removeOldRecord: false,
|
removeOldRecord: false,
|
||||||
@@ -456,6 +492,23 @@ export default {
|
|||||||
}
|
}
|
||||||
return Object.values(planMap).sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
return Object.values(planMap).sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||||
},
|
},
|
||||||
|
detailSegmentTabs() {
|
||||||
|
const SEG_ORDER = ['INLET', 'PROCESS', 'OUTLET']
|
||||||
|
const SEG_LABELS = { INLET: '入口段', PROCESS: '工艺段', OUTLET: '出口段' }
|
||||||
|
const map = {}
|
||||||
|
for (const g of this.detailParamGroups) {
|
||||||
|
const seg = g.segmentType || 'OTHER'
|
||||||
|
if (!map[seg]) {
|
||||||
|
map[seg] = { segmentType: seg, segmentLabel: SEG_LABELS[seg] || seg, anomalyCount: 0, groups: [] }
|
||||||
|
}
|
||||||
|
map[seg].groups.push(g)
|
||||||
|
map[seg].anomalyCount += g.params.filter(p => p._hasAnomaly).length
|
||||||
|
}
|
||||||
|
for (const seg of SEG_ORDER) {
|
||||||
|
if (!map[seg]) map[seg] = { segmentType: seg, segmentLabel: SEG_LABELS[seg], anomalyCount: 0, groups: [] }
|
||||||
|
}
|
||||||
|
return SEG_ORDER.map(s => map[s])
|
||||||
|
},
|
||||||
// 选中行是否全来自同一个版本(决定是否可以"移除旧记录")
|
// 选中行是否全来自同一个版本(决定是否可以"移除旧记录")
|
||||||
singleSourceVersion() {
|
singleSourceVersion() {
|
||||||
if (!this.selected.length) return false
|
if (!this.selected.length) return false
|
||||||
@@ -580,6 +633,7 @@ export default {
|
|||||||
openDetail(row) {
|
openDetail(row) {
|
||||||
this.detailRow = row
|
this.detailRow = row
|
||||||
this.detailParams = []
|
this.detailParams = []
|
||||||
|
this.detailActiveTab = 'INLET'
|
||||||
this.detailDrawerVisible = true
|
this.detailDrawerVisible = true
|
||||||
this.loadDetailData(row)
|
this.loadDetailData(row)
|
||||||
},
|
},
|
||||||
@@ -587,6 +641,45 @@ export default {
|
|||||||
this.detailRow = null
|
this.detailRow = null
|
||||||
this.detailParams = []
|
this.detailParams = []
|
||||||
},
|
},
|
||||||
|
// Returns true if any preset plan points were auto-created
|
||||||
|
async autoInitPresetParams(row, existingPlans) {
|
||||||
|
try {
|
||||||
|
const existingCodes = new Set(existingPlans.map(p => p.pointCode))
|
||||||
|
const missing = PRESET_PLANS.filter(p => !existingCodes.has(p.pointCode))
|
||||||
|
if (!missing.length) return false
|
||||||
|
const srcId = row.enCoilId || row.coilId
|
||||||
|
if (!srcId) return false
|
||||||
|
const presetRes = await getPresetSetupByCoilId(srcId)
|
||||||
|
const preset = (presetRes && presetRes.data && presetRes.data.data) || {}
|
||||||
|
for (const def of missing) {
|
||||||
|
const planRes = await addProcessPlan({
|
||||||
|
versionId: row.versionId,
|
||||||
|
segmentType: def.segmentType,
|
||||||
|
segmentName: def.segmentName,
|
||||||
|
pointName: def.pointName,
|
||||||
|
pointCode: def.pointCode,
|
||||||
|
sortOrder: def.sortOrder
|
||||||
|
})
|
||||||
|
const planId = planRes.data
|
||||||
|
for (const p of def.params) {
|
||||||
|
const val = preset[p.paramCode.toLowerCase()] ?? preset[p.paramCode]
|
||||||
|
await addProcessPlanParam({
|
||||||
|
planId,
|
||||||
|
paramCode: p.paramCode,
|
||||||
|
paramName: p.paramName,
|
||||||
|
unit: p.unit,
|
||||||
|
targetValue: (val !== null && val !== undefined && val !== '') ? Number(val) : null,
|
||||||
|
presetSrcId: srcId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[autoInitPresetParams] failed', e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async loadDetailData(row) {
|
async loadDetailData(row) {
|
||||||
if (!row.versionId) return
|
if (!row.versionId) return
|
||||||
this.detailLoading = true
|
this.detailLoading = true
|
||||||
@@ -595,7 +688,14 @@ export default {
|
|||||||
listProcessPlan({ versionId: row.versionId, pageNum: 1, pageSize: 200 }),
|
listProcessPlan({ versionId: row.versionId, pageNum: 1, pageSize: 200 }),
|
||||||
listAllProcessAnomaly({ coilId: row.coilId, versionId: row.versionId })
|
listAllProcessAnomaly({ coilId: row.coilId, versionId: row.versionId })
|
||||||
])
|
])
|
||||||
const plans = plansRes.rows || []
|
let plans = plansRes.rows || []
|
||||||
|
|
||||||
|
const added = await this.autoInitPresetParams(row, plans)
|
||||||
|
if (added) {
|
||||||
|
const refreshed = await listProcessPlan({ versionId: row.versionId, pageNum: 1, pageSize: 200 })
|
||||||
|
plans = refreshed.rows || []
|
||||||
|
}
|
||||||
|
|
||||||
const anomalyList = anomalyRes.data || []
|
const anomalyList = anomalyRes.data || []
|
||||||
const anomalyMap = {}
|
const anomalyMap = {}
|
||||||
for (const a of anomalyList) {
|
for (const a of anomalyList) {
|
||||||
@@ -910,6 +1010,54 @@ export default {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
/* ── 分段 Tab ── */
|
||||||
|
.seg-tabs { margin-top: 0; }
|
||||||
|
.seg-tab-bar {
|
||||||
|
display: inline-flex;
|
||||||
|
background: #f0f2f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 3px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.seg-tab-btn {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
transition: background .15s, color .15s, box-shadow .15s;
|
||||||
|
white-space: nowrap;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.seg-tab-btn:hover:not(.is-active) { background: #e4e7ed; color: #303133; }
|
||||||
|
.seg-tab-btn.is-active {
|
||||||
|
background: #fff;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,.1);
|
||||||
|
}
|
||||||
|
.seg-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
background: #f56c6c;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.seg-tab-pane {}
|
||||||
|
|
||||||
.param-group { margin-bottom: 20px; }
|
.param-group { margin-bottom: 20px; }
|
||||||
.param-group-hd {
|
.param-group-hd {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -918,15 +1066,8 @@ export default {
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
.pg-segment {
|
|
||||||
font-size: 11px;
|
|
||||||
color: #fff;
|
|
||||||
background: #5F7BA0;
|
|
||||||
padding: 1px 9px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
.pg-point { font-size: 13px; font-weight: 600; color: #303133; }
|
.pg-point { font-size: 13px; font-weight: 600; color: #303133; }
|
||||||
.pg-code { font-size: 11px; color: #909399; margin-left: 5px; }
|
.pg-code { font-size: 11px; color: #909399; }
|
||||||
.val-danger { color: #f56c6c; font-weight: 600; }
|
.val-danger { color: #f56c6c; font-weight: 600; }
|
||||||
.val-ok { color: #67c23a; }
|
.val-ok { color: #67c23a; }
|
||||||
.val-anomaly { color: #e6a23c; font-weight: 600; }
|
.val-anomaly { color: #e6a23c; font-weight: 600; }
|
||||||
@@ -936,6 +1077,7 @@ export default {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #c0c4cc;
|
color: #c0c4cc;
|
||||||
}
|
}
|
||||||
|
.param-group :deep(.row-anomaly td) { background: #fff8f0 !important; }
|
||||||
::v-deep .row-anomaly td { background: #fff8f8 !important; }
|
::v-deep .row-anomaly td { background: #fff8f8 !important; }
|
||||||
::v-deep .el-drawer__header { padding: 16px 20px 12px; font-size: 15px; font-weight: 600; color: #303133; margin-bottom: 0; border-bottom: 1px solid #f0f2f5; }
|
::v-deep .el-drawer__header { padding: 16px 20px 12px; font-size: 15px; font-weight: 600; color: #303133; margin-bottom: 0; border-bottom: 1px solid #f0f2f5; }
|
||||||
::v-deep .el-drawer__body { overflow: hidden; }
|
::v-deep .el-drawer__body { overflow: hidden; }
|
||||||
|
|||||||
Reference in New Issue
Block a user