import { useState } from "react"; import { trpc } from "@/lib/trpc"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { toast } from "sonner"; import { Plus, Play, RefreshCw, Activity, Clock, CheckCircle2, XCircle, Wifi, WifiOff } from "lucide-react"; const JURISDICTIONS = ["CN", "HK", "US", "EU", "SG", "AE", "ALL"]; const CATEGORIES = ["regulation", "trade_rule", "credit", "asset_document", "court_judgment", "tax_rule"]; const FREQUENCIES = ["realtime", "hourly", "daily", "weekly", "monthly"]; export default function Crawlers() { const [createOpen, setCreateOpen] = useState(false); const [form, setForm] = useState({ name: "", jurisdiction: "", type: "external" as "internal" | "external", source: "", category: "", frequency: "daily" }); const [selectedCrawler, setSelectedCrawler] = useState(null); const utils = trpc.useUtils(); const { data: crawlers, isLoading, refetch } = trpc.crawler.list.useQuery(); const { data: logs } = trpc.crawler.logs.useQuery({ crawlerId: selectedCrawler || undefined, limit: 30 }); const triggerMutation = trpc.crawler.trigger.useMutation({ onSuccess: (data) => { toast.success((data as any).message); utils.crawler.list.invalidate(); utils.crawler.logs.invalidate(); }, onError: (e) => toast.error(e.message), }); const createMutation = trpc.crawler.create.useMutation({ onSuccess: () => { toast.success("采集器创建成功"); setCreateOpen(false); utils.crawler.list.invalidate(); setForm({ name: "", jurisdiction: "", type: "external", source: "", category: "", frequency: "daily" }); }, onError: (e) => toast.error(e.message), }); const crawlerList = (crawlers as any[]) || []; const logList = (logs as any[]) || []; return (

采集器监控

管理内部/外部数据采集通道

新增采集器
setForm(f => ({ ...f, name: e.target.value }))} className="bg-input border-border/50 h-9" placeholder="例:JP-FSA法规采集器" />
setForm(f => ({ ...f, source: e.target.value }))} className="bg-input border-border/50 h-9" placeholder="https://www.fsa.go.jp" />
{/* Crawler List */}
{isLoading ? (
加载中...
) : crawlerList.map((crawler: any) => ( setSelectedCrawler(crawler._id?.toString())} >
{crawler.type === "internal" ? : }

{crawler.name}

{crawler.jurisdiction}

{crawler.source}

{crawler.frequency} {crawler.successRate}% 采集 {crawler.totalCollected} 条 {crawler.lastRun && 最后运行: {new Date(crawler.lastRun).toLocaleDateString("zh-CN")}}
{crawler.status === "active" ? "运行中" : "已停止"}
))}
{/* Logs Panel */} {selectedCrawler ? "采集日志" : "选择采集器查看日志"} {logList.length === 0 ? (
{selectedCrawler ? "暂无日志记录" : "点击左侧采集器查看日志"}
) : (
{logList.map((log: any, i: number) => (
{log.status === "triggered" ? : log.status === "success" ? : } {log.action}

{log.message}

{new Date(log.timestamp).toLocaleString("zh-CN")}

))}
)}
); }