feat: 外观支持自定义 CSS/JS,并升级发帖工作台与积分存量看板

后台可注入全站 CSS/JS(SSR 生效,CSS 可热换);发帖/编辑改为锁一屏工作室;经济看板增加存量健康度;主题令牌与弹层误关一并收紧。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-18 04:16:32 +08:00
parent fa713bfc3f
commit 5f7193042f
41 changed files with 2297 additions and 873 deletions

View File

@@ -45,14 +45,16 @@ export function AdminChartCard({
hint,
children,
actions,
className = "",
}: {
title: string;
hint?: string;
children: React.ReactNode;
actions?: React.ReactNode;
className?: string;
}) {
return (
<div className="panel p-4 sm:p-5 flex flex-col min-h-[260px]">
<div className={`panel p-4 sm:p-5 flex flex-col ${className}`.trim()}>
<div className="flex items-start justify-between gap-2 mb-3">
<div className="min-w-0">
<h3 className="text-[14.5px] font-semibold" style={{ color: "var(--ink)" }}>
@@ -66,7 +68,7 @@ export function AdminChartCard({
</div>
{/* 捕获阶段拦截 mousedown:冒泡阶段太晚,浏览器已开始 SVG 多柱选区黑框 */}
<div
className="flex-1 min-h-[180px] w-full overflow-hidden select-none [&_*]:select-none [&_svg]:outline-none [&_.recharts-surface]:outline-none [&_.recharts-tooltip-cursor]:hidden"
className="w-full overflow-hidden select-none [&_*]:select-none [&_svg]:outline-none [&_.recharts-surface]:outline-none [&_.recharts-tooltip-cursor]:hidden"
onMouseDownCapture={(e) => e.preventDefault()}
>
{children}
@@ -144,15 +146,17 @@ export function SingleAreaChart({
dataKey,
name,
color = "var(--accent)",
height = 200,
}: {
data: { date: string; [k: string]: string | number }[];
dataKey: string;
name: string;
color?: string;
height?: number;
}) {
const rows = data.map((d) => ({ ...d, label: shortDate(String(d.date)) }));
return (
<ResponsiveContainer width="100%" height={200}>
<ResponsiveContainer width="100%" height={height}>
<AreaChart data={rows} margin={{ top: 12, right: 12, left: 0, bottom: 0 }} accessibilityLayer={false}>
<CartesianGrid stroke="var(--line)" strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="label" tick={{ fill: "var(--ink-3)", fontSize: 11 }} axisLine={false} tickLine={false} />
@@ -183,8 +187,10 @@ export function NamedBarChart({
layout?: "vertical" | "horizontal";
}) {
if (layout === "horizontal") {
// 固定像素高度,避免 overflow 滚动条 ↔ ResponsiveContainer 互相触发闪烁
const chartH = Math.max(180, Math.min(320, data.length * 28));
return (
<ResponsiveContainer width="100%" height={Math.max(180, data.length * 28)}>
<ResponsiveContainer width="100%" height={chartH}>
<BarChart data={data} layout="vertical" margin={{ top: 8, right: 12, left: 8, bottom: 4 }} accessibilityLayer={false}>
<CartesianGrid stroke="var(--line)" strokeDasharray="3 3" horizontal={false} />
<XAxis type="number" tick={{ fill: "var(--ink-3)", fontSize: 11 }} axisLine={false} allowDecimals={false} />