From be85a121031530d69865fd30d0dfb2fe0998a6a3 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期四, 21 五月 2026 15:21:40 +0800
Subject: [PATCH] 优化查询条件分类
---
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue | 600 ++++++++++-------------------------------------------------
1 files changed, 102 insertions(+), 498 deletions(-)
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
index b384569..c836168 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
@@ -1,8 +1,8 @@
-<!--OA妯″潡锛氳垂鐢ㄦ姤閿�-->
+<!--OA妯″潡锛氳垂鐢ㄦ姤閿�锛堝鎵瑰疄渚� listPage锛宐usinessType=17锛�-->
<template>
<div class="app-container">
<div class="search_form mb20">
- <div class="search_fields">
+ <div>
<span class="search_title">鐢宠浜猴細</span>
<el-input
v-model="searchForm.applicantKeyword"
@@ -10,40 +10,15 @@
placeholder="濮撳悕鎴栫紪鍙�"
clearable
:prefix-icon="Search"
- @keyup.enter="handleQuery"
+ @keyup.enter="onSearch"
/>
- <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 type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
<el-button @click="resetSearch">閲嶇疆</el-button>
</div>
- <div class="search_actions">
- <el-button type="success" plain @click="handleImportClick">瀵煎叆</el-button>
- <el-button type="warning" plain @click="handleExport">瀵煎嚭</el-button>
- <el-button type="primary" @click="openFormDialog('add')">鏂板璐圭敤鎶ラ攢</el-button>
+ <div>
+ <el-button type="primary" @click="openAddWithTemplate">鏂板璐圭敤鎶ラ攢</el-button>
</div>
</div>
-
- <input ref="importInputRef" type="file" accept="application/json,.json" class="sr-only-input" @change="onImportFile" />
-
<div class="table_list">
<PIMTable
rowKey="id"
@@ -53,404 +28,123 @@
:isSelection="false"
:tableLoading="tableLoading"
:total="page.total"
- @pagination="pagination"
+ @pagination="onPagination"
/>
</div>
- <!-- 鏂板 / 缂栬緫 -->
- <el-dialog
- v-model="formDialog.visible"
- :title="formDialog.title"
- width="1120px"
- append-to-body
- destroy-on-close
- class="cost-reimburse-form-dialog"
- @closed="onFormClosed"
- >
- <el-alert type="info" show-icon :closable="false" class="mb16">
- <template #title>鍏ㄥ搧绫昏垂鐢ㄦ姤閿� 路 鍒嗙被妯℃澘涓�閿~鎶�</template>
- <template #default>
- 鏀寔宸梾銆佸姙鍏噰璐�佷笟鍔℃嫑寰呫�佷氦閫氳垂銆侀�氳璐圭瓑锛涙寜閲戦鑷姩鍖归厤瀹℃壒閾撅紙500鍏冨唴鐩村睘涓婄骇锛岃秴5000鍏冭储鍔℃�荤洃澶嶆牳锛夈��
- </template>
- </el-alert>
+ <ApprovalInstanceSubmitDialog
+ v-model="submitDialog.visible"
+ :title="submitDialogTitle"
+ :form="submitForm"
+ :rules="submitFormRules"
+ :fields="submitFormFields"
+ :active-template="activeTemplate"
+ :user-options="flowUserOptions"
+ :is-edit="isSubmitEdit"
+ :saving="submitSaving"
+ :form-ref="submitFormRef"
+ flow-attachments-only
+ @submit="onSubmit"
+ />
- <div v-if="!formDialog.readonly" class="template-bar mb16">
- <span class="template-label">鍒嗙被妯℃澘锛�</span>
- <el-button
- v-for="(tpl, key) in CATEGORY_TEMPLATES"
- :key="key"
- size="small"
- :type="form.expenseCategory === key ? 'primary' : 'default'"
- plain
- @click="applyTemplate(key)"
- >
- {{ tpl.label }}
- </el-button>
- </div>
+ <ApprovalTemplateBindDialog
+ v-model:visible="templateBindVisible"
+ :module-key="APPROVAL_MODULE_KEYS.COST_REIMBURSE"
+ skip-form-confirm
+ @confirm="onTemplateBound"
+ @closed="onTemplateBindClosed"
+ />
- <el-form
- ref="formRef"
- :model="form"
- :rules="formRules"
- label-width="120px"
- class="cost-reimburse-form"
- :disabled="formDialog.readonly"
- >
- <el-card class="form-section" shadow="never">
- <template #header><span class="card-header-title">鍩烘湰淇℃伅</span></template>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="鍛樺伐缂栧彿">
- <el-input v-model="form.employeeNo" readonly placeholder="閫夋嫨鍛樺伐鍚庤嚜鍔ㄥ甫鍑�" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鍛樺伐濮撳悕" prop="applicantId">
- <el-select
- v-model="form.applicantId"
- filterable
- remote
- clearable
- reserve-keyword
- placeholder="璇烽�夋嫨鎴栨悳绱㈠憳宸�"
- style="width: 100%"
- :remote-method="remoteSearchApplicantForm"
- :loading="applicantFormSearchLoading"
- @change="onApplicantChange"
- >
- <el-option
- v-for="u in applicantFormOptions"
- :key="u.userId"
- :label="userSelectLabel(u)"
- :value="u.userId"
- />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="璐圭敤绫诲瀷" prop="expenseCategory">
- <el-select
- v-model="form.expenseCategory"
- placeholder="璇烽�夋嫨璐圭敤绫诲瀷"
- style="width: 100%"
- @change="onExpenseCategoryChange"
- >
- <el-option
- v-for="opt in EXPENSE_CATEGORY_OPTIONS"
- :key="opt.value"
- :label="opt.label"
- :value="opt.value"
- />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鎶ラ攢鐘舵��">
- <el-tag
- :type="form.approvalResult === 'approved' ? 'success' : form.approvalResult === 'rejected' ? 'danger' : 'warning'"
- effect="plain"
- >
- {{
- form.approvalResult === "approved"
- ? "宸查�氳繃"
- : form.approvalResult === "rejected"
- ? "宸查┏鍥�"
- : "瀹℃牳涓�"
- }}
- </el-tag>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="24">
- <el-form-item label="鎶ラ攢鍘熷洜" prop="reimburseReason">
- <el-input
- v-model="form.reimburseReason"
- type="textarea"
- :rows="3"
- placeholder="璇峰~鍐欐姤閿�鍘熷洜"
- maxlength="2000"
- show-word-limit
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="鎶ラ攢閲戦" prop="applyAmount">
- <div class="amount-row">
- <el-input-number
- v-model="form.applyAmount"
- :min="0"
- :precision="2"
- controls-position="right"
- class="amount-input"
- @change="autoAssignApprovalFlow"
- />
- <el-button v-if="!formDialog.readonly" type="primary" link @click="syncApplyAmountFromDetails">
- 鎸夋槑缁嗘眹鎬� {{ detailTotalAmount }} 鍏�
- </el-button>
- </div>
- </el-form-item>
- </el-col>
- </el-row>
- </el-card>
-
- <el-card class="form-section" shadow="never">
- <template #header>
- <div class="card-header-row">
- <span class="card-header-title">鎶ラ攢鏄庣粏</span>
- <el-button v-if="!formDialog.readonly" type="primary" plain size="small" @click="addExpenseDetail">
- 鏂板鏄庣粏
- </el-button>
- </div>
- </template>
-
- <el-table :data="form.expenseDetails" border size="small" class="detail-table">
- <el-table-column type="index" label="搴忓彿" width="55" align="center" />
- <el-table-column label="鍙戠エ鏃ユ湡" width="150">
- <template #default="{ row }">
- <el-date-picker
- v-if="!formDialog.readonly"
- v-model="row.invoiceDate"
- type="date"
- value-format="YYYY-MM-DD"
- size="small"
- style="width: 100%"
- />
- <span v-else>{{ row.invoiceDate || "鈥�" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="璐圭敤绉戠洰" width="130">
- <template #default="{ row }">
- <el-select v-if="!formDialog.readonly" v-model="row.expenseSubject" size="small" style="width: 100%">
- <el-option
- v-for="opt in EXPENSE_SUBJECT_OPTIONS"
- :key="opt.value"
- :label="opt.label"
- :value="opt.value"
- />
- </el-select>
- <span v-else>{{ expenseSubjectLabel(row.expenseSubject) }}</span>
- </template>
- </el-table-column>
- <el-table-column label="閲戦" width="120">
- <template #default="{ row }">
- <el-input-number
- v-if="!formDialog.readonly"
- v-model="row.amount"
- :min="0"
- :precision="2"
- size="small"
- controls-position="right"
- style="width: 100%"
- @change="onDetailAmountChange"
- />
- <span v-else>{{ row.amount ?? "鈥�" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="鎻忚堪" min-width="140">
- <template #default="{ row }">
- <el-input v-if="!formDialog.readonly" v-model="row.description" size="small" placeholder="璇存槑" />
- <span v-else>{{ row.description || "鈥�" }}</span>
- </template>
- </el-table-column>
- <el-table-column v-if="!formDialog.readonly" label="鎿嶄綔" width="70" align="center">
- <template #default="{ $index }">
- <el-button type="danger" link size="small" @click="removeExpenseDetail($index)">鍒犻櫎</el-button>
- </template>
- </el-table-column>
- </el-table>
- </el-card>
-
- <el-card class="form-section" shadow="never">
- <template #header><span class="card-header-title">鏀舵淇℃伅</span></template>
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="鏀舵浜�" prop="payee">
- <el-input v-model="form.payee" placeholder="璇疯緭鍏ユ敹娆句汉" maxlength="50" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鏀舵璐﹀彿" prop="payeeAccount">
- <el-input v-model="form.payeeAccount" placeholder="閾惰鍗″彿" maxlength="30" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="寮�鎴锋敮琛�" prop="bankBranch">
- <el-input v-model="form.bankBranch" placeholder="寮�鎴锋敮琛屽叏绉�" maxlength="100" />
- </el-form-item>
- </el-col>
- </el-row>
- </el-card>
-
- <el-card class="form-section" shadow="never">
- <template #header><span class="card-header-title">闄勪欢锛堝彂绁級</span></template>
- <el-form-item label-width="0" class="attachment-form-item">
- <div class="upload-block">
- <FileUpload v-model:file-list="form.attachmentList" :limit="20" button-text="鐐瑰嚮閫夋嫨鏂囦欢" />
- </div>
- </el-form-item>
- </el-card>
-
- <el-card class="form-section" shadow="never">
- <template #header>
- <div class="card-header-row">
- <span class="card-header-title">瀹℃壒娴佺▼</span>
- <el-button v-if="!formDialog.readonly" type="primary" link size="small" @click="autoAssignApprovalFlow">
- 鎸夎鍒欓噸鏂板垎閰�
- </el-button>
- </div>
- </template>
- <el-alert type="success" :title="approvalRuleHint" show-icon :closable="false" class="mb12" />
- <el-form-item prop="approvalFlowNodes" label-width="0">
- <ApprovalFlowEditor
- v-if="!formDialog.readonly"
- v-model="form.approvalFlowNodes"
- :user-options="flowUserOptions"
- @update:model-value="onApprovalFlowChange"
- />
- <ApprovalFlowProgress v-else :nodes="form.approvalFlowNodes" :current-index="form.currentNodeIndex" />
- <p v-if="!formDialog.readonly" class="flow-tip">绯荤粺宸叉寜閲戦涓庤垂鐢ㄧ被鍨嬭嚜鍔ㄥ垎閰嶅鎵逛汉锛屽彲鎵嬪姩璋冩暣銆�</p>
- </el-form-item>
- </el-card>
- </el-form>
- <template #footer>
- <el-button v-if="!formDialog.readonly" type="primary" @click="submitForm">鎻� 浜�</el-button>
- <el-button @click="formDialog.visible = false">{{ formDialog.readonly ? "鍏� 闂�" : "鍙� 娑�" }}</el-button>
- </template>
- </el-dialog>
-
- <!-- 璇︽儏 -->
- <el-dialog v-model="detailDialog.visible" title="璐圭敤鎶ラ攢璇︽儏" width="900px" append-to-body destroy-on-close>
- <DetailPanel :row="detailRow" />
- <el-divider content-position="left">瀹℃壒娴佺▼</el-divider>
- <ApprovalFlowProgress :nodes="detailRow.approvalFlowNodes" :current-index="detailRow.currentNodeIndex ?? 0" />
- <el-divider content-position="left">瀹℃壒璁板綍</el-divider>
- <el-timeline v-if="detailRow.approvalRecords?.length">
- <el-timeline-item
- v-for="(rec, i) in detailRow.approvalRecords"
- :key="i"
- :type="rec.result === 'approved' ? 'success' : rec.result === 'rejected' ? 'danger' : 'primary'"
- :timestamp="rec.time"
- >
- {{ rec.operatorName }} 鈥� {{ approvalActionLabel(rec.result) }}锛歿{ rec.opinion || "鏃犳剰瑙�" }}
- </el-timeline-item>
- </el-timeline>
- <el-empty v-else description="鏆傛棤瀹℃壒璁板綍" :image-size="60" />
- <template #footer>
- <el-button type="primary" @click="detailDialog.visible = false">鍏� 闂�</el-button>
- </template>
- </el-dialog>
-
- <!-- 瀹℃壒 -->
- <el-dialog
- v-model="approveDialog.visible"
- title="璐圭敤鎶ラ攢瀹℃壒"
- width="1000px"
- append-to-body
- destroy-on-close
- @closed="approveOpinion = ''"
- >
- <DetailPanel :row="approveDialog.row" />
- <el-divider content-position="left">娴佺▼杩涘害</el-divider>
- <ApprovalFlowProgress
- :nodes="approveDialog.row?.approvalFlowNodes"
- :current-index="approveDialog.row?.currentNodeIndex ?? 0"
- />
- <el-form label-width="100px" class="mt16">
- <el-form-item label="瀹℃壒鎰忚" required>
- <el-input
- v-model="approveOpinion"
- type="textarea"
- :rows="3"
- maxlength="500"
- show-word-limit
- placeholder="閫氳繃鍙暀绌猴紱椹冲洖璇峰~鍐欏叿浣撳師鍥狅紙濡傦細鍙戠エ妯$硦闇�閲嶄紶锛�"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button type="success" @click="submitApprove('approved')">閫� 杩�</el-button>
- <el-button type="danger" @click="submitApprove('rejected')">椹� 鍥�</el-button>
- <el-button @click="approveDialog.visible = false">鍙� 娑�</el-button>
- </template>
- </el-dialog>
+ <ApprovalInstanceDetailDialog
+ v-model="detailDialog.visible"
+ title="璐圭敤鎶ラ攢璇︽儏"
+ :row="detailRow"
+ @edit="openEditFromDetail"
+ />
</div>
</template>
<script setup>
-import FileUpload from "@/components/AttachmentUpload/file/index.vue";
-import ApprovalFlowEditor from "@/views/officeProcessAutomation/AttendManage/overtime-apply/components/ApprovalFlowEditor.vue";
-import ApprovalFlowProgress from "../travel-reimburse/components/ApprovalFlowProgress.vue";
-import DetailPanel from "./components/DetailPanel.vue";
-import { useCostReimburse } from "./useCostReimburse.js";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage } from "element-plus";
+import { onMounted, reactive } from "vue";
+import ApprovalInstanceDetailDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceDetailDialog.vue";
+import ApprovalInstanceSubmitDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue";
+import ApprovalTemplateBindDialog from "../../ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue";
+import { buildInstanceTableColumns } from "../../ApproveManage/approve-shared/approvalInstanceFormConfigTable.js";
+import { APPROVAL_MODULE_KEYS } from "../../ApproveManage/approve-shared/approvalModuleRegistry.js";
+import { useApprovalInstanceModule } from "../../ApproveManage/approve-shared/useApprovalInstanceModule.js";
+import { useFlowUserOptions } from "../../ApproveManage/approve-shared/useFlowUserOptions.js";
-const cr = useCostReimburse();
+const searchForm = reactive({ applicantKeyword: "" });
+
+const mod = useApprovalInstanceModule({
+ moduleKey: APPROVAL_MODULE_KEYS.COST_REIMBURSE,
+ buildExtraListParams(sf) {
+ const extra = {};
+ const kw = (sf?.applicantKeyword || "").trim();
+ if (kw && /[\u4e00-\u9fa5]/.test(kw)) extra.applicantName = kw;
+ return extra;
+ },
+});
+
const {
- Search,
- EXPENSE_CATEGORY_OPTIONS,
- CATEGORY_TEMPLATES,
- EXPENSE_SUBJECT_OPTIONS,
- expenseSubjectLabel,
- searchForm,
+ tableData,
tableLoading,
page,
- tableData,
- tableColumn,
- importInputRef,
- formRef,
- form,
- formDialog,
- formRules,
detailDialog,
detailRow,
- approveDialog,
- approveOpinion,
- applicantFormSearchLoading,
- applicantFormOptions,
- flowUserOptions,
- detailTotalAmount,
- approvalRuleHint,
- handleQuery,
- resetSearch,
- pagination,
- remoteSearchApplicantForm,
- userSelectLabel,
- onApplicantChange,
- onExpenseCategoryChange,
- applyTemplate,
- onDetailAmountChange,
- onApprovalFlowChange,
- addExpenseDetail,
- removeExpenseDetail,
- syncApplyAmountFromDetails,
- autoAssignApprovalFlow,
- openFormDialog,
- onFormClosed,
+ submitDialog,
submitForm,
- approvalActionLabel,
- submitApprove,
- handleExport,
- handleImportClick,
- onImportFile,
-} = cr;
+ submitFormRef,
+ submitSaving,
+ isSubmitEdit,
+ activeTemplate,
+ submitFormFields,
+ submitFormRules,
+ submitDialogTitle,
+ templateBindVisible,
+ handleQuery,
+ initModuleList,
+ pagination,
+ openAddWithTemplate,
+ onTemplateBound,
+ onTemplateBindClosed,
+ openEditFromDetail,
+ submitInstanceForm,
+ buildTableActions,
+} = mod;
+
+const { flowUserOptions, loadFlowUsers } = useFlowUserOptions();
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions);
+
+function onSearch() {
+ handleQuery(searchForm);
+}
+
+function resetSearch() {
+ searchForm.applicantKeyword = "";
+ onSearch();
+}
+
+function onPagination(obj) {
+ pagination(obj, searchForm);
+}
+
+async function onSubmit() {
+ const ok = await submitInstanceForm({ skipValidate: true });
+ if (ok) ElMessage.success(isSubmitEdit.value ? "淇敼鎴愬姛" : "鎻愪氦鎴愬姛");
+}
+
+onMounted(async () => {
+ loadFlowUsers();
+ await initModuleList(searchForm);
+});
</script>
<style scoped>
.mb20 {
margin-bottom: 20px;
-}
-.mb16 {
- margin-bottom: 16px;
-}
-.mb12 {
- margin-bottom: 12px;
-}
-.mt16 {
- margin-top: 16px;
}
.search_form {
display: flex;
@@ -459,98 +153,8 @@
justify-content: space-between;
gap: 12px;
}
-.search_fields {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 4px;
-}
-.search_actions {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
-}
.search_title {
font-size: 14px;
color: var(--el-text-color-regular);
-}
-.sr-only-input {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border: 0;
-}
-.template-bar {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 8px;
-}
-.template-label {
- font-size: 14px;
- color: var(--el-text-color-secondary);
- flex-shrink: 0;
-}
-.form-section {
- margin-bottom: 16px;
- border: 1px solid var(--el-border-color-lighter);
-}
-.form-section :deep(.el-card__header) {
- padding: 12px 16px;
- background: var(--el-fill-color-lighter);
-}
-.form-section :deep(.el-card__body) {
- padding: 16px 16px 4px;
-}
-.card-header-title {
- font-size: 15px;
- font-weight: 600;
-}
-.card-header-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
-}
-.amount-row {
- display: flex;
- align-items: center;
- gap: 12px;
- width: 100%;
-}
-.amount-input {
- flex: 1;
- min-width: 160px;
-}
-.attachment-form-item {
- margin-bottom: 0;
-}
-.detail-table {
- margin-bottom: 0;
-}
-.upload-block {
- width: 100%;
-}
-.flow-tip {
- font-size: 12px;
- color: var(--el-text-color-secondary);
- margin-top: 8px;
-}
-.cost-reimburse-form-dialog :deep(.el-dialog__body) {
- padding-top: 12px;
-}
-.cost-reimburse-form :deep(.el-form-item) {
- margin-bottom: 18px;
-}
-.cost-reimburse-form :deep(.el-input-number) {
- width: 100%;
-}
-.cost-reimburse-form :deep(.el-row) {
- margin-bottom: 0;
}
</style>
--
Gitblit v1.9.3