前端样式修改

main
kangwenjing 2026-04-15 18:15:06 +08:00
parent 14087b3a5a
commit 7b1c424ba2
1 changed files with 20 additions and 17 deletions

View File

@ -10,10 +10,10 @@ const DASHBOARD_PREVIEW_COUNT = 5;
const DASHBOARD_HISTORY_PREVIEW_COUNT = 3; const DASHBOARD_HISTORY_PREVIEW_COUNT = 3;
const baseStats = [ const baseStats = [
{ name: "本月新增商机", metricKey: "monthlyOpportunities", icon: TrendingUp, color: "text-emerald-600 dark:text-emerald-400", bg: "bg-emerald-100 dark:bg-emerald-500/20", mobileVariant: "hero" }, { name: "本月新增商机", metricKey: "monthlyOpportunities", icon: TrendingUp, color: "text-emerald-600 dark:text-emerald-400", bg: "bg-emerald-100 dark:bg-emerald-500/20" },
{ name: "本月已签单商机金额", metricKey: "monthlyWonOpportunities", icon: BarChart3, color: "text-amber-600 dark:text-amber-400", bg: "bg-amber-100 dark:bg-amber-500/20", mobileVariant: "hero" }, { name: "本月已签单商机金额", metricKey: "monthlyWonOpportunities", icon: BarChart3, color: "text-amber-600 dark:text-amber-400", bg: "bg-amber-100 dark:bg-amber-500/20" },
{ name: "已推送OMS项目", metricKey: "pushedOmsProjects", icon: Users, color: "text-blue-600 dark:text-blue-400", bg: "bg-blue-100 dark:bg-blue-500/20", mobileVariant: "compact" }, { name: "已推送OMS项目", metricKey: "pushedOmsProjects", icon: Users, color: "text-blue-600 dark:text-blue-400", bg: "bg-blue-100 dark:bg-blue-500/20" },
{ name: "本月新增渠道", metricKey: "monthlyChannels", icon: Building2, color: "text-violet-600 dark:text-violet-400", bg: "bg-violet-100 dark:bg-violet-500/20", mobileVariant: "compact" }, { name: "本月新增渠道", metricKey: "monthlyChannels", icon: Building2, color: "text-violet-600 dark:text-violet-400", bg: "bg-violet-100 dark:bg-violet-500/20" },
] as const; ] as const;
const statRoutes: Record<(typeof baseStats)[number]["metricKey"], { pathname: string; state?: { tab: "sales" | "channel" } }> = { const statRoutes: Record<(typeof baseStats)[number]["metricKey"], { pathname: string; state?: { tab: "sales" | "channel" } }> = {
@ -44,11 +44,15 @@ function formatStatDisplay(metricKey: (typeof baseStats)[number]["metricKey"], v
}; };
} }
function getMobileStatCardClass(mobileVariant: (typeof baseStats)[number]["mobileVariant"]) { function getStatValueClass(valueText: string) {
if (mobileVariant === "hero") { const length = valueText.length;
return "col-span-2 min-h-[116px]"; if (length >= 8) {
return "text-[17px] min-[380px]:text-[18px] min-[430px]:text-[20px] min-[520px]:text-[28px]";
} }
return "col-span-1 min-h-[104px]"; if (length >= 7) {
return "text-[18px] min-[380px]:text-[20px] min-[430px]:text-[22px] min-[520px]:text-[30px]";
}
return "text-[20px] min-[380px]:text-[22px] min-[430px]:text-[24px] min-[520px]:text-[34px]";
} }
export default function Dashboard() { export default function Dashboard() {
@ -177,8 +181,7 @@ export default function Dashboard() {
<div className="grid grid-cols-2 gap-x-3 gap-y-4 min-[520px]:grid-cols-2 sm:gap-4 xl:grid-cols-4"> <div className="grid grid-cols-2 gap-x-3 gap-y-4 min-[520px]:grid-cols-2 sm:gap-4 xl:grid-cols-4">
{stats.map((stat, i) => { {stats.map((stat, i) => {
const display = formatStatDisplay(stat.metricKey, stat.value); const display = formatStatDisplay(stat.metricKey, stat.value);
const isHeroCard = isMobileViewport && stat.mobileVariant === "hero"; const valueClassName = getStatValueClass(display.value);
const mobileClass = isMobileViewport ? getMobileStatCardClass(stat.mobileVariant) : "";
return ( return (
<motion.button <motion.button
@ -188,29 +191,29 @@ export default function Dashboard() {
initial={disableMobileMotion ? false : { opacity: 0, y: 20 }} initial={disableMobileMotion ? false : { opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={disableMobileMotion ? { duration: 0 } : { delay: i * 0.1 }} transition={disableMobileMotion ? { duration: 0 } : { delay: i * 0.1 }}
className={`crm-card group overflow-hidden rounded-xl border border-slate-200/80 bg-white/95 p-3 text-left shadow-[0_14px_40px_-28px_rgba(15,23,42,0.35)] transition-all duration-200 hover:-translate-y-0.5 hover:border-violet-200 hover:shadow-[0_18px_44px_-24px_rgba(109,40,217,0.22)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 focus-visible:ring-offset-2 dark:hover:bg-slate-900 min-[520px]:min-h-[128px] sm:rounded-2xl sm:p-5 ${mobileClass} ${!isMobileViewport ? "min-h-[104px]" : ""}`} className="crm-card group min-h-[104px] overflow-hidden rounded-xl border border-slate-200/80 bg-white/95 p-3 text-left shadow-[0_14px_40px_-28px_rgba(15,23,42,0.35)] transition-all duration-200 hover:-translate-y-0.5 hover:border-violet-200 hover:shadow-[0_18px_44px_-24px_rgba(109,40,217,0.22)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 focus-visible:ring-offset-2 dark:hover:bg-slate-900 min-[520px]:min-h-[128px] sm:rounded-2xl sm:p-5"
whileTap={disableMobileMotion ? undefined : { scale: 0.98 }} whileTap={disableMobileMotion ? undefined : { scale: 0.98 }}
aria-label={`查看${stat.name}`} aria-label={`查看${stat.name}`}
> >
<div className="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-transparent via-violet-200/70 to-transparent opacity-0 transition-opacity duration-200 group-hover:opacity-100" /> <div className="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-transparent via-violet-200/70 to-transparent opacity-0 transition-opacity duration-200 group-hover:opacity-100" />
<div className={`flex h-full flex-col justify-between ${isHeroCard ? "gap-4" : "gap-3"} min-[520px]:gap-4`}> <div className="flex h-full flex-col justify-between gap-3 min-[520px]:gap-4">
<div className="flex items-start gap-3 sm:gap-4"> <div className="flex items-start gap-3 sm:gap-4">
<div className={`flex shrink-0 items-center justify-center rounded-xl ${stat.bg} shadow-inner shadow-white/60 ${isHeroCard ? "h-11 w-11" : "h-10 w-10"} min-[520px]:h-12 min-[520px]:w-12`}> <div className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-xl ${stat.bg} shadow-inner shadow-white/60 min-[400px]:h-10 min-[400px]:w-10 min-[520px]:h-12 min-[520px]:w-12`}>
<stat.icon className={`${isHeroCard ? "h-5.5 w-5.5" : "h-5 w-5"} ${stat.color} min-[520px]:h-6 min-[520px]:w-6`} /> <stat.icon className={`h-4.5 w-4.5 ${stat.color} min-[400px]:h-5 min-[400px]:w-5 min-[520px]:h-6 min-[520px]:w-6`} />
</div> </div>
<div className="min-w-0 pt-0.5"> <div className="min-w-0 pt-0.5">
<p className={`overflow-hidden font-semibold leading-5 text-slate-500 dark:text-slate-400 ${isHeroCard ? "text-[13px]" : "text-xs"} min-[520px]:max-h-10 min-[520px]:text-sm`}> <p className="max-h-10 overflow-hidden text-[11px] font-semibold leading-5 text-slate-500 dark:text-slate-400 min-[400px]:text-xs min-[520px]:text-sm">
{stat.name} {stat.name}
</p> </p>
</div> </div>
</div> </div>
<div className="flex items-end justify-between gap-2 min-[520px]:gap-3"> <div className="flex items-end justify-between gap-2 min-[520px]:gap-3">
<div className="min-w-0 flex-1 overflow-hidden"> <div className="min-w-0 flex-1 overflow-hidden">
<p className={`overflow-hidden text-ellipsis whitespace-nowrap font-bold leading-none tracking-[-0.04em] text-slate-900 dark:text-white ${isHeroCard ? "text-[30px]" : "text-[22px]"} min-[400px]:text-[26px] min-[520px]:text-[34px]`}> <p className={`overflow-hidden text-ellipsis whitespace-nowrap font-bold leading-none tracking-[-0.05em] text-slate-900 dark:text-white ${valueClassName}`}>
{display.value} {display.value}
</p> </p>
</div> </div>
<span className="shrink-0 rounded-full bg-slate-100 px-2 py-1 text-[10px] font-medium tracking-wide text-slate-500 dark:bg-slate-800 dark:text-slate-300 min-[520px]:px-2.5 min-[520px]:text-xs"> <span className="shrink-0 rounded-full bg-slate-100 px-1.5 py-1 text-[10px] font-medium tracking-wide text-slate-500 dark:bg-slate-800 dark:text-slate-300 min-[430px]:px-2 min-[520px]:px-2.5 min-[520px]:text-xs">
{display.unit} {display.unit}
</span> </span>
</div> </div>