fix(qa): 第五轮系统性质量保障缺陷修复 + 单元/集成测试

安全/鉴权:登出清服务端 httpOnly 令牌、登录 IP 限流前置 bcrypt、停用账号
令牌在 6 个内联 role handler 失效、admin/items 存在性 oracle 认证前置、
同意偏好 sanitize fail-closed、api-crypto 按 HTTP 方法判体。

CMS:workflow 状态更新+审计收进交互式 $transaction(失败即回滚)、编辑器
richtext 走 textarea 防换行损毁、array/object 子字段按作用域读写防污染、
about/contact/erp-upgrade 补 ISR revalidate 与路由映射。

UI/a11y:AnimatedCounter 入视口前显起点、CLS 累计上报、MetricCard 动画入
effect 并卸载取消 rAF、后台状态四态映射/搜索防抖+序号守卫/媒体单 input/
吞错改横幅/表单 label-aria 关联。

新增对应判别性单测与 pagination/rate-limit/client-ip/sanitize/validate-content-data
等模块及 tests-integration 真库集成层。
This commit is contained in:
2026-09-28 10:48:07 +08:00
parent 040951c0a3
commit a366bd1400
224 changed files with 8938 additions and 5579 deletions
+31 -97
View File
@@ -25,6 +25,11 @@ body {
--color-brand-light: #E04A68;
--color-brand-soft: rgba(196, 30, 58, 0.12);
--color-brand-bg: #FEF2F4;
/* Tailwind 无法对「任意值形式的裸 var()」套用 /NN 透明度修饰符(bg-[var(--color-brand-bg)] 再加 /50
编译后 0 条规则,静默失效),故底色通道也要提供 rgb 三元组,配合 tailwind.config.js 的
brand.bg = rgb(var(--color-brand-bg-rgb) / <alpha-value>) 让 bg-brand-bg/50 正确生成。
254 242 244 = #FEF2F4,alpha 默认 1 时与裸 var() 同色,无视觉漂移。 */
--color-brand-bg-rgb: 254 242 244;
--color-brand-rgb: 196 30 58;
/* 品牌「文字墨色」专用通道(text-brand-ink)。
与 --color-brand-rgb 分离的原因:--color-brand-rgb 同时服务 bg-brand / border-brand,
@@ -33,6 +38,10 @@ body {
深色下取 #F87171(248 113 113):于 #0A0E14/#0F1419/#151B23 分别 6.99/6.69/6.26,全达 AA。 */
--color-brand-ink: #C41E3A;
--color-brand-ink-rgb: 196 30 58;
/* 品牌红文字通道的 hover 档:与 --color-brand-hover(底色通道)分离,
暗色下必须提亮,否则 #A01830 压在 #0A0E14 上仅 2.06:1(验收 A-12 残留项)。 */
--color-brand-ink-hover: #A01830;
--color-brand-ink-hover-rgb: 160 24 48;
/* 详情页 Hero badge 的 accent 文字色(accentColor 由各页 hero-themes 传入,
经 --badge-accent 自定义属性注入)。浅色 = 原色;深色 = 45% accent + 55% 白的提亮变体
—— accentColor 多为深色系(#1e3a5f 等),直接用作深底文字仅 1.65:1 */
@@ -76,6 +85,10 @@ body {
--color-bg-secondary-rgb: 248 250 252;
--color-bg-tertiary: #F1F5F9;
--color-bg-section: #F8FAFC;
/* 同 --color-brand-bg-rgb 的理由:bg-[var(--color-bg-section)] 再加 /60 这类写法编译后 0 条规则。
与 --color-bg-secondary-rgb 当前同值但语义不同(section ≠ secondary),故独立建三元组,
避免任一 token 调整时静默串色。248 250 252 = #F8FAFC。 */
--color-bg-section-rgb: 248 250 252;
--color-bg-hover: #F1F5F9;
--color-bg-elevated: #FFFFFF;
--color-bg-primary-rgb: 255 255 255;
@@ -268,12 +281,14 @@ body {
/* ease-out 别名:默认使用水墨曲线(Porsche 风格:快速开始,缓慢结束) */
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
/* 动效延迟序列(错落动效用) */
--stagger-xs: 30ms;
--stagger-sm: 60ms;
--stagger-md: 100ms;
--stagger-lg: 150ms;
--stagger-xl: 200ms;
/* 动效延迟序列(错落动效用)—— 已删除:--stagger-xs…xl 此前零消费者。
全站 stagger 全部由 framer-motion 以「秒」为单位的数值驱动
(transition.delay / variants.staggerChildren),CSS 自定义属性无法被
transition.delay 读取(除非物质化 getComputedStyle),故令牌在此形同虚设。
唯一事实来源改为 JS:src/components/ui/scroll-reveal.tsx(StaggerReveal
默认 staggerDelay = 0.05)与 page-transition.tsx。
取值区间按 CONTEXT.md「动效设计四原则」4:子元素 stagger 30–60ms、
Section 间 stagger 100–150ms。 */
/* 容器宽度 */
--container-max: 1360px;
@@ -369,15 +384,6 @@ body {
--accordion-trigger-height: 3rem;
--accordion-trigger-padding-x: 1rem;
/* Skeleton 骨架屏 */
--skeleton-radius: var(--radius-sm);
--skeleton-bg: var(--color-bg-tertiary);
/* Stats Bar 数据条 */
--stats-bar-gap: var(--spacing-2xl);
--stats-number-size: var(--font-size-4xl);
--stats-label-size: var(--font-size-xs);
/* === 12 列网格系统(Grid System)=== */
--grid-columns: 12;
--grid-gap-xs: 0.5rem;
@@ -419,6 +425,8 @@ html[data-theme='dark'] {
--color-bg-secondary-rgb: 15 20 25;
--color-bg-tertiary: #151B23;
--color-bg-section: #0F1419;
/* 与 :root 的 --color-bg-section-rgb 成对覆写,否则深色下 bg-bg-section/NN 会退回浅色值 */
--color-bg-section-rgb: 15 20 25;
--color-bg-hover: #151B23;
--color-bg-elevated: #151B23;
@@ -470,7 +478,11 @@ html[data-theme='dark'] {
此前只翻了 error-bg,导致深色下白字压浅粉底仅 1.04:1(首页「首批客户共创计划」卡)。 */
--color-brand-ink: #F87171;
--color-brand-ink-rgb: 248 113 113;
--color-brand-ink-hover: #FCA5A5;
--color-brand-ink-hover-rgb: 252 165 165;
--color-brand-bg: #2A1418;
/* 42 20 24 = #2A1418;与上方 --color-brand-bg 同步翻转,否则 bg-brand-bg/NN 在深色下仍是浅粉 */
--color-brand-bg-rgb: 42 20 24;
/* hero 徽章文字:accentColor 直用作深底文字不可读(如 #1e3a5f 于 #0B1119 仅 1.65:1),
深色下按 accent 45% + 白 55% 提亮;--badge-accent 未注入时回退到反色墨字。 */
@@ -1312,16 +1324,13 @@ p, li, span {
/* WCAG 2.5.5 (AAA): 44×44px 触控目标仅作用于移动端导航/页眉/页脚交互元素。
原全局选择器会撑大正文行内链接,破坏排版节奏与组件自有的尺寸控制。 */
@media (max-width: 768px) {
/* MobileTabBar 是 fixed bottom-0 高度 64px,需给 main 与 footer 都留出安全区
防止正文/页脚被遮挡("联系我们"等页脚内链接被截问题)。
使用 calc + env(safe-area-inset-bottom) 兼容刘海屏/小白条。
全站单一 <main>(ClientLayout,2026-09-19 polish 三重 main 收敛后)。 */
/* MobileTabBar 是 fixed bottom-0 高度 64px(含 env 安全区),需给页面底部留出等高的避让。
全站单一 <main>(ClientLayout)且 <Footer> 就在 main 内,因此这条 padding 是唯一的避让点:
此前 main 与 footer 各加一次,safe-area 被算了两遍(验收 R-1,iPhone SE 上底部空 128px+2×inset)。
使用 calc + env(safe-area-inset-bottom) 兼容刘海屏/小白条。 */
main {
padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
}
footer {
padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
}
nav a,
nav button,
header a,
@@ -1456,78 +1465,3 @@ p, li, span {
.glow-border:hover {
animation-play-state: running;
}
/* === CMS RichTextEditor (TipTap) 样式 === */
/* 仅用于 CMS Studio 暗色主题,选择器限定在 .rich-text-editor-content */
.rich-text-editor-content {
min-height: 120px;
padding: 0.75rem;
color: white;
font-size: 0.875rem;
line-height: 1.6;
outline: none;
}
.rich-text-editor-content:empty::before {
content: attr(data-placeholder);
color: rgba(255, 255, 255, 0.3);
pointer-events: none;
}
.rich-text-editor-content h2 {
font-size: 1.125rem;
font-weight: 600;
margin: 0.75rem 0 0.5rem;
color: white;
}
.rich-text-editor-content h3 {
font-size: 1rem;
font-weight: 600;
margin: 0.5rem 0 0.375rem;
color: white;
}
.rich-text-editor-content p {
margin: 0.375rem 0;
}
.rich-text-editor-content ul,
.rich-text-editor-content ol {
padding-left: 1.25rem;
margin: 0.5rem 0;
}
.rich-text-editor-content ul {
list-style: disc;
}
.rich-text-editor-content ol {
list-style: decimal;
}
.rich-text-editor-content li {
margin: 0.25rem 0;
}
.rich-text-editor-content blockquote {
border-left: 3px solid var(--color-brand);
padding-left: 0.75rem;
margin: 0.5rem 0;
color: rgba(255, 255, 255, 0.7);
font-style: italic;
}
.rich-text-editor-content a {
color: var(--color-brand);
text-decoration: underline;
}
.rich-text-editor-content strong {
font-weight: 600;
color: white;
}
.rich-text-editor-content em {
font-style: italic;
}