chore: add frontend/src/stores/dataStore.ts
This commit is contained in:
@@ -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<void>;
|
||||
fetchRecords: (params?: {
|
||||
accountId?: number;
|
||||
type?: 'income' | 'expense';
|
||||
category?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}) => Promise<void>;
|
||||
fetchBudgets: (month?: string) => Promise<void>;
|
||||
fetchDashboardSummary: () => Promise<void>;
|
||||
fetchTrendData: (startDate: string, endDate: string) => Promise<void>;
|
||||
fetchMonthlyCompare: (month: string) => Promise<void>;
|
||||
|
||||
// ===== Actions - 数据操作 =====
|
||||
createRecord: (data: any) => Promise<void>;
|
||||
updateRecord: (id: number, data: any) => Promise<void>;
|
||||
deleteRecord: (id: number) => Promise<void>;
|
||||
createBudget: (data: any) => Promise<void>;
|
||||
updateBudget: (id: number, data: any) => Promise<void>;
|
||||
deleteBudget: (id: number) => Promise<void>;
|
||||
clearError: () => void;
|
||||
}
|
||||
|
||||
export const useDataStore = create<DataState>((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 }),
|
||||
}));
|
||||
Reference in New Issue
Block a user