From 2d3530a7c11557a40807ad7c0b9e302ce6ce3a9f Mon Sep 17 00:00:00 2001
From: hsy <1029150275@qq.com>
Date: 星期五, 28 八月 2026 10:16:32 +0800
Subject: [PATCH] feat: 综合业务模块页面重构与交互完善
---
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js | 541 ++++++++++++++++++++++++++++-------------------------
1 files changed, 287 insertions(+), 254 deletions(-)
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
index 79ffe6b..9d8b616 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
@@ -1,7 +1,33 @@
import { Search } from "@element-plus/icons-vue";
import dayjs from "dayjs";
-import { userListNoPageByTenantId } from "@/api/system/user.js";
-import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, watch } from "vue";
+import {
+ deleteFinReimbursement,
+ getFinReimbursementDetail,
+ listFinReimbursementPage,
+ persistFinReimbursement,
+} from "@/api/officeProcessAutomation/finReimbursement.js";
+import { ElMessageBox } from "element-plus";
+import { userListNoPageByTenantId, deptTreeSelect } from "@/api/system/user.js";
+import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref } from "vue";
+import {
+ buildCostReimbursementSaveDto,
+ buildFinReimbursementListParams,
+ filterReimbursementRowsBySearch,
+ hasActiveReimbursementSearch,
+ canDeleteReimbursementRow,
+ canEditReimbursementRow,
+ enrichReimbursementListRowsWithApprovalFlow,
+ filterRowsByReimbursementType,
+ FIN_REIMBURSEMENT_TYPE,
+ mapCostReimbursementRow,
+ mapFinReimbursementDetailRow,
+ resolveReimbursementDeleteId,
+ unwrapFinReimbursementDetail,
+ unwrapFinReimbursementPage,
+ validateReimbursementApprovalNodes,
+ validateReimbursementPersistDto,
+} from "../shared/finReimbursementMappers.js";
+import { consumeReimburseEditFromApprove } from "../shared/reimburseApproveBridge.js";
import {
EXPENSE_CATEGORY_OPTIONS,
CATEGORY_TEMPLATES,
@@ -42,110 +68,13 @@
export function useCostReimburse() {
const { proxy } = getCurrentInstance();
- const allRows = ref([
- {
- id: "1",
- reimburseNo: "CR202605100001",
- applicantId: "mock_1",
- employeeNo: "zhangsan",
- employeeName: "寮犱笁",
- applicantNo: "zhangsan",
- applicantName: "寮犱笁",
- expenseCategory: "office_procurement",
- reimburseReason: "閲囪喘鎵撳嵃鏈虹榧撱�丄4绾哥瓑鍔炲叕鑰楁潗銆�",
- applyAmount: 680,
- payee: "寮犱笁",
- payeeAccount: "6222 **** **** 1234",
- bankBranch: "涓浗宸ュ晢閾惰鏉窞瑗挎箹鏀",
- expenseDetails: [
- { id: "d1", invoiceDate: "2026-05-08", expenseSubject: "office_supply", amount: 380, description: "A4澶嶅嵃绾�" },
- { id: "d2", invoiceDate: "2026-05-08", expenseSubject: "office_supply", amount: 300, description: "纭掗紦" },
- ],
- attachmentList: [{ name: "閲囪喘鍙戠エ.pdf", url: "/mock/invoice1.pdf" }],
- approvalFlowNodes: demoFlowNodes(680, "office_procurement"),
- currentNodeIndex: 0,
- approvalResult: "pending",
- rejectReason: "",
- approvalRecords: [],
- applyTime: "2026-05-10 09:15:00",
- createTime: "2026-05-10 09:15:00",
- deptId: "101",
- deptName: "琛屾斂閮�",
- },
- {
- id: "2",
- reimburseNo: "CR202605080002",
- applicantId: "mock_2",
- employeeNo: "lisi",
- employeeName: "鏉庡洓",
- applicantNo: "lisi",
- applicantName: "鏉庡洓",
- expenseCategory: "business_entertainment",
- reimburseReason: "鎺ュ緟閲嶇偣瀹㈡埛鍟嗗姟瀹磋銆�",
- applyAmount: 3200,
- payee: "鏉庡洓",
- payeeAccount: "6217 **** **** 5678",
- bankBranch: "鎷涘晢閾惰姝︽眽鍏夎胺鏀",
- expenseDetails: [
- { id: "d3", invoiceDate: "2026-05-06", expenseSubject: "entertainment", amount: 3200, description: "瀹㈡埛瀹磋" },
- ],
- attachmentList: [],
- approvalFlowNodes: demoFlowNodes(3200, "business_entertainment").map((n, i) => ({
- ...n,
- nodeStatus: i === 0 ? "error" : "wait",
- approveOpinion: i === 0 ? "鍙戠エ妯$硦闇�閲嶄紶" : "",
- approveTime: i === 0 ? "2026-05-09 14:20:00" : "",
- })),
- currentNodeIndex: 0,
- approvalResult: "rejected",
- rejectReason: "鍙戠エ妯$硦闇�閲嶄紶",
- approvalRecords: [
- { operatorName: "鐩村睘涓婄骇", result: "rejected", opinion: "鍙戠エ妯$硦闇�閲嶄紶", time: "2026-05-09 14:20:00" },
- ],
- applyTime: "2026-05-07 16:30:00",
- createTime: "2026-05-07 16:30:00",
- deptId: "102",
- deptName: "閿�鍞儴",
- },
- {
- id: "3",
- reimburseNo: "CR202605050003",
- applicantId: "mock_3",
- employeeNo: "wangwu",
- employeeName: "鐜嬩簲",
- applicantNo: "wangwu",
- applicantName: "鐜嬩簲",
- expenseCategory: "communication",
- reimburseReason: "5鏈堝洜鍏瘽璐规姤閿�銆�",
- applyAmount: 198,
- payee: "鐜嬩簲",
- payeeAccount: "6228 **** **** 9012",
- bankBranch: "涓浗寤鸿閾惰鎴愰兘楂樻柊鏀",
- expenseDetails: [
- { id: "d4", invoiceDate: "2026-05-05", expenseSubject: "phone", amount: 198, description: "璇濊垂璐﹀崟" },
- ],
- attachmentList: [{ name: "璇濊垂璐﹀崟.jpg", url: "/mock/phone.jpg" }],
- approvalFlowNodes: demoFlowNodes(198, "communication").map((n) => ({
- ...n,
- nodeStatus: "finish",
- approveOpinion: "鍚屾剰",
- approveTime: "2026-05-06 10:00:00",
- })),
- currentNodeIndex: 0,
- approvalResult: "approved",
- rejectReason: "",
- approvalRecords: [{ operatorName: "鐩村睘涓婄骇", result: "approved", opinion: "鍚屾剰", time: "2026-05-06 10:00:00" }],
- applyTime: "2026-05-05 11:00:00",
- createTime: "2026-05-05 11:00:00",
- deptId: "103",
- deptName: "鎶�鏈儴",
- },
- ]);
+ const allRows = ref([]);
const searchForm = reactive({
applicantKeyword: "",
- applyTimeFrom: "",
- applyTimeTo: "",
+ deptId: undefined,
+ expenseCategory: "",
+ applyTimeRange: [],
});
const tableLoading = ref(false);
const page = reactive({ current: 1, size: 10, total: 0 });
@@ -153,56 +82,63 @@
const allUsersCache = ref([]);
const applicantFormSearchLoading = ref(false);
const applicantFormOptions = ref([]);
+ const deptOptions = ref([]);
const formRef = ref();
const form = reactive(createEmptyForm());
const formDialog = reactive({ visible: false, title: "", mode: "add", readonly: false });
const detailDialog = reactive({ visible: false });
+ const detailLoading = ref(false);
const detailRow = ref({});
const approveDialog = reactive({ visible: false, row: null });
const approveOpinion = ref("");
+ const submitSaving = ref(false);
- const filteredList = computed(() => {
- let list = [...allRows.value];
- const kw = (searchForm.applicantKeyword || "").trim().toLowerCase();
- if (kw) {
- list = list.filter((r) => {
- const name = (r.applicantName || r.employeeName || "").toLowerCase();
- const no = (r.applicantNo || r.employeeNo || "").toLowerCase();
- return name.includes(kw) || no.includes(kw);
- });
- }
- if (searchForm.applyTimeFrom) {
- list = list.filter((r) => {
- const t = (r.applyTime || r.createTime || "").slice(0, 10);
- return !t || t >= searchForm.applyTimeFrom;
- });
- }
- if (searchForm.applyTimeTo) {
- list = list.filter((r) => {
- const t = (r.applyTime || r.createTime || "").slice(0, 10);
- return !t || t <= searchForm.applyTimeTo;
- });
- }
- return list.sort((a, b) => (String(a.createTime) < String(b.createTime) ? 1 : -1));
- });
-
- watch(
- filteredList,
- (list) => {
- page.total = list.length;
- const maxPage = Math.max(1, Math.ceil(list.length / page.size) || 1);
- if (page.current > maxPage) page.current = maxPage;
- },
- { immediate: true }
- );
-
- const tableData = computed(() => {
- const start = (page.current - 1) * page.size;
- return filteredList.value.slice(start, start + page.size).map((r) => ({
+ const tableData = computed(() =>
+ allRows.value.map((r) => ({
...r,
approvalFlowSummary: formatApprovalFlowSummary(r),
- }));
- });
+ }))
+ );
+
+ async function fetchList() {
+ tableLoading.value = true;
+ try {
+ const res = await listFinReimbursementPage(
+ buildFinReimbursementListParams({
+ page,
+ searchForm,
+ reimbursementType: FIN_REIMBURSEMENT_TYPE.COST,
+ })
+ );
+ const { records, total } = unwrapFinReimbursementPage(res);
+ const filtered = filterRowsByReimbursementType(
+ records,
+ FIN_REIMBURSEMENT_TYPE.COST
+ );
+ let mapped = filtered.map(mapCostReimbursementRow);
+ mapped = await enrichReimbursementListRowsWithApprovalFlow(
+ mapped,
+ FIN_REIMBURSEMENT_TYPE.COST
+ );
+ if (hasActiveReimbursementSearch(searchForm)) {
+ mapped = filterReimbursementRowsBySearch(mapped, searchForm);
+ }
+ allRows.value = mapped;
+ const dropped = records.length - filtered.length;
+ let nextTotal =
+ dropped > 0 ? Math.max(0, Number(total) - dropped) : Number(total);
+ if (hasActiveReimbursementSearch(searchForm)) {
+ nextTotal = mapped.length;
+ }
+ page.total = nextTotal;
+ } catch {
+ allRows.value = [];
+ page.total = 0;
+ proxy?.$modal?.msgError?.("璐圭敤鎶ラ攢鍒楄〃鍔犺浇澶辫触");
+ } finally {
+ tableLoading.value = false;
+ }
+ }
const flowUserOptions = computed(() => allUsersCache.value.filter(isActiveUser));
@@ -219,6 +155,9 @@
{ label: "鎶ラ攢鍗曞彿", prop: "reimburseNo", width: 150 },
{ label: "鐢宠浜虹紪鍙�", prop: "applicantNo", width: 110 },
{ label: "鐢宠浜�", prop: "applicantName", minWidth: 90 },
+ { label: "閮ㄩ棬", prop: "applicantDeptName", minWidth: 100 },
+ { label: "璐圭敤绫诲瀷", prop: "expenseCategory", minWidth: 90, formatData: (v) => expenseCategoryLabel(v) },
+ { label: "浠樻鏂�", prop: "payer", minWidth: 100 },
{ label: "鎶ラ攢閲戦(鍏�)", prop: "applyAmount", width: 110 },
{ label: "鎶ラ攢鍘熷洜", prop: "reimburseReason", minWidth: 160, showOverflowTooltip: true },
{ label: "鐢宠鏃堕棿", prop: "applyTime", width: 165 },
@@ -247,21 +186,22 @@
{
name: "缂栬緫",
type: "text",
- disabled: (row) => row.approvalResult === "pending" || row.approvalResult === "approved",
+ disabled: (row) => !canEditReimbursementRow(row),
clickFun: (row) => openFormDialog("edit", row),
},
{ name: "璇︽儏", type: "text", clickFun: (row) => openDetail(row) },
{
- name: "瀹℃壒",
- type: "text",
- disabled: (row) => row.approvalResult !== "pending",
- clickFun: (row) => openApprove(row),
+ name: "鍒犻櫎",
+ type: "danger",
+ disabled: (row) => !canDeleteReimbursementRow(row),
+ clickFun: (row) => confirmRemoveRow(row),
},
],
},
]);
const formRules = {
+ deptId: [{ required: true, message: "璇烽�夋嫨褰掑睘閮ㄩ棬", trigger: "change" }],
applicantId: [{ required: true, message: "璇烽�夋嫨鍛樺伐", trigger: "change" }],
expenseCategory: [{ required: true, message: "璇烽�夋嫨璐圭敤绫诲瀷", trigger: "change" }],
reimburseReason: [{ required: true, message: "璇峰~鍐欐姤閿�鍘熷洜", trigger: "blur" }],
@@ -290,21 +230,39 @@
async function loadUserPool() {
try {
- allUsersCache.value = unwrapArray(await userListNoPageByTenantId());
+ allUsersCache.value = unwrapArray(await userListNoPageByTenantId({}));
} catch {
allUsersCache.value = [];
+ }
+ }
+
+ function formatDeptOptions(list) {
+ return (list || []).map((d) => ({
+ ...d,
+ rawLabel: d.label,
+ label: d.label, // 閮ㄩ棬涓嬫媺涓嶈鏄剧ずid
+ children: d.children && d.children.length ? formatDeptOptions(d.children) : undefined,
+ }));
+ }
+
+ async function loadDeptOptions() {
+ try {
+ const res = await deptTreeSelect();
+ deptOptions.value = formatDeptOptions(res.data || []);
+ } catch {
+ deptOptions.value = [];
}
}
function userSelectLabel(u) {
const nick = u.nickName || "";
const name = u.userName || "";
- if (nick && name && nick !== name) return `${nick}锛�${name}锛塦;
- return nick || name || `鐢ㄦ埛${u.userId ?? u.id ?? ""}`;
+ const empNo = employeeNoFromUser(u);
+ return `${nick || name || "鐢ㄦ埛"} - ${empNo}`;
}
function userById(id) {
- return allUsersCache.value.find((u) => String(u.userId ?? u.id) === String(id));
+ return applicantFormOptions.value.find((u) => String(u.userId ?? u.id) === String(id));
}
function employeeNoFromUser(u) {
@@ -312,22 +270,21 @@
return u.userName ?? u.userCode ?? u.jobNumber ?? u.workNo ?? (u.userId != null ? String(u.userId) : "");
}
- function filterUsersByQuery(query) {
- const list = allUsersCache.value.filter(isActiveUser);
- const q = (query || "").trim().toLowerCase();
- if (!q) return [...list];
- return list.filter((u) => {
- const nick = (u.nickName || "").toLowerCase();
- const uname = (u.userName || "").toLowerCase();
- return nick.includes(q) || uname.includes(q);
- });
- }
-
async function remoteSearchApplicantForm(query) {
applicantFormSearchLoading.value = true;
try {
- if (!allUsersCache.value.length) await loadUserPool();
- applicantFormOptions.value = filterUsersByQuery(query);
+ const q = (query || "").trim();
+ const params = {};
+ if (form.deptId) {
+ params.deptId = form.deptId;
+ }
+ if (q) {
+ params.nickName = q; // You can pass nickName to backend
+ }
+ const res = await userListNoPageByTenantId(params);
+ applicantFormOptions.value = unwrapArray(res).filter(isActiveUser);
+ } catch {
+ applicantFormOptions.value = [];
} finally {
applicantFormSearchLoading.value = false;
}
@@ -339,17 +296,56 @@
form.employeeName = u.nickName || u.userName || "";
form.employeeNo = employeeNoFromUser(u);
form.payee = form.payee || form.employeeName;
- form.deptId = String(u.deptId ?? u.sysDeptId ?? "");
- form.deptName = u.dept?.deptName ?? u.deptName ?? "";
+ const userDeptId = String(u.deptId ?? u.sysDeptId ?? "");
+ if (userDeptId && form.deptId !== userDeptId) {
+ form.deptId = userDeptId;
+ const name = findDeptLabel(deptOptions.value, userDeptId);
+ form.deptName = name || (u.dept?.deptName ?? u.deptName ?? "");
+ }
} else {
form.employeeName = "";
form.employeeNo = "";
}
}
+ function findDeptLabel(nodes, id) {
+ if (!nodes) return null;
+ for (const node of nodes) {
+ if (String(node.id) === String(id)) return node.rawLabel || node.label;
+ if (node.children) {
+ const hit = findDeptLabel(node.children, id);
+ if (hit) return hit;
+ }
+ }
+ return null;
+ }
+
+ function onDeptChange(deptId) {
+ if (deptId) {
+ const name = findDeptLabel(deptOptions.value, deptId);
+ if (name) form.deptName = name;
+
+ remoteSearchApplicantForm("");
+ if (form.applicantId) {
+ const u = userById(form.applicantId);
+ if (u && String(u.deptId ?? u.sysDeptId) !== String(deptId)) {
+ form.applicantId = "";
+ onApplicantChange("");
+ }
+ }
+ } else {
+ form.deptName = "";
+ remoteSearchApplicantForm("");
+ }
+ }
+
function autoAssignApprovalFlow() {
const amount = Number(form.applyAmount) || detailTotalAmount.value || 0;
- form.approvalFlowNodes = buildAutoApprovalFlow(amount, form.expenseCategory || "other");
+ form.approvalFlowNodes = buildAutoApprovalFlow(
+ amount,
+ form.expenseCategory || "other",
+ form.approvalFlowNodes
+ );
nextTick(() => formRef.value?.validateField?.("approvalFlowNodes"));
}
@@ -393,27 +389,81 @@
function handleQuery() {
page.current = 1;
- tableLoading.value = true;
- setTimeout(() => {
- tableLoading.value = false;
- }, 150);
+ return fetchList();
}
function resetSearch() {
searchForm.applicantKeyword = "";
- searchForm.applyTimeFrom = "";
- searchForm.applyTimeTo = "";
handleQuery();
}
function pagination(obj) {
page.current = obj.page;
page.size = obj.limit;
+ return fetchList();
}
- function openDetail(row) {
- detailRow.value = { ...row };
+ async function loadCostDetailRow(row) {
+ const id = resolveReimbursementDeleteId(row);
+ if (id == null) {
+ throw new Error("missing id");
+ }
+ const res = await getFinReimbursementDetail(id);
+ const raw = unwrapFinReimbursementDetail(res);
+ return mapFinReimbursementDetailRow(raw, FIN_REIMBURSEMENT_TYPE.COST);
+ }
+
+ async function openDetail(row) {
+ const id = resolveReimbursementDeleteId(row);
+ if (id == null) {
+ proxy?.$modal?.msgWarning?.("鏃犳硶鏌ョ湅璇︽儏锛氱己灏戞姤閿�鍗� ID");
+ return;
+ }
detailDialog.visible = true;
+ detailLoading.value = true;
+ detailRow.value = { ...row };
+ try {
+ detailRow.value = await loadCostDetailRow(row);
+ } catch {
+ proxy?.$modal?.msgError?.("鍔犺浇璇︽儏澶辫触");
+ detailDialog.visible = false;
+ } finally {
+ detailLoading.value = false;
+ }
+ }
+
+ async function confirmRemoveRow(row) {
+ const id = resolveReimbursementDeleteId(row);
+ if (id == null) {
+ proxy?.$modal?.msgWarning?.("鏃犳硶鍒犻櫎锛氱己灏戞姤閿�鍗� ID");
+ return;
+ }
+ const title = row.reimburseNo || row.billNo || row.reimburseReason || "璇ユ姤閿�鍗�";
+ try {
+ await ElMessageBox.confirm(
+ `纭畾瑕佸垹闄ゃ��${title}銆嶅悧锛熷垹闄ゅ悗涓嶅彲鎭㈠銆俙,
+ "鍒犻櫎纭",
+ {
+ type: "warning",
+ confirmButtonText: "纭畾鍒犻櫎",
+ cancelButtonText: "鍙栨秷",
+ distinguishCancelAndClose: true,
+ autofocus: false,
+ }
+ );
+ } catch {
+ return;
+ }
+ try {
+ await deleteFinReimbursement([id]);
+ proxy?.$modal?.msgSuccess?.("鍒犻櫎鎴愬姛");
+ if (detailDialog.visible && resolveReimbursementDeleteId(detailRow.value) === id) {
+ detailDialog.visible = false;
+ }
+ await handleQuery();
+ } catch {
+ proxy?.$modal?.msgError?.("鍒犻櫎澶辫触");
+ }
}
function openApprove(row) {
@@ -434,16 +484,20 @@
if (!allUsersCache.value.length) await loadUserPool();
Object.assign(form, createEmptyForm());
if (mode === "edit" && row) {
- Object.assign(form, {
- ...JSON.parse(JSON.stringify(row)),
- attachmentList: JSON.parse(JSON.stringify(row.attachmentList || row.invoiceAttachments || [])),
- approvalFlowNodes: JSON.parse(JSON.stringify(row.approvalFlowNodes || [])),
- expenseDetails: JSON.parse(JSON.stringify(row.expenseDetails || [])),
- });
- const u = userById(row.applicantId);
- applicantFormOptions.value = u
- ? [u]
- : [{ userId: row.applicantId, nickName: row.employeeName, userName: row.employeeNo }];
+ let editRow = row;
+ try {
+ editRow = await loadCostDetailRow(row);
+ } catch {
+ proxy?.$modal?.msgError?.("鍔犺浇鎶ラ攢璇︽儏澶辫触");
+ return;
+ }
+ Object.assign(form, buildCostReimbursementSaveDto(editRow));
+ formDialog.title = "缂栬緫璐圭敤鎶ラ攢";
+ await remoteSearchApplicantForm("");
+ const u = userById(editRow.applicantId);
+ if (!u) {
+ applicantFormOptions.value.unshift({ userId: editRow.applicantId, nickName: editRow.employeeName, userName: editRow.applicantCode });
+ }
} else {
form.approvalFlowNodes = buildAutoApprovalFlow(0, "other");
remoteSearchApplicantForm("");
@@ -469,66 +523,31 @@
return;
}
syncApplyAmountFromDetails();
- autoAssignApprovalFlow();
- const payload = {
- reimburseNo: form.reimburseNo || `CR${dayjs().format("YYYYMMDDHHmmss")}`,
- applicantId: form.applicantId,
- employeeNo: form.employeeNo,
- employeeName: form.employeeName,
- applicantNo: form.employeeNo,
- applicantName: form.employeeName,
- expenseCategory: form.expenseCategory,
- reimburseReason: form.reimburseReason,
- applyAmount: form.applyAmount,
- payee: form.payee,
- payeeAccount: form.payeeAccount,
- bankBranch: form.bankBranch,
- expenseDetails: JSON.parse(JSON.stringify(form.expenseDetails)),
- attachmentList: JSON.parse(JSON.stringify(form.attachmentList || [])),
- invoiceAttachments: (form.attachmentList || []).map((f, i) => ({
- id: f.id ?? f.uid ?? `inv_${Date.now()}_${i}`,
- name: f.name || f.fileName || "鏈懡鍚�",
- url: f.url || f.downloadURL || "",
- })),
- approvalFlowNodes: initApprovalFlowNodes(form.approvalFlowNodes),
- currentNodeIndex: 0,
- deptId: form.deptId,
- deptName: form.deptName,
- };
-
- if (formDialog.mode === "add") {
- const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
- allRows.value.unshift({
- id: `local_${Date.now()}`,
- ...payload,
- approvalResult: "pending",
- rejectReason: "",
- approvalRecords: [],
- applyTime: now,
- createTime: now,
- });
- proxy?.$modal?.msgSuccess?.("鎻愪氦鎴愬姛锛屽凡杩涘叆瀹℃壒锛堟湰鍦版ā鎷燂級");
- } else {
- const idx = allRows.value.findIndex((r) => r.id === form.id);
- if (idx !== -1) {
- const prev = allRows.value[idx];
- allRows.value[idx] = {
- ...prev,
- ...payload,
- id: form.id,
- approvalResult: prev.approvalResult === "rejected" ? "pending" : prev.approvalResult,
- approvalFlowNodes: initApprovalFlowNodes(form.approvalFlowNodes),
- currentNodeIndex: 0,
- rejectReason: prev.approvalResult === "rejected" ? "" : prev.rejectReason,
- applyTime: prev.applyTime,
- createTime: prev.createTime,
- };
- }
- proxy?.$modal?.msgSuccess?.("淇濆瓨鎴愬姛锛堟湰鍦版ā鎷燂級");
+ if (submitSaving.value) return;
+ const isEdit = formDialog.mode === "edit";
+ const dto = buildCostReimbursementSaveDto(form);
+ const check = validateReimbursementPersistDto(dto, isEdit);
+ if (!check.ok) {
+ proxy?.$modal?.msgWarning?.(check.message);
+ return;
}
- formDialog.visible = false;
- handleQuery();
+ const nodeCheck = validateReimbursementApprovalNodes(dto);
+ if (!nodeCheck.ok) {
+ proxy?.$modal?.msgWarning?.(nodeCheck.message);
+ return;
+ }
+ submitSaving.value = true;
+ try {
+ await persistFinReimbursement(dto, isEdit);
+ proxy?.$modal?.msgSuccess?.(isEdit ? "淇濆瓨鎴愬姛" : "鎻愪氦鎴愬姛");
+ formDialog.visible = false;
+ await handleQuery();
+ } catch {
+ proxy?.$modal?.msgError?.(isEdit ? "淇濆瓨澶辫触" : "鎻愪氦澶辫触");
+ } finally {
+ submitSaving.value = false;
+ }
}
async function submitApprove(result) {
@@ -569,15 +588,17 @@
}
function handleExport() {
- const data = filteredList.value;
- const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json;charset=utf-8" });
- const url = URL.createObjectURL(blob);
- const a = document.createElement("a");
- a.href = url;
- a.download = `璐圭敤鎶ラ攢瀵煎嚭_${dayjs().format("YYYYMMDDHHmmss")}.json`;
- a.click();
- URL.revokeObjectURL(url);
- proxy?.$modal?.msgSuccess?.(`宸插鍑� ${data.length} 鏉);
+ const params = buildFinReimbursementListParams({
+ page: { current: 1, size: 99999 }, // Just so the function doesn't crash, backend export ignores pagination anyway
+ searchForm,
+ reimbursementType: FIN_REIMBURSEMENT_TYPE.COST,
+ });
+ // Remove pagination parameters since export doesn't need them
+ delete params.current;
+ delete params.size;
+ delete params["page.current"];
+ delete params["page.size"];
+ proxy.download("/finReimbursement/export", params, `璐圭敤鎶ラ攢瀵煎嚭_${dayjs().format("YYYYMMDDHHmmss")}.xlsx`);
}
function handleImportClick() {
@@ -607,7 +628,15 @@
reader.readAsText(file, "utf-8");
}
- onMounted(() => loadUserPool());
+ onMounted(async () => {
+ loadUserPool();
+ loadDeptOptions();
+ await fetchList();
+ const editPayload = consumeReimburseEditFromApprove();
+ if (editPayload?.reimbursementId != null) {
+ await openFormDialog("edit", { reimbursementId: editPayload.reimbursementId });
+ }
+ });
return {
Search,
@@ -627,11 +656,13 @@
formDialog,
formRules,
detailDialog,
+ detailLoading,
detailRow,
approveDialog,
approveOpinion,
applicantFormSearchLoading,
applicantFormOptions,
+ deptOptions,
flowUserOptions,
detailTotalAmount,
approvalRuleHint,
@@ -641,6 +672,7 @@
remoteSearchApplicantForm,
userSelectLabel,
onApplicantChange,
+ onDeptChange,
onExpenseCategoryChange,
applyTemplate,
onDetailAmountChange,
@@ -652,6 +684,7 @@
openFormDialog,
onFormClosed,
submitForm,
+ submitSaving,
openDetail,
approvalActionLabel,
submitApprove,
--
Gitblit v1.9.3