import type { UIBill } from '$lib/models/bill'; import type { CategoryStat, MonthlyStat, DailyExpenseData, TotalStats, PieChartDataItem } from '$lib/types/analysis'; import { pieColors } from '$lib/constants/chart'; /** * 计算分类统计 */ export function calculateCategoryStats(records: UIBill[]): CategoryStat[] { const stats = new Map(); for (const r of records) { if (!stats.has(r.category)) { stats.set(r.category, { expense: 0, income: 0, count: 0 }); } const s = stats.get(r.category)!; s.count++; const amount = r.amount || 0; if (r.incomeExpense === '支出') { s.expense += amount; } else { s.income += amount; } } return [...stats.entries()] .map(([category, data]) => ({ category, ...data })) .sort((a, b) => b.expense - a.expense); } /** * 计算月度统计 */ export function calculateMonthlyStats(records: UIBill[]): MonthlyStat[] { const stats = new Map(); for (const r of records) { const month = r.time.substring(0, 7); // YYYY-MM if (!stats.has(month)) { stats.set(month, { expense: 0, income: 0 }); } const s = stats.get(month)!; const amount = r.amount || 0; if (r.incomeExpense === '支出') { s.expense += amount; } else { s.income += amount; } } return [...stats.entries()] .map(([month, data]) => ({ month, ...data })) .sort((a, b) => a.month.localeCompare(b.month)); } /** * 计算每日支出数据(用于面积图) */ export function calculateDailyExpenseData(records: UIBill[]): DailyExpenseData[] { const stats = new Map(); for (const r of records) { if (r.incomeExpense !== '支出') continue; const date = r.time.substring(0, 10); // YYYY-MM-DD const amount = r.amount || 0; stats.set(date, (stats.get(date) || 0) + amount); } return [...stats.entries()] .map(([date, value]) => ({ date: new Date(date), value })) .sort((a, b) => a.date.getTime() - b.date.getTime()); } /** * 计算总计统计 */ export function calculateTotalStats(records: UIBill[]): TotalStats { return { expense: records .filter(r => r.incomeExpense === '支出') .reduce((sum, r) => sum + (r.amount || 0), 0), income: records .filter(r => r.incomeExpense === '收入') .reduce((sum, r) => sum + (r.amount || 0), 0), count: records.length, }; } /** * 计算百分比 */ export function getPercentage(value: number, total: number): number { return total > 0 ? (value / total) * 100 : 0; } /** * 计算饼状图数据 */ export function calculatePieChartData( categoryStats: CategoryStat[], totalExpense: number ): PieChartDataItem[] { return categoryStats .filter(s => s.expense > 0) .map((stat, i) => ({ category: stat.category, value: stat.expense, color: pieColors[i % pieColors.length], percentage: getPercentage(stat.expense, totalExpense) })); } /** * 获取 Top N 支出记录 */ export function getTopExpenses(records: UIBill[], n: number = 10): UIBill[] { return records .filter(r => r.incomeExpense === '支出') .sort((a, b) => (b.amount || 0) - (a.amount || 0)) .slice(0, n); } /** * 统计支出/收入笔数 */ export function countByType(records: UIBill[], type: '支出' | '收入'): number { return records.filter(r => r.incomeExpense === type).length; }