gis-view-webgl2 0.1.3 → 0.1.5
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 +17 -14
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +252 -37
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +79 -0
- package/docs/GisView.md +53 -53
- 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 +66 -0
- package/docs/api/index.html +22 -8
- package/docs/demos/ApiCoverageDemo.vue +1 -1
- package/docs/demos/ConnectDemo.vue +114 -3
- package/docs/demos/ExtensionDemo.vue +69 -12
- package/docs/demos/MinimapDemo.vue +62 -16
- package/docs/demos/SearchFilterDemo.vue +13 -11
- package/docs/demos/api-index.js +26 -21
- package/docs/demos/demos.js +1 -1
- package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +1 -0
- package/docs/demos/gis-view-demo/GisViewDemoStage.vue +25 -16
- package/docs/demos/gis-view-demo/panels/OpsPanels.vue +2 -2
- package/docs/demos/gis-view-demo/useGisViewDemo.js +36 -25
- package/docs/index.html +41 -4
- package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +6 -5
- package/docs/internals/16-01-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/212.md +5 -3
- package/docs/internals/18-/346/200/247/350/203/275/345/256/236/346/265/213/344/270/216WebGPU/350/257/204/344/274/260.md +12 -0
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +29 -6
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +7 -6
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +24 -3
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +1 -1
- 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 +423 -3
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +169 -62
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +32 -3
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +12 -8
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +13 -9
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +156 -12
- 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 +3 -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 +11 -5
- package/docs/steps/README.md +8 -8
- package/docs/steps/selftest-demos.html +173 -12
- package/docs/steps/selftest-docs.html +33 -4
- package/docs/steps/selftest-features.html +91 -10
- package/docs/steps/selftest-full.html +315 -12
- package/docs/steps/selftest-perf.html +39 -1
- package/docs/steps/selftest-scale.html +98 -0
- package/docs/steps/selftest-select.html +90 -0
- package/package.json +1 -1
|
@@ -28,6 +28,13 @@ const arrow = ref(true)
|
|
|
28
28
|
const arrowSize = ref(12)
|
|
29
29
|
const arrowPositionsText = ref('0.5')
|
|
30
30
|
const circuitSpacing = ref(12)
|
|
31
|
+
const startPointId = ref('')
|
|
32
|
+
const dotsVisible = ref(true)
|
|
33
|
+
const dotSize = ref(5)
|
|
34
|
+
const startDotColor = ref('#2f6fd0')
|
|
35
|
+
const endDotColor = ref('#d9544d')
|
|
36
|
+
const dotShape = ref('circle')
|
|
37
|
+
const draftLayer = ref('above')
|
|
31
38
|
const draft = ref(null)
|
|
32
39
|
const actionText = ref('配置好线条之后点「开始连线」,依次点两个节点')
|
|
33
40
|
const dataText = ref('正在创建视图…')
|
|
@@ -72,6 +79,10 @@ const createInitialLines = () => {
|
|
|
72
79
|
const isChannel = computed(() => linkKind.value !== 'line')
|
|
73
80
|
const circuits = computed(() => linkKind.value === 'channel-3' ? 3 : linkKind.value === 'channel-2' ? 2 : 1)
|
|
74
81
|
const arrowPositions = computed(() => arrowPositionsText.value.split(',').map(value => Number(value.trim())).filter(value => Number.isFinite(value)))
|
|
82
|
+
const startPointLabel = computed(() => {
|
|
83
|
+
const point = points.find(item => String(item.id) === String(startPointId.value))
|
|
84
|
+
return point ? point.name ?? String(point.id) : ''
|
|
85
|
+
})
|
|
75
86
|
const isDraftLive = computed(() => Boolean(draft.value) && ['start', 'from', 'ready', 'restart', 'pick'].includes(draft.value.phase))
|
|
76
87
|
const canFinish = computed(() => draft.value?.phase === 'ready')
|
|
77
88
|
const draftText = computed(() => {
|
|
@@ -110,10 +121,19 @@ const currentDraftStyle = () => ({
|
|
|
110
121
|
color: lineColor.value,
|
|
111
122
|
width: lineWidth.value,
|
|
112
123
|
dot: 4,
|
|
124
|
+
dots: dotsVisible.value
|
|
125
|
+
? {
|
|
126
|
+
size: dotSize.value,
|
|
127
|
+
shape: dotShape.value,
|
|
128
|
+
from: { color: startDotColor.value },
|
|
129
|
+
to: { color: endDotColor.value }
|
|
130
|
+
}
|
|
131
|
+
: false,
|
|
113
132
|
arrow: arrow.value,
|
|
114
133
|
arrowSize: arrowSize.value,
|
|
115
134
|
arrowPositions: arrowPositions.value,
|
|
116
|
-
circuitSpacing: circuitSpacing.value
|
|
135
|
+
circuitSpacing: circuitSpacing.value,
|
|
136
|
+
layer: draftLayer.value
|
|
117
137
|
})
|
|
118
138
|
|
|
119
139
|
/** 预览与真实落地共用同一份线条字段:颜色 / 线宽 / 方向箭头都从这里出发 */
|
|
@@ -129,6 +149,7 @@ const startDraft = () => {
|
|
|
129
149
|
}
|
|
130
150
|
view.endAddChannel()
|
|
131
151
|
view.beginAddChannel({
|
|
152
|
+
...(startPointId.value ? { from: startPointId.value } : {}),
|
|
132
153
|
kind: isChannel.value ? 'channel' : 'line',
|
|
133
154
|
circuits: circuits.value,
|
|
134
155
|
routing: routing.value,
|
|
@@ -136,7 +157,9 @@ const startDraft = () => {
|
|
|
136
157
|
channelDraftStyle: currentDraftStyle(),
|
|
137
158
|
lineProps: currentLineProps()
|
|
138
159
|
})
|
|
139
|
-
actionText.value =
|
|
160
|
+
actionText.value = startPointId.value
|
|
161
|
+
? `开始连线:起点已指定为 ${startPointLabel.value},下一次点节点只选终点(${isChannel.value ? `通道 ${circuits.value} 回` : '独立线路'} / ${routing.value === 'orthogonal' ? '正交折线' : '直线'})`
|
|
162
|
+
: `开始连线:先点起点,再点终点(${isChannel.value ? `通道 ${circuits.value} 回` : '独立线路'} / ${routing.value === 'orthogonal' ? '正交折线' : '直线'})`
|
|
140
163
|
pushLog('beginAddChannel', actionText.value)
|
|
141
164
|
}
|
|
142
165
|
|
|
@@ -177,6 +200,60 @@ const commitDraft = () => {
|
|
|
177
200
|
emit('notice', actionText.value)
|
|
178
201
|
}
|
|
179
202
|
|
|
203
|
+
/** 直接把本次新增的线路 / 通道对象交给 commitAddChannel:不再先 addLine / addChannel */
|
|
204
|
+
const commitWithObject = () => {
|
|
205
|
+
if (!view || !canFinish.value) {
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
const current = view.getAddChannelDraft()
|
|
209
|
+
if (!current?.from || !current?.to) {
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
sequence += 1
|
|
213
|
+
const from = current.from.id
|
|
214
|
+
const to = current.to.id
|
|
215
|
+
const middle = middlePathOf(current)
|
|
216
|
+
const fullPath = fullPathOf(current)
|
|
217
|
+
let payload
|
|
218
|
+
if (current.kind === 'line') {
|
|
219
|
+
payload = {
|
|
220
|
+
id: `connect-object-line-${sequence}`,
|
|
221
|
+
name: `对象线路 ${sequence}`,
|
|
222
|
+
from,
|
|
223
|
+
to,
|
|
224
|
+
...(fullPath ? { path: fullPath } : {}),
|
|
225
|
+
...currentLineProps()
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const lineIds = Array.from({ length: current.circuits }, (_item, index) => `connect-object-line-${sequence}-${index + 1}`)
|
|
230
|
+
const channelId = `connect-object-channel-${sequence}`
|
|
231
|
+
payload = {
|
|
232
|
+
id: channelId,
|
|
233
|
+
name: `对象通道 ${sequence}`,
|
|
234
|
+
from,
|
|
235
|
+
to,
|
|
236
|
+
lineIds,
|
|
237
|
+
...(middle ? { path: middle } : {}),
|
|
238
|
+
groups: [{ id: channelId, lineIds, ...(middle ? { path: middle } : {}) }]
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const result = view.commitAddChannel(payload)
|
|
242
|
+
refreshState()
|
|
243
|
+
if (!result.ok) {
|
|
244
|
+
actionText.value = `commitAddChannel(对象) 没有落地:${result.reason ?? '未知原因'}`
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
const lines = result.lines?.length ?? (result.line ? 1 : 0)
|
|
248
|
+
actionText.value = `commitAddChannel(对象) → ${result.kind === 'line' ? '线路对象' : '通道对象'} ${String(result.channelId ?? result.lineId ?? '')} · ${lines} 回线`
|
|
249
|
+
if (result.lineIds?.length) {
|
|
250
|
+
view.selectFeatures(result.lineIds)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
pushLog('commitAddChannel(对象)', actionText.value)
|
|
254
|
+
emit('notice', actionText.value)
|
|
255
|
+
}
|
|
256
|
+
|
|
180
257
|
const middlePathOf = current => (current.path?.length ? current.path.map(point => [point[0], point[1]]) : null)
|
|
181
258
|
const fullPathOf = current => {
|
|
182
259
|
const middle = middlePathOf(current)
|
|
@@ -267,7 +344,7 @@ const resetScene = () => {
|
|
|
267
344
|
pushLog('reset', actionText.value)
|
|
268
345
|
}
|
|
269
346
|
|
|
270
|
-
watch([lineColor, lineWidth, arrow, arrowSize, arrowPositionsText, circuitSpacing], () => {
|
|
347
|
+
watch([lineColor, lineWidth, arrow, arrowSize, arrowPositionsText, circuitSpacing, dotsVisible, dotSize, startDotColor, endDotColor, dotShape, draftLayer], () => {
|
|
271
348
|
if (view && isDraftLive.value) {
|
|
272
349
|
view.updateAddChannelStyle(currentDraftStyle())
|
|
273
350
|
draft.value = view.getAddChannelDraft()
|
|
@@ -333,6 +410,14 @@ onBeforeUnmount(() => {
|
|
|
333
410
|
</select>
|
|
334
411
|
</label>
|
|
335
412
|
</div>
|
|
413
|
+
<div class="dv-row">
|
|
414
|
+
<label>指定起点
|
|
415
|
+
<select v-model="startPointId" data-demo="connect-start-point" :disabled="isDraftLive">
|
|
416
|
+
<option value="">点击时再选</option>
|
|
417
|
+
<option v-for="point in points" :key="String(point.id)" :value="String(point.id)">{{ point.name ?? point.id }}</option>
|
|
418
|
+
</select>
|
|
419
|
+
</label>
|
|
420
|
+
</div>
|
|
336
421
|
<div class="dv-row">
|
|
337
422
|
<label>线色 <input v-model="lineColor" type="color" data-demo="connect-color" /></label>
|
|
338
423
|
<label>线宽 <input v-model.number="lineWidth" type="range" min="1" max="6" step="0.2" data-demo="connect-width" /> {{ lineWidth.toFixed(1) }}</label>
|
|
@@ -356,6 +441,31 @@ onBeforeUnmount(() => {
|
|
|
356
441
|
<p class="dv-hint">草稿样式直接传给 <code>beginAddChannel({ channelDraftStyle })</code>;连线期间改颜色 / 线宽 / 箭头位置 / 回线间距会走 <code>updateAddChannelStyle()</code>,预览立即更新。正交折线会自动补一个 90 度拐点;落地线路用完整 path,通道只写中间拐点。</p>
|
|
357
442
|
</div>
|
|
358
443
|
|
|
444
|
+
<div class="dv-group">
|
|
445
|
+
<h3>示例点与层级</h3>
|
|
446
|
+
<div class="dv-row">
|
|
447
|
+
<label><input v-model="dotsVisible" type="checkbox" data-demo="connect-dots-visible" /> 显示两端示例点</label>
|
|
448
|
+
<label>形状
|
|
449
|
+
<select v-model="dotShape" data-demo="connect-dot-shape">
|
|
450
|
+
<option value="circle">圆形</option>
|
|
451
|
+
<option value="square">方形</option>
|
|
452
|
+
</select>
|
|
453
|
+
</label>
|
|
454
|
+
<label>尺寸 <input v-model.number="dotSize" type="range" min="2" max="12" step="1" data-demo="connect-dot-size" /> {{ dotSize }}px</label>
|
|
455
|
+
</div>
|
|
456
|
+
<div class="dv-row">
|
|
457
|
+
<label>起点色 <input v-model="startDotColor" type="color" data-demo="connect-dot-from-color" :disabled="!dotsVisible" /></label>
|
|
458
|
+
<label>终点色 <input v-model="endDotColor" type="color" data-demo="connect-dot-to-color" :disabled="!dotsVisible" /></label>
|
|
459
|
+
<label>示例线路层级
|
|
460
|
+
<select v-model="draftLayer" data-demo="connect-draft-layer">
|
|
461
|
+
<option value="above">真实节点之上</option>
|
|
462
|
+
<option value="below-points">真实节点之下</option>
|
|
463
|
+
</select>
|
|
464
|
+
</label>
|
|
465
|
+
</div>
|
|
466
|
+
<p class="dv-hint">示例点始终落在被点击节点的正中心(多回线偏移不会带走它),可通过 <code>dots: false</code> 关闭,或用 <code>dots.from / dots.to</code> 分别配颜色、尺寸与形状。层级只作用于示例线 / 箭头 / <code>customDraw</code>;示例点仍画在真实节点之后,保证看得见。</p>
|
|
467
|
+
</div>
|
|
468
|
+
|
|
359
469
|
<div class="dv-group">
|
|
360
470
|
<h3>草稿与落地</h3>
|
|
361
471
|
<div class="dv-row">
|
|
@@ -364,6 +474,7 @@ onBeforeUnmount(() => {
|
|
|
364
474
|
</div>
|
|
365
475
|
<div class="dv-row">
|
|
366
476
|
<button type="button" class="dv-button" data-demo="connect-default" :disabled="!canFinish" @click="commitDraft">默认添加(commitAddChannel)</button>
|
|
477
|
+
<button type="button" class="dv-button" data-demo="connect-commit-object" :disabled="!canFinish" @click="commitWithObject">对象提交(通道 / 线路对象)</button>
|
|
367
478
|
<button type="button" class="dv-button" data-demo="connect-add-lines" :disabled="!canFinish" @click="addWithLines">用 addLines 添加</button>
|
|
368
479
|
<button type="button" class="dv-button" data-demo="connect-add-channels" :disabled="!canFinish" @click="addWithChannels">用 addChannels 添加</button>
|
|
369
480
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* 扩展能力:用同一个视图把六个扩展点串起来 ——
|
|
4
4
|
* 运行时插件(view.use)、自定义图元(registerSymbol)、自定义条件运算符(registerOperator)
|
|
5
|
-
* 自定义导出格式(defineExportFormat
|
|
5
|
+
* 自定义导出格式(defineExportFormat)、WebGL 自定义层(addWebGLLayer)与实例命令(registerCommand)。
|
|
6
6
|
* 每一块都可单独开关,读数里能直接看到注册表与可见数据。
|
|
7
7
|
*/
|
|
8
8
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
|
@@ -20,6 +20,8 @@ const webglMounted = ref(false)
|
|
|
20
20
|
const webglEnabled = ref(false)
|
|
21
21
|
const webglOrder = ref(20)
|
|
22
22
|
const webglDrawCount = ref(0)
|
|
23
|
+
const commandRegistered = ref(false)
|
|
24
|
+
const commandReadout = ref('实例命令尚未注册。注册后可以执行 focusHighestLoad。')
|
|
23
25
|
const readout = ref('正在创建视图…')
|
|
24
26
|
const exportText = ref('尚未导出。点上面的按钮,会走 defineExportFormat 注册的同步适配器。')
|
|
25
27
|
|
|
@@ -33,6 +35,7 @@ let disposeSymbol = null
|
|
|
33
35
|
let disposePlugin = null
|
|
34
36
|
let disposeExport = null
|
|
35
37
|
let disposeWebgl = null
|
|
38
|
+
let disposeCommand = null
|
|
36
39
|
|
|
37
40
|
/** WebGL 自定义层:按 gl 缓存程序 / 缓冲,draw 里只更新 uniform 与画一笔 */
|
|
38
41
|
const webglResources = new WeakMap()
|
|
@@ -172,18 +175,21 @@ const paintSparkDiamond = (context, info) => {
|
|
|
172
175
|
context.fill()
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
const visiblePointCount = () => view?.getVisibleData()?.points?.length ?? points.length
|
|
176
|
-
const
|
|
178
|
+
const visiblePointCount = () => view?.getVisibleData()?.points?.length ?? points.length
|
|
179
|
+
const COMMAND_NAME = 'focusHighestLoad'
|
|
180
|
+
const refreshReadout = () => {
|
|
177
181
|
if (!view) {
|
|
178
182
|
return
|
|
179
183
|
}
|
|
180
184
|
const operators = view.listOperators()
|
|
181
185
|
const symbols = view.listSymbols()
|
|
186
|
+
const commands = view.listCommands()
|
|
182
187
|
readout.value = [
|
|
183
188
|
`自定义运算符 ${operators.includes('evenIndex') ? 'evenIndex 已注册' : '未注册'}`,
|
|
184
189
|
`筛选 ${operatorOn.value ? '开' : '关'}(可见 ${visiblePointCount()} / ${points.length} 个)`,
|
|
185
190
|
`自定义图元 ${symbols.includes('spark') ? `spark 已注册(${symbolVariant.value === 'diamond' ? '菱形' : '十字'})` : '未注册'}${symbolOn.value ? ' / 使用中' : ''}`,
|
|
186
191
|
`运行时插件 ${pluginOn.value ? '已挂载' : '未挂载'}`,
|
|
192
|
+
`实例命令 ${commands.includes(COMMAND_NAME) ? `${COMMAND_NAME} 已注册(${commands.length} 个)` : '未注册'}`,
|
|
187
193
|
`WebGL 上下文 ${view.gl ? '可直接取' : '不可用'}`,
|
|
188
194
|
`WebGL 层 ${webglMounted.value ? `已挂载 · ${webglEnabled.value ? '绘制中' : '已隐藏'} · order ${webglOrder.value} · draw ${webglDrawCount.value} 次` : '未挂载'}`
|
|
189
195
|
].join(' · ')
|
|
@@ -199,7 +205,7 @@ const buildOptions = () => ({
|
|
|
199
205
|
fitPadding: 40,
|
|
200
206
|
features: 'view',
|
|
201
207
|
label: { enabled: true, content: '{name}', position: 'top', gap: 7, maxCount: 20, style: { color: '#27405f', fontSize: '11px' } },
|
|
202
|
-
onReady: () => emit('notice', '扩展能力:已挂载
|
|
208
|
+
onReady: () => emit('notice', '扩展能力:已挂载 6 个扩展点')
|
|
203
209
|
})
|
|
204
210
|
|
|
205
211
|
const toggleOperator = () => {
|
|
@@ -249,10 +255,47 @@ const togglePlugin = () => {
|
|
|
249
255
|
disposePlugin?.()
|
|
250
256
|
disposePlugin = null
|
|
251
257
|
}
|
|
252
|
-
refreshReadout()
|
|
258
|
+
refreshReadout()
|
|
253
259
|
emit('notice', pluginOn.value ? '插件已挂载:左下角出现徽标' : '插件已卸载')
|
|
254
260
|
}
|
|
255
261
|
|
|
262
|
+
/** 实例命令:业务侧把「选中最重载节点并定位」注册进视图;handler 里通过 context.view 继续调实例方法 */
|
|
263
|
+
const toggleCommand = () => {
|
|
264
|
+
if (!view) {
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
if (commandRegistered.value) {
|
|
268
|
+
disposeCommand?.()
|
|
269
|
+
disposeCommand = null
|
|
270
|
+
commandRegistered.value = false
|
|
271
|
+
commandReadout.value = `已卸载 ${COMMAND_NAME};listCommands() 里不再有它。`
|
|
272
|
+
refreshReadout()
|
|
273
|
+
emit('notice', `实例命令 ${COMMAND_NAME} 已卸载`)
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
disposeCommand = view.registerCommand(COMMAND_NAME, (payload, context) => {
|
|
277
|
+
const threshold = Number(payload?.threshold ?? 1)
|
|
278
|
+
const target = points.find(point => Number(point.max_loading_rate) >= threshold) ?? points[0]
|
|
279
|
+
context.view.focusOn(target.id, { select: true, zoom: true, maxZoom: 8 })
|
|
280
|
+
return { id: target.id, name: target.name, threshold, selected: context.view.getSelectionData().count }
|
|
281
|
+
})
|
|
282
|
+
commandRegistered.value = view.listCommands().includes(COMMAND_NAME)
|
|
283
|
+
commandReadout.value = `${COMMAND_NAME} 已注册,可执行;同名再注册会覆盖。`
|
|
284
|
+
refreshReadout()
|
|
285
|
+
emit('notice', `实例命令 ${COMMAND_NAME} 已注册`)
|
|
286
|
+
}
|
|
287
|
+
const runCommand = () => {
|
|
288
|
+
if (!view) {
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
const result = view.executeCommand(COMMAND_NAME, { threshold: 1 })
|
|
292
|
+
commandReadout.value = result.ok
|
|
293
|
+
? `executeCommand -> ok: true · value: ${JSON.stringify(result.value)}`
|
|
294
|
+
: `executeCommand -> ok: false · ${result.reason ?? '未知原因'}`
|
|
295
|
+
refreshReadout()
|
|
296
|
+
emit('notice', result.ok ? `实例命令已执行:${result.value?.name ?? COMMAND_NAME}` : `实例命令执行失败:${result.reason ?? '未知原因'}`)
|
|
297
|
+
}
|
|
298
|
+
|
|
256
299
|
/** 挂载 / 移除 WebGL 自定义层:层本身不改数据,只在每帧最后叠加一圈光环 */
|
|
257
300
|
const toggleWebgl = () => {
|
|
258
301
|
if (!view) {
|
|
@@ -350,6 +393,8 @@ onBeforeUnmount(() => {
|
|
|
350
393
|
disposeExport = null
|
|
351
394
|
disposeWebgl?.()
|
|
352
395
|
disposeWebgl = null
|
|
396
|
+
disposeCommand?.()
|
|
397
|
+
disposeCommand = null
|
|
353
398
|
view?.destroy()
|
|
354
399
|
view = null
|
|
355
400
|
})
|
|
@@ -365,7 +410,7 @@ onBeforeUnmount(() => {
|
|
|
365
410
|
</div>
|
|
366
411
|
<aside class="dv-panel">
|
|
367
412
|
<div class="dv-group">
|
|
368
|
-
<h3
|
|
413
|
+
<h3>六个扩展点</h3>
|
|
369
414
|
<div class="dv-row">
|
|
370
415
|
<button
|
|
371
416
|
type="button"
|
|
@@ -400,6 +445,17 @@ onBeforeUnmount(() => {
|
|
|
400
445
|
<div class="dv-row">
|
|
401
446
|
<button type="button" class="dv-button dv-button--primary" data-demo="extension-export" @click="exportSummary">自定义格式导出</button>
|
|
402
447
|
</div>
|
|
448
|
+
<div class="dv-row">
|
|
449
|
+
<button
|
|
450
|
+
type="button"
|
|
451
|
+
class="dv-button"
|
|
452
|
+
:class="{ 'dv-button--primary': commandRegistered }"
|
|
453
|
+
:aria-pressed="commandRegistered"
|
|
454
|
+
data-demo="extension-command-register"
|
|
455
|
+
@click="toggleCommand"
|
|
456
|
+
>{{ commandRegistered ? '卸载实例命令' : '注册实例命令' }}</button>
|
|
457
|
+
<button type="button" class="dv-button" :disabled="!commandRegistered" data-demo="extension-command-run" @click="runCommand">执行命令</button>
|
|
458
|
+
</div>
|
|
403
459
|
<div class="dv-row">
|
|
404
460
|
<button
|
|
405
461
|
type="button"
|
|
@@ -415,12 +471,13 @@ onBeforeUnmount(() => {
|
|
|
415
471
|
<button type="button" class="dv-button" :disabled="!webglMounted" data-demo="extension-webgl-order" @click="cycleWebglOrder">order {{ webglOrder }}(切换)</button>
|
|
416
472
|
<button type="button" class="dv-button" :disabled="!webglMounted" data-demo="extension-webgl-request" @click="requestWebglFrame">requestRender()</button>
|
|
417
473
|
</div>
|
|
418
|
-
<p class="dv-hint">运算符注册进条件语言,图元注册进符号表,插件挂在实例上,导出格式挂在模块注册表,WebGL 层直接拿 <code>gl</code>
|
|
419
|
-
</div>
|
|
420
|
-
<div class="dv-group">
|
|
421
|
-
<h3>注册表读数</h3>
|
|
422
|
-
<p class="dv-readout" data-demo="extension-readout" aria-live="polite">{{ readout }}</p>
|
|
474
|
+
<p class="dv-hint">运算符注册进条件语言,图元注册进符号表,插件挂在实例上,导出格式挂在模块注册表,WebGL 层直接拿 <code>gl</code> 在内置内容之后画自己的东西,实例命令把业务动作注册进当前视图;六者都不需要改组件源码。图元切换走一次 <code>patchPoints</code> 批量补丁,WebGL 层可用 <code>enabled</code> / <code>order</code> 原地维护,命令通过 <code>executeCommand</code> 同步执行并返回 handler 的结果。</p>
|
|
423
475
|
</div>
|
|
476
|
+
<div class="dv-group">
|
|
477
|
+
<h3>注册表读数</h3>
|
|
478
|
+
<p class="dv-readout" data-demo="extension-readout" aria-live="polite">{{ readout }}</p>
|
|
479
|
+
<p class="dv-readout" data-demo="extension-command-readout" aria-live="polite">{{ commandReadout }}</p>
|
|
480
|
+
</div>
|
|
424
481
|
<div class="dv-group">
|
|
425
482
|
<h3>导出结果</h3>
|
|
426
483
|
<pre class="dv-readout demo-extension-export" data-demo="extension-export-text">{{ exportText }}</pre>
|
|
@@ -17,12 +17,17 @@ const enabled = ref(true)
|
|
|
17
17
|
const size = ref('medium')
|
|
18
18
|
const position = ref('bottom-right')
|
|
19
19
|
const extent = ref('auto')
|
|
20
|
-
const ruleMode = ref('
|
|
20
|
+
const ruleMode = ref('inherit')
|
|
21
21
|
const levelMode = ref('all')
|
|
22
|
-
const
|
|
22
|
+
const pointSizeScale = ref(1)
|
|
23
|
+
const lineWidthScale = ref(1)
|
|
24
|
+
const pointZoomScale = ref(0)
|
|
25
|
+
const lineZoomScale = ref(0)
|
|
23
26
|
const channelCircuits = ref(true)
|
|
24
|
-
const
|
|
27
|
+
const zoomMode = ref('manual')
|
|
28
|
+
const followZoomScale = ref(1)
|
|
25
29
|
const dragAction = ref('pan')
|
|
30
|
+
const extentFollowCamera = ref(false)
|
|
26
31
|
const maxPixelRatio = ref('2')
|
|
27
32
|
const gridPlacement = ref('below-data')
|
|
28
33
|
const customLayers = ref(true)
|
|
@@ -125,6 +130,7 @@ const minimapOptions = () => {
|
|
|
125
130
|
margin: 12,
|
|
126
131
|
padding: 0.05,
|
|
127
132
|
extent: extent.value,
|
|
133
|
+
extentFollowCamera: extentFollowCamera.value,
|
|
128
134
|
background: 'inherit',
|
|
129
135
|
backgroundStyle: 'rgba(255, 255, 255, 0.94)',
|
|
130
136
|
borderColor: '#c7d8eb',
|
|
@@ -140,16 +146,19 @@ const minimapOptions = () => {
|
|
|
140
146
|
levels: levelMode.value === 'top' ? [2] : undefined,
|
|
141
147
|
cascade: 'both',
|
|
142
148
|
layered: true,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
// 自己规则 / 沿用主图都乘这一份;与主图相机倍数、小图自身倍数都无关
|
|
150
|
+
pointSizeScale: Number(pointSizeScale.value),
|
|
151
|
+
lineWidthScale: Number(lineWidthScale.value),
|
|
152
|
+
// 只有小图自身放大时才按这两个指数联动;沿用主图的节点 / 线路也一样
|
|
153
|
+
pointZoomScale: Number(pointZoomScale.value),
|
|
154
|
+
lineZoomScale: Number(lineZoomScale.value),
|
|
147
155
|
boxStyle: { fill: '#3678bf', fillOpacity: 0.16, stroke: '#3678bf', strokeWidth: 1, strokeStyle: 'dashed', radius: 3 },
|
|
148
156
|
selectStyle: { fill: '#f0a43c', stroke: '#f0a43c' },
|
|
149
157
|
interactive: true,
|
|
150
158
|
onInteraction: handleMinimapInteraction,
|
|
151
159
|
longPressDelay: 180,
|
|
152
|
-
zoomable:
|
|
160
|
+
zoomable: zoomMode.value === 'manual' ? true : zoomMode.value === 'follow' ? 'follow' : false,
|
|
161
|
+
followZoomScale: Number(followZoomScale.value),
|
|
153
162
|
minZoom: 0.7,
|
|
154
163
|
maxZoom: 6,
|
|
155
164
|
zoomStep: 1.35,
|
|
@@ -168,10 +177,14 @@ const refreshReadout = () => {
|
|
|
168
177
|
const sizeText = root ? `${Math.round(root.getBoundingClientRect().width)}×${Math.round(root.getBoundingClientRect().height)}` : '未挂载'
|
|
169
178
|
readout.value = `${sizeText} · 备份 ${canvas?.width ?? 0}×${canvas?.height ?? 0} · 像素比 ${ratio ? ratio.toFixed(2) : '—'} · 范围 ${extent.value} · 自定义层 ${options.layers.length}`
|
|
170
179
|
if (view) {
|
|
171
|
-
zoomReadout.value = `小图倍数 ${view.getMinimapZoom().toFixed(2)} · ${
|
|
180
|
+
zoomReadout.value = `小图倍数 ${view.getMinimapZoom().toFixed(2)} · ${zoomModeLabel.value}`
|
|
172
181
|
}
|
|
173
182
|
}
|
|
174
183
|
|
|
184
|
+
const zoomModeLabel = computed(() => zoomMode.value === 'manual'
|
|
185
|
+
? '手动缩放小图'
|
|
186
|
+
: zoomMode.value === 'follow' ? `跟随主图(指数 ${Number(followZoomScale.value).toFixed(1)})` : '不缩放小图,滚轮缩放主图')
|
|
187
|
+
|
|
175
188
|
const applyMinimap = () => {
|
|
176
189
|
if (!view) {
|
|
177
190
|
return
|
|
@@ -193,10 +206,20 @@ const zoomMinimap = (factor) => {
|
|
|
193
206
|
|
|
194
207
|
const refreshZoom = () => {
|
|
195
208
|
if (view) {
|
|
196
|
-
zoomReadout.value = `小图倍数 ${view.getMinimapZoom().toFixed(2)} · ${
|
|
209
|
+
zoomReadout.value = `小图倍数 ${view.getMinimapZoom().toFixed(2)} · ${zoomModeLabel.value}`
|
|
197
210
|
}
|
|
198
211
|
}
|
|
199
212
|
|
|
213
|
+
/** 拖拽演示:框选不依赖 zoomable;平移才需要手动缩放后内容有可移动余量 */
|
|
214
|
+
const showDragDemo = (action) => {
|
|
215
|
+
dragAction.value = action
|
|
216
|
+
zoomMode.value = action === 'box' ? 'off' : 'manual'
|
|
217
|
+
applyMinimap()
|
|
218
|
+
eventText.value = action === 'box'
|
|
219
|
+
? '拖拽演示:缩放模式已关闭,在小图空白处直接拖动仍能拉出框选矩形'
|
|
220
|
+
: '拖拽演示:已切到手动缩放,在小图空白处拖动平移小图内容'
|
|
221
|
+
}
|
|
222
|
+
|
|
200
223
|
const refreshLayerReadout = () => {
|
|
201
224
|
layerReadout.value = `网格层 ${layerPaints.grid} 次(${layerPaints.gridStage}) · 指北针层 ${layerPaints.compass} 次`
|
|
202
225
|
refreshReadout()
|
|
@@ -289,6 +312,9 @@ const sizeSummary = computed(() => `${SIZES[size.value]?.[0] ?? 0}×${SIZES[size
|
|
|
289
312
|
</select>
|
|
290
313
|
</label>
|
|
291
314
|
</div>
|
|
315
|
+
<div class="dv-row">
|
|
316
|
+
<label><input v-model="extentFollowCamera" type="checkbox" data-demo="minimap-extent-follow" @change="applyMinimap" /> 自动范围随相机收窄(默认关)</label>
|
|
317
|
+
</div>
|
|
292
318
|
<p class="dv-hint"><code>maxPixelRatio</code> 越大越清晰,也越吃填充率;高分屏按机器档位调到 2 通常就够。</p>
|
|
293
319
|
</div>
|
|
294
320
|
<div class="dv-group">
|
|
@@ -309,11 +335,24 @@ const sizeSummary = computed(() => `${SIZES[size.value]?.[0] ?? 0}×${SIZES[size
|
|
|
309
335
|
</label>
|
|
310
336
|
</div>
|
|
311
337
|
<div class="dv-row">
|
|
312
|
-
<label
|
|
338
|
+
<label>节点整体倍数 <input v-model.number="pointSizeScale" type="range" min="0" max="3" step="0.1" data-demo="minimap-point-size-scale" @change="applyMinimap" /> {{ Number(pointSizeScale).toFixed(1) }}</label>
|
|
339
|
+
<label>线路整体倍数 <input v-model.number="lineWidthScale" type="range" min="0" max="3" step="0.1" data-demo="minimap-line-width-scale" @change="applyMinimap" /> {{ Number(lineWidthScale).toFixed(1) }}</label>
|
|
340
|
+
</div>
|
|
341
|
+
<div class="dv-row">
|
|
342
|
+
<label>小图放大节点 <input v-model.number="pointZoomScale" type="range" min="0" max="1" step="0.1" data-demo="minimap-point-zoom-scale" @change="applyMinimap" /> {{ Number(pointZoomScale).toFixed(1) }}</label>
|
|
343
|
+
<label>小图放大线路 <input v-model.number="lineZoomScale" type="range" min="0" max="1" step="0.1" data-demo="minimap-line-zoom-scale" @change="applyMinimap" /> {{ Number(lineZoomScale).toFixed(1) }}</label>
|
|
344
|
+
</div>
|
|
345
|
+
<div class="dv-row">
|
|
313
346
|
<label><input v-model="channelCircuits" type="checkbox" data-demo="minimap-circuits" @change="applyMinimap" /> 画全部回线</label>
|
|
314
347
|
</div>
|
|
315
348
|
<div class="dv-row">
|
|
316
|
-
<label
|
|
349
|
+
<label>缩放模式
|
|
350
|
+
<select v-model="zoomMode" data-demo="minimap-zoomable" @change="applyMinimap">
|
|
351
|
+
<option value="off">关闭</option>
|
|
352
|
+
<option value="manual">手动缩放小图</option>
|
|
353
|
+
<option value="follow">跟随主图</option>
|
|
354
|
+
</select>
|
|
355
|
+
</label>
|
|
317
356
|
<label>空白拖动
|
|
318
357
|
<select v-model="dragAction" data-demo="minimap-drag-action" @change="applyMinimap">
|
|
319
358
|
<option value="pan">平移小图</option>
|
|
@@ -321,7 +360,14 @@ const sizeSummary = computed(() => `${SIZES[size.value]?.[0] ?? 0}×${SIZES[size
|
|
|
321
360
|
</select>
|
|
322
361
|
</label>
|
|
323
362
|
</div>
|
|
324
|
-
<
|
|
363
|
+
<div class="dv-row">
|
|
364
|
+
<label>跟随指数 <input v-model.number="followZoomScale" type="range" min="0" max="2" step="0.1" data-demo="minimap-follow-scale" @change="applyMinimap" /> {{ Number(followZoomScale).toFixed(1) }}</label>
|
|
365
|
+
</div>
|
|
366
|
+
<div class="dv-row">
|
|
367
|
+
<button type="button" class="dv-button" data-demo="minimap-drag-box" @click="showDragDemo('box')">拖拽演示:框选</button>
|
|
368
|
+
<button type="button" class="dv-button" data-demo="minimap-drag-pan" @click="showDragDemo('pan')">拖拽演示:平移</button>
|
|
369
|
+
</div>
|
|
370
|
+
<p class="dv-hint">沿用主图规则时,节点 / 线路先按「小图视口 ÷ 主图视口」缩放,主图放大缩小不会改变它们的大小;滚轮或 + / - 放大鹰眼图时,再按 <code>pointZoomScale</code> / <code>lineZoomScale</code> 的指数变化。两个整体倍数对自有规则与沿用主图的要素都生效。<code>zoomable: true</code> 是小图手动缩放,<code>'follow'</code> 是整体范围跟随主图,两者互斥;<code>followZoomScale</code> 是主图倍数的指数。框选不依赖缩放开关:选「关闭」+ <code>dragAction: 'box'</code>,空白处拖动仍会直接框选。视口框可以拖,空白处长按跟随。</p>
|
|
325
371
|
</div>
|
|
326
372
|
<div class="dv-group">
|
|
327
373
|
<h3>扩展层</h3>
|
|
@@ -341,9 +387,9 @@ const sizeSummary = computed(() => `${SIZES[size.value]?.[0] ?? 0}×${SIZES[size
|
|
|
341
387
|
<div class="dv-group">
|
|
342
388
|
<h3>小图倍数</h3>
|
|
343
389
|
<div class="dv-row">
|
|
344
|
-
<button type="button" class="dv-button" data-demo="minimap-zoom-out" @click="zoomMinimap(1 / 1.35)">-</button>
|
|
345
|
-
<button type="button" class="dv-button dv-button--primary" data-demo="minimap-zoom-reset" @click="resetZoom">1×</button>
|
|
346
|
-
<button type="button" class="dv-button" data-demo="minimap-zoom-in" @click="zoomMinimap(1.35)">+</button>
|
|
390
|
+
<button type="button" class="dv-button" data-demo="minimap-zoom-out" :disabled="zoomMode === 'follow'" @click="zoomMinimap(1 / 1.35)">-</button>
|
|
391
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="minimap-zoom-reset" :disabled="zoomMode === 'follow'" @click="resetZoom">1×</button>
|
|
392
|
+
<button type="button" class="dv-button" data-demo="minimap-zoom-in" :disabled="zoomMode === 'follow'" @click="zoomMinimap(1.35)">+</button>
|
|
347
393
|
</div>
|
|
348
394
|
<p class="dv-readout" data-demo="minimap-zoom-readout">{{ zoomReadout }}</p>
|
|
349
395
|
</div>
|
|
@@ -48,10 +48,11 @@ let view = null
|
|
|
48
48
|
let requestSeq = 0
|
|
49
49
|
|
|
50
50
|
const SCOPES = [
|
|
51
|
-
{ id: 'all', label: '
|
|
51
|
+
{ id: 'all', label: '全部四类' },
|
|
52
52
|
{ id: 'points', label: '只看节点' },
|
|
53
53
|
{ id: 'lines', label: '只看线路' },
|
|
54
|
-
{ id: 'channels', label: '只看通道' }
|
|
54
|
+
{ id: 'channels', label: '只看通道' },
|
|
55
|
+
{ id: 'groups', label: '只看分组' }
|
|
55
56
|
]
|
|
56
57
|
const FIELD_MODES = [
|
|
57
58
|
{ id: 'all', label: '全部字段', fields: null },
|
|
@@ -64,6 +65,7 @@ const PRESETS = [
|
|
|
64
65
|
{ id: 'name', demo: 'search-filter-preset-name', label: '变电站(limit 3)', keyword: '变电站', scope: 'points', field: 'all', limit: 3 },
|
|
65
66
|
{ id: 'owner', demo: 'search-filter-preset-owner', label: '北城运检(props.owner)', keyword: '北城运检', scope: 'points', field: 'owner', limit: 10 },
|
|
66
67
|
{ id: 'channel', demo: 'search-filter-preset-channel', label: '甲通道(scope=channels)', keyword: '甲通道', scope: 'channels', field: 'all', limit: 10 },
|
|
68
|
+
{ id: 'group', demo: 'search-filter-preset-group', label: 'channel-a(scope=groups)', keyword: 'channel-a', scope: 'groups', field: 'all', limit: 10 },
|
|
67
69
|
{ id: 'symbol', demo: 'search-filter-preset-symbol', label: 'square(显式 symbol)', keyword: 'square', scope: 'points', field: 'symbol', limit: 10 }
|
|
68
70
|
]
|
|
69
71
|
const FILTERS = [
|
|
@@ -72,15 +74,15 @@ const FILTERS = [
|
|
|
72
74
|
{ id: 'zone', demo: 'search-filter-filter-zone', label: '分区 2 / 4', condition: { points: { zone_name: { in: ['分区 2', '分区 4'] } }, cascade: 'binding' } },
|
|
73
75
|
{ id: 'loading', demo: 'search-filter-filter-loading', label: '线路负载 ≥ 1', condition: { lines: { max_loading_rate: { gte: 1 } } } }
|
|
74
76
|
]
|
|
75
|
-
const KIND_LABELS = { point: '节点', line: '线路', channel: '通道' }
|
|
77
|
+
const KIND_LABELS = { point: '节点', line: '线路', channel: '通道', group: '分组' }
|
|
76
78
|
|
|
77
|
-
const scopeLabel = computed(() => SCOPES.find(item => item.id === scope.value)?.label ?? '
|
|
79
|
+
const scopeLabel = computed(() => SCOPES.find(item => item.id === scope.value)?.label ?? '全部四类')
|
|
78
80
|
const searchSummary = computed(() => {
|
|
79
81
|
const result = searchResult.value
|
|
80
82
|
if (!result) {
|
|
81
83
|
return '还没有搜索'
|
|
82
84
|
}
|
|
83
|
-
return `命中 ${result.total} 条:节点 ${result.points.length} / 线路 ${result.lines.length} / 通道 ${result.channels.length}`
|
|
85
|
+
return `命中 ${result.total} 条:节点 ${result.points.length} / 线路 ${result.lines.length} / 通道 ${result.channels.length} / 分组 ${result.groups?.length ?? 0}`
|
|
84
86
|
})
|
|
85
87
|
const searchMeta = computed(() => {
|
|
86
88
|
const result = searchResult.value
|
|
@@ -172,8 +174,8 @@ const applyWorker = () => {
|
|
|
172
174
|
: 'searchWorker=false:searchAsync 退回主线程同步计算'
|
|
173
175
|
}
|
|
174
176
|
const focusHit = (hit) => {
|
|
175
|
-
view?.focusOn(hit, { select: true })
|
|
176
|
-
actionText.value = `focusOn(${KIND_LABELS[hit.kind] ?? hit.kind} #${hit.index}, { select: true })`
|
|
177
|
+
view?.focusOn(hit, { select: true, zoom: true })
|
|
178
|
+
actionText.value = `focusOn(${KIND_LABELS[hit.kind] ?? hit.kind} #${hit.index}, { select: true, zoom: true })`
|
|
177
179
|
}
|
|
178
180
|
const focusAllHits = () => {
|
|
179
181
|
const hits = searchResult.value?.hits ?? []
|
|
@@ -181,8 +183,8 @@ const focusAllHits = () => {
|
|
|
181
183
|
actionText.value = '当前没有可定位的命中'
|
|
182
184
|
return
|
|
183
185
|
}
|
|
184
|
-
view?.focusOn(hits, { select: true })
|
|
185
|
-
actionText.value = `focusOn(hits, { select: true }):${hits.length} 条命中一起进包围盒并选中`
|
|
186
|
+
view?.focusOn(hits, { select: true, zoom: true })
|
|
187
|
+
actionText.value = `focusOn(hits, { select: true, zoom: true }):${hits.length} 条命中一起进包围盒并选中`
|
|
186
188
|
}
|
|
187
189
|
const readVisibleData = () => {
|
|
188
190
|
if (!view) {
|
|
@@ -296,7 +298,7 @@ onBeforeUnmount(() => {
|
|
|
296
298
|
<div class='dv-chips'>
|
|
297
299
|
<button v-for='preset in PRESETS' :key='preset.id' type='button' class='dv-chip' :data-demo='preset.demo' @click='choosePreset(preset)'>{{ preset.label }}</button>
|
|
298
300
|
</div>
|
|
299
|
-
<p class='dv-hint'
|
|
301
|
+
<p class='dv-hint'>scope 可写 points / lines / channels / groups,也认 point / line / channel / group;显式 fields 能搜默认会跳过的 symbol。搜索基于全量数据,不隐藏、也不自动移动视角。</p>
|
|
300
302
|
<div class='dv-row'>
|
|
301
303
|
<button type='button' class='dv-button' data-demo='search-filter-focus-all' @click='focusAllHits'>聚焦并选中全部命中</button>
|
|
302
304
|
</div>
|
|
@@ -335,7 +337,7 @@ onBeforeUnmount(() => {
|
|
|
335
337
|
</div>
|
|
336
338
|
<div class='dv-group'>
|
|
337
339
|
<h3>两件事别混用</h3>
|
|
338
|
-
<p class='dv-hint'>search 只把要素找出来,结果带 bounds;要居中就再调 focusOn。setFilter
|
|
340
|
+
<p class='dv-hint'>search 只把要素找出来,结果带 bounds;要居中就再调 focusOn。setFilter 只隐藏显示、不删数据,也不清空选中集:条件放宽后,原本选中的要素仍保持选中。</p>
|
|
339
341
|
</div>
|
|
340
342
|
</aside>
|
|
341
343
|
</section>
|