feat(portal-shell): v2.0 P0 shadcn standardization + security + streaming + error handling

- shadcn/ui 标准化:废弃纸感令牌,统一 bg-background/text-foreground 等
- Tailwind v4 + @theme inline,移除 tailwind.config.js
- React 19 use() + Suspense 流式渲染,首屏骨架秒出
- 三级错误边界:Route → Section → Widget 层层兜底
- 错误上报:useErrorReport → sendBeacon → /api/log mock 端点
- 三层安全边界:L1 角色门禁 / L2 权限点门禁 / L3 数据范围
- 权限位图 base36 压缩:67 权限点 → ~14 字符,JWT 体积减少 ≥ 99%
- notify 统一 Toast 封装,禁止业务直接 import sonner
- PluginBoundary 替代 PluginLoader(错误边界 + Suspense + Skeleton 三件套)

验证:typecheck 0 错误 / lint 0 错误 / build 6 路由生成成功
This commit is contained in:
SpecialX
2026-07-17 16:10:05 +08:00
parent f7e52b5b7f
commit 9cedf0c437
140 changed files with 10872 additions and 3192 deletions

View File

@@ -1,112 +1,116 @@
/**
* Layer 1: Primitive Tokens原始色板/字号/间距/阴影)
* Layer 1: Primitive Tokens原始色板/字号/间距/阴影/字体家族
*
* 业务代码不直接引用本层令牌,仅 Layer 2 Semantic 引用。
* 维护者ai13teacher-portal
* 仅 Layer 2 Semantic 引用,业务代码不直接使用
* 色板层不区分明暗,主题差异在 Semantic 层体现。
*
* 对齐CICD 项目 src/app/styles/tokens/primitive.css
* 关联project_rules §3.10 设计令牌规范
*
* 命名规范:
* - 颜色:--color-<hue>-<level>HSL 分量,供 Layer 2 组合)
* - 字号:--font-size-<n>1-9 阶梯)
* - 间距:--space-<n>4px 基准阶梯)
* - 阴影:--shadow-<n>
* - 字重:--font-weight-<name>
* - 圆角:--radius-<name>
*/
:root {
/* ============ 颜色原始色板HSL 分量,非完整颜色) ============ */
/* 中性色(纸张/墨色) */
--color-paper-h: 40;
--color-paper-s: 20%;
--color-paper-l-50: 50%;
--color-paper-l-98: 98%;
--color-paper-l-99: 99%;
/* ============ 色板HSL 分量,非完整颜色) ============ */
--color-ink-h: 25;
--color-ink-s: 3%;
--color-ink-l-15: 15%;
--color-ink-l-45: 45%;
--color-ink-l-60: 60%;
/* Zinc 中性色板shadcn 默认) */
--color-zinc-50: 0 0% 99%;
--color-zinc-100: 240 4.8% 95.9%;
--color-zinc-200: 240 5.9% 90%;
--color-zinc-300: 240 4.8% 83.9%;
--color-zinc-400: 240 5% 64.9%;
--color-zinc-500: 240 3.8% 46.1%;
--color-zinc-600: 240 5.2% 33.9%;
--color-zinc-700: 240 5.3% 26.1%;
--color-zinc-800: 240 5.9% 10%;
--color-zinc-900: 240 5.9% 3.9%;
--color-zinc-950: 240 10% 3.9%;
/* 强调色(深蓝 */
--color-accent-h: 220;
--color-accent-s: 60%;
--color-accent-l-35: 35%;
--color-accent-l-50: 50%;
/* Stone 暖灰(用于纸感业务扩展,如备课编辑器 */
--color-stone-50: 60 4.8% 95.9%;
--color-stone-100: 60 5.1% 90%;
--color-stone-200: 20 5.9% 90%;
--color-stone-300: 24 5.7% 82.9%;
--color-stone-400: 24 5.4% 63.9%;
--color-stone-500: 25 5.1% 44.7%;
--color-stone-600: 33 5% 39.8%;
--color-stone-700: 30 5.2% 32.7%;
--color-stone-800: 12 6.5% 31.4%;
--color-stone-900: 24 10% 10%;
--color-stone-950: 20 14.3% 4.1%;
/* 分隔线(暖灰 */
--color-rule-h: 30;
--color-rule-s: 10%;
--color-rule-l-85: 85%;
--color-rule-l-90: 90%;
/* Indigo 强调色(业务交互强调 */
--color-indigo-500: 238.7 83.5% 66.7%;
--color-indigo-600: 238.6 84.5% 59.8%;
/* 语义原始色 */
--color-success-h: 142;
--color-success-s: 71%;
--color-success-l-45: 45%;
/* ============ 字号阶梯10 级0 最小 9 最大) ============ */
--font-size-0: 9px; /* 角色标签微字号 */
--font-size-1: 12px; /* 元信息/角色标签 */
--font-size-2: 13px; /* inline-node body */
--font-size-3: 13.5px; /* inline-node 主文(纸感) */
--font-size-4: 14px; /* 标题/按钮 */
--font-size-5: 16px; /* 正文Fraunces 16px */
--font-size-6: 18px; /* H2 */
--font-size-7: 20px; /* H1 */
--font-size-8: 24px; /* 区块标题 */
--font-size-9: 32px; /* 页面标题 */
--color-warning-h: 38;
--color-warning-s: 92%;
--color-warning-l-50: 50%;
/* ============ 间距阶梯 ============ */
--space-0: 0;
--space-0_5: 0.125rem; /* 2px */
--space-1: 0.25rem; /* 4px */
--space-1_5: 0.375rem; /* 6px */
--space-2: 0.5rem; /* 8px */
--space-2_5: 0.625rem; /* 10px */
--space-3: 0.75rem; /* 12px */
--space-3_5: 0.875rem; /* 14px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-7: 1.75rem; /* 28px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-18: 4.5rem; /* 72px */
--color-danger-h: 0;
--color-danger-s: 84%;
--color-danger-l-60: 60%;
/* ============ 阴影阶梯 ============ */
--shadow-1: 0 1px 2px rgba(15, 15, 15, 0.04);
--shadow-2: 0 1px 3px rgba(15, 15, 15, 0.06), 0 1px 2px rgba(15, 15, 15, 0.04);
--shadow-3: 0 4px 6px rgba(15, 15, 15, 0.05), 0 2px 4px rgba(15, 15, 15, 0.04);
--shadow-4: 0 1px 2px rgba(15, 15, 15, 0.04), 0 8px 24px rgba(15, 15, 15, 0.04);
--shadow-5: 0 1px 2px rgba(15, 15, 15, 0.06), 0 12px 36px rgba(15, 15, 15, 0.08);
--shadow-6: 0 10px 15px rgba(15, 15, 15, 0.1), 0 4px 6px rgba(15, 15, 15, 0.05);
--color-info-h: 199;
--color-info-s: 89%;
--color-info-l-48: 48%;
/* ============ 字体家族 ============ */
/* 引用 next/font 在 <html> 上注入的 CSS 变量fallback 保证 SSR/无字体时降级) */
--font-family-sans: var(--font-inter, 'Inter'), system-ui, sans-serif;
--font-family-serif: var(--font-fraunces, 'Fraunces'), Georgia, serif;
--font-family-mono: var(--font-jetbrains-mono, 'JetBrains Mono'), ui-monospace, monospace;
/* ============ 字号阶梯1-91 最小 9 最大) ============ */
--font-size-1: 0.75rem; /* 12px - 辅助说明 */
--font-size-2: 0.875rem; /* 14px - 次要正文 */
--font-size-3: 1rem; /* 16px - 正文 body */
--font-size-4: 1.125rem; /* 18px - 强调正文 */
--font-size-5: 1.25rem; /* 20px - 小标题 */
--font-size-6: 1.5rem; /* 24px - 区块标题 */
--font-size-7: 1.875rem; /* 30px - 页面标题 */
--font-size-8: 2.25rem; /* 36px - Hero */
--font-size-9: 3rem; /* 48px - 大数字 */
/* ============ 行高阶梯 ============ */
--leading-tight: 1.2;
--leading-snug: 1.35;
--leading-normal: 1.5;
--leading-relaxed: 1.65;
--leading-loose: 1.8;
/* ============ 间距阶梯4px 基准) ============ */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
/* ============ 字重阶梯 ============ */
--weight-regular: 400;
--weight-medium: 500;
--weight-semibold: 600;
--weight-bold: 700;
/* ============ 阴影 ============ */
--shadow-1: 0 1px 2px 0 hsl(25 3% 15% / 0.05);
--shadow-2: 0 1px 3px 0 hsl(25 3% 15% / 0.1), 0 1px 2px -1px hsl(25 3% 15% / 0.1);
--shadow-3: 0 4px 6px -1px hsl(25 3% 15% / 0.1), 0 2px 4px -2px hsl(25 3% 15% / 0.1);
--shadow-4: 0 10px 15px -3px hsl(25 3% 15% / 0.1), 0 4px 6px -4px hsl(25 3% 15% / 0.1);
/* ============ 动效 ============ */
--duration-fast: 150ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
/* ============ 字重 ============ */
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* ============ 圆角 ============ */
--radius-sm: 0.25rem; /* 4px */
--radius-md: 0.375rem; /* 6px */
--radius-lg: 0.5rem; /* 8px */
--radius-full: 9999px;
/* ============ 行高 ============ */
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.75;
/* ============ 字间距 ============ */
--letter-spacing-tight: -0.01em;
--letter-spacing-normal: 0;
--letter-spacing-wide: 0.025em;
/* ============ z-index ============ */
--z-dropdown: 1000;
--z-sticky: 1100;
--z-modal: 1300;
--z-popover: 1400;
--z-toast: 1500;
}