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/useApproveList.js |   71 +++++++++++++++++
 src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue         |   77 ++++++++++++++++++
 src/views/qualityManagement/finalInspection/index.vue                          |   75 ++++++++++++++++--
 3 files changed, 209 insertions(+), 14 deletions(-)

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
index 4a9cf29..01b94a0 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
@@ -48,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>
@@ -58,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 }">
@@ -329,11 +337,53 @@
         </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 { computed, onMounted, ref } from "vue";
 import { APPROVAL_MODULE_KEYS } from "../approve-shared/approvalModuleRegistry.js";
@@ -374,6 +424,9 @@
   approveDialog,
   approveOpinion,
   approveSubmitting,
+  batchApproveDialog,
+  batchApproveOpinion,
+  batchApproveSubmitting,
   submitReimburseApprove,
   submitDialog,
   isSubmitEdit,
@@ -387,6 +440,8 @@
   handleQuery,
   resetSearch,
   pagination,
+  handleSelectionChange,
+  openBatchApproveDialog,
   resetSubmitDialogState,
   openSubmitDialog,
   openEditDialog,
@@ -394,6 +449,7 @@
   backToTemplatePick,
   submitInstanceForm,
   submitApprove,
+  submitBatchApprove,
   openDetail,
   openApprove,
 } = al;
@@ -435,6 +491,21 @@
   }
   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" ? "鎵归噺瀹℃壒宸查�氳繃" : "鎵归噺瀹℃壒宸查┏鍥�");
+    }
   }
 }
 
@@ -611,4 +682,4 @@
   border-left: 3px solid var(--el-color-primary);
   line-height: 1.4;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
index 25b6ae5..5708239 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
@@ -85,6 +85,10 @@
   const approveDialog = reactive({ visible: false, row: null });
   const approveOpinion = ref("");
   const approveSubmitting = ref(false);
+  const selectedRows = ref([]);
+  const batchApproveDialog = reactive({ visible: false });
+  const batchApproveOpinion = ref("");
+  const batchApproveSubmitting = ref(false);
 
   /** 宸梾/璐圭敤鎶ラ攢涓撶敤璇︽儏銆佸鎵瑰脊绐� */
   const reimburseDialog = reactive({
@@ -189,7 +193,7 @@
         {
           name: "瀹℃壒",
           type: "text",
-          disabled: (row) => row.approvalStatus !== "pending" || !row.isApprove,
+          disabled: (row) => !canApprove(row),
           clickFun: (row) => openApprove(row),
         },
         // {
@@ -263,6 +267,28 @@
     page.current = p;
     page.size = limit;
     fetchApprovalList();
+  }
+
+  function handleSelectionChange(selection) {
+    selectedRows.value = selection || [];
+  }
+
+  function canApprove(row) {
+    return row?.approvalStatus === "pending" && Boolean(row?.isApprove);
+  }
+
+  function openBatchApproveDialog() {
+    if (!selectedRows.value.length) {
+      ElMessage.warning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    const approveRows = selectedRows.value.filter(canApprove);
+    if (!approveRows.length) {
+      ElMessage.warning("鎵�閫夋暟鎹笉鍙鎵�");
+      return;
+    }
+    batchApproveOpinion.value = "";
+    batchApproveDialog.visible = true;
   }
 
   async function openReimburseDetail(row, mode) {
@@ -636,6 +662,40 @@
     }
   }
 
+  async function submitBatchApprove(result) {
+    const approveRows = selectedRows.value.filter(canApprove);
+    if (!approveRows.length) {
+      ElMessage.warning("鎵�閫夋暟鎹笉鍙鎵�");
+      return { ok: false };
+    }
+    if (result === "rejected" && !(batchApproveOpinion.value || "").trim()) {
+      return { needOpinion: true };
+    }
+    if (batchApproveSubmitting.value) return { ok: false };
+
+    batchApproveSubmitting.value = true;
+    try {
+      const results = await Promise.allSettled(
+        approveRows.map((row) =>
+          approveApprovalInstance(
+            buildApproveInstanceDto(row, result, batchApproveOpinion.value)
+          )
+        )
+      );
+      const successCount = results.filter((item) => item.status === "fulfilled").length;
+      const failCount = approveRows.length - successCount;
+
+      batchApproveDialog.visible = false;
+      await fetchApprovalList();
+      return { ok: successCount > 0, result, successCount, failCount };
+    } catch {
+      ElMessage.error("鎵归噺瀹℃壒澶辫触");
+      return { ok: false };
+    } finally {
+      batchApproveSubmitting.value = false;
+    }
+  }
+
   function approvalActionLabel(result) {
     if (result === "approved") return "閫氳繃";
     if (result === "rejected") return "椹冲洖";
@@ -664,6 +724,10 @@
     approveDialog,
     approveOpinion,
     approveSubmitting,
+    selectedRows,
+    batchApproveDialog,
+    batchApproveOpinion,
+    batchApproveSubmitting,
     submitReimburseApprove,
     isReimburseApprovalInstance,
     submitDialog,
@@ -685,6 +749,8 @@
     handleQuery,
     resetSearch,
     pagination,
+    handleSelectionChange,
+    openBatchApproveDialog,
     resetSubmitDialogState,
     openSubmitDialog,
     openEditDialog,
@@ -695,8 +761,9 @@
     submitInstanceForm,
     submitNewApproval,
     submitApprove,
+    submitBatchApprove,
     openDetail,
     openApprove,
     fetchApprovalList,
   };
-}
\ No newline at end of file
+}
diff --git a/src/views/qualityManagement/finalInspection/index.vue b/src/views/qualityManagement/finalInspection/index.vue
index 4a59fa4..e671ab5 100644
--- a/src/views/qualityManagement/finalInspection/index.vue
+++ b/src/views/qualityManagement/finalInspection/index.vue
@@ -59,6 +59,10 @@
         <el-button type="primary"
                    @click="openForm('add')">鏂板</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
+        <el-button type="primary"
+                   plain
+                   :loading="batchSubmitting"
+                   @click="handleBatchSubmit">鎵归噺鎻愪氦</el-button>
         <el-button type="danger"
                    plain
                    @click="handleDelete">鍒犻櫎</el-button>
@@ -289,15 +293,7 @@
           clickFun: row => {
             submit(row.id);
           },
-          disabled: row => {
-            // 宸叉彁浜ゅ垯绂佺敤
-            if (row.inspectState == 1) return true;
-            // 濡傛灉妫�楠屽憳鏈夊�硷紝鍙湁褰撳墠鐧诲綍鐢ㄦ埛鑳芥彁浜�
-            if (row.checkName) {
-              return row.checkName !== userStore.nickName;
-            }
-            return false;
-          },
+          disabled: row => !canSubmit(row),
         },
         {
           name: "鍒嗛厤妫�楠屽憳",
@@ -326,6 +322,7 @@
   const tableData = ref([]);
   const selectedRows = ref([]);
   const tableLoading = ref(false);
+  const batchSubmitting = ref(false);
   const currentRow = ref(null);
   const page = reactive({
     current: 1,
@@ -391,6 +388,16 @@
   // 琛ㄦ牸閫夋嫨鏁版嵁
   const handleSelectionChange = selection => {
     selectedRows.value = selection;
+  };
+
+  const canSubmit = row => {
+    // 宸叉彁浜ゅ垯绂佺敤
+    if (row.inspectState == 1) return false;
+    // 濡傛灉妫�楠屽憳鏈夊�硷紝鍙湁褰撳墠鐧诲綍鐢ㄦ埛鑳芥彁浜�
+    if (row.checkName) {
+      return row.checkName === userStore.nickName;
+    }
+    return true;
   };
 
   // 鎵撳紑寮规
@@ -464,6 +471,56 @@
     }
   };
 
+  const handleBatchSubmit = () => {
+    if (selectedRows.value.length === 0) {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+
+    const submitRows = selectedRows.value.filter(item => canSubmit(item));
+    if (submitRows.length === 0) {
+      proxy.$modal.msgWarning("鎵�閫夋暟鎹笉鍙彁浜�");
+      return;
+    }
+
+    ElMessageBox.confirm(
+      `閫変腑鐨�${submitRows.length}鏉″唴瀹瑰皢琚彁浜わ紝鏄惁纭鎻愪氦锛焋,
+      "鎵归噺鎻愪氦",
+      {
+        confirmButtonText: "纭",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      }
+    )
+      .then(async () => {
+        batchSubmitting.value = true;
+        const results = await Promise.allSettled(
+          submitRows.map(item => submitQualityInspect({ id: item.id }))
+        );
+        const successCount = results.filter(
+          item => item.status === "fulfilled" && item.value?.code === 200
+        ).length;
+        const failCount = submitRows.length - successCount;
+
+        if (successCount > 0 && failCount === 0) {
+          proxy.$modal.msgSuccess("鎵归噺鎻愪氦鎴愬姛");
+        } else if (successCount > 0) {
+          proxy.$modal.msgWarning(
+            `鎴愬姛鎻愪氦${successCount}鏉★紝澶辫触${failCount}鏉
+          );
+        } else {
+          proxy.$modal.msgError("鎵归噺鎻愪氦澶辫触");
+        }
+        getList();
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      })
+      .finally(() => {
+        batchSubmitting.value = false;
+      });
+  };
+
   // 鍏抽棴寮规
   const closeDia = () => {
     proxy.resetForm("formRef");

--
Gitblit v1.9.3