Files
users/components/data-integration/ai-analysis-tools.tsx

1157 lines
47 KiB
TypeScript
Raw Normal View History

"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
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 { Checkbox } from "@/components/ui/checkbox"
import { Badge } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import {
Brain,
Lightbulb,
BarChart,
PieChart,
Users,
Tag,
Play,
Settings,
Search,
Filter,
Copy,
Check,
Zap,
Plus,
Download,
} from "lucide-react"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog"
import { useToast } from "@/components/ui/use-toast"
import { Textarea } from "@/components/ui/textarea"
interface AIAnalysisTask {
id: string
name: string
type: string
status: "pending" | "running" | "completed" | "failed"
progress: number
createdAt: string
completedAt: string | null
dataSource: string
parameters: Record<string, any>
result: any | null
}
interface AIModel {
id: string
name: string
provider: string
type: string
capabilities: string[]
apiEndpoint: string
apiKey: string
status: "active" | "inactive"
}
export function AIAnalysisTools() {
const { toast } = useToast()
const [activeTab, setActiveTab] = useState("analysis-tasks")
const [isCreatingTask, setIsCreatingTask] = useState(false)
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null)
const [isAddingModel, setIsAddingModel] = useState(false)
const [copiedText, setCopiedText] = useState<string | null>(null)
// 模拟AI分析任务
const [aiTasks, setAiTasks] = useState<AIAnalysisTask[]>([
{
id: "task-1",
name: "用户行为分析",
type: "behavior-analysis",
status: "completed",
progress: 100,
createdAt: "2023-07-20 10:15",
completedAt: "2023-07-20 10:25",
dataSource: "行为分析平台",
parameters: {
userSegment: "高价值用户",
timeRange: "近30天",
analysisDepth: "深度",
},
result: {
summary: "高价值用户在过去30天内主要活跃在晚间时段偏好浏览科技和旅游类内容平均会话时长较长。",
insights: [
"用户主要在晚上9点至11点活跃",
"科技类内容点击率高于平均水平58%",
"平均会话时长为8.5分钟高于普通用户3.2分钟",
"移动端访问占比87%其中iOS设备占65%",
],
recommendations: [
"在晚间时段推送个性化内容",
"增加科技和旅游类内容的推荐权重",
"针对长会话时长优化页面加载速度",
"优化iOS端用户体验",
],
},
},
{
id: "task-2",
name: "用户分群自动发现",
type: "segment-discovery",
status: "completed",
progress: 100,
createdAt: "2023-07-19 14:30",
completedAt: "2023-07-19 15:10",
dataSource: "用户数据库",
parameters: {
clusteringMethod: "K-Means",
features: ["消费行为", "活跃度", "内容偏好"],
maxClusters: 8,
},
result: {
summary: "系统自动发现了5个明显的用户分群其中包括高消费低活跃、高活跃低消费等特征明显的群体。",
clusters: [
{
name: "高消费科技爱好者",
size: 12500,
characteristics: ["高消费", "科技内容偏好", "中等活跃度"],
},
{
name: "活跃社交用户",
size: 28900,
characteristics: ["高活跃度", "社交内容偏好", "中等消费"],
},
{
name: "奢侈品偶尔购买者",
size: 8700,
characteristics: ["低活跃度", "高单次消费", "奢侈品偏好"],
},
{
name: "日常必需品购买者",
size: 45600,
characteristics: ["高频次低金额消费", "中等活跃度", "生活类内容偏好"],
},
{
name: "休眠用户",
size: 15400,
characteristics: ["极低活跃度", "低消费", "无明显内容偏好"],
},
],
},
},
{
id: "task-3",
name: "用户流失预测",
type: "churn-prediction",
status: "running",
progress: 65,
createdAt: "2023-07-21 09:45",
completedAt: null,
dataSource: "用户数据库 & 行为分析平台",
parameters: {
predictionHorizon: "30天",
modelType: "随机森林",
features: ["活跃度", "消费频率", "客服交互", "产品使用情况"],
},
result: null,
},
{
id: "task-4",
name: "营销策略生成",
type: "strategy-generation",
status: "pending",
progress: 0,
createdAt: "2023-07-21 10:30",
completedAt: null,
dataSource: "用户画像 & 行为分析",
parameters: {
targetSegment: "流失风险用户",
campaignGoal: "提高留存率",
budgetConstraint: "中等",
channelPreference: ["短信", "应用内推送", "邮件"],
},
result: null,
},
{
id: "task-5",
name: "自动标签生成",
type: "tag-generation",
status: "completed",
progress: 100,
createdAt: "2023-07-18 11:20",
completedAt: "2023-07-18 12:05",
dataSource: "用户数据库 & 行为分析平台",
parameters: {
tagCategories: ["兴趣", "消费习惯", "活跃模式"],
minConfidence: 0.7,
maxTagsPerUser: 10,
},
result: {
summary: "系统为85%的用户生成了新标签平均每用户5.3个标签。",
tagCategories: [
{
name: "兴趣标签",
count: 28,
coverage: "92%",
examples: ["科技爱好者", "旅游达人", "美食家", "运动健身"],
},
{
name: "消费习惯",
count: 15,
coverage: "88%",
examples: ["奢侈品偏好", "理性消费", "冲动购物", "价格敏感"],
},
{
name: "活跃模式",
count: 12,
coverage: "95%",
examples: ["工作时间活跃", "夜间活跃", "周末活跃", "低频高质"],
},
],
},
},
])
// 模拟AI模型
const [aiModels, setAiModels] = useState<AIModel[]>([
{
id: "model-1",
name: "GPT-4",
provider: "OpenAI",
type: "大语言模型",
capabilities: ["文本生成", "内容分析", "情感分析", "摘要生成"],
apiEndpoint: "https://api.openai.com/v1/chat/completions",
apiKey: "sk-***********",
status: "active",
},
{
id: "model-2",
name: "Claude 2",
provider: "Anthropic",
type: "大语言模型",
capabilities: ["文本生成", "内容分析", "代码生成", "问答"],
apiEndpoint: "https://api.anthropic.com/v1/complete",
apiKey: "sk-ant-***********",
status: "active",
},
{
id: "model-3",
name: "MCP SERVER AI",
provider: "内部服务",
type: "专用分析模型",
capabilities: ["用户行为分析", "分群发现", "标签生成", "预测模型"],
apiEndpoint: "https://mcp.example.com/api/analyze",
apiKey: "mcp-***********",
status: "active",
},
{
id: "model-4",
name: "DALL-E 3",
provider: "OpenAI",
type: "图像生成模型",
capabilities: ["图像生成", "图像编辑", "风格迁移"],
apiEndpoint: "https://api.openai.com/v1/images/generations",
apiKey: "sk-***********",
status: "inactive",
},
])
// 模拟分析模板
const analysisTemplates = [
{
id: "template-1",
name: "用户行为分析",
description: "分析用户的行为模式、偏好和习惯",
type: "behavior-analysis",
parameters: {
userSegment: "所有用户",
timeRange: "近30天",
analysisDepth: "标准",
},
},
{
id: "template-2",
name: "用户分群发现",
description: "自动发现数据中的用户分群",
type: "segment-discovery",
parameters: {
clusteringMethod: "K-Means",
features: ["消费行为", "活跃度", "内容偏好"],
maxClusters: 8,
},
},
{
id: "template-3",
name: "用户流失预测",
description: "预测未来可能流失的用户",
type: "churn-prediction",
parameters: {
predictionHorizon: "30天",
modelType: "随机森林",
features: ["活跃度", "消费频率", "客服交互", "产品使用情况"],
},
},
{
id: "template-4",
name: "营销策略生成",
description: "为特定用户分群生成营销策略",
type: "strategy-generation",
parameters: {
targetSegment: "流失风险用户",
campaignGoal: "提高留存率",
budgetConstraint: "中等",
channelPreference: ["短信", "应用内推送", "邮件"],
},
},
{
id: "template-5",
name: "自动标签生成",
description: "基于用户数据自动生成用户标签",
type: "tag-generation",
parameters: {
tagCategories: ["兴趣", "消费习惯", "活跃模式"],
minConfidence: 0.7,
maxTagsPerUser: 10,
},
},
]
const getStatusBadge = (status: string) => {
switch (status) {
case "pending":
return <Badge className="bg-gray-100 text-gray-800"></Badge>
case "running":
return <Badge className="bg-blue-100 text-blue-800"></Badge>
case "completed":
return <Badge className="bg-green-100 text-green-800"></Badge>
case "failed":
return <Badge className="bg-red-100 text-red-800"></Badge>
case "active":
return <Badge className="bg-green-100 text-green-800"></Badge>
case "inactive":
return <Badge className="bg-gray-100 text-gray-800"></Badge>
default:
return <Badge></Badge>
}
}
const getTaskTypeIcon = (type: string) => {
switch (type) {
case "behavior-analysis":
return <Users className="h-5 w-5 text-blue-500" />
case "segment-discovery":
return <PieChart className="h-5 w-5 text-purple-500" />
case "churn-prediction":
return <BarChart className="h-5 w-5 text-orange-500" />
case "strategy-generation":
return <Lightbulb className="h-5 w-5 text-yellow-500" />
case "tag-generation":
return <Tag className="h-5 w-5 text-green-500" />
default:
return <Brain className="h-5 w-5" />
}
}
const selectedTask = selectedTaskId ? aiTasks.find((task) => task.id === selectedTaskId) : null
const handleCopyText = (text: string) => {
navigator.clipboard.writeText(text)
setCopiedText(text)
toast({
title: "已复制到剪贴板",
description: "文本已成功复制到剪贴板",
})
setTimeout(() => setCopiedText(null), 2000)
}
const toggleModelStatus = (id: string) => {
setAiModels(
aiModels.map((model) => {
if (model.id === id) {
return {
...model,
status: model.status === "active" ? "inactive" : "active",
}
}
return model
}),
)
}
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
<h2 className="text-2xl font-bold">AI分析工具</h2>
<div className="flex items-center gap-2">
<Button variant="outline">
<Settings className="mr-2 h-4 w-4" />
AI设置
</Button>
<Button onClick={() => setIsCreatingTask(true)}>
<Brain className="mr-2 h-4 w-4" />
</Button>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
<TabsList>
<TabsTrigger value="analysis-tasks"></TabsTrigger>
<TabsTrigger value="ai-models">AI模型</TabsTrigger>
<TabsTrigger value="templates"></TabsTrigger>
<TabsTrigger value="settings">AI设置</TabsTrigger>
</TabsList>
<TabsContent value="analysis-tasks" className="space-y-4">
<Card>
<CardHeader>
<div className="flex justify-between items-center">
<CardTitle>AI分析任务</CardTitle>
<div className="flex items-center gap-2">
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input type="text" placeholder="搜索任务..." className="pl-8 w-[200px]" />
</div>
<Select defaultValue="all">
<SelectTrigger className="w-[150px]">
<Filter className="mr-2 h-4 w-4" />
<SelectValue placeholder="任务状态" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
<SelectItem value="pending"></SelectItem>
<SelectItem value="running"></SelectItem>
<SelectItem value="completed"></SelectItem>
<SelectItem value="failed"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
</CardHeader>
<CardContent>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[50px]"></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{aiTasks.map((task) => (
<TableRow key={task.id}>
<TableCell>{getTaskTypeIcon(task.type)}</TableCell>
<TableCell className="font-medium">{task.name}</TableCell>
<TableCell>{task.dataSource}</TableCell>
<TableCell>{task.createdAt}</TableCell>
<TableCell>{getStatusBadge(task.status)}</TableCell>
<TableCell>
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div
className={`h-2.5 rounded-full ${
task.status === "completed"
? "bg-green-600"
: task.status === "running"
? "bg-blue-600"
: task.status === "failed"
? "bg-red-600"
: "bg-gray-400"
}`}
style={{ width: `${task.progress}%` }}
></div>
</div>
</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="sm" onClick={() => setSelectedTaskId(task.id)}>
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="ai-models" className="space-y-4">
<Card>
<CardHeader>
<div className="flex justify-between items-center">
<CardTitle>AI模型管理</CardTitle>
<Button onClick={() => setIsAddingModel(true)}></Button>
</div>
</CardHeader>
<CardContent>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead>API端点</TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{aiModels.map((model) => (
<TableRow key={model.id}>
<TableCell className="font-medium">{model.name}</TableCell>
<TableCell>{model.provider}</TableCell>
<TableCell>{model.type}</TableCell>
<TableCell>
<div className="flex flex-wrap gap-1">
{model.capabilities.slice(0, 2).map((capability, index) => (
<Badge key={index} variant="outline">
{capability}
</Badge>
))}
{model.capabilities.length > 2 && (
<Badge variant="outline">+{model.capabilities.length - 2}</Badge>
)}
</div>
</TableCell>
<TableCell className="max-w-[200px] truncate">{model.apiEndpoint}</TableCell>
<TableCell>
<div className="flex items-center space-x-2">
<Switch
checked={model.status === "active"}
onCheckedChange={() => toggleModelStatus(model.id)}
aria-label="Toggle model"
/>
<span>{getStatusBadge(model.status)}</span>
</div>
</TableCell>
<TableCell className="text-right">
<div className="flex justify-end space-x-2">
<Button variant="ghost" size="sm">
</Button>
<Button variant="ghost" size="sm">
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
<Card>
<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">
<Label></Label>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center space-x-2">
<Switch id="behavior-analysis" defaultChecked />
<Label htmlFor="behavior-analysis"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="segment-discovery" defaultChecked />
<Label htmlFor="segment-discovery"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="churn-prediction" defaultChecked />
<Label htmlFor="churn-prediction"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="tag-generation" defaultChecked />
<Label htmlFor="tag-generation"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="strategy-generation" defaultChecked />
<Label htmlFor="strategy-generation"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="content-generation" defaultChecked />
<Label htmlFor="content-generation"></Label>
</div>
</div>
</div>
<div className="flex justify-end">
<Button>MCP配置</Button>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="templates" className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{analysisTemplates.map((template) => (
<Card key={template.id} className="border shadow-sm">
<CardHeader>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{getTaskTypeIcon(template.type)}
<CardTitle className="text-lg">{template.name}</CardTitle>
</div>
<Button variant="ghost" size="icon">
<Settings className="h-4 w-4" />
</Button>
</div>
<CardDescription>{template.description}</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
<h4 className="text-sm font-medium"></h4>
<div className="text-sm">
{Object.entries(template.parameters).map(([key, value]) => (
<div key={key} className="flex justify-between py-1 border-b border-dashed border-gray-200">
<span className="text-muted-foreground">{key}:</span>
<span className="font-medium">
{Array.isArray(value) ? value.join(", ") : value.toString()}
</span>
</div>
))}
</div>
</div>
<div className="mt-4 flex justify-end">
<Button onClick={() => setIsCreatingTask(true)}>
<Play className="mr-2 h-4 w-4" />
使
</Button>
</div>
</CardContent>
</Card>
))}
<Card className="border shadow-sm border-dashed">
<CardContent className="p-6 flex flex-col items-center justify-center h-full">
<div className="rounded-full bg-muted p-3 mb-4">
<Plus className="h-6 w-6 text-muted-foreground" />
</div>
<h3 className="text-lg font-medium mb-2"></h3>
<p className="text-sm text-muted-foreground text-center mb-4"></p>
<Button variant="outline"></Button>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="settings" className="space-y-4">
<Card>
<CardHeader>
<CardTitle>AI分析设置</CardTitle>
<CardDescription>AI分析的全局设置</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="enable-ai">AI分析</Label>
<Switch id="enable-ai" defaultChecked />
</div>
<p className="text-sm text-muted-foreground">AI分析功能将暂停</p>
</div>
<div className="space-y-2">
<Label htmlFor="default-model">AI模型</Label>
<Select defaultValue="model-3">
<SelectTrigger id="default-model">
<SelectValue placeholder="选择默认模型" />
</SelectTrigger>
<SelectContent>
{aiModels
.filter((model) => model.status === "active")
.map((model) => (
<SelectItem key={model.id} value={model.id}>
{model.name} ({model.provider})
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="analysis-concurrency"></Label>
<Select defaultValue="3">
<SelectTrigger id="analysis-concurrency">
<SelectValue placeholder="选择并发任务数" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
</SelectContent>
</Select>
<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">
<Checkbox id="access-user-db" defaultChecked />
<Label htmlFor="access-user-db"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="access-transaction" defaultChecked />
<Label htmlFor="access-transaction"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="access-behavior" defaultChecked />
<Label htmlFor="access-behavior"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="access-user-value" defaultChecked />
<Label htmlFor="access-user-value"></Label>
</div>
</div>
</div>
<div className="space-y-2">
<Label></Label>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center space-x-2">
<Checkbox id="anonymize-data" defaultChecked />
<Label htmlFor="anonymize-data"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="cache-results" defaultChecked />
<Label htmlFor="cache-results"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="auto-update" defaultChecked />
<Label htmlFor="auto-update"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="save-history" defaultChecked />
<Label htmlFor="save-history"></Label>
</div>
</div>
</div>
<div className="space-y-2">
<Label></Label>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center space-x-2">
<Checkbox id="notify-completion" defaultChecked />
<Label htmlFor="notify-completion"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="notify-error" defaultChecked />
<Label htmlFor="notify-error"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="notify-insight" defaultChecked />
<Label htmlFor="notify-insight"></Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="notify-email" defaultChecked />
<Label htmlFor="notify-email"></Label>
</div>
</div>
</div>
<div className="flex justify-end">
<Button></Button>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
{/* 任务详情对话框 */}
<Dialog open={!!selectedTaskId} onOpenChange={(open) => !open && setSelectedTaskId(null)}>
<DialogContent className="sm:max-w-[700px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>AI分析任务的详细信息和结果</DialogDescription>
</DialogHeader>
{selectedTask && (
<div className="space-y-4 py-4">
<div className="flex items-center gap-4">
{getTaskTypeIcon(selectedTask.type)}
<div>
<h3 className="font-medium text-lg">{selectedTask.name}</h3>
<p className="text-sm text-muted-foreground">
{selectedTask.dataSource} | {selectedTask.createdAt}
</p>
</div>
<div className="ml-auto">{getStatusBadge(selectedTask.status)}</div>
</div>
<Separator />
<div className="space-y-2">
<h4 className="text-sm font-medium"></h4>
<div className="grid grid-cols-2 gap-2">
{Object.entries(selectedTask.parameters).map(([key, value]) => (
<div key={key} className="flex justify-between py-1 border-b border-dashed border-gray-200">
<span className="text-sm text-muted-foreground">{key}:</span>
<span className="text-sm font-medium">
{Array.isArray(value) ? value.join(", ") : value.toString()}
</span>
</div>
))}
</div>
</div>
{selectedTask.status === "running" && (
<div className="space-y-2">
<div className="flex justify-between items-center">
<h4 className="text-sm font-medium"></h4>
<span className="text-sm font-medium">{selectedTask.progress}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div
className="bg-blue-600 h-2.5 rounded-full"
style={{ width: `${selectedTask.progress}%` }}
></div>
</div>
<p className="text-xs text-muted-foreground">
...: {Math.round((100 - selectedTask.progress) / 10)}
</p>
</div>
)}
{selectedTask.result && (
<div className="space-y-4">
<Separator />
<div className="space-y-2">
<div className="flex justify-between items-center">
<h4 className="text-sm font-medium"></h4>
<Button
variant="ghost"
size="sm"
onClick={() => handleCopyText(selectedTask.result.summary)}
className="h-7"
>
{copiedText === selectedTask.result.summary ? (
<Check className="h-3 w-3 mr-1" />
) : (
<Copy className="h-3 w-3 mr-1" />
)}
</Button>
</div>
<div className="p-3 bg-muted rounded-md text-sm">{selectedTask.result.summary}</div>
</div>
{selectedTask.result.insights && (
<div className="space-y-2">
<h4 className="text-sm font-medium"></h4>
<div className="space-y-1">
{selectedTask.result.insights.map((insight: string, index: number) => (
<div key={index} className="flex items-start gap-2">
<Lightbulb className="h-4 w-4 text-yellow-500 mt-0.5" />
<p className="text-sm">{insight}</p>
</div>
))}
</div>
</div>
)}
{selectedTask.result.recommendations && (
<div className="space-y-2">
<h4 className="text-sm font-medium"></h4>
<div className="space-y-1">
{selectedTask.result.recommendations.map((recommendation: string, index: number) => (
<div key={index} className="flex items-start gap-2">
<Zap className="h-4 w-4 text-blue-500 mt-0.5" />
<p className="text-sm">{recommendation}</p>
</div>
))}
</div>
</div>
)}
{selectedTask.result.clusters && (
<div className="space-y-2">
<h4 className="text-sm font-medium"></h4>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{selectedTask.result.clusters.map((cluster: any, index: number) => (
<TableRow key={index}>
<TableCell className="font-medium">{cluster.name}</TableCell>
<TableCell>{cluster.size.toLocaleString()}</TableCell>
<TableCell>
<div className="flex flex-wrap gap-1">
{cluster.characteristics.map((char: string, charIndex: number) => (
<Badge key={charIndex} variant="outline">
{char}
</Badge>
))}
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
)}
{selectedTask.result.tagCategories && (
<div className="space-y-2">
<h4 className="text-sm font-medium"></h4>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{selectedTask.result.tagCategories.map((category: any, index: number) => (
<TableRow key={index}>
<TableCell className="font-medium">{category.name}</TableCell>
<TableCell>{category.count}</TableCell>
<TableCell>{category.coverage}</TableCell>
<TableCell>
<div className="flex flex-wrap gap-1">
{category.examples.map((example: string, exampleIndex: number) => (
<Badge key={exampleIndex} variant="outline">
{example}
</Badge>
))}
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
)}
</div>
)}
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={() => setSelectedTaskId(null)}>
</Button>
{selectedTask?.status === "completed" && (
<Button>
<Download className="mr-2 h-4 w-4" />
</Button>
)}
</DialogFooter>
</DialogContent>
</Dialog>
{/* 添加模型对话框 */}
<Dialog open={isAddingModel} onOpenChange={setIsAddingModel}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>AI模型</DialogTitle>
<DialogDescription>AI模型连接</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="model-name" className="text-right">
</Label>
<Input id="model-name" className="col-span-3" placeholder="例如GPT-4" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="model-provider" className="text-right">
</Label>
<Input id="model-provider" className="col-span-3" placeholder="例如OpenAI" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="model-type" className="text-right">
</Label>
<Select>
<SelectTrigger id="model-type" className="col-span-3">
<SelectValue placeholder="选择模型类型" />
</SelectTrigger>
<SelectContent>
<SelectItem value="llm"></SelectItem>
<SelectItem value="image-gen"></SelectItem>
<SelectItem value="custom"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="api-endpoint" className="text-right">
API端点
</Label>
<Input id="api-endpoint" className="col-span-3" placeholder="例如https://api.openai.com/v1/chat" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="api-key" className="text-right">
API密钥
</Label>
<Input id="api-key" type="password" className="col-span-3" placeholder="输入API密钥" />
</div>
<div className="grid grid-cols-4 items-start gap-4">
<Label htmlFor="capabilities" className="text-right pt-2">
</Label>
<Textarea id="capabilities" className="col-span-3" placeholder="例如:文本生成, 内容分析" rows={3} />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<div className="text-right">
<Label htmlFor="model-status"></Label>
</div>
<div className="flex items-center space-x-2 col-span-3">
<Switch id="model-status" defaultChecked />
<Label htmlFor="model-status"></Label>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsAddingModel(false)}>
</Button>
<Button onClick={() => setIsAddingModel(false)}></Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 创建分析任务对话框 */}
<Dialog open={isCreatingTask} onOpenChange={setIsCreatingTask}>
<DialogContent className="sm:max-w-[600px]">
<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="task-type" className="text-right">
</Label>
<Select>
<SelectTrigger id="task-type" className="col-span-3">
<SelectValue placeholder="选择分析类型" />
</SelectTrigger>
<SelectContent>
<SelectItem value="behavior-analysis"></SelectItem>
<SelectItem value="segment-discovery"></SelectItem>
<SelectItem value="churn-prediction"></SelectItem>
<SelectItem value="strategy-generation"></SelectItem>
<SelectItem value="tag-generation"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="task-datasource" className="text-right">
</Label>
<Select>
<SelectTrigger id="task-datasource" className="col-span-3">
<SelectValue placeholder="选择数据源" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
<SelectItem value="user-db"></SelectItem>
<SelectItem value="transaction"></SelectItem>
<SelectItem value="behavior"></SelectItem>
<SelectItem value="user-value"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-start gap-4">
<Label htmlFor="task-parameters" className="text-right pt-2">
</Label>
<Textarea
id="task-parameters"
className="col-span-3"
placeholder="输入JSON格式的分析参数例如{ 'userSegment': '高价值用户', 'timeRange': '近30天' }"
rows={5}
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<div className="text-right">
<Label htmlFor="schedule-task"></Label>
</div>
<div className="flex items-center space-x-2 col-span-3">
<Switch id="schedule-task" />
<Label htmlFor="schedule-task"></Label>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsCreatingTask(false)}>
</Button>
<Button onClick={() => setIsCreatingTask(false)}>
<Play className="mr-2 h-4 w-4" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}