Merge branch 'dev_NEW_pro' of http://114.132.189.42:9002/r/product-inventory-management into dev_西宁_青铝绿行
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # è´¢å¡ç®¡çåç«¯ææ¡£ï¼ä»
è´è´£æ¨¡åï¼ |
| | | |
| | | æ´æ°æ¶é´ï¼2026-05-12 |
| | | éç¨èå´ï¼ä»
ä»¥ä¸ 6 个模åï¼ï¼ |
| | | 1. åºå®èµäº§ï¼`/financial/fixed-assets`ï¼ |
| | | 2. æ å½¢èµäº§ï¼`/financial/intangible-assets`ï¼ |
| | | 3. æ»è´¦ç§ç®ï¼`/financial/general-ledger`ï¼ |
| | | 4. åè¯ï¼`/financial/voucher`ï¼ |
| | | 5. ç§ç®æ»è´¦ï¼`/financial/voucher-general-ledger`ï¼ |
| | | 6. ç§ç®æç»è´¦ï¼`/financial/voucher-detail-ledger`ï¼ |
| | | |
| | | --- |
| | | |
| | | ## 1. ç»ä¸çº¦å® |
| | | |
| | | ### 1.1 ååºç»æ |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "success", |
| | | "data": {} |
| | | } |
| | | ``` |
| | | |
| | | ### 1.2 åé¡µç»æï¼å¦ææ¯å页æ¥å£ï¼ |
| | | 请æ±åæ°å»ºè®®ï¼ |
| | | - `current`ï¼é¡µç ï¼ |
| | | - `size`ï¼æ¯é¡µæ¡æ°ï¼ |
| | | |
| | | ååºå»ºè®®ï¼ |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "data": { |
| | | "records": [], |
| | | "total": 0 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 1.3 éé¢ä¸ç²¾åº¦ |
| | | - éé¢å段建议 `decimal(18,2)`ã |
| | | - åå端ç»ä¸ä¿ç两ä½å°æ°ã |
| | | |
| | | --- |
| | | |
| | | ## 2. 模åä¸ï¼æ»è´¦ç§ç®ï¼å·²æ¥çå® APIï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/generalLedger/index.vue` |
| | | API æä»¶ï¼`src/api/financialManagement/accountSubject.js` |
| | | |
| | | ### 2.1 æ¥å£ç°ç¶ |
| | | - `GET /accountSubject/list` |
| | | - `POST /accountSubject/add` |
| | | - `PUT /accountSubject/edit` |
| | | - `DELETE /accountSubject/remove/{ids}` |
| | | - `POST /accountSubject/export` |
| | | |
| | | ### 2.2 åæ®µæ¨¡å |
| | | - `id` |
| | | - `subjectCode`ï¼ç§ç®ç¼ç ï¼ |
| | | - `subjectName`ï¼ç§ç®åç§°ï¼ |
| | | - `subjectType`ï¼ç§ç®ç±»åï¼ |
| | | - `balanceDirection`ï¼ä½é¢æ¹åï¼åæ¹/è´·æ¹ï¼ |
| | | - `status`ï¼0 å¯ç¨ï¼1 ç¦ç¨ï¼ |
| | | - `remark` |
| | | |
| | | ### 2.3 ä¸å¡è§å |
| | | - `subjectCode`ã`subjectName`ã`subjectType` å¿
å¡«ã |
| | | - å é¤éè¦åå¼ç¨æ ¡éªï¼è¥å·²è¢«åè¯åå½å¼ç¨ï¼ä¸å
许å é¤ï¼ã |
| | | |
| | | --- |
| | | |
| | | ## 3. 模åäºï¼åºå®èµäº§ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/assets/fixedAssets.vue` |
| | | |
| | | ### 3.1 建议æ¥å£ |
| | | - `GET /financial/fixedAsset/page` |
| | | - `POST /financial/fixedAsset/add` |
| | | - `PUT /financial/fixedAsset/update` |
| | | - `DELETE /financial/fixedAsset/delete` |
| | | - `POST /financial/fixedAsset/depreciate`ï¼ææè®¡æï¼ |
| | | |
| | | ### 3.2 åæ®µæ¨¡å |
| | | - `id, assetCode, assetName, category, specification` |
| | | - `purchaseDate, originalValue, usefulLife, residualRate` |
| | | - `accumulatedDepreciation, netValue` |
| | | - `location, department, keeper, status, remark` |
| | | |
| | | ### 3.3 æ ¸å¿å
¬å¼ï¼å¿
é¡»ä¸è´ï¼ |
| | | - `monthlyDepreciation = originalValue * (1 - residualRate/100) / (usefulLife*12)` |
| | | - `accumulatedDepreciation += monthlyDepreciation` |
| | | - `netValue = originalValue - accumulatedDepreciation` |
| | | |
| | | ### 3.4 ç¶æå»ºè®® |
| | | - `in_use`ï¼å¨ç¨ï¼ |
| | | - `idle`ï¼é²ç½®ï¼ |
| | | - `repair`ï¼ç»´ä¿®ä¸ï¼ |
| | | - `scrapped`ï¼æ¥åºï¼ |
| | | |
| | | --- |
| | | |
| | | ## 4. 模åä¸ï¼æ å½¢èµäº§ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/assets/intangibleAssets.vue` |
| | | |
| | | ### 4.1 建议æ¥å£ |
| | | - `GET /financial/intangibleAsset/page` |
| | | - `POST /financial/intangibleAsset/add` |
| | | - `PUT /financial/intangibleAsset/update` |
| | | - `DELETE /financial/intangibleAsset/delete` |
| | | - `POST /financial/intangibleAsset/amortize`ï¼æææéï¼ |
| | | |
| | | ### 4.2 åæ®µæ¨¡å |
| | | - `id, assetCode, assetName, category, certificateNo` |
| | | - `acquisitionDate, originalValue, amortizationPeriod, residualRate` |
| | | - `accumulatedAmortization, netValue` |
| | | - `validityDate, status, description, remark` |
| | | |
| | | ### 4.3 æ ¸å¿å
¬å¼ï¼å¿
é¡»ä¸è´ï¼ |
| | | - `monthlyAmortization = originalValue * (1 - residualRate/100) / (amortizationPeriod*12)` |
| | | - `accumulatedAmortization += monthlyAmortization` |
| | | - `netValue = originalValue - accumulatedAmortization` |
| | | - å½ `netValue <= 0`ï¼ |
| | | - `netValue = 0` |
| | | - `status = amortized` |
| | | |
| | | ### 4.4 ç¶æå»ºè®® |
| | | - `in_use`ï¼å¨ç¨ï¼ |
| | | - `expired`ï¼å°æï¼ |
| | | - `amortized`ï¼å·²æéå®ï¼ |
| | | |
| | | --- |
| | | |
| | | ## 5. 模ååï¼åè¯ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/voucher/index.vue` |
| | | |
| | | ### 5.1 建议æ¥å£ |
| | | - `GET /financial/voucher/page` |
| | | - `POST /financial/voucher/add` |
| | | - `PUT /financial/voucher/update` |
| | | - `POST /financial/voucher/post`ï¼è¿è´¦ï¼ |
| | | - `POST /financial/voucher/cancel`ï¼ä½åºï¼ |
| | | - `GET /financial/voucher/detail/{id}` |
| | | |
| | | ### 5.2 ä¸»è¡¨åæ®µ |
| | | - `id, voucherNo, voucherDate, summary` |
| | | - `debit, credit, creator, status, attachmentCount, remark` |
| | | |
| | | ### 5.3 åå½å段 |
| | | - `subjectCode, subjectName, summary, debit, credit` |
| | | |
| | | ### 5.4 å
³é®æ ¡éª |
| | | - åå½è³å°ä¸æ¡ææè¡ï¼ç§ç®ä¸ç©ºï¼ä¸åæ¹æè´·æ¹ > 0ï¼ã |
| | | - å贷平衡ï¼`sum(debit) == sum(credit)` ä¸ > 0ï¼ä¸æ»¡è¶³ç¦æ¢ä¿åã |
| | | |
| | | ### 5.5 ç¶ææµè½¬ |
| | | - `unposted -> posted` |
| | | - `unposted -> cancelled` |
| | | |
| | | --- |
| | | |
| | | ## 6. 模åäºï¼ç§ç®æ»è´¦ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/voucher/generalLedger.vue` |
| | | |
| | | ### 6.1 建议æ¥å£ |
| | | - `GET /financial/ledger/general` |
| | | |
| | | ### 6.2 请æ±åæ° |
| | | - `subjectCode`ï¼æ«çº§ææå®ç§ç®ï¼ |
| | | - `startMonth`ï¼YYYY-MMï¼ |
| | | - `endMonth`ï¼YYYY-MMï¼ |
| | | |
| | | ### 6.3 ååºå段 |
| | | - `date, voucherNo, summary` |
| | | - `debit, credit, direction, balance` |
| | | |
| | | ### 6.4 è§å |
| | | - ä»
å¨éæ©ç§ç®åè¿åæ°æ®ã |
| | | - æ¯æâæåä½é¢ / æ¬æå计 / æ¬å¹´ç´¯è®¡âè¡ï¼å¯éè¿ `rowType` åæ®µåºåï¼ã |
| | | |
| | | --- |
| | | |
| | | ## 7. 模åå
ï¼ç§ç®æç»è´¦ï¼å½åå端为 mockï¼å¾
å端å®ç°ï¼ |
| | | |
| | | å端æä»¶ï¼`src/views/financialManagement/voucher/detailLedger.vue` |
| | | |
| | | ### 7.1 建议æ¥å£ |
| | | - `GET /financial/ledger/detail` |
| | | |
| | | ### 7.2 请æ±åæ° |
| | | - `subjectCode` |
| | | - `auxiliaryType`ï¼customer/supplier/department/employee/projectï¼ |
| | | - `auxiliaryId` |
| | | - `startMonth`ï¼YYYY-MMï¼ |
| | | - `endMonth`ï¼YYYY-MMï¼ |
| | | |
| | | ### 7.3 ååºå段 |
| | | - `date, voucherNo, summary` |
| | | - `debit, credit, direction, balance` |
| | | |
| | | ### 7.4 è§å |
| | | - å
éç§ç®ï¼åæ¥æç»ã |
| | | - è¾
婿 ¸ç®æ¡ä»¶ä¸ºå¯éï¼ä½å»ºè®®åç«¯æ¯æç»´åº¦è¿æ»¤ã |
| | | |
| | | --- |
| | | |
| | | ## 8. æ¨èæå°è¡¨è®¾è®¡ï¼ä»
æ¬èå´ï¼ |
| | | |
| | | - `fin_account_subject` |
| | | - `fin_fixed_asset` |
| | | - `fin_intangible_asset` |
| | | - `fin_voucher` |
| | | - `fin_voucher_entry` |
| | | - `fin_ledger_snapshot_general`ï¼å¯éï¼åæ§è½ä¼åï¼ |
| | | - `fin_ledger_snapshot_detail`ï¼å¯éï¼åæ§è½ä¼åï¼ |
| | | |
| | | --- |
| | | |
| | | ## 9. AI çæå端任å¡é¡ºåºï¼å»ºè®®ï¼ |
| | | |
| | | 1. å
宿 **æ»è´¦ç§ç®**ï¼å·²æ APIï¼æç¨³å®ï¼ã |
| | | 2. 宿 **åè¯ + åå½ + åè´·å¹³è¡¡æ ¡éª + ç¶ææµè½¬**ã |
| | | 3. å®ç° **ç§ç®æ»è´¦ / ç§ç®æç»è´¦** æ¥è¯¢ã |
| | | 4. å®ç° **åºå®èµäº§ææ§** ä¸ **æ å½¢èµäº§æé**ã |
| | | 5. è¡¥æµè¯ï¼ |
| | | - åè´·å¹³è¡¡æ ¡éª |
| | | - ææ§/æéå
¬å¼ |
| | | - ç§ç®è¢«å¼ç¨ç¦æ¢å é¤ |
| | | |
| | |
| | | "BTYX": { |
| | | "env": { |
| | | "VITE_APP_TITLE": "æ²³å帮太ä¼éè¿åºå£æéå
¬å¸", |
| | | "VITE_BASE_API": "http://127.0.0.1:9001", |
| | | "VITE_JAVA_API": "http://127.0.0.1:9000" |
| | | "VITE_BASE_API": "http://1.15.17.182:9056", |
| | | "VITE_JAVA_API": "http://1.15.17.182:9057" |
| | | }, |
| | | "logo": "logo/BTYXLogo.png", |
| | | "favicon": "favicon/BTYXfavicon.ico" |
| | |
| | | "logo": "logo/ZXZNLogo.png", |
| | | "favicon": "favicon/ZXZNfavicon.ico" |
| | | }, |
| | | "HYZC": { |
| | | "env": { |
| | | "VITE_APP_TITLE": "山西åäº¿ä¼æå»ºææéå
¬å¸", |
| | | "VITE_BASE_API": "http://36.137.13.103:9001", |
| | | "VITE_JAVA_API": "http://36.137.13.103:9000" |
| | | }, |
| | | "logo": "logo/HYZCLogo.png", |
| | | "favicon": "favicon/HYZCfavicon.ico" |
| | | }, |
| | | "WTXC": { |
| | | "env": { |
| | | "VITE_APP_TITLE": "å®å¤ä¸éæ°æ", |
| | | "VITE_BASE_API": "http://42.63.71.140:9001", |
| | | "VITE_JAVA_API": "http://42.63.71.140:9000" |
| | | }, |
| | | "logo": "logo/WTXCLogo.png", |
| | | "favicon": "favicon/WTXCfavicon.ico" |
| | | }, |
| | | "KYHG": { |
| | | "env": { |
| | | "VITE_APP_TITLE": "å±±è¥¿å¤æºåå·¥æéå
¬å¸", |
| | | "VITE_BASE_API": "http://36.137.13.29:9001", |
| | | "VITE_JAVA_API": "http://36.137.13.29:9000" |
| | | }, |
| | | "logo": "logo/KYHGLogo.png", |
| | | "favicon": "favicon/KYHGfavicon.ico" |
| | | }, |
| | | "DYKJ": { |
| | | "env": { |
| | | "VITE_APP_TITLE": "山西德çç§ææéå
¬å¸", |
| | | "VITE_BASE_API": "http://36.137.12.37:9001", |
| | | "VITE_JAVA_API": "http://36.137.12.37:9000" |
| | | }, |
| | | "logo": "logo/DYKJLogo.png", |
| | | "favicon": "favicon/DYKJfavicon.ico" |
| | | }, |
| | | "logo": "/src/assets/logo/logo.png", |
| | | "favicon": "/public/favicon.ico" |
| | | } |
| | |
| | | }) |
| | | } |
| | | |
| | | // æµå
¥å
¬æµ· |
| | | export function backCustomer(id) { |
| | | return request({ |
| | | url: '/basic/customer/back/' + id, |
| | | method: 'post' |
| | | }) |
| | | } |
| | | |
| | | export function shareCustomer(data) { |
| | | return request({ |
| | | url: '/basic/customer/together', |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | /** éè´å
¥åºå页å表 */ |
| | | export const listPageAccountPurchase = (params) => { |
| | | return request({ |
| | | url: "/accountPurchase/listPageAccountPurchase", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | /** éè´éè´§å页å表 */ |
| | | export const listPageAccountPurchaseReturn = (params) => { |
| | | return request({ |
| | | url: "/accountPurchase/listPageAccountPurchaseReturn", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | /** éå®åºåºå页å表 */ |
| | | export const listPageAccountSales = (params) => { |
| | | return request({ |
| | | url: "/accountSales/listPageAccountSales", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | /** éå®éè´§å页å表 */ |
| | | export const listPageAccountSalesReturn = (params) => { |
| | | return request({ |
| | | url: "/accountSales/listPageAccountSalesReturn", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // åºå®èµäº§å页æ¥è¯¢ï¼current/sizeï¼ |
| | | export function listFixedAssetPage(params) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢åºå®èµäº§ |
| | | export function addFixedAsset(data) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹åºå®èµäº§ |
| | | export function updateFixedAsset(data) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/update", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤åºå®èµäº§ï¼åç«¯è¦æ± ids=1&ids=2 å½¢å¼ï¼ |
| | | export function deleteFixedAsset(ids) { |
| | | const idList = Array.isArray(ids) ? ids : [ids]; |
| | | const query = idList |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | .map(id => `ids=${encodeURIComponent(id)}`) |
| | | .join("&"); |
| | | return request({ |
| | | url: `/financial/fixedAsset/delete?${query}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | // ææ§è®¡æï¼{} 表示å
¨é¨å¨ç¨èµäº§ï¼ |
| | | export function depreciateFixedAsset(data = {}) { |
| | | return request({ |
| | | url: "/financial/fixedAsset/depreciate", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ å½¢èµäº§å页æ¥è¯¢ï¼current/sizeï¼ |
| | | export function listIntangibleAssetPage(params) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢æ å½¢èµäº§ |
| | | export function addIntangibleAsset(data) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹æ å½¢èµäº§ |
| | | export function updateIntangibleAsset(data) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/update", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å 餿 å½¢èµäº§ï¼åç«¯è¦æ± ids=1&ids=2 å½¢å¼ï¼ |
| | | export function deleteIntangibleAsset(ids) { |
| | | const idList = Array.isArray(ids) ? ids : [ids]; |
| | | const query = idList |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | .map(id => `ids=${encodeURIComponent(id)}`) |
| | | .join("&"); |
| | | return request({ |
| | | url: `/financial/intangibleAsset/delete?${query}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | // æé计æï¼{} 表示å
¨é¨å¨ç¨èµäº§ï¼ |
| | | export function amortizeIntangibleAsset(data = {}) { |
| | | return request({ |
| | | url: "/financial/intangibleAsset/amortize", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // ç§ç®æ»è´¦ |
| | | export function getGeneralLedger(params) { |
| | | return request({ |
| | | url: "/financial/ledger/general", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // ç§ç®æç»è´¦ |
| | | export function getDetailLedger(params) { |
| | | return request({ |
| | | url: "/financial/ledger/detail", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // åè¯å页æ¥è¯¢ï¼current/size + è¿æ»¤æ¡ä»¶ï¼ |
| | | export function listVoucherPage(params) { |
| | | return request({ |
| | | url: "/financial/voucher/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢åè¯ |
| | | export function addVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹åè¯ï¼ä»
æªè¿è´¦ï¼ |
| | | export function updateVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/update", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // è¿è´¦ |
| | | export function postVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/post", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ä½åº |
| | | export function cancelVoucher(data) { |
| | | return request({ |
| | | url: "/financial/voucher/cancel", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // 详æ
|
| | | export function getVoucherDetail(id) { |
| | | return request({ |
| | | url: `/financial/voucher/detail/${id}`, |
| | | method: "get", |
| | | }); |
| | | } |
| | |
| | | }); |
| | | } |
| | | |
| | | // æ ¹æ®éè´å°è´¦ ID æ¥è¯¢å¯é产åçä¿¡æ¯ |
| | | export function getPurchaseReturnOrderByPurchaseLedgerId(query) { |
| | | return request({ |
| | | url: "/purchaseReturnOrders/getByPurchaseLedgerId", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ¥ç详æ
|
| | | // purchaseReturnOrders/selectById/xxx |
| | | export function getPurchaseReturnOrderDetail(id) { |
| | |
| | | name: "PurchaseIn", |
| | | meta: { title: "éè´å
¥åº" }, |
| | | }, |
| | | |
| | | { |
| | | path: "purchase-return", |
| | | component: () => import("@/views/financialManagement/payable/purchaseReturn.vue"), |
| | | name: "PurchaseReturn", |
| | | meta: { title: "éè´éè´§" }, |
| | | }, |
| | | { |
| | | path: "input-invoice", |
| | | component: () => import("@/views/financialManagement/payable/input-invoice.vue"), |
| | |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢å®¢æ·</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="back">æµå
¥å
¬æµ·</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="info" |
| | | plain |
| | |
| | | addReturnVisit, |
| | | getReturnVisit, |
| | | } from "@/api/basicData/customerFile.js"; |
| | | import {listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer} from "@/api/basicData/customer.js"; |
| | | import {listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer, backCustomer} from "@/api/basicData/customer.js"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | |
| | | }); |
| | | }; |
| | | |
| | | const back = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id); |
| | | ElMessageBox.confirm("éä¸ç客æ·å°æµå
¥å
¬æµ·ï¼æ¯å¦ç¡®è®¤ï¼", "æµå
¥å
¬æµ·æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | return Promise.all(ids.map(id => backCustomer(id))) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("æµå
¥å
¬æµ·æå"); |
| | | selectedRows.value = []; |
| | | getList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(error => { |
| | | if (error === "cancel" || error === "close") { |
| | | proxy.$modal.msg("已忶"); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // æå¼å访æéå¼¹çª |
| | | const openReminderDialog = row => { |
| | | currentCustomerId.value = row.id; |
| | |
| | | <el-button type="primary" |
| | | link |
| | | :disabled="isTopLevelNode(data, node)" |
| | | @click="openProDia('edit', data)"> |
| | | @click="openProDia('edit', data, node)"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="openProDia('add', data)"> |
| | | @click="openProDia('add', data, node)"> |
| | | æ·»å 产å |
| | | </el-button> |
| | | <el-button v-if="!node.childNodes.length" |
| | |
| | | const search = ref(""); |
| | | const currentId = ref(""); |
| | | const currentParentId = ref(""); |
| | | /** 产åå¼¹çªï¼add åç¶èç¹ idï¼edit åå½åèç¹ id ä¸ parentIdï¼ä¸ä¾èµæ éä¸é¡¹ï¼ */ |
| | | const productDialogTarget = ref(null); |
| | | const operationType = ref(""); |
| | | const treeLoad = ref(false); |
| | | const list = ref([]); |
| | |
| | | return [null, undefined, "", 0, "0"].includes(data?.parentId); |
| | | }; |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProDia = (type, data) => { |
| | | if (data && type === "edit" && isTopLevelNode(data)) { |
| | | const openProDia = (type, data, node) => { |
| | | if (data && type === "edit" && isTopLevelNode(data, node)) { |
| | | proxy.$modal.msgWarning("ä¸çº§èç¹ä¸è½ç¼è¾æå é¤"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | productDia.value = true; |
| | | form.value.productName = ""; |
| | | if (type === "edit") { |
| | | form.value.productName = data.productName; |
| | | productDialogTarget.value = null; |
| | | if (type === "add" && data) { |
| | | productDialogTarget.value = { parentId: data.id }; |
| | | } else if (type === "edit" && data) { |
| | | let parentId = data.parentId; |
| | | if ( |
| | | [null, undefined, ""].includes(parentId) && |
| | | node?.parent?.data?.id != null |
| | | ) { |
| | | parentId = node.parent.data.id; |
| | | } |
| | | productDialogTarget.value = { id: data.id, parentId }; |
| | | } |
| | | productDia.value = true; |
| | | form.value.productName = |
| | | type === "edit" && data ? data.productName : ""; |
| | | }; |
| | | // æå¼è§æ ¼åå·å¼¹æ¡ |
| | | const openModelDia = (type, data) => { |
| | |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add") { |
| | | form.value.parentId = currentId.value; |
| | | form.value.parentId = |
| | | productDialogTarget.value?.parentId ?? currentId.value; |
| | | form.value.id = ""; |
| | | } else if (operationType.value === "addOne") { |
| | | form.value.id = ""; |
| | | form.value.parentId = ""; |
| | | } else { |
| | | form.value.id = currentId.value; |
| | | form.value.parentId = ""; |
| | | form.value.id = |
| | | productDialogTarget.value?.id ?? currentId.value; |
| | | form.value.parentId = productDialogTarget.value?.parentId ?? ""; |
| | | } |
| | | addOrEditProduct(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProDia = () => { |
| | | proxy.$refs.formRef.resetFields(); |
| | | productDialogTarget.value = null; |
| | | productDia.value = false; |
| | | }; |
| | | |
| | |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #originalValue="{ row }"> |
| | |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-form :model="form" :rules="rules" :disabled="isView" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç¼å·" prop="assetCode"> |
| | |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button v-if="!isView" type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | | listFixedAssetPage, |
| | | addFixedAsset, |
| | | updateFixedAsset, |
| | | deleteFixedAsset, |
| | | depreciateFixedAsset, |
| | | } from "@/api/financialManagement/fixedAsset"; |
| | | |
| | | defineOptions({ |
| | | name: "åºå®èµäº§", |
| | |
| | | const columns = [ |
| | | { label: "èµäº§ç¼å·", prop: "assetCode", width: "130" }, |
| | | { label: "èµäº§åç§°", prop: "assetName", width: "150" }, |
| | | { label: "èµäº§ç±»å«", prop: "category", slot: "category" }, |
| | | { label: "èµäº§ç±»å«", prop: "category", dataType: "slot", slot: "category" }, |
| | | { label: "è§æ ¼åå·", prop: "specification", width: "120" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", slot: "originalValue" }, |
| | | { label: "ç´¯è®¡ææ§", prop: "accumulatedDepreciation", slot: "accumulatedDepreciation" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "180", fixed: "right" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", dataType: "slot", slot: "originalValue" }, |
| | | { label: "ç´¯è®¡ææ§", prop: "accumulatedDepreciation", dataType: "slot", slot: "accumulatedDepreciation" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", dataType: "slot", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", dataType: "slot", slot: "status" }, |
| | | { label: "æä½", prop: "operation", dataType: "slot", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const multipleList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const isView = ref(false); |
| | | const currentId = ref(null); |
| | | const selectedIds = computed(() => |
| | | multipleList.value |
| | | .map(item => item?.id) |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | ); |
| | | |
| | | const form = reactive({ |
| | | const createDefaultForm = () => ({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | |
| | | const rules = { |
| | | assetName: [{ required: true, message: "请è¾å
¥èµäº§åç§°", trigger: "blur" }], |
| | | category: [{ required: true, message: "è¯·éæ©èµäº§ç±»å«", trigger: "change" }], |
| | |
| | | originalValue: [{ required: true, message: "请è¾å
¥èµäº§åå¼", trigger: "blur" }], |
| | | usefulLife: [{ required: true, message: "请è¾å
¥ä½¿ç¨å¹´é", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, assetCode: "GD2024001", assetName: "åå
¬çµè", category: "electronic", specification: "èæ³ThinkPad X1", purchaseDate: "2023-01-15", originalValue: 8000, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 1520, netValue: 6480, location: "åå
¬å®¤", department: "è´¢å¡é¨", keeper: "å¼ ä¸", status: "in_use", remark: "" }, |
| | | { id: 2, assetCode: "GD2024002", assetName: "æå°æº", category: "electronic", specification: "æ æ®M479fdw", purchaseDate: "2023-03-20", originalValue: 3500, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 532, netValue: 2968, location: "æå°å®¤", department: "è¡æ¿é¨", keeper: "æå", status: "in_use", remark: "" }, |
| | | { id: 3, assetCode: "GD2024003", assetName: "åå
¬æ¡æ¤
", category: "furniture", specification: "宿¨åå
¬æ¡", purchaseDate: "2023-06-10", originalValue: 2500, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 118.75, netValue: 2381.25, location: "åå
¬å®¤", department: "éå®é¨", keeper: "çäº", status: "in_use", remark: "" }, |
| | | { id: 4, assetCode: "GD2024004", assetName: "åå¡è½¦", category: "vehicle", specification: "å«å
GL8", purchaseDate: "2022-08-01", originalValue: 280000, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 53200, netValue: 226800, location: "å车åº", department: "è¡æ¿é¨", keeper: "èµµå
", status: "in_use", remark: "" }, |
| | | ]; |
| | | |
| | | const totalOriginalValue = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0); |
| | |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { in_use: "å¨ç¨", idle: "é²ç½®", scrapped: "æ¥åº" }; |
| | | return map[status] || status; |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { in_use: "å¨ç¨", idle: "é²ç½®", repair: "ç»´ä¿®ä¸", scrapped: "æ¥åº" }; |
| | | return map[key] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { in_use: "success", idle: "warning", scrapped: "info" }; |
| | | return map[status] || ""; |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { in_use: "success", idle: "warning", repair: "warning", scrapped: "info" }; |
| | | return map[key] || ""; |
| | | }; |
| | | |
| | | const calculateNetValue = () => { |
| | | form.netValue = Number((form.originalValue - form.accumulatedDepreciation).toFixed(2)); |
| | | const originalValue = Number(form.originalValue || 0); |
| | | const accumulatedDepreciation = Number(form.accumulatedDepreciation || 0); |
| | | form.netValue = Number((originalValue - accumulatedDepreciation).toFixed(2)); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.assetCode) { |
| | | result = result.filter(item => item.assetCode.includes(filters.assetCode)); |
| | | // èè°çº¦å®ï¼å页忰åºå®ä¸º current/sizeï¼è¿å data.records/data.total |
| | | const getTableData = async () => { |
| | | try { |
| | | const { data } = await listFixedAssetPage({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | assetCode: filters.assetCode, |
| | | assetName: filters.assetName, |
| | | category: filters.category, |
| | | status: filters.status, |
| | | }); |
| | | dataList.value = data?.records || []; |
| | | multipleList.value = []; |
| | | pagination.total = Number(data?.total || 0); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | if (filters.assetName) { |
| | | result = result.filter(item => item.assetName.includes(filters.assetName)); |
| | | } |
| | | if (filters.category) { |
| | | result = result.filter(item => item.category === filters.category); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `GD${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "æ°å¢åºå®èµäº§"; |
| | | Object.assign(form, { |
| | | assetCode: "GD" + Date.now().toString().slice(-8), |
| | | assetName: "", |
| | | category: "", |
| | | specification: "", |
| | | Object.assign(form, createDefaultForm(), { |
| | | assetCode: buildAssetCode(), |
| | | purchaseDate: new Date().toISOString().split('T')[0], |
| | | originalValue: 0, |
| | | usefulLife: 5, |
| | | residualRate: 5, |
| | | accumulatedDepreciation: 0, |
| | | netValue: 0, |
| | | location: "", |
| | | department: "", |
| | | keeper: "", |
| | | status: "in_use", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | isView.value = false; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åºå®èµäº§"; |
| | | Object.assign(form, row); |
| | | Object.assign(form, createDefaultForm(), row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çèµäº§: ${row.assetName}`); |
| | | edit(row); |
| | | isView.value = true; |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | }).then(async () => { |
| | | // èè°çº¦å®ï¼å 餿¥å£ä½¿ç¨ ids=1&ids=2 |
| | | await deleteFixedAsset([row.id]); |
| | | if (dataList.value.length === 1 && pagination.currentPage > 1) { |
| | | pagination.currentPage -= 1; |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleDepreciation = () => { |
| | | ElMessageBox.confirm("确认è¿è¡æ¬æææ§è®¡æåï¼", "æç¤º", { |
| | | const ids = selectedIds.value; |
| | | const confirmText = ids.length |
| | | ? `确认对éä¸ç ${ids.length} æ¡èµäº§è¿è¡æ¬æææ§è®¡æåï¼` |
| | | : "确认è¿è¡æ¬æææ§è®¡æåï¼"; |
| | | ElMessageBox.confirm(confirmText, "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | mockData.forEach(item => { |
| | | if (item.status === "in_use") { |
| | | const monthlyDepreciation = (item.originalValue * (1 - item.residualRate / 100)) / (item.usefulLife * 12); |
| | | item.accumulatedDepreciation = Number((item.accumulatedDepreciation + monthlyDepreciation).toFixed(2)); |
| | | item.netValue = Number((item.originalValue - item.accumulatedDepreciation).toFixed(2)); |
| | | } |
| | | }); |
| | | }).then(async () => { |
| | | await depreciateFixedAsset({ ids }); |
| | | ElMessage.success("ææ§è®¡æå®æ"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (isView.value) { |
| | | dialogVisible.value = false; |
| | | return; |
| | | } |
| | | formRef.value.validate(async valid => { |
| | | if (valid) { |
| | | calculateNetValue(); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form }; |
| | | try { |
| | | calculateNetValue(); |
| | | const payload = { ...form }; |
| | | if (isEdit.value) { |
| | | payload.id = currentId.value; |
| | | await updateFixedAsset(payload); |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | await addFixedAsset(payload); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | dialogVisible.value = false; |
| | | await getTableData(); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #originalValue="{ row }"> |
| | |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-form :model="form" :rules="rules" :disabled="isView" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç¼å·" prop="assetCode"> |
| | |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button v-if="!isView" type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | | listIntangibleAssetPage, |
| | | addIntangibleAsset, |
| | | updateIntangibleAsset, |
| | | deleteIntangibleAsset, |
| | | amortizeIntangibleAsset, |
| | | } from "@/api/financialManagement/intangibleAsset"; |
| | | |
| | | defineOptions({ |
| | | name: "æ å½¢èµäº§", |
| | |
| | | const columns = [ |
| | | { label: "èµäº§ç¼å·", prop: "assetCode", width: "130" }, |
| | | { label: "èµäº§åç§°", prop: "assetName", width: "150" }, |
| | | { label: "èµäº§ç±»å«", prop: "category", slot: "category" }, |
| | | { label: "èµäº§ç±»å«", prop: "category", dataType: "slot", slot: "category" }, |
| | | { label: "è¯ä¹¦ç¼å·", prop: "certificateNo", width: "150" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", slot: "originalValue" }, |
| | | { label: "累计æé", prop: "accumulatedAmortization", slot: "accumulatedAmortization" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "180", fixed: "right" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", dataType: "slot", slot: "originalValue" }, |
| | | { label: "累计æé", prop: "accumulatedAmortization", dataType: "slot", slot: "accumulatedAmortization" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", dataType: "slot", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", dataType: "slot", slot: "status" }, |
| | | { label: "æä½", prop: "operation", dataType: "slot", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const multipleList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const isView = ref(false); |
| | | const currentId = ref(null); |
| | | const selectedIds = computed(() => |
| | | multipleList.value |
| | | .map(item => item?.id) |
| | | .filter(id => id !== undefined && id !== null && id !== "") |
| | | ); |
| | | |
| | | const form = reactive({ |
| | | const createDefaultForm = () => ({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | |
| | | const rules = { |
| | | assetName: [{ required: true, message: "请è¾å
¥èµäº§åç§°", trigger: "blur" }], |
| | | category: [{ required: true, message: "è¯·éæ©èµäº§ç±»å«", trigger: "change" }], |
| | |
| | | originalValue: [{ required: true, message: "请è¾å
¥èµäº§åå¼", trigger: "blur" }], |
| | | amortizationPeriod: [{ required: true, message: "请è¾å
¥æéå¹´é", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, assetCode: "WX2024001", assetName: "ERP软件许å¯", category: "software", certificateNo: "SW-2023-001", acquisitionDate: "2023-01-01", originalValue: 50000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 5000, netValue: 45000, validityDate: "2033-01-01", status: "in_use", description: "ä¼ä¸èµæºè®¡å管çç³»ç»", remark: "" }, |
| | | { id: 2, assetCode: "WX2024002", assetName: "åæä¸å©", category: "patent", certificateNo: "ZL202210123456.7", acquisitionDate: "2022-06-15", originalValue: 100000, amortizationPeriod: 20, residualRate: 0, accumulatedAmortization: 3750, netValue: 96250, validityDate: "2042-06-15", status: "in_use", description: "ä¸ç§æ°åç产工èº", remark: "" }, |
| | | { id: 3, assetCode: "WX2024003", assetName: "åæ æ", category: "trademark", certificateNo: "TM-2023-008", acquisitionDate: "2023-03-10", originalValue: 20000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 1500, netValue: 18500, validityDate: "2033-03-10", status: "in_use", description: "å
¬å¸åçåæ ", remark: "" }, |
| | | { id: 4, assetCode: "WX2024004", assetName: "åå°ä½¿ç¨æ", category: "land", certificateNo: "åå½ç¨(2023)第001å·", acquisitionDate: "2023-07-01", originalValue: 500000, amortizationPeriod: 50, residualRate: 0, accumulatedAmortization: 5000, netValue: 495000, validityDate: "2073-07-01", status: "in_use", description: "å·¥ä¸ç¨å°ä½¿ç¨æ", remark: "" }, |
| | | ]; |
| | | |
| | | const totalOriginalValue = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0); |
| | |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { in_use: "å¨ç¨", idle: "é²ç½®", amortized: "å·²æé宿¯" }; |
| | | return map[status] || status; |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { |
| | | in_use: "å¨ç¨", |
| | | idle: "é²ç½®", |
| | | expired: "已尿", |
| | | amortized: "å·²æé宿¯", |
| | | }; |
| | | return map[key] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { in_use: "success", idle: "warning", amortized: "info" }; |
| | | return map[status] || ""; |
| | | const key = String(status || "").toLowerCase(); |
| | | const map = { in_use: "success", idle: "warning", expired: "warning", amortized: "info" }; |
| | | return map[key] || ""; |
| | | }; |
| | | |
| | | const calculateNetValue = () => { |
| | | form.netValue = Number((form.originalValue - form.accumulatedAmortization).toFixed(2)); |
| | | const originalValue = Number(form.originalValue || 0); |
| | | const accumulatedAmortization = Number(form.accumulatedAmortization || 0); |
| | | form.netValue = Number((originalValue - accumulatedAmortization).toFixed(2)); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.assetCode) { |
| | | result = result.filter(item => item.assetCode.includes(filters.assetCode)); |
| | | // èè°çº¦å®ï¼å页忰åºå®ä¸º current/sizeï¼è¿å data.records/data.total |
| | | const getTableData = async () => { |
| | | try { |
| | | const { data } = await listIntangibleAssetPage({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | assetCode: filters.assetCode, |
| | | assetName: filters.assetName, |
| | | category: filters.category, |
| | | status: filters.status, |
| | | }); |
| | | dataList.value = data?.records || []; |
| | | multipleList.value = []; |
| | | pagination.total = Number(data?.total || 0); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | if (filters.assetName) { |
| | | result = result.filter(item => item.assetName.includes(filters.assetName)); |
| | | } |
| | | if (filters.category) { |
| | | result = result.filter(item => item.category === filters.category); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `WX${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "æ°å¢æ å½¢èµäº§"; |
| | | Object.assign(form, { |
| | | assetCode: "WX" + Date.now().toString().slice(-8), |
| | | assetName: "", |
| | | category: "", |
| | | certificateNo: "", |
| | | Object.assign(form, createDefaultForm(), { |
| | | assetCode: buildAssetCode(), |
| | | acquisitionDate: new Date().toISOString().split('T')[0], |
| | | originalValue: 0, |
| | | amortizationPeriod: 10, |
| | | residualRate: 0, |
| | | accumulatedAmortization: 0, |
| | | netValue: 0, |
| | | validityDate: "", |
| | | status: "in_use", |
| | | description: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | isView.value = false; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ å½¢èµäº§"; |
| | | Object.assign(form, row); |
| | | Object.assign(form, createDefaultForm(), row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çèµäº§: ${row.assetName}`); |
| | | edit(row); |
| | | isView.value = true; |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | }).then(async () => { |
| | | // èè°çº¦å®ï¼å 餿¥å£ä½¿ç¨ ids=1&ids=2 |
| | | await deleteIntangibleAsset([row.id]); |
| | | if (dataList.value.length === 1 && pagination.currentPage > 1) { |
| | | pagination.currentPage -= 1; |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleAmortization = () => { |
| | | ElMessageBox.confirm("确认è¿è¡æ¬ææé计æåï¼", "æç¤º", { |
| | | const ids = selectedIds.value; |
| | | const confirmText = ids.length |
| | | ? `确认对éä¸ç ${ids.length} æ¡èµäº§è¿è¡æ¬ææé计æåï¼` |
| | | : "确认è¿è¡æ¬ææé计æåï¼"; |
| | | ElMessageBox.confirm(confirmText, "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | mockData.forEach(item => { |
| | | if (item.status === "in_use") { |
| | | const monthlyAmortization = (item.originalValue * (1 - item.residualRate / 100)) / (item.amortizationPeriod * 12); |
| | | item.accumulatedAmortization = Number((item.accumulatedAmortization + monthlyAmortization).toFixed(2)); |
| | | item.netValue = Number((item.originalValue - item.accumulatedAmortization).toFixed(2)); |
| | | if (item.netValue <= 0) { |
| | | item.status = "amortized"; |
| | | item.netValue = 0; |
| | | } |
| | | } |
| | | }); |
| | | }).then(async () => { |
| | | await amortizeIntangibleAsset({ ids }); |
| | | ElMessage.success("æé计æå®æ"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (isView.value) { |
| | | dialogVisible.value = false; |
| | | return; |
| | | } |
| | | formRef.value.validate(async valid => { |
| | | if (valid) { |
| | | calculateNetValue(); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form }; |
| | | try { |
| | | calculateNetValue(); |
| | | const payload = { ...form }; |
| | | if (isEdit.value) { |
| | | payload.id = currentId.value; |
| | | await updateIntangibleAsset(payload); |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | await addIntangibleAsset(payload); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | dialogVisible.value = false; |
| | | await getTableData(); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage"> |
| | | </PIMTable> |
| | | <el-table ref="tableRef" |
| | | v-loading="loading" |
| | | :data="dataList" |
| | | row-key="id" |
| | | :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" |
| | | height="calc(100vh - 280px)" |
| | | border |
| | | stripe |
| | | highlight-current-row |
| | | class="subject-table"> |
| | | <el-table-column label="ç§ç®ç¼ç " prop="subjectCode" width="140"> |
| | | <template #default="scope"> |
| | | <span class="subject-code">{{ scope.row.subjectCode }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç§ç®åç§°" prop="subjectName" min-width="180"> |
| | | <template #default="scope"> |
| | | <span class="subject-name" :class="{ 'is-parent': scope.row.children?.length > 0 }"> |
| | | {{ scope.row.subjectName }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç§ç®ç±»å" prop="subjectType" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag size="small" :type="getSubjectTypeType(scope.row.subjectType)"> |
| | | {{ scope.row.subjectType }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢æ¹å" prop="balanceDirection" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag size="small" :type="scope.row.balanceDirection === 'åæ¹' ? 'primary' : 'danger'"> |
| | | {{ scope.row.balanceDirection }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¶æ" prop="status" width="80" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag size="small" :type="scope.row.status === 0 || scope.row.status === '0' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 0 || scope.row.status === '0' ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" prop="remark" show-overflow-tooltip min-width="150" /> |
| | | <el-table-column label="æä½" align="center" fixed="right" width="240"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" icon="Plus" @click="addChild(scope.row)">æ°å¢</el-button> |
| | | <el-button link type="primary" icon="Edit" @click="edit(scope.row)">ç¼è¾</el-button> |
| | | <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <FormDialog :title="dialogTitle" |
| | | v-model="dialogVisible" |
| | |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-form-item label="ç¶çº§ç§ç®"> |
| | | <el-input :model-value="parentSubjectLabel" |
| | | disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®ç¼ç " |
| | | prop="subjectCode"> |
| | | <el-input v-model="form.subjectCode" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ref, reactive, onMounted, getCurrentInstance, nextTick } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: "150", |
| | | width: "220", |
| | | operation: [ |
| | | { |
| | | name: "æ°å¢", |
| | | type: "primary", |
| | | clickFun: row => { |
| | | addChild(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "primary", |
| | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const parentSubjectLabel = ref("顶级ç§ç®"); |
| | | const formRef = ref(null); |
| | | const tableRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const loading = ref(false); |
| | | |
| | | const form = reactive({ |
| | | id: undefined, |
| | | parentId: null, |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | loading.value = true; |
| | | const query = { |
| | | pageNum: pagination.currentPage, |
| | | pageSize: pagination.pageSize, |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | ...filters, |
| | | }; |
| | | listAccountSubject(query).then(response => { |
| | | dataList.value = response.data.records; |
| | | pagination.total = response.data.total; |
| | | dataList.value = response.data.records || []; |
| | | loading.value = false; |
| | | }).catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | | |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢ç§ç®"; |
| | | const buildParentSubjectLabel = parentRow => { |
| | | if (!parentRow) { |
| | | return "顶级ç§ç®"; |
| | | } |
| | | const code = parentRow.subjectCode || ""; |
| | | const name = parentRow.subjectName || ""; |
| | | return `${code} ${name}`.trim(); |
| | | }; |
| | | |
| | | const resetForm = ({ parentId = null, parentRow = null } = {}) => { |
| | | Object.assign(form, { |
| | | id: undefined, |
| | | parentId, |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | |
| | | status: 0, |
| | | remark: "", |
| | | }); |
| | | parentSubjectLabel.value = buildParentSubjectLabel(parentRow); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢ç§ç®"; |
| | | resetForm({ parentId: null, parentRow: null }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const addChild = row => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢åç§ç®"; |
| | | resetForm({ parentId: row.id, parentRow: row }); |
| | | form.subjectType = row.subjectType || ""; |
| | | form.balanceDirection = row.balanceDirection || "åæ¹"; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const findSubjectById = (nodes, id) => { |
| | | for (const item of nodes || []) { |
| | | if (item.id === id) { |
| | | return item; |
| | | } |
| | | if (item.children && item.children.length > 0) { |
| | | const found = findSubjectById(item.children, id); |
| | | if (found) { |
| | | return found; |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const edit = row => { |
| | | isEdit.value = true; |
| | | dialogTitle.value = "ç¼è¾ç§ç®"; |
| | | Object.assign(form, row); |
| | | form.parentId = row.parentId ?? null; |
| | | const parentRow = |
| | | row.parentId === null || row.parentId === undefined |
| | | ? null |
| | | : findSubjectById(dataList.value, row.parentId); |
| | | parentSubjectLabel.value = parentRow |
| | | ? buildParentSubjectLabel(parentRow) |
| | | : row.parentId |
| | | ? `ä¸çº§ID: ${row.parentId}` |
| | | : buildParentSubjectLabel(null); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .subject-table { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | |
| | | :deep(.el-table__row) { |
| | | transition: background-color 0.3s; |
| | | } |
| | | |
| | | :deep(.el-table__row:hover) { |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .subject-code { |
| | | color: #606266; |
| | | } |
| | | |
| | | .subject-name { |
| | | font-weight: 500; |
| | | |
| | | &.is-parent { |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <!-- éè´å
¥åº --> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="å
¥åºåå·:"> |
| | | <el-input v-model="filters.inCode" placeholder="请è¾å
¥å
¥åºåå·" clearable style="width: 200px;" /> |
| | | <el-input v-model="filters.inboundBatches" placeholder="请è¾å
¥å
¥åºåå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | <el-input v-model="filters.supplierName" placeholder="请è¾å
¥ä¾åºå" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="å
¥åºæ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" clearable /> |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #amount="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">å é¤</el-button> |
| | | <template #inboundDate="{ row }"> |
| | | {{ row.InboundDate || row.inboundDate || "" }} |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºåå·" prop="inCode"> |
| | | <el-input v-model="form.inCode" placeholder="请è¾å
¥å
¥åºåå·" :disabled="isEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºå" prop="supplierId"> |
| | | <el-select v-model="form.supplierId" placeholder="è¯·éæ©ä¾åºå" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºæ¥æ" prop="inDate"> |
| | | <el-date-picker v-model="form.inDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºéé¢" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å
¥åºæç»" prop="details"> |
| | | <el-table :data="form.details" border style="width: 100%"> |
| | | <el-table-column prop="materialName" label="ç©æåç§°" width="150"> |
| | | <template #default="{ $index }"> |
| | | <el-input v-model="form.details[$index].materialName" placeholder="ç©æåç§°" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="spec" label="è§æ ¼" width="120"> |
| | | <template #default="{ $index }"> |
| | | <el-input v-model="form.details[$index].spec" placeholder="è§æ ¼" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="quantity" label="æ°é" width="100"> |
| | | <template #default="{ $index }"> |
| | | <el-input-number v-model="form.details[$index].quantity" :min="0" style="width: 100%;" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unitPrice" label="åä»·" width="120"> |
| | | <template #default="{ $index }"> |
| | | <el-input-number v-model="form.details[$index].unitPrice" :min="0" :precision="2" style="width: 100%;" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="total" label="éé¢" width="120"> |
| | | <template #default="{ row }"> |
| | | <span>Â¥{{ formatMoney(row.quantity * row.unitPrice) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ $index }"> |
| | | <el-button type="danger" link @click="removeDetail($index)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-button type="primary" link @click="addDetail" style="margin-top: 10px;">+ æ·»å æç»</el-button> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listPageAccountPurchase } from "@/api/financialManagement/accountPurchase"; |
| | | |
| | | defineOptions({ |
| | | name: "éè´å
¥åº", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const filters = reactive({ |
| | | inCode: "", |
| | | supplierId: "", |
| | | inboundBatches: "", |
| | | supplierName: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "å
¥åºåå·", prop: "inCode", width: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "å
¥åºæ¥æ", prop: "inDate", width: "120" }, |
| | | { label: "å
¥åºéé¢", prop: "amount", slot: "amount" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | { label: "å
¥åºåå·", prop: "inboundBatches", minWidth: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", minWidth: "180" }, |
| | | { |
| | | label: "å
¥åºæ¥æ", |
| | | prop: "InboundDate", |
| | | minWidth: "170", |
| | | dataType: "slot", |
| | | slot: "inboundDate", |
| | | }, |
| | | { label: "产ååç§°", prop: "productName", minWidth: "140" }, |
| | | { label: "产åè§æ ¼", prop: "specificationModel", minWidth: "140" }, |
| | | { label: "éè´è®¢åå·", prop: "purchaseContractNumber", minWidth: "150" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | const tableLoading = ref(false); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "åäº¬åææä¾åºå" }, |
| | | { id: 2, name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { id: 3, name: "广å·å
è£
ææå" }, |
| | | { id: 4, name: "æ·±å³äºéé
ä»¶å
¬å¸" }, |
| | | ]; |
| | | function buildFilterParams() { |
| | | const params = { |
| | | inboundBatches: filters.inboundBatches || undefined, |
| | | supplierName: filters.supplierName || undefined, |
| | | }; |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | } |
| | | |
| | | const form = reactive({ |
| | | inCode: "", |
| | | supplierId: "", |
| | | inDate: "", |
| | | amount: 0, |
| | | details: [], |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | inCode: [{ required: true, message: "请è¾å
¥å
¥åºåå·", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], |
| | | inDate: [{ required: true, message: "è¯·éæ©å
¥åºæ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥å
¥åºéé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, inCode: "RK2024001", supplierId: 1, supplierName: "åäº¬åææä¾åºå", inDate: "2024-01-10", amount: 8000, status: "approved", details: [{ materialName: "颿", spec: "Q235", quantity: 10, unitPrice: 500 }], remark: "" }, |
| | | { id: 2, inCode: "RK2024002", supplierId: 2, supplierName: "䏿µ·çµåå
å¨ä»¶å
¬å¸", inDate: "2024-01-12", amount: 12000, status: "pending", details: [{ materialName: "è¯ç", spec: "STM32", quantity: 100, unitPrice: 80 }], remark: "" }, |
| | | { id: 3, inCode: "RK2024003", supplierId: 3, supplierName: "广å·å
è£
ææå", inDate: "2024-01-15", amount: 3500, status: "approved", details: [{ materialName: "纸箱", spec: "50*40*30", quantity: 500, unitPrice: 5 }], remark: "" }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
å®¡æ ¸", approved: "å·²å®¡æ ¸", rejected: "已驳å" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger" }; |
| | | return map[status] || ""; |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.inCode) { |
| | | result = result.filter(item => item.inCode.includes(filters.inCode)); |
| | | } |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.inDate >= filters.dateRange[0] && item.inDate <= filters.dateRange[1]); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | tableLoading.value = true; |
| | | listPageAccountPurchase({ |
| | | ...buildFilterParams(), |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }) |
| | | .then((res) => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | dataList.value = res.data.records ?? []; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | dataList.value = []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.inCode = ""; |
| | | filters.supplierId = ""; |
| | | filters.inboundBatches = ""; |
| | | filters.supplierName = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const addDetail = () => { |
| | | form.details.push({ materialName: "", spec: "", quantity: 0, unitPrice: 0 }); |
| | | }; |
| | | |
| | | const removeDetail = (index) => { |
| | | form.details.splice(index, 1); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢å
¥åº"; |
| | | Object.assign(form, { |
| | | inCode: "RK" + Date.now().toString().slice(-8), |
| | | supplierId: "", |
| | | inDate: new Date().toISOString().split('T')[0], |
| | | amount: 0, |
| | | details: [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }], |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾å
¥åº"; |
| | | Object.assign(form, row); |
| | | if (!form.details || form.details.length === 0) { |
| | | form.details = [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }]; |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çå
¥åºå: ${row.inCode}`); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥å
¥åºååï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const supplier = supplierList.find(item => item.id === form.supplierId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | proxy.download( |
| | | "/accountPurchase/exportAccountPurchaseInbound", |
| | | buildFilterParams(), |
| | | `éè´å
¥åº_${new Date().getTime()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <!-- éè´éè´§ --> |
| | | |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="éè´§åå·:"> |
| | | <el-input |
| | | v-model="filters.returnNo" |
| | | placeholder="请è¾å
¥éè´§åå·" |
| | | clearable |
| | | style="width: 200px" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-input |
| | | v-model="filters.supplierName" |
| | | placeholder="请è¾å
¥ä¾åºå" |
| | | clearable |
| | | style="width: 200px" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="éè´§æ¥æ:"> |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | |
| | | <div> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | |
| | | size: pagination.pageSize, |
| | | |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | import { listPageAccountPurchaseReturn } from "@/api/financialManagement/accountPurchase"; |
| | | |
| | | defineOptions({ |
| | | name: "éè´éè´§", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const filters = reactive({ |
| | | returnNo: "", |
| | | |
| | | supplierName: "", |
| | | |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | |
| | | pageSize: 10, |
| | | |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "éè´§åå·", prop: "returnNo", minWidth: "150" }, |
| | | |
| | | { label: "ä¾åºå", prop: "supplierName", minWidth: "180" }, |
| | | |
| | | { label: "å
³èå
¥åºåå·", prop: "inboundBatches", minWidth: "150" }, |
| | | |
| | | { label: "éè´§æ¥æ", prop: "preparedAt", minWidth: "170" }, |
| | | |
| | | { |
| | | label: "鿬¾æ»é¢", |
| | | |
| | | prop: "totalAmount", |
| | | |
| | | minWidth: "150", |
| | | |
| | | align: "right", |
| | | |
| | | formatData: (val) => |
| | | val === null || val === undefined || val === "" |
| | | ? "" |
| | | : Number(val).toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }), |
| | | }, |
| | | |
| | | { label: "éè´§æ¹å¼", prop: "returnType", minWidth: "150" }, |
| | | |
| | | { label: "éè´è®¢åå·", prop: "purchaseContractNumber", minWidth: "150" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | |
| | | const tableLoading = ref(false); |
| | | |
| | | function buildFilterParams() { |
| | | const params = { |
| | | returnNo: filters.returnNo || undefined, |
| | | |
| | | supplierName: filters.supplierName || undefined, |
| | | }; |
| | | |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | |
| | | return params; |
| | | } |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | tableLoading.value = true; |
| | | |
| | | listPageAccountPurchaseReturn({ |
| | | ...buildFilterParams(), |
| | | |
| | | current: pagination.currentPage, |
| | | |
| | | size: pagination.pageSize, |
| | | }) |
| | | .then((res) => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | |
| | | dataList.value = res.data.records ?? []; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | |
| | | dataList.value = []; |
| | | } |
| | | }) |
| | | |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | }) |
| | | |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.returnNo = ""; |
| | | |
| | | filters.supplierName = ""; |
| | | |
| | | filters.dateRange = []; |
| | | |
| | | pagination.currentPage = 1; |
| | | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | |
| | | pagination.pageSize = limit; |
| | | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | proxy.download( |
| | | "/accountPurchase/exportAccountPurchaseReturn", |
| | | |
| | | buildFilterParams(), |
| | | |
| | | `éè´éè´§_${new Date().getTime()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | |
| | | justify-content: space-between; |
| | | |
| | | margin-bottom: 15px; |
| | | } |
| | | </style> |
| | | |
| | |
| | | <template> |
| | | <!-- éå®åºåº --> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="åºåºåå·:"> |
| | | <el-input v-model="filters.outCode" placeholder="请è¾å
¥åºåºåå·" clearable style="width: 200px;" /> |
| | | <el-input v-model="filters.outboundBatches" placeholder="请è¾å
¥åºåºåå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | <el-form-item label="客æ·åç§°:"> |
| | | <el-input v-model="filters.customerName" placeholder="请è¾å
¥å®¢æ·åç§°" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="åºåºæ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" clearable /> |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #status="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | /> |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºåå·" prop="outCode"> |
| | | <el-input v-model="form.outCode" placeholder="请è¾å
¥åºåºåå·" :disabled="isEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºæ¥æ" prop="outDate"> |
| | | <el-date-picker v-model="form.outDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éé¢" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listPageAccountSales } from "@/api/financialManagement/accountSales"; |
| | | |
| | | defineOptions({ |
| | | name: "éå®åºåº", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const filters = reactive({ |
| | | outCode: "", |
| | | customerId: "", |
| | | outboundBatches: "", |
| | | customerName: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "åºåºåå·", prop: "outCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "åºåºæ¥æ", prop: "outDate", width: "120" }, |
| | | { label: "éé¢", prop: "amount", width: "120" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | { label: "åºåºåå·", prop: "outboundBatches", minWidth: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", minWidth: "180" }, |
| | | { label: "åºåºæ¥æ", prop: "shippingDate", width: "170" }, |
| | | { label: "产ååç§°", prop: "productName", minWidth: "140" }, |
| | | { label: "产åè§æ ¼", prop: "specificationModel", minWidth: "140" }, |
| | | { |
| | | label: "éé¢", |
| | | prop: "outboundAmount", |
| | | minWidth: "120", |
| | | align: "right", |
| | | formatData: (val) => (val === null || val === undefined || val === "" ? "" : Number(val).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 })), |
| | | }, |
| | | { label: "åè´§ç¼å·", prop: "shippingNo", minWidth: "140" }, |
| | | { label: "éå®è®¢åå·", prop: "salesContractNo", minWidth: "150" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | const tableLoading = ref(false); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | function buildFilterParams() { |
| | | const params = { |
| | | outboundBatches: filters.outboundBatches || undefined, |
| | | customerName: filters.customerName || undefined, |
| | | }; |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | } |
| | | |
| | | const form = reactive({ |
| | | outCode: "", |
| | | customerId: "", |
| | | outDate: "", |
| | | amount: 0, |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | outCode: [{ required: true, message: "请è¾å
¥åºåºåå·", trigger: "blur" }], |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | outDate: [{ required: true, message: "è¯·éæ©åºåºæ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, outCode: "CK2024001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", outDate: "2024-01-15", amount: 5000, status: "approved", remark: "" }, |
| | | { id: 2, outCode: "CK2024002", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", outDate: "2024-01-16", amount: 8000, status: "pending", remark: "" }, |
| | | { id: 3, outCode: "CK2024003", customerId: 3, customerName: "广å·å®ä¸æéå
¬å¸", outDate: "2024-01-18", amount: 12000, status: "approved", remark: "" }, |
| | | { id: 4, outCode: "CK2024004", customerId: 4, customerName: "æ·±å³çµåå
¬å¸", outDate: "2024-01-20", amount: 3500, status: "pending", remark: "" }, |
| | | ]; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
å®¡æ ¸", approved: "å·²å®¡æ ¸", rejected: "已驳å" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger" }; |
| | | return map[status] || ""; |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.outCode) { |
| | | result = result.filter(item => item.outCode.includes(filters.outCode)); |
| | | } |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.outDate >= filters.dateRange[0] && item.outDate <= filters.dateRange[1]); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | tableLoading.value = true; |
| | | listPageAccountSales({ |
| | | ...buildFilterParams(), |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }) |
| | | .then((res) => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | dataList.value = res.data.records ?? []; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | dataList.value = []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.outCode = ""; |
| | | filters.customerId = ""; |
| | | filters.outboundBatches = ""; |
| | | filters.customerName = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢åºåº"; |
| | | Object.assign(form, { |
| | | outCode: "CK" + Date.now(), |
| | | customerId: "", |
| | | outDate: "", |
| | | amount: 0, |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åºåº"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çåºåºå: ${row.outCode}`); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const customer = customerList.find(item => item.id === form.customerId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, customerName: customer?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥åºåºååï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | proxy.download( |
| | | "/accountSales/exportAccountSalesOutbound", |
| | | buildFilterParams(), |
| | | `éå®åºåº_${new Date().getTime()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | <template> |
| | | <!-- éå®éè´§ --> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="éè´§åå·:"> |
| | | <el-input v-model="filters.returnCode" placeholder="请è¾å
¥éè´§åå·" clearable style="width: 200px;" /> |
| | | <el-input v-model="filters.returnNo" placeholder="请è¾å
¥éè´§åå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | <el-form-item label="客æ·åç§°:"> |
| | | <el-input v-model="filters.customerName" placeholder="请è¾å
¥å®¢æ·åç§°" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="éè´§æ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" clearable /> |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #status="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">å®¡æ ¸</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | /> |
| | | </div> |
| | | |
| | | <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§åå·" prop="returnCode"> |
| | | <el-input v-model="form.returnCode" placeholder="请è¾å
¥éè´§åå·" :disabled="isEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
³èåºåºå" prop="outCode"> |
| | | <el-select v-model="form.outCode" placeholder="è¯·éæ©åºåºå" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§æ¥æ" prop="returnDate"> |
| | | <el-date-picker v-model="form.returnDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§éé¢" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§åå " prop="reason"> |
| | | <el-input v-model="form.reason" placeholder="请è¾å
¥éè´§åå " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listPageAccountSalesReturn } from "@/api/financialManagement/accountSales"; |
| | | |
| | | defineOptions({ |
| | | name: "éå®éè´§", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const filters = reactive({ |
| | | returnCode: "", |
| | | customerId: "", |
| | | returnNo: "", |
| | | customerName: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "éè´§åå·", prop: "returnCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "å
³èåºåºå", prop: "outCode", width: "150" }, |
| | | { label: "éè´§æ¥æ", prop: "returnDate", width: "120" }, |
| | | { label: "éè´§éé¢", prop: "amount", width: "120" }, |
| | | { label: "éè´§åå ", prop: "reason", width: "150", showOverflowTooltip: true }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | { label: "éè´§åå·", prop: "returnNo", minWidth: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", minWidth: "180" }, |
| | | { label: "å
³èåè´§åå·", prop: "shippingNo", minWidth: "150" }, |
| | | { label: "éè´§æ¥æ", prop: "makeTime", minWidth: "170" }, |
| | | { |
| | | label: "鿬¾æ»é¢", |
| | | prop: "refundAmount", |
| | | minWidth: "120", |
| | | align: "right", |
| | | formatData: (val) => |
| | | val === null || val === undefined || val === "" |
| | | ? "" |
| | | : Number(val).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }), |
| | | }, |
| | | { label: "éè´§åå ", prop: "returnReason", minWidth: "150", showOverflowTooltip: true }, |
| | | { label: "éå®è®¢åå·", prop: "salesContractNo", minWidth: "150" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | const tableLoading = ref(false); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | function buildFilterParams() { |
| | | const params = { |
| | | returnNo: filters.returnNo || undefined, |
| | | customerName: filters.customerName || undefined, |
| | | }; |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | } |
| | | |
| | | const outList = [ |
| | | { outCode: "CK2024001", customerId: 1 }, |
| | | { outCode: "CK2024002", customerId: 2 }, |
| | | { outCode: "CK2024003", customerId: 3 }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | returnCode: "", |
| | | outCode: "", |
| | | customerId: "", |
| | | returnDate: "", |
| | | amount: 0, |
| | | reason: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | returnCode: [{ required: true, message: "请è¾å
¥éè´§åå·", trigger: "blur" }], |
| | | outCode: [{ required: true, message: "è¯·éæ©å
³èåºåºå", trigger: "change" }], |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | returnDate: [{ required: true, message: "è¯·éæ©éè´§æ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éè´§éé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, returnCode: "TH2024001", outCode: "CK2024001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", returnDate: "2024-01-20", amount: 1000, reason: "è´¨éé®é¢", status: "approved", remark: "" }, |
| | | { id: 2, returnCode: "TH2024002", outCode: "CK2024002", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", returnDate: "2024-01-22", amount: 500, reason: "è§æ ¼ä¸ç¬¦", status: "pending", remark: "" }, |
| | | ]; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
å®¡æ ¸", approved: "å·²å®¡æ ¸", rejected: "已驳å" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger" }; |
| | | return map[status] || ""; |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.returnCode) { |
| | | result = result.filter(item => item.returnCode.includes(filters.returnCode)); |
| | | } |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.returnDate >= filters.dateRange[0] && item.returnDate <= filters.dateRange[1]); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | tableLoading.value = true; |
| | | listPageAccountSalesReturn({ |
| | | ...buildFilterParams(), |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }) |
| | | .then((res) => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | dataList.value = res.data.records ?? []; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | dataList.value = []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.returnCode = ""; |
| | | filters.customerId = ""; |
| | | filters.returnNo = ""; |
| | | filters.customerName = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢éè´§"; |
| | | Object.assign(form, { |
| | | returnCode: "TH" + Date.now(), |
| | | outCode: "", |
| | | customerId: "", |
| | | returnDate: "", |
| | | amount: 0, |
| | | reason: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾éè´§"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çéè´§å: ${row.returnCode}`); |
| | | }; |
| | | |
| | | const handleAudit = (row) => { |
| | | ElMessageBox.confirm("ç¡®è®¤å®¡æ ¸éè¿è¯¥éè´§ååï¼", "æç¤º", { |
| | | confirmButtonText: "éè¿", |
| | | cancelButtonText: "驳å", |
| | | distinguishCancelAndClose: true, |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "approved"; |
| | | } |
| | | ElMessage.success("å®¡æ ¸éè¿"); |
| | | getTableData(); |
| | | }).catch((action) => { |
| | | if (action === "cancel") { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "rejected"; |
| | | } |
| | | ElMessage.warning("已驳å"); |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const customer = customerList.find(item => item.id === form.customerId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, customerName: customer?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | proxy.download( |
| | | "/accountSales/exportAccountSalesReturn", |
| | | buildFilterParams(), |
| | | `éå®éè´§_${new Date().getTime()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ä¼è®¡ç§ç®:"> |
| | | <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="è¯·éæ©ä¼è®¡ç§ç®" clearable style="width: 250px;" filterable /> |
| | | </el-form-item> |
| | | <el-form-item label="è¾
婿 ¸ç®:"> |
| | | <el-select v-model="filters.auxiliary" placeholder="è¯·éæ©è¾
婿 ¸ç®" clearable style="width: 180px;"> |
| | | <el-option label="客æ·" value="customer" /> |
| | | <el-option label="ä¾åºå" value="supplier" /> |
| | | <el-option label="é¨é¨" value="department" /> |
| | | <el-option label="åå·¥" value="employee" /> |
| | | <el-option label="项ç®" value="project" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ ¸ç®å¯¹è±¡:"> |
| | | <el-select v-model="filters.auxiliaryItem" placeholder="è¯·éæ©æ ¸ç®å¯¹è±¡" clearable style="width: 200px;" :disabled="!filters.auxiliary"> |
| | | <el-option v-for="item in auxiliaryItems" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æ¥è¯¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | <el-button @click="handlePrint" icon="Printer">æå°</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="app-container ledger-page"> |
| | | <div class="ledger-layout"> |
| | | <aside class="subject-panel"> |
| | | <el-input v-model="subjectKeyword" placeholder="请è¾å
¥ç§ç®åç§°/ç¼å·" clearable prefix-icon="Search" /> |
| | | <el-scrollbar class="subject-tree-scroll"> |
| | | <el-tree |
| | | ref="subjectTreeRef" |
| | | :data="subjectOptions" |
| | | node-key="code" |
| | | :props="{ label: 'name', children: 'children' }" |
| | | highlight-current |
| | | default-expand-all |
| | | :expand-on-click-node="false" |
| | | :filter-node-method="filterSubjectNode" |
| | | @node-click="handleSubjectClick" |
| | | > |
| | | <template #default="{ data }"> |
| | | <span class="subject-node">{{ data.code }} {{ data.name }}</span> |
| | | </template> |
| | | </el-tree> |
| | | </el-scrollbar> |
| | | </aside> |
| | | |
| | | <div class="ledger-header" v-if="currentSubject"> |
| | | <h2>ç§ç®æç»è´¦</h2> |
| | | <p>ç§ç®: {{ currentSubject.code }} {{ currentSubject.name }}</p> |
| | | <p v-if="filters.auxiliary && filters.auxiliaryItem">è¾
婿 ¸ç®: {{ getAuxiliaryLabel() }}</p> |
| | | <p>æé´: {{ filters.startMonth }} è³ {{ filters.endMonth }}</p> |
| | | <section class="ledger-content"> |
| | | <el-form :model="filters" :inline="true" class="filter-form"> |
| | | <el-form-item label="æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æ¥è¯¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | <el-button @click="handlePrint" icon="Printer">æå°</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="table_list"> |
| | | <el-table :data="dataList" border style="width: 100%"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="voucherNo" label="åè¯åå·" width="120" /> |
| | | <el-table-column prop="summary" label="æè¦" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="debit" label="åæ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="credit" label="è´·æ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¹å" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.direction === 'å' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢" width="150"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-empty v-if="!currentSubject" description="è¯·éæ©ä¼è®¡ç§ç®æ¥è¯¢" style="margin-top: 50px;" /> |
| | | </section> |
| | | </div> |
| | | |
| | | <div class="table_list"> |
| | | <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="voucherNo" label="åè¯åå·" width="120" /> |
| | | <el-table-column prop="summary" label="æè¦" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column label="åæ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è´·æ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¹å" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.direction === 'å' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢" width="150"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-empty v-if="!currentSubject" description="è¯·éæ©ä¼è®¡ç§ç®æ¥è¯¢" style="margin-top: 50px;" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed, watch } from "vue"; |
| | | import { ref, reactive, onMounted, computed, watch, nextTick } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listAccountSubject } from "@/api/financialManagement/accountSubject"; |
| | | import { getDetailLedger } from "@/api/financialManagement/ledger"; |
| | | |
| | | defineOptions({ |
| | | name: "ç§ç®æç»è´¦", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | subject: [], |
| | | auxiliary: "", |
| | | auxiliaryItem: "", |
| | | startMonth: "2024-01", |
| | | endMonth: "2024-03", |
| | | subject: "", |
| | | startMonth: "", |
| | | endMonth: "", |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const subjectOptions = ref([]); |
| | | const subjectKeyword = ref(""); |
| | | const subjectTreeRef = ref(); |
| | | |
| | | const subjectOptions = [ |
| | | { |
| | | code: "1122", |
| | | name: "åºæ¶è´¦æ¬¾", |
| | | children: [ |
| | | { code: "112201", name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { code: "112202", name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { code: "112203", name: "广å·å®ä¸æéå
¬å¸" }, |
| | | ], |
| | | }, |
| | | { |
| | | code: "2202", |
| | | name: "åºä»è´¦æ¬¾", |
| | | children: [ |
| | | { code: "220201", name: "åäº¬åææä¾åºå" }, |
| | | { code: "220202", name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { code: "220203", name: "广å·å
è£
ææå" }, |
| | | ], |
| | | }, |
| | | { |
| | | code: "6602", |
| | | name: "管çè´¹ç¨", |
| | | children: [ |
| | | { code: "660201", name: "åå
¬è´¹" }, |
| | | { code: "660202", name: "å·®æ
è´¹" }, |
| | | { code: "660203", name: "ä¸å¡æå¾
è´¹" }, |
| | | ], |
| | | }, |
| | | const getPreviousMonth = () => { |
| | | const date = new Date(); |
| | | date.setDate(1); |
| | | date.setMonth(date.getMonth() - 1); |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | return `${year}-${month}`; |
| | | }; |
| | | |
| | | const defaultMonth = getPreviousMonth(); |
| | | filters.startMonth = defaultMonth; |
| | | filters.endMonth = defaultMonth; |
| | | |
| | | const fallbackSubjects = [ |
| | | { code: "1122", name: "åºæ¶è´¦æ¬¾" }, |
| | | { code: "2202", name: "åºä»è´¦æ¬¾" }, |
| | | { code: "6602", name: "管çè´¹ç¨" }, |
| | | ]; |
| | | |
| | | const auxiliaryItems = computed(() => { |
| | | const map = { |
| | | customer: [ |
| | | { value: "1", label: "åäº¬ç§ææéå
¬å¸" }, |
| | | { value: "2", label: "䏿µ·è´¸æå
¬å¸" }, |
| | | { value: "3", label: "广å·å®ä¸æéå
¬å¸" }, |
| | | ], |
| | | supplier: [ |
| | | { value: "1", label: "åäº¬åææä¾åºå" }, |
| | | { value: "2", label: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { value: "3", label: "广å·å
è£
ææå" }, |
| | | ], |
| | | department: [ |
| | | { value: "1", label: "è´¢å¡é¨" }, |
| | | { value: "2", label: "éå®é¨" }, |
| | | { value: "3", label: "éè´é¨" }, |
| | | ], |
| | | employee: [ |
| | | { value: "1", label: "å¼ ä¸" }, |
| | | { value: "2", label: "æå" }, |
| | | { value: "3", label: "çäº" }, |
| | | ], |
| | | project: [ |
| | | { value: "1", label: "项ç®A" }, |
| | | { value: "2", label: "项ç®B" }, |
| | | { value: "3", label: "项ç®C" }, |
| | | ], |
| | | }; |
| | | return map[filters.auxiliary] || []; |
| | | }); |
| | | |
| | | watch(() => filters.auxiliary, () => { |
| | | filters.auxiliaryItem = ""; |
| | | }); |
| | | |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject || filters.subject.length === 0) return null; |
| | | const code = filters.subject[filters.subject.length - 1]; |
| | | return findSubject(subjectOptions, code); |
| | | }); |
| | | const toTree = (nodes = []) => |
| | | nodes |
| | | .filter(item => item.subjectCode && item.subjectName) |
| | | .map(item => ({ |
| | | code: item.subjectCode, |
| | | name: item.subjectName, |
| | | children: toTree(item.children || []), |
| | | })); |
| | | |
| | | const findSubject = (options, code) => { |
| | | for (const item of options) { |
| | |
| | | return null; |
| | | }; |
| | | |
| | | const getAuxiliaryLabel = () => { |
| | | const item = auxiliaryItems.value.find(i => i.value === filters.auxiliaryItem); |
| | | return item ? item.label : ""; |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject) return null; |
| | | return findSubject(subjectOptions.value, filters.subject); |
| | | }); |
| | | |
| | | const getFirstSubjectCode = (nodes = []) => { |
| | | for (const item of nodes) { |
| | | if (item.code) return item.code; |
| | | if (item.children && item.children.length > 0) { |
| | | const childCode = getFirstSubjectCode(item.children); |
| | | if (childCode) return childCode; |
| | | } |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | const setDefaultSubjectSelection = async () => { |
| | | const firstCode = getFirstSubjectCode(subjectOptions.value); |
| | | if (!firstCode) { |
| | | filters.subject = ""; |
| | | subjectTreeRef.value?.setCurrentKey(null); |
| | | return; |
| | | } |
| | | filters.subject = firstCode; |
| | | await nextTick(); |
| | | subjectTreeRef.value?.setCurrentKey(firstCode); |
| | | }; |
| | | |
| | | const filterSubjectNode = (value, data) => { |
| | | const keyword = value?.trim(); |
| | | if (!keyword) return true; |
| | | return `${data.code}${data.name}`.includes(keyword); |
| | | }; |
| | | |
| | | watch(subjectKeyword, (value) => { |
| | | subjectTreeRef.value?.filter(value || ""); |
| | | }); |
| | | |
| | | const handleSubjectClick = async (data) => { |
| | | filters.subject = data.code; |
| | | await getTableData(); |
| | | }; |
| | | |
| | | const loadSubjectOptions = async () => { |
| | | let options = []; |
| | | try { |
| | | const { data } = await listAccountSubject({ |
| | | current: 1, |
| | | size: 1000, |
| | | }); |
| | | options = toTree(data?.records || []); |
| | | } catch (error) { |
| | | // å
¨å±æ¦æªå¨å·²æç¤ºï¼ä¸é¢èµ°å
åºç§ç® |
| | | } |
| | | if (options.length === 0) { |
| | | options = fallbackSubjects.map(item => ({ ...item, children: [] })); |
| | | } |
| | | subjectOptions.value = options; |
| | | await setDefaultSubjectSelection(); |
| | | if (filters.subject) { |
| | | await getTableData(); |
| | | } |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { date: "2024-01-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 10000 }, |
| | | { date: "2024-01-05", voucherNo: "è®°-0001", summary: "éå®åºåº", debit: 5000, credit: 0, direction: "å", balance: 15000 }, |
| | | { date: "2024-01-10", voucherNo: "è®°-0002", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 3000, direction: "å", balance: 12000 }, |
| | | { date: "2024-01-15", voucherNo: "è®°-0003", summary: "éå®åºåº", debit: 8000, credit: 0, direction: "å", balance: 20000 }, |
| | | { date: "2024-01-20", voucherNo: "è®°-0004", summary: "éå®éè´§", debit: 0, credit: 2000, direction: "å", balance: 18000 }, |
| | | { date: "2024-01-25", voucherNo: "è®°-0005", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 5000, direction: "å", balance: 13000 }, |
| | | { date: "2024-01-31", voucherNo: "-", summary: "æ¬æå计", debit: 13000, credit: 10000, direction: "å", balance: 13000 }, |
| | | { date: "2024-02-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 13000 }, |
| | | { date: "2024-02-10", voucherNo: "è®°-0006", summary: "éå®åºåº", debit: 6000, credit: 0, direction: "å", balance: 19000 }, |
| | | { date: "2024-02-15", voucherNo: "è®°-0007", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 4000, direction: "å", balance: 15000 }, |
| | | { date: "2024-02-28", voucherNo: "-", summary: "æ¬æå计", debit: 6000, credit: 4000, direction: "å", balance: 15000 }, |
| | | { date: "2024-03-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 15000 }, |
| | | { date: "2024-03-05", voucherNo: "è®°-0008", summary: "éå®åºåº", debit: 7000, credit: 0, direction: "å", balance: 22000 }, |
| | | { date: "2024-03-10", voucherNo: "è®°-0009", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 6000, direction: "å", balance: 16000 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬æå计", debit: 7000, credit: 6000, direction: "å", balance: 16000 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬å¹´ç´¯è®¡", debit: 26000, credit: 20000, direction: "å", balance: 16000 }, |
| | | ]; |
| | | |
| | | const getTableData = () => { |
| | | // èè°çº¦å®ï¼æç»è´¦æç§ç®ä¸æé´è¿æ»¤ |
| | | const getTableData = async () => { |
| | | if (!currentSubject.value) { |
| | | dataList.value = []; |
| | | return; |
| | | } |
| | | dataList.value = [...mockData]; |
| | | try { |
| | | const { data } = await getDetailLedger({ |
| | | subjectCode: currentSubject.value.code, |
| | | startMonth: filters.startMonth, |
| | | endMonth: filters.endMonth, |
| | | }); |
| | | dataList.value = Array.isArray(data) ? data : data?.records || []; |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.subject = []; |
| | | filters.auxiliary = ""; |
| | | filters.auxiliaryItem = ""; |
| | | filters.startMonth = "2024-01"; |
| | | filters.endMonth = "2024-03"; |
| | | const resetFilters = async () => { |
| | | filters.startMonth = defaultMonth; |
| | | filters.endMonth = defaultMonth; |
| | | dataList.value = []; |
| | | }; |
| | | |
| | | const getSummaries = (param) => { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | if (column.property === "debit") { |
| | | const values = data.map(item => Number(item.debit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else if (column.property === "credit") { |
| | | const values = data.map(item => Number(item.credit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | subjectKeyword.value = ""; |
| | | subjectTreeRef.value?.filter(""); |
| | | await setDefaultSubjectSelection(); |
| | | if (filters.subject) { |
| | | await getTableData(); |
| | | } |
| | | }; |
| | | |
| | | const handlePrint = () => { |
| | |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // é»è®¤ä¸å è½½æ°æ®ï¼éè¦éæ©ç§ç® |
| | | onMounted(async () => { |
| | | await loadSubjectOptions(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .ledger-header { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | h2 { |
| | | margin: 0 0 10px 0; |
| | | } |
| | | p { |
| | | color: #606266; |
| | | margin: 5px 0; |
| | | } |
| | | .ledger-layout { |
| | | display: flex; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .subject-panel { |
| | | width: 260px; |
| | | flex-shrink: 0; |
| | | padding: 12px; |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 8px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .subject-tree-scroll { |
| | | height: 600px; |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | .subject-node { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .ledger-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .filter-form { |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .text-primary { |
| | |
| | | color: #e6a23c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .subject-panel :deep(.el-tree-node__content) { |
| | | height: 34px; |
| | | } |
| | | |
| | | .subject-panel :deep(.el-tree-node.is-current > .el-tree-node__content) { |
| | | background-color: #f0f7ff; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ä¼è®¡ç§ç®:"> |
| | | <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="è¯·éæ©ä¼è®¡ç§ç®" clearable style="width: 250px;" filterable /> |
| | | </el-form-item> |
| | | <el-form-item label="æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æ¥è¯¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | <el-button @click="handlePrint" icon="Printer">æå°</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="app-container ledger-page"> |
| | | <div class="ledger-layout"> |
| | | <aside class="subject-panel"> |
| | | <el-input v-model="subjectKeyword" placeholder="请è¾å
¥ç§ç®åç§°/ç¼å·" clearable prefix-icon="Search" /> |
| | | <el-scrollbar class="subject-tree-scroll"> |
| | | <el-tree |
| | | ref="subjectTreeRef" |
| | | :data="subjectOptions" |
| | | node-key="code" |
| | | :props="{ label: 'name', children: 'children' }" |
| | | highlight-current |
| | | default-expand-all |
| | | :expand-on-click-node="false" |
| | | :filter-node-method="filterSubjectNode" |
| | | @node-click="handleSubjectClick" |
| | | > |
| | | <template #default="{ data }"> |
| | | <span class="subject-node">{{ data.code }} {{ data.name }}</span> |
| | | </template> |
| | | </el-tree> |
| | | </el-scrollbar> |
| | | </aside> |
| | | |
| | | <div class="ledger-header" v-if="currentSubject"> |
| | | <h2>ç§ç®æ»è´¦</h2> |
| | | <p>ç§ç®: {{ currentSubject.code }} {{ currentSubject.name }}</p> |
| | | <p>æé´: {{ filters.startMonth }} è³ {{ filters.endMonth }}</p> |
| | | <section class="ledger-content"> |
| | | <el-form :model="filters" :inline="true" class="filter-form"> |
| | | <el-form-item label="æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æ¥è¯¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | <el-button @click="handlePrint" icon="Printer">æå°</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="table_list"> |
| | | <el-table :data="dataList" border style="width: 100%"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="voucherNo" label="åè¯åå·" width="120" /> |
| | | <el-table-column prop="summary" label="æè¦" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="debit" label="åæ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="credit" label="è´·æ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¹å" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.direction === 'å' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢" width="150"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-empty v-if="!currentSubject" description="è¯·éæ©ä¼è®¡ç§ç®æ¥è¯¢" style="margin-top: 50px;" /> |
| | | </section> |
| | | </div> |
| | | |
| | | <div class="table_list"> |
| | | <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="voucherNo" label="åè¯åå·" width="120" /> |
| | | <el-table-column prop="summary" label="æè¦" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column label="åæ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è´·æ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¹å" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.direction === 'å' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢" width="150"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-empty v-if="!currentSubject" description="è¯·éæ©ä¼è®¡ç§ç®æ¥è¯¢" style="margin-top: 50px;" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ref, reactive, onMounted, computed, watch, nextTick } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listAccountSubject } from "@/api/financialManagement/accountSubject"; |
| | | import { getGeneralLedger } from "@/api/financialManagement/ledger"; |
| | | |
| | | defineOptions({ |
| | | name: "ç§ç®æ»è´¦", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | subject: [], |
| | | startMonth: "2024-01", |
| | | endMonth: "2024-03", |
| | | subject: "", |
| | | startMonth: "", |
| | | endMonth: "", |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const subjectOptions = ref([]); |
| | | const subjectKeyword = ref(""); |
| | | const subjectTreeRef = ref(); |
| | | |
| | | const subjectOptions = [ |
| | | { |
| | | code: "1001", |
| | | name: "åºåç°é", |
| | | children: [], |
| | | }, |
| | | { |
| | | code: "1002", |
| | | name: "é¶è¡å款", |
| | | children: [ |
| | | { code: "100201", name: "å·¥åé¶è¡" }, |
| | | { code: "100202", name: "建设é¶è¡" }, |
| | | ], |
| | | }, |
| | | { |
| | | code: "1122", |
| | | name: "åºæ¶è´¦æ¬¾", |
| | | children: [], |
| | | }, |
| | | { |
| | | code: "2202", |
| | | name: "åºä»è´¦æ¬¾", |
| | | children: [], |
| | | }, |
| | | { |
| | | code: "6001", |
| | | name: "主è¥ä¸å¡æ¶å
¥", |
| | | children: [], |
| | | }, |
| | | const getPreviousMonth = () => { |
| | | const date = new Date(); |
| | | date.setDate(1); |
| | | date.setMonth(date.getMonth() - 1); |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | return `${year}-${month}`; |
| | | }; |
| | | |
| | | const defaultMonth = getPreviousMonth(); |
| | | filters.startMonth = defaultMonth; |
| | | filters.endMonth = defaultMonth; |
| | | |
| | | const fallbackSubjects = [ |
| | | { code: "1001", name: "åºåç°é" }, |
| | | { code: "1002", name: "é¶è¡å款" }, |
| | | { code: "1122", name: "åºæ¶è´¦æ¬¾" }, |
| | | { code: "2202", name: "åºä»è´¦æ¬¾" }, |
| | | { code: "6001", name: "主è¥ä¸å¡æ¶å
¥" }, |
| | | ]; |
| | | |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject || filters.subject.length === 0) return null; |
| | | const code = filters.subject[filters.subject.length - 1]; |
| | | return findSubject(subjectOptions, code); |
| | | }); |
| | | const toTree = (nodes = []) => |
| | | nodes |
| | | .filter(item => item.subjectCode && item.subjectName) |
| | | .map(item => ({ |
| | | code: item.subjectCode, |
| | | name: item.subjectName, |
| | | children: toTree(item.children || []), |
| | | })); |
| | | |
| | | const findSubject = (options, code) => { |
| | | for (const item of options) { |
| | |
| | | return null; |
| | | }; |
| | | |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject) return null; |
| | | return findSubject(subjectOptions.value, filters.subject); |
| | | }); |
| | | |
| | | const getFirstSubjectCode = (nodes = []) => { |
| | | for (const item of nodes) { |
| | | if (item.code) return item.code; |
| | | if (item.children && item.children.length > 0) { |
| | | const childCode = getFirstSubjectCode(item.children); |
| | | if (childCode) return childCode; |
| | | } |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | const setDefaultSubjectSelection = async () => { |
| | | const firstCode = getFirstSubjectCode(subjectOptions.value); |
| | | if (!firstCode) { |
| | | filters.subject = ""; |
| | | subjectTreeRef.value?.setCurrentKey(null); |
| | | return; |
| | | } |
| | | filters.subject = firstCode; |
| | | await nextTick(); |
| | | subjectTreeRef.value?.setCurrentKey(firstCode); |
| | | }; |
| | | |
| | | const filterSubjectNode = (value, data) => { |
| | | const keyword = value?.trim(); |
| | | if (!keyword) return true; |
| | | return `${data.code}${data.name}`.includes(keyword); |
| | | }; |
| | | |
| | | watch(subjectKeyword, (value) => { |
| | | subjectTreeRef.value?.filter(value || ""); |
| | | }); |
| | | |
| | | const handleSubjectClick = async (data) => { |
| | | filters.subject = data.code; |
| | | await getTableData(); |
| | | }; |
| | | |
| | | const loadSubjectOptions = async () => { |
| | | let options = []; |
| | | try { |
| | | const { data } = await listAccountSubject({ |
| | | current: 1, |
| | | size: 1000, |
| | | status: 0, |
| | | }); |
| | | options = toTree(data?.records || []); |
| | | } catch (error) { |
| | | // å
¨å±æ¦æªå¨å·²æç¤ºï¼ä¸é¢èµ°å
åºç§ç® |
| | | } |
| | | if (options.length === 0) { |
| | | options = fallbackSubjects.map(item => ({ ...item, children: [] })); |
| | | } |
| | | subjectOptions.value = options; |
| | | await setDefaultSubjectSelection(); |
| | | if (filters.subject) { |
| | | await getTableData(); |
| | | } |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { date: "2024-01-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 100000 }, |
| | | { date: "2024-01-05", voucherNo: "è®°-0001", summary: "é宿¶å
¥", debit: 5650, credit: 0, direction: "å", balance: 105650 }, |
| | | { date: "2024-01-10", voucherNo: "è®°-0002", summary: "éè´æ¯åº", debit: 0, credit: 8000, direction: "å", balance: 97650 }, |
| | | { date: "2024-01-15", voucherNo: "è®°-0003", summary: "æ¶å°è´§æ¬¾", debit: 10000, credit: 0, direction: "å", balance: 107650 }, |
| | | { date: "2024-01-20", voucherNo: "è®°-0004", summary: "æ¯ä»è´¹ç¨", debit: 0, credit: 5000, direction: "å", balance: 102650 }, |
| | | { date: "2024-01-31", voucherNo: "-", summary: "æ¬æå计", debit: 15650, credit: 13000, direction: "å", balance: 102650 }, |
| | | { date: "2024-02-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 102650 }, |
| | | { date: "2024-02-10", voucherNo: "è®°-0005", summary: "é宿¶å
¥", debit: 8000, credit: 0, direction: "å", balance: 110650 }, |
| | | { date: "2024-02-15", voucherNo: "è®°-0006", summary: "éè´æ¯åº", debit: 0, credit: 12000, direction: "å", balance: 98650 }, |
| | | { date: "2024-02-28", voucherNo: "-", summary: "æ¬æå计", debit: 8000, credit: 12000, direction: "å", balance: 98650 }, |
| | | { date: "2024-03-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 98650 }, |
| | | { date: "2024-03-05", voucherNo: "è®°-0007", summary: "é宿¶å
¥", debit: 12000, credit: 0, direction: "å", balance: 110650 }, |
| | | { date: "2024-03-10", voucherNo: "è®°-0008", summary: "æ¯ä»å·¥èµ", debit: 0, credit: 15000, direction: "å", balance: 95650 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬æå计", debit: 12000, credit: 15000, direction: "å", balance: 95650 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬å¹´ç´¯è®¡", debit: 35650, credit: 40000, direction: "å", balance: 95650 }, |
| | | ]; |
| | | |
| | | const getTableData = () => { |
| | | // èè°çº¦å®ï¼æ»è´¦æ¥å£è¿åè¡æ°ç»ï¼rowType/date/voucherNo/summary/debit/credit/direction/balanceï¼ |
| | | const getTableData = async () => { |
| | | if (!currentSubject.value) { |
| | | dataList.value = []; |
| | | return; |
| | | } |
| | | dataList.value = [...mockData]; |
| | | try { |
| | | const { data } = await getGeneralLedger({ |
| | | subjectCode: currentSubject.value.code, |
| | | startMonth: filters.startMonth, |
| | | endMonth: filters.endMonth, |
| | | }); |
| | | dataList.value = Array.isArray(data) ? data : data?.records || []; |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.subject = []; |
| | | filters.startMonth = "2024-01"; |
| | | filters.endMonth = "2024-03"; |
| | | const resetFilters = async () => { |
| | | filters.startMonth = defaultMonth; |
| | | filters.endMonth = defaultMonth; |
| | | dataList.value = []; |
| | | }; |
| | | |
| | | const getSummaries = (param) => { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | if (column.property === "debit") { |
| | | const values = data.map(item => Number(item.debit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else if (column.property === "credit") { |
| | | const values = data.map(item => Number(item.credit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | subjectKeyword.value = ""; |
| | | subjectTreeRef.value?.filter(""); |
| | | await setDefaultSubjectSelection(); |
| | | if (filters.subject) { |
| | | await getTableData(); |
| | | } |
| | | }; |
| | | |
| | | const handlePrint = () => { |
| | |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // é»è®¤ä¸å è½½æ°æ®ï¼éè¦éæ©ç§ç® |
| | | onMounted(async () => { |
| | | await loadSubjectOptions(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .ledger-header { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | h2 { |
| | | margin: 0 0 10px 0; |
| | | } |
| | | p { |
| | | color: #606266; |
| | | margin: 5px 0; |
| | | } |
| | | .ledger-layout { |
| | | display: flex; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .subject-panel { |
| | | width: 260px; |
| | | flex-shrink: 0; |
| | | padding: 12px; |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 8px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .subject-tree-scroll { |
| | | height: 600px; |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | .subject-node { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .ledger-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .filter-form { |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .text-primary { |
| | |
| | | color: #e6a23c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .subject-panel :deep(.el-tree-node__content) { |
| | | height: 34px; |
| | | } |
| | | |
| | | .subject-panel :deep(.el-tree-node.is-current > .el-tree-node__content) { |
| | | background-color: #f0f7ff; |
| | | } |
| | | </style> |
| | |
| | | </el-form-item> |
| | | <el-form-item label="å¶å人:"> |
| | | <el-select v-model="filters.creator" placeholder="è¯·éæ©å¶å人" clearable style="width: 150px;"> |
| | | <el-option label="å¼ ä¸" value="å¼ ä¸" /> |
| | | <el-option label="æå" value="æå" /> |
| | | <el-option label="çäº" value="çäº" /> |
| | | <el-option |
| | | v-for="item in creatorOptions" |
| | | :key="item" |
| | | :label="item" |
| | | :value="item" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)" v-if="row.status === 'unposted'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handlePost(row)" v-if="row.status === 'unposted'">è¿è´¦</el-button> |
| | | <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'unposted'">ä½åº</el-button> |
| | | <el-button type="primary" link @click="edit(row)" v-if="canEditVoucher(row.status)">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handlePost(row)" v-if="canEditVoucher(row.status)">è¿è´¦</el-button> |
| | | <el-button type="danger" link @click="handleCancel(row)" v-if="canEditVoucher(row.status)">ä½åº</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | |
| | | <h2 class="voucher-title">è®°è´¦åè¯</h2> |
| | | <div class="voucher-period">{{ form.voucherDate ? form.voucherDate.substring(0, 7) + 'æ' : '' }}</div> |
| | | </div> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="0"> |
| | | <el-form :model="form" :rules="rules" :disabled="isViewMode" ref="formRef" label-width="0"> |
| | | <div class="voucher-info"> |
| | | <div class="voucher-no-section"> |
| | | <span class="label">åè¯åï¼</span> |
| | | <el-select v-model="form.voucherPrefix" style="width: 70px;"> |
| | | <el-select v-model="form.voucherPrefix" :disabled="isViewMode" style="width: 70px;"> |
| | | <el-option label="è®°" value="è®°" /> |
| | | </el-select> |
| | | <el-input v-model="form.voucherNum" style="width: 60px;" /> |
| | | <el-input v-model="form.voucherNum" :disabled="isViewMode" style="width: 60px;" /> |
| | | <span class="label" style="margin-left: 5px;">å·</span> |
| | | </div> |
| | | <div class="voucher-date-section"> |
| | | <span class="label">æ¥æï¼</span> |
| | | <el-date-picker v-model="form.voucherDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 140px;" /> |
| | | <el-date-picker v-model="form.voucherDate" :disabled="isViewMode" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 140px;" /> |
| | | </div> |
| | | <div class="voucher-attachment-section"> |
| | | <span class="label">éä»¶ï¼</span> |
| | | <el-input-number v-model="form.attachmentCount" :min="0" :controls="false" style="width: 60px;" /> |
| | | <el-input-number v-model="form.attachmentCount" :disabled="isViewMode" :min="0" :controls="false" style="width: 60px;" /> |
| | | <span class="label" style="margin-left: 5px;">å¼ </span> |
| | | <el-button type="primary" link style="margin-left: 10px;">ä¸ä¼ æä»¶</el-button> |
| | | <el-button type="primary" link :disabled="isViewMode" style="margin-left: 10px;">ä¸ä¼ æä»¶</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="voucher-table"> |
| | |
| | | <tbody> |
| | | <tr v-for="(entry, rowIndex) in form.entries" :key="rowIndex" @click="selectRow(rowIndex)" :class="{ 'selected-row': selectedRowIndex === rowIndex }"> |
| | | <td class="col-summary"> |
| | | <el-input v-model="entry.summary" placeholder="请è¾å
¥æè¦" @focus="selectRow(rowIndex)" /> |
| | | <el-input v-model="entry.summary" :disabled="isViewMode" placeholder="请è¾å
¥æè¦" @focus="selectRow(rowIndex)" /> |
| | | </td> |
| | | <td class="col-subject"> |
| | | <el-select v-model="entry.subjectCode" placeholder="éæ©ç§ç®" filterable @change="(val) => handleSubjectChange(val, rowIndex)" @focus="selectRow(rowIndex)"> |
| | | <el-option v-for="item in subjectList" :key="item.code" :label="item.code + item.name" :value="item.code" /> |
| | | </el-select> |
| | | <el-tree-select |
| | | v-model="entry.subjectCode" |
| | | :data="subjectTreeOptions" |
| | | :props="subjectTreeSelectProps" |
| | | :disabled="isViewMode" |
| | | placeholder="éæ©ç§ç®" |
| | | filterable |
| | | check-strictly |
| | | clearable |
| | | :render-after-expand="false" |
| | | @change="(val) => handleSubjectChange(val, rowIndex)" |
| | | @focus="selectRow(rowIndex)" |
| | | /> |
| | | <div class="subject-name">{{ entry.subjectName }}</div> |
| | | </td> |
| | | <!-- åæ¹11å --> |
| | | <template v-if="editingCell.row === rowIndex && editingCell.type === 'debit'"> |
| | | <td colspan="11" class="debit-input-cell"> |
| | | <el-input-number ref="amountInputRef" v-model="entry.debit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | <el-input-number ref="amountInputRef" v-model="entry.debit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | </td> |
| | | </template> |
| | | <template v-else> |
| | |
| | | <!-- è´·æ¹11å --> |
| | | <template v-if="editingCell.row === rowIndex && editingCell.type === 'credit'"> |
| | | <td colspan="11" class="credit-input-cell"> |
| | | <el-input-number ref="amountInputRef" v-model="entry.credit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | <el-input-number ref="amountInputRef" v-model="entry.credit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | </td> |
| | | </template> |
| | | <template v-else> |
| | |
| | | </td> |
| | | </template> |
| | | <td class="col-action"> |
| | | <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="form.entries.length <= 2">å é¤</el-button> |
| | | <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="isViewMode || form.entries.length <= 2">å é¤</el-button> |
| | | </td> |
| | | </tr> |
| | | <tr class="total-row"> |
| | |
| | | </table> |
| | | </div> |
| | | <div class="voucher-toolbar"> |
| | | <el-button type="primary" link @click="addEntry" icon="Plus">æ°å¢è¡</el-button> |
| | | <el-button type="primary" link @click="addEntry" icon="Plus" :disabled="isViewMode">æ°å¢è¡</el-button> |
| | | </div> |
| | | <div class="voucher-footer"> |
| | | <div class="creator-section"> |
| | | <span class="label">å¶å人ï¼{{ form.creator }}</span> |
| | | <span class="label">å¶å人ï¼</span> |
| | | <el-select |
| | | v-model="form.creator" |
| | | :disabled="isViewMode" |
| | | placeholder="è¯·éæ©å¶å人" |
| | | filterable |
| | | clearable |
| | | style="width: 200px;" |
| | | > |
| | | <el-option |
| | | v-for="item in creatorOptions" |
| | | :key="item" |
| | | :label="item" |
| | | :value="item" |
| | | /> |
| | | </el-select> |
| | | </div> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | <template #footer> |
| | | <div> |
| | | <el-button type="primary" @click="submitForm" :disabled="!isBalanced">ä¿å</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button v-if="!isViewMode" type="primary" @click="submitForm" :disabled="!isBalanced">ä¿å</el-button> |
| | | <el-button @click="dialogVisible = false">{{ isViewMode ? 'å
³é' : 'åæ¶' }}</el-button> |
| | | </div> |
| | | </template> |
| | | </FormDialog> |
| | |
| | | import { ref, reactive, onMounted, computed, nextTick } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user"; |
| | | import { listAccountSubject } from "@/api/financialManagement/accountSubject"; |
| | | import { |
| | | listVoucherPage, |
| | | addVoucher, |
| | | updateVoucher, |
| | | postVoucher, |
| | | cancelVoucher, |
| | | getVoucherDetail, |
| | | } from "@/api/financialManagement/voucher"; |
| | | |
| | | defineOptions({ |
| | | name: "åè¯ç®¡ç", |
| | | }); |
| | | |
| | | const userStore = useUserStore(); |
| | | const getDefaultCreator = () => userStore.nickName || userStore.name || "å¼ ä¸"; |
| | | |
| | | const filters = reactive({ |
| | | voucherNo: "", |
| | |
| | | { label: "åè¯åå·", prop: "voucherNo", width: "120" }, |
| | | { label: "åè¯æ¥æ", prop: "voucherDate", width: "120" }, |
| | | { label: "æè¦", prop: "summary", showOverflowTooltip: true }, |
| | | { label: "åæ¹éé¢", prop: "debit", slot: "debit" }, |
| | | { label: "è´·æ¹éé¢", prop: "credit", slot: "credit" }, |
| | | { label: "åæ¹éé¢", prop: "debit", dataType: "slot", slot: "debit" }, |
| | | { label: "è´·æ¹éé¢", prop: "credit", dataType: "slot", slot: "credit" }, |
| | | { label: "å¶å人", prop: "creator", width: "100" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "220", fixed: "right" }, |
| | | { label: "ç¶æ", prop: "status", dataType: "slot", slot: "status" }, |
| | | { label: "æä½", prop: "operation", dataType: "slot", slot: "operation", width: "220", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const dialogMode = ref("add"); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | const isViewMode = computed(() => dialogMode.value === "view"); |
| | | |
| | | const subjectList = [ |
| | | { code: "1001", name: "åºåç°é" }, |
| | | { code: "1002", name: "é¶è¡å款" }, |
| | | { code: "1122", name: "åºæ¶è´¦æ¬¾" }, |
| | | { code: "2202", name: "åºä»è´¦æ¬¾" }, |
| | | { code: "5001", name: "çäº§ææ¬" }, |
| | | { code: "6001", name: "主è¥ä¸å¡æ¶å
¥" }, |
| | | { code: "6401", name: "主è¥ä¸å¡ææ¬" }, |
| | | const fallbackSubjectTree = [ |
| | | { subjectCode: "1001", subjectName: "åºåç°é", balanceDirection: "åæ¹", children: [] }, |
| | | { subjectCode: "1002", subjectName: "é¶è¡å款", balanceDirection: "åæ¹", children: [] }, |
| | | { subjectCode: "1122", subjectName: "åºæ¶è´¦æ¬¾", balanceDirection: "åæ¹", children: [] }, |
| | | { subjectCode: "2202", subjectName: "åºä»è´¦æ¬¾", balanceDirection: "è´·æ¹", children: [] }, |
| | | { subjectCode: "5001", subjectName: "çäº§ææ¬", balanceDirection: "åæ¹", children: [] }, |
| | | { subjectCode: "6001", subjectName: "主è¥ä¸å¡æ¶å
¥", balanceDirection: "è´·æ¹", children: [] }, |
| | | { subjectCode: "6401", subjectName: "主è¥ä¸å¡ææ¬", balanceDirection: "åæ¹", children: [] }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | const subjectTreeOptions = ref([]); |
| | | const subjectList = ref([]); |
| | | const subjectTreeSelectProps = { |
| | | children: "children", |
| | | label: "label", |
| | | value: "value", |
| | | }; |
| | | |
| | | const buildSubjectTreeOptions = (nodes = [], flatList = []) => |
| | | (nodes || []) |
| | | .filter(item => item.subjectCode && item.subjectName) |
| | | .map(item => { |
| | | const balanceDirection = item.balanceDirection || ""; |
| | | const flatItem = { |
| | | code: item.subjectCode, |
| | | name: item.subjectName, |
| | | balanceDirection, |
| | | }; |
| | | flatList.push(flatItem); |
| | | return { |
| | | value: flatItem.code, |
| | | label: `${flatItem.code} ${flatItem.name}${balanceDirection ? ` [${balanceDirection}]` : ""}`, |
| | | children: buildSubjectTreeOptions(item.children || [], flatList), |
| | | }; |
| | | }); |
| | | |
| | | const createEmptyEntry = () => ({ |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | balanceDirection: "", |
| | | summary: "", |
| | | debit: 0, |
| | | credit: 0, |
| | | }); |
| | | |
| | | const createDefaultForm = () => ({ |
| | | voucherNo: "", |
| | | voucherPrefix: "è®°", |
| | | voucherNum: "", |
| | | voucherDate: "", |
| | | attachmentCount: 0, |
| | | entries: [], |
| | | creator: "å¼ ä¸", |
| | | entries: [createEmptyEntry(), createEmptyEntry()], |
| | | creator: getDefaultCreator(), |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | |
| | | const userOptions = ref([]); |
| | | |
| | | const creatorOptions = computed(() => { |
| | | const source = [ |
| | | ...userOptions.value.map(item => item.nickName || item.userName || item.name), |
| | | getDefaultCreator(), |
| | | form.creator, |
| | | filters.creator, |
| | | ]; |
| | | return [...new Set(source.filter(Boolean))]; |
| | | }); |
| | | |
| | | const selectedRowIndex = ref(-1); |
| | |
| | | const rules = { |
| | | voucherDate: [{ required: true, message: "è¯·éæ©åè¯æ¥æ", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, voucherNo: "è®°-0001", voucherDate: "2024-01-15", summary: "é宿¶å
¥", debit: 5650, credit: 5650, creator: "å¼ ä¸", status: "posted", entries: [{ subjectCode: "1002", subjectName: "é¶è¡å款", summary: "é宿¶å
¥", debit: 5650, credit: 0 }, { subjectCode: "6001", subjectName: "主è¥ä¸å¡æ¶å
¥", summary: "é宿¶å
¥", debit: 0, credit: 5000 }, { subjectCode: "2221", subjectName: "åºäº¤ç¨è´¹", summary: "é项ç¨é¢", debit: 0, credit: 650 }] }, |
| | | { id: 2, voucherNo: "è®°-0002", voucherDate: "2024-01-16", summary: "éè´åææ", debit: 9040, credit: 9040, creator: "æå", status: "unposted", entries: [{ subjectCode: "5001", subjectName: "çäº§ææ¬", summary: "éè´åææ", debit: 8000, credit: 0 }, { subjectCode: "2221", subjectName: "åºäº¤ç¨è´¹", summary: "è¿é¡¹ç¨é¢", debit: 1040, credit: 0 }, { subjectCode: "2202", subjectName: "åºä»è´¦æ¬¾", summary: "éè´åææ", debit: 0, credit: 9040 }] }, |
| | | { id: 3, voucherNo: "è®°-0003", voucherDate: "2024-01-18", summary: "æ¯ä»è´§æ¬¾", debit: 5000, credit: 5000, creator: "å¼ ä¸", status: "posted", entries: [{ subjectCode: "2202", subjectName: "åºä»è´¦æ¬¾", summary: "æ¯ä»è´§æ¬¾", debit: 5000, credit: 0 }, { subjectCode: "1002", subjectName: "é¶è¡å款", summary: "æ¯ä»è´§æ¬¾", debit: 0, credit: 5000 }] }, |
| | | ]; |
| | | |
| | | const totalDebit = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.debit), 0); |
| | |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const normalizeVoucherStatus = status => String(status || "").toLowerCase(); |
| | | |
| | | const canEditVoucher = status => { |
| | | const key = normalizeVoucherStatus(status); |
| | | return key === "unposted" || status === "æªè¿è´¦"; |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const key = normalizeVoucherStatus(status); |
| | | const map = { unposted: "æªè¿è´¦", posted: "å·²è¿è´¦", cancelled: "å·²ä½åº" }; |
| | | return map[status] || status; |
| | | return map[key] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const key = normalizeVoucherStatus(status); |
| | | const map = { unposted: "warning", posted: "success", cancelled: "info" }; |
| | | return map[status] || ""; |
| | | return map[key] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.voucherNo) { |
| | | result = result.filter(item => item.voucherNo.includes(filters.voucherNo)); |
| | | // èè°çº¦å®ï¼åé¡µåæ°ä½¿ç¨ current/sizeï¼æ¥æèå´æå为 startDate/endDate |
| | | const getTableData = async () => { |
| | | try { |
| | | const [startDate, endDate] = |
| | | filters.dateRange && filters.dateRange.length === 2 ? filters.dateRange : ["", ""]; |
| | | const { data } = await listVoucherPage({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | voucherNo: filters.voucherNo, |
| | | creator: filters.creator, |
| | | status: filters.status, |
| | | startDate, |
| | | endDate, |
| | | }); |
| | | dataList.value = data?.records || []; |
| | | pagination.total = Number(data?.total || 0); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.voucherDate >= filters.dateRange[0] && item.voucherDate <= filters.dateRange[1]); |
| | | }; |
| | | |
| | | // åè¯åå½éçç§ç®ä¸æä¸æ»è´¦ç§ç®ä¿æä¸è´ï¼é¿å
æäº¤ä¸åå¨ç§ç® |
| | | const loadSubjectList = async () => { |
| | | try { |
| | | const { data } = await listAccountSubject({ |
| | | current: 1, |
| | | size: 1000, |
| | | status: 0 |
| | | }); |
| | | const flatList = []; |
| | | const treeOptions = buildSubjectTreeOptions(data?.records || [], flatList); |
| | | if (treeOptions.length > 0) { |
| | | subjectTreeOptions.value = treeOptions; |
| | | subjectList.value = flatList; |
| | | return; |
| | | } |
| | | const fallbackFlatList = []; |
| | | subjectTreeOptions.value = buildSubjectTreeOptions(fallbackSubjectTree, fallbackFlatList); |
| | | subjectList.value = fallbackFlatList; |
| | | } catch (error) { |
| | | // å
¨å±æ¦æªå¨å·²æç¤ºé误ï¼è¿éä¿çé»è®¤ç§ç®ä½ä¸ºå
åº |
| | | const fallbackFlatList = []; |
| | | subjectTreeOptions.value = buildSubjectTreeOptions(fallbackSubjectTree, fallbackFlatList); |
| | | subjectList.value = fallbackFlatList; |
| | | } |
| | | if (filters.creator) { |
| | | result = result.filter(item => item.creator === filters.creator); |
| | | }; |
| | | |
| | | const loadUserOptions = async () => { |
| | | try { |
| | | const { data } = await userListNoPageByTenantId(); |
| | | userOptions.value = Array.isArray(data) ? data : []; |
| | | } catch (error) { |
| | | userOptions.value = []; |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | |
| | | }; |
| | | |
| | | const addEntry = () => { |
| | | form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }); |
| | | if (isViewMode.value) { |
| | | return; |
| | | } |
| | | form.entries.push(createEmptyEntry()); |
| | | }; |
| | | |
| | | const selectRow = (index) => { |
| | |
| | | }; |
| | | |
| | | const openAmountInput = (index, type) => { |
| | | if (isViewMode.value) { |
| | | return; |
| | | } |
| | | editingCell.row = index; |
| | | editingCell.type = type; |
| | | nextTick(() => { |
| | |
| | | }; |
| | | |
| | | const removeEntry = (index) => { |
| | | if (isViewMode.value) { |
| | | return; |
| | | } |
| | | if (form.entries.length <= 2) { |
| | | return; |
| | | } |
| | | form.entries.splice(index, 1); |
| | | calculateTotal(); |
| | | }; |
| | | |
| | | const handleSubjectChange = (val, index) => { |
| | | const subject = subjectList.find(item => item.code === val); |
| | | const subject = subjectList.value.find(item => item.code === val); |
| | | if (subject) { |
| | | form.entries[index].subjectName = subject.name; |
| | | form.entries[index].balanceDirection = subject.balanceDirection || ""; |
| | | } else { |
| | | form.entries[index].subjectName = ""; |
| | | form.entries[index].balanceDirection = ""; |
| | | } |
| | | }; |
| | | |
| | | const calculateTotal = () => { |
| | | // èªå¨è®¡ç®ï¼ç±computed屿§å¤ç |
| | | }; |
| | | |
| | | const add = () => { |
| | | dialogMode.value = "add"; |
| | | isEdit.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "æ°å¢åè¯"; |
| | | const nextNum = String(mockData.length + 1).padStart(2, "0"); |
| | | Object.assign(form, { |
| | | voucherNo: "è®°-" + nextNum, |
| | | const nextNum = String((pagination.total || 0) + 1).padStart(4, "0"); |
| | | Object.assign(form, createDefaultForm(), { |
| | | voucherPrefix: "è®°", |
| | | voucherNum: nextNum, |
| | | voucherNo: `è®°-${nextNum}`, |
| | | voucherDate: new Date().toISOString().split('T')[0], |
| | | attachmentCount: 0, |
| | | entries: [ |
| | | { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }, |
| | | { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }, |
| | | { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }, |
| | | { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }, |
| | | ], |
| | | creator: "å¼ ä¸", |
| | | remark: "", |
| | | }); |
| | | selectedRowIndex.value = 0; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åè¯"; |
| | | const parts = row.voucherNo.split('-'); |
| | | Object.assign(form, { |
| | | ...row, |
| | | voucherPrefix: parts[0] || 'è®°', |
| | | voucherNum: parts[1] || '', |
| | | }); |
| | | if (form.entries.length < 4) { |
| | | while (form.entries.length < 4) { |
| | | form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }); |
| | | const openVoucherDialog = async (row, mode = "edit") => { |
| | | try { |
| | | dialogMode.value = mode; |
| | | isEdit.value = mode === "edit"; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = mode === "view" ? "æ¥çåè¯" : "ç¼è¾åè¯"; |
| | | const { data } = await getVoucherDetail(row.id); |
| | | const detail = data || row; |
| | | const parts = (detail.voucherNo || "").split("-"); |
| | | Object.assign(form, createDefaultForm(), detail, { |
| | | voucherPrefix: parts[0] || "è®°", |
| | | voucherNum: parts[1] || "", |
| | | creator: detail.creator || getDefaultCreator(), |
| | | entries: |
| | | detail.entries?.map(item => ({ |
| | | subjectCode: item.subjectCode || "", |
| | | subjectName: item.subjectName || "", |
| | | balanceDirection: item.balanceDirection || "", |
| | | summary: item.summary || "", |
| | | debit: Number(item.debit || 0), |
| | | credit: Number(item.credit || 0), |
| | | })) || [], |
| | | }); |
| | | if (form.entries.length < 2) { |
| | | while (form.entries.length < 2) { |
| | | form.entries.push(createEmptyEntry()); |
| | | } |
| | | } |
| | | selectedRowIndex.value = 0; |
| | | dialogVisible.value = true; |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | selectedRowIndex.value = 0; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çåè¯: ${row.voucherNo}`); |
| | | const edit = async row => { |
| | | await openVoucherDialog(row, "edit"); |
| | | }; |
| | | |
| | | const view = async row => { |
| | | await openVoucherDialog(row, "view"); |
| | | }; |
| | | |
| | | const handlePost = (row) => { |
| | |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "posted"; |
| | | } |
| | | }).then(async () => { |
| | | await postVoucher({ id: row.id }); |
| | | ElMessage.success("è¿è´¦æå"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "cancelled"; |
| | | } |
| | | }).then(async () => { |
| | | await cancelVoucher({ id: row.id }); |
| | | ElMessage.success("ä½åºæå"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (isViewMode.value) { |
| | | dialogVisible.value = false; |
| | | return; |
| | | } |
| | | formRef.value.validate(async valid => { |
| | | if (valid) { |
| | | // åç½®æ ¡éªï¼ä¸å端è§å对é½ï¼åå°æ æè¯·æ± |
| | | if (!isBalanced.value) { |
| | | ElMessage.error("åè´·ä¸å¹³è¡¡ï¼è¯·æ£æ¥åå½"); |
| | | return; |
| | | } |
| | | |
| | | const validEntries = form.entries.filter(e => e.subjectCode && (e.debit > 0 || e.credit > 0)); |
| | | const validEntries = form.entries.filter( |
| | | entry => entry.subjectCode && (Number(entry.debit) > 0 || Number(entry.credit) > 0) |
| | | ); |
| | | if (validEntries.length === 0) { |
| | | ElMessage.error("请è³å°å¡«å䏿¡ææåå½"); |
| | | return; |
| | | } |
| | | |
| | | const invalidEntry = validEntries.find( |
| | | entry => Number(entry.debit) > 0 && Number(entry.credit) > 0 |
| | | ); |
| | | if (invalidEntry) { |
| | | ElMessage.error("åä¸åå½ä¸è½åæ¶å¡«ååæ¹åè´·æ¹"); |
| | | return; |
| | | } |
| | | |
| | | const summary = validEntries.find(e => e.debit > 0)?.summary || ""; |
| | | |
| | | const voucherNo = `${form.voucherPrefix}-${form.voucherNum}`; |
| | | const dataToSave = { |
| | | ...form, |
| | | voucherNo, |
| | | voucherDate: form.voucherDate, |
| | | summary, |
| | | creator: form.creator, |
| | | attachmentCount: Number(form.attachmentCount || 0), |
| | | remark: form.remark, |
| | | debit: totalDebitEntry.value, |
| | | credit: totalCreditEntry.value, |
| | | entries: validEntries, |
| | | entries: validEntries.map(entry => ({ |
| | | subjectCode: entry.subjectCode, |
| | | subjectName: entry.subjectName, |
| | | summary: entry.summary, |
| | | debit: Number(entry.debit || 0), |
| | | credit: Number(entry.credit || 0), |
| | | })), |
| | | }; |
| | | |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...dataToSave }; |
| | | try { |
| | | if (isEdit.value) { |
| | | await updateVoucher({ |
| | | id: currentId.value, |
| | | ...dataToSave, |
| | | }); |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | await addVoucher(dataToSave); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...dataToSave, status: "unposted" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | dialogVisible.value = false; |
| | | await getTableData(); |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | onMounted(async () => { |
| | | await loadUserOptions(); |
| | | await loadSubjectList(); |
| | | await getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | .col-subject { |
| | | position: relative; |
| | | |
| | | .el-select { |
| | | .el-select, |
| | | .el-tree-select { |
| | | .el-input input { |
| | | font-size: 12px; |
| | | } |
| | |
| | | // è·åå·¥åºå表 |
| | | const getProcessList = () => { |
| | | list().then(res => { |
| | | processOptions.value = res.data |
| | | processOptions.value = res.data.records |
| | | }) |
| | | } |
| | | |
| | |
| | | batchApproveStockInRecords, |
| | | } from "@/api/inventoryManagement/stockInRecord.js"; |
| | | import { |
| | | findAllQualifiedStockInRecordTypeOptions, findAllUnQualifiedStockInRecordTypeOptions, |
| | | findAllQualifiedStockInRecordTypeOptions, |
| | | // findAllUnQualifiedStockInRecordTypeOptions, |
| | | } from "@/api/basicData/enum.js"; |
| | | |
| | | const {proxy} = getCurrentInstance(); |
| | |
| | | }) |
| | | return |
| | | } |
| | | findAllUnQualifiedStockInRecordTypeOptions() |
| | | .then(res => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | }) |
| | | // findAllUnQualifiedStockInRecordTypeOptions() |
| | | // .then(res => { |
| | | // stockRecordTypeOptions.value = res.data; |
| | | // }) |
| | | } |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { |
| | | findAllQualifiedStockInRecordTypeOptions, |
| | | findAllUnQualifiedStockInRecordTypeOptions, |
| | | // findAllUnQualifiedStockInRecordTypeOptions, |
| | | } from "@/api/basicData/enum.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | |
| | | const fetchStockRecordTypeOptions = () => { |
| | | findAllQualifiedStockInRecordTypeOptions().then(res => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | findAllUnQualifiedStockInRecordTypeOptions().then(res => { |
| | | stockRecordTypeOptions.value = [ |
| | | ...stockRecordTypeOptions.value, |
| | | ...res.data, |
| | | ]; |
| | | }); |
| | | // findAllUnQualifiedStockInRecordTypeOptions().then(res => { |
| | | // stockRecordTypeOptions.value = [ |
| | | // ...stockRecordTypeOptions.value, |
| | | // ...res.data, |
| | | // ]; |
| | | // }); |
| | | }); |
| | | }; |
| | | |
| | |
| | | <span class="title-text">产åå表</span> |
| | | </div> |
| | | <el-button type="primary" size="small" style="margin-bottom: 20px" @click="isShowProductsModal = true" :disabled="!formState.purchaseLedgerId">æ·»å 产å</el-button> |
| | | <el-table :data="formState.purchaseReturnOrderProductsDtos" |
| | | <div class="product-table-scroll"> |
| | | <el-table class="product-table-inner" |
| | | :data="formState.purchaseReturnOrderProductsDtos" |
| | | border |
| | | max-height="400" |
| | | :scroll-y="true" |
| | | show-summary |
| | | :summary-method="summarizeChildrenTable"> |
| | | <el-table-column align="center" |
| | |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="å
¥åºåå·" |
| | | prop="inboundBatches" |
| | | width="150" /> |
| | | <el-table-column label="æ¹æ¬¡å·" |
| | | prop="batchNo" |
| | | width="150" /> |
| | | <el-table-column label="产å大类" |
| | | prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | |
| | | prop="unit" |
| | | width="70" /> |
| | | <el-table-column label="æ°é" |
| | | prop="quantity" |
| | | prop="stockInNum" |
| | | width="100" /> |
| | | <el-table-column label="å¯éè´§æ°é" |
| | | prop="availableQuality" |
| | | prop="unQuantity" |
| | | width="130" /> |
| | | <el-table-column label="å·²éè´§æ°é" |
| | | width="130"> |
| | | <template #default="scope"> |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éè´§æ°é" |
| | | prop="returnQuantity" |
| | | width="180"> |
| | |
| | | placeholder="请è¾å
¥éè´§æ°é" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºåé¢è¦æ°é" |
| | | <!-- <el-table-column label="åºåé¢è¦æ°é" |
| | | prop="warnNum" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | width="80" /> |
| | | width="80" /> --> |
| | | <el-table-column label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | width="120" /> |
| | | <el-table-column label="éè´§æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | width="180"> |
| | | width="120"> |
| | | <template #default="scope"> |
| | | {{ formatAmount(getReturnTotal(scope.row)) || '--' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¯å¦è´¨æ£" |
| | | prop="isChecked" |
| | | width="150"> |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.isChecked ? 'success' : 'info'"> |
| | | {{ scope.row.isChecked ? 'æ¯' : 'å¦' }} |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="section-title"> |
| | |
| | | import {getOptions, purchaseList} from "@/api/procurementManagement/procurementLedger.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | const ProductList = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/ProductList.vue")); |
| | | import { |
| | | productList, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | |
| | | return Number.isNaN(num) ? 0 : num |
| | | } |
| | | |
| | | /** å·²éè´§æ°é = å
¥åºè¡æ»æ°é â å½åå¯éè´§æ°éï¼å©ä½ï¼ */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0) |
| | | const un = Number(row?.unQuantity ?? 0) |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0 |
| | | return Math.max(total - un, 0) |
| | | } |
| | | |
| | | const getReturnTotal = (row) => { |
| | | const qty = toNumber(row?.returnQuantity) |
| | | const unitPrice = toNumber(row?.taxInclusiveUnitPrice) |
| | |
| | | } |
| | | |
| | | const getReturnQtyMax = (row) => { |
| | | const max = Number(row?.availableQuality) |
| | | const max = Number(row?.unQuantity) |
| | | if (Number.isNaN(max) || max < 0) { |
| | | return 0 |
| | | } |
| | |
| | | return proxy.summarizeTable( |
| | | param, |
| | | [ |
| | | "quantity", |
| | | "availableQuality", |
| | | "stockInNum", |
| | | "unQuantity", |
| | | "returnQuantity", |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ], |
| | | { |
| | | quantity: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | stockInNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | returnQuantity: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | availableQuality: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | unQuantity: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | |
| | | } |
| | | } |
| | | |
| | | // å¤çæ¹åéè´å°è´¦æ°æ® |
| | | const handleChangePurchaseLedgerId = async () => { |
| | | // å¤çæ¹åéè´å°è´¦æ°æ®ï¼ä¸è¯·æ±æ¥å£åæ¾äº§åï¼äº§åä»
å¨ãæ·»å 产åãå¼¹çªå¾éååå
¥ï¼ |
| | | const handleChangePurchaseLedgerId = () => { |
| | | resetFeeInfo() |
| | | if (!formState.value.purchaseLedgerId) { |
| | | formState.value.purchaseReturnOrderProductsDtos = [] |
| | | return |
| | | } |
| | | const res = await productList({ salesLedgerId: formState.value.purchaseLedgerId, type: 2 }); |
| | | formState.value.purchaseReturnOrderProductsDtos = res.data.map(item => ({ |
| | | ...item, |
| | | returnQuantity: undefined, |
| | | taxInclusiveTotalPrice: 0, |
| | | salesLedgerProductId: item.id, |
| | | })) |
| | | formState.value.purchaseReturnOrderProductsDtos = [] |
| | | syncTotalAmount() |
| | | } |
| | | |
| | |
| | | ...item, |
| | | returnQuantity: undefined, |
| | | taxInclusiveTotalPrice: 0, |
| | | salesLedgerProductId: item.id, |
| | | // salesLedgerProductId: item.salesLedgerProductId, |
| | | })); |
| | | formState.value.purchaseReturnOrderProductsDtos.push(...newProducts); |
| | | syncTotalAmount() |
| | |
| | | // éè¡æ ¡éªéè´§æ°éï¼ä»»æä¸è¡æªå¡«/éæ³/è¶
éé½ä¸å
许æäº¤ |
| | | const invalidRowIndex = productList.findIndex((item) => { |
| | | const qty = Number(item.returnQuantity) |
| | | const maxQty = Number(item.availableQuality) |
| | | const maxQty = Number(item.unQuantity) |
| | | |
| | | if (item.returnQuantity === null || item.returnQuantity === undefined || item.returnQuantity === "") { |
| | | return true |
| | |
| | | |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | createPurchaseReturnOrder(formState.value).then(res => { |
| | | console.log(productList) |
| | | const submitPayload = { |
| | | ...formState.value, |
| | | purchaseReturnOrderProductsDtos: productList.map((row) => ({ |
| | | ...row, |
| | | stockInRecordId: row.id, |
| | | })), |
| | | } |
| | | createPurchaseReturnOrder(submitPayload).then(res => { |
| | | // å
³éæ¨¡ææ¡ |
| | | isShow.value = false; |
| | | // åç¥ç¶ç»ä»¶å·²å®æ |
| | |
| | | border-radius: 50%; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .product-table-scroll { |
| | | width: 100%; |
| | | overflow-x: auto; |
| | | } |
| | | |
| | | .product-table-inner { |
| | | min-width: 1280px; |
| | | } |
| | | </style> |
| | |
| | | width="1200" |
| | | @close="closeModal" |
| | | > |
| | | <div class="table_list"> |
| | | <div class="table_list" v-loading="tableLoading"> |
| | | <el-table :data="tableData" |
| | | border |
| | | row-key="id" |
| | | @selection-change="handleChangeSelection"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="å
¥åºåå·" |
| | | prop="inboundBatches" |
| | | width="150" /> |
| | | <el-table-column label="æ¹æ¬¡å·" |
| | | prop="batchNo" |
| | | width="150" /> |
| | | <el-table-column label="产å大类" |
| | | prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | |
| | | prop="unit" |
| | | width="70" /> |
| | | <el-table-column label="æ°é" |
| | | prop="quantity" |
| | | prop="stockInNum" |
| | | width="70" /> |
| | | <el-table-column label="åºåé¢è¦æ°é" |
| | | <el-table-column label="å¯éè´§æ°é" |
| | | prop="unQuantity" |
| | | width="130" /> |
| | | <el-table-column label="å·²éè´§æ°é" |
| | | width="130"> |
| | | <template #default="scope"> |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="åºåé¢è¦æ°é" |
| | | prop="warnNum" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | width="80" /> |
| | | width="80" /> --> |
| | | <el-table-column label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" |
| | | <!-- <el-table-column label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | width="150" /> --> |
| | | <el-table-column label="æ¯å¦è´¨æ£" |
| | | prop="isChecked" |
| | | width="150"> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | </div> |
| | | |
| | | <template #footer> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {computed, reactive, ref, onMounted} from "vue"; |
| | | import {productList} from "@/api/procurementManagement/procurementLedger.js"; |
| | | import {computed, ref, onMounted} from "vue"; |
| | | import {getPurchaseReturnOrderByPurchaseLedgerId} from "@/api/procurementManagement/purchase_return_order.js"; |
| | | import {ElMessage} from "element-plus"; |
| | | |
| | | const props = defineProps({ |
| | |
| | | }, |
| | | |
| | | purchaseLedgerId: { |
| | | type: Number, |
| | | type: [Number, String], |
| | | required: true, |
| | | } |
| | | }); |
| | |
| | | const tableData = ref([]) |
| | | const selectedRows = ref([]) |
| | | const tableLoading = ref(false) |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | const total = ref(0) |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList() |
| | | /** å·²éè´§æ°é = å
¥åºè¡æ»æ°é â å½åå¯éè´§æ°éï¼å©ä½ï¼ */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0) |
| | | const un = Number(row?.unQuantity ?? 0) |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0 |
| | | return Math.max(total - un, 0) |
| | | } |
| | | |
| | | const handleChangeSelection = (val) => { |
| | | selectedRows.value = val; |
| | | } |
| | | |
| | | /** ä¸ New.vue ä¸éè´å°è´¦åæ´æ¶è§£æ getByPurchaseLedgerId çè§åä¸è´ */ |
| | | const parseProductRowsFromLedgerResponse = (res) => { |
| | | const payload = res?.data |
| | | let list = [] |
| | | if (Array.isArray(payload)) { |
| | | list = payload |
| | | } else if (payload && typeof payload === 'object') { |
| | | const nested = |
| | | payload.purchaseReturnOrderProductsDtos || |
| | | payload.purchaseReturnOrderProductsDetailVoList |
| | | list = Array.isArray(nested) ? nested : [] |
| | | if (list.length && list[0]?.salesLedgerProduct) { |
| | | list = list.map((item) => ({ ...item, ...item.salesLedgerProduct })) |
| | | } |
| | | } |
| | | return list |
| | | } |
| | | |
| | | const fetchData = () => { |
| | | tableLoading.value = true; |
| | | productList({salesLedgerId: props.purchaseLedgerId, type: 2}).then((res) => { |
| | | tableData.value = res.data; |
| | | }).finally(() => { |
| | | tableLoading.value = false; |
| | | if (props.purchaseLedgerId === undefined || props.purchaseLedgerId === null || props.purchaseLedgerId === '') { |
| | | tableData.value = [] |
| | | return |
| | | } |
| | | tableLoading.value = true |
| | | getPurchaseReturnOrderByPurchaseLedgerId({ |
| | | purchaseLedgerId: props.purchaseLedgerId, |
| | | }) |
| | | .then((res) => { |
| | | const list = parseProductRowsFromLedgerResponse(res) |
| | | tableData.value = list |
| | | }) |
| | | .catch(() => { |
| | | tableData.value = [] |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | const handleSubmit = () => { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="éæåå·ï¼"> |
| | | <el-input v-model="searchForm.no" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | <el-input |
| | | v-model="searchForm.no" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleQuery"> æç´¢ </el-button> |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div> |
| | | <el-button type="primary" @click="isShowNewModal = true">æ°å¢</el-button> |
| | | <el-button type="primary" @click="isShowNewModal = true" |
| | | >æ°å¢</el-button |
| | | > |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | @pagination="paginationChange" |
| | | > |
| | | <template #operation="{ row }"> |
| | | <el-button link type="primary" size="small" style="color: #67C23A" @click="handleDetail(row)">详æ
</el-button> |
| | | <el-button link size="small" @click="handleDelete(row)">å é¤</el-button> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | style="color: #67c23a" |
| | | @click="handleDetail(row)" |
| | | >详æ
</el-button |
| | | > |
| | | <el-button link size="small" @click="handleDelete(row)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <new v-if="isShowNewModal" |
| | | v-model:visible="isShowNewModal" |
| | | @completed="handleQuery" /> |
| | | <new |
| | | v-if="isShowNewModal" |
| | | v-model:visible="isShowNewModal" |
| | | @completed="handleQuery" |
| | | /> |
| | | |
| | | <el-dialog |
| | | v-model="detailVisible" |
| | |
| | | > |
| | | <div v-loading="detailLoading"> |
| | | <el-descriptions :column="3" border> |
| | | <el-descriptions-item label="éæåå·">{{ detailData.no || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="éè´§æ¹å¼">{{ getReturnTypeLabel(detailData.returnType) }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¾åºååç§°">{{ detailData.supplierName || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="项ç®é¶æ®µ">{{ getProjectPhaseLabel(detailData.projectPhase) }}</el-descriptions-item> |
| | | <el-descriptions-item label="å
³èåå·">{{ detailData.purchaseContractNumber || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¶ä½æ¥æ">{{ detailData.preparedAt || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¶å人">{{ detailData.preparedUserName || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="éæäºº">{{ detailData.returnUserName || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ´åææ£é¢">{{ formatAmount(detailData.totalDiscountAmount) }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ´åææ£ç">{{ detailData.totalDiscountRate ?? '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æäº¤éé¢">{{ formatAmount(detailData.totalAmount) }}</el-descriptions-item> |
| | | <el-descriptions-item label="å建人">{{ detailData.createUserName || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´">{{ detailData.createTime || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æè¿æ´æ°æ¶é´">{{ detailData.updateTime || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨" :span="3">{{ detailData.remark || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="éæåå·">{{ |
| | | detailData.no || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="éè´§æ¹å¼">{{ |
| | | getReturnTypeLabel(detailData.returnType) |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¾åºååç§°">{{ |
| | | detailData.supplierName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="项ç®é¶æ®µ">{{ |
| | | getProjectPhaseLabel(detailData.projectPhase) |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å
³èçéè´è®¢åå·">{{ |
| | | detailData.purchaseContractNumber || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¶ä½æ¥æ">{{ |
| | | detailData.preparedAt || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¶å人">{{ |
| | | detailData.preparedUserName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="éæäºº">{{ |
| | | detailData.returnUserName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ´åææ£é¢">{{ |
| | | formatAmount(detailData.totalDiscountAmount) |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ´åææ£ç">{{ |
| | | detailData.totalDiscountRate ?? "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æäº¤éé¢">{{ |
| | | formatAmount(detailData.totalAmount) |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å建人">{{ |
| | | detailData.createUserName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´">{{ |
| | | detailData.createTime || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æè¿æ´æ°æ¶é´">{{ |
| | | detailData.updateTime || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨" :span="3">{{ |
| | | detailData.remark || "--" |
| | | }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <el-divider content-position="left">产åå表</el-divider> |
| | |
| | | max-height="420" |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="产å大类" prop="productCategory" min-width="120" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" min-width="140" show-overflow-tooltip /> |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="å
¥åºåå·" prop="inboundBatches" width="150" /> |
| | | <el-table-column label="æ¹æ¬¡å·" prop="batchNo" width="150" /> |
| | | <el-table-column |
| | | label="产å大类" |
| | | prop="productCategory" |
| | | min-width="120" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="è§æ ¼åå·" |
| | | prop="specificationModel" |
| | | min-width="140" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="åä½" prop="unit" width="80" /> |
| | | <el-table-column label="æ°é" prop="quantity" width="80" /> |
| | | <el-table-column label="éè´§æ°é" prop="returnQuantity" width="100" /> |
| | | <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="120" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" width="90" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" prop="taxInclusiveUnitPrice" width="130"> |
| | | <template #default="scope">{{ formatAmount(scope.row.taxInclusiveUnitPrice) }}</template> |
| | | <el-table-column label="æ°é" prop="stockInNum" width="80" /> |
| | | <el-table-column label="å¯éè´§æ°é" |
| | | prop="unQuantity" |
| | | width="100" /> |
| | | <el-table-column label="å·²éè´§æ°é" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éè´§æ»ä»·(å
)" prop="taxInclusiveTotalPrice" width="130"> |
| | | <template #default="scope">{{ formatAmount(scope.row.taxInclusiveTotalPrice) }}</template> |
| | | <!-- <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="120" /> --> |
| | | <!-- <el-table-column label="ç¨ç(%)" prop="taxRate" width="90" /> --> |
| | | <el-table-column |
| | | label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | width="130" |
| | | > |
| | | <template #default="scope">{{ |
| | | formatAmount(scope.row.taxInclusiveUnitPrice) |
| | | }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="ä¸éè´§æ»ä»·(å
)" prop="taxExclusiveTotalPrice" width="140"> |
| | | <template #default="scope">{{ formatAmount(scope.row.taxExclusiveTotalPrice) }}</template> |
| | | <!-- <el-table-column |
| | | label="éè´§æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | width="130" |
| | | > |
| | | <template #default="scope">{{ |
| | | formatAmount(scope.row.taxInclusiveTotalPrice) |
| | | }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¯å¦è´¨æ£" prop="isChecked" width="100" align="center"> |
| | | <el-table-column |
| | | label="ä¸éè´§æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | width="140" |
| | | > |
| | | <template #default="scope">{{ |
| | | formatAmount(scope.row.taxExclusiveTotalPrice) |
| | | }}</template> |
| | | </el-table-column> --> |
| | | <el-table-column |
| | | label="æ¯å¦è´¨æ£" |
| | | prop="isChecked" |
| | | width="100" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.isChecked ? 'success' : 'info'"> |
| | | {{ scope.row.isChecked ? 'æ¯' : 'å¦' }} |
| | | {{ scope.row.isChecked ? "æ¯" : "å¦" }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | import { ref, reactive, toRefs, onMounted, defineAsyncComponent, getCurrentInstance } from 'vue' |
| | | const { proxy } = getCurrentInstance() |
| | | import {findPurchaseReturnOrderListPage, getPurchaseReturnOrderDetail, deletePurchaseReturnOrder} from "@/api/procurementManagement/purchase_return_order.js"; |
| | | const New = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/New.vue")); |
| | | const tableData = ref([]) |
| | | const selectedRows = ref([]) |
| | | const tableLoading = ref(false) |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | onMounted, |
| | | defineAsyncComponent, |
| | | getCurrentInstance, |
| | | } from "vue"; |
| | | const { proxy } = getCurrentInstance(); |
| | | import { |
| | | findPurchaseReturnOrderListPage, |
| | | getPurchaseReturnOrderDetail, |
| | | deletePurchaseReturnOrder, |
| | | } from "@/api/procurementManagement/purchase_return_order.js"; |
| | | const New = defineAsyncComponent(() => |
| | | import("@/views/procurementManagement/purchaseReturnOrder/New.vue") |
| | | ); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }) |
| | | const detailVisible = ref(false) |
| | | const detailLoading = ref(false) |
| | | const detailData = ref({}) |
| | | const detailProducts = ref([]) |
| | | }); |
| | | const detailVisible = ref(false); |
| | | const detailLoading = ref(false); |
| | | const detailData = ref({}); |
| | | const detailProducts = ref([]); |
| | | // æ¯å¦æ¾ç¤ºæ°å¢å¼¹æ¡ |
| | | const isShowNewModal = ref(false) |
| | | const isShowNewModal = ref(false); |
| | | const returnTypeOptions = [ |
| | | { label: 'éè´§éæ¬¾', value: 0 }, |
| | | { label: 'ææ¶', value: 1 }, |
| | | ] |
| | | { label: "éè´§éæ¬¾", value: 0 }, |
| | | { label: "ææ¶", value: 1 }, |
| | | ]; |
| | | const projectPhaseOptions = [ |
| | | { label: 'ç«é¡¹', value: 0 }, |
| | | { label: '设计', value: 1 }, |
| | | { label: 'éè´', value: 2 }, |
| | | { label: 'ç产', value: 3 }, |
| | | { label: 'åºè´§', value: 4 }, |
| | | ] |
| | | { label: "ç«é¡¹", value: 0 }, |
| | | { label: "设计", value: 1 }, |
| | | { label: "éè´", value: 2 }, |
| | | { label: "ç产", value: 3 }, |
| | | { label: "åºè´§", value: 4 }, |
| | | ]; |
| | | const tableColumn = ref([ |
| | | { |
| | | label: 'éæåå·', |
| | | prop: 'no', |
| | | label: "éæåå·", |
| | | prop: "no", |
| | | }, |
| | | { |
| | | label: 'éè´§æ¹å¼', |
| | | prop: 'returnType', |
| | | formatData: (val) => returnTypeOptions.find(item => item.value === val)?.label || '--', |
| | | label: "éè´§æ¹å¼", |
| | | prop: "returnType", |
| | | formatData: (val) => |
| | | returnTypeOptions.find((item) => item.value === val)?.label || "--", |
| | | }, |
| | | { |
| | | label: 'ä¾åºååç§°', |
| | | prop: 'supplierName', |
| | | label: "ä¾åºååç§°", |
| | | prop: "supplierName", |
| | | width: 180, |
| | | }, |
| | | { |
| | | label: '项ç®é¶æ®µ', |
| | | prop: 'projectPhase', |
| | | label: "项ç®é¶æ®µ", |
| | | prop: "projectPhase", |
| | | width: 100, |
| | | formatData: (val) => projectPhaseOptions.find(item => String(item.value) === String(val))?.label || '--', |
| | | formatData: (val) => |
| | | projectPhaseOptions.find((item) => String(item.value) === String(val)) |
| | | ?.label || "--", |
| | | }, |
| | | { |
| | | label: 'å
³èåå·', |
| | | prop: 'purchaseContractNumber', |
| | | label: "å
³èçéè´è®¢åå·", |
| | | prop: "purchaseContractNumber", |
| | | width: 160, |
| | | }, |
| | | { |
| | | label: 'å¶ä½æ¥æ', |
| | | prop: 'preparedAt', |
| | | label: "å¶ä½æ¥æ", |
| | | prop: "preparedAt", |
| | | width: 130, |
| | | }, |
| | | { |
| | | label: 'å¶å人', |
| | | prop: 'preparedUserName', |
| | | label: "å¶å人", |
| | | prop: "preparedUserName", |
| | | width: 110, |
| | | }, |
| | | { |
| | | label: 'éæäºº', |
| | | prop: 'returnUserName', |
| | | label: "éæäºº", |
| | | prop: "returnUserName", |
| | | width: 110, |
| | | }, |
| | | |
| | | { |
| | | label: 'æ´åææ£é¢', |
| | | prop: 'totalDiscountAmount', |
| | | label: "æ´åææ£é¢", |
| | | prop: "totalDiscountAmount", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: 'æ´åææ£ç', |
| | | prop: 'totalDiscountRate', |
| | | label: "æ´åææ£ç", |
| | | prop: "totalDiscountRate", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: 'æäº¤éé¢', |
| | | prop: 'totalAmount', |
| | | label: "æäº¤éé¢", |
| | | prop: "totalAmount", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: 'å建人', |
| | | prop: 'createUserName', |
| | | label: "å建人", |
| | | prop: "createUserName", |
| | | width: 110, |
| | | }, |
| | | { |
| | | label: 'å建æ¶é´', |
| | | prop: 'createTime', |
| | | label: "å建æ¶é´", |
| | | prop: "createTime", |
| | | width: 170, |
| | | }, |
| | | { |
| | | label: 'æè¿æ´æ°æ¶é´', |
| | | prop: 'updateTime', |
| | | label: "æè¿æ´æ°æ¶é´", |
| | | prop: "updateTime", |
| | | width: 170, |
| | | }, |
| | | { |
| | | label: '夿³¨', |
| | | prop: 'remark', |
| | | label: "夿³¨", |
| | | prop: "remark", |
| | | width: 180, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | width: 120, |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | operation: [ |
| | | { |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: row => {handleDetail(row);}, |
| | | }, |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | handleDetail(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å é¤", |
| | | clickFun: row => {handleDelete(row)}, |
| | | clickFun: (row) => { |
| | | handleDelete(row); |
| | | }, |
| | | }, |
| | | ], |
| | | ], |
| | | }, |
| | | |
| | | ]) |
| | | ]); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | no: '', |
| | | } |
| | | }) |
| | | const { searchForm } = toRefs(data) |
| | | no: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getList() |
| | | } |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // å é¤æä½ |
| | | const handleDelete = (row) => { |
| | | console.log('å é¤è¡æ°æ®ï¼', row) |
| | | proxy?.$modal?.confirm('ç¡®å®è¦å é¤åï¼å é¤å°æ æ³æ¢å¤').then(() => { |
| | | // è¿éè°ç¨å 餿¥å£ï¼ä¼ å
¥ row.id |
| | | deletePurchaseReturnOrder(row.id).then(() => { |
| | | proxy?.$modal?.msgSuccess?.("å 餿å"); |
| | | getList() |
| | | }).catch(() => { |
| | | proxy?.$modal?.msgError?.('å é¤å¤±è´¥') |
| | | console.log("å é¤è¡æ°æ®ï¼", row); |
| | | proxy?.$modal |
| | | ?.confirm("ç¡®å®è¦å é¤åï¼å é¤å°æ æ³æ¢å¤") |
| | | .then(() => { |
| | | // è¿éè°ç¨å 餿¥å£ï¼ä¼ å
¥ row.id |
| | | deletePurchaseReturnOrder(row.id) |
| | | .then(() => { |
| | | proxy?.$modal?.msgSuccess?.("å 餿å"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy?.$modal?.msgError?.("å é¤å¤±è´¥"); |
| | | }); |
| | | }) |
| | | }).catch(() => { |
| | | // åæ¶å é¤ |
| | | proxy?.$modal?.msgInfo?.('已忶å é¤') |
| | | |
| | | }) |
| | | } |
| | | .catch(() => { |
| | | // åæ¶å é¤ |
| | | proxy?.$modal?.msgInfo?.("已忶å é¤"); |
| | | }); |
| | | }; |
| | | // æ¥ç详æ
|
| | | const handleDetail = (row) => { |
| | | if (!row?.id) { |
| | | proxy?.$modal?.msgWarning?.('æªè·åå°åæ®ID') |
| | | return |
| | | proxy?.$modal?.msgWarning?.("æªè·åå°åæ®ID"); |
| | | return; |
| | | } |
| | | detailVisible.value = true |
| | | detailLoading.value = true |
| | | getPurchaseReturnOrderDetail(row.id).then(res => { |
| | | const payload = res?.data || {} |
| | | detailData.value = payload |
| | | // æ¼æ¥è¿ä¸ªå¯¹è±¡æä¸ä¸ªå¯¹è±¡ï¼æ¹ä¾¿å±ç¤º item å item.salesLedgerProduct éçåæ®µ |
| | | detailVisible.value = true; |
| | | detailLoading.value = true; |
| | | getPurchaseReturnOrderDetail(row.id) |
| | | .then((res) => { |
| | | const payload = res?.data || {}; |
| | | detailData.value = payload; |
| | | // æ¼æ¥è¿ä¸ªå¯¹è±¡æä¸ä¸ªå¯¹è±¡ï¼æ¹ä¾¿å±ç¤º item å item.salesLedgerProduct éçåæ®µ |
| | | |
| | | |
| | | detailProducts.value = |
| | | payload.purchaseReturnOrderProductsDetailVoList.map(item => ({ ...item, ...item.salesLedgerProduct })) || |
| | | [] |
| | | }).catch(() => { |
| | | proxy?.$modal?.msgError?.('è·å详æ
失败') |
| | | }).finally(() => { |
| | | detailLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | detailProducts.value = |
| | | payload.purchaseReturnOrderProductsDetailVoList.map((item) => ({ |
| | | ...item, |
| | | ...item.salesLedgerProduct, |
| | | })) || []; |
| | | }) |
| | | .catch(() => { |
| | | proxy?.$modal?.msgError?.("è·å详æ
失败"); |
| | | }) |
| | | .finally(() => { |
| | | detailLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList() |
| | | } |
| | | getList(); |
| | | }; |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true |
| | | findPurchaseReturnOrderListPage({ ...searchForm.value, ...page }).then(res => { |
| | | tableLoading.value = false |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total |
| | | }).catch(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | tableLoading.value = true; |
| | | findPurchaseReturnOrderListPage({ ...searchForm.value, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter(item => item.id); |
| | | } |
| | | selectedRows.value = selection.filter((item) => item.id); |
| | | }; |
| | | |
| | | const getReturnTypeLabel = (value) => { |
| | | return returnTypeOptions.find(item => String(item.value) === String(value))?.label || '--' |
| | | } |
| | | return ( |
| | | returnTypeOptions.find((item) => String(item.value) === String(value)) |
| | | ?.label || "--" |
| | | ); |
| | | }; |
| | | |
| | | const getProjectPhaseLabel = (value) => { |
| | | return projectPhaseOptions.find(item => String(item.value) === String(value))?.label || '--' |
| | | } |
| | | return ( |
| | | projectPhaseOptions.find((item) => String(item.value) === String(value)) |
| | | ?.label || "--" |
| | | ); |
| | | }; |
| | | |
| | | const formatAmount = (value) => { |
| | | if (value === null || value === undefined || value === '') { |
| | | return '--' |
| | | if (value === null || value === undefined || value === "") { |
| | | return "--"; |
| | | } |
| | | const num = Number(value) |
| | | const num = Number(value); |
| | | if (Number.isNaN(num)) { |
| | | return value |
| | | return value; |
| | | } |
| | | return num.toFixed(2) |
| | | } |
| | | return num.toFixed(2); |
| | | }; |
| | | |
| | | /** å·²éè´§æ°é = å
¥åºè¡æ»æ°é â å½åå¯éè´§æ°éï¼å©ä½ï¼ */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0); |
| | | const un = Number(row?.unQuantity ?? 0); |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0; |
| | | return Math.max(total - un, 0); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | getList(); |
| | | }); |
| | | </script> |
| | | <style scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | margin-top: unset; |
| | | } |
| | | </style> |
| | | |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢è¿ç¨æ£éª' : 'ç¼è¾è¿ç¨æ£éª'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢è¿ç¨æ£éª' : 'ç¼è¾è¿ç¨æ£éª'" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å·¥åºï¼" prop="process"> |
| | | <el-select v-model="form.process" placeholder="è¯·éæ©å·¥åº" clearable :disabled="processQuantityDisabled" style="width: 100%"> |
| | | <el-option v-for="item in processList" :key="item.name" :label="item.name" :value="item.name"/> |
| | | <el-form-item label="å·¥åºï¼" |
| | | prop="process"> |
| | | <el-select v-model="form.process" |
| | | placeholder="è¯·éæ©å·¥åº" |
| | | clearable |
| | | :disabled="processQuantityDisabled" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in processList" |
| | | :key="item.name" |
| | | :label="item.name" |
| | | :value="item.name" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产ååç§°ï¼" prop="productId"> |
| | | <el-tree-select |
| | | v-model="form.productId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="operationType === 'edit'" |
| | | style="width: 100%" |
| | | /> |
| | | <el-form-item label="产ååç§°ï¼" |
| | | prop="productId"> |
| | | <el-tree-select v-model="form.productId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="operationType === 'edit'" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="productModelId"> |
| | | <el-select v-model="form.productModelId" placeholder="è¯·éæ©" clearable :disabled="operationType === 'edit'" |
| | | filterable readonly @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | <el-form-item label="è§æ ¼åå·ï¼" |
| | | prop="productModelId"> |
| | | <el-select v-model="form.productModelId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | filterable |
| | | readonly |
| | | @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææ éæ©ï¼" prop="testStandardId"> |
| | | <el-select |
| | | v-model="form.testStandardId" |
| | | placeholder="è¯·éæ©ææ " |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" |
| | | /> |
| | | <el-form-item label="ææ éæ©ï¼" |
| | | prop="testStandardId"> |
| | | <el-select v-model="form.testStandardId" |
| | | placeholder="è¯·éæ©ææ " |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input v-model="form.unit" placeholder="请è¾å
¥" disabled/> |
| | | <el-form-item label="åä½ï¼" |
| | | prop="unit"> |
| | | <el-input v-model="form.unit" |
| | | placeholder="请è¾å
¥" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ°éï¼" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请è¾å
¥" clearable :precision="2" :disabled="processQuantityDisabled"/> |
| | | <el-form-item label="æ°éï¼" |
| | | prop="quantity"> |
| | | <el-input-number :step="0.01" |
| | | :min="0" |
| | | style="width: 100%" |
| | | v-model="form.quantity" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :precision="2" |
| | | :disabled="processQuantityDisabled" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµåä½ï¼" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请è¾å
¥" clearable/> |
| | | <el-form-item label="æ£æµåä½ï¼" |
| | | prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµç»æï¼" prop="checkResult"> |
| | | <el-form-item label="æ£æµç»æï¼" |
| | | prop="checkResult"> |
| | | <el-select v-model="form.checkResult"> |
| | | <el-option label="åæ ¼" value="åæ ¼" /> |
| | | <el-option label="ä¸åæ ¼" value="ä¸åæ ¼" /> |
| | | <el-option label="åæ ¼" |
| | | value="åæ ¼" /> |
| | | <el-option label="ä¸åæ ¼" |
| | | value="ä¸åæ ¼" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£éªåï¼" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="è¯·éæ©" clearable> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" |
| | | :value="item.nickName"/> |
| | | </el-select> |
| | | <el-form-item label="æ£éªåï¼" |
| | | prop="checkName"> |
| | | <el-select v-model="form.checkName" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.nickName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµæ¥æï¼" prop="checkTime"> |
| | | <el-date-picker |
| | | v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | <el-form-item label="æ£æµæ¥æï¼" |
| | | prop="checkTime"> |
| | | <el-date-picker v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400" |
| | | > |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" clearable/> |
| | | </template> |
| | | </PIMTable> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400"> |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" |
| | | clearable /> |
| | | </template> |
| | | </PIMTable> |
| | | <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="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, toRefs, computed, getCurrentInstance, nextTick} from "vue"; |
| | | import {getOptions} from "@/api/procurementManagement/procurementLedger.js"; |
| | | import {modelList, productTreeList} from "@/api/basicData/product.js"; |
| | | import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import {userListNoPage} from "@/api/system/user.js"; |
| | | import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | import { list } from "@/api/productionManagement/productionProcess"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | computed, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { |
| | | qualityInspectAdd, |
| | | qualityInspectUpdate, |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import { |
| | | qualityInspectDetailByProductId, |
| | | getQualityTestStandardParamByTestStandardId, |
| | | } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { qualityInspectParamInfo } from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | import { list } from "@/api/productionManagement/productionProcess"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | |
| | | |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" },], |
| | | process: [{ required: true, message: "è¯·éæ©å·¥åº", trigger: "change" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | testStandardId: [{required: false, message: "è¯·éæ©ææ ", trigger: "change"}], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请è¾å
¥", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const userList = ref([]); |
| | | const { form, rules } = toRefs(data); |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åå·¥åºãæ°éç½®ç° |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | const processList = ref([]); // å·¥åºä¸æå表ï¼å·¥åºåç§° nameï¼ |
| | | const supplierList = ref([]); |
| | | const productOptions = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ææ ", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "æ åå¼", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "å
æ§å¼", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "æ£éªå¼", |
| | | prop: "testValue", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const currentProductId = ref(0); |
| | | const testStandardOptions = ref([]); // ææ éæ©ä¸ææ¡æ°æ® |
| | | const modelOptions = ref([]); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type; |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }); |
| | | // å 载工åºä¸æå表 |
| | | try { |
| | | const res = await list(); |
| | | processList.value = res.data || []; |
| | | } catch (e) { |
| | | console.error("å 载工åºå表失败", e); |
| | | processList.value = []; |
| | | } |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | // å
éç½®è¡¨åæ°æ®ï¼ä¿æåæ®µå®æ´ï¼é¿å
å¼¹çªé¦æ¬¡æ¸²ææ¶è§¦åå¿
填红æ¡âéªä¸ä¸âï¼ |
| | | form.value = { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | } |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | // å
ç¡®ä¿äº§åæ å·²å è½½ï¼å¦åç¼è¾æ¶äº§å/è§æ ¼åå·æ æ³åæ¾ |
| | | await getProductOptions(); |
| | | if (operationType.value === 'edit') { |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // å
è®¾ç½®è¡¨åæ°æ®ï¼ä½ææ¶æ¸
空 testStandardIdï¼çé项å è½½å®æåå设置 |
| | | form.value = {...row, testStandardId: ''} |
| | | currentProductId.value = row.productId || 0 |
| | | // å
³é®ï¼ç¼è¾æ¶å è½½è§æ ¼åå·ä¸æéé¡¹ï¼æè½åæ¾ productModelId |
| | | if (currentProductId.value) { |
| | | try { |
| | | const res = await modelList({ id: currentProductId.value }); |
| | | modelOptions.value = res || []; |
| | | // 忥åå¡« model / unitï¼æäºæ¥å£è¿åç row éå¯è½æ²¡å¸¦å
¨ï¼ |
| | | if (form.value.productModelId) { |
| | | handleChangeModel(form.value.productModelId); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½è§æ ¼åå·å¤±è´¥", e); |
| | | modelOptions.value = []; |
| | | } |
| | | } |
| | | // ç¼è¾æ¨¡å¼ä¸ï¼å
å è½½ææ é项ï¼ç¶åå è½½åæ°å表 |
| | | if (currentProductId.value) { |
| | | // å
å è½½ææ é项 |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 1, |
| | | process: form.value.process || '' |
| | | } |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | testStandardOptions.value = res.data || []; |
| | | // ä½¿ç¨ nextTick å setTimeout ç¡®ä¿éé¡¹å·²ç»æ¸²æå° DOM |
| | | nextTick(() => { |
| | | setTimeout(() => { |
| | | // 妿ç¼è¾æ°æ®ä¸æ testStandardIdï¼å设置并å 载对åºçåæ° |
| | | if (savedTestStandardId) { |
| | | // ç¡®ä¿ç±»åå¹é
ï¼item.id å¯è½æ¯æ°åæåç¬¦ä¸²ï¼ |
| | | const matchedOption = testStandardOptions.value.find(item => |
| | | item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // ç¡®ä¿ä½¿ç¨å¹é
项ç idï¼ä¿æç±»åä¸è´ï¼ |
| | | form.value.testStandardId = matchedOption.id; |
| | | // ç¼è¾ä¿ç忣éªå¼ï¼ç´æ¥æåååæ°æ°æ® |
| | | getQualityInspectParamList(row.id); |
| | | } else { |
| | | // 妿æ¾ä¸å°å¹é
项ï¼å°è¯ç´æ¥ä½¿ç¨åå¼ |
| | | console.warn('æªæ¾å°å¹é
çææ é项ï¼testStandardId:', savedTestStandardId, 'å¯ç¨é项:', testStandardOptions.value); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } else { |
| | | // å¦åä½¿ç¨æ§çé»è¾ |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | }, 100); |
| | | }); |
| | | }); |
| | | } else { |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } |
| | | // æååæå¼å¼¹çªï¼å¹¶æ¸
çæ ¡éªæï¼é¿å
å¿
å¡«æç¤ºéªç |
| | | dialogFormVisible.value = true; |
| | | nextTick(() => { |
| | | proxy.$refs?.formRef?.clearValidate?.(); |
| | | }); |
| | | } |
| | | const getProductOptions = () => { |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return productOptions.value; |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | process: [{ required: true, message: "è¯·éæ©å·¥åº", trigger: "change" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | testStandardId: [ |
| | | { required: false, message: "è¯·éæ©ææ ", trigger: "change" }, |
| | | ], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请è¾å
¥", trigger: "change" }], |
| | | }, |
| | | }); |
| | | }; |
| | | const getModels = (value) => { |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }) |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | const userList = ref([]); |
| | | const { form, rules } = toRefs(data); |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åå·¥åºãæ°éç½®ç° |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | const processList = ref([]); // å·¥åºä¸æå表ï¼å·¥åºåç§° nameï¼ |
| | | const supplierList = ref([]); |
| | | const productOptions = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ææ ", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "æ åå¼", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "å
æ§å¼", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "æ£éªå¼", |
| | | prop: "testValue", |
| | | dataType: "slot", |
| | | slot: "slot", |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const currentProductId = ref(0); |
| | | const testStandardOptions = ref([]); // ææ éæ©ä¸ææ¡æ°æ® |
| | | const modelOptions = ref([]); |
| | | |
| | | const handleChangeModel = (value) => { |
| | | form.value.model = modelOptions.value.find(item => item.id == value)?.model || ''; |
| | | form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || ''; |
| | | } |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type; |
| | | getOptions().then(res => { |
| | | supplierList.value = res.data; |
| | | }); |
| | | // å 载工åºä¸æå表 |
| | | try { |
| | | const res = await list({ size: -1, current: -1 }); |
| | | processList.value = res.data.records || []; |
| | | } catch (e) { |
| | | console.error("å 载工åºå表失败", e); |
| | | processList.value = []; |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | // å
éç½®è¡¨åæ°æ®ï¼ä¿æåæ®µå®æ´ï¼é¿å
å¼¹çªé¦æ¬¡æ¸²ææ¶è§¦åå¿
填红æ¡âéªä¸ä¸âï¼ |
| | | form.value = { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // å·¥åºååå¤ç |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | form.value.inspectType = 1 |
| | | const processName = form.value.process || ''; |
| | | if (operationType.value === "add") { |
| | | tableData.value.forEach((item) => { |
| | | delete item.id |
| | | }) |
| | | } |
| | | const data = { |
| | | ...form.value, |
| | | process: processName, // ä¿ç process åæ®µä»¥å
¼å®¹å端 |
| | | qualityInspectParams: tableData.value |
| | | } |
| | | if (operationType.value === "add") { |
| | | qualityInspectAdd(data).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | // å
ç¡®ä¿äº§åæ å·²å è½½ï¼å¦åç¼è¾æ¶äº§å/è§æ ¼åå·æ æ³åæ¾ |
| | | await getProductOptions(); |
| | | if (operationType.value === "edit") { |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // å
è®¾ç½®è¡¨åæ°æ®ï¼ä½ææ¶æ¸
空 testStandardIdï¼çé项å è½½å®æåå设置 |
| | | form.value = { ...row, testStandardId: "" }; |
| | | currentProductId.value = row.productId || 0; |
| | | // å
³é®ï¼ç¼è¾æ¶å è½½è§æ ¼åå·ä¸æéé¡¹ï¼æè½åæ¾ productModelId |
| | | if (currentProductId.value) { |
| | | try { |
| | | const res = await modelList({ id: currentProductId.value }); |
| | | modelOptions.value = res || []; |
| | | // 忥åå¡« model / unitï¼æäºæ¥å£è¿åç row éå¯è½æ²¡å¸¦å
¨ï¼ |
| | | if (form.value.productModelId) { |
| | | handleChangeModel(form.value.productModelId); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½è§æ ¼åå·å¤±è´¥", e); |
| | | modelOptions.value = []; |
| | | } |
| | | } |
| | | // ç¼è¾æ¨¡å¼ä¸ï¼å
å è½½ææ é项ï¼ç¶åå è½½åæ°å表 |
| | | if (currentProductId.value) { |
| | | // å
å è½½ææ é项 |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 1, |
| | | process: form.value.process || "", |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | testStandardOptions.value = res.data || []; |
| | | // ä½¿ç¨ nextTick å setTimeout ç¡®ä¿éé¡¹å·²ç»æ¸²æå° DOM |
| | | nextTick(() => { |
| | | setTimeout(() => { |
| | | // 妿ç¼è¾æ°æ®ä¸æ testStandardIdï¼å设置并å 载对åºçåæ° |
| | | if (savedTestStandardId) { |
| | | // ç¡®ä¿ç±»åå¹é
ï¼item.id å¯è½æ¯æ°åæåç¬¦ä¸²ï¼ |
| | | const matchedOption = testStandardOptions.value.find( |
| | | item => |
| | | item.id == savedTestStandardId || |
| | | String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // ç¡®ä¿ä½¿ç¨å¹é
项ç idï¼ä¿æç±»åä¸è´ï¼ |
| | | form.value.testStandardId = matchedOption.id; |
| | | // ç¼è¾ä¿ç忣éªå¼ï¼ç´æ¥æåååæ°æ°æ® |
| | | getQualityInspectParamList(row.id); |
| | | } else { |
| | | // 妿æ¾ä¸å°å¹é
项ï¼å°è¯ç´æ¥ä½¿ç¨åå¼ |
| | | console.warn( |
| | | "æªæ¾å°å¹é
çææ é项ï¼testStandardId:", |
| | | savedTestStandardId, |
| | | "å¯ç¨é项:", |
| | | testStandardOptions.value |
| | | ); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } else { |
| | | // å¦åä½¿ç¨æ§çé»è¾ |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | }, 100); |
| | | }); |
| | | }); |
| | | } else { |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | const processName = form.value.process || ''; |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 1, |
| | | process: processName |
| | | } |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // ä¿å䏿æ¡éé¡¹æ°æ® |
| | | testStandardOptions.value = res.data || []; |
| | | // æ¸
ç©ºè¡¨æ ¼æ°æ®ï¼çå¾
ç¨æ·éæ©ææ |
| | | tableData.value = []; |
| | | // æ¸
ç©ºææ éæ© |
| | | form.value.testStandardId = ''; |
| | | }) |
| | | } |
| | | // æååæå¼å¼¹çªï¼å¹¶æ¸
çæ ¡éªæï¼é¿å
å¿
å¡«æç¤ºéªç |
| | | dialogFormVisible.value = true; |
| | | nextTick(() => { |
| | | proxy.$refs?.formRef?.clearValidate?.(); |
| | | }); |
| | | }; |
| | | const getProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return productOptions.value; |
| | | }); |
| | | }; |
| | | const getModels = value => { |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value; |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res; |
| | | }); |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = (testStandardId) => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId).then(res => { |
| | | tableData.value = res.data || []; |
| | | }).catch(error => { |
| | | console.error('è·åæ ååæ°å¤±è´¥:', error); |
| | | tableData.value = []; |
| | | }).finally(() => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | const getQualityInspectParamList = (id) => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }) |
| | | } |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | tableData.value = []; |
| | | testStandardOptions.value = []; |
| | | form.value.testStandardId = ''; |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | const handleChangeModel = value => { |
| | | form.value.model = |
| | | modelOptions.value.find(item => item.id == value)?.model || ""; |
| | | form.value.unit = |
| | | modelOptions.value.find(item => item.id == value)?.unit || ""; |
| | | }; |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // å·¥åºååå¤ç |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | form.value.inspectType = 1; |
| | | const processName = form.value.process || ""; |
| | | if (operationType.value === "add") { |
| | | tableData.value.forEach(item => { |
| | | delete item.id; |
| | | }); |
| | | } |
| | | const data = { |
| | | ...form.value, |
| | | process: processName, // ä¿ç process åæ®µä»¥å
¼å®¹å端 |
| | | qualityInspectParams: tableData.value, |
| | | }; |
| | | if (operationType.value === "add") { |
| | | qualityInspectAdd(data).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | } else { |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | const processName = form.value.process || ""; |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 1, |
| | | process: processName, |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // ä¿å䏿æ¡éé¡¹æ°æ® |
| | | testStandardOptions.value = res.data || []; |
| | | // æ¸
ç©ºè¡¨æ ¼æ°æ®ï¼çå¾
ç¨æ·éæ©ææ |
| | | tableData.value = []; |
| | | // æ¸
ç©ºææ éæ© |
| | | form.value.testStandardId = ""; |
| | | }); |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = testStandardId => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | tableData.value = res.data || []; |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ ååæ°å¤±è´¥:", error); |
| | | tableData.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | const getQualityInspectParamList = id => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | tableData.value = []; |
| | | testStandardOptions.value = []; |
| | | form.value.testStandardId = ""; |
| | | dialogFormVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åè´§æ°é" |
| | | prop="totalQuantity" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å货车çå·" |
| | | prop="shippingCarNumber" |
| | | show-overflow-tooltip |
| | |
| | | label="å®¡æ ¸ç¶æ" |
| | | prop="status" |
| | | align="center" |
| | | width="120" |
| | | width="100" |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag :type="getApprovalStatusType(scope.row.status)"> |
| | |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åºåºåå·" |
| | | prop="outboundBatches" |
| | | show-overflow-tooltip |
| | | width="130" |
| | | /> |
| | | <el-table-column fixed="right" label="æä½" width="220" align="center"> |
| | | <template #default="scope"> |
| | | <!-- <el-button--> |
| | |
| | | <el-descriptions-item label="å¿«éåå·" :span="2">{{ |
| | | detailRow.expressNumber || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="åºåºåå·" :span="2">{{ |
| | | detailRow.outboundBatches || "--" |
| | | }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-table |
| | | :data="getDeliveryProductInfoList()" |
| | |
| | | <el-descriptions-item label="客æ·åç§°">{{ detail.customerName }}</el-descriptions-item> |
| | | <el-descriptions-item label="éå®åå·">{{ detail.salesContractNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¸å¡å">{{ detail.salesman }}</el-descriptions-item> |
| | | <el-descriptions-item label="å
³èåºåºåå·">{{ detail.shippingNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="项ç®åç§°">{{ detail.projectName }}</el-descriptions-item> |
| | | <el-descriptions-item label="å
³èåè´§åå·">{{ detail.shippingNo }}</el-descriptions-item> |
| | | <!-- <el-descriptions-item label="项ç®åç§°">{{ detail.projectName }}</el-descriptions-item> --> |
| | | <el-descriptions-item label="å¶å人">{{ detail.maker }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¶åæ¶é´">{{ detail.makeTime }}</el-descriptions-item> |
| | | <el-descriptions-item label="éè´§åå ">{{ detail.returnReason }}</el-descriptions-item> |
| | |
| | | |
| | | <div style="padding-top: 20px"> |
| | | <span class="descriptions">产åå表</span> |
| | | <PIMTable :isShowPagination="false" rowKey="id" :column="tableColumn" :tableData="tableData" /> |
| | | <PIMTable :isShowPagination="false" rowKey="id" :column="tableColumn" :tableData="tableData"> |
| | | <template #totalReturnNum="{ row }"> |
| | | {{ calcAlreadyReturned(row) }} |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | |
| | | const tableData = ref([]); |
| | | const availableProducts = ref([]); |
| | | |
| | | const sameKey = (a, b) => a != null && b != null && String(a) === String(b); |
| | | |
| | | /** ä¸ formDia ä¸è´ï¼ä¸¤ä»½å表æ id åå¹¶ï¼é¿å
åªå productDtoData æ¶ç¼ºåºåºåå·/æ¹æ¬¡/æ°é */ |
| | | const mergeShippingProductLists = (data) => { |
| | | const lists = [data?.shippingProductVoList, data?.productDtoData].filter(Array.isArray); |
| | | if (!lists.length) return []; |
| | | const map = new Map(); |
| | | for (const list of lists) { |
| | | for (const p of list) { |
| | | if (p == null) continue; |
| | | const key = p.id != null ? String(p.id) : null; |
| | | if (!key) continue; |
| | | const prev = map.get(key); |
| | | map.set(key, prev ? { ...prev, ...p } : { ...p }); |
| | | } |
| | | } |
| | | return Array.from(map.values()); |
| | | }; |
| | | |
| | | const pickShippingLine = (normalized) => { |
| | | const pid = normalized?.returnSaleLedgerProductId ?? normalized?.id; |
| | | const sid = normalized?.stockOutRecordId ?? normalized?.shippingProductId; |
| | | const direct = availableProducts.value.find( |
| | | (p) => |
| | | sameKey(p?.id, pid) || |
| | | sameKey(p?.stockOutRecordId, pid) || |
| | | sameKey(p?.id, sid) || |
| | | sameKey(p?.stockOutRecordId, sid) |
| | | ); |
| | | if (direct) return direct; |
| | | const pmid = normalized?.productModelId; |
| | | if (pmid == null || pmid === "") return undefined; |
| | | const candidates = availableProducts.value.filter((p) => sameKey(p?.productModelId, pmid)); |
| | | if (!candidates.length) return undefined; |
| | | if (candidates.length === 1) return candidates[0]; |
| | | const spec = String(normalized?.specificationModel ?? normalized?.model ?? ""); |
| | | if (spec) { |
| | | const hit = candidates.find((p) => { |
| | | const ps = String(p?.specificationModel ?? p?.model ?? ""); |
| | | return ps && ps === spec; |
| | | }); |
| | | if (hit) return hit; |
| | | } |
| | | return candidates[0]; |
| | | }; |
| | | |
| | | const isEmptyText = (v) => v === "" || v == null || v === undefined; |
| | | |
| | | const firstFiniteNumber = (...vals) => { |
| | | for (const v of vals) { |
| | | if (v === "" || v == null || v === undefined) continue; |
| | | const n = Number(v); |
| | | if (Number.isFinite(n)) return n; |
| | | } |
| | | return undefined; |
| | | }; |
| | | |
| | | const firstNonEmptyText = (...vals) => { |
| | | const hit = vals.find((v) => !isEmptyText(v)); |
| | | return hit === undefined ? "" : hit; |
| | | }; |
| | | |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockOutNum ?? row?.totalQuantity ?? row?.totalReturnNum ?? 0); |
| | | const un = Number(row?.unQuantity ?? 0); |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0; |
| | | return Math.max(total - un, 0); |
| | | }; |
| | | |
| | | /** 详æ
è¡¨ç¨ productName / modelï¼åå¹¶æ¶å¿è®©ç©ºä¸²çæåºåºè¡å段 */ |
| | | const mergeDetailProductRow = (product, normalized) => { |
| | | const row = { ...product, ...normalized }; |
| | | row.outboundBatches = firstNonEmptyText( |
| | | row.outboundBatches, |
| | | product?.outboundBatches, |
| | | product?.shippingNo, |
| | | product?.outboundNo, |
| | | normalized?.outboundBatches, |
| | | normalized?.outboundNo, |
| | | normalized?.shippingNo |
| | | ); |
| | | row.batchNo = firstNonEmptyText( |
| | | row.batchNo, |
| | | product?.batchNo, |
| | | product?.batchNumber, |
| | | product?.lotNo, |
| | | product?.batchCode, |
| | | product?.shippingBatchNo, |
| | | normalized?.batchNo, |
| | | normalized?.batchNumber, |
| | | normalized?.lotNo, |
| | | normalized?.shippingBatchNo |
| | | ); |
| | | const stock = firstFiniteNumber( |
| | | row.stockOutNum, |
| | | product?.stockOutNum, |
| | | product?.totalQuantity, |
| | | product?.shippingQuantity, |
| | | product?.deliveryQuantity, |
| | | product?.quantity, |
| | | product?.outQuantity, |
| | | normalized?.stockOutNum, |
| | | normalized?.totalQuantity, |
| | | normalized?.shippingQuantity, |
| | | normalized?.deliveryQuantity |
| | | ); |
| | | if (stock !== undefined) row.stockOutNum = stock; |
| | | const un = firstFiniteNumber( |
| | | row.unQuantity, |
| | | product?.unQuantity, |
| | | product?.remainingQuantity, |
| | | product?.noReturnQuantity, |
| | | product?.canReturnQuantity, |
| | | product?.availableReturnNum, |
| | | normalized?.unQuantity, |
| | | normalized?.remainingQuantity, |
| | | normalized?.noReturnQuantity, |
| | | normalized?.canReturnQuantity |
| | | ); |
| | | if (un !== undefined) row.unQuantity = un; |
| | | else { |
| | | const s = Number(row.stockOutNum); |
| | | const ret = Number(row.totalReturnNum ?? 0); |
| | | if (Number.isFinite(s) && s >= 0 && Number.isFinite(ret) && ret >= 0) { |
| | | row.unQuantity = Math.max(0, s - ret); |
| | | } |
| | | } |
| | | const returned = firstFiniteNumber( |
| | | row.totalReturnNum, |
| | | product?.totalReturnNum, |
| | | product?.totalReturnedNum, |
| | | normalized?.totalReturnNum, |
| | | normalized?.totalReturnedNum |
| | | ); |
| | | if (returned !== undefined) row.totalReturnNum = returned; |
| | | else if (isEmptyText(row.totalReturnNum)) row.totalReturnNum = 0; |
| | | if (isEmptyText(row.unit)) { |
| | | row.unit = firstNonEmptyText(product?.unit, normalized?.unit); |
| | | } |
| | | row.productName = firstNonEmptyText( |
| | | row.productName, |
| | | normalized?.productName, |
| | | normalized?.productCategory, |
| | | product?.productName, |
| | | product?.productCategory |
| | | ); |
| | | row.model = firstNonEmptyText( |
| | | row.model, |
| | | normalized?.model, |
| | | normalized?.specificationModel, |
| | | product?.model, |
| | | product?.specificationModel |
| | | ); |
| | | return row; |
| | | }; |
| | | |
| | | const normalizeDetailRow = (raw) => { |
| | | const ledgerId = |
| | | raw?.returnSaleLedgerProductId ?? |
| | | raw?.saleLedgerProductId ?? |
| | | raw?.stockOutRecordId ?? |
| | | raw?.shippingProductId; |
| | | const productId = ledgerId ?? raw?.id; |
| | | const num = Number(raw?.num ?? raw?.returnQuantity ?? 0); |
| | | return { |
| | | ...raw, |
| | | id: productId, |
| | | returnSaleLedgerProductId: productId, |
| | | productModelId: raw?.productModelId, |
| | | stockOutRecordId: raw?.stockOutRecordId, |
| | | shippingProductId: raw?.shippingProductId, |
| | | productName: raw?.productName ?? raw?.productCategory ?? raw?.productTypeName ?? "", |
| | | model: raw?.model ?? raw?.specificationModel ?? raw?.specModel ?? "", |
| | | outboundBatches: raw?.outboundBatches ?? raw?.outboundNo ?? raw?.shippingNo, |
| | | batchNo: |
| | | raw?.batchNo ?? |
| | | raw?.batchNumber ?? |
| | | raw?.lotNo ?? |
| | | raw?.batchCode ?? |
| | | raw?.shippingBatchNo, |
| | | stockOutNum: |
| | | raw?.stockOutNum ?? |
| | | raw?.totalQuantity ?? |
| | | raw?.shippingQuantity ?? |
| | | raw?.deliveryQuantity ?? |
| | | raw?.quantity, |
| | | totalReturnNum: raw?.totalReturnNum ?? raw?.totalReturnedNum, |
| | | unQuantity: |
| | | raw?.unQuantity ?? |
| | | raw?.remainingQuantity ?? |
| | | raw?.noReturnQuantity ?? |
| | | raw?.canReturnQuantity, |
| | | returnQuantity: Number.isFinite(num) ? num : 0, |
| | | price: Number(raw?.taxInclusiveUnitPrice ?? raw?.price ?? 0), |
| | | amount: Number(raw?.amount ?? 0).toFixed(2), |
| | | isQuality: raw?.isQuality ?? 2, |
| | | remark: raw?.remark ?? "", |
| | | }; |
| | | }; |
| | | |
| | | const tableColumn = [ |
| | | {align: "center", label: "产å大类", prop: "productCategory"}, |
| | | {align: "center", label: "è§æ ¼åå·", prop: "specificationModel"}, |
| | | {align: "center", label: "åºåºåå·", prop: "outboundBatches"}, |
| | | {align: "center", label: "æ¹æ¬¡å·", prop: "batchNo"}, |
| | | {align: "center", label: "产å大类", prop: "productName"}, |
| | | {align: "center", label: "è§æ ¼åå·", prop: "model"}, |
| | | {align: "center", label: "åä½", prop: "unit", width: 80}, |
| | | {align: "center", label: "æ»æ°é", prop: "quantity", width: 120}, |
| | | {align: "center", label: "å·²éè´§æ°é", prop: "totalReturnNum", width: 120}, |
| | | {align: "center", label: "æ»æ°é", prop: "stockOutNum", width: 120}, |
| | | {align: "center", label: "å·²éè´§æ°é", prop: "totalReturnNum", width: 120, dataType: "slot", slot: "totalReturnNum"}, |
| | | {align: "center", label: "æªéè´§æ°é", prop: "unQuantity", width: 120}, |
| | | {align: "center", label: "éè´§æ°é", prop: "returnQuantity", width: 120}, |
| | | {align: "center", label: "é货产ååä»·", prop: "price", width: 120}, |
| | |
| | | if (detail.value.shippingId) { |
| | | const productRes = await returnManagementGetByShippingId({ shippingId: detail.value.shippingId }); |
| | | if (productRes.code === 200) { |
| | | availableProducts.value = productRes.data.productDtoData || []; |
| | | availableProducts.value = mergeShippingProductLists(productRes.data); |
| | | } |
| | | } |
| | | |
| | | |
| | | const list = |
| | | detail.value?.returnSaleProducts || |
| | | detail.value?.returnSaleProductList || |
| | | detail.value?.returnSaleProductDtoData || |
| | | []; |
| | | |
| | | tableData.value = Array.isArray(list) ? list.map(raw => { |
| | | const productId = raw?.returnSaleLedgerProductId ?? raw?.saleLedgerProductId ?? raw?.id; |
| | | const product = availableProducts.value.find((p) => p.id === productId); |
| | | const normalized = { |
| | | ...raw, |
| | | id: productId, |
| | | returnQuantity: Number(raw?.num ?? raw?.returnQuantity ?? 0), |
| | | price: Number(raw?.taxInclusiveUnitPrice ?? raw?.price ?? 0), |
| | | amount: Number(raw?.amount ?? 0).toFixed(2), |
| | | isQuality: raw?.isQuality ?? 2, |
| | | remark: raw?.remark ?? "", |
| | | }; |
| | | return product ? { ...product, ...normalized } : normalized; |
| | | }) : []; |
| | | detail.value?.returnSaleProductList || |
| | | detail.value?.returnSaleProductDtoData || |
| | | []; |
| | | |
| | | tableData.value = Array.isArray(list) |
| | | ? list.map((raw) => { |
| | | const normalized = normalizeDetailRow(raw); |
| | | const product = pickShippingLine(normalized); |
| | | return product ? mergeDetailProductRow(product, normalized) : normalized; |
| | | }) |
| | | : []; |
| | | |
| | | const headerShipNo = detail.value?.shippingNo; |
| | | if (headerShipNo && Array.isArray(tableData.value) && tableData.value.length) { |
| | | tableData.value = tableData.value.map((r) => |
| | | isEmptyText(r.outboundBatches) ? { ...r, outboundBatches: headerShipNo } : r |
| | | ); |
| | | } |
| | | } catch (e) { |
| | | console.error("Failed to load detail", e); |
| | | } finally { |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="å
³èåºåºåå·ï¼" prop="shippingId"> |
| | | <el-form-item label="å
³èåè´§åå·ï¼" prop="shippingId"> |
| | | <el-select v-model="form.shippingId" filterable placeholder="è¯·éæ©åºåºåå·" @change="outboundNoChange"> |
| | | <el-option |
| | | v-for="item in outboundOptions" |
| | |
| | | <el-button type="primary" @click="openProductSelection" :disabled="!form.shippingId">æ·»å 产å</el-button> |
| | | </div> |
| | | <PIMTable :isShowPagination="false" rowKey="id" :column="tableColumn" :tableData="tableData"> |
| | | <template #totalReturnNum="{ row }"> |
| | | {{ calcAlreadyReturned(row) }} |
| | | </template> |
| | | <template #returnQuantity="{ row }"> |
| | | <el-input |
| | | v-model="row.returnQuantity" |
| | |
| | | placeholder="请è¾å
¥" |
| | | /> |
| | | </template> |
| | | <template #action="{ row, index }"> |
| | | <template #action="{ index }"> |
| | | <el-button type="danger" link @click="deleteRow(index)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | |
| | | row-key="id" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" prop="outboundBatches" label="åºåºåå·" /> |
| | | <el-table-column align="center" prop="batchNo" label="æ¹æ¬¡å·" /> |
| | | <el-table-column align="center" prop="productCategory" label="产å大类" /> |
| | | <el-table-column align="center" prop="specificationModel" label="è§æ ¼åå·" /> |
| | | <el-table-column align="center" prop="unit" label="åä½" /> |
| | | <el-table-column align="center" prop="quantity" label="æ»æ°é" /> |
| | | <el-table-column align="center" prop="stockOutNum" label="æ»æ°é" /> |
| | | <el-table-column align="center" prop="unQuantity" label="æªéè´§æ°é" /> |
| | | <el-table-column align="center" label="å·²éè´§æ°é"> |
| | | <template #default="{ row }">{{ calcAlreadyReturned(row) }}</template> |
| | |
| | | const { form, rules } = toRefs(data); |
| | | |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.quantity ?? row?.totalQuantity ?? row?.totalReturnNum ?? 0); |
| | | const total = Number(row?.stockOutNum ?? row?.totalQuantity ?? row?.totalReturnNum ?? 0); |
| | | const un = Number(row?.unQuantity ?? 0); |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0; |
| | | return Math.max(total - un, 0); |
| | | }; |
| | | |
| | | const tableColumn = ref([ |
| | | {align: "center", label: "åºåºåå·", prop: "outboundBatches" }, |
| | | {align: "center", label: "æ¹æ¬¡å·", prop: "batchNo" }, |
| | | {align: "center", label: "产å大类", prop: "productCategory" }, |
| | | {align: "center", label: "è§æ ¼åå·", prop: "specificationModel" }, |
| | | {align: "center", label: "åä½", prop: "unit", width: 80 }, |
| | | {align: "center", label: "æ»æ°é", prop: "quantity", width: 120 }, |
| | | {align: "center", label: "å·²éè´§æ°é", prop: "totalReturnNum", width: 120 }, |
| | | {align: "center", label: "æ»æ°é", prop: "stockOutNum", width: 120 }, |
| | | {align: "center", label: "å·²éè´§æ°é", prop: "totalReturnNum", width: 120, dataType: "slot", slot: "totalReturnNum" }, |
| | | {align: "center", label: "æªéè´§æ°é", prop: "unQuantity", width: 120 }, |
| | | {align: "center", label: "éè´§æ°é", prop: "returnQuantity", dataType: "slot", slot: "returnQuantity", width: 120 }, |
| | | {align: "center", label: "é货产ååä»·", prop: "price", dataType: "slot", slot: "price", width: 120 }, |
| | |
| | | tableData.value.splice(index, 1); |
| | | }; |
| | | |
| | | const sameKey = (a, b) => a != null && b != null && String(a) === String(b); |
| | | |
| | | /** æ¥å£å¯è½ææ shippingProductVoList / productDtoData 两份ï¼åªåå
¶ä¸ä¼ç¼ºæ¹æ¬¡ãæ°éçåæ®µ */ |
| | | const mergeShippingProductLists = (data) => { |
| | | const lists = [data?.shippingProductVoList, data?.productDtoData].filter(Array.isArray); |
| | | if (!lists.length) return []; |
| | | const map = new Map(); |
| | | for (const list of lists) { |
| | | for (const p of list) { |
| | | if (p == null) continue; |
| | | const key = p.id != null ? String(p.id) : null; |
| | | if (!key) continue; |
| | | const prev = map.get(key); |
| | | map.set(key, prev ? { ...prev, ...p } : { ...p }); |
| | | } |
| | | } |
| | | return Array.from(map.values()); |
| | | }; |
| | | |
| | | const pickShippingLine = (normalized) => { |
| | | const pid = normalized?.returnSaleLedgerProductId ?? normalized?.id; |
| | | const sid = normalized?.stockOutRecordId ?? normalized?.shippingProductId; |
| | | const direct = availableProducts.value.find( |
| | | (p) => |
| | | sameKey(p?.id, pid) || |
| | | sameKey(p?.stockOutRecordId, pid) || |
| | | sameKey(p?.id, sid) || |
| | | sameKey(p?.stockOutRecordId, sid) |
| | | ); |
| | | if (direct) return direct; |
| | | const pmid = normalized?.productModelId; |
| | | if (pmid == null || pmid === "") return undefined; |
| | | const candidates = availableProducts.value.filter((p) => sameKey(p?.productModelId, pmid)); |
| | | if (!candidates.length) return undefined; |
| | | if (candidates.length === 1) return candidates[0]; |
| | | const spec = String(normalized?.specificationModel ?? normalized?.model ?? ""); |
| | | if (spec) { |
| | | const hit = candidates.find((p) => { |
| | | const ps = String(p?.specificationModel ?? p?.model ?? ""); |
| | | return ps && ps === spec; |
| | | }); |
| | | if (hit) return hit; |
| | | } |
| | | return candidates[0]; |
| | | }; |
| | | |
| | | const isEmptyText = (v) => v === "" || v == null || v === undefined; |
| | | |
| | | const firstFiniteNumber = (...vals) => { |
| | | for (const v of vals) { |
| | | if (v === "" || v == null || v === undefined) continue; |
| | | const n = Number(v); |
| | | if (Number.isFinite(n)) return n; |
| | | } |
| | | return undefined; |
| | | }; |
| | | |
| | | const firstNonEmptyText = (...vals) => { |
| | | const hit = vals.find((v) => !isEmptyText(v)); |
| | | return hit === undefined ? "" : hit; |
| | | }; |
| | | |
| | | /** 详æ
æ¥å£å段常ä¸å
¨ï¼{...product,...normalized} ä¼è¢« normalized éç空串çæåºåºè¡ä¸çå±ç¤ºå段 */ |
| | | const mergeShippingLineWithDetail = (product, normalized) => { |
| | | const row = { ...product, ...normalized }; |
| | | row.outboundBatches = firstNonEmptyText( |
| | | row.outboundBatches, |
| | | product?.outboundBatches, |
| | | product?.shippingNo, |
| | | product?.outboundNo, |
| | | normalized?.outboundBatches, |
| | | normalized?.outboundNo, |
| | | normalized?.shippingNo |
| | | ); |
| | | row.batchNo = firstNonEmptyText( |
| | | row.batchNo, |
| | | product?.batchNo, |
| | | product?.batchNumber, |
| | | product?.lotNo, |
| | | product?.batchCode, |
| | | product?.shippingBatchNo, |
| | | normalized?.batchNo, |
| | | normalized?.batchNumber, |
| | | normalized?.lotNo, |
| | | normalized?.shippingBatchNo |
| | | ); |
| | | const stock = firstFiniteNumber( |
| | | row.stockOutNum, |
| | | product?.stockOutNum, |
| | | product?.totalQuantity, |
| | | product?.shippingQuantity, |
| | | product?.deliveryQuantity, |
| | | product?.quantity, |
| | | product?.outQuantity, |
| | | normalized?.stockOutNum, |
| | | normalized?.totalQuantity, |
| | | normalized?.shippingQuantity, |
| | | normalized?.deliveryQuantity |
| | | ); |
| | | if (stock !== undefined) row.stockOutNum = stock; |
| | | const un = firstFiniteNumber( |
| | | row.unQuantity, |
| | | product?.unQuantity, |
| | | product?.remainingQuantity, |
| | | product?.noReturnQuantity, |
| | | product?.canReturnQuantity, |
| | | product?.availableReturnNum, |
| | | normalized?.unQuantity, |
| | | normalized?.remainingQuantity, |
| | | normalized?.noReturnQuantity, |
| | | normalized?.canReturnQuantity |
| | | ); |
| | | if (un !== undefined) row.unQuantity = un; |
| | | else { |
| | | const s = Number(row.stockOutNum); |
| | | const ret = Number(row.totalReturnNum ?? 0); |
| | | if (Number.isFinite(s) && s >= 0 && Number.isFinite(ret) && ret >= 0) { |
| | | row.unQuantity = Math.max(0, s - ret); |
| | | } |
| | | } |
| | | const returned = firstFiniteNumber( |
| | | row.totalReturnNum, |
| | | product?.totalReturnNum, |
| | | product?.totalReturnedNum, |
| | | normalized?.totalReturnNum, |
| | | normalized?.totalReturnedNum |
| | | ); |
| | | if (returned !== undefined) row.totalReturnNum = returned; |
| | | else if (isEmptyText(row.totalReturnNum)) row.totalReturnNum = 0; |
| | | if (isEmptyText(row.unit)) { |
| | | row.unit = firstNonEmptyText(product?.unit, normalized?.unit); |
| | | } |
| | | if (isEmptyText(row.productCategory)) { |
| | | row.productCategory = firstNonEmptyText( |
| | | normalized?.productCategory, |
| | | normalized?.productName, |
| | | product?.productCategory, |
| | | product?.productName |
| | | ); |
| | | } |
| | | if (isEmptyText(row.specificationModel)) { |
| | | row.specificationModel = firstNonEmptyText( |
| | | normalized?.specificationModel, |
| | | normalized?.model, |
| | | product?.specificationModel, |
| | | product?.model |
| | | ); |
| | | } |
| | | return row; |
| | | }; |
| | | |
| | | const normalizeDetailRow = (raw) => { |
| | | const productId = raw?.returnSaleLedgerProductId ?? raw?.saleLedgerProductId ?? raw?.id; |
| | | const ledgerId = |
| | | raw?.returnSaleLedgerProductId ?? |
| | | raw?.saleLedgerProductId ?? |
| | | raw?.stockOutRecordId ?? |
| | | raw?.shippingProductId; |
| | | const productId = ledgerId ?? raw?.id; |
| | | const returnSaleProductId = raw?.returnSaleProductId ?? raw?.id; |
| | | const num = Number(raw?.num ?? raw?.returnQuantity ?? 0); |
| | | return { |
| | |
| | | returnSaleProductId, |
| | | returnSaleLedgerProductId: productId, |
| | | productModelId: raw?.productModelId, |
| | | stockOutRecordId: raw?.stockOutRecordId, |
| | | shippingProductId: raw?.shippingProductId, |
| | | productCategory: raw?.productCategory ?? raw?.productName ?? raw?.productTypeName ?? "", |
| | | specificationModel: raw?.specificationModel ?? raw?.model ?? raw?.specModel ?? "", |
| | | outboundBatches: raw?.outboundBatches ?? raw?.outboundNo ?? raw?.shippingNo, |
| | | batchNo: |
| | | raw?.batchNo ?? |
| | | raw?.batchNumber ?? |
| | | raw?.lotNo ?? |
| | | raw?.batchCode ?? |
| | | raw?.shippingBatchNo, |
| | | stockOutNum: |
| | | raw?.stockOutNum ?? |
| | | raw?.totalQuantity ?? |
| | | raw?.shippingQuantity ?? |
| | | raw?.deliveryQuantity ?? |
| | | raw?.quantity, |
| | | totalReturnNum: raw?.totalReturnNum ?? raw?.totalReturnedNum, |
| | | unQuantity: |
| | | raw?.unQuantity ?? |
| | | raw?.remainingQuantity ?? |
| | | raw?.noReturnQuantity ?? |
| | | raw?.canReturnQuantity, |
| | | num, |
| | | returnQuantity: Number.isFinite(num) ? num : 0, |
| | | price: Number(raw?.taxInclusiveUnitPrice ?? raw?.price ?? 0), |
| | |
| | | |
| | | const setFormForEdit = async (row) => { |
| | | const res = await returnManagementGetById({ returnManagementId: row?.id }); |
| | | console.log("res", res); |
| | | const detail = res?.data ?? res ?? {}; |
| | | |
| | | Object.assign(form.value, detail); |
| | |
| | | tableData.value = Array.isArray(list) |
| | | ? list.map((raw) => { |
| | | const normalized = normalizeDetailRow(raw); |
| | | const product = availableProducts.value.find((p) => p.id === normalized.id); |
| | | return product ? { ...product, ...normalized } : normalized; |
| | | const product = pickShippingLine(normalized); |
| | | return product ? mergeShippingLineWithDetail(product, normalized) : normalized; |
| | | }) |
| | | : []; |
| | | |
| | | |
| | | const headerShipNo = detail?.shippingNo ?? form.value?.shippingNo; |
| | | if (headerShipNo && Array.isArray(tableData.value) && tableData.value.length) { |
| | | tableData.value = tableData.value.map((r) => |
| | | isEmptyText(r.outboundBatches) ? { ...r, outboundBatches: headerShipNo } : r |
| | | ); |
| | | } |
| | | |
| | | calculateTotalRefund(); |
| | | }; |
| | | |
| | |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (!valid) return; |
| | | const returnSaleProducts = (tableData.value || []).map(el => ({ |
| | | returnSaleLedgerProductId: el.returnSaleLedgerProductId ?? el.id, |
| | | stockOutRecordId: el.returnSaleLedgerProductId ?? el.id, |
| | | productModelId: el.productModelId, |
| | | unit: el.unit, |
| | | num: Number(el.num ?? el.returnQuantity ?? 0), |
| | |
| | | // If backend returns project info, set it |
| | | if (res.data.projectId) form.value.projectId = res.data.projectId; |
| | | |
| | | // Store available products for selection |
| | | availableProducts.value = res.data.productDtoData || []; |
| | | availableProducts.value = mergeShippingProductLists(res.data); |
| | | if (clearTable) tableData.value = []; |
| | | } |
| | | }; |
| | |
| | | }; |
| | | |
| | | const calculateRowAmount = (row) => { |
| | | const quantity = Number(row.returnQuantity || 0); |
| | | const stockOutNum = Number(row.returnQuantity || 0); |
| | | const price = Number(row.price || 0); |
| | | row.amount = (quantity * price).toFixed(2); |
| | | row.amount = (stockOutNum * price).toFixed(2); |
| | | }; |
| | | |
| | | const calculateTotalRefund = () => { |
| | |
| | | amount: "0.00", |
| | | isQuality: 2, |
| | | remark: "", |
| | | productCategory: product.productCategory ?? product.productName ?? "", |
| | | productName: product.productName, |
| | | specificationModel: product.specificationModel, |
| | | specificationModel: product.specificationModel ?? product.model ?? "", |
| | | unit: product.unit, |
| | | quantity: product.quantity, |
| | | stockOutNum: product.stockOutNum, |
| | | totalReturnNum: product.totalReturnNum, |
| | | unQuantity: product.unQuantity |
| | | }); |
| | |
| | | <el-form-item label="éå®åå·"> |
| | | <el-input v-model="searchForm.salesContractNo" placeholder="éå®åå·" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="å
³èåºåºåå·"> |
| | | <el-input v-model="searchForm.shippingNo" placeholder="å
³èåºåºåå·" clearable /> |
| | | <el-form-item label="å
³èåè´§åå·"> |
| | | <el-input v-model="searchForm.shippingNo" placeholder="å
³èåè´§åå·" clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">æç´¢</el-button> |
| | |
| | | ]); |
| | | |
| | | const defaultColumns = [ |
| | | { label: "éè´§åå·", prop: "returnNo", width: 160 }, |
| | | { label: "åæ®ç¶æ", prop: "status", width: 90, dataType: "slot", slot: "status" }, |
| | | { label: "å¶åæ¶é´", prop: "makeTime", width: 170 }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: 220 }, |
| | | { label: "éå®åå·", prop: "salesContractNo", width: 160 }, |
| | | { label: "ä¸å¡å", prop: "salesman", width: 120 }, |
| | | { label: "å
³èåºåºåå·", prop: "shippingNo", width: 170 }, |
| | | { label: "项ç®åç§°", prop: "projectName", width: 180 }, |
| | | { label: "å¶å人", prop: "maker", width: 120 }, |
| | | { label: "éè´§åå·", prop: "returnNo", minWidth: 160 }, |
| | | { label: "åæ®ç¶æ", prop: "status", minWidth: 90, dataType: "slot", slot: "status" }, |
| | | { label: "å¶åæ¶é´", prop: "makeTime", minWidth: 170 }, |
| | | { label: "客æ·åç§°", prop: "customerName", minWidth: 220 }, |
| | | { label: "éå®åå·", prop: "salesContractNo", minWidth: 160 }, |
| | | { label: "ä¸å¡å", prop: "salesman", minWidth: 120 }, |
| | | { label: "å
³èåè´§åå·", prop: "shippingNo", minWidth: 170 }, |
| | | { label: "项ç®åç§°", prop: "projectName", minWidth: 180 }, |
| | | { label: "å¶å人", prop: "maker", minWidth: 120 }, |
| | | { |
| | | label: "æä½", |
| | | prop: "operation", |
| | | dataType: "action", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 240, |
| | | minWidth: 240, |
| | | operation: [ |
| | | { name: "ç¼è¾", disabled: (row) => row.status !== 0, type: "text", clickFun: (row) => openForm("edit", row) }, |
| | | { name: "鿬¾å¤ç", disabled: (row) => row.status !== 0, type: "text", clickFun: (row) => handleRowHandle(row) }, |
| | |
| | | å®¡æ ¸æç»: "å®¡æ ¸æç»", |
| | | å®¡æ ¸éè¿: "å®¡æ ¸éè¿", |
| | | å·²åè´§: "å·²åè´§", |
| | | é¨ååè´§: "é¨ååè´§", |
| | | }; |
| | | return statusTextMap[statusStr] || "å¾
åè´§"; |
| | | }; |
| | |
| | | å®¡æ ¸æç»: "danger", |
| | | å®¡æ ¸éè¿: "success", |
| | | å·²åè´§: "success", |
| | | é¨ååè´§: "warning", |
| | | }; |
| | | return typeTextMap[statusStr] || "info"; |
| | | }; |