Merge branch 'dev_NEW_pro' into dev_天津_宝东
# Conflicts:
# multiple/config.json
# src/api/productionManagement/productionOrder.js
# src/api/salesManagement/deliveryLedger.js
# src/views/basicData/customerFile/index.vue
# src/views/basicData/customerFileOpenSea/index.vue
# src/views/basicData/product/index.vue
# src/views/collaborativeApproval/approvalManagement/index.vue
# src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
# src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
# src/views/collaborativeApproval/approvalProcess/index.vue
# src/views/collaborativeApproval/purchaseApproval/index.vue
# src/views/customerService/feedbackRegistration/components/formDia.vue
# src/views/equipmentManagement/upkeep/Form/formDia.vue
# src/views/equipmentManagement/upkeep/index.vue
# src/views/inventoryManagement/dispatchLog/Record.vue
# src/views/inventoryManagement/stockManagement/Qualified.vue
# src/views/inventoryManagement/stockManagement/Record.vue
# src/views/inventoryManagement/stockReport/index.vue
# src/views/procurementManagement/procurementLedger/index.vue
# src/views/procurementManagement/purchaseReturnOrder/index.vue
# src/views/productionManagement/productStructure/index.vue
# src/views/productionManagement/productionCosting/index.vue
# src/views/productionManagement/productionOrder/New.vue
# src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
# src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
# src/views/productionManagement/productionOrder/index.vue
# src/views/qualityManagement/processInspection/components/formDia.vue
# src/views/salesManagement/deliveryLedger/index.vue
# src/views/salesManagement/returnOrder/components/detailDia.vue
# src/views/salesManagement/salesLedger/index.vue
# src/views/salesManagement/salesQuotation/index.vue
已添加83个文件
已修改190个文件
已删除9个文件
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # æ¬å°æä»¶ä¸ä¼ README |
| | | |
| | | æ¬ææ¡£åºäºä»¥ä¸å®ç°æ´çï¼ |
| | | |
| | | - `src/components/AttachmentUpload/file/index.vue` |
| | | - `src/components/AttachmentUpload/image/index.vue` |
| | | - `src/components/AttachmentPreview/image/index.vue` |
| | | - `src/components/Dialog/FileList.vue` |
| | | - `src/api/basicData/common.js` |
| | | - `src/api/publicApi/commonFile.js` |
| | | |
| | | ç¸å
³ç»ä»¶å·²å¨ `src/main.js` 䏿³¨å为å
¨å±ç»ä»¶ï¼å¯ç´æ¥å¨é¡µé¢ä¸ä½¿ç¨ï¼ |
| | | |
| | | - `FileUpload` |
| | | - `ImageUpload` |
| | | - `ImagePreview` |
| | | - `FileListDialog` |
| | | |
| | | ## 1. åè½æ¦è§ |
| | | |
| | | å½åè¿å¥ä¸ä¼ è½å主è¦å为 4 é¨åï¼ |
| | | |
| | | 1. `FileUpload`ï¼æ®éæä»¶ä¸ä¼ ï¼æ¯æææ½ãæ¹éä¸ä¼ ãé¢è§ãå é¤ |
| | | 2. `ImageUpload`ï¼å¾çä¸ä¼ ï¼æ¯æå¾çå¢å±ç¤ºãé¢è§ãå é¤ |
| | | 3. `ImagePreview`ï¼å¾çå表é¢è§å±ç¤º |
| | | 4. `FileListDialog`ï¼ä¸å¡éä»¶å¼¹çªï¼æ¯ææ¥è¯¢ãä¸ä¼ ãå é¤ãä¸è½½ |
| | | |
| | | ä¸ä¼ åºå±ç»ä¸èµ°æ¥å£ï¼ |
| | | |
| | | - `POST /common/upload` |
| | | |
| | | å¯¹åºæ¹æ³å¨ `src/api/basicData/common.js`ï¼ |
| | | |
| | | ```js |
| | | uploadFile(data) |
| | | ``` |
| | | |
| | | ## 2. ä¸ä¼ æ¥å£è¯´æ |
| | | |
| | | ### 2.1 éç¨ä¸ä¼ æ¥å£ |
| | | |
| | | æä»¶ä¸ä¼ ç»ä»¶åå¾çä¸ä¼ ç»ä»¶é½è°ç¨äºï¼ |
| | | |
| | | ```js |
| | | import { uploadFile } from '@/api/basicData/common' |
| | | ``` |
| | | |
| | | æ¥å£ç¹å¾ï¼ |
| | | |
| | | - è¯·æ±æ¹å¼ï¼`POST` |
| | | - å°åï¼`/common/upload` |
| | | - 请æ±ç±»åï¼`multipart/form-data` |
| | | - æ¯æ `FormData` æ¹éä¸ä¼ |
| | | - é»è®¤å段åï¼`files` |
| | | |
| | | ç»ä»¶å
é¨ä¼è¿æ ·ç»è£
åæ°ï¼ |
| | | |
| | | ```js |
| | | const formData = new FormData() |
| | | validFiles.forEach((file) => { |
| | | formData.append(props.uploadFieldName, file.raw) |
| | | }) |
| | | ``` |
| | | |
| | | ### 2.2 ä¸ä¼ è¿åå¼è¦æ± |
| | | |
| | | ä¸ä¼ æååï¼ç»ä»¶ä¼å°è¯ä»ä»¥ä¸ç»æä¸æåæ°ç»ï¼ |
| | | |
| | | - `response` |
| | | - `response.data` |
| | | - `response.data.data` |
| | | - `response.payload` |
| | | - `response.payload.data` |
| | | - `response.rows` |
| | | - `response.result` |
| | | |
| | | å æ¤å端è¿åæ°ç»æ¶ï¼ä¸é¢ä»»æä¸ç§ç»æé½å¯ä»¥è¢«è¯å«ã |
| | | |
| | | ç»ä»¶å±ç¤ºæ¶å¸¸ç¨å°çåæ®µæï¼ |
| | | |
| | | - æä»¶åï¼`name` / `originalFilename` / `fileName` / `uidFilename` |
| | | - æä»¶å°åï¼`url` / `downloadURL` |
| | | - å¾çå°åï¼`url` / `previewURL` / `previewUrl` |
| | | - 主é®ï¼`id` |
| | | |
| | | 建议ä¸ä¼ æ¥å£è¿åçå项对象尽éå
å«ï¼ |
| | | |
| | | ```js |
| | | { |
| | | id: 1, |
| | | originalFilename: 'demo.pdf', |
| | | downloadURL: 'https://xxx/demo.pdf', |
| | | previewURL: 'https://xxx/demo.png' |
| | | } |
| | | ``` |
| | | |
| | | ## 3. FileUpload æä»¶ä¸ä¼ ç»ä»¶ |
| | | |
| | | ç»ä»¶è·¯å¾ï¼ |
| | | |
| | | `src/components/AttachmentUpload/file/index.vue` |
| | | |
| | | ### 3.1 åºç¡ç¨æ³ |
| | | |
| | | ```vue |
| | | <template> |
| | | <FileUpload v-model:file-list="fileList" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | |
| | | const fileList = ref([]) |
| | | </script> |
| | | ``` |
| | | |
| | | ### 3.2 常ç¨å±æ§ |
| | | |
| | | | 屿§ | 说æ | ç±»å | é»è®¤å¼ | |
| | | | --- | --- | --- | --- | |
| | | | `fileList` | ç»å®æä»¶å表 | `Array` | `[]` | |
| | | | `limit` | æå¤§ä¸ä¼ æ°é | `Number` | `10` | |
| | | | `fileSize` | å个æä»¶å¤§å°éå¶ï¼åä½ MB | `Number` | `50` | |
| | | | `fileType` | å
许ä¸ä¼ çæä»¶ç±»åï¼å¦ `['pdf', 'docx']` | `Array` | `[]` | |
| | | | `buttonText` | ä¸ä¼ æç¤ºææ¡ | `String` | `åå»éæ©æä»¶` | |
| | | | `disabled` | æ¯å¦ç¦ç¨ | `Boolean` | `false` | |
| | | | `uploadFieldName` | `FormData` åæ®µå | `String` | `files` | |
| | | | `index` | è¡¨æ ¼/åè¡¨è¡æ¨¡å¼ä¸çå½åè¡ç´¢å¼ | `Number` | `-1` | |
| | | | `childrenKey` | è¡å
æè½½å段å | `String` | `files` | |
| | | |
| | | ### 3.3 äºä»¶ |
| | | |
| | | | äºä»¶ | 说æ | |
| | | | --- | --- | |
| | | | `update:fileList` | æä»¶å表ååæ¶è§¦å | |
| | | | `change` | æä»¶å表ååæ¶è§¦åï¼è¿åææ°å表 | |
| | | |
| | | ### 3.4 éå¶è§å |
| | | |
| | | ç»ä»¶å
å·²å®ç°ï¼ |
| | | |
| | | - æä»¶æ°ééå¶ |
| | | - æä»¶å¤§å°éå¶ |
| | | - æä»¶ç±»åæ ¡éª |
| | | - ä¸ä¼ ä¸ç¶æéå® |
| | | - 失败åèªå¨æ¸
空å½åéæ©éå |
| | | |
| | | ä¾å¦éå¶ PDF/Wordï¼ |
| | | |
| | | ```vue |
| | | <FileUpload |
| | | v-model:file-list="fileList" |
| | | :limit="5" |
| | | :file-size="20" |
| | | :file-type="['pdf', 'doc', 'docx']" |
| | | /> |
| | | ``` |
| | | |
| | | ### 3.5 è¿åæ°æ®æ ¼å¼å»ºè®® |
| | | |
| | | `FileUpload` æ´é忥æ¶è¿æ ·çåè¡¨ï¼ |
| | | |
| | | ```js |
| | | [ |
| | | { |
| | | id: 1, |
| | | originalFilename: 'åå.pdf', |
| | | downloadURL: 'https://xxx/contract.pdf' |
| | | } |
| | | ] |
| | | ``` |
| | | |
| | | å 为ç»ä»¶æå¼æä»¶æ¶ä¼ä¼å
读åï¼ |
| | | |
| | | ```js |
| | | url || downloadURL || previewURL || previewUrl |
| | | ``` |
| | | |
| | | ### 3.6 è¡å
åµå¥æ¨¡å¼ |
| | | |
| | | 妿ä¸ä¼ ç»ä»¶æ¾å¨è¡¨æ ¼æä¸è¡ä¸ï¼å¯é
å `index` å `childrenKey` 使ç¨ï¼ |
| | | |
| | | ```vue |
| | | <FileUpload |
| | | v-model:file-list="tableData" |
| | | :index="scope.$index" |
| | | children-key="files" |
| | | /> |
| | | ``` |
| | | |
| | | æ¤æ¶ç»ä»¶ä¼èªå¨è¯»åï¼ |
| | | |
| | | ```js |
| | | tableData[scope.$index].files |
| | | ``` |
| | | |
| | | ## 4. ImageUpload å¾çä¸ä¼ ç»ä»¶ |
| | | |
| | | ç»ä»¶è·¯å¾ï¼ |
| | | |
| | | `src/components/AttachmentUpload/image/index.vue` |
| | | |
| | | ### 4.1 åºç¡ç¨æ³ |
| | | |
| | | ```vue |
| | | <template> |
| | | <ImageUpload v-model:file-list="imageList" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | |
| | | const imageList = ref([]) |
| | | </script> |
| | | ``` |
| | | |
| | | ### 4.2 é»è®¤è¡ä¸º |
| | | |
| | | å¾çä¸ä¼ ç»ä»¶é»è®¤ï¼ |
| | | |
| | | - æå¤ä¸ä¼ `10` å¼ |
| | | - åå¼ ä¸è¶
è¿ `10MB` |
| | | - é»è®¤æ¯ææ ¼å¼ï¼`png / jpg / jpeg / webp` |
| | | - ä½¿ç¨ `picture-card` 飿 ¼å±ç¤º |
| | | - ç¹å»ç¼©ç¥å¾å¯é¢è§å¤§å¾ |
| | | |
| | | ### 4.3 常ç¨ç¤ºä¾ |
| | | |
| | | ```vue |
| | | <ImageUpload |
| | | v-model:file-list="imageList" |
| | | :limit="9" |
| | | :file-size="5" |
| | | :file-type="['png', 'jpg', 'jpeg']" |
| | | button-text="ä¸ä¼ å¾ç" |
| | | /> |
| | | ``` |
| | | |
| | | ### 4.4 è¿åæ°æ®æ ¼å¼å»ºè®® |
| | | |
| | | `ImageUpload` å±ç¤ºå¾çæ¶ä¼å
读åï¼ |
| | | |
| | | ```js |
| | | url || previewURL || previewUrl |
| | | ``` |
| | | |
| | | 建议å端è¿åï¼ |
| | | |
| | | ```js |
| | | [ |
| | | { |
| | | id: 1, |
| | | originalFilename: 'ç°åºå¾ç.jpg', |
| | | previewURL: 'https://xxx/image.jpg' |
| | | } |
| | | ] |
| | | ``` |
| | | |
| | | ### 4.5 è¡å
åµå¥æ¨¡å¼ |
| | | |
| | | å¾çç»ä»¶åæ ·æ¯æè¡å
åæ®µååï¼ |
| | | |
| | | ```vue |
| | | <ImageUpload |
| | | v-model:file-list="tableData" |
| | | :index="scope.$index" |
| | | children-key="images" |
| | | /> |
| | | ``` |
| | | |
| | | é»è®¤åååæ®µä¸º `images`ã |
| | | |
| | | ## 5. ImagePreview å¾çé¢è§ç»ä»¶ |
| | | |
| | | ç»ä»¶è·¯å¾ï¼ |
| | | |
| | | `src/components/AttachmentPreview/image/index.vue` |
| | | |
| | | ### 5.1 åºç¡ç¨æ³ |
| | | |
| | | ```vue |
| | | <ImagePreview :file-list="imageList" /> |
| | | ``` |
| | | |
| | | ### 5.2 å¯é
置项 |
| | | |
| | | | 屿§ | 说æ | ç±»å | é»è®¤å¼ | |
| | | | --- | --- | --- | --- | |
| | | | `fileList` | å¾çå表 | `Array` | `[]` | |
| | | | `thumbSize` | 缩ç¥å¾å¤§å° | `Number` | `72` | |
| | | | `gap` | 缩ç¥å¾é´è· | `Number` | `10` | |
| | | |
| | | ### 5.3 æ°æ®è¦æ± |
| | | |
| | | ç»ä»¶ä¼è¿æ»¤æ²¡æ `previewURL` ç项ï¼å æ¤å¦æè¦æ£å¸¸æ¾ç¤ºï¼å»ºè®®è³å°å
å«ï¼ |
| | | |
| | | ```js |
| | | [ |
| | | { |
| | | previewURL: 'https://xxx/image.jpg', |
| | | originalFilename: 'å¾ç1.jpg' |
| | | } |
| | | ] |
| | | ``` |
| | | |
| | | 妿å表为空ï¼ç»ä»¶æ¾ç¤ºâææ å¾çâã |
| | | |
| | | ## 6. FileListDialog éä»¶å¼¹çªç»ä»¶ |
| | | |
| | | ç»ä»¶è·¯å¾ï¼ |
| | | |
| | | `src/components/Dialog/FileList.vue` |
| | | |
| | | è¿ä¸ªç»ä»¶éåä¸å¡è¡¨åæè¯¦æ
页éçâé件管çâåºæ¯ï¼è½åå
æ¬ï¼ |
| | | |
| | | - æ ¹æ®ä¸å¡ä¸»é®æ¥è¯¢éä»¶å表 |
| | | - æå¼å¼¹çªæ¥çéä»¶ |
| | | - å¨å¼¹çªä¸ç»§ç»ä¸ä¼ éä»¶ |
| | | - å é¤éä»¶ |
| | | - ä¸è½½éä»¶ |
| | | |
| | | ### 6.1 ç»ä»¶å±æ§ |
| | | |
| | | | 屿§ | 说æ | ç±»å | é»è®¤å¼ | |
| | | | --- | --- | --- | --- | |
| | | | `visible` | æ¯å¦æ¾ç¤ºå¼¹çª | `Boolean` | å¿
ä¼ | |
| | | | `recordType` | ä¸å¡ç±»å | `String` | `''` | |
| | | | `recordId` | ä¸å¡ä¸»é® | `Number` | `0` | |
| | | | `title` | å¼¹çªæ é¢ | `String` | `éä»¶` | |
| | | | `width` | å¼¹çªå®½åº¦ | `String` | `50%` | |
| | | | `showActions` | æ¯å¦æ¾ç¤ºä¸è½½/å 餿ä½å | `Boolean` | `true` | |
| | | |
| | | ### 6.2 åºç¡ç¨æ³ |
| | | |
| | | ```vue |
| | | <template> |
| | | <el-button @click="visible = true">æ¥çéä»¶</el-button> |
| | | |
| | | <FileListDialog |
| | | v-model:visible="visible" |
| | | record-type="salesLedger" |
| | | :record-id="rowId" |
| | | title="éä»¶å表" |
| | | /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | |
| | | const visible = ref(false) |
| | | const rowId = ref(1001) |
| | | </script> |
| | | ``` |
| | | |
| | | ### 6.3 ç»ä»¶å
é¨ä¾èµçæ¥å£ |
| | | |
| | | `FileListDialog` æ¬èº«ä¸ç´æ¥è°ç¨ `commonFile.js`ï¼èæ¯ä¾èµï¼ |
| | | |
| | | - `attachmentList` |
| | | - `createAttachment` |
| | | - `deleteAttachment` |
| | | |
| | | å¤çé»è¾ä¸ºï¼ |
| | | |
| | | 1. æå¼å¼¹çªåæ ¹æ® `recordType + recordId` æ¥è¯¢éä»¶ |
| | | 2. ç¹å»âä¸ä¼ éä»¶âåï¼å
é¨ä½¿ç¨ `AttachmentUpload/file` å
ä¸ä¼ å° `/common/upload` |
| | | 3. ä¸ä¼ æååï¼å°è¿åçæä»¶å¯¹è±¡åå·²æå表ä¸èµ·æäº¤ç» `createAttachment` |
| | | 4. å 餿¶è°ç¨ `deleteAttachment` |
| | | 5. ä¸è½½æ¶ç´æ¥ `window.open(downloadURL, '_blank')` |
| | | |
| | | å æ¤è¿éè¦ç¹å«æ³¨æï¼ |
| | | |
| | | - `recordType` å `recordId` å¿
é¡»æ¯ææä¸å¡æ è¯ |
| | | - ä¸ä¼ æåè¿åçæ°æ®ï¼éè¦è½è¢« `createAttachment` ç´æ¥æ¥æ¶ |
| | | - å表ä¸çä¸è½½å°ååæ®µåºä¸º `downloadURL` |
| | | |
| | | ## 7. commonFile.js 说æ |
| | | |
| | | æä»¶è·¯å¾ï¼ |
| | | |
| | | `src/api/publicApi/commonFile.js` |
| | | |
| | | å½åæä»¶æä¾çæ¯å
Œ
±æä»¶å 餿¥å£ï¼ |
| | | |
| | | ```js |
| | | delCommonFile(ids) |
| | | delCommonFileInvoiceLedger(ids) |
| | | ``` |
| | | |
| | | å¯¹åºæ¥å£ï¼ |
| | | |
| | | - `/commonFile/delCommonFile` |
| | | - `/invoiceLedger/delFile` |
| | | |
| | | è¿ä¸¤ä¸ªæ¹æ³æ´éåå·²ç»åå
·ä½ä¸å¡ç»å®åçâå é¤å·²ä¿åéä»¶âåºæ¯ï¼ä¸è´è´£ä¸ä¼ æä»¶æ¬èº«ã |
| | | |
| | | 示ä¾ï¼ |
| | | |
| | | ```js |
| | | import { delCommonFile } from '@/api/publicApi/commonFile' |
| | | |
| | | await delCommonFile([1, 2, 3]) |
| | | ``` |
| | | |
| | | ## 8. æ¨èä½¿ç¨æ¹å¼ |
| | | |
| | | ### 8.1 æ®éä¸å¡è¡¨åä¸ä¼ éä»¶ |
| | | |
| | | ```vue |
| | | <FileUpload v-model:file-list="form.storageBlobDTOs" /> |
| | | ``` |
| | | |
| | | æäº¤è¡¨åæ¶ç´æ¥å¸¦ä¸ï¼ |
| | | |
| | | ```js |
| | | { |
| | | ...form, |
| | | storageBlobDTOs: form.storageBlobDTOs |
| | | } |
| | | ``` |
| | | |
| | | ### 8.2 å¾çç±»ä¸å¡ |
| | | |
| | | ```vue |
| | | <ImageUpload v-model:file-list="form.images" /> |
| | | <ImagePreview :file-list="form.images" /> |
| | | ``` |
| | | |
| | | ### 8.3 å·²è½åºé件管ç |
| | | |
| | | ```vue |
| | | <FileListDialog |
| | | v-model:visible="dialogVisible" |
| | | :record-type="recordType" |
| | | :record-id="recordId" |
| | | /> |
| | | ``` |
| | | |
| | | éå详æ
页ã审æ¹é¡µãå°è´¦é¡µè¿ç±»âæ¥çå¹¶ç»´æ¤å½åä¸å¡éä»¶âçåºæ¯ã |
| | | |
| | | ## 9. 注æäºé¡¹ |
| | | |
| | | 1. `FileUpload` å `ImageUpload` åªæ¯è´è´£ææä»¶å
ä¼ å° `/common/upload`ï¼ä¸çäºå·²ç»åä¸å¡æ°æ®ç»å®ã |
| | | 2. 妿ä¸å¡éè¦æä¹
åéä»¶å
³ç³»ï¼ä»éè¦å¨ä¿åè¡¨åæ¶æè¿åçæä»¶å¯¹è±¡æäº¤ç»ä¸å¡æ¥å£ã |
| | | 3. `ImagePreview` å½ååªè¯å« `previewURL`ï¼å¦æå端åªè¿å `url`ï¼é¢è§ç»ä»¶å°ä¸ä¼å±ç¤ºï¼æå¥½ç»ä¸è¡¥é½ `previewURL`ã |
| | | 4. `FileListDialog` ä¾èµ `recordType`ã`recordId` æ¥è¯¢åä¿åéä»¶å
³ç³»ï¼æ°å¢ä¸å¡æ¶è¦å
确认å端å
³èæ¥å£å¯ç¨ã |
| | | 5. å 餿¬å°å表项åå é¤å·²ä¿åéä»¶æ¯ä¸¤ä»¶äºï¼ |
| | | - ä¸ä¼ ç»ä»¶éçå é¤ï¼åªä¼ä»å½åå端ç»å®æ°ç»ä¸ç§»é¤ |
| | | - `commonFile.js` / `deleteAttachment`ï¼ææ¯çæ£è°ç¨å端å é¤ |
| | | |
| | | ## 10. ä¸å¥æå¸¸è§ç页é¢åæ³ |
| | | |
| | | ```vue |
| | | <template> |
| | | <el-form :model="form"> |
| | | <el-form-item label="éä»¶"> |
| | | <FileUpload v-model:file-list="form.storageBlobDTOs" :limit="5" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="ç°åºå¾ç"> |
| | | <ImageUpload v-model:file-list="form.images" :limit="9" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="å¾çé¢è§"> |
| | | <ImagePreview :file-list="form.images" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | |
| | | const form = ref({ |
| | | storageBlobDTOs: [], |
| | | images: [], |
| | | }) |
| | | </script> |
| | | ``` |
| | | |
| | | å¦æä½ çç®æ æ¯âå
ä¸ä¼ ï¼åè·ä¸å¡ä¸èµ·ä¿åâï¼è¿å¥åæ³å¯ä»¥ç´æ¥ä½ä¸ºåºç¡æ¨¡æ¿ä½¿ç¨ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # è´¢å¡ç®¡çåç«¯ææ¡£ï¼ä»
è´è´£æ¨¡åï¼ |
| | | |
| | | æ´æ°æ¶é´ï¼2026-05-12 |
| | | éç¨èå´ï¼ä»
ä»¥ä¸ 6 个模åï¼ï¼ |
| | | 1. åºå®èµäº§ï¼`/financial/fixed-assets`ï¼ |
| | | 2. æ å½¢èµäº§ï¼`/financial/intangible-assets`ï¼ |
| | | 3. æ»è´¦ç§ç®ï¼`/financial/general-ledger`ï¼ |
| | | 4. åè¯ï¼`/financial/voucher`ï¼ |
| | | 5. ç§ç®æ»è´¦ï¼`/financial/voucher-general-ledger`ï¼ |
| | | 6. ç§ç®æç»è´¦ï¼`/financial/voucher-detail-ledger`ï¼ |
| | | |
| | | --- |
| | | |
| | | ## 1. ç»ä¸çº¦å® |
| | | |
| | | ### 1.1 ååºç»æ |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "success", |
| | | "data": {} |
| | | } |
| | | ``` |
| | | |
| | | ### 1.2 åé¡µç»æï¼å¦ææ¯å页æ¥å£ï¼ |
| | | 请æ±åæ°å»ºè®®ï¼ |
| | | - `current`ï¼é¡µç ï¼ |
| | | - `size`ï¼æ¯é¡µæ¡æ°ï¼ |
| | | |
| | | ååºå»ºè®®ï¼ |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "data": { |
| | | "records": [], |
| | | "total": 0 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 1.3 éé¢ä¸ç²¾åº¦ |
| | | - éé¢å段建议 `decimal(18,2)`ã |
| | | - åå端ç»ä¸ä¿ç两ä½å°æ°ã |
| | | |
| | | --- |
| | | |
| | | ## 2. 模åä¸ï¼æ»è´¦ç§ç®ï¼å·²æ¥çå® APIï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/generalLedger/index.vue` |
| | | API æä»¶ï¼`src/api/financialManagement/accountSubject.js` |
| | | |
| | | ### 2.1 æ¥å£ç°ç¶ |
| | | - `GET /accountSubject/list` |
| | | - `POST /accountSubject/add` |
| | | - `PUT /accountSubject/edit` |
| | | - `DELETE /accountSubject/remove/{ids}` |
| | | - `POST /accountSubject/export` |
| | | |
| | | ### 2.2 åæ®µæ¨¡å |
| | | - `id` |
| | | - `subjectCode`ï¼ç§ç®ç¼ç ï¼ |
| | | - `subjectName`ï¼ç§ç®åç§°ï¼ |
| | | - `subjectType`ï¼ç§ç®ç±»åï¼ |
| | | - `balanceDirection`ï¼ä½é¢æ¹åï¼åæ¹/è´·æ¹ï¼ |
| | | - `status`ï¼0 å¯ç¨ï¼1 ç¦ç¨ï¼ |
| | | - `remark` |
| | | |
| | | ### 2.3 ä¸å¡è§å |
| | | - `subjectCode`ã`subjectName`ã`subjectType` å¿
å¡«ã |
| | | - å é¤éè¦åå¼ç¨æ ¡éªï¼è¥å·²è¢«åè¯åå½å¼ç¨ï¼ä¸å
许å é¤ï¼ã |
| | | |
| | | --- |
| | | |
| | | ## 3. 模åäºï¼åºå®èµäº§ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/assets/fixedAssets.vue` |
| | | |
| | | ### 3.1 建议æ¥å£ |
| | | - `GET /financial/fixedAsset/page` |
| | | - `POST /financial/fixedAsset/add` |
| | | - `PUT /financial/fixedAsset/update` |
| | | - `DELETE /financial/fixedAsset/delete` |
| | | - `POST /financial/fixedAsset/depreciate`ï¼ææè®¡æï¼ |
| | | |
| | | ### 3.2 åæ®µæ¨¡å |
| | | - `id, assetCode, assetName, category, specification` |
| | | - `purchaseDate, originalValue, usefulLife, residualRate` |
| | | - `accumulatedDepreciation, netValue` |
| | | - `location, department, keeper, status, remark` |
| | | |
| | | ### 3.3 æ ¸å¿å
¬å¼ï¼å¿
é¡»ä¸è´ï¼ |
| | | - `monthlyDepreciation = originalValue * (1 - residualRate/100) / (usefulLife*12)` |
| | | - `accumulatedDepreciation += monthlyDepreciation` |
| | | - `netValue = originalValue - accumulatedDepreciation` |
| | | |
| | | ### 3.4 ç¶æå»ºè®® |
| | | - `in_use`ï¼å¨ç¨ï¼ |
| | | - `idle`ï¼é²ç½®ï¼ |
| | | - `repair`ï¼ç»´ä¿®ä¸ï¼ |
| | | - `scrapped`ï¼æ¥åºï¼ |
| | | |
| | | --- |
| | | |
| | | ## 4. 模åä¸ï¼æ å½¢èµäº§ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/assets/intangibleAssets.vue` |
| | | |
| | | ### 4.1 建议æ¥å£ |
| | | - `GET /financial/intangibleAsset/page` |
| | | - `POST /financial/intangibleAsset/add` |
| | | - `PUT /financial/intangibleAsset/update` |
| | | - `DELETE /financial/intangibleAsset/delete` |
| | | - `POST /financial/intangibleAsset/amortize`ï¼æææéï¼ |
| | | |
| | | ### 4.2 åæ®µæ¨¡å |
| | | - `id, assetCode, assetName, category, certificateNo` |
| | | - `acquisitionDate, originalValue, amortizationPeriod, residualRate` |
| | | - `accumulatedAmortization, netValue` |
| | | - `validityDate, status, description, remark` |
| | | |
| | | ### 4.3 æ ¸å¿å
¬å¼ï¼å¿
é¡»ä¸è´ï¼ |
| | | - `monthlyAmortization = originalValue * (1 - residualRate/100) / (amortizationPeriod*12)` |
| | | - `accumulatedAmortization += monthlyAmortization` |
| | | - `netValue = originalValue - accumulatedAmortization` |
| | | - å½ `netValue <= 0`ï¼ |
| | | - `netValue = 0` |
| | | - `status = amortized` |
| | | |
| | | ### 4.4 ç¶æå»ºè®® |
| | | - `in_use`ï¼å¨ç¨ï¼ |
| | | - `expired`ï¼å°æï¼ |
| | | - `amortized`ï¼å·²æéå®ï¼ |
| | | |
| | | --- |
| | | |
| | | ## 5. 模ååï¼åè¯ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/voucher/index.vue` |
| | | |
| | | ### 5.1 建议æ¥å£ |
| | | - `GET /financial/voucher/page` |
| | | - `POST /financial/voucher/add` |
| | | - `PUT /financial/voucher/update` |
| | | - `POST /financial/voucher/post`ï¼è¿è´¦ï¼ |
| | | - `POST /financial/voucher/cancel`ï¼ä½åºï¼ |
| | | - `GET /financial/voucher/detail/{id}` |
| | | |
| | | ### 5.2 ä¸»è¡¨åæ®µ |
| | | - `id, voucherNo, voucherDate, summary` |
| | | - `debit, credit, creator, status, attachmentCount, remark` |
| | | |
| | | ### 5.3 åå½å段 |
| | | - `subjectCode, subjectName, summary, debit, credit` |
| | | |
| | | ### 5.4 å
³é®æ ¡éª |
| | | - åå½è³å°ä¸æ¡ææè¡ï¼ç§ç®ä¸ç©ºï¼ä¸åæ¹æè´·æ¹ > 0ï¼ã |
| | | - å贷平衡ï¼`sum(debit) == sum(credit)` ä¸ > 0ï¼ä¸æ»¡è¶³ç¦æ¢ä¿åã |
| | | |
| | | ### 5.5 ç¶ææµè½¬ |
| | | - `unposted -> posted` |
| | | - `unposted -> cancelled` |
| | | |
| | | --- |
| | | |
| | | ## 6. 模åäºï¼ç§ç®æ»è´¦ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/voucher/generalLedger.vue` |
| | | |
| | | ### 6.1 建议æ¥å£ |
| | | - `GET /financial/ledger/general` |
| | | |
| | | ### 6.2 请æ±åæ° |
| | | - `subjectCode`ï¼æ«çº§ææå®ç§ç®ï¼ |
| | | - `startMonth`ï¼YYYY-MMï¼ |
| | | - `endMonth`ï¼YYYY-MMï¼ |
| | | |
| | | ### 6.3 ååºå段 |
| | | - `date, voucherNo, summary` |
| | | - `debit, credit, direction, balance` |
| | | |
| | | ### 6.4 è§å |
| | | - ä»
å¨éæ©ç§ç®åè¿åæ°æ®ã |
| | | - æ¯æâæåä½é¢ / æ¬æå计 / æ¬å¹´ç´¯è®¡âè¡ï¼å¯éè¿ `rowType` åæ®µåºåï¼ã |
| | | |
| | | --- |
| | | |
| | | ## 7. 模åå
ï¼ç§ç®æç»è´¦ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/voucher/detailLedger.vue` |
| | | |
| | | ### 7.1 建议æ¥å£ |
| | | - `GET /financial/ledger/detail` |
| | | |
| | | ### 7.2 请æ±åæ° |
| | | - `subjectCode` |
| | | - `auxiliaryType`ï¼customer/supplier/department/employee/projectï¼ |
| | | - `auxiliaryId` |
| | | - `startMonth`ï¼YYYY-MMï¼ |
| | | - `endMonth`ï¼YYYY-MMï¼ |
| | | |
| | | ### 7.3 ååºå段 |
| | | - `date, voucherNo, summary` |
| | | - `debit, credit, direction, balance` |
| | | |
| | | ### 7.4 è§å |
| | | - å
éç§ç®ï¼åæ¥æç»ã |
| | | - è¾
婿 ¸ç®æ¡ä»¶ä¸ºå¯éï¼ä½å»ºè®®åç«¯æ¯æç»´åº¦è¿æ»¤ã |
| | | |
| | | --- |
| | | |
| | | ## 8. æ¨èæå°è¡¨è®¾è®¡ï¼ä»
æ¬èå´ï¼ |
| | | |
| | | - `fin_account_subject` |
| | | - `fin_fixed_asset` |
| | | - `fin_intangible_asset` |
| | | - `fin_voucher` |
| | | - `fin_voucher_entry` |
| | | - `fin_ledger_snapshot_general`ï¼å¯éï¼åæ§è½ä¼åï¼ |
| | | - `fin_ledger_snapshot_detail`ï¼å¯éï¼åæ§è½ä¼åï¼ |
| | | |
| | | --- |
| | | |
| | | ## 9. AI çæå端任å¡é¡ºåºï¼å»ºè®®ï¼ |
| | | |
| | | 1. å
宿 **æ»è´¦ç§ç®**ï¼å·²æ APIï¼æç¨³å®ï¼ã |
| | | 2. 宿 **åè¯ + åå½ + åè´·å¹³è¡¡æ ¡éª + ç¶ææµè½¬**ã |
| | | 3. å®ç° **ç§ç®æ»è´¦ / ç§ç®æç»è´¦** æ¥è¯¢ã |
| | | 4. å®ç° **åºå®èµäº§ææ§** ä¸ **æ å½¢èµäº§æé**ã |
| | | 5. è¡¥æµè¯ï¼ |
| | | - åè´·å¹³è¡¡æ ¡éª |
| | | - ææ§/æéå
¬å¼ |
| | | - ç§ç®è¢«å¼ç¨ç¦æ¢å é¤ |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | { |
| | | "compilerOptions": { |
| | | "baseUrl": ".", |
| | | "paths": { |
| | | "@/*": ["src/*"], |
| | | "~/*": ["./*"] |
| | | } |
| | | }, |
| | | "include": ["src/**/*.js", "src/**/*.vue", "vite.config.js"] |
| | | } |
| | |
| | | |
| | | const envFilePath = path.join(process.cwd(), '.env.production.local'); |
| | | |
| | | async function copyFileWithOverwrite(src, dest) { |
| | | await fs.mkdir(path.dirname(dest), { recursive: true }); |
| | | if (fsSync.existsSync(dest)) { |
| | | try { |
| | | await fs.chmod(dest, 0o666); |
| | | } catch { |
| | | // Ignore chmod failure and try delete directly. |
| | | } |
| | | await fs.rm(dest, { force: true }); |
| | | } |
| | | await fs.copyFile(src, dest); |
| | | } |
| | | |
| | | try { |
| | | // 1ï¸â£ çæ .env |
| | | console.log("=======çæ.env======="); |
| | |
| | | const backupFile = path.join(replacePath, config[key]); |
| | | const replaceFile = path.join(resourcePath, companyMap[key]); |
| | | |
| | | await fs.mkdir(path.dirname(backupFile), { recursive: true }); |
| | | await fs.copyFile(originFile, backupFile); |
| | | await fs.copyFile(replaceFile, originFile); |
| | | await copyFileWithOverwrite(originFile, backupFile); |
| | | await copyFileWithOverwrite(replaceFile, originFile); |
| | | } |
| | | |
| | | console.log("=====å¼å§æå
======"); |
| | |
| | | const originFile = path.join(rootPath, config[key]); |
| | | const backupFile = path.join(replacePath, config[key]); |
| | | |
| | | await fs.copyFile(backupFile, originFile); |
| | | await copyFileWithOverwrite(backupFile, originFile); |
| | | } |
| | | await fs.rm(replacePath, { recursive: true, force: true }); |
| | | console.log(`ðï¸ å·²å é¤ ${replacePath}`); |
| | |
| | | }, |
| | | "overrides": { |
| | | "quill": "2.0.2" |
| | | } |
| | | }, |
| | | "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // éç¨ä¸ä¼ æ¥å£ï¼æ¯æ FormData æ¹éä¼ æä»¶ |
| | | export function uploadFile(data) { |
| | | return request({ |
| | | url: '/common/upload', |
| | | method: 'post', |
| | | data, |
| | | headers: { |
| | | 'Content-Type': 'multipart/form-data', |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | // éç¨ä¸ä¼ æ¥å£ï¼æ¯æ FormData æ¹éä¼ æä»¶,æ°¸ä¸è¿æï¼æ
ç¨ |
| | | export function uploadPublicFile(data) { |
| | | return request({ |
| | | url: '/common/public/upload', |
| | | method: 'post', |
| | | data, |
| | | headers: { |
| | | 'Content-Type': 'multipart/form-data', |
| | | }, |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | export function listCustomer(query) { |
| | | return request({ |
| | | url: '/basic/customer/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // åé
å®¢æ· |
| | | export function assignCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/assignCustomer', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | // åæ¶å®¢æ· |
| | | export function recycleCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/recycleCustomer', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | // æµå
¥å
¬æµ· |
| | | export function backCustomer(id) { |
| | | return request({ |
| | | url: '/basic/customer/back/' + id, |
| | | method: 'post' |
| | | }) |
| | | } |
| | | |
| | | export function shareCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/together', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function getCustomer(id) { |
| | | return request({ |
| | | url: '/basic/customer/' + id, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | export function addCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/addCustomer', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function updateCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/updateCustomer', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function exportCustomer(query) { |
| | | return request({ |
| | | url: '/basic/customer/export', |
| | | method: 'get', |
| | | params: query, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | | |
| | | export function delCustomer(ids) { |
| | | return request({ |
| | | url: '/basic/customer/delCustomer', |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | export function listCustomer(query) { |
| | | return request({ |
| | | url: '/basic/customer/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // å®¢æ·æ¡£æ¡ç§æµ·æ¥è¯¢ |
| | | export function listCustomerPrivatePool(query) { |
| | | return request({ |
| | | url: '/customerPrivatePool/listPage', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | export function addCustomerPrivatePool(data) { |
| | | return request({ |
| | | url: '/customerPrivatePool/add', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function addCustomerPrivate(data) { |
| | | return request({ |
| | | url: '/customerPrivate/add', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function delCustomerPrivate(ids) { |
| | | return request({ |
| | | url: '/customerPrivate/delete', |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | | |
| | | export function delCustomerPrivatePool(id) { |
| | | return request({ |
| | | url: '/customerPrivatePool/delete/' + id, |
| | | method: 'delete', |
| | | }) |
| | | } |
| | | |
| | | export function shareCustomer(data) { |
| | | return request({ |
| | | url: '/customerPrivatePool/together', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function getCustomer(id) { |
| | | return request({ |
| | | url: '/basic/customer/' + id, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | export function getCustomerPrivatePoolById(id) { |
| | | return request({ |
| | | url: '/customerPrivatePool/getbyId/' + id, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | export function getCustomerPrivatePoolInfo(id) { |
| | | return request({ |
| | | url: '/customerPrivatePool/info/' + id, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | export function addCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/addCustomer', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function updateCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/updateCustomer', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function updateCustomerPrivatePool(data) { |
| | | return request({ |
| | | url: '/customerPrivatePool/update', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | export function exportCustomer(query) { |
| | | return request({ |
| | | url: '/basic/customer/export', |
| | | method: 'get', |
| | | params: query, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | | |
| | | export function delCustomer(ids) { |
| | | return request({ |
| | | url: '/basic/customer/delCustomer', |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | | |
| | | export function addCustomerFollow(data) { |
| | | return request({ |
| | | url: '/basic/customer-follow/add', |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // åæ°ç»´æ¤é¡µé¢æ¥å£ |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢åæ°å表 |
| | | export function parameterListPage(query) { |
| | | return request({ |
| | | url: "/basic/parameter/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢åæ° |
| | | export function addParameter(data) { |
| | | return request({ |
| | | url: "/basic/parameter/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // ç¼è¾åæ° |
| | | export function updateParameter(data) { |
| | | return request({ |
| | | url: "/basic/parameter/update", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤åæ° |
| | | export function delParameter(ids) { |
| | | return request({ |
| | | url: "/basic/parameter/del", |
| | | method: "delete", |
| | | data: Array.isArray(ids) ? ids : [ids], |
| | | }); |
| | | } |
| | | |
| | | // è·å产åç±»åå表 |
| | | export function getProductTypes() { |
| | | return request({ |
| | | url: "/basic/product/typeList", |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢åºç¡åæ° |
| | | export function addBaseParam(data) { |
| | | return request({ |
| | | url: "/technologyParam/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // ç¼è¾åºç¡åæ° |
| | | export function editBaseParam(data) { |
| | | return request({ |
| | | url: "/technologyParam/edit", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // æ¥è¯¢åºç¡åæ°å表 |
| | | export function getBaseParamList(query) { |
| | | return request({ |
| | | url: "/technologyParam/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // å é¤åºç¡åæ° |
| | | export function removeBaseParam(ids) { |
| | | return request({ |
| | | url: `/technologyParam/remove/` + ids, |
| | | method: "delete", |
| | | }); |
| | | } |
| | |
| | | import request from '@/utils/request' |
| | | import request from "@/utils/request"; |
| | | |
| | | // å·¥åºå表å页æ¥è¯¢ |
| | | export function productProcessListPage(query) { |
| | | return request({ |
| | | url: '/productProcess/listPage', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | url: "/technologyOperation/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // é件页颿¥å£ |
| | | import request from '@/utils/request' |
| | | |
| | | // éä»¶æ¥è¯¢ |
| | | export function attachmentList(query) { |
| | | return request({ |
| | | url: '/storageAttachment/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // éä»¶æ°å¢ |
| | | export function createAttachment(data) { |
| | | return request({ |
| | | url: '/storageAttachment/add', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | // éä»¶å é¤ |
| | | export function deleteAttachment(data) { |
| | | return request({ |
| | | url: '/storageAttachment/delete', |
| | | method: 'delete', |
| | | data |
| | | }) |
| | | } |
| | |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc éªæ¶å®¡æ¹ |
| | | * @param {éªæ¶åæ°} data |
| | | * @returns |
| | | */ |
| | | export const repairAcceptance = (data) => { |
| | | return request({ |
| | | url: `/device/repair/acceptance`, |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | /** éè´å
¥åºå页å表 */ |
| | | export const listPageAccountPurchase = (params) => { |
| | | return request({ |
| | | url: "/accountPurchase/listPageAccountPurchase", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | /** éè´éè´§å页å表 */ |
| | | export const listPageAccountPurchaseReturn = (params) => { |
| | | return request({ |
| | | url: "/accountPurchase/listPageAccountPurchaseReturn", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | /** éå®åºåºå页å表 */ |
| | | export const listPageAccountSales = (params) => { |
| | | return request({ |
| | | url: "/accountSales/listPageAccountSales", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | /** éå®éè´§å页å表 */ |
| | | export const listPageAccountSalesReturn = (params) => { |
| | | return request({ |
| | | url: "/accountSales/listPageAccountSalesReturn", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢æ»å¸ç§ç®å表 |
| | | export function listAccountSubject(query) { |
| | | return request({ |
| | | url: "/accountSubject/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢æ»å¸ç§ç® |
| | | export function addAccountSubject(data) { |
| | | return request({ |
| | | url: "/accountSubject/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹æ»å¸ç§ç® |
| | | export function updateAccountSubject(data) { |
| | | return request({ |
| | | url: "/accountSubject/edit", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // å 餿»å¸ç§ç® |
| | | export function delAccountSubject(ids) { |
| | | return request({ |
| | | url: "/accountSubject/remove/" + ids, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | // å¯¼åºæ»å¸ç§ç® |
| | | export function exportAccountSubject(data) { |
| | | return request({ |
| | | url: "/accountSubject/export", |
| | | method: "post", |
| | | data: data, |
| | | responseType: "blob", |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // åºå®èµäº§å页æ¥è¯¢ï¼current/sizeï¼ |
| | | export function listFixedAssetPage(params) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢åºå®èµäº§ |
| | | export function addFixedAsset(data) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹åºå®èµäº§ |
| | | export function updateFixedAsset(data) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/update", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤åºå®èµäº§ï¼åç«¯è¦æ± ids=1&ids=2 å½¢å¼ï¼ |
| | | export function deleteFixedAsset(ids) { |
| | | const idList = Array.isArray(ids) ? ids : [ids]; |
| | | const query = idList |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | .map(id => `ids=${encodeURIComponent(id)}`) |
| | | .join("&"); |
| | | return request({ |
| | | url: `/financial/fixedAsset/delete?${query}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | // ææ§è®¡æï¼{} 表示å
¨é¨å¨ç¨èµäº§ï¼ |
| | | export function depreciateFixedAsset(data = {}) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/depreciate", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ å½¢èµäº§å页æ¥è¯¢ï¼current/sizeï¼ |
| | | export function listIntangibleAssetPage(params) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢æ å½¢èµäº§ |
| | | export function addIntangibleAsset(data) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹æ å½¢èµäº§ |
| | | export function updateIntangibleAsset(data) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/update", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å 餿 å½¢èµäº§ï¼åç«¯è¦æ± ids=1&ids=2 å½¢å¼ï¼ |
| | | export function deleteIntangibleAsset(ids) { |
| | | const idList = Array.isArray(ids) ? ids : [ids]; |
| | | const query = idList |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | .map(id => `ids=${encodeURIComponent(id)}`) |
| | | .join("&"); |
| | | return request({ |
| | | url: `/financial/intangibleAsset/delete?${query}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | // æé计æï¼{} 表示å
¨é¨å¨ç¨èµäº§ï¼ |
| | | export function amortizeIntangibleAsset(data = {}) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/amortize", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // ç§ç®æ»è´¦ |
| | | export function getGeneralLedger(params) { |
| | | return request({ |
| | | url: "/financial/ledger/general", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // ç§ç®æç»è´¦ |
| | | export function getDetailLedger(params) { |
| | | return request({ |
| | | url: "/financial/ledger/detail", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // åè¯å页æ¥è¯¢ï¼current/size + è¿æ»¤æ¡ä»¶ï¼ |
| | | export function listVoucherPage(params) { |
| | | return request({ |
| | | url: "/financial/voucher/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢åè¯ |
| | | export function addVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹åè¯ï¼ä»
æªè¿è´¦ï¼ |
| | | export function updateVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/update", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // è¿è´¦ |
| | | export function postVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/post", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä½åº |
| | | export function cancelVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/cancel", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // 详æ
|
| | | export function getVoucherDetail(id) { |
| | | return request({ |
| | | url: `/financial/voucher/detail/${id}`, |
| | | method: "get", |
| | | }); |
| | | } |
| | |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | }; |
| | | |
| | | export const batchDeletePendingStockInRecords = (ids) => { |
| | | return request({ |
| | | url: "/stockInRecord/pending", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | }; |
| | | |
| | | // æ¹é审æ¹å
¥åºè®°å½ï¼approvalStatus: approved/rejectedï¼ |
| | | export const batchApproveStockInRecords = (data) => { |
| | | return request({ |
| | | url: "/stockInRecord/approve", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| | |
| | | }); |
| | | }; |
| | | |
| | | export const getStockInventoryBatchNoQty = (params) => { |
| | | return request({ |
| | | url: "/stockInventory/getBatchNoQty", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // å建åºåè®°å½ |
| | | export const createStockInventory = (params) => { |
| | | return request({ |
| | |
| | | export const subtractStockInventory = (params) => { |
| | | return request({ |
| | | url: "/stockInventory/subtractStockInventory", |
| | | method: "post", |
| | | data: params, |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢å
¥åºè®°å½ï¼ä»
å建记å½ï¼ä¸è°æ´åºåï¼ |
| | | export const addStockInRecordOnly = (params) => { |
| | | return request({ |
| | | url: "/stockInventory/addStockInRecordOnly", |
| | | method: "post", |
| | | data: params, |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢åºåºè®°å½ï¼ä»
å建记å½ï¼ä¸è°æ´åºåï¼ |
| | | export const addStockOutRecordOnly = (params) => { |
| | | return request({ |
| | | url: "/stockInventory/addStockOutRecordOnly", |
| | | method: "post", |
| | | data: params, |
| | | }); |
| | |
| | | }); |
| | | }; |
| | | |
| | | export const getStockInventoryByModelId = (productModelId) => { |
| | | return request({ |
| | | url: "/stockInventory/getByModelId", |
| | | method: "get", |
| | | params: { productModelId }, |
| | | }); |
| | | }; |
| | | |
| | |
| | | data: ids, |
| | | }); |
| | | } |
| | | |
| | | //å é¤å¾
审æ¹åºåºä¿¡æ¯ |
| | | export const delPendingStockOut = (ids) => { |
| | | return request({ |
| | | url: "/stockOutRecord/pending", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| | | |
| | | // æ¹é审æ¹åºåºè®°å½ï¼approvalStatus: approved/rejectedï¼ |
| | | export const batchApproveStockOutRecords = (data) => { |
| | | return request({ |
| | | url: "/stockOutRecord/approve", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢å
¥åºè®°å½ï¼ä»
å建记å½ï¼ä¸è°æ´åºåï¼ |
| | | export const addUnqualifiedStockInRecordOnly = (params) => { |
| | | return request({ |
| | | url: "/stockUninventory/addStockInRecordOnly", |
| | | method: "post", |
| | | data: params, |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢åºåºè®°å½ï¼ä»
å建记å½ï¼ä¸è°æ´åºåï¼ |
| | | export const addUnqualifiedStockOutRecordOnly = (params) => { |
| | | return request({ |
| | | url: "/stockUninventory/addStockOutRecordOnly", |
| | | method: "post", |
| | | data: params, |
| | | }); |
| | | }; |
| | | |
| | | // å»ç»åºåè®°å½ |
| | | export const frozenStockUninventory = (params) => { |
| | | return request({ |
| | |
| | | }); |
| | | } |
| | | |
| | | export function productUploadFile(data) { |
| | | return request({ |
| | | url: "/file/uploadFile", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // export function getProductRecordById(params) { |
| | | // return request({ |
| | | // url: "/purchase/registration/getProductRecordById", |
| | |
| | | }); |
| | | } |
| | | |
| | | // æ ¹æ®éè´å°è´¦ ID æ¥è¯¢å¯é产åçä¿¡æ¯ |
| | | export function getPurchaseReturnOrderByPurchaseLedgerId(query) { |
| | | return request({ |
| | | url: "/purchaseReturnOrders/getByPurchaseLedgerId", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ¥ç详æ
|
| | | // purchaseReturnOrders/selectById/xxx |
| | | export function getPurchaseReturnOrderDetail(id) { |
| | |
| | | // å页æ¥è¯¢ |
| | | export function listPage(query) { |
| | | return request({ |
| | | url: "/processRoute/page", |
| | | url: "/technologyRouting/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | |
| | | export function add(data) { |
| | | return request({ |
| | | url: "/processRoute", |
| | | url: "/technologyRouting/addTechRoute", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // export function del(ids) { |
| | | // return request({ |
| | | // url: "/processRoute/" + ids, |
| | | // method: "delete", |
| | | // }); |
| | | // } |
| | | export function del(ids) { |
| | | return request({ |
| | | url: '/processRoute/' + ids, |
| | | method: 'delete', |
| | | }) |
| | | url: "/technologyRouting/delete", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| | | |
| | | export function update(data) { |
| | | return request({ |
| | | url: '/processRoute', |
| | | method: 'put', |
| | | url: "/technologyRouting/editTechRoute", |
| | | method: "put", |
| | | data: data, |
| | | }) |
| | | }); |
| | | } |
| | | |
| | | // è·å详æ
|
| | | export function getById(id) { |
| | | return request({ |
| | | url: `/processRoute/${id}`, |
| | | method: 'get', |
| | | }) |
| | | } |
| | | method: "get", |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // éä»¶å表 |
| | | export function listProcessRouteFiles(query) { |
| | | return request({ |
| | | url: "/technologyRoutingFile/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢éä»¶ |
| | | export function addProcessRouteFile(data) { |
| | | return request({ |
| | | url: "/technologyRoutingFile/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤éä»¶ |
| | | export function delProcessRouteFile(ids) { |
| | | return request({ |
| | | url: "/technologyRoutingFile/del", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| | |
| | | // å表æ¥è¯¢ |
| | | export function findProcessRouteItemList(query) { |
| | | return request({ |
| | | url: "/processRouteItem/list", |
| | | url: "/technologyRoutingOperation/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | |
| | | export function addOrUpdateProcessRouteItem(data) { |
| | | return request({ |
| | | url: "/processRouteItem", |
| | | url: "/technologyRoutingOperation/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | export function addOrUpdateProcessRouteItem1(data) { |
| | | return request({ |
| | | url: "/technologyRoutingOperation", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | | } |
| | |
| | | // æåºæ¥å£ |
| | | export function sortProcessRouteItem(data) { |
| | | return request({ |
| | | url: "/processRouteItem/sort", |
| | | url: "/technologyRoutingOperation/sort", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | // å°idæ°ç»è½¬æ¢ä¸ºéå·åéçåç¬¦ä¸²ï¼æ¼æ¥å°URLåé¢ |
| | | const idsStr = Array.isArray(ids) ? ids.join(",") : ids; |
| | | return request({ |
| | | url: `/processRouteItem/batchDelete/${idsStr}`, |
| | | url: `/technologyRoutingOperation/${idsStr}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | // è·åå·¥åºåæ°å表 |
| | | export function getProcessParamList(query) { |
| | | return request({ |
| | | url: `/technologyRoutingOperationParam/list`, |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // å·¥èºè·¯çº¿åæ°æ°å¢ |
| | | export function addProcessRouteItemParam(data) { |
| | | return request({ |
| | | url: "/technologyRoutingOperationParam/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // å·¥èºè·¯çº¿åæ°ä¿®æ¹ |
| | | export function editProcessRouteItemParam(data) { |
| | | return request({ |
| | | url: "/technologyRoutingOperationParam", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | | } |
| | | // å·¥èºè·¯çº¿åæ°å é¤ |
| | | export function delProcessRouteItemParam(id) { |
| | | return request({ |
| | | url: `/technologyRoutingOperationParam/${id}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | // æå·¥èºè·¯çº¿å·¥åºåæ¥å·¥åºåæ° |
| | | export function syncProcessParamItem(data) { |
| | | return request({ |
| | | url: "/technologyRoutingOperationParam/sync", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // æå·¥èºè·¯çº¿å·¥åºåæ¥å·¥åºåæ°-ç产订å |
| | | export function syncProcessParamItemOrder(data) { |
| | | return request({ |
| | | url: "/productionOrderRoutingOperationParam/sync", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | |
| | | // å页æ¥è¯¢ |
| | | export function listPage(query) { |
| | | return request({ |
| | | url: "/productBom/listPage", |
| | | url: "/technologyBom/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | // æ°å¢ |
| | | export function add(data) { |
| | | return request({ |
| | | url: "/productBom/add", |
| | | url: "/technologyBom/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // å¤å¶ |
| | | export function copy(data) { |
| | | return request({ |
| | | url: "/technologyBom/copy", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // ä¿®æ¹ |
| | | export function update(data) { |
| | | return request({ |
| | | url: "/productBom/update", |
| | | url: "/technologyBom/update", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | |
| | | // æ¹éå é¤ |
| | | export function batchDelete(ids) { |
| | | return request({ |
| | | url: "/productBom/batchDelete", |
| | | url: "/technologyBom/batchDelete", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | |
| | | // æ ¹æ®äº§ååå·IDæ¥è¯¢BOM |
| | | export function getByModel(productModelId) { |
| | | return request({ |
| | | url: "/productBom/getByModel", |
| | | url: "/technologyBom/getByModel", |
| | | method: "get", |
| | | params: { productModelId }, |
| | | }); |
| | |
| | | // 导åºBOM |
| | | export function exportBom(bomId) { |
| | | return request({ |
| | | url: "/productBom/exportBom", |
| | | url: "/technologyBom/exportBom", |
| | | method: "post", |
| | | params: { bomId }, |
| | | responseType: "blob", |
| | |
| | | // ä¸è½½æ¨¡æ¿ |
| | | export function downloadTemplate() { |
| | | return request({ |
| | | url: "/productBom/downloadTemplate", |
| | | url: "/technologyBom/downloadTemplate", |
| | | method: "get", |
| | | responseType: "blob", |
| | | }); |
| | | } |
| | | } |
| | |
| | | // å表æ¥è¯¢ |
| | | export function findProductProcessRouteItemList(query) { |
| | | return request({ |
| | | url: "/productProcessRoute/list", |
| | | url: "/productionOrderRouting/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | |
| | | export function addOrUpdateProductProcessRouteItem(data) { |
| | | return request({ |
| | | url: "/productProcessRoute/updateRouteItem", |
| | | url: "/productionOrderRouting/updateRouteItem", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | // ç产订åä¸ï¼æ°å¢å·¥èºè·¯çº¿é¡¹ç® |
| | | export function addRouteItem(data) { |
| | | return request({ |
| | | url: "/productProcessRoute/addRouteItem", |
| | | url: "/productionOrderRouting/addRouteItem", |
| | | method: "post", |
| | | data, |
| | | }); |
| | |
| | | // è·åç产订åå
³èçå·¥èºè·¯çº¿ä¸»ä¿¡æ¯ |
| | | export function listMain(orderId) { |
| | | return request({ |
| | | url: "/productProcessRoute/listMain", |
| | | url: "/productionOrderRouting/listMain", |
| | | method: "get", |
| | | params: { orderId }, |
| | | }); |
| | |
| | | // å é¤å·¥èºè·¯çº¿é¡¹ç®ï¼è·¯ç±åæ¼æ¥ idï¼ |
| | | export function deleteRouteItem(id) { |
| | | return request({ |
| | | url: `/productProcessRoute/deleteRouteItem/${id}`, |
| | | url: `/productionOrderRouting/deleteRouteItem/${id}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | |
| | | // ç产订åä¸ï¼æåºå·¥èºè·¯çº¿é¡¹ç® |
| | | export function sortRouteItem(data) { |
| | | return request({ |
| | | url: "/productProcessRoute/sortRouteItem", |
| | | url: "/productionOrderRouting/sortRouteItem", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | // è·åå·¥åºåæ°å表-ç产订å |
| | | export function findProcessParamListOrder(query) { |
| | | return request({ |
| | | url: `/productionOrderRoutingOperationParam/list`, |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // å·¥èºè·¯çº¿åæ°æ°å¢-ç产订å |
| | | export function addProcessRouteItemParamOrder(data) { |
| | | return request({ |
| | | url: "/productionOrderRoutingOperationParam", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // å·¥èºè·¯çº¿åæ°ä¿®æ¹-ç产订å |
| | | export function editProcessRouteItemParamOrder(data) { |
| | | return request({ |
| | | url: "/productionOrderRoutingOperationParam", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | | } |
| | | // å·¥èºè·¯çº¿åæ°å é¤-ç产订å |
| | | export function delProcessRouteItemParamOrder(id) { |
| | | return request({ |
| | | url: `/productionOrderRoutingOperationParam/${id}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | |
| | | // å页æ¥è¯¢ |
| | | export function queryList(id) { |
| | | return request({ |
| | | url: "/productStructure/listBybomId/" + id, |
| | | url: "/technologyBomStructure/listByBomId/" + id, |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | // å页æ¥è¯¢-产å订å |
| | | export function queryList2(id) { |
| | | return request({ |
| | | url: "/productionBomStructure/listByBomId/" + id, |
| | | method: "get", |
| | | }); |
| | | } |
| | | export function add(data) { |
| | | return request({ |
| | | url: "/productStructure", |
| | | url: "/productStructure/" + data.bomId, |
| | | method: "post", |
| | | data: data.children, |
| | | }); |
| | | } |
| | | |
| | | export function addBomDetail(data) { |
| | | return request({ |
| | | url: "/technologyBomStructure", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // å页æ¥è¯¢-产å订å |
| | | // export function queryList2(id) { |
| | | // return request({ |
| | | // url: "/productionOrderStructure/getBomStructs/" + id, |
| | | // method: "get", |
| | | // }); |
| | | // } |
| | | |
| | | export function add2(data) { |
| | | return request({ |
| | | url: "/productionBomStructure/addOrUpdateBomStructs", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | // salesLedger/productionAccounting/page |
| | | export function salesLedgerProductionAccountingList(query) { |
| | | return request({ |
| | | url: "/salesLedger/productionAccounting/page", |
| | | url: "/productionAccount/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | // |
| | | export function salesLedgerProductionAccountingListProductionDetails(query) { |
| | | return request({ |
| | | url: "/salesLedger/productionAccounting/listProductionDetails", |
| | | url: "/productionAccount/listProductionDetails", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | |
| | | export function productOrderListPage(query) { |
| | | return request({ |
| | | url: "/productOrder/page", |
| | | url: "/productionOrder/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | // ç产订å-ç»å®å·¥èºè·¯çº¿ |
| | | export function bindingRoute(data) { |
| | | return request({ |
| | | url: "/productOrder/bindingRoute", |
| | | url: "/productionOrder/bindingRoute", |
| | | method: "post", |
| | | data, |
| | | }); |
| | |
| | | // ç产订å-æ°å¢ |
| | | export function addProductOrder(data) { |
| | | return request({ |
| | | url: "/productOrder/addProductOrder", |
| | | url: "/productionOrder/addOrder", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // ç产订å-ä¿®æ¹ |
| | | export function updateProductOrder(data) { |
| | | return request({ |
| | | url: "/productionOrder/updateOrder", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | |
| | | export function delProductOrder(ids) { |
| | | return request({ |
| | | url: `/productOrder/${ids}`, |
| | | url: `/productionOrder/delete`, |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // ç产订å-ä¿å颿å°è´¦ |
| | | // export function saveMaterialPickingLedger(data) { |
| | | // return request({ |
| | | // url: "/productOrderMaterial/save", |
| | | // method: "post", |
| | | // data, |
| | | // }); |
| | | // } |
| | | export function saveMaterialPickingLedger(data) { |
| | | return request({ |
| | | url: "/productOrderMaterial/update", |
| | | url: "/productionOrderPick/savePick", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | export function updateMaterialPickingLedger(data) { |
| | | return request({ |
| | | url: "/productionOrderPick/updatePick", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ç产订å-é¢æè¯¦æ
å表 |
| | | export function listMaterialPickingDetail(query) { |
| | | // çäº§è®¢åæº¯æºè¯¦æ
|
| | | export function getOrderDetail(npsNo) { |
| | | return request({ |
| | | url: "/productOrderMaterial/detailList", |
| | | url: "/productionOrder/ordeDetail", |
| | | method: "get", |
| | | params: { npsNo }, |
| | | }); |
| | | } |
| | | // ç产订å-é¢æè¯¦æ
å表 |
| | | // export function listMaterialPickingDetail(query) { |
| | | // return request({ |
| | | // url: "/productOrderMaterial/detailList", |
| | | // method: "get", |
| | | // params: query, |
| | | // }); |
| | | // } |
| | | export function listMaterialPickingBom(productionOrderId) { |
| | | return request({ |
| | | url: "/productionOrder/pick/" + productionOrderId, |
| | | method: "get", |
| | | }); |
| | | } |
| | | export function listMaterialPickingDetail(productionOrderId) { |
| | | return request({ |
| | | url: "/productionOrderPick/detail/" + productionOrderId, |
| | | method: "get", |
| | | }); |
| | | } |
| | | // ç产订å-è¡¥æè®°å½å表 |
| | | export function listMaterialSupplementRecord(query) { |
| | | return request({ |
| | | url: "/productionOrderPickRecord/feeding", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // ç产订å-è¡¥æè®°å½å表 |
| | | export function listMaterialSupplementRecord(query) { |
| | | // ç产订å-è·åæ¥æºæ°æ® |
| | | export function getProductOrderSource(id) { |
| | | return request({ |
| | | url: "/productOrderMaterial/supplementRecord", |
| | | url: `/productionOrder/source/${id}`, |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | |
| | | // å页æ¥è¯¢ |
| | | export function listPage(query) { |
| | | return request({ |
| | | url: "/productProcess/listPage", |
| | | url: "/technologyOperation/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | |
| | | export function processList(query) { |
| | | return request({ |
| | | url: "/productProcess/list", |
| | | url: "/technologyOperation/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // å·¥åºæ¥è¯¢ |
| | | export function list(query) { |
| | | return request({ |
| | | url: "/technologyOperation/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | export function add(data) { |
| | | return request({ |
| | | url: "/productProcess", |
| | | url: "/technologyOperation/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | |
| | | export function del(data) { |
| | | return request({ |
| | | url: '/productProcess/batchDelete', |
| | | method: 'delete', |
| | | url: "/technologyOperation/batchDelete", |
| | | method: "delete", |
| | | data: data, |
| | | }) |
| | | }); |
| | | } |
| | | |
| | | export function update(data) { |
| | | return request({ |
| | | url: '/productProcess/update', |
| | | method: 'put', |
| | | url: "/technologyOperation/update", |
| | | method: "put", |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // å·¥åºæ¥è¯¢ |
| | | export function list() { |
| | | return request({ |
| | | url: "/productProcess/list", |
| | | method: "get", |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | // 导å
¥æ°æ® |
| | | export function importData(data) { |
| | | return request({ |
| | | url: "/productProcess/importData", |
| | | url: "/technologyOperation/importData", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | // ä¸è½½æ¨¡æ¿ |
| | | export function downloadTemplate() { |
| | | return request({ |
| | | url: "/productProcess/downloadTemplate", |
| | | url: "/technologyOperation/downloadTemplate", |
| | | method: "post", |
| | | responseType: "blob", |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // è·åå·¥åºåæ°å表 |
| | | export function getProcessParamList(params) { |
| | | return request({ |
| | | url: `/technologyOperationParam/list`, |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // æ·»å å·¥åºåæ° |
| | | export function addProcessParam(data) { |
| | | return request({ |
| | | url: "/technologyOperationParam/", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // ç¼è¾å·¥åºåæ° |
| | | export function editProcessParam(data) { |
| | | return request({ |
| | | url: "/technologyOperationParam/", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤å·¥åºåæ° |
| | | export function deleteProcessParam(id) { |
| | | return request({ |
| | | url: `/technologyOperationParam/batchDelete/${id}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | |
| | | |
| | | export function productWorkOrderPage(query) { |
| | | return request({ |
| | | url: "/productWorkOrder/page", |
| | | url: "/productionOperationTask/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | |
| | | export function updateProductWorkOrder(data) { |
| | | return request({ |
| | | url: "/productWorkOrder/updateProductWorkOrder", |
| | | url: "/productionOperationTask/updateProductWorkOrder", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | }); |
| | | } |
| | | |
| | | export function assignProductWorkOrder(data) { |
| | | return request({ |
| | | url: "/productionOperationTask/assign", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // ä¸è½½å·¥åæµè½¬å¡ï¼è¿åæä»¶æµï¼ |
| | | export function downProductWorkOrder(id) { |
| | | return request({ |
| | | url: "/productWorkOrder/down", |
| | | url: "/productionOperationTask/down", |
| | | method: "post", |
| | | data: { id }, |
| | | responseType: "blob", |
| | |
| | | // å·¥å-è¡¥æ |
| | | export function addWorkOrderMaterialSupplement(data) { |
| | | return request({ |
| | | url: "/productWorkOrder/material/supplement", |
| | | url: "/productionOperationTask/material/supplement", |
| | | method: "post", |
| | | data, |
| | | }); |
| | |
| | | // å·¥å-éæ |
| | | export function addWorkOrderMaterialReturn(data) { |
| | | return request({ |
| | | url: "/productWorkOrder/material/return", |
| | | url: "/productionOperationTask/material/return", |
| | | method: "post", |
| | | data, |
| | | }); |
| | |
| | | // å·¥å-è¡¥æè®°å½ |
| | | export function listWorkOrderMaterialSupplementRecord(query) { |
| | | return request({ |
| | | url: "/productWorkOrder/material/supplementRecord", |
| | | url: "/productionOperationTask/material/supplementRecord", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | // å·¥å-é¢ç¨ï¼æäº¤å®é
é¢ç¨æ°éï¼ |
| | | export function pickWorkOrderMaterial(data) { |
| | | return request({ |
| | | url: "/productWorkOrder/material/pick", |
| | | url: "/productionOperationTask/material/pick", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // è·åå·¥åºç»è®¡æ°æ® |
| | | export function getOperationStatistics(query) { |
| | | return request({ |
| | | url: "/productionOperationTask/getOperation", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // ç产订å页颿¥å£ |
| | | import request from "@/utils/request"; |
| | | |
| | | export function productionPlanListPage(query) { |
| | | return request({ |
| | | url: "/productionPlan/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æåæ°æ® |
| | | export function loadProdData(query) { |
| | | return request({ |
| | | url: "/productionPlan/loadProdData", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function summaryByProductType(query) { |
| | | return request({ |
| | | url: "/productionPlan/summaryByProductType", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // 导åºç产计å |
| | | export function exportProductionPlan(bomId) { |
| | | return request({ |
| | | url: "/productionPlan/export", |
| | | method: "post", |
| | | params: { bomId }, |
| | | responseType: "blob", |
| | | }); |
| | | } |
| | | |
| | | // ç产计å-æ°å¢ä¿®æ¹ |
| | | export function productionPlanAdd(query) { |
| | | return request({ |
| | | url: "/productionPlan/addProductionPlan", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | export function productionPlanUpdate(query) { |
| | | return request({ |
| | | url: "/productionPlan/updateProductionPlan", |
| | | method: "put", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // ç产计å-å é¤ |
| | | export function productionPlanDelete(data) { |
| | | return request({ |
| | | url: "/productionPlan/deleteProductionPlan", |
| | | method: "delete", |
| | | data, |
| | | }); |
| | | } |
| | | // åå¹¶ä¸å |
| | | export function productionPlanCombine(query) { |
| | | return request({ |
| | | url: "/productionPlan/combine", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // 追踪è¿åº¦ |
| | | export function trackProgressByNo(query) { |
| | | return request({ |
| | | url: "/track/trackProgressByNo", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | |
| | | } |
| | | |
| | | // ä¿®æ¹åè´§å°è´¦ |
| | | export function getDeliveryDetail(id) { |
| | | return request({ |
| | | url: `/shippingInfo/getDateil/${id}`, |
| | | method: "get", |
| | | }); |
| | | } |
| | | // ä¿®æ¹åè´§å°è´¦ |
| | | export function getDeliveryDetailByShippingNo(query) { |
| | | return request({ |
| | | url: "/shippingInfo/getDateilByShippingNo", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function addOrUpdateDeliveryLedger(query) { |
| | | return request({ |
| | | url: "/shippingInfo/update", |
| | |
| | | } |
| | | |
| | | // ä¸ä¼ APK |
| | | export function uploadApk(data) { |
| | | export function add(data) { |
| | | return request({ |
| | | url: "/app/uploadApk", |
| | | url: "/app/add", |
| | | method: "post", |
| | | data, |
| | | headers: { |
| | | "Content-Type": "multipart/form-data", |
| | | }, |
| | | data |
| | | }); |
| | | } |
| | |
| | | }
|
| | | }
|
| | | .table_list {
|
| | | margin-top: 20px;
|
| | | background: rgba(255, 255, 255, 0.88);
|
| | | border: 1px solid var(--surface-border);
|
| | | border-radius: var(--radius-md);
|
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { Cpu } from '@element-plus/icons-vue' |
| | | |
| | | export const generalAssistant = { |
| | | key: 'general', |
| | | label: 'å¾
åå©ç', |
| | | title: 'å¾
åæºè½å©ç', |
| | | tooltip: 'å¾
å婿', |
| | | icon: Cpu, |
| | | apiBase: '/xiaozhi', |
| | | storageKey: 'ai_chat_uuid', |
| | | placeholder: '请è¾å
¥æ¨çé®é¢... (Enter åé, Shift+Enter æ¢è¡)', |
| | | welcomeMessage: 'ä½ å¥½', |
| | | description: 'æå¯ä»¥åçä½ çé®é¢ï¼ä¸ºä½ æä¾ä¸å¡æ°æ®è§£è¯»ä¿¡æ¯ãå¤ç建议åè¾
å©å³çæ¯æã', |
| | | allowFileUpload: true, |
| | | emptySessionText: 'ææ åå²ä¼è¯', |
| | | quickPrompts: [ |
| | | 'æå½åæåªäºå®¡æ¹å¾
åéè¦å¤çï¼', |
| | | '帮æååºä»å¤©æ°å¢ç审æ¹å¾
åã', |
| | | 'å½åå¾
æå®¡æ¹çåæ®ï¼ææ¶é´ååºååºæ¥ã', |
| | | 'æåèµ·ç审æ¹éï¼åªäºè¿å¨å¤çä¸ï¼', |
| | | 'æ¥è¯¢æµç¨ç¼å· XXX ç审æ¹è¯¦æ
ã', |
| | | 'æµç¨ç¼å· XXX ç°å¨å¡å¨åªä¸ªå®¡æ¹èç¹ï¼å½å审æ¹äººæ¯è°ï¼', |
| | | 'å¸®ææ¥çæµç¨ç¼å· XXX çå®¡æ¹æµè½¬è®°å½ã', |
| | | 'è¿7天æç审æ¹å¾
åç»è®¡æ
嵿乿 ·ï¼', |
| | | 'æ¬ææç审æ¹ä¸ï¼éè¿ã驳åãå¤çä¸åæå¤å°ï¼', |
| | | 'è¿30天åç±»åå®¡æ¹æ°éå叿¯ä»ä¹ï¼', |
| | | '帮æå®¡æ¹éè¿æµç¨ç¼å· XXXï¼å¤æ³¨âåæâã', |
| | | '帮æé©³åæµç¨ç¼å· XXXï¼å¤æ³¨â请补å
说æâã', |
| | | 'æ¤éæåå对æµç¨ç¼å· XXX çå®¡æ¹æä½ã', |
| | | '帮æä¿®æ¹æµç¨ç¼å· XXX ç夿³¨ä¸ºâ已补å
éä»¶âã', |
| | | 'å 餿åèµ·çæµç¨ç¼å· XXXã' |
| | | ] |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { generalAssistant } from './generalAssistant' |
| | | import { purchaseAssistant } from './purchaseAssistant' |
| | | |
| | | export { generalAssistant, purchaseAssistant } |
| | | |
| | | export const builtInAssistants = [generalAssistant, purchaseAssistant] |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { ShoppingCart } from '@element-plus/icons-vue' |
| | | |
| | | export const purchaseAssistant = { |
| | | key: 'purchase', |
| | | label: 'éè´å©ç', |
| | | title: 'éè´æºè½å©ç', |
| | | tooltip: 'éè´æºè½å©ç', |
| | | icon: ShoppingCart, |
| | | apiBase: '/purchase-ai', |
| | | storageKey: 'purchase_ai_chat_uuid', |
| | | placeholder: '请è¾å
¥éè´é®é¢... (Enter åé, Shift+Enter æ¢è¡)', |
| | | welcomeMessage: 'ä½ å¥½', |
| | | description: 'æå¯ä»¥åå©ä½ åæéè´è®¢åãå°è´§è¿åº¦ãä¾åºå表ç°å仿¬¾æ
åµï¼å¸®å©ä½ å¿«éå®ä½éè´å¼å¸¸ã', |
| | | allowFileUpload: true, |
| | | allowMultipleFileUpload: true, |
| | | fileAnalyzeUrl: '/purchase-ai/analyze-files', |
| | | emptySessionText: 'ææ éè´ä¼è¯', |
| | | quickPrompts: [ |
| | | 'æ¬æéè´é颿åååçç©ææåªäºï¼', |
| | | 'åªäºéè´è®¢åè¿æªå
¥åºï¼', |
| | | 'æè¿7天ä¾åºåå°è´§å¼å¸¸æåªäºï¼', |
| | | '帮æç»è®¡å¾
仿¬¾éè´å', |
| | | 'ååºæ¬æéè´éè´§æ
åµ' |
| | | ] |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup> |
| | | const props = defineProps({ |
| | | fileList: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | thumbSize: { |
| | | type: Number, |
| | | default: 72, |
| | | }, |
| | | gap: { |
| | | type: Number, |
| | | default: 10, |
| | | }, |
| | | }) |
| | | |
| | | const normalizedList = computed(() => { |
| | | return (props.fileList || []) |
| | | .filter((item) => item && item.previewURL) |
| | | .map((item, index) => ({ |
| | | id: item.id ?? index, |
| | | name: item.originalFilename || `image-${index + 1}`, |
| | | url: item.previewURL, |
| | | })) |
| | | }) |
| | | const previewUrls = computed(() => normalizedList.value.map((item) => item.url)) |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="attachment-image-preview"> |
| | | <div v-if="!normalizedList.length" class="empty">ææ å¾ç</div> |
| | | |
| | | <div v-else class="thumbs" :style="{ gap: `${gap}px` }"> |
| | | <el-image |
| | | v-for="(item, index) in normalizedList" |
| | | :key="item.id" |
| | | class="thumb" |
| | | :style="{ width: `${thumbSize}px`, height: `${thumbSize}px` }" |
| | | :src="item.url" |
| | | :preview-src-list="previewUrls" |
| | | :initial-index="index" |
| | | fit="cover" |
| | | preview-teleported |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped lang="scss"> |
| | | .attachment-image-preview { |
| | | width: 100%; |
| | | } |
| | | |
| | | .empty { |
| | | height: 120px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: var(--el-text-color-secondary); |
| | | border: 1px dashed var(--el-border-color); |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .thumbs { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .thumb { |
| | | border: 1px solid var(--el-border-color); |
| | | border-radius: 6px; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | background: #fff; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup> |
| | | import { UploadFilled } from '@element-plus/icons-vue' |
| | | import { uploadFile } from '@/api/basicData/common' |
| | | |
| | | const props = defineProps({ |
| | | fileList: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | index: { |
| | | type: Number, |
| | | default: -1, |
| | | }, |
| | | childrenKey: { |
| | | type: String, |
| | | default: 'files', |
| | | }, |
| | | limit: { |
| | | type: Number, |
| | | default: 10, |
| | | }, |
| | | fileSize: { |
| | | type: Number, |
| | | default: 50, |
| | | }, |
| | | fileType: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | buttonText: { |
| | | type: String, |
| | | default: 'åå»éæ©æä»¶', |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | uploadFieldName: { |
| | | type: String, |
| | | default: 'files', |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:fileList', 'change']) |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | const uploadRef = ref() |
| | | const uploadQueueTimer = ref(null) |
| | | const uploading = ref(false) |
| | | const queuedUidSet = ref(new Set()) |
| | | const innerList = ref([]) |
| | | |
| | | function readListFromProps() { |
| | | if (props.index > -1) { |
| | | const row = props.fileList?.[props.index] |
| | | return Array.isArray(row?.[props.childrenKey]) ? row[props.childrenKey] : [] |
| | | } |
| | | return Array.isArray(props.fileList) ? props.fileList : [] |
| | | } |
| | | |
| | | watch( |
| | | () => props.fileList, |
| | | () => { |
| | | innerList.value = [...readListFromProps()] |
| | | }, |
| | | { deep: true, immediate: true }, |
| | | ) |
| | | |
| | | const currentList = computed({ |
| | | get() { |
| | | return innerList.value |
| | | }, |
| | | set(value) { |
| | | const nextList = Array.isArray(value) ? value : [] |
| | | innerList.value = nextList |
| | | |
| | | if (props.index > -1) { |
| | | const nextModelValue = Array.isArray(props.fileList) ? [...props.fileList] : [] |
| | | const currentRow = nextModelValue[props.index] || {} |
| | | nextModelValue[props.index] = { |
| | | ...currentRow, |
| | | [props.childrenKey]: nextList, |
| | | } |
| | | emit('update:fileList', nextModelValue) |
| | | emit('change', nextList, nextModelValue) |
| | | return |
| | | } |
| | | |
| | | emit('update:fileList', nextList) |
| | | emit('change', nextList, nextList) |
| | | }, |
| | | }) |
| | | |
| | | const displayFileList = computed(() => { |
| | | return currentList.value.map((item, index) => ({ |
| | | uid: getItemUid(item, index), |
| | | name: getItemName(item, index), |
| | | url: getItemUrl(item), |
| | | status: 'success', |
| | | rawData: item, |
| | | })) |
| | | }) |
| | | |
| | | const uploadTip = computed(() => { |
| | | if (!props.fileType.length) return `å个æä»¶ä¸è¶
è¿ ${props.fileSize}MB` |
| | | return `æ¯æ ${props.fileType.join('/')}ï¼å个æä»¶ä¸è¶
è¿ ${props.fileSize}MB` |
| | | }) |
| | | |
| | | function getItemUid(item, index) { |
| | | if (item?.id !== undefined && item?.id !== null) return `${item.id}` |
| | | return `${getItemName(item, index)}-${getItemUrl(item) || index}` |
| | | } |
| | | |
| | | function getItemUrl(item) { |
| | | if (!item) return '' |
| | | if (typeof item === 'string') return item |
| | | return item.url || item.downloadURL || item.previewURL || item.previewUrl || '' |
| | | } |
| | | |
| | | function getItemName(item, index = 0) { |
| | | if (!item) return `file-${index + 1}` |
| | | if (typeof item === 'string') return `file-${index + 1}` |
| | | return item.name || item.originalFilename || item.fileName || item.uidFilename || `file-${index + 1}` |
| | | } |
| | | |
| | | function normalizeResponseItem(item, index) { |
| | | if (typeof item === 'string') { |
| | | return { |
| | | name: `file-${currentList.value.length + index + 1}`, |
| | | url: item, |
| | | } |
| | | } |
| | | return Object.assign({}, item, { |
| | | url: item.url || item.downloadURL || item.previewURL || item.previewUrl || '', |
| | | name: item.name || item.originalFilename || item.fileName || item.uidFilename || `file-${currentList.value.length + index + 1}`, |
| | | }) |
| | | } |
| | | |
| | | function extractResponseArray(response) { |
| | | if (Array.isArray(response)) return response |
| | | if (Array.isArray(response?.data)) return response.data |
| | | if (Array.isArray(response?.data?.data)) return response.data.data |
| | | if (Array.isArray(response?.payload)) return response.payload |
| | | if (Array.isArray(response?.payload?.data)) return response.payload.data |
| | | if (Array.isArray(response?.rows)) return response.rows |
| | | if (Array.isArray(response?.result)) return response.result |
| | | return [] |
| | | } |
| | | |
| | | function validateFile(rawFile) { |
| | | const extension = rawFile.name.includes('.') |
| | | ? rawFile.name.slice(rawFile.name.lastIndexOf('.') + 1).toLowerCase() |
| | | : '' |
| | | |
| | | if (props.fileType.length) { |
| | | const isValidType = props.fileType.some((type) => { |
| | | const normalizedType = String(type).toLowerCase() |
| | | return rawFile.type.toLowerCase().includes(normalizedType) || extension === normalizedType |
| | | }) |
| | | if (!isValidType) { |
| | | proxy.$modal.msgError(`请ä¸ä¼ ${props.fileType.join('/')} æ ¼å¼çæä»¶`) |
| | | return false |
| | | } |
| | | } |
| | | |
| | | const isWithinSize = rawFile.size / 1024 / 1024 <= props.fileSize |
| | | if (!isWithinSize) { |
| | | proxy.$modal.msgError(`æä»¶å¤§å°ä¸è½è¶
è¿ ${props.fileSize}MB`) |
| | | return false |
| | | } |
| | | |
| | | return true |
| | | } |
| | | |
| | | function scheduleUpload(uploadFiles) { |
| | | clearTimeout(uploadQueueTimer.value) |
| | | uploadQueueTimer.value = setTimeout(() => { |
| | | const readyFiles = uploadFiles.filter((file) => file.raw && !queuedUidSet.value.has(file.uid)) |
| | | if (!readyFiles.length) return |
| | | |
| | | const remainCount = props.limit - currentList.value.length |
| | | if (remainCount <= 0) { |
| | | proxy.$modal.msgError(`æå¤ä¸ä¼ ${props.limit} 个æä»¶`) |
| | | uploadRef.value?.clearFiles() |
| | | return |
| | | } |
| | | |
| | | const selectedFiles = readyFiles.slice(0, remainCount) |
| | | if (selectedFiles.length < readyFiles.length) { |
| | | proxy.$modal.msgWarning(`æå¤ä¸ä¼ ${props.limit} 个æä»¶ï¼è¶
åºé¨å已忽ç¥`) |
| | | } |
| | | |
| | | selectedFiles.forEach((file) => queuedUidSet.value.add(file.uid)) |
| | | uploadSelectedFiles(selectedFiles) |
| | | }, 0) |
| | | } |
| | | |
| | | async function uploadSelectedFiles(files) { |
| | | const validFiles = files.filter((file) => validateFile(file.raw)) |
| | | const invalidFiles = files.filter((file) => !validFiles.includes(file)) |
| | | |
| | | invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid)) |
| | | |
| | | if (!validFiles.length) { |
| | | uploadRef.value?.clearFiles() |
| | | return |
| | | } |
| | | |
| | | const formData = new FormData() |
| | | validFiles.forEach((file) => { |
| | | formData.append(props.uploadFieldName, file.raw) |
| | | }) |
| | | |
| | | uploading.value = true |
| | | proxy.$modal.loading('æä»¶ä¸ä¼ ä¸ï¼è¯·ç¨å...') |
| | | |
| | | try { |
| | | const response = await uploadFile(formData) |
| | | const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index)) |
| | | |
| | | if (!responseList.length) { |
| | | proxy.$modal.msgError('ä¸ä¼ æ¥å£æªè¿åæ°ç»æ°æ®') |
| | | return |
| | | } |
| | | |
| | | currentList.value = [...currentList.value, ...responseList] |
| | | proxy.$modal.msgSuccess('ä¸ä¼ æå') |
| | | } catch (error) { |
| | | proxy.$modal.msgError(error?.message || 'ä¸ä¼ 失败') |
| | | } finally { |
| | | validFiles.forEach((file) => queuedUidSet.value.delete(file.uid)) |
| | | invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid)) |
| | | uploadRef.value?.clearFiles() |
| | | uploading.value = false |
| | | proxy.$modal.closeLoading() |
| | | } |
| | | } |
| | | |
| | | function handleChange(file, uploadFiles) { |
| | | if (props.disabled || uploading.value) return |
| | | scheduleUpload(uploadFiles) |
| | | } |
| | | |
| | | function handleRemove(file) { |
| | | const targetUrl = file.url || getItemUrl(file.rawData) |
| | | const nextList = currentList.value.filter((item, index) => { |
| | | const itemUrl = getItemUrl(item) |
| | | const itemName = getItemName(item, index) |
| | | return !(itemUrl === targetUrl && itemName === file.name) |
| | | }) |
| | | currentList.value = nextList |
| | | } |
| | | |
| | | function handleExceed() { |
| | | proxy.$modal.msgError(`æå¤ä¸ä¼ ${props.limit} 个æä»¶`) |
| | | } |
| | | |
| | | function openFile(file) { |
| | | const fileUrl = file.url || getItemUrl(file.rawData) |
| | | if (!fileUrl) return |
| | | window.open(fileUrl, '_blank') |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | clearTimeout(uploadQueueTimer.value) |
| | | }) |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="attachment-upload-file"> |
| | | <el-upload |
| | | ref="uploadRef" |
| | | drag |
| | | :auto-upload="false" |
| | | :multiple="true" |
| | | :show-file-list="true" |
| | | :file-list="displayFileList" |
| | | :disabled="disabled || uploading" |
| | | :limit="limit" |
| | | :on-change="handleChange" |
| | | :on-remove="handleRemove" |
| | | :on-exceed="handleExceed" |
| | | :on-preview="openFile" |
| | | > |
| | | <el-icon class="upload-drag-icon"><UploadFilled /></el-icon> |
| | | <div class="el-upload__text"> |
| | | å°æä»¶æå°æ¤å¤ï¼æ <em>{{ buttonText }}</em> |
| | | </div> |
| | | <div class="upload-tip">{{ uploadTip }}</div> |
| | | </el-upload> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped lang="scss"> |
| | | .attachment-upload-file { |
| | | width: 100%; |
| | | } |
| | | |
| | | .upload-drag-icon { |
| | | font-size: 40px; |
| | | color: var(--el-text-color-secondary); |
| | | } |
| | | |
| | | .upload-tip { |
| | | margin-top: 8px; |
| | | color: var(--el-text-color-secondary); |
| | | font-size: 12px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup> |
| | | import {Plus} from '@element-plus/icons-vue' |
| | | import {uploadFile} from '@/api/basicData/common' |
| | | |
| | | const props = defineProps({ |
| | | fileList: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | index: { |
| | | type: Number, |
| | | default: -1, |
| | | }, |
| | | childrenKey: { |
| | | type: String, |
| | | default: 'images', |
| | | }, |
| | | limit: { |
| | | type: Number, |
| | | default: 10, |
| | | }, |
| | | fileSize: { |
| | | type: Number, |
| | | default: 10, |
| | | }, |
| | | fileType: { |
| | | type: Array, |
| | | default: () => ['png', 'jpg', 'jpeg', 'webp'], |
| | | }, |
| | | buttonText: { |
| | | type: String, |
| | | default: 'ä¸ä¼ å¾ç', |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | uploadFieldName: { |
| | | type: String, |
| | | default: 'files', |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:fileList', 'change']) |
| | | const {proxy} = getCurrentInstance() |
| | | |
| | | const uploadRef = ref() |
| | | const previewVisible = ref(false) |
| | | const previewUrl = ref('') |
| | | const uploadQueueTimer = ref(null) |
| | | const uploading = ref(false) |
| | | const queuedUidSet = ref(new Set()) |
| | | |
| | | const currentList = computed({ |
| | | get() { |
| | | if (props.index > -1) { |
| | | const row = props.fileList?.[props.index] |
| | | return Array.isArray(row?.[props.childrenKey]) ? row[props.childrenKey] : [] |
| | | } |
| | | return Array.isArray(props.fileList) ? props.fileList : [] |
| | | }, |
| | | set(value) { |
| | | const nextList = Array.isArray(value) ? value : [] |
| | | if (props.index > -1) { |
| | | const nextModelValue = Array.isArray(props.fileList) ? [...props.fileList] : [] |
| | | const currentRow = nextModelValue[props.index] || {} |
| | | nextModelValue[props.index] = { |
| | | ...currentRow, |
| | | [props.childrenKey]: nextList, |
| | | } |
| | | emit('update:fileList', nextModelValue) |
| | | emit('change', nextList, nextModelValue) |
| | | return |
| | | } |
| | | emit('update:fileList', nextList) |
| | | emit('change', nextList, nextList) |
| | | }, |
| | | }) |
| | | |
| | | const displayFileList = computed(() => { |
| | | return currentList.value.map((item, index) => ({ |
| | | uid: getItemUid(item, index), |
| | | name: getItemName(item, index), |
| | | url: getItemUrl(item), |
| | | status: 'success', |
| | | rawData: item, |
| | | })) |
| | | }) |
| | | |
| | | const uploadTip = computed(() => { |
| | | return `æ¯æ ${props.fileType.join('/')}ï¼åå¼ ä¸è¶
è¿ ${props.fileSize}MBï¼æå¤ä¸ä¼ ${props.limit} å¼ å¾ç` |
| | | }) |
| | | |
| | | function getItemUid(item, index) { |
| | | if (item?.id !== undefined && item?.id !== null) return `${item.id}` |
| | | return `${getItemName(item, index)}-${getItemUrl(item) || index}` |
| | | } |
| | | |
| | | function getItemUrl(item) { |
| | | if (!item) return '' |
| | | if (typeof item === 'string') return item |
| | | return item.url || item.previewURL || '' |
| | | } |
| | | |
| | | function getItemName(item, index = 0) { |
| | | if (!item) return `image-${index + 1}` |
| | | if (typeof item === 'string') return `image-${index + 1}` |
| | | return item.name || item.fileName || item.originalFilename || `image-${index + 1}` |
| | | } |
| | | |
| | | function normalizeResponseItem(item, index) { |
| | | if (typeof item === 'string') { |
| | | return { |
| | | name: `image-${currentList.value.length + index + 1}`, |
| | | url: item, |
| | | } |
| | | } |
| | | return Object.assign({}, item, { |
| | | url: item.url || item.previewURL || item.previewUrl || '', |
| | | name: item.name || item.originalFilename || item.fileName || `image-${currentList.value.length + index + 1}`, |
| | | }) |
| | | } |
| | | |
| | | function extractResponseArray(response) { |
| | | if (Array.isArray(response)) return response |
| | | if (Array.isArray(response?.data)) return response.data |
| | | if (Array.isArray(response?.data?.data)) return response.data.data |
| | | if (Array.isArray(response?.payload)) return response.payload |
| | | if (Array.isArray(response?.payload?.data)) return response.payload.data |
| | | if (Array.isArray(response?.rows)) return response.rows |
| | | if (Array.isArray(response?.result)) return response.result |
| | | return [] |
| | | } |
| | | |
| | | function validateFile(rawFile) { |
| | | let isValidType = false |
| | | const extension = rawFile.name.includes('.') |
| | | ? rawFile.name.slice(rawFile.name.lastIndexOf('.') + 1).toLowerCase() |
| | | : '' |
| | | |
| | | if (props.fileType.length) { |
| | | isValidType = props.fileType.some((type) => { |
| | | const normalizedType = String(type).toLowerCase() |
| | | return rawFile.type.toLowerCase().includes(normalizedType) || extension === normalizedType |
| | | }) |
| | | } else { |
| | | isValidType = rawFile.type.includes('image') |
| | | } |
| | | |
| | | if (!isValidType) { |
| | | proxy.$modal.msgError(`请ä¸ä¼ ${props.fileType.join('/')} æ ¼å¼çå¾ç`) |
| | | return false |
| | | } |
| | | |
| | | const isWithinSize = rawFile.size / 1024 / 1024 <= props.fileSize |
| | | if (!isWithinSize) { |
| | | proxy.$modal.msgError(`å¾ç大å°ä¸è½è¶
è¿ ${props.fileSize}MB`) |
| | | return false |
| | | } |
| | | |
| | | return true |
| | | } |
| | | |
| | | function scheduleUpload(uploadFiles) { |
| | | clearTimeout(uploadQueueTimer.value) |
| | | uploadQueueTimer.value = setTimeout(() => { |
| | | const readyFiles = uploadFiles.filter((file) => file.raw && !queuedUidSet.value.has(file.uid)) |
| | | if (!readyFiles.length) return |
| | | |
| | | const remainCount = props.limit - currentList.value.length |
| | | if (remainCount <= 0) { |
| | | proxy.$modal.msgError(`æå¤ä¸ä¼ ${props.limit} å¼ å¾ç`) |
| | | uploadRef.value?.clearFiles() |
| | | return |
| | | } |
| | | |
| | | const selectedFiles = readyFiles.slice(0, remainCount) |
| | | if (selectedFiles.length < readyFiles.length) { |
| | | proxy.$modal.msgWarning(`æå¤ä¸ä¼ ${props.limit} å¼ å¾çï¼è¶
åºé¨å已忽ç¥`) |
| | | } |
| | | |
| | | selectedFiles.forEach((file) => queuedUidSet.value.add(file.uid)) |
| | | uploadSelectedFiles(selectedFiles) |
| | | }, 0) |
| | | } |
| | | |
| | | async function uploadSelectedFiles(files) { |
| | | const validFiles = files.filter((file) => validateFile(file.raw)) |
| | | const invalidFiles = files.filter((file) => !validFiles.includes(file)) |
| | | |
| | | invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid)) |
| | | |
| | | if (!validFiles.length) { |
| | | uploadRef.value?.clearFiles() |
| | | return |
| | | } |
| | | |
| | | const formData = new FormData() |
| | | validFiles.forEach((file) => { |
| | | formData.append(props.uploadFieldName, file.raw) |
| | | }) |
| | | |
| | | uploading.value = true |
| | | proxy.$modal.loading('å¾çä¸ä¼ ä¸ï¼è¯·ç¨å...') |
| | | |
| | | try { |
| | | const response = await uploadFile(formData) |
| | | const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index)) |
| | | |
| | | if (!responseList.length) { |
| | | proxy.$modal.msgError('ä¸ä¼ æ¥å£æªè¿åæ°ç»æ°æ®') |
| | | return |
| | | } |
| | | console.log('responseList', responseList) |
| | | |
| | | currentList.value = [...currentList.value, ...responseList] |
| | | console.log('currentList.value', currentList.value) |
| | | proxy.$modal.msgSuccess('ä¸ä¼ æå') |
| | | } catch (error) { |
| | | proxy.$modal.msgError(error?.message || 'ä¸ä¼ 失败') |
| | | } finally { |
| | | validFiles.forEach((file) => queuedUidSet.value.delete(file.uid)) |
| | | invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid)) |
| | | uploadRef.value?.clearFiles() |
| | | uploading.value = false |
| | | proxy.$modal.closeLoading() |
| | | } |
| | | } |
| | | |
| | | function handleChange(file, uploadFiles) { |
| | | if (props.disabled || uploading.value) return |
| | | scheduleUpload(uploadFiles) |
| | | } |
| | | |
| | | function handleRemove(file) { |
| | | const targetUrl = file.url || getItemUrl(file.rawData) |
| | | const nextList = currentList.value.filter((item, index) => { |
| | | const itemUrl = getItemUrl(item) |
| | | const itemName = getItemName(item, index) |
| | | return !(itemUrl === targetUrl && itemName === file.name) |
| | | }) |
| | | currentList.value = nextList |
| | | } |
| | | |
| | | function handlePreview(file) { |
| | | previewUrl.value = file.url || getItemUrl(file.rawData) |
| | | previewVisible.value = true |
| | | } |
| | | |
| | | function handleExceed() { |
| | | proxy.$modal.msgError(`æå¤ä¸ä¼ ${props.limit} å¼ å¾ç`) |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | clearTimeout(uploadQueueTimer.value) |
| | | }) |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="attachment-upload-image"> |
| | | <el-upload |
| | | ref="uploadRef" |
| | | :auto-upload="false" |
| | | :multiple="true" |
| | | :show-file-list="true" |
| | | :file-list="displayFileList" |
| | | list-type="picture-card" |
| | | accept="image/*" |
| | | :disabled="disabled || uploading" |
| | | :limit="limit" |
| | | :on-change="handleChange" |
| | | :on-remove="handleRemove" |
| | | :on-preview="handlePreview" |
| | | :on-exceed="handleExceed" |
| | | > |
| | | <div class="upload-trigger"> |
| | | <el-icon> |
| | | <Plus/> |
| | | </el-icon> |
| | | <span>{{ buttonText }}</span> |
| | | </div> |
| | | </el-upload> |
| | | |
| | | <div class="upload-tip"> |
| | | {{ uploadTip }} |
| | | </div> |
| | | |
| | | <el-dialog v-model="previewVisible" title="å¾çé¢è§" width="720px" append-to-body> |
| | | <img class="preview-image" :src="previewUrl" alt="preview"/> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped lang="scss"> |
| | | .attachment-upload-image { |
| | | width: 100%; |
| | | } |
| | | |
| | | .upload-trigger { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 6px; |
| | | color: var(--el-text-color-secondary); |
| | | font-size: 12px; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .upload-tip { |
| | | margin-top: 8px; |
| | | color: var(--el-text-color-secondary); |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .preview-image { |
| | | display: block; |
| | | max-width: 100%; |
| | | margin: 0 auto; |
| | | } |
| | | |
| | | :deep(.el-upload-list--picture-card) { |
| | | margin: 0; |
| | | } |
| | | |
| | | :deep(.el-upload--picture-card) { |
| | | width: 132px; |
| | | height: 132px; |
| | | } |
| | | |
| | | :deep(.el-upload-list--picture-card .el-upload-list__item) { |
| | | width: 132px; |
| | | height: 132px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="isShow" |
| | | :title="title" |
| | | :width="width" |
| | | @close="handleClose" |
| | | class="attachment-dialog"> |
| | | <!-- å·¥å
·æ --> |
| | | <div v-if="editable" |
| | | class="toolbar"> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="handleUpload"> |
| | | ä¸ä¼ éä»¶ |
| | | </el-button> |
| | | </div> |
| | | <!-- ä¸ä¼ ç»ä»¶å¼¹çª --> |
| | | <el-dialog v-model="uploadDialogVisible" |
| | | title="ä¸ä¼ éä»¶" |
| | | width="50%" |
| | | @close="closeUpload"> |
| | | <AttachmentUpload v-model:file-list="newFileList" /> |
| | | <template #footer> |
| | | <el-button @click="saveUpload">ä¿å</el-button> |
| | | <el-button @click="closeUpload">å
³é</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- æä»¶åè¡¨è¡¨æ ¼ --> |
| | | <div class="table-container"> |
| | | <el-table :data="tableData" |
| | | border |
| | | class="attachment-table" |
| | | :height="tableData.length > 0 ? 'auto' : '120px'"> |
| | | <el-table-column label="éä»¶åç§°" |
| | | prop="originalFilename" |
| | | show-overflow-tooltip /> |
| | | <el-table-column v-if="showActions" |
| | | fixed="right" |
| | | label="æä½" |
| | | :width="150" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | class="download-link" |
| | | @click="previewFile(scope.row.previewURL)"> |
| | | é¢è§ |
| | | </el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | class="download-link" |
| | | @click="downloadFile(scope.row.downloadURL)"> |
| | | ä¸è½½ |
| | | </el-button> |
| | | <el-button v-if="editable" |
| | | link |
| | | type="danger" |
| | | size="small" |
| | | @click="handleDelete(scope.row)"> |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-dialog> |
| | | <filePreview ref="filePreviewRef" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ElMessage } from 'element-plus' |
| | | import { ref, computed, getCurrentInstance, onMounted, watch } from "vue"; |
| | | import AttachmentUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | import { |
| | | attachmentList, |
| | | deleteAttachment, |
| | | createAttachment, |
| | | } from "@/api/basicData/storageAttachment.js"; |
| | | import filePreview from '@/components/filePreview/index.vue' |
| | | const filePreviewRef = ref() |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | required: true, |
| | | }, |
| | | recordType: { |
| | | type: String, |
| | | default: "", |
| | | required: true, |
| | | }, |
| | | recordId: { |
| | | type: Number, |
| | | default: 0, |
| | | required: true, |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: "éä»¶", |
| | | }, |
| | | width: { |
| | | type: String, |
| | | default: "50%", |
| | | }, |
| | | showActions: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | editable: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["close", "download", "upload", "delete"]); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const uploadDialogVisible = ref(false); |
| | | const newFileList = ref([]); |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | | }, |
| | | set(val) { |
| | | emit("update:visible", val); |
| | | }, |
| | | }); |
| | | |
| | | const handleClose = () => { |
| | | isShow.value = false; |
| | | }; |
| | | |
| | | // é¢è§æä»¶ |
| | | const previewFile = (url) => { |
| | | if (url) { |
| | | filePreviewRef.value.open(url) |
| | | } else { |
| | | ElMessage.warning('æä»¶å°åæ æï¼æ æ³é¢è§') |
| | | } |
| | | } |
| | | |
| | | const handleUpload = () => { |
| | | uploadDialogVisible.value = true; |
| | | }; |
| | | |
| | | const saveUpload = async () => { |
| | | // æ£æ¥æ¯å¦ææ°ä¸ä¼ çæä»¶ |
| | | if (newFileList.value.length > 0) { |
| | | createAttachment({ |
| | | application: "file", |
| | | recordType: props.recordType, |
| | | recordId: props.recordId, |
| | | storageBlobDTOs: [...newFileList.value, ...tableData.value], |
| | | }).then((res) => { |
| | | if (res && res.code === 200) { |
| | | proxy?.$modal?.msgSuccess("ä¸ä¼ æå"); |
| | | newFileList.value = []; |
| | | // å·æ°å表 |
| | | setList(); |
| | | } |
| | | }).finally(() => { |
| | | uploadDialogVisible.value = false; |
| | | }) |
| | | } |
| | | } |
| | | |
| | | const closeUpload = () => { |
| | | newFileList.value = []; |
| | | uploadDialogVisible.value = false; |
| | | }; |
| | | |
| | | const handleDelete = async (row, index) => { |
| | | deleteAttachment([row.storageAttachmentId]).then((res) => { |
| | | if (res && res.code === 200) { |
| | | proxy?.$modal?.msgSuccess("å 餿å"); |
| | | setList(); |
| | | } |
| | | }) |
| | | }; |
| | | |
| | | const setList = () => { |
| | | attachmentList({ |
| | | recordType: props.recordType, |
| | | recordId: props.recordId, |
| | | }).then(res => { |
| | | tableData.value = (res && res.data) || []; |
| | | }); |
| | | }; |
| | | |
| | | const downloadFile = url => { |
| | | window.open(url, "_blank"); |
| | | }; |
| | | onMounted(() => { |
| | | setList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .attachment-dialog { |
| | | border-radius: 12px; |
| | | } |
| | | |
| | | .toolbar { |
| | | margin-bottom: 16px; |
| | | text-align: right; |
| | | } |
| | | |
| | | .table-container { |
| | | max-height: 40vh; |
| | | overflow-y: auto; |
| | | min-height: 120px; |
| | | padding-bottom: 16px; |
| | | box-sizing: border-box; |
| | | will-change: scroll-position; |
| | | transform: translateZ(0); |
| | | -webkit-overflow-scrolling: touch; |
| | | } |
| | | |
| | | :deep(.el-table) { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | :deep(.el-table__body-wrapper) { |
| | | overflow-y: auto; |
| | | will-change: transform; |
| | | transform: translateZ(0); |
| | | } |
| | | |
| | | :deep(.el-table__body tr) { |
| | | transition: none; |
| | | } |
| | | |
| | | :deep(.el-dialog__footer) { |
| | | padding-top: 12px; |
| | | border-top: 1px solid #e9ecef; |
| | | } |
| | | |
| | | .attachment-table { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | :deep(.el-dialog__header) { |
| | | background-color: #f8f9fa; |
| | | border-bottom: 1px solid #e9ecef; |
| | | padding: 16px 20px; |
| | | } |
| | | |
| | | :deep(.el-dialog__title) { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | :deep(.el-dialog__body) { |
| | | padding: 16px 20px; |
| | | } |
| | | |
| | | :deep(.el-table__empty-text) { |
| | | color: #999; |
| | | } |
| | | </style> |
| | |
| | | @pagination="paginationSearch" |
| | | @change="handleChange" /> |
| | | </el-dialog> |
| | | <!-- // todo éä»¶é¢è§ç¸å
³ --> |
| | | <filePreview v-if="showPreview" |
| | | ref="filePreviewRef" /> |
| | | </template> |
| | |
| | | :before-upload="handleBeforeUpload" |
| | | :on-success="handleUploadSuccess" |
| | | :on-error="handleUploadError" |
| | | name="file" |
| | | name="files" |
| | | :show-file-list="false" |
| | | :headers="headers" |
| | | class="editor-img-uploader" |
| | | v-if="type == 'url'" |
| | | v-if="type === 'url'" |
| | | > |
| | | <i ref="uploadRef" class="editor-img-uploader"></i> |
| | | </el-upload> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import axios from "axios"; |
| | | import { QuillEditor } from "@vueup/vue-quill"; |
| | | import "@vueup/vue-quill/dist/vue-quill.snow.css"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import {uploadPublicFile} from "@/api/basicData/common.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const quillEditorRef = ref(); |
| | | const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // ä¸ä¼ çå¾çæå¡å¨å°å |
| | | const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/public/upload"); // ä¸ä¼ çå¾çæå¡å¨å°å |
| | | const headers = ref({ |
| | | Authorization: "Bearer " + getToken(), |
| | | }); |
| | |
| | | function handleUploadSuccess(res, file) { |
| | | // 妿ä¸ä¼ æå |
| | | if (res.code == 200) { |
| | | const imageUrl = resolveImageUrl(res); |
| | | if (!imageUrl) { |
| | | proxy.$modal.msgError("æªè·åå°å¾çå°å"); |
| | | return; |
| | | } |
| | | // è·å坿æ¬å®ä¾ |
| | | let quill = toRaw(quillEditorRef.value).getQuill(); |
| | | // è·åå
æ ä½ç½® |
| | | let length = quill.selection.savedRange.index; |
| | | const selection = quill.getSelection(true); |
| | | const length = selection ? selection.index : quill.getLength(); |
| | | // æå
¥å¾çï¼res.url为æå¡å¨è¿åçå¾ç龿¥å°å |
| | | quill.insertEmbed( |
| | | length, |
| | | "image", |
| | | import.meta.env.VITE_APP_BASE_API + res.fileName |
| | | ); |
| | | quill.insertEmbed(length, "image", imageUrl); |
| | | // è°æ´å
æ å°æå |
| | | quill.setSelection(length + 1); |
| | | } else { |
| | | proxy.$modal.msgError("å¾çæå
¥å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | function resolveImageUrl(res) { |
| | | if (!res) return ""; |
| | | // å
¼å®¹æ°æ¥å£: data[0].previewURL |
| | | const previewURL = res?.data?.[0]?.previewURL; |
| | | if (previewURL) { |
| | | return previewURL; |
| | | } |
| | | // å
¼å®¹æ§æ¥å£ |
| | | if (res.url) { |
| | | return res.url; |
| | | } |
| | | if (res.fileName) { |
| | | return `${import.meta.env.VITE_APP_BASE_API}${res.fileName}`; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | // ä¸ä¼ 失败å¤ç |
| | |
| | | |
| | | function insertImage(file) { |
| | | const formData = new FormData(); |
| | | formData.append("file", file); |
| | | axios |
| | | .post(uploadUrl.value, formData, { |
| | | headers: { |
| | | "Content-Type": "multipart/form-data", |
| | | Authorization: headers.value.Authorization, |
| | | }, |
| | | }) |
| | | .then((res) => { |
| | | handleUploadSuccess(res.data); |
| | | }); |
| | | formData.append("files", file); |
| | | uploadPublicFile(formData).then((res) => { |
| | | handleUploadSuccess(res) |
| | | }) |
| | | } |
| | | </script> |
| | | |
| | |
| | | <template> |
| | | <el-table |
| | | ref="multipleTable" |
| | | v-loading="tableLoading" |
| | | :border="border" |
| | | :data="tableData" |
| | | :header-cell-style="mergedHeaderCellStyle" |
| | | :height="height" |
| | | :highlight-current-row="highlightCurrentRow" |
| | | :row-class-name="rowClassName" |
| | | :row-style="rowStyle" |
| | | :row-key="rowKey" |
| | | :style="tableStyle" |
| | | tooltip-effect="dark" |
| | | :expand-row-keys="expandRowKeys" |
| | | :show-summary="isShowSummary" |
| | | :summary-method="summaryMethod" |
| | | @row-click="rowClick" |
| | | @current-change="currentChange" |
| | | @selection-change="handleSelectionChange" |
| | | @expand-change="expandChange" |
| | | class="lims-table" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | type="selection" |
| | | width="55" |
| | | v-if="isSelection" |
| | | /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | |
| | | <el-table-column |
| | | v-for="(item, index) in column" |
| | | :key="index" |
| | | :column-key="item.columnKey" |
| | | :filter-method="item.filterHandler" |
| | | :filter-multiple="item.filterMultiple" |
| | | :filtered-value="item.filteredValue" |
| | | :filters="item.filters" |
| | | :fixed="item.fixed" |
| | | :label="item.label" |
| | | :prop="item.prop" |
| | | :show-overflow-tooltip="item.dataType !== 'action' && item.dataType !== 'slot'" |
| | | :align="item.align" |
| | | :sortable="!!item.sortable" |
| | | :type="item.type" |
| | | :width="item.width" |
| | | :minWidth="item.minWidth" |
| | | > |
| | | <el-table ref="multipleTable" |
| | | v-loading="tableLoading" |
| | | :border="border" |
| | | :data="tableData" |
| | | :header-cell-style="mergedHeaderCellStyle" |
| | | :height="height" |
| | | :highlight-current-row="highlightCurrentRow" |
| | | :row-class-name="rowClassName" |
| | | :row-style="rowStyle" |
| | | :row-key="rowKey" |
| | | :style="tableStyle" |
| | | tooltip-effect="dark" |
| | | :expand-row-keys="expandRowKeys" |
| | | :show-summary="isShowSummary" |
| | | :summary-method="summaryMethod" |
| | | @row-click="rowClick" |
| | | @current-change="currentChange" |
| | | @selection-change="handleSelectionChange" |
| | | @expand-change="expandChange" |
| | | class="lims-table"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | :selectable="selectable" |
| | | width="55" |
| | | v-if="isSelection" /> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column v-for="(item, index) in column" |
| | | :key="index" |
| | | :column-key="item.columnKey" |
| | | :filter-method="item.filterHandler" |
| | | :filter-multiple="item.filterMultiple" |
| | | :filtered-value="item.filteredValue" |
| | | :filters="item.filters" |
| | | :fixed="item.fixed" |
| | | :label="item.label" |
| | | :prop="item.prop" |
| | | :show-overflow-tooltip="item.dataType !== 'action' && item.dataType !== 'slot'" |
| | | :align="item.align" |
| | | :sortable="!!item.sortable" |
| | | :type="item.type" |
| | | :width="item.width" |
| | | :minWidth="item.minWidth"> |
| | | <template #header="scope"> |
| | | <div class="pim-table-header-cell" :class="{ 'has-extra': item.headerSlot }"> |
| | | <div class="pim-table-header-cell" |
| | | :class="{ 'has-extra': item.headerSlot }"> |
| | | <div class="pim-table-header-title"> |
| | | {{ item.label }} |
| | | </div> |
| | | <div v-if="item.headerSlot" class="pim-table-header-extra"> |
| | | <slot :name="item.headerSlot" :column="scope.column" /> |
| | | <div v-if="item.headerSlot" |
| | | class="pim-table-header-extra"> |
| | | <slot :name="item.headerSlot" |
| | | :column="scope.column" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <template |
| | | v-if="item.hasOwnProperty('colunmTemplate')" |
| | | #[item.colunmTemplate]="scope" |
| | | > |
| | | <slot |
| | | v-if="item.theadSlot" |
| | | :name="item.theadSlot" |
| | | :index="scope.$index" |
| | | :row="scope.row" |
| | | /> |
| | | <template v-if="item.hasOwnProperty('colunmTemplate')" |
| | | #[item.colunmTemplate]="scope"> |
| | | <slot v-if="item.theadSlot" |
| | | :name="item.theadSlot" |
| | | :index="scope.$index" |
| | | :row="scope.row" /> |
| | | </template> |
| | | |
| | | <template #default="scope"> |
| | | <!-- ææ§½ --> |
| | | <div v-if="item.dataType == 'slot'"> |
| | | <slot |
| | | v-if="item.slot" |
| | | :index="scope.$index" |
| | | :name="item.slot" |
| | | :row="scope.row" |
| | | /> |
| | | <slot v-if="item.slot" |
| | | :index="scope.$index" |
| | | :name="item.slot" |
| | | :row="scope.row" /> |
| | | </div> |
| | | <!-- è¿åº¦æ¡ --> |
| | | <div v-else-if="item.dataType == 'progress'"> |
| | |
| | | </div> |
| | | <!-- å¾ç --> |
| | | <div v-else-if="item.dataType == 'image'"> |
| | | <img |
| | | :src="javaApi + '/img/' + scope.row[item.prop]" |
| | | alt="" |
| | | style="width: 40px; height: 40px; margin-top: 10px" |
| | | /> |
| | | <img :src="javaApi + '/img/' + scope.row[item.prop]" |
| | | alt="" |
| | | style="width: 40px; height: 40px; margin-top: 10px" /> |
| | | </div> |
| | | |
| | | <!-- tag --> |
| | | <div v-else-if="item.dataType == 'tag'"> |
| | | <el-tag |
| | | v-if=" |
| | | <el-tag v-if=" |
| | | typeof dataTypeFn(scope.row[item.prop], item.formatData) === |
| | | 'string' |
| | | " |
| | | :title="formatters(scope.row[item.prop], item.formatData)" |
| | | :type="formatType(scope.row[item.prop], item.formatType)" |
| | | > |
| | | :title="formatters(scope.row[item.prop], item.formatData)" |
| | | :type="formatType(scope.row[item.prop], item.formatType)"> |
| | | {{ formatters(scope.row[item.prop], item.formatData) }} |
| | | </el-tag> |
| | | |
| | | <el-tag |
| | | v-for="(tag, index) in dataTypeFn( |
| | | <el-tag v-for="(tag, index) in dataTypeFn( |
| | | scope.row[item.prop], |
| | | item.formatData |
| | | )" |
| | | v-else-if=" |
| | | v-else-if=" |
| | | typeof dataTypeFn(scope.row[item.prop], item.formatData) === |
| | | 'object' |
| | | " |
| | | :key="index" |
| | | :title="formatters(scope.row[item.prop], item.formatData)" |
| | | :type="formatType(tag, item.formatType)" |
| | | > |
| | | :key="index" |
| | | :title="formatters(scope.row[item.prop], item.formatData)" |
| | | :type="formatType(tag, item.formatType)"> |
| | | {{ item.tagGroup ? tag[item.tagGroup.label] ?? tag : tag }} |
| | | </el-tag> |
| | | |
| | | <el-tag |
| | | v-else |
| | | :title="formatters(scope.row[item.prop], item.formatData)" |
| | | :type="formatType(scope.row[item.prop], item.formatType)" |
| | | > |
| | | <el-tag v-else |
| | | :title="formatters(scope.row[item.prop], item.formatData)" |
| | | :type="formatType(scope.row[item.prop], item.formatType)"> |
| | | {{ formatters(scope.row[item.prop], item.formatData) }} |
| | | </el-tag> |
| | | </div> |
| | | |
| | | <!-- æé® --> |
| | | <div v-else-if="item.dataType == 'action'" @click.stop> |
| | | <template v-for="(o, key) in item.operation" :key="key"> |
| | | <el-button |
| | | v-show="o.type != 'upload'" |
| | | v-if="o.showHide ? o.showHide(scope.row) : true" |
| | | :disabled="isOperationDisabled(o, scope.row)" |
| | | :plain="o.plain" |
| | | type="primary" |
| | | :style="{ |
| | | <div v-else-if="item.dataType == 'action'" |
| | | @click.stop> |
| | | <template v-for="(o, key) in item.operation" |
| | | :key="key"> |
| | | <el-button v-show="o.type != 'upload'" |
| | | v-if="o.showHide ? o.showHide(scope.row) : true" |
| | | :disabled="isOperationDisabled(o, scope.row)" |
| | | :plain="o.plain" |
| | | type="primary" |
| | | :style="{ |
| | | color: getOperationColor(o, scope.row), |
| | | fontWeight: 'bold', |
| | | }" |
| | | link |
| | | @click.stop="o.clickFun(scope.row)" |
| | | :key="key" |
| | | > |
| | | link |
| | | @click.stop="o.clickFun(scope.row)" |
| | | :key="key"> |
| | | {{ o.name }} |
| | | </el-button> |
| | | <el-upload |
| | | :action=" |
| | | <el-upload :action=" |
| | | javaApi + |
| | | o.url + |
| | | '?id=' + |
| | | (o.uploadIdFun ? o.uploadIdFun(scope.row) : scope.row.id) |
| | | " |
| | | ref="uploadRef" |
| | | :multiple="o.multiple ? o.multiple : false" |
| | | :limit="1" |
| | | :disabled="isOperationDisabled(o, scope.row)" |
| | | :accept=" |
| | | ref="uploadRef" |
| | | :multiple="o.multiple ? o.multiple : false" |
| | | :limit="1" |
| | | :disabled="isOperationDisabled(o, scope.row)" |
| | | :accept=" |
| | | o.accept |
| | | ? o.accept |
| | | : '.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar' |
| | | " |
| | | v-if="o.type == 'upload'" |
| | | style="display: inline-block; width: 50px" |
| | | v-show="o.showHide ? o.showHide(scope.row) : true" |
| | | :headers="uploadHeader" |
| | | :before-upload="(file) => beforeUpload(file, scope.$index)" |
| | | :on-change=" |
| | | v-if="o.type == 'upload'" |
| | | style="display: inline-block; width: 50px" |
| | | v-show="o.showHide ? o.showHide(scope.row) : true" |
| | | :headers="uploadHeader" |
| | | :before-upload="(file) => beforeUpload(file, scope.$index)" |
| | | :on-change=" |
| | | (file, fileList) => handleChange(file, fileList, scope.$index) |
| | | " |
| | | :on-error=" |
| | | :on-error=" |
| | | (error, file, fileList) => |
| | | onError(error, file, fileList, scope.$index) |
| | | " |
| | | :on-success=" |
| | | :on-success=" |
| | | (response, file, fileList) => |
| | | handleSuccessUp(response, file, fileList, scope.$index) |
| | | " |
| | | :on-exceed="onExceed" |
| | | :show-file-list="false" |
| | | > |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | :disabled="isOperationDisabled(o, scope.row)" |
| | | :style="{ |
| | | :on-exceed="onExceed" |
| | | :show-file-list="false"> |
| | | <el-button link |
| | | type="primary" |
| | | :disabled="isOperationDisabled(o, scope.row)" |
| | | :style="{ |
| | | color: getOperationColor(o, scope.row), |
| | | }" |
| | | >{{ o.name }}</el-button |
| | | > |
| | | }">{{ o.name }}</el-button> |
| | | </el-upload> |
| | | </template> |
| | | </div> |
| | | <!-- å¯ç¹å»çæå --> |
| | | <div |
| | | v-else-if="item.dataType == 'link'" |
| | | class="cell link" |
| | | style="width: 100%" |
| | | @click="goLink(scope.row, item.linkMethod)" |
| | | > |
| | | <div v-else-if="item.dataType == 'link'" |
| | | class="cell link" |
| | | style="width: 100%" |
| | | @click="goLink(scope.row, item.linkMethod)"> |
| | | <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span> |
| | | </div> |
| | | <!-- é»è®¤çº¯å±ç¤ºæ°æ® --> |
| | | <div v-else class="cell" style="width: 100%"> |
| | | <div v-else |
| | | class="cell" |
| | | style="width: 100%"> |
| | | <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span> |
| | | <span v-else>{{ |
| | | formatters(scope.row[item.prop], item.formatData) |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-if="isShowPagination" |
| | | :total="page.total" |
| | | :layout="page.layout" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationSearch" |
| | | /> |
| | | <pagination v-if="isShowPagination" |
| | | :total="page.total" |
| | | :layout="page.layout" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationSearch" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import pagination from "./Pagination.vue"; |
| | | import { computed, ref, inject, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import pagination from "./Pagination.vue"; |
| | | import { computed, ref, inject, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | // è·åå
¨å±ç uploadHeader |
| | | const { proxy } = getCurrentInstance(); |
| | | const uploadHeader = proxy.uploadHeader; |
| | | const javaApi = proxy.javaApi; |
| | | // è·åå
¨å±ç uploadHeader |
| | | const { proxy } = getCurrentInstance(); |
| | | const uploadHeader = proxy.uploadHeader; |
| | | const javaApi = proxy.javaApi; |
| | | |
| | | const emit = defineEmits(["pagination", "expand-change", "selection-change", "row-click"]); |
| | | const emit = defineEmits([ |
| | | "pagination", |
| | | "expand-change", |
| | | "selection-change", |
| | | "row-click", |
| | | ]); |
| | | |
| | | // Filters |
| | | const typeFn = (val, row) => { |
| | | return typeof val === "function" ? val(row) : val; |
| | | }; |
| | | // Filters |
| | | const typeFn = (val, row) => { |
| | | return typeof val === "function" ? val(row) : val; |
| | | }; |
| | | |
| | | const formatters = (val, format) => { |
| | | return typeof format === "function" ? format(val) : val; |
| | | }; |
| | | const formatters = (val, format) => { |
| | | return typeof format === "function" ? format(val) : val; |
| | | }; |
| | | |
| | | // Propsï¼ä½¿ç¨ defineProps çé TS å½¢å¼ï¼ |
| | | const props = defineProps({ |
| | | tableLoading: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | height: { |
| | | type: [Number, String], |
| | | default: "calc(100vh - 22em)", |
| | | }, |
| | | expandRowKeys: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | summaryMethod: { |
| | | type: Function, |
| | | default: () => {}, |
| | | }, |
| | | rowClick: { |
| | | type: Function, |
| | | default: () => {}, |
| | | }, |
| | | currentChange: { |
| | | type: Function, |
| | | default: () => {}, |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | isSelection: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | isShowPagination: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | isShowSummary: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | highlightCurrentRow: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | headerCellStyle: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | column: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | rowClassName: { |
| | | type: Function, |
| | | default: () => "", |
| | | }, |
| | | rowStyle: { |
| | | type: [Object, Function], |
| | | default: () => ({}), |
| | | }, |
| | | tableData: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | rowKey: { |
| | | type: String, |
| | | default: 'id', |
| | | }, |
| | | page: { |
| | | type: Object, |
| | | default: () => ({ |
| | | total: 0, |
| | | current: 0, |
| | | size: 10, |
| | | layout: "total, sizes, prev, pager, next, jumper", |
| | | }), |
| | | }, |
| | | total: { |
| | | type: Number, |
| | | default: 0, |
| | | }, |
| | | tableStyle: { |
| | | type: [String, Object], |
| | | default: () => ({ width: "100%" }), |
| | | }, |
| | | }); |
| | | // Propsï¼ä½¿ç¨ defineProps çé TS å½¢å¼ï¼ |
| | | const props = defineProps({ |
| | | tableLoading: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | height: { |
| | | type: [Number, String], |
| | | default: "calc(100vh - 22em)", |
| | | }, |
| | | expandRowKeys: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | summaryMethod: { |
| | | type: Function, |
| | | default: () => {}, |
| | | }, |
| | | rowClick: { |
| | | type: Function, |
| | | default: () => {}, |
| | | }, |
| | | currentChange: { |
| | | type: Function, |
| | | default: () => {}, |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | isSelection: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | selectable: { |
| | | type: Function, |
| | | default: () => true, |
| | | }, |
| | | isShowPagination: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | isShowSummary: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | highlightCurrentRow: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | headerCellStyle: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | column: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | rowClassName: { |
| | | type: Function, |
| | | default: () => "", |
| | | }, |
| | | rowStyle: { |
| | | type: [Object, Function], |
| | | default: () => ({}), |
| | | }, |
| | | tableData: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | rowKey: { |
| | | type: String, |
| | | default: "id", |
| | | }, |
| | | page: { |
| | | type: Object, |
| | | default: () => ({ |
| | | total: 0, |
| | | current: 0, |
| | | size: 10, |
| | | layout: "total, sizes, prev, pager, next, jumper", |
| | | }), |
| | | }, |
| | | total: { |
| | | type: Number, |
| | | default: 0, |
| | | }, |
| | | tableStyle: { |
| | | type: [String, Object], |
| | | default: () => ({ width: "100%" }), |
| | | }, |
| | | }); |
| | | |
| | | const mergedHeaderCellStyle = computed(() => ({ |
| | | background: "var(--surface-soft)", |
| | | color: "var(--text-secondary)", |
| | | fontWeight: 600, |
| | | ...props.headerCellStyle, |
| | | })); |
| | | const mergedHeaderCellStyle = computed(() => ({ |
| | | background: "var(--surface-soft)", |
| | | color: "var(--text-secondary)", |
| | | fontWeight: 600, |
| | | ...props.headerCellStyle, |
| | | })); |
| | | |
| | | // Data |
| | | const uploadRefs = ref([]); |
| | | const currentFiles = ref({}); |
| | | const uploadKeys = ref({}); |
| | | // Data |
| | | const uploadRefs = ref([]); |
| | | const currentFiles = ref({}); |
| | | const uploadKeys = ref({}); |
| | | |
| | | const indexMethod = (index) => { |
| | | return (props.page.current - 1) * props.page.size + index + 1; |
| | | }; |
| | | const indexMethod = index => { |
| | | return (props.page.current - 1) * props.page.size + index + 1; |
| | | }; |
| | | |
| | | // ç¹å» link äºä»¶ |
| | | const goLink = (row, linkMethod) => { |
| | | if (!linkMethod) { |
| | | return ElMessage.warning("请é
ç½® link äºä»¶"); |
| | | } |
| | | const parentMethod = getParentMethod(linkMethod); |
| | | if (typeof parentMethod === "function") { |
| | | parentMethod(row); |
| | | } else { |
| | | console.warn(`ç¶ç»ä»¶ä¸æªæ¾å°æ¹æ³: ${linkMethod}`); |
| | | } |
| | | }; |
| | | |
| | | // è·åç¶ç»ä»¶æ¹æ³ï¼ç¤ºä¾å®ç°ï¼ |
| | | const getParentMethod = (methodName) => { |
| | | const parentMethods = inject("parentMethods", {}); |
| | | return parentMethods[methodName]; |
| | | }; |
| | | |
| | | const dataTypeFn = (val, format) => { |
| | | if (typeof format === "function") { |
| | | return format(val); |
| | | } else return val; |
| | | }; |
| | | const validTagTypes = ["primary", "success", "info", "warning", "danger"]; |
| | | |
| | | const formatType = (val, format) => { |
| | | const type = typeof format === "function" ? format(val) : undefined; |
| | | return validTagTypes.includes(type) ? type : undefined; |
| | | }; |
| | | |
| | | const isOperationDisabled = (operation, row) => { |
| | | if (!operation?.disabled) return false; |
| | | return typeof operation.disabled === "function" |
| | | ? !!operation.disabled(row) |
| | | : !!operation.disabled; |
| | | }; |
| | | |
| | | const parseHexToRgb = (hex) => { |
| | | const normalized = String(hex || "").trim().replace("#", ""); |
| | | if (normalized.length === 3) { |
| | | const r = parseInt(normalized[0] + normalized[0], 16); |
| | | const g = parseInt(normalized[1] + normalized[1], 16); |
| | | const b = parseInt(normalized[2] + normalized[2], 16); |
| | | if ([r, g, b].some((n) => Number.isNaN(n))) return null; |
| | | return { r, g, b }; |
| | | } |
| | | if (normalized.length === 6 || normalized.length === 8) { |
| | | const r = parseInt(normalized.slice(0, 2), 16); |
| | | const g = parseInt(normalized.slice(2, 4), 16); |
| | | const b = parseInt(normalized.slice(4, 6), 16); |
| | | if ([r, g, b].some((n) => Number.isNaN(n))) return null; |
| | | return { r, g, b }; |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const fadeColor = (color, alpha = 0.35) => { |
| | | const c = String(color || "").trim(); |
| | | if (!c) return undefined; |
| | | if (c.startsWith("#")) { |
| | | const rgb = parseHexToRgb(c); |
| | | if (!rgb) return c; |
| | | return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`; |
| | | } |
| | | const rgbMatch = c.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*[\d.]+\s*)?\)$/i); |
| | | if (rgbMatch) { |
| | | const r = Number(rgbMatch[1]); |
| | | const g = Number(rgbMatch[2]); |
| | | const b = Number(rgbMatch[3]); |
| | | if ([r, g, b].some((n) => Number.isNaN(n))) return c; |
| | | return `rgba(${r}, ${g}, ${b}, ${alpha})`; |
| | | } |
| | | if (c.includes("--el-color-primary")) { |
| | | return "var(--el-color-primary-light-5)"; |
| | | } |
| | | if (c.includes("--el-color-danger")) { |
| | | return "var(--el-color-danger-light-5)"; |
| | | } |
| | | return "var(--el-text-color-disabled)"; |
| | | }; |
| | | |
| | | const getOperationColor = (operation, row) => { |
| | | const baseColor = |
| | | operation?.name === "å é¤" || operation?.name === "delete" |
| | | ? "#D93025" |
| | | : operation?.name === "详æ
" |
| | | ? "#67C23A" |
| | | : operation?.color || "var(--el-color-primary)"; |
| | | |
| | | if (isOperationDisabled(operation, row)) { |
| | | return fadeColor(baseColor, 0.35); |
| | | } |
| | | return baseColor; |
| | | }; |
| | | |
| | | // æä»¶ååå¤ç |
| | | const handleChange = (file, fileList, index) => { |
| | | if (fileList.length > 1) { |
| | | const earliestFile = fileList[0]; |
| | | uploadRefs.value[index]?.handleRemove(earliestFile); |
| | | } |
| | | currentFiles.value[index] = file; |
| | | }; |
| | | |
| | | // æä»¶ä¸ä¼ åæ ¡éª |
| | | const beforeUpload = (rawFile, index) => { |
| | | currentFiles.value[index] = {}; |
| | | if (rawfile.size > 1024 * 1024 * 10 * 10) { |
| | | ElMessage.error("ä¸ä¼ æä»¶ä¸è¶
è¿10M"); |
| | | return false; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | // ä¸ä¼ æå |
| | | const handleSuccessUp = (response, file, fileList, index) => { |
| | | if (response.code == 200) { |
| | | if (uploadRefs[index]) { |
| | | uploadRefs[index].clearFiles(); |
| | | // ç¹å» link äºä»¶ |
| | | const goLink = (row, linkMethod) => { |
| | | if (!linkMethod) { |
| | | return ElMessage.warning("请é
ç½® link äºä»¶"); |
| | | } |
| | | currentFiles[index] = file; |
| | | ElMessage.success("ä¸ä¼ æå"); |
| | | resetUploadComponent(index); |
| | | } else { |
| | | ElMessage.error(response.message); |
| | | } |
| | | }; |
| | | const parentMethod = getParentMethod(linkMethod); |
| | | if (typeof parentMethod === "function") { |
| | | parentMethod(row); |
| | | } else { |
| | | console.warn(`ç¶ç»ä»¶ä¸æªæ¾å°æ¹æ³: ${linkMethod}`); |
| | | } |
| | | }; |
| | | |
| | | const resetUploadComponent = (index) => { |
| | | uploadKeys[index] = Date.now(); |
| | | }; |
| | | // è·åç¶ç»ä»¶æ¹æ³ï¼ç¤ºä¾å®ç°ï¼ |
| | | const getParentMethod = methodName => { |
| | | const parentMethods = inject("parentMethods", {}); |
| | | return parentMethods[methodName]; |
| | | }; |
| | | |
| | | // ä¸ä¼ 失败 |
| | | const onError = (error, file, fileList, index) => { |
| | | ElMessage.error("æä»¶ä¸ä¼ 失败ï¼è¯·éè¯"); |
| | | if (uploadRefs.value[index]) { |
| | | uploadRefs.value[index].clearFiles(); |
| | | } |
| | | }; |
| | | const dataTypeFn = (val, format) => { |
| | | if (typeof format === "function") { |
| | | return format(val); |
| | | } else return val; |
| | | }; |
| | | const validTagTypes = ["primary", "success", "info", "warning", "danger"]; |
| | | |
| | | // æä»¶æ°éè¶
éæç¤º |
| | | const onExceed = () => { |
| | | ElMessage.warning("è¶
åºæä»¶ä¸ªæ°"); |
| | | }; |
| | | const formatType = (val, format) => { |
| | | const type = typeof format === "function" ? format(val) : undefined; |
| | | return validTagTypes.includes(type) ? type : undefined; |
| | | }; |
| | | |
| | | const paginationSearch = ({ page, limit }) => { |
| | | emit("pagination", { page: page, limit: limit }); |
| | | }; |
| | | const isOperationDisabled = (operation, row) => { |
| | | if (!operation?.disabled) return false; |
| | | return typeof operation.disabled === "function" |
| | | ? !!operation.disabled(row) |
| | | : !!operation.disabled; |
| | | }; |
| | | |
| | | const rowClick = (row) => { |
| | | emit("row-click", row); |
| | | }; |
| | | const parseHexToRgb = hex => { |
| | | const normalized = String(hex || "") |
| | | .trim() |
| | | .replace("#", ""); |
| | | if (normalized.length === 3) { |
| | | const r = parseInt(normalized[0] + normalized[0], 16); |
| | | const g = parseInt(normalized[1] + normalized[1], 16); |
| | | const b = parseInt(normalized[2] + normalized[2], 16); |
| | | if ([r, g, b].some(n => Number.isNaN(n))) return null; |
| | | return { r, g, b }; |
| | | } |
| | | if (normalized.length === 6 || normalized.length === 8) { |
| | | const r = parseInt(normalized.slice(0, 2), 16); |
| | | const g = parseInt(normalized.slice(2, 4), 16); |
| | | const b = parseInt(normalized.slice(4, 6), 16); |
| | | if ([r, g, b].some(n => Number.isNaN(n))) return null; |
| | | return { r, g, b }; |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const expandChange = (row, expandedRows) => { |
| | | emit("expand-change", row, expandedRows); |
| | | }; |
| | | const fadeColor = (color, alpha = 0.35) => { |
| | | const c = String(color || "").trim(); |
| | | if (!c) return undefined; |
| | | if (c.startsWith("#")) { |
| | | const rgb = parseHexToRgb(c); |
| | | if (!rgb) return c; |
| | | return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`; |
| | | } |
| | | const rgbMatch = c.match( |
| | | /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*[\d.]+\s*)?\)$/i |
| | | ); |
| | | if (rgbMatch) { |
| | | const r = Number(rgbMatch[1]); |
| | | const g = Number(rgbMatch[2]); |
| | | const b = Number(rgbMatch[3]); |
| | | if ([r, g, b].some(n => Number.isNaN(n))) return c; |
| | | return `rgba(${r}, ${g}, ${b}, ${alpha})`; |
| | | } |
| | | if (c.includes("--el-color-primary")) { |
| | | return "var(--el-color-primary-light-5)"; |
| | | } |
| | | if (c.includes("--el-color-danger")) { |
| | | return "var(--el-color-danger-light-5)"; |
| | | } |
| | | return "var(--el-text-color-disabled)"; |
| | | }; |
| | | |
| | | const handleSelectionChange = (newSelection) => { |
| | | emit("selection-change", newSelection); |
| | | }; |
| | | const getOperationColor = (operation, row) => { |
| | | const baseColor = |
| | | operation?.name === "å é¤" || operation?.name === "delete" |
| | | ? "#D93025" |
| | | : operation?.name === "详æ
" |
| | | ? "#67C23A" |
| | | : operation?.color || "var(--el-color-primary)"; |
| | | |
| | | if (isOperationDisabled(operation, row)) { |
| | | return fadeColor(baseColor, 0.35); |
| | | } |
| | | return baseColor; |
| | | }; |
| | | |
| | | // æä»¶ååå¤ç |
| | | const handleChange = (file, fileList, index) => { |
| | | if (fileList.length > 1) { |
| | | const earliestFile = fileList[0]; |
| | | uploadRefs.value[index]?.handleRemove(earliestFile); |
| | | } |
| | | currentFiles.value[index] = file; |
| | | }; |
| | | |
| | | // æä»¶ä¸ä¼ åæ ¡éª |
| | | const beforeUpload = (rawFile, index) => { |
| | | currentFiles.value[index] = {}; |
| | | if (rawfile.size > 1024 * 1024 * 10 * 10) { |
| | | ElMessage.error("ä¸ä¼ æä»¶ä¸è¶
è¿10M"); |
| | | return false; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | // ä¸ä¼ æå |
| | | const handleSuccessUp = (response, file, fileList, index) => { |
| | | if (response.code == 200) { |
| | | if (uploadRefs[index]) { |
| | | uploadRefs[index].clearFiles(); |
| | | } |
| | | currentFiles[index] = file; |
| | | ElMessage.success("ä¸ä¼ æå"); |
| | | resetUploadComponent(index); |
| | | } else { |
| | | ElMessage.error(response.message); |
| | | } |
| | | }; |
| | | |
| | | const resetUploadComponent = index => { |
| | | uploadKeys[index] = Date.now(); |
| | | }; |
| | | |
| | | // ä¸ä¼ 失败 |
| | | const onError = (error, file, fileList, index) => { |
| | | ElMessage.error("æä»¶ä¸ä¼ 失败ï¼è¯·éè¯"); |
| | | if (uploadRefs.value[index]) { |
| | | uploadRefs.value[index].clearFiles(); |
| | | } |
| | | }; |
| | | |
| | | // æä»¶æ°éè¶
éæç¤º |
| | | const onExceed = () => { |
| | | ElMessage.warning("è¶
åºæä»¶ä¸ªæ°"); |
| | | }; |
| | | |
| | | const paginationSearch = ({ page, limit }) => { |
| | | emit("pagination", { page: page, limit: limit }); |
| | | }; |
| | | |
| | | const rowClick = row => { |
| | | emit("row-click", row); |
| | | }; |
| | | |
| | | const expandChange = (row, expandedRows) => { |
| | | emit("expand-change", row, expandedRows); |
| | | }; |
| | | |
| | | const handleSelectionChange = newSelection => { |
| | | emit("selection-change", newSelection); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .lims-table { |
| | | border: 1px solid var(--surface-border); |
| | | border-radius: 18px; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | } |
| | | .lims-table { |
| | | border: 1px solid var(--surface-border); |
| | | border-radius: 18px; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | } |
| | | |
| | | .cell { |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | padding-right: 0 !important; |
| | | padding-left: 0 !important; |
| | | } |
| | | .cell { |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | padding-right: 0 !important; |
| | | padding-left: 0 !important; |
| | | } |
| | | |
| | | .pim-table-header-extra :deep(.el-input), |
| | | .pim-table-header-extra :deep(.el-select) { |
| | | width: 100%; |
| | | } |
| | | .pim-table-header-extra :deep(.el-input), |
| | | .pim-table-header-extra :deep(.el-select) { |
| | | width: 100%; |
| | | } |
| | | |
| | | .pim-table-header-title { |
| | | font-weight: 600; |
| | | } |
| | | .pim-table-header-title { |
| | | font-weight: 600; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="visible" |
| | | :title="title" |
| | | width="800px" |
| | | destroy-on-close> |
| | | <div class="param-list-container"> |
| | | <div class="params-header"> |
| | | <span>åæ°å表</span> |
| | | <div> |
| | | <el-button v-if="editable" |
| | | type="primary" |
| | | link |
| | | size="small" |
| | | @click="handleAddParam"> |
| | | <el-icon> |
| | | <Plus /> |
| | | </el-icon>æ°å¢ |
| | | </el-button> |
| | | <!-- <el-button v-if="editable" |
| | | type="primary" |
| | | link |
| | | size="small" |
| | | @click="getsyncProcessParamItem"> |
| | | <el-icon> |
| | | <Refresh /> |
| | | </el-icon>忥工åºåæ° |
| | | </el-button> --> |
| | | </div> |
| | | </div> |
| | | <div class="params-list"> |
| | | <div v-for="param in paramList" |
| | | :key="param.id" |
| | | class="param-item"> |
| | | <div class="param-info"> |
| | | <span class="param-code">{{ param.paramName }}</span> |
| | | <span class="param-value"> |
| | | æ åå¼ï¼{{ param.standardValue || "-" }} {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | <div class="param-actions"> |
| | | <el-button v-if="editable" |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="handleEditParam(param)"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button v-if="editable" |
| | | link |
| | | type="danger" |
| | | size="small" |
| | | @click="handleDeleteParam(param)"> |
| | | å é¤ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <el-empty v-if="!paramList || paramList.length === 0" |
| | | description="ææ åæ°" |
| | | :image-size="50" /> |
| | | </div> |
| | | </div> |
| | | <!-- éæ©åæ°å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="selectParamDialogVisible" |
| | | title="鿩忰" |
| | | width="1000px"> |
| | | <div class="param-select-container"> |
| | | <!-- 左侧忰å表 --> |
| | | <div class="param-list-area"> |
| | | <div class="area-title">å¯éåæ°</div> |
| | | <div class="search-box"> |
| | | <el-input v-model="paramSearchKeyword" |
| | | placeholder="请è¾å
¥åæ°åç§°æç´¢" |
| | | clearable |
| | | size="small" |
| | | @input="getBaseParamListData"> |
| | | <template #prefix> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | </template> |
| | | </el-input> |
| | | </div> |
| | | <el-table :data="filteredParamList" |
| | | height="400" |
| | | border |
| | | highlight-current-row |
| | | @current-change="handleSelectParam"> |
| | | <el-table-column prop="paramName" |
| | | label="åæ°åç§°" /> |
| | | <el-table-column prop="paramType" |
| | | label="åæ°ç±»å"> |
| | | <template #default="scope"> |
| | | <el-tag size="small" |
| | | :type="getParamTypeTag(scope.row.paramType)">{{ getParamTypeText(scope.row.paramType) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <!-- å页æ§ä»¶ --> |
| | | <div class="pagination-container" |
| | | style="margin-top: 10px;"> |
| | | <el-pagination :current-page="paramPage.current" |
| | | :page-size="paramPage.size" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="paramPage.total" |
| | | @size-change="getBaseParamListData" |
| | | @current-change="getBaseParamListData" |
| | | size="small" /> |
| | | </div> |
| | | </div> |
| | | <!-- å³ä¾§åæ°è¯¦æ
--> |
| | | <div class="param-detail-area"> |
| | | <div class="area-title">åæ°è¯¦æ
</div> |
| | | <el-form v-if="selectedParam" |
| | | :model="selectedParam" |
| | | label-width="100px" |
| | | class="param-detail-form"> |
| | | <el-form-item label="åæ°åç§°"> |
| | | <span class="detail-text">{{ selectedParam.paramName }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="åæ°ç±»å"> |
| | | <el-tag size="small" |
| | | :type="getParamTypeTag(selectedParam.paramType)">{{ getParamTypeText(selectedParam.paramType) }}</el-tag> |
| | | </el-form-item> |
| | | <el-form-item label="åæ°æ ¼å¼"> |
| | | <span class="detail-text">{{ selectedParam.paramFormat || '-' }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="åä½"> |
| | | <span class="detail-text">{{ selectedParam.unit || '-' }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="æ åå¼"> |
| | | <el-input v-model="selectedParam.standardValue" |
| | | @input="val => onStandardValueInput(val, selectedParam)" |
| | | placeholder="请è¾å
¥é»è®¤å¼" /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¯å¦å¿
å¡«"> |
| | | <el-switch :active-value="1" |
| | | :inactive-value="0" |
| | | v-model="selectedParam.isRequired" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <el-empty v-else |
| | | description="请ä»å·¦ä¾§éæ©åæ°" |
| | | :image-size="100" /> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <el-button type="primary" |
| | | @click="handleParamSelectSubmit">ç¡®å®</el-button> |
| | | <el-button @click="selectParamDialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- ç¼è¾åæ°å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="editParamDialogVisible" |
| | | title="ç¼è¾åæ°" |
| | | width="600px"> |
| | | <el-form :model="editParamForm" |
| | | :rules="editParamRules" |
| | | ref="editParamFormRef" |
| | | label-width="120px"> |
| | | <el-form-item label="åæ°åç§°"> |
| | | <span class="detail-text">{{ editParamForm.paramName }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="åæ°ç±»å"> |
| | | <el-tag size="small" |
| | | :type="getParamTypeTag(editParamForm.paramType)"> |
| | | {{ getParamTypeText(editParamForm.paramType) }} |
| | | </el-tag> |
| | | </el-form-item> |
| | | <el-form-item label="åæ°æ ¼å¼"> |
| | | <span class="detail-text">{{ editParamForm.paramFormat || '-' }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="åä½"> |
| | | <span class="detail-text">{{ editParamForm.unit || '-' }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="æ åå¼" |
| | | prop="standardValue"> |
| | | <el-input v-model="editParamForm.standardValue" |
| | | @input="val => onStandardValueInput(val, editParamForm)" |
| | | placeholder="请è¾å
¥æ åå¼" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" |
| | | @click="handleEditParamSubmit">ç¡®å®</el-button> |
| | | <el-button @click="editParamDialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, watch } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | delProcessRouteItemParam, |
| | | editProcessRouteItemParam, |
| | | addProcessRouteItemParam, |
| | | } from "@/api/productionManagement/processRouteItem.js"; |
| | | import { |
| | | addProcessRouteItemParamOrder, |
| | | delProcessRouteItemParamOrder, |
| | | editProcessRouteItemParamOrder, |
| | | } from "@/api/productionManagement/productProcessRoute.js"; |
| | | |
| | | import { getBaseParamList } from "@/api/basicData/parameterMaintenance.js"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: "åæ°å表", |
| | | }, |
| | | routeId: { |
| | | type: Number, |
| | | default: 0, |
| | | }, |
| | | process: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | paramList: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | editable: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | orderId: { |
| | | type: Number, |
| | | default: 0, |
| | | }, |
| | | pageType: { |
| | | type: String, |
| | | default: "route", |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:modelValue", "refresh"]); |
| | | |
| | | const visible = computed({ |
| | | get: () => props.modelValue, |
| | | set: value => emit("update:modelValue", value), |
| | | }); |
| | | |
| | | // ååºå¼æ°æ® |
| | | const selectParamDialogVisible = ref(false); |
| | | const editParamDialogVisible = ref(false); |
| | | const paramSearchKeyword = ref(""); |
| | | const selectedParam = ref(null); |
| | | const filteredParamList = ref([]); |
| | | const paramPage = ref({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | const editParamForm = ref({ |
| | | id: null, |
| | | processId: null, |
| | | paramId: null, |
| | | paramName: "", |
| | | standardValue: null, |
| | | isRequired: 0, |
| | | paramType: null, |
| | | paramFormat: "", |
| | | unit: "", |
| | | }); |
| | | |
| | | const onStandardValueInput = (val, target) => { |
| | | const data = target.value || target; |
| | | const type = data.paramType || data.parameterType; |
| | | if (type === 1) { |
| | | // æ°å¼æ ¼å¼ï¼ä¸è½è¾å
¥ä¸ææè±æå符 |
| | | data.standardValue = val.replace(/[a-zA-Z\u4e00-\u9fa5]/g, ""); |
| | | } |
| | | }; |
| | | |
| | | const editParamRules = ref({ |
| | | standardValue: [ |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | const type = |
| | | editParamForm.value.paramType || editParamForm.value.parameterType; |
| | | if (type === 1 && value) { |
| | | if (/[a-zA-Z\u4e00-\u9fa5]/.test(value)) { |
| | | return callback(new Error("æ°å¼æ ¼å¼ä¸è½å
å«ä¸è±æå符")); |
| | | } |
| | | } |
| | | callback(); |
| | | }, |
| | | trigger: "blur", |
| | | }, |
| | | ], |
| | | }); |
| | | const editParamFormRef = ref(null); |
| | | |
| | | // æ°å¢åæ° |
| | | const handleAddParam = () => { |
| | | selectedParam.value = null; |
| | | paramSearchKeyword.value = ""; |
| | | paramPage.value.current = 1; |
| | | // è·åå¯éåæ°å表 |
| | | getBaseParamListData(); |
| | | selectParamDialogVisible.value = true; |
| | | }; |
| | | |
| | | // ç¼è¾åæ° |
| | | const handleEditParam = param => { |
| | | editParamForm.value = { |
| | | id: param.id, |
| | | processId: props.process.id, |
| | | paramId: param.paramId, |
| | | paramName: param.parameterName || param.paramName, |
| | | standardValue: param.standardValue, |
| | | isRequired: param.isRequired || 0, |
| | | paramType: param.parameterType || param.paramType, |
| | | paramFormat: param.parameterFormat || param.paramFormat, |
| | | unit: param.unit || param.unit, |
| | | }; |
| | | editParamDialogVisible.value = true; |
| | | }; |
| | | |
| | | // å é¤åæ° |
| | | const handleDeleteParam = param => { |
| | | ElMessageBox.confirm("ç¡®å®è¦å é¤è¯¥åæ°åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // è°ç¨APIå é¤åæ° |
| | | if (props.pageType === "order") { |
| | | delProcessRouteItemParamOrder(param.id) |
| | | .then(res => { |
| | | ElMessage.success("å 餿å"); |
| | | emit("refresh"); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error("å é¤åæ°å¤±è´¥"); |
| | | console.error("å é¤åæ°å¤±è´¥ï¼", err); |
| | | }); |
| | | } else { |
| | | delProcessRouteItemParam(param.id) |
| | | .then(res => { |
| | | ElMessage.success("å 餿å"); |
| | | emit("refresh"); |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error("å é¤åæ°å¤±è´¥"); |
| | | console.error("å é¤åæ°å¤±è´¥ï¼", err); |
| | | }); |
| | | } |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | const getsyncProcessParamItem = () => { |
| | | emit("getsyncProcessParamItem"); |
| | | }; |
| | | |
| | | // è·åå¯éåæ°å表 |
| | | const getBaseParamListData = () => { |
| | | console.log(paramPage, "paramPage.size"); |
| | | |
| | | getBaseParamList({ |
| | | paramName: paramSearchKeyword.value, |
| | | current: paramPage.value.current, |
| | | size: paramPage.value.size, |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | filteredParamList.value = res.data?.records || []; |
| | | paramPage.value.total = res.data.total || 0; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 鿩忰 |
| | | const handleSelectParam = param => { |
| | | selectedParam.value = param; |
| | | }; |
| | | |
| | | // æäº¤éæ©åæ° |
| | | const handleParamSelectSubmit = () => { |
| | | if (!selectedParam.value) { |
| | | ElMessage.warning("请å
éæ©ä¸ä¸ªåæ°"); |
| | | return; |
| | | } |
| | | |
| | | if (!props.process || !props.process.id) { |
| | | ElMessage.error("å·¥èºè·¯çº¿é¡¹ç®ä¿¡æ¯ä¸å®æ´"); |
| | | return; |
| | | } |
| | | |
| | | // è°ç¨APIæ°å¢åæ° |
| | | if (props.pageType === "order") { |
| | | addProcessRouteItemParamOrder({ |
| | | productionOrderId: Number(props.orderId), |
| | | productionOrderRoutingOperationId: props.process.id, |
| | | technologyRoutingOperationParamId: props.process.id, |
| | | paramId: selectedParam.value.id, |
| | | standardValue: selectedParam.value.standardValue || "", |
| | | isRequired: selectedParam.value.isRequired || 0, |
| | | }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("æ·»å åæ°æå"); |
| | | selectParamDialogVisible.value = false; |
| | | emit("refresh"); |
| | | } else { |
| | | ElMessage.error(res.msg || "æ·»å åæ°å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error("æ·»å åæ°å¤±è´¥"); |
| | | console.error("æ·»å åæ°å¤±è´¥ï¼", err); |
| | | }); |
| | | } else { |
| | | console.log(selectedParam.value, "selectedParam"); |
| | | |
| | | addProcessRouteItemParam({ |
| | | technologyRoutingOperationId: props.process.id, |
| | | paramId: selectedParam.value.id, |
| | | standardValue: selectedParam.value.standardValue || "", |
| | | isRequired: selectedParam.value.isRequired || 0, |
| | | }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("æ·»å åæ°æå"); |
| | | selectParamDialogVisible.value = false; |
| | | emit("refresh"); |
| | | } else { |
| | | ElMessage.error(res.msg || "æ·»å åæ°å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error("æ·»å åæ°å¤±è´¥"); |
| | | console.error("æ·»å åæ°å¤±è´¥ï¼", err); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // æäº¤ç¼è¾åæ° |
| | | const handleEditParamSubmit = () => { |
| | | if (!editParamFormRef.value) return; |
| | | editParamFormRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (props.pageType === "order") { |
| | | editProcessRouteItemParamOrder({ |
| | | id: editParamForm.value.id, |
| | | standardValue: editParamForm.value.standardValue || "", |
| | | isRequired: editParamForm.value.isRequired || 0, |
| | | // productionOrderRoutingOperationId: props.process.id, |
| | | }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("ç¼è¾æå"); |
| | | editParamDialogVisible.value = false; |
| | | emit("refresh"); |
| | | } else { |
| | | ElMessage.error(res.msg || "ç¼è¾å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error("ç¼è¾åæ°å¤±è´¥"); |
| | | console.error("ç¼è¾åæ°å¤±è´¥ï¼", err); |
| | | }); |
| | | } else { |
| | | // è°ç¨APIä¿®æ¹åæ° |
| | | editProcessRouteItemParam({ |
| | | id: editParamForm.value.id, |
| | | technologyRoutingOperationId: props.process.id, |
| | | paramId: editParamForm.value.paramId, |
| | | standardValue: editParamForm.value.standardValue || "", |
| | | isRequired: editParamForm.value.isRequired || 0, |
| | | }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("ç¼è¾æå"); |
| | | editParamDialogVisible.value = false; |
| | | emit("refresh"); |
| | | } else { |
| | | ElMessage.error(res.msg || "ç¼è¾å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error("ç¼è¾åæ°å¤±è´¥"); |
| | | console.error("ç¼è¾åæ°å¤±è´¥ï¼", err); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // è·ååæ°ç±»åæ ç¾ |
| | | const getParamTypeTag = type => { |
| | | const typeMap = { |
| | | 1: "primary", |
| | | 2: "info", |
| | | 3: "warning", |
| | | 4: "success", |
| | | }; |
| | | return typeMap[type] || "default"; |
| | | }; |
| | | |
| | | // è·ååæ°ç±»åææ¬ |
| | | const getParamTypeText = type => { |
| | | const typeMap = { |
| | | 1: "æ°å¼æ ¼å¼", |
| | | 2: "ææ¬æ ¼å¼", |
| | | 3: "䏿é项", |
| | | 4: "æ¶é´æ ¼å¼", |
| | | }; |
| | | return typeMap[type] || type; |
| | | }; |
| | | |
| | | watch( |
| | | () => props.modelValue, |
| | | newVal => { |
| | | if (!newVal) { |
| | | // å¼¹çªå
³éæ¶éç½®æ°æ® |
| | | selectParamDialogVisible.value = false; |
| | | editParamDialogVisible.value = false; |
| | | selectedParam.value = null; |
| | | paramSearchKeyword.value = ""; |
| | | paramPage.value.current = 1; |
| | | filteredParamList.value = []; |
| | | editParamForm.value = { |
| | | id: null, |
| | | processId: null, |
| | | paramId: null, |
| | | paramName: "", |
| | | standardValue: null, |
| | | isRequired: 0, |
| | | paramType: null, |
| | | paramFormat: "", |
| | | unit: "", |
| | | }; |
| | | } |
| | | } |
| | | ); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .param-list-container { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .params-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | padding-bottom: 10px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .params-header span { |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | } |
| | | |
| | | .params-list { |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .param-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 12px 16px; |
| | | margin-bottom: 8px; |
| | | background-color: #f9f9f9; |
| | | border-radius: 4px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .param-item:hover { |
| | | background-color: #ecf5ff; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .param-info { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .param-code { |
| | | font-weight: 500; |
| | | color: #303133; |
| | | min-width: 120px; |
| | | } |
| | | |
| | | .param-value { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .param-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | /* æ»å¨æ¡æ ·å¼ */ |
| | | .params-list::-webkit-scrollbar { |
| | | width: 6px; |
| | | } |
| | | |
| | | .params-list::-webkit-scrollbar-track { |
| | | background: #f1f1f1; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | .params-list::-webkit-scrollbar-thumb { |
| | | background: #c1c1c1; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | .params-list::-webkit-scrollbar-thumb:hover { |
| | | background: #a8a8a8; |
| | | } |
| | | |
| | | /* éæ©åæ°å¯¹è¯æ¡æ ·å¼ */ |
| | | .param-select-container { |
| | | display: flex; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .param-list-area { |
| | | flex: 1; |
| | | min-width: 400px; |
| | | } |
| | | |
| | | .param-detail-area { |
| | | flex: 1; |
| | | min-width: 300px; |
| | | } |
| | | |
| | | .area-title { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | margin-bottom: 10px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .search-box { |
| | | display: flex; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .param-detail-form { |
| | | background: #f9f9f9; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .detail-text { |
| | | font-weight: 500; |
| | | } |
| | | </style> |
| | |
| | | </el-row> |
| | | |
| | | <el-form-item label="éä»¶" prop="attachmentIds"> |
| | | <el-upload |
| | | v-model:file-list="fileList" |
| | | :action="upload.url" |
| | | :headers="upload.headers" |
| | | multiple |
| | | name="files" |
| | | :on-success="handleUploadSuccess" |
| | | :on-error="handleUploadError" |
| | | :on-remove="handleRemove" |
| | | > |
| | | <el-button type="primary">ä¸ä¼ æä»¶</el-button> |
| | | </el-upload> |
| | | <FileUpload v-model:file-list="form.storageBlobDTOs" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="visible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submit">ç¡®å®</el-button> |
| | | <el-button @click="visible = false">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | import { computed, reactive, ref, watch } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { getToken } from '@/utils/auth' |
| | | import FileUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { type: Boolean, default: false }, |
| | |
| | | }) |
| | | |
| | | const formRef = ref() |
| | | const fileList = ref([]) |
| | | const upload = reactive({ |
| | | url: import.meta.env.VITE_APP_BASE_API + '/basic/customer-follow/upload', |
| | | headers: { Authorization: 'Bearer ' + getToken() } |
| | | }) |
| | | |
| | | const form = ref({ |
| | | planNodeId: undefined, |
| | |
| | | managerName: '', |
| | | departmentName: '', |
| | | remark: '', |
| | | attachmentIds: [] |
| | | storageBlobDTOs: [] |
| | | }) |
| | | |
| | | const rules = { |
| | |
| | | managerName: info.managerName || '', |
| | | departmentName: info.departmentName || '', |
| | | remark: '', |
| | | attachmentIds: [] |
| | | storageBlobDTOs: [] |
| | | } |
| | | fileList.value = [] |
| | | } |
| | | |
| | | watch( |
| | |
| | | form.value.completionProgress = 100 |
| | | form.value.totalProgress = 100 |
| | | if (!form.value.actualEndTime) form.value.actualEndTime = form.value.reportDate || '' |
| | | } |
| | | |
| | | function handleUploadError() { |
| | | ElMessage.error('ä¸ä¼ æä»¶å¤±è´¥') |
| | | } |
| | | |
| | | function handleUploadSuccess(res, file) { |
| | | if (res?.code !== 200) { |
| | | ElMessage.error(res?.msg || 'ä¸ä¼ 失败') |
| | | return |
| | | } |
| | | const attachmentId = res?.data?.id ?? res?.data?.tempId ?? '' |
| | | if (!attachmentId) return |
| | | form.value.attachmentIds.push(attachmentId) |
| | | try { |
| | | file.attachmentId = attachmentId |
| | | } catch (e) {} |
| | | ElMessage.success('ä¸ä¼ æå') |
| | | } |
| | | |
| | | function handleRemove(file) { |
| | | const attachmentId = file?.attachmentId |
| | | if (!attachmentId) return |
| | | form.value.attachmentIds = (form.value.attachmentIds || []).filter(id => id !== attachmentId) |
| | | } |
| | | |
| | | async function submit() { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <AIChatSidebar :assistants="assistants" default-assistant="purchase" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import AIChatSidebar from '@/components/AIChatSidebar/index.vue' |
| | | import { purchaseAssistant } from '@/components/AIChatSidebar/assistants' |
| | | |
| | | const assistants = [purchaseAssistant] |
| | | </script> |
| | |
| | | props: {
|
| | | iconClass: {
|
| | | type: String,
|
| | | required: true
|
| | | default: ''
|
| | | },
|
| | | className: {
|
| | | type: String,
|
| | |
| | | transformData: (workbookData) => workbookData, |
| | | }); |
| | | |
| | | // 计ç®å±æ§ - 夿æä»¶ç±»å |
| | | // 计ç®å±æ§ - 夿æä»¶ç±»åï¼æ¯æURL带æ¥è¯¢åæ°ï¼ |
| | | const isImage = computed(() => { |
| | | const state = /\.(jpg|jpeg|png|gif)$/i.test(fileUrl.value); |
| | | const state = /\.(jpg|jpeg|png|gif)(\?.*)?$/i.test(fileUrl.value); |
| | | if (state) { |
| | | imgUrl.value = fileUrl.value.replaceAll('word', 'img'); |
| | | } |
| | |
| | | |
| | | const isPdf = computed(() => { |
| | | console.log(fileUrl.value) |
| | | return /\.pdf$/i.test(fileUrl.value); |
| | | return /\.pdf(\?.*)?$/i.test(fileUrl.value); |
| | | }); |
| | | |
| | | const isDoc = computed(() => { |
| | | return /\.(doc|docx)$/i.test(fileUrl.value); |
| | | return /\.(doc|docx)(\?.*)?$/i.test(fileUrl.value); |
| | | }); |
| | | |
| | | const isXls = computed(() => { |
| | | const state = /\.(xls|xlsx)$/i.test(fileUrl.value); |
| | | const state = /\.(xls|xlsx)(\?.*)?$/i.test(fileUrl.value); |
| | | if (state) { |
| | | options.value.xls = /\.(xls)$/i.test(fileUrl.value); |
| | | options.value.xls = /\.(xls)(\?.*)?$/i.test(fileUrl.value); |
| | | } |
| | | return state; |
| | | }); |
| | | |
| | | const isZipOrRar = computed(() => { |
| | | return /\.(zip|rar)$/i.test(fileUrl.value); |
| | | return /\.(zip|rar)(\?.*)?$/i.test(fileUrl.value); |
| | | }); |
| | | |
| | | const isSupported = computed(() => { |
| | |
| | | }; |
| | | |
| | | const open = (url) => { |
| | | fileUrl.value = window.location.protocol+'//'+window.location.host+ url; |
| | | fileUrl.value = url; |
| | | dialogVisible.value = true; |
| | | }; |
| | | const handleClose = () => { |
| | |
| | | <template> |
| | | <div :class="{ 'has-logo': showLogo }" class="sidebar-container"> |
| | | <logo v-if="showLogo" :collapse="isCollapse" /> |
| | | <div :class="{ 'has-logo': showLogo }" |
| | | class="sidebar-container"> |
| | | <logo v-if="showLogo" |
| | | :collapse="isCollapse" /> |
| | | <el-scrollbar wrap-class="scrollbar-wrapper"> |
| | | <el-menu |
| | | :default-active="activeMenu" |
| | | :collapse="isCollapse" |
| | | :background-color="getMenuBackground" |
| | | :text-color="getMenuTextColor" |
| | | :unique-opened="true" |
| | | :active-text-color="theme" |
| | | :collapse-transition="false" |
| | | mode="vertical" |
| | | :class="sideTheme" |
| | | > |
| | | <sidebar-item |
| | | v-for="(route, index) in sidebarRouters" |
| | | :key="route.path + index" |
| | | :item="route" |
| | | :base-path="route.path" |
| | | /> |
| | | <el-menu :default-active="activeMenu" |
| | | :collapse="isCollapse" |
| | | :background-color="getMenuBackground" |
| | | :text-color="getMenuTextColor" |
| | | :unique-opened="true" |
| | | :active-text-color="theme" |
| | | :collapse-transition="false" |
| | | mode="vertical" |
| | | :class="sideTheme"> |
| | | <sidebar-item v-for="(route, index) in sidebarRouters" |
| | | :key="route.path + index" |
| | | :item="route" |
| | | :base-path="route.path" /> |
| | | </el-menu> |
| | | </el-scrollbar> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import Logo from './Logo' |
| | | import SidebarItem from './SidebarItem' |
| | | import variables from '@/assets/styles/variables.module.scss' |
| | | import useAppStore from '@/store/modules/app' |
| | | import useSettingsStore from '@/store/modules/settings' |
| | | import usePermissionStore from '@/store/modules/permission' |
| | | import Logo from "./Logo"; |
| | | import SidebarItem from "./SidebarItem"; |
| | | import variables from "@/assets/styles/variables.module.scss"; |
| | | import useAppStore from "@/store/modules/app"; |
| | | import useSettingsStore from "@/store/modules/settings"; |
| | | import usePermissionStore from "@/store/modules/permission"; |
| | | |
| | | const route = useRoute() |
| | | const appStore = useAppStore() |
| | | const settingsStore = useSettingsStore() |
| | | const permissionStore = usePermissionStore() |
| | | const route = useRoute(); |
| | | const appStore = useAppStore(); |
| | | const settingsStore = useSettingsStore(); |
| | | const permissionStore = usePermissionStore(); |
| | | |
| | | const sidebarRouters = computed(() => permissionStore.sidebarRouters) |
| | | const showLogo = computed(() => settingsStore.sidebarLogo) |
| | | const sideTheme = computed(() => settingsStore.sideTheme) |
| | | const theme = computed(() => settingsStore.theme) |
| | | const isCollapse = computed(() => !appStore.sidebar.opened) |
| | | const sidebarRouters = computed(() => permissionStore.sidebarRouters); |
| | | const showLogo = computed(() => settingsStore.sidebarLogo); |
| | | const sideTheme = computed(() => settingsStore.sideTheme); |
| | | const theme = computed(() => settingsStore.theme); |
| | | const isCollapse = computed(() => !appStore.sidebar.opened); |
| | | |
| | | const getMenuBackground = computed(() => 'var(--sidebar-bg)') |
| | | const getMenuBackground = computed(() => "var(--sidebar-bg)"); |
| | | |
| | | const getMenuTextColor = computed(() => { |
| | | if (settingsStore.isDark) { |
| | | return 'var(--sidebar-text)' |
| | | } |
| | | return sideTheme.value === 'theme-dark' ? variables.menuText : variables.menuLightText |
| | | }) |
| | | const getMenuTextColor = computed(() => { |
| | | if (settingsStore.isDark) { |
| | | return "var(--sidebar-text)"; |
| | | } |
| | | return sideTheme.value === "theme-dark" |
| | | ? variables.menuText |
| | | : variables.menuLightText; |
| | | }); |
| | | |
| | | const activeMenu = computed(() => { |
| | | const { meta, path } = route |
| | | if (meta.activeMenu) { |
| | | return meta.activeMenu |
| | | } |
| | | return path |
| | | }) |
| | | const activeMenu = computed(() => { |
| | | const { meta, path } = route; |
| | | if (meta.activeMenu) { |
| | | return meta.activeMenu; |
| | | } |
| | | return path; |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .sidebar-container { |
| | | background-color: v-bind(getMenuBackground); |
| | | border-radius: 22px; |
| | | overflow: hidden; |
| | | |
| | | .scrollbar-wrapper { |
| | | .sidebar-container { |
| | | background-color: v-bind(getMenuBackground); |
| | | } |
| | | |
| | | .el-menu { |
| | | border: none; |
| | | height: 100%; |
| | | width: 100% !important; |
| | | border-radius: 22px; |
| | | overflow: hidden; |
| | | |
| | | .el-menu-item, |
| | | .el-sub-menu__title { |
| | | margin-bottom: 6px; |
| | | border-radius: 14px; |
| | | color: v-bind(getMenuTextColor); |
| | | .scrollbar-wrapper { |
| | | background-color: v-bind(getMenuBackground); |
| | | } |
| | | |
| | | &:hover { |
| | | background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important; |
| | | .el-menu { |
| | | border: none; |
| | | height: 100%; |
| | | width: 100% !important; |
| | | border-radius: 22px; |
| | | |
| | | .el-menu-item, |
| | | .el-sub-menu__title { |
| | | margin-bottom: 6px; |
| | | border-radius: 14px; |
| | | color: v-bind(getMenuTextColor); |
| | | |
| | | &:hover { |
| | | background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important; |
| | | border-radius: 14px; |
| | | } |
| | | } |
| | | |
| | | .el-menu-item { |
| | | &.is-active { |
| | | color: v-bind(theme); |
| | | background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; |
| | | font-weight: 600; |
| | | } |
| | | } |
| | | |
| | | .el-sub-menu__title { |
| | | color: v-bind(getMenuTextColor); |
| | | } |
| | | |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title) { |
| | | color: v-bind(theme) !important; |
| | | font-weight: 600; |
| | | background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; |
| | | border-radius: 14px; |
| | | margin: 0 10px 6px !important; |
| | | // width: calc(100% - 20px) !important; |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | background-clip: padding-box; |
| | | } |
| | | |
| | | :deep(.el-menu-item.is-active) { |
| | | margin: 0 10px 6px !important; |
| | | width: calc(100% - 20px) !important; |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | background-clip: padding-box; |
| | | border-radius: 14px; |
| | | } |
| | | |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title .menu-title), |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title .svg-icon), |
| | | :deep(.el-menu-item.is-active .menu-title), |
| | | :deep(.el-menu-item.is-active .svg-icon) { |
| | | color: v-bind(theme) !important; |
| | | } |
| | | |
| | | :deep(.el-sub-menu__title:hover), |
| | | :deep(.el-menu-item:hover) { |
| | | border-radius: 14px; |
| | | } |
| | | } |
| | | |
| | | .el-menu-item { |
| | | &.is-active { |
| | | color: v-bind(theme); |
| | | background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; |
| | | font-weight: 600; |
| | | } |
| | | } |
| | | |
| | | .el-sub-menu__title { |
| | | color: v-bind(getMenuTextColor); |
| | | } |
| | | |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title) { |
| | | color: v-bind(theme) !important; |
| | | font-weight: 600; |
| | | background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; |
| | | border-radius: 14px; |
| | | margin: 0 10px 6px !important; |
| | | width: calc(100% - 20px) !important; |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | background-clip: padding-box; |
| | | } |
| | | |
| | | :deep(.el-menu-item.is-active) { |
| | | margin: 0 10px 6px !important; |
| | | width: calc(100% - 20px) !important; |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | background-clip: padding-box; |
| | | border-radius: 14px; |
| | | } |
| | | |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title .menu-title), |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title .svg-icon), |
| | | :deep(.el-menu-item.is-active .menu-title), |
| | | :deep(.el-menu-item.is-active .svg-icon) { |
| | | color: v-bind(theme) !important; |
| | | } |
| | | |
| | | :deep(.el-sub-menu__title:hover), |
| | | :deep(.el-menu-item:hover) { |
| | | border-radius: 14px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template>
|
| | | <div :class="classObj"
|
| | | class="app-wrapper"
|
| | | :style="{ '--current-color': theme }">
|
| | | <div v-if="device === 'mobile' && sidebar.opened"
|
| | | class="drawer-bg"
|
| | | @click="handleClickOutside" />
|
| | | <sidebar v-if="!sidebar.hide"
|
| | | class="sidebar-container" />
|
| | | <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }"
|
| | | class="main-container">
|
| | | <div :class="{ 'fixed-header': fixedHeader }">
|
| | | <navbar @setLayout="setLayout" />
|
| | | <tags-view v-if="needTagsView" />
|
| | | </div>
|
| | | <app-main />
|
| | | <settings ref="settingRef" />
|
| | | </div>
|
| | | </div>
|
| | | </template>
|
| | |
|
| | | <script setup>
|
| | | import { useWindowSize } from "@vueuse/core";
|
| | | import Sidebar from "./components/Sidebar/index.vue";
|
| | | import { AppMain, Navbar, Settings, TagsView } from "./components";
|
| | | import defaultSettings from "@/settings";
|
| | |
|
| | | import useAppStore from "@/store/modules/app";
|
| | | import useSettingsStore from "@/store/modules/settings";
|
| | |
|
| | | const settingsStore = useSettingsStore();
|
| | | const theme = computed(() => settingsStore.theme);
|
| | | const sideTheme = computed(() => settingsStore.sideTheme);
|
| | | const sidebar = computed(() => useAppStore().sidebar);
|
| | | const device = computed(() => useAppStore().device);
|
| | | const needTagsView = computed(() => settingsStore.tagsView);
|
| | | const fixedHeader = computed(() => settingsStore.fixedHeader);
|
| | |
|
| | | const classObj = computed(() => ({
|
| | | hideSidebar: !sidebar.value.opened,
|
| | | openSidebar: sidebar.value.opened,
|
| | | withoutAnimation: sidebar.value.withoutAnimation,
|
| | | mobile: device.value === "mobile",
|
| | | }));
|
| | |
|
| | | const { width, height } = useWindowSize();
|
| | | const WIDTH = 992; // refer to Bootstrap's responsive design
|
| | |
|
| | | watch(
|
| | | () => device.value,
|
| | | () => {
|
| | | if (device.value === "mobile" && sidebar.value.opened) {
|
| | | useAppStore().closeSideBar({ withoutAnimation: false });
|
| | | }
|
| | | }
|
| | | );
|
| | |
|
| | | watchEffect(() => {
|
| | | if (width.value - 1 < WIDTH) {
|
| | | useAppStore().toggleDevice("mobile");
|
| | | useAppStore().closeSideBar({ withoutAnimation: true });
|
| | | } else {
|
| | | useAppStore().toggleDevice("desktop");
|
| | | }
|
| | | });
|
| | |
|
| | | function handleClickOutside() {
|
| | | useAppStore().closeSideBar({ withoutAnimation: false });
|
| | | }
|
| | |
|
| | | const settingRef = ref(null);
|
| | | function setLayout() {
|
| | | settingRef.value.openSetting();
|
| | | }
|
| | | </script>
|
| | |
|
| | | <style lang="scss" scoped>
|
| | | @import "@/assets/styles/mixin.scss";
|
| | | @import "@/assets/styles/variables.module.scss";
|
| | |
|
| | | .app-wrapper {
|
| | | @include clearfix;
|
| | | position: relative;
|
| | | height: 100%;
|
| | | width: 100%;
|
| | | background: radial-gradient(
|
| | | circle at top,
|
| | | rgba(223, 232, 226, 0.95),
|
| | | transparent 32%
|
| | | ),
|
| | | linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%);
|
| | |
|
| | | &.mobile.openSidebar {
|
| | | position: fixed;
|
| | | top: 0;
|
| | | }
|
| | | }
|
| | |
|
| | | .drawer-bg {
|
| | | background: #000;
|
| | | opacity: 0.3;
|
| | | width: 100%;
|
| | | top: 0;
|
| | | height: 100%;
|
| | | position: absolute;
|
| | | z-index: 999;
|
| | | }
|
| | |
|
| | | .fixed-header {
|
| | | position: fixed;
|
| | | top: 0px;
|
| | | padding-top: 12px;
|
| | | right: 16px;
|
| | | z-index: 9;
|
| | | width: calc(100% - #{$base-sidebar-width} - 32px);
|
| | | transition: width 0.28s, right 0.28s;
|
| | | padding-bottom: 8px;
|
| | | background-color: #f3f6f4;
|
| | | }
|
| | | .hideSidebar .fixed-header {
|
| | | width: calc(100% - 100px);
|
| | | }
|
| | |
|
| | | .sidebarHide .fixed-header {
|
| | | width: calc(100% - 32px);
|
| | | }
|
| | |
|
| | | .mobile .fixed-header {
|
| | | width: 100%;
|
| | | }
|
| | | </style>
|
| | | <template> |
| | | <div :class="classObj" |
| | | class="app-wrapper" |
| | | :style="{ '--current-color': theme }"> |
| | | <div v-if="device === 'mobile' && sidebar.opened" |
| | | class="drawer-bg" |
| | | @click="handleClickOutside" /> |
| | | <sidebar v-if="!sidebar.hide" |
| | | class="sidebar-container" /> |
| | | <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" |
| | | class="main-container"> |
| | | <div :class="{ 'fixed-header': fixedHeader }"> |
| | | <navbar @setLayout="setLayout" /> |
| | | <tags-view v-if="needTagsView" /> |
| | | </div> |
| | | <app-main /> |
| | | <settings ref="settingRef" /> |
| | | </div> |
| | | <AIChatSidebar v-if="showGlobalAiChat" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { useWindowSize } from "@vueuse/core"; |
| | | import { useRoute } from "vue-router"; |
| | | import Sidebar from "./components/Sidebar/index.vue"; |
| | | import { AppMain, Navbar, Settings, TagsView } from "./components"; |
| | | import AIChatSidebar from "@/components/AIChatSidebar/index.vue"; |
| | | import defaultSettings from "@/settings"; |
| | | |
| | | import useAppStore from "@/store/modules/app"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import useSettingsStore from "@/store/modules/settings"; |
| | | |
| | | const settingsStore = useSettingsStore(); |
| | | const userStore = useUserStore(); |
| | | const route = useRoute(); |
| | | const theme = computed(() => settingsStore.theme); |
| | | const sideTheme = computed(() => settingsStore.sideTheme); |
| | | const sidebar = computed(() => useAppStore().sidebar); |
| | | const device = computed(() => useAppStore().device); |
| | | const needTagsView = computed(() => settingsStore.tagsView); |
| | | const fixedHeader = computed(() => settingsStore.fixedHeader); |
| | | const aiEnabled = computed(() => Number(userStore.aiEnabled) === 1); |
| | | const showGlobalAiChat = computed(() => { |
| | | const isIndustrialBrainRoute = String(route.path || "").startsWith("/ai-industrial-brain"); |
| | | return !isIndustrialBrainRoute && aiEnabled.value; |
| | | }); |
| | | |
| | | const classObj = computed(() => ({ |
| | | hideSidebar: !sidebar.value.opened, |
| | | openSidebar: sidebar.value.opened, |
| | | withoutAnimation: sidebar.value.withoutAnimation, |
| | | mobile: device.value === "mobile", |
| | | })); |
| | | |
| | | const { width, height } = useWindowSize(); |
| | | const WIDTH = 992; // refer to Bootstrap's responsive design |
| | | |
| | | watch( |
| | | () => device.value, |
| | | () => { |
| | | if (device.value === "mobile" && sidebar.value.opened) { |
| | | useAppStore().closeSideBar({ withoutAnimation: false }); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watchEffect(() => { |
| | | if (width.value - 1 < WIDTH) { |
| | | useAppStore().toggleDevice("mobile"); |
| | | useAppStore().closeSideBar({ withoutAnimation: true }); |
| | | } else { |
| | | useAppStore().toggleDevice("desktop"); |
| | | } |
| | | }); |
| | | |
| | | function handleClickOutside() { |
| | | useAppStore().closeSideBar({ withoutAnimation: false }); |
| | | } |
| | | |
| | | const settingRef = ref(null); |
| | | function setLayout() { |
| | | settingRef.value.openSetting(); |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import "@/assets/styles/mixin.scss"; |
| | | @import "@/assets/styles/variables.module.scss"; |
| | | |
| | | .app-wrapper { |
| | | @include clearfix; |
| | | position: relative; |
| | | height: 100%; |
| | | width: 100%; |
| | | background: radial-gradient( |
| | | circle at top, |
| | | rgba(223, 232, 226, 0.95), |
| | | transparent 32% |
| | | ), |
| | | linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%); |
| | | |
| | | &.mobile.openSidebar { |
| | | position: fixed; |
| | | top: 0; |
| | | } |
| | | } |
| | | |
| | | .drawer-bg { |
| | | background: #000; |
| | | opacity: 0.3; |
| | | width: 100%; |
| | | top: 0; |
| | | height: 100%; |
| | | position: absolute; |
| | | z-index: 999; |
| | | } |
| | | |
| | | .fixed-header { |
| | | position: fixed; |
| | | top: 0px; |
| | | padding-top: 12px; |
| | | right: 16px; |
| | | z-index: 9; |
| | | width: calc(100% - #{$base-sidebar-width} - 32px); |
| | | transition: width 0.28s, right 0.28s; |
| | | padding-bottom: 8px; |
| | | background-color: #f3f6f4; |
| | | } |
| | | .hideSidebar .fixed-header { |
| | | width: calc(100% - 100px); |
| | | } |
| | | |
| | | .sidebarHide .fixed-header { |
| | | width: calc(100% - 32px); |
| | | } |
| | | |
| | | .mobile .fixed-header { |
| | | width: 100%; |
| | | } |
| | | </style> |
| | |
| | | // 坿æ¬ç»ä»¶ |
| | | import Editor from "@/components/Editor"; |
| | | // æä»¶ä¸ä¼ ç»ä»¶ |
| | | import FileUpload from "@/components/FileUpload"; |
| | | import FileUpload from "@/components/AttachmentUpload/file"; |
| | | // å¾çä¸ä¼ ç»ä»¶ |
| | | import ImageUpload from "@/components/ImageUpload"; |
| | | import ImageUpload from "@/components/AttachmentUpload/image"; |
| | | // å¾çé¢è§ç»ä»¶ |
| | | import ImagePreview from "@/components/ImagePreview"; |
| | | import ImagePreview from "@/components/AttachmentPreview/image"; |
| | | // éä»¶å¼¹çªç»ä»¶ |
| | | import FileListDialog from "@/components/Dialog/FileList.vue"; |
| | | // åå
¸æ ç¾ç»ä»¶ |
| | | import DictTag from "@/components/DictTag"; |
| | | // è¡¨æ ¼ç»ä»¶ |
| | |
| | | app.component("FileUpload", FileUpload); |
| | | app.component("ImageUpload", ImageUpload); |
| | | app.component("ImagePreview", ImagePreview); |
| | | app.component("FileListDialog", FileListDialog); |
| | | app.component("RightToolbar", RightToolbar); |
| | | app.component("Editor", Editor); |
| | | app.component("PIMTable", PIMTable); |
| | |
| | | saveAs(text, name, opts) {
|
| | | saveAs(text, name, opts);
|
| | | },
|
| | | byUrl(url, filename) {
|
| | | // å°URLä¸çpreviewæ¿æ¢ædownload
|
| | | const downloadUrl = url.replace(/preview/g, 'download')
|
| | | const link = document.createElement('a')
|
| | | link.href = downloadUrl
|
| | | link.download = filename || ''
|
| | | document.body.appendChild(link)
|
| | | link.click()
|
| | | document.body.removeChild(link)
|
| | | },
|
| | | async printErrMsg(data) {
|
| | | const resText = await data.text();
|
| | | const rspObj = JSON.parse(resText);
|
| | |
| | | component: () => import("@/views/register"), |
| | | hidden: true, |
| | | }, |
| | | // ç³»ç»æ¶æå¾ |
| | | // { |
| | | // path: "/system-architecture", |
| | | // component: Layout, |
| | | // redirect: "/system-architecture/index", |
| | | // children: [ |
| | | // { |
| | | // path: "index", |
| | | // component: () => import("@/views/systemArchitecture/index.vue"), |
| | | // name: "SystemArchitecture", |
| | | // meta: { title: "ç³»ç»æ¶æå¾", icon: "tree" }, |
| | | // }, |
| | | // ], |
| | | // }, |
| | | { |
| | | path: "/:pathMatch(.*)*", |
| | | component: () => import("@/views/error/404"), |
| | |
| | | ], |
| | | }, |
| | | { |
| | | path: "/ai-industrial-brain", |
| | | component: Layout, |
| | | children: [ |
| | | { |
| | | path: "index", |
| | | component: () => import("@/views/aiIndustrialBrain/index.vue"), |
| | | name: "AiIndustrialBrain", |
| | | meta: { title: "AIå·¥ä¸å¤§è", icon: "skill" }, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | path: "/user", |
| | | component: Layout, |
| | | hidden: true, |
| | |
| | | name: "DeviceInfo", |
| | | meta: { title: "设å¤ä¿¡æ¯", icon: "monitor" }, |
| | | }, |
| | | // æ·»å 项ç®è¯¦æ
页é¢è·¯ç±é
ç½® |
| | | { |
| | | path: "/oaSystem/projectManagement/projectDetail", |
| | | component: Layout, |
| | | hidden: true, |
| | | children: [ |
| | | { |
| | | path: ":projectId", |
| | | component: () => import("@/views/oaSystem/projectManagement/projectDetail.vue"), |
| | | name: "ProjectDetail", |
| | | meta: { title: "项ç®è¯¦æ
", activeMenu: "/oaSystem/projectManagement" }, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | path: "/projectManagement/Management/detail", |
| | | component: Layout, |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | // è´¢å¡ç®¡ç模åè·¯ç± |
| | | { |
| | | path: "/financial", |
| | | component: Layout, |
| | | hidden: false, |
| | | redirect: "/financial/general-ledger", |
| | | alwaysShow: true, |
| | | meta: { title: "è´¢å¡ç®¡ç", icon: "money" }, |
| | | children: [ |
| | | { |
| | | path: "sales-out", |
| | | component: () => import("@/views/financialManagement/receivable/salesOut.vue"), |
| | | name: "SalesOut", |
| | | meta: { title: "éå®åºåº" }, |
| | | }, |
| | | { |
| | | path: "sales-return", |
| | | component: () => import("@/views/financialManagement/receivable/salesReturn.vue"), |
| | | name: "SalesReturn", |
| | | meta: { title: "éå®éè´§" }, |
| | | }, |
| | | |
| | | { |
| | | path: "invoice-apply", |
| | | component: () => import("@/views/financialManagement/receivable/invoiceApply.vue"), |
| | | name: "InvoiceApply", |
| | | meta: { title: "å¼ç¥¨ç³è¯·" }, |
| | | }, |
| | | { |
| | | path: "output-invoice", |
| | | component: () => import("@/views/financialManagement/receivable/outputInvoice.vue"), |
| | | name: "OutputInvoice", |
| | | meta: { title: "é项å票" }, |
| | | }, |
| | | { |
| | | path: "receipt", |
| | | component: () => import("@/views/financialManagement/receivable/receipt.vue"), |
| | | name: "Receipt", |
| | | meta: { title: "æ¶æ¬¾å" }, |
| | | }, |
| | | { |
| | | path: "receivable-reconciliation", |
| | | component: () => import("@/views/financialManagement/receivable/reconciliation.vue"), |
| | | name: "ReceivableReconciliation", |
| | | meta: { title: "åºæ¶å¯¹è´¦" }, |
| | | }, |
| | | { |
| | | path: "purchase-in", |
| | | component: () => import("@/views/financialManagement/payable/purchaseIn.vue"), |
| | | name: "PurchaseIn", |
| | | meta: { title: "éè´å
¥åº" }, |
| | | }, |
| | | { |
| | | path: "purchase-return", |
| | | component: () => import("@/views/financialManagement/payable/purchaseReturn.vue"), |
| | | name: "PurchaseReturn", |
| | | meta: { title: "éè´éè´§" }, |
| | | }, |
| | | { |
| | | path: "input-invoice", |
| | | component: () => import("@/views/financialManagement/payable/input-invoice.vue"), |
| | | name: "InputInvoice", |
| | | meta: { title: "è¿é¡¹å票" }, |
| | | }, |
| | | { |
| | | path: "payment-apply", |
| | | component: () => import("@/views/financialManagement/payable/paymentApply.vue"), |
| | | name: "PaymentApply", |
| | | meta: { title: "仿¬¾ç³è¯·" }, |
| | | }, |
| | | |
| | | { |
| | | path: "payment", |
| | | component: () => import("@/views/financialManagement/payable/payment.vue"), |
| | | name: "Payment", |
| | | meta: { title: "仿¬¾å" }, |
| | | }, |
| | | { |
| | | path: "payable-reconciliation", |
| | | component: () => import("@/views/financialManagement/payable/reconciliation.vue"), |
| | | name: "PayableReconciliation", |
| | | meta: { title: "åºä»å¯¹è´¦" }, |
| | | }, |
| | | { |
| | | path: "fixed-assets", |
| | | component: () => import("@/views/financialManagement/assets/fixedAssets.vue"), |
| | | name: "FixedAssets", |
| | | meta: { title: "åºå®èµäº§" }, |
| | | }, |
| | | { |
| | | path: "intangible-assets", |
| | | component: () => import("@/views/financialManagement/assets/intangibleAssets.vue"), |
| | | name: "IntangibleAssets", |
| | | meta: { title: "æ å½¢èµäº§" }, |
| | | }, |
| | | { |
| | | path: "general-ledger", |
| | | component: () => import("@/views/financialManagement/generalLedger/index.vue"), |
| | | name: "GeneralLedger", |
| | | meta: { title: "æ»å¸ç§ç®" }, |
| | | }, |
| | | { |
| | | path: "voucher", |
| | | component: () => import("@/views/financialManagement/voucher/index.vue"), |
| | | name: "Voucher", |
| | | meta: { title: "åè¯" }, |
| | | }, |
| | | { |
| | | path: "voucher-general-ledger", |
| | | component: () => import("@/views/financialManagement/voucher/generalLedger.vue"), |
| | | name: "VoucherGeneralLedger", |
| | | meta: { title: "ç§ç®æ»å¸" }, |
| | | }, |
| | | { |
| | | path: "voucher-detail-ledger", |
| | | component: () => import("@/views/financialManagement/voucher/detailLedger.vue"), |
| | | name: "VoucherDetailLedger", |
| | | meta: { title: "ç§ç®æç»å¸" }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | // å¨æè·¯ç±ï¼åºäºç¨æ·æé卿å»å è½½ |
| | |
| | | import auth from '@/plugins/auth'
|
| | | import router, { constantRoutes, dynamicRoutes } from '@/router'
|
| | | import { getRouters } from '@/api/menu'
|
| | | import Layout from '@/layout/index'
|
| | | import ParentView from '@/components/ParentView'
|
| | | import InnerLink from '@/layout/components/InnerLink'
|
| | | import Layout from '@/layout/index' |
| | | import ParentView from '@/components/ParentView' |
| | | import InnerLink from '@/layout/components/InnerLink' |
| | | import useUserStore from '@/store/modules/user' |
| | |
|
| | | // å¹é
viewsé颿æç.vueæä»¶
|
| | | const modules = import.meta.glob('./../../views/**/*.vue')
|
| | |
| | | return new Promise(resolve => {
|
| | | // åå端请æ±è·¯ç±æ°æ®
|
| | | getRouters().then(res => {
|
| | | const sdata = JSON.parse(JSON.stringify(res.data))
|
| | | const rdata = JSON.parse(JSON.stringify(res.data))
|
| | | const defaultData = JSON.parse(JSON.stringify(res.data))
|
| | | const aiEnabled = Number(useUserStore().aiEnabled) === 1 |
| | | const rawRoutes = filterAiFeatureRoutes(res.data, aiEnabled) |
| | | const sdata = JSON.parse(JSON.stringify(rawRoutes)) |
| | | const rdata = JSON.parse(JSON.stringify(rawRoutes)) |
| | | const defaultData = JSON.parse(JSON.stringify(rawRoutes)) |
| | | const sidebarRoutes = filterAsyncRouter(sdata)
|
| | | const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
| | | const defaultRoutes = filterAsyncRouter(defaultData)
|
| | | const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
|
| | | asyncRoutes.forEach(route => { router.addRoute(route) })
|
| | | this.setRoutes(rewriteRoutes)
|
| | | // å°è´¢å¡ç®¡çè·¯ç±åå¹¶å°ä¾§è¾¹æ
|
| | | this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
|
| | | this.setDefaultRoutes(sidebarRoutes)
|
| | | this.setTopbarRoutes(defaultRoutes)
|
| | | resolve(rewriteRoutes)
|
| | | })
|
| | | })
|
| | | const defaultRoutes = filterAsyncRouter(defaultData) |
| | | const asyncRoutes = filterDynamicRoutes(dynamicRoutes) |
| | | asyncRoutes.forEach(route => { router.addRoute(route) }) |
| | | this.setRoutes(rewriteRoutes) |
| | | const constantSidebarRoutes = filterAiFeatureRoutes(constantRoutes, aiEnabled) |
| | | // å°è´¢å¡ç®¡çè·¯ç±åå¹¶å°ä¾§è¾¹æ |
| | | this.setSidebarRouters(constantSidebarRoutes.concat(sidebarRoutes)) |
| | | this.setDefaultRoutes(sidebarRoutes) |
| | | this.setTopbarRoutes(defaultRoutes) |
| | | resolve(rewriteRoutes) |
| | | }) |
| | | }) |
| | | }
|
| | | }
|
| | | })
|
| | |
|
| | | // éååå°ä¼ æ¥çè·¯ç±å符串ï¼è½¬æ¢ä¸ºç»ä»¶å¯¹è±¡
|
| | | function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
| | | function filterAiFeatureRoutes(routes = [], aiEnabled = false) { |
| | | if (aiEnabled) { |
| | | return routes |
| | | } |
| | | return routes.reduce((acc, route) => { |
| | | if (!route || isAiFeatureRoute(route)) { |
| | | return acc |
| | | } |
| | | const nextRoute = { ...route } |
| | | if (Array.isArray(nextRoute.children) && nextRoute.children.length > 0) { |
| | | nextRoute.children = filterAiFeatureRoutes(nextRoute.children, aiEnabled) |
| | | } |
| | | acc.push(nextRoute) |
| | | return acc |
| | | }, []) |
| | | } |
| | | |
| | | function isAiFeatureRoute(route = {}) { |
| | | const path = String(route.path || '').toLowerCase() |
| | | const component = String(route.component || '').toLowerCase() |
| | | const name = String(route.name || '').toLowerCase() |
| | | const title = String(route?.meta?.title ?? route?.title ?? '') |
| | | |
| | | return ( |
| | | path.includes('chathome') || |
| | | component.includes('chathome') || |
| | | name.includes('chathome') || |
| | | title.includes('AI') |
| | | ) |
| | | } |
| | | |
| | | function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { |
| | | return asyncRouterMap.filter(route => {
|
| | | if (type && route.children) {
|
| | | route.children = filterChildren(route.children)
|
| | |
| | | })
|
| | | }
|
| | |
|
| | | function filterChildren(childrenMap, lastRouter = false) {
|
| | | function filterChildren(childrenMap, lastRouter = false) { |
| | | var children = []
|
| | | childrenMap.forEach(el => {
|
| | | el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path
|
| | |
| | | children.push(el)
|
| | | }
|
| | | })
|
| | | return children
|
| | | }
|
| | |
|
| | | // å¨æè·¯ç±éåï¼éªè¯æ¯å¦å
·å¤æé
|
| | | export function filterDynamicRoutes(routes) {
|
| | | return children |
| | | } |
| | | |
| | | // å¨æè·¯ç±éåï¼éªè¯æ¯å¦å
·å¤æé |
| | | export function filterDynamicRoutes(routes) { |
| | | const res = []
|
| | | routes.forEach(route => {
|
| | | if (route.permissions) {
|
| | |
| | | const useUserStore = defineStore(
|
| | | 'user',
|
| | | {
|
| | | state: () => ({
|
| | | token: getToken(),
|
| | | id: '',
|
| | | name: '',
|
| | | avatar: '',
|
| | | roles: [],
|
| | | permissions: []
|
| | | state: () => ({ |
| | | token: getToken(), |
| | | id: '', |
| | | name: '', |
| | | avatar: '', |
| | | roles: [], |
| | | permissions: [], |
| | | aiEnabled: 0 |
| | | }),
|
| | | actions: {
|
| | | // ç»å½
|
| | |
| | | this.id = user.userId
|
| | | this.name = user.userName
|
| | | this.avatar = avatar
|
| | | this.currentFactoryName = user.currentFactoryName
|
| | | this.nickName = user.nickName
|
| | | this.roleName = user.roles[0].roleName
|
| | | this.currentDeptId = user.tenantId
|
| | | this.currentLoginTime = this.getCurrentTime()
|
| | | resolve(res)
|
| | | }).catch(error => {
|
| | | reject(error)
|
| | | })
|
| | | })
|
| | | this.currentFactoryName = user.currentFactoryName |
| | | this.nickName = user.nickName |
| | | this.roleName = user.roles[0].roleName |
| | | this.currentDeptId = user.tenantId |
| | | this.currentLoginTime = this.getCurrentTime() |
| | | this.aiEnabled = Number(res.aiEnabled) === 1 ? 1 : 0 |
| | | resolve(res) |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | },
|
| | | // éåºç³»ç»
|
| | | logOut() {
|
| | | return new Promise((resolve, reject) => {
|
| | | logout(this.token).then(() => {
|
| | | this.token = ''
|
| | | this.roles = []
|
| | | this.permissions = []
|
| | | removeToken()
|
| | | resolve()
|
| | | }).catch(error => {
|
| | | reject(error)
|
| | | })
|
| | | this.token = '' |
| | | this.roles = [] |
| | | this.permissions = [] |
| | | this.aiEnabled = 0 |
| | | removeToken() |
| | | resolve() |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | })
|
| | | },
|
| | | // ç»å½æ ¡éª
|
| | |
| | | return `<el-dialog v-model="dialogVisible" @open="onOpen" @close="onClose" title="Dialog Titile"> |
| | | ${str} |
| | | <template #footer> |
| | | <el-button @click="close">åæ¶</el-button> |
| | | <el-button type="primary" @click="handelConfirm">ç¡®å®</el-button> |
| | | <el-button type="primary" @click="handelConfirm">ç¡®å®</el-button> |
| | | <el-button @click="close">åæ¶</el-button> |
| | | </template> |
| | | </el-dialog>` |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <transition name="fade"> |
| | | <section v-if="visible" class="assistant-workspace"> |
| | | <div class="assistant-workspace__panel"> |
| | | <button |
| | | v-if="assistantMode === 'pending'" |
| | | type="button" |
| | | class="workspace-back-btn" |
| | | @click="$emit('close')" |
| | | > |
| | | <el-icon><ArrowLeftBold /></el-icon> |
| | | <span>è¿åå·¥ä¸å¤§å±</span> |
| | | </button> |
| | | |
| | | <div class="assistant-workspace__body"> |
| | | <AIChatSidebar |
| | | v-if="assistantMode !== 'pending'" |
| | | :key="assistantMode" |
| | | class="workspace-chat" |
| | | :assistants="assistantMode === 'purchase' ? [purchaseAssistant] : [generalAssistant]" |
| | | :default-assistant="assistantMode" |
| | | :hide-trigger="true" |
| | | :auto-open="true" |
| | | drawer-size="100%" |
| | | drawer-direction="ttb" |
| | | header-extra-action-text="è¿åå·¥ä¸å¤§å±" |
| | | @header-extra-action="$emit('close')" |
| | | /> |
| | | |
| | | <div v-else class="workspace-pending"> |
| | | <div class="workspace-pending__content"> |
| | | <h3>{{ agentTitle }}</h3> |
| | | <p>æ£å¨å¼åï¼æ¬è¯·æå¾
......</p> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | </transition> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | import { ArrowLeftBold } from "@element-plus/icons-vue"; |
| | | import AIChatSidebar from "@/components/AIChatSidebar/index.vue"; |
| | | import { generalAssistant, purchaseAssistant } from "@/components/AIChatSidebar/assistants"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | agent: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | defineEmits(["close"]); |
| | | |
| | | const agentKey = computed(() => String(props.agent?.key || "")); |
| | | const agentTitle = computed(() => String(props.agent?.name || "AI婿")); |
| | | const assistantMode = computed(() => { |
| | | if (agentKey.value === "purchase") return "purchase"; |
| | | if (agentKey.value === "general") return "general"; |
| | | return "pending"; |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .assistant-workspace { |
| | | position: fixed; |
| | | inset: 0; |
| | | z-index: 2100; |
| | | padding: 12px; |
| | | background: rgba(33, 49, 63, 0.24); |
| | | backdrop-filter: blur(2px); |
| | | } |
| | | |
| | | .assistant-workspace__panel { |
| | | position: relative; |
| | | height: 100%; |
| | | border-radius: 22px; |
| | | border: 1px solid var(--surface-border); |
| | | background: linear-gradient(180deg, #f9fcfb 0%, #f0f5f2 100%); |
| | | box-shadow: var(--shadow-md); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .assistant-workspace__body { |
| | | height: 100%; |
| | | min-height: 100%; |
| | | } |
| | | |
| | | .workspace-back-btn { |
| | | position: absolute; |
| | | top: 16px; |
| | | right: 20px; |
| | | z-index: 5; |
| | | height: 36px; |
| | | padding: 0 14px; |
| | | border: 1px solid rgba(38, 112, 183, 0.3); |
| | | border-radius: 10px; |
| | | background: rgba(255, 255, 255, 0.92); |
| | | color: #25528f; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | cursor: pointer; |
| | | transition: all 0.2s ease; |
| | | } |
| | | |
| | | .workspace-back-btn:hover { |
| | | border-color: rgba(31, 122, 114, 0.45); |
| | | color: #1f5ddf; |
| | | box-shadow: 0 8px 16px rgba(31, 122, 114, 0.14); |
| | | transform: translateY(-1px); |
| | | } |
| | | |
| | | .workspace-chat { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .workspace-chat :deep(.ai-chat-sidebar-wrapper) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .workspace-chat :deep(.ai-chat-drawer) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .workspace-chat :deep(.el-drawer) { |
| | | height: 100% !important; |
| | | width: 100% !important; |
| | | } |
| | | |
| | | .workspace-pending { |
| | | height: 100%; |
| | | display: grid; |
| | | place-items: center; |
| | | padding: 20px; |
| | | color: var(--text-secondary); |
| | | } |
| | | |
| | | .workspace-pending__content { |
| | | display: grid; |
| | | gap: 12px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .workspace-pending__content h3 { |
| | | margin: 0; |
| | | font-size: 36px; |
| | | color: var(--text-primary); |
| | | } |
| | | |
| | | .workspace-pending__content p { |
| | | margin: 0; |
| | | font-size: 24px; |
| | | } |
| | | |
| | | .fade-enter-active, |
| | | .fade-leave-active { |
| | | transition: opacity 0.2s ease; |
| | | } |
| | | |
| | | .fade-enter-from, |
| | | .fade-leave-to { |
| | | opacity: 0; |
| | | } |
| | | |
| | | @media (max-width: 1600px) { |
| | | .workspace-back-btn { |
| | | top: 12px; |
| | | right: 14px; |
| | | height: 32px; |
| | | padding: 0 12px; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .workspace-pending__content h3 { |
| | | font-size: 30px; |
| | | } |
| | | |
| | | .workspace-pending__content p { |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div ref="screenRef" class="ai-brain-screen"> |
| | | <section class="brain-stage"> |
| | | <header class="brain-head"> |
| | | <div class="head-date"> |
| | | <p>{{ weekLabel }}</p> |
| | | <p>{{ dateLabel }}</p> |
| | | </div> |
| | | |
| | | <div class="head-title"> |
| | | <span>AIå·¥ä¸å¤§è</span> |
| | | </div> |
| | | |
| | | <div class="head-actions"> |
| | | <button type="button" class="head-back-btn" @click="goBack"> |
| | | <el-icon><ArrowLeftBold /></el-icon> |
| | | <span>è¿å</span> |
| | | </button> |
| | | </div> |
| | | </header> |
| | | |
| | | <section class="brain-intro"> |
| | | <h2>å·¥ä¸AIæ°ååå·¥ï¼èµè½æºé æ°çºªå
</h2> |
| | | <p>å
大AI婿ååä¼ä¸ç®¡çãéå®ãéè´ãç产ãè´¢å¡åæ°æ®å
¨é¾è·¯</p> |
| | | <div class="intro-sign">é¿éäº Ã åé®å¤§æ¨¡å à æºè½ä½AI</div> |
| | | </section> |
| | | |
| | | <section class="carousel-area"> |
| | | <button type="button" class="nav-btn nav-btn--left" @click="prevCard"> |
| | | <el-icon><ArrowLeftBold /></el-icon> |
| | | </button> |
| | | |
| | | <div class="carousel-track"> |
| | | <article |
| | | v-for="card in visibleCards" |
| | | :key="card.agent.key" |
| | | class="agent-card" |
| | | :class="{ 'agent-card--active': card.offset === 0 }" |
| | | :style="getCardStyle(card.offset)" |
| | | @click="openAssistant(card.realIndex)" |
| | | > |
| | | <div class="agent-card__head" :class="{ 'agent-card__head--active': card.offset === 0 }"> |
| | | {{ card.agent.name }} |
| | | </div> |
| | | |
| | | <div class="agent-card__body" :class="{ 'agent-card__body--active': card.offset === 0 }"> |
| | | <div class="avatar-shell" :class="{ 'avatar-shell--active': card.offset === 0 }"> |
| | | <div class="avatar-base"></div> |
| | | <div class="avatar-cut"> |
| | | <img v-if="card.agent.avatar" class="avatar-cut__img" :src="card.agent.avatar" :alt="card.agent.name" /> |
| | | </div> |
| | | </div> |
| | | <div v-if="card.offset === 0" class="highlight-list"> |
| | | <div |
| | | v-for="highlight in card.agent.highlights" |
| | | :key="highlight" |
| | | class="highlight-item" |
| | | > |
| | | {{ highlight }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </article> |
| | | </div> |
| | | |
| | | <button type="button" class="nav-btn nav-btn--right" @click="nextCard"> |
| | | <el-icon><ArrowRightBold /></el-icon> |
| | | </button> |
| | | </section> |
| | | |
| | | <section class="brain-footer"> |
| | | <div class="footer-grid-overlay"></div> |
| | | |
| | | <div class="footer-metrics"> |
| | | <article class="footer-metric"> |
| | | <span class="footer-metric__label">å¨çº¿æºè½ä½</span> |
| | | <strong class="footer-metric__value">{{ agents.length }}个</strong> |
| | | <small class="footer-metric__hint">å
¨é¾è·¯ååè¿è¡</small> |
| | | </article> |
| | | |
| | | <article class="footer-metric footer-metric--focus"> |
| | | <span class="footer-metric__label">å½åç¦ç¹</span> |
| | | <strong class="footer-metric__value">{{ getFooterAgentName(focusAgent.name) }}</strong> |
| | | <small class="footer-metric__hint">{{ focusAgent.highlights?.[0] || "æºè½åæèå¨" }}</small> |
| | | </article> |
| | | |
| | | <article class="footer-metric footer-metric--period"> |
| | | <span class="footer-metric__label">è½®æå¨æ</span> |
| | | <strong class="footer-metric__value">{{ carouselSecondsText }}</strong> |
| | | <div class="footer-period-control"> |
| | | <button type="button" class="period-btn" @click="adjustCarouselSeconds(-0.5)">-</button> |
| | | <input |
| | | v-model.number="carouselSeconds" |
| | | class="period-input" |
| | | type="number" |
| | | min="2" |
| | | max="12" |
| | | step="0.5" |
| | | /> |
| | | <span class="period-unit">s</span> |
| | | <button type="button" class="period-btn" @click="adjustCarouselSeconds(0.5)">+</button> |
| | | </div> |
| | | <input |
| | | v-model.number="carouselSeconds" |
| | | class="footer-period-slider" |
| | | type="range" |
| | | min="2" |
| | | max="12" |
| | | step="0.5" |
| | | /> |
| | | <small class="footer-metric__hint">坿å¨è®¾ç½® 2.0s - 12.0s</small> |
| | | </article> |
| | | </div> |
| | | |
| | | <div class="footer-rail"> |
| | | <div class="footer-rail__line"> |
| | | <span class="footer-rail__flow"></span> |
| | | </div> |
| | | <div class="footer-rail__nodes"> |
| | | <button |
| | | v-for="node in footerNodes" |
| | | :key="node.key" |
| | | type="button" |
| | | class="footer-node" |
| | | :class="{ 'footer-node--active': node.index === carouselIndex }" |
| | | @click="openAssistant(node.index)" |
| | | > |
| | | <span class="footer-node__dot"></span> |
| | | <span class="footer-node__name">{{ getFooterAgentName(node.name) }}</span> |
| | | </button> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | </section> |
| | | |
| | | <AiAssistantWorkspace |
| | | :visible="fullscreenVisible" |
| | | :agent="currentAgent" |
| | | @close="closeFullscreen" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import { ArrowLeftBold, ArrowRightBold } from "@element-plus/icons-vue"; |
| | | import AiAssistantWorkspace from "./components/AiAssistantWorkspace.vue"; |
| | | import todoAvatar from "@/assets/AI/å¾
å婿.png"; |
| | | import salesAvatar from "@/assets/AI/éå®å©æ.png"; |
| | | import purchaseAvatar from "@/assets/AI/éè´å©æ.png"; |
| | | import productionAvatar from "@/assets/AI/çäº§å©æ.png"; |
| | | import financeAvatar from "@/assets/AI/è´¢å¡å©æ.png"; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const agents = [ |
| | | { |
| | | key: "general", |
| | | name: "AIå¾
å婿", |
| | | highlights: ["è·¨æ¨¡åæµç¨è¯æ", "ç»è¥é£é©æºè½æé"], |
| | | }, |
| | | { |
| | | key: "sales", |
| | | name: "AIéå®å©æ", |
| | | highlights: ["å®¢æ·æµå¤±é£é©åæ", "忬¾ä¸æ¥ä»·çç¥å»ºè®®"], |
| | | }, |
| | | { |
| | | key: "purchase", |
| | | name: "AIéè´å©æ", |
| | | highlights: ["ä¾åºé¾ææ åæ", "éè´è®¢åæºè½çæ"], |
| | | }, |
| | | { |
| | | key: "production", |
| | | name: "AIçäº§å©æ", |
| | | highlights: ["å·¥åºç¶é¢å®ä½", "产è½ä¸æ¥åºæºè½é¢è¦"], |
| | | }, |
| | | { |
| | | key: "finance", |
| | | name: "AIè´¢å¡å©æ", |
| | | highlights: ["ç°éæµååé¢å¤", "è´¹ç¨ç»ææºè½åæ"], |
| | | }, |
| | | ]; |
| | | |
| | | const avatarByAgentKey = { |
| | | general: todoAvatar, |
| | | sales: salesAvatar, |
| | | purchase: purchaseAvatar, |
| | | production: productionAvatar, |
| | | finance: financeAvatar, |
| | | }; |
| | | |
| | | for (let i = agents.length - 1; i >= 0; i -= 1) { |
| | | const agent = agents[i]; |
| | | const avatar = avatarByAgentKey[agent.key]; |
| | | if (!avatar) { |
| | | agents.splice(i, 1); |
| | | continue; |
| | | } |
| | | agent.avatar = avatar; |
| | | } |
| | | |
| | | const carouselIndex = ref(Math.min(2, Math.max(agents.length - 1, 0))); |
| | | const fullscreenVisible = ref(false); |
| | | const screenRef = ref(null); |
| | | const carouselIntervalMs = ref(4500); |
| | | |
| | | let carouselTimer = null; |
| | | |
| | | const fallbackAgent = { |
| | | key: "fallback", |
| | | name: "AI婿", |
| | | avatar: "", |
| | | highlights: [], |
| | | }; |
| | | |
| | | const currentAgent = computed(() => agents[carouselIndex.value] || agents[0] || fallbackAgent); |
| | | const focusAgent = computed(() => currentAgent.value || fallbackAgent); |
| | | const footerNodes = computed(() => |
| | | agents.map((agent, index) => ({ |
| | | key: agent.key, |
| | | name: agent.name, |
| | | index, |
| | | })) |
| | | ); |
| | | const carouselSeconds = computed({ |
| | | get: () => Number((carouselIntervalMs.value / 1000).toFixed(1)), |
| | | set: (value) => { |
| | | const next = Number(value); |
| | | if (!Number.isFinite(next)) return; |
| | | const clamped = Math.max(2, Math.min(12, Math.round(next * 2) / 2)); |
| | | carouselIntervalMs.value = Math.round(clamped * 1000); |
| | | }, |
| | | }); |
| | | const carouselSecondsText = computed(() => `${carouselSeconds.value.toFixed(1)}s`); |
| | | |
| | | const weekLabel = computed(() => { |
| | | const weekMap = ["æææ¥", "ææä¸", "ææäº", "ææä¸", "ææå", "ææäº", "ææå
"]; |
| | | return weekMap[new Date().getDay()]; |
| | | }); |
| | | |
| | | const dateLabel = computed(() => { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | return `${year}å¹´${month}æ${day}æ¥`; |
| | | }); |
| | | |
| | | const visibleCards = computed(() => { |
| | | const total = agents.length; |
| | | return agents |
| | | .map((agent, index) => { |
| | | let offset = index - carouselIndex.value; |
| | | if (offset > total / 2) offset -= total; |
| | | if (offset < -total / 2) offset += total; |
| | | return { agent, offset, realIndex: index }; |
| | | }) |
| | | .filter((item) => Math.abs(item.offset) <= 2) |
| | | .sort((a, b) => a.offset - b.offset); |
| | | }); |
| | | |
| | | function getCardStyle(offset) { |
| | | const distance = Math.abs(offset); |
| | | const scale = distance === 0 ? 1 : distance === 1 ? 0.88 : 0.78; |
| | | const opacity = distance === 0 ? 1 : distance === 1 ? 0.92 : 0.76; |
| | | return { |
| | | transform: `translateX(${offset * 340}px) scale(${scale})`, |
| | | zIndex: String(50 - distance), |
| | | opacity, |
| | | }; |
| | | } |
| | | |
| | | function getFooterAgentName(name) { |
| | | return String(name || "AI婿").replace(/^AI/, ""); |
| | | } |
| | | |
| | | function adjustCarouselSeconds(delta) { |
| | | carouselSeconds.value = carouselSeconds.value + delta; |
| | | } |
| | | |
| | | function prevCard() { |
| | | const total = agents.length; |
| | | if (!total) return; |
| | | carouselIndex.value = (carouselIndex.value - 1 + total) % total; |
| | | } |
| | | |
| | | function nextCard() { |
| | | const total = agents.length; |
| | | if (!total) return; |
| | | carouselIndex.value = (carouselIndex.value + 1) % total; |
| | | } |
| | | |
| | | async function enterBrowserFullscreen() { |
| | | if (document.fullscreenElement) return; |
| | | const target = screenRef.value || document.documentElement; |
| | | if (!target || typeof target.requestFullscreen !== "function") return; |
| | | try { |
| | | await target.requestFullscreen(); |
| | | } catch (error) { |
| | | // Ignore: browser may block fullscreen when there is no direct user activation. |
| | | } |
| | | } |
| | | |
| | | async function exitBrowserFullscreen() { |
| | | if (!document.fullscreenElement || typeof document.exitFullscreen !== "function") return; |
| | | try { |
| | | await document.exitFullscreen(); |
| | | } catch (error) { |
| | | // Ignore fullscreen exit failures. |
| | | } |
| | | } |
| | | |
| | | function goBack() { |
| | | closeFullscreen(); |
| | | exitBrowserFullscreen(); |
| | | if (window.history.length > 1) { |
| | | router.back(); |
| | | return; |
| | | } |
| | | router.push("/index"); |
| | | } |
| | | |
| | | function openAssistant(index) { |
| | | if (!agents.length) return; |
| | | carouselIndex.value = index; |
| | | fullscreenVisible.value = true; |
| | | } |
| | | |
| | | function closeFullscreen() { |
| | | fullscreenVisible.value = false; |
| | | } |
| | | |
| | | function startCarousel() { |
| | | stopCarousel(); |
| | | if (fullscreenVisible.value) return; |
| | | carouselTimer = window.setInterval(() => { |
| | | nextCard(); |
| | | }, carouselIntervalMs.value); |
| | | } |
| | | |
| | | function stopCarousel() { |
| | | if (carouselTimer) { |
| | | window.clearInterval(carouselTimer); |
| | | carouselTimer = null; |
| | | } |
| | | } |
| | | |
| | | function handleEscClose(event) { |
| | | if (event.key === "Escape" && fullscreenVisible.value) { |
| | | closeFullscreen(); |
| | | } |
| | | } |
| | | |
| | | watch( |
| | | () => fullscreenVisible.value, |
| | | (opened) => { |
| | | if (opened) { |
| | | stopCarousel(); |
| | | } else { |
| | | startCarousel(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => carouselIntervalMs.value, |
| | | () => { |
| | | if (!fullscreenVisible.value) { |
| | | startCarousel(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | onMounted(() => { |
| | | startCarousel(); |
| | | window.addEventListener("keydown", handleEscClose); |
| | | window.requestAnimationFrame(() => { |
| | | enterBrowserFullscreen(); |
| | | }); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | stopCarousel(); |
| | | window.removeEventListener("keydown", handleEscClose); |
| | | exitBrowserFullscreen(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .ai-brain-screen { |
| | | position: fixed; |
| | | inset: 0; |
| | | z-index: 1900; |
| | | padding: 10px; |
| | | overflow: hidden; |
| | | background: var(--app-bg); |
| | | } |
| | | |
| | | .brain-stage { |
| | | position: relative; |
| | | height: 100%; |
| | | min-height: 100%; |
| | | border-radius: 22px; |
| | | border: 1px solid var(--surface-border); |
| | | background: |
| | | radial-gradient(circle at 14% 8%, rgba(31, 122, 114, 0.14), transparent 40%), |
| | | radial-gradient(circle at 86% 12%, rgba(30, 91, 255, 0.1), transparent 42%), |
| | | linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(245, 249, 247, 0.94)), |
| | | repeating-linear-gradient( |
| | | 135deg, |
| | | rgba(255, 255, 255, 0.05) 0, |
| | | rgba(255, 255, 255, 0.05) 14px, |
| | | rgba(31, 122, 114, 0.03) 14px, |
| | | rgba(31, 122, 114, 0.03) 28px |
| | | ); |
| | | box-shadow: var(--shadow-sm); |
| | | } |
| | | |
| | | .brain-head { |
| | | display: grid; |
| | | grid-template-columns: 220px minmax(0, 1fr) 180px; |
| | | align-items: center; |
| | | padding: 12px 18px 0; |
| | | } |
| | | |
| | | .head-date { |
| | | color: var(--text-secondary); |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .head-date p { |
| | | margin: 0; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .head-title { |
| | | justify-self: center; |
| | | width: min(760px, 95%); |
| | | height: 68px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-radius: 0 0 46px 46px; |
| | | color: #fff; |
| | | font-size: 42px; |
| | | font-style: italic; |
| | | font-weight: 700; |
| | | letter-spacing: 1px; |
| | | background: linear-gradient(135deg, #1f7a72 0%, #1e5bff 100%); |
| | | box-shadow: 0 16px 30px rgba(31, 122, 114, 0.24); |
| | | } |
| | | |
| | | .head-actions { |
| | | justify-self: end; |
| | | } |
| | | |
| | | .head-back-btn { |
| | | height: 40px; |
| | | padding: 0 14px; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | border: none; |
| | | border-radius: 999px; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: var(--colorPrimary); |
| | | background: var(--surface-base); |
| | | box-shadow: 0 8px 18px rgba(31, 49, 38, 0.12); |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .brain-intro { |
| | | text-align: center; |
| | | margin-top: 34px; |
| | | } |
| | | |
| | | .brain-intro h2 { |
| | | margin: 0; |
| | | font-size: 44px; |
| | | font-style: italic; |
| | | font-weight: 700; |
| | | color: var(--text-primary); |
| | | } |
| | | |
| | | .brain-intro p { |
| | | margin: 12px 0 10px; |
| | | font-size: 28px; |
| | | color: var(--text-secondary); |
| | | } |
| | | |
| | | .intro-sign { |
| | | display: inline-block; |
| | | padding: 6px 18px; |
| | | border-radius: 999px; |
| | | font-size: 24px; |
| | | font-weight: 700; |
| | | color: #1e5bff; |
| | | background: rgba(255, 255, 255, 0.82); |
| | | border: 1px solid rgba(30, 91, 255, 0.18); |
| | | } |
| | | |
| | | .carousel-area { |
| | | position: relative; |
| | | margin-top: 34px; |
| | | padding: 0 72px 12px; |
| | | } |
| | | |
| | | .carousel-track { |
| | | position: relative; |
| | | height: 500px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .brain-footer { |
| | | position: relative; |
| | | margin: 0 72px; |
| | | height: clamp(226px, 25vh,0); |
| | | border-radius: 18px; |
| | | border: 1px solid rgba(31, 122, 114, 0.28); |
| | | background: |
| | | linear-gradient(120deg, rgba(31, 122, 114, 0.14), rgba(30, 91, 255, 0.14)), |
| | | linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(236, 244, 249, 0.9)); |
| | | box-shadow: |
| | | 0 16px 34px rgba(31, 81, 131, 0.12), |
| | | inset 0 1px 0 rgba(255, 255, 255, 0.72); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .brain-footer::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: -22%; |
| | | bottom: -120%; |
| | | width: 52%; |
| | | height: 260%; |
| | | background: radial-gradient(ellipse at center, rgba(30, 91, 255, 0.2) 0%, rgba(30, 91, 255, 0) 72%); |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .brain-footer::after { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | background: linear-gradient(110deg, transparent 12%, rgba(255, 255, 255, 0.24) 38%, transparent 64%); |
| | | transform: translateX(-120%); |
| | | animation: footerSweep 5.8s linear infinite; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .footer-grid-overlay { |
| | | position: absolute; |
| | | inset: 0; |
| | | background: |
| | | repeating-linear-gradient( |
| | | 90deg, |
| | | rgba(31, 122, 114, 0.07) 0, |
| | | rgba(31, 122, 114, 0.07) 1px, |
| | | transparent 1px, |
| | | transparent 36px |
| | | ), |
| | | repeating-linear-gradient( |
| | | 0deg, |
| | | rgba(30, 91, 255, 0.06) 0, |
| | | rgba(30, 91, 255, 0.06) 1px, |
| | | transparent 1px, |
| | | transparent 28px |
| | | ); |
| | | opacity: 0.72; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .footer-metrics { |
| | | position: relative; |
| | | z-index: 2; |
| | | padding: 14px 20px 72px; |
| | | display: grid; |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | gap: 12px; |
| | | } |
| | | |
| | | .footer-metric { |
| | | min-height: 76px; |
| | | border-radius: 12px; |
| | | padding: 10px 14px; |
| | | border: 1px solid rgba(37, 124, 188, 0.2); |
| | | background: linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(245, 250, 255, 0.82)); |
| | | box-shadow: 0 10px 18px rgba(29, 83, 134, 0.08); |
| | | display: grid; |
| | | grid-template-rows: auto auto 1fr; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .footer-metric--focus { |
| | | border-color: rgba(38, 122, 194, 0.34); |
| | | box-shadow: |
| | | 0 12px 22px rgba(30, 91, 255, 0.12), |
| | | inset 0 0 0 1px rgba(85, 148, 232, 0.2); |
| | | } |
| | | |
| | | .footer-metric__label { |
| | | font-size: 14px; |
| | | color: rgba(38, 72, 108, 0.88); |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .footer-metric__value { |
| | | font-size: 30px; |
| | | line-height: 1; |
| | | font-style: italic; |
| | | font-weight: 700; |
| | | color: #1f5ddf; |
| | | text-shadow: 0 3px 10px rgba(30, 91, 255, 0.18); |
| | | } |
| | | |
| | | .footer-metric__hint { |
| | | margin-top: auto; |
| | | font-size: 13px; |
| | | color: rgba(52, 89, 128, 0.82); |
| | | } |
| | | |
| | | .footer-metric--period .footer-metric__hint { |
| | | margin-top: 0; |
| | | line-height: 1.25; |
| | | } |
| | | |
| | | .footer-metric--period { |
| | | min-height: 122px; |
| | | grid-template-rows: auto auto auto auto auto; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .footer-period-control { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .period-btn { |
| | | width: 22px; |
| | | height: 22px; |
| | | border-radius: 50%; |
| | | border: 1px solid rgba(38, 112, 183, 0.28); |
| | | background: rgba(255, 255, 255, 0.9); |
| | | color: #2054c9; |
| | | font-size: 14px; |
| | | font-weight: 700; |
| | | line-height: 1; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .period-input { |
| | | width: 56px; |
| | | height: 24px; |
| | | border-radius: 8px; |
| | | border: 1px solid rgba(38, 112, 183, 0.24); |
| | | background: rgba(255, 255, 255, 0.94); |
| | | color: #1f5ddf; |
| | | font-size: 13px; |
| | | font-weight: 600; |
| | | text-align: center; |
| | | padding: 0 4px; |
| | | } |
| | | |
| | | .period-unit { |
| | | font-size: 12px; |
| | | font-weight: 600; |
| | | color: rgba(40, 80, 117, 0.86); |
| | | } |
| | | |
| | | .footer-period-slider { |
| | | width: min(250px, 100%); |
| | | height: 3px; |
| | | accent-color: #2a6ded; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .footer-rail { |
| | | position: absolute; |
| | | left: 20px; |
| | | right: 20px; |
| | | bottom: 18px; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .footer-rail__line { |
| | | position: relative; |
| | | height: 2px; |
| | | border-radius: 999px; |
| | | background: linear-gradient(90deg, rgba(31, 122, 114, 0.12), rgba(30, 91, 255, 0.6), rgba(31, 122, 114, 0.12)); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .footer-rail__flow { |
| | | position: absolute; |
| | | top: -1px; |
| | | left: -18%; |
| | | width: 22%; |
| | | height: 4px; |
| | | border-radius: 999px; |
| | | background: linear-gradient(90deg, rgba(31, 122, 114, 0), rgba(59, 146, 244, 0.92), rgba(30, 91, 255, 0)); |
| | | filter: blur(0.2px); |
| | | animation: railFlow 3.1s ease-in-out infinite; |
| | | } |
| | | |
| | | .footer-rail__nodes { |
| | | margin-top: 12px; |
| | | display: grid; |
| | | grid-template-columns: repeat(5, minmax(0, 1fr)); |
| | | gap: 8px; |
| | | } |
| | | |
| | | .footer-node { |
| | | height: 34px; |
| | | border: 1px solid rgba(38, 112, 183, 0.18); |
| | | border-radius: 999px; |
| | | background: rgba(255, 255, 255, 0.76); |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | color: rgba(40, 80, 117, 0.92); |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | cursor: pointer; |
| | | transition: all 0.26s ease; |
| | | } |
| | | |
| | | .footer-node:hover { |
| | | transform: translateY(-1px); |
| | | border-color: rgba(31, 122, 114, 0.34); |
| | | box-shadow: 0 8px 14px rgba(31, 122, 114, 0.14); |
| | | } |
| | | |
| | | .footer-node--active { |
| | | color: #fff; |
| | | border-color: transparent; |
| | | background: linear-gradient(135deg, rgba(31, 122, 114, 0.94), rgba(30, 91, 255, 0.94)); |
| | | box-shadow: 0 10px 18px rgba(30, 91, 255, 0.28); |
| | | } |
| | | |
| | | .footer-node__dot { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | background: rgba(30, 91, 255, 0.72); |
| | | box-shadow: 0 0 10px rgba(30, 91, 255, 0.52); |
| | | } |
| | | |
| | | .footer-node--active .footer-node__dot { |
| | | background: #fff; |
| | | box-shadow: 0 0 12px rgba(255, 255, 255, 0.72); |
| | | animation: nodePulse 1.4s ease-in-out infinite; |
| | | } |
| | | |
| | | .agent-card { |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 0; |
| | | width: 460px; |
| | | margin-left: -230px; |
| | | cursor: pointer; |
| | | transform-origin: center bottom; |
| | | transition: transform 0.35s ease, opacity 0.35s ease; |
| | | } |
| | | |
| | | .agent-card__head { |
| | | height: 56px; |
| | | border-radius: 12px 12px 0 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 28px; |
| | | color: #fff; |
| | | font-weight: 700; |
| | | background: linear-gradient(135deg, #1f7a72 0%, #1e5bff 100%); |
| | | } |
| | | |
| | | .agent-card__head--active { |
| | | box-shadow: |
| | | 0 12px 22px rgba(30, 91, 255, 0.26), |
| | | inset 0 0 0 1px rgba(255, 255, 255, 0.28); |
| | | position: relative; |
| | | } |
| | | |
| | | .agent-card__head--active::after { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 12px; |
| | | right: 12px; |
| | | bottom: 6px; |
| | | height: 3px; |
| | | border-radius: 999px; |
| | | background: linear-gradient(90deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.22)); |
| | | } |
| | | |
| | | .agent-card__body { |
| | | position: relative; |
| | | height: 430px; |
| | | border: 1px solid var(--surface-border-strong); |
| | | border-top: none; |
| | | border-radius: 0 0 20px 20px; |
| | | background: rgba(255, 255, 255, 0.96); |
| | | overflow: hidden; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: flex-end; |
| | | isolation: isolate; |
| | | box-shadow: 0 12px 24px rgba(31, 49, 38, 0.1); |
| | | } |
| | | |
| | | .agent-card__body--active { |
| | | background: linear-gradient(180deg, rgba(248, 252, 251, 0.96), rgba(225, 241, 250, 0.9)); |
| | | border-color: rgba(31, 122, 114, 0.35); |
| | | } |
| | | |
| | | .agent-card__body--active::after { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | background: linear-gradient(108deg, transparent 28%, rgba(255, 255, 255, 0.34) 50%, transparent 72%); |
| | | transform: translateX(-125%); |
| | | animation: bodySweep 3.6s linear infinite; |
| | | pointer-events: none; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .avatar-shell { |
| | | position: relative; |
| | | width: 248px; |
| | | height: 430px; |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: center; |
| | | --base-core: rgba(53, 143, 222, 0.4); |
| | | --base-ring: rgba(39, 122, 201, 0.62); |
| | | --base-glow: rgba(46, 133, 214, 0.28); |
| | | } |
| | | |
| | | .avatar-shell::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: -10px; |
| | | width: 268px; |
| | | height: 58px; |
| | | transform: translateX(-50%); |
| | | border-radius: 50%; |
| | | background: radial-gradient( |
| | | ellipse at center, |
| | | rgba(55, 140, 219, 0.22) 0%, |
| | | rgba(55, 140, 219, 0.11) 46%, |
| | | rgba(55, 140, 219, 0) 74% |
| | | ); |
| | | filter: blur(2.4px); |
| | | animation: baseGlow 4.6s ease-in-out infinite; |
| | | z-index: 1; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .avatar-shell::after { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: 0; |
| | | width: 248px; |
| | | height: 46px; |
| | | transform: translateX(-50%); |
| | | border-radius: 50%; |
| | | border: 2px solid var(--base-ring); |
| | | box-shadow: |
| | | inset 0 0 0 1px rgba(255, 255, 255, 0.64), |
| | | 0 0 24px var(--base-glow); |
| | | animation: basePulse 3.1s ease-in-out infinite; |
| | | z-index: 4; |
| | | } |
| | | |
| | | .avatar-base { |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: 2px; |
| | | width: 224px; |
| | | height: 38px; |
| | | transform: translateX(-50%); |
| | | z-index: 2; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .avatar-base::before { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | border-radius: 50%; |
| | | background: |
| | | radial-gradient( |
| | | ellipse at center, |
| | | rgba(255, 255, 255, 0.96) 0%, |
| | | rgba(255, 255, 255, 0.92) 36%, |
| | | var(--base-core) 68%, |
| | | rgba(38, 118, 195, 0.08) 100% |
| | | ); |
| | | box-shadow: |
| | | 0 0 30px var(--base-core), |
| | | 0 0 10px rgba(255, 255, 255, 0.34) inset; |
| | | z-index: 3; |
| | | } |
| | | |
| | | .avatar-base::after { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | width: 194px; |
| | | height: 194px; |
| | | transform: translate(-50%, -50%); |
| | | border-radius: 50%; |
| | | background: |
| | | conic-gradient( |
| | | from 180deg, |
| | | transparent 0deg, |
| | | var(--base-ring) 48deg, |
| | | transparent 112deg, |
| | | var(--base-ring) 208deg, |
| | | transparent 284deg, |
| | | rgba(33, 114, 191, 0.48) 332deg, |
| | | transparent 360deg |
| | | ); |
| | | -webkit-mask: radial-gradient(circle, transparent 61%, #000 62%, #000 68%, transparent 70%); |
| | | mask: radial-gradient(circle, transparent 61%, #000 62%, #000 68%, transparent 70%); |
| | | opacity: 0.62; |
| | | animation: baseRotate 10.5s linear infinite; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .avatar-shell--active { |
| | | --base-core: rgba(50, 141, 217, 0.52); |
| | | --base-ring: rgba(42, 127, 205, 0.76); |
| | | --base-glow: rgba(38, 130, 211, 0.38); |
| | | } |
| | | |
| | | .avatar-cut { |
| | | position: relative; |
| | | width: 220px; |
| | | height: 430px; |
| | | z-index: 6; |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: center; |
| | | filter: saturate(1.04) drop-shadow(0 14px 18px rgba(24, 44, 66, 0.14)); |
| | | transform-origin: center 82%; |
| | | animation: avatarFloat 3.2s ease-in-out infinite; |
| | | } |
| | | |
| | | .avatar-cut__img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; |
| | | object-position: center bottom; |
| | | display: block; |
| | | } |
| | | |
| | | .agent-card--active .avatar-cut { |
| | | animation-duration: 2.6s; |
| | | } |
| | | |
| | | .highlight-list { |
| | | position: absolute; |
| | | right: 10px; |
| | | top: 14px; |
| | | display: grid; |
| | | gap: 8px; |
| | | width: 220px; |
| | | z-index: 16; |
| | | } |
| | | |
| | | .highlight-item { |
| | | border-radius: 10px; |
| | | padding: 8px 10px; |
| | | font-size: 18px; |
| | | line-height: 1.4; |
| | | color: #fff; |
| | | background: rgba(33, 49, 63, 0.92); |
| | | box-shadow: 0 8px 16px rgba(21, 30, 40, 0.22); |
| | | } |
| | | |
| | | .agent-card--active .highlight-item { |
| | | background: rgba(31, 122, 114, 0.9); |
| | | } |
| | | |
| | | .nav-btn { |
| | | position: absolute; |
| | | top: 212px; |
| | | z-index: 80; |
| | | width: 50px; |
| | | height: 50px; |
| | | border-radius: 50%; |
| | | border: none; |
| | | font-size: 30px; |
| | | color: var(--colorPrimary); |
| | | background: var(--surface-base); |
| | | box-shadow: 0 10px 20px rgba(31, 49, 38, 0.16); |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .nav-btn--left { |
| | | left: 14px; |
| | | } |
| | | |
| | | .nav-btn--right { |
| | | right: 14px; |
| | | } |
| | | |
| | | .ai-fullscreen { |
| | | position: fixed; |
| | | inset: 0; |
| | | z-index: 2100; |
| | | padding: 12px; |
| | | background: rgba(33, 49, 63, 0.24); |
| | | backdrop-filter: blur(2px); |
| | | } |
| | | |
| | | .ai-panel { |
| | | height: 100%; |
| | | border-radius: 22px; |
| | | border: 1px solid var(--surface-border); |
| | | background: linear-gradient(180deg, #f9fcfb 0%, #f0f5f2 100%); |
| | | display: grid; |
| | | grid-template-rows: 62px minmax(0, 1fr) 110px; |
| | | box-shadow: var(--shadow-md); |
| | | } |
| | | |
| | | .ai-panel__top { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 0 24px; |
| | | } |
| | | |
| | | .ai-brand { |
| | | font-size: 34px; |
| | | color: var(--text-primary); |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .ai-close { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: none; |
| | | border-radius: 50%; |
| | | background: transparent; |
| | | font-size: 30px; |
| | | color: var(--text-secondary); |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .ai-panel__center { |
| | | padding: 8px 20px 10px; |
| | | display: grid; |
| | | grid-template-rows: 120px 290px minmax(0, 1fr); |
| | | gap: 10px; |
| | | min-height: 0; |
| | | } |
| | | |
| | | .welcome-card { |
| | | border-radius: 14px; |
| | | background: linear-gradient(135deg, rgba(232, 244, 242, 0.95), rgba(230, 237, 250, 0.9)); |
| | | padding: 16px 18px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | gap: 12px; |
| | | border: 1px solid var(--surface-border); |
| | | } |
| | | |
| | | .welcome-card__text h3 { |
| | | margin: 0; |
| | | font-size: 28px; |
| | | color: var(--text-primary); |
| | | } |
| | | |
| | | .welcome-card__text p { |
| | | margin: 8px 0 0; |
| | | font-size: 20px; |
| | | color: var(--text-secondary); |
| | | } |
| | | |
| | | .mini-avatar { |
| | | width: 120px; |
| | | height: 120px; |
| | | border-radius: 14px; |
| | | border: 1px solid var(--surface-border); |
| | | background-color: #fff; |
| | | background-clip: border-box; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .mini-avatar__img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; |
| | | object-position: center bottom; |
| | | display: block; |
| | | } |
| | | |
| | | .recommend-card { |
| | | border-radius: 14px; |
| | | border: 1px solid var(--surface-border); |
| | | background: rgba(255, 255, 255, 0.86); |
| | | padding: 12px 14px; |
| | | } |
| | | |
| | | .recommend-card__head { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | color: var(--text-primary); |
| | | font-size: 24px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .refresh-btn { |
| | | border: none; |
| | | background: transparent; |
| | | color: var(--text-secondary); |
| | | font-size: 18px; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .recommend-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 8px 18px; |
| | | } |
| | | |
| | | .recommend-item { |
| | | border: 1px solid var(--surface-border); |
| | | border-radius: 8px; |
| | | text-align: left; |
| | | padding: 8px 10px; |
| | | font-size: 18px; |
| | | color: var(--text-secondary); |
| | | background: #fff; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .recommend-item:hover { |
| | | background: rgba(31, 122, 114, 0.08); |
| | | color: var(--colorPrimary); |
| | | } |
| | | |
| | | .chat-card { |
| | | border-radius: 14px; |
| | | border: 1px solid var(--surface-border); |
| | | background: #fff; |
| | | min-height: 0; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .chat-empty { |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: var(--text-tertiary); |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .chat-messages { |
| | | height: 100%; |
| | | overflow-y: auto; |
| | | padding: 14px; |
| | | display: grid; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .chat-row { |
| | | display: flex; |
| | | } |
| | | |
| | | .chat-row--assistant { |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .chat-row--user { |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .chat-bubble { |
| | | max-width: 72%; |
| | | border-radius: 12px; |
| | | padding: 10px 12px; |
| | | font-size: 18px; |
| | | line-height: 1.5; |
| | | white-space: pre-wrap; |
| | | color: var(--text-primary); |
| | | background: var(--surface-soft); |
| | | border: 1px solid var(--surface-border); |
| | | } |
| | | |
| | | .chat-row--user .chat-bubble { |
| | | color: #fff; |
| | | background: linear-gradient(135deg, #1f7a72 0%, #1e5bff 100%); |
| | | border: none; |
| | | } |
| | | |
| | | .ai-panel__input { |
| | | display: grid; |
| | | grid-template-columns: minmax(0, 1fr) 130px; |
| | | gap: 12px; |
| | | padding: 14px 20px 18px; |
| | | } |
| | | |
| | | .ask-input :deep(.el-input__wrapper) { |
| | | height: 74px; |
| | | border-radius: 18px; |
| | | box-shadow: 0 0 0 1px var(--surface-border) inset; |
| | | background: #fff; |
| | | } |
| | | |
| | | .ask-input :deep(.el-input__inner) { |
| | | font-size: 20px; |
| | | } |
| | | |
| | | .send-btn { |
| | | align-self: center; |
| | | height: 56px; |
| | | font-size: 20px; |
| | | min-width: 98px; |
| | | } |
| | | |
| | | .fade-enter-active, |
| | | .fade-leave-active { |
| | | transition: opacity 0.2s ease; |
| | | } |
| | | |
| | | .fade-enter-from, |
| | | .fade-leave-to { |
| | | opacity: 0; |
| | | } |
| | | |
| | | @keyframes avatarFloat { |
| | | 0%, |
| | | 100% { |
| | | transform: translateY(0); |
| | | } |
| | | 50% { |
| | | transform: translateY(-8px); |
| | | } |
| | | } |
| | | |
| | | @keyframes basePulse { |
| | | 0%, |
| | | 100% { |
| | | transform: translateX(-50%) scale(1); |
| | | opacity: 0.88; |
| | | } |
| | | 50% { |
| | | transform: translateX(-50%) scale(1.045); |
| | | opacity: 0.95; |
| | | } |
| | | } |
| | | |
| | | @keyframes baseRotate { |
| | | from { |
| | | transform: translate(-50%, -50%) rotate(0deg); |
| | | } |
| | | to { |
| | | transform: translate(-50%, -50%) rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes baseGlow { |
| | | 0%, |
| | | 100% { |
| | | transform: translateX(-50%) scaleX(1); |
| | | opacity: 0.84; |
| | | } |
| | | 50% { |
| | | transform: translateX(-50%) scaleX(1.06); |
| | | opacity: 0.96; |
| | | } |
| | | } |
| | | |
| | | @keyframes bodySweep { |
| | | 0% { |
| | | transform: translateX(-125%); |
| | | } |
| | | 100% { |
| | | transform: translateX(135%); |
| | | } |
| | | } |
| | | |
| | | @keyframes footerSweep { |
| | | 0% { |
| | | transform: translateX(-120%); |
| | | } |
| | | 100% { |
| | | transform: translateX(140%); |
| | | } |
| | | } |
| | | |
| | | @keyframes railFlow { |
| | | 0% { |
| | | transform: translateX(0); |
| | | opacity: 0; |
| | | } |
| | | 20% { |
| | | opacity: 1; |
| | | } |
| | | 80% { |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: translateX(520%); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | @keyframes nodePulse { |
| | | 0%, |
| | | 100% { |
| | | transform: scale(1); |
| | | } |
| | | 50% { |
| | | transform: scale(1.25); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 1600px) { |
| | | .head-title { |
| | | font-size: 34px; |
| | | height: 60px; |
| | | } |
| | | |
| | | .brain-intro h2 { |
| | | font-size: 36px; |
| | | } |
| | | |
| | | .brain-intro p { |
| | | font-size: 22px; |
| | | } |
| | | |
| | | .intro-sign { |
| | | font-size: 20px; |
| | | } |
| | | |
| | | .agent-card { |
| | | width: 380px; |
| | | margin-left: -190px; |
| | | } |
| | | |
| | | .agent-card__head { |
| | | font-size: 24px; |
| | | height: 54px; |
| | | } |
| | | |
| | | .agent-card__body { |
| | | height: 390px; |
| | | } |
| | | |
| | | .highlight-list { |
| | | width: 184px; |
| | | } |
| | | |
| | | .highlight-item { |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .avatar-shell { |
| | | width: 220px; |
| | | height: 390px; |
| | | } |
| | | |
| | | .avatar-cut { |
| | | width: 202px; |
| | | height: 390px; |
| | | } |
| | | |
| | | .avatar-base { |
| | | width: 194px; |
| | | height: 34px; |
| | | } |
| | | |
| | | .avatar-base::after { |
| | | width: 164px; |
| | | height: 164px; |
| | | } |
| | | |
| | | .avatar-shell::before { |
| | | width: 236px; |
| | | height: 48px; |
| | | bottom: -9px; |
| | | } |
| | | |
| | | .avatar-shell::after { |
| | | width: 220px; |
| | | height: 40px; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .brain-footer { |
| | | margin: 0 52px; |
| | | height: clamp(210px, 23vh, 264px); |
| | | } |
| | | |
| | | .footer-metrics { |
| | | padding: 12px 14px 66px; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .footer-metric { |
| | | min-height: 66px; |
| | | padding: 8px 10px; |
| | | } |
| | | |
| | | .footer-metric--period { |
| | | min-height: 108px; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .footer-metric__label { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .footer-metric__value { |
| | | font-size: 24px; |
| | | } |
| | | |
| | | .footer-metric__hint { |
| | | font-size: 11px; |
| | | } |
| | | |
| | | .footer-period-control { |
| | | gap: 6px; |
| | | } |
| | | |
| | | .period-btn { |
| | | width: 20px; |
| | | height: 20px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .period-input { |
| | | width: 50px; |
| | | height: 22px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .footer-period-slider { |
| | | width: 100%; |
| | | } |
| | | |
| | | .footer-rail { |
| | | left: 14px; |
| | | right: 14px; |
| | | bottom: 14px; |
| | | } |
| | | |
| | | .footer-rail__nodes { |
| | | margin-top: 10px; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .footer-node { |
| | | height: 30px; |
| | | font-size: 12px; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .footer-node__dot { |
| | | width: 7px; |
| | | height: 7px; |
| | | } |
| | | |
| | | .ai-brand { |
| | | font-size: 28px; |
| | | } |
| | | |
| | | .welcome-card__text h3, |
| | | .recommend-card__head { |
| | | font-size: 22px; |
| | | } |
| | | |
| | | .welcome-card__text p, |
| | | .recommend-item, |
| | | .chat-bubble, |
| | | .refresh-btn, |
| | | .chat-empty, |
| | | .ask-input :deep(.el-input__inner), |
| | | .send-btn { |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div class="search_form" style="margin-bottom: 20px;"> |
| | | <div> |
| | | <span class="search_title">客æ·åç§°ï¼</span> |
| | | <el-input v-model="searchForm.customerName" |
| | |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢å®¢æ·</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="back">æµå
¥å
¬æµ·</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="info" |
| | | plain |
| | |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | | :action="upload.url + '?updateSupport=' + upload.updateSupport" |
| | | :action="upload.url" |
| | | :data="upload.data" |
| | | :disabled="upload.isUploading" |
| | | :before-upload="upload.beforeUpload" |
| | | :on-progress="upload.onProgress" |
| | |
| | | import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue"; |
| | | import { Search, Paperclip, Upload } from "@element-plus/icons-vue"; |
| | | import { |
| | | addCustomerPrivate, |
| | | delCustomerPrivate, |
| | | getCustomer, |
| | | getCustomerPrivatePoolById, |
| | | getCustomerPrivatePoolInfo, |
| | | listCustomerPrivatePool, |
| | | updateCustomerPrivatePool, |
| | | addCustomerFollow, |
| | | updateCustomerFollow, |
| | | delCustomerFollow, |
| | | addReturnVisit, |
| | | getReturnVisit, |
| | | } from "@/api/basicData/customerFile.js"; |
| | | import {listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer, backCustomer} from "@/api/basicData/customer.js"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | |
| | | const negotiationFormRef = ref(); |
| | | const negotiationForm = reactive({ |
| | | customerName: "", |
| | | customerPrivatePoolId: "", |
| | | customerId: "", |
| | | followUpMethod: "", |
| | | followUpLevel: "", |
| | | followUpTime: "", |
| | |
| | | searchForm: { |
| | | customerName: "", |
| | | customerType: "", |
| | | type: 0 |
| | | }, |
| | | form: { |
| | | customerName: "", |
| | |
| | | contactPhone: "", |
| | | contactPosition: "", |
| | | customerType: "", |
| | | type: 0 |
| | | }, |
| | | rules: { |
| | | customerName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | // 设置ä¸ä¼ ç请æ±å¤´é¨ |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/customerPrivate/importData", |
| | | url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData", |
| | | data: { |
| | | type: 0 |
| | | }, |
| | | // æä»¶ä¸ä¼ åçåè° |
| | | beforeUpload: file => { |
| | | console.log("æä»¶å³å°ä¸ä¼ ", file); |
| | |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | listCustomerPrivatePool({ ...searchForm.value, ...page }).then(res => { |
| | | listCustomer({ ...searchForm.value, ...page }).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | |
| | | } |
| | | /** ä¸è½½æ¨¡æ¿ */ |
| | | function importTemplate() { |
| | | proxy.download("/customerPrivate/downloadTemplate", {}, "客æ·å¯¼å
¥æ¨¡æ¿.xlsx"); |
| | | proxy.download("/basic/customer/downloadTemplate", {}, "客æ·å¯¼å
¥æ¨¡æ¿.xlsx"); |
| | | } |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | |
| | | contactPosition: "", |
| | | }, |
| | | ]; |
| | | form.value.type = 0; |
| | | form.value.maintenanceTime = getCurrentDate(); |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (type === "edit") { |
| | | getCustomerPrivatePoolById(row.id).then(res => { |
| | | getCustomer(row.id).then(res => { |
| | | form.value = { ...res.data }; |
| | | const persons = String(res.data.contactPerson || "").split(","); |
| | | const phones = String(res.data.contactPhone || "").split(","); |
| | |
| | | form.value.contactPhone = formYYs.value.contactList |
| | | .map(item => item.contactPhone) |
| | | .join(","); |
| | | addCustomer(form.value).then(res => { |
| | | form.value.contactPosition = formYYs.value.contactList |
| | | .map(item => item.contactPosition || "") |
| | | .join(","); |
| | |
| | | form.value.contactPosition = formYYs.value.contactList |
| | | .map(item => item.contactPosition || "") |
| | | .join(","); |
| | | updateCustomerPrivatePool(form.value).then(res => { |
| | | updateCustomer(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/customerPrivate/export", {}, "å®¢æ·æ¡£æ¡.xlsx"); |
| | | proxy.download("/basic/customer/export", {type: 0}, "å®¢æ·æ¡£æ¡.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delCustomerPrivate(ids) |
| | | delCustomer(ids) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | const back = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id); |
| | | ElMessageBox.confirm("éä¸ç客æ·å°æµå
¥å
¬æµ·ï¼æ¯å¦ç¡®è®¤ï¼", "æµå
¥å
¬æµ·æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | return Promise.all(ids.map(id => backCustomer(id))) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("æµå
¥å
¬æµ·æå"); |
| | | selectedRows.value = []; |
| | | getList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(error => { |
| | | if (error === "cancel" || error === "close") { |
| | | proxy.$modal.msg("已忶"); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | |
| | | if (reminderForm.id) { |
| | | submitvalue.value = { |
| | | id: reminderForm.id, |
| | | customerPrivatePoolId: reminderForm.id, |
| | | customerPrivatePoolId: currentCustomerId.value, |
| | | customerId: currentCustomerId.value, |
| | | isEnabled: reminderForm.reminderSwitch ? 1 : 0, |
| | | content: reminderForm.reminderContent, |
| | | reminderTime: reminderForm.reminderTime, |
| | |
| | | }; |
| | | } else { |
| | | submitvalue.value = { |
| | | customerPrivatePoolId: currentCustomerId.value, |
| | | customerId: currentCustomerId.value, |
| | | isEnabled: reminderForm.reminderSwitch ? 1 : 0, |
| | | content: reminderForm.reminderContent, |
| | | reminderTime: reminderForm.reminderTime, |
| | |
| | | // æå¼æ´½è°è¿åº¦å¼¹çª |
| | | const openNegotiationDialog = row => { |
| | | negotiationForm.customerName = row.customerName; |
| | | negotiationForm.customerPrivatePoolId = row.id; |
| | | negotiationForm.customerId = row.id; |
| | | negotiationForm.followUpMethod = ""; |
| | | negotiationForm.followUpLevel = ""; |
| | | negotiationForm.followUpTime = ""; |
| | |
| | | // ä¿®æ¹æä½ |
| | | updateCustomerFollow(negotiationForm).then(res => { |
| | | // æ´æ°æ¬å°æ°æ® |
| | | getCustomer(negotiationForm.customerPrivatePoolId).then(res => { |
| | | getCustomer(negotiationForm.customerId).then(res => { |
| | | // æ´æ°æ¬å°æ°æ® |
| | | negotiationRecords.value = res.data.followUpList || []; |
| | | }); |
| | |
| | | |
| | | // æå¼è¯¦æ
å¼¹çª |
| | | const openDetailDialog = row => { |
| | | getCustomerPrivatePoolInfo(row.id).then(res => { |
| | | getCustomer(row.id).then(res => { |
| | | // å¡«å
客æ·åºæ¬ä¿¡æ¯ |
| | | Object.assign(detailForm, res.data); |
| | | |
| | |
| | | // å°å½åè®°å½æ°æ®å¡«å
å°è¡¨å |
| | | Object.assign(negotiationForm, { |
| | | customerName: row.customerName, |
| | | customerPrivatePoolId: row.customerPrivatePoolId, |
| | | customerId: row.customerId, |
| | | followUpMethod: row.followUpMethod, |
| | | followUpLevel: row.followUpLevel, |
| | | followUpTime: row.followUpTime, |
| | |
| | | // }); |
| | | delCustomerFollow(row.id).then(() => { |
| | | // å 餿ååæ´æ°æ¬å°æ°æ® |
| | | getCustomer(row.customerPrivatePoolId).then(res => { |
| | | getCustomer(row.customerId).then(res => { |
| | | // æ´æ°æ¬å°æ°æ® |
| | | negotiationRecords.value = res.data.followUpList || []; |
| | | }); |
| | |
| | | const downloadAttachment = row => { |
| | | if (row.url) { |
| | | // proxy.download(row.url, {}, row.name); |
| | | proxy.$download.name(row.url); |
| | | proxy.$download.byUrl(row.url, row.originalFilename); |
| | | } else { |
| | | proxy.$modal.msgError("ä¸è½½é¾æ¥ä¸åå¨"); |
| | | } |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div class="search_form" style="margin-bottom: 20px;"> |
| | | <div> |
| | | <span class="search_title">客æ·åç§°ï¼</span> |
| | | <el-input v-model="searchForm.customerName" |
| | |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | | :action="upload.url + '?updateSupport=' + upload.updateSupport" |
| | | :action="upload.url" |
| | | :data="upload.data" |
| | | :disabled="upload.isUploading" |
| | | :before-upload="upload.beforeUpload" |
| | | :on-progress="upload.onProgress" |
| | |
| | | import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue"; |
| | | import { Search, Paperclip, Upload } from "@element-plus/icons-vue"; |
| | | import { |
| | | addCustomer, |
| | | addCustomerPrivatePool, |
| | | delCustomerPrivatePool, |
| | | delCustomer, |
| | | getCustomer, |
| | | shareCustomer, |
| | | listCustomer, |
| | | updateCustomer, |
| | | addCustomerFollow, |
| | | updateCustomerFollow, |
| | | delCustomerFollow, |
| | | addReturnVisit, |
| | | getReturnVisit, |
| | | } from "@/api/basicData/customerFile.js"; |
| | | import { |
| | | listCustomer, |
| | | addCustomer, |
| | | delCustomer, |
| | | updateCustomer, |
| | | getCustomer, |
| | | assignCustomer, |
| | | recycleCustomer, |
| | | shareCustomer, |
| | | } from "@/api/basicData/customer.js"; |
| | | |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | |
| | | type: "text", |
| | | showHide: row => row.usageStatus == 1, |
| | | clickFun: row => { |
| | | recycleCustomer(row); |
| | | recycle(row); |
| | | }, |
| | | }, |
| | | { |
| | |
| | | searchForm: { |
| | | customerName: "", |
| | | customerType: "", |
| | | type: 1 |
| | | }, |
| | | form: { |
| | | customerName: "", |
| | |
| | | contactPhone: "", |
| | | contactPosition: "", |
| | | customerType: "", |
| | | type: 1 |
| | | }, |
| | | rules: { |
| | | customerName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData", |
| | | data: { |
| | | type: 1 |
| | | }, |
| | | // æä»¶ä¸ä¼ åçåè° |
| | | beforeUpload: file => { |
| | | console.log("æä»¶å³å°ä¸ä¼ ", file); |
| | |
| | | const openShareDialog = row => { |
| | | shareForm.id = row.id; |
| | | shareForm.customerName = row.customerName; |
| | | shareForm.boundIds = []; |
| | | shareForm.boundIds = row.userIds || []; |
| | | ensureUserList().then(() => { |
| | | shareDialogVisible.value = true; |
| | | }); |
| | |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | addCustomerPrivatePool({ |
| | | customerId: assignForm.id, |
| | | boundId: assignForm.boundId, |
| | | assignCustomer({ |
| | | id: assignForm.id, |
| | | usageUser: assignForm.boundId, |
| | | }).then(() => { |
| | | proxy.$modal.msgSuccess("åé
æå"); |
| | | closeAssignDialog(); |
| | |
| | | return; |
| | | } |
| | | shareCustomer({ |
| | | customerId: shareForm.id, |
| | | boundIds: shareForm.boundIds, |
| | | id: shareForm.id, |
| | | userIds: shareForm.boundIds, |
| | | }).then(() => { |
| | | proxy.$modal.msgSuccess("å
±äº«æå"); |
| | | closeShareDialog(); |
| | |
| | | }); |
| | | }); |
| | | }; |
| | | const recycleCustomer = row => { |
| | | const recycle = row => { |
| | | ElMessageBox.confirm("ç¡®è®¤åæ¶å®¢æ·â" + row.customerName + "âåï¼", "åæ¶æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | return delCustomerPrivatePool(row.id); |
| | | }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("åæ¶æå"); |
| | | getList(); |
| | | return recycleCustomer({id: row.id}).then(() => { |
| | | proxy.$modal.msgSuccess("åæ¶æå"); |
| | | getList(); |
| | | }) |
| | | }) |
| | | .catch(error => { |
| | | if (error === "cancel" || error === "close") { |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/basic/customer/export", {}, "å®¢æ·æ¡£æ¡.xlsx"); |
| | | proxy.download("/basic/customer/export", {type: 1}, "å®¢æ·æ¡£æ¡.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | const downloadAttachment = row => { |
| | | if (row.url) { |
| | | // proxy.download(row.url, {}, row.name); |
| | | proxy.$download.name(row.url); |
| | | proxy.$download.byUrl(row.url, row.originalFilename); |
| | | } else { |
| | | proxy.$modal.msgError("ä¸è½½é¾æ¥ä¸åå¨"); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">åæ°åç§°ï¼</span> |
| | | <el-input v-model="searchForm.paramName" |
| | | style="width: 200px" |
| | | placeholder="请è¾å
¥åæ°åç§°" |
| | | clearable /> |
| | | <!-- å
³è产åç±»åæç´¢ --> |
| | | <!-- <span class="search_title ml10">å
³è产åç±»åï¼</span> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 200px"d |
| | | placeholder="请è¾å
¥å
³è产åç±»å" |
| | | clearable /> --> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | <el-button @click="handleReset">éç½®</el-button> |
| | | <el-button type="primary" |
| | | @click="handleAdd" |
| | | style="margin-left: 10px">æ°å¢åæ°</el-button> |
| | | <!-- 产åç±»åç»´æ¤æé® --> |
| | | <!-- <el-button type="primary" |
| | | @click="handleProductTypeMaintenance" |
| | | style="margin-left: 10px">产åç±»åç»´æ¤</el-button> --> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="paramName" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | height="calc(100vh - 320px)" |
| | | :tableLoading="tableLoading" |
| | | :isSelection="false" |
| | | :isShowPagination="true" |
| | | @pagination="pagination"> |
| | | </PIMTable> |
| | | </div> |
| | | <!-- æ°å¢/ç¼è¾å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="500px"> |
| | | <el-form :model="formData" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="åæ°ç¼ç " |
| | | prop="paramCode"> |
| | | <el-input v-model="formData.paramCode" |
| | | disabled |
| | | placeholder="èªå¨çæ" /> |
| | | </el-form-item> |
| | | <el-form-item label="åæ°åç§°" |
| | | prop="paramName"> |
| | | <el-input v-model="formData.paramName" |
| | | placeholder="请è¾å
¥åæ°åç§°" /> |
| | | </el-form-item> |
| | | <el-form-item label="åæ°ç±»å" |
| | | prop="paramType"> |
| | | <el-select v-model="formData.paramType" |
| | | @change="handleParamTypeChange" |
| | | placeholder="è¯·éæ©åæ°ç±»å"> |
| | | <el-option label="æ°å¼æ ¼å¼" |
| | | :value="1" /> |
| | | <el-option label="ææ¬æ ¼å¼" |
| | | :value="2" /> |
| | | <el-option label="䏿é项" |
| | | :value="3" /> |
| | | <el-option label="æ¶é´æ ¼å¼" |
| | | :value="4" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <!-- <el-form-item label="å弿¨¡å¼" |
| | | prop="valueMode"> |
| | | <el-select v-model="formData.valueMode" |
| | | placeholder="è¯·éæ©å弿¨¡å¼"> |
| | | <el-option label="åå¼" |
| | | value="1" /> |
| | | <el-option label="åºé´" |
| | | value="2" /> |
| | | </el-select> |
| | | </el-form-item> --> |
| | | <el-form-item label="åä½" |
| | | prop="unit"> |
| | | <el-input v-model="formData.unit" |
| | | placeholder="请è¾å
¥åä½" /> |
| | | </el-form-item> |
| | | <el-form-item label="å弿 ¼å¼" |
| | | v-if="formData.paramType == 1 || formData.paramType == 2" |
| | | prop="paramFormat"> |
| | | <el-input v-model="formData.paramFormat" |
| | | placeholder="请è¾å
¥å弿 ¼å¼" /> |
| | | <!-- <el-select v-model="formData.paramFormat" |
| | | placeholder="è¯·éæ©å弿¨¡å¼"> |
| | | <el-option label="#.00000" |
| | | value="#.00000" /> |
| | | <el-option label="#.0000" |
| | | value="#.0000" /> |
| | | <el-option label="#.000" |
| | | value="#.000" /> |
| | | <el-option label="#.00" |
| | | value="#.00" /> |
| | | </el-select> --> |
| | | </el-form-item> |
| | | <el-form-item label="䏿åå
¸" |
| | | v-else-if="formData.paramType == 3" |
| | | prop="paramFormat"> |
| | | <el-select v-model="formData.paramFormat" |
| | | placeholder="è¯·éæ©å弿¨¡å¼"> |
| | | <el-option v-for="item in dictTypes" |
| | | :key="item.dictType" |
| | | :label="item.dictName" |
| | | :value="item.dictType" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶é´æ ¼å¼" |
| | | v-else-if="formData.paramType == 4" |
| | | prop="paramFormat"> |
| | | <el-select v-model="formData.paramFormat" |
| | | placeholder="è¯·éæ©å弿¨¡å¼"> |
| | | <el-option label="YYYY-MM-DD" |
| | | value="YYYY-MM-DD" /> |
| | | <el-option label="YYYY-MM-DD HH:mm:ss" |
| | | value="YYYY-MM-DD HH:mm:ss" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¯å¦å¿
å¡«" |
| | | prop="isRequired"> |
| | | <el-switch v-model="formData.isRequired" |
| | | :active-value="1" |
| | | :inactive-value="0" /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="formData.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" @click="handleSubmit">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 产åç±»åç»´æ¤å¯¹è¯æ¡ --> |
| | | <!-- <el-dialog v-model="productTypeDialogVisible" |
| | | title="产åç±»åç»´æ¤" |
| | | width="600px"> |
| | | <div class="product-type-header"> |
| | | <el-button type="primary" |
| | | @click="handleAddProductType">æ°å¢äº§åç±»å</el-button> |
| | | </div> |
| | | <el-table :data="productTypeList" |
| | | border |
| | | style="width: 100%; margin-top: 10px; margin-bottom: 20px"> |
| | | <el-table-column prop="typeCode" |
| | | label="ç±»åç¼ç " |
| | | width="150" /> |
| | | <el-table-column prop="typeName" |
| | | label="ç±»ååç§°" /> |
| | | <el-table-column label="æä½" |
| | | width="150"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEditProductType(scope.row)">ç¼è¾</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="handleDeleteProductType(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> --> |
| | | <!-- æ°å¢/ç¼è¾äº§åç±»åå¯¹è¯æ¡ --> |
| | | <!-- <el-dialog v-model="productTypeFormVisible" |
| | | :title="productTypeDialogTitle" |
| | | width="400px"> |
| | | <el-form :model="productTypeForm" |
| | | :rules="productTypeRules" |
| | | ref="productTypeFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="ç±»åç¼ç " |
| | | prop="typeCode"> |
| | | <el-input v-model="productTypeForm.typeCode" |
| | | placeholder="请è¾å
¥ç±»åç¼ç " /> |
| | | </el-form-item> |
| | | <el-form-item label="ç±»ååç§°" |
| | | prop="typeName"> |
| | | <el-input v-model="productTypeForm.typeName" |
| | | placeholder="请è¾å
¥ç±»ååç§°" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="productTypeFormVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" |
| | | @click="handleProductTypeSubmit">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> --> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, reactive } from "vue"; |
| | | import { |
| | | parameterListPage, |
| | | addParameter, |
| | | updateParameter, |
| | | delParameter, |
| | | addBaseParam, |
| | | editBaseParam, |
| | | getBaseParamList, |
| | | removeBaseParam, |
| | | // getProductTypes as getProductTypesApi, |
| | | } from "@/api/basicData/parameterMaintenance.js"; |
| | | import { listType } from "@/api/system/dict/type"; |
| | | import { deptTreeSelect } from "@/api/system/user.js"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "åæ°ç¼ç ", |
| | | prop: "paramCode", |
| | | }, |
| | | { |
| | | label: "åæ°åç§°", |
| | | prop: "paramName", |
| | | }, |
| | | { |
| | | label: "åæ°ç±»å", |
| | | prop: "paramType", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | 1: "primary", |
| | | 2: "info", |
| | | 3: "warning", |
| | | 4: "success", |
| | | }; |
| | | return typeMap[params] || "default"; |
| | | }, |
| | | formatData: val => { |
| | | const labelMap = { |
| | | 1: "æ°å¼æ ¼å¼", |
| | | 2: "ææ¬æ ¼å¼", |
| | | 3: "䏿é项", |
| | | 4: "æ¶é´æ ¼å¼", |
| | | }; |
| | | return labelMap[val] || val; |
| | | }, |
| | | }, |
| | | // { |
| | | // label: "å弿¨¡å¼", |
| | | // prop: "valueMode", |
| | | // dataType: "tag", |
| | | // formatType: params => { |
| | | // return params === 2 ? "warning" : "success"; |
| | | // }, |
| | | // formatData: val => { |
| | | // return val === 2 ? "åºé´" : "åå¼"; |
| | | // }, |
| | | // }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "å弿 ¼å¼", |
| | | prop: "paramFormat", |
| | | }, |
| | | { |
| | | label: "æ¯å¦å¿
å¡«", |
| | | prop: "isRequired", |
| | | dataType: "tag", |
| | | formatType: val => { |
| | | return val === 1 ? "success" : "info"; |
| | | }, |
| | | formatData: val => { |
| | | return val === 1 ? "æ¯" : "å¦"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "夿³¨", |
| | | prop: "remark", |
| | | }, |
| | | { |
| | | label: "å建æ¶é´", |
| | | prop: "createTime", |
| | | }, |
| | | { |
| | | label: "æä½", |
| | | dataType: "action", |
| | | width: "150", |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | clickFun: row => { |
| | | handleEdit(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å é¤", |
| | | clickFun: row => { |
| | | handleDelete(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | paramName: "", |
| | | productName: "", |
| | | }); |
| | | |
| | | // å¯¹è¯æ¡ç¸å
³ |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const formData = reactive({ |
| | | id: null, |
| | | paramCode: "", |
| | | paramName: "", |
| | | paramType: "", |
| | | // valueMode: "1", |
| | | unit: "", |
| | | remark: "", |
| | | isRequired: 0, |
| | | paramFormat: "", |
| | | }); |
| | | const rules = reactive({ |
| | | paramName: [{ required: true, message: "请è¾å
¥åæ°åç§°", trigger: "blur" }], |
| | | paramType: [{ required: true, message: "è¯·éæ©åæ°ç±»å", trigger: "change" }], |
| | | // valueMode: [{ required: true, message: "è¯·éæ©å弿¨¡å¼", trigger: "change" }], |
| | | unit: [ |
| | | { |
| | | required: false, |
| | | message: "请è¾å
¥åä½", |
| | | trigger: "blur", |
| | | validator: (rule, value, callback) => { |
| | | if (formData.paramType === 1 && !value) { |
| | | callback(new Error("æ°å¼ç±»åå¿
须填ååä½")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | }); |
| | | // const productTypes = ref([]); |
| | | const isEdit = ref(false); |
| | | |
| | | // 产åç±»åç»´æ¤ç¸å
³ - 已注é |
| | | // const productTypeDialogVisible = ref(false); |
| | | // const productTypeFormVisible = ref(false); |
| | | // const productTypeDialogTitle = ref(""); |
| | | // const productTypeFormRef = ref(null); |
| | | // const productTypeList = ref([]); |
| | | // const productTypeForm = reactive({ |
| | | // id: null, |
| | | // typeCode: "", |
| | | // typeName: "", |
| | | // }); |
| | | // const productTypeRules = reactive({ |
| | | // typeCode: [{ required: true, message: "请è¾å
¥ç±»åç¼ç ", trigger: "blur" }], |
| | | // typeName: [{ required: true, message: "请è¾å
¥ç±»ååç§°", trigger: "blur" }], |
| | | // }); |
| | | // const isProductTypeEdit = ref(false); |
| | | const handleParamTypeChange = () => { |
| | | if (formData.paramType === 1) { |
| | | formData.paramFormat = "#.00000"; |
| | | } else if (formData.paramType === 4) { |
| | | formData.paramFormat = "YYYY-MM-DD HH:mm:ss"; |
| | | } else { |
| | | formData.paramFormat = ""; |
| | | } |
| | | // 触ååä½å段éªè¯ |
| | | if (formRef.value) { |
| | | formRef.value.validateField("unit"); |
| | | } |
| | | }; |
| | | // 产åç±»åç»´æ¤æé®ç¹å»äºä»¶ - 已注é |
| | | // const handleProductTypeMaintenance = () => { |
| | | // productTypeDialogVisible.value = true; |
| | | // getProductTypeList(); |
| | | // }; |
| | | |
| | | // è·å产åç±»åå表 - 已注é |
| | | // const getProductTypeList = () => { |
| | | // productTypeList.value = [ |
| | | // { id: 1, typeCode: "TYPE001", typeName: "3.5ç å" }, |
| | | // { id: 2, typeCode: "TYPE002", typeName: "5.0ç å" }, |
| | | // { id: 3, typeCode: "TYPE003", typeName: "æ¿æ" }, |
| | | // ]; |
| | | // }; |
| | | |
| | | // æ°å¢äº§åç±»å - 已注é |
| | | // const handleAddProductType = () => { |
| | | // isProductTypeEdit.value = false; |
| | | // productTypeDialogTitle.value = "æ°å¢äº§åç±»å"; |
| | | // productTypeForm.id = null; |
| | | // productTypeForm.typeCode = ""; |
| | | // productTypeForm.typeName = ""; |
| | | // productTypeFormVisible.value = true; |
| | | // }; |
| | | |
| | | // ç¼è¾äº§åç±»å - 已注é |
| | | // const handleEditProductType = row => { |
| | | // isProductTypeEdit.value = true; |
| | | // productTypeDialogTitle.value = "ç¼è¾äº§åç±»å"; |
| | | // productTypeForm.id = row.id; |
| | | // productTypeForm.typeCode = row.typeCode; |
| | | // productTypeForm.typeName = row.typeName; |
| | | // productTypeFormVisible.value = true; |
| | | // }; |
| | | |
| | | // å é¤äº§åç±»å - 已注é |
| | | // const handleDeleteProductType = row => { |
| | | // ElMessageBox.confirm("ç¡®å®è¦å é¤è¯¥äº§åç±»ååï¼", "æç¤º", { |
| | | // confirmButtonText: "ç¡®å®", |
| | | // cancelButtonText: "åæ¶", |
| | | // type: "warning", |
| | | // }) |
| | | // .then(() => { |
| | | // ElMessage.success("å 餿å"); |
| | | // getProductTypeList(); |
| | | // }) |
| | | // .catch(() => {}); |
| | | // }; |
| | | |
| | | // æäº¤äº§åç±»å表å - 已注é |
| | | // const handleProductTypeSubmit = () => { |
| | | // productTypeFormRef.value.validate(valid => { |
| | | // if (valid) { |
| | | // ElMessage.success(isProductTypeEdit.value ? "ç¼è¾æå" : "æ°å¢æå"); |
| | | // productTypeFormVisible.value = false; |
| | | // getProductTypeList(); |
| | | // } |
| | | // }); |
| | | // }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | /** éç½®æé®æä½ */ |
| | | const handleReset = () => { |
| | | searchForm.paramName = ""; |
| | | searchForm.productName = ""; |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | // è°ç¨æ°æ¥å£ /baseParam/list |
| | | getBaseParamList({ |
| | | paramName: searchForm.paramName, |
| | | current: page.current, |
| | | size: page.size, |
| | | }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | if (res.code === 200) { |
| | | tableData.value = res.data.records || []; |
| | | page.total = res.data.total || 0; |
| | | console.log(tableData.value, "tableData.value"); |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | ElMessage.error("æ¥è¯¢å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // è·å产åç±»åå表 - 已注é |
| | | // const getProductTypes = () => { |
| | | // productTypes.value = [ |
| | | // { label: "3.5ç å", value: "type1" }, |
| | | // { label: "5.0ç å", value: "type2" }, |
| | | // { label: "æ¿æ", value: "type3" }, |
| | | // ]; |
| | | // }; |
| | | |
| | | // æ°å¢æé®ç¹å»äºä»¶ |
| | | const handleAdd = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢åæ°"; |
| | | // é置表å |
| | | formData.id = null; |
| | | formData.paramCode = ""; |
| | | formData.paramName = ""; |
| | | formData.paramType = ""; |
| | | // formData.valueMode = "1"; |
| | | formData.unit = ""; |
| | | formData.remark = ""; |
| | | formData.isRequired = 0; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // ç¼è¾æé®ç¹å»äºä»¶ |
| | | const handleEdit = row => { |
| | | isEdit.value = true; |
| | | dialogTitle.value = "ç¼è¾åæ°"; |
| | | // å¡«å
è¡¨åæ°æ® |
| | | formData.id = row.id; |
| | | formData.paramCode = row.paramCode || ""; |
| | | formData.paramName = row.paramName || ""; |
| | | formData.paramType = row.paramType !== undefined ? row.paramType : null; |
| | | // formData.valueMode = |
| | | // row.valueMode !== undefined ? String(row.valueMode) : "1"; |
| | | formData.unit = row.unit || ""; |
| | | formData.remark = row.remark || ""; |
| | | formData.paramFormat = row.paramFormat || ""; |
| | | formData.isRequired = row.isRequired || 0; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // å 餿é®ç¹å»äºä»¶ |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("ç¡®å®è¦å é¤è¿æ¡æ°æ®åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // è°ç¨æ°æ¥å£ /baseParam/remove/{id} |
| | | removeBaseParam([row.id]) |
| | | .then(res => { |
| | | ElMessage.success("å 餿å"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("å é¤å¤±è´¥"); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | // åæ¶å é¤ |
| | | }); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const handleSubmit = () => { |
| | | if (formData.paramType == 3 && !formData.paramFormat) { |
| | | ElMessage.warning("䏿åå
¸ä¸è½ä¸ºç©ºï¼"); |
| | | return; |
| | | } |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (formData.id) { |
| | | // ç¼è¾ä½¿ç¨æ°æ¥å£ /technologyParam/edit |
| | | editBaseParam(formData) |
| | | .then(res => { |
| | | ElMessage.success("ç¼è¾æå"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | // ElMessage.error("ç¼è¾å¤±è´¥"); |
| | | }); |
| | | } else { |
| | | // æ°å¢ä½¿ç¨æ°æ¥å£ /technologyParam/add |
| | | addBaseParam(formData) |
| | | .then(res => { |
| | | ElMessage.success("æ°å¢æå"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("æ°å¢å¤±è´¥"); |
| | | }); |
| | | } |
| | | } else { |
| | | return false; |
| | | } |
| | | }); |
| | | }; |
| | | const dictTypes = ref([]); |
| | | const getDictTypes = () => { |
| | | listType({ pageNum: 1, pageSize: 1000 }).then(res => { |
| | | dictTypes.value = res.rows || []; |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getDictTypes(); |
| | | getList(); |
| | | // getProductTypes(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .app-container { |
| | | padding: 24px; |
| | | background-color: #f0f2f5; |
| | | min-height: calc(100vh - 48px); |
| | | } |
| | | |
| | | .search_form { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 24px; |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .search_title { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .ml10 { |
| | | margin-left: 10px; |
| | | } |
| | | } |
| | | |
| | | .table_list { |
| | | background-color: #ffffff; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | overflow: hidden; |
| | | height: calc(100vh - 230px); |
| | | } |
| | | |
| | | :deep(.el-table) { |
| | | border: none; |
| | | border-radius: 6px; |
| | | overflow: hidden; |
| | | box-shadow: 0 4px 16px rgba(102, 126, 234, 0.1); |
| | | |
| | | .el-table__header-wrapper { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | |
| | | th { |
| | | background: transparent; |
| | | font-weight: 600; |
| | | // color: #ffffff; |
| | | border-bottom: none; |
| | | padding: 16px 0; |
| | | letter-spacing: 0.5px; |
| | | } |
| | | } |
| | | |
| | | .el-table__body-wrapper { |
| | | tr { |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | background: linear-gradient( |
| | | 90deg, |
| | | rgba(102, 126, 234, 0.05) 0%, |
| | | rgba(118, 75, 162, 0.05) 100% |
| | | ); |
| | | transform: scale(1.002); |
| | | box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1); |
| | | } |
| | | |
| | | td { |
| | | border-bottom: 1px solid #f0f0f0; |
| | | padding: 14px 0; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | tr.current-row { |
| | | background: linear-gradient( |
| | | 90deg, |
| | | rgba(102, 126, 234, 0.08) 0%, |
| | | rgba(118, 75, 162, 0.08) 100% |
| | | ); |
| | | } |
| | | |
| | | // æ°å¼åæ®µæ ·å¼ |
| | | .quantity-cell, |
| | | .volume-cell, |
| | | .dimension-cell { |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | font-family: "Courier New", monospace; |
| | | text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2); |
| | | } |
| | | |
| | | // è§æ ¼åæ®µæ ·å¼ |
| | | .spec-cell { |
| | | color: #67c23a; |
| | | font-weight: 500; |
| | | padding: 4px 8px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | // ç¼ç åæ®µæ ·å¼ |
| | | .code-cell { |
| | | color: #e6a23c; |
| | | font-family: "Courier New", monospace; |
| | | font-weight: 500; |
| | | padding: 4px 8px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | // æ¥æåæ®µæ ·å¼ |
| | | .date-cell { |
| | | color: #909399; |
| | | font-style: italic; |
| | | } |
| | | } |
| | | |
| | | .el-table__empty-block { |
| | | padding: 60px 0; |
| | | background-color: #fafafa; |
| | | } |
| | | } |
| | | |
| | | .pagination-container { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | padding: 16px 20px; |
| | | background-color: #ffffff; |
| | | border-top: 1px solid #ebeef5; |
| | | border-radius: 0 0 12px 12px; |
| | | } |
| | | |
| | | :deep(.el-button) { |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: translateY(-1px); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .app-container { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .search_form { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | gap: 12px; |
| | | |
| | | .el-form { |
| | | width: 100%; |
| | | |
| | | .el-form-item { |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | .el-button { |
| | | margin-right: 12px; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-table) { |
| | | th, |
| | | td { |
| | | padding: 10px 0; |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <div class="app-container product-view"> |
| | | <div class="left"> |
| | | <div> |
| | | <el-input |
| | | v-model="search" |
| | | style="width: 210px" |
| | | placeholder="è¾å
¥å
³é®åè¿è¡æç´¢" |
| | | @change="searchFilter" |
| | | @clear="searchFilter" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | <el-button |
| | | v-if="false" |
| | | type="primary" |
| | | @click="openProDia('addOne')" |
| | | style="margin-left: 10px" |
| | | >æ°å¢äº§å大类</el-button |
| | | > |
| | | <el-input v-model="search" |
| | | style="width: 210px" |
| | | placeholder="è¾å
¥å
³é®åè¿è¡æç´¢" |
| | | @change="searchFilter" |
| | | @clear="searchFilter" |
| | | clearable |
| | | prefix-icon="Search" /> |
| | | <el-button v-if="false" |
| | | type="primary" |
| | | @click="openProDia('addOne')" |
| | | style="margin-left: 10px">æ°å¢äº§å大类</el-button> |
| | | </div> |
| | | <div ref="containerRef"> |
| | | <el-tree |
| | | ref="tree" |
| | | v-loading="treeLoad" |
| | | :data="list" |
| | | @node-click="handleNodeClick" |
| | | :expand-on-click-node="false" |
| | | @node-expand="handleNodeExpand" |
| | | @node-collapse="handleNodeCollapse" |
| | | :key="treeKey" |
| | | :default-expanded-keys="expandedKeys" |
| | | :filter-node-method="filterNode" |
| | | :props="{ children: 'children', label: 'label' }" |
| | | highlight-current |
| | | node-key="id" |
| | | class="product-tree-scroll" |
| | | style="height: calc(100vh - 190px); overflow-y: auto" |
| | | > |
| | | <el-tree ref="tree" |
| | | v-loading="treeLoad" |
| | | :data="list" |
| | | @node-click="handleNodeClick" |
| | | :expand-on-click-node="false" |
| | | @node-expand="handleNodeExpand" |
| | | @node-collapse="handleNodeCollapse" |
| | | :key="treeKey" |
| | | :default-expanded-keys="expandedKeys" |
| | | :filter-node-method="filterNode" |
| | | :props="{ children: 'children', label: 'label' }" |
| | | highlight-current |
| | | node-key="id" |
| | | class="product-tree-scroll" |
| | | style="height: calc(100vh - 190px); overflow-y: auto"> |
| | | <template #default="{ node, data }"> |
| | | <div class="custom-tree-node"> |
| | | <span class="tree-node-content"> |
| | |
| | | <span class="tree-node-label">{{ data.label }}</span> |
| | | </span> |
| | | <div> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | :disabled="isTopLevelNode(data, node)" |
| | | @click="openProDia('edit', data)" |
| | | > |
| | | <el-button type="primary" |
| | | link |
| | | :disabled="isTopLevelNode(data, node)" |
| | | @click="openProDia('edit', data, node)"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button type="primary" link @click="openProDia('add', data)"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="openProDia('add', data, node)"> |
| | | æ·»å 产å |
| | | </el-button> |
| | | <el-button |
| | | v-if="!node.childNodes.length" |
| | | style="margin-left: 4px" |
| | | type="danger" |
| | | link |
| | | :disabled="isTopLevelNode(data, node)" |
| | | @click="remove(node, data)" |
| | | > |
| | | <el-button v-if="!node.childNodes.length" |
| | | style="margin-left: 4px" |
| | | type="danger" |
| | | link |
| | | :disabled="isTopLevelNode(data, node)" |
| | | @click="remove(node, data)"> |
| | | å é¤ |
| | | </el-button> |
| | | </div> |
| | |
| | | <el-button type="primary" @click="openModelDia('add')"> |
| | | æ°å¢å°ºå¯¸ |
| | | </el-button> |
| | | <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" /> |
| | | <el-button |
| | | type="danger" |
| | | @click="handleDelete" |
| | | style="margin-left: 10px" |
| | | plain |
| | | > |
| | | <ImportExcel :product-id="currentId" |
| | | @uploadSuccess="getModelList" /> |
| | | <el-button type="danger" |
| | | @click="handleDelete" |
| | | style="margin-left: 10px" |
| | | plain> |
| | | å é¤ |
| | | </el-button> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | ></PIMTable> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination"></PIMTable> |
| | | </div> |
| | | <el-dialog v-model="productDia" title="产å" width="400px" @keydown.enter.prevent> |
| | | <el-form |
| | | :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | <el-dialog v-model="productDia" |
| | | title="产å" |
| | | width="400px" |
| | | @keydown.enter.prevent> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产ååç§°ï¼" prop="productName"> |
| | | <el-input |
| | | v-model="form.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°" |
| | | maxlength="20" |
| | | show-word-limit |
| | | clearable |
| | | @keydown.enter.prevent |
| | | /> |
| | | <el-form-item label="产ååç§°ï¼" |
| | | prop="productName"> |
| | | <el-input v-model="form.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°" |
| | | maxlength="20" |
| | | show-word-limit |
| | | clearable |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeProDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog |
| | | v-model="modelDia" |
| | | title="尺寸" |
| | | width="400px" |
| | | @close="closeModelDia" |
| | | @keydown.enter.prevent |
| | | > |
| | | <el-form |
| | | :model="modelForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="modelRules" |
| | | ref="modelFormRef" |
| | | > |
| | | <el-dialog v-model="modelDia" |
| | | title="尺寸" |
| | | width="400px" |
| | | @close="closeModelDia" |
| | | @keydown.enter.prevent> |
| | | <el-form :model="modelForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="modelRules" |
| | | ref="modelFormRef"> |
| | | <el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产åç¼å·ï¼" |
| | | prop="productCode"> |
| | | <el-input v-model="modelForm.productCode" |
| | | placeholder="请è¾å
¥äº§åç¼å·" |
| | | clearable |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="尺寸ï¼" prop="model"> |
| | | <el-input |
| | | v-model="modelForm.model" |
| | | placeholder="请è¾å
¥å°ºå¯¸" |
| | | clearable |
| | | @keydown.enter.prevent |
| | | /> |
| | | <el-form-item label="尺寸ï¼" |
| | | prop="model"> |
| | | <el-input v-model="modelForm.model" |
| | | placeholder="请è¾å
¥å°ºå¯¸" |
| | | clearable |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input |
| | | v-model="modelForm.unit" |
| | | placeholder="请è¾å
¥åä½" |
| | | clearable |
| | | @keydown.enter.prevent |
| | | /> |
| | | <el-form-item label="åä½ï¼" |
| | | prop="unit"> |
| | | <el-input v-model="modelForm.unit" |
| | | placeholder="请è¾å
¥åä½" |
| | | clearable |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitModelForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitModelForm">确认</el-button> |
| | | <el-button @click="closeModelDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { nextTick, ref } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | addOrEditProduct, |
| | | addOrEditProductModel, |
| | | delProduct, |
| | | delProductModel, |
| | | modelListPage, |
| | | productTreeList, |
| | | } from "@/api/basicData/product.js"; |
| | | import ImportExcel from "./ImportExcel/index.vue"; |
| | | import { nextTick, ref } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | addOrEditProduct, |
| | | addOrEditProductModel, |
| | | delProduct, |
| | | delProductModel, |
| | | modelListPage, |
| | | productTreeList, |
| | | } from "@/api/basicData/product.js"; |
| | | import ImportExcel from "./ImportExcel/index.vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tree = ref(null); |
| | | const containerRef = ref(null); |
| | | const treeKey = ref(0); |
| | | const expandedKeySet = new Set(); |
| | | const EXPANDED_STORAGE_KEY = "basicData_product_tree_expanded_keys_v2"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const tree = ref(null); |
| | | const containerRef = ref(null); |
| | | const treeKey = ref(0); |
| | | const expandedKeySet = new Set(); |
| | | const EXPANDED_STORAGE_KEY = "basicData_product_tree_expanded_keys_v2"; |
| | | |
| | | const loadExpandedKeys = () => { |
| | | if (typeof window === "undefined") { |
| | | return []; |
| | | } |
| | | try { |
| | | const saved = localStorage.getItem(EXPANDED_STORAGE_KEY); |
| | | return saved ? JSON.parse(saved) : []; |
| | | } catch (error) { |
| | | console.error(error); |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | const saveExpandedKeys = () => { |
| | | if (typeof window === "undefined") { |
| | | return; |
| | | } |
| | | localStorage.setItem( |
| | | EXPANDED_STORAGE_KEY, |
| | | JSON.stringify(Array.from(expandedKeySet)) |
| | | ); |
| | | }; |
| | | |
| | | loadExpandedKeys().forEach((key) => expandedKeySet.add(key)); |
| | | |
| | | const syncExpandedKeysFromTree = () => { |
| | | const keys = []; |
| | | const walk = (nodes) => { |
| | | (nodes || []).forEach((item) => { |
| | | if (item.expanded && item.data?.id !== undefined) { |
| | | keys.push(item.data.id); |
| | | } |
| | | if (item.childNodes && item.childNodes.length) { |
| | | walk(item.childNodes); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | walk(tree.value?.root?.childNodes); |
| | | expandedKeySet.clear(); |
| | | keys.forEach((key) => expandedKeySet.add(key)); |
| | | expandedKeys.value = keys; |
| | | saveExpandedKeys(); |
| | | }; |
| | | |
| | | const normalizeExpandedKeys = (treeData) => { |
| | | const parentMap = new Map(); |
| | | const walk = (nodes, parentId = null) => { |
| | | (nodes || []).forEach((item) => { |
| | | parentMap.set(item.id, parentId); |
| | | if (item.children && item.children.length) { |
| | | walk(item.children, item.id); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | walk(treeData); |
| | | |
| | | const normalizedKeys = Array.from(expandedKeySet).filter((key) => { |
| | | if (!parentMap.has(key)) { |
| | | return false; |
| | | const loadExpandedKeys = () => { |
| | | if (typeof window === "undefined") { |
| | | return []; |
| | | } |
| | | let currentId = key; |
| | | while (parentMap.has(currentId)) { |
| | | const parentId = parentMap.get(currentId); |
| | | if (!parentId) { |
| | | return true; |
| | | } |
| | | if (!expandedKeySet.has(parentId)) { |
| | | try { |
| | | const saved = localStorage.getItem(EXPANDED_STORAGE_KEY); |
| | | return saved ? JSON.parse(saved) : []; |
| | | } catch (error) { |
| | | console.error(error); |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | const saveExpandedKeys = () => { |
| | | if (typeof window === "undefined") { |
| | | return; |
| | | } |
| | | localStorage.setItem( |
| | | EXPANDED_STORAGE_KEY, |
| | | JSON.stringify(Array.from(expandedKeySet)) |
| | | ); |
| | | }; |
| | | |
| | | loadExpandedKeys().forEach(key => expandedKeySet.add(key)); |
| | | |
| | | const syncExpandedKeysFromTree = () => { |
| | | const keys = []; |
| | | const walk = nodes => { |
| | | (nodes || []).forEach(item => { |
| | | if (item.expanded && item.data?.id !== undefined) { |
| | | keys.push(item.data.id); |
| | | } |
| | | if (item.childNodes && item.childNodes.length) { |
| | | walk(item.childNodes); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | walk(tree.value?.root?.childNodes); |
| | | expandedKeySet.clear(); |
| | | keys.forEach(key => expandedKeySet.add(key)); |
| | | expandedKeys.value = keys; |
| | | saveExpandedKeys(); |
| | | }; |
| | | |
| | | const normalizeExpandedKeys = treeData => { |
| | | const parentMap = new Map(); |
| | | const walk = (nodes, parentId = null) => { |
| | | (nodes || []).forEach(item => { |
| | | parentMap.set(item.id, parentId); |
| | | if (item.children && item.children.length) { |
| | | walk(item.children, item.id); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | walk(treeData); |
| | | |
| | | const normalizedKeys = Array.from(expandedKeySet).filter(key => { |
| | | if (!parentMap.has(key)) { |
| | | return false; |
| | | } |
| | | currentId = parentId; |
| | | } |
| | | return true; |
| | | }); |
| | | |
| | | if (normalizedKeys.length !== expandedKeySet.size) { |
| | | expandedKeySet.clear(); |
| | | normalizedKeys.forEach((key) => expandedKeySet.add(key)); |
| | | saveExpandedKeys(); |
| | | } |
| | | }; |
| | | |
| | | const productDia = ref(false); |
| | | const modelDia = ref(false); |
| | | const modelOperationType = ref(""); |
| | | const search = ref(""); |
| | | const currentId = ref(""); |
| | | const currentParentId = ref(""); |
| | | const operationType = ref(""); |
| | | const treeLoad = ref(false); |
| | | const list = ref([]); |
| | | const expandedKeys = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "尺寸", |
| | | prop: "model", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openModelDia("edit", row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const isShowButton = ref(false); |
| | | const selectedRows = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | const data = reactive({ |
| | | form: { |
| | | productName: "", |
| | | }, |
| | | rules: { |
| | | productName: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | { max: 20, message: "产ååç§°ä¸è½è¶
è¿20个å符", trigger: "blur" }, |
| | | ], |
| | | }, |
| | | modelForm: { |
| | | model: "", |
| | | unit: "", |
| | | }, |
| | | modelRules: { |
| | | model: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unit: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules, modelForm, modelRules } = toRefs(data); |
| | | // æ¥è¯¢äº§åæ |
| | | const getProductTreeList = () => { |
| | | treeLoad.value = true; |
| | | productTreeList() |
| | | .then((res) => { |
| | | list.value = res || []; |
| | | normalizeExpandedKeys(list.value); |
| | | expandedKeys.value = Array.from(expandedKeySet); |
| | | treeKey.value += 1; |
| | | nextTick(() => { |
| | | tree.value?.setDefaultExpandedKeys?.(expandedKeys.value); |
| | | }); |
| | | }) |
| | | .catch((err) => { |
| | | console.error(err); |
| | | }) |
| | | .finally(() => { |
| | | treeLoad.value = false; |
| | | }); |
| | | }; |
| | | const handleNodeExpand = (data) => { |
| | | nextTick(syncExpandedKeysFromTree); |
| | | }; |
| | | const handleNodeCollapse = (data, node) => { |
| | | node?.eachNode?.((item) => { |
| | | item.collapse(); |
| | | }); |
| | | nextTick(syncExpandedKeysFromTree); |
| | | }; |
| | | // è¿æ»¤äº§åæ |
| | | const searchFilter = () => { |
| | | proxy.$refs.tree.filter(search.value); |
| | | }; |
| | | const isTopLevelNode = (data, node) => { |
| | | if (node?.level !== undefined) { |
| | | return node.level === 1; |
| | | } |
| | | return [null, undefined, "", 0, "0"].includes(data?.parentId); |
| | | }; |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProDia = (type, data) => { |
| | | if (data && type === "edit" && isTopLevelNode(data)) { |
| | | proxy.$modal.msgWarning("ä¸çº§èç¹ä¸è½ç¼è¾æå é¤"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | productDia.value = true; |
| | | form.value.productName = ""; |
| | | if (type === "edit") { |
| | | form.value.productName = data.productName; |
| | | } |
| | | }; |
| | | // æå¼å°ºå¯¸å¼¹æ¡ |
| | | const openModelDia = (type, data) => { |
| | | modelOperationType.value = type; |
| | | modelDia.value = true; |
| | | modelForm.value.model = ""; |
| | | modelForm.value.model = ""; |
| | | modelForm.value.id = ""; |
| | | if (type === "edit") { |
| | | modelForm.value = { ...data }; |
| | | } |
| | | }; |
| | | // æäº¤äº§ååç§°ä¿®æ¹ |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate((valid) => { |
| | | if (valid) { |
| | | if (operationType.value === "add") { |
| | | form.value.parentId = currentId.value; |
| | | form.value.id = ""; |
| | | } else if (operationType.value === "addOne") { |
| | | form.value.id = ""; |
| | | form.value.parentId = ""; |
| | | } else { |
| | | form.value.id = currentId.value; |
| | | form.value.parentId = ""; |
| | | let currentId = key; |
| | | while (parentMap.has(currentId)) { |
| | | const parentId = parentMap.get(currentId); |
| | | if (!parentId) { |
| | | return true; |
| | | } |
| | | if (!expandedKeySet.has(parentId)) { |
| | | return false; |
| | | } |
| | | currentId = parentId; |
| | | } |
| | | addOrEditProduct(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeProDia(); |
| | | getProductTreeList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProDia = () => { |
| | | proxy.$refs.formRef.resetFields(); |
| | | productDia.value = false; |
| | | }; |
| | | return true; |
| | | }); |
| | | |
| | | // å é¤äº§å |
| | | const remove = (node, data) => { |
| | | if (isTopLevelNode(data, node)) { |
| | | proxy.$modal.msgWarning("ä¸çº§èç¹ä¸è½ç¼è¾æå é¤"); |
| | | return; |
| | | } |
| | | let ids = []; |
| | | ids.push(data.id); |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delProduct(ids) |
| | | .then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | if (normalizedKeys.length !== expandedKeySet.size) { |
| | | expandedKeySet.clear(); |
| | | normalizedKeys.forEach(key => expandedKeySet.add(key)); |
| | | saveExpandedKeys(); |
| | | } |
| | | }; |
| | | |
| | | const productDia = ref(false); |
| | | const modelDia = ref(false); |
| | | const modelOperationType = ref(""); |
| | | const search = ref(""); |
| | | const currentId = ref(""); |
| | | const currentParentId = ref(""); |
| | | /** 产åå¼¹çªï¼add åç¶èç¹ idï¼edit åå½åèç¹ id ä¸ parentIdï¼ä¸ä¾èµæ éä¸é¡¹ï¼ */ |
| | | const productDialogTarget = ref(null); |
| | | const operationType = ref(""); |
| | | const treeLoad = ref(false); |
| | | const list = ref([]); |
| | | const expandedKeys = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "产åç¼å·", |
| | | prop: "productCode", |
| | | }, |
| | | { |
| | | label: "尺寸", |
| | | prop: "model", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openModelDia("edit", row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const isShowButton = ref(false); |
| | | const selectedRows = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | const data = reactive({ |
| | | form: { |
| | | productName: "", |
| | | }, |
| | | rules: { |
| | | productName: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | { max: 20, message: "产ååç§°ä¸è½è¶
è¿20个å符", trigger: "blur" }, |
| | | ], |
| | | }, |
| | | modelForm: { |
| | | model: "", |
| | | unit: "", |
| | | productCode: "", |
| | | }, |
| | | modelRules: { |
| | | model: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unit: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productCode: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules, modelForm, modelRules } = toRefs(data); |
| | | // æ¥è¯¢äº§åæ |
| | | const getProductTreeList = () => { |
| | | treeLoad.value = true; |
| | | productTreeList() |
| | | .then(res => { |
| | | list.value = res || []; |
| | | normalizeExpandedKeys(list.value); |
| | | expandedKeys.value = Array.from(expandedKeySet); |
| | | treeKey.value += 1; |
| | | nextTick(() => { |
| | | tree.value?.setDefaultExpandedKeys?.(expandedKeys.value); |
| | | }); |
| | | }) |
| | | .catch(err => { |
| | | console.error(err); |
| | | }) |
| | | .finally(() => { |
| | | treeLoad.value = false; |
| | | }); |
| | | }; |
| | | const handleNodeExpand = data => { |
| | | nextTick(syncExpandedKeysFromTree); |
| | | }; |
| | | const handleNodeCollapse = (data, node) => { |
| | | node?.eachNode?.(item => { |
| | | item.collapse(); |
| | | }); |
| | | nextTick(syncExpandedKeysFromTree); |
| | | }; |
| | | // è¿æ»¤äº§åæ |
| | | const searchFilter = () => { |
| | | proxy.$refs.tree.filter(search.value); |
| | | }; |
| | | const isTopLevelNode = (data, node) => { |
| | | if (node?.level !== undefined) { |
| | | return node.level === 1; |
| | | } |
| | | return [null, undefined, "", 0, "0"].includes(data?.parentId); |
| | | }; |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProDia = (type, data, node) => { |
| | | if (data && type === "edit" && isTopLevelNode(data, node)) { |
| | | proxy.$modal.msgWarning("ä¸çº§èç¹ä¸è½ç¼è¾æå é¤"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | productDialogTarget.value = null; |
| | | if (type === "add" && data) { |
| | | productDialogTarget.value = { parentId: data.id }; |
| | | } else if (type === "edit" && data) { |
| | | let parentId = data.parentId; |
| | | if ( |
| | | [null, undefined, ""].includes(parentId) && |
| | | node?.parent?.data?.id != null |
| | | ) { |
| | | parentId = node.parent.data.id; |
| | | } |
| | | productDialogTarget.value = { id: data.id, parentId }; |
| | | } |
| | | productDia.value = true; |
| | | form.value.productName = |
| | | type === "edit" && data ? data.productName : ""; |
| | | }; |
| | | // æå¼è§æ ¼åå·å¼¹æ¡ |
| | | const openModelDia = (type, data) => { |
| | | modelOperationType.value = type; |
| | | modelDia.value = true; |
| | | modelForm.value.model = ""; |
| | | modelForm.value.unit = ""; |
| | | modelForm.value.productCode = ""; |
| | | modelForm.value.id = ""; |
| | | if (type === "edit") { |
| | | modelForm.value = { ...data }; |
| | | } |
| | | }; |
| | | // æäº¤äº§ååç§°ä¿®æ¹ |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add") { |
| | | form.value.parentId = |
| | | productDialogTarget.value?.parentId ?? currentId.value; |
| | | form.value.id = ""; |
| | | } else if (operationType.value === "addOne") { |
| | | form.value.id = ""; |
| | | form.value.parentId = ""; |
| | | } else { |
| | | form.value.id = |
| | | productDialogTarget.value?.id ?? currentId.value; |
| | | form.value.parentId = productDialogTarget.value?.parentId ?? ""; |
| | | } |
| | | addOrEditProduct(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeProDia(); |
| | | getProductTreeList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | } |
| | | }); |
| | | }; |
| | | // éæ©äº§å |
| | | const handleNodeClick = (val, node, el) => { |
| | | // 夿æ¯å¦ä¸ºå¶åèç¹ |
| | | isShowButton.value = !(val.children && val.children.length > 0); |
| | | // åªæå¶åèç¹ææ§è¡ä»¥ä¸é»è¾ |
| | | currentId.value = val.id; |
| | | currentParentId.value = val.parentId; |
| | | getModelList(); |
| | | }; |
| | | }; |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProDia = () => { |
| | | proxy.$refs.formRef.resetFields(); |
| | | productDialogTarget.value = null; |
| | | productDia.value = false; |
| | | }; |
| | | |
| | | // æäº¤å°ºå¯¸ä¿®æ¹ |
| | | const submitModelForm = () => { |
| | | proxy.$refs.modelFormRef.validate((valid) => { |
| | | if (valid) { |
| | | modelForm.value.productId = currentId.value; |
| | | addOrEditProductModel(modelForm.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeModelDia(); |
| | | getModelList(); |
| | | }); |
| | | // å é¤äº§å |
| | | const remove = (node, data) => { |
| | | if (isTopLevelNode(data, node)) { |
| | | proxy.$modal.msgWarning("ä¸çº§èç¹ä¸è½ç¼è¾æå é¤"); |
| | | return; |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éåå·å¼¹æ¡ |
| | | const closeModelDia = () => { |
| | | proxy.$refs.modelFormRef.resetFields(); |
| | | modelDia.value = false; |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æ¥è¯¢å°ºå¯¸ |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getModelList(); |
| | | }; |
| | | const getModelList = () => { |
| | | tableLoading.value = true; |
| | | modelListPage({ |
| | | id: currentId.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | }).then((res) => { |
| | | console.log("res", res); |
| | | tableData.value = res.records; |
| | | page.total = res.total; |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // å é¤å°ºå¯¸ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delProductModel(ids) |
| | | .then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getModelList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | let ids = []; |
| | | ids.push(data.id); |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delProduct(ids) |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getProductTreeList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // éæ©äº§å |
| | | const handleNodeClick = (val, node, el) => { |
| | | // 夿æ¯å¦ä¸ºå¶åèç¹ |
| | | isShowButton.value = !(val.children && val.children.length > 0); |
| | | // åªæå¶åèç¹ææ§è¡ä»¥ä¸é»è¾ |
| | | currentId.value = val.id; |
| | | currentParentId.value = val.parentId; |
| | | getModelList(); |
| | | }; |
| | | |
| | | // æäº¤å°ºå¯¸ä¿®æ¹ |
| | | const submitModelForm = () => { |
| | | proxy.$refs.modelFormRef.validate(valid => { |
| | | if (valid) { |
| | | modelForm.value.productId = currentId.value; |
| | | addOrEditProductModel(modelForm.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeModelDia(); |
| | | getModelList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | // è°ç¨treeè¿æ»¤æ¹æ³ 䏿è±è¿æ»¤ |
| | | const filterNode = (value, data, node) => { |
| | | if (!value) { |
| | | //å¦ææ°æ®ä¸ºç©ºï¼åè¿åtrue,æ¾ç¤ºææçæ°æ®é¡¹ |
| | | return true; |
| | | } |
| | | // æ¥è¯¢å表æ¯å¦æå¹é
æ°æ®ï¼å°å¼å°åï¼å¹é
è±ææ°æ® |
| | | let val = value.toLowerCase(); |
| | | return chooseNode(val, data, node); // è°ç¨è¿æ»¤äºå±æ¹æ³ |
| | | }; |
| | | // è¿æ»¤ç¶èç¹ / åèç¹ (妿è¾å
¥çåæ°æ¯ç¶èç¹ä¸è½å¹é
ï¼åè¿å该èç¹ä»¥åå
¶ä¸çææåèç¹ï¼å¦æåæ°æ¯åèç¹ï¼åè¿å该èç¹çç¶èç¹ãnameæ¯ä¸æå符ï¼enNameæ¯è±æå符. |
| | | const chooseNode = (value, data, node) => { |
| | | if (data.label.indexOf(value) !== -1) { |
| | | return true; |
| | | } |
| | | const level = node.level; |
| | | // å¦æä¼ å
¥çèç¹æ¬èº«å°±æ¯ä¸çº§èç¹å°±ä¸ç¨æ ¡éªäº |
| | | if (level === 1) { |
| | | return false; |
| | | } |
| | | // å
åå½åèç¹çç¶èç¹ |
| | | let parentData = node.parent; |
| | | // éåå½åèç¹çç¶èç¹ |
| | | let index = 0; |
| | | while (index < level - 1) { |
| | | // 妿å¹é
å°ç´æ¥è¿åï¼æ¤å¤name弿¯ä¸æå符ï¼enNameæ¯è±æå符ã夿å¹é
ä¸è±æè¿æ»¤ |
| | | if (parentData.data.label.indexOf(value) !== -1) { |
| | | }; |
| | | // å
³éåå·å¼¹æ¡ |
| | | const closeModelDia = () => { |
| | | proxy.$refs.modelFormRef.resetFields(); |
| | | modelDia.value = false; |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æ¥è¯¢å°ºå¯¸ |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getModelList(); |
| | | }; |
| | | const getModelList = () => { |
| | | tableLoading.value = true; |
| | | modelListPage({ |
| | | id: currentId.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | }).then(res => { |
| | | console.log("res", res); |
| | | tableData.value = res.records; |
| | | page.total = res.total; |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // å é¤è§æ ¼åå· |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delProductModel(ids) |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getModelList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // è°ç¨treeè¿æ»¤æ¹æ³ 䏿è±è¿æ»¤ |
| | | const filterNode = (value, data, node) => { |
| | | if (!value) { |
| | | //å¦ææ°æ®ä¸ºç©ºï¼åè¿åtrue,æ¾ç¤ºææçæ°æ®é¡¹ |
| | | return true; |
| | | } |
| | | // å¦åçè¯åå¾ä¸ä¸å±åå¹é
|
| | | parentData = parentData.parent; |
| | | index++; |
| | | } |
| | | // 没å¹é
å°è¿åfalse |
| | | return false; |
| | | }; |
| | | getProductTreeList(); |
| | | // æ¥è¯¢å表æ¯å¦æå¹é
æ°æ®ï¼å°å¼å°åï¼å¹é
è±ææ°æ® |
| | | let val = value.toLowerCase(); |
| | | return chooseNode(val, data, node); // è°ç¨è¿æ»¤äºå±æ¹æ³ |
| | | }; |
| | | // è¿æ»¤ç¶èç¹ / åèç¹ (妿è¾å
¥çåæ°æ¯ç¶èç¹ä¸è½å¹é
ï¼åè¿å该èç¹ä»¥åå
¶ä¸çææåèç¹ï¼å¦æåæ°æ¯åèç¹ï¼åè¿å该èç¹çç¶èç¹ãnameæ¯ä¸æå符ï¼enNameæ¯è±æå符. |
| | | const chooseNode = (value, data, node) => { |
| | | if (data.label.indexOf(value) !== -1) { |
| | | return true; |
| | | } |
| | | const level = node.level; |
| | | // å¦æä¼ å
¥çèç¹æ¬èº«å°±æ¯ä¸çº§èç¹å°±ä¸ç¨æ ¡éªäº |
| | | if (level === 1) { |
| | | return false; |
| | | } |
| | | // å
åå½åèç¹çç¶èç¹ |
| | | let parentData = node.parent; |
| | | // éåå½åèç¹çç¶èç¹ |
| | | let index = 0; |
| | | while (index < level - 1) { |
| | | // 妿å¹é
å°ç´æ¥è¿åï¼æ¤å¤name弿¯ä¸æå符ï¼enNameæ¯è±æå符ã夿å¹é
ä¸è±æè¿æ»¤ |
| | | if (parentData.data.label.indexOf(value) !== -1) { |
| | | return true; |
| | | } |
| | | // å¦åçè¯åå¾ä¸ä¸å±åå¹é
|
| | | parentData = parentData.parent; |
| | | index++; |
| | | } |
| | | // 没å¹é
å°è¿åfalse |
| | | return false; |
| | | }; |
| | | getProductTreeList(); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .product-view { |
| | | display: flex; |
| | | } |
| | | .left { |
| | | width: 450px; |
| | | min-width: 450px; |
| | | padding: 16px; |
| | | background: #ffffff; |
| | | } |
| | | .right { |
| | | flex: 1; |
| | | min-width: 0; |
| | | padding: 16px; |
| | | margin-left: 20px; |
| | | background: #ffffff; |
| | | } |
| | | .custom-tree-node { |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | font-size: 14px; |
| | | padding-right: 8px; |
| | | } |
| | | .tree-node-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | } |
| | | .tree-node-content .orange-icon { |
| | | flex-shrink: 0; |
| | | } |
| | | .tree-node-label { |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | .orange-icon { |
| | | color: orange; |
| | | font-size: 18px; |
| | | margin-right: 8px; /* 徿 䏿åä¹é´å ç¹é´è· */ |
| | | } |
| | | .product-tree-scroll { |
| | | scrollbar-width: thin; |
| | | scrollbar-color: #c0c4cc #f5f7fa; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar { |
| | | width: 8px; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar-track { |
| | | background: #f5f7fa; |
| | | border-radius: 4px; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar-thumb { |
| | | background: #c0c4cc; |
| | | border-radius: 4px; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar-thumb:hover { |
| | | background: #909399; |
| | | } |
| | | .product-view { |
| | | display: flex; |
| | | } |
| | | .left { |
| | | width: 450px; |
| | | min-width: 450px; |
| | | padding: 16px; |
| | | background: #ffffff; |
| | | } |
| | | .right { |
| | | flex: 1; |
| | | min-width: 0; |
| | | padding: 16px; |
| | | margin-left: 20px; |
| | | background: #ffffff; |
| | | } |
| | | .custom-tree-node { |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | font-size: 14px; |
| | | padding-right: 8px; |
| | | } |
| | | .tree-node-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | } |
| | | .tree-node-content .orange-icon { |
| | | flex-shrink: 0; |
| | | } |
| | | .tree-node-label { |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | .orange-icon { |
| | | color: orange; |
| | | font-size: 18px; |
| | | margin-right: 8px; /* 徿 䏿åä¹é´å ç¹é´è· */ |
| | | } |
| | | .product-tree-scroll { |
| | | scrollbar-width: thin; |
| | | scrollbar-color: #c0c4cc #f5f7fa; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar { |
| | | width: 8px; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar-track { |
| | | background: #f5f7fa; |
| | | border-radius: 4px; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar-thumb { |
| | | background: #c0c4cc; |
| | | border-radius: 4px; |
| | | } |
| | | .product-tree-scroll::-webkit-scrollbar-thumb:hover { |
| | | background: #909399; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div> |
| | | <div class="search_form"> |
| | | <div> |
| | | <div style="margin-bottom: 10px;"> |
| | | <span class="search_title">ä¾åºåæ¡£æ¡ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.supplierName" |
| | |
| | | >æç´¢</el-button |
| | | > |
| | | </div> |
| | | <div> |
| | | <div style="margin-bottom: 10px;"> |
| | | <el-button type="primary" @click="openForm('add')" |
| | | >æ°å¢ä¾åºå</el-button |
| | | > |
| | |
| | | } |
| | | // ä¸è½½éä»¶ |
| | | const downLoadFile = (row) => { |
| | | proxy.$download.name(row.url); |
| | | proxy.$download.byUrl(row.url, row.originalFilename); |
| | | } |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | <div class="header-actions"> |
| | | <div class="header-actions" v-if="approverList.length > 0"> |
| | | <el-button @click="handleReset" size="default"> |
| | | <el-icon><RefreshLeft /></el-icon> |
| | | éç½® |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'approval' ? '审æ¹' : '详æ
'" |
| | | width="700px" |
| | | @close="closeDia" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" ref="formRef"> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="æµç¨ç¼å·ï¼" prop="approveId"> |
| | | <el-input v-model="form.approveId" placeholder="èªå¨ç¼å·" clearable disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="ç³è¯·é¨é¨ï¼"> |
| | | <el-select |
| | | disabled |
| | | v-model="form.approveDeptId" |
| | | placeholder="éæ©é¨é¨" |
| | | > |
| | | <el-option |
| | | v-for="user in productOptions" |
| | | :key="user.deptId" |
| | | :label="user.deptName" |
| | | :value="user.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-if="!isQuotationApproval && !isPurchaseApproval"> |
| | | <el-col :span="24"> |
| | | <el-form-item :label="props.approveType == 5 ? 'éè´ååå·ï¼' : '审æ¹äºç±ï¼'" prop="approveReason"> |
| | | <el-input v-model="form.approveReason" placeholder="请è¾å
¥" clearable type="textarea" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'approval' ? '审æ¹' : '详æ
'" |
| | | width="700px" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | ref="formRef"> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="æµç¨ç¼å·ï¼" |
| | | prop="approveId"> |
| | | <el-input v-model="form.approveId" |
| | | placeholder="èªå¨ç¼å·" |
| | | clearable |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="ç³è¯·é¨é¨ï¼"> |
| | | <el-select disabled |
| | | v-model="form.approveDeptId" |
| | | placeholder="éæ©é¨é¨"> |
| | | <el-option v-for="user in productOptions" |
| | | :key="user.deptId" |
| | | :label="user.deptName" |
| | | :value="user.deptId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-if="!isQuotationApproval && !isPurchaseApproval"> |
| | | <el-col :span="24"> |
| | | <el-form-item :label="props.approveType == 5 ? 'éè´ååå·ï¼' : '审æ¹äºç±ï¼'" |
| | | prop="approveReason"> |
| | | <el-input v-model="form.approveReason" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | type="textarea" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <!-- æ¥ä»·å®¡æ¹ï¼å±ç¤ºæ¥ä»·è¯¦æ
ï¼å¤ç¨é宿¥ä»·"æ¥ç详æ
å¯¹è¯æ¡"å
å®¹ç»æï¼ --> |
| | | <div v-if="isQuotationApproval" style="margin: 10px 0 18px;"> |
| | | <div v-if="isQuotationApproval" |
| | | style="margin: 10px 0 18px;"> |
| | | <el-divider content-position="left">æ¥ä»·è¯¦æ
</el-divider> |
| | | <el-skeleton :loading="quotationLoading" animated> |
| | | <el-skeleton :loading="quotationLoading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="h3" style="width: 30%" /> |
| | | <el-skeleton-item variant="text" style="width: 100%" /> |
| | | <el-skeleton-item variant="text" style="width: 100%" /> |
| | | <el-skeleton-item variant="h3" |
| | | style="width: 30%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <template #default> |
| | | <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo" description="æªæ¥è¯¢å°å¯¹åºæ¥ä»·è¯¦æ
" /> |
| | | <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo" |
| | | description="æªæ¥è¯¢å°å¯¹åºæ¥ä»·è¯¦æ
" /> |
| | | <template v-else> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="æ¥ä»·åå·">{{ currentQuotation.quotationNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ currentQuotation.customer }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¸å¡å">{{ currentQuotation.salesperson }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·æ¥æ">{{ currentQuotation.quotationDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="æææè³">{{ currentQuotation.validDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ currentQuotation.paymentMethod }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·æ»é¢" :span="2"> |
| | | <el-descriptions-item label="æ¥ä»·æ»é¢" |
| | | :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div style="margin-top: 20px;"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="currentQuotation.products || []" border style="width: 100%"> |
| | | <el-table-column prop="product" label="产ååç§°" /> |
| | | <el-table-column prop="specification" label="尺寸" /> |
| | | <el-table-column prop="unit" label="åä½" /> |
| | | <el-table-column prop="unitPrice" label="åä»·"> |
| | | <el-table :data="currentQuotation.products || []" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="product" |
| | | label="产ååç§°" /> |
| | | <el-table-column prop="specification" |
| | | label="è§æ ¼åå·" /> |
| | | <el-table-column prop="unit" |
| | | label="åä½" /> |
| | | <el-table-column prop="unitPrice" |
| | | label="åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <div v-if="currentQuotation.remark" style="margin-top: 20px;"> |
| | | <div v-if="currentQuotation.remark" |
| | | style="margin-top: 20px;"> |
| | | <h4>夿³¨</h4> |
| | | <p>{{ currentQuotation.remark }}</p> |
| | | </div> |
| | |
| | | </template> |
| | | </el-skeleton> |
| | | </div> |
| | | |
| | | <!-- éè´å®¡æ¹ï¼å±ç¤ºéè´è¯¦æ
--> |
| | | <div v-if="isPurchaseApproval" style="margin: 10px 0 18px;"> |
| | | <div v-if="isPurchaseApproval" |
| | | style="margin: 10px 0 18px;"> |
| | | <el-divider content-position="left">éè´è¯¦æ
</el-divider> |
| | | <el-skeleton :loading="purchaseLoading" animated> |
| | | <el-skeleton :loading="purchaseLoading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="h3" style="width: 30%" /> |
| | | <el-skeleton-item variant="text" style="width: 100%" /> |
| | | <el-skeleton-item variant="text" style="width: 100%" /> |
| | | <el-skeleton-item variant="h3" |
| | | style="width: 30%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <template #default> |
| | | <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber" description="æªæ¥è¯¢å°å¯¹åºéè´è¯¦æ
" /> |
| | | <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber" |
| | | description="æªæ¥è¯¢å°å¯¹åºéè´è¯¦æ
" /> |
| | | <template v-else> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="éè´ååå·">{{ currentPurchase.purchaseContractNumber }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¾åºååç§°">{{ currentPurchase.supplierName }}</el-descriptions-item> |
| | | <el-descriptions-item label="项ç®åç§°">{{ currentPurchase.projectName }}</el-descriptions-item> |
| | |
| | | <el-descriptions-item label="ç¾è®¢æ¥æ">{{ currentPurchase.executionDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="å½å
¥æ¥æ">{{ currentPurchase.entryDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ currentPurchase.paymentMethod }}</el-descriptions-item> |
| | | <el-descriptions-item label="ååéé¢" :span="2"> |
| | | <el-descriptions-item label="ååéé¢" |
| | | :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div style="margin-top: 20px;"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="currentPurchase.productData || []" border style="width: 100%"> |
| | | <el-table-column prop="productCategory" label="产ååç§°" /> |
| | | <el-table-column prop="specificationModel" label="尺寸" /> |
| | | <el-table-column prop="unit" label="åä½" /> |
| | | <el-table-column prop="quantity" label="æ°é" /> |
| | | <el-table-column prop="taxInclusiveUnitPrice" label="å«ç¨åä»·"> |
| | | <el-table :data="currentPurchase.productData || []" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="productCategory" |
| | | label="产ååç§°" /> |
| | | <el-table-column prop="specificationModel" |
| | | label="è§æ ¼åå·" /> |
| | | <el-table-column prop="unit" |
| | | label="åä½" /> |
| | | <el-table-column prop="quantity" |
| | | label="æ°é" /> |
| | | <el-table-column prop="taxInclusiveUnitPrice" |
| | | label="å«ç¨åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="taxInclusiveTotalPrice" label="å«ç¨æ»ä»·"> |
| | | <el-table-column prop="taxInclusiveTotalPrice" |
| | | label="å«ç¨æ»ä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | </template> |
| | | </el-skeleton> |
| | | </div> |
| | | |
| | | <el-form :model="{ activities }" ref="formRef" label-position="top"> |
| | | <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical"> |
| | | <el-step |
| | | v-for="(activity, index) in activities" |
| | | :key="index" |
| | | finish-status="success" |
| | | :title="getNodeTitle(index, activities.length)" |
| | | :description="activity.approveNodeUser" |
| | | :icon="getNodeIcon(activity, index)" |
| | | > |
| | | <template #icon> |
| | | <el-icon v-if="activity.approveNodeStatus === 2" color="red" :size="22"><WarningFilled /></el-icon> |
| | | <el-icon v-else-if="activity.isShen" color="#1890ff" :size="22"><Edit /></el-icon> |
| | | <el-icon v-else-if="activity.approveNodeStatus === 1" color="#67C23A" :size="26"><Check /></el-icon> |
| | | <el-icon v-else color="#C0C4CC" :size="22"><MoreFilled /></el-icon> |
| | | </template> |
| | | <!-- å货审æ¹ï¼å±ç¤ºå货详æ
--> |
| | | <div v-if="isDeliveryApproval" |
| | | style="margin: 10px 0 18px;"> |
| | | <el-divider content-position="left">å货详æ
</el-divider> |
| | | <el-skeleton :loading="deliveryLoading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="h3" |
| | | style="width: 30%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <template #default> |
| | | <el-empty v-if="!currentDelivery || !currentDelivery.shippingInfo" |
| | | description="æªæ¥è¯¢å°å¯¹åºå货详æ
" /> |
| | | <template v-else> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="éå®è®¢å">{{ currentDelivery.shippingInfo.salesContractNo || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å货订åå·">{{ currentDelivery.shippingInfo.shippingNo || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ currentDelivery.shippingInfo.customerName || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§ç±»å">{{ currentDelivery.shippingInfo.type || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§æ¥æ">{{ currentDelivery.shippingInfo.shippingDate || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®¡æ ¸ç¶æ">{{ currentDelivery.shippingInfo.status || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å货车çå·">{{ currentDelivery.shippingInfo.shippingCarNumber || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éå
¬å¸">{{ currentDelivery.shippingInfo.expressCompany || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éåå·" |
| | | :span="2">{{ currentDelivery.shippingInfo.expressNumber || '--' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <div style="margin-top: 20px;"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="deliveryProductList" |
| | | border |
| | | size="small" |
| | | style="width: 100%"> |
| | | <el-table-column prop="batchNo" |
| | | label="æ¹å·" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="productName" |
| | | label="产ååç§°" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="specificationModel" |
| | | label="è§æ ¼åå·" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="deliveryQuantity" |
| | | label="åè´§æ°é" |
| | | align="center" /> |
| | | </el-table> |
| | | </div> |
| | | <div v-if="currentDelivery.shippingInfo.storageBlobVOs && currentDelivery.shippingInfo.storageBlobVOs.length" |
| | | style="margin-top: 20px;"> |
| | | <h4>åè´§å¾ç</h4> |
| | | <ImagePreview :file-list="currentDelivery.shippingInfo.storageBlobVOs" /> |
| | | </div> |
| | | </template> |
| | | </template> |
| | | </el-skeleton> |
| | | </div> |
| | | <el-form :model="{ activities }" |
| | | ref="formRef" |
| | | label-position="top"> |
| | | <el-steps :active="getActiveStep()" |
| | | finish-status="success" |
| | | process-status="process" |
| | | align-center |
| | | direction="vertical"> |
| | | <el-step v-for="(activity, index) in activities" |
| | | :key="index" |
| | | finish-status="success" |
| | | :title="getNodeTitle(index, activities.length)" |
| | | :description="activity.approveNodeUser" |
| | | :icon="getNodeIcon(activity, index)"> |
| | | <template #icon> |
| | | <el-icon v-if="activity.approveNodeStatus === 2" |
| | | color="red" |
| | | :size="22"> |
| | | <WarningFilled /> |
| | | </el-icon> |
| | | <el-icon v-else-if="activity.isShen" |
| | | color="#1890ff" |
| | | :size="22"> |
| | | <Edit /> |
| | | </el-icon> |
| | | <el-icon v-else-if="activity.approveNodeStatus === 1" |
| | | color="#67C23A" |
| | | :size="26"> |
| | | <Check /> |
| | | </el-icon> |
| | | <el-icon v-else |
| | | color="#C0C4CC" |
| | | :size="22"> |
| | | <MoreFilled /> |
| | | </el-icon> |
| | | </template> |
| | | <template #title> |
| | | <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span> |
| | | </template> |
| | | <template #description> |
| | | <div class="node-user"> |
| | | <div class="avatar-wrapper"> |
| | | <img :src="userStore.avatar" class="user-avatar" alt=""/> |
| | | <img :src="userStore.avatar" |
| | | class="user-avatar" |
| | | alt="" /> |
| | | </div> |
| | | <span style="color: #000000">{{ activity.approveNodeUser }}-{{activity.isApproval}}</span> |
| | | </div> |
| | | <div v-if="!activity.isShen" class="node-reason"> |
| | | <div v-if="!activity.isShen" |
| | | class="node-reason"> |
| | | <span>å®¡æ¹æè§ï¼</span>{{ activity.approveNodeReason }} |
| | | </div> |
| | | <div v-else-if="activity.isShen"> |
| | | <el-form-item |
| | | :prop="'activities.' + index + '.approveNodeReason'" |
| | | :rules="[{ required: true, message: 'å®¡æ¹æè§ä¸è½ä¸ºç©º', trigger: 'blur' }]" |
| | | > |
| | | <el-input v-model="activity.approveNodeReason" clearable type="textarea" :disabled="operationType === 'view'"></el-input> |
| | | <el-form-item :prop="'activities.' + index + '.approveNodeReason'" |
| | | :rules="[{ required: true, message: 'å®¡æ¹æè§ä¸è½ä¸ºç©º', trigger: 'blur' }]"> |
| | | <el-input v-model="activity.approveNodeReason" |
| | | clearable |
| | | type="textarea" |
| | | :disabled="operationType === 'view'"></el-input> |
| | | </el-form-item> |
| | | </div> |
| | | </template> |
| | | </el-step> |
| | | </el-steps> |
| | | </el-form> |
| | | <template #footer v-if="operationType === 'approval'"> |
| | | <template #footer |
| | | v-if="operationType === 'approval'"> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm(2)">ä¸éè¿</el-button> |
| | | <el-button type="primary" @click="submitForm(1)">éè¿</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm(2)">ä¸éè¿</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm(1)">éè¿</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, getCurrentInstance, nextTick, reactive, ref, toRefs } from "vue"; |
| | | import { |
| | | approveProcessDetails, |
| | | getDept, |
| | | updateApproveNode |
| | | } from "@/api/collaborativeApproval/approvalProcess.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue' |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js"; |
| | | const emit = defineEmits(['close']) |
| | | const { proxy } = getCurrentInstance() |
| | | import { |
| | | computed, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | reactive, |
| | | ref, |
| | | toRefs, |
| | | } from "vue"; |
| | | import { |
| | | approveProcessDetails, |
| | | getDept, |
| | | updateApproveNode, |
| | | } from "@/api/collaborativeApproval/approvalProcess.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { |
| | | WarningFilled, |
| | | Edit, |
| | | Check, |
| | | MoreFilled, |
| | | } from "@element-plus/icons-vue"; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { getDeliveryDetailByShippingNo } from "@/api/salesManagement/deliveryLedger.js"; |
| | | import ImagePreview from "@/components/AttachmentPreview/image/index.vue"; |
| | | const emit = defineEmits(["close"]); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const props = defineProps({ |
| | | approveType: { |
| | | type: [Number, String], |
| | | default: 0 |
| | | } |
| | | }) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const activities = ref([]) |
| | | const formRef = ref(null); |
| | | const userStore = useUserStore() |
| | | const productOptions = ref([]); |
| | | const quotationLoading = ref(false) |
| | | const currentQuotation = ref({}) |
| | | const purchaseLoading = ref(false) |
| | | const currentPurchase = ref({}) |
| | | const isQuotationApproval = computed(() => Number(props.approveType) === 6) |
| | | const isPurchaseApproval = computed(() => Number(props.approveType) === 5) |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | approveId: "", |
| | | approveDeptId: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | }, |
| | | }); |
| | | const { form } = toRefs(data); |
| | | |
| | | // èç¹æ é¢ |
| | | const getNodeTitle = (index, len) => { |
| | | if (index === len - 1) return 'ç»æ'; |
| | | return '审æ¹'; |
| | | }; |
| | | |
| | | // è·åå½åæ¿æ´»æ¥éª¤ |
| | | const getActiveStep = () => { |
| | | // å¦æææ isShen é½ä¸º falseï¼è¿åæåä¸ä¸ªæ¥éª¤ï¼å
¨é¨å®æï¼ |
| | | const hasActive = activities.value.some(a => a.isShen === true); |
| | | if (!hasActive) return activities.value.length; |
| | | // å½åèç¹ç´¢å¼ |
| | | return activities.value.findIndex(a => a.isShen == true); |
| | | }; |
| | | // æ¥éª¤icon |
| | | const getNodeIcon = (activity, index) => { |
| | | if (activity.approveNodeStatus === 2) return 'el-icon-warning'; // ä¸éè¿ |
| | | if (activity.isShen) return 'Edit'; |
| | | return ''; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | currentQuotation.value = {} |
| | | currentPurchase.value = {} |
| | | form.value = {...row} |
| | | // ç«å³æ¸
é¤è¡¨åéªè¯ç¶æï¼å ä¸ºåæ®µæ¯disabledçï¼ä¸éè¦éªè¯ï¼ |
| | | nextTick(() => { |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | }); |
| | | // ç¡®ä¿é项å è½½å®æååå¹é
å¼ç±»å |
| | | getProductOptions().then(() => { |
| | | // ç¡®ä¿å¼ç±»åå¹é
ï¼å¦æé项已å è½½ï¼ |
| | | if (productOptions.value.length > 0 && form.value.approveDeptId) { |
| | | const matchedOption = productOptions.value.find(opt => |
| | | opt.deptId == form.value.approveDeptId || |
| | | String(opt.deptId) === String(form.value.approveDeptId) |
| | | ); |
| | | if (matchedOption) { |
| | | form.value.approveDeptId = matchedOption.deptId; |
| | | } |
| | | } |
| | | // 忬¡æ¸
é¤éªè¯ï¼ç¡®ä¿é项å è½½åå¼å¹é
æ£ç¡® |
| | | nextTick(() => { |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | // æ¥ä»·å®¡æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"æ¥ä»·åå·"廿¥æ¥ä»·å表 |
| | | if (isQuotationApproval.value) { |
| | | const quotationNo = row?.approveReason; |
| | | if (quotationNo) { |
| | | quotationLoading.value = true |
| | | getQuotationList({ quotationNo }).then((res) => { |
| | | const records = res?.data?.records || [] |
| | | currentQuotation.value = records[0] || {} |
| | | }).finally(() => { |
| | | quotationLoading.value = false |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // éè´å®¡æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"éè´ååå·"廿¥éè´è¯¦æ
|
| | | if (isPurchaseApproval.value) { |
| | | const purchaseContractNumber = row?.approveReason; |
| | | if (purchaseContractNumber) { |
| | | purchaseLoading.value = true |
| | | getPurchaseByCode({ purchaseContractNumber }).then((res) => { |
| | | currentPurchase.value = res |
| | | }).catch((err) => { |
| | | console.error('æ¥è¯¢éè´è¯¦æ
失败:', err) |
| | | proxy.$modal.msgError('æ¥è¯¢éè´è¯¦æ
失败') |
| | | }).finally(() => { |
| | | purchaseLoading.value = false |
| | | }) |
| | | } |
| | | } |
| | | |
| | | approveProcessDetails(row.approveId).then((res) => { |
| | | activities.value = res.data |
| | | // å¢å isApprovalåæ®µ |
| | | activities.value.forEach(item => { |
| | | if (item.url && item.url.includes('word')) { |
| | | item.urlTem = item.url.replaceAll('word', 'img') |
| | | } else { |
| | | item.urlTem = item.url |
| | | } |
| | | if (item.approveNodeStatus === 2) { |
| | | item.isApproval = '已驳å'; |
| | | } else if (item.approveNodeStatus === 1) { |
| | | item.isApproval = 'å·²åæ'; |
| | | } else { |
| | | item.isApproval = 'æªå®¡æ¹'; |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | const getProductOptions = () => { |
| | | return getDept().then((res) => { |
| | | productOptions.value = res.data; |
| | | }); |
| | | }; |
| | | // æäº¤å®¡æ¹ |
| | | const submitForm = (status) => { |
| | | const filteredActivities = activities.value.filter(activity => activity.isShen); |
| | | if (!filteredActivities || filteredActivities.length === 0) { |
| | | proxy.$modal.msgError("æªæ¾å°å¾
审æ¹çèç¹"); |
| | | return; |
| | | } |
| | | const currentActivity = filteredActivities[0]; |
| | | if (!currentActivity) { |
| | | proxy.$modal.msgError("æªæ¾å°å¾
审æ¹çèç¹"); |
| | | return; |
| | | } |
| | | currentActivity.approveNodeStatus = status; |
| | | // 夿æ¯å¦ä¸ºæå䏿¥ |
| | | const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1; |
| | | updateApproveNode({ ...currentActivity, isLast }).then(() => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | const props = defineProps({ |
| | | approveType: { |
| | | type: [Number, String], |
| | | default: 0, |
| | | }, |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | quotationLoading.value = false |
| | | currentQuotation.value = {} |
| | | purchaseLoading.value = false |
| | | currentPurchase.value = {} |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const activities = ref([]); |
| | | const formRef = ref(null); |
| | | const userStore = useUserStore(); |
| | | const productOptions = ref([]); |
| | | const quotationLoading = ref(false); |
| | | const currentQuotation = ref({}); |
| | | const purchaseLoading = ref(false); |
| | | const currentPurchase = ref({}); |
| | | const deliveryLoading = ref(false); |
| | | const currentDelivery = ref({}); |
| | | const deliveryProductList = ref([]); |
| | | const isQuotationApproval = computed(() => Number(props.approveType) === 6); |
| | | const isPurchaseApproval = computed(() => Number(props.approveType) === 5); |
| | | const isDeliveryApproval = computed(() => Number(props.approveType) === 7); |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | approveId: "", |
| | | approveDeptId: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | }, |
| | | }); |
| | | const { form } = toRefs(data); |
| | | |
| | | // èç¹æ é¢ |
| | | const getNodeTitle = (index, len) => { |
| | | if (index === len - 1) return "ç»æ"; |
| | | return "审æ¹"; |
| | | }; |
| | | |
| | | // è·åå½åæ¿æ´»æ¥éª¤ |
| | | const getActiveStep = () => { |
| | | // å¦æææ isShen é½ä¸º falseï¼è¿åæåä¸ä¸ªæ¥éª¤ï¼å
¨é¨å®æï¼ |
| | | const hasActive = activities.value.some(a => a.isShen === true); |
| | | if (!hasActive) return activities.value.length; |
| | | // å½åèç¹ç´¢å¼ |
| | | return activities.value.findIndex(a => a.isShen == true); |
| | | }; |
| | | // æ¥éª¤icon |
| | | const getNodeIcon = (activity, index) => { |
| | | if (activity.approveNodeStatus === 2) return "el-icon-warning"; // ä¸éè¿ |
| | | if (activity.isShen) return "Edit"; |
| | | return ""; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | currentQuotation.value = {}; |
| | | currentPurchase.value = {}; |
| | | form.value = { ...row }; |
| | | // ç«å³æ¸
é¤è¡¨åéªè¯ç¶æï¼å ä¸ºåæ®µæ¯disabledçï¼ä¸éè¦éªè¯ï¼ |
| | | nextTick(() => { |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | }); |
| | | // ç¡®ä¿é项å è½½å®æååå¹é
å¼ç±»å |
| | | getProductOptions().then(() => { |
| | | // ç¡®ä¿å¼ç±»åå¹é
ï¼å¦æé项已å è½½ï¼ |
| | | if (productOptions.value.length > 0 && form.value.approveDeptId) { |
| | | const matchedOption = productOptions.value.find( |
| | | opt => |
| | | opt.deptId == form.value.approveDeptId || |
| | | String(opt.deptId) === String(form.value.approveDeptId) |
| | | ); |
| | | if (matchedOption) { |
| | | form.value.approveDeptId = matchedOption.deptId; |
| | | } |
| | | } |
| | | // 忬¡æ¸
é¤éªè¯ï¼ç¡®ä¿é项å è½½åå¼å¹é
æ£ç¡® |
| | | nextTick(() => { |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | // æ¥ä»·å®¡æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"æ¥ä»·åå·"廿¥æ¥ä»·å表 |
| | | if (isQuotationApproval.value) { |
| | | const quotationNo = row?.approveReason; |
| | | if (quotationNo) { |
| | | quotationLoading.value = true; |
| | | getQuotationList({ quotationNo }) |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | | currentQuotation.value = records[0] || {}; |
| | | }) |
| | | .finally(() => { |
| | | quotationLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // éè´å®¡æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"éè´ååå·"廿¥éè´è¯¦æ
|
| | | if (isPurchaseApproval.value) { |
| | | const purchaseContractNumber = row?.approveReason; |
| | | if (purchaseContractNumber) { |
| | | purchaseLoading.value = true; |
| | | getPurchaseByCode({ purchaseContractNumber }) |
| | | .then(res => { |
| | | currentPurchase.value = res; |
| | | }) |
| | | .catch(err => { |
| | | console.error("æ¥è¯¢éè´è¯¦æ
失败:", err); |
| | | proxy.$modal.msgError("æ¥è¯¢éè´è¯¦æ
失败"); |
| | | }) |
| | | .finally(() => { |
| | | purchaseLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | // å货审æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"åè´§åå·"廿¥å货详æ
|
| | | if (isDeliveryApproval.value) { |
| | | const deliveryNo = row?.approveReason; |
| | | if (deliveryNo) { |
| | | deliveryLoading.value = true; |
| | | currentDelivery.value = {}; |
| | | deliveryProductList.value = []; |
| | | getDeliveryDetailByShippingNo({ shippingNo: deliveryNo }) |
| | | .then(res => { |
| | | const detailData = res?.data || res || {}; |
| | | currentDelivery.value = detailData; |
| | | deliveryProductList.value = |
| | | detailData.shippingProductDetailDtoList || []; |
| | | }) |
| | | .catch(err => { |
| | | console.error("æ¥è¯¢å货详æ
失败:", err); |
| | | proxy.$modal.msgError("æ¥è¯¢å货详æ
失败"); |
| | | }) |
| | | .finally(() => { |
| | | deliveryLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | approveProcessDetails(row.approveId).then(res => { |
| | | activities.value = res.data; |
| | | // å¢å isApprovalåæ®µ |
| | | activities.value.forEach(item => { |
| | | if (item.url && item.url.includes("word")) { |
| | | item.urlTem = item.url.replaceAll("word", "img"); |
| | | } else { |
| | | item.urlTem = item.url; |
| | | } |
| | | if (item.approveNodeStatus === 2) { |
| | | item.isApproval = "已驳å"; |
| | | } else if (item.approveNodeStatus === 1) { |
| | | item.isApproval = "å·²åæ"; |
| | | } else { |
| | | item.isApproval = "æªå®¡æ¹"; |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const getDeliveryProductInfoList = () => { |
| | | const row = currentDelivery.value; |
| | | if (!row) return []; |
| | | const normalizeBatchNoList = value => { |
| | | if (Array.isArray(value)) return value; |
| | | if (typeof value === "string" && value.includes(",")) { |
| | | return value |
| | | .split(",") |
| | | .map(item => item.trim()) |
| | | .filter(Boolean); |
| | | } |
| | | return value ? [value] : []; |
| | | }; |
| | | const detailList = deliveryProductList.value.length |
| | | ? deliveryProductList.value |
| | | : [ |
| | | row.batchNoDetailList, |
| | | row.batchNoList, |
| | | row.shippingBatchList, |
| | | row.shippingInfoDetailList, |
| | | row.detailList, |
| | | row.batchDetailList, |
| | | ].find(value => Array.isArray(value) && value.length); |
| | | const batchNoList = normalizeBatchNoList(row.batchNo); |
| | | const toTableRow = (item = {}) => ({ |
| | | batchNo: |
| | | typeof item === "string" || typeof item === "number" |
| | | ? item |
| | | : item.batchNo ?? item.batchNumber ?? row.batchNo ?? "--", |
| | | productName: item.productName ?? row.productName ?? "--", |
| | | specificationModel: |
| | | item.specificationModel ?? item.model ?? row.specificationModel ?? "--", |
| | | deliveryQuantity: |
| | | item.deliveryQuantity ?? |
| | | item.quantity ?? |
| | | item.shippingQuantity ?? |
| | | row.deliveryQuantity ?? |
| | | row.quantity ?? |
| | | "--", |
| | | }); |
| | | if (detailList?.length) { |
| | | return detailList.map(toTableRow); |
| | | } |
| | | if (batchNoList.length) { |
| | | return batchNoList.map(batchNo => toTableRow({ batchNo })); |
| | | } |
| | | return [toTableRow()]; |
| | | }; |
| | | const getApprovalStatusText = status => { |
| | | const statusMap = { |
| | | 0: "å¾
å®¡æ ¸", |
| | | 1: "å®¡æ ¸éè¿", |
| | | 2: "å®¡æ ¸æç»", |
| | | 3: "å®¡æ ¸ä¸", |
| | | }; |
| | | return statusMap[status] || "å¾
å®¡æ ¸"; |
| | | }; |
| | | |
| | | const getProductOptions = () => { |
| | | return getDept().then(res => { |
| | | productOptions.value = res.data; |
| | | }); |
| | | }; |
| | | // æäº¤å®¡æ¹ |
| | | const submitForm = status => { |
| | | const filteredActivities = activities.value.filter( |
| | | activity => activity.isShen |
| | | ); |
| | | if (!filteredActivities || filteredActivities.length === 0) { |
| | | proxy.$modal.msgError("æªæ¾å°å¾
审æ¹çèç¹"); |
| | | return; |
| | | } |
| | | const currentActivity = filteredActivities[0]; |
| | | if (!currentActivity) { |
| | | proxy.$modal.msgError("æªæ¾å°å¾
审æ¹çèç¹"); |
| | | return; |
| | | } |
| | | currentActivity.approveNodeStatus = status; |
| | | // 夿æ¯å¦ä¸ºæå䏿¥ |
| | | const isLast = |
| | | activities.value.findIndex(a => a.isShen) === activities.value.length - 1; |
| | | updateApproveNode({ ...currentActivity, isLast }).then(() => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | quotationLoading.value = false; |
| | | currentQuotation.value = {}; |
| | | purchaseLoading.value = false; |
| | | currentPurchase.value = {}; |
| | | emit("close"); |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | .node-user { |
| | | margin: 10px 0; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .node-status { |
| | | color: #1890ff; |
| | | margin-left: 8px; |
| | | font-size: 14px; |
| | | } |
| | | .node-reason { |
| | | font-size: 15px; |
| | | color: #333; |
| | | margin: 10px 0; |
| | | } |
| | | .user-avatar { |
| | | cursor: pointer; |
| | | width: 30px; |
| | | height: 30px; |
| | | border-radius: 50px; |
| | | } |
| | | .signImg { |
| | | cursor: pointer; |
| | | width: 200px; |
| | | height: 60px; |
| | | } |
| | | .node-user { |
| | | margin: 10px 0; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .node-status { |
| | | color: #1890ff; |
| | | margin-left: 8px; |
| | | font-size: 14px; |
| | | } |
| | | .node-reason { |
| | | font-size: 15px; |
| | | color: #333; |
| | | margin: 10px 0; |
| | | } |
| | | .user-avatar { |
| | | cursor: pointer; |
| | | width: 30px; |
| | | height: 30px; |
| | | border-radius: 50px; |
| | | } |
| | | .signImg { |
| | | cursor: pointer; |
| | | width: 200px; |
| | | height: 60px; |
| | | } |
| | | </style> |
| | |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶ææï¼" prop="remark"> |
| | | <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload |
| | | :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError" |
| | | :on-success="handleUploadSuccess" :on-remove="handleRemove"> |
| | | <el-button type="primary" v-if="operationType !== 'view'">ä¸ä¼ </el-button> |
| | | <template #tip v-if="operationType !== 'view'"> |
| | | <div class="el-upload__tip"> |
| | | æä»¶æ ¼å¼æ¯æ |
| | | docï¼docxï¼xlsï¼xlsxï¼pptï¼pptxï¼pdfï¼txtï¼xmlï¼jpgï¼jpegï¼pngï¼gifï¼bmpï¼rarï¼zipï¼7z |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <FileUpload v-model:file-list="fileList" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | import useUserStore from "@/store/modules/user"; |
| | | import FileUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | const userStore = useUserStore(); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | |
| | | approveDeptName: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | tempFileIds: [], |
| | | startDate: "", // 请åå¼å§æ¶é´ |
| | | endDate: "", // 请åç»ææ¶é´ |
| | | price: null, // æ¥ééé¢ |
| | |
| | | currentApproveStatus.value = row.approveStatus |
| | | approveProcessGetInfo({id: row.approveId,approveReason: '1'}).then(res => { |
| | | form.value = {...res.data} |
| | | fileList.value = res.data.storageBlobVOS |
| | | }) |
| | | } |
| | | } |
| | |
| | | productOptions.value = res.data; |
| | | // 妿已æé¨é¨IDï¼èªå¨è®¾ç½®é¨é¨åç§°ï¼ç¨äºéªè¯ï¼ |
| | | if (form.value.approveDeptId && productOptions.value.length > 0) { |
| | | const matchedDept = productOptions.value.find(dept => |
| | | dept.deptId == form.value.approveDeptId || |
| | | const matchedDept = productOptions.value.find(dept => |
| | | dept.deptId == form.value.approveDeptId || |
| | | String(dept.deptId) === String(form.value.approveDeptId) |
| | | ); |
| | | if (matchedDept) { |
| | |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | |
| | | return |
| | | } |
| | | } |
| | | form.value.storageBlobDTOList = fileList.value |
| | | |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add" || currentApproveStatus.value == 3) { |
| | |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | |
| | | // ä¸ä¼ åæ ¡æ£ |
| | | function handleBeforeUpload(file) { |
| | | // æ ¡æ£æä»¶å¤§å° |
| | | // if (file.size > 1024 * 1024 * 10) { |
| | | // proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿10MB!"); |
| | | // return false; |
| | | // } |
| | | proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); |
| | | return true; |
| | | } |
| | | // ä¸ä¼ 失败 |
| | | function handleUploadError(err) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | // ä¸ä¼ æååè° |
| | | function handleUploadSuccess(res, file, uploadFiles) { |
| | | proxy.$modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | // ç¡®ä¿ tempFileIds åå¨ä¸ä¸ºæ°ç» |
| | | if (!form.value.tempFileIds) { |
| | | form.value.tempFileIds = []; |
| | | } |
| | | form.value.tempFileIds.push(res.data.tempId); |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | proxy.$refs.fileUpload.handleRemove(file); |
| | | } |
| | | } |
| | | // ç§»é¤æä»¶ |
| | | function handleRemove(file) { |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | | delLedgerFile(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | |
| | | tableData.value = list |
| | | } |
| | | const downLoadFile = (row) => { |
| | | proxy.$download.name(row.url); |
| | | |
| | | proxy.$download.byUrl(row.url, row.originalFilename); |
| | | } |
| | | const lookFile = (row) => { |
| | | filePreviewRef.value.open(row.url) |
| | |
| | | </div> |
| | | <div class="filter-item"> |
| | | <span class="filter-label">审æ¹ç¶æ</span> |
| | | <el-select |
| | | v-model="searchForm.approveStatus" |
| | | clearable |
| | | <el-select |
| | | v-model="searchForm.approveStatus" |
| | | clearable |
| | | @change="handleQuery" |
| | | placeholder="è¯·éæ©ç¶æ" |
| | | class="search-select" |
| | |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <div class="custom-table"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumnCopy" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumnCopy" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | class="custom-table" |
| | | ></PIMTable> |
| | | </el-card> |
| | | |
| | | <!-- å¼¹çªç»ä»¶ --> |
| | |
| | | |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="showAddContactDialog = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="addContact">ç¡®å®</el-button> |
| | | <el-button @click="showAddContactDialog = false">åæ¶</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div class="search_form" style="margin-bottom: 20px;"> |
| | | <div> |
| | | <span class="search_title">ç¥è¯æ é¢ï¼</span> |
| | | <el-input |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { Clock, Location, User, UserFilled } from '@element-plus/icons-vue' |
| | | import Editor from "@/components/Editor/index.vue"; |
| | | import {getMeetSummaryItems,getMeetSummary} from '@/api/collaborativeApproval/meeting.js' |
| | | import dayjs from "dayjs"; |
| | | |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form-item label="ä¾åºååç§°ï¼"> |
| | | <el-input v-model="searchForm.supplierName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | <el-input v-model="searchForm.supplierName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="éè´ååå·ï¼"> |
| | | <el-input |
| | | v-model="searchForm.purchaseContractNumber" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-input v-model="searchForm.purchaseContractNumber" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | </el-form-item> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | <el-input v-model="searchForm.salesContractNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="项ç®åç§°ï¼"> |
| | | <el-input v-model="searchForm.projectName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | <el-input v-model="searchForm.projectName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> |
| | | <el-button type="primary" |
| | | @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;"> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | @expand-change="expandChange" |
| | | height="calc(100vh - 18.5em)" |
| | | :row-class-name="tableRowClassName" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | @expand-change="expandChange" |
| | | height="calc(100vh - 18.5em)" |
| | | :row-class-name="tableRowClassName"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | | <el-table-column type="expand"> |
| | | <template #default="props"> |
| | | <el-table |
| | | :data="props.row.children" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeChildrenTable" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="尺寸" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" /> |
| | | <el-table-column label="æ°é" prop="quantity" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" /> |
| | | <el-table-column |
| | | label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table :data="props.row.children" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeChildrenTable"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="产å大类" |
| | | prop="productCategory" /> |
| | | <el-table-column label="尺寸" |
| | | prop="specificationModel" /> |
| | | <el-table-column label="åä½" |
| | | prop="unit" /> |
| | | <el-table-column label="æ°é" |
| | | prop="quantity" /> |
| | | <el-table-column label="ç¨ç(%)" |
| | | prop="taxRate" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" /> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column |
| | | label="éè´ååå·" |
| | | prop="purchaseContractNumber" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="ä¾åºååç§°" |
| | | width="240" |
| | | prop="supplierName" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="订åç¶æ" width="100" align="center"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="éè´ååå·" |
| | | prop="purchaseContractNumber" |
| | | width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ä¾åºååç§°" |
| | | width="240" |
| | | prop="supplierName" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="订åç¶æ" |
| | | width="100" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.isInvalid" type="danger" size="small">失æ</el-tag> |
| | | <el-tag v-else type="success" size="small">æ£å¸¸</el-tag> |
| | | <el-tag v-if="scope.row.isInvalid" |
| | | type="danger" |
| | | size="small">失æ</el-tag> |
| | | <el-tag v-else |
| | | type="success" |
| | | size="small">æ£å¸¸</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="项ç®åç§°" |
| | | prop="projectName" |
| | | width="420" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="审æ¹ç¶æ" |
| | | prop="approvalStatus" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | <el-table-column label="项ç®åç§°" |
| | | prop="projectName" |
| | | width="420" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="审æ¹ç¶æ" |
| | | prop="approvalStatus" |
| | | width="200" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag |
| | | size="small" |
| | | > |
| | | <el-tag size="small"> |
| | | {{ approvalStatusText[scope.row.approvalStatus] || 'æªç¥ç¶æ' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="仿¬¾æ¹å¼" |
| | | width="100" |
| | | prop="paymentMethod" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="ååéé¢(å
)" |
| | | prop="contractAmount" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="å½å
¥äºº" |
| | | prop="recorderName" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å½å
¥æ¥æ" |
| | | prop="entryDate" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | fixed="right" |
| | | label="æä½" |
| | | min-width="150" |
| | | align="center" |
| | | > |
| | | <el-table-column label="仿¬¾æ¹å¼" |
| | | width="100" |
| | | prop="paymentMethod" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ååéé¢(å
)" |
| | | prop="contractAmount" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="å½å
¥äºº" |
| | | prop="recorderName" |
| | | width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="å½å
¥æ¥æ" |
| | | prop="entryDate" |
| | | width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | min-width="150" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="approvePurchase(scope.row)" |
| | | :disabled="scope.row.approvalStatus !== 0" |
| | | >审æ¹</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="rejectPurchase(scope.row)" |
| | | :disabled="scope.row.approvalStatus !== 0" |
| | | >æç»å®¡æ¹</el-button |
| | | > |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="approvePurchase(scope.row)" |
| | | :disabled="scope.row.approvalStatus !== 0">审æ¹</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="rejectPurchase(scope.row)" |
| | | :disabled="scope.row.approvalStatus !== 0">æç»å®¡æ¹</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | getSalesLedgerWithProducts, |
| | | addOrUpdateSalesLedgerProduct, |
| | | delProduct, |
| | | delLedgerFile, |
| | | getProductInfoByContractNo, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { |
| | | addOrEditPurchase, |
| | | delPurchase, |
| | | getSalesNo, |
| | | purchaseListPage, |
| | | productList, |
| | | getPurchaseById, |
| | | getOptions, |
| | | createPurchaseNo, updateApprovalStatus, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import QRCode from "qrcode"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { |
| | | ref, |
| | | onMounted, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | getSalesLedgerWithProducts, |
| | | addOrUpdateSalesLedgerProduct, |
| | | delProduct, |
| | | delLedgerFile, |
| | | getProductInfoByContractNo, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { |
| | | addOrEditPurchase, |
| | | delPurchase, |
| | | getSalesNo, |
| | | purchaseListPage, |
| | | productList, |
| | | getPurchaseById, |
| | | getOptions, |
| | | createPurchaseNo, |
| | | updateApprovalStatus, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import QRCode from "qrcode"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const productData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const productSelectedRows = ref([]); |
| | | const modelOptions = ref([]); |
| | | const userList = ref([]); |
| | | const productOptions = ref([]); |
| | | const salesContractList = ref([]); |
| | | const supplierList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const fileList = ref([]); |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import dayjs from "dayjs"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const productData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const productSelectedRows = ref([]); |
| | | const modelOptions = ref([]); |
| | | const userList = ref([]); |
| | | const productOptions = ref([]); |
| | | const salesContractList = ref([]); |
| | | const supplierList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const fileList = ref([]); |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import dayjs from "dayjs"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | const userStore = useUserStore(); |
| | | |
| | | const userStore = useUserStore(); |
| | | // äºç»´ç ç¸å
³åé |
| | | const qrCodeDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | |
| | | // äºç»´ç ç¸å
³åé |
| | | const qrCodeDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | // 订å审æ¹ç¶ææ¾ç¤ºææ¬ |
| | | const approvalStatusText = { |
| | | 0: "å¾
审æ¹", |
| | | 1: "审æ¹éè¿", |
| | | 2: "审æ¹å¤±è´¥", |
| | | }; |
| | | |
| | | // 订å审æ¹ç¶ææ¾ç¤ºææ¬ |
| | | const approvalStatusText = { |
| | | 0: 'å¾
审æ¹', |
| | | 1: '审æ¹éè¿', |
| | | 2: '审æ¹å¤±è´¥' |
| | | }; |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierName: "", // ä¾åºååç§° |
| | | purchaseContractNumber: "", // éè´ååç¼å· |
| | | salesContractNo: "", // éå®ååç¼å· |
| | | projectName: "", // 项ç®åç§° |
| | | entryDate: null, // å½å
¥æ¥æ |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }, |
| | | form: { |
| | | purchaseContractNumber: "", |
| | | salesLedgerId: "", |
| | | projectName: "", |
| | | recorderId: "", |
| | | entryDate: "", |
| | | productData: [], |
| | | supplierName: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | executionDate: "", |
| | | approvalStatus: "0", |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | entryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | executionDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData(data.searchForm); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierName: "", // ä¾åºååç§° |
| | | purchaseContractNumber: "", // éè´ååç¼å· |
| | | salesContractNo: "", // éå®ååç¼å· |
| | | projectName: "", // 项ç®åç§° |
| | | entryDate: null, // å½å
¥æ¥æ |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }, |
| | | form: { |
| | | purchaseContractNumber: "", |
| | | salesLedgerId: "", |
| | | projectName: "", |
| | | recorderId: "", |
| | | entryDate: "", |
| | | productData: [], |
| | | supplierName: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | executionDate: "", |
| | | approvalStatus: "0", |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | entryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | executionDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData(data.searchForm); |
| | | // 产å表åå¼¹æ¡æ°æ® |
| | | const productFormVisible = ref(false); |
| | | const productOperationType = ref(""); |
| | | const productOperationIndex = ref(""); |
| | | const currentId = ref(""); |
| | | const productFormData = reactive({ |
| | | productForm: { |
| | | productId: "", |
| | | productCategory: "", |
| | | productModelId: "", |
| | | specificationModel: "", |
| | | unit: "", |
| | | quantity: "", |
| | | taxInclusiveUnitPrice: "", |
| | | taxRate: "", |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | warnNum: "", |
| | | }, |
| | | productRules: { |
| | | productId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | unit: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | warnNum: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxExclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | invoiceType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { productForm, productRules } = toRefs(productFormData); |
| | | // const upload = reactive({ |
| | | // // ä¸ä¼ çå°å |
| | | // url: import.meta.env.VITE_APP_BASE_API + "/file/upload", |
| | | // // 设置ä¸ä¼ ç请æ±å¤´é¨ |
| | | // headers: { Authorization: "Bearer " + getToken() }, |
| | | // }); |
| | | |
| | | // 产å表åå¼¹æ¡æ°æ® |
| | | const productFormVisible = ref(false); |
| | | const productOperationType = ref(""); |
| | | const productOperationIndex = ref(""); |
| | | const currentId = ref(""); |
| | | const productFormData = reactive({ |
| | | productForm: { |
| | | productId: "", |
| | | productCategory: "", |
| | | productModelId: "", |
| | | specificationModel: "", |
| | | unit: "", |
| | | quantity: "", |
| | | taxInclusiveUnitPrice: "", |
| | | taxRate: "", |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | warnNum: "", |
| | | }, |
| | | productRules: { |
| | | productId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | unit: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | warnNum: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxExclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | invoiceType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { productForm, productRules } = toRefs(productFormData); |
| | | const upload = reactive({ |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/file/upload", |
| | | // 设置ä¸ä¼ ç请æ±å¤´é¨ |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | }); |
| | | const changeDaterange = value => { |
| | | if (value) { |
| | | searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | searchForm.entryDateStart = undefined; |
| | | searchForm.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | searchForm.entryDateStart = undefined; |
| | | searchForm.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeChildrenTable = (param) => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | [ |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeChildrenTable = param => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | "ticketsNum", |
| | | "ticketsAmount", |
| | | "futureTickets", |
| | | "futureTicketsAmount", |
| | | ], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | const paginationChange = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const { entryDate, ...rest } = searchForm; |
| | | purchaseListPage({ ...rest, ...page }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | // tableData.value = res.data.records; |
| | | // å¤çæ°æ®ï¼æ·»å 失æç¶ææ è®° |
| | | tableData.value = res.data.records.map(record => ({ |
| | | ...record, |
| | | isInvalid: record.isWhite === 1, |
| | | })); |
| | | tableData.value.map(item => { |
| | | item.children = []; |
| | | }); |
| | | total.value = res.data.total; |
| | | expandedRowKeys.value = []; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | const productSelected = selectedRows => { |
| | | productSelectedRows.value = selectedRows; |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | if (expandedRows.length > 0) { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | productList({ salesLedgerId: row.id, type: 2 }).then(res => { |
| | | const index = tableData.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | tableData.value[index].children = res.data; |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable(param, ["contractAmount"]); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeProTable = param => { |
| | | return proxy.summarizeTable(param, [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | "ticketsNum", |
| | | "ticketsAmount", |
| | | "futureTickets", |
| | | "futureTicketsAmount", |
| | | ], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const { entryDate, ...rest } = searchForm; |
| | | purchaseListPage({ ...rest, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | // tableData.value = res.data.records; |
| | | // å¤çæ°æ®ï¼æ·»å 失æç¶ææ è®° |
| | | tableData.value = res.data.records.map(record => ({ |
| | | ...record, |
| | | isInvalid: record.isWhite === 1 |
| | | })); |
| | | tableData.value.map((item) => { |
| | | item.children = []; |
| | | ]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | if (operationType.value == "add") { |
| | | createPurchaseNo().then(res => { |
| | | form.value.purchaseContractNumber = res.data; |
| | | }); |
| | | total.value = res.data.total; |
| | | expandedRowKeys.value = []; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | } |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | const productSelected = (selectedRows) => { |
| | | productSelectedRows.value = selectedRows; |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | if (expandedRows.length > 0) { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | productList({ salesLedgerId: row.id, type: 2 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | | if (index > -1) { |
| | | tableData.value[index].children = res.data; |
| | | getSalesNo().then(res => { |
| | | salesContractList.value = res; |
| | | }); |
| | | getOptions().then(res => { |
| | | // ä¾åºåè¿æ»¤åºisWhite=0 çæ°æ® |
| | | supplierList.value = res.data.filter(item => item.isWhite == 0); |
| | | }); |
| | | form.value.recorderId = userStore.id; |
| | | form.value.entryDate = getCurrentDate(); |
| | | if (type === "edit") { |
| | | currentId.value = row.id; |
| | | getPurchaseById({ id: row.id, type: 2 }).then(res => { |
| | | form.value = { ...res }; |
| | | productData.value = form.value.productData; |
| | | if (form.value.salesLedgerFiles) { |
| | | fileList.value = form.value.salesLedgerFiles; |
| | | } else { |
| | | fileList.value = []; |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | // ä¸ä¼ åæ ¡æ£ |
| | | function handleBeforeUpload(file) { |
| | | // æ ¡æ£æä»¶å¤§å° |
| | | if (file.size > 1024 * 1024 * 10) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿10MB!"); |
| | | return false; |
| | | } |
| | | proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); |
| | | return true; |
| | | } |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, ["contractAmount"]); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeProTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | if (operationType.value == "add") { |
| | | createPurchaseNo().then((res) => { |
| | | form.value.purchaseContractNumber = res.data; |
| | | }); |
| | | // ä¸ä¼ 失败 |
| | | function handleUploadError(err) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | userListNoPage().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | getSalesNo().then((res) => { |
| | | salesContractList.value = res; |
| | | }); |
| | | getOptions().then((res) => { |
| | | // ä¾åºåè¿æ»¤åºisWhite=0 çæ°æ® |
| | | supplierList.value = res.data.filter((item) => item.isWhite == 0); |
| | | }); |
| | | form.value.recorderId = userStore.id; |
| | | form.value.entryDate = getCurrentDate(); |
| | | if (type === "edit") { |
| | | currentId.value = row.id; |
| | | getPurchaseById({ id: row.id, type: 2 }).then((res) => { |
| | | form.value = { ...res }; |
| | | productData.value = form.value.productData; |
| | | if (form.value.salesLedgerFiles) { |
| | | fileList.value = form.value.salesLedgerFiles; |
| | | } else { |
| | | fileList.value = []; |
| | | // ä¸ä¼ æååè° |
| | | function handleUploadSuccess(res, file, uploadFiles) { |
| | | proxy.$modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | file.tempId = res.data.tempId; |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | proxy.$refs.fileUpload.handleRemove(file); |
| | | } |
| | | } |
| | | // ç§»é¤æä»¶ |
| | | function handleRemove(file) { |
| | | console.log("handleRemove", file.id); |
| | | if (file.size > 1024 * 1024 * 10) { |
| | | // ä»
å端æ¸
çï¼ä¸è°ç¨å 餿¥å£åæç¤º |
| | | return; |
| | | } |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | | delLedgerFile(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }); |
| | | } |
| | | } |
| | | // æäº¤è¡¨å |
| | | const submitForm = n => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | if (productData.value.length > 0) { |
| | | form.value.productData = proxy.HaveJson(productData.value); |
| | | } else { |
| | | proxy.$modal.msgWarning("请添å 产åä¿¡æ¯"); |
| | | return; |
| | | } |
| | | let tempFileIds = []; |
| | | if (fileList.value.length > 0) { |
| | | tempFileIds = fileList.value.map(item => item.tempId); |
| | | } |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 2; |
| | | form.value.approvalStatus = n; |
| | | addOrEditPurchase(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | // ä¸ä¼ åæ ¡æ£ |
| | | function handleBeforeUpload(file) { |
| | | // æ ¡æ£æä»¶å¤§å° |
| | | if (file.size > 1024 * 1024 * 10) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿10MB!"); |
| | | return false; |
| | | } |
| | | proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); |
| | | return true; |
| | | } |
| | | // ä¸ä¼ 失败 |
| | | function handleUploadError(err) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | // ä¸ä¼ æååè° |
| | | function handleUploadSuccess(res, file, uploadFiles) { |
| | | proxy.$modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | file.tempId = res.data.tempId; |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | proxy.$refs.fileUpload.handleRemove(file); |
| | | } |
| | | } |
| | | // ç§»é¤æä»¶ |
| | | function handleRemove(file) { |
| | | console.log("handleRemove", file.id); |
| | | if (file.size > 1024 * 1024 * 10) { |
| | | // ä»
å端æ¸
çï¼ä¸è°ç¨å 餿¥å£åæç¤º |
| | | return; |
| | | } |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | | delLedgerFile(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProductForm = (type, row, index) => { |
| | | productOperationType.value = type; |
| | | productOperationIndex.value = index; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | } |
| | | productFormVisible.value = true; |
| | | getProductOptions(); |
| | | }; |
| | | const getProductOptions = () => { |
| | | productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const getModels = value => { |
| | | if (value) { |
| | | productForm.value.productCategory = |
| | | findNodeById(productOptions.value, value) || ""; |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res; |
| | | }); |
| | | } else { |
| | | productForm.value.productCategory = ""; |
| | | modelOptions.value = []; |
| | | } |
| | | }; |
| | | const getProductModel = value => { |
| | | const index = modelOptions.value.findIndex(item => item.id === value); |
| | | if (index !== -1) { |
| | | productForm.value.specificationModel = modelOptions.value[index].model; |
| | | productForm.value.unit = modelOptions.value[index].unit; |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.unit = null; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹çlabel |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼ç´æ¥è¿åï¼å·²ç»æ¯labelåç¬¦ä¸²ï¼ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | } |
| | | // æäº¤è¡¨å |
| | | const submitForm = (n) => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | | if (valid) { |
| | | if (productData.value.length > 0) { |
| | | form.value.productData = proxy.HaveJson(productData.value); |
| | | } else { |
| | | proxy.$modal.msgWarning("请添å 产åä¿¡æ¯"); |
| | | // æäº¤äº§å表å |
| | | const submitProduct = () => { |
| | | proxy.$refs["productFormRef"].validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitProductEdit(); |
| | | } else { |
| | | if (productOperationType.value === "add") { |
| | | productData.value.push({ ...productForm.value }); |
| | | console.log("productData.value---", productData.value); |
| | | } else { |
| | | productData.value[productOperationIndex.value] = { |
| | | ...productForm.value, |
| | | }; |
| | | } |
| | | closeProductDia(); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | const submitProductEdit = () => { |
| | | productForm.value.salesLedgerId = currentId.value; |
| | | productForm.value.type = 2; |
| | | addOrUpdateSalesLedgerProduct(productForm.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeProductDia(); |
| | | getPurchaseById({ id: currentId.value, type: 2 }).then(res => { |
| | | productData.value = res.productData; |
| | | }); |
| | | }); |
| | | }; |
| | | // å é¤äº§å |
| | | const deleteProduct = () => { |
| | | if (productSelectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | if (operationType.value === "add") { |
| | | productSelectedRows.value.forEach(selectedRow => { |
| | | const index = productData.value.findIndex( |
| | | product => product.id === selectedRow.id |
| | | ); |
| | | if (index !== -1) { |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | } else { |
| | | let ids = []; |
| | | if (productSelectedRows.value.length > 0) { |
| | | ids = productSelectedRows.value.map(item => item.id); |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delProduct(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | closeProductDia(); |
| | | getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then( |
| | | res => { |
| | | productData.value = res.productData; |
| | | } |
| | | ); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | } |
| | | }; |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProductDia = () => { |
| | | proxy.resetForm("productFormRef"); |
| | | productFormVisible.value = false; |
| | | }; |
| | | // 审æ¹éè¿æ¹æ³ |
| | | const approvePurchase = row => { |
| | | ElMessageBox.confirm( |
| | | `确认éè¿éè´ååå·ä¸º ${row.purchaseContractNumber} ç审æ¹ï¼`, |
| | | "审æ¹ç¡®è®¤", |
| | | { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(() => { |
| | | updateApprovalStatus({ id: row.id, approvalStatus: 1 }).then(res => { |
| | | proxy.$modal.msgSuccess("å®¡æ¹æå"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶审æ¹"); |
| | | }); |
| | | }; |
| | | |
| | | // å®¡æ¹æç»æ¹æ³ |
| | | const rejectPurchase = row => { |
| | | ElMessageBox.confirm( |
| | | `确认æç»éè´ååå·ä¸º ${row.purchaseContractNumber} ç审æ¹ï¼`, |
| | | "审æ¹ç¡®è®¤", |
| | | { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(() => { |
| | | updateApprovalStatus({ id: row.id, approvalStatus: 2 }).then(res => { |
| | | proxy.$modal.msgSuccess("å®¡æ¹æå"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶审æ¹"); |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/purchase/ledger/export", {}, "éè´å°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® |
| | | const unauthorizedData = selectedRows.value.filter( |
| | | item => item.recorderName !== userStore.nickName |
| | | ); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); |
| | | return; |
| | | } |
| | | let tempFileIds = []; |
| | | if (fileList.value.length > 0) { |
| | | tempFileIds = fileList.value.map((item) => item.tempId); |
| | | } |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 2; |
| | | form.value.approvalStatus = n; |
| | | addOrEditPurchase(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProductForm = (type, row, index) => { |
| | | productOperationType.value = type; |
| | | productOperationIndex.value = index; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | } |
| | | productFormVisible.value = true; |
| | | getProductOptions(); |
| | | }; |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const getModels = (value) => { |
| | | if (value) { |
| | | productForm.value.productCategory = findNodeById(productOptions.value, value) || ""; |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | } else { |
| | | productForm.value.productCategory = ""; |
| | | modelOptions.value = []; |
| | | } |
| | | }; |
| | | const getProductModel = (value) => { |
| | | const index = modelOptions.value.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | productForm.value.specificationModel = modelOptions.value[index].model; |
| | | productForm.value.unit = modelOptions.value[index].unit; |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.unit = null; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹çlabel |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼ç´æ¥è¿åï¼å·²ç»æ¯labelåç¬¦ä¸²ï¼ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // æäº¤äº§å表å |
| | | const submitProduct = () => { |
| | | proxy.$refs["productFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitProductEdit(); |
| | | } else { |
| | | if (productOperationType.value === "add") { |
| | | productData.value.push({ ...productForm.value }); |
| | | console.log("productData.value---", productData.value); |
| | | } else { |
| | | productData.value[productOperationIndex.value] = { |
| | | ...productForm.value, |
| | | }; |
| | | } |
| | | closeProductDia(); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | const submitProductEdit = () => { |
| | | productForm.value.salesLedgerId = currentId.value; |
| | | productForm.value.type = 2; |
| | | addOrUpdateSalesLedgerProduct(productForm.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeProductDia(); |
| | | getPurchaseById({ id: currentId.value, type: 2 }).then((res) => { |
| | | productData.value = res.productData; |
| | | }); |
| | | }); |
| | | }; |
| | | // å é¤äº§å |
| | | const deleteProduct = () => { |
| | | if (productSelectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | if (operationType.value === "add") { |
| | | productSelectedRows.value.forEach((selectedRow) => { |
| | | const index = productData.value.findIndex( |
| | | (product) => product.id === selectedRow.id |
| | | ); |
| | | if (index !== -1) { |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | } else { |
| | | let ids = []; |
| | | if (productSelectedRows.value.length > 0) { |
| | | ids = productSelectedRows.value.map((item) => item.id); |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delProduct(ids).then((res) => { |
| | | delPurchase(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | closeProductDia(); |
| | | getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then( |
| | | (res) => { |
| | | productData.value = res.productData; |
| | | } |
| | | ); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | } |
| | | }; |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProductDia = () => { |
| | | proxy.resetForm("productFormRef"); |
| | | productFormVisible.value = false; |
| | | }; |
| | | // 审æ¹éè¿æ¹æ³ |
| | | const approvePurchase = (row) => { |
| | | ElMessageBox.confirm(`确认éè¿éè´ååå·ä¸º ${row.purchaseContractNumber} ç审æ¹ï¼`, '审æ¹ç¡®è®¤', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }).then(() => { |
| | | updateApprovalStatus({ id: row.id, approvalStatus: 1}).then((res)=>{ |
| | | proxy.$modal.msgSuccess('å®¡æ¹æå'); |
| | | getList(); |
| | | }) |
| | | }).catch(() => { |
| | | proxy.$modal.msg('已忶审æ¹'); |
| | | }); |
| | | }; |
| | | |
| | | // å®¡æ¹æç»æ¹æ³ |
| | | const rejectPurchase = (row) => { |
| | | ElMessageBox.confirm(`确认æç»éè´ååå·ä¸º ${row.purchaseContractNumber} ç审æ¹ï¼`, '审æ¹ç¡®è®¤', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }).then(() => { |
| | | updateApprovalStatus({ id: row.id, approvalStatus: 2}).then((res)=>{ |
| | | proxy.$modal.msgSuccess('å®¡æ¹æå'); |
| | | getList(); |
| | | }) |
| | | }).catch(() => { |
| | | proxy.$modal.msg('已忶审æ¹'); |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/purchase/ledger/export", {}, "éè´å°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® |
| | | const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delPurchase(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | const mathNum = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | if (!productForm.value.taxInclusiveUnitPrice) { |
| | | return; |
| | | } |
| | | if (!productForm.value.quantity) { |
| | | return; |
| | | } |
| | | // å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxInclusiveTotalPrice = |
| | | proxy.calculateTaxIncludeTotalPrice( |
| | | productForm.value.taxInclusiveUnitPrice, |
| | | productForm.value.quantity |
| | | ); |
| | | if (productForm.value.taxRate) { |
| | | // ä¸å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | productForm.value.taxInclusiveTotalPrice, |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | }; |
| | | const reverseMathNum = (field) => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | const taxRate = Number(productForm.value.taxRate); |
| | | if (!taxRate) return; |
| | | if (field === 'taxInclusiveTotalPrice') { |
| | | // å·²ç¥å«ç¨æ»ä»·åæ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2); |
| | | } |
| | | // å·²ç¥å«ç¨æ»ä»·åå«ç¨åä»·ï¼åç®æ°é |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2); |
| | | } |
| | | // åç®ä¸å«ç¨æ»ä»· |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2); |
| | | } else if (field === 'taxExclusiveTotalPrice') { |
| | | // åç®å«ç¨æ»ä»· |
| | | productForm.value.taxInclusiveTotalPrice = |
| | | (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2); |
| | | // å·²ç¥æ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2); |
| | | } |
| | | // å·²ç¥å«ç¨åä»·ï¼åç®æ°é |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2); |
| | | } |
| | | } |
| | | }; |
| | | // éå®ååéæ©æ¹åæ¹æ³ |
| | | const salesLedgerChange = async (row) => { |
| | | console.log("row", row); |
| | | var index = salesContractList.value.findIndex((item) => item.id == row); |
| | | console.log("index", index); |
| | | if (index > -1) { |
| | | form.value.projectName = salesContractList.value[index].projectName; |
| | | await querygProductInfoByContractNo(); |
| | | } |
| | | }; |
| | | |
| | | const querygProductInfoByContractNo = async () => { |
| | | const { code, data } = await getProductInfoByContractNo({ |
| | | contractNo: form.value.salesLedgerId, |
| | | }); |
| | | if (code == 200) { |
| | | productData.value = data; |
| | | } |
| | | }; |
| | | |
| | | // æ¾ç¤ºäºç»´ç |
| | | const showQRCode = async (row) => { |
| | | try { |
| | | // æå»ºäºç»´ç å
容ï¼åªå
å«éè´ååå·ï¼çº¯ææ¬ï¼ |
| | | const qrContent = row.purchaseContractNumber || ''; |
| | | // æ£æ¥å
容æ¯å¦ä¸ºç©º |
| | | if (!qrContent || qrContent.trim() === '') { |
| | | proxy.$modal.msgWarning("è¯¥è¡æ²¡æéè´ååå·ï¼æ æ³çæäºç»´ç "); |
| | | }; |
| | | const mathNum = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | qrCodeUrl.value = await QRCode.toDataURL(qrContent, { |
| | | width: 200, |
| | | margin: 2, |
| | | color: { |
| | | dark: '#000000', |
| | | light: '#FFFFFF' |
| | | if (!productForm.value.taxInclusiveUnitPrice) { |
| | | return; |
| | | } |
| | | if (!productForm.value.quantity) { |
| | | return; |
| | | } |
| | | // å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxInclusiveTotalPrice = |
| | | proxy.calculateTaxIncludeTotalPrice( |
| | | productForm.value.taxInclusiveUnitPrice, |
| | | productForm.value.quantity |
| | | ); |
| | | if (productForm.value.taxRate) { |
| | | // ä¸å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | productForm.value.taxInclusiveTotalPrice, |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | }; |
| | | const reverseMathNum = field => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | const taxRate = Number(productForm.value.taxRate); |
| | | if (!taxRate) return; |
| | | if (field === "taxInclusiveTotalPrice") { |
| | | // å·²ç¥å«ç¨æ»ä»·åæ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = ( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | Number(productForm.value.quantity) |
| | | ).toFixed(2); |
| | | } |
| | | // å·²ç¥å«ç¨æ»ä»·åå«ç¨åä»·ï¼åç®æ°é |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = ( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | Number(productForm.value.taxInclusiveUnitPrice) |
| | | ).toFixed(2); |
| | | } |
| | | // åç®ä¸å«ç¨æ»ä»· |
| | | productForm.value.taxExclusiveTotalPrice = ( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | (1 + taxRate / 100) |
| | | ).toFixed(2); |
| | | } else if (field === "taxExclusiveTotalPrice") { |
| | | // åç®å«ç¨æ»ä»· |
| | | productForm.value.taxInclusiveTotalPrice = ( |
| | | Number(productForm.value.taxExclusiveTotalPrice) * |
| | | (1 + taxRate / 100) |
| | | ).toFixed(2); |
| | | // å·²ç¥æ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = ( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | Number(productForm.value.quantity) |
| | | ).toFixed(2); |
| | | } |
| | | // å·²ç¥å«ç¨åä»·ï¼åç®æ°é |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = ( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | Number(productForm.value.taxInclusiveUnitPrice) |
| | | ).toFixed(2); |
| | | } |
| | | } |
| | | }; |
| | | // éå®ååéæ©æ¹åæ¹æ³ |
| | | const salesLedgerChange = async row => { |
| | | console.log("row", row); |
| | | var index = salesContractList.value.findIndex(item => item.id == row); |
| | | console.log("index", index); |
| | | if (index > -1) { |
| | | form.value.projectName = salesContractList.value[index].projectName; |
| | | await querygProductInfoByContractNo(); |
| | | } |
| | | }; |
| | | |
| | | const querygProductInfoByContractNo = async () => { |
| | | const { code, data } = await getProductInfoByContractNo({ |
| | | contractNo: form.value.salesLedgerId, |
| | | }); |
| | | if (code == 200) { |
| | | productData.value = data; |
| | | } |
| | | }; |
| | | |
| | | // æ¾ç¤ºäºç»´ç |
| | | const showQRCode = async row => { |
| | | try { |
| | | // æå»ºäºç»´ç å
容ï¼åªå
å«éè´ååå·ï¼çº¯ææ¬ï¼ |
| | | const qrContent = row.purchaseContractNumber || ""; |
| | | // æ£æ¥å
容æ¯å¦ä¸ºç©º |
| | | if (!qrContent || qrContent.trim() === "") { |
| | | proxy.$modal.msgWarning("è¯¥è¡æ²¡æéè´ååå·ï¼æ æ³çæäºç»´ç "); |
| | | return; |
| | | } |
| | | qrCodeUrl.value = await QRCode.toDataURL(qrContent, { |
| | | width: 200, |
| | | margin: 2, |
| | | color: { |
| | | dark: "#000000", |
| | | light: "#FFFFFF", |
| | | }, |
| | | }); |
| | | qrCodeDialogVisible.value = true; |
| | | } catch (error) { |
| | | console.error("çæäºç»´ç 失败:", error); |
| | | proxy.$modal.msgError("çæäºç»´ç 失败ï¼" + error.message); |
| | | } |
| | | }; |
| | | |
| | | // ä¸è½½äºç»´ç |
| | | const downloadQRCode = () => { |
| | | if (!qrCodeUrl.value) { |
| | | proxy.$modal.msgWarning("äºç»´ç æªçæ"); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement("a"); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `éè´ååå·äºç»´ç _${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | proxy.$modal.msgSuccess("ä¸è½½æå"); |
| | | }; |
| | | |
| | | // æ«ç æ°å¢å¯¹è¯æ¡ç¸å
³åé |
| | | const scanAddDialogVisible = ref(false); |
| | | const scanAddForm = reactive({ |
| | | scanContent: "", |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | contractAmount: "", |
| | | paymentMethod: "", |
| | | recorderName: "", |
| | | scanRemark: "", |
| | | }); |
| | | const scanAddRules = { |
| | | purchaseContractNumber: [ |
| | | { required: true, message: "请è¾å
¥éè´ååå·", trigger: "blur" }, |
| | | ], |
| | | supplierName: [ |
| | | { required: true, message: "请è¾å
¥ä¾åºååç§°", trigger: "blur" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请è¾å
¥é¡¹ç®åç§°", trigger: "blur" }], |
| | | }; |
| | | |
| | | // æ«ç ç»è®°å¯¹è¯æ¡ç¸å
³åé |
| | | const scanDialogVisible = ref(false); |
| | | const scanForm = reactive({ |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | scanTime: "", |
| | | scannerName: "", |
| | | scanStatus: "æªæ«ç ", |
| | | scanRemark: "", |
| | | }); |
| | | const scanRules = { |
| | | scanRemark: [{ required: true, message: "请è¾å
¥æ«ç 夿³¨", trigger: "blur" }], |
| | | }; |
| | | const scanRecords = ref([]); |
| | | |
| | | // æå¼æ«ç æ°å¢å¯¹è¯æ¡ |
| | | const openScanAddDialog = () => { |
| | | scanAddForm.scanContent = ""; |
| | | scanAddForm.purchaseContractNumber = ""; |
| | | scanAddForm.supplierName = ""; |
| | | scanAddForm.projectName = ""; |
| | | scanAddForm.contractAmount = ""; |
| | | scanAddForm.paymentMethod = ""; |
| | | scanAddForm.recorderName = userStore.nickName; |
| | | scanAddForm.scanRemark = ""; |
| | | scanAddDialogVisible.value = true; |
| | | }; |
| | | |
| | | // è§£ææ«ç å
å®¹ï¼æ¨¡æè§£æäºç»´ç æ°æ®ï¼ |
| | | const parseScanContent = content => { |
| | | if (!content) return; |
| | | |
| | | // 模æè§£æäºç»´ç å
容ï¼è¿éå¯ä»¥æ ¹æ®å®é
éæ±è°æ´è§£æé»è¾ |
| | | // å设æ«ç å
å®¹æ ¼å¼ä¸ºï¼ååå·|ä¾åºå|项ç®|éé¢|仿¬¾æ¹å¼ |
| | | const parts = content.split("|"); |
| | | if (parts.length >= 3) { |
| | | scanAddForm.purchaseContractNumber = parts[0] || ""; |
| | | scanAddForm.supplierName = parts[1] || ""; |
| | | scanAddForm.projectName = parts[2] || ""; |
| | | scanAddForm.contractAmount = parts[3] || ""; |
| | | scanAddForm.paymentMethod = parts[4] || ""; |
| | | } |
| | | }; |
| | | |
| | | // å
³éæ«ç æ°å¢å¯¹è¯æ¡ |
| | | const closeScanAddDialog = () => { |
| | | scanAddDialogVisible.value = false; |
| | | proxy.resetForm("scanAddFormRef"); |
| | | }; |
| | | |
| | | // æäº¤æ«ç æ°å¢ |
| | | const submitScanAdd = () => { |
| | | proxy.$refs["scanAddFormRef"].validate(valid => { |
| | | if (valid) { |
| | | // æå»ºæ°å¢æ°æ® |
| | | const newData = { |
| | | purchaseContractNumber: scanAddForm.purchaseContractNumber, |
| | | supplierName: scanAddForm.supplierName, |
| | | projectName: scanAddForm.projectName, |
| | | contractAmount: scanAddForm.contractAmount, |
| | | paymentMethod: scanAddForm.paymentMethod, |
| | | recorderName: scanAddForm.recorderName, |
| | | entryDate: getCurrentDate(), |
| | | remark: scanAddForm.scanRemark, |
| | | type: 2, |
| | | }; |
| | | |
| | | // æ¨¡ææ°å¢æå |
| | | proxy.$modal.msgSuccess("æ«ç æ°å¢æåï¼"); |
| | | closeScanAddDialog(); |
| | | |
| | | // å¯ä»¥éæ©æ¯å¦å·æ°å表 |
| | | // getList(); |
| | | } |
| | | }); |
| | | qrCodeDialogVisible.value = true; |
| | | } catch (error) { |
| | | console.error('çæäºç»´ç 失败:', error); |
| | | proxy.$modal.msgError("çæäºç»´ç 失败ï¼" + error.message); |
| | | }; |
| | | |
| | | // æå¼æ«ç ç»è®°å¯¹è¯æ¡ |
| | | const openScanDialog = row => { |
| | | scanForm.purchaseContractNumber = row.purchaseContractNumber; |
| | | scanForm.supplierName = row.supplierName; |
| | | scanForm.projectName = row.projectName; |
| | | scanForm.scanTime = getCurrentDateTime(); |
| | | scanForm.scannerName = userStore.nickName; |
| | | scanForm.scanStatus = "æªæ«ç "; |
| | | scanForm.scanRemark = ""; |
| | | scanRecords.value = []; |
| | | scanDialogVisible.value = true; |
| | | }; |
| | | |
| | | // å
³éæ«ç ç»è®°å¯¹è¯æ¡ |
| | | const closeScanDialog = () => { |
| | | scanDialogVisible.value = false; |
| | | proxy.resetForm("scanFormRef"); |
| | | }; |
| | | |
| | | // æäº¤æ«ç ç»è®° |
| | | const submitScan = () => { |
| | | proxy.$refs["scanFormRef"].validate(valid => { |
| | | if (valid) { |
| | | // æ·»å æ«ç è®°å½ |
| | | scanRecords.value.push({ |
| | | ...scanForm, |
| | | id: Date.now(), // 模æID |
| | | scanTime: getCurrentDateTime(), |
| | | }); |
| | | scanForm.scanStatus = "å·²æ«ç "; |
| | | scanForm.scanRemark = scanForm.scanRemark || "æ "; |
| | | proxy.$modal.msgSuccess("æ«ç ç»è®°æåï¼"); |
| | | closeScanDialog(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åæ¥ææ¶é´ |
| | | function getCurrentDateTime() { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | const hours = String(now.getHours()).padStart(2, "0"); |
| | | const minutes = String(now.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(now.getSeconds()).padStart(2, "0"); |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | }; |
| | | |
| | | // ä¸è½½äºç»´ç |
| | | const downloadQRCode = () => { |
| | | if (!qrCodeUrl.value) { |
| | | proxy.$modal.msgWarning("äºç»´ç æªçæ"); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement('a'); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `éè´ååå·äºç»´ç _${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | proxy.$modal.msgSuccess("ä¸è½½æå"); |
| | | }; |
| | | // æ·»å è¡ç±»åæ¹æ³ |
| | | const tableRowClassName = ({ row }) => { |
| | | return row.isInvalid ? "invalid-row" : ""; |
| | | }; |
| | | |
| | | // æ«ç æ°å¢å¯¹è¯æ¡ç¸å
³åé |
| | | const scanAddDialogVisible = ref(false); |
| | | const scanAddForm = reactive({ |
| | | scanContent: "", |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | contractAmount: "", |
| | | paymentMethod: "", |
| | | recorderName: "", |
| | | scanRemark: "", |
| | | }); |
| | | const scanAddRules = { |
| | | purchaseContractNumber: [{ required: true, message: "请è¾å
¥éè´ååå·", trigger: "blur" }], |
| | | supplierName: [{ required: true, message: "请è¾å
¥ä¾åºååç§°", trigger: "blur" }], |
| | | projectName: [{ required: true, message: "请è¾å
¥é¡¹ç®åç§°", trigger: "blur" }], |
| | | }; |
| | | |
| | | // æ«ç ç»è®°å¯¹è¯æ¡ç¸å
³åé |
| | | const scanDialogVisible = ref(false); |
| | | const scanForm = reactive({ |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | scanTime: "", |
| | | scannerName: "", |
| | | scanStatus: "æªæ«ç ", |
| | | scanRemark: "", |
| | | }); |
| | | const scanRules = { |
| | | scanRemark: [{ required: true, message: "请è¾å
¥æ«ç 夿³¨", trigger: "blur" }], |
| | | }; |
| | | const scanRecords = ref([]); |
| | | |
| | | // æå¼æ«ç æ°å¢å¯¹è¯æ¡ |
| | | const openScanAddDialog = () => { |
| | | scanAddForm.scanContent = ""; |
| | | scanAddForm.purchaseContractNumber = ""; |
| | | scanAddForm.supplierName = ""; |
| | | scanAddForm.projectName = ""; |
| | | scanAddForm.contractAmount = ""; |
| | | scanAddForm.paymentMethod = ""; |
| | | scanAddForm.recorderName = userStore.nickName; |
| | | scanAddForm.scanRemark = ""; |
| | | scanAddDialogVisible.value = true; |
| | | }; |
| | | |
| | | // è§£ææ«ç å
å®¹ï¼æ¨¡æè§£æäºç»´ç æ°æ®ï¼ |
| | | const parseScanContent = (content) => { |
| | | if (!content) return; |
| | | |
| | | // 模æè§£æäºç»´ç å
容ï¼è¿éå¯ä»¥æ ¹æ®å®é
éæ±è°æ´è§£æé»è¾ |
| | | // å设æ«ç å
å®¹æ ¼å¼ä¸ºï¼ååå·|ä¾åºå|项ç®|éé¢|仿¬¾æ¹å¼ |
| | | const parts = content.split('|'); |
| | | if (parts.length >= 3) { |
| | | scanAddForm.purchaseContractNumber = parts[0] || ""; |
| | | scanAddForm.supplierName = parts[1] || ""; |
| | | scanAddForm.projectName = parts[2] || ""; |
| | | scanAddForm.contractAmount = parts[3] || ""; |
| | | scanAddForm.paymentMethod = parts[4] || ""; |
| | | } |
| | | }; |
| | | |
| | | // å
³éæ«ç æ°å¢å¯¹è¯æ¡ |
| | | const closeScanAddDialog = () => { |
| | | scanAddDialogVisible.value = false; |
| | | proxy.resetForm("scanAddFormRef"); |
| | | }; |
| | | |
| | | // æäº¤æ«ç æ°å¢ |
| | | const submitScanAdd = () => { |
| | | proxy.$refs["scanAddFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // æå»ºæ°å¢æ°æ® |
| | | const newData = { |
| | | purchaseContractNumber: scanAddForm.purchaseContractNumber, |
| | | supplierName: scanAddForm.supplierName, |
| | | projectName: scanAddForm.projectName, |
| | | contractAmount: scanAddForm.contractAmount, |
| | | paymentMethod: scanAddForm.paymentMethod, |
| | | recorderName: scanAddForm.recorderName, |
| | | entryDate: getCurrentDate(), |
| | | remark: scanAddForm.scanRemark, |
| | | type: 2 |
| | | }; |
| | | |
| | | // æ¨¡ææ°å¢æå |
| | | proxy.$modal.msgSuccess("æ«ç æ°å¢æåï¼"); |
| | | closeScanAddDialog(); |
| | | |
| | | // å¯ä»¥éæ©æ¯å¦å·æ°å表 |
| | | // getList(); |
| | | } |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | }; |
| | | |
| | | // æå¼æ«ç ç»è®°å¯¹è¯æ¡ |
| | | const openScanDialog = (row) => { |
| | | scanForm.purchaseContractNumber = row.purchaseContractNumber; |
| | | scanForm.supplierName = row.supplierName; |
| | | scanForm.projectName = row.projectName; |
| | | scanForm.scanTime = getCurrentDateTime(); |
| | | scanForm.scannerName = userStore.nickName; |
| | | scanForm.scanStatus = "æªæ«ç "; |
| | | scanForm.scanRemark = ""; |
| | | scanRecords.value = []; |
| | | scanDialogVisible.value = true; |
| | | }; |
| | | |
| | | // å
³éæ«ç ç»è®°å¯¹è¯æ¡ |
| | | const closeScanDialog = () => { |
| | | scanDialogVisible.value = false; |
| | | proxy.resetForm("scanFormRef"); |
| | | }; |
| | | |
| | | // æäº¤æ«ç ç»è®° |
| | | const submitScan = () => { |
| | | proxy.$refs["scanFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // æ·»å æ«ç è®°å½ |
| | | scanRecords.value.push({ |
| | | ...scanForm, |
| | | id: Date.now(), // 模æID |
| | | scanTime: getCurrentDateTime(), |
| | | }); |
| | | scanForm.scanStatus = "å·²æ«ç "; |
| | | scanForm.scanRemark = scanForm.scanRemark || "æ "; |
| | | proxy.$modal.msgSuccess("æ«ç ç»è®°æåï¼"); |
| | | closeScanDialog(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åæ¥ææ¶é´ |
| | | function getCurrentDateTime() { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | const hours = String(now.getHours()).padStart(2, "0"); |
| | | const minutes = String(now.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(now.getSeconds()).padStart(2, "0"); |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | // æ·»å è¡ç±»åæ¹æ³ |
| | | const tableRowClassName = ({ row }) => { |
| | | return row.isInvalid ? 'invalid-row' : ''; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .invalid-row { |
| | | opacity: 0.6; |
| | | background-color: #f5f7fa; |
| | | } |
| | | .invalid-row { |
| | | opacity: 0.6; |
| | | background-color: #f5f7fa; |
| | | } |
| | | </style> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | <FileListDialog ref="fileListDialogRef" |
| | | v-model="fileDialogVisible" |
| | | :show-upload-button="true" |
| | | :show-delete-button="true" |
| | | :delete-method="handleAttachmentDelete" |
| | | :rules-regulations-management-id="currentFileRuleId" |
| | | :name-column-label="'éä»¶åç§°'" |
| | | @upload="handleAttachmentUpload"/> |
| | | <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="rules_regulations_management" :record-id="recordId" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | addReadingStatus, |
| | | updateReadingStatus, |
| | | } from "@/api/collaborativeApproval/sealManagement.js"; |
| | | import FileListDialog from "@/components/Dialog/FileListDialog.vue"; |
| | | const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); |
| | | import { |
| | | listRuleFiles, |
| | | delRuleFile, |
| | |
| | | total: 0, |
| | | }); |
| | | // éä»¶å¼¹çª |
| | | const fileDialogVisible = ref(false); |
| | | const fileListDialogRef = ref(null); |
| | | const currentFileRuleId = ref(null); |
| | | const filePage = reactive({ |
| | | current: 1, |
| | | size: 1000, |
| | | total: 0, |
| | | }); |
| | | // è§ç« å¶åº¦ç¸å
³ |
| | | const showRegulationDialog = ref(false); |
| | | const showRegulationDetailDialog = ref(false); |
| | |
| | | ); |
| | | }; |
| | | |
| | | // éä»¶ï¼æ¥è¯¢ |
| | | const fetchRuleFiles = async rulesRegulationsManagementId => { |
| | | const params = { |
| | | current: filePage.current, |
| | | size: filePage.size, |
| | | rulesRegulationsManagementId, |
| | | }; |
| | | const res = await listRuleFiles(params); |
| | | const records = res?.data?.records || []; |
| | | filePage.total = res?.data?.total || records.length; |
| | | const mapped = records.map(item => ({ |
| | | id: item.id, |
| | | name: item.fileName || item.name, |
| | | url: item.fileUrl || item.url, |
| | | raw: item, |
| | | })); |
| | | fileListDialogRef.value?.setList(mapped); |
| | | }; |
| | | |
| | | // æå¼éä»¶å¼¹çª |
| | | const openFileDialog = async row => { |
| | | currentFileRuleId.value = row.id; |
| | | fileDialogVisible.value = true; |
| | | await fetchRuleFiles(row.id); |
| | | }; |
| | | const recordId =ref(0) |
| | | const fileDialogVisible = ref(false) |
| | | |
| | | // å·æ°éä»¶å表 |
| | | const refreshFileList = async () => { |
| | | if (!currentFileRuleId.value) return; |
| | | await fetchRuleFiles(currentFileRuleId.value); |
| | | }; |
| | | |
| | | // ä¸ä¼ éä»¶ï¼ç±åç»ä»¶è§¦åï¼ |
| | | const handleAttachmentUpload = async filePayload => { |
| | | if (!currentFileRuleId.value) return; |
| | | const payload = { |
| | | name: filePayload?.fileName || filePayload?.name, |
| | | url: filePayload?.fileUrl || filePayload?.url, |
| | | rulesRegulationsManagementId: currentFileRuleId.value, |
| | | }; |
| | | await addRuleFile(payload); |
| | | ElMessage.success("æä»¶ä¸ä¼ æå"); |
| | | await refreshFileList(); |
| | | }; |
| | | |
| | | // å é¤éä»¶ |
| | | const handleAttachmentDelete = async row => { |
| | | if (!row?.id) return false; |
| | | try { |
| | | await ElMessageBox.confirm("确认å é¤è¯¥éä»¶ï¼", "æç¤º", { type: "warning" }); |
| | | } catch { |
| | | return false; |
| | | } |
| | | await delRuleFile([row.id]); |
| | | ElMessage.success("å 餿å"); |
| | | await refreshFileList(); |
| | | }; |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | const openFileDialog = async (row) => { |
| | | recordId.value = row.id |
| | | fileDialogVisible.value = true |
| | | } |
| | | |
| | | // è·åè§ç« å¶åº¦åè¡¨æ°æ® |
| | | const getRegulationList = async () => { |
| | |
| | | </el-form-item> |
| | | <el-form-item label="ç´§æ¥ç¨åº¦" prop="urgency"> |
| | | <el-radio-group v-model="sealForm.urgency"> |
| | | <el-radio label="normal">æ®é</el-radio> |
| | | <el-radio label="urgent">ç´§æ¥</el-radio> |
| | | <el-radio label="very-urgent">ç¹æ¥</el-radio> |
| | | <el-radio value="normal">æ®é</el-radio> |
| | | <el-radio value="urgent">ç´§æ¥</el-radio> |
| | | <el-radio value="very-urgent">ç¹æ¥</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="éä»¶ä¸ä¼ "> |
| | | <AttachmentUploadFile |
| | | v-model:fileList="sealForm.storageBlobDTOs" |
| | | :limit="10" |
| | | :fileSize="50" |
| | | buttonText="ç¹å»ä¸ä¼ éä»¶" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </FormDialog> |
| | |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·åå " :span="2">{{ currentSealDetail.reason }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <!-- éä»¶å表 --> |
| | | <div v-if="currentSealDetail.storageBlobVOList?.length || currentSealDetail.storageBlobDTOs?.length" class="attachment-section"> |
| | | <div class="attachment-title">éä»¶å表ï¼</div> |
| | | <el-table :data="currentSealDetail.storageBlobVOList || currentSealDetail.storageBlobDTOs" border class="attachment-table"> |
| | | <el-table-column label="éä»¶åç§°" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.originalFilename || scope.row.name || scope.row.fileName || 'æªå½åæä»¶' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="æä½" width="150" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="previewFile(scope.row)">é¢è§</el-button> |
| | | <el-button link type="primary" size="small" @click="downloadFile(scope.row)">ä¸è½½</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </FormDialog> |
| | | <!-- æä»¶é¢è§ç»ä»¶ --> |
| | | <FilePreview ref="filePreviewRef" /> |
| | | |
| | | </div> |
| | | </template> |
| | |
| | | import useUserStore from '@/store/modules/user' |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue' |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | import AttachmentUploadFile from '@/components/AttachmentUpload/file/index.vue' |
| | | import FilePreview from '@/components/filePreview/index.vue' |
| | | import download from '@/plugins/download.js' |
| | | |
| | | // ååºå¼æ°æ® |
| | | // ç¨å°ç³è¯·ç¸å
³ |
| | |
| | | const tableLoading = ref(false) |
| | | const showSealDetailDialog = ref(false) |
| | | const currentSealDetail = ref(null) |
| | | const filePreviewRef = ref(null) |
| | | const sealFormRef = ref() |
| | | const userList = ref([]) |
| | | const sealForm = reactive({ |
| | |
| | | reason: '', |
| | | approveUserId: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | status: 'pending', |
| | | storageBlobDTOs: [] |
| | | }) |
| | | |
| | | const sealRules = { |
| | |
| | | reason: '', |
| | | approveUserId: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | status: 'pending', |
| | | storageBlobDTOs: [] |
| | | }) |
| | | } |
| | | }).catch(err => { |
| | |
| | | reason: '', |
| | | approveUserId: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | status: 'pending', |
| | | storageBlobDTOs: [] |
| | | }) |
| | | // æ¸
é¤è¡¨åéªè¯ç¶æ |
| | | if (sealFormRef.value) { |
| | |
| | | const viewSealDetail = (row) => { |
| | | currentSealDetail.value = row |
| | | showSealDetailDialog.value = true |
| | | } |
| | | |
| | | // é¢è§æä»¶ |
| | | const previewFile = (row) => { |
| | | const url = row.previewURL || row.previewUrl || row.url |
| | | if (url && filePreviewRef.value) { |
| | | filePreviewRef.value.open(url) |
| | | } else { |
| | | ElMessage.warning('æä»¶å°åæ æï¼æ æ³é¢è§') |
| | | } |
| | | } |
| | | |
| | | // ä¸è½½æä»¶ |
| | | const downloadFile = (row) => { |
| | | const url = row.downloadURL || row.downloadUrl || row.url |
| | | if (url) { |
| | | const filename = row.originalFilename || row.name || row.fileName || 'download' |
| | | download.byUrl(url, filename) |
| | | } else { |
| | | ElMessage.warning('æä»¶å°åæ æï¼æ æ³ä¸è½½') |
| | | } |
| | | } |
| | | // 审æ¹ç¨å°ç³è¯· |
| | | const approveSeal = (row) => { |
| | |
| | | .ml-10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .attachment-section { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .attachment-title { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .attachment-table { |
| | | border-radius: 4px; |
| | | } |
| | | </style> |
| | |
| | | tableData.value = list |
| | | } |
| | | const downLoadFile = (row) => { |
| | | proxy.$download.name(row.url); |
| | | |
| | | proxy.$download.byUrl(row.url, row.originalFilename); |
| | | } |
| | | const lookFile = (row) => { |
| | | filePreviewRef.value.open(row.url) |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search-wrapper"> |
| | | <div class="search-wrapper mb20"> |
| | | <el-form |
| | | :model="searchForm" |
| | | class="demo-form-inline" |
| | |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | <FileListDialog |
| | | ref="fileListRef" |
| | | v-model="fileListDialogVisible" |
| | | title="å®åéä»¶" |
| | | :show-upload-button="true" |
| | | :show-delete-button="true" |
| | | :upload-method="handleFileUpload" |
| | | :delete-method="handleFileDelete" |
| | | /> |
| | | </div> |
| | | <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="after_sales_service" :record-id="recordId" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick, defineAsyncComponent} from "vue"; |
| | | import FormDia from "@/views/customerService/afterSalesHandling/components/formDia.vue"; |
| | | import FileListDialog from "@/components/Dialog/FileListDialog.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import request from "@/utils/request"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import { |
| | | afterSalesServiceListPage, |
| | | afterSalesServiceFileListPage, |
| | | afterSalesServiceFileDel, |
| | | } from "@/api/customerService/index.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore() |
| | | const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | |
| | | }) |
| | | } |
| | | |
| | | |
| | | // æå¼éä»¶å¼¹çª |
| | | const recordId =ref(0) |
| | | const fileDialogVisible = ref(false) |
| | | |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | const openFilesFormDia = async (row) => { |
| | | currentFileRow.value = row |
| | | try { |
| | | const res = await afterSalesServiceFileListPage({ |
| | | afterSalesServiceId: row.id, |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | if (res.code === 200 && fileListRef.value) { |
| | | const fileList = (res.data?.records || []).map((item) => ({ |
| | | name: item.name || item.fileName, |
| | | url: item.url || item.fileUrl, |
| | | id: item.id, |
| | | ...item, |
| | | })) |
| | | fileListRef.value.open(fileList) |
| | | fileListDialogVisible.value = true |
| | | } else { |
| | | fileListRef.value?.open([]) |
| | | fileListDialogVisible.value = true |
| | | } |
| | | } catch (error) { |
| | | proxy.$modal.msgError("è·åéä»¶å表失败") |
| | | fileListRef.value?.open([]) |
| | | fileListDialogVisible.value = true |
| | | } |
| | | } |
| | | |
| | | // ä¸ä¼ éä»¶ |
| | | const handleFileUpload = async () => { |
| | | if (!currentFileRow.value) { |
| | | proxy.$modal.msgWarning("请å
éæ©æ°æ®") |
| | | return |
| | | } |
| | | return new Promise((resolve) => { |
| | | const input = document.createElement("input") |
| | | input.type = "file" |
| | | input.style.display = "none" |
| | | input.onchange = async (e) => { |
| | | const file = e.target.files[0] |
| | | if (!file) { |
| | | resolve(null) |
| | | return |
| | | } |
| | | try { |
| | | const formData = new FormData() |
| | | formData.append("file", file) |
| | | formData.append("id", currentFileRow.value.id) |
| | | const uploadRes = await request({ |
| | | url: "/afterSalesService/file/upload", |
| | | method: "post", |
| | | data: formData, |
| | | headers: { |
| | | "Content-Type": "multipart/form-data", |
| | | Authorization: `Bearer ${getToken()}`, |
| | | }, |
| | | }) |
| | | if (uploadRes.code === 200) { |
| | | proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå") |
| | | // éæ°è·åæä»¶å表 |
| | | const listRes = await afterSalesServiceFileListPage({ |
| | | afterSalesServiceId: currentFileRow.value.id, |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | if (listRes.code === 200 && fileListRef.value) { |
| | | const fileList = (listRes.data?.records || []).map((item) => ({ |
| | | name: item.fileName, |
| | | url: item.fileUrl, |
| | | id: item.id, |
| | | ...item, |
| | | })) |
| | | fileListRef.value.setList(fileList) |
| | | } |
| | | resolve({ name: file.name, url: "", id: null }) |
| | | } else { |
| | | proxy.$modal.msgError(uploadRes.msg || "æä»¶ä¸ä¼ 失败") |
| | | resolve(null) |
| | | } |
| | | } catch (err) { |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败") |
| | | resolve(null) |
| | | } finally { |
| | | document.body.removeChild(input) |
| | | } |
| | | } |
| | | document.body.appendChild(input) |
| | | input.click() |
| | | }) |
| | | } |
| | | |
| | | // å é¤éä»¶ |
| | | const handleFileDelete = async (row) => { |
| | | try { |
| | | // æ·»å ç¡®è®¤å¯¹è¯æ¡ |
| | | const confirmResult = await ElMessageBox.confirm( |
| | | 'ç¡®å®è¦å é¤è¿ä¸ªéä»¶åï¼', |
| | | 'å é¤ç¡®è®¤', |
| | | { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | } |
| | | ) |
| | | |
| | | if (confirmResult === 'confirm') { |
| | | const res = await afterSalesServiceFileDel(row.id) |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("å 餿å") |
| | | if (currentFileRow.value && fileListRef.value) { |
| | | const listRes = await afterSalesServiceFileListPage({ |
| | | afterSalesServiceId: currentFileRow.value.id, |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | if (listRes.code === 200) { |
| | | const fileList = (listRes.data?.records || []).map((item) => ({ |
| | | name: item.fileName, |
| | | url: item.fileUrl, |
| | | id: item.id, |
| | | ...item, |
| | | })) |
| | | fileListRef.value.setList(fileList) |
| | | } |
| | | } |
| | | } else { |
| | | proxy.$modal.msgError(res.msg || "å é¤å¤±è´¥") |
| | | return false |
| | | } |
| | | } |
| | | } catch (error) { |
| | | // å¦æç¨æ·åæ¶å é¤ï¼ä¸æ¾ç¤ºéè¯¯ä¿¡æ¯ |
| | | if (error !== 'cancel') { |
| | | proxy.$modal.msgError("å é¤å¤±è´¥") |
| | | } |
| | | return false |
| | | } |
| | | recordId.value = row.id |
| | | fileDialogVisible.value = true |
| | | } |
| | | |
| | | // æ¥è¯¢å表 |
| | |
| | | v-model="form.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('productName')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | v-model="form.batchNumber" |
| | | placeholder="请è¾å
¥äº§åæ¹å·" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('batchNumber')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | type="date" |
| | | placeholder="è¯·éæ©ä¸´ææ¥æ" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('expiryDate')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | :min="0" |
| | | placeholder="请è¾å
¥åºåæ°é" |
| | | style="width: 100%" |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('stockQuantity')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | v-model="form.customerName" |
| | | placeholder="请è¾å
¥å®¢æ·åç§°" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('customerName')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | v-model="form.contactPhone" |
| | | placeholder="请è¾å
¥èç³»çµè¯" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('contactPhone')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | v-model="form.problemDesc" |
| | | placeholder="请è¾å
¥é®é¢æè¿°" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('problemDesc')" |
| | | type="textarea" |
| | | :rows="3" |
| | | /> |
| | |
| | | v-model="form.handlerId" |
| | | placeholder="è¯·éæ©å¤ç人" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('handlerId')" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | |
| | | type="date" |
| | | placeholder="è¯·éæ©å¤çæ¥æ" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('handleDate')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | v-model="form.handleResult" |
| | | placeholder="请è¾å
¥å¤çç»æ" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | :disabled="isFieldDisabled('handleResult')" |
| | | type="textarea" |
| | | :rows="3" |
| | | /> |
| | |
| | | return 'æ°å¢ä¸´æå®å'; |
| | | case 'edit': |
| | | return 'ç¼è¾ä¸´æå®å'; |
| | | case 'handle': |
| | | return 'å¤ç临æå®å'; |
| | | case 'view': |
| | | return 'æ¥ç临æå®å'; |
| | | default: |
| | |
| | | }) |
| | | const { form, rules } = toRefs(data); |
| | | const userList = ref([]) |
| | | const handleEditableFields = ["handlerId", "handleDate", "handleResult"]; |
| | | |
| | | const isFieldDisabled = (field) => { |
| | | if (operationType.value === "view") return true; |
| | | if (operationType.value === "handle") return !handleEditableFields.includes(field); |
| | | return false; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | |
| | | } else { |
| | | // ç¼è¾ææ¥çæ¶å¡«å
æ°æ® |
| | | form.value = { ...row }; |
| | | if (type === 'edit' && !form.value.handlerId) { |
| | | if (type === 'handle' && !form.value.handlerId) { |
| | | form.value.handlerId = userStore.id; |
| | | form.value.handleDate = getCurrentDate(); |
| | | } |
| | |
| | | } |
| | | |
| | | const submitForm = () => { |
| | | if (operationType.value === "handle") { |
| | | if (!form.value.handlerId || !form.value.handleDate || !form.value.handleResult) { |
| | | proxy.$modal.msgWarning("请填åå¤ç人ãå¤çæ¥æåå¤çç»æ"); |
| | | return; |
| | | } |
| | | handleSubmit(); |
| | | return; |
| | | } |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | const submitData = { |
| | | id: form.value.id, |
| | | productName: form.value.productName, |
| | | batchNumber: form.value.batchNumber, |
| | | expireDate: form.value.expiryDate, |
| | | stockQuantity: form.value.stockQuantity, |
| | | customerName: form.value.customerName, |
| | | contactPhone: form.value.contactPhone, |
| | | disRes: form.value.problemDesc, |
| | | status: form.value.status, |
| | | disposeUserId: form.value.handlerId, |
| | | disposeNickName: userList.value.find(item => item.userId === form.value.handlerId)?.nickName, |
| | | disposeResult: form.value.handleResult, |
| | | disDate: form.value.handleDate |
| | | }; |
| | | |
| | | const apiCall = operationType.value === 'add' ? expiryAfterSalesAdd : expiryAfterSalesUpdate; |
| | | apiCall(submitData).then(() => { |
| | | proxy.$modal.msgSuccess(operationType.value === 'add' ? "æ°å¢æå" : "æ´æ°æå"); |
| | | closeDia(); |
| | | }).catch(error => { |
| | | console.error('æäº¤æ°æ®å¤±è´¥:', error); |
| | | proxy.$modal.msgError('æäº¤æ°æ®å¤±è´¥ï¼è¯·ç¨åéè¯'); |
| | | }); |
| | | handleSubmit(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | const handleSubmit = () => { |
| | | const submitData = { |
| | | id: form.value.id, |
| | | productName: form.value.productName, |
| | | batchNumber: form.value.batchNumber, |
| | | expireDate: form.value.expiryDate, |
| | | stockQuantity: form.value.stockQuantity, |
| | | customerName: form.value.customerName, |
| | | contactPhone: form.value.contactPhone, |
| | | disRes: form.value.problemDesc, |
| | | status: operationType.value === "handle" ? 2 : form.value.status, |
| | | disposeUserId: form.value.handlerId, |
| | | disposeNickName: userList.value.find(item => item.userId === form.value.handlerId)?.nickName, |
| | | disposeResult: form.value.handleResult, |
| | | disDate: form.value.handleDate |
| | | }; |
| | | |
| | | const apiCall = operationType.value === 'add' ? expiryAfterSalesAdd : expiryAfterSalesUpdate; |
| | | apiCall(submitData).then(() => { |
| | | const successText = operationType.value === "add" ? "æ°å¢æå" : operationType.value === "handle" ? "å¤çæå" : "æ´æ°æå"; |
| | | proxy.$modal.msgSuccess(successText); |
| | | closeDia(); |
| | | }).catch(error => { |
| | | console.error('æäº¤æ°æ®å¤±è´¥:', error); |
| | | proxy.$modal.msgError('æäº¤æ°æ®å¤±è´¥ï¼è¯·ç¨åéè¯'); |
| | | }); |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">ä¸´ææ¥æï¼</span> |
| | | <el-date-picker |
| | |
| | | <el-button type="danger" @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | |
| | | |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="openForm('view', row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="openForm('edit', row)" v-if="row.status === 1">ç¼è¾</el-button> |
| | | <el-button type="primary" link @click="openForm('handle', row)" v-if="row.status === 1">å¤ç</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | |
| | | current: page.value.current, |
| | | size: page.value.size |
| | | }; |
| | | |
| | | |
| | | expiryAfterSalesListPage(queryParams).then(res => { |
| | | // æ å°å端è¿åæ°æ®å°åç«¯è¡¨æ ¼ |
| | | tableData.value = res.data.records.map(item => ({ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | title="æ°å¢å®åå" |
| | | width="90%" |
| | | @close="closeDia" |
| | | > |
| | | <el-dialog v-model="dialogFormVisible" |
| | | title="æ°å¢å®åå" |
| | | width="90%" |
| | | @close="closeDia"> |
| | | <div> |
| | | <span class="descriptions">åºç¡èµæ</span> |
| | | <el-form |
| | | :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="客æ·åç§°ï¼" prop="customerName"> |
| | | <el-select |
| | | v-model="form.customerName" |
| | | filterable |
| | | @change="customerNameChange" |
| | | > |
| | | <el-option |
| | | v-for="item in customerNameOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | <el-form-item label="客æ·åç§°ï¼" |
| | | prop="customerName"> |
| | | <el-select v-model="form.customerName" |
| | | filterable |
| | | @change="customerNameChange"> |
| | | <el-option v-for="item in customerNameOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="å®åç±»åï¼" prop="serviceType"> |
| | | <el-select |
| | | v-model="form.serviceType" |
| | | filterable |
| | | > |
| | | <el-option |
| | | v-for="dict in serviceTypeOptions" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="dict.value" |
| | | /> |
| | | <el-form-item label="å®åç±»åï¼" |
| | | prop="serviceType"> |
| | | <el-select v-model="form.serviceType" |
| | | filterable> |
| | | <el-option v-for="dict in serviceTypeOptions" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="dict.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="å
³èéå®åå·ï¼" prop="salesContractNo"> |
| | | <el-select |
| | | v-model="form.salesContractNo" |
| | | @change="associatedSalesOrderNumberChange" |
| | | filterable |
| | | > |
| | | <el-option |
| | | v-for="item in associatedSalesOrderNumberOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | <el-form-item label="å
³èéå®åå·ï¼" |
| | | prop="salesContractNo"> |
| | | <el-select v-model="form.salesContractNo" |
| | | @change="associatedSalesOrderNumberChange" |
| | | filterable> |
| | | <el-option v-for="item in associatedSalesOrderNumberOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="ç´§æ¥ç¨åº¦ï¼" prop="urgency"> |
| | | <el-select |
| | | v-model="form.urgency" |
| | | filterable |
| | | > |
| | | <el-option |
| | | v-for="dict in urgencyOptions" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="dict.value" |
| | | /> |
| | | <el-form-item label="ç´§æ¥ç¨åº¦ï¼" |
| | | prop="urgency"> |
| | | <el-select v-model="form.urgency" |
| | | filterable> |
| | | <el-option v-for="dict in urgencyOptions" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="dict.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="é®é¢æè¿°ï¼" prop="proDesc"> |
| | | <el-input |
| | | v-model="form.proDesc" |
| | | placeholder="请è¾å
¥é®é¢æè¿°" |
| | | /> |
| | | <el-form-item label="é®é¢æè¿°ï¼" |
| | | prop="proDesc"> |
| | | <el-input v-model="form.proDesc" |
| | | placeholder="请è¾å
¥é®é¢æè¿°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <hr> |
| | | <div style="padding-top: 20px"> |
| | | <div style="display: flex; justify-content: space-between"> |
| | | <span class="descriptions">å
³è产å</span> |
| | | <el-button |
| | | type="primary" |
| | | style="margin-right: 12px; margin-bottom: 10px" |
| | | @click="isShowProductSelectDialog = true" |
| | | > |
| | | <div style="padding-top: 20px"> |
| | | <div style="display: flex; justify-content: space-between"> |
| | | <span class="descriptions">å
³è产å</span> |
| | | <el-button type="primary" |
| | | style="margin-right: 12px; margin-bottom: 10px" |
| | | @click="isShowProductSelectDialog = true"> |
| | | éæ©äº§å |
| | | </el-button> |
| | | </div> |
| | | <PIMTable |
| | | :isShowPagination="false" |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | > |
| | | <template #approveStatus="{ row }"> |
| | | <el-tag :type="getApproveStatusType(row)" size="small"> |
| | | {{ getApproveStatusText(row) }} |
| | | </el-tag> |
| | | </template> |
| | | <template #shippingStatus="{ row }"> |
| | | <el-tag :type="getShippingStatusType(row)" size="small"> |
| | | {{ getShippingStatusText(row) }} |
| | | </el-tag> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <PIMTable :isShowPagination="false" |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData"> |
| | | <template #approveStatus="{ row }"> |
| | | <el-tag :type="getApproveStatusType(row)" |
| | | size="small"> |
| | | {{ getApproveStatusText(row) }} |
| | | </el-tag> |
| | | </template> |
| | | <template #shippingStatus="{ row }"> |
| | | <el-tag :type="getShippingStatusType(row)" |
| | | size="small"> |
| | | {{ getShippingStatusText(row) }} |
| | | </el-tag> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- éæ©äº§åå¼¹çª --> |
| | | <ProductSelectDialog |
| | | v-model="isShowProductSelectDialog" |
| | | :products="currentSalesOrderProducts" |
| | | :selected-ids="currentSelectedProductIds" |
| | | @confirm="handleSelectProducts" |
| | | /> |
| | | <ProductSelectDialog v-model="isShowProductSelectDialog" |
| | | :products="currentSalesOrderProducts" |
| | | :selected-ids="currentSelectedProductIds" |
| | | @confirm="handleSelectProducts" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue"; |
| | | import ProductSelectDialog from "./ProductSelectDialog.vue"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | import {afterSalesServiceAdd, afterSalesServiceUpdate, getAllCustomerList, getSalesLedger } from "@/api/customerService/index.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const formRef = ref(null) |
| | | const customerNameOptions = ref([]) |
| | | const userStore = useUserStore(); |
| | | import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue"; |
| | | import ProductSelectDialog from "./ProductSelectDialog.vue"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { |
| | | afterSalesServiceAdd, |
| | | afterSalesServiceUpdate, |
| | | getAllCustomerList, |
| | | getSalesLedger, |
| | | } from "@/api/customerService/index.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const formRef = ref(null); |
| | | const customerNameOptions = ref([]); |
| | | const userStore = useUserStore(); |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | topic: "", |
| | | serviceType: "", |
| | | urgency: "", |
| | | salesLedgerId: null, |
| | | productModelIds: "", |
| | | customerId: null, |
| | | salesContractNo: "", |
| | | proDesc: "", |
| | | customerName: "" |
| | | }, |
| | | rules: { |
| | | customerName: [{required: true, message: "è¯·éæ©å®¢æ·åç§°", trigger: "change"}], |
| | | serviceType: [{required: true, message: "è¯·éæ©å®åç±»å", trigger: "change"}], |
| | | urgency: [{required: true, message: "è¯·éæ©ç´§æ¥ç¨åº¦", trigger: "change"}], |
| | | feedbackDate: [{required: true, message: "è¯·éæ©", trigger: "change"}], |
| | | } |
| | | }) |
| | | |
| | | // èªå®ä¹æ ¡éªå½æ°ï¼å¤ææ¯å¦éè¦æ ¡éªå®åç¼å· |
| | | |
| | | const { form, rules } = toRefs(data); |
| | | const userList = ref([]) |
| | | |
| | | const formatCurrency = (val) => { |
| | | if (val === null || val === undefined || val === '') return '-' |
| | | const num = Number(val) |
| | | return Number.isFinite(num) ? num.toFixed(2) : '-' |
| | | } |
| | | |
| | | const { post_sale_waiting_list, degree_of_urgency } = proxy.useDict( |
| | | "post_sale_waiting_list", |
| | | "degree_of_urgency" |
| | | ); |
| | | |
| | | const serviceTypeOptions = computed(() => post_sale_waiting_list?.value || []); |
| | | const urgencyOptions = computed(() => degree_of_urgency?.value || []); |
| | | |
| | | const getProductRowId = (row) => { |
| | | return row?.id ?? row?.productModelId ?? row?.modelId ?? `${row?.productCategory || row?.productName || ""}-${row?.specificationModel || row?.model || ""}-${row?.unit || ""}` |
| | | } |
| | | |
| | | const normalizeProductRow = (row) => { |
| | | return { |
| | | ...row, |
| | | id: getProductRowId(row), |
| | | productCategory: row?.productCategory ?? row?.productName ?? '', |
| | | specificationModel: row?.specificationModel ?? row?.model ?? '', |
| | | unit: row?.unit ?? '', |
| | | approveStatus: row?.approveStatus ?? null, |
| | | shippingStatus: row?.shippingStatus ?? '', |
| | | expressCompany: row?.expressCompany ?? '', |
| | | expressNumber: row?.expressNumber ?? '', |
| | | shippingCarNumber: row?.shippingCarNumber ?? '', |
| | | shippingDate: row?.shippingDate ?? '', |
| | | quantity: row?.quantity ?? 0, |
| | | taxRate: row?.taxRate ?? 0, |
| | | taxInclusiveUnitPrice: row?.taxInclusiveUnitPrice ?? 0, |
| | | taxInclusiveTotalPrice: row?.taxInclusiveTotalPrice ?? 0, |
| | | taxExclusiveTotalPrice: row?.taxExclusiveTotalPrice ?? 0, |
| | | } |
| | | } |
| | | |
| | | const tableColumn = ref([ |
| | | { label: "产å大类", prop: "productCategory" }, |
| | | { label: "尺寸", prop: "specificationModel" }, |
| | | { label: "åä½", prop: "unit" }, |
| | | { |
| | | label: "产åç¶æ", |
| | | prop: "approveStatus", |
| | | width: 100, |
| | | align: "center", |
| | | dataType: "slot", |
| | | slot: "approveStatus", |
| | | }, |
| | | { |
| | | label: "åè´§ç¶æ", |
| | | align: "center", |
| | | width: 140, |
| | | dataType: "slot", |
| | | slot: "shippingStatus", |
| | | }, |
| | | { label: "å¿«éå
¬å¸", prop: "expressCompany", width: 140 }, |
| | | { label: "å¿«éåå·", prop: "expressNumber", width: 160 }, |
| | | { label: "å货车ç", prop: "shippingCarNumber", minWidth: 100, align: "center" }, |
| | | { label: "åè´§æ¥æ", prop: "shippingDate", minWidth: 100, align: "center" }, |
| | | { label: "æ°é", prop: "quantity", width: 100 }, |
| | | { label: "ç¨ç(%)", prop: "taxRate", width: 100 }, |
| | | { |
| | | label: "å«ç¨åä»·(å
)", |
| | | prop: "taxInclusiveUnitPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | label: "å«ç¨æ»ä»·(å
)", |
| | | prop: "taxInclusiveTotalPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | label: "ä¸å«ç¨æ»ä»·(å
)", |
| | | prop: "taxExclusiveTotalPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | operation: [ |
| | | { |
| | | name: "å é¤", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | tableData.value = tableData.value.filter(i => getProductRowId(i) !== getProductRowId(row)) |
| | | }, |
| | | |
| | | }, |
| | | ], |
| | | }, |
| | | ]) |
| | | const tableData = ref([]) |
| | | // éæ©äº§åå¼¹çª |
| | | const isShowProductSelectDialog = ref(false) |
| | | const handleSelectProducts = (rows) => { |
| | | if (!Array.isArray(rows)) return |
| | | const existingIds = new Set(tableData.value.map(i => String(getProductRowId(i)))) |
| | | const mapped = rows |
| | | .map(normalizeProductRow) |
| | | .filter(r => !existingIds.has(String(getProductRowId(r)))) |
| | | tableData.value = tableData.value.concat(mapped) |
| | | } |
| | | const currentSelectedProductIds = computed(() => { |
| | | return tableData.value.map(item => getProductRowId(item)).filter(item => item !== undefined && item !== null && item !== '') |
| | | }) |
| | | |
| | | const associatedSalesOrderNumberChange = () => { |
| | | const opt = associatedSalesOrderNumberOptions.value.find( |
| | | (item) => item.value === form.value.salesContractNo |
| | | ) |
| | | tableData.value = (opt?.productData || []).map(normalizeProductRow) |
| | | form.value.salesLedgerId = opt?.id || null |
| | | } |
| | | |
| | | const associatedSalesOrderNumberOptions = ref([]) |
| | | |
| | | const currentSalesOrderProducts = computed(() => { |
| | | const opt = associatedSalesOrderNumberOptions.value.find( |
| | | (item) => item.value === form.value.salesContractNo |
| | | ) |
| | | return (opt?.productData || []).map(normalizeProductRow) |
| | | }) |
| | | |
| | | const customerNameChange = (val) => { |
| | | form.value.salesContractNo = ""; |
| | | form.value.salesLedgerId = null; |
| | | tableData.value = []; |
| | | associatedSalesOrderNumberOptions.value = []; |
| | | const opt = customerNameOptions.value.find(item => item.value === val); |
| | | if (opt) { |
| | | form.value.customerId = opt.id; |
| | | } else { |
| | | form.value.customerId = null; |
| | | } |
| | | getSalesLedger({ |
| | | customerName: form.value.customerName |
| | | }).then(res => { |
| | | if(res.code === 200){ |
| | | associatedSalesOrderNumberOptions.value = res.data.records.map(item => ({ |
| | | label: item.salesContractNo, |
| | | value: item.salesContractNo, |
| | | productData:item.productData, |
| | | id: item.id |
| | | })) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const getApproveStatusText = (row) => { |
| | | if (!row) return 'ä¸è¶³' |
| | | if (row.approveStatus === 1 && (!row.shippingDate || !row.shippingCarNumber)) { |
| | | return 'å
è¶³' |
| | | } |
| | | if (row.approveStatus === 0 && (row.shippingDate || row.shippingCarNumber)) { |
| | | return 'å·²åºåº' |
| | | } |
| | | return 'ä¸è¶³' |
| | | } |
| | | |
| | | const getApproveStatusType = (row) => { |
| | | const statusText = getApproveStatusText(row) |
| | | return statusText === 'ä¸è¶³' ? 'danger' : 'success' |
| | | } |
| | | |
| | | const getShippingStatusText = (row) => { |
| | | if (!row) return 'å¾
åè´§' |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return 'å·²åè´§' |
| | | } |
| | | const status = row.shippingStatus |
| | | if (status === null || status === undefined || status === '') { |
| | | return 'å¾
åè´§' |
| | | } |
| | | const map = { |
| | | 'å¾
åè´§': 'å¾
åè´§', |
| | | 'å¾
å®¡æ ¸': 'å¾
å®¡æ ¸', |
| | | 'å®¡æ ¸ä¸': 'å®¡æ ¸ä¸', |
| | | 'å®¡æ ¸æç»': 'å®¡æ ¸æç»', |
| | | 'å®¡æ ¸éè¿': 'å®¡æ ¸éè¿', |
| | | 'å·²åè´§': 'å·²åè´§' |
| | | } |
| | | return map[String(status).trim()] || 'å¾
åè´§' |
| | | } |
| | | |
| | | const getShippingStatusType = (row) => { |
| | | if (!row) return 'info' |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return 'success' |
| | | } |
| | | const status = row.shippingStatus |
| | | if (status === null || status === undefined || status === '') { |
| | | return 'info' |
| | | } |
| | | const map = { |
| | | 'å¾
åè´§': 'info', |
| | | 'å¾
å®¡æ ¸': 'warning', |
| | | 'å®¡æ ¸ä¸': 'warning', |
| | | 'å®¡æ ¸æç»': 'danger', |
| | | 'å®¡æ ¸éè¿': 'success', |
| | | 'å·²åè´§': 'success' |
| | | } |
| | | return map[String(status).trim()] || 'info' |
| | | } |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog =async (type, row) => { |
| | | // 请æ±å¤ä¸ªæ¥å£ï¼è·åæ°æ® |
| | | let res = await getAllCustomerList({ |
| | | current: 1, |
| | | size: 1000, |
| | | total: 0, |
| | | const data = reactive({ |
| | | form: { |
| | | topic: "", |
| | | serviceType: "", |
| | | urgency: "", |
| | | salesLedgerId: null, |
| | | productModelIds: "", |
| | | customerId: null, |
| | | salesContractNo: "", |
| | | proDesc: "", |
| | | customerName: "", |
| | | }, |
| | | rules: { |
| | | customerName: [ |
| | | { required: true, message: "è¯·éæ©å®¢æ·åç§°", trigger: "change" }, |
| | | ], |
| | | serviceType: [ |
| | | { required: true, message: "è¯·éæ©å®åç±»å", trigger: "change" }, |
| | | ], |
| | | urgency: [{ required: true, message: "è¯·éæ©ç´§æ¥ç¨åº¦", trigger: "change" }], |
| | | feedbackDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | if(res.records){ |
| | | customerNameOptions.value = res.records.map(item => ({ |
| | | label: item.customerName, |
| | | value: item.customerName, |
| | | id: item.id |
| | | })); |
| | | } |
| | | |
| | | // èªå®ä¹æ ¡éªå½æ°ï¼å¤ææ¯å¦éè¦æ ¡éªå®åç¼å· |
| | | |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | form.value = {} |
| | | proxy.resetForm("formRef"); |
| | | form.value.checkUserId = userStore.id; |
| | | form.value.feedbackDate = getCurrentDate(); |
| | | // æ°å¢æ¶æ¸
空已éå
³è产å |
| | | if (type === "add") { |
| | | tableData.value = [] |
| | | } |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (type === "edit") { |
| | | form.value = {...row} |
| | | if (form.value.customerName) { |
| | | const res = await getSalesLedger({ customerName: form.value.customerName }) |
| | | if (res?.code === 200) { |
| | | console.log(res) |
| | | associatedSalesOrderNumberOptions.value = (res.data?.records || []).map(item => ({ |
| | | const { form, rules } = toRefs(data); |
| | | const userList = ref([]); |
| | | |
| | | const formatCurrency = val => { |
| | | if (val === null || val === undefined || val === "") return "-"; |
| | | const num = Number(val); |
| | | return Number.isFinite(num) ? num.toFixed(2) : "-"; |
| | | }; |
| | | |
| | | const { post_sale_waiting_list, degree_of_urgency } = proxy.useDict( |
| | | "post_sale_waiting_list", |
| | | "degree_of_urgency" |
| | | ); |
| | | |
| | | const serviceTypeOptions = computed(() => post_sale_waiting_list?.value || []); |
| | | const urgencyOptions = computed(() => degree_of_urgency?.value || []); |
| | | |
| | | const getProductRowId = row => { |
| | | return ( |
| | | row?.id ?? |
| | | row?.productModelId ?? |
| | | row?.modelId ?? |
| | | `${row?.productCategory || row?.productName || ""}-${ |
| | | row?.specificationModel || row?.model || "" |
| | | }-${row?.unit || ""}` |
| | | ); |
| | | }; |
| | | |
| | | const normalizeProductRow = row => { |
| | | return { |
| | | ...row, |
| | | id: getProductRowId(row), |
| | | productCategory: row?.productCategory ?? row?.productName ?? "", |
| | | specificationModel: row?.specificationModel ?? row?.model ?? "", |
| | | unit: row?.unit ?? "", |
| | | approveStatus: row?.approveStatus ?? null, |
| | | shippingStatus: row?.shippingStatus ?? "", |
| | | expressCompany: row?.expressCompany ?? "", |
| | | expressNumber: row?.expressNumber ?? "", |
| | | shippingCarNumber: row?.shippingCarNumber ?? "", |
| | | shippingDate: row?.shippingDate ?? "", |
| | | quantity: row?.quantity ?? 0, |
| | | taxRate: row?.taxRate ?? 0, |
| | | taxInclusiveUnitPrice: row?.taxInclusiveUnitPrice ?? 0, |
| | | taxInclusiveTotalPrice: row?.taxInclusiveTotalPrice ?? 0, |
| | | taxExclusiveTotalPrice: row?.taxExclusiveTotalPrice ?? 0, |
| | | noQuantity: row?.noQuantity ?? 0, |
| | | }; |
| | | }; |
| | | |
| | | const tableColumn = ref([ |
| | | { label: "产å大类", prop: "productCategory" }, |
| | | { label: "è§æ ¼åå·", prop: "specificationModel" }, |
| | | { label: "åä½", prop: "unit" }, |
| | | { |
| | | label: "产åç¶æ", |
| | | prop: "approveStatus", |
| | | width: 100, |
| | | align: "center", |
| | | dataType: "slot", |
| | | slot: "approveStatus", |
| | | }, |
| | | { |
| | | label: "åè´§ç¶æ", |
| | | align: "center", |
| | | width: 140, |
| | | dataType: "slot", |
| | | slot: "shippingStatus", |
| | | }, |
| | | { label: "å¿«éå
¬å¸", prop: "expressCompany", width: 140 }, |
| | | { label: "å¿«éåå·", prop: "expressNumber", width: 160 }, |
| | | { |
| | | label: "å货车ç", |
| | | prop: "shippingCarNumber", |
| | | minWidth: 100, |
| | | align: "center", |
| | | }, |
| | | { label: "åè´§æ¥æ", prop: "shippingDate", minWidth: 100, align: "center" }, |
| | | { label: "æ°é", prop: "quantity", width: 100 }, |
| | | { label: "ç¨ç(%)", prop: "taxRate", width: 100 }, |
| | | { |
| | | label: "å«ç¨åä»·(å
)", |
| | | prop: "taxInclusiveUnitPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | label: "å«ç¨æ»ä»·(å
)", |
| | | prop: "taxInclusiveTotalPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | label: "ä¸å«ç¨æ»ä»·(å
)", |
| | | prop: "taxExclusiveTotalPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | operation: [ |
| | | { |
| | | name: "å é¤", |
| | | type: "text", |
| | | clickFun: row => { |
| | | tableData.value = tableData.value.filter( |
| | | i => getProductRowId(i) !== getProductRowId(row) |
| | | ); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | // éæ©äº§åå¼¹çª |
| | | const isShowProductSelectDialog = ref(false); |
| | | const handleSelectProducts = rows => { |
| | | if (!Array.isArray(rows)) return; |
| | | const existingIds = new Set( |
| | | tableData.value.map(i => String(getProductRowId(i))) |
| | | ); |
| | | const mapped = rows |
| | | .map(normalizeProductRow) |
| | | .filter(r => !existingIds.has(String(getProductRowId(r)))); |
| | | tableData.value = tableData.value.concat(mapped); |
| | | }; |
| | | const currentSelectedProductIds = computed(() => { |
| | | return tableData.value |
| | | .map(item => getProductRowId(item)) |
| | | .filter(item => item !== undefined && item !== null && item !== ""); |
| | | }); |
| | | |
| | | const associatedSalesOrderNumberChange = () => { |
| | | const opt = associatedSalesOrderNumberOptions.value.find( |
| | | item => item.value === form.value.salesContractNo |
| | | ); |
| | | tableData.value = (opt?.productData || []).map(normalizeProductRow); |
| | | form.value.salesLedgerId = opt?.id || null; |
| | | }; |
| | | |
| | | const associatedSalesOrderNumberOptions = ref([]); |
| | | |
| | | const currentSalesOrderProducts = computed(() => { |
| | | const opt = associatedSalesOrderNumberOptions.value.find( |
| | | item => item.value === form.value.salesContractNo |
| | | ); |
| | | return (opt?.productData || []).map(normalizeProductRow); |
| | | }); |
| | | |
| | | const customerNameChange = val => { |
| | | form.value.salesContractNo = ""; |
| | | form.value.salesLedgerId = null; |
| | | tableData.value = []; |
| | | associatedSalesOrderNumberOptions.value = []; |
| | | const opt = customerNameOptions.value.find(item => item.value === val); |
| | | if (opt) { |
| | | form.value.customerId = opt.id; |
| | | } else { |
| | | form.value.customerId = null; |
| | | } |
| | | getSalesLedger({ |
| | | customerName: form.value.customerName, |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | associatedSalesOrderNumberOptions.value = res.data.records.map(item => ({ |
| | | label: item.salesContractNo, |
| | | value: item.salesContractNo, |
| | | productData: item.productData, |
| | | id: item.id |
| | | })) |
| | | id: item.id, |
| | | })); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const getApproveStatusText = row => { |
| | | if (!row) return "ä¸è¶³"; |
| | | if ( |
| | | row.approveStatus === 1 && |
| | | (!row.shippingDate || !row.shippingCarNumber) |
| | | ) { |
| | | return "å
è¶³"; |
| | | } |
| | | console.log(form.value) |
| | | } |
| | | } |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | // å¹é
产ååå·IDs |
| | | form.value.productModelIds = tableData.value.map(item => item.id).join(",") |
| | | if (operationType.value === "add") { |
| | | afterSalesServiceAdd(form.value).then(response => { |
| | | proxy.$modal.msgSuccess("æ°å¢æå") |
| | | closeDia() |
| | | }) |
| | | } else { |
| | | afterSalesServiceUpdate(form.value).then(response => { |
| | | proxy.$modal.msgSuccess("ä¿®æ¹æå") |
| | | closeDia() |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | if (row.approveStatus === 0 && (row.shippingDate || row.shippingCarNumber)) { |
| | | return "å·²åºåº"; |
| | | } |
| | | return "ä¸è¶³"; |
| | | }; |
| | | |
| | | const getApproveStatusType = row => { |
| | | const statusText = getApproveStatusText(row); |
| | | return statusText === "ä¸è¶³" ? "danger" : "success"; |
| | | }; |
| | | |
| | | const getShippingStatusText = row => { |
| | | if (!row) return "å¾
åè´§"; |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return "å·²åè´§"; |
| | | } |
| | | const status = row.shippingStatus; |
| | | if (status === null || status === undefined || status === "") { |
| | | return "å¾
åè´§"; |
| | | } |
| | | const map = { |
| | | å¾
åè´§: "å¾
åè´§", |
| | | å¾
å®¡æ ¸: "å¾
å®¡æ ¸", |
| | | å®¡æ ¸ä¸: "å®¡æ ¸ä¸", |
| | | å®¡æ ¸æç»: "å®¡æ ¸æç»", |
| | | å®¡æ ¸éè¿: "å®¡æ ¸éè¿", |
| | | å·²åè´§: "å·²åè´§", |
| | | }; |
| | | return map[String(status).trim()] || "å¾
åè´§"; |
| | | }; |
| | | |
| | | const getShippingStatusType = row => { |
| | | if (!row) return "info"; |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return "success"; |
| | | } |
| | | const status = row.shippingStatus; |
| | | if (status === null || status === undefined || status === "") { |
| | | return "info"; |
| | | } |
| | | const map = { |
| | | å¾
åè´§: "info", |
| | | å¾
å®¡æ ¸: "warning", |
| | | å®¡æ ¸ä¸: "warning", |
| | | å®¡æ ¸æç»: "danger", |
| | | å®¡æ ¸éè¿: "success", |
| | | å·²åè´§: "success", |
| | | }; |
| | | return map[String(status).trim()] || "info"; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async (type, row) => { |
| | | // 请æ±å¤ä¸ªæ¥å£ï¼è·åæ°æ® |
| | | let res = await getAllCustomerList({ |
| | | current: 1, |
| | | size: 1000, |
| | | total: 0, |
| | | }); |
| | | console.log(res, "res"); |
| | | |
| | | if (res.data.records) { |
| | | customerNameOptions.value = res.data.records.map(item => ({ |
| | | label: item.customerName, |
| | | value: item.customerName, |
| | | id: item.id, |
| | | })); |
| | | } else { |
| | | } |
| | | |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | form.value = {}; |
| | | proxy.resetForm("formRef"); |
| | | form.value.checkUserId = userStore.id; |
| | | form.value.feedbackDate = getCurrentDate(); |
| | | // æ°å¢æ¶æ¸
空已éå
³è产å |
| | | if (type === "add") { |
| | | tableData.value = []; |
| | | } |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (type === "edit") { |
| | | form.value = { ...row }; |
| | | if (form.value.customerName) { |
| | | const res = await getSalesLedger({ |
| | | customerName: form.value.customerName, |
| | | }); |
| | | if (res?.code === 200) { |
| | | console.log(res); |
| | | associatedSalesOrderNumberOptions.value = (res.data?.records || []).map( |
| | | item => ({ |
| | | label: item.salesContractNo, |
| | | value: item.salesContractNo, |
| | | productData: item.productData, |
| | | id: item.id, |
| | | }) |
| | | ); |
| | | } |
| | | } |
| | | console.log(form.value); |
| | | } |
| | | }; |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | // å¹é
产ååå·IDs |
| | | form.value.productModelIds = tableData.value |
| | | .map(item => item.id) |
| | | .join(","); |
| | | if (operationType.value === "add") { |
| | | afterSalesServiceAdd(form.value).then(response => { |
| | | proxy.$modal.msgSuccess("æ°å¢æå"); |
| | | closeDia(); |
| | | }); |
| | | } else { |
| | | afterSalesServiceUpdate(form.value).then(response => { |
| | | proxy.$modal.msgSuccess("ä¿®æ¹æå"); |
| | | closeDia(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .descriptions { |
| | | margin-bottom: 20px; |
| | | display: inline-block; |
| | | font-size: 1rem; |
| | | font-weight: 600; |
| | | padding-left: 12px; |
| | | position: relative; |
| | | } |
| | | .descriptions { |
| | | margin-bottom: 20px; |
| | | display: inline-block; |
| | | font-size: 1rem; |
| | | font-weight: 600; |
| | | padding-left: 12px; |
| | | position: relative; |
| | | } |
| | | |
| | | .descriptions::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 1rem; |
| | | background-color: #002FA7; /* Element é»è®¤çº¢è² */ |
| | | border-radius: 2px; |
| | | } |
| | | .descriptions::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 1rem; |
| | | background-color: #002fa7; /* Element é»è®¤çº¢è² */ |
| | | border-radius: 2px; |
| | | } |
| | | </style> |
| | |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="visible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleSubmit">ç¡®å®</el-button> |
| | | <el-button @click="visible = false">åæ¶</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">æ£å®æ¥æï¼</span> |
| | | <el-date-picker |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="showRegisterDialog = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitDefectForm">ç¡®å®</el-button> |
| | | <el-button @click="showRegisterDialog = false">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å·¡æ£é¡¹ç®" prop="inspectionProject"> |
| | | <el-input v-model="form.inspectionProject" placeholder="请è¾å
¥å·¡æ£é¡¹ç®" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¯å¦å¯ç¨" prop="isEnabled"> |
| | | <el-radio-group v-model="form.isEnabled"> |
| | | <el-radio :value="1">æ¯</el-radio> |
| | | <el-radio :value="0">å¦</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="夿³¨" prop="remarks"> |
| | | <el-input v-model="form.remarks" placeholder="请è¾å
¥å¤æ³¨" type="textarea" /> |
| | | </el-form-item> |
| | |
| | | taskName: undefined, |
| | | inspector: '', |
| | | inspectorIds: '', |
| | | inspectionProject: '', |
| | | isEnabled: 1, |
| | | remarks: '', |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | |
| | | taskName: undefined, |
| | | inspector: '', |
| | | inspectorIds: '', |
| | | inspectionProject: '', |
| | | isEnabled: 1, |
| | | remarks: '', |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | |
| | | const currentMediaIndex = ref(0); |
| | | const mediaList = ref([]); // åå¨å½åè¦æ¥ççåªä½å表ï¼å«å¾çåè§é¢å¯¹è±¡ï¼ |
| | | const mediaType = ref('image'); // image | video |
| | | const javaApi = proxy.javaApi; |
| | | |
| | | // å¤ç URLï¼å° Windows è·¯å¾è½¬æ¢ä¸ºå¯è®¿é®ç URL |
| | | function processFileUrl(fileUrl) { |
| | | if (!fileUrl) return ''; |
| | | |
| | | // 妿 URL æ¯ Windows è·¯å¾æ ¼å¼ï¼å
å«åææ ï¼ï¼éè¦è½¬æ¢ |
| | | if (fileUrl && fileUrl.indexOf('\\') > -1) { |
| | | // æ¥æ¾ uploads å
³é®åçä½ç½®ï¼ä»é£éå¼å§æåç¸å¯¹è·¯å¾ |
| | | const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads'); |
| | | if (uploadsIndex > -1) { |
| | | // ä» uploads å¼å§æåè·¯å¾ï¼å¹¶å°åææ æ¿æ¢ä¸ºæ£ææ |
| | | const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/'); |
| | | fileUrl = '/' + relativePath; |
| | | } else { |
| | | // å¦ææ²¡ææ¾å° uploadsï¼æåæåä¸ä¸ªç®å½åæä»¶å |
| | | const parts = fileUrl.split('\\'); |
| | | const fileName = parts[parts.length - 1]; |
| | | fileUrl = '/uploads/' + fileName; |
| | | } |
| | | } |
| | | |
| | | // ç¡®ä¿ææé http å¼å¤´ç URL 齿¼æ¥ baseUrl |
| | | if (fileUrl && !fileUrl.startsWith('http')) { |
| | | // ç¡®ä¿è·¯å¾ä»¥ / å¼å¤´ |
| | | if (!fileUrl.startsWith('/')) { |
| | | fileUrl = '/' + fileUrl; |
| | | } |
| | | // æ¼æ¥ baseUrl |
| | | fileUrl = javaApi + fileUrl; |
| | | } |
| | | |
| | | return fileUrl; |
| | | } |
| | | |
| | | // å¤çæ¯ä¸ç±»æ°æ®ï¼å离å¾çåè§é¢ |
| | | function processItems(items) { |
| | |
| | | } |
| | | |
| | | items.forEach(item => { |
| | | if (!item || !item.url) return; |
| | | if (!item || !item.previewURL || !item.contentType) return; |
| | | |
| | | |
| | | // å¤çæä»¶ URL |
| | | const fileUrl = processFileUrl(item.url); |
| | | |
| | | // æ ¹æ®æä»¶æ©å±å夿æ¯å¾çè¿æ¯è§é¢ |
| | | const urlLower = fileUrl.toLowerCase(); |
| | | if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) { |
| | | const fileUrl = item.previewURL; |
| | | const contentType = String(item.contentType).toLowerCase(); |
| | | |
| | | // æ ¹æ® contentType 夿æ¯å¾çè¿æ¯è§é¢ |
| | | if (contentType.startsWith('image/')) { |
| | | images.push(fileUrl); |
| | | } else if (urlLower.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/)) { |
| | | } else if (contentType.startsWith('video/')) { |
| | | videos.push(fileUrl); |
| | | } else if (item.contentType) { |
| | | // 妿æ contentTypeï¼ä½¿ç¨ contentType 夿 |
| | | if (item.contentType.startsWith('image/')) { |
| | | images.push(fileUrl); |
| | | } else if (item.contentType.startsWith('video/')) { |
| | | videos.push(fileUrl); |
| | | } |
| | | } |
| | | }); |
| | | |
| | |
| | | // æå¼å¼¹çªå¹¶å è½½æ°æ® |
| | | const openDialog = async (row) => { |
| | | // ä½¿ç¨æ£ç¡®çåæ®µåï¼commonFileListBefore, commonFileListAfter |
| | | // productionIssues å¯è½ä¸åå¨ï¼ä½¿ç¨ç©ºæ°ç» |
| | | const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBefore || []); |
| | | const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfter || []); |
| | | const { images: issueImgs, videos: issueVids } = processItems(row.productionIssues || []); |
| | | const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBeforeVO || []); |
| | | const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfterVO || []); |
| | | const { images: issueImgs, videos: issueVids } = processItems(row.commonFileListVO || []); |
| | | |
| | | beforeProductionImgs.value = beforeImgs; |
| | | beforeProductionVideos.value = beforeVids; |
| | |
| | | class="no-data">--</span> |
| | | </div> |
| | | </template> |
| | | <template #isEnabled="{ row }"> |
| | | <el-tag :type="row.isEnabled === 1 ? 'success' : 'danger'" size="small"> |
| | | {{ row.isEnabled == 1 ? 'æ¯' : 'å¦' }} |
| | | </el-tag> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-card> |
| | |
| | | // åé
ç½® |
| | | const columns = ref([ |
| | | { prop: "taskName", label: "å·¡æ£ä»»å¡åç§°", minWidth: 160 }, |
| | | { prop: "inspectionProject", label: "å·¡æ£é¡¹ç®", minWidth: 150 }, |
| | | { prop: "remarks", label: "夿³¨", minWidth: 150 }, |
| | | { prop: "inspector", label: "æ§è¡å·¡æ£äºº", minWidth: 150, slot: "inspector" }, |
| | | { |
| | | prop: "isEnabled", |
| | | label: "æ¯å¦å¯ç¨", |
| | | minWidth: 100, |
| | | dataType: "slot", |
| | | slot: "isEnabled" |
| | | }, |
| | | { |
| | | prop: "frequencyType", |
| | | label: "颿¬¡", |
| | |
| | | operationsArr.value = ["edit"]; |
| | | } else if (value === "task") { |
| | | const operationColumn = getOperationColumn(["viewFile"]); |
| | | // 宿¶ä»»å¡è®°å½ä¸å±ç¤º"æ¯å¦å¯ç¨"å |
| | | const taskColumns = columns.value.filter(col => col.prop !== "isEnabled"); |
| | | tableColumns.value = [ |
| | | ...columns.value, |
| | | ...taskColumns, |
| | | ...(operationColumn ? [operationColumn] : []), |
| | | ]; |
| | | operationsArr.value = ["viewFile"]; |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç(%)" prop="taxRate"> |
| | | <!-- <el-input |
| | | v-model="form.taxRate" |
| | | placeholder="请è¾å
¥ç¨ç" |
| | | type="number" |
| | | > |
| | | <template #append> % </template> |
| | | </el-input> --> |
| | | <el-select |
| | | v-model="form.taxRate" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | @change="mathNum" |
| | | > |
| | | <el-option label="1" :value="1" /> |
| | | <el-option label="6" :value="6" /> |
| | | <el-option label="13" :value="13" /> |
| | | <el-option |
| | | v-for="dict in tax_rate" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="Number(dict.value)" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | calculateTaxExclusiveTotalPrice, |
| | | } from "@/utils/summarizeTable"; |
| | | import { ElMessage } from "element-plus"; |
| | | import {ref} from "vue"; |
| | | import {ref, getCurrentInstance} from "vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { tax_rate } = proxy.useDict("tax_rate"); |
| | | |
| | | defineOptions({ |
| | | name: "设å¤å°è´¦è¡¨å", |
| | |
| | | const showQRCode = async (row) => { |
| | | // ç´æ¥ä½¿ç¨URLï¼ä¸è¦ç¨JSON.stringifyå
è£
|
| | | const qrContent = proxy.javaApi + '/device-info?deviceId=' + row.id; |
| | | qrCodeUrl.value = await QRCode.toDataURL(qrContent); |
| | | const qrDataUrl = await QRCode.toDataURL(qrContent, { width: 200, margin: 2 }); |
| | | |
| | | // å建canvasåæå¸¦åç§°çäºç»´ç å¾ç |
| | | const canvas = document.createElement('canvas'); |
| | | const ctx = canvas.getContext('2d'); |
| | | const qrSize = 200; |
| | | const textHeight = 30; |
| | | const padding = 10; |
| | | canvas.width = qrSize + padding * 2; |
| | | canvas.height = qrSize + textHeight + padding * 2; |
| | | |
| | | // å¡«å
ç½è²èæ¯ |
| | | ctx.fillStyle = '#ffffff'; |
| | | ctx.fillRect(0, 0, canvas.width, canvas.height); |
| | | |
| | | // ç»å¶äºç»´ç |
| | | const qrImg = new Image(); |
| | | qrImg.src = qrDataUrl; |
| | | await new Promise((resolve) => { qrImg.onload = resolve; }); |
| | | ctx.drawImage(qrImg, padding, padding, qrSize, qrSize); |
| | | |
| | | // ç»å¶è®¾å¤åç§° |
| | | ctx.fillStyle = '#333333'; |
| | | ctx.font = 'bold 14px Arial'; |
| | | ctx.textAlign = 'center'; |
| | | ctx.fillText(row.deviceName || '', canvas.width / 2, qrSize + padding + 20); |
| | | |
| | | qrCodeUrl.value = canvas.toDataURL('image/png'); |
| | | qrRowData.value = row; |
| | | qrDialogVisible.value = true; |
| | | }; |
| | |
| | | form.value.entryDate = getCurrentDate(); |
| | | } |
| | | |
| | | // ä¸ä¼ åæ ¡æ£ |
| | | function handleBeforeUpload(file) { |
| | | proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); |
| | | return true; |
| | | } |
| | | // ä¸ä¼ 失败 |
| | | function handleUploadError(err) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | // ä¸ä¼ æååè° |
| | | function handleUploadSuccess(res, file, uploadFiles) { |
| | | proxy.$modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | file.tempId = res.data.tempId; |
| | | form.value.tempFileIds.push(file.tempId); |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | proxy.$refs.fileUpload.handleRemove(file); |
| | | } |
| | | } |
| | | // ç§»é¤æä»¶ |
| | | function handleRemove(file) { |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | | delLedgerFile(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // å¤çæææ¥æè¾å
¥ï¼åªå
è®¸æ£æ´æ° |
| | | const handleValidInput = (value) => { |
| | | if (value === '' || value === null || value === undefined) { |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å®è£
ä½ç½®ï¼" prop="instationLocation"> |
| | | <el-input |
| | | v-model="form.instationLocation" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£å®åä½ï¼" prop="unit"> |
| | | <el-input |
| | | v-model="form.unit" |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è¯ä¹¦ç¼å·ï¼" prop="model"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è¯ä¹¦ç¼å·ï¼" prop="model"> |
| | | <el-input |
| | | v-model="form.model" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææ°é´å®æ¥æï¼" prop="mostDate"> |
| | | <el-date-picker |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æææ¥æ(天)ï¼" prop="valid"> |
| | | <el-input |
| | |
| | | > |
| | | <template #append>æ¥</template> |
| | | </el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£å®å¨æï¼" prop="cycle"> |
| | | <el-input |
| | | v-model="form.cycle" |
| | | placeholder="请è¾å
¥æ£å®å¨æ" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | form: { |
| | | code: "", |
| | | name: "", |
| | | instationLocation: "", |
| | | mostDate:"", |
| | | model: "", |
| | | cycle:"", |
| | | validDate: "", |
| | | nextDate: "", |
| | | userId: "", |
| | |
| | | nextDate: [{required: true, message: "è¯·éæ©", trigger: "change"}], |
| | | userId: [{required: true, message: "è¯·éæ©", trigger: "change"}], |
| | | recordDate: [{required: true, message: "è¯·éæ©", trigger: "change"}], |
| | | instationLocation: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | mostDate: [{required: true, message: "è¯·éæ©", trigger: "change"}], |
| | | cycle: [{required: true, message: "è¯·éæ©", trigger: "blur"}], |
| | | valid: [ |
| | | {required: true, message: "请è¾å
¥", trigger: "blur"}, |
| | | { |
| | |
| | | } |
| | | // ä¸è½½éä»¶ |
| | | const downLoadFile = (row) => { |
| | | proxy.$download.name(row.url); |
| | | proxy.$download.byUrl(row.url, row.originalFilename); |
| | | } |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">å½å
¥æ¥æï¼</span> |
| | | <el-date-picker |
| | |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :dbRowClick="dbRowClick" |
| | | :rowClassName="rowClassName" |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | |
| | | align: "center", |
| | | }, |
| | | { |
| | | label: "å®è£
ä½ç½®", |
| | | prop: "instationLocation", |
| | | width: 150, |
| | | align:"center" |
| | | }, |
| | | { |
| | | label: "æ£å®åä½", |
| | | prop: "unit", |
| | | width: 200, |
| | |
| | | width: 130, |
| | | align:"center" |
| | | }, |
| | | { |
| | | label: "æ£å®å¨æ(天)", |
| | | prop: "cycle", |
| | | width: 130, |
| | | align:"center" |
| | | }, |
| | | { |
| | | label: "ç¶æ", |
| | | prop: "status", |
| | |
| | | |
| | | const dbRowClick = (row)=>{ |
| | | rowClickData.value?.openDialog(row) |
| | | } |
| | | |
| | | // è¡æ ·å¼ï¼å¿«å°æï¼7天å
ï¼æé¾ææ 红 |
| | | const rowClassName = ({ row }) => { |
| | | console.log('rowClassName called:', row); |
| | | // valid æ¯ææå¤©æ°ï¼mostDate æ¯ææ°æ£å®æ¥æ |
| | | if (row.valid && row.mostDate) { |
| | | const mostDate = new Date(row.mostDate); |
| | | // 计ç®å°ææ¥æ = æ£å®æ¥æ + ææå¤©æ° |
| | | const validDays = parseInt(row.valid) || 0; |
| | | const expireDate = new Date(mostDate); |
| | | expireDate.setDate(expireDate.getDate() + validDays); |
| | | |
| | | const now = new Date(); |
| | | const diffDays = Math.ceil((expireDate - now) / (1000 * 60 * 60 * 24)); |
| | | console.log('row:', row.code, 'validDays:', validDays, 'expireDate:', expireDate, 'diffDays:', diffDays); |
| | | // 7天å
å°ææå·²é¾æé½æ 红 |
| | | if (diffDays <= 7) { |
| | | console.log('return warning-row'); |
| | | return 'warning-row'; |
| | | } |
| | | } else { |
| | | console.log('row missing valid or mostDate:', row.valid, row.mostDate); |
| | | } |
| | | return ''; |
| | | } |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | :deep(.el-table .warning-row) { |
| | | background-color: #fef0f0 !important; |
| | | } |
| | | :deep(.el-table .warning-row:hover > td) { |
| | | background-color: #f9d5d5 !important; |
| | | } |
| | | :deep(.el-table .el-table__body tr.warning-row td) { |
| | | background-color: #fef0f0 !important; |
| | | } |
| | | </style> |
| | |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | {{ scope.row.runtimeDuration || '-' }} |
| | | {{ getRuntimeDurationDisplay(scope.row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, computed } from 'vue' |
| | | import { ref, onMounted, onUnmounted, computed } from 'vue' |
| | | import dayjs from 'dayjs' |
| | | import { ElMessage } from 'element-plus' |
| | | import { |
| | | VideoPlay, |
| | |
| | | |
| | | return filtered |
| | | }) |
| | | |
| | | // è¿è¡ä¸æ ç»ææ¶é´æ¶ï¼è¿è¡æ¶é¿ééå½åæ¶é´ååï¼ç¨ tick è§¦åæ¨¡æ¿éç® |
| | | const runtimeDisplayTick = ref(0) |
| | | |
| | | /** åå端å¯è½ä½¿ç¨çå¼å§/ç»ææ¶é´å段 */ |
| | | const pickStartTime = (row) => row?.startRuntimeTime ?? row?.startTime ?? row?.start_time |
| | | const pickEndTime = (row) => row?.endRuntimeTime ?? row?.endTime ?? row?.end_time |
| | | |
| | | /** |
| | | * è§£ææ¥å£/å端åå
¥çåç±»æ¶é´ï¼æ¶é´æ³ãISO å符串ãyyyy-MM-dd HH:mm:ssãJackson æ°ç» [y,M,d,h,m,s]ãå«ä¸æç toLocaleString ç |
| | | */ |
| | | const parseDeviceTime = (input) => { |
| | | if (input === null || input === undefined || input === '') return null |
| | | if (typeof input === 'number' && !Number.isNaN(input)) { |
| | | const d = dayjs(input) |
| | | return d.isValid() ? d.toDate() : null |
| | | } |
| | | if (Array.isArray(input)) { |
| | | const [y, mo, day, h = 0, mi = 0, se = 0] = input |
| | | if (y == null || y === '') return null |
| | | const d = dayjs() |
| | | .year(Number(y)) |
| | | .month(Number(mo || 1) - 1) |
| | | .date(Number(day || 1)) |
| | | .hour(Number(h) || 0) |
| | | .minute(Number(mi) || 0) |
| | | .second(Number(se) || 0) |
| | | return d.isValid() ? d.toDate() : null |
| | | } |
| | | const s = String(input).trim() |
| | | if (!s || s === '-') return null |
| | | let d = dayjs(s) |
| | | if (d.isValid()) return d.toDate() |
| | | d = dayjs(s.replace(/-/g, '/')) |
| | | if (d.isValid()) return d.toDate() |
| | | d = dayjs(s.replace(/\//g, '-')) |
| | | if (d.isValid()) return d.toDate() |
| | | return null |
| | | } |
| | | |
| | | const formatDurationMs = (durationMs) => { |
| | | if (durationMs == null || Number.isNaN(durationMs) || durationMs < 0) return '-' |
| | | const hours = Math.floor(durationMs / (1000 * 60 * 60)) |
| | | const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60)) |
| | | if (hours === 0 && minutes === 0) return 'ä¸è¶³1åé' |
| | | return `${hours}å°æ¶${minutes}åé` |
| | | } |
| | | |
| | | const hasMeaningfulEnd = (endRaw) => |
| | | endRaw !== null && |
| | | endRaw !== undefined && |
| | | String(endRaw).trim() !== '' && |
| | | String(endRaw).trim() !== '-' |
| | | |
| | | const formatStoredDuration = (row) => { |
| | | const rd = row?.runtimeDuration |
| | | if (rd === null || rd === undefined) return '' |
| | | const t = String(rd).trim() |
| | | return t === '' || t === '-' ? '' : String(rd) |
| | | } |
| | | |
| | | /** è¿è¡ä¸ï¼å§ç»ç¨ãå½åæ¶é´ - å¼å§æ¶é´ãï¼å·²åæ¢ï¼ä¼å
æ¥å£ runtimeDurationï¼å¦åç¨ç»æ-å¼å§ï¼æ ç»æå¯çå·²åæ¶é¿æå¨ææ¨ç® */ |
| | | const getRuntimeDurationDisplay = (row) => { |
| | | void runtimeDisplayTick.value |
| | | const start = parseDeviceTime(pickStartTime(row)) |
| | | if (!start) { |
| | | return formatStoredDuration(row) || '-' |
| | | } |
| | | |
| | | const statusStr = String(row?.status ?? '').trim() |
| | | const isRunning = statusStr === 'è¿è¡ä¸' || statusStr === '1' |
| | | const endRaw = pickEndTime(row) |
| | | const hasEnd = hasMeaningfulEnd(endRaw) |
| | | |
| | | // æ ç»ææ¶é´ï¼è¿è¡ä¸ä¸å®å¨æç®ï¼å·²åæ¢åä¼å
å±ç¤ºåç«¯å·²åæ¶é¿ï¼æ²¡æåæå½åæ¶é´æ¨ç® |
| | | if (!hasEnd) { |
| | | if (isRunning) return formatDurationMs(Date.now() - start.getTime()) |
| | | const stored = formatStoredDuration(row) |
| | | if (stored) return stored |
| | | return formatDurationMs(Date.now() - start.getTime()) |
| | | } |
| | | |
| | | if (isRunning) { |
| | | return formatDurationMs(Date.now() - start.getTime()) |
| | | } |
| | | |
| | | const end = parseDeviceTime(endRaw) |
| | | const stored = formatStoredDuration(row) |
| | | if (stored) return stored |
| | | if (end) return formatDurationMs(end.getTime() - start.getTime()) |
| | | return '-' |
| | | } |
| | | |
| | | // æ£æ¥è®¾å¤æ¯å¦è¶
æ¶æªå¯å¨ |
| | | const isOverdue = (device) => { |
| | |
| | | device.endRuntimeTime = currentTime |
| | | // 计ç®è¿è¡æ¶é¿ |
| | | if (device.startRuntimeTime) { |
| | | const startTime = new Date(device.startRuntimeTime) |
| | | const endTime = new Date(currentTime) |
| | | const duration = endTime - startTime |
| | | const hours = Math.floor(duration / (1000 * 60 * 60)) |
| | | const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60)) |
| | | device.runtimeDuration = `${hours}å°æ¶${minutes}åé` |
| | | const startTime = parseDeviceTime(device.startRuntimeTime) |
| | | const endTime = parseDeviceTime(currentTime) |
| | | if (startTime && endTime) { |
| | | device.runtimeDuration = formatDurationMs(endTime.getTime() - startTime.getTime()) |
| | | } |
| | | } |
| | | } |
| | | const params = { |
| | |
| | | |
| | | |
| | | |
| | | // ç»ä»¶æè½½æ¶åå§åæ°æ® |
| | | const POLL_MS = 60 * 1000 |
| | | const RUNTIME_TICK_MS = 30 * 1000 |
| | | let listPollTimer = null |
| | | let runtimeTickTimer = null |
| | | |
| | | // ç»ä»¶æè½½æ¶æåæ°æ®ï¼å¹¶æ¯åéå·æ°ä¸æ¬¡å表ï¼è¿è¡ä¸æ¶é¿æ¯ 30 ç§å·æ°æ¾ç¤º |
| | | onMounted(() => { |
| | | getList() |
| | | listPollTimer = setInterval(() => { |
| | | getList() |
| | | }, POLL_MS) |
| | | runtimeTickTimer = setInterval(() => { |
| | | runtimeDisplayTick.value++ |
| | | }, RUNTIME_TICK_MS) |
| | | }) |
| | | |
| | | onUnmounted(() => { |
| | | if (listPollTimer != null) { |
| | | clearInterval(listPollTimer) |
| | | listPollTimer = null |
| | | } |
| | | if (runtimeTickTimer != null) { |
| | | clearInterval(runtimeTickTimer) |
| | | runtimeTickTimer = null |
| | | } |
| | | }) |
| | | </script> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="visible" |
| | | title="éªæ¶å®¡æ¹" |
| | | width="500px" |
| | | @confirm="submitForm" |
| | | @cancel="handleCancel" |
| | | @close="handleCancel" |
| | | > |
| | | <el-form :model="form" :rules="rules" label-width="100px"> |
| | | <el-form-item label="éªæ¶äºº" prop="acceptanceName"> |
| | | <el-select |
| | | v-model="form.acceptanceName" |
| | | placeholder="è¯·éæ©éªæ¶äºº" |
| | | filterable |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.nickName" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="éªæ¶æ¶é´" prop="acceptanceTime"> |
| | | <el-date-picker |
| | | v-model="form.acceptanceTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©éªæ¶æ¶é´" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="éªæ¶å¤æ³¨" prop="acceptanceRemark"> |
| | | <el-input |
| | | v-model="form.acceptanceRemark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥éªæ¶å¤æ³¨" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ref, reactive } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { repairAcceptance } from "@/api/equipmentManagement/repair"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | defineOptions({ |
| | | name: "éªæ¶å®¡æ¹å¼¹çª", |
| | | }); |
| | | |
| | | const emits = defineEmits(["ok"]); |
| | | |
| | | const visible = ref(false); |
| | | const loading = ref(false); |
| | | const repairId = ref(null); |
| | | const userList = ref([]); |
| | | |
| | | const form = reactive({ |
| | | acceptanceName: undefined, |
| | | acceptanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | acceptanceRemark: undefined, |
| | | }); |
| | | |
| | | const rules = { |
| | | acceptanceName: [ |
| | | { required: true, message: "è¯·éæ©éªæ¶äºº", trigger: "change" }, |
| | | ], |
| | | acceptanceTime: [ |
| | | { required: true, message: "è¯·éæ©éªæ¶æ¶é´", trigger: "change" }, |
| | | ], |
| | | acceptanceRemark: [ |
| | | { required: true, message: "请è¾å
¥éªæ¶å¤æ³¨", trigger: "blur" }, |
| | | ], |
| | | }; |
| | | |
| | | // å è½½ç¨æ·å表 |
| | | const loadUserList = async () => { |
| | | const { data } = await userListNoPageByTenantId(); |
| | | userList.value = data; |
| | | }; |
| | | |
| | | // æå¼å¼¹çª |
| | | const open = async (row) => { |
| | | repairId.value = row.id; |
| | | visible.value = true; |
| | | // é置表å |
| | | form.acceptanceName = undefined; |
| | | form.acceptanceTime = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | form.acceptanceRemark = undefined; |
| | | await loadUserList(); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | if (!form.acceptanceName) { |
| | | ElMessage.warning("è¯·éæ©éªæ¶äºº"); |
| | | return; |
| | | } |
| | | if (!form.acceptanceTime) { |
| | | ElMessage.warning("è¯·éæ©éªæ¶æ¶é´"); |
| | | return; |
| | | } |
| | | if (!form.acceptanceRemark) { |
| | | ElMessage.warning("请è¾å
¥éªæ¶å¤æ³¨"); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | try { |
| | | const { code } = await repairAcceptance({ |
| | | id: repairId.value, |
| | | acceptanceName: form.acceptanceName, |
| | | acceptanceTime: form.acceptanceTime, |
| | | acceptanceRemark: form.acceptanceRemark, |
| | | }); |
| | | if (code === 200) { |
| | | ElMessage.success("éªæ¶éè¿"); |
| | | visible.value = false; |
| | | emits("ok"); |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handleCancel = () => { |
| | | visible.value = false; |
| | | }; |
| | | |
| | | defineExpose({ |
| | | open, |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped></style> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç±»ç®"> |
| | | <el-input v-model="form.machineryCategory" placeholder="请è¾å
¥ç±»ç®" /> |
| | | <el-form-item label="æ¥ä¿®æ¥ä¿®é¡¹ç®"> |
| | | <el-input v-model="form.machineryCategory" placeholder="请è¾å
¥æ¥ä¿®æ¥ä¿®é¡¹ç®" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="维修人"> |
| | | <el-input v-model="form.maintenanceName" placeholder="请è¾å
¥ç»´ä¿®äººå§å" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-if="id"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¥ä¿®ç¶æ"> |
| | | <el-select v-model="form.status"> |
| | | <el-select v-model="form.status" disabled> |
| | | <el-option label="å¾
ç»´ä¿®" :value="0"></el-option> |
| | | <el-option label="å®ç»" :value="1"></el-option> |
| | | <el-option label="å·²éªæ¶" :value="1"></el-option> |
| | | <el-option label="失败" :value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- éªæ¶ä¿¡æ¯å±ç¤º --> |
| | | <el-row v-if="id && form.status === 1"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éªæ¶äºº"> |
| | | <el-input v-model="form.acceptanceName" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éªæ¶æ¶é´"> |
| | | <el-input v-model="form.acceptanceTime" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éªæ¶å¤æ³¨"> |
| | | <el-input v-model="form.acceptanceRemark" type="textarea" :rows="2" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶" prop="attachmentIds"> |
| | | <FileUpload v-model:file-list="form.storageBlobDTOs" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import FileUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | import { |
| | | addRepair, |
| | | editRepair, |
| | |
| | | |
| | | const userStore = useUserStore(); |
| | | const deviceOptions = ref([]); |
| | | const fileList = ref([]); |
| | | |
| | | const loadDeviceName = async () => { |
| | | const { data } = await getDeviceLedger(); |
| | |
| | | remark: undefined, // æ
éç°è±¡ |
| | | status: 0, // æ¥ä¿®ç¶æ |
| | | machineryCategory: undefined, |
| | | storageBlobDTOs: [], |
| | | maintenanceName: undefined, // 维修人 |
| | | }); |
| | | |
| | | const setDeviceModel = (deviceId) => { |
| | |
| | | form.remark = data.remark; |
| | | form.status = data.status; |
| | | form.machineryCategory = data.machineryCategory; |
| | | form.storageBlobDTOs = data.storageBlobVOs || []; |
| | | form.maintenanceName = data.maintenanceName; |
| | | form.acceptanceName = data.acceptanceName; |
| | | form.acceptanceTime = data.acceptanceTime; |
| | | form.acceptanceRemark = data.acceptanceRemark; |
| | | }; |
| | | |
| | | const sendForm = async () => { |
| | |
| | | const openAdd = async () => { |
| | | id.value = undefined; |
| | | visible.value = true; |
| | | fileList.value = []; |
| | | await nextTick(); |
| | | await loadDeviceName(); |
| | | }; |
| | |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 2" type="danger">失败</el-tag> |
| | | <el-tag v-if="row.status === 1" type="success">å®ç»</el-tag> |
| | | <el-tag v-if="row.status === 3" type="info">å¾
éªæ¶</el-tag> |
| | | <el-tag v-if="row.status === 0" type="warning">å¾
ç»´ä¿®</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | :disabled="row.status === 1" |
| | | :disabled="row.status === 1 || row.status === 3" |
| | | @click="editRepair(row.id)" |
| | | > |
| | | ç¼è¾ |
| | |
| | | <el-button |
| | | type="success" |
| | | link |
| | | :disabled="row.status === 1" |
| | | :disabled="row.status !== 0" |
| | | @click="addMaintain(row)" |
| | | > |
| | | ç»´ä¿® |
| | | </el-button> |
| | | <el-button |
| | | type="warning" |
| | | link |
| | | :disabled="row.status !== 3" |
| | | @click="openAcceptance(row)" |
| | | > |
| | | éªæ¶ |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | link |
| | | :disabled="row.status === 1" |
| | | :disabled="row.status === 1 || row.status === 3" |
| | | @click="delRepairByIds(row.id)" |
| | | > |
| | | å é¤ |
| | | </el-button> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | @click="openFileDialog(row)" |
| | | > |
| | | éä»¶ |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <RepairModal ref="repairModalRef" @ok="getTableData"/> |
| | | <MaintainModal ref="maintainModalRef" @ok="getTableData"/> |
| | | <AcceptanceModal ref="acceptanceModalRef" @ok="getTableData"/> |
| | | <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" :record-type="'device_repair'" :record-id="recordId" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, getCurrentInstance, computed } from "vue"; |
| | | import {onMounted, getCurrentInstance, computed, ref, defineAsyncComponent} from "vue"; |
| | | import {usePaginationApi} from "@/hooks/usePaginationApi"; |
| | | import {getRepairPage, delRepair} from "@/api/equipmentManagement/repair"; |
| | | import RepairModal from "./Modal/RepairModal.vue"; |
| | | import {ElMessageBox, ElMessage} from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import MaintainModal from "./Modal/MaintainModal.vue"; |
| | | import AcceptanceModal from "./Modal/AcceptanceModal.vue"; |
| | | const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); |
| | | |
| | | defineOptions({ |
| | | name: "è®¾å¤æ¥ä¿®", |
| | |
| | | // æ¨¡ææ¡å®ä¾ |
| | | const repairModalRef = ref(); |
| | | const maintainModalRef = ref(); |
| | | const acceptanceModalRef = ref(); |
| | | |
| | | // è¡¨æ ¼å¤éæ¡éä¸é¡¹ |
| | | const multipleList = ref([]); |
| | |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "ç±»ç®", |
| | | label: "æ¥ä¿®é¡¹ç®", |
| | | align: "center", |
| | | prop: "machineryCategory", |
| | | }, |
| | |
| | | formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""), |
| | | }, |
| | | { |
| | | label: "éªæ¶äºº", |
| | | align: "center", |
| | | prop: "acceptanceName", |
| | | }, |
| | | { |
| | | label: "éªæ¶æ¶é´", |
| | | align: "center", |
| | | prop: "acceptanceTime", |
| | | formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : ""), |
| | | }, |
| | | { |
| | | label: "ç¶æ", |
| | | align: "center", |
| | | prop: "status", |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | // æå¼éä»¶å¼¹çª |
| | | const recordId =ref(0) |
| | | const fileDialogVisible = ref(false) |
| | | |
| | | const openFileDialog = async (row) => { |
| | | recordId.value = row.id |
| | | fileDialogVisible.value = true |
| | | } |
| | | |
| | | // å¤éååä»ä¹ |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | |
| | | maintainModalRef.value.open(row.id, row); |
| | | }; |
| | | |
| | | // æå¼éªæ¶å¼¹çª |
| | | const openAcceptance = (row) => { |
| | | acceptanceModalRef.value.open(row); |
| | | }; |
| | | |
| | | const changePage = ({page, limit}) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false" :disabled="formLoading">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm" :loading="formLoading">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false" :disabled="formLoading">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ç±»ç®"> |
| | | <el-form-item label="ä¿å
»é¡¹ç®"> |
| | | <el-input |
| | | v-model="form.machineryCategory" |
| | | placeholder="请è¾å
¥ç±»ç®" |
| | | placeholder="请è¾å
¥ä¿å
»é¡¹ç®" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="å½å
¥äºº"> |
| | |
| | | <el-option label="失败" :value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ä¿å
»äºº"> |
| | | <el-input |
| | | v-model="form.maintenancePerson" |
| | | placeholder="请è¾å
¥ä¿å
»äººå§å" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="计åä¿å
»æ¥æ"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶" prop="attachmentIds"> |
| | | <FileUpload v-model:file-list="form.storageBlobDTOs" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </FormDialog> |
| | | </template> |
| | |
| | | import { onMounted } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import FileUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | |
| | | defineOptions({ |
| | | name: "设å¤ä¿å
»æ°å¢è®¡å", |
| | |
| | | createUser: undefined, // å½å
¥äºº |
| | | status: 0, //ä¿ä¿®ç¶æ |
| | | machineryCategory: undefined, |
| | | storageBlobDTOs: [], |
| | | maintenancePerson: undefined, // ä¿å
»äºº |
| | | }); |
| | | |
| | | const setDeviceModel = (deviceId) => { |
| | |
| | | form.createUser = Number(data.createUser); |
| | | form.status = data.status; |
| | | form.machineryCategory = data.machineryCategory; |
| | | form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format( |
| | | "YYYY-MM-DD HH:mm:ss" |
| | | ); |
| | | form.maintenancePerson = data.maintenancePerson; |
| | | if (data.maintenancePlanTime) { |
| | | form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format( |
| | | "YYYY-MM-DD HH:mm:ss" |
| | | ); |
| | | } |
| | | form.storageBlobDTOs = data.storageBlobVOs || []; |
| | | }; |
| | | |
| | | // ç¨æ·å表 |
| | |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¿å
»é¡¹ç®" prop="machineryCategory"> |
| | | <el-input |
| | | v-model.trim="form.machineryCategory" |
| | | placeholder="请è¾å
¥ä¿å
»é¡¹ç®" |
| | | maxlength="100" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¿å
»äºº" prop="maintenancePerson"> |
| | | <el-input |
| | | v-model.trim="form.maintenancePerson" |
| | | placeholder="请è¾å
¥ä¿å
»äººå§å" |
| | | maxlength="100" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä»»å¡é¢ç" prop="frequencyType"> |
| | | <el-select v-model="form.frequencyType" placeholder="è¯·éæ©" clearable> |
| | | <el-option label="æ¯æ¥" value="DAILY"/> |
| | |
| | | taskName: undefined, |
| | | // å½å
¥äººï¼åéä¸ä¸ªç¨æ· id |
| | | inspector: undefined, |
| | | machineryCategory: "", |
| | | remarks: '', |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | | week: '', |
| | | time: '', |
| | | deviceModel: undefined, // 尺寸 |
| | | registrationDate: '' |
| | | deviceModel: undefined, // è§æ ¼åå· |
| | | registrationDate: '', |
| | | maintenancePerson: '' // ä¿å
»äºº |
| | | }, |
| | | rules: { |
| | | taskId: [{ required: true, message: "è¯·éæ©è®¾å¤", trigger: "change" },], |
| | | inspector: [{ required: true, message: "è¯·éæ©å½å
¥äºº", trigger: "blur" },], |
| | | registrationDate: [{ required: true, message: "è¯·éæ©ç»è®°æ¶é´", trigger: "change" }] |
| | | registrationDate: [{ required: true, message: "è¯·éæ©ç»è®°æ¶é´", trigger: "change" }], |
| | | machineryCategory: [{ required: true, message: "请è¾å
¥ä¿å
»é¡¹ç®", trigger: "blur" }] |
| | | } |
| | | }) |
| | | const { form, rules } = toRefs(data) |
| | |
| | | taskId: undefined, |
| | | taskName: undefined, |
| | | inspector: undefined, |
| | | inspector: undefined, |
| | | machineryCategory: "", |
| | | remarks: '', |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | | week: '', |
| | | time: '', |
| | | deviceModel: undefined, |
| | | registrationDate: '' |
| | | registrationDate: '', |
| | | maintenancePerson: '' |
| | | } |
| | | } |
| | | |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
| | | <el-tabs v-model="activeTab" |
| | | @tab-change="handleTabChange"> |
| | | <!-- 宿¶ä»»å¡ç®¡çtab --> |
| | | <el-tab-pane label="宿¶ä»»å¡ç®¡ç" name="scheduled"> |
| | | <el-tab-pane label="宿¶ä»»å¡ç®¡ç" |
| | | name="scheduled"> |
| | | <div class="search_form"> |
| | | <el-form :model="scheduledFilters" :inline="true"> |
| | | <el-form :model="scheduledFilters" |
| | | :inline="true"> |
| | | <el-form-item label="ä»»å¡åç§°"> |
| | | <el-input |
| | | v-model="scheduledFilters.taskName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥ä»»å¡åç§°" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getScheduledTableData" |
| | | /> |
| | | <el-input v-model="scheduledFilters.taskName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥ä»»å¡åç§°" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getScheduledTableData" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä»»å¡ç¶æ"> |
| | | <el-select v-model="scheduledFilters.status" placeholder="è¯·éæ©ä»»å¡ç¶æ" clearable style="width: 200px"> |
| | | <el-option label="å¯ç¨" value="1" /> |
| | | <el-option label="åç¨" value="0" /> |
| | | <el-select v-model="scheduledFilters.status" |
| | | placeholder="è¯·éæ©ä»»å¡ç¶æ" |
| | | clearable |
| | | style="width: 200px"> |
| | | <el-option label="å¯ç¨" |
| | | value="1" /> |
| | | <el-option label="åç¨" |
| | | value="0" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getScheduledTableData">æç´¢</el-button> |
| | | <el-button type="primary" |
| | | @click="getScheduledTableData">æç´¢</el-button> |
| | | <el-button @click="resetScheduledFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" size="large">宿¶ä»»å¡ç®¡ç</el-text> |
| | | <el-text class="mx-1" |
| | | size="large">宿¶ä»»å¡ç®¡ç</el-text> |
| | | <div> |
| | | <el-button type="primary" icon="Plus" @click="addScheduledTask"> |
| | | <el-button type="primary" |
| | | icon="Plus" |
| | | @click="addScheduledTask"> |
| | | æ°å¢ä»»å¡ |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | icon="Delete" |
| | | :disabled="scheduledMultipleList.length <= 0" |
| | | @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))" |
| | | > |
| | | <el-button type="danger" |
| | | icon="Delete" |
| | | :disabled="scheduledMultipleList.length <= 0" |
| | | @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))"> |
| | | æ¹éå é¤ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="scheduledColumns" |
| | | :tableData="scheduledDataList" |
| | | :page="{ |
| | | <PIMTable rowKey="id" |
| | | isSelection |
| | | :column="scheduledColumns" |
| | | :tableData="scheduledDataList" |
| | | :page="{ |
| | | current: scheduledPagination.currentPage, |
| | | size: scheduledPagination.pageSize, |
| | | total: scheduledPagination.total, |
| | | }" |
| | | @selection-change="handleScheduledSelectionChange" |
| | | @pagination="changeScheduledPage" |
| | | > |
| | | @selection-change="handleScheduledSelectionChange" |
| | | @pagination="changeScheduledPage"> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 1" type="success">å¯ç¨</el-tag> |
| | | <el-tag v-if="row.status === 0" type="danger">åç¨</el-tag> |
| | | <el-tag v-if="row.status === 1" |
| | | type="success">å¯ç¨</el-tag> |
| | | <el-tag v-if="row.status === 0" |
| | | type="danger">åç¨</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | @click="editScheduledTask(row)" |
| | | > |
| | | <el-button type="primary" |
| | | link |
| | | @click="editScheduledTask(row)"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | link |
| | | @click="delScheduledTaskByIds(row.id)" |
| | | > |
| | | <el-button type="danger" |
| | | link |
| | | @click="delScheduledTaskByIds(row.id)"> |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-tab-pane> |
| | | |
| | | <!-- ä»»å¡è®°å½tabï¼å设å¤ä¿å
»é¡µé¢ï¼ --> |
| | | <el-tab-pane label="ä»»å¡è®°å½" name="record"> |
| | | <el-tab-pane label="ä»»å¡è®°å½" |
| | | name="record"> |
| | | <div class="search_form"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form-item label="设å¤åç§°"> |
| | | <el-input |
| | | v-model="filters.deviceName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" |
| | | /> |
| | | <el-input v-model="filters.deviceName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" /> |
| | | </el-form-item> |
| | | <el-form-item label="计åä¿å
»æ¥æ"> |
| | | <el-date-picker |
| | | v-model="filters.maintenancePlanTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©è®¡åä¿å
»æ¥æ" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,2)" |
| | | /> |
| | | <el-date-picker v-model="filters.maintenancePlanTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©è®¡åä¿å
»æ¥æ" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,2)" /> |
| | | </el-form-item> |
| | | <el-form-item label="å®é
ä¿å
»æ¥æ"> |
| | | <el-date-picker |
| | | v-model="filters.maintenanceActuallyTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©å®é
ä¿å
»æ¥æ" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,1)" |
| | | /> |
| | | <el-date-picker v-model="filters.maintenanceActuallyTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©å®é
ä¿å
»æ¥æ" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,1)" /> |
| | | </el-form-item> |
| | | <el-form-item label="å®é
ä¿å
»äºº"> |
| | | <el-input |
| | | v-model="filters.maintenanceActuallyName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å®é
ä¿å
»äºº" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" |
| | | /> |
| | | <el-input v-model="filters.maintenanceActuallyName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å®é
ä¿å
»äºº" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button type="primary" |
| | | @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" size="large">ä»»å¡è®°å½</el-text> |
| | | <el-text class="mx-1" |
| | | size="large">ä»»å¡è®°å½</el-text> |
| | | <div> |
| | | <el-button type="success" icon="Van" @click="addPlan"> |
| | | <el-button type="success" |
| | | icon="Van" |
| | | @click="addPlan"> |
| | | æ°å¢è®¡å |
| | | </el-button> |
| | | <el-button @click="handleOut"> |
| | | å¯¼åº |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | icon="Delete" |
| | | :disabled="multipleList.length <= 0 || hasFinishedStatus" |
| | | @click="delRepairByIds(multipleList.map((item) => item.id))" |
| | | > |
| | | <el-button type="danger" |
| | | icon="Delete" |
| | | :disabled="multipleList.length <= 0 || hasFinishedStatus" |
| | | @click="delRepairByIds(multipleList.map((item) => item.id))"> |
| | | æ¹éå é¤ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | <PIMTable rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #maintenanceResultRef="{ row }"> |
| | | <div>{{ row.maintenanceResult || '-' }}</div> |
| | | </template> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 2" type="danger">失败</el-tag> |
| | | <el-tag v-if="row.status === 1" type="success">å®ç»</el-tag> |
| | | <el-tag v-if="row.status === 0" type="warning">å¾
ä¿å
»</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <!-- è¿ä¸ªåè½è·æ°å¢ä¿å
»åè½ä¸æ¨¡ä¸æ ·ï¼æå¥æä¹ï¼ --> |
| | | <!-- <el-button |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage"> |
| | | <template #maintenanceResultRef="{ row }"> |
| | | <div>{{ row.maintenanceResult || '-' }}</div> |
| | | </template> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 2" |
| | | type="danger">失败</el-tag> |
| | | <el-tag v-if="row.status === 1" |
| | | type="success">å®ç»</el-tag> |
| | | <el-tag v-if="row.status === 0" |
| | | type="warning">å¾
ä¿å
»</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <!-- è¿ä¸ªåè½è·æ°å¢ä¿å
»åè½ä¸æ¨¡ä¸æ ·ï¼æå¥æä¹ï¼ --> |
| | | <!-- <el-button |
| | | type="primary" |
| | | text |
| | | @click="addMaintain(row)" |
| | | > |
| | | æ°å¢ä¿å
» |
| | | </el-button> --> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="editPlan(row.id)" |
| | | > |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button |
| | | type="success" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="addMaintain(row)" |
| | | > |
| | | ä¿å
» |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="delRepairByIds(row.id)" |
| | | > |
| | | å é¤ |
| | | </el-button> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | @click="openFileDialog(row)" |
| | | > |
| | | éä»¶ |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | <el-button type="primary" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="editPlan(row.id)"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button type="success" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="addMaintain(row)"> |
| | | ä¿å
» |
| | | </el-button> |
| | | <el-button type="danger" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="delRepairByIds(row.id)"> |
| | | å é¤ |
| | | </el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="openFileDialog(row)"> |
| | | éä»¶ |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | <PlanModal ref="planModalRef" @ok="getTableData" /> |
| | | <MaintenanceModal ref="maintainModalRef" @ok="getTableData" /> |
| | | <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" /> |
| | | <FileListDialog |
| | | ref="fileListDialogRef" |
| | | v-model="fileDialogVisible" |
| | | :show-upload-button="true" |
| | | :show-delete-button="true" |
| | | :delete-method="handleAttachmentDelete" |
| | | :name-column-label="'éä»¶åç§°'" |
| | | :rulesRegulationsManagementId="currentMaintenanceTaskId" |
| | | @upload="handleAttachmentUpload" /> |
| | | <PlanModal ref="planModalRef" |
| | | @ok="getTableData" /> |
| | | <MaintenanceModal ref="maintainModalRef" |
| | | @ok="getTableData" /> |
| | | <FormDia ref="formDiaRef" |
| | | @closeDia="getScheduledTableData" /> |
| | | <FileList v-if="fileDialogVisible" |
| | | v-model:visible="fileDialogVisible" |
| | | :record-type="'device_maintenance'" |
| | | :record-id="currentMaintenanceTaskId" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, reactive, getCurrentInstance, nextTick, computed } from 'vue' |
| | | import { Search } from '@element-plus/icons-vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import PlanModal from './Form/PlanModal.vue' |
| | | import MaintenanceModal from './Form/MaintenanceModal.vue' |
| | | import FormDia from './Form/formDia.vue' |
| | | import FileListDialog from '@/components/Dialog/FileListDialog.vue' |
| | | import { |
| | | getUpkeepPage, |
| | | delUpkeep, |
| | | deviceMaintenanceTaskList, |
| | | deviceMaintenanceTaskDel, |
| | | } from '@/api/equipmentManagement/upkeep' |
| | | import { |
| | | listMaintenanceTaskFiles, |
| | | addMaintenanceTaskFile, |
| | | delMaintenanceTaskFile, |
| | | } from '@/api/equipmentManagement/maintenanceTaskFile' |
| | | import dayjs from 'dayjs' |
| | | import { |
| | | ref, |
| | | onMounted, |
| | | reactive, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | computed, |
| | | defineAsyncComponent, |
| | | } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PlanModal from "./Form/PlanModal.vue"; |
| | | import MaintenanceModal from "./Form/MaintenanceModal.vue"; |
| | | import FormDia from "./Form/formDia.vue"; |
| | | import { |
| | | getUpkeepPage, |
| | | delUpkeep, |
| | | deviceMaintenanceTaskList, |
| | | deviceMaintenanceTaskDel, |
| | | } from "@/api/equipmentManagement/upkeep"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | const FileList = defineAsyncComponent(() => |
| | | import("@/components/Dialog/FileList.vue") |
| | | ); |
| | | |
| | | // Tabç¸å
³ |
| | | const activeTab = ref('scheduled') |
| | | // Tabç¸å
³ |
| | | const activeTab = ref("scheduled"); |
| | | |
| | | // 计åå¼¹çªæ§å¶å¨ |
| | | const planModalRef = ref() |
| | | // ä¿å
»å¼¹çªæ§å¶å¨ |
| | | const maintainModalRef = ref() |
| | | // 宿¶ä»»å¡å¼¹çªæ§å¶å¨ |
| | | const formDiaRef = ref() |
| | | // éä»¶å¼¹çª |
| | | const fileListDialogRef = ref(null) |
| | | const fileDialogVisible = ref(false) |
| | | const currentMaintenanceTaskId = ref(null) |
| | | // 计åå¼¹çªæ§å¶å¨ |
| | | const planModalRef = ref(); |
| | | // ä¿å
»å¼¹çªæ§å¶å¨ |
| | | const maintainModalRef = ref(); |
| | | // 宿¶ä»»å¡å¼¹çªæ§å¶å¨ |
| | | const formDiaRef = ref(); |
| | | // éä»¶å¼¹çª |
| | | const fileListDialogRef = ref(null); |
| | | const fileDialogVisible = ref(false); |
| | | const currentMaintenanceTaskId = ref(null); |
| | | |
| | | // ä»»å¡è®°å½tabï¼å设å¤ä¿å
»é¡µé¢ï¼ç¸å
³åé |
| | | const filters = reactive({ |
| | | deviceName: '', |
| | | maintenancePlanTime: '', |
| | | maintenanceActuallyTime: '', |
| | | maintenanceActuallyName: '', |
| | | }) |
| | | // ä»»å¡è®°å½tabï¼å设å¤ä¿å
»é¡µé¢ï¼ç¸å
³åé |
| | | const filters = reactive({ |
| | | deviceName: "", |
| | | maintenancePlanTime: "", |
| | | maintenanceActuallyTime: "", |
| | | maintenanceActuallyName: "", |
| | | }); |
| | | |
| | | const dataList = ref([]) |
| | | const pagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }) |
| | | const multipleList = ref([]) |
| | | const dataList = ref([]); |
| | | const pagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | const multipleList = ref([]); |
| | | |
| | | // 宿¶ä»»å¡ç®¡çtabç¸å
³åé |
| | | const scheduledFilters = reactive({ |
| | | taskName: '', |
| | | status: '', |
| | | }) |
| | | // 宿¶ä»»å¡ç®¡çtabç¸å
³åé |
| | | const scheduledFilters = reactive({ |
| | | taskName: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const scheduledDataList = ref([]) |
| | | const scheduledPagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }) |
| | | const scheduledMultipleList = ref([]) |
| | | const scheduledDataList = ref([]); |
| | | const scheduledPagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | const scheduledMultipleList = ref([]); |
| | | |
| | | // 宿¶ä»»å¡ç®¡çè¡¨æ ¼åé
ç½® |
| | | const scheduledColumns = ref([ |
| | | { prop: "taskName", label: "设å¤åç§°"}, |
| | | { |
| | | label: "尺寸", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | prop: "frequencyType", |
| | | label: "颿¬¡", |
| | | minWidth: 150, |
| | | // PIMTable 使ç¨çæ¯ formatDataï¼è䏿¯ Element-Plus ç formatter |
| | | formatData: (cell) => ({ |
| | | DAILY: "æ¯æ¥", |
| | | WEEKLY: "æ¯å¨", |
| | | MONTHLY: "æ¯æ", |
| | | QUARTERLY: "å£åº¦" |
| | | }[cell] || "") |
| | | }, |
| | | { |
| | | prop: "frequencyDetail", |
| | | label: "å¼å§æ¥æä¸æ¶é´", |
| | | minWidth: 150, |
| | | // åæ ·æ¹ç¨ formatDataï¼PIMTable å
é¨ä¼æåå
æ ¼å¼ä¼ è¿æ¥ |
| | | formatData: (cell) => { |
| | | if (typeof cell !== 'string') return ''; |
| | | let val = cell; |
| | | const replacements = { |
| | | MON: 'å¨ä¸', |
| | | TUE: 'å¨äº', |
| | | WED: 'å¨ä¸', |
| | | THU: 'å¨å', |
| | | FRI: 'å¨äº', |
| | | SAT: 'å¨å
', |
| | | SUN: '卿¥' |
| | | }; |
| | | // ä½¿ç¨æ£å䏿¬¡æ§æ¿æ¢ææå¹é
项 |
| | | return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]); |
| | | } |
| | | }, |
| | | { prop: "registrant", label: "ç»è®°äºº", minWidth: 100 }, |
| | | { prop: "registrationDate", label: "ç»è®°æ¥æ", minWidth: 100 }, |
| | | { |
| | | fixed: "right", |
| | | label: "æä½", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "200px", |
| | | }, |
| | | ]) |
| | | // 宿¶ä»»å¡ç®¡çè¡¨æ ¼åé
ç½® |
| | | const scheduledColumns = ref([ |
| | | { prop: "taskName", label: "设å¤åç§°" }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "ä¿å
»é¡¹ç®", |
| | | prop: "machineryCategory", |
| | | minWidth: 120, |
| | | formatData: cell => cell || "--", |
| | | }, |
| | | { |
| | | prop: "frequencyType", |
| | | label: "颿¬¡", |
| | | minWidth: 150, |
| | | // PIMTable 使ç¨çæ¯ formatDataï¼è䏿¯ Element-Plus ç formatter |
| | | formatData: cell => |
| | | ({ |
| | | DAILY: "æ¯æ¥", |
| | | WEEKLY: "æ¯å¨", |
| | | MONTHLY: "æ¯æ", |
| | | QUARTERLY: "å£åº¦", |
| | | }[cell] || ""), |
| | | }, |
| | | { |
| | | prop: "frequencyDetail", |
| | | label: "å¼å§æ¥æä¸æ¶é´", |
| | | minWidth: 150, |
| | | // åæ ·æ¹ç¨ formatDataï¼PIMTable å
é¨ä¼æåå
æ ¼å¼ä¼ è¿æ¥ |
| | | formatData: cell => { |
| | | if (typeof cell !== "string") return ""; |
| | | let val = cell; |
| | | const replacements = { |
| | | MON: "å¨ä¸", |
| | | TUE: "å¨äº", |
| | | WED: "å¨ä¸", |
| | | THU: "å¨å", |
| | | FRI: "å¨äº", |
| | | SAT: "å¨å
", |
| | | SUN: "卿¥", |
| | | }; |
| | | // ä½¿ç¨æ£å䏿¬¡æ§æ¿æ¢ææå¹é
项 |
| | | return val.replace( |
| | | /MON|TUE|WED|THU|FRI|SAT|SUN/g, |
| | | match => replacements[match] |
| | | ); |
| | | }, |
| | | }, |
| | | { prop: "maintenancePerson", label: "ä¿å
»äºº", minWidth: 100 }, |
| | | { prop: "registrant", label: "ç»è®°äºº", minWidth: 100 }, |
| | | { prop: "registrationDate", label: "ç»è®°æ¥æ", minWidth: 100 }, |
| | | { |
| | | fixed: "right", |
| | | label: "æä½", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "200px", |
| | | }, |
| | | ]); |
| | | |
| | | // ä»»å¡è®°å½è¡¨æ ¼åé
ç½®ï¼å设å¤ä¿å
»è¡¨æ ¼åï¼ |
| | | const columns = ref([ |
| | | { |
| | | label: "设å¤åç§°", |
| | | align: "center", |
| | | prop: "deviceName", |
| | | }, |
| | | { |
| | | label: "尺寸", |
| | | align: "center", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "计åä¿å
»æ¥æ", |
| | | align: "center", |
| | | prop: "maintenancePlanTime", |
| | | formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"), |
| | | }, |
| | | { |
| | | label: "å½å
¥äºº", |
| | | align: "center", |
| | | prop: "createUserName", |
| | | }, |
| | | { |
| | | label: "ç±»ç®", |
| | | align: "center", |
| | | prop: "machineryCategory", |
| | | }, |
| | | // { |
| | | // label: "å½å
¥æ¥æ", |
| | | // align: "center", |
| | | // prop: "createTime", |
| | | // formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"), |
| | | // width: 200, |
| | | // }, |
| | | { |
| | | label: "å®é
ä¿å
»äºº", |
| | | align: "center", |
| | | prop: "maintenanceActuallyName", |
| | | }, |
| | | { |
| | | label: "å®é
ä¿å
»æ¥æ", |
| | | align: "center", |
| | | prop: "maintenanceActuallyTime", |
| | | formatData: (cell) => |
| | | cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | }, |
| | | { |
| | | label: "ä¿å
ȍȾ", |
| | | align: "center", |
| | | prop: "maintenanceResult", |
| | | dataType: "slot", |
| | | slot: "maintenanceResultRef", |
| | | }, |
| | | { |
| | | label: "ç¶æ", |
| | | align: "center", |
| | | prop: "status", |
| | | dataType: "slot", |
| | | slot: "statusRef", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "æä½", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "350px", |
| | | }, |
| | | ]) |
| | | // ä»»å¡è®°å½è¡¨æ ¼åé
ç½®ï¼å设å¤ä¿å
»è¡¨æ ¼åï¼ |
| | | const columns = ref([ |
| | | { |
| | | label: "设å¤åç§°", |
| | | align: "center", |
| | | prop: "deviceName", |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | align: "center", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "计åä¿å
»æ¥æ", |
| | | align: "center", |
| | | prop: "maintenancePlanTime", |
| | | formatData: cell => dayjs(cell).format("YYYY-MM-DD"), |
| | | }, |
| | | { |
| | | label: "å½å
¥äºº", |
| | | align: "center", |
| | | prop: "createUserName", |
| | | }, |
| | | { |
| | | label: "ä¿å
»é¡¹ç®", |
| | | align: "center", |
| | | prop: "machineryCategory", |
| | | formatData: cell => cell || "--", |
| | | }, |
| | | // { |
| | | // label: "å½å
¥æ¥æ", |
| | | // align: "center", |
| | | // prop: "createTime", |
| | | // formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"), |
| | | // width: 200, |
| | | // }, |
| | | { |
| | | label: "å®é
ä¿å
»äºº", |
| | | align: "center", |
| | | prop: "maintenanceActuallyName", |
| | | }, |
| | | { |
| | | label: "å®é
ä¿å
»æ¥æ", |
| | | align: "center", |
| | | prop: "maintenanceActuallyTime", |
| | | formatData: cell => |
| | | cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | }, |
| | | { |
| | | label: "ä¿å
ȍȾ", |
| | | align: "center", |
| | | prop: "maintenanceResult", |
| | | dataType: "slot", |
| | | slot: "maintenanceResultRef", |
| | | }, |
| | | { |
| | | label: "ç¶æ", |
| | | align: "center", |
| | | prop: "status", |
| | | dataType: "slot", |
| | | slot: "statusRef", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "æä½", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "350px", |
| | | }, |
| | | ]); |
| | | |
| | | // Tab忢å¤ç |
| | | const handleTabChange = (tabName) => { |
| | | if (tabName === 'record') { |
| | | getTableData() |
| | | } else if (tabName === 'scheduled') { |
| | | getScheduledTableData() |
| | | } |
| | | } |
| | | |
| | | // 宿¶ä»»å¡ç®¡çç¸å
³æ¹æ³ |
| | | const getScheduledTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: scheduledPagination.currentPage, |
| | | size: scheduledPagination.pageSize, |
| | | taskName: scheduledFilters.taskName || undefined, |
| | | status: scheduledFilters.status || undefined, |
| | | // Tab忢å¤ç |
| | | const handleTabChange = tabName => { |
| | | if (tabName === "record") { |
| | | getTableData(); |
| | | } else if (tabName === "scheduled") { |
| | | getScheduledTableData(); |
| | | } |
| | | const { code, data } = await deviceMaintenanceTaskList(params) |
| | | if (code === 200) { |
| | | scheduledDataList.value = data?.records || [] |
| | | scheduledPagination.total = data?.total || 0 |
| | | }; |
| | | |
| | | // 宿¶ä»»å¡ç®¡çç¸å
³æ¹æ³ |
| | | const getScheduledTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: scheduledPagination.currentPage, |
| | | size: scheduledPagination.pageSize, |
| | | taskName: scheduledFilters.taskName || undefined, |
| | | status: scheduledFilters.status || undefined, |
| | | }; |
| | | const { code, data } = await deviceMaintenanceTaskList(params); |
| | | if (code === 200) { |
| | | scheduledDataList.value = data?.records || []; |
| | | scheduledPagination.total = data?.total || 0; |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error("è·å宿¶ä»»å¡å表失败"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('è·å宿¶ä»»å¡å表失败') |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const resetScheduledFilters = () => { |
| | | scheduledFilters.taskName = '' |
| | | scheduledFilters.status = '' |
| | | getScheduledTableData() |
| | | } |
| | | const resetScheduledFilters = () => { |
| | | scheduledFilters.taskName = ""; |
| | | scheduledFilters.status = ""; |
| | | getScheduledTableData(); |
| | | }; |
| | | |
| | | const handleScheduledSelectionChange = (selection) => { |
| | | scheduledMultipleList.value = selection |
| | | } |
| | | const handleScheduledSelectionChange = selection => { |
| | | scheduledMultipleList.value = selection; |
| | | }; |
| | | |
| | | const changeScheduledPage = (page) => { |
| | | scheduledPagination.currentPage = page.page |
| | | scheduledPagination.pageSize = page.limit |
| | | getScheduledTableData() |
| | | } |
| | | const changeScheduledPage = page => { |
| | | scheduledPagination.currentPage = page.page; |
| | | scheduledPagination.pageSize = page.limit; |
| | | getScheduledTableData(); |
| | | }; |
| | | |
| | | const addScheduledTask = () => { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog('add'); |
| | | }); |
| | | } |
| | | const addScheduledTask = () => { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog("add"); |
| | | }); |
| | | }; |
| | | |
| | | const editScheduledTask = (row) => { |
| | | if (row) { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog('edit', row); |
| | | }); |
| | | } |
| | | } |
| | | const editScheduledTask = row => { |
| | | if (row) { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog("edit", row); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const delScheduledTaskByIds = async (ids) => { |
| | | try { |
| | | await ElMessageBox.confirm('ç¡®å®å é¤éä¸ç宿¶ä»»å¡åï¼', 'æç¤º', { |
| | | type: 'warning', |
| | | const delScheduledTaskByIds = async ids => { |
| | | try { |
| | | await ElMessageBox.confirm("ç¡®å®å é¤éä¸ç宿¶ä»»å¡åï¼", "æç¤º", { |
| | | type: "warning", |
| | | }); |
| | | const payload = Array.isArray(ids) ? ids : [ids]; |
| | | await deviceMaintenanceTaskDel(payload); |
| | | ElMessage.success("å é¤å®æ¶ä»»å¡æå"); |
| | | getScheduledTableData(); |
| | | } catch (error) { |
| | | // ç¨æ·åæ¶å é¤ |
| | | } |
| | | }; |
| | | |
| | | const handleScheduledOut = () => { |
| | | ElMessage.info("导åºå®æ¶ä»»å¡åè½å¾
å®ç°"); |
| | | }; |
| | | |
| | | // ä»»å¡è®°å½ç¸å
³æ¹æ³ï¼å设å¤ä¿å
»é¡µé¢æ¹æ³ï¼ |
| | | const getTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: pagination.value.currentPage, |
| | | size: pagination.value.pageSize, |
| | | deviceName: filters.deviceName || undefined, |
| | | maintenancePlanTime: filters.maintenancePlanTime |
| | | ? dayjs(filters.maintenancePlanTime).format("YYYY-MM-DD") |
| | | : undefined, |
| | | maintenanceActuallyTime: filters.maintenanceActuallyTime |
| | | ? dayjs(filters.maintenanceActuallyTime).format("YYYY-MM-DD") |
| | | : undefined, |
| | | maintenanceActuallyName: filters.maintenanceActuallyName || undefined, |
| | | }; |
| | | |
| | | const { code, data } = await getUpkeepPage(params); |
| | | if (code === 200) { |
| | | dataList.value = data.records; |
| | | pagination.value.total = data.total; |
| | | } |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.deviceName = ""; |
| | | filters.maintenancePlanTime = ""; |
| | | filters.maintenanceActuallyTime = ""; |
| | | filters.maintenanceActuallyName = ""; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleSelectionChange = selection => { |
| | | multipleList.value = selection; |
| | | }; |
| | | |
| | | // æ£æ¥éä¸çè®°å½ä¸æ¯å¦æå®ç»ç¶æç |
| | | const hasFinishedStatus = computed(() => { |
| | | return multipleList.value.some(item => item.status === 1); |
| | | }); |
| | | |
| | | const changePage = page => { |
| | | pagination.value.currentPage = page.page; |
| | | pagination.value.pageSize = page.limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const addMaintain = row => { |
| | | maintainModalRef.value.open(row.id, row); |
| | | }; |
| | | |
| | | const addPlan = () => { |
| | | planModalRef.value.openModal(); |
| | | }; |
| | | |
| | | const editPlan = id => { |
| | | planModalRef.value.openEdit(id); |
| | | }; |
| | | |
| | | const delRepairByIds = async ids => { |
| | | // æ£æ¥æ¯å¦æå®ç»ç¶æçè®°å½ |
| | | const hasFinished = multipleList.value.some(item => item.status === 1); |
| | | if (hasFinished) { |
| | | ElMessage.warning("ä¸è½å é¤ç¶æä¸ºå®ç»çè®°å½"); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | await ElMessageBox.confirm("确认å é¤ä¿å
»æ°æ®, æ¤æä½ä¸å¯é?", "è¦å", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }); |
| | | |
| | | const { code } = await delUpkeep(ids); |
| | | if (code === 200) { |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | } |
| | | } catch (error) { |
| | | // ç¨æ·åæ¶å é¤ |
| | | } |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | const payload = Array.isArray(ids) ? ids : [ids] |
| | | await deviceMaintenanceTaskDel(payload) |
| | | ElMessage.success('å é¤å®æ¶ä»»å¡æå') |
| | | getScheduledTableData() |
| | | } catch (error) { |
| | | // ç¨æ·åæ¶å é¤ |
| | | } |
| | | } |
| | | .then(() => { |
| | | proxy.download("/device/maintenance/export", {}, "设å¤ä¿å
».xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | const handleScheduledOut = () => { |
| | | ElMessage.info('导åºå®æ¶ä»»å¡åè½å¾
å®ç°') |
| | | } |
| | | |
| | | // ä»»å¡è®°å½ç¸å
³æ¹æ³ï¼å设å¤ä¿å
»é¡µé¢æ¹æ³ï¼ |
| | | const getTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: pagination.value.currentPage, |
| | | size: pagination.value.pageSize, |
| | | deviceName: filters.deviceName || undefined, |
| | | maintenancePlanTime: filters.maintenancePlanTime ? dayjs(filters.maintenancePlanTime).format('YYYY-MM-DD') : undefined, |
| | | maintenanceActuallyTime: filters.maintenanceActuallyTime ? dayjs(filters.maintenanceActuallyTime).format('YYYY-MM-DD') : undefined, |
| | | maintenanceActuallyName: filters.maintenanceActuallyName || undefined, |
| | | const handleDateChange = (date, type) => { |
| | | if (type === 1) { |
| | | filters.maintenanceActuallyTime = date |
| | | ? dayjs(date).format("YYYY-MM-DD") |
| | | : ""; |
| | | } else { |
| | | filters.maintenancePlanTime = date ? dayjs(date).format("YYYY-MM-DD") : ""; |
| | | } |
| | | getTableData(); |
| | | }; |
| | | |
| | | const { code, data } = await getUpkeepPage(params) |
| | | if (code === 200) { |
| | | dataList.value = data.records |
| | | pagination.value.total = data.total |
| | | // æå¼éä»¶å¼¹çª |
| | | const openFileDialog = async row => { |
| | | currentMaintenanceTaskId.value = row.id; |
| | | fileDialogVisible.value = true; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // æ ¹æ®é»è®¤æ¿æ´»ç Tab è°ç¨å¯¹åºçæ¥è¯¢æ¥å£ |
| | | if (activeTab.value === "scheduled") { |
| | | getScheduledTableData(); |
| | | } else { |
| | | getTableData(); |
| | | } |
| | | } catch (error) { |
| | | console.log(error); |
| | | |
| | | } |
| | | } |
| | | |
| | | const resetFilters = () => { |
| | | filters.deviceName = '' |
| | | filters.maintenancePlanTime = '' |
| | | filters.maintenanceActuallyTime = '' |
| | | filters.maintenanceActuallyName = '' |
| | | getTableData() |
| | | } |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | multipleList.value = selection |
| | | } |
| | | |
| | | // æ£æ¥éä¸çè®°å½ä¸æ¯å¦æå®ç»ç¶æç |
| | | const hasFinishedStatus = computed(() => { |
| | | return multipleList.value.some(item => item.status === 1) |
| | | }) |
| | | |
| | | const changePage = (page) => { |
| | | pagination.value.currentPage = page.page |
| | | pagination.value.pageSize = page.limit |
| | | getTableData() |
| | | } |
| | | |
| | | const addMaintain = (row) => { |
| | | maintainModalRef.value.open(row.id, row) |
| | | } |
| | | |
| | | const addPlan = () => { |
| | | planModalRef.value.openModal() |
| | | } |
| | | |
| | | const editPlan = (id) => { |
| | | planModalRef.value.openEdit(id) |
| | | } |
| | | |
| | | const delRepairByIds = async (ids) => { |
| | | // æ£æ¥æ¯å¦æå®ç»ç¶æçè®°å½ |
| | | const hasFinished = multipleList.value.some(item => item.status === 1) |
| | | if (hasFinished) { |
| | | ElMessage.warning('ä¸è½å é¤ç¶æä¸ºå®ç»çè®°å½') |
| | | return |
| | | } |
| | | |
| | | try { |
| | | await ElMessageBox.confirm('确认å é¤ä¿å
»æ°æ®, æ¤æä½ä¸å¯é?', 'è¦å', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }) |
| | | |
| | | const { code } = await delUpkeep(ids) |
| | | if (code === 200) { |
| | | ElMessage.success('å 餿å') |
| | | getTableData() |
| | | } |
| | | } catch (error) { |
| | | // ç¨æ·åæ¶å é¤ |
| | | } |
| | | } |
| | | |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm('éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼', '导åº', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }) |
| | | .then(() => { |
| | | proxy.download('/device/maintenance/export', {}, '设å¤ä¿å
».xlsx') |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info('已忶') |
| | | }) |
| | | } |
| | | |
| | | const handleDateChange = (date, type) => { |
| | | if (type === 1) { |
| | | filters.maintenanceActuallyTime = date ? dayjs(date).format('YYYY-MM-DD') : '' |
| | | } else { |
| | | filters.maintenancePlanTime = date ? dayjs(date).format('YYYY-MM-DD') : '' |
| | | } |
| | | getTableData() |
| | | } |
| | | |
| | | // éä»¶ç¸å
³æ¹æ³ |
| | | // æ¥è¯¢éä»¶å表 |
| | | const fetchMaintenanceTaskFiles = async (deviceMaintenanceId) => { |
| | | try { |
| | | const params = { |
| | | current: 1, |
| | | size: 100, |
| | | deviceMaintenanceId, |
| | | rulesRegulationsManagementId:deviceMaintenanceId |
| | | } |
| | | const res = await listMaintenanceTaskFiles(params) |
| | | const records = res?.data?.records || [] |
| | | const mapped = records.map(item => ({ |
| | | id: item.id, |
| | | name: item.fileName || item.name, |
| | | url: item.fileUrl || item.url, |
| | | raw: item, |
| | | })) |
| | | fileListDialogRef.value?.setList(mapped) |
| | | } catch (error) { |
| | | ElMessage.error('è·åéä»¶å表失败') |
| | | } |
| | | } |
| | | |
| | | // æå¼éä»¶å¼¹çª |
| | | const openFileDialog = async (row) => { |
| | | currentMaintenanceTaskId.value = row.id |
| | | fileDialogVisible.value = true |
| | | await fetchMaintenanceTaskFiles(row.id) |
| | | } |
| | | |
| | | // å·æ°éä»¶å表 |
| | | const refreshFileList = async () => { |
| | | if (!currentMaintenanceTaskId.value) return |
| | | await fetchMaintenanceTaskFiles(currentMaintenanceTaskId.value) |
| | | } |
| | | |
| | | // ä¸ä¼ éä»¶ |
| | | const handleAttachmentUpload = async (filePayload) => { |
| | | if (!currentMaintenanceTaskId.value) return |
| | | try { |
| | | const payload = { |
| | | name: filePayload?.fileName || filePayload?.name, |
| | | url: filePayload?.fileUrl || filePayload?.url, |
| | | deviceMaintenanceId: currentMaintenanceTaskId.value, |
| | | } |
| | | await addMaintenanceTaskFile(payload) |
| | | ElMessage.success('æä»¶ä¸ä¼ æå') |
| | | await refreshFileList() |
| | | } catch (error) { |
| | | ElMessage.error('æä»¶ä¸ä¼ 失败') |
| | | } |
| | | } |
| | | |
| | | // å é¤éä»¶ |
| | | const handleAttachmentDelete = async (row) => { |
| | | if (!row?.id) return false |
| | | try { |
| | | await ElMessageBox.confirm('确认å é¤è¯¥éä»¶ï¼', 'æç¤º', { type: 'warning' }) |
| | | } catch { |
| | | return false |
| | | } |
| | | try { |
| | | await delMaintenanceTaskFile(row.id) |
| | | ElMessage.success('å 餿å') |
| | | await refreshFileList() |
| | | return true |
| | | } catch (error) { |
| | | ElMessage.error('å é¤å¤±è´¥') |
| | | return false |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | | // æ ¹æ®é»è®¤æ¿æ´»ç Tab è°ç¨å¯¹åºçæ¥è¯¢æ¥å£ |
| | | if (activeTab.value === 'scheduled') { |
| | | getScheduledTableData() |
| | | } else { |
| | | getTableData() |
| | | } |
| | | }) |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| | | |
| | | |
| | |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleSubmit">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åé
书ç±ï¼" prop="documentationId"> |
| | | <!-- <el-select v-model="borrowForm.documentationId" placeholder="è¯·éæ©åé
书ç±" style="width: 100%" @change="handleScanContent"> |
| | | <el-option |
| | | v-for="item in documentList" |
| | | :key="item.id" |
| | | :label="item.docName || item.name" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> --> |
| | | <div style="display: flex; gap: 10px;"> |
| | | <el-select v-model="borrowForm.documentationId" placeholder="è¯·éæ©åé
书ç±" style="flex: 1;width: 100px;" @change="handleSelectChange"> |
| | | <el-select |
| | | v-if="borrowOperationType !== 'edit'" |
| | | v-model="borrowForm.documentationId" |
| | | placeholder="è¯·éæ©åé
书ç±" |
| | | style="flex: 1;width: 100px;" |
| | | @change="handleSelectChange" |
| | | > |
| | | <el-option |
| | | v-for="item in documentList" |
| | | :key="item.id" |
| | |
| | | /> |
| | | </el-select> |
| | | <el-input |
| | | v-else |
| | | v-model="currentEditDocName" |
| | | style="flex: 1;width: 100px;" |
| | | disabled |
| | | /> |
| | | <el-input |
| | | v-if="borrowOperationType !== 'edit'" |
| | | v-model="scanContent" |
| | | placeholder="æ«ç è¾å
¥" |
| | | style="width: 100px;" |
| | |
| | | const selectedRows = ref([]); |
| | | const documentList = ref([]); // ææ¡£å表ï¼ç¨äºåé
书ç±éæ© |
| | | const scanContent = ref() // æ«ç å
容 |
| | | const currentEditDocName = ref(''); // ç¼è¾æ¶åå¨çææ¡£åç§° |
| | | // å页ç¸å
³ |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | disabled: (row) => row.borrowStatus === 'å½è¿', |
| | | clickFun: (row) => { |
| | | openBorrowDia('edit', row) |
| | | }, |
| | |
| | | if (type === "edit") { |
| | | // ç¼è¾æ¨¡å¼ï¼å è½½ç°ææ°æ® |
| | | Object.assign(borrowForm, data); |
| | | // åå¨ææ¡£åç§°ç¨äºæ¾ç¤º |
| | | currentEditDocName.value = data.docName || ''; |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼æ¸
空表å |
| | | Object.keys(borrowForm).forEach(key => { |
| | | borrowForm[key] = ""; |
| | | }); |
| | | // 设置é»è®¤ç¶æ |
| | | borrowForm.borrowStatus = "åé
"; |
| | | currentEditDocName.value = ''; // æ¸
空ç¼è¾æ¶çææ¡£åç§° |
| | | // 设置é»è®¤ç¶æ |
| | | borrowForm.borrowStatus = "åé
"; |
| | | // 设置å½åæ¥æä¸ºåé
æ¥æ |
| | | borrowForm.borrowDate = new Date().toISOString().split('T')[0]; |
| | | } |
| | |
| | | proxy.$refs.borrowFormRef.resetFields(); |
| | | borrowDia.value = false; |
| | | scanContent.value = ''; // æ¸
空æ«ç å
容 |
| | | currentEditDocName.value = ''; // æ¸
空ç¼è¾æ¶çææ¡£åç§° |
| | | }; |
| | | |
| | | // æäº¤åé
表å |
| | |
| | | documentForm[key] = ""; |
| | | }); |
| | | documentForm.attachments = []; // æ°å¢æ¨¡å¼ä¸ä¹æ¸
空éä»¶ |
| | | // 设置é»è®¤å¼ - 使ç¨åå
¸æ°æ®ç第ä¸ä¸ªé项ä½ä¸ºé»è®¤å¼ |
| | | // 设置é»è®¤å¼ - ææ¡£ç¶æé»è®¤è®¾ç½®ä¸º"æ£å¸¸" |
| | | if (document_status.value && document_status.value.length > 0) { |
| | | documentForm.docStatus = document_status.value[0].value; |
| | | const normalStatus = document_status.value.find(item => item.label === 'æ£å¸¸'); |
| | | documentForm.docStatus = normalStatus ? normalStatus.value : document_status.value[0].value; |
| | | } |
| | | if (document_urgency.value && document_urgency.value.length > 0) { |
| | | documentForm.urgencyLevel = document_urgency.value[0].value; |
| | | const normalUrgency = document_urgency.value.find(item => item.label === 'æ®é'); |
| | | documentForm.urgencyLevel = normalUrgency ? normalUrgency.value : document_urgency.value[0].value; |
| | | } |
| | | } |
| | | }; |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææ¡£ï¼" prop="borrowId"> |
| | | <!-- <el-select v-model="returnForm.borrowId" placeholder="è¯·éæ©ææ¡£" style="flex: 1;" @change="handleDocumentChange"> |
| | | <el-option |
| | | v-for="item in documentList" |
| | | :key="item.id" |
| | | :label="item.docName || item.name" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> --> |
| | | <div style="display: flex; gap: 10px;"> |
| | | <el-select v-model="returnForm.borrowId" placeholder="è¯·éæ©ææ¡£" style="width: 120px;" @change="handleDocumentChange"> |
| | | <el-select |
| | | v-if="returnOperationType !== 'edit'" |
| | | v-model="returnForm.borrowId" |
| | | placeholder="è¯·éæ©ææ¡£" |
| | | style="width: 120px;" |
| | | @change="handleDocumentChange" |
| | | > |
| | | <el-option |
| | | v-for="item in documentList" |
| | | :key="item.id" |
| | |
| | | /> |
| | | </el-select> |
| | | <el-input |
| | | v-else |
| | | v-model="currentEditDocName" |
| | | style="width: 120px;" |
| | | disabled |
| | | /> |
| | | <el-input |
| | | v-if="returnOperationType !== 'edit'" |
| | | v-model="scanContent" |
| | | placeholder="æ«ç è¾å
¥" |
| | | style="flex: 1;" |
| | |
| | | const documentList = ref([]); // ææ¡£å表 |
| | | const borrowInfoList = ref([]); // åé
ä¿¡æ¯å表 |
| | | const scanContent = ref(); // æ«ç å
容 |
| | | const currentEditDocName = ref(''); // ç¼è¾æ¶åå¨çææ¡£åç§° |
| | | |
| | | // å页ç¸å
³ |
| | | const pagination = reactive({ |
| | |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | disabled: (row) => row.borrowStatus === 'å½è¿', |
| | | clickFun: (row) => { |
| | | openReturnDia('edit', row) |
| | | }, |
| | |
| | | if (type === "edit") { |
| | | // ç¼è¾æ¨¡å¼ï¼å è½½ç°ææ°æ® |
| | | Object.assign(returnForm, data); |
| | | // ç¼è¾æ¨¡å¼ä¸ï¼ææ¡£éæ©åèªå¨å¡«å
åé
人ååºå½è¿æ¥æ |
| | | if (returnForm.borrowId) { |
| | | handleDocumentChange(returnForm.borrowId); |
| | | } |
| | | // åå¨ææ¡£åç§°ç¨äºæ¾ç¤º |
| | | currentEditDocName.value = data.docName || ''; |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼æ¸
空表å |
| | | Object.keys(returnForm).forEach(key => { |
| | | returnForm[key] = ""; |
| | | }); |
| | | currentEditDocName.value = ''; // æ¸
空ç¼è¾æ¶çææ¡£åç§° |
| | | // 设置é»è®¤ç¶æ |
| | | returnForm.borrowStatus = "å½è¿"; |
| | | // 设置å½åæ¥æä¸ºå½è¿æ¥æ |
| | |
| | | returnDia.value = false; |
| | | scanContent.value = ''; // æ¸
空æ«ç å
容 |
| | | borrowInfoList.value = []; // æ¸
空åé
ä¿¡æ¯å表 |
| | | currentEditDocName.value = ''; // æ¸
空ç¼è¾æ¶çææ¡£åç§° |
| | | }; |
| | | |
| | | // æäº¤å½è¿è¡¨å |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="èµäº§ç¼å·:"> |
| | | <el-input v-model="filters.assetCode" placeholder="请è¾å
¥èµäº§ç¼å·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="èµäº§åç§°:"> |
| | | <el-input v-model="filters.assetName" placeholder="请è¾å
¥èµäº§åç§°" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="èµäº§ç±»å«:"> |
| | | <el-select v-model="filters.category" placeholder="è¯·éæ©ç±»å«" clearable style="width: 150px;"> |
| | | <el-option label="æ¿å±å»ºç" value="building" /> |
| | | <el-option label="æºå¨è®¾å¤" value="machine" /> |
| | | <el-option label="è¿è¾å·¥å
·" value="vehicle" /> |
| | | <el-option label="çµå设å¤" value="electronic" /> |
| | | <el-option label="åå
¬å®¶å
·" value="furniture" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="filters.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="å¨ç¨" value="in_use" /> |
| | | <el-option label="é²ç½®" value="idle" /> |
| | | <el-option label="æ¥åº" value="scrapped" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div> |
| | | <el-statistic title="èµäº§åå¼å计" :value="totalOriginalValue" precision="2" prefix="Â¥" /> |
| | | <el-statistic title="ç´¯è®¡ææ§å计" :value="totalDepreciation" precision="2" prefix="Â¥" style="margin-left: 30px;" /> |
| | | <el-statistic title="åå¼å计" :value="totalNetValue" precision="2" prefix="Â¥" style="margin-left: 30px;" /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">æ°å¢èµäº§</el-button> |
| | | <el-button type="warning" @click="handleDepreciation" icon="Money">ææ§è®¡æ</el-button> |
| | | <!-- <el-button @click="handleOut" icon="Download">导åº</el-button> --> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #originalValue="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.originalValue) }}</span> |
| | | </template> |
| | | <template #accumulatedDepreciation="{ row }"> |
| | | <span class="text-warning">Â¥{{ formatMoney(row.accumulatedDepreciation) }}</span> |
| | | </template> |
| | | <template #netValue="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.netValue) }}</span> |
| | | </template> |
| | | <template #category="{ row }"> |
| | | <el-tag>{{ getCategoryLabel(row.category) }}</el-tag> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" :disabled="isView" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç¼å·" prop="assetCode"> |
| | | <el-input v-model="form.assetCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§åç§°" prop="assetName"> |
| | | <el-input v-model="form.assetName" placeholder="请è¾å
¥èµäº§åç§°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç±»å«" prop="category"> |
| | | <el-select v-model="form.category" placeholder="è¯·éæ©èµäº§ç±»å«" style="width: 100%;"> |
| | | <el-option label="æ¿å±å»ºç" value="building" /> |
| | | <el-option label="æºå¨è®¾å¤" value="machine" /> |
| | | <el-option label="è¿è¾å·¥å
·" value="vehicle" /> |
| | | <el-option label="çµå设å¤" value="electronic" /> |
| | | <el-option label="åå
¬å®¶å
·" value="furniture" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·" prop="specification"> |
| | | <el-input v-model="form.specification" placeholder="请è¾å
¥è§æ ¼åå·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è´ç½®æ¥æ" prop="purchaseDate"> |
| | | <el-date-picker v-model="form.purchaseDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§åå¼" prop="originalValue"> |
| | | <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="使ç¨å¹´é" prop="usefulLife"> |
| | | <el-input-number v-model="form.usefulLife" :min="1" :max="50" style="width: 100%;" /> |
| | | <span style="margin-left: 10px;">å¹´</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ®å¼ç" prop="residualRate"> |
| | | <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" /> |
| | | <span style="margin-left: 10px;">%</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç´¯è®¡ææ§"> |
| | | <el-input v-model="form.accumulatedDepreciation" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§åå¼"> |
| | | <el-input v-model="form.netValue" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åæ¾å°ç¹" prop="location"> |
| | | <el-input v-model="form.location" placeholder="请è¾å
¥åæ¾å°ç¹" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="使ç¨é¨é¨" prop="department"> |
| | | <el-input v-model="form.department" placeholder="请è¾å
¥ä½¿ç¨é¨é¨" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¿ç®¡äºº" prop="keeper"> |
| | | <el-input v-model="form.keeper" placeholder="请è¾å
¥ä¿ç®¡äºº" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-select v-model="form.status" placeholder="è¯·éæ©ç¶æ" style="width: 100%;"> |
| | | <el-option label="å¨ç¨" value="in_use" /> |
| | | <el-option label="é²ç½®" value="idle" /> |
| | | <el-option label="æ¥åº" value="scrapped" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button v-if="!isView" type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | | listFixedAssetPage, |
| | | addFixedAsset, |
| | | updateFixedAsset, |
| | | deleteFixedAsset, |
| | | depreciateFixedAsset, |
| | | } from "@/api/financialManagement/fixedAsset"; |
| | | |
| | | defineOptions({ |
| | | name: "åºå®èµäº§", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "èµäº§ç¼å·", prop: "assetCode", width: "130" }, |
| | | { label: "èµäº§åç§°", prop: "assetName", width: "150" }, |
| | | { label: "èµäº§ç±»å«", prop: "category", dataType: "slot", slot: "category" }, |
| | | { label: "è§æ ¼åå·", prop: "specification", width: "120" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", dataType: "slot", slot: "originalValue" }, |
| | | { label: "ç´¯è®¡ææ§", prop: "accumulatedDepreciation", dataType: "slot", slot: "accumulatedDepreciation" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", dataType: "slot", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", dataType: "slot", slot: "status" }, |
| | | { label: "æä½", prop: "operation", dataType: "slot", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const multipleList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const isView = ref(false); |
| | | const currentId = ref(null); |
| | | const selectedIds = computed(() => |
| | | multipleList.value |
| | | .map(item => item?.id) |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | ); |
| | | |
| | | const createDefaultForm = () => ({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | | specification: "", |
| | | purchaseDate: "", |
| | | originalValue: 0, |
| | | usefulLife: 5, |
| | | residualRate: 5, |
| | | accumulatedDepreciation: 0, |
| | | netValue: 0, |
| | | location: "", |
| | | department: "", |
| | | keeper: "", |
| | | status: "in_use", |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | |
| | | const rules = { |
| | | assetName: [{ required: true, message: "请è¾å
¥èµäº§åç§°", trigger: "blur" }], |
| | | category: [{ required: true, message: "è¯·éæ©èµäº§ç±»å«", trigger: "change" }], |
| | | purchaseDate: [{ required: true, message: "è¯·éæ©è´ç½®æ¥æ", trigger: "change" }], |
| | | originalValue: [{ required: true, message: "请è¾å
¥èµäº§åå¼", trigger: "blur" }], |
| | | usefulLife: [{ required: true, message: "请è¾å
¥ä½¿ç¨å¹´é", trigger: "blur" }], |
| | | }; |
| | | |
| | | const totalOriginalValue = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0); |
| | | }); |
| | | |
| | | const totalDepreciation = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedDepreciation), 0); |
| | | }); |
| | | |
| | | const totalNetValue = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.netValue), 0); |
| | | }); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getCategoryLabel = (category) => { |
| | | const map = { |
| | | building: "æ¿å±å»ºç", |
| | | machine: "æºå¨è®¾å¤", |
| | | vehicle: "è¿è¾å·¥å
·", |
| | | electronic: "çµå设å¤", |
| | | furniture: "åå
¬å®¶å
·", |
| | | }; |
| | | return map[category] || category; |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { in_use: "å¨ç¨", idle: "é²ç½®", repair: "ç»´ä¿®ä¸", scrapped: "æ¥åº" }; |
| | | return map[key] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { in_use: "success", idle: "warning", repair: "warning", scrapped: "info" }; |
| | | return map[key] || ""; |
| | | }; |
| | | |
| | | const calculateNetValue = () => { |
| | | const originalValue = Number(form.originalValue || 0); |
| | | const accumulatedDepreciation = Number(form.accumulatedDepreciation || 0); |
| | | form.netValue = Number((originalValue - accumulatedDepreciation).toFixed(2)); |
| | | }; |
| | | |
| | | // èè°çº¦å®ï¼å页忰åºå®ä¸º current/sizeï¼è¿å data.records/data.total |
| | | const getTableData = async () => { |
| | | try { |
| | | const { data } = await listFixedAssetPage({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | assetCode: filters.assetCode, |
| | | assetName: filters.assetName, |
| | | category: filters.category, |
| | | status: filters.status, |
| | | }); |
| | | dataList.value = data?.records || []; |
| | | multipleList.value = []; |
| | | pagination.total = Number(data?.total || 0); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | }; |
| | | |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.assetCode = ""; |
| | | filters.assetName = ""; |
| | | filters.category = ""; |
| | | filters.status = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `GD${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "æ°å¢åºå®èµäº§"; |
| | | Object.assign(form, createDefaultForm(), { |
| | | assetCode: buildAssetCode(), |
| | | purchaseDate: new Date().toISOString().split('T')[0], |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | isView.value = false; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åºå®èµäº§"; |
| | | Object.assign(form, createDefaultForm(), row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | edit(row); |
| | | isView.value = true; |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥åºå®èµäº§åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(async () => { |
| | | // èè°çº¦å®ï¼å 餿¥å£ä½¿ç¨ ids=1&ids=2 |
| | | await deleteFixedAsset([row.id]); |
| | | if (dataList.value.length === 1 && pagination.currentPage > 1) { |
| | | pagination.currentPage -= 1; |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleDepreciation = () => { |
| | | const ids = selectedIds.value; |
| | | const confirmText = ids.length |
| | | ? `确认对éä¸ç ${ids.length} æ¡èµäº§è¿è¡æ¬æææ§è®¡æåï¼` |
| | | : "确认è¿è¡æ¬æææ§è®¡æåï¼"; |
| | | ElMessageBox.confirm(confirmText, "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(async () => { |
| | | await depreciateFixedAsset({ ids }); |
| | | ElMessage.success("ææ§è®¡æå®æ"); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | if (isView.value) { |
| | | dialogVisible.value = false; |
| | | return; |
| | | } |
| | | formRef.value.validate(async valid => { |
| | | if (valid) { |
| | | try { |
| | | calculateNetValue(); |
| | | const payload = { ...form }; |
| | | if (isEdit.value) { |
| | | payload.id = currentId.value; |
| | | await updateFixedAsset(payload); |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | await addFixedAsset(payload); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | await getTableData(); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | |
| | | > div:first-child { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-warning { |
| | | color: #e6a23c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="èµäº§ç¼å·:"> |
| | | <el-input v-model="filters.assetCode" placeholder="请è¾å
¥èµäº§ç¼å·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="èµäº§åç§°:"> |
| | | <el-input v-model="filters.assetName" placeholder="请è¾å
¥èµäº§åç§°" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="èµäº§ç±»å«:"> |
| | | <el-select v-model="filters.category" placeholder="è¯·éæ©ç±»å«" clearable style="width: 150px;"> |
| | | <el-option label="ä¸å©æ" value="patent" /> |
| | | <el-option label="åæ æ" value="trademark" /> |
| | | <el-option label="è使" value="copyright" /> |
| | | <el-option label="软件" value="software" /> |
| | | <el-option label="åå°ä½¿ç¨æ" value="land" /> |
| | | <el-option label="å
¶ä»" value="other" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="filters.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="å¨ç¨" value="in_use" /> |
| | | <el-option label="é²ç½®" value="idle" /> |
| | | <el-option label="å·²æé宿¯" value="amortized" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div> |
| | | <el-statistic title="èµäº§åå¼å计" :value="totalOriginalValue" precision="2" prefix="Â¥" /> |
| | | <el-statistic title="累计æéå计" :value="totalAmortization" precision="2" prefix="Â¥" style="margin-left: 30px;" /> |
| | | <el-statistic title="åå¼å计" :value="totalNetValue" precision="2" prefix="Â¥" style="margin-left: 30px;" /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">æ°å¢èµäº§</el-button> |
| | | <el-button type="warning" @click="handleAmortization" icon="Money">æé计æ</el-button> |
| | | <!-- <el-button @click="handleOut" icon="Download">导åº</el-button> --> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #originalValue="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.originalValue) }}</span> |
| | | </template> |
| | | <template #accumulatedAmortization="{ row }"> |
| | | <span class="text-warning">Â¥{{ formatMoney(row.accumulatedAmortization) }}</span> |
| | | </template> |
| | | <template #netValue="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.netValue) }}</span> |
| | | </template> |
| | | <template #category="{ row }"> |
| | | <el-tag>{{ getCategoryLabel(row.category) }}</el-tag> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" :disabled="isView" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç¼å·" prop="assetCode"> |
| | | <el-input v-model="form.assetCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§åç§°" prop="assetName"> |
| | | <el-input v-model="form.assetName" placeholder="请è¾å
¥èµäº§åç§°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç±»å«" prop="category"> |
| | | <el-select v-model="form.category" placeholder="è¯·éæ©èµäº§ç±»å«" style="width: 100%;"> |
| | | <el-option label="ä¸å©æ" value="patent" /> |
| | | <el-option label="åæ æ" value="trademark" /> |
| | | <el-option label="è使" value="copyright" /> |
| | | <el-option label="软件" value="software" /> |
| | | <el-option label="åå°ä½¿ç¨æ" value="land" /> |
| | | <el-option label="å
¶ä»" value="other" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è¯ä¹¦ç¼å·" prop="certificateNo"> |
| | | <el-input v-model="form.certificateNo" placeholder="请è¾å
¥è¯ä¹¦ç¼å·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å徿¥æ" prop="acquisitionDate"> |
| | | <el-date-picker v-model="form.acquisitionDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§åå¼" prop="originalValue"> |
| | | <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æéå¹´é" prop="amortizationPeriod"> |
| | | <el-input-number v-model="form.amortizationPeriod" :min="1" :max="50" style="width: 100%;" /> |
| | | <span style="margin-left: 10px;">å¹´</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ®å¼ç" prop="residualRate"> |
| | | <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" /> |
| | | <span style="margin-left: 10px;">%</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="累计æé"> |
| | | <el-input v-model="form.accumulatedAmortization" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§åå¼"> |
| | | <el-input v-model="form.netValue" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æææè³" prop="validityDate"> |
| | | <el-date-picker v-model="form.validityDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-select v-model="form.status" placeholder="è¯·éæ©ç¶æ" style="width: 100%;"> |
| | | <el-option label="å¨ç¨" value="in_use" /> |
| | | <el-option label="é²ç½®" value="idle" /> |
| | | <el-option label="å·²æé宿¯" value="amortized" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="èµäº§æè¿°" prop="description"> |
| | | <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请è¾å
¥èµäº§æè¿°" /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button v-if="!isView" type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | | listIntangibleAssetPage, |
| | | addIntangibleAsset, |
| | | updateIntangibleAsset, |
| | | deleteIntangibleAsset, |
| | | amortizeIntangibleAsset, |
| | | } from "@/api/financialManagement/intangibleAsset"; |
| | | |
| | | defineOptions({ |
| | | name: "æ å½¢èµäº§", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "èµäº§ç¼å·", prop: "assetCode", width: "130" }, |
| | | { label: "èµäº§åç§°", prop: "assetName", width: "150" }, |
| | | { label: "èµäº§ç±»å«", prop: "category", dataType: "slot", slot: "category" }, |
| | | { label: "è¯ä¹¦ç¼å·", prop: "certificateNo", width: "150" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", dataType: "slot", slot: "originalValue" }, |
| | | { label: "累计æé", prop: "accumulatedAmortization", dataType: "slot", slot: "accumulatedAmortization" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", dataType: "slot", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", dataType: "slot", slot: "status" }, |
| | | { label: "æä½", prop: "operation", dataType: "slot", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const multipleList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const isView = ref(false); |
| | | const currentId = ref(null); |
| | | const selectedIds = computed(() => |
| | | multipleList.value |
| | | .map(item => item?.id) |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | ); |
| | | |
| | | const createDefaultForm = () => ({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | | certificateNo: "", |
| | | acquisitionDate: "", |
| | | originalValue: 0, |
| | | amortizationPeriod: 10, |
| | | residualRate: 0, |
| | | accumulatedAmortization: 0, |
| | | netValue: 0, |
| | | validityDate: "", |
| | | status: "in_use", |
| | | description: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | |
| | | const rules = { |
| | | assetName: [{ required: true, message: "请è¾å
¥èµäº§åç§°", trigger: "blur" }], |
| | | category: [{ required: true, message: "è¯·éæ©èµäº§ç±»å«", trigger: "change" }], |
| | | acquisitionDate: [{ required: true, message: "è¯·éæ©å徿¥æ", trigger: "change" }], |
| | | originalValue: [{ required: true, message: "请è¾å
¥èµäº§åå¼", trigger: "blur" }], |
| | | amortizationPeriod: [{ required: true, message: "请è¾å
¥æéå¹´é", trigger: "blur" }], |
| | | }; |
| | | |
| | | const totalOriginalValue = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0); |
| | | }); |
| | | |
| | | const totalAmortization = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedAmortization), 0); |
| | | }); |
| | | |
| | | const totalNetValue = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.netValue), 0); |
| | | }); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getCategoryLabel = (category) => { |
| | | const map = { |
| | | patent: "ä¸å©æ", |
| | | trademark: "åæ æ", |
| | | copyright: "è使", |
| | | software: "软件", |
| | | land: "åå°ä½¿ç¨æ", |
| | | other: "å
¶ä»", |
| | | }; |
| | | return map[category] || category; |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { |
| | | in_use: "å¨ç¨", |
| | | idle: "é²ç½®", |
| | | expired: "已尿", |
| | | amortized: "å·²æé宿¯", |
| | | }; |
| | | return map[key] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { in_use: "success", idle: "warning", expired: "warning", amortized: "info" }; |
| | | return map[key] || ""; |
| | | }; |
| | | |
| | | const calculateNetValue = () => { |
| | | const originalValue = Number(form.originalValue || 0); |
| | | const accumulatedAmortization = Number(form.accumulatedAmortization || 0); |
| | | form.netValue = Number((originalValue - accumulatedAmortization).toFixed(2)); |
| | | }; |
| | | |
| | | // èè°çº¦å®ï¼å页忰åºå®ä¸º current/sizeï¼è¿å data.records/data.total |
| | | const getTableData = async () => { |
| | | try { |
| | | const { data } = await listIntangibleAssetPage({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | assetCode: filters.assetCode, |
| | | assetName: filters.assetName, |
| | | category: filters.category, |
| | | status: filters.status, |
| | | }); |
| | | dataList.value = data?.records || []; |
| | | multipleList.value = []; |
| | | pagination.total = Number(data?.total || 0); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | }; |
| | | |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.assetCode = ""; |
| | | filters.assetName = ""; |
| | | filters.category = ""; |
| | | filters.status = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `WX${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "æ°å¢æ å½¢èµäº§"; |
| | | Object.assign(form, createDefaultForm(), { |
| | | assetCode: buildAssetCode(), |
| | | acquisitionDate: new Date().toISOString().split('T')[0], |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | isView.value = false; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ å½¢èµäº§"; |
| | | Object.assign(form, createDefaultForm(), row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | edit(row); |
| | | isView.value = true; |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥æ å½¢èµäº§åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(async () => { |
| | | // èè°çº¦å®ï¼å 餿¥å£ä½¿ç¨ ids=1&ids=2 |
| | | await deleteIntangibleAsset([row.id]); |
| | | if (dataList.value.length === 1 && pagination.currentPage > 1) { |
| | | pagination.currentPage -= 1; |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleAmortization = () => { |
| | | const ids = selectedIds.value; |
| | | const confirmText = ids.length |
| | | ? `确认对éä¸ç ${ids.length} æ¡èµäº§è¿è¡æ¬ææé计æåï¼` |
| | | : "确认è¿è¡æ¬ææé计æåï¼"; |
| | | ElMessageBox.confirm(confirmText, "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(async () => { |
| | | await amortizeIntangibleAsset({ ids }); |
| | | ElMessage.success("æé计æå®æ"); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | if (isView.value) { |
| | | dialogVisible.value = false; |
| | | return; |
| | | } |
| | | formRef.value.validate(async valid => { |
| | | if (valid) { |
| | | try { |
| | | calculateNetValue(); |
| | | const payload = { ...form }; |
| | | if (isEdit.value) { |
| | | payload.id = currentId.value; |
| | | await updateIntangibleAsset(payload); |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | await addIntangibleAsset(payload); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | await getTableData(); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | |
| | | > div:first-child { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-warning { |
| | | color: #e6a23c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| | |
| | | </PIMTable> |
| | | </div> |
| | | <Modal ref="modalRef" @success="getTableData"></Modal> |
| | | <FileListDialog |
| | | ref="fileListRef" |
| | | v-model="fileListDialogVisible" |
| | | :show-upload-button="true" |
| | | :show-delete-button="true" |
| | | :upload-method="handleUpload" |
| | | :delete-method="handleFileDelete" |
| | | /> |
| | | <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="account_expense" :record-id="recordId" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { usePaginationApi } from "@/hooks/usePaginationApi"; |
| | | import { listPage, delAccountExpense, fileListPage, fileAdd, fileDel } from "@/api/financialManagement/expenseManagement"; |
| | | import { onMounted, getCurrentInstance, ref, computed } from "vue"; |
| | | import { listPage, delAccountExpense } from "@/api/financialManagement/expenseManagement"; |
| | | import {onMounted, getCurrentInstance, ref, computed, defineAsyncComponent} from "vue"; |
| | | import Modal from "./Modal.vue"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import FileListDialog from "@/components/Dialog/FileListDialog.vue"; |
| | | import request from "@/utils/request"; |
| | | import { getToken } from "@/utils/auth"; |
| | | const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); |
| | | |
| | | defineOptions({ |
| | | name: "æ¯åºç®¡ç", |
| | |
| | | const modalRef = ref(); |
| | | const { checkout_payment } = proxy.useDict("checkout_payment"); |
| | | const { expense_types } = proxy.useDict("expense_types"); |
| | | const fileListRef = ref(null); |
| | | const fileListDialogVisible = ref(false); |
| | | const currentFileRow = ref(null); |
| | | const accountType = ref('æ¯åº'); |
| | | |
| | | const { |
| | |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // æå¼éä»¶å¼¹çª |
| | | const recordId =ref(0) |
| | | const fileDialogVisible = ref(false) |
| | | |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | const openFilesFormDia = async (row) => { |
| | | currentFileRow.value = row; |
| | | accountType.value = 'æ¯åº'; |
| | | try { |
| | | const res = await fileListPage({ |
| | | accountId: row.id, |
| | | accountType: accountType.value, |
| | | current: 1, |
| | | size: 100 |
| | | }); |
| | | if (res.code === 200 && fileListRef.value) { |
| | | // å°æ°æ®è½¬æ¢ä¸º FileListDialog éè¦çæ ¼å¼ |
| | | const fileList = (res.data?.records || []).map(item => ({ |
| | | name: item.name, |
| | | url: item.url, |
| | | id: item.id, |
| | | ...item |
| | | })); |
| | | fileListRef.value.open(fileList); |
| | | fileListDialogVisible.value = true; |
| | | } |
| | | } catch (error) { |
| | | proxy.$modal.msgError("è·åéä»¶å表失败"); |
| | | } |
| | | }; |
| | | |
| | | // ä¸ä¼ éä»¶ |
| | | const handleUpload = async () => { |
| | | if (!currentFileRow.value) { |
| | | proxy.$modal.msgWarning("请å
éæ©æ°æ®"); |
| | | return null; |
| | | } |
| | | |
| | | return new Promise((resolve) => { |
| | | // å建ä¸ä¸ªéèçæä»¶è¾å
¥å
ç´ |
| | | const input = document.createElement('input'); |
| | | input.type = 'file'; |
| | | input.style.display = 'none'; |
| | | input.onchange = async (e) => { |
| | | const file = e.target.files[0]; |
| | | if (!file) { |
| | | resolve(null); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // ä½¿ç¨ FormData ä¸ä¼ æä»¶ |
| | | const formData = new FormData(); |
| | | formData.append('file', file); |
| | | |
| | | const uploadRes = await request({ |
| | | url: '/file/upload', |
| | | method: 'post', |
| | | data: formData, |
| | | headers: { |
| | | 'Content-Type': 'multipart/form-data', |
| | | Authorization: `Bearer ${getToken()}` |
| | | } |
| | | }); |
| | | |
| | | if (uploadRes.code === 200) { |
| | | // ä¿åéä»¶ä¿¡æ¯ |
| | | const fileData = { |
| | | accountId: currentFileRow.value.id, |
| | | accountType: accountType.value, |
| | | name: uploadRes.data.originalName || file.name, |
| | | url: uploadRes.data.tempPath || uploadRes.data.url |
| | | }; |
| | | |
| | | const saveRes = await fileAdd(fileData); |
| | | if (saveRes.code === 200) { |
| | | proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); |
| | | // éæ°å è½½æä»¶å表 |
| | | const listRes = await fileListPage({ |
| | | accountId: currentFileRow.value.id, |
| | | accountType: accountType.value, |
| | | current: 1, |
| | | size: 100 |
| | | }); |
| | | if (listRes.code === 200 && fileListRef.value) { |
| | | const fileList = (listRes.data?.records || []).map(item => ({ |
| | | name: item.name, |
| | | url: item.url, |
| | | id: item.id, |
| | | ...item |
| | | })); |
| | | fileListRef.value.setList(fileList); |
| | | } |
| | | // è¿åæ°æä»¶ä¿¡æ¯ |
| | | resolve({ |
| | | name: fileData.name, |
| | | url: fileData.url, |
| | | id: saveRes.data?.id |
| | | }); |
| | | } else { |
| | | proxy.$modal.msgError(saveRes.msg || "æä»¶ä¿å失败"); |
| | | resolve(null); |
| | | } |
| | | } else { |
| | | proxy.$modal.msgError(uploadRes.msg || "æä»¶ä¸ä¼ 失败"); |
| | | resolve(null); |
| | | } |
| | | } catch (error) { |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); |
| | | resolve(null); |
| | | } finally { |
| | | document.body.removeChild(input); |
| | | } |
| | | }; |
| | | |
| | | document.body.appendChild(input); |
| | | input.click(); |
| | | }); |
| | | }; |
| | | |
| | | // å é¤éä»¶ |
| | | const handleFileDelete = async (row) => { |
| | | try { |
| | | const res = await fileDel([row.id]); |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | // éæ°å è½½æä»¶å表 |
| | | if (currentFileRow.value && fileListRef.value) { |
| | | const listRes = await fileListPage({ |
| | | accountId: currentFileRow.value.id, |
| | | accountType: accountType.value, |
| | | current: 1, |
| | | size: 100 |
| | | }); |
| | | if (listRes.code === 200) { |
| | | const fileList = (listRes.data?.records || []).map(item => ({ |
| | | name: item.name, |
| | | url: item.url, |
| | | id: item.id, |
| | | ...item |
| | | })); |
| | | fileListRef.value.setList(fileList); |
| | | } |
| | | } |
| | | return true; // è¿å true 表示å 餿åï¼ç»ä»¶ä¼æ´æ°å表 |
| | | } else { |
| | | proxy.$modal.msgError(res.msg || "å é¤å¤±è´¥"); |
| | | return false; |
| | | } |
| | | } catch (error) { |
| | | proxy.$modal.msgError("å é¤å¤±è´¥"); |
| | | return false; |
| | | } |
| | | }; |
| | | recordId.value = row.id |
| | | fileDialogVisible.value = true |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form-item label="ç§ç®ç¼ç :"> |
| | | <el-input v-model="filters.subjectCode" |
| | | placeholder="请è¾å
¥ç§ç®ç¼ç " |
| | | clearable |
| | | style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®åç§°:"> |
| | | <el-input v-model="filters.subjectName" |
| | | placeholder="请è¾å
¥ç§ç®åç§°" |
| | | clearable |
| | | style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®ç±»å:"> |
| | | <el-select v-model="filters.subjectType" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 200px;"> |
| | | <el-option label="èµäº§ç±»" |
| | | value="èµäº§ç±»" /> |
| | | <el-option label="è´åºç±»" |
| | | value="è´åºç±»" /> |
| | | <el-option label="æçç±»" |
| | | value="æçç±»" /> |
| | | <el-option label="ææ¬ç±»" |
| | | value="ææ¬ç±»" /> |
| | | <el-option label="æçç±»" |
| | | value="æçç±»" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="add" |
| | | icon="Plus">æ°å¢</el-button> |
| | | <!-- <el-button @click="handleOut" |
| | | icon="Download">导åº</el-button> --> |
| | | </div> |
| | | </div> |
| | | <el-table ref="tableRef" |
| | | v-loading="loading" |
| | | :data="dataList" |
| | | row-key="id" |
| | | :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" |
| | | height="calc(100vh - 280px)" |
| | | border |
| | | stripe |
| | | highlight-current-row |
| | | class="subject-table"> |
| | | <el-table-column label="ç§ç®ç¼ç " prop="subjectCode" width="140"> |
| | | <template #default="scope"> |
| | | <span class="subject-code">{{ scope.row.subjectCode }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç§ç®åç§°" prop="subjectName" min-width="180"> |
| | | <template #default="scope"> |
| | | <span class="subject-name" :class="{ 'is-parent': scope.row.children?.length > 0 }"> |
| | | {{ scope.row.subjectName }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç§ç®ç±»å" prop="subjectType" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag size="small" :type="getSubjectTypeType(scope.row.subjectType)"> |
| | | {{ scope.row.subjectType }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢æ¹å" prop="balanceDirection" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag size="small" :type="scope.row.balanceDirection === 'åæ¹' ? 'primary' : 'danger'"> |
| | | {{ scope.row.balanceDirection }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¶æ" prop="status" width="80" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag size="small" :type="scope.row.status === 0 || scope.row.status === '0' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 0 || scope.row.status === '0' ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" prop="remark" show-overflow-tooltip min-width="150" /> |
| | | <el-table-column label="æä½" align="center" fixed="right" width="240"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" icon="Plus" @click="addChild(scope.row)">æ°å¢</el-button> |
| | | <el-button link type="primary" icon="Edit" @click="edit(scope.row)">ç¼è¾</el-button> |
| | | <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <FormDialog :title="dialogTitle" |
| | | v-model="dialogVisible" |
| | | width="600px" |
| | | @confirm="submitForm" |
| | | @cancel="dialogVisible = false"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-form-item label="ç¶çº§ç§ç®"> |
| | | <el-input :model-value="parentSubjectLabel" |
| | | disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®ç¼ç " |
| | | prop="subjectCode"> |
| | | <el-input v-model="form.subjectCode" |
| | | placeholder="请è¾å
¥ç§ç®ç¼ç " /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®åç§°" |
| | | prop="subjectName"> |
| | | <el-input v-model="form.subjectName" |
| | | placeholder="请è¾å
¥ç§ç®åç§°" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®ç±»å" |
| | | prop="subjectType"> |
| | | <el-select v-model="form.subjectType" |
| | | placeholder="è¯·éæ©ç§ç®ç±»å" |
| | | style="width: 100%;"> |
| | | <el-option label="èµäº§ç±»" |
| | | value="èµäº§ç±»" /> |
| | | <el-option label="è´åºç±»" |
| | | value="è´åºç±»" /> |
| | | <el-option label="æçç±»" |
| | | value="æçç±»" /> |
| | | <el-option label="ææ¬ç±»" |
| | | value="ææ¬ç±»" /> |
| | | <el-option label="æçç±»" |
| | | value="æçç±»" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ä½é¢æ¹å" |
| | | prop="balanceDirection"> |
| | | <el-radio-group v-model="form.balanceDirection"> |
| | | <el-radio label="åæ¹">åæ¹</el-radio> |
| | | <el-radio label="è´·æ¹">è´·æ¹</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" |
| | | prop="status"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio :label="0">å¯ç¨</el-radio> |
| | | <el-radio :label="1">ç¦ç¨</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" |
| | | @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance, nextTick } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | | listAccountSubject, |
| | | addAccountSubject, |
| | | updateAccountSubject, |
| | | delAccountSubject, |
| | | exportAccountSubject, |
| | | } from "@/api/financialManagement/accountSubject"; |
| | | |
| | | defineOptions({ |
| | | name: "æ»å¸ç§ç®", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const filters = reactive({ |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "ç§ç®ç¼ç ", prop: "subjectCode", width: "120" }, |
| | | { label: "ç§ç®åç§°", prop: "subjectName", width: "150" }, |
| | | { label: "ç§ç®ç±»å", prop: "subjectType" }, |
| | | { |
| | | label: "ä½é¢æ¹å", |
| | | prop: "balanceDirection", |
| | | dataType: "tag", |
| | | formatData: value => { |
| | | if (value === "åæ¹") { |
| | | return "åæ¹"; |
| | | } |
| | | return "è´·æ¹"; |
| | | }, |
| | | formatType: value => { |
| | | if (value === "åæ¹") { |
| | | return "primary"; |
| | | } |
| | | return "danger"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "ç¶æ", |
| | | prop: "status", |
| | | dataType: "tag", |
| | | formatData: value => { |
| | | if (value === 0 || value === "0") { |
| | | return "å¯ç¨"; |
| | | } |
| | | return "ç¦ç¨"; |
| | | }, |
| | | formatType: value => { |
| | | if (value === 0 || value === "0") { |
| | | return "success"; |
| | | } |
| | | return "info"; |
| | | }, |
| | | }, |
| | | |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: "220", |
| | | operation: [ |
| | | { |
| | | name: "æ°å¢", |
| | | type: "primary", |
| | | clickFun: row => { |
| | | addChild(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "primary", |
| | | clickFun: row => { |
| | | edit(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å é¤", |
| | | type: "danger", |
| | | clickFun: row => { |
| | | handleDelete(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const parentSubjectLabel = ref("顶级ç§ç®"); |
| | | const formRef = ref(null); |
| | | const tableRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const loading = ref(false); |
| | | |
| | | const form = reactive({ |
| | | id: undefined, |
| | | parentId: null, |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | | balanceDirection: "åæ¹", |
| | | status: 0, |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | subjectCode: [{ required: true, message: "请è¾å
¥ç§ç®ç¼ç ", trigger: "blur" }], |
| | | subjectName: [{ required: true, message: "请è¾å
¥ç§ç®åç§°", trigger: "blur" }], |
| | | subjectType: [ |
| | | { required: true, message: "è¯·éæ©ç§ç®ç±»å", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | const getSubjectTypeType = type => { |
| | | const map = { |
| | | èµäº§ç±»: "success", |
| | | è´åºç±»: "danger", |
| | | æçç±»: "warning", |
| | | ææ¬ç±»: "info", |
| | | æçç±»: "primary", |
| | | }; |
| | | return map[type] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | loading.value = true; |
| | | const query = { |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | ...filters, |
| | | }; |
| | | listAccountSubject(query).then(response => { |
| | | dataList.value = response.data.records || []; |
| | | loading.value = false; |
| | | }).catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.subjectCode = ""; |
| | | filters.subjectName = ""; |
| | | filters.subjectType = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = obj => { |
| | | pagination.currentPage = obj.page; |
| | | pagination.pageSize = obj.limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildParentSubjectLabel = parentRow => { |
| | | if (!parentRow) { |
| | | return "顶级ç§ç®"; |
| | | } |
| | | const code = parentRow.subjectCode || ""; |
| | | const name = parentRow.subjectName || ""; |
| | | return `${code} ${name}`.trim(); |
| | | }; |
| | | |
| | | const resetForm = ({ parentId = null, parentRow = null } = {}) => { |
| | | Object.assign(form, { |
| | | id: undefined, |
| | | parentId, |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | | balanceDirection: "åæ¹", |
| | | status: 0, |
| | | remark: "", |
| | | }); |
| | | parentSubjectLabel.value = buildParentSubjectLabel(parentRow); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢ç§ç®"; |
| | | resetForm({ parentId: null, parentRow: null }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const addChild = row => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢åç§ç®"; |
| | | resetForm({ parentId: row.id, parentRow: row }); |
| | | form.subjectType = row.subjectType || ""; |
| | | form.balanceDirection = row.balanceDirection || "åæ¹"; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const findSubjectById = (nodes, id) => { |
| | | for (const item of nodes || []) { |
| | | if (item.id === id) { |
| | | return item; |
| | | } |
| | | if (item.children && item.children.length > 0) { |
| | | const found = findSubjectById(item.children, id); |
| | | if (found) { |
| | | return found; |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const edit = row => { |
| | | isEdit.value = true; |
| | | dialogTitle.value = "ç¼è¾ç§ç®"; |
| | | Object.assign(form, row); |
| | | form.parentId = row.parentId ?? null; |
| | | const parentRow = |
| | | row.parentId === null || row.parentId === undefined |
| | | ? null |
| | | : findSubjectById(dataList.value, row.parentId); |
| | | parentSubjectLabel.value = parentRow |
| | | ? buildParentSubjectLabel(parentRow) |
| | | : row.parentId |
| | | ? `ä¸çº§ID: ${row.parentId}` |
| | | : buildParentSubjectLabel(null); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (isEdit.value) { |
| | | updateAccountSubject(form).then(() => { |
| | | ElMessage.success("ç¼è¾æå"); |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | }); |
| | | } else { |
| | | addAccountSubject(form).then(() => { |
| | | ElMessage.success("æ°å¢æå"); |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = row => { |
| | | const ids = row.id; |
| | | ElMessageBox.confirm("确认å é¤è¯¥ç§ç®åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | return delAccountSubject(ids); |
| | | }) |
| | | .then(() => { |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | proxy.download( |
| | | "accountSubject/export", |
| | | { |
| | | ...filters, |
| | | }, |
| | | `account_subject_${new Date().getTime()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .subject-table { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | |
| | | :deep(.el-table__row) { |
| | | transition: background-color 0.3s; |
| | | } |
| | | |
| | | :deep(.el-table__row:hover) { |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .subject-code { |
| | | color: #606266; |
| | | } |
| | | |
| | | .subject-name { |
| | | font-weight: 500; |
| | | |
| | | &.is-parent { |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="å票代ç :"> |
| | | <el-input v-model="filters.invoiceCode" placeholder="请è¾å
¥å票代ç " clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="å票å·ç :"> |
| | | <el-input v-model="filters.invoiceNo" placeholder="请è¾å
¥å票å·ç " clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="认è¯ç¶æ:"> |
| | | <el-select v-model="filters.certifyStatus" placeholder="è¯·éæ©è®¤è¯ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="æªè®¤è¯" value="uncertified" /> |
| | | <el-option label="已认è¯" value="certified" /> |
| | | <el-option label="认è¯å¤±è´¥" value="failed" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div> |
| | | <el-button type="success" @click="handleBatchCertify" icon="Check" :disabled="selectedRows.length === 0">æ¹é认è¯</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">å½å
¥å票</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #amount="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #taxAmount="{ row }"> |
| | | <span class="text-danger">Â¥{{ formatMoney(row.taxAmount) }}</span> |
| | | </template> |
| | | <template #totalAmount="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.totalAmount) }}</span> |
| | | </template> |
| | | <template #certifyStatus="{ row }"> |
| | | <el-tag :type="getCertifyStatusType(row.certifyStatus)">{{ getCertifyStatusLabel(row.certifyStatus) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleCertify(row)" v-if="row.certifyStatus === 'uncertified'">认è¯</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票代ç " prop="invoiceCode"> |
| | | <el-input v-model="form.invoiceCode" placeholder="请è¾å
¥å票代ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票å·ç " prop="invoiceNo"> |
| | | <el-input v-model="form.invoiceNo" placeholder="请è¾å
¥å票å·ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºå" prop="supplierId"> |
| | | <el-select v-model="form.supplierId" placeholder="è¯·éæ©ä¾åºå" style="width: 100%;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¼ç¥¨æ¥æ" prop="invoiceDate"> |
| | | <el-date-picker v-model="form.invoiceDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="éé¢(ä¸å«ç¨)" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ç¨ç" prop="taxRate"> |
| | | <el-select v-model="form.taxRate" placeholder="è¯·éæ©ç¨ç" style="width: 100%;" @change="calculateTax"> |
| | | <el-option |
| | | v-for="dict in tax_rate" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="Number(dict.value)" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ç¨é¢"> |
| | | <el-input v-model="form.taxAmount" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="认è¯ç¶æ" prop="certifyStatus"> |
| | | <el-select v-model="form.certifyStatus" placeholder="è¯·éæ©è®¤è¯ç¶æ" style="width: 100%;" disabled> |
| | | <el-option label="æªè®¤è¯" value="uncertified" /> |
| | | <el-option label="已认è¯" value="certified" /> |
| | | <el-option label="认è¯å¤±è´¥" value="failed" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è®¤è¯æ¥æ" prop="certifyDate"> |
| | | <el-date-picker v-model="form.certifyDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å票å
容" prop="content"> |
| | | <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请è¾å
¥å票å
容" /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | |
| | | defineOptions({ |
| | | name: "è¿é¡¹å票", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { tax_rate } = proxy.useDict("tax_rate"); |
| | | |
| | | const filters = reactive({ |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | supplierId: "", |
| | | certifyStatus: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "å票代ç ", prop: "invoiceCode", width: "130" }, |
| | | { label: "å票å·ç ", prop: "invoiceNo", width: "120" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "å¼ç¥¨æ¥æ", prop: "invoiceDate", width: "120" }, |
| | | { label: "éé¢", prop: "amount", slot: "amount" }, |
| | | { label: "ç¨é¢", prop: "taxAmount", slot: "taxAmount" }, |
| | | { label: "ä»·ç¨å计", prop: "totalAmount", slot: "totalAmount" }, |
| | | { label: "认è¯ç¶æ", prop: "certifyStatus", slot: "certifyStatus" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const selectedRows = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "åäº¬åææä¾åºå" }, |
| | | { id: 2, name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { id: 3, name: "广å·å
è£
ææå" }, |
| | | { id: 4, name: "æ·±å³äºéé
ä»¶å
¬å¸" }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | supplierId: "", |
| | | invoiceDate: "", |
| | | amount: 0, |
| | | taxRate: 13, |
| | | taxAmount: 0, |
| | | totalAmount: 0, |
| | | certifyStatus: "uncertified", |
| | | certifyDate: "", |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | invoiceCode: [{ required: true, message: "请è¾å
¥å票代ç ", trigger: "blur" }], |
| | | invoiceNo: [{ required: true, message: "请è¾å
¥å票å·ç ", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], |
| | | invoiceDate: [{ required: true, message: "è¯·éæ©å¼ç¥¨æ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éé¢", trigger: "blur" }], |
| | | taxRate: [{ required: true, message: "è¯·éæ©ç¨ç", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", supplierId: 1, supplierName: "åäº¬åææä¾åºå", invoiceDate: "2024-01-08", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, certifyStatus: "certified", certifyDate: "2024-01-15", content: "åææéè´", remark: "" }, |
| | | { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", supplierId: 2, supplierName: "䏿µ·çµåå
å¨ä»¶å
¬å¸", invoiceDate: "2024-01-10", amount: 12000, taxRate: 13, taxAmount: 1560, totalAmount: 13560, certifyStatus: "uncertified", certifyDate: "", content: "çµåå
å¨ä»¶", remark: "" }, |
| | | { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", supplierId: 3, supplierName: "广å·å
è£
ææå", invoiceDate: "2024-01-12", amount: 3500, taxRate: 13, taxAmount: 455, totalAmount: 3955, certifyStatus: "certified", certifyDate: "2024-01-18", content: "å
è£
ææ", remark: "" }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const calculateTax = () => { |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2)); |
| | | }; |
| | | |
| | | const getCertifyStatusLabel = (status) => { |
| | | const map = { uncertified: "æªè®¤è¯", certified: "已认è¯", failed: "认è¯å¤±è´¥" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getCertifyStatusType = (status) => { |
| | | const map = { uncertified: "info", certified: "success", failed: "danger" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.invoiceCode) { |
| | | result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode)); |
| | | } |
| | | if (filters.invoiceNo) { |
| | | result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo)); |
| | | } |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | if (filters.certifyStatus) { |
| | | result = result.filter(item => item.certifyStatus === filters.certifyStatus); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.invoiceCode = ""; |
| | | filters.invoiceNo = ""; |
| | | filters.supplierId = ""; |
| | | filters.certifyStatus = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "å½å
¥å票"; |
| | | Object.assign(form, { |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | supplierId: "", |
| | | invoiceDate: new Date().toISOString().split('T')[0], |
| | | amount: 0, |
| | | taxRate: 13, |
| | | taxAmount: 0, |
| | | totalAmount: 0, |
| | | certifyStatus: "uncertified", |
| | | certifyDate: "", |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾å票"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çå票: ${row.invoiceCode}-${row.invoiceNo}`); |
| | | }; |
| | | |
| | | const handleCertify = (row) => { |
| | | ElMessageBox.confirm("确认认è¯è¯¥å票åï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].certifyStatus = "certified"; |
| | | mockData[index].certifyDate = new Date().toISOString().split('T')[0]; |
| | | } |
| | | ElMessage.success("è®¤è¯æå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleBatchCertify = () => { |
| | | ElMessageBox.confirm(`确认æ¹é认è¯éä¸ç ${selectedRows.value.length} å¼ å票åï¼`, "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | selectedRows.value.forEach(row => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1 && mockData[index].certifyStatus === "uncertified") { |
| | | mockData[index].certifyStatus = "certified"; |
| | | mockData[index].certifyDate = new Date().toISOString().split('T')[0]; |
| | | } |
| | | }); |
| | | ElMessage.success("æ¹éè®¤è¯æå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const supplier = supplierList.find(item => item.id === form.supplierId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, supplierName: supplier?.name }); |
| | | ElMessage.success("å½å
¥æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| src/views/financialManagement/payable/payment.vue
src/views/financialManagement/payable/paymentApply.vue
src/views/financialManagement/payable/purchaseIn.vue
src/views/financialManagement/payable/purchaseReturn.vue
src/views/financialManagement/payable/reconciliation.vue
src/views/financialManagement/receivable/invoiceApply.vue
src/views/financialManagement/receivable/outputInvoice.vue
src/views/financialManagement/receivable/receipt.vue
src/views/financialManagement/receivable/reconciliation.vue
src/views/financialManagement/receivable/salesOut.vue
src/views/financialManagement/receivable/salesReturn.vue
src/views/financialManagement/revenueManagement/index.vue
src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue
src/views/financialManagement/voucher/detailLedger.vue
src/views/financialManagement/voucher/generalLedger.vue
src/views/financialManagement/voucher/index.vue
src/views/index.vue
src/views/inventoryManagement/dispatchLog/Record.vue
src/views/inventoryManagement/dispatchLog/index.vue
src/views/inventoryManagement/receiptManagement/Record.vue
src/views/inventoryManagement/receiptManagement/index.vue
src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
src/views/inventoryManagement/stockManagement/New.vue
src/views/inventoryManagement/stockManagement/Qualified.vue
src/views/inventoryManagement/stockManagement/Record.vue
src/views/inventoryManagement/stockManagement/Subtract.vue
src/views/inventoryManagement/stockReport/index.vue
src/views/lavorissue/ledger/filesDia.vue
src/views/lavorissue/statistics/index.vue
src/views/oaSystem/projectManagement/components/milestoneList.vue (已删除)
src/views/oaSystem/projectManagement/components/phaseGoalList.vue (已删除)
src/views/oaSystem/projectManagement/components/projectForm.vue (已删除)
src/views/oaSystem/projectManagement/components/taskTree.vue (已删除)
src/views/oaSystem/projectManagement/index.vue (已删除)
src/views/oaSystem/projectManagement/projectDetail.vue (已删除)
src/views/personnelManagement/contractManagement/filesDia.vue
src/views/personnelManagement/contractManagement/index.vue
src/views/personnelManagement/dimission/components/formDia.vue
src/views/personnelManagement/dimission/index.vue
src/views/personnelManagement/employeeRecord/index.vue
src/views/personnelManagement/socialSecuritySet/index.vue
src/views/procurementManagement/paymentEntry/index.vue
src/views/procurementManagement/paymentLedger/index.vue
src/views/procurementManagement/procurementInvoiceLedger/index.vue
src/views/procurementManagement/procurementLedger/fileList.vue
src/views/procurementManagement/procurementLedger/index.vue
src/views/procurementManagement/procurementReport/index.vue
src/views/procurementManagement/purchaseReturnOrder/New.vue
src/views/procurementManagement/purchaseReturnOrder/ProductList.vue
src/views/procurementManagement/purchaseReturnOrder/index.vue
src/views/productionManagement/processRoute/New.vue
src/views/productionManagement/processRoute/index.vue
src/views/productionManagement/processRoute/processRouteItem/index.vue
src/views/productionManagement/processStatistics/index.vue
src/views/productionManagement/productStructure/Detail/index.vue
src/views/productionManagement/productStructure/index.vue
src/views/productionManagement/productionCosting/index.vue
src/views/productionManagement/productionOrder/New.vue
src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue
src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue
src/views/productionManagement/productionOrder/index.vue
src/views/productionManagement/productionProcess/index.vue
src/views/productionManagement/productionReporting/index.vue
src/views/productionManagement/productionTraceability/index.vue
src/views/productionManagement/workOrder/components/filesDia.vue
src/views/productionManagement/workOrder/index.vue
src/views/productionManagement/workOrderEdit/index.vue
src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
src/views/productionManagement/workOrderManagement/components/filesDia.vue
src/views/productionManagement/workOrderManagement/index.vue
src/views/productionPlan/productionPlan/components/PIMTable.vue
src/views/productionPlan/productionPlan/index.vue
src/views/projectManagement/Management/components/formDia.vue
src/views/projectManagement/Management/index.vue
src/views/projectManagement/Management/projectDetail.vue
src/views/projectManagement/projectType/components/ProjectTypeDialog.vue
src/views/projectManagement/projectType/index.vue
src/views/qualityManagement/finalInspection/components/filesDia.vue
src/views/qualityManagement/finalInspection/index.vue
src/views/qualityManagement/nonconformingManagement/index.vue
src/views/qualityManagement/processInspection/components/filesDia.vue
src/views/qualityManagement/processInspection/components/formDia.vue
src/views/qualityManagement/processInspection/index.vue
src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue
src/views/qualityManagement/rawMaterialInspection/index.vue
src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
src/views/reportAnalysis/PSIDataAnalysis/index.vue
src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue
src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
src/views/reportAnalysis/dataDashboard/index.vue
src/views/reportAnalysis/dataDashboard/index0.vue
src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
src/views/reportAnalysis/productionAnalysis/components/center-center.vue
src/views/reportAnalysis/productionAnalysis/components/center-top.vue
src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
src/views/reportAnalysis/productionAnalysis/components/left-top.vue
src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
src/views/reportAnalysis/productionAnalysis/components/right-top.vue
src/views/reportAnalysis/productionAnalysis/index.vue
src/views/safeProduction/accidentReportingRecord/index.vue
src/views/safeProduction/dangerInvestigation/index.vue
src/views/safeProduction/emergencyPlanReview/index.vue
src/views/safeProduction/hazardSourceLedger/index.vue
src/views/safeProduction/hazardousMaterialsControl/index.vue
src/views/safeProduction/safeQualifications/index.vue
src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue
src/views/safeProduction/safeWorkApproval/fileList.vue
src/views/safeProduction/safeWorkApproval/index.vue
src/views/safeProduction/safetyTrainingAssessment/index.vue
src/views/salesManagement/deliveryLedger/index.vue
src/views/salesManagement/invoiceLedger/index.vue
src/views/salesManagement/receiptPaymentLedger/index.vue
src/views/salesManagement/returnOrder/components/detailDia.vue
src/views/salesManagement/returnOrder/components/formDia.vue
src/views/salesManagement/returnOrder/index.vue
src/views/salesManagement/salesLedger/fileList.vue
src/views/salesManagement/salesLedger/index.vue
src/views/system/appVersion/index.vue
src/views/systemArchitecture/index.vue
src/views/tool/build/CodeTypeDialog.vue
vite.config.js |