From fa7fa3b7e38e6828dbb6964e72eac8e6b87c2b6e Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期三, 02 九月 2026 18:53:10 +0800
Subject: [PATCH] 批量提交和批量审批

---
 src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue |  219 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 198 insertions(+), 21 deletions(-)

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
index b87a964..01b94a0 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
@@ -3,30 +3,35 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div class="search_fields">
-        <span class="search_title">瀹℃壒绫诲瀷锛�</span>
+        <span class="search_title">妯℃澘绫诲瀷锛�</span>
         <el-select
-          v-model="searchForm.approvalType"
-          placeholder="璇烽�夋嫨瀹℃壒绫诲瀷"
+          v-model="searchForm.businessType"
+          placeholder="璇烽�夋嫨妯℃澘绫诲瀷"
           clearable
           filterable
           style="width: 200px"
         >
           <el-option
-            v-for="opt in APPROVAL_TYPE_OPTIONS"
+            v-for="opt in searchBusinessTypeOptions"
+            :key="`search-biz-type-${opt.value}`"
+            :label="opt.label"
+            :value="opt.value"
+          />
+        </el-select>
+        <span class="search_title" style="margin-left: 12px">瀹℃壒鐘舵�侊細</span>
+        <el-select
+          v-model="searchForm.status"
+          placeholder="璇烽�夋嫨瀹℃壒鐘舵��"
+          clearable
+          style="width: 140px"
+        >
+          <el-option
+            v-for="opt in APPROVAL_STATUS_SEARCH_OPTIONS"
             :key="opt.value"
             :label="opt.label"
             :value="opt.value"
           />
         </el-select>
-        <span class="search_title" style="margin-left: 12px">鐢宠浜哄悕绉帮細</span>
-        <el-input
-          v-model="searchForm.applicantKeyword"
-          style="width: 200px"
-          placeholder="璇疯緭鍏ョ敵璇蜂汉鍚嶇О"
-          clearable
-          :prefix-icon="Search"
-          @keyup.enter="handleQuery"
-        />
         <span class="search_title" style="margin-left: 12px">鍒涘缓鏃堕棿锛�</span>
         <el-date-picker
           v-model="searchForm.createTimeRange"
@@ -43,6 +48,13 @@
         <el-button :icon="RefreshRight" @click="resetSearch">閲嶇疆</el-button>
       </div>
       <div class="search_actions">
+        <el-button
+          type="success"
+          :icon="CircleCheck"
+          @click="openBatchApproveDialog"
+        >
+          鎵归噺瀹℃壒
+        </el-button>
         <el-button type="primary" :icon="Plus" @click="openSubmitDialog">鎻愪氦瀹℃壒</el-button>
       </div>
     </div>
@@ -53,9 +65,10 @@
         :column="tableColumn"
         :tableData="tableData"
         :page="page"
-        :isSelection="false"
+        :isSelection="true"
         :tableLoading="tableLoading"
         :total="page.total"
+        @selection-change="handleSelectionChange"
         @pagination="pagination"
       >
         <template #approveType="{ row }">
@@ -167,7 +180,7 @@
       class="approve-detail-dialog"
     >
       <div class="approve-detail-body">
-        <ApproveDetailPanel :row="detailRow" />
+        <ApproveDetailPanel :row="detailRow" :detail-data="detailData" />
         <div class="detail-block">
           <div class="detail-block-title">
             瀹℃壒娴佺▼锛坽{ detailRow.tasks?.length || detailRow.flowNodes?.length || 0 }} 椤癸級
@@ -218,6 +231,64 @@
       </template>
     </el-dialog>
 
+    <!-- 宸梾/璐圭敤鎶ラ攢璇︽儏锛堝鎵瑰垪琛級 -->
+    <el-dialog
+      v-model="reimburseDialog.visible"
+      :title="reimburseDialog.mode === 'approve' ? reimburseApproveTitle : reimburseDetailTitle"
+      width="1000px"
+      append-to-body
+      destroy-on-close
+      @closed="approveOpinion = ''"
+    >
+      <FinReimburseApprovePanel
+        :mode="reimburseDialog.mode"
+        :module-key="reimburseDialog.moduleKey"
+        :reimburse-row="reimburseDialog.reimburseRow"
+        :loading="reimburseDialog.loading"
+        v-model:approve-opinion="approveOpinion"
+      />
+      <template #footer>
+        <template v-if="reimburseDialog.mode === 'approve'">
+          <el-button
+            type="success"
+            :loading="approveSubmitting"
+            @click="onReimburseApprove('approved')"
+          >
+            閫� 杩�
+          </el-button>
+          <el-button
+            type="danger"
+            :loading="approveSubmitting"
+            @click="onReimburseApprove('rejected')"
+          >
+            椹� 鍥�
+          </el-button>
+          <el-button :disabled="approveSubmitting" @click="reimburseDialog.visible = false">
+            鍙� 娑�
+          </el-button>
+        </template>
+        <template v-else>
+          <el-button
+            v-if="reimburseDialog.instanceRow?.approvalStatus === 'pending'"
+            @click="openEditFromReimburseDetail"
+          >
+            淇� 鏀�
+          </el-button>
+          <el-button
+            v-if="
+              reimburseDialog.instanceRow?.approvalStatus === 'pending' &&
+              reimburseDialog.instanceRow?.isApprove
+            "
+            type="primary"
+            @click="openReimburseApproveFromDetail"
+          >
+            鍘诲鎵�
+          </el-button>
+          <el-button type="primary" @click="reimburseDialog.visible = false">鍏� 闂�</el-button>
+        </template>
+      </template>
+    </el-dialog>
+
     <!-- 瀹℃壒鎿嶄綔 -->
     <el-dialog
       v-model="approveDialog.visible"
@@ -227,7 +298,7 @@
       destroy-on-close
       @closed="approveOpinion = ''"
     >
-      <ApproveDetailPanel :row="approveDialog.row" />
+      <ApproveDetailPanel :row="approveDialog.row" :detail-data="detailData" />
       <div class="detail-block mt16">
         <div class="detail-block-title">
           瀹℃壒娴佺▼锛坽{ approveDialog.row?.tasks?.length || approveDialog.row?.flowNodes?.length || 0 }} 椤癸級
@@ -266,13 +337,57 @@
         </el-button>
       </template>
     </el-dialog>
+
+    <!-- 鎵归噺瀹℃壒鎿嶄綔 -->
+    <el-dialog
+      v-model="batchApproveDialog.visible"
+      title="鎵归噺瀹℃壒澶勭悊"
+      width="560px"
+      append-to-body
+      destroy-on-close
+      @closed="batchApproveOpinion = ''"
+    >
+      <el-form label-width="100px">
+        <el-form-item label="瀹℃壒鎰忚">
+          <el-input
+            v-model="batchApproveOpinion"
+            type="textarea"
+            :rows="4"
+            maxlength="500"
+            show-word-limit
+            placeholder="閫氳繃鍙暀绌猴紱椹冲洖璇峰~鍐欏叿浣撳師鍥�"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button
+          type="success"
+          :loading="batchApproveSubmitting"
+          @click="onBatchApprove('approved')"
+        >
+          閫� 杩�
+        </el-button>
+        <el-button
+          type="danger"
+          :loading="batchApproveSubmitting"
+          @click="onBatchApprove('rejected')"
+        >
+          椹� 鍥�
+        </el-button>
+        <el-button :disabled="batchApproveSubmitting" @click="batchApproveDialog.visible = false">
+          鍙� 娑�
+        </el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { Plus, RefreshRight } from "@element-plus/icons-vue";
+import { CircleCheck, Plus, RefreshRight } from "@element-plus/icons-vue";
 import { ElMessage } from "element-plus";
-import { onMounted, ref } from "vue";
+import { computed, onMounted, ref } from "vue";
+import { APPROVAL_MODULE_KEYS } from "../approve-shared/approvalModuleRegistry.js";
+import FinReimburseApprovePanel from "../../ReimburseManage/shared/components/FinReimburseApprovePanel.vue";
 import ApprovalTemplateFormSection from "../approve-shared/components/ApprovalTemplateFormSection.vue";
 import ApprovalTemplatePicker from "../approve-shared/components/ApprovalTemplatePicker.vue";
 import { useFlowUserOptions } from "../approve-shared/useFlowUserOptions.js";
@@ -285,7 +400,9 @@
 const al = useApproveList();
 const {
   Search,
-  APPROVAL_TYPE_OPTIONS,
+  APPROVAL_STATUS_SEARCH_OPTIONS,
+  searchBusinessTypeOptions,
+  loadSearchBusinessTypeOptions,
   submitBusinessTypeOptions,
   submitTemplateCards,
   selectedBusinessTypeLabel,
@@ -302,9 +419,15 @@
   tableColumn,
   detailDialog,
   detailRow,
+  detailData,
+  reimburseDialog,
   approveDialog,
   approveOpinion,
   approveSubmitting,
+  batchApproveDialog,
+  batchApproveOpinion,
+  batchApproveSubmitting,
+  submitReimburseApprove,
   submitDialog,
   isSubmitEdit,
   submitDialogTitle,
@@ -317,6 +440,8 @@
   handleQuery,
   resetSearch,
   pagination,
+  handleSelectionChange,
+  openBatchApproveDialog,
   resetSubmitDialogState,
   openSubmitDialog,
   openEditDialog,
@@ -324,6 +449,7 @@
   backToTemplatePick,
   submitInstanceForm,
   submitApprove,
+  submitBatchApprove,
   openDetail,
   openApprove,
 } = al;
@@ -334,6 +460,17 @@
   const ok = await submitInstanceForm();
   if (ok) ElMessage.success(isSubmitEdit.value ? "淇敼鎴愬姛" : "瀹℃壒宸叉彁浜�");
 }
+
+const reimburseDetailTitle = computed(() =>
+  reimburseDialog.moduleKey === APPROVAL_MODULE_KEYS.COST_REIMBURSE
+    ? "璐圭敤鎶ラ攢璇︽儏"
+    : "宸梾鎶ラ攢璇︽儏"
+);
+const reimburseApproveTitle = computed(() =>
+  reimburseDialog.moduleKey === APPROVAL_MODULE_KEYS.COST_REIMBURSE
+    ? "璐圭敤鎶ラ攢瀹℃壒"
+    : "宸梾鎶ラ攢瀹℃壒"
+);
 
 async function onApprove(result) {
   const ret = await submitApprove(result);
@@ -346,14 +483,40 @@
   }
 }
 
+async function onReimburseApprove(result) {
+  const ret = await submitReimburseApprove(result);
+  if (ret?.needOpinion) {
+    ElMessage.warning("椹冲洖鏃惰濉啓瀹℃壒鎰忚");
+    return;
+  }
+  if (ret?.ok) {
+    ElMessage.success(result === "approved" ? "宸查�氳繃" : "宸查┏鍥�");
+  }
+}
+
+async function onBatchApprove(result) {
+  const ret = await submitBatchApprove(result);
+  if (ret?.needOpinion) {
+    ElMessage.warning("椹冲洖鏃惰濉啓瀹℃壒鎰忚");
+    return;
+  }
+  if (ret?.ok) {
+    if (ret.failCount > 0) {
+      ElMessage.warning(`鎴愬姛瀹℃壒${ret.successCount}鏉★紝澶辫触${ret.failCount}鏉);
+    } else {
+      ElMessage.success(result === "approved" ? "鎵归噺瀹℃壒宸查�氳繃" : "鎵归噺瀹℃壒宸查┏鍥�");
+    }
+  }
+}
+
 function formatRecordTime(time) {
   return formatDisplayTime(time) || "鈥�";
 }
 
-function openApproveFromDetail() {
+async function openApproveFromDetail() {
   const row = detailRow.value;
   detailDialog.visible = false;
-  openApprove(row);
+  await openApprove(row);
 }
 
 function openEditFromDetail() {
@@ -362,8 +525,22 @@
   openEditDialog(row);
 }
 
+function openEditFromReimburseDetail() {
+  const row = reimburseDialog.instanceRow;
+  reimburseDialog.visible = false;
+  if (row) openEditDialog(row);
+}
+
+async function openReimburseApproveFromDetail() {
+  const row = reimburseDialog.instanceRow;
+  if (!row) return;
+  reimburseDialog.mode = "approve";
+  approveOpinion.value = "";
+}
+
 onMounted(() => {
   loadFlowUsers();
+  loadSearchBusinessTypeOptions();
   handleQuery();
 });
 </script>

--
Gitblit v1.9.3