refactor: overhaul UI for streamlined user experience

Redesign navigation, home overview, user portrait, and valuation pages
with improved functionality and responsive design.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2025-07-18 13:47:12 +00:00
parent 440b310c6f
commit 2408d50cb0
316 changed files with 55785 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import { Button } from "@/components/ui/button"
import { Calendar, Download, Filter } from "lucide-react"
export function DashboardHeader() {
return (
<div className="flex flex-col md:flex-row justify-between items-start md:items-center space-y-4 md:space-y-0">
<div>
<h1 className="text-2xl font-bold tracking-tight"></h1>
<p className="text-muted-foreground"></p>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline" size="sm">
<Calendar className="mr-2 h-4 w-4" />
</Button>
<Button variant="outline" size="sm">
<Filter className="mr-2 h-4 w-4" />
</Button>
<Button variant="outline" size="sm">
<Download className="mr-2 h-4 w-4" />
</Button>
</div>
</div>
)
}

View File

@@ -0,0 +1,179 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { formatNumber, getGrowthClass } from "@/lib/utils"
import { ArrowDown, ArrowUp } from "lucide-react"
export function DashboardOverview() {
// 这里应该从API获取实际数据
const overviewData = {
userAcquisition: {
total: 12458,
growth: 12.5,
sources: [
{ name: "微信", value: 4523, growth: 15.2 },
{ name: "抖音", value: 3254, growth: 24.3 },
{ name: "小红书", value: 2145, growth: 8.7 },
{ name: "公众号", value: 1536, growth: -3.2 },
{ name: "官网", value: 1000, growth: 5.6 },
],
},
userRetention: {
rate: 68.5,
growth: 2.3,
periods: [
{ name: "7天", value: 85.2, growth: 1.2 },
{ name: "30天", value: 68.5, growth: 2.3 },
{ name: "90天", value: 42.3, growth: 3.5 },
{ name: "180天", value: 28.7, growth: -1.2 },
{ name: "365天", value: 15.4, growth: -2.5 },
],
},
userConversion: {
rate: 12.5,
growth: 3.2,
stages: [
{ name: "浏览", value: 100, growth: 0 },
{ name: "注册", value: 35.2, growth: 2.1 },
{ name: "首次购买", value: 12.5, growth: 3.2 },
{ name: "复购", value: 8.3, growth: 5.4 },
{ name: "会员", value: 4.2, growth: 1.8 },
],
},
}
return (
<Tabs defaultValue="acquisition" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="acquisition"></TabsTrigger>
<TabsTrigger value="retention"></TabsTrigger>
<TabsTrigger value="conversion"></TabsTrigger>
</TabsList>
<TabsContent value="acquisition">
<Card className="border shadow-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium"></p>
<p className="text-2xl font-bold">{formatNumber(overviewData.userAcquisition.total)}</p>
</div>
<div className={`flex items-center ${getGrowthClass(overviewData.userAcquisition.growth)}`}>
{overviewData.userAcquisition.growth > 0 ? (
<ArrowUp className="h-4 w-4 mr-1" />
) : (
<ArrowDown className="h-4 w-4 mr-1" />
)}
<span>{Math.abs(overviewData.userAcquisition.growth)}%</span>
</div>
</div>
<div className="space-y-2">
{overviewData.userAcquisition.sources.map((source) => (
<div key={source.name} className="flex items-center justify-between">
<div className="flex items-center">
<span className="text-sm">{source.name}</span>
</div>
<div className="flex items-center space-x-2">
<span className="text-sm">{formatNumber(source.value)}</span>
<span className={`text-xs ${getGrowthClass(source.growth)}`}>
{source.growth > 0 ? "+" : ""}
{source.growth}%
</span>
</div>
</div>
))}
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="retention">
<Card className="border shadow-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">30</p>
<p className="text-2xl font-bold">{overviewData.userRetention.rate}%</p>
</div>
<div className={`flex items-center ${getGrowthClass(overviewData.userRetention.growth)}`}>
{overviewData.userRetention.growth > 0 ? (
<ArrowUp className="h-4 w-4 mr-1" />
) : (
<ArrowDown className="h-4 w-4 mr-1" />
)}
<span>{Math.abs(overviewData.userRetention.growth)}%</span>
</div>
</div>
<div className="space-y-2">
{overviewData.userRetention.periods.map((period) => (
<div key={period.name} className="flex items-center justify-between">
<div className="flex items-center">
<span className="text-sm">{period.name}</span>
</div>
<div className="flex items-center space-x-2">
<span className="text-sm">{period.value}%</span>
<span className={`text-xs ${getGrowthClass(period.growth)}`}>
{period.growth > 0 ? "+" : ""}
{period.growth}%
</span>
</div>
</div>
))}
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="conversion">
<Card className="border shadow-sm">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium"></p>
<p className="text-2xl font-bold">{overviewData.userConversion.rate}%</p>
</div>
<div className={`flex items-center ${getGrowthClass(overviewData.userConversion.growth)}`}>
{overviewData.userConversion.growth > 0 ? (
<ArrowUp className="h-4 w-4 mr-1" />
) : (
<ArrowDown className="h-4 w-4 mr-1" />
)}
<span>{Math.abs(overviewData.userConversion.growth)}%</span>
</div>
</div>
<div className="space-y-2">
{overviewData.userConversion.stages.map((stage) => (
<div key={stage.name} className="flex items-center justify-between">
<div className="flex items-center">
<span className="text-sm">{stage.name}</span>
</div>
<div className="flex items-center space-x-2">
<span className="text-sm">{stage.value}%</span>
<span className={`text-xs ${getGrowthClass(stage.growth)}`}>
{stage.growth > 0 ? "+" : ""}
{stage.growth}%
</span>
</div>
</div>
))}
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
)
}

View File

@@ -0,0 +1,74 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { formatDate } from "@/lib/utils"
import { Activity, User, ShoppingCart, MessageSquare } from "lucide-react"
export function RecentActivities() {
// 这里应该从API获取实际数据
const activities = [
{
id: 1,
type: "user",
title: "新用户注册",
description: "张三通过微信小程序注册成为新用户",
time: new Date(2023, 6, 15, 14, 30),
},
{
id: 2,
type: "purchase",
title: "完成购买",
description: "李四购买了高级会员服务",
time: new Date(2023, 6, 15, 13, 45),
},
{
id: 3,
type: "message",
title: "客户咨询",
description: "王五咨询了产品使用问题",
time: new Date(2023, 6, 15, 11, 20),
},
{
id: 4,
type: "activity",
title: "活动参与",
description: "赵六参与了新品推广活动",
time: new Date(2023, 6, 15, 10, 15),
},
]
const getIcon = (type: string) => {
switch (type) {
case "user":
return <User className="h-4 w-4" />
case "purchase":
return <ShoppingCart className="h-4 w-4" />
case "message":
return <MessageSquare className="h-4 w-4" />
case "activity":
return <Activity className="h-4 w-4" />
default:
return <Activity className="h-4 w-4" />
}
}
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-base font-medium"></CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{activities.map((activity) => (
<div key={activity.id} className="flex items-start space-x-3">
<div className="bg-primary/10 rounded-full p-2 text-primary">{getIcon(activity.type)}</div>
<div className="space-y-1">
<p className="text-sm font-medium">{activity.title}</p>
<p className="text-xs text-muted-foreground">{activity.description}</p>
<p className="text-xs text-muted-foreground">{formatDate(activity.time)}</p>
</div>
</div>
))}
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,36 @@
"use client"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"
export function TrafficSourceAnalysis() {
// 这里应该从API获取实际数据
const data = [
{ source: "微信", value: 4000 },
{ source: "抖音", value: 3000 },
{ source: "小红书", value: 2000 },
{ source: "公众号", value: 2780 },
{ source: "官网", value: 1890 },
{ source: "其他", value: 2390 },
]
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-base font-medium"></CardTitle>
</CardHeader>
<CardContent>
<div className="h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data}>
<XAxis dataKey="source" />
<YAxis />
<Tooltip />
<Bar dataKey="value" fill="hsl(var(--primary))" />
</BarChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,40 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { formatNumber } from "@/lib/utils"
export function UserAssetsSummary() {
// 这里应该从API获取实际数据
const stats = {
totalUsers: 125689,
activeUsers: 78452,
highValueUsers: 12458,
conversionRate: 12.5,
}
return (
<Card className="border shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-base font-medium"></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1">
<p className="text-xs text-muted-foreground"></p>
<p className="text-2xl font-bold">{formatNumber(stats.totalUsers)}</p>
</div>
<div className="space-y-1">
<p className="text-xs text-muted-foreground"></p>
<p className="text-2xl font-bold">{formatNumber(stats.activeUsers)}</p>
</div>
<div className="space-y-1">
<p className="text-xs text-muted-foreground"></p>
<p className="text-2xl font-bold">{formatNumber(stats.highValueUsers)}</p>
</div>
<div className="space-y-1">
<p className="text-xs text-muted-foreground"></p>
<p className="text-2xl font-bold">{stats.conversionRate}%</p>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,138 @@
"use client"
import { Card, CardContent } from "@/components/ui/card"
import { LineChart, BarChart } from "@/components/charts"
interface UserBehaviorAnalysisProps {
timeRange: string
}
export function UserBehaviorAnalysis({ timeRange }: UserBehaviorAnalysisProps) {
// 这里应该根据timeRange从API获取实际数据
return (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground">/</div>
<div className="text-2xl font-bold">12.5</div>
<div className="text-xs text-green-600"> 1.2</div>
</div>
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground">使/</div>
<div className="text-2xl font-bold">42</div>
<div className="text-xs text-green-600"> 5</div>
</div>
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground">/</div>
<div className="text-2xl font-bold">8.3</div>
<div className="text-xs text-green-600"> 0.5</div>
</div>
</div>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<LineChart
data={{
labels: ["1月", "2月", "3月", "4月", "5月", "6月", "7月"],
datasets: [
{
label: "日活跃用户",
data: [45000, 48000, 52000, 55000, 58000, 62000, 65000],
borderColor: "rgb(59, 130, 246)",
backgroundColor: "rgba(59, 130, 246, 0.1)",
},
{
label: "周活跃用户",
data: [75000, 78000, 82000, 85000, 88000, 92000, 95000],
borderColor: "rgb(16, 185, 129)",
backgroundColor: "rgba(16, 185, 129, 0.1)",
},
{
label: "月活跃用户",
data: [95000, 98000, 102000, 105000, 108000, 112000, 115000],
borderColor: "rgb(249, 115, 22)",
backgroundColor: "rgba(249, 115, 22, 0.1)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<BarChart
data={{
labels: ["0-3点", "3-6点", "6-9点", "9-12点", "12-15点", "15-18点", "18-21点", "21-24点"],
datasets: [
{
label: "活跃用户数",
data: [8000, 4000, 25000, 58000, 42000, 38000, 75000, 62000],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2">使</h3>
<BarChart
data={{
labels: ["iOS", "Android", "Windows", "MacOS", "其他"],
datasets: [
{
label: "用户数量",
data: [52000, 55000, 10000, 5000, 1000],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
</div>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<LineChart
data={{
labels: ["浏览", "搜索", "加入购物车", "下单", "支付", "评价", "复购"],
datasets: [
{
label: "高价值用户",
data: [100, 95, 85, 80, 78, 65, 60],
borderColor: "rgb(16, 185, 129)",
backgroundColor: "rgba(16, 185, 129, 0.1)",
},
{
label: "中价值用户",
data: [100, 85, 65, 55, 50, 35, 25],
borderColor: "rgb(59, 130, 246)",
backgroundColor: "rgba(59, 130, 246, 0.1)",
},
{
label: "低价值用户",
data: [100, 70, 40, 25, 20, 10, 5],
borderColor: "rgb(156, 163, 175)",
backgroundColor: "rgba(156, 163, 175, 0.1)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
</div>
)
}

View File

@@ -0,0 +1,37 @@
"use client"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"
export function UserGrowthChart() {
// 这里应该从API获取实际数据
const data = [
{ date: "1月", users: 4000 },
{ date: "2月", users: 5000 },
{ date: "3月", users: 6000 },
{ date: "4月", users: 7000 },
{ date: "5月", users: 9000 },
{ date: "6月", users: 12000 },
{ date: "7月", users: 15000 },
]
return (
<Card className="col-span-2">
<CardHeader className="pb-2">
<CardTitle className="text-base font-medium"></CardTitle>
</CardHeader>
<CardContent>
<div className="h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={data}>
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="users" stroke="hsl(var(--primary))" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,126 @@
"use client"
import { Card, CardContent } from "@/components/ui/card"
import { LineChart, BarChart } from "@/components/charts"
interface UserLifecycleAnalysisProps {
timeRange: string
}
export function UserLifecycleAnalysis({ timeRange }: UserLifecycleAnalysisProps) {
// 这里应该根据timeRange从API获取实际数据
return (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground"></div>
<div className="text-2xl font-bold">12,458</div>
<div className="text-xs text-green-600"> 8.2%</div>
</div>
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground">(30)</div>
<div className="text-2xl font-bold">68.5%</div>
<div className="text-xs text-green-600"> 2.3%</div>
</div>
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground"></div>
<div className="text-2xl font-bold">5.2%</div>
<div className="text-xs text-green-600"> 0.8%</div>
</div>
</div>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<BarChart
data={{
labels: ["获取阶段", "激活阶段", "留存阶段", "转化阶段", "忠诚阶段"],
datasets: [
{
label: "用户数量",
data: [25000, 18000, 12000, 8000, 5000],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<LineChart
data={{
labels: ["1天", "7天", "14天", "30天", "60天", "90天", "180天", "365天"],
datasets: [
{
label: "留存率",
data: [95, 85, 75, 68, 55, 42, 28, 15],
borderColor: "rgb(59, 130, 246)",
backgroundColor: "rgba(59, 130, 246, 0.1)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<BarChart
data={{
labels: ["产品体验差", "需求不匹配", "竞品转移", "价格因素", "服务问题", "其他原因"],
datasets: [
{
label: "流失用户比例",
data: [35, 25, 20, 10, 8, 2],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
</div>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<LineChart
data={{
labels: ["1月", "2月", "3月", "4月", "5月", "6月", "7月"],
datasets: [
{
label: "高价值用户比例",
data: [18, 19, 20, 22, 23, 24, 25],
borderColor: "rgb(16, 185, 129)",
backgroundColor: "rgba(16, 185, 129, 0.1)",
},
{
label: "中价值用户比例",
data: [42, 43, 44, 45, 45, 46, 45],
borderColor: "rgb(59, 130, 246)",
backgroundColor: "rgba(59, 130, 246, 0.1)",
},
{
label: "低价值用户比例",
data: [40, 38, 36, 33, 32, 30, 30],
borderColor: "rgb(156, 163, 175)",
backgroundColor: "rgba(156, 163, 175, 0.1)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
</div>
)
}

View File

@@ -0,0 +1,138 @@
"use client"
import { Card, CardContent } from "@/components/ui/card"
import { PieChart, BarChart } from "@/components/charts"
interface UserPortraitOverviewProps {
timeRange: string
}
export function UserPortraitOverview({ timeRange }: UserPortraitOverviewProps) {
// 这里应该根据timeRange从API获取实际数据
return (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<PieChart
data={{
labels: ["男性", "女性", "未知"],
datasets: [
{
data: [45, 52, 3],
backgroundColor: ["rgb(59, 130, 246)", "rgb(236, 72, 153)", "rgb(156, 163, 175)"],
},
],
}}
height={180}
/>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<PieChart
data={{
labels: ["18岁以下", "18-24岁", "25-34岁", "35-44岁", "45-54岁", "55岁以上"],
datasets: [
{
data: [5, 25, 40, 20, 7, 3],
backgroundColor: [
"rgb(59, 130, 246)",
"rgb(16, 185, 129)",
"rgb(249, 115, 22)",
"rgb(139, 92, 246)",
"rgb(236, 72, 153)",
"rgb(156, 163, 175)",
],
},
],
}}
height={180}
/>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<PieChart
data={{
labels: ["高价值", "中价值", "低价值"],
datasets: [
{
data: [25, 45, 30],
backgroundColor: ["rgb(16, 185, 129)", "rgb(59, 130, 246)", "rgb(156, 163, 175)"],
},
],
}}
height={180}
/>
</CardContent>
</Card>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"> Top 10</h3>
<BarChart
data={{
labels: ["北京", "上海", "广东", "浙江", "江苏", "四川", "湖北", "福建", "山东", "河南"],
datasets: [
{
label: "用户数量",
data: [3200, 2800, 4500, 2100, 1900, 1600, 1400, 1200, 1100, 900],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<BarChart
data={{
labels: ["学生", "企业员工", "自由职业", "企业管理者", "公务员", "其他"],
datasets: [
{
label: "用户数量",
data: [15000, 35000, 20000, 18000, 7000, 5000],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
</div>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-2"></h3>
<BarChart
data={{
labels: ["科技", "金融", "教育", "旅游", "健康", "美食", "时尚", "体育", "娱乐", "汽车"],
datasets: [
{
label: "用户数量",
data: [6500, 4800, 3900, 5200, 4100, 6800, 3500, 2900, 5500, 3200],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
</div>
)
}

View File

@@ -0,0 +1,153 @@
"use client"
import { Card, CardContent } from "@/components/ui/card"
import { PieChart, BarChart } from "@/components/charts"
import { Badge } from "@/components/ui/badge"
interface UserTagsDistributionProps {
timeRange: string
}
export function UserTagsDistribution({ timeRange }: UserTagsDistributionProps) {
// 这里应该根据timeRange从API获取实际数据
// 模拟标签分类数据
const tagCategories = [
{ name: "行为特征", count: 45, color: "bg-blue-100 text-blue-800" },
{ name: "偏好特征", count: 38, color: "bg-green-100 text-green-800" },
{ name: "价值特征", count: 22, color: "bg-purple-100 text-purple-800" },
{ name: "生命周期", count: 15, color: "bg-yellow-100 text-yellow-800" },
{ name: "渠道来源", count: 12, color: "bg-indigo-100 text-indigo-800" },
{ name: "社交属性", count: 8, color: "bg-pink-100 text-pink-800" },
]
// 模拟热门标签数据
const hotTags = [
{ name: "高价值用户", count: 12458, category: "价值特征" },
{ name: "活跃用户", count: 45678, category: "行为特征" },
{ name: "数据分析师", count: 8765, category: "职业角色" },
{ name: "营销决策者", count: 6543, category: "职业角色" },
{ name: "技术爱好者", count: 15432, category: "偏好特征" },
{ name: "潜在客户", count: 23456, category: "价值特征" },
{ name: "流失风险", count: 5678, category: "生命周期" },
{ name: "企业客户", count: 3456, category: "客户类型" },
{ name: "新注册用户", count: 7890, category: "生命周期" },
{ name: "社交活跃", count: 12345, category: "社交属性" },
]
return (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-4"></h3>
<PieChart
data={{
labels: tagCategories.map((cat) => cat.name),
datasets: [
{
data: tagCategories.map((cat) => cat.count),
backgroundColor: [
"rgb(59, 130, 246)",
"rgb(16, 185, 129)",
"rgb(139, 92, 246)",
"rgb(249, 115, 22)",
"rgb(99, 102, 241)",
"rgb(236, 72, 153)",
],
},
],
}}
height={250}
/>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-4"> Top 10</h3>
<BarChart
data={{
labels: hotTags.map((tag) => tag.name),
datasets: [
{
label: "用户数量",
data: hotTags.map((tag) => tag.count),
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</CardContent>
</Card>
</div>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-4"></h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{tagCategories.map((category) => (
<div key={category.name} className="space-y-2">
<div className="flex justify-between items-center">
<Badge className={category.color}>{category.name}</Badge>
<span className="text-sm text-muted-foreground">{category.count}</span>
</div>
<div className="space-y-1">
{hotTags
.filter((tag) => tag.category === category.name)
.slice(0, 3)
.map((tag) => (
<div key={tag.name} className="flex justify-between items-center text-sm">
<span>{tag.name}</span>
<span className="text-muted-foreground">{(tag.count / 1000).toFixed(1)}k用户</span>
</div>
))}
</div>
</div>
))}
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<h3 className="text-sm font-medium mb-4"></h3>
<div className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground">/</div>
<div className="text-2xl font-bold">5.8</div>
<div className="text-xs text-green-600"> 0.3</div>
</div>
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground"></div>
<div className="text-2xl font-bold">92.5%</div>
<div className="text-xs text-green-600"> 1.2%</div>
</div>
<div className="bg-primary/10 rounded-lg p-4">
<div className="text-sm text-muted-foreground"></div>
<div className="text-2xl font-bold">87.3%</div>
<div className="text-xs text-green-600"> 2.1%</div>
</div>
</div>
<BarChart
data={{
labels: ["0个标签", "1-3个标签", "4-6个标签", "7-9个标签", "10个以上标签"],
datasets: [
{
label: "用户数量",
data: [7500, 25000, 45000, 35000, 12500],
backgroundColor: "rgba(59, 130, 246, 0.8)",
},
],
}}
height={250}
/>
</div>
</CardContent>
</Card>
</div>
)
}

View File

@@ -0,0 +1,36 @@
"use client"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { PieChart, Pie, Cell, ResponsiveContainer, Legend, Tooltip } from "recharts"
export function UserValueDistribution() {
// 这里应该从API获取实际数据
const data = [
{ name: "高价值", value: 25, color: "hsl(var(--primary))" },
{ name: "中价值", value: 45, color: "hsl(var(--secondary))" },
{ name: "低价值", value: 30, color: "hsl(var(--muted))" },
]
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-base font-medium"></CardTitle>
</CardHeader>
<CardContent>
<div className="h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie data={data} cx="50%" cy="50%" innerRadius={40} outerRadius={80} paddingAngle={2} dataKey="value">
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Tooltip />
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
)
}