refactor: restructure project into 5 core modules

Organize project by 5 core modules based on requirement docs.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2026-01-31 04:18:24 +00:00
parent b4aa620c93
commit 22e725887a
74 changed files with 4219 additions and 9234 deletions

View File

@@ -0,0 +1,222 @@
"use client"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { TrendingUp, TrendingDown, AlertCircle, Download, RefreshCw } from 'lucide-react'
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, AreaChart, Area } from 'recharts'
export default function TrendPrediction() {
const userGrowthPrediction = [
{ date: '2024-06', actual: 428, predicted: null },
{ date: '2024-07', actual: null, predicted: 445 },
{ date: '2024-08', actual: null, predicted: 468 },
{ date: '2024-09', actual: null, predicted: 495 },
{ date: '2024-10', actual: null, predicted: 528 },
{ date: '2024-11', actual: null, predicted: 565 },
{ date: '2024-12', actual: null, predicted: 608 },
]
const valuePrediction = [
{ date: '2024-06', actual: 125.8, predicted: null },
{ date: '2024-07', actual: null, predicted: 132.5 },
{ date: '2024-08', actual: null, predicted: 141.2 },
{ date: '2024-09', actual: null, predicted: 151.8 },
{ date: '2024-10', actual: null, predicted: 164.5 },
{ date: '2024-11', actual: null, predicted: 179.2 },
{ date: '2024-12', actual: null, predicted: 196.5 },
]
const churnRiskData = [
{ segment: '高价值用户', risk: 8.5, count: 1285, trend: 'down' },
{ segment: 'VIP用户', risk: 12.3, count: 856, trend: 'stable' },
{ segment: '活跃用户', risk: 18.6, count: 12856, trend: 'up' },
{ segment: '沉默用户', risk: 45.8, count: 28560, trend: 'up' },
]
const opportunityData = [
{ name: '新品推广机会', potential: '¥85.6M', probability: 78, timeframe: '未来30天' },
{ name: '复购唤醒', potential: '¥56.2M', probability: 65, timeframe: '未来15天' },
{ name: '会员升级', potential: '¥38.5M', probability: 82, timeframe: '未来7天' },
{ name: '交叉销售', potential: '¥92.8M', probability: 58, timeframe: '未来45天' },
]
return (
<div className="min-h-screen bg-gray-50 p-6">
<div className="mb-8 flex items-center justify-between">
<div>
<h1 className="text-4xl font-bold text-gray-900 mb-2"></h1>
<p className="text-gray-600"></p>
</div>
<div className="flex items-center gap-4">
<Button variant="outline">
<RefreshCw className="w-4 h-4 mr-2" />
</Button>
<Button>
<Download className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
{/* 预测指标 */}
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 mb-6">
<Card className="bg-gradient-to-br from-blue-50 to-blue-100 border-blue-200">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-gray-600"></CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-blue-600 mb-1">445M</div>
<div className="text-sm text-gray-600">+3.9% </div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-green-50 to-green-100 border-green-200">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-gray-600"></CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-green-600 mb-1">¥132.5亿</div>
<div className="text-sm text-gray-600">+5.3% </div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-orange-50 to-orange-100 border-orange-200">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-gray-600"></CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-orange-600 mb-1">42.5K</div>
<div className="text-sm text-gray-600"></div>
</CardContent>
</Card>
<Card className="bg-gradient-to-br from-purple-50 to-purple-100 border-purple-200">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-gray-600"></CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-purple-600 mb-1">94.2%</div>
<div className="text-sm text-gray-600"></div>
</CardContent>
</Card>
</div>
{/* 趋势图表 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<Card>
<CardHeader>
<CardTitle className="text-gray-900 flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-blue-600" />
</CardTitle>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={userGrowthPrediction}>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis dataKey="date" stroke="#6b7280" />
<YAxis stroke="#6b7280" />
<Tooltip />
<Line type="monotone" dataKey="actual" stroke="#3b82f6" strokeWidth={2} name="实际值" />
<Line type="monotone" dataKey="predicted" stroke="#8b5cf6" strokeWidth={2} strokeDasharray="5 5" name="预测值" />
</LineChart>
</ResponsiveContainer>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-gray-900 flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-green-600" />
</CardTitle>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={valuePrediction}>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis dataKey="date" stroke="#6b7280" />
<YAxis stroke="#6b7280" />
<Tooltip />
<Area type="monotone" dataKey="actual" stroke="#10b981" fill="#10b981" fillOpacity={0.3} name="实际值" />
<Area type="monotone" dataKey="predicted" stroke="#8b5cf6" fill="#8b5cf6" fillOpacity={0.2} strokeDasharray="5 5" name="预测值" />
</AreaChart>
</ResponsiveContainer>
</CardContent>
</Card>
</div>
{/* 流失风险预警 */}
<Card className="mb-6">
<CardHeader>
<CardTitle className="text-gray-900 flex items-center gap-2">
<AlertCircle className="w-5 h-5 text-orange-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{churnRiskData.map((item, index) => (
<div key={index} className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
<div className="flex items-center gap-4">
<div>
<div className="text-lg font-semibold text-gray-900">{item.segment}</div>
<div className="text-sm text-gray-600">{item.count.toLocaleString()} </div>
</div>
</div>
<div className="flex items-center gap-6">
<div className="text-right">
<div className="text-2xl font-bold text-orange-600">{item.risk}%</div>
<div className="text-xs text-gray-600"></div>
</div>
{item.trend === 'down' ? (
<TrendingDown className="w-6 h-6 text-green-600" />
) : item.trend === 'up' ? (
<TrendingUp className="w-6 h-6 text-red-600" />
) : (
<div className="w-6 h-6 flex items-center justify-center">
<div className="w-4 h-0.5 bg-gray-400"></div>
</div>
)}
</div>
</div>
))}
</div>
</CardContent>
</Card>
{/* 商机预测 */}
<Card>
<CardHeader>
<CardTitle className="text-gray-900 flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-purple-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{opportunityData.map((item, index) => (
<div key={index} className="p-4 bg-gradient-to-br from-purple-50 to-pink-50 rounded-lg border border-purple-200">
<div className="flex items-center justify-between mb-2">
<h3 className="text-lg font-semibold text-gray-900">{item.name}</h3>
<Badge variant="outline" className="border-purple-500 text-purple-600 bg-purple-50">
{item.timeframe}
</Badge>
</div>
<div className="flex items-end justify-between">
<div>
<div className="text-sm text-gray-600 mb-1"></div>
<div className="text-2xl font-bold text-purple-600">{item.potential}</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-600 mb-1"></div>
<div className="text-2xl font-bold text-green-600">{item.probability}%</div>
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
)
}