从词云想法到内容 Treemap:统计口径、CMS 建模与交互修正
用五张图复盘博客 Treemap:文章计数、Payload 关系模型、数据流、原地探索,以及边框遮挡的修复。
首页热力图下面有些空,我最初想加一个词云,后来改成 bundle size 分析工具那样的 Treemap。真正需要先解决的,却是一个数据问题:一个矩形到底在数什么?
最终上线的是 Category → Article:每篇已发布文章权重为 1,点击分类在原图内展开,点击文章进入详情。下面用五张图复盘这次取舍。
1. 标签次数,不等于文章数量
旧方案按标签使用次数分配面积。假设文章 A 有三个标签,文章 B 只有一个标签,A 就占了四份权重里的三份;读者却容易把它理解为“这里的文章更多”。
图 1:编辑时多打标签,不应让一篇文章在“文章数量”图里占据更多权重。
因此,聚合改为按文章 ID 去重,再按单一 Category 分组。各分类文章数之和 = 唯一已发布文章数。引用的分类缺失或未发布时,文章进入 Uncategorized,不暴露未公开分类的名称。
权重可加总,但标题栏、间距和紧凑导航也占空间,不应拿屏幕像素精确换算百分比。
2. 关系只存一份,图表只保留必要层级
Category 管内容归属,Series 管连载顺序。为了画图而强制每篇文章加入 Series,会让内容组织迁就组件。
下面是当前关系模型,箭头表示文章保存的引用;虚线表示反向查询。
图 2:Post 保存关系,Category 和 Series 通过 Join 查看使用它们的文章。
Payload 的 Join 字段 提供反向视图,不需要保存第二份文章 ID 数组,也不需要编写双向同步 hook。分类里的关键配置只有:
{
name: "posts",
type: "join",
collection: "posts",
on: "category",
}图表叶子改为文章后,Tags 的关系、徽标、查询与发布校验已经不是当前体验的必要条件,因此活跃 Tags collection 被移除,历史数据仍保留用于核验。
当前没有嵌套分类。 Trading → 股票研究 → 猪周期属于后续建模方向;这次上线的层级仍是 Category → Article,Series 不参与图表。
3. CMS 决定数据,UI 只接收树
组件放在 packages/ui,查询和业务适配留在应用层。Mermaid 是设计参考,实际 Treemap 使用 D3 计算矩形、React 渲染。
图 3:UI 不认识 Payload,也不负责查询文章或拼接博客路由。
公开查询显式使用 _status = published、draft: false、overrideAccess: false;每页读取 200 条直到结束,只选择图表需要的字段。不能把 Join 第一页的数组长度当总数。 响应不包含正文或草稿,分类查询也关闭 Join 展开。
发布状态统一使用 Payload 原生草稿机制,适用于 Posts、Pages、Categories、Series 和 Media;Series.progress 则保留为独立的业务进度。Changed 是后台对较新草稿的提示,不是第三个持久化发布状态。
颜色使用现有 chart、card、foreground 等 CSS token。布局与渲染分开的依据可见 D3 Treemap。
4. 点击后,在原位置继续探索
第一版点击区块,会在图表下面新增详情模块。用户点的是矩形,却要向下找结果,探索过程被打断了。现在分类展开和返回都发生在原画布内。
图 4:分类负责展开,叶子负责导航,不再出现图外详情面板。
| 元素 | 当前行为 |
|---|---|
| 分类节点 | 显示名称和文章数,点击原地展开 |
| 文章叶子 | 只显示标题,点击打开文章 |
| 可点击区域 | pointer;允许动效时 hover 放大 1.015 倍 |
| 根分类 | 保留相同交互,不增加特殊分支 |
| 键盘操作 | 支持激活,展开和返回时管理焦点 |
每篇文章的权重都为 1,重复显示“1”只会挤占标题空间,所以叶子隐藏可见数量,布局权重保持不变。
密集数据导致矩形过小时,总览可退回紧凑分类导航,进入分类后用受限的可滚动画布呈现文章。这个回退保证内容可达,不再表达比例面积。
5. 边框消失,是绘制顺序出了问题
截图里,分类顶部与侧边的 border 被标题背景盖住了。加粗边框不能解决遮挡;需要拆开背景、交互区和边框。
图 5:边框绘制在 hover 层之上,但不拦截下面按钮的鼠标事件。
这类问题需要浏览器视觉验收。组件测试能证明回调被触发,却不能证明边框没有被遮住。
最终网站测试套件通过 142 项测试,覆盖计数守恒、重复 ID、展开/返回、文章激活及叶子标题;同时完成类型检查、针对性 lint 和线上视觉检查。Tags 的历史记录与迁移日志保留只读,旧迁移写入路径关闭。
这次最重要的简化,是先把“数什么”定义清楚,再决定需要哪些实体和交互。嵌套矩形是表达手段,不是增加内容层级的理由。
讨论
登录 GitHub 即可评论。同一篇文章的不同语言版本共用此讨论区。
在 GitHub 查看讨论