yyb
7 天以前 61b9452f138841d453bf4b2503d78c2aaf2e4394
src/views/officeProcessAutomation/ApproveManage/approve-shared/useApprovalInstanceModule.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,408 @@
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 {
  filterInstanceRowsByModuleSearch,
  hasActiveModuleSearch,
} from "./approvalInstanceListSearch.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([]);
  /** åˆ—表查询 businessType:优先 registry å†™æ­»æžšä¸¾ï¼Œå†å›žé€€ TypeEnums */
  const defaultListBusinessType = computed(() => {
    const fixed = getModuleListBusinessType(moduleKey);
    if (fixed != null && fixed !== "") return fixed;
    const resolved = resolveModuleBusinessType(moduleKey, businessTypeOptions.value);
    if (resolved != null && resolved !== "") return resolved;
    return "";
  });
  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)
      );
      let rows = mapped.map((row) => mapListRow(row, caches));
      if (hasActiveModuleSearch(moduleKey, searchForm)) {
        rows = filterInstanceRowsByModuleSearch(moduleKey, rows, searchForm);
      }
      tableData.value = rows;
      page.total = hasActiveModuleSearch(moduleKey, searchForm)
        ? rows.length
        : 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,
  };
}