import { deleteApprovalInstance, listApprovalInstancePage, saveApprovalInstance, updateApprovalInstance, } from "@/api/officeProcessAutomation/approvalInstance.js"; import useUserStore from "@/store/modules/user"; import { ElMessage, ElMessageBox } from "element-plus"; import { computed, reactive, ref } from "vue"; import { applyBindingToForm, buildFormPayloadRules, validateTemplateBinding, } from "./approvalTemplateBindingUtils.js"; import { buildApprovalInstanceListParams, buildEditFormFromInstanceRow, buildInstanceDto, canEditBusinessInstanceRow, createEmptySubmitForm, mapInstanceFromApi, resolveInstanceFormFields, unwrapInstancePage, } from "../approve-list/approveListConstants.js"; import { fetchBusinessTypeOptions } from "../approve-template/approveTemplateConstants.js"; import { collectOptionSourcesFromFields, fetchSelectOptionCaches, } from "../approve-template/selectOptionSource.js"; import { enrichInstanceRowFromFormConfig } from "./approvalInstanceFormConfigTable.js"; import { getApprovalModuleConfig, getModuleListBusinessType, resolveModuleBusinessType, } from "./approvalModuleRegistry.js"; /** * 业务申请页共用:审批实例列表查询、新增/修改保存、详情/编辑弹窗(与审批列表一致) * * @param {object} options * @param {string} options.moduleKey approvalModuleRegistry 中的 key * @param {(row: object) => object} [options.enrichListRow] 列表行增强(从 formPayload 解析展示字段) * @param {(base: object) => object} [options.buildExtraListParams] 追加查询参数 * @param {() => void} [options.beforeSave] 保存前钩子(如同步业务字段到 formPayload) * @param {import('vue').ComputedRef|object} [options.extraFormRules] 额外表单校验 */ export function useApprovalInstanceModule(options = {}) { const { moduleKey, enrichListRow, buildExtraListParams, beforeSave, extraFormRules, } = options; const userStore = useUserStore(); const moduleConfig = computed(() => getApprovalModuleConfig(moduleKey)); const businessTypeOptions = ref([]); /** 与审批列表一致:优先用 TypeEnums 的 value,匹配不到再回退 approvalType */ const defaultListBusinessType = computed(() => { const resolved = resolveModuleBusinessType(moduleKey, businessTypeOptions.value); if (resolved != null && resolved !== "") return resolved; return getModuleListBusinessType(moduleKey); }); async function loadBusinessTypeOptions() { if (businessTypeOptions.value.length) return; try { businessTypeOptions.value = await fetchBusinessTypeOptions(); } catch { businessTypeOptions.value = []; } } const tableData = ref([]); const tableLoading = ref(false); const page = reactive({ current: 1, size: 10, total: 0 }); const detailDialog = reactive({ visible: false }); const detailRow = ref({}); const submitDialog = reactive({ visible: false, mode: "add" }); const submitEditRow = ref(null); const submitForm = reactive(createEmptySubmitForm("")); const submitFormRef = ref(); const submitSaving = ref(false); const templateBindVisible = ref(false); const pendingTemplateBinding = ref(null); /** 最近一次列表查询条件(保存后刷新列表时沿用) */ let lastListSearchForm = null; const isSubmitEdit = computed(() => submitDialog.mode === "edit"); const activeTemplate = computed(() => submitForm.templateSnapshot || null); const submitFormFields = computed(() => { const tplFields = activeTemplate.value?.fields; if (tplFields?.length) return tplFields; return submitForm.formFieldDefs || []; }); const submitFormRules = computed(() => ({ ...buildFormPayloadRules(submitFormFields.value), ...(extraFormRules?.value ?? extraFormRules ?? {}), })); const submitDialogTitle = computed(() => { const label = moduleConfig.value?.label || "申请"; if (submitDialog.mode === "edit") { return `修改${activeTemplate.value?.label || submitForm.templateName || label}`; } return `新增${label}`; }); function mapListRow(row, caches) { const mapped = mapInstanceFromApi(row); const fromFormConfig = enrichInstanceRowFromFormConfig(mapped, caches); return enrichListRow ? enrichListRow(fromFormConfig) : fromFormConfig; } async function fetchList(searchForm = {}) { await loadBusinessTypeOptions(); tableLoading.value = true; try { let extraParams = {}; if (buildExtraListParams) { extraParams = buildExtraListParams(searchForm) || {}; } const res = await listApprovalInstancePage( buildApprovalInstanceListParams({ page, searchForm, businessType: defaultListBusinessType.value, extraParams, }) ); const { records, total } = unwrapInstancePage(res); const mapped = records.map(mapInstanceFromApi); const allFields = []; for (const row of mapped) { const { fields } = resolveInstanceFormFields(row); allFields.push(...fields); } const caches = await fetchSelectOptionCaches( collectOptionSourcesFromFields(allFields) ); tableData.value = mapped.map((row) => mapListRow(row, caches)); page.total = total; } catch { tableData.value = []; page.total = 0; ElMessage.error(`${moduleConfig.value?.label || "申请"}列表加载失败`); } finally { tableLoading.value = false; } } function handleQuery(searchForm) { lastListSearchForm = searchForm; page.current = 1; return fetchList(searchForm); } /** 进入页面:先拉 TypeEnums 解析 businessType,再查列表 */ async function initModuleList(searchForm) { await loadBusinessTypeOptions(); return handleQuery(searchForm); } function pagination({ page: p, limit }, searchForm) { page.current = p; page.size = limit; return fetchList(searchForm); } function openDetail(row) { detailRow.value = { ...row }; detailDialog.visible = true; } function openEdit(row) { if (!canEditBusinessInstanceRow(row)) { ElMessage.warning("进行中或已完成的审批不可修改"); return; } if (!row?.id) { ElMessage.warning("无法修改:缺少审批实例 ID"); return; } submitDialog.mode = "edit"; submitEditRow.value = { ...row }; Object.assign(submitForm, buildEditFormFromInstanceRow(row)); submitDialog.visible = true; } function openEditFromDetail() { const row = detailRow.value; detailDialog.visible = false; openEdit(row); } function resetSubmitForm() { Object.assign(submitForm, createEmptySubmitForm("")); submitEditRow.value = null; } function openAddWithTemplate() { submitDialog.visible = false; pendingTemplateBinding.value = null; templateBindVisible.value = true; } function onTemplateBound(binding) { pendingTemplateBinding.value = binding; } function onTemplateBindClosed() { const binding = pendingTemplateBinding.value; if (!binding) return; pendingTemplateBinding.value = null; openAddFromBinding(binding); } function openAddFromBinding(binding) { resetSubmitForm(); applyBindingToForm(submitForm, binding); submitDialog.mode = "add"; submitEditRow.value = null; submitDialog.visible = true; } function closeSubmitDialog() { submitDialog.visible = false; } async function submitInstanceForm(options = {}) { const { skipValidate = false } = options; if (!skipValidate) { if (!submitFormRef.value?.validate) { ElMessage.warning("表单未就绪,请关闭弹窗后重试"); return false; } try { await submitFormRef.value.validate(); } catch { ElMessage.warning("请完善表单必填项后再保存"); return false; } } if (!activeTemplate.value) { ElMessage.warning("未加载审批模板,无法保存"); return false; } const bindingCheck = validateTemplateBinding({ flowNodes: submitForm.flowNodes }); if (!bindingCheck.ok) { ElMessage.warning(bindingCheck.message); return false; } if (!submitForm.templateId) { ElMessage.warning("缺少模板 ID,无法提交"); return false; } if (beforeSave) { try { await beforeSave(submitForm, { isEdit: isSubmitEdit.value, editRow: submitEditRow.value }); } catch { return false; } } if (submitSaving.value) return false; submitSaving.value = true; try { const dto = buildInstanceDto({ submitForm, activeTemplate: activeTemplate.value, userStore, flowNodes: bindingCheck.nodes, existingRow: isSubmitEdit.value ? submitEditRow.value : null, }); if (isSubmitEdit.value) { await updateApprovalInstance(dto); } else { await saveApprovalInstance(dto); } submitDialog.visible = false; if (!isSubmitEdit.value) page.current = 1; await fetchList(lastListSearchForm ?? {}); 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 { ElMessage.error(isSubmitEdit.value ? "保存失败" : "提交失败"); 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 (submitDialog.visible && submitEditRow.value?.id === row.id) { submitDialog.visible = false; } await fetchList(lastListSearchForm ?? {}); } catch { /* 错误由拦截器提示 */ } } /** 构建标准操作列:详情、修改、删除(与审批列表一致) */ function buildTableActions(extraOperations = []) { return [ { name: "详情", type: "text", clickFun: (row) => openDetail(row) }, { name: "修改", type: "text", disabled: (row) => !canEditBusinessInstanceRow(row), clickFun: (row) => openEdit(row), }, { name: "删除", type: "danger", clickFun: (row) => removeInstance(row), }, ...extraOperations, ]; } return { moduleConfig, defaultListBusinessType, tableData, tableLoading, page, detailDialog, detailRow, submitDialog, submitEditRow, submitForm, submitFormRef, submitSaving, isSubmitEdit, activeTemplate, submitFormFields, submitFormRules, submitDialogTitle, templateBindVisible, pendingTemplateBinding, fetchList, handleQuery, initModuleList, pagination, openDetail, openEdit, openEditFromDetail, openAddWithTemplate, onTemplateBound, onTemplateBindClosed, openAddFromBinding, closeSubmitDialog, resetSubmitForm, submitInstanceForm, removeInstance, buildTableActions, loadBusinessTypeOptions, canEditBusinessInstanceRow, }; }