"use client"; import { useState, useEffect } from "react"; import { useSession } from "next-auth/react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Label } from "@/components/ui/label"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { detectionApi, type DetectionTask } from "@/lib/api/detection"; import type { QueryListResponse, ApiQueryItem } from "@/lib/api/queries"; import { fetchWithAuth } from "@/lib/api/client"; import { PLATFORM_MAP, PLATFORMS } from "@/lib/platforms"; import { LoadingState, ErrorState, EmptyState } from "@/components/ui/api-states"; import { Plus, Trash2, Play, Loader2, ScanSearch, CheckCircle, } from "lucide-react"; const FREQUENCY_MAP: Record = { daily: "每日", weekly: "每周", hourly: "每小时", }; const STATUS_CONFIG: Record = { active: { label: "运行中", className: "bg-emerald-100 text-emerald-700 hover:bg-emerald-100" }, paused: { label: "已暂停", className: "bg-amber-100 text-amber-700 hover:bg-amber-100" }, completed: { label: "已完成", className: "bg-blue-100 text-blue-700 hover:bg-blue-100" }, }; interface CreateFormData { query_id: string; platforms: string[]; frequency: string; } const emptyForm: CreateFormData = { query_id: "", platforms: [], frequency: "weekly", }; export default function DetectionPage() { const { data: session } = useSession(); const token = (session as { accessToken?: string })?.accessToken; const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [queries, setQueries] = useState([]); const [dialogOpen, setDialogOpen] = useState(false); const [formData, setFormData] = useState(emptyForm); const [saving, setSaving] = useState(false); const [formErrors, setFormErrors] = useState>({}); const [mutationError, setMutationError] = useState(null); const [successMsg, setSuccessMsg] = useState(null); const [actionLoading, setActionLoading] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [deletingId, setDeletingId] = useState(null); const [deleting, setDeleting] = useState(false); async function loadTasks() { if (!token) return; try { setLoading(true); setError(null); const result = await detectionApi.listTasks(token); setTasks(result.items ?? []); } catch (err) { setError(err instanceof Error ? err.message : "获取检测任务失败"); } finally { setLoading(false); } } async function loadQueries() { try { const result = await fetchWithAuth("/api/v1/queries/") as QueryListResponse; setQueries(result.items ?? []); } catch { // ignore } } useEffect(() => { if (token) loadTasks(); }, [token]); function showSuccess(msg: string) { setSuccessMsg(msg); setTimeout(() => setSuccessMsg(null), 3000); } function openAddDialog() { setFormData(emptyForm); setFormErrors({}); setMutationError(null); setDialogOpen(true); loadQueries(); } function togglePlatform(platform: string) { setFormData((prev) => { const platforms = prev.platforms.includes(platform) ? prev.platforms.filter((p) => p !== platform) : [...prev.platforms, platform]; return { ...prev, platforms }; }); } function validateForm(): boolean { const errors: Record = {}; if (!formData.query_id) errors.query_id = "请选择查询词"; if (formData.platforms.length === 0) errors.platforms = "请至少选择一个平台"; setFormErrors(errors); return Object.keys(errors).length === 0; } async function handleCreate() { if (!validateForm() || !token) return; try { setSaving(true); setMutationError(null); await detectionApi.createTask(token, { query_id: formData.query_id, platforms: formData.platforms, frequency: formData.frequency, }); setDialogOpen(false); showSuccess("创建成功"); loadTasks(); } catch (err) { setMutationError(err instanceof Error ? err.message : "创建失败"); } finally { setSaving(false); } } function openDeleteDialog(id: string) { setDeletingId(id); setDeleteDialogOpen(true); } async function handleDelete() { if (!deletingId || !token) return; try { setDeleting(true); await detectionApi.deleteTask(token, deletingId); setDeleteDialogOpen(false); setDeletingId(null); showSuccess("删除成功"); loadTasks(); } catch (err) { setMutationError(err instanceof Error ? err.message : "删除失败"); } finally { setDeleting(false); } } async function handleTrigger(taskId: string) { if (!token) return; setActionLoading(taskId); setMutationError(null); try { await detectionApi.triggerTask(token, taskId); showSuccess("检测已触发"); loadTasks(); } catch (err) { setMutationError(err instanceof Error ? err.message : "触发检测失败"); } finally { setActionLoading(null); } } if (loading) { return (

检测任务

管理AI搜索检测任务

); } if (error) { return (

检测任务

管理AI搜索检测任务

); } return (

检测任务

管理AI搜索检测任务

新建检测任务 配置新的AI搜索检测任务
{formErrors.query_id && (

{formErrors.query_id}

)}
{PLATFORMS.map((p) => ( ))}
{formErrors.platforms && (

{formErrors.platforms}

)}
{mutationError && (

{mutationError}

)}
{successMsg && (
{successMsg}
)} {mutationError && !dialogOpen && (
{mutationError}
)} {tasks.length === 0 ? ( } message="暂无检测任务" description="点击右上角按钮创建您的第一个检测任务" /> ) : ( 检测任务列表
查询词 平台 频率 状态 上次运行 下次运行 操作 {tasks.map((task) => { const matchedQuery = queries.find((q) => q.id === task.query_id); const statusCfg = STATUS_CONFIG[task.status] ?? { label: task.status, className: "bg-gray-100 text-gray-600", }; return ( {matchedQuery?.keyword ?? task.query_id}
{task.platforms.map((p) => ( {PLATFORM_MAP[p] || p} ))}
{FREQUENCY_MAP[task.frequency] || task.frequency} {statusCfg.label} {task.last_run_at ? new Date(task.last_run_at).toLocaleString("zh-CN") : "从未"} {task.next_run_at ? new Date(task.next_run_at).toLocaleString("zh-CN") : "—"}
); })}
)} 确认删除 删除后无法恢复,确定要删除这个检测任务吗?
); }