gis-view-webgl2 0.1.8 → 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 +7 -5
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +214 -14
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +65 -547
- 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 -526
- package/docs/demos/BasicDemo.vue +142 -25
- package/docs/demos/BatchAddDemo.vue +127 -1
- package/docs/demos/ChannelDemo.vue +61 -0
- package/docs/demos/CopyMergeDemo.vue +125 -36
- package/docs/demos/EventDemo.vue +12 -4
- package/docs/demos/api-index.js +26 -16
- 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/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/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 +2 -1
- 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/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +48 -11
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +8 -4
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +57 -0
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +26 -3
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +34 -1
- 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 +13 -10
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +14 -8
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +43 -3
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +3 -1
- 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 +5 -2
- package/docs/steps/README.md +200 -121
- package/docs/steps/index.html +216 -0
- package/docs/steps/selftest-demos.html +209 -31
- package/docs/steps/selftest-docs.html +17 -6
- package/docs/steps/selftest-features.html +311 -3
- package/docs/steps/selftest-labels.html +81 -0
- package/docs/steps/selftest-perf.html +31 -3
- package/docs/steps/selftest-select.html +51 -1
- 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
|
@@ -25,7 +25,7 @@ const copyItemsText = ref('—')
|
|
|
25
25
|
const copyLineChannelsText = ref('—')
|
|
26
26
|
const manualText = ref('—')
|
|
27
27
|
const mergeBackendText = ref('合并变更 → 后端事务:—')
|
|
28
|
-
const splitText = ref('
|
|
28
|
+
const splitText = ref('自动解除合并:先选中一个合并后的节点(不选就找第一个带 mergeInfo 的节点)')
|
|
29
29
|
const lastSplitResult = shallowRef(null)
|
|
30
30
|
const copyBackendText = ref('复制落库:—')
|
|
31
31
|
const mergeSyncText = ref('合并落库:—')
|
|
@@ -45,8 +45,12 @@ const mergedVoltage = ref('525')
|
|
|
45
45
|
const mergedColor = ref('#ff5d8f')
|
|
46
46
|
const mergedSize = ref('16')
|
|
47
47
|
const mergedSymbol = ref('square')
|
|
48
|
-
const mergedStrokeColor = ref('#ffffff')
|
|
49
|
-
|
|
48
|
+
const mergedStrokeColor = ref('#ffffff')
|
|
49
|
+
/** 合并时取双方最高值的字段;空字符串表示不取任何键 */
|
|
50
|
+
const mergeMaxKeys = ref('')
|
|
51
|
+
/** 拆分 / 自动解除合并后是否选中结果节点 */
|
|
52
|
+
const splitSelect = ref(false)
|
|
53
|
+
const mergeTargetId = ref('')
|
|
50
54
|
const mergeKeep = ref('target')
|
|
51
55
|
const rewriteOnConfirm = ref(true)
|
|
52
56
|
const pointOptions = ref([])
|
|
@@ -73,6 +77,7 @@ let disposed = false
|
|
|
73
77
|
|
|
74
78
|
const pointLabel = item => String(item?.name ?? item?.label ?? item?.id ?? '节点')
|
|
75
79
|
const pointKey = item => String(item?.id ?? item?.label ?? item?.name ?? '—')
|
|
80
|
+
const parsedMergeMaxKeys = () => mergeMaxKeys.value.split(',').map(key => key.trim()).filter(Boolean)
|
|
76
81
|
const itemId = item => (item?.id ?? item?.line_id ?? item?.channel_id ?? '无 id')
|
|
77
82
|
/** 复制载荷的 lines 与 lineChannels 一一对齐:直接列出每条回线复制后属于哪条通道 / 哪一组 */
|
|
78
83
|
const describeLineChannels = (payload) => {
|
|
@@ -102,6 +107,14 @@ const describeMergeChanges = changes => describeMergeChangeSets(changes ? [chang
|
|
|
102
107
|
const describeMergeImpact = impact => (impact
|
|
103
108
|
? `影响 ${impact.lineCount} 条线路(改线 ${impact.linesChanged} / 删线 ${impact.removedLines})· 通道 改 ${impact.channelsChanged} / 删 ${impact.removedChannels} / 并 ${impact.mergedChannels}`
|
|
104
109
|
: '影响读数:当前数据对不上')
|
|
110
|
+
const voltageOf = item => item?.vn_kv ?? item?.voltage ?? item?.voltageKv ?? item?.voltage_level ?? item?.voltageLevel ?? '—'
|
|
111
|
+
/** 合并追溯读数:来源、保留前电压与快照条数,演示解除合并需要的字段都在这里 */
|
|
112
|
+
const describeMergeInfo = (item) => {
|
|
113
|
+
const info = item?.mergeInfo
|
|
114
|
+
if (!info || typeof info !== 'object') return 'mergeInfo —'
|
|
115
|
+
const sourceIds = (info.sourceIds ?? info.points?.map(snapshot => snapshot.id) ?? []).map(id => String(id ?? '无 id')).join(' / ') || '—'
|
|
116
|
+
return `mergeInfo v${info.version} · 保留前 ${info.targetPoint?.name ?? '—'} / ${voltageOf(info.targetPoint)} kV · 来源 [${sourceIds}] · 快照 线 ${info.lines?.length ?? 0} / 通道 ${info.channels?.length ?? 0}`
|
|
117
|
+
}
|
|
105
118
|
const backendBusy = computed(() => backendBusyCount.value > 0)
|
|
106
119
|
const backendStateText = computed(() => `假后端 v${backendSnapshot.value.version} · 已应用 ${backendSnapshot.value.applied} 条 · op 缓存 ${backendSnapshot.value.cached} · 幂等回放 ${backendSnapshot.value.replays ?? 0} 次 · 模拟延迟 ${backendSnapshot.value.latency}ms`)
|
|
107
120
|
const backendModeHint = computed(() => FAKE_BACKEND_MODES.find(item => item.value === backendMode.value)?.hint ?? '')
|
|
@@ -749,27 +762,31 @@ const prepareMerge = () => {
|
|
|
749
762
|
return actionText.value
|
|
750
763
|
}
|
|
751
764
|
|
|
752
|
-
/**
|
|
765
|
+
/** 解除合并:优先用 mergeInfo 自动恢复全部来源;没有合并记录时才退回手工两份拆分 */
|
|
753
766
|
const splitSelectedPoint = () => {
|
|
754
767
|
const data = view?.getData()
|
|
755
768
|
if (!data?.points?.length) return '没有可拆分的节点'
|
|
756
|
-
const
|
|
769
|
+
const mergedPoint = selectedPoints.value.find(item => item?.mergeInfo) ?? data.points.find(item => item?.mergeInfo)
|
|
770
|
+
const source = mergedPoint ?? selectedPoints.value[0] ?? data.points[0]
|
|
771
|
+
const auto = Boolean(mergedPoint?.mergeInfo)
|
|
757
772
|
splitRound += 1
|
|
758
773
|
const offset = 0.42 + splitRound * 0.04
|
|
759
|
-
const result =
|
|
760
|
-
{
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
774
|
+
const result = auto
|
|
775
|
+
? view.splitPoint(source.id, null, { label: '解除合并(自动恢复)', select: splitSelect.value })
|
|
776
|
+
: view.splitPoint(source.id, [
|
|
777
|
+
{
|
|
778
|
+
x: Number(source.x) - offset,
|
|
779
|
+
y: Number(source.y) - offset * 0.55,
|
|
780
|
+
name: `${pointLabel(source)} · A`,
|
|
781
|
+
zone_name: '解除合并后 · A'
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
x: Number(source.x) + offset,
|
|
785
|
+
y: Number(source.y) + offset * 0.55,
|
|
786
|
+
name: `${pointLabel(source)} · B`,
|
|
787
|
+
zone_name: '解除合并后 · B'
|
|
788
|
+
}
|
|
789
|
+
], { autoLines: 'alternate', label: '解除合并(手工拆分)', select: splitSelect.value })
|
|
773
790
|
lastSplitResult.value = result
|
|
774
791
|
if (!result.ok) {
|
|
775
792
|
splitText.value = `解除合并失败:${result.reason}`
|
|
@@ -777,6 +794,14 @@ const splitSelectedPoint = () => {
|
|
|
777
794
|
pushLog('splitPoint', splitText.value)
|
|
778
795
|
return splitText.value
|
|
779
796
|
}
|
|
797
|
+
if (result.autoRestored) {
|
|
798
|
+
const restoredNames = (result.items ?? []).map(item => `${pointLabel(item)}(${String(voltageOf(item))} kV)`).join(' / ')
|
|
799
|
+
splitText.value = `splitPoint(自动解除合并):${pointLabel(source)} → ${restoredNames} · 恢复来源 ${(result.mergeSourceIds ?? []).map(id => String(id)).join(' / ') || '—'} · 改线 ${result.linesChanged} / 改通道 ${result.channelsChanged} · ${splitSelect.value ? '已选中拆分结果' : '未选中拆分结果'} · mergeInfo 已从保留节点移除`
|
|
800
|
+
actionText.value = splitText.value
|
|
801
|
+
pushLog('splitPoint/auto', splitText.value)
|
|
802
|
+
refreshState()
|
|
803
|
+
return splitText.value
|
|
804
|
+
}
|
|
780
805
|
const lineSlot = new Map()
|
|
781
806
|
for (const assignment of result.lineAssignments ?? []) {
|
|
782
807
|
for (const lineId of assignment.lineIds ?? []) {
|
|
@@ -792,10 +817,9 @@ const splitSelectedPoint = () => {
|
|
|
792
817
|
return slots.size <= 1
|
|
793
818
|
})
|
|
794
819
|
const counts = result.lineAssignments.map(assignment => `${assignment.index === 0 ? 'A' : assignment.index === 1 ? 'B' : String(assignment.index + 1)} ${assignment.lineCount}`).join(' / ')
|
|
795
|
-
splitText.value = `splitPoint(解除合并):${pointLabel(source)} → ${result.pointIds.join(' / ')} · autoLines=${result.autoLines} · 自动分线 ${result.autoAssignedLines}(${counts})· 改线 ${result.linesChanged} / 删线 ${result.removedLines} · 改通道 ${result.channelsChanged} / 删通道 ${result.removedChannels} · 通道成组${channelsKept ? '保持完整' : '发生拆分'}`
|
|
820
|
+
splitText.value = `splitPoint(解除合并):${pointLabel(source)} → ${result.pointIds.join(' / ')} · autoLines=${result.autoLines} · 自动分线 ${result.autoAssignedLines}(${counts})· 改线 ${result.linesChanged} / 删线 ${result.removedLines} · 改通道 ${result.channelsChanged} / 删通道 ${result.removedChannels} · 通道成组${channelsKept ? '保持完整' : '发生拆分'} · ${splitSelect.value ? '已选中拆分结果' : '未选中拆分结果'}`
|
|
796
821
|
actionText.value = splitText.value
|
|
797
822
|
pushLog('splitPoint', splitText.value)
|
|
798
|
-
view.selectFeatures(result.pointIds)
|
|
799
823
|
refreshState()
|
|
800
824
|
return splitText.value
|
|
801
825
|
}
|
|
@@ -816,7 +840,7 @@ const mergePatch = () => {
|
|
|
816
840
|
return patch
|
|
817
841
|
}
|
|
818
842
|
|
|
819
|
-
const mergeSelectedByApi = () => {
|
|
843
|
+
const mergeSelectedByApi = () => {
|
|
820
844
|
const points = selectedPoints.value
|
|
821
845
|
if (points.length < 2) return 'mergeSelectedPoints:至少选中两个节点'
|
|
822
846
|
const targetId = mergeTargetOptions.value.some(item => item.value === mergeTargetId.value)
|
|
@@ -841,8 +865,59 @@ const mergeSelectedByApi = () => {
|
|
|
841
865
|
actionText.value = mergeText.value
|
|
842
866
|
pushLog('mergeSelectedPoints', mergeText.value)
|
|
843
867
|
refreshState()
|
|
844
|
-
return mergeText.value
|
|
845
|
-
}
|
|
868
|
+
return mergeText.value
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/** 把面板上的字段清单写进 merge.maxKeys;空清单表示所有业务字段都取保留节点 */
|
|
872
|
+
const applyMergeMaxKeys = () => {
|
|
873
|
+
if (!view) return '画布还没准备好'
|
|
874
|
+
const maxKeys = parsedMergeMaxKeys()
|
|
875
|
+
const merge = view.getOptions().merge ?? {}
|
|
876
|
+
view.updateOptions({ merge: { ...merge, enabled: true, maxKeys } })
|
|
877
|
+
actionText.value = maxKeys.length
|
|
878
|
+
? `merge.maxKeys = [${maxKeys.join(', ')}]:合并时这些数值字段取双方最高值`
|
|
879
|
+
: 'merge.maxKeys = []:合并时所有业务字段保留目标节点(组件不再默认取电压最高)'
|
|
880
|
+
pushLog('merge.maxKeys', actionText.value)
|
|
881
|
+
return actionText.value
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/** 缺省合并:不传 item,按 merge.maxKeys 决定哪些数值字段取双方最高值 */
|
|
885
|
+
const mergeSelectedByDefault = () => {
|
|
886
|
+
const points = selectedPoints.value
|
|
887
|
+
if (points.length < 2) return 'mergeSelectedPoints:至少选中两个节点'
|
|
888
|
+
const targetId = mergeTargetOptions.value.some(item => item.value === mergeTargetId.value)
|
|
889
|
+
? mergeTargetId.value
|
|
890
|
+
: mergeTargetOptions.value[0].value
|
|
891
|
+
const target = points.find(item => pointKey(item) === targetId)
|
|
892
|
+
const sources = points.filter(item => pointKey(item) !== targetId).map(item => item.id)
|
|
893
|
+
const maxKeys = parsedMergeMaxKeys()
|
|
894
|
+
const maxReadout = maxKeys.length
|
|
895
|
+
? maxKeys.map((key) => {
|
|
896
|
+
const values = points.map(item => Number(item[key])).filter(Number.isFinite)
|
|
897
|
+
return `${key}=${values.length ? Math.max(...values) : '无数字'}`
|
|
898
|
+
}).join(' / ')
|
|
899
|
+
: '未配置(全部字段取保留节点)'
|
|
900
|
+
const result = view.mergeSelectedPoints({
|
|
901
|
+
target: { id: target?.id ?? targetId },
|
|
902
|
+
sources,
|
|
903
|
+
label: 'demo 缺省合并(maxKeys)'
|
|
904
|
+
})
|
|
905
|
+
if (result.ok) {
|
|
906
|
+
const item = result.target?.item
|
|
907
|
+
view.selectFeatures(target?.id ?? targetId)
|
|
908
|
+
mergeText.value = `mergeSelectedPoints(缺省规则)· 保留 ${pointLabel(item)} · maxKeys ${maxReadout} · 结果 ${String(voltageOf(item))} kV · 名称 ${item?.name ?? '—'} / 图标 ${String(item?.symbol ?? '—')} · ${describeMergeInfo(item)}`
|
|
909
|
+
pendingText.value = maxKeys.length
|
|
910
|
+
? `这次没有 item 改写:maxKeys 列出的字段取参与场站最高值,其余字段取保留节点`
|
|
911
|
+
: '这次没有 item 改写:maxKeys 为空,全部业务字段(含电压)取保留节点'
|
|
912
|
+
}
|
|
913
|
+
else {
|
|
914
|
+
mergeText.value = `mergeSelectedPoints 失败:${result.reason}`
|
|
915
|
+
}
|
|
916
|
+
actionText.value = mergeText.value
|
|
917
|
+
pushLog('mergeSelectedPoints/default', mergeText.value)
|
|
918
|
+
refreshState()
|
|
919
|
+
return mergeText.value
|
|
920
|
+
}
|
|
846
921
|
|
|
847
922
|
const handlePointMerge = (payload) => {
|
|
848
923
|
if (payload.phase === 'pending') {
|
|
@@ -880,7 +955,7 @@ const handlePointMerge = (payload) => {
|
|
|
880
955
|
if (payload.phase === 'merge') {
|
|
881
956
|
mergeBackendText.value = payload.ok ? describeMergeChanges(payload.changes) : '合并变更 → 后端事务:合并失败,没有变更集'
|
|
882
957
|
mergeText.value = payload.ok
|
|
883
|
-
? `merge · 被并 ${pointLabel(payload.source?.item)} → 保留 ${pointLabel(payload.target?.item)} · 最终 ${pointLabel(payload.item)} · 色 ${payload.item?.color ?? '—'} / 尺寸 ${payload.item?.size ?? '—'} / 图标 ${String(payload.item?.symbol ?? '—')} · 改线 ${payload.linesChanged} / 删线 ${payload.removedLines}`
|
|
958
|
+
? `merge · 被并 ${pointLabel(payload.source?.item)} → 保留 ${pointLabel(payload.target?.item)} · 最终 ${pointLabel(payload.item)} · 电压 ${String(voltageOf(payload.item))} kV · 色 ${payload.item?.color ?? '—'} / 尺寸 ${payload.item?.size ?? '—'} / 图标 ${String(payload.item?.symbol ?? '—')} · 改线 ${payload.linesChanged} / 删线 ${payload.removedLines} · ${describeMergeInfo(payload.item)}`
|
|
884
959
|
: `merge 失败:${payload.reason}`
|
|
885
960
|
pendingMerge.value = null
|
|
886
961
|
pendingText.value = payload.ok ? '合并已落地,拖动来源节点已经收进保留目标' : `合并失败:${payload.reason}`
|
|
@@ -933,13 +1008,16 @@ const resetScene = () => {
|
|
|
933
1008
|
copyText.value = '复制:—'
|
|
934
1009
|
copyLineChannelsText.value = '—'
|
|
935
1010
|
mergeBackendText.value = '合并变更 → 后端事务:—'
|
|
936
|
-
splitText.value = '
|
|
1011
|
+
splitText.value = '自动解除合并:先选中一个合并后的节点(不选就找第一个带 mergeInfo 的节点)'
|
|
937
1012
|
clipboardText.value = '剪贴板:—'
|
|
938
1013
|
copyEventText.value = '还没有 pointCopy 事件'
|
|
939
1014
|
copySourcesText.value = '—'
|
|
940
1015
|
copyItemsText.value = '—'
|
|
941
1016
|
manualText.value = '—'
|
|
942
1017
|
mergeKeep.value = 'target'
|
|
1018
|
+
mergeMaxKeys.value = ''
|
|
1019
|
+
splitSelect.value = false
|
|
1020
|
+
view.updateOptions({ merge: { enabled: true, distance: 12, channelGroups: 'keep', maxKeys: [] } })
|
|
943
1021
|
resetBackendDemo()
|
|
944
1022
|
copyRound = 0
|
|
945
1023
|
pasteRound = 0
|
|
@@ -1012,7 +1090,7 @@ onMounted(() => {
|
|
|
1012
1090
|
},
|
|
1013
1091
|
// Ctrl+C / Ctrl+V 监听的三件套:copy 打开 + keyboard 打开 + 快捷键映射(不写也是这两个缺省值)
|
|
1014
1092
|
shortcuts: { copy: 'ctrl+c', paste: 'ctrl+v' },
|
|
1015
|
-
merge: { enabled: true, distance: 12, channelGroups: 'keep' },
|
|
1093
|
+
merge: { enabled: true, distance: 12, channelGroups: 'keep', maxKeys: [] },
|
|
1016
1094
|
onReady: () => {
|
|
1017
1095
|
nextTick(() => {
|
|
1018
1096
|
view?.fitView(50)
|
|
@@ -1178,23 +1256,34 @@ onBeforeUnmount(() => {
|
|
|
1178
1256
|
<input v-model="mergedStrokeColor" data-demo="merge-stroke-color" type="text" />
|
|
1179
1257
|
</label>
|
|
1180
1258
|
</div>
|
|
1181
|
-
<div class="dv-row">
|
|
1182
|
-
<label><input v-model="rewriteOnConfirm" type="checkbox" /> 手动拖拽合并确认时改写 payload.item</label>
|
|
1183
|
-
</div>
|
|
1184
|
-
<div class="dv-row">
|
|
1185
|
-
<
|
|
1186
|
-
|
|
1259
|
+
<div class="dv-row">
|
|
1260
|
+
<label><input v-model="rewriteOnConfirm" type="checkbox" /> 手动拖拽合并确认时改写 payload.item</label>
|
|
1261
|
+
</div>
|
|
1262
|
+
<div class="dv-row cm-form">
|
|
1263
|
+
<label>
|
|
1264
|
+
maxKeys(逗号分隔)
|
|
1265
|
+
<input v-model="mergeMaxKeys" data-demo="merge-max-keys" type="text" placeholder="例如 vn_kv,value;留空则都不取最高" />
|
|
1266
|
+
</label>
|
|
1267
|
+
<button type="button" class="dv-button" data-demo="merge-apply-max-keys" @click="applyMergeMaxKeys">应用 maxKeys</button>
|
|
1268
|
+
</div>
|
|
1269
|
+
<div class="dv-row">
|
|
1270
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="merge-default" :disabled="!canMerge" @click="mergeSelectedByDefault">按缺省规则合并(按 maxKeys 取最高)</button>
|
|
1271
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="merge-selected" :disabled="!canMerge" @click="mergeSelectedByApi">合并选中(指定保留节点)</button>
|
|
1272
|
+
</div>
|
|
1187
1273
|
<p class="dv-readout" data-demo="merge-readout">{{ mergeText }}</p>
|
|
1188
1274
|
<p class="dv-readout" data-demo="merge-backend-readout">{{ mergeBackendText }}</p>
|
|
1189
|
-
<p class="dv-hint">
|
|
1275
|
+
<p class="dv-hint">「按缺省规则合并」不传 item:只有 <code>merge.maxKeys</code> 列出的数值字段取双方最高值(缺省为空,不取任何键),名称 / 图标等其余业务字段取保留节点;合并后的节点带 <code>mergeInfo</code>(来源快照、线路 / 通道快照)。「指定保留节点」仍可用 item 覆盖字段。拖点相撞走 pending / confirm 两站,确认前也能改写。合并落地后的 changes 可直接作为后端事务。</p>
|
|
1190
1276
|
</div>
|
|
1191
1277
|
<div class="dv-group">
|
|
1192
1278
|
<h3>4 · 解除合并</h3>
|
|
1193
1279
|
<div class="dv-row">
|
|
1194
|
-
<
|
|
1280
|
+
<label><input v-model="splitSelect" data-demo="split-select" type="checkbox" /> 解除合并后选中恢复出的节点</label>
|
|
1281
|
+
</div>
|
|
1282
|
+
<div class="dv-row">
|
|
1283
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="split-selected" @click="splitSelectedPoint">自动解除合并(恢复来源)</button>
|
|
1195
1284
|
</div>
|
|
1196
1285
|
<p class="dv-readout" data-demo="split-readout">{{ splitText }}</p>
|
|
1197
|
-
<p class="dv-hint"><code>splitPoint(source, [A, B], { autoLines: 'alternate' })</code>
|
|
1286
|
+
<p class="dv-hint"><code>splitPoint(source)</code> 省略 parts 时读取节点上的 <code>mergeInfo</code>,恢复合并前的保留站和全部来源节点,并按快照恢复线路端点、通道端点、分组与回线归属。默认 <code>select: false</code>,拆分后不选中任何恢复节点;勾选这一项才传 <code>select: true</code>。没有合并记录时自动退回 <code>splitPoint(source, [A, B], { autoLines: 'alternate' })</code> 的手工拆分;自动路径返回 <code>autoRestored: true</code> 与 <code>mergeSourceIds</code>。</p>
|
|
1198
1287
|
</div>
|
|
1199
1288
|
<div class="dv-group">
|
|
1200
1289
|
<h3>5 · 待确认合并</h3>
|
package/docs/demos/EventDemo.vue
CHANGED
|
@@ -59,7 +59,9 @@ const describePayload = (payload) => {
|
|
|
59
59
|
if (payload.labelId !== undefined) parts.push(`labelId=${payload.labelId}`)
|
|
60
60
|
if (payload.count !== undefined) parts.push(`count=${payload.count}`)
|
|
61
61
|
if (payload.channelId !== undefined) parts.push(`channel=${payload.channelId}`)
|
|
62
|
-
if (payload.camera !== undefined) parts.push('camera=…')
|
|
62
|
+
if (payload.camera !== undefined) parts.push('camera=…')
|
|
63
|
+
if (payload.idleTime !== undefined) parts.push(`idleTime=${Math.round(payload.idleTime)}ms`)
|
|
64
|
+
if (payload.timeout !== undefined) parts.push(`timeout=${payload.timeout}ms`)
|
|
63
65
|
if (payload.screen !== undefined) parts.push(`screen=[${payload.screen.map(value => Number(value).toFixed(0)).join(', ')}]`)
|
|
64
66
|
if (payload.action) {
|
|
65
67
|
const action = payload.action
|
|
@@ -114,8 +116,11 @@ const buildOptions = () => ({
|
|
|
114
116
|
channels: dataset.channels,
|
|
115
117
|
pointStyle: { size: 7, color: '#ffffff', strokeColor: '#4c8dff', strokeWidth: 1.5 },
|
|
116
118
|
lineStyle: { width: 1.5, color: 'rgba(120, 168, 226, 0.55)' },
|
|
117
|
-
showGrid: true,
|
|
118
|
-
fitPadding: 60,
|
|
119
|
+
showGrid: true,
|
|
120
|
+
fitPadding: 60,
|
|
121
|
+
// 停手 3 秒发一次 idle,事件日志与计数里都能看到;
|
|
122
|
+
// onCameraChange / cameraChange 也在这条链路上,高频开关打开后能看到逐帧载荷。
|
|
123
|
+
idleTimeout: 3000,
|
|
119
124
|
label: { content: '{name}', position: 'top', gap: 8, maxCount: 30, draggable: true, editable: true, style: { color: '#27405f', fontSize: '12px' } },
|
|
120
125
|
// 标签按优化档显示(限数 + 避让),见 docs/GisView.md 的「标签显示优化开关」
|
|
121
126
|
labelOptimize: true,
|
|
@@ -168,7 +173,10 @@ const methods = [
|
|
|
168
173
|
{ name: 'focusOn(前三个节点, { select: true })', run: () => { const ids = (view?.getData().points ?? []).slice(0, 3).map(point => point.id ?? 0); return ids.length > 0 ? view?.focusOn(ids, { select: true }) : null } },
|
|
169
174
|
{ name: 'getStats()', run: () => view?.getStats() },
|
|
170
175
|
{ name: 'getQuality()', run: () => view?.getQuality() },
|
|
171
|
-
{ name: 'getCamera()', run: () => view?.getCamera() },
|
|
176
|
+
{ name: 'getCamera()', run: () => view?.getCamera() },
|
|
177
|
+
{ name: 'getScale()', run: () => view?.getScale() },
|
|
178
|
+
{ name: 'setScale(当前 × 1.25)', run: () => view?.setScale(view.getScale() * 1.25) },
|
|
179
|
+
{ name: 'getIdleTime()', run: () => view?.getIdleTime() },
|
|
172
180
|
{ name: 'getBounds()', run: () => view?.getBounds() },
|
|
173
181
|
{ name: 'getFilterResult()', run: () => view?.getFilterResult() },
|
|
174
182
|
{ name: 'getLabelStates()', run: () => view?.getLabelStates() },
|