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/officeProcessAutomation/ReimburseManage/travel-reimburse/index.vue                      |   20 --
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js |   11 +
 src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js               |   85 +++++----
 src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue                           |   22 +
 src/views/officeProcessAutomation/HrManage/work-handover/index.vue                                |   28 +-
 src/views/officeProcessAutomation/ApproveManage/approve-shared/useApprovalInstanceModule.js       |   14 +
 src/views/officeProcessAutomation/HrManage/regular-apply/index.vue                                |   30 +-
 src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/useTravelReimburse.js          |   15 +
 src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue                              |   22 +
 src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue                               |   33 +--
 src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js              |   15 +
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js      |  136 +++++++++++++++
 src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js              |   41 ++--
 src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue                        |   20 --
 14 files changed, 317 insertions(+), 175 deletions(-)

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
index 96f7158..938a787 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
@@ -12,6 +12,10 @@
   isDynamicOptionSource,
   resolveSelectDisplayLabel,
 } from "../approve-template/selectOptionSource.js";
+import {
+  appendDotNotationQuery,
+  buildApprovalInstanceSearchDto,
+} from "../approve-shared/approvalInstanceListSearch.js";
 
 /** 瀹℃壒绫诲瀷锛堜笌鍚庣瀛楁 approvalType 瀵归綈锛屽悗鏈熷彲鍚屾锛� */
 export const APPROVAL_TYPE_OPTIONS = [
@@ -520,35 +524,26 @@
   };
 }
 
-export function buildApprovalInstanceListParams({ page, searchForm, businessType, extraParams }) {
-  const extra = { ...(extraParams && typeof extraParams === "object" ? extraParams : {}) };
-  if (extra.createTime != null && extra.createTimeStart == null) {
-    extra.createTimeStart = extra.createTime;
+export function buildApprovalInstanceListParams({
+  page,
+  searchForm,
+  businessType,
+  extraParams,
+}) {
+  const dto = buildApprovalInstanceSearchDto(searchForm, extraParams);
+  const bizType = businessType ?? searchForm?.businessType;
+  if (bizType != null && bizType !== "") {
+    dto.businessType = bizType;
   }
-  delete extra.createTime;
 
   const params = {
     current: page.current,
     size: page.size,
-    ...extra,
+    "page.current": page.current,
+    "page.size": page.size,
+    ...dto,
   };
-  const bizType = businessType ?? searchForm?.businessType;
-  if (bizType != null && bizType !== "") {
-    params.businessType = bizType;
-  }
-  if (searchForm?.status) {
-    params.status = searchForm.status;
-  }
-  const range =
-    searchForm?.createTimeRange ??
-    searchForm?.applyDateRange ??
-    searchForm?.transferDateRange;
-  if (Array.isArray(range) && range[0]) {
-    params.createTimeStart = range[0];
-  }
-  if (Array.isArray(range) && range[1]) {
-    params.createTimeEnd = range[1];
-  }
+  appendDotNotationQuery(params, "approvalInstanceDto", dto);
   return params;
 }
 
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
index fef178f..0251647 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
@@ -5,6 +5,10 @@
   formatFieldDisplayValue,
   resolveInstanceFormFields,
 } from "../approve-list/approveListConstants.js";
+import {
+  INSTANCE_NO_SEARCH_MODULE_KEYS,
+  INSTANCE_NO_TABLE_COLUMN,
+} from "./approvalInstanceListSearch.js";
 
 /** 鍒楄〃/璇︽儏涓嶅洖鏄句负鐙珛鍒楃殑濉姤椤� key锛堥伩鍏嶈鐩栧疄渚嬬郴缁熷瓧娈碉級 */
 const DEFAULT_EXCLUDE_KEYS = new Set([
@@ -101,6 +105,7 @@
  */
 export function buildInstanceTableColumns(tableDataRef, buildTableActions, options = {}) {
   const {
+    moduleKey,
     excludeKeys = DEFAULT_EXCLUDE_KEYS,
     beforeFormColumns = [],
     extraColumns = [],
@@ -108,9 +113,15 @@
     actionWidth = 260,
   } = options;
 
+  const leadingCols =
+    moduleKey && INSTANCE_NO_SEARCH_MODULE_KEYS.has(moduleKey)
+      ? [INSTANCE_NO_TABLE_COLUMN]
+      : [];
+
   return computed(() => {
     const formCols = getFormConfigFieldColumns(tableDataRef.value?.[0], { excludeKeys });
     return [
+      ...leadingCols,
       ...beforeFormColumns,
       ...formCols,
       ...extraColumns,
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js
new file mode 100644
index 0000000..3042d0c
--- /dev/null
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js
@@ -0,0 +1,136 @@
+import { APPROVAL_MODULE_KEYS } from "./approvalModuleRegistry.js";
+
+/** 鏀寔瀹℃壒鍗曞彿鏌ヨ/涓昏〃灞曠ず鐨勫鎵圭敵璇锋ā鍧� */
+export const INSTANCE_NO_SEARCH_MODULE_KEYS = new Set([
+  APPROVAL_MODULE_KEYS.REGULAR,
+  APPROVAL_MODULE_KEYS.TRANSFER,
+  APPROVAL_MODULE_KEYS.WORK_HANDOVER,
+  APPROVAL_MODULE_KEYS.LEAVE,
+  APPROVAL_MODULE_KEYS.OVERTIME,
+]);
+
+export const INSTANCE_NO_TABLE_COLUMN = {
+  label: "瀹℃壒鍗曞彿",
+  prop: "instanceNo",
+  width: 170,
+  showOverflowTooltip: true,
+};
+
+/** 鎵佸钩鍖栦负 Spring GET 鍙粦瀹氱殑 query锛坅pprovalInstanceDto.xxx锛屽嬁鐢ㄦ柟鎷彿锛� */
+export function appendDotNotationQuery(target, prefix, fields) {
+  if (!fields || typeof fields !== "object") return;
+  for (const [key, value] of Object.entries(fields)) {
+    if (value == null || value === "") continue;
+    target[`${prefix}.${key}`] = value;
+  }
+}
+
+function pickApplicantFromSearchForm(searchForm = {}) {
+  const out = {};
+  const sf = searchForm || {};
+  const name = (sf.applicantName || "").trim();
+  const kw = (sf.applicantKeyword || "").trim();
+  const id = sf.applicantId;
+
+  if (name) out.applicantName = name;
+  if (kw) {
+    out.applicantName = kw;
+    if (/^\d+$/.test(kw)) out.applicantId = Number(kw);
+  }
+  if (id != null && id !== "") {
+    out.applicantId = typeof id === "number" ? id : Number(id) || id;
+  }
+  return out;
+}
+
+function pickInstanceNoFromSearchForm(searchForm = {}) {
+  const no = (searchForm?.instanceNo || "").trim();
+  return no ? { instanceNo: no } : {};
+}
+
+/** 缁勮 approvalInstanceDto 鏌ヨ鐗囨锛堢敵璇蜂汉 + 瀹℃壒鍗曞彿锛� */
+export function buildApprovalInstanceSearchDto(searchForm = {}, extraParams = {}) {
+  const dto = {
+    ...(extraParams && typeof extraParams === "object" ? extraParams : {}),
+  };
+  Object.assign(dto, pickApplicantFromSearchForm(searchForm));
+  Object.assign(dto, pickInstanceNoFromSearchForm(searchForm));
+  delete dto.createTime;
+  delete dto.createTimeStart;
+  delete dto.createTimeEnd;
+  return dto;
+}
+
+function getRowPayloadValue(row, keys) {
+  const keyList = Array.isArray(keys) ? keys : [keys];
+  const payload = row?.formPayload || {};
+  for (const k of keyList) {
+    if (row?.[k] != null && row[k] !== "") return row[k];
+    if (payload[k] != null && payload[k] !== "") return payload[k];
+  }
+  return "";
+}
+
+function matchApplicantKeyword(row, keyword) {
+  const kw = (keyword || "").trim().toLowerCase();
+  if (!kw) return true;
+  const parts = [
+    row?.applicantName,
+    row?.applicantNo,
+    row?.applicantId,
+    getRowPayloadValue(row, ["applicant", "applicantName", "applicantId"]),
+  ]
+    .filter((v) => v != null && v !== "")
+    .map((v) => String(v).toLowerCase());
+  return parts.some((p) => p.includes(kw));
+}
+
+function matchApplicantId(row, applicantId) {
+  if (applicantId == null || applicantId === "") return true;
+  const id = String(applicantId);
+  if (row?.applicantId != null && String(row.applicantId) === id) return true;
+  const payloadApplicant = getRowPayloadValue(row, [
+    "applicant",
+    "applicantId",
+    "applicantUserId",
+  ]);
+  return String(payloadApplicant) === id;
+}
+
+function matchSelectValue(row, keys, expected) {
+  if (!expected) return true;
+  const raw = getRowPayloadValue(row, keys);
+  return String(raw) === String(expected);
+}
+
+function matchInstanceNo(row, instanceNo) {
+  const kw = (instanceNo || "").trim().toLowerCase();
+  if (!kw) return true;
+  const parts = [row?.instanceNo, row?.bizId]
+    .filter((v) => v != null && v !== "")
+    .map((v) => String(v).toLowerCase());
+  return parts.some((p) => p.includes(kw));
+}
+
+/** 鏄惁瀛樺湪鍒楄〃绛涢�夋潯浠讹紙鐢宠浜� / 瀹℃壒鍗曞彿锛� */
+export function hasActiveModuleSearch(moduleKey, searchForm = {}) {
+  const sf = searchForm || {};
+  if ((sf.instanceNo || "").trim()) return true;
+  if ((sf.applicantKeyword || "").trim()) return true;
+  if ((sf.applicantName || "").trim()) return true;
+  return sf.applicantId != null && sf.applicantId !== "";
+}
+
+/** 鎸夌敵璇蜂汉銆佸鎵瑰崟鍙峰仛鍓嶇鍏滃簳绛涢�� */
+export function filterInstanceRowsByModuleSearch(moduleKey, rows, searchForm = {}) {
+  const sf = searchForm || {};
+  const list = Array.isArray(rows) ? rows : [];
+  if (!hasActiveModuleSearch(moduleKey, sf)) return list;
+
+  return list.filter(
+    (row) =>
+      matchInstanceNo(row, sf.instanceNo) &&
+      matchApplicantId(row, sf.applicantId) &&
+      matchApplicantKeyword(row, sf.applicantKeyword || sf.applicantName)
+  );
+}
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/useApprovalInstanceModule.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/useApprovalInstanceModule.js
index 8dd8bba..b474bb2 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/useApprovalInstanceModule.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/useApprovalInstanceModule.js
@@ -29,6 +29,10 @@
 } from "../approve-template/selectOptionSource.js";
 import { enrichInstanceRowFromFormConfig } from "./approvalInstanceFormConfigTable.js";
 import {
+  filterInstanceRowsByModuleSearch,
+  hasActiveModuleSearch,
+} from "./approvalInstanceListSearch.js";
+import {
   getApprovalModuleConfig,
   getModuleListBusinessType,
   resolveModuleBusinessType,
@@ -146,8 +150,14 @@
       const caches = await fetchSelectOptionCaches(
         collectOptionSourcesFromFields(allFields)
       );
-      tableData.value = mapped.map((row) => mapListRow(row, caches));
-      page.total = total;
+      let rows = mapped.map((row) => mapListRow(row, caches));
+      if (hasActiveModuleSearch(moduleKey, searchForm)) {
+        rows = filterInstanceRowsByModuleSearch(moduleKey, rows, searchForm);
+      }
+      tableData.value = rows;
+      page.total = hasActiveModuleSearch(moduleKey, searchForm)
+        ? rows.length
+        : total;
     } catch {
       tableData.value = [];
       page.total = 0;
diff --git a/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue b/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
index 546ca87..4d800df 100644
--- a/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
+++ b/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
@@ -3,7 +3,15 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">鐢宠浜猴細</span>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
         <el-input
           v-model="searchForm.applicantKeyword"
           style="width: 220px"
@@ -12,10 +20,6 @@
           :prefix-icon="Search"
           @keyup.enter="onSearch"
         />
-        <span class="search_title" style="margin-left: 12px">璇峰亣绫诲瀷锛�</span>
-        <el-select v-model="searchForm.leaveType" placeholder="鍏ㄩ儴" clearable style="width: 180px">
-          <el-option v-for="opt in LEAVE_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
-        </el-select>
         <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
         <el-button @click="resetSearch">閲嶇疆</el-button>
       </div>
@@ -177,8 +181,8 @@
 }
 
 const searchForm = reactive({
+  instanceNo: "",
   applicantKeyword: "",
-  leaveType: "",
 });
 
 function validateLeaveBeforeSave() {
@@ -274,15 +278,17 @@
   }
 );
 
-const tableColumn = buildInstanceTableColumns(tableData, buildTableActions);
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+  moduleKey: APPROVAL_MODULE_KEYS.LEAVE,
+});
 
 function onSearch() {
   handleQuery(searchForm);
 }
 
 function resetSearch() {
+  searchForm.instanceNo = "";
   searchForm.applicantKeyword = "";
-  searchForm.leaveType = "";
   onSearch();
 }
 
diff --git a/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue b/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue
index 17303aa..9b3d91e 100644
--- a/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue
+++ b/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue
@@ -3,7 +3,15 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">鐢宠浜猴細</span>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
         <el-input
           v-model="searchForm.applicantKeyword"
           style="width: 220px"
@@ -12,10 +20,6 @@
           :prefix-icon="Search"
           @keyup.enter="onSearch"
         />
-        <span class="search_title" style="margin-left: 12px">鍔犵彮绫诲瀷锛�</span>
-        <el-select v-model="searchForm.overtimeType" placeholder="鍏ㄩ儴" clearable style="width: 180px">
-          <el-option v-for="opt in OVERTIME_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
-        </el-select>
         <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
         <el-button @click="resetSearch">閲嶇疆</el-button>
       </div>
@@ -145,8 +149,8 @@
 const { proxy } = getCurrentInstance();
 
 const searchForm = reactive({
+  instanceNo: "",
   applicantKeyword: "",
-  overtimeType: "",
 });
 
 const mod = useApprovalInstanceModule({
@@ -192,15 +196,17 @@
   }
 }
 
-const tableColumn = buildInstanceTableColumns(tableData, buildTableActions);
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+  moduleKey: APPROVAL_MODULE_KEYS.OVERTIME,
+});
 
 function onSearch() {
   handleQuery(searchForm);
 }
 
 function resetSearch() {
+  searchForm.instanceNo = "";
   searchForm.applicantKeyword = "";
-  searchForm.overtimeType = "";
   onSearch();
 }
 
diff --git a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
index ef4a1e8..291d57d 100644
--- a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
@@ -3,7 +3,15 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">鐢宠浜猴細</span>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
         <el-input
           v-model="searchForm.applicantName"
           style="width: 220px"
@@ -11,18 +19,6 @@
           clearable
           :prefix-icon="Search"
           @keyup.enter="onSearch"
-        />
-        <span class="search_title" style="margin-left: 12px">鐢宠鏃ユ湡锛�</span>
-        <el-date-picker
-          v-model="searchForm.applyDateRange"
-          type="daterange"
-          range-separator="鑷�"
-          start-placeholder="寮�濮�"
-          end-placeholder="缁撴潫"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 260px"
-          clearable
         />
         <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
         <el-button @click="resetSearch">閲嶇疆</el-button>
@@ -88,8 +84,8 @@
 import { useFlowUserOptions } from "../../ApproveManage/approve-shared/useFlowUserOptions.js";
 
 const searchForm = reactive({
+  instanceNo: "",
   applicantName: "",
-  applyDateRange: null,
 });
 
 const mod = useApprovalInstanceModule({
@@ -131,15 +127,17 @@
 
 const { flowUserOptions, loadFlowUsers } = useFlowUserOptions();
 
-const tableColumn = buildInstanceTableColumns(tableData, buildTableActions);
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+  moduleKey: APPROVAL_MODULE_KEYS.REGULAR,
+});
 
 function onSearch() {
   handleQuery(searchForm);
 }
 
 function resetSearch() {
+  searchForm.instanceNo = "";
   searchForm.applicantName = "";
-  searchForm.applyDateRange = null;
   onSearch();
 }
 
diff --git a/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
index 4121157..f731d57 100644
--- a/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
@@ -3,7 +3,15 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">鐢宠浜猴細</span>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
         <el-select
           v-model="searchForm.applicantId"
           filterable
@@ -22,18 +30,6 @@
             :value="u.userId"
           />
         </el-select>
-        <span class="search_title" style="margin-left: 12px">杞矖鏃堕棿锛�</span>
-        <el-date-picker
-          v-model="searchForm.transferDateRange"
-          type="daterange"
-          range-separator="鑷�"
-          start-placeholder="寮�濮�"
-          end-placeholder="缁撴潫"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 260px"
-          clearable
-        />
         <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
         <el-button @click="resetSearch">閲嶇疆</el-button>
       </div>
@@ -131,15 +127,12 @@
 }
 
 const searchForm = reactive({
+  instanceNo: "",
   applicantId: "",
-  transferDateRange: null,
 });
 
 const mod = useApprovalInstanceModule({
   moduleKey: APPROVAL_MODULE_KEYS.TRANSFER,
-  buildExtraListParams() {
-    return {};
-  },
 });
 
 const {
@@ -304,15 +297,17 @@
   }
 );
 
-const tableColumn = buildInstanceTableColumns(tableData, buildTableActions);
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+  moduleKey: APPROVAL_MODULE_KEYS.TRANSFER,
+});
 
 function onSearch() {
   handleQuery(searchForm);
 }
 
 async function resetSearch() {
+  searchForm.instanceNo = "";
   searchForm.applicantId = "";
-  searchForm.transferDateRange = null;
   onSearch();
   await remoteSearchApplicant("");
 }
diff --git a/src/views/officeProcessAutomation/HrManage/work-handover/index.vue b/src/views/officeProcessAutomation/HrManage/work-handover/index.vue
index 9078e97..a9b96ae 100644
--- a/src/views/officeProcessAutomation/HrManage/work-handover/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/work-handover/index.vue
@@ -3,7 +3,15 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">鐢宠浜猴細</span>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
         <el-select
           v-model="searchForm.applicantId"
           filterable
@@ -21,14 +29,6 @@
             :label="userSelectLabel(u)"
             :value="u.userId"
           />
-        </el-select>
-        <span class="search_title" style="margin-left: 12px">浜ゆ帴鐘舵�侊細</span>
-        <el-select v-model="searchForm.handoverStatus" placeholder="鍏ㄩ儴" clearable style="width: 140px">
-          <el-option v-for="o in handoverStatusOptions" :key="o.value" :label="o.label" :value="o.value" />
-        </el-select>
-        <span class="search_title" style="margin-left: 12px">浜ゆ帴绫诲瀷锛�</span>
-        <el-select v-model="searchForm.handoverType" placeholder="鍏ㄩ儴" clearable style="width: 140px">
-          <el-option v-for="o in handoverTypeOptions" :key="o.value" :label="o.label" :value="o.value" />
         </el-select>
         <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
         <el-button @click="resetSearch">閲嶇疆</el-button>
@@ -105,9 +105,8 @@
 ];
 
 const searchForm = reactive({
+  instanceNo: "",
   applicantId: "",
-  handoverStatus: "",
-  handoverType: "",
 });
 
 const mod = useApprovalInstanceModule({
@@ -200,16 +199,17 @@
   }
 }
 
-const tableColumn = buildInstanceTableColumns(tableData, buildTableActions);
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+  moduleKey: APPROVAL_MODULE_KEYS.WORK_HANDOVER,
+});
 
 function onSearch() {
   handleQuery(searchForm);
 }
 
 async function resetSearch() {
+  searchForm.instanceNo = "";
   searchForm.applicantId = "";
-  searchForm.handoverStatus = "";
-  searchForm.handoverType = "";
   onSearch();
   await remoteSearchApplicant("");
 }
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
index 521bb76..c9da4fc 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
@@ -12,26 +12,6 @@
           :prefix-icon="Search"
           @keyup.enter="handleQuery"
         />
-        <span class="search_title" style="margin-left: 12px">鐢宠鏃堕棿锛�</span>
-        <el-date-picker
-          v-model="searchForm.applyTimeFrom"
-          type="date"
-          placeholder="寮�濮嬫棩鏈�"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 150px"
-          clearable
-        />
-        <span class="search_title" style="margin-left: 8px">鑷�</span>
-        <el-date-picker
-          v-model="searchForm.applyTimeTo"
-          type="date"
-          placeholder="缁撴潫鏃ユ湡"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 150px; margin-left: 8px"
-          clearable
-        />
         <el-button type="primary" style="margin-left: 10px" @click="handleQuery">鎼滅储</el-button>
         <el-button @click="resetSearch">閲嶇疆</el-button>
       </div>
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
index 520c2a5..3b90a3b 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
@@ -12,6 +12,8 @@
 import {
   buildCostReimbursementSaveDto,
   buildFinReimbursementListParams,
+  filterReimbursementRowsBySearch,
+  hasActiveReimbursementSearch,
   canDeleteReimbursementRow,
   canEditReimbursementRow,
   enrichReimbursementListRowsWithApprovalFlow,
@@ -70,8 +72,6 @@
 
   const searchForm = reactive({
     applicantKeyword: "",
-    applyTimeFrom: "",
-    applyTimeTo: "",
   });
   const tableLoading = ref(false);
   const page = reactive({ current: 1, size: 10, total: 0 });
@@ -116,10 +116,17 @@
         mapped,
         FIN_REIMBURSEMENT_TYPE.COST
       );
+      if (hasActiveReimbursementSearch(searchForm)) {
+        mapped = filterReimbursementRowsBySearch(mapped, searchForm);
+      }
       allRows.value = mapped;
       const dropped = records.length - filtered.length;
-      page.total =
+      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;
@@ -327,8 +334,6 @@
 
   function resetSearch() {
     searchForm.applicantKeyword = "";
-    searchForm.applyTimeFrom = "";
-    searchForm.applyTimeTo = "";
     handleQuery();
   }
 
diff --git a/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js b/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
index 44fd1b4..2525f70 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
@@ -165,11 +165,49 @@
 function pickApplicantQuery(searchForm = {}) {
   const kw = (searchForm.applicantKeyword || "").trim();
   if (!kw) return {};
-  if (/[\u4e00-\u9fa5]/.test(kw)) return { applicantName: kw };
-  return { applicantCode: kw };
+  // 鍗犱綅銆屽鍚嶆垨缂栧彿銆嶏細濮撳悕璧� applicantName锛涚紪鍙峰彟浼� applicantCode
+  const out = { applicantName: kw };
+  if (!/[\u4e00-\u9fa5]/.test(kw)) {
+    out.applicantCode = kw;
+  }
+  return out;
 }
 
-/** 缁勮 listPage 鏌ヨ鍙傛暟锛坧age + finReimbursementDto锛� */
+/** 鏄惁瀛樺湪鍒楄〃绛涢�夋潯浠讹紙浠呯敵璇蜂汉锛� */
+export function hasActiveReimbursementSearch(searchForm = {}) {
+  return Boolean((searchForm?.applicantKeyword || "").trim());
+}
+
+/** 鏈嶅姟绔湭鐢熸晥鏃讹紝鎸夌敵璇蜂汉鍋氬墠绔厹搴曠瓫閫� */
+export function filterReimbursementRowsBySearch(rows, searchForm = {}) {
+  const list = Array.isArray(rows) ? rows : [];
+  const kw = (searchForm?.applicantKeyword || "").trim().toLowerCase();
+  if (!kw) return list;
+
+  return list.filter((row) => {
+    const parts = [
+      row.applicantName,
+      row.employeeName,
+      row.applicantNo,
+      row.applicantCode,
+      row.employeeNo,
+    ]
+      .filter((v) => v != null && v !== "")
+      .map((v) => String(v).toLowerCase());
+    return parts.some((p) => p.includes(kw));
+  });
+}
+
+/** 鎵佸钩鍖栦负 Spring GET 鍙粦瀹氱殑 query锛坒inReimbursementDto.xxx锛屽嬁鐢ㄦ柟鎷彿锛� */
+function appendDotNotationQuery(target, prefix, fields) {
+  if (!fields || typeof fields !== "object") return;
+  for (const [key, value] of Object.entries(fields)) {
+    if (value == null || value === "") continue;
+    target[`${prefix}.${key}`] = value;
+  }
+}
+
+/** 缁勮 listPage 鏌ヨ鍙傛暟锛堟墎骞� page.* / finReimbursementDto.*锛屼笌 detail 鎺ュ彛涓�鑷达級 */
 export function buildFinReimbursementListParams({
   page,
   searchForm,
@@ -182,40 +220,15 @@
     ...(extraDto && typeof extraDto === "object" ? extraDto : {}),
   };
 
-  if (searchForm?.billStatus) {
-    dto.billStatus = searchForm.billStatus;
-  }
-
-  const range =
-    searchForm?.createTimeRange ??
-    searchForm?.applyDateRange ??
-    (searchForm?.applyTimeFrom || searchForm?.applyTimeTo
-      ? [searchForm.applyTimeFrom, searchForm.applyTimeTo]
-      : null);
-
-  if (Array.isArray(range) && range[0]) {
-    dto.createTimeStart = range[0];
-  }
-  if (Array.isArray(range) && range[1]) {
-    dto.createTimeEnd = range[1];
-  }
-
-  if (reimbursementType === FIN_REIMBURSEMENT_TYPE.TRAVEL) {
-    if (searchForm?.travelStartFrom) {
-      dto.startTimeStart = searchForm.travelStartFrom;
-    }
-    if (searchForm?.travelEndTo) {
-      dto.endTimeEnd = searchForm.travelEndTo;
-    }
-  }
-
-  return {
-    page: {
-      current: page.current,
-      size: page.size,
-    },
-    finReimbursementDto: dto,
+  const params = {
+    current: page.current,
+    size: page.size,
+    "page.current": page.current,
+    "page.size": page.size,
+    ...dto,
   };
+  appendDotNotationQuery(params, "finReimbursementDto", dto);
+  return params;
 }
 
 function pickTravelField(obj, keys) {
diff --git a/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/index.vue b/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/index.vue
index 045ed4d..17737e3 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/index.vue
+++ b/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/index.vue
@@ -12,26 +12,6 @@
           :prefix-icon="Search"
           @keyup.enter="handleQuery"
         />
-        <span class="search_title" style="margin-left: 12px">鍑哄樊寮�濮嬶細</span>
-        <el-date-picker
-          v-model="searchForm.travelStartFrom"
-          type="date"
-          placeholder="寮�濮嬫棩鏈�"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 150px"
-          clearable
-        />
-        <span class="search_title" style="margin-left: 8px">缁撴潫锛�</span>
-        <el-date-picker
-          v-model="searchForm.travelEndTo"
-          type="date"
-          placeholder="缁撴潫鏃ユ湡"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 150px"
-          clearable
-        />
         <el-button type="primary" style="margin-left: 10px" @click="handleQuery">鎼滅储</el-button>
         <el-button @click="resetSearch">閲嶇疆</el-button>
       </div>
diff --git a/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/useTravelReimburse.js b/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/useTravelReimburse.js
index 4726117..c92d88c 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/useTravelReimburse.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/useTravelReimburse.js
@@ -11,6 +11,8 @@
 import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref } from "vue";
 import {
   buildFinReimbursementListParams,
+  filterReimbursementRowsBySearch,
+  hasActiveReimbursementSearch,
   buildTravelReimbursementSaveDto,
   canDeleteReimbursementRow,
   canEditReimbursementRow,
@@ -61,7 +63,7 @@
 
   const allRows = ref([]);
 
-  const searchForm = reactive({ applicantKeyword: "", travelStartFrom: "", travelEndTo: "" });
+  const searchForm = reactive({ applicantKeyword: "" });
   const tableLoading = ref(false);
   const page = reactive({ current: 1, size: 10, total: 0 });
   const importInputRef = ref(null);
@@ -100,10 +102,17 @@
         mapped,
         FIN_REIMBURSEMENT_TYPE.TRAVEL
       );
+      if (hasActiveReimbursementSearch(searchForm)) {
+        mapped = filterReimbursementRowsBySearch(mapped, searchForm);
+      }
       allRows.value = mapped;
       const dropped = records.length - filtered.length;
-      page.total =
+      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;
@@ -376,8 +385,6 @@
 
   function resetSearch() {
     searchForm.applicantKeyword = "";
-    searchForm.travelStartFrom = "";
-    searchForm.travelEndTo = "";
     handleQuery();
   }
 

--
Gitblit v1.9.3