Files
klp-oa/klp-ui/src/views/wms/mill/dr-plan.vue
wangyu 53a180787b 1完成酸轧轧辊调整
2完成双机架工艺规格串联
3完成双机架计划串联
4完成双机架wip快捷录入检索
5完成双机架实绩串联
2026-05-19 17:13:37 +08:00

386 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="plan-page">
<!-- 上方轧制队列 -->
<div class="queue-section">
<div class="section-header"><span>双机架轧制队列</span></div>
<el-table :data="planList" border size="mini" class="queue-table"
:row-class-name="queueRowClass"
@current-change="handleQueueSelect"
highlight-current-row
height="calc(50vh - 120px)">
<el-table-column label="序号" prop="sortNo" width="50" align="center" fixed />
<el-table-column label="入场钢卷号" prop="enterCoilNo" width="115" />
<el-table-column label="当前钢卷号" prop="currentCoilNo" width="115" />
<el-table-column label="钢卷编号" prop="inMatNo" width="110" />
<el-table-column label="合金牌号" prop="alloyNo" width="90" />
<el-table-column label="采料厚度(mm)" prop="inMatThick" width="105" align="right" />
<el-table-column label="成品厚度(mm)" prop="outThick" width="105" align="right" />
<el-table-column label="采料宽度(mm)" prop="inMatWidth" width="105" align="right" />
<el-table-column label="采料重量(t)" prop="inMatWeight" width="100" align="right" />
<el-table-column label="道次数" prop="passCount" width="65" align="center" />
<el-table-column label="生产状态" prop="prodStatus" width="85" align="center">
<template slot-scope="{ row }">
<span :class="prodStatusClass(row.prodStatus)">{{ prodStatusLabel(row.prodStatus) }}</span>
</template>
</el-table-column>
<el-table-column label="工艺方案" prop="recipeNo" min-width="100" />
</el-table>
</div>
<!-- 下方 -->
<div class="bottom-section">
<!-- 左侧绑定工艺 -->
<div class="pass-panel">
<template v-if="!selectedPlan">
<div class="section-header"><span>轧制工艺</span></div>
<div class="pass-empty">请在上方选择一条计划</div>
</template>
<template v-else-if="selectedPlan.recipeId && !passEditMode">
<div class="section-header">
<span>轧制工艺</span>
<div style="display:flex;align-items:center;gap:8px">
<span class="recipe-tag">{{ selectedPlan.recipeNo }}</span>
<el-button size="mini" icon="el-icon-edit" @click="enterEditMode">修改绑定</el-button>
</div>
</div>
<el-table :data="passList" border size="mini" class="pass-table"
:row-class-name="passRowClass" height="calc(50vh - 160px)">
<el-table-column label="道次" prop="passNo" width="45" align="center" fixed />
<el-table-column label="入口厚(mm)" prop="inThick" width="90" align="right" />
<el-table-column label="出口厚(mm)" prop="outThick" width="90" align="right" />
<el-table-column label="轧制力(kN)" prop="rollForce" width="85" align="right" />
<el-table-column label="入口张力(kN)" prop="inTension" width="90" align="right" />
<el-table-column label="出口张力(kN)" prop="outTension" width="90" align="right" />
<el-table-column label="最高速度" prop="maxSpeed" width="80" align="right" />
<el-table-column label="压下量(mm)" prop="reduction" width="85" align="right">
<template slot-scope="{ row }"><span class="calc-val">{{ row.reduction }}</span></template>
</el-table-column>
<el-table-column label="总压下量(mm)" prop="totalReduction" width="100" align="right">
<template slot-scope="{ row }"><span class="calc-val">{{ row.totalReduction }}</span></template>
</el-table-column>
</el-table>
</template>
<template v-else-if="!selectedPlan.recipeId && !passEditMode">
<div class="section-header"><span>轧制工艺</span></div>
<div class="pass-empty">
<span>该计划尚未绑定工艺参数</span>
<div style="margin-top:12px">
<el-button size="mini" type="primary" icon="el-icon-document-copy" @click="openSelectRecipe">选择已有方案</el-button>
</div>
</div>
</template>
</div>
<!-- 右侧查询 + 操作 -->
<div class="op-panel">
<div class="section-header"><span>条件查询</span></div>
<div class="query-form">
<el-form size="mini" label-width="72px">
<el-form-item label="钢卷号">
<el-input v-model="query.inMatNo" clearable placeholder="入场/当前钢卷号" />
</el-form-item>
<el-form-item label=" " label-width="72px">
<el-checkbox v-model="query.hideFinished">不显示已完成</el-checkbox>
</el-form-item>
<el-form-item label=" " label-width="72px">
<el-button size="mini" type="primary" icon="el-icon-search" @click="loadList">查询</el-button>
<el-button size="mini" icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="section-header" style="margin-top:8px"><span>队列操作</span></div>
<div class="op-buttons">
<el-button size="mini" type="primary" icon="el-icon-plus" @click="handleAdd">新增计划</el-button>
<el-button size="mini" icon="el-icon-edit" :disabled="!selectedPlan" @click="handleEdit">修改</el-button>
<el-button size="mini" icon="el-icon-document" :disabled="!selectedPlan" @click="handleFinish">完成</el-button>
<el-button size="mini" type="danger" icon="el-icon-delete" :disabled="!selectedPlan" @click="handleDelete">删除</el-button>
<el-button size="mini" icon="el-icon-top" :disabled="!selectedPlan" @click="handleMoveUp">Up 上移</el-button>
<el-button size="mini" icon="el-icon-bottom" :disabled="!selectedPlan" @click="handleMoveDown">Down 下移</el-button>
</div>
</div>
</div>
<!-- 新增/修改对话框 -->
<el-dialog :title="planDialogTitle" :visible.sync="planDialogVisible" width="680px"
:close-on-click-modal="false" append-to-body>
<el-form :model="form" :rules="rules" ref="formRef" size="mini" label-width="100px">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input v-model="form.enterCoilNo"
@keyup.enter.native="onCoilNoInput" @blur="onCoilNoInput" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当前钢卷号">
<el-input v-model="form.currentCoilNo" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合金牌号" prop="alloyNo">
<el-input v-model="form.alloyNo" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工艺方案">
<el-select v-model="form.recipeId" filterable clearable
placeholder="可后续在下方绑定" style="width:100%" @change="onRecipeChange">
<el-option v-for="r in recipeOptions" :key="r.recipeId"
:label="`${r.recipeNo}${r.alloyNo}`" :value="r.recipeId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料厚度">
<el-input v-model="form.inMatThick"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="成品厚度">
<el-input v-model="form.outThick"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料宽度">
<el-input v-model="form.inMatWidth"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料重量">
<el-input v-model="form.inMatWeight"><template slot="append">t</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料长度">
<el-input v-model="form.inMatLength"><template slot="append">m</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注">
<el-input v-model="form.remark" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer">
<el-button size="mini" @click="planDialogVisible = false">取消</el-button>
<el-button size="mini" type="primary" @click="handleSave">确定</el-button>
</div>
</el-dialog>
<!-- 选择方案对话框 -->
<el-dialog title="选择工艺方案" :visible.sync="selectRecipeVisible" width="480px"
:close-on-click-modal="false" append-to-body>
<el-select v-model="selectRecipeId" filterable clearable
placeholder="输入方案号或合金号搜索" style="width:100%">
<el-option v-for="r in recipeOptions" :key="r.recipeId"
:label="`${r.recipeNo}${r.alloyNo}${r.inThick}→${r.outThick}mm`"
:value="r.recipeId" />
</el-select>
<div slot="footer">
<el-button size="mini" @click="selectRecipeVisible = false">取消</el-button>
<el-button size="mini" type="primary" @click="confirmSelectRecipe">确定绑定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listDrPlan, addDrPlan, updateDrPlan, delDrPlan, moveUpDrPlan, moveDownDrPlan, finishDrPlan,
listDrRecipe, getDrRecipeDetail, queryCoilByNo } from '@/api/wms/drMill'
const emptyForm = () => ({
planId: null, planNo: '', enterCoilNo: '', currentCoilNo: '', inMatNo: '',
alloyNo: '', recipeId: null, recipeNo: '',
outThick: '', inMatThick: '', inMatWidth: '', inMatLength: '',
inMatWeight: '', inMatOd: '', inMatId: '', passCount: 0, remark: ''
})
export default {
name: 'DrPlan',
data() {
return {
planList: [],
selectedPlan: null,
passList: [],
recipeOptions: [],
query: { inMatNo: '', hideFinished: false },
passEditMode: false,
planDialogVisible: false,
isNew: true,
form: emptyForm(),
rules: {
enterCoilNo: [{ required: true, message: '请输入入场钢卷号', trigger: 'blur' }],
alloyNo: [{ required: true, message: '请输入合金牌号', trigger: 'blur' }],
},
selectRecipeVisible: false,
selectRecipeId: null,
}
},
computed: {
planDialogTitle() { return this.isNew ? '新增双机架计划' : '修改双机架计划' }
},
mounted() {
this.loadList()
this.refreshRecipeOptions()
},
methods: {
refreshRecipeOptions() {
listDrRecipe({}).then(res => { this.recipeOptions = res.data || [] })
},
loadList() {
const params = { inMatNo: this.query.inMatNo }
if (this.query.hideFinished) params.planStatus = '0'
listDrPlan(params).then(res => { this.planList = res.data || [] })
},
resetQuery() {
this.query = { inMatNo: '', hideFinished: false }
this.loadList()
},
handleQueueSelect(row) {
this.selectedPlan = row
this.passList = []
if (!row) return
if (row.recipeId) {
getDrRecipeDetail(row.recipeId).then(res => {
this.passList = (res.data && res.data.passList) || []
})
}
},
// 入场钢卷号变化 → 查 WMS 自动填入
onCoilNoInput() {
const v = (this.form.enterCoilNo || '').trim()
if (!v) return
this.form.inMatNo = v
queryCoilByNo(v).then(res => {
const c = res.data
if (!c) return
if (c.actualThickness && !this.form.inMatThick) this.form.inMatThick = c.actualThickness
if (c.actualWidth && !this.form.inMatWidth) this.form.inMatWidth = String(c.actualWidth)
if (c.netWeight && !this.form.inMatWeight) this.form.inMatWeight = String(c.netWeight)
if (c.length && !this.form.inMatLength) this.form.inMatLength = String(c.length)
if (c.currentCoilNo && !this.form.currentCoilNo) this.form.currentCoilNo = c.currentCoilNo
this.$message.success('已从 WMS 自动填入钢卷数据')
}).catch(() => {})
},
openSelectRecipe() {
this.selectRecipeId = null
this.selectRecipeVisible = true
},
confirmSelectRecipe() {
if (!this.selectRecipeId) { this.$message.warning('请选择方案'); return }
const r = this.recipeOptions.find(x => x.recipeId === this.selectRecipeId)
updateDrPlan({ ...this.selectedPlan, recipeId: this.selectRecipeId, recipeNo: r ? r.recipeNo : '', passCount: r ? r.passCount : 0 })
.then(() => {
this.$message.success('方案绑定成功')
this.selectRecipeVisible = false
this.selectedPlan = { ...this.selectedPlan, recipeId: this.selectRecipeId, recipeNo: r ? r.recipeNo : '' }
getDrRecipeDetail(this.selectRecipeId).then(res => {
this.passList = (res.data && res.data.passList) || []
})
this.loadList()
})
},
enterEditMode() { this.openSelectRecipe() },
onRecipeChange(recipeId) {
if (!recipeId) return
const r = this.recipeOptions.find(x => x.recipeId === recipeId)
if (r) {
if (!this.form.alloyNo) this.form.alloyNo = r.alloyNo
if (!this.form.inMatThick) this.form.inMatThick = r.inThick
if (!this.form.outThick) this.form.outThick = r.outThick
if (!this.form.inMatWidth) this.form.inMatWidth = r.outWidth
}
},
handleAdd() { this.isNew = true; this.form = emptyForm(); this.planDialogVisible = true },
handleEdit() { this.isNew = false; this.form = { ...this.selectedPlan }; this.planDialogVisible = true },
handleSave() {
this.$refs.formRef.validate(valid => {
if (!valid) return
if (this.form.recipeId) {
const r = this.recipeOptions.find(x => x.recipeId === this.form.recipeId)
if (r) { this.form.recipeNo = r.recipeNo; this.form.passCount = r.passCount }
}
const api = this.isNew ? addDrPlan : updateDrPlan
api(this.form).then(() => {
this.$message.success('保存成功')
this.planDialogVisible = false
this.loadList()
})
})
},
handleDelete() {
this.$confirm(`确定删除该计划?`, '提示', { type: 'warning' }).then(() => {
delDrPlan(this.selectedPlan.planId).then(() => {
this.$message.success('删除成功')
this.selectedPlan = null; this.passList = []
this.loadList()
})
})
},
handleFinish() {
finishDrPlan(this.selectedPlan.planId).then(() => {
this.$message.success('计划已完成')
this.loadList()
})
},
handleMoveUp() { moveUpDrPlan(this.selectedPlan.planId).then(() => this.loadList()) },
handleMoveDown() { moveDownDrPlan(this.selectedPlan.planId).then(() => this.loadList()) },
queueRowClass({ row }) {
if (row.prodStatus === 'Rolling') return 'row-rolling'
return ''
},
passRowClass({ rowIndex }) { return rowIndex % 2 === 0 ? '' : 'alt-row' },
prodStatusLabel(s) {
return { Idle: '待轧', Rolling: '轧制中', NextCoil: '下一卷', Done: '已完成', Error: '异常' }[s] || s
},
prodStatusClass(s) {
return { Idle: 'status-wait', Rolling: 'status-rolling', Done: 'status-done', Error: 'status-err' }[s] || ''
}
}
}
</script>
<style scoped lang="scss">
.plan-page { display: flex; flex-direction: column; height: calc(100vh - 84px); background: #f0f2f5; padding: 8px 12px; box-sizing: border-box; gap: 8px; }
.section-header { background: #1c2b3a; color: #ecf0f1; padding: 6px 12px; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; border-radius: 3px 3px 0 0; }
.recipe-tag { font-size: 11px; font-weight: 400; color: #a9bcd0; margin-right: 4px; }
.queue-section { background: #fff; border: 1px solid #dde1e6; border-radius: 3px; display: flex; flex-direction: column; flex-shrink: 0; }
.queue-table {
::v-deep .row-rolling td { background: #fef3e2 !important; }
::v-deep .el-table__row.current-row td { background: #e8f0fb !important; }
}
.bottom-section { display: flex; flex: 1; gap: 8px; overflow: hidden; }
.pass-panel { flex: 1; background: #fff; border: 1px solid #dde1e6; border-radius: 3px; display: flex; flex-direction: column; overflow: hidden; }
.pass-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #909399; font-size: 12px; }
.pass-table { flex: 1; ::v-deep .el-table__row.alt-row td { background: #f7f9fc !important; } }
.calc-val { font-family: 'Courier New', monospace; font-weight: 600; color: #1d4e89; }
.op-panel { width: 260px; flex-shrink: 0; background: #fff; border: 1px solid #dde1e6; border-radius: 3px; display: flex; flex-direction: column; overflow-y: auto; }
.query-form { padding: 10px 12px 4px; border-bottom: 1px solid #e4e7ed; }
.op-buttons { padding: 8px 12px; display: flex; flex-direction: column; gap: 6px; .el-button { width: 100%; justify-content: flex-start; } }
.status-wait { color: #7f8c8d; }
.status-rolling { color: #d68910; font-weight: 700; }
.status-done { color: #2471a3; }
.status-err { color: #c0392b; font-weight: 700; }
</style>