From 6689bfb1c2f0638e8493adfa058d57d86e473eac Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期二, 19 五月 2026 17:21:10 +0800
Subject: [PATCH] 审批列表得提交审批根据模板类型区分

---
 src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js               |   74 +++++++++++++++---
 src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js |   36 +++++++++
 src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue                       |   55 +++++++++++++
 src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js         |    7 +
 src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js          |    1 
 src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js       |   31 -------
 6 files changed, 158 insertions(+), 46 deletions(-)

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
index 80af992..8974cf4 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
@@ -50,10 +50,16 @@
   return {
     id: row?.id,
     key: String(row?.id ?? ""),
+    businessType: row?.businessType ?? cfg.approvalType ?? row?.approvalType ?? "",
     approvalType: cfg.approvalType || row?.approvalType || "",
     label: row?.templateName || "鈥�",
     summaryPlaceholder: (row?.description || "").trim() || cfg.summaryPlaceholder || "鐐瑰嚮濉啓骞舵彁浜�",
   };
+}
+
+export function matchBusinessTypeValue(a, b) {
+  if (a == null || a === "" || b == null || b === "") return false;
+  return a === b || a === Number(b) || Number(a) === b || String(a) === String(b);
 }
 
 /** 瀹℃壒璁板綍 approveAction 鈫� 椤甸潰 result */
@@ -275,6 +281,7 @@
   const dto = {
     templateId,
     templateName: submitForm?.templateName || tpl.label || "",
+    businessType: tpl.businessType ?? submitForm?.businessType ?? "",
     title,
     formConfig: buildInstanceFormConfigJson({ ...tpl, fields: tpl.fields || submitForm?.formFieldDefs }, payload),
     tasks: taskList,
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
index cdae763..5fce871 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
@@ -77,7 +77,34 @@
       @closed="resetSubmitDialogState"
     >
       <template v-if="submitDialog.step === 1 && !isSubmitEdit">
-        <p class="template-hint">璇烽�夋嫨宸插惎鐢ㄧ殑瀹℃壒妯℃澘锛岀郴缁熷皢鎸夋ā鏉块厤缃紩瀵煎~鎶ャ��</p>
+        <p class="template-hint">璇峰厛閫夋嫨妯℃澘绫诲瀷锛屽啀閫夋嫨璇ョ被鍨嬩笅宸插惎鐢ㄧ殑瀹℃壒妯℃澘銆�</p>
+        <div v-loading="submitTemplatesLoading" class="template-grid">
+          <div
+            v-for="opt in submitBusinessTypeOptions"
+            :key="`biz-type-${opt.value}`"
+            class="template-card"
+            :class="{ 'is-disabled': !countTemplatesByBusinessType(opt.value) }"
+            @click="onBusinessTypePick(opt.value)"
+          >
+            <span class="template-card-type">{{ opt.label }}</span>
+            <span class="template-card-desc">
+              {{ countTemplatesByBusinessType(opt.value) }} 涓彲鐢ㄦā鏉�
+            </span>
+          </div>
+          <el-empty
+            v-if="!submitTemplatesLoading && !submitBusinessTypeOptions.length"
+            description="鏆傛棤妯℃澘绫诲瀷"
+            :image-size="80"
+            class="template-empty"
+          />
+        </div>
+      </template>
+
+      <template v-else-if="submitDialog.step === 2 && !isSubmitEdit">
+        <p class="template-hint">
+          褰撳墠绫诲瀷锛歿{ selectedBusinessTypeLabel || "鈥�" }}锛岃閫夋嫨鍏蜂綋瀹℃壒妯℃澘銆�
+          <el-button type="primary" link class="ml8" @click="backToBusinessTypePick">鏇存崲绫诲瀷</el-button>
+        </p>
         <div v-loading="submitTemplatesLoading" class="template-grid">
           <div
             v-for="card in submitTemplateCards"
@@ -92,7 +119,7 @@
           </div>
           <el-empty
             v-if="!submitTemplatesLoading && !submitTemplateCards.length"
-            description="鏆傛棤鍙敤瀹℃壒妯℃澘"
+            description="璇ョ被鍨嬩笅鏆傛棤鍙敤瀹℃壒妯℃澘"
             :image-size="80"
             class="template-empty"
           />
@@ -132,12 +159,18 @@
 
       <template #footer>
         <el-button
-          v-if="submitDialog.step === 2 || isSubmitEdit"
+          v-if="submitDialog.step === 3 || isSubmitEdit"
           type="primary"
           :loading="submitSaving"
           @click="onSubmitInstance"
         >
           {{ isSubmitEdit ? "淇� 瀛�" : "鎻� 浜�" }}
+        </el-button>
+        <el-button
+          v-if="submitDialog.step === 2 && !isSubmitEdit"
+          @click="backToBusinessTypePick"
+        >
+          涓婁竴姝�
         </el-button>
         <el-button @click="submitDialog.visible = false">
           {{ submitDialog.step === 1 && !isSubmitEdit ? "鍙� 娑�" : "鍏� 闂�" }}
@@ -274,8 +307,13 @@
 const {
   Search,
   APPROVAL_TYPE_OPTIONS,
+  submitBusinessTypeOptions,
   submitTemplateCards,
+  selectedBusinessTypeLabel,
+  countTemplatesByBusinessType,
   submitTemplatesLoading,
+  onBusinessTypePick,
+  backToBusinessTypePick,
   approvalTypeLabel,
   approvalActionLabel,
   searchForm,
@@ -434,6 +472,17 @@
   border-color: var(--el-color-primary);
   box-shadow: var(--shadow-sm, 0 2px 8px rgba(0, 0, 0, 0.06));
 }
+.template-card.is-disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+.template-card.is-disabled:hover {
+  border-color: var(--el-border-color-lighter);
+  box-shadow: none;
+}
+.ml8 {
+  margin-left: 8px;
+}
 .template-card-type {
   display: inline-block;
   padding: 2px 8px;
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
index 337b00d..4b8510a 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
@@ -1,7 +1,6 @@
 import {
   getApprovalTemplateDetail,
   listApprovalTemplate,
-  TEMPLATE_TYPE_BUILTIN,
   TEMPLATE_TYPE_CUSTOM,
 } from "@/api/officeProcessAutomation/approvalTemplate.js";
 import {
@@ -16,6 +15,7 @@
 import { ElMessage, ElMessageBox } from "element-plus";
 import { computed, reactive, ref } from "vue";
 import {
+  fetchBusinessTypeOptions,
   formatDisplayTime,
   mapEnabledFromApi,
   mapTemplateFromApi,
@@ -36,6 +36,7 @@
   createEmptySubmitForm,
   mapInstanceFromApi,
   mapSubmitTemplateCard,
+  matchBusinessTypeValue,
   validateSubmitFlowNodes,
   unwrapInstancePage,
 } from "./approveListConstants.js";
@@ -45,8 +46,17 @@
   const userStore = useUserStore();
 
   const tableData = ref([]);
-  const submitTemplateCards = ref([]);
+  const submitBusinessTypeOptions = ref([]);
+  const allSubmitTemplates = ref([]);
+  const selectedBusinessType = ref("");
   const submitTemplatesLoading = ref(false);
+
+  const submitTemplateCards = computed(() => {
+    if (selectedBusinessType.value == null || selectedBusinessType.value === "") return [];
+    return allSubmitTemplates.value.filter((card) =>
+      matchBusinessTypeValue(card.businessType, selectedBusinessType.value)
+    );
+  });
 
   const searchForm = reactive({
     approvalType: "",
@@ -75,9 +85,22 @@
     if (submitDialog.mode === "edit") {
       return `淇敼${activeTemplate.value?.label || submitForm.templateName || "瀹℃壒"}`;
     }
-    if (submitDialog.step === 1) return "閫夋嫨瀹℃壒妯℃澘";
+    if (submitDialog.step === 1) return "閫夋嫨妯℃澘绫诲瀷";
+    if (submitDialog.step === 2) return `閫夋嫨瀹℃壒妯℃澘${businessTypeLabel(selectedBusinessType.value) ? `锛�${businessTypeLabel(selectedBusinessType.value)}锛塦 : ""}`;
     return `鎻愪氦${activeTemplate.value?.label || "瀹℃壒"}`;
   });
+
+  const selectedBusinessTypeLabel = computed(() => businessTypeLabel(selectedBusinessType.value));
+
+  function businessTypeLabel(type) {
+    if (type == null || type === "") return "";
+    const hit = submitBusinessTypeOptions.value.find((x) => matchBusinessTypeValue(x.value, type));
+    return hit?.label || "";
+  }
+
+  function countTemplatesByBusinessType(type) {
+    return allSubmitTemplates.value.filter((card) => matchBusinessTypeValue(card.businessType, type)).length;
+  }
 
   const activeTemplate = computed(() => submitForm.templateSnapshot || null);
 
@@ -187,17 +210,17 @@
   async function loadSubmitTemplates() {
     submitTemplatesLoading.value = true;
     try {
-      const [builtinRes, customRes] = await Promise.all([
-        listApprovalTemplate(TEMPLATE_TYPE_BUILTIN),
+      const [typeOptions, customRes] = await Promise.all([
+        fetchBusinessTypeOptions(),
         listApprovalTemplate(TEMPLATE_TYPE_CUSTOM),
       ]);
-      const merged = [
-        ...unwrapTemplateList(builtinRes),
-        ...unwrapTemplateList(customRes),
-      ].filter((row) => mapEnabledFromApi(row.enabled));
-      submitTemplateCards.value = merged.map(mapSubmitTemplateCard);
+      submitBusinessTypeOptions.value = typeOptions;
+      allSubmitTemplates.value = unwrapTemplateList(customRes)
+        .filter((row) => mapEnabledFromApi(row.enabled))
+        .map(mapSubmitTemplateCard);
     } catch {
-      submitTemplateCards.value = [];
+      submitBusinessTypeOptions.value = [];
+      allSubmitTemplates.value = [];
       ElMessage.error("鍔犺浇瀹℃壒妯℃澘澶辫触");
     } finally {
       submitTemplatesLoading.value = false;
@@ -236,6 +259,7 @@
   function resetSubmitDialogState() {
     submitDialog.mode = "add";
     submitDialog.step = 1;
+    selectedBusinessType.value = "";
     submitEditRow.value = null;
     Object.assign(submitForm, createEmptySubmitForm(""));
   }
@@ -256,7 +280,7 @@
       return;
     }
     submitDialog.mode = "edit";
-    submitDialog.step = 2;
+    submitDialog.step = 3;
     submitEditRow.value = { ...row };
     Object.assign(submitForm, buildEditFormFromInstanceRow(row));
     submitDialog.visible = true;
@@ -276,10 +300,11 @@
       Object.assign(submitForm, {
         ...base,
         templateName: mapped.templateName || tpl.label || "",
+        businessType: mapped.businessType ?? card.businessType ?? selectedBusinessType.value,
         templateSnapshot: tpl,
         formFieldDefs: tpl.fields || [],
       });
-      submitDialog.step = 2;
+      submitDialog.step = 3;
     } catch {
       ElMessage.error("鍔犺浇妯℃澘璇︽儏澶辫触");
     } finally {
@@ -287,8 +312,22 @@
     }
   }
 
-  function backToTemplatePick() {
+  function onBusinessTypePick(type) {
+    if (!countTemplatesByBusinessType(type)) {
+      ElMessage.warning("璇ョ被鍨嬩笅鏆傛棤鍙敤瀹℃壒妯℃澘");
+      return;
+    }
+    selectedBusinessType.value = type;
+    submitDialog.step = 2;
+  }
+
+  function backToBusinessTypePick() {
+    selectedBusinessType.value = "";
     submitDialog.step = 1;
+  }
+
+  function backToTemplatePick() {
+    submitDialog.step = 2;
   }
 
   async function submitInstanceForm() {
@@ -474,7 +513,12 @@
     activeTemplate,
     submitFormFields,
     submitFormRules,
+    submitBusinessTypeOptions,
     submitTemplateCards,
+    selectedBusinessType,
+    selectedBusinessTypeLabel,
+    businessTypeLabel,
+    countTemplatesByBusinessType,
     submitTemplatesLoading,
     handleQuery,
     resetSearch,
@@ -482,7 +526,9 @@
     resetSubmitDialogState,
     openSubmitDialog,
     openEditDialog,
+    onBusinessTypePick,
     onTemplatePick,
+    backToBusinessTypePick,
     backToTemplatePick,
     submitInstanceForm,
     submitNewApproval,
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
index 3b5fb21..517e01c 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
@@ -1,4 +1,5 @@
 import dayjs from "dayjs";
+import { getTypeEnums } from "@/api/basicData/enum.js";
 import { TEMPLATE_TYPE_CUSTOM } from "@/api/officeProcessAutomation/approvalTemplate.js";
 import { APPROVAL_TYPE_OPTIONS } from "../approve-list/approveListConstants.js";
 import {
@@ -8,6 +9,41 @@
   validateFormConfigData,
 } from "./formConfigUtils.js";
 
+export function unwrapEnumList(data) {
+  if (Array.isArray(data)) return data;
+  if (!data || typeof data !== "object") return [];
+  if (Array.isArray(data.TypeEnums)) return data.TypeEnums;
+  if (Array.isArray(data.typeEnums)) return data.typeEnums;
+  const nested = Object.values(data).find((v) => Array.isArray(v));
+  return nested || [];
+}
+
+export function normalizeBusinessTypeOptions(data) {
+  return unwrapEnumList(data)
+    .map((item) => {
+      const rawValue = item?.value ?? item?.code ?? item?.businessType ?? item?.dictValue ?? item?.key;
+      if (rawValue == null || rawValue === "") return null;
+      const num = Number(rawValue);
+      const value =
+        typeof rawValue === "number" || (Number.isFinite(num) && String(rawValue).trim() !== "")
+          ? num
+          : rawValue;
+      const label =
+        item?.label ?? item?.name ?? item?.desc ?? item?.dictLabel ?? item?.text ?? String(value);
+      return { label, value };
+    })
+    .filter(Boolean);
+}
+
+export async function fetchBusinessTypeOptions() {
+  try {
+    const res = await getTypeEnums();
+    return normalizeBusinessTypeOptions(res?.data);
+  } catch {
+    return [];
+  }
+}
+
 /** 鑺傜偣鍐呭鎵规柟寮忥細浼氱 / 鎴栫 */
 export const NODE_SIGN_MODE_OPTIONS = [
   { value: "countersign", label: "浼氱", desc: "鏈妭鐐规墍鏈夊鎵逛汉鍧囬渶閫氳繃" },
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
index 0cb20ea..733f463 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
@@ -264,6 +264,7 @@
   }));
   return {
     label: row?.templateName || "瀹℃壒",
+    businessType: row?.businessType ?? cfg.approvalType ?? "",
     approvalType: cfg.approvalType || "",
     summaryPlaceholder: cfg.summaryPlaceholder || "",
     approvalMode: cfg.approvalMode || "parallel",
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
index 3e27afb..95eac7b 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
@@ -6,13 +6,13 @@
   TEMPLATE_TYPE_CUSTOM,
   updateApprovalTemplate,
 } from "@/api/officeProcessAutomation/approvalTemplate.js";
-import { getTypeEnums } from "@/api/basicData/enum.js";
 import { Search } from "@element-plus/icons-vue";
 import { ElMessage, ElMessageBox } from "element-plus";
 import { reactive, ref } from "vue";
 import {
   buildApprovalTemplateListParams,
   createEmptyTemplateForm,
+  fetchBusinessTypeOptions,
   flowNodesSummary,
   mapTemplateFromApi,
   mapTemplateToApi,
@@ -29,32 +29,6 @@
   { value: 0, label: "绯荤粺鍐呯疆" },
   { value: 1, label: "鑷畾涔�" },
 ];
-
-function unwrapEnumList(data) {
-  if (Array.isArray(data)) return data;
-  if (!data || typeof data !== "object") return [];
-  if (Array.isArray(data.TypeEnums)) return data.TypeEnums;
-  if (Array.isArray(data.typeEnums)) return data.typeEnums;
-  const nested = Object.values(data).find((v) => Array.isArray(v));
-  return nested || [];
-}
-
-function normalizeTypeEnumOptions(data) {
-  return unwrapEnumList(data)
-    .map((item) => {
-      const rawValue = item?.value ?? item?.code ?? item?.businessType ?? item?.dictValue ?? item?.key;
-      if (rawValue == null || rawValue === "") return null;
-      const num = Number(rawValue);
-      const value =
-        typeof rawValue === "number" || (Number.isFinite(num) && String(rawValue).trim() !== "")
-          ? num
-          : rawValue;
-      const label =
-        item?.label ?? item?.name ?? item?.desc ?? item?.dictLabel ?? item?.text ?? String(value);
-      return { label, value };
-    })
-    .filter(Boolean);
-}
 
 function matchTemplateTypeValue(options, type) {
   if (type == null || type === "") return false;
@@ -99,8 +73,7 @@
 
   async function loadTemplateTypeOptions() {
     try {
-      const res = await getTypeEnums();
-      const list = normalizeTypeEnumOptions(res?.data);
+      const list = await fetchBusinessTypeOptions();
       templateTypeOptions.value = list.length ? list : [...FALLBACK_TEMPLATE_TYPE_OPTIONS];
     } catch {
       templateTypeOptions.value = [...FALLBACK_TEMPLATE_TYPE_OPTIONS];

--
Gitblit v1.9.3