gis-view-webgl2 0.1.7 → 0.1.8
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 +9 -8
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +232 -228
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +49 -0
- package/docs/GisView.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 +48 -0
- package/docs/demos/BasicDemo.vue +2 -2
- package/docs/demos/BatchAddDemo.vue +6 -6
- package/docs/demos/ConnectDemo.vue +164 -33
- package/docs/demos/CopyMergeDemo.vue +27 -14
- package/docs/demos/EventDemo.vue +17 -20
- package/docs/demos/LabelDemo.vue +5 -5
- package/docs/demos/api-index.js +31 -29
- 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/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/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +1 -1
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +21 -16
- 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 +54 -31
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +3 -3
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +9 -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 +2 -2
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +12 -13
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +23 -21
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +19 -17
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +125 -78
- 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 +2 -2
- 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 +4 -4
- package/docs/steps/README.md +9 -7
- package/docs/steps/bench-scale.html +1 -1
- package/docs/steps/index.html +6 -6
- package/docs/steps/selftest-actions.html +26 -25
- package/docs/steps/selftest-data.html +1 -1
- package/docs/steps/selftest-demos.html +102 -6
- package/docs/steps/selftest-docs.html +3 -3
- package/docs/steps/selftest-features.html +283 -51
- package/docs/steps/selftest-full.html +3 -3
- package/docs/steps/selftest-labels.html +1 -1
- package/docs/steps/selftest-perf.html +31 -4
- package/docs/steps/selftest-select.html +20 -5
- 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/package.json +1 -1
|
@@ -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()
|
|
@@ -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
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## 坐标、相机与像素
|
|
6
6
|
|
|
7
7
|
- **世界坐标**:`x` / `y` 就是数据里的数值(经纬度、平面坐标都行),组件不做投影、不做坐标轴翻转。数据范围由节点与线路顶点共同决定(`getBounds()`)。
|
|
8
|
-
- **相机**:`{ center: [x, y], scale }`,`scale` 的单位是**屏幕 CSS 像素 / 世界单位**(`getCamera()` / `setCamera()`)。`fitView(padding)` 适应整份数据,`zoomToBounds(bounds, padding)` 适应一段范围,`zoomTo(scale, origin)` 直接设比例,`zoomBy(factor, origin)` 按锚点缩放,`focusOn(target
|
|
8
|
+
- **相机**:`{ center: [x, y], scale }`,`scale` 的单位是**屏幕 CSS 像素 / 世界单位**(`getCamera()` / `setCamera()`)。`fitView(padding)` 适应整份数据,`zoomToBounds(bounds, padding)` 适应一段范围,`zoomTo(scale, origin)` 直接设比例,`zoomBy(factor, origin)` 按锚点缩放,`focusOn(target)` 缺省只挪中心。
|
|
9
9
|
- **滚轮缩放**:以光标为锚点,`deltaY` 100 对应 1.12 倍 —— 缩放量按 `deltaY` 的大小算(`deltaMode` 是行 / 页时先折算成像素),触摸板的连续小位移因此是平滑的小步长,鼠标滚轮的一格仍是原来那一步,一次甩得再大也有上限(`deltaY` 夹在 ±240,免得某个设备一次甩出十倍)。**缩放缺省是关的**(`interactive` / `zoom` 都没写时滚轮完全不接),写 `interactive: true` 或 `features: { zoom: true }` / `features: 'view'` 才接;接着的时候一律 `preventDefault()`(含 Ctrl + 滚轮的浏览器缩放)。
|
|
10
10
|
- **三档坐标口径**(`world` 不多余:数据 `x` / `y` 就是世界坐标,事件载荷里叫 `world` 只是标口径;三档之间是「客户端坐标 ↔ 画布 CSS 像素 ↔ 世界坐标」的换算链):
|
|
11
11
|
- **世界坐标(`x` / `y`)**:数据、相机、几何读数、`focusOn` 与拖拽落点用的都是这一套,没有第二套;
|
|
@@ -7,20 +7,20 @@
|
|
|
7
7
|
| 字段 | 载荷 | 说明 |
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| `onReady` | `{ canvas, gl }` | WebGL2 上下文与缓冲区就绪 |
|
|
10
|
-
| `onSelect` | `GisViewSelectPayload \| null` | 选中操作回调:回答「这一下选了什么」。真实单击 / Ctrl / `multiSelect` 取反 / 框选 / `selectAt()` 每次成功操作都发(清空与「取反取没了」的载荷是 `null`);`selectFeatures()` / `deselect()` / `focusOn({ select })` 等按引用接口只在选中集实际变化时发。载荷带 `items` / `box` / `originalEvent` / `mode`;单通道时顶层还有 `channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups
|
|
11
|
-
| `onClick` | `GisViewClickPayload \| null` | 单击要素或空白处(空白处为 `null
|
|
12
|
-
| `onClusterClick` | `GisViewClusterClickPayload` | 点中多成员聚合圆:带 `id` / `count` / `pointIndexes` / `pointIds` / `bounds` / `radius` / `coordinate` / `screen` / `
|
|
10
|
+
| `onSelect` | `GisViewSelectPayload \| null` | 选中操作回调:回答「这一下选了什么」。真实单击 / Ctrl / `multiSelect` 取反 / 框选 / `selectAt()` 每次成功操作都发(清空与「取反取没了」的载荷是 `null`);`selectFeatures()` / `deselect()` / `focusOn({ select })` 等按引用接口只在选中集实际变化时发。载荷带 `items` / `box` / `originalEvent` / `mode`;单通道时顶层还有 `channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups` / `lineIndexes` / `lineIds`,多通道看 `channelContexts`。`items[]` 里的回线不挂任何通道字段,归属统一从外层映射或 `getLineChannel()` 取 |
|
|
11
|
+
| `onClick` | `GisViewClickPayload \| null` | 单击要素或空白处(空白处为 `null`):命中数据读 `type` / `index` / `item` / `screen` / `coordinate`,`selected` / `wasSelected` 是点完之后 / 点之前的选中态快照;按键、修饰键与指针位置读 `originalEvent`,整份选中集读数用 `view.getSelectionInfo()`(回调里就是 `payload.item` 所在视图的那一份)。另有 `action`(`type` 是 `select` / `toggleSelect` / `clearSelection` / `click`,`double` 说明这一下是不是双击里的第二下,`press` / `duration` / `longPress` 说明这一次按下想做什么、按了多久、算不算长按),见「指针事件」。`selectTrigger: 'dblClick'` 时第一下只发 `action.type: 'click'`,第二下才应用选中并带 `double: true` |
|
|
12
|
+
| `onClusterClick` | `GisViewClusterClickPayload` | 点中多成员聚合圆:带 `id` / `count` / `pointIndexes` / `pointIds` / `bounds` / `radius` / `coordinate` / `screen` / `originalEvent` / `action`(`action.type` 恒为 `clusterClick`、`action.target` 为 `cluster`)。这一条**不会同时发 `onClick`**,也不会改动选中集;聚合圆内部的原始节点不参与普通点击。选中集读数用 `getSelectionInfo()` |
|
|
13
13
|
| `onDblClick` | `GisViewClickPayload \| null` | 双击要素或空白处(320ms / 6px 内连续两次单击):`action.type` 是 `dblClick`、`action.double` 为 `true`。普通双击在两种 `selectTrigger` 下都会于第二下派发;`dblClick` 模式下选中 / 清空 / 多选取反也在这一下应用,`click` 模式下第一下已经应用过。`click` 模式的 Ctrl / `multiSelect` 取反快捷路保持原口径,不额外发 `onClick` / `onDblClick` |
|
|
14
|
-
| `onPointerDown` | `GisViewPointerPayload` | 左键 / 中键 /
|
|
14
|
+
| `onPointerDown` | `GisViewPointerPayload` | 左键 / 中键 / 右键按下:命中的要素与光标位置在载荷顶层;`action.type` 是这一次按下的**意图**(`dragPoint` / `dragWaypoint` / `boxSelect` / `pan` / `copy` / `channelDraft` / `contextMenu`…),`action.target` 说明点的是什么。点 / 线读 `item`,通道回线读 `channel` / `channelContext`,鼠标键、修饰键与 clientX / clientY 读 `originalEvent`,见「指针事件」 |
|
|
15
15
|
| `onPointerUp` | `GisViewPointerPayload` | 左键 / 中键 / 右键抬起,与 `onPointerDown` 成对:`action.type` 是**刚做完的那件事**(拖过就是 `dragPoint` / `dragWaypoint` / `boxSelect` / `pan`,没拖就是 `click`),`action.moved` 说明这一次是拖还是点,另带 `action.press`(按下时的意图)/ `duration`(按了多久)/ `longPress`(长按) |
|
|
16
16
|
| `onContextMenu` | `GisViewPointerPayload` | 每一次右键:点、线、通道、空白都只发这一条(要素信息在载荷里);`action.type` 固定是 `contextMenu`、`action.target` 说明点的是点 / 线 / 通道 / 空白。命中通道回线时,同一载荷另带 `channelContext`(几组 / 当前哪组 / 可用交互),不再有单独的通道右键回调。要自己弹菜单就在回调里渲染。**右键回调缺省关**(`contextMenu` / `interactive` 都没写时组件不接右键),写 `interactive: true` / `contextMenu: true` 或 `features: 'view'` 才发;接住右键时系统菜单默认已被收掉(`preventContextMenu` 缺省 `true`),想看系统菜单写 `preventContextMenu: false` |
|
|
17
17
|
| `onCascadeHighlight` | `GisViewCascadeHighlightInfo \| null` | 点击级联高亮变了:载荷带来源 `origin`(节点 / 线路 / 通道回线)、范围 `scope` 与亮着的 `points` / `lines` / `pointIndexes` / `lineIndexes`,与 `getCascadeHighlight()` 同一份;点空白或关掉开关时是 `null`。这条高亮不改选中集,事件总线名为 `cascadeHighlight` |
|
|
18
|
-
| `onHover` | `GisViewHoverPayload \| null` | 悬停要素变化(空白处 / 移出画布是 `null`):与拾取结果同一份读数(`type` / `index` / `item` + 通道字段,另有光标位置 `x` / `y` / `world`
|
|
18
|
+
| `onHover` | `GisViewHoverPayload \| null` | 悬停要素变化(空白处 / 移出画布是 `null`):与拾取结果同一份读数(`type` / `index` / `item` + 通道字段,另有光标位置 `x` / `y` / `world` 与 `lngLat`)+ `action`(`type` 恒为 `hover`、`target` 说明停在什么上面);指针正好停在最近一次指针事件上时 `originalEvent` 是那一次事件,按键、修饰键与 clientX / clientY 从它读。选中集总读数用 `getSelectionInfo()`;程序化悬停 `hoverAt()` 也走同一条 |
|
|
19
19
|
| `onFilter` | `GisViewFilterResult` | 筛选完成(含 Worker 异步返回):隐藏的点 / 线下标、剩余可见数量、是否用了 Worker 与计算耗时。`filter.mode: 'remove'` 时删除完成后只发最终一次,`hiddenPoints` / `hiddenLines` 为空 |
|
|
20
20
|
| `onCursor` | `[number, number] \| null` | 光标世界坐标,移出画布时为 `null` |
|
|
21
21
|
| `onPointer` | `GisViewPointerState` | 光标每次移动(含移出画布)都发:`{ inside, screen, world, lngLat }`,屏幕坐标是画布 CSS 像素、`lngLat` 要有 GeoJSON 背景才有;`getPointer()` 随时能读同一份,移出时 `inside: false` |
|
|
22
22
|
| `onPointDrag` | `GisViewPointDragPayload` | 拖拽点:`phase` 为 `start` / `drag` / `end`,载荷带最新坐标、本次移动的全部节点 `points` 与跟随移动的线路 `lines` |
|
|
23
|
-
| `onPointCopy` | `GisViewPointCopyPayload \| GisViewBatchCopyPayload` | 复制节点:`phase` 为 `start` / `move` / `commit` / `end` / `cancel`(复制到剪贴板另有 `clipboard`,批量复制 / 整组粘贴是 `batch`),`commit` 阶段可以 `payload.cancel()` 取消、`payload.update()`
|
|
23
|
+
| `onPointCopy` | `GisViewPointCopyPayload \| GisViewBatchCopyPayload` | 复制节点:`phase` 为 `start` / `move` / `commit` / `end` / `cancel`(复制到剪贴板另有 `clipboard`,批量复制 / 整组粘贴是 `batch`),`commit` 阶段可以 `payload.cancel()` 取消、`payload.update()` 改数据;落点与最新节点都读 `payload.item`(批量看 `items[0]`),不再另挂 `point` / `coordinate`。`clipboard` 载荷带整组 `sources` / `items` / `lines` / `channels` / `count` / `anchor`,`batch` 载荷另带 `indexes` / `lineSources` / `channelSources` / `revert()`,见「节点复制」 |
|
|
24
24
|
| `onPointMerge` | `GisViewPointMergePayload \| GisViewMergeResult \| GisViewMergeFailure \| GisViewMergeSelectedPayload` | 合并节点:拖到另一个节点上时先发 `pending`(组件不弹任何界面,在回调里调 `payload.confirm()` / `cancel()` 裁决,或存下来自己接界面),之后发 `confirm`(载荷里的 `item` 是合并结果,可改 / 可取消)、`cancel`,数据并完 / 并失败再发一条 `merge`(`ok: true` / `ok: false`);拖拽入口的载荷带 `trigger`(`'collision'` / `'center'`),说明这一次按哪种时机命中;框选 / 多选之后调 `mergeSelectedPoints()` 时**逐对发 `merge`**,收尾再发一条 `phase: 'mergeSelected'` 的汇总(`merged` 逐对列出、`skipped` 说明谁没并进去),见「节点合并」 |
|
|
25
25
|
| `onRemove` | `GisViewRemovePayload` | 删除请求(Delete 键 / `requestRemove()`):`pending` 时给一份载荷说明要删什么、连带什么、能按哪些范围删(`points` / `lines` / `waypoints` / `groups` / `channels` / `actions` / `counts`),确认之后发 `phase: 'remove'`(`result` 是结果)、取消发 `phase: 'cancel'`;组件不弹任何界面,弹不弹、怎么弹都由使用方决定(在回调里调 `payload.confirm(scope)` / `payload.cancel()`,或存下来自己接界面),见「删除(remove)」 |
|
|
26
26
|
| `onWaypointChange` | `GisViewWaypointPayload` | 拐点新增 / 选中 / 删除与拖动,`phase` 为 `add` / `select` / `remove` 或拖动时的 `start` / `move` / `end`,载荷带最新线路数据与所属集合的 `group_id`;通道内的回线共用骨架,载荷是**被操作的那条回线**的推导结果。同步后端只接改变数据的 `add` / `remove` / `end`,并用 `getChannelGroupWaypoints().path` 提交整份骨架,见「通道与多回线」 |
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
| `onChannelGroupChange` | `GisViewChannelGroupChangePayload` | 分组变化:拆分 / 合并 / 挪线 / 新建组(`action.type` 为 `splitGroups` / `mergeGroups` / `moveLine` / `addGroup`)与整份声明跟着补丁换(`setGroups`,`updateChannel` 写 `groups` / `lineIds`);`action` 带这一笔的 `groupId` / `groupIds` / `lineIds`(`updateLine` 顺带挪组时 `action.via` 为 `'updateLine'`),`item` / `groups` 是与 `dataChange('channelGroups')` 同一份的最新读数。同步后端时用 `getChannelGroups(channelId)` 提交整份声明,不按 action 猜 delta,见「通道分组」 |
|
|
31
31
|
| `onLabelDrag` | `GisViewLabelDragPayload` | 标签被拖走(`draggable`):`phase` 为 `start` / `move` / `end`,载荷带 `kind`(`point` / `line`)、`index` / `id` / `item`、当前偏移 `offset`、起手时的 `startOffset` 与本次位移 `delta`(CSS 像素),见「标签的拖动与编辑」 |
|
|
32
32
|
| `onLabelClick` | `GisViewLabelClickPayload` | 点了一下标签(`editable`,位移小于 3 px 才算点选):载荷带 `kind` / `index` / `id` / `item`、当前 `offset` 与这一条的状态读数 `state`,使用方按它选中这一条 |
|
|
33
|
-
| `onLabelContextMenu` | `GisViewLabelPointerPayload` | 在标签上按右键(`draggable` / `editable` 任开一个):载荷与 `onLabelClick`
|
|
33
|
+
| `onLabelContextMenu` | `GisViewLabelPointerPayload` | 在标签上按右键(`draggable` / `editable` 任开一个):载荷与 `onLabelClick` 同一份读数,菜单定位与按键读 `payload.originalEvent.clientX` / `clientY`;系统菜单按 `preventContextMenu` 收(缺省收),要弹自己的菜单就接这一条 |
|
|
34
34
|
| `onLabelDblClick` | `GisViewLabelPointerPayload` | 双击标签(开关同上):载荷与 `onLabelContextMenu` 一致(原始事件是 `dblclick`),用它开自己的编辑框 |
|
|
35
35
|
| `onLabelChange` | `GisViewLabelChangePayload` | 标签状态变了:`action` 为 `update`(`updateLabel`)/ `reset`(`resetLabel`)/ `resetAll`(`resetLabels`)/ `add`(`addLabel`)/ `remove`(`removeLabel`)/ `restore`(`setLabelStates`),批量的三条各发 `addAll` / `updateAll` / `removeAll`;载荷带 `kind` / `index` / `id` 与 `patch` / `state` / `removed`,批量那三条另带 `count` 与 `added` / `updated` / `items` / `skipped`;面板按它回显 |
|
|
36
36
|
| `onCameraChange` | `GisViewCamera` | 平移或缩放后触发 |
|
|
@@ -108,19 +108,24 @@ const view = createGisView('#map', {
|
|
|
108
108
|
| `selectTrigger: 'dblClick'` 的第二下 | `onClick → onSelect → onSelectChange`(有变化时)`→ onDblClick` |
|
|
109
109
|
|
|
110
110
|
`onSelect` 与 `onSelectChange` 带同一份通道上下文:选中集只涉及**一条通道**时,载荷顶层平铺
|
|
111
|
-
`channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
`
|
|
116
|
-
|
|
111
|
+
`channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups` 与本次选中的
|
|
112
|
+
`lineIndexes` / `lineIds`;`group` 是当前组(真实单击 / `selectAt` 是命中的那一组,框选与按引用接口是
|
|
113
|
+
选中集里第一条通道回线所在的组),`groups` 是整条通道的全部分组。选中集涉及**多条通道**时顶层不再摊平,
|
|
114
|
+
改看 `channelContexts`(每项带这组字段与本次选中的 `lineIndexes` / `lineIds`)。单通道不会再额外造一份
|
|
115
|
+
`channelContexts`。两个事件共享同一批 `items`、分组对象与数组引用,不会深拷贝。
|
|
116
|
+
|
|
117
|
+
`items[]` 里的通道回线**不再挂任何通道字段**(`channelId` / `channelIndex` / `channel` / `groupId` /
|
|
118
|
+
`groupIndex` / `group` / `groups` / `lineIndexes` / `lineIds`)。要用 `items` 反查归属,按选中的线路下标
|
|
119
|
+
查外层的 `lineIndexes` / `lineIds`,再对应顶层上下文或 `channelContexts`;只问一条线路时也可以直接调
|
|
120
|
+
`getLineChannel(index)`。`groups[].lineIds` 是整组成员,外层 `lineIndexes` / `lineIds` 是本次选中子集,
|
|
121
|
+
两者语义不同。纯节点或空选中没有这些通道字段。
|
|
117
122
|
|
|
118
123
|
`cause` 取 `click`(指针点击)/ `boxSelect`(框选)/ `keyboard`(Esc 清空)/ `api`(接口调用)。
|
|
119
124
|
|
|
120
125
|
`action` 是更细的一份判读(与指针事件同一套写法):`action.type` 是 `select`(点中一个或接口选中)/
|
|
121
126
|
`toggleSelect`(Ctrl 点击取反,取反取没了也是它)/ `boxSelect`(框选,没框到东西也是它)/
|
|
122
|
-
`clearSelection`(点空白、Esc 或接口清空),`action.target` 是 `selection` / `empty
|
|
123
|
-
|
|
127
|
+
`clearSelection`(点空白、Esc 或接口清空),`action.target` 是 `selection` / `empty`;这一批数据直接读载荷的
|
|
128
|
+
`items`,修饰键读 `originalEvent`。单击触发模式下,Ctrl 取反与框选这两条路**不经过 `onClick`**;
|
|
124
129
|
点空白仍会发 `onClick`(载荷为 `null`),顺序同样是 `onClick → onSelect → onSelectChange(有变化时)`。`selectTrigger: 'dblClick'`
|
|
125
130
|
时第一下仍会发 `onClick`,第二下应用选中 / 取反 / 清空后按 `onClick → onSelect → onSelectChange(有变化时)→ onDblClick` 派发。
|
|
126
131
|
要按操作分支的面板认这一个字段:
|
|
@@ -184,7 +189,7 @@ view.off('*') // 不传 handler = 摘掉这个事件的全部订阅,返回摘
|
|
|
184
189
|
| 约定 | 说明 |
|
|
185
190
|
| --- | --- |
|
|
186
191
|
| 事件名与回调一一对应 | `dataChange` 就是 `onDataChange`、`selectChange` 就是 `onSelectChange`,载荷完全同一份;`GisViewEventMap` 是事件名到载荷的类型表,`EVENT_NAMES` 是运行时的名字数组 |
|
|
187
|
-
|
|
|
192
|
+
| 指针类事件只有一个参数 | `pointerDown` / `pointerUp` / `contextMenu` 的回调收一份载荷,事件总线订阅时也是同一份直接载荷;原始 DOM 事件读 `payload.originalEvent`,不再额外包 `{ payload, event }` |
|
|
188
193
|
| 回调抛错不扩散 | 订阅者抛错只走 `onError`:不打断其它订阅者,也不影响组件自己的流程 |
|
|
189
194
|
| 高频事件不白算 | 组件内部按「有没有人订阅」短路(`count(name) \|\| count('*')`):没人听的时候不构造载荷,`pointer` / `hover` / `render` 这些每帧级别的事件也可以直接订阅 |
|
|
190
195
|
| 订阅时机 | 创建时的 `ready` 与首次 `dataChange` 发生在 `createGisView` 返回之前,这两条要在 options 里写回调(`onReady` 等) |
|
|
@@ -44,7 +44,7 @@ view.updateData({ channels: nextChannels })
|
|
|
44
44
|
view.addPoint({ id: 's-1', x: 116.4, y: 39.9, name: '甲站' })
|
|
45
45
|
view.addPoints(stations) // 返回 { ok, count, indexes, items }
|
|
46
46
|
|
|
47
|
-
//
|
|
47
|
+
// 通道(通道信息 + 线路数组 lineData,见「新增通道」)
|
|
48
48
|
view.addChannel({ from: 's-1', to: 's-2', name: '甲通道' }, [{ id: 'c-1' }, { id: 'c-2' }])
|
|
49
49
|
view.addChannels(channelDeclarations) // 批量版:逐条落地,只重排一次、只记一步历史、只发一条 addChannels
|
|
50
50
|
```
|
|
@@ -52,7 +52,7 @@ view.addChannels(channelDeclarations) // 批量版:逐条落地,只
|
|
|
52
52
|
两条新增路线的分工:`addPoint` / `addPoints` / `addLines` 追加的是「现成的数据对象」;
|
|
53
53
|
`addChannel` / `addChannels` / `addLine` 是按两端节点新建(通道声明一起改)。`addChannels` 的返回里
|
|
54
54
|
`results` 与传入顺序一一对应(失败的条目也在里面,按各自的 `reason` 读),`lineIds` / `channelIds`
|
|
55
|
-
|
|
55
|
+
只列成功那几条;新增类方法缺省都不改选中集,需要选中时显式传 `options.select: true`。十万级数据下批量新增只付
|
|
56
56
|
一次场景重排,比逐条 `addChannel` 少几十上百次整份重建。新增类方法的完整清单(节点 / 线路 / 通道 / **分组** / **拐点**)与返回口径见「按方法新增要素」。
|
|
57
57
|
|
|
58
58
|
补点 / 补线的代价与「场景铺到哪一步」无关:发散渲染还没铺完时又来一批(「刚删完紧接着补」这种最常见的
|
|
@@ -321,12 +321,11 @@ const geometryOnly = view.getChangedLines({ reasons: ['path'] })
|
|
|
321
321
|
| --- | --- | --- | --- |
|
|
322
322
|
| 节点(一条 / 一批) | `addPoint(item, options?)` / `addPoints(items, options?)` | 与数据里的 `points[]` 同一份字段(`id` / `x` / `y` / 样式 / 业务字段) | 单条是 `{ ok, count, indexes, items, index, item }`、批量去掉 `index` / `item`;追加进 `points`,走引擎的增量追加(见「动态换数据与加载完成」) |
|
|
323
323
|
| 线路(现成数据,一条 / 一批) | `addLines(items, options?)` | 与数据里的 `lines[]` 同一份字段(`id` / `from` / `to` / `path`…),**不挂通道** | `{ ok, count, indexes, items }`;追加进 `lines` |
|
|
324
|
-
| 独立线路(按两端节点新建) | `addLine(
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
|
329
|
-
| 通道(已有通道:一次加多组) | `addChannelGroups(channelId, groups, options?)` | `groups` 每项 `{ id?, path?, lineData \| lines \| lineItems }`,线路数据与 `addChannel` 第二段同一份写法 | `{ ok, count, results, lineIds, groupIds, groups }`:逐组落地但只记一步历史、只发一条 `addChannelGroups` 数据变更;`options.emit: false` 只落地不通知 |
|
|
324
|
+
| 独立线路(按两端节点新建) | `addLine(spec, lineData?, options?)` | `from` / `to` 必填;`spec` 可写 `name?` / `id?` / `lineProps?` / `path?` / `routing?` / `lineData?`,第二段 `lineData` 一条或一批(每项可以只写一个 id:裸字符串 / 数字按 `{ id }` 收);`options?: { emit?, select? }` | 与 `addChannel` 同一份结果结构(`kind: 'line'`);缺省不改变选中集,`select: true` 才选中新回线 |
|
|
325
|
+
| 通道(只新建) | `addChannel(spec, lineData?, group?, options?)` | 第一段 `{ from, to, name?, id?, channelProps?, path?, routing?, lineData?, group? }`;第二段线路数组 lineData(一条或一批)—— **按每项 id 建 `lineIds`**;第三段可选分组信息 `{ id?, lineIds?, path? }`;`options?: { emit?, select? }`。两个节点之间已有通道时整条失败,见「新增通道」 | `{ ok, reason?, plan, resolution, kind, circuits, line, lineId, lineIds, lines, lineIndex, channelId, groupId, channelLineIds, channelIndex }`;缺省不改变选中集,`select: true` 才选中新回线 |
|
|
326
|
+
| 通道(已有通道:新加一组 / 并进某一组) | `addChannelGroupLines(channelId, lineData?, groupInfo?, options?)` / `joinChannelGroupLines(channelId, groupId?, lineData?, options?)` | 第二段都是线路数组 lineData(按每项 id 建回线);新加一组可用 `groupInfo` 给组 id 与骨架,并进某一组用 `groupId`(认分组 id 也认序号,从 0 起,不传就挑第一个还能并的);`options?: { emit?, select? }` | 与 `addChannel` 同一份结果;`channelLineIds` 是更新后的完整成员表;缺省不选中,`select: true` 才选中新回线 |
|
|
327
|
+
| 通道(一批) | `addChannels(items, options?)` | 每一项与 `addChannel` 第一段同一套写法,可在项内写 `lineData` / `group`;`options?: { emit?, select? }` | `{ ok, count, results, lineIds, channelIds, items }`:`results` 与传入顺序一一对应,失败的在各自 `reason` 里;只重排一次、只记一步历史;缺省不选中,`select: true` 才选中全部成功的新回线 |
|
|
328
|
+
| 通道(已有通道:一次加多组) | `addChannelGroups(channelId, groups, options?)` | `groups` 每项 `{ id?, groupId?, path?, lineData? \| lines? }`,线路数据与 `addChannel` 第二段同一份写法;`lineItems` 已移除;`options?: { emit?, label?, select? }` | `{ ok, count, results, lineIds, groupIds, groups }`:逐组落地但只记一步历史、只发一条 `addChannelGroups` 数据变更;缺省不选中,`select: true` 才选中新回线 |
|
|
330
329
|
| 分组(把回线拆成新组) | `addChannelGroup(channelId, lineIds, options?)` | 要拆出去的回线 `lineIds`;`options.path` 给新组骨架(缺省沿用来源组、`path: []` 要一条直连);`options.id` 指定组 id,撞上这条通道已有分组时拒绝(不会写出两个同名分组) | `{ ok, reason, channelId, lineIds, groupId, groupIndex, groups, skipped }`;到组数 / 回数上限时 `ok: false` + `reason`,显式 `id` 重名同样 `ok: false` + `reason` |
|
|
331
330
|
| 分组(整份重拆 / 合并 / 单条改派) | `splitChannelGroups(channelId, [{ id, lineIds, path? }…])` / `mergeChannelGroups(channelId, groupIds, targetGroupId)` / `moveChannelLine(channelId, lineId, { type: 'newGroup' } \| { type: 'joinGroup', groupId })` | 前两个整份换分组声明(第一个组沿用当前拐点);`moveChannelLine` 挪一条回线,也接受 `'newGroup'` 或直接给分组 id | 都把最终声明写回数据副本、记一条「通道分组」历史;`moveChannelLine` 返回 `{ ok, channelId, lineId, groupId, groupIndex, groups }` |
|
|
332
331
|
| 分组(删一个:把一组并进相邻组) | `removeChannelGroup(channelId, groupId, options?)` | `channelId` / `groupId` 目标通道与要删的组;`options.targetGroupId` 指名并到哪一组(缺省前一组,第一组并后一组) | `{ ok, reason, channelId, groupId, targetGroupId, lineIds, groups }`;骨架 / 拐点沿用目标组,并完只剩一组时声明回到「整条通道一组」;单组通道 `ok: false`(删整条用 `removeChannel`) |
|
|
@@ -352,7 +351,7 @@ const added = view.addChannel({ from: 's-1', to: 's-2', path: [[116.7, 40.0]] },
|
|
|
352
351
|
console.log(added.channelId, added.lineIds, added.groupId)
|
|
353
352
|
// 多条通道一次建:每项与 addChannel 同一套写法,只重排一次、只记一步历史
|
|
354
353
|
const many = view.addChannels([
|
|
355
|
-
{ from: 's-2', to: 's-3',
|
|
354
|
+
{ from: 's-2', to: 's-3', lineData: [{ id: 'line-c' }, { id: 'line-d' }] },
|
|
356
355
|
{ from: 's-3', to: 's-4', name: '三号通道', lineData: [{ id: 'line-e' }] }
|
|
357
356
|
])
|
|
358
357
|
|
|
@@ -380,7 +379,7 @@ view.getParallelGroups()
|
|
|
380
379
|
```
|
|
381
380
|
|
|
382
381
|
四条口径:
|
|
383
|
-
- **新增时的线路 / 通道 `path` 口径**:`addLine` / `addChannel
|
|
382
|
+
- **新增时的线路 / 通道 `path` 口径**:`addLine` / `addChannel` 的 `routing: 'orthogonal'` 会自动补一个先横后纵的 90° 拐点;输入里的 `path` 一律按**中间拐点**解释,独立线路落地成 `[起点, ...中间拐点, 终点]` 的完整 `path`,新建通道 / 分组只把中间拐点写进骨架。`beginAddChannel` / `beginAddLine` 草稿用同一份解析,预览与落地一致。
|
|
384
383
|
|
|
385
384
|
- **返回里带下标**:`addPoint` / `addPoints` / `addLines` 的 `indexes` 是追加后在数组里的位置(老数据一个都不动),后续 `updatePoint` / `updateLine` 直接拿它当 id 用;`addChannel` 给的是 `lineIndex` / `channelIndex` / `groupId`。
|
|
386
385
|
- **事件与历史**:新增发 `dataChange`(`addPoints` / `addLines` / `addLine` / `addChannel` / `addChannels` / `addChannelGroups`),分组改动另发 `channelGroups`(`onChannelGroupChange`,见「通道分组」);`addPoints` / `addLines` / `addChannels` / `addChannelGroups` / `addWaypoints` 的批量写法都只记一步历史、只发一条数据变更,`options.emit: false` 只落地不通知。开着历史时每一步都可撤销。
|