feat(student-portal): 完整实现 student-portal 微前端

包含 src 全部实现、Dockerfile、配置文件、contracts 包等
This commit is contained in:
SpecialX
2026-07-10 19:10:36 +08:00
parent 24c2860b41
commit 74474a2d04
80 changed files with 17226 additions and 70 deletions

View File

@@ -0,0 +1,196 @@
/**
* globals.css — student-portal 全局样式ai14
*
* 引入顺序semantic.css含 primitive.css→ Tailwind 三层
* 对齐 teacher-portal Shell 纸感设计paper/ink/accent/rule
*/
@import './semantic.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
/* === 基础排版 === */
@layer base {
html,
body {
background: var(--color-paper);
color: var(--color-ink);
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--font-serif);
font-weight: 600;
letter-spacing: -0.01em;
color: var(--color-ink);
}
/* 焦点可见性A11y WCAG 2.2*/
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
border-radius: var(--radius-sm);
}
/* 链接 */
a {
color: var(--color-accent);
text-decoration: none;
transition: color var(--duration-fast) var(--ease-standard);
}
a:hover {
color: var(--color-accent-hover);
}
}
/* === 组件层 === */
@layer components {
/* 纸感分隔线 */
.rule {
border-top: 1px solid var(--color-rule);
}
.rule-thin {
border-top: 2px solid var(--color-rule);
}
/* 左侧竖线标记(节点展开样式,对齐 teacher-portal*/
.mark-left {
border-left: 2px solid var(--color-rule);
padding-left: var(--space-md);
}
/* 纸感卡片 */
.card-paper {
background: var(--color-paper);
border: 1px solid var(--color-rule);
border-radius: var(--radius-base);
box-shadow: var(--shadow-paper);
}
/* 主按钮 */
.btn-primary {
background: var(--color-accent);
color: var(--color-paper);
padding: var(--space-sm) var(--space-lg);
border-radius: var(--radius-base);
font-family: var(--font-sans);
font-size: var(--font-size-3);
transition: background var(--duration-fast) var(--ease-standard);
}
.btn-primary:hover {
background: var(--color-accent-hover);
}
.btn-primary:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 次级按钮 */
.btn-secondary {
background: transparent;
color: var(--color-accent);
border: 1px solid var(--color-accent);
padding: var(--space-sm) var(--space-lg);
border-radius: var(--radius-base);
}
/* 输入框 */
.input-paper {
background: var(--color-paper);
border: 1px solid var(--color-rule);
border-radius: var(--radius-base);
padding: var(--space-sm) var(--space-md);
font-family: var(--font-sans);
font-size: var(--font-size-3);
color: var(--color-ink);
transition: border-color var(--duration-fast) var(--ease-standard);
}
.input-paper:focus {
border-color: var(--color-accent);
outline: none;
box-shadow: var(--shadow-focus);
}
/* 徽标 */
.badge {
display: inline-flex;
align-items: center;
padding: 2px var(--space-sm);
border-radius: var(--radius-sm);
font-size: var(--font-size-2);
font-weight: 500;
}
.badge-success {
background: hsl(var(--hue-success), var(--sat-success), 92%);
color: var(--color-success);
}
.badge-warning {
background: hsl(var(--hue-warning), var(--sat-warning), 92%);
color: var(--color-warning);
}
.badge-danger {
background: hsl(var(--hue-danger), var(--sat-danger), 92%);
color: var(--color-danger);
}
.badge-info {
background: hsl(var(--hue-info), var(--sat-info), 92%);
color: var(--color-info);
}
}
/* === 工具层 === */
@layer utilities {
/* 文本截断 */
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 多行截断 */
.text-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 屏幕阅读器专用 */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* 减少动画A11y prefers-reduced-motion*/
@media (prefers-reduced-motion: reduce) {
.motion-safe {
animation: none !important;
transition: none !important;
}
}
}

View File

@@ -0,0 +1,89 @@
/**
* Layer 1 — Primitive 原始令牌ai14
*
* 项目规则 §3.10:原始色板/字号/间距/阴影,业务代码不直接引用
* 仅 semantic.css 引用此文件
* 与 teacher-portal Shell 令牌对齐paper/ink/accent/rule
*/
:root {
/* === 原始色板HSL 分量,供 semantic.css 组合)=== */
--hue-paper: 40;
--sat-paper: 20%;
--light-paper: 98%;
--hue-ink: 25;
--sat-ink: 3%;
--light-ink: 15%;
--light-ink-muted: 45%;
--light-ink-subtle: 65%;
--hue-accent: 220;
--sat-accent: 60%;
--light-accent: 35%;
--light-accent-hover: 28%;
--light-accent-muted: 92%;
--hue-rule: 30;
--sat-rule: 10%;
--light-rule: 90%;
/* 语义色相 */
--hue-success: 142;
--sat-success: 50%;
--light-success: 35%;
--hue-warning: 38;
--sat-warning: 80%;
--light-warning: 45%;
--hue-danger: 0;
--sat-danger: 70%;
--light-danger: 45%;
--hue-info: 200;
--sat-info: 70%;
--light-info: 45%;
/* === 字号阶梯(对齐 var(--font-size-1~9)=== */
--font-size-1: 12px;
--font-size-2: 14px;
--font-size-3: 16px;
--font-size-4: 18px;
--font-size-5: 20px;
--font-size-6: 24px;
--font-size-7: 30px;
--font-size-8: 36px;
--font-size-9: 48px;
/* === 间距阶梯 === */
--space-xs: 4px;
--space-sm: 8px;
--space-md: 12px;
--space-lg: 16px;
--space-xl: 24px;
--space-2xl: 32px;
--space-3xl: 48px;
/* === 圆角 === */
--radius-sm: 3px;
--radius-base: 6px;
--radius-md: 8px;
--radius-lg: 12px;
/* === 阴影(纸感)=== */
--shadow-paper: 0 1px 2px hsl(25, 3%, 15%, 0.04);
--shadow-card: 0 2px 8px hsl(25, 3%, 15%, 0.06);
--shadow-focus: 0 0 0 3px hsl(220, 60%, 35%, 0.2);
/* === 字体族(仅 primitive.css 可定义字面量)=== */
--font-serif-literal: 'Fraunces', Georgia, serif;
--font-sans-literal: 'Inter', system-ui, sans-serif;
--font-mono-literal: 'JetBrains Mono', monospace;
/* === 过渡 === */
--duration-fast: 120ms;
--duration-base: 200ms;
--duration-slow: 400ms;
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
}

View File

@@ -0,0 +1,58 @@
/**
* Layer 2 — Semantic 语义令牌ai14
*
* 项目规则 §3.10:业务代码唯一引用入口
* 引用 primitive.css 原始令牌组合为语义令牌
* 支持 light/dark 双主题(当前仅 light
*/
@import './primitive.css';
:root {
/* === 语义色light 主题)=== */
--color-paper: hsl(var(--hue-paper), var(--sat-paper), var(--light-paper));
--color-ink: hsl(var(--hue-ink), var(--sat-ink), var(--light-ink));
--color-ink-muted: hsl(var(--hue-ink), var(--sat-ink), var(--light-ink-muted));
--color-ink-subtle: hsl(var(--hue-ink), var(--sat-ink), var(--light-ink-subtle));
--color-accent: hsl(var(--hue-accent), var(--sat-accent), var(--light-accent));
--color-accent-hover: hsl(var(--hue-accent), var(--sat-accent), var(--light-accent-hover));
--color-accent-muted: hsl(var(--hue-accent), var(--sat-accent), var(--light-accent-muted));
--color-rule: hsl(var(--hue-rule), var(--sat-rule), var(--light-rule));
--color-success: hsl(var(--hue-success), var(--sat-success), var(--light-success));
--color-warning: hsl(var(--hue-warning), var(--sat-warning), var(--light-warning));
--color-danger: hsl(var(--hue-danger), var(--sat-danger), var(--light-danger));
--color-info: hsl(var(--hue-info), var(--sat-info), var(--light-info));
/* === 语义字体族(暴露给 Tailwind=== */
--font-serif: var(--font-serif-literal);
--font-sans: var(--font-sans-literal);
--font-mono: var(--font-mono-literal);
/* === 语义间距 === */
--space-xs: var(--space-xs);
--space-sm: var(--space-sm);
--space-md: var(--space-md);
--space-lg: var(--space-lg);
--space-xl: var(--space-xl);
--space-2xl: var(--space-2xl);
--space-3xl: var(--space-3xl);
}
/* dark 主题预留P6+ 启用)*/
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) {
--color-paper: hsl(var(--hue-paper), var(--sat-paper), 8%);
--color-ink: hsl(var(--hue-ink), var(--sat-ink), 92%);
--color-ink-muted: hsl(var(--hue-ink), var(--sat-ink), 65%);
--color-ink-subtle: hsl(var(--hue-ink), var(--sat-ink), 45%);
--color-accent: hsl(var(--hue-accent), var(--sat-accent), 55%);
--color-accent-hover: hsl(var(--hue-accent), var(--sat-accent), 65%);
--color-accent-muted: hsl(var(--hue-accent), var(--sat-accent), 15%);
--color-rule: hsl(var(--hue-rule), var(--sat-rule), 20%);
}
}