From 6bbc8752460b9b98dfeb8b616662b4d929a179ab Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 11 六月 2026 15:44:36 +0800
Subject: [PATCH] pro 1.山西省诺颢新材料有限公司配置

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

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
new file mode 100644
index 0000000..9442a5a
--- /dev/null
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
@@ -0,0 +1,702 @@
+import {
+  getApprovalTemplateDetail,
+  listApprovalTemplate,
+  TEMPLATE_TYPE_CUSTOM,
+} from "@/api/officeProcessAutomation/approvalTemplate.js";
+import {
+  approveApprovalInstance,
+  deleteApprovalInstance,
+  listApprovalInstancePage,
+  saveApprovalInstance,
+  updateApprovalInstance,
+} from "@/api/officeProcessAutomation/approvalInstance.js";
+import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
+import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
+import { getDeliveryDetailByShippingNo } from "@/api/salesManagement/deliveryLedger.js";
+import useUserStore from "@/store/modules/user";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { computed, getCurrentInstance, reactive, ref } from "vue";
+import {
+  inferReimburseModuleKeyFromInstance,
+  loadReimburseDetailForInstance,
+  navigateToReimburseManageForEdit,
+  resolveFinReimbursementIdFromInstance,
+} from "../../ReimburseManage/shared/reimburseApproveBridge.js";
+import {
+  fetchBusinessTypeOptions,
+  formatDisplayTime,
+  mapEnabledFromApi,
+  unwrapTemplateList,
+} from "../approve-template/approveTemplateConstants.js";
+import {
+  buildFormPayloadRules,
+  buildTemplateBindingFromDetail,
+  validateTemplateBinding,
+} from "../approve-shared/approvalTemplateBindingUtils.js";
+import {
+  APPROVAL_STATUS_SEARCH_OPTIONS,
+  APPROVAL_TYPE_OPTIONS,
+  approvalStatusLabel,
+  approvalStatusTagType,
+  approvalTypeLabel,
+  buildApprovalInstanceListParams,
+  buildApproveInstanceDto,
+  buildEditFormFromInstanceRow,
+  buildInstanceDto,
+  createEmptySubmitForm,
+  mapInstanceFromApi,
+  mapSubmitTemplateCard,
+  matchBusinessTypeValue,
+  unwrapInstancePage,
+} from "./approveListConstants.js";
+
+export function useApproveList() {
+  const { proxy } = getCurrentInstance() || {};
+  const userStore = useUserStore();
+
+  const tableData = ref([]);
+  const searchBusinessTypeOptions = 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({
+    businessType: "",
+    status: "",
+    createTimeRange: [],
+  });
+
+  const tableLoading = ref(false);
+  const page = reactive({ current: 1, size: 10, total: 0 });
+
+  const detailDialog = reactive({ visible: false });
+  const detailRow = ref({});
+  const detailData = ref({});
+
+  const approveDialog = reactive({ visible: false, row: null });
+  const approveOpinion = ref("");
+  const approveSubmitting = ref(false);
+
+  /** 宸梾/璐圭敤鎶ラ攢涓撶敤璇︽儏銆佸鎵瑰脊绐� */
+  const reimburseDialog = reactive({
+    visible: false,
+    mode: "detail",
+    moduleKey: "",
+    loading: false,
+    reimburseRow: {},
+    instanceRow: null,
+  });
+
+  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(() => ({
+    templateKey: [{ required: true, message: "璇烽�夋嫨瀹℃壒绫诲瀷", trigger: "change" }],
+    ...buildFormPayloadRules(submitFormFields.value),
+  }));
+
+  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",
+      dataType: "tag",
+      formatData: (v) => (v ? "鏄�" : "鍚�"),
+      formatType: (v) => (v ? "success" : "danger"),
+    },
+    {
+      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.filter((x) => ![5, 6, 7].includes(x.value));
+      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.businessType = "";
+    searchForm.status = "";
+    searchForm.createTimeRange = [];
+    handleQuery();
+  }
+
+  async function loadSearchBusinessTypeOptions() {
+    try {
+      searchBusinessTypeOptions.value = await fetchBusinessTypeOptions();
+    } catch {
+      searchBusinessTypeOptions.value = [];
+    }
+  }
+
+  function pagination({ page: p, limit }) {
+    page.current = p;
+    page.size = limit;
+    fetchApprovalList();
+  }
+
+  async function openReimburseDetail(row, mode) {
+    const moduleKey = inferReimburseModuleKeyFromInstance(row);
+    if (!moduleKey) return false;
+    reimburseDialog.mode = mode;
+    reimburseDialog.moduleKey = moduleKey;
+    reimburseDialog.instanceRow = row;
+    reimburseDialog.visible = true;
+    reimburseDialog.loading = true;
+    reimburseDialog.reimburseRow = {};
+    try {
+      const { reimburseRow, moduleKey: resolvedMk } =
+        await loadReimburseDetailForInstance(row, moduleKey);
+      reimburseDialog.moduleKey = resolvedMk || moduleKey;
+      reimburseDialog.reimburseRow = reimburseRow;
+      return true;
+    } catch {
+      ElMessage.error("鍔犺浇鎶ラ攢璇︽儏澶辫触");
+      reimburseDialog.visible = false;
+      return false;
+    } finally {
+      reimburseDialog.loading = false;
+    }
+  }
+
+  async function openDetail(row) {
+    if (isReimburseApprovalInstance(row)) {
+      await openReimburseDetail(row, "detail");
+      return;
+    }
+    
+    detailRow.value = { ...row };
+    // 鎶ヤ环瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鎶ヤ环鍗曞彿"鍘绘煡鎶ヤ环鍒楄〃
+    if (row.businessType === 6) {
+      const quotationNo = row?.quotationNo;
+      if (quotationNo) {
+        const res = await getQuotationList({ quotationNo });
+        const records = res?.data?.records || [];
+        detailData.value = records[0] || {};
+      }
+    }
+    
+    // 閲囪喘瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"閲囪喘鍚堝悓鍙�"鍘绘煡閲囪喘璇︽儏
+    else if (row.businessType === 5) {
+      const purchaseContractNumber = row?.purchaseContractNumber;
+      if (purchaseContractNumber) {
+        const res = await getPurchaseByCode({ purchaseContractNumber });
+        detailData.value = res || {};
+      }
+    }
+    
+    // 鍙戣揣瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鍙戣揣鍗曞彿"鍘绘煡鍙戣揣璇︽儏
+    else if (row.businessType === 7) {
+      const shippingNo = row?.shippingNo;
+      if (shippingNo) {
+        const res = await getDeliveryDetailByShippingNo({ shippingNo });
+        detailData.value = res?.data || res || {};
+      }
+    }
+    
+    // 鍏朵粬瀹℃壒绫诲瀷
+    else {
+      detailData.value = {};
+    }
+    
+    detailDialog.visible = true;
+  }
+
+  async function openApprove(row) {
+
+    if (inferReimburseModuleKeyFromInstance(row)) {
+      approveOpinion.value = "";
+      await openReimburseDetail(row, "approve");
+      return;
+    }
+    approveDialog.row = { ...row };
+    approveOpinion.value = "";
+    // 鎶ヤ环瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鎶ヤ环鍗曞彿"鍘绘煡鎶ヤ环鍒楄〃
+    if (row.businessType === 6) {
+      const quotationNo = row?.quotationNo;
+      if (quotationNo) {
+        const res = await getQuotationList({ quotationNo });
+        const records = res?.data?.records || [];
+        detailData.value = records[0] || {};
+      }
+    }
+
+    // 閲囪喘瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"閲囪喘鍚堝悓鍙�"鍘绘煡閲囪喘璇︽儏
+    else if (row.businessType === 5) {
+      const purchaseContractNumber = row?.purchaseContractNumber;
+      if (purchaseContractNumber) {
+        const res = await getPurchaseByCode({ purchaseContractNumber });
+        detailData.value = res || {};
+      }
+    }
+
+    // 鍙戣揣瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鍙戣揣鍗曞彿"鍘绘煡鍙戣揣璇︽儏
+    else if (row.businessType === 7) {
+      const shippingNo = row?.shippingNo;
+      if (shippingNo) {
+        const res = await getDeliveryDetailByShippingNo({ shippingNo });
+        detailData.value = res?.data || res || {};
+      }
+    }
+
+    // 鍏朵粬瀹℃壒绫诲瀷
+    else {
+      detailData.value = {};
+    }
+    approveDialog.visible = true;
+  }
+
+  function isReimburseApprovalInstance(row) {
+    return Boolean(inferReimburseModuleKeyFromInstance(row));
+  }
+
+  function resetSubmitDialogState() {
+    submitDialog.mode = "add";
+    submitDialog.step = 1;
+    selectedBusinessType.value = "";
+    submitEditRow.value = null;
+    Object.assign(submitForm, createEmptySubmitForm(""));
+  }
+
+  function openSubmitDialog() {
+    resetSubmitDialogState();
+    submitDialog.visible = true;
+    loadSubmitTemplates();
+  }
+
+  async function openEditDialog(row) {
+    if (row?.approvalStatus !== "pending") {
+      ElMessage.warning("浠呭鏍镐腑鐨勫鎵瑰彲淇敼");
+      return;
+    }
+    const moduleKey = inferReimburseModuleKeyFromInstance(row);
+    if (moduleKey) {
+      const rid = resolveFinReimbursementIdFromInstance(row);
+      if (rid == null) {
+        ElMessage.warning("鏃犳硶淇敼锛氱己灏戞姤閿�鍗� ID");
+        return;
+      }
+      try {
+        await navigateToReimburseManageForEdit(proxy?.$router, moduleKey, rid);
+      } catch {
+        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 applied = buildTemplateBindingFromDetail(res);
+      Object.assign(submitForm, {
+        templateKey: String(card.id),
+        ...applied,
+        businessType:
+          applied.businessType ?? card.businessType ?? selectedBusinessType.value,
+      });
+      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 bindingCheck = validateTemplateBinding({ flowNodes: submitForm.flowNodes });
+    if (!bindingCheck.ok) {
+      ElMessage.warning(bindingCheck.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: bindingCheck.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 bindingCheck = validateTemplateBinding({ flowNodes: submitForm.flowNodes });
+    if (!bindingCheck.ok) {
+      ElMessage.warning(bindingCheck.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: bindingCheck.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 submitReimburseApprove(result) {
+    const row = reimburseDialog.instanceRow;
+    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)
+      );
+      reimburseDialog.visible = false;
+      await fetchApprovalList();
+      return { ok: true, result };
+    } catch {
+      ElMessage.error("瀹℃壒鎿嶄綔澶辫触");
+      return { ok: false };
+    } finally {
+      approveSubmitting.value = false;
+    }
+  }
+
+  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,
+    APPROVAL_STATUS_SEARCH_OPTIONS,
+    searchBusinessTypeOptions,
+    loadSearchBusinessTypeOptions,
+    approvalTypeLabel,
+    approvalStatusLabel,
+    approvalStatusTagType,
+    approvalActionLabel,
+    searchForm,
+    tableLoading,
+    page,
+    tableData,
+    tableColumn,
+    detailDialog,
+    detailRow,
+    detailData,
+    reimburseDialog,
+    approveDialog,
+    approveOpinion,
+    approveSubmitting,
+    submitReimburseApprove,
+    isReimburseApprovalInstance,
+    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,
+  };
+}
\ No newline at end of file

--
Gitblit v1.9.3