import { getApprovalTemplateDetail, listApprovalTemplate, 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 { Search } from "@element-plus/icons-vue"; import { ElMessage, ElMessageBox } from "element-plus"; import { computed, reactive, ref } from "vue"; import { fetchBusinessTypeOptions, formatDisplayTime, mapEnabledFromApi, mapTemplateFromApi, unwrapTemplateDetail, unwrapTemplateList, } from "../approve-template/approveTemplateConstants.js"; import { buildSubmitTemplateFromRow } from "../approve-template/formConfigUtils.js"; import { APPROVAL_TYPE_OPTIONS, approvalStatusLabel, approvalStatusTagType, approvalTypeLabel, buildApprovalInstanceListParams, buildApproveInstanceDto, buildEditFormFromInstanceRow, buildInstanceDto, clearLegacyApproveListStorage, createEmptySubmitForm, mapInstanceFromApi, mapSubmitTemplateCard, matchBusinessTypeValue, validateSubmitFlowNodes, unwrapInstancePage, } from "./approveListConstants.js"; export function useApproveList() { clearLegacyApproveListStorage(); const userStore = useUserStore(); const tableData = 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: "", applicantKeyword: "", createTimeRange: [], }); const tableLoading = ref(false); const page = reactive({ current: 1, size: 10, total: 0 }); const detailDialog = reactive({ visible: false }); const detailRow = ref({}); const approveDialog = reactive({ visible: false, row: null }); const approveOpinion = ref(""); const approveSubmitting = ref(false); 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 isSubmitEdit = computed(() => submitDialog.mode === "edit"); const submitDialogTitle = computed(() => { if (submitDialog.mode === "edit") { return `修改${activeTemplate.value?.label || submitForm.templateName || "审批"}`; } 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); /** 填报项定义(新增/修改与 formConfig 一致) */ const submitFormFields = computed(() => { const tplFields = activeTemplate.value?.fields; if (tplFields?.length) return tplFields; return submitForm.formFieldDefs || []; }); const submitFormRules = computed(() => { const rules = { templateKey: [{ required: true, message: "请选择审批类型", trigger: "change" }], }; submitFormFields.value.forEach((f) => { if (!f.required) return; if (f.type === "number") { rules[`formPayload.${f.key}`] = [{ required: true, message: `请填写${f.label}`, trigger: "blur" }]; } else if (f.type === "datetimerange") { rules[`formPayload.${f.key}`] = [{ required: true, message: `请选择${f.label}`, trigger: "change" }]; } else { rules[`formPayload.${f.key}`] = [{ required: true, message: `请填写${f.label}`, trigger: "blur" }]; } }); return rules; }); const tableColumn = ref([ { label: "申请人编号", prop: "applicantNo", width: 110 }, { label: "申请人名称", prop: "applicantName", minWidth: 100 }, { label: "业务类型", prop: "businessName", minWidth: 120 }, { label: "审批类型", prop: "approvalType", minWidth: 140, dataType: "slot", slot: "approveType", }, { label: "待我审批", prop: "unread", width: 90, align: "center", formatData: (v) => (v ? "是" : "否"), }, { label: "审批状态", prop: "approvalStatus", width: 100, dataType: "tag", formatData: (v) => approvalStatusLabel(v), formatType: (v) => approvalStatusTagType(v), }, { label: "创建时间", prop: "createTime", width: 170, formatData: (v) => formatDisplayTime(v), }, { dataType: "action", label: "操作", align: "center", fixed: "right", width: 240, operation: [ { name: "详情", type: "text", clickFun: (row) => openDetail(row) }, { 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), }, ], }, ]); 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 [typeOptions, customRes] = await Promise.all([ fetchBusinessTypeOptions(), listApprovalTemplate(TEMPLATE_TYPE_CUSTOM), ]); submitBusinessTypeOptions.value = typeOptions; allSubmitTemplates.value = unwrapTemplateList(customRes) .filter((row) => mapEnabledFromApi(row.enabled)) .map(mapSubmitTemplateCard); } catch { submitBusinessTypeOptions.value = []; allSubmitTemplates.value = []; ElMessage.error("加载审批模板失败"); } finally { submitTemplatesLoading.value = false; } } function handleQuery() { page.current = 1; fetchApprovalList(); } function resetSearch() { searchForm.approvalType = ""; searchForm.applicantKeyword = ""; searchForm.createTimeRange = []; handleQuery(); } function pagination({ page: p, limit }) { page.current = p; page.size = limit; fetchApprovalList(); } function openDetail(row) { detailRow.value = { ...row }; detailDialog.visible = true; } function openApprove(row) { approveDialog.row = { ...row }; approveOpinion.value = ""; approveDialog.visible = true; } function resetSubmitDialogState() { submitDialog.mode = "add"; submitDialog.step = 1; selectedBusinessType.value = ""; 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 = 3; submitEditRow.value = { ...row }; Object.assign(submitForm, buildEditFormFromInstanceRow(row)); submitDialog.visible = true; } 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 || "", businessType: mapped.businessType ?? card.businessType ?? selectedBusinessType.value, templateSnapshot: tpl, formFieldDefs: tpl.fields || [], }); submitDialog.step = 3; } catch { ElMessage.error("加载模板详情失败"); } finally { submitTemplatesLoading.value = false; } } 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() { if (submitDialog.mode === "edit") return submitEditApproval(); return submitNewApproval(); } async function submitNewApproval() { 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.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; } } 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?.id) return { ok: false }; if (result === "rejected" && !(approveOpinion.value || "").trim()) { return { needOpinion: true }; } 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; } } function approvalActionLabel(result) { if (result === "approved") return "通过"; if (result === "rejected") return "驳回"; return "待处理"; } return { Search, APPROVAL_TYPE_OPTIONS, approvalTypeLabel, approvalStatusLabel, approvalStatusTagType, approvalActionLabel, searchForm, tableLoading, page, tableData, tableColumn, detailDialog, detailRow, approveDialog, approveOpinion, approveSubmitting, submitDialog, isSubmitEdit, submitDialogTitle, submitForm, submitFormRef, submitSaving, activeTemplate, submitFormFields, submitFormRules, submitBusinessTypeOptions, submitTemplateCards, selectedBusinessType, selectedBusinessTypeLabel, businessTypeLabel, countTemplatesByBusinessType, submitTemplatesLoading, handleQuery, resetSearch, pagination, resetSubmitDialogState, openSubmitDialog, openEditDialog, onBusinessTypePick, onTemplatePick, backToBusinessTypePick, backToTemplatePick, submitInstanceForm, submitNewApproval, submitApprove, openDetail, openApprove, fetchApprovalList, }; }