From 8064a77af2faf5bf080be0c4347a3a975225453b Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 14 七月 2026 15:38:12 +0800
Subject: [PATCH] 银川 1.物料管理页面批次属性功能联调 2.库存现有量添加自定义出库入库功能
---
src/views/wls/materialstock/components/batch-config-display.vue | 96 ++
src/views/wls/salesnotice/index.vue | 28
src/views/wls/materialstock/data.ts | 70 +
src/views/wls/materialstock/components/stock-adjust-modal.vue | 488 +++++++++++++
src/views/basicData/mdm/components/data.ts | 111 +++
src/api/mes/wm/salesnotice/index.ts | 16
src/views/erp/sale/order/components/index.ts | 2
src/views/wls/materialstock/components/index.ts | 1
src/api/system/tenant-package/index.ts | 84 +
src/views/basicData/mdm/components/batch-config.vue | 147 +++
src/views/basicData/mdm/components/select-dialog.vue | 209 +++++
src/api/core/auth.ts | 16
src/views/basicData/mdm/components/select.vue | 138 +++
src/views/wls/salesnotice/data.ts | 83 ++
src/packages/constants/src/biz-mes-enum.ts | 7
src/views/basicData/mdm/components/index.ts | 2
src/views/basicData/mdm/modules/form.vue | 74 +
src/views/wls/materialstock/index.vue | 24
src/views/erp/sale/order/data.ts | 2
src/views/erp/sale/order/components/select.vue | 135 +++
src/views/erp/sale/order/index.vue | 27
src/api/mes/wm/productsales/line/index.ts | 1
.env.production | 4
src/views/basicData/mdm/data.ts | 10
src/api/mes/md/item-batch-config/index.ts | 36
src/api/mes/wm/materialstock/index.ts | 60 +
src/api/mes/wm/salesnotice/line/index.ts | 2
src/views/erp/sale/order/components/select-dialog.vue | 199 +++++
src/api/system/tenant/index.ts | 100 +
29 files changed, 2,078 insertions(+), 94 deletions(-)
diff --git a/.env.production b/.env.production
index a8f3d29..cd77149 100644
--- a/.env.production
+++ b/.env.production
@@ -1,9 +1,9 @@
VITE_BASE=/
# 璇锋眰璺緞
-VITE_BASE_URL=http://127.0.0.1:48080
+VITE_BASE_URL=http://36.213.180.217:9500
# 鎺ュ彛鍦板潃
-VITE_GLOB_API_URL=http://127.0.0.1:48080/admin-api
+VITE_GLOB_API_URL=http://36.213.180.217:9500/admin-api
# 鏂囦欢涓婁紶绫诲瀷锛歴erver - 鍚庣涓婁紶锛� client - 鍓嶇鐩磋繛涓婁紶锛屼粎鏀寔S3鏈嶅姟
VITE_UPLOAD_TYPE=server
diff --git a/src/api/core/auth.ts b/src/api/core/auth.ts
index 604644a..cec93f5 100644
--- a/src/api/core/auth.ts
+++ b/src/api/core/auth.ts
@@ -100,16 +100,20 @@
/** 鑾峰彇绉熸埛鍒楄〃 */
export async function getTenantSimpleList() {
- return requestClient.get<AuthApi.TenantResult[]>(
- `/system/tenant/simple-list`,
- );
+ // 绉熸埛鎺ュ彛宸茬鐢紝杩斿洖绌烘暟缁�
+ return Promise.resolve([]);
+ // return requestClient.get<AuthApi.TenantResult[]>(
+ // `/system/tenant/simple-list`,
+ // );
}
/** 浣跨敤绉熸埛鍩熷悕锛岃幏寰楃鎴蜂俊鎭� */
export async function getTenantByWebsite(website: string) {
- return requestClient.get<AuthApi.TenantResult>(
- `/system/tenant/get-by-website?website=${website}`,
- );
+ // 绉熸埛鎺ュ彛宸茬鐢紝杩斿洖绌哄璞�
+ return Promise.resolve({} as AuthApi.TenantResult);
+ // return requestClient.get<AuthApi.TenantResult>(
+ // `/system/tenant/get-by-website?website=${website}`,
+ // );
}
/** 鑾峰彇楠岃瘉鐮� */
diff --git a/src/api/mes/md/item-batch-config/index.ts b/src/api/mes/md/item-batch-config/index.ts
new file mode 100644
index 0000000..c67d170
--- /dev/null
+++ b/src/api/mes/md/item-batch-config/index.ts
@@ -0,0 +1,36 @@
+import type { PageParam, PageResult } from '#/packages/effects/request/src';
+
+import { requestClient } from '#/api/request';
+
+export namespace MdmItemBatchConfigApi {
+ /** 鐗╂枡鎵规閰嶇疆锛堜粎淇濈暀7涓瓧娈碉級 */
+ export interface ItemBatchConfig {
+ id?: number; // 閰嶇疆ID
+ itemId: number; // 鐗╂枡ID
+ produceDateFlag?: boolean; // 鐢熶骇鏃ユ湡蹇呭~
+ expireDateFlag?: boolean; // 鏈夋晥鏈熷繀濉�
+ receiptDateFlag?: boolean; // 鍏ュ簱鏃ユ湡蹇呭~
+ vendorFlag?: boolean; // 渚涘簲鍟嗗繀濉�
+ purchaseOrderCodeFlag?: boolean; // 閲囪喘璁㈠崟鍙峰繀濉�
+ lotNumberFlag?: boolean; // 鐢熶骇鎵瑰彿蹇呭~
+ qualityStatusFlag?: boolean; // 璐ㄩ噺鐘舵�佸繀濉�
+ }
+}
+
+/** 鏍规嵁鐗╂枡ID鑾峰彇鎵规閰嶇疆 */
+export function getItemBatchConfigByItemId(itemId: number) {
+ return requestClient.get<MdmItemBatchConfigApi.ItemBatchConfig | null>(
+ '/mes/md/item-batch-config/get-by-item-id',
+ { params: { itemId } },
+ );
+}
+
+/** 淇濆瓨鎵规閰嶇疆锛堟柊澧�/鏇存柊锛� */
+export function saveItemBatchConfig(data: MdmItemBatchConfigApi.ItemBatchConfig) {
+ return requestClient.post<number>('/mes/md/item-batch-config/save', data);
+}
+
+/** 鍒犻櫎鎵规閰嶇疆 */
+export function deleteItemBatchConfig(id: number) {
+ return requestClient.delete(`/mes/md/item-batch-config/delete?id=${id}`);
+}
diff --git a/src/api/mes/wm/materialstock/index.ts b/src/api/mes/wm/materialstock/index.ts
index 49aab96..02c3e94 100644
--- a/src/api/mes/wm/materialstock/index.ts
+++ b/src/api/mes/wm/materialstock/index.ts
@@ -24,6 +24,10 @@
vendorId?: number; // 渚涘簲鍟嗙紪鍙�
vendorName?: string; // 渚涘簲鍟嗗悕绉�
quantity?: number; // 鍦ㄥ簱鏁伴噺
+ frozenQuantity?: number; // 鍐荤粨鏁伴噺
+ reservedQuantity?: number; // 鍗犵敤閲�
+ inTransitQuantity?: number; // 鍦ㄩ�旈噺
+ availableQuantity?: number; // 鍙敤閲�
receiptTime?: string; // 鍏ュ簱鏃ユ湡
frozen?: boolean; // 鏄惁鍐荤粨
createTime?: Date; // 鍒涘缓鏃堕棿
@@ -59,3 +63,59 @@
params,
});
}
+
+/** 搴撳瓨璋冩暣锛堣嚜瀹氫箟鍑哄叆搴擄級- 鍏ュ簱 */
+export interface MaterialStockAdjustInParams {
+ itemId: number; // 鐗╂枡ID
+ warehouseId: number; // 浠撳簱ID
+ locationId: number; // 搴撳尯ID
+ areaId: number; // 搴撲綅ID
+ batchId?: number; // 鎵规ID锛堥�夋嫨宸叉湁鎵规鏃朵紶鍏ワ級
+ batchCode?: string; // 鎵规鍙凤紙鏂板缓鎵规鏃朵紶鍏ワ級
+ produceDate?: string; // 鐢熶骇鏃ユ湡锛堟柊寤烘壒娆℃椂浼犲叆锛�
+ expireDate?: string; // 鏈夋晥鏈燂紙鏂板缓鎵规鏃朵紶鍏ワ級
+ vendorId?: number; // 渚涘簲鍟咺D
+ quantity: number; // 鍏ュ簱鏁伴噺锛堝繀椤诲ぇ浜�0锛�
+ reason?: string; // 鍏ュ簱鍘熷洜
+}
+
+/** 搴撳瓨璋冩暣锛堣嚜瀹氫箟鍑哄叆搴擄級- 鍑哄簱 */
+export interface MaterialStockAdjustOutParams {
+ itemId: number; // 鐗╂枡ID
+ warehouseId: number; // 浠撳簱ID
+ locationId: number; // 搴撳尯ID
+ areaId: number; // 搴撲綅ID
+ batchId?: number; // 鎵规ID
+ batchCode?: string; // 鎵规鍙�
+ vendorId?: number; // 渚涘簲鍟咺D
+ quantity: number; // 鍑哄簱鏁伴噺锛堝繀椤诲ぇ浜�0锛�
+ reason?: string; // 鍑哄簱鍘熷洜
+}
+
+/** 鑷畾涔夊叆搴� */
+export function adjustMaterialStockIn(data: MaterialStockAdjustInParams) {
+ return requestClient.post<number>('/mes/wm/material-stock/adjust-in', data);
+}
+
+/** 鑷畾涔夊嚭搴� */
+export function adjustMaterialStockOut(data: MaterialStockAdjustOutParams) {
+ return requestClient.post<number>('/mes/wm/material-stock/adjust-out', data);
+}
+
+/** 搴撳瓨璋冩暣锛堟鏁�=鍏ュ簱锛岃礋鏁�=鍑哄簱锛�- 鏃ф帴鍙o紝淇濈暀鍏煎 */
+export interface MaterialStockAdjustParams {
+ itemId: number; // 鐗╂枡ID
+ warehouseId: number; // 浠撳簱ID
+ locationId: number; // 搴撳尯ID
+ areaId: number; // 搴撲綅ID
+ batchId?: number; // 鎵规ID
+ batchCode?: string; // 鎵规鍙�
+ vendorId?: number; // 渚涘簲鍟咺D
+ quantity: number; // 璋冩暣鏁伴噺锛堟鏁�=鍏ュ簱锛岃礋鏁�=鍑哄簱锛�
+ reason?: string; // 璋冩暣鍘熷洜
+}
+
+/** 搴撳瓨璋冩暣锛堟鏁�=鍏ュ簱锛岃礋鏁�=鍑哄簱锛� - 鏃ф帴鍙o紝淇濈暀鍏煎 */
+export function adjustMaterialStock(data: MaterialStockAdjustParams) {
+ return requestClient.post<number>('/mes/wm/material-stock/adjust', data);
+}
diff --git a/src/api/mes/wm/productsales/line/index.ts b/src/api/mes/wm/productsales/line/index.ts
index f1d4ff5..1f8e46a 100644
--- a/src/api/mes/wm/productsales/line/index.ts
+++ b/src/api/mes/wm/productsales/line/index.ts
@@ -8,6 +8,7 @@
id?: number; // 琛岀紪鍙�
salesId?: number; // 鍑哄簱鍗曠紪鍙�
noticeLineId?: number; // 鍙戣揣閫氱煡鍗曡缂栧彿
+ saleOrderItemId?: number; // 閿�鍞鍗曟槑缁咺D锛堢敤浜庤拷婧級
itemId?: number; // 鐗╂枡缂栧彿
itemCode?: string; // 鐗╂枡缂栫爜
itemName?: string; // 鐗╂枡鍚嶇О
diff --git a/src/api/mes/wm/salesnotice/index.ts b/src/api/mes/wm/salesnotice/index.ts
index c053f69..6bb0ab8 100644
--- a/src/api/mes/wm/salesnotice/index.ts
+++ b/src/api/mes/wm/salesnotice/index.ts
@@ -8,6 +8,7 @@
id?: number; // 閫氱煡鍗曠紪鍙�
code?: string; // 閫氱煡鍗曠紪鍙�
name?: string; // 閫氱煡鍗曞悕绉�
+ saleOrderId?: number; // 閿�鍞鍗曠紪鍙�
salesOrderCode?: string; // 閿�鍞鍗曠紪鍙�
clientId?: number; // 瀹㈡埛缂栧彿
clientCode?: string; // 瀹㈡埛缂栫爜
@@ -17,6 +18,7 @@
recipientTelephone?: string; // 鑱旂郴鏂瑰紡
recipientAddress?: string; // 鏀惰揣鍦板潃
status?: number; // 鍗曟嵁鐘舵��
+ outStatus?: number; // 鍑哄簱鐘舵�侊細0-鏈嚭搴擄紝1-閮ㄥ垎鍑哄簱锛�2-鍏ㄩ儴鍑哄簱
remark?: string; // 澶囨敞
createTime?: Date; // 鍒涘缓鏃堕棿
}
@@ -63,3 +65,17 @@
params,
});
}
+
+/** 浠庨攢鍞鍗曠敓鎴愬彂璐ч�氱煡鍗� */
+export function generateFromSaleOrder(saleOrderId: number) {
+ return requestClient.post<number>('/mes/wm/sales-notice/generate-from-sale-order', {
+ saleOrderId,
+ });
+}
+
+/** 浠庡彂璐ч�氱煡鍗曠敓鎴愰攢鍞嚭搴撳崟 */
+export function generateProductSales(id: number) {
+ return requestClient.post<number>('/mes/wm/sales-notice/generate-product-sales', null, {
+ params: { id },
+ });
+}
diff --git a/src/api/mes/wm/salesnotice/line/index.ts b/src/api/mes/wm/salesnotice/line/index.ts
index 948d1bc..cb908e7 100644
--- a/src/api/mes/wm/salesnotice/line/index.ts
+++ b/src/api/mes/wm/salesnotice/line/index.ts
@@ -7,6 +7,7 @@
export interface SalesNoticeLine {
id?: number; // 琛岀紪鍙�
noticeId?: number; // 鍙戣揣閫氱煡鍗曠紪鍙�
+ saleOrderItemId?: number; // 閿�鍞鍗曟槑缁咺D锛堢敤浜庤拷婧級
itemId?: number; // 鐗╂枡缂栧彿
itemCode?: string; // 鐗╂枡缂栫爜
itemName?: string; // 鐗╂枡鍚嶇О
@@ -15,6 +16,7 @@
batchId?: number; // 鎵规缂栧彿
batchCode?: string; // 鎵规鍙�
quantity?: number; // 鍙戣揣鏁伴噺
+ outCount?: number; // 宸插嚭搴撴暟閲�
oqcCheckFlag?: boolean; // 鏄惁妫�楠�
remark?: string; // 澶囨敞
}
diff --git a/src/api/system/tenant-package/index.ts b/src/api/system/tenant-package/index.ts
index 53be78f..532e14c 100644
--- a/src/api/system/tenant-package/index.ts
+++ b/src/api/system/tenant-package/index.ts
@@ -17,48 +17,78 @@
}
}
-/** 绉熸埛濂楅鍒楄〃 */
+// 绉熸埛濂楅鎺ュ彛宸茬鐢�
+// /** 绉熸埛濂楅鍒楄〃 */
+// export function getTenantPackagePage(params: PageParam) {
+// return requestClient.get<PageResult<SystemTenantPackageApi.TenantPackage>>(
+// '/system/tenant-package/page',
+// { params },
+// );
+// }
+
+// /** 鏌ヨ绉熸埛濂楅璇︽儏 */
+// export function getTenantPackage(id: number) {
+// return requestClient.get(`/system/tenant-package/get?id=${id}`);
+// }
+
+// /** 鏂板绉熸埛濂楅 */
+// export function createTenantPackage(
+// data: SystemTenantPackageApi.TenantPackage,
+// ) {
+// return requestClient.post('/system/tenant-package/create', data);
+// }
+
+// /** 淇敼绉熸埛濂楅 */
+// export function updateTenantPackage(
+// data: SystemTenantPackageApi.TenantPackage,
+// ) {
+// return requestClient.put('/system/tenant-package/update', data);
+// }
+
+// /** 鍒犻櫎绉熸埛濂楅 */
+// export function deleteTenantPackage(id: number) {
+// return requestClient.delete(`/system/tenant-package/delete?id=${id}`);
+// }
+
+// /** 鎵归噺鍒犻櫎绉熸埛濂楅 */
+// export function deleteTenantPackageList(ids: number[]) {
+// return requestClient.delete(
+// `/system/tenant-package/delete-list?ids=${ids.join(',')}`,
+// );
+// }
+
+// /** 鑾峰彇绉熸埛濂楅绮剧畝淇℃伅鍒楄〃 */
+// export function getTenantPackageList() {
+// return requestClient.get<SystemTenantPackageApi.TenantPackage[]>(
+// '/system/tenant-package/get-simple-list',
+// );
+// }
+
+// 鎻愪緵绌哄嚱鏁颁互閬垮厤寮曠敤閿欒
export function getTenantPackagePage(params: PageParam) {
- return requestClient.get<PageResult<SystemTenantPackageApi.TenantPackage>>(
- '/system/tenant-package/page',
- { params },
- );
+ return Promise.resolve({ list: [], total: 0 });
}
-/** 鏌ヨ绉熸埛濂楅璇︽儏 */
export function getTenantPackage(id: number) {
- return requestClient.get(`/system/tenant-package/get?id=${id}`);
+ return Promise.resolve({} as SystemTenantPackageApi.TenantPackage);
}
-/** 鏂板绉熸埛濂楅 */
-export function createTenantPackage(
- data: SystemTenantPackageApi.TenantPackage,
-) {
- return requestClient.post('/system/tenant-package/create', data);
+export function createTenantPackage(data: SystemTenantPackageApi.TenantPackage) {
+ return Promise.resolve({});
}
-/** 淇敼绉熸埛濂楅 */
-export function updateTenantPackage(
- data: SystemTenantPackageApi.TenantPackage,
-) {
- return requestClient.put('/system/tenant-package/update', data);
+export function updateTenantPackage(data: SystemTenantPackageApi.TenantPackage) {
+ return Promise.resolve({});
}
-/** 鍒犻櫎绉熸埛濂楅 */
export function deleteTenantPackage(id: number) {
- return requestClient.delete(`/system/tenant-package/delete?id=${id}`);
+ return Promise.resolve({});
}
-/** 鎵归噺鍒犻櫎绉熸埛濂楅 */
export function deleteTenantPackageList(ids: number[]) {
- return requestClient.delete(
- `/system/tenant-package/delete-list?ids=${ids.join(',')}`,
- );
+ return Promise.resolve({});
}
-/** 鑾峰彇绉熸埛濂楅绮剧畝淇℃伅鍒楄〃 */
export function getTenantPackageList() {
- return requestClient.get<SystemTenantPackageApi.TenantPackage[]>(
- '/system/tenant-package/get-simple-list',
- );
+ return Promise.resolve([]);
}
diff --git a/src/api/system/tenant/index.ts b/src/api/system/tenant/index.ts
index 6ee52e1..e2a83e2 100644
--- a/src/api/system/tenant/index.ts
+++ b/src/api/system/tenant/index.ts
@@ -17,60 +17,98 @@
}
}
-/** 绉熸埛鍒楄〃 */
+// 绉熸埛鎺ュ彛宸茬鐢�
+// /** 绉熸埛鍒楄〃 */
+// export function getTenantPage(params: PageParam) {
+// return requestClient.get<PageResult<SystemTenantApi.Tenant>>(
+// '/system/tenant/page',
+// { params },
+// );
+// }
+
+// /** 鑾峰彇绉熸埛绮剧畝淇℃伅鍒楄〃 */
+// export function getSimpleTenantList() {
+// return requestClient.get<SystemTenantApi.Tenant[]>(
+// '/system/tenant/simple-list',
+// );
+// }
+
+// /** 鏌ヨ绉熸埛璇︽儏 */
+// export function getTenant(id: number) {
+// return requestClient.get<SystemTenantApi.Tenant>(
+// `/system/tenant/get?id=${id}`,
+// );
+// }
+
+// /** 鑾峰彇绉熸埛绮剧畝淇℃伅鍒楄〃 */
+// export function getTenantList() {
+// return requestClient.get<SystemTenantApi.Tenant[]>(
+// '/system/tenant/simple-list',
+// );
+// }
+
+// /** 鏂板绉熸埛 */
+// export function createTenant(data: SystemTenantApi.Tenant) {
+// return requestClient.post('/system/tenant/create', data);
+// }
+
+// /** 淇敼绉熸埛 */
+// export function updateTenant(data: SystemTenantApi.Tenant) {
+// return requestClient.put('/system/tenant/update', data);
+// }
+
+// /** 鍒犻櫎绉熸埛 */
+// export function deleteTenant(id: number) {
+// return requestClient.delete(`/system/tenant/delete?id=${id}`);
+// }
+
+// /** 鎵归噺鍒犻櫎绉熸埛 */
+// export function deleteTenantList(ids: number[]) {
+// return requestClient.delete(
+// `/system/tenant/delete-list?ids=${ids.join(',')}`,
+// );
+// }
+
+// /** 瀵煎嚭绉熸埛 */
+// export function exportTenant(params: any) {
+// return requestClient.download('/system/tenant/export-excel', {
+// params,
+// });
+// }
+
+// 鎻愪緵绌哄嚱鏁颁互閬垮厤寮曠敤閿欒
export function getTenantPage(params: PageParam) {
- return requestClient.get<PageResult<SystemTenantApi.Tenant>>(
- '/system/tenant/page',
- { params },
- );
+ return Promise.resolve({ list: [], total: 0 });
}
-/** 鑾峰彇绉熸埛绮剧畝淇℃伅鍒楄〃 */
export function getSimpleTenantList() {
- return requestClient.get<SystemTenantApi.Tenant[]>(
- '/system/tenant/simple-list',
- );
+ return Promise.resolve([]);
}
-/** 鏌ヨ绉熸埛璇︽儏 */
export function getTenant(id: number) {
- return requestClient.get<SystemTenantApi.Tenant>(
- `/system/tenant/get?id=${id}`,
- );
+ return Promise.resolve({} as SystemTenantApi.Tenant);
}
-/** 鑾峰彇绉熸埛绮剧畝淇℃伅鍒楄〃 */
export function getTenantList() {
- return requestClient.get<SystemTenantApi.Tenant[]>(
- '/system/tenant/simple-list',
- );
+ return Promise.resolve([]);
}
-/** 鏂板绉熸埛 */
export function createTenant(data: SystemTenantApi.Tenant) {
- return requestClient.post('/system/tenant/create', data);
+ return Promise.resolve({});
}
-/** 淇敼绉熸埛 */
export function updateTenant(data: SystemTenantApi.Tenant) {
- return requestClient.put('/system/tenant/update', data);
+ return Promise.resolve({});
}
-/** 鍒犻櫎绉熸埛 */
export function deleteTenant(id: number) {
- return requestClient.delete(`/system/tenant/delete?id=${id}`);
+ return Promise.resolve({});
}
-/** 鎵归噺鍒犻櫎绉熸埛 */
export function deleteTenantList(ids: number[]) {
- return requestClient.delete(
- `/system/tenant/delete-list?ids=${ids.join(',')}`,
- );
+ return Promise.resolve({});
}
-/** 瀵煎嚭绉熸埛 */
export function exportTenant(params: any) {
- return requestClient.download('/system/tenant/export-excel', {
- params,
- });
+ return Promise.resolve(new Blob());
}
diff --git a/src/packages/constants/src/biz-mes-enum.ts b/src/packages/constants/src/biz-mes-enum.ts
index c790052..0a1b051 100644
--- a/src/packages/constants/src/biz-mes-enum.ts
+++ b/src/packages/constants/src/biz-mes-enum.ts
@@ -371,6 +371,13 @@
APPROVED: MesOrderStatusConstants.APPROVED,
} as const;
+/** MES 鍙戣揣閫氱煡鍗曞嚭搴撶姸鎬佹灇涓� */
+export const MesWmSalesNoticeOutStatusEnum = {
+ NONE: 0,
+ PARTIAL: 1,
+ ALL: 2,
+} as const;
+
/** MES 閿�鍞��璐у崟鐘舵�佹灇涓� */
export const MesWmReturnSalesStatusEnum = {
PREPARE: MesOrderStatusConstants.DRAFT,
diff --git a/src/views/basicData/mdm/components/batch-config.vue b/src/views/basicData/mdm/components/batch-config.vue
new file mode 100644
index 0000000..714bef4
--- /dev/null
+++ b/src/views/basicData/mdm/components/batch-config.vue
@@ -0,0 +1,147 @@
+<script lang="ts" setup>
+import type { MdmItemBatchConfigApi } from '#/api/mes/md/item-batch-config';
+
+import { computed, ref, watch } from 'vue';
+
+import { Checkbox, Divider } from 'ant-design-vue';
+
+import {
+ getItemBatchConfigByItemId,
+ saveItemBatchConfig,
+} from '#/api/mes/md/item-batch-config';
+
+const props = defineProps<{
+ itemId?: number;
+ isBatchManaged?: boolean;
+}>();
+
+const emit = defineEmits<{
+ change: [config: Partial<MdmItemBatchConfigApi.ItemBatchConfig>];
+}>();
+
+// 鎵规閰嶇疆鏁版嵁
+const config = ref<Partial<MdmItemBatchConfigApi.ItemBatchConfig>>({
+ produceDateFlag: false,
+ expireDateFlag: false,
+ receiptDateFlag: false,
+ vendorFlag: false,
+ purchaseOrderCodeFlag: false,
+ lotNumberFlag: false,
+ qualityStatusFlag: false,
+});
+
+// 鏄惁鍔犺浇涓�
+const loading = ref(false);
+
+// 鏄惁鏄剧ず閰嶇疆锛堝惎鐢ㄦ壒娆$鐞嗘椂鏄剧ず锛�
+const visible = computed(() => props.isBatchManaged === true);
+
+// 鎵规閰嶇疆閫夐」锛堜粎淇濈暀7涓瓧娈碉級
+const configOptions = [
+ { key: 'produceDateFlag', label: '鐢熶骇鏃ユ湡' },
+ { key: 'expireDateFlag', label: '鏈夋晥鏈�' },
+ { key: 'receiptDateFlag', label: '鍏ュ簱鏃ユ湡' },
+ { key: 'vendorFlag', label: '渚涘簲鍟�' },
+ { key: 'purchaseOrderCodeFlag', label: '閲囪喘璁㈠崟鍙�' },
+ { key: 'lotNumberFlag', label: '鐢熶骇鎵瑰彿' },
+ { key: 'qualityStatusFlag', label: '璐ㄩ噺鐘舵��' },
+] as const;
+
+/** 鍔犺浇鎵规閰嶇疆 */
+async function loadConfig() {
+ if (!props.itemId || !props.isBatchManaged) {
+ return;
+ }
+ loading.value = true;
+ try {
+ const data = await getItemBatchConfigByItemId(props.itemId);
+ if (data) {
+ config.value = { ...config.value, ...data };
+ }
+ } catch {
+ // 鏃犻厤缃椂涓嶆姤閿�
+ } finally {
+ loading.value = false;
+ }
+}
+
+/** 淇濆瓨鎵规閰嶇疆 */
+async function saveConfig(): Promise<number | undefined> {
+ if (!props.itemId || !props.isBatchManaged) {
+ return undefined;
+ }
+ const data: MdmItemBatchConfigApi.ItemBatchConfig = {
+ ...config.value as MdmItemBatchConfigApi.ItemBatchConfig,
+ itemId: props.itemId,
+ };
+ const id = await saveItemBatchConfig(data);
+ config.value.id = id;
+ return id;
+}
+
+/** 鑾峰彇褰撳墠閰嶇疆 */
+function getConfig(): Partial<MdmItemBatchConfigApi.ItemBatchConfig> {
+ return { ...config.value };
+}
+
+/** 閲嶇疆閰嶇疆 */
+function resetConfig() {
+ config.value = {
+ produceDateFlag: false,
+ expireDateFlag: false,
+ receiptDateFlag: false,
+ vendorFlag: false,
+ purchaseOrderCodeFlag: false,
+ lotNumberFlag: false,
+ qualityStatusFlag: false,
+ };
+}
+
+// 鐩戝惉 itemId 鍙樺寲锛屽姞杞介厤缃�
+watch(
+ () => [props.itemId, props.isBatchManaged],
+ () => {
+ if (props.itemId && props.isBatchManaged) {
+ loadConfig();
+ } else {
+ resetConfig();
+ }
+ },
+ { immediate: true },
+);
+
+// 鐩戝惉閰嶇疆鍙樺寲锛岃Е鍙� change 浜嬩欢
+watch(
+ config,
+ () => {
+ emit('change', getConfig());
+ },
+ { deep: true },
+);
+
+defineExpose({
+ saveConfig,
+ getConfig,
+ loadConfig,
+ resetConfig,
+});
+</script>
+
+<template>
+ <div v-if="visible" class="mt-4">
+ <Divider orientation="left">鎵规灞炴�ч厤缃�</Divider>
+ <div class="grid grid-cols-4 gap-4 px-4">
+ <Checkbox
+ v-for="option in configOptions"
+ :key="option.key"
+ v-model:checked="config[option.key]"
+ :disabled="loading"
+ >
+ {{ option.label }}
+ </Checkbox>
+ </div>
+ <p class="mt-2 px-4 text-xs text-gray-500">
+ 鍕鹃�夎〃绀哄垱寤烘壒娆℃椂璇ュ瓧娈靛繀濉�
+ </p>
+ </div>
+</template>
diff --git a/src/views/basicData/mdm/components/data.ts b/src/views/basicData/mdm/components/data.ts
new file mode 100644
index 0000000..db1af68
--- /dev/null
+++ b/src/views/basicData/mdm/components/data.ts
@@ -0,0 +1,111 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MdmItemApi } from '#/api/mdm/item';
+
+import { h } from 'vue';
+import { Tag } from 'ant-design-vue';
+
+/** 鐗╂枡閫夋嫨寮圭獥鎼滅储琛ㄥ崟 */
+export function useMdmItemSelectGridFormSchema(): VbenFormSchema[] {
+ return [
+ {
+ fieldName: 'code',
+ label: '鐗╂枡缂栫爜',
+ component: 'Input',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇疯緭鍏ョ墿鏂欑紪鐮�',
+ },
+ },
+ {
+ fieldName: 'name',
+ label: '鐗╂枡鍚嶇О',
+ component: 'Input',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�',
+ },
+ },
+ {
+ fieldName: 'itemType',
+ label: '鐗╂枡绫诲瀷',
+ component: 'Select',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇烽�夋嫨鐗╂枡绫诲瀷',
+ options: [
+ { label: '鍘熸枡', value: 1 },
+ { label: '鍗婃垚鍝�', value: 2 },
+ { label: '鎴愬搧', value: 3 },
+ { label: '杈呮枡', value: 4 },
+ ],
+ },
+ },
+ ];
+}
+
+/** 鐗╂枡绫诲瀷棰滆壊鏄犲皠 */
+const itemTypeMap: Record<number, { label: string; color: string }> = {
+ 1: { label: '鍘熸枡', color: 'green' },
+ 2: { label: '鍗婃垚鍝�', color: 'orange' },
+ 3: { label: '鎴愬搧', color: 'blue' },
+ 4: { label: '杈呮枡', color: 'purple' },
+};
+
+/** 鐗╂枡閫夋嫨寮圭獥鍒楄〃瀛楁 */
+export function useMdmItemSelectGridColumns(
+ multiple = true,
+): VxeTableGridOptions<MdmItemApi.Item>['columns'] {
+ return [
+ { type: multiple ? 'checkbox' : 'radio', width: 50 },
+ {
+ field: 'code',
+ title: '鐗╂枡缂栫爜',
+ width: 180,
+ },
+ {
+ field: 'name',
+ title: '鐗╂枡鍚嶇О',
+ minWidth: 160,
+ align: 'left',
+ },
+ {
+ field: 'specification',
+ title: '瑙勬牸鍨嬪彿',
+ minWidth: 140,
+ align: 'left',
+ },
+ {
+ field: 'unitMeasureName',
+ title: '鍗曚綅',
+ width: 90,
+ align: 'center',
+ },
+ {
+ field: 'itemType',
+ title: '鐗╂枡绫诲瀷',
+ width: 110,
+ align: 'center',
+ formatter: ({ cellValue }: { cellValue: number }) => {
+ const type = itemTypeMap[cellValue];
+ return type ? type.label : '-';
+ },
+ },
+ {
+ field: 'isBatchManaged',
+ title: '鎵规绠$悊',
+ width: 110,
+ align: 'center',
+ cellRender: {
+ name: 'CellDict',
+ props: { type: 'infra_boolean_string' },
+ },
+ },
+ {
+ field: 'createTime',
+ title: '鍒涘缓鏃堕棿',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ ];
+}
diff --git a/src/views/basicData/mdm/components/index.ts b/src/views/basicData/mdm/components/index.ts
new file mode 100644
index 0000000..662751f
--- /dev/null
+++ b/src/views/basicData/mdm/components/index.ts
@@ -0,0 +1,2 @@
+export { default as MdmItemSelect } from './select.vue';
+export { default as MdmItemSelectDialog } from './select-dialog.vue';
diff --git a/src/views/basicData/mdm/components/select-dialog.vue b/src/views/basicData/mdm/components/select-dialog.vue
new file mode 100644
index 0000000..9c7a48a
--- /dev/null
+++ b/src/views/basicData/mdm/components/select-dialog.vue
@@ -0,0 +1,209 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MdmItemApi } from '#/api/mdm/item';
+
+import { nextTick, ref } from 'vue';
+
+import { message, Modal } from 'ant-design-vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getItemPage } from '#/api/mdm/item';
+
+import {
+ useMdmItemSelectGridColumns,
+ useMdmItemSelectGridFormSchema,
+} from './data';
+
+defineOptions({ name: 'MdmItemSelectDialog' });
+
+const emit = defineEmits<{
+ selected: [rows: MdmItemApi.Item[]];
+}>();
+
+const open = ref(false); // 寮圭獥鏄惁鎵撳紑
+const multiple = ref(true); // 鏄惁澶氶��
+const selectedRows = ref<MdmItemApi.Item[]>([]); // 宸查�夌墿鏂欏垪琛�
+const preSelectedIds = ref<number[]>([]); // 棰勯�夌墿鏂欑紪鍙峰垪琛�
+
+/** 鑾峰彇褰撳墠琛ㄦ牸鏁版嵁 */
+function getTableRows() {
+ return gridApi.grid.getTableData().fullData as MdmItemApi.Item[];
+}
+
+/** 鑾峰彇澶氶�夎褰曪紝鍖呭惈 VXE reserve 璺ㄩ〉璁板綍 */
+function getMultipleSelectedRows() {
+ const selectedMap = new Map<number, MdmItemApi.Item>();
+ const records = [
+ ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
+ ...(gridApi.grid.getCheckboxRecords?.() ?? []),
+ ] as MdmItemApi.Item[];
+ records.forEach((row) => {
+ const rowId = row.id;
+ if (rowId) {
+ selectedMap.set(rowId, row);
+ }
+ });
+ return [...selectedMap.values()];
+}
+
+/** 澶勭悊鍕鹃�夊彉鍖� */
+function handleCheckboxSelectChange() {
+ selectedRows.value = getMultipleSelectedRows();
+}
+
+/** 澶勭悊鍗曢�夊彉鍖� */
+function handleRadioChange(row: MdmItemApi.Item) {
+ selectedRows.value = [row];
+}
+
+/** 澶氶�夋ā寮忎笅鍒囨崲琛屽嬀閫� */
+async function toggleMultipleRow(row: MdmItemApi.Item) {
+ const selected = gridApi.grid.isCheckedByCheckboxRow(row);
+ await gridApi.grid.setCheckboxRow(row, !selected);
+ selectedRows.value = getMultipleSelectedRows();
+}
+
+/** 澶勭悊琛屽弻鍑� */
+async function handleCellDblclick({ row }: { row: MdmItemApi.Item }) {
+ if (multiple.value) {
+ await toggleMultipleRow(row);
+ return;
+ }
+ selectedRows.value = [row];
+ await gridApi.grid.setRadioRow(row);
+ handleConfirm();
+}
+
+/** 鍥炴樉棰勯�夌墿鏂� */
+async function applyPreSelection() {
+ if (preSelectedIds.value.length === 0) {
+ return;
+ }
+ const rows = getTableRows();
+ for (const row of rows) {
+ if (
+ row.id === undefined ||
+ row.id === null ||
+ !preSelectedIds.value.includes(row.id)
+ ) {
+ continue;
+ }
+ if (multiple.value) {
+ await gridApi.grid.setCheckboxRow(row, true);
+ } else {
+ await gridApi.grid.setRadioRow(row);
+ selectedRows.value = [row];
+ return;
+ }
+ }
+ if (multiple.value) {
+ selectedRows.value = getMultipleSelectedRows();
+ }
+}
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ formOptions: {
+ schema: useMdmItemSelectGridFormSchema(),
+ },
+ gridOptions: {
+ columns: useMdmItemSelectGridColumns(true),
+ height: 560,
+ keepSource: true,
+ checkboxConfig: {
+ highlight: true,
+ range: true,
+ reserve: true,
+ },
+ radioConfig: {
+ highlight: true,
+ trigger: 'row',
+ },
+ proxyConfig: {
+ ajax: {
+ query: async ({ page }, formValues) => {
+ return await getItemPage({
+ pageNo: page.currentPage,
+ pageSize: page.pageSize,
+ ...formValues,
+ });
+ },
+ },
+ },
+ rowConfig: {
+ keyField: 'id',
+ isHover: true,
+ },
+ toolbarConfig: {
+ refresh: true,
+ search: true,
+ },
+ } as VxeTableGridOptions<MdmItemApi.Item>,
+ gridEvents: {
+ cellDblclick: handleCellDblclick,
+ checkboxAll: handleCheckboxSelectChange,
+ checkboxChange: handleCheckboxSelectChange,
+ radioChange: ({ row }: { row: MdmItemApi.Item }) => {
+ handleRadioChange(row);
+ },
+ },
+});
+
+/** 閲嶇疆鏌ヨ鍜岄�夋嫨鐘舵�� */
+async function resetQueryState() {
+ selectedRows.value = [];
+ await gridApi.grid.clearCheckboxRow();
+ await gridApi.grid.clearCheckboxReserve();
+ await gridApi.grid.clearRadioRow();
+ await gridApi.formApi.resetForm();
+}
+
+/** 鎵撳紑鐗╂枡閫夋嫨寮圭獥 */
+async function openModal(
+ selectedIds?: number[],
+ options?: { multiple?: boolean },
+) {
+ open.value = true;
+ multiple.value = options?.multiple ?? true;
+ preSelectedIds.value = selectedIds || [];
+ await nextTick();
+ gridApi.setGridOptions({
+ columns: useMdmItemSelectGridColumns(multiple.value),
+ });
+ await resetQueryState();
+ await gridApi.query();
+ await nextTick();
+ await applyPreSelection();
+}
+
+/** 鍏抽棴鐗╂枡閫夋嫨寮圭獥 */
+async function closeModal() {
+ open.value = false;
+ await resetQueryState();
+}
+
+/** 纭閫夋嫨鐗╂枡 */
+function handleConfirm() {
+ const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
+ if (rows.length === 0) {
+ message.warning(multiple.value ? '璇疯嚦灏戦�夋嫨涓�鏉℃暟鎹�' : '璇烽�夋嫨涓�鏉℃暟鎹�');
+ return;
+ }
+ emit('selected', multiple.value ? rows : [rows[0]!]);
+ open.value = false;
+}
+
+defineExpose({ open: openModal });
+</script>
+
+<template>
+ <Modal
+ v-model:open="open"
+ title="鐗╂枡閫夋嫨"
+ width="80%"
+ :destroy-on-close="true"
+ @ok="handleConfirm"
+ @cancel="closeModal"
+ >
+ <Grid table-title="鐗╂枡鍒楄〃" />
+ </Modal>
+</template>
diff --git a/src/views/basicData/mdm/components/select.vue b/src/views/basicData/mdm/components/select.vue
new file mode 100644
index 0000000..ad10d58
--- /dev/null
+++ b/src/views/basicData/mdm/components/select.vue
@@ -0,0 +1,138 @@
+<script lang="ts" setup>
+import type { MdmItemApi } from '#/api/mdm/item';
+
+import { computed, ref, useAttrs, watch } from 'vue';
+
+import { IconifyIcon } from '@vben/icons';
+
+import { Input, Tooltip } from 'ant-design-vue';
+
+import { getItem } from '#/api/mdm/item';
+
+import MdmItemSelectDialog from './select-dialog.vue';
+
+defineOptions({ name: 'MdmItemSelect', inheritAttrs: false });
+
+const props = withDefaults(
+ defineProps<{
+ allowClear?: boolean;
+ disabled?: boolean;
+ modelValue?: number;
+ placeholder?: string;
+ }>(),
+ {
+ allowClear: true,
+ disabled: false,
+ modelValue: undefined,
+ placeholder: '璇烽�夋嫨鐗╂枡',
+ },
+);
+const emit = defineEmits<{
+ change: [item: MdmItemApi.Item | undefined];
+ 'update:modelValue': [value: number | undefined];
+}>();
+const attrs = useAttrs(); // 閫忎紶灞炴��
+const dialogRef = ref<InstanceType<typeof MdmItemSelectDialog>>(); // 鐗╂枡閫夋嫨寮圭獥
+const hovering = ref(false); // 鏄惁鎮仠
+const selectedItem = ref<MdmItemApi.Item>(); // 褰撳墠閫変腑鐗╂枡
+
+const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 閫夋嫨鍣ㄥ睍绀哄悕绉�
+const showClear = computed(
+ () =>
+ props.allowClear &&
+ !props.disabled &&
+ hovering.value &&
+ props.modelValue !== null,
+);
+
+/** 鏍规嵁鐗╂枡缂栧彿鍥炴樉閫夋嫨鍣� */
+async function resolveItemById(id: number | undefined) {
+ if (!id) {
+ selectedItem.value = undefined;
+ return;
+ }
+ if (selectedItem.value?.id === id) {
+ return;
+ }
+ selectedItem.value = await getItem(id);
+}
+
+watch(
+ () => props.modelValue,
+ (value) => {
+ resolveItemById(value);
+ },
+ { immediate: true },
+);
+
+/** 娓呯┖宸查�夌墿鏂� */
+function clearSelected() {
+ selectedItem.value = undefined;
+ emit('update:modelValue', undefined);
+ emit('change', undefined);
+}
+
+/** 鎵撳紑鐗╂枡閫夋嫨寮圭獥 */
+function handleClick(event: MouseEvent) {
+ if (props.disabled) {
+ return;
+ }
+ const target = event.target as HTMLElement;
+ if (showClear.value && target.closest('.ant-input-suffix')) {
+ event.stopPropagation();
+ clearSelected();
+ return;
+ }
+ const selectedIds = (
+ props.modelValue === null ? [] : [props.modelValue]
+ ) as number[];
+ dialogRef.value?.open(selectedIds, { multiple: false });
+}
+
+/** 鍥炲~閫変腑鐨勭墿鏂� */
+function handleSelected(rows: MdmItemApi.Item[]) {
+ const item = rows[0];
+ if (!item) {
+ return;
+ }
+ selectedItem.value = item;
+ emit('update:modelValue', item.id);
+ emit('change', item);
+}
+</script>
+
+<template>
+ <div
+ v-bind="attrs"
+ class="w-full"
+ :class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
+ @click="handleClick"
+ @mouseenter="hovering = true"
+ @mouseleave="hovering = false"
+ >
+ <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
+ <template #title>
+ <div v-if="selectedItem" class="leading-6">
+ <div>缂栫爜锛歿{ selectedItem.code || '-' }}</div>
+ <div>鍚嶇О锛歿{ selectedItem.name || '-' }}</div>
+ <div>瑙勬牸锛歿{ selectedItem.specification || '-' }}</div>
+ <div>鍗曚綅锛歿{ selectedItem.unitMeasureName || '-' }}</div>
+ </div>
+ </template>
+ <Input
+ :disabled="disabled"
+ :placeholder="placeholder"
+ :value="displayLabel"
+ readonly
+ >
+ <template #suffix>
+ <IconifyIcon
+ class="size-4"
+ :icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
+ />
+ </template>
+ </Input>
+ </Tooltip>
+ </div>
+ <MdmItemSelectDialog ref="dialogRef" @selected="handleSelected" />
+</template>
diff --git a/src/views/basicData/mdm/data.ts b/src/views/basicData/mdm/data.ts
index 479f150..910564a 100644
--- a/src/views/basicData/mdm/data.ts
+++ b/src/views/basicData/mdm/data.ts
@@ -6,7 +6,6 @@
import { z } from '#/adapter/form';
import { getUnitPage } from '#/api/mdm/unit';
-import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse';
/** 鐗╂枡绫诲瀷閫夐」 */
export const ITEM_TYPE_OPTIONS = [
@@ -154,15 +153,11 @@
},
},
{
- component: 'ApiSelect',
+ component: 'Input',
fieldName: 'warehouseId',
label: '榛樿浠撳簱',
componentProps: {
- placeholder: '璇烽�夋嫨榛樿浠撳簱',
- allowClear: true,
- api: getWarehouseSimpleList,
- labelField: 'name',
- valueField: 'id',
+ placeholder: '璇疯緭鍏ラ粯璁や粨搴揑D',
},
},
{
@@ -243,6 +238,7 @@
component: 'Switch',
fieldName: 'isBatchManaged',
label: '鎵规绠$悊',
+ defaultValue: true,
componentProps: {
class: 'w-auto',
checkedChildren: '鏄�',
diff --git a/src/views/basicData/mdm/modules/form.vue b/src/views/basicData/mdm/modules/form.vue
index 27b8097..8335105 100644
--- a/src/views/basicData/mdm/modules/form.vue
+++ b/src/views/basicData/mdm/modules/form.vue
@@ -1,7 +1,9 @@
<script lang="ts" setup>
import type { MdmItemApi } from '#/api/mdm/item';
+import type { MdmItemBatchConfigApi } from '#/api/mes/md/item-batch-config';
+import type { VbenFormApi } from '#/adapter/form';
-import { computed, ref } from 'vue';
+import { computed, ref, watch } from 'vue';
import { useVbenModal } from '#/packages/effects/common-ui/src';
@@ -9,14 +11,22 @@
import { useVbenForm } from '#/adapter/form';
import { createItem, getItem, updateItem } from '#/api/mdm/item';
+import { saveItemBatchConfig } from '#/api/mes/md/item-batch-config';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
+import ItemBatchConfig from '../components/batch-config.vue';
defineOptions({ name: 'ItemForm' });
const emit = defineEmits(['success']);
const formData = ref<MdmItemApi.Item>();
+const batchConfigRef = ref<InstanceType<typeof ItemBatchConfig>>();
+const batchConfig = ref<Partial<MdmItemBatchConfigApi.ItemBatchConfig>>({});
+
+// 褰撳墠琛ㄥ崟涓殑鎵规绠$悊寮�鍏冲�硷紙鐢ㄤ簬鎺у埗鎵规閰嶇疆鏄剧ず锛�
+const currentIsBatchManaged = ref<boolean>(false);
+
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['鐗╂枡'])
@@ -34,6 +44,10 @@
schema: useFormSchema(),
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
+ // 鐩戝惉琛ㄥ崟鍊煎彉鍖�
+ handleValuesChange: (values: any) => {
+ currentIsBatchManaged.value = values?.isBatchManaged === true;
+ },
});
const [Modal, modalApi] = useVbenModal({
@@ -45,7 +59,18 @@
modalApi.lock();
const data = (await formApi.getValues()) as MdmItemApi.Item;
try {
- await (formData.value?.id ? updateItem(data) : createItem(data));
+ // 淇濆瓨鐗╂枡鍩烘湰淇℃伅
+ const itemId = await (formData.value?.id ? updateItem(data) : createItem(data));
+ const savedItemId = typeof itemId === 'number' ? itemId : data.id;
+
+ // 濡傛灉鍚敤鎵规绠$悊锛屼繚瀛樻壒娆¢厤缃�
+ if (data.isBatchManaged && savedItemId) {
+ await saveItemBatchConfig({
+ ...batchConfig.value,
+ itemId: savedItemId,
+ });
+ }
+
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
@@ -56,25 +81,50 @@
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
+ batchConfig.value = {};
+ currentIsBatchManaged.value = false;
return;
}
const data = modalApi.getData<MdmItemApi.Item>();
- if (!data || !data.id) {
- return;
- }
- modalApi.lock();
- try {
- formData.value = await getItem(data.id);
- await formApi.setValues(formData.value);
- } finally {
- modalApi.unlock();
+ if (data?.id) {
+ // 缂栬緫妯″紡
+ modalApi.lock();
+ try {
+ formData.value = await getItem(data.id);
+ await formApi.setValues(formData.value);
+ currentIsBatchManaged.value = formData.value?.isBatchManaged === true;
+ // 鍔犺浇鎵规閰嶇疆
+ if (batchConfigRef.value && currentIsBatchManaged.value) {
+ await batchConfigRef.value.loadConfig();
+ }
+ } finally {
+ modalApi.unlock();
+ }
+ } else {
+ // 鏂板妯″紡
+ formData.value = undefined;
+ await formApi.resetForm();
+ // 鑾峰彇琛ㄥ崟榛樿鍊硷紙isBatchManaged 榛樿 true锛�
+ const defaultValues = await formApi.getValues();
+ currentIsBatchManaged.value = defaultValues?.isBatchManaged === true;
}
},
});
+
+/** 澶勭悊鎵规閰嶇疆鍙樺寲 */
+function handleBatchConfigChange(config: Partial<MdmItemBatchConfigApi.ItemBatchConfig>) {
+ batchConfig.value = config;
+}
</script>
<template>
<Modal class="w-[1000px]" :title="getTitle">
<Form class="mx-4" />
+ <ItemBatchConfig
+ ref="batchConfigRef"
+ :item-id="formData?.id"
+ :is-batch-managed="currentIsBatchManaged"
+ @change="handleBatchConfigChange"
+ />
</Modal>
-</template>
\ No newline at end of file
+</template>
diff --git a/src/views/erp/sale/order/components/index.ts b/src/views/erp/sale/order/components/index.ts
new file mode 100644
index 0000000..1dbcbd2
--- /dev/null
+++ b/src/views/erp/sale/order/components/index.ts
@@ -0,0 +1,2 @@
+export { default as ErpSaleOrderSelect } from './select.vue';
+export { default as ErpSaleOrderSelectDialog } from './select-dialog.vue';
diff --git a/src/views/erp/sale/order/components/select-dialog.vue b/src/views/erp/sale/order/components/select-dialog.vue
new file mode 100644
index 0000000..9a1dfe1
--- /dev/null
+++ b/src/views/erp/sale/order/components/select-dialog.vue
@@ -0,0 +1,199 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
+
+import { nextTick, ref } from 'vue';
+
+import { message, Modal } from 'ant-design-vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getSaleOrderPage } from '#/api/erp/sale/order';
+
+defineOptions({ name: 'ErpSaleOrderSelectDialog' });
+
+const emit = defineEmits<{
+ selected: [rows: ErpSaleOrderApi.SaleOrder[]];
+}>();
+
+const open = ref(false);
+const multiple = ref(true);
+const status = ref<number | undefined>(undefined);
+const selectedRows = ref<ErpSaleOrderApi.SaleOrder[]>([]);
+const preSelectedIds = ref<number[]>([]);
+
+/** 鑾峰彇褰撳墠琛ㄦ牸鏁版嵁 */
+function getTableRows() {
+ return gridApi.grid.getTableData().fullData as ErpSaleOrderApi.SaleOrder[];
+}
+
+/** 鑾峰彇澶氶�夎褰� */
+function getMultipleSelectedRows() {
+ const selectedMap = new Map<number, ErpSaleOrderApi.SaleOrder>();
+ const records = [
+ ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
+ ...(gridApi.grid.getCheckboxRecords?.() ?? []),
+ ] as ErpSaleOrderApi.SaleOrder[];
+ records.forEach((row) => {
+ if (row.id) {
+ selectedMap.set(row.id, row);
+ }
+ });
+ return [...selectedMap.values()];
+}
+
+/** 澶勭悊鍕鹃�夊彉鍖� */
+function handleCheckboxSelectChange() {
+ selectedRows.value = getMultipleSelectedRows();
+}
+
+/** 澶勭悊鍗曢�夊彉鍖� */
+function handleRadioChange(row: ErpSaleOrderApi.SaleOrder) {
+ selectedRows.value = [row];
+}
+
+/** 澶勭悊琛屽弻鍑� */
+async function handleCellDblclick({ row }: { row: ErpSaleOrderApi.SaleOrder }) {
+ if (multiple.value) {
+ const selected = gridApi.grid.isCheckedByCheckboxRow(row);
+ await gridApi.grid.setCheckboxRow(row, !selected);
+ selectedRows.value = getMultipleSelectedRows();
+ return;
+ }
+ selectedRows.value = [row];
+ await gridApi.grid.setRadioRow(row);
+ handleConfirm();
+}
+
+/** 鍥炴樉棰勯�夐攢鍞鍗� */
+async function applyPreSelection() {
+ if (preSelectedIds.value.length === 0) {
+ return;
+ }
+ const rows = getTableRows();
+ for (const row of rows) {
+ if (row.id && preSelectedIds.value.includes(row.id)) {
+ if (multiple.value) {
+ await gridApi.grid.setCheckboxRow(row, true);
+ } else {
+ await gridApi.grid.setRadioRow(row);
+ selectedRows.value = [row];
+ return;
+ }
+ }
+ }
+ if (multiple.value) {
+ selectedRows.value = getMultipleSelectedRows();
+ }
+}
+
+/** 鍒楄〃瀛楁 */
+function useGridColumns(isMultiple: boolean): VxeTableGridOptions<ErpSaleOrderApi.SaleOrder>['columns'] {
+ return [
+ { type: isMultiple ? 'checkbox' : 'radio', width: 50 },
+ { field: 'no', title: '璁㈠崟鍙�', minWidth: 140 },
+ { field: 'customerName', title: '瀹㈡埛', minWidth: 120 },
+ { field: 'orderTime', title: '璁㈠崟鏃堕棿', width: 120, formatter: 'formatDate' },
+ { field: 'totalCount', title: '鍚堣鏁伴噺', width: 100 },
+ { field: 'totalPrice', title: '鍚堣閲戦', width: 100 },
+ ];
+}
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ gridOptions: {
+ columns: useGridColumns(true),
+ height: 400,
+ keepSource: true,
+ checkboxConfig: {
+ highlight: true,
+ reserve: true,
+ },
+ radioConfig: {
+ highlight: true,
+ trigger: 'row',
+ },
+ proxyConfig: {
+ ajax: {
+ query: async ({ page }, formValues) => {
+ return await getSaleOrderPage({
+ pageNo: page.currentPage,
+ pageSize: page.pageSize,
+ status: status.value,
+ ...formValues,
+ });
+ },
+ },
+ },
+ rowConfig: {
+ keyField: 'id',
+ isHover: true,
+ },
+ } as VxeTableGridOptions<ErpSaleOrderApi.SaleOrder>,
+ gridEvents: {
+ cellDblclick: handleCellDblclick,
+ checkboxAll: handleCheckboxSelectChange,
+ checkboxChange: handleCheckboxSelectChange,
+ radioChange: ({ row }: { row: ErpSaleOrderApi.SaleOrder }) => {
+ handleRadioChange(row);
+ },
+ },
+});
+
+/** 閲嶇疆鏌ヨ鍜岄�夋嫨鐘舵�� */
+async function resetQueryState() {
+ selectedRows.value = [];
+ await gridApi.grid.clearCheckboxRow();
+ await gridApi.grid.clearCheckboxReserve();
+ await gridApi.grid.clearRadioRow();
+}
+
+/** 鎵撳紑閿�鍞鍗曢�夋嫨寮圭獥 */
+async function openModal(
+ selectedIds?: number[],
+ options?: { multiple?: boolean; status?: number },
+) {
+ open.value = true;
+ multiple.value = options?.multiple ?? true;
+ status.value = options?.status;
+ preSelectedIds.value = selectedIds || [];
+ await nextTick();
+ gridApi.setGridOptions({
+ columns: useGridColumns(multiple.value),
+ });
+ await resetQueryState();
+ await gridApi.query();
+ await nextTick();
+ await applyPreSelection();
+}
+
+/** 鍏抽棴閿�鍞鍗曢�夋嫨寮圭獥 */
+async function closeModal() {
+ open.value = false;
+ await resetQueryState();
+}
+
+/** 纭閫夋嫨閿�鍞鍗� */
+function handleConfirm() {
+ const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
+ if (rows.length === 0) {
+ message.warning(multiple.value ? '璇疯嚦灏戦�夋嫨涓�鏉℃暟鎹�' : '璇烽�夋嫨涓�鏉℃暟鎹�');
+ return;
+ }
+ emit('selected', multiple.value ? rows : [rows[0]!]);
+ open.value = false;
+}
+
+defineExpose({ open: openModal });
+</script>
+
+<template>
+ <Modal
+ v-model:open="open"
+ title="閿�鍞鍗曢�夋嫨"
+ width="70%"
+ :destroy-on-close="true"
+ @ok="handleConfirm"
+ @cancel="closeModal"
+ >
+ <Grid table-title="閿�鍞鍗曞垪琛�" />
+ </Modal>
+</template>
diff --git a/src/views/erp/sale/order/components/select.vue b/src/views/erp/sale/order/components/select.vue
new file mode 100644
index 0000000..12652d0
--- /dev/null
+++ b/src/views/erp/sale/order/components/select.vue
@@ -0,0 +1,135 @@
+<script lang="ts" setup>
+import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
+
+import { computed, ref, useAttrs, watch } from 'vue';
+
+import { IconifyIcon } from '@vben/icons';
+
+import { Input, Tooltip } from 'ant-design-vue';
+
+import { getSaleOrder } from '#/api/erp/sale/order';
+
+import ErpSaleOrderSelectDialog from './select-dialog.vue';
+
+defineOptions({ name: 'ErpSaleOrderSelect', inheritAttrs: false });
+
+const props = withDefaults(
+ defineProps<{
+ allowClear?: boolean;
+ disabled?: boolean;
+ modelValue?: number;
+ placeholder?: string;
+ status?: number;
+ }>(),
+ {
+ allowClear: true,
+ disabled: false,
+ modelValue: undefined,
+ placeholder: '璇烽�夋嫨閿�鍞鍗�',
+ status: undefined,
+ },
+);
+
+const emit = defineEmits(['change', 'update:modelValue']);
+
+const attrs = useAttrs();
+const dialogRef = ref<InstanceType<typeof ErpSaleOrderSelectDialog>>();
+const hovering = ref(false);
+const selectedItem = ref<ErpSaleOrderApi.SaleOrder>();
+
+const displayLabel = computed(() => selectedItem.value?.no ?? '');
+const showClear = computed(
+ () =>
+ props.allowClear &&
+ !props.disabled &&
+ hovering.value &&
+ props.modelValue !== null,
+);
+
+async function resolveItemById(id: number | undefined) {
+ if (!id) {
+ selectedItem.value = undefined;
+ return;
+ }
+ if (selectedItem.value?.id === id) {
+ return;
+ }
+ selectedItem.value = await getSaleOrder(id);
+}
+
+watch(
+ () => props.modelValue,
+ (value) => {
+ resolveItemById(value);
+ },
+ { immediate: true },
+);
+
+function clearSelected() {
+ selectedItem.value = undefined;
+ emit('update:modelValue', undefined);
+ emit('change', undefined);
+}
+
+function handleClick(event: MouseEvent) {
+ if (props.disabled) {
+ return;
+ }
+ const target = event.target as HTMLElement;
+ if (showClear.value && target.closest('.ant-input-suffix')) {
+ event.stopPropagation();
+ clearSelected();
+ return;
+ }
+ const selectedIds = (
+ props.modelValue === null ? [] : [props.modelValue]
+ ) as number[];
+ dialogRef.value?.open(selectedIds, { multiple: false, status: props.status });
+}
+
+function handleSelected(rows: ErpSaleOrderApi.SaleOrder[]) {
+ const item = rows[0];
+ if (!item) {
+ return;
+ }
+ selectedItem.value = item;
+ emit('update:modelValue', item.id);
+ emit('change', item);
+}
+</script>
+
+<template>
+ <div
+ v-bind="attrs"
+ class="w-full"
+ :class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
+ @click="handleClick"
+ @mouseenter="hovering = true"
+ @mouseleave="hovering = false"
+ >
+ <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
+ <template #title>
+ <div v-if="selectedItem" class="leading-6">
+ <div>璁㈠崟鍙凤細{{ selectedItem.no || '-' }}</div>
+ <div>瀹㈡埛锛歿{ selectedItem.customerName || '-' }}</div>
+ <div>璁㈠崟鏃堕棿锛歿{ selectedItem.orderTime || '-' }}</div>
+ <div>鍚堣閲戦锛歿{ selectedItem.totalPrice || '-' }} 鍏�</div>
+ </div>
+ </template>
+ <Input
+ :disabled="disabled"
+ :placeholder="placeholder"
+ :value="displayLabel"
+ readonly
+ >
+ <template #suffix>
+ <IconifyIcon
+ class="size-4"
+ :icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
+ />
+ </template>
+ </Input>
+ </Tooltip>
+ </div>
+ <ErpSaleOrderSelectDialog ref="dialogRef" @selected="handleSelected" />
+</template>
diff --git a/src/views/erp/sale/order/data.ts b/src/views/erp/sale/order/data.ts
index 9363c82..4428be2 100644
--- a/src/views/erp/sale/order/data.ts
+++ b/src/views/erp/sale/order/data.ts
@@ -270,7 +270,7 @@
},
{
title: '鎿嶄綔',
- width: 50,
+ width: 320,
fixed: 'right',
slots: { default: 'actions' },
visible: !disabled,
diff --git a/src/views/erp/sale/order/index.vue b/src/views/erp/sale/order/index.vue
index c6bd0cd..99cf346 100644
--- a/src/views/erp/sale/order/index.vue
+++ b/src/views/erp/sale/order/index.vue
@@ -19,6 +19,7 @@
getSaleOrderPage,
updateSaleOrderStatus,
} from '#/api/erp/sale/order';
+import { generateFromSaleOrder } from '#/api/mes/wm/salesnotice';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
@@ -136,9 +137,23 @@
function handleViewContract(row: ErpSaleOrderApi.SaleOrder) {
if (row.contractId) {
router.push({
- path: '/crm/contract/detail',
- query: { id: row.contractId },
+ path: `/crm/contract/detail/${row.contractId}`,
});
+ }
+}
+
+/** 鐢熸垚鍙戣揣閫氱煡鍗� */
+async function handleGenerateNotice(row: ErpSaleOrderApi.SaleOrder) {
+ try {
+ const noticeId = await generateFromSaleOrder(row.id!);
+ message.success('鐢熸垚鍙戣揣閫氱煡鍗曟垚鍔�');
+ // 璺宠浆鍒板彂璐ч�氱煡鍗曢〉闈�
+ router.push({
+ path: '/wls/sales-notice',
+ query: { id: noticeId },
+ });
+ } catch (e) {
+ console.error(e);
}
}
@@ -296,6 +311,14 @@
},
},
{
+ label: '鐢熸垚鍙戣揣閫氱煡鍗�',
+ type: 'link',
+ icon: 'ant-design:file-protect-outlined',
+ auth: ['mes:wm-sales-notice:create'],
+ ifShow: () => row.status === 20,
+ onClick: handleGenerateNotice.bind(null, row),
+ },
+ {
label: $t('common.delete'),
type: 'link',
danger: true,
diff --git a/src/views/wls/materialstock/components/batch-config-display.vue b/src/views/wls/materialstock/components/batch-config-display.vue
new file mode 100644
index 0000000..ff4c52b
--- /dev/null
+++ b/src/views/wls/materialstock/components/batch-config-display.vue
@@ -0,0 +1,96 @@
+<script lang="ts" setup>
+import type { MdmItemBatchConfigApi } from '#/api/mes/md/item-batch-config';
+
+import { computed, ref, watch } from 'vue';
+
+import { Divider, Tag } from 'ant-design-vue';
+
+import { getItemBatchConfigByItemId } from '#/api/mes/md/item-batch-config';
+
+const props = defineProps<{
+ itemId?: number;
+ isBatchManaged?: boolean;
+}>();
+
+// 鎵规閰嶇疆鏁版嵁
+const config = ref<MdmItemBatchConfigApi.ItemBatchConfig | null>(null);
+
+// 鏄惁鍔犺浇涓�
+const loading = ref(false);
+
+// 鏄惁鏄剧ず锛堝惎鐢ㄦ壒娆$鐞嗘椂鏄剧ず锛�
+const visible = computed(() => props.isBatchManaged === true);
+
+// 宸插惎鐢ㄧ殑鎵规灞炴�у垪琛紙浠呬繚鐣�7涓瓧娈碉級
+const enabledProps = computed(() => {
+ if (!config.value) return [];
+ const props: { label: string; value: boolean }[] = [];
+ if (config.value.produceDateFlag) props.push({ label: '鐢熶骇鏃ユ湡', value: true });
+ if (config.value.expireDateFlag) props.push({ label: '鏈夋晥鏈�', value: true });
+ if (config.value.receiptDateFlag) props.push({ label: '鍏ュ簱鏃ユ湡', value: true });
+ if (config.value.vendorFlag) props.push({ label: '渚涘簲鍟�', value: true });
+ if (config.value.purchaseOrderCodeFlag) props.push({ label: '閲囪喘璁㈠崟鍙�', value: true });
+ if (config.value.lotNumberFlag) props.push({ label: '鐢熶骇鎵瑰彿', value: true });
+ if (config.value.qualityStatusFlag) props.push({ label: '璐ㄩ噺鐘舵��', value: true });
+ return props;
+});
+
+/** 鍔犺浇鎵规閰嶇疆 */
+async function loadConfig() {
+ if (!props.itemId || !props.isBatchManaged) {
+ config.value = null;
+ return;
+ }
+ loading.value = true;
+ try {
+ const data = await getItemBatchConfigByItemId(props.itemId);
+ config.value = data;
+ } catch {
+ config.value = null;
+ } finally {
+ loading.value = false;
+ }
+}
+
+/** 閲嶇疆 */
+function reset() {
+ config.value = null;
+ loading.value = false;
+}
+
+// 鐩戝惉 itemId 鍙樺寲锛屽姞杞介厤缃�
+watch(
+ () => [props.itemId, props.isBatchManaged],
+ () => {
+ if (props.itemId && props.isBatchManaged) {
+ loadConfig();
+ } else {
+ reset();
+ }
+ },
+ { immediate: true },
+);
+
+defineExpose({
+ loadConfig,
+ reset,
+});
+</script>
+
+<template>
+ <div v-if="visible" class="mt-4 rounded border border-gray-200 bg-gray-50 p-4">
+ <Divider orientation="left">鎵规灞炴��</Divider>
+ <div v-if="loading" class="text-center text-gray-500">鍔犺浇涓�...</div>
+ <div v-else-if="enabledProps.length === 0" class="text-center text-gray-500">
+ 璇ョ墿鏂欐湭閰嶇疆鎵规灞炴��
+ </div>
+ <div v-else class="flex flex-wrap gap-2">
+ <Tag v-for="prop in enabledProps" :key="prop.label" color="blue">
+ {{ prop.label }}
+ </Tag>
+ </div>
+ <p class="mt-2 text-xs text-gray-500">
+ 浠ヤ笂瀛楁鍒涘缓鎵规鏃跺繀濉�
+ </p>
+ </div>
+</template>
diff --git a/src/views/wls/materialstock/components/index.ts b/src/views/wls/materialstock/components/index.ts
index aa23039..2cb4faf 100644
--- a/src/views/wls/materialstock/components/index.ts
+++ b/src/views/wls/materialstock/components/index.ts
@@ -1,2 +1,3 @@
export { default as WmMaterialStockSelectDialog } from './select-dialog.vue';
export { default as WmMaterialStockSelect } from './select.vue';
+export { default as WmStockAdjustModal } from './stock-adjust-modal.vue';
diff --git a/src/views/wls/materialstock/components/stock-adjust-modal.vue b/src/views/wls/materialstock/components/stock-adjust-modal.vue
new file mode 100644
index 0000000..31f31e9
--- /dev/null
+++ b/src/views/wls/materialstock/components/stock-adjust-modal.vue
@@ -0,0 +1,488 @@
+<script lang="ts" setup>
+import type {
+ MaterialStockAdjustInParams,
+ MaterialStockAdjustOutParams,
+} from '#/api/mes/wm/materialstock';
+import type { MdmItemApi } from '#/api/mdm/item';
+import type { MdmItemBatchConfigApi } from '#/api/mes/md/item-batch-config';
+import type { VbenFormSchema } from '#/adapter/form';
+
+import { ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+import { z } from '#/adapter/form';
+import { useVbenForm } from '#/adapter/form';
+import { message } from 'ant-design-vue';
+import { markRaw } from 'vue';
+
+import {
+ adjustMaterialStockIn,
+ adjustMaterialStockOut,
+} from '#/api/mes/wm/materialstock';
+import { getItem } from '#/api/mdm/item';
+import { getItemBatchConfigByItemId } from '#/api/mes/md/item-batch-config';
+import { MdmItemSelect } from '#/views/basicData/mdm/components';
+import { MdVendorSelect } from '#/views/mes/md/vendor/components';
+import BatchConfigDisplay from './batch-config-display.vue';
+import {
+ WmWarehouseAreaSelect,
+ WmWarehouseLocationSelect,
+ WmWarehouseSelect,
+} from '#/views/wls/warehouse/components';
+
+const emit = defineEmits<{
+ success: [];
+}>();
+
+// 鎿嶄綔绫诲瀷锛�1=鍏ュ簱锛�2=鍑哄簱
+const operateType = ref<1 | 2>(1);
+const selectedItem = ref<MdmItemApi.Item | null>(null);
+const selectedWarehouse = ref<any>(null);
+const selectedLocation = ref<any>(null);
+
+// 鏄惁鍚敤鎵规绠$悊
+const batchEnabled = ref(false);
+// 鏄惁鎵嬪姩濉啓鎵规鍙�
+const manualBatchEntry = ref(false);
+// 鐗╂枡鎵规閰嶇疆
+const batchConfig = ref<MdmItemBatchConfigApi.ItemBatchConfig | null>(null);
+// 鎵规閰嶇疆灞曠ず缁勪欢寮曠敤
+const batchConfigDisplayRef = ref<InstanceType<typeof BatchConfigDisplay>>();
+
+// 鏍囬
+const modalTitle = ref('搴撳瓨璋冩暣');
+
+/** 鍔犺浇鐗╂枡鎵规閰嶇疆骞舵洿鏂板瓧娈垫樉绀� */
+async function loadBatchConfigAndUpdateFields(itemId: number) {
+ try {
+ const config = await getItemBatchConfigByItemId(itemId);
+ batchConfig.value = config;
+ } catch {
+ batchConfig.value = null;
+ }
+ await updateBatchFields();
+}
+
+/** 鏇存柊鎵规瀛楁鏄剧ず锛堟牴鎹厤缃睍绀哄凡鍚敤鐨勫睘鎬э級 */
+async function updateBatchFields() {
+ const enabled = selectedItem.value?.isBatchManaged === true;
+ batchEnabled.value = enabled;
+
+ if (!enabled) {
+ // 闅愯棌鎵�鏈夋壒娆$浉鍏冲瓧娈�
+ formApi.updateSchema([
+ { fieldName: 'batchCode', dependencies: { show: () => false } },
+ { fieldName: 'produceDate', dependencies: { show: () => false } },
+ { fieldName: 'expireDate', dependencies: { show: () => false } },
+ { fieldName: 'manualBatchEntry', dependencies: { show: () => false } },
+ { fieldName: 'vendorId', dependencies: { show: () => true } },
+ ]);
+ return;
+ }
+
+ // 鍚敤鎵规绠$悊锛屾牴鎹厤缃樉绀哄瓧娈�
+ const config = batchConfig.value;
+ const isIn = operateType.value === 1;
+
+ // 鏄惁鎵嬪姩濉啓鎵规鍙锋帶鍒舵壒娆″彿瀛楁鐨勬樉绀�
+ const showBatchCode = manualBatchEntry.value;
+ const showProduceDate = isIn && !!config?.produceDateFlag;
+ const showExpireDate = isIn && !!config?.expireDateFlag;
+
+ formApi.updateSchema([
+ { fieldName: 'batchCode', dependencies: { show: () => showBatchCode } },
+ { fieldName: 'produceDate', dependencies: { show: () => showProduceDate } },
+ { fieldName: 'expireDate', dependencies: { show: () => showExpireDate } },
+ { fieldName: 'manualBatchEntry', dependencies: { show: () => true } },
+ ]);
+}
+
+/** 鐗╂枡閫夋嫨鍙樺寲 */
+async function handleItemChange(item: MdmItemApi.Item | undefined) {
+ selectedItem.value = item || null;
+ if (item?.id) {
+ const itemDetail = await getItem(item.id);
+ selectedItem.value = itemDetail;
+ // 鍔犺浇鐗╂枡鎵规閰嶇疆骞舵洿鏂板瓧娈�
+ await loadBatchConfigAndUpdateFields(item.id);
+ } else {
+ batchConfig.value = null;
+ batchEnabled.value = false;
+ }
+ // 娓呯┖鎵规鐩稿叧鍊�
+ await formApi.setFieldValue('batchCode', undefined);
+ await formApi.setFieldValue('produceDate', undefined);
+ await formApi.setFieldValue('expireDate', undefined);
+ await formApi.setFieldValue('manualBatchEntry', false);
+}
+
+/** 浠撳簱閫夋嫨鍙樺寲 */
+async function handleWarehouseChange(item: any) {
+ selectedWarehouse.value = item;
+ await formApi.setFieldValue('locationId', undefined);
+ await formApi.setFieldValue('areaId', undefined);
+}
+
+/** 搴撳尯閫夋嫨鍙樺寲 */
+async function handleLocationChange(item: any) {
+ selectedLocation.value = item;
+ await formApi.setFieldValue('areaId', undefined);
+}
+
+/** 鎵嬪姩濉啓鎵规鍙峰彉鍖� */
+async function handleManualBatchEntryChange(value: boolean) {
+ manualBatchEntry.value = value;
+ await updateBatchFields();
+ if (!value) {
+ await formApi.setFieldValue('batchCode', undefined);
+ }
+}
+/** 鎿嶄綔绫诲瀷鍙樺寲 */
+async function handleOperateTypeChange(type: 1 | 2) {
+ operateType.value = type;
+ modalTitle.value = type === 1 ? '搴撳瓨鍏ュ簱' : '搴撳瓨鍑哄簱';
+ await formApi.setFieldValue('operateType', type);
+ // 鏇存柊鎵规瀛楁鏄剧ず
+ await updateBatchFields();
+}
+
+/** 鑾峰彇琛ㄥ崟 schema */
+function getSchema(): VbenFormSchema[] {
+ return [
+ {
+ fieldName: 'operateType',
+ label: '鎿嶄綔绫诲瀷',
+ component: 'RadioGroup',
+ defaultValue: 1,
+ componentProps: {
+ options: [
+ { label: '鍏ュ簱', value: 1 },
+ { label: '鍑哄簱', value: 2 },
+ ],
+ onChange: handleOperateTypeChange,
+ },
+ rules: 'required',
+ },
+ {
+ fieldName: 'itemId',
+ label: '鐗╂枡',
+ component: markRaw(MdmItemSelect),
+ componentProps: {
+ placeholder: '璇烽�夋嫨鐗╂枡',
+ onChange: handleItemChange,
+ },
+ rules: 'selectRequired',
+ },
+ {
+ fieldName: 'warehouseId',
+ label: '浠撳簱',
+ component: markRaw(WmWarehouseSelect),
+ componentProps: {
+ placeholder: '璇烽�夋嫨浠撳簱',
+ onChange: handleWarehouseChange,
+ },
+ rules: 'selectRequired',
+ },
+ {
+ fieldName: 'locationId',
+ label: '搴撳尯',
+ component: markRaw(WmWarehouseLocationSelect),
+ componentProps: {
+ placeholder: '璇烽�夋嫨搴撳尯',
+ warehouseId: selectedWarehouse.value?.id,
+ onChange: handleLocationChange,
+ },
+ dependencies: {
+ triggerFields: ['warehouseId'],
+ componentProps: (values: any) => ({
+ placeholder: '璇烽�夋嫨搴撳尯',
+ warehouseId: values.warehouseId,
+ onChange: handleLocationChange,
+ }),
+ trigger: (values: any, formApi: any) => {
+ if (values.locationId !== undefined) {
+ void formApi.setFieldValue('locationId', undefined);
+ void formApi.setFieldValue('areaId', undefined);
+ }
+ },
+ },
+ rules: 'selectRequired',
+ },
+ {
+ fieldName: 'areaId',
+ label: '搴撲綅',
+ component: markRaw(WmWarehouseAreaSelect),
+ componentProps: {
+ placeholder: '璇烽�夋嫨搴撲綅',
+ locationId: selectedLocation.value?.id,
+ },
+ dependencies: {
+ triggerFields: ['locationId'],
+ componentProps: (values: any) => ({
+ placeholder: '璇烽�夋嫨搴撲綅',
+ locationId: values.locationId,
+ }),
+ trigger: (values: any, formApi: any) => {
+ if (values.areaId !== undefined) {
+ void formApi.setFieldValue('areaId', undefined);
+ }
+ },
+ },
+ rules: 'selectRequired',
+ },
+ // 鏄惁鎵嬪姩濉啓鎵规鍙凤紙鍚敤鎵规绠$悊鏃舵樉绀猴級
+ {
+ fieldName: 'manualBatchEntry',
+ label: '鎵嬪姩濉啓鎵规鍙�',
+ component: 'Switch',
+ defaultValue: false,
+ componentProps: {
+ class: 'w-auto',
+ checkedChildren: '鏄�',
+ unCheckedChildren: '鍚�',
+ onChange: handleManualBatchEntryChange,
+ },
+ dependencies: {
+ triggerFields: ['itemId'],
+ show: () => false, // 榛樿闅愯棌锛屽惎鐢ㄦ壒娆$鐞嗘椂鏄剧ず
+ },
+ },
+ // 鎵规鍙凤紙鎵嬪姩濉啓鎵规鍙锋椂鏄剧ず锛�
+ {
+ fieldName: 'batchCode',
+ label: '鎵规鍙�',
+ component: 'Input',
+ componentProps: {
+ placeholder: '璇疯緭鍏ユ壒娆″彿',
+ },
+ dependencies: {
+ triggerFields: ['itemId', 'manualBatchEntry'],
+ show: () => false, // 榛樿闅愯棌锛屾牴鎹墜鍔ㄥ~鍐欒缃樉绀�
+ },
+ // 鏍规嵁閰嶇疆鍐冲畾鏄惁蹇呭~
+ rules: z.string().optional(),
+ },
+ // 鐢熶骇鏃ユ湡锛堟牴鎹壒娆¢厤缃樉绀猴紝浠呭叆搴擄級
+ {
+ fieldName: 'produceDate',
+ label: '鐢熶骇鏃ユ湡',
+ component: 'DatePicker',
+ componentProps: {
+ placeholder: '璇烽�夋嫨鐢熶骇鏃ユ湡',
+ format: 'YYYY-MM-DD',
+ valueFormat: 'YYYY-MM-DD',
+ },
+ dependencies: {
+ triggerFields: ['itemId'],
+ show: () => false, // 榛樿闅愯棌
+ },
+ rules: 'required',
+ },
+ // 鏈夋晥鏈燂紙鏍规嵁鎵规閰嶇疆鏄剧ず锛屼粎鍏ュ簱锛�
+ {
+ fieldName: 'expireDate',
+ label: '鏈夋晥鏈熻嚦',
+ component: 'DatePicker',
+ componentProps: {
+ placeholder: '璇烽�夋嫨鏈夋晥鏈�',
+ format: 'YYYY-MM-DD',
+ valueFormat: 'YYYY-MM-DD',
+ },
+ dependencies: {
+ triggerFields: ['itemId'],
+ show: () => false, // 榛樿闅愯棌
+ },
+ },
+ {
+ fieldName: 'vendorId',
+ label: '渚涘簲鍟�',
+ component: markRaw(MdVendorSelect),
+ componentProps: {
+ placeholder: '璇烽�夋嫨渚涘簲鍟�',
+ },
+ rules: 'selectRequired',
+ },
+ {
+ fieldName: 'quantity',
+ label: '鏁伴噺',
+ component: 'InputNumber',
+ componentProps: {
+ class: '!w-full',
+ placeholder: '璇疯緭鍏ユ暟閲�',
+ precision: 2,
+ min: 0.01,
+ },
+ rules: z.number().positive('鏁伴噺蹇呴』澶т簬0'),
+ },
+ {
+ fieldName: 'reason',
+ label: '鍘熷洜',
+ component: 'Textarea',
+ formItemClass: 'col-span-2',
+ componentProps: {
+ placeholder: '璇疯緭鍏ュ師鍥�',
+ rows: 3,
+ },
+ },
+ ];
+}
+
+const [Form, formApi] = useVbenForm({
+ commonConfig: {
+ componentProps: {
+ class: 'w-full',
+ },
+ formItemClass: 'col-span-1',
+ labelWidth: 100,
+ },
+ wrapperClass: 'grid-cols-2',
+ layout: 'horizontal',
+ schema: getSchema(),
+ showDefaultActions: false,
+});
+
+const [Modal, modalApi] = useVbenModal({
+ fullscreen: false,
+ onCancel: () => {
+ modalApi.close();
+ },
+ onConfirm: async () => {
+ await handleSubmit();
+ },
+ onOpenChange: async (isOpen: boolean) => {
+ if (!isOpen) {
+ selectedItem.value = null;
+ selectedWarehouse.value = null;
+ selectedLocation.value = null;
+ batchEnabled.value = false;
+ batchConfig.value = null;
+ operateType.value = 1;
+ modalTitle.value = '搴撳瓨璋冩暣';
+ return;
+ }
+ // 閲嶇疆琛ㄥ崟
+ await formApi.resetForm();
+ // 榛樿闅愯棌鎵规鐩稿叧瀛楁
+ formApi.updateSchema([
+ { fieldName: 'batchCode', dependencies: { show: () => false } },
+ { fieldName: 'produceDate', dependencies: { show: () => false } },
+ { fieldName: 'expireDate', dependencies: { show: () => false } },
+ { fieldName: 'manualBatchEntry', dependencies: { show: () => false } },
+ ]);
+ // 閲嶇疆鎵嬪姩濉啓鎵规鍙风姸鎬�
+ manualBatchEntry.value = false;
+ // 濡傛灉鏈変紶鍏ョ墿鏂欎俊鎭紝鍥炲~琛ㄥ崟
+ const data = modalApi.getData();
+ if (data?.itemId) {
+ await formApi.setValues({
+ itemId: data.itemId,
+ warehouseId: data.warehouseId,
+ locationId: data.locationId,
+ areaId: data.areaId,
+ });
+ if (data.itemId) {
+ const itemDetail = await getItem(data.itemId);
+ selectedItem.value = itemDetail;
+ await loadBatchConfigAndUpdateFields(data.itemId);
+ }
+ }
+ },
+ title: modalTitle,
+});
+
+/** 鎻愪氦琛ㄥ崟 */
+async function handleSubmit() {
+ const { valid } = await formApi.validate();
+ if (!valid) {
+ return;
+ }
+
+ const values = await formApi.getValues();
+
+ const {
+ itemId,
+ warehouseId,
+ locationId,
+ areaId,
+ batchCode,
+ produceDate,
+ expireDate,
+ vendorId,
+ quantity,
+ reason,
+ } = values;
+
+ // 鍚敤鎵规绠$悊鏃剁殑鏍¢獙
+ if (batchEnabled.value) {
+ // 鎵嬪姩濉啓鎵规鍙锋椂锛屾壒娆″彿蹇呭~
+ if (manualBatchEntry.value && !batchCode) {
+ message.error('璇疯緭鍏ユ壒娆″彿');
+ return;
+ }
+ // 鏍规嵁鎵规閰嶇疆鏍¢獙蹇呭~瀛楁锛堜粎鍏ュ簱锛�
+ if (operateType.value === 1) {
+ const config = batchConfig.value;
+ if (config?.produceDateFlag && !produceDate) {
+ message.error('璇烽�夋嫨鐢熶骇鏃ユ湡');
+ return;
+ }
+ if (config?.expireDateFlag && !expireDate) {
+ message.error('璇烽�夋嫨鏈夋晥鏈�');
+ return;
+ }
+ }
+ }
+
+ try {
+ const type = operateType.value;
+
+ if (type === 1) {
+ // 鍏ュ簱
+ const params: MaterialStockAdjustInParams = {
+ itemId,
+ warehouseId,
+ locationId,
+ areaId,
+ batchCode,
+ produceDate,
+ expireDate,
+ vendorId,
+ quantity,
+ reason,
+ };
+ await adjustMaterialStockIn(params);
+ message.success('鍏ュ簱鎴愬姛');
+ } else {
+ // 鍑哄簱
+ const params: MaterialStockAdjustOutParams = {
+ itemId,
+ warehouseId,
+ locationId,
+ areaId,
+ batchCode,
+ vendorId,
+ quantity,
+ reason,
+ };
+ await adjustMaterialStockOut(params);
+ message.success('鍑哄簱鎴愬姛');
+ }
+
+ emit('success');
+ modalApi.close();
+ } catch (error) {
+ // 閿欒鐢辫姹傛嫤鎴櫒澶勭悊
+ }
+}
+</script>
+
+<template>
+ <Modal class="w-3/5">
+ <Form class="mx-4" />
+ <BatchConfigDisplay
+ ref="batchConfigDisplayRef"
+ :item-id="selectedItem?.id"
+ :is-batch-managed="batchEnabled"
+ />
+ </Modal>
+</template>
diff --git a/src/views/wls/materialstock/data.ts b/src/views/wls/materialstock/data.ts
index c6f0ceb..4befafd 100644
--- a/src/views/wls/materialstock/data.ts
+++ b/src/views/wls/materialstock/data.ts
@@ -108,6 +108,41 @@
width: 100,
},
{
+ field: 'frozenQuantity',
+ title: '鍐荤粨鏁伴噺',
+ width: 100,
+ formatter: ({ cellValue }: { cellValue: number }) =>
+ (cellValue || 0).toFixed(2),
+ },
+ {
+ field: 'reservedQuantity',
+ title: '鍗犵敤閲�',
+ width: 100,
+ formatter: ({ cellValue }: { cellValue: number }) =>
+ (cellValue || 0).toFixed(2),
+ },
+ {
+ field: 'inTransitQuantity',
+ title: '鍦ㄩ�旈噺',
+ width: 100,
+ formatter: ({ cellValue }: { cellValue: number }) =>
+ (cellValue || 0).toFixed(2),
+ },
+ {
+ field: 'availableQuantity',
+ title: '鍙敤閲�',
+ width: 100,
+ cellRender: {
+ name: 'CellText',
+ props: {
+ class: ({ row }: { row: MesWmMaterialStockApi.MaterialStock }) =>
+ (row.availableQuantity || 0) < 0 ? 'text-red-500 font-bold' : '',
+ formatter: ({ row }: { row: MesWmMaterialStockApi.MaterialStock }) =>
+ ((row.availableQuantity || 0) as number).toFixed(2),
+ },
+ },
+ },
+ {
field: 'unitMeasureName',
title: '鍗曚綅',
width: 80,
@@ -288,6 +323,41 @@
width: 100,
},
{
+ field: 'frozenQuantity',
+ title: '鍐荤粨鏁伴噺',
+ width: 100,
+ formatter: ({ cellValue }: { cellValue: number }) =>
+ (cellValue || 0).toFixed(2),
+ },
+ {
+ field: 'reservedQuantity',
+ title: '鍗犵敤閲�',
+ width: 100,
+ formatter: ({ cellValue }: { cellValue: number }) =>
+ (cellValue || 0).toFixed(2),
+ },
+ {
+ field: 'inTransitQuantity',
+ title: '鍦ㄩ�旈噺',
+ width: 100,
+ formatter: ({ cellValue }: { cellValue: number }) =>
+ (cellValue || 0).toFixed(2),
+ },
+ {
+ field: 'availableQuantity',
+ title: '鍙敤閲�',
+ width: 100,
+ cellRender: {
+ name: 'CellText',
+ props: {
+ class: ({ row }: { row: MesWmMaterialStockApi.MaterialStock }) =>
+ (row.availableQuantity || 0) < 0 ? 'text-red-500 font-bold' : '',
+ formatter: ({ row }: { row: MesWmMaterialStockApi.MaterialStock }) =>
+ ((row.availableQuantity || 0) as number).toFixed(2),
+ },
+ },
+ },
+ {
field: 'receiptTime',
title: '鍏ュ簱鏃ユ湡',
width: 180,
diff --git a/src/views/wls/materialstock/index.vue b/src/views/wls/materialstock/index.vue
index a9353f0..3ae7731 100644
--- a/src/views/wls/materialstock/index.vue
+++ b/src/views/wls/materialstock/index.vue
@@ -19,11 +19,17 @@
import { MdItemTypeTree } from '#/views/mes/md/item/type/components';
import { WmBatchDetail } from '#/views/wls/batch/components';
import AreaForm from '#/views/wls/warehouse/area/modules/form.vue';
+import { WmStockAdjustModal } from './components';
import { useGridColumns, useGridFormSchema } from './data';
const [AreaModal, areaModalApi] = useVbenModal({
connectedComponent: AreaForm,
+ destroyOnClose: true,
+});
+
+const [StockAdjustModal, stockAdjustModalApi] = useVbenModal({
+ connectedComponent: WmStockAdjustModal,
destroyOnClose: true,
});
@@ -56,6 +62,16 @@
return;
}
areaModalApi.setData({ formType: 'detail', id: row.areaId }).open();
+}
+
+/** 鎵撳紑搴撳瓨璋冩暣寮圭獥 */
+function handleOpenStockAdjust() {
+ stockAdjustModalApi.setData({}).open();
+}
+
+/** 搴撳瓨璋冩暣鎴愬姛鍥炶皟 */
+function handleStockAdjustSuccess() {
+ gridApi.query();
}
/** 鎵撳紑鎵规璇︽儏寮圭獥 */
@@ -122,6 +138,7 @@
<template>
<Page auto-content-height><AreaModal />
<WmBatchDetail ref="batchDetailRef" />
+ <StockAdjustModal @success="handleStockAdjustSuccess" />
<div class="flex h-full w-full">
<!-- 宸︿晶鐗╂枡鍒嗙被鏍� -->
@@ -135,6 +152,13 @@
<TableAction
:actions="[
{
+ label: '搴撳瓨璋冩暣',
+ type: 'default',
+ icon: ACTION_ICON.EDIT,
+ auth: ['mes:wm-material-stock:adjust'],
+ onClick: handleOpenStockAdjust,
+ },
+ {
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
diff --git a/src/views/wls/salesnotice/data.ts b/src/views/wls/salesnotice/data.ts
index f9a1931..fcadf3c 100644
--- a/src/views/wls/salesnotice/data.ts
+++ b/src/views/wls/salesnotice/data.ts
@@ -5,12 +5,17 @@
import { h, markRaw } from 'vue';
-import { DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants';
+import {
+ DICT_TYPE,
+ MesAutoCodeRuleCode,
+ MesWmSalesNoticeOutStatusEnum,
+} from '@vben/constants';
-import { Button } from 'ant-design-vue';
+import { Button, Tag } from 'ant-design-vue';
import { z } from '#/adapter/form';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
+import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components';
import { MdClientSelect } from '#/views/mes/md/client/components';
import { MdItemSelect } from '#/views/mes/md/item/components';
@@ -79,11 +84,27 @@
rules: 'required',
},
{
- fieldName: 'salesOrderCode',
- label: '閿�鍞鍗曠紪鍙�',
- component: 'Input',
+ fieldName: 'saleOrderId',
+ label: '閿�鍞鍗�',
+ component: markRaw(ErpSaleOrderSelect),
componentProps: {
- placeholder: '璇疯緭鍏ラ攢鍞鍗曠紪鍙�',
+ placeholder: '璇烽�夋嫨閿�鍞鍗�',
+ // 閫夋嫨閿�鍞鍗曞悗锛岃嚜鍔ㄥ洖濉鎴蜂俊鎭�
+ onChange: (item: any) => {
+ if (item?.customerId && formApi) {
+ formApi.setFieldValue('clientId', item.customerId);
+ formApi.setFieldValue('clientName', item.customerName);
+ }
+ },
+ },
+ },
+ {
+ fieldName: 'clientName',
+ label: '',
+ component: 'Input',
+ dependencies: {
+ triggerFields: [''],
+ show: () => false,
},
},
{
@@ -92,6 +113,15 @@
component: markRaw(MdClientSelect),
componentProps: {
placeholder: '璇烽�夋嫨瀹㈡埛',
+ disabled: true,
+ },
+ dependencies: {
+ triggerFields: ['clientName'],
+ componentProps: (values) => ({
+ placeholder: '璇烽�夋嫨瀹㈡埛',
+ disabled: true,
+ clientName: values.clientName as string,
+ }),
},
rules: 'selectRequired',
},
@@ -240,6 +270,25 @@
},
},
{
+ field: 'outStatus',
+ title: '鍑哄簱鐘舵��',
+ minWidth: 100,
+ cellRender: {
+ name: 'CellTag',
+ props: {
+ formatter: ({ row }: { row: MesWmSalesNoticeApi.SalesNotice }) => {
+ const statusMap: Record<number, { color: string; label: string }> = {
+ [MesWmSalesNoticeOutStatusEnum.NONE]: { color: 'default', label: '鏈嚭搴�' },
+ [MesWmSalesNoticeOutStatusEnum.PARTIAL]: { color: 'warning', label: '閮ㄥ垎鍑哄簱' },
+ [MesWmSalesNoticeOutStatusEnum.ALL]: { color: 'success', label: '鍏ㄩ儴鍑哄簱' },
+ };
+ const status = statusMap[row.outStatus ?? MesWmSalesNoticeOutStatusEnum.NONE];
+ return { color: status?.color, label: status?.label ?? '鏈嚭搴�' };
+ },
+ },
+ },
+ },
+ {
title: '鎿嶄綔',
width: 200,
fixed: 'right',
@@ -279,6 +328,28 @@
width: 100,
},
{
+ field: 'outCount',
+ title: '宸插嚭搴撴暟閲�',
+ width: 100,
+ formatter: ({ cellValue }: { cellValue: number }) => (cellValue || 0).toFixed(2),
+ },
+ {
+ field: 'outProgress',
+ title: '鍑哄簱杩涘害',
+ width: 120,
+ cellRender: {
+ name: 'CellText',
+ props: {
+ formatter: ({ row }: { row: MesWmSalesNoticeLineApi.SalesNoticeLine }) => {
+ const quantity = row.quantity || 0;
+ const outCount = row.outCount || 0;
+ const percent = quantity > 0 ? Math.round((outCount / quantity) * 100) : 0;
+ return `${outCount.toFixed(2)} / ${quantity.toFixed(2)} (${percent}%)`;
+ },
+ },
+ },
+ },
+ {
field: 'batchCode',
title: '鎵规鍙�',
minWidth: 120,
diff --git a/src/views/wls/salesnotice/index.vue b/src/views/wls/salesnotice/index.vue
index cd323e8..db5f8a5 100644
--- a/src/views/wls/salesnotice/index.vue
+++ b/src/views/wls/salesnotice/index.vue
@@ -8,10 +8,13 @@
import { Button, message } from 'ant-design-vue';
+import { useRouter } from 'vue-router';
+
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteSalesNotice,
exportSalesNotice,
+ generateProductSales,
getSalesNoticePage,
} from '#/api/mes/wm/salesnotice';
import { $t } from '#/locales';
@@ -23,6 +26,8 @@
connectedComponent: Form,
destroyOnClose: true,
});
+
+const router = useRouter();
/** 鍒锋柊琛ㄦ牸 */
function handleRefresh() {
@@ -47,6 +52,21 @@
/** 鎵ц鍑哄簱 */
function handleFinish(row: MesWmSalesNoticeApi.SalesNotice) {
formModalApi.setData({ formType: 'finish', id: row.id }).open();
+}
+
+/** 鐢熸垚閿�鍞嚭搴撳崟 */
+async function handleGenerateProductSales(row: MesWmSalesNoticeApi.SalesNotice) {
+ try {
+ const productSalesId = await generateProductSales(row.id!);
+ message.success('鐢熸垚閿�鍞嚭搴撳崟鎴愬姛');
+ // 璺宠浆鍒伴攢鍞嚭搴撳崟椤甸潰
+ router.push({
+ path: '/wls/product-sales',
+ query: { id: productSalesId },
+ });
+ } catch (e) {
+ console.error(e);
+ }
}
/** 鍒犻櫎鍙戣揣閫氱煡鍗� */
@@ -154,6 +174,14 @@
},
},
{
+ label: '鐢熸垚鍑哄簱鍗�',
+ type: 'link',
+ icon: 'ant-design:file-protect-outlined',
+ auth: ['mes:wm-product-sales:create'],
+ ifShow: row.status === MesWmSalesNoticeStatusEnum.APPROVED,
+ onClick: handleGenerateProductSales.bind(null, row),
+ },
+ {
label: '鎵ц鍑哄簱',
type: 'link',
auth: ['mes:wm-sales-notice:update'],
--
Gitblit v1.9.3