'use client'
import { useState, useEffect, useCallback } from 'react'
import Link from 'next/link'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
import { Button } from '@/components/ui/Button'
import { SuccessTag, PendingTag, WarningTag, ErrorTag } from '@/components/ui/Tag'
import { useToast } from '@/components/ui/Toast'
import {
FileText,
Video,
Search,
Filter,
Clock,
User,
Building,
ChevronRight,
AlertTriangle,
Download,
Eye,
File,
MessageSquareWarning,
Loader2,
} from 'lucide-react'
import { Modal } from '@/components/ui/Modal'
import { getPlatformInfo } from '@/lib/platforms'
import { api } from '@/lib/api'
import { USE_MOCK } from '@/contexts/AuthContext'
import type { TaskResponse } from '@/types/task'
// ==================== Mock 数据 ====================
const mockScriptTasks = [
{
id: 'script-001',
title: '夏日护肤推广脚本',
fileName: '夏日护肤推广_脚本v2.docx',
fileSize: '245 KB',
creatorName: '小美护肤',
agencyName: '星耀传媒',
projectName: 'XX品牌618推广',
platform: 'douyin',
aiScore: 88,
submittedAt: '2026-02-06 14:30',
hasHighRisk: false,
agencyApproved: true,
isAppeal: false,
},
{
id: 'script-002',
title: '新品口红试色脚本',
fileName: '口红试色_脚本v1.docx',
fileSize: '312 KB',
creatorName: '美妆Lisa',
agencyName: '创意无限',
projectName: 'XX品牌618推广',
platform: 'xiaohongshu',
aiScore: 72,
submittedAt: '2026-02-06 12:15',
hasHighRisk: true,
agencyApproved: true,
isAppeal: true,
appealReason: '已修改违规用词,请求品牌方重新审核',
},
]
const mockVideoTasks = [
{
id: 'video-001',
title: '夏日护肤推广',
fileName: '夏日护肤_成片v2.mp4',
fileSize: '128 MB',
creatorName: '小美护肤',
agencyName: '星耀传媒',
projectName: 'XX品牌618推广',
platform: 'douyin',
aiScore: 85,
duration: '02:15',
submittedAt: '2026-02-06 15:00',
hasHighRisk: false,
agencyApproved: true,
isAppeal: false,
},
{
id: 'video-002',
title: '新品口红试色',
fileName: '口红试色_终版.mp4',
fileSize: '256 MB',
creatorName: '美妆Lisa',
agencyName: '创意无限',
projectName: 'XX品牌618推广',
platform: 'xiaohongshu',
aiScore: 68,
duration: '03:42',
submittedAt: '2026-02-06 13:45',
hasHighRisk: true,
agencyApproved: true,
isAppeal: true,
appealReason: '已按要求重新剪辑,删除了争议片段,请求终审',
},
{
id: 'video-003',
title: '健身器材开箱',
fileName: '健身器材_开箱v3.mp4',
fileSize: '198 MB',
creatorName: '健身教练王',
agencyName: '美妆达人MCN',
projectName: 'XX运动品牌',
platform: 'bilibili',
aiScore: 92,
duration: '04:20',
submittedAt: '2026-02-06 11:30',
hasHighRisk: false,
agencyApproved: true,
isAppeal: false,
},
]
// ==================== 类型定义 ====================
interface UITask {
id: string
title: string
fileName: string
fileSize: string
creatorName: string
agencyName: string
projectName: string
platform: string
aiScore: number
submittedAt: string
hasHighRisk: boolean
agencyApproved: boolean
isAppeal: boolean
appealReason?: string
duration?: string
}
// ==================== 映射函数 ====================
/**
* 将后端 TaskResponse 映射为 UI 任务格式
*/
function mapTaskToUI(task: TaskResponse, type: 'script' | 'video'): UITask {
const isScript = type === 'script'
// AI 评分:脚本用 script_ai_score,视频用 video_ai_score
const aiScore = isScript
? (task.script_ai_score ?? 0)
: (task.video_ai_score ?? 0)
// AI 审核结果中检测是否有高风险(severity === 'high')
const aiResult = isScript ? task.script_ai_result : task.video_ai_result
const hasHighRisk = aiResult?.violations?.some(v => v.severity === 'high') ?? false
// 代理商审核状态
const agencyStatus = isScript ? task.script_agency_status : task.video_agency_status
const agencyApproved = agencyStatus === 'passed' || agencyStatus === 'force_passed'
// 文件名
const fileName = isScript
? (task.script_file_name ?? '未上传脚本')
: (task.video_file_name ?? '未上传视频')
// 视频时长:后端返回秒数,转为 mm:ss 格式
let duration: string | undefined
if (!isScript && task.video_duration) {
const minutes = Math.floor(task.video_duration / 60)
const seconds = task.video_duration % 60
duration = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
}
// 格式化提交时间
const submittedAt = formatDateTime(task.updated_at)
// 平台信息:从项目获取
const platform = task.project.platform || ''
return {
id: task.id,
title: `${task.project.name} · ${task.name}`,
fileName,
fileSize: isScript ? '--' : '--',
creatorName: task.creator.name,
agencyName: task.agency.name,
projectName: task.project.name,
platform,
aiScore,
submittedAt,
hasHighRisk,
agencyApproved,
isAppeal: task.is_appeal,
appealReason: task.appeal_reason ?? undefined,
duration,
}
}
/**
* 格式化日期时间为 YYYY-MM-DD HH:mm
*/
function formatDateTime(isoString: string): string {
try {
const d = new Date(isoString)
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const hours = String(d.getHours()).padStart(2, '0')
const minutes = String(d.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
} catch {
return isoString
}
}
// ==================== 子组件 ====================
function ScoreTag({ score }: { score: number }) {
if (score >= 85) return
申诉理由
{task.appealReason}
{task.fileName}
{task.fileSize} {task.duration && ` · ${task.duration}`}
审核代理商提交的脚本和视频
暂无待审脚本
暂无待审视频
{previewTask.task.appealReason}
视频播放区域
实际开发中将嵌入视频播放器
脚本预览区域
实际开发中将嵌入文档预览组件