From 930d38ed2a3c2131be3305a585602c7a5a275fe3 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期二, 19 五月 2026 17:09:12 +0800
Subject: [PATCH] Merge branch 'dev-new_pro_OA' of http://114.132.189.42:9002/r/product-inventory-management into dev-new_pro_OA

---
 src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js |  475 ++++++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 308 insertions(+), 167 deletions(-)

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
index c3c3241..337b00d 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
@@ -1,57 +1,52 @@
-import { Search } from "@element-plus/icons-vue";
-import dayjs from "dayjs";
+import {
+  getApprovalTemplateDetail,
+  listApprovalTemplate,
+  TEMPLATE_TYPE_BUILTIN,
+  TEMPLATE_TYPE_CUSTOM,
+} from "@/api/officeProcessAutomation/approvalTemplate.js";
+import {
+  approveApprovalInstance,
+  deleteApprovalInstance,
+  listApprovalInstancePage,
+  saveApprovalInstance,
+  updateApprovalInstance,
+} from "@/api/officeProcessAutomation/approvalInstance.js";
 import useUserStore from "@/store/modules/user";
-import { computed, reactive, ref, watch } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { computed, reactive, ref } from "vue";
+import {
+  formatDisplayTime,
+  mapEnabledFromApi,
+  mapTemplateFromApi,
+  unwrapTemplateDetail,
+  unwrapTemplateList,
+} from "../approve-template/approveTemplateConstants.js";
+import { buildSubmitTemplateFromRow } from "../approve-template/formConfigUtils.js";
 import {
   APPROVAL_TYPE_OPTIONS,
-  SUBMIT_TEMPLATES,
-  approvalModeLabel,
   approvalStatusLabel,
   approvalStatusTagType,
   approvalTypeLabel,
+  buildApprovalInstanceListParams,
+  buildApproveInstanceDto,
+  buildEditFormFromInstanceRow,
+  buildInstanceDto,
+  clearLegacyApproveListStorage,
   createEmptySubmitForm,
-  createInitialMockRows,
-  loadStoredRows,
-  saveStoredRows,
-  buildDefaultFlowNodes,
+  mapInstanceFromApi,
+  mapSubmitTemplateCard,
+  validateSubmitFlowNodes,
+  unwrapInstancePage,
 } from "./approveListConstants.js";
-import { buildSubmitTemplateFromRow } from "../approve-template/formConfigUtils.js";
-
-function advanceFlow(row, result, opinion) {
-  const nodes = row.approvalFlowNodes || [];
-  const idx = row.currentNodeIndex ?? 0;
-  const node = nodes[idx];
-  if (!node) return;
-  node.nodeStatus = result === "approved" ? "finish" : "error";
-  node.approveOpinion = opinion || (result === "approved" ? "鍚屾剰" : "椹冲洖");
-  node.approveTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
-  row.approvalRecords = row.approvalRecords || [];
-  row.approvalRecords.push({
-    operatorName: node.approverName || "瀹℃壒浜�",
-    result,
-    opinion: node.approveOpinion,
-    time: node.approveTime,
-  });
-  if (result === "rejected") {
-    row.approvalStatus = "rejected";
-    row.rejectReason = opinion || node.approveOpinion;
-    return;
-  }
-  const next = idx + 1;
-  if (next < nodes.length) {
-    row.currentNodeIndex = next;
-    nodes[next].nodeStatus = "process";
-    row.approvalStatus = "pending";
-  } else {
-    row.approvalStatus = "approved";
-    row.rejectReason = "";
-  }
-}
 
 export function useApproveList() {
+  clearLegacyApproveListStorage();
   const userStore = useUserStore();
-  const stored = loadStoredRows();
-  const allRows = ref(stored?.length ? stored : createInitialMockRows());
+
+  const tableData = ref([]);
+  const submitTemplateCards = ref([]);
+  const submitTemplatesLoading = ref(false);
 
   const searchForm = reactive({
     approvalType: "",
@@ -67,59 +62,37 @@
 
   const approveDialog = reactive({ visible: false, row: null });
   const approveOpinion = ref("");
+  const approveSubmitting = ref(false);
 
-  const submitDialog = reactive({ visible: false, step: 1 });
+  const submitDialog = reactive({ visible: false, step: 1, mode: "add" });
+  const submitEditRow = ref(null);
   const submitForm = reactive(createEmptySubmitForm(""));
   const submitFormRef = ref();
+  const submitSaving = ref(false);
 
-  const filteredList = computed(() => {
-    let list = [...allRows.value];
-    if (searchForm.approvalType) {
-      list = list.filter((r) => r.approvalType === searchForm.approvalType);
+  const isSubmitEdit = computed(() => submitDialog.mode === "edit");
+  const submitDialogTitle = computed(() => {
+    if (submitDialog.mode === "edit") {
+      return `淇敼${activeTemplate.value?.label || submitForm.templateName || "瀹℃壒"}`;
     }
-    const kw = (searchForm.applicantKeyword || "").trim().toLowerCase();
-    if (kw) {
-      list = list.filter((r) => {
-        const name = (r.applicantName || "").toLowerCase();
-        const no = (r.applicantNo || "").toLowerCase();
-        return name.includes(kw) || no.includes(kw);
-      });
-    }
-    const range = searchForm.createTimeRange;
-    if (range?.length === 2) {
-      const [from, to] = range;
-      list = list.filter((r) => {
-        const t = (r.createTime || "").slice(0, 10);
-        return t && t >= from && t <= to;
-      });
-    }
-    return list.sort((a, b) => (String(a.createTime) < String(b.createTime) ? 1 : -1));
+    if (submitDialog.step === 1) return "閫夋嫨瀹℃壒妯℃澘";
+    return `鎻愪氦${activeTemplate.value?.label || "瀹℃壒"}`;
   });
 
-  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 activeTemplate = computed(() => submitForm.templateSnapshot || null);
 
-  const tableData = computed(() => {
-    const start = (page.current - 1) * page.size;
-    return filteredList.value.slice(start, start + page.size);
+  /** 濉姤椤瑰畾涔夛紙鏂板/淇敼涓� formConfig 涓�鑷达級 */
+  const submitFormFields = computed(() => {
+    const tplFields = activeTemplate.value?.fields;
+    if (tplFields?.length) return tplFields;
+    return submitForm.formFieldDefs || [];
   });
-
-  const activeTemplate = computed(
-    () => submitForm.templateSnapshot || SUBMIT_TEMPLATES[submitForm.templateKey] || null
-  );
 
   const submitFormRules = computed(() => {
     const rules = {
       templateKey: [{ required: true, message: "璇烽�夋嫨瀹℃壒绫诲瀷", trigger: "change" }],
     };
-    (activeTemplate.value?.fields || []).forEach((f) => {
+    submitFormFields.value.forEach((f) => {
       if (!f.required) return;
       if (f.type === "number") {
         rules[`formPayload.${f.key}`] = [{ required: true, message: `璇峰~鍐�${f.label}`, trigger: "blur" }];
@@ -135,6 +108,7 @@
   const tableColumn = ref([
     { label: "鐢宠浜虹紪鍙�", prop: "applicantNo", width: 110 },
     { label: "鐢宠浜哄悕绉�", prop: "applicantName", minWidth: 100 },
+    { label: "涓氬姟绫诲瀷", prop: "businessName", minWidth: 120 },
     {
       label: "瀹℃壒绫诲瀷",
       prop: "approvalType",
@@ -143,14 +117,7 @@
       slot: "approveType",
     },
     {
-      label: "瀹℃壒鏂瑰紡",
-      prop: "approvalMode",
-      width: 90,
-      dataType: "slot",
-      slot: "approvalMethod",
-    },
-    {
-      label: "鏄惁鏈",
+      label: "寰呮垜瀹℃壒",
       prop: "unread",
       width: 90,
       align: "center",
@@ -164,35 +131,82 @@
       formatData: (v) => approvalStatusLabel(v),
       formatType: (v) => approvalStatusTagType(v),
     },
-    { label: "鍒涘缓鏃堕棿", prop: "createTime", width: 170 },
+    {
+      label: "鍒涘缓鏃堕棿",
+      prop: "createTime",
+      width: 170,
+      formatData: (v) => formatDisplayTime(v),
+    },
     {
       dataType: "action",
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 160,
+      width: 240,
       operation: [
         { name: "璇︽儏", type: "text", clickFun: (row) => openDetail(row) },
         {
-          name: "瀹℃壒",
+          name: "淇敼",
           type: "text",
           disabled: (row) => row.approvalStatus !== "pending",
+          clickFun: (row) => openEditDialog(row),
+        },
+        {
+          name: "瀹℃壒",
+          type: "text",
+          disabled: (row) => row.approvalStatus !== "pending" || !row.isApprove,
           clickFun: (row) => openApprove(row),
+        },
+        {
+          name: "鍒犻櫎",
+          type: "danger",
+          clickFun: (row) => removeInstance(row),
         },
       ],
     },
   ]);
 
-  function persist() {
-    saveStoredRows(allRows.value);
+  async function fetchApprovalList() {
+    tableLoading.value = true;
+    try {
+      const res = await listApprovalInstancePage(
+        buildApprovalInstanceListParams({ page, searchForm })
+      );
+      const { records, total } = unwrapInstancePage(res);
+      tableData.value = records.map(mapInstanceFromApi);
+      page.total = total;
+    } catch {
+      tableData.value = [];
+      page.total = 0;
+      ElMessage.error("瀹℃壒鍒楄〃鍔犺浇澶辫触");
+    } finally {
+      tableLoading.value = false;
+    }
+  }
+
+  async function loadSubmitTemplates() {
+    submitTemplatesLoading.value = true;
+    try {
+      const [builtinRes, customRes] = await Promise.all([
+        listApprovalTemplate(TEMPLATE_TYPE_BUILTIN),
+        listApprovalTemplate(TEMPLATE_TYPE_CUSTOM),
+      ]);
+      const merged = [
+        ...unwrapTemplateList(builtinRes),
+        ...unwrapTemplateList(customRes),
+      ].filter((row) => mapEnabledFromApi(row.enabled));
+      submitTemplateCards.value = merged.map(mapSubmitTemplateCard);
+    } catch {
+      submitTemplateCards.value = [];
+      ElMessage.error("鍔犺浇瀹℃壒妯℃澘澶辫触");
+    } finally {
+      submitTemplatesLoading.value = false;
+    }
   }
 
   function handleQuery() {
-    tableLoading.value = true;
     page.current = 1;
-    setTimeout(() => {
-      tableLoading.value = false;
-    }, 200);
+    fetchApprovalList();
   }
 
   function resetSearch() {
@@ -205,50 +219,81 @@
   function pagination({ page: p, limit }) {
     page.current = p;
     page.size = limit;
-  }
-
-  function markRead(row) {
-    if (!row.unread) return;
-    const hit = allRows.value.find((r) => r.id === row.id);
-    if (hit) {
-      hit.unread = false;
-      persist();
-    }
+    fetchApprovalList();
   }
 
   function openDetail(row) {
-    markRead(row);
     detailRow.value = { ...row };
     detailDialog.visible = true;
   }
 
   function openApprove(row) {
-    markRead(row);
     approveDialog.row = { ...row };
     approveOpinion.value = "";
     approveDialog.visible = true;
   }
 
-  function openSubmitDialog() {
-    Object.assign(submitForm, createEmptySubmitForm(""));
+  function resetSubmitDialogState() {
+    submitDialog.mode = "add";
     submitDialog.step = 1;
+    submitEditRow.value = null;
+    Object.assign(submitForm, createEmptySubmitForm(""));
+  }
+
+  function openSubmitDialog() {
+    resetSubmitDialogState();
+    submitDialog.visible = true;
+    loadSubmitTemplates();
+  }
+
+  function openEditDialog(row) {
+    if (row?.approvalStatus !== "pending") {
+      ElMessage.warning("浠呭鏍镐腑鐨勫鎵瑰彲淇敼");
+      return;
+    }
+    if (!row?.id) {
+      ElMessage.warning("鏃犳硶淇敼锛氱己灏戝鎵瑰疄渚� ID");
+      return;
+    }
+    submitDialog.mode = "edit";
+    submitDialog.step = 2;
+    submitEditRow.value = { ...row };
+    Object.assign(submitForm, buildEditFormFromInstanceRow(row));
     submitDialog.visible = true;
   }
 
-  /** @param {string} key 鍐呯疆妯℃澘 key 鎴栬嚜瀹氫箟 id */
-  function onTemplatePick(key, templateRow) {
-    const base = templateRow
-      ? createEmptySubmitForm(key, buildSubmitTemplateFromRow(templateRow))
-      : createEmptySubmitForm(key);
-    Object.assign(submitForm, {
-      ...base,
-      templateSnapshot: templateRow ? buildSubmitTemplateFromRow(templateRow) : null,
-    });
-    submitDialog.step = 2;
+  async function onTemplatePick(card) {
+    if (!card?.id) return;
+    submitTemplatesLoading.value = true;
+    try {
+      const res = await getApprovalTemplateDetail(card.id);
+      const mapped = mapTemplateFromApi(unwrapTemplateDetail(res));
+      const tpl = {
+        ...buildSubmitTemplateFromRow(mapped),
+        templateId: mapped.id,
+      };
+      const base = createEmptySubmitForm(String(card.id), tpl, mapped.flowNodes);
+      Object.assign(submitForm, {
+        ...base,
+        templateName: mapped.templateName || tpl.label || "",
+        templateSnapshot: tpl,
+        formFieldDefs: tpl.fields || [],
+      });
+      submitDialog.step = 2;
+    } catch {
+      ElMessage.error("鍔犺浇妯℃澘璇︽儏澶辫触");
+    } finally {
+      submitTemplatesLoading.value = false;
+    }
   }
 
   function backToTemplatePick() {
     submitDialog.step = 1;
+  }
+
+  async function submitInstanceForm() {
+    if (submitDialog.mode === "edit") return submitEditApproval();
+    return submitNewApproval();
   }
 
   async function submitNewApproval() {
@@ -258,56 +303,143 @@
     } catch {
       return false;
     }
-    const tpl = activeTemplate.value;
-    if (!tpl) return false;
-    const id = `user_${Date.now()}`;
-    const summary =
-      submitForm.formPayload.summary ||
-      submitForm.formPayload.handoverTo ||
-      `${tpl.label}鐢宠`;
-    const row = {
-      id,
-      bizId: `BIZ${dayjs().format("YYYYMMDDHHmmss")}`,
-      applicantNo: userStore.name || String(userStore.id || "褰撳墠鐢ㄦ埛"),
-      applicantName: userStore.nickName || userStore.name || "褰撳墠鐢ㄦ埛",
-      approvalType: tpl.approvalType,
-      approvalMode: submitForm.approvalMode,
-      unread: false,
-      approvalStatus: "pending",
-      createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
-      summary,
-      formPayload: { ...submitForm.formPayload },
-      approvalFlowNodes: (submitForm.approvalFlowNodes?.length
-        ? submitForm.approvalFlowNodes
-        : buildDefaultFlowNodes()
-      ).map((n, i) => ({ ...n, nodeStatus: i === 0 ? "process" : n.nodeStatus || "wait" })),
-      currentNodeIndex: 0,
-      approvalRecords: [],
-      rejectReason: "",
-    };
-    allRows.value.unshift(row);
-    persist();
-    submitDialog.visible = false;
-    page.current = 1;
-    return true;
+    if (!activeTemplate.value) return false;
+    const flowCheck = validateSubmitFlowNodes(submitForm.flowNodes);
+    if (!flowCheck.ok) {
+      ElMessage.warning(flowCheck.message);
+      return false;
+    }
+    if (!submitForm.templateId) {
+      ElMessage.warning("缂哄皯妯℃澘 ID锛屾棤娉曟彁浜�");
+      return false;
+    }
+    if (submitSaving.value) return false;
+    submitSaving.value = true;
+    try {
+      await saveApprovalInstance(
+        buildInstanceDto({
+          submitForm,
+          activeTemplate: activeTemplate.value,
+          userStore,
+          flowNodes: flowCheck.nodes,
+        })
+      );
+      submitDialog.visible = false;
+      page.current = 1;
+      await fetchApprovalList();
+      return true;
+    } catch {
+      return false;
+    } finally {
+      submitSaving.value = false;
+    }
   }
 
-  function submitApprove(result) {
+  async function submitEditApproval() {
+    if (!submitFormRef.value) return false;
+    try {
+      await submitFormRef.value.validate();
+    } catch {
+      return false;
+    }
+    if (!activeTemplate.value) return false;
+    const flowCheck = validateSubmitFlowNodes(submitForm.flowNodes);
+    if (!flowCheck.ok) {
+      ElMessage.warning(flowCheck.message);
+      return false;
+    }
+    if (!submitForm.instanceId) {
+      ElMessage.warning("缂哄皯瀹℃壒瀹炰緥 ID锛屾棤娉曚繚瀛�");
+      return false;
+    }
+    if (submitSaving.value) return false;
+    submitSaving.value = true;
+    try {
+      await updateApprovalInstance(
+        buildInstanceDto({
+          submitForm,
+          activeTemplate: activeTemplate.value,
+          flowNodes: flowCheck.nodes,
+          existingRow: submitEditRow.value,
+        })
+      );
+      submitDialog.visible = false;
+      await fetchApprovalList();
+      if (detailDialog.visible && detailRow.value?.id === submitForm.instanceId) {
+        const hit = tableData.value.find((r) => r.id === submitForm.instanceId);
+        if (hit) detailRow.value = { ...hit };
+        else detailDialog.visible = false;
+      }
+      return true;
+    } catch {
+      return false;
+    } finally {
+      submitSaving.value = false;
+    }
+  }
+
+  async function removeInstance(row) {
+    if (row?.id == null || row.id === "") {
+      ElMessage.warning("鏃犳硶鍒犻櫎锛氱己灏戝鎵瑰疄渚� ID");
+      return;
+    }
+    const title = row.title || row.templateName || row.instanceNo || "璇ュ鎵�";
+    try {
+      await ElMessageBox.confirm(
+        `纭畾瑕佸垹闄ゅ鎵广��${title}銆嶅悧锛熷垹闄ゅ悗涓嶅彲鎭㈠銆俙,
+        "鍒犻櫎纭",
+        {
+          type: "warning",
+          confirmButtonText: "纭畾鍒犻櫎",
+          cancelButtonText: "鍙栨秷",
+          distinguishCancelAndClose: true,
+          autofocus: false,
+        }
+      );
+    } catch {
+      return;
+    }
+    try {
+      await deleteApprovalInstance([row.id]);
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      if (detailDialog.visible && detailRow.value?.id === row.id) {
+        detailDialog.visible = false;
+      }
+      if (approveDialog.visible && approveDialog.row?.id === row.id) {
+        approveDialog.visible = false;
+      }
+      await fetchApprovalList();
+    } catch {
+      /* 閿欒鐢辨嫤鎴櫒鎻愮ず */
+    }
+  }
+
+  async function submitApprove(result) {
     const row = approveDialog.row;
-    if (!row) return;
-    const hit = allRows.value.find((r) => r.id === row.id);
-    if (!hit || hit.approvalStatus !== "pending") return;
+    if (!row?.id) return { ok: false };
     if (result === "rejected" && !(approveOpinion.value || "").trim()) {
       return { needOpinion: true };
     }
-    advanceFlow(hit, result, (approveOpinion.value || "").trim());
-    hit.unread = false;
-    persist();
-    approveDialog.visible = false;
-    if (detailDialog.visible && detailRow.value?.id === hit.id) {
-      detailRow.value = { ...hit };
+    if (approveSubmitting.value) return { ok: false };
+    approveSubmitting.value = true;
+    try {
+      await approveApprovalInstance(
+        buildApproveInstanceDto(row, result, approveOpinion.value)
+      );
+      approveDialog.visible = false;
+      await fetchApprovalList();
+      if (detailDialog.visible && detailRow.value?.id === row.id) {
+        const hit = tableData.value.find((r) => r.id === row.id);
+        if (hit) detailRow.value = { ...hit };
+        else detailDialog.visible = false;
+      }
+      return { ok: true, result };
+    } catch {
+      ElMessage.error("瀹℃壒鎿嶄綔澶辫触");
+      return { ok: false };
+    } finally {
+      approveSubmitting.value = false;
     }
-    return { ok: true };
   }
 
   function approvalActionLabel(result) {
@@ -319,9 +451,7 @@
   return {
     Search,
     APPROVAL_TYPE_OPTIONS,
-    SUBMIT_TEMPLATES,
     approvalTypeLabel,
-    approvalModeLabel,
     approvalStatusLabel,
     approvalStatusTagType,
     approvalActionLabel,
@@ -334,20 +464,31 @@
     detailRow,
     approveDialog,
     approveOpinion,
+    approveSubmitting,
     submitDialog,
+    isSubmitEdit,
+    submitDialogTitle,
     submitForm,
     submitFormRef,
+    submitSaving,
     activeTemplate,
+    submitFormFields,
     submitFormRules,
+    submitTemplateCards,
+    submitTemplatesLoading,
     handleQuery,
     resetSearch,
     pagination,
+    resetSubmitDialogState,
     openSubmitDialog,
+    openEditDialog,
     onTemplatePick,
     backToTemplatePick,
+    submitInstanceForm,
     submitNewApproval,
     submitApprove,
     openDetail,
     openApprove,
+    fetchApprovalList,
   };
 }

--
Gitblit v1.9.3