Files
users/app/page.tsx
v0 b17b488f8e refactor: restructure navigation and module layout
Reorganize navigation and module structure based on new requirements.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
2026-01-31 04:32:36 +00:00

397 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState, useEffect } from "react"
import {
Users,
Database,
RefreshCw,
Activity,
Globe,
Brain,
Tags,
LineChart,
Package,
ArrowUpRight,
Zap,
Target,
CheckCircle,
} from "lucide-react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import { useRouter } from "next/navigation"
import Link from "next/link"
interface SystemMetrics {
dataTaskSuccessRate: number
tagCoverageRate: number
valuePredictionAccuracy: number
apiDailyCallGrowth: number
}
interface DataFlowStats {
totalUsers: number
totalProjects: number
totalDataSources: number
totalTags: number
totalModels: number
totalApiCalls: number
}
export default function OverviewPage() {
const router = useRouter()
const [searchQuery, setSearchQuery] = useState("")
const [isRefreshing, setIsRefreshing] = useState(false)
const [lastUpdate, setLastUpdate] = useState(new Date())
// 核心OKR指标 (按PRD定义)
const [metrics, setMetrics] = useState<SystemMetrics>({
dataTaskSuccessRate: 99.7,
tagCoverageRate: 82.3,
valuePredictionAccuracy: 87.5,
apiDailyCallGrowth: 32.8,
})
// 数据流统计
const [stats, setStats] = useState<DataFlowStats>({
totalUsers: 4028567890,
totalProjects: 1256,
totalDataSources: 28,
totalTags: 1892,
totalModels: 15,
totalApiCalls: 8956234,
})
// 实时数据流
const [dataFlows, setDataFlows] = useState([
{ source: "存客宝", type: "用户行为", count: 125680, status: "running" },
{ source: "触客宝", type: "互动数据", count: 89234, status: "running" },
{ source: "数智员工", type: "账号数据", count: 45678, status: "running" },
{ source: "外部API", type: "征信数据", count: 12890, status: "waiting" },
])
useEffect(() => {
const interval = setInterval(() => {
// 模拟实时数据更新
setStats((prev) => ({
...prev,
totalUsers: prev.totalUsers + Math.floor(Math.random() * 1000),
totalApiCalls: prev.totalApiCalls + Math.floor(Math.random() * 100),
}))
setDataFlows((prev) =>
prev.map((flow) => ({
...flow,
count: flow.count + Math.floor(Math.random() * 100),
})),
)
}, 3000)
return () => clearInterval(interval)
}, [])
const refreshData = async () => {
setIsRefreshing(true)
await new Promise((r) => setTimeout(r, 1000))
setLastUpdate(new Date())
setIsRefreshing(false)
}
const formatNumber = (num: number): string => {
if (num >= 1000000000) return `${(num / 1000000000).toFixed(2)}B`
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`
return num.toString()
}
// 5大功能模块快捷入口按HTML文档重构
const modules = [
{
title: "数据接入",
icon: Database,
color: "text-blue-600",
bg: "bg-blue-100",
href: "/data-ingestion/sources",
desc: "数据源管理、清洗规则、任务调度、数据血缘",
stats: `${stats.totalDataSources}个数据源`,
},
{
title: "标签画像",
icon: Tags,
color: "text-green-600",
bg: "bg-green-100",
href: "/tag-portrait/tags",
desc: "标签体系、用户画像、人群圈选",
stats: `${stats.totalTags}个标签`,
},
{
title: "AI Agent",
icon: Brain,
color: "text-purple-600",
bg: "bg-purple-100",
href: "/ai-agent/chat",
desc: "智能对话、AI打标、AI清洗、自然语言查询",
stats: "5大AI能力",
},
{
title: "数据输出",
icon: Package,
color: "text-orange-600",
bg: "bg-orange-100",
href: "/data-output/packages",
desc: "流量包、API市场、数据订阅",
stats: `${formatNumber(stats.totalApiCalls)}次调用`,
},
{
title: "系统监控",
icon: LineChart,
color: "text-cyan-600",
bg: "bg-cyan-100",
href: "/system/health",
desc: "系统健康、告警中心、操作日志",
stats: "实时监控",
},
]
return (
<div className="space-y-6 p-6">
{/* Header */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-gray-900"></h1>
<p className="text-gray-500 mt-1"></p>
</div>
<div className="flex items-center gap-3">
<Badge variant="outline" className="bg-green-50 text-green-600 border-green-200 px-3 py-1">
<Activity className="w-3 h-3 mr-1" />
</Badge>
<span className="text-xs text-gray-400"> {lastUpdate.toLocaleTimeString()}</span>
<Button variant="outline" size="sm" onClick={refreshData} disabled={isRefreshing} className="bg-white/50">
<RefreshCw className={`w-4 h-4 mr-2 ${isRefreshing ? "animate-spin" : ""}`} />
</Button>
</div>
</div>
{/* 核心OKR指标 */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
<Card className="border-none shadow-sm bg-white/60 backdrop-blur-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-gray-500"></span>
<Badge className="bg-green-100 text-green-700 text-xs">99.5%</Badge>
</div>
<div className="text-3xl font-bold text-gray-900">{metrics.dataTaskSuccessRate}%</div>
<Progress value={metrics.dataTaskSuccessRate} className="h-2 mt-2" />
</CardContent>
</Card>
<Card className="border-none shadow-sm bg-white/60 backdrop-blur-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-gray-500"></span>
<Badge className="bg-blue-100 text-blue-700 text-xs">80%</Badge>
</div>
<div className="text-3xl font-bold text-gray-900">{metrics.tagCoverageRate}%</div>
<Progress value={metrics.tagCoverageRate} className="h-2 mt-2" />
</CardContent>
</Card>
<Card className="border-none shadow-sm bg-white/60 backdrop-blur-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-gray-500"></span>
<Badge className="bg-purple-100 text-purple-700 text-xs">85%</Badge>
</div>
<div className="text-3xl font-bold text-gray-900">{metrics.valuePredictionAccuracy}%</div>
<Progress value={metrics.valuePredictionAccuracy} className="h-2 mt-2" />
</CardContent>
</Card>
<Card className="border-none shadow-sm bg-white/60 backdrop-blur-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-gray-500">API调用增长</span>
<Badge className="bg-orange-100 text-orange-700 text-xs">30%QoQ</Badge>
</div>
<div className="text-3xl font-bold text-gray-900">+{metrics.apiDailyCallGrowth}%</div>
<Progress value={metrics.apiDailyCallGrowth} className="h-2 mt-2" />
</CardContent>
</Card>
</div>
{/* 用户资产总览 */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<Card className="lg:col-span-2 border-none shadow-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white overflow-hidden relative">
<div className="absolute top-0 right-0 w-64 h-64 bg-white/10 rounded-full -mr-16 -mt-16 blur-3xl" />
<CardHeader className="pb-2 relative z-10">
<CardTitle className="flex items-center gap-2 text-blue-100">
<Users className="w-5 h-5" />
</CardTitle>
</CardHeader>
<CardContent className="relative z-10">
<div className="flex items-baseline gap-2 mb-4">
<span className="text-5xl font-bold">{formatNumber(stats.totalUsers)}</span>
<span className="text-blue-100"></span>
</div>
<div className="grid grid-cols-3 gap-4 pt-4 border-t border-white/20">
<div>
<div className="text-2xl font-bold text-green-300">+2.3%</div>
<div className="text-sm text-blue-100"></div>
</div>
<div>
<div className="text-2xl font-bold">{stats.totalProjects.toLocaleString()}</div>
<div className="text-sm text-blue-100"></div>
</div>
<div>
<div className="text-2xl font-bold">125.6B</div>
<div className="text-sm text-blue-100">()</div>
</div>
</div>
</CardContent>
</Card>
{/* 实时数据流 */}
<Card className="border-none shadow-md bg-white/60 backdrop-blur-md">
<CardHeader className="pb-2">
<CardTitle className="text-gray-700 text-sm font-medium flex items-center gap-2">
<Zap className="w-4 h-4 text-yellow-500" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{dataFlows.map((flow, i) => (
<div key={i} className="flex items-center justify-between p-2 rounded-lg bg-gray-50">
<div className="flex items-center gap-2">
<div
className={`w-2 h-2 rounded-full ${flow.status === "running" ? "bg-green-500 animate-pulse" : "bg-yellow-500"}`}
/>
<div>
<div className="text-sm font-medium text-gray-900">{flow.source}</div>
<div className="text-xs text-gray-500">{flow.type}</div>
</div>
</div>
<div className="text-right">
<div className="text-sm font-semibold text-gray-900">{flow.count.toLocaleString()}</div>
<div className="text-xs text-gray-500">/</div>
</div>
</div>
))}
</CardContent>
</Card>
</div>
{/* 5大功能模块入口 */}
<div>
<h2 className="text-lg font-semibold text-gray-900 mb-4"></h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4">
{modules.map((module, index) => (
<Link key={index} href={module.href}>
<Card className="h-full cursor-pointer hover:shadow-lg transition-all duration-300 border-none shadow-sm bg-white/60 backdrop-blur-md group hover:-translate-y-1">
<CardContent className="p-5">
<div
className={`w-12 h-12 rounded-2xl ${module.bg} flex items-center justify-center mb-3 group-hover:scale-110 transition-transform`}
>
<module.icon className={`w-6 h-6 ${module.color}`} />
</div>
<h3 className="font-bold text-gray-900 mb-1">{module.title}</h3>
<p className="text-xs text-gray-500 mb-2 line-clamp-2">{module.desc}</p>
<div className="flex items-center justify-between">
<Badge variant="secondary" className="text-xs">
{module.stats}
</Badge>
<ArrowUpRight className="w-4 h-4 text-gray-400 group-hover:text-blue-500 transition-colors" />
</div>
</CardContent>
</Card>
</Link>
))}
</div>
</div>
{/* 系统关联图说明 */}
<Card className="border-none shadow-md bg-white/60 backdrop-blur-md">
<CardHeader>
<CardTitle className="text-gray-700 text-sm font-medium flex items-center gap-2">
<Globe className="w-4 h-4" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* 数据流入 */}
<div className="space-y-3">
<h4 className="font-semibold text-gray-900 flex items-center gap-2">
<ArrowUpRight className="w-4 h-4 text-blue-500" />
</h4>
<div className="space-y-2">
{[
{ name: "存客宝", desc: "用户基础信息、交易流水" },
{ name: "触客宝", desc: "互动行为、活动参与" },
{ name: "数智员工", desc: "账号信息、粉丝画像" },
{ name: "外部系统", desc: "征信数据、行业数据" },
].map((item, i) => (
<div key={i} className="flex items-start gap-2 p-2 rounded-lg bg-blue-50">
<CheckCircle className="w-4 h-4 text-blue-500 mt-0.5" />
<div>
<div className="text-sm font-medium text-gray-900">{item.name}</div>
<div className="text-xs text-gray-500">{item.desc}</div>
</div>
</div>
))}
</div>
</div>
{/* 神射手处理 */}
<div className="space-y-3">
<h4 className="font-semibold text-gray-900 flex items-center gap-2">
<Target className="w-4 h-4 text-purple-500" />
</h4>
<div className="space-y-2">
{[
{ name: "数据治理", desc: "清洗、标准化、质量监控" },
{ name: "标签计算", desc: "规则/模型/AI标签生成" },
{ name: "价值评估", desc: "CLV、RFM、流失预测" },
{ name: "AI洞察", desc: "智能分析、报告生成" },
].map((item, i) => (
<div key={i} className="flex items-start gap-2 p-2 rounded-lg bg-purple-50">
<Zap className="w-4 h-4 text-purple-500 mt-0.5" />
<div>
<div className="text-sm font-medium text-gray-900">{item.name}</div>
<div className="text-xs text-gray-500">{item.desc}</div>
</div>
</div>
))}
</div>
</div>
{/* 服务输出 */}
<div className="space-y-3">
<h4 className="font-semibold text-gray-900 flex items-center gap-2">
<Package className="w-4 h-4 text-green-500" />
</h4>
<div className="space-y-2">
{[
{ name: "存客宝/触客宝", desc: "画像API、人群包推送" },
{ name: "数智员工", desc: "账号价值评估API" },
{ name: "聚宝盆", desc: "核心指标聚合接口" },
{ name: "外部客户", desc: "行业报告、趋势分析包" },
].map((item, i) => (
<div key={i} className="flex items-start gap-2 p-2 rounded-lg bg-green-50">
<ArrowUpRight className="w-4 h-4 text-green-500 mt-0.5" />
<div>
<div className="text-sm font-medium text-gray-900">{item.name}</div>
<div className="text-xs text-gray-500">{item.desc}</div>
</div>
</div>
))}
</div>
</div>
</div>
</CardContent>
</Card>
</div>
)
}