feat(aps): 新增排产单明细合并功能及优化界面展示

- 在排产单明细表格中添加多选合并功能
- 实现排产单明细合并对话框及合并逻辑
- 优化排产单明细表格列配置和表单布局
- 添加合并校验和接收产需单API接口
- 重构订单绑定解绑逻辑提升用户体验
- 添加ScheduleDetailCoilBind组件引入
This commit is contained in:
2026-06-29 16:07:29 +08:00
parent da01bfaa48
commit ee376f922f
19 changed files with 1547 additions and 154 deletions

View File

@@ -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(() => { })
},

View File

@@ -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>