From 5b248a9716688d8132cfb02b4ba0abecd4060b06 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期三, 20 五月 2026 11:49:08 +0800
Subject: [PATCH] 审批模板流程化

---
 src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js |  273 ++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 171 insertions(+), 102 deletions(-)

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
index c56d055..4b2abe4 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
@@ -1,24 +1,62 @@
-import { Search } from "@element-plus/icons-vue";
-import dayjs from "dayjs";
-import { ElMessageBox } from "element-plus";
-import { computed, reactive, ref, watch } from "vue";
 import {
+  addApprovalTemplate,
+  deleteApprovalTemplate,
+  getApprovalTemplateDetail,
+  listApprovalTemplatePage,
+  TEMPLATE_TYPE_CUSTOM,
+  updateApprovalTemplate,
+} from "@/api/officeProcessAutomation/approvalTemplate.js";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { reactive, ref } from "vue";
+import {
+  buildApprovalTemplateListParams,
   createEmptyTemplateForm,
-  createInitialMockTemplates,
+  fetchBusinessTypeOptions,
   flowNodesSummary,
-  getBuiltinTemplates,
-  loadStoredTemplates,
+  mapTemplateFromApi,
+  mapTemplateToApi,
   nodeSignModeLabel,
-  saveStoredTemplates,
+  formatDisplayTime,
+  unwrapTemplateDetail,
   validateTemplateForm,
 } from "./approveTemplateConstants.js";
+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 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 {
+    localStorage.removeItem(LEGACY_STORAGE_KEY);
+  } catch {
+    /* ignore */
+  }
+}
 
 export function useApproveTemplate() {
-  const stored = loadStoredTemplates();
-  const allTemplates = ref(stored?.length ? stored : createInitialMockTemplates());
+  clearLegacyStorage();
 
-  const activeTab = ref("custom");
-  const builtinTemplates = getBuiltinTemplates();
+  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: "",
@@ -27,51 +65,42 @@
 
   const tableLoading = ref(false);
   const page = reactive({ current: 1, size: 10, total: 0 });
+  const tableData = ref([]);
 
   const formDialog = reactive({ visible: false, title: "", mode: "add" });
   const form = reactive(createEmptyTemplateForm());
   const formRef = ref();
 
+  async function loadTemplateTypeOptions() {
+    try {
+      const list = await fetchBusinessTypeOptions();
+      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 filteredList = computed(() => {
-    let list = [...allTemplates.value];
-    const kw = (searchForm.keyword || "").trim().toLowerCase();
-    if (kw) {
-      list = list.filter((r) => {
-        const name = (r.templateName || "").toLowerCase();
-        const desc = (r.description || "").toLowerCase();
-        return name.includes(kw) || desc.includes(kw);
-      });
-    }
-    if (searchForm.enabledOnly) {
-      list = list.filter((r) => r.enabled !== false);
-    }
-    return list.sort((a, b) => (String(a.updateTime) < String(b.updateTime) ? 1 : -1));
-  });
-
-  watch(
-    filteredList,
-    (list) => {
-      page.total = list.length;
-      const maxPage = Math.max(1, Math.ceil(list.length / page.size) || 1);
-      if (page.current > maxPage) page.current = maxPage;
-    },
-    { immediate: true }
-  );
-
-  const tableData = computed(() => {
-    const start = (page.current - 1) * page.size;
-    return filteredList.value.slice(start, start + page.size);
-  });
+  const detailLoading = ref(false);
 
   const formRules = {
     templateName: [{ required: true, message: "璇疯緭鍏ユā鏉垮悕绉�", trigger: "blur" }],
+    businessType: [{ required: true, message: "璇烽�夋嫨妯℃澘绫诲瀷", trigger: "change" }],
   };
 
   const tableColumn = ref([
     { label: "妯℃澘鍚嶇О", prop: "templateName", minWidth: 140 },
+    {
+      label: "妯℃澘绫诲瀷",
+      prop: "businessType",
+      width: 100,
+      align: "center",
+      formatData: (v) => templateTypeLabel(v),
+    },
     { label: "璇存槑", prop: "description", minWidth: 160, showOverflowTooltip: true },
     {
       label: "鑺傜偣鏁�",
@@ -96,31 +125,59 @@
       formatData: (v) => (v !== false ? "鍚敤" : "鍋滅敤"),
       formatType: (v) => (v !== false ? "success" : "info"),
     },
-    { label: "鏇存柊鏃堕棿", prop: "updateTime", width: 170 },
+    {
+      label: "鍒涘缓鏃堕棿",
+      prop: "createdTime",
+      width: 170,
+      showOverflowTooltip: true,
+      formatData: (v) => formatDisplayTime(v),
+    },
+    {
+      label: "鏇存柊鏃堕棿",
+      prop: "updatedTime",
+      width: 170,
+      showOverflowTooltip: true,
+      formatData: (v) => formatDisplayTime(v),
+    },
     {
       dataType: "action",
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 200,
+      width: 220,
       operation: [
         { name: "璇︽儏", type: "text", clickFun: (row) => openDetail(row) },
         { name: "缂栬緫", type: "text", clickFun: (row) => openFormDialog("edit", row) },
-        { name: "鍒犻櫎", type: "text", clickFun: (row) => removeTemplate(row) },
+        {
+          name: "鍒犻櫎",
+          type: "danger",
+          link: true,
+          clickFun: (row) => removeTemplate(row),
+        },
       ],
     },
   ]);
 
-  function persist() {
-    saveStoredTemplates(allTemplates.value);
+  async function fetchTemplateList() {
+    tableLoading.value = true;
+    try {
+      const res = await listApprovalTemplatePage(
+        buildApprovalTemplateListParams({ page, searchForm })
+      );
+      const data = res?.data || {};
+      tableData.value = (data.records || []).map(mapTemplateFromApi);
+      page.total = Number(data.total || 0);
+    } catch {
+      tableData.value = [];
+      page.total = 0;
+    } finally {
+      tableLoading.value = false;
+    }
   }
 
   function handleQuery() {
-    tableLoading.value = true;
     page.current = 1;
-    setTimeout(() => {
-      tableLoading.value = false;
-    }, 150);
+    fetchTemplateList();
   }
 
   function resetSearch() {
@@ -132,6 +189,7 @@
   function pagination({ page: p, limit }) {
     page.current = p;
     page.size = limit;
+    fetchTemplateList();
   }
 
   function resetForm(row) {
@@ -145,26 +203,40 @@
       id: row.id,
       templateName: row.templateName || "",
       description: row.description || "",
+      businessType: row.businessType ?? "",
+      formConfig: row.formConfig || "",
+      formConfigData: JSON.parse(
+        JSON.stringify(row.formConfigData || parseFormConfigToData(row.formConfig))
+      ),
       enabled: row.enabled !== false,
       flowNodes: JSON.parse(JSON.stringify(row.flowNodes || [base.flowNodes[0]])),
+      storageBlobDTOs: JSON.parse(JSON.stringify(row.storageBlobDTOs || [])),
     });
   }
 
   function openFormDialog(mode, row) {
     formDialog.mode = mode;
-    formDialog.title = mode === "add" ? "鏂板缓鑷畾涔夊鎵规ā鏉�" : "缂栬緫鑷畾涔夊鎵规ā鏉�";
+    formDialog.title = mode === "add" ? "鏂板缓瀹℃壒妯℃澘" : "缂栬緫瀹℃壒妯℃澘";
     resetForm(mode === "edit" ? row : null);
     formDialog.visible = true;
   }
 
-  function openDetail(row) {
-    detailRow.value = { ...row };
+  async function openDetail(row) {
+    if (row?.id == null || row.id === "") {
+      ElMessage.warning("鏃犳硶鏌ョ湅璇︽儏锛氱己灏戞ā鏉� ID");
+      return;
+    }
     detailDialog.visible = true;
-  }
-
-  function isNameDuplicate(name, excludeId) {
-    const n = (name || "").trim();
-    return allTemplates.value.some((t) => t.templateName?.trim() === n && t.id !== excludeId);
+    detailLoading.value = true;
+    detailRow.value = {};
+    try {
+      const res = await getApprovalTemplateDetail(row.id);
+      detailRow.value = mapTemplateFromApi(unwrapTemplateDetail(res));
+    } catch {
+      detailDialog.visible = false;
+    } finally {
+      detailLoading.value = false;
+    }
   }
 
   async function submitForm() {
@@ -178,64 +250,61 @@
     if (!validated.ok) {
       return { message: validated.message };
     }
-    if (isNameDuplicate(validated.name, form.id)) {
-      return { message: "妯℃澘鍚嶇О宸插瓨鍦紝璇锋洿鎹㈠悕绉�" };
+    if (formDialog.mode === "edit" && !form.id) {
+      return { message: "缂哄皯妯℃澘 ID锛屾棤娉曚繚瀛樹慨鏀�" };
     }
-    const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
-    if (formDialog.mode === "add") {
-      allTemplates.value.unshift({
-        id: `tpl_${Date.now()}`,
-        templateName: validated.name,
-        description: (form.description || "").trim(),
-        enabled: form.enabled !== false,
-        createTime: now,
-        updateTime: now,
-        flowNodes: validated.nodes,
-      });
-    } else {
-      const hit = allTemplates.value.find((t) => t.id === form.id);
-      if (!hit) return { message: "妯℃澘涓嶅瓨鍦ㄦ垨宸插垹闄�" };
-      hit.templateName = validated.name;
-      hit.description = (form.description || "").trim();
-      hit.enabled = form.enabled !== false;
-      hit.flowNodes = validated.nodes;
-      hit.updateTime = now;
+    const dto = mapTemplateToApi(form);
+    try {
+      if (formDialog.mode === "add") {
+        await addApprovalTemplate(dto);
+      } else {
+        await updateApprovalTemplate(dto);
+      }
+    } catch {
+      return false;
     }
-    persist();
     formDialog.visible = false;
     page.current = 1;
+    await fetchTemplateList();
     return { ok: true };
   }
 
   async function removeTemplate(row) {
+    if (row?.id == null || row.id === "") {
+      ElMessage.warning("鏃犳硶鍒犻櫎锛氱己灏戞ā鏉� ID");
+      return;
+    }
+    const name = row.templateName || "鏈懡鍚嶆ā鏉�";
     try {
-      await ElMessageBox.confirm(`纭畾鍒犻櫎妯℃澘銆�${row.templateName}銆嶅悧锛焋, "鎻愮ず", {
-        type: "warning",
-        confirmButtonText: "鍒犻櫎",
-        cancelButtonText: "鍙栨秷",
-      });
+      await ElMessageBox.confirm(
+        `纭畾瑕佸垹闄ゅ鎵规ā鏉裤��${name}銆嶅悧锛熷垹闄ゅ悗涓嶅彲鎭㈠銆俙,
+        "鍒犻櫎纭",
+        {
+          type: "warning",
+          confirmButtonText: "纭畾鍒犻櫎",
+          cancelButtonText: "鍙栨秷",
+          distinguishCancelAndClose: true,
+          autofocus: false,
+        }
+      );
     } catch {
       return;
     }
-    const idx = allTemplates.value.findIndex((t) => t.id === row.id);
-    if (idx >= 0) {
-      allTemplates.value.splice(idx, 1);
-      persist();
+    try {
+      await deleteApprovalTemplate([row.id]);
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      await fetchTemplateList();
+    } catch {
+      /* 閿欒鐢辨嫤鎴櫒鎻愮ず */
     }
-  }
-
-  function toggleEnabled(row) {
-    const hit = allTemplates.value.find((t) => t.id === row.id);
-    if (!hit) return;
-    hit.enabled = !hit.enabled;
-    hit.updateTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
-    persist();
   }
 
   return {
     Search,
-    activeTab,
-    builtinTemplates,
+    templateTypeOptions,
+    loadTemplateTypeOptions,
+    templateTypeLabel,
+    fetchTemplateList,
     nodeSignModeLabel,
     flowNodesSummary,
     searchForm,
@@ -249,12 +318,12 @@
     formRules,
     detailDialog,
     detailRow,
+    detailLoading,
     handleQuery,
     resetSearch,
     pagination,
     openFormDialog,
     openDetail,
     submitForm,
-    toggleEnabled,
   };
 }

--
Gitblit v1.9.3