From 0bfedb95c882e088d5efb8cc90d3f1c17ccd8316 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 6 Feb 2026 18:53:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BA=E6=89=80=E6=9C=89=E7=BB=88?= =?UTF-8?q?=E7=AB=AF=E6=B7=BB=E5=8A=A0=E5=B9=B3=E5=8F=B0=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 frontend/lib/platforms.ts 共享平台配置模块 - 支持6个平台: 抖音、小红书、B站、快手、微博、微信视频号 - 品牌方终端: 项目看板、项目详情、终审台列表添加平台显示 - 代理商终端: 工作台概览、审核台、Brief配置、达人管理、 数据报表、消息中心、申诉处理添加平台显示 - 达人端: 任务列表添加平台显示 - 统一使用彩色头部条样式展示平台信息 Co-Authored-By: Claude Opus 4.5 --- frontend/app/agency/appeals/page.tsx | 68 +- frontend/app/agency/briefs/page.tsx | 74 +- frontend/app/agency/creators/page.tsx | 78 +- frontend/app/agency/messages/page.tsx | 20 +- frontend/app/agency/page.tsx | 88 +- frontend/app/agency/reports/page.tsx | 61 +- frontend/app/agency/review/page.tsx | 63 +- frontend/app/brand/agencies/page.tsx | 6 +- frontend/app/brand/briefs/page.tsx | 352 +++++- frontend/app/brand/final-review/page.tsx | 18 +- frontend/app/brand/page.tsx | 128 ++- frontend/app/brand/projects/[id]/page.tsx | 14 +- frontend/app/brand/projects/create/page.tsx | 50 +- frontend/app/brand/review/page.tsx | 86 +- frontend/app/brand/rules/page.tsx | 1007 ++++++++++++++--- frontend/app/brand/settings/page.tsx | 2 +- frontend/app/creator/page.tsx | 71 +- frontend/components/layout/DesktopLayout.tsx | 8 +- frontend/components/layout/MobileLayout.tsx | 8 +- .../components/layout/ResponsiveLayout.tsx | 8 +- frontend/components/ui/Modal.tsx | 12 +- frontend/lib/platforms.ts | 22 + 22 files changed, 1784 insertions(+), 460 deletions(-) create mode 100644 frontend/lib/platforms.ts diff --git a/frontend/app/agency/appeals/page.tsx b/frontend/app/agency/appeals/page.tsx index da59d68..a258692 100644 --- a/frontend/app/agency/appeals/page.tsx +++ b/frontend/app/agency/appeals/page.tsx @@ -17,6 +17,7 @@ import { FileText, Video } from 'lucide-react' +import { getPlatformInfo } from '@/lib/platforms' // 申诉状态类型 type AppealStatus = 'pending' | 'processing' | 'approved' | 'rejected' @@ -31,6 +32,7 @@ interface Appeal { taskTitle: string creatorId: string creatorName: string + platform: string type: AppealType contentType: 'script' | 'video' reason: string @@ -48,6 +50,7 @@ const mockAppeals: Appeal[] = [ taskTitle: '夏日护肤推广脚本', creatorId: 'creator-001', creatorName: '小美护肤', + platform: 'douyin', type: 'ai', contentType: 'script', reason: 'AI误判', @@ -61,6 +64,7 @@ const mockAppeals: Appeal[] = [ taskTitle: '新品口红试色', creatorId: 'creator-002', creatorName: '美妆Lisa', + platform: 'xiaohongshu', type: 'agency', contentType: 'video', reason: '审核标准不清晰', @@ -74,6 +78,7 @@ const mockAppeals: Appeal[] = [ taskTitle: '健身器材推荐', creatorId: 'creator-003', creatorName: '健身教练王', + platform: 'bilibili', type: 'ai', contentType: 'script', reason: '违禁词误判', @@ -88,6 +93,7 @@ const mockAppeals: Appeal[] = [ taskTitle: '美妆新品测评', creatorId: 'creator-004', creatorName: '达人小红', + platform: 'xiaohongshu', type: 'agency', contentType: 'video', reason: '品牌调性理解差异', @@ -116,38 +122,47 @@ function AppealCard({ appeal }: { appeal: Appeal }) { const status = statusConfig[appeal.status] const type = typeConfig[appeal.type] const StatusIcon = status.icon + const platform = getPlatformInfo(appeal.platform) return ( -
- {/* 顶部:状态和类型 */} -
-
-
- -
-
- {appeal.taskTitle} -
- - - {appeal.creatorName} - - · - - {appeal.contentType === 'script' ? : +
+ {/* 平台顶部条 */} + {platform && ( +
+ {platform.icon} + {platform.name} +
+ )} +
+ {/* 顶部:状态和类型 */} +
+
+
+ +
+
+ {appeal.taskTitle} +
+ + + {appeal.creatorName} + + · + + {appeal.contentType === 'script' ? : +
+
+ + {status.label} + + +
-
- - {status.label} - - -
-
{/* 申诉信息 */}
@@ -167,6 +182,7 @@ function AppealCard({ appeal }: { appeal: Appeal }) { 提交时间: {appeal.createdAt} {appeal.updatedAt && 处理时间: {appeal.updatedAt}}
+
) diff --git a/frontend/app/agency/briefs/page.tsx b/frontend/app/agency/briefs/page.tsx index 274c1cb..eec8c10 100644 --- a/frontend/app/agency/briefs/page.tsx +++ b/frontend/app/agency/briefs/page.tsx @@ -15,6 +15,7 @@ import { ChevronRight, Settings } from 'lucide-react' +import { getPlatformInfo } from '@/lib/platforms' // 模拟 Brief 列表 const mockBriefs = [ @@ -22,6 +23,7 @@ const mockBriefs = [ id: 'brief-001', projectName: 'XX品牌618推广', brandName: 'XX护肤品牌', + platform: 'douyin', status: 'configured', uploadedAt: '2026-02-01', configuredAt: '2026-02-02', @@ -33,6 +35,7 @@ const mockBriefs = [ id: 'brief-002', projectName: '新品口红系列', brandName: 'XX美妆品牌', + platform: 'xiaohongshu', status: 'pending', uploadedAt: '2026-02-05', configuredAt: null, @@ -44,6 +47,7 @@ const mockBriefs = [ id: 'brief-003', projectName: '护肤品秋季活动', brandName: 'XX护肤品牌', + platform: 'bilibili', status: 'configured', uploadedAt: '2025-09-15', configuredAt: '2025-09-16', @@ -74,7 +78,7 @@ export default function AgencyBriefsPage() { const configuredCount = mockBriefs.filter(b => b.status === 'configured').length return ( -
+
{/* 页面标题 */}
@@ -136,35 +140,44 @@ export default function AgencyBriefsPage() { {/* Brief 列表 */}
- {filteredBriefs.map((brief) => ( - - - -
-
-
- {brief.status === 'configured' ? ( - - ) : ( - - )} -
-
-
-

{brief.projectName}

- -
-
- {brief.brandName} - - - 上传于 {brief.uploadedAt} - -
-
+ {filteredBriefs.map((brief) => { + const platform = getPlatformInfo(brief.platform) + return ( + + + {/* 平台顶部条 */} + {platform && ( +
+ {platform.icon} + {platform.name}
+ )} + +
+
+
+ {brief.status === 'configured' ? ( + + ) : ( + + )} +
+
+
+

{brief.projectName}

+ +
+
+ {brief.brandName} + + + 上传于 {brief.uploadedAt} + +
+
+
{brief.status === 'configured' && ( @@ -201,7 +214,8 @@ export default function AgencyBriefsPage() { - ))} + ) + })}
{filteredBriefs.length === 0 && ( diff --git a/frontend/app/agency/creators/page.tsx b/frontend/app/agency/creators/page.tsx index 6e1a162..1d7f153 100644 --- a/frontend/app/agency/creators/page.tsx +++ b/frontend/app/agency/creators/page.tsx @@ -27,6 +27,7 @@ import { FolderPlus, X } from 'lucide-react' +import { getPlatformInfo } from '@/lib/platforms' // 任务进度阶段 type TaskStage = 'script_pending' | 'script_ai_review' | 'script_agency_review' | 'script_brand_review' | @@ -50,6 +51,7 @@ interface CreatorTask { id: string name: string projectName: string + platform: string stage: TaskStage appealRemaining: number appealUsed: number @@ -95,8 +97,8 @@ const mockCreators: Creator[] = [ trend: 'up', joinedAt: '2025-08-15', tasks: [ - { id: 'task-001', name: '夏日护肤推广', projectName: 'XX品牌618', stage: 'video_agency_review', appealRemaining: 1, appealUsed: 0 }, - { id: 'task-002', name: '防晒霜测评', projectName: 'XX品牌618', stage: 'script_brand_review', appealRemaining: 0, appealUsed: 1 }, + { id: 'task-001', name: '夏日护肤推广', projectName: 'XX品牌618', platform: 'douyin', stage: 'video_agency_review', appealRemaining: 1, appealUsed: 0 }, + { id: 'task-002', name: '防晒霜测评', projectName: 'XX品牌618', platform: 'douyin', stage: 'script_brand_review', appealRemaining: 0, appealUsed: 1 }, ], }, { @@ -112,7 +114,7 @@ const mockCreators: Creator[] = [ trend: 'stable', joinedAt: '2025-10-20', tasks: [ - { id: 'task-003', name: '新品口红试色', projectName: '口红系列推广', stage: 'video_pending', appealRemaining: 2, appealUsed: 0 }, + { id: 'task-003', name: '新品口红试色', projectName: '口红系列推广', platform: 'xiaohongshu', stage: 'video_pending', appealRemaining: 2, appealUsed: 0 }, ], }, { @@ -128,7 +130,7 @@ const mockCreators: Creator[] = [ trend: 'up', joinedAt: '2025-12-01', tasks: [ - { id: 'task-004', name: '健身器材使用教程', projectName: 'XX运动品牌', stage: 'script_ai_review', appealRemaining: 1, appealUsed: 0 }, + { id: 'task-004', name: '健身器材使用教程', projectName: 'XX运动品牌', platform: 'bilibili', stage: 'script_ai_review', appealRemaining: 1, appealUsed: 0 }, ], }, { @@ -304,7 +306,7 @@ export default function AgencyCreatorsPage() { } return ( -
+
{/* 页面标题 */}
@@ -387,8 +389,8 @@ export default function AgencyCreatorsPage() { {/* 达人列表 */} - - + +
@@ -556,33 +558,45 @@ export default function AgencyCreatorsPage() {
进行中的任务
- {creator.tasks.map(task => ( -
-
-
-
{task.name}
-
项目: {task.projectName}
+ {creator.tasks.map(task => { + const taskPlatform = getPlatformInfo(task.platform) + return ( +
+ {/* 平台顶部条 */} + {taskPlatform && ( +
+ {taskPlatform.icon} + {taskPlatform.name} +
+ )} +
+
+
+
{task.name}
+
项目: {task.projectName}
+
+ +
+
+
+ 申诉次数: + {task.appealRemaining} + / + 已用 {task.appealUsed} +
+ +
-
-
-
- 申诉次数: - {task.appealRemaining} - / - 已用 {task.appealUsed} -
- -
-
- ))} + ) + })}
diff --git a/frontend/app/agency/messages/page.tsx b/frontend/app/agency/messages/page.tsx index 7fb6ccb..33b3fdb 100644 --- a/frontend/app/agency/messages/page.tsx +++ b/frontend/app/agency/messages/page.tsx @@ -17,6 +17,7 @@ import { MoreVertical, PlusCircle } from 'lucide-react' +import { getPlatformInfo } from '@/lib/platforms' // 消息类型 interface Message { @@ -29,6 +30,7 @@ interface Message { icon: typeof Bell iconColor: string bgColor: string + platform?: string // 申诉次数请求专用字段 appealRequest?: { creatorName: string @@ -50,6 +52,7 @@ const mockMessages: Message[] = [ icon: PlusCircle, iconColor: 'text-accent-amber', bgColor: 'bg-accent-amber/20', + platform: 'douyin', appealRequest: { creatorName: '李小红', taskName: '618美妆推广视频', @@ -67,6 +70,7 @@ const mockMessages: Message[] = [ icon: FileText, iconColor: 'text-accent-indigo', bgColor: 'bg-accent-indigo/20', + platform: 'xiaohongshu', }, { id: 'msg-003', @@ -78,6 +82,7 @@ const mockMessages: Message[] = [ icon: PlusCircle, iconColor: 'text-accent-amber', bgColor: 'bg-accent-amber/20', + platform: 'xiaohongshu', appealRequest: { creatorName: '美妆达人小王', taskName: '双11护肤品种草', @@ -95,6 +100,7 @@ const mockMessages: Message[] = [ icon: CheckCircle, iconColor: 'text-accent-green', bgColor: 'bg-accent-green/20', + platform: 'xiaohongshu', }, { id: 'msg-005', @@ -106,6 +112,7 @@ const mockMessages: Message[] = [ icon: XCircle, iconColor: 'text-accent-coral', bgColor: 'bg-accent-coral/20', + platform: 'bilibili', }, { id: 'msg-006', @@ -117,6 +124,7 @@ const mockMessages: Message[] = [ icon: Users, iconColor: 'text-purple-400', bgColor: 'bg-purple-500/20', + platform: 'douyin', }, { id: 'msg-007', @@ -128,6 +136,7 @@ const mockMessages: Message[] = [ icon: AlertTriangle, iconColor: 'text-orange-400', bgColor: 'bg-orange-500/20', + platform: 'xiaohongshu', }, { id: 'msg-008', @@ -139,6 +148,7 @@ const mockMessages: Message[] = [ icon: Video, iconColor: 'text-purple-400', bgColor: 'bg-purple-500/20', + platform: 'bilibili', }, ] @@ -222,15 +232,23 @@ export default function AgencyMessagesPage() { const Icon = message.icon const isAppealRequest = message.type === 'appeal_quota_request' const appealStatus = message.appealRequest?.status + const platform = message.platform ? getPlatformInfo(message.platform) : null return ( !isAppealRequest && markAsRead(message.id)} > + {/* 平台顶部条 */} + {platform && ( +
+ {platform.icon} + {platform.name} +
+ )}
diff --git a/frontend/app/agency/page.tsx b/frontend/app/agency/page.tsx index 28c6832..9fa3958 100644 --- a/frontend/app/agency/page.tsx +++ b/frontend/app/agency/page.tsx @@ -15,6 +15,7 @@ import { MessageSquare, TrendingUp } from 'lucide-react' +import { getPlatformInfo } from '@/lib/platforms' // 模拟统计数据 const stats = { @@ -66,6 +67,7 @@ const projectOverview = [ { id: 'proj-001', name: 'XX品牌618推广', + platform: 'douyin', total: 20, submitted: 15, passed: 10, @@ -76,6 +78,7 @@ const projectOverview = [ { id: 'proj-002', name: '新品口红系列', + platform: 'xiaohongshu', total: 12, submitted: 8, passed: 6, @@ -86,6 +89,7 @@ const projectOverview = [ { id: 'proj-003', name: '护肤品秋季活动', + platform: 'bilibili', total: 15, submitted: 12, passed: 9, @@ -102,6 +106,7 @@ const pendingTasks = [ videoTitle: '夏日护肤推广', creatorName: '小美护肤', brandName: 'XX品牌', + platform: 'douyin', aiScore: 85, submittedAt: '2026-02-04 14:30', hasHighRisk: false, @@ -111,6 +116,7 @@ const pendingTasks = [ videoTitle: '新品口红试色', creatorName: '美妆达人Lisa', brandName: 'XX品牌', + platform: 'xiaohongshu', aiScore: 72, submittedAt: '2026-02-04 13:45', hasHighRisk: true, @@ -120,6 +126,7 @@ const pendingTasks = [ videoTitle: '健身器材开箱', creatorName: '健身教练王', brandName: 'XX运动', + platform: 'bilibili', aiScore: 68, submittedAt: '2026-02-04 14:50', hasHighRisk: true, @@ -134,7 +141,7 @@ function UrgentLevelIcon({ level }: { level: string }) { export default function AgencyDashboard() { return ( -
+
{/* 页面标题 */}

代理商工作台

@@ -251,10 +258,19 @@ export default function AgencyDashboard() {
{projectOverview.map((project) => { const totalReviewing = project.reviewingScript + project.reviewingVideo + const projectPlatform = getPlatformInfo(project.platform) return (
- {project.name} +
+ {project.name} + {projectPlatform && ( + + {projectPlatform.icon} + {projectPlatform.name} + + )} +
{project.submitted}/{project.total} 已提交 @@ -312,7 +328,7 @@ export default function AgencyDashboard() { 待审核任务 - +
+ @@ -334,35 +351,46 @@ export default function AgencyDashboard() { - {pendingTasks.map((task) => ( - - + + - - - - - - - ))} + + + + + + + + ) + })}
达人
视频平台 达人 品牌 AI评分
-
-
{task.videoTitle}
- {task.hasHighRisk && ( - - 高风险 + {pendingTasks.map((task) => { + const platform = getPlatformInfo(task.platform) + return ( +
+
+
{task.videoTitle}
+ {task.hasHighRisk && ( + + 高风险 + + )} +
+
+ {platform && ( + + {platform.icon} + {platform.name} )} - - {task.creatorName}{task.brandName} - = 80 ? 'text-accent-green' : task.aiScore >= 60 ? 'text-yellow-400' : 'text-accent-coral' - }`}> - {task.aiScore}分 - - {task.submittedAt} - - - -
{task.creatorName}{task.brandName} + = 80 ? 'text-accent-green' : task.aiScore >= 60 ? 'text-yellow-400' : 'text-accent-coral' + }`}> + {task.aiScore}分 + + {task.submittedAt} + + + +
diff --git a/frontend/app/agency/reports/page.tsx b/frontend/app/agency/reports/page.tsx index 1682735..d6248d9 100644 --- a/frontend/app/agency/reports/page.tsx +++ b/frontend/app/agency/reports/page.tsx @@ -20,6 +20,7 @@ import { File, Check } from 'lucide-react' +import { getPlatformInfo } from '@/lib/platforms' // 时间范围类型 type DateRange = 'week' | 'month' | 'quarter' | 'year' @@ -113,10 +114,10 @@ const mockDataByRange: Record 项目名称 + 平台 脚本数 视频数 通过率 @@ -406,26 +408,37 @@ export default function AgencyReportsPage() { - {mockProjectStats.map((project) => ( - - {project.name} - {project.scripts} - {project.videos} - - = 90 ? 'text-accent-green' : project.passRate >= 80 ? 'text-accent-indigo' : 'text-orange-400'}`}> - {project.passRate}% - - - -
-
= 90 ? 'bg-accent-green' : project.passRate >= 80 ? 'bg-accent-indigo' : 'bg-orange-400'}`} - style={{ width: `${project.passRate}%` }} - /> -
- - - ))} + {mockProjectStats.map((project) => { + const platform = getPlatformInfo(project.platform) + return ( + + {project.name} + + {platform && ( + + {platform.icon} + {platform.name} + + )} + + {project.scripts} + {project.videos} + + = 90 ? 'text-accent-green' : project.passRate >= 80 ? 'text-accent-indigo' : 'text-orange-400'}`}> + {project.passRate}% + + + +
+
= 90 ? 'bg-accent-green' : project.passRate >= 80 ? 'bg-accent-indigo' : 'bg-orange-400'}`} + style={{ width: `${project.passRate}%` }} + /> +
+ + + ) + })} diff --git a/frontend/app/agency/review/page.tsx b/frontend/app/agency/review/page.tsx index 678fe9b..af57d0c 100644 --- a/frontend/app/agency/review/page.tsx +++ b/frontend/app/agency/review/page.tsx @@ -18,6 +18,7 @@ import { Eye, File } from 'lucide-react' +import { getPlatformInfo } from '@/lib/platforms' // 模拟脚本待审列表 const mockScriptTasks = [ @@ -28,6 +29,7 @@ const mockScriptTasks = [ fileSize: '245 KB', creatorName: '小美护肤', projectName: 'XX品牌618推广', + platform: 'douyin', aiScore: 88, riskLevel: 'low' as const, submittedAt: '2026-02-06 14:30', @@ -40,6 +42,7 @@ const mockScriptTasks = [ fileSize: '312 KB', creatorName: '美妆Lisa', projectName: 'XX品牌618推广', + platform: 'xiaohongshu', aiScore: 72, riskLevel: 'medium' as const, submittedAt: '2026-02-06 12:15', @@ -52,6 +55,7 @@ const mockScriptTasks = [ fileSize: '189 KB', creatorName: '健身教练王', projectName: 'XX运动品牌', + platform: 'bilibili', aiScore: 95, riskLevel: 'low' as const, submittedAt: '2026-02-06 10:00', @@ -64,6 +68,7 @@ const mockScriptTasks = [ fileSize: '278 KB', creatorName: '达人D', projectName: 'XX品牌618推广', + platform: 'kuaishou', aiScore: 62, riskLevel: 'high' as const, submittedAt: '2026-02-06 09:00', @@ -80,6 +85,7 @@ const mockVideoTasks = [ fileSize: '128 MB', creatorName: '小美护肤', projectName: 'XX品牌618推广', + platform: 'douyin', aiScore: 85, riskLevel: 'low' as const, duration: '02:15', @@ -93,6 +99,7 @@ const mockVideoTasks = [ fileSize: '256 MB', creatorName: '美妆Lisa', projectName: 'XX品牌618推广', + platform: 'xiaohongshu', aiScore: 68, riskLevel: 'medium' as const, duration: '03:42', @@ -106,6 +113,7 @@ const mockVideoTasks = [ fileSize: '198 MB', creatorName: '达人C', projectName: 'XX品牌618推广', + platform: 'bilibili', aiScore: 58, riskLevel: 'high' as const, duration: '04:20', @@ -119,6 +127,7 @@ const mockVideoTasks = [ fileSize: '167 MB', creatorName: '达人D', projectName: 'XX品牌618推广', + platform: 'wechat', aiScore: 91, riskLevel: 'low' as const, duration: '01:45', @@ -145,6 +154,7 @@ type VideoTask = typeof mockVideoTasks[0] function ScriptTaskCard({ task }: { task: ScriptTask }) { const riskConfig = riskLevelConfig[task.riskLevel] + const platform = getPlatformInfo(task.platform) const handleDownload = (e: React.MouseEvent) => { e.stopPropagation() @@ -153,15 +163,23 @@ function ScriptTaskCard({ task }: { task: ScriptTask }) { } return ( -
- {/* 顶部:达人名 · 任务名 + 状态标签 */} -
-
-
- {task.creatorName} · {task.title} +
+ {/* 平台顶部条 */} + {platform && ( +
+ {platform.icon} + {platform.name} +
+ )} +
+ {/* 顶部:达人名 · 任务名 + 状态标签 */} +
+
+
+ {task.creatorName} · {task.title} +
+ {riskConfig.label}
- {riskConfig.label} -
{/* 文件信息 */}
@@ -198,12 +216,14 @@ function ScriptTaskCard({ task }: { task: ScriptTask }) {
+
) } function VideoTaskCard({ task }: { task: VideoTask }) { const riskConfig = riskLevelConfig[task.riskLevel] + const platform = getPlatformInfo(task.platform) const handleDownload = (e: React.MouseEvent) => { e.stopPropagation() @@ -212,15 +232,23 @@ function VideoTaskCard({ task }: { task: VideoTask }) { } return ( -
- {/* 顶部:达人名 · 任务名 + 状态标签 */} -
-
-
- {task.creatorName} · {task.title} +
+ {/* 平台顶部条 */} + {platform && ( +
+ {platform.icon} + {platform.name} +
+ )} +
+ {/* 顶部:达人名 · 任务名 + 状态标签 */} +
+
+
+ {task.creatorName} · {task.title} +
+ {riskConfig.label}
- {riskConfig.label} -
{/* 文件信息 */}
@@ -257,6 +285,7 @@ function VideoTaskCard({ task }: { task: VideoTask }) {
+
) } @@ -276,7 +305,7 @@ export default function AgencyReviewListPage() { ) return ( -
+
{/* 页面标题 */}
diff --git a/frontend/app/brand/agencies/page.tsx b/frontend/app/brand/agencies/page.tsx index 7806600..411fdff 100644 --- a/frontend/app/brand/agencies/page.tsx +++ b/frontend/app/brand/agencies/page.tsx @@ -238,7 +238,7 @@ export default function AgenciesManagePage() { } return ( -
+
{/* 页面标题 */}
@@ -325,8 +325,8 @@ export default function AgenciesManagePage() { {/* 代理商列表 */} - - + +
diff --git a/frontend/app/brand/briefs/page.tsx b/frontend/app/brand/briefs/page.tsx index f51fdb0..61d68bb 100644 --- a/frontend/app/brand/briefs/page.tsx +++ b/frontend/app/brand/briefs/page.tsx @@ -1,13 +1,23 @@ 'use client' import { useState } from 'react' -import { Plus, FileText, Upload, Trash2, Edit } from 'lucide-react' +import { Plus, FileText, Upload, Trash2, Edit, Check, Search, X, Eye } from 'lucide-react' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card' import { Button } from '@/components/ui/Button' import { Input } from '@/components/ui/Input' import { Modal } from '@/components/ui/Modal' import { SuccessTag, PendingTag } from '@/components/ui/Tag' +// 平台选项 +const platformOptions = [ + { id: 'douyin', name: '抖音', icon: '🎵', color: 'bg-[#1a1a1a]' }, + { id: 'xiaohongshu', name: '小红书', icon: '📕', color: 'bg-[#fe2c55]' }, + { id: 'bilibili', name: 'B站', icon: '📺', color: 'bg-[#00a1d6]' }, + { id: 'kuaishou', name: '快手', icon: '⚡', color: 'bg-[#ff4906]' }, + { id: 'weibo', name: '微博', icon: '🔴', color: 'bg-[#e6162d]' }, + { id: 'wechat', name: '微信视频号', icon: '💬', color: 'bg-[#07c160]' }, +] + // 模拟 Brief 列表 const mockBriefs = [ { @@ -15,6 +25,7 @@ const mockBriefs = [ name: '2024 夏日护肤活动', description: '夏日护肤系列产品推广规范', status: 'active', + platforms: ['douyin', 'xiaohongshu'], rulesCount: 12, creatorsCount: 45, createdAt: '2024-01-15', @@ -25,6 +36,7 @@ const mockBriefs = [ name: '新品口红上市', description: '春季新品口红营销 Brief', status: 'active', + platforms: ['xiaohongshu', 'bilibili'], rulesCount: 8, creatorsCount: 32, createdAt: '2024-02-01', @@ -35,6 +47,7 @@ const mockBriefs = [ name: '年货节活动', description: '春节年货促销活动规范', status: 'archived', + platforms: ['douyin', 'kuaishou'], rulesCount: 15, creatorsCount: 78, createdAt: '2024-01-01', @@ -43,40 +56,104 @@ const mockBriefs = [ ] export default function BriefsPage() { - const [briefs] = useState(mockBriefs) + const [briefs, setBriefs] = useState(mockBriefs) const [showCreateModal, setShowCreateModal] = useState(false) const [searchQuery, setSearchQuery] = useState('') + // 新建 Brief 表单 + const [newBriefName, setNewBriefName] = useState('') + const [newBriefDesc, setNewBriefDesc] = useState('') + const [selectedPlatforms, setSelectedPlatforms] = useState([]) + + // 查看详情 + const [showDetailModal, setShowDetailModal] = useState(false) + const [selectedBrief, setSelectedBrief] = useState(null) + const filteredBriefs = briefs.filter((brief) => brief.name.toLowerCase().includes(searchQuery.toLowerCase()) ) + // 切换平台选择 + const togglePlatform = (platformId: string) => { + setSelectedPlatforms(prev => + prev.includes(platformId) + ? prev.filter(id => id !== platformId) + : [...prev, platformId] + ) + } + + // 获取平台信息 + const getPlatformInfo = (platformId: string) => { + return platformOptions.find(p => p.id === platformId) + } + + // 创建 Brief + const handleCreateBrief = () => { + if (!newBriefName.trim() || selectedPlatforms.length === 0) return + + const newBrief = { + id: `brief-${Date.now()}`, + name: newBriefName, + description: newBriefDesc, + status: 'active' as const, + platforms: selectedPlatforms, + rulesCount: 0, + creatorsCount: 0, + createdAt: new Date().toISOString().split('T')[0], + updatedAt: new Date().toISOString().split('T')[0], + } + + setBriefs([newBrief, ...briefs]) + setShowCreateModal(false) + setNewBriefName('') + setNewBriefDesc('') + setSelectedPlatforms([]) + } + + // 查看 Brief 详情 + const viewBriefDetail = (brief: typeof mockBriefs[0]) => { + setSelectedBrief(brief) + setShowDetailModal(true) + } + + // 删除 Brief + const handleDeleteBrief = (id: string) => { + setBriefs(briefs.filter(b => b.id !== id)) + } + return (
-

Brief 管理

-
{/* 搜索 */} -
- + + 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" />
{/* Brief 列表 */}
{filteredBriefs.map((brief) => ( - +
-
- +
+
{brief.status === 'active' ? ( 使用中 @@ -85,24 +162,57 @@ export default function BriefsPage() { )}
-

{brief.name}

-

{brief.description}

+

{brief.name}

+

{brief.description}

-
+ {/* 平台标签 */} +
+ {brief.platforms.map(platformId => { + const platform = getPlatformInfo(platformId) + return platform ? ( + + {platform.icon} + {platform.name} + + ) : null + })} +
+ +
{brief.rulesCount} 条规则 {brief.creatorsCount} 位达人
-
- +
+ 更新于 {brief.updatedAt} -
- - +
@@ -111,58 +221,210 @@ export default function BriefsPage() { ))} {/* 新建卡片 */} - setShowCreateModal(true)} + className="p-5 rounded-xl border-2 border-dashed border-border-subtle hover:border-accent-indigo hover:bg-accent-indigo/5 transition-all flex flex-col items-center justify-center min-h-[240px]" > - -
- -
- 新建 Brief -
-
+
+ +
+ 新建 Brief +
{/* 新建 Brief 弹窗 */} setShowCreateModal(false)} + onClose={() => { + setShowCreateModal(false) + setNewBriefName('') + setNewBriefDesc('') + setSelectedPlatforms([]) + }} title="新建 Brief" - size="md" + size="lg" > -
- +
- + + setNewBriefName(e.target.value)} + placeholder="输入 Brief 名称" + className="w-full px-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" + /> +
+ +
+
代理商