分类统计

This commit is contained in:
2026-06-19 16:17:10 +08:00 Unverified
parent 43fb1c65dd
commit b8e1e0e13b
3 changed files with 35 additions and 4 deletions
+5 -3
View File
@@ -10,9 +10,11 @@ interface Props {
subtitle?: string; subtitle?: string;
type: 'pie' | 'line'; type: 'pie' | 'line';
items: ChartItem[]; items: ChartItem[];
totalLabel?: string;
valueLabel?: string;
} }
const { title, subtitle, type, items } = Astro.props; const { title, subtitle, type, items, totalLabel = '篇文章', valueLabel = '篇' } = Astro.props;
const colors = ['#3eb8be', '#f2a65a', '#7f9cf5', '#68b684', '#f07f7f', '#b18ce8', '#e0b34f', '#5ca4d3']; const colors = ['#3eb8be', '#f2a65a', '#7f9cf5', '#68b684', '#f07f7f', '#b18ce8', '#e0b34f', '#5ca4d3'];
const safeItems = items.filter((item) => item.value > 0); const safeItems = items.filter((item) => item.value > 0);
const total = safeItems.reduce((sum, item) => sum + item.value, 0); const total = safeItems.reduce((sum, item) => sum + item.value, 0);
@@ -74,7 +76,7 @@ const labelStep = Math.max(1, Math.ceil(linePoints.length / 6));
></circle> ></circle>
))} ))}
<text x="60" y="56" text-anchor="middle" class="pie-chart-total">{total}</text> <text x="60" y="56" text-anchor="middle" class="pie-chart-total">{total}</text>
<text x="60" y="72" text-anchor="middle" class="pie-chart-label">篇文章</text> <text x="60" y="72" text-anchor="middle" class="pie-chart-label">{totalLabel}</text>
</svg> </svg>
<div class="chart-legend"> <div class="chart-legend">
@@ -124,7 +126,7 @@ const labelStep = Math.max(1, Math.ceil(linePoints.length / 6));
{linePoints.map((point, index) => ( {linePoints.map((point, index) => (
<> <>
<circle class="line-chart-dot" cx={point.x} cy={point.y} r="4"></circle> <circle class="line-chart-dot" cx={point.x} cy={point.y} r="4"></circle>
<title>{`${point.label}: ${point.value} `}</title> <title>{`${point.label}: ${point.value} ${valueLabel}`}</title>
{index % labelStep === 0 && ( {index % labelStep === 0 && (
<text class="line-chart-x-label" x={point.x} y={lineHeight - 12} text-anchor="middle"> <text class="line-chart-x-label" x={point.x} y={lineHeight - 12} text-anchor="middle">
{point.label} {point.label}
+19 -1
View File
@@ -11,12 +11,30 @@ const categoryStats = categories.map((category) => ({
value: posts.filter((post) => post.categories.includes(category)).length, value: posts.filter((post) => post.categories.includes(category)).length,
href: getCategoryHref(category), href: getCategoryHref(category),
})); }));
const uncategorizedCount = posts.filter((post) => post.categories.length === 0).length;
const multiCategoryCount = posts.filter((post) => post.categories.length > 1).length;
const categoryArchiveCount = categoryStats.reduce((sum, category) => sum + category.value, 0) + uncategorizedCount;
const categoryChartStats = [
...categoryStats,
{
label: '未分类',
value: uncategorizedCount,
},
];
const categoryChartSubtitle = [
`${posts.length} 篇文章`,
`${categoryArchiveCount} 次归档`,
multiCategoryCount ? `${multiCategoryCount} 篇多分类` : null,
uncategorizedCount ? `${uncategorizedCount} 篇未分类` : null,
]
.filter(Boolean)
.join(' · ');
--- ---
<BaseLayout title="分类"> <BaseLayout title="分类">
<section class="content-wrap narrow"> <section class="content-wrap narrow">
<PageHeaderCard title="分类" subtitle={`共 ${categories.length} 个分类`} /> <PageHeaderCard title="分类" subtitle={`共 ${categories.length} 个分类`} />
<StatsChart title="分类文章统计" subtitle={`共 ${posts.length} 篇文章`} type="pie" items={categoryStats} /> <StatsChart title="分类归档统计" subtitle={categoryChartSubtitle} type="pie" items={categoryChartStats} totalLabel="次归档" />
<div class="term-grid"> <div class="term-grid">
{categoryStats.map((category) => ( {categoryStats.map((category) => (
<a href={category.href}> <a href={category.href}>
+11
View File
@@ -646,6 +646,7 @@ a {
.post-layout .article { .post-layout .article {
width: 100%; width: 100%;
min-width: 0;
margin: 0; margin: 0;
} }
@@ -1314,6 +1315,7 @@ time {
.article { .article {
--article-padding: clamp(24px, 5vw, 48px); --article-padding: clamp(24px, 5vw, 48px);
width: min(var(--article-width), calc(100% - (var(--page-gutter) * 2))); width: min(var(--article-width), calc(100% - (var(--page-gutter) * 2)));
min-width: 0;
margin: 64px auto 84px; margin: 64px auto 84px;
border: 1px solid rgba(255, 255, 255, 0.58); border: 1px solid rgba(255, 255, 255, 0.58);
border-radius: 8px; border-radius: 8px;
@@ -1329,6 +1331,10 @@ time {
-webkit-backdrop-filter: blur(18px) saturate(165%); -webkit-backdrop-filter: blur(18px) saturate(165%);
} }
.prose {
min-width: 0;
}
.article-header { .article-header {
position: relative; position: relative;
margin-bottom: 28px; margin-bottom: 28px;
@@ -1665,6 +1671,7 @@ time {
} }
.prose pre { .prose pre {
max-width: 100%;
margin: 0; margin: 0;
overflow-x: auto; overflow-x: auto;
padding: 18px; padding: 18px;
@@ -1931,6 +1938,9 @@ time {
.prose .code-block { .prose .code-block {
position: relative; position: relative;
width: 100%;
max-width: 100%;
min-width: 0;
margin: 1.25em 0; margin: 1.25em 0;
overflow: hidden; overflow: hidden;
border: 1px solid rgba(15, 118, 110, 0.16); border: 1px solid rgba(15, 118, 110, 0.16);
@@ -1967,6 +1977,7 @@ time {
} }
.prose .code-block pre { .prose .code-block pre {
max-width: 100%;
max-height: none; max-height: none;
padding: 44px 18px 18px; padding: 44px 18px 18px;
background: transparent; background: transparent;