diff --git a/src/components/home/DailyInsightCard/DailyInsightCard.tsx b/src/components/home/DailyInsightCard/DailyInsightCard.tsx index 505ace7..bfd82f7 100644 --- a/src/components/home/DailyInsightCard/DailyInsightCard.tsx +++ b/src/components/home/DailyInsightCard/DailyInsightCard.tsx @@ -21,6 +21,7 @@ interface DailyInsightCardProps { top3Categories?: { name: string; amount: number }[]; todayTransactionCount?: number; last7DaysSpend?: number[]; // 最近7天每日支出 + recentTransactions?: { date: string; categoryName: string; amount: number; type: string; note?: string }[]; } export const DailyInsightCard: React.FC = ({ @@ -39,6 +40,7 @@ export const DailyInsightCard: React.FC = ({ top3Categories, todayTransactionCount, last7DaysSpend, + recentTransactions, }) => { const [aiData, setAiData] = useState<{ spending: string; budget: string; emoji?: string; tip?: string } | null>(null); const [isAiLoading, setIsAiLoading] = useState(false); @@ -74,6 +76,7 @@ export const DailyInsightCard: React.FC = ({ top3Categories, todayTransactionCount, last7DaysSpend, + recentTransactions, }); setAiData(result); } catch (e) { @@ -85,7 +88,7 @@ export const DailyInsightCard: React.FC = ({ const timer = setTimeout(fetchAI, 500); return () => clearTimeout(timer); - }, [todaySpend, yesterdaySpend, monthlyBudget, monthlySpent, topCategory, maxTransaction, lastWeekSpend, streakDays, budgetRemaining, daysRemaining, weeklyTotal, avgDailySpend, top3Categories, todayTransactionCount, last7DaysSpend]); + }, [todaySpend, yesterdaySpend, monthlyBudget, monthlySpent, topCategory, maxTransaction, lastWeekSpend, streakDays, budgetRemaining, daysRemaining, weeklyTotal, avgDailySpend, top3Categories, todayTransactionCount, last7DaysSpend, recentTransactions]); const getSpendingInsight = (today: number, yesterday: number) => { if (aiData) return { text: {aiData.spending}, type: 'ai' }; diff --git a/src/pages/Home/Home.css b/src/pages/Home/Home.css index b52bfdd..3224f6c 100644 --- a/src/pages/Home/Home.css +++ b/src/pages/Home/Home.css @@ -30,20 +30,21 @@ .home-greeting { display: flex; flex-direction: column; - gap: 2px; - /* Tighter gap */ + gap: 0.75rem; + /* Increased gap for better breathing room */ } /* ... */ .greeting-text { font-family: 'Outfit', sans-serif; - font-size: 1.5rem; - /* Slightly smaller for compactness */ + font-size: 1.75rem; + /* Restored size */ font-weight: 700; - margin: 0; + margin: 0.25rem 0; + /* Added vertical margin */ color: var(--text-primary); - line-height: 1.2; + line-height: 1.3; } /* ... */ diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 837c565..f852e3d 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -207,6 +207,16 @@ function Home() { }; }, [todayTransactions, weekTransactions, categories, monthlyBudgetSpentTotal]); + const recentTransactionsForAI = useMemo(() => { + return weekTransactions.map(t => ({ + date: new Date(t.transactionDate).toLocaleDateString('zh-CN'), + categoryName: categories.find(c => c.id === t.categoryId)?.name || '未知', + amount: t.amount, + type: t.type, + note: t.note + })); + }, [weekTransactions, categories]); + const handleQuickTransaction = () => { navigate('/transactions?action=new'); }; @@ -513,6 +523,7 @@ function Home() { lastWeekSpend={lastWeekSpend} streakDays={streakInfo?.currentStreak || 0} budgetRemaining={monthlyBudgetTotal - monthlyBudgetSpentTotal} + recentTransactions={recentTransactionsForAI} avgDailySpend={avgDailySpend} top3Categories={top3Categories} todayTransactionCount={todayTransactionCount} diff --git a/src/services/aiService.ts b/src/services/aiService.ts index 3457066..3f5fe4b 100644 --- a/src/services/aiService.ts +++ b/src/services/aiService.ts @@ -319,6 +319,7 @@ export interface DailyInsightContext { // 分类分析 topCategory?: { name: string; amount: number }; top3Categories?: { name: string; amount: number }[]; // 本月前三分类 + recentTransactions?: { date: string; categoryName: string; amount: number; type: string; note?: string }[]; // 最近7天交易详情 // 交易详情 maxTransaction?: { note: string; amount: number }; @@ -333,7 +334,9 @@ export interface DailyInsightContext { */ export async function getDailyInsight(context: DailyInsightContext): Promise<{ spending: string; budget: string; emoji?: string; tip?: string }> { // Hash needs to include new fields - const currentHash = JSON.stringify(context); + // Simplify recentTransactions for hash to avoid order issues or too long string + const contextForHash = { ...context, recentTransactions: context.recentTransactions?.length }; + const currentHash = JSON.stringify(contextForHash); const NOW = Date.now(); const CACHE_TTL = 30 * 60 * 1000; // 30 Minutes @@ -348,6 +351,12 @@ export async function getDailyInsight(context: DailyInsightContext): Promise<{ s const weekday = new Date().toLocaleDateString('zh-CN', { weekday: 'long' }); const weekDiff = context.lastWeekSpend !== undefined ? (context.todaySpend - context.lastWeekSpend) : 0; + // Format recent transactions for AI (simplify to save tokens) + const formattedTransactions = context.recentTransactions + ?.slice(0, 50) // Limit to 50 items + .map(t => `${t.date}: ${t.categoryName} ${t.type === 'expense' ? '-' : '+'}${t.amount.toFixed(1)}${t.note ? ` (${t.note})` : ''}`) + .join('\n'); + // Prepare enriched context for the backend // We format some fields to help the AI understand better (like percentages) const enrichedContext = { @@ -356,7 +365,8 @@ export async function getDailyInsight(context: DailyInsightContext): Promise<{ s weekDiff, date: new Date().toLocaleDateString('zh-CN'), monthProgressPercent: `${(context.monthProgress * 100).toFixed(0)}%`, - budgetUsedPercent: `${(context.monthlySpent / (context.monthlyBudget || 1) * 100).toFixed(0)}%` + budgetUsedPercent: `${(context.monthlySpent / (context.monthlyBudget || 1) * 100).toFixed(0)}%`, + recentTransactionsSummary: formattedTransactions // Send as specific field }; try {