Your Name 0bfedb95c8 feat: 为所有终端添加平台显示功能
- 新增 frontend/lib/platforms.ts 共享平台配置模块
- 支持6个平台: 抖音、小红书、B站、快手、微博、微信视频号
- 品牌方终端: 项目看板、项目详情、终审台列表添加平台显示
- 代理商终端: 工作台概览、审核台、Brief配置、达人管理、
  数据报表、消息中心、申诉处理添加平台显示
- 达人端: 任务列表添加平台显示
- 统一使用彩色头部条样式展示平台信息

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 18:53:51 +08:00

957 lines
40 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, Shield, Ban, Building2, Search, X, Upload, Trash2, FileText, Check, Download, Eye } from 'lucide-react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
import { Button } from '@/components/ui/Button'
import { Modal } from '@/components/ui/Modal'
// 平台规则库数据
const platformRuleLibraries = [
{
id: 'douyin',
name: '抖音',
icon: '🎵',
color: 'bg-[#25F4EE]',
rules: { forbiddenWords: 156, competitors: 0, whitelist: 12 },
version: 'v2024.02',
updatedAt: '2024-02-01',
},
{
id: 'xiaohongshu',
name: '小红书',
icon: '📕',
color: 'bg-[#fe2c55]',
rules: { forbiddenWords: 142, competitors: 0, whitelist: 8 },
version: 'v2024.01',
updatedAt: '2024-01-20',
},
{
id: 'bilibili',
name: 'B站',
icon: '📺',
color: 'bg-[#00a1d6]',
rules: { forbiddenWords: 98, competitors: 0, whitelist: 15 },
version: 'v2024.02',
updatedAt: '2024-02-03',
},
{
id: 'kuaishou',
name: '快手',
icon: '⚡',
color: 'bg-[#ff4906]',
rules: { forbiddenWords: 134, competitors: 0, whitelist: 10 },
version: 'v2024.01',
updatedAt: '2024-01-15',
},
{
id: 'weibo',
name: '微博',
icon: '🔴',
color: 'bg-[#e6162d]',
rules: { forbiddenWords: 89, competitors: 0, whitelist: 6 },
version: 'v2023.12',
updatedAt: '2023-12-20',
},
]
// 模拟规则数据
const initialRules = {
forbiddenWords: [
{ id: '1', word: '最好', category: '极限词' },
{ id: '2', word: '第一', category: '极限词' },
{ id: '3', word: '最佳', category: '极限词' },
{ id: '4', word: '100%有效', category: '虚假宣称' },
{ id: '5', word: '立即见效', category: '虚假宣称' },
{ id: '6', word: '永久', category: '极限词' },
{ id: '7', word: '绝对', category: '极限词' },
{ id: '8', word: '最低价', category: '价格欺诈' },
],
competitors: [
{ id: '1', name: '竞品A' },
{ id: '2', name: '竞品B' },
{ id: '3', name: '竞品C' },
],
whitelist: [
{ id: '1', term: '品牌专属术语1', reason: '品牌授权使用' },
{ id: '2', term: '特定产品名', reason: '官方产品名称' },
],
}
const categoryOptions = [
{ value: '极限词', label: '极限词' },
{ value: '虚假宣称', label: '虚假宣称' },
{ value: '价格欺诈', label: '价格欺诈' },
{ value: '平台规则', label: '平台规则' },
{ value: '自定义', label: '自定义' },
]
export default function RulesPage() {
const [activeTab, setActiveTab] = useState<'platforms' | 'forbidden' | 'competitors' | 'whitelist'>('platforms')
const [rules, setRules] = useState(initialRules)
const [searchQuery, setSearchQuery] = useState('')
const [platforms, setPlatforms] = useState(platformRuleLibraries)
// 上传规则库
const [showUploadModal, setShowUploadModal] = useState(false)
const [uploadPlatform, setUploadPlatform] = useState('')
const [uploadFile, setUploadFile] = useState<File | null>(null)
// 重新上传规则库
const [showReuploadModal, setShowReuploadModal] = useState(false)
const [reuploadPlatform, setReuploadPlatform] = useState<typeof platformRuleLibraries[0] | null>(null)
// 下载确认
const [showDownloadModal, setShowDownloadModal] = useState(false)
const [downloadPlatform, setDownloadPlatform] = useState<typeof platformRuleLibraries[0] | null>(null)
// 查看规则库详情
const [showDetailModal, setShowDetailModal] = useState(false)
const [selectedPlatform, setSelectedPlatform] = useState<typeof platformRuleLibraries[0] | null>(null)
// 添加违禁词
const [showAddWordModal, setShowAddWordModal] = useState(false)
const [newWord, setNewWord] = useState('')
const [newCategory, setNewCategory] = useState('极限词')
const [batchWords, setBatchWords] = useState('')
// 添加竞品
const [showAddCompetitorModal, setShowAddCompetitorModal] = useState(false)
const [newCompetitor, setNewCompetitor] = useState('')
// 添加白名单
const [showAddWhitelistModal, setShowAddWhitelistModal] = useState(false)
const [newWhitelistTerm, setNewWhitelistTerm] = useState('')
const [newWhitelistReason, setNewWhitelistReason] = useState('')
// 过滤违禁词
const filteredWords = rules.forbiddenWords.filter(w =>
searchQuery === '' ||
w.word.toLowerCase().includes(searchQuery.toLowerCase()) ||
w.category.toLowerCase().includes(searchQuery.toLowerCase())
)
// 查看平台详情
const viewPlatformDetail = (platform: typeof platformRuleLibraries[0]) => {
setSelectedPlatform(platform)
setShowDetailModal(true)
}
// 重新上传
const handleReupload = (platform: typeof platformRuleLibraries[0]) => {
setReuploadPlatform(platform)
setShowReuploadModal(true)
}
// 下载规则
const handleDownload = (platform: typeof platformRuleLibraries[0]) => {
setDownloadPlatform(platform)
setShowDownloadModal(true)
}
// 添加单个违禁词
const handleAddWord = () => {
if (!newWord.trim()) return
setRules({
...rules,
forbiddenWords: [
...rules.forbiddenWords,
{ id: Date.now().toString(), word: newWord.trim(), category: newCategory }
]
})
setNewWord('')
setShowAddWordModal(false)
}
// 批量添加违禁词
const handleBatchAdd = () => {
const words = batchWords.split('\n').filter(w => w.trim())
if (words.length === 0) return
const newWords = words.map((word, i) => ({
id: `${Date.now()}-${i}`,
word: word.trim(),
category: newCategory
}))
setRules({
...rules,
forbiddenWords: [...rules.forbiddenWords, ...newWords]
})
setBatchWords('')
setShowAddWordModal(false)
}
// 删除违禁词
const handleDeleteWord = (id: string) => {
setRules({
...rules,
forbiddenWords: rules.forbiddenWords.filter(w => w.id !== id)
})
}
// 添加竞品
const handleAddCompetitor = () => {
if (!newCompetitor.trim()) return
setRules({
...rules,
competitors: [
...rules.competitors,
{ id: Date.now().toString(), name: newCompetitor.trim() }
]
})
setNewCompetitor('')
setShowAddCompetitorModal(false)
}
// 删除竞品
const handleDeleteCompetitor = (id: string) => {
setRules({
...rules,
competitors: rules.competitors.filter(c => c.id !== id)
})
}
// 添加白名单
const handleAddWhitelist = () => {
if (!newWhitelistTerm.trim()) return
setRules({
...rules,
whitelist: [
...rules.whitelist,
{ id: Date.now().toString(), term: newWhitelistTerm.trim(), reason: newWhitelistReason.trim() }
]
})
setNewWhitelistTerm('')
setNewWhitelistReason('')
setShowAddWhitelistModal(false)
}
// 删除白名单
const handleDeleteWhitelist = (id: string) => {
setRules({
...rules,
whitelist: rules.whitelist.filter(w => w.id !== id)
})
}
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold text-text-primary"></h1>
<p className="text-sm text-text-secondary mt-1"></p>
</div>
{/* 标签页 */}
<div className="flex gap-1 p-1 bg-bg-elevated rounded-xl w-fit">
<button
type="button"
className={`px-4 py-2.5 rounded-lg text-sm font-medium transition-colors flex items-center gap-2 ${
activeTab === 'platforms'
? 'bg-bg-card text-text-primary shadow-sm'
: 'text-text-secondary hover:text-text-primary'
}`}
onClick={() => setActiveTab('platforms')}
>
<FileText size={16} />
<span className="px-2 py-0.5 rounded-full bg-accent-indigo/15 text-accent-indigo text-xs">
{platforms.length}
</span>
</button>
<button
type="button"
className={`px-4 py-2.5 rounded-lg text-sm font-medium transition-colors flex items-center gap-2 ${
activeTab === 'forbidden'
? 'bg-bg-card text-text-primary shadow-sm'
: 'text-text-secondary hover:text-text-primary'
}`}
onClick={() => setActiveTab('forbidden')}
>
<Ban size={16} />
<span className="px-2 py-0.5 rounded-full bg-accent-coral/15 text-accent-coral text-xs">
{rules.forbiddenWords.length}
</span>
</button>
<button
type="button"
className={`px-4 py-2.5 rounded-lg text-sm font-medium transition-colors flex items-center gap-2 ${
activeTab === 'competitors'
? 'bg-bg-card text-text-primary shadow-sm'
: 'text-text-secondary hover:text-text-primary'
}`}
onClick={() => setActiveTab('competitors')}
>
<Building2 size={16} />
<span className="px-2 py-0.5 rounded-full bg-accent-amber/15 text-accent-amber text-xs">
{rules.competitors.length}
</span>
</button>
<button
type="button"
className={`px-4 py-2.5 rounded-lg text-sm font-medium transition-colors flex items-center gap-2 ${
activeTab === 'whitelist'
? 'bg-bg-card text-text-primary shadow-sm'
: 'text-text-secondary hover:text-text-primary'
}`}
onClick={() => setActiveTab('whitelist')}
>
<Shield size={16} />
<span className="px-2 py-0.5 rounded-full bg-accent-green/15 text-accent-green text-xs">
{rules.whitelist.length}
</span>
</button>
</div>
{/* 平台规则库 */}
{activeTab === 'platforms' && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<p className="text-sm text-text-tertiary mt-1">
</p>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{platforms.map((platform) => (
<div
key={platform.id}
className="p-4 rounded-xl border border-border-subtle bg-bg-card hover:border-accent-indigo/50 transition-all"
>
<div className="flex items-center gap-3 mb-3">
<div className={`w-10 h-10 ${platform.color} rounded-xl flex items-center justify-center text-xl`}>
{platform.icon}
</div>
<div>
<h3 className="font-medium text-text-primary">{platform.name}</h3>
<p className="text-xs text-text-tertiary">{platform.version}</p>
</div>
</div>
<div className="flex items-center gap-4 text-xs text-text-tertiary mb-3">
<span>{platform.rules.forbiddenWords} </span>
<span>{platform.rules.whitelist} </span>
</div>
<div className="flex items-center justify-between pt-3 border-t border-border-subtle">
<span className="text-xs text-text-tertiary"> {platform.updatedAt}</span>
<div className="flex gap-1">
<button
type="button"
onClick={() => viewPlatformDetail(platform)}
className="p-1.5 rounded-lg text-text-tertiary hover:text-accent-indigo hover:bg-accent-indigo/10 transition-colors"
title="查看详情"
>
<Eye size={16} />
</button>
<button
type="button"
onClick={() => handleReupload(platform)}
className="p-1.5 rounded-lg text-text-tertiary hover:text-accent-indigo hover:bg-accent-indigo/10 transition-colors"
title="重新上传"
>
<Upload size={16} />
</button>
<button
type="button"
onClick={() => handleDownload(platform)}
className="p-1.5 rounded-lg text-text-tertiary hover:text-accent-green hover:bg-accent-green/10 transition-colors"
title="下载规则"
>
<Download size={16} />
</button>
</div>
</div>
</div>
))}
{/* 上传规则库 */}
<button
type="button"
onClick={() => setShowUploadModal(true)}
className="p-4 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 gap-2 text-text-tertiary hover:text-accent-indigo min-h-[160px]"
>
<Upload size={24} />
<span className="font-medium"></span>
<span className="text-xs"> JSON / Excel </span>
</button>
</div>
</CardContent>
</Card>
)}
{/* 自定义违禁词列表 */}
{activeTab === 'forbidden' && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<p className="text-sm text-text-tertiary mt-1"></p>
</CardHeader>
<CardContent className="space-y-4">
{/* 搜索框和添加按钮 */}
<div className="flex items-center gap-3">
<div className="relative flex-1 max-w-md">
<Search size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-tertiary" />
<input
type="text"
placeholder="搜索违禁词或分类..."
value={searchQuery}
onChange={(e) => 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"
/>
</div>
<button
type="button"
onClick={() => setShowAddWordModal(true)}
className="inline-flex items-center gap-2 px-4 py-2.5 rounded-xl border-2 border-dashed border-border-subtle hover:border-accent-coral hover:bg-accent-coral/5 transition-all text-text-tertiary hover:text-accent-coral"
>
<Plus size={18} />
<span className="font-medium"></span>
</button>
</div>
{/* 按分类分组显示 */}
{(() => {
const grouped = filteredWords.reduce((acc, word) => {
if (!acc[word.category]) acc[word.category] = []
acc[word.category].push(word)
return acc
}, {} as Record<string, typeof filteredWords>)
return Object.entries(grouped).map(([category, words]) => (
<div key={category} className="space-y-2">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-text-secondary">{category}</span>
<span className="text-xs text-text-tertiary">({words.length})</span>
</div>
<div className="flex flex-wrap gap-2">
{words.map((word) => (
<div
key={word.id}
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-elevated border border-border-subtle group hover:border-accent-coral/50"
>
<span className="text-text-primary">{word.word}</span>
<button
type="button"
onClick={() => handleDeleteWord(word.id)}
className="text-text-tertiary hover:text-accent-coral transition-colors"
>
<X size={14} />
</button>
</div>
))}
</div>
</div>
))
})()}
{filteredWords.length === 0 && (
<div className="text-center py-8 text-text-tertiary">
<Ban size={32} className="mx-auto mb-2 opacity-50" />
<p></p>
</div>
)}
</CardContent>
</Card>
)}
{/* 竞品列表 */}
{activeTab === 'competitors' && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<p className="text-sm text-text-tertiary mt-1"> Logo </p>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{rules.competitors.map((competitor) => (
<div key={competitor.id} className="p-4 rounded-xl bg-bg-elevated border border-border-subtle flex items-center justify-between group hover:border-accent-amber/50">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-accent-amber/15 rounded-lg flex items-center justify-center">
<Building2 size={20} className="text-accent-amber" />
</div>
<span className="font-medium text-text-primary">{competitor.name}</span>
</div>
<button
type="button"
onClick={() => handleDeleteCompetitor(competitor.id)}
className="p-2 rounded-lg text-text-tertiary hover:text-accent-coral hover:bg-accent-coral/10 transition-colors"
>
<Trash2 size={16} />
</button>
</div>
))}
{/* 添加竞品按钮 */}
<button
type="button"
onClick={() => setShowAddCompetitorModal(true)}
className="p-4 rounded-xl border-2 border-dashed border-border-subtle hover:border-accent-amber hover:bg-accent-amber/5 transition-all flex items-center justify-center gap-2 text-text-tertiary hover:text-accent-amber"
>
<Plus size={20} />
<span className="font-medium"></span>
</button>
</div>
</CardContent>
</Card>
)}
{/* 白名单 */}
{activeTab === 'whitelist' && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<p className="text-sm text-text-tertiary mt-1">使</p>
</CardHeader>
<CardContent>
<div className="space-y-3">
{rules.whitelist.map((item) => (
<div key={item.id} className="flex items-center justify-between p-4 rounded-xl bg-bg-elevated border border-border-subtle hover:border-accent-green/50">
<div>
<p className="font-medium text-text-primary">{item.term}</p>
<p className="text-sm text-text-tertiary mt-0.5">{item.reason}</p>
</div>
<button
type="button"
onClick={() => handleDeleteWhitelist(item.id)}
className="p-2 rounded-lg text-text-tertiary hover:text-accent-coral hover:bg-accent-coral/10 transition-colors"
>
<Trash2 size={16} />
</button>
</div>
))}
{/* 添加白名单按钮 */}
<button
type="button"
onClick={() => setShowAddWhitelistModal(true)}
className="w-full p-4 rounded-xl border-2 border-dashed border-border-subtle hover:border-accent-green hover:bg-accent-green/5 transition-all flex items-center justify-center gap-2 text-text-tertiary hover:text-accent-green"
>
<Plus size={20} />
<span className="font-medium"></span>
</button>
</div>
</CardContent>
</Card>
)}
{/* 上传规则库弹窗 */}
<Modal
isOpen={showUploadModal}
onClose={() => { setShowUploadModal(false); setUploadPlatform(''); setUploadFile(null); }}
title="上传平台规则库"
>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<select
value={uploadPlatform}
onChange={(e) => setUploadPlatform(e.target.value)}
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"
>
<option value=""></option>
<option value="douyin"></option>
<option value="xiaohongshu"></option>
<option value="bilibili">B站</option>
<option value="kuaishou"></option>
<option value="weibo"></option>
<option value="wechat"></option>
<option value="other"></option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="border-2 border-dashed border-border-subtle rounded-xl p-8 text-center hover:border-accent-indigo transition-colors cursor-pointer">
<Upload size={32} className="mx-auto text-text-tertiary mb-3" />
<p className="text-sm text-text-primary mb-1"></p>
<p className="text-xs text-text-tertiary"> JSON / Excel / Word / PDF </p>
</div>
</div>
<div className="p-4 rounded-xl bg-accent-indigo/10 border border-accent-indigo/20">
<h4 className="text-sm font-medium text-accent-indigo mb-2"></h4>
<ul className="text-xs text-text-secondary space-y-1">
<li> JSON forbiddenWordswhitelist </li>
<li> Excel </li>
<li> Word / PDFAI </li>
</ul>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowUploadModal(false); setUploadPlatform(''); }}>
</Button>
<Button disabled={!uploadPlatform}>
</Button>
</div>
</div>
</Modal>
{/* 查看规则库详情弹窗 */}
<Modal
isOpen={showDetailModal}
onClose={() => { setShowDetailModal(false); setSelectedPlatform(null); }}
title={selectedPlatform ? `${selectedPlatform.name}规则库详情` : '规则库详情'}
size="lg"
>
{selectedPlatform && (
<div className="space-y-4">
<div className="flex items-center gap-4 p-4 rounded-xl bg-bg-elevated">
<div className={`w-14 h-14 ${selectedPlatform.color} rounded-xl flex items-center justify-center text-2xl`}>
{selectedPlatform.icon}
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-text-primary">{selectedPlatform.name}</h3>
<div className="flex items-center gap-4 mt-1 text-sm text-text-tertiary">
<span>{selectedPlatform.version}</span>
<span>{selectedPlatform.updatedAt}</span>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-4">
<div className="p-4 rounded-xl bg-accent-coral/10 border border-accent-coral/20 text-center">
<p className="text-2xl font-bold text-accent-coral">{selectedPlatform.rules.forbiddenWords}</p>
<p className="text-sm text-text-secondary mt-1"></p>
</div>
<div className="p-4 rounded-xl bg-accent-green/10 border border-accent-green/20 text-center">
<p className="text-2xl font-bold text-accent-green">{selectedPlatform.rules.whitelist}</p>
<p className="text-sm text-text-secondary mt-1"></p>
</div>
<div className="p-4 rounded-xl bg-accent-indigo/10 border border-accent-indigo/20 text-center">
<p className="text-2xl font-bold text-accent-indigo">12</p>
<p className="text-sm text-text-secondary mt-1"></p>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-text-primary mb-2"></h4>
<div className="space-y-2">
{[
{ name: '极限词', count: 45 },
{ name: '虚假宣传', count: 38 },
{ name: '敏感词汇', count: 32 },
{ name: '价格欺诈', count: 21 },
{ name: '其他', count: 20 },
].map((cat) => (
<div key={cat.name} className="flex items-center justify-between p-3 rounded-lg bg-bg-elevated">
<span className="text-sm text-text-primary">{cat.name}</span>
<span className="text-sm text-text-tertiary">{cat.count} </span>
</div>
))}
</div>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => setShowDetailModal(false)}>
</Button>
<Button variant="secondary" onClick={() => selectedPlatform && handleDownload(selectedPlatform)}>
<Download size={16} />
</Button>
<Button onClick={() => {
if (selectedPlatform) {
setShowDetailModal(false)
handleReupload(selectedPlatform)
}
}}>
<Upload size={16} />
</Button>
</div>
</div>
)}
</Modal>
{/* 重新上传规则库弹窗 */}
<Modal
isOpen={showReuploadModal}
onClose={() => { setShowReuploadModal(false); setReuploadPlatform(null); }}
title={reuploadPlatform ? `重新上传${reuploadPlatform.name}规则库` : '重新上传规则库'}
>
{reuploadPlatform && (
<div className="space-y-4">
<div className="flex items-center gap-3 p-3 rounded-xl bg-bg-elevated">
<div className={`w-10 h-10 ${reuploadPlatform.color} rounded-lg flex items-center justify-center text-xl`}>
{reuploadPlatform.icon}
</div>
<div>
<p className="font-medium text-text-primary">{reuploadPlatform.name}</p>
<p className="text-xs text-text-tertiary">{reuploadPlatform.version}</p>
</div>
</div>
<div className="p-4 rounded-xl bg-accent-amber/10 border border-accent-amber/20">
<p className="text-sm text-accent-amber font-medium mb-1"> </p>
<p className="text-xs text-text-secondary"></p>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="border-2 border-dashed border-border-subtle rounded-xl p-6 text-center hover:border-accent-indigo transition-colors cursor-pointer">
<Upload size={28} className="mx-auto text-text-tertiary mb-2" />
<p className="text-sm text-text-primary mb-1"></p>
<p className="text-xs text-text-tertiary"> JSON / Excel / Word / PDF </p>
</div>
<p className="text-xs text-text-tertiary mt-2">Word PDF AI </p>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowReuploadModal(false); setReuploadPlatform(null); }}>
</Button>
<Button>
</Button>
</div>
</div>
)}
</Modal>
{/* 下载规则库弹窗 */}
<Modal
isOpen={showDownloadModal}
onClose={() => { setShowDownloadModal(false); setDownloadPlatform(null); }}
title={downloadPlatform ? `下载${downloadPlatform.name}规则库` : '下载规则库'}
>
{downloadPlatform && (
<div className="space-y-4">
<div className="flex items-center gap-3 p-3 rounded-xl bg-bg-elevated">
<div className={`w-10 h-10 ${downloadPlatform.color} rounded-lg flex items-center justify-center text-xl`}>
{downloadPlatform.icon}
</div>
<div>
<p className="font-medium text-text-primary">{downloadPlatform.name}</p>
<p className="text-xs text-text-tertiary">{downloadPlatform.version} · {downloadPlatform.updatedAt}</p>
</div>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-3"></label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
className="p-3 rounded-xl border-2 border-border-subtle hover:border-accent-indigo hover:bg-accent-indigo/5 transition-all text-left"
>
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-lg bg-accent-green/15 flex items-center justify-center">
<FileText size={18} className="text-accent-green" />
</div>
<div>
<p className="font-medium text-text-primary text-sm">JSON</p>
<p className="text-xs text-text-tertiary"></p>
</div>
</div>
</button>
<button
type="button"
className="p-3 rounded-xl border-2 border-border-subtle hover:border-accent-indigo hover:bg-accent-indigo/5 transition-all text-left"
>
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-lg bg-accent-indigo/15 flex items-center justify-center">
<FileText size={18} className="text-accent-indigo" />
</div>
<div>
<p className="font-medium text-text-primary text-sm">Excel</p>
<p className="text-xs text-text-tertiary"></p>
</div>
</div>
</button>
<button
type="button"
className="p-3 rounded-xl border-2 border-border-subtle hover:border-accent-indigo hover:bg-accent-indigo/5 transition-all text-left"
>
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-lg bg-[#2b579a]/15 flex items-center justify-center">
<FileText size={18} className="text-[#2b579a]" />
</div>
<div>
<p className="font-medium text-text-primary text-sm">Word</p>
<p className="text-xs text-text-tertiary"></p>
</div>
</div>
</button>
<button
type="button"
className="p-3 rounded-xl border-2 border-border-subtle hover:border-accent-indigo hover:bg-accent-indigo/5 transition-all text-left"
>
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-lg bg-accent-coral/15 flex items-center justify-center">
<FileText size={18} className="text-accent-coral" />
</div>
<div>
<p className="font-medium text-text-primary text-sm">PDF</p>
<p className="text-xs text-text-tertiary"></p>
</div>
</div>
</button>
</div>
</div>
<div className="p-3 rounded-xl bg-bg-elevated text-sm">
<div className="flex justify-between text-text-secondary mb-1">
<span></span>
<span className="text-text-primary">{downloadPlatform.rules.forbiddenWords} </span>
</div>
<div className="flex justify-between text-text-secondary">
<span></span>
<span className="text-text-primary">{downloadPlatform.rules.whitelist} </span>
</div>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowDownloadModal(false); setDownloadPlatform(null); }}>
</Button>
<Button>
<Download size={16} />
</Button>
</div>
</div>
)}
</Modal>
{/* 添加违禁词弹窗 */}
<Modal
isOpen={showAddWordModal}
onClose={() => { setShowAddWordModal(false); setNewWord(''); setBatchWords(''); }}
title="添加违禁词"
>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<select
value={newCategory}
onChange={(e) => setNewCategory(e.target.value)}
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"
>
{categoryOptions.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="flex gap-2">
<input
type="text"
value={newWord}
onChange={(e) => setNewWord(e.target.value)}
placeholder="输入违禁词"
className="flex-1 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"
/>
<Button onClick={handleAddWord} disabled={!newWord.trim()}>
</Button>
</div>
</div>
<div className="relative">
<div className="absolute inset-x-0 top-1/2 border-t border-border-subtle" />
<div className="relative flex justify-center">
<span className="bg-bg-card px-3 text-sm text-text-tertiary"></span>
</div>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2">
<Upload size={14} className="inline mr-1" />
</label>
<textarea
value={batchWords}
onChange={(e) => setBatchWords(e.target.value)}
placeholder="最好&#10;第一&#10;最佳&#10;..."
className="w-full h-32 px-4 py-3 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary placeholder-text-tertiary resize-none focus:outline-none focus:ring-2 focus:ring-accent-indigo font-mono text-sm"
/>
<div className="flex justify-between items-center mt-2">
<span className="text-xs text-text-tertiary">
{batchWords.split('\n').filter(w => w.trim()).length}
</span>
<Button onClick={handleBatchAdd} disabled={!batchWords.trim()}>
</Button>
</div>
</div>
</div>
</Modal>
{/* 添加竞品弹窗 */}
<Modal
isOpen={showAddCompetitorModal}
onClose={() => { setShowAddCompetitorModal(false); setNewCompetitor(''); }}
title="添加竞品"
>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<input
type="text"
value={newCompetitor}
onChange={(e) => setNewCompetitor(e.target.value)}
placeholder="输入竞品品牌名称"
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"
/>
</div>
<p className="text-sm text-text-tertiary">
AI将在视频中自动检测该品牌的Logo或名称出现
</p>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowAddCompetitorModal(false); setNewCompetitor(''); }}>
</Button>
<Button onClick={handleAddCompetitor} disabled={!newCompetitor.trim()}>
</Button>
</div>
</div>
</Modal>
{/* 添加白名单弹窗 */}
<Modal
isOpen={showAddWhitelistModal}
onClose={() => { setShowAddWhitelistModal(false); setNewWhitelistTerm(''); setNewWhitelistReason(''); }}
title="添加白名单"
>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<input
type="text"
value={newWhitelistTerm}
onChange={(e) => setNewWhitelistTerm(e.target.value)}
placeholder="输入需要豁免的词汇"
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"
/>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<input
type="text"
value={newWhitelistReason}
onChange={(e) => setNewWhitelistReason(e.target.value)}
placeholder="例如:品牌授权使用"
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"
/>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowAddWhitelistModal(false); setNewWhitelistTerm(''); setNewWhitelistReason(''); }}>
</Button>
<Button onClick={handleAddWhitelist} disabled={!newWhitelistTerm.trim()}>
</Button>
</div>
</div>
</Modal>
</div>
)
}