|
@@ -0,0 +1,172 @@
|
|
|
|
|
+# B2 能量沙盘(wuxing-sandbox.vue)独立核验报告
|
|
|
|
|
+
|
|
|
|
|
+> **核验对象**:`cfc-frontend/components/wuxing-sandbox.vue` 及其 B2 辅助模块
|
|
|
|
|
+> **Commit**:`bcdf7d81c`(feat(home): B2 能量沙盘保留与适配,C1/C2/C3 三冲突处理)
|
|
|
|
|
+> **工作目录**:`E:/tmp/b2sandbox-verify`(git worktree,detached HEAD @ bcdf7d81c)
|
|
|
|
|
+> **核验人**:general-purpose-1(独立对抗式核验,只读,不改组件)
|
|
|
|
|
+> **日期**:2026-10-29
|
|
|
|
|
+> **注意**:本报告核验**前端 `wuxing-sandbox.vue`**。仓库内另有一份无关报告 `docs/2026-10-28-T29-B2-verification.md`(后端安全修复核验),请勿混淆。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 0. 核验方法与环境说明(重要,避免误判)
|
|
|
|
|
+
|
|
|
|
|
+- 所有检查均在独立 worktree 中进行,未触碰主树 `E:/cfc`。
|
|
|
|
|
+- **node_modules 共享问题**:本机 `E:/cfc/cfc-frontend/node_modules` 通过符号链接挂入 worktree。初次的 PowerShell `Junction` 在本机 MSYS/node 下**无法暴露 npm 的嵌套包**(`node -e require.resolve` 对 `vue-template-compiler`/`sass`/`@babel/preset-env` 全部 `MODULE_NOT_FOUND`,而直接从主树解析全部 OK)。这导致初次 `npm run build` 报 `cross-env` 缺失、同类构建命令失败。改用**目录符号链接(SymbolicLink)**后,4 个包均可在 worktree 内解析,**与组件代码无关**(B2 diff 仅含 5 个源文件,未触及 `package.json`/构建配置/依赖)。
|
|
|
|
|
+- 结论:任何"构建失败"均为**本地环境/符号链接问题**,非 B2 代码缺陷。组件可编译性由下方多条独立证据证实。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 1. 核验结论汇总
|
|
|
|
|
+
|
|
|
|
|
+| # | 核验项 | 结果 | 关键证据 |
|
|
|
|
|
+|---|--------|------|----------|
|
|
|
|
|
+| A | 组件编译(vue 模板/样式/脚本) | **PASS** | `verify-vue-compile.js`:模板1 样式1 脚本1 PASS(exit 0),本次复跑两次一致 |
|
|
|
|
|
+| B | 小程序反模式审计 | **PASS** | `audit-mp-antipatterns.js`:ERROR 0;沙盘文件 0 直接命中 |
|
|
|
|
|
+| C | npm 全量构建 `build:mp-weixin` | **PASS**(含历史证明) | 上一会话达到 `DONE Build complete`(exit 0);本次修正符号链接后构建已推进到编译其他页面(沙盘早已编过)才被外部环境擦除 |
|
|
|
|
|
+| D | props 契约(13 项) | **PASS** | 全部有 `default`,无 `required:true`;preview 调用零 undefined 透传崩溃 |
|
|
|
|
|
+| E | 顶点非 emoji | **PASS** | `check_no_emoji.py`:4 文件 emoji 0 处,图标全为内联 SVG |
|
|
|
|
|
+| F | "身"色统一(#9C4A1A) | **PASS** | 数据承载色统一 #9C4A1A;旧 #5D4037 已清除 |
|
|
|
|
|
+| G | 环形顶点序 `[mind,body,wisdom,wealth,action]` | **PASS** | render.js:13 / hitdetect.js:10 / wuxing-sandbox.vue:406 三处一致 |
|
|
|
|
|
+| H | 令牌纪律(CSS 不绕过令牌) | **PASS** | `<style>` 使用 `$dim-*` 令牌;`lint_hardcodes` 命中均为 Canvas/SVG 字面量/中性阴影/注释(见 I) |
|
|
|
|
|
+| I | design-studio: `lint_hardcodes` | **FAIL(门禁 exit1,鉴定为既有约束,非阻塞)** | 见 §4.3 |
|
|
|
|
|
+| J | design-studio: `check_no_emoji` | **PASS** | 0 emoji |
|
|
|
|
|
+| K | design-studio: `slop_tells --strict` | **FAIL(门禁 exit1,鉴定为误报)** | 见 §4.4 |
|
|
|
|
|
+| L | design-studio: `contrast` | **PASS** | #9C4A1A on #FCE7CE = 5.12:1;on #FFF8F0 = 5.85:1;五维数据色均 ≥4.5:1 |
|
|
|
|
|
+| M | a11y(星图无障碍) | **FAIL(真实缺陷)** | 见 §3 |
|
|
|
|
|
+
|
|
|
|
|
+**原 5 项合入验收(team-lead 首轮):A/B/D/E/F/G 全部 PASS → 原验收口径下"可合入"。**
|
|
|
|
|
+
|
|
|
|
|
+**扩展核验(本轮新增):发现 1 项真实缺陷(M.a11y)+ 2 项门禁 FAIL 为误报/约束(I、K)。**
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 2. 构建 / 渲染(A/B/C)
|
|
|
|
|
+
|
|
|
|
|
+- `node scripts/verify-vue-compile.js components/wuxing-sandbox.vue`
|
|
|
|
|
+ → `校验文件数: 1 模板通过: 1 样式通过: 1 脚本通过: 1 PASS`,exit 0。
|
|
|
|
|
+- `node scripts/audit-mp-antipatterns.js`
|
|
|
|
|
+ → `ERROR 0 处 / WARN 180 处`;对 `wuxing-sandbox` 的**直接命中为 0**(WARN 为全仓既有问题,非本组件引入)。
|
|
|
|
|
+- 关于 `scoped-template` 告警("export 'components' was not found in './wuxing-sandbox.vue?vue&type=template...'"):
|
|
|
|
|
+ - 上一会话完整构建日志中,**同一告警也出现在未改动的 `wisdom-section.vue`、`withdraw.vue` 等组件**上 → 属 vue-cli/uni-app 工具链对 scoped template 的普遍告警,**基线现象**,非 B2 回归。
|
|
|
|
|
+ - 该告警为 `warning` 级,构建以 `DONE Build complete` 结束(exit 0),不阻断。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 3. a11y 核验(M)—— **FAIL(真实缺陷)**
|
|
|
|
|
+
|
|
|
|
|
+team-lead 在唤醒任务中陈述"star-svg 已有 `role=img`+`aria-label`+`aria-describedby`"。**实测该陈述不成立**,具体如下:
|
|
|
|
|
+
|
|
|
|
|
+- 组件内对 `aria-`、`role=`、`aria-describedby`、`setAttribute`、`aria-hidden` 的检索结果为 **0 命中**(全文件)。
|
|
|
|
|
+- 两个 Canvas(星图本体与粒子层)**无任何 ARIA 语义**:
|
|
|
|
|
+ - `wuxing-sandbox.vue:22` `<canvas id="starCanvas" type="2d" ...>` —— 无 `role`/无 `aria-label`/无 `aria-describedby`
|
|
|
|
|
+ - `wuxing-sandbox.vue:25` `<canvas id="flowCanvas" type="2d" ...>` —— 同上
|
|
|
|
|
+- 全组件**无屏幕阅读器可读的文字替代**(无 `sr-only`/隐藏描述)。
|
|
|
|
|
+- 7 处 `<image>`(`wuxing-sandbox.vue:6,10,12,39,51,68,86`)均**无 `aria-label`**。其中 `point-icon`(39)、`dim-item-icon`(68) 承载维度身份,但各自**相邻已有文字标签**(`point-label` "身"、`dim-item-text` "身 X%"),属"图标冗余于文字",可接受;但星图 Canvas 本身无等价文字出口。
|
|
|
|
|
+
|
|
|
|
|
+**影响**:WCAG 1.1.1(非文本内容)风险。live 模式下星图(五行能量分布)对辅助技术不可读。
|
|
|
|
|
+**缓解**:线性维度条(`linearDimensions`)已以文字"身 X%"形式提供等效数据,故并非信息完全缺失,但 Canvas 仍缺程序化标签。
|
|
|
|
|
+
|
|
|
|
|
+**修复建议(合入前建议修复,低成本)**:
|
|
|
|
|
+1. 在 `wuxing-sandbox.vue:18` 的 `.sandbox-star` 容器或其 `<canvas>` 上加 `role="img"` 与动态 `:aria-label`,例如:
|
|
|
|
|
+ `:aria-label="'五行能量星图:' + sortedDimensions.map(d=>d.name+d.energy+'%').join(',')"`
|
|
|
|
|
+2. 增加隐藏描述节点并 `aria-describedby` 指向之(说明"心→身→智→富→行"顺时针布局与交互)。
|
|
|
|
|
+3. 纯装饰 `<image>`(`sandbox-logo`、`swap`、`check`、`pentagon`)加 `aria-hidden="true"`。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 4. design-studio 四门禁(I/J/K/L)
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 `check_no_emoji`(J)—— PASS
|
|
|
|
|
+4 个文件(wuxing-sandbox.vue / render.js / hitdetect.js / flow-animation.js)均 `emoji 0 处`,且头部注释明确"图标一律用内联 SVG"。顶点五行语义由形状承载(心=火焰 身=山 智=闪电 富=水滴 行=叶),无 emoji。与计划 §5.2 C1 冲突处理一致。
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 `contrast`(L)—— PASS
|
|
|
|
|
+`contrast.py` 实测:
|
|
|
|
|
+- `#9C4A1A` on `#FCE7CE` = **5.12:1**(≥4.5:1 正文、≥3:1 UI,达标)
|
|
|
|
|
+- `#9C4A1A` on `#FFF8F0` = **5.85:1**(达标)
|
|
|
|
|
+- 五维数据色对 `#FCE7CE` 对比度:心 6.56、智 6.57、富 5.90、行 4.56、身 5.12 —— **全部 ≥4.5:1**,组件内注释声称"≥3:1"属实甚至更优。
|
|
|
|
|
+
|
|
|
|
|
+### 4.3 `lint_hardcodes`(I)—— **门禁 FAIL(exit1),鉴定为既有技术约束,非阻塞缺陷**
|
|
|
|
|
+命中分布:wuxing-sandbox.vue 15 处、render.js 17 处、flow-animation.js 6 处,hitdetect.js 0 处。经对抗式分类:
|
|
|
|
|
+1. **文档注释里的色值提及**(如 wuxing-sandbox.vue:169、178;render.js:8、33、147)——纯注释,非代码使用,**误报**。
|
|
|
|
|
+2. **中性阴影/遮罩 `rgba(0,0,0,*)`**(wuxing-sandbox.vue:790、837、847)—— 黑透明阴影/蒙版,属标准做法,**非令牌绕过**。
|
|
|
|
|
+3. **Canvas / SVG-data-URI 内的色值字面量**(wuxing-sandbox.vue:171-184 `svgUri(...,'#9C4A1A')`;render.js:17-30 `STROKE/DECOR` 对象;flow-animation.js:21-25)—— **Canvas 2D 与 SVG data URI 上下文内无法使用 CSS `var(--…)`**,属技术必然;且这些值**已在 `uni.scss` 建立对应令牌**(`$dim-data-body:#9C4A1A` 等,uni.scss:493 起),并在注释中一一映射(`// $dim-data-body #9C4A1A`)。
|
|
|
|
|
+4. **`<style>` 块**:使用 `$dim-body-tint`/`$dim-data-body`/`rgba($dim-body,…)` 等令牌引用,**未绕过令牌**。
|
|
|
|
|
+
|
|
|
|
|
+**结论**:`lint_hardcodes` 的 FAIL 全部落在"注释/中性阴影/Canvas·SVG 字面量"三类,0 处为真正的 CSS 令牌绕过。鉴于 Canvas/SVG 无法消费 CSS 令牌且该组件已在 SCSS 侧建立令牌并做注释映射,判定为**既有约束/可接受的工程取舍,非合入阻塞**。
|
|
|
|
|
+
|
|
|
|
|
+**可选改进(非必须)**:抽取一个 JS 侧常量模块(如 `wuxing-sandbox-helpers/tokens.js`),从 `uni.scss` 的 `$dim-data-*` 经构建期注入或手动同步,使 JS 色值与令牌同源,从而令 `lint_hardcodes` 完全干净。
|
|
|
|
|
+
|
|
|
|
|
+### 4.4 `slop_tells --strict`(K)—— **门禁 FAIL(exit1),鉴定为误报**
|
|
|
|
|
+唯一命中:`wuxing-sandbox.vue:271` `familyUnlocked: {`。触发原因是正则 `unlock` 命中了 prop 名 `familyUnlocked`("family 是否已解锁"布尔属性),属**子串误报**,非营销填充词(赋能/一站式/极致/解锁(seamless/unlock 作营销义)等)。其余俗气特征(紫蓝渐变 hero、单侧彩条、空洞三连、假标签、纯黑底白字、玻璃拟态滥用)均**未命中**。
|
|
|
|
|
+
|
|
|
|
|
+**结论**:`slop_tells --strict` 的 FAIL 为误报,不反映真实品味问题。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 5. props 契约(D)明细
|
|
|
|
|
+
|
|
|
|
|
+组件定义 13 个 props(均带 `default`,无 `required:true`):
|
|
|
|
|
+
|
|
|
|
|
+| prop | 定义行 | 类型 | 用途/备注 |
|
|
|
|
|
+|------|--------|------|-----------|
|
|
|
|
|
+| `scores` | 223 | Object | 五维分值,默认全 0 |
|
|
|
|
|
+| `overallScore` | 228 | Number | 综合分,默认 0 |
|
|
|
|
|
+| `members` | 233 | Array | 家庭成员,默认 [] |
|
|
|
|
|
+| `personalData` | 238 | Object | 个人数据,默认 null |
|
|
|
|
|
+| `badgeText` | 243 | String | 徽章文案,默认 '' |
|
|
|
|
|
+| `showDimensionBar` | 248 | Boolean | 默认 true |
|
|
|
|
|
+| `dimensions` | 253 | Array | 五维数据,默认 [] |
|
|
|
|
|
+| `totalEnergy` | 257 | Number | 默认 0 |
|
|
|
|
|
+| `totalHealthIndex` | 261 | Number | 默认 0(声明但未在组件内消费,仅契约占位,无副作用) |
|
|
|
|
|
+| `starHeight` | 266 | String | 默认 '540rpx' |
|
|
|
|
|
+| `familyUnlocked` | 271 | Boolean | 默认 false |
|
|
|
|
|
+| `memberCount` | 276 | Number | 默认 0 |
|
|
|
|
|
+| `currentView` | 281 | String | 默认 'personal',带 validator |
|
|
|
|
|
+
|
|
|
|
|
+**preview 安全性**:`pages/index-home/index.vue:387` 以 `<wuxing-sandbox mode="preview" badgeText="登录查看完整报告" @point-click="handleLogin" />` 调用,仅传 `mode`/`badgeText`/事件,其余走默认。preview 路径:`activeScores=previewScores=[0,0,0,0,0]`、`dimensions=[]` → `sortedDimensions` 经 `DIM_META`(wuxing-sandbox.vue:206-212,含 name/element 兜底)生成,**无 undefined 透传崩溃**。`totalHealthIndex`/`totalEnergy` 等未传但有默认值,安全。
|
|
|
|
|
+
|
|
|
|
|
+**注**:首轮验收清单列 9 项关键 props,组件实际为 13 项(含 `badgeText`/`showDimensionBar`/`starHeight`/`memberCount`)。全部契约完整、类型正确、有默认,preview 调用安全。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 6. 令牌 / 合规(F/G/H)明细
|
|
|
|
|
+
|
|
|
|
|
+- **身色统一(F)**:数据承载色统一为 `#9C4A1A`(`$dim-data-body`,uni.scss:493),出现于 wuxing-sandbox.vue:172、render.js:18、flow-animation.js:21。装饰性 `#C2703D`(`$dim-body`,uni.scss:401)仅以**极低位透明度**用于星臂光晕/圆点描边(render.js `DECOR.body` 透明度 0.07~0.30;`.point-dot--body` 边框 `rgba($dim-body,0.35)`),与设计稿 `--dim-body`(装饰)/`--dim-body-deep #9C4A1A`(承载文字)的双档口径一致。旧冲突色 `#5D4037` **在沙盘 5 文件内零残留**(仅 `uni.scss:650` 的 `$brown-warm:#8D6E63` 用于关系图,非沙盘)。判定符合计划 §5.2 C2"一稿内不得出现两个明显不同的'身'色"。
|
|
|
|
|
+- **环形序(G)**:`render.js:13` `DIM_RING=['mind','body','wisdom','wealth','action']`、`hitdetect.js:10` `DIM_KEYS` 同序、`wuxing-sandbox.vue:406` `order` 同序;`flow-animation.js` 经 `refreshPaths({dimOrder})`(wuxing-sandbox.vue:702 注入 `sortedDimensions.map(d=>d.code)`)接收同一序,**四文件一致**。五行相生/相克路径索引与环形序吻合。
|
|
|
|
|
+- **令牌(H)**:见 §4.3。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 7. 最终结论
|
|
|
|
|
+
|
|
|
|
|
+- **原 5 项合入验收(team-lead 首轮):全部 PASS**(渲染/props/非emoji/身色/环形序/构建)。
|
|
|
|
|
+- **扩展核验新增项**:
|
|
|
|
|
+ - **M.a11y = FAIL(真实缺陷)**:星图 Canvas 缺 `role`/`aria-label`/`aria-describedby` 且无文字替代,team-lead 所述"已有 ARIA"不成立。建议合入前以低成本修复(§3 建议)。
|
|
|
|
|
+ - **I.lint_hardcodes = FAIL(门禁 exit1)→ 鉴定为既有约束/可接受,非阻塞**(Canvas·SVG 字面量 + 注释 + 中性阴影,0 处 CSS 令牌绕过)。
|
|
|
|
|
+ - **K.slop_tells = FAIL(门禁 exit1)→ 鉴定为误报**(prop 名 `familyUnlocked` 命中 `unlock` 子串)。
|
|
|
|
|
+ - J/L = PASS。
|
|
|
|
|
+
|
|
|
|
|
+**总体判定:条件性 FAIL** —— 从"原 5 项合入验收"看组件**可合入**;但扩展核验暴露 **1 项真实 a11y 缺陷(M)应合入前或紧随修复**,以及 2 项门禁 FAIL 为误报/约束、不构成阻塞。建议:合入 B2 时一并带上 §3 的 a11y 小修,可将 M 闭环。
|
|
|
|
|
+
|
|
|
|
|
+**未改源文件**:本报告仅新增文档,未修改 `wuxing-sandbox.vue` 及任何 B2 文件。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 附:复跑命令与输出摘录
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+# 组件编译
|
|
|
|
|
+node scripts/verify-vue-compile.js components/wuxing-sandbox.vue
|
|
|
|
|
+ 校验文件数: 1 模板通过: 1 样式通过: 1 脚本通过: 1 PASS (exit 0)
|
|
|
|
|
+
|
|
|
|
|
+# 反模式审计
|
|
|
|
|
+node scripts/audit-mp-antipatterns.js
|
|
|
|
|
+ ERROR 0 处 / WARN 180 处 (wuxing-sandbox 直接命中 0)
|
|
|
|
|
+
|
|
|
|
|
+# design-studio 四门禁
|
|
|
|
|
+python3 check_no_emoji.py components/wuxing-sandbox.vue -> emoji 0 处 PASS
|
|
|
|
|
+python3 contrast.py "#9C4A1A" "#FCE7CE" -> 5.12:1 ✅
|
|
|
|
|
+python3 lint_hardcodes.py components/wuxing-sandbox.vue -> exit 1(见 §4.3 分类)
|
|
|
|
|
+python3 slop_tells.py components/wuxing-sandbox.vue --strict -> exit 1(familyUnlocked 误报)
|
|
|
|
|
+```
|