feat(wms): 新增库存汇总统计与预警功能
- 添加MES库存状态、预警类型和处理状态枚举定义
- 实现库存多维汇总功能,支持按品种、批次、仓库维度统计
- 开发库存预警管理模块,包含实时预警计算和处理功能
- 集成PDA扫码盘点功能到盘点任务界面
- 实现盘点差异处理机制,支持盘盈入库和盘亏出库操作
- 构建完整的库存预警处理流程和界面组件
| | |
| | | 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; // æ±æ»æ°é |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢åºåå°è´¦å页 */ |
| | |
| | | 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 }, |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; // å¤ç人ID |
| | | 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); |
| | | } |
| | |
| | | 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 } }, |
| | | ); |
| | | } |
| | |
| | | LOSS: 3, |
| | | } as const; |
| | | |
| | | /** MES åºåç¶ææä¸¾ï¼å¯¹é½å端 MesWmStockStateEnumï¼ */ |
| | | export const MesWmStockStateEnum = { |
| | | TEMP: 10, // æååºï¼å¾
è´¨æ£ï¼ |
| | | QUALIFIED: 20, // åæ ¼åºï¼æ£å¼åºåï¼ |
| | | 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, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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: 'åæ ¼åºï¼æ£å¼åºåï¼', 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, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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', |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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: 'æ£å¨éæ°è®¡ç®å¹¶è½åºé¢è¦...', |
| | | 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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | |
| | | deleteStockTaking, |
| | | exportStockTaking, |
| | | getStockTakingPage, |
| | | processStockTakingDifference, |
| | | } from '#/api/mes/wm/stocktaking/task'; |
| | | import { $t } from '#/locales'; |
| | | |
| | |
| | | await cancelStockTaking(row.id!); |
| | | message.success('åæ¶æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** å¤ççç¹å·®å¼ï¼ççå
¥åº/çäºåºåºï¼è°æ´åºåï¼ */ |
| | | async function handleProcessDifference( |
| | | row: MesWmStockTakingTaskApi.StockTakingTask, |
| | | ) { |
| | | const hideLoading = message.loading({ |
| | | content: 'æ£å¨å¤ççç¹å·®å¼...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await processStockTakingDifference(row.id!); |
| | | message.success('å¤ççç¹å·®å¼æå'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å é¤çç¹ä»»å¡ */ |
| | |
| | | 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> |
| | |
| | | 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, |
| | |
| | | }>(); |
| | | |
| | | 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, |
| | |
| | | <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 |