refactor(盘库流程): 重构盘库流程页面与组件,完善排产明细功能
1. 重构盘库流程的步骤与状态映射,调整流程节点顺序与名称 2. 拆分通用盘库详情组件PlanDetailPanel,复用各流程页面 3. 新增计划审批、盘库执行页面,完善差异审批页面 4. 为排产单明细添加增删改查API与前端操作功能 5. 为排产日期添加格式化注解,完善参数接收格式
This commit is contained in:
@@ -57,7 +57,10 @@
|
||||
<div class="detail-card">
|
||||
<div class="detail-card-header">
|
||||
<span>产需单信息</span>
|
||||
<button class="header-btn" @click="handleEdit(currentReq)">编辑</button>
|
||||
<div style="display:flex; gap:6px;">
|
||||
<button class="header-btn" :disabled="currentReq.scheduleStatus !== 0 && currentReq.scheduleStatus !== 3" @click="handleEdit(currentReq)">编辑</button>
|
||||
<button v-if="currentReq.scheduleStatus === 0 || currentReq.scheduleStatus === 3" class="header-btn" style="background:rgba(255,255,255,0.4);border-color:rgba(255,255,255,0.7);" @click="handleDispatch">{{ currentReq.scheduleStatus === 3 ? '重新下发' : '下发' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-card-body">
|
||||
<table class="req-info-table">
|
||||
@@ -134,6 +137,10 @@
|
||||
<td class="req-td-label">备注</td>
|
||||
<td class="req-td-value" colspan="3">{{ currentReq.remark }}</td>
|
||||
</tr>
|
||||
<tr v-if="currentReq.returnReason">
|
||||
<td class="req-td-label" style="color:#e74c3c;">驳回原因</td>
|
||||
<td class="req-td-value" colspan="3" style="color:#e74c3c;background:#fdecea;">{{ currentReq.returnReason }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -141,8 +148,8 @@
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card-header">
|
||||
<span>已绑定的销售订单({{ boundOrderList.length }} 条)</span>
|
||||
<button class="header-btn" @click="openBindDialog">+ 添加订单</button>
|
||||
<span>已绑定的销售订单({{ (currentReq.orderList || []).length }} 条)</span>
|
||||
<button v-if="canEdit" class="header-btn" @click="openBindDialog">+ 添加订单</button>
|
||||
</div>
|
||||
<div class="detail-card-body" v-loading="boundLoading">
|
||||
<div v-if="(currentReq.orderList || []).length === 0"
|
||||
@@ -151,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 class="link-btn" @click="handleUnbindByOrderId(order.orderId)">解绑</button>
|
||||
<button v-if="canEdit" class="link-btn" @click="handleUnbindByOrderId(order.orderId)">解绑</button>
|
||||
</div>
|
||||
<div class="bound-order-card-body">
|
||||
<div style="font-size:12px; color:#7f8c8d; margin-bottom:3px;">
|
||||
@@ -172,6 +179,7 @@
|
||||
<div class="detail-card">
|
||||
<div class="detail-card-header">
|
||||
<span>排产明细({{ requirementDetailList.length }} 条,合计 {{ detailTotalWeight }} T)</span>
|
||||
<button v-if="canEdit" class="header-btn" @click="handleDetailAdd">+ 新增</button>
|
||||
</div>
|
||||
<div class="detail-card-body" style="padding:0;">
|
||||
<el-table :data="requirementDetailList" border size="small" v-loading="detailLoading" class="aps-table">
|
||||
@@ -179,6 +187,14 @@
|
||||
<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="remark" min-width="120" />
|
||||
<el-table-column label="操作" width="130" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<span style="white-space:nowrap;">
|
||||
<el-button v-if="canEdit" type="text" size="small" style="color:#409EFF;" @click="handleDetailEdit(scope.row)">编辑</el-button>
|
||||
<el-button v-if="canEdit" type="text" size="small" style="color:#ff4d4f;" @click="handleDetailDelete(scope.row)">删除</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="requirementDetailList.length === 0 && !detailLoading" description="暂无排产明细" />
|
||||
</div>
|
||||
@@ -348,6 +364,29 @@
|
||||
<el-button @click="bindDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 排产明细新增/编辑对话框 -->
|
||||
<el-dialog :title="detailDialogTitle" :visible.sync="detailDialogVisible" width="500px" append-to-body
|
||||
:close-on-click-modal="false">
|
||||
<el-form ref="detailForm" :model="detailForm" :rules="detailFormRules" label-width="100px" size="small">
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="detailForm.spec" placeholder="请输入规格" />
|
||||
</el-form-item>
|
||||
<el-form-item label="材质" prop="material">
|
||||
<el-input v-model="detailForm.material" placeholder="请输入材质" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排产吨数" prop="scheduleWeight">
|
||||
<el-input-number v-model="detailForm.scheduleWeight" :min="0" :precision="3" :controls="false" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="detailForm.remark" type="textarea" :rows="2" placeholder="可选" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="detailBtnLoading" type="danger" @click="submitDetailForm">确 定</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -360,7 +399,9 @@ import {
|
||||
delRequirement,
|
||||
addRel,
|
||||
delRel,
|
||||
addRequirementDetail
|
||||
addRequirementDetail,
|
||||
updateRequirementDetail,
|
||||
delRequirementDetail
|
||||
} from '@/api/aps/requirement'
|
||||
import { listCrmOrder } from '@/api/aps/order'
|
||||
import { parseProductContent } from '@/utils/productContent'
|
||||
@@ -385,7 +426,6 @@ export default {
|
||||
statusBadgeMap: { 0: 'gray', 1: 'blue', 2: 'green', 3: 'red' },
|
||||
|
||||
// 绑定订单
|
||||
boundOrderList: [],
|
||||
bindDialogVisible: false,
|
||||
bindQuery: { keyword: '', pageNum: 1, pageSize: 50 },
|
||||
candidateOrderList: [],
|
||||
@@ -402,6 +442,17 @@ export default {
|
||||
detailLoading: false,
|
||||
requirementDetailList: [],
|
||||
|
||||
// 排产明细新增/编辑对话框
|
||||
detailDialogVisible: false,
|
||||
detailDialogTitle: '新增排产明细',
|
||||
detailBtnLoading: false,
|
||||
detailForm: this.getEmptyDetailForm(),
|
||||
detailFormRules: {
|
||||
spec: [{ required: true, message: '规格不能为空', trigger: 'blur' }],
|
||||
material: [{ required: true, message: '材质不能为空', trigger: 'blur' }],
|
||||
scheduleWeight: [{ required: true, message: '排产吨数不能为空', trigger: 'change' }]
|
||||
},
|
||||
|
||||
// 新增/编辑对话框
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增产需单',
|
||||
@@ -414,6 +465,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
canEdit() {
|
||||
return this.currentReq && (this.currentReq.scheduleStatus === 0 || this.currentReq.scheduleStatus === 3)
|
||||
},
|
||||
detailTotalWeight() {
|
||||
return this.requirementDetailList.reduce((sum, item) => {
|
||||
const n = Number(item.scheduleWeight)
|
||||
@@ -452,6 +506,19 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
getEmptyDetailForm() {
|
||||
return {
|
||||
scheduleDetailId: undefined,
|
||||
scheduleId: undefined,
|
||||
orderDetailId: undefined,
|
||||
spec: '',
|
||||
material: '',
|
||||
scheduleWeight: 0,
|
||||
productType: '',
|
||||
remark: ''
|
||||
}
|
||||
},
|
||||
|
||||
handleSearch() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
@@ -651,8 +718,8 @@ export default {
|
||||
},
|
||||
|
||||
handleUnbindByOrderId(orderId) {
|
||||
const rel = this.boundOrderList.find(r => r.orderId === orderId)
|
||||
if (!rel) {
|
||||
const order = (this.currentReq.orderList || []).find(o => o.orderId === orderId)
|
||||
if (!order) {
|
||||
this.$message.warning('未找到关联记录')
|
||||
return
|
||||
}
|
||||
@@ -661,12 +728,120 @@ export default {
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delRel(rel.relId).then(() => {
|
||||
delRel(order.relId || orderId).then(() => {
|
||||
this.$modal.msgSuccess('解绑成功')
|
||||
this.handleReqClick(this.currentReq)
|
||||
})
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
// ====== 下发 ======
|
||||
handleDispatch() {
|
||||
this.$confirm('确认下发该产需单吗?下发后将进入审核流程。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
updateRequirement({
|
||||
scheduleId: this.currentReq.scheduleId,
|
||||
scheduleStatus: 1
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('下发成功')
|
||||
this.getList()
|
||||
this.handleReqClick(this.currentReq)
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('下发失败')
|
||||
})
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
// ====== 排产明细新增/编辑/删除 ======
|
||||
resetDetailForm() {
|
||||
this.detailForm = this.getEmptyDetailForm()
|
||||
},
|
||||
|
||||
handleDetailAdd() {
|
||||
if (!this.currentReq) {
|
||||
this.$message.warning('请先选择一个产需单')
|
||||
return
|
||||
}
|
||||
this.resetDetailForm()
|
||||
this.detailForm.scheduleId = this.currentReq.scheduleId
|
||||
this.detailDialogTitle = '新增排产明细'
|
||||
this.detailDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.detailForm && this.$refs.detailForm.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
handleDetailEdit(row) {
|
||||
this.detailForm = { ...this.getEmptyDetailForm(), ...row }
|
||||
this.detailDialogTitle = '编辑排产明细'
|
||||
this.detailDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.detailForm && this.$refs.detailForm.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
submitDetailForm() {
|
||||
this.$refs.detailForm.validate(valid => {
|
||||
if (!valid) return
|
||||
this.detailBtnLoading = true
|
||||
const formData = { ...this.detailForm }
|
||||
if (this.detailForm.scheduleDetailId) {
|
||||
// 更新
|
||||
updateRequirementDetail(formData).then(() => {
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
this.detailDialogVisible = false
|
||||
this.refreshDetailList()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('修改失败')
|
||||
}).finally(() => {
|
||||
this.detailBtnLoading = false
|
||||
})
|
||||
} else {
|
||||
// 新增
|
||||
addRequirementDetail(formData).then(() => {
|
||||
this.$modal.msgSuccess('新增成功')
|
||||
this.detailDialogVisible = false
|
||||
this.refreshDetailList()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('新增失败')
|
||||
}).finally(() => {
|
||||
this.detailBtnLoading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
handleDetailDelete(row) {
|
||||
this.$confirm(`确认删除排产明细「${row.spec} / ${row.material}」吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delRequirementDetail(row.scheduleDetailId).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.refreshDetailList()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('删除失败')
|
||||
})
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
refreshDetailList() {
|
||||
if (!this.currentReq) return
|
||||
this.detailLoading = true
|
||||
getRequirement(this.currentReq.scheduleId).then(res => {
|
||||
if (res.data) {
|
||||
this.requirementDetailList = res.data.detailList || []
|
||||
}
|
||||
}).catch(() => {
|
||||
this.requirementDetailList = []
|
||||
}).finally(() => {
|
||||
this.detailLoading = false
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -849,6 +1024,11 @@ export default {
|
||||
border-color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.detail-card-header .header-btn:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.detail-card-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -13,55 +13,115 @@
|
||||
@change="handleDateChange"
|
||||
/>
|
||||
<el-button size="small" class="aps-btn-red" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||||
<el-tabs v-model="activeTab" size="small" style="margin:0 0 0 16px;" @tab-click="handleQuery">
|
||||
<el-tab-pane label="待审核" name="pending" />
|
||||
<el-tab-pane label="已排产" name="scheduled" />
|
||||
</el-tabs>
|
||||
<div class="aps-sch-summary" v-if="summaryText">
|
||||
<span>{{ summaryText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 排产明细卡片 -->
|
||||
<div class="detail-card aps-sch-card">
|
||||
<!-- 待审核 Tab -->
|
||||
<div class="detail-card aps-sch-card" v-show="activeTab === 'pending'">
|
||||
<div class="detail-card-header">
|
||||
<span>排产明细</span>
|
||||
<span v-if="detailList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
||||
共 {{ scheduleList.length }} 个产需单,{{ detailList.length }} 条明细
|
||||
<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">
|
||||
<el-table
|
||||
v-if="pendingScheduleList.length > 0"
|
||||
:data="pendingScheduleList"
|
||||
border
|
||||
size="small"
|
||||
class="aps-table"
|
||||
@row-click="handleScheduleClick"
|
||||
:expand-row-keys="expandedRowKeys"
|
||||
@expand-change="handleExpandChange"
|
||||
row-key="scheduleId"
|
||||
>
|
||||
<el-table-column type="expand" width="40">
|
||||
<template slot-scope="{ row }">
|
||||
<el-table :data="expandDetailMap[row.scheduleId] || []" border size="mini" style="margin:6px 0;" @row-click.stop="handleRowClick">
|
||||
<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="remark" min-width="100" />
|
||||
</el-table>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="140" />
|
||||
<el-table-column label="订货单位" prop="customerName" min-width="140" />
|
||||
<el-table-column label="业务员" prop="businessUser" width="80" align="center" />
|
||||
<el-table-column label="明细数量" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
{{ (row.detailList || []).length }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排产总吨数" width="100" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
{{ scheduleTotalWeight(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交货期(天)" prop="deliveryCycle" width="90" align="center" />
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<span style="white-space:nowrap;">
|
||||
<el-button type="text" size="small" style="color:#52c41a;" @click.stop="handleAccept(scope.row)">接收</el-button>
|
||||
<el-button type="text" size="small" style="color:#ff4d4f;" @click.stop="handleReject(scope.row)">驳回</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-else-if="!loading" style="padding:40px;text-align:center;color:#909399;">
|
||||
{{ hasQueried ? '该日期暂无待审核产需单' : '请选择日期查询' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已排产 Tab -->
|
||||
<div class="detail-card aps-sch-card" v-show="activeTab === 'scheduled'">
|
||||
<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>
|
||||
<div class="detail-card-body" style="padding:0;" v-loading="schLoading">
|
||||
<el-table
|
||||
v-if="detailList.length > 0"
|
||||
:data="detailList"
|
||||
v-if="scheduledItemList.length > 0"
|
||||
:data="scheduledItemList"
|
||||
border
|
||||
size="small"
|
||||
class="aps-table"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="140" fixed="left">
|
||||
<template slot-scope="{ row }">
|
||||
<span class="sch-link-text">{{ row.scheduleNo }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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="businessUser" width="80" align="center" />
|
||||
<el-table-column label="交货期(天)" prop="deliveryCycle" width="90" align="center" />
|
||||
<el-table-column label="备注" prop="remark" min-width="140" />
|
||||
<el-table-column label="备注" prop="remark" min-width="100" />
|
||||
<el-table-column label="操作" width="110" align="center" fixed="right">
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-else-if="!schLoading" style="padding:40px;text-align:center;color:#909399;">
|
||||
{{ hasQueried ? '该日期暂无排产数据' : '请选择日期查询排产数据' }}
|
||||
{{ hasQueried ? '该日期暂无已排产数据' : '请选择日期查询' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下钻弹窗 -->
|
||||
<el-dialog
|
||||
title="来源订单信息"
|
||||
:visible.sync="drillDialogVisible"
|
||||
width="600px"
|
||||
append-to-body
|
||||
>
|
||||
<!-- 下钻弹窗(来源订单信息) -->
|
||||
<el-dialog title="来源订单信息" :visible.sync="drillDialogVisible" width="600px" append-to-body>
|
||||
<div v-if="drillOrder" class="detail-card" style="border:none;box-shadow:none;">
|
||||
<div class="detail-card-body">
|
||||
<div class="form-grid-2">
|
||||
@@ -80,12 +140,66 @@
|
||||
<el-empty description="未找到订单信息" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 驳回理由对话框 -->
|
||||
<el-dialog title="驳回产需单" :visible.sync="rejectDialogVisible" width="450px" append-to-body :close-on-click-modal="false">
|
||||
<el-form ref="rejectForm" :model="rejectForm" label-width="90px" size="small">
|
||||
<el-form-item label="排产单号">
|
||||
<span style="color:#2c3e50;font-weight:500;">{{ rejectForm.scheduleNo }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="驳回理由" prop="returnReason">
|
||||
<el-input v-model="rejectForm.returnReason" type="textarea" :rows="4" placeholder="请填写驳回理由" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="rejectBtnLoading" type="danger" @click="confirmReject">确 定</el-button>
|
||||
<el-button @click="rejectDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</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-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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listRequirement } from '@/api/aps/requirement'
|
||||
import { getCrmOrderInfo } from '@/api/aps/schedule'
|
||||
import {
|
||||
listRequirement,
|
||||
updateRequirement
|
||||
} from '@/api/aps/requirement'
|
||||
import {
|
||||
getCrmOrderInfo,
|
||||
listScheduleItem,
|
||||
addScheduleItem,
|
||||
updateScheduleItem,
|
||||
delScheduleItem
|
||||
} from '@/api/aps/schedule'
|
||||
|
||||
export default {
|
||||
name: 'ApsSchedule',
|
||||
@@ -96,11 +210,36 @@ export default {
|
||||
const d = String(today.getDate()).padStart(2, '0')
|
||||
return {
|
||||
queryDate: `${y}-${m}-${d}`,
|
||||
activeTab: 'pending',
|
||||
loading: false,
|
||||
schLoading: false,
|
||||
hasQueried: false,
|
||||
scheduleList: [],
|
||||
detailList: [],
|
||||
|
||||
// 待审核
|
||||
pendingScheduleList: [],
|
||||
expandedRowKeys: [],
|
||||
expandDetailMap: {},
|
||||
summaryText: '',
|
||||
|
||||
// 已排产
|
||||
scheduledItemList: [],
|
||||
|
||||
// 驳回
|
||||
rejectDialogVisible: false,
|
||||
rejectBtnLoading: false,
|
||||
rejectForm: {
|
||||
scheduleId: null,
|
||||
scheduleNo: '',
|
||||
returnReason: ''
|
||||
},
|
||||
|
||||
// 已排产编辑
|
||||
editDialogVisible: false,
|
||||
editDialogTitle: '编辑排产明细',
|
||||
editBtnLoading: false,
|
||||
editForm: this.getEmptyEditForm(),
|
||||
|
||||
// 下钻
|
||||
drillDialogVisible: false,
|
||||
drillOrder: null
|
||||
}
|
||||
@@ -109,59 +248,233 @@ export default {
|
||||
this.handleQuery()
|
||||
},
|
||||
methods: {
|
||||
getEmptyEditForm() {
|
||||
return {
|
||||
itemId: undefined,
|
||||
scheduleId: undefined,
|
||||
scheduleNo: '',
|
||||
spec: '',
|
||||
material: '',
|
||||
scheduleWeight: 0,
|
||||
productType: '',
|
||||
customerName: '',
|
||||
remark: ''
|
||||
}
|
||||
},
|
||||
|
||||
handleDateChange() {
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
handleQuery() {
|
||||
if (!this.queryDate) {
|
||||
this.$message.warning('请选择生产日期')
|
||||
return
|
||||
}
|
||||
this.schLoading = true
|
||||
this.hasQueried = true
|
||||
this.detailList = []
|
||||
this.scheduleList = []
|
||||
if (this.activeTab === 'pending') {
|
||||
this.queryPending()
|
||||
} else {
|
||||
this.queryScheduled()
|
||||
}
|
||||
},
|
||||
|
||||
// 后端 list 接口已通过 fillDetailList 填充 detailList
|
||||
listRequirement({ prodDate: this.queryDate, pageNum: 1, pageSize: 999 }).then(res => {
|
||||
// ====== 待审核 ======
|
||||
queryPending() {
|
||||
this.loading = true
|
||||
this.pendingScheduleList = []
|
||||
|
||||
listRequirement({
|
||||
prodDate: this.queryDate,
|
||||
scheduleStatus: 1,
|
||||
pageNum: 1,
|
||||
pageSize: 999
|
||||
}).then(res => {
|
||||
const list = res.rows || []
|
||||
this.scheduleList = list
|
||||
this.pendingScheduleList = list
|
||||
|
||||
// 扁平化所有 detailList
|
||||
const merged = []
|
||||
// 构建展开映射
|
||||
const map = {}
|
||||
list.forEach(sch => {
|
||||
const details = sch.detailList || []
|
||||
details.forEach(d => {
|
||||
merged.push({
|
||||
...d,
|
||||
scheduleNo: sch.scheduleNo,
|
||||
customerName: sch.customerName,
|
||||
businessUser: sch.businessUser,
|
||||
deliveryCycle: sch.deliveryCycle,
|
||||
_scheduleId: sch.scheduleId
|
||||
})
|
||||
})
|
||||
map[sch.scheduleId] = (sch.detailList || []).map(d => ({
|
||||
...d,
|
||||
scheduleId: sch.scheduleId,
|
||||
productType: sch.productType || d.productType || ''
|
||||
}))
|
||||
})
|
||||
this.detailList = merged
|
||||
this.expandDetailMap = map
|
||||
|
||||
const totalWeight = merged.reduce((sum, d) => sum + (parseFloat(d.scheduleWeight) || 0), 0)
|
||||
this.summaryText = `共 ${list.length} 个产需单,${merged.length} 条明细,排产总吨数 ${totalWeight.toFixed(3)} 吨`
|
||||
const totalWeight = list.reduce((sum, sch) => {
|
||||
const details = sch.detailList || []
|
||||
return sum + details.reduce((s, d) => s + (parseFloat(d.scheduleWeight) || 0), 0)
|
||||
}, 0)
|
||||
const totalCount = list.reduce((sum, sch) => sum + (sch.detailList || []).length, 0)
|
||||
this.summaryText = `共 ${list.length} 个产需单,${totalCount} 条明细,排产总吨数 ${totalWeight.toFixed(3)} 吨`
|
||||
}).catch(() => {
|
||||
this.detailList = []
|
||||
this.pendingScheduleList = []
|
||||
this.summaryText = ''
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleAccept(sch) {
|
||||
this.$confirm(
|
||||
`确认接收产需单「${sch.scheduleNo}」的全部排产明细吗?`,
|
||||
'提示',
|
||||
{ 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(() => {
|
||||
this.$modal.msgSuccess('接收成功,排产明细已写入')
|
||||
this.queryPending()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('接收失败')
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
handleReject(sch) {
|
||||
this.rejectForm.scheduleId = sch.scheduleId
|
||||
this.rejectForm.scheduleNo = sch.scheduleNo
|
||||
this.rejectForm.returnReason = ''
|
||||
this.rejectDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.rejectForm && this.$refs.rejectForm.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
confirmReject() {
|
||||
if (!this.rejectForm.returnReason || !this.rejectForm.returnReason.trim()) {
|
||||
this.$message.warning('请填写驳回理由')
|
||||
return
|
||||
}
|
||||
this.rejectBtnLoading = true
|
||||
updateRequirement({
|
||||
scheduleId: this.rejectForm.scheduleId,
|
||||
scheduleStatus: 3,
|
||||
returnReason: this.rejectForm.returnReason.trim()
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已驳回')
|
||||
this.rejectDialogVisible = false
|
||||
this.queryPending()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('驳回失败')
|
||||
}).finally(() => {
|
||||
this.rejectBtnLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
// ====== 已排产 ======
|
||||
queryScheduled() {
|
||||
this.schLoading = true
|
||||
this.scheduledItemList = []
|
||||
|
||||
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.summaryText = `共 ${this.scheduledItemList.length} 条明细,排产总吨数 ${totalWeight.toFixed(3)} 吨`
|
||||
}).catch(() => {
|
||||
this.scheduledItemList = []
|
||||
this.summaryText = ''
|
||||
}).finally(() => {
|
||||
this.schLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleEditScheduled(row) {
|
||||
this.editForm = { ...this.getEmptyEditForm(), ...row }
|
||||
this.editDialogTitle = '编辑排产明细'
|
||||
this.editDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.editForm && this.$refs.editForm.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
submitEditForm() {
|
||||
this.editBtnLoading = true
|
||||
updateScheduleItem(this.editForm).then(() => {
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
this.editDialogVisible = false
|
||||
this.queryScheduled()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('修改失败')
|
||||
}).finally(() => {
|
||||
this.editBtnLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleDeleteScheduled(row) {
|
||||
this.$confirm(`确认删除排产明细「${row.spec} / ${row.material}」吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delScheduleItem(row.itemId || row.scheduleItemId).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.queryScheduled()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('删除失败')
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// ====== 下钻 ======
|
||||
// ====== 待审核辅助方法 ======
|
||||
scheduleTotalWeight(sch) {
|
||||
const details = sch.detailList || []
|
||||
const total = details.reduce((sum, d) => sum + (parseFloat(d.scheduleWeight) || 0), 0)
|
||||
return total.toFixed(3)
|
||||
},
|
||||
|
||||
handleScheduleClick(sch) {
|
||||
// 父行单击展开/折叠
|
||||
const idx = this.expandedRowKeys.indexOf(sch.scheduleId)
|
||||
if (idx >= 0) {
|
||||
this.expandedRowKeys.splice(idx, 1)
|
||||
} else {
|
||||
this.expandedRowKeys.push(sch.scheduleId)
|
||||
}
|
||||
},
|
||||
|
||||
handleExpandChange() {
|
||||
// noop
|
||||
},
|
||||
|
||||
handleRowClick(row) {
|
||||
// 通过 _scheduleId 找到产需单,然后找到关联的订单
|
||||
const sch = this.scheduleList.find(s => s.scheduleId === row._scheduleId)
|
||||
// 子行(明细行)点击查看来源订单
|
||||
const sch = this.pendingScheduleList.find(s => s.scheduleId === row.scheduleId)
|
||||
if (!sch || !sch.orderList || sch.orderList.length === 0) {
|
||||
this.$message.warning('未找到关联订单')
|
||||
return
|
||||
}
|
||||
// 取第一个关联订单展示
|
||||
const order = sch.orderList[0]
|
||||
getCrmOrderInfo(order.orderId).then(res => {
|
||||
this.drillOrder = res.data
|
||||
@@ -198,6 +511,7 @@ export default {
|
||||
border-radius: $aps-radius;
|
||||
box-shadow: $aps-shadow-sm;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.aps-sch-label {
|
||||
@@ -252,15 +566,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.sch-link-text {
|
||||
color: $aps-red-2;
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
color: $aps-red-3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
// 复用卡片/网格变量
|
||||
.aps-btn-red {
|
||||
@include aps-btn-red;
|
||||
@@ -283,6 +588,7 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-card-body {
|
||||
@@ -313,4 +619,22 @@ export default {
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid $aps-silver-mid;
|
||||
}
|
||||
|
||||
// Tabs 覆盖
|
||||
::v-deep .el-tabs__item {
|
||||
font-size: 13px;
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__header {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__active-bar {
|
||||
background-color: $aps-red-2 !important;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
color: $aps-red-2 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user