import { useEffect, useState, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import ReactEChartsCore from 'echarts-for-react/lib/core'; import * as echarts from 'echarts/core'; import { LineChart } from 'echarts/charts'; import { GridComponent, TooltipComponent } from 'echarts/components'; import { CanvasRenderer } from 'echarts/renderers'; import { useAuthStore } from '../store/auth'; import { profileApi } from '../lib/api'; import type { ProfileOverview, AdminRecord } from '../types'; import { showToast } from '../components/Toast'; import styles from './ProfilePage.module.css'; echarts.use([LineChart, GridComponent, TooltipComponent, CanvasRenderer]); export function ProfilePage() { const user = useAuthStore((s) => s.user); const logout = useAuthStore((s) => s.logout); const navigate = useNavigate(); const [overview, setOverview] = useState(null); const [records, setRecords] = useState([]); const [recordsTotal, setRecordsTotal] = useState(0); const [recordsPage, setRecordsPage] = useState(1); const [trendPeriod, setTrendPeriod] = useState<'7d' | '30d'>('7d'); const [loading, setLoading] = useState(true); const fetchOverview = useCallback(async () => { try { const { data } = await profileApi.getOverview(trendPeriod); setOverview(data); } catch { showToast('加载消费概览失败'); } }, [trendPeriod]); const fetchRecords = useCallback(async () => { try { const { data } = await profileApi.getRecords(recordsPage, 20); if (recordsPage === 1) { setRecords(data.results); } else { setRecords((prev) => [...prev, ...data.results]); } setRecordsTotal(data.total); } catch { showToast('加载消费记录失败'); } }, [recordsPage]); useEffect(() => { Promise.all([fetchOverview(), fetchRecords()]).finally(() => setLoading(false)); }, []); useEffect(() => { fetchOverview(); }, [fetchOverview]); useEffect(() => { fetchRecords(); }, [fetchRecords]); const handleLogout = () => { logout(); navigate('/login', { replace: true }); }; if (loading || !overview) { return (
); } const dailyPercent = overview.daily_seconds_limit > 0 ? (overview.daily_seconds_used / overview.daily_seconds_limit) * 100 : 0; const monthlyPercent = overview.monthly_seconds_limit > 0 ? (overview.monthly_seconds_used / overview.monthly_seconds_limit) * 100 : 0; const totalRemaining = Math.max(0, overview.monthly_seconds_limit - overview.total_seconds_used); const totalPercent = overview.monthly_seconds_limit > 0 ? (overview.total_seconds_used / overview.monthly_seconds_limit) * 100 : 0; const sparklineOption: echarts.EChartsCoreOption = { tooltip: { trigger: 'axis', backgroundColor: '#1e1e2a', borderColor: '#2a2a38', textStyle: { color: '#e2e8f0', fontSize: 12 }, }, grid: { left: 0, right: 0, top: 5, bottom: 0, containLabel: false }, xAxis: { type: 'category', show: false, data: overview.daily_trend.map((d) => d.date.slice(5)) }, yAxis: { type: 'value', show: false }, series: [{ type: 'line', data: overview.daily_trend.map((d) => d.seconds), smooth: true, symbol: 'none', lineStyle: { color: '#00b8e6', width: 2 }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: 'rgba(0, 184, 230, 0.3)' }, { offset: 1, color: 'rgba(0, 184, 230, 0.02)' }, ]), }, }], }; const statusMap: Record = { queued: '排队中', processing: '生成中', completed: '已完成', failed: '失败' }; return (

个人中心

{user?.username}
{/* Quota warning */} {dailyPercent >= 80 && dailyPercent < 100 && (
今日额度已使用 {dailyPercent.toFixed(0)}%,请合理使用
)} {dailyPercent >= 100 && (
今日额度已用完,请明天再试
)} {/* Consumption Overview */}

消费概览

总额度
已消耗: {overview.total_seconds_used.toLocaleString()}s / {overview.monthly_seconds_limit.toLocaleString()}s
80 ? (totalPercent >= 100 ? 'var(--color-danger)' : 'var(--color-warning)') : 'var(--color-primary)', }} />
剩余 {totalRemaining.toLocaleString()}s
今日额度
已用: {overview.daily_seconds_used.toLocaleString()}s / {overview.daily_seconds_limit.toLocaleString()}s
80 ? (dailyPercent >= 100 ? 'var(--color-danger)' : 'var(--color-warning)') : 'var(--color-primary)', }} />
{dailyPercent.toFixed(1)}%
本月额度
已用: {overview.monthly_seconds_used.toLocaleString()}s / {overview.monthly_seconds_limit.toLocaleString()}s
80 ? 'var(--color-warning)' : 'var(--color-primary)', }} />
{monthlyPercent.toFixed(1)}%
{/* Consumption Trend */}

消费趋势

{/* Consumption Records */}

消费记录

{records.length === 0 ? (
暂无记录
) : ( records.map((r) => (
{new Date(r.created_at).toLocaleString('zh-CN')}
{r.prompt || '-'}
{r.seconds_consumed.toLocaleString()}s {r.mode === 'universal' ? '全能参考' : '首尾帧'} {statusMap[r.status]}
)) )}
{records.length < recordsTotal && ( )}
); }