From 4ab0be7d4441f378add1f242b168d80fb27e65fe Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期五, 22 五月 2026 17:57:44 +0800
Subject: [PATCH] OA部分查询条件变更
---
src/views/financialManagement/voucher/index.vue | 534 ++++++++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 404 insertions(+), 130 deletions(-)
diff --git a/src/views/financialManagement/voucher/index.vue b/src/views/financialManagement/voucher/index.vue
index 817185c..1aa6f69 100644
--- a/src/views/financialManagement/voucher/index.vue
+++ b/src/views/financialManagement/voucher/index.vue
@@ -9,9 +9,12 @@
</el-form-item>
<el-form-item label="鍒跺崟浜�:">
<el-select v-model="filters.creator" placeholder="璇烽�夋嫨鍒跺崟浜�" clearable style="width: 150px;">
- <el-option label="寮犱笁" value="寮犱笁" />
- <el-option label="鏉庡洓" value="鏉庡洓" />
- <el-option label="鐜嬩簲" value="鐜嬩簲" />
+ <el-option
+ v-for="item in creatorOptions"
+ :key="item"
+ :label="item"
+ :value="item"
+ />
</el-select>
</el-form-item>
<el-form-item label="鐘舵��:">
@@ -29,13 +32,13 @@
<div class="table_list">
<div class="actions">
<div>
- <el-statistic title="鍊熸柟鍚堣" :value="totalDebit" precision="2" prefix="楼" />
- <el-statistic title="璐锋柟鍚堣" :value="totalCredit" precision="2" prefix="楼" style="margin-left: 30px;" />
+ <el-statistic title="鍊熸柟鍚堣" :value="totalDebit" :precision="2" prefix="楼" />
+ <el-statistic title="璐锋柟鍚堣" :value="totalCredit" :precision="2" prefix="楼" style="margin-left: 30px;" />
</div>
<div>
<el-button type="primary" @click="add" icon="Plus">鏂板鍑瘉</el-button>
- <el-button @click="handleImport" icon="Upload">瀵煎叆</el-button>
- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+ <!-- <el-button @click="handleImport" icon="Upload">瀵煎叆</el-button> -->
+ <!-- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button> -->
</div>
</div>
<PIMTable
@@ -62,9 +65,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>
@@ -75,25 +78,29 @@
<h2 class="voucher-title">璁拌处鍑瘉</h2>
<div class="voucher-period">{{ form.voucherDate ? form.voucherDate.substring(0, 7) + '鏈�' : '' }}</div>
</div>
- <el-form :model="form" :rules="rules" ref="formRef" label-width="0">
+ <el-form :model="form" :rules="rules" :disabled="isViewMode" ref="formRef" label-width="0">
<div class="voucher-info">
<div class="voucher-no-section">
<span class="label">鍑瘉瀛楋細</span>
- <el-select v-model="form.voucherPrefix" style="width: 70px;">
+ <el-select v-model="form.voucherPrefix" :disabled="isViewMode" style="width: 70px;">
<el-option label="璁�" value="璁�" />
+ <el-option label="鐜�" value="鐜�" />
+ <el-option label="閾�" value="閾�" />
+ <el-option label="杞�" value="杞�" />
+ <el-option label="鏀�" value="鏀�" />
+ <el-option label="浠�" value="浠�" />
</el-select>
- <el-input v-model="form.voucherNum" style="width: 60px;" />
+ <el-input v-model="form.voucherNum" :disabled="isViewMode" style="width: 60px;" />
<span class="label" style="margin-left: 5px;">鍙�</span>
</div>
<div class="voucher-date-section">
<span class="label">鏃ユ湡锛�</span>
- <el-date-picker v-model="form.voucherDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 140px;" />
+ <el-date-picker v-model="form.voucherDate" :disabled="isViewMode" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 140px;" />
</div>
<div class="voucher-attachment-section">
<span class="label">闄勪欢锛�</span>
- <el-input-number v-model="form.attachmentCount" :min="0" :controls="false" style="width: 60px;" />
+ <el-input-number v-model="form.attachmentCount" :disabled="isViewMode" :min="0" :controls="false" style="width: 60px;" />
<span class="label" style="margin-left: 5px;">寮�</span>
- <el-button type="primary" link style="margin-left: 10px;">涓婁紶鏂囦欢</el-button>
</div>
</div>
<div class="voucher-table">
@@ -134,18 +141,28 @@
<tbody>
<tr v-for="(entry, rowIndex) in form.entries" :key="rowIndex" @click="selectRow(rowIndex)" :class="{ 'selected-row': selectedRowIndex === rowIndex }">
<td class="col-summary">
- <el-input v-model="entry.summary" placeholder="璇疯緭鍏ユ憳瑕�" @focus="selectRow(rowIndex)" />
+ <el-input v-model="entry.summary" :disabled="isViewMode" placeholder="璇疯緭鍏ユ憳瑕�" @focus="selectRow(rowIndex)" />
</td>
<td class="col-subject">
- <el-select v-model="entry.subjectCode" placeholder="閫夋嫨绉戠洰" filterable @change="(val) => handleSubjectChange(val, rowIndex)" @focus="selectRow(rowIndex)">
- <el-option v-for="item in subjectList" :key="item.code" :label="item.code + item.name" :value="item.code" />
- </el-select>
- <div class="subject-name">{{ entry.subjectName }}</div>
+ <el-tree-select
+ v-model="entry.subjectCode"
+ :data="subjectTreeOptions"
+ :props="subjectTreeSelectProps"
+ :disabled="isViewMode"
+ placeholder="閫夋嫨绉戠洰"
+ filterable
+ check-strictly
+ clearable
+ :render-after-expand="false"
+ @change="(val) => handleSubjectChange(val, rowIndex)"
+ @focus="selectRow(rowIndex)"
+ />
+ <!-- <div class="subject-name">{{ entry.subjectName }}</div> -->
</td>
<!-- 鍊熸柟11鍒� -->
<template v-if="editingCell.row === rowIndex && editingCell.type === 'debit'">
<td colspan="11" class="debit-input-cell">
- <el-input-number ref="amountInputRef" v-model="entry.debit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
+ <el-input-number ref="amountInputRef" v-model="entry.debit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" />
</td>
</template>
<template v-else>
@@ -156,7 +173,7 @@
<!-- 璐锋柟11鍒� -->
<template v-if="editingCell.row === rowIndex && editingCell.type === 'credit'">
<td colspan="11" class="credit-input-cell">
- <el-input-number ref="amountInputRef" v-model="entry.credit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
+ <el-input-number ref="amountInputRef" v-model="entry.credit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" />
</td>
</template>
<template v-else>
@@ -165,7 +182,7 @@
</td>
</template>
<td class="col-action">
- <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="form.entries.length <= 2">鍒犻櫎</el-button>
+ <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="isViewMode || form.entries.length <= 2">鍒犻櫎</el-button>
</td>
</tr>
<tr class="total-row">
@@ -182,22 +199,68 @@
</table>
</div>
<div class="voucher-toolbar">
- <el-button type="primary" link @click="addEntry" icon="Plus">鏂板琛�</el-button>
+ <el-button type="primary" link @click="addEntry" icon="Plus" :disabled="isViewMode">鏂板琛�</el-button>
</div>
<div class="voucher-footer">
<div class="creator-section">
- <span class="label">鍒跺崟浜猴細{{ form.creator }}</span>
+ <span class="label">鍒跺崟浜猴細</span>
+ <el-select
+ v-model="form.creator"
+ :disabled="isViewMode"
+ placeholder="璇烽�夋嫨鍒跺崟浜�"
+ filterable
+ clearable
+ style="width: 200px;"
+ >
+ <el-option
+ v-for="item in creatorOptions"
+ :key="item"
+ :label="item"
+ :value="item"
+ />
+ </el-select>
</div>
</div>
+ <!-- 缂栬緫妯″紡锛氫娇鐢� AttachmentUploadFile 涓婁紶缁勪欢 -->
+ <div class="voucher-attachment-upload" v-if="!isViewMode">
+ <div class="attachment-label">闄勪欢涓婁紶锛�</div>
+ <AttachmentUploadFile
+ v-model:fileList="form.attachments"
+ :disabled="isViewMode"
+ :limit="10"
+ :fileSize="50"
+ buttonText="鐐瑰嚮涓婁紶闄勪欢"
+ @change="handleAttachmentChange"
+ />
+ </div>
</el-form>
+ <!-- 鏌ョ湅妯″紡锛氬睍绀洪檮浠跺垪琛紙鏀惧湪 el-form 澶栭潰锛岄伩鍏嶈 disabled锛� -->
+ <div class="voucher-attachment-upload" v-if="isViewMode && form.attachments?.length">
+ <div class="attachment-label">闄勪欢鍒楄〃锛�</div>
+ <el-table :data="form.attachments" border class="attachment-table">
+ <el-table-column label="闄勪欢鍚嶇О" show-overflow-tooltip>
+ <template #default="scope">
+ {{ scope.row.originalFilename || scope.row.name || scope.row.fileName || '鏈懡鍚嶆枃浠�' }}
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
+ <template #default="scope">
+ <el-button link type="primary" size="small" @click="previewFile(scope.row)">棰勮</el-button>
+ <el-button link type="primary" size="small" @click="downloadFile(scope.row)">涓嬭浇</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
</div>
<template #footer>
<div>
- <el-button type="primary" @click="submitForm" :disabled="!isBalanced">淇濆瓨</el-button>
- <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ <el-button v-if="!isViewMode" type="primary" @click="submitForm" :disabled="!isBalanced">淇濆瓨</el-button>
+ <el-button @click="dialogVisible = false">{{ isViewMode ? '鍏抽棴' : '鍙栨秷' }}</el-button>
</div>
</template>
</FormDialog>
+ <!-- 鏂囦欢棰勮缁勪欢 -->
+ <FilePreview ref="filePreviewRef" />
</div>
</template>
@@ -205,10 +268,28 @@
import { ref, reactive, onMounted, computed, nextTick } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
+import AttachmentUploadFile from "@/components/AttachmentUpload/file/index.vue";
+import FileList from "@/components/Dialog/FileList.vue";
+import FilePreview from "@/components/filePreview/index.vue";
+import download from "@/plugins/download.js";
+import useUserStore from "@/store/modules/user";
+import { userListNoPageByTenantId } from "@/api/system/user";
+import { listAccountSubject } from "@/api/financialManagement/accountSubject";
+import {
+ listVoucherPage,
+ addVoucher,
+ updateVoucher,
+ postVoucher,
+ cancelVoucher,
+ getVoucherDetail,
+} from "@/api/financialManagement/voucher";
defineOptions({
name: "鍑瘉绠$悊",
});
+
+const userStore = useUserStore();
+const getDefaultCreator = () => userStore.nickName || userStore.name || "寮犱笁";
const filters = reactive({
voucherNo: "",
@@ -227,39 +308,94 @@
{ label: "鍑瘉瀛楀彿", prop: "voucherNo", width: "120" },
{ label: "鍑瘉鏃ユ湡", prop: "voucherDate", width: "120" },
{ label: "鎽樿", prop: "summary", showOverflowTooltip: true },
- { label: "鍊熸柟閲戦", prop: "debit", slot: "debit" },
- { label: "璐锋柟閲戦", prop: "credit", slot: "credit" },
+ { label: "鍊熸柟閲戦", prop: "debit", dataType: "slot", slot: "debit" },
+ { label: "璐锋柟閲戦", prop: "credit", dataType: "slot", slot: "credit" },
{ label: "鍒跺崟浜�", prop: "creator", width: "100" },
- { label: "鐘舵��", prop: "status", slot: "status" },
- { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "220", fixed: "right" },
+ { label: "鐘舵��", prop: "status", dataType: "slot", slot: "status" },
+ { label: "鎿嶄綔", prop: "operation", dataType: "slot", slot: "operation", width: "220", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
+const dialogMode = ref("add");
const isEdit = ref(false);
const currentId = ref(null);
+const isViewMode = computed(() => dialogMode.value === "view");
+const filePreviewRef = 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: undefined,
+ credit: undefined,
+});
+
+const createDefaultForm = () => ({
voucherNo: "",
voucherPrefix: "璁�",
voucherNum: "",
voucherDate: "",
attachmentCount: 0,
- entries: [],
- creator: "寮犱笁",
+ attachments: [],
+ entries: [createEmptyEntry(), createEmptyEntry()],
+ creator: getDefaultCreator(),
remark: "",
+});
+
+const form = reactive({
+ ...createDefaultForm(),
+});
+
+const userOptions = ref([]);
+
+const creatorOptions = computed(() => {
+ const source = [
+ ...userOptions.value.map(item => item.nickName || item.userName || item.name),
+ getDefaultCreator(),
+ form.creator,
+ filters.creator,
+ ];
+ return [...new Set(source.filter(Boolean))];
});
const selectedRowIndex = ref(-1);
@@ -276,12 +412,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 +434,79 @@
return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
+const normalizeVoucherStatus = status => String(status || "").toLowerCase();
+
+const canEditVoucher = status => {
+ const key = normalizeVoucherStatus(status);
+ return key === "unposted" || status === "鏈繃璐�";
+};
+
const getStatusLabel = (status) => {
+ const key = normalizeVoucherStatus(status);
const map = { unposted: "鏈繃璐�", posted: "宸茶繃璐�", cancelled: "宸蹭綔搴�" };
- return map[status] || status;
+ return map[key] || status;
};
const getStatusType = (status) => {
+ const key = normalizeVoucherStatus(status);
const map = { unposted: "warning", posted: "success", cancelled: "info" };
- return map[status] || "";
+ return map[key] || "";
};
-const getTableData = () => {
- let result = [...mockData];
- if (filters.voucherNo) {
- result = result.filter(item => item.voucherNo.includes(filters.voucherNo));
+// 鑱旇皟绾﹀畾锛氬垎椤靛弬鏁颁娇鐢� current/size锛屾棩鏈熻寖鍥存媶鍒嗕负 startDate/endDate
+const getTableData = async () => {
+ try {
+ const [startDate, endDate] =
+ filters.dateRange && filters.dateRange.length === 2 ? filters.dateRange : ["", ""];
+ const { data } = await listVoucherPage({
+ current: pagination.currentPage,
+ size: pagination.pageSize,
+ voucherNo: filters.voucherNo,
+ creator: filters.creator,
+ status: filters.status,
+ startDate,
+ endDate,
+ });
+ dataList.value = data?.records || [];
+ pagination.total = Number(data?.total || 0);
+ } catch (error) {
+ // 鎻愮ず鐢卞叏灞�璇锋眰鎷︽埅鍣ㄥ鐞嗭紝杩欓噷浠呴槻姝㈡湭鎹曡幏寮傚父
}
- if (filters.dateRange && filters.dateRange.length === 2) {
- result = result.filter(item => item.voucherDate >= filters.dateRange[0] && item.voucherDate <= filters.dateRange[1]);
+};
+
+// 鍑瘉鍒嗗綍閲岀殑绉戠洰涓嬫媺涓庢�昏处绉戠洰淇濇寔涓�鑷达紝閬垮厤鎻愪氦涓嶅瓨鍦ㄧ鐩�
+const loadSubjectList = async () => {
+ try {
+ const { data } = await listAccountSubject({
+ current: 1,
+ size: 1000,
+ status: 0
+ });
+ const flatList = [];
+ const treeOptions = buildSubjectTreeOptions(data?.records || [], flatList);
+ if (treeOptions.length > 0) {
+ subjectTreeOptions.value = treeOptions;
+ subjectList.value = flatList;
+ return;
+ }
+ const fallbackFlatList = [];
+ subjectTreeOptions.value = buildSubjectTreeOptions(fallbackSubjectTree, fallbackFlatList);
+ subjectList.value = fallbackFlatList;
+ } catch (error) {
+ // 鍏ㄥ眬鎷︽埅鍣ㄥ凡鎻愮ず閿欒锛岃繖閲屼繚鐣欓粯璁ょ鐩綔涓哄厹搴�
+ const fallbackFlatList = [];
+ subjectTreeOptions.value = buildSubjectTreeOptions(fallbackSubjectTree, fallbackFlatList);
+ subjectList.value = fallbackFlatList;
}
- if (filters.creator) {
- result = result.filter(item => item.creator === filters.creator);
+};
+
+const loadUserOptions = async () => {
+ try {
+ const { data } = await userListNoPageByTenantId();
+ userOptions.value = Array.isArray(data) ? data : [];
+ } catch (error) {
+ userOptions.value = [];
}
- if (filters.status) {
- result = result.filter(item => item.status === filters.status);
- }
- pagination.total = result.length;
- dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
@@ -348,7 +525,35 @@
};
const addEntry = () => {
- form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
+ if (isViewMode.value) {
+ return;
+ }
+ form.entries.push(createEmptyEntry());
+};
+
+const handleAttachmentChange = (fileList) => {
+ form.attachmentCount = fileList?.length || 0;
+};
+
+// 浣跨敤椤圭洰灏佽鐨� filePreview 缁勪欢棰勮鏂囦欢
+const previewFile = (row) => {
+ const url = row.previewURL || row.previewUrl || row.url;
+ if (url && filePreviewRef.value) {
+ filePreviewRef.value.open(url);
+ } else {
+ ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曢瑙�');
+ }
+};
+
+// 浣跨敤椤圭洰灏佽鐨� download 鎻掍欢涓嬭浇鏂囦欢
+const downloadFile = (row) => {
+ const url = row.downloadURL || row.downloadUrl || row.url;
+ if (url) {
+ const filename = row.originalFilename || row.name || row.fileName || 'download';
+ download.byUrl(url, filename);
+ } else {
+ ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曚笅杞�');
+ }
};
const selectRow = (index) => {
@@ -356,6 +561,9 @@
};
const openAmountInput = (index, type) => {
+ if (isViewMode.value) {
+ return;
+ }
editingCell.row = index;
editingCell.type = type;
nextTick(() => {
@@ -402,65 +610,86 @@
};
const removeEntry = (index) => {
+ if (isViewMode.value) {
+ return;
+ }
+ if (form.entries.length <= 2) {
+ return;
+ }
form.entries.splice(index, 1);
- calculateTotal();
};
const handleSubjectChange = (val, index) => {
- const subject = subjectList.find(item => item.code === val);
+ const subject = subjectList.value.find(item => item.code === val);
if (subject) {
form.entries[index].subjectName = subject.name;
+ form.entries[index].balanceDirection = subject.balanceDirection || "";
+ } else {
+ form.entries[index].subjectName = "";
+ form.entries[index].balanceDirection = "";
}
-};
-
-const calculateTotal = () => {
- // 鑷姩璁$畻锛岀敱computed灞炴�у鐞�
};
const add = () => {
+ dialogMode.value = "add";
isEdit.value = false;
+ currentId.value = null;
dialogTitle.value = "鏂板鍑瘉";
- const nextNum = String(mockData.length + 1).padStart(2, "0");
- Object.assign(form, {
- voucherNo: "璁�-" + nextNum,
+ const nextNum = String((pagination.total || 0) + 1).padStart(4, "0");
+ Object.assign(form, createDefaultForm(), {
voucherPrefix: "璁�",
voucherNum: nextNum,
+ voucherNo: `璁�-${nextNum}`,
voucherDate: new Date().toISOString().split('T')[0],
- attachmentCount: 0,
- entries: [
- { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
- { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
- { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
- { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
- ],
- creator: "寮犱笁",
- remark: "",
});
selectedRowIndex.value = 0;
dialogVisible.value = true;
};
-const edit = (row) => {
- isEdit.value = true;
- currentId.value = row.id;
- dialogTitle.value = "缂栬緫鍑瘉";
- const parts = row.voucherNo.split('-');
- Object.assign(form, {
- ...row,
- voucherPrefix: parts[0] || '璁�',
- voucherNum: parts[1] || '',
- });
- if (form.entries.length < 4) {
- while (form.entries.length < 4) {
- form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
+const openVoucherDialog = async (row, mode = "edit") => {
+ try {
+ dialogMode.value = mode;
+ isEdit.value = mode === "edit";
+ currentId.value = row.id;
+ dialogTitle.value = mode === "view" ? "鏌ョ湅鍑瘉" : "缂栬緫鍑瘉";
+ const { data } = await getVoucherDetail(row.id);
+ const detail = data || row;
+ const parts = (detail.voucherNo || "").split("-");
+ const attachments = detail.storageBlobVOList || detail.storageBlobDTOs || detail.attachments || [];
+ Object.assign(form, createDefaultForm(), {
+ ...detail,
+ voucherPrefix: parts[0] || "璁�",
+ voucherNum: parts[1] || "",
+ creator: detail.creator || getDefaultCreator(),
+ attachments,
+ entries:
+ detail.entries?.map(item => ({
+ subjectCode: item.subjectCode || "",
+ subjectName: item.subjectName || "",
+ balanceDirection: item.balanceDirection || "",
+ summary: item.summary || "",
+ debit: Number(item.debit || 0),
+ credit: Number(item.credit || 0),
+ })) || [],
+ });
+ if (form.entries.length < 2) {
+ while (form.entries.length < 2) {
+ form.entries.push(createEmptyEntry());
+ }
}
+ selectedRowIndex.value = 0;
+ dialogVisible.value = true;
+ } catch (error) {
+ // 鎻愮ず鐢卞叏灞�璇锋眰鎷︽埅鍣ㄥ鐞嗭紝杩欓噷浠呴槻姝㈡湭鎹曡幏寮傚父
}
- selectedRowIndex.value = 0;
- dialogVisible.value = true;
};
-const view = (row) => {
- ElMessage.info(`鏌ョ湅鍑瘉: ${row.voucherNo}`);
+const edit = async row => {
+ await openVoucherDialog(row, "edit");
+};
+
+const view = async row => {
+ await openVoucherDialog(row, "view");
};
const handlePost = (row) => {
@@ -468,13 +697,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 +709,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 +725,80 @@
};
const submitForm = () => {
- formRef.value.validate((valid) => {
+ if (isViewMode.value) {
+ dialogVisible.value = false;
+ return;
+ }
+ formRef.value.validate(async valid => {
if (valid) {
+ // 鍓嶇疆鏍¢獙锛氫笌鍚庣瑙勫垯瀵归綈锛屽噺灏戞棤鏁堣姹�
if (!isBalanced.value) {
ElMessage.error("鍊熻捶涓嶅钩琛★紝璇锋鏌ュ垎褰�");
return;
}
- const validEntries = form.entries.filter(e => e.subjectCode && (e.debit > 0 || e.credit > 0));
+ const validEntries = form.entries.filter(
+ entry => entry.subjectCode && (Number(entry.debit) > 0 || Number(entry.credit) > 0)
+ );
+ if (validEntries.length === 0) {
+ ElMessage.error("璇疯嚦灏戝~鍐欎竴鏉℃湁鏁堝垎褰�");
+ return;
+ }
+
+ const invalidEntry = validEntries.find(
+ entry => Number(entry.debit) > 0 && Number(entry.credit) > 0
+ );
+ if (invalidEntry) {
+ ElMessage.error("鍚屼竴鍒嗗綍涓嶈兘鍚屾椂濉啓鍊熸柟鍜岃捶鏂�");
+ return;
+ }
+
const summary = validEntries.find(e => e.debit > 0)?.summary || "";
const voucherNo = `${form.voucherPrefix}-${form.voucherNum}`;
const dataToSave = {
- ...form,
voucherNo,
+ voucherDate: form.voucherDate,
summary,
+ creator: form.creator,
+ attachmentCount: Number(form.attachmentCount || 0),
+ remark: form.remark,
debit: totalDebitEntry.value,
credit: totalCreditEntry.value,
- entries: validEntries,
+ storageBlobDTOs: form.attachments || [],
+ entries: validEntries.map(entry => ({
+ subjectCode: entry.subjectCode,
+ subjectName: entry.subjectName,
+ summary: entry.summary,
+ debit: Number(entry.debit || 0),
+ credit: Number(entry.credit || 0),
+ })),
};
- if (isEdit.value) {
- const index = mockData.findIndex(item => item.id === currentId.value);
- if (index !== -1) {
- mockData[index] = { ...mockData[index], ...dataToSave };
+ try {
+ if (isEdit.value) {
+ await updateVoucher({
+ id: currentId.value,
+ ...dataToSave,
+ });
+ ElMessage.success("缂栬緫鎴愬姛");
+ } else {
+ await addVoucher(dataToSave);
+ ElMessage.success("鏂板鎴愬姛");
}
- ElMessage.success("缂栬緫鎴愬姛");
- } else {
- const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
- mockData.push({ id: newId, ...dataToSave, status: "unposted" });
- ElMessage.success("鏂板鎴愬姛");
+ dialogVisible.value = false;
+ await getTableData();
+ } catch (error) {
+ // 鎻愮ず鐢卞叏灞�璇锋眰鎷︽埅鍣ㄥ鐞嗭紝杩欓噷浠呴槻姝㈡湭鎹曡幏寮傚父
}
- dialogVisible.value = false;
- getTableData();
}
});
};
-onMounted(() => {
- getTableData();
+onMounted(async () => {
+ await loadUserOptions();
+ await loadSubjectList();
+ await getTableData();
});
</script>
@@ -610,6 +868,21 @@
.voucher-attachment-section {
display: flex;
align-items: center;
+ }
+}
+
+.voucher-attachment-upload {
+ margin-top: 15px;
+ padding: 0 10px;
+
+ .attachment-label {
+ font-size: 14px;
+ color: #606266;
+ margin-bottom: 10px;
+ }
+
+ .attachment-table {
+ border-radius: 4px;
}
}
@@ -780,7 +1053,8 @@
.col-subject {
position: relative;
- .el-select {
+ .el-select,
+ .el-tree-select {
.el-input input {
font-size: 12px;
}
--
Gitblit v1.9.3