import React, { useState, useMemo } from 'react'; import { useAuth } from '@/contexts/AuthContext'; import { profileAPI } from '@/api'; import { mockOrders, mockMembershipPlans, mockUserCoupons, mockApps, mockOperationLogs } from '@/mock/data'; import type { Order, UserCoupon, MarketApp, OperationLog } from '@/types'; import './ProfilePage.css'; // ===== Mock API Keys ===== interface ApiKey { id: string; prefix: string; fullKey: string; createdAt: string; status: 'active' | 'disabled'; } const mockApiKeys: ApiKey[] = [ { id: 'ak1', prefix: 'sk-aiflow-****a1b2', fullKey: 'sk-aiflow-u1-x8j3a1b2c3d4e5f6g7h8i9j0k1l2', createdAt: '2026-03-15T08:00:00Z', status: 'active' }, { id: 'ak2', prefix: 'sk-aiflow-****c3d4', fullKey: 'sk-aiflow-u1-y9k4c3d4e5f6g7h8i9j0k1l2m3n4', createdAt: '2026-04-20T10:00:00Z', status: 'active' }, { id: 'ak3', prefix: 'sk-aiflow-****e5f6', fullKey: 'sk-aiflow-u1-z0l5e5f6g7h8i9j0k1l2m3n4o5p6', createdAt: '2026-05-10T14:00:00Z', status: 'disabled' }, ]; // ===== SVG Icons ===== const SvgIcons = { // Password lock: ( ), phone: ( ), mail: ( ), log: ( ), order: ( ), member: ( ), coupon: ( ), apps: ( ), key: ( ), copy: ( ), check: ( ), eye: ( ), calendar: ( ), diamond: ( ), close: ( ), checkCircle: ( ), }; // ===== Tabs ===== type ProfileTab = 'security' | 'orders' | 'membership' | 'coupons' | 'apps' | 'apikeys'; interface TabDef { key: ProfileTab; label: string; icon: JSX.Element; } const tabs: TabDef[] = [ { key: 'security', label: '账号安全', icon: SvgIcons.lock }, { key: 'orders', label: '我的订单', icon: SvgIcons.order }, { key: 'membership', label: '我的会员', icon: SvgIcons.member }, { key: 'coupons', label: '我的优惠券', icon: SvgIcons.coupon }, { key: 'apps', label: '我的已购应用', icon: SvgIcons.apps }, { key: 'apikeys', label: 'API密钥管理', icon: SvgIcons.key }, ]; // ===== Helper ===== function formatDate(dateStr: string): string { const d = new Date(dateStr); return d.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } function formatShortDate(dateStr: string): string { const d = new Date(dateStr); return d.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }); } // ===== ProfilePage ===== const ProfilePage: React.FC = () => { const { user } = useAuth(); const [activeTab, setActiveTab] = useState('security'); // Security form states const [passwordForm, setPasswordForm] = useState({ old: '', newPass: '', confirm: '' }); const [bindForm, setBindForm] = useState({ type: 'phone' as 'phone' | 'email', value: '' }); // Order filter const [orderFilter, setOrderFilter] = useState('all'); // Coupon filter const [couponFilter, setCouponFilter] = useState('all'); // API Key const [apiKeys, setApiKeys] = useState(mockApiKeys); const [showKeyId, setShowKeyId] = useState(null); const [copiedId, setCopiedId] = useState(null); if (!user) return null; const memberPlan = mockMembershipPlans.find(p => p.id === user.membership?.planId); // User's purchased apps (via mockOrders -> app type orders) const purchasedAppOrders = useMemo(() => mockOrders.filter(o => o.userId === 'u1' && o.orderType === 'app' && o.status === 'paid'), []); const purchasedApps: MarketApp[] = useMemo(() => { return purchasedAppOrders .map(o => mockApps.find(a => a.name === o.itemName)) .filter((a): a is MarketApp => !!a); }, [purchasedAppOrders]); // Login logs (recent 5) const loginLogs = useMemo(() => mockOperationLogs.filter(l => l.userId === 'u1').slice(0, 5), []); // Filter orders const filteredOrders = useMemo(() => { const userOrders = mockOrders.filter(o => o.userId === 'u1'); return orderFilter === 'all' ? userOrders : userOrders.filter(o => o.status === orderFilter); }, [orderFilter]); // Filter coupons const filteredCoupons = useMemo(() => { return couponFilter === 'all' ? mockUserCoupons : mockUserCoupons.filter(c => c.status === couponFilter); }, [couponFilter]); const handleCopyKey = (key: ApiKey) => { navigator.clipboard.writeText(key.fullKey); setCopiedId(key.id); setTimeout(() => setCopiedId(null), 2000); }; const handleGenerateKey = () => { const newKey: ApiKey = { id: `ak${Date.now()}`, prefix: `sk-aiflow-****${Math.random().toString(36).slice(2, 6)}`, fullKey: `sk-aiflow-u1-${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`, createdAt: new Date().toISOString(), status: 'active', }; setApiKeys(prev => [newKey, ...prev]); }; const handleCopyPrefix = (key: ApiKey) => { navigator.clipboard.writeText(key.prefix); setCopiedId(key.id); setTimeout(() => setCopiedId(null), 2000); }; const handleToggleKeyStatus = (keyId: string) => { setApiKeys(prev => prev.map(k => k.id === keyId ? { ...k, status: k.status === 'active' ? 'disabled' as const : 'active' as const } : k )); }; const orderStatusLabels: Record = { all: '全部', pending: '待支付', paid: '已支付', refunded: '已退款', }; const orderStatusColors: Record = { pending: 'var(--color-warning)', paid: 'var(--color-success)', refunded: 'var(--color-text-tertiary)', }; const orderTypeLabels: Record = { membership: '会员', app: '应用', service: '服务', }; const couponStatusConfig: Record = { unused: { label: '未使用', color: 'var(--color-success)', bg: 'var(--color-success-light)' }, used: { label: '已使用', color: 'var(--color-text-tertiary)', bg: 'var(--color-bg-tertiary)' }, expired: { label: '已过期', color: 'var(--color-error)', bg: 'var(--color-error-light)' }, }; const getCouponDescription = (coupon: UserCoupon['coupon']) => { if (coupon.type === 'fixed') return `满${coupon.minAmount}减${coupon.value}`; return `${coupon.value}折券,满${coupon.minAmount}可用`; }; const getLevelGradient = (level: number) => { const gradients: Record = { 0: 'linear-gradient(135deg, #8B919E, #BFC4CE)', 2: 'linear-gradient(135deg, #5B5FED, #8185F5)', 3: 'linear-gradient(135deg, #E8922C, #F0A94C)', }; return gradients[level] || gradients[0]; }; return (
{/* Header */}
{user.username.charAt(0).toUpperCase()}

{user.username}

{user.membership && ( {user.membership.planName} )}
{user.membership && (

{SvgIcons.calendar} 会员到期:{formatShortDate(user.membership.expireAt)}

)}
{/* Body */}
{/* Left Tabs */} {/* Right Content */}
{activeTab === 'security' && ( )} {activeTab === 'orders' && ( )} {activeTab === 'membership' && ( )} {activeTab === 'coupons' && ( )} {activeTab === 'apps' && ( )} {activeTab === 'apikeys' && ( )}
); }; // ===== Security Content ===== interface SecurityContentProps { passwordForm: { old: string; newPass: string; confirm: string }; setPasswordForm: React.Dispatch>; bindForm: { type: 'phone' | 'email'; value: string }; setBindForm: React.Dispatch>; loginLogs: OperationLog[]; } const SecurityContent: React.FC = ({ passwordForm, setPasswordForm, bindForm, setBindForm, loginLogs }) => { const [pwdMsg, setPwdMsg] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const [pwdLoading, setPwdLoading] = useState(false); const handleChangePassword = async () => { setPwdMsg(null); if (!passwordForm.old.trim()) { setPwdMsg({ type: 'error', text: '请输入旧密码' }); return; } if (!passwordForm.newPass.trim() || passwordForm.newPass.length < 6) { setPwdMsg({ type: 'error', text: '新密码长度至少为6位' }); return; } if (passwordForm.newPass !== passwordForm.confirm) { setPwdMsg({ type: 'error', text: '两次输入的新密码不一致' }); return; } setPwdLoading(true); try { await profileAPI.updatePassword({ old_password: passwordForm.old, new_password: passwordForm.newPass, }); setPwdMsg({ type: 'success', text: '密码修改成功' }); setPasswordForm({ old: '', newPass: '', confirm: '' }); } catch (e: any) { setPwdMsg({ type: 'error', text: e.message || e.detail || '密码修改失败' }); } finally { setPwdLoading(false); } }; return (
{/* Change Password */}

{SvgIcons.lock} 修改密码

{pwdMsg && (
{pwdMsg.text}
)}
setPasswordForm(p => ({ ...p, old: e.target.value }))} />
setPasswordForm(p => ({ ...p, newPass: e.target.value }))} />
setPasswordForm(p => ({ ...p, confirm: e.target.value }))} />
{/* Bind Phone/Email */}

{SvgIcons.phone} 绑定手机 / 邮箱

setBindForm(f => ({ ...f, value: e.target.value }))} />
{/* Login Logs */}

{SvgIcons.log} 最近登录日志

{loginLogs.map(log => ( ))}
操作 目标 IP地址 时间
{log.action} {log.target} {log.ip} {formatDate(log.createdAt)}
); }; // ===== Orders Content ===== interface OrdersContentProps { orders: Order[]; filter: Order['status'] | 'all'; setFilter: React.Dispatch>; statusLabels: Record; statusColors: Record; typeLabels: Record; } const OrdersContent: React.FC = ({ orders, filter, setFilter, statusLabels, statusColors, typeLabels }) => { return (
{/* Filter Tabs */}
{(Object.keys(statusLabels) as (Order['status'] | 'all')[]).map(key => ( ))}
{/* Table */}
{orders.map(order => ( ))} {orders.length === 0 && ( )}
订单号 类型 商品名 金额 支付方式 状态 时间
{order.id} {typeLabels[order.orderType]} {order.itemName} ¥{order.amount.toFixed(2)} {order.paymentMethod === 'wechat' ? '微信支付' : '支付宝'} {statusLabels[order.status]} {formatDate(order.createdAt)}
暂无订单
); }; // ===== Membership Content ===== interface MembershipContentProps { user: NonNullable['user']>; memberPlan: typeof mockMembershipPlans[0] | undefined; } const MembershipContent: React.FC = ({ user, memberPlan }) => { const [autoRenew, setAutoRenew] = useState(user.membership?.autoRenew ?? false); // 使用 memberPlan 或从 user.membership 获取 level,判断是否付费会员 const effectiveLevel = memberPlan?.level ?? user.membership?.level ?? 0; const effectiveName = memberPlan?.name || user.membership?.planName || '免费版'; const hasPaidMembership = effectiveLevel > 0; return (
{SvgIcons.diamond}

{effectiveName}

{hasPaidMembership && ( Lv.{effectiveLevel} )}
{hasPaidMembership && user.membership && ( <> {memberPlan && (
每日可用Token {memberPlan.dailyFreeTokens.toLocaleString()}
会员到期 {formatShortDate(user.membership.expireAt)}
月费 ¥{memberPlan.price}/月
)} {!memberPlan && user.membership && (
会员到期 {formatShortDate(user.membership.expireAt)}
)} {memberPlan && (

会员权益

    {memberPlan.features.map((f, i) => (
  • {SvgIcons.checkCircle} {f}
  • ))}
)}
自动续费
)} {!hasPaidMembership && (

您当前使用的是免费版,升级会员享受更多权益

)}
); }; // ===== Coupons Content ===== interface CouponsContentProps { coupons: UserCoupon[]; filter: UserCoupon['status'] | 'all'; setFilter: React.Dispatch>; getDescription: (coupon: UserCoupon['coupon']) => string; statusConfig: Record; } const CouponsContent: React.FC = ({ coupons, filter, setFilter, getDescription, statusConfig }) => { const filterLabels: Record = { all: '全部', unused: '未使用', used: '已使用', expired: '已过期', }; return (
{(Object.keys(filterLabels) as (UserCoupon['status'] | 'all')[]).map(key => ( ))}
{coupons.map(uc => { const cfg = statusConfig[uc.status]; return (
{uc.coupon.code}
{getDescription(uc.coupon)}
有效期:{formatShortDate(uc.coupon.validFrom)} ~ {formatShortDate(uc.coupon.validTo)}
{cfg.label} {uc.status === 'unused' && ( )}
); })} {coupons.length === 0 && (
暂无优惠券
)}
); }; // ===== Apps Content ===== interface AppsContentProps { apps: MarketApp[]; purchasedOrders: Order[]; } const AppsContent: React.FC = ({ apps, purchasedOrders }) => { const appTypeLabels: Record = { skill: '技能', agent: '智能体', mcp: 'MCP', }; const findOrderDate = (appName: string) => { const order = purchasedOrders.find(o => o.itemName === appName); return order ? formatDate(order.createdAt) : ''; }; return (
{apps.map(app => (
{app.icon || app.name.charAt(0)}

{app.name}

{appTypeLabels[app.type] || app.type} {app.source === 'official' ? '官方' : '用户'}

{app.description}

{SvgIcons.calendar} {findOrderDate(app.name)}
))} {apps.length === 0 && (
暂未购买任何应用
)}
); }; // ===== API Keys Content ===== interface ApiKeysContentProps { apiKeys: ApiKey[]; showKeyId: string | null; setShowKeyId: React.Dispatch>; copiedId: string | null; onCopy: (key: ApiKey) => void; onCopyPrefix: (key: ApiKey) => void; onGenerate: () => void; onToggleStatus: (keyId: string) => void; } const ApiKeysContent: React.FC = ({ apiKeys, showKeyId, setShowKeyId, copiedId, onCopy, onCopyPrefix, onGenerate, onToggleStatus, }) => { return (

API密钥

使用API密钥访问AIFlow接口,请妥善保管您的密钥

密钥前缀 创建时间 状态 操作
{apiKeys.map(key => (
{key.prefix} {showKeyId === key.id && (
{key.fullKey}
)}
{formatDate(key.createdAt)} {key.status === 'active' ? '活跃' : '已禁用'}
))}
); }; export default ProfilePage;