- 新增 frontend/lib/platforms.ts 共享平台配置模块 - 支持6个平台: 抖音、小红书、B站、快手、微博、微信视频号 - 品牌方终端: 项目看板、项目详情、终审台列表添加平台显示 - 代理商终端: 工作台概览、审核台、Brief配置、达人管理、 数据报表、消息中心、申诉处理添加平台显示 - 达人端: 任务列表添加平台显示 - 统一使用彩色头部条样式展示平台信息 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
957 lines
40 KiB
TypeScript
957 lines
40 KiB
TypeScript
'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 格式:包含 forbiddenWords、whitelist 字段的对象</li>
|
||
<li>• Excel 格式:第一列为词汇,第二列为分类(可选)</li>
|
||
<li>• Word / PDF:AI 将自动识别并提取规则内容</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="最好 第一 最佳 ..."
|
||
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>
|
||
)
|
||
}
|