chore: 以本地为准,上传全部并替换 GitHub

This commit is contained in:
卡若
2026-02-03 11:36:53 +08:00
parent 1219166526
commit b404bf546e
131 changed files with 37618 additions and 3930 deletions

View File

@@ -1,23 +1,55 @@
"use client"
import Link from "next/link"
import { useState } from "react"
import { useState, useEffect, useRef } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
import { Cpu, BarChart3, TrendingUp, Users, Target, Brain, ArrowRight, Send, Sparkles } from "lucide-react"
import { Cpu, BarChart3, TrendingUp, Users, Target, Brain, ArrowRight, Send, Sparkles, Loader2 } from "lucide-react"
interface ChatMessage {
role: "user" | "assistant"
content: string
timestamp: string
}
interface AIStatus {
status: string
model: string
database?: {
connected: boolean
totalUsers: number
latency: number
}
}
export default function AIAssistant() {
const [message, setMessage] = useState("")
const [chatHistory, setChatHistory] = useState([
const [isLoading, setIsLoading] = useState(false)
const [aiStatus, setAIStatus] = useState<AIStatus | null>(null)
const chatEndRef = useRef<HTMLDivElement>(null)
const [chatHistory, setChatHistory] = useState<ChatMessage[]>([
{
role: "assistant",
content: "你好我是神射手AI助手可以帮助你分析用户数据、生成报告、预测趋势。有什么我可以帮助你的吗?",
timestamp: "10:30",
content: "你好我是神射手AI助手可以帮助你查询用户数据、分析RFM估值。\n\n💡 试试:\n- 查 13407000001\n- 28533368 qq\n- 系统状态\n- 帮助",
timestamp: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }),
},
])
// 获取 AI 状态
useEffect(() => {
fetch("/api/ai-chat")
.then(res => res.json())
.then(data => setAIStatus(data))
.catch(console.error)
}, [])
// 自动滚动到底部
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: "smooth" })
}, [chatHistory])
const aiFeatures = [
{
icon: <BarChart3 className="w-8 h-8" />,
@@ -53,24 +85,52 @@ export default function AIAssistant() {
},
]
const quickQuestions = ["分析今日新增用户特征", "预测本月用户增长趋势", "高价值用户流失预警", "生成本周运营报告"]
const quickQuestions = ["查 13407000001", "系统状态", "RFM分析", "高价值用户 TOP10", "帮助"]
const handleSend = () => {
if (!message.trim()) return
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" }),
},
])
const handleSend = async () => {
if (!message.trim() || isLoading) return
const userMessage: ChatMessage = {
role: "user",
content: message,
timestamp: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }),
}
setChatHistory(prev => [...prev, userMessage])
setMessage("")
setIsLoading(true)
try {
const response = await fetch("/api/ai-chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: userMessage.content })
})
const data = await response.json()
if (data.success && data.response) {
setChatHistory(prev => [...prev, {
role: "assistant",
content: data.response.content,
timestamp: data.response.timestamp || new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })
}])
} else {
setChatHistory(prev => [...prev, {
role: "assistant",
content: `⚠️ ${data.error || "请求失败,请稍后重试"}`,
timestamp: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })
}])
}
} catch (error: any) {
setChatHistory(prev => [...prev, {
role: "assistant",
content: `⚠️ 网络错误: ${error.message}`,
timestamp: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })
}])
} finally {
setIsLoading(false)
}
}
return (
@@ -97,22 +157,24 @@ export default function AIAssistant() {
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-gray-600"></span>
<Badge variant="outline" className="border-green-200 text-green-600 bg-green-50">
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse mr-2"></div>
<Badge variant="outline" className={`${aiStatus?.status === 'online' ? 'border-green-200 text-green-600 bg-green-50' : 'border-yellow-200 text-yellow-600 bg-yellow-50'}`}>
<div className={`w-2 h-2 rounded-full ${aiStatus?.status === 'online' ? 'bg-green-500' : 'bg-yellow-500'} animate-pulse mr-2`}></div>
{aiStatus?.status === 'online' ? '运行中' : '加载中'}
</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>
<span className="text-gray-900 font-semibold">{aiStatus?.model || '神射手 AI'}</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>
<span className="text-gray-600"></span>
<span className="text-gray-900 font-semibold">{aiStatus?.database?.latency || '--'}ms</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>
<span className="text-gray-600"></span>
<span className="text-green-600 font-semibold">
{aiStatus?.database?.totalUsers ? `${(aiStatus.database.totalUsers / 1e8).toFixed(1)}亿` : '--'}
</span>
</div>
</div>
</CardContent>
@@ -184,13 +246,24 @@ export default function AIAssistant() {
<div
className={`max-w-[80%] rounded-2xl p-4 shadow-sm ${chat.role === "user" ? "bg-gradient-to-br from-blue-500 to-purple-500 text-white" : "bg-white border border-gray-100 text-gray-800"}`}
>
<div className="mb-1">{chat.content}</div>
<div className="mb-1 whitespace-pre-wrap">{chat.content}</div>
<div className={`text-xs ${chat.role === "user" ? "text-blue-100" : "text-gray-400"}`}>
{chat.timestamp}
</div>
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-white border border-gray-100 rounded-2xl p-4 shadow-sm">
<div className="flex items-center gap-2 text-gray-500">
<Loader2 className="w-4 h-4 animate-spin" />
<span>...</span>
</div>
</div>
</div>
)}
<div ref={chatEndRef} />
</div>
<div className="mb-4 flex flex-wrap gap-2">
@@ -224,9 +297,10 @@ export default function AIAssistant() {
/>
<Button
onClick={handleSend}
className="bg-gradient-to-r from-blue-500 to-purple-500 h-[88px] w-20 shadow-md hover:shadow-lg transition-shadow"
disabled={isLoading || !message.trim()}
className="bg-gradient-to-r from-blue-500 to-purple-500 h-[88px] w-20 shadow-md hover:shadow-lg transition-shadow disabled:opacity-50"
>
<Send className="w-6 h-6" />
{isLoading ? <Loader2 className="w-6 h-6 animate-spin" /> : <Send className="w-6 h-6" />}
</Button>
</div>
</CardContent>