feat(aps): 新增产需明细转排产功能并优化页面布局
1. 新增convertRequirementDetail接口实现产需明细下发排产 2. 为排产明细BO添加日期格式化注解 3. 产需单页面添加拖拽分栏、产需转化弹窗、撤回按钮和下发进度展示 4. 移除订单状态查询和列表展示,简化排产页面标签页结构 5. 优化产需单列表和详情页的布局样式
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
package com.klp.flow.domain.bo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.klp.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -42,6 +45,8 @@ public class SchProdScheduleDetailBo extends BaseEntity {
|
||||
/**
|
||||
* 排产日期
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private Date scheduleDate;
|
||||
|
||||
/**
|
||||
|
||||
@@ -82,6 +82,15 @@ export function delRequirementDetail(detailIds) {
|
||||
})
|
||||
}
|
||||
|
||||
// 产需明细逐条下发到排产明细(sch_prod_schedule_item)
|
||||
export function convertRequirementDetail(data) {
|
||||
return request({
|
||||
url: '/flow/prodScheduleItem/receiveFromDetail',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// ====== 销售订单-产需单关联(SchSaleScheduleRel) ======
|
||||
|
||||
// 查询关联列表
|
||||
|
||||
@@ -53,11 +53,6 @@
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单状态" prop="orderStatus">
|
||||
<el-select v-model="queryParams.orderStatus" placeholder="请选择订单状态" clearable style="width: 140px;">
|
||||
<el-option v-for="(value, key) in ORDER_STATUS" :key="value" :label="key" :value="value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="aps-btn-silver" icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -96,7 +91,6 @@
|
||||
<div style="font-size: 12px; color: #909399;">
|
||||
签订地点: {{ item.signLocation || '-' }}
|
||||
</div>
|
||||
<el-tag :type="statusTagType(item.orderStatus)" size="small">{{ statusLabel(item.orderStatus) }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="orderList.length === 0 && !orderLoading"
|
||||
|
||||
@@ -62,11 +62,6 @@
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单状态" prop="orderStatus">
|
||||
<el-select v-model="queryParams.orderStatus" placeholder="请选择订单状态" clearable style="width: 140px;">
|
||||
<el-option v-for="(value, key) in ORDER_STATUS" :key="value" :label="key" :value="value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="aps-btn-silver" icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
@@ -96,7 +91,6 @@
|
||||
<div style="font-size: 12px; color: #909399;">
|
||||
签订地点: {{ item.signLocation || '-' }}
|
||||
</div>
|
||||
<el-tag :type="statusTagType(item.orderStatus)" size="small">{{ statusLabel(item.orderStatus) }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="orderList.length === 0 && !orderLoading"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<div class="aps-req-page">
|
||||
<el-row :gutter="20" class="aps-req-row">
|
||||
<drag-resize-panel direction="horizontal" :initial-size="360" :min-size="260" class="aps-req-row">
|
||||
<template #panelA>
|
||||
<!-- 左侧:产需单列表 -->
|
||||
<el-col :span="8" class="aps-req-left" v-loading="reqLoading">
|
||||
<div class="aps-req-left" v-loading="reqLoading">
|
||||
<div class="aps-req-scroll">
|
||||
<!-- 筛选区 -->
|
||||
<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;">
|
||||
@@ -45,7 +47,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-item-right">
|
||||
<el-button size="mini" plain @click.stop="handleJumpToSchedule(item)">跳转排产</el-button>
|
||||
<el-button v-if="item.scheduleStatus === 1 || item.scheduleStatus === 2" size="mini" plain @click.stop="openConvertDialogForReq(item)">产需转化</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,10 +59,13 @@
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize" @pagination="getList"
|
||||
style="padding: 10px; margin-bottom: 10px !important;" />
|
||||
</el-col>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #panelB>
|
||||
<!-- 右侧:详情区 -->
|
||||
<el-col :span="16" class="aps-req-right">
|
||||
<div class="aps-req-right">
|
||||
<div class="aps-req-scroll">
|
||||
<el-empty v-if="!currentReq" description="选择产需单查看更多信息" />
|
||||
|
||||
<div v-else class="detail-panel">
|
||||
@@ -74,6 +79,11 @@
|
||||
<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 v-if="currentReq.scheduleStatus === 1" class="header-btn"
|
||||
style="background:rgba(255,255,255,0.4);border-color:rgba(255,255,255,0.7);"
|
||||
@click="handleRecall">撤回</button>
|
||||
<button v-if="currentReq.scheduleStatus === 1 || currentReq.scheduleStatus === 2" class="header-btn"
|
||||
@click="openConvertDialogForReq(currentReq)">产需转化</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-card-body">
|
||||
@@ -194,13 +204,30 @@
|
||||
<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>
|
||||
<span style="display:flex;align-items:center;gap:8px;">
|
||||
<span v-if="detailDispatchProgress.total > 0" style="font-size:12px;font-weight:400;opacity:0.9;">
|
||||
已下发 {{ detailDispatchProgress.issued }} / {{ detailDispatchProgress.total }}({{ detailDispatchProgress.percent }}%)
|
||||
</span>
|
||||
<button v-if="canEdit" class="header-btn" @click="handleDetailAdd">+ 新增</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-card-body" style="padding:0;">
|
||||
<el-table :data="requirementDetailList" border size="small" v-loading="detailLoading" class="aps-table">
|
||||
<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="工序步骤" min-width="130">
|
||||
<template slot-scope="scope">
|
||||
<span style="font-size:12px;">{{ getStepNamesByProcessId(scope.row.processId) || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排产日期" prop="scheduleDate" width="110" align="center" />
|
||||
<el-table-column label="下发状态" width="90" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.isIssued === 1 || scope.row.isIssued === '1'" style="color:#52c41a;font-size:12px;">已下发</span>
|
||||
<span v-else style="color:#c0c4cc;font-size:12px;">未下发</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" min-width="120" />
|
||||
<el-table-column label="操作" width="130" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
@@ -217,8 +244,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</drag-resize-panel>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
@@ -350,7 +379,7 @@
|
||||
<!-- 左侧:订单列表 -->
|
||||
<div style="width: 45%; display: flex; flex-direction: column;">
|
||||
<div style="display: flex; gap: 6px; margin-bottom: 10px;">
|
||||
<el-input v-model="bindQuery.keyword" placeholder="搜索订单编号/客户名" size="small" clearable style="flex:1;"
|
||||
<el-input v-model="bindQuery.keyword" placeholder="搜索订单编号/客户名/业务员" size="small" clearable style="flex:1;"
|
||||
@change="searchBindOrders" />
|
||||
<el-button size="small" class="aps-btn-red" icon="el-icon-search" @click="searchBindOrders">搜索</el-button>
|
||||
</div>
|
||||
@@ -449,6 +478,68 @@
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 产需转化对话框 -->
|
||||
<el-dialog title="产需转化" :visible.sync="convertDialogVisible" width="1100px" append-to-body
|
||||
:close-on-click-modal="false">
|
||||
<div style="margin-bottom:12px; font-size:13px; color:#2c3e50;">
|
||||
产需单号:<strong>{{ convertReqNo }}</strong>
|
||||
<span style="margin-left:16px;">共 {{ convertDetailList.length }} 条明细</span>
|
||||
</div>
|
||||
<el-table :data="convertDetailList" border size="small" class="aps-table"
|
||||
@selection-change="(val) => convertSelection = val">
|
||||
<el-table-column type="selection" width="40" />
|
||||
<el-table-column label="规格" prop="spec" min-width="100" />
|
||||
<el-table-column label="材质" prop="material" width="80" align="center" />
|
||||
<el-table-column label="排产吨数" prop="scheduleWeight" width="90" align="right" />
|
||||
<el-table-column label="工序ID" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.processId" placeholder="选择工序" size="small" clearable filterable
|
||||
style="width:130px;" @change="(val) => onConvertProcessChange(scope.row, val)">
|
||||
<el-option v-for="p in processList" :key="p.processId" :label="p.processName" :value="p.processId">
|
||||
<span>{{ p.processName }}</span>
|
||||
<span v-if="p._steps" style="display:block;font-size:11px;color:#909399;">{{ p._steps }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序步骤" min-width="140">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row._stepNames" style="font-size:12px; color:#2c3e50;">{{ scope.row._stepNames }}</span>
|
||||
<span v-else style="font-size:12px; color:#c0c4cc;">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排产日期" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.scheduleDate" type="date" value-format="yyyy-MM-dd" placeholder="排产日期"
|
||||
size="small" clearable style="width:130px;" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下发状态" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.isIssued === 1 || scope.row.isIssued === '1'" style="color:#52c41a;font-size:12px;">已下发</span>
|
||||
<span v-else style="color:#c0c4cc;font-size:12px;">未下发</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" min-width="100" />
|
||||
<el-table-column label="操作" width="70" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" style="color:#ff4d4f;"
|
||||
:disabled="!scope.row.processId || !scope.row.scheduleDate"
|
||||
@click="handleConvertSingle(scope.row)">下发</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="convertProgressShow" style="margin-top:12px;">
|
||||
<el-progress :percentage="convertProgress" :status="convertProgress === 100 ? 'success' : ''" />
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer" style="margin-top:12px;">
|
||||
<el-button :loading="convertBtnLoading" type="danger" :disabled="convertSelection.length === 0"
|
||||
@click="confirmConvertSelected">批量下发({{ convertSelection.length }})</el-button>
|
||||
<el-button :loading="convertBtnLoading" type="danger" :disabled="!convertAllValid" @click="confirmConvertAll">全部下发</el-button>
|
||||
<el-button @click="convertDialogVisible = false">关 闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -464,13 +555,17 @@ import {
|
||||
listRel,
|
||||
addRequirementDetail,
|
||||
updateRequirementDetail,
|
||||
delRequirementDetail
|
||||
delRequirementDetail,
|
||||
convertRequirementDetail
|
||||
} from '@/api/aps/requirement'
|
||||
import { listCrmOrder } from '@/api/aps/order'
|
||||
import { listProcess } from '@/api/aps/process'
|
||||
import { parseProductContent } from '@/utils/productContent'
|
||||
import DragResizePanel from '@/components/DragResizePanel'
|
||||
|
||||
export default {
|
||||
name: 'ApsRequirement',
|
||||
components: { DragResizePanel },
|
||||
data() {
|
||||
return {
|
||||
// 左侧列表
|
||||
@@ -520,6 +615,16 @@ export default {
|
||||
scheduleWeight: [{ required: true, message: '排产吨数不能为空', trigger: 'change' }]
|
||||
},
|
||||
|
||||
// 产需转化对话框
|
||||
convertDialogVisible: false,
|
||||
convertDetailList: [],
|
||||
convertBtnLoading: false,
|
||||
convertReqNo: '',
|
||||
convertProgress: 0,
|
||||
convertProgressShow: false,
|
||||
convertSelection: [],
|
||||
processList: [],
|
||||
|
||||
// 新增/编辑对话框
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增产需单',
|
||||
@@ -541,6 +646,16 @@ export default {
|
||||
const n = Number(item.scheduleWeight)
|
||||
return isNaN(n) ? sum : sum + n
|
||||
}, 0)
|
||||
},
|
||||
convertAllValid() {
|
||||
return this.convertDetailList.length > 0 &&
|
||||
this.convertDetailList.every(d => d.processId && d.scheduleDate)
|
||||
},
|
||||
detailDispatchProgress() {
|
||||
const total = this.requirementDetailList.length
|
||||
if (total === 0) return { issued: 0, total: 0, percent: 0 }
|
||||
const issued = this.requirementDetailList.filter(d => d.isIssued === 1 || d.isIssued === '1').length
|
||||
return { issued, total, percent: Math.round((issued / total) * 100) }
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -582,6 +697,9 @@ export default {
|
||||
spec: '',
|
||||
material: '',
|
||||
scheduleWeight: 0,
|
||||
processId: '',
|
||||
scheduleDate: '',
|
||||
isIssued: 0,
|
||||
productType: '',
|
||||
remark: ''
|
||||
}
|
||||
@@ -592,18 +710,6 @@ export default {
|
||||
this.getList()
|
||||
},
|
||||
|
||||
// 跳转到排产单页面(路由跳转)
|
||||
handleJumpToSchedule(item) {
|
||||
this.$router.push({
|
||||
path: '/aps/schedule',
|
||||
query: {
|
||||
prodDate: item.prodDate || '',
|
||||
scheduleNo: item.scheduleNo || '',
|
||||
scheduleStatus: item.scheduleStatus
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
statusBadgeType(status) {
|
||||
return this.statusBadgeMap[status] || 'gray'
|
||||
},
|
||||
@@ -624,11 +730,22 @@ export default {
|
||||
handleReqClick(item) {
|
||||
this.currentReq = item
|
||||
this.detailLoading = true
|
||||
getRequirement(item.scheduleId).then(res => {
|
||||
Promise.all([
|
||||
getRequirement(item.scheduleId),
|
||||
listProcess({ pageNum: 1, pageSize: 1000 })
|
||||
]).then(([res, processRes]) => {
|
||||
if (res.data) {
|
||||
this.currentReq = res.data
|
||||
this.requirementDetailList = res.data.detailList || []
|
||||
}
|
||||
// 构建工序查找表
|
||||
this.processList = (processRes.rows || []).map(p => ({
|
||||
...p,
|
||||
_steps: (p.stepList && p.stepList.length > 0)
|
||||
? p.stepList.slice().sort((a, b) => (a.stepOrder || 0) - (b.stepOrder || 0))
|
||||
.map(s => s.stepName).join(' → ')
|
||||
: ''
|
||||
}))
|
||||
}).catch(() => {
|
||||
this.requirementDetailList = []
|
||||
}).finally(() => {
|
||||
@@ -963,6 +1080,286 @@ export default {
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
handleRecall() {
|
||||
this.$confirm('确认撤回该产需单吗?撤回后将恢复为草稿状态。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
updateRequirement({
|
||||
scheduleId: this.currentReq.scheduleId,
|
||||
scheduleStatus: 0
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('撤回成功')
|
||||
this.getList()
|
||||
this.handleReqClick(this.currentReq)
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('撤回失败')
|
||||
})
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
// ====== 产需转化 ======
|
||||
openConvertDialogForReq(item) {
|
||||
// 确保加载了该产需单的详情
|
||||
this.convertReqNo = item.scheduleNo || ''
|
||||
this.convertProgressShow = false
|
||||
this.convertProgress = 0
|
||||
const loadDetail = item.scheduleId === (this.currentReq && this.currentReq.scheduleId)
|
||||
? Promise.resolve(this.currentReq)
|
||||
: getRequirement(item.scheduleId)
|
||||
|
||||
// 同时加载工序列表和明细
|
||||
Promise.all([
|
||||
loadDetail,
|
||||
listProcess({ pageNum: 1, pageSize: 1000 })
|
||||
]).then(([res, processRes]) => {
|
||||
const data = res.data || res
|
||||
const detailList = (data.detailList || []).map(d => ({ ...d }))
|
||||
// 构建工序查找表
|
||||
const processRows = processRes.rows || []
|
||||
const processMap = new Map()
|
||||
processRows.forEach(p => {
|
||||
processMap.set(p.processId, p)
|
||||
})
|
||||
this.processList = processRows.map(p => ({
|
||||
...p,
|
||||
_steps: (p.stepList && p.stepList.length > 0)
|
||||
? p.stepList.slice().sort((a, b) => (a.stepOrder || 0) - (b.stepOrder || 0))
|
||||
.map(s => s.stepName).join(' → ')
|
||||
: ''
|
||||
}))
|
||||
// 为已有 processId 的明细填充 _stepNames,默认 scheduleDate 为今天
|
||||
const today = this.getTodayStr()
|
||||
detailList.forEach(d => {
|
||||
if (!d.scheduleDate) {
|
||||
d.scheduleDate = today
|
||||
}
|
||||
if (d.processId) {
|
||||
this.fillProcessStepInfo(d)
|
||||
}
|
||||
})
|
||||
this.convertDetailList = detailList
|
||||
this.convertDialogVisible = true
|
||||
}).catch(() => {
|
||||
this.convertDetailList = []
|
||||
this.convertDialogVisible = true
|
||||
})
|
||||
},
|
||||
|
||||
fillProcessStepInfo(row) {
|
||||
const pid = row.processId ? String(row.processId) : ''
|
||||
if (!pid) return
|
||||
const process = this.processList.find(p => String(p.processId) === pid)
|
||||
if (process) {
|
||||
const steps = (process.stepList || []).slice().sort((a, b) => (a.stepOrder || 0) - (b.stepOrder || 0))
|
||||
row._stepNames = steps.map(s => s.stepName).join(' → ')
|
||||
row._stepList = steps
|
||||
} else {
|
||||
row._stepNames = ''
|
||||
row._stepList = []
|
||||
}
|
||||
},
|
||||
|
||||
onConvertProcessChange(row, val) {
|
||||
if (!val) {
|
||||
row._stepNames = ''
|
||||
row._stepList = []
|
||||
return
|
||||
}
|
||||
this.fillProcessStepInfo(row)
|
||||
},
|
||||
|
||||
getStepNamesByProcessId(processId) {
|
||||
if (!processId) return ''
|
||||
const pid = String(processId)
|
||||
const process = this.processList.find(p => String(p.processId) === pid)
|
||||
if (!process || !process.stepList || process.stepList.length === 0) return ''
|
||||
return process.stepList.slice().sort((a, b) => (a.stepOrder || 0) - (b.stepOrder || 0))
|
||||
.map(s => s.stepName).join(' → ')
|
||||
},
|
||||
|
||||
handleConvertSingle(row) {
|
||||
if (!row.processId) {
|
||||
this.$message.warning('请先选择工序')
|
||||
return
|
||||
}
|
||||
if (!row.scheduleDate) {
|
||||
this.$message.warning('请先填写排产日期')
|
||||
return
|
||||
}
|
||||
this.$confirm(`确认下发明细「${row.spec} / ${row.material}」吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.convertBtnLoading = true
|
||||
// 先保存 processId、scheduleDate 并标记已下发
|
||||
updateRequirementDetail({
|
||||
scheduleDetailId: row.scheduleDetailId,
|
||||
processId: row.processId,
|
||||
scheduleDate: row.scheduleDate,
|
||||
isIssued: 1
|
||||
}).then(() => {
|
||||
// 构建下发 BO,逐条下发
|
||||
return convertRequirementDetail({
|
||||
detailList: [{
|
||||
scheduleDetailId: row.scheduleDetailId,
|
||||
processId: row.processId || null,
|
||||
stepList: (row._stepList || []).map(s => ({
|
||||
stepId: s.stepId,
|
||||
stepOrder: s.stepOrder,
|
||||
stepName: s.stepName
|
||||
}))
|
||||
}]
|
||||
})
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('下发成功')
|
||||
row.isIssued = 1
|
||||
this.refreshDetailList()
|
||||
}).catch(() => {
|
||||
this.$modal.msgError('下发失败')
|
||||
}).finally(() => {
|
||||
this.convertBtnLoading = false
|
||||
})
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
confirmConvertAll() {
|
||||
if (this.convertDetailList.length === 0) {
|
||||
this.$message.warning('没有可下发的明细')
|
||||
return
|
||||
}
|
||||
const invalidItems = this.convertDetailList.filter(d => !d.processId || !d.scheduleDate)
|
||||
if (invalidItems.length > 0) {
|
||||
this.$message.warning(`还有 ${invalidItems.length} 条明细未填写工序或排产日期,请完善后再下发`)
|
||||
return
|
||||
}
|
||||
this.$confirm(`确认下发全部 ${this.convertDetailList.length} 条明细吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.convertBtnLoading = true
|
||||
this.convertProgressShow = true
|
||||
this.convertProgress = 0
|
||||
const total = this.convertDetailList.length
|
||||
let completed = 0
|
||||
let failed = 0
|
||||
|
||||
const processNext = (index) => {
|
||||
if (index >= total) {
|
||||
this.convertBtnLoading = false
|
||||
this.refreshDetailList()
|
||||
if (failed === 0) {
|
||||
this.$modal.msgSuccess(`成功下发 ${completed} 条明细`)
|
||||
} else if (completed === 0) {
|
||||
this.$modal.msgError('下发失败,请重试')
|
||||
} else {
|
||||
this.$modal.msgWarning(`成功下发 ${completed} 条,${failed} 条失败`)
|
||||
}
|
||||
return
|
||||
}
|
||||
const row = this.convertDetailList[index]
|
||||
updateRequirementDetail({
|
||||
scheduleDetailId: row.scheduleDetailId,
|
||||
processId: row.processId,
|
||||
scheduleDate: row.scheduleDate,
|
||||
isIssued: 1
|
||||
}).then(() => {
|
||||
return convertRequirementDetail({
|
||||
detailList: [{
|
||||
scheduleDetailId: row.scheduleDetailId,
|
||||
processId: row.processId,
|
||||
stepList: (row._stepList || []).map(s => ({
|
||||
stepId: s.stepId,
|
||||
stepOrder: s.stepOrder,
|
||||
stepName: s.stepName
|
||||
}))
|
||||
}]
|
||||
})
|
||||
}).then(() => {
|
||||
completed++
|
||||
row.isIssued = 1
|
||||
}).catch(() => {
|
||||
failed++
|
||||
}).finally(() => {
|
||||
this.convertProgress = Math.round(((completed + failed) / total) * 100)
|
||||
processNext(index + 1)
|
||||
})
|
||||
}
|
||||
processNext(0)
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
confirmConvertSelected() {
|
||||
if (this.convertSelection.length === 0) {
|
||||
this.$message.warning('请勾选要下发的明细')
|
||||
return
|
||||
}
|
||||
const invalidItems = this.convertSelection.filter(d => !d.processId || !d.scheduleDate)
|
||||
if (invalidItems.length > 0) {
|
||||
this.$message.warning(`所选明细中有 ${invalidItems.length} 条未填写工序或排产日期,请完善后再下发`)
|
||||
return
|
||||
}
|
||||
this.$confirm(`确认下发所选 ${this.convertSelection.length} 条明细吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.convertBtnLoading = true
|
||||
this.convertProgressShow = true
|
||||
this.convertProgress = 0
|
||||
const list = [...this.convertSelection]
|
||||
const total = list.length
|
||||
let completed = 0
|
||||
let failed = 0
|
||||
|
||||
const processNext = (index) => {
|
||||
if (index >= total) {
|
||||
this.convertBtnLoading = false
|
||||
this.refreshDetailList()
|
||||
if (failed === 0) {
|
||||
this.$modal.msgSuccess(`成功下发 ${completed} 条明细`)
|
||||
} else if (completed === 0) {
|
||||
this.$modal.msgError('下发失败,请重试')
|
||||
} else {
|
||||
this.$modal.msgWarning(`成功下发 ${completed} 条,${failed} 条失败`)
|
||||
}
|
||||
return
|
||||
}
|
||||
const row = list[index]
|
||||
updateRequirementDetail({
|
||||
scheduleDetailId: row.scheduleDetailId,
|
||||
processId: row.processId,
|
||||
scheduleDate: row.scheduleDate,
|
||||
isIssued: 1
|
||||
}).then(() => {
|
||||
return convertRequirementDetail({
|
||||
detailList: [{
|
||||
scheduleDetailId: row.scheduleDetailId,
|
||||
processId: row.processId,
|
||||
stepList: (row._stepList || []).map(s => ({
|
||||
stepId: s.stepId,
|
||||
stepOrder: s.stepOrder,
|
||||
stepName: s.stepName
|
||||
}))
|
||||
}]
|
||||
})
|
||||
}).then(() => {
|
||||
completed++
|
||||
row.isIssued = 1
|
||||
}).catch(() => {
|
||||
failed++
|
||||
}).finally(() => {
|
||||
this.convertProgress = Math.round(((completed + failed) / total) * 100)
|
||||
processNext(index + 1)
|
||||
})
|
||||
}
|
||||
processNext(0)
|
||||
}).catch(() => { })
|
||||
},
|
||||
|
||||
// ====== 排产明细新增/编辑/删除 ======
|
||||
resetDetailForm() {
|
||||
this.detailForm = this.getEmptyDetailForm()
|
||||
@@ -1058,8 +1455,7 @@ export default {
|
||||
@import './scss/aps-theme.scss';
|
||||
|
||||
.aps-req-page {
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
height: calc(100vh - 84px);
|
||||
box-sizing: border-box;
|
||||
background: $aps-silver-1;
|
||||
}
|
||||
@@ -1070,17 +1466,19 @@ export default {
|
||||
|
||||
// ====== 左侧(参照 HTML 产需单列表) ======
|
||||
.aps-req-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
border: 1px solid $aps-silver-3;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.aps-req-scroll {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
background: $aps-silver-mid;
|
||||
padding: 8px 12px;
|
||||
@@ -1088,15 +1486,12 @@ export default {
|
||||
font-weight: 600;
|
||||
color: $aps-text;
|
||||
border-bottom: 1px solid $aps-border;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -1198,21 +1593,15 @@ export default {
|
||||
border: 1px solid $aps-silver-3;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
background: $aps-bg;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
// ====== 右侧详情卡片 ======
|
||||
|
||||
@@ -13,18 +13,17 @@
|
||||
@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-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="accepted" />
|
||||
<el-tab-pane label="已排产" name="scheduled" />
|
||||
</el-tabs>
|
||||
<div v-if="summaryText" class="aps-sch-summary">
|
||||
<span>{{ summaryText }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- 待审核 Tab -->
|
||||
<div v-loading="loading" v-show="activeTab === 'pending'" class="detail-card aps-sch-card">
|
||||
<!-- <div v-loading="loading" 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;">
|
||||
@@ -34,7 +33,6 @@
|
||||
<div element-loading-text="正在加载待审核产需单..." class="detail-card-body" style="padding:0;">
|
||||
<div v-if="pendingScheduleList.length > 0" class="aps-sch-list">
|
||||
<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">
|
||||
<span class="aps-sch-no">{{ sch.scheduleNo }}</span>
|
||||
@@ -49,7 +47,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产需单完整信息表 -->
|
||||
<table class="req-info-table">
|
||||
<colgroup>
|
||||
<col style="width: 88px;">
|
||||
@@ -131,7 +128,6 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 明细列表 -->
|
||||
<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>
|
||||
@@ -162,7 +158,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已接收 Tab -->
|
||||
<div v-loading="acceptedLoading" v-show="activeTab === 'accepted'" class="detail-card aps-sch-card">
|
||||
<div class="detail-card-header">
|
||||
<span>已接收产需单</span>
|
||||
@@ -283,10 +278,10 @@
|
||||
{{ hasQueried ? '该日期暂无已接收产需单' : '请选择日期查询' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 已排产 Tab -->
|
||||
<div v-loading="schLoading" v-show="activeTab === 'scheduled'" class="detail-card aps-sch-card">
|
||||
<div v-loading="schLoading" class="detail-card aps-sch-card">
|
||||
<div class="detail-card-header">
|
||||
<span>已排产明细</span>
|
||||
<div style="display:flex; align-items:center; gap:8px;">
|
||||
@@ -696,7 +691,7 @@ export default {
|
||||
const d = String(today.getDate()).padStart(2, '0')
|
||||
return {
|
||||
queryDate: `${y}-${m}-${d}`,
|
||||
activeTab: 'pending',
|
||||
activeTab: 'scheduled',
|
||||
loading: false,
|
||||
schLoading: false,
|
||||
hasQueried: false,
|
||||
|
||||
Reference in New Issue
Block a user