包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、 antdesign(管理后台)、landingpage(营销落地页)、 数据库 SQL 和配置文件。
74 lines
2.0 KiB
TypeScript
74 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
interface StatData {
|
|
label: string;
|
|
value: string;
|
|
change: string;
|
|
isPositive: boolean;
|
|
}
|
|
|
|
interface StatsCardsProps {
|
|
stats?: StatData[];
|
|
cols?: 4 | 5;
|
|
}
|
|
|
|
const COLS_CLASS: Record<number, string> = {
|
|
4: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
5: "grid grid-cols-2 md:grid-cols-5 gap-4",
|
|
};
|
|
|
|
export default function StatsCards({ stats = [], cols }: StatsCardsProps) {
|
|
if (!stats || stats.length === 0) return null;
|
|
|
|
const gridClass = COLS_CLASS[cols ?? stats.length] ?? COLS_CLASS[4];
|
|
|
|
return (
|
|
<div className={gridClass}>
|
|
{stats.map((stat, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex flex-col gap-2 items-start justify-start
|
|
bg-[#f9fafb] dark:bg-gray-800
|
|
border border-[#f3f4f6] dark:border-gray-700
|
|
rounded-2xl p-4"
|
|
>
|
|
{/* Label */}
|
|
<div
|
|
className="text-[#9ca1af] dark:text-gray-400"
|
|
style={{ fontFamily: 'Inter, sans-serif', fontSize: 12, fontWeight: 700, lineHeight: '150%', letterSpacing: '0.01em' }}
|
|
>
|
|
{stat.label}
|
|
</div>
|
|
|
|
{/* Value */}
|
|
<div
|
|
className="text-[#111827] dark:text-white truncate w-full"
|
|
style={{ fontFamily: 'Inter, sans-serif', fontSize: 24, fontWeight: 700, lineHeight: '130%', letterSpacing: '-0.005em' }}
|
|
title={stat.value}
|
|
>
|
|
{stat.value}
|
|
</div>
|
|
|
|
{/* Change indicator */}
|
|
{stat.change ? (
|
|
<div
|
|
style={{
|
|
fontFamily: 'Inter, sans-serif',
|
|
fontSize: 12,
|
|
fontWeight: 500,
|
|
lineHeight: '150%',
|
|
letterSpacing: '0.01em',
|
|
color: stat.isPositive ? '#10b981' : '#ef4444',
|
|
}}
|
|
>
|
|
{stat.change}
|
|
</div>
|
|
) : (
|
|
<div style={{ height: 18 }} />
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|