# 康师傅产品创新看板 · v6(展示逻辑重构:五类报告页 + 阶段产出 + 液态玻璃) > 研发中心 AI 赋能实战营的**组织资产系统**:30–40 位学员共用一个机会卡池、一份证据台账、一组分析报告。 > 本版按 `fmode-code-lit` 规范**重写骨架**,并把前四轮谈定的业务全部落进来 —— 是「用新规范做出旧需求」,不是缩水重做。 - 入口:`index.html`(由 `scripts/config.mjs` 生成,零构建) - 旧版(IIFE + `window.KF_*` + innerHTML)原样保留在 `legacy/`,只作业务逻辑参考,**不参与部署** - 代码规模:`src/` 6,216 行(含 `src/dev/` 两个开发辅助)· `test/` 529 行(46 个 Node 单测) - v6 只改**展示逻辑**,不动 v5 的分层骨架:新增 `core/library/`(报告类型归类)、五个报告类型页、阶段产出内容视角、玻璃令牌重做(见第 8 节)。 --- ## 1. 目录结构与分层 ``` src/ app.config.js 全应用唯一配置(Parse 地址 / 表名 / 阈值),只有公开 App ID,无凭据 app.js 入口:注入设计令牌、注册外壳、打印启动横幅 core/ [core] 纯 ESM 逻辑:不碰 window/document/fetch,不 import Lit,Node 可测 taxonomy/ 阶段 / 层 / 优先级 / 状态 / 决策 / 上传方式 + 字段归一(S1品类分析、S1 品类分析、S1 都认) opportunity/ 机会卡建模、多人评分统计、排序、状态流转(statusPatch 只产出 patch,不写库) evidence/chain.js 证据链双向反查(结论↔成果↔归纳↔机会卡),全部返回「名字 + 人名」 kfs/ Parse 行 → 业务模型(buildModel)、台账六维筛选(ledger)、派生统计(stats) report.js 报告章节 / 管线 / 结论块 / 概览 index.js 唯一公共出口(74 个导出) components/ [wc] 组件库:extends LitElement,只渲染与交互,零业务 kf-glass-card / kf-stat / kf-badge / kf-empty / kf-table / kf-drawer / kf-toast kf-opportunity-card 机会卡:五要素 + 来源四要素 + 状态快捷切换 + 内嵌报告链接(stopPropagation) kf-score-panel 多人评分面板(平均 / 人工 / Agent / 逐条明细 / 我来评分) kf-evidence-chain 证据链:成果→引用它的结论;归纳→来源报告 kf-pipeline 数据管线 layout/ [page] 外壳 kf-app 布局 / 路由订阅 / 全局抽屉 / 写操作(改状态、评分)/ 所有交互事件的唯一入口 kf-nav / kf-topbar 导航(池子数、卡住人数)/ 顶栏(「我是谁」、同步状态、刷新) pages/ [page] 七个视图:工作台 / 池 / 阶段 / 报告目录 / 报告阅读 / 台账 / 成员 services/ board-service:取数(http)→ core.buildModel → signal;写 Parse 后局部合并 + 后台刷新 utils/ router.js(History、子路径安全)/ http.js(唯一网络出口、分页、超时重试)/ storage.js styles/tokens.js 设计令牌 + 玻璃基元(Constructable StyleSheet,各组件 shadow root 内 `adoptedStyleSheets` 引入) dev/serve.mjs 本地预览服务器(History 回退 200 / `/parse` 同源代理 / 子路径挂载)—— 不参与部署 lib/vendor/ vendored 依赖(见第 2 节) scripts/ config / vendor / check / verify-modules / verify-render / verify-browser / seed / deploy.sh test/ core 单测(node:test,无 jsdom) ``` **分层边界(越界即返工)**:`core` 不认识 DOM(`scripts/check.mjs` 第 5 项硬校验);`components` 不 import services、不 fetch;`layout/pages` 组装一切,写库只经过 `services → utils/http`。 --- ## 2. 依赖走哪条路:**方案 B(本地 vendor)** 实测(2026-10-29,本机直连): | 地址 | 结果 | |---|---| | `https://repose.fmode.cn/npm/importmap/lit-v3.json` | **404** | | `https://kangshifu.fmode.cn/npm/importmap/lit-v3.json` | **404** | | `https://app.fmode.cn/npm/importmap/lit-v3.json` | **404** | | `https://kangshifu.fmode.cn/npm/lit/v3.3.3/lit.js` | **404** | 方案 A(探测真实 Repose)无果:本机 `.credentials` 与工作目录中没有任何 Repose 地址或凭据,三条已知候选路径全部 404。 因此 **vendor 化**:`scripts/vendor.mjs` 用 `npm pack` 下载(不 `npm install`、不留 node_modules),解到 `lib/vendor/<包>/v<版本>/`,去掉 `.ts / .d.ts / .map / src / development / decorators / test`,并校验 `package.json` 入口文件存在。 | 包 | 版本 | importmap 入口 | |---|---|---| | lit | 3.3.3 | `lib/vendor/lit/v3.3.3/index.js` | | lit-html | 3.3.3 | `lib/vendor/lit-html/v3.3.3/lit-html.js` | | lit-element | 4.2.2 | `lib/vendor/lit-element/v4.2.2/index.js` | | @lit/reactive-element | 2.1.2 | `lib/vendor/@lit/reactive-element/v2.1.2/reactive-element.js` | | @lit/context | 1.1.6 | `lib/vendor/@lit/context/v1.1.6/index.js` | | @lit-labs/signals | 0.3.0 | `lib/vendor/@lit-labs/signals/v0.3.0/index.js` | | signal-polyfill | 0.2.2 | `lib/vendor/signal-polyfill/v0.2.2/dist/index.js` | importmap 里每个包都同时映射 `pkg` 与 `pkg/`(子路径),保证 lit 系**单实例**(模块图实测:46 个模块中,vendor 只出现 `lit@v3.3.3`(含 `directives/class-map.js`)、`lit-html@v3.3.3`、`@lit/reactive-element@v2.1.2`、`@lit-labs/signals@v0.3.0` 各一个版本;其余 41 个是本仓 `src/` 文件)。所有 vendor 路径都写在 `` 前缀下,与业务码同源同前缀,**随 base 走**。 > 以后 Repose 恢复:把 `scripts/config.mjs` 里 `importMapJson()` 的 vendor 路径换成 `/npm/...` 即可,源码零改动(源码只 import 裸标识符)。 ### 依赖注入(@lit/context)的取舍 `@lit/context` 已 vendor 进 importmap,但本版**未使用**:应用只有一个全局服务(`boardService` 单例 + signals),页面层直接 import 单例、组件靠属性与事件通信,少一层间接。若之后拆成多团队协作的模块化页面,再按规范接 `provide()/ContextConsumer`。这是**刻意没做**的一项,不是遗漏。 --- ## 3. 机会卡为什么这么建模:新建 `KfOpportunity`,不复用 `KfArtifact` | 维度 | 成果 `KfArtifact` | 机会卡 | |---|---|---| | 生命周期 | 一次交付,写完即历史(改它等于篡改台账) | 候选 → 立项 / 否决,反复流转 | | 状态 | 无 | `status` + `decision` + `decisionLog`(每次改都留痕) | | 评分 | 无 | `scores[]` 多人评分,平均分随人变化 | | 组织含义 | 过程资产(过程 / 沉淀 / 记录) | **组织资产**:研发中心的机会储备 | 把两者合在一个类里,客户「改一次卡片状态」就会污染「成果台账」(updatedAt 变了、证据链追溯的成果也跟着变)。所以: - 机会卡单独一个类,**关联只存 id**(`linkedArtifacts[] / linkedProject`),成果被删卡片不消失,只标「库里已不存在」; - 卡片上的「谁 · 哪个 Agent · 什么方式 · 什么时候」是四要素 + 两个扩展位(`agentId`、`sourceSession`),为后续多个数字分身留好位置,不写死单分身; - 一处 `oneLiner` 是历史测试残留的占位符(`"x"`)→ core 里按「未填写」处理,**不编造内容**。 --- ## 4. 22 项验收:怎么验、实测结果 复现命令(全部在本机实跑过): ```bash node scripts/config.mjs local # 生成 index.html(base / importmap / 前缀三处同源) node --test 'test/*.test.js' # core 单测:38/38 通过 node scripts/check.mjs # 静态体检:CHECK OK(14 组断言:A 组 1–9 + 凭据 + 语法 + importmap + 元素注册 + wc 用 Lit) node src/dev/serve.mjs --port 4173 & # 本地:History 回退 200 + /parse 同源代理 node scripts/verify-modules.mjs http://127.0.0.1:4173/ # MODULE GRAPH OK node scripts/verify-modules.mjs http://127.0.0.1:4174/kangshifu/dev/inno-board/ # 子路径:MODULE GRAPH OK node scripts/verify-render.mjs # jsdom 渲染冒烟:RENDER SMOKE OK(含写库闭环) CHROME_PATH= node scripts/verify-browser.mjs # 真实浏览器:BROWSER VERIFY OK(36/36) ``` ### A. 规范合规 | # | 项 | 怎么验 | 实测结果 | |---|---|---|---| | 1 | 无装饰器 | `check.mjs` 去注释后匹配 `@customElement/@property/@state/...` | ✓ 0 命中 | | 2 | 相对 import 带 `.js` | 扫描 `from './x'`、`import('./x')`,并检查目标文件存在 | ✓ 全部带 `.js` 且存在 | | 3 | 无 `window.KF_*` | grep `src/` | ✓ 0 命中 | | 4 | 无 innerHTML | 去注释后 grep | ✓ 0 命中(全部 Lit 模板) | | 5 | core 不碰 DOM | core 下逐行 grep `window/document/fetch/localStorage/navigator/HTMLElement/CustomEvent`(跳过 import 行) | ✓ 0 命中 | | 6 | 视图不直接 fetch | pages/layout/components 下 grep `fetch(`;services 也只经 `utils/http.js` | ✓ 0 命中,`fetch(` 只出现在 `utils/http.js` | | 7 | base 三处同源 | `config.mjs` 由前缀文件生成 base / importmap / 入口,写完反向自检;`check.mjs --env` 再校一次 | ✓ `base === deploy/prefix/local.txt === importmap 前缀`,14 条映射全部以 base 开头 | | 8 | 无 `@lit-labs/router` | grep src + index.html | ✓ 0 命中;路由自写 `utils/router.js` | | 9 | lit 单实例 | importmap 每包只出现一个版本目录;源码不直引 `lib/vendor`;模块图爬取里 vendor 路径去重 | ✓ 两处都过 | | 10 | 模块图全 200 | `verify-modules.mjs`:index → 入口 → 递归静态 import(含动态 import 字面量)→ 裸标识符按 importmap 解析(含 `/` 前缀映射)→ 逐个请求断言 200 + `text/javascript`;再断言深链接 6 条 200 | ✓ 根部署:46 个模块全部 200;子路径 `/kangshifu/dev/inno-board/`:同样 46 个全部 200;深链接 6 条(含不存在路径)全部 200 且无重定向 | ### B. 功能 真实浏览器(Chromium 156 headless,1440×960)逐项实测,截图在 `docs/screenshots/`。 | # | 项 | 怎么触发的 | 实测输出 | |---|---|---|---| | 11 | 真实取数 | 页面直连 Parse(本地经 `/parse` 同源代理)| 26 成果 / 9 机会卡 / 11 成员 / 4 归纳 / 4 原材料;卡片数、成员数、卡住人数均来自库,与 core 计算一致(见 12 的一致性断言) | | 12 | 6 维筛选叠加 | `/pool` 点「候选」→「S 级」→「拌面」;`/ledger` 六行筛选条(优先级/品类/阶段/人/层/状态) | 页面结果与 `core.applyFilter` 结果逐 id 相等(测试从页面取 DOM、从 core 取期望值比较);分面计数受其他维度约束(0 条的置灰) | | 13 | URL 状态保留、刷新不丢 | 筛选后读 URL → `page.reload()` | URL = `/pool?status=candidate&priority=S&category=拌面&sort=score`;刷新后按钮仍 `aria-pressed=true`,列表 id 集合不变 | | 14 | 深链接直接访问 | 直接打开 `/pool/`、`/report/`、`/ledger?focus=…`、`/stages`、不存在路径;子路径部署同一套 | 抽屉直接打开正确卡片;台账高亮行;不存在路径落「页面不存在」;**子路径** `/kangshifu/dev/inno-board/` 下 46 个模块 200、深链接 6 条 200 | | 15 | 卡片五要素 | `/pool` 读每张卡的文本与数据 | 9 张卡的优先级/品类/阶段、名字、一句话、评分/证据/引用、决策行、来源行全部齐全(断言:每张卡都有这些字段且文本里有「决策:」与「N 人评分 · 平均」) | | 16 | 谁 + Agent + 方式 + 时间 | 同上,断言四要素非空且文本里出现 | 例:`张伟 · 康师傅产品创新助手 · 会话上传 · 10月29日 08:32`;3 个 Agent(主分身 / 品类调研 / 概念设计)都出现;缺字段的卡显示「未记录」而不是编造 | | 17 | 多人评分显示平均 | 打开有一张 6 人评分的卡 | 大字 `81.3 /100 平均`,明细:李娜 86、吴敏 90、周涛 84、评价 Agent 88、赵磊 78、郑凯 62;人工平均 80 · Agent 平均 88 · 分歧 28 | | 18 | 改状态可持久 | 抽屉里点「已立项」→ 刷新 → 重新拉库 | 刷新后仍是 `approved`,决策 `by=张伟`,决策记录新增一条;seed 二次执行不覆盖人工改过的状态 | | 19 | 排序切换顺序真实变化 | `/pool` 点「综合评分」 | 优先级序 `[neS5…, qVpN…, ws5X…, …]` → 评分序 `[neS5…, 8fmk…, qVpN…, …]`,顺序不同且平均分单调不增;URL 写入 `?sort=score` | | 20 | 首页三视角 + 痛点对照 | `/`,顶栏选「张伟」 | 三视角齐全;「给你的」显示 `张伟,你已提交 3 份成果`、`还差 4 个阶段没交:S2、S3、S4、S5`;池子视角 9 张卡 + 状态分布 + 最新 3 张卡片形态 + 最高分;现场看板 5×11 矩阵 + 点名 `何雨 G3 · 停在 S4 · 会话 13 条 · 31 小时没动静`;五大痛点 5 行对照 | | 21 | 五阶段板块 | `/stages` | 5 个板块,各显示份数 / 产出人数 / 引用 / 机会卡数 + 最新 4 条(带作者、方式、Agent、时间)+ 谁产的;S3=5 份成果、4 张机会卡 | | 22 | 证据链双向、点名到人 | 报告页点结论旁「看证据链」→ 抽屉;「在台账中高亮」;台账点成果行 → 反查;「打开证据链」 | 抽屉:`4 人独立提到 / 4 条原文 / 3 条支撑成果`,列出 `夜宵场景低卡高蛋白拌面(张伟)`、`办公室3分钟速食拌面(李娜)`、`学生党实惠大份拌面(王芳)`,原文带平台·点赞·作者(抖音评论·点赞 412·夜宵党_312…);跳台账后对应行高亮;反查:成果 → 「被这些结论引用」逐条列出结论原文与报告名 + 被哪些归纳引用 + 出现在哪张机会卡 | 报告页(保留 v3 能力):吸顶章节导航 + 滚动高亮 + 点章节写回 `?ch=`(刷新还原)、每章「这章在回答:」、数据管线 `2,727 → 1,190 → 730,过滤率 73%`、KPI、交叉分析表、结论块、附录「结论 × 证据」总表 —— 以上均由浏览器脚本断言。 ### C. 红线 | 项 | 结果 | |---|---| | 前端无凭据 | `check.mjs` 按 `.credentials` 里每个真实值逐条比对 `src/` 与 `index.html`:**0 泄露**;`masterKey` 字符串 0 命中;源码里的长随机串白名单校验通过 | | 控制台无报错 | 真实浏览器全程监听 `console.error` + `pageerror`:**0 条**(网络断开的 ERR 与 favicon 除外) | | 对比度 ≥ 4.5:1 | 浏览器脚本在真实渲染后的 DOM 上逐个文字节点取 `getComputedStyle` 颜色,沿 shadow 宿主向上合成背景色后计算 WCAG 对比度:**460 个可量化节点,最差 6.28:1**。2 个未量化:品牌方块「康」与头像「伟」——白字压在多色渐变上,不可逐像素取色,属装饰层 | --- ## 5. 这一版没做什么(明确列出) 1. **线上部署未执行**:`scripts/deploy.sh` 写好了(同源前缀校验 → 体检 → 模块图 → 先删后传 index.html → 带 `no-cache` → 上传后复验),但本机没有 OBS 凭据 / `obsutil`,**未上传到 kangshifu.fmode.cn**。因此「线上模块图 + 线上深链接刷新」**未验证**;子路径部署是在本地用同前缀结构(`/kangshifu/dev/inno-board/`)+ 404 边界模拟验证的。 2. **CDN 回退规则**:需要 CDN 侧把「非静态资源路径」200 返回 index.html;本版用 `src/dev/serve.mjs` 模拟了这条规则,线上是否配置**未验证**。 3. **@lit/context 未接入**(理由见第 2 节)。 4. **原材料库页面未做**:工作台痛点表里「原材料库」的一行链到台账 `?layer=raw`(数据里有 4 条原材料),没有单独的原材料浏览页。按第 9 节优先级,把厚度放在池子 / 工作台 / 报告 / 台账上。 5. **搜索最多 1000 条/表**:Parse 单次上限 1000,超过会分页(最多 50 页)并在 UI 标「数据被截断」;当前数据量远小于上限。 6. **多人同时写同一条卡**:没有冲突检测(后写覆盖)。客户当前规模(30–40 人、改状态操作稀疏)可接受,未来可按 `updatedAt` 做乐观锁。 7. **历史测试残留的数据未清理**:如「夜宵低卡拌面(与 seed-opp-1 重复)」及其 decisionLog 里旧验收脚本写的条目;本版只做展示纠正(占位一句话不显示),没有删客户库里的数据。 --- ## 6. 示例数据 - 所有示例卡都带 `seedKey`,卡片右上角显示「示例」徽标,**与学员真实提交可区分** - `node scripts/seed.mjs` **幂等**:已存在的卡只补空字段和缺失的示例评分,**不覆盖**任何人改过的状态 / 决策;旧版 `seed-opp-*` 按标题认领,不会产生重复卡 - `node scripts/seed.mjs --check`:校验每张示例卡的四要素齐全(`✓ SEED CHECK OK(6 张)`) - 凭据只从环境变量 `PARSE_MASTER_KEY` 读,只用于脚本,不进前端 ## 7. 本地运行 ```bash node --version # ≥ 20(用到 fetch / node:test) node scripts/vendor.mjs # 首次:把 lib/vendor 补齐(已有则可跳过) node scripts/config.mjs local # 生成根部署的 index.html node src/dev/serve.mjs --port 4173 # 打开 http://127.0.0.1:4173/ # 子路径部署预览: node scripts/config.mjs local-sub node src/dev/serve.mjs --port 4174 --mount /kangshifu/dev/inno-board # 打开 http://127.0.0.1:4174/kangshifu/dev/inno-board/ ``` 生产:`./scripts/deploy.sh prod`(需 `OBS_BUCKET`;上传后设置 `CDN_BASE` 复验)。 --- ## 8. v6:展示逻辑(这一轮改了什么、怎么验的) 客户原话只落三件事:**「每个阶段的产出在里面我们可以看到」**、**「竞品分析、品类分析……单独分类用页面展示」**、**「展示的逻辑能不能够做的比较好」**;明确说流程推进不是重点、数据由别人灌。所以 v6 的全部改动都在这三件事上,机会卡流转 / 写库 / 状态机一行没动。 ### 8.1 路由表(v6) | 路径 | 视图 | 展示什么 | |---|---|---| | `/reports` | `` 报告中心 | 五类各几份(条形长度 = 占比、近 7 天新增、几人做过、几份可看全文)+ 每类最新两份 + 跨类型最新 6 份 + 阶段 × 类型总账 + 未归类如实列出 | | `/reports/category` | `` 品类分析 | 赛道分布(每赛道份数 / 竞品篇数 / 参与人 / 平均证据)+ 阶段 × 份数(竞争密度)+ 卡片流 | | `/reports/competitor` | 同上 type=competitor 竞品分析 | 对比矩阵(对位 × 阶段 / 原文)+ 空档标记 + 卡片流 | | `/reports/voc` | 同上 type=voc VOC / 用户洞察 | 原文摘录(平台 · 点赞 · 作者 · 出处)+ 原文量排行 + 卡片流 | | `/reports/pain` | 同上 type=pain 痛点清单 | 频次 × 证据图表(同一条痛点按报告结论归并)+ 对应机会卡(立项的标出)+ 卡片流 | | `/reports/concept` | 同上 type=concept 概念提案 | 方案 × 关联机会的多人平均分 + 卡片流(当前库里 0 条,空态按「将来会出现什么」展示) | | `/stages` | `` 五阶段产出 | 阶段总览条 + 每阶段**按类型分组**的卡片流 + 谁产的 + 全部 / 只看我的 / 只看有证据 | | `/report/:id` | `` 报告阅读 | 执行摘要 → 章节速览(每章一句,点跳)+ 吸顶章节导航 + 章节内结论 / 表格 / 矩阵 + 附录「结论 × 证据」 | | `/report` | — | 兼容旧链接,落到报告中心 | 每个类型页都可加 `?stage=S1`(按阶段二次筛选 —— 报告类型与阶段不是一一对应)、`?ev=1`(只看有证据的)、`?scope=mine`(只看我的,全选中的人);全部写进 URL,刷新不丢。 **每条产出的通用件**(卡片上一行齐):谁上传的 · 哪个 Agent · 什么方式 · 什么时候 · 属于哪个阶段 · 证据强度;能看正文的给「阅读全文」,只有登记信息的如实写「仅登记信息」。 ### 8.2 展示逻辑的几条硬规则(都在 core,单测覆盖) - **归类**:`core/library/types.js` 的 `REPORT_TYPES` 定义五类;`core/library/reports.js` 的 `TYPE_FOR_ARTIFACT` 把 `artifactType` 映射到类型页(`opportunity_card` → 竞品页,且在竞品 / VOC 页同时作为「证据」出现)。映射不到的进报告中心「未归类」表,不隐藏。 - **指标对齐**:每张卡固定 4 个指标,列名由类型页统一定义(`metricLabels`),取不到的显示「—」而**不塌列**——同一行卡片能直接横着比。 - **数字有参照**:每类有「占全部 %」「近 7 天新增 / 无新增」「N 人做过」「x/y 可看全文」;卡片上「只看有证据的 N」「N / 总数份」实时跟着筛选变。 - **关键结论来自算得出来的地方**:报告执行摘要 > 报告副标题 > 正文开头 > 机会卡一句话 > 原文摘录,都没有就写「正文未入知识库」——**不生成一句话**。 - **有证据 = 原文 / 佐证**:成果按 `verified / supported` 算,机会卡按三档 `高 / 中` 算;模型推测不算(`core.hasEvidence`,类型页与阶段页共用)。 - **痛点归并**:同一份报告结论(evidence 锚点)被多人整理成多份 pain_list 时合并为一条,频次 = 份数;严重度没有字段就是「待标注」,不猜。 - **空态说人话**:每个类型的空态给四行——会出现什么 / 谁来填(阶段 + Agent)/ 对应字段 / 填完长什么样 + 现在能做什么。全应用 `` 的默认标题也从「暂无数据」改成「这里将来会出现内容」。 ### 8.3 视觉:液态玻璃(任务书第 3 节,数值照抄) | 项 | 做法 | 实测 | |---|---|---| | 3.1 玻璃基元 | 卡片 `.kf-card` 白面 `rgba(255,255,255,.60 → .55)` + `backdrop-filter: blur(24px) saturate(180%)` | computed `backdrop-filter=blur(24px) saturate(1.8)`;背景里无实底 `background-color: rgba(0,0,0,0)` | | 3.2 发丝边 | `border: 1px solid transparent` + `padding-box` 白面 + `border-box` 135° 上亮下暗渐变 + `inset 0 1px 0 rgba(255,255,255,.7)` | computed:`border=1px solid rgba(0,0,0,0)`,background-image 第三层为 `linear-gradient(135deg, rgba(255,255,255,.92) 0%, rgba(255,255,255,.22) 40%, rgba(10,20,48,.1) 100%)` | | 3.3 三层纵纵深 | 底层光场(4 个色斑,单层 alpha ≤ .14,慢移)→ 面板 .46 → 卡片 .55–.60 → 抽屉 .85–.94 | 导航面板实测 alpha 0.46;卡片 0.60 → 0.55;抽屉 0.90 | | 3.4 hover | `translateY(-2px)` + 高光条横扫(background-position)+ 阴影加深 | hover 后 `matrix(…, 0, -2)`;高光 `120% → -13%` | | 3.5 对比度 | 文字全部实色;按「玻璃后是最深光场」算最坏合成色 | 3 个页共 1,856 个文字节点,**最差 4.63:1**(类型页)/ 4.88:1(阶段页 / 报告中心) | **v5 被否决的根因(修掉的两个 bug)**: 1. 卡片当时被组件里的实底 `rgba(255,255,255,.88)` 覆盖 → 现在只改 `--kf-glass-alpha`,外层仍是半透明。 2. 共享表的 `.kf-glass` 用 `var(--kf-glass-alpha)` 写进 `linear-gradient` —— Chrome 实测整条声明失效(导航背景 `bg=none`,这就是「写了 blur 却看不出玻璃」的另一个原因)→ 改成字面量 alpha,逐条实测生效。 ### 8.4 16 项验收:实测结果 复现命令(全部实跑过,2026-10-29): ```bash node scripts/check.mjs # CHECK OK(14 组) node --test 'test/*.test.js' # 46/46(v5 的 38 项全过 + v6 新增 8 项) node scripts/verify-modules.mjs http://127.0.0.1:4173/ # MODULE GRAPH OK(47 模块 + 深链接) node src/dev/fixture-server.mjs --port 4176 --dir /tmp/kfcache & # 可选:快照回放(数据为真实 Parse 的一次原样抓取) node scripts/verify-render.mjs --url http://127.0.0.1:4176 # RENDER SMOKE OK(jsdom,27 项) CHROME_PATH= node scripts/verify-glass.mjs # GLASS VERIFY OK(8 项,第 10–15 项) CHROME_PATH= node scripts/verify-browser.mjs # BROWSER VERIFY OK(48 项,含 v5 的 36 项 + v6 的 12 项) ``` | # | 项 | 怎么验(实测) | 结果 | |---|---|---|---| | 1 | 报告中心各类型数量 + 最新 | `/reports` 读 5 张类型卡的份数 | 品类 1 · 竞品 25 · VOC 10 · 痛点 9 · 概念 0(合计 45,未归类 0) | | 2 | 五个类型页独立访问、刷新不丢 | 逐页 `goto` → `reload`,断网模式下用 localStorage 快照重建 | 5/5:视图 `VIEW-REPORT-TYPE`,卡片数 = 库内原始行按同一映射算出的数(1 / 26 / 12 / 10 / 1),F5 后一致 | | 3 | 按阶段二次筛选 | 竞品页点 `S1` | 25 → 4 份;URL 带 `?stage=S1`;F5 后仍 4(`?stage=S4&ev=1` 组合后 4 份,见 `v6-12`) | | 4 | 来源四要素 | 读卡片文本 | 例:`张伟 · 康师傅产品创新助手 · Skill 上传 · 5 小时前` | | 5 | 阶段页全部产出按类型分组 | `/stages` 读 5 个阶段板块 | S1 6(竞品 3 / 痛点 2 / 机会卡 1)、S2 8、S3 9、S4 6、S5 6,合计 35 | | 6 | 只看我的 / 只看有证据 | 点 `只看有证据的`(证据等级纠正为「成果原始等级 / 机会卡高中」后) | 35 → 26,关掉恢复 35;卡片上「有证据 N」随之变化 | | 7 | 卡片横向比较 | 见 `v6-05b-pain-cards.png`:每张卡「频次 / 严重度 / 解决状态 / 阶段」四列同宽同序 | 指标缺失显示「—」,不塌列 | | 8 | 空态说清将来会出现什么 | `/reports/concept`(库里 0 条) | 「会出现 / 谁来填 / 对应字段 / 填完后 / 现在能做」五行 + 结论面占位说明 | | 9 | 长报告章节导航 + 结论摘要 | `/report/bCld0Iy77e` | 执行摘要 4 条 → 章节速览 6 条(每章一句,点跳)→ 吸顶导航 6 章(滚动高亮、`?ch=` 写回) | | 10 | 卡片半透明 + blur 生效 | computed style | `background-image` 白层 alpha 0.60/0.55,`background-color` 透明,`backdrop-filter: blur(24px) saturate(1.8)` | | 11 | 边框是渐变 | computed style | `border: 1px solid rgba(0,0,0,0)` + 135° 三色渐变作为边框层 | | 12 | 背景有光场 | 同一块区域移除卡片前后像素对比 | 背景 RGB 范围 [244,234,217](有色斑);玻璃卡片平均亮度 239 vs 背景 236 | | 13 | 三层纵深 | 面板 / 卡片 / 抽屉的 alpha | 0.46 / 0.60–0.55 / 0.85–0.94(截图 `11-glass-layers-drawer.png`) | | 14 | hover 位移 + 高光 | hover 前后 computed | `translateY(-2px)`;高光 `background-position 120% → -13%`;阴影变化 | | 15 | 对比度 ≥ 4.5:1 | verify-glass 逐文字节点 | 1,856 个节点,**最差 4.63:1**;`verify-browser` 另量 460 个节点最差 7.31:1(旧量法按浅色底) | | 16 | 截图 6 张 | `docs/screenshots/` | `v6-01` 中心 · `v6-02..06` 五类 · `v6-07/08` 阶段页(全部 / S1)· `v6-10` 报告页 · 另附 `11/12/13`(三层 / hover 前后) | 截图清单:`docs/screenshots/v6-01-reports-center.png`、`v6-02-type-category.png`、`v6-03-type-competitor.png`、`v6-03b-type-competitor-S1.png`、`v6-04-type-voc.png`、`v6-05-type-pain.png`、`v6-05b-pain-cards.png`(卡片横向比较)、`v6-06-type-concept.png`(空态)、`v6-07-stages.png`、`v6-08-stages-S1.png`、`v6-09-stages-evidence.png`、`v6-10-report.png`、`v6-11-concept-empty-plan.png`、`v6-12-type-filtered.png`、`11-glass-layers-drawer.png`、`12-hover-before.png`、`13-hover-after.png`。 ### 8.5 v6 没做什么(明确列出) 1. **不造业务数据**:类型页各块按「库里有什么显示什么」;`概念提案` 页当前 0 条,显示的是空态说明书,不是假卡片。品类页当前只有 1 条 KfSource,没有「赛道 / SKU / 竞争密度」字段,所以赛道显示为「未归类」——这是如实显示,等别人按 `KfSource.payload` 灌进来就会按赛道分组。 2. **流程推进没动**:机会卡状态、评分、决策记录、写库逻辑与 v5 完全一致。 3. **VOC 的情感 / 主题 / 人群切分没有自己算**:库里的 voc_report 没有这些字段,代码不按标题猜;字段到了就显示,没到显示「未标注」。同理「严重度」没有字段时显示「待标注」。 4. **类型归属是单值**:一份 `opportunity_card` 只归竞品页,VOC 页里它作为「证据补充」出现;如果客户想把同一份卡同时归到两个类型页,需要在 `REPORT_TYPES` 里改成多值映射(一行改动)。 5. **线上部署仍未执行**(与 v5 相同):本机没有 OBS 凭据;`scripts/deploy.sh` 未运行。 6. **jsdom 渲染冒烟在真实 Parse 下偶发卡住**:后端抖动时 jsdom 的网络会话偶尔不回调;已提供 `src/dev/fixture-server.mjs` + `--url` 快照回放模式(数据来自真实 Parse 的一次原样抓取,不做任何加工,也不生成数据),验收用的就是这一模式。 7. **「只看我的」在没选「我是谁」时不按人筛**(全量显示并在计数旁写「未选人,未按人筛」),不假装筛过。