Files
users/app/data-market/page.tsx

327 lines
12 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 { 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 Link from "next/link"
import {
Package,
Server,
Webhook,
ArrowUpRight,
TrendingUp,
Users,
Zap,
DollarSign,
Loader2,
Diamond,
Award,
Medal,
Shield,
Target,
BarChart3,
Globe,
ArrowDownToLine,
ArrowUpFromLine,
} from "lucide-react"
export default function DataMarketPage() {
const [loading, setLoading] = useState(true)
const [pools, setPools] = useState<any[]>([])
const [stats, setStats] = useState({
totalPackages: 0,
totalApis: 8,
totalSubscriptions: 45,
monthlyRevenue: 125000,
apiCalls: 8956234
})
useEffect(() => {
loadData()
}, [])
const loadData = async () => {
setLoading(true)
try {
const res = await fetch('/api/traffic-packages?action=pools')
const data = await res.json()
if (data.success) {
setPools(data.pools)
setStats(prev => ({
...prev,
totalPackages: data.pools.length
}))
}
} catch (error) {
console.error('加载失败:', error)
} finally {
setLoading(false)
}
}
const formatNumber = (num: number): string => {
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`
if (num >= 1000) return `${(num / 1000).toFixed(0)}K`
return num.toLocaleString()
}
// 流量池图标映射
const poolIcons: Record<string, any> = {
diamond: Diamond,
gold: Award,
silver: Medal,
bronze: Shield,
potential: Target
}
// 流量池颜色映射
const poolColors: Record<string, string> = {
diamond: 'from-purple-500 to-pink-500',
gold: 'from-yellow-500 to-orange-500',
silver: 'from-gray-400 to-gray-500',
bronze: 'from-orange-400 to-orange-600',
potential: 'from-blue-400 to-cyan-500'
}
const modules = [
{
title: "流量包",
icon: Package,
color: "text-purple-600",
bg: "bg-purple-100",
href: "/data-market/packages",
desc: "用户人群包创建与导出,按流量池分层管理",
stats: `${pools.length}个流量池`,
},
{
title: "API市场",
icon: Server,
color: "text-blue-600",
bg: "bg-blue-100",
href: "/data-market/api",
desc: "开放API接口服务支持用户查询、画像等",
stats: `${stats.totalApis}个API`,
},
{
title: "开放接口",
icon: Globe,
color: "text-orange-600",
bg: "bg-orange-100",
href: "/data-market/open-api",
desc: "第三方系统对接,存客宝/点了码数据流入流出",
stats: "3个接入方",
},
{
title: "数据订阅",
icon: Webhook,
color: "text-green-600",
bg: "bg-green-100",
href: "/data-market/subscription",
desc: "实时数据推送服务支持Webhook订阅",
stats: `${stats.totalSubscriptions}个订阅`,
},
]
if (loading) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-purple-50/20 flex items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-blue-500" />
<span className="ml-3 text-gray-500">...</span>
</div>
)
}
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-purple-50/20">
<div className="p-6 space-y-6">
{/* 顶部标题 */}
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div>
<h1 className="text-2xl font-bold text-gray-900"></h1>
<p className="text-sm text-gray-500 mt-1">
</p>
</div>
</div>
{/* 统计概览 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Card className="bg-white/80 backdrop-blur border-0 shadow-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500"></p>
<p className="text-2xl font-bold text-gray-900">{pools.length}</p>
</div>
<div className="w-12 h-12 rounded-xl bg-purple-100 flex items-center justify-center">
<Package className="h-6 w-6 text-purple-600" />
</div>
</div>
</CardContent>
</Card>
<Card className="bg-white/80 backdrop-blur border-0 shadow-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500">API接口</p>
<p className="text-2xl font-bold text-gray-900">{stats.totalApis}</p>
</div>
<div className="w-12 h-12 rounded-xl bg-blue-100 flex items-center justify-center">
<Server className="h-6 w-6 text-blue-600" />
</div>
</div>
</CardContent>
</Card>
<Card className="bg-white/80 backdrop-blur border-0 shadow-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500">API调用</p>
<p className="text-2xl font-bold text-gray-900">{formatNumber(stats.apiCalls)}</p>
</div>
<div className="w-12 h-12 rounded-xl bg-green-100 flex items-center justify-center">
<Zap className="h-6 w-6 text-green-600" />
</div>
</div>
</CardContent>
</Card>
<Card className="bg-white/80 backdrop-blur border-0 shadow-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500"></p>
<p className="text-2xl font-bold text-gray-900">¥{formatNumber(stats.monthlyRevenue)}</p>
</div>
<div className="w-12 h-12 rounded-xl bg-orange-100 flex items-center justify-center">
<DollarSign className="h-6 w-6 text-orange-600" />
</div>
</div>
</CardContent>
</Card>
</div>
{/* 功能模块 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 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/70 backdrop-blur group hover:-translate-y-1">
<CardContent className="p-6">
<div className={`w-14 h-14 rounded-2xl ${module.bg} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform`}>
<module.icon className={`w-7 h-7 ${module.color}`} />
</div>
<h3 className="text-lg font-bold text-gray-900 mb-2">{module.title}</h3>
<p className="text-sm text-gray-500 mb-4">{module.desc}</p>
<div className="flex items-center justify-between">
<Badge variant="secondary">{module.stats}</Badge>
<ArrowUpRight className="w-5 h-5 text-gray-400 group-hover:text-blue-500 transition-colors" />
</div>
</CardContent>
</Card>
</Link>
))}
</div>
{/* 流量池资产 */}
<Card className="border-none shadow-lg bg-white/80 backdrop-blur">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Target className="h-5 w-5 text-purple-500" />
<Badge variant="outline" className="ml-2"></Badge>
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
{pools.map((pool) => {
const PoolIcon = poolIcons[pool.id] || Package
const color = poolColors[pool.id] || 'from-gray-400 to-gray-500'
return (
<Link key={pool.id} href={`/data-market/packages?pool=${pool.id}`}>
<Card className="cursor-pointer hover:shadow-lg transition-all duration-300 border-0 overflow-hidden group">
<div className={`h-2 bg-gradient-to-r ${color}`} />
<CardContent className="p-4">
<div className={`w-12 h-12 rounded-xl bg-gradient-to-r ${color} flex items-center justify-center mb-3 group-hover:scale-110 transition-transform`}>
<PoolIcon className="h-6 w-6 text-white" />
</div>
<h3 className="font-bold text-gray-900 mb-1">{pool.name}</h3>
<div className="flex items-center justify-between mt-3">
<span className="text-xl font-bold text-gray-900">
{formatNumber(pool.count || 0)}
</span>
<span className="text-xs text-gray-500"></span>
</div>
</CardContent>
</Card>
</Link>
)
})}
</div>
</CardContent>
</Card>
{/* 数据变现趋势 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="border-none shadow-md bg-white/80">
<CardHeader>
<CardTitle className="text-sm font-medium flex items-center gap-2">
<TrendingUp className="h-4 w-4 text-green-500" />
API调用趋势
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
{[
{ name: '用户画像API', calls: 3500000, growth: 12.5 },
{ name: '智能搜索API', calls: 2800000, growth: 8.3 },
{ name: '人群圈选API', calls: 1500000, growth: 25.6 },
{ name: 'RFM评分API', calls: 1156234, growth: 15.2 },
].map((api, i) => (
<div key={i} className="flex items-center justify-between p-3 rounded-lg bg-gray-50">
<div>
<div className="font-medium text-gray-900">{api.name}</div>
<div className="text-sm text-gray-500">{formatNumber(api.calls)} </div>
</div>
<Badge className="bg-green-100 text-green-700">
+{api.growth}%
</Badge>
</div>
))}
</div>
</CardContent>
</Card>
<Card className="border-none shadow-md bg-white/80">
<CardHeader>
<CardTitle className="text-sm font-medium flex items-center gap-2">
<BarChart3 className="h-4 w-4 text-blue-500" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{[
{ name: '用户行为数据', percentage: 35 },
{ name: '交易数据', percentage: 28 },
{ name: '标签更新', percentage: 22 },
{ name: '用户画像变更', percentage: 15 },
].map((sub, i) => (
<div key={i}>
<div className="flex items-center justify-between mb-1">
<span className="text-sm text-gray-700">{sub.name}</span>
<span className="text-sm font-medium">{sub.percentage}%</span>
</div>
<Progress value={sub.percentage} className="h-2" />
</div>
))}
</div>
</CardContent>
</Card>
</div>
</div>
</div>
)
}