'use client' import { useState, useEffect, useCallback } from 'react' import { useRouter, useParams } from 'next/navigation' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card' import { Button } from '@/components/ui/Button' import { Modal, ConfirmModal } from '@/components/ui/Modal' import { SuccessTag, WarningTag, ErrorTag, PendingTag } from '@/components/ui/Tag' import { ReviewSteps, getBrandReviewSteps } from '@/components/ui/ReviewSteps' import { useToast } from '@/components/ui/Toast' import { api } from '@/lib/api' import { USE_MOCK } from '@/contexts/AuthContext' import { ArrowLeft, FileText, CheckCircle, XCircle, AlertTriangle, User, Building, Clock, Eye, Download, Shield, MessageSquare, MessageSquareWarning, Loader2, } from 'lucide-react' import { FilePreview, FileInfoCard, FilePreviewModal, type FileInfo } from '@/components/ui/FilePreview' import type { TaskResponse } from '@/types/task' // Mock 脚本任务数据(USE_MOCK 模式使用) const mockScriptTask = { id: 'script-001', title: '夏日护肤推广脚本', creatorName: '小美护肤', agencyName: '星耀传媒', projectName: 'XX品牌618推广', submittedAt: '2026-02-06 14:30', aiScore: 88, status: 'brand_reviewing', file: { id: 'file-001', fileName: '夏日护肤推广_脚本v2.docx', fileSize: '245 KB', fileType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', fileUrl: '/demo/scripts/script-001.docx', uploadedAt: '2026-02-06 14:30', } as FileInfo, isAppeal: false, appealReason: '', scriptContent: { opening: '大家好!今天给大家分享一款超级好用的夏日护肤神器~', productIntro: '这款XX品牌的防晒霜,SPF50+,PA++++,真的是夏天出门必备!质地轻薄不油腻,涂上去清清爽爽的。', demo: '我先在手背上试一下,大家看,延展性特别好,轻轻一抹就推开了,完全不会搓泥。', closing: '夏天防晒真的很重要,姐妹们赶紧冲!链接在小黄车1号链接~', }, agencyReview: { reviewer: '张经理', result: 'approved', comment: '脚本整体符合要求,卖点覆盖完整,建议通过。', reviewedAt: '2026-02-06 15:00', }, aiAnalysis: { violations: [ { id: 'v1', type: '违禁词', content: '神器', suggestion: '建议替换为"好物"或"必备品"', severity: 'medium' }, ], complianceChecks: [ { item: '品牌名称正确', passed: true }, { item: 'SPF标注准确', passed: true }, { item: '无绝对化用语', passed: false, note: '"超级好用"建议修改' }, { item: '引导语规范', passed: true }, ], sellingPoints: [ { point: 'SPF50+ PA++++', covered: true }, { point: '轻薄质地', covered: true }, { point: '不油腻', covered: true }, { point: '延展性好', covered: true }, ], }, } // 从 TaskResponse 映射出页面所需的数据结构 function mapTaskToView(task: TaskResponse) { const violations = (task.script_ai_result?.violations || []).map((v, idx) => ({ id: `v-${idx}`, type: v.type, content: v.content, suggestion: v.suggestion, severity: v.severity, })) const softWarnings = (task.script_ai_result?.soft_warnings || []).map((w, idx) => ({ id: `w-${idx}`, type: w.type, content: w.content, suggestion: w.suggestion, })) const fileExtension = task.script_file_name?.split('.').pop()?.toLowerCase() || '' const mimeTypeMap: Record = { docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', doc: 'application/msword', pdf: 'application/pdf', txt: 'text/plain', rtf: 'application/rtf', } const agencyResult = task.script_agency_status || 'pending' const agencyResultLabel = agencyResult === 'passed' ? '建议通过' : agencyResult === 'rejected' ? '建议驳回' : '待审核' return { id: task.id, title: task.name, creatorName: task.creator.name, agencyName: task.agency.name, projectName: task.project.name, submittedAt: task.script_uploaded_at || task.created_at, aiScore: task.script_ai_score || 0, status: task.stage, file: { id: task.id, fileName: task.script_file_name || '未上传文件', fileSize: '', fileType: mimeTypeMap[fileExtension] || 'application/octet-stream', fileUrl: task.script_file_url || '', uploadedAt: task.script_uploaded_at || undefined, } as FileInfo, isAppeal: task.is_appeal, appealReason: task.appeal_reason || '', agencyReview: { reviewer: task.agency.name, result: agencyResult, resultLabel: agencyResultLabel, comment: task.script_agency_comment || '', reviewedAt: '', }, aiAnalysis: { violations, softWarnings, sellingPoints: [] as Array<{ point: string; covered: boolean }>, }, } } function ReviewProgressBar({ taskStatus }: { taskStatus: string }) { const steps = getBrandReviewSteps(taskStatus) const currentStep = steps.find(s => s.status === 'current') return (
审核流程 当前:{currentStep?.label || '品牌方终审'}
) } function LoadingSkeleton() { return (
) } export default function BrandScriptReviewPage() { const router = useRouter() const params = useParams() const toast = useToast() const taskId = params.id as string const [showApproveModal, setShowApproveModal] = useState(false) const [showRejectModal, setShowRejectModal] = useState(false) const [rejectReason, setRejectReason] = useState('') const [viewMode, setViewMode] = useState<'file' | 'parsed'>('file') const [showFilePreview, setShowFilePreview] = useState(false) const [loading, setLoading] = useState(!USE_MOCK) const [submitting, setSubmitting] = useState(false) const [taskData, setTaskData] = useState | null>(null) // 加载任务数据 const loadTask = useCallback(async () => { if (USE_MOCK) return try { setLoading(true) const response = await api.getTask(taskId) setTaskData(mapTaskToView(response)) } catch (err) { const message = err instanceof Error ? err.message : '加载任务失败' toast.error(message) } finally { setLoading(false) } }, [taskId, toast]) useEffect(() => { loadTask() }, [loadTask]) // USE_MOCK 模式下使用 mock 数据 const task = USE_MOCK ? { ...mockScriptTask, agencyReview: { ...mockScriptTask.agencyReview, resultLabel: '建议通过', }, aiAnalysis: { ...mockScriptTask.aiAnalysis, softWarnings: [] as Array<{ id: string; type: string; content: string; suggestion: string }>, }, } : taskData const handleApprove = async () => { if (USE_MOCK) { setShowApproveModal(false) toast.success('审核通过') router.push('/brand/review') return } try { setSubmitting(true) await api.reviewScript(taskId, { action: 'pass', comment: '' }) setShowApproveModal(false) toast.success('审核通过') router.push('/brand/review') } catch (err) { const message = err instanceof Error ? err.message : '操作失败' toast.error(message) } finally { setSubmitting(false) } } const handleReject = async () => { if (!rejectReason.trim()) { toast.error('请填写驳回原因') return } if (USE_MOCK) { setShowRejectModal(false) toast.success('已驳回') router.push('/brand/review') return } try { setSubmitting(true) await api.reviewScript(taskId, { action: 'reject', comment: rejectReason }) setShowRejectModal(false) toast.success('已驳回') router.push('/brand/review') } catch (err) { const message = err instanceof Error ? err.message : '操作失败' toast.error(message) } finally { setSubmitting(false) } } // 加载中 if (loading) { return } // 数据未加载到 if (!task) { return (

任务数据加载失败

) } return (
{/* 顶部导航 */}

{task.title}

{task.isAppeal && ( 申诉 )}
{task.creatorName} {task.agencyName} {task.submittedAt}
{/* 申诉理由 */} {task.isAppeal && task.appealReason && (

申诉理由

{task.appealReason}

)} {/* 审核流程进度条 */}
{/* 左侧:脚本内容 */}
{/* 文件信息卡片 */} {task.file.fileUrl && ( setShowFilePreview(true)} /> )} {viewMode === 'file' ? ( 文件预览 {task.file.fileUrl ? ( ) : (

暂无文件

)}
) : ( AI 解析内容 (AI 自动提取的结构化内容) {USE_MOCK && 'scriptContent' in task ? ( <>
开场白

{(task as typeof mockScriptTask).scriptContent.opening}

产品介绍

{(task as typeof mockScriptTask).scriptContent.productIntro}

使用演示

{(task as typeof mockScriptTask).scriptContent.demo}

结尾引导

{(task as typeof mockScriptTask).scriptContent.closing}

) : (

API 暂不支持解析内容预览,请切换到「原文件」查看

)}
)} {/* 代理商初审意见 */} 代理商初审意见 {task.agencyReview.comment ? (
{task.agencyReview.result === 'passed' || task.agencyReview.result === 'approved' ? ( ) : ( )}
{task.agencyReview.reviewer} {(task.agencyReview.result === 'passed' || task.agencyReview.result === 'approved') ? ( {task.agencyReview.resultLabel} ) : task.agencyReview.result === 'rejected' ? ( {task.agencyReview.resultLabel} ) : ( {task.agencyReview.resultLabel} )}

{task.agencyReview.comment}

{task.agencyReview.reviewedAt && (

{task.agencyReview.reviewedAt}

)}
) : (

暂无代理商审核意见

)}
{/* 右侧:AI 分析面板 */}
{/* AI 评分 */}
AI 综合评分 = 85 ? 'text-accent-green' : task.aiScore >= 70 ? 'text-yellow-400' : 'text-accent-coral'}`}> {task.aiScore}
{/* 违规检测 */} 违规检测 ({task.aiAnalysis.violations.length}) {task.aiAnalysis.violations.map((v) => (
{v.type}

{v.content}

{v.suggestion}

))} {task.aiAnalysis.violations.length === 0 && (

未发现违规内容

)}
{/* 软性提醒 */} {task.aiAnalysis.softWarnings && task.aiAnalysis.softWarnings.length > 0 && ( 软性提醒 ({task.aiAnalysis.softWarnings.length}) {task.aiAnalysis.softWarnings.map((w) => (
{w.type}

{w.content}

{w.suggestion}

))}
)} {/* 合规检查 - 仅 mock 模式显示 */} {USE_MOCK && 'complianceChecks' in task.aiAnalysis && ( 合规检查 {(task.aiAnalysis as typeof mockScriptTask.aiAnalysis).complianceChecks.map((check, idx) => (
{check.passed ? ( ) : ( )}
{check.item} {check.note && (

{check.note}

)}
))}
)} {/* 卖点覆盖 */} {task.aiAnalysis.sellingPoints.length > 0 && ( 卖点覆盖 {task.aiAnalysis.sellingPoints.map((sp, idx) => (
{sp.covered ? ( ) : ( )} {sp.point}
))}
)}
{/* 底部决策栏 */}
项目:{task.projectName}
{/* 通过确认弹窗 */} setShowApproveModal(false)} onConfirm={handleApprove} title="确认通过" message="确定要通过此脚本的审核吗?通过后达人将收到通知,可以开始拍摄视频。" confirmText="确认通过" /> {/* 驳回弹窗 */} setShowRejectModal(false)} title="驳回审核">

请填写驳回原因,达人将收到通知并根据您的反馈进行修改。