1完成酸轧轧辊调整
2完成双机架工艺规格串联 3完成双机架计划串联 4完成双机架wip快捷录入检索 5完成双机架实绩串联
This commit is contained in:
468
klp-ui/src/views/micro/pages/dr/components/Actual.vue
Normal file
468
klp-ui/src/views/micro/pages/dr/components/Actual.vue
Normal file
@@ -0,0 +1,468 @@
|
||||
<template>
|
||||
<div class="actual-page">
|
||||
<div class="body-wrap">
|
||||
|
||||
<!-- 实绩表格 -->
|
||||
<div class="table-card">
|
||||
<div class="card-header">
|
||||
<span class="card-title">双机架实绩</span>
|
||||
<el-radio-group v-model="query.planStatus" size="mini" @change="onStatusChange">
|
||||
<el-radio-button label="">全部</el-radio-button>
|
||||
<el-radio-button label="0">准备</el-radio-button>
|
||||
<el-radio-button label="1">上线</el-radio-button>
|
||||
<el-radio-button label="2">生产中</el-radio-button>
|
||||
<el-radio-button label="3">生产完成</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-button size="mini" icon="el-icon-refresh" style="margin-left:8px" @click="loadList">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
size="mini"
|
||||
border
|
||||
highlight-current-row
|
||||
:height="tableHeight"
|
||||
style="width:100%"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<el-table-column type="index" width="40" align="center" />
|
||||
<el-table-column label="计划号" prop="planNo" width="150" show-overflow-tooltip />
|
||||
<el-table-column label="入场钢卷号" prop="enterCoilNo" min-width="120" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ row.enterCoilNo || row.inMatNo || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出口钢卷号" prop="currentCoilNo" min-width="120" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ row.currentCoilNo || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合金牌号" prop="alloyNo" width="100" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ row.alloyNo || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计划状态" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag :type="planStatusType(row.planStatus)" size="mini">
|
||||
{{ planStatusLabel(row.planStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生产状态" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag :type="prodStatusType(row.prodStatus)" size="mini" effect="plain">
|
||||
{{ row.prodStatus || '—' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="来料厚(mm)" width="82" align="right">
|
||||
<template slot-scope="{ row }">{{ row.inMatThick || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="来料宽(mm)" width="82" align="right">
|
||||
<template slot-scope="{ row }">{{ row.inMatWidth || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="来料重(t)" width="76" align="right">
|
||||
<template slot-scope="{ row }">{{ row.inMatWeight || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="成品厚(mm)" width="82" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span class="accent-val">{{ row.outThick || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="道次数" prop="passCount" width="60" align="center">
|
||||
<template slot-scope="{ row }">{{ row.passCount || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工艺方案" prop="recipeNo" width="110" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ row.recipeNo || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" width="148">
|
||||
<template slot-scope="{ row }">{{ formatDate(row.createTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建人" prop="createBy" width="80" align="center">
|
||||
<template slot-scope="{ row }">{{ row.createBy || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" min-width="100" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ row.remark || '—' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="table-footer">
|
||||
<el-pagination
|
||||
small
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
:total="total"
|
||||
:page-size="query.pageSize"
|
||||
:current-page="query.pageNum"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧面板 -->
|
||||
<div class="side-panel">
|
||||
|
||||
<!-- 查找 -->
|
||||
<div class="panel-block">
|
||||
<div class="panel-title">查找</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<span class="filter-label">钢卷号</span>
|
||||
<el-input
|
||||
v-model="query.inMatNo"
|
||||
size="mini" clearable
|
||||
placeholder="入场/出口钢卷号"
|
||||
style="width:100%"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<span class="filter-label">开始时间</span>
|
||||
<el-date-picker
|
||||
v-model="query.createStartTime"
|
||||
type="datetime"
|
||||
size="mini"
|
||||
style="width:100%"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="创建开始时间"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<span class="filter-label">结束时间</span>
|
||||
<el-date-picker
|
||||
v-model="query.createEndTime"
|
||||
type="datetime"
|
||||
size="mini"
|
||||
style="width:100%"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="创建结束时间"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" size="mini" :loading="loading" @click="handleSearch">查找</el-button>
|
||||
<el-button size="mini" @click="handleReset">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 选中行详情 -->
|
||||
<div v-if="selectedRow" class="panel-block detail-block">
|
||||
<div class="panel-title">计划详情</div>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">计划号</span>
|
||||
<span class="detail-val">{{ selectedRow.planNo }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">入场卷号</span>
|
||||
<span class="detail-val">{{ selectedRow.enterCoilNo || selectedRow.inMatNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">出口卷号</span>
|
||||
<span class="detail-val">{{ selectedRow.currentCoilNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">合金牌号</span>
|
||||
<span class="detail-val">{{ selectedRow.alloyNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">来料厚</span>
|
||||
<span class="detail-val accent">{{ selectedRow.inMatThick ? selectedRow.inMatThick + ' mm' : '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">来料宽</span>
|
||||
<span class="detail-val accent">{{ selectedRow.inMatWidth ? selectedRow.inMatWidth + ' mm' : '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">来料重</span>
|
||||
<span class="detail-val accent">{{ selectedRow.inMatWeight ? selectedRow.inMatWeight + ' t' : '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">来料长</span>
|
||||
<span class="detail-val">{{ selectedRow.inMatLength ? selectedRow.inMatLength + ' m' : '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">成品厚</span>
|
||||
<span class="detail-val accent">{{ selectedRow.outThick ? selectedRow.outThick + ' mm' : '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">道次数</span>
|
||||
<span class="detail-val">{{ selectedRow.passCount || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">工艺方案</span>
|
||||
<span class="detail-val">{{ selectedRow.recipeNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">计划状态</span>
|
||||
<span class="detail-val">
|
||||
<el-tag :type="planStatusType(selectedRow.planStatus)" size="mini">
|
||||
{{ planStatusLabel(selectedRow.planStatus) }}
|
||||
</el-tag>
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">生产状态</span>
|
||||
<span class="detail-val">
|
||||
<el-tag :type="prodStatusType(selectedRow.prodStatus)" size="mini" effect="plain">
|
||||
{{ selectedRow.prodStatus || '—' }}
|
||||
</el-tag>
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">创建人</span>
|
||||
<span class="detail-val">{{ selectedRow.createBy || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-key">创建时间</span>
|
||||
<span class="detail-val">{{ formatDate(selectedRow.createTime) }}</span>
|
||||
</div>
|
||||
<div v-if="selectedRow.remark" class="detail-row">
|
||||
<span class="detail-key">备注</span>
|
||||
<span class="detail-val">{{ selectedRow.remark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listDrActualPage } from '@/api/wms/drMill'
|
||||
|
||||
const PLAN_STATUS = {
|
||||
'0': { label: '准备', type: 'info' },
|
||||
'1': { label: '上线', type: 'primary' },
|
||||
'2': { label: '生产中', type: 'warning' },
|
||||
'3': { label: '生产完成', type: 'success' },
|
||||
}
|
||||
|
||||
const PROD_STATUS_TYPE = {
|
||||
Idle: 'info',
|
||||
Rolling: 'warning',
|
||||
NextCoil: 'primary',
|
||||
Done: 'success',
|
||||
Error: 'danger',
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'DrActual',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
list: [],
|
||||
total: 0,
|
||||
selectedRow: null,
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
planStatus: '',
|
||||
inMatNo: '',
|
||||
createStartTime: '',
|
||||
createEndTime: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableHeight() { return 'calc(100vh - 210px)' }
|
||||
},
|
||||
created() { this.loadList() },
|
||||
methods: {
|
||||
loadList() {
|
||||
this.loading = true
|
||||
const params = {
|
||||
...this.query,
|
||||
planStatus: this.query.planStatus || undefined,
|
||||
inMatNo: this.query.inMatNo || undefined,
|
||||
createStartTime: this.query.createStartTime || undefined,
|
||||
createEndTime: this.query.createEndTime || undefined,
|
||||
}
|
||||
listDrActualPage(params)
|
||||
.then(res => {
|
||||
this.list = res.rows || []
|
||||
this.total = res.total || 0
|
||||
})
|
||||
.finally(() => { this.loading = false })
|
||||
},
|
||||
handlePageChange(page) {
|
||||
this.query.pageNum = page
|
||||
this.loadList()
|
||||
},
|
||||
handleSizeChange(size) {
|
||||
this.query.pageSize = size
|
||||
this.query.pageNum = 1
|
||||
this.loadList()
|
||||
},
|
||||
onStatusChange() {
|
||||
this.query.pageNum = 1
|
||||
this.loadList()
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.selectedRow = (this.selectedRow && this.selectedRow.planId === row.planId) ? null : row
|
||||
},
|
||||
handleSearch() {
|
||||
this.query.pageNum = 1
|
||||
this.loadList()
|
||||
},
|
||||
handleReset() {
|
||||
this.query.planStatus = ''
|
||||
this.query.inMatNo = ''
|
||||
this.query.createStartTime = ''
|
||||
this.query.createEndTime = ''
|
||||
this.query.pageNum = 1
|
||||
this.selectedRow = null
|
||||
this.loadList()
|
||||
},
|
||||
planStatusLabel(s) { return (PLAN_STATUS[s] || { label: s || '—' }).label },
|
||||
planStatusType(s) { return (PLAN_STATUS[s] || { type: '' }).type },
|
||||
prodStatusType(s) { return PROD_STATUS_TYPE[s] || 'info' },
|
||||
formatDate(v) {
|
||||
if (!v) return '—'
|
||||
return String(v).replace('T', ' ').substring(0, 19)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.actual-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #f5f7fa;
|
||||
padding: 10px 12px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.body-wrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── 表格卡片 ── */
|
||||
.table-card {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
flex-shrink: 0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.table-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 6px 10px;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
background: #fafafa;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.accent-val {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* ── 侧边面板 ── */
|
||||
.side-panel {
|
||||
width: 220px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar { width: 4px; }
|
||||
&::-webkit-scrollbar-thumb { background: #dcdfe6; border-radius: 2px; }
|
||||
}
|
||||
|
||||
.panel-block {
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.filter-item { margin-bottom: 10px; }
|
||||
|
||||
.filter-label {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ── 详情 ── */
|
||||
.detail-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.detail-key {
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
flex-shrink: 0;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.detail-val {
|
||||
font-size: 12px;
|
||||
color: #303133;
|
||||
text-align: right;
|
||||
word-break: break-all;
|
||||
|
||||
&.accent {
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
516
klp-ui/src/views/micro/pages/dr/components/Plan.vue
Normal file
516
klp-ui/src/views/micro/pages/dr/components/Plan.vue
Normal file
@@ -0,0 +1,516 @@
|
||||
<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="240">
|
||||
<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>
|
||||
<span v-if="boundVersionCode" class="recipe-tag" style="color:#409eff;background:#ecf5ff">
|
||||
{{ boundVersionCode }}
|
||||
</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="300">
|
||||
<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="520px"
|
||||
:close-on-click-modal="false" append-to-body>
|
||||
<el-form size="small" label-width="72px">
|
||||
<el-form-item label="工艺方案">
|
||||
<el-select v-model="selectRecipeId" filterable clearable
|
||||
placeholder="输入方案号或合金号搜索" style="width:100%"
|
||||
@change="onSelectRecipeChange">
|
||||
<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>
|
||||
</el-form-item>
|
||||
<el-form-item label="版本">
|
||||
<el-select v-model="selectVersionId" placeholder="请先选择方案" style="width:100%"
|
||||
:disabled="!selectRecipeId || !selectVersionList.length">
|
||||
<el-option v-for="v in selectVersionList" :key="v.versionId"
|
||||
:label="`${v.versionCode}${v.isActive === 1 ? '(当前激活)' : v.status === '1' ? '(已发布)' : '(草稿)'}`"
|
||||
:value="v.versionId" />
|
||||
</el-select>
|
||||
<div v-if="selectRecipeId && !selectVersionList.length" style="font-size:12px;color:#f56c6c;margin-top:4px">
|
||||
该方案暂无版本,请先在「规程」页新增版本
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button size="mini" @click="selectRecipeVisible = false">取消</el-button>
|
||||
<el-button size="mini" type="primary" @click="confirmSelectRecipe"
|
||||
:disabled="!selectVersionId">确定绑定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listDrPlan, addDrPlan, updateDrPlan, delDrPlan, moveUpDrPlan, moveDownDrPlan, finishDrPlan,
|
||||
listDrRecipe, getDrRecipeDetail,
|
||||
listDrRecipeVersions, getDrRecipeVersionDetail,
|
||||
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: 'DrPlanTab',
|
||||
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,
|
||||
selectVersionList: [], // 选方案后加载的版本列表
|
||||
selectVersionId: null, // 用户选中的版本ID
|
||||
boundVersionCode: '', // 当前计划绑定的版本号(展示用)
|
||||
}
|
||||
},
|
||||
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 = []
|
||||
this.boundVersionCode = ''
|
||||
if (!row) return
|
||||
if (row.versionId) {
|
||||
// 优先用版本ID精确加载道次
|
||||
getDrRecipeVersionDetail(row.versionId).then(res => {
|
||||
this.passList = (res.data && res.data.passList) || []
|
||||
this.boundVersionCode = res.data ? res.data.versionCode : ''
|
||||
})
|
||||
} else if (row.recipeId) {
|
||||
// 兼容旧数据:取激活版本道次(后端 selectDetailById 已处理)
|
||||
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.selectVersionId = null
|
||||
this.selectVersionList = []
|
||||
this.selectRecipeVisible = true
|
||||
},
|
||||
onSelectRecipeChange(recipeId) {
|
||||
this.selectVersionId = null
|
||||
this.selectVersionList = []
|
||||
if (!recipeId) return
|
||||
listDrRecipeVersions(recipeId).then(res => {
|
||||
this.selectVersionList = res.data || []
|
||||
// 默认选中激活版本
|
||||
const active = this.selectVersionList.find(v => v.isActive === 1)
|
||||
if (active) this.selectVersionId = active.versionId
|
||||
else if (this.selectVersionList.length) this.selectVersionId = this.selectVersionList[0].versionId
|
||||
})
|
||||
},
|
||||
confirmSelectRecipe() {
|
||||
if (!this.selectRecipeId) { this.$message.warning('请选择方案'); return }
|
||||
if (!this.selectVersionId) { this.$message.warning('请选择版本'); return }
|
||||
const r = this.recipeOptions.find(x => x.recipeId === this.selectRecipeId)
|
||||
const v = this.selectVersionList.find(x => x.versionId === this.selectVersionId)
|
||||
updateDrPlan({
|
||||
...this.selectedPlan,
|
||||
recipeId: this.selectRecipeId,
|
||||
recipeNo: r ? r.recipeNo : '',
|
||||
versionId: this.selectVersionId,
|
||||
passCount: r ? r.passCount : 0,
|
||||
}).then(() => {
|
||||
this.$message.success('方案绑定成功')
|
||||
this.selectRecipeVisible = false
|
||||
this.selectedPlan = {
|
||||
...this.selectedPlan,
|
||||
recipeId: this.selectRecipeId,
|
||||
recipeNo: r ? r.recipeNo : '',
|
||||
versionId: this.selectVersionId,
|
||||
}
|
||||
getDrRecipeVersionDetail(this.selectVersionId).then(res => {
|
||||
this.passList = (res.data && res.data.passList) || []
|
||||
this.boundVersionCode = res.data ? res.data.versionCode : ''
|
||||
})
|
||||
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: 100%;
|
||||
background: #f5f7fa;
|
||||
padding: 8px 12px;
|
||||
box-sizing: border-box;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 16px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.recipe-tag {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #909399;
|
||||
background: #f0f2f5;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.queue-section {
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.04);
|
||||
}
|
||||
.queue-table {
|
||||
::v-deep .row-rolling td { background: #fef3e2 !important; }
|
||||
::v-deep .el-table__row.current-row td { background: #ecf5ff !important; }
|
||||
}
|
||||
|
||||
.bottom-section { display: flex; flex: 1; gap: 8px; overflow: hidden; min-height: 0; }
|
||||
|
||||
.pass-panel {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.04);
|
||||
}
|
||||
.pass-empty {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
.pass-table {
|
||||
flex: 1;
|
||||
::v-deep .el-table__row.alt-row td { background: #fafafa !important; }
|
||||
}
|
||||
|
||||
.calc-val { font-family: 'Courier New', monospace; font-weight: 600; color: #409eff; }
|
||||
|
||||
.op-panel {
|
||||
width: 260px;
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.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: #909399; }
|
||||
.status-rolling { color: #e6a23c; font-weight: 700; }
|
||||
.status-done { color: #409eff; }
|
||||
.status-err { color: #f56c6c; font-weight: 700; }
|
||||
</style>
|
||||
485
klp-ui/src/views/micro/pages/dr/components/ProcessSpec.vue
Normal file
485
klp-ui/src/views/micro/pages/dr/components/ProcessSpec.vue
Normal file
@@ -0,0 +1,485 @@
|
||||
<template>
|
||||
<div class="process-page">
|
||||
|
||||
<!-- 左栏:方案列表 -->
|
||||
<div class="col-panel left-panel">
|
||||
<div class="panel-header">
|
||||
<span>工艺方案</span>
|
||||
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleAddRecipe">新增</el-button>
|
||||
</div>
|
||||
<div class="search-bar">
|
||||
<el-input v-model="searchKey" placeholder="方案号/合金号" size="mini" clearable
|
||||
prefix-icon="el-icon-search" @input="loadRecipeList" />
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<div v-for="r in recipeList" :key="r.recipeId"
|
||||
:class="['list-item', { active: selectedRecipeId === r.recipeId }]"
|
||||
@click="handleSelectRecipe(r)">
|
||||
<div class="item-main">{{ r.recipeNo }}</div>
|
||||
<div class="item-sub">{{ r.alloyNo }} · {{ r.inThick }}→{{ r.outThick }}mm</div>
|
||||
</div>
|
||||
<div v-if="!recipeList.length" class="empty-tip">暂无方案</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 中栏:版本列表 -->
|
||||
<div class="col-panel mid-panel">
|
||||
<div class="panel-header">
|
||||
<span>版本列表</span>
|
||||
<el-button type="primary" size="mini" icon="el-icon-plus"
|
||||
:disabled="!selectedRecipeId" @click="handleAddVersion">新增版本</el-button>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<div v-for="v in versionList" :key="v.versionId"
|
||||
:class="['list-item', { active: selectedVersionId === v.versionId }]"
|
||||
@click="handleSelectVersion(v)">
|
||||
<div class="item-main">
|
||||
{{ v.versionCode }}
|
||||
<el-tag v-if="v.isActive === 1" size="mini" type="success" style="margin-left:4px">激活</el-tag>
|
||||
<el-tag v-else-if="v.status === '1'" size="mini" type="info" style="margin-left:4px">已发布</el-tag>
|
||||
<el-tag v-else size="mini" style="margin-left:4px">草稿</el-tag>
|
||||
</div>
|
||||
<div class="item-sub">{{ v.remark }}</div>
|
||||
<div class="item-actions">
|
||||
<span v-if="v.isActive !== 1" class="action-link" @click.stop="handleActivate(v)">激活</span>
|
||||
<span class="action-link danger" @click.stop="handleDeleteVersion(v)">删除</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedRecipeId && !versionList.length" class="empty-tip">暂无版本,请新增</div>
|
||||
<div v-if="!selectedRecipeId" class="empty-tip">请先选择方案</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右栏:版本详情 + 道次 -->
|
||||
<div class="right-panel">
|
||||
<div v-if="!versionForm.versionId && !isNewVersion" class="no-select">
|
||||
<i class="el-icon-document"></i>
|
||||
<p>请选择或新建版本</p>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<!-- 版本基本信息 -->
|
||||
<div class="detail-header">
|
||||
<span>{{ isNewVersion ? '新建版本' : versionForm.versionCode }}</span>
|
||||
<div class="btn-group">
|
||||
<el-button size="mini" type="primary" icon="el-icon-check" @click="handleSaveVersion">保存</el-button>
|
||||
<el-button size="mini" icon="el-icon-refresh" @click="handleResetVersion">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form :model="versionForm" ref="versionFormRef" size="mini" label-width="76px"
|
||||
class="version-form" :rules="versionRules">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="5">
|
||||
<el-form-item label="版本号" prop="versionCode">
|
||||
<el-input v-model="versionForm.versionCode" placeholder="如 V1.0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="versionForm.status" style="width:100%">
|
||||
<el-option label="草稿" value="0" />
|
||||
<el-option label="已发布" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="versionForm.remark" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 道次参数 -->
|
||||
<div class="pass-section">
|
||||
<div class="pass-header">
|
||||
<span>道次参数</span>
|
||||
<div class="btn-group">
|
||||
<el-button size="mini" icon="el-icon-plus" @click="addPass">增加道次</el-button>
|
||||
<el-button size="mini" icon="el-icon-minus" @click="removeLastPass" :disabled="!passList.length">删除末道</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="passList" border size="mini" class="pass-table"
|
||||
:row-class-name="passRowClass" height="calc(100vh - 310px)">
|
||||
<el-table-column label="道次" prop="passNo" width="50" align="center" fixed>
|
||||
<template slot-scope="{ row }"><b>{{ row.passNo }}</b></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入口厚度(mm)" width="100" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-input v-model="row.inThick" size="mini" @blur="calcPass(row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出口厚度(mm)" width="100" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-input v-model="row.outThick" size="mini" @blur="calcPass(row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="宽度(mm)" width="90" align="right">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.width" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="轧制力(kN)" width="90" align="right">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.rollForce" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入口张力(kN)" width="95" align="right">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.inTension" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出口张力(kN)" width="95" align="right">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.outTension" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最高速度(m/min)" width="110" align="right">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.maxSpeed" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入口单位张力" width="100" align="right">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.inUnitTension" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出口单位张力" width="100" align="right">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.outUnitTension" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="压下量(mm)" width="90" align="right">
|
||||
<template slot-scope="{ row }"><span class="calc-val">{{ row.reduction }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总压下量(mm)" width="100" align="right">
|
||||
<template slot-scope="{ row }"><span class="calc-val">{{ row.totalReduction }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="100">
|
||||
<template slot-scope="{ row }"><el-input v-model="row.remark" size="mini" /></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑方案对话框 -->
|
||||
<el-dialog :title="recipeDialogTitle" :visible.sync="recipeDialogVisible" width="520px" append-to-body>
|
||||
<el-form :model="recipeForm" :rules="recipeRules" ref="recipeFormRef" size="small" label-width="88px">
|
||||
<el-form-item label="方案记录号" prop="recipeNo">
|
||||
<el-input v-model="recipeForm.recipeNo" :disabled="!!recipeForm.recipeId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合金号" prop="alloyNo">
|
||||
<el-input v-model="recipeForm.alloyNo" />
|
||||
</el-form-item>
|
||||
<el-form-item label="原料厚度">
|
||||
<el-input v-model="recipeForm.inThick"><template slot="append">mm</template></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="成品厚度">
|
||||
<el-input v-model="recipeForm.outThick"><template slot="append">mm</template></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="成品宽度">
|
||||
<el-input v-model="recipeForm.outWidth"><template slot="append">mm</template></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="recipeDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitRecipe">确定</el-button>
|
||||
<el-button v-if="recipeForm.recipeId" type="danger" @click="handleDeleteRecipe">删除</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listDrRecipe, addDrRecipe, updateDrRecipe, delDrRecipe,
|
||||
listDrRecipeVersions, getDrRecipeVersionDetail,
|
||||
addDrRecipeVersion, updateDrRecipeVersion,
|
||||
activateDrRecipeVersion, delDrRecipeVersion
|
||||
} from '@/api/wms/drMill'
|
||||
|
||||
const emptyPass = (no) => ({
|
||||
passNo: no, inThick: '', outThick: '', width: '',
|
||||
rollForce: '', inTension: '', outTension: '',
|
||||
maxSpeed: '', inUnitTension: '', outUnitTension: '',
|
||||
reduction: '', totalReduction: '', remark: ''
|
||||
})
|
||||
|
||||
const emptyVersionForm = () => ({
|
||||
versionId: null, recipeId: null, versionCode: '', status: '0', remark: ''
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'DrProcessTab',
|
||||
data() {
|
||||
return {
|
||||
// 方案
|
||||
searchKey: '',
|
||||
recipeList: [],
|
||||
selectedRecipeId: null,
|
||||
recipeDialogVisible: false,
|
||||
recipeDialogTitle: '新增工艺方案',
|
||||
recipeForm: {},
|
||||
recipeRules: {
|
||||
recipeNo: [{ required: true, message: '请输入方案记录号', trigger: 'blur' }],
|
||||
alloyNo: [{ required: true, message: '请输入合金号', trigger: 'blur' }],
|
||||
},
|
||||
// 版本
|
||||
versionList: [],
|
||||
selectedVersionId: null,
|
||||
isNewVersion: false,
|
||||
versionForm: emptyVersionForm(),
|
||||
versionRules: {
|
||||
versionCode: [{ required: true, message: '请输入版本号', trigger: 'blur' }],
|
||||
},
|
||||
// 道次
|
||||
passList: [],
|
||||
}
|
||||
},
|
||||
mounted() { this.loadRecipeList() },
|
||||
methods: {
|
||||
// ── 方案 ──────────────────────────────────────────────────
|
||||
loadRecipeList() {
|
||||
listDrRecipe({ recipeNo: this.searchKey, alloyNo: this.searchKey }).then(res => {
|
||||
this.recipeList = res.data || []
|
||||
})
|
||||
},
|
||||
handleSelectRecipe(r) {
|
||||
this.selectedRecipeId = r.recipeId
|
||||
this.isNewVersion = false
|
||||
this.selectedVersionId = null
|
||||
this.versionForm = emptyVersionForm()
|
||||
this.passList = []
|
||||
this.loadVersionList(r.recipeId)
|
||||
},
|
||||
handleAddRecipe() {
|
||||
this.recipeForm = { recipeNo: '', alloyNo: '', inThick: '', outThick: '', outWidth: '', status: '0' }
|
||||
this.recipeDialogTitle = '新增工艺方案'
|
||||
this.recipeDialogVisible = true
|
||||
},
|
||||
handleEditRecipe(r) {
|
||||
this.recipeForm = { ...r }
|
||||
this.recipeDialogTitle = '编辑工艺方案'
|
||||
this.recipeDialogVisible = true
|
||||
},
|
||||
submitRecipe() {
|
||||
this.$refs.recipeFormRef.validate(valid => {
|
||||
if (!valid) return
|
||||
const api = this.recipeForm.recipeId ? updateDrRecipe : addDrRecipe
|
||||
api(this.recipeForm).then(() => {
|
||||
this.$message.success('保存成功')
|
||||
this.recipeDialogVisible = false
|
||||
this.loadRecipeList()
|
||||
})
|
||||
})
|
||||
},
|
||||
handleDeleteRecipe() {
|
||||
this.$confirm('确定删除该方案及所有版本?', '提示', { type: 'warning' }).then(() => {
|
||||
delDrRecipe([this.recipeForm.recipeId]).then(() => {
|
||||
this.$message.success('删除成功')
|
||||
this.recipeDialogVisible = false
|
||||
this.selectedRecipeId = null
|
||||
this.versionList = []
|
||||
this.versionForm = emptyVersionForm()
|
||||
this.passList = []
|
||||
this.loadRecipeList()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// ── 版本 ──────────────────────────────────────────────────
|
||||
loadVersionList(recipeId) {
|
||||
listDrRecipeVersions(recipeId).then(res => {
|
||||
this.versionList = res.data || []
|
||||
})
|
||||
},
|
||||
handleSelectVersion(v) {
|
||||
this.selectedVersionId = v.versionId
|
||||
this.isNewVersion = false
|
||||
getDrRecipeVersionDetail(v.versionId).then(res => {
|
||||
this.versionForm = { ...res.data }
|
||||
this.passList = (res.data.passList || []).map(p => ({ ...p }))
|
||||
})
|
||||
},
|
||||
handleAddVersion() {
|
||||
this.isNewVersion = true
|
||||
this.selectedVersionId = null
|
||||
this.versionForm = { ...emptyVersionForm(), recipeId: this.selectedRecipeId }
|
||||
this.passList = []
|
||||
},
|
||||
handleSaveVersion() {
|
||||
this.$refs.versionFormRef.validate(valid => {
|
||||
if (!valid) return
|
||||
this.versionForm.passList = this.passList
|
||||
const api = this.isNewVersion ? addDrRecipeVersion : updateDrRecipeVersion
|
||||
api(this.versionForm).then(res => {
|
||||
this.$message.success('保存成功')
|
||||
this.isNewVersion = false
|
||||
if (res.data) this.versionForm.versionId = res.data
|
||||
this.loadVersionList(this.selectedRecipeId)
|
||||
})
|
||||
})
|
||||
},
|
||||
handleResetVersion() {
|
||||
if (this.isNewVersion) {
|
||||
this.versionForm = { ...emptyVersionForm(), recipeId: this.selectedRecipeId }
|
||||
this.passList = []
|
||||
} else {
|
||||
this.handleSelectVersion({ versionId: this.selectedVersionId })
|
||||
}
|
||||
},
|
||||
handleActivate(v) {
|
||||
this.$confirm(`确定激活版本 ${v.versionCode}?`, '提示', { type: 'warning' }).then(() => {
|
||||
activateDrRecipeVersion(v.versionId).then(() => {
|
||||
this.$message.success('激活成功')
|
||||
this.loadVersionList(this.selectedRecipeId)
|
||||
if (this.selectedVersionId === v.versionId) {
|
||||
this.versionForm.isActive = 1
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
handleDeleteVersion(v) {
|
||||
this.$confirm(`确定删除版本 ${v.versionCode}?`, '提示', { type: 'warning' }).then(() => {
|
||||
delDrRecipeVersion(v.versionId).then(() => {
|
||||
this.$message.success('删除成功')
|
||||
if (this.selectedVersionId === v.versionId) {
|
||||
this.selectedVersionId = null
|
||||
this.versionForm = emptyVersionForm()
|
||||
this.passList = []
|
||||
}
|
||||
this.loadVersionList(this.selectedRecipeId)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// ── 道次 ──────────────────────────────────────────────────
|
||||
addPass() { this.passList.push(emptyPass(this.passList.length + 1)) },
|
||||
removeLastPass() { if (this.passList.length) this.passList.pop() },
|
||||
calcPass(row) {
|
||||
const inT = parseFloat(row.inThick) || 0
|
||||
const outT = parseFloat(row.outThick) || 0
|
||||
row.reduction = outT > 0 ? (inT - outT).toFixed(3) : ''
|
||||
const base = parseFloat(this.passList[0] && this.passList[0].inThick) || 0
|
||||
for (const p of this.passList) {
|
||||
const pOut = parseFloat(p.outThick) || 0
|
||||
if (base > 0 && pOut > 0) p.totalReduction = parseFloat((base - pOut).toFixed(3))
|
||||
}
|
||||
},
|
||||
passRowClass({ rowIndex }) { return rowIndex % 2 === 0 ? '' : 'alt-row' }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.process-page {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
gap: 0;
|
||||
background: #f5f7fa;
|
||||
padding: 10px 12px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.col-panel {
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.04);
|
||||
}
|
||||
.left-panel { width: 200px; }
|
||||
.mid-panel { width: 220px; }
|
||||
|
||||
.panel-header {
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
padding: 10px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-bar { padding: 6px 8px; border-bottom: 1px solid #e4e7ed; flex-shrink: 0; }
|
||||
|
||||
.item-list { flex: 1; overflow-y: auto; }
|
||||
|
||||
.list-item {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
cursor: pointer;
|
||||
transition: background .15s;
|
||||
&:hover { background: #f5f7fa; }
|
||||
&.active {
|
||||
background: #ecf5ff;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
.item-main { font-size: 13px; font-weight: 600; color: #303133; }
|
||||
.item-sub { font-size: 12px; color: #909399; margin-top: 2px; }
|
||||
.item-actions {
|
||||
display: flex; gap: 8px; margin-top: 4px;
|
||||
.action-link {
|
||||
font-size: 12px; color: #409eff; cursor: pointer;
|
||||
&:hover { text-decoration: underline; }
|
||||
&.danger { color: #f56c6c; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip { text-align: center; color: #c0c4cc; padding: 24px 0; font-size: 13px; }
|
||||
|
||||
.right-panel {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.no-select {
|
||||
flex: 1; display: flex; flex-direction: column; align-items: center;
|
||||
justify-content: center; color: #c0c4cc; font-size: 13px;
|
||||
i { font-size: 48px; margin-bottom: 10px; }
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
padding: 10px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-group { display: flex; gap: 6px; }
|
||||
|
||||
.version-form { padding: 8px 12px 4px; flex-shrink: 0; border-bottom: 1px solid #e4e7ed; }
|
||||
|
||||
.pass-section { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
|
||||
|
||||
.pass-header {
|
||||
padding: 8px 16px;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pass-table {
|
||||
flex: 1;
|
||||
::v-deep .el-table__row.alt-row td { background: #fafafa !important; }
|
||||
::v-deep .el-input__inner { text-align: right; padding: 0 4px !important; }
|
||||
}
|
||||
|
||||
.calc-val { font-family: 'Courier New', monospace; font-weight: 600; color: #409eff; }
|
||||
</style>
|
||||
89
klp-ui/src/views/micro/pages/dr/index.vue
Normal file
89
klp-ui/src/views/micro/pages/dr/index.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="dr-container">
|
||||
<div class="dr-sidebar">
|
||||
<el-menu :default-active="activeMenu" class="sidebar-menu" @select="handleMenuSelect">
|
||||
<el-menu-item index="plan">
|
||||
<i class="el-icon-s-order"></i>
|
||||
<span slot="title">计划</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="processSpec">
|
||||
<i class="el-icon-s-tools"></i>
|
||||
<span slot="title">规程</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="actual">
|
||||
<i class="el-icon-s-data"></i>
|
||||
<span slot="title">实绩</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div style="flex:1;overflow:hidden;">
|
||||
<component :is="currentComponent" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Plan from './components/Plan.vue'
|
||||
import ProcessSpec from './components/ProcessSpec.vue'
|
||||
import Actual from './components/Actual.vue'
|
||||
|
||||
export default {
|
||||
name: 'DrSystem',
|
||||
components: { Plan, ProcessSpec, Actual },
|
||||
data() {
|
||||
return { activeMenu: 'plan' }
|
||||
},
|
||||
computed: {
|
||||
currentComponent() {
|
||||
return { plan: 'Plan', processSpec: 'ProcessSpec', actual: 'Actual' }[this.activeMenu]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMenuSelect(index) { this.activeMenu = index }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dr-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: calc(100vh - 84px);
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.dr-sidebar {
|
||||
width: 100px;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 2px 0 8px rgba(0,0,0,.05);
|
||||
|
||||
.sidebar-menu {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
border-right: none;
|
||||
|
||||
::v-deep .el-menu-item {
|
||||
color: #606266;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
|
||||
i {
|
||||
font-size: 18px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&:hover { background-color: #ecf5ff; color: #409eff; }
|
||||
&.is-active {
|
||||
background-color: #ecf5ff;
|
||||
color: #409eff;
|
||||
border-right: 3px solid #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user