Files
users/components/data-integration/data-collection-settings.tsx

699 lines
29 KiB
TypeScript
Raw Normal View History

"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Switch } from "@/components/ui/switch"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Badge } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Plus, Trash2, Clock, RefreshCw, Database, Server, FileText, Settings, HelpCircle } from "lucide-react"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
interface CollectionTask {
id: string
name: string
source: string
type: string
schedule: string
lastRun: string | null
nextRun: string | null
status: "active" | "paused" | "error" | "completed"
recordsCollected: number
}
export function DataCollectionSettings() {
const [activeTab, setActiveTab] = useState("tasks")
const [isAddingTask, setIsAddingTask] = useState(false)
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null)
// 模拟数据采集任务
const [collectionTasks, setCollectionTasks] = useState<CollectionTask[]>([
{
id: "1",
name: "用户基本信息采集",
source: "CRM系统",
type: "数据库",
schedule: "每日 00:00",
lastRun: "2023-07-20 00:00",
nextRun: "2023-07-21 00:00",
status: "active",
recordsCollected: 125678,
},
{
id: "2",
name: "用户行为数据采集",
source: "行为分析平台",
type: "API",
schedule: "每小时",
lastRun: "2023-07-20 15:00",
nextRun: "2023-07-20 16:00",
status: "active",
recordsCollected: 458921,
},
{
id: "3",
name: "社交媒体数据采集",
source: "MCP SERVER",
type: "AI工具",
schedule: "每日 02:00",
lastRun: "2023-07-20 02:00",
nextRun: "2023-07-21 02:00",
status: "active",
recordsCollected: 87452,
},
{
id: "4",
name: "历史交易数据导入",
source: "交易系统",
type: "文件",
schedule: "手动",
lastRun: "2023-07-15 10:30",
nextRun: null,
status: "completed",
recordsCollected: 254789,
},
{
id: "5",
name: "设备信息采集",
source: "设备管理系统",
type: "API",
schedule: "每日 03:00",
lastRun: "2023-07-20 03:00",
nextRun: "2023-07-21 03:00",
status: "error",
recordsCollected: 12547,
},
])
// 模拟数据源
const dataSources = [
{ id: "crm", name: "CRM系统", type: "数据库" },
{ id: "behavior", name: "行为分析平台", type: "API" },
{ id: "mcp", name: "MCP SERVER", type: "AI工具" },
{ id: "transaction", name: "交易系统", type: "文件" },
{ id: "device", name: "设备管理系统", type: "API" },
{ id: "social", name: "社交媒体平台", type: "API" },
{ id: "ecommerce", name: "电商平台", type: "API" },
]
// 模拟采集类型
const collectionTypes = [
{ id: "full", name: "全量采集", description: "采集所有数据" },
{ id: "incremental", name: "增量采集", description: "仅采集新增或变更的数据" },
{ id: "scheduled", name: "定时采集", description: "按照设定的时间计划采集数据" },
{ id: "event", name: "事件触发采集", description: "在特定事件发生时采集数据" },
{ id: "manual", name: "手动采集", description: "手动触发数据采集" },
]
const getStatusBadge = (status: CollectionTask["status"]) => {
switch (status) {
case "active":
return <Badge className="bg-green-100 text-green-800"></Badge>
case "paused":
return <Badge className="bg-yellow-100 text-yellow-800"></Badge>
case "error":
return <Badge className="bg-red-100 text-red-800"></Badge>
case "completed":
return <Badge className="bg-blue-100 text-blue-800"></Badge>
}
}
const getSourceIcon = (type: string) => {
switch (type) {
case "数据库":
return <Database className="h-4 w-4 text-blue-500" />
case "API":
return <Server className="h-4 w-4 text-purple-500" />
case "文件":
return <FileText className="h-4 w-4 text-green-500" />
case "AI工具":
return <Settings className="h-4 w-4 text-orange-500" />
default:
return <Database className="h-4 w-4" />
}
}
const addTask = (task: Omit<CollectionTask, "id" | "lastRun" | "recordsCollected">) => {
const newTask: CollectionTask = {
id: `task-${Date.now()}`,
...task,
lastRun: null,
recordsCollected: 0,
}
setCollectionTasks([...collectionTasks, newTask])
setIsAddingTask(false)
}
const deleteTask = (id: string) => {
setCollectionTasks(collectionTasks.filter((task) => task.id !== id))
}
const toggleTaskStatus = (id: string) => {
setCollectionTasks(
collectionTasks.map((task) => {
if (task.id === id) {
return {
...task,
status: task.status === "active" ? "paused" : "active",
}
}
return task
}),
)
}
const selectedTask = selectedTaskId ? collectionTasks.find((task) => task.id === selectedTaskId) : null
return (
<div className="space-y-4">
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
<TabsList>
<TabsTrigger value="tasks"></TabsTrigger>
<TabsTrigger value="sources"></TabsTrigger>
<TabsTrigger value="settings"></TabsTrigger>
</TabsList>
<TabsContent value="tasks" className="space-y-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<Badge className="bg-blue-100 text-blue-800"> {collectionTasks.length} </Badge>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<HelpCircle className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p className="max-w-xs">
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<Button onClick={() => setIsAddingTask(true)}>
<Plus className="mr-2 h-4 w-4" />
</Button>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{collectionTasks.map((task) => (
<TableRow key={task.id}>
<TableCell className="font-medium">{task.name}</TableCell>
<TableCell>
<div className="flex items-center gap-1">
{getSourceIcon(task.type)}
<span>{task.source}</span>
</div>
</TableCell>
<TableCell>{task.schedule}</TableCell>
<TableCell>{task.lastRun || "从未运行"}</TableCell>
<TableCell>{task.nextRun || "无计划"}</TableCell>
<TableCell>{getStatusBadge(task.status)}</TableCell>
<TableCell>{task.recordsCollected.toLocaleString()}</TableCell>
<TableCell className="text-right">
<div className="flex justify-end space-x-2">
<Button
variant="ghost"
size="icon"
onClick={() => toggleTaskStatus(task.id)}
disabled={task.status === "completed"}
>
{task.status === "active" ? (
<Clock className="h-4 w-4 text-yellow-500" />
) : (
<RefreshCw className="h-4 w-4 text-green-500" />
)}
</Button>
<Button variant="ghost" size="icon" onClick={() => setSelectedTaskId(task.id)}>
<Settings className="h-4 w-4" />
</Button>
<Button variant="ghost" size="icon" onClick={() => deleteTask(task.id)}>
<Trash2 className="h-4 w-4 text-red-500" />
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</TabsContent>
<TabsContent value="sources" className="space-y-4">
<div className="flex justify-between items-center">
<h3 className="text-lg font-medium"></h3>
<Button variant="outline"></Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{dataSources.map((source) => (
<Card key={source.id} className="border shadow-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{getSourceIcon(source.type)}
<div>
<h4 className="font-medium">{source.name}</h4>
<p className="text-sm text-muted-foreground">{source.type}</p>
</div>
</div>
<Button variant="ghost" size="icon">
<Settings className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
))}
</div>
<Card className="mt-4">
<CardHeader>
<CardTitle>MCP SERVER </CardTitle>
<CardDescription>MCP SERVER AI工具的采集参数</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="mcp-server-url"></Label>
<Input id="mcp-server-url" defaultValue="https://mcp.example.com/api" />
</div>
<div className="space-y-2">
<Label htmlFor="mcp-api-key">API密钥</Label>
<Input id="mcp-api-key" type="password" defaultValue="••••••••••••••••" />
</div>
</div>
<div className="space-y-2">
<Label htmlFor="mcp-model">AI模型选择</Label>
<Select defaultValue="gpt-4">
<SelectTrigger id="mcp-model">
<SelectValue placeholder="选择AI模型" />
</SelectTrigger>
<SelectContent>
<SelectItem value="gpt-4">GPT-4</SelectItem>
<SelectItem value="gpt-3.5-turbo">GPT-3.5 Turbo</SelectItem>
<SelectItem value="claude-2">Claude 2</SelectItem>
<SelectItem value="llama-2">Llama 2</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="mcp-auto-process"></Label>
<Switch id="mcp-auto-process" defaultChecked />
</div>
<p className="text-sm text-muted-foreground">
使AI处理采集的数据
</p>
</div>
<Separator />
<div className="space-y-2">
<Label></Label>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center space-x-2">
<Switch id="collect-profile" defaultChecked />
<Label htmlFor="collect-profile"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="collect-posts" defaultChecked />
<Label htmlFor="collect-posts"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="collect-interactions" defaultChecked />
<Label htmlFor="collect-interactions"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="collect-followers" defaultChecked />
<Label htmlFor="collect-followers"></Label>
</div>
</div>
</div>
<div className="flex justify-end">
<Button>MCP配置</Button>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="settings" className="space-y-4">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="enable-collection"></Label>
<Switch id="enable-collection" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="space-y-2">
<Label htmlFor="collection-threads">线</Label>
<Select defaultValue="5">
<SelectTrigger id="collection-threads">
<SelectValue placeholder="选择并发线程数" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="20">20</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
</p>
</div>
<div className="space-y-2">
<Label htmlFor="retry-attempts"></Label>
<Select defaultValue="3">
<SelectTrigger id="retry-attempts">
<SelectValue placeholder="选择重试次数" />
</SelectTrigger>
<SelectContent>
<SelectItem value="0"></SelectItem>
<SelectItem value="1">1</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="retry-delay"></Label>
<Input id="retry-delay" type="number" defaultValue="60" />
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="data-deduplication"></Label>
<Switch id="data-deduplication" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="data-validation"></Label>
<Switch id="data-validation" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="notification"></Label>
<Switch id="notification" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="flex justify-end">
<Button></Button>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</CardContent>
</Card>
{/* 添加采集任务对话框 */}
<Dialog open={isAddingTask} onOpenChange={setIsAddingTask}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="task-name" className="text-right">
</Label>
<Input id="task-name" className="col-span-3" placeholder="例如:用户基本信息采集" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="data-source" className="text-right">
</Label>
<Select>
<SelectTrigger id="data-source" className="col-span-3">
<SelectValue placeholder="选择数据源" />
</SelectTrigger>
<SelectContent>
{dataSources.map((source) => (
<SelectItem key={source.id} value={source.id}>
{source.name} ({source.type})
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="collection-type" className="text-right">
</Label>
<Select>
<SelectTrigger id="collection-type" className="col-span-3">
<SelectValue placeholder="选择采集类型" />
</SelectTrigger>
<SelectContent>
{collectionTypes.map((type) => (
<SelectItem key={type.id} value={type.id}>
{type.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="schedule" className="text-right">
</Label>
<Select>
<SelectTrigger id="schedule" className="col-span-3">
<SelectValue placeholder="选择采集频率" />
</SelectTrigger>
<SelectContent>
<SelectItem value="hourly"></SelectItem>
<SelectItem value="daily"></SelectItem>
<SelectItem value="weekly"></SelectItem>
<SelectItem value="monthly"></SelectItem>
<SelectItem value="manual"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="time" className="text-right">
</Label>
<Input id="time" type="time" className="col-span-3" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<div className="text-right">
<Label htmlFor="auto-process"></Label>
</div>
<div className="flex items-center space-x-2 col-span-3">
<Switch id="auto-process" />
<Label htmlFor="auto-process"></Label>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsAddingTask(false)}>
</Button>
<Button
onClick={() =>
addTask({
name: "新建采集任务",
source: "CRM系统",
type: "数据库",
schedule: "每日 00:00",
nextRun: "2023-07-21 00:00",
status: "active",
})
}
>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 任务详情对话框 */}
<Dialog open={!!selectedTaskId} onOpenChange={(open) => !open && setSelectedTaskId(null)}>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
{selectedTask && (
<div className="space-y-4 py-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.name}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.source}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.schedule}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<div>{getStatusBadge(selectedTask.status)}</div>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.lastRun || "从未运行"}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.nextRun || "无计划"}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.recordsCollected.toLocaleString()}</p>
</div>
</div>
<Separator />
<div className="space-y-2">
<Label></Label>
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>ID</TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell>IMEI</TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
<div className="space-y-2">
<Label></Label>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
</div>
</div>
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={() => setSelectedTaskId(null)}>
</Button>
<Button variant="outline" onClick={() => {}}>
</Button>
<Button onClick={() => setSelectedTaskId(null)}></Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}