Merge remote-tracking branch 'origin/dev_pro2.0' into dev_pro2.0
# Conflicts:
# src/views/erp/purchase/invoice/modules/form.vue
| | |
| | | <excludeFolder url="file://$MODULE_DIR$/.cache" /> |
| | | <excludeFolder url="file://$MODULE_DIR$/dist" /> |
| | | <excludeFolder url="file://$MODULE_DIR$/build" /> |
| | | <excludeFolder url="file://$MODULE_DIR$/.agents" /> |
| | | <excludeFolder url="file://$MODULE_DIR$/.codex" /> |
| | | </content> |
| | | <orderEntry type="inheritedJdk" /> |
| | | <orderEntry type="sourceFolder" forTests="false" /> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | @echo off |
| | | setlocal EnableExtensions EnableDelayedExpansion |
| | | |
| | | REM ============================================================ |
| | | REM Vue Frontend Deploy Script |
| | | REM ============================================================ |
| | | |
| | | REM ========================= |
| | | REM Configuration |
| | | REM ========================= |
| | | |
| | | set "COMPANY=WTXC" |
| | | |
| | | set "REMOTE_USER=root" |
| | | set "REMOTE_HOST=42.63.71.140" |
| | | set "REMOTE_PORT=22" |
| | | |
| | | set "PASSWORD=Nxwtxc123." |
| | | |
| | | 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.70.231:9001/admin-api', |
| | | }, |
| | | WTXC: { |
| | | name: 'å®å¤ä¸éæ°æ', |
| | | logo: '/logos/YZFXLogo.png', |
| | | favicon: '/logos/YZFXLogo.ico', |
| | | apiUrl: 'http://42.63.71.140:9001/admin-api', |
| | | }, |
| | | }; |
| | |
| | | totalCount?: number; // å计æ°é |
| | | totalPrice?: number; // å计éé¢ï¼åä½ï¼å
|
| | | totalProductPrice?: number; // 产åéé¢ï¼åä½ï¼å
|
| | | totalTaxPrice?: number; // å计ç¨é¢ï¼åä½ï¼å
|
| | | discountPercent?: number; // 伿 çï¼ç¾åæ¯ |
| | | discountPrice?: number; // 伿 éé¢ï¼åä½ï¼å
|
| | | depositPrice?: number; // å®ééé¢ï¼åä½ï¼å
|
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesProFeedbackApi { |
| | | export interface FeedbackSubmitReqVO { |
| | | id: number; |
| | | produceDate?: string; |
| | | expireDate?: string; |
| | | receiptDate?: string; |
| | | vendorId?: number; |
| | | clientId?: number; |
| | | purchaseOrderCode?: string; |
| | | salesOrderCode?: string; |
| | | toolId?: number; |
| | | moldId?: number; |
| | | lotNumber?: string; |
| | | qualityStatus?: number; |
| | | } |
| | | |
| | | /** MES ç产æ¥å·¥ */ |
| | | export interface Feedback { |
| | | id?: number; |
| | |
| | | remark?: string; // 夿³¨ |
| | | creator?: string; // å建人 |
| | | createTime?: number; // å建æ¶é´ |
| | | processInstanceId?: string; // BPM æµç¨å®ä¾ç¼å· |
| | | processDefinitionKey?: string; // BPM æµç¨å®ä¹ Key |
| | | } |
| | | |
| | | /** MES ç产æ¥å·¥å页æ¥è¯¢åæ° */ |
| | |
| | | } |
| | | |
| | | /** æäº¤ç产æ¥å·¥ */ |
| | | export function submitFeedback(id: number) { |
| | | return requestClient.put(`/mes/pro/feedback/submit?id=${id}`); |
| | | export function submitFeedback(data: MesProFeedbackApi.FeedbackSubmitReqVO) { |
| | | return requestClient.put(`/mes/pro/feedback/submit`, data); |
| | | } |
| | | |
| | | /** 驳åç产æ¥å·¥ */ |
| | |
| | | completionProgress?: number; // 宿è¿åº¦ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: number; // å建æ¶é´ |
| | | feedbackApprove?: boolean; // æ¯å¦æ¥å·¥å®¡æ¹ |
| | | processDefinitionKey?: string; // æ¥å·¥å®¡æ¹æµç¨å®ä¹ Key |
| | | feedbackRole?: string; // æ¥å·¥è§è² |
| | | } |
| | | |
| | | /** MES ç产订åå页æ¥è¯¢åæ° */ |
| | |
| | | `/mes/pro/work-order/check-auto-finish/${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·å¾ç产订åå®¡æ¹æµç¨å表 */ |
| | | export function getWorkOrderApproveProcessList() { |
| | | return requestClient.get<Array<{ id: string; name: string; key: string }>>( |
| | | '/mes/pro/work-order/approve-process-list', |
| | | ); |
| | | } |
| | |
| | | } |
| | | |
| | | /** éè¿è§è²æ è¯è·åç¨æ·ç²¾ç®ä¿¡æ¯å表 */ |
| | | export function getSimpleUserListByRoleCode(code: string) { |
| | | export function getSimpleUserListByRoleCode(code: Object) { |
| | | return requestClient.get<SystemUserApi.UserSimple[]>('/system/user/list-by-role-code', { |
| | | params: { code }, |
| | | params: code , |
| | | }); |
| | | } |
| | |
| | | }, |
| | | component: () => import('#/views/mes/pd/project/detail/index.vue'), |
| | | }, |
| | | { |
| | | path: 'pro/feedback/detail/:id', |
| | | name: 'MesProFeedbackDetail', |
| | | meta: { |
| | | title: 'ç产æ¥å·¥è¯¦æ
', |
| | | activePath: '/mes/pro/feedback', |
| | | }, |
| | | props: true, |
| | | component: () => import('#/views/mes/pro/feedback/detail/index.vue'), |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | { |
| | | fieldName: 'accountId', |
| | | fieldName: 'accountName', |
| | | label: '仿¬¾è´¦æ·', |
| | | component: 'ApiSelect', |
| | | component: 'Input', |
| | | rules: 'required', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä»æ¬¾è´¦æ·', |
| | | placeholder: '请è¾å
¥ä»æ¬¾è´¦æ·', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getAccountSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | help: '仿¬¾è´¦æ·æ°æ®æ¥èªãè´¢å¡ç®¡ç â ç»ç®è´¦æ·ãèåç»´æ¤çå¼å¯ç¶æè´¦æ·ï¼æ é项æ¶è¯·å
å¨ç»ç®è´¦æ·é¡µé¢æ°å¢ã', |
| | | }, |
| | | { |
| | | fieldName: 'totalPrice', |
| | |
| | | id: undefined, |
| | | no: '', |
| | | supplierId: undefined, |
| | | accountId: undefined, |
| | | accountName: undefined, |
| | | financeUserId: undefined, |
| | | paymentTime: undefined, |
| | | remark: '', |
| | |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value)); |
| | | if (!data || !data.id) { |
| | | // æ°å¢æ¶ï¼é»è®¤éä¸è´¦æ· |
| | | // æ°å¢æ¶ï¼é»è®¤å¡«å
¥é»è®¤ç»ç®è´¦æ·åç§° |
| | | const accountList = await getAccountSimpleList(); |
| | | const defaultAccount = accountList.find((item) => item.defaultStatus); |
| | | if (defaultAccount) { |
| | | await formApi.setValues({ accountId: defaultAccount.id }); |
| | | await formApi.setValues({ accountName: defaultAccount.name }); |
| | | } |
| | | return; |
| | | } |
| | |
| | | import type { Ref } from 'vue'; |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { getSupplierSimpleList } from '#/api/srm/supplier'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | export function useFormSchema( |
| | | orderTotalTaxPrice?: Ref<number | undefined>, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | |
| | | }, |
| | | componentProps: (_values, form) => ({ |
| | | placeholder: 'è¯·éæ©éè´è®¢å', |
| | | onChange: (order: { supplierName?: string } | undefined) => { |
| | | onChange: ( |
| | | order: |
| | | | { supplierName?: string; totalTaxPrice?: number } |
| | | | undefined, |
| | | ) => { |
| | | form.setFieldValue('supplierName', order?.supplierName); |
| | | if (orderTotalTaxPrice) { |
| | | orderTotalTaxPrice.value = order?.totalTaxPrice; |
| | | } |
| | | }, |
| | | }), |
| | | }, |
| | |
| | | label: 'æ¥ç¥¨éé¢ï¼å
ï¼', |
| | | component: 'InputNumber', |
| | | rules: 'required', |
| | | description: () => |
| | | orderTotalTaxPrice?.value != null |
| | | ? `éè´è®¢åæªå¼ç¥¨éé¢ï¼${Number(orderTotalTaxPrice.value).toFixed(2)} å
` |
| | | : '', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥æ¥ç¥¨éé¢', |
| | | min: 0, |
| | | precision: 2, |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['purchaseOrderId'], |
| | | componentProps: () => ({ |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥æ¥ç¥¨éé¢', |
| | | min: 0, |
| | | max: orderTotalTaxPrice?.value ?? undefined, |
| | | precision: 2, |
| | | }), |
| | | rules: () => { |
| | | const max = orderTotalTaxPrice?.value; |
| | | if (max != null) { |
| | | return z |
| | | .number({ message: '请è¾å
¥æ¥ç¥¨éé¢' }) |
| | | .min(0, 'æ¥ç¥¨éé¢ä¸è½å°äº 0') |
| | | .max(max, `æ¥ç¥¨éé¢ä¸è½å¤§äºæªå¼ç¥¨éé¢ ${Number(max).toFixed(2)}`); |
| | | } |
| | | return z |
| | | .number({ message: '请è¾å
¥æ¥ç¥¨éé¢' }) |
| | | .min(0, 'æ¥ç¥¨éé¢ä¸è½å°äº 0'); |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | |
| | | { |
| | | title: 'æ¥ç¥¨éé¢ï¼å
ï¼', |
| | | field: 'price', |
| | | minWidth: 150, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | title: 'å©ä½å¯ä»éé¢', |
| | | field: 'remainingInvoicePrice', |
| | | minWidth: 150, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | |
| | | const formData = ref<ErpPurchaseInvoiceApi.PurchaseInvoice>(); |
| | | const formType = ref<FormType>('create'); |
| | | const ocrSupplierName = ref(''); |
| | | const orderTotalTaxPrice = ref<number>(); // æééè´è®¢åå计ç¨é¢ï¼æªå¼ç¥¨éé¢ï¼ç¨äºæç¤ºä¸æ ¡éªï¼ |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | schema: useFormSchema(orderTotalTaxPrice), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | ocrSupplierName.value = ''; |
| | | orderTotalTaxPrice.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | |
| | | }>(); |
| | | formType.value = data.formType; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema()); |
| | | formApi.updateSchema(useFormSchema(orderTotalTaxPrice)); |
| | | await formApi.resetForm(); |
| | | // OCR è¯å«æ°æ®é¢å¡«ï¼æ°å»ºåºæ¯ï¼ç¼è¾/详æ
ä¸å¤çï¼ |
| | | const ocr = data?.ocrData; |
| | |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'totalTaxPrice', |
| | | title: 'æªå¼ç¥¨éé¢', |
| | | formatter: 'formatAmount2', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | minWidth: 100, |
| | |
| | | }, |
| | | { |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | componentProps: (values) => ({ |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥æ¯ä»è®¢é', |
| | | precision: 2, |
| | | min: 0, |
| | | }, |
| | | max: values.totalPrice ?? undefined, |
| | | }), |
| | | fieldName: 'depositPrice', |
| | | label: 'æ¯ä»è®¢é', |
| | | dependencies: { |
| | | triggerFields: ['totalPrice'], |
| | | rules(values) { |
| | | if (values.totalPrice != null) { |
| | | return z |
| | | .number() |
| | | .min(0, '订éä¸è½å°äº0') |
| | | .max(values.totalPrice, '订éä¸è½å¤§äºä¼æ åéé¢'); |
| | | } |
| | | return z.number().min(0, '订éä¸è½å°äº0'); |
| | | }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | |
| | | }, |
| | | { |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | componentProps: (values) => ({ |
| | | class: '!w-full', |
| | | placeholder: '请è¾å
¥æ¶å订é', |
| | | precision: 2, |
| | | min: 0, |
| | | }, |
| | | max: values.totalPrice ?? undefined, |
| | | }), |
| | | fieldName: 'depositPrice', |
| | | label: 'æ¶å订é', |
| | | dependencies: { |
| | | triggerFields: ['totalPrice'], |
| | | rules(values) { |
| | | if (values.totalPrice != null) { |
| | | return z |
| | | .number() |
| | | .min(0, '订éä¸è½å°äº0') |
| | | .max(values.totalPrice, 'æ¶å订éä¸è½å¤§äºä¼æ åéé¢'); |
| | | } |
| | | return z.number().min(0, '订éä¸è½å°äº0').optional(); |
| | | }, |
| | | }, |
| | | rules: z.number().min(0).optional(), |
| | | }, |
| | | ]; |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProFeedbackApi } from '#/api/mes/pro/feedback'; |
| | | import type { MesProTaskApi } from '#/api/mes/pro/task'; |
| | | import type { DescriptionItemSchema } from '#/components/description'; |
| | | |
| | | import { h, markRaw } from 'vue'; |
| | | |
| | |
| | | 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 { DictTag } from '#/components/dict-tag'; |
| | | |
| | | /** ç产æ¥å·¥è¡¨åç±»å */ |
| | | export type FormType = 'approve' | 'create' | 'detail' | 'submit' | 'update'; |
| | |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** ç产æ¥å·¥è¯¦æ
æè¦ */ |
| | | export function useDetailSchema(): DescriptionItemSchema[] { |
| | | return [ |
| | | { field: 'code', label: 'æ¥è®¢åå·' }, |
| | | { |
| | | field: 'type', |
| | | label: 'æ¥å·¥ç±»å', |
| | | render: (val) => |
| | | h(DictTag, { type: DICT_TYPE.MES_PRO_FEEDBACK_TYPE, value: val }), |
| | | }, |
| | | { field: 'workOrderId', label: 'ç产订å', render: (_val, data) => data.workOrderCode }, |
| | | { field: 'taskId', label: 'ç产任å¡', render: (_val, data) => data.taskCode }, |
| | | { field: 'workstationName', label: 'å·¥ä½ç«' }, |
| | | { field: 'itemCode', label: '产åç¼ç ' }, |
| | | { field: 'itemName', label: '产ååç§°' }, |
| | | { field: 'unitMeasureName', label: 'åä½' }, |
| | | { field: 'itemSpecification', label: 'è§æ ¼' }, |
| | | { field: 'reportableQuantity', label: '坿¥å·¥æ°é' }, |
| | | { field: 'feedbackQuantity', label: 'æ¥å·¥æ°é' }, |
| | | { field: 'qualifiedQuantity', label: 'åæ ¼åæ°é' }, |
| | | { field: 'unqualifiedQuantity', label: 'ä¸è¯åæ°é' }, |
| | | { field: 'laborScrapQuantity', label: 'å·¥åºæ°é' }, |
| | | { field: 'materialScrapQuantity', label: 'æåºæ°é' }, |
| | | { field: 'otherScrapQuantity', label: 'å
¶ä»åºå' }, |
| | | { field: 'feedbackUserNickname', label: 'æ¥å·¥äºº' }, |
| | | { field: 'feedbackTime', label: 'æ¥å·¥æ¶é´' }, |
| | | { field: 'approveUserNickname', label: 'å®¡æ ¸äºº' }, |
| | | { field: 'remark', label: '夿³¨' }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesProFeedbackApi } from '#/api/mes/pro/feedback'; |
| | | |
| | | import { onMounted, ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { useTabs } from '@vben/hooks'; |
| | | |
| | | import { Card, Tabs } from 'ant-design-vue'; |
| | | |
| | | import { TableAction } from '#/adapter/vxe-table'; |
| | | import { getFeedback } from '#/api/mes/pro/feedback'; |
| | | import { useDescription } from '#/components/description'; |
| | | |
| | | import { useDetailSchema } from '../data'; |
| | | import ItemConsumeList from '../modules/item-consume-list.vue'; |
| | | import ProductProduceList from '../modules/product-produce-list.vue'; |
| | | |
| | | const props = defineProps<{ id: string }>(); |
| | | |
| | | const router = useRouter(); |
| | | const tabs = useTabs(); |
| | | |
| | | const loading = ref(false); |
| | | const feedbackId = ref(0); |
| | | const feedback = ref<MesProFeedbackApi.Feedback>({}); |
| | | const activeTab = ref('itemConsume'); |
| | | |
| | | const [Descriptions] = useDescription({ |
| | | bordered: false, |
| | | column: 4, |
| | | schema: useDetailSchema(), |
| | | }); |
| | | |
| | | async function loadFeedbackDetail() { |
| | | loading.value = true; |
| | | try { |
| | | feedback.value = await getFeedback(feedbackId.value); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | function handleBack() { |
| | | tabs.closeCurrentTab(); |
| | | router.push({ path: '/mes/pro/feedback' }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | feedbackId.value = Number(props.id); |
| | | loadFeedbackDetail(); |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page |
| | | auto-content-height |
| | | :loading="loading" |
| | | :title="feedback.code ? `ç产æ¥å·¥è¯¦æ
- ${feedback.code}` : 'ç产æ¥å·¥è¯¦æ
'" |
| | | > |
| | | <Card class="min-h-[10%]"> |
| | | <Descriptions :data="feedback" /> |
| | | </Card> |
| | | |
| | | </Page> |
| | | </template> |
| | |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | |
| | | |
| | | const userStore = useUserStore(); |
| | | const currentUserId = userStore.userInfo?.id; // å½åç»å½ç¨æ· IDï¼ç¨äºå®¡æ¹äººæé夿 |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | |
| | | /** 审æ¹ç产æ¥å·¥ */ |
| | | function handleApprove(row: MesProFeedbackApi.Feedback) { |
| | | formModalApi.setData({ formType: 'approve', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleViewProcess(row: MesProFeedbackApi.Feedback) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('该æ¥å·¥å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ path: '/bpm/process-instance/detail', query: { id: row.processInstanceId } }); |
| | | } |
| | | |
| | | /** 详æ
ç产æ¥å·¥ */ |
| | |
| | | ifShow: () => row.status === MesProFeedbackStatusEnum.PREPARE, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | // { |
| | | // label: $t('common.approve'), |
| | | // type: 'link', |
| | | // auth: ['mes:pro-feedback:approve'], |
| | | // ifShow: () => |
| | | // row.status === MesProFeedbackStatusEnum.APPROVING && |
| | | // row.approveUserId === currentUserId, |
| | | // onClick: handleApprove.bind(null, row), |
| | | // }, |
| | | { |
| | | label: $t('common.approve'), |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | auth: ['mes:pro-feedback:approve'], |
| | | ifShow: () => |
| | | row.status === MesProFeedbackStatusEnum.APPROVING && |
| | | row.approveUserId === currentUserId, |
| | | onClick: handleApprove.bind(null, row), |
| | | icon: ACTION_ICON.VIEW, |
| | | ifShow: () => row.status === MesProFeedbackStatusEnum.APPROVING, |
| | | onClick: handleViewProcess.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | |
| | | <script lang="ts" setup> |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { MesProFeedbackApi } from '#/api/mes/pro/feedback'; |
| | |
| | | updateFeedback, |
| | | } from '#/api/mes/pro/feedback'; |
| | | import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process'; |
| | | import { getWorkOrder } from '#/api/mes/pro/workorder'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | |
| | | } |
| | | } |
| | | |
| | | const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>(); |
| | | const batchActionType = ref<'submit' | 'approve'>(''); |
| | | |
| | | /** æ§è¡æäº¤ï¼å«æ¹æ¬¡å±æ§ï¼ */ |
| | | async function doSubmit(batchFields?: Record<string, unknown>) { |
| | | if (!formData.value?.id) return; |
| | | modalApi.lock(); |
| | | try { |
| | | await submitFeedback({ id: formData.value.id, ...batchFields }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æ¥å·¥åå·²æäº¤'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤ï¼ä»
å½è¡¨åè¾å¿«ç
§æåæ´æ¶æå
ä¿åï¼åè°ç¨æäº¤æ¥å£ */ |
| | | async function handleSubmit() { |
| | | const { valid } = await formApi.validate(); |
| | |
| | | await updateFeedback(data); |
| | | originalSnapshot.value = JSON.stringify(data); |
| | | } |
| | | await submitFeedback(formData.value!.id!); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æ¥è®¢åå·²æäº¤'); |
| | | |
| | | // æ£æ¥æ¯å¦éè¦å®¡æ¹ï¼è¥éè¦å®¡æ¹ï¼åä¸éè¦å¨æ¤é¶æ®µå¡«åæ¹æ¬¡å±æ§ï¼ç´æ¥æäº¤ |
| | | let feedbackApprove = false; |
| | | if (formData.value?.workOrderId) { |
| | | try { |
| | | const workOrder = await getWorkOrder(formData.value.workOrderId); |
| | | feedbackApprove = workOrder?.feedbackApprove ?? false; |
| | | } catch { |
| | | // ignore |
| | | } |
| | | } |
| | | |
| | | if (feedbackApprove) { |
| | | await doSubmit(); |
| | | return; |
| | | } |
| | | |
| | | let checkResult; |
| | | try { |
| | | checkResult = await getBatchRequirements(formData.value!.id!); |
| | | } catch { |
| | | // ignore |
| | | } |
| | | |
| | | if (checkResult?.needBatchInput) { |
| | | batchActionType.value = 'submit'; |
| | | batchDialogRef.value?.open({ checkResult }); |
| | | } else { |
| | | await doSubmit(); |
| | | } |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>(); |
| | | |
| | | /** æ§è¡å®¡æ¹ï¼å«æ¹æ¬¡å±æ§ï¼ */ |
| | | async function doApprove(batchFields?: Record<string, unknown>) { |
| | |
| | | const checkResult = await getBatchRequirements(formData.value.id); |
| | | if (checkResult?.needBatchInput) { |
| | | // éè¦å¡«åæ¹æ¬¡å±æ§ï¼å¼¹çªæ¶é |
| | | batchActionType.value = 'approve'; |
| | | batchDialogRef.value?.open({ checkResult }); |
| | | } else { |
| | | await doApprove(); |
| | |
| | | |
| | | /** æ¹æ¬¡å¼¹çªç¡®è®¤åè° */ |
| | | function handleBatchConfirm(batchData: Record<string, unknown>) { |
| | | doApprove(batchData); |
| | | if (batchActionType.value === 'submit') { |
| | | doSubmit(batchData); |
| | | } else { |
| | | doApprove(batchData); |
| | | } |
| | | } |
| | | |
| | | /** 审æ¹ä¸éè¿ */ |
| | |
| | | } |
| | | |
| | | /** çäº§ä»»å¡æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useTaskFormSchema(formApi?: VbenFormApi, workOrderQuantity?: Ref<number | undefined>): VbenFormSchema[] { |
| | | export function useTaskFormSchema(formApi?: VbenFormApi, workOrderQuantity?: Ref<number | undefined>, feedbackRole?: Ref<string | undefined>): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'workstationId', |
| | |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©æ¥å·¥äºº', |
| | | api: getSimpleUserListByRoleCode, |
| | | params: { code: 'production_submit_work' }, |
| | | params: { code: feedbackRole?.value || 'production_submit_work' }, |
| | | labelField: 'nickname', |
| | | valueField: 'id', |
| | | mode: 'multiple', |
| | |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createTask, getTask, updateTask } from '#/api/mes/pro/task'; |
| | | import { getWorkOrder } from '#/api/mes/pro/workorder'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useTaskFormSchema } from '../data'; |
| | |
| | | routeId?: number; |
| | | workOrderId?: number; |
| | | }>({}); // æ°å¢ä»»å¡æ¶ç订å / å·¥åºä¸ä¸æ |
| | | const feedbackRole = ref<string>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id |
| | |
| | | context.value = {}; |
| | | return; |
| | | } |
| | | formApi.setState({ schema: useTaskFormSchema(formApi, workOrderQuantity) }); |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ |
| | | colorCode?: string; |
| | | id?: number; |
| | |
| | | routeId?: number; |
| | | workOrderId?: number; |
| | | }>(); |
| | | |
| | | let wid: number | undefined; |
| | | |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getTask(data.id); |
| | | wid = formData.value.workOrderId; |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | return; |
| | | } else { |
| | | context.value = { |
| | | colorCode: data.colorCode, |
| | | itemId: data.itemId, |
| | | processId: data.processId, |
| | | routeId: data.routeId, |
| | | workOrderId: data.workOrderId, |
| | | }; |
| | | wid = data.workOrderId; |
| | | workOrderQuantity.value = data.quantity; |
| | | await formApi.setValues({ |
| | | colorCode: data.colorCode || '#00AEF3', |
| | | duration: 1, |
| | | quantity: data.quantity, |
| | | }); |
| | | } |
| | | context.value = { |
| | | colorCode: data.colorCode, |
| | | itemId: data.itemId, |
| | | processId: data.processId, |
| | | routeId: data.routeId, |
| | | workOrderId: data.workOrderId, |
| | | }; |
| | | workOrderQuantity.value = data.quantity; |
| | | await formApi.setValues({ |
| | | colorCode: data.colorCode || '#00AEF3', |
| | | duration: 1, |
| | | quantity: data.quantity, |
| | | }); |
| | | |
| | | if (wid) { |
| | | try { |
| | | const wo = await getWorkOrder(wid); |
| | | feedbackRole.value = wo.feedbackRole; |
| | | } catch { |
| | | // ignore |
| | | } |
| | | } |
| | | |
| | | formApi.setState({ schema: useTaskFormSchema(formApi, workOrderQuantity, feedbackRole) }); |
| | | }, |
| | | }); |
| | | </script> |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder'; |
| | | import { type MesProWorkOrderApi, getWorkOrderApproveProcessList } from '#/api/mes/pro/workorder'; |
| | | import { getSimpleRoleList } from '#/api/system/role'; |
| | | import type { MesProWorkOrderBomApi } from '#/api/mes/pro/workorder/bom'; |
| | | import type { MesProWorkOrderProcessApi } from '#/api/mes/pro/workorder/process'; |
| | | |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'feedbackRole', |
| | | label: 'æ¥å·¥è§è²', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getSimpleRoleList, |
| | | disabled: headerReadonly, |
| | | labelField: 'name', |
| | | valueField: 'code', |
| | | mode: 'multiple', |
| | | placeholder: 'è¯·éæ©æ¥å·¥è§è²', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'feedbackApprove', |
| | | label: 'æ¯å¦æ¥å·¥å®¡æ¹', |
| | | component: 'Switch', |
| | | componentProps: { |
| | | class: '!w-auto', |
| | | checkedChildren: 'æ¯', |
| | | unCheckedChildren: 'å¦', |
| | | disabled: headerReadonly, |
| | | }, |
| | | defaultValue: false, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'processDefinitionKey', |
| | | label: 'å®¡æ¹æµç¨', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getWorkOrderApproveProcessList, |
| | | disabled: headerReadonly, |
| | | labelField: 'name', |
| | | valueField: 'key', |
| | | placeholder: 'è¯·éæ©å®¡æ¹æµç¨', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['feedbackApprove'], |
| | | show: (values) => !!values.feedbackApprove, |
| | | rules(values) { |
| | | if (values.feedbackApprove) { |
| | | return 'selectRequired'; |
| | | } |
| | | return undefined; |
| | | }, |
| | | }, |
| | | }, |
| | | |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | |
| | | if (!valid || !formData.value?.id) return; |
| | | modalApi.lock(); |
| | | try { |
| | | const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder; |
| | | const data = (await formApi.getValues()) as any; |
| | | if (Array.isArray(data.feedbackRole)) { |
| | | data.feedbackRole = data.feedbackRole.join(','); |
| | | } |
| | | await updateWorkOrder({ ...formData.value, ...data }); |
| | | if (tempProcessList.value.length > 0) { |
| | | const processList = tempProcessList.value.map(item => ({ |
| | |
| | | if (isEditable.value) { |
| | | const current = JSON.stringify(await formApi.getValues()); |
| | | if (current !== originalSnapshot.value) { |
| | | const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder; |
| | | const data = (await formApi.getValues()) as any; |
| | | if (Array.isArray(data.feedbackRole)) { |
| | | data.feedbackRole = data.feedbackRole.join(','); |
| | | } |
| | | await updateWorkOrder({ ...formData.value, ...data }); |
| | | originalSnapshot.value = current; |
| | | } |
| | |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder; |
| | | const data = (await formApi.getValues()) as any; |
| | | if (Array.isArray(data.feedbackRole)) { |
| | | data.feedbackRole = data.feedbackRole.join(','); |
| | | } |
| | | try { |
| | | if (formData.value?.id) { |
| | | // ç¼è¾æ¨¡å¼ |
| | |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getWorkOrder(data.id); |
| | | await formApi.setValues(formData.value); |
| | | const formValues: any = { ...formData.value }; |
| | | if (formValues.feedbackRole && typeof formValues.feedbackRole === 'string') { |
| | | formValues.feedbackRole = formValues.feedbackRole.split(','); |
| | | } |
| | | await formApi.setValues(formValues); |
| | | currentProductId.value = formData.value.productId; |
| | | } finally { |
| | | modalApi.unlock(); |
| | |
| | | import type { MesProFeedbackApi } from '#/api/mes/pro/feedback'; |
| | | |
| | | import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { |
| | | DICT_TYPE, |
| | | MesProFeedbackStatusEnum, |
| | | } from '@vben/constants'; |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { useAccess } from '@vben/access'; |
| | | |
| | | import { Button, message, Popconfirm, Space } from 'ant-design-vue'; |
| | | |
| | |
| | | connectedComponent: FeedbackForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const { hasAccessByCodes } = useAccess(); |
| | | const router = useRouter(); |
| | | |
| | | const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>(); |
| | | const approvingRow = ref<MesProFeedbackApi.Feedback>(); |
| | |
| | | |
| | | /** æäº¤ï¼ä»
èç¨¿ï¼ */ |
| | | async function handleSubmit(row: MesProFeedbackApi.Feedback) { |
| | | await submitFeedback(row.id!); |
| | | await submitFeedback({ id: row.id! }); |
| | | message.success('æ¥å·¥åå·²æäº¤'); |
| | | refreshGrid(); |
| | | emit('reported'); |
| | |
| | | message.success('æ¥å·¥å已驳å'); |
| | | refreshGrid(); |
| | | emit('reported'); |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleViewProcess(row: MesProFeedbackApi.Feedback) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('该æ¥å·¥å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ path: '/bpm/process-instance/detail', query: { id: row.processInstanceId } }); |
| | | } |
| | | |
| | | /** ç¶æå¸¸é便¨¡æ¿ä½¿ç¨ */ |
| | |
| | | å é¤ |
| | | </Button> |
| | | </Popconfirm> |
| | | <!-- 审æ¹ä¸ï¼å®¡æ¹éè¿ã驳å --> |
| | | <Popconfirm |
| | | <!-- 审æ¹ä¸ï¼æ¥çå®¡æ¹ --> |
| | | <Button |
| | | v-if="row.status === STATUS_APPROVING" |
| | | type="link" |
| | | size="small" |
| | | @click="handleViewProcess(row)" |
| | | > |
| | | æ¥çå®¡æ¹ |
| | | </Button> |
| | | <!-- |
| | | <Popconfirm |
| | | v-if="row.status === STATUS_APPROVING && hasAccessByCodes(['mes:pro-feedback:approve'])" |
| | | title="确认审æ¹éè¿è¯¥æ¥å·¥åï¼" |
| | | @confirm="handleApprove(row)" |
| | | > |
| | |
| | | </Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="row.status === STATUS_APPROVING" |
| | | v-if="row.status === STATUS_APPROVING && hasAccessByCodes(['mes:pro-feedback:approve'])" |
| | | title="确认驳å该æ¥å·¥åï¼" |
| | | @confirm="handleReject(row)" |
| | | > |
| | |
| | | 驳å |
| | | </Button> |
| | | </Popconfirm> |
| | | --> |
| | | </Space> |
| | | </template> |
| | | </Grid> |
| | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { |
| | | approveFeedback, |
| | | createFeedback, |
| | | createFeedback, getBatchRequirements, |
| | | getFeedback, |
| | | getFeedbackPage, |
| | | rejectFeedback, |
| | |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../../../pro/feedback/data'; |
| | | |
| | | import BatchInputDialog from '../../../pro/feedback/modules/batch-input-dialog.vue'; |
| | | const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>(); |
| | | const batchActionType = ref<'submit' | 'approve'>(''); |
| | | const pendingSubmitMode = ref<'saveAndSubmit' | 'submit'>('submit'); |
| | | defineOptions({ name: 'WorkbenchFeedbackTab' }); |
| | | |
| | | const props = defineProps<{ |
| | |
| | | itemSpecification: task.itemSpecification, |
| | | unitMeasureName: task.unitMeasureName, |
| | | scheduledQuantity: task.quantity, |
| | | feedbackApprove: task.feedbackApprove, |
| | | checkFlag, |
| | | }); |
| | | |
| | | originalSnapshot.value = JSON.stringify(await getAlignedData()); |
| | | } |
| | | |
| | |
| | | if (props.task) { |
| | | await resetForTask(props.task); |
| | | } |
| | | |
| | | emit('reported'); |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | |
| | | pendingSubmitMode.value = 'saveAndSubmit'; |
| | | |
| | | // 4. è°ç¨æäº¤æ¥å£ |
| | | await submitFeedback(record.id!); |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task.feedbackApprove); |
| | | if (!isDone) return; |
| | | |
| | | feedbackId.value = record.id; |
| | | feedbackStatus.value = record.status; |
| | | |
| | |
| | | await updateFeedback(data); |
| | | originalSnapshot.value = JSON.stringify(data); |
| | | } |
| | | await submitFeedback(feedbackId.value!); |
| | | message.success('æ¥è®¢åå·²æäº¤'); |
| | | |
| | | pendingSubmitMode.value = 'submit'; |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task.feedbackApprove); |
| | | if (!isDone) return; |
| | | |
| | | message.success('æ¥å·¥åå·²æäº¤'); |
| | | // é置表å为æ°å»ºæ¨¡å¼ |
| | | if (props.task) { |
| | | await resetForTask(props.task); |
| | | } |
| | | emit('reported'); |
| | | } |
| | | |
| | | |
| | | /** æäº¤æ¹æ³ */ |
| | | async function handleOnSubmit(id:any,feedbackRate:any): Promise<boolean> { |
| | | if (feedbackRate&&feedbackRate==true){ |
| | | await submitFeedback({ id: feedbackId.value! }); |
| | | return true; |
| | | } |
| | | |
| | | const checkResult = await getBatchRequirements(id); |
| | | if (checkResult?.needBatchInput) { |
| | | batchActionType.value = 'submit'; |
| | | batchDialogRef.value?.open({ checkResult }); |
| | | return false; |
| | | } else { |
| | | await submitFeedback({ id: feedbackId.value! }); |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | /** æ¹æ¬¡ç¡®è®¤åè° */ |
| | | async function handleBatchConfirm(batchData: Record<string, unknown>) { |
| | | if (batchActionType.value === 'submit') { |
| | | await submitFeedback({ id: feedbackId.value!, ...batchData }); |
| | | |
| | | if (pendingSubmitMode.value === 'saveAndSubmit') { |
| | | const data = await getAlignedData(); |
| | | const code = data.code as string; |
| | | Modal.success({ |
| | | title: 'ä¿åå¹¶æäº¤æå', |
| | | content: `æ¥å·¥å ${code} å·²æäº¤`, |
| | | centered: true, |
| | | }); |
| | | } else { |
| | | message.success('æ¥å·¥åå·²æäº¤'); |
| | | } |
| | | |
| | | if (props.task) { |
| | | await resetForTask(props.task); |
| | | } |
| | | emit('reported'); |
| | | } |
| | | } |
| | | |
| | | /** 审æ¹éè¿ */ |
| | |
| | | |
| | | <template> |
| | | <div class="feedback-tab"> |
| | | <BatchInputDialog ref="batchDialogRef" @confirm="handleBatchConfirm" /> |
| | | <div v-if="!task" class="feedback-tab__empty"> |
| | | 请ä»å·¦ä¾§å·¥ååè¡¨éæ©ä¸æ¡ä»»å¡ |
| | | </div> |
| | |
| | | } from '#/api/mes/pro/workrecord'; |
| | | import { getPendingFeedbackPage } from '#/api/mes/pro/task'; |
| | | import { router } from '#/router'; |
| | | import { useAccess } from '@vben/access'; |
| | | |
| | | import TaskListPanel from './components/TaskListPanel.vue'; |
| | | import WorkstationPanel from './components/WorkstationPanel.vue'; |
| | |
| | | // --- Tab é
ç½® --- |
| | | const tabItems: WorkbenchTabItem[] = [ |
| | | { key: 'detail', label: 'å·¥å详æ
', icon: 'lucide:file-text' }, |
| | | { key: 'feedback', label: 'æ¥å·¥', icon: 'lucide:edit-3' }, |
| | | { key: 'history', label: 'æ¥å·¥è®°å½', icon: 'lucide:history' }, |
| | | { key: 'feedback', label: 'æ¥å·¥', icon: 'lucide:edit-3', auth: ['mes:pro-feedback:create'] }, |
| | | { key: 'history', label: 'æ¥å·¥è®°å½', icon: 'lucide:history' ,auth: ['mes:pro-feedback:query']}, |
| | | { key: 'record-param', label: 'ç产记å½åæ°', icon: 'lucide:file-cog' }, |
| | | { key: 'product-issue', label: '颿', icon: 'lucide:clipboard-list' }, |
| | | { key: 'return-issue', label: 'éæ', icon: 'lucide:rotate-ccw' }, |
| | | ]; |
| | | |
| | | const { hasAccessByCodes } = useAccess(); |
| | | |
| | | const availableTabs = computed(() => { |
| | | return tabItems.filter((tab) => { |
| | | if (!tab.auth || tab.auth.length === 0) return true; |
| | | return hasAccessByCodes(tab.auth); |
| | | }); |
| | | }); |
| | | |
| | | const activeTab = ref<WorkbenchTabKey>('detail'); |
| | | const activeTabLabel = computed( |
| | | () => tabItems.find((t) => t.key === activeTab.value)?.label ?? '', |
| | | () => availableTabs.value.find((t) => t.key === activeTab.value)?.label ?? '', |
| | | ); |
| | | |
| | | // å½å¯ç¨ tab ååæ¶ï¼å¦æå½åéä¸ç tab ä¸å¯ç¨ï¼åèªå¨éæ©ç¬¬ä¸ä¸ªå¯ç¨ç tab |
| | | watch( |
| | | availableTabs, |
| | | (tabs) => { |
| | | if (tabs.length > 0 && !tabs.find((t) => t.key === activeTab.value)) { |
| | | activeTab.value = tabs[0]!.key; |
| | | } |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | |
| | | // --- å·¥ä½ç« --- |
| | |
| | | |
| | | <nav class="workbench-nav"> |
| | | <button |
| | | v-for="tab in tabItems" |
| | | v-for="tab in availableTabs" |
| | | :key="tab.key" |
| | | type="button" |
| | | class="workbench-nav__item" |
| | |
| | | key: WorkbenchTabKey; |
| | | label: string; |
| | | icon: string; |
| | | auth?: string[]; |
| | | } |
| | | |
| | | /** å¾
æ¥å·¥ä»»å¡ï¼ä» API ç±»åé导åºï¼ä¾¿äºç»ä»¶å¼ç¨ï¼ */ |