|
|
5 dni temu | |
|---|---|---|
| .. | ||
| css | 5 dni temu | |
| docs | 5 dni temu | |
| js | 5 dni temu | |
| tools | 5 dni temu | |
| README.md | 5 dni temu | |
| TASK-dashboard-v1.md | 5 dni temu | |
| TASK-dashboard-v2.md | 5 dni temu | |
| TASK-dashboard-v3.md | 5 dni temu | |
| TASK-dashboard-v4.md | 5 dni temu | |
| index.html | 5 dni temu | |
第一版跑通了取数与筛选,但被判定「太普通、没有达到系统性看板的要求」。 这一版在保留 v1 已通过能力的前提下,补上了分析报告层,并把它和台账层打通: 从一句结论,能一路追到原始成果。
入口:index.html(纯静态,浏览器直接打开或任意静态服务器托管均可;file:// 下也已实测可读)。
python3 -m http.server 8765 # 然后打开 http://127.0.0.1:8765/
v1 被打回的直接原因是「实心白卡片 + 靠阴影分开」。这一版把视觉主线重新做成液态玻璃:
| 要求 | 落地 |
|---|---|
| 半透明磨砂层 | 面板/卡片 background: rgba(255,255,255,.26~.58) + backdrop-filter: blur(20~32px) saturate(175~190%) |
| 玻璃为什么"透" | 页面底层有真实光场 .ambient:四团色光(蓝/紫/薄荷/橙)+ 一道从左上斜下的天光,极缓漂移(46–64s),再叠一层 SVG 湍流噪点。没有底层光,backdrop-filter 无物可模糊,玻璃就成了白纸 |
| 发丝边 | 1px 半透明边;.glass 基元用双层背景做上亮下暗的渐变边(模拟玻璃边缘折光),面板底部另有一条更暗的极淡内边;吸顶的筛选条/目录条上方不圆角、无缝接到玻璃顶栏 |
| 分层光照 | 所有光源从左上:每块玻璃顶部 inset 0 1px 0 rgba(255,255,255,.7) 内高光 + 左上角径向光斑放在容器层级;阴影全部"往下偏右、极浅"(0 10px 30px -16px rgba(32,48,90,.14)),没有新拟物那种大阴影凸起/凹陷 |
| 纵深 | 三层透明度:L1 面板最透 → L2 卡片/指标稍实 → L3 顶栏/侧栏/抽屉更实;关键数字用强调色微光(text-shadow 柔光)而不是底色块 |
| 铁律:不糊数据 | 正文/数字一律落在 --ink(对比度 ≈ 14:1)与 --ink-2(≈ 8:1)上;说明文字 --ink-3(≈ 5.2:1);万字级报告正文放在更实的"纸"衬底(rgba(255,255,255,.62~.78))。模糊只存在于装饰层 |
配色:客户说"配色无所谓",这里选了浅色(与 v1 的"浅色底"方向一致、更适合长文阅读),并明确定义了对比度层级。
新增 报告层:
#/reportIndex:每份报告一张卡片(几章、几条结论、几条支撑成果、几条原文)。#/report/<成果id>:
?ch= 会写回地址,刷新可还原)5 大场景 × 现有产品 × 未满足需求)、二级编号(1.1 / 1.2 …)归纳(沉淀层) ──展开──▶ 归纳自哪几份报告 ──▶ 报告
▲
│
报告结论 ──证据锚点──▶ 支撑成果(过程层) ──▶ 台账定位(?focus=)
▲
└──────── 成果抽屉:「作为证据,被这些结论引用」 ◀── 成果
台账行标题「报告」标记 ──▶ 报告 ──▶ 顶部「成果台账」──▶ 台账
?focus= 高亮并置顶那一行,给出"正在定位"的说明和"清除定位"真实取数(Parse REST,只带 appId)· 6 维筛选(优先级/阶段/品类/证据/层/成员)+ 组合叠加 · 筛选写 URL 刷新保留 · hash 路由直链落位 · 四层区分 · 五阶段人数分布 · 15 秒轮询 · 搜索。
KfArtifact.report(而不是新建 KfReport)结论:挂在 KfArtifact 上新增 report 字段(Object/JSON),不新建 class。 理由:
ownerName/ownerSession)/ 同一阶段 / 同一层 / 同一项目。另建 class 就得复制或引用这套出身,而"没有出身的记录不许进沉淀层"的规则就会有两处实现,迟早不一致。KfInsight。一条报告被回灌时,是它里面的结论被写成 KfInsight,并引用报告支撑的成果——这正好是现有规则。derivedFrom 一直都是 KfArtifact.objectId;报告的 evidence 里 derivedFrom 也指向同一批 id,不需要跨表映射。KfReport 并加 artifact 指针;届时 report 字段可以原样迁过去(结构不变)。KfArtifact.report){
"version": 1,
"title": "…", "subtitle": "…", "method": "…", "sources": "…", "structure": "…",
"pipeline": [{"label":"原始 VOC 采集","value":2727}, {"label":"去噪去重","value":1190}, {"label":"精准 VOC","value":730}],
"filterRate": "73%",
"generatedAt": "ISO 时间",
"kpis": [{"value":2727,"unit":"条","label":"原始 VOC","note":"一行解读"}],
"executive": ["四句话"],
"chapters": [
{"id":"ch1","no":1,"stage":"S1","en":"MARKET FUNNEL","title":"全景漏斗","subtitle":"…",
"lead":"这一章在回答什么问题",
"sections":[
{"id":"s1-1","no":"1.1","title":"…","kind":"funnel","data":[…]},
{"id":"s2-1","no":"2.1","title":"…","kind":"matrix","data":{"cols":[…],"rows":[…]}},
{"id":"s2-2","no":"2.2","title":"…","kind":"findings","data":[{"id":"f-1","conclusion":"…","attitude":"…","evidence":"c-texture"}]}
]}
],
"evidence": {
"c-texture": {"claim":"…","ownerCount":4,
"derivedFrom":["<KfArtifact.objectId>", …], // 支撑它的过程层成果
"quotes":[{"artId":"<objectId|null>","author":"…","text":"…","ref":"…","likes":412}]}
}
}
chapters[].stage 只能取 S1–S5(沿用五阶段,没有发明阶段)。示例报告 6 章对应:Ch1 S1、Ch2/Ch3 S2、Ch4 S4、Ch5/Ch6 S5。kind 是渲染器的键:para / longpara / funnel / matrix / findings / table / two / fit / score / ladder / statement / milestones。遇到未知 kind 会原样列出键值并标黄,不会静默吞内容。esc()。避免把 HTML 塞进库里(注入、样式漂移、分身不好读)。将来要做富文本,再加 format:"md",由渲染端解析。derivedFrom 指向的成果是"事实来源";quotes 是原文片段。quotes[].artId 为空表示"原文来自外部采集、没有对应成果记录",抽屉里单独列出,不冒充成果。KfInsight.citedInsightIds(Array,可选):这条归纳对应报告里的哪几条结论。没有时前端用"归纳支撑成果 ∩ 报告证据 derivedFrom"取交集推断。KfInsight.derivedFrom / derivedArtifacts:不变。| 层 | layer |
谁写 | 页面上的样子 |
|---|---|---|---|
| 原材料 | raw |
调研 Agent | 原材料页;每条必须带出处 |
| 过程 | process |
学员 + 分身 | 台账主体;报告挂在过程层成果上 |
| 沉淀 | sediment |
归纳 + 证据筛选 | 归纳页;必须能回溯到支撑成果 |
| 记录 | record |
自动落入 | 记录页;只留档,也保留在台账里以便看出"排除了什么" |
同一个库,靠字段区分。前端所有列表都能看到层标。
index.html 入口
css/tokens.css 设计变量(光场色、三层玻璃、发丝边、光照、文字对比度层级)
css/base.css 光场/噪点、侧栏顶栏、应用骨架、玻璃基元
css/components.css 指标、筛选、表格、归纳、抽屉、空态、骨架
css/report.css 报告阅读页与目录页
css/responsive.css 1280 / 1024 / 768
js/config.js ★ 唯一需要改数据源的地方
js/taxonomy.js 阶段/层/优先级/证据枚举
js/model.js 原始行 → 页面对象(含 report 规整、容错)
js/query.js 筛选(纯函数)
js/router.js hash 路由(report / reportIndex / focus / ev / ch / back)
js/views/*.js 各视图(report.js = 报告阅读、目录、证据链抽屉、滚动高亮)
js/app.js 入口:取数 → 建模 → 按 hash 渲染 → 事件委托 → 轮询
tools/seed_report.py 为 1 条成果写入示例报告(可重复执行)
tools/seed_long_report.py 把示例报告扩到约 2 万字(压测第 13 项;再跑 seed_report.py 可还原)
tools/seed_insight_link.py 把 1 条归纳与示例报告打通(让"归纳自哪几份报告"有样本)
tools/enrich.py / reset_demo.py / setup_cloud.py v1 遗留的数据准备脚本
docs/verify.js 浏览器点击验收脚本(16 项 + 3 项视觉核验)
docs/smoke.js jsdom 冒烟(快速看各页能否渲染)
tools/check_frontend_secrets.sh 红线自检:按 .js/.html/.css 与按"文本内容"两种口径扫前端
docs/verify-run.log 最后一次验收的完整输出
docs/screenshots/ 截图
PARSE_MASTER_KEY,仅限本地脚本)python3 tools/seed_report.py # 写入示例报告(康师傅拌面 · 江中骨架,6 章)
python3 tools/seed_insight_link.py # 打通 1 条归纳 → 报告
python3 tools/seed_long_report.py # 可选:扩到 ~2 万字做压测
masterKey 只从环境变量读取,不进入任何前端文件。红线自检:./tools/check_frontend_secrets.sh。
验证方式:真实 Chromium(headless Chrome 153)+ puppeteer-core,在 http://127.0.0.1:8765 上真实点击/滚动/断网,脚本 docs/verify.js,完整输出 docs/verify-run.log。最后一次运行 19/19 PASS(16 项 + 3 项视觉核验)。屏幕上还逐张看过了 docs/screenshots/ 里的截图。
| # | 项 | 结论 | 怎么验的 / 结果 |
|---|---|---|---|
| 1 | 真实取数,条数与库一致 | ✅ 已验证 | 浏览器内以公开读方式直连 Parse 取 5 张表,与导航计数比对:session 10 / 归纳 4 / 成果 26 / 项目 10 / 原材料 4,全部一致 |
| 2 | 单选筛选生效 | ✅ 已验证 | 在台账页点「优先级 S」:库中 S 级成果 7 条 → 页面 7 行、提示 7 条(未筛 26 行) |
| 3 | 组合筛选(交集正确) | ✅ 已验证 | 再点「S1」:独立计算库中 S∩S1 = 0 → 页面 0 条并显示空态;已选条件回显「优先级 S / S1」。注意:验收脚本只断言了空交集;非空的 S∩S3 = 4 条是我在调试脚本里单独点过、人工核对与库一致的,不在 16 项自动断言里 |
| 4 | 筛选状态写入 URL,刷新保留 | ✅ 已验证 | #/artifacts?priority=S&stage=S1:新标签打开结果一致、选中 chip 保留;页面 reload 后仍一致 |
| 5 | 搜索命中 | ✅ 已验证 | 搜「张伟」:页面 4 条 = 库中 4 条,4 行都命中;其中 1 行成员是陈静(标题写着"与张伟重复"),属按标题/成员/项目联合搜索的预期。清空后恢复 26 行 |
| 6 | 四层区分可见 | ✅ 已验证 | 台账层列出现 process / sediment / record 三种层标;总览「分层」区块 4 个层标(raw/process/sediment/record)。总览「成果总数」卡把被归纳引用的成果改计入沉淀层,所以 raw=0;原文要看原材料页(KfSource,4 条,其中 3 条是早期探针占位) |
| 7 | 五阶段人数分布正确 | ✅ 已验证 | 库中 10 条会话恰好每阶段 2 人,但页面不是平均分配出来的:页面按「会话阶段 + 成果阶段取最远者」统计,结果 2/2/2/2/2,与独立按 KfSession 统计一致,合计 10 |
| 8 | 前端无凭据泄露(红线) | ✅ 已验证 | ① 浏览器内读取全部 24 个前端文件(index + 13 个 js + 5 个 css ≈ 210 KB)匹配 masterKey / X-Parse-Master-Key / cc84… / PARSE_MASTER_KEY:0 处;② 对交付目录的 index.html / js / css grep -rniE:无;③ 另用环境变量的实际值全文搜索整个交付目录(含 tools、docs、README):无命中 |
| 9 | 成果详情页是报告形态 | ✅ 已验证 | 在台账真实点击带「报告」标记的行标题 → 报告页:目录 6 章、管线 3 步、章首导语 6、KPI 4、表格 9、结论块 6、证据锚点 18 |
| 10 | 章节导航跳转 + 滚动高亮 | ✅ 已验证 | 初始高亮 ch1;点目录「4 产品适配」→ 滚动收敛到第 4 章顶部距视口 96px、高亮切到 ch4;合成滚动到第 6 章/第 2 章,高亮分别跟到 ch6 / ch2 |
| 11 | 报告结论反查支撑成果 | ✅ 已验证 | 点 Ch2 结论旁「看原文」→ 抽屉列出 3 条支撑成果(张伟/李娜/王芳,均可在库中找到)、3 条原文,提示"证据链完整";每条成果有跳转 |
| 12 | 台账→报告→台账往返不乱 | ✅ 已验证 | 证据抽屉点成果 → 台账 26 行、定位行高亮、抽屉已关、导航停在「成果」;清除定位后 URL 干净;再进同一报告在顶部;报告顶部「成果台账」→ 台账 26 行 |
| 13 | 长报告可读(万字级) | ✅ 已验证 | 库中真实版本(seed_long_report.py 生成)= 6 章、可见正文 21,440 字、DOM 1,196 节点、页高 24,773px。平滑滚到 33% → 高亮 ch2 → 跳底 → 高亮 ch6 且到底 → 跳第 4 章 → ch4;无横向溢出 |
| 14 | 1280 / 1024 / 768 三档不错乱 | ✅ 已验证 | 三档下报告页与台账页:横向溢出 0px、无越界元素;正文始终 15px;KPI 4 列/2 列/2 列;目录条保持单行。768 的截图人工看过 |
| 15 | 空态 / 加载态 / 错误态有设计 | ✅ 已验证 | 空态:搜不存在的词 →「当前条件下没有成果」+ 逐步放宽提示;报告目录 →「没有匹配的报告」。加载态:人为把 Parse 延迟 1.5s + reload → 骨架 38 块。错误态:人为断开 Parse →「报告没有加载出来」+ 原因 + 重试(侧栏红点「读取失败」);恢复网络点重试 → 报告正常渲染 |
| 16 | 无 console 报错(有一处保留说明) | ✅ 已验证 | 走完 10 个页面:pageerror 0;console.error 共 5 条,全部来自第 15 项人为断网测试的 net::ERR_FAILED,非网络类错误 0 条。正常使用(不断网)时控制台无报错 |
补充(不计入 16 项,说明闭环的另外两条路):
| 项 | 结论 | 说明 |
|---|---|---|
| 视觉核验 V1/V2 | ✅ 已验证 | 归纳卡「归纳自 1 份报告(2 条结论)」展开 → 出现报告链接 → 点击进入该报告 |
| 视觉核验 V3 | ✅ 已验证 | 成果抽屉「作为证据,被这些结论引用 1:『太软没嚼劲』… 来自报告…」 |
file:// 直接打开 |
✅ 已验证 | 用 Chrome 以 file:///…/index.html#/report/… 打开,报告正常渲染、0 报错 |
明确未验证(不做"已实现"的模糊表述):
backdrop-filter 性能与观感都会不同)未验证。backdrop-filter 已加 -webkit- 前缀,并有 @supports not 降级(不支持时面板回到高亮度实底);但Safari 上的玻璃观感、Firefox 上的降级效果未实测。--ink 对最亮玻璃 ≈14:1、--ink-2 ≈8:1、--ink-3 ≈5.2:1,均按"玻璃背景与最亮光斑叠加"的最坏情况估算;没有逐元素跑 axe / 取色器扫描。极小字号(10.5–11px)的浅色元信息(如 gauge-l、r-toc-s)属于装饰性文字,没有保证 4.5:1。tools/seed_report.py 直接写库。citedInsightIds + 交集推断,没有自动生成归纳。没做到的 / 已知限制:
kind 之外的富文本(表格合并、图片、脚注)暂不支持;未知 kind 会原样陈列而不是报错。c-texture)。不同报告可以用相同 id,前端按"第一份含该结论的报告"打开证据链——目前只有一份报告,不受影响;多报告后建议上传方用 <成果id>/<结论id> 命名。seed_report.py 里逐条列出),不是真实采集到的评论;看板不会把它们伪装成学员成果(无 artId 的原文单独列为"未挂到成果上的原文")。KfSource 是早先的探针占位(sourceType=probe),不是真实原材料,页面如实显示。sources 全为空——报告的证据链因此是"挂到成果 + 原文片段"的组合,而不是"成果自带原文"。这是数据现状,不是前端丢失。环境事件(需告知):验收过程中 Parse 服务端(kangshifu.fmode.cn/parse)出现过约 10 分钟 503 Service Unavailable(连 /parse/health 也 503,且 CORS 预检失败),期间前端正确显示错误态;服务恢复后重跑,全部通过。
chapters[].stage 只允许 S1–S5,不设"第六阶段"。ownerCount 和原文片段是报告作者写进报告的数据;看板能做的是"把它们挂到真实成果上、并检查成果在库里是否还存在",而不是替作者重新统计。抽屉里同时显示"提到人数 / 原文条数 / 支撑成果数 / 库里可追数",可追数与引用数不一致时会标黄提示。citedInsightIds(可选)+ 交集推断;如果客户希望归纳直接引用报告里的结论 id,需要在上传 skill 约定字段(见第 8 节)。report:按第 2 节结构产出;上传时校验:chapters[].stage ∈ S1..S5、evidence[*].derivedFrom 必须是本批成果 id、kind 在渲染器白名单内,否则落记录层并给出原因。KfInsight(带 derivedFrom 与 citedInsightIds),并接"证据够不够格回灌"的判定接口(v1/v2 已留位)。report 是结构化数据,分身可以直接按 kind/evidence 检索;建议为结论加 embedding 字段做语义召回。KfSource.payload 上定稿,不动表结构;报告的 matrix 表可以按新字段配置列。KfReport,artifact 指针回指成果,KfArtifact.report 原样迁移。