/** * 文件职责:财务概览页面状态编排。 */ import type { FinanceOverviewCompositionViewItem, FinanceOverviewDashboardState, FinanceOverviewFilterState, FinanceOverviewTrendState, OptionItem, } from '../types'; import type { FinanceOverviewDimension, FinanceOverviewTrendRange, } from '#/api/finance/overview'; import type { StoreListItemDto } from '#/api/store'; import { computed, onActivated, onMounted, reactive, ref, watch } from 'vue'; import { useAccessStore } from '@vben/stores'; import { createDefaultFinanceOverviewDashboard, DEFAULT_OVERVIEW_FILTER, DEFAULT_OVERVIEW_TREND_STATE, FINANCE_OVERVIEW_VIEW_PERMISSION, OVERVIEW_DIMENSION_OPTIONS, } from './overview-page/constants'; import { createDataActions } from './overview-page/data-actions'; import { toCostCompositionViewItems, toIncomeCompositionViewItems, } from './overview-page/helpers'; /** 创建财务概览页面组合状态。 */ export function useFinanceOverviewPage() { const accessStore = useAccessStore(); const stores = ref([]); const filters = reactive({ ...DEFAULT_OVERVIEW_FILTER, }); const trendState = reactive({ ...DEFAULT_OVERVIEW_TREND_STATE, }); const dashboard = reactive( createDefaultFinanceOverviewDashboard(), ); const isStoreLoading = ref(false); const isDashboardLoading = ref(false); const accessCodeSet = computed( () => new Set((accessStore.accessCodes ?? []).map(String)), ); const canView = computed(() => accessCodeSet.value.has(FINANCE_OVERVIEW_VIEW_PERMISSION), ); const storeOptions = computed(() => stores.value.map((item) => ({ label: item.name, value: item.id, })), ); const showStoreSelect = computed(() => filters.dimension === 'store'); const hasStore = computed(() => stores.value.length > 0); const canQueryCurrentScope = computed( () => filters.dimension === 'tenant' || Boolean(filters.storeId), ); const incomeTrendPoints = computed(() => trendState.incomeRange === '7' ? dashboard.incomeTrend.last7Days : dashboard.incomeTrend.last30Days, ); const profitTrendPoints = computed(() => trendState.profitRange === '7' ? dashboard.profitTrend.last7Days : dashboard.profitTrend.last30Days, ); const incomeCompositionItems = computed( () => toIncomeCompositionViewItems(dashboard.incomeComposition.items), ); const costCompositionItems = computed( () => toCostCompositionViewItems(dashboard.costComposition.items), ); const topProductMaxPercentage = computed(() => { if (dashboard.topProducts.items.length === 0) { return 0; } return Math.max( ...dashboard.topProducts.items.map((item) => item.percentage), ); }); const dataActions = createDataActions({ stores, filters, dashboard, isStoreLoading, isDashboardLoading, }); function ensureStoreSelection() { if (filters.dimension !== 'store') { return false; } if (filters.storeId) { return false; } const firstStore = stores.value[0]; if (!firstStore) { return false; } filters.storeId = firstStore.id; return true; } async function loadByCurrentScope() { if (!canView.value) { return; } if (filters.dimension === 'store' && !filters.storeId) { dataActions.clearDashboard(); return; } await dataActions.loadDashboard(); } async function initPageData() { if (!canView.value) { stores.value = []; filters.storeId = ''; dataActions.clearDashboard(); return; } await dataActions.loadStores(); if (ensureStoreSelection()) { return; } await loadByCurrentScope(); } function setDimension(value: FinanceOverviewDimension) { filters.dimension = value; } function setStoreId(value: string) { filters.storeId = value; } function setIncomeRange(value: FinanceOverviewTrendRange) { trendState.incomeRange = value; } function setProfitRange(value: FinanceOverviewTrendRange) { trendState.profitRange = value; } watch( () => [filters.dimension, filters.storeId], async () => { if (!canView.value) { return; } if (filters.dimension === 'store' && ensureStoreSelection()) { return; } await loadByCurrentScope(); }, ); watch( () => canView.value, async (value, oldValue) => { if (value === oldValue) { return; } if (!value) { stores.value = []; filters.storeId = ''; dataActions.clearDashboard(); return; } await initPageData(); }, ); onMounted(async () => { await initPageData(); }); onActivated(() => { if (!canView.value) { return; } if (stores.value.length === 0) { void initPageData(); return; } if (!showStoreSelect.value || filters.storeId) { void loadByCurrentScope(); } }); return { canQueryCurrentScope, canView, costCompositionItems, dashboard, hasStore, incomeCompositionItems, incomeTrendPoints, isDashboardLoading, isStoreLoading, OVERVIEW_DIMENSION_OPTIONS, profitTrendPoints, showStoreSelect, storeOptions, topProductMaxPercentage, trendState, filters, setDimension, setStoreId, setIncomeRange, setProfitRange, }; }