# 2026-10-12 ## 专题的「迷雾显示区域」改成一组圆(多圆 + 地图点选) 用户提出两点:① 显示区不该只有一个圆,应该能圈多个;② 应该在**地图上点选**, 而不是让他手填经纬度和半径。 ### 方案(先出清单获批,分批落地) 先摸清了现状:单圆硬编码在 6 处(`topics` 三列 / `HasArea` / `topicAreaCond` / `fitParams` / 后台三个输入框 / `topic.html` 的 `fogArea`)。同时发现项目里**已经有** 一套现成的地图选圆交互(`/admin/fog-mask` 那个页面:点选圆心 + 拖拽 + 半径可调 + 多圆列表),可以直接抄它的模式。而 `fog_masks` 是**另一个语义**(圈内迷雾对访客隐藏), 用户明确这轮不动它。 三个岔路口问过用户,都选了推荐项:只改专题(不做站点级)、后台做成**专题表单内嵌地图** (不是独立管理页)、不动遮罩区。 原计划 B1(存 areas)/B2(查询认多圆)之间会留一个「存了但只认第一个圆」的陷阱状态, 所以合并成一批:数据层与查询层一起做,后台先用兼容桥(仍手填单圆,落进 areas)。 ### 落地(三个提交) - `1bb35db` 后端与查询层:`topics.areas` jsonb 数组 + 存量回填;`Topic.AreaList()` 归一(改为空格时退回老的三列,未回填的老行与直接构造 Topic 的调用方都继续工作); `topicAreaCond` 改成 N 个圆的 OR;`fitParams` 改用所有圆的并集外接矩形。 - `44763df` 前台渲染:`clip-path` 从 `circle()` 换成 `path()`(多子路径、同绕向 ⇒ nonzero 下自然并集);拼路径抽成 `fog-clip.js` 以便跑 node 单测;顺带清掉已死的 `fitCircle`;`verify.sh` 挂进前端单测。 - 待办(B3):**后台地图编辑器**还没做(专题表单目前仍是三个手填输入框)—— 这是用户最初诉求的第二半,下一批做。 ### 关键取舍 - **为什么不单开一张表**:圆是专题定义的组成部分、与专题一起读写、没有独立生命周期; 单开表还要过一遍「能不能从备份重导」的归属口径、改 `dataset_ownership_test` 白名单、 注意迁移块顺序。jsonb 一次全绕开。 - **废弃列保留并镜像第一个圆**:回滚旧二进制时退化成「第一个圆」总比「没有区域」强。 - **`fitCircle` 删掉**:多圆表达不了「圆心 + 半径」,留着就是两套并行的贴合口径。 ### 只在这轮踩到的坑 - **CSSOM 会把 `clip-path` 规范化**:`style.clipPath` 读回来被补上空格 (`path("M 405 296 a 87 87 …")`),且位置不固定(`296 a87`)、第一个子路径尾部 还可能多个空格。想解析回几何必须每处分隔都用 `\s*`/`\s+` 容忍,别写死 —— 我连着写错两版正则(先是 `Z$` 卡在尾部空格)。 - **`--clip` 截图的 `pad=10`**(×scale)又咬了一次:这次用**自校准**绕开的 —— 用单圆掩码反解出 scale 与 offset(实测 2.0 / 19.2),不再手算元素坐标。