Merge branch 'dev_NEW_pro' of http://114.132.189.42:9002/r/product-inventory-management into dev_NEW_pro
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # è´¢å¡ç®¡çåç«¯ææ¡£ï¼ä»
è´è´£æ¨¡åï¼ |
| | | |
| | | æ´æ°æ¶é´ï¼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. è¡¥æµè¯ï¼ |
| | | - åè´·å¹³è¡¡æ ¡éª |
| | | - ææ§/æéå
¬å¼ |
| | | - ç§ç®è¢«å¼ç¨ç¦æ¢å é¤ |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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", |
| | | }); |
| | | } |
| | |
| | | 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 isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | 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 || []; |
| | | 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 resetFilters = () => { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `GD${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.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; |
| | | }; |
| | |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åºå®èµäº§"; |
| | | Object.assign(form, row); |
| | | Object.assign(form, createDefaultForm(), row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | 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(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | 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({}); |
| | | ElMessage.success("ææ§è®¡æå®æ"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | 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(); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | 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 isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | 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 || []; |
| | | 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 resetFilters = () => { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `WX${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.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; |
| | | }; |
| | |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ å½¢èµäº§"; |
| | | Object.assign(form, row); |
| | | Object.assign(form, createDefaultForm(), row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | 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(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | 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({}); |
| | | ElMessage.success("æé计æå®æ"); |
| | | getTableData(); |
| | | await getTableData(); |
| | | }); |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | 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(); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | :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" |
| | |
| | | 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 isEdit = ref(false); |
| | | |
| | | const form = reactive({ |
| | | id: undefined, |
| | | parentId: null, |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | |
| | | |
| | | const getTableData = () => { |
| | | const query = { |
| | | pageNum: pagination.currentPage, |
| | | pageSize: pagination.pageSize, |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | ...filters, |
| | | }; |
| | | listAccountSubject(query).then(response => { |
| | |
| | | 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; |
| | | }; |
| | | |
| | |
| | | <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"> |
| | | <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 label="è´·æ¹" width="150"> |
| | | <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> |
| | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listAccountSubject } from "@/api/financialManagement/accountSubject"; |
| | | import { getDetailLedger } from "@/api/financialManagement/ledger"; |
| | | |
| | | defineOptions({ |
| | | name: "ç§ç®æç»è´¦", |
| | |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const subjectOptions = 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 fallbackSubjects = [ |
| | | { code: "1122", name: "åºæ¶è´¦æ¬¾" }, |
| | | { code: "2202", name: "åºä»è´¦æ¬¾" }, |
| | | { code: "6602", name: "管çè´¹ç¨" }, |
| | | ]; |
| | | |
| | | const loadSubjectOptions = async () => { |
| | | try { |
| | | const { data } = await listAccountSubject({ |
| | | current: 1, |
| | | size: 1000, |
| | | }); |
| | | const records = data?.records || []; |
| | | if (records.length > 0) { |
| | | subjectOptions.value = records |
| | | .filter(item => item.subjectCode && item.subjectName) |
| | | .map(item => ({ |
| | | code: item.subjectCode, |
| | | name: item.subjectName, |
| | | children: [], |
| | | })); |
| | | return; |
| | | } |
| | | } catch (error) { |
| | | // å
¨å±æ¦æªå¨å·²æç¤ºï¼ä¸é¢èµ°å
åºç§ç® |
| | | } |
| | | subjectOptions.value = fallbackSubjects.map(item => ({ ...item, children: [] })); |
| | | }; |
| | | |
| | | const auxiliaryItems = computed(() => { |
| | | const map = { |
| | |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject || filters.subject.length === 0) return null; |
| | | const code = filters.subject[filters.subject.length - 1]; |
| | | return findSubject(subjectOptions, code); |
| | | return findSubject(subjectOptions.value, code); |
| | | }); |
| | | |
| | | const findSubject = (options, code) => { |
| | |
| | | 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 = () => { |
| | | // èè°çº¦å®ï¼æç»è´¦æ¥å£å¯æè¾
婿 ¸ç®è¿æ»¤ï¼auxiliaryType/auxiliaryIdï¼ |
| | | const getTableData = async () => { |
| | | if (!currentSubject.value) { |
| | | dataList.value = []; |
| | | return; |
| | | } |
| | | dataList.value = [...mockData]; |
| | | try { |
| | | const { data } = await getDetailLedger({ |
| | | subjectCode: currentSubject.value.code, |
| | | auxiliaryType: filters.auxiliary, |
| | | auxiliaryId: filters.auxiliaryItem, |
| | | startMonth: filters.startMonth, |
| | | endMonth: filters.endMonth, |
| | | }); |
| | | dataList.value = Array.isArray(data) ? data : data?.records || []; |
| | | } catch (error) { |
| | | // æç¤ºç±å
¨å±è¯·æ±æ¦æªå¨å¤çï¼è¿éä»
鲿¢æªæè·å¼å¸¸ |
| | | } |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // é»è®¤ä¸å è½½æ°æ®ï¼éè¦éæ©ç§ç® |
| | | onMounted(async () => { |
| | | await loadSubjectOptions(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | <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"> |
| | | <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 label="è´·æ¹" width="150"> |
| | | <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> |
| | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listAccountSubject } from "@/api/financialManagement/accountSubject"; |
| | | import { getGeneralLedger } from "@/api/financialManagement/ledger"; |
| | | |
| | | defineOptions({ |
| | | name: "ç§ç®æ»è´¦", |
| | |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const subjectOptions = 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 fallbackSubjects = [ |
| | | { code: "1001", name: "åºåç°é" }, |
| | | { code: "1002", name: "é¶è¡å款" }, |
| | | { code: "1122", name: "åºæ¶è´¦æ¬¾" }, |
| | | { code: "2202", name: "åºä»è´¦æ¬¾" }, |
| | | { code: "6001", name: "主è¥ä¸å¡æ¶å
¥" }, |
| | | ]; |
| | | |
| | | const toCascaderTree = (nodes = []) => |
| | | nodes |
| | | .filter(item => item.subjectCode && item.subjectName) |
| | | .map(item => ({ |
| | | code: item.subjectCode, |
| | | name: item.subjectName, |
| | | children: toCascaderTree(item.children || []), |
| | | })); |
| | | |
| | | const loadSubjectOptions = async () => { |
| | | try { |
| | | const { data } = await listAccountSubject({ |
| | | current: 1, |
| | | size: 1000, |
| | | status: 0, |
| | | }); |
| | | const options = toCascaderTree(data?.records || []); |
| | | if (options.length > 0) { |
| | | subjectOptions.value = options; |
| | | return; |
| | | } |
| | | } catch (error) { |
| | | // å
¨å±æ¦æªå¨å·²æç¤ºï¼ä¸é¢èµ°å
åºç§ç® |
| | | } |
| | | subjectOptions.value = fallbackSubjects.map(item => ({ ...item, children: [] })); |
| | | }; |
| | | |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject || filters.subject.length === 0) return null; |
| | | const code = filters.subject[filters.subject.length - 1]; |
| | | return findSubject(subjectOptions, code); |
| | | return findSubject(subjectOptions.value, code); |
| | | }); |
| | | |
| | | const findSubject = (options, code) => { |
| | |
| | | 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 = () => { |
| | |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // é»è®¤ä¸å è½½æ°æ®ï¼éè¦éæ©ç§ç® |
| | | onMounted(async () => { |
| | | await loadSubjectOptions(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | </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> |
| | |
| | | <el-input v-model="entry.summary" 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" |
| | | 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å --> |
| | |
| | | import { ref, reactive, onMounted, computed, nextTick } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { listAccountSubject } from "@/api/financialManagement/accountSubject"; |
| | | import { |
| | | listVoucherPage, |
| | | addVoucher, |
| | | updateVoucher, |
| | | postVoucher, |
| | | cancelVoucher, |
| | | getVoucherDetail, |
| | | } from "@/api/financialManagement/voucher"; |
| | | |
| | | defineOptions({ |
| | | name: "åè¯ç®¡ç", |
| | |
| | | { 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 isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | 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: [], |
| | | entries: [createEmptyEntry(), createEmptyEntry()], |
| | | creator: "å¼ ä¸", |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | |
| | | 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); |
| | | } |
| | | 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 }); |
| | | form.entries.push(createEmptyEntry()); |
| | | }; |
| | | |
| | | const selectRow = (index) => { |
| | |
| | | }; |
| | | |
| | | const removeEntry = (index) => { |
| | | 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 = () => { |
| | | 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 edit = async row => { |
| | | try { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åè¯"; |
| | | 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] || "", |
| | | 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) => { |
| | |
| | | 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) => { |
| | | 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 loadSubjectList(); |
| | | await getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | .col-subject { |
| | | position: relative; |
| | | |
| | | .el-select { |
| | | .el-select, |
| | | .el-tree-select { |
| | | .el-input input { |
| | | font-size: 12px; |
| | | } |