From e7d96e2c3d8138acb4c1c041c24d0f18f8e4f84b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E6=98=8A?=
<15984991+wwh2328692301@user.noreply.gitee.com>
Date: Thu, 9 Jul 2026 17:15:24 +0800
Subject: [PATCH] =?UTF-8?q?feat(wms):=20=E6=96=B0=E5=A2=9EAPS=E6=8E=92?=
=?UTF-8?q?=E4=BA=A7=E7=9B=B8=E5=85=B3=E9=A1=B5=E9=9D=A2=E5=8F=8A=E4=BA=A7?=
=?UTF-8?q?=E9=9C=80=E5=8D=95=E8=B7=B3=E8=BD=AC=E6=8E=92=E4=BA=A7=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1. 新增/aps/requirement和/aps/schedule路由页面
2. 实现产需单列表跳转排产单功能
3. 为排产页面添加参数接收与自动查询逻辑
4. 新增弹窗组件间传递APS页面参数的能力
5. 优化产需单列表UI布局与绑定状态展示
---
klp-ui/src/router/index.js | 27 ++++
klp-ui/src/views/wms/post/aps/requirement.vue | 117 +++++++++++++++---
klp-ui/src/views/wms/post/aps/schedule.vue | 50 +++++++-
klp-ui/src/views/wms/post/flow.vue | 35 ++++++
4 files changed, 213 insertions(+), 16 deletions(-)
diff --git a/klp-ui/src/router/index.js b/klp-ui/src/router/index.js
index cbf559ee8..f2639818e 100644
--- a/klp-ui/src/router/index.js
+++ b/klp-ui/src/router/index.js
@@ -183,6 +183,33 @@ export const dynamicRoutes = [
}
]
},
+ // APS 排产独立页面(用于产需单跳转排产单)
+ {
+ path: '/aps/requirement',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: '',
+ component: () => import('@/views/wms/post/aps/requirement'),
+ name: 'ApsRequirement',
+ meta: { title: '产需单' }
+ }
+ ]
+ },
+ {
+ path: '/aps/schedule',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: '',
+ component: () => import('@/views/wms/post/aps/schedule'),
+ name: 'ApsSchedule',
+ meta: { title: '排产单' }
+ }
+ ]
+ },
{
path: '/system/dict-data',
component: Layout,
diff --git a/klp-ui/src/views/wms/post/aps/requirement.vue b/klp-ui/src/views/wms/post/aps/requirement.vue
index d36e06b29..426f1255b 100644
--- a/klp-ui/src/views/wms/post/aps/requirement.vue
+++ b/klp-ui/src/views/wms/post/aps/requirement.vue
@@ -27,16 +27,23 @@
-
- {{ item.scheduleNo }}
- {{
- statusMap[item.scheduleStatus] || '未知' }}
-
-
- 生产日期:{{ item.prodDate || '-' }} 客户:{{ item.customerName || '-' }} 业务员:{{ item.businessUser || '-' }}
-
-
-
+
+
+
+ {{ item.scheduleNo }}
+ {{
+ statusMap[item.scheduleStatus] || '未知' }}
+
+
+ 生产日期:{{ item.prodDate || '-' }} 客户:{{ item.customerName || '-' }} 业务员:{{ item.businessUser || '-' }}
+
+
+
+
+
+
+ 跳转排产
+
@@ -362,7 +369,7 @@
-
+
@@ -382,6 +389,25 @@
总数量:{{ bindPreviewTotalQty }} 吨
含税总额:{{ bindPreviewTotalAmount }}
+
+
+
+ 该订单已绑定以下产需单:
+
+
+
+ {{ rel.scheduleNo }}
+ {{ bindPreviewRelStatusMap[rel.scheduleStatus] || '' }}
+
+
+
@@ -471,6 +497,10 @@ export default {
bindPreviewProductName: '',
bindPreviewTotalQty: 0,
bindPreviewTotalAmount: 0,
+ // 当前预览订单已绑定的产需单列表
+ bindPreviewExistingRels: [],
+ // 产需单状态映射(用于展示绑定状态)
+ bindPreviewRelStatusMap: { 0: '草稿', 1: '待审核', 2: '已下达', 3: '已退回' },
// 排产明细
detailLoading: false,
@@ -559,6 +589,18 @@ 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'
},
@@ -738,6 +780,7 @@ export default {
this.bindPreviewProductName = ''
this.bindPreviewTotalQty = 0
this.bindPreviewTotalAmount = 0
+ this.bindPreviewExistingRels = []
this.searchBindOrders()
},
@@ -780,6 +823,29 @@ export default {
this.bindPreviewTotalQty = 0
this.bindPreviewTotalAmount = 0
}
+ // 查询该订单已关联了哪些产需单
+ this.bindPreviewExistingRels = []
+ listRel({ orderId: order.orderId }).then(res => {
+ const rows = res.rows || []
+ // 按 scheduleId 去重,并与当前已有的 reqList 匹配获取单号
+ const seen = new Set()
+ const reqMap = new Map()
+ ;(this.reqList || []).forEach(r => reqMap.set(r.scheduleId, r))
+ const unique = []
+ rows.forEach(r => {
+ const key = r.scheduleId
+ if (!key || seen.has(key)) return
+ seen.add(key)
+ // 从已加载的 reqList 中找单号,找不到则不展示(已删除或不在当前页)
+ const match = reqMap.get(key)
+ if (match) {
+ unique.push({ ...r, scheduleNo: match.scheduleNo, scheduleStatus: match.scheduleStatus })
+ }
+ })
+ this.bindPreviewExistingRels = unique
+ }).catch(() => {
+ this.bindPreviewExistingRels = []
+ })
},
confirmBind() {
@@ -1038,6 +1104,31 @@ export default {
transition: background 0.15s;
}
+.list-item-body {
+ display: flex;
+ align-items: stretch;
+ gap: 8px;
+}
+
+.list-item-left {
+ flex: 1;
+ min-width: 0;
+}
+
+.list-item-right {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ padding-left: 4px;
+}
+
+.item-actions {
+ display: flex;
+ gap: 8px;
+ margin-top: 4px;
+}
+
.list-item:hover {
background: $aps-silver-1;
}
@@ -1063,10 +1154,6 @@ export default {
margin-bottom: 4px;
}
-.list-item .item-actions {
- display: flex;
- gap: 8px;
-}
// ====== 徽标 ======
.badge {
diff --git a/klp-ui/src/views/wms/post/aps/schedule.vue b/klp-ui/src/views/wms/post/aps/schedule.vue
index cdc8fb3f8..b1784e73e 100644
--- a/klp-ui/src/views/wms/post/aps/schedule.vue
+++ b/klp-ui/src/views/wms/post/aps/schedule.vue
@@ -653,6 +653,13 @@ import ProcessSelect from '@/components/KLPService/ProcessSelect/index.vue'
export default {
name: 'ApsSchedule',
components: { ProcessSelect },
+ props: {
+ // 由 flow.vue 通过 switchApsPage 传入的参数
+ apsParams: {
+ type: Object,
+ default: null
+ }
+ },
data() {
const today = new Date()
const y = today.getFullYear()
@@ -726,8 +733,49 @@ export default {
)
}
},
- watch: {},
+ watch: {
+ // 接收 flow.vue 传入的参数:设置日期并自动查询
+ apsParams: {
+ handler(val) {
+ if (val && val.prodDate) {
+ this.queryDate = val.prodDate
+ }
+ if (val && val.scheduleStatus !== undefined) {
+ // 根据产需单状态自动切换 Tab
+ // 1=待审核 2=已接收 4=已排产
+ const statusToTab = { 1: 'pending', 2: 'accepted', 4: 'scheduled' }
+ const tab = statusToTab[val.scheduleStatus] || 'pending'
+ this.activeTab = tab
+ }
+ this.$nextTick(() => {
+ this.handleQuery()
+ })
+ },
+ immediate: false
+ }
+ },
created() {
+ // 优先从路由参数获取(产需单点击"跳转排产"通过 $router.push 传参)
+ const query = this.$route.query
+ // 其次从 apsParams prop 获取(弹窗内由 flow.vue 切换组件传入)
+ const params = this.apsParams
+
+ let hasExternal = false
+ if (query && query.prodDate) {
+ this.queryDate = query.prodDate
+ hasExternal = true
+ if (query.scheduleStatus !== undefined) {
+ const statusToTab = { 1: 'pending', 2: 'accepted', 4: 'scheduled' }
+ this.activeTab = statusToTab[Number(query.scheduleStatus)] || 'pending'
+ }
+ } else if (params && params.prodDate) {
+ this.queryDate = params.prodDate
+ hasExternal = true
+ if (params.scheduleStatus !== undefined) {
+ const statusToTab = { 1: 'pending', 2: 'accepted', 4: 'scheduled' }
+ this.activeTab = statusToTab[params.scheduleStatus] || 'pending'
+ }
+ }
this.handleQuery()
},
methods: {
diff --git a/klp-ui/src/views/wms/post/flow.vue b/klp-ui/src/views/wms/post/flow.vue
index 1343ef01a..db3ce6a13 100644
--- a/klp-ui/src/views/wms/post/flow.vue
+++ b/klp-ui/src/views/wms/post/flow.vue
@@ -44,6 +44,7 @@
v-if="dynamicComponent && !dialogLoading"
:is="dynamicComponent"
:key="dialogKey"
+ :aps-params="apsDialogParams"
/>
@@ -533,6 +534,8 @@ export default {
dialogLoading: false,
dynamicComponent: null,
dialogKey: 0,
+ // APS 页面切换参数(传递给子组件)
+ apsDialogParams: null,
}
},
@@ -701,6 +704,7 @@ export default {
this.dialogWidth = dialogWidth
this.dialogLoading = true
this.dynamicComponent = null
+ this.apsDialogParams = params || null
this.resolveImport(componentPath)
.then(module => {
@@ -716,6 +720,36 @@ export default {
})
},
+ /**
+ * APS 页面间切换(由子组件调用 this.$parent.switchApsPage)
+ * @param {string} componentPath - 相对于 @/views/ 的路径,如 'wms/post/aps/schedule'
+ * @param {object} params - 传递给目标组件的参数
+ * @param {string} title - 弹窗标题
+ */
+ switchApsPage(componentPath, params, title) {
+ if (!componentPath) {
+ this.$message.warning('组件路径不能为空')
+ return
+ }
+
+ this.dialogLoading = true
+ this.dynamicComponent = null
+ this.apsDialogParams = params || null
+ if (title) this.dialogTitle = title
+
+ this.resolveImport(componentPath)
+ .then(module => {
+ this.dynamicComponent = module.default || module
+ })
+ .catch(err => {
+ console.error('[FlowChart] switchApsPage error:', err)
+ this.$message.error('组件加载失败: ' + (err.message || '未知错误'))
+ })
+ .finally(() => {
+ this.dialogLoading = false
+ })
+ },
+
resolveImport(componentPath) {
// 与 src/store/modules/permission.js 的 loadView 同款模式
// componentPath 相对于 @/views/,如 'crm/order/index'
@@ -730,6 +764,7 @@ export default {
handleDialogClosed() {
this.dynamicComponent = null
this.dialogKey++
+ this.apsDialogParams = null
},
},
}