"use client" import * as React from "react" import Link from "next/link" import { motion, useAnimation } from "motion/react" import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, type CarouselApi } from "@/components/ui/carousel" import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { Info } from "lucide-react" import { cn } from "@/lib/utils" import { PayLevel, User } from "@/lib/services/auth" import { useUser } from "@/contexts/user-context" import services from "@/lib/services" import type { UserPayConfig } from "@/lib/services/admin" /** * 等级配置 */ interface LevelConfig { level: PayLevel name: string nameEn: string minScore: number maxScore: number | null gradient: string textColor: string metalEffect?: boolean dailyLimit: number | null feeRate: number | string scoreRate: number | string } /** 等级视觉样式配置(不包含业务数据) */ const LEVEL_STYLES = [ { level: PayLevel.Ordinary, name: "普通会员", nameEn: "ORDINARY MEMBER", gradient: "from-[#1e3a8a] via-[#2563eb] to-[#1d4ed8] shadow-lg shadow-blue-900/10", textColor: "text-white/95", }, { level: PayLevel.Gold, name: "黄金会员", nameEn: "GOLD MEMBER", gradient: "from-[#FDFCF0] via-[#FCE38A] to-[#B45309] shadow-lg shadow-amber-500/10", textColor: "text-[#854D0E]", metalEffect: true, }, { level: PayLevel.WhiteGold, name: "白金会员", nameEn: "PLATINUM MEMBER", gradient: "from-[#F8FAFC] via-[#E2E8F0] to-[#94A3B8] shadow-lg shadow-slate-500/10", textColor: "text-slate-800", metalEffect: true, }, { level: PayLevel.BlackGold, name: "黑金会员", nameEn: "BLACK MEMBER", gradient: "from-[#020617] via-[#1e293b] to-[#020617] shadow-lg shadow-black/20", textColor: "text-[#FFE5B4]", metalEffect: true, }, ] as const /** 合并 API 数据和视觉样式 */ function mergeLevelConfigs(apiConfigs: UserPayConfig[]): LevelConfig[] { return LEVEL_STYLES.map(style => { const apiConfig = apiConfigs.find(c => c.level === style.level) if (apiConfig) { return { ...style, minScore: apiConfig.min_score, maxScore: apiConfig.max_score, dailyLimit: apiConfig.daily_limit, feeRate: apiConfig.fee_rate, scoreRate: apiConfig.score_rate, } } return { ...style, minScore: 0, maxScore: null, dailyLimit: null, feeRate: 0, scoreRate: 0, } }) } function getLevelConfig(score: number, configs: LevelConfig[]): LevelConfig { if (configs.length === 0) { return { level: PayLevel.Ordinary, name: "加载中...", nameEn: "LOADING...", gradient: "from-blue-600 via-blue-500 to-blue-700", textColor: "text-white", minScore: 0, maxScore: null, dailyLimit: null, feeRate: 0, scoreRate: 0, } } for (let i = configs.length - 1; i >= 0; i--) { if (score >= configs[i].minScore) { return configs[i] } } return configs[0] } function MembershipCard({ config, user, score, currentLevel, isActive, levelConfigs, }: { config: LevelConfig user: User score: number currentLevel: LevelConfig isActive: boolean levelConfigs: LevelConfig[] }) { const isAccessible = levelConfigs.findIndex(l => l.level === config.level) <= levelConfigs.findIndex(l => l.level === currentLevel.level) const isDarkCard = config.level === PayLevel.BlackGold || config.level === PayLevel.Ordinary return (
{config.metalEffect && ( <>
)}
{config.nameEn}
{config.name}
平台分数
{score.toLocaleString()}
LINUX DO Credit
分数范围
{config.minScore < 0 ? "0.00" : config.minScore.toLocaleString()} - {config.maxScore?.toLocaleString() || "∞"}
{isAccessible ? ( {user?.nickname?.slice(0, 2)} ) : (
未获得
)}
) } function getPayLevelLabel(level: PayLevel): string { switch (level) { case PayLevel.BlackGold: return "黑金会员" case PayLevel.WhiteGold: return "白金会员" case PayLevel.Gold: return "黄金会员" case PayLevel.Ordinary: return "普通会员" default: return "未知等级" } } export function ProfileMain() { const { user, loading, getTrustLevelLabel } = useUser() const [api, setApi] = React.useState() const [current, setCurrent] = React.useState(0) const [levelConfigs, setLevelConfigs] = React.useState([]) const controls = useAnimation() const isAnimatingRef = React.useRef(false) const handleAvatarClick = () => { if (isAnimatingRef.current) return isAnimatingRef.current = true controls.start({ rotate: [0, -20, 20, -20, 20, 0], transition: { duration: 0.5, ease: "easeInOut" } }) setTimeout(() => { isAnimatingRef.current = false }, 650) } const score = user?.pay_score ?? 0 const currentLevel = getLevelConfig(score, levelConfigs) React.useEffect(() => { async function fetchPayConfigs() { const configs = await services.config.getUserPayConfigs() setLevelConfigs(mergeLevelConfigs(configs)) } fetchPayConfigs() }, []) React.useEffect(() => { if (!api) return setCurrent(api.selectedScrollSnap()) api.on("select", () => setCurrent(api.selectedScrollSnap())) }, [api]) React.useEffect(() => { if (!api || levelConfigs.length === 0 || !currentLevel) return const scrollToCurrentLevel = () => { const currentLevelIndex = levelConfigs.findIndex( (config) => config.level === currentLevel.level ) if (currentLevelIndex >= 0) { api.scrollTo(currentLevelIndex, true) } } scrollToCurrentLevel() api.on("reInit", scrollToCurrentLevel) return () => { api.off("reInit", scrollToCurrentLevel) } }, [api, currentLevel, levelConfigs]) if (loading) { return (

个人资料

) } if (!user) { return (
未找到用户信息
) } return (
设置 个人资料

会员等级

{levelConfigs.map((config, index) => ( api?.scrollTo(index)} > ))}

基本信息

{(user.nickname || user.username).slice(0, 2)}
账户
@{user.username}
昵称
{user.nickname || '未设置'}
用户ID
{user.id}
信任等级
{getTrustLevelLabel(user.trust_level)}
平台等级
{getPayLevelLabel(user.pay_level)}
平台分数
{user.pay_score.toLocaleString()}
每日流转限额
{currentLevel.dailyLimit ? `${ currentLevel.dailyLimit.toLocaleString() } 积分` : '无限额'}
集市手续费率
{typeof currentLevel.feeRate === 'number' ? `${ (currentLevel.feeRate * 100).toFixed(2) }%` : `${ (parseFloat(currentLevel.feeRate as string) * 100).toFixed(2) }%`}
平台分数转化率
{typeof currentLevel.scoreRate === 'number' ? `${ (currentLevel.scoreRate * 100).toFixed(2) }%` : `${ (parseFloat(currentLevel.scoreRate as string) * 100).toFixed(2) }%`}
积分基准值

上一次从社区同步的积分基准值

{parseFloat(user.community_balance).toLocaleString()}
) }