refactor: 重构页面布局,样式
This commit is contained in:
+257
-75
@@ -7,98 +7,280 @@ import {
|
||||
Users,
|
||||
Activity,
|
||||
ArrowUpRight,
|
||||
ArrowDownRight
|
||||
ArrowDownRight,
|
||||
Play,
|
||||
Zap
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/card';
|
||||
import { Button } from '../../components/ui/button';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
AreaChart,
|
||||
Area,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer
|
||||
} from 'recharts';
|
||||
|
||||
const chartData = [
|
||||
{ name: '06:00', morning: 400, evening: 240 },
|
||||
{ name: '09:00', morning: 700, evening: 300 },
|
||||
{ name: '12:00', morning: 600, evening: 450 },
|
||||
{ name: '15:00', morning: 800, evening: 500 },
|
||||
{ name: '18:00', morning: 500, evening: 900 },
|
||||
{ name: '21:00', morning: 300, evening: 1200 },
|
||||
{ name: '00:00', morning: 200, evening: 800 },
|
||||
];
|
||||
|
||||
export default function Dashboard() {
|
||||
const stats = [
|
||||
{ name: '总分类数', value: '12', icon: ListMusic, change: '+2', trend: 'up' },
|
||||
{ name: '活跃频道', value: '45', icon: Mic2, change: '+5', trend: 'up' },
|
||||
{ name: '节目总数', value: '3,284', icon: Disc3, change: '+124', trend: 'up' },
|
||||
{ name: '订阅用户', value: '12.4K', icon: Users, change: '-24', trend: 'down' },
|
||||
{
|
||||
name: '总分类数',
|
||||
value: '12',
|
||||
icon: ListMusic,
|
||||
change: '+2',
|
||||
trend: 'up',
|
||||
color: 'from-[#D28F4F] to-[#A64452]',
|
||||
iconColor: 'text-[#D28F4F]'
|
||||
},
|
||||
{
|
||||
name: '活跃频道',
|
||||
value: '45',
|
||||
icon: Mic2,
|
||||
change: '+5',
|
||||
trend: 'up',
|
||||
color: 'from-[#6C5CE7] to-[#A64452]',
|
||||
iconColor: 'text-[#6C5CE7]'
|
||||
},
|
||||
{
|
||||
name: '节目总数',
|
||||
value: '3,284',
|
||||
icon: Disc3,
|
||||
change: '+124',
|
||||
trend: 'up',
|
||||
color: 'from-[#D28F4F] to-[#E29A66]',
|
||||
iconColor: 'text-[#D28F4F]'
|
||||
},
|
||||
{
|
||||
name: '订阅用户',
|
||||
value: '12.4K',
|
||||
icon: Users,
|
||||
change: '-24',
|
||||
trend: 'down',
|
||||
color: 'from-[#8C7E6C] to-[#4A3A2C]',
|
||||
iconColor: 'text-[#4A3A2C]'
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-8 animate-in fade-in duration-500">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div className="space-y-10 pb-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="flex flex-col md:flex-row md:items-center justify-between gap-6"
|
||||
>
|
||||
<div>
|
||||
<h1 className="text-3xl font-extrabold tracking-tight text-slate-900 dark:text-slate-100">欢迎回来,管理员</h1>
|
||||
<p className="text-slate-500 mt-1">这是早安电台管理系统的实时概览。</p>
|
||||
<h1 className="text-4xl font-black tracking-tight text-[#4A3A2C] flex items-center gap-4">
|
||||
欢迎回来,管理员
|
||||
<span className="text-3xl animate-bounce">☕</span>
|
||||
</h1>
|
||||
<p className="text-[#8C7E6C] font-medium mt-3 flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-xl bg-[#FAF5E6] flex items-center justify-center shadow-sm border border-[#D28F4F]/10">
|
||||
<Zap className="w-4 h-4 text-[#D28F4F] fill-[#D28F4F]" />
|
||||
</div>
|
||||
系统运行良好。当前有 <span className="text-[#D28F4F] font-black underline decoration-2 underline-offset-4">1,248</span> 名听众正在共度美好时光。
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 bg-white dark:bg-slate-900 border px-4 py-2 rounded-2xl shadow-sm self-start">
|
||||
<Activity className="w-4 h-4 text-emerald-500 animate-pulse" />
|
||||
<span className="text-sm font-medium text-slate-600 dark:text-slate-400 uppercase tracking-widest">系统在线</span>
|
||||
<div className="flex items-center space-x-4 bg-white/40 backdrop-blur-3xl border border-[#D28F4F]/10 px-8 py-4 rounded-[2.5rem] shadow-glass self-start relative group">
|
||||
<div className="relative">
|
||||
<Activity className="w-5 h-5 text-emerald-500" />
|
||||
<div className="absolute inset-0 bg-emerald-500 blur-md opacity-40 animate-pulse" />
|
||||
</div>
|
||||
<span className="text-[11px] font-black text-[#8C7E6C] uppercase tracking-[0.3em]">实时广播中</span>
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-[#D28F4F] rounded-full shadow-[0_0_10px_#D28F4F]" />
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
||||
{stats.map((stat) => (
|
||||
<Card key={stat.name} className="border-none shadow-sm hover:shadow-md transition-shadow group rounded-3xl overflow-hidden bg-white dark:bg-slate-900">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-semibold text-slate-500 uppercase tracking-wider">{stat.name}</CardTitle>
|
||||
<div className={`p-2 rounded-xl group-hover:scale-110 transition-transform ${stat.trend === 'up' ? 'bg-emerald-50 text-emerald-600' : 'bg-rose-50 text-rose-600'
|
||||
}`}>
|
||||
<stat.icon className="h-5 w-5" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold tracking-tight text-slate-900 dark:text-white mt-1">{stat.value}</div>
|
||||
<p className="text-xs text-slate-500 mt-2 flex items-center">
|
||||
<span className={`flex items-center font-medium mr-1 ${stat.trend === 'up' ? 'text-emerald-500' : 'text-rose-500'
|
||||
}`}>
|
||||
{stat.trend === 'up' ? <ArrowUpRight className="w-3 h-3 mr-0.5" /> : <ArrowDownRight className="w-3 h-3 mr-0.5" />}
|
||||
{stat.change}
|
||||
</span>
|
||||
较上月
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-4">
|
||||
{stats.map((stat, index) => (
|
||||
<motion.div
|
||||
key={stat.name}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: index * 0.1, type: "spring", stiffness: 100 }}
|
||||
>
|
||||
<Card className="glass-card warm-noise border-none group relative overflow-hidden h-full rounded-[2.5rem] p-4">
|
||||
<div className={`absolute -top-10 -right-10 w-40 h-40 bg-gradient-to-br ${stat.color} opacity-0 group-hover:opacity-10 blur-3xl transition-opacity duration-700`} />
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-6">
|
||||
<CardTitle className="text-[11px] font-black text-[#8C7E6C] uppercase tracking-[0.2em]">{stat.name}</CardTitle>
|
||||
<div className={`p-4 rounded-[1.5rem] transition-all duration-700 shadow-sm border border-white/40 bg-white/90 ${stat.iconColor} group-hover:rotate-12 group-hover:scale-110 group-hover:shadow-[0_20px_40px_rgba(0,0,0,0.05)]`}>
|
||||
<stat.icon className="h-6 w-6" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-4xl font-black tracking-tight text-[#4A3A2C] mb-4">{stat.value}</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center bg-white/60 px-3 py-1.5 rounded-full border border-white/40 backdrop-blur-md shadow-sm">
|
||||
<span className={`flex items-center text-[11px] font-black ${stat.trend === 'up' ? 'text-emerald-600' : 'text-rose-600'}`}>
|
||||
{stat.trend === 'up' ? <ArrowUpRight className="w-3.5 h-3.5 mr-1" /> : <ArrowDownRight className="w-3.5 h-3.5 mr-1" />}
|
||||
{stat.change}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-[10px] font-black text-[#8C7E6C]/40 uppercase tracking-[0.2em]">增长率</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<Card className="lg:col-span-2 border-none shadow-sm rounded-3xl bg-white dark:bg-slate-900">
|
||||
<CardHeader className="pb-0">
|
||||
<CardTitle className="text-lg font-bold flex items-center">
|
||||
<TrendingUp className="w-5 h-5 mr-2 text-primary" />
|
||||
增长趋势分析
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="h-[300px] flex items-center justify-center text-slate-400 bg-slate-50/50 dark:bg-slate-950/20 m-6 rounded-2xl border-2 border-dashed">
|
||||
图表占位 (Growth data visualization)
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="border-none shadow-sm rounded-3xl bg-white dark:bg-slate-900">
|
||||
<CardHeader className="pb-0">
|
||||
<CardTitle className="text-lg font-bold flex items-center">
|
||||
<Radio className="w-5 h-5 mr-2 text-primary" />
|
||||
热门频道排行
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-6">
|
||||
<div className="space-y-4">
|
||||
{[1, 2, 3, 4, 5].map(i => (
|
||||
<div key={i} className="flex items-center justify-between p-3 rounded-2xl hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors cursor-pointer group">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center font-bold text-slate-500 group-hover:bg-primary group-hover:text-white transition-colors">
|
||||
#{i}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-slate-900 dark:text-slate-100">早安爵士 #{i}</p>
|
||||
<p className="text-xs text-slate-400">经典古典预设</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-sm font-bold text-slate-900 dark:text-slate-100">{(1000 / i).toFixed(0)}</p>
|
||||
<p className="text-[10px] text-slate-400 uppercase tracking-tighter">收听量</p>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<motion.div
|
||||
className="lg:col-span-2"
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
>
|
||||
<Card className="glass-card warm-noise border-none h-full overflow-hidden rounded-[3rem] p-6 relative z-10">
|
||||
<CardHeader className="p-10 border-b border-[#4A3A2C]/5 flex flex-row items-center justify-between bg-[#FAF5E6]/40 rounded-t-[2.5rem]">
|
||||
<div>
|
||||
<CardTitle className="text-2xl font-black flex items-center tracking-tight text-[#4A3A2C]">
|
||||
<div className="w-10 h-10 rounded-[1.2rem] bg-[#D28F4F] flex items-center justify-center mr-4 shadow-lg shadow-[#D28F4F]/20">
|
||||
<TrendingUp className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
收听热度分布
|
||||
</CardTitle>
|
||||
<p className="text-[#8C7E6C] text-sm mt-2 font-medium">监测不同时段的声波流向与用户留存</p>
|
||||
</div>
|
||||
<div className="flex gap-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-3 h-3 rounded-full bg-[#D28F4F] shadow-[0_0_12px_#D28F4F]" />
|
||||
<span className="text-xs font-black text-[#8C7E6C] uppercase underline decoration-[#D28F4F]/20 underline-offset-4">晨间氛围</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-3 h-3 rounded-full bg-[#A64452] shadow-[0_0_12px_#A64452]" />
|
||||
<span className="text-xs font-black text-[#8C7E6C] uppercase underline decoration-[#A64452]/20 underline-offset-4">傍晚陪伴</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-10">
|
||||
<div className="h-[400px] w-full">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart data={chartData}>
|
||||
<defs>
|
||||
<linearGradient id="colorMorning" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#D28F4F" stopOpacity={0.3} />
|
||||
<stop offset="95%" stopColor="#D28F4F" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
<linearGradient id="colorEvening" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#A64452" stopOpacity={0.3} />
|
||||
<stop offset="95%" stopColor="#A64452" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#4A3A2C" strokeOpacity={0.05} />
|
||||
<XAxis
|
||||
dataKey="name"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: '#8C7E6C', fontSize: 11, fontWeight: 900 }}
|
||||
dy={15}
|
||||
/>
|
||||
<YAxis hide />
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
borderRadius: '24px',
|
||||
border: 'none',
|
||||
boxShadow: '0 20px 50px rgba(74, 58, 44, 0.1)',
|
||||
background: 'rgba(255, 253, 235, 0.9)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
padding: '16px',
|
||||
color: '#4A3A2C'
|
||||
}}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="morning"
|
||||
stroke="#D28F4F"
|
||||
strokeWidth={5}
|
||||
fillOpacity={1}
|
||||
fill="url(#colorMorning)"
|
||||
animationDuration={2000}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="evening"
|
||||
stroke="#A64452"
|
||||
strokeWidth={5}
|
||||
fillOpacity={1}
|
||||
fill="url(#colorEvening)"
|
||||
animationDuration={2000}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
>
|
||||
<Card className="glass-card warm-noise border-none h-full overflow-hidden rounded-[3rem] p-6 relative z-10">
|
||||
<CardHeader className="p-8 border-b border-[#4A3A2C]/5 bg-[#FAF5E6]/40 rounded-t-[2.5rem]">
|
||||
<CardTitle className="text-xl font-black flex items-center tracking-tight text-[#4A3A2C] uppercase tracking-widest">
|
||||
<div className="w-10 h-10 rounded-[1.2rem] bg-white flex items-center justify-center mr-4 shadow-sm">
|
||||
<Radio className="w-5 h-5 text-[#D28F4F]" />
|
||||
</div>
|
||||
热门频道排行
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-8">
|
||||
<div className="space-y-6">
|
||||
{[1, 2, 3, 4, 5].map(i => (
|
||||
<div
|
||||
key={i}
|
||||
className={`flex items-center justify-between p-5 rounded-[2rem] transition-all cursor-pointer group hover:scale-[1.03] border border-[#D28F4F]/5 relative overflow-hidden ${i === 1 ? 'bg-gradient-to-r from-[#D28F4F]/10 to-[#A64452]/5 border-[#D28F4F]/20 shadow-sm' : 'hover:bg-white/60'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center space-x-6 relative z-10">
|
||||
<div className="w-14 h-14 rounded-2xl overflow-hidden relative shadow-md ring-4 ring-white">
|
||||
<img src={`https://picsum.photos/seed/radio${i}/100/100`} alt="" className="object-cover w-full h-full group-hover:scale-125 transition-transform duration-1000" />
|
||||
<div className="absolute inset-0 bg-[#4A3A2C]/20 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<Play className="w-6 h-6 text-white fill-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-base font-black text-[#4A3A2C]">爵士小镇 #{i}</p>
|
||||
{i <= 3 && (
|
||||
<div className={`w-2 h-2 rounded-full ${i === 1 ? 'bg-[#D28F4F]' : i === 2 ? 'bg-[#8C7E6C]' : 'bg-[#E29A66]'} shadow-[0_0_10px_currentColor]`} />
|
||||
)}
|
||||
</div>
|
||||
<div className="audio-wave mt-2 opacity-40 group-hover:opacity-100 transition-opacity flex gap-0.5">
|
||||
<span className="w-1 h-3 bg-[#D28F4F] rounded-full animate-wave" />
|
||||
<span className="w-1 h-5 bg-[#D28F4F] rounded-full animate-wave-slow" />
|
||||
<span className="w-1 h-2 bg-[#D28F4F] rounded-full animate-wave" />
|
||||
<span className="w-1 h-4 bg-[#D28F4F] rounded-full animate-wave-slow" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right relative z-10">
|
||||
<p className="text-lg font-black text-[#4A3A2C]">{(1240 / i).toFixed(0)}</p>
|
||||
<p className="text-[10px] text-[#8C7E6C] font-black uppercase tracking-widest">活跃度</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button variant="ghost" className="w-full mt-10 rounded-[1.5rem] h-14 font-black text-xs uppercase tracking-[0.3em] text-[#8C7E6C] hover:text-[#D28F4F] hover:bg-white transition-all">
|
||||
查看全量声波图谱
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user