This commit is contained in:
2026-05-14 16:34:24 +08:00
47 changed files with 2779 additions and 528 deletions

View File

@@ -0,0 +1,247 @@
<template>
<div class="app-container acid-op-page">
<el-row :gutter="16">
<!-- 左侧合卷表单 -->
<el-col :span="15">
<div class="op-card">
<div class="op-header">
<span class="op-title">合卷操作</span>
<el-tag size="mini" type="info" style="margin-left:8px">actionType = 200</el-tag>
</div>
<el-form ref="form" :model="form" :rules="rules" label-width="110px" size="small">
<!-- 源卷列表动态 -->
<div class="sub-section-label">源卷列表</div>
<div v-for="(item, idx) in form.sourceCoils" :key="idx" class="source-row">
<el-form-item
:label="'源卷 ' + (idx + 1)"
:prop="'sourceCoils.' + idx + '.enterCoilNo'"
:rules="[{ required: true, message: '请输入源卷号', trigger: 'blur' }]"
style="margin-bottom:8px"
>
<el-input v-model="item.enterCoilNo" :placeholder="'入场钢卷号 ' + (idx + 1)"
clearable style="width:calc(100% - 36px)"
@keyup.enter.native="onSourceCoilInput(item)"
@blur="onSourceCoilInput(item)" />
<el-button icon="el-icon-minus" size="mini" type="danger" circle
style="margin-left:6px" :disabled="form.sourceCoils.length <= 1"
@click="removeSourceCoil(idx)" />
</el-form-item>
</div>
<el-button size="mini" type="primary" plain icon="el-icon-plus"
style="margin-left:110px;margin-bottom:12px" @click="addSourceCoil">
添加源卷
</el-button>
<el-divider content-position="left" style="margin:4px 0 12px">合卷结果</el-divider>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="合卷后卷号" prop="targetCoilNo">
<el-input v-model="form.targetCoilNo" placeholder="请输入合卷后钢卷号" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合卷后重量(t)" prop="mergedWeight">
<el-input-number v-model="form.mergedWeight" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="出口厚度(mm)" prop="exitThickness">
<el-input-number v-model="form.exitThickness" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出口宽度(mm)" prop="exitWidth">
<el-input-number v-model="form.exitWidth" :precision="1" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="班组" prop="team">
<el-select v-model="form.team" placeholder="请选择" style="width:100%" clearable>
<el-option label="甲班" value="甲" />
<el-option label="乙班" value="乙" />
<el-option label="丙班" value="丙" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注">
<el-input v-model="form.remark" placeholder="选填" clearable />
</el-form-item>
</el-col>
</el-row>
<div style="text-align:right;padding-top:4px">
<el-button @click="resetForm">重置</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm">确认合卷</el-button>
</div>
</el-form>
</div>
<!-- 历史记录 -->
<div class="op-card" style="margin-top:12px">
<div class="op-header">
<span class="op-title">最近合卷记录</span>
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadHistory">刷新</el-button>
</div>
<el-table v-loading="historyLoading" :data="historyList" size="mini" border style="width:100%">
<el-table-column prop="enterCoilNo" label="源卷号" min-width="120" show-overflow-tooltip />
<el-table-column prop="currentCoilNo" label="合卷后卷号" min-width="120" show-overflow-tooltip />
<el-table-column prop="exitThickness" label="出口厚(mm)" width="90" align="right" />
<el-table-column prop="exitWidth" label="出口宽(mm)" width="90" align="right" />
<el-table-column prop="netWeight" label="重量(t)" width="80" align="right" />
<el-table-column prop="team" label="班组" width="60" align="center" />
<el-table-column prop="createTime" label="录入时间" width="150" />
</el-table>
<pagination v-show="historyTotal > 0" :total="historyTotal"
:page.sync="historyQuery.pageNum" :limit.sync="historyQuery.pageSize"
@pagination="loadHistory" style="margin-top:6px" />
</div>
</el-col>
<!-- 右侧L2 匹配面板以第一个源卷为锚点 -->
<el-col :span="9">
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
</el-col>
</el-row>
</div>
</template>
<script>
import { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } from '@/api/wms/coilWarehouseOperationLog'
import L2MatchPanel from '../panels/L2MatchPanel.vue'
export default {
name: 'AcidMerge',
components: { L2MatchPanel },
data() {
return {
form: this.defaultForm(),
rules: {
targetCoilNo: [{ required: true, message: '合卷后卷号不能为空', trigger: 'blur' }],
},
submitting: false,
l2HotCoilId: '',
historyLoading: false,
historyList: [],
historyTotal: 0,
historyQuery: { pageNum: 1, pageSize: 10, actionType: 200 },
}
},
created() {
this.loadHistory()
},
methods: {
defaultForm() {
return {
sourceCoils: [{ enterCoilNo: '' }],
targetCoilNo: '',
mergedWeight: undefined,
exitThickness: undefined,
exitWidth: undefined,
team: undefined,
remark: '',
actionType: 200,
}
},
addSourceCoil() {
this.form.sourceCoils.push({ enterCoilNo: '' })
},
removeSourceCoil(idx) {
this.form.sourceCoils.splice(idx, 1)
// 重新推算锚点
this.l2HotCoilId = (this.form.sourceCoils[0]?.enterCoilNo || '').trim()
},
onSourceCoilInput(item) {
// 以列表第一个源卷的卷号作为 L2 查询锚点
const first = (this.form.sourceCoils[0]?.enterCoilNo || '').trim()
this.l2HotCoilId = first
},
applyL2Fill(data) {
if (data.exit_thick != null) this.form.exitThickness = parseFloat(data.exit_thick)
if (data.exit_width != null) this.form.exitWidth = parseFloat(data.exit_width)
if (data.entry_weight != null) this.form.mergedWeight = parseFloat(data.entry_weight)
this.$message.success('L2 数据已写入表单')
},
submitForm() {
this.$refs.form.validate(valid => {
if (!valid) return
this.submitting = true
// 将源卷列表序列化放入 remark 或 enterCoilNo多对一合卷用逗号分隔
const sourceNos = this.form.sourceCoils.map(s => s.enterCoilNo).filter(Boolean).join(',')
const payload = {
enterCoilNo: sourceNos,
currentCoilNo: this.form.targetCoilNo,
exitThickness: this.form.exitThickness,
exitWidth: this.form.exitWidth,
netWeight: this.form.mergedWeight,
team: this.form.team,
remark: this.form.remark,
actionType: 200,
}
addCoilWarehouseOperationLog(payload).then(() => {
this.$modal.msgSuccess('合卷操作已记录')
this.resetForm()
this.loadHistory()
}).finally(() => { this.submitting = false })
})
},
resetForm() {
this.form = this.defaultForm()
this.l2HotCoilId = ''
this.$nextTick(() => { this.$refs.form && this.$refs.form.clearValidate() })
},
loadHistory() {
this.historyLoading = true
listCoilWarehouseOperationLog(this.historyQuery).then(res => {
this.historyList = res.rows || []
this.historyTotal = res.total || 0
}).finally(() => { this.historyLoading = false })
},
}
}
</script>
<style scoped>
.acid-op-page { background: #f5f7fa; }
.op-card {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 16px;
}
.op-header {
display: flex;
align-items: center;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 2px solid #67c23a;
}
.op-title { font-size: 15px; font-weight: 600; color: #303133; }
.sub-section-label {
font-size: 12px;
color: #909399;
margin: 0 0 8px 110px;
font-weight: 500;
}
.source-row { position: relative; }
</style>

View File

@@ -0,0 +1,237 @@
<template>
<div class="app-container acid-op-page">
<el-row :gutter="16">
<!-- 左侧操作表单 + 历史记录 -->
<el-col :span="15">
<!-- 新增表单 -->
<div class="op-card">
<div class="op-header">
<span class="op-title">酸轧操作录入</span>
<el-tag size="mini" type="info" style="margin-left:8px">actionType = 11</el-tag>
</div>
<el-form ref="form" :model="form" :rules="rules" label-width="110px" size="small">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input v-model="form.enterCoilNo" placeholder="回车自动查询 L2"
clearable @keyup.enter.native="onEnterCoilInput"
@blur="onEnterCoilInput" @clear="clearL2" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出口钢卷号" prop="currentCoilNo">
<el-input v-model="form.currentCoilNo" placeholder="请输入出口钢卷号" clearable />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left" style="margin:8px 0 12px">出口实绩</el-divider>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="出口厚度(mm)" prop="exitThickness">
<el-input-number v-model="form.exitThickness" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="出口宽度(mm)" prop="exitWidth">
<el-input-number v-model="form.exitWidth" :precision="1" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="出口长度(m)" prop="exitLength">
<el-input-number v-model="form.exitLength" :precision="1" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="入口重量(t)" prop="entryWeight">
<el-input-number v-model="form.entryWeight" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="出口重量(t)" prop="exitWeight">
<el-input-number v-model="form.exitWeight" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="工艺编码" prop="processCode">
<el-input v-model="form.processCode" placeholder="—" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="班组" prop="team">
<el-select v-model="form.team" placeholder="请选择" style="width:100%" clearable>
<el-option label="甲班" value="甲" />
<el-option label="乙班" value="乙" />
<el-option label="丙班" value="丙" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item label="备注">
<el-input v-model="form.remark" placeholder="选填" clearable />
</el-form-item>
</el-col>
</el-row>
<div style="text-align:right;padding-top:4px">
<el-button @click="resetForm">重置</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm">新增录入</el-button>
</div>
</el-form>
</div>
<!-- 历史记录 -->
<div class="op-card" style="margin-top:12px">
<div class="op-header">
<span class="op-title">最近录入记录</span>
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadHistory">刷新</el-button>
</div>
<el-table v-loading="historyLoading" :data="historyList" size="mini" border style="width:100%">
<el-table-column prop="enterCoilNo" label="入场钢卷号" min-width="120" show-overflow-tooltip />
<el-table-column prop="currentCoilNo" label="出口钢卷号" min-width="120" show-overflow-tooltip />
<el-table-column prop="exitThickness" label="出口厚(mm)" width="90" align="right" />
<el-table-column prop="exitWidth" label="出口宽(mm)" width="90" align="right" />
<el-table-column prop="exitLength" label="长度(m)" width="80" align="right" />
<el-table-column prop="entryWeight" label="入口重(t)" width="80" align="right" />
<el-table-column prop="team" label="班组" width="60" align="center" />
<el-table-column prop="createTime" label="录入时间" width="150" />
</el-table>
<pagination v-show="historyTotal > 0" :total="historyTotal"
:page.sync="historyQuery.pageNum" :limit.sync="historyQuery.pageSize"
@pagination="loadHistory" style="margin-top:6px" />
</div>
</el-col>
<!-- 右侧L2 匹配面板 -->
<el-col :span="9">
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
</el-col>
</el-row>
</div>
</template>
<script>
import { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } from '@/api/wms/coilWarehouseOperationLog'
import L2MatchPanel from '../panels/L2MatchPanel.vue'
export default {
name: 'AcidNormal',
components: { L2MatchPanel },
data() {
return {
form: this.defaultForm(),
rules: {
enterCoilNo: [{ required: true, message: '入场钢卷号不能为空', trigger: 'blur' }],
},
submitting: false,
l2HotCoilId: '',
historyLoading: false,
historyList: [],
historyTotal: 0,
historyQuery: { pageNum: 1, pageSize: 10, actionType: 11 },
}
},
created() {
this.loadHistory()
},
methods: {
defaultForm() {
return {
enterCoilNo: '',
currentCoilNo: '',
exitThickness: undefined,
exitWidth: undefined,
exitLength: undefined,
entryWeight: undefined,
exitWeight: undefined,
processCode: '',
team: undefined,
remark: '',
actionType: 11,
}
},
onEnterCoilInput() {
const v = (this.form.enterCoilNo || '').trim()
this.l2HotCoilId = v || ''
},
clearL2() {
this.l2HotCoilId = ''
},
applyL2Fill(data) {
if (data.exit_thick != null) this.form.exitThickness = parseFloat(data.exit_thick)
if (data.exit_width != null) this.form.exitWidth = parseFloat(data.exit_width)
if (data.exit_length != null) this.form.exitLength = parseFloat(data.exit_length)
if (data.entry_weight != null) this.form.entryWeight = parseFloat(data.entry_weight)
if (data.process_code) this.form.processCode = data.process_code
this.$message.success('L2 数据已写入表单')
},
submitForm() {
this.$refs.form.validate(valid => {
if (!valid) return
this.submitting = true
addCoilWarehouseOperationLog({ ...this.form }).then(() => {
this.$modal.msgSuccess('录入成功')
this.resetForm()
this.loadHistory()
}).finally(() => { this.submitting = false })
})
},
resetForm() {
const coilNo = this.form.enterCoilNo
this.form = this.defaultForm()
this.$nextTick(() => {
this.$refs.form && this.$refs.form.clearValidate()
})
},
loadHistory() {
this.historyLoading = true
listCoilWarehouseOperationLog(this.historyQuery).then(res => {
this.historyList = res.rows || []
this.historyTotal = res.total || 0
}).finally(() => { this.historyLoading = false })
},
}
}
</script>
<style scoped>
.acid-op-page { background: #f5f7fa; }
.op-card {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 16px;
}
.op-header {
display: flex;
align-items: center;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 2px solid #409eff;
}
.op-title { font-size: 15px; font-weight: 600; color: #303133; }
</style>

View File

@@ -0,0 +1,280 @@
<template>
<div class="app-container acid-op-page">
<el-row :gutter="16">
<!-- 左侧分卷表单 -->
<el-col :span="15">
<div class="op-card">
<div class="op-header">
<span class="op-title">分卷操作</span>
<el-tag size="mini" type="info" style="margin-left:8px">actionType = 520</el-tag>
</div>
<el-form ref="form" :model="form" :rules="rules" label-width="110px" size="small">
<!-- 源卷 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="源卷号" prop="enterCoilNo">
<el-input v-model="form.enterCoilNo" placeholder="回车自动查询 L2" clearable
@keyup.enter.native="onEnterCoilInput"
@blur="onEnterCoilInput" @clear="clearL2" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="原卷重量(t)">
<el-input-number v-model="form.sourceWeight" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left" style="margin:4px 0 12px">子卷明细</el-divider>
<!-- 子卷列表动态 -->
<el-row v-for="(child, idx) in form.children" :key="idx"
:gutter="12" style="margin-bottom:8px" align="middle" type="flex">
<el-col :span="1" style="text-align:center;color:#909399;font-size:12px">
{{ idx + 1 }}
</el-col>
<el-col :span="10">
<el-form-item
:prop="'children.' + idx + '.coilNo'"
:rules="[{ required: true, message: '请输入子卷号', trigger: 'blur' }]"
style="margin-bottom:0"
>
<el-input v-model="child.coilNo" :placeholder="'子卷号 ' + (idx + 1)" clearable size="small" />
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item style="margin-bottom:0">
<el-input-number v-model="child.weight" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="重量(t)" size="small" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item style="margin-bottom:0">
<el-input-number v-model="child.length" :precision="1" :min="0"
:controls="false" style="width:100%" placeholder="长度(m)" size="small" />
</el-form-item>
</el-col>
<el-col :span="1">
<el-button icon="el-icon-minus" size="mini" type="danger" circle
:disabled="form.children.length <= 1"
@click="removeChild(idx)" />
</el-col>
</el-row>
<div style="display:flex;align-items:center;margin-left:12px;margin-bottom:12px;gap:12px">
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="addChild">
添加子卷
</el-button>
<span v-if="weightLeft != null" :class="['weight-hint', weightLeft < 0 ? 'over' : '']">
已分配 {{ weightAssigned.toFixed(3) }} t
<template v-if="form.sourceWeight">
/ 剩余 <b>{{ weightLeft.toFixed(3) }} t</b>
</template>
</span>
</div>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="出口厚度(mm)">
<el-input-number v-model="form.exitThickness" :precision="3" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="出口宽度(mm)">
<el-input-number v-model="form.exitWidth" :precision="1" :min="0"
:controls="false" style="width:100%" placeholder="—" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="班组">
<el-select v-model="form.team" placeholder="请选择" style="width:100%" clearable>
<el-option label="甲班" value="甲" />
<el-option label="乙班" value="乙" />
<el-option label="丙班" value="丙" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注">
<el-input v-model="form.remark" placeholder="选填" clearable />
</el-form-item>
<div style="text-align:right;padding-top:4px">
<el-button @click="resetForm">重置</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm">确认分卷</el-button>
</div>
</el-form>
</div>
<!-- 历史记录 -->
<div class="op-card" style="margin-top:12px">
<div class="op-header">
<span class="op-title">最近分卷记录</span>
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadHistory">刷新</el-button>
</div>
<el-table v-loading="historyLoading" :data="historyList" size="mini" border style="width:100%">
<el-table-column prop="enterCoilNo" label="源卷号" min-width="120" show-overflow-tooltip />
<el-table-column prop="currentCoilNo" label="子卷号" min-width="120" show-overflow-tooltip />
<el-table-column prop="exitThickness" label="出口厚(mm)" width="90" align="right" />
<el-table-column prop="exitWidth" label="出口宽(mm)" width="90" align="right" />
<el-table-column prop="netWeight" label="重量(t)" width="80" align="right" />
<el-table-column prop="team" label="班组" width="60" align="center" />
<el-table-column prop="createTime" label="录入时间" width="150" />
</el-table>
<pagination v-show="historyTotal > 0" :total="historyTotal"
:page.sync="historyQuery.pageNum" :limit.sync="historyQuery.pageSize"
@pagination="loadHistory" style="margin-top:6px" />
</div>
</el-col>
<!-- 右侧L2 匹配面板 -->
<el-col :span="9">
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
</el-col>
</el-row>
</div>
</template>
<script>
import { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } from '@/api/wms/coilWarehouseOperationLog'
import L2MatchPanel from '../panels/L2MatchPanel.vue'
export default {
name: 'AcidSplit',
components: { L2MatchPanel },
data() {
return {
form: this.defaultForm(),
rules: {
enterCoilNo: [{ required: true, message: '源卷号不能为空', trigger: 'blur' }],
},
submitting: false,
l2HotCoilId: '',
historyLoading: false,
historyList: [],
historyTotal: 0,
historyQuery: { pageNum: 1, pageSize: 10, actionType: 520 },
}
},
computed: {
weightAssigned() {
return this.form.children.reduce((s, c) => s + (c.weight || 0), 0)
},
weightLeft() {
if (!this.form.sourceWeight) return null
return this.form.sourceWeight - this.weightAssigned
},
},
created() {
this.loadHistory()
},
methods: {
defaultForm() {
return {
enterCoilNo: '',
sourceWeight: undefined,
children: [{ coilNo: '', weight: undefined, length: undefined }],
exitThickness: undefined,
exitWidth: undefined,
team: undefined,
remark: '',
actionType: 520,
}
},
addChild() {
this.form.children.push({ coilNo: '', weight: undefined, length: undefined })
},
removeChild(idx) {
this.form.children.splice(idx, 1)
},
onEnterCoilInput() {
this.l2HotCoilId = (this.form.enterCoilNo || '').trim()
},
clearL2() {
this.l2HotCoilId = ''
},
applyL2Fill(data) {
if (data.exit_thick != null) this.form.exitThickness = parseFloat(data.exit_thick)
if (data.exit_width != null) this.form.exitWidth = parseFloat(data.exit_width)
if (data.entry_weight != null) this.form.sourceWeight = parseFloat(data.entry_weight)
this.$message.success('L2 数据已写入表单')
},
submitForm() {
this.$refs.form.validate(valid => {
if (!valid) return
this.submitting = true
// 每个子卷提交一条记录
const records = this.form.children.map(child => ({
enterCoilNo: this.form.enterCoilNo,
currentCoilNo: child.coilNo,
netWeight: child.weight,
exitLength: child.length,
exitThickness: this.form.exitThickness,
exitWidth: this.form.exitWidth,
team: this.form.team,
remark: this.form.remark,
actionType: 520,
}))
// 串行提交(也可并行,视后端是否支持批量而定)
const submitAll = records.reduce((p, rec) => {
return p.then(() => addCoilWarehouseOperationLog(rec))
}, Promise.resolve())
submitAll.then(() => {
this.$modal.msgSuccess(`分卷操作已记录(共 ${records.length} 条子卷)`)
this.resetForm()
this.loadHistory()
}).finally(() => { this.submitting = false })
})
},
resetForm() {
this.form = this.defaultForm()
this.l2HotCoilId = ''
this.$nextTick(() => { this.$refs.form && this.$refs.form.clearValidate() })
},
loadHistory() {
this.historyLoading = true
listCoilWarehouseOperationLog(this.historyQuery).then(res => {
this.historyList = res.rows || []
this.historyTotal = res.total || 0
}).finally(() => { this.historyLoading = false })
},
}
}
</script>
<style scoped>
.acid-op-page { background: #f5f7fa; }
.op-card {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 16px;
}
.op-header {
display: flex;
align-items: center;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 2px solid #e6a23c;
}
.op-title { font-size: 15px; font-weight: 600; color: #303133; }
.weight-hint { font-size: 12px; color: #606266; }
.weight-hint.over { color: #f56c6c; }
.weight-hint b { color: #67c23a; }
.weight-hint.over b { color: #f56c6c; }
</style>

View File

@@ -1,123 +1,53 @@
<template>
<div class="trace-result-container">
<!-- 操作步骤基于标准化step渲染 -->
<el-card shadow="hover" class="mb20" v-if="standardSteps && standardSteps.length > 0">
<el-card shadow="hover" class="mb20" v-if="tracePanels && tracePanels.length > 0">
<div slot="header" class="card-header">
<span class="title-dot"></span>
<span class="title-text">钢卷追溯操作步骤</span>
</div>
<el-timeline v-loading="loadingCoilDetails">
<el-timeline-item v-for="(step, index) in standardSteps" :key="index"
:timestamp="`步骤 ${step.original.display_step || step.original.step}`" placement="top">
<el-card shadow="hover">
<div class="step-header">
<span class="step-action">{{ step.action }}</span>
<el-tag size="mini" :type="getStepTagType(step.action)">{{ step.original.operation || step.action
}}</el-tag>
<el-tag size="mini" type="info" v-if="step.operation">
操作人{{ step.operation }}
</el-tag>
</div>
<!-- 标准化步骤详情 - 核心展示旧钢卷/新钢卷关键信息 -->
<div class="step-details">
<!-- 核心操作信息 -->
<el-row class="detail-row" v-if="step.time">
<el-col :span="8" class="detail-label">操作时间</el-col>
<el-col :span="16" class="detail-value">{{ step.time }}</el-col>
</el-row>
<template v-for="(panel, pIdx) in tracePanels">
<!-- 线性段单条时间线 -->
<div v-if="panel.type === 'linear'" :key="'lin-' + pIdx" class="trace-panel-block">
<el-timeline v-loading="loadingCoilDetails">
<el-timeline-item v-for="(rawStep, idx) in panel.steps" :key="pIdx + '-' + idx"
:timestamp="stepTimestamp(rawStep)" placement="top">
<el-card shadow="hover">
<trace-step-body :standard-step="formatStep(rawStep)"
:get-step-tag-type="getStepTagType" :parse-changed-fields="parseChangedFields" />
</el-card>
</el-timeline-item>
</el-timeline>
</div>
<!-- 新增步骤专属显示卷号 -->
<el-row class="detail-row" v-if="step.action === '创建'">
<el-col :span="8" class="detail-label">卷号</el-col>
<el-col :span="16" class="detail-value">{{ step.original.current_coil_no }}</el-col>
</el-row>
<!-- 旧钢卷关键信息操作前- 新增优化展示核心字段hover弹窗更多信息 -->
<el-row class="detail-row" v-if="step.oldCoilInfoList && step.oldCoilInfoList.length">
<el-col :span="8" class="detail-label">操作前钢卷</el-col>
<el-col :span="16" class="detail-value">
<div class="coil-info-item" v-for="(coil, idx) in step.oldCoilInfoList" :key="idx">
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'"
:loading="loadingCoilDetails">
<div class="coil-detail-item">
<p><b>入场卷号</b>{{ coil.enterCoilNo || '-' }}</p>
<p><b>当前卷号</b>{{ coil.currentCoilNo || '-' }}</p>
<p><b>物料类型</b>{{ coil.materialType || '-' }}</p>
<p><b>物料名称</b>{{ coil.itemName || '-' }}</p>
<p><b>规格</b>{{ coil.specification || '-' }}</p>
<p><b>材质</b>{{ coil.material || '-' }}</p>
<p><b>生产厂家</b>{{ coil.manufacturer || '-' }}</p>
<p><b>净重</b>{{ coil.netWeight }} kg</p>
<p><b>逻辑库区</b>{{ coil.warehouseName || '-' }}</p>
<p><b>实际库存</b>{{ coil.actualWarehouseName || '-' }}</p>
<p><b>镀层质量</b>{{ coil.zincLayer || '-' }}</p>
<p><b>质量状态</b>{{ coil.qualityStatus || '-' }}</p>
<p><b>创建时间</b>{{ coil.createTime || '-' }}</p>
</div>
<div slot="reference" class="coil-info-summary">
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}{{ coil.specification }}- {{
coil.materialType }} - {{ coil.netWeight }}kg
</div>
</el-popover>
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除无法查询到信息</div>
</div>
</el-col>
</el-row>
<!-- 新钢卷关键信息操作后- 新增优化展示核心字段hover弹窗更多信息 -->
<el-row class="detail-row" v-if="step.newCoilInfoList && step.newCoilInfoList.length">
<el-col :span="8" class="detail-label">操作后钢卷</el-col>
<el-col :span="16" class="detail-value">
<div class="coil-info-item" v-for="(coil, idx) in step.newCoilInfoList" :key="idx">
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'"
:loading="loadingCoilDetails">
<div class="coil-detail-item">
<p><b>入场卷号</b>{{ coil.enterCoilNo || '-' }}</p>
<p><b>当前卷号</b>{{ coil.currentCoilNo || '-' }}</p>
<p><b>物料类型</b>{{ coil.materialType || '-' }}</p>
<p><b>物料名称</b>{{ coil.itemName || '-' }}</p>
<p><b>规格</b>{{ coil.specification || '-' }}</p>
<p><b>材质</b>{{ coil.material || '-' }}</p>
<p><b>生产厂家</b>{{ coil.manufacturer || '-' }}</p>
<p><b>净重</b>{{ coil.netWeight }} kg</p>
<p><b>逻辑库区</b>{{ coil.warehouseName || '-' }}</p>
<p><b>实际库存</b>{{ coil.actualWarehouseName || '-' }}</p>
<p><b>镀层质量</b>{{ coil.zincLayer || '-' }}</p>
<p><b>质量状态</b>{{ coil.qualityStatus || '-' }}</p>
<p><b>创建时间</b>{{ coil.createTime || '-' }}</p>
</div>
<div slot="reference" class="coil-info-summary">
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}{{ coil.specification }}- {{
coil.materialType }} - {{ coil.netWeight }}kg
</div>
</el-popover>
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除无法查询到信息</div>
</div>
</el-col>
</el-row>
<!-- 更新专属修改内容保留原有逻辑适配上下文 -->
<el-row class="detail-row" v-if="step.changedFields">
<el-col :span="8" class="detail-label">修改内容</el-col>
<el-col :span="16" class="detail-value">
<div class="changed-field-item" v-for="(item, idx) in parseChangedFields(step.changedFields)"
:key="idx">
{{ item.field }}<span class="old-value">{{ item.oldVal }}</span> <span class="new-value">{{
item.newVal }}</span>
</div>
</el-col>
</el-row>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
<!-- 合卷段多列并排 + 合卷汇聚 -->
<div v-else-if="panel.type === 'merge_join'" :key="'mj-' + pIdx" class="trace-panel-block merge-join-block">
<div class="merge-caption">合卷前各卷加工过程并排展示</div>
<el-row :gutter="12" type="flex" class="merge-lanes-row">
<el-col v-for="(lane, li) in panel.lanes" :key="li" :span="laneColSpan(panel.lanes.length)" class="merge-lane-col">
<div class="lane-header-tag">
{{ (panel.laneLabels && panel.laneLabels[li]) || ('来源 ' + (li + 1)) }}
</div>
<div class="lane-steps-stack">
<el-card v-for="(rawStep, si) in lane" :key="si" shadow="hover" class="lane-step-card mb10">
<trace-step-body :standard-step="formatStep(rawStep)"
:get-step-tag-type="getStepTagType" :parse-changed-fields="parseChangedFields" compact />
</el-card>
</div>
</el-col>
</el-row>
<div class="merge-converge">
<el-divider content-position="center">合卷汇聚</el-divider>
<el-card shadow="hover">
<trace-step-body :standard-step="formatStep(panel.mergeStep)"
:get-step-tag-type="getStepTagType" :parse-changed-fields="parseChangedFields" />
</el-card>
</div>
</div>
</template>
</el-card>
<!-- 无记录提示 -->
<div class="empty-tip" v-if="!standardSteps || standardSteps.length === 0">
<div class="empty-tip" v-if="!tracePanels || tracePanels.length === 0">
<el-empty description="未找到相关钢卷记录"></el-empty>
</div>
</div>
@@ -125,9 +55,11 @@
<script>
import { listMaterialCoil } from '@/api/wms/coil';
import TraceStepBody from './TraceStepBody.vue';
export default {
name: 'CoilTraceResult',
components: { TraceStepBody },
props: {
// 追溯结果数据
traceResult: {
@@ -144,21 +76,28 @@ export default {
};
},
computed: {
// 生成标准化步骤列表(核心基于原始steps转换新增旧/新钢卷信息列表
// 生成标准化步骤列表(用于批量拉取钢卷详情等
standardSteps() {
if (!this.traceResult || !this.traceResult.steps || this.traceResult.steps.length === 0) {
return [];
}
// 遍历原始步骤,转换为标准化步骤(包含旧/新钢卷关键信息)
return this.traceResult.steps.map(step => this.formatStep(step));
}
},
/** 后端拆好的线性段 / 合卷并排段;无 traceLayout 时退化为单条线性 */
tracePanels() {
if (this.traceResult && Array.isArray(this.traceResult.traceLayout) && this.traceResult.traceLayout.length) {
return this.traceResult.traceLayout;
}
if (this.traceResult && this.traceResult.steps && this.traceResult.steps.length) {
return [{ type: 'linear', steps: this.traceResult.steps }];
}
return [];
},
},
watch: {
traceResult: {
handler(newVal) {
if (newVal) {
console.log('追溯结果更新,已生成标准化步骤:', this.standardSteps);
// 步骤更新后,防抖获取钢卷详情(避免频繁请求)
this.debounceFetchCoilDetails();
} else {
// 无追溯结果时,清空缓存
@@ -169,6 +108,14 @@ export default {
}
},
methods: {
stepTimestamp(rawStep) {
const d = rawStep.display_step != null ? rawStep.display_step : rawStep.step;
return `步骤 ${d}`;
},
laneColSpan(laneCount) {
const n = Math.max(1, laneCount || 1);
return Math.max(1, Math.floor(24 / n));
},
// 根据操作类型获取标签样式
getStepTagType(action) {
const typeMap = {
@@ -295,7 +242,6 @@ export default {
standardStep.newCoilIds = originalStep.current_coil_id ? [originalStep.current_coil_id.trim()] : [];
break;
case '更新':
console.log('更新操作后钢卷ID:', originalStep.new_coil_id, originalStep);
standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : [];
break;
case '分卷':
@@ -417,7 +363,6 @@ export default {
step.restoredCoilInfo = step.newCoilInfoList[0] || null;
}
});
console.log('更新后的标准化步骤:', this.standardSteps);
} else {
this.$message.warning('获取钢卷详情失败');
}
@@ -466,6 +411,59 @@ export default {
margin-bottom: 20px;
}
.trace-panel-block {
margin-bottom: 24px;
}
.trace-panel-block:last-child {
margin-bottom: 0;
}
.merge-join-block {
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 16px;
background: #fafbfc;
}
.merge-caption {
font-size: 14px;
color: #606266;
margin-bottom: 12px;
font-weight: 500;
}
.merge-lanes-row {
align-items: flex-start;
}
.merge-lane-col {
min-width: 0;
}
.lane-header-tag {
font-size: 13px;
font-weight: 600;
color: #409eff;
margin-bottom: 8px;
padding: 6px 10px;
background: #ecf5ff;
border-radius: 4px;
word-break: break-all;
}
.lane-steps-stack {
min-height: 40px;
}
.lane-step-card.mb10 {
margin-bottom: 10px;
}
.merge-converge {
margin-top: 16px;
}
/* 操作步骤样式增强 */
.step-header {
display: flex;

View File

@@ -0,0 +1,188 @@
<template>
<div class="l2-panel">
<!-- 最佳匹配 -->
<div class="panel-block">
<div class="pb-header">
<span class="pb-title">最佳匹配</span>
<el-tag v-if="bestMatch" size="mini" type="success" style="margin-left:6px">热卷号已找到</el-tag>
<el-tag v-else-if="matchLoading" size="mini" type="info" style="margin-left:6px">查询中</el-tag>
<el-tag v-else-if="hotCoilId" size="mini" type="warning" style="margin-left:6px">未找到</el-tag>
<el-tag v-else size="mini" style="margin-left:6px">待输入</el-tag>
</div>
<template v-if="bestMatch">
<div class="match-grid">
<div class="mg-item" v-for="f in PLAN_FIELDS" :key="f.key">
<span class="mg-label">{{ f.label }}</span>
<span class="mg-value">{{ formatPlanField(f.key, bestMatch[f.key]) }}</span>
</div>
</div>
<el-button type="primary" size="small" style="width:100%;margin-top:10px"
icon="el-icon-download" @click="$emit('fill', bestMatch)">
写入表单
</el-button>
</template>
<div v-else-if="matchLoading" class="pb-empty"><i class="el-icon-loading" /></div>
<div v-else class="pb-empty">{{ hotCoilId ? '未找到匹配的 L2 计划记录' : '输入入场钢卷号后自动查询' }}</div>
</div>
<!-- 最近20条 -->
<div class="panel-block" style="margin-top:12px">
<div class="pb-header">
<span class="pb-title">最近 20 条生产记录</span>
<el-button type="text" size="mini" icon="el-icon-refresh"
style="margin-left:auto" :loading="recentLoading" @click="loadRecentList">刷新</el-button>
</div>
<el-table :data="recentList" size="mini" v-loading="recentLoading"
max-height="400" border style="width:100%">
<el-table-column label="热卷号" min-width="110" show-overflow-tooltip>
<template slot-scope="{ row }">
<span :class="(row.hot_coilid || row.encoilid) === hotCoilId ? 'hot-match' : ''">
{{ row.hot_coilid || row.encoilid || '—' }}
</span>
</template>
</el-table-column>
<el-table-column label="出口厚" width="72" align="right">
<template slot-scope="{ row }">{{ row.exit_thick != null ? row.exit_thick : '—' }}</template>
</el-table-column>
<el-table-column label="出口宽" width="72" align="right">
<template slot-scope="{ row }">{{ row.exit_width != null ? row.exit_width : '—' }}</template>
</el-table-column>
<el-table-column label="长度(m)" width="76" align="right">
<template slot-scope="{ row }">{{ row.exit_length != null ? row.exit_length : '—' }}</template>
</el-table-column>
<el-table-column label="重量(t)" width="72" align="right">
<template slot-scope="{ row }">{{ row.entry_weight != null ? row.entry_weight : '—' }}</template>
</el-table-column>
<el-table-column label="" width="52" fixed="right" align="center">
<template slot-scope="{ row }">
<el-button type="text" size="mini" @click="$emit('fill', row)">写入</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import { getTimingPlanDetailByHotcoilId, getExcoilList } from '@/api/l2/timing'
const PLAN_FIELDS = [
{ key: 'hot_coilid', label: '热卷号' },
{ key: 'status', label: '状态' },
{ key: 'process_code', label: '工艺编码' },
{ key: 'entry_thick', label: '入口厚(mm)' },
{ key: 'entry_width', label: '入口宽(mm)' },
{ key: 'entry_weight', label: '入口重量(t)' },
{ key: 'exit_thick', label: '出口厚(mm)' },
{ key: 'exit_width', label: '出口宽(mm)' },
{ key: 'exit_length', label: '出口长度(m)' },
{ key: 'park_type', label: '包装要求' },
{ key: 'trimming', label: '切边要求' },
]
export default {
name: 'L2MatchPanel',
props: {
hotCoilId: { type: String, default: '' }
},
data() {
return {
PLAN_FIELDS,
bestMatch: null,
matchLoading: false,
recentList: [],
recentLoading: false,
}
},
watch: {
hotCoilId(val) {
if (val) this.loadBestMatch(val)
else this.bestMatch = null
}
},
created() {
this.loadRecentList()
if (this.hotCoilId) this.loadBestMatch(this.hotCoilId)
},
methods: {
loadBestMatch(hotCoilId) {
this.bestMatch = null
this.matchLoading = true
getTimingPlanDetailByHotcoilId(hotCoilId).then(res => {
const fr = res?.data?.firstRow
this.bestMatch = (fr && Object.keys(fr).length > 0) ? fr : null
}).catch(() => {}).finally(() => { this.matchLoading = false })
},
loadRecentList() {
this.recentLoading = true
getExcoilList(1, 20).then(res => {
const rows = res?.data?.rows || []
this.recentList = rows.map(row => this.normalizeExcoilRow(row))
}).catch(() => {}).finally(() => { this.recentLoading = false })
},
formatPlanField(key, val) {
if (val == null) return '—'
if (key === 'trimming') {
if (val === 1 || val === '1') return '净边料'
if (val === 0 || val === '0') return '毛边料'
}
return val
},
normalizeExcoilRow(row) {
const g = k => row[k] != null ? row[k] : (row[k.toUpperCase()] != null ? row[k.toUpperCase()] : null)
return {
hot_coilid: g('hot_coilid'),
encoilid: g('encoilid'),
excoilid: g('excoilid'),
exit_thick: g('exit_thick'),
exit_width: g('exit_width'),
exit_length: g('exit_length'),
entry_weight: g('used_entry_weight') != null ? g('used_entry_weight')
: (g('meas_exit_weight') != null ? g('meas_exit_weight') : g('entry_weight')),
}
}
}
}
</script>
<style scoped>
.l2-panel { font-size: 13px; }
.panel-block {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 12px;
}
.pb-header {
display: flex;
align-items: center;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f2f5;
}
.pb-title { font-weight: 600; color: #303133; font-size: 13px; }
.match-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px 12px;
}
.mg-item { display: flex; flex-direction: column; }
.mg-label { font-size: 11px; color: #909399; }
.mg-value { font-size: 13px; color: #303133; font-weight: 500; }
.pb-empty {
text-align: center;
color: #c0c4cc;
padding: 20px 0;
font-size: 12px;
}
.hot-match { color: #67c23a; font-weight: 600; }
</style>

View File

@@ -0,0 +1,196 @@
<template>
<div :class="compact ? 'step-details step-details-compact' : 'step-details'">
<div class="step-header">
<span class="step-action">{{ standardStep.action }}</span>
<el-tag size="mini" :type="getStepTagType(standardStep.action)">
{{ standardStep.original.operation || standardStep.action }}
</el-tag>
<el-tag size="mini" type="info" v-if="standardStep.operation">
操作人{{ standardStep.operation }}
</el-tag>
</div>
<el-row class="detail-row" v-if="standardStep.time && !compact">
<el-col :span="8" class="detail-label">操作时间</el-col>
<el-col :span="16" class="detail-value">{{ standardStep.time }}</el-col>
</el-row>
<el-row class="detail-row" v-if="compact && standardStep.time">
<el-col :span="24" class="detail-value text-muted">{{ standardStep.time }}</el-col>
</el-row>
<el-row class="detail-row" v-if="standardStep.action === '创建'">
<el-col :span="compact ? 24 : 8" class="detail-label">卷号</el-col>
<el-col :span="compact ? 24 : 16" class="detail-value">{{ standardStep.original.current_coil_no }}</el-col>
</el-row>
<el-row class="detail-row" v-if="standardStep.oldCoilInfoList && standardStep.oldCoilInfoList.length">
<el-col :span="compact ? 24 : 8" class="detail-label">操作前钢卷</el-col>
<el-col :span="compact ? 24 : 16" class="detail-value">
<div class="coil-info-item" v-for="(coil, idx) in standardStep.oldCoilInfoList" :key="idx">
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'">
<div class="coil-detail-item">
<p><b>入场卷号</b>{{ coil.enterCoilNo || '-' }}</p>
<p><b>当前卷号</b>{{ coil.currentCoilNo || '-' }}</p>
<p><b>物料类型</b>{{ coil.materialType || '-' }}</p>
<p><b>物料名称</b>{{ coil.itemName || '-' }}</p>
<p><b>规格</b>{{ coil.specification || '-' }}</p>
<p><b>材质</b>{{ coil.material || '-' }}</p>
<p><b>生产厂家</b>{{ coil.manufacturer || '-' }}</p>
<p><b>净重</b>{{ coil.netWeight }} kg</p>
<p><b>逻辑库区</b>{{ coil.warehouseName || '-' }}</p>
<p><b>实际库存</b>{{ coil.actualWarehouseName || '-' }}</p>
<p><b>镀层质量</b>{{ coil.zincLayer || '-' }}</p>
<p><b>质量状态</b>{{ coil.qualityStatus || '-' }}</p>
<p><b>创建时间</b>{{ coil.createTime || '-' }}</p>
</div>
<div slot="reference" class="coil-info-summary">
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}{{ coil.specification }}-{{ coil.materialType }}-{{ coil.netWeight }}kg
</div>
</el-popover>
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除无法查询到信息</div>
</div>
</el-col>
</el-row>
<el-row class="detail-row" v-if="standardStep.newCoilInfoList && standardStep.newCoilInfoList.length">
<el-col :span="compact ? 24 : 8" class="detail-label">操作后钢卷</el-col>
<el-col :span="compact ? 24 : 16" class="detail-value">
<div class="coil-info-item" v-for="(coil, idx) in standardStep.newCoilInfoList" :key="idx">
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'">
<div class="coil-detail-item">
<p><b>入场卷号</b>{{ coil.enterCoilNo || '-' }}</p>
<p><b>当前卷号</b>{{ coil.currentCoilNo || '-' }}</p>
<p><b>物料类型</b>{{ coil.materialType || '-' }}</p>
<p><b>物料名称</b>{{ coil.itemName || '-' }}</p>
<p><b>规格</b>{{ coil.specification || '-' }}</p>
<p><b>材质</b>{{ coil.material || '-' }}</p>
<p><b>生产厂家</b>{{ coil.manufacturer || '-' }}</p>
<p><b>净重</b>{{ coil.netWeight }} kg</p>
<p><b>逻辑库区</b>{{ coil.warehouseName || '-' }}</p>
<p><b>实际库存</b>{{ coil.actualWarehouseName || '-' }}</p>
<p><b>镀层质量</b>{{ coil.zincLayer || '-' }}</p>
<p><b>质量状态</b>{{ coil.qualityStatus || '-' }}</p>
<p><b>创建时间</b>{{ coil.createTime || '-' }}</p>
</div>
<div slot="reference" class="coil-info-summary">
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}{{ coil.specification }}-{{ coil.materialType }}-{{ coil.netWeight }}kg
</div>
</el-popover>
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除无法查询到信息</div>
</div>
</el-col>
</el-row>
<el-row class="detail-row" v-if="standardStep.changedFields">
<el-col :span="compact ? 24 : 8" class="detail-label">修改内容</el-col>
<el-col :span="compact ? 24 : 16" class="detail-value">
<div class="changed-field-item" v-for="(item, idx) in parseChangedFields(standardStep.changedFields)" :key="idx">
{{ item.field }}<span class="old-value">{{ item.oldVal }}</span> <span class="new-value">{{ item.newVal }}</span>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'TraceStepBody',
props: {
standardStep: { type: Object, required: true },
getStepTagType: { type: Function, required: true },
parseChangedFields: { type: Function, required: true },
compact: { type: Boolean, default: false },
},
};
</script>
<style scoped>
.step-header {
display: flex;
align-items: center;
margin-bottom: 12px;
flex-wrap: wrap;
gap: 8px;
}
.step-action {
font-size: 15px;
font-weight: 600;
color: #333;
margin-right: 8px;
}
.step-details {
padding: 10px 0;
}
.step-details-compact .step-header {
margin-bottom: 8px;
}
.detail-row {
margin-bottom: 12px;
align-items: flex-start;
}
.detail-label {
color: #666;
font-size: 14px;
padding-top: 4px;
}
.detail-value {
color: #333;
font-size: 14px;
}
.text-muted {
color: #909399;
font-size: 13px;
}
.changed-field-item {
margin-bottom: 4px;
line-height: 1.5;
}
.old-value {
color: #F56C6C;
}
.new-value {
color: #67C23A;
}
.coil-detail-item {
font-size: 13px;
line-height: 1.8;
color: #333;
}
.coil-detail-item p {
margin: 0;
padding: 2px 0;
}
.coil-detail-item p b {
color: #666;
min-width: 80px;
}
.coil-info-item {
margin-bottom: 8px;
display: inline-block;
margin-right: 12px;
}
.coil-info-summary {
cursor: pointer;
color: #409EFF;
background: #f5faff;
padding: 4px 8px;
border-radius: 4px;
border: 1px solid #e6f7ff;
white-space: nowrap;
}
.coil-info-deleted {
color: #F56C6C;
background: #fff5f5;
}
</style>

View File

@@ -26,25 +26,9 @@
<!-- 主内容区 - 左右布局 -->
<div class="content-wrapper">
<!-- 左侧酸轧信息推荐 -->
<!-- 左侧L2 匹配面板 -->
<div>
<!-- 酸连轧最近10条记录展示 -->
<el-card class="recent-records-card" v-if="acidRecentRecords && acidRecentRecords.length > 0">
<div slot="header" class="card-header">
<span><i class="el-icon-time"></i> 酸连轧最近记录</span>
</div>
<el-table :data="acidRecentRecords" stripe size="small" @row-click="handleClickRecord">
<el-table-column prop="currentCoilNo" label="加工前卷号" show-overflow-tooltip></el-table-column>
<el-table-column prop="excoilId" label="出口卷号" show-overflow-tooltip></el-table-column>
<el-table-column prop="exitWeight" label="出口重量(t)" width="100">
<template slot-scope="scope">
{{ scope.row.exitWeight ? scope.row.exitWeight + ' t' : '—' }}
</template>
</el-table-column>
<el-table-column prop="team" label="班组" width="80"></el-table-column>
</el-table>
</el-card>
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
</div>
<!-- 右侧更新表单 -->
<div>
@@ -138,35 +122,6 @@
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="质量状态" prop="qualityStatus">
<el-select v-model="updateForm.qualityStatus" placeholder="请选择质量状态" style="width: 100%">
<el-option v-for="item in dict.type.coil_quality_status" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="切边要求" prop="trimmingRequirement">
<el-select v-model="updateForm.trimmingRequirement" placeholder="请选择切边要求" style="width: 100%">
<el-option label="净边料" value="净边料" />
<el-option label="毛边料" value="毛边料" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="包装要求" prop="packagingRequirement">
<el-select v-model="updateForm.packagingRequirement" placeholder="请选择包装要求" style="width: 100%">
<el-option label="裸包" value="裸包" />
<el-option label="普包" value="普包" />
<el-option label="简包" value="简包" />
<el-option label="精包" value="精包" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="毛重(t)" prop="grossWeight">
@@ -361,8 +316,6 @@
<script>
import { getMaterialCoil, updateMaterialCoil, getFirstHeatCoilMaterial } from '@/api/wms/coil';
import { completeAction, getPendingAction } from '@/api/wms/pendingAction';
// import { getTimingPlanList } from '@/api/l2/timing'
import { getAcidTypingPrefill } from '@/api/pocket/acidTyping';
import { saveCoilCache, getCoilCacheByCoilId, delCoilCache } from '@/api/wms/coilCache';
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect";
import RawMaterialSelect from "@/components/KLPService/RawMaterialSelect";
@@ -373,6 +326,7 @@ import AbnormalForm from './components/AbnormalForm';
import { generateCoilNoPrefix } from "@/utils/coil/coilNo";
import { addCoilContractRel } from "@/api/wms/coilContractRel";
import ContractSelect from "@/components/KLPService/ContractSelect";
import L2MatchPanel from './panels/L2MatchPanel.vue';
export default {
@@ -384,7 +338,8 @@ export default {
WarehouseSelect,
TimeInput,
AbnormalForm,
ContractSelect
ContractSelect,
L2MatchPanel,
},
dicts: ['coil_quality_status', 'coil_abnormal_position', 'coil_abnormal_code', 'coil_abnormal_degree', 'coil_business_purpose'],
data() {
@@ -479,9 +434,6 @@ export default {
type: 'info',
title: ''
},
isAcidRolling: false,
// 酸连轧最近记录
acidRecentRecords: [],
// 异常信息
abnormals: [],
// 异常表单弹窗
@@ -507,6 +459,9 @@ export default {
};
},
computed: {
l2HotCoilId() {
return (this.currentInfo && this.currentInfo.enterCoilNo) || ''
},
// 动态显示标签
getItemLabel() {
if (this.updateForm.materialType === '成品') {
@@ -530,98 +485,13 @@ export default {
// 从路由参数获取coilId和actionId
const coilId = this.$route.query.coilId;
const actionId = this.$route.query.actionId;
let actionType = ''
const pendingActionRes = await getPendingAction(actionId)
actionType = pendingActionRes.data.actionType
// 填写生产开始时间
this.$set(this.updateForm, 'productionStartTime', pendingActionRes.data.createTime)
this.isAcidRolling = (actionType == 11)
if (this.isAcidRolling) {
this.acidPrefill.visible = true
this.acidPrefill.type = 'info'
this.acidPrefill.title = '正在结合酸轧二级系统自动填写部分信息...'
}
if (coilId) {
await this.loadCoilInfo(coilId);
if (this.isAcidRolling) {
const currentCoilNo = this.currentInfo && this.currentInfo.currentCoilNo
if (!currentCoilNo) {
this.acidPrefill.type = 'warning'
this.acidPrefill.title = '当前钢卷号为空'
} else {
try {
const prefillRes = await getAcidTypingPrefill(currentCoilNo)
const prefill = prefillRes && prefillRes.data
if (!prefill || prefill.length === 0) {
this.acidPrefill.type = 'info'
this.acidPrefill.title = '未在二级系统中查找到对应信息,请自行填写'
} else {
// 处理返回的列表数据
if (Array.isArray(prefill)) {
this.acidRecentRecords = prefill;
// 使用第一条记录填充表单(保持原有逻辑)
const firstRecord = prefill[0];
if (firstRecord) {
if (firstRecord.exitWeight != null && firstRecord.exitWeight !== '') {
const w = Number(firstRecord.exitWeight)
if (!Number.isNaN(w)) {
this.$set(this.updateForm, 'grossWeight', w)
this.$set(this.updateForm, 'netWeight', w)
}
}
if (firstRecord.exitLength != null && firstRecord.exitLength !== '') {
const len = Number(firstRecord.exitLength)
if (!Number.isNaN(len)) {
this.$set(this.updateForm, 'length', len)
}
}
if (firstRecord.team) {
this.$set(this.updateForm, 'team', firstRecord.team)
}
}
} else {
// 为了兼容旧版本的单个对象返回格式
if (prefill.exitWeight != null && prefill.exitWeight !== '') {
const w = Number(prefill.exitWeight)
if (!Number.isNaN(w)) {
this.$set(this.updateForm, 'grossWeight', w)
this.$set(this.updateForm, 'netWeight', w)
}
}
if (prefill.exitLength != null && prefill.exitLength !== '') {
const len = Number(prefill.exitLength)
if (!Number.isNaN(len)) {
this.$set(this.updateForm, 'length', len)
}
}
if (prefill.team) {
this.$set(this.updateForm, 'team', prefill.team)
}
}
this.acidPrefill.type = 'success'
this.acidPrefill.title = '已结合酸轧二级系统完成部分信息填写'
console.log('[typing] acid rolling prefill applied:', prefill)
}
} catch (e) {
console.error('[typing] acid rolling prefill request failed:', e)
this.acidPrefill.type = 'error'
this.acidPrefill.title = '未在二级系统中查找到对应信息,故自动填写失败'
}
}
}
}
const currentCoilNoPrefix = generateCoilNoPrefix()
@@ -653,6 +523,36 @@ export default {
}
},
methods: {
applyL2Fill(data) {
if (data.entry_weight != null) {
const w = parseFloat(data.entry_weight)
if (!Number.isNaN(w)) {
this.$set(this.updateForm, 'grossWeight', w)
this.$set(this.updateForm, 'netWeight', w)
}
}
if (data.exit_length != null) {
const len = parseFloat(data.exit_length)
if (!Number.isNaN(len)) {
this.$set(this.updateForm, 'length', len)
this.$set(this.updateForm, 'actualLength', len)
}
}
if (data.exit_thick != null) this.$set(this.updateForm, 'actualThickness', parseFloat(data.exit_thick))
if (data.exit_width != null) this.$set(this.updateForm, 'actualWidth', parseFloat(data.exit_width))
// 包装要求
if (data.park_type != null && data.park_type !== '') {
this.$set(this.updateForm, 'packagingRequirement', data.park_type)
}
// 切边要求1=净边料0=毛边料
if (data.trimming != null) {
const t = String(data.trimming)
if (t === '1') this.$set(this.updateForm, 'trimmingRequirement', '净边料')
else if (t === '0') this.$set(this.updateForm, 'trimmingRequirement', '毛边料')
}
this.$message.success('L2 数据已写入表单')
},
// 处理材料类型变化
handleMaterialTypeChange(value) {
// 清空物品选择
@@ -667,17 +567,6 @@ export default {
}
},
handleClickRecord(row) {
this.updateForm = {
...this.updateForm,
currentCoilNo: row.excoilId,
team: row.team,
netWeight: row.exitWeight,
grossWeight: row.exitWeight,
length: row.exitLength,
}
},
// 加载钢卷信息
async loadCoilInfo(coilId) {
try {
@@ -1050,7 +939,7 @@ export default {
.content-wrapper {
display: grid;
margin-top: 10px;
grid-template-columns: 600px 1fr;
grid-template-columns: 340px 1fr;
gap: 10px;
align-items: stretch; // 改为stretch让子元素高度一致
}

View File

@@ -178,75 +178,78 @@
</div>
</div>
<!-- 参数对比表 -->
<div v-if="!detailLoading && detailParams.length === 0" class="detail-empty">
暂无参数数据
</div>
<template v-else>
<div
v-for="group in detailParamGroups"
:key="group.planId"
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"
<!-- 参数分段 Tab -->
<div class="seg-tabs">
<div class="seg-tab-bar">
<button
v-for="seg in detailSegmentTabs"
:key="seg.segmentType"
:class="['seg-tab-btn', { 'is-active': detailActiveTab === seg.segmentType }]"
@click="detailActiveTab = seg.segmentType"
>
<el-table-column label="参数名称" prop="paramName" min-width="120" show-overflow-tooltip />
<el-table-column label="编码" prop="paramCode" width="90" show-overflow-tooltip />
<el-table-column label="单位" prop="unit" width="56" 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="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>
{{ seg.segmentLabel }}
<span v-if="seg.anomalyCount > 0" class="seg-badge">{{ seg.anomalyCount }}</span>
</button>
</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>
</el-drawer>
@@ -391,9 +394,41 @@
import { listProcessCoilRecord, batchRebindCoilRecord } from '@/api/wms/processCoilRecord'
import { listProcessSpec } from '@/api/wms/processSpec'
import { listProcessSpecVersion } from '@/api/wms/processSpecVersion'
import { listProcessPlan } from '@/api/wms/processPlan'
import { listProcessPlanParam } from '@/api/wms/processPlanParam'
import { listProcessPlan, addProcessPlan } from '@/api/wms/processPlan'
import { listProcessPlanParam, addProcessPlanParam } from '@/api/wms/processPlanParam'
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 = [
{ value: 'DRAFT', label: '草稿' },
@@ -422,6 +457,7 @@ export default {
detailLoading: false,
detailRow: null,
detailParams: [],
detailActiveTab: 'INLET',
bindDialogVisible: false,
removeOldRecord: false,
@@ -456,6 +492,23 @@ export default {
}
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() {
if (!this.selected.length) return false
@@ -580,6 +633,7 @@ export default {
openDetail(row) {
this.detailRow = row
this.detailParams = []
this.detailActiveTab = 'INLET'
this.detailDrawerVisible = true
this.loadDetailData(row)
},
@@ -587,6 +641,45 @@ export default {
this.detailRow = null
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) {
if (!row.versionId) return
this.detailLoading = true
@@ -595,7 +688,14 @@ export default {
listProcessPlan({ versionId: row.versionId, pageNum: 1, pageSize: 200 }),
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 anomalyMap = {}
for (const a of anomalyList) {
@@ -910,6 +1010,54 @@ export default {
min-width: 0;
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-hd {
display: flex;
@@ -918,15 +1066,8 @@ export default {
margin-bottom: 6px;
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-code { font-size: 11px; color: #909399; margin-left: 5px; }
.pg-code { font-size: 11px; color: #909399; }
.val-danger { color: #f56c6c; font-weight: 600; }
.val-ok { color: #67c23a; }
.val-anomaly { color: #e6a23c; font-weight: 600; }
@@ -936,6 +1077,7 @@ export default {
font-size: 13px;
color: #c0c4cc;
}
.param-group :deep(.row-anomaly td) { background: #fff8f0 !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__body { overflow: hidden; }