diff --git a/app/ClientLayout.tsx b/app/ClientLayout.tsx index 2d8bcc9..debd792 100644 --- a/app/ClientLayout.tsx +++ b/app/ClientLayout.tsx @@ -1,14 +1,14 @@ "use client" import type React from "react" - -import { MainNav } from "@/components/main-nav" -import { UserNav } from "@/components/user-nav" -import { ThemeToggle } from "@/components/theme-toggle" -import { Search } from "lucide-react" -import { Input } from "@/components/ui/input" -import { Inter } from "next/font/google" import "./globals.css" +import { Inter } from "next/font/google" +import { useState, useEffect } from "react" +import { usePathname } from "next/navigation" +import Sidebar from "./components/Sidebar" +import MobileHeader from "./components/MobileHeader" +import MobileSidebar from "./components/MobileSidebar" +import BottomNav from "./components/BottomNav" const inter = Inter({ subsets: ["latin"] }) @@ -17,37 +17,56 @@ export default function ClientLayout({ }: { children: React.ReactNode }) { + const [isMobile, setIsMobile] = useState(false) + const [sidebarOpen, setSidebarOpen] = useState(false) + const pathname = usePathname() + + useEffect(() => { + const checkMobile = () => { + setIsMobile(window.innerWidth < 768) + } + + checkMobile() + window.addEventListener("resize", checkMobile) + + return () => window.removeEventListener("resize", checkMobile) + }, []) + return ( - 神射手数据资产中台 - + 用户数据资产中台 + -
-
-
-
- - 神射手数据中台 - - -
-
-
-
- - -
-
- - -
-
-
-
{children}
+ {/* 背景装饰 */} +
+
+
+
+
+ +
+ {/* 桌面端侧边栏 */} + {!isMobile && } + + {/* 移动端侧边栏 */} + {isMobile && setSidebarOpen(false)} />} + + {/* 主内容区域 */} +
+ {/* 移动端头部 */} + {isMobile && setSidebarOpen(true)} />} + + {/* 内容区域 */} +
{children}
+
+
+ + {/* 移动端底部导航 */} + {isMobile && } ) diff --git a/app/ai-agent/chat/page.tsx b/app/ai-agent/chat/page.tsx new file mode 100644 index 0000000..e55b980 --- /dev/null +++ b/app/ai-agent/chat/page.tsx @@ -0,0 +1,432 @@ +"use client" + +import { useState, useRef, useEffect } from "react" +import { Card, CardContent } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Input } from "@/components/ui/input" +import { ScrollArea } from "@/components/ui/scroll-area" +import { + Bot, + Send, + User, + Sparkles, + Database, + Tags, + FileText, + Download, + Copy, + ThumbsUp, + ThumbsDown, + Loader2, + Zap, + Search, + BarChart3, + Table, + RefreshCw, +} from "lucide-react" + +// 第四部分:AI Agent智能系统 - 智能对话 +export default function AIAgentChatPage() { + const [messages, setMessages] = useState([ + { + id: "1", + role: "assistant", + content: "您好!我是神射手AI Agent,可以帮您完成以下任务:\n\n- **自然语言查询**:用自然语言查询用户数据\n- **智能打标**:AI自动为用户打标签\n- **数据清洗**:AI识别并清洗异常数据\n- **智能报告**:自动生成数据分析报告\n\n请问有什么可以帮您?", + timestamp: new Date(), + }, + ]) + const [inputValue, setInputValue] = useState("") + const [isLoading, setIsLoading] = useState(false) + const scrollRef = useRef(null) + + // 快捷指令 + const quickCommands = [ + { icon: Search, label: "查询高价值用户", command: "帮我找出最近30天内消费超过1万元的高价值用户" }, + { icon: Tags, label: "AI打标签", command: "为最近7天未登录的用户打上流失风险标签" }, + { icon: Zap, label: "数据清洗", command: "检查用户表中的手机号格式,清洗异常数据" }, + { icon: FileText, label: "生成报告", command: "生成本月用户增长分析报告" }, + ] + + // 支持的指令类型 + const supportedTasks = [ + { type: "NLQ", label: "自然语言查询", desc: "用自然语言查询数据库", icon: Search }, + { type: "TAG", label: "AI打标", desc: "智能为用户打标签", icon: Tags }, + { type: "CLEAN", label: "数据清洗", desc: "AI识别清洗异常数据", icon: Zap }, + { type: "REPORT", label: "智能报告", desc: "自动生成分析报告", icon: FileText }, + ] + + useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTop = scrollRef.current.scrollHeight + } + }, [messages]) + + const handleSend = async () => { + if (!inputValue.trim() || isLoading) return + + const userMessage = { + id: Date.now().toString(), + role: "user", + content: inputValue, + timestamp: new Date(), + } + setMessages((prev) => [...prev, userMessage]) + setInputValue("") + setIsLoading(true) + + // 模拟AI响应 + setTimeout(() => { + let response: any = { + id: (Date.now() + 1).toString(), + role: "assistant", + timestamp: new Date(), + } + + // 根据输入内容生成不同类型的响应 + if (inputValue.includes("高价值") || inputValue.includes("消费") || inputValue.includes("查询")) { + response.content = "我已理解您的需求。正在为您查询符合条件的用户..." + response.taskType = "NLQ" + response.result = { + type: "table", + title: "高价值用户查询结果", + sql: "SELECT * FROM users WHERE total_spending > 10000 AND last_active_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)", + data: [ + { id: "U001", name: "张三", phone: "138****1234", spending: "¥15,680", lastActive: "2天前" }, + { id: "U002", name: "李四", phone: "139****5678", spending: "¥12,340", lastActive: "1天前" }, + { id: "U003", name: "王五", phone: "137****9012", spending: "¥11,890", lastActive: "3天前" }, + ], + total: 823, + } + } else if (inputValue.includes("打标") || inputValue.includes("标签")) { + response.content = "我将为符合条件的用户打上「流失风险」标签。这是一个AI打标任务,需要您确认后执行。" + response.taskType = "TAG" + response.result = { + type: "confirmation", + title: "AI打标任务确认", + description: "为最近7天未登录的用户打上「流失风险」标签", + affectedCount: 12456, + confidence: 0.87, + needReview: true, + } + } else if (inputValue.includes("清洗") || inputValue.includes("异常")) { + response.content = "我已扫描用户表,发现以下数据质量问题:" + response.taskType = "CLEAN" + response.result = { + type: "cleaning", + title: "数据清洗建议", + issues: [ + { field: "phone", issue: "格式不规范", count: 234, example: "1381234567 → 138****4567" }, + { field: "email", issue: "缺失值", count: 1256, suggestion: "填充默认值或标记" }, + { field: "birth_date", issue: "超出范围", count: 45, example: "2099-01-01" }, + ], + needReview: true, + } + } else if (inputValue.includes("报告") || inputValue.includes("分析")) { + response.content = "正在为您生成用户增长分析报告..." + response.taskType = "REPORT" + response.result = { + type: "report", + title: "用户增长分析报告(2026年1月)", + summary: "本月新增用户23,456人,同比增长15.2%;活跃用户856,234人,DAU/MAU比值0.32。", + highlights: [ + "新用户转化率提升至18.5%,环比增长3.2%", + "高价值用户占比8.7%,贡献65%的交易额", + "用户流失率下降至2.1%,创历史新低", + ], + } + } else { + response.content = "我理解您的需求。请问您想要:\n1. 查询用户数据\n2. AI打标签\n3. 数据清洗\n4. 生成报告\n\n请选择或详细描述您的需求。" + } + + setMessages((prev) => [...prev, response]) + setIsLoading(false) + }, 1500) + } + + const handleQuickCommand = (command: string) => { + setInputValue(command) + } + + const renderMessageContent = (message: any) => { + return ( +
+

{message.content}

+ + {message.result?.type === "table" && ( + + +
+

{message.result.title}

+ 共 {message.result.total} 条 +
+
+ {message.result.sql} +
+
+ + + + + + + + + + + + {message.result.data.map((row: any, index: number) => ( + + + + + + + + ))} + +
ID姓名手机消费金额最近活跃
{row.id}{row.name}{row.phone}{row.spending}{row.lastActive}
+
+
+ + +
+
+
+ )} + + {message.result?.type === "confirmation" && ( + + +

{message.result.title}

+

{message.result.description}

+
+ 影响用户数:{message.result.affectedCount.toLocaleString()} + 置信度:{(message.result.confidence * 100).toFixed(0)}% +
+ {message.result.needReview && ( +
+ + + +
+ )} +
+
+ )} + + {message.result?.type === "cleaning" && ( + + +

{message.result.title}

+
+ {message.result.issues.map((issue: any, index: number) => ( +
+
+
+ {issue.field} + {issue.issue} +
+ {issue.example && ( +

示例:{issue.example}

+ )} + {issue.suggestion && ( +

建议:{issue.suggestion}

+ )} +
+ {issue.count} 条 +
+ ))} +
+
+ + +
+
+
+ )} + + {message.result?.type === "report" && ( + + +

{message.result.title}

+

{message.result.summary}

+
+

核心发现:

+ {message.result.highlights.map((highlight: string, index: number) => ( +
+ + {highlight} +
+ ))} +
+
+ + +
+
+
+ )} +
+ ) + } + + return ( +
+
+ {/* 左侧:功能面板 */} +
+

AI能力

+
+ {supportedTasks.map((task) => ( +
+
+ +
+
+

{task.label}

+

{task.desc}

+
+
+ ))} +
+ +
+

快捷指令

+
+ {quickCommands.map((cmd, index) => ( + + ))} +
+
+
+ + {/* 中间:对话区域 */} +
+ {/* 消息列表 */} + +
+ {messages.map((message) => ( +
+
+ {message.role === "user" ? ( + + ) : ( + + )} +
+
+
+ {message.role === "user" ? ( +

{message.content}

+ ) : ( + renderMessageContent(message) + )} +
+ {message.role === "assistant" && ( +
+ + + + +
+ )} +
+
+ ))} + + {isLoading && ( +
+
+ +
+
+
+ + AI正在思考... +
+
+
+ )} +
+
+ + {/* 输入区域 */} +
+
+
+
+ setInputValue(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && handleSend()} + placeholder="输入您的问题,例如:帮我找出最近30天的高价值用户..." + className="pr-12 py-6 bg-white" + /> + +
+
+

+ AI生成的内容可能存在误差,重要操作请人工确认 +

+
+
+
+
+
+ ) +} diff --git a/app/ai-agent/nlq/page.tsx b/app/ai-agent/nlq/page.tsx new file mode 100644 index 0000000..34f76ca --- /dev/null +++ b/app/ai-agent/nlq/page.tsx @@ -0,0 +1,336 @@ +"use client" + +import { useState } from "react" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Input } from "@/components/ui/input" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { + Search, + Play, + Download, + Copy, + History, + Star, + StarOff, + Clock, + Database, + Loader2, + ChevronRight, + Table, + BarChart3, +} from "lucide-react" + +// 第四部分:AI Agent - 自然语言查询 (NLQ) +export default function NLQPage() { + const [query, setQuery] = useState("") + const [isLoading, setIsLoading] = useState(false) + const [activeTab, setActiveTab] = useState("query") + const [showResult, setShowResult] = useState(false) + const [queryResult, setQueryResult] = useState(null) + + // 示例查询 + const exampleQueries = [ + { label: "高价值用户", query: "查找最近30天消费超过1万元的用户" }, + { label: "流失用户分析", query: "统计最近7天未登录的活跃用户数量" }, + { label: "地域分布", query: "按省份统计用户数量,按数量降序排列" }, + { label: "消费趋势", query: "统计每天的交易金额,最近30天" }, + ] + + // 历史查询 + const historyQueries = [ + { id: "1", query: "查找最近30天消费超过1万元的用户", time: "2分钟前", starred: true }, + { id: "2", query: "统计各渠道用户转化率", time: "1小时前", starred: false }, + { id: "3", query: "获取VIP用户的平均消费金额", time: "2小时前", starred: true }, + { id: "4", query: "按年龄段统计用户数量", time: "昨天", starred: false }, + { id: "5", query: "查询高活跃度但低消费的用户群体", time: "昨天", starred: false }, + ] + + const handleExecuteQuery = () => { + if (!query.trim()) return + setIsLoading(true) + setShowResult(false) + + // 模拟AI解析和查询 + setTimeout(() => { + setQueryResult({ + sql: `SELECT u.id, u.name, u.phone, SUM(t.amount) as total_spending +FROM users u +JOIN transactions t ON u.id = t.user_id +WHERE t.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) +GROUP BY u.id, u.name, u.phone +HAVING total_spending > 10000 +ORDER BY total_spending DESC +LIMIT 100`, + data: [ + { id: "U001", name: "张三", phone: "138****1234", total_spending: 25680 }, + { id: "U002", name: "李四", phone: "139****5678", total_spending: 18920 }, + { id: "U003", name: "王五", phone: "137****9012", total_spending: 15340 }, + { id: "U004", name: "赵六", phone: "136****3456", total_spending: 13280 }, + { id: "U005", name: "钱七", phone: "135****7890", total_spending: 12150 }, + ], + totalCount: 823, + executionTime: "0.23s", + explanation: "我将您的查询解析为:从用户表和交易表中联合查询,筛选最近30天内消费总额超过10000元的用户,按消费金额降序排列。", + }) + setShowResult(true) + setIsLoading(false) + }, 1500) + } + + const handleExampleClick = (exampleQuery: string) => { + setQuery(exampleQuery) + } + + const handleHistoryClick = (historyQuery: string) => { + setQuery(historyQuery) + } + + return ( +
+
+ {/* 顶部标题 */} +
+
+

自然语言查询

+

用自然语言描述您的数据需求,AI自动转换为SQL并执行查询

+
+
+ +
+ {/* 左侧:历史记录 */} +
+ + + + + 历史查询 + + + +
+ {historyQueries.map((item) => ( + + ))} +
+
+
+
+ + {/* 右侧:查询区域 */} +
+ {/* 查询输入 */} + + +
+
+ + setQuery(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleExecuteQuery()} + placeholder="输入您的查询,例如:查找最近30天消费超过1万元的用户" + className="pl-12 py-6 text-lg bg-white" + /> +
+
+
+ 示例查询: + {exampleQueries.map((example, index) => ( + + ))} +
+ +
+
+
+
+ + {/* 查询结果 */} + {showResult && queryResult && ( +
+ {/* AI解释 */} + + +
+
+ +
+
+

{queryResult.explanation}

+
+ 执行时间:{queryResult.executionTime} + 结果数量:{queryResult.totalCount} 条 +
+
+
+
+
+ + {/* SQL和结果 */} + + +
+ + + + + 查询结果 + + + + SQL语句 + + + + 图表 + + + +
+ + +
+ + + + +
+
+ + + + + + + + + + {queryResult.data.map((row: any, index: number) => ( + + + + + + + ))} + +
用户ID姓名手机号消费总额
{row.id}{row.name}{row.phone} + ¥{row.total_spending.toLocaleString()} +
+
+
+ + 显示 1-{queryResult.data.length} 条,共 {queryResult.totalCount} 条 + +
+ + +
+
+ + +
+
{queryResult.sql}
+
+
+ + +
+
+ +
+
+ +

图表视图即将支持

+
+
+
+ +
+
+ )} + + {/* 无结果时的提示 */} + {!showResult && !isLoading && ( + + + +

开始您的数据探索

+

+ 输入自然语言描述您想要查询的数据,AI将自动转换为SQL并执行 +

+
+ {exampleQueries.map((example, index) => ( + + ))} +
+
+
+ )} +
+
+
+
+ ) +} diff --git a/app/ai-agent/smart-tag/page.tsx b/app/ai-agent/smart-tag/page.tsx new file mode 100644 index 0000000..02ef3e1 --- /dev/null +++ b/app/ai-agent/smart-tag/page.tsx @@ -0,0 +1,550 @@ +"use client" + +import { useState } from "react" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Input } from "@/components/ui/input" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Textarea } from "@/components/ui/textarea" +import { Label } from "@/components/ui/label" +import { Checkbox } from "@/components/ui/checkbox" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { + Tags, + Plus, + Search, + Play, + Pause, + CheckCircle2, + Clock, + AlertCircle, + Settings, + FileText, + RefreshCw, + Sparkles, + ThumbsUp, + ThumbsDown, + Eye, + MoreVertical, + Bot, + Zap, +} from "lucide-react" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +// 第四部分:AI Agent - AI打标 +export default function AISmartTagPage() { + const [activeTab, setActiveTab] = useState("tasks") + const [searchQuery, setSearchQuery] = useState("") + const [showCreateDialog, setShowCreateDialog] = useState(false) + const [showReviewDialog, setShowReviewDialog] = useState(false) + const [selectedTask, setSelectedTask] = useState(null) + const [selectedItems, setSelectedItems] = useState([]) + + // AI打标任务列表 + const tagTasks = [ + { + id: "1", + name: "高价值用户识别", + prompt: "根据用户的消费金额、频次、最近活跃时间,识别高价值用户", + status: "completed", + targetTag: "高价值用户", + dataSource: "用户表+交易表", + affectedCount: 45678, + confidence: 0.92, + createdAt: "2026-01-30 10:00", + completedAt: "2026-01-30 10:15", + needReview: false, + }, + { + id: "2", + name: "流失风险预警", + prompt: "识别最近30天未登录且之前活跃的用户,标记为流失风险", + status: "pending_review", + targetTag: "流失风险", + dataSource: "用户行为表", + affectedCount: 12456, + confidence: 0.87, + createdAt: "2026-01-31 09:00", + completedAt: "2026-01-31 09:12", + needReview: true, + reviewStats: { approved: 0, rejected: 0, pending: 12456 }, + }, + { + id: "3", + name: "兴趣标签推断", + prompt: "根据用户浏览和购买记录,推断用户兴趣偏好", + status: "running", + targetTag: "兴趣标签", + dataSource: "行为日志表", + affectedCount: 0, + confidence: 0, + createdAt: "2026-01-31 14:00", + progress: 67, + }, + { + id: "4", + name: "地域特征识别", + prompt: "根据IP地址和收货地址,识别用户地域特征", + status: "failed", + targetTag: "地域标签", + dataSource: "用户表+订单表", + affectedCount: 0, + confidence: 0, + createdAt: "2026-01-29 15:00", + error: "数据源连接超时", + }, + ] + + // 待审核的标签结果 + const pendingReviews = [ + { id: "r1", userId: "U10001", userName: "张三", currentTags: ["活跃用户"], newTag: "流失风险", confidence: 0.92, reason: "30天未登录" }, + { id: "r2", userId: "U10002", userName: "李四", currentTags: ["普通用户"], newTag: "流失风险", confidence: 0.88, reason: "25天未登录" }, + { id: "r3", userId: "U10003", userName: "王五", currentTags: ["新用户"], newTag: "流失风险", confidence: 0.75, reason: "注册后未活跃" }, + { id: "r4", userId: "U10004", userName: "赵六", currentTags: ["VIP用户"], newTag: "流失风险", confidence: 0.65, reason: "消费频次下降" }, + { id: "r5", userId: "U10005", userName: "钱七", currentTags: ["活跃用户"], newTag: "流失风险", confidence: 0.58, reason: "互动减少" }, + ] + + const getStatusBadge = (status: string) => { + switch (status) { + case "completed": + return 已完成 + case "running": + return 执行中 + case "pending_review": + return 待审核 + case "failed": + return 失败 + default: + return 未知 + } + } + + const getConfidenceColor = (confidence: number) => { + if (confidence >= 0.8) return "text-green-600" + if (confidence >= 0.6) return "text-yellow-600" + return "text-red-600" + } + + const handleOpenReview = (task: any) => { + setSelectedTask(task) + setShowReviewDialog(true) + } + + const handleBatchApprove = () => { + console.log("批量通过", selectedItems) + setSelectedItems([]) + } + + const handleBatchReject = () => { + console.log("批量拒绝", selectedItems) + setSelectedItems([]) + } + + return ( +
+
+ {/* 顶部标题 */} +
+
+

AI智能打标

+

使用AI自动识别用户特征并打标签,支持人工审核确认

+
+
+ +
+
+ + {/* 统计卡片 */} +
+ + +
+
+

任务总数

+

{tagTasks.length}

+
+ +
+
+
+ + +
+
+

待审核

+

+ {tagTasks.filter((t) => t.status === "pending_review").length} +

+
+ +
+
+
+ + +
+
+

已打标用户

+

58,134

+
+ +
+
+
+ + +
+
+

平均置信度

+

87.5%

+
+ +
+
+
+
+ + {/* Tabs */} + +
+ + 打标任务 + 审核中心 + 历史记录 + +
+ + setSearchQuery(e.target.value)} + className="pl-9 w-64 bg-white" + /> +
+
+ + +
+ {tagTasks.map((task) => ( + + +
+
+
+

{task.name}

+ {getStatusBadge(task.status)} + {task.targetTag} +
+

{task.prompt}

+
+ 数据源:{task.dataSource} + {task.affectedCount > 0 && ( + 影响用户:{task.affectedCount.toLocaleString()} + )} + {task.confidence > 0 && ( + + 置信度:{(task.confidence * 100).toFixed(0)}% + + )} + 创建时间:{task.createdAt} +
+ {task.status === "running" && ( +
+
+ 执行进度 + {task.progress}% +
+
+
+
+
+ )} + {task.status === "failed" && ( +
+ 错误信息:{task.error} +
+ )} +
+
+ {task.status === "pending_review" && ( + + )} + {task.status === "failed" && ( + + )} + + + + + + + + 查看日志 + + + + 编辑配置 + + + +
+
+ + + ))} +
+ + + + + +
+ 待审核标签(共 {pendingReviews.length} 条) +
+ {selectedItems.length > 0 && ( + <> + 已选 {selectedItems.length} 项 + + + + )} +
+
+
+ +
+ {pendingReviews.map((item) => ( +
+
+ { + if (checked) { + setSelectedItems([...selectedItems, item.id]) + } else { + setSelectedItems(selectedItems.filter((id) => id !== item.id)) + } + }} + /> +
+
+ {item.userName} + ({item.userId}) +
+
+ 现有标签: + {item.currentTags.map((tag, index) => ( + + {tag} + + ))} + + + + {item.newTag} + +
+

原因:{item.reason}

+
+
+
+
+

置信度

+

+ {(item.confidence * 100).toFixed(0)}% +

+
+
+ + +
+
+
+ ))} +
+
+
+
+ + + + + 历史记录将显示所有已完成的打标任务及其审核结果 + + + + + + {/* 创建任务弹窗 */} + + + + 创建AI打标任务 + 配置AI打标规则,AI将自动识别符合条件的用户并打标签 + +
+
+ + +
+
+ +