'use client' import { useState, useEffect, useCallback } from 'react' import { useRouter, useParams } from 'next/navigation' import { useToast } from '@/components/ui/Toast' import { ArrowLeft, FileText, Download, Eye, Target, Ban, File, Building2, Calendar, Clock, ChevronRight, Loader2 } from 'lucide-react' import { ResponsiveLayout } from '@/components/layout/ResponsiveLayout' import { Modal } from '@/components/ui/Modal' import { Button } from '@/components/ui/Button' import { api } from '@/lib/api' import { USE_MOCK } from '@/contexts/AuthContext' import type { BriefResponse } from '@/types/brief' import type { TaskResponse } from '@/types/task' // 代理商Brief文档类型 type AgencyBriefFile = { id: string name: string size: string uploadedAt: string description?: string url?: string } // 页面视图模型 type BriefViewModel = { taskName: string agencyName: string brandName: string deadline: string createdAt: string files: AgencyBriefFile[] sellingPoints: { id: string; content: string; required: boolean }[] blacklistWords: { id: string; word: string; reason: string }[] contentRequirements: string[] } // 模拟任务数据 const mockTaskInfo = { id: 'task-001', taskName: 'XX品牌618推广', agencyName: '星辰传媒', brandName: 'XX护肤品牌', deadline: '2026-06-18', createdAt: '2026-02-08', } // 模拟代理商Brief数据 const mockAgencyBrief = { files: [ { id: 'af1', name: '达人拍摄指南.pdf', size: '1.5MB', uploadedAt: '2026-02-02', description: '详细的拍摄流程和注意事项' }, { id: 'af2', name: '产品卖点话术.docx', size: '800KB', uploadedAt: '2026-02-02', description: '推荐使用的话术和表达方式' }, { id: 'af3', name: '品牌视觉参考.pdf', size: '3.2MB', uploadedAt: '2026-02-02', description: '视觉风格和拍摄参考示例' }, { id: 'af4', name: '产品素材包.zip', size: '15.6MB', uploadedAt: '2026-02-02', description: '产品图片、视频素材等' }, ] as AgencyBriefFile[], sellingPoints: [ { id: 'sp1', content: 'SPF50+ PA++++', required: true }, { id: 'sp2', content: '轻薄质地,不油腻', required: true }, { id: 'sp3', content: '延展性好,易推开', required: false }, { id: 'sp4', content: '适合敏感肌', required: false }, { id: 'sp5', content: '夏日必备防晒', required: true }, ], blacklistWords: [ { id: 'bw1', word: '最好', reason: '绝对化用语' }, { id: 'bw2', word: '第一', reason: '绝对化用语' }, { id: 'bw3', word: '神器', reason: '夸大宣传' }, { id: 'bw4', word: '完美', reason: '绝对化用语' }, { id: 'bw5', word: '100%', reason: '虚假宣传' }, ], contentRequirements: [ '视频时长:60-90秒', '需展示产品质地和使用效果', '需在户外或阳光下拍摄', '需提及产品核心卖点', ], } function buildMockViewModel(): BriefViewModel { return { taskName: mockTaskInfo.taskName, agencyName: mockTaskInfo.agencyName, brandName: mockTaskInfo.brandName, deadline: mockTaskInfo.deadline, createdAt: mockTaskInfo.createdAt, files: mockAgencyBrief.files, sellingPoints: mockAgencyBrief.sellingPoints, blacklistWords: mockAgencyBrief.blacklistWords, contentRequirements: mockAgencyBrief.contentRequirements, } } function buildViewModelFromAPI(task: TaskResponse, brief: BriefResponse): BriefViewModel { // 优先显示代理商上传的文档,没有则降级到品牌方附件 const agencyAtts = brief.agency_attachments ?? [] const brandAtts = brief.attachments ?? [] const sourceAtts = agencyAtts.length > 0 ? agencyAtts : brandAtts const files: AgencyBriefFile[] = sourceAtts.map((att, idx) => ({ id: att.id || `att-${idx}`, name: att.name, size: att.size || '', uploadedAt: brief.updated_at?.split('T')[0] || '', description: undefined, url: att.url, })) // Map selling points const sellingPoints = (brief.selling_points ?? []).map((sp, idx) => ({ id: `sp-${idx}`, content: sp.content, required: sp.required ?? (sp.priority === 'core'), })) // Map blacklist words const blacklistWords = (brief.blacklist_words ?? []).map((bw, idx) => ({ id: `bw-${idx}`, word: bw.word, reason: bw.reason, })) // Build content requirements const contentRequirements: string[] = [] if (brief.min_duration != null || brief.max_duration != null) { const minStr = brief.min_duration != null ? `${brief.min_duration}` : '?' const maxStr = brief.max_duration != null ? `${brief.max_duration}` : '?' contentRequirements.push(`视频时长:${minStr}-${maxStr}秒`) } if (brief.other_requirements) { contentRequirements.push(brief.other_requirements) } return { taskName: task.name, agencyName: task.agency.name, brandName: task.project.brand_name || task.project.name, deadline: '', // backend task has no deadline field yet createdAt: task.created_at.split('T')[0], files, sellingPoints, blacklistWords, contentRequirements, } } // 骨架屏 function BriefSkeleton() { return (
查看任务要求和Brief文档
代理商
{viewModel.agencyName}
品牌方
{viewModel.brandName}
分配时间
{viewModel.createdAt}
截止日期
{viewModel.deadline}
{file.name}
{file.size}
{file.description && ({file.description}
)}必选卖点(必须在内容中提及)
可选卖点(建议提及)
文件预览区域
实际开发中将嵌入文件预览组件