feat(wm): 新增SN码管理和设备入库功能
- 添加MES设备入库单状态枚举和相关常量定义
- 实现SN码状态枚举和在库单件列表的搜索表单及字段配置
- 添加SN码入库登记和出库核销的表单设计
- 集成SN码单件详情的描述字段展示
- 更新盘点计划支持自动生成和周期配置
- 修改杂项出入库和供应商退货单的审批流程
- 移除原有的提交按钮改为审批流程处理
- 优化盘点任务的开始盘点和结果编辑功能
- 添加条码扫码解析和成图预览组件
- 扩展设备台账和条码相关的API接口
- 新增SN码的入库出库及分页查询功能
- 添加设备物料编号字段到设备管理中
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmTrainingApi { |
| | | /** å¹è®åè®äººå */ |
| | | export interface Participant { |
| | | id?: number; |
| | | trainingId?: number; |
| | | employeeId?: number; |
| | | employeeNo?: string; |
| | | employeeName?: string; |
| | | attended?: boolean; |
| | | score?: number; |
| | | isPass?: boolean; |
| | | certificateId?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** å¹è®æ´»å¨ */ |
| | | export interface Training { |
| | | id?: number; |
| | | trainingNo?: string; |
| | | name?: string; |
| | | trainingType?: number; |
| | | trainingTypeName?: string; |
| | | trainer?: string; |
| | | location?: string; |
| | | hours?: number; |
| | | startDate?: string; |
| | | endDate?: string; |
| | | issueCertificate?: boolean; |
| | | certValidityMonths?: number; |
| | | status?: number; |
| | | statusName?: string; |
| | | participantCount?: number; |
| | | participants?: Participant[]; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** å¹è®æ´»å¨æ¥è¯¢åæ° */ |
| | | export interface TrainingPageParam extends PageParam { |
| | | trainingNo?: string; |
| | | name?: string; |
| | | trainingType?: number; |
| | | status?: number; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢å¹è®æ´»å¨å页 */ |
| | | export function getTrainingPage(params: HrmTrainingApi.TrainingPageParam) { |
| | | return requestClient.get<PageResult<HrmTrainingApi.Training>>( |
| | | '/hrm/training/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢å¹è®æ´»å¨è¯¦æ
ï¼å«åè®äººåï¼ */ |
| | | export function getTraining(id: number) { |
| | | return requestClient.get<HrmTrainingApi.Training>(`/hrm/training/get?id=${id}`); |
| | | } |
| | | |
| | | /** å建å¹è®æ´»å¨ */ |
| | | export function createTraining(data: ReqTraining) { |
| | | return requestClient.post('/hrm/training/create', data); |
| | | } |
| | | |
| | | /** æ´æ°å¹è®æ´»å¨ */ |
| | | export function updateTraining(data: ReqTraining) { |
| | | return requestClient.put('/hrm/training/update', data); |
| | | } |
| | | |
| | | /** å é¤å¹è®æ´»å¨ */ |
| | | export function deleteTraining(ids: number[]) { |
| | | return requestClient.delete('/hrm/training/delete', { params: { ids: ids.join(',') } }); |
| | | } |
| | | |
| | | /** å¼å§å¹è®ï¼è稿 â å¾
å¼å±ï¼ */ |
| | | export function startTraining(id: number) { |
| | | return requestClient.post('/hrm/training/start', null, { params: { id } }); |
| | | } |
| | | |
| | | /** 宿å¹è®ï¼è稿/å¾
å¼å± â å·²å®æï¼ */ |
| | | export function completeTraining(id: number) { |
| | | return requestClient.post('/hrm/training/complete', null, { params: { id } }); |
| | | } |
| | | |
| | | /** åæ¶å¹è®ï¼è稿/å¾
å¼å± â å·²åæ¶ï¼ */ |
| | | export function cancelTraining(id: number) { |
| | | return requestClient.post('/hrm/training/cancel', null, { params: { id } }); |
| | | } |
| | | |
| | | /** æ¹éæ·»å åè®åå·¥ */ |
| | | export function addTrainingParticipants(trainingId: number, employeeIds: number[]) { |
| | | return requestClient.post('/hrm/training-participant/add', { trainingId, employeeIds }); |
| | | } |
| | | |
| | | /** æ´æ°åè®è®°å½ */ |
| | | export function updateTrainingParticipant(data: ReqParticipant) { |
| | | return requestClient.put('/hrm/training-participant/update', data); |
| | | } |
| | | |
| | | /** å é¤åè®è®°å½ */ |
| | | export function deleteTrainingParticipants(ids: number[]) { |
| | | return requestClient.delete('/hrm/training-participant/delete', { |
| | | params: { ids: ids.join(',') }, |
| | | }); |
| | | } |
| | | |
| | | /** è·åææ¬¡å¹è®çåè®äººåå表 */ |
| | | export function getTrainingParticipants(trainingId: number) { |
| | | return requestClient.get<HrmTrainingApi.Participant[]>( |
| | | `/hrm/training-participant/list?trainingId=${trainingId}`, |
| | | ); |
| | | } |
| | | |
| | | /** å¹è®æ´»å¨æ°å¢/æ´æ°è¯·æ± */ |
| | | export interface ReqTraining { |
| | | id?: number; |
| | | name?: string; |
| | | trainingType?: number; |
| | | trainer?: string; |
| | | location?: string; |
| | | hours?: number; |
| | | startDate?: string; |
| | | endDate?: string; |
| | | issueCertificate?: boolean; |
| | | certValidityMonths?: number; |
| | | remark?: string; |
| | | } |
| | | |
| | | /** åè®è®°å½æ´æ°è¯·æ± */ |
| | | export interface ReqParticipant { |
| | | id?: number; |
| | | attended?: boolean; |
| | | score?: number; |
| | | isPass?: boolean; |
| | | remark?: string; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmWorkHourSummaryApi { |
| | | /** æåº¦å·¥æ¶æ±æ» */ |
| | | export interface WorkHourSummary { |
| | | id?: number; |
| | | employeeId?: number; // åå·¥ID |
| | | employeeNo?: string; // åå·¥ç¼å· |
| | | employeeName?: string; // åå·¥å§å |
| | | deptId?: number; // é¨é¨ID |
| | | deptName?: string; // é¨é¨åç§° |
| | | period?: string; // æ ¸ç®å¨æï¼yyyy-MMï¼ |
| | | workDays?: number; // åºå¤å¤©æ° |
| | | workHours?: number; // æ£å¸¸å·¥æ¶ï¼å°æ¶ï¼ |
| | | overtimeHours?: number; // å çå·¥æ¶ï¼å°æ¶ï¼ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** çææåº¦å·¥æ¶æ±æ» */ |
| | | export function generateWorkHourSummary(period: string) { |
| | | return requestClient.post('/hrm/work-hour-summary/generate-monthly', null, { |
| | | params: { period }, |
| | | }); |
| | | } |
| | | |
| | | /** è·åæåº¦å·¥æ¶æ±æ»å页 */ |
| | | export function getWorkHourSummaryPage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmWorkHourSummaryApi.WorkHourSummary>>( |
| | | '/hrm/work-hour-summary/page', |
| | | { params }, |
| | | ); |
| | | } |
| | |
| | | specification?: string; // è§æ ¼åå· |
| | | machineryTypeId?: number; // 设å¤ç±»åç¼å· |
| | | machineryTypeName?: string; // 设å¤ç±»ååç§° |
| | | itemId?: number; // 设å¤ç©æç¼å·ï¼MDM ç©æï¼ |
| | | workshopId?: number; // æå±è½¦é´ç¼å· |
| | | workshopName?: string; // æå±è½¦é´åç§° |
| | | status?: number; // 设å¤ç¶æ |
| | |
| | | return requestClient.download('/mes/wm/barcode/export-excel', { params }); |
| | | } |
| | | |
| | | /** çææ¡ç å
容 */ |
| | | export function generateBarcodeContent(bizType: number, bizCode: string) { |
| | | /** çææ¡ç å
å®¹ï¼æ¯æå¤å ä½ç¬¦ç»åï¼å¯ä¼ å
¥ä¸å¡åç§°ï¼ */ |
| | | export function generateBarcodeContent( |
| | | bizType: number, |
| | | bizCode: string, |
| | | bizName?: string, |
| | | ) { |
| | | return requestClient.get<string>('/mes/wm/barcode/generate-content', { |
| | | params: { bizCode, bizType }, |
| | | params: { bizCode, bizName, bizType }, |
| | | }); |
| | | } |
| | | |
| | | /** çææ¡ç /äºç»´ç å¾çï¼æå¡ç«¯ zxing æå¾ï¼è¿å PNG Base64ï¼ä¸è½åºï¼ */ |
| | | export function renderBarcodeImage( |
| | | content: string, |
| | | format?: number, |
| | | width?: number, |
| | | height?: number, |
| | | ) { |
| | | return requestClient.post<string>('/mes/wm/barcode/render', null, { |
| | | params: { content, format, height, width }, |
| | | }); |
| | | } |
| | | |
| | | /** è§£ææ¡ç å
容ï¼ä¼å
精确å½ä¸ï¼å
¶æ¬¡æå¯ç¨é
置模æ¿åè§£ï¼è¿åä¸å¡åæ®µï¼ */ |
| | | export function parseBarcodeContent(content: string) { |
| | | return requestClient.post<Record<string, any>>('/mes/wm/barcode/parse', null, { |
| | | params: { content }, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | import type { MesWmEquipmentReceiptLineApi } from './line'; |
| | | |
| | | export namespace MesWmEquipmentReceiptApi { |
| | | /** MES 设å¤å
¥åºå */ |
| | | export interface EquipmentReceipt { |
| | | id?: number; // ç¼å· |
| | | code?: string; // åæ®ç¼ç |
| | | name?: string; // åæ®åç§° |
| | | receiptDate?: number; // å
¥åºæ¥æ |
| | | source?: string; // å
¥åºæ¥æº |
| | | status?: number; // åæ®ç¶æ |
| | | processInstanceId?: string; // å®¡æ¹æµç¨å®ä¾ç¼å· |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | lines?: MesWmEquipmentReceiptLineApi.EquipmentReceiptLine[]; // æç»è¡ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢è®¾å¤å
¥åºåå页 */ |
| | | export function getEquipmentReceiptPage(params: PageParam) { |
| | | return requestClient.get< |
| | | PageResult<MesWmEquipmentReceiptApi.EquipmentReceipt> |
| | | >('/mes/wm/equipment-receipt/page', { params }); |
| | | } |
| | | |
| | | /** æ¥è¯¢è®¾å¤å
¥åºå详æ
*/ |
| | | export function getEquipmentReceipt(id: number) { |
| | | return requestClient.get<MesWmEquipmentReceiptApi.EquipmentReceipt>( |
| | | `/mes/wm/equipment-receipt/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢è®¾å¤å
¥åºå */ |
| | | export function createEquipmentReceipt( |
| | | data: MesWmEquipmentReceiptApi.EquipmentReceipt, |
| | | ) { |
| | | return requestClient.post<number>('/mes/wm/equipment-receipt/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹è®¾å¤å
¥åºå */ |
| | | export function updateEquipmentReceipt( |
| | | data: MesWmEquipmentReceiptApi.EquipmentReceipt, |
| | | ) { |
| | | return requestClient.put('/mes/wm/equipment-receipt/update', data); |
| | | } |
| | | |
| | | /** å é¤è®¾å¤å
¥åºå */ |
| | | export function deleteEquipmentReceipt(id: number) { |
| | | return requestClient.delete(`/mes/wm/equipment-receipt/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æ¥è¯¢å¯ç¨ç设å¤å
¥åºåå®¡æ¹æµç¨å®ä¹ */ |
| | | export function getEquipmentReceiptApproveProcessList() { |
| | | return requestClient.get<{ id: string; name: string; key: string }[]>( |
| | | '/mes/wm/equipment-receipt/approval-process-list', |
| | | ); |
| | | } |
| | | |
| | | /** æäº¤è®¾å¤å
¥åºåå®¡æ¹ */ |
| | | export function submitEquipmentReceiptApproval( |
| | | id: number, |
| | | processDefinitionKey: string, |
| | | ) { |
| | | return requestClient.put( |
| | | `/mes/wm/equipment-receipt/submit-approval?id=${id}&processDefinitionKey=${processDefinitionKey}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ§è¡è®¾å¤å
¥åº */ |
| | | export function finishEquipmentReceipt(id: number) { |
| | | return requestClient.put(`/mes/wm/equipment-receipt/finish?id=${id}`); |
| | | } |
| | | |
| | | /** åæ¶è®¾å¤å
¥åºå */ |
| | | export function cancelEquipmentReceipt(id: number) { |
| | | return requestClient.put(`/mes/wm/equipment-receipt/cancel?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesWmEquipmentReceiptLineApi { |
| | | /** MES 设å¤å
¥åºåè¡ */ |
| | | export interface EquipmentReceiptLine { |
| | | id?: number; // ç¼å· |
| | | receiptId?: number; // å
¥åºåç¼å· |
| | | machineryId?: number; // 设å¤å°è´¦ç¼å· |
| | | machineryCode?: string; // 设å¤ç¼ç |
| | | machineryName?: string; // 设å¤åç§° |
| | | itemId?: number; // 设å¤ç©æç¼å· |
| | | itemCode?: string; // ç©æç¼ç |
| | | itemName?: string; // ç©æåç§° |
| | | specification?: string; // è§æ ¼åå· |
| | | unitMeasureName?: string; // 计éåä½åç§° |
| | | quantity?: number; // å
¥åºæ°é |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢è®¾å¤å
¥åºåè¡å表 */ |
| | | export function getEquipmentReceiptLineListByReceiptId(receiptId: number) { |
| | | return requestClient.get< |
| | | MesWmEquipmentReceiptLineApi.EquipmentReceiptLine[] |
| | | >(`/mes/wm/equipment-receipt-line/list-by-receipt-id?receiptId=${receiptId}`); |
| | | } |
| | | |
| | | /** æ¥è¯¢è®¾å¤å
¥åºåè¡è¯¦æ
*/ |
| | | export function getEquipmentReceiptLine(id: number) { |
| | | return requestClient.get<MesWmEquipmentReceiptLineApi.EquipmentReceiptLine>( |
| | | `/mes/wm/equipment-receipt-line/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢è®¾å¤å
¥åºåè¡ */ |
| | | export function createEquipmentReceiptLine( |
| | | data: MesWmEquipmentReceiptLineApi.EquipmentReceiptLine, |
| | | ) { |
| | | return requestClient.post('/mes/wm/equipment-receipt-line/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹è®¾å¤å
¥åºåè¡ */ |
| | | export function updateEquipmentReceiptLine( |
| | | data: MesWmEquipmentReceiptLineApi.EquipmentReceiptLine, |
| | | ) { |
| | | return requestClient.put('/mes/wm/equipment-receipt-line/update', data); |
| | | } |
| | | |
| | | /** å é¤è®¾å¤å
¥åºåè¡ */ |
| | | export function deleteEquipmentReceiptLine(id: number) { |
| | | return requestClient.delete( |
| | | `/mes/wm/equipment-receipt-line/delete?id=${id}`, |
| | | ); |
| | | } |
| | |
| | | sourceDocCode?: string; // æ¥æºåæ®ç¼ç |
| | | issueDate?: number; // åºåºæ¥æ |
| | | status?: number; // åæ®ç¶æ |
| | | processInstanceId?: string; // å®¡æ¹æµç¨å®ä¾ç¼å· |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | |
| | | return requestClient.delete(`/mes/wm/misc-issue/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤æé¡¹åºåºå */ |
| | | export function submitMiscIssue(id: number) { |
| | | return requestClient.put(`/mes/wm/misc-issue/submit?id=${id}`); |
| | | /** æ¥è¯¢å¯ç¨çæé¡¹åºåºåå®¡æ¹æµç¨å®ä¹ */ |
| | | export function getMiscIssueApproveProcessList() { |
| | | return requestClient.get<{ id: string; name: string; key: string }[]>( |
| | | '/mes/wm/misc-issue/approval-process-list', |
| | | ); |
| | | } |
| | | |
| | | /** æäº¤æé¡¹åºåºåå®¡æ¹ */ |
| | | export function submitMiscIssueApproval( |
| | | id: number, |
| | | processDefinitionKey: string, |
| | | ) { |
| | | return requestClient.put( |
| | | `/mes/wm/misc-issue/submit?id=${id}&processDefinitionKey=${processDefinitionKey}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ§è¡åºåº */ |
| | |
| | | sourceDocCode?: string; // æ¥æºåæ®ç¼ç |
| | | receiptDate?: number; // å
¥åºæ¥æ |
| | | status?: number; // åæ®ç¶æ |
| | | processInstanceId?: string; // å®¡æ¹æµç¨å®ä¾ç¼å· |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | |
| | | return requestClient.delete(`/mes/wm/misc-receipt/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤æé¡¹å
¥åºå */ |
| | | export function submitMiscReceipt(id: number) { |
| | | return requestClient.put(`/mes/wm/misc-receipt/submit?id=${id}`); |
| | | /** æ¥è¯¢å¯ç¨çæé¡¹å
¥åºåå®¡æ¹æµç¨å®ä¹ */ |
| | | export function getMiscReceiptApproveProcessList() { |
| | | return requestClient.get<{ id: string; name: string; key: string }[]>( |
| | | '/mes/wm/misc-receipt/approval-process-list', |
| | | ); |
| | | } |
| | | |
| | | /** æäº¤æé¡¹å
¥åºåå®¡æ¹ */ |
| | | export function submitMiscReceiptApproval( |
| | | id: number, |
| | | processDefinitionKey: string, |
| | | ) { |
| | | return requestClient.put( |
| | | `/mes/wm/misc-receipt/submit?id=${id}&processDefinitionKey=${processDefinitionKey}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ§è¡å
¥åº */ |
| | |
| | | transportCode?: string; // è¿åå· |
| | | transportTelephone?: string; // èç³»çµè¯ |
| | | status?: number; // åæ®ç¶æ |
| | | processInstanceId?: string; // å®¡æ¹æµç¨å®ä¾ç¼å· |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | |
| | | return requestClient.delete(`/mes/wm/return-vendor/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤ä¾åºåéè´§å */ |
| | | export function submitReturnVendor(id: number) { |
| | | return requestClient.put(`/mes/wm/return-vendor/submit?id=${id}`); |
| | | /** æ¥è¯¢å¯ç¨çä¾åºåéè´§åå®¡æ¹æµç¨å®ä¹ */ |
| | | export function getReturnVendorApproveProcessList() { |
| | | return requestClient.get<{ id: string; name: string; key: string }[]>( |
| | | '/mes/wm/return-vendor/approval-process-list', |
| | | ); |
| | | } |
| | | |
| | | /** æäº¤ä¾åºåéè´§åå®¡æ¹ */ |
| | | export function submitReturnVendorApproval( |
| | | id: number, |
| | | processDefinitionKey: string, |
| | | ) { |
| | | return requestClient.put( |
| | | `/mes/wm/return-vendor/submit?id=${id}&processDefinitionKey=${processDefinitionKey}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ§è¡æ£è´§ */ |
| | |
| | | unitName?: string; // åä½åç§° |
| | | batchCode?: string; // æ¹æ¬¡å· |
| | | workOrderId?: number; // ç产订åç¼å· |
| | | status?: number; // ç¶æï¼1-å¨åº 2-å·²åºåºï¼ |
| | | warehouseId?: number; // å¨åºä»åº ID |
| | | locationId?: number; // å¨åºåºåº ID |
| | | areaId?: number; // å¨åºåºä½ ID |
| | | createTime?: Date; // çææ¶é´ |
| | | } |
| | | |
| | | /** MES SN ç å件详æ
*/ |
| | | export interface SnDetail { |
| | | id?: number; // ç¼å· |
| | | code?: string; // SN ç |
| | | itemId?: number; // ç©æç¼å· |
| | | itemCode?: string; // ç©æç¼ç |
| | | itemName?: string; // ç©æåç§° |
| | | batchCode?: string; // æ¹æ¬¡å· |
| | | status?: number; // ç¶æï¼1-å¨åº 2-å·²åºåºï¼ |
| | | warehouseId?: number; // å¨åºä»åº ID |
| | | locationId?: number; // å¨åºåºåº ID |
| | | areaId?: number; // å¨åºåºä½ ID |
| | | lastTransactionType?: number; // æè¿äºå¡ç±»åï¼1-å
¥åº 2-åºåºï¼ |
| | | lastTransactionTypeName?: string; // æè¿äºå¡ç±»ååç§° |
| | | lastTransactionQuantity?: number; // æè¿äºå¡å卿°é |
| | | lastTransactionBizType?: number; // æè¿äºå¡ä¸å¡ç±»å |
| | | lastTransactionBizCode?: string; // æè¿äºå¡ä¸å¡åå· |
| | | lastTransactionTime?: string; // æè¿äºå¡åçæ¶é´ |
| | | } |
| | | |
| | | /** SN ç å
¥åºç»è®°åæ° */ |
| | | export interface SnInStock { |
| | | code?: string; // SN ç |
| | | itemId?: number; // ç©æç¼å· |
| | | warehouseId?: number; // ä»åº ID |
| | | locationId?: number; // åºåº ID |
| | | areaId?: number; // åºä½ ID |
| | | batchCode?: string; // æ¹æ¬¡å· |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | |
| | | /** SN ç åºåºæ ¸éåæ° */ |
| | | export interface SnOutStock { |
| | | code?: string; // SN ç |
| | | itemId?: number; // ç©æç¼å· |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | |
| | | /** SN ç å¨åº/ç¶æå页æ¥è¯¢åæ° */ |
| | | export interface SnInStockPageParams extends PageParam { |
| | | code?: string; // SN ç |
| | | itemId?: number; // ç©æç¼å· |
| | | batchCode?: string; // æ¹æ¬¡å· |
| | | warehouseId?: number; // ä»åº ID |
| | | locationId?: number; // åºåº ID |
| | | areaId?: number; // åºä½ ID |
| | | status?: number; // ç¶æï¼1-å¨åº 2-å·²åºåºï¼ |
| | | } |
| | | |
| | | /** MES SN ç çæåæ° */ |
| | |
| | | }); |
| | | } |
| | | |
| | | /** SN ç å
¥åºç»è®° */ |
| | | export function registerSnInStock(data: MesWmSnApi.SnInStock) { |
| | | return requestClient.post<boolean>('/mes/wm/sn/in-stock', data); |
| | | } |
| | | |
| | | /** SN ç åºåºæ ¸é */ |
| | | export function outboundSn(data: MesWmSnApi.SnOutStock) { |
| | | return requestClient.post<boolean>('/mes/wm/sn/out-stock', data); |
| | | } |
| | | |
| | | /** æ¥è¯¢ SN ç å件详æ
*/ |
| | | export function getSnDetail(code: string) { |
| | | return requestClient.get<MesWmSnApi.SnDetail>('/mes/wm/sn/detail', { |
| | | params: { code }, |
| | | }); |
| | | } |
| | | |
| | | /** æ¥è¯¢ SN ç å¨åº/ç¶æå页 */ |
| | | export function getSnInStockPage(params: MesWmSnApi.SnInStockPageParams) { |
| | | return requestClient.get<PageResult<MesWmSnApi.Sn>>( |
| | | '/mes/wm/sn/in-stock-page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** å¯¼åº SN ç åç» Excel */ |
| | | export function exportSnGroupExcel(params: MesWmSnApi.PageParams) { |
| | | return requestClient.download('/mes/wm/sn/group-export-excel', { params }); |
| | |
| | | endTime?: number; // ç»ææ¶é´ |
| | | blindFlag?: boolean; // æ¯å¦ç²ç |
| | | frozen?: boolean; // æ¯å¦å»ç»åºå |
| | | autoGenerate?: boolean; // æ¯å¦èªå¨çæçç¹ä»»å¡ |
| | | generateCron?: string; // èªå¨çæå¨æ(Cron) |
| | | lastGenerateTime?: string; // 䏿¬¡çæä»»å¡æ¶é´ |
| | | status?: number; // ç¶æ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; // å建æ¶é´ |
| | |
| | | startTime?: number; // å¼å§æ¶é´ |
| | | endTime?: number; // ç»ææ¶é´ |
| | | status?: number; // åæ®ç¶æ |
| | | processInstanceId?: string; // å®¡æ¹æµç¨å®ä¾ç¼å· |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; // å建æ¶é´ |
| | | } |
| | |
| | | return requestClient.delete(`/mes/wm/stocktaking-task/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤çç¹ä»»å¡ */ |
| | | /** å¼å§çç¹ï¼æäº¤çç¹ä»»å¡ï¼ */ |
| | | export function submitStockTaking(id: number) { |
| | | return requestClient.put('/mes/wm/stocktaking-task/submit', null, { |
| | | params: { id }, |
| | |
| | | }); |
| | | } |
| | | |
| | | /** æ§è¡çç¹ä»»å¡ */ |
| | | export function finishStockTaking(id: number) { |
| | | return requestClient.put('/mes/wm/stocktaking-task/finish', { id }); |
| | | /** æ¥è¯¢å¯ç¨ççç¹ç»æå®¡æ¹æµç¨å®ä¹ */ |
| | | export function getStockTakingApproveProcessList() { |
| | | return requestClient.get<{ id: string; name: string; key: string }[]>( |
| | | '/mes/wm/stocktaking-task/approval-process-list', |
| | | ); |
| | | } |
| | | |
| | | /** æäº¤çç¹ç»æå®¡æ¹ */ |
| | | export function submitStockTakingApproval( |
| | | id: number, |
| | | processDefinitionKey: string, |
| | | ) { |
| | | return requestClient.put( |
| | | `/mes/wm/stocktaking-task/submit-approval?id=${id}&processDefinitionKey=${processDefinitionKey}`, |
| | | ); |
| | | } |
| | | |
| | | /** 导åºçç¹ä»»å¡ */ |
| | |
| | | BOOK: 'lucide:book', |
| | | AUDIT: 'lucide:file-check', |
| | | CALC: 'lucide:calculator', |
| | | SCAN: 'lucide:scan-line', |
| | | }; |
| | |
| | | PRODUCTRECPT_CODE: 'PRODUCTRECPT_CODE', |
| | | WM_MISC_ISSUE_CODE: 'WM_MISC_ISSUE_CODE', |
| | | WM_MISC_RECEIPT_CODE: 'WM_MISC_RECEIPT_CODE', |
| | | WM_EQUIP_RECEIPT_CODE: 'WM_EQUIP_RECEIPT_CODE', |
| | | WM_OUTSOURCE_ISSUE_CODE: 'WM_OUTSOURCE_ISSUE_CODE', |
| | | WM_OUTSOURCE_RECEIPT_CODE: 'WM_OUTSOURCE_RECEIPT_CODE', |
| | | WM_PACKAGE_CODE: 'WM_PACKAGE_CODE', |
| | |
| | | /** MES æé¡¹åºåºåç¶ææä¸¾ */ |
| | | export const MesWmMiscIssueStatusEnum = { |
| | | PREPARE: MesOrderStatusConstants.DRAFT, |
| | | APPROVING: MesOrderStatusConstants.APPROVING, |
| | | APPROVED: MesOrderStatusConstants.APPROVED, |
| | | FINISHED: MesOrderStatusConstants.FINISHED, |
| | | CANCELED: MesOrderStatusConstants.CANCELLED, |
| | |
| | | /** MES æé¡¹å
¥åºåç¶ææä¸¾ */ |
| | | export const MesWmMiscReceiptStatusEnum = { |
| | | PREPARE: MesOrderStatusConstants.DRAFT, |
| | | APPROVING: MesOrderStatusConstants.APPROVING, |
| | | APPROVED: MesOrderStatusConstants.APPROVED, |
| | | FINISHED: MesOrderStatusConstants.FINISHED, |
| | | CANCELED: MesOrderStatusConstants.CANCELLED, |
| | |
| | | USER = 604, |
| | | CLIENT = 605, |
| | | } |
| | | |
| | | /** MES SN ç ç¶ææä¸¾ */ |
| | | export enum MesWmSnStatusEnum { |
| | | IN_STOCK = 1, // å¨åº |
| | | OUT_STOCK = 2, // å·²åºåº |
| | | } |
| | | |
| | | /** MES 设å¤å
¥åºåç¶ææä¸¾ */ |
| | | export const MesWmEquipmentReceiptStatusEnum = { |
| | | PREPARE: MesOrderStatusConstants.DRAFT, |
| | | APPROVING: MesOrderStatusConstants.APPROVING, |
| | | APPROVED: MesOrderStatusConstants.APPROVED, |
| | | FINISHED: MesOrderStatusConstants.FINISHED, |
| | | CANCELED: MesOrderStatusConstants.CANCELLED, |
| | | } as const; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmTrainingApi } from '#/api/hrm/training'; |
| | | |
| | | /** å¹è®ç±»åé项 */ |
| | | export const trainingTypeOptions = [ |
| | | { label: 'å®å
¨å¹è®', value: 1 }, |
| | | { label: 'æè½å¹è®', value: 2 }, |
| | | { label: '管çå¹è®', value: 3 }, |
| | | { label: 'å
¶ä»', value: 4 }, |
| | | ]; |
| | | |
| | | /** å¹è®ç¶æé项 */ |
| | | export const trainingStatusOptions = [ |
| | | { label: 'è稿', value: 0 }, |
| | | { label: 'å¾
å¼å±', value: 10 }, |
| | | { label: '已宿', value: 20 }, |
| | | { label: '已忶', value: 30 }, |
| | | ]; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'edit' | 'detail'; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'å¹è®åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¹è®åç§°', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'trainingType', |
| | | label: 'å¹è®ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: trainingTypeOptions, |
| | | placeholder: 'è¯·éæ©å¹è®ç±»å', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'trainer', |
| | | label: '讲å¸', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®²å¸', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'location', |
| | | label: 'å¹è®å°ç¹', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¹è®å°ç¹', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'hours', |
| | | label: 'å¹è®å¦æ¶', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¹è®å¦æ¶', |
| | | min: 0, |
| | | precision: 1, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'startDate', |
| | | label: 'å¼å§æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©å¼å§æ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'endDate', |
| | | label: 'ç»ææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©ç»ææ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'issueCertificate', |
| | | label: 'èªå¨åè¯', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'æ¯', value: 1 }, |
| | | { label: 'å¦', value: 0 }, |
| | | ], |
| | | placeholder: 'æ¯å¦èªå¨åè¯', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'certValidityMonths', |
| | | label: 'è¯ä¹¦æææ(æ)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: 'èªå¨åè¯æ¶å¡«å', |
| | | min: 0, |
| | | precision: 0, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 2 }, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'trainingNo', |
| | | label: 'å¹è®ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¹è®ç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'å¹è®åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¹è®åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'trainingType', |
| | | label: 'å¹è®ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: trainingTypeOptions, |
| | | placeholder: 'è¯·éæ©å¹è®ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: trainingStatusOptions, |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmTrainingApi.Training>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'trainingNo', |
| | | title: 'å¹è®ç¼å·', |
| | | width: 140, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: 'å¹è®åç§°', |
| | | minWidth: 180, |
| | | }, |
| | | { |
| | | field: 'trainingTypeName', |
| | | title: 'å¹è®ç±»å', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'trainer', |
| | | title: '讲å¸', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'location', |
| | | title: 'å¹è®å°ç¹', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'hours', |
| | | title: '妿¶', |
| | | width: 80, |
| | | }, |
| | | { |
| | | field: 'startDate', |
| | | title: 'å¼å§æ¥æ', |
| | | width: 105, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'endDate', |
| | | title: 'ç»ææ¥æ', |
| | | width: 105, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'participantCount', |
| | | title: 'åè®äººæ°', |
| | | width: 90, |
| | | }, |
| | | { |
| | | field: 'statusName', |
| | | title: 'ç¶æ', |
| | | width: 90, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 240, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmTrainingApi } from '#/api/hrm/training'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { isEmpty } from '@vben/utils'; |
| | | |
| | | import { message, Modal, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelTraining, |
| | | completeTraining, |
| | | deleteTraining, |
| | | getTrainingPage, |
| | | startTraining, |
| | | } from '#/api/hrm/training'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import Participants from './modules/participants.vue'; |
| | | |
| | | /** HRM å¹è®ç®¡çå表 */ |
| | | defineOptions({ name: 'HrmTraining' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | const [ParticipantsModal, participantsModalApi] = useVbenModal({ |
| | | connectedComponent: Participants, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: HrmTrainingApi.Training) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: HrmTrainingApi.Training) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleParticipants(row: HrmTrainingApi.Training) { |
| | | participantsModalApi.setData({ trainingId: row.id }).open(); |
| | | } |
| | | |
| | | async function handleDelete(ids: number[]) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting'), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteTraining(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å¼å§å¹è®ï¼è稿 â å¾
å¼å± */ |
| | | function handleStart(row: HrmTrainingApi.Training) { |
| | | Modal.confirm({ |
| | | title: 'å¼å§å¹è®', |
| | | content: `æ¯å¦å¼å§å¹è®ã${row.name}ãï¼`, |
| | | onOk: async () => { |
| | | await startTraining(row.id!); |
| | | message.success('å¼å§å¹è®æå'); |
| | | handleRefresh(); |
| | | }, |
| | | }); |
| | | } |
| | | |
| | | /** 宿å¹è®ï¼è稿/å¾
å¼å± â 已宿 */ |
| | | function handleComplete(row: HrmTrainingApi.Training) { |
| | | Modal.confirm({ |
| | | title: '宿å¹è®', |
| | | content: `æ¯å¦å®æå¹è®ã${row.name}ãï¼`, |
| | | onOk: async () => { |
| | | await completeTraining(row.id!); |
| | | message.success('宿å¹è®æå'); |
| | | handleRefresh(); |
| | | }, |
| | | }); |
| | | } |
| | | |
| | | /** åæ¶å¹è®ï¼è稿/å¾
å¼å± â 已忶 */ |
| | | function handleCancel(row: HrmTrainingApi.Training) { |
| | | Modal.confirm({ |
| | | title: 'åæ¶å¹è®', |
| | | content: `æ¯å¦åæ¶å¹è®ã${row.name}ãï¼`, |
| | | onOk: async () => { |
| | | await cancelTraining(row.id!); |
| | | message.success('åæ¶å¹è®æå'); |
| | | handleRefresh(); |
| | | }, |
| | | }); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: HrmTrainingApi.Training[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getTrainingPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmTrainingApi.Training>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ParticipantsModal @success="handleRefresh" /> |
| | | <Grid table-title="å¹è®ç®¡çå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['å¹è®']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:training:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:training:delete'], |
| | | popConfirm: { |
| | | title: 'æ¯å¦å 餿é䏿°æ®ï¼', |
| | | confirm: handleDelete.bind(null, checkedIds), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag v-if="row.status === 0" color="default">è稿</Tag> |
| | | <Tag v-else-if="row.status === 10" color="processing">å¾
å¼å±</Tag> |
| | | <Tag v-else-if="row.status === 20" color="success">已宿</Tag> |
| | | <Tag v-else-if="row.status === 30" color="error">已忶</Tag> |
| | | <Tag v-else color="default">-</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['hrm:training:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'ç¼è¾', |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:training:update'], |
| | | ifShow: () => row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åè®äººå', |
| | | type: 'link', |
| | | icon: 'lucide:users', |
| | | auth: ['hrm:training:update'], |
| | | ifShow: () => row.status === 0 || row.status === 10 || row.status === 20, |
| | | onClick: handleParticipants.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'å¼å§', |
| | | type: 'link', |
| | | icon: 'lucide:play', |
| | | auth: ['hrm:training:update'], |
| | | ifShow: () => row.status === 0, |
| | | onClick: handleStart.bind(null, row), |
| | | }, |
| | | { |
| | | label: '宿', |
| | | type: 'link', |
| | | icon: 'lucide:check', |
| | | auth: ['hrm:training:update'], |
| | | ifShow: () => row.status === 0 || row.status === 10, |
| | | onClick: handleComplete.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åæ¶', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.CLOSE, |
| | | auth: ['hrm:training:update'], |
| | | ifShow: () => row.status === 0 || row.status === 10, |
| | | popConfirm: { |
| | | title: 'æ¯å¦åæ¶å¹è®ï¼', |
| | | confirm: handleCancel.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:training:delete'], |
| | | ifShow: () => row.status === 0, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.name]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmTrainingApi, ReqTraining } from '#/api/hrm/training'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createTraining, getTraining, updateTraining } from '#/api/hrm/training'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<HrmTrainingApi.Training>(); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return $t('ui.actionTitle.create', ['å¹è®']); |
| | | } else if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['å¹è®']); |
| | | } |
| | | return 'å¹è®è¯¦æ
'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'vertical', |
| | | schema: useFormSchema(formType.value), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const values = await formApi.getValues<ReqTraining>(); |
| | | // èªå¨åè¯å段ï¼è¡¨åç¨ 0/1 ä¸æï¼æäº¤æ¶è½¬å¸å° |
| | | const data = { |
| | | ...values, |
| | | issueCertificate: Boolean(values.issueCertificate), |
| | | } as ReqTraining; |
| | | try { |
| | | await (formType.value === 'create' ? createTraining(data) : updateTraining(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value)); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getTraining(data.id); |
| | | await formApi.setValues({ |
| | | ...formData.value, |
| | | // å¸å° â 0/1 ä¸æå¼ |
| | | issueCertificate: formData.value.issueCertificate ? 1 : 0, |
| | | }); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/3" :show-confirm-button="formType !== 'detail'"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | import type { HrmTrainingApi } from '#/api/hrm/training'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Modal as AntModal, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | addTrainingParticipants, |
| | | deleteTrainingParticipants, |
| | | getTrainingParticipants, |
| | | updateTrainingParticipant, |
| | | } from '#/api/hrm/training'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { HrmEmployeeSelectDialog } from '#/views/hrm/employee/components'; |
| | | |
| | | defineOptions({ name: 'HrmTrainingParticipants' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const trainingId = ref<number>(); |
| | | |
| | | /** åè®äººåç¼è¾è¡¨å */ |
| | | const participantFormData = ref<HrmTrainingApi.Participant>(); |
| | | const editVisible = ref(false); |
| | | const editSaving = ref(false); |
| | | const [EditForm, editFormApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'vertical', |
| | | schema: [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'attended', |
| | | label: 'æ¯å¦åè®', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'æ¯', value: 1 }, |
| | | { label: 'å¦', value: 0 }, |
| | | ], |
| | | placeholder: 'æ¯å¦åè®', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'score', |
| | | label: 'æç»©', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æç»©', |
| | | min: 0, |
| | | precision: 1, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'isPass', |
| | | label: 'æ¯å¦åæ ¼', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'æ¯', value: 1 }, |
| | | { label: 'å¦', value: 0 }, |
| | | ], |
| | | placeholder: 'æ¯å¦åæ ¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 2 }, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | /** æå¼ç¼è¾å¼¹çª */ |
| | | function openEditForm(row: HrmTrainingApi.Participant) { |
| | | participantFormData.value = row; |
| | | editFormApi.setValues({ |
| | | id: row.id, |
| | | attended: row.attended ? 1 : 0, |
| | | score: row.score, |
| | | isPass: row.isPass ? 1 : 0, |
| | | remark: row.remark, |
| | | }); |
| | | editVisible.value = true; |
| | | } |
| | | |
| | | /** ä¿ååè®è®°å½ */ |
| | | async function handleEditSave() { |
| | | const { valid } = await editFormApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | editSaving.value = true; |
| | | const values = await editFormApi.getValues<HrmTrainingApi.Participant>(); |
| | | try { |
| | | await updateTrainingParticipant({ |
| | | id: values.id, |
| | | attended: Boolean(values.attended), |
| | | score: values.score, |
| | | isPass: Boolean(values.isPass), |
| | | remark: values.remark, |
| | | } as HrmTrainingApi.Participant); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | editVisible.value = false; |
| | | await loadParticipants(); |
| | | } finally { |
| | | editSaving.value = false; |
| | | } |
| | | } |
| | | |
| | | /** å é¤åè®è®°å½ */ |
| | | async function handleDelete(row: HrmTrainingApi.Participant) { |
| | | await deleteTrainingParticipants([row.id!]); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | await loadParticipants(); |
| | | } |
| | | |
| | | /** å工鿩弹çªï¼å¤éï¼ */ |
| | | const employeeDialogRef = ref<InstanceType<typeof HrmEmployeeSelectDialog>>(); |
| | | function openEmployeeSelect() { |
| | | employeeDialogRef.value?.open([], { multiple: true }); |
| | | } |
| | | |
| | | async function handleEmployeesSelected(rows: HrmEmployeeApi.Employee[]) { |
| | | const employeeIds = rows |
| | | .map((item) => item.id) |
| | | .filter((id): id is number => id !== undefined && id !== null); |
| | | if (employeeIds.length === 0) { |
| | | return; |
| | | } |
| | | try { |
| | | await addTrainingParticipants(trainingId.value!, employeeIds); |
| | | message.success('æ·»å åè®äººåæå'); |
| | | await loadParticipants(); |
| | | emit('success'); |
| | | } catch (e) { |
| | | // å端å
åºæç¤ºå·²å¼¹åº |
| | | } |
| | | } |
| | | |
| | | async function loadParticipants() { |
| | | if (!trainingId.value) { |
| | | return; |
| | | } |
| | | const list = await getTrainingParticipants(trainingId.value); |
| | | gridApi.grid.loadData(list); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: [ |
| | | { type: 'seq', title: 'åºå·', width: 60 }, |
| | | { field: 'employeeNo', title: 'åå·¥ç¼å·', width: 130 }, |
| | | { field: 'employeeName', title: 'åå·¥å§å', minWidth: 120 }, |
| | | { |
| | | field: 'attended', |
| | | title: 'æ¯å¦åè®', |
| | | width: 100, |
| | | slots: { default: 'attended' }, |
| | | }, |
| | | { |
| | | field: 'score', |
| | | title: 'æç»©', |
| | | width: 90, |
| | | }, |
| | | { |
| | | field: 'isPass', |
| | | title: 'æ¯å¦åæ ¼', |
| | | width: 100, |
| | | slots: { default: 'isPass' }, |
| | | }, |
| | | { field: 'remark', title: '夿³¨', minWidth: 120 }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 140, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ], |
| | | height: 'auto', |
| | | keepSource: true, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: false, |
| | | search: false, |
| | | }, |
| | | } as VxeTableGridOptions<HrmTrainingApi.Participant>, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | onOpenChange(isOpen) { |
| | | if (!isOpen) { |
| | | trainingId.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ trainingId?: number }>(); |
| | | trainingId.value = data?.trainingId; |
| | | if (trainingId.value) { |
| | | loadParticipants(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="åè®äººå管ç" class="w-[900px]" :show-confirm-button="false"> |
| | | <HrmEmployeeSelectDialog |
| | | ref="employeeDialogRef" |
| | | @selected="handleEmployeesSelected" |
| | | /> |
| | | <Grid table-title="åè®äººåå表"> |
| | | <template #toolbar-tools> |
| | | <a-button type="primary" @click="openEmployeeSelect"> |
| | | <i-ant-design-plus-outlined class="mr-1" />æ·»å åè®äººå |
| | | </a-button> |
| | | </template> |
| | | <template #attended="{ row }"> |
| | | <Tag v-if="row.attended" color="success">æ¯</Tag> |
| | | <Tag v-else color="default">å¦</Tag> |
| | | </template> |
| | | <template #isPass="{ row }"> |
| | | <Tag v-if="row.isPass" color="success">åæ ¼</Tag> |
| | | <Tag v-else-if="row.attended" color="error">ä¸åæ ¼</Tag> |
| | | <Tag v-else color="default">-</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <a-button type="link" size="small" @click="openEditForm(row)">ç¼è¾</a-button> |
| | | <a-popconfirm |
| | | :title="$t('ui.actionMessage.deleteConfirm', [row.employeeName ?? row.id])" |
| | | @confirm="handleDelete(row)" |
| | | > |
| | | <a-button type="link" danger size="small">å é¤</a-button> |
| | | </a-popconfirm> |
| | | </template> |
| | | </Grid> |
| | | |
| | | <AntModal |
| | | v-model:open="editVisible" |
| | | title="ç¼è¾åè®è®°å½" |
| | | width="480px" |
| | | :confirm-loading="editSaving" |
| | | @ok="handleEditSave" |
| | | > |
| | | <EditForm class="pt-4" /> |
| | | </AntModal> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmWorkHourSummaryApi } from '#/api/hrm/work-hour-summary'; |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'period', |
| | | label: 'æ ¸ç®å¨æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | picker: 'month', |
| | | allowClear: true, |
| | | format: 'YYYY-MM', |
| | | valueFormat: 'YYYY-MM', |
| | | style: { width: '100%' }, |
| | | placeholder: 'è¯·éæ©æ ¸ç®å¨æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'employeeId', |
| | | label: 'åå·¥', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getEmployeeSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©åå·¥', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: 'é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmWorkHourSummaryApi.WorkHourSummary>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'period', |
| | | title: 'æ ¸ç®å¨æ', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'employeeNo', |
| | | title: 'åå·¥ç¼å·', |
| | | width: 130, |
| | | }, |
| | | { |
| | | field: 'employeeName', |
| | | title: 'åå·¥å§å', |
| | | minWidth: 110, |
| | | }, |
| | | { |
| | | field: 'deptName', |
| | | title: 'é¨é¨', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'workDays', |
| | | title: 'åºå¤å¤©æ°', |
| | | width: 90, |
| | | }, |
| | | { |
| | | field: 'workHours', |
| | | title: 'æ£å¸¸å·¥æ¶(h)', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'overtimeHours', |
| | | title: 'å çå·¥æ¶(h)', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | title: '夿³¨', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 100, |
| | | formatter: 'formatDate', |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmWorkHourSummaryApi } from '#/api/hrm/work-hour-summary'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, DatePicker, Form, FormItem, Modal } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | generateWorkHourSummary, |
| | | getWorkHourSummaryPage, |
| | | } from '#/api/hrm/work-hour-summary'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | |
| | | /** HRM æåº¦å·¥æ¶æ±æ» */ |
| | | defineOptions({ name: 'HrmWorkHourSummary' }); |
| | | |
| | | // çææåº¦æ±æ»å¼¹çª |
| | | const generateVisible = ref(false); |
| | | const generatePeriod = ref<string>(); |
| | | const generateLoading = ref(false); |
| | | |
| | | /** æå¼çæå¼¹çªï¼å¸¦åºæç´¢æ¡ä»¶ä¸çæ ¸ç®å¨æ */ |
| | | function openGenerateModal() { |
| | | void gridApi.formApi.getValues().then((values) => { |
| | | generatePeriod.value = values.period; |
| | | }); |
| | | generateVisible.value = true; |
| | | } |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ§è¡çæ */ |
| | | async function handleGenerate() { |
| | | if (!generatePeriod.value) { |
| | | message.warning('è¯·éæ©æ ¸ç®å¨æ'); |
| | | return; |
| | | } |
| | | generateLoading.value = true; |
| | | try { |
| | | await generateWorkHourSummary(generatePeriod.value); |
| | | message.success('æåº¦å·¥æ¶æ±æ»çææå'); |
| | | generateVisible.value = false; |
| | | handleRefresh(); |
| | | } finally { |
| | | generateLoading.value = false; |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getWorkHourSummaryPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmWorkHourSummaryApi.WorkHourSummary>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <!-- çææåº¦å·¥æ¶æ±æ»å¼¹çª --> |
| | | <Modal |
| | | v-model:open="generateVisible" |
| | | title="çææåº¦å·¥æ¶æ±æ»" |
| | | width="420px" |
| | | :confirm-loading="generateLoading" |
| | | @ok="handleGenerate" |
| | | > |
| | | <Form layout="vertical" class="pt-4"> |
| | | <FormItem label="æ ¸ç®å¨æ" required> |
| | | <DatePicker |
| | | v-model:value="generatePeriod" |
| | | picker="month" |
| | | format="YYYY-MM" |
| | | valueFormat="YYYY-MM" |
| | | placeholder="è¯·éæ©æ ¸ç®å¨æ" |
| | | style="width: 100%" |
| | | /> |
| | | </FormItem> |
| | | </Form> |
| | | </Modal> |
| | | |
| | | <Grid table-title="æåº¦å·¥æ¶æ±æ»å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'çææåº¦æ±æ»', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:work-hour:generate'], |
| | | onClick: openGenerateModal, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| | |
| | | export { default as Barcode } from './barcode.vue'; |
| | | export { default as BarcodeDetail } from './detail.vue'; |
| | | export { default as PrinterLabel } from './printer-label.vue'; |
| | | export { default as ScanModal } from './scan-modal.vue'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | |
| | | import { |
| | | Button, |
| | | Descriptions, |
| | | Divider, |
| | | Empty, |
| | | Image, |
| | | Input, |
| | | message, |
| | | Modal, |
| | | Tag, |
| | | } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | parseBarcodeContent, |
| | | renderBarcodeImage, |
| | | } from '#/api/mes/wm/barcode'; |
| | | import { DictTag } from '#/components/dict-tag'; |
| | | |
| | | defineOptions({ name: 'WlsBarcodeScanModal' }); |
| | | |
| | | const open = ref(false); |
| | | const content = ref(''); |
| | | const parseResult = ref<Record<string, any>>(); |
| | | const imageBase64 = ref(''); |
| | | const parsing = ref(false); |
| | | const rendering = ref(false); |
| | | |
| | | function openModal() { |
| | | open.value = true; |
| | | content.value = ''; |
| | | parseResult.value = undefined; |
| | | imageBase64.value = ''; |
| | | } |
| | | |
| | | /** è§£ææ¡ç å
容 */ |
| | | async function handleParse() { |
| | | if (!content.value?.trim()) { |
| | | message.warning('请å
æ«ç æªæ«å
¥ææå¨è¾å
¥æ¡ç å
容'); |
| | | return; |
| | | } |
| | | parsing.value = true; |
| | | try { |
| | | parseResult.value = await parseBarcodeContent(content.value.trim()); |
| | | } finally { |
| | | parsing.value = false; |
| | | } |
| | | } |
| | | |
| | | /** æå¡ç«¯æå¾é¢è§ï¼PNG Base64ï¼ä¸è½åºï¼ */ |
| | | async function handleRender() { |
| | | if (!content.value?.trim()) { |
| | | message.warning('请å
æ«ç æªæ«å
¥ææå¨è¾å
¥æ¡ç å
容'); |
| | | return; |
| | | } |
| | | rendering.value = true; |
| | | try { |
| | | const base64 = await renderBarcodeImage( |
| | | content.value.trim(), |
| | | parseResult.value?.format, |
| | | ); |
| | | imageBase64.value = base64 ? `data:image/png;base64,${base64}` : ''; |
| | | if (!base64) { |
| | | message.warning('æå¾å¤±è´¥ï¼è¯·æ£æ¥æ¡ç å
容æ¯å¦ç¬¦åæéæ ¼å¼'); |
| | | } |
| | | } finally { |
| | | rendering.value = false; |
| | | } |
| | | } |
| | | |
| | | /** å¹é
æ¹å¼ææ¡ */ |
| | | const matchedLabel = computed(() => { |
| | | if (parseResult.value?.matched) { |
| | | return '精确å½ä¸'; |
| | | } |
| | | if (parseResult.value?.fields) { |
| | | return '模æ¿åè§£'; |
| | | } |
| | | return 'æªå¹é
'; |
| | | }); |
| | | const matchedColor = computed(() => { |
| | | if (parseResult.value?.matched) { |
| | | return 'green'; |
| | | } |
| | | if (parseResult.value?.fields) { |
| | | return 'blue'; |
| | | } |
| | | return 'orange'; |
| | | }); |
| | | |
| | | defineExpose({ open: openModal }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal v-model:open="open" title="æ«ç è§£æ / æ¡ç é¢è§" width="720px"> |
| | | <div class="space-y-4"> |
| | | <div class="flex gap-2"> |
| | | <Input |
| | | v-model:value="content" |
| | | placeholder="æ«ç æªæ«å
¥ææå¨è¾å
¥æ¡ç å
容ï¼å车解æ" |
| | | @press-enter="handleParse" |
| | | /> |
| | | <Button type="primary" :loading="parsing" @click="handleParse"> |
| | | è§£æ |
| | | </Button> |
| | | <Button :loading="rendering" @click="handleRender">æå¾é¢è§</Button> |
| | | </div> |
| | | |
| | | <div v-if="parseResult"> |
| | | <Divider orientation="left">è§£æç»æ</Divider> |
| | | <Descriptions |
| | | v-if="parseResult.bizType != null || parseResult.bizCode != null" |
| | | bordered |
| | | :column="2" |
| | | size="small" |
| | | > |
| | | <Descriptions.Item label="å¹é
æ¹å¼"> |
| | | <Tag :color="matchedColor">{{ matchedLabel }}</Tag> |
| | | </Descriptions.Item> |
| | | <Descriptions.Item v-if="parseResult.format != null" label="æ¡ç æ ¼å¼"> |
| | | <DictTag |
| | | :type="DICT_TYPE.MES_WM_BARCODE_FORMAT" |
| | | :value="parseResult.format" |
| | | /> |
| | | </Descriptions.Item> |
| | | <Descriptions.Item v-if="parseResult.bizType != null" label="ä¸å¡ç±»å"> |
| | | <DictTag |
| | | :type="DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE" |
| | | :value="parseResult.bizType" |
| | | /> |
| | | </Descriptions.Item> |
| | | <Descriptions.Item v-if="parseResult.barcodeId != null" label="æ¡ç ç¼å·"> |
| | | {{ parseResult.barcodeId }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="ä¸å¡ç¼ç "> |
| | | {{ parseResult.bizCode || '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="ä¸å¡åç§°"> |
| | | {{ parseResult.bizName || '-' }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item v-if="parseResult.bizId != null" label="ä¸å¡ç¼å·"> |
| | | {{ parseResult.bizId }} |
| | | </Descriptions.Item> |
| | | </Descriptions> |
| | | |
| | | <div v-if="parseResult.fields" class="mt-2"> |
| | | <div class="mb-1 text-sm font-medium">模æ¿å段</div> |
| | | <div |
| | | v-for="(value, key) in parseResult.fields" |
| | | :key="key" |
| | | class="mb-1 flex items-center gap-2" |
| | | > |
| | | <Tag>{{ key }}</Tag> |
| | | <span>{{ value }}</span> |
| | | </div> |
| | | </div> |
| | | <Empty |
| | | v-else-if="parseResult.bizType == null && parseResult.bizCode == null" |
| | | description="æªå¹é
å°æ¡ç è®°å½ææ¡ç é
置模æ¿" |
| | | /> |
| | | </div> |
| | | |
| | | <div v-if="imageBase64"> |
| | | <Divider orientation="left">æå¾é¢è§</Divider> |
| | | <div class="flex justify-center rounded bg-gray-100 p-4"> |
| | | <Image :src="imageBase64" :width="220" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <Button @click="open = false">å
³é</Button> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| | |
| | | } from '#/api/mes/wm/barcode'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { Barcode, BarcodeDetail } from './components'; |
| | | import { Barcode, BarcodeDetail, ScanModal } from './components'; |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | |
| | | }); |
| | | |
| | | const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); |
| | | const scanModalRef = ref<InstanceType<typeof ScanModal>>(); |
| | | |
| | | /** æ«ç è§£æ / æå¾é¢è§ */ |
| | | function handleScan() { |
| | | scanModalRef.value?.open(); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <BarcodeDetail ref="barcodeDetailRef" /> |
| | | <ScanModal ref="scanModalRef" /> |
| | | |
| | | <Grid table-title="æ¡ç å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ«ç è§£æ', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.SCAN, |
| | | auth: ['mes:wm-barcode:query'], |
| | | onClick: handleScan, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.create', ['æ¡ç ']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Select } from 'ant-design-vue'; |
| | | |
| | | interface ProcessDefinition { |
| | | id: string; |
| | | name: string; |
| | | key: string; |
| | | } |
| | | |
| | | type ProcessSubmitFn = (id: number, processDefinitionKey: string) => Promise<unknown>; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const bizId = ref<number>(); |
| | | const processList = ref<ProcessDefinition[]>([]); |
| | | const selectedProcessKey = ref<string>(''); |
| | | |
| | | const processOptions = computed(() => { |
| | | return processList.value.map((p) => ({ |
| | | label: p.name, |
| | | value: p.key, |
| | | })); |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!selectedProcessKey.value) { |
| | | message.warning('è¯·éæ©å®¡æ¹æµç¨'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await modalApi.getData<{ submitFn: ProcessSubmitFn }>().submitFn( |
| | | bizId.value!, |
| | | selectedProcessKey.value, |
| | | ); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æäº¤å®¡æ¹æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | bizId.value = undefined; |
| | | selectedProcessKey.value = ''; |
| | | processList.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | | id: number; |
| | | processList: ProcessDefinition[]; |
| | | submitFn: ProcessSubmitFn; |
| | | }>(); |
| | | bizId.value = data.id; |
| | | processList.value = data.processList || []; |
| | | // é»è®¤éæ©ç¬¬ä¸ä¸ªæµç¨ |
| | | const first = processList.value[0]; |
| | | if (first) { |
| | | selectedProcessKey.value = first.key; |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="éæ©å®¡æ¹æµç¨" class="w-1/3"> |
| | | <div class="mb-4"> |
| | | <span class="mr-2">å®¡æ¹æµç¨ï¼</span> |
| | | <Select |
| | | v-model:value="selectedProcessKey" |
| | | :options="processOptions" |
| | | style="width: 200px" |
| | | placeholder="è¯·éæ©å®¡æ¹æµç¨" |
| | | /> |
| | | </div> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmEquipmentReceiptApi } from '#/api/mes/wm/equipmentreceipt'; |
| | | import type { MesWmEquipmentReceiptLineApi } from '#/api/mes/wm/equipmentreceipt/line'; |
| | | |
| | | import { h, markRaw } from 'vue'; |
| | | |
| | | import { MesAutoCodeRuleCode, MesWmEquipmentReceiptStatusEnum } from '@vben/constants'; |
| | | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { DvMachinerySelect } from '#/views/mes/dv/machinery/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'finish' | 'update'; |
| | | |
| | | /** 表å头鍿¯å¦åªè¯»ï¼è¯¦æ
ãæ§è¡å
¥åºæï¼ */ |
| | | function isHeaderReadonly(formType: FormType): boolean { |
| | | return formType === 'detail' || formType === 'finish'; |
| | | } |
| | | |
| | | /** 设å¤å
¥åºåç¶æå±ç¤ºæ å° */ |
| | | export const EQUIPMENT_RECEIPT_STATUS_MAP: Record< |
| | | number, |
| | | { text: string; color: string } |
| | | > = { |
| | | [MesWmEquipmentReceiptStatusEnum.PREPARE]: { |
| | | text: 'è稿', |
| | | color: 'default', |
| | | }, |
| | | [MesWmEquipmentReceiptStatusEnum.APPROVING]: { |
| | | text: '审æ¹ä¸', |
| | | color: 'processing', |
| | | }, |
| | | [MesWmEquipmentReceiptStatusEnum.APPROVED]: { |
| | | text: '已审æ¹', |
| | | color: 'success', |
| | | }, |
| | | [MesWmEquipmentReceiptStatusEnum.FINISHED]: { |
| | | text: '已宿', |
| | | color: 'cyan', |
| | | }, |
| | | [MesWmEquipmentReceiptStatusEnum.CANCELED]: { |
| | | text: '已忶', |
| | | color: 'warning', |
| | | }, |
| | | }; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema( |
| | | formType: FormType, |
| | | formApi?: VbenFormApi, |
| | | ): VbenFormSchema[] { |
| | | const headerReadonly = isHeaderReadonly(formType); |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'åæ®ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åæ®ç¼ç ', |
| | | }, |
| | | rules: 'required', |
| | | suffix: headerReadonly |
| | | ? undefined |
| | | : () => |
| | | h( |
| | | Button, |
| | | { |
| | | type: 'default', |
| | | onClick: async () => { |
| | | const code = await generateAutoCode( |
| | | MesAutoCodeRuleCode.WM_EQUIP_RECEIPT_CODE, |
| | | ); |
| | | await formApi?.setFieldValue('code', code); |
| | | }, |
| | | }, |
| | | { default: () => 'çæ' }, |
| | | ), |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åæ®åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åæ®åç§°', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'receiptDate', |
| | | label: 'å
¥åºæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | format: 'YYYY-MM-DD', |
| | | placeholder: 'è¯·éæ©å
¥åºæ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'source', |
| | | label: 'å
¥åºæ¥æº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å
¥åºæ¥æº', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 3, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'åæ®ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åæ®ç¼ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åæ®åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åæ®åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'source', |
| | | label: 'å
¥åºæ¥æº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥å
¥åºæ¥æº', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'receiptDate', |
| | | label: 'å
¥åºæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'åæ®ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: Object.entries(EQUIPMENT_RECEIPT_STATUS_MAP).map( |
| | | ([value, item]) => ({ |
| | | label: item.text, |
| | | value: Number(value), |
| | | }), |
| | | ), |
| | | placeholder: 'è¯·éæ©åæ®ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesWmEquipmentReceiptApi.EquipmentReceipt>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'code', |
| | | title: 'åæ®ç¼ç ', |
| | | minWidth: 160, |
| | | slots: { default: 'code' }, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: 'åæ®åç§°', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'source', |
| | | title: 'å
¥åºæ¥æº', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'receiptDate', |
| | | title: 'å
¥åºæ¥æ', |
| | | width: 180, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'åæ®ç¶æ', |
| | | minWidth: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 240, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** 设å¤å
¥åºåè¡å表çåæ®µ */ |
| | | export function useLineGridColumns( |
| | | editable = true, |
| | | ): VxeTableGridOptions<MesWmEquipmentReceiptLineApi.EquipmentReceiptLine>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'machineryCode', |
| | | title: '设å¤ç¼ç ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'machineryName', |
| | | title: '设å¤åç§°', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'itemCode', |
| | | title: 'ç©æç¼ç ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'itemName', |
| | | title: 'ç©æåç§°', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'specification', |
| | | title: 'è§æ ¼åå·', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'unitMeasureName', |
| | | title: 'åä½', |
| | | width: 80, |
| | | }, |
| | | { |
| | | field: 'quantity', |
| | | title: 'å
¥åºæ°é', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | title: '夿³¨', |
| | | minWidth: 120, |
| | | }, |
| | | ...(editable |
| | | ? [ |
| | | { |
| | | title: 'æä½', |
| | | width: 160, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | } as const, |
| | | ] |
| | | : []), |
| | | ]; |
| | | } |
| | | |
| | | /** 设å¤å
¥åºåè¡æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useLineFormSchema(formApi?: VbenFormApi): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'itemId', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'machineryId', |
| | | label: '设å¤', |
| | | component: markRaw(DvMachinerySelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©è®¾å¤', |
| | | onChange: async (machinery?: { itemId?: number }) => { |
| | | await formApi?.setFieldValue('itemId', machinery?.itemId); |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: 'å
¥åºæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0.01, |
| | | defaultValue: 1, |
| | | placeholder: '请è¾å
¥å
¥åºæ°é', |
| | | precision: 2, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 3, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmEquipmentReceiptApi } from '#/api/mes/wm/equipmentreceipt'; |
| | | |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmEquipmentReceiptStatusEnum } from '@vben/constants'; |
| | | |
| | | import { Button, message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelEquipmentReceipt, |
| | | deleteEquipmentReceipt, |
| | | getEquipmentReceiptApproveProcessList, |
| | | getEquipmentReceiptPage, |
| | | submitEquipmentReceiptApproval, |
| | | } from '#/api/mes/wm/equipmentreceipt'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue'; |
| | | import { EQUIPMENT_RECEIPT_STATUS_MAP, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建设å¤å
¥åºå */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç设å¤å
¥åºå */ |
| | | function handleDetail(row: MesWmEquipmentReceiptApi.EquipmentReceipt) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾è®¾å¤å
¥åºå */ |
| | | function handleEdit(row: MesWmEquipmentReceiptApi.EquipmentReceipt) { |
| | | formModalApi.setData({ formType: 'update', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ§è¡å
¥åº */ |
| | | function handleFinish(row: MesWmEquipmentReceiptApi.EquipmentReceipt) { |
| | | formModalApi.setData({ formType: 'finish', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmitApproval(row: MesWmEquipmentReceiptApi.EquipmentReceipt) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getEquipmentReceiptApproveProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi |
| | | .setData({ |
| | | id: row.id, |
| | | processList, |
| | | submitFn: submitEquipmentReceiptApproval, |
| | | }) |
| | | .open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleProcessDetail(row: MesWmEquipmentReceiptApi.EquipmentReceipt) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('è¯¥åæ®å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | /** å é¤è®¾å¤å
¥åºå */ |
| | | async function handleDelete(row: MesWmEquipmentReceiptApi.EquipmentReceipt) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.code]), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteEquipmentReceipt(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.code])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** åæ¶è®¾å¤å
¥åºå */ |
| | | async function handleCancel(row: MesWmEquipmentReceiptApi.EquipmentReceipt) { |
| | | await cancelEquipmentReceipt(row.id!); |
| | | message.success('åæ¶æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getEquipmentReceiptPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesWmEquipmentReceiptApi.EquipmentReceipt>, |
| | | }); |
| | | |
| | | const StatusEnum = MesWmEquipmentReceiptStatusEnum; |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | |
| | | <Grid table-title="设å¤å
¥åºåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['设å¤å
¥åºå']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mes:wm:equipment-receipt:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #code="{ row }"> |
| | | <Button type="link" @click="handleDetail(row)"> |
| | | {{ row.code }} |
| | | </Button> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag :color="EQUIPMENT_RECEIPT_STATUS_MAP[row.status ?? 0]?.color"> |
| | | {{ EQUIPMENT_RECEIPT_STATUS_MAP[row.status ?? 0]?.text ?? row.status }} |
| | | </Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:wm:equipment-receipt:update'], |
| | | ifShow: row.status === StatusEnum.PREPARE, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:wm:equipment-receipt:delete'], |
| | | ifShow: row.status === StatusEnum.PREPARE, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.code]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm:equipment-receipt:submit'], |
| | | ifShow: row.status === StatusEnum.PREPARE, |
| | | onClick: handleSubmitApproval.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm:equipment-receipt:query'], |
| | | ifShow: row.status === StatusEnum.APPROVING, |
| | | onClick: handleProcessDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ§è¡å
¥åº', |
| | | type: 'link', |
| | | auth: ['mes:wm:equipment-receipt:finish'], |
| | | ifShow: row.status === StatusEnum.APPROVED, |
| | | onClick: handleFinish.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åæ¶', |
| | | type: 'link', |
| | | danger: true, |
| | | auth: ['mes:wm:equipment-receipt:cancel'], |
| | | ifShow: row.status === StatusEnum.APPROVED, |
| | | popConfirm: { |
| | | title: 'ç¡®è®¤åæ¶è¯¥è®¾å¤å
¥åºååï¼', |
| | | confirm: handleCancel.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { MesWmEquipmentReceiptApi } from '#/api/mes/wm/equipmentreceipt'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmEquipmentReceiptStatusEnum } from '@vben/constants'; |
| | | |
| | | import { Button, Divider, message, Popconfirm } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createEquipmentReceipt, |
| | | finishEquipmentReceipt, |
| | | getEquipmentReceipt, |
| | | updateEquipmentReceipt, |
| | | } from '#/api/mes/wm/equipmentreceipt'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | import LineList from './line-list.vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | const formData = ref<MesWmEquipmentReceiptApi.EquipmentReceipt>(); |
| | | const originalSnapshot = ref(''); // 表ååå§æ°æ®å¿«ç
§ï¼ç¨äºæäº¤æ¶è·³è¿æªåæ´çä¿åè¯·æ± |
| | | const isEditable = computed(() => |
| | | // æ¯å¦ä¸ºç¼è¾æ¨¡å¼ï¼å¯ä¿åï¼ |
| | | ['create', 'update'].includes(formType.value), |
| | | ); |
| | | const isFinish = computed(() => formType.value === 'finish'); // æ¯å¦ä¸ºæ§è¡å
¥åºæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return $t('ui.actionTitle.view', ['设å¤å
¥åºå']); |
| | | } |
| | | if (formType.value === 'finish') { |
| | | return 'æ§è¡å
¥åº'; |
| | | } |
| | | return formType.value === 'update' |
| | | ? $t('ui.actionTitle.edit', ['设å¤å
¥åºå']) |
| | | : $t('ui.actionTitle.create', ['设å¤å
¥åºå']); |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 110, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** æ§è¡å
¥åº */ |
| | | async function handleFinish() { |
| | | if (!formData.value?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await finishEquipmentReceipt(formData.value.id); |
| | | message.success('å
¥åºæå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!isEditable.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = |
| | | (await formApi.getValues()) as MesWmEquipmentReceiptApi.EquipmentReceipt; |
| | | try { |
| | | if (formData.value?.id) { |
| | | await updateEquipmentReceipt({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | } else { |
| | | const id = await createEquipmentReceipt(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmEquipmentReceiptStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | } |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | originalSnapshot.value = ''; |
| | | return; |
| | | } |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: useFormSchema(formType.value, formApi) }); |
| | | formApi.setDisabled( |
| | | formType.value === 'detail' || formType.value === 'finish', |
| | | ); |
| | | modalApi.setState({ showConfirmButton: isEditable.value }); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getEquipmentReceipt(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | <!-- éæ°å»ºæ¨¡å¼å±ç¤ºè®¾å¤æç» --> |
| | | <template v-if="formData?.id"> |
| | | <Divider>è®¾å¤æç»</Divider> |
| | | <div class="mx-4"> |
| | | <LineList :form-type="formType" :receipt-id="formData.id" /> |
| | | </div> |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm |
| | | v-if="isFinish" |
| | | title="确认æ§è¡å
¥åºï¼æ§è¡åå°è®¾å¤è®¡å
¥è®¾å¤ä»åºåå°è´¦ã" |
| | | @confirm="handleFinish" |
| | | > |
| | | <Button type="primary">æ§è¡å
¥åº</Button> |
| | | </Popconfirm> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmEquipmentReceiptLineApi } from '#/api/mes/wm/equipmentreceipt/line'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createEquipmentReceiptLine, |
| | | getEquipmentReceiptLine, |
| | | updateEquipmentReceiptLine, |
| | | } from '#/api/mes/wm/equipmentreceipt/line'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useLineFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesWmEquipmentReceiptLineApi.EquipmentReceiptLine>(); |
| | | const receiptId = ref<number>(); // æå±å
¥åºåç¼å· |
| | | |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id |
| | | ? $t('ui.actionTitle.edit', ['设å¤']) |
| | | : $t('ui.actionTitle.create', ['设å¤']); |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 90, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useLineFormSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = |
| | | (await formApi.getValues()) as MesWmEquipmentReceiptLineApi.EquipmentReceiptLine; |
| | | data.receiptId = receiptId.value; |
| | | try { |
| | | await (formData.value?.id |
| | | ? updateEquipmentReceiptLine({ ...data, id: formData.value.id }) |
| | | : createEquipmentReceiptLine(data)); |
| | | // å
³éå¹¶æç¤º |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | formApi.setState({ schema: useLineFormSchema(formApi) }); |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ |
| | | id?: number; |
| | | receiptId: number; |
| | | }>(); |
| | | receiptId.value = data.receiptId; |
| | | if (!data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getEquipmentReceiptLine(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmEquipmentReceiptLineApi } from '#/api/mes/wm/equipmentreceipt/line'; |
| | | |
| | | import { computed } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteEquipmentReceiptLine, |
| | | getEquipmentReceiptLineListByReceiptId, |
| | | } from '#/api/mes/wm/equipmentreceipt/line'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useLineGridColumns } from '../data'; |
| | | import LineForm from './line-form.vue'; |
| | | |
| | | const props = defineProps<{ |
| | | formType: FormType; |
| | | receiptId: number; |
| | | }>(); |
| | | |
| | | const isEditable = computed(() => |
| | | // æ¯å¦å¯ç¼è¾æç»è¡ |
| | | ['create', 'update'].includes(props.formType), |
| | | ); |
| | | |
| | | const [LineFormModal, lineFormModalApi] = useVbenModal({ |
| | | connectedComponent: LineForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ·»å è®¾å¤ */ |
| | | function handleCreate() { |
| | | lineFormModalApi.setData({ receiptId: props.receiptId }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾è®¾å¤ */ |
| | | function handleEdit(row: MesWmEquipmentReceiptLineApi.EquipmentReceiptLine) { |
| | | lineFormModalApi.setData({ id: row.id, receiptId: props.receiptId }).open(); |
| | | } |
| | | |
| | | /** å é¤è®¾å¤ */ |
| | | async function handleDelete(row: MesWmEquipmentReceiptLineApi.EquipmentReceiptLine) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.machineryName]), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteEquipmentReceiptLine(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.machineryName])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: useLineGridColumns(isEditable.value), |
| | | height: 360, |
| | | keepSource: true, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => { |
| | | if (!props.receiptId) { |
| | | return { list: [], total: 0 }; |
| | | } |
| | | const list = await getEquipmentReceiptLineListByReceiptId( |
| | | props.receiptId, |
| | | ); |
| | | return { list, total: list.length }; |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesWmEquipmentReceiptLineApi.EquipmentReceiptLine>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <div> |
| | | <LineFormModal @success="handleRefresh" /> |
| | | <Grid table-title="è®¾å¤æç»"> |
| | | <template v-if="isEditable" #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ·»å 设å¤', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | ifShow: isEditable, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | ifShow: isEditable, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.machineryName]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | </template> |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmMiscIssueApi } from '#/api/mes/wm/miscissue'; |
| | | |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmMiscIssueStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | |
| | | cancelMiscIssue, |
| | | deleteMiscIssue, |
| | | exportMiscIssue, |
| | | getMiscIssueApproveProcessList, |
| | | getMiscIssuePage, |
| | | submitMiscIssueApproval, |
| | | } from '#/api/mes/wm/miscissue'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue'; |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | /** æ§è¡åºåº */ |
| | | function handleFinish(row: MesWmMiscIssueApi.MiscIssue) { |
| | | formModalApi.setData({ formType: 'finish', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmitApproval(row: MesWmMiscIssueApi.MiscIssue) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getMiscIssueApproveProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi |
| | | .setData({ |
| | | id: row.id, |
| | | processList, |
| | | submitFn: submitMiscIssueApproval, |
| | | }) |
| | | .open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleProcessDetail(row: MesWmMiscIssueApi.MiscIssue) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('è¯¥åæ®å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | /** å 餿项åºåºå */ |
| | |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | |
| | | <Grid table-title="æé¡¹åºåºåå表"> |
| | | <template #toolbar-tools> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-misc-issue:update'], |
| | | ifShow: row.status === MesWmMiscIssueStatusEnum.PREPARE, |
| | | onClick: handleSubmitApproval.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-misc-issue:query'], |
| | | ifShow: row.status === MesWmMiscIssueStatusEnum.APPROVING, |
| | | onClick: handleProcessDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ§è¡åºåº', |
| | | type: 'link', |
| | | auth: ['mes:wm-misc-issue:finish'], |
| | |
| | | createMiscIssue, |
| | | finishMiscIssue, |
| | | getMiscIssue, |
| | | submitMiscIssue, |
| | | updateMiscIssue, |
| | | } from '#/api/mes/wm/miscissue'; |
| | | import { $t } from '#/locales'; |
| | |
| | | ['create', 'update'].includes(formType.value), |
| | | ); |
| | | const isFinish = computed(() => formType.value === 'finish'); // æ¯å¦ä¸ºæ§è¡åºåºæ¨¡å¼ |
| | | const canSubmit = computed( |
| | | () => |
| | | // æ¯å¦å¯æäº¤ |
| | | formType.value === 'update' && |
| | | formData.value?.status === MesWmMiscIssueStatusEnum.PREPARE, |
| | | ); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return $t('ui.actionTitle.view', ['æé¡¹åºåºå']); |
| | |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** æäº¤åºåºåï¼è¡¨åæä¿®æ¹æ¶å
ä¿åï¼åè°ç¨æäº¤æ¥å£ */ |
| | | async function handleSubmit() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid || !formData.value?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const current = JSON.stringify(await formApi.getValues()); |
| | | if (current !== originalSnapshot.value) { |
| | | const data = (await formApi.getValues()) as MesWmMiscIssueApi.MiscIssue; |
| | | await updateMiscIssue({ ...formData.value, ...data }); |
| | | originalSnapshot.value = current; |
| | | } |
| | | await submitMiscIssue(formData.value.id); |
| | | message.success('æäº¤æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** æ§è¡åºåº */ |
| | | async function handleFinish() { |
| | |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm |
| | | v-if="canSubmit" |
| | | title="确认æäº¤è¯¥æé¡¹åºåºåï¼ãæäº¤åå°ä¸è½ä¿®æ¹ã" |
| | | @confirm="handleSubmit" |
| | | > |
| | | <Button type="primary">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="isFinish" |
| | | title="确认æ§è¡åºåºï¼æ§è¡åå°æ´æ°åºåå°è´¦ã" |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmMiscReceiptApi } from '#/api/mes/wm/miscreceipt'; |
| | | |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmMiscReceiptStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | |
| | | cancelMiscReceipt, |
| | | deleteMiscReceipt, |
| | | exportMiscReceipt, |
| | | getMiscReceiptApproveProcessList, |
| | | getMiscReceiptPage, |
| | | submitMiscReceiptApproval, |
| | | } from '#/api/mes/wm/miscreceipt'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue'; |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | /** æ§è¡å
¥åº */ |
| | | function handleFinish(row: MesWmMiscReceiptApi.MiscReceipt) { |
| | | formModalApi.setData({ formType: 'finish', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmitApproval(row: MesWmMiscReceiptApi.MiscReceipt) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getMiscReceiptApproveProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi |
| | | .setData({ |
| | | id: row.id, |
| | | processList, |
| | | submitFn: submitMiscReceiptApproval, |
| | | }) |
| | | .open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleProcessDetail(row: MesWmMiscReceiptApi.MiscReceipt) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('è¯¥åæ®å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | /** å 餿项å
¥åºå */ |
| | |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | |
| | | <Grid table-title="æé¡¹å
¥åºåå表"> |
| | | <template #toolbar-tools> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm:misc-receipt:submit'], |
| | | ifShow: row.status === MesWmMiscReceiptStatusEnum.PREPARE, |
| | | onClick: handleSubmitApproval.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm:misc-receipt:query'], |
| | | ifShow: row.status === MesWmMiscReceiptStatusEnum.APPROVING, |
| | | onClick: handleProcessDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ§è¡å
¥åº', |
| | | type: 'link', |
| | | auth: ['mes:wm-misc-receipt:finish'], |
| | |
| | | createMiscReceipt, |
| | | finishMiscReceipt, |
| | | getMiscReceipt, |
| | | submitMiscReceipt, |
| | | updateMiscReceipt, |
| | | } from '#/api/mes/wm/miscreceipt'; |
| | | import { $t } from '#/locales'; |
| | |
| | | ['create', 'update'].includes(formType.value), |
| | | ); |
| | | const isFinish = computed(() => formType.value === 'finish'); // æ¯å¦ä¸ºæ§è¡å
¥åºæ¨¡å¼ |
| | | const canSubmit = computed( |
| | | () => |
| | | // æ¯å¦å¯æäº¤ |
| | | formType.value === 'update' && |
| | | formData.value?.status === MesWmMiscReceiptStatusEnum.PREPARE, |
| | | ); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return $t('ui.actionTitle.view', ['æé¡¹å
¥åºå']); |
| | |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** æäº¤å
¥åºåï¼è¡¨åæä¿®æ¹æ¶å
ä¿åï¼åè°ç¨æäº¤æ¥å£ */ |
| | | async function handleSubmit() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid || !formData.value?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const current = JSON.stringify(await formApi.getValues()); |
| | | if (current !== originalSnapshot.value) { |
| | | const data = |
| | | (await formApi.getValues()) as MesWmMiscReceiptApi.MiscReceipt; |
| | | await updateMiscReceipt({ ...formData.value, ...data }); |
| | | originalSnapshot.value = current; |
| | | } |
| | | await submitMiscReceipt(formData.value.id); |
| | | message.success('æäº¤æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** æ§è¡å
¥åº */ |
| | | async function handleFinish() { |
| | |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm |
| | | v-if="canSubmit" |
| | | title="确认æäº¤è¯¥æé¡¹å
¥åºåï¼ãæäº¤åå°ä¸è½ä¿®æ¹ã" |
| | | @confirm="handleSubmit" |
| | | > |
| | | <Button type="primary">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="isFinish" |
| | | title="确认æ§è¡å
¥åºï¼æ§è¡åå°æ´æ°åºåå°è´¦ã" |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmReturnVendorApi } from '#/api/mes/wm/returnvendor'; |
| | | |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmReturnVendorStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | |
| | | cancelReturnVendor, |
| | | deleteReturnVendor, |
| | | exportReturnVendor, |
| | | getReturnVendorApproveProcessList, |
| | | getReturnVendorPage, |
| | | submitReturnVendorApproval, |
| | | } from '#/api/mes/wm/returnvendor'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue'; |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | /** æ§è¡æ£è´§ */ |
| | | function handleStock(row: MesWmReturnVendorApi.ReturnVendor) { |
| | | formModalApi.setData({ formType: 'stock', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmitApproval(row: MesWmReturnVendorApi.ReturnVendor) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getReturnVendorApproveProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi |
| | | .setData({ |
| | | id: row.id, |
| | | processList, |
| | | submitFn: submitReturnVendorApproval, |
| | | }) |
| | | .open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleProcessDetail(row: MesWmReturnVendorApi.ReturnVendor) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('è¯¥åæ®å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | /** 宿éè´§ */ |
| | |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | |
| | | <Grid table-title="ä¾åºåéè´§åå表"> |
| | | <template #toolbar-tools> |
| | |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-return-vendor:update'], |
| | | ifShow: row.status === MesWmReturnVendorStatusEnum.PREPARE, |
| | | onClick: handleSubmitApproval.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-return-vendor:query'], |
| | | ifShow: row.status === MesWmReturnVendorStatusEnum.APPROVING, |
| | | onClick: handleProcessDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ§è¡æ£è´§', |
| | | type: 'link', |
| | | auth: ['mes:wm-return-vendor:update'], |
| | |
| | | finishReturnVendor, |
| | | getReturnVendor, |
| | | stockReturnVendor, |
| | | submitReturnVendor, |
| | | updateReturnVendor, |
| | | } from '#/api/mes/wm/returnvendor'; |
| | | import { $t } from '#/locales'; |
| | |
| | | ); |
| | | const isStock = computed(() => formType.value === 'stock'); // æ¯å¦ä¸ºæ£è´§æ¨¡å¼ |
| | | const isFinish = computed(() => formType.value === 'finish'); // æ¯å¦ä¸ºå®æéè´§æ¨¡å¼ |
| | | const canSubmit = computed( |
| | | () => |
| | | // æ¯å¦å¯æäº¤ |
| | | formType.value === 'update' && |
| | | formData.value?.status === MesWmReturnVendorStatusEnum.PREPARE, |
| | | ); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return $t('ui.actionTitle.view', ['ä¾åºåéè´§å']); |
| | |
| | | return formatDate(value, 'YYYY-MM-DD HH:mm:ss'); |
| | | } |
| | | return undefined; |
| | | } |
| | | |
| | | /** æäº¤éè´§åï¼è¡¨åæä¿®æ¹æ¶å
ä¿åï¼åè°ç¨æäº¤æ¥å£ */ |
| | | async function handleSubmit() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid || !formData.value?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const current = JSON.stringify(await formApi.getValues()); |
| | | if (current !== originalSnapshot.value) { |
| | | const data = |
| | | (await formApi.getValues()) as MesWmReturnVendorApi.ReturnVendor; |
| | | await updateReturnVendor({ ...formData.value, ...data }); |
| | | originalSnapshot.value = current; |
| | | } |
| | | await submitReturnVendor(formData.value.id); |
| | | message.success('æäº¤æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** æ§è¡æ£è´§ï¼æ£è´§æ°éä¸éè´§æ°éä¸ä¸è´æ¶äºæ¬¡ç¡®è®¤ */ |
| | |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm |
| | | v-if="canSubmit" |
| | | title="确认æäº¤è¯¥éè´§åï¼ãæäº¤åå°ä¸è½ä¿®æ¹ã" |
| | | @confirm="handleSubmit" |
| | | > |
| | | <Button type="primary">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Button v-if="isStock" type="primary" @click="handleStock"> |
| | | æ§è¡æ£è´§ |
| | | </Button> |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmSnApi } from '#/api/mes/wm/sn'; |
| | | import type { DescriptionItemSchema } from '#/components/description'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { MesWmSnStatusEnum } from '@vben/constants'; |
| | | import { formatDateTime } from '@vben/utils'; |
| | | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | | WmWarehouseLocationSelect, |
| | | WmWarehouseSelect, |
| | | } from '#/views/wls/warehouse/components'; |
| | | |
| | | /** SN ç¶æéé¡¹ï¼æ ç¬ç«åå
¸ï¼ä½¿ç¨å¸¸éæ å°ï¼ */ |
| | | export const SN_STATUS_OPTIONS = [ |
| | | { label: 'å¨åº', value: MesWmSnStatusEnum.IN_STOCK }, |
| | | { label: 'å·²åºåº', value: MesWmSnStatusEnum.OUT_STOCK }, |
| | | ]; |
| | | |
| | | /** çæ SN ç ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å¨åºåä»¶å表çæç´¢è¡¨å */ |
| | | export function useInStockGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'SN ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥ SN ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç©æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'batchCode', |
| | | label: 'æ¹æ¬¡å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¹æ¬¡å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: SN_STATUS_OPTIONS, |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å¨åºåä»¶å表çåæ®µ */ |
| | | export function useInStockGridColumns(): VxeTableGridOptions<MesWmSnApi.Sn>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'code', |
| | | title: 'SN ç ', |
| | | minWidth: 180, |
| | | }, |
| | | { |
| | | field: 'itemCode', |
| | | title: 'ç©æç¼ç ', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'itemName', |
| | | title: 'ç©æåç§°', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'specification', |
| | | title: 'è§æ ¼åå·', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'unitName', |
| | | title: 'åä½', |
| | | width: 80, |
| | | }, |
| | | { |
| | | field: 'batchCode', |
| | | title: 'æ¹æ¬¡å·', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 90, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'warehouseId', |
| | | title: 'ä»åº', |
| | | minWidth: 140, |
| | | slots: { default: 'locations' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'çææ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 140, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** SN ç å
¥åºç»è®°ç表å */ |
| | | export function useInStockFormSchema( |
| | | formApi?: VbenFormApi, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'SN ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'æ«ç æªæ«å
¥æè¾å
¥ SN ç ', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç©æï¼é¡»å¯ç¨åºåå·ç®¡çï¼', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'warehouseId', |
| | | label: 'ä»åº', |
| | | component: markRaw(WmWarehouseSelect), |
| | | componentProps: { |
| | | onChange: () => |
| | | formApi?.setValues({ areaId: undefined, locationId: undefined }), |
| | | placeholder: 'è¯·éæ©ä»åº', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'locationId', |
| | | label: 'åºåº', |
| | | component: markRaw(WmWarehouseLocationSelect), |
| | | dependencies: { |
| | | triggerFields: ['warehouseId'], |
| | | show: (values) => !!values.warehouseId, |
| | | componentProps: (values) => ({ |
| | | onChange: () => formApi?.setFieldValue('areaId', undefined), |
| | | placeholder: 'è¯·éæ©åºåº', |
| | | warehouseId: values.warehouseId, |
| | | }), |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'areaId', |
| | | label: 'åºä½', |
| | | component: markRaw(WmWarehouseAreaSelect), |
| | | dependencies: { |
| | | triggerFields: ['locationId'], |
| | | show: (values) => !!values.locationId, |
| | | componentProps: (values) => ({ |
| | | locationId: values.locationId, |
| | | placeholder: 'è¯·éæ©åºä½', |
| | | }), |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'batchCode', |
| | | label: 'æ¹æ¬¡å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¹æ¬¡å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** SN ç åºåºæ ¸éç表å */ |
| | | export function useOutStockFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'SN ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'æ«ç æªæ«å
¥æè¾å
¥ SN ç ', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç©æ', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** SN ç å件详æ
çæè¿°å段 */ |
| | | export function useSnDetailSchema(): DescriptionItemSchema[] { |
| | | return [ |
| | | { |
| | | field: 'code', |
| | | label: 'SN ç ', |
| | | render: (value) => value || '-', |
| | | }, |
| | | { |
| | | field: 'itemCode', |
| | | label: 'ç©æç¼ç ', |
| | | render: (value) => value || '-', |
| | | }, |
| | | { |
| | | field: 'itemName', |
| | | label: 'ç©æåç§°', |
| | | render: (value) => value || '-', |
| | | }, |
| | | { |
| | | field: 'batchCode', |
| | | label: 'æ¹æ¬¡å·', |
| | | render: (value) => value || '-', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | label: 'ç¶æ', |
| | | render: (value) => |
| | | value === MesWmSnStatusEnum.OUT_STOCK |
| | | ? 'å·²åºåº' |
| | | : value === MesWmSnStatusEnum.IN_STOCK |
| | | ? 'å¨åº' |
| | | : '-', |
| | | }, |
| | | { |
| | | field: 'warehouseId', |
| | | label: 'å¨åºä½ç½®', |
| | | slot: 'location', |
| | | }, |
| | | { |
| | | field: 'lastTransactionTypeName', |
| | | label: 'æè¿æµæ°´', |
| | | slot: 'lastTransaction', |
| | | }, |
| | | { |
| | | field: 'lastTransactionTime', |
| | | label: 'æè¿æµæ°´æ¶é´', |
| | | render: (value) => formatDateTime(value) || '-', |
| | | }, |
| | | ]; |
| | | } |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmSnApi } from '#/api/mes/wm/sn'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import { message, Tabs, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | |
| | | exportSnDetailExcel, |
| | | exportSnGroupExcel, |
| | | getSnGroupPage, |
| | | getSnInStockPage, |
| | | } from '#/api/mes/wm/sn'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import { |
| | | useGridColumns, |
| | | useGridFormSchema, |
| | | useInStockGridColumns, |
| | | useInStockGridFormSchema, |
| | | } from './data'; |
| | | import { |
| | | formatWmLocation, |
| | | getWmLocationMaps, |
| | | type WmLocationMaps, |
| | | } from './location-maps'; |
| | | import Detail from './modules/detail.vue'; |
| | | import Form from './modules/form.vue'; |
| | | import InStockModal from './modules/in-stock-modal.vue'; |
| | | import OutStockModal from './modules/out-stock-modal.vue'; |
| | | import SnDetailModal from './modules/sn-detail-modal.vue'; |
| | | |
| | | const activeKey = ref('group'); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | const [InStockModalRef, inStockModalApi] = useVbenModal({ |
| | | connectedComponent: InStockModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [OutStockModalRef, outStockModalApi] = useVbenModal({ |
| | | connectedComponent: OutStockModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const snDetailModalRef = ref<InstanceType<typeof SnDetailModal>>(); |
| | | |
| | | /** å·æ°å½åé¡µç¾ */ |
| | | function handleRefresh() { |
| | | if (activeKey.value === 'group') { |
| | | gridApi.query(); |
| | | } else { |
| | | inStockGridApi.query(); |
| | | } |
| | | } |
| | | |
| | | /** çæ SN ç */ |
| | |
| | | } |
| | | } |
| | | |
| | | /** å
¥åºç»è®° */ |
| | | function handleInStock() { |
| | | inStockModalApi.open(); |
| | | } |
| | | |
| | | /** åºåºæ ¸é */ |
| | | function handleOutStock() { |
| | | outStockModalApi.open(); |
| | | } |
| | | |
| | | /** æ¥çå件详æ
*/ |
| | | function handleSnDetail(row: MesWmSnApi.Sn) { |
| | | snDetailModalRef.value?.open(row.code); |
| | | } |
| | | |
| | | /** å¨åºåä»¶å表ä½ç½®åç§°æ å°ï¼æå è½½ï¼é¦æ¬¡è®¿é®æ¶åå§åï¼ */ |
| | | const locationMapsRef = ref<Awaited<ReturnType<typeof getWmLocationMaps>>>(); |
| | | const EMPTY_MAPS: WmLocationMaps = { |
| | | warehouseNameMap: {}, |
| | | locationNameMap: {}, |
| | | areaNameMap: {}, |
| | | }; |
| | | async function ensureLocationMaps() { |
| | | if (!locationMapsRef.value) { |
| | | locationMapsRef.value = await getWmLocationMaps(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | |
| | | }, |
| | | } as VxeTableGridOptions<MesWmSnApi.SnGroup>, |
| | | }); |
| | | |
| | | const [InStockGrid, inStockGridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useInStockGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useInStockGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getSnInStockPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesWmSnApi.Sn>, |
| | | }); |
| | | |
| | | // 馿¬¡æè½½æ¶é¢çä½ç½®åç§°æ å°ï¼ä¾å表å±ç¤º |
| | | void ensureLocationMaps(); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <DetailModal /> |
| | | <InStockModalRef @success="handleRefresh" /> |
| | | <OutStockModalRef @success="handleRefresh" /> |
| | | <SnDetailModal ref="snDetailModalRef" /> |
| | | |
| | | <Tabs v-model:active-key="activeKey" class="px-4 pt-1" type="card"> |
| | | <Tabs.TabPane key="group" tab="SN ç åç»"> |
| | | <Grid table-title="SN ç å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="in-stock" tab="å¨åºåä»¶"> |
| | | <InStockGrid table-title="SN åä»¶ç¶æå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'å
¥åºç»è®°', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mes:wm-sn:update'], |
| | | onClick: handleInStock, |
| | | }, |
| | | { |
| | | label: 'åºåºæ ¸é', |
| | | type: 'primary', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:wm-sn:update'], |
| | | onClick: handleOutStock, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag :color="row.status === 1 ? 'green' : 'orange'"> |
| | | {{ row.status === 1 ? 'å¨åº' : 'å·²åºåº' }} |
| | | </Tag> |
| | | </template> |
| | | <template #locations="{ row }"> |
| | | <span v-if="row.status === 1"> |
| | | {{ |
| | | formatWmLocation( |
| | | locationMapsRef ?? EMPTY_MAPS, |
| | | row.warehouseId, |
| | | row.locationId, |
| | | row.areaId, |
| | | ) |
| | | }} |
| | | </span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: '详æ
', |
| | | type: 'link', |
| | | auth: ['mes:wm-sn:query'], |
| | | onClick: handleSnDetail.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </InStockGrid> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { getWarehouseAreaSimpleList } from '#/api/mes/wm/warehouse/area'; |
| | | import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse'; |
| | | import { getWarehouseLocationSimpleList } from '#/api/mes/wm/warehouse/location'; |
| | | |
| | | export interface WmLocationMaps { |
| | | warehouseNameMap: Record<number, string>; |
| | | locationNameMap: Record<number, string>; |
| | | areaNameMap: Record<number, string>; |
| | | } |
| | | |
| | | let cached: WmLocationMaps | null = null; |
| | | |
| | | /** å è½½ä»åº/åºåº/åºä½ ID -> åç§°æ å°ï¼æ¨¡å级ç¼åï¼ä»
馿¬¡å è½½ï¼ */ |
| | | export async function getWmLocationMaps(): Promise<WmLocationMaps> { |
| | | if (cached) { |
| | | return cached; |
| | | } |
| | | const [warehouses, locations, areas] = await Promise.all([ |
| | | getWarehouseSimpleList(), |
| | | getWarehouseLocationSimpleList(), |
| | | getWarehouseAreaSimpleList(), |
| | | ]); |
| | | cached = { |
| | | warehouseNameMap: Object.fromEntries( |
| | | warehouses.map((w) => [w.id!, w.name || w.code || '']), |
| | | ), |
| | | locationNameMap: Object.fromEntries( |
| | | locations.map((l) => [l.id!, l.name || l.code || '']), |
| | | ), |
| | | areaNameMap: Object.fromEntries( |
| | | areas.map((a) => [a.id!, a.name || a.code || '']), |
| | | ), |
| | | }; |
| | | return cached; |
| | | } |
| | | |
| | | /** æ ¹æ® ID 渲æ ä»åº/åºåº/åºä½ ææ¬ï¼ID 缺失æ¶è¿åç©ºä¸²ï¼ */ |
| | | export function formatWmLocation( |
| | | maps: WmLocationMaps, |
| | | warehouseId?: number, |
| | | locationId?: number, |
| | | areaId?: number, |
| | | ): string { |
| | | return [ |
| | | warehouseId ? maps.warehouseNameMap[warehouseId] : '', |
| | | locationId ? maps.locationNameMap[locationId] : '', |
| | | areaId ? maps.areaNameMap[areaId] : '', |
| | | ] |
| | | .filter(Boolean) |
| | | .join(' / '); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmSnApi } from '#/api/mes/wm/sn'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { registerSnInStock } from '#/api/mes/wm/sn'; |
| | | |
| | | import { useInStockFormSchema } from '../data'; |
| | | |
| | | defineOptions({ name: 'WlsSnInStockModal' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as MesWmSnApi.SnInStock; |
| | | try { |
| | | await registerSnInStock(data); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('å
¥åºç»è®°æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | formApi.setState({ schema: useInStockFormSchema(formApi) }); |
| | | await formApi.resetForm(); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="SN ç å
¥åºç»è®°" class="w-1/2"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmSnApi } from '#/api/mes/wm/sn'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { outboundSn } from '#/api/mes/wm/sn'; |
| | | |
| | | import { useOutStockFormSchema } from '../data'; |
| | | |
| | | defineOptions({ name: 'WlsSnOutStockModal' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as MesWmSnApi.SnOutStock; |
| | | try { |
| | | await outboundSn(data); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('åºåºæ ¸éæå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | formApi.setState({ schema: useOutStockFormSchema() }); |
| | | await formApi.resetForm(); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="SN ç åºåºæ ¸é" class="w-1/2"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmSnApi } from '#/api/mes/wm/sn'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { Button, Empty, Input, message, Modal } from 'ant-design-vue'; |
| | | |
| | | import { getSnDetail } from '#/api/mes/wm/sn'; |
| | | import { useDescription } from '#/components/description'; |
| | | |
| | | import { useSnDetailSchema } from '../data'; |
| | | import { formatWmLocation, getWmLocationMaps } from '../location-maps'; |
| | | |
| | | defineOptions({ name: 'WlsSnDetailModal' }); |
| | | |
| | | const open = ref(false); |
| | | const code = ref(''); |
| | | const detail = ref<MesWmSnApi.SnDetail>(); |
| | | const loading = ref(false); |
| | | const locationMaps = ref<Awaited<ReturnType<typeof getWmLocationMaps>>>(); |
| | | |
| | | const locationText = computed(() => |
| | | detail.value |
| | | ? formatWmLocation( |
| | | locationMaps.value ?? { |
| | | warehouseNameMap: {}, |
| | | locationNameMap: {}, |
| | | areaNameMap: {}, |
| | | }, |
| | | detail.value.warehouseId, |
| | | detail.value.locationId, |
| | | detail.value.areaId, |
| | | ) |
| | | : '-', |
| | | ); |
| | | |
| | | const lastTransactionText = computed(() => { |
| | | const d = detail.value; |
| | | if (!d) { |
| | | return '-'; |
| | | } |
| | | const direction = d.lastTransactionTypeName || ''; |
| | | const quantity = d.lastTransactionQuantity ?? ''; |
| | | const bizCode = d.lastTransactionBizCode || '-'; |
| | | return [direction, quantity, bizCode].filter(Boolean).join(' '); |
| | | }); |
| | | |
| | | async function loadDetail(snCode: string) { |
| | | loading.value = true; |
| | | try { |
| | | detail.value = await getSnDetail(snCode); |
| | | if (!locationMaps.value) { |
| | | locationMaps.value = await getWmLocationMaps(); |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | async function handleQuery() { |
| | | if (!code.value?.trim()) { |
| | | message.warning('请è¾å
¥ææ«ç è·å SN ç '); |
| | | return; |
| | | } |
| | | await loadDetail(code.value.trim()); |
| | | } |
| | | |
| | | function openModal(snCode?: string) { |
| | | open.value = true; |
| | | code.value = snCode ?? ''; |
| | | detail.value = undefined; |
| | | if (snCode) { |
| | | void loadDetail(snCode); |
| | | } |
| | | } |
| | | |
| | | defineExpose({ open: openModal }); |
| | | |
| | | const [Descriptions] = useDescription({ |
| | | bordered: true, |
| | | column: 1, |
| | | schema: useSnDetailSchema(), |
| | | useCard: false, |
| | | labelStyle: { width: '80px', whiteSpace: 'nowrap' }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal v-model:open="open" title="SN ç å件详æ
" width="720px"> |
| | | <div class="flex gap-2"> |
| | | <Input |
| | | v-model:value="code" |
| | | placeholder="è¾å
¥ææ«ç SN ç åæ¥è¯¢" |
| | | @press-enter="handleQuery" |
| | | /> |
| | | <Button type="primary" :loading="loading" @click="handleQuery"> |
| | | æ¥è¯¢ |
| | | </Button> |
| | | </div> |
| | | <div class="mt-4"> |
| | | <Descriptions v-if="detail" :data="detail"> |
| | | <template #location> |
| | | {{ locationText }} |
| | | </template> |
| | | <template #lastTransaction> |
| | | {{ lastTransactionText }} |
| | | </template> |
| | | </Descriptions> |
| | | <Empty |
| | | v-else |
| | | :description="loading ? 'æ¥è¯¢ä¸...' : 'ææ æ°æ®ï¼è¯·è¾å
¥ SN ç æ¥è¯¢'" |
| | | /> |
| | | </div> |
| | | <template #footer> |
| | | <Button @click="open = false">å
³é</Button> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| | |
| | | rules: z.boolean().default(false), |
| | | }, |
| | | { |
| | | fieldName: 'autoGenerate', |
| | | label: 'èªå¨çæ', |
| | | component: 'Switch', |
| | | componentProps: { |
| | | checkedChildren: 'æ¯', |
| | | unCheckedChildren: 'å¦', |
| | | }, |
| | | rules: z.boolean().default(false), |
| | | }, |
| | | { |
| | | fieldName: 'generateCron', |
| | | label: 'çæå¨æ(Cron)', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ Cron 表达å¼ï¼å¦ 0 0 2 * * ?', |
| | | helpMessage: |
| | | 'å¯ç¨èªå¨çæåï¼éå¨ãç³»ç»ç®¡ç->宿¶ä»»å¡ãé
ç½® handler=MesWmStockTakingPlanAutoJob ç cron ä»»å¡ï¼å°æèªå¨ææ¬æ¹æ¡çæçç¹ä»»å¡', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['autoGenerate'], |
| | | show: (values) => !!values.autoGenerate, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'lastGenerateTime', |
| | | label: '䏿¬¡çææ¶é´', |
| | | component: 'Input', |
| | | disabled: true, |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'autoGenerate', |
| | | title: 'èªå¨çæ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'lastGenerateTime', |
| | | title: '䏿¬¡çææ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 120, |
| | |
| | | } from '#/views/wls/warehouse/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'execute' | 'submit' | 'update'; |
| | | export type FormType = 'create' | 'detail' | 'submit' | 'update'; |
| | | |
| | | /** 表å头鍿¯å¦åªè¯»ï¼æäº¤ãæ§è¡çç¹ã详æ
æï¼ */ |
| | | /** 表å头鍿¯å¦åªè¯»ï¼æäº¤ã详æ
æï¼ */ |
| | | function isHeaderReadonly(formType: FormType): boolean { |
| | | return ( |
| | | formType === 'detail' || formType === 'execute' || formType === 'submit' |
| | | ); |
| | | return formType === 'detail' || formType === 'submit'; |
| | | } |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | |
| | | import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmStockTakingTaskStatusEnum } from '@vben/constants'; |
| | |
| | | cancelStockTaking, |
| | | deleteStockTaking, |
| | | exportStockTaking, |
| | | getStockTakingApproveProcessList, |
| | | getStockTakingPage, |
| | | submitStockTakingApproval, |
| | | } from '#/api/mes/wm/stocktaking/task'; |
| | | import { |
| | | importStockTakingQuantity, |
| | |
| | | } from '#/api/wls/stocktaking/task/line'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue'; |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | formModalApi.setData({ formType: 'update', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æäº¤çç¹ä»»å¡ */ |
| | | /** å¼å§çç¹ */ |
| | | function handleSubmit(row: MesWmStockTakingTaskApi.StockTakingTask) { |
| | | formModalApi.setData({ formType: 'submit', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ§è¡çç¹ */ |
| | | function handleExecute(row: MesWmStockTakingTaskApi.StockTakingTask) { |
| | | formModalApi.setData({ formType: 'execute', id: row.id }).open(); |
| | | /** æäº¤ç»æå®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmitApproval(row: MesWmStockTakingTaskApi.StockTakingTask) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getStockTakingApproveProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi |
| | | .setData({ |
| | | id: row.id, |
| | | processList, |
| | | submitFn: submitStockTakingApproval, |
| | | }) |
| | | .open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleProcessDetail(row: MesWmStockTakingTaskApi.StockTakingTask) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('该任å¡å°æªæäº¤ç»æå®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | /** åæ¶çç¹ä»»å¡ */ |
| | |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | |
| | | <Grid table-title="çç¹ä»»å¡å表"> |
| | | <template #toolbar-tools> |
| | |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | label: 'å¼å§çç¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-stock-taking-task:update'], |
| | | ifShow: row.status === StatusEnum.PREPARE, |
| | |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æ§è¡çç¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-stock-taking-task:update'], |
| | | ifShow: row.status === StatusEnum.APPROVING, |
| | | onClick: handleExecute.bind(null, row), |
| | | }, |
| | | { |
| | | label: '导å
¥å®çæ°', |
| | | type: 'link', |
| | | icon: ACTION_ICON.UPLOAD, |
| | |
| | | onClick: handleImport.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤ç»æå®¡æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-stock-taking-task:update'], |
| | | ifShow: row.status === StatusEnum.APPROVING, |
| | | onClick: handleSubmitApproval.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | auth: ['mes:wm-stock-taking-task:query'], |
| | | ifShow: row.status === StatusEnum.APPROVING, |
| | | onClick: handleProcessDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åæ¶', |
| | | type: 'link', |
| | | danger: true, |
| | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createStockTaking, |
| | | finishStockTaking, |
| | | getStockTaking, |
| | | submitStockTaking, |
| | | updateStockTaking, |
| | |
| | | const isEditable = computed(() => |
| | | ['create', 'update'].includes(formType.value), |
| | | ); |
| | | const isExecute = computed(() => formType.value === 'execute'); // æ¯å¦æ§è¡çç¹æ¨¡å¼ |
| | | const isSubmit = computed(() => formType.value === 'submit'); // æ¯å¦æäº¤æ¨¡å¼ |
| | | const canSubmit = computed( |
| | | () => |
| | | formType.value === 'update' && |
| | | formData.value?.status === MesWmStockTakingTaskStatusEnum.PREPARE, |
| | | const isSubmit = computed(() => formType.value === 'submit'); // æ¯å¦å¼å§çç¹æ¨¡å¼ |
| | | const isResultEditable = computed( |
| | | // çç¹ä¸ï¼å®¡æ¹ä¸ï¼å¯ç»´æ¤çç¹ç»æ |
| | | () => formData.value?.status === MesWmStockTakingTaskStatusEnum.APPROVING, |
| | | ); |
| | | const showLineTab = computed(() => !formData.value?.blindFlag); // ç²çä¸å±ç¤ºçç¹æ¸
å |
| | | const showResultTab = computed( |
| | | () => |
| | | isExecute.value || |
| | | (!!formData.value?.status && |
| | | formData.value.status !== MesWmStockTakingTaskStatusEnum.PREPARE), |
| | | !!formData.value?.status && |
| | | formData.value.status !== MesWmStockTakingTaskStatusEnum.PREPARE, |
| | | ); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return $t('ui.actionTitle.view', ['çç¹ä»»å¡']); |
| | | } |
| | | if (formType.value === 'execute') { |
| | | return 'æ§è¡çç¹'; |
| | | } |
| | | if (formType.value === 'submit') { |
| | | return 'æäº¤çç¹ä»»å¡'; |
| | | return 'å¼å§çç¹'; |
| | | } |
| | | return formType.value === 'update' |
| | | ? $t('ui.actionTitle.edit', ['çç¹ä»»å¡']) |
| | |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** æäº¤çç¹ä»»å¡ï¼è¡¨åæä¿®æ¹æ¶å
ä¿åï¼åè°ç¨æäº¤æ¥å£ */ |
| | | /** å¼å§çç¹ï¼è¡¨åæä¿®æ¹æ¶å
ä¿åï¼åæäº¤ */ |
| | | async function handleSubmit() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid || !formData.value?.id) { |
| | |
| | | originalSnapshot.value = current; |
| | | } |
| | | await submitStockTaking(formData.value.id); |
| | | message.success('æäº¤æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** æ§è¡çç¹ */ |
| | | async function handleExecute() { |
| | | if (!formData.value?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await finishStockTaking(formData.value.id); |
| | | message.success('æ§è¡çç¹æå'); |
| | | message.success('å¼å§çç¹æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | subTabsName.value = data.formType === 'execute' ? 'results' : 'lines'; |
| | | subTabsName.value = 'lines'; |
| | | formApi.setState({ schema: useFormSchema(formType.value, formApi) }); |
| | | formApi.setDisabled(!isEditable.value); |
| | | modalApi.setState({ showConfirmButton: isEditable.value }); |
| | |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane v-if="showResultTab" key="results" tab="çç¹ç»æ"> |
| | | <ResultList |
| | | :form-type="isExecute ? 'execute' : 'detail'" |
| | | :form-type="isResultEditable ? 'execute' : 'detail'" |
| | | :task-id="formData.id" |
| | | /> |
| | | </Tabs.TabPane> |
| | |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm |
| | | v-if="canSubmit || isSubmit" |
| | | title="确认æäº¤è¯¥çç¹ä»»å¡ï¼ãæäº¤åå°ä¸è½ä¿®æ¹ã" |
| | | v-if="isSubmit" |
| | | title="确认å¼å§çç¹ï¼å¼å§åå°å»ç»åºåå¹¶å¯å½å
¥/导å
¥å®çæ°ã" |
| | | @confirm="handleSubmit" |
| | | > |
| | | <Button type="primary">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="isExecute" |
| | | title="确认æ§è¡çç¹æä½ï¼" |
| | | @confirm="handleExecute" |
| | | > |
| | | <Button type="primary">æ§è¡çç¹</Button> |
| | | <Button type="primary">å¼å§çç¹</Button> |
| | | </Popconfirm> |
| | | </div> |
| | | </template> |
| | |
| | | { label: 'éå®éè´§', value: 120 }, |
| | | { label: 'å¤ååæ', value: 121 }, |
| | | { label: 'å¤åå
¥åº', value: 122 }, |
| | | { label: 'çç¹è°æ´', value: 107 }, |
| | | { label: 'SN åºå
¥åº', value: 109 }, |
| | | { label: 'éè´å
¥åºçº¢å²', value: 111 }, |
| | | { label: '设å¤å°è´¦å
¥åº', value: 400 }, |
| | | { label: '设å¤å
¥åº', value: 401 }, |
| | | ]; |
| | | |
| | | /** å表çæç´¢è¡¨å */ |