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 |  139 +++++++++++++++++++++++++++++++++-------------
 1 files changed, 100 insertions(+), 39 deletions(-)

diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
index 3b90a3b..9d8b616 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
@@ -7,7 +7,7 @@
   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,
@@ -72,6 +72,9 @@
 
   const searchForm = reactive({
     applicantKeyword: "",
+    deptId: undefined,
+    expenseCategory: "",
+    applyTimeRange: [],
   });
   const tableLoading = ref(false);
   const page = reactive({ current: 1, size: 10, total: 0 });
@@ -79,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 });
@@ -151,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 },
@@ -194,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" }],
@@ -222,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) {
@@ -244,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;
     }
@@ -271,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("");
     }
   }
 
@@ -431,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("");
@@ -532,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() {
@@ -572,6 +630,7 @@
 
   onMounted(async () => {
     loadUserPool();
+    loadDeptOptions();
     await fetchList();
     const editPayload = consumeReimburseEditFromApprove();
     if (editPayload?.reimbursementId != null) {
@@ -603,6 +662,7 @@
     approveOpinion,
     applicantFormSearchLoading,
     applicantFormOptions,
+    deptOptions,
     flowUserOptions,
     detailTotalAmount,
     approvalRuleHint,
@@ -612,6 +672,7 @@
     remoteSearchApplicantForm,
     userSelectLabel,
     onApplicantChange,
+    onDeptChange,
     onExpenseCategoryChange,
     applyTemplate,
     onDetailAmountChange,

--
Gitblit v1.9.3