| | |
| | | --> |
| | | <template> |
| | | <view class="template-select-page sales-account"> |
| | | <PageHeader title="选择审批模板" |
| | | <PageHeader :title="pageHeaderTitle" |
| | | @back="goBack" /> |
| | | |
| | | <view v-if="typeOptions.length" |
| | | <view v-if="typeOptions.length && !moduleKey" |
| | | class="step-section"> |
| | | <view class="tabs-wrap"> |
| | | <up-tabs :list="tabList" |
| | |
| | | line-color="#2979ff" |
| | | @click="onTabClick" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="useAllTemplatesFallback && allTemplates.length" |
| | | class="fallback-hint"> |
| | | <text>当前类型下无匹配模板,已显示全部 {{ allTemplates.length }} 个可用模板</text> |
| | | </view> |
| | | |
| | | <view class="search-section"> |
| | |
| | | class="loading-wrap"> |
| | | <up-loading-icon mode="circle" /> |
| | | <text class="loading-text">加载中...</text> |
| | | </view> |
| | | <view v-else-if="!typeOptions.length" |
| | | class="empty-wrap"> |
| | | <up-empty mode="list" |
| | | text="未获取到审批类型" /> |
| | | </view> |
| | | <view v-else-if="displayList.length" |
| | | class="ledger-list"> |
| | |
| | | import { computed, ref } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { listApprovalTemplateByType } from "@/api/oa/approvalTemplate.js"; |
| | | import { OA_NAV } from "@/config/oaPaths.js"; |
| | | import { |
| | | buildTypeLabelMap, |
| | | CUSTOM_TEMPLATE_LIST_TYPE, |
| | | fetchApprovalTemplateTypes, |
| | | buildTypeOptionsFromTemplates, |
| | | FALLBACK_BUSINESS_TYPE_OPTIONS, |
| | | fetchEnabledApprovalTemplates, |
| | | filterTemplatesByBusinessType, |
| | | filterTemplatesByBusinessTypes, |
| | | getBusinessTypeLabel, |
| | | getDefaultTypeTabIndex, |
| | | pickTabIndexWithTemplates, |
| | | } from "../../_utils/approvalTemplateType.js"; |
| | | import { |
| | | getApprovalModuleConfig, |
| | | getModuleMatchingBusinessTypes, |
| | | resolveModuleBusinessType, |
| | | } from "../../_utils/approvalModuleRegistry.js"; |
| | | |
| | | const moduleKey = ref(""); |
| | | const typeOptions = ref([]); |
| | | const typeLabelMap = ref({}); |
| | | /** 全部自定义已启用模板(list/1 一次拉取) */ |
| | |
| | | typeOptions.value.map(opt => ({ name: opt.name })) |
| | | ); |
| | | |
| | | const moduleConfig = computed(() => |
| | | moduleKey.value ? getApprovalModuleConfig(moduleKey.value) : null |
| | | ); |
| | | |
| | | const pageHeaderTitle = computed(() => { |
| | | if (moduleConfig.value?.label) { |
| | | return `选择${moduleConfig.value.label}模板`; |
| | | } |
| | | return "选择审批模板"; |
| | | }); |
| | | |
| | | const moduleBusinessTypes = computed(() => { |
| | | if (!moduleKey.value) return []; |
| | | return getModuleMatchingBusinessTypes(moduleKey.value, typeOptions.value); |
| | | }); |
| | | |
| | | const currentTypeOption = computed(() => typeOptions.value[activeTab.value]); |
| | | |
| | | /** 无 moduleKey 且当前 Tab 筛不到时,展示全部模板避免「有数据却空白」 */ |
| | | const useAllTemplatesFallback = computed(() => { |
| | | if (moduleKey.value) return false; |
| | | if (!allTemplates.value.length) return false; |
| | | const businessType = currentTypeOption.value?.value; |
| | | if (businessType == null || businessType === "") return true; |
| | | return filterTemplatesByBusinessType(allTemplates.value, businessType).length === 0; |
| | | }); |
| | | |
| | | const currentSource = computed(() => { |
| | | if (moduleKey.value && moduleBusinessTypes.value.length) { |
| | | const filtered = filterTemplatesByBusinessTypes( |
| | | allTemplates.value, |
| | | moduleBusinessTypes.value |
| | | ); |
| | | if (filtered.length) return filtered; |
| | | return allTemplates.value; |
| | | } |
| | | if (useAllTemplatesFallback.value) { |
| | | return allTemplates.value; |
| | | } |
| | | const businessType = currentTypeOption.value?.value; |
| | | return filterTemplatesByBusinessType(allTemplates.value, businessType); |
| | | }); |
| | |
| | | }); |
| | | |
| | | const emptyText = computed(() => { |
| | | if (allTemplates.value.length === 0) { |
| | | return "暂无已启用的审批模板,请先在「审批模板」中创建并启用"; |
| | | } |
| | | if (moduleConfig.value?.label) { |
| | | return `暂无${moduleConfig.value.label}可用模板`; |
| | | } |
| | | if (useAllTemplatesFallback.value) { |
| | | return "当前类型下无匹配模板"; |
| | | } |
| | | const typeName = currentTypeOption.value?.name || "该审批类型"; |
| | | return `暂无${typeName}下的模板`; |
| | | return `暂无${typeName}下的模板(可切换上方类型)`; |
| | | }); |
| | | |
| | | const businessTypeText = type => |
| | |
| | | return count != null ? `${count} 个` : "-"; |
| | | }; |
| | | |
| | | const normalizeList = data => { |
| | | const list = Array.isArray(data) |
| | | ? data |
| | | : Array.isArray(data?.records) |
| | | ? data.records |
| | | : []; |
| | | return list.filter(item => String(item?.enabled ?? "1") === "1"); |
| | | }; |
| | | |
| | | const loadCustomTemplates = () => |
| | | listApprovalTemplateByType(CUSTOM_TEMPLATE_LIST_TYPE) |
| | | .then(res => { |
| | | allTemplates.value = normalizeList(res?.data); |
| | | }) |
| | | .catch(() => { |
| | | allTemplates.value = []; |
| | | uni.showToast({ title: "加载模板列表失败", icon: "none" }); |
| | | }); |
| | | |
| | | const initPage = async () => { |
| | | loading.value = true; |
| | | keyword.value = ""; |
| | | allTemplates.value = []; |
| | | try { |
| | | const [opts] = await Promise.all([ |
| | | const [opts, templates] = await Promise.all([ |
| | | fetchApprovalTemplateTypes(), |
| | | loadCustomTemplates(), |
| | | fetchEnabledApprovalTemplates(), |
| | | ]); |
| | | typeOptions.value = opts; |
| | | typeLabelMap.value = buildTypeLabelMap(opts); |
| | | activeTab.value = getDefaultTypeTabIndex(opts); |
| | | let resolvedOpts = opts?.length ? opts : buildTypeOptionsFromTemplates(templates); |
| | | if (!resolvedOpts.length) { |
| | | resolvedOpts = [...FALLBACK_BUSINESS_TYPE_OPTIONS]; |
| | | } |
| | | typeOptions.value = resolvedOpts; |
| | | typeLabelMap.value = buildTypeLabelMap(resolvedOpts); |
| | | allTemplates.value = templates; |
| | | |
| | | if (!templates.length) { |
| | | uni.showToast({ |
| | | title: "未获取到已启用模板", |
| | | icon: "none", |
| | | duration: 2500, |
| | | }); |
| | | } |
| | | |
| | | if (moduleKey.value) { |
| | | const resolved = resolveModuleBusinessType(moduleKey.value, opts); |
| | | const idx = opts.findIndex( |
| | | opt => String(opt.value) === String(resolved) |
| | | ); |
| | | activeTab.value = |
| | | idx >= 0 ? idx : pickTabIndexWithTemplates(resolvedOpts, templates); |
| | | } else { |
| | | activeTab.value = pickTabIndexWithTemplates(resolvedOpts, templates); |
| | | } |
| | | } catch { |
| | | typeOptions.value = []; |
| | | typeLabelMap.value = {}; |
| | | uni.showToast({ title: "获取审批类型失败", icon: "none" }); |
| | | allTemplates.value = []; |
| | | uni.showToast({ title: "加载模板失败", icon: "none" }); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | |
| | | uni.showToast({ title: "该模板已停用", icon: "none" }); |
| | | return; |
| | | } |
| | | const base = `${OA_NAV.approveListApply}?templateId=${item.id}`; |
| | | uni.navigateTo({ |
| | | url: `${OA_NAV.approveListApply}?templateId=${item.id}`, |
| | | url: moduleKey.value ? `${base}&moduleKey=${moduleKey.value}` : base, |
| | | }); |
| | | }; |
| | | |
| | | onLoad(() => { |
| | | onLoad(options => { |
| | | moduleKey.value = options?.moduleKey || ""; |
| | | initPage(); |
| | | }); |
| | | </script> |
| | |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .fallback-hint { |
| | | margin: 8px 12px 0; |
| | | padding: 8px 12px; |
| | | font-size: 12px; |
| | | color: #e6a23c; |
| | | background: #fdf6ec; |
| | | border-radius: 6px; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .step-section { |
| | | background: #fff; |
| | | border-bottom: 1px solid #f0f0f0; |