From 07d766a545881be779de94a800f6494ec46c1001 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期二, 19 五月 2026 17:10:08 +0800
Subject: [PATCH] 模板类型接口获取

---
 src/api/basicData/enum.js                                                                    |    8 
 src/api/officeProcessAutomation/approvalTemplate.js                                          |    5 
 src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js |   21 -
 src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js       |  102 +++++---
 src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue                   |  552 +++++++++++++++++++++++++++++++++++----------
 5 files changed, 504 insertions(+), 184 deletions(-)

diff --git a/src/api/basicData/enum.js b/src/api/basicData/enum.js
index 350df17..9e8b503 100644
--- a/src/api/basicData/enum.js
+++ b/src/api/basicData/enum.js
@@ -1,5 +1,13 @@
 import request from "@/utils/request.js";
 
+/** 瀹℃壒妯℃澘绫诲瀷绛夐�氱敤鏋氫妇锛圱ypeEnums锛� */
+export function getTypeEnums() {
+    return request({
+        url: '/basic/enum/TypeEnums',
+        method: 'get'
+    })
+}
+
 export function findAllStockRecordTypeOptions() {
     return request({
         url: '/basic/enum/stockRecordType',
diff --git a/src/api/officeProcessAutomation/approvalTemplate.js b/src/api/officeProcessAutomation/approvalTemplate.js
index 4aef140..3ade018 100644
--- a/src/api/officeProcessAutomation/approvalTemplate.js
+++ b/src/api/officeProcessAutomation/approvalTemplate.js
@@ -4,11 +4,6 @@
 export const TEMPLATE_TYPE_BUILTIN = 0;
 export const TEMPLATE_TYPE_CUSTOM = 1;
 
-export const TEMPLATE_TYPE_OPTIONS = [
-  { value: TEMPLATE_TYPE_BUILTIN, label: "绯荤粺鍐呯疆" },
-  { value: TEMPLATE_TYPE_CUSTOM, label: "鑷畾涔�" },
-];
-
 /** 鏌ヨ鎵�鏈夊鎵规ā鏉� */
 export function listApprovalTemplate(type) {
   return request({
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
index 3325e55..3b5fb21 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
@@ -1,8 +1,5 @@
 import dayjs from "dayjs";
-import {
-  TEMPLATE_TYPE_CUSTOM,
-  TEMPLATE_TYPE_OPTIONS,
-} from "@/api/officeProcessAutomation/approvalTemplate.js";
+import { TEMPLATE_TYPE_CUSTOM } from "@/api/officeProcessAutomation/approvalTemplate.js";
 import { APPROVAL_TYPE_OPTIONS } from "../approve-list/approveListConstants.js";
 import {
   buildFormConfigJson,
@@ -10,14 +7,6 @@
   parseFormConfigToData,
   validateFormConfigData,
 } from "./formConfigUtils.js";
-
-export { TEMPLATE_TYPE_OPTIONS };
-
-export function templateTypeLabel(type) {
-  if (type == null || type === "") return "鈥�";
-  const n = Number(type);
-  return TEMPLATE_TYPE_OPTIONS.find((x) => x.value === n)?.label || "鈥�";
-}
 
 /** 鑺傜偣鍐呭鎵规柟寮忥細浼氱 / 鎴栫 */
 export const NODE_SIGN_MODE_OPTIONS = [
@@ -165,6 +154,7 @@
     enabled: mapEnabledFromApi(row.enabled),
     enabledRaw: row.enabled,
     templateType: row.templateType != null ? Number(row.templateType) : undefined,
+    businessType: row.businessType ?? "",
     formConfig: row.formConfig,
     formConfigData: parseFormConfigToData(row.formConfig),
     createdUser: row.createdUser,
@@ -183,7 +173,8 @@
     templateName: (form.templateName || "").trim(),
     description: (form.description || "").trim(),
     enabled: mapEnabledToApi(form.enabled),
-    templateType: form.templateType ?? TEMPLATE_TYPE_CUSTOM,
+    templateType: TEMPLATE_TYPE_CUSTOM,
+    businessType: form.businessType ?? "",
     formConfig: buildFormConfigJson(form.formConfigData),
     nodes: nodes.map((n, i) => {
       const node = {
@@ -248,6 +239,7 @@
     templateName: "",
     description: "",
     templateType: TEMPLATE_TYPE_CUSTOM,
+    businessType: "",
     formConfig: "",
     formConfigData: createEmptyFormConfigData(),
     enabled: true,
@@ -277,6 +269,9 @@
 export function validateTemplateForm(form) {
   const name = (form.templateName || "").trim();
   if (!name) return { ok: false, message: "璇峰~鍐欐ā鏉垮悕绉�" };
+  if (form.businessType == null || form.businessType === "") {
+    return { ok: false, message: "璇烽�夋嫨妯℃澘绫诲瀷" };
+  }
   const nodes = normalizeFlowNodes(form.flowNodes);
   if (!nodes.length) return { ok: false, message: "璇疯嚦灏戦厤缃竴涓鎵硅妭鐐�" };
   for (let i = 0; i < nodes.length; i++) {
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
index 9d7324c..5bc65dd 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
@@ -1,432 +1,724 @@
 <!--OA妯″潡锛氬鎵规ā鏉�-->
+
 <template>
+
   <div class="app-container approve-template-page">
-    <el-tabs v-model="activeTab" class="template-tabs">
-      <el-tab-pane label="绯荤粺甯哥敤瀹℃壒" name="builtin">
-        <el-alert type="info" show-icon :closable="false" class="mb16">
-          <template #title>绯荤粺棰勭疆妯℃澘</template>
-          <template #default>
-            浠ヤ笅涓� OA 妯″潡鍐呯疆鐨勫父鐢ㄥ鎵圭被鍨嬶紝濉姤瀛楁涓庨粯璁ゅ鎵规柟寮忕敱绯荤粺缁存姢锛涙彁浜ゅ鎵规椂鍙洿鎺ラ�夌敤銆�
-          </template>
-        </el-alert>
-        <div v-loading="builtinLoading" class="builtin-grid">
-          <template v-if="builtinTemplates.length">
-            <div v-for="item in builtinTemplates" :key="item.key" class="builtin-card">
-            <span class="builtin-label">{{ item.label }}</span>
-            <p class="builtin-summary">{{ item.summary }}</p>
-            <div class="builtin-meta">
-              <el-tag size="small" effect="plain">{{ item.fieldCount }} 涓~鎶ラ」</el-tag>
-              <el-tag size="small" type="warning" effect="plain">
-                榛樿{{ item.defaultMode === "or_sign" ? "鎴栫" : "涓庣" }}
-              </el-tag>
-              <el-tag size="small" type="info" effect="plain">鍙</el-tag>
-            </div>
-            </div>
-          </template>
-          <el-empty v-else-if="!builtinLoading" description="鏆傛棤绯荤粺甯哥敤瀹℃壒妯℃澘" :image-size="80" />
-        </div>
-      </el-tab-pane>
 
-      <el-tab-pane label="鑷畾涔夊鎵规ā鏉�" name="custom">
-        <div class="search_form mb20">
-          <div class="search_fields">
-            <span class="search_title">妯℃澘鍚嶇О锛�</span>
-            <el-input
-              v-model="searchForm.keyword"
-              style="width: 220px"
-              placeholder="鎼滅储鍚嶇О鎴栬鏄�"
-              clearable
-              :prefix-icon="Search"
-              @keyup.enter="handleQuery"
-            />
-            <el-checkbox v-model="searchForm.enabledOnly" class="ml12" @change="handleQuery">
-              浠呮樉绀哄惎鐢�
-            </el-checkbox>
-            <el-button type="primary" :icon="Search" class="ml10" @click="handleQuery">鎼滅储</el-button>
-            <el-button :icon="RefreshRight" @click="resetSearch">閲嶇疆</el-button>
-          </div>
-          <div class="search_actions">
-            <el-button type="primary" :icon="Plus" @click="openFormDialog('add')">鏂板缓妯℃澘</el-button>
-          </div>
-        </div>
+    <div class="search_form mb20">
 
-        <div class="table_list">
-          <PIMTable
-            rowKey="id"
-            :column="tableColumn"
-            :tableData="tableData"
-            :page="page"
-            :isSelection="false"
-            :tableLoading="tableLoading"
-            :total="page.total"
-            @pagination="pagination"
-          />
-        </div>
-      </el-tab-pane>
-    </el-tabs>
+      <div class="search_fields">
+
+        <span class="search_title">妯℃澘鍚嶇О锛�</span>
+
+        <el-input
+
+          v-model="searchForm.keyword"
+
+          style="width: 220px"
+
+          placeholder="鎼滅储鍚嶇О鎴栬鏄�"
+
+          clearable
+
+          :prefix-icon="Search"
+
+          @keyup.enter="handleQuery"
+
+        />
+
+        <el-checkbox v-model="searchForm.enabledOnly" class="ml12" @change="handleQuery">
+
+          浠呮樉绀哄惎鐢�
+
+        </el-checkbox>
+
+        <el-button type="primary" :icon="Search" class="ml10" @click="handleQuery">鎼滅储</el-button>
+
+        <el-button :icon="RefreshRight" @click="resetSearch">閲嶇疆</el-button>
+
+      </div>
+
+      <div class="search_actions">
+
+        <el-button type="primary" :icon="Plus" @click="openFormDialog('add')">鏂板缓妯℃澘</el-button>
+
+      </div>
+
+    </div>
+
+
+
+    <div class="table_list">
+
+      <PIMTable
+
+        rowKey="id"
+
+        :column="tableColumn"
+
+        :tableData="tableData"
+
+        :page="page"
+
+        :isSelection="false"
+
+        :tableLoading="tableLoading"
+
+        :total="page.total"
+
+        @pagination="pagination"
+
+      />
+
+    </div>
+
+
 
     <!-- 鏂板缓 / 缂栬緫 -->
+
     <el-dialog
+
       v-model="formDialog.visible"
+
       :title="formDialog.title"
+
       width="1020px"
+
       append-to-body
+
       destroy-on-close
+
       class="template-form-dialog"
-      @closed="formRef?.resetFields?.()"
+
+      @closed="onFormDialogClosed"
+
     >
-      <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
+
+      <el-form
+
+        v-if="formDialog.visible"
+
+        ref="formRef"
+
+        :model="form"
+
+        :rules="formRules"
+
+        label-width="100px"
+
+      >
+
         <el-row :gutter="20">
+
           <el-col :span="8">
+
             <el-form-item label="妯℃澘鍚嶇О" prop="templateName">
+
               <el-input v-model="form.templateName" placeholder="濡傦細椤圭洰绔嬮」瀹℃壒" maxlength="50" show-word-limit />
+
             </el-form-item>
+
           </el-col>
+
           <el-col :span="8">
-            <el-form-item label="妯℃澘绫诲瀷" prop="templateType">
-              <el-select v-model="form.templateType" placeholder="璇烽�夋嫨" style="width: 100%">
+
+            <el-form-item label="妯℃澘绫诲瀷" prop="businessType">
+
+              <el-select v-model="form.businessType" placeholder="璇烽�夋嫨" style="width: 100%">
+
                 <el-option
-                  v-for="opt in TEMPLATE_TYPE_OPTIONS"
-                  :key="opt.value"
+
+                  v-for="opt in templateTypeOptions"
+
+                  :key="`tpl-type-${opt.value}`"
+
                   :label="opt.label"
+
                   :value="opt.value"
+
                 />
+
               </el-select>
+
             </el-form-item>
+
           </el-col>
+
           <el-col :span="8">
+
             <el-form-item label="鍚敤鐘舵��">
+
               <el-switch v-model="form.enabled" active-text="鍚敤" inactive-text="鍋滅敤" />
+
             </el-form-item>
+
           </el-col>
+
         </el-row>
+
         <el-form-item label="妯℃澘璇存槑">
+
           <el-input
+
             v-model="form.description"
+
             type="textarea"
+
             :rows="2"
+
             placeholder="绠�瑕佽鏄庤妯℃澘鐨勯�傜敤鍦烘櫙"
+
             maxlength="200"
+
             show-word-limit
+
           />
+
         </el-form-item>
+
         <el-form-item label="濉姤閰嶇疆">
+
           <FormConfigEditor v-model="form.formConfigData" />
+
           <p class="flow-tip">閰嶇疆鎻愪氦瀹℃壒鏃堕渶濉啓鐨勮〃鍗曢」锛屼繚瀛樺悗鍐欏叆 formConfig锛圝SON锛夈��</p>
+
         </el-form-item>
+
         <el-form-item label="瀹℃壒娴佺▼" required>
+
           <TemplateFlowEditor v-model="form.flowNodes" :user-options="flowUserOptions" />
+
           <p class="flow-tip">
+
             鎸夐『搴忔祦杞細鍙负姣忎釜鑺傜偣娣诲姞澶氬悕瀹℃壒浜猴紱浼氱闇�鍏ㄩ儴閫氳繃锛屾垨绛句换涓�浜洪�氳繃鍗冲彲杩涘叆涓嬩竴鑺傜偣銆�
+
           </p>
+
         </el-form-item>
+
       </el-form>
+
       <template #footer>
+
         <el-button type="primary" @click="onSubmitForm">淇� 瀛�</el-button>
+
         <el-button @click="formDialog.visible = false">鍙� 娑�</el-button>
+
       </template>
+
     </el-dialog>
+
+
 
     <!-- 璇︽儏 -->
+
     <el-dialog v-model="detailDialog.visible" title="妯℃澘璇︽儏" width="880px" append-to-body destroy-on-close>
+
       <div v-loading="detailLoading" class="detail-dialog-body">
+
       <el-descriptions :column="2" border>
+
         <el-descriptions-item label="妯℃澘鍚嶇О">{{ detailRow.templateName }}</el-descriptions-item>
-        <el-descriptions-item label="妯℃澘绫诲瀷">{{ templateTypeLabel(detailRow.templateType) }}</el-descriptions-item>
+
+        <el-descriptions-item label="妯℃澘绫诲瀷">{{ templateTypeLabel(detailRow.businessType) }}</el-descriptions-item>
+
         <el-descriptions-item label="鐘舵��">
+
           <el-tag :type="detailRow.enabled !== false ? 'success' : 'info'" size="small">
+
             {{ detailRow.enabled !== false ? "鍚敤" : "鍋滅敤" }}
+
           </el-tag>
+
         </el-descriptions-item>
+
         <el-descriptions-item label="璇存槑" :span="2">{{ detailRow.description || "鈥�" }}</el-descriptions-item>
+
         <el-descriptions-item label="濉姤鎻愮ず" :span="2">
+
           {{ detailFormConfig.summaryPlaceholder || "鈥�" }}
+
         </el-descriptions-item>
+
         <el-descriptions-item label="鍒涘缓浜�">{{ detailRow.createdUserName || "鈥�" }}</el-descriptions-item>
+
         <el-descriptions-item label="鍒涘缓鏃堕棿">{{ formatDisplayTime(detailRow.createdTime) }}</el-descriptions-item>
+
         <el-descriptions-item label="鏇存柊鏃堕棿">{{ formatDisplayTime(detailRow.updatedTime) }}</el-descriptions-item>
+
       </el-descriptions>
+
       <el-divider content-position="left">濉姤椤癸紙{{ detailFormConfig.fields?.length || 0 }} 椤癸級</el-divider>
+
       <el-table
+
         v-if="detailFormConfig.fields?.length"
+
         :data="detailFormConfig.fields"
+
         border
+
         size="small"
+
         class="mb16"
+
       >
+
         <el-table-column prop="label" label="鏄剧ず鍚嶇О" min-width="120" />
+
         <el-table-column prop="key" label="瀛楁鏍囪瘑" min-width="100" />
+
         <el-table-column label="绫诲瀷" width="100">
+
           <template #default="{ row }">{{ formFieldTypeLabel(row.type) }}</template>
+
         </el-table-column>
+
         <el-table-column label="蹇呭~" width="70" align="center">
+
           <template #default="{ row }">{{ row.required !== false ? "鏄�" : "鍚�" }}</template>
+
         </el-table-column>
+
         <el-table-column label="榛樿鍊�" min-width="120" show-overflow-tooltip>
+
           <template #default="{ row }">{{ formatDefaultValueDisplay(row) }}</template>
+
         </el-table-column>
+
       </el-table>
+
       <el-empty v-else description="鏈厤缃~鎶ラ」" :image-size="48" class="mb16" />
+
       <el-divider content-position="left">瀹℃壒娴佺▼锛坽{ detailRow.flowNodes?.length || 0 }} 涓妭鐐癸級</el-divider>
+
       <div v-if="detailRow.flowNodes?.length" class="detail-flow">
+
         <div v-for="(node, index) in detailRow.flowNodes" :key="index" class="detail-node">
+
           <div class="detail-node-head">
+
             <span class="detail-node-order">鑺傜偣 {{ index + 1 }}</span>
+
             <el-tag size="small" :type="node.signMode === 'or_sign' ? 'warning' : 'primary'">
+
               {{ nodeSignModeLabel(node.signMode) }}
+
             </el-tag>
+
           </div>
+
           <div class="detail-approvers">
+
             <el-tag
+
               v-for="a in node.approvers"
+
               :key="String(a.approverId)"
+
               class="detail-approver-tag"
+
               effect="plain"
+
             >
+
               {{ a.approverName || "鈥�" }}
+
             </el-tag>
+
             <span v-if="!node.approvers?.length" class="text-muted">鏈厤缃鎵逛汉</span>
+
           </div>
+
           <el-icon v-if="index < detailRow.flowNodes.length - 1" class="detail-arrow"><ArrowRight /></el-icon>
+
         </div>
+
       </div>
+
       <el-empty v-else description="鏆傛棤娴佺▼鑺傜偣" :image-size="60" />
+
       </div>
+
       <template #footer>
+
         <el-button @click="detailDialog.visible = false">鍏� 闂�</el-button>
+
         <el-button type="primary" @click="editFromDetail">缂� 杈�</el-button>
+
       </template>
+
     </el-dialog>
+
   </div>
+
 </template>
 
+
+
 <script setup>
-import { ArrowRight, Document, Plus, RefreshRight } from "@element-plus/icons-vue";
+
+import { ArrowRight, Plus, RefreshRight } from "@element-plus/icons-vue";
+
 import { ElMessage } from "element-plus";
-import { computed, onMounted, ref } from "vue";
+
+import { computed, nextTick, onMounted, ref } from "vue";
+
 import { userListNoPageByTenantId } from "@/api/system/user.js";
+
 import FormConfigEditor from "./components/FormConfigEditor.vue";
+
 import TemplateFlowEditor from "./components/TemplateFlowEditor.vue";
+
 import { formatDisplayTime } from "./approveTemplateConstants.js";
+
 import { formatDefaultValueDisplay, formFieldTypeLabel, parseFormConfigToData } from "./formConfigUtils.js";
+
 import { useApproveTemplate } from "./useApproveTemplate.js";
 
-const at = useApproveTemplate();
+
+
 const {
+
   Search,
-  TEMPLATE_TYPE_OPTIONS,
+
+  templateTypeOptions,
+
+  loadTemplateTypeOptions,
+
   templateTypeLabel,
-  activeTab,
-  builtinTemplates,
-  builtinLoading,
-  loadBuiltinTemplates,
+
   nodeSignModeLabel,
+
   searchForm,
+
   tableLoading,
+
   page,
+
   tableData,
+
   tableColumn,
+
   formDialog,
+
   form,
+
   formRef,
+
   formRules,
+
   detailDialog,
+
   detailRow,
+
   detailLoading,
+
   fetchTemplateList,
+
   handleQuery,
+
   resetSearch,
+
   pagination,
+
   openFormDialog,
+
   openDetail,
+
   submitForm,
-} = at;
+
+} = useApproveTemplate();
+
+
 
 const flowUserOptions = ref([]);
 
+
+
 const detailFormConfig = computed(() =>
+
   parseFormConfigToData(detailRow.value?.formConfigData ?? detailRow.value?.formConfig)
+
 );
 
+
+
 function unwrapArray(payload) {
+
   if (Array.isArray(payload)) return payload;
+
   if (payload?.data && Array.isArray(payload.data)) return payload.data;
+
   if (payload?.rows && Array.isArray(payload.rows)) return payload.rows;
+
   return [];
+
 }
+
+
 
 function isActiveUser(u) {
+
   if (u.delFlag === "2" || u.delFlag === 2) return false;
+
   if (u.status == null) return true;
+
   return String(u.status) === "0";
+
 }
+
+
 
 async function loadUsers() {
+
   try {
+
     const res = await userListNoPageByTenantId();
+
     flowUserOptions.value = unwrapArray(res).filter(isActiveUser);
+
   } catch {
+
     flowUserOptions.value = [];
+
   }
+
 }
+
+
 
 async function onSubmitForm() {
+
   const ret = await submitForm();
+
   if (ret?.message) {
+
     ElMessage.warning(ret.message);
+
     return;
+
   }
+
   if (ret?.ok) ElMessage.success("淇濆瓨鎴愬姛");
+
 }
 
-function editFromDetail() {
-  const row = detailRow.value;
-  detailDialog.visible = false;
-  openFormDialog("edit", row);
+
+
+function onFormDialogClosed() {
+
+  formRef.value?.resetFields?.();
+
 }
+
+
+
+async function editFromDetail() {
+
+  const row = detailRow.value;
+
+  detailDialog.visible = false;
+
+  await nextTick();
+
+  openFormDialog("edit", row);
+
+}
+
+
 
 onMounted(() => {
+
   loadUsers();
-  loadBuiltinTemplates();
+
+  loadTemplateTypeOptions();
+
   fetchTemplateList();
+
 });
+
 </script>
 
+
+
 <style scoped>
+
 .mb20 {
+
   margin-bottom: 20px;
+
 }
+
 .mb16 {
+
   margin-bottom: 16px;
+
 }
+
 .mb16.el-empty {
+
   padding: 8px 0;
+
 }
+
 .ml10 {
+
   margin-left: 10px;
+
 }
+
 .ml12 {
+
   margin-left: 12px;
+
 }
-.page-header .header-title {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  font-size: 18px;
-  font-weight: 600;
-  color: var(--el-text-color-primary);
-  margin-bottom: 8px;
-}
-.title-icon {
-  font-size: 22px;
-  color: var(--el-color-primary);
-}
-.header-desc {
-  margin: 0;
-  font-size: 13px;
-  color: var(--el-text-color-secondary);
-  line-height: 1.6;
-  max-width: 920px;
-}
+
 .search_form {
+
   display: flex;
+
   flex-wrap: wrap;
+
   align-items: center;
+
   justify-content: space-between;
+
   gap: 12px;
+
 }
+
 .search_fields {
+
   display: flex;
+
   flex-wrap: wrap;
+
   align-items: center;
+
   gap: 4px;
+
 }
+
 .search_actions {
+
   display: flex;
+
   gap: 8px;
+
 }
-.builtin-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
-  gap: 12px;
-}
-.builtin-card {
-  padding: 14px 16px;
-  border: 1px solid var(--el-border-color-lighter);
-  border-radius: var(--radius-md, 8px);
-  background: var(--el-fill-color-blank);
-}
-.builtin-label {
-  font-size: 15px;
-  font-weight: 600;
-  color: var(--el-text-color-primary);
-}
-.builtin-summary {
-  margin: 8px 0 10px;
-  font-size: 12px;
-  color: var(--el-text-color-secondary);
-  line-height: 1.5;
-  min-height: 36px;
-}
-.builtin-meta {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-}
+
 .flow-tip {
+
   font-size: 12px;
+
   color: var(--el-text-color-secondary);
+
   margin: 8px 0 0;
+
   line-height: 1.5;
+
 }
+
 .detail-flow {
+
   display: flex;
+
   flex-wrap: wrap;
+
   align-items: flex-start;
+
   gap: 8px;
+
 }
+
 .detail-node {
+
   position: relative;
+
   min-width: 180px;
+
   max-width: 240px;
+
   padding: 12px;
+
   border: 1px solid var(--el-border-color-lighter);
+
   border-radius: 8px;
+
   background: var(--el-fill-color-lighter);
+
 }
+
 .detail-node-head {
+
   display: flex;
+
   align-items: center;
+
   justify-content: space-between;
+
   margin-bottom: 8px;
+
 }
+
 .detail-node-order {
+
   font-weight: 600;
+
   font-size: 13px;
+
 }
+
 .detail-approvers {
+
   display: flex;
+
   flex-wrap: wrap;
+
   gap: 4px;
+
 }
+
 .detail-approver-tag {
+
   margin: 0;
+
 }
+
 .detail-arrow {
+
   position: absolute;
+
   right: -20px;
+
   top: 50%;
+
   transform: translateY(-50%);
+
   color: var(--el-text-color-placeholder);
+
 }
+
 .detail-dialog-body {
+
   min-height: 120px;
+
 }
+
 .text-muted {
+
   font-size: 12px;
+
   color: var(--el-text-color-placeholder);
+
 }
+
 .template-form-dialog :deep(.el-dialog__body) {
+
   padding-top: 8px;
+
 }
+
 </style>
+
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
index 8489e13..3e27afb 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
@@ -2,13 +2,11 @@
   addApprovalTemplate,
   deleteApprovalTemplate,
   getApprovalTemplateDetail,
-  listApprovalTemplate,
   listApprovalTemplatePage,
-  TEMPLATE_TYPE_BUILTIN,
   TEMPLATE_TYPE_CUSTOM,
-  TEMPLATE_TYPE_OPTIONS,
   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";
@@ -16,12 +14,9 @@
   buildApprovalTemplateListParams,
   createEmptyTemplateForm,
   flowNodesSummary,
-  mapBuiltinCardFromApi,
   mapTemplateFromApi,
   mapTemplateToApi,
   nodeSignModeLabel,
-  templateTypeLabel,
-  unwrapTemplateList,
   formatDisplayTime,
   unwrapTemplateDetail,
   validateTemplateForm,
@@ -29,6 +24,44 @@
 import { parseFormConfigToData } from "./formConfigUtils.js";
 
 const LEGACY_STORAGE_KEY = "oa_approve_template_custom_v1";
+
+const FALLBACK_TEMPLATE_TYPE_OPTIONS = [
+  { 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;
+  return options.some(
+    (x) => x.value === type || x.value === Number(type) || String(x.value) === String(type)
+  );
+}
 
 function clearLegacyStorage() {
   try {
@@ -41,9 +74,15 @@
 export function useApproveTemplate() {
   clearLegacyStorage();
 
-  const activeTab = ref("custom");
-  const builtinTemplates = ref([]);
-  const builtinLoading = ref(false);
+  const templateTypeOptions = ref([...FALLBACK_TEMPLATE_TYPE_OPTIONS]);
+
+  function templateTypeLabel(type) {
+    if (type == null || type === "") return "鈥�";
+    const hit = templateTypeOptions.value.find(
+      (x) => x.value === type || x.value === Number(type) || String(x.value) === String(type)
+    );
+    return hit?.label || "鈥�";
+  }
 
   const searchForm = reactive({
     keyword: "",
@@ -58,20 +97,33 @@
   const form = reactive(createEmptyTemplateForm());
   const formRef = ref();
 
+  async function loadTemplateTypeOptions() {
+    try {
+      const res = await getTypeEnums();
+      const list = normalizeTypeEnumOptions(res?.data);
+      templateTypeOptions.value = list.length ? list : [...FALLBACK_TEMPLATE_TYPE_OPTIONS];
+    } catch {
+      templateTypeOptions.value = [...FALLBACK_TEMPLATE_TYPE_OPTIONS];
+    }
+    if (!matchTemplateTypeValue(templateTypeOptions.value, form.businessType)) {
+      form.businessType = templateTypeOptions.value[0]?.value ?? "";
+    }
+  }
+
   const detailDialog = reactive({ visible: false });
   const detailRow = ref({});
   const detailLoading = ref(false);
 
   const formRules = {
     templateName: [{ required: true, message: "璇疯緭鍏ユā鏉垮悕绉�", trigger: "blur" }],
-    templateType: [{ required: true, message: "璇烽�夋嫨妯℃澘绫诲瀷", trigger: "change" }],
+    businessType: [{ required: true, message: "璇烽�夋嫨妯℃澘绫诲瀷", trigger: "change" }],
   };
 
   const tableColumn = ref([
     { label: "妯℃澘鍚嶇О", prop: "templateName", minWidth: 140 },
     {
       label: "妯℃澘绫诲瀷",
-      prop: "templateType",
+      prop: "businessType",
       width: 100,
       align: "center",
       formatData: (v) => templateTypeLabel(v),
@@ -133,19 +185,6 @@
     },
   ]);
 
-  async function loadBuiltinTemplates() {
-    builtinLoading.value = true;
-    try {
-      const res = await listApprovalTemplate(TEMPLATE_TYPE_BUILTIN);
-      builtinTemplates.value = unwrapTemplateList(res).map(mapBuiltinCardFromApi);
-    } catch {
-      builtinTemplates.value = [];
-      ElMessage.warning("绯荤粺甯哥敤瀹℃壒鍔犺浇澶辫触");
-    } finally {
-      builtinLoading.value = false;
-    }
-  }
-
   async function fetchTemplateList() {
     tableLoading.value = true;
     try {
@@ -191,7 +230,7 @@
       id: row.id,
       templateName: row.templateName || "",
       description: row.description || "",
-      templateType: row.templateType ?? TEMPLATE_TYPE_CUSTOM,
+      businessType: row.businessType ?? "",
       formConfig: row.formConfig || "",
       formConfigData: JSON.parse(
         JSON.stringify(row.formConfigData || parseFormConfigToData(row.formConfig))
@@ -253,9 +292,6 @@
     formDialog.visible = false;
     page.current = 1;
     await fetchTemplateList();
-    if (dto.templateType === TEMPLATE_TYPE_BUILTIN) {
-      await loadBuiltinTemplates();
-    }
     return { ok: true };
   }
 
@@ -284,9 +320,6 @@
       await deleteApprovalTemplate([row.id]);
       ElMessage.success("鍒犻櫎鎴愬姛");
       await fetchTemplateList();
-      if (row.templateType === TEMPLATE_TYPE_BUILTIN) {
-        await loadBuiltinTemplates();
-      }
     } catch {
       /* 閿欒鐢辨嫤鎴櫒鎻愮ず */
     }
@@ -294,12 +327,9 @@
 
   return {
     Search,
-    TEMPLATE_TYPE_OPTIONS,
+    templateTypeOptions,
+    loadTemplateTypeOptions,
     templateTypeLabel,
-    activeTab,
-    builtinTemplates,
-    builtinLoading,
-    loadBuiltinTemplates,
     fetchTemplateList,
     nodeSignModeLabel,
     flowNodesSummary,

--
Gitblit v1.9.3