From e1e0d48eb7ab2b305e6062f23610f8be9db3bed6 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 29 八月 2026 13:46:26 +0800
Subject: [PATCH] feat(wms): 新增库存汇总统计与预警功能
---
src/views/wls/stocktaking/task/index.vue | 29 +
src/views/wls/stocktaking/task/modules/result-list.vue | 65 +++
src/api/mes/wm/stock-warning/index.ts | 61 +++
src/views/wls/stock-warning/data.ts | 211 +++++++++++++
src/views/wls/stock-warning/modules/current-modal.vue | 63 +++
src/api/mes/wm/stocktaking/task/index.ts | 19 +
src/views/wls/stock-summary/data.ts | 130 ++++++++
src/views/wls/stock-warning/modules/handle-modal.vue | 74 ++++
src/views/wls/stock-warning/index.vue | 133 ++++++++
src/packages/constants/src/biz-mes-enum.ts | 20 +
src/api/mes/wm/materialstock/index.ts | 48 +++
src/views/wls/stock-summary/index.vue | 87 +++++
12 files changed, 938 insertions(+), 2 deletions(-)
diff --git a/src/api/mes/wm/materialstock/index.ts b/src/api/mes/wm/materialstock/index.ts
index 3527368..7316dc2 100644
--- a/src/api/mes/wm/materialstock/index.ts
+++ b/src/api/mes/wm/materialstock/index.ts
@@ -36,6 +36,20 @@
frozen?: boolean; // 鏄惁鍐荤粨
createTime?: Date; // 鍒涘缓鏃堕棿
}
+
+ /** 搴撳瓨姹囨�荤粨鏋� */
+ export interface MaterialStockSummary {
+ itemId?: number; // 鍝佺ID锛堟寜鍝佺姹囨�绘椂杩斿洖锛�
+ itemCode?: string; // 鍝佺缂栫爜
+ itemName?: string; // 鍝佺鍚嶇О
+ batchId?: number; // 鎵规ID锛堟寜鎵规姹囨�绘椂杩斿洖锛�
+ batchCode?: string; // 鎵规鍙�
+ warehouseId?: number; // 浠撳簱ID锛堟寜浠撳簱姹囨�绘椂杩斿洖锛�
+ warehouseName?: string; // 浠撳簱鍚嶇О
+ stockState?: number; // 搴撳瓨鐘舵��
+ stockStateName?: string; // 搴撳瓨鐘舵�佸悕绉�
+ totalQuantity?: number; // 姹囨�绘暟閲�
+ }
}
/** 鏌ヨ搴撳瓨鍙拌处鍒嗛〉 */
@@ -142,3 +156,37 @@
export function adjustMaterialStock(data: MaterialStockAdjustParams) {
return requestClient.post<number>('/mes/wm/material-stock/adjust', data);
}
+
+/** 搴撳瓨姹囨�绘煡璇㈠弬鏁� */
+export interface MaterialStockSummaryParams {
+ stockState?: number; // 搴撳瓨鐘舵�侊細10鏆傚瓨搴擄紝20鍚堟牸搴擄紝30宸插嚭搴�
+ itemId?: number; // 鍝佺ID
+ batchCode?: string; // 鎵规鍙�
+ warehouseId?: number; // 浠撳簱ID
+ locationId?: number; // 搴撳尯ID
+ areaId?: number; // 搴撲綅ID
+}
+
+/** 鎸夊搧绉嶆眹鎬诲簱瀛樻暟閲� */
+export function getStockSummaryByItem(params: MaterialStockSummaryParams) {
+ return requestClient.get<MesWmMaterialStockApi.MaterialStockSummary[]>(
+ '/mes/wm/material-stock/summary-by-item',
+ { params },
+ );
+}
+
+/** 鎸夋壒娆℃眹鎬诲簱瀛樻暟閲� */
+export function getStockSummaryByBatch(params: MaterialStockSummaryParams) {
+ return requestClient.get<MesWmMaterialStockApi.MaterialStockSummary[]>(
+ '/mes/wm/material-stock/summary-by-batch',
+ { params },
+ );
+}
+
+/** 鎸変粨搴撴眹鎬诲簱瀛樻暟閲� */
+export function getStockSummaryByWarehouse(params: MaterialStockSummaryParams) {
+ return requestClient.get<MesWmMaterialStockApi.MaterialStockSummary[]>(
+ '/mes/wm/material-stock/summary-by-warehouse',
+ { params },
+ );
+}
diff --git a/src/api/mes/wm/stock-warning/index.ts b/src/api/mes/wm/stock-warning/index.ts
new file mode 100644
index 0000000..05dfbba
--- /dev/null
+++ b/src/api/mes/wm/stock-warning/index.ts
@@ -0,0 +1,61 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace MesWmStockWarningApi {
+ /** MES 搴撳瓨棰勮璁板綍 */
+ export interface StockWarning {
+ id?: number; // 缂栧彿
+ itemId?: number; // 鍝佺ID
+ itemCode?: string; // 鍝佺缂栫爜
+ itemName?: string; // 鍝佺鍚嶇О
+ batchId?: number; // 鎵规ID
+ batchCode?: string; // 鎵规鍙�
+ warehouseId?: number; // 浠撳簱ID
+ warehouseName?: string; // 浠撳簱鍚嶇О
+ warningType?: number; // 棰勮绫诲瀷锛�10-浣庡簱瀛橈紝20-涓存湡锛�30-搴撻緞
+ warningTypeName?: string; // 棰勮绫诲瀷鍚嶇О
+ thresholdValue?: number; // 闃堝�硷紙浣庡簱瀛�=鏈�浣庡簱瀛橈紝涓存湡=鍓╀綑澶╂暟锛屽簱榫�=瓒呴緞澶╂暟锛�
+ currentValue?: number; // 褰撳墠鍊硷紙浣庡簱瀛�=鍦ㄥ簱鏁伴噺锛屼复鏈�=鍓╀綑澶╂暟锛屽簱榫�=搴撻緞澶╂暟锛�
+ expireDate?: string; // 杩囨湡鏃ユ湡锛堜复鏈熼璀︽墠鏈夛級
+ receiptTime?: string; // 鍏ュ簱鏃堕棿锛堝簱榫勯璀︽墠鏈夛級
+ status?: number; // 澶勭悊鐘舵�侊細10-鏈鐞嗭紝20-宸插鐞�
+ statusName?: string; // 澶勭悊鐘舵�佸悕绉�
+ handleUserId?: number; // 澶勭悊浜篒D
+ handleTime?: string; // 澶勭悊鏃堕棿
+ handleRemark?: string; // 澶勭悊澶囨敞
+ }
+
+ /** 搴撳瓨棰勮鍒嗛〉鏌ヨ鍙傛暟 */
+ export interface StockWarningPageParams extends PageParam {
+ itemCode?: string; // 鍝佺缂栫爜妯$硦鏌ヨ
+ batchCode?: string; // 鎵规鍙锋ā绯婃煡璇�
+ warningType?: number; // 棰勮绫诲瀷
+ status?: number; // 澶勭悊鐘舵��
+ }
+}
+
+/** 鏌ヨ搴撳瓨棰勮鍒嗛〉锛堣惤搴撹褰曪級 */
+export function getStockWarningPage(params: MesWmStockWarningApi.StockWarningPageParams) {
+ return requestClient.get<PageResult<MesWmStockWarningApi.StockWarning>>(
+ '/mes/wm/stock-warning/page',
+ { params },
+ );
+}
+
+/** 瀹炴椂璁$畻褰撳墠搴撳瓨棰勮锛堜笉钀藉簱锛� */
+export function getCurrentStockWarnings() {
+ return requestClient.get<MesWmStockWarningApi.StockWarning[]>(
+ '/mes/wm/stock-warning/current',
+ );
+}
+
+/** 閲嶆柊璁$畻骞惰惤搴撳綋鍓嶅簱瀛橀璀� */
+export function refreshStockWarnings() {
+ return requestClient.post<number>('/mes/wm/stock-warning/refresh');
+}
+
+/** 澶勭悊搴撳瓨棰勮锛堟爣璁板凡澶勭悊锛� */
+export function handleStockWarning(data: { id: number; handleRemark?: string }) {
+ return requestClient.put<boolean>('/mes/wm/stock-warning/handle', data);
+}
diff --git a/src/api/mes/wm/stocktaking/task/index.ts b/src/api/mes/wm/stocktaking/task/index.ts
index 77622f3..d438400 100644
--- a/src/api/mes/wm/stocktaking/task/index.ts
+++ b/src/api/mes/wm/stocktaking/task/index.ts
@@ -84,3 +84,22 @@
params,
});
}
+
+/** PDA 鎵爜鐩樼偣 */
+export function scanStockTaking(data: {
+ taskId: number; // 鐩樼偣浠诲姟缂栧彿
+ scanCode: string; // 杩芥函鐮�/琚嬬爜/鎵樼洏鐮�/鎵规鍙�
+ takingQuantity: number; // 鐩樼偣鏁伴噺
+ remark?: string; // 澶囨敞
+}) {
+ return requestClient.post<number>('/mes/wm/stocktaking-task/scan', data);
+}
+
+/** 澶勭悊鐩樼偣宸紓锛堢洏鐩堝叆搴�/鐩樹簭鍑哄簱锛岃皟鏁村簱瀛橈級 */
+export function processStockTakingDifference(id: number) {
+ return requestClient.post<boolean>(
+ '/mes/wm/stocktaking-task/process-difference',
+ null,
+ { params: { id } },
+ );
+}
diff --git a/src/packages/constants/src/biz-mes-enum.ts b/src/packages/constants/src/biz-mes-enum.ts
index 4c1981e..e26d510 100644
--- a/src/packages/constants/src/biz-mes-enum.ts
+++ b/src/packages/constants/src/biz-mes-enum.ts
@@ -226,6 +226,26 @@
LOSS: 3,
} as const;
+/** MES 搴撳瓨鐘舵�佹灇涓撅紙瀵归綈鍚庣 MesWmStockStateEnum锛� */
+export const MesWmStockStateEnum = {
+ TEMP: 10, // 鏆傚瓨搴擄紙寰呰川妫�锛�
+ QUALIFIED: 20, // 鍚堟牸搴擄紙姝e紡搴撳瓨锛�
+ OUT: 30, // 宸插嚭搴�
+} as const;
+
+/** MES 搴撳瓨棰勮绫诲瀷鏋氫妇 */
+export const MesWmStockWarningTypeEnum = {
+ LOW_STOCK: 10, // 浣庡簱瀛�
+ EXPIRING: 20, // 涓存湡
+ AGEING: 30, // 搴撻緞
+} as const;
+
+/** MES 搴撳瓨棰勮澶勭悊鐘舵�佹灇涓� */
+export const MesWmStockWarningStatusEnum = {
+ UNHANDLED: 10, // 鏈鐞�
+ HANDLED: 20, // 宸插鐞�
+} as const;
+
/** MES 鐩樼偣鏂规鍙傛暟绫诲瀷鏋氫妇 */
export const MesWmStockTakingParamTypeEnum = {
WAREHOUSE: 102,
diff --git a/src/views/wls/stock-summary/data.ts b/src/views/wls/stock-summary/data.ts
new file mode 100644
index 0000000..63af57f
--- /dev/null
+++ b/src/views/wls/stock-summary/data.ts
@@ -0,0 +1,130 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
+
+import { markRaw } from 'vue';
+
+import { MdItemSelect } from '#/views/mes/md/item/components';
+import { WmWarehouseSelect } from '../warehouse/components';
+
+export type SummaryDimension = 'item' | 'batch' | 'warehouse';
+
+export const SUMMARY_DIMENSION_OPTIONS: {
+ label: string;
+ value: SummaryDimension;
+}[] = [
+ { label: '鎸夊搧绉嶆眹鎬�', value: 'item' },
+ { label: '鎸夋壒娆℃眹鎬�', value: 'batch' },
+ { label: '鎸変粨搴撴眹鎬�', value: 'warehouse' },
+];
+
+/** 姹囨�绘煡璇㈣〃鍗� */
+export function useGridFormSchema(): VbenFormSchema[] {
+ return [
+ {
+ fieldName: 'itemId',
+ label: '鍝佺',
+ component: markRaw(MdItemSelect),
+ componentProps: {
+ placeholder: '璇烽�夋嫨鍝佺',
+ },
+ },
+ {
+ fieldName: 'batchCode',
+ label: '鎵规鍙�',
+ component: 'Input',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇疯緭鍏ユ壒娆″彿',
+ },
+ },
+ {
+ fieldName: 'warehouseId',
+ label: '浠撳簱',
+ component: markRaw(WmWarehouseSelect),
+ componentProps: {
+ placeholder: '璇烽�夋嫨浠撳簱',
+ },
+ },
+ {
+ fieldName: 'stockState',
+ label: '搴撳瓨鐘舵��',
+ component: 'Select',
+ componentProps: {
+ allowClear: true,
+ options: [
+ { label: '鏆傚瓨搴擄紙寰呰川妫�锛�', value: 10 },
+ { label: '鍚堟牸搴擄紙姝e紡搴撳瓨锛�', value: 20 },
+ { label: '宸插嚭搴擄紙閿�鍞墸鍑忥級', value: 30 },
+ ],
+ placeholder: '璇烽�夋嫨搴撳瓨鐘舵��',
+ },
+ },
+ ];
+}
+
+/** 姹囨�诲垪琛ㄥ瓧娈� */
+export function useGridColumns(
+ dimension: SummaryDimension,
+): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStockSummary>['columns'] {
+ const base: VxeTableGridOptions<MesWmMaterialStockApi.MaterialStockSummary>['columns'] = [
+ {
+ field: 'stockState',
+ title: '搴撳瓨鐘舵��',
+ width: 110,
+ cellRender: {
+ name: 'CellTag',
+ props: {
+ options: [
+ { label: '鏆傚瓨搴�', value: 10, color: 'orange' },
+ { label: '鍚堟牸搴�', value: 20, color: 'green' },
+ { label: '宸插嚭搴�', value: 30, color: 'default' },
+ ],
+ },
+ },
+ },
+ {
+ field: 'totalQuantity',
+ title: '姹囨�绘暟閲�',
+ width: 120,
+ },
+ ];
+ if (dimension === 'item') {
+ return [
+ {
+ field: 'itemCode',
+ title: '鍝佺缂栫爜',
+ minWidth: 120,
+ },
+ {
+ field: 'itemName',
+ title: '鍝佺鍚嶇О',
+ minWidth: 140,
+ },
+ ...base,
+ ];
+ }
+ if (dimension === 'batch') {
+ return [
+ {
+ field: 'itemName',
+ title: '鍝佺鍚嶇О',
+ minWidth: 140,
+ },
+ {
+ field: 'batchCode',
+ title: '鎵规鍙�',
+ minWidth: 160,
+ },
+ ...base,
+ ];
+ }
+ return [
+ {
+ field: 'warehouseName',
+ title: '浠撳簱鍚嶇О',
+ minWidth: 140,
+ },
+ ...base,
+ ];
+}
diff --git a/src/views/wls/stock-summary/index.vue b/src/views/wls/stock-summary/index.vue
new file mode 100644
index 0000000..a8abedf
--- /dev/null
+++ b/src/views/wls/stock-summary/index.vue
@@ -0,0 +1,87 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
+
+import { ref } from 'vue';
+
+import { Page } from '@vben/common-ui';
+
+import { Segmented } from 'ant-design-vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+ getStockSummaryByBatch,
+ getStockSummaryByItem,
+ getStockSummaryByWarehouse,
+} from '#/api/mes/wm/materialstock';
+
+import {
+ SUMMARY_DIMENSION_OPTIONS,
+ useGridColumns,
+ useGridFormSchema,
+ type SummaryDimension,
+} from './data';
+
+/** 褰撳墠姹囨�荤淮搴� */
+const dimension = ref<SummaryDimension>('item');
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ formOptions: {
+ schema: useGridFormSchema(),
+ },
+ gridOptions: {
+ columns: useGridColumns(dimension.value),
+ height: 'auto',
+ keepSource: true,
+ proxyConfig: {
+ ajax: {
+ query: async (_, formValues) => {
+ const params = { ...formValues };
+ if (dimension.value === 'item') {
+ const list = await getStockSummaryByItem(params);
+ return { list, total: list.length };
+ }
+ if (dimension.value === 'batch') {
+ const list = await getStockSummaryByBatch(params);
+ return { list, total: list.length };
+ }
+ const list = await getStockSummaryByWarehouse(params);
+ return { list, total: list.length };
+ },
+ },
+ },
+ pagerConfig: {
+ enabled: false,
+ },
+ rowConfig: {
+ isHover: true,
+ },
+ toolbarConfig: {
+ refresh: true,
+ search: true,
+ },
+ } as VxeTableGridOptions<MesWmMaterialStockApi.MaterialStockSummary>,
+});
+
+/** 鍒囨崲姹囨�荤淮搴� */
+function handleDimensionChange(value: string | number) {
+ dimension.value = value as SummaryDimension;
+ gridApi.setGridOptions({
+ columns: useGridColumns(dimension.value),
+ });
+ gridApi.reload();
+}
+</script>
+
+<template>
+ <Page auto-content-height>
+ <div class="mb-4">
+ <Segmented
+ :options="SUMMARY_DIMENSION_OPTIONS"
+ :value="dimension"
+ @change="handleDimensionChange"
+ />
+ </div>
+ <Grid table-title="搴撳瓨澶氱淮姹囨��" />
+ </Page>
+</template>
diff --git a/src/views/wls/stock-warning/data.ts b/src/views/wls/stock-warning/data.ts
new file mode 100644
index 0000000..c83b5e5
--- /dev/null
+++ b/src/views/wls/stock-warning/data.ts
@@ -0,0 +1,211 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmStockWarningApi } from '#/api/mes/wm/stock-warning';
+
+/** 鍒楄〃鐨勬悳绱㈣〃鍗� */
+export function useGridFormSchema(): VbenFormSchema[] {
+ return [
+ {
+ fieldName: 'itemCode',
+ label: '鍝佺缂栫爜',
+ component: 'Input',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇疯緭鍏ュ搧绉嶇紪鐮�',
+ },
+ },
+ {
+ fieldName: 'batchCode',
+ label: '鎵规鍙�',
+ component: 'Input',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇疯緭鍏ユ壒娆″彿',
+ },
+ },
+ {
+ fieldName: 'warningType',
+ label: '棰勮绫诲瀷',
+ component: 'Select',
+ componentProps: {
+ allowClear: true,
+ options: [
+ { label: '浣庡簱瀛�', value: 10 },
+ { label: '涓存湡', value: 20 },
+ { label: '搴撻緞', value: 30 },
+ ],
+ placeholder: '璇烽�夋嫨棰勮绫诲瀷',
+ },
+ },
+ {
+ fieldName: 'status',
+ label: '澶勭悊鐘舵��',
+ component: 'Select',
+ componentProps: {
+ allowClear: true,
+ options: [
+ { label: '鏈鐞�', value: 10 },
+ { label: '宸插鐞�', value: 20 },
+ ],
+ placeholder: '璇烽�夋嫨澶勭悊鐘舵��',
+ },
+ },
+ ];
+}
+
+/** 鍒楄〃鐨勫瓧娈� */
+export function useGridColumns(): VxeTableGridOptions<MesWmStockWarningApi.StockWarning>['columns'] {
+ return [
+ {
+ field: 'itemCode',
+ title: '鍝佺缂栫爜',
+ minWidth: 120,
+ },
+ {
+ field: 'itemName',
+ title: '鍝佺鍚嶇О',
+ minWidth: 140,
+ },
+ {
+ field: 'batchCode',
+ title: '鎵规鍙�',
+ minWidth: 140,
+ },
+ {
+ field: 'warehouseName',
+ title: '浠撳簱',
+ minWidth: 100,
+ },
+ {
+ field: 'warningType',
+ title: '棰勮绫诲瀷',
+ width: 100,
+ cellRender: {
+ name: 'CellTag',
+ props: {
+ options: [
+ { label: '浣庡簱瀛�', value: 10, color: 'orange' },
+ { label: '涓存湡', value: 20, color: 'gold' },
+ { label: '搴撻緞', value: 30, color: 'purple' },
+ ],
+ },
+ },
+ },
+ {
+ field: 'thresholdValue',
+ title: '闃堝��',
+ width: 100,
+ },
+ {
+ field: 'currentValue',
+ title: '褰撳墠鍊�',
+ width: 100,
+ },
+ {
+ field: 'expireDate',
+ title: '杩囨湡鏃ユ湡',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ {
+ field: 'receiptTime',
+ title: '鍏ュ簱鏃堕棿',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ {
+ field: 'status',
+ title: '澶勭悊鐘舵��',
+ width: 100,
+ cellRender: {
+ name: 'CellTag',
+ props: {
+ options: [
+ { label: '鏈鐞�', value: 10, color: 'red' },
+ { label: '宸插鐞�', value: 20, color: 'green' },
+ ],
+ },
+ },
+ },
+ {
+ field: 'handleTime',
+ title: '澶勭悊鏃堕棿',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ {
+ field: 'handleRemark',
+ title: '澶勭悊澶囨敞',
+ minWidth: 140,
+ },
+ {
+ title: '鎿嶄綔',
+ width: 80,
+ fixed: 'right',
+ slots: { default: 'actions' },
+ },
+ ];
+}
+
+/** 瀹炴椂璁$畻闈㈡澘鐨勫瓧娈� */
+export function useCurrentGridColumns(): VxeTableGridOptions<MesWmStockWarningApi.StockWarning>['columns'] {
+ return [
+ {
+ field: 'itemCode',
+ title: '鍝佺缂栫爜',
+ minWidth: 120,
+ },
+ {
+ field: 'itemName',
+ title: '鍝佺鍚嶇О',
+ minWidth: 140,
+ },
+ {
+ field: 'batchCode',
+ title: '鎵规鍙�',
+ minWidth: 140,
+ },
+ {
+ field: 'warehouseName',
+ title: '浠撳簱',
+ minWidth: 100,
+ },
+ {
+ field: 'warningType',
+ title: '棰勮绫诲瀷',
+ width: 100,
+ cellRender: {
+ name: 'CellTag',
+ props: {
+ options: [
+ { label: '浣庡簱瀛�', value: 10, color: 'orange' },
+ { label: '涓存湡', value: 20, color: 'gold' },
+ { label: '搴撻緞', value: 30, color: 'purple' },
+ ],
+ },
+ },
+ },
+ {
+ field: 'thresholdValue',
+ title: '闃堝��',
+ width: 100,
+ },
+ {
+ field: 'currentValue',
+ title: '褰撳墠鍊�',
+ width: 100,
+ },
+ {
+ field: 'expireDate',
+ title: '杩囨湡鏃ユ湡',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ {
+ field: 'receiptTime',
+ title: '鍏ュ簱鏃堕棿',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ ];
+}
diff --git a/src/views/wls/stock-warning/index.vue b/src/views/wls/stock-warning/index.vue
new file mode 100644
index 0000000..87edba1
--- /dev/null
+++ b/src/views/wls/stock-warning/index.vue
@@ -0,0 +1,133 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmStockWarningApi } from '#/api/mes/wm/stock-warning';
+
+import { Page, useVbenModal } from '@vben/common-ui';
+import { MesWmStockWarningStatusEnum } from '@vben/constants';
+
+import { message } from 'ant-design-vue';
+
+import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+ getStockWarningPage,
+ refreshStockWarnings,
+} from '#/api/mes/wm/stock-warning';
+
+import { useGridColumns, useGridFormSchema } from './data';
+import CurrentModal from './modules/current-modal.vue';
+import HandleModal from './modules/handle-modal.vue';
+
+const [CurrentModalComponent, currentModalApi] = useVbenModal({
+ connectedComponent: CurrentModal,
+ destroyOnClose: true,
+});
+
+const [HandleModalComponent, handleModalApi] = useVbenModal({
+ connectedComponent: HandleModal,
+ destroyOnClose: true,
+});
+
+/** 鍒锋柊琛ㄦ牸 */
+function handleRefresh() {
+ gridApi.query();
+}
+
+/** 瀹炴椂璁$畻褰撳墠棰勮 */
+function handleCurrent() {
+ currentModalApi.open();
+}
+
+/** 閲嶆柊璁$畻骞惰惤搴� */
+async function handleRefreshWarnings() {
+ const hideLoading = message.loading({
+ content: '姝e湪閲嶆柊璁$畻骞惰惤搴撻璀�...',
+ duration: 0,
+ });
+ try {
+ const count = await refreshStockWarnings();
+ message.success(`閲嶆柊璁$畻瀹屾垚锛屽叡 ${count} 鏉¢璀);
+ handleRefresh();
+ } finally {
+ hideLoading();
+ }
+}
+
+/** 澶勭悊棰勮 */
+function handleOpenHandle(row: MesWmStockWarningApi.StockWarning) {
+ handleModalApi.setData({ id: row.id }).open();
+}
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ formOptions: {
+ schema: useGridFormSchema(),
+ },
+ gridOptions: {
+ columns: useGridColumns(),
+ height: 'auto',
+ keepSource: true,
+ proxyConfig: {
+ ajax: {
+ query: async ({ page }, formValues) => {
+ return await getStockWarningPage({
+ pageNo: page.currentPage,
+ pageSize: page.pageSize,
+ ...formValues,
+ });
+ },
+ },
+ },
+ rowConfig: {
+ keyField: 'id',
+ isHover: true,
+ },
+ toolbarConfig: {
+ refresh: true,
+ search: true,
+ },
+ } as VxeTableGridOptions<MesWmStockWarningApi.StockWarning>,
+});
+</script>
+
+<template>
+ <Page auto-content-height>
+ <CurrentModalComponent @success="handleRefresh" />
+ <HandleModalComponent @success="handleRefresh" />
+
+ <Grid table-title="搴撳瓨棰勮鍒楄〃">
+ <template #toolbar-tools>
+ <TableAction
+ :actions="[
+ {
+ label: '瀹炴椂璁$畻',
+ type: 'primary',
+ icon: ACTION_ICON.SEARCH,
+ auth: ['mes:wm-stock-warning:query'],
+ onClick: handleCurrent,
+ },
+ {
+ label: '閲嶆柊璁$畻',
+ type: 'default',
+ icon: ACTION_ICON.REFRESH,
+ auth: ['mes:wm-stock-warning:update'],
+ onClick: handleRefreshWarnings,
+ },
+ ]"
+ />
+ </template>
+ <template #actions="{ row }">
+ <TableAction
+ :actions="[
+ {
+ label: '澶勭悊',
+ type: 'link',
+ icon: ACTION_ICON.EDIT,
+ auth: ['mes:wm-stock-warning:update'],
+ ifShow: row.status === MesWmStockWarningStatusEnum.UNHANDLED,
+ onClick: handleOpenHandle.bind(null, row),
+ },
+ ]"
+ />
+ </template>
+ </Grid>
+ </Page>
+</template>
diff --git a/src/views/wls/stock-warning/modules/current-modal.vue b/src/views/wls/stock-warning/modules/current-modal.vue
new file mode 100644
index 0000000..e806ba1
--- /dev/null
+++ b/src/views/wls/stock-warning/modules/current-modal.vue
@@ -0,0 +1,63 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmStockWarningApi } from '#/api/mes/wm/stock-warning';
+
+import { ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getCurrentStockWarnings } from '#/api/mes/wm/stock-warning';
+import { $t } from '#/locales';
+
+import { useCurrentGridColumns } from '../data';
+
+const list = ref<MesWmStockWarningApi.StockWarning[]>([]);
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ gridOptions: {
+ columns: useCurrentGridColumns(),
+ height: 420,
+ keepSource: true,
+ proxyConfig: {
+ ajax: {
+ query: async () => {
+ return { list: list.value, total: list.value.length };
+ },
+ },
+ },
+ rowConfig: {
+ keyField: 'id',
+ isHover: true,
+ },
+ toolbarConfig: {
+ refresh: true,
+ },
+ } as VxeTableGridOptions<MesWmStockWarningApi.StockWarning>,
+});
+
+const [Modal, modalApi] = useVbenModal({
+ async onConfirm() {
+ await modalApi.close();
+ },
+ async onOpenChange(isOpen: boolean) {
+ if (!isOpen) {
+ list.value = [];
+ return;
+ }
+ modalApi.lock();
+ try {
+ list.value = await getCurrentStockWarnings();
+ gridApi.reload();
+ } finally {
+ modalApi.unlock();
+ }
+ },
+});
+</script>
+
+<template>
+ <Modal :title="$t('ui.actionTitle.view', ['瀹炴椂搴撳瓨棰勮'])" class="w-4/5">
+ <Grid table-title="褰撳墠棰勮锛堝疄鏃惰绠楋紝鏈惤搴擄級" />
+ </Modal>
+</template>
diff --git a/src/views/wls/stock-warning/modules/handle-modal.vue b/src/views/wls/stock-warning/modules/handle-modal.vue
new file mode 100644
index 0000000..dbd9e67
--- /dev/null
+++ b/src/views/wls/stock-warning/modules/handle-modal.vue
@@ -0,0 +1,74 @@
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { handleStockWarning } from '#/api/mes/wm/stock-warning';
+import { $t } from '#/locales';
+
+const emit = defineEmits(['success']);
+const warningId = ref<number>();
+
+const [Form, formApi] = useVbenForm({
+ commonConfig: {
+ componentProps: {
+ class: 'w-full',
+ },
+ formItemClass: 'col-span-1',
+ labelWidth: 100,
+ },
+ layout: 'horizontal',
+ schema: [
+ {
+ component: 'Textarea',
+ fieldName: 'handleRemark',
+ label: '澶勭悊澶囨敞',
+ componentProps: {
+ allowClear: true,
+ maxLength: 200,
+ placeholder: '璇疯緭鍏ュ鐞嗗娉�',
+ rows: 3,
+ },
+ },
+ ],
+ showDefaultActions: false,
+ wrapperClass: 'grid-cols-1',
+});
+
+const [Modal, modalApi] = useVbenModal({
+ async onConfirm() {
+ const { valid } = await formApi.validate();
+ if (!valid || !warningId.value) {
+ return;
+ }
+ modalApi.lock();
+ try {
+ const { handleRemark } = await formApi.getValues();
+ await handleStockWarning({ id: warningId.value, handleRemark });
+ await modalApi.close();
+ emit('success');
+ message.success($t('ui.actionMessage.operationSuccess'));
+ } finally {
+ modalApi.unlock();
+ }
+ },
+ async onOpenChange(isOpen: boolean) {
+ if (!isOpen) {
+ warningId.value = undefined;
+ await formApi.resetForm();
+ return;
+ }
+ const data = modalApi.getData<{ id: number }>();
+ warningId.value = data.id;
+ },
+});
+</script>
+
+<template>
+ <Modal :title="$t('ui.actionTitle.edit', ['搴撳瓨棰勮'])">
+ <Form />
+ </Modal>
+</template>
diff --git a/src/views/wls/stocktaking/task/index.vue b/src/views/wls/stocktaking/task/index.vue
index 4ffefe8..8d52536 100644
--- a/src/views/wls/stocktaking/task/index.vue
+++ b/src/views/wls/stocktaking/task/index.vue
@@ -14,6 +14,7 @@
deleteStockTaking,
exportStockTaking,
getStockTakingPage,
+ processStockTakingDifference,
} from '#/api/mes/wm/stocktaking/task';
import { $t } from '#/locales';
@@ -60,6 +61,23 @@
await cancelStockTaking(row.id!);
message.success('鍙栨秷鎴愬姛');
handleRefresh();
+}
+
+/** 澶勭悊鐩樼偣宸紓锛堢洏鐩堝叆搴�/鐩樹簭鍑哄簱锛岃皟鏁村簱瀛橈級 */
+async function handleProcessDifference(
+ row: MesWmStockTakingTaskApi.StockTakingTask,
+) {
+ const hideLoading = message.loading({
+ content: '姝e湪澶勭悊鐩樼偣宸紓...',
+ duration: 0,
+ });
+ try {
+ await processStockTakingDifference(row.id!);
+ message.success('澶勭悊鐩樼偣宸紓鎴愬姛');
+ handleRefresh();
+ } finally {
+ hideLoading();
+ }
}
/** 鍒犻櫎鐩樼偣浠诲姟 */
@@ -193,6 +211,17 @@
confirm: handleCancel.bind(null, row),
},
},
+ {
+ label: '澶勭悊宸紓',
+ type: 'link',
+ icon: ACTION_ICON.AUDIT,
+ auth: ['mes:wm-stock-taking-task:update'],
+ ifShow: row.status === StatusEnum.FINISHED,
+ popConfirm: {
+ title: '纭澶勭悊鐩樼偣宸紓锛熷皢鎸夌洏鐐圭粨鏋滅洏鐩堝叆搴�/鐩樹簭鍑哄簱璋冩暣搴撳瓨銆�',
+ confirm: handleProcessDifference.bind(null, row),
+ },
+ },
]"
/>
</template>
diff --git a/src/views/wls/stocktaking/task/modules/result-list.vue b/src/views/wls/stocktaking/task/modules/result-list.vue
index 0e9c315..b253a77 100644
--- a/src/views/wls/stocktaking/task/modules/result-list.vue
+++ b/src/views/wls/stocktaking/task/modules/result-list.vue
@@ -2,13 +2,14 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingResultApi } from '#/api/mes/wm/stocktaking/task/result';
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
-import { message } from 'ant-design-vue';
+import { Button, Input, InputNumber, message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import { scanStockTaking } from '#/api/mes/wm/stocktaking/task';
import {
deleteStockTakingResult,
getStockTakingResultPage,
@@ -24,6 +25,37 @@
}>();
const isExecute = computed(() => props.formType === 'execute'); // 鎵ц鐩樼偣鎬佸彲缁存姢鐩樼偣缁撴灉
+
+// PDA 鎵爜鐩樼偣杈撳叆
+const scanCode = ref('');
+const takingQuantity = ref<number | undefined>(undefined);
+const scanning = ref(false);
+
+/** 鎵爜鐩樼偣 */
+async function handleScan() {
+ if (!scanCode.value) {
+ message.warning('璇疯緭鍏ヨ拷婧爜/琚嬬爜/鎵樼洏鐮�/鎵规鍙�');
+ return;
+ }
+ if (takingQuantity.value === undefined || takingQuantity.value === null) {
+ message.warning('璇疯緭鍏ョ洏鐐规暟閲�');
+ return;
+ }
+ scanning.value = true;
+ try {
+ const resultId = await scanStockTaking({
+ taskId: props.taskId,
+ scanCode: scanCode.value,
+ takingQuantity: takingQuantity.value,
+ });
+ message.success(`鎵爜鎴愬姛锛岀洏鐐圭粨鏋滅紪鍙凤細${resultId}`);
+ scanCode.value = '';
+ takingQuantity.value = undefined;
+ handleRefresh();
+ } finally {
+ scanning.value = false;
+ }
+}
const [ResultFormModal, resultFormModalApi] = useVbenModal({
connectedComponent: ResultForm,
@@ -95,6 +127,35 @@
<template>
<div>
<ResultFormModal @success="handleRefresh" />
+ <!-- PDA 鎵爜鐩樼偣 -->
+ <div
+ v-if="isExecute"
+ class="mb-3 flex flex-wrap items-center gap-2 rounded-md border border-dashed border-yellow-300 bg-yellow-50 px-3 py-2"
+ >
+ <span class="font-medium text-yellow-700">PDA 鎵爜鐩樼偣锛�</span>
+ <Input
+ v-model:value="scanCode"
+ allow-clear
+ class="!w-56"
+ placeholder="杩芥函鐮�/琚嬬爜/鎵樼洏鐮�/鎵规鍙�"
+ @press-enter="handleScan"
+ />
+ <InputNumber
+ v-model:value="takingQuantity"
+ :min="0"
+ :precision="2"
+ class="!w-32"
+ placeholder="鐩樼偣鏁伴噺"
+ @press-enter="handleScan"
+ />
+ <Button
+ type="primary"
+ :loading="scanning"
+ @click="handleScan"
+ >
+ 鎵爜鐩樼偣
+ </Button>
+ </div>
<Grid table-title="鐩樼偣缁撴灉">
<template v-if="isExecute" #toolbar-tools>
<TableAction
--
Gitblit v1.9.3