feat: 实现产需单按工序步骤生成排产明细功能
1. 重构接收产需单接口,支持按配置工序步骤生成明细 2. 新增工艺、工艺步骤CRUD接口与管理页面 3. 新增工序选择组件 4. 优化产需单页面,增加历史记录功能 5. 为排产明细添加工序步骤名称展示
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<div class="filter-section" style="padding: 10px; border-bottom: 1px solid #e4e7ed;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
|
||||
<div style="display: flex; align-items: center; gap: 4px; flex-wrap: wrap;">
|
||||
<el-input v-model="queryParams.scheduleNo" placeholder="排产单号" clearable @keyup.enter.native="handleSearch"
|
||||
<el-input v-model="queryParams.scheduleNo" placeholder="产需单号" clearable @keyup.enter.native="handleSearch"
|
||||
style="width: 130px;" size="small" />
|
||||
<el-input v-model="queryParams.customerName" placeholder="客户名" clearable
|
||||
@keyup.enter.native="handleSearch" style="width: 130px;" size="small" />
|
||||
@@ -58,15 +58,19 @@
|
||||
<div class="detail-card-header">
|
||||
<span>产需单信息</span>
|
||||
<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>
|
||||
<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">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="req-td-label">排产单号</td>
|
||||
<td class="req-td-label">产需单号</td>
|
||||
<td class="req-td-value">{{ currentReq.scheduleNo }}</td>
|
||||
<td class="req-td-label">生产日期</td>
|
||||
<td class="req-td-value">{{ currentReq.prodDate }}</td>
|
||||
@@ -75,7 +79,7 @@
|
||||
<td class="req-td-label">排产状态</td>
|
||||
<td class="req-td-value"><span class="aps-status-tag"
|
||||
:class="'status-' + (currentReq.scheduleStatus || 0)">{{ statusMap[currentReq.scheduleStatus] ||
|
||||
'未知' }}</span></td>
|
||||
'未知' }}</span></td>
|
||||
<td class="req-td-label">业务员</td>
|
||||
<td class="req-td-value">{{ currentReq.businessUser }}</td>
|
||||
</tr>
|
||||
@@ -139,7 +143,8 @@
|
||||
</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>
|
||||
<td class="req-td-value" colspan="3" style="color:#e74c3c;background:#fdecea;">{{
|
||||
currentReq.returnReason }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -190,8 +195,10 @@
|
||||
<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>
|
||||
<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>
|
||||
@@ -204,13 +211,15 @@
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="700px" append-to-body
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
:close-on-click-modal="false">
|
||||
<el-form ref="reqForm" :model="reqForm" :rules="reqRules" label-width="110px" size="small">
|
||||
<!-- 第一行:必填项 -->
|
||||
<div style="display: flex; gap: 16px;">
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<el-form ref="reqForm" :model="reqForm" :rules="reqRules" label-width="80px" size="small">
|
||||
<!-- 第一行:必填项 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排产单号" prop="scheduleNo">
|
||||
<el-form-item label="产需单号" prop="scheduleNo">
|
||||
<el-input v-model="reqForm.scheduleNo" placeholder="自动生成或手动填写" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -282,19 +291,42 @@
|
||||
<el-form-item label="交货重量偏差" prop="weightDeviation">
|
||||
<el-input v-model="reqForm.weightDeviation" />
|
||||
</el-form-item>
|
||||
<el-form-item label="其他技术要求" prop="otherTechReq">
|
||||
<el-input v-model="reqForm.otherTechReq" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="付款情况说明" prop="paymentDesc">
|
||||
<el-input v-model="reqForm.paymentDesc" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="其他技术要求" prop="otherTechReq">
|
||||
<el-input v-model="reqForm.otherTechReq" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="reqForm.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<!-- 右侧:历史记录 -->
|
||||
<div style="width: 200px; flex-shrink: 0;" v-if="!reqForm.scheduleId && scheduleNoHistory.length > 0">
|
||||
<div style="font-size: 13px; font-weight: 600; color: #2c3e50; margin-bottom: 8px; padding-left: 8px; border-left: 3px solid #ff4d4f;">
|
||||
历史记录
|
||||
</div>
|
||||
<div style="border: 1px solid #e4e7ed; border-radius: 4px; max-height: 450px; overflow-y: auto;">
|
||||
<div v-for="item in scheduleNoHistory" :key="item.scheduleNo" class="history-item"
|
||||
@click="useHistoryRecord(item)"
|
||||
style="padding: 8px 10px; border-bottom: 1px solid #ecf0f1; cursor: pointer; font-size: 12px; transition: background 0.15s;">
|
||||
<div style="font-weight: 500; color: #2c3e50;">{{ item.scheduleNo }}</div>
|
||||
<div style="color: #7f8c8d; margin-top: 2px;">{{ item.customerName || '-' }}</div>
|
||||
<div style="color: #b0b0b0; font-size: 11px;">{{ item.prodDate || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="btnLoading" type="danger" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
@@ -376,7 +408,8 @@
|
||||
<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-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="可选" />
|
||||
@@ -458,9 +491,10 @@ export default {
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增产需单',
|
||||
btnLoading: false,
|
||||
scheduleNoHistory: [],
|
||||
reqForm: this.getEmptyForm(),
|
||||
reqRules: {
|
||||
scheduleNo: [{ required: true, message: '排产单号不能为空', trigger: 'blur' }],
|
||||
scheduleNo: [{ required: true, message: '产需单号不能为空', trigger: 'blur' }],
|
||||
prodDate: [{ required: true, message: '生产日期不能为空', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
@@ -564,6 +598,17 @@ export default {
|
||||
|
||||
handleAdd() {
|
||||
this.resetForm()
|
||||
this.reqForm.scheduleNo = this.generateScheduleNo()
|
||||
this.reqForm.prodDate = this.getTodayStr()
|
||||
const lastForm = this.getLastFormData()
|
||||
if (lastForm) {
|
||||
Object.keys(lastForm).forEach(key => {
|
||||
if (key !== 'scheduleId' && key !== 'scheduleNo' && key !== 'prodDate' && lastForm[key] !== undefined && lastForm[key] !== '' && lastForm[key] !== null) {
|
||||
this.reqForm[key] = lastForm[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
this.scheduleNoHistory = this.getScheduleNoHistory()
|
||||
this.dialogTitle = '新增产需单'
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
@@ -586,9 +631,14 @@ export default {
|
||||
this.$refs.reqForm.validate(valid => {
|
||||
if (!valid) return
|
||||
this.btnLoading = true
|
||||
const action = this.reqForm.scheduleId ? updateRequirement : addRequirement
|
||||
const isNew = !this.reqForm.scheduleId
|
||||
const action = isNew ? addRequirement : updateRequirement
|
||||
action(this.reqForm).then(() => {
|
||||
this.$modal.msgSuccess(this.reqForm.scheduleId ? '修改成功' : '新增成功')
|
||||
this.$modal.msgSuccess(isNew ? '新增成功' : '修改成功')
|
||||
if (isNew) {
|
||||
this.saveHistoryRecord(this.reqForm)
|
||||
this.saveLastFormData(this.reqForm)
|
||||
}
|
||||
this.dialogVisible = false
|
||||
this.getList()
|
||||
}).catch(() => {
|
||||
@@ -615,6 +665,71 @@ export default {
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
getTodayStr() {
|
||||
return new Date().toISOString().split('T')[0]
|
||||
},
|
||||
|
||||
generateScheduleNo() {
|
||||
const now = new Date()
|
||||
const dateStr = now.getFullYear().toString() +
|
||||
(now.getMonth() + 1).toString().padStart(2, '0') +
|
||||
now.getDate().toString().padStart(2, '0')
|
||||
const prefix = 'PCXD' + dateStr
|
||||
const existingNums = this.reqList
|
||||
.filter(item => item.scheduleNo && item.scheduleNo.startsWith(prefix))
|
||||
.map(item => {
|
||||
const seq = parseInt(item.scheduleNo.substring(prefix.length), 10)
|
||||
return isNaN(seq) ? 0 : seq
|
||||
})
|
||||
const maxSeq = existingNums.length > 0 ? Math.max(...existingNums) : 0
|
||||
const newSeq = (maxSeq + 1).toString().padStart(3, '0')
|
||||
return prefix + newSeq
|
||||
},
|
||||
|
||||
getScheduleNoHistory() {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem('aps_requirement_history') || '[]')
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
saveHistoryRecord(formData) {
|
||||
if (!formData.scheduleNo) return
|
||||
const history = this.getScheduleNoHistory()
|
||||
const idx = history.findIndex(item => item.scheduleNo === formData.scheduleNo)
|
||||
if (idx >= 0) history.splice(idx, 1)
|
||||
history.unshift({ ...formData })
|
||||
delete history[0].scheduleId
|
||||
if (history.length > 20) history.pop()
|
||||
localStorage.setItem('aps_requirement_history', JSON.stringify(history))
|
||||
},
|
||||
|
||||
useHistoryRecord(item) {
|
||||
Object.keys(item).forEach(key => {
|
||||
if (key !== 'scheduleId' && item[key] !== undefined) {
|
||||
this.reqForm[key] = item[key]
|
||||
}
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
this.$refs.reqForm && this.$refs.reqForm.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
getLastFormData() {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem('aps_requirement_last_form') || 'null')
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
saveLastFormData(formData) {
|
||||
const data = { ...formData }
|
||||
delete data.scheduleId
|
||||
localStorage.setItem('aps_requirement_last_form', JSON.stringify(data))
|
||||
},
|
||||
|
||||
openBindDialog() {
|
||||
this.bindDialogVisible = true
|
||||
this.selectedBindOrders = []
|
||||
@@ -1223,4 +1338,8 @@ export default {
|
||||
.aps-bind-dialog ::v-deep .el-dialog__body {
|
||||
padding: 16px 20px 0 20px;
|
||||
}
|
||||
|
||||
.history-item:hover {
|
||||
background: #fdecea;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user