fix: resolve deployment errors and add missing files

Fix CSS issue and create missing documentation files
Add new data platform and mobile optimization features

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2025-07-19 02:34:18 +00:00
parent ea95107713
commit 901763fae0
20 changed files with 3216 additions and 1882 deletions

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return null
}

871
app/user-valuation/page.tsx Normal file
View File

@@ -0,0 +1,871 @@
"use client"
import { useState } from "react"
import { Card, CardContent, CardDescription, 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Input } from "@/components/ui/input"
import {
ArrowUpRight,
Download,
Filter,
RefreshCw,
Target,
TrendingUp,
Users,
Layers,
Tag,
BarChart3,
Search,
HelpCircle,
ChevronRight,
ChevronDown,
} from "lucide-react"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"
// 使用通用公司名称和当天时间线的用户数据
const mockUsers = [
{
id: "1",
name: "张明华",
company: "科技创新有限公司",
recency: 1,
frequency: 28,
monetary: 45600,
rfmScore: 95,
level: "高价值",
growth: "+8.5%",
upgradeFromDays: 45,
nextUpgrade: "VIP客户",
upgradeProbability: 85,
lastActive: "今天 14:30",
},
{
id: "2",
name: "李雨婷",
company: "数字营销公司",
recency: 3,
frequency: 22,
monetary: 32800,
rfmScore: 88,
level: "高价值",
growth: "+6.2%",
upgradeFromDays: 28,
nextUpgrade: "白金会员",
upgradeProbability: 78,
lastActive: "今天 13:45",
},
{
id: "3",
name: "王建国",
company: "软件开发工作室",
recency: 2,
frequency: 25,
monetary: 38900,
rfmScore: 91,
level: "高价值",
growth: "+7.8%",
upgradeFromDays: 38,
nextUpgrade: "企业VIP",
upgradeProbability: 82,
lastActive: "今天 12:20",
},
{
id: "4",
name: "刘思琪",
company: "数据分析咨询",
recency: 7,
frequency: 18,
monetary: 25400,
rfmScore: 76,
level: "中高价值",
growth: "+4.1%",
upgradeFromDays: 12,
nextUpgrade: "高价值用户",
upgradeProbability: 68,
lastActive: "今天 11:15",
},
{
id: "5",
name: "陈浩然",
company: "云计算服务商",
recency: 4,
frequency: 20,
monetary: 28700,
rfmScore: 82,
level: "中高价值",
growth: "+5.3%",
upgradeFromDays: 22,
nextUpgrade: "高价值用户",
upgradeProbability: 72,
lastActive: "今天 10:30",
},
{
id: "6",
name: "周欣妍",
company: "用户体验设计",
recency: 12,
frequency: 15,
monetary: 18600,
rfmScore: 65,
level: "中等价值",
growth: "+3.2%",
upgradeFromDays: 8,
nextUpgrade: "中高价值用户",
upgradeProbability: 55,
lastActive: "今天 09:45",
},
{
id: "7",
name: "马志强",
company: "企业解决方案",
recency: 6,
frequency: 16,
monetary: 22100,
rfmScore: 71,
level: "中等价值",
growth: "+4.8%",
upgradeFromDays: 15,
nextUpgrade: "中高价值用户",
upgradeProbability: 61,
lastActive: "今天 08:20",
},
{
id: "8",
name: "赵丽娟",
company: "品牌营销策划",
recency: 15,
frequency: 12,
monetary: 15800,
rfmScore: 58,
level: "中等价值",
growth: "+2.1%",
upgradeFromDays: 5,
nextUpgrade: "中高价值用户",
upgradeProbability: 45,
lastActive: "今天 07:30",
},
]
export default function UserValuationPage() {
const [timeRange, setTimeRange] = useState("30days")
const [searchQuery, setSearchQuery] = useState("")
const [showModelDialog, setShowModelDialog] = useState(false)
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false)
const [expandedSections, setExpandedSections] = useState({
overview: true,
model: false,
analysis: true,
upgradePaths: false,
})
const filteredUsers = mockUsers.filter(
(user) =>
user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
user.company.toLowerCase().includes(searchQuery.toLowerCase()),
)
const getRFMLevelColor = (level: string) => {
switch (level) {
case "高价值":
return "bg-green-100 text-green-800"
case "中高价值":
return "bg-blue-100 text-blue-800"
case "中等价值":
return "bg-yellow-100 text-yellow-800"
case "低价值":
return "bg-gray-100 text-gray-800"
case "流失风险":
return "bg-red-100 text-red-800"
default:
return "bg-gray-100 text-gray-800"
}
}
const toggleSection = (section: string) => {
setExpandedSections((prev) => ({
...prev,
[section]: !prev[section],
}))
}
return (
<div className="container mx-auto p-4 space-y-4 md:space-y-6">
{/* 页面标题和工具栏 */}
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
<div>
<h1 className="text-2xl md:text-3xl font-bold"></h1>
<p className="text-sm md:text-base text-muted-foreground mt-1"></p>
</div>
<div className="flex flex-wrap gap-2">
<Select value={timeRange} onValueChange={setTimeRange}>
<SelectTrigger className="w-[120px] md:w-[140px] text-sm">
<SelectValue placeholder="时间范围" />
</SelectTrigger>
<SelectContent>
<SelectItem value="7days">7</SelectItem>
<SelectItem value="30days">30</SelectItem>
<SelectItem value="90days">90</SelectItem>
</SelectContent>
</Select>
<Button variant="outline" size="icon" className="h-9 w-9 bg-transparent">
<Filter className="h-4 w-4" />
</Button>
<Button variant="outline" size="icon" className="h-9 w-9 bg-transparent">
<RefreshCw className="h-4 w-4" />
</Button>
<Button variant="outline" size="sm" className="hidden md:flex bg-transparent">
<Download className="mr-2 h-4 w-4" />
</Button>
</div>
</div>
{/* 用户估值概览 */}
<Collapsible
open={expandedSections.overview}
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, overview: open }))}
>
<Card className="border-none shadow-md">
<CollapsibleTrigger className="w-full">
<CardHeader className="bg-gradient-to-r from-green-50 to-emerald-50 border-b hover:from-green-100 hover:to-emerald-100 transition-colors cursor-pointer py-3 md:py-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<Target className="h-4 w-4 md:h-5 md:w-5 text-green-600" />
<div className="text-left">
<CardTitle className="text-base md:text-lg flex items-center gap-2">
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation()
setShowModelDialog(true)
}}
className="h-5 w-5 md:h-6 md:w-6 p-0"
>
<HelpCircle className="h-3 w-3 md:h-4 md:w-4" />
</Button>
</CardTitle>
<CardDescription className="text-xs md:text-sm"></CardDescription>
</div>
</div>
{expandedSections.overview ? (
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
) : (
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
)}
</div>
</CardHeader>
</CollapsibleTrigger>
<CollapsibleContent>
<CardContent className="p-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 md:gap-4">
<Card className="bg-gradient-to-br from-green-50 to-emerald-50 border-none shadow-sm">
<CardContent className="p-3 md:p-4">
<div className="flex justify-between items-start">
<div>
<div className="text-xs md:text-sm font-medium text-green-600"></div>
<div className="text-lg md:text-2xl font-bold mt-1 md:mt-2">¥3,248</div>
<div className="text-xs text-green-600 mt-1 flex items-center">
<ArrowUpRight className="h-3 w-3 md:h-4 md:w-4 mr-1" />
+8.2%
</div>
</div>
<div className="bg-green-100 p-1.5 md:p-2 rounded-full">
<Target className="h-3 w-3 md:h-5 md:w-5 text-green-600" />
</div>
</div>
<div className="mt-2 md:mt-3">
<div className="flex justify-between text-xs mb-1">
<span></span>
<span>94%</span>
</div>
<Progress value={94} className="h-1 md:h-1.5 bg-green-100" />
</div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-purple-50 to-pink-50 border-none shadow-sm">
<CardContent className="p-3 md:p-4">
<div className="flex justify-between items-start">
<div>
<div className="text-xs md:text-sm font-medium text-purple-600"></div>
<div className="text-lg md:text-2xl font-bold mt-1 md:mt-2">45,678</div>
<div className="text-xs text-green-600 mt-1 flex items-center">
<ArrowUpRight className="h-3 w-3 md:h-4 md:w-4 mr-1" />
+12.3%
</div>
</div>
<div className="bg-purple-100 p-1.5 md:p-2 rounded-full">
<TrendingUp className="h-3 w-3 md:h-5 md:w-5 text-purple-600" />
</div>
</div>
<div className="mt-2 md:mt-3">
<div className="flex justify-between text-xs mb-1">
<span></span>
<span>28.6%</span>
</div>
<Progress value={28.6} className="h-1 md:h-1.5 bg-purple-100" />
</div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-blue-50 to-indigo-50 border-none shadow-sm">
<CardContent className="p-3 md:p-4">
<div className="flex justify-between items-start">
<div>
<div className="text-xs md:text-sm font-medium text-blue-600"></div>
<div className="text-lg md:text-2xl font-bold mt-1 md:mt-2">15.8%</div>
<div className="text-xs text-green-600 mt-1 flex items-center">
<ArrowUpRight className="h-3 w-3 md:h-4 md:w-4 mr-1" />
+2.1%
</div>
</div>
<div className="bg-blue-100 p-1.5 md:p-2 rounded-full">
<BarChart3 className="h-3 w-3 md:h-5 md:w-5 text-blue-600" />
</div>
</div>
<div className="mt-2 md:mt-3">
<div className="flex justify-between text-xs mb-1">
<span></span>
<span>11.2%</span>
</div>
<Progress value={141} className="h-1 md:h-1.5 bg-blue-100" />
</div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-amber-50 to-yellow-50 border-none shadow-sm">
<CardContent className="p-3 md:p-4">
<div className="flex justify-between items-start">
<div>
<div className="text-xs md:text-sm font-medium text-amber-600"></div>
<div className="text-lg md:text-2xl font-bold mt-1 md:mt-2">12.5%</div>
<div className="text-xs text-green-600 mt-1 flex items-center">
<ArrowUpRight className="h-3 w-3 md:h-4 md:w-4 mr-1" />
+1.8%
</div>
</div>
<div className="bg-amber-100 p-1.5 md:p-2 rounded-full">
<Users className="h-3 w-3 md:h-5 md:w-5 text-amber-600" />
</div>
</div>
<div className="mt-2 md:mt-3">
<div className="flex justify-between text-xs mb-1">
<span></span>
<span>15%</span>
</div>
<Progress value={83} className="h-1 md:h-1.5 bg-amber-100" />
</div>
</CardContent>
</Card>
</div>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
{/* RFM估值模型 */}
<Collapsible
open={expandedSections.model}
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, model: open }))}
>
<Card className="border-none shadow-md">
<CollapsibleTrigger className="w-full">
<CardHeader className="bg-gradient-to-r from-blue-50 to-indigo-50 border-b hover:from-blue-100 hover:to-indigo-100 transition-colors cursor-pointer py-3 md:py-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<BarChart3 className="h-4 w-4 md:h-5 md:w-5 text-blue-600" />
<div className="text-left">
<CardTitle className="flex items-center gap-2 text-base md:text-lg">
RFM估值模型
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation()
setShowModelDialog(true)
}}
className="h-5 w-5 md:h-6 md:w-6 p-0"
>
<HelpCircle className="h-3 w-3 md:h-4 md:w-4" />
</Button>
</CardTitle>
<CardDescription className="text-xs md:text-sm"></CardDescription>
</div>
</div>
{expandedSections.model ? (
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
) : (
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
)}
</div>
</CardHeader>
</CollapsibleTrigger>
<CollapsibleContent>
<CardContent className="p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
<div className="space-y-2">
<h3 className="font-semibold flex items-center text-sm md:text-base">
<span className="bg-red-100 text-red-800 w-5 h-5 md:w-6 md:h-6 rounded-full flex items-center justify-center mr-2 text-xs md:text-sm">
R
</span>
</h3>
<p className="text-xs md:text-sm text-muted-foreground"></p>
<div className="bg-gray-50 p-2 md:p-3 rounded-md">
<div className="text-xs md:text-sm space-y-1">
<div className="flex justify-between">
<span>1-3</span>
<span className="font-medium">5</span>
</div>
<div className="flex justify-between">
<span>4-7</span>
<span className="font-medium">4</span>
</div>
<div className="flex justify-between">
<span>8-14</span>
<span className="font-medium">3</span>
</div>
<div className="flex justify-between">
<span>15-30</span>
<span className="font-medium">2</span>
</div>
<div className="flex justify-between">
<span>30</span>
<span className="font-medium">1</span>
</div>
</div>
</div>
</div>
<div className="space-y-2">
<h3 className="font-semibold flex items-center text-sm md:text-base">
<span className="bg-green-100 text-green-800 w-5 h-5 md:w-6 md:h-6 rounded-full flex items-center justify-center mr-2 text-xs md:text-sm">
F
</span>
</h3>
<p className="text-xs md:text-sm text-muted-foreground"></p>
<div className="bg-gray-50 p-2 md:p-3 rounded-md">
<div className="text-xs md:text-sm space-y-1">
<div className="flex justify-between">
<span>25/</span>
<span className="font-medium">5</span>
</div>
<div className="flex justify-between">
<span>20-24/</span>
<span className="font-medium">4</span>
</div>
<div className="flex justify-between">
<span>15-19/</span>
<span className="font-medium">3</span>
</div>
<div className="flex justify-between">
<span>10-14/</span>
<span className="font-medium">2</span>
</div>
<div className="flex justify-between">
<span>1-9/</span>
<span className="font-medium">1</span>
</div>
</div>
</div>
</div>
<div className="space-y-2">
<h3 className="font-semibold flex items-center text-sm md:text-base">
<span className="bg-blue-100 text-blue-800 w-5 h-5 md:w-6 md:h-6 rounded-full flex items-center justify-center mr-2 text-xs md:text-sm">
M
</span>
</h3>
<p className="text-xs md:text-sm text-muted-foreground"></p>
<div className="bg-gray-50 p-2 md:p-3 rounded-md">
<div className="text-xs md:text-sm space-y-1">
<div className="flex justify-between">
<span>30000</span>
<span className="font-medium">5</span>
</div>
<div className="flex justify-between">
<span>20000-29999</span>
<span className="font-medium">4</span>
</div>
<div className="flex justify-between">
<span>10000-19999</span>
<span className="font-medium">3</span>
</div>
<div className="flex justify-between">
<span>5000-9999</span>
<span className="font-medium">2</span>
</div>
<div className="flex justify-between">
<span>5000</span>
<span className="font-medium">1</span>
</div>
</div>
</div>
</div>
</div>
<div className="mt-4 md:mt-6">
<h3 className="font-semibold mb-3 text-sm md:text-base">RFM评分等级</h3>
<div className="grid grid-cols-2 md:grid-cols-5 gap-2 md:gap-3">
<div className="bg-green-100 p-2 md:p-3 rounded-md text-center">
<p className="font-medium text-green-800 text-xs md:text-sm"></p>
<p className="text-xs text-green-700">85-100</p>
</div>
<div className="bg-blue-100 p-2 md:p-3 rounded-md text-center">
<p className="font-medium text-blue-800 text-xs md:text-sm"></p>
<p className="text-xs text-blue-700">70-84</p>
</div>
<div className="bg-yellow-100 p-2 md:p-3 rounded-md text-center">
<p className="font-medium text-yellow-800 text-xs md:text-sm"></p>
<p className="text-xs text-yellow-700">55-69</p>
</div>
<div className="bg-gray-100 p-2 md:p-3 rounded-md text-center">
<p className="font-medium text-gray-800 text-xs md:text-sm"></p>
<p className="text-xs text-gray-700">40-54</p>
</div>
<div className="bg-red-100 p-2 md:p-3 rounded-md text-center">
<p className="font-medium text-red-800 text-xs md:text-sm"></p>
<p className="text-xs text-red-700">0-39</p>
</div>
</div>
</div>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
{/* 用户价值分析 */}
<Collapsible
open={expandedSections.analysis}
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, analysis: open }))}
>
<Card className="border-none shadow-md">
<CollapsibleTrigger className="w-full">
<CardHeader className="bg-gradient-to-r from-purple-50 to-pink-50 border-b hover:from-purple-100 hover:to-pink-100 transition-colors cursor-pointer py-3 md:py-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<Users className="h-4 w-4 md:h-5 md:w-5 text-purple-600" />
<div className="text-left">
<CardTitle className="text-base md:text-lg"></CardTitle>
<CardDescription className="text-xs md:text-sm">
({filteredUsers.length})
</CardDescription>
</div>
</div>
{expandedSections.analysis ? (
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
) : (
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
)}
</div>
</CardHeader>
</CollapsibleTrigger>
<CollapsibleContent>
<CardContent className="p-4">
<div className="mb-4">
<div className="relative w-full md:w-64">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="搜索用户或公司..."
className="pl-8 text-sm"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
</div>
{/* 移动端卡片布局 */}
<div className="space-y-3 md:hidden">
{filteredUsers.map((user) => (
<div key={user.id} className="bg-white border rounded-lg p-3 space-y-2">
<div className="flex items-center justify-between">
<div>
<p className="font-medium text-sm">{user.name}</p>
<p className="text-xs text-gray-500">{user.company}</p>
</div>
<Badge className={`${getRFMLevelColor(user.level)} text-xs`}>{user.rfmScore}</Badge>
</div>
<div className="grid grid-cols-3 gap-2 text-xs">
<div className="text-center">
<p className="text-gray-500">R值</p>
<p className="font-medium">{user.recency}</p>
</div>
<div className="text-center">
<p className="text-gray-500">F值</p>
<p className="font-medium">{user.frequency}</p>
</div>
<div className="text-center">
<p className="text-gray-500">M值</p>
<p className="font-medium">¥{(user.monetary / 1000).toFixed(0)}K</p>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-gray-500">{user.lastActive}</span>
<span className="text-xs text-green-600">{user.growth}</span>
</div>
</div>
))}
</div>
{/* 桌面端表格布局 */}
<div className="hidden md:block rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-center">RFM评分</TableHead>
<TableHead className="text-center">R值</TableHead>
<TableHead className="text-center">F值</TableHead>
<TableHead className="text-center">M值</TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredUsers.map((user) => (
<TableRow key={user.id} className="hover:bg-gray-50">
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.company}</TableCell>
<TableCell className="text-center">
<Badge className={`${getRFMLevelColor(user.level)}`}>
{user.rfmScore} ({user.level})
</Badge>
</TableCell>
<TableCell className="text-center">{user.recency}</TableCell>
<TableCell className="text-center">{user.frequency}/</TableCell>
<TableCell className="text-center">¥{user.monetary.toLocaleString()}</TableCell>
<TableCell className="text-right text-green-600">{user.growth}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
{/* 用户升级路径分析 */}
<Collapsible
open={expandedSections.upgradePaths}
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, upgradePaths: open }))}
>
<Card className="border-none shadow-md">
<CollapsibleTrigger className="w-full">
<CardHeader className="bg-gradient-to-r from-orange-50 to-red-50 border-b hover:from-orange-100 hover:to-red-100 transition-colors cursor-pointer py-3 md:py-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<TrendingUp className="h-4 w-4 md:h-5 md:w-5 text-orange-600" />
<div className="text-left">
<CardTitle className="flex items-center gap-2 text-base md:text-lg">
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation()
setShowUpgradeDialog(true)
}}
className="h-5 w-5 md:h-6 md:w-6 p-0"
>
<HelpCircle className="h-3 w-3 md:h-4 md:w-4" />
</Button>
</CardTitle>
<CardDescription className="text-xs md:text-sm"></CardDescription>
</div>
</div>
{expandedSections.upgradePaths ? (
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
) : (
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
)}
</div>
</CardHeader>
</CollapsibleTrigger>
<CollapsibleContent>
<CardContent className="p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 md:gap-4 mb-4 md:mb-6">
<Card className="bg-white shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-sm md:text-lg flex items-center">
<Target className="h-4 w-4 md:h-5 md:w-5 mr-2 text-green-600" />
</CardTitle>
</CardHeader>
<CardContent className="text-xs md:text-sm">
<div className="space-y-1 md:space-y-2">
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">2,345</span>
</div>
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">5,678</span>
</div>
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">1,234</span>
</div>
</div>
</CardContent>
</Card>
<Card className="bg-white shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-sm md:text-lg flex items-center">
<Layers className="h-4 w-4 md:h-5 md:w-5 mr-2 text-blue-600" />
</CardTitle>
</CardHeader>
<CardContent className="text-xs md:text-sm">
<div className="space-y-1 md:space-y-2">
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">12.5%</span>
</div>
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">28</span>
</div>
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">1,856</span>
</div>
</div>
</CardContent>
</Card>
<Card className="bg-white shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-sm md:text-lg flex items-center">
<Tag className="h-4 w-4 md:h-5 md:w-5 mr-2 text-purple-600" />
</CardTitle>
</CardHeader>
<CardContent className="text-xs md:text-sm">
<div className="space-y-1 md:space-y-2">
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">¥2.3M</span>
</div>
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">¥1,245</span>
</div>
<div className="flex justify-between items-center">
<span>ROI提升</span>
<span className="font-medium">35.8%</span>
</div>
</div>
</CardContent>
</Card>
</div>
<div className="space-y-3 md:space-y-4">
<h3 className="text-sm md:text-lg font-medium"></h3>
<div className="space-y-2 md:space-y-3">
{filteredUsers.slice(0, 5).map((user) => (
<div
key={user.id}
className="flex flex-col md:flex-row md:items-center justify-between p-3 md:p-4 border rounded-lg hover:bg-gray-50 space-y-2 md:space-y-0"
>
<div className="flex items-center gap-3 md:gap-4">
<div>
<p className="font-medium text-sm md:text-base">{user.name}</p>
<p className="text-xs md:text-sm text-gray-500">{user.company}</p>
</div>
<Badge className={`${getRFMLevelColor(user.level)} text-xs`}>{user.level}</Badge>
</div>
<div className="flex flex-col md:flex-row items-start md:items-center gap-2 md:gap-4">
<div className="text-left md:text-right">
<p className="text-xs md:text-sm font-medium">: {user.nextUpgrade}</p>
<p className="text-xs text-gray-500">: {user.upgradeProbability}%</p>
</div>
<div className="w-full md:w-24">
<Progress value={user.upgradeProbability} className="h-1.5 md:h-2" />
</div>
<Button variant="outline" size="sm" className="text-xs bg-transparent">
</Button>
</div>
</div>
))}
</div>
</div>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
{/* RFM模型说明对话框 */}
<Dialog open={showModelDialog} onOpenChange={setShowModelDialog}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>RFM估值模型说明</DialogTitle>
<DialogDescription>RFM模型是一种用于客户价值评估的经典方法</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="p-4 bg-blue-50 rounded-lg">
<h4 className="font-medium mb-2">RFM模型</h4>
<p className="text-sm text-gray-600">
RFM模型是一种用于分析客户价值和客户创利能力的重要工具RecencyFrequencyMonetary
</p>
</div>
<div className="p-4 bg-green-50 rounded-lg">
<h4 className="font-medium mb-2">使RFM评分</h4>
<p className="text-sm text-gray-600">
1-5150-100便
</p>
</div>
<div className="p-4 bg-purple-50 rounded-lg">
<h4 className="font-medium mb-2"></h4>
<p className="text-sm text-gray-600">
RFM三个维度
</p>
</div>
</div>
</DialogContent>
</Dialog>
{/* 升级路径说明对话框 */}
<Dialog open={showUpgradeDialog} onOpenChange={setShowUpgradeDialog}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="p-4 bg-purple-50 rounded-lg">
<h4 className="font-medium mb-2"></h4>
<p className="text-sm text-gray-600">
RFM评分
</p>
</div>
<div className="p-4 bg-orange-50 rounded-lg">
<h4 className="font-medium mb-2"></h4>
<p className="text-sm text-gray-600">
RFM中得分较低的
</p>
</div>
<div className="p-4 bg-green-50 rounded-lg">
<h4 className="font-medium mb-2"></h4>
<p className="text-sm text-gray-600">
</p>
</div>
</div>
</DialogContent>
</Dialog>
</div>
)
}