Merge remote-tracking branch 'origin/dev_pro2.0' into dev_pro2.0
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | @echo off |
| | | setlocal EnableExtensions EnableDelayedExpansion |
| | | |
| | | REM ============================================================ |
| | | REM Vue Frontend Deploy Script |
| | | REM ============================================================ |
| | | |
| | | REM ========================= |
| | | REM Configuration |
| | | REM ========================= |
| | | |
| | | set "COMPANY=ZSJC" |
| | | |
| | | set "REMOTE_USER=root" |
| | | set "REMOTE_HOST=116.172.73.239" |
| | | set "REMOTE_PORT=22" |
| | | |
| | | set "PASSWORD=ecs-QGY8ecs-QGY8" |
| | | |
| | | set "ZIP_FILE=dist.zip" |
| | | set "REMOTE_DIR=/home/vue/dist" |
| | | |
| | | set "SSH_OPTS=-o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=15 -o ServerAliveCountMax=3" |
| | | |
| | | set "RETRY_COUNT=3" |
| | | |
| | | set "SSHPASS_DIR=%~dp0bin" |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Start |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo ============================================================ |
| | | echo Vue Dist Auto Deploy |
| | | echo ============================================================ |
| | | echo. |
| | | echo Company : %COMPANY% |
| | | echo Server : %REMOTE_USER%@%REMOTE_HOST% |
| | | echo Port : %REMOTE_PORT% |
| | | echo Remote Dir : %REMOTE_DIR% |
| | | echo Package : %ZIP_FILE% |
| | | echo. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Check sshpass |
| | | REM ============================================================ |
| | | |
| | | echo [1/7] Checking sshpass... |
| | | |
| | | if exist "%SSHPASS_DIR%\sshpass.exe" ( |
| | | set "PATH=%SSHPASS_DIR%;%PATH%" |
| | | ) |
| | | |
| | | where sshpass >nul 2>&1 |
| | | |
| | | if not errorlevel 1 ( |
| | | echo [OK] sshpass found. |
| | | goto CHECK_PNPM |
| | | ) |
| | | |
| | | echo [WARN] sshpass not found. |
| | | |
| | | if not exist "%SSHPASS_DIR%" ( |
| | | mkdir "%SSHPASS_DIR%" >nul 2>&1 |
| | | ) |
| | | |
| | | if exist "%SSHPASS_DIR%\sshpass.exe" ( |
| | | set "PATH=%SSHPASS_DIR%;%PATH%" |
| | | echo [OK] sshpass found. |
| | | goto CHECK_PNPM |
| | | ) |
| | | |
| | | echo [INFO] Downloading sshpass... |
| | | |
| | | powershell -NoProfile -ExecutionPolicy Bypass -Command "$url='https://github.com/xhcoding/sshpass-win32/releases/download/v1.0.3/sshpass.exe'; $out='%SSHPASS_DIR%\sshpass.exe'; Invoke-WebRequest -Uri $url -OutFile $out" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] Failed to download sshpass. |
| | | echo [ERROR] Please install sshpass manually. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | set "PATH=%SSHPASS_DIR%;%PATH%" |
| | | |
| | | echo [OK] sshpass installed. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Check pnpm |
| | | REM ============================================================ |
| | | |
| | | :CHECK_PNPM |
| | | |
| | | echo. |
| | | echo [2/7] Checking pnpm... |
| | | |
| | | where pnpm >nul 2>&1 |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] pnpm not found. |
| | | echo [HINT] npm install -g pnpm |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | call pnpm --version |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] pnpm execution failed. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Check node |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [3/7] Checking Node.js... |
| | | |
| | | where node >nul 2>&1 |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] Node.js not found. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | call node --version |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] Node.js execution failed. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Check node_modules |
| | | REM ============================================================ |
| | | |
| | | if not exist "node_modules" ( |
| | | echo. |
| | | echo [INFO] node_modules not found. |
| | | echo [INFO] Running pnpm install... |
| | | |
| | | call pnpm install |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] pnpm install failed. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] pnpm install completed. |
| | | ) else ( |
| | | echo [OK] node_modules exists. |
| | | ) |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Build |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [4/7] Building project... |
| | | |
| | | if exist "%ZIP_FILE%" ( |
| | | echo. |
| | | echo [WARN] %ZIP_FILE% already exists. |
| | | set "REBUILD=" |
| | | set /p "REBUILD=Rebuild? Y/N (default Y): " |
| | | |
| | | if /I "!REBUILD!"=="N" ( |
| | | echo [INFO] Skip build. |
| | | goto CHECK_ZIP |
| | | ) |
| | | ) |
| | | |
| | | echo [INFO] Company: %COMPANY% |
| | | echo [INFO] Command: pnpm run build |
| | | echo. |
| | | |
| | | set "VITE_COMPANY=%COMPANY%" |
| | | |
| | | call pnpm run build |
| | | |
| | | if errorlevel 1 ( |
| | | echo. |
| | | echo [ERROR] Build failed. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo. |
| | | echo [OK] Build completed. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Check ZIP |
| | | REM ============================================================ |
| | | |
| | | :CHECK_ZIP |
| | | |
| | | echo. |
| | | echo [5/7] Checking package... |
| | | |
| | | if not exist "%ZIP_FILE%" ( |
| | | echo [ERROR] %ZIP_FILE% not found. |
| | | echo [HINT] Please check whether your build generates dist.zip. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | for %%F in ("%ZIP_FILE%") do set "ZIP_BYTES=%%~zF" |
| | | |
| | | set /a ZIP_MB=ZIP_BYTES/1024/1024 |
| | | |
| | | echo [OK] Package found. |
| | | echo [INFO] File size: !ZIP_BYTES! bytes |
| | | echo [INFO] File size: !ZIP_MB! MB |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Set sshpass password |
| | | REM ============================================================ |
| | | |
| | | set "SSHPASS=%PASSWORD%" |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Test SSH |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [6/7] Testing SSH connection... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "echo SSH_OK" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] SSH connection failed. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] SSH connection successful. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Create remote directory |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Creating remote directory... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "mkdir -p '%REMOTE_DIR%'" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] Failed to create remote directory. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] Remote directory ready. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Upload temporary file |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Uploading package... |
| | | |
| | | set "REMOTE_TMP=%REMOTE_DIR%/.dist.zip.uploading" |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "rm -f '%REMOTE_TMP%'" |
| | | |
| | | sshpass -e scp -P %REMOTE_PORT% %SSH_OPTS% "%ZIP_FILE%" %REMOTE_USER%@%REMOTE_HOST%:"%REMOTE_TMP%" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] Upload failed. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] Upload completed. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Check remote file |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Checking remote package... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "test -f '%REMOTE_TMP%'" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] Remote package does not exist. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] Remote package exists. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Compare file size |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Checking package size... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "stat -c %%s '%REMOTE_TMP%'" |
| | | |
| | | echo. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Rename temporary file |
| | | REM ============================================================ |
| | | |
| | | echo [INFO] Activating package... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "mv -f '%REMOTE_TMP%' '%REMOTE_DIR%/%ZIP_FILE%'" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] Failed to activate package. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] Package activated. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Check unzip |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Checking unzip... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "command -v unzip" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [ERROR] unzip is not installed on server. |
| | | echo. |
| | | echo CentOS/RHEL: |
| | | echo yum install -y unzip |
| | | echo. |
| | | echo Ubuntu/Debian: |
| | | echo apt install -y unzip |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] unzip found. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Test ZIP |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Testing remote ZIP... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "cd '%REMOTE_DIR%' && unzip -t '%ZIP_FILE%'" |
| | | |
| | | if errorlevel 1 ( |
| | | echo. |
| | | echo [ERROR] ZIP test failed. |
| | | echo [INFO] Remote ZIP has been kept. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo [OK] ZIP test passed. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Extract |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Extracting package... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "cd '%REMOTE_DIR%' && unzip -o '%ZIP_FILE%'" |
| | | |
| | | if errorlevel 1 ( |
| | | echo. |
| | | echo [ERROR] Remote unzip failed. |
| | | echo [INFO] Remote ZIP has been kept. |
| | | goto ERROR_EXIT |
| | | ) |
| | | |
| | | echo. |
| | | echo [OK] Extract completed. |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Remove ZIP |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo [INFO] Removing remote ZIP... |
| | | |
| | | sshpass -e ssh -p %REMOTE_PORT% %SSH_OPTS% %REMOTE_USER%@%REMOTE_HOST% "rm -f '%REMOTE_DIR%/%ZIP_FILE%'" |
| | | |
| | | if errorlevel 1 ( |
| | | echo [WARN] Failed to remove remote ZIP. |
| | | ) else ( |
| | | echo [OK] Remote ZIP removed. |
| | | ) |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Success |
| | | REM ============================================================ |
| | | |
| | | echo. |
| | | echo ============================================================ |
| | | echo DEPLOY SUCCESS |
| | | echo ============================================================ |
| | | echo. |
| | | echo Company : %COMPANY% |
| | | echo Server : %REMOTE_HOST%:%REMOTE_PORT% |
| | | echo Remote Dir : %REMOTE_DIR% |
| | | echo Package : %ZIP_FILE% |
| | | echo Size : !ZIP_MB! MB |
| | | echo. |
| | | echo Deployment completed successfully. |
| | | echo. |
| | | |
| | | goto END |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM Error |
| | | REM ============================================================ |
| | | |
| | | :ERROR_EXIT |
| | | |
| | | echo. |
| | | echo ============================================================ |
| | | echo DEPLOY FAILED |
| | | echo ============================================================ |
| | | echo. |
| | | echo Please check the error message above. |
| | | echo. |
| | | |
| | | goto END |
| | | |
| | | |
| | | REM ============================================================ |
| | | REM End |
| | | REM ============================================================ |
| | | |
| | | :END |
| | | |
| | | pause |
| | | endlocal |
| | |
| | | favicon: '/logos/YZFXLogo.ico', |
| | | apiUrl: 'http://42.63.71.140:9004/admin-api', |
| | | }, |
| | | ZSJC: { |
| | | name: 'å®å¤ä¸ç建æç§ææéå
¬å¸', |
| | | logo: '/logos/YZFXLogo.png', |
| | | favicon: '/logos/YZFXLogo.ico', |
| | | apiUrl: 'http://116.172.73.239:9001/admin-api', |
| | | }, |
| | | }; |
| | |
| | | /** 请åç³è¯· */ |
| | | export interface Leave { |
| | | id?: number; |
| | | employeeId?: number; |
| | | employeeName?: string; |
| | | userId?: number; |
| | | userName?: string; |
| | | deptName?: string; |
| | | leaveType?: number; |
| | | startTime?: string; |
| | |
| | | processInstanceId?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** å®¡æ¹æµç¨ */ |
| | | export interface ApproveProcess { |
| | | key: string; |
| | | name: string; |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** æäº¤è¯·åç³è¯·ï¼å起审æ¹ï¼ */ |
| | | export function submitLeave(id: number) { |
| | | return requestClient.post(`/hrm/leave/submit?id=${id}`); |
| | | export function submitLeave(id: number, processDefinitionKey: string) { |
| | | return requestClient.post('/hrm/leave/submit', null, { |
| | | params: { id, processDefinitionKey }, |
| | | }); |
| | | } |
| | | |
| | | /** æ¤é请åç³è¯· */ |
| | |
| | | |
| | | /** è·åå¯ç¨æµç¨å®ä¹å表 */ |
| | | export function getLeaveApproveProcessDefinitionList() { |
| | | return requestClient.get<any[]>('/hrm/leave/approve-process-definition-list'); |
| | | return requestClient.get<HrmLeaveApi.ApproveProcess[]>( |
| | | '/hrm/leave/approve-process-definition-list', |
| | | ); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** è´¨æ£æ¹æ¡éé¡¹ï¼æ¹æ¡-产åå
³èï¼ */ |
| | | export namespace MesQcTemplateItemOptionApi { |
| | | export interface Option { |
| | | id?: number; // 产åå
³èç¼å· |
| | | templateId?: number; // è´¨æ£æ¹æ¡ ID |
| | | templateCode?: string; // è´¨æ£æ¹æ¡ç¼å· |
| | | templateName?: string; // è´¨æ£æ¹æ¡åç§° |
| | | quantityCheck?: number; // æä½æ£æµæ° |
| | | quantityUnqualified?: number; // æå¤§ä¸åæ ¼æ° |
| | | criticalRate?: number; // æå¤§è´å½ç¼ºé·çï¼%ï¼ |
| | | majorRate?: number; // æå¤§ä¸¥é缺é·çï¼%ï¼ |
| | | minorRate?: number; // æå¤§è½»å¾®ç¼ºé·çï¼%ï¼ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢äº§åå
³èå页 */ |
| | | export function getTemplateItemPage( |
| | | params: PageParam & { templateId?: number }, |
| | |
| | | export function deleteTemplateItem(id: number) { |
| | | return requestClient.delete(`/mes/qc/template/item/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æ ¹æ®ç©ææå·¥å/ä»»å¡åæ£æµç§ç±»è·åå¯ç¨è´¨æ£æ¹æ¡å表 */ |
| | | export function getTemplateItemOptionList(params: { |
| | | itemId?: number; |
| | | qcType: number; |
| | | taskId?: number; |
| | | workOrderId?: number; |
| | | }) { |
| | | return requestClient.get<MesQcTemplateItemOptionApi.Option[]>( |
| | | '/mes/qc/template/item/list-by-item-and-type', |
| | | { params }, |
| | | ); |
| | | } |
| | |
| | | { params: { issueId } }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢å¯éæç颿åè¡åè¡¨ï¼æç产订åè¿æ»¤ï¼ä»
è¿å已宿颿çè®°å½ï¼ */ |
| | | export function getReturnableIssueLines(workOrderId: number) { |
| | | return requestClient.get<MesWmProductIssueLineApi.ProductIssueLine[]>( |
| | | '/mes/wm/product-issue-line/returnable-list', |
| | | { params: { workOrderId } }, |
| | | ); |
| | | } |
| | |
| | | export interface ReturnIssueLine { |
| | | id?: number; // è¡ç¼å· |
| | | issueId?: number; // éæåç¼å· |
| | | issueLineId?: number; // 颿åè¡ç¼å·ï¼å
³èæ¥æºé¢æè®°å½ï¼ |
| | | itemId?: number; // ç©æç¼å· |
| | | itemCode?: string; // ç©æç¼ç |
| | | itemName?: string; // ç©æåç§° |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmLeaveApi } from '#/api/hrm/leave'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { UserSelect } from '#/views/system/user/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'userId', |
| | | label: 'ç³è¯·äºº', |
| | | component: markRaw(UserSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç³è¯·äºº', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'leaveType', |
| | |
| | | export function useGridColumns(): VxeTableGridOptions<HrmLeaveApi.Leave>['columns'] { |
| | | return [ |
| | | { field: 'id', title: 'ç³è¯·ç¼å·', width: 80 }, |
| | | { field: 'employeeName', title: 'ç³è¯·äºº', minWidth: 100 }, |
| | | { field: 'userName', title: 'ç³è¯·äºº', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { |
| | | field: 'leaveType', |
| | |
| | | bordered: true, |
| | | column: 2, |
| | | schema: [ |
| | | { label: 'åå·¥å§å', field: 'employeeName', render: textRender }, |
| | | { label: 'ç³è¯·äºº', field: 'userName', render: textRender }, |
| | | { label: 'æå±é¨é¨', field: 'deptName', render: textRender }, |
| | | { |
| | | label: '请åç±»å', |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmLeaveApi } from '#/api/hrm/leave'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | import { message, Modal, Select, Tag } from 'ant-design-vue'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictLabel } from '#/packages/effects/hooks/src'; |
| | |
| | | import { |
| | | cancelLeave, |
| | | deleteLeave, |
| | | getLeaveApproveProcessDefinitionList, |
| | | getLeavePage, |
| | | submitLeave, |
| | | } from '#/api/hrm/leave'; |
| | |
| | | } |
| | | } |
| | | |
| | | /** æäº¤è¯·åç³è¯· */ |
| | | const submitModalVisible = ref(false); |
| | | const currentRow = ref<HrmLeaveApi.Leave>(); |
| | | const processList = ref<HrmLeaveApi.ApproveProcess[]>([]); |
| | | const selectedProcessKey = ref<string>(); |
| | | const submitLoading = ref(false); |
| | | |
| | | /** æäº¤è¯·åç³è¯·ï¼å
éæ©å®¡æ¹æµç¨ï¼ */ |
| | | async function handleSubmit(row: HrmLeaveApi.Leave) { |
| | | await submitLeave(row.id!); |
| | | processList.value = await getLeaveApproveProcessDefinitionList(); |
| | | currentRow.value = row; |
| | | selectedProcessKey.value = undefined; |
| | | submitModalVisible.value = true; |
| | | } |
| | | |
| | | async function confirmSubmit() { |
| | | if (!selectedProcessKey.value) { |
| | | message.warning('è¯·éæ©è¯·åå®¡æ¹æµç¨'); |
| | | return; |
| | | } |
| | | submitLoading.value = true; |
| | | try { |
| | | await submitLeave(currentRow.value!.id!, selectedProcessKey.value); |
| | | message.success('æäº¤æå'); |
| | | submitModalVisible.value = false; |
| | | handleRefresh(); |
| | | } finally { |
| | | submitLoading.value = false; |
| | | } |
| | | } |
| | | |
| | | /** æ¤é请åç³è¯· */ |
| | |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <Modal |
| | | v-model:open="submitModalVisible" |
| | | title="æäº¤è³åååå
¬" |
| | | width="420px" |
| | | :confirm-loading="submitLoading" |
| | | @ok="confirmSubmit" |
| | | > |
| | | <Select |
| | | v-model:value="selectedProcessKey" |
| | | class="w-full" |
| | | placeholder="è¯·éæ©è¯·åå®¡æ¹æµç¨" |
| | | :options="processList.map((item) => ({ label: item.name, value: item.key }))" |
| | | /> |
| | | </Modal> |
| | | </Page> |
| | | </template> |
| | |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useUserStore } from '@vben/stores'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | |
| | | formApi.setDisabled(data.formType === 'detail'); |
| | | modalApi.setState({ showConfirmButton: data.formType !== 'detail' }); |
| | | if (!data?.id) { |
| | | // æ°å¢æ¶é»è®¤ç³è¯·äººä¸ºå½åç»å½ç¨æ·ï¼å¯ä¿®æ¹ |
| | | await formApi.setFieldValue('userId', userStore.userInfo?.id); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { DatePicker, message } from 'ant-design-vue'; |
| | | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | /** 导åºåæ°ï¼å端 PageReqVO 以æ°ç»è§£æ inspectDate èµ·æ¢ï¼ */ |
| | | interface ExportParams { |
| | | inspectDate?: [string, string]; |
| | | } |
| | | |
| | | interface ExportModalData { |
| | | /** å¼¹çªæ é¢ */ |
| | | title?: string; |
| | | /** 模å导åºå½æ°ï¼requestClient.download(...) è¿å Blob */ |
| | | exportApi: (params: ExportParams) => Promise<Blob>; |
| | | /** å¯¼åºæä»¶å */ |
| | | fileName: string; |
| | | } |
| | | |
| | | defineOptions({ name: 'QcExportRangeModal' }); |
| | | |
| | | const rangePickerProps = getRangePickerDefaultProps(); |
| | | |
| | | const modalData = ref<ExportModalData>(); |
| | | const range = ref<[string, string] | undefined>(undefined); |
| | | const exporting = ref(false); |
| | | const title = ref('导åº'); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (exporting.value) { |
| | | return; |
| | | } |
| | | const data = modalData.value; |
| | | if (!data) { |
| | | return; |
| | | } |
| | | exporting.value = true; |
| | | modalApi.lock(); |
| | | try { |
| | | const params: ExportParams = |
| | | range.value && range.value.length === 2 |
| | | ? { inspectDate: range.value } |
| | | : {}; |
| | | const blob = await data.exportApi(params); |
| | | downloadFileFromBlobPart({ fileName: data.fileName, source: blob }); |
| | | message.success('å¯¼åºæå'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | exporting.value = false; |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | modalData.value = undefined; |
| | | range.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<ExportModalData>(); |
| | | modalData.value = data; |
| | | range.value = undefined; |
| | | title.value = data?.title ?? '导åº'; |
| | | modalApi.setState({ confirmText: '导åº' }); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="title" class="w-2/5" :mask-closable="false"> |
| | | <div class="px-1 py-3"> |
| | | <div class="mb-2 text-sm font-medium text-gray-700">æ£éªæ¶é´</div> |
| | | <DatePicker.RangePicker |
| | | v-model:value="range" |
| | | v-bind="rangePickerProps" |
| | | allow-clear |
| | | class="w-full" |
| | | /> |
| | | <div class="mt-2 text-xs text-gray-400"> |
| | | ä¸éæ¶é´å导åºå
¨é¨æ°æ®ï¼å¯¼åºæãæ£æµæ¥æãè¿æ»¤ |
| | | </div> |
| | | </div> |
| | | </Modal> |
| | | </template> |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getTemplateItemOptionList } from '#/api/mes/qc/template/item'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProTaskSelect } from '#/views/mes/pro/task/components'; |
| | |
| | | statuses: [MesProTaskStatusEnum.PREPARE], |
| | | workOrderId: values.workOrderId, |
| | | workstationId: values.workstationId, |
| | | }), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'templateId', |
| | | label: 'è´¨æ£æ¹æ¡', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | placeholder: 'è¯·éæ©è´¨æ£æ¹æ¡', |
| | | }, |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['workOrderId', 'taskId'], |
| | | componentProps: (values) => ({ |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | disabled: formType !== 'create' || !values.workOrderId, |
| | | params: values.workOrderId |
| | | ? { |
| | | qcType: 2, |
| | | workOrderId: values.workOrderId, |
| | | taskId: values.taskId, |
| | | } |
| | | : {}, |
| | | placeholder: values.workOrderId |
| | | ? 'è¯·éæ©è´¨æ£æ¹æ¡' |
| | | : '请å
éæ©ç产订å', |
| | | }), |
| | | }, |
| | | }, |
| | |
| | | placeholder: 'è¯·éæ©æ£æµç»æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'inspectDate', |
| | | label: 'æ£æµæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesQcStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteIpqc, exportIpqc, getIpqcPage } from '#/api/mes/qc/ipqc'; |
| | | import { $t } from '#/locales'; |
| | | import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ExportRangeModal, exportRangeModalApi] = useVbenModal({ |
| | | connectedComponent: QcExportRangeModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const data = await exportIpqc(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'è¿ç¨æ£éªå.xls', source: data }); |
| | | /** 导åºè¡¨æ ¼ï¼å¼¹çªéæ£éªæ¶é´èå´ï¼ */ |
| | | function handleExport() { |
| | | exportRangeModalApi |
| | | .setData({ |
| | | title: 'ææ£éªæ¶é´å¯¼åºè¿ç¨æ£éªå', |
| | | exportApi: exportIpqc, |
| | | fileName: 'è¿ç¨æ£éªå.xls', |
| | | }) |
| | | .open(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ExportRangeModal /> |
| | | <Grid table-title="è¿ç¨æ£éªåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getTemplateItemOptionList } from '#/api/mes/qc/template/item'; |
| | | import { getSupplierSimpleList } from '#/api/srm/supplier'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'templateId', |
| | | label: 'è´¨æ£æ¹æ¡', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | placeholder: 'è¯·éæ©è´¨æ£æ¹æ¡', |
| | | }, |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['itemId'], |
| | | componentProps: (values) => ({ |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | disabled: formType !== 'create' || !values.itemId, |
| | | params: values.itemId |
| | | ? { itemId: values.itemId, qcType: 1 } |
| | | : {}, |
| | | placeholder: values.itemId ? 'è¯·éæ©è´¨æ£æ¹æ¡' : '请å
éæ©äº§åç©æ', |
| | | }), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'vendorId', |
| | | label: 'ä¾åºå', |
| | | component: 'ApiSelect', |
| | |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesQcStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteIqc, exportIqc, getIqcPage } from '#/api/mes/qc/iqc'; |
| | | import { $t } from '#/locales'; |
| | | import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ExportRangeModal, exportRangeModalApi] = useVbenModal({ |
| | | connectedComponent: QcExportRangeModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const data = await exportIqc(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'æ¥ææ£éªå.xls', source: data }); |
| | | /** 导åºè¡¨æ ¼ï¼å¼¹çªéæ£éªæ¶é´èå´ï¼ */ |
| | | function handleExport() { |
| | | exportRangeModalApi |
| | | .setData({ |
| | | title: 'ææ£éªæ¶é´å¯¼åºæ¥ææ£éªå', |
| | | exportApi: exportIqc, |
| | | fileName: 'æ¥ææ£éªå.xls', |
| | | }) |
| | | .open(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ExportRangeModal /> |
| | | <Grid table-title="æ¥ææ£éªåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getTemplateItemOptionList } from '#/api/mes/qc/template/item'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | |
| | | componentProps: (values) => ({ |
| | | disabled: !!values.sourceDocId, |
| | | placeholder: 'è¯·éæ©äº§åç©æ', |
| | | }), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'templateId', |
| | | label: 'è´¨æ£æ¹æ¡', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | placeholder: 'è¯·éæ©è´¨æ£æ¹æ¡', |
| | | }, |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['itemId'], |
| | | componentProps: (values) => ({ |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | disabled: formType !== 'create' || !values.itemId, |
| | | params: values.itemId |
| | | ? { itemId: values.itemId, qcType: 3 } |
| | | : {}, |
| | | placeholder: values.itemId ? 'è¯·éæ©è´¨æ£æ¹æ¡' : '请å
éæ©äº§åç©æ', |
| | | }), |
| | | }, |
| | | }, |
| | |
| | | placeholder: 'è¯·éæ©æ£æµç»æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'inspectDate', |
| | | label: 'æ£æµæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesQcStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteOqc, exportOqc, getOqcPage } from '#/api/mes/qc/oqc'; |
| | | import { $t } from '#/locales'; |
| | | import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ExportRangeModal, exportRangeModalApi] = useVbenModal({ |
| | | connectedComponent: QcExportRangeModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const data = await exportOqc(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'åºè´§æ£éªå.xls', source: data }); |
| | | /** 导åºè¡¨æ ¼ï¼å¼¹çªéæ£éªæ¶é´èå´ï¼ */ |
| | | function handleExport() { |
| | | exportRangeModalApi |
| | | .setData({ |
| | | title: 'ææ£éªæ¶é´å¯¼åºè´§æ£éªå', |
| | | exportApi: exportOqc, |
| | | fileName: 'åºè´§æ£éªå.xls', |
| | | }) |
| | | .open(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ExportRangeModal /> |
| | | <Grid table-title="åºè´§æ£éªåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getTemplateItemOptionList } from '#/api/mes/qc/template/item'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | | /** 表åç±»å */ |
| | |
| | | componentProps: (values) => ({ |
| | | disabled: !!values.sourceDocId, |
| | | placeholder: 'è¯·éæ©äº§åç©æ', |
| | | }), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'templateId', |
| | | label: 'è´¨æ£æ¹æ¡', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | placeholder: 'è¯·éæ©è´¨æ£æ¹æ¡', |
| | | }, |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['itemId'], |
| | | componentProps: (values) => ({ |
| | | api: getTemplateItemOptionList, |
| | | autoSelect: 'first', |
| | | labelField: 'templateName', |
| | | valueField: 'templateId', |
| | | disabled: formType !== 'create' || !values.itemId, |
| | | params: values.itemId |
| | | ? { itemId: values.itemId, qcType: 4 } |
| | | : {}, |
| | | placeholder: values.itemId ? 'è¯·éæ©è´¨æ£æ¹æ¡' : '请å
éæ©äº§åç©æ', |
| | | }), |
| | | }, |
| | | }, |
| | |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'inspectDate', |
| | | label: 'æ£æµæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesQcStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteRqc, exportRqc, getRqcPage } from '#/api/mes/qc/rqc'; |
| | | import { $t } from '#/locales'; |
| | | import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ExportRangeModal, exportRangeModalApi] = useVbenModal({ |
| | | connectedComponent: QcExportRangeModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const data = await exportRqc(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'éè´§æ£éªå.xls', source: data }); |
| | | /** 导åºè¡¨æ ¼ï¼å¼¹çªéæ£éªæ¶é´èå´ï¼ */ |
| | | function handleExport() { |
| | | exportRangeModalApi |
| | | .setData({ |
| | | title: 'ææ£éªæ¶é´å¯¼éè´§æ£éªå', |
| | | exportApi: exportRqc, |
| | | fileName: 'éè´§æ£éªå.xls', |
| | | }) |
| | | .open(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ExportRangeModal /> |
| | | <Grid table-title="éè´§æ£éªåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | export { default as WmProductIssueLineSelect } from './select.vue'; |
| | | export { default as WmProductIssueLineSelectDialog } from './select-dialog.vue'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line'; |
| | | |
| | | import { nextTick, ref } from 'vue'; |
| | | |
| | | import { message, Modal } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getReturnableIssueLines } from '#/api/mes/wm/productissue/line'; |
| | | |
| | | const props = defineProps<{ |
| | | workOrderId?: number; |
| | | }>(); |
| | | |
| | | const emit = defineEmits<{ |
| | | selected: [rows: MesWmProductIssueLineApi.ProductIssueLine[]]; |
| | | }>(); |
| | | |
| | | const open = ref(false); |
| | | const selectedRows = ref<MesWmProductIssueLineApi.ProductIssueLine[]>([]); |
| | | |
| | | const columns: VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>['columns'] = [ |
| | | { type: 'radio', width: 60 }, |
| | | { 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: 'batchCode', title: 'æ¹æ¬¡å·', minWidth: 120 }, |
| | | ]; |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns, |
| | | height: 480, |
| | | keepSource: true, |
| | | radioConfig: { highlight: true, trigger: 'row' }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => { |
| | | if (!props.workOrderId) { |
| | | return { list: [], total: 0 }; |
| | | } |
| | | const list = await getReturnableIssueLines(props.workOrderId); |
| | | return { list, total: list.length }; |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>, |
| | | gridEvents: { |
| | | radioChange: ({ row }: { row: MesWmProductIssueLineApi.ProductIssueLine }) => { |
| | | selectedRows.value = [row]; |
| | | }, |
| | | cellDblclick: async ({ row }: { row: MesWmProductIssueLineApi.ProductIssueLine }) => { |
| | | selectedRows.value = [row]; |
| | | await gridApi.grid.setRadioRow(row); |
| | | handleConfirm(); |
| | | }, |
| | | }, |
| | | }); |
| | | |
| | | async function openModal() { |
| | | open.value = true; |
| | | selectedRows.value = []; |
| | | await nextTick(); |
| | | await gridApi.grid.clearRadioRow(); |
| | | await gridApi.query(); |
| | | } |
| | | |
| | | function closeModal() { |
| | | open.value = false; |
| | | selectedRows.value = []; |
| | | } |
| | | |
| | | function handleConfirm() { |
| | | if (selectedRows.value.length === 0) { |
| | | message.warning('è¯·éæ©ä¸æ¡é¢æè®°å½'); |
| | | return; |
| | | } |
| | | emit('selected', selectedRows.value); |
| | | open.value = false; |
| | | } |
| | | |
| | | defineExpose({ open: openModal }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | v-model:open="open" |
| | | :destroy-on-close="true" |
| | | title="éæ©é¢æè®°å½" |
| | | width="70%" |
| | | @cancel="closeModal" |
| | | @ok="handleConfirm" |
| | | > |
| | | <Grid table-title="å¯éæçé¢æè®°å½" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line'; |
| | | |
| | | import { computed, ref, useAttrs, watch } from 'vue'; |
| | | |
| | | import { IconifyIcon } from '@vben/icons'; |
| | | |
| | | import { Input, Tooltip } from 'ant-design-vue'; |
| | | |
| | | import { getProductIssueLine } from '#/api/mes/wm/productissue/line'; |
| | | |
| | | import ProductIssueLineSelectDialog from './select-dialog.vue'; |
| | | |
| | | defineOptions({ name: 'WmProductIssueLineSelect', inheritAttrs: false }); |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | allowClear?: boolean; |
| | | disabled?: boolean; |
| | | modelValue?: number; |
| | | placeholder?: string; |
| | | workOrderId?: number; |
| | | }>(), |
| | | { |
| | | allowClear: true, |
| | | disabled: false, |
| | | modelValue: undefined, |
| | | placeholder: 'è¯·éæ©é¢æè®°å½', |
| | | workOrderId: undefined, |
| | | }, |
| | | ); |
| | | |
| | | const emit = defineEmits<{ |
| | | change: [item: MesWmProductIssueLineApi.ProductIssueLine | undefined]; |
| | | 'update:modelValue': [value: number | undefined]; |
| | | }>(); |
| | | |
| | | const attrs = useAttrs(); |
| | | const dialogRef = ref<InstanceType<typeof ProductIssueLineSelectDialog>>(); |
| | | const hovering = ref(false); |
| | | const selectedItem = ref<MesWmProductIssueLineApi.ProductIssueLine>(); |
| | | |
| | | const displayLabel = computed(() => { |
| | | const item = selectedItem.value; |
| | | if (!item) { |
| | | return ''; |
| | | } |
| | | return `${item.itemCode || '-'} | ${item.itemName || '-'} | æ°é:${item.quantity}`; |
| | | }); |
| | | |
| | | const showClear = computed( |
| | | () => |
| | | props.allowClear && |
| | | !props.disabled && |
| | | hovering.value && |
| | | props.modelValue !== null, |
| | | ); |
| | | |
| | | async function resolveItemById(id: number | undefined) { |
| | | if (!id) { |
| | | selectedItem.value = undefined; |
| | | return; |
| | | } |
| | | if (selectedItem.value?.id === id) { |
| | | return; |
| | | } |
| | | selectedItem.value = await getProductIssueLine(id); |
| | | } |
| | | |
| | | watch(() => props.modelValue, resolveItemById, { immediate: true }); |
| | | |
| | | function clearSelected() { |
| | | selectedItem.value = undefined; |
| | | emit('update:modelValue', undefined); |
| | | emit('change', undefined); |
| | | } |
| | | |
| | | function handleClick(event: MouseEvent) { |
| | | if (props.disabled) { |
| | | return; |
| | | } |
| | | const target = event.target as HTMLElement; |
| | | if (showClear.value && target.closest('.ant-input-suffix')) { |
| | | event.stopPropagation(); |
| | | clearSelected(); |
| | | return; |
| | | } |
| | | dialogRef.value?.open(); |
| | | } |
| | | |
| | | function handleSelected(rows: MesWmProductIssueLineApi.ProductIssueLine[]) { |
| | | const item = rows[0]; |
| | | if (!item) { |
| | | return; |
| | | } |
| | | selectedItem.value = item; |
| | | emit('update:modelValue', item.id); |
| | | emit('change', item); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div |
| | | v-bind="attrs" |
| | | class="w-full" |
| | | :class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'" |
| | | @click="handleClick" |
| | | @mouseenter="hovering = true" |
| | | @mouseleave="hovering = false" |
| | | > |
| | | <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false"> |
| | | <template #title> |
| | | <div v-if="selectedItem" class="leading-6"> |
| | | <div>ç©æç¼ç ï¼{{ selectedItem.itemCode || '-' }}</div> |
| | | <div>ç©æåç§°ï¼{{ selectedItem.itemName || '-' }}</div> |
| | | <div>è§æ ¼åå·ï¼{{ selectedItem.specification || '-' }}</div> |
| | | <div>å¯éæ°éï¼{{ selectedItem.quantity ?? '-' }}</div> |
| | | <div>æ¹æ¬¡å·ï¼{{ selectedItem.batchCode || '-' }}</div> |
| | | </div> |
| | | </template> |
| | | <Input |
| | | :disabled="disabled" |
| | | :placeholder="placeholder" |
| | | :value="displayLabel" |
| | | readonly |
| | | > |
| | | <template #suffix> |
| | | <IconifyIcon |
| | | class="size-4" |
| | | :icon="showClear ? 'lucide:circle-x' : 'lucide:search'" |
| | | /> |
| | | </template> |
| | | </Input> |
| | | </Tooltip> |
| | | </div> |
| | | <ProductIssueLineSelectDialog |
| | | ref="dialogRef" |
| | | :work-order-id="workOrderId" |
| | | @selected="handleSelected" |
| | | /> |
| | | </template> |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProTaskApi } from '#/api/mes/pro/task'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmProductIssueApi } from '#/api/mes/wm/productissue'; |
| | | import type { MesWmProductIssueDetailApi } from '#/api/mes/wm/productissue/detail'; |
| | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProTaskSelect } from '#/views/mes/pro/task/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | import { WmMaterialStockSelect } from '#/views/wls/materialstock/components'; |
| | | import { |
| | |
| | | label: 'ç产订å', |
| | | component: markRaw(ProWorkOrderSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç产订å', |
| | | status: MesProWorkOrderStatusEnum.CONFIRMED, |
| | | // 订ååæ´ï¼æ¸
空已éç产任å¡ï¼é¿å
ä»»å¡ä¸è®¢åä¸ä¸è´ |
| | | onChange: async () => { |
| | | await formApi?.setFieldValue('taskId', undefined); |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'taskId', |
| | | label: 'ç产任å¡', |
| | | component: markRaw(ProTaskSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç产任å¡', |
| | | // éä¸ä»»å¡åèªå¨å¸¦åºå
¶å·¥ä½ç« |
| | | onChange: async (task?: MesProTaskApi.Task) => { |
| | | await formApi?.setFieldValue('workstationId', task?.workstationId); |
| | | }, |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['workOrderId'], |
| | | componentProps: (values) => ({ |
| | | disabled: !values.workOrderId, |
| | | placeholder: values.workOrderId ? 'è¯·éæ©ç产任å¡' : '请å
éæ©ç产订å', |
| | | workOrderId: values.workOrderId, |
| | | }), |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | |
| | | { |
| | | field: 'workOrderCode', |
| | | title: 'ç产订å', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'taskCode', |
| | | title: 'ç产任å¡', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'batchId', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | |
| | | label: 'åºåè®°å½', |
| | | component: markRaw(WmMaterialStockSelect), |
| | | componentProps: { |
| | | // éæ©åºåè®°å½åï¼èªå¨åå¡«ä»åº/åºåº/åºä½/æ¹æ¬¡/æ°é |
| | | // éæ©åºåè®°å½åï¼èªå¨åå¡«ç©æ/ä»åº/åºåº/åºä½/æ¹æ¬¡/æ°éï¼å
¨é¨ä»¥åºåè®°å½ä¸ºå |
| | | onChange: async (stock?: MesWmMaterialStockApi.MaterialStock) => { |
| | | await formApi?.setValues({ |
| | | areaId: stock?.areaId, |
| | | batchCode: stock?.batchCode, |
| | | batchId: stock?.batchId, |
| | | itemId: stock?.itemId, |
| | | locationId: stock?.locationId, |
| | | quantity: stock?.quantity, |
| | | quantityMax: stock?.quantity, |
| | |
| | | dependencies: { |
| | | triggerFields: ['itemId'], |
| | | componentProps: (values) => ({ |
| | | // åºå鿩弹çªåªè¯å« mdmItemId å
¥åï¼å¹¶ç¨å®ä½ä¸º itemId è¿æ»¤æ¡ä»¶ |
| | | mdmItemId: values.itemId, |
| | | }), |
| | | }, |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line'; |
| | | import type { MesWmReturnIssueApi } from '#/api/mes/wm/returnissue'; |
| | | import type { MesWmReturnIssueDetailApi } from '#/api/mes/wm/returnissue/detail'; |
| | | import type { MesWmReturnIssueLineApi } from '#/api/mes/wm/returnissue/line'; |
| | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | import { WmProductIssueLineSelect } from '#/views/wls/productissue/components'; |
| | | import { WmMaterialStockSelect } from '#/views/wls/materialstock/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | |
| | | } |
| | | |
| | | /** éæåè¡æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useLineFormSchema(formApi?: VbenFormApi): VbenFormSchema[] { |
| | | export function useLineFormSchema( |
| | | formApi?: VbenFormApi, |
| | | workOrderId?: number, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'materialStockId', |
| | | label: 'åºåè®°å½', |
| | | component: markRaw(WmMaterialStockSelect), |
| | | fieldName: 'issueLineId', |
| | | label: 'é¢æè®°å½', |
| | | component: markRaw(WmProductIssueLineSelect), |
| | | componentProps: { |
| | | // éæ©åºåè®°å½åï¼èªå¨åå¡«ç©æ/æ¹æ¬¡/æ°é |
| | | onChange: async (stock?: MesWmMaterialStockApi.MaterialStock) => { |
| | | workOrderId, |
| | | onChange: async (line?: MesWmProductIssueLineApi.ProductIssueLine) => { |
| | | await formApi?.setValues({ |
| | | batchCode: stock?.batchCode, |
| | | batchId: stock?.batchId, |
| | | itemId: stock?.itemId, |
| | | materialDisplayName: stock |
| | | ? [stock.itemName, stock.specification].filter(Boolean).join(' | ') |
| | | batchCode: line?.batchCode, |
| | | batchId: line?.batchId, |
| | | itemId: line?.itemId, |
| | | materialDisplayName: line |
| | | ? [line.itemName, line.specification].filter(Boolean).join(' | ') |
| | | : undefined, |
| | | quantity: stock?.quantity, |
| | | quantityMax: stock?.quantity, |
| | | quantity: line?.quantity, |
| | | quantityMax: line?.quantity, |
| | | }); |
| | | }, |
| | | virtualFilter: 'only', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: 'éæ©åºååèªå¨å¸¦åº', |
| | | placeholder: 'éæ©é¢æè®°å½åèªå¨å¸¦åº', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: 'éæ©åºååèªå¨å¸¦åº', |
| | | placeholder: 'éæ©é¢æè®°å½åèªå¨å¸¦åº', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | <template v-if="formData?.id"> |
| | | <Divider>ç©æä¿¡æ¯</Divider> |
| | | <div class="mx-4"> |
| | | <LineList :form-type="formType" :issue-id="formData.id" /> |
| | | <LineList :form-type="formType" :issue-id="formData.id" :work-order-id="formData.workOrderId" /> |
| | | </div> |
| | | </template> |
| | | <template #prepend-footer> |
| | |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesWmReturnIssueLineApi.ReturnIssueLine>(); |
| | | const issueId = ref<number>(); // æå±éæåç¼å· |
| | | const issueId = ref<number>(); |
| | | const workOrderId = ref<number>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id |
| | |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | formApi.setState({ schema: useLineFormSchema(formApi) }); |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ id?: number; issueId: number }>(); |
| | | const data = modalApi.getData<{ id?: number; issueId: number; workOrderId?: number }>(); |
| | | issueId.value = data.issueId; |
| | | workOrderId.value = data.workOrderId; |
| | | formApi.setState({ schema: useLineFormSchema(formApi, workOrderId.value) }); |
| | | if (!data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getReturnIssueLine(data.id); |
| | | // è®¾ç½®å° valuesï¼å¹¶æå»ºç©æå±ç¤ºå |
| | | await formApi.setValues({ |
| | | ...formData.value, |
| | | materialDisplayName: [formData.value.itemName, formData.value.specification] |
| | |
| | | const props = defineProps<{ |
| | | formType: FormType; |
| | | issueId: number; |
| | | workOrderId?: number; |
| | | }>(); |
| | | |
| | | const isEditable = computed(() => |
| | |
| | | |
| | | /** æ·»å ç©æ */ |
| | | function handleCreate() { |
| | | lineFormModalApi.setData({ issueId: props.issueId }).open(); |
| | | lineFormModalApi.setData({ issueId: props.issueId, workOrderId: props.workOrderId }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ç©æ */ |
| | | function handleEdit(row: MesWmReturnIssueLineApi.ReturnIssueLine) { |
| | | lineFormModalApi.setData({ id: row.id, issueId: props.issueId }).open(); |
| | | lineFormModalApi.setData({ id: row.id, issueId: props.issueId, workOrderId: props.workOrderId }).open(); |
| | | } |
| | | |
| | | /** å é¤ç©æ */ |