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.
- package/README.md +14 -11
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +445 -241
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +65 -498
- package/docs/GisView.md +1 -1
- package/docs/GisView/345/256/236/347/216/260/345/216/237/347/220/206.md +1 -1
- 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
- package/docs/demos/BasicDemo.vue +144 -27
- package/docs/demos/BatchAddDemo.vue +133 -7
- package/docs/demos/ChannelDemo.vue +61 -0
- package/docs/demos/ConnectDemo.vue +164 -33
- package/docs/demos/CopyMergeDemo.vue +144 -42
- package/docs/demos/EventDemo.vue +29 -24
- package/docs/demos/LabelDemo.vue +5 -5
- package/docs/demos/api-index.js +53 -41
- package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +1 -1
- package/docs/demos/gis-view-demo/GisViewDemoStage.vue +1 -1
- package/docs/demos/gis-view-demo/panels/DataPanels.vue +1 -1
- package/docs/demos/gis-view-demo/useGisViewDemo.js +31 -33
- package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +1 -0
- package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +3 -0
- 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
- package/docs/from0/05-/347/233/270/346/234/272/344/270/216/344/272/244/344/272/222.md +1 -1
- 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
- package/docs/index.html +136 -13
- 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
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +1 -1
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +82 -14
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +23 -17
- 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
- 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
- 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
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +11 -7
- 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
- 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
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +28 -5
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +46 -14
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +5 -2
- 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
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +30 -22
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +168 -81
- 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
- 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
- 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
- package/docs/steps/README.md +201 -120
- package/docs/steps/bench-scale.html +1 -1
- package/docs/steps/index.html +222 -6
- package/docs/steps/selftest-actions.html +26 -25
- package/docs/steps/selftest-data.html +1 -1
- package/docs/steps/selftest-demos.html +290 -16
- package/docs/steps/selftest-docs.html +17 -6
- package/docs/steps/selftest-features.html +594 -54
- package/docs/steps/selftest-full.html +3 -3
- package/docs/steps/selftest-labels.html +82 -1
- package/docs/steps/selftest-perf.html +62 -7
- package/docs/steps/selftest-select.html +71 -6
- package/docs/steps/step-05-pick.js +4 -8
- package/docs/steps/step-07-channels.js +3 -5
- package/docs/steps/step-08-labels.js +3 -3
- package/docs/steps/step-10-full.js +3 -3
- package/docs/steps/step-12-events.js +41 -24
- 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
- package/package.json +1 -1
- 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
- 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
- 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
- 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
- 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,7 @@ const {
|
|
|
21
21
|
<p>Ctrl + 点击线路或通道加点之后,拐点不会分散写进每条回线,而是归到一个新的拐点集合里:一个分组一组(没拆分过就是一条通道一组),组里有 member_line_ids(哪些回线共用)与 group_route(两端 + 拐点),结构对齐 test.json 的 parallel_groups;「导出拐点集合」按钮会把这份集合复制到剪贴板,可以直接回写业务数据。</p>
|
|
22
22
|
<p>这一轮补上三块:① 通道可以分组了——默认一条通道一组,「拆分分组」按每组几条回线把它拆开(第一组沿用当前拐点、新分组的线路不带拐点),「合并分组」把其余分组并进第一组(被并入的线路跟随第一组的拐点,第一组没有拐点就一条都不带),加点 / 拖点只作用于同一分组,分组声明写回 channels[].groups;② 鹰眼图也支持规则——「规则沿用主图」只搬颜色与层级(主图的像素尺寸不会搬过来),「按电压单独配色」给小图自己配一份(半径缩到 1 ~ 2.6px、高电压用方块、zIndex 沿用电压分档),「分层」控制分层叠画、层级下拉只画某几层,「放大节点 / 放大线路」让节点与线路跟着小图自身的倍数放大(滚轮或 - / + 都算);③ 定向更新——updatePoint / updateLine / updateChannel 改一个节点、线路或通道就重渲染相关的视图,只重解析受影响的那一小片,其余要素与缓存一个字节都不动,鹰眼图与标签覆盖层跟着重画。</p>
|
|
23
23
|
<p>热力图这一轮把节点那一层补上了:合成数据的节点与线路都按一张「热区场」随机生成 max_loading_rate(在数据范围里撒 4 ~ 6 个热区,节点取自己坐标处的场值、线路取两端中点处的场值 —— 画出来是几团往外化开的色斑,而不是均匀噪点),作用对象因此可以切「节点 + 线路 + 通道」/「只按节点上色」/「线路 + 通道回线」/「只按通道自己的值」四档;面板上的「重新随机一份热力数据」能在任意数据集上现生成一份(换种子、按当前节点范围重建场,只改字段值,坐标与拓扑一动不动),真实数据 new.json 的节点本来没有这一列、线路那一列又恒为 1.5,点一下就能立刻看到热力。作用对象带上节点时图标不再跟着换:图标符号写了 tint(逐实例着色),填充按白色烘焙、由热力色逐实例染上,描边写 currentStrokeColor 不参与染色 —— 形状与描边还是那张图标,只是填充跟着值走;贴图缓存键里因此不再有逐点的热力色,900 节点时绘制调用仍然在 36 上下(以前一个节点一张贴图时会涨到约 1500,10 万级直接把主线程烤住),想看「完全不参与热力图」的效果就把节点从作用对象里去掉 —— 图标本身不会变成内置圆 / 方块。</p>
|
|
24
|
-
<p>这一轮补上六件事:① <b>节点复制</b>——按住 Alt(面板上可以改成别的键)点一个节点,复制品立刻压在源节点上跟着鼠标走,松开左键(不再看 Alt 什么时候松)或再点一下落地;也可以点面板上的「先点按钮再选节点」再点一个节点(复制品落在它旁边),或者选中节点按 Ctrl+C、把鼠标移到目标位置按 Ctrl+V。复制全程走 <b>onPointCopy</b>:start / move / commit / end / cancel(复制到剪贴板另发一条 clipboard),落地前的 commit 阶段可以 payload.cancel() 撤掉、payload.update()
|
|
24
|
+
<p>这一轮补上六件事:① <b>节点复制</b>——按住 Alt(面板上可以改成别的键)点一个节点,复制品立刻压在源节点上跟着鼠标走,松开左键(不再看 Alt 什么时候松)或再点一下落地;也可以点面板上的「先点按钮再选节点」再点一个节点(复制品落在它旁边),或者选中节点按 Ctrl+C、把鼠标移到目标位置按 Ctrl+V。复制全程走 <b>onPointCopy</b>:start / move / commit / end / cancel(复制到剪贴板另发一条 clipboard),落地前的 commit 阶段可以 payload.cancel() 撤掉、payload.update() 改数据,id 与整份数据由 copy.createId / copy.createItem 定;落地之后的 end 阶段还能 payload.revert() 整份回滚(面板上「复制后模拟接口失败」那个勾就是它)。② <b>节点合并</b>——把一个节点拖到另一个节点上(碰撞按两个节点画在屏幕上的半径之和 + merge.distance 判,整组拖动不参与),<b>组件不弹任何对话框</b>:pending 停在原地等使用方裁决,面板上的「合并确认」三档分别是「停住(由这个自写弹层来确认)」/ false 直接合并 / 函数返回 Promise(先调接口再决定,resolve(false) 取消);确认会先推进到 confirm 阶段并把合并结果给你(可以改,也可以在这一步因为「接口失败」整份撤掉),确认之后来源节点的数据并进目标(缺省 { ...来源, ...目标 },可用 merge.createItem 整份接管)、<b>尺寸取双方最大、图标优先目标</b>、绑在它上面的线路改绑、合并出来的自环与零长线删掉;两端因此撞成同一条的通道按 merge.channelGroups 处理(各自保留分组 / 合成一个分组,自环的整条删),读数里会带上改端点的通道数与并掉的通道数。取消或接口失败则把拖动的节点与跟随它的线路还原回原位,并补发一条拖拽结束。③ <b>所有快捷键都能改</b>——options.shortcuts 按动作配(框选 / 多选 / 加点 / 复制 / 复制到剪贴板 / 粘贴 / 取消 / 删除 / 适应视图),写法是纯修饰键(ctrl / meta / shift / alt / mod)、组合键(ctrl+c)、单键(escape / 0)或数组,写 false 表示不绑定;面板「视图 · 交互」里那四个下拉就是改框选 / 多选 / 加点 / 复制的修饰键——比如把框选改成 Shift,之后 Shift + 拖动才出选框,按住 Shift 时连光标变体也跟着走。④ <b>选中抬层</b>——selectionOnTop(缺省开)把选中的节点、拖动中的整组、待确认合并的目标节点与选中线路所在的整条通道画在所有要素之上:选中了就一定看得见,与 zIndex 无关;关掉就完全按 zIndex 与数据顺序画。⑤ <b>删除(Delete)</b>——选中拐点 / 线路 / 节点之后按 Delete(或面板上的「删除选中」)先出一份删除载荷:要删什么、连带什么、能按哪些范围删。<b>组件不弹界面</b>:pending 停在原地等 <b>confirm(scope)</b> / cancel(),Esc 也能取消;范围三档是「只删选中的线路」/「删除整个分组」/「删除整条通道」(后两档只在删的是通道回线、且这条通道分过组时才有),选中的拐点与节点一律一起删 —— <b>删节点会连挂在它身上的线路、以及拿它当端点的整条通道一起删</b>,载荷里的 lineCount / channelCount 就是这份连带读数;确认之后整段合成一条历史,撤销一步全回来;开关是 features.remove,快捷键照样走 shortcuts.remove(可以单独配成 shift+delete 之类)。⑥ <b>通道选中模式</b>——channelSelectMode 决定点中一条通道回线时选中什么:line(缺省,只选这一条)/ group(连它所在分组一起)/ channel(整条通道一起);在 group / channel 下 Ctrl + 点击是<b>整批取反</b>,通道以外的线路永远只选它自己。它只影响「下一次点选怎么摊开」,切换走 updateOptions,场景一个字节都不用重排。面板「视图 · 复制合并」里那两项就是它们。</p>
|
|
25
25
|
<p>定位口径这一轮统一了:<code>focusOn(target)</code> 缺省只把中心移到目标上,比例一点不动;要按目标范围适配必须显式写 <code>{ zoom: true }</code>,可再配 <code>padding</code> / <code>maxZoom</code>。节点、线路、通道与分组走同一条规则,搜索面板与画布菜单里需要适配的按钮都已经写成显式开启。</p>
|
|
26
26
|
</div>
|
|
27
27
|
<span class="demo-badge">WebGL2 · 无内置 GUI</span>
|
|
@@ -67,7 +67,7 @@ const {
|
|
|
67
67
|
<span>world ({{ canvasMenu.world[0].toFixed(2) }}, {{ canvasMenu.world[1].toFixed(2) }}) · 选中 {{ canvasMenu.selection?.count ?? 0 }} 个</span>
|
|
68
68
|
<button type="button" @click="closeCanvasMenu">×</button>
|
|
69
69
|
</div>
|
|
70
|
-
<p class="demo-channel-menu-hint">右键载荷(onContextMenu
|
|
70
|
+
<p class="demo-channel-menu-hint">右键载荷(onContextMenu)里带着命中的要素、world 与 originalEvent,选中集读数由 view.getSelectionInfo() / getSelectionData() 取:这份菜单整个写在 demo 里,组件不带任何界面。每一行都是一个公开方法;落在空白处先对 originalEvent 调 preventDefault 收掉浏览器自带菜单。空白处多一行「新增场站」:弹一份 demo 自己的表单,收集字段后走 addPoint,落点默认就是这一下右键的 world 坐标。</p>
|
|
71
71
|
<div class="demo-channel-menu-actions">
|
|
72
72
|
<button v-for="row in canvasMenuRows" :key="row.id" type="button" :data-demo="'canvas-menu-' + row.id" :disabled="!row.enabled" :title="row.reason" @click="runCanvasMenuAction(row)">{{ row.label }}</button>
|
|
73
73
|
</div>
|
|
@@ -179,7 +179,7 @@ const {
|
|
|
179
179
|
<span class="demo-control-hint">{{ channelDraftReadout }}</span>
|
|
180
180
|
</div>
|
|
181
181
|
<div class="demo-controls">
|
|
182
|
-
<details class="demo-hint-more"><summary>说明</summary><span class="demo-control-hint">新建通道(
|
|
182
|
+
<details class="demo-hint-more"><summary>说明</summary><span class="demo-control-hint">新建通道(beginAddChannel):两个节点之间没有通道就建一条,已有通道就变成新分组 / 并入某一组;「一次加几回」决定这一次往通道里加几条回线 —— 新通道 / 新分组 / 并入分组的余量都按它算,加不下的方案置灰(上限是 options.channelMaxGroups / channelGroupMaxLines)。新建线路(beginAddLine):只加线路、不挂任何通道,两个节点相同才不允许。两个入口各自固定语义,不能靠 continueAddChannel 中途切换;要换入口请点对应的新增按钮。勾上「交接模式」之后两个节点选完就先收起草稿(假通道消失),由 demo 自己在 onChannelDraft 的 ready 回调里调 addChannel 落地(面板的逐条属性走第二段 lineData;与「选完两个节点直接落地」同时勾时后者先落地)。选完两个节点后,结束事件(ready)的读数(hasChannel / hasGroup / groupCount / groups / group / channel / existingChannelId)摆到下面「结束事件」那一行;旁边的「收掉假通道不新建」演示 endAddChannel:静默收掉示例假通道、数据不动、不补 cancel 事件</span></details>
|
|
183
183
|
</div>
|
|
184
184
|
<div class="demo-controls">
|
|
185
185
|
<span class="demo-control-hint">onChannelDraftClick(草稿里每点一次都发):{{ draftClickText }}</span>
|
|
@@ -1426,7 +1426,7 @@ export const useGisViewDemo = () => {
|
|
|
1426
1426
|
if (!payload) {
|
|
1427
1427
|
return []
|
|
1428
1428
|
}
|
|
1429
|
-
const count =
|
|
1429
|
+
const count = viewRef.value?.getSelectionInfo().count ?? 0
|
|
1430
1430
|
return [
|
|
1431
1431
|
{ id: 'addStation', label: `新增场站(addPoint)· 落点 (${payload.world[0].toFixed(1)}, ${payload.world[1].toFixed(1)})`, enabled: !payload.hit, reason: '这一下命中了要素:在空白处右键才能新增场站' },
|
|
1432
1432
|
{ id: 'locate', label: `定位并适配选中(focusOn)${count ? ` · ${count} 个` : ''}`, enabled: count > 0, reason: '先在画布上选一个 / 一批要素' },
|
|
@@ -2806,7 +2806,7 @@ export const useGisViewDemo = () => {
|
|
|
2806
2806
|
selectionReadout.value = `左键载荷:${describeSelectionPayload(payload)}`
|
|
2807
2807
|
const pickedChannel = payload?.channelId === undefined ? '' : ` · 通道 ${String(payload.channelId)}${payload.groupId === undefined ? '' : ` / 分组 ${String(payload.groupId)}`}`
|
|
2808
2808
|
lastEvent.value = payload
|
|
2809
|
-
? `单击 ${formatFeature(payload)}${pickedChannel} · 世界 (${payload.coordinate[0].toFixed(4)}, ${payload.coordinate[1].toFixed(4)}) · ${payload.selected ? '已选中' : '未选中'}(点之前${payload.wasSelected ? '已选中' : '未选中'})· 选中集 ${
|
|
2809
|
+
? `单击 ${formatFeature(payload)}${pickedChannel} · 世界 (${payload.coordinate[0].toFixed(4)}, ${payload.coordinate[1].toFixed(4)}) · ${payload.selected ? '已选中' : '未选中'}(点之前${payload.wasSelected ? '已选中' : '未选中'})· 选中集 ${viewRef.value?.getSelectionInfo().count ?? 0} 个`
|
|
2810
2810
|
: '单击空白处,已清空选中。'
|
|
2811
2811
|
}
|
|
2812
2812
|
const handleDblClick = (payload) => {
|
|
@@ -2820,7 +2820,7 @@ export const useGisViewDemo = () => {
|
|
|
2820
2820
|
const target = payload.hit ? formatFeature(payload) : '空白处'
|
|
2821
2821
|
const channel = payload.channelId === undefined ? '' : ` · 通道 ${String(payload.channelId)}${payload.groupId === undefined ? '' : ` / 分组 ${String(payload.groupId)}`}`
|
|
2822
2822
|
const state = payload.selected === undefined ? '' : ` · ${payload.selected ? '已选中' : '未选中'}`
|
|
2823
|
-
return `${target}${channel}${state} · world (${payload.world[0].toFixed(4)}, ${payload.world[1].toFixed(4)}) · button ${payload.button}`
|
|
2823
|
+
return `${target}${channel}${state} · world (${payload.world[0].toFixed(4)}, ${payload.world[1].toFixed(4)}) · button ${payload.originalEvent?.button ?? '-'}`
|
|
2824
2824
|
}
|
|
2825
2825
|
const handlePointerDown = (payload) => {
|
|
2826
2826
|
writePointerText(`按下 ${describePointer(payload)}`)
|
|
@@ -2839,7 +2839,7 @@ export const useGisViewDemo = () => {
|
|
|
2839
2839
|
viewRef.value?.selectFeatures(payload.item.id)
|
|
2840
2840
|
}
|
|
2841
2841
|
// 选完再取一次选中集读数:面板上的「把选中的 N 个并拢」按的是点下去那一刻的实际选中集
|
|
2842
|
-
pointMenu.value = { ...payload, kept: payload.wasSelected === true
|
|
2842
|
+
pointMenu.value = { ...payload, kept: payload.wasSelected === true }
|
|
2843
2843
|
}
|
|
2844
2844
|
else {
|
|
2845
2845
|
pointMenu.value = null
|
|
@@ -2852,12 +2852,12 @@ export const useGisViewDemo = () => {
|
|
|
2852
2852
|
else {
|
|
2853
2853
|
closeChannelMenu()
|
|
2854
2854
|
// 空白处与「普通线路」在这里弹画布菜单
|
|
2855
|
-
canvasMenu.value =
|
|
2855
|
+
canvasMenu.value = payload
|
|
2856
2856
|
}
|
|
2857
2857
|
// 组件只发回调、不碰浏览器默认行为:右键菜单由使用方决定,这里先把自带的那份收掉
|
|
2858
2858
|
payload.originalEvent?.preventDefault?.()
|
|
2859
2859
|
}
|
|
2860
|
-
lastEvent.value = `右键 ${describePointer(payload)} · ${payload.type === 'point' ? (payload.wasSelected ? '命中已经选中的那一个:保留整份选中集' : '命中的没在选中集里:先把它选上再弹面板') : payload.channelContext ? '命中通道回线:同一份载荷的 channelContext 直接弹通道右键框' : '落在空白处 / 普通线路上:弹画布菜单(每一行都是一个公开方法)'} · 选中集 ${
|
|
2860
|
+
lastEvent.value = `右键 ${describePointer(payload)} · ${payload.type === 'point' ? (payload.wasSelected ? '命中已经选中的那一个:保留整份选中集' : '命中的没在选中集里:先把它选上再弹面板') : payload.channelContext ? '命中通道回线:同一份载荷的 channelContext 直接弹通道右键框' : '落在空白处 / 普通线路上:弹画布菜单(每一行都是一个公开方法)'} · 选中集 ${viewRef.value?.getSelectionInfo().count ?? 0} 个 · onContextMenu 每个右键都发(点 / 线 / 通道 / 空白),要自己弹菜单就在回调里 payload.originalEvent.preventDefault()`
|
|
2861
2861
|
}
|
|
2862
2862
|
const handleDrag = (payload) => {
|
|
2863
2863
|
const phase = payload.phase === 'start' ? '开始拖拽' : payload.phase === 'drag' ? '拖拽中' : '拖拽结束'
|
|
@@ -5397,8 +5397,8 @@ export const useGisViewDemo = () => {
|
|
|
5397
5397
|
const selectedItemsJson = ref('')
|
|
5398
5398
|
/**
|
|
5399
5399
|
* 指针载荷里的选中信息:selected(现在在不在选中集里)/ wasSelected(onClick 给「点之前」的那一份,
|
|
5400
|
-
* 右键与按下 /
|
|
5401
|
-
*
|
|
5400
|
+
* 右键与按下 / 松开就是当下);整份选中集读数改调 view.getSelectionInfo()。
|
|
5401
|
+
* 左键右键要自己接一套操作(自建面板、按命中分支)时,看快照 + 这一次读数就够了。
|
|
5402
5402
|
* Ctrl + 点击(多选取反)与 Ctrl + 拖动(框选)不走 onClick,那一下的结果看 onSelectChange。
|
|
5403
5403
|
*/
|
|
5404
5404
|
const describeSelectionPayload = (payload) => {
|
|
@@ -5409,7 +5409,7 @@ export const useGisViewDemo = () => {
|
|
|
5409
5409
|
const target = payload.type ? formatFeature(payload) : '空白处'
|
|
5410
5410
|
const state = payload.selected === true ? '现在已选中' : '现在未选中'
|
|
5411
5411
|
const before = payload.wasSelected === undefined ? '' : ` · ${payload.wasSelected ? '在这之前已选中' : '在这之前未选中'}`
|
|
5412
|
-
const info =
|
|
5412
|
+
const info = viewRef.value?.getSelectionInfo()
|
|
5413
5413
|
const bag = info ? ` · 选中集 ${info.count} 个(点 ${info.points} / 线 ${info.lines} / 通道 ${info.channels})${info.ids.length ? ` · ids ${info.ids.slice(0, 3).map(String).join(', ')}${info.ids.length > 3 ? '…' : ''}` : ''}` : ''
|
|
5414
5414
|
return `${target} · ${state}${before}${bag}`
|
|
5415
5415
|
}
|
|
@@ -5434,11 +5434,11 @@ export const useGisViewDemo = () => {
|
|
|
5434
5434
|
lines: data.lines.slice(0, 3).map((line, slot) => line.name ?? line.id ?? `lines[${data.lineIndexes[slot]}]`),
|
|
5435
5435
|
channels: data.channels.slice(0, 3).map(channel => channel.name ?? channel.id ?? '(无 id)')
|
|
5436
5436
|
}, null, 1)
|
|
5437
|
-
lastEvent.value = '选中信息:getSelectionData() 一份取全(ids / count / 点线通道条数 / 要素本身 / 下标 / bounds
|
|
5437
|
+
lastEvent.value = '选中信息:getSelectionData() 一份取全(ids / count / 点线通道条数 / 要素本身 / 下标 / bounds),指针 / 右键载荷不再挂这份读数,需要时直接调 getSelectionInfo()'
|
|
5438
5438
|
}
|
|
5439
5439
|
/**
|
|
5440
5440
|
* 节点右键面板:组件本身不弹任何菜单,右键载荷里就已经给全了 ——
|
|
5441
|
-
* wasSelected
|
|
5441
|
+
* wasSelected(命中前它在不在选中集里),整份选中集读数调 view.getSelectionInfo()。
|
|
5442
5442
|
* 这里的规矩是常见的那一套:右键已经选中的节点保留整份选中集,右键没选中的节点先把它选上再弹面板;
|
|
5443
5443
|
* 面板上的按钮各走一个公开方法(focusOn / copyPoint / mergeSelectedPoints / removePoints / selectFeatures(null))。
|
|
5444
5444
|
*/
|
|
@@ -5507,7 +5507,7 @@ export const useGisViewDemo = () => {
|
|
|
5507
5507
|
if (!payload) {
|
|
5508
5508
|
return []
|
|
5509
5509
|
}
|
|
5510
|
-
const count =
|
|
5510
|
+
const count = viewRef.value?.getSelectionInfo().count ?? 0
|
|
5511
5511
|
return [
|
|
5512
5512
|
{ id: 'focus', label: '聚焦到它(focusOn)', enabled: true, reason: '' },
|
|
5513
5513
|
{ id: 'copy', label: '复制一份(copyPoint)', enabled: true, reason: '' },
|
|
@@ -5733,13 +5733,13 @@ export const useGisViewDemo = () => {
|
|
|
5733
5733
|
const view = viewRef.value
|
|
5734
5734
|
const lineOnly = payload.kind === 'line'
|
|
5735
5735
|
// 面板上的逐条属性(一条 / 一批)就是第二段:按每项 id 建回线,这些 id 自动组成通道的 lineIds
|
|
5736
|
-
|
|
5736
|
+
const members = (payload.lineData ?? payload.linePropsByMember ?? []).filter(Boolean)
|
|
5737
5737
|
const baseProps = payload.lineProps ?? {}
|
|
5738
5738
|
const lineData = members.length
|
|
5739
5739
|
? members.map(item => ({ ...baseProps, ...item }))
|
|
5740
5740
|
: Array.from({ length: Math.max(payload.circuits ?? 1, 1) }, () => ({ ...baseProps }))
|
|
5741
5741
|
if (lineOnly) {
|
|
5742
|
-
const lineResult = view.addLine({ from: payload.from.id, to: payload.to.id, name: payload.name, lineProps: payload.lineProps }, lineData)
|
|
5742
|
+
const lineResult = view.addLine({ from: payload.from.id, to: payload.to.id, name: payload.name, lineProps: payload.lineProps }, lineData, { select: true })
|
|
5743
5743
|
if (lineResult.ok) {
|
|
5744
5744
|
lastAddedChannel.value = { lineIds: lineResult.lineIds ?? [], lineId: lineResult.lineId, channelId: null, resolution: lineResult.resolution }
|
|
5745
5745
|
syncChannelLabel()
|
|
@@ -5750,9 +5750,9 @@ export const useGisViewDemo = () => {
|
|
|
5750
5750
|
}
|
|
5751
5751
|
const result = payload.hasChannel && payload.channel
|
|
5752
5752
|
? (payload.hasGroup
|
|
5753
|
-
? view.joinChannelGroupLines(payload.channel.id, undefined, lineData)
|
|
5754
|
-
: view.addChannelGroupLines(payload.channel.id, lineData))
|
|
5755
|
-
: view.addChannel({ from: payload.from.id, to: payload.to.id, name: payload.name }, lineData)
|
|
5753
|
+
? view.joinChannelGroupLines(payload.channel.id, undefined, lineData, { select: true })
|
|
5754
|
+
: view.addChannelGroupLines(payload.channel.id, lineData, undefined, { select: true }))
|
|
5755
|
+
: view.addChannel({ from: payload.from.id, to: payload.to.id, name: payload.name }, lineData, undefined, { select: true })
|
|
5756
5756
|
if (result.ok) {
|
|
5757
5757
|
lastAddedChannel.value = { lineIds: result.lineIds ?? [], lineId: result.lineId, channelId: result.channelId, resolution: result.resolution }
|
|
5758
5758
|
syncChannelLabel()
|
|
@@ -5828,9 +5828,9 @@ export const useGisViewDemo = () => {
|
|
|
5828
5828
|
channelDraft.value = null
|
|
5829
5829
|
draftClickText.value = '—'
|
|
5830
5830
|
const lineOnly = kind === 'line'
|
|
5831
|
-
view.beginAddChannel
|
|
5832
|
-
|
|
5833
|
-
|
|
5831
|
+
const begin = lineOnly ? view.beginAddLine : view.beginAddChannel
|
|
5832
|
+
begin.call(view, {
|
|
5833
|
+
// circuits 是这一段一次加几回
|
|
5834
5834
|
circuits: addChannelCircuits.value,
|
|
5835
5835
|
autoCommit: addChannelAutoCommit.value,
|
|
5836
5836
|
// 连续新增:落地之后自动开下一段;'chain' 再以上一段终点当下一段起点
|
|
@@ -5849,16 +5849,15 @@ export const useGisViewDemo = () => {
|
|
|
5849
5849
|
? `新建线路:点两个节点,第二个点完直接落到图上(不挂通道${addChannelCircuits.value > 1 ? `,一次 ${addChannelCircuits.value} 条` : ''})`
|
|
5850
5850
|
: `新建通道(一次 ${addChannelCircuits.value} 回):点第一个节点当起点,再点第二个节点出方案${addChannelAutoCommit.value ? ',第二个点完直接落地' : '(下面的按钮选怎么加)'}${addChannelContinuous.value ? ';落地之后草稿自动重开,可以接着加下一条' : ''}`
|
|
5851
5851
|
}
|
|
5852
|
-
/**
|
|
5853
|
-
const continueAddChannel = (
|
|
5852
|
+
/** 手动开下一段草稿:连续模式落地时是自动的,这里演示「草稿退出之后接着加」 */
|
|
5853
|
+
const continueAddChannel = () => {
|
|
5854
5854
|
const view = viewRef.value
|
|
5855
5855
|
if (!view) {
|
|
5856
5856
|
return
|
|
5857
5857
|
}
|
|
5858
|
-
const lineOnly = kind === 'line'
|
|
5858
|
+
const lineOnly = channelDraft.value?.kind === 'line'
|
|
5859
5859
|
view.continueAddChannel({
|
|
5860
|
-
//
|
|
5861
|
-
kind: lineOnly ? 'line' : 'channel',
|
|
5860
|
+
// 入口语义由本次草稿决定;想切换通道 / 独立线路请点上面的两个新增按钮
|
|
5862
5861
|
circuits: addChannelCircuits.value,
|
|
5863
5862
|
autoCommit: addChannelAutoCommit.value,
|
|
5864
5863
|
continuous: addChannelContinuous.value ? (addChannelChain.value ? 'chain' : true) : false,
|
|
@@ -5936,7 +5935,7 @@ export const useGisViewDemo = () => {
|
|
|
5936
5935
|
if (!view || !draft) {
|
|
5937
5936
|
return
|
|
5938
5937
|
}
|
|
5939
|
-
const result = view.commitAddChannel(option ? { type: option.type, groupId: option.groupId } : undefined)
|
|
5938
|
+
const result = view.commitAddChannel(option ? { resolution: { type: option.type, groupId: option.groupId } } : undefined, { select: true })
|
|
5940
5939
|
channelDraft.value = null
|
|
5941
5940
|
if (result.ok) {
|
|
5942
5941
|
lastAddedChannel.value = { lineIds: result.lineIds ?? [], lineId: result.lineId, channelId: result.channelId, resolution: result.resolution }
|
|
@@ -5967,7 +5966,7 @@ export const useGisViewDemo = () => {
|
|
|
5967
5966
|
from: from.id,
|
|
5968
5967
|
to: target.id,
|
|
5969
5968
|
name: `新增通道 ${String(from.id)} ↔ ${String(target.id)}`
|
|
5970
|
-
}, Array.from({ length: Math.max(Math.trunc(Number(addChannelCircuits.value)) || 1, 1) }, (_item, index) => ({ color: '#f4a259', width: 3, ...(buildLinePropsByMember()?.[index] ?? {}) })))
|
|
5969
|
+
}, Array.from({ length: Math.max(Math.trunc(Number(addChannelCircuits.value)) || 1, 1) }, (_item, index) => ({ color: '#f4a259', width: 3, ...(buildLinePropsByMember()?.[index] ?? {}) })), undefined, { select: true })
|
|
5971
5970
|
if (result.ok) {
|
|
5972
5971
|
lastAddedChannel.value = { lineIds: result.lineIds ?? [], lineId: result.lineId, channelId: result.channelId, resolution: result.resolution }
|
|
5973
5972
|
}
|
|
@@ -5978,7 +5977,7 @@ export const useGisViewDemo = () => {
|
|
|
5978
5977
|
syncChannelGroupPanel()
|
|
5979
5978
|
syncParallelGroups()
|
|
5980
5979
|
}
|
|
5981
|
-
/**
|
|
5980
|
+
/** 程序化新增独立线路:不挂通道,一次加几回由「一次加几回」下拉决定 */
|
|
5982
5981
|
const addLineProgrammatic = () => {
|
|
5983
5982
|
const view = viewRef.value
|
|
5984
5983
|
if (!view) {
|
|
@@ -5991,19 +5990,18 @@ export const useGisViewDemo = () => {
|
|
|
5991
5990
|
lastEvent.value = '程序化新增线路:至少要有两个节点'
|
|
5992
5991
|
return
|
|
5993
5992
|
}
|
|
5993
|
+
const lineData = Array.from({ length: Math.max(Math.trunc(Number(addChannelCircuits.value)) || 1, 1) }, (_item, index) => ({ ...(buildLinePropsByMember()?.[index] ?? {}) }))
|
|
5994
5994
|
const result = view.addLine({
|
|
5995
5995
|
from: from.id,
|
|
5996
5996
|
to: target.id,
|
|
5997
|
-
circuits: addChannelCircuits.value,
|
|
5998
5997
|
name: `独立线路 ${String(from.id)} ↔ ${String(target.id)}`,
|
|
5999
|
-
lineProps: { color: '#25c2a0', width: 2.5 }
|
|
6000
|
-
|
|
6001
|
-
})
|
|
5998
|
+
lineProps: { color: '#25c2a0', width: 2.5 }
|
|
5999
|
+
}, lineData, { select: true })
|
|
6002
6000
|
if (result.ok) {
|
|
6003
6001
|
lastAddedChannel.value = { lineIds: result.lineIds ?? [], lineId: result.lineId, channelId: null, resolution: result.resolution }
|
|
6004
6002
|
}
|
|
6005
6003
|
lastEvent.value = result.ok
|
|
6006
|
-
? `程序化新增独立线路(一次 ${result.circuits} 条):${describeChannelAdd(result)}
|
|
6004
|
+
? `程序化新增独立线路(一次 ${result.circuits} 条):${describeChannelAdd(result)}(不挂通道,通道声明里不会有它)`
|
|
6007
6005
|
: `没能新增:${result.reason}`
|
|
6008
6006
|
syncChannelLabel()
|
|
6009
6007
|
syncChannelGroupPanel()
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
| `shadow` | `GisViewShadow` | 无(不画阴影) | 阴影:`{ color, blur, offsetX, offsetY }`,偏移与模糊都是 CSS 像素 |
|
|
16
16
|
| `visible` | `boolean` | `true` | `false` 时这个节点不画(等价于被筛选隐藏,但数据还在);被它锚定的通道与回线一起消失 |
|
|
17
17
|
| `clickable` / `draggable` / `copyable` / `mergeable` | `boolean` | 前两个跟随 `options.clickable` / `options.draggable`(**`options.draggable` 缺省关**:不写它就没有节点可拖,逐条的 `draggable: true` 也得等这个总开关或者 `features.drag` 打开才生效);`copyable` / `mergeable` 缺省 `true` | 逐条覆盖 `options` 上的同名开关 |
|
|
18
|
+
| `mergeInfo` | `GisViewMergeInfo \| null` | 无 | `mergePoints` / `mergeSelectedPoints` 写入的合并追溯(版本 `1`):保留节点保存 `targetPoint`、`sourceIds`、来源 `points` 快照与受影响 `lines` / `channels` 快照;`splitPoint(source)` 省略 `parts` 时据此自动解除合并。可 JSON 序列化,随数据导出;保留字段,业务数据不要占用。字段详解见 [manual 07 的「合并追溯」](../manual/07-%E6%8C%87%E9%92%88%E4%B8%8E%E8%A6%81%E7%B4%A0%E7%BC%96%E8%BE%91.md#%E5%90%88%E5%B9%B6%E8%BF%BD%E6%BA%AFmergeinfo) |
|
|
18
19
|
| `cursor` / `ctrlCursor` | `GisViewCursorValue` | 跟随 `options.cursorStyle.point` / `cursorStyle.ctrl.point` | 悬停这个点时的光标(按住 Ctrl / ⌘ 时用后者),写法见 GisView.md 的「光标」 |
|
|
19
20
|
| `hoverStyle` / `selectStyle` / `multiSelectStyle` / `boxSelectStyle` | `GisViewPointStyle` | 分别跟随 `options.pointHoverStyle` / `pointSelectStyle` / `pointMultiSelectStyle` / `pointBoxSelectStyle`(后两个缺省回落到选中样式) | 逐条覆盖悬停 / 选中 / Ctrl 多选 / 框选样式,按字段覆盖自身样式 |
|
|
20
21
|
| `labels` | `GisViewItemLabel \| GisViewItemLabel[]` | 无(不写且没配 `label` 就不画标签) | 这个点上的标签清单(可以多条),见下文「标签」;`labelVisible` / `labelVisibility` / `labelPosition` / `labelOffset` 是旧式单条写法 |
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
| `from` / `to` | `string \| number` | **必填** | 两端节点 id,节点不存在时整条通道与回线一起不显示;两端都要落在节点上,只声明一端不行 |
|
|
12
12
|
| `circuits` | `number` | `1` | 回线数量(`createChannels` 用它展开回线) |
|
|
13
13
|
| `lineIds` | `Array<string \| number>` | 无(`createChannels` 生成的那一份就是它) | 组成这条通道的线路 id,**顺序即回线顺序**(沿骨架左法线从负到正) |
|
|
14
|
+
| `mergeInfo` | `GisViewMergeInfo \| null` | 无 | 合并后仍存在的受影响通道带轻量追溯:`{ version, targetId, sourceIds, originalIds }`;`originalIds` 是参与合并、被改端点或被并掉的原始通道 id,便于后端对账。可 JSON 序列化;保留字段,业务数据不要占用。完整快照在保留节点的 `mergeInfo` 上 |
|
|
14
15
|
| `path` | `GisViewCoordinate[]` | 无(两端直连) | 骨架的**中间拐点**(不含两端,两端由节点坐标补齐):与线路的 `path` 相反,这里只写中间 |
|
|
15
16
|
| `groups` | `GisViewChannelGroup[]` | 无(= 一个通道一组) | 分组声明:`[{ id, lineIds, path, waypointOffsets? }]`,一组一条走廊;拆分 / 合并的结果写在这里,见「分组」 |
|
|
16
17
|
| `width` / `offsetMode` / `inset` / `tangentInset` | `number` / `'tangent' \| 'inset'` / `number` / `number \| null` | 分别回落到 `options.channelWidth`(`0.02`)/ `'tangent'` / `options.channelInset`(`0`)/ `options.channelTangentInset`(`null` = 按回线线宽收) | 通道外观:宽度(`tangent` 下是最外侧两条回线的中心距)、偏移方式、内收比例(负数为外收)、两端切点内收 |
|
|
@@ -19,6 +20,8 @@
|
|
|
19
20
|
| `lineProps` / `linePropsByMember` | `Partial<GisViewLine>` / 数组 | 无 | 回线上透传的业务字段(`lineProps` 全体共用、`linePropsByMember[i]` 只作用第 i 回,后者优先) |
|
|
20
21
|
| `channelProps` | `Partial<GisViewChannel>` | 无 | 通道上额外透传的字段(原样带到通道声明里) |
|
|
21
22
|
|
|
23
|
+
`channelSimplify` 是视图级渲染选项(`options.channelSimplify`),不是通道数据字段:它可以让分组在回线数超过阈值时只画一条代表线,但不写入 `channels`、不改 `lineIds` / `groups`,也不会出现在 `exportData()` 的通道字段里。
|
|
24
|
+
|
|
22
25
|
### 通道分组(拐点分组 / parallel_groups)
|
|
23
26
|
|
|
24
27
|
- **一组**(默认):`lineIds` 是全部成员、`path` 是这一组的拐点;`getChannelGroups(id)` 返回一组。
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
| `type` | 命中的要素类型:`'point'` / `'line'`(通道回线也是 `'line'`) |
|
|
12
12
|
| `index` / `item` | 在 `points` / `lines` 里的下标与数据对象(空白处没有这两项) |
|
|
13
13
|
| `screen` / `coordinate` / `distance` | 命中点:画布 CSS 像素、世界坐标、拾取距离(像素) |
|
|
14
|
-
| `
|
|
15
|
-
| `selected` / `wasSelected` | 这一刻 / 点之前在不在选中集里 |
|
|
14
|
+
| `selected` / `wasSelected` | 这一刻 / 点之前在不在选中集里(事件快照;整份计数用 `getSelectionInfo()`) |
|
|
16
15
|
| `channelIndex` / `channel` / `groupId` / `group` | 命中通道回线时多给的通道读数(`channel` 是通道定义,`group` 是分组读数) |
|
|
17
16
|
| `action` | 这次操作的判读(见下) |
|
|
18
17
|
| `lngLat` | 配了 GeoJSON 底图时的经纬度 |
|
|
19
18
|
|
|
20
19
|
`action` 是同一套写法:`type` 是动作(`select` / `clearSelection` / `click` / `hover` / `contextMenu` /
|
|
21
20
|
`dragPoint` / `boxSelect` / `pan` / `selectLabel` / `dragLabel` / `dblClick`…)、`target` 是目标
|
|
22
|
-
(`point` / `line` / `channel` / `empty` / `label` / `selection`)、`
|
|
23
|
-
|
|
24
|
-
`
|
|
21
|
+
(`point` / `line` / `channel` / `empty` / `label` / `selection`)、`moved` / `double` / `press` / `duration`
|
|
22
|
+
说明拖没拖、是不是双击的第二下、按了多久。命中的数据在载荷顶层:点 / 线读 `item`,通道回线读
|
|
23
|
+
`channel` / `channelContext`,标签读 `label` / `parent`,拐点读 `lineIndex` / `pathIndex` / `coordinate` / `line`。
|
|
24
|
+
鼠标键、修饰键与 clientX / clientY 读 `payload.originalEvent`;整份选中集读数用 `view.getSelectionInfo()`。
|
|
25
25
|
|
|
26
26
|
### 选中集
|
|
27
27
|
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
| `onLabelClick` / `onLabelDrag` / `onLabelContextMenu` / `onLabelDblClick` | `kind` / `index` / `id` / `labelId` / `item` / `label`(这一条标签的 id、内容、来源、合并后的样式)/ `parent`(挂在哪条要素上,线路在通道里时带通道 / 分组)/ `state` |
|
|
42
42
|
| `onQualityChange` | `{ mode, name, level, glow, ornament, scale, budgetMs, frameMs }` |
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
`pointerDown` / `pointerUp` / `contextMenu` 与其余事件一样是「回调一份载荷、总线同一份载荷」,不再额外包
|
|
45
|
+
`{ payload, event }`;要原始 DOM 事件就读 `payload.originalEvent`。通道回线的右键上下文在同一份
|
|
46
46
|
`contextMenu` 载荷的 `channelContext` 上。
|
|
47
47
|
|
|
48
48
|
### 自建面板常用的读数
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
| `listPoints(options)` / `listLines(options)` | 带 `index` / `item` / 几何 / 颜色的快照,`withScreen: true` 直接给屏幕坐标 |
|
|
54
54
|
| `getLineChannel(ref)` | 这条回线属于哪条通道、哪一组(`{ lineIndex, channelId, channelIndex, channel, groupId, group }`) |
|
|
55
55
|
| `isSelected(ref)` | 这个要素在不在选中集里 |
|
|
56
|
+
| `getSelectionInfo()` | 选中集轻量计数:`{ ids, count, points, lines, channels }`;指针 / 点击 / 悬停载荷不再重复挂这一份 |
|
|
56
57
|
| `getSelectionData()` | 选中集一次取全:读数(`ids` / `count` / `pointCount` / `lineCount` / `channelCount`)/ 要素本身(`points` / `lines` / `channels`)/ `items` / 下标 / 世界包围盒 `bounds` |
|
|
57
58
|
|
|
58
59
|
## 示例数据
|
|
@@ -152,7 +152,7 @@ export const DEFAULT_SHORTCUTS = {
|
|
|
152
152
|
|
|
153
153
|
② **不能被拾取**。预览跟着鼠标走,光标十有八九正压在它上面;如果 `pick()` 会命中它,鼠标一移过去就会「停在预览上」。所以 `pick` 与 `pickDraggablePoint` 都跳过 `pointCopy.index`,合并的候选里也要排除它。
|
|
154
154
|
|
|
155
|
-
③ **要能改**。使用方可能想在落地前改复制品的名字、颜色、业务字段 —— 事件载荷里给 `update(patch)
|
|
155
|
+
③ **要能改**。使用方可能想在落地前改复制品的名字、颜色、业务字段 —— 事件载荷里给 `update(patch)`,内部只重解析这一个节点(`setCopyPreviewItem` 复用 `updatePoint` 那条增量路径),坐标仍以预览落点为准(`payload.item.x` / `payload.item.y`)。
|
|
156
156
|
|
|
157
157
|
落地分两步,中间留了一个可以反悔的口子:
|
|
158
158
|
|
|
@@ -274,24 +274,40 @@ const shift = ordinal * (span + gap) // 第 1 组原地不动(ordinal 为 0
|
|
|
274
274
|
- `setCurrentChannelGroup(channelId, groupId)` 记下「当前分组」并把它整组选中,右键框与分组列表共用这一份状态;
|
|
275
275
|
- `runChannelAction(actionId, options)` 把界面的动作转成引擎调用,返回执行之后的新上下文。
|
|
276
276
|
|
|
277
|
-
|
|
277
|
+
新增流程走同一条思路,入口按语义拆成 `beginAddChannel()`(新建通道)与 `beginAddLine()`(新建线路):
|
|
278
|
+
两者都进入草稿态 → 只认节点(点空白继续等)→ 第二个节点点完给方案表
|
|
278
279
|
(`newChannel` / `newGroup` / `joinGroup`,按 `channelMaxGroups` / `channelGroupMaxLines` 标 `enabled`),
|
|
279
280
|
`applyChannelAdd`(实例层)把方案落到数据副本:新回线 id 防撞、`groups` 声明按引擎现状重写、新组不带拐点、
|
|
280
281
|
并入就塞进目标组。草稿期间数据整份换过要在 `validateChannelDraft` 里作废,否则会拿着旧下标去落点。
|
|
281
282
|
|
|
282
|
-
|
|
283
|
+
一次加几回、加不加通道,由入口语义与 `circuits` / `lineData` 决定(草稿载荷里的 `kind` 只读):
|
|
283
284
|
|
|
284
285
|
- **余量按「这一次加几回」算**:`count` 传进 `buildChannelAddOptions` 之后,`newGroup` 要「还能新分组」且
|
|
285
286
|
`count <= channelGroupMaxLines`,`joinGroup` 要这一组剩下的余量 `>= count`(不可用时 `reason` 写清
|
|
286
287
|
「只剩 R 回余量,本次要加 M 回」)。一条条加的时候这些判断都等价于「还能不能加一条」,所以旧代码不用改,
|
|
287
288
|
只是把「1」换成调用方给的数。
|
|
288
289
|
- **落地一次生成一批**:`applyChannelAdd` 按 `count` 循环,每条 id 都查一次重(`uniqueLineId`)、名字加 `#序号`,
|
|
289
|
-
`newChannel` 把这一批 `lineIds` 整体写进新通道,`newGroup` / `joinGroup`
|
|
290
|
+
`newChannel` 把这一批 `lineIds` 整体写进新通道,`newGroup` / `joinGroup` 整体并进目标分组。落地后缺省不改变
|
|
291
|
+
选中集;需要选中整批新回线时,由 `commitAddChannel(..., { select: true })` 或程序化新增的 `options.select` 打开。
|
|
290
292
|
结果里 `lines` / `lineIds` 是这一批(`line` / `lineId` 是第一条),界面「移除上一条新增」按它整批摘。
|
|
291
|
-
-
|
|
292
|
-
`
|
|
293
|
-
`
|
|
294
|
-
`
|
|
293
|
+
- **新建线路默认不碰通道**:`beginAddLine()` 的 `kind` 恒为 `line`,不写 `existingChannel` 时方案表留空,
|
|
294
|
+
`applyChannelAdd` 只往 `lines` 里加,`channels` 一个字节都不动;程序化落地就是
|
|
295
|
+
`addLine({ from, to }, lineData)`(一次多回由 `lineData` 条数决定)。两端已有通道且显式写
|
|
296
|
+
`existingChannel: 'newGroup' / 'joinGroup' / 'auto'` 时,方案表才会改走通道分组,把这批新回线挂进已有通道。
|
|
297
|
+
`beginAddChannel({ kind: 'line' })` 已移除;`continueAddChannel(...)` 只续配置,不能在下一段切换通道 / 线路入口。
|
|
298
|
+
|
|
299
|
+
**已有通道策略**由草稿的 `singleChannel` / `existingChannel` 决定,方案表仍负责计算「能不能加」:
|
|
300
|
+
|
|
301
|
+
- `singleChannel: true` 只对 `beginAddChannel` 生效,等价于缺省 `existingChannel: 'block'`;两端已有通道时
|
|
302
|
+
`ready` 载荷带 `blocked: true`,提交返回失败并发 `phase: 'blocked'`,不会自动新建分组。
|
|
303
|
+
- `existingChannel: 'newGroup'` 固定新加一组;`'joinGroup'` 用 `groupId` / `groupIndex`(从 0 起)指定分组;
|
|
304
|
+
`'auto'` 有新分组余量时优先新分组,否则并进可用分组;`'block'` 直接失败。
|
|
305
|
+
- 没有已有通道时,这些策略不改变「新建通道」;`beginAddLine` 在没有通道时始终落成独立线路。
|
|
306
|
+
- 阻断只影响**缺省提交**:使用方显式传一个可用的 `resolution`,仍可把这次新增改道到新分组 / 指定分组。
|
|
307
|
+
|
|
308
|
+
解除合并(`splitPoint`)和通道分组共用同一个「成员不能拆散」的约束:`options.autoLines: 'alternate'`
|
|
309
|
+
把同一条通道的成员当成一个分配单位、独立线路各算一个单位,轮流给负载最少的新节点;返回的
|
|
310
|
+
`autoAssignedLines` 与 `lineAssignments` 让界面直接显示每份接到哪些回线、通道是否仍成组。
|
|
295
311
|
|
|
296
312
|
### 6.13 每点一次都要能拿到信息:草稿回调与连续新增
|
|
297
313
|
|
|
@@ -315,9 +331,9 @@ const shift = ordinal * (span + gap) // 第 1 组原地不动(ordinal 为 0
|
|
|
315
331
|
|
|
316
332
|
同理,鼠标的每一种动作都应当有一条平行的回调,且载荷里直接带上「当前是哪个元素」:
|
|
317
333
|
`onHover`(悬停)、`onClick`(单击)、`onDblClick`(双击)、`onPointerDown` / `onPointerUp`(左 / 中 / 右键
|
|
318
|
-
按下与抬起)、`onContextMenu
|
|
319
|
-
把拾取结果摊平(`type` / `index` / `item`
|
|
320
|
-
|
|
334
|
+
按下与抬起)、`onContextMenu`(右键)。后三条都只收一个 `payload`,统一由 `buildPointerPayload(pick(x, y), point, event)` 造:
|
|
335
|
+
把拾取结果摊平(`type` / `index` / `item` 与通道字段),光标位置放 `world` / `x` / `y`;按键、修饰键与 `clientX` / `clientY`
|
|
336
|
+
读 `payload.originalEvent`,选中集读数调 `getSelectionInfo()`。命中空白处 `hit` 为 false。要自己弹右键菜单就在回调里 `payload.originalEvent.preventDefault()`,
|
|
321
337
|
引擎只负责「告诉你点到了什么」,不替你决定界面长什么样。
|
|
322
338
|
|
|
323
339
|
|