gis-view-webgl2 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +14 -11
  2. package/dist/gis-view.css +1 -1
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +445 -241
  5. package/dist/index.js +1 -1
  6. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +65 -498
  7. package/docs/GisView.md +1 -1
  8. package/docs/GisView/345/256/236/347/216/260/345/216/237/347/220/206.md +1 -1
  9. package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +114 -478
  10. package/docs/demos/BasicDemo.vue +144 -27
  11. package/docs/demos/BatchAddDemo.vue +133 -7
  12. package/docs/demos/ChannelDemo.vue +61 -0
  13. package/docs/demos/ConnectDemo.vue +164 -33
  14. package/docs/demos/CopyMergeDemo.vue +144 -42
  15. package/docs/demos/EventDemo.vue +29 -24
  16. package/docs/demos/LabelDemo.vue +5 -5
  17. package/docs/demos/api-index.js +53 -41
  18. package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +1 -1
  19. package/docs/demos/gis-view-demo/GisViewDemoStage.vue +1 -1
  20. package/docs/demos/gis-view-demo/panels/DataPanels.vue +1 -1
  21. package/docs/demos/gis-view-demo/useGisViewDemo.js +31 -33
  22. package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +1 -0
  23. package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +3 -0
  24. package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +8 -7
  25. package/docs/from0/05-/347/233/270/346/234/272/344/270/216/344/272/244/344/272/222.md +1 -1
  26. package/docs/from0/07-/351/200/232/351/201/223/345/210/206/347/273/204/344/270/216/345/244/232/345/233/236/347/272/277.md +26 -10
  27. package/docs/index.html +136 -13
  28. package/docs/internals/15-04-/351/200/220/350/275/256/350/256/260/345/275/225-/347/203/255/345/212/233/345/233/276/344/270/216/346/200/247/350/203/275.md +10 -0
  29. package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +1 -1
  30. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +82 -14
  31. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +23 -17
  32. package/docs/manual/05-/347/224/273/350/264/250/344/270/216/346/211/271/351/207/217/346/233/264/346/226/260.md +65 -0
  33. package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +9 -10
  34. package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +100 -40
  35. package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +11 -7
  36. package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +66 -6
  37. package/docs/manual/10-/351/200/232/351/201/223/347/274/226/350/276/221/344/270/216/346/226/260/345/242/236.md +226 -493
  38. package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +28 -5
  39. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +46 -14
  40. package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +5 -2
  41. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +34 -29
  42. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +30 -22
  43. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +168 -81
  44. package/docs/manual/19-/350/257/273/346/225/260/344/270/216/346/213/226/346/213/275/351/242/204/350/247/210.md +1 -1
  45. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +5 -3
  46. package/docs/manual/21-/350/257/264/346/230/216/344/270/216/345/270/270/350/247/201/351/227/256/351/242/230.md +9 -6
  47. package/docs/steps/README.md +201 -120
  48. package/docs/steps/bench-scale.html +1 -1
  49. package/docs/steps/index.html +222 -6
  50. package/docs/steps/selftest-actions.html +26 -25
  51. package/docs/steps/selftest-data.html +1 -1
  52. package/docs/steps/selftest-demos.html +290 -16
  53. package/docs/steps/selftest-docs.html +17 -6
  54. package/docs/steps/selftest-features.html +594 -54
  55. package/docs/steps/selftest-full.html +3 -3
  56. package/docs/steps/selftest-labels.html +82 -1
  57. package/docs/steps/selftest-perf.html +62 -7
  58. package/docs/steps/selftest-select.html +71 -6
  59. package/docs/steps/step-05-pick.js +4 -8
  60. package/docs/steps/step-07-channels.js +3 -5
  61. package/docs/steps/step-08-labels.js +3 -3
  62. package/docs/steps/step-10-full.js +3 -3
  63. package/docs/steps/step-12-events.js +41 -24
  64. package/docs//344/273/2160/345/210/260/345/256/214/346/225/264/347/232/204/345/256/236/347/216/260.md +1 -0
  65. package/package.json +1 -1
  66. package/docs/ai/01-/350/265/267/346/272/220/344/270/216/350/277/231/344/273/275/346/261/207/346/200/273/347/232/204/347/224/261/346/235/245.md +0 -29
  67. package/docs/ai/02-/346/214/211/345/206/205/345/256/271/345/210/206/347/261/273-/350/277/231/344/270/200/350/267/257/351/203/275/345/234/250/346/224/271/344/273/200/344/271/210.md +0 -66
  68. package/docs/ai/03-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/200/357/274/211/357/274/232/351/200/211/345/236/213/351/200/232/351/201/223/344/270/216/346/240/267/345/274/217.md +0 -178
  69. package/docs/ai/04-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/272/214/357/274/211/357/274/232/344/272/244/344/272/222/346/200/247/350/203/275/344/270/216/346/225/260/346/215/256.md +0 -140
  70. package/docs/ai/05-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/211/357/274/211/357/274/232/345/256/277/344/270/273/346/227/201/346/224/257/344/270/216/346/224/266/345/217/243.md +0 -43
@@ -21,7 +21,10 @@ view.updateOptions({
21
21
  when: { vn_kv: { gte: 500 } }, // 只给命中的节点显示,条件语言同上
22
22
  enabled: true, // 显式 false 整体关掉
23
23
  className: 'my-label',
24
- style: { background: 'rgba(23, 36, 54, 0.86)', color: '#ffffff' }
24
+ style: { background: 'rgba(23, 36, 54, 0.86)', color: '#ffffff' },
25
+ zIndex: 20, // 显式层级优先;不写且 followNodeZIndex 打开时跟随节点
26
+ followNodeZIndex: false, // true = 跟随节点样式链的 zIndex,可被更高层节点盖住
27
+ zoomScale: 0 // 0 固定屏幕字号;1 与地图同倍率,0.5 为平方根
25
28
  }
26
29
  })
27
30
  ```
@@ -40,6 +43,9 @@ view.updateOptions({
40
43
  | `when` | `GisViewConditionInput` | 无 | 条件语言,只给命中的节点渲染标签 |
41
44
  | `maxCount` | `number` | `200` | 最多渲染多少个标签(按当前视口内可见点的顺序取,被避让丢掉的候选不计入),`0` 表示不限制;只在 `labelOptimize: true` 时生效(缺省强制显示档不截断) |
42
45
  | `className` / `style` | `string` / `Record<string, string>` | 无 | 附加类名与内联样式:**不用写 HTML 也能改外观** —— `style` 是内联样式的增量合并,`background` / `color` / `fontSize: '14px'` / `fontWeight` / `borderRadius` / `padding` 这些写什么加什么(值按 CSS 写法给字符串),逐条再叠 `updateLabel` 的 `style`;不配就是样式表里的默认(白底 88% / `#1f3550` / 12px),想清掉某一项就写 `{ 那一项: null }` |
46
+ | `zIndex` | `number` | 无 | 标签层级:越大越靠上。显式值优先于 `followNodeZIndex`;同类标签按层级从低到高排,高层的 DOM / Canvas 标签压住低层。参与「更高层节点图标盖住标签」的判定 |
47
+ | `followNodeZIndex` | `boolean` | `false` | 节点标签跟随节点样式链里生效的 `zIndex`(节点自身 > 规则 > `pointStyle`)。跟随后的标签可被更高层节点图标整条让位,也可被更高层标签盖住;显式 `zIndex` 优先。线路标签忽略这一项 |
48
+ | `zoomScale` | `number` | `0` | 字号随地图缩放的指数:`0` 保持屏幕字号不变,`1` 与地图同倍率放大 / 缩小,`0.5` 表示地图放大 4 倍时字号放大 2 倍。基数是该项 `style.fontSize`(缺省 12px);实时交互按 0.1px 量化,停手后按真实排版校正盒子与避让 |
43
49
  | `avoidOverlap` | `boolean` | `true` | 标签避让:和已经放下的标签重叠时按 `overlapMode` 处理;只在 `labelOptimize: true` 时生效 |
44
50
  | `overlapGap` | `number` | `2` | 避让留白(CSS 像素),两个标签的矩形至少隔开这么多 |
45
51
  | `overlapMode` | `'shift' \| 'hide'` | `'shift'` | 撞上别的标签之后怎么办:`shift` 主动换落点(节点换锚点 / 线路沿线挪),仍放不下才不画;`hide` 直接不显示,不做任何位置回退。逐条标签可以各自覆盖;只压到底图区域文字时仍按软冲突照画 |
@@ -50,6 +56,10 @@ view.updateOptions({
50
56
  | `draggable` | `boolean` | `false` | 标签能不能拖:打开后标签自己接住指针(`pointer-events: auto`),按住挪到别处记成偏移,移动节点时偏移照旧叠在节点位移上。见「标签的拖动与编辑」 ;两个开关各管各的手势:`draggable: true` / `editable: false` 只拖不选(点一下什么都不发) |
51
57
  | `editable` | `boolean` | `false` | 标签能不能点选:打开后点一下发一条 `labelClick`,使用方按它选中这一条、调 `updateLabel` 改内容 / 字号 / 颜色 ;`editable: true` / `draggable: false` 时标签照样接指针,但按下不拦事件(画布照常平移 / 缩放、文字照常能选),松开那一下没挪出 3 px 才补发 `labelClick`;右键与双击两个开关任开一个都算 |
52
58
 
59
+ **层级与字号联动。** `zIndex` 决定标签之间的先后;节点标签写 `followNodeZIndex: true` 时会取节点样式链定下来的那一层。覆盖层只对「显式分层或跟随节点」的点标签做节点遮挡判定:更高层节点图标压到标签时整条让位,避免 DOM 标签浮在节点图标上面。遮挡索引按 64px 屏幕网格惰性建立,只有确实存在非 0 节点层时才扫描视口,数据量大但没配层级时不会多付这一趟。节点层级或尺寸通过 `updatePoint` / `patchPoints` 改变时,即便被改的节点自己没有标签,其他跟随层级的标签也会重新判定。
60
+
61
+ `zoomScale` 只缩放字号,不改标签的锚点与固定像素间距:`0`(默认)在任何地图比例下都按 `style.fontSize` 显示,`1` 与地图同倍率,其他数字是指数。缩放交互期间先即时改字号,停手后重排一次,把新字号用于碰撞盒与避让;同一次排版里相同相机比例与相同指数只计算一次,连续滚轮不会逐条重复 `Math.pow`。
62
+
53
63
  函数形态的 `content` 会收到 `{ index, item, x, y, screen, radius }`(`radius` 是节点画在屏幕上的半径),需要自己拼 DOM 时直接返回元素即可。`anchor: 'edge'` 时标签到节点的距离始终是「符号当前的屏幕半径 + `gap`」:节点被悬停 / 选中样式放大,标签跟着往外让,缩小跟着靠近;缩放地图时标签按新相机重新投影,间距不会跟着地图一起被放大(鹰眼图放大缩小几次之后标签也不会飘出画布)。`html: true` 时字符串按 HTML 渲染:模板串里的取值仍然会转义,自己拼标记时也要记得转义业务字段。标签只渲染当前视口内的节点,相机变化后按 `requestAnimationFrame` 合并重排,移出视口的标签 DOM 回收进池子复用;单个节点可以用 `labelVisible: false`、`labelPosition`、`labelOffset` 覆盖全局配置,也可以用 `labelVisibility: 'always' | 'hidden'` 单独指定显隐策略(`labelVisible: true` 等价于 `always`)。
54
64
 
55
65
  `html` 是**逐条**开关:全局 `label.html: true` 时,某条数据仍可写 `labels: [{ id: 'default', content: '<b>{name}</b>', html: false }]`,这一条就显示字面的 `<b>…</b>`;反过来全局 `false`、单条写 `true` 也会按标记渲染。运行时用 `updateLabel(kind, ref, { html: true | false })` 双向切换,写 `null` 回到全局 / 声明值。只有字符串 `content` 受它控制,函数返回 `HTMLElement` 时始终按元素插入。`getLabels()` 的清单以及 `labelClick` / `labelDrag` 载荷里的 `label` 都会带 `html`;HTML 模式下 `content` 返回 `innerHTML`,编辑面板回写时不会把 `<b>` / `<span>` 洗成纯文本。导出数据会把显式 `true` / `false` 一起写进 `labels`,导出再回灌保持原语义。
@@ -94,7 +104,9 @@ view.updateOptions({
94
104
  followOnInteract: true, // 没收起时,拖节点 / 拖拐点期间标签跟着线路走;false 停手后才归位
95
105
  draggable: true, // 标签能拖走(偏移记在组件里,再移动节点时叠在节点位移上)
96
106
  editable: true, // 点一下发 labelClick,改什么由使用方调 updateLabel 落
97
- when: { vn_kv: { gte: 500 } } // 只给命中的线路显示
107
+ when: { vn_kv: { gte: 500 } }, // 只给命中的线路显示
108
+ zIndex: 10, // 线路标签之间的层级:越大越靠上
109
+ zoomScale: 0 // 0 固定屏幕字号;1 与地图同倍率
98
110
  }
99
111
  })
100
112
  ```
@@ -107,6 +119,8 @@ view.updateOptions({
107
119
 
108
120
  线路标签与节点标签是两组独立配置(`lineLabel` 对 `label`),但排在同一轮、共用一张 64px 避让网格,所以两类标签不会叠在一起。扫描候选前会先按屏幕小格去重:同一小片区域只收一条线 —— 真实数据里一条通道常有成百上千条并行回线,中点几乎重合,不筛的话扫描名额全被一条走廊吃光、避让阶段又集体被丢,**整屏一条线路标签都显示不出来**;筛过之后候选摊到整个视口,`maxCount` 才有意义。`scanLimit` 限制收下的候选数,考察的线路条数另有 `max(scanLimit × 8, 2048)` 的硬上限兜底。
109
121
 
122
+ 线路标签同样支持 `zIndex` 与 `zoomScale`:`zIndex` 越大越靠上,DOM 标签按显式层号排序,同一张 Canvas 上的标签也按层号从小到大绘制;`zoomScale` 与节点标签的公式相同,基数是这一条 `style.fontSize`(缺省 12px),`0` 固定屏幕字号、`1` 与地图同倍率、`0.5` 是平方根。线路标签不认 `followNodeZIndex`,线路本身没有「节点层」可跟;需要跨类别控制先后时,给节点与线路标签分别写显式 `zIndex`。
123
+
110
124
  线路标签撞上别人(节点标签 / 别的线路标签)时默认也不直接丢掉:`overlapMode: 'shift'` 会沿线往两侧各试 2 步(`slideFallback`),每一步把这一条自己整个让开(标签宽度 + 两侧留白 + 一点余量),左右交替;找到空位当场落笔,并把位置记进这一条的状态、下一轮排版从那儿接着走,不会来回跳。写 `overlapMode: 'hide'` 则碰撞就直接不显示,不尝试滑动。底图的区域文字只算软冲突:`shift` 时沿线躲得开就躲、躲不开照画,`hide` 时也不为它隐藏。落点只取视口里的:滑出视口的那一条下一轮会被候选筛选当作「不在视口里」丢掉,看起来就是闪一下没了。
111
125
 
112
126
  一条线自己写了 `lineLabel` / `lineLabelPosition` / `lineLabelOffset` 时(`lineLabel: false` 不算),这条线算「有自己的标签」:它不占 `scanLimit` 的扫描名额,也不参与上面那一步的屏幕小格去重,因此通道里的并行回线都能作为候选各显示各的标签——`createChannels` 的 `linePropsByMember` 一次就能给每条回线写一份自己的 `lineLabelPosition`。这类候选排两轮:每个屏幕小格先收一条(先把标签铺开),同格里剩下的排在后面补位,空出来的位置再给它们;否则通道密集时先放下的几条回线就把整片区域占满,勾上「逐条显示」反而比不勾更少。
@@ -176,9 +190,9 @@ view.updateOptions({
176
190
  lineLabel: { draggable: true, editable: true }
177
191
  })
178
192
 
179
- view.on('labelClick', payload => view.selectFeatures(payload)) // { kind, index, id, labelId, item, label, parent, offset, state, selected, selection }
193
+ view.on('labelClick', payload => view.selectFeatures(payload)) // { kind, index, id, labelId, item, label, parent, offset, state, selected }
180
194
  view.on('labelDrag', payload => {}) // { phase: 'start' | 'move' | 'end', kind, index, id, labelId, item, label, parent, offset, startOffset, delta, state? }
181
- view.on('labelContextMenu', payload => openMenu(payload)) // 右键标签:{ ...labelClick 那一份, clientX, clientY, originalEvent }
195
+ view.on('labelContextMenu', payload => openMenu(payload)) // 右键标签:{ ...labelClick 那一份, originalEvent };菜单位置读 originalEvent.clientX / clientY
182
196
  view.on('labelDblClick', payload => openEditor(payload)) // 双击标签:载荷同上(originalEvent 是 dblclick)
183
197
  view.on('labelChange', payload => {}) // { action: 'update' | 'reset' | 'resetAll' | 'add' | 'remove' | 'restore' | 'addAll' | 'updateAll' | 'removeAll', kind, index, id, labelId?, patch?, state?, removed? }
184
198
  ```
@@ -250,7 +264,16 @@ view.resolveLabelTargets('point', 'selection') // 先看这批选中的定位到
250
264
 
251
265
  **位置现给就行。** `addLabel('point', 'p-1', { content: '备注', position: 'top-left' })` —— 节点标签认八向(`top` / `bottom` / `left` / `right` 与四个斜角)加 `center`,线路标签认沿线位置(比例 0 ~ 1、百分比、绝对距离);加完再用 `updateLabel({ position })` 改同样可以,`resetLabel` 回到默认锚点。想「一开始就显示几条标签」,把这几条写进 `options.label` / `options.lineLabel` 的数组(同一个要素多条,用 `id` 区分),或者直接写进数据的 `labels`(连位置、样式、偏移一起,见「标签(labels,导入即回显)」)都行 —— 两种写法都跟着数据走,刷新 / 换一份数据之后照旧在。
252
266
 
253
- `style` 是**增量合并**:`{ fontSize: '16px' }` 只改字号,颜色 / 背景那些不受影响;某一项写 `null` 只摘掉这一项覆盖(`updateLabel('point', 'p-1', { style: { fontSize: null } })` 把字号还给配置里的那一份)。`content` / `html` / `position` / `offset` / `style` 五个字段都认这个约定:不写 = 保持,写 `null` = 回到默认。恢复默认时组件会把当初写上去的内联样式摘干净,不会留下一条「看着像默认、其实是覆盖」的状态。
267
+ **层级与字号也能逐条改。** `updateLabel` 的补丁认得 `zIndex` / `followNodeZIndex` / `zoomScale`:写数值当场改这一条的层级或缩放指数,写 `null` 回到配置里的默认(节点标签恢复跟随开关、层号回到样式链或 0)。`getLabels(kind, ref)` 的每一条也会给出当下生效的 `zIndex`、`followNodeZIndex` 与 `zoomScale`,面板不用重新推导样式链:
268
+
269
+ ```js
270
+ view.updateLabel('point', 'p-1', { followNodeZIndex: true })
271
+ view.updateLabel('point', 'p-1', { zIndex: 30 }) // 显式层级压过跟随
272
+ view.updateLabel('line', 'l-1', { zIndex: 10, zoomScale: 1 })
273
+ view.updateLabel('point', 'p-1', { zIndex: null }) // 回到跟随节点
274
+ ```
275
+
276
+ `style` 是**增量合并**:`{ fontSize: '16px' }` 只改字号,颜色 / 背景那些不受影响;某一项写 `null` 只摘掉这一项覆盖(`updateLabel('point', 'p-1', { style: { fontSize: null } })` 把字号还给配置里的那一份)。`content` / `html` / `position` / `offset` / `style` / `zIndex` / `followNodeZIndex` / `zoomScale` 都认这个约定:不写 = 保持,写 `null` = 回到默认。恢复默认时组件会把当初写上去的内联样式摘干净,不会留下一条「看着像默认、其实是覆盖」的状态。
254
277
 
255
278
  状态按「要素 + `labelId`」记(要素按 id、没写 id 才按下标),换数据之后同 id 的标签仍带着自己那一份;`getLabelStates(kind?)` 传 `'point'` / `'line'` 只取一类。每次改动都会发一条 `labelChange`(`update` / `reset` / `resetAll` / `add` / `remove` / `restore`,载荷里带 `labelId`),面板按它回显;`setLabelStates(saved)` 能把整份状态灌回去,刷新页面 / 换一份数据之后接着用。拖动 / 点选只在标签自己身上发生,画布的框选、平移、`pointer` 那一套事件都不受影响;宿主用 CSS `zoom` 放大过时,指针位移会先除回布局像素,标签跟得住指针。三条批量的(`addLabels` / `updateLabels` / `removeLabels`)各发一条同名 `action`(`addAll` / `updateAll` / `removeAll`),载荷里带 `count` 与逐条明细;「拖得动」与「点得选」是两个开关,`editable: false` 时点一下不发 `labelClick`,`draggable: false` 的标签按下不拦事件(画布照常平移 / 缩放),松开那一下没挪出 3 px 才补发 `labelClick`。
256
279
 
@@ -159,12 +159,12 @@ view.focusOn(['station-1', 'station-2'], { select: true, zoom: true }) // 适配
159
159
  view.focusOn('station-1', { zoom: true, maxZoom: 8 }) // 零面积目标也能放大到 8 倍
160
160
  view.focusOn({ minX: 116, minY: 39, maxX: 117, maxY: 40 }, { zoom: true, padding: 60 })
161
161
 
162
- // 只挪中心、比例一点都不动:focusOn(target, { center: true })
163
- view.focusOn([116.4, 39.9], { center: true }) // 世界坐标(数组 / { x, y } 都认)
164
- view.focusOn('station-1024', { center: true }) // 要素:id / { type, index } / 搜索命中
165
- view.focusOn({ type: 'channel', channelId: 'channel-a' }, { center: true }) // 通道:取它范围的中心
166
- view.focusOn({ lngLat: [116.4, 39.9] }, { center: true }) // 配了 GeoJSON 底图时按经纬度
167
- view.focusOn({ minX: 116, minY: 39, maxX: 117, maxY: 40 }, { center: true }) // 一段范围:取中心
162
+ // 只挪中心、比例一点都不动:不写 zoom / scale 就是缺省行为
163
+ view.focusOn([116.4, 39.9]) // 世界坐标(数组 / { x, y } 都认)
164
+ view.focusOn('station-1024') // 要素:id / { type, index } / 搜索命中
165
+ view.focusOn({ type: 'channel', channelId: 'channel-a' }) // 通道:取它范围的中心
166
+ view.focusOn({ lngLat: [116.4, 39.9] }) // 配了 GeoJSON 底图时按经纬度
167
+ view.focusOn({ minX: 116, minY: 39, maxX: 117, maxY: 40 }) // 一段范围:取中心
168
168
 
169
169
  // flyTo:目标与选项和 focusOn 完全一致,只多 duration / easing,把相机平滑飞过去
170
170
  view.flyTo(hit, { zoom: true, duration: 600, easing: 'ease-in-out' })
@@ -185,7 +185,6 @@ const flying = view.cameraAnimating // 只读:按钮在「飞向
185
185
  | --- | --- |
186
186
  | `zoom: true` | 显式要求按目标范围适配视图;不写就只居中、比例不动 |
187
187
  | `scale` | 直接设成这个比例,不再按范围适配(优先级高于 `zoom`);线路 / 通道 / 分组不会在此基础上再自动放大 |
188
- | `center: true` | 显式只把中心挪过去、比例一点不动(与缺省行为一致,保留旧写法) |
189
188
  | `padding` | 按范围适配(`zoom: true`)时四周留白(CSS 像素,缺省 40) |
190
189
  | `maxZoom` | 适配(`zoom: true`)时的最大比例:单个节点 / 一条直线没有面积,靠它撑出一个最小范围 |
191
190
  | `select: true`(或另给一批写法) | 把这些被聚焦的要素同时变成选中状态(与接口选中同一份 state) |
@@ -246,7 +245,7 @@ const search = async keyword => {
246
245
 
247
246
  ## 初始视图
248
247
 
249
- 不配 `initialView` 时,首次拿到数据会 `fitView()`:整个数据范围铺满画布。数据一多,一屏全是点线看不出重点,`initialView` 用一份配置决定首屏落在哪里:
248
+ 不配 `initialView`(也没配顶层 `center` / `scale`)时,首次拿到数据会 `fitView()`:整个数据范围铺满画布。数据一多,一屏全是点线看不出重点,`initialView` 用一份配置决定首屏落在哪里:
250
249
 
251
250
  ```js
252
251
  createGisView(container, {
@@ -255,6 +254,9 @@ createGisView(container, {
255
254
  // 聚焦数据中心、比「适应视图」再放大 3 倍(最常见的一种)
256
255
  initialView: { mode: 'center', zoom: 3 }
257
256
  })
257
+
258
+ // 只想直接给中心点和绝对比例:可以写在顶层,不必再包一层 initialView
259
+ createGisView(container, { points, lines, center: [500, 300], scale: 2 })
258
260
  ```
259
261
 
260
262
  | 字段 | 类型 | 说明 |
@@ -269,6 +271,7 @@ createGisView(container, {
269
271
  几个约定:
270
272
 
271
273
  - 只影响**首次落位**。之后拖拽 / 缩放 / `setCamera()` 都不会被它拉回来;`resetView()` 回到的是这次落位之后的相机。
274
+ - 顶层 `center` / `scale` 分别是 `initialView.center` / `initialView.scale` 的简写,创建时直接传即可;只要显式给出非空 `initialView` 对象,就整份以 `initialView` 为准,不再合并这两个简写。运行中 `updateOptions({ center })` / `updateOptions({ scale })` 同样只改下一次落位(首次加载或 `applyInitialView()`),要立刻移动 / 缩放用 `setCenter()` / `setScale()`。
272
275
  - 换数据、或者改了 `initialView` 想再套用一遍,调 `applyInitialView()`(demo 切数据集走的就是它)。
273
276
  - `fitOnInit: false` 只在**没配** `initialView` 时表示「首屏什么都不做,保持 `setCamera()` 给的位置」;配了 `initialView` 就以它为准。
274
277
  - 容器还没尺寸(`display: none` / 0×0)时这次落位会连目标一起挂着:挂的是**请求那一刻**的 `initialView` 与数据包围盒,等容器显示出来再照它兑现 —— 中间新加的远处节点不会把这次落位带跑。
@@ -280,7 +283,7 @@ createGisView(container, {
280
283
 
281
284
  1. **视野只由业务范围决定(推荐)**:配 `initialView: { bounds, padding }`(或 `mode: 'center'` + `scale`)把首屏钉在一块业务范围上 —— `bounds` 不跟着数据长,新节点进来包围盒变大也不影响相机;数据分布本来就广时,业务范围与数据边界分开正是想要的。
282
285
  2. **新节点不进自动适应**:`fitView()` 只手动调(首屏一次、切数据集一次),`addPoint` / `addPoints` 追加之后组件不会自动适应;要展示新节点就 `focusOn(id, { zoom: true })` 或高亮 / `setDropPreview`,别整幅重适应。
283
- 3. **只平移不改比例**:`setCamera({ center })` 或 `focusOn(id, { center: true })` —— 中心挪过去、`scale` 一点不动,两点之间的屏幕距离就不变。
286
+ 3. **只平移不改比例**:`setCamera({ center })` 或 `focusOn(id)` —— 中心挪过去、`scale` 一点不动,两点之间的屏幕距离就不变。
284
287
  4. **按「节点间距」而不是「包围盒」算适应**:`focusOn([...几个代表性要素], { zoom: true })` / `zoomToBounds(业务范围)`;也可以给 `minScale` 一个下界,缩到这条线就停住。
285
288
  5. **想让节点本身不跟着缩**:`pointZoomScale` / `lineZoomScale` 调小(`0` = 完全不跟随地图缩放),尺寸主要由 `pointSizeScale` / `lineWidthScale` 的常数倍数决定 —— 比例被带跑时至少节点不会一起变小。
286
289
 
@@ -308,6 +311,34 @@ view.updateOptions({ minScale: scale / 5, maxScale: scale * 5 })
308
311
 
309
312
  demo 的「初始视图与背景」面板里就有现成的入口(`docs/demos/GisViewDemo.vue`):两个输入框(最小比例 / 最大比例)直接写数字,改完走 `updateOptions({ minScale, maxScale })` **并顺手 `setCamera` 把当前相机夹回区间**(更新配置本身不会把相机拉回来,demo 里把这一步显式补上了);`以当前比例设限(÷5 ~ ×5)` 按当下比例算一对;`试一下下限` / `试一下上限` 各喂一个远超区间的比例给 `setCamera()`,读数里能看到相机停在哪;`解除限制` 回到组件缺省的 `0.002 ~ 20000`。自测页 `docs/steps/selftest-demo.html` 有 3 条断言盯着这一套(按当前比例设限并下发 / 越界喂值被夹在上下限 / 解除限制回到缺省)。
310
313
 
314
+ ## 比例读写、画布事件与空闲检测
315
+
316
+ 当前缩放比例用 `getScale()` 读,和 `getCamera().scale` 是同一份数(像素 / 世界单位);运行中改比例用 `setScale(scale, origin?)`,锚点缺省是视口中心,传 `[x, y]` 就是画布 CSS 像素里的固定点。`zoomTo()` / `zoomBy()` / `setCamera({ scale })` 也在同一条相机链上,都会受 `minScale` / `maxScale` 夹取并触发 `cameraChange`。
317
+
318
+ ```js
319
+ const scale = view.getScale() // 当前比例
320
+ view.setScale(12) // 以视口中心为锚点改比例
321
+ view.setScale(12, [320, 180]) // 指定画布 CSS 像素锚点
322
+ view.on('cameraChange', camera => { // 平移 / 缩放都会发
323
+ console.log(camera.center, camera.scale)
324
+ })
325
+ ```
326
+
327
+ 画布平移与缩放统一看 `onCameraChange` / `on('cameraChange')`:载荷是当前的 `{ center, scale }`,用户拖拽、滚轮、双击和程序化改相机都走这一条。`viewport` 只表示**画布尺寸 / 宿主显示比例**变化,不是平移缩放事件。
328
+
329
+ 用户空闲用 `idleTimeout` 打开:根节点上超过阈值没有指针 / 滚轮 / 键盘活动,就调用一次 `onIdle` 并派发 `idle`。`getIdleTime()` 随时可读当前空闲毫秒数;程序化 API 调用不算用户操作,不会把计时清零。每个空闲段只触发一次,恢复操作后重新计时。实现只保留一个定时器并复用同一份时间戳,长时间静止不会持续建监听或累积计时器;`destroy()` 会清掉它并移除根节点监听。
330
+
331
+ ```js
332
+ const view = createGisView(container, {
333
+ points,
334
+ idleTimeout: 60000,
335
+ onIdle: ({ idleTime, timeout }) => console.log(idleTime, timeout)
336
+ })
337
+ view.on('idle', payload => console.log('idle', payload.idleTime))
338
+ view.getIdleTime() // 距创建或上一次用户操作的毫秒数
339
+ view.updateOptions({ idleTimeout: 0 }) // 关闭空闲回调
340
+ ```
341
+
311
342
  ## 适应视图的节点间距(fitSpacing)
312
343
 
313
344
  「两个本来一样的视图,只因为多了一个远处节点,节点就突然很大、很挤」——根因是自动适应读的是**数据包围盒**:远处节点把范围撑大,`fitView()` 只能把 `camera.scale` 压小,节点之间的距离随之变短;节点自己的屏幕尺寸不跟着缩,看起来就像放大了一样。
@@ -598,15 +629,16 @@ console.log(minimapElement?.parentElement === minimapHost) // true
598
629
  | --- | --- |
599
630
  | `deselect(refs?)` ↔ `selectFeatures(refs, { mode: 'remove' })` | 取消选中的两个入口:`deselect` 是「去掉这几条」的短写法(写法解析完全一样),不给目标就是取消全部(`selectFeatures(null)` / `selectFeatures([])` 同义) |
600
631
  | `zoomIn(step?)` / `zoomOut(step?)` ↔ `zoomBy(factor, options?)` | 前者按 `zoomStep`(缺省 1.4)乘 / 除一档(`zoomOut(step)` 就是 `zoomBy(1 / step)`);要给具体倍数、要指定锚点用 `zoomBy` |
601
- | `addChannels(items, options?)` ↔ `addChannel(spec, lineData?, group?)` / `addChannel(input, lineItems?)` | 前者是批量版(一次多条通道 / 独立线路,逐条落地但只重排一次、只记一步历史、只发一条事件,每项里的 `lineItems` 照认),后者一次一条 |
602
- | `addChannel(spec, lineData?, group?)`(新签名,只新建)↔ `addChannelGroupLines(channelId, lineData, groupInfo?)` / `joinChannelGroupLines(channelId, groupId, lineData)` | 新签名只负责「两个节点之间还没有通道」的新建(第二段 / 第三段显式给了才算);已经有通道时要新加一组 / 并进某一组就走拆出来的这两个方法(语义直说,不必自己拼 `resolution`);只写 `{ from, to }` 的老调用照旧按方案表分流 |
632
+ | `addChannels(items, options?)` ↔ `addChannel(spec, lineData?, group?, options?)` | 前者是批量版(一次多条通道,逐条落地但只重排一次、只记一步历史、只发一条事件),后者一次一条;两条入口的 `lineData` 与 `options.select` 语义一致 |
633
+ | `addChannel(spec, lineData?, group?, options?)`(只新建)↔ `addChannelGroupLines(channelId, lineData, groupInfo?)` / `joinChannelGroupLines(channelId, groupId, lineData)` | `addChannel` 只负责「两个节点之间还没有通道」的新建;已经有通道时要新加一组 / 并进某一组就走拆出来的这两个方法。两端已有通道时 `addChannel` 整条失败,不再按方案表分流 |
603
634
  | `updateChannel(id, { group })`(单数:改一组的信息)↔ `updateChannel(id, { groups })`(复数:整份换分组声明) | 同一份分组数据的两档写法:只改某一组的字段(id 之外的字段 / 骨架 / 成员)用 `group`,整份重拆用 `groups`;两个一起给时以 `groups` 为准(`group` 丢掉并给一条 `warnOnce`) |
604
- | `addChannel(input, lineItems?)` ↔ `addChannel(input)` / `linePropsByMember` | 第二段与 `linePropsByMember` 同义(一条或一批逐条回线属性):给了几条就建几条(`circuits` 忽略),同下标两边都写时第二段优先 |
635
+ | `addLine(spec, lineData?, options?)` ↔ `addLines(items, options?)` | 前者按两个节点新建独立线路、用 `lineData` 一次生成多回;后者追加现成的线路数据对象。两者都只碰 `lines`,`channels` 不动 |
605
636
  | `getChannelAddOptions(from, to, count?, kind?)` ↔ `getChannelBetween(from, to)` | 同一份通道读数的一宽一窄:方案表要 `count` / `kind` 才能算余量与可选方案,通道读数只回答「有没有通道 / 有几组 / 当前哪组」;界面做「这两个点之间已经有通道」这类判断用窄的那条,不必凑参数 |
606
- | `addChannel` 顶层 `channelId` / `groupId` / `groupIndex` ↔ `resolution: { type: 'joinGroup', … }` | 同一件事的两种写法(与 `updateLine` 的分组指令同款):并进已有通道时指定「哪条通道、哪一组」;只给 `channelId` 按这条通道自己的推荐顺序挑(新分组 → 第一个能并的分组)。结果里的 `channelLineIds` 是更新后的完整成员表,见「新增通道」 |
637
+ | `addChannelGroups(channelId, groups, options?)` ↔ 多次 `addChannelGroupLines(channelId, lineData, groupInfo?, options?)` | 前者一次新加多组,只重排一次、只记一步历史;后者一次一组。两边的每组线路数据都写在 `lineData` / `lines`,`lineItems` 已移除 |
607
638
  | `project(x, y)` / `unproject(x, y)` ↔ `clientToScreen(clientX, clientY)` / `screenToClient(x, y)` ↔ `clientToWorld(clientX, clientY)` / `worldToClient(x, y)` | 同一套换算链的三档入口:`project` / `unproject` 收**画布 CSS 像素**,`clientTo*` 这一对吃鼠标事件里的 `clientX` / `clientY`(自动减 canvas rect、除宿主缩放)只换到画布像素,再接一层就是世界坐标(`clientToWorld` / `worldToClient`);经纬度那一对是 `lngLatToScreen` / `screenToLngLat` |
608
639
  | `fitView(padding?)` / `applyInitialView()` / `resetView()` / `zoomToBounds(bounds, padding?)` | 视角的四个语义:适应**数据范围**(并记下首次落位)/ 按 `options.initialView` 落位(可反复调用)/ 回到**首次落位那一刻**的相机 / 按给定的世界坐标范围适应 |
609
- | `focusOn(ref, options?)` ↔ `zoomToBounds(bounds, padding?)` | `focusOn` 认要素(id / 下标 / 搜索命中 / 坐标 / 矩形 / 一批),缺省只按范围中心居中;写 `{ zoom: true }` 才走适应那一条,另带 `center` / `scale` / `maxZoom` / `select` 这些选项 |
640
+ | `getCenter()` / `setCenter(center)` ↔ `getCamera().center` / `setCamera({ center })` | 只关心中心时用短入口:`getCenter()` 返回 `[x, y]` 的独立副本;`setCenter([x, y])` 只换中心、保留当前比例并返回实例。要同时改比例、飞行动画或恢复整份相机读数,仍用 `setCamera` / `flyToCamera` |
641
+ | `focusOn(ref, options?)` ↔ `zoomToBounds(bounds, padding?)` | `focusOn` 认要素(id / 下标 / 搜索命中 / 坐标 / 矩形 / 一批),缺省只按范围中心居中;写 `{ zoom: true }` 才走适应那一条,另带 `scale` / `maxZoom` / `select` 这些选项 |
610
642
  | `flyTo(target, options?)` ↔ `focusOn(target, options?)` | 同一套目标与选项解析,区别只在落位方式:`focusOn` 立即改相机,`flyTo` 默认用 450 ms 平滑过去,并多认 `duration` / `easing`;需要随时停在当前帧用 `stopCameraAnimation()` |
611
643
  | `pick(x, y)` ↔ `pickAt(clientX, clientY)` | 坐标口径不同:`pick` 收画布 CSS 像素,`pickAt` 直接吃鼠标事件里的客户端坐标(结果里另加 `target` 与 `lngLat`) |
612
644
  | `hoverAt(clientX, clientY)` / `clearHover()` / `getHover()` | 三个动作:程序化悬停 / 撤掉并补发 `onHover(null)` / 读当前那一份(`hoverAt` 的返回与 `onHover` 载荷同一份) |
@@ -125,7 +125,7 @@ view.exportData({ format: 'test', parts: ['waypoint'] })
125
125
  | `scope` | 导哪一块 | 范围相关选项 |
126
126
  | --- | --- | --- |
127
127
  | `'all'`(缺省) | 全部数据,再按 `points` / `lines` / `channels` 收窄 | —— |
128
- | `'viewport'` | 当前视口内的节点 / 线路 / 通道(通道按成员回线是否可见整条带上) | `margin`(屏幕 CSS 像素外扩)、`rect`(直接给世界矩形);这一档不认 `points` / `lines` / `channels` 三个筛选 |
128
+ | `'viewport'` | 当前视口内的节点 / 线路 / 通道(通道按成员回线是否可见整条带上);`selectionKinds` 可改为按类型 / 端点口径筛 | `margin`(屏幕 CSS 像素外扩)、`rect`(直接给世界矩形)、`selectionKinds`;这一档不认 `points` / `lines` / `channels` 三个筛选 |
129
129
  | `'selection'` | 当前选中集;`points` 可显式点名,线路取两端都在集合里的那些,通道跟成员线路 | `points` / `lines` / `channels`、`withLines: false` 可不要线路;没有节点时返回 `ok: false` + `reason`,不抛异常 |
130
130
 
131
131
  `exportData` 的 `format: 'full'` 始终返回标准文档本身,不夹带 `scope` / `viewRect` / `counts` 这些读数;`input` 与自定义格式同样保持导出器的原始结果。所有导出都只走这一个入口:范围写 `scope`、分项写 `parts`、口径写 `format`,没有再按业务表拆出第二套实例方法。
@@ -152,11 +152,14 @@ view.exportData({ format: 'test', parts: ['waypoint'] })
152
152
 
153
153
  `margin` 是**屏幕 CSS 像素**的外扩:把贴着视口边的一圈也带上(按当前 scale 换算成世界单位),`rect` 可以直接给一份世界矩形替掉视口(写法同 `getViewRect()`)。`format` / `parts` / `labels` / `box` 都照 `exportData` 写;`points` / `lines` / `channels` 这三个筛选这一档不认(范围由视口定)。`view` / `new` / `test` 业务口径会返回 `scope: 'viewport'` 与 `viewRect`(这次筛用的世界矩形)/ `margin` 三条读数;`format: 'full' | 'input' | 自定义格式` 保持导出器原始结果,不附这些读数。
154
154
 
155
+ `selectionKinds` 与 `options.selectionKinds` 的写法相同,只对 `scope: 'viewport'` 生效:不写时保持上面那套旧口径;写 `'line'` / `'channel'` 时仍按几何是否沾视口筛选;写 `'lineEndpoints'` / `'channelEndpoints'` 时,只有两端节点都落在视口世界矩形(含 `margin` / `rect`)内的独立线路 / 通道才会导出。端点模式不会顺带导出节点,节点要不要仍看有没有 `'point'`;纯 `path`、缺失端点、自环以及端点被筛掉 / 隐藏的都会跳过。旧模式与端点模式可以并存,结果取并集。
156
+
155
157
  演示页「编辑与保存」(`?demo=backend`)把这一步和分组 / 拐点 / 场站改动的接口请求体摆在一起看,见 `docs/demos/BackendSyncDemo.vue`;两种推送策略(点保存一起传 / 改完就传的合并 + 延迟)也在这一页。
156
158
 
157
159
  ```js
158
160
  const visible = view.exportData({ scope: 'viewport', margin: 24 })
159
161
  // 业务口径:{ points, lines, channels, parallelGroups, counts, warnings, scope, viewRect, margin }
162
+ const connected = view.exportData({ scope: 'viewport', selectionKinds: ['lineEndpoints', 'channelEndpoints'] })
160
163
  const inner = view.exportData({ scope: 'viewport', format: 'input' }) // 与创建入参同构,保持原始结果形状
161
164
  ```
162
165
 
@@ -198,7 +201,7 @@ next.importData(saved) // 已有实例上整份灌回来(顶
198
201
 
199
202
  | 方法 | 出什么 | 选项(除共用项) |
200
203
  | --- | --- | --- |
201
- | `exportData(options?)` | **统一入口**:按 `scope` + `parts` + `format` 给 | `scope: 'all' \| 'viewport' \| 'selection'`(缺省 `all`,也认 `visible` / `view` / `selected`);`parts` 多选分项(见下);`format: 'view' \| 'new' \| 'test' \| 'input' \| 'full'`(缺省 `'view'`;`'input'` / `'full'` 见「五套口径」);`withLines: false`(selection 不要线路);`all`(列出全部分组);`tcFilePath` / `warningMsg`(`'new'` 口径那两列);`box: false` / `showStation: false`(`'new'` 口径不要四至范围 / 母线清单) |
204
+ | `exportData(options?)` | **统一入口**:按 `scope` + `parts` + `format` 给 | `scope: 'all' \| 'viewport' \| 'selection'`(缺省 `all`,也认 `visible` / `view` / `selected`);`parts` 多选分项(见下);`format: 'view' \| 'new' \| 'test' \| 'input' \| 'full'`(缺省 `'view'`;`'input'` / `'full'` 见「五套口径」);`selectionKinds`(仅 `viewport`,支持 `lineEndpoints` / `channelEndpoints`);`withLines: false`(selection 不要线路);`all`(列出全部分组);`tcFilePath` / `warningMsg`(`'new'` 口径那两列);`box: false` / `showStation: false`(`'new'` 口径不要四至范围 / 母线清单) |
202
205
 
203
206
  `points` / `lines` / `channels` 是「只导出这几个」的筛选(`id` / 下标 / `{ id }` / `{ index }`,单个或数组;不写就是全部),`extra` 是额外挂到结果上的字段(`tc_file_path` 这类业务元数据,同名的键以它为准)。
204
207
 
@@ -16,9 +16,9 @@
16
16
  | `importData(input, options?)` | 整份喂回一份数据 / 一份标准文档(`exportData` 的五种格式都认,最标准的是 `format: 'full'` 那一份):顶层出哪一列就应用哪一列(`points` / `lines` / `channels` / `labelStates` / `view` / `filter`),没出的列一个都不动;只给通道(不带点线)时单独换通道声明,只给 `labelStates` 时只动标签状态(整份语义)。返回 `{ ok, reason, applied, counts, warnings }`,见「标准数据文档(full):接收 == 导出」 |
17
17
  | `patchPoints(entries, patch?)` / `patchLines(entries, patch?)` | 批量打补丁(只动点到的这几条,不换整份数据):`patchPoints([{ id: 'a', name: 'A' }, { index: 3, x: 10 }])` 一次改几条(`id` / `index` 之外的字段就是这一条的补丁),或者 `patchPoints(['a', 'b'], { status: 1 })` 把同一份补丁盖到一批定位上(`'a'` / `{ index: 2 }` 都认)。认不出来的进 `skipped`、不影响其余条目;返回 `{ ok, reason, indexes, items, count, skipped }`,**只记一条历史、只发一条** `updatePoints` / `updateLines` 数据变更 —— 十万级批量改不必逐条付事件与历史的代价,见「批量更新(batch)」 |
18
18
  | `getLabelStates(kind?)` | 标签的运行时状态(拖出来的偏移 + 逐条覆盖,每条带 `labelId`):`kind` 写 `'point'` / `'line'` 只取一类;做持久化 / 回显编辑面板用它,见「标签的拖动与编辑」 |
19
- | `getLabels(kind, ref?)` | 一条要素上现在有哪些标签(配置里的每一条 + 数据 / 运行时多出来的,各自带内容 / 锚点 / 偏移 / 样式 / 可见性、来源(`source`:`'config'` 配置里定义的 / `'data'` 数据里写的 / `'runtime'` 运行时加的)与运行时状态);不给 `ref` 时给「有标签的要素」清单;找不到要素返回 `null` |
19
+ | `getLabels(kind, ref?)` | 一条要素上现在有哪些标签(配置里的每一条 + 数据 / 运行时多出来的,各自带内容 / 锚点 / 偏移 / 样式 / 可见性、当下生效的 `zIndex` / `followNodeZIndex` / `zoomScale`、来源(`source`:`'config'` 配置里定义的 / `'data'` 数据里写的 / `'runtime'` 运行时加的)与运行时状态);不给 `ref` 时给「有标签的要素」清单;找不到要素返回 `null` |
20
20
  | `setLabelContent(content, options?)` | 一键换**全局**标签内容(面板上「显示名称 / 电压等级」这类切换):`content` 与 `label.content` 同一套写法(`'{vn_kv}'` 模板串 / 函数 / `null` 回到默认),只换内容,位置 / 样式 / 显隐保持;`options.kind` 缺省 `'point'`(`'line'` / `'both'` 连线路标签一起),`options.labelId` 缺省 `'default'`(写 `'*'` 换配置里的每一条),见「节点标签」 |
21
- | `updateLabel(kind, ref, patch)` | 逐条改一个标签:`patch.labelId` 指定改哪一条(缺省 `'default'`),另外认 `content` / `html` / `position` / `style` / `offset` / `visibility` / `parallel` / `hidden`,不写的保持、写 `null` 回到默认,`style` 是增量合并;改完当场重排一次并返回这一条的最新状态读数,找不到要素或这条要素上没有这一条标签时返回 `null` 并记一条 warning |
21
+ | `updateLabel(kind, ref, patch)` | 逐条改一个标签:`patch.labelId` 指定改哪一条(缺省 `'default'`),另外认 `content` / `html` / `position` / `style` / `offset` / `visibility` / `parallel` / `hidden` / `zIndex` / `followNodeZIndex` / `zoomScale`,不写的保持、写 `null` 回到默认,`style` 是增量合并;改完当场重排一次并返回这一条的最新状态读数,找不到要素或这条要素上没有这一条标签时返回 `null` 并记一条 warning |
22
22
  | `addLabel(kind, ref, definition?, labelId?)` | 运行时加一条标签(不写进业务数据):`definition` 与配置里的一条同一套写法,`id` 缺省自动取 `note1` / `note2`…;同 id 已存在时按 `updateLabel` 处理 |
23
23
  | `removeLabel(kind, ref, labelId?)` | 删一条标签:运行时加的(`addLabel`)删掉那一份;数据 / 配置里还声明着同 id 的标签时再标一条隐藏(它照样会画出来,数据是使用方的,组件只记一条覆盖)。返回 `{ removed, hidden }`,找不到返回 `null` |
24
24
  | `addLabels(kind, refs, definition?, options?)` | 批量加同一套标签:`refs` 见「统一加 / 改 / 删一批标签」(单个 / 数组 / `'all'` / `'visible'` / `'selection'` / `{ channel }` / `{ group }`),`definition` 认一条定义、数组或 `(item, index) => 定义`,已有同 id 的缺省跳过、`options.overwrite` 覆盖;返回 `{ kind, requested, added, updated, skipped }` |
@@ -35,13 +35,15 @@
35
35
  | `zoomIn(step?)` / `zoomOut(step?)` / `zoomBy(factor, origin?)` | 相对缩放,`origin` 为画布 CSS 像素锚点(缺省视口中心) |
36
36
  | `zoomTo(scale, origin?)` | 直接设成指定比例(像素 / 世界单位) |
37
37
  | `zoomToBounds(bounds, padding?)` | 把一段世界坐标矩形适应到视图内,退化成一点时只居中不改比例 |
38
- | `focusOn(target, options?)` | 把视角移到目标上:单条(搜索命中 / 要素 id / `{ type: 'point' \| 'line' \| 'channel', id \| index }` / `{ channelId }` 整条通道 / `{ type: 'group', channelId?, groupId }` / 坐标 / 矩形 / `{ lngLat }`)或**一批**(数组取整体包围盒 —— 搜到三个节点就是这三个点的中心当屏幕中心);字符串先节点后线路,裸字符串不表示通道,同 id 用 `type` 消歧,分组必须显式写 `type: 'group'`(全局唯一 `groupId` 可省略通道)。**缺省只居中、比例一点不动**;`options.zoom: true` 才按解析出的真实范围适配(节点 / 线路 / 通道 / 分组共用这一条,不额外放大),`scale` 优先级高于 `zoom`。`options` 还认 `center: true`(显式只居中,与缺省一致)、`padding`、`maxZoom`、`select`(顺带选中这批,`mode` 决定怎么并进当前选中集) |
39
- | `flyTo(target, options?)` | 平滑飞到与 `focusOn` 相同的目标:目标写法与 `zoom` / `scale` / `center` / `padding` / `maxZoom` / `select` / `mode` 完全同义,另认 `duration`(毫秒,缺省 450)与 `easing`(`linear` / `ease-in` / `ease-out` / `ease-in-out` 或 `(progress) => progress`)。中心线性插值、比例按对数插值;`duration <= 0` 或系统要求减少动态效果时立即落位。再次飞行、用户拖拽 / 滚轮或任何程序化改相机都会停在当前帧,不补跳到终点 |
38
+ | `focusOn(target, options?)` | 把视角移到目标上:单条(搜索命中 / 要素 id / `{ type: 'point' \| 'line' \| 'channel', id \| index }` / `{ channelId }` 整条通道 / `{ type: 'group', channelId?, groupId }` / 坐标 / 矩形 / `{ lngLat }`)或**一批**(数组取整体包围盒 —— 搜到三个节点就是这三个点的中心当屏幕中心);字符串先节点后线路,裸字符串不表示通道,同 id 用 `type` 消歧,分组必须显式写 `type: 'group'`(全局唯一 `groupId` 可省略通道)。**缺省只居中、比例一点不动**;`options.zoom: true` 才按解析出的真实范围适配(节点 / 线路 / 通道 / 分组共用这一条,不额外放大),`scale` 优先级高于 `zoom`。`options` 还认 `padding`、`maxZoom`、`select`(顺带选中这批,`mode` 决定怎么并进当前选中集) |
39
+ | `flyTo(target, options?)` | 平滑飞到与 `focusOn` 相同的目标:目标写法与 `zoom` / `scale` / `padding` / `maxZoom` / `select` / `mode` 完全同义,另认 `duration`(毫秒,缺省 450)与 `easing`(`linear` / `ease-in` / `ease-out` / `ease-in-out` 或 `(progress) => progress`)。中心线性插值、比例按对数插值;`duration <= 0` 或系统要求减少动态效果时立即落位。再次飞行、用户拖拽 / 滚轮或任何程序化改相机都会停在当前帧,不补跳到终点 |
40
40
  | `search(keyword \| options)` | 按关键字搜索节点 / 线路 / 通道 / 通道分组并返回命中:`scope` 认复数的 `points` / `lines` / `channels` / `groups`,也认与 `focusOn` 一致的单数别名 `point` / `line` / `channel` / `group`;结果分 `points` / `lines` / `channels` / `groups` 四类,分组命中带 `channelIndex` / `groupId` / `groupIndex`,可直接交给 `focusOn` / `selectFeatures`。**不动视角**:要不要聚焦由调用方自己调 `focusOn`,见「搜索与定位」 |
41
41
  | `searchAsync(keyword \| options)` | 同一份口径的异步版:数据量大时把扫描放进 Worker(10 万点 / 10 万线下同步要占主线程 40 ms 上下的那一段),返回 `Promise<GisViewSearchResult>`;结果带 `worker` 与 `stale` 两个字段,见「异步搜索」 |
42
- | `setCamera(camera)` / `getCamera()` | 读写 `{ center, scale }` |
42
+ | `setCamera(camera)` / `getCamera()` / `getCenter()` / `setCenter(center)` | 读写相机 `{ center, scale }`;`getCenter()` 直接给当前中心 `[x, y]`(独立副本),`setCenter(center)` 只移动中心、保留当前比例并返回实例 |
43
+ | `getScale()` / `setScale(scale, origin?)` | 读写当前比例(像素 / 世界单位):`getScale()` 与 `getCamera().scale` 同一份读数;`setScale()` 立即改比例,`origin` 是画布 CSS 像素锚点(缺省视口中心),返回实例 |
43
44
  | `flyToCamera(camera, options?)` | 直接飞到相机目标 `{ center?, scale? }`:不写的那一项沿用当前相机,不经过要素解析、也不改选中与数据;`duration` / `easing` 与 `flyTo` 同义,参数非法时保持当前相机 |
44
45
  | `stopCameraAnimation()` / `getCameraAnimation()` | 停止当前相机飞行(停在当前帧,返回实例)/ 读当前飞行 `{ active, progress, duration, from, to }`(没有动画时 `null`) |
46
+ | `getIdleTime()` | 距创建或上一次指针 / 滚轮 / 键盘操作的毫秒数;程序化 API 调用不重置。配合 `idleTimeout` / `onIdle` / `idle` 做「多久没人动」的读数 |
45
47
  | `getZoomBaseScale()` / `setZoomBaseScale(scale?)` | 读 / 重定**缩放联动的基准比例**(缺省是首次适应视图时的比例):不传参数就取当下相机的比例 —— 把现在这一屏当成「原样大小」,之后放大缩小都从它算倍数;配合 `pointZoomScale` / `lineZoomScale` 使用 |
46
48
  | `getBounds()` | 数据范围(没有点时回退到背景 GeoJSON 的 `rawBounds`,也就是 `aspectScale` 之前的真实经纬度范围);每次返回独立副本,重复调用命中缓存 |
47
49
  | `getViewRect()` | 当前可视范围的世界坐标矩形;自建缩略图 / 坐标读数用它换算视口框,内置鹰眼图见「鹰眼图」 |
@@ -72,28 +74,29 @@
72
74
  | `mapDataToGeoJson(data, options?)` | `createGeoMapper(options).mapData(data)` 的快捷写法:把 `{ points, lines, channels }` 整份搬到 GeoJSON 坐标,原数据不动;没有 GeoJSON 背景时是 `null` |
73
75
  | `pick(x, y)` | 拾取指定画布位置上的要素(`clickable: false` 的要素不会返回) |
74
76
  | `pickAt(clientX, clientY)` | 按**浏览器客户端坐标**拾取(就是鼠标事件里的 `clientX` / `clientY`):与指针事件载荷同一份结果(`type` / `index` / `item` / `distance` / `screen` / `coordinate` 与通道字段),另加 `target`(`'point'` / `'line'` / `'channel'`)与 `lngLat`;命中通道回线时 `group` 是当前组、`groups` 是整条通道的全部分组;没命中返回 `null`。只读,不改选中也不动光标 |
75
- | `selectAt(clientX, clientY)` | 只有客户端坐标时用它像普通单击一样立即选中命中要素:节点 / 线路直接选中,通道回线按 `channelSelectMode` 摊开;空白或聚合圆返回 `null` 且不改选中。不等待 `selectTrigger: 'dblClick'`,也不派发 `onClick` / `onDblClick`;每次成功命中都发 `onSelect`,选中集实际变化时再发 `onSelectChange`,顺序 `onSelect → onSelectChange`(`cause: 'api'`)。选中通道回线时载荷带当前 `group` / 完整 `groups`,多通道看 `channelContexts`。已知节点 / 线路 id 时直接用 `selectFeatures`,见「按坐标单击选中(selectAt)」 |
77
+ | `selectAt(clientX, clientY)` | 只有客户端坐标时用它像普通单击一样立即选中命中要素:节点 / 线路直接选中,通道回线按 `channelSelectMode` 摊开;空白或聚合圆返回 `null` 且不改选中。不等待 `selectTrigger: 'dblClick'`,也不派发 `onClick` / `onDblClick`;每次成功命中都发 `onSelect`,选中集实际变化时再发 `onSelectChange`,顺序 `onSelect → onSelectChange`(`cause: 'api'`)。选中通道回线时外层带当前 `group` / 完整 `groups` / 本次选中的 `lineIndexes` / `lineIds`,多通道看 `channelContexts`;`items[]` 不挂通道字段。已知节点 / 线路 id 时直接用 `selectFeatures`,见「按坐标单击选中(selectAt)」 |
76
78
  | `hoverAt(clientX, clientY)` / `clearHover()` / `getHover()` | 程序化悬停:`hoverAt` 让指针「停」到某个客户端坐标上(悬停高亮、光标与 `onHover` 一起更新,返回与 `onHover` 载荷同一份读数,空白处 `null`;不走悬停阈值,也不用先订阅事件)、`clearHover()` 撤掉并补发一条 `onHover(null)`、`getHover()` 随时读当前这一份 —— 外部列表 / 表格 hover 到某一行时联动高亮图上的要素用它 |
77
79
  | `getFeaturesInRect(rect)` | 画布 CSS 像素矩形里有什么(与框选 / `selectInBox` 同一套坐标与判定):返回 `{ rect, worldRect, points, lines }` —— `points` 是框内的节点(与 `onSelect` 的 `items` 同一口径)、`lines` 是包围盒沾到框的线路(与 `listLines` 同一份)、`worldRect` 是这块像素对应的世界坐标范围;只读,不改选中也不画框 |
78
80
  | `getChannelsInView()` | 当前视口里有哪几条通道(同一通道的几回线只算一条):每条给 `{ channelId, channelIndex, channel, lineIndexes, groupIds }`,侧边栏「画面里的通道」、按视口统计用它 |
79
- | `selectFeatures(refs, options?)` | 接口选中(换一份选中集;`selectFeatures(null)` / 空数组就是清空):`refs` 认节点 / 线路 id、id 数组、`{ type, index }`、通道写法(`{ type: 'channel', channelId }` / `{ channelId, groupId }` / `{ channelId, lineIds }`)、分组写法(`{ type: 'group', channelId?, groupId }`,全局唯一 `groupId` 可省略通道)以及拾取载荷与搜索命中;`options.mode` 给 `'replace'`(缺省,换一份)/ `'add'` / `'toggle'`(在就取消、不在就选上)/ `'remove'`。会同步选中样式与读数;选中集实际变化时按 `onSelect → onSelectChange` 派发(`cause: 'api'`),载荷带同一份通道上下文(单通道摊平,多通道看 `channelContexts`),不发 `onClick` / `onDblClick`,见「接口选中(selectFeatures)与读回选中」 |
81
+ | `selectFeatures(refs, options?)` | 接口选中(换一份选中集;`selectFeatures(null)` / 空数组就是清空):`refs` 认节点 / 线路 id、id 数组、`{ type, index }`、通道写法(`{ type: 'channel', channelId }` / `{ channelId, groupId }` / `{ channelId, lineIds }`)、分组写法(`{ type: 'group', channelId?, groupId }`,全局唯一 `groupId` 可省略通道)以及拾取载荷与搜索命中;`options.mode` 给 `'replace'`(缺省,换一份)/ `'add'` / `'toggle'`(在就取消、不在就选上)/ `'remove'`。会同步选中样式与读数;选中集实际变化时按 `onSelect → onSelectChange` 派发(`cause: 'api'`),载荷外层带同一份通道上下文(单通道摊平,多通道看 `channelContexts`),`items[]` 不挂通道字段,不发 `onClick` / `onDblClick`,见「接口选中(selectFeatures)与读回选中」 |
80
82
  | `selectWaypoint(lineRef, pathIndex?, options?)` | 代码选中一个拐点:`selectWaypoint('line-1', 2)` / `{ lineIndex, pathIndex }` / `{ lineId, pathIndex }`,并同时选中所在线路;有效下标从 `getWaypoints()` 取。传 `null` 只取消拐点选中、不改要素选中集,见「代码选中拐点(selectWaypoint)」 |
81
83
  | `deselect(refs?)` | 取消选中:写法与 `selectFeatures` 完全一样,把这些从当前选中集里去掉、其余选中原样留着;不传 / `null` 等于取消全部(也就是 `selectFeatures(null)`),空数组是「没有目标」什么都不做。改变选中集时按 `onSelect → onSelectChange` 派发(`cause: 'api'`;清空时 `onSelect` 载荷为 `null`),见「取消选中(deselect / selectFeatures(null))」 |
82
- | `getSelections()` | 读回选中结果 `[{ type, index, item, … }]`(按选中顺序):要 id 列表 / 个数 / 包围盒看 `getSelectionData()` |
84
+ | `getSelections()` | 读回选中结果 `[{ type, index, item, … }]`(按选中顺序):通道回线不带通道字段,单条归属用 `getLineChannel(index)`;要 id 列表 / 个数 / 包围盒看 `getSelectionData()` |
85
+ | `getSelectionInfo()` | 选中集轻量计数 `{ ids, count, points, lines, channels }`(通道回线按通道算一个),与 `onSelectChange` 顶层同名字段同口径;按选中集引用缓存,十万级选中下也只是一次读取。指针 / 点击 / 悬停载荷不再重复挂 `selection`,状态栏这类只要计数的读数用它;要要素对象或包围盒再用 `getSelectionData()` |
83
86
  | `isSelected(ref)` | 这个要素在不在当前选中集里:`isSelected('node-0')`(id)/ `isSelected({ type: 'line', index: 12 })`(也可以只写下标,按 id 认不出来时当下标),返回 `true` / `false`。面板上高亮「当前这一行」时不必自己比对 `getSelections()`;选中集变化时缓存一份键集合(见「接口选中」),十万级选中下这一问也是常数级 |
84
- | `getSelectionData()` | 选中集一次取全:读数(`ids` / `count` / `pointCount` / `lineCount` / `channelCount`)加要素本身(`points` / `lines` / `channels`(按通道去重,与 `getData()` 同一批对象)),另有 `items`(与 `getSelections()` 同一份)、`pointIndexes` / `lineIndexes` 与 `bounds`(世界包围盒,空选中时 `null`),见「接口选中(selectFeatures)与读回选中」 |
87
+ | `getSelectionData()` | 选中集一次取全:读数(`ids` / `count` / `pointCount` / `lineCount` / `channelCount`)加要素本身(`points` / `lines` / `channels`(按通道去重,与 `getData()` 同一批对象)),另有 `items`(与 `getSelections()` 同一份,通道回线不带通道字段)、`pointIndexes` / `lineIndexes` 与 `bounds`(世界包围盒,空选中时 `null`),见「接口选中(selectFeatures)与读回选中」 |
85
88
  | `getCascadeHighlight()` | 当前点击级联高亮的读数:`{ origin, scope, points, lines, pointIndexes, lineIndexes }`;没有高亮或 `cascadeHighlight` 关着时返回 `null`。它只读、不改选中集,见「点击级联高亮」 |
86
- | `selectInBox(box, options?)` | 按画布 CSS 像素矩形框选,等价于 Shift + 拖动(跟随 `shortcuts.boxSelect`);`options.selectionKinds` 可只覆盖这一次的节点 / 线路 / 通道类型过滤。每次框选操作都发 `onSelect`(空框的载荷为 `null`),选中集实际变化时再发 `onSelectChange`,顺序 `onSelect → onSelectChange`(`cause: 'boxSelect'`);通道回线的分组字段与鼠标框选同一份 |
89
+ | `selectInBox(box, options?)` | 按画布 CSS 像素矩形框选,等价于 Shift + 拖动(跟随 `shortcuts.boxSelect`);`options.selectionKinds` 可只覆盖这一次的节点 / 线路 / 通道类型过滤,也支持 `'lineEndpoints'` / `'channelEndpoints'` 要求两端节点都在框内。每次框选操作都发 `onSelect`(空框的载荷为 `null`),选中集实际变化时再发 `onSelectChange`,顺序 `onSelect → onSelectChange`(`cause: 'boxSelect'`);通道回线的分组字段与鼠标框选同一份 |
87
90
  | `beginCopyPoint(options?)` | 开始复制一个节点:给 `id` / `index` 直接出预览(`commit: true` 出预览即落地,`follow: true` 跟随鼠标,`autoCommit: false` 停在预览态),不给来源就是「先点按钮再选节点」模式,见「节点复制」 |
88
91
  | `commitPointCopy()` / `cancelPointCopy()` | 落地 / 取消预览中的复制(返回 `end` / `cancel` 载荷) |
89
- | `updateCopyPoint(patch)` | 改预览节点的数据(等价于回调里的 `payload.update` / `setItem`),返回改完的节点 |
92
+ | `updateCopyPoint(patch)` | 改预览节点的数据(等价于回调里的 `payload.update`),返回改完的节点 |
90
93
  | `getPointCopy()` | 当前复制状态:预览中是 `{ phase: 'preview', index, item, source, … }`,等选点时是 `{ phase: 'pick' }`,没有就 `null` |
91
94
  | `copyPoint(source?)` | 复制一个节点到它旁边(一步到位):来源给 id / 下标 / 一份节点数据,不给就用选中的节点(再没有用光标下的);返回新节点 `{ index, item }` |
92
95
  | `copyPoints(sources?, options?)` / `copySelectedPoints(options?)` | **批量复制**(不经过剪贴板):不给 `sources` 时复制当前选中集(框选之后一次复制整组);默认只带选中要素 —— 选中线路复制线路、选中通道复制整条通道及回线,并自动补所需端点(`supportSources` / `supportItems` / `supportIndexes`)。显式 `sources` 时只复制这批节点;`lineIndexes` / `channelIndexes` 可再点名,`withLines: true` / `withChannels: true` 才恢复「来源内部全带」的旧口径(写 `false` 则连选中线路 / 通道也不带)。落点按 `coordinate` / `screen` 或 `copy.offset`,`select: true` 复制后选中新节点。返回 `phase: 'batch'` 载荷(`sources` / `items` / `indexes` / `selectedPointCount` / `lines` / `channels` / `count` / `revert()`),复制不了时 `ok: false` + `reason` |
93
96
  | `copyPointToClipboard(sources?, options?)` / `copyPointsToClipboard(sources?, options?)` / `pastePoint(options?)` / `pastePoints(options?)` / `getPointClipboard()` | 剪贴板:`copyPointToClipboard` 默认收当前选中的全部节点(只有没选中才回落到光标下的那个),`copyPointsToClipboard` 只认给定 / 选中的一批;Ctrl+V 时单节点且没有内部线路 / 通道仍走原预览,多节点或带线路 / 通道走整组落地,`pastePoints` 强制整组(单节点也走批量载荷)。`getPointClipboard()` 读 `count` / `sources` / `items` / `lines` / `channels` / `anchor` 快照,见「节点复制」 |
94
- | `mergePoints(source, target, options?)` | 把来源节点并进目标节点(接口调用,不做确认):来源 / 目标收 id、下标或 `{ id }` / `{ index }`,节点数据按 `merge.createItem` 合并(缺省目标优先、尺寸取双方最大、图标优先目标),改线路绑定、删自环 / 零长线 / 自环通道、把撞成同一条的通道合成一条,返回 `{ ok, item, linesChanged, removedLines, removedChannels, mergedChannels }`;并不了返回 `ok: false` 并另发一条失败事件,见「节点合并」 |
97
+ | `mergePoints(source, target, options?)` | 把来源节点并进目标节点(接口调用,不做确认):来源 / 目标收 id、下标或 `{ id }` / `{ index }`,节点数据按 `merge.createItem` 合并(缺省以保留节点为底,名称 / 图标等业务字段取保留端,`merge.maxKeys` 列出的数值字段取双方最高、缺省不取,尺寸取双方最大、图标类型用保留端),线路 `from` / `to` 与通道 `from` / `to` 改到保留节点,删自环 / 零长线 / 自环通道、把撞成同一条的通道合成一条。保留节点写完整 `mergeInfo` 快照,受影响通道写轻量 `mergeInfo`;返回 `{ ok, item, linesChanged, removedLines, removedChannels, mergedChannels }`,并不了返回 `ok: false` 并另发一条失败事件,见「节点合并」 |
95
98
  | `mergeSelectedPoints(options?)` | 框选 / 多选之后一步把选中的节点并到一个目标上(接口调用):`target` 不指定就自动挑**绑定线路最多**的那一个(一样多取画得大的、再取下标小的),来源默认是除目标之外的选中节点、也可以用 `sources`(id / 下标 / `{ id }` / `{ index }`,单个或数组)直接给 —— 这时不用先选中;`mergeable: false` 默认跳过(`force: true` 越过)、`label` 改这一步历史的标签。**多个来源合成一步历史**(数据变更收成一条 `batch`),返回 `{ phase: 'mergeSelected', ok, reason, target, merged, skipped, sourcesMerged, linesChanged, removedLines, removedChannels, mergedChannels, channelsChanged }`,见「节点合并」 |
96
- | `splitPoint(source, parts, options?)` | `mergePoints` 的逆操作:把一个节点拆成两份或更多(第一份接在原下标上、缺省沿用原 id,其余追加到末尾),按每份的 `item` / 顶层字段生成新节点,按 `lines` 分配线路、按 `channels` / `channelFrom` / `channelTo` 分配受影响通道的两端。没显式分配的线路跟第一份;没显式分配的通道端点跟它第一条被分配过的成员回线。线路的 `from` / `to` 与和来源坐标重合的 `start` / `end` / `path` 顶点改到对应新节点;拆完后的自环 / 零长度线路与两端落到同一份的通道删掉,成员全没了的通道也删掉。整次调用只记一条历史、只发一条 `splitPoint` 数据变更,返回的 `changes` 可直接作为后端事务输入;至少两份、坐标必须有效、线路不能重复分配,写错时 `ok: false` 并在 `reason` 说明 |
99
+ | `splitPoint(source, parts?, options?)` | `mergePoints` 的逆操作。省略 `parts`(或传 `null` / `[]`)且节点带 `mergeInfo` 时自动解除合并:恢复第一次合并前的保留节点、全部来源节点、线路与通道,保留合并后新建且不在快照里的要素;来源 id 冲突 / 追溯不完整时 `ok: false` 且不动数据,整次一步历史。传 `parts` 时手工拆成两份或更多(第一份接在原下标上、缺省沿用原 id,其余追加),按每份 `item` / 顶层字段生成节点,按 `lines` 分配线路、按 `channels` / `channelFrom` / `channelTo` 分配通道两端;未分配线路默认跟第一份,`autoLines: 'alternate'` 按「通道成员成组、独立线路单条」轮流给负载最少的一份,未分配通道端点跟第一条被分配过的成员回线。`options.select` 缺省 `false`:不选中结果,只摘掉来源节点自己的选中态;写 `true` 才选中全部结果节点。拆完删自环 / 零长度线路、两端相同的通道与成员全空的通道。返回 `{ autoRestored, mergeSourceIds, autoLines, autoAssignedLines, lineAssignments, changes, ... }`,`changes` 可直接作为后端事务输入;手工拆分至少两份、坐标必须有效、线路不能重复分配,写错时 `ok: false` 并在 `reason` 说明 |
97
100
  | `getPointMerge()` / `resolvePointMerge(confirmed?, options?)` / `commitPointMerge()` / `revertPointMerge(options?)` | 读 / 裁决 / 收尾当前待确认的合并(把一个节点拖到另一个节点上之后):`resolvePointMerge(true, { keep, item })` 推进到 `confirm` 阶段 —— `keep` 选保留碰撞目标(缺省)还是拖动的来源,`item` 改写最终节点的信息与样式,位置 / id 始终取保留端;`resolvePointMerge(false)` 让拖动的节点回原位。`pending` / `confirm` / `getPointMerge()` 的载荷带 `impact`(受影响线路条数、具体 lineIds 与通道读数;`lineCount` 按线路去重,`linesChanged` / `removedLines` 与落地结果同口径)。自己接管数据落地时用 `commitPointMerge()` 收状态、`revertPointMerge()` 还原(`silent: true` 不补发 cancel 载荷) |
98
101
  | `removePoints(refs, options?)` | 移除节点(接口调用):`'a'` / `{ index: 2 }` / 数组都收,绑在它上面的线路一并删掉、声明了它当端点的整条通道(含成员回线)跟着删,返回 `{ ok, indexes, items, removedLines, removedChannels }` |
99
102
  | `requestRemove(options?)` | 发起一次删除(Delete 键同一条路):把选中的拐点 / 线路 / 节点与它们的连带影响摊成一份载荷交给 `onRemove` / `'remove'`,停在 `pending` 等裁决(确认方式见 `remove.confirm`),没有可删的东西时返回 `{ phase: 'empty', ok: false, reason }`,见「删除(remove)」 |
@@ -104,8 +107,8 @@
104
107
 
105
108
  | `exportImage(options?)` | 导出当前画面(含标签)为图片,返回 data URL;`scope: 'viewport'`(缺省)导当前视口、`'all'` 临时适应全部可见内容,另有 `padding` / `type` / `quality` / `scale` / `background` / `includeLabels` / `download`,见「导出图片」 |
106
109
  | `exportSelection(options?)` | 把选中的节点(可选带上两端都在集合里的线路)单独导成图片 / 数据:`points` 不给就用选中集,`withLines` / `format` / `padding` 见「导出选中的节点」;返回 `{ ok, reason, points, lines, counts, box, pointIndexes, lineIndexes, image }`,出图时临时藏起别的要素与相机、出完还原 |
107
- | `exportData(options?)` | **统一导出入口**:`scope` 选范围(`'all'` 缺省 / `'viewport'` / `'selection'`,也认 `visible` / `view` / `selected`),`parts` 选分项,`format` 选口径。`format: 'view'`(缺省)给 `{ points, lines, channels, parallelGroups }`、`'new'` 给 new.json 口径、`'test'` 给 test.json 口径、`'input'` 给与 `createGisView` 入参同构的回灌口径、`'full'` 给标准文档;`format` 也可以是注册过的自定义名字。`points` / `lines` / `channels` 可以只要一部分,解不出的引用进 `warnings`,`format` 写错(或没注册)抛异常,见「导出数据」与「自定义导出格式」 |
108
- | `snapshotScene(options?)` | 当前画面的矢量快照(屏幕坐标的线 / 点 / 标签 / 背景 / 网格):`{ width, height, background, backgroundImage, grid, regions, lines, points, backgroundPoints, decorations, waypoints, raised, labels }`,`options.background: false` 丢背景、`options.backgroundColor` 只换底色、`options.labels` 原样带进去,见「导出图片」 |
110
+ | `exportData(options?)` | **统一导出入口**:`scope` 选范围(`'all'` 缺省 / `'viewport'` / `'selection'`,也认 `visible` / `view` / `selected`),`parts` 选分项,`format` 选口径。`format: 'view'`(缺省)给 `{ points, lines, channels, parallelGroups }`、`'new'` 给 new.json 口径、`'test'` 给 test.json 口径、`'input'` 给与 `createGisView` 入参同构的回灌口径、`'full'` 给标准文档;`format` 也可以是注册过的自定义名字。`points` / `lines` / `channels` 可以只要一部分;`scope: 'viewport'` 可用 `selectionKinds` 按类型或 `lineEndpoints` / `channelEndpoints` 筛。解不出的引用进 `warnings`,`format` 写错(或没注册)抛异常,见「导出数据」与「自定义导出格式」 |
111
+ | `snapshotScene(options?)` | 当前画面的矢量快照(屏幕坐标的线 / 点 / 标签 / 背景 / 网格):`{ width, height, background, backgroundImage, grid, regions, lines, raisedLines, points, backgroundPoints, decorations, waypoints, raised, labels }`;`raisedLines` 是抬层的选中线路 / 通道,导出时排在普通节点之后、`raised` 端点节点之前。`options.background: false` 丢背景、`options.backgroundColor` 只换底色、`options.labels` 原样带进去,见「导出图片」 |
109
112
  | `addGraphic(graphic)` / `addGraphics(list)` | 新增扩展图形(SVG 路径 / 图片 / Canvas):世界坐标锚点、屏幕像素尺寸、可选 `scaleWithView`;`id` 已存在时按更新覆盖,返回归一化后的图形读数(批量返回数组)。只动图形层 DOM,不重排场景,见「扩展图形(graphic)」 |
110
113
  | `updateGraphic(id, patch)` | 改一个扩展图形的字段(写法与创建一致,`style.width` / `style.height` 也认),返回改后的读数;id 不存在返回 `null` |
111
114
  | `removeGraphic(id)` / `clearGraphics()` | 按 id 删一条 / 清空整层,分别返回删掉的读数与清掉的条数;节点 / 线路 / 标签 / 数据 / 历史都不受影响 |
@@ -129,22 +132,24 @@
129
132
  | `addChannelGroupWaypoint(channelId, groupId?, coordinate?)` | 给某个分组加拐点、把这一组单独拉走:不给坐标就按默认位置放一个(第 n 组沿法线往外让 n 档),其余分组一根拐点都不动,见「分组拐点」 |
130
133
  | `removeChannelGroupWaypoint(channelId, groupId?, pathIndex?)` | 删掉某个分组的拐点(缺省删最后一个) |
131
134
  | `runChannelAction(actionId, options?)` | 执行右键交互框里的动作:`select-group` / `deselect-group` / `add-waypoint` / `remove-waypoint` / `split` / `merge`(`options` 带 `channelId` / `groupId` / `coordinate` / `pathIndex` / `size`),返回执行之后的上下文 |
132
- | `beginAddChannel(options?)` | 进入「新增通道」:缺省点两个节点选起点 / 终点,或由 `options.from` 预指定起点、只需再点终点;`options` 还支持 `kind`(`'channel'` 新建通道 / `'line'` 新建独立线路,缺省 `'channel'`)/ `circuits`(一次加几条,缺省 1)/ `autoCommit` / `continuous` / `chain` / `name` / `lineId` / `id` / `lineProps` / `linePropsByMember` / `lineItems`(第二段,连同线路一起新建)/ `routing`(`'straight'` / `'orthogonal'`)/ `path`(中间拐点)/ `channelProps` / `channelDraftStyle`(本次草稿的预览样式,含 `dots` / `layer`)/ `handoff`(交接模式:ready 发完收起草稿,自己调 `addChannel` 落地)/ `select`(点中的节点顺手选中)/ `originalEvent`,见「新增通道」 |
133
- | `getAddChannelDraft()` | 当前新增通道草稿(两个节点的信息与可选方案),没有草稿时为 `null` |
135
+ | `beginAddChannel(options?)` | 进入「新增通道」:缺省点两个节点选起点 / 终点,或由 `options.from` 预指定起点、只需再点终点。`options` 支持 `autoCommit` / `continuous` / `chain` / `singleChannel` / `existingChannel` / `groupId` / `groupIndex` / `circuits` / `name` / `lineId` / `id` / `lineProps` / `linePropsByMember` / `lineData`(第二段)/ `routing` / `path` / `channelProps` / `channelDraftStyle` / `handoff` / `select`(只选中点中的节点)/ `originalEvent`。入口固定为通道语义;旧 `kind` 与 `lineItems` 不再生效。 |
136
+ | `beginAddLine(options?)` | 进入「新增线路」:与 `beginAddChannel` 共用拾取 / 预览 / 连续新增,入口固定为线路语义;不写 `existingChannel` 时只新建独立线路,显式写 `existingChannel: 'newGroup' / 'joinGroup' / 'auto'` 且两端已有通道时才把新回线挂进已有通道。旧 `beginAddChannel({ kind: 'line' })` 已移除,见「10 通道编辑与新增」。 |
137
+ | `getAddChannelDraft()` | 当前新增草稿(两个节点的信息、可选方案与 `lineData`),没有草稿时为 `null` |
134
138
  | `updateAddChannelStyle(patch?)` | 字段级合并当前草稿的预览样式(`color` / `width` / `dot` / `dotColor` / `dots` / `layer` / `arrow` / `arrowSize` / `arrowPositions` / `arrowReverse` / `arrowColor` / `circuitSpacing` / `opacity` / `customDraw`);传 `null` 回到全局 `channelDraftStyle`,没有草稿时什么也不做 |
135
- | `continueAddChannel(options?)` | 开下一段草稿:连续模式落地时内部自动调(发 `phase: 'restart'`),也可以手动调;草稿已经退出时按 `options` 重新进来 |
139
+ | `continueAddChannel(options?)` | 开下一段草稿:连续模式落地时内部自动调(发 `phase: 'restart'`),也可以手动调;草稿已经退出时按通道入口重新进来。只续配置,不能把当前段切换成线路;旧 `kind` 不生效。 |
136
140
  | `cancelAddChannel()` | 取消新增通道(草稿期间按 Esc 等价,会补一条 `phase: 'cancel'`) |
137
141
  | `endAddChannel()` | 结束新增通道:静默收掉视图上的示例假通道 / 线路(不补 `cancel` 事件),数据一个字节都不改 —— 结束事件里决定「不新建了」就用它 |
138
- | `commitAddChannel(input?, options?)` | 把已 `ready` 的草稿落到数据上。`input` 有五类:不传(用草稿 `preferred`)、旧 resolution(字符串,或只含 `type` / `groupId` / `groupIndex` / `channelId` 的对象)、直接线路对象(有 `from` / `to` / `path` / `kind: 'line'` 等线路形状)、直接通道对象(必须有 `lineIds`,缺省新建通道)、容器 `{ channel?, line?, lines?, lineData?, lineItems?, channelGroups?, group?, channelProps?, lineProps?, resolution?, ... }`。对象字段优先、没写的沿用草稿;`line` 缺省表示独立线路,`lines` 才是成员回线表;直接通道对象缺省新建,直接线路对象缺省按独立线路落地,要并进已有分组必须用容器并显式写 `resolution`。`options?: { auto?, continuous?, originalEvent? }` 只影响本次事件与连续草稿。方案读数不能整条回传,只取 `{ type, groupId, groupIndex, channelId }`。完整字段优先级与示例见「10 通道编辑与新增」的 `commitAddChannel` / 对象直传 |
142
+ | `commitAddChannel(input?, options?)` | 把已 `ready` 的草稿落到数据上。第一个参数只接受一个容器对象:`{ channel?, line?, lines? / lineData?, channelGroups? / group?, lineProps?, linePropsByMember?, channelProps?, path?, routing?, resolution?, from?, to?, name?, id? }`;字符串、数组和旧顶层分派字段(`kind` / `circuits` / `channelId` / `groupId` / `lineId` / `lineItems` / `lineIds` 等)都明确失败。`options?: { auto?, continuous?, originalEvent?, select? }`;`select: true` 才选中落地的新回线。完整优先级见「10 通道编辑与新增」。 |
139
143
  | `getChannelAddOptions(from, to, count?, kind?)` | 两个节点之间的新增方案:新通道 / 新分组 / 并进某一组 / 无法添加;`count` 是这一次要加的回数(余量按它判),`kind: 'line'` 时方案表为空(独立线路不挑方案) |
140
- | `getChannelBetween(from, to)` | 两个节点之间的通道读数(只读):有通道时 `hasChannel: true`,`channelId` / `channelIndex` / `groupCount` / `currentGroupId` 与 `groups` / `group` / `channel`(id / name / lineIds / 分组明细)都给全;没有通道时 `hasChannel: false`、`channel: null`、`groups: []`,端点认不出来返回 `null` —— 与 `getChannelAddOptions` 的读数同一份,界面做「这两个点之间已经有通道 / 有几组」的判断用它,不必算方案表 |
141
- | `addChannel(spec, lineData?, group?)` | 程序化新增通道(新签名,只新建):第一段通道信息(`from` / `to` / `name` / `id` / `path` 等通道字段),第二段线路数组 line_data —— 一条或一批(字符串 / 数字按 id 认),**按每项 id 建 `lineIds`**,第三段可选分组信息(`{ id?, lineIds?, path? }`,单个 / 一组一个)—— **第二段 / 第三段显式给了**才走这条新路;两个节点之间已经有通道时整条失败(`reason` 指路下面两条)。旧写法 `addChannel(input, lineItems?)`(带 `circuits` / `resolution` / `channelId` / `groupId` / `groupIndex` / `kind`,或只写 `{ from, to }`)照样认:按方案表新建或并进已有通道,第二段 `lineItems` 给了几条就建几条 |
142
- | `addChannelGroupLines(channelId, lineData, groupInfo?)` | 给已有通道**新加一组**回线(原 `addChannel` 的 `newGroup` 那一支):第二段线路数组 line_data 按每项 id 建回线,`groupInfo`(`{ id?, path? }`)给新分组的 id 与中间拐点,缺省 `${通道 id}::N`、不带拐点;通道 / 线路认不出来时 `ok: false` + `reason` |
143
- | `addChannelGroups(channelId, groups, options?)` | 给已有通道**一次新加多组**回线:`groups` 每项 `{ id?, path?, lineData \| lines \| lineItems }`,线路数据与 `addChannel` 第二段同一份写法;逐组落地但**只记一步历史、只发一条** `addChannelGroups` 数据变更,返回 `{ ok, reason, count, results, lineIds, groupIds, groups }`,`options.emit: false` 只落地不通知 |
144
- | `joinChannelGroupLines(channelId, groupId, lineData)` | 把新回线**并进已有通道的某一组**(原 `addChannel` 的 `joinGroup` 那一支):第二段线路数组 line_data,`groupId` 认分组 id 也认序号(从 0 起、按声明顺序),不传就挑第一个还有余量的分组 |
145
- | `addChannels(input, options?)` | `addChannel` 的批量版:一次新增多条通道 / 独立线路(每条是新签名或旧写法,逐条各自认;每项的 `routing` / `path` 走同一套解析,`path` 是中间拐点),逐条落地但**只重排一次场景、只记一步历史、只发一条** `addChannels` 数据变更;返回 `{ ok, count, results, lineIds, channelIds, items }`,失败条目在各自的 `reason` 里说明原因、不影响其余条目,`options.emit: false` 时不发 `onDataChange`,见「动态换数据与加载完成」 |
146
- | `addLine(input, lineItems?)` | 程序化新增独立线路:等价于 `addChannel({ ...input, kind: 'line' })`,只往 `lines` 里加、不碰任何通道(`resolution: 'newLine'` 也认);第二段 `lineItems` 同样认 |;`routing: 'orthogonal'` 自动正交折线,`path` 是中间拐点,落地成含两端的完整折线
147
- | `addPoint(item, options?)` / `addPoints(items, options?)` / `addLines(items, options?)` | 把**现成的数据对象**追加到末尾(批量导入、从别处拷过来、按坐标补几条):老数据一条不动,返回 `{ ok, count, indexes, items }`(`addPoint` 另有单条的 `index` / `item`),`options.emit: false` 时不发 `onDataChange`;与 `addChannel` / `addLine` 的分工是后者走「通道 / 草稿」那一套 |
144
+ | `getChannelBetween(from, to)` | 两个节点之间的通道读数(只读):有通道时 `hasChannel: true`,`channelId` / `channelIndex` / `groupCount` / `currentGroupId` 与 `groups` / `group` / `channel`(id / name / lineIds / 分组明细)都给全;没有通道时 `hasChannel: false`、`channel: null`、`groups: []`,端点认不出来返回 `null` |
145
+ | `getDirectConnections(pointIds)` | 一组节点之间的**直接**通道 / 线路(只读):两端都必须真实存在且都在传入的 id 集合里,只认通道端点或线路 `from` / `to`,不穿中间节点;通道先按 `channels` 顺序、线路再按 `lines` 顺序,通道与成员回线可同时命中。每项带 `type` / `from` / `to` / `item`,通道项另有 `channelId` / `channelIndex` / `lineIds`,线路项另有 `lineId` / `lineIndex`;没有结果返回 `[]` |
146
+ | `addChannel(spec, lineData?, group?, options?)` | 程序化**只新建**通道:第一段 `{ from, to, name?, id?, channelProps?, path?, routing?, lineData?, group? }`,第二段线路数据按每项 id 建 `lineIds`,第三段可选分组信息(`{ id?, lineIds?, path? }`)。两端已有通道时整条失败(`reason` 指路 `addChannelGroupLines` / `joinChannelGroupLines`)。`options?: { emit?, select? }`,缺省不选中新回线。 |
147
+ | `addChannelGroupLines(channelId, lineData?, groupInfo?, options?)` | 给已有通道**新加一组**回线:第二段按每项 id 建回线,`groupInfo`(`{ id?, groupId?, path?, ... }`)给新分组的 id 与中间拐点,缺省 `${通道 id}::N`、不带拐点;`options?: { emit?, select? }`,缺省不选中 |
148
+ | `addChannelGroups(channelId, groups, options?)` | 给已有通道**一次新加多组**回线:`groups` 每项 `{ id?, groupId?, path?, lineData? \| lines?, ... }`;旧 `lineItems` 会让整批明确失败且不写数据。逐组落地但只记一步历史、只发一条 `addChannelGroups` 数据变更;`options?: { emit?, label?, select? }` |
149
+ | `joinChannelGroupLines(channelId, groupId?, lineData?, options?)` | 把新回线**并进已有通道的某一组**:第二段线路数据,`groupId` 认分组 id 也认序号(从 0 起、按声明顺序),不传就挑第一个还有余量的分组;`options?: { emit?, select? }` |
150
+ | `addChannels(input, options?)` | `addChannel` 的批量版:一次新建多条通道(每项写法同 `addChannel`),逐条落地但只重排一次场景、只记一步历史、只发一条 `addChannels` 数据变更;返回 `{ ok, count, results, lineIds, channelIds, items }`,失败条目在各自 `reason` 里说明;`options?: { emit?, select? }`,`select` 一次选中全部成功的新回线 |
151
+ | `addLine(spec, lineData?, options?)` | 程序化新增独立线路:`spec` 为 `{ from, to, name?, id?, lineProps?, path?, routing?, lineData? }`,第二段每条按自己的 id 建;不碰任何通道。落地走追加式引擎路径,已有通道声明时也能增量加线;`options?: { emit?, select? }`,缺省不选中 |
152
+ | `addPoint(item, options?)` / `addPoints(items, options?)` / `addLines(items, options?)` | 把**现成的数据对象**追加到末尾(批量导入、从别处拷过来、按坐标补几条):老数据一条不动,返回 `{ ok, count, indexes, items }`(`addPoint` 另有单条的 `index` / `item`);`options?: { emit?, select? }`,`select: true` 选中这次新加的要素,缺省不选中 |
148
153
  | `removeLine(lineId)` | 移除一条线路(撤销刚落地的新增线路):挂在通道上的回线会从通道声明里一起摘掉,通道空了整条删掉,返回 `{ ok, line, lineId, index, channelId, removedChannel }` |
149
154
  | `removeLines(lineIds)` | 一次移除多条线路(传数组或单条 id):通道声明与分组跟着收拾、删空的通道整条走,返回 `{ ok, lineIds, indexes, lines, entries, removedChannels }` |
150
155
  | `removeChannel(channelId)` | 移除整条通道:它名下的回线一条条摘掉,返回 `{ ok, channelId, lineIds, lines, removedChannel }` |
@@ -187,7 +192,7 @@
187
192
  | `canUndo()` / `canRedo()` | 能不能撤销 / 重做(界面据此灰掉按钮) |
188
193
  | `getHistoryState()` / `getHistory()` | 历史读数 `{ enabled, limit, canUndo, canRedo, undoCount, redoCount }` / 已经记下的操作(从旧到新,`{ type, label, time }`) |
189
194
  | `clearHistory()` | 清空历史(撤销与重做两份栈一起),返回清掉的条数 |
190
- | `batch(fn, options?)` | 批量更新:回调里对 `updatePoint` / `updateLine` / `updateData` 的连续改动收成一条 `onDataChange`(`type: 'batch'`,`changes` 按调用顺序列出每一步)与一步历史,`options.label` 给这一步起名;收尾时筛选只重算一次、标签重排 / 提示框 / 鹰眼图各补一次(`onFilter` 也只发一条),可以嵌套(只有最外层收尾),见「批量更新」 |
195
+ | `batch(fn, options?)` | 批量增删改:回调里的连续改动(`patchPoints` / `patchLines` / `updateChannel`、`removePoints` / `removeLines` / `removeChannel`、分组拆分 / 删除 / 新增等)收成一条 `onDataChange`(`type: 'batch'`,`changes` 按调用顺序列出每一步)与一步历史,`options.label` 给这一步起名;收尾时筛选只重算一次、标签重排 / 提示框 / 鹰眼图各补一次(`onFilter` 也只发一条),可以嵌套(只有最外层收尾),见「批量更新」 |
191
196
  | `getQuality()` / `setQuality(next)` | 读当前画质读数 `{ mode, name, level, glow, ornament, scale, budgetMs, frameMs }` / 换一档(`'high'` / `'balanced'` / `'lean'` / `'fast'` / `'auto'` 或 `{ mode, budgetMs, minScale }`),换完发一条 `onQualityChange`,见「画质」 |
192
197
  | `updateOptions({ effects })` / `getEffects()` | 换 / 读动画效果的全局配置(`effects: false` 就是一处关掉):`getEffects()` 给 `{ enabled, respectReducedMotion, line, point }`,其中 `enabled` 是**运行时口径**(只在数据里逐条配了 `effect` 时也是 `true`),见「动画效果(effects)」 |
193
198
  | `getLineEffects()` | 这一帧动点的读数 `{ lineIndex, slot, x, y, angle, total, size }`(世界坐标 + 朝向 + 所在线路 + 沿线总长):自建覆盖层(跟着图标走的标签 / 提示)与读数面板用它,没动的时候是空表 |
@@ -201,7 +206,7 @@
201
206
 
202
207
  返回 `this` 的方法都可以链式调用;返回其它值的方法(`get*` / `list*` / `export*` / `search` / `pick*` / `add*` / `remove*` / `copy*` / `merge*` / `locate*` 这些)在下面的「参数速查」里都标了返回类型。
203
208
 
204
- **怎么确认「组件到底有哪些属性和方法」**:上面那张表按功能分组介绍,下面的「参数速查」把实例的**每个成员逐个列全**(当前 265 个:260 个方法 + `el` / `canvas` / `gl` / `cameraAnimating` / `dataVersion` 五个属性),`options` 表列全 96 个键,数据字段表列全 250 个字段。这三份清单不靠手抄 —— 它们直接对着源码与类型对账:
209
+ **怎么确认「组件到底有哪些属性和方法」**:上面那张表按功能分组介绍,下面的「参数速查」把实例的**每个成员逐个列全**(当前 273 个:268 个方法 + `el` / `canvas` / `gl` / `cameraAnimating` / `dataVersion` 五个属性),`options` 表列全 100 个键,数据字段表列全 266 个字段。这三份清单不靠手抄 —— 它们直接对着源码与类型对账:
205
210
 
206
211
  - `node docs/steps/gen-api-index.mjs`:从 `create.js` 的实例定义、本文档的两张表与 `types.d.ts` 生成 `docs/demos/api-index.js`(`API_SUMMARY` / `API_SECTIONS` / `API_OPTIONS`),demo 的「实例 API」面板读的就是它;带 `--check` 时只校验有没有过期。
207
212
  - `node docs/steps/check-doc-coverage.mjs`:10 项双向对账 —— 实例成员 × 文档 / × 参数速查 / × 类型、参数速查与实例方法主表各自反查过期条目、类型反查过期声明、数据字段 × 文档、options 键 × 文档 / options 表反查过期字段、demo 清单是否最新。加一个实例成员、改一个 options 键而没同步文档时,这个脚本会直接报出来。