feat(aps): 新增排产单明细合并功能及优化界面展示
- 在排产单明细表格中添加多选合并功能 - 实现排产单明细合并对话框及合并逻辑 - 优化排产单明细表格列配置和表单布局 - 添加合并校验和接收产需单API接口 - 重构订单绑定解绑逻辑提升用户体验 - 添加ScheduleDetailCoilBind组件引入
This commit is contained in:
27
klp-ui/src/api/aps/detailCoilRel.js
Normal file
27
klp-ui/src/api/aps/detailCoilRel.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询排产明细-钢卷绑定关系列表
|
||||
export function listDetailCoilRel(query) {
|
||||
return request({
|
||||
url: '/flow/detailCoilRel/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 批量新增绑定关系
|
||||
export function batchAddDetailCoilRel(data) {
|
||||
return request({
|
||||
url: '/flow/detailCoilRel/batch',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除绑定关系
|
||||
export function delDetailCoilRel(relIds) {
|
||||
return request({
|
||||
url: '/flow/detailCoilRel/' + relIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -70,3 +70,32 @@ export function delScheduleItem(ids) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// ====== 排产单明细项 接收/合并 ======
|
||||
|
||||
// 接收产需单(后端全字段复制)
|
||||
export function receiveScheduleItem(scheduleId) {
|
||||
return request({
|
||||
url: '/flow/prodScheduleItem/receive',
|
||||
method: 'post',
|
||||
data: { scheduleId }
|
||||
})
|
||||
}
|
||||
|
||||
// 合并验证(检查源排产单字段一致性)
|
||||
export function validateMerge(data) {
|
||||
return request({
|
||||
url: '/flow/prodScheduleItem/mergeValidate',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 合并排产单明细项
|
||||
export function mergeScheduleItem(data) {
|
||||
return request({
|
||||
url: '/flow/prodScheduleItem/merge',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
<div v-for="order in currentReq.orderList" :key="order.orderId" class="bound-order-card">
|
||||
<div class="bound-order-card-header">
|
||||
<span>{{ order.orderCode || ('订单 #' + order.orderId) }}</span>
|
||||
<button v-if="canEdit" class="link-btn" @click="handleUnbindByOrderId(order.orderId)">解绑</button>
|
||||
<button v-if="canEdit" class="link-btn" @click="handleUnbindByOrderId(order)">解绑</button>
|
||||
</div>
|
||||
<div class="bound-order-card-body">
|
||||
<div style="font-size:12px; color:#7f8c8d; margin-bottom:3px;">
|
||||
@@ -399,6 +399,7 @@ import {
|
||||
delRequirement,
|
||||
addRel,
|
||||
delRel,
|
||||
listRel,
|
||||
addRequirementDetail,
|
||||
updateRequirementDetail,
|
||||
delRequirementDetail
|
||||
@@ -628,7 +629,8 @@ export default {
|
||||
searchBindOrders() {
|
||||
this.bindSearchLoading = true
|
||||
listCrmOrder(this.bindQuery).then(res => {
|
||||
this.candidateOrderList = res.rows || []
|
||||
const boundOrderIds = (this.currentReq?.orderList || []).map(o => o.orderId)
|
||||
this.candidateOrderList = (res.rows || []).filter(o => !boundOrderIds.includes(o.orderId))
|
||||
}).catch(() => {
|
||||
this.candidateOrderList = []
|
||||
}).finally(() => {
|
||||
@@ -672,65 +674,87 @@ export default {
|
||||
}
|
||||
this.bindBtnLoading = true
|
||||
const scheduleId = this.currentReq.scheduleId
|
||||
const allPromises = []
|
||||
|
||||
this.selectedBindOrders.forEach(order => {
|
||||
// 1. 创建订单关联
|
||||
allPromises.push(
|
||||
addRel({
|
||||
orderId: order.orderId,
|
||||
scheduleId: scheduleId,
|
||||
relWeight: null,
|
||||
remark: ''
|
||||
})
|
||||
)
|
||||
|
||||
// 2. 解析 productContent,每行产品创建一条排产明细
|
||||
if (order.productContent) {
|
||||
const parsed = parseProductContent(order.productContent)
|
||||
const products = parsed.products || []
|
||||
const productType = parsed.productName || ''
|
||||
products.forEach(prod => {
|
||||
allPromises.push(
|
||||
addRequirementDetail({
|
||||
scheduleId: scheduleId,
|
||||
orderDetailId: order.orderId,
|
||||
spec: prod.spec || '',
|
||||
material: prod.material || '',
|
||||
scheduleWeight: prod.quantity || 0,
|
||||
productType: productType,
|
||||
remark: prod.remark || ''
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
const orderTasks = this.selectedBindOrders.map(order => {
|
||||
return addRel({
|
||||
orderId: order.orderId,
|
||||
scheduleId: scheduleId,
|
||||
relWeight: null,
|
||||
remark: ''
|
||||
}).then(() => {
|
||||
if (!order.productContent) return
|
||||
let products = []
|
||||
let productType = ''
|
||||
try {
|
||||
const parsed = parseProductContent(order.productContent)
|
||||
products = (parsed.products || []).filter(p => p.spec && p.material)
|
||||
productType = parsed.productName || ''
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
if (products.length === 0) return
|
||||
const detailPromises = products.map(prod =>
|
||||
addRequirementDetail({
|
||||
scheduleId: scheduleId,
|
||||
orderDetailId: order.orderId,
|
||||
spec: prod.spec,
|
||||
material: prod.material,
|
||||
scheduleWeight: prod.quantity || 0,
|
||||
productType: productType,
|
||||
remark: prod.remark || ''
|
||||
})
|
||||
)
|
||||
return Promise.all(detailPromises)
|
||||
})
|
||||
})
|
||||
|
||||
Promise.all(allPromises).then(() => {
|
||||
this.$modal.msgSuccess('绑定成功,排产明细已生成')
|
||||
Promise.allSettled(orderTasks).then(results => {
|
||||
const successCount = results.filter(r => r.status === 'fulfilled').length
|
||||
const failCount = results.filter(r => r.status === 'rejected').length
|
||||
this.bindDialogVisible = false
|
||||
this.handleReqClick(this.currentReq)
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('绑定或创建明细失败')
|
||||
if (failCount === 0) {
|
||||
this.$modal.msgSuccess(`成功绑定 ${successCount} 个订单,排产明细已生成`)
|
||||
} else if (successCount === 0) {
|
||||
this.$modal.msgError('绑定失败,请重试')
|
||||
} else {
|
||||
this.$modal.msgWarning(`成功绑定 ${successCount} 个,${failCount} 个失败`)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.bindBtnLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleUnbindByOrderId(orderId) {
|
||||
const order = (this.currentReq.orderList || []).find(o => o.orderId === orderId)
|
||||
if (!order) {
|
||||
this.$message.warning('未找到关联记录')
|
||||
return
|
||||
}
|
||||
this.$confirm('确认解绑该订单吗?', '提示', {
|
||||
handleUnbindByOrderId(order) {
|
||||
this.$confirm('确认解绑该订单吗?解绑将同时删除对应的排产明细。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delRel(order.relId || orderId).then(() => {
|
||||
const tasks = []
|
||||
// 1. 删除该订单关联的排产明细
|
||||
const detailIds = this.requirementDetailList
|
||||
.filter(d => d.orderDetailId === order.orderId)
|
||||
.map(d => d.scheduleDetailId)
|
||||
detailIds.forEach(id => {
|
||||
tasks.push(delRequirementDetail(id))
|
||||
})
|
||||
// 2. 查询并删除关联表记录
|
||||
tasks.push(
|
||||
listRel({ orderId: order.orderId, scheduleId: this.currentReq.scheduleId }).then(res => {
|
||||
const rows = res.rows || []
|
||||
if (rows.length === 0) {
|
||||
throw new Error('未找到关联记录')
|
||||
}
|
||||
const relId = rows[0].id || rows[0].relId
|
||||
return delRel(relId)
|
||||
})
|
||||
)
|
||||
Promise.all(tasks).then(() => {
|
||||
this.$modal.msgSuccess('解绑成功')
|
||||
this.handleReqClick(this.currentReq)
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('解绑失败')
|
||||
})
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
@@ -17,22 +17,22 @@
|
||||
<el-tab-pane label="待审核" name="pending" />
|
||||
<el-tab-pane label="已排产" name="scheduled" />
|
||||
</el-tabs>
|
||||
<div class="aps-sch-summary" v-if="summaryText">
|
||||
<div v-if="summaryText" class="aps-sch-summary">
|
||||
<span>{{ summaryText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 待审核 Tab -->
|
||||
<div class="detail-card aps-sch-card" v-show="activeTab === 'pending'">
|
||||
<div v-show="activeTab === 'pending'" class="detail-card aps-sch-card">
|
||||
<div class="detail-card-header">
|
||||
<span>待审核产需单明细</span>
|
||||
<span v-if="pendingScheduleList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
||||
共 {{ pendingScheduleList.length }} 个产需单
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-card-body" style="padding:0;" v-loading="loading">
|
||||
<div v-loading="loading" class="detail-card-body" style="padding:0;">
|
||||
<div v-if="pendingScheduleList.length > 0" class="aps-sch-list">
|
||||
<div class="aps-sch-item" v-for="sch in pendingScheduleList" :key="sch.scheduleId">
|
||||
<div v-for="sch in pendingScheduleList" :key="sch.scheduleId" class="aps-sch-item">
|
||||
<!-- 产需单头部:排产单号 + 状态标签 + 操作 -->
|
||||
<div class="aps-sch-item-header">
|
||||
<div class="aps-sch-item-header-left">
|
||||
@@ -131,7 +131,7 @@
|
||||
</table>
|
||||
|
||||
<!-- 明细列表 -->
|
||||
<div class="aps-sch-item-details" v-if="(sch.detailList || []).length > 0">
|
||||
<div v-if="(sch.detailList || []).length > 0" class="aps-sch-item-details">
|
||||
<div class="aps-sch-detail-header">
|
||||
<span class="aps-sch-detail-col col-spec">规格</span>
|
||||
<span class="aps-sch-detail-col col-material">材质</span>
|
||||
@@ -140,9 +140,9 @@
|
||||
<span class="aps-sch-detail-col col-remark">备注</span>
|
||||
</div>
|
||||
<div
|
||||
class="aps-sch-detail-row"
|
||||
v-for="(d, di) in sch.detailList"
|
||||
:key="di"
|
||||
class="aps-sch-detail-row"
|
||||
@click="handleDetailClick(sch, d)"
|
||||
>
|
||||
<span class="aps-sch-detail-col col-spec">{{ d.spec }}</span>
|
||||
@@ -162,34 +162,84 @@
|
||||
</div>
|
||||
|
||||
<!-- 已排产 Tab -->
|
||||
<div class="detail-card aps-sch-card" v-show="activeTab === 'scheduled'">
|
||||
<div v-show="activeTab === 'scheduled'" class="detail-card aps-sch-card">
|
||||
<div class="detail-card-header">
|
||||
<span>已排产明细</span>
|
||||
<span v-if="scheduledItemList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
||||
共 {{ scheduledItemList.length }} 条
|
||||
</span>
|
||||
<div style="display:flex; align-items:center; gap:8px;">
|
||||
<span v-if="scheduledItemList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
||||
共 {{ scheduledItemList.length }} 条
|
||||
</span>
|
||||
<span v-if="selectedItems.length >= 2" style="margin-left: auto;">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="warning"
|
||||
icon="el-icon-link"
|
||||
@click.stop="handleMergePrepare"
|
||||
>
|
||||
合并选中项 ({{ selectedItems.length }})
|
||||
</el-button>
|
||||
</span>
|
||||
<span v-else style="margin-left: auto;">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="warning"
|
||||
icon="el-icon-link"
|
||||
disabled
|
||||
>
|
||||
合并选中项
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-card-body" style="padding:0;" v-loading="schLoading">
|
||||
<div v-loading="schLoading" class="detail-card-body" style="padding:0;">
|
||||
<el-table
|
||||
v-if="scheduledItemList.length > 0"
|
||||
ref="scheduledTable"
|
||||
:data="scheduledItemList"
|
||||
border
|
||||
size="small"
|
||||
class="aps-table"
|
||||
:row-class-name="getItemRowClassName"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="140" />
|
||||
<el-table-column label="规格" prop="spec" min-width="120" />
|
||||
<el-table-column label="材质" prop="material" width="90" align="center" />
|
||||
<el-table-column label="排产吨数" prop="scheduleWeight" width="100" align="right" />
|
||||
<el-table-column label="品名" prop="productType" min-width="100" />
|
||||
<el-table-column label="订货单位" prop="customerName" min-width="140" />
|
||||
<el-table-column label="备注" prop="remark" min-width="100" />
|
||||
<el-table-column label="操作" width="110" align="center" fixed="right">
|
||||
<el-table-column type="selection" width="45" align="center" />
|
||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="生产日期" prop="prodDate" width="110" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="排产状态" prop="scheduleStatus" width="90" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span style="white-space:nowrap;">
|
||||
<el-button type="text" size="small" style="color:#409EFF;" @click="handleEditScheduled(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="small" style="color:#ff4d4f;" @click="handleDeleteScheduled(scope.row)">删除</el-button>
|
||||
</span>
|
||||
<span class="aps-status-tag" :class="'status-' + (scope.row.scheduleStatus || 1)">{{ statusMap[scope.row.scheduleStatus] || '未知' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格" prop="spec" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="材质" prop="material" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="排产吨数" prop="scheduleWeight" width="100" align="right" show-overflow-tooltip />
|
||||
<el-table-column label="品名项" prop="productItem" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="品名" prop="productType" min-width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="订货单位" prop="customerName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="业务员" prop="businessUser" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="联系电话" prop="businessPhone" width="110" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="交货期(天)" prop="deliveryCycle" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="产品用途" prop="usePurpose" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="厚度公差" prop="thicknessTolerance" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="宽度公差" prop="widthTolerance" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="表面质量" prop="surfaceQuality" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="表面处理" prop="surfaceTreatment" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="内径尺寸" prop="innerDiameter" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="外径要求" prop="outerDiameter" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="包装要求" prop="packReq" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="切边要求" prop="cutEdgeReq" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="单件重量" prop="singleCoilWeight" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="交货重量偏差" prop="weightDeviation" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="其他技术要求" prop="otherTechReq" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="付款情况说明" prop="paymentDesc" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="单行排产备注" prop="rowRemark" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="备注" prop="remark" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<div style="display:flex; justify-content:center; gap:0;">
|
||||
<el-button type="text" size="small" style="color:#409EFF;padding:0 6px;" @click="handleEditScheduled(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="small" style="color:#ff4d4f;padding:0 6px;" @click="handleDeleteScheduled(scope.row)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -237,33 +287,329 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 已排产明细编辑对话框 -->
|
||||
<el-dialog :title="editDialogTitle" :visible.sync="editDialogVisible" width="500px" append-to-body
|
||||
:close-on-click-modal="false">
|
||||
<el-form ref="editForm" :model="editForm" label-width="100px" size="small">
|
||||
<el-form-item label="排产单号">
|
||||
<span style="color:#2c3e50;">{{ editForm.scheduleNo }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="editForm.spec" />
|
||||
</el-form-item>
|
||||
<el-form-item label="材质" prop="material">
|
||||
<el-input v-model="editForm.material" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排产吨数" prop="scheduleWeight">
|
||||
<el-input-number v-model="editForm.scheduleWeight" :min="0" :precision="3" :controls="false" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="品名" prop="productType">
|
||||
<el-input v-model="editForm.productType" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="editForm.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
<el-dialog
|
||||
:title="editDialogTitle"
|
||||
:visible.sync="editDialogVisible"
|
||||
width="800px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="editForm" :model="editForm" label-width="110px" size="small">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排产单号">
|
||||
<span style="color:#2c3e50;font-weight:500;">{{ editForm.scheduleNo }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="订货单位">
|
||||
<el-input v-model="editForm.customerName" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="editForm.spec" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="材质" prop="material">
|
||||
<el-input v-model="editForm.material" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排产吨数">
|
||||
<el-input-number v-model="editForm.scheduleWeight" :min="0" :precision="3" :controls="false" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排产总计划吨数">
|
||||
<el-input-number v-model="editForm.totalPlanWeight" :min="0" :precision="3" :controls="false" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="品名">
|
||||
<el-input v-model="editForm.productType" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="品名项">
|
||||
<el-input v-model="editForm.productItem" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="业务员">
|
||||
<el-input v-model="editForm.businessUser" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="editForm.businessPhone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- <el-row :gutter="16">-->
|
||||
<!-- <el-col :span="12">-->
|
||||
<!-- <el-form-item label="关联销售合同号">-->
|
||||
<!-- <el-input v-model="editForm.relContractNo" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="12">-->
|
||||
<!-- <el-form-item label="订单日期">-->
|
||||
<!-- <el-date-picker v-model="editForm.orderDate" type="date" value-format="yyyy-MM-dd" style="width:100%" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="交货期(天)">
|
||||
<el-input-number v-model="editForm.deliveryCycle" :min="0" :controls="false" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="产品用途">
|
||||
<el-input v-model="editForm.usePurpose" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="厚度公差">
|
||||
<el-input v-model="editForm.thicknessTolerance" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="宽度公差">
|
||||
<el-input v-model="editForm.widthTolerance" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="表面质量">
|
||||
<el-input v-model="editForm.surfaceQuality" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="表面处理">
|
||||
<el-input v-model="editForm.surfaceTreatment" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="内径尺寸">
|
||||
<el-input v-model="editForm.innerDiameter" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="外径要求">
|
||||
<el-input v-model="editForm.outerDiameter" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="包装要求">
|
||||
<el-input v-model="editForm.packReq" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="切边要求">
|
||||
<el-input v-model="editForm.cutEdgeReq" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单件重量">
|
||||
<el-input v-model="editForm.singleCoilWeight" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="交货重量偏差">
|
||||
<el-input v-model="editForm.weightDeviation" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="其他技术要求">
|
||||
<el-input v-model="editForm.otherTechReq" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="付款情况说明">
|
||||
<el-input v-model="editForm.paymentDesc" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单行排产备注">
|
||||
<el-input v-model="editForm.rowRemark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="editForm.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="editBtnLoading" type="danger" @click="submitEditForm">确 定</el-button>
|
||||
<el-button @click="editDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 合并排产明细对话框 -->
|
||||
<el-dialog
|
||||
title="合并排产明细"
|
||||
:visible.sync="mergeDialogVisible"
|
||||
width="850px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div style="margin-bottom:12px;font-size:13px;color:#e67e22;">
|
||||
选中 {{ mergeForm.itemCount }} 条明细,请选择一个作为合并模板,下方字段将自动填充,您可修改后确认合并。
|
||||
</div>
|
||||
|
||||
<!-- 选择模板 -->
|
||||
<el-table :data="mergeSourceRows" border size="small" style="margin-bottom:12px;" @row-click="pickMergeTemplate">
|
||||
<el-table-column width="50" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-radio v-model="mergeTemplateIndex" :label="scope.$index" @click.native.stop />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="规格" prop="spec" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="材质" prop="material" width="80" show-overflow-tooltip />
|
||||
<el-table-column label="排产吨数" prop="scheduleWeight" width="90" align="right" />
|
||||
<el-table-column label="品名" prop="productType" min-width="80" show-overflow-tooltip />
|
||||
<el-table-column label="订货单位" prop="customerName" min-width="120" show-overflow-tooltip />
|
||||
</el-table>
|
||||
|
||||
<!-- 合并表单 -->
|
||||
<el-form ref="mergeFormRef" :model="mergeForm" label-width="110px" size="small">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排产单号"><el-input v-model="mergeForm.scheduleNo" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="订货单位"><el-input v-model="mergeForm.customerName" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="规格"><el-input v-model="mergeForm.spec" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="材质"><el-input v-model="mergeForm.material" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排产吨数(累加)">
|
||||
<el-input-number v-model="mergeForm.scheduleWeight" :min="0" :precision="3" :controls="false" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="品名"><el-input v-model="mergeForm.productType" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="品名项"><el-input v-model="mergeForm.productItem" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="业务员"><el-input v-model="mergeForm.businessUser" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话"><el-input v-model="mergeForm.businessPhone" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="交货期(天)">
|
||||
<el-input-number v-model="mergeForm.deliveryCycle" :min="0" :controls="false" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="产品用途"><el-input v-model="mergeForm.usePurpose" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="厚度公差"><el-input v-model="mergeForm.thicknessTolerance" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="宽度公差"><el-input v-model="mergeForm.widthTolerance" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="表面质量"><el-input v-model="mergeForm.surfaceQuality" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="表面处理"><el-input v-model="mergeForm.surfaceTreatment" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="内径尺寸"><el-input v-model="mergeForm.innerDiameter" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="外径要求"><el-input v-model="mergeForm.outerDiameter" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="包装要求"><el-input v-model="mergeForm.packReq" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="切边要求"><el-input v-model="mergeForm.cutEdgeReq" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单件重量"><el-input v-model="mergeForm.singleCoilWeight" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="交货重量偏差"><el-input v-model="mergeForm.weightDeviation" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="其他技术要求"><el-input v-model="mergeForm.otherTechReq" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="付款情况说明"><el-input v-model="mergeForm.paymentDesc" /></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="备注"><el-input v-model="mergeForm.remark" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div style="margin-top:8px;font-size:12px;color:#909399;">
|
||||
合并后 schedule_detail_ids:{{ mergeForm.scheduleDetailIds }}
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="mergeBtnLoading" type="warning" @click="confirmMerge">确 定 合 并</el-button>
|
||||
<el-button @click="mergeDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -275,9 +621,10 @@ import {
|
||||
import {
|
||||
getCrmOrderInfo,
|
||||
listScheduleItem,
|
||||
addScheduleItem,
|
||||
updateScheduleItem,
|
||||
delScheduleItem
|
||||
delScheduleItem,
|
||||
receiveScheduleItem,
|
||||
mergeScheduleItem
|
||||
} from '@/api/aps/schedule'
|
||||
|
||||
export default {
|
||||
@@ -301,6 +648,24 @@ export default {
|
||||
|
||||
// 已排产
|
||||
scheduledItemList: [],
|
||||
selectedItems: [],
|
||||
sourceColorMap: {},
|
||||
|
||||
// 合并对话框
|
||||
mergeDialogVisible: false,
|
||||
mergeBtnLoading: false,
|
||||
mergeTemplateIndex: 0,
|
||||
mergeSourceRows: [],
|
||||
mergeForm: {
|
||||
itemCount: 0, scheduleNo: '', customerName: '', spec: '', material: '',
|
||||
scheduleWeight: 0, productType: '', productItem: '', businessUser: '',
|
||||
businessPhone: '', deliveryCycle: undefined, usePurpose: '',
|
||||
thicknessTolerance: '', widthTolerance: '', surfaceQuality: '',
|
||||
surfaceTreatment: '', innerDiameter: '', outerDiameter: '',
|
||||
packReq: '', cutEdgeReq: '', singleCoilWeight: '',
|
||||
weightDeviation: '', otherTechReq: '', paymentDesc: '',
|
||||
remark: '', scheduleDetailIds: ''
|
||||
},
|
||||
|
||||
// 驳回
|
||||
rejectDialogVisible: false,
|
||||
@@ -322,20 +687,47 @@ export default {
|
||||
drillOrder: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeTab() {
|
||||
this.selectedItems = []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.handleQuery()
|
||||
},
|
||||
methods: {
|
||||
getEmptyEditForm() {
|
||||
return {
|
||||
itemId: undefined,
|
||||
scheduleId: undefined,
|
||||
scheduleNo: '',
|
||||
prodDate: '',
|
||||
scheduleStatus: undefined,
|
||||
totalPlanWeight: undefined,
|
||||
relContractNo: '',
|
||||
businessUser: '',
|
||||
businessPhone: '',
|
||||
orderDate: '',
|
||||
customerName: '',
|
||||
deliveryCycle: undefined,
|
||||
usePurpose: '',
|
||||
productType: '',
|
||||
thicknessTolerance: '',
|
||||
widthTolerance: '',
|
||||
surfaceQuality: '',
|
||||
surfaceTreatment: '',
|
||||
innerDiameter: '',
|
||||
outerDiameter: '',
|
||||
packReq: '',
|
||||
cutEdgeReq: '',
|
||||
singleCoilWeight: '',
|
||||
weightDeviation: '',
|
||||
otherTechReq: '',
|
||||
paymentDesc: '',
|
||||
spec: '',
|
||||
material: '',
|
||||
scheduleWeight: 0,
|
||||
productType: '',
|
||||
customerName: '',
|
||||
productItem: '',
|
||||
rowRemark: '',
|
||||
remark: ''
|
||||
}
|
||||
},
|
||||
@@ -386,40 +778,18 @@ export default {
|
||||
},
|
||||
|
||||
handleAccept(sch) {
|
||||
const details = sch.detailList || []
|
||||
if (details.length === 0) {
|
||||
this.$message.warning('该产需单无可排产的明细')
|
||||
return
|
||||
}
|
||||
this.$confirm(
|
||||
`确认接收产需单「${sch.scheduleNo}」的全部排产明细吗?`,
|
||||
`确认接收产需单「${sch.scheduleNo}」的全部排产明细吗?共 ${details.length} 条明细`,
|
||||
'提示',
|
||||
{ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||
).then(() => {
|
||||
const details = sch.detailList || []
|
||||
if (details.length === 0) {
|
||||
this.$message.warning('该产需单无可排产的明细')
|
||||
return
|
||||
}
|
||||
|
||||
const promises = details.map(detail =>
|
||||
addScheduleItem({
|
||||
scheduleId: sch.scheduleId,
|
||||
scheduleNo: sch.scheduleNo,
|
||||
spec: detail.spec || '',
|
||||
material: detail.material || '',
|
||||
scheduleWeight: detail.scheduleWeight || 0,
|
||||
productType: sch.productType || detail.productType || '',
|
||||
customerName: sch.customerName || '',
|
||||
remark: detail.remark || ''
|
||||
})
|
||||
)
|
||||
|
||||
// 更新产需单状态为 2(已接收)
|
||||
promises.push(
|
||||
updateRequirement({
|
||||
scheduleId: sch.scheduleId,
|
||||
scheduleStatus: 2
|
||||
})
|
||||
)
|
||||
|
||||
this.$message.info('正在处理接收...')
|
||||
Promise.all(promises).then(() => {
|
||||
receiveScheduleItem(sch.scheduleId).then(() => {
|
||||
this.$modal.msgSuccess('接收成功,排产明细已写入')
|
||||
this.queryPending()
|
||||
}).catch(() => {
|
||||
@@ -463,12 +833,24 @@ export default {
|
||||
queryScheduled() {
|
||||
this.schLoading = true
|
||||
this.scheduledItemList = []
|
||||
this.sourceColorMap = {}
|
||||
this.selectedItems = []
|
||||
|
||||
listScheduleItem({ prodDate: this.queryDate, pageNum: 1, pageSize: 999 }).then(res => {
|
||||
this.scheduledItemList = res.rows || []
|
||||
const totalWeight = this.scheduledItemList.reduce((sum, d) => sum + (parseFloat(d.scheduleWeight) || 0), 0)
|
||||
this.scheduledItemList = (res.rows || []).sort((a, b) => (a.scheduleNo || '').localeCompare(b.scheduleNo || ''))
|
||||
try {
|
||||
this.sourceColorMap = this.buildGroupColorMap(this.scheduledItemList)
|
||||
} catch (e) {
|
||||
console.error('buildGroupColorMap error:', e)
|
||||
this.sourceColorMap = {}
|
||||
}
|
||||
const totalWeight = this.scheduledItemList.reduce((sum, d) => {
|
||||
const w = parseFloat(d.scheduleWeight)
|
||||
return sum + (isNaN(w) ? 0 : w)
|
||||
}, 0)
|
||||
this.summaryText = `共 ${this.scheduledItemList.length} 条明细,排产总吨数 ${totalWeight.toFixed(3)} 吨`
|
||||
}).catch(() => {
|
||||
}).catch((e) => {
|
||||
console.error('queryScheduled error:', e)
|
||||
this.scheduledItemList = []
|
||||
this.summaryText = ''
|
||||
}).finally(() => {
|
||||
@@ -504,7 +886,7 @@ export default {
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delScheduleItem(row.itemId || row.scheduleItemId).then(() => {
|
||||
delScheduleItem(row.scheduleId).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.queryScheduled()
|
||||
}).catch(() => {
|
||||
@@ -513,6 +895,97 @@ export default {
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// ====== 合并 ======
|
||||
handleSelectionChange(rows) {
|
||||
this.selectedItems = rows
|
||||
},
|
||||
|
||||
getItemRowClassName({ row }) {
|
||||
const key = row.scheduleNo
|
||||
if (!key) return ''
|
||||
const colorIndex = this.sourceColorMap[key]
|
||||
return colorIndex !== undefined ? `merge-source-${colorIndex}` : ''
|
||||
},
|
||||
|
||||
buildGroupColorMap(list) {
|
||||
const map = {}
|
||||
let colorIdx = 0
|
||||
list.forEach(item => {
|
||||
const key = item.scheduleNo
|
||||
if (!key) return
|
||||
if (!(key in map)) {
|
||||
map[key] = colorIdx % 5
|
||||
colorIdx++
|
||||
}
|
||||
})
|
||||
return map
|
||||
},
|
||||
|
||||
handleMergePrepare() {
|
||||
if (this.selectedItems.length < 2) {
|
||||
this.$message.warning('请至少选择2条排产明细进行合并')
|
||||
return
|
||||
}
|
||||
this.mergeSourceRows = [...this.selectedItems]
|
||||
this.mergeTemplateIndex = 0
|
||||
this.pickMergeTemplate(this.mergeSourceRows[0])
|
||||
this.mergeDialogVisible = true
|
||||
},
|
||||
|
||||
pickMergeTemplate(row) {
|
||||
const idx = this.mergeSourceRows.indexOf(row)
|
||||
if (idx >= 0) this.mergeTemplateIndex = idx
|
||||
this.mergeForm = {
|
||||
itemCount: this.mergeSourceRows.length,
|
||||
scheduleNo: row.scheduleNo || '',
|
||||
customerName: row.customerName || '',
|
||||
spec: row.spec || '',
|
||||
material: row.material || '',
|
||||
scheduleWeight: this.mergeSourceRows.reduce((sum, r) => sum + (parseFloat(r.scheduleWeight) || 0), 0),
|
||||
productType: row.productType || '',
|
||||
productItem: row.productItem || '',
|
||||
businessUser: row.businessUser || '',
|
||||
businessPhone: row.businessPhone || '',
|
||||
deliveryCycle: row.deliveryCycle,
|
||||
usePurpose: row.usePurpose || '',
|
||||
thicknessTolerance: row.thicknessTolerance || '',
|
||||
widthTolerance: row.widthTolerance || '',
|
||||
surfaceQuality: row.surfaceQuality || '',
|
||||
surfaceTreatment: row.surfaceTreatment || '',
|
||||
innerDiameter: row.innerDiameter || '',
|
||||
outerDiameter: row.outerDiameter || '',
|
||||
packReq: row.packReq || '',
|
||||
cutEdgeReq: row.cutEdgeReq || '',
|
||||
singleCoilWeight: row.singleCoilWeight || '',
|
||||
weightDeviation: row.weightDeviation || '',
|
||||
otherTechReq: row.otherTechReq || '',
|
||||
paymentDesc: row.paymentDesc || '',
|
||||
remark: row.remark || '',
|
||||
scheduleDetailIds: this.mergeSourceRows.map(r => r.scheduleDetailIds || '').filter(Boolean).join(',')
|
||||
}
|
||||
},
|
||||
|
||||
confirmMerge() {
|
||||
this.mergeBtnLoading = true
|
||||
const ids = this.mergeSourceRows.map(r => r.scheduleId)
|
||||
const mergedBo = {
|
||||
...this.mergeForm,
|
||||
prodDate: this.queryDate,
|
||||
scheduleStatus: 2
|
||||
}
|
||||
delete mergedBo.itemCount
|
||||
mergeScheduleItem({ ids, mergedBo }).then(() => {
|
||||
this.$modal.msgSuccess(`合并成功:${ids.length} 条明细合并为 1 条`)
|
||||
this.mergeDialogVisible = false
|
||||
this.selectedItems = []
|
||||
this.queryScheduled()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('合并失败')
|
||||
}).finally(() => {
|
||||
this.mergeBtnLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
// ====== 下钻 & 辅助方法 ======
|
||||
scheduleTotalWeight(sch) {
|
||||
const details = sch.detailList || []
|
||||
@@ -594,6 +1067,7 @@ export default {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,4 +1304,21 @@ export default {
|
||||
color: $aps-text-muted;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
// ====== 合并候选行颜色高亮(同一源排产单 → 同色) ======
|
||||
::v-deep .el-table__body tr.merge-source-0 > td {
|
||||
background-color: #f0f5ff !important;
|
||||
}
|
||||
::v-deep .el-table__body tr.merge-source-1 > td {
|
||||
background-color: #f6ffed !important;
|
||||
}
|
||||
::v-deep .el-table__body tr.merge-source-2 > td {
|
||||
background-color: #fff7e6 !important;
|
||||
}
|
||||
::v-deep .el-table__body tr.merge-source-3 > td {
|
||||
background-color: #f9f0ff !important;
|
||||
}
|
||||
::v-deep .el-table__body tr.merge-source-4 > td {
|
||||
background-color: #fff0f6 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user