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:
27
components/dashboard/dashboard-header.tsx
Normal file
27
components/dashboard/dashboard-header.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
179
components/dashboard/dashboard-overview.tsx
Normal file
179
components/dashboard/dashboard-overview.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
74
components/dashboard/recent-activities.tsx
Normal file
74
components/dashboard/recent-activities.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
36
components/dashboard/traffic-source-analysis.tsx
Normal file
36
components/dashboard/traffic-source-analysis.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
40
components/dashboard/user-assets-summary.tsx
Normal file
40
components/dashboard/user-assets-summary.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
138
components/dashboard/user-behavior-analysis.tsx
Normal file
138
components/dashboard/user-behavior-analysis.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
37
components/dashboard/user-growth-chart.tsx
Normal file
37
components/dashboard/user-growth-chart.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
126
components/dashboard/user-lifecycle-analysis.tsx
Normal file
126
components/dashboard/user-lifecycle-analysis.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
138
components/dashboard/user-portrait-overview.tsx
Normal file
138
components/dashboard/user-portrait-overview.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
153
components/dashboard/user-tags-distribution.tsx
Normal file
153
components/dashboard/user-tags-distribution.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
36
components/dashboard/user-value-distribution.tsx
Normal file
36
components/dashboard/user-value-distribution.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user