feat(aps): 新增排产单明细合并功能及优化界面展示
- 在排产单明细表格中添加多选合并功能 - 实现排产单明细合并对话框及合并逻辑 - 优化排产单明细表格列配置和表单布局 - 添加合并校验和接收产需单API接口 - 重构订单绑定解绑逻辑提升用户体验 - 添加ScheduleDetailCoilBind组件引入
This commit is contained in:
334
klp-ui/src/views/wms/coil/components/ScheduleDetailCoilBind.vue
Normal file
334
klp-ui/src/views/wms/coil/components/ScheduleDetailCoilBind.vue
Normal file
@@ -0,0 +1,334 @@
|
||||
<template>
|
||||
<span>
|
||||
<el-button icon="el-icon-link" size="mini" @click="open">绑定排产明细</el-button>
|
||||
|
||||
<el-dialog title="排产明细 - 钢卷绑定" :visible.sync="visible" width="1200px" append-to-body :close-on-click-modal="false"
|
||||
v-loading="loading">
|
||||
<!-- 上半部分:今日排产明细 -->
|
||||
<div class="bind-section">
|
||||
<div class="bind-section-header">今日排产明细({{ scheduleItems.length }} 条)</div>
|
||||
<el-table :data="scheduleItems" highlight-current-row @row-click="handleDetailClick" max-height="300"
|
||||
size="small">
|
||||
<el-table-column prop="scheduleNo" label="排产单号" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="prodDate" label="生产日期" width="90" />
|
||||
<el-table-column prop="spec" label="规格" width="90" show-overflow-tooltip />
|
||||
<el-table-column prop="material" label="材质" width="70" />
|
||||
<el-table-column prop="scheduleWeight" label="排产吨数" width="80" />
|
||||
<el-table-column prop="productType" label="品名" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="customerName" label="订货单位" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="已绑钢卷" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" type="info">{{ scope.row._boundCount || 0 }} 卷</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 下半部分:已绑定钢卷 -->
|
||||
<div class="bind-section" v-if="currentDetail" style="margin-top:16px;">
|
||||
<div class="bind-section-header">
|
||||
已绑定钢卷 — {{ currentDetail.spec }} / {{ currentDetail.material }}
|
||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openCoilSelect">绑定钢卷</el-button>
|
||||
</div>
|
||||
<el-table :data="boundCoils" size="small" v-loading="boundCoilsLoading">
|
||||
<el-table-column prop="currentCoilNo" label="钢卷号" width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="specification" label="规格" width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="material" label="材质" width="70" />
|
||||
<el-table-column prop="netWeight" label="净重" width="80" />
|
||||
<el-table-column prop="_remark" label="备注" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="70" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" style="color:#ff4d4f;" size="mini" @click="handleUnbind(scope.row)">解绑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="boundCoils.length === 0 && !boundCoilsLoading" description="暂未绑定钢卷" />
|
||||
</div>
|
||||
|
||||
<el-empty v-if="scheduleItems.length === 0 && !loading" description="今日无排产明细" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- 选卷子弹窗 -->
|
||||
<el-dialog title="选择钢卷" :visible.sync="coilDialogVisible" width="900px" append-to-body
|
||||
:close-on-click-modal="false">
|
||||
<div style="display:flex;gap:8px;margin-bottom:10px;">
|
||||
<el-input v-model="coilQuery.keyword" placeholder="钢卷号 / 入场卷号" size="small" style="width:220px"
|
||||
clearable @keyup.enter.native="searchCoils" />
|
||||
<el-button size="small" type="primary" icon="el-icon-search" @click="searchCoils">搜索</el-button>
|
||||
<span style="margin-left:auto;font-size:12px;color:#909399;line-height:32px;">
|
||||
已选 {{ selectedCoils.length }} 卷
|
||||
</span>
|
||||
</div>
|
||||
<el-table :data="coilList" @selection-change="handleCoilSelection" ref="coilSelectTable" size="small"
|
||||
v-loading="coilListLoading" max-height="400">
|
||||
<el-table-column type="selection" width="50" :selectable="checkCoilSelectable" />
|
||||
<el-table-column prop="currentCoilNo" label="钢卷号" width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="enterCoilNo" label="入场卷号" width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="specification" label="规格" width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="material" label="材质"/>
|
||||
<el-table-column prop="netWeight" label="净重" width="80" />
|
||||
<el-table-column prop="warehouseName" label="仓库" width="80" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<pagination v-show="coilTotal > 0" :total="coilTotal" :page.sync="coilQuery.pageNum"
|
||||
:limit.sync="coilQuery.pageSize" @pagination="searchCoils" style="padding:10px 0 0 0;" />
|
||||
<span slot="footer">
|
||||
<el-button type="primary" @click="confirmBindCoils" :loading="bindLoading"
|
||||
:disabled="selectedCoils.length === 0">确定绑定({{ selectedCoils.length }} 卷)</el-button>
|
||||
<el-button @click="coilDialogVisible = false">取消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listScheduleItem } from '@/api/aps/schedule'
|
||||
import { listMaterialCoil } from '@/api/wms/coil'
|
||||
import { listDetailCoilRel, batchAddDetailCoilRel, delDetailCoilRel } from '@/api/aps/detailCoilRel'
|
||||
|
||||
export default {
|
||||
name: 'ScheduleDetailCoilBind',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
scheduleItems: [],
|
||||
currentDetail: null,
|
||||
boundCoils: [],
|
||||
boundCoilsLoading: false,
|
||||
allBoundCoilIds: new Set(),
|
||||
|
||||
coilDialogVisible: false,
|
||||
coilListLoading: false,
|
||||
coilList: [],
|
||||
coilTotal: 0,
|
||||
selectedCoils: [],
|
||||
bindLoading: false,
|
||||
coilQuery: {
|
||||
keyword: '',
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
prodDate() {
|
||||
return new Date().toISOString().slice(0, 10)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.visible = true
|
||||
this.currentDetail = null
|
||||
this.boundCoils = []
|
||||
this.loadScheduleItems()
|
||||
},
|
||||
|
||||
async loadScheduleItems() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await listScheduleItem({ prodDate: this.prodDate })
|
||||
this.scheduleItems = (res.rows || []).map(item => {
|
||||
item._boundCount = 0
|
||||
return item
|
||||
})
|
||||
// 加载所有已绑定的钢卷ID
|
||||
await this.loadAllBoundCoilIds()
|
||||
} catch (e) {
|
||||
this.scheduleItems = []
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async loadAllBoundCoilIds() {
|
||||
const ids = this.scheduleItems.map(i => i.id || i.scheduleDetailId).filter(Boolean)
|
||||
if (ids.length === 0) return
|
||||
try {
|
||||
// 尝试批量查询
|
||||
const res = await listDetailCoilRel({ scheduleDetailIds: ids.join(',') })
|
||||
const rows = res.rows || []
|
||||
this.allBoundCoilIds = new Set(rows.map(r => r.coilId))
|
||||
this.applyBoundCounts(rows)
|
||||
} catch (e) {
|
||||
// 批量查询失败则逐个查询
|
||||
try {
|
||||
const allRows = []
|
||||
const tasks = ids.map(id =>
|
||||
listDetailCoilRel({ scheduleDetailId: id }).then(r => {
|
||||
(r.rows || []).forEach(row => allRows.push(row))
|
||||
}).catch(() => {})
|
||||
)
|
||||
await Promise.all(tasks)
|
||||
this.allBoundCoilIds = new Set(allRows.map(r => r.coilId))
|
||||
this.applyBoundCounts(allRows)
|
||||
} catch (e2) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
applyBoundCounts(rows) {
|
||||
const countMap = {}
|
||||
rows.forEach(r => {
|
||||
const key = r.scheduleDetailId
|
||||
countMap[key] = (countMap[key] || 0) + 1
|
||||
})
|
||||
this.scheduleItems.forEach(item => {
|
||||
const key = item.id || item.scheduleDetailId
|
||||
item._boundCount = countMap[key] || 0
|
||||
})
|
||||
},
|
||||
|
||||
async handleDetailClick(row) {
|
||||
this.currentDetail = row
|
||||
this.boundCoilsLoading = true
|
||||
try {
|
||||
const detailId = row.id || row.scheduleDetailId
|
||||
const res = await listDetailCoilRel({ scheduleDetailId: detailId })
|
||||
const rels = res.rows || []
|
||||
if (rels.length > 0) {
|
||||
const coilIds = rels.map(r => r.coilId).filter(Boolean)
|
||||
const coilRes = await listMaterialCoil({
|
||||
pageNum: 1,
|
||||
pageSize: 200,
|
||||
coilIds: coilIds.join(',')
|
||||
})
|
||||
const coilMap = {}
|
||||
if (coilRes.rows) {
|
||||
coilRes.rows.forEach(c => { coilMap[c.coilId || c.id] = c })
|
||||
}
|
||||
this.boundCoils = rels.map(r => ({
|
||||
...r,
|
||||
...(coilMap[r.coilId] || {}),
|
||||
_remark: r.remark || ''
|
||||
}))
|
||||
} else {
|
||||
this.boundCoils = []
|
||||
}
|
||||
} catch (e) {
|
||||
this.boundCoils = []
|
||||
} finally {
|
||||
this.boundCoilsLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
async openCoilSelect() {
|
||||
this.coilDialogVisible = true
|
||||
this.selectedCoils = []
|
||||
this.coilQuery.keyword = ''
|
||||
this.coilQuery.pageNum = 1
|
||||
await this.searchCoils()
|
||||
},
|
||||
|
||||
async searchCoils() {
|
||||
this.coilListLoading = true
|
||||
try {
|
||||
const params = {
|
||||
pageNum: this.coilQuery.pageNum,
|
||||
pageSize: this.coilQuery.pageSize,
|
||||
status: 0,
|
||||
dataType: 1,
|
||||
exclusiveStatus: 0
|
||||
}
|
||||
if (this.coilQuery.keyword) {
|
||||
params.currentCoilNo = this.coilQuery.keyword
|
||||
}
|
||||
const res = await listMaterialCoil(params)
|
||||
// 过滤掉已绑定的钢卷
|
||||
this.coilList = (res.rows || []).filter(c => !this.allBoundCoilIds.has(c.coilId || c.id))
|
||||
this.coilTotal = res.total || 0
|
||||
} catch (e) {
|
||||
this.coilList = []
|
||||
} finally {
|
||||
this.coilListLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
checkCoilSelectable(row) {
|
||||
return !this.allBoundCoilIds.has(row.coilId || row.id)
|
||||
},
|
||||
|
||||
handleCoilSelection(selection) {
|
||||
this.selectedCoils = selection
|
||||
},
|
||||
|
||||
async confirmBindCoils() {
|
||||
if (this.selectedCoils.length === 0) {
|
||||
this.$message.warning('请至少选择一个钢卷')
|
||||
return
|
||||
}
|
||||
this.bindLoading = true
|
||||
try {
|
||||
const detailId = this.currentDetail.id || this.currentDetail.scheduleDetailId
|
||||
const data = this.selectedCoils.map(c => ({
|
||||
scheduleDetailId: detailId,
|
||||
coilId: c.coilId || c.id,
|
||||
remark: ''
|
||||
}))
|
||||
await batchAddDetailCoilRel(data)
|
||||
this.$modal.msgSuccess(`成功绑定 ${this.selectedCoils.length} 卷`)
|
||||
this.coilDialogVisible = false
|
||||
// 记录新绑定的钢卷ID
|
||||
this.selectedCoils.forEach(c => {
|
||||
this.allBoundCoilIds.add(c.coilId || c.id)
|
||||
})
|
||||
// 更新当前明细的已绑卷数
|
||||
if (this.currentDetail) {
|
||||
this.currentDetail._boundCount = (this.currentDetail._boundCount || 0) + this.selectedCoils.length
|
||||
}
|
||||
// 刷新已绑定钢卷列表
|
||||
await this.handleDetailClick(this.currentDetail)
|
||||
} catch (e) {
|
||||
this.$modal.msgError('绑定失败')
|
||||
} finally {
|
||||
this.bindLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
async handleUnbind(row) {
|
||||
try {
|
||||
await this.$confirm('确认解绑该钢卷吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
const relId = row.relId || row.id
|
||||
await delDetailCoilRel(relId)
|
||||
this.$modal.msgSuccess('解绑成功')
|
||||
// 从已绑定集合中移除
|
||||
if (row.coilId) {
|
||||
this.allBoundCoilIds.delete(row.coilId)
|
||||
}
|
||||
if (this.currentDetail) {
|
||||
this.currentDetail._boundCount = Math.max(0, (this.currentDetail._boundCount || 0) - 1)
|
||||
}
|
||||
await this.handleDetailClick(this.currentDetail)
|
||||
} catch (e) {
|
||||
if (e !== 'cancel') {
|
||||
this.$modal.msgError('解绑失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bind-section {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bind-section-header {
|
||||
background: #f5f7fa;
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
</style>
|
||||
@@ -26,6 +26,7 @@
|
||||
<div class="flow-left">
|
||||
<div class="flow-section-title">
|
||||
<span>源卷列表</span>
|
||||
<schedule-detail-coil-bind />
|
||||
<el-button v-if="!readonly" type="primary" size="small" @click="addSourceCoil">添加源卷</el-button>
|
||||
</div>
|
||||
|
||||
@@ -374,6 +375,7 @@ import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
|
||||
import TimeInput from "@/components/TimeInput";
|
||||
import AbnormalForm from './components/AbnormalForm';
|
||||
import PlanSheetViewer from './components/PlanSheetViewer.vue';
|
||||
import ScheduleDetailCoilBind from './components/ScheduleDetailCoilBind'
|
||||
import { generateCoilNoPrefix } from "@/utils/coil/coilNo";
|
||||
import ContractSelect from "@/components/KLPService/ContractSelect";
|
||||
|
||||
@@ -389,6 +391,7 @@ export default {
|
||||
AbnormalForm,
|
||||
ContractSelect,
|
||||
PlanSheetViewer,
|
||||
ScheduleDetailCoilBind,
|
||||
},
|
||||
dicts: ['coil_quality_status', 'coil_abnormal_position', 'coil_abnormal_code', 'coil_abnormal_degree', 'coil_business_purpose'],
|
||||
data() {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
:name="tab.value"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<h3 class="section-title" v-else>待领物料列表</h3>
|
||||
<schedule-detail-coil-bind />
|
||||
<el-button size="mini" icon="el-icon-refresh" @click="getMaterialCoil">刷新</el-button>
|
||||
</div>
|
||||
|
||||
@@ -309,6 +310,7 @@ import CoilCard from '@/components/KLPService/Renderer/CoilCard.vue'
|
||||
import LabelRender from './LabelRender/index.vue'
|
||||
import StepSplit from './stepSplit.vue'
|
||||
import ExceptionManager from '../components/ExceptionManager'
|
||||
import ScheduleDetailCoilBind from '../components/ScheduleDetailCoilBind'
|
||||
import { getCoilTagPrintType } from '@/views/wms/coil/js/coilPrint'
|
||||
|
||||
export default {
|
||||
@@ -334,7 +336,8 @@ export default {
|
||||
CoilCard,
|
||||
LabelRender,
|
||||
StepSplit,
|
||||
ExceptionManager
|
||||
ExceptionManager,
|
||||
ScheduleDetailCoilBind
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<div class="flow-left">
|
||||
<div class="flow-section-title">
|
||||
<span>母卷信息</span>
|
||||
<schedule-detail-coil-bind />
|
||||
</div>
|
||||
<div class="coil-card mother-coil">
|
||||
<div class="coil-header">
|
||||
@@ -377,6 +378,7 @@ import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
|
||||
import TimeInput from "@/components/TimeInput";
|
||||
import AbnormalForm from './components/AbnormalForm';
|
||||
import PlanSheetViewer from './components/PlanSheetViewer.vue';
|
||||
import ScheduleDetailCoilBind from './components/ScheduleDetailCoilBind'
|
||||
import { generateCoilNoPrefix } from "@/utils/coil/coilNo";
|
||||
import ContractSelect from "@/components/KLPService/ContractSelect";
|
||||
|
||||
@@ -391,6 +393,7 @@ export default {
|
||||
AbnormalForm,
|
||||
ContractSelect,
|
||||
PlanSheetViewer,
|
||||
ScheduleDetailCoilBind,
|
||||
},
|
||||
dicts: ['coil_quality_status', 'coil_abnormal_position', 'coil_abnormal_code', 'coil_abnormal_degree', 'coil_business_purpose'],
|
||||
data() {
|
||||
|
||||
@@ -30,9 +30,10 @@
|
||||
<!-- 右侧:更新表单 -->
|
||||
<div>
|
||||
<el-card class="form-card">
|
||||
<div slot="header" class="card-header">
|
||||
<div slot="header" class="card-header">
|
||||
<span><i class="el-icon-edit-outline"></i> {{ '更新信息' }}</span>
|
||||
<div>
|
||||
<schedule-detail-coil-bind />
|
||||
<el-button size="small" @click="saveTemp" :loading="loading">暂存内容</el-button>
|
||||
<el-button type="primary" size="small" @click="handleSave" :loading="loading">保存更新</el-button>
|
||||
</div>
|
||||
@@ -405,6 +406,7 @@ import ContractSelect from "@/components/KLPService/ContractSelect";
|
||||
import L2MatchPanel from './panels/L2MatchPanel.vue'
|
||||
import DrMatchPanel from './panels/DrMatchPanel.vue';
|
||||
import PlanSheetViewer from './components/PlanSheetViewer.vue';
|
||||
import ScheduleDetailCoilBind from './components/ScheduleDetailCoilBind'
|
||||
|
||||
|
||||
// actionType -> 产线名称映射
|
||||
@@ -431,6 +433,7 @@ export default {
|
||||
L2MatchPanel,
|
||||
DrMatchPanel,
|
||||
PlanSheetViewer,
|
||||
ScheduleDetailCoilBind,
|
||||
},
|
||||
dicts: ['coil_quality_status', 'coil_abnormal_position', 'coil_abnormal_code', 'coil_abnormal_degree', 'coil_business_purpose'],
|
||||
data() {
|
||||
|
||||
Reference in New Issue
Block a user