369 lines
16 KiB
TypeScript
369 lines
16 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { platformRulesApi, PlatformBrief, PlatformDetailResponse } from "@/lib/api/platform-rules";
|
|
|
|
const PRIORITY_COLORS: Record<string, string> = {
|
|
P0: "bg-red-100 text-red-800",
|
|
P1: "bg-yellow-100 text-yellow-800",
|
|
P2: "bg-green-100 text-green-800",
|
|
};
|
|
|
|
const SENSITIVITY_COLORS: Record<string, string> = {
|
|
high: "bg-red-100 text-red-800",
|
|
medium: "bg-yellow-100 text-yellow-800",
|
|
low: "bg-green-100 text-green-800",
|
|
};
|
|
|
|
export default function PlatformRulesPage() {
|
|
const [platforms, setPlatforms] = useState<PlatformBrief[]>([]);
|
|
const [selectedPlatform, setSelectedPlatform] = useState<string | null>(null);
|
|
const [platformDetail, setPlatformDetail] = useState<PlatformDetailResponse | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [detailLoading, setDetailLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
loadPlatforms();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (selectedPlatform) {
|
|
loadPlatformDetail(selectedPlatform);
|
|
}
|
|
}, [selectedPlatform]);
|
|
|
|
const loadPlatforms = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await platformRulesApi.listPlatforms();
|
|
setPlatforms(response.platforms);
|
|
if (response.platforms.length > 0 && !selectedPlatform) {
|
|
setSelectedPlatform(response.platforms[0].id);
|
|
}
|
|
} catch (error) {
|
|
console.error("加载平台列表失败:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const loadPlatformDetail = async (platformId: string) => {
|
|
try {
|
|
setDetailLoading(true);
|
|
const detail = await platformRulesApi.getPlatformDetail(platformId);
|
|
setPlatformDetail(detail);
|
|
} catch (error) {
|
|
console.error("加载平台详情失败:", error);
|
|
} finally {
|
|
setDetailLoading(false);
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-64">
|
|
<div className="text-muted-foreground">加载中...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h1 className="text-2xl font-bold tracking-tight">平台规则管理</h1>
|
|
<p className="text-muted-foreground">管理各内容平台的发布规则和配置</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
|
{/* 平台列表 */}
|
|
<Card className="lg:col-span-1">
|
|
<CardHeader>
|
|
<CardTitle className="text-lg">平台列表</CardTitle>
|
|
<CardDescription>共 {platforms.length} 个平台</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="p-0">
|
|
<div className="divide-y">
|
|
{platforms.map((platform) => (
|
|
<button
|
|
key={platform.id}
|
|
onClick={() => setSelectedPlatform(platform.id)}
|
|
className={`w-full px-4 py-3 text-left hover:bg-muted/50 transition-colors ${
|
|
selectedPlatform === platform.id ? "bg-muted" : ""
|
|
}`}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<span className="font-medium">{platform.name}</span>
|
|
<Badge className={PRIORITY_COLORS[platform.priority] || "bg-gray-100"}>
|
|
{platform.priority}
|
|
</Badge>
|
|
</div>
|
|
<div className="text-sm text-muted-foreground mt-1">
|
|
{platform.platform_type}
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 平台详情 */}
|
|
<Card className="lg:col-span-3">
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<CardTitle className="text-xl">
|
|
{platformDetail?.name || "选择平台"}
|
|
</CardTitle>
|
|
<CardDescription>
|
|
{platformDetail?.platform_type} - {platformDetail?.id}
|
|
</CardDescription>
|
|
</div>
|
|
{platformDetail && (
|
|
<div className="flex gap-2">
|
|
<Badge
|
|
className={
|
|
SENSITIVITY_COLORS[
|
|
platformDetail.ai_sensitivity.detection_level
|
|
] || "bg-gray-100"
|
|
}
|
|
>
|
|
AI检测: {platformDetail.ai_sensitivity.detection_level}
|
|
</Badge>
|
|
<Badge variant={platformDetail.enabled ? "default" : "secondary"}>
|
|
{platformDetail.enabled ? "已启用" : "已禁用"}
|
|
</Badge>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{detailLoading ? (
|
|
<div className="flex items-center justify-center h-64">
|
|
<div className="text-muted-foreground">加载详情中...</div>
|
|
</div>
|
|
) : !platformDetail ? (
|
|
<div className="flex items-center justify-center h-64">
|
|
<div className="text-muted-foreground">请选择一个平台查看详情</div>
|
|
</div>
|
|
) : (
|
|
<Tabs defaultValue="basic" className="space-y-4">
|
|
<TabsList>
|
|
<TabsTrigger value="basic">基础配置</TabsTrigger>
|
|
<TabsTrigger value="ai">AI检测</TabsTrigger>
|
|
<TabsTrigger value="sensitive">敏感词</TabsTrigger>
|
|
<TabsTrigger value="seo">SEO规则</TabsTrigger>
|
|
<TabsTrigger value="html">HTML规则</TabsTrigger>
|
|
<TabsTrigger value="publish">发布规则</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="basic" className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<h4 className="font-semibold mb-2">内容长度</h4>
|
|
<div className="text-sm space-y-1">
|
|
<p>最小: {platformDetail.content_length.min} 字</p>
|
|
<p>最大: {platformDetail.content_length.max} 字</p>
|
|
<p>推荐: {platformDetail.content_length.recommended} 字</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-semibold mb-2">结构偏好</h4>
|
|
<div className="text-sm space-y-1">
|
|
<p>引言: {platformDetail.structure_preference.has_intro ? "需要" : "不需要"}</p>
|
|
<p>结论: {platformDetail.structure_preference.has_conclusion ? "需要" : "不需要"}</p>
|
|
<p>目录: {platformDetail.structure_preference.has_toc ? "需要" : "不需要"}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">标题规则</h4>
|
|
<div className="text-sm space-y-1">
|
|
<p>长度: {platformDetail.title_rules.min_length} - {platformDetail.title_rules.max_length} 字</p>
|
|
<p>大小写: {platformDetail.title_rules.case_style}</p>
|
|
{platformDetail.title_rules.avoid_patterns.length > 0 && (
|
|
<p>避免: {platformDetail.title_rules.avoid_patterns.join(", ")}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">标签规则</h4>
|
|
<div className="text-sm space-y-1">
|
|
<p>数量: {platformDetail.tag_rules.min_tags} - {platformDetail.tag_rules.max_tags} 个</p>
|
|
<p>样式: {platformDetail.tag_rules.tag_style}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">最佳发布时间</h4>
|
|
<div className="flex flex-wrap gap-2">
|
|
{platformDetail.best_publish_times.map((time, i) => (
|
|
<Badge key={i} variant="outline">{time}</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">最佳发布日</h4>
|
|
<div className="flex flex-wrap gap-2">
|
|
{platformDetail.best_publish_days.map((day, i) => (
|
|
<Badge key={i} variant="outline">{day}</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="ai" className="space-y-4">
|
|
<div>
|
|
<h4 className="font-semibold mb-2">AI检测级别</h4>
|
|
<Badge className={SENSITIVITY_COLORS[platformDetail.ai_sensitivity.detection_level]}>
|
|
{platformDetail.ai_sensitivity.detection_level.toUpperCase()}
|
|
</Badge>
|
|
<p className="text-sm text-muted-foreground mt-2">
|
|
{platformDetail.ai_sensitivity.humanization_required
|
|
? "需要去AI化处理"
|
|
: "无需去AI化处理"}
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">禁用模式</h4>
|
|
{platformDetail.ai_sensitivity.banned_patterns.length > 0 ? (
|
|
<div className="flex flex-wrap gap-1">
|
|
{platformDetail.ai_sensitivity.banned_patterns.map((pattern, i) => (
|
|
<Badge key={i} variant="destructive" className="text-xs">
|
|
{pattern}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">无</p>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">安全模式</h4>
|
|
{platformDetail.ai_sensitivity.safe_patterns.length > 0 ? (
|
|
<div className="flex flex-wrap gap-1">
|
|
{platformDetail.ai_sensitivity.safe_patterns.map((pattern, i) => (
|
|
<Badge key={i} variant="default" className="text-xs bg-green-600">
|
|
{pattern}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">无</p>
|
|
)}
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="sensitive" className="space-y-4">
|
|
<div>
|
|
<h4 className="font-semibold mb-2">敏感词配置</h4>
|
|
<div className="text-sm space-y-2">
|
|
<p>需要检查: {platformDetail.sensitive_words.check_required ? "是" : "否"}</p>
|
|
<p>自动过滤: {platformDetail.sensitive_words.auto_filter ? "是" : "否"}</p>
|
|
<p>最大容忍度: {platformDetail.sensitive_words.max_tolerance}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">敏感词分类</h4>
|
|
<div className="flex flex-wrap gap-2">
|
|
{platformDetail.sensitive_words.categories.map((cat, i) => (
|
|
<Badge key={i} variant="outline">{cat}</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="seo" className="space-y-4">
|
|
<div>
|
|
<h4 className="font-semibold mb-2">关键词密度</h4>
|
|
<div className="text-sm space-y-1">
|
|
<p>范围: {platformDetail.seo_rules.keyword_density.min}% - {platformDetail.seo_rules.keyword_density.max}%</p>
|
|
<p>推荐: {platformDetail.seo_rules.keyword_density.recommended}%</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">关键词位置</h4>
|
|
<div className="flex flex-wrap gap-2">
|
|
{platformDetail.seo_rules.keyword_position.map((pos, i) => (
|
|
<Badge key={i} variant="outline">{pos}</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">内链规则</h4>
|
|
<div className="text-sm space-y-1">
|
|
<p>
|
|
内链数量: {platformDetail.seo_rules.internal_links.min || 0} - {platformDetail.seo_rules.internal_links.max || 0}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="html" className="space-y-4">
|
|
<div>
|
|
<h4 className="font-semibold mb-2">支持的HTML标签</h4>
|
|
<div className="flex flex-wrap gap-1">
|
|
{platformDetail.html_rules.supported_tags.map((tag, i) => (
|
|
<Badge key={i} variant="default" className="text-xs">
|
|
<{tag}>
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">禁用的HTML标签</h4>
|
|
<div className="flex flex-wrap gap-1">
|
|
{platformDetail.html_rules.banned_tags.map((tag, i) => (
|
|
<Badge key={i} variant="destructive" className="text-xs">
|
|
<{tag}>
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">媒体支持</h4>
|
|
<div className="text-sm space-y-1">
|
|
<p>图片: {platformDetail.html_rules.image_support ? "支持" : "不支持"}</p>
|
|
<p>视频: {platformDetail.html_rules.video_support ? "支持" : "不支持"}</p>
|
|
<p>代码块: {platformDetail.html_rules.code_block_support ? "支持" : "不支持"}</p>
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="publish" className="space-y-4">
|
|
<div>
|
|
<h4 className="font-semibold mb-2">发布规则</h4>
|
|
<div className="text-sm space-y-2">
|
|
<p>自动发布: {platformDetail.publish_rules.auto_publish ? "是" : "否"}</p>
|
|
<p>需要审核: {platformDetail.publish_rules.require_review ? "是" : "否"}</p>
|
|
<p>发布时机: {platformDetail.publish_rules.publish_timing}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold mb-2">内容风格</h4>
|
|
<p className="text-sm">{platformDetail.content_style}</p>
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|