Your Name e4959d584f feat: 完善代理商端业务逻辑与前后端框架
主要更新:
- 更新代理商端文档,明确项目由品牌方分配流程
- 新增Brief配置详情页(已配置)设计稿
- 完善工作台紧急待办中品牌新任务功能
- 整理Pencil设计文件中代理商端页面顺序
- 新增后端FastAPI框架及核心API
- 新增前端Next.js页面和组件库
- 添加.gitignore排除构建和缓存文件

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 19:27:31 +08:00

169 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState } from 'react'
import { Plus, FileText, Upload, Trash2, Edit } 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'
// 模拟 Brief 列表
const mockBriefs = [
{
id: 'brief-001',
name: '2024 夏日护肤活动',
description: '夏日护肤系列产品推广规范',
status: 'active',
rulesCount: 12,
creatorsCount: 45,
createdAt: '2024-01-15',
updatedAt: '2024-02-01',
},
{
id: 'brief-002',
name: '新品口红上市',
description: '春季新品口红营销 Brief',
status: 'active',
rulesCount: 8,
creatorsCount: 32,
createdAt: '2024-02-01',
updatedAt: '2024-02-03',
},
{
id: 'brief-003',
name: '年货节活动',
description: '春节年货促销活动规范',
status: 'archived',
rulesCount: 15,
creatorsCount: 78,
createdAt: '2024-01-01',
updatedAt: '2024-01-20',
},
]
export default function BriefsPage() {
const [briefs] = useState(mockBriefs)
const [showCreateModal, setShowCreateModal] = useState(false)
const [searchQuery, setSearchQuery] = useState('')
const filteredBriefs = briefs.filter((brief) =>
brief.name.toLowerCase().includes(searchQuery.toLowerCase())
)
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold text-gray-900">Brief </h1>
<Button icon={Plus} onClick={() => setShowCreateModal(true)}>
Brief
</Button>
</div>
{/* 搜索 */}
<div className="max-w-md">
<Input
placeholder="搜索 Brief..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
{/* Brief 列表 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filteredBriefs.map((brief) => (
<Card key={brief.id} className="hover:shadow-md transition-shadow">
<CardContent className="p-5">
<div className="flex items-start justify-between mb-3">
<div className="p-2 bg-blue-50 rounded-lg">
<FileText size={24} className="text-blue-600" />
</div>
{brief.status === 'active' ? (
<SuccessTag>使</SuccessTag>
) : (
<PendingTag></PendingTag>
)}
</div>
<h3 className="font-semibold text-gray-900 mb-1">{brief.name}</h3>
<p className="text-sm text-gray-500 mb-4">{brief.description}</p>
<div className="flex gap-4 text-sm text-gray-500 mb-4">
<span>{brief.rulesCount} </span>
<span>{brief.creatorsCount} </span>
</div>
<div className="flex items-center justify-between pt-3 border-t">
<span className="text-xs text-gray-400">
{brief.updatedAt}
</span>
<div className="flex gap-2">
<button type="button" className="p-1 hover:bg-gray-100 rounded">
<Edit size={16} className="text-gray-500" />
</button>
<button type="button" className="p-1 hover:bg-gray-100 rounded">
<Trash2 size={16} className="text-gray-500" />
</button>
</div>
</div>
</CardContent>
</Card>
))}
{/* 新建卡片 */}
<Card
className="border-dashed cursor-pointer hover:border-blue-400 hover:bg-blue-50/50 transition-colors"
onClick={() => setShowCreateModal(true)}
>
<CardContent className="p-5 flex flex-col items-center justify-center h-full min-h-[200px]">
<div className="p-3 bg-gray-100 rounded-full mb-3">
<Plus size={24} className="text-gray-500" />
</div>
<span className="text-gray-500"> Brief</span>
</CardContent>
</Card>
</div>
{/* 新建 Brief 弹窗 */}
<Modal
isOpen={showCreateModal}
onClose={() => setShowCreateModal(false)}
title="新建 Brief"
size="md"
>
<div className="space-y-4">
<Input label="Brief 名称" placeholder="输入 Brief 名称" />
<div>
<label className="block text-sm font-medium text-gray-700 mb-1"></label>
<textarea
className="w-full h-20 p-3 border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="输入 Brief 描述..."
/>
</div>
{/* 上传 PDF */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Brief
</label>
<div className="border-2 border-dashed rounded-lg p-6 text-center hover:border-blue-400 transition-colors cursor-pointer">
<Upload size={32} className="mx-auto text-gray-400 mb-2" />
<p className="text-sm text-gray-600"> PDF </p>
<p className="text-xs text-gray-400 mt-1">AI </p>
</div>
</div>
<div className="flex gap-3 justify-end pt-4">
<Button variant="ghost" onClick={() => setShowCreateModal(false)}>
</Button>
<Button onClick={() => setShowCreateModal(false)}>
</Button>
</div>
</div>
</Modal>
</div>
)
}