yyb
13 小时以前 4ab0be7d4441f378add1f242b168d80fb27e65fe
OA部分查询条件变更
已添加1个文件
已修改13个文件
488 ■■■■■ 文件已修改
src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-shared/useApprovalInstanceModule.js 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/HrManage/regular-apply/index.vue 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/HrManage/work-handover/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ReimburseManage/travel-reimburse/useTravelReimburse.js 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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;
}
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,
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(approvalInstanceDto.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)
  );
}
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;
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();
}
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();
}
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();
}
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("");
}
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("");
}
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>
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();
  }
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 æŸ¥è¯¢å‚数(page + 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(finReimbursementDto.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: {
  const params = {
      current: page.current,
      size: page.size,
    },
    finReimbursementDto: dto,
    "page.current": page.current,
    "page.size": page.size,
    ...dto,
  };
  appendDotNotationQuery(params, "finReimbursementDto", dto);
  return params;
}
function pickTravelField(obj, keys) {
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>
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();
  }