feat: 对接达人端和代理商端个人中心页面 API
- creator/profile: 使用 useAuth() 获取真实用户数据,dashboard API 获取统计 - agency/profile: 同上,使用 getAgencyDashboard() 获取代理商统计数据 - 两端退出登录改为调用 logout() 清除 token,替代原 TODO 占位 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3a6e25b5b1
commit
68dac332d4
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
CircleUser,
|
||||
@ -18,19 +18,23 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useToast } from '@/components/ui/Toast'
|
||||
import { useAuth } from '@/contexts/AuthContext'
|
||||
import { USE_MOCK } from '@/contexts/AuthContext'
|
||||
import { api } from '@/lib/api'
|
||||
import type { AgencyDashboard } from '@/types/dashboard'
|
||||
|
||||
// 代理商数据
|
||||
// Mock 数据(USE_MOCK 模式)
|
||||
const mockAgency = {
|
||||
name: '星辰传媒',
|
||||
initial: '星',
|
||||
agencyId: 'AG789012', // 代理商ID
|
||||
agencyId: 'AG789012',
|
||||
companyName: '上海星辰文化传媒有限公司',
|
||||
role: '官方认证代理商',
|
||||
stats: {
|
||||
creators: 156, // 管理达人数
|
||||
reviewed: 1280, // 累计审核数
|
||||
passRate: 88, // 通过率
|
||||
monthlyTasks: 45, // 本月任务
|
||||
creators: 156,
|
||||
reviewed: 1280,
|
||||
passRate: 88,
|
||||
monthlyTasks: 45,
|
||||
},
|
||||
}
|
||||
|
||||
@ -89,12 +93,48 @@ const menuItems = [
|
||||
// 代理商卡片组件
|
||||
function AgencyCard() {
|
||||
const toast = useToast()
|
||||
const { user } = useAuth()
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [stats, setStats] = useState({ creators: 0, totalTasks: 0, passRate: 0, pendingReview: 0 })
|
||||
|
||||
useEffect(() => {
|
||||
const loadStats = async () => {
|
||||
if (USE_MOCK) {
|
||||
setStats({
|
||||
creators: mockAgency.stats.creators,
|
||||
totalTasks: mockAgency.stats.reviewed,
|
||||
passRate: mockAgency.stats.passRate,
|
||||
pendingReview: mockAgency.stats.monthlyTasks,
|
||||
})
|
||||
return
|
||||
}
|
||||
try {
|
||||
const dashboard = await api.getAgencyDashboard()
|
||||
const totalPassed = dashboard.today_passed.script + dashboard.today_passed.video
|
||||
const totalPending = dashboard.pending_review.script + dashboard.pending_review.video
|
||||
setStats({
|
||||
creators: dashboard.total_creators,
|
||||
totalTasks: dashboard.total_tasks,
|
||||
passRate: dashboard.total_tasks > 0 ? Math.round((totalPassed / Math.max(totalPassed + totalPending, 1)) * 100) : 0,
|
||||
pendingReview: totalPending,
|
||||
})
|
||||
} catch {
|
||||
// 静默失败
|
||||
}
|
||||
}
|
||||
loadStats()
|
||||
}, [])
|
||||
|
||||
const displayName = USE_MOCK ? mockAgency.name : (user?.name || '代理商')
|
||||
const displayInitial = USE_MOCK ? mockAgency.initial : (user?.name?.[0] || '?')
|
||||
const displayAgencyId = USE_MOCK ? mockAgency.agencyId : (user?.agency_id || '--')
|
||||
const displayRole = USE_MOCK ? mockAgency.role : (user?.is_verified ? '认证代理商' : '代理商')
|
||||
const displayCompany = USE_MOCK ? mockAgency.companyName : (user?.tenant_name || '--')
|
||||
|
||||
// 复制代理商ID
|
||||
const handleCopyId = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(mockAgency.agencyId)
|
||||
await navigator.clipboard.writeText(displayAgencyId)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
} catch {
|
||||
@ -113,17 +153,17 @@ function AgencyCard() {
|
||||
background: 'linear-gradient(135deg, #6366F1 0%, #4F46E5 100%)',
|
||||
}}
|
||||
>
|
||||
<span className="text-[32px] font-bold text-white">{mockAgency.initial}</span>
|
||||
<span className="text-[32px] font-bold text-white">{displayInitial}</span>
|
||||
</div>
|
||||
{/* 代理商信息 */}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<span className="text-xl font-semibold text-text-primary">{mockAgency.name}</span>
|
||||
<span className="text-sm text-text-secondary">{mockAgency.role}</span>
|
||||
<span className="text-xl font-semibold text-text-primary">{displayName}</span>
|
||||
<span className="text-sm text-text-secondary">{displayRole}</span>
|
||||
{/* 代理商ID */}
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-xs text-text-tertiary">代理商ID:</span>
|
||||
<div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-bg-elevated">
|
||||
<span className="text-xs font-mono font-medium text-accent-indigo">{mockAgency.agencyId}</span>
|
||||
<span className="text-xs font-mono font-medium text-accent-indigo">{displayAgencyId}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyId}
|
||||
@ -147,7 +187,7 @@ function AgencyCard() {
|
||||
{/* 公司名称 */}
|
||||
<div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-bg-elevated">
|
||||
<Building2 size={16} className="text-text-tertiary" />
|
||||
<span className="text-sm text-text-secondary">{mockAgency.companyName}</span>
|
||||
<span className="text-sm text-text-secondary">{displayCompany}</span>
|
||||
</div>
|
||||
|
||||
{/* 统计数据 */}
|
||||
@ -155,24 +195,24 @@ function AgencyCard() {
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<Users size={14} className="text-accent-indigo" />
|
||||
<span className="text-xl font-bold text-text-primary">{mockAgency.stats.creators}</span>
|
||||
<span className="text-xl font-bold text-text-primary">{stats.creators}</span>
|
||||
</div>
|
||||
<span className="text-xs text-text-secondary">管理达人</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<FileCheck size={14} className="text-accent-blue" />
|
||||
<span className="text-xl font-bold text-text-primary">{mockAgency.stats.reviewed}</span>
|
||||
<span className="text-xl font-bold text-text-primary">{stats.totalTasks}</span>
|
||||
</div>
|
||||
<span className="text-xs text-text-secondary">累计审核</span>
|
||||
<span className="text-xs text-text-secondary">总任务数</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span className="text-xl font-bold text-accent-green">{mockAgency.stats.passRate}%</span>
|
||||
<span className="text-xl font-bold text-accent-green">{stats.passRate}%</span>
|
||||
<span className="text-xs text-text-secondary">通过率</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span className="text-xl font-bold text-accent-amber">{mockAgency.stats.monthlyTasks}</span>
|
||||
<span className="text-xs text-text-secondary">本月任务</span>
|
||||
<span className="text-xl font-bold text-accent-amber">{stats.pendingReview}</span>
|
||||
<span className="text-xs text-text-secondary">待审核</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -245,6 +285,7 @@ function LogoutCard({ onLogout }: { onLogout: () => void }) {
|
||||
|
||||
export default function AgencyProfilePage() {
|
||||
const router = useRouter()
|
||||
const { logout } = useAuth()
|
||||
|
||||
// 菜单项点击处理
|
||||
const handleMenuClick = (menuId: string) => {
|
||||
@ -264,7 +305,7 @@ export default function AgencyProfilePage() {
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
// TODO: 实际退出逻辑
|
||||
logout()
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
CircleUser,
|
||||
@ -17,12 +17,16 @@ import {
|
||||
import { ResponsiveLayout } from '@/components/layout/ResponsiveLayout'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useToast } from '@/components/ui/Toast'
|
||||
import { useAuth } from '@/contexts/AuthContext'
|
||||
import { USE_MOCK } from '@/contexts/AuthContext'
|
||||
import { api } from '@/lib/api'
|
||||
import type { CreatorDashboard } from '@/types/dashboard'
|
||||
|
||||
// 用户数据
|
||||
// Mock 数据(USE_MOCK 模式)
|
||||
const mockUser = {
|
||||
name: '李小红',
|
||||
initial: '李',
|
||||
creatorId: 'CR123456', // 达人ID
|
||||
creatorId: 'CR123456',
|
||||
role: '抖音达人 · 已认证',
|
||||
stats: {
|
||||
completed: 28,
|
||||
@ -86,12 +90,40 @@ const menuItems = [
|
||||
// 用户卡片组件
|
||||
function UserCard() {
|
||||
const toast = useToast()
|
||||
const { user } = useAuth()
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [stats, setStats] = useState({ completed: 0, inProgress: 0, totalTasks: 0 })
|
||||
|
||||
useEffect(() => {
|
||||
const loadStats = async () => {
|
||||
if (USE_MOCK) {
|
||||
setStats({ completed: mockUser.stats.completed, inProgress: mockUser.stats.inProgress, totalTasks: mockUser.stats.completed + mockUser.stats.inProgress })
|
||||
return
|
||||
}
|
||||
try {
|
||||
const dashboard = await api.getCreatorDashboard()
|
||||
setStats({
|
||||
completed: dashboard.completed,
|
||||
inProgress: dashboard.pending_script + dashboard.pending_video + dashboard.in_review,
|
||||
totalTasks: dashboard.total_tasks,
|
||||
})
|
||||
} catch {
|
||||
// 静默失败,保持默认值
|
||||
}
|
||||
}
|
||||
loadStats()
|
||||
}, [])
|
||||
|
||||
const displayName = USE_MOCK ? mockUser.name : (user?.name || '用户')
|
||||
const displayInitial = USE_MOCK ? mockUser.initial : (user?.name?.[0] || '?')
|
||||
const displayCreatorId = USE_MOCK ? mockUser.creatorId : (user?.creator_id || '--')
|
||||
const displayRole = USE_MOCK ? mockUser.role : (user?.is_verified ? '达人 · 已认证' : '达人')
|
||||
const passRate = stats.totalTasks > 0 ? Math.round((stats.completed / stats.totalTasks) * 100) : 0
|
||||
|
||||
// 复制达人ID
|
||||
const handleCopyId = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(mockUser.creatorId)
|
||||
await navigator.clipboard.writeText(displayCreatorId)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
} catch {
|
||||
@ -110,17 +142,17 @@ function UserCard() {
|
||||
background: 'linear-gradient(135deg, #6366F1 0%, #4F46E5 100%)',
|
||||
}}
|
||||
>
|
||||
<span className="text-[32px] font-bold text-white">{mockUser.initial}</span>
|
||||
<span className="text-[32px] font-bold text-white">{displayInitial}</span>
|
||||
</div>
|
||||
{/* 用户信息 */}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<span className="text-xl font-semibold text-text-primary">{mockUser.name}</span>
|
||||
<span className="text-sm text-text-secondary">{mockUser.role}</span>
|
||||
<span className="text-xl font-semibold text-text-primary">{displayName}</span>
|
||||
<span className="text-sm text-text-secondary">{displayRole}</span>
|
||||
{/* 达人ID */}
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-xs text-text-tertiary">达人ID:</span>
|
||||
<div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-bg-elevated">
|
||||
<span className="text-xs font-mono font-medium text-accent-indigo">{mockUser.creatorId}</span>
|
||||
<span className="text-xs font-mono font-medium text-accent-indigo">{displayCreatorId}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyId}
|
||||
@ -144,15 +176,15 @@ function UserCard() {
|
||||
{/* 统计数据 */}
|
||||
<div className="flex items-center justify-around pt-4 border-t border-border-subtle">
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span className="text-2xl font-bold text-text-primary">{mockUser.stats.completed}</span>
|
||||
<span className="text-2xl font-bold text-text-primary">{stats.completed}</span>
|
||||
<span className="text-xs text-text-secondary">完成任务</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span className="text-2xl font-bold text-accent-green">{mockUser.stats.passRate}%</span>
|
||||
<span className="text-2xl font-bold text-accent-green">{passRate}%</span>
|
||||
<span className="text-xs text-text-secondary">通过率</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span className="text-2xl font-bold text-accent-indigo">{mockUser.stats.inProgress}</span>
|
||||
<span className="text-2xl font-bold text-accent-indigo">{stats.inProgress}</span>
|
||||
<span className="text-xs text-text-secondary">进行中</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -226,6 +258,7 @@ function LogoutCard({ onLogout }: { onLogout: () => void }) {
|
||||
|
||||
export default function CreatorProfilePage() {
|
||||
const router = useRouter()
|
||||
const { logout } = useAuth()
|
||||
|
||||
// 菜单项点击处理
|
||||
const handleMenuClick = (menuId: string) => {
|
||||
@ -245,7 +278,7 @@ export default function CreatorProfilePage() {
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
// TODO: 实际退出逻辑
|
||||
logout()
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user