重构数据统计页面,添加背景装饰元素,优化布局和样式,提升视觉效果和用户体验。更新统计组件以使用更灵活的卡片样式,增强信息展示的清晰度和可读性。调整归档和分类统计组件的样式,确保一致性和美观性。

This commit is contained in:
宇阳
2026-01-31 18:44:22 +08:00
parent 9b49dcd99b
commit 3b0839a272
7 changed files with 258 additions and 194 deletions

View File

@@ -27,16 +27,14 @@ interface YearData {
const Title = ({ data }: { data: YearData }) => {
return (
<div>
<div className="text-xl font-sans inline-block text_markSty">
{data.year} {data.wordCount / 1000 > 50 && '🔥'}
</div>
<div className="dark:text-[#86909c]">
<span className="text-primary">{data.total}</span>
</div>
<div className="dark:text-[#86909c]">
<span className="text-primary">{(data.wordCount / 1000).toFixed(2)}</span> K
</div>
<div className="flex flex-wrap items-baseline gap-x-4 gap-y-1">
<span className="text-xl font-bold text-slate-800 dark:text-slate-100">
{data.year} {data.wordCount / 1000 > 50}
</span>
<span className="text-sm text-slate-500 dark:text-slate-400">
<span className="font-semibold text-primary">{data.total}</span> · {' '}
<span className="font-semibold text-primary">{(data.wordCount / 1000).toFixed(2)}K</span>
</span>
</div>
);
};
@@ -83,94 +81,113 @@ export default ({ list }: { list: Article[] }) => {
}, [list]);
return (
<>
{/* <div className="w-3/6 mx-auto"> */}
<div className="">
<h3 className="flex justify-center items-center text-2xl mb-3">
<Image src={archiving.src} alt="归档" width={36} height={36} className="mr-3" />
</h3>
<section className="space-y-6">
<div className="flex items-center gap-3">
<div className="flex items-center justify-center w-12 h-12 rounded-xl bg-yellow-500/10 dark:bg-yellow-500/20">
<Image src={archiving.src} alt="归档" width={28} height={28} className="opacity-90" />
</div>
<div>
<h2 className="text-xl font-bold text-slate-800 dark:text-slate-100 tracking-tight"></h2>
<p className="text-sm text-slate-500 dark:text-slate-400 mt-0.5">线</p>
</div>
</div>
{result.length ? (
<Accordion
className="[&>hr]:bg-[#eee] !px-0 [&>hr]:dark:bg-[#4e5969] [&>hr]: "
motionProps={{
variants: {
enter: {
y: 0,
opacity: 1,
height: 'auto',
transition: {
height: {
type: 'spring',
stiffness: 500,
damping: 30,
duration: 1,
},
opacity: {
ease: 'easeInOut',
duration: 1,
},
{result.length ? (
<Accordion
className="[&>hr]:!bg-slate-200 dark:[&>hr]:!bg-slate-600/50 !px-0 [&_[data-hover=true]]:!bg-transparent"
itemClasses={{
base: 'py-4',
title: 'text-base font-medium',
trigger: 'px-4 py-3 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800/50',
content: 'px-4 pb-4',
}}
motionProps={{
variants: {
enter: {
y: 0,
opacity: 1,
height: 'auto',
transition: {
height: {
type: 'spring',
stiffness: 500,
damping: 30,
duration: 1,
},
},
exit: {
y: -10,
opacity: 0,
height: 0,
transition: {
height: {
ease: 'easeInOut',
duration: 0.25,
},
opacity: {
ease: 'easeInOut',
duration: 0.3,
},
opacity: {
ease: 'easeInOut',
duration: 1,
},
},
},
}}
>
{result.map((item, index) => (
<AccordionItem key={index} aria-label={item.year + '年'} title={<Title data={item} />}>
{Object.keys(item.month).map((month, index) => (
<div key={index} className="ml-3">
<div className="relative border-l border-gray-300 dark:border-[#4e5969] ">
<div className="mb-8 ml-4">
<div className="absolute w-3 h-3 bg-blue-500 rounded-full -left-1.5 border border-white"></div>
<div className="ml-2 sm:ml-6">
<div className="flex items-center space-x-4">
<div className="text-2xl text-gray-600 dark:text-primary">
{month} {item.month[+month].wordCount / 1000 > 10 && '🔥'}
</div>
<div>{item.month[+month].total} </div>
<div>{(item.month[+month].wordCount / 1000).toFixed(2)} K字</div>
</div>
{item.month[+month].list.map((article: Article, index) => (
<div key={index} className="group flex justify-between py-2">
<Link href={`/article/${article.id}`} target="_blank" className="dark:text-[#bfbfbf] group-hover:text-primary ">
{dayjs(+article.createTime!).format('MM-DD')} {article.title}
</Link>
<span className="hidden sm:flex items-center min-w-24 text-sm text-white group-hover:text-gray-400 ">
<AiOutlineEye className="mr-1" />
exit: {
y: -10,
opacity: 0,
height: 0,
transition: {
height: {
ease: 'easeInOut',
duration: 0.25,
},
opacity: {
ease: 'easeInOut',
duration: 0.3,
},
},
},
},
}}
>
{result.map((item, index) => (
<AccordionItem key={index} aria-label={item.year + '年'} title={<Title data={item} />}>
<div className="space-y-6 pl-2">
{Object.keys(item.month).map((month, monthIdx) => (
<div key={monthIdx} className="relative pl-6">
<div className="absolute left-0 top-1.5 w-px h-[calc(100%-0.5rem)] bg-gradient-to-b from-primary/60 to-slate-200 dark:to-slate-600" />
<div className="absolute left-0 top-1.5 w-2.5 h-2.5 rounded-full bg-primary shadow-sm ring-2 ring-white dark:ring-black-b" />
<div className="rounded-xl bg-slate-50/80 dark:bg-slate-800/30 p-4 border border-slate-100 dark:border-slate-700/50">
<div className="flex flex-wrap items-center gap-2 mb-3">
<span className="text-lg font-semibold text-slate-700 dark:text-slate-200">
{month} {item.month[+month].wordCount / 1000 > 10}
</span>
<span className="text-sm text-slate-500 dark:text-slate-400">
{item.month[+month].total} · {(item.month[+month].wordCount / 1000).toFixed(2)}K
</span>
</div>
<ul className="space-y-1">
{item.month[+month].list.map((article: Article, artIdx) => (
<li key={artIdx}>
<Link
href={`/article/${article.id}`}
target="_blank"
className="group flex items-center justify-between gap-4 py-2 px-3 mx-3 rounded-lg text-slate-600 dark:text-slate-300 hover:bg-gray-100/80 dark:hover:bg-slate-700/50 hover:text-primary"
>
<span className="flex-1 truncate">
<span className="text-slate-400 dark:text-slate-500 text-sm tabular-nums mr-2">
{dayjs(+article.createTime!).format('MM-DD')}
</span>
{article.title}
</span>
<span className="hidden sm:flex items-center gap-1 text-xs text-slate-400 group-hover:text-slate-500 shrink-0">
<AiOutlineEye className="text-sm" />
{article.view}
</span>
</div>
))}
</div>
</div>
</Link>
</li>
))}
</ul>
</div>
</div>
))}
</AccordionItem>
))}
</Accordion>
) : (
<div className="flex justify-center w-full my-10">
<Spinner />
</div>
)}
</div>
</>
</div>
</AccordionItem>
))}
</Accordion>
) : (
<div className="flex justify-center w-full py-16">
<Spinner />
</div>
)}
</section>
);
};

View File

@@ -32,31 +32,45 @@ export default () => {
const option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(255,255,255,0.95)',
borderColor: 'rgba(0,0,0,0.08)',
borderWidth: 1,
textStyle: { color: '#1e293b' },
},
legend: {
show: false,
},
color: ['#0ea5e9', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444', '#6366f1', '#ec4899'],
series: [
{
name: '数量统计',
type: 'pie',
radius: ['5%', '70%'],
radius: ['40%', '75%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
padAngle: 5,
padAngle: 3,
itemStyle: {
borderRadius: 10,
borderRadius: 8,
borderColor: 'transparent',
borderWidth: 0,
},
label: {
show: false,
position: 'center',
show: true,
formatter: '{b}\n{d}%',
fontSize: 12,
},
emphasis: {
label: {
show: false,
itemStyle: {
shadowBlur: 12,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.2)',
},
label: { show: true },
},
labelLine: {
show: false,
show: true,
length: 8,
length2: 6,
},
data: list,
},
@@ -79,12 +93,17 @@ export default () => {
}, [list]);
return (
<div className="flex flex-col items-center mb-5 md:mb-0">
<h3 className="flex items-center text-xl mb-5">
<Image src={cate.src} alt="分类一览" width={25} height={25} className="mr-3" />
</h3>
<div ref={chartRef} className="min-w-[300px] h-[300px]"></div>
<div className="rounded-2xl border border-slate-200 dark:border-slate-700/50 bg-slate-50/50 dark:bg-slate-800/30 p-6 overflow-hidden">
<div className="flex items-center gap-3 mb-6">
<div className="flex items-center justify-center w-10 h-10 rounded-lg bg-emerald-500/10 dark:bg-emerald-500/20">
<Image src={cate.src} alt="分类一览" width={22} height={22} className="opacity-90" />
</div>
<div>
<h3 className="text-lg font-semibold text-slate-800 dark:text-slate-100"></h3>
<p className="text-xs text-slate-500 dark:text-slate-400"></p>
</div>
</div>
<div ref={chartRef} className="min-w-[260px] h-[280px] w-full" />
</div>
);
};

View File

@@ -16,47 +16,36 @@ export default () => {
getTagData();
}, []);
const colors = [
{
color: '#0d6efd',
backgroundColor: 'rgba(13, 110, 253, .2)',
},
{
color: '#6610f2',
backgroundColor: 'rgba(102, 16, 242, .2)',
},
{
color: '#20c997',
backgroundColor: 'rgba(32, 201, 151, .2)',
},
{
color: '#dc3545',
backgroundColor: 'rgba(220, 53, 69, .2)',
},
{
color: '#fd7e14',
backgroundColor: 'rgba(253, 126, 20, .2)',
},
const tagStyles = [
'bg-sky-100 text-sky-700 dark:bg-sky-500/20 dark:text-sky-300',
'bg-violet-100 text-violet-700 dark:bg-violet-500/20 dark:text-violet-300',
'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300',
'bg-rose-100 text-rose-700 dark:bg-rose-500/20 dark:text-rose-300',
'bg-amber-100 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300',
'bg-indigo-100 text-indigo-700 dark:bg-indigo-500/20 dark:text-indigo-300',
];
return (
<>
<div className="flex flex-col items-center">
<h3 className="flex items-center text-xl mb-5">
<Image src={tag.src} alt="标签墙" width={25} height={25} className="mr-3" />
</h3>
<div className="overflow-auto h-[270px] pr-1 grid grid-cols-6 gap-2 hide_sliding">
{list.map((item, index) => {
const { color, backgroundColor } = colors[getRandom(0, colors.length - 1)];
return (
<span key={index} className="flex justify-center items-center px-4 h-8 text-xs rounded-md whitespace-nowrap line-clamp-1" style={{ color, backgroundColor }}>
{item.name}
</span>
);
})}
<div className="rounded-2xl border border-slate-200 dark:border-slate-700/50 bg-slate-50/50 dark:bg-slate-800/30 p-6 overflow-hidden">
<div className="flex items-center gap-3 mb-6">
<div className="flex items-center justify-center w-10 h-10 rounded-lg bg-violet-500/10 dark:bg-violet-500/20">
<Image src={tag.src} alt="标签墙" width={22} height={22} className="opacity-90" />
</div>
<div>
<h3 className="text-lg font-semibold text-slate-800 dark:text-slate-100"></h3>
<p className="text-xs text-slate-500 dark:text-slate-400"></p>
</div>
</div>
</>
<div className="overflow-auto max-h-[280px] pr-2 grid grid-cols-2 xs:grid-cols-3 sm:grid-cols-4 gap-2 hide_sliding">
{list.map((item, index) => (
<span
key={index}
className={`inline-flex justify-center items-center px-3 py-2 text-sm font-medium rounded-xl whitespace-nowrap transition-[transform,box-shadow] hover:scale-105 hover:shadow-md ${tagStyles[getRandom(0, tagStyles.length - 1)]}`}
>
{item.name}
</span>
))}
</div>
</div>
);
};

View File

@@ -41,56 +41,80 @@ export default ({ aTotal }: Props) => {
getData();
}, []);
const statCards = [
{
title: '文章总计',
value: aTotal,
icon: article,
gradient: 'from-sky-500 to-blue-600',
bgLight: 'bg-sky-50 dark:bg-sky-500/10',
borderColor: 'border-sky-200 dark:border-sky-500/30',
textColor: 'text-sky-600 dark:text-sky-400',
},
{
title: '评论总计',
value: commentList.length,
icon: comment,
gradient: 'from-amber-500 to-orange-500',
bgLight: 'bg-amber-50 dark:bg-amber-500/10',
borderColor: 'border-amber-200 dark:border-amber-500/30',
textColor: 'text-amber-600 dark:text-amber-400',
},
{
title: '分类总计',
value: cateList.length,
icon: cate,
gradient: 'from-emerald-500 to-teal-500',
bgLight: 'bg-emerald-50 dark:bg-emerald-500/10',
borderColor: 'border-emerald-200 dark:border-emerald-500/30',
textColor: 'text-emerald-600 dark:text-emerald-400',
},
{
title: '友联总计',
value: linkList.length,
icon: friend,
gradient: 'from-rose-500 to-red-500',
bgLight: 'bg-rose-50 dark:bg-rose-500/10',
borderColor: 'border-rose-200 dark:border-rose-500/30',
textColor: 'text-rose-600 dark:text-rose-400',
},
];
return (
<>
<h3 className="flex items-center text-2xl mb-3">
<Image src={statis.src} alt="统计" width={36} height={36} className="mr-3" />
</h3>
<div className="mb-10 mt-5">
<div className="grid grid-cols-1 gap-2 xs:grid-cols-2 md:grid-cols-4 md:gap-4">
<div className="flex justify-between items-center px-4 sm:px-5 h-20 sm:h-24 border-2 border-[#0EA5E9] rounded-lg bg-[#F0F9FF]">
<Image src={article} alt="文章" />
<div className="flex flex-col">
<h3 className="text-2xl sm:text-3xl font-sans text-[#0EA5E9] text-end">{aTotal}</h3>
<p className="text-[#0EA5E9]"></p>
</div>
</div>
<div className="flex justify-between items-center px-4 sm:px-5 h-20 sm:h-24 border-2 border-[#F59E0B] rounded-lg bg-[#FFFBEB]">
<Image src={comment} alt="" />
<div className="flex flex-col">
<h3 className="text-2xl sm:text-3xl font-sans text-[#F59E0B] text-end">{commentList.length}</h3>
<p className="text-[#F59E0B]"></p>
</div>
</div>
<div className="flex justify-between items-center px-4 sm:px-5 h-20 sm:h-24 border-2 border-[#0E9F6E] rounded-lg bg-[#F3FAF7]">
<Image src={cate} alt="分类" />
<div className="flex flex-col">
<h3 className="text-2xl sm:text-3xl font-sans text-[#0E9F6E] text-end">{cateList.length}</h3>
<p className="text-[#0E9F6E]"></p>
</div>
</div>
<div className="flex justify-between items-center px-4 sm:px-5 h-20 sm:h-24 border-2 border-[#EC160F] rounded-lg bg-[#FFF0F0]">
<Image src={friend} alt="友联" />
<div className="flex flex-col">
<h3 className="text-2xl sm:text-3xl font-sans text-[#EC160F] text-end">{linkList.length}</h3>
<p className="text-[#EC160F]"></p>
</div>
</div>
<section className="space-y-10">
<div className="flex items-center gap-3">
<div className="flex items-center justify-center w-12 h-12 rounded-xl bg-primary/10 dark:bg-primary/20">
<Image src={statis.src} alt="统计" width={28} height={28} className="opacity-90" />
</div>
<div className="flex flex-col md:flex-row justify-between my-14">
<CateStatis />
<TagStatis />
<div>
<h2 className="text-xl font-bold text-slate-800 dark:text-slate-100 tracking-tight"></h2>
<p className="text-sm text-slate-500 dark:text-slate-400 mt-0.5"></p>
</div>
</div>
</>
<div className="grid grid-cols-1 xs:grid-cols-2 lg:grid-cols-4 gap-4">
{statCards.map((card, index) => (
<div
key={index}
className={`group relative overflow-hidden rounded-2xl border ${card.borderColor} ${card.bgLight} p-5 transition-[transform,box-shadow] duration-300 hover:shadow-lg hover:-translate-y-0.5 hover:border-opacity-80`}
>
<div className="relative flex items-center justify-between">
<div className="flex items-center justify-center rounded-xl dark:bg-black/20">
<Image src={card.icon} alt={card.title} width={48} height={48} />
</div>
<div className="text-right">
<p className={`text-3xl font-bold tabular-nums ${card.textColor}`}>{card.value}</p>
<p className={`text-sm font-medium mt-0.5 ${card.textColor} opacity-90`}>{card.title}</p>
</div>
</div>
</div>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 pt-4">
<CateStatis />
<TagStatis />
</div>
</section>
);
};

View File

@@ -13,18 +13,33 @@ export default async () => {
<title>📊 </title>
<meta name="description" content="📊 数据统计" />
{/* 背景装饰 */}
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute inset-0 bg-[linear-gradient(rgba(148,163,184,0.06)_1px,transparent_1px),linear-gradient(90deg,rgba(148,163,184,0.06)_1px,transparent_1px)] bg-[size:64px_64px]" />
<div className="absolute -top-1/2 left-1/2 -translate-x-1/2 w-[800px] h-[800px] rounded-full bg-primary/6 blur-[120px]" />
<div className="absolute top-1/4 right-0 w-96 h-96 rounded-full bg-violet-400/8 blur-[80px]" />
<div className="absolute bottom-1/4 left-0 w-80 h-80 rounded-full bg-cyan-400/8 blur-[80px]" />
</div>
<Slide isRipple={false} src="https://bu.dusays.com/2025/12/04/6930fd6cda541.jpg">
{/* 星空背景组件 */}
<Starry />
<div className="absolute top-[45%] left-[50%] transform -translate-x-1/2 flex flex-col items-center">
<div className="text-white text-[20px] xs:text-[25px] sm:text-[30px] whitespace-nowrap custom_text_shadow"></div>
<div className="absolute top-[45%] left-1/2 -translate-x-1/2 flex flex-col items-center">
<h1 className="text-white text-2xl xs:text-3xl sm:text-4xl font-bold tracking-wide whitespace-nowrap custom_text_shadow drop-shadow-lg">
</h1>
<p className="mt-2 text-white/90 text-sm sm:text-base custom_text_shadow"></p>
</div>
</Slide>
<div className="w-[90%] xl:w-[1200px] my-10 mx-auto bg-white dark:bg-black-b p-6 sm:p-10 rounded-xl border dark:border-black-b ">
<Statis aTotal={data?.length} />
<Archiving list={data} />
<div className="w-[92%] max-w-6xl mx-auto -mt-8 sm:-mt-12 relative z-10 mb-16">
<div className="rounded-2xl shadow-xl overflow-hidden bg-white dark:bg-black-b/95 backdrop-blur-sm border border-slate-200/80 dark:border-slate-700/50">
<div className="p-6 sm:p-10 lg:p-12 space-y-12">
<Statis aTotal={data?.length} />
<Archiving list={data} />
</div>
</div>
</div>
</>
);

View File

@@ -121,7 +121,7 @@ export default () => {
<div className="absolute bottom-1/4 left-0 w-80 h-80 rounded-full bg-cyan-400/8 blur-[80px]" />
</div>
<div className="py-16 border-b dark:border-[#4e5969] bg-[linear-gradient(to_right,#fff1eb_0%,#d0edfb_100%)] dark:bg-[linear-gradient(to_right,#232931_0%,#232931_100%)] ">
<div className="py-16 border-b dark:border-[#4e5969]">
<div className="relative z-10">
{/* 头部区域 */}
<div className="flex flex-col items-center px-4 pt-12 md:pt-16 pb-8">

View File

@@ -14,6 +14,6 @@
@apply border rounded-md box-border dark:border-black-b dark:bg-black-b hover:!border-primary outline-none transition-shadow ease-in-out;
&:focus {
@apply border-2 border-primary shadow-[0_10px_20px_1px_rgb(83,157,253,.1)]
@apply border-2 border-primary shadow-[0_10px_20px_1px_rgb(83,157,253,.1)];
}
}