lovart3/components/greeting.tsx
2025-12-18 11:08:14 +08:00

125 lines
3.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { motion } from "framer-motion";
import { useSession } from "next-auth/react";
import { useEffect, useState } from "react";
type QuotaInfo = {
used: number;
limit: number | null;
remaining: number | null;
unlimited: boolean;
percentage: number;
};
export const Greeting = () => {
const { data: session } = useSession();
const [imageCount, setImageCount] = useState<number | null>(null);
const [quota, setQuota] = useState<QuotaInfo | null>(null);
// 获取用户名或邮箱前缀
const getUserDisplayName = () => {
if (session?.user?.name) {
return session.user.name;
}
if (session?.user?.email) {
// 如果没有名字,使用邮箱的前缀部分
return session.user.email.split('@')[0];
}
return "";
};
const displayName = getUserDisplayName();
// 获取用户生图统计数据和配额信息
useEffect(() => {
const fetchData = async () => {
try {
// 获取生图统计
const statsResponse = await fetch("/api/user/image-stats");
if (statsResponse.ok) {
const statsData = await statsResponse.json();
setImageCount(statsData.totalImages);
}
// 获取配额信息
const quotaResponse = await fetch("/api/user/image-quota");
if (quotaResponse.ok) {
const quotaData = await quotaResponse.json();
setQuota(quotaData);
}
} catch (error) {
console.error("Failed to fetch data:", error);
}
};
if (session?.user?.id) {
fetchData();
}
}, [session?.user?.id]);
// 获取进度条颜色
const getProgressColor = (percentage: number) => {
if (percentage >= 90) return "bg-red-500";
if (percentage >= 70) return "bg-yellow-500";
return "bg-green-500";
};
return (
<div
className="mx-auto mt-4 flex size-full max-w-3xl flex-col justify-center px-4 md:mt-16 md:px-8"
key="overview"
>
<motion.div
animate={{ opacity: 1, y: 0 }}
className="font-semibold text-xl md:text-2xl"
exit={{ opacity: 0, y: 10 }}
initial={{ opacity: 0, y: 10 }}
transition={{ delay: 0.5 }}
>
你好{displayName && `,${displayName}`},很高兴见到你
{imageCount !== null && imageCount > 0 && (
<span className="ml-2 text-base font-normal text-zinc-500">
(截止今日已为您处理 <span className="font-semibold text-green-600">{imageCount.toLocaleString()}</span> 张图)
</span>
)}
</motion.div>
<motion.div
animate={{ opacity: 1, y: 0 }}
className="text-xl text-zinc-500 md:text-2xl"
exit={{ opacity: 0, y: 10 }}
initial={{ opacity: 0, y: 10 }}
transition={{ delay: 0.6 }}
>
今天我能为你做些什么?
</motion.div>
{/* 配额进度条 - 仅当有限额时显示 */}
{quota && !quota.unlimited && quota.limit && (
<motion.div
animate={{ opacity: 1, y: 0 }}
initial={{ opacity: 0, y: 10 }}
transition={{ delay: 0.7 }}
className="mt-4"
>
<div className="text-sm text-zinc-500 mb-1">
本月出图:{quota.used}/{quota.limit} 张
{quota.remaining !== null && quota.remaining <= 10 && quota.remaining > 0 && (
<span className="ml-2 text-yellow-600">(剩余 {quota.remaining} 张)</span>
)}
{quota.remaining === 0 && (
<span className="ml-2 text-red-600">(配额已用完)</span>
)}
</div>
<div className="w-64 h-2 bg-zinc-200 dark:bg-zinc-700 rounded-full overflow-hidden">
<div
className={`h-full rounded-full transition-all ${getProgressColor(quota.percentage)}`}
style={{ width: `${Math.min(quota.percentage, 100)}%` }}
/>
</div>
</motion.div>
)}
</div>
);
};