From 14363b1ae7cb0d730158ec8dfbee55a85b2fc09f Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 12 五月 2026 15:23:17 +0800
Subject: [PATCH] feat(financial): 实现财务模块数据接口联调
---
src/api/financialManagement/intangibleAsset.js | 50 ++
src/api/financialManagement/voucher.js | 54 ++
src/api/financialManagement/fixedAsset.js | 50 ++
src/views/financialManagement/generalLedger/index.vue | 74 ++
src/views/financialManagement/assets/intangibleAssets.vue | 157 +++---
src/views/financialManagement/voucher/index.vue | 333 +++++++++----
FINANCIAL_MANAGEMENT_BACKEND_SPEC.md | 233 ++++++++++
src/api/financialManagement/ledger.js | 19
src/views/financialManagement/voucher/detailLedger.vue | 103 ++--
src/views/financialManagement/assets/fixedAssets.vue | 149 +++---
src/views/financialManagement/voucher/generalLedger.vue | 107 ++--
11 files changed, 952 insertions(+), 377 deletions(-)
diff --git a/FINANCIAL_MANAGEMENT_BACKEND_SPEC.md b/FINANCIAL_MANAGEMENT_BACKEND_SPEC.md
new file mode 100644
index 0000000..ee7e5b7
--- /dev/null
+++ b/FINANCIAL_MANAGEMENT_BACKEND_SPEC.md
@@ -0,0 +1,233 @@
+# 璐㈠姟绠$悊鍚庣鏂囨。锛堜粎璐熻矗妯″潡锛�
+
+鏇存柊鏃堕棿锛�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`锛圷YYY-MM锛�
+- `endMonth`锛圷YYY-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`锛坈ustomer/supplier/department/employee/project锛�
+- `auxiliaryId`
+- `startMonth`锛圷YYY-MM锛�
+- `endMonth`锛圷YYY-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. 琛ユ祴璇曪細
+ - 鍊熻捶骞宠 鏍¢獙
+ - 鎶樻棫/鎽婇攢鍏紡
+ - 绉戠洰琚紩鐢ㄧ姝㈠垹闄�
+
diff --git a/src/api/financialManagement/fixedAsset.js b/src/api/financialManagement/fixedAsset.js
new file mode 100644
index 0000000..5c28db4
--- /dev/null
+++ b/src/api/financialManagement/fixedAsset.js
@@ -0,0 +1,50 @@
+import request from "@/utils/request";
+
+// 鍥哄畾璧勪骇鍒嗛〉鏌ヨ锛坈urrent/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,
+ });
+}
diff --git a/src/api/financialManagement/intangibleAsset.js b/src/api/financialManagement/intangibleAsset.js
new file mode 100644
index 0000000..802e649
--- /dev/null
+++ b/src/api/financialManagement/intangibleAsset.js
@@ -0,0 +1,50 @@
+import request from "@/utils/request";
+
+// 鏃犲舰璧勪骇鍒嗛〉鏌ヨ锛坈urrent/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,
+ });
+}
diff --git a/src/api/financialManagement/ledger.js b/src/api/financialManagement/ledger.js
new file mode 100644
index 0000000..17e62fc
--- /dev/null
+++ b/src/api/financialManagement/ledger.js
@@ -0,0 +1,19 @@
+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,
+ });
+}
diff --git a/src/api/financialManagement/voucher.js b/src/api/financialManagement/voucher.js
new file mode 100644
index 0000000..ccb0908
--- /dev/null
+++ b/src/api/financialManagement/voucher.js
@@ -0,0 +1,54 @@
+import request from "@/utils/request";
+
+// 鍑瘉鍒嗛〉鏌ヨ锛坈urrent/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",
+ });
+}
diff --git a/src/views/financialManagement/assets/fixedAssets.vue b/src/views/financialManagement/assets/fixedAssets.vue
index 61eb245..58b2210 100644
--- a/src/views/financialManagement/assets/fixedAssets.vue
+++ b/src/views/financialManagement/assets/fixedAssets.vue
@@ -189,6 +189,13 @@
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: "鍥哄畾璧勪骇",
@@ -210,13 +217,13 @@
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([]);
@@ -226,7 +233,7 @@
const isEdit = ref(false);
const currentId = ref(null);
-const form = reactive({
+const createDefaultForm = () => ({
assetCode: "",
assetName: "",
category: "",
@@ -244,6 +251,10 @@
remark: "",
});
+const form = reactive({
+ ...createDefaultForm(),
+});
+
const rules = {
assetName: [{ required: true, message: "璇疯緭鍏ヨ祫浜у悕绉�", trigger: "blur" }],
category: [{ required: true, message: "璇烽�夋嫨璧勪骇绫诲埆", trigger: "change" }],
@@ -251,13 +262,6 @@
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);
@@ -288,35 +292,39 @@
};
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 = () => {
@@ -334,25 +342,15 @@
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;
};
@@ -361,7 +359,7 @@
isEdit.value = true;
currentId.value = row.id;
dialogTitle.value = "缂栬緫鍥哄畾璧勪骇";
- Object.assign(form, row);
+ Object.assign(form, createDefaultForm(), row);
dialogVisible.value = true;
};
@@ -374,13 +372,14 @@
confirmButtonText: "纭畾",
cancelButtonText: "鍙栨秷",
type: "warning",
- }).then(() => {
- const index = mockData.findIndex(item => item.id === row.id);
- if (index !== -1) {
- mockData.splice(index, 1);
+ }).then(async () => {
+ // 鑱旇皟绾﹀畾锛氬垹闄ゆ帴鍙d娇鐢� ids=1&ids=2
+ await deleteFixedAsset([row.id]);
+ if (dataList.value.length === 1 && pagination.currentPage > 1) {
+ pagination.currentPage -= 1;
}
ElMessage.success("鍒犻櫎鎴愬姛");
- getTableData();
+ await getTableData();
});
};
@@ -389,16 +388,10 @@
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();
});
};
@@ -407,22 +400,24 @@
};
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();
}
});
};
diff --git a/src/views/financialManagement/assets/intangibleAssets.vue b/src/views/financialManagement/assets/intangibleAssets.vue
index 14dae55..649ec5b 100644
--- a/src/views/financialManagement/assets/intangibleAssets.vue
+++ b/src/views/financialManagement/assets/intangibleAssets.vue
@@ -182,6 +182,13 @@
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: "鏃犲舰璧勪骇",
@@ -203,13 +210,13 @@
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([]);
@@ -219,7 +226,7 @@
const isEdit = ref(false);
const currentId = ref(null);
-const form = reactive({
+const createDefaultForm = () => ({
assetCode: "",
assetName: "",
category: "",
@@ -236,6 +243,10 @@
remark: "",
});
+const form = reactive({
+ ...createDefaultForm(),
+});
+
const rules = {
assetName: [{ required: true, message: "璇疯緭鍏ヨ祫浜у悕绉�", trigger: "blur" }],
category: [{ required: true, message: "璇烽�夋嫨璧勪骇绫诲埆", trigger: "change" }],
@@ -243,13 +254,6 @@
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);
@@ -281,35 +285,44 @@
};
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 = () => {
@@ -327,24 +340,15 @@
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;
};
@@ -353,7 +357,7 @@
isEdit.value = true;
currentId.value = row.id;
dialogTitle.value = "缂栬緫鏃犲舰璧勪骇";
- Object.assign(form, row);
+ Object.assign(form, createDefaultForm(), row);
dialogVisible.value = true;
};
@@ -366,13 +370,14 @@
confirmButtonText: "纭畾",
cancelButtonText: "鍙栨秷",
type: "warning",
- }).then(() => {
- const index = mockData.findIndex(item => item.id === row.id);
- if (index !== -1) {
- mockData.splice(index, 1);
+ }).then(async () => {
+ // 鑱旇皟绾﹀畾锛氬垹闄ゆ帴鍙d娇鐢� ids=1&ids=2
+ await deleteIntangibleAsset([row.id]);
+ if (dataList.value.length === 1 && pagination.currentPage > 1) {
+ pagination.currentPage -= 1;
}
ElMessage.success("鍒犻櫎鎴愬姛");
- getTableData();
+ await getTableData();
});
};
@@ -381,20 +386,10 @@
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();
});
};
@@ -403,22 +398,24 @@
};
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();
}
});
};
diff --git a/src/views/financialManagement/generalLedger/index.vue b/src/views/financialManagement/generalLedger/index.vue
index fbe2210..2d370f4 100644
--- a/src/views/financialManagement/generalLedger/index.vue
+++ b/src/views/financialManagement/generalLedger/index.vue
@@ -68,6 +68,10 @@
: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"
@@ -201,8 +205,15 @@
label: "鎿嶄綔",
align: "center",
fixed: "right",
- width: "150",
+ width: "220",
operation: [
+ {
+ name: "鏂板",
+ type: "primary",
+ clickFun: row => {
+ addChild(row);
+ },
+ },
{
name: "缂栬緫",
type: "primary",
@@ -224,11 +235,13 @@
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: "",
@@ -258,8 +271,8 @@
const getTableData = () => {
const query = {
- pageNum: pagination.currentPage,
- pageSize: pagination.pageSize,
+ current: pagination.currentPage,
+ size: pagination.pageSize,
...filters,
};
listAccountSubject(query).then(response => {
@@ -282,11 +295,19 @@
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: "",
@@ -294,13 +315,54 @@
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;
};
diff --git a/src/views/financialManagement/voucher/detailLedger.vue b/src/views/financialManagement/voucher/detailLedger.vue
index 7f85790..202ece1 100644
--- a/src/views/financialManagement/voucher/detailLedger.vue
+++ b/src/views/financialManagement/voucher/detailLedger.vue
@@ -43,13 +43,13 @@
<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>
@@ -75,6 +75,8 @@
<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: "绉戠洰鏄庣粏璐�",
@@ -89,36 +91,36 @@
});
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 = {
@@ -158,7 +160,7 @@
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) => {
@@ -182,31 +184,24 @@
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 = () => {
+// 鑱旇皟绾﹀畾锛氭槑缁嗚处鎺ュ彛鍙寜杈呭姪鏍哥畻杩囨护锛坅uxiliaryType/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 = () => {
@@ -249,8 +244,8 @@
ElMessage.success("瀵煎嚭鎴愬姛");
};
-onMounted(() => {
- // 榛樿涓嶅姞杞芥暟鎹紝闇�瑕侀�夋嫨绉戠洰
+onMounted(async () => {
+ await loadSubjectOptions();
});
</script>
diff --git a/src/views/financialManagement/voucher/generalLedger.vue b/src/views/financialManagement/voucher/generalLedger.vue
index 5da2d70..b21feac 100644
--- a/src/views/financialManagement/voucher/generalLedger.vue
+++ b/src/views/financialManagement/voucher/generalLedger.vue
@@ -28,13 +28,13 @@
<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>
@@ -60,6 +60,8 @@
<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: "绉戠洰鎬昏处",
@@ -72,42 +74,47 @@
});
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) => {
@@ -126,30 +133,22 @@
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 = () => {
@@ -190,8 +189,8 @@
ElMessage.success("瀵煎嚭鎴愬姛");
};
-onMounted(() => {
- // 榛樿涓嶅姞杞芥暟鎹紝闇�瑕侀�夋嫨绉戠洰
+onMounted(async () => {
+ await loadSubjectOptions();
});
</script>
diff --git a/src/views/financialManagement/voucher/index.vue b/src/views/financialManagement/voucher/index.vue
index 817185c..2d34f5c 100644
--- a/src/views/financialManagement/voucher/index.vue
+++ b/src/views/financialManagement/voucher/index.vue
@@ -62,9 +62,9 @@
</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>
@@ -137,9 +137,18 @@
<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鍒� -->
@@ -205,6 +214,15 @@
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: "鍑瘉绠$悊",
@@ -227,11 +245,11 @@
{ 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([]);
@@ -241,25 +259,64 @@
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);
@@ -276,12 +333,6 @@
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);
@@ -304,32 +355,70 @@
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 = () => {
@@ -348,7 +437,7 @@
};
const addEntry = () => {
- form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
+ form.entries.push(createEmptyEntry());
};
const selectRow = (index) => {
@@ -402,61 +491,69 @@
};
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) => {
@@ -468,13 +565,10 @@
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();
});
};
@@ -483,13 +577,10 @@
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();
});
};
@@ -502,45 +593,74 @@
};
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>
@@ -780,7 +900,8 @@
.col-subject {
position: relative;
- .el-select {
+ .el-select,
+ .el-tree-select {
.el-input input {
font-size: 12px;
}
--
Gitblit v1.9.3