FILE_UPLOAD_README.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,480 @@ # æ¬å°æä»¶ä¸ä¼ 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> ``` å¦æä½ çç®æ æ¯âå ä¸ä¼ ï¼åè·ä¸å¡ä¸èµ·ä¿åâï¼è¿å¥åæ³å¯ä»¥ç´æ¥ä½ä¸ºåºç¡æ¨¡æ¿ä½¿ç¨ã jsconfig.json
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,10 @@ { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "~/*": ["./*"] } }, "include": ["src/**/*.js", "src/**/*.vue", "vite.config.js"] } src/api/basicData/productProcess.js
@@ -1,10 +1,10 @@ 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, }); } src/api/productionManagement/productionOrder.js
@@ -45,6 +45,15 @@ }); } // ç产订å-ä¿®æ¹ export function updateProductOrder(data) { return request({ url: "/productionOrder/updateOrder", method: "post", data: data, }); } export function delProductOrder(ids) { return request({ url: `/productionOrder/delete`, src/api/productionManagement/productionProcess.js
@@ -4,7 +4,7 @@ // å页æ¥è¯¢ export function listPage(query) { return request({ url: "/productProcess/listPage", url: "/technologyOperation/listPage", method: "get", params: query, }); @@ -53,7 +53,7 @@ // å¯¼å ¥æ°æ® export function importData(data) { return request({ url: "/productProcess/importData", url: "/technologyOperation/importData", method: "post", data: data, }); @@ -62,7 +62,7 @@ // ä¸è½½æ¨¡æ¿ export function downloadTemplate() { return request({ url: "/productProcess/downloadTemplate", url: "/technologyOperation/downloadTemplate", method: "post", responseType: "blob", }); src/components/Dialog/FileList.vue
@@ -5,7 +5,8 @@ @close="handleClose" class="attachment-dialog"> <!-- å·¥å ·æ --> <div class="toolbar"> <div v-if="editable" class="toolbar"> <el-button type="primary" size="small" @click="handleUpload"> @@ -16,10 +17,11 @@ <el-dialog v-model="uploadDialogVisible" title="ä¸ä¼ éä»¶" width="50%" @close="handleUploadClose"> @close="closeUpload"> <AttachmentUpload v-model:file-list="newFileList" /> <template #footer> <el-button @click="handleUploadClose">å ³é</el-button> <el-button @click="saveUpload">ä¿å</el-button> <el-button @click="closeUpload">å ³é</el-button> </template> </el-dialog> <!-- æä»¶åè¡¨è¡¨æ ¼ --> @@ -40,11 +42,12 @@ <el-button link type="primary" size="small" :href="scope.row.downloadURL" class="download-link"> class="download-link" @click="downloadFile(scope.row.downloadURL)"> ä¸è½½ </el-button> <el-button link <el-button v-if="editable" link type="danger" size="small" @click="handleDelete(scope.row)"> @@ -93,6 +96,10 @@ type: Boolean, default: true, }, editable: { type: Boolean, default: true, }, }); const emit = defineEmits(["close", "download", "upload", "delete"]); @@ -119,7 +126,7 @@ uploadDialogVisible.value = true; }; const handleUploadClose = async () => { const saveUpload = async () => { // æ£æ¥æ¯å¦ææ°ä¸ä¼ çæä»¶ if (newFileList.value.length > 0) { try { @@ -136,6 +143,11 @@ proxy?.$modal?.msgError("ä¸ä¼ 失败"); } } uploadDialogVisible.value = false; }; const closeUpload = () => { newFileList.value = []; uploadDialogVisible.value = false; }; @@ -160,6 +172,9 @@ }); }; const downloadFile = url => { window.open(url, "_blank"); }; onMounted(() => { setList(); }); src/components/PIMTable/PIMTable.vue
@@ -21,6 +21,7 @@ class="lims-table"> <el-table-column align="center" type="selection" :selectable="selectable" width="55" v-if="isSelection" /> <el-table-column align="center" @@ -258,6 +259,10 @@ type: Boolean, default: false, }, selectable: { type: Function, default: () => true, }, isShowPagination: { type: Boolean, default: true, src/layout/components/Sidebar/index.vue
@@ -1,142 +1,142 @@ <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> src/main.js
@@ -48,6 +48,8 @@ import ImageUpload from "@/components/AttachmentUpload/image"; // å¾çé¢è§ç»ä»¶ import ImagePreview from "@/components/AttachmentPreview/image"; // éä»¶å¼¹çªç»ä»¶ import FileListDialog from "@/components/Dialog/FileList.vue"; // åå ¸æ ç¾ç»ä»¶ import DictTag from "@/components/DictTag"; // è¡¨æ ¼ç»ä»¶ @@ -92,6 +94,7 @@ 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); src/router/index.js
@@ -146,106 +146,106 @@ path: "general-ledger", component: () => import("@/views/financialManagement/generalLedger/index.vue"), name: "GeneralLedger", meta: { title: "æ»å¸ç§ç®" } meta: { title: "æ»å¸ç§ç®" }, }, { path: "sales-out", component: () => import("@/views/financialManagement/receivable/salesOut.vue"), name: "SalesOut", meta: { title: "éå®åºåº" } meta: { title: "éå®åºåº" }, }, { path: "sales-return", component: () => import("@/views/financialManagement/receivable/salesReturn.vue"), name: "SalesReturn", meta: { title: "éå®éè´§" } meta: { title: "éå®éè´§" }, }, { path: "receivable-reconciliation", component: () => import("@/views/financialManagement/receivable/reconciliation.vue"), name: "ReceivableReconciliation", meta: { title: "åºæ¶å¯¹è´¦" } meta: { title: "åºæ¶å¯¹è´¦" }, }, { path: "invoice-apply", component: () => import("@/views/financialManagement/receivable/invoiceApply.vue"), name: "InvoiceApply", meta: { title: "å¼ç¥¨ç³è¯·" } meta: { title: "å¼ç¥¨ç³è¯·" }, }, { path: "output-invoice", component: () => import("@/views/financialManagement/receivable/outputInvoice.vue"), name: "OutputInvoice", meta: { title: "é项å票" } meta: { title: "é项å票" }, }, { path: "receipt", component: () => import("@/views/financialManagement/receivable/receipt.vue"), name: "Receipt", meta: { title: "æ¶æ¬¾å" } meta: { title: "æ¶æ¬¾å" }, }, { path: "purchase-in", component: () => import("@/views/financialManagement/payable/purchaseIn.vue"), name: "PurchaseIn", meta: { title: "éè´å ¥åº" } meta: { title: "éè´å ¥åº" }, }, { path: "payable-reconciliation", component: () => import("@/views/financialManagement/payable/reconciliation.vue"), name: "PayableReconciliation", meta: { title: "åºä»å¯¹è´¦" } meta: { title: "åºä»å¯¹è´¦" }, }, { path: "input-invoice", component: () => import("@/views/financialManagement/payable/input-invoice.vue"), name: "InputInvoice", meta: { title: "è¿é¡¹å票" } meta: { title: "è¿é¡¹å票" }, }, { path: "payment-apply", component: () => import("@/views/financialManagement/payable/paymentApply.vue"), name: "PaymentApply", meta: { title: "仿¬¾ç³è¯·" } meta: { title: "仿¬¾ç³è¯·" }, }, { path: "payment", component: () => import("@/views/financialManagement/payable/payment.vue"), name: "Payment", meta: { title: "仿¬¾å" } meta: { title: "仿¬¾å" }, }, { path: "fixed-assets", component: () => import("@/views/financialManagement/assets/fixedAssets.vue"), name: "FixedAssets", meta: { title: "åºå®èµäº§" } meta: { title: "åºå®èµäº§" }, }, { path: "intangible-assets", component: () => import("@/views/financialManagement/assets/intangibleAssets.vue"), name: "IntangibleAssets", meta: { title: "æ å½¢èµäº§" } meta: { title: "æ å½¢èµäº§" }, }, { path: "voucher", component: () => import("@/views/financialManagement/voucher/index.vue"), name: "Voucher", meta: { title: "åè¯" } meta: { title: "åè¯" }, }, { path: "voucher-general-ledger", component: () => import("@/views/financialManagement/voucher/generalLedger.vue"), name: "VoucherGeneralLedger", meta: { title: "ç§ç®æ»å¸" } meta: { title: "ç§ç®æ»å¸" }, }, { path: "voucher-detail-ledger", component: () => import("@/views/financialManagement/voucher/detailLedger.vue"), name: "VoucherDetailLedger", meta: { title: "ç§ç®æç»å¸" } } ] } meta: { title: "ç§ç®æç»å¸" }, }, ], }, ]; // å¨æè·¯ç±ï¼åºäºç¨æ·æé卿å»å è½½ src/views/basicData/product/index.vue
@@ -2,41 +2,34 @@ <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"> @@ -47,25 +40,23 @@ <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)"> ç¼è¾ </el-button> <el-button type="primary" link @click="openProDia('add', data)"> <el-button type="primary" link @click="openProDia('add', data)"> æ·»å 产å </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> @@ -75,103 +66,109 @@ </div> </div> <div class="right"> <div style="margin-bottom: 10px" v-if="isShowButton"> <el-button type="primary" @click="openModelDia('add')"> <div style="margin-bottom: 10px" v-if="isShowButton"> <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> @@ -180,473 +177,480 @@ </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(""); 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) => { 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.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 = 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 = ""; } 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(); 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> src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
@@ -212,14 +212,7 @@ </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> @@ -235,7 +228,7 @@ 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, @@ -254,14 +247,7 @@ 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); @@ -564,63 +550,15 @@ ); }; // éä»¶ï¼æ¥è¯¢ 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 () => { src/views/customerService/afterSalesHandling/index.vue
@@ -102,33 +102,19 @@ ></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: { @@ -303,144 +289,15 @@ }) } // æå¼éä»¶å¼¹çª 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 } // æ¥è¯¢å表 src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -228,39 +228,6 @@ 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) { src/views/financialManagement/expenseManagement/index.vue
@@ -76,27 +76,18 @@ </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: "æ¯åºç®¡ç", @@ -108,9 +99,6 @@ 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 { @@ -315,156 +303,16 @@ 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(); src/views/financialManagement/revenueManagement/index.vue
@@ -3,22 +3,22 @@ <el-form :model="filters" :inline="true"> <el-form-item label="æ¶å ¥æ¥æ:"> <el-date-picker v-model="filters.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" placeholder="è¯·éæ©" clearable @change="changeDaterange" /> placeholder="è¯·éæ©" clearable @change="changeDaterange"/> </el-form-item> <el-form-item label="æ¶æ¬¾æ¹å¼:"> <el-select v-model="filters.incomeMethodLabel" placeholder="è¯·éæ©" clearable style="width: 200px;" > <el-option v-for="item in incomeMethodOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> v-model="filters.incomeMethodLabel" placeholder="è¯·éæ©" clearable style="width: 200px;" > <el-option v-for="item in incomeMethodOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="getTableData">æç´¢</el-button> @@ -29,32 +29,32 @@ <div class="actions"> <div></div> <div> <el-button type="primary" @click="add" icon="Plus"> æ°å¢ </el-button> <el-button type="primary" @click="add" icon="Plus"> æ°å¢</el-button> <el-button @click="handleOut" icon="download">导åº</el-button> <el-button type="danger" icon="Delete" :disabled="multipleList.length <= 0 || hasBusinessIdInSelection" @click="handleBatchDelete" type="danger" icon="Delete" :disabled="multipleList.length <= 0 || hasBusinessIdInSelection" @click="handleBatchDelete" > æ¹éå é¤ </el-button> </div> </div> <PIMTable rowKey="id" isSelection :column="columns" :tableData="dataList" :page="{ rowKey="id" isSelection :column="columns" :tableData="dataList" :page="{ current: pagination.currentPage, size: pagination.pageSize, total: pagination.total, }" :isShowSummary="true" :summaryMethod="summarizeMainTable" @selection-change="handleSelectionChange" @pagination="changePage" :isShowSummary="true" :summaryMethod="summarizeMainTable" @selection-change="handleSelectionChange" @pagination="changePage" > <template #incomeMethodSlot="{ row }"> <el-tag> @@ -62,18 +62,18 @@ </el-tag> </template> <template #operation="{ row }"> <el-button type="primary" link :disabled="!!row.businessId" @click="edit(row.id)" <el-button type="primary" link :disabled="!!row.businessId" @click="edit(row.id)" > ç¼è¾ </el-button> <el-button type="primary" link @click="openFilesFormDia(row)" type="primary" link @click="openFilesFormDia(row)" > éä»¶ </el-button> @@ -81,28 +81,16 @@ </PIMTable> </div> <Modal ref="modalRef" @success="getTableData"></Modal> <!-- todo éä»¶é¢è§ç¸å ³ --> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" :show-upload-button="true" :show-delete-button="true" :upload-method="handleUpload" :delete-method="handleFileDelete" /> <FileListDialog v-if="fileListDialogVisible" :record-id="currentRecordId" record-type="account_income" v-model:visible="fileListDialogVisible"/> </div> </template> <script setup> import { usePaginationApi } from "@/hooks/usePaginationApi"; import { listPage, delAccountIncome, fileListPage, fileAdd, fileDel } from "@/api/financialManagement/revenueManagement"; import { onMounted, getCurrentInstance, ref, computed } from "vue"; import Modal from "./Modal.vue"; import { ElMessageBox, ElMessage } from "element-plus"; import {usePaginationApi} from "@/hooks/usePaginationApi"; import {listPage, delAccountIncome} from "@/api/financialManagement/revenueManagement"; import {onMounted, getCurrentInstance, ref, computed} from "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"; defineOptions({ name: "æ¶å ¥ç®¡ç", @@ -110,15 +98,14 @@ // è¡¨æ ¼å¤éæ¡éä¸é¡¹ const multipleList = ref([]); const { proxy } = getCurrentInstance(); const {proxy} = getCurrentInstance(); const modalRef = ref(); const { payment_methods } = proxy.useDict("payment_methods"); const { receipt_payment_type } = proxy.useDict("receipt_payment_type"); const { income_types } = proxy.useDict("income_types"); const {payment_methods} = proxy.useDict("payment_methods"); const {receipt_payment_type} = proxy.useDict("receipt_payment_type"); const {income_types} = proxy.useDict("income_types"); const fileListRef = ref(null); const fileListDialogVisible = ref(false); const currentFileRow = ref(null); const accountType = ref('æ¶å ¥'); const currentRecordId = ref(0); const incomeMethodOptions = computed(() => { const merged = [...(payment_methods.value || []), ...(receipt_payment_type.value || [])]; @@ -127,7 +114,7 @@ const label = item?.label; if (!label) return; if (!uniqueMap.has(label)) { uniqueMap.set(label, { label, value: label }); uniqueMap.set(label, {label, value: label}); } }); return Array.from(uniqueMap.values()); @@ -142,86 +129,86 @@ resetFilters, onCurrentChange, } = usePaginationApi( listPage, { incomeMethodLabel: undefined, entryDate: undefined, }, [ listPage, { label: "æ¶å ¥æ¥æ", prop: "incomeDate", incomeMethodLabel: undefined, entryDate: undefined, }, { label: "æ¶å ¥ç±»å", prop: "incomeType", dataType: "tag", formatData: (params) => { if (income_types.value.find((m) => m.value == params)) { return income_types.value.find((m) => m.value == params).label; } else { return null } [ { label: "æ¶å ¥æ¥æ", prop: "incomeDate", }, }, { label: "客æ·åç§°", prop: "customerName", width: '200' { label: "æ¶å ¥ç±»å", prop: "incomeType", dataType: "tag", formatData: (params) => { if (income_types.value.find((m) => m.value == params)) { return income_types.value.find((m) => m.value == params).label; } else { return null } }, }, { label: "客æ·åç§°", prop: "customerName", width: '200' }, { label: "æ¶å ¥éé¢", prop: "incomeMoney", }, { label: "æ¶å ¥éé¢", prop: "incomeMoney", }, { label: "æ¶å ¥æè¿°", prop: "incomeDescribed", }, { label: "æ¶å ¥æè¿°", prop: "incomeDescribed", }, { label: "æ¶æ¬¾æ¹å¼", prop: "incomeMethodLabel", align: 'center', width: '100', dataType: "slot", slot: "incomeMethodSlot", }, { label: "å票å·ç ", prop: "invoiceNumber", }, { label: "æ¶æ¬¾æ¹å¼", prop: "incomeMethodLabel", align: 'center', width: '100', dataType: "slot", slot: "incomeMethodSlot", }, { label: "å票å·ç ", prop: "invoiceNumber", }, { label: "夿³¨", prop: "note", }, { label: "夿³¨", prop: "note", }, { label: "å½å ¥äºº", prop: "inputUser", }, { label: "å½å ¥æ¥æ", prop: "inputTime", }, { label: "å½å ¥äºº", prop: "inputUser", }, { label: "å½å ¥æ¥æ", prop: "inputTime", }, }, { fixed: "right", label: "æä½", dataType: "slot", slot: "operation", align: "center", width: "160px", }, ], undefined, { fixed: "right", label: "æä½", dataType: "slot", slot: "operation", align: "center", width: "160px", }, ], undefined, { incomeMethodLabel: (value) => ({ incomeMethodLabel: value || undefined, }), } incomeMethodLabel: (value) => ({ incomeMethodLabel: value || undefined, }), } ); // è¡¨æ ¼åè®¡ï¼æ¶å ¥éé¢ @@ -232,8 +219,8 @@ const getIncomeMethodLabel = (row) => { const methodValue = row?.incomeMethod; const dictList = String(row?.businessType) === "1" ? receipt_payment_type.value : payment_methods.value; ? receipt_payment_type.value : payment_methods.value; return dictList.find((item) => item.value == methodValue)?.label || "--"; }; @@ -259,9 +246,9 @@ } modalRef.value.loadForm(id); }; const changePage = ({ page, limit }) => { const changePage = ({page, limit}) => { pagination.currentPage = page; pagination.pageSize = limit; pagination.pageSize = limit; onCurrentChange(page); }; const deleteRow = (id) => { @@ -283,13 +270,13 @@ return; } } ElMessageBox.confirm("æ¤æä½å°æ°¸ä¹ å é¤è¯¥æ°æ®, æ¯å¦ç»§ç»?", "æç¤º", { confirmButtonText: "ç¡®å®", cancelButtonText: "åæ¶", type: "warning", }).then(async () => { const { code } = await delAccountIncome(id); const {code} = await delAccountIncome(id); if (code == 200) { ElMessage({ type: "success", @@ -306,13 +293,13 @@ proxy.$modal.msgWarning("è¯·éæ©è¦å é¤çæ°æ®"); return; } // æ£æ¥æ¯å¦æ businessId if (hasBusinessIdInSelection.value) { proxy.$modal.msgWarning("éä¸çè®°å½ä¸å å«å·²å ³èä¸å¡çè®°å½ï¼ä¸è½å é¤"); return; } const ids = multipleList.value.map((item) => item.id); deleteRow(ids); }; @@ -336,162 +323,17 @@ cancelButtonText: "åæ¶", type: "warning", }) .then(() => { proxy.download(`/account/accountIncome/export`, {}, "æ¶å ¥å°è´¦.xlsx"); }) .catch(() => { proxy.$modal.msg("已忶"); }); .then(() => { proxy.download(`/account/accountIncome/export`, {}, "æ¶å ¥å°è´¦.xlsx"); }) .catch(() => { proxy.$modal.msg("已忶"); }); }; // æå¼éä»¶å¼¹æ¡ 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; } currentRecordId.value = row.id; fileListDialogVisible.value = true; }; onMounted(() => { @@ -503,6 +345,7 @@ .table_list { margin-top: unset; } .actions { display: flex; justify-content: space-between; src/views/inventoryManagement/stockManagement/New.vue
@@ -57,14 +57,7 @@ style="width: 100%" /> </el-form-item> <el-form-item label="æ¹å·" prop="batchNo" :rules="[ { required: true, message: '请è¾å ¥æ¹å·', trigger: 'blur', } ]"> prop="batchNo"> <el-input v-model="formState.batchNo" placeholder="请è¾å ¥æ¹å·" /> </el-form-item> src/views/procurementManagement/procurementInvoiceLedger/index.vue
@@ -69,7 +69,7 @@ <el-button type="primary" link @click="downLoadFile(row)" @click="openFileDialog(row)" > éä»¶ </el-button> @@ -83,16 +83,7 @@ </template> </PIMTable> </div> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" title="éä»¶å表" :showUploadButton="true" :showDeleteButton="true" :deleteMethod="handleDeleteFile" :uploadMethod="handleFileUpload" :rulesRegulationsManagementId="currentRowId" /> <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="ticket_registration" :record-id="recordId" /> <EditModal ref="editmodalRef" @success="getTableData"></EditModal> </div> </template> @@ -113,9 +104,9 @@ import { onMounted } from "vue"; import { ElMessageBox } from "element-plus"; import EditModal from "./Modal/EditModal.vue"; import FileListDialog from '@/components/Dialog/FileListDialog.vue'; import useUserStore from "@/store/modules/user.js"; const userStore = useUserStore(); const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); defineOptions({ name: "æ¥ç¥¨å°è´¦", @@ -290,143 +281,15 @@ onCurrentChange(page); }; const downLoadFile = row => { currentRowId.value = row.id; if (fileListRef.value) { fileListRef.value.open(row.commonFiles || []); } }; // æå¼éä»¶å¼¹çª const recordId =ref(0) const fileDialogVisible = ref(false) // ä¸ä¼ éä»¶ï¼èªå®ä¹ä¸ä¼ æ¹æ³ï¼ const handleFileUpload = async () => { if (!currentRowId.value) { proxy.$modal.msgWarning("缺å°ç»è®°IDï¼æ æ³ä¿åéä»¶"); 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 { // ä½¿ç¨ FormData ä¸ä¼ æä»¶ const formData = new FormData(); formData.append('file', file); formData.append('type', '4'); // type åæ°ï¼ç¨æ·æªæå®å ·ä½å¼ï¼å ä¼ ç©ºå符串 formData.append('id', currentRowId.value); // å½åè¡ç id const uploadRes = await request({ url: '/file/uploadByCommon', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data', Authorization: `Bearer ${getToken()}` } }); if (uploadRes.code === 200) { proxy.$modal.msgSuccess("éä»¶ä¸ä¼ æå"); // å·æ°å表è·åææ°æ°æ® await new Promise((resolveRefresh) => { // è°ç¨ API è·åææ°åè¡¨æ°æ® productRecordPage({ ...filters, current: pagination.currentPage, size: pagination.pageSize }).then(({ code, data }) => { if (code === 200) { // æ´æ°æ°æ®å表 dataList.value = data.records; pagination.total = data.total; // ä»å¤é¨æ°æ®è·å commonFiles const currentRow = dataList.value.find(row => row.id === currentRowId.value); if (currentRow && fileListRef.value) { // å·æ°éä»¶å表ï¼ä½¿ç¨ä»å¤é¨è·åçææ° commonFiles fileListRef.value.open(currentRow.commonFiles || []); } resolveRefresh(); } else { resolveRefresh(); } }).catch(() => { resolveRefresh(); }); }); resolve({ name: uploadRes.data?.originalName || file.name, url: uploadRes.data?.tempPath || uploadRes.data?.url, id: uploadRes.data?.id }); } else { proxy.$modal.msgError(uploadRes.msg || "æä»¶ä¸ä¼ 失败"); resolve(null); } } catch (error) { console.error("éä»¶ä¸ä¼ 失败:", error); proxy.$modal.msgError("éä»¶ä¸ä¼ 失败"); resolve(null); } finally { document.body.removeChild(input); } }; document.body.appendChild(input); input.click(); }); }; // å é¤éä»¶ const handleDeleteFile = async (file) => { try { await delCommonFile([file.id]); proxy.$modal.msgSuccess("å 餿å"); // å·æ°å表è·åææ°æ°æ® await new Promise((resolveRefresh) => { // è°ç¨ API è·åææ°åè¡¨æ°æ® productRecordPage({ ...filters, current: pagination.currentPage, size: pagination.pageSize }).then(({ code, data }) => { if (code === 200) { // æ´æ°æ°æ®å表 dataList.value = data.records; pagination.total = data.total; // ä»å¤é¨æ°æ®è·å commonFiles const currentRow = dataList.value.find(row => row.id === currentRowId.value); if (currentRow && fileListRef.value) { // å·æ°éä»¶å表ï¼ä½¿ç¨ä»å¤é¨è·åçææ° commonFiles fileListRef.value.open(currentRow.commonFiles || []); } resolveRefresh(); } else { resolveRefresh(); } }).catch(() => { resolveRefresh(); }); }); return true; } catch (error) { proxy.$modal.msgError("å é¤å¤±è´¥"); return false; } }; // æå¼éä»¶å¼¹æ¡ const openFileDialog = async (row) => { recordId.value = row.id fileDialogVisible.value = true } const openEdit = (row) => { editmodalRef.value.open(row); src/views/procurementManagement/procurementLedger/index.vue
@@ -9,7 +9,7 @@ placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> @change="handleQuery"/> </el-form-item> <el-form-item label="éè´ååå·ï¼"> <el-input v-model="searchForm.purchaseContractNumber" @@ -17,21 +17,21 @@ placeholder="请è¾å ¥" @change="handleQuery" clearable :prefix-icon="Search" /> :prefix-icon="Search"/> </el-form-item> <el-form-item label="éå®ååå·ï¼"> <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> @change="handleQuery"/> </el-form-item> <el-form-item label="项ç®åç§°ï¼"> <el-input v-model="searchForm.projectName" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> @change="handleQuery"/> </el-form-item> <el-form-item label="å½å ¥æ¥æï¼"> <el-date-picker v-model="searchForm.entryDate" @@ -40,11 +40,12 @@ type="daterange" placeholder="è¯·éæ©" clearable @change="changeDaterange" /> @change="changeDaterange"/> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> @click="handleQuery"> æç´¢ </el-button> </el-form-item> </el-form> </div> @@ -57,7 +58,8 @@ <el-button @click="handleOut">导åº</el-button> <el-button type="danger" plain @click="handleDelete">å é¤</el-button> @click="handleDelete">å é¤ </el-button> </div> <el-table :data="tableData" border @@ -71,7 +73,7 @@ height="calc(100vh - 21.5em)"> <el-table-column align="center" type="selection" width="55" /> width="55"/> <el-table-column type="expand"> <template #default="props"> <el-table :data="props.row.children" @@ -81,62 +83,62 @@ <el-table-column align="center" label="åºå·" type="index" width="60" /> width="60"/> <el-table-column label="产å大类" prop="productCategory" /> prop="productCategory"/> <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> prop="specificationModel"/> <el-table-column label="åä½" prop="unit" /> prop="unit"/> <el-table-column label="æ°é" prop="quantity" /> prop="quantity"/> <el-table-column label="å¯ç¨æ°é" prop="availableQuality" /> prop="availableQuality"/> <el-table-column label="éè´§æ°é" prop="returnQuality" /> prop="returnQuality"/> <el-table-column label="ç¨ç(%)" prop="taxRate" /> prop="taxRate"/> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> :formatter="formattedNumber"/> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> :formatter="formattedNumber"/> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> :formatter="formattedNumber"/> </el-table> </template> </el-table-column> <el-table-column align="center" label="åºå·" type="index" width="60" /> width="60"/> <el-table-column label="éè´ååå·" prop="purchaseContractNumber" width="160" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column label="éå®ååå·" prop="salesContractNo" width="160" show-overflow-tooltip /> width="160" show-overflow-tooltip/> <el-table-column label="ä¾åºååç§°" prop="supplierName" width="160" show-overflow-tooltip /> width="160" show-overflow-tooltip/> <el-table-column label="项ç®åç§°" prop="projectName" width="320" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column label="审æ¹ç¶æ" prop="approvalStatus" width="100" show-overflow-tooltip> <template #default="scope"> <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small"> <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small"> {{ approvalStatusText[scope.row.approvalStatus] || 'æªç¥ç¶æ' }} </el-tag> </template> @@ -144,28 +146,28 @@ <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="100" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column label="仿¬¾æ¹å¼" width="100" prop="paymentMethod" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column label="ååéé¢(å )" prop="contractAmount" width="200" show-overflow-tooltip :formatter="formattedNumber" /> :formatter="formattedNumber"/> <el-table-column label="å½å ¥äºº" prop="recorderName" width="120" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column label="å½å ¥æ¥æ" prop="entryDate" width="100" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column label="夿³¨" prop="remarks" width="200" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column fixed="right" label="æä½" width="120" @@ -174,10 +176,11 @@ <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">ç¼è¾</el-button> :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">ç¼è¾ </el-button> <el-button link type="primary" @click="downLoadFile(scope.row)">éä»¶</el-button> @click="openFileDialog(scope.row)">éä»¶</el-button> </template> </el-table-column> </el-table> @@ -186,15 +189,15 @@ layout="total, sizes, prev, pager, next, jumper" :page="page.current" :limit="page.size" @pagination="paginationChange" /> @pagination="paginationChange"/> </div> <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢éè´å°è´¦é¡µé¢' : 'ç¼è¾éè´å°è´¦é¡µé¢'" :width="'70%'" :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia"> :title="operationType === 'add' ? 'æ°å¢éè´å°è´¦é¡µé¢' : 'ç¼è¾éè´å°è´¦é¡µé¢'" :width="'70%'" :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia"> <el-form :model="form" label-width="140px" label-position="top" @@ -206,7 +209,7 @@ prop="purchaseContractNumber"> <el-input v-model="form.purchaseContractNumber" placeholder="请è¾å ¥" clearable /> clearable/> </el-form-item> </el-col> <el-col :span="12"> @@ -220,7 +223,7 @@ <el-option v-for="item in salesContractList" :key="item.id" :label="item.salesContractNo" :value="item.id" /> :value="item.id"/> </el-select> </el-form-item> </el-col> @@ -236,7 +239,8 @@ <el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id" >{{item.supplierName + '---' + item.supplierType}}</el-option> :value="item.id">{{ item.supplierName + '---' + item.supplierType }} </el-option> </el-select> </el-form-item> </el-col> @@ -245,7 +249,7 @@ prop="projectName"> <el-input v-model="form.projectName" placeholder="请è¾å ¥" clearable /> clearable/> </el-form-item> </el-col> </el-row> @@ -254,7 +258,7 @@ <el-form-item label="仿¬¾æ¹å¼"> <el-input v-model="form.paymentMethod" placeholder="请è¾å ¥" clearable /> clearable/> </el-form-item> </el-col> <el-col :span="12"> @@ -266,7 +270,7 @@ format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> clearable/> </el-form-item> </el-col> </el-row> @@ -274,7 +278,7 @@ <el-col :span="12"> <el-form-item label="å½å ¥äººï¼" prop="recorderId"> <el-input v-model="form.recorderName" placeholder="èªå¨å¡«å " disabled /> <el-input v-model="form.recorderName" placeholder="èªå¨å¡«å " disabled/> </el-form-item> </el-col> <el-col :span="12"> @@ -286,7 +290,7 @@ format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> clearable/> </el-form-item> </el-col> </el-row> @@ -294,10 +298,12 @@ <el-form-item label="产åä¿¡æ¯ï¼" prop="entryDate"> <el-button type="primary" @click="openProductForm('add')">æ·»å </el-button> @click="openProductForm('add')">æ·»å </el-button> <el-button plain type="danger" @click="deleteProduct">å é¤</el-button> @click="deleteProduct">å é¤ </el-button> </el-form-item> <div class="select-button-group" style="width: 500px; margin: 20px 0;" @@ -321,12 +327,12 @@ :value="item.templateName"> <div style="display: flex; justify-content: space-between; align-items: center;"> <span>{{ item.templateName }}</span> <el-icon v-if="item.id" class="delete-icon" @click.stop="handleDeleteTemplate(item)" style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;"> <Delete /> <el-icon v-if="item.id" class="delete-icon" @click.stop="handleDeleteTemplate(item)" style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;"> <Delete/> </el-icon> </div> </el-option> @@ -347,40 +353,40 @@ :summary-method="summarizeProTable"> <el-table-column align="center" type="selection" width="55" /> width="55"/> <el-table-column align="center" label="åºå·" type="index" width="60" /> width="60"/> <el-table-column label="产å大类" prop="productCategory" /> prop="productCategory"/> <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> prop="specificationModel"/> <el-table-column label="åä½" prop="unit" width="70" /> width="70"/> <el-table-column label="æ°é" prop="quantity" width="70" /> width="70"/> <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="120" show-overflow-tooltip /> show-overflow-tooltip/> <el-table-column label="ç¨ç(%)" prop="taxRate" width="80" /> width="80"/> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" /> width="150"/> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" /> width="150"/> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="150" /> width="150"/> <el-table-column label="æ¯å¦è´¨æ£" prop="isChecked" width="150"> @@ -397,7 +403,8 @@ <template #default="scope"> <el-button link type="primary" @click="openProductForm('edit', scope.row, scope.$index)">ç¼è¾</el-button> @click="openProductForm('edit', scope.row, scope.$index)">ç¼è¾ </el-button> </template> </el-table-column> </el-table> @@ -409,7 +416,7 @@ placeholder="请è¾å ¥" clearable type="textarea" :rows="2" /> :rows="2"/> </el-form-item> </el-col> </el-row> @@ -417,24 +424,7 @@ <el-col :span="24"> <el-form-item label="éä»¶ææï¼" prop="purchaseLedgerFiles"> <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">ä¸ä¼ </el-button> <template #tip> <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> @@ -442,26 +432,26 @@ </FormDialog> <!-- å¯¼å ¥å¼¹çª --> <FormDialog v-model="importUpload.open" :title="importUpload.title" :width="'600px'" @close="importUpload.open = false" @confirm="submitImportFile" @cancel="importUpload.open = false" v-model="importUpload.open" :title="importUpload.title" :width="'600px'" @close="importUpload.open = false" @confirm="submitImportFile" @cancel="importUpload.open = false" > <el-upload ref="importUploadRef" :limit="1" accept=".xlsx,.xls" :action="importUpload.url" :headers="importUpload.headers" :before-upload="importUpload.beforeUpload" :on-success="importUpload.onSuccess" :on-error="importUpload.onError" :on-progress="importUpload.onProgress" :on-change="importUpload.onChange" :auto-upload="false" drag ref="importUploadRef" :limit="1" accept=".xlsx,.xls" :action="importUpload.url" :headers="importUpload.headers" :before-upload="importUpload.beforeUpload" :on-success="importUpload.onSuccess" :on-error="importUpload.onError" :on-progress="importUpload.onProgress" :on-change="importUpload.onChange" :auto-upload="false" drag > <i class="el-icon-upload"></i> <div class="el-upload__text"> @@ -476,12 +466,12 @@ </el-upload> </FormDialog> <FormDialog v-model="productFormVisible" :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" :width="'40%'" :operation-type="productOperationType" @close="closeProductDia" @confirm="submitProduct" @cancel="closeProductDia"> :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" :width="'40%'" :operation-type="productOperationType" @close="closeProductDia" @confirm="submitProduct" @cancel="closeProductDia"> <el-form :model="productForm" label-width="140px" label-position="top" @@ -499,7 +489,7 @@ @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" /> style="width: 100%"/> </el-form-item> </el-col> </el-row> @@ -515,7 +505,7 @@ <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> :value="item.id"/> </el-select> </el-form-item> </el-col> @@ -526,7 +516,7 @@ prop="unit"> <el-input v-model="productForm.unit" placeholder="请è¾å ¥" clearable /> clearable/> </el-form-item> </el-col> <el-col :span="12"> @@ -537,11 +527,11 @@ clearable @change="mathNum"> <el-option label="1" value="1" /> value="1"/> <el-option label="6" value="6" /> value="6"/> <el-option label="13" value="13" /> value="13"/> </el-select> </el-form-item> </el-col> @@ -556,7 +546,7 @@ :min="0" clearable style="width: 100%" @change="mathNum" /> @change="mathNum"/> </el-form-item> </el-col> <el-col :span="12"> @@ -569,7 +559,7 @@ style="width: 100%" v-model="productForm.quantity" placeholder="请è¾å ¥" @change="mathNum" /> @change="mathNum"/> </el-form-item> </el-col> </el-row> @@ -583,7 +573,7 @@ :min="0" clearable style="width: 100%" @change="reverseMathNum('taxInclusiveTotalPrice')" /> @change="reverseMathNum('taxInclusiveTotalPrice')"/> </el-form-item> </el-col> <el-col :span="12"> @@ -595,7 +585,7 @@ :min="0" clearable style="width: 100%" @change="reverseMathNum('taxExclusiveTotalPrice')" /> @change="reverseMathNum('taxExclusiveTotalPrice')"/> </el-form-item> </el-col> </el-row> @@ -607,9 +597,9 @@ placeholder="è¯·éæ©" clearable> <el-option label="墿®ç¥¨" value="墿®ç¥¨" /> value="墿®ç¥¨"/> <el-option label="å¢ä¸ç¥¨" value="å¢ä¸ç¥¨" /> value="å¢ä¸ç¥¨"/> </el-select> </el-form-item> </el-col> @@ -621,7 +611,7 @@ :step="0.1" :min="0" clearable style="width: 100%" /> style="width: 100%"/> </el-form-item> </el-col> </el-row> @@ -631,24 +621,23 @@ prop="isChecked"> <el-radio-group v-model="productForm.isChecked"> <el-radio label="æ¯" :value="true" /> :value="true"/> <el-radio label="å¦" :value="false" /> :value="false"/> </el-radio-group> </el-form-item> </el-col> </el-row> </el-form> </FormDialog> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" title="éä»¶å表" /> <FileList v-if="fileListDialogVisible" v-model:visible="fileListDialogVisible" record-type="purchase_ledger" :record-id="recordId" /> </div> </template> <script setup> import {Search, Delete} from "@element-plus/icons-vue"; import FormDialog from '@/components/Dialog/FormDialog.vue'; import FileListDialog from '@/components/Dialog/FileListDialog.vue'; import { getToken } from "@/utils/auth"; import pagination from "@/components/PIMTable/Pagination.vue"; import { @@ -661,10 +650,7 @@ } from "vue"; import { Search, Delete } from "@element-plus/icons-vue"; import { ElMessageBox, ElMessage } from "element-plus"; import FormDialog from '@/components/Dialog/FormDialog.vue'; import FileListDialog from '@/components/Dialog/FileListDialog.vue'; import { getSalesLedgerWithProducts, addOrUpdateSalesLedgerProduct, delProduct, delLedgerFile, @@ -685,431 +671,434 @@ delPurchaseTemplate, } from "@/api/procurementManagement/procurementLedger.js"; import useFormData from "@/hooks/useFormData.js"; const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); const { proxy } = getCurrentInstance(); const tableData = ref([]); const productData = ref([]); const selectedRows = ref([]); const productSelectedRows = ref([]); const modelOptions = 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"; const {proxy} = getCurrentInstance(); const tableData = ref([]); const productData = ref([]); const selectedRows = ref([]); const productSelectedRows = ref([]); const modelOptions = ref([]); const productOptions = ref([]); const salesContractList = ref([]); const supplierList = ref([]); const tableLoading = ref(false); const fileListDialogVisible = 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 FileUpload from "@/components/AttachmentUpload/file/index.vue"; const userStore = useUserStore(); const userStore = useUserStore(); // 订å审æ¹ç¶ææ¾ç¤ºææ¬ const approvalStatusText = { 1: "å¾ å®¡æ ¸", 2: "审æ¹ä¸", 3: "审æ¹éè¿", 4: "审æ¹å¤±è´¥", // 订å审æ¹ç¶ææ¾ç¤ºææ¬ const approvalStatusText = { 1: "å¾ å®¡æ ¸", 2: "审æ¹ä¸", 3: "审æ¹éè¿", 4: "审æ¹å¤±è´¥", }; // è·å审æ¹ç¶ææ ç¾ç±»å const getApprovalStatusType = (status) => { const typeMap = { 1: "info", // å¾ å®¡æ ¸ - ç°è² 2: "warning", // 审æ¹ä¸ - æ©è² 3: "success", // 审æ¹éè¿ - ç»¿è² 4: "danger", // 审æ¹å¤±è´¥ - çº¢è² }; return typeMap[status] || ""; }; // è·å审æ¹ç¶ææ ç¾ç±»å const getApprovalStatusType = (status) => { const typeMap = { 1: "info", // å¾ å®¡æ ¸ - ç°è² 2: "warning", // 审æ¹ä¸ - æ©è² 3: "success", // 审æ¹éè¿ - ç»¿è² 4: "danger", // 审æ¹å¤±è´¥ - çº¢è² }; return typeMap[status] || ""; }; const templateName = ref(""); const filterInputValue = ref(""); const templateList = ref([]); const isTemplateNameDuplicate = ref(false); // æ 记模æ¿åç§°æ¯å¦éå¤ // å½åéä¸ç模æ¿IDï¼ç¨äºåºåæ°å¢æ¨¡æ¿è¿æ¯æ´æ°æ¨¡æ¿ï¼ const currentTemplateId = ref(null); const templateName = ref(""); const filterInputValue = ref(""); const templateList = ref([]); const isTemplateNameDuplicate = ref(false); // æ 记模æ¿åç§°æ¯å¦éå¤ // å½åéä¸ç模æ¿IDï¼ç¨äºåºåæ°å¢æ¨¡æ¿è¿æ¯æ´æ°æ¨¡æ¿ï¼ const currentTemplateId = ref(null); // æ£æ¥æ¨¡æ¿åç§°æ¯å¦éå¤ const checkTemplateNameDuplicate = name => { if (!name || name.trim() === "") { isTemplateNameDuplicate.value = false; return false; } const isDuplicate = templateList.value.some( // æ£æ¥æ¨¡æ¿åç§°æ¯å¦éå¤ const checkTemplateNameDuplicate = name => { if (!name || name.trim() === "") { isTemplateNameDuplicate.value = false; return false; } const isDuplicate = templateList.value.some( item => item.templateName === name.trim() ); isTemplateNameDuplicate.value = isDuplicate; return isDuplicate; }; ); isTemplateNameDuplicate.value = isDuplicate; return isDuplicate; }; // 鲿宿¶å¨ let duplicateCheckTimer = null; const onTemplateFilterChange = val => { filterInputValue.value = val ?? ""; // æ¸ é¤ä¹åç宿¶å¨ if (duplicateCheckTimer) { clearTimeout(duplicateCheckTimer); } // 宿¶æ£æ¥æ¨¡æ¿åç§°æ¯å¦éå¤ï¼é²æå¤çï¼é¿å é¢ç¹æç¤ºï¼ if (val && val.trim()) { duplicateCheckTimer = setTimeout(() => { const isDuplicate = checkTemplateNameDuplicate(val); if (isDuplicate) { ElMessage({ message: "模æ¿åç§°å·²åå¨ï¼è¯·æ´æ¢æ¨¡æ¿åç§°", type: "warning", duration: 2000, }); } }, 300); // 300ms 鲿 } else { isTemplateNameDuplicate.value = false; } }; // allow-create æ¶ï¼è¾å ¥ä¸åå¨çå 容ä¼ä½ä¸º string å¼è¿åï¼è¿é忥åè¾å ¥æ¡ä»¥ç¡®ä¿æåä¸ä¸¢ const onTemplateChange = async val => { if (typeof val === "string") { filterInputValue.value = val; // éæ©æè¾å ¥æ¶æ£æ¥éå¤ checkTemplateNameDuplicate(val); } // è¿æ»¤æ°æ®ï¼æ¥æ¾å¹é çæ¨¡æ¿ const matchedTemplate = templateList.value.find( item => item.templateName === val ); if (matchedTemplate?.id) { // è®°å½å½åéä¸ç模æ¿IDï¼åç»ä¿åæ¶è¿è¡æ´æ°æä½ currentTemplateId.value = matchedTemplate.id; // éä¸å·²ææ¨¡æ¿æ¶ï¼ä¸åºè§ä¸ºâ模æ¿åç§°éå¤å¯¼è´ä¸å¯ä¿åâ isTemplateNameDuplicate.value = false; // 妿æ¾å°æ¨¡æ¿ï¼åªèµå¼ä¾åºåã项ç®åç§°ã仿¬¾æ¹å¼å产åä¿¡æ¯ if (matchedTemplate.supplierId) { form.value.supplierId = matchedTemplate.supplierId; } if (matchedTemplate.supplierName) { form.value.supplierName = matchedTemplate.supplierName; } if (matchedTemplate.projectName) { form.value.projectName = matchedTemplate.projectName; } if (matchedTemplate.paymentMethod) { form.value.paymentMethod = matchedTemplate.paymentMethod; } // æ¨¡æ¿æ°æ®ä¸ç产ååæ®µæ¯ productListï¼éè¦è½¬æ¢ä¸º productData productData.value = matchedTemplate.productList || matchedTemplate.productData || []; } else { // æªå¹é å°å·²ææ¨¡æ¿ï¼è§ä¸ºæ°æ¨¡æ¿ currentTemplateId.value = null; // å¦ææ²¡ææ¾å°æ¨¡æ¿ï¼é置表åï¼ä¿æå½å表åç¶æï¼ const currentFormData = { ...form.value }; const currentProductData = [...productData.value]; // å¦æå¯¹è¯æ¡æªæå¼ï¼å æå¼ if (!dialogFormVisible.value) { operationType.value = "add"; dialogFormVisible.value = true; } // çå¾ ä¸ä¸ä¸ª tick 忢夿°æ® await nextTick(); form.value = { ...form.value, ...currentFormData, }; productData.value = currentProductData; } }; // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® 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: "", recorderName: "", entryDate: "", productData: [], supplierName: "", supplierId: "", paymentMethod: "", executionDate: "", isChecked: false, }, 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, // 设置å½å ¥æ¥æèå´ä¸ºå½å¤© entryDate: [ dayjs().startOf("day").format("YYYY-MM-DD"), dayjs().endOf("day").format("YYYY-MM-DD"), ], entryDateStart: dayjs().startOf("day").format("YYYY-MM-DD"), entryDateEnd: dayjs().endOf("day").format("YYYY-MM-DD"), }); // 产å表åå¼¹æ¡æ°æ® 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: "", isChecked: false, }, 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: true, message: "è¯·éæ©", trigger: "change" }], taxInclusiveTotalPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], taxExclusiveTotalPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], invoiceType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], isChecked: [{ 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 importUploadRef = ref(null); const importUpload = reactive({ title: "å¯¼å ¥éè´å°è´¦", open: false, url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import", headers: { Authorization: "Bearer " + getToken() }, isUploading: false, beforeUpload: (file) => { const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls"); const isLt10M = file.size / 1024 / 1024 < 10; if (!isExcel) { proxy.$modal.msgError("ä¸ä¼ æä»¶åªè½æ¯ xlsx/xls æ ¼å¼!"); return false; } if (!isLt10M) { proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶ è¿ 10MB!"); return false; } return true; }, onChange: (file, fileList) => { // noop }, onProgress: (event, file, fileList) => { // noop }, onSuccess: (response, file, fileList) => { importUpload.isUploading = false; if (response?.code === 200) { proxy.$modal.msgSuccess("å¯¼å ¥æå"); importUpload.open = false; if (importUploadRef.value) { importUploadRef.value.clearFiles?.(); } getList(); } else { proxy.$modal.msgError(response?.msg || "å¯¼å ¥å¤±è´¥"); } }, onError: () => { importUpload.isUploading = false; proxy.$modal.msgError("å¯¼å ¥å¤±è´¥ï¼è¯·éè¯"); }, }); const handleImport = () => { importUpload.title = "å¯¼å ¥éè´å°è´¦"; importUpload.open = true; importUpload.isUploading = false; if (importUploadRef.value) { importUploadRef.value.clearFiles?.(); } }; // ä¸è½½å¯¼å ¥æ¨¡æ¿ï¼å¦å端路å¾ä¸åï¼å¯å¨æ¤å¤è°æ´ï¼ const downloadTemplate = () => { proxy.download("/purchase/ledger/exportTemplate", {}, "éè´å°è´¦å¯¼å ¥æ¨¡æ¿.xlsx"); }; const submitImportFile = () => { importUpload.isUploading = true; proxy.$refs["importUploadRef"]?.submit?.(); }; 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 handleButtonClick = async () => { // æ£æ¥æ¨¡æ¿åç§°æ¯å¦ä¸ºç©º if (!templateName.value || templateName.value.trim() === "") { ElMessage({ message: "请è¾å ¥æ¨¡æ¿åç§°", type: "warning", }); return; } // 妿æ¯âæ°å¢æ¨¡æ¿âï¼æ²¡æéä¸å·²ææ¨¡æ¿ï¼ï¼æéè¦åéåæ ¡éªï¼ // è¥æ¯éä¸å·²ææ¨¡æ¿åä¿®æ¹ï¼åå 许使ç¨ååç§°ï¼è§ä¸ºæ´æ°ï¼ if (!currentTemplateId.value) { const isDuplicate = checkTemplateNameDuplicate(templateName.value); // 鲿宿¶å¨ let duplicateCheckTimer = null; const onTemplateFilterChange = val => { filterInputValue.value = val ?? ""; // æ¸ é¤ä¹åç宿¶å¨ if (duplicateCheckTimer) { clearTimeout(duplicateCheckTimer); } // 宿¶æ£æ¥æ¨¡æ¿åç§°æ¯å¦éå¤ï¼é²æå¤çï¼é¿å é¢ç¹æç¤ºï¼ if (val && val.trim()) { duplicateCheckTimer = setTimeout(() => { const isDuplicate = checkTemplateNameDuplicate(val); if (isDuplicate) { ElMessage({ message: "模æ¿åç§°å·²åå¨ï¼è¯·æ´æ¢æ¨¡æ¿åç§°", type: "warning", duration: 2000, }); return; } }, 300); // 300ms 鲿 } else { isTemplateNameDuplicate.value = false; } }; // allow-create æ¶ï¼è¾å ¥ä¸åå¨çå 容ä¼ä½ä¸º string å¼è¿åï¼è¿é忥åè¾å ¥æ¡ä»¥ç¡®ä¿æåä¸ä¸¢ const onTemplateChange = async val => { if (typeof val === "string") { filterInputValue.value = val; // éæ©æè¾å ¥æ¶æ£æ¥éå¤ checkTemplateNameDuplicate(val); } // è¿æ»¤æ°æ®ï¼æ¥æ¾å¹é çæ¨¡æ¿ const matchedTemplate = templateList.value.find( item => item.templateName === val ); if (matchedTemplate?.id) { // è®°å½å½åéä¸ç模æ¿IDï¼åç»ä¿åæ¶è¿è¡æ´æ°æä½ currentTemplateId.value = matchedTemplate.id; // éä¸å·²ææ¨¡æ¿æ¶ï¼ä¸åºè§ä¸ºâ模æ¿åç§°éå¤å¯¼è´ä¸å¯ä¿åâ isTemplateNameDuplicate.value = false; // 妿æ¾å°æ¨¡æ¿ï¼åªèµå¼ä¾åºåã项ç®åç§°ã仿¬¾æ¹å¼å产åä¿¡æ¯ if (matchedTemplate.supplierId) { form.value.supplierId = matchedTemplate.supplierId; } if (matchedTemplate.supplierName) { form.value.supplierName = matchedTemplate.supplierName; } if (matchedTemplate.projectName) { form.value.projectName = matchedTemplate.projectName; } if (matchedTemplate.paymentMethod) { form.value.paymentMethod = matchedTemplate.paymentMethod; } // æ¨¡æ¿æ°æ®ä¸ç产ååæ®µæ¯ productListï¼éè¦è½¬æ¢ä¸º productData productData.value = matchedTemplate.productList || matchedTemplate.productData || []; } else { // æªå¹é å°å·²ææ¨¡æ¿ï¼è§ä¸ºæ°æ¨¡æ¿ currentTemplateId.value = null; // å¦ææ²¡ææ¾å°æ¨¡æ¿ï¼é置表åï¼ä¿æå½å表åç¶æï¼ const currentFormData = {...form.value}; const currentProductData = [...productData.value]; // å¦æå¯¹è¯æ¡æªæå¼ï¼å æå¼ if (!dialogFormVisible.value) { operationType.value = "add"; dialogFormVisible.value = true; } // æ£æ¥ä¾åºåæ¯å¦éæ© if (!form.value.supplierId) { // çå¾ ä¸ä¸ä¸ª tick 忢夿°æ® await nextTick(); form.value = { ...form.value, ...currentFormData, }; productData.value = currentProductData; } }; // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® 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: "", recorderName: "", entryDate: "", productData: [], supplierName: "", supplierId: "", paymentMethod: "", executionDate: "", isChecked: false, }, 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, // 设置å½å ¥æ¥æèå´ä¸ºå½å¤© entryDate: [ dayjs().startOf("day").format("YYYY-MM-DD"), dayjs().endOf("day").format("YYYY-MM-DD"), ], entryDateStart: dayjs().startOf("day").format("YYYY-MM-DD"), entryDateEnd: dayjs().endOf("day").format("YYYY-MM-DD"), }); // 产å表åå¼¹æ¡æ°æ® 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: "", isChecked: false, }, 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: true, message: "è¯·éæ©", trigger: "change"}], taxInclusiveTotalPrice: [ {required: true, message: "请è¾å ¥", trigger: "blur"}, ], taxExclusiveTotalPrice: [ {required: true, message: "请è¾å ¥", trigger: "blur"}, ], invoiceType: [{required: true, message: "è¯·éæ©", trigger: "change"}], isChecked: [{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 importUploadRef = ref(null); const importUpload = reactive({ title: "å¯¼å ¥éè´å°è´¦", open: false, url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import", headers: {Authorization: "Bearer " + getToken()}, isUploading: false, beforeUpload: (file) => { const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls"); const isLt10M = file.size / 1024 / 1024 < 10; if (!isExcel) { proxy.$modal.msgError("ä¸ä¼ æä»¶åªè½æ¯ xlsx/xls æ ¼å¼!"); return false; } if (!isLt10M) { proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶ è¿ 10MB!"); return false; } return true; }, onChange: (file, fileList) => { // noop }, onProgress: (event, file, fileList) => { // noop }, onSuccess: (response, file, fileList) => { importUpload.isUploading = false; if (response?.code === 200) { proxy.$modal.msgSuccess("å¯¼å ¥æå"); importUpload.open = false; if (importUploadRef.value) { importUploadRef.value.clearFiles?.(); } getList(); } else { proxy.$modal.msgError(response?.msg || "å¯¼å ¥å¤±è´¥"); } }, onError: () => { importUpload.isUploading = false; proxy.$modal.msgError("å¯¼å ¥å¤±è´¥ï¼è¯·éè¯"); }, }); const handleImport = () => { importUpload.title = "å¯¼å ¥éè´å°è´¦"; importUpload.open = true; importUpload.isUploading = false; if (importUploadRef.value) { importUploadRef.value.clearFiles?.(); } }; // ä¸è½½å¯¼å ¥æ¨¡æ¿ï¼å¦å端路å¾ä¸åï¼å¯å¨æ¤å¤è°æ´ï¼ const downloadTemplate = () => { proxy.download("/purchase/ledger/exportTemplate", {}, "éè´å°è´¦å¯¼å ¥æ¨¡æ¿.xlsx"); }; const submitImportFile = () => { importUpload.isUploading = true; proxy.$refs["importUploadRef"]?.submit?.(); }; 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 handleButtonClick = async () => { // æ£æ¥æ¨¡æ¿åç§°æ¯å¦ä¸ºç©º if (!templateName.value || templateName.value.trim() === "") { ElMessage({ message: "请è¾å ¥æ¨¡æ¿åç§°", type: "warning", }); return; } // 妿æ¯âæ°å¢æ¨¡æ¿âï¼æ²¡æéä¸å·²ææ¨¡æ¿ï¼ï¼æéè¦åéåæ ¡éªï¼ // è¥æ¯éä¸å·²ææ¨¡æ¿åä¿®æ¹ï¼åå 许使ç¨ååç§°ï¼è§ä¸ºæ´æ°ï¼ if (!currentTemplateId.value) { const isDuplicate = checkTemplateNameDuplicate(templateName.value); if (isDuplicate) { ElMessage({ message: "请å éæ©ä¾åºå", message: "模æ¿åç§°å·²åå¨ï¼è¯·æ´æ¢æ¨¡æ¿åç§°", type: "warning", }); return; } } // æ£æ¥æ¯å¦æäº§åæ°æ® if (!productData.value || productData.value.length === 0) { ElMessage({ message: 'è¯·å æ·»å 产åä¿¡æ¯', type: 'warning', // æ£æ¥ä¾åºåæ¯å¦éæ© if (!form.value.supplierId) { ElMessage({ message: "请å éæ©ä¾åºå", type: "warning", }); return; } // æ£æ¥æ¯å¦æäº§åæ°æ® if (!productData.value || productData.value.length === 0) { ElMessage({ message: 'è¯·å æ·»å 产åä¿¡æ¯', type: 'warning', }); return; } try { let params = { productData: proxy.HaveJson(productData.value), supplierId: form.value.supplierId, paymentMethod: form.value.paymentMethod, recorderId: form.value.recorderId, recorderName: form.value.recorderName, projectName: form.value.projectName, templateName: templateName.value.trim(), }; console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value); // 妿 currentTemplateId æå¼ï¼è¯´æå½åæ¯âç¼è¾å·²ææ¨¡æ¿â â è°ç¨æ´æ°æ¥å£ // å¦åä¸ºâæ°å»ºæ¨¡æ¿â â è°ç¨æ°å¢æ¥å£ let res; if (currentTemplateId.value) { res = await updatePurchaseTemplate({ id: currentTemplateId.value, ...params, }); return; } else { res = await addPurchaseTemplate(params); } try { let params = { productData: proxy.HaveJson(productData.value), supplierId: form.value.supplierId, paymentMethod: form.value.paymentMethod, recorderId: form.value.recorderId, recorderName: form.value.recorderName, projectName: form.value.projectName, templateName: templateName.value.trim(), }; console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value); // 妿 currentTemplateId æå¼ï¼è¯´æå½åæ¯âç¼è¾å·²ææ¨¡æ¿â â è°ç¨æ´æ°æ¥å£ // å¦åä¸ºâæ°å»ºæ¨¡æ¿â â è°ç¨æ°å¢æ¥å£ let res; if (currentTemplateId.value) { res = await updatePurchaseTemplate({ id: currentTemplateId.value, ...params, }); } else { res = await addPurchaseTemplate(params); } if (res && res.code === 200) { ElMessage({ message: currentTemplateId.value ? "æ¨¡æ¿æ´æ°æå" : "模æ¿ä¿åæå", type: "success", }); // ä¿åæååéæ°è·å模æ¿å表 await getTemplateList(); // æ¸ ç©ºæ¨¡æ¿åç§°è¾å ¥ templateName.value = ""; filterInputValue.value = ""; isTemplateNameDuplicate.value = false; // ä¿å/æ´æ°å®æåï¼éç½®å½å模æ¿ID currentTemplateId.value = null; } else { ElMessage({ message: res?.msg || "模æ¿ä¿å失败", type: "error", }); } } catch (error) { console.error("ä¿å模æ¿å¤±è´¥:", error); if (res && res.code === 200) { ElMessage({ message: "模æ¿ä¿å失败ï¼è¯·ç¨åéè¯", message: currentTemplateId.value ? "æ¨¡æ¿æ´æ°æå" : "模æ¿ä¿åæå", type: "success", }); // ä¿åæååéæ°è·å模æ¿å表 await getTemplateList(); // æ¸ ç©ºæ¨¡æ¿åç§°è¾å ¥ templateName.value = ""; filterInputValue.value = ""; isTemplateNameDuplicate.value = false; // ä¿å/æ´æ°å®æåï¼éç½®å½å模æ¿ID currentTemplateId.value = null; } else { ElMessage({ message: res?.msg || "模æ¿ä¿å失败", type: "error", }); } }; // å表åè®¡æ¹æ³ const summarizeChildrenTable = param => { return proxy.summarizeTable( } catch (error) { console.error("ä¿å模æ¿å¤±è´¥:", error); ElMessage({ message: "模æ¿ä¿å失败ï¼è¯·ç¨åéè¯", type: "error", }); } }; // å表åè®¡æ¹æ³ const summarizeChildrenTable = param => { return proxy.summarizeTable( param, [ "taxInclusiveUnitPrice", @@ -1121,20 +1110,20 @@ "futureTicketsAmount", ], { ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° 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 }) ); }; 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; @@ -1151,458 +1140,461 @@ .catch(() => { tableLoading.value = false; }); }; // è¡¨æ ¼éæ©æ°æ® const handleSelectionChange = selection => { selectedRows.value = selection; }; const productSelected = selectedRows => { productSelectedRows.value = selectedRows; }; const expandedRowKeys = ref([]); // å±å¼è¡ const expandChange = async (row, expandedRows) => { if (expandedRows.length > 0) { expandedRowKeys.value = []; }; // è¡¨æ ¼éæ©æ°æ® const handleSelectionChange = selection => { selectedRows.value = selection; }; const productSelected = selectedRows => { productSelectedRows.value = selectedRows; }; const expandedRowKeys = ref([]); // å±å¼è¡ const expandChange = async (row, expandedRows) => { if (expandedRows.length > 0) { expandedRowKeys.value = []; try { const res = await productList({salesLedgerId: row.id, type: 2}); 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.error("å 载产åå表失败:", error); proxy.$modal.msgError("å 载产åå表失败"); // å±å¼å¤±è´¥æ¶ï¼ç§»é¤å±å¼ç¶æ const index = expandedRows.findIndex(item => item.id === row.id); if (index > -1) { expandedRows.splice(index, 1); } } } else { expandedRowKeys.value = []; } }; // 主表åè®¡æ¹æ³ const summarizeMainTable = param => { return proxy.summarizeTable(param, ["contractAmount"]); }; // å表åè®¡æ¹æ³ const summarizeProTable = param => { return proxy.summarizeTable(param, [ "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // æå¼å¼¹æ¡ const openForm = async (type, row) => { // ç¼è¾æ¶æ£æ¥å®¡æ ¸ç¶æï¼åªæå¾ å®¡æ ¸(1)å审æ¹å¤±è´¥(4)æè½ç¼è¾ if (type === "edit" && row) { if (row.approvalStatus !== 1 && row.approvalStatus !== 4) { proxy.$modal.msgWarning("åªæå¾ å®¡æ ¸å审æ¹å¤±è´¥ç¶æçè®°å½æè½ç¼è¾"); return; } } await getTemplateList(); operationType.value = type; form.value = {}; productData.value = []; fileList.value = []; templateName.value = ""; filterInputValue.value = ""; isTemplateNameDuplicate.value = false; try { // å¹¶è¡å è½½åºç¡æ°æ® const [salesRes, supplierRes] = await Promise.all([ getSalesNo(), getOptions(), ]); salesContractList.value = salesRes || []; // ä¾åºåè¿æ»¤åºisWhite=0 çæ°æ® supplierList.value = (supplierRes.data || []).filter( item => item.isWhite === 0 ); // 设置é»è®¤å¼ form.value.recorderName = userStore.nickName; form.value.entryDate = getCurrentDate(); if (type === "add") { // æ°å¢æ¶çæéè´ååå· try { const res = await productList({ salesLedgerId: row.id, type: 2 }); const index = tableData.value.findIndex(item => item.id === row.id); if (index > -1) { tableData.value[index].children = res.data || []; expandedRowKeys.value.push(row.id); const purchaseNoRes = await createPurchaseNo(); if (purchaseNoRes?.data) { form.value.purchaseContractNumber = purchaseNoRes.data; } } catch (error) { console.error("å 载产åå表失败:", error); proxy.$modal.msgError("å 载产åå表失败"); // å±å¼å¤±è´¥æ¶ï¼ç§»é¤å±å¼ç¶æ const index = expandedRows.findIndex(item => item.id === row.id); if (index > -1) { expandedRows.splice(index, 1); } console.error("çæéè´ååå·å¤±è´¥:", error); proxy.$modal.msgWarning("çæéè´ååå·å¤±è´¥"); } } else { expandedRowKeys.value = []; } }; // 主表åè®¡æ¹æ³ const summarizeMainTable = param => { return proxy.summarizeTable(param, ["contractAmount"]); }; // å表åè®¡æ¹æ³ const summarizeProTable = param => { return proxy.summarizeTable(param, [ "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // æå¼å¼¹æ¡ const openForm = async (type, row) => { // ç¼è¾æ¶æ£æ¥å®¡æ ¸ç¶æï¼åªæå¾ å®¡æ ¸(1)å审æ¹å¤±è´¥(4)æè½ç¼è¾ if (type === "edit" && row) { if (row.approvalStatus !== 1 && row.approvalStatus !== 4) { proxy.$modal.msgWarning("åªæå¾ å®¡æ ¸å审æ¹å¤±è´¥ç¶æçè®°å½æè½ç¼è¾"); } else if (type === "edit" && row?.id) { // ç¼è¾æ¶å è½½æ°æ® currentId.value = row.id; try { const purchaseRes = await getPurchaseById({id: row.id, type: 2}); form.value = {...purchaseRes}; productData.value = purchaseRes.productData || []; fileList.value = purchaseRes.storageBlobVOS || []; } catch (error) { console.error("å è½½éè´å°è´¦æ°æ®å¤±è´¥:", error); proxy.$modal.msgError("å è½½æ°æ®å¤±è´¥"); return; } } await getTemplateList(); operationType.value = type; form.value = {}; productData.value = []; fileList.value = []; templateName.value = ""; filterInputValue.value = ""; isTemplateNameDuplicate.value = false; if (form.value.salesLedgerId == -1) { form.value.salesLedgerId = null; } console.log(form.value, "form.value==========="); dialogFormVisible.value = true; } catch (error) { console.error("æå¼è¡¨å失败:", error); proxy.$modal.msgError("å è½½åºç¡æ°æ®å¤±è´¥"); } }; // ä¸ä¼ åæ ¡æ£ 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); } } // ç§»é¤æä»¶ async function handleRemove(file) { if (!file?.id) { return; } console.log("handleRemove", file.id); if (file.size > 1024 * 1024 * 10) { // ä» åç«¯æ¸ çï¼ä¸è°ç¨å 餿¥å£åæç¤º return; } if (operationType.value === "edit" && file.id) { try { // å¹¶è¡å è½½åºç¡æ°æ® const [salesRes, supplierRes] = await Promise.all([ getSalesNo(), getOptions(), ]); salesContractList.value = salesRes || []; // ä¾åºåè¿æ»¤åºisWhite=0 çæ°æ® supplierList.value = (supplierRes.data || []).filter( item => item.isWhite === 0 ); // 设置é»è®¤å¼ form.value.recorderName = userStore.nickName; form.value.entryDate = getCurrentDate(); if (type === "add") { // æ°å¢æ¶çæéè´ååå· try { const purchaseNoRes = await createPurchaseNo(); if (purchaseNoRes?.data) { form.value.purchaseContractNumber = purchaseNoRes.data; } } catch (error) { console.error("çæéè´ååå·å¤±è´¥:", error); proxy.$modal.msgWarning("çæéè´ååå·å¤±è´¥"); } } else if (type === "edit" && row?.id) { // ç¼è¾æ¶å è½½æ°æ® currentId.value = row.id; try { const purchaseRes = await getPurchaseById({ id: row.id, type: 2 }); form.value = { ...purchaseRes }; productData.value = purchaseRes.productData || []; fileList.value = purchaseRes.salesLedgerFiles || []; } catch (error) { console.error("å è½½éè´å°è´¦æ°æ®å¤±è´¥:", error); proxy.$modal.msgError("å è½½æ°æ®å¤±è´¥"); return; } } if (form.value.salesLedgerId == -1) { form.value.salesLedgerId = null; } console.log(form.value, "form.value==========="); dialogFormVisible.value = true; await delLedgerFile([file.id]); proxy.$modal.msgSuccess("å 餿å"); } catch (error) { console.error("æå¼è¡¨å失败:", error); proxy.$modal.msgError("å è½½åºç¡æ°æ®å¤±è´¥"); } }; // ä¸ä¼ åæ ¡æ£ 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); console.error("å é¤æä»¶å¤±è´¥:", error); proxy.$modal.msgError("å é¤æä»¶å¤±è´¥"); } } // ç§»é¤æä»¶ async function handleRemove(file) { if (!file?.id) { return; } console.log("handleRemove", file.id); if (file.size > 1024 * 1024 * 10) { // ä» åç«¯æ¸ çï¼ä¸è°ç¨å 餿¥å£åæç¤º return; } } if (operationType.value === "edit" && file.id) { try { await delLedgerFile([file.id]); proxy.$modal.msgSuccess("å 餿å"); } catch (error) { console.error("å é¤æä»¶å¤±è´¥:", error); proxy.$modal.msgError("å é¤æä»¶å¤±è´¥"); } } } // æäº¤è¡¨å const submitForm = () => { proxy.$refs["formRef"].validate(valid => { if (valid) { if (productData.value.length > 0) { // æ°å¢æ¶ï¼éè¦ä»æ¯ä¸ªäº§å对象ä¸å é¤ id åæ®µ let processedProductData = productData.value; if (operationType.value === "add") { processedProductData = productData.value.map(product => { const { id, ...rest } = product; return rest; }); } form.value.productData = proxy.HaveJson(processedProductData); } 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; // 妿salesLedgerId为空ï¼åä¸ä¼ ésalesContractNo if (!form.value.salesLedgerId) { form.value.salesContractNo = ""; } // æ°å¢æ¶ä¸ä¼ éid const submitData = { ...form.value }; // æäº¤è¡¨å const submitForm = () => { proxy.$refs["formRef"].validate(valid => { if (valid) { if (productData.value.length > 0) { // æ°å¢æ¶ï¼éè¦ä»æ¯ä¸ªäº§å对象ä¸å é¤ id åæ®µ let processedProductData = productData.value; if (operationType.value === "add") { delete submitData.id; processedProductData = productData.value.map(product => { const {id, ...rest} = product; return rest; }); } addOrEditPurchase(submitData).then(res => { proxy.$modal.msgSuccess("æäº¤æå"); closeDia(); getList(); }); form.value.productData = proxy.HaveJson(processedProductData); } else { proxy.$modal.msgWarning("请添å 产åä¿¡æ¯"); return; } }); }; // å ³éå¼¹æ¡ const closeDia = () => { proxy.resetForm("formRef"); dialogFormVisible.value = false; }; // æå¼äº§åå¼¹æ¡ const openProductForm = async (type, row, index) => { productOperationType.value = type; productOperationIndex.value = index; productForm.value = {}; proxy.resetForm("productFormRef"); productFormVisible.value = true; // å è·å产åé项ï¼ç¡®ä¿æ°æ®å è½½å®æ await getProductOptions(); // çå¾ DOM æ´æ° await nextTick(); if (type === "add") { productForm.value.isChecked = false; } form.value.storageBlobDTOS = fileList.value; form.value.type = 2; if (type === "edit") { // å¤å¶è¡æ°æ® productForm.value = { ...row }; // 妿æ¯ä»æ¨¡æ¿å è½½çæ°æ®ï¼å¯è½æ²¡æ productId å productModelId // éè¦æ ¹æ® productCategory å specificationModel æ¥æ¥æ¾å¯¹åºç ID if (!productForm.value.productId && productForm.value.productCategory) { // æ ¹æ® productCategory æ¥æ¾ productId const findProductIdByCategory = (nodes, categoryName) => { for (let i = 0; i < nodes.length; i++) { if (nodes[i].label === categoryName) { return nodes[i].value; } if (nodes[i].children && nodes[i].children.length > 0) { const found = findProductIdByCategory(nodes[i].children, categoryName); if (found) return found; } // 妿salesLedgerId为空ï¼åä¸ä¼ ésalesContractNo if (!form.value.salesLedgerId) { form.value.salesContractNo = ""; } // æ°å¢æ¶ä¸ä¼ éid const submitData = {...form.value}; if (operationType.value === "add") { delete submitData.id; } addOrEditPurchase(submitData).then(res => { proxy.$modal.msgSuccess("æäº¤æå"); closeDia(); getList(); }); } }); }; // å ³éå¼¹æ¡ const closeDia = () => { proxy.resetForm("formRef"); dialogFormVisible.value = false; }; // æå¼äº§åå¼¹æ¡ const openProductForm = async (type, row, index) => { productOperationType.value = type; productOperationIndex.value = index; productForm.value = {}; proxy.resetForm("productFormRef"); productFormVisible.value = true; // å è·å产åé项ï¼ç¡®ä¿æ°æ®å è½½å®æ await getProductOptions(); // çå¾ DOM æ´æ° await nextTick(); if (type === "add") { productForm.value.isChecked = false; } if (type === "edit") { // å¤å¶è¡æ°æ® productForm.value = {...row}; // 妿æ¯ä»æ¨¡æ¿å è½½çæ°æ®ï¼å¯è½æ²¡æ productId å productModelId // éè¦æ ¹æ® productCategory å specificationModel æ¥æ¥æ¾å¯¹åºç ID if (!productForm.value.productId && productForm.value.productCategory) { // æ ¹æ® productCategory æ¥æ¾ productId const findProductIdByCategory = (nodes, categoryName) => { for (let i = 0; i < nodes.length; i++) { if (nodes[i].label === categoryName) { return nodes[i].value; } return null; }; const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory); if (productId) { productForm.value.productId = productId; // è·ååå·å表并çå¾ å®æ const modelRes = await modelList({ id: productId }); modelOptions.value = modelRes; // çå¾ DOM æ´æ° await nextTick(); // æ ¹æ® specificationModel æ¥æ¾ productModelId if (productForm.value.specificationModel && modelOptions.value.length > 0) { const modelItem = modelOptions.value.find( item => item.model === productForm.value.specificationModel ); if (modelItem) { productForm.value.productModelId = modelItem.id; // è®¾ç½®è§æ ¼åå·ååä½ getProductModel(modelItem.id); } if (nodes[i].children && nodes[i].children.length > 0) { const found = findProductIdByCategory(nodes[i].children, categoryName); if (found) return found; } } } else if (productForm.value.productId) { // 妿æ productIdï¼æ£å¸¸å è½½åå·å表 await getModels(productForm.value.productId); return null; }; const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory); if (productId) { productForm.value.productId = productId; // è·ååå·å表并çå¾ å®æ const modelRes = await modelList({id: productId}); modelOptions.value = modelRes; // çå¾ DOM æ´æ° await nextTick(); if (productForm.value.productModelId) { getProductModel(productForm.value.productModelId); // æ ¹æ® specificationModel æ¥æ¾ productModelId if (productForm.value.specificationModel && modelOptions.value.length > 0) { const modelItem = modelOptions.value.find( item => item.model === productForm.value.specificationModel ); if (modelItem) { productForm.value.productModelId = modelItem.id; // è®¾ç½®è§æ ¼åå·ååä½ getProductModel(modelItem.id); } } } // æååçå¾ ä¸æ¬¡ DOM æ´æ°ï¼ç¡®ä¿æææ°æ®é½å·²è®¾ç½® } else if (productForm.value.productId) { // 妿æ productIdï¼æ£å¸¸å è½½åå·å表 await getModels(productForm.value.productId); // çå¾ DOM æ´æ° await nextTick(); if (productForm.value.productModelId) { getProductModel(productForm.value.productModelId); } } }; const getProductOptions = () => { return productTreeList().then(res => { productOptions.value = convertIdToValue(res); // æååçå¾ ä¸æ¬¡ DOM æ´æ°ï¼ç¡®ä¿æææ°æ®é½å·²è®¾ç½® await nextTick(); } }; const getProductOptions = () => { return productTreeList().then(res => { productOptions.value = convertIdToValue(res); return res; }); }; const getModels = value => { if (value) { productForm.value.productCategory = findNodeById(productOptions.value, value) || ""; return modelList({id: value}).then(res => { modelOptions.value = res; return res; }); }; const getModels = value => { if (value) { productForm.value.productCategory = findNodeById(productOptions.value, value) || ""; return modelList({ id: value }).then(res => { modelOptions.value = res; return res; }); } else { productForm.value.productCategory = ""; modelOptions.value = []; return Promise.resolve([]); } }; 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; }); } else { productForm.value.productCategory = ""; modelOptions.value = []; return Promise.resolve([]); } // æäº¤äº§å表å 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; }; 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 (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); if (nodes[i].children && nodes[i].children.length > 0) { const foundNode = findNodeById(nodes[i].children, productId); if (foundNode) { return foundNode; // å¨åèç¹ä¸æ¾å°ï¼ç´æ¥è¿åï¼å·²ç»æ¯labelåç¬¦ä¸²ï¼ } ElMessageBox.confirm("éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) } } 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); } ElMessageBox.confirm("éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { delProduct(ids).then(res => { proxy.$modal.msgSuccess("å 餿å"); closeProductDia(); getPurchaseById({ id: currentId.value, type: 2 }).then( res => { productData.value = res.productData; } getPurchaseById({id: currentId.value, type: 2}).then( res => { productData.value = res.productData; } ); }); }) .catch(() => { proxy.$modal.msg("已忶"); }); } }; // å ³é产åå¼¹æ¡ const closeProductDia = () => { proxy.resetForm("productFormRef"); productFormVisible.value = false; }; // å¯¼åº const handleOut = () => { ElMessageBox.confirm("éä¸çå 容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) } }; // å ³é产åå¼¹æ¡ const closeProductDia = () => { proxy.resetForm("productFormRef"); productFormVisible.value = false; }; // å¯¼åº 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) { ids = selectedRows.value.filter(item => item.salesLedgerId === null).map(item => item.id); } else { proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); return; } ElMessageBox.confirm("éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) }; // å é¤ const handleDelete = () => { let ids = []; if (selectedRows.value.length > 0) { ids = selectedRows.value.filter(item => item.salesLedgerId === null).map(item => item.id); } else { proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); return; } ElMessageBox.confirm("éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { delPurchase(ids).then(res => { proxy.$modal.msgSuccess("å 餿å"); @@ -1612,172 +1604,172 @@ .catch(() => { proxy.$modal.msg("已忶"); }); }; // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } 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 ( // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } 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" || field === "taxExclusiveTotalPrice" ) { const value = Number(productForm.value[field]); if (value < 0) { productForm.value[field] = "0"; proxy.$modal.msgWarning("å¼ä¸è½å°äº0"); return; } } if (field === "taxInclusiveTotalPrice") { // å·²ç¥å«ç¨æ»ä»·åæ°éï¼åç®å«ç¨åä»· if (productForm.value.quantity) { productForm.value.taxInclusiveUnitPrice = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxInclusiveUnitPrice) < 0) { productForm.value.taxInclusiveUnitPrice = "0"; } } // å·²ç¥å«ç¨æ»ä»·åå«ç¨åä»·ï¼åç®æ°é else if (productForm.value.taxInclusiveUnitPrice) { productForm.value.quantity = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.quantity) < 0) { productForm.value.quantity = "0"; } } // åç®ä¸å«ç¨æ»ä»· productForm.value.taxExclusiveTotalPrice = ( Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxExclusiveTotalPrice) < 0) { productForm.value.taxExclusiveTotalPrice = "0"; } } else if (field === "taxExclusiveTotalPrice") { // åç®å«ç¨æ»ä»· productForm.value.taxInclusiveTotalPrice = ( Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxInclusiveTotalPrice) < 0) { productForm.value.taxInclusiveTotalPrice = "0"; } // å·²ç¥æ°éï¼åç®å«ç¨åä»· if (productForm.value.quantity) { productForm.value.taxInclusiveUnitPrice = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxInclusiveUnitPrice) < 0) { productForm.value.taxInclusiveUnitPrice = "0"; } } // å·²ç¥å«ç¨åä»·ï¼åç®æ°é else if (productForm.value.taxInclusiveUnitPrice) { productForm.value.quantity = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.quantity) < 0) { productForm.value.quantity = "0"; } } } }; // éå®ååéæ©æ¹åæ¹æ³ const salesLedgerChange = async row => { console.log("row", row); var index = salesContractList.value.findIndex(item => item.id == row); console.log("index", index); if (index > -1) { await querygProductInfoByContractNo(); } }; const querygProductInfoByContractNo = async () => { const { code, data } = await getProductInfoByContractNo({ contractNo: form.value.salesLedgerId, }); if (code == 200) { productData.value = data; } }; const fileListRef = ref(null); const fileListDialogVisible = ref(false); const downLoadFile = row => { if (fileListRef.value) { fileListRef.value.open(row.salesLedgerFiles); } }; // è·å模æ¿ä¿¡æ¯ const getTemplateList = async () => { let res = await getPurchaseTemplateList(); if (res && res.code === 200 && Array.isArray(res.data)) { templateList.value = res.data; } }; // å 餿¨¡æ¿ const handleDeleteTemplate = async (item) => { if (!item.id) { proxy.$modal.msgWarning("æ æ³å é¤è¯¥æ¨¡æ¿"); ) { const value = Number(productForm.value[field]); if (value < 0) { productForm.value[field] = "0"; proxy.$modal.msgWarning("å¼ä¸è½å°äº0"); return; } try { await ElMessageBox.confirm( } if (field === "taxInclusiveTotalPrice") { // å·²ç¥å«ç¨æ»ä»·åæ°éï¼åç®å«ç¨åä»· if (productForm.value.quantity) { productForm.value.taxInclusiveUnitPrice = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxInclusiveUnitPrice) < 0) { productForm.value.taxInclusiveUnitPrice = "0"; } } // å·²ç¥å«ç¨æ»ä»·åå«ç¨åä»·ï¼åç®æ°é else if (productForm.value.taxInclusiveUnitPrice) { productForm.value.quantity = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.quantity) < 0) { productForm.value.quantity = "0"; } } // åç®ä¸å«ç¨æ»ä»· productForm.value.taxExclusiveTotalPrice = ( Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxExclusiveTotalPrice) < 0) { productForm.value.taxExclusiveTotalPrice = "0"; } } else if (field === "taxExclusiveTotalPrice") { // åç®å«ç¨æ»ä»· productForm.value.taxInclusiveTotalPrice = ( Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxInclusiveTotalPrice) < 0) { productForm.value.taxInclusiveTotalPrice = "0"; } // å·²ç¥æ°éï¼åç®å«ç¨åä»· if (productForm.value.quantity) { productForm.value.taxInclusiveUnitPrice = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.taxInclusiveUnitPrice) < 0) { productForm.value.taxInclusiveUnitPrice = "0"; } } // å·²ç¥å«ç¨åä»·ï¼åç®æ°é else if (productForm.value.taxInclusiveUnitPrice) { productForm.value.quantity = ( Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice) ).toFixed(2); // ç¡®ä¿ç»æä¸ä¸ºè´æ° if (Number(productForm.value.quantity) < 0) { productForm.value.quantity = "0"; } } } }; // éå®ååéæ©æ¹åæ¹æ³ const salesLedgerChange = async row => { console.log("row", row); var index = salesContractList.value.findIndex(item => item.id == row); console.log("index", index); if (index > -1) { await querygProductInfoByContractNo(); } }; const querygProductInfoByContractNo = async () => { const {code, data} = await getProductInfoByContractNo({ contractNo: form.value.salesLedgerId, }); if (code == 200) { productData.value = data; } }; // è·å模æ¿ä¿¡æ¯ const getTemplateList = async () => { let res = await getPurchaseTemplateList(); if (res && res.code === 200 && Array.isArray(res.data)) { templateList.value = res.data; } }; // æå¼éä»¶å¼¹æ¡ const openFileDialog = async (row) => { recordId.value = row.id fileListDialogVisible.value = true } // å 餿¨¡æ¿ const handleDeleteTemplate = async (item) => { if (!item.id) { proxy.$modal.msgWarning("æ æ³å é¤è¯¥æ¨¡æ¿"); return; } try { await ElMessageBox.confirm( `ç¡®å®è¦å 餿¨¡æ¿"${item.templateName}"åï¼`, "å é¤ç¡®è®¤", { @@ -1785,118 +1777,122 @@ cancelButtonText: "åæ¶", type: "warning", } ); const res = await delPurchaseTemplate([item.id]); if (res && res.code === 200) { ElMessage({ message: "å 餿å", type: "success", }); // 妿å é¤çæ¯å½åéä¸ç模æ¿ï¼æ¸ ç©ºéæ© if (templateName.value === item.templateName) { templateName.value = ""; filterInputValue.value = ""; } // éæ°è·å模æ¿å表 await getTemplateList(); } else { ElMessage({ message: res?.msg || "å é¤å¤±è´¥", type: "error", }); } } catch (error) { if (error !== "cancel") { console.error("å 餿¨¡æ¿å¤±è´¥:", error); ElMessage({ message: "å é¤å¤±è´¥ï¼è¯·ç¨åéè¯", type: "error", }); } } }; ); onMounted(() => { getList(); getTemplateList(); }); const res = await delPurchaseTemplate([item.id]); if (res && res.code === 200) { ElMessage({ message: "å 餿å", type: "success", }); // 妿å é¤çæ¯å½åéä¸ç模æ¿ï¼æ¸ ç©ºéæ© if (templateName.value === item.templateName) { templateName.value = ""; filterInputValue.value = ""; } // éæ°è·å模æ¿å表 await getTemplateList(); } else { ElMessage({ message: res?.msg || "å é¤å¤±è´¥", type: "error", }); } } catch (error) { if (error !== "cancel") { console.error("å 餿¨¡æ¿å¤±è´¥:", error); ElMessage({ message: "å é¤å¤±è´¥ï¼è¯·ç¨åéè¯", type: "error", }); } } }; onMounted(() => { getList(); getTemplateList(); }); </script> <style scoped lang="scss"> .invalid-row { opacity: 0.6; background-color: #f5f7fa; .invalid-row { opacity: 0.6; background-color: #f5f7fa; } .el-row { justify-content: space-between; align-items: center; } .no-arrow-select { --el-select-suffix-icon-color: transparent; /* éèé»è®¤ä¸æç®å¤´ */ } .select-button-group { display: flex; align-items: center; } // 审æ¹äººèç¹å®¹å¨æ ·å¼ .approver-nodes-container { display: flex; flex-wrap: wrap; gap: 16px; padding: 16px; background-color: #f8f9fa; border-radius: 4px; border: 1px solid #e4e7ed; } .approver-node-item { flex: 0 0 calc(33.333% - 12px); min-width: 200px; padding: 12px; background-color: #fff; border-radius: 4px; border: 1px solid #dcdfe6; transition: all 0.3s; &:hover { border-color: #409eff; box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1); } .el-row { justify-content: space-between; align-items: center; } .no-arrow-select { --el-select-suffix-icon-color: transparent; /* éèé»è®¤ä¸æç®å¤´ */ } .select-button-group { display: flex; align-items: center; } // 审æ¹äººèç¹å®¹å¨æ ·å¼ .approver-nodes-container { display: flex; flex-wrap: wrap; gap: 16px; padding: 16px; background-color: #f8f9fa; border-radius: 4px; border: 1px solid #e4e7ed; } } .approver-node-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .approver-node-label { font-size: 13px; font-weight: 500; color: #606266; } @media (max-width: 1200px) { .approver-node-item { flex: 0 0 calc(33.333% - 12px); min-width: 200px; padding: 12px; background-color: #fff; border-radius: 4px; border: 1px solid #dcdfe6; transition: all 0.3s; &:hover { border-color: #409eff; box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1); } flex: 0 0 calc(50% - 8px); } .approver-node-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } @media (max-width: 768px) { .approver-node-item { flex: 0 0 100%; } .approver-node-label { font-size: 13px; font-weight: 500; color: #606266; } // å é¤å¾æ æ ·å¼ .delete-icon { transition: all 0.3s; &:hover { color: #f56c6c !important; transform: scale(1.2); } @media (max-width: 1200px) { .approver-node-item { flex: 0 0 calc(50% - 8px); } } @media (max-width: 768px) { .approver-node-item { flex: 0 0 100%; } } // å é¤å¾æ æ ·å¼ .delete-icon { transition: all 0.3s; &:hover { color: #f56c6c !important; transform: scale(1.2); } } } </style> src/views/productionManagement/processRoute/index.vue
@@ -61,7 +61,8 @@ import EditProcess from "@/views/productionManagement/processRoute/Edit.vue"; import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue"; import { listPage, del } from "@/api/productionManagement/processRoute.js"; import FileList from "@/components/Dialog/FileList.vue"; const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); import { useRouter } from "vue-router"; import { ElMessage, ElMessageBox } from "element-plus"; src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -68,7 +68,8 @@ style="margin-right: 10px;"> å¡çè§å¾ </el-button> <el-button type="primary" <el-button v-if="editable" type="primary" @click="handleAdd">æ°å¢</el-button> </div> </div> @@ -133,12 +134,12 @@ link size="small" @click="handleEdit(scope.row)" :disabled="scope.row.isComplete">ç¼è¾</el-button> :disabled="scope.row.isComplete || !editable">ç¼è¾</el-button> <el-button type="danger" link size="small" @click="handleDelete(scope.row)" :disabled="scope.row.isComplete">å é¤</el-button> :disabled="scope.row.isComplete || !editable">å é¤</el-button> </template> </el-table-column> </el-table> @@ -152,7 +153,8 @@ style="margin-right: 10px;"> è¡¨æ ¼è§å¾ </el-button> <el-button type="primary" <el-button v-if="editable" type="primary" @click="handleAdd">æ°å¢</el-button> </div> </div> @@ -196,7 +198,7 @@ link size="small" @click="handleEdit(item)" :disabled="item.isComplete">ç¼è¾</el-button> :disabled="item.isComplete || !editable">ç¼è¾</el-button> <el-button type="info" link size="small" @@ -205,7 +207,7 @@ link size="small" @click="handleDelete(item)" :disabled="item.isComplete">å é¤</el-button> :disabled="item.isComplete || !editable">å é¤</el-button> </div> </div> </div> @@ -216,7 +218,7 @@ style="margin-top: 20px;"> <div class="section-title">BOM ç»æ</div> <div class="section-actions" v-if="pageType === 'order'"> v-if="pageType === 'order' && editable"> <el-button v-if="!bomDataValue.isEdit" type="primary" @click="bomDataValue.isEdit = true"> @@ -447,7 +449,6 @@ @confirm="handleProductSelect" single /> <!-- åæ°åè¡¨å¯¹è¯æ¡ --> <!-- :editable="!routeInfo.status" --> <ProcessParamListDialog v-model="showParamListDialog" :title="`${currentProcess ? (currentProcess.processName || currentProcess.technologyOperationName || currentProcess.operationName) : ''} - åæ°å表`" :route-id="routeId" @@ -455,6 +456,7 @@ :process="currentProcess" :page-type="pageType" :param-list="paramList" :editable="editable" @getsyncProcessParamItem="getsyncProcessParamItem" @refresh="refreshParamList" /> </div> @@ -509,6 +511,7 @@ const routeId = computed(() => route.query.id); const orderId = computed(() => route.query.orderId); const pageType = computed(() => route.query.type); const editable = computed(() => route.query.editable !== "false"); const tableLoading = ref(false); const tableData = ref([]); @@ -878,6 +881,7 @@ // åå§åææ½æåº const initSortable = () => { destroySortable(); if (!editable.value) return; if (viewMode.value === "table") { // è¡¨æ ¼è§å¾çææ½æåº src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
@@ -55,14 +55,15 @@ controls-position="right" placeholder="è¾å ¥å®é æ°é" style="width: 100%;" :disabled="row.returned" :disabled="row.returned || orderRow?.end" @change="val => handleActualQtyChange(row, val)" /> </template> </el-table-column> </el-table> <template #footer> <span class="dialog-footer"> <el-button type="warning" <el-button v-if="!orderRow?.end" type="warning" :loading="materialReturnConfirming" :disabled="!canOpenReturnSummary" @click="openReturnSummaryDialog"> src/views/productionManagement/productionOrder/index.vue
@@ -40,6 +40,8 @@ value="3" /> <el-option label="已忶" value="4" /> <el-option label="å·²ç»æ" value="5" /> </el-select> </el-form-item> <el-form-item> @@ -65,6 +67,7 @@ :tableLoading="tableLoading" :row-class-name="tableRowClassName" :isSelection="true" :selectable="row => !row.endOrder" @selection-change="handleSelectionChange" @pagination="pagination"> <template #completionStatus="{ row }"> @@ -210,6 +213,7 @@ listProcessBom, delProductOrder, getProductOrderSource, updateProductOrder, } from "@/api/productionManagement/productionOrder.js"; import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js"; import MaterialLedgerDialog from "@/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue"; @@ -243,7 +247,7 @@ prop: "npsNo", width: "150px", }, // 1.å¾ å¼å§ã2.è¿è¡ä¸ã3.已宿ã4.已忶 // 1.å¾ å¼å§ã2.è¿è¡ä¸ã3.已宿ã4.已忶ã5.å·²ç»æ { label: "ç¶æ", prop: "status", @@ -256,6 +260,8 @@ ? "è¿è¡ä¸" : val === 3 ? "已宿" : val === 5 ? "å·²ç»æ" : "已忶", formatType: val => val === 1 @@ -264,7 +270,9 @@ ? "warning" : val === 3 ? "success" : "danger", : val === 5 ? "danger" : "info", }, { label: "产ååç§°", @@ -319,7 +327,7 @@ label: "æä½", align: "center", fixed: "right", width: 260, width: 280, operation: [ { name: "å·¥èºè·¯çº¿", @@ -332,7 +340,7 @@ { name: "ç»å®å·¥èºè·¯çº¿", type: "text", showHide: row => !row.processRouteCode, showHide: row => !row.processRouteCode && !row.endOrder, clickFun: row => { openBindRouteDialog(row, "add"); }, @@ -340,7 +348,7 @@ { name: "æ´æ¢å·¥èºè·¯çº¿", type: "text", showHide: row => row.processRouteCode, showHide: row => row.processRouteCode && !row.endOrder, clickFun: row => { openBindRouteDialog(row, "change"); }, @@ -356,6 +364,7 @@ name: "颿", type: "text", color: "#5EC7AB", showHide: row => !row.endOrder, clickFun: row => { openMaterialDialog(row); }, @@ -364,6 +373,7 @@ name: "è¡¥æ", type: "text", color: "#5EC7AB", showHide: row => !row.endOrder, clickFun: row => { openMaterialSupplementDialog(row); }, @@ -379,9 +389,34 @@ { name: "æå°é¢æå", type: "text", color: "#409eff", color: "#5EC7AB", showHide: row => !row.endOrder, clickFun: row => { handlePrint(row); }, }, { name: "ç产追溯", type: "text", color: "#409eff", clickFun: row => { router.push({ path: "/productionManagement/productionTraceability", query: { npsNo: row.npsNo, productName: row.productName, model: row.model, }, }); }, }, { name: "ç»æè®¢å", type: "text", color: "red", showHide: row => !row.endOrder, clickFun: row => { handleEndOrder(row); }, }, ], @@ -627,6 +662,7 @@ quantity: row.quantity || 0, orderId, type: "order", editable: !row.endOrder, }, }); } catch (e) { @@ -721,6 +757,26 @@ }); }; // ç»æè®¢å const handleEndOrder = row => { ElMessageBox.confirm(`æ¯å¦ç¡®è®¤ç»æè®¢åï¼${row.npsNo}ï¼`, "æç¤º", { confirmButtonText: "ç¡®å®", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { const params = { id: row.id, endOrder: true, }; updateProductOrder(params).then(() => { proxy.$modal.msgSuccess("ç»æè®¢åæå"); getList(); }); }) .catch(() => {}); }; const handleConfirmRoute = () => {}; onMounted(() => { src/views/productionManagement/productionTraceability/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,831 @@ <template> <div class="app-container"> <el-card style="height:82vh;overflow:auto;"> <template #header> <div class="card-header"> <el-form :inline="true" :model="searchForm" class="search-form"> <el-form-item label="ç产订åå·"> <el-select v-model="selectedNpsNo" filterable remote reserve-keyword placeholder="请è¾å ¥ç产订åå·" :loading="npsNoLoading" :remote-method="handleNpsNoSearch" @change="handleSearch" style="width: 400px;"> <el-option v-for="option in npsNoOptions" :key="option.id" :label="option.npsNo + '-' + option.productName + '-' + option.model" :value="option.id" /> </el-select> </el-form-item> <el-form-item> <el-button @click="handleBack">è¿å</el-button> </el-form-item> </el-form> </div> </template> <!-- åºç¡ä¿¡æ¯ --> <div v-if="rowData.productionOrderDto" class="detail-section"> <h3 class="section-title">åºç¡ä¿¡æ¯</h3> <el-descriptions :column="3" border> <el-descriptions-item label="ç产订åå·">{{ rowData.productionOrderDto?.npsNo || '-' }}</el-descriptions-item> <el-descriptions-item label="产ååç§°">{{ rowData.productionOrderDto?.productName || '-' }}</el-descriptions-item> <el-descriptions-item label="产åè§æ ¼">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item> <el-descriptions-item label="ç©æç¼ç ">{{ rowData.productionOrderDto?.materialCode || '-' }}</el-descriptions-item> <el-descriptions-item label="è®¡åæ°é">{{ rowData.productionOrderDto?.quantity || 0 }} <span class="unit">{{ rowData.productionOrderDto?.unit || '-' }}</span></el-descriptions-item> <el-descriptions-item label="å½åç¶æ"> <el-tag :type="getStatusType(rowData.productionOrderDto?.status)"> {{ getStatusText(rowData.productionOrderDto?.status) }} </el-tag> </el-descriptions-item> <el-descriptions-item label="客æ·åç§°">{{ rowData.productionOrderDto?.customerName || '-' }}</el-descriptions-item> <el-descriptions-item label="å¼å§æ¥æ">{{ rowData.productionOrderDto?.startTime || '-' }}</el-descriptions-item> <el-descriptions-item label="宿è¿åº¦"> <el-progress :percentage="rowData.productionOrderDto?.completionStatus" :color="customColors(rowData.productionOrderDto?.completionStatus)" :status="rowData.productionOrderDto?.completionStatus === 100 ? 'success' : ''" style="width: 120px;" /> </el-descriptions-item> </el-descriptions> </div> <el-empty v-else description="请æç´¢ç产订åå·" /> <!-- ç产æ¥å·¥è®°å½ --> <div v-if="rowData.productionRecords && rowData.productionRecords.length > 0" class="progress-container"> <div class="progress-section"> <h3 class="section-title">å·¥åä¿¡æ¯</h3> <div class="order-item"> <el-table :data="rowData.productionRecords" border style="width: 100%"> <el-table-column prop="productNo" label="å·¥åç¼å·" align="center"> </el-table-column> <el-table-column prop="productName" label="产ååç§°" align="center" /> <el-table-column prop="model" label="è§æ ¼" align="center" /> <el-table-column prop="processName" label="å·¥åºåç§°" align="center" /> <el-table-column prop="requiredQuantity" label="éæ±æ°é" align="center" /> <el-table-column prop="completedQuantity" label="宿æ°é" align="center" /> <el-table-column label="详æ " align="center" width="200"> <template #default="{ row }"> <el-link @click="handleClickStep(row)" type="primary">æ¥å·¥è®°å½</el-link> <el-link @click="handleClickQuality(row)" style="margin-left:20px" type="primary">è´¨æ£ä¿¡æ¯</el-link> </template> </el-table-column> </el-table> </div> </div> </div> <el-empty v-else-if="rowData.productionOrderDto" description="ææ æ¥å·¥è®°å½" /> </el-card> <!-- ç产æ¥å·¥è¯¦æ å¼¹çª --> <el-dialog v-model="detailDialogVisible" title="ç产æ¥å·¥è¯¦æ " width="1000px" :close-on-click-modal="false" custom-class="custom-dialog"> <div class="detail-container"> <!-- åºç¡ä¿¡æ¯ --> <div class="detail-section"> <h3 class="section-title">åºç¡ä¿¡æ¯</h3> <el-descriptions :column="3" border> <el-descriptions-item label="ç产工åå·">{{ detailData.npsNo || '-' }}</el-descriptions-item> <el-descriptions-item label="çç»"> <el-tag :type="detailData.schedule === 'ç½ç' ? 'primary' : 'warning'">{{ detailData.schedule || '-' }}</el-tag> </el-descriptions-item> <el-descriptions-item label="产åç¼ç ">{{ detailData.materialCode || '-' }}</el-descriptions-item> <el-descriptions-item label="产ååç§°">{{ detailData.productName || '-' }}</el-descriptions-item> <el-descriptions-item label="è§æ ¼">{{ detailData.model || '-' }}</el-descriptions-item> <el-descriptions-item label="åæ ¼æ°é"><span class="num2">{{ detailData.qualifiedQuantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item> <el-descriptions-item label="ä¸åæ ¼æ°é"><span class="num3">{{ detailData.unqualifiedQuantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item> <el-descriptions-item label="æ»æ°é"><span class="num1">{{ detailData.quantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item> <el-descriptions-item label="å¼å§æ¶é´">{{ detailData.reportingTime || '-' }}</el-descriptions-item> </el-descriptions> </div> <div class="detail-section"> <h3 class="section-title">æ¥å·¥æç»</h3> <el-table :data="[detailData]" border style="width: 100%"> <el-table-column label="æ¥å·¥åå·" prop="productNo" align="center" /> <el-table-column label="äº§åºæ°é" prop="qualifiedQuantity" align="center" /> <el-table-column label="æ¥åºæ°é" prop="unqualifiedQuantity" align="center" /> <el-table-column label="å建æ¶é´" prop="reportingTime" align="center" /> <el-table-column label="æä½" align="center" width="200"> <template #default="{ row }"> <el-button type="primary" link @click="showInput(row.id)">æ¥çæå ¥</el-button> <el-button type="primary" link @click="showParamDetail(row.productionOperationParamList)">åæ°è¯¦æ </el-button> </template> </el-table-column> </el-table> </div> </div> <template #footer> <div class="dialog-footer"> <el-button @click="detailDialogVisible = false">å ³é</el-button> </div> </template> </el-dialog> <!-- æå ¥æ¨¡ææ¡ --> <input-modal v-if="isShowInput" v-model:visible="isShowInput" :production-product-main-id="isShowingId" /> <!-- åæ°è¯¦æ å¼¹çª --> <el-dialog v-model="paramDetailVisible" title="åæ°è¯¦æ " width="600px"> <div v-if="currentParams && currentParams.length > 0" class="param-detail-list"> <el-descriptions :column="1" border> <el-descriptions-item v-for="param in currentParams" :key="param.id" :label="param.paramName"> {{ param.inputValue }} <span v-if="param.unit && param.unit !== '/'" class="unit-text">({{ param.unit }})</span> </el-descriptions-item> </el-descriptions> </div> <el-empty v-else description="ææ åæ°æ°æ®" /> <template #footer> <span class="dialog-footer"> <el-button @click="paramDetailVisible = false">å ³é</el-button> </span> </template> </el-dialog> <!-- è´¨æ£ä¿¡æ¯å¼¹çª --> <el-dialog v-model="qualityDialogVisible" title="è´¨æ£è¯¦æ " width="1000px" :close-on-click-modal="false" custom-class="custom-dialog"> <div class="detail-container"> <div v-for="(record, index) in qualityRecords" :key="record.id" class="quality-record-block"> <div class="detail-section"> <h3 class="section-title">æ£æµè®°å½ {{ index + 1 }} - {{ record.checkTime }}</h3> <el-descriptions :column="3" border> <el-descriptions-item label="æ£æµæ¥æ">{{ record.checkTime || '-' }}</el-descriptions-item> <el-descriptions-item label="ç产工åå·">{{ record.workOrderNo || '-' }}</el-descriptions-item> <el-descriptions-item label="å·¥åº">{{ record.process || '-' }}</el-descriptions-item> <el-descriptions-item label="æ£éªå">{{ record.checkName || '-' }}</el-descriptions-item> <el-descriptions-item label="产ååç§°">{{ record.productName || '-' }}</el-descriptions-item> <el-descriptions-item label="è§æ ¼åå·">{{ record.model || '-' }}</el-descriptions-item> <el-descriptions-item label="æ°é">{{ record.quantity || 0 }} {{ record.unit || '-' }}</el-descriptions-item> <el-descriptions-item label="æ£æµåä½">{{ record.checkCompany || '-' }}</el-descriptions-item> <el-descriptions-item label="æ£æµç»æ"> <el-tag :type="record.checkResult === 'åæ ¼' ? 'success' : 'danger'"> {{ record.checkResult || '-' }} </el-tag> </el-descriptions-item> </el-descriptions> <h4 class="sub-section-title">æ£éªææ å表</h4> <el-table :data="record.inspectItems" border style="width: 100%"> <el-table-column label="åºå·" type="index" width="60" align="center" /> <el-table-column label="ææ " prop="itemName" align="center" /> <el-table-column label="åä½" prop="unit" align="center" /> <el-table-column label="æ åå¼" prop="standardValue" align="center" /> <el-table-column label="å æ§å¼" prop="controlValue" align="center" /> <el-table-column label="å®é å¼" prop="actualValue" align="center" /> </el-table> </div> <!-- <div class="detail-section"> </div> --> <el-divider v-if="index < qualityRecords.length - 1" /> </div> </div> <template #footer> <div class="dialog-footer"> <el-button @click="qualityDialogVisible = false">å ³é</el-button> </div> </template> </el-dialog> </div> </template> <script setup> import { ref, reactive, onMounted } from "vue"; import { useRoute, useRouter } from "vue-router"; import { ElMessage } from "element-plus"; import InputModal from "@/views/productionManagement/productionReporting/Input.vue"; const route = useRoute(); const router = useRouter(); // æç´¢ç¸å ³ const searchForm = reactive({ npsNo: "", }); const selectedNpsNo = ref(null); const npsNoLoading = ref(false); const npsNoOptions = ref([ { id: 1, npsNo: "PO20240301001", productName: "ç²¾å¯æ¶²å缸", model: "HG-100/50-500", materialCode: "MAT-2024-001", quantity: 500, unit: "ä»¶", status: 1, customerName: "éå·¥æºæ¢°æéå ¬å¸", startTime: "2024-03-01", completionStatus: 65, }, { id: 2, npsNo: "PO20240301002", productName: "å·¥ä¸ä¼ºæçµæº", model: "SV-400W-3000", materialCode: "MAT-2024-002", quantity: 200, unit: "å°", status: 2, customerName: "èªå¨å设å¤ç§æå ¬å¸", startTime: "2024-03-02", completionStatus: 100, }, { id: 3, npsNo: "PO20240301003", productName: "é«åå¯å°å", model: "SR-80-5", materialCode: "MAT-2024-003", quantity: 5000, unit: "个", status: 0, customerName: "å¯å°ç³»ç»é ä»¶å", startTime: "2024-03-05", completionStatus: 0, }, ]); // è¯¦æ æ°æ® const rowData = reactive({ productionOrderDto: null, productionRecords: [], }); // æ¥å·¥è¯¦æ å¼¹çª const detailDialogVisible = ref(false); const detailData = ref({}); // æå ¥æ¨¡ææ¡ const isShowInput = ref(false); const isShowingId = ref(0); const showInput = id => { isShowInput.value = true; isShowingId.value = id; }; // åæ°è¯¦æ å¼¹çª const paramDetailVisible = ref(false); const currentParams = ref([]); const showParamDetail = params => { currentParams.value = params || []; paramDetailVisible.value = true; }; // è´¨æ£ä¿¡æ¯å¼¹çª const qualityDialogVisible = ref(false); const qualityRecords = ref([]); // ç¶æå¤ç const getStatusType = status => { const typeMap = { 0: "info", 1: "primary", 2: "success" }; return typeMap[status] || "info"; }; const getStatusText = status => { const statusMap = { 0: "æªå¼å§", 1: "ç产ä¸", 2: "已宿" }; return statusMap[status] || "æªç¥"; }; const customColors = percentage => { if (percentage < 30) return "#f56c6c"; if (percentage < 70) return "#e6a23c"; return "#67c23a"; }; // 模ææç´¢æ¹æ³ const handleNpsNoSearch = query => { if (query) { npsNoLoading.value = true; setTimeout(() => { npsNoLoading.value = false; }, 300); } }; const handleSearch = id => { const selected = npsNoOptions.value.find(item => item.id === id); if (selected) { rowData.productionOrderDto = selected; rowData.productionRecords = [ { id: 1001, productNo: "MO-2024-001-01", productName: selected.productName, model: selected.model, processName: "æ¯å¯å å·¥", requiredQuantity: selected.quantity, completedQuantity: Math.floor(selected.quantity * 0.4), qualifiedQuantity: Math.floor(selected.quantity * 0.4) - 2, unqualifiedQuantity: 2, reportingTime: "2024-03-01 10:00:00", schedule: "ç½ç", postName: "å¼ ä¸", unit: selected.unit, }, { id: 1002, productNo: "MO-2024-001-02", productName: selected.productName, model: selected.model, processName: "ç²¾å å·¥", requiredQuantity: Math.floor(selected.quantity * 0.4), completedQuantity: Math.floor(selected.quantity * 0.25), qualifiedQuantity: Math.floor(selected.quantity * 0.25), unqualifiedQuantity: 0, reportingTime: "2024-03-01 16:00:00", schedule: "ç½ç", postName: "æå", unit: selected.unit, }, ]; } }; const handleBack = () => { router.back(); }; const handleClickStep = row => { detailData.value = { id: row.id || Math.floor(Math.random() * 1000), productNo: row.productNo, npsNo: rowData.productionOrderDto.npsNo, schedule: row.schedule, postName: row.postName, materialCode: rowData.productionOrderDto.materialCode, productName: row.productName, model: row.model, qualifiedQuantity: row.qualifiedQuantity, unqualifiedQuantity: row.unqualifiedQuantity || 0, quantity: row.completedQuantity, unit: row.unit, reportingTime: row.reportingTime, productionOperationParamList: [ { id: 1, paramName: "主轴转é", inputValue: "2400", unit: "rpm" }, { id: 2, paramName: "è¿ç»é度", inputValue: "120", unit: "mm/min" }, { id: 3, paramName: "ååæ·±åº¦", inputValue: "0.5", unit: "mm" }, { id: 4, paramName: "å·å´æ¶²åå", inputValue: "0.6", unit: "Mpa" }, ], }; detailDialogVisible.value = true; }; const handleClickQuality = row => { qualityRecords.value = [ { id: 2001, checkTime: "2024-03-01 11:30:00", workOrderNo: row.productNo, process: row.processName, checkName: "è´¨éé¨-ç建å½", productName: row.productName, model: row.model, unit: row.unit, quantity: row.completedQuantity, checkCompany: "å é¨å®éªå®¤", checkResult: "åæ ¼", inspectItems: [ { id: 1, itemName: "å¤å¾å°ºå¯¸", unit: "mm", standardValue: "100.00±0.05", controlValue: "100.00±0.03", actualValue: "100.01", result: "åæ ¼", }, { id: 2, itemName: "å å¾å°ºå¯¸", unit: "mm", standardValue: "50.00+0.02/-0", controlValue: "50.00+0.01/-0", actualValue: "50.01", result: "åæ ¼", }, { id: 3, itemName: "表é¢ç²ç³åº¦", unit: "Ra", standardValue: "â¤1.6", controlValue: "â¤1.2", actualValue: "0.8", result: "åæ ¼", }, ], }, { id: 2001, checkTime: "2024-03-01 11:30:00", workOrderNo: row.productNo, process: row.processName, checkName: "è´¨éé¨-ç建å½", productName: row.productName, model: row.model, unit: row.unit, quantity: row.completedQuantity, checkCompany: "å é¨å®éªå®¤", checkResult: "åæ ¼", inspectItems: [ { id: 1, itemName: "å¤å¾å°ºå¯¸", unit: "mm", standardValue: "100.00±0.05", controlValue: "100.00±0.03", actualValue: "100.01", result: "åæ ¼", }, { id: 2, itemName: "å å¾å°ºå¯¸", unit: "mm", standardValue: "50.00+0.02/-0", controlValue: "50.00+0.01/-0", actualValue: "50.01", result: "åæ ¼", }, { id: 3, itemName: "表é¢ç²ç³åº¦", unit: "Ra", standardValue: "â¤1.6", controlValue: "â¤1.2", actualValue: "0.8", result: "åæ ¼", }, ], }, ]; qualityDialogVisible.value = true; }; onMounted(() => { if (route.query.npsNo) { const npsNo = route.query.npsNo; const found = npsNoOptions.value.find(item => item.npsNo === npsNo); if (found) { selectedNpsNo.value = found.id; handleSearch(found.id); } else { // å¦ææ²¡æ¾å°ï¼å建ä¸ä¸ªä¸´æ¶ç const mockItem = { id: Date.now(), npsNo: npsNo, productName: route.query.productName || "ç²¾å¯æ¶²å缸", model: route.query.model || "HG-100/50-500", materialCode: "MAT-2024-MOCK", quantity: 100, unit: "ä»¶", status: 1, customerName: "模æå®¢æ·", startTime: "2024-03-01", completionStatus: 50, }; npsNoOptions.value.push(mockItem); selectedNpsNo.value = mockItem.id; handleSearch(mockItem.id); } } }); </script> <style scoped> .app-container { padding: 20px; background-color: #f5f7fa; min-height: 100vh; } .card-header { display: flex; justify-content: space-between; align-items: center; padding: 0 10px; } .search-form { width: 100%; } .search-form .el-form-item { margin-right: 10px; } .detail-section { margin-bottom: 24px; background-color: #ffffff; border-radius: 10px; padding: 24px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08); transition: all 0.3s ease; } .detail-section:hover { box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12); } .section-title { font-size: 16px; font-weight: 600; margin-bottom: 20px; color: #1a1a1a; border-bottom: 2px solid #409eff; padding-bottom: 10px; display: flex; align-items: center; } .section-title::before { content: ""; display: inline-block; width: 4px; height: 16px; background-color: #409eff; margin-right: 8px; border-radius: 2px; } .sub-section-title { font-size: 14px; font-weight: 600; margin-bottom: 16px; color: #303133; display: flex; align-items: center; } .sub-section-title::before { content: ""; display: inline-block; width: 3px; height: 12px; background-color: #67c23a; margin-right: 8px; border-radius: 2px; } .unit { font-size: 12px; color: #909399; margin-left: 4px; } :deep(.el-descriptions) { border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } :deep(.el-descriptions__row:nth-child(odd)) { background-color: #f9f9f9; } :deep(.el-descriptions__label) { font-weight: 500; color: #606266; background-color: #f5f7fa; } :deep(.el-descriptions__content) { color: #303133; font-weight: 500; } .progress-container { display: flex; gap: 24px; } .progress-section { margin-bottom: 24px; background-color: #ffffff; border-radius: 10px; padding: 24px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08); flex: 1; transition: all 0.3s ease; width: 100%; } .progress-section:hover { box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12); } .order-item { margin-bottom: 20px; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } :deep(.el-table) { border-radius: 8px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } :deep(.el-table th) { background-color: #f5f7fa !important; font-weight: 600; color: #606266; } :deep(.el-progress-bar__inner) { border-radius: 10px; } :deep(.el-tag) { border-radius: 12px; padding: 2px 10px; } /* å¼¹çªæ ·å¼ */ .detail-container { max-height: 600px; overflow-y: auto; padding: 0 16px; } .process-item { margin-bottom: 24px; padding: 20px; background-color: #ffffff; border-radius: 8px; border: 1px solid #ebeef5; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .process-header { margin-bottom: 20px; padding-bottom: 12px; border-bottom: 1px solid #f0f2f5; } .process-title { font-size: 15px; font-weight: 600; margin-bottom: 12px; color: #1a1a1a; display: flex; align-items: center; } .process-title::before { content: ""; display: inline-block; width: 4px; height: 16px; background-color: #409eff; margin-right: 8px; border-radius: 2px; } .process-info { display: flex; gap: 20px; font-size: 13px; color: #606266; } .process-label { padding: 4px 12px; background-color: #ecf5ff; border-radius: 4px; color: #409eff; font-weight: 500; } .process-details { margin-bottom: 20px; } .num1 { color: #1107cc; font-weight: 600; } .num2 { color: #0fcf25; font-weight: 600; } .num3 { color: #d31818; font-weight: 600; } .dialog-footer { text-align: center; padding: 20px; border-top: 1px solid #ebeef5; } .dialog-footer .el-button { min-width: 100px; padding: 8px 20px; } /* èªå®ä¹å¯¹è¯æ¡æ ·å¼ */ :deep(.custom-dialog) { border-radius: 12px; overflow: hidden; } :deep(.custom-dialog .el-dialog__header) { background-color: #f5f7fa; padding: 20px; border-bottom: 1px solid #ebeef5; } :deep(.custom-dialog .el-dialog__title) { font-size: 18px; font-weight: 600; color: #1a1a1a; } :deep(.custom-dialog .el-dialog__body) { padding: 20px; } .unit-text { margin-left: 5px; color: #909399; font-size: 12px; } .param-detail-list { padding: 10px; } </style> src/views/productionManagement/workOrderEdit/index.vue
@@ -204,6 +204,11 @@ width: "140", }, { label: "æå®æ¥å·¥äºº", prop: "userNames", width: "180", }, { label: "æä½", width: "200", align: "center", src/views/productionManagement/workOrderManagement/index.vue
@@ -244,6 +244,7 @@ @refresh="getList" /> <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" :editable="!currentWorkOrderRow?.endOrder" :record-type="'production_operation_task'" :record-id="currentWorkOrderId" /> </div> @@ -264,8 +265,11 @@ import QRCode from "qrcode"; import { getCurrentInstance, reactive, toRefs } from "vue"; import MaterialDialog from "./components/MaterialDialog.vue"; import FileList from "@/components/Dialog/FileList.vue"; const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); import useUserStore from "@/store/modules/user"; const { proxy } = getCurrentInstance(); const userStore = useUserStore(); const tableColumn = ref([ { @@ -368,7 +372,23 @@ clickFun: row => { showReportDialog(row); }, disabled: row => row.planQuantity <= 0, showHide: row => !row.endOrder, disabled: row => { if (row.planQuantity <= 0) return true; if (!row.userIds) return false; try { const userIds = typeof row.userIds === "string" ? JSON.parse(row.userIds) : row.userIds; if (Array.isArray(userIds)) { return !userIds.some(id => String(id) === String(userStore.id)); } return true; } catch (e) { return true; } }, }, ], }, @@ -614,9 +634,11 @@ const printTransferCard = () => { window.print(); }; const currentWorkOrderRow = ref(null); const openWorkOrderFiles = row => { currentWorkOrderId.value = row.id; currentWorkOrderRow.value = row; fileDialogVisible.value = true; }; @@ -801,6 +823,7 @@ }; onMounted(() => { userStore.getInfo(); getList(); // è·åç¨æ·å表 userListNoPageByTenantId().then(res => { src/views/safeProduction/dangerInvestigation/index.vue
@@ -415,17 +415,12 @@ </el-row> </el-form> </FormDialog> <!-- todo éä»¶é¢è§ç¸å ³ --> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" :show-upload-button="true" :show-delete-button="true" :is-show-pagination="true" :page="filePagination" :upload-method="handleUpload" :delete-method="handleFileDelete" @pagination="paginationSearch" title="éä»¶å表" /> <FileListDialog v-if="fileListDialogVisible" :record-id="currentRecordId" record-type="safe_hidden" v-model:visible="fileListDialogVisible"/> </div> </template> @@ -436,7 +431,6 @@ import { ElMessageBox, ElMessage } from "element-plus"; import useUserStore from "@/store/modules/user"; import { userListNoPage } from "@/api/system/user.js"; import FileListDialog from "@/components/Dialog/FileListDialog.vue"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; import { @@ -445,13 +439,9 @@ safeHiddenUpdate, safeHiddenDel, fileListPage, safeHiddenFileAdd, safeHiddenFileDel, } from "@/api/safeProduction/dangerInvestigation.js"; import useFormData from "@/hooks/useFormData.js"; import request from "@/utils/request"; import dayjs from "dayjs"; import { get } from "@vueuse/core"; const userStore = useUserStore(); const { proxy } = getCurrentInstance(); @@ -459,6 +449,7 @@ const selectedRows = ref([]); const userList = ref([]); const tableLoading = ref(false); const currentRecordId = ref(0); const page = reactive({ current: 1, size: 100, @@ -943,18 +934,8 @@ const fileListDialogVisible = ref(false); const currentFileRow = ref(null); const downLoadFile = row => { currentFileRow.value = row; fileListPage({ safeHiddenId: row.id, current: filePagination.value.current, size: filePagination.value.size, }).then(res => { if (fileListRef.value) { fileListRef.value.open(res.data.records || []); console.log("res.data", res.data); filePagination.value.total = res.data.total || 0; } }); currentRecordId.value = row.id; fileListDialogVisible.value = true; }; const currentUserId = ref(""); const currentUserName = ref(""); @@ -990,147 +971,6 @@ userList.value = res.data; }); }); // ä¸ä¼ éä»¶ 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 = { safeHiddenId: currentFileRow.value.id, name: uploadRes.data.originalName || file.name, url: uploadRes.data.tempPath || uploadRes.data.url, }; const saveRes = await safeHiddenFileAdd(fileData); if (saveRes.code === 200) { proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); // éæ°å è½½æä»¶å表 const listRes = await fileListPage({ safeHiddenId: currentFileRow.value.id, current: filePagination.value.current, size: filePagination.value.size, }); 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); filePagination.value.total = listRes.data?.total || 0; } // è¿åæ°æä»¶ä¿¡æ¯ 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 paginationSearch = async (page, size) => { filePagination.value.current = page; filePagination.value.size = size; const listRes = await fileListPage({ safeHiddenId: currentFileRow.value.id, current: filePagination.value.current, size: filePagination.value.size, }); 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); filePagination.value.total = listRes.data?.total || 0; } }; // å é¤éä»¶ const handleFileDelete = async row => { try { const res = await safeHiddenFileDel([row.id]); if (res.code === 200) { proxy.$modal.msgSuccess("å 餿å"); // éæ°å è½½æä»¶å表 if (currentFileRow.value && fileListRef.value) { const listRes = await fileListPage({ safeHiddenId: currentFileRow.value.id, current: filePagination.value.current, size: filePagination.value.size, }); 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); filePagination.value.total = listRes.data?.total || 0; } } return true; // è¿å true 表示å 餿åï¼ç»ä»¶ä¼æ´æ°å表 } else { proxy.$modal.msgError(res.msg || "å é¤å¤±è´¥"); return false; } } catch (error) { proxy.$modal.msgError("å é¤å¤±è´¥"); return false; } }; </script> <style scoped lang="scss"> src/views/safeProduction/safeQualifications/index.vue
@@ -110,7 +110,7 @@ <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">éä»¶</el-button> @click="openFileDialog(scope.row)">éä»¶</el-button> </template> </el-table-column> </el-table> @@ -203,16 +203,7 @@ </el-form> </FormDialog> <!-- todo éä»¶é¢è§ç¸å ³ --> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" :show-upload-button="true" :show-delete-button="true" :is-show-pagination="true" :page="filePagination" :upload-method="handleUpload" :delete-method="handleFileDelete" @pagination="paginationSearch" title="éä»¶å表" /> <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="safe_certification" :record-id="recordId" /> </div> </template> @@ -223,7 +214,6 @@ import { ElMessageBox, ElMessage } from "element-plus"; import useUserStore from "@/store/modules/user"; import { userListNoPage } from "@/api/system/user.js"; import FileListDialog from "@/components/Dialog/FileListDialog.vue"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; import { @@ -238,7 +228,7 @@ import useFormData from "@/hooks/useFormData.js"; import request from "@/utils/request"; import dayjs from "dayjs"; const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); const userStore = useUserStore(); const { proxy } = getCurrentInstance(); const tableData = ref([]); @@ -524,19 +514,17 @@ size: 10, total: 0, }); const downLoadFile = row => { currentFileRow.value = row; fileListPage({ safeCertificationId: row.id, current: filePagination.value.current, size: filePagination.value.size, }).then(res => { if (fileListRef.value) { fileListRef.value.open(res.data.records); } filePagination.value.total = res.data.total || 0; }); }; // æå¼éä»¶å¼¹çª const recordId =ref(0) const fileDialogVisible = ref(false) // æå¼éä»¶å¼¹æ¡ const openFileDialog = async (row) => { recordId.value = row.id fileDialogVisible.value = true } const currentFactoryName = ref(""); const getCurrentFactoryName = async () => { let res = await userStore.getInfo(); src/views/safeProduction/safetyTrainingAssessment/index.vue
@@ -239,7 +239,7 @@ <el-descriptions-item label="éä»¶å表:"> <el-button type="primary" size="small" @click="downLoadFile(endform)">éä»¶å表</el-button> @click="openFileDialog(endform)">éä»¶å表</el-button> </el-descriptions-item> </el-descriptions> <!-- <el-divider style="margin: 20px 0;" /> --> @@ -359,22 +359,12 @@ </template> </el-dialog> <!-- todo éä»¶é¢è§ç¸å ³ --> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" :show-upload-button="true" :show-delete-button="true" :is-show-pagination="true" :page="filePagination" :upload-method="handleUpload" :delete-method="handleFileDelete" @pagination="paginationSearch" title="éä»¶å表" /> <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="safe_training" :record-id="recordId" /> </div> </template> <script setup> import { Search } from "@element-plus/icons-vue"; import FileListDialog from "@/components/Dialog/FileListDialog.vue"; import { onMounted, ref, @@ -403,6 +393,7 @@ import useUserStore from "@/store/modules/user"; import dayjs from "dayjs"; const userStore = useUserStore(); const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); // 表åéªè¯è§å const rules = { @@ -621,7 +612,7 @@ name: "éä»¶", type: "text", clickFun: row => { downLoadFile(row); openFileDialog(row); }, color: "#007AFF", }, @@ -783,27 +774,17 @@ form.value.principalMobile = selectedUser.phonenumber; } }; /** * ä¸è½½æä»¶ * * @param row ä¸è½½æä»¶çç¸å ³ä¿¡æ¯å¯¹è±¡ */ const fileListRef = ref(null); const fileListDialogVisible = ref(false); const currentFileRow = ref(null); const downLoadFile = row => { currentFileRow.value = row; safeTrainingFileListPage({ safeTrainingId: row.id, current: filePagination.value.current, size: filePagination.value.size, }).then(res => { if (fileListRef.value) { fileListRef.value.open(res.data.records); filePagination.value.total = res.data?.total || 0; } }); }; // æå¼éä»¶å¼¹çª const recordId =ref(0) const fileDialogVisible = ref(false) // æå¼éä»¶å¼¹æ¡ const openFileDialog = async (row) => { recordId.value = row.id fileDialogVisible.value = true } // ä¸ä¼ éä»¶ const handleUpload = async () => { if (!currentFileRow.value) { src/views/salesManagement/invoiceLedger/index.vue
@@ -44,7 +44,7 @@ <el-table-column fixed="right" label="æä½" width="150" align="center"> <template #default="scope"> <el-button link type="primary" @click="openForm(scope.row)">ç¼è¾</el-button> <el-button link type="primary" @click="downLoadFile(scope.row)">éä»¶</el-button> <el-button link type="primary" @click="openFileDialog(scope.row)">éä»¶</el-button> <el-button link type="primary" @click="delInvoiceLedger(scope.row)">å é¤</el-button> </template> </el-table-column> @@ -134,7 +134,7 @@ </div> </template> </el-dialog> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" /> <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="invoice_registration_product" :record-id="recordId" /> </div> </template> @@ -155,8 +155,8 @@ import useUserStore from "@/store/modules/user.js"; import useFormData from "@/hooks/useFormData"; import dayjs from "dayjs"; import FileListDialog from '@/components/Dialog/FileListDialog.vue'; import { getCurrentDate } from "@/utils/index.js"; const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); const { proxy } = getCurrentInstance(); const tableData = ref([]); @@ -422,17 +422,14 @@ getList(); }; //éä»¶ç¸å ³ const fileListRef = ref(null) const fileListDialogVisible = ref(false) //æ¥çéä»¶ const downLoadFile = (row) => { invoiceLedgerProductInfo({ id: row.id }).then((res) => { if (fileListRef.value) { fileListRef.value.open(res.data.fileList) fileListDialogVisible.value = true } }); // æå¼éä»¶å¼¹çª const recordId =ref(0) const fileDialogVisible = ref(false) // æå¼éä»¶å¼¹æ¡ const openFileDialog = async (row) => { recordId.value = row.id fileDialogVisible.value = true } onMounted(() => { src/views/salesManagement/salesLedger/index.vue
@@ -231,7 +231,7 @@ :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">ç¼è¾</el-button> <el-button link type="primary" @click="downLoadFile(scope.row)">éä»¶</el-button> @click="openFileDialog(scope.row)">éä»¶</el-button> </template> </el-table-column> </el-table> @@ -740,9 +740,7 @@ </FormDialog> <!-- // todo éä»¶é¢è§ç¸å ³ --> <!-- éä»¶åè¡¨å¼¹çª --> <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" title="éä»¶å表" /> <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="sales_ledger" :record-id="recordId" /> <!-- æå°é¢è§å¼¹çª --> <el-dialog v-model="printPreviewVisible" title="æå°é¢è§" @@ -904,10 +902,8 @@ import { onMounted, ref, getCurrentInstance } from "vue"; import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js"; import { ElMessageBox, ElMessage } from "element-plus"; import { UploadFilled, Download } from "@element-plus/icons-vue"; import useUserStore from "@/store/modules/user"; import { userListNoPage } from "@/api/system/user.js"; import FileListDialog from "@/components/Dialog/FileListDialog.vue"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; import { @@ -929,6 +925,9 @@ import { useRouter, useRoute } from "vue-router"; import { listCustomerPrivatePool } from "@/api/basicData/customerFile.js"; import FileUpload from "@/components/AttachmentUpload/file/index.vue"; const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); const router = useRouter(); const route = useRoute(); const userStore = useUserStore(); @@ -2440,20 +2439,15 @@ return statusStr === "å¾ åè´§" || statusStr === "å®¡æ ¸æç»"; }; /** * ä¸è½½æä»¶ * * @param row ä¸è½½æä»¶çç¸å ³ä¿¡æ¯å¯¹è±¡ */ const fileListRef = ref(null); const fileListDialogVisible = ref(false); const downLoadFile = row => { getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => { if (fileListRef.value) { fileListRef.value.open(res.salesLedgerFiles); } }); }; // æå¼éä»¶å¼¹çª const recordId =ref(0) const fileDialogVisible = ref(false) // æå¼éä»¶å¼¹æ¡ const openFileDialog = async (row) => { recordId.value = row.id fileDialogVisible.value = true } // æå¼åè´§å¼¹æ¡ const openDeliveryForm = row => {