96 lines
5.9 KiB
Markdown
96 lines
5.9 KiB
Markdown
# 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),不再手算元素坐标。
|
||
|
||
## 后台的显示区编辑器(B3,用户诉求的后半)
|
||
|
||
三个手填输入框(中心纬度/经度/半径)换成**地图 + 圆列表**:点地图加圆、拖圆心移动、
|
||
拖圆边改半径、列表里可改半径/定位/删除、可一次清空;改动立刻写进隐藏字段
|
||
`areas`(JSON) 与 `crs`,随专题表单一起提交。复用 `/admin/fog-mask` 那套约定
|
||
(`/api/map-config` 取底图、底图坐标系随表单带上、后端换算成 GCJ-02),不另起炉灶。
|
||
|
||
### 实测抓到的 bug(探针一跑就现形)
|
||
|
||
`/admin/topics` **没设 `p.HasMap = true`** ⇒ 布局不引入 Leaflet。表现极隐蔽:
|
||
脚本加载了、`rondTopicAreas` 也在,就是 `window.L` 永远 undefined,`init` 静默返回、
|
||
**控制台不报错**、页面看着像「什么都没发生」。已加守卫
|
||
`TestTopicsAdminPageLoadsLeaflet`(按函数体截取后断言含 `HasMap = true`)。
|
||
记忆里那条「带地图页须 `p.HasMap=true`」是真的会咬人。
|
||
|
||
### 两个设计上的取舍
|
||
|
||
- **areas 的初值渲染进隐藏字段**(`value="{{.Form.AreasJSON}}"`),JS 也从隐藏字段读初值,
|
||
而不是另传一份。这样脚本没加载上时,表单提交的仍是服务端渲染的原值 ——
|
||
否则一次保存就把已有显示区**静默清空**。守卫
|
||
`TestTopicAdminUsesMapEditorNotCoordInputs` 钉住了「初值来源」与「旧输入框必须没了」。
|
||
- **上限两边各写一份**(前端 `MAX_AREAS/MIN_KM/MAX_KM` vs 后端 Go 常量),并加守卫
|
||
`TestTopicAreaLimitsMatchBetweenGoAndJS` 直接比对数字。不一致的表现很别扭:
|
||
前端放行、后端拒绝,用户看到一句红字却不知道改哪里。
|
||
|
||
### 小坑
|
||
|
||
- 半径输入框套了一层**定宽容器**:站点全局 input 规则是 `width:100%` 且带 10 个 `:not()`
|
||
(特异性 (0,11,1)),直接给 input 写宽度压不过。
|
||
- `topic-areas.js` 只把**纯函数**(校验/序列化/增删改/几何换算)暴露出来供 node 单测,
|
||
另加一个最小测试钩子 `api._map` 供无头验证(同 `window.rondMap._state` 惯例)——
|
||
不给把手就只能真拿鼠标点地图,CDP 下很难稳定复现。
|
||
|
||
### gofmt / 行尾
|
||
|
||
`gofmt -l` 报的整文件重写有两层原因,已一并处理:
|
||
① Go 1.19 起 gofmt 会重排**文档注释**里的缩进列表(我写的 `// ① …` 被要求改成 tab 缩进块);
|
||
② 工作区是 CRLF 而 `.gitattributes` 声明 `eol=lf`。
|
||
`gofmt -w` 归一后 `git diff` 没有额外噪声 —— 说明 **blob 本来就是 LF,CRLF 只是检出产物**,
|
||
之前担心的「模板 CRLF 与 .gitattributes 冲突」不是仓库内容问题。
|
||
|
||
### 记忆压缩
|
||
|
||
`MEMORY.md` 压到 **8919**(原 9285):合并「归属口径/防复发」与「跨备份/附属数据」两节
|
||
(同属归属)、若干长条目的**理由**下沉 details(规矩一字未删)、收紧自己新加的那条。
|
||
12 节 → 11 节。
|