refactor: 页面重构
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
Coins
|
||||
} from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { AnimatedCounter } from '@/components/AnimatedCounter.tsx';
|
||||
import {
|
||||
AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend
|
||||
} from 'recharts';
|
||||
@@ -40,11 +41,11 @@ import type { RadioChannel } from '@/types/radio.ts';
|
||||
|
||||
// ─── Color Palette ───
|
||||
const COLORS = {
|
||||
listen: '#D28F4F', // primary (Orange)
|
||||
sub: '#6A7F6A', // secondary (Green)
|
||||
renew: '#C86354', // distinct warm red
|
||||
muted: '#8C7E6C',
|
||||
chart: ['#D28F4F', '#6A7F6A', '#C86354', '#E8B878', '#8BAE8B'],
|
||||
listen: '#0f172a', // Slate-900 (primary)
|
||||
sub: '#3b82f6', // Blue-500
|
||||
renew: '#10b981', // Emerald-500
|
||||
muted: '#9ca3af', // Gray-400
|
||||
chart: ['#0f172a', '#3b82f6', '#10b981', '#f59e0b', '#6366f1'],
|
||||
};
|
||||
|
||||
export default function Dashboard() {
|
||||
@@ -244,7 +245,7 @@ export default function Dashboard() {
|
||||
value: vipStats.activeVipUsers,
|
||||
icon: Crown,
|
||||
subLabel: '当前生效的尊享会员',
|
||||
color: '#F59E0B',
|
||||
color: '#f59e0b',
|
||||
trend: vipStats.newVipOrders > 0 ? `+${vipStats.newVipOrders} 订单` : undefined,
|
||||
},
|
||||
{
|
||||
@@ -252,7 +253,7 @@ export default function Dashboard() {
|
||||
value: `¥${(vipStats.vipRevenue / 100).toFixed(2)}`,
|
||||
icon: Coins,
|
||||
subLabel: '区间内会员特权变现',
|
||||
color: '#D28F4F',
|
||||
color: '#0f172a',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -265,8 +266,7 @@ export default function Dashboard() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8 pb-20 overflow-x-hidden bg-background min-h-screen text-foreground p-6 md:p-10 font-sans relative">
|
||||
<div className="absolute inset-0 warm-noise" />
|
||||
<div className="space-y-8 pb-20 overflow-x-hidden min-h-screen text-gray-900 font-sans relative">
|
||||
|
||||
{/* ═══ Header ═══ */}
|
||||
<header className="relative z-10 flex flex-col xl:flex-row xl:items-end justify-between gap-6 pb-6 border-b border-border">
|
||||
@@ -340,7 +340,7 @@ export default function Dashboard() {
|
||||
initial={{ opacity: 0, y: 16 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: idx * 0.08 }}
|
||||
className="bg-card rounded-2xl p-5 border border-border shadow-sm hover:shadow-md transition-all duration-300 flex flex-col gap-3 group"
|
||||
className="bg-white rounded-[2rem] p-6 border border-gray-100/50 shadow-soft shadow-hover-spring flex flex-col gap-3 group relative overflow-hidden"
|
||||
>
|
||||
<div className="flex justify-between items-start">
|
||||
<p className="text-xs font-semibold text-muted-foreground">{stat.name}</p>
|
||||
@@ -348,8 +348,13 @@ export default function Dashboard() {
|
||||
<stat.icon className="w-4 h-4" style={{ color: stat.color }} />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-2xl md:text-3xl font-bold text-foreground leading-none">
|
||||
{loading ? '—' : stat.value.toLocaleString()}
|
||||
<p className="text-2xl md:text-3xl font-bold text-gray-900 leading-none">
|
||||
{loading ? '—' : (
|
||||
<AnimatedCounter
|
||||
value={stat.value}
|
||||
format={typeof stat.value === 'string' && stat.value.startsWith('¥') ? 'currency' : 'number'}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-[11px] text-muted-foreground">{stat.subLabel}</span>
|
||||
@@ -372,7 +377,7 @@ export default function Dashboard() {
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="bg-card rounded-2xl p-6 border border-border shadow-sm flex flex-col gap-4">
|
||||
<div className="bg-white rounded-[2rem] p-8 border-glass shadow-soft flex flex-col gap-4">
|
||||
{/* Chart header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
@@ -449,7 +454,7 @@ export default function Dashboard() {
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.15 }}
|
||||
>
|
||||
<div className="bg-card rounded-2xl p-6 border border-border shadow-sm h-full flex flex-col">
|
||||
<div className="bg-white rounded-[2rem] p-8 border-glass shadow-soft h-full flex flex-col">
|
||||
<div className="flex items-center justify-between mb-6 pb-4 border-b border-border">
|
||||
<h3 className="text-sm font-bold text-foreground">商业转化漏斗</h3>
|
||||
<TrendingUp className="w-4 h-4 text-muted-foreground" />
|
||||
@@ -457,10 +462,10 @@ export default function Dashboard() {
|
||||
|
||||
{/* LTV highlight */}
|
||||
{funnelData && (
|
||||
<div className="mb-6 rounded-xl bg-primary/10 border border-primary/20 p-4 text-center">
|
||||
<p className="text-xs text-muted-foreground mb-1">人均生命周期价值 (LTV)</p>
|
||||
<p className="text-2xl font-bold" style={{ color: COLORS.listen }}>
|
||||
¥{(funnelData.ltv / 100).toFixed(2)}
|
||||
<div className="mb-6 rounded-2xl bg-gray-50/50 border border-gray-100 p-5 text-center shadow-inner group transition-all duration-300 hover:bg-white hover:shadow-soft">
|
||||
<p className="text-xs text-gray-500 mb-2 transition-colors group-hover:text-gray-900">人均生命周期价值 (LTV)</p>
|
||||
<p className="text-3xl font-black" style={{ color: COLORS.listen }}>
|
||||
<AnimatedCounter value={funnelData.ltv / 100} format="currency" />
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -481,10 +486,10 @@ export default function Dashboard() {
|
||||
<span className="text-xs font-bold text-foreground">{step.label}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-bold text-foreground">{step.value.toLocaleString()}</span>
|
||||
<span className="text-sm font-bold text-gray-900"><AnimatedCounter value={step.value}/></span>
|
||||
{convRate && (
|
||||
<span className="text-[10px] font-semibold text-muted-foreground bg-muted px-1.5 py-0.5 rounded">
|
||||
{convRate}%
|
||||
<span className="text-[10px] font-semibold text-gray-500 bg-gray-50 px-2 py-0.5 rounded-full border border-gray-100">
|
||||
<AnimatedCounter value={convRate} format="percent" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user