Your Name 4753626e5a feat: 完成代理商/品牌方前端及文档更新
代理商端前端:
- 新增达人管理页面(含任务申诉次数管理)
- 新增消息中心(含申诉次数申请审批)
- 新增 Brief 管理(列表、详情)
- 新增审核中心(脚本审核、视频审核)
- 新增数据报表页面

品牌方端前端:
- 优化首页仪表盘布局
- 新增项目管理(列表、详情、创建)
- 新增代理商管理页面
- 新增审核中心(脚本终审、视频终审)
- 新增系统设置页面

文档更新:
- 申诉次数改为按任务分配(每任务初始1次)
- 更新 PRD、FeatureSummary、User_Role_Interfaces 等文档
- 更新 UI 设计规范和开发计划

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 15:39:23 +08:00

299 lines
11 KiB
TypeScript

'use client'
import { useState } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
import { Button } from '@/components/ui/Button'
import {
BarChart3,
TrendingUp,
TrendingDown,
Download,
Calendar,
FileText,
Video,
Users,
CheckCircle,
XCircle,
Clock
} from 'lucide-react'
// 模拟数据
const mockStats = {
totalScripts: 156,
totalVideos: 128,
passRate: 85,
avgReviewTime: 4.2,
trend: {
scripts: '+12%',
videos: '+8%',
passRate: '+3%',
reviewTime: '-15%',
},
}
const mockProjectStats = [
{ name: 'XX品牌618推广', scripts: 45, videos: 38, passRate: 92 },
{ name: '新品口红系列', scripts: 32, videos: 28, passRate: 85 },
{ name: '护肤品秋季活动', scripts: 28, videos: 25, passRate: 78 },
{ name: 'XX运动品牌', scripts: 51, videos: 37, passRate: 88 },
]
const mockWeeklyData = [
{ day: '周一', submitted: 25, passed: 22, rejected: 3 },
{ day: '周二', submitted: 32, passed: 28, rejected: 4 },
{ day: '周三', submitted: 18, passed: 16, rejected: 2 },
{ day: '周四', submitted: 41, passed: 35, rejected: 6 },
{ day: '周五', submitted: 35, passed: 30, rejected: 5 },
{ day: '周六', submitted: 12, passed: 11, rejected: 1 },
{ day: '周日', submitted: 8, passed: 7, rejected: 1 },
]
const mockCreatorRanking = [
{ name: '小美护肤', passRate: 95, total: 28 },
{ name: '健身教练王', passRate: 92, total: 15 },
{ name: '美妆Lisa', passRate: 88, total: 22 },
{ name: '时尚达人', passRate: 82, total: 18 },
{ name: '美食探店', passRate: 78, total: 25 },
]
function StatCard({ title, value, unit, trend, icon: Icon, color }: {
title: string
value: number | string
unit?: string
trend?: string
icon: React.ElementType
color: string
}) {
const isPositive = trend?.startsWith('+') || trend?.startsWith('-') && title.includes('时间')
return (
<Card>
<CardContent className="py-4">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-text-secondary">{title}</p>
<div className="flex items-baseline gap-1 mt-1">
<span className={`text-2xl font-bold ${color}`}>{value}</span>
{unit && <span className="text-text-secondary">{unit}</span>}
</div>
{trend && (
<div className={`flex items-center gap-1 mt-1 text-xs ${isPositive ? 'text-accent-green' : 'text-accent-coral'}`}>
{isPositive ? <TrendingUp size={12} /> : <TrendingDown size={12} />}
{trend} vs
</div>
)}
</div>
<div className={`w-10 h-10 rounded-lg ${color.replace('text-', 'bg-')}/20 flex items-center justify-center`}>
<Icon size={20} className={color} />
</div>
</div>
</CardContent>
</Card>
)
}
export default function AgencyReportsPage() {
const [dateRange, setDateRange] = useState('week')
return (
<div className="space-y-6">
{/* 页面标题 */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-text-primary"></h1>
<p className="text-sm text-text-secondary mt-1"></p>
</div>
<div className="flex items-center gap-3">
<div className="flex items-center gap-1 p-1 bg-bg-elevated rounded-lg">
<button
type="button"
onClick={() => setDateRange('week')}
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
dateRange === 'week' ? 'bg-bg-card text-text-primary shadow-sm' : 'text-text-secondary'
}`}
>
</button>
<button
type="button"
onClick={() => setDateRange('month')}
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
dateRange === 'month' ? 'bg-bg-card text-text-primary shadow-sm' : 'text-text-secondary'
}`}
>
</button>
<button
type="button"
onClick={() => setDateRange('quarter')}
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
dateRange === 'quarter' ? 'bg-bg-card text-text-primary shadow-sm' : 'text-text-secondary'
}`}
>
</button>
</div>
<Button variant="secondary">
<Download size={16} />
</Button>
</div>
</div>
{/* 核心指标 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<StatCard
title="脚本审核量"
value={mockStats.totalScripts}
trend={mockStats.trend.scripts}
icon={FileText}
color="text-accent-indigo"
/>
<StatCard
title="视频审核量"
value={mockStats.totalVideos}
trend={mockStats.trend.videos}
icon={Video}
color="text-purple-400"
/>
<StatCard
title="通过率"
value={mockStats.passRate}
unit="%"
trend={mockStats.trend.passRate}
icon={CheckCircle}
color="text-accent-green"
/>
<StatCard
title="平均审核时长"
value={mockStats.avgReviewTime}
unit="小时"
trend={mockStats.trend.reviewTime}
icon={Clock}
color="text-orange-400"
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* 本周趋势 */}
<Card className="lg:col-span-2">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<BarChart3 size={18} className="text-blue-500" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{mockWeeklyData.map((day) => (
<div key={day.day} className="flex items-center gap-4">
<div className="w-12 text-sm text-text-secondary font-medium">{day.day}</div>
<div className="flex-1">
<div className="flex h-6 rounded-full overflow-hidden bg-bg-elevated">
<div
className="bg-accent-green transition-all"
style={{ width: `${(day.passed / day.submitted) * 100}%` }}
/>
<div
className="bg-accent-coral transition-all"
style={{ width: `${(day.rejected / day.submitted) * 100}%` }}
/>
</div>
</div>
<div className="w-24 text-right text-sm">
<span className="text-accent-green font-medium">{day.passed}</span>
<span className="text-text-tertiary"> / </span>
<span className="text-text-secondary">{day.submitted}</span>
</div>
</div>
))}
</div>
<div className="flex gap-6 mt-4 text-sm">
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-accent-green rounded" />
<span className="text-text-secondary"></span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-accent-coral rounded" />
<span className="text-text-secondary"></span>
</div>
</div>
</CardContent>
</Card>
{/* 达人排名 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Users size={18} className="text-accent-indigo" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{mockCreatorRanking.map((creator, index) => (
<div key={creator.name} className="flex items-center gap-3 p-3 rounded-lg bg-bg-elevated">
<div className={`w-7 h-7 rounded-full flex items-center justify-center text-sm font-bold ${
index === 0 ? 'bg-yellow-500/20 text-yellow-400' :
index === 1 ? 'bg-gray-500/20 text-gray-400' :
index === 2 ? 'bg-orange-500/20 text-orange-400' : 'bg-bg-page text-text-tertiary'
}`}>
{index + 1}
</div>
<div className="flex-1 min-w-0">
<div className="font-medium text-text-primary truncate">{creator.name}</div>
<div className="text-xs text-text-tertiary">{creator.total} </div>
</div>
<div className={`font-bold ${creator.passRate >= 90 ? 'text-accent-green' : creator.passRate >= 80 ? 'text-accent-indigo' : 'text-orange-400'}`}>
{creator.passRate}%
</div>
</div>
))}
</CardContent>
</Card>
</div>
{/* 项目统计 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<table className="w-full">
<thead>
<tr className="border-b border-border-subtle text-left text-sm text-text-secondary">
<th className="pb-3 font-medium"></th>
<th className="pb-3 font-medium text-center"></th>
<th className="pb-3 font-medium text-center"></th>
<th className="pb-3 font-medium text-center"></th>
<th className="pb-3 font-medium"></th>
</tr>
</thead>
<tbody>
{mockProjectStats.map((project) => (
<tr key={project.name} className="border-b border-border-subtle last:border-0">
<td className="py-4 font-medium text-text-primary">{project.name}</td>
<td className="py-4 text-center text-text-secondary">{project.scripts}</td>
<td className="py-4 text-center text-text-secondary">{project.videos}</td>
<td className="py-4 text-center">
<span className={`font-medium ${project.passRate >= 90 ? 'text-accent-green' : project.passRate >= 80 ? 'text-accent-indigo' : 'text-orange-400'}`}>
{project.passRate}%
</span>
</td>
<td className="py-4">
<div className="w-full h-2 bg-bg-elevated rounded-full overflow-hidden">
<div
className={`h-full ${project.passRate >= 90 ? 'bg-accent-green' : project.passRate >= 80 ? 'bg-accent-indigo' : 'bg-orange-400'}`}
style={{ width: `${project.passRate}%` }}
/>
</div>
</td>
</tr>
))}
</tbody>
</table>
</CardContent>
</Card>
</div>
)
}