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 | 167 ++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 121 insertions(+), 46 deletions(-)
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
index 04b26ff..9d8b616 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
@@ -7,13 +7,16 @@
persistFinReimbursement,
} from "@/api/officeProcessAutomation/finReimbursement.js";
import { ElMessageBox } from "element-plus";
-import { userListNoPageByTenantId } from "@/api/system/user.js";
+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,
@@ -69,8 +72,9 @@
const searchForm = reactive({
applicantKeyword: "",
- applyTimeFrom: "",
- applyTimeTo: "",
+ deptId: undefined,
+ expenseCategory: "",
+ applyTimeRange: [],
});
const tableLoading = ref(false);
const page = reactive({ current: 1, size: 10, total: 0 });
@@ -78,6 +82,7 @@
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 });
@@ -106,11 +111,26 @@
})
);
const { records, total } = unwrapFinReimbursementPage(res);
- allRows.value = filterRowsByReimbursementType(
+ const filtered = filterRowsByReimbursementType(
records,
FIN_REIMBURSEMENT_TYPE.COST
- ).map(mapCostReimbursementRow);
- page.total = total;
+ );
+ 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;
@@ -135,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 },
@@ -178,6 +201,7 @@
]);
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" }],
@@ -206,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) {
@@ -228,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;
}
@@ -255,11 +296,46 @@
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("");
}
}
@@ -318,8 +394,6 @@
function resetSearch() {
searchForm.applicantKeyword = "";
- searchForm.applyTimeFrom = "";
- searchForm.applyTimeTo = "";
handleQuery();
}
@@ -417,17 +491,13 @@
proxy?.$modal?.msgError?.("鍔犺浇鎶ラ攢璇︽儏澶辫触");
return;
}
- Object.assign(form, {
- ...JSON.parse(JSON.stringify(editRow)),
- reimbursementId: editRow.reimbursementId ?? editRow.id,
- attachmentList: JSON.parse(JSON.stringify(editRow.attachmentList || editRow.invoiceAttachments || [])),
- approvalFlowNodes: JSON.parse(JSON.stringify(editRow.approvalFlowNodes || [])),
- expenseDetails: JSON.parse(JSON.stringify(editRow.expenseDetails || [])),
- });
+ Object.assign(form, buildCostReimbursementSaveDto(editRow));
+ formDialog.title = "缂栬緫璐圭敤鎶ラ攢";
+ await remoteSearchApplicantForm("");
const u = userById(editRow.applicantId);
- applicantFormOptions.value = u
- ? [u]
- : [{ userId: editRow.applicantId, nickName: editRow.employeeName, userName: editRow.employeeNo }];
+ if (!u) {
+ applicantFormOptions.value.unshift({ userId: editRow.applicantId, nickName: editRow.employeeName, userName: editRow.applicantCode });
+ }
} else {
form.approvalFlowNodes = buildAutoApprovalFlow(0, "other");
remoteSearchApplicantForm("");
@@ -518,15 +588,17 @@
}
function handleExport() {
- const data = allRows.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() {
@@ -558,6 +630,7 @@
onMounted(async () => {
loadUserPool();
+ loadDeptOptions();
await fetchList();
const editPayload = consumeReimburseEditFromApprove();
if (editPayload?.reimbursementId != null) {
@@ -589,6 +662,7 @@
approveOpinion,
applicantFormSearchLoading,
applicantFormOptions,
+ deptOptions,
flowUserOptions,
detailTotalAmount,
approvalRuleHint,
@@ -598,6 +672,7 @@
remoteSearchApplicantForm,
userSelectLabel,
onApplicantChange,
+ onDeptChange,
onExpenseCategoryChange,
applyTemplate,
onDetailAmountChange,
--
Gitblit v1.9.3