diff --git a/frontend/src/stores/dataStore.ts b/frontend/src/stores/dataStore.ts new file mode 100644 index 0000000..9069aa2 --- /dev/null +++ b/frontend/src/stores/dataStore.ts @@ -0,0 +1,226 @@ +/** + * 数据状态管理 - DataStore + * 功能:使用 Zustand 管理全局数据状态,包括账户、账单、预算、统计数据的 CRUD + * 状态流转:每个 action 遵循 loading -> 请求 -> success/error -> loading=false 的流程 + * 级联刷新:CRUD 操作成功后自动重新拉取列表和仪表盘汇总数据,保证跨页面数据一致性 + * API 依赖:accountsApi、recordsApi、budgetsApi、statisticsApi + */ +import { create } from 'zustand'; +import type { Account, Record, Budget, DashboardSummary, TrendStat, MonthlyCompare } from '../types'; +import { accountsApi, recordsApi, budgetsApi, statisticsApi } from '../services'; + +/** + * DataState 接口 - 定义全局数据状态的结构 + */ +interface DataState { + // ===== 数据实体 ===== + accounts: Account[]; // 账户列表 + records: Record[]; // 账单记录列表 + budgets: Budget[]; // 预算配置列表 + dashboardSummary: DashboardSummary | null; // 仪表盘汇总数据(余额、收支、预算使用率) + trendData: TrendStat[]; // 趋势统计数据(用于折线图) + monthlyCompare: MonthlyCompare | null; // 月度对比数据(用于柱状对比图) + loading: boolean; // 全局加载状态 + error: string | null; // 全局错误信息 + + // ===== Actions - 数据查询 ===== + fetchAccounts: () => Promise; + fetchRecords: (params?: { + accountId?: number; + type?: 'income' | 'expense'; + category?: string; + startDate?: string; + endDate?: string; + }) => Promise; + fetchBudgets: (month?: string) => Promise; + fetchDashboardSummary: () => Promise; + fetchTrendData: (startDate: string, endDate: string) => Promise; + fetchMonthlyCompare: (month: string) => Promise; + + // ===== Actions - 数据操作 ===== + createRecord: (data: any) => Promise; + updateRecord: (id: number, data: any) => Promise; + deleteRecord: (id: number) => Promise; + createBudget: (data: any) => Promise; + updateBudget: (id: number, data: any) => Promise; + deleteBudget: (id: number) => Promise; + clearError: () => void; +} + +export const useDataStore = create((set, get) => ({ + accounts: [], + records: [], + budgets: [], + dashboardSummary: null, + trendData: [], + monthlyCompare: null, + loading: false, + error: null, + + // API: GET /api/accounts - 获取账户列表 + fetchAccounts: async () => { + set({ loading: true, error: null }); + try { + const data = await accountsApi.getAccounts(); + set({ accounts: data }); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: GET /api/records - 获取账单记录列表(支持多条件筛选) + fetchRecords: async (params) => { + set({ loading: true, error: null }); + try { + const data = await recordsApi.getRecords(params); + set({ records: data }); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: GET /api/budgets - 获取预算列表(可按月份筛选) + fetchBudgets: async (month) => { + set({ loading: true, error: null }); + try { + const data = await budgetsApi.getBudgets(month); + set({ budgets: data }); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: GET /api/statistics/dashboard - 获取仪表盘汇总数据 + // 同时更新 accounts 状态,因为汇总数据中包含账户信息 + fetchDashboardSummary: async () => { + set({ loading: true, error: null }); + try { + const data = await statisticsApi.getDashboardSummary(); + set({ dashboardSummary: data, accounts: data.accounts }); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: fetchTrendData - 获取日期趋势数据(用于折线图) + fetchTrendData: async (startDate: string, endDate: string) => { + set({ loading: true, error: null }); + try { + const data = await statisticsApi.getTrendStats(startDate, endDate); + set({ trendData: data }); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: fetchMonthlyCompare - 获取本月与上月对比数据(用于柱状对比图) + fetchMonthlyCompare: async (month: string) => { + set({ loading: true, error: null }); + try { + const data = await statisticsApi.getMonthlyCompare(month); + set({ monthlyCompare: data }); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: POST /api/records - 创建账单记录,成功后级联刷新列表和仪表盘 + // 级联刷新目的:确保其他页面(如 Dashboard、Budget)能立即看到最新数据 + createRecord: async (data) => { + set({ loading: true, error: null }); + try { + await recordsApi.createRecord(data); + await get().fetchRecords(); + await get().fetchDashboardSummary(); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: PUT /api/records/:id - 更新账单记录,成功后级联刷新 + updateRecord: async (id, data) => { + set({ loading: true, error: null }); + try { + await recordsApi.updateRecord(id, data); + await get().fetchRecords(); + await get().fetchDashboardSummary(); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: DELETE /api/records/:id - 删除账单记录,成功后级联刷新 + deleteRecord: async (id) => { + set({ loading: true, error: null }); + try { + await recordsApi.deleteRecord(id); + await get().fetchRecords(); + await get().fetchDashboardSummary(); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: POST /api/budgets - 创建预算,成功后级联刷新预算列表和仪表盘 + createBudget: async (data) => { + set({ loading: true, error: null }); + try { + await budgetsApi.createBudget(data); + await get().fetchBudgets(); + await get().fetchDashboardSummary(); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: PUT /api/budgets/:id - 更新预算,成功后级联刷新 + updateBudget: async (id, data) => { + set({ loading: true, error: null }); + try { + await budgetsApi.updateBudget(id, data); + await get().fetchBudgets(); + await get().fetchDashboardSummary(); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // API: DELETE /api/budgets/:id - 删除预算,成功后级联刷新 + deleteBudget: async (id) => { + set({ loading: true, error: null }); + try { + await budgetsApi.deleteBudget(id); + await get().fetchBudgets(); + await get().fetchDashboardSummary(); + } catch (error) { + set({ error: (error as Error).message }); + } finally { + set({ loading: false }); + } + }, + + // 清除全局错误信息,用于错误状态重置 + clearError: () => set({ error: null }), +}));