feat: 在首页新增每日洞察卡,集成AI服务提供个性化财务分析。
This commit is contained in:
@@ -13,6 +13,14 @@ interface DailyInsightCardProps {
|
||||
maxTransaction?: { note?: string; amount: number };
|
||||
lastWeekSpend?: number;
|
||||
streakDays?: number;
|
||||
// 新增字段
|
||||
budgetRemaining?: number;
|
||||
daysRemaining?: number;
|
||||
weeklyTotal?: number;
|
||||
avgDailySpend?: number;
|
||||
top3Categories?: { name: string; amount: number }[];
|
||||
todayTransactionCount?: number;
|
||||
last7DaysSpend?: number[]; // 最近7天每日支出
|
||||
}
|
||||
|
||||
export const DailyInsightCard: React.FC<DailyInsightCardProps> = ({
|
||||
@@ -23,9 +31,16 @@ export const DailyInsightCard: React.FC<DailyInsightCardProps> = ({
|
||||
topCategory,
|
||||
maxTransaction,
|
||||
lastWeekSpend,
|
||||
streakDays
|
||||
streakDays,
|
||||
budgetRemaining,
|
||||
daysRemaining,
|
||||
weeklyTotal,
|
||||
avgDailySpend,
|
||||
top3Categories,
|
||||
todayTransactionCount,
|
||||
last7DaysSpend,
|
||||
}) => {
|
||||
const [aiData, setAiData] = useState<{ spending: string; budget: string } | null>(null);
|
||||
const [aiData, setAiData] = useState<{ spending: string; budget: string; emoji?: string; tip?: string } | null>(null);
|
||||
const [isAiLoading, setIsAiLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -50,7 +65,15 @@ export const DailyInsightCard: React.FC<DailyInsightCardProps> = ({
|
||||
amount: maxTransaction.amount
|
||||
} : undefined,
|
||||
lastWeekSpend,
|
||||
streakDays
|
||||
streakDays,
|
||||
// 新增数据
|
||||
budgetRemaining,
|
||||
daysRemaining: daysRemaining ?? (daysInMonth - today),
|
||||
weeklyTotal,
|
||||
avgDailySpend,
|
||||
top3Categories,
|
||||
todayTransactionCount,
|
||||
last7DaysSpend,
|
||||
});
|
||||
setAiData(result);
|
||||
} catch (e) {
|
||||
@@ -62,7 +85,7 @@ export const DailyInsightCard: React.FC<DailyInsightCardProps> = ({
|
||||
|
||||
const timer = setTimeout(fetchAI, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [todaySpend, yesterdaySpend, monthlyBudget, monthlySpent, topCategory, maxTransaction, lastWeekSpend, streakDays]);
|
||||
}, [todaySpend, yesterdaySpend, monthlyBudget, monthlySpent, topCategory, maxTransaction, lastWeekSpend, streakDays, budgetRemaining, daysRemaining, weeklyTotal, avgDailySpend, top3Categories, todayTransactionCount, last7DaysSpend]);
|
||||
|
||||
const getSpendingInsight = (today: number, yesterday: number) => {
|
||||
if (aiData) return { text: <span>{aiData.spending}</span>, type: 'ai' };
|
||||
@@ -114,6 +137,7 @@ export const DailyInsightCard: React.FC<DailyInsightCardProps> = ({
|
||||
<div className="daily-insight__header">
|
||||
<Icon icon={aiData ? "solar:magic-stick-3-bold-duotone" : "solar:stars-minimalistic-bold-duotone"} width="20" />
|
||||
<span>{aiData ? 'AI 每日简报' : '每日简报'}</span>
|
||||
{aiData?.emoji && <span className="daily-insight__emoji">{aiData.emoji}</span>}
|
||||
{isAiLoading && !aiData && <span className="daily-insight__loading-badge">AI 思考中...</span>}
|
||||
</div>
|
||||
|
||||
@@ -130,12 +154,46 @@ export const DailyInsightCard: React.FC<DailyInsightCardProps> = ({
|
||||
<p className="daily-insight__text animate-fade-in">{spendingInsight.text}</p>
|
||||
</div>
|
||||
|
||||
{/* 7-Day Trend Chart */}
|
||||
{last7DaysSpend && last7DaysSpend.length > 0 && (
|
||||
<div className="daily-insight__section">
|
||||
<span className="daily-insight__title">近7天趋势</span>
|
||||
<div className="trend-chart">
|
||||
{last7DaysSpend.map((amount, index) => {
|
||||
const maxVal = Math.max(...last7DaysSpend, 1);
|
||||
const heightPercent = Math.max((amount / maxVal) * 100, 10); // Min 10% height
|
||||
const isToday = index === last7DaysSpend.length - 1;
|
||||
const isMax = amount === maxVal && amount > 0;
|
||||
|
||||
return (
|
||||
<div key={index} className="trend-bar-wrapper" title={`Day ${index - 6}: ¥${amount}`}>
|
||||
<div
|
||||
className={`trend-bar ${isToday ? 'trend-bar--today' : ''} ${isMax ? 'trend-bar--max' : ''}`}
|
||||
style={{ height: `${heightPercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="daily-insight__divider" />
|
||||
|
||||
<div className="daily-insight__section">
|
||||
<span className="daily-insight__title">预算风向标</span>
|
||||
<p className="daily-insight__text animate-fade-in">{budgetInsight.text}</p>
|
||||
</div>
|
||||
|
||||
{aiData?.tip && (
|
||||
<>
|
||||
<div className="daily-insight__divider" />
|
||||
<div className="daily-insight__tip">
|
||||
<Icon icon="solar:lightbulb-bolt-bold-duotone" width="16" />
|
||||
<span>{aiData.tip}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user