品牌方功能: - 项目看板: 添加截止日期编辑功能 - 项目详情: 添加代理商管理、截止日期编辑、最近任务显示代理商 - 项目创建: 代理商选择支持搜索(名称/ID/公司名) - 代理商管理: 通过ID邀请、添加备注/分配项目/移除操作 - Brief配置: 新增项目级Brief和规则配置页面 - 系统设置: 完善账户安全(密码/2FA/邮箱/手机/设备管理)、数据导出、退出登录 代理商功能: - 个人中心: 新增代理商ID展示、公司信息(企业验证)、个人信息编辑 - 账户设置: 密码修改、手机/邮箱绑定、两步验证 - 通知设置: 分类型和渠道的通知开关 - 审核历史: 搜索筛选和统计展示 - 帮助反馈: FAQ分类搜索和客服联系 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
292 lines
11 KiB
TypeScript
292 lines
11 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { useRouter } from 'next/navigation'
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
|
|
import { Button } from '@/components/ui/Button'
|
|
import {
|
|
ArrowLeft,
|
|
History,
|
|
CheckCircle,
|
|
XCircle,
|
|
Search,
|
|
Filter,
|
|
FileText,
|
|
Video,
|
|
User,
|
|
Calendar,
|
|
Download
|
|
} from 'lucide-react'
|
|
|
|
// 审核历史记录类型
|
|
interface ReviewHistoryItem {
|
|
id: string
|
|
taskId: string
|
|
taskTitle: string
|
|
creatorName: string
|
|
contentType: 'script' | 'video'
|
|
result: 'approved' | 'rejected'
|
|
reason?: string
|
|
reviewedAt: string
|
|
projectName: string
|
|
}
|
|
|
|
// 模拟审核历史数据
|
|
const mockHistoryData: ReviewHistoryItem[] = [
|
|
{
|
|
id: 'h-001',
|
|
taskId: 'task-101',
|
|
taskTitle: '夏日护肤推广脚本',
|
|
creatorName: '小美护肤',
|
|
contentType: 'script',
|
|
result: 'approved',
|
|
reviewedAt: '2026-02-06 14:30',
|
|
projectName: 'XX品牌618推广',
|
|
},
|
|
{
|
|
id: 'h-002',
|
|
taskId: 'task-102',
|
|
taskTitle: '新品口红试色视频',
|
|
creatorName: '美妆Lisa',
|
|
contentType: 'video',
|
|
result: 'rejected',
|
|
reason: '背景音乐版权问题',
|
|
reviewedAt: '2026-02-06 11:20',
|
|
projectName: 'YY口红新品发布',
|
|
},
|
|
{
|
|
id: 'h-003',
|
|
taskId: 'task-103',
|
|
taskTitle: '健身器材推荐脚本',
|
|
creatorName: '健身教练王',
|
|
contentType: 'script',
|
|
result: 'approved',
|
|
reviewedAt: '2026-02-05 16:45',
|
|
projectName: 'ZZ运动品牌推广',
|
|
},
|
|
{
|
|
id: 'h-004',
|
|
taskId: 'task-104',
|
|
taskTitle: '美妆新品测评视频',
|
|
creatorName: '达人小红',
|
|
contentType: 'video',
|
|
result: 'rejected',
|
|
reason: '品牌调性不符',
|
|
reviewedAt: '2026-02-05 10:15',
|
|
projectName: 'XX品牌618推广',
|
|
},
|
|
{
|
|
id: 'h-005',
|
|
taskId: 'task-105',
|
|
taskTitle: '数码产品开箱脚本',
|
|
creatorName: '科技小哥',
|
|
contentType: 'script',
|
|
result: 'approved',
|
|
reviewedAt: '2026-02-04 15:30',
|
|
projectName: 'AA数码新品上市',
|
|
},
|
|
]
|
|
|
|
export default function AgencyReviewHistoryPage() {
|
|
const router = useRouter()
|
|
const [searchQuery, setSearchQuery] = useState('')
|
|
const [filterResult, setFilterResult] = useState<'all' | 'approved' | 'rejected'>('all')
|
|
const [filterType, setFilterType] = useState<'all' | 'script' | 'video'>('all')
|
|
|
|
// 筛选数据
|
|
const filteredHistory = mockHistoryData.filter(item => {
|
|
const matchesSearch = searchQuery === '' ||
|
|
item.taskTitle.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
item.creatorName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
item.projectName.toLowerCase().includes(searchQuery.toLowerCase())
|
|
const matchesResult = filterResult === 'all' || item.result === filterResult
|
|
const matchesType = filterType === 'all' || item.contentType === filterType
|
|
return matchesSearch && matchesResult && matchesType
|
|
})
|
|
|
|
// 统计
|
|
const approvedCount = mockHistoryData.filter(i => i.result === 'approved').length
|
|
const rejectedCount = mockHistoryData.filter(i => i.result === 'rejected').length
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* 顶部导航 */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<button
|
|
type="button"
|
|
onClick={() => router.back()}
|
|
className="p-2 rounded-lg hover:bg-bg-elevated transition-colors"
|
|
>
|
|
<ArrowLeft size={20} className="text-text-secondary" />
|
|
</button>
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-text-primary">审核历史</h1>
|
|
<p className="text-sm text-text-secondary mt-0.5">查看您的历史审核记录</p>
|
|
</div>
|
|
</div>
|
|
<Button variant="secondary">
|
|
<Download size={16} />
|
|
导出记录
|
|
</Button>
|
|
</div>
|
|
|
|
{/* 统计卡片 */}
|
|
<div className="grid grid-cols-3 gap-4">
|
|
<div className="p-4 rounded-xl bg-bg-card card-shadow">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-accent-indigo/15 flex items-center justify-center">
|
|
<History size={20} className="text-accent-indigo" />
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold text-text-primary">{mockHistoryData.length}</p>
|
|
<p className="text-sm text-text-secondary">总审核数</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="p-4 rounded-xl bg-bg-card card-shadow">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-accent-green/15 flex items-center justify-center">
|
|
<CheckCircle size={20} className="text-accent-green" />
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold text-accent-green">{approvedCount}</p>
|
|
<p className="text-sm text-text-secondary">已通过</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="p-4 rounded-xl bg-bg-card card-shadow">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-accent-coral/15 flex items-center justify-center">
|
|
<XCircle size={20} className="text-accent-coral" />
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold text-accent-coral">{rejectedCount}</p>
|
|
<p className="text-sm text-text-secondary">已驳回</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 搜索和筛选 */}
|
|
<div className="flex flex-wrap items-center gap-4">
|
|
<div className="relative flex-1 min-w-[240px] max-w-md">
|
|
<Search size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-tertiary" />
|
|
<input
|
|
type="text"
|
|
placeholder="搜索任务、达人或项目..."
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
className="w-full pl-10 pr-4 py-2.5 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-text-tertiary">结果:</span>
|
|
<div className="flex items-center gap-1 p-1 bg-bg-elevated rounded-lg">
|
|
{[
|
|
{ value: 'all', label: '全部' },
|
|
{ value: 'approved', label: '通过' },
|
|
{ value: 'rejected', label: '驳回' },
|
|
].map((tab) => (
|
|
<button
|
|
key={tab.value}
|
|
type="button"
|
|
onClick={() => setFilterResult(tab.value as typeof filterResult)}
|
|
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
|
|
filterResult === tab.value ? 'bg-bg-card text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary'
|
|
}`}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-text-tertiary">类型:</span>
|
|
<div className="flex items-center gap-1 p-1 bg-bg-elevated rounded-lg">
|
|
{[
|
|
{ value: 'all', label: '全部' },
|
|
{ value: 'script', label: '脚本' },
|
|
{ value: 'video', label: '视频' },
|
|
].map((tab) => (
|
|
<button
|
|
key={tab.value}
|
|
type="button"
|
|
onClick={() => setFilterType(tab.value as typeof filterType)}
|
|
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
|
|
filterType === tab.value ? 'bg-bg-card text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary'
|
|
}`}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 历史列表 */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<History size={18} className="text-accent-indigo" />
|
|
审核记录
|
|
<span className="ml-auto text-sm font-normal text-text-secondary">
|
|
共 {filteredHistory.length} 条
|
|
</span>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
{filteredHistory.length > 0 ? (
|
|
filteredHistory.map((item) => (
|
|
<div
|
|
key={item.id}
|
|
className="p-4 rounded-xl bg-bg-elevated hover:bg-bg-elevated/80 transition-colors"
|
|
>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className={`px-2 py-0.5 rounded text-xs font-medium ${
|
|
item.result === 'approved'
|
|
? 'bg-accent-green/15 text-accent-green'
|
|
: 'bg-accent-coral/15 text-accent-coral'
|
|
}`}>
|
|
{item.result === 'approved' ? '已通过' : '已驳回'}
|
|
</span>
|
|
<span className="flex items-center gap-1 text-xs text-text-tertiary">
|
|
{item.contentType === 'script' ? <FileText size={12} /> : <Video size={12} />}
|
|
{item.contentType === 'script' ? '脚本' : '视频'}
|
|
</span>
|
|
</div>
|
|
<h3 className="font-medium text-text-primary mb-1">{item.taskTitle}</h3>
|
|
<div className="flex items-center gap-4 text-sm text-text-secondary">
|
|
<span className="flex items-center gap-1">
|
|
<User size={14} />
|
|
{item.creatorName}
|
|
</span>
|
|
<span>{item.projectName}</span>
|
|
</div>
|
|
{item.reason && (
|
|
<p className="mt-2 text-sm text-accent-coral">驳回原因: {item.reason}</p>
|
|
)}
|
|
</div>
|
|
<div className="text-right">
|
|
<div className="flex items-center gap-1 text-sm text-text-tertiary">
|
|
<Calendar size={14} />
|
|
{item.reviewedAt}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="text-center py-12 text-text-tertiary">
|
|
<History size={48} className="mx-auto mb-4 opacity-50" />
|
|
<p>没有找到匹配的审核记录</p>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|