--- type ChartItem = { label: string; value: number; href?: string; }; interface Props { title: string; subtitle?: string; type: 'pie' | 'line'; items: ChartItem[]; totalLabel?: string; valueLabel?: string; } const { title, subtitle, type, items, totalLabel = '篇文章', valueLabel = '篇' } = Astro.props; const colors = ['#3eb8be', '#f2a65a', '#7f9cf5', '#68b684', '#f07f7f', '#b18ce8', '#e0b34f', '#5ca4d3']; const safeItems = items.filter((item) => item.value > 0); const total = safeItems.reduce((sum, item) => sum + item.value, 0); const circumference = 100; let offset = 0; const pieItems = safeItems.map((item, index) => { const percent = total ? item.value / total : 0; const dash = percent * circumference; const segment = { ...item, color: colors[index % colors.length], dash, offset, percentText: `${Math.round(percent * 100)}%`, }; offset -= dash; return segment; }); const lineWidth = 640; const lineHeight = 240; const padding = { top: 24, right: 24, bottom: 42, left: 40 }; const plotWidth = lineWidth - padding.left - padding.right; const plotHeight = lineHeight - padding.top - padding.bottom; const maxValue = Math.max(1, ...safeItems.map((item) => item.value)); const linePoints = safeItems.map((item, index) => { const x = safeItems.length === 1 ? padding.left + plotWidth / 2 : padding.left + (index / (safeItems.length - 1)) * plotWidth; const y = padding.top + plotHeight - (item.value / maxValue) * plotHeight; return { ...item, x, y }; }); const points = linePoints.map((point) => `${point.x},${point.y}`).join(' '); const areaPoints = linePoints.length ? `${padding.left},${padding.top + plotHeight} ${points} ${padding.left + plotWidth},${padding.top + plotHeight}` : ''; const labelStep = Math.max(1, Math.ceil(linePoints.length / 6)); ---

{title}

{subtitle &&

{subtitle}

}
{type === 'pie' ? (
{pieItems.map((item) => ( ))} {total} {totalLabel}
{pieItems.map((item, index) => { const content = ( <> {item.label} {item.value} {item.percentText} ); return item.href ? ( {content} ) : (
{content}
); })}
) : (
{[0, 0.5, 1].map((ratio) => ( <> {Math.round(maxValue * ratio)} ))} {linePoints.length > 0 && } {linePoints.length > 0 && } {linePoints.map((point, index) => ( <> {`${point.label}: ${point.value} ${valueLabel}`} {index % labelStep === 0 && ( {point.label} )} ))}
)}