refactor: restructure project into 5 core modules

Organize project by 5 core modules based on requirement docs.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2026-01-31 04:18:24 +00:00
parent b4aa620c93
commit 22e725887a
74 changed files with 4219 additions and 9234 deletions

View File

@@ -1,188 +1,235 @@
"use client"
import { useCallback, useMemo, useState } from "react"
import { BarChart3, Database, FileText } from 'lucide-react'
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
import Link from "next/link"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import TaskList from "@/components/ai-assistant/task-list"
import CreateTaskDialog from "@/components/ai-assistant/create-task-dialog"
import ReportCards from "@/components/ai-assistant/report-cards"
import { sanitizeText } from "@/lib/text-sanitize"
import type { AnalysisTask, DatabaseInfo, ReportItem, ReportTemplate } from "@/types/ai-assistant"
import { Textarea } from "@/components/ui/textarea"
import { Cpu, BarChart3, TrendingUp, Users, Target, Brain, ArrowRight, Send, Sparkles } from 'lucide-react'
const initialTasks: AnalysisTask[] = [
{
id: "task_001",
name: "用户行为分析报告",
database: "微信用户数据库",
status: "completed",
progress: 100,
createdAt: "2025-01-15T09:00:00Z",
completedAt: "2025-01-15T09:30:00Z",
reportUrl: "/reports/user-behavior-analysis.pdf",
description: "基于近30天行为的聚合与序列分析",
},
{
id: "task_002",
name: "流量关键词趋势分析",
database: "流量关键词库",
status: "running",
progress: 65,
createdAt: "2025-01-15T10:00:00Z",
description: "关注核心流量词与曝光、点击、转化等指标",
},
{
id: "task_003",
name: "用户价值分层报告",
database: "微信用户数据库",
status: "pending",
progress: 0,
createdAt: "2025-01-15T10:30:00Z",
description: "RFM 分层、价值区间分布与运营建议",
},
]
export default function AIAssistant() {
const [message, setMessage] = useState("")
const [chatHistory, setChatHistory] = useState([
{
role: "assistant",
content: "你好我是神射手AI助手可以帮助你分析用户数据、生成报告、预测趋势。有什么我可以帮助你的吗",
timestamp: "10:30",
},
])
const databases: DatabaseInfo[] = [
{ id: "db_001", name: "微信用户数据库", type: "MySQL", tables: 25, records: 4000000000, lastUpdated: "2025-01-15T11:00:00Z" },
{ id: "db_002", name: "流量关键词库", type: "PostgreSQL", tables: 8, records: 150000, lastUpdated: "2025-01-15T10:45:00Z" },
{ id: "db_003", name: "用户行为日志", type: "MongoDB", tables: 12, records: 1500000000, lastUpdated: "2025-01-15T11:15:00Z" },
]
// AI功能卡片
const aiFeatures = [
{
icon: <BarChart3 className="w-8 h-8" />,
title: "数据分析",
description: "自动分析用户行为,生成洞察报告",
color: "from-blue-500 to-cyan-500",
href: "/platform/ai-assistant/data-analysis",
stats: { reports: 1258, insights: 856 }
},
{
icon: <TrendingUp className="w-8 h-8" />,
title: "趋势预测",
description: "预测用户行为和价值变化趋势",
color: "from-purple-500 to-pink-500",
href: "/platform/ai-assistant/trend-prediction",
stats: { accuracy: 94.2, predictions: 2450 }
},
{
icon: <Users className="w-8 h-8" />,
title: "用户画像",
description: "智能生成用户群体画像",
color: "from-green-500 to-emerald-500",
href: "/platform/ai-assistant/user-profiling",
stats: { profiles: 4285, segments: 128 }
},
{
icon: <Target className="w-8 h-8" />,
title: "精准推荐",
description: "推荐最佳营销策略和用户群",
color: "from-orange-500 to-red-500",
href: "/platform/ai-assistant/recommendation",
stats: { campaigns: 586, conversion: 18.5 }
},
]
const templates: ReportTemplate[] = [
{ id: "template_001", name: "用户画像分析报告", description: "深度分析用户特征、行为模式和价值分层", category: "用户分析", fields: ["用户基本信息", "RFM分析", "行为轨迹", "价值评估", "推荐策略"] },
{ id: "template_002", name: "流量趋势分析报告", description: "分析关键词搜索趋势和流量变化", category: "流量分析", fields: ["关键词热度", "搜索趋势", "竞争分析", "机会识别", "优化建议"] },
{ id: "template_003", name: "业务运营报告", description: "综合业务数据分析和运营建议", category: "运营分析", fields: ["核心指标", "增长分析", "用户留存", "转化漏斗", "运营建议"] },
{ id: "template_004", name: "数据质量报告", description: "评估数据完整性、准确性和一致性", category: "数据质量", fields: ["数据完整性", "准确性检查", "一致性验证", "异常检测", "改进建议"] },
]
// 快捷问题
const quickQuestions = [
"分析今日新增用户特征",
"预测本月用户增长趋势",
"高价值用户流失预警",
"生成本周运营报告",
]
const reportItems: ReportItem[] = [
{ id: "rpt-1", title: "用户行为分析报告", source: "数据源:微信用户数据库", description: '本报告基于近30天互动行为输出用户行为模式与高频路径。\\n"]]}', updatedAt: new Date().toISOString() },
{ id: "rpt-2", title: "流量关键词趋势分析", source: "数据源:流量关键词库", description: '追踪 000000 类流量词及其曝光、点击、转化趋势,适配日/周/月视角。\\n" ]]}', updatedAt: new Date().toISOString() },
{ id: "rpt-3", title: "用户价值分层报告", source: "数据源:微信用户数据库", description: '结合 RFM 得分与标签,给出 S/A/B/C/D 分层与经营建议。\\n" ]] }', updatedAt: new Date().toISOString() },
]
const handleSend = () => {
if (!message.trim()) return
export default function AIAssistantPage() {
const [tasks, setTasks] = useState<AnalysisTask[]>(initialTasks)
// 进度推进(仅演示用)
const tick = useCallback(() => {
setTasks((prev) =>
prev.map((t) => {
if (t.status === "running" && t.progress < 100) {
const p = Math.min(100, t.progress + Math.random() * 12)
if (p >= 100) {
return {
...t,
progress: 100,
status: "completed",
completedAt: new Date().toISOString(),
reportUrl: `/reports/${t.id}.pdf`,
}
}
return { ...t, progress: p }
}
return t
}),
)
}, [])
const onCreate = (task: AnalysisTask) => {
setTasks((prev) => [task, ...prev])
// 模拟启动
setTimeout(() => {
setTasks((prev) => prev.map((t) => (t.id === task.id ? { ...t, status: "running" } : t)))
}, 800)
setChatHistory([
...chatHistory,
{
role: "user",
content: message,
timestamp: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }),
},
{
role: "assistant",
content: "正在分析您的问题,请稍候...",
timestamp: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }),
},
])
setMessage("")
}
const sanitizedItems = useMemo(
() => reportItems.map((i) => ({ ...i, description: sanitizeText(i.description) })),
[],
)
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-indigo-50">
<div className="container mx-auto px-4 py-8">
<div className="mb-6">
<h1 className="text-3xl font-bold">AI智能助手</h1>
<p className="text-gray-600"></p>
<div className="min-h-screen bg-gray-50 p-6">
{/* Header */}
<div className="mb-8">
<div className="flex items-center gap-3 mb-2">
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center">
<Brain className="w-6 h-6 text-white" />
</div>
<div>
<h1 className="text-4xl font-bold text-gray-900">AI智能助手</h1>
<p className="text-gray-600"></p>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* AI状态 */}
<Card>
<CardHeader>
<CardTitle className="text-gray-900 flex items-center gap-2">
<Cpu className="w-5 h-5 text-green-600" />
AI运行状态
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-gray-600"></span>
<Badge variant="outline" className="border-green-500 text-green-600 bg-green-50">
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse mr-2"></div>
</Badge>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-600"></span>
<span className="text-gray-900 font-semibold">v3.5</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-600"></span>
<span className="text-gray-900 font-semibold">0.8s</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-600"></span>
<span className="text-green-600 font-semibold">94.2%</span>
</div>
</div>
</CardContent>
</Card>
{/* AI功能卡片 */}
<div className="lg:col-span-2 grid grid-cols-1 lg:grid-cols-2 gap-6">
{aiFeatures.map((feature, index) => (
<Link key={index} href={feature.href}>
<Card className="hover:shadow-lg transition-shadow cursor-pointer h-full">
<CardContent className="p-6">
<div className="flex items-start gap-4 mb-4">
<div className={`w-16 h-16 rounded-xl bg-gradient-to-br ${feature.color} flex items-center justify-center text-white`}>
{feature.icon}
</div>
<div className="flex-1">
<div className="flex items-center justify-between mb-2">
<h3 className="text-xl font-semibold text-gray-900">{feature.title}</h3>
<ArrowRight className="w-5 h-5 text-gray-400" />
</div>
<p className="text-gray-600 text-sm">{feature.description}</p>
</div>
</div>
<div className="grid grid-cols-2 gap-4 pt-4 border-t border-gray-200">
{Object.entries(feature.stats).map(([key, value], i) => (
<div key={i}>
<div className="text-xs text-gray-600 mb-1">{key === 'reports' ? '报告数' : key === 'insights' ? '洞察数' : key === 'accuracy' ? '准确率' : key === 'predictions' ? '预测数' : key === 'profiles' ? '画像数' : key === 'segments' ? '分组数' : key === 'campaigns' ? '活动数' : '转化率'}</div>
<div className="text-lg font-semibold text-gray-900">
{typeof value === 'number' && value % 1 !== 0 ? `${value}%` : value}
</div>
</div>
))}
</div>
</CardContent>
</Card>
</Link>
))}
</div>
<Tabs defaultValue="analysis" className="space-y-6">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="analysis" className="flex items-center gap-2">
<BarChart3 className="w-4 h-4" />
</TabsTrigger>
<TabsTrigger value="databases" className="flex items-center gap-2">
<Database className="w-4 h-4" />
</TabsTrigger>
<TabsTrigger value="templates" className="flex items-center gap-2">
<FileText className="w-4 h-4" />
</TabsTrigger>
</TabsList>
<TabsContent value="analysis" className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold"></h2>
<CreateTaskDialog databases={databases} templates={templates} onCreate={onCreate} />
</div>
<TaskList tasks={tasks} onTick={tick} />
<ReportCards items={sanitizedItems} />
</TabsContent>
<TabsContent value="databases">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Database className="w-4 h-4" />
</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{databases.map((db) => (
<Card key={db.id} className="border">
<CardContent className="p-4 space-y-1">
<div className="font-medium">{db.name}</div>
<div className="text-sm text-gray-600">
{db.type} · {db.tables}
</div>
<div className="text-xs text-gray-500">
{new Date(db.lastUpdated).toLocaleString("zh-CN")}
</div>
</CardContent>
</Card>
{/* 对话区域 */}
<div className="lg:col-span-2">
<Card className="bg-white/5 border-white/10 h-[calc(100vh-200px)]">
<CardHeader className="border-b border-white/10">
<CardTitle className="text-white flex items-center gap-2">
<BarChart3 className="w-5 h-5" />
AI对话
</CardTitle>
</CardHeader>
<CardContent className="p-6 flex flex-col h-[calc(100%-80px)]">
{/* 聊天历史 */}
<div className="flex-1 overflow-y-auto space-y-4 mb-6">
{chatHistory.map((chat, index) => (
<div key={index} className={`flex ${chat.role === "user" ? "justify-end" : "justify-start"}`}>
<div
className={`max-w-[80%] rounded-2xl p-4 ${
chat.role === "user"
? "bg-gradient-to-br from-blue-500 to-purple-500 text-white"
: "bg-white/10 text-white"
}`}
>
<div className="mb-1">{chat.content}</div>
<div className="text-xs opacity-70">{chat.timestamp}</div>
</div>
</div>
))}
</CardContent>
</Card>
</TabsContent>
</div>
<TabsContent value="templates">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileText className="w-4 h-4" />
</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{templates.map((t) => (
<Card key={t.id} className="border">
<CardContent className="p-4 space-y-2">
<div className="font-medium">{t.name}</div>
<div className="text-sm text-gray-600">{t.category}</div>
<div className="text-xs text-gray-500">: {t.fields.join(" / ")}</div>
</CardContent>
</Card>
{/* 快捷问题 */}
<div className="mb-4 flex flex-wrap gap-2">
{quickQuestions.map((question, index) => (
<Button
key={index}
variant="outline"
size="sm"
onClick={() => setMessage(question)}
className="bg-white/5 border-white/10 hover:bg-white/10 text-xs"
>
<Sparkles className="w-3 h-3 mr-1" />
{question}
</Button>
))}
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
{/* 输入框 */}
<div className="flex items-end gap-3">
<Textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
handleSend()
}
}}
placeholder="输入您的问题按Enter发送..."
className="bg-white/5 border-white/10 text-white resize-none"
rows={3}
/>
<Button onClick={handleSend} className="bg-gradient-to-r from-blue-500 to-purple-500 h-[88px]">
<Send className="w-5 h-5" />
</Button>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
)