gis-view-webgl2 0.1.2 → 0.1.4
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 +21 -14
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +421 -76
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +151 -0
- package/docs/GisView.md +4 -4
- 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 +137 -0
- package/docs/api/index.html +23 -9
- package/docs/demos/ApiCoverageDemo.vue +644 -0
- package/docs/demos/ChannelDemo.vue +14 -12
- package/docs/demos/ClusterDemo.vue +251 -0
- package/docs/demos/CopyMergeDemo.vue +3 -4
- package/docs/demos/EventDemo.vue +3 -3
- package/docs/demos/ExtensionDemo.vue +69 -12
- package/docs/demos/GisViewDemo.vue +64 -8809
- package/docs/demos/SearchFilterDemo.vue +13 -11
- package/docs/demos/api-index.js +58 -47
- package/docs/demos/demos.js +34 -17
- package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +29 -0
- package/docs/demos/gis-view-demo/GisViewDemoDialogs.vue +82 -0
- package/docs/demos/gis-view-demo/GisViewDemoFooter.vue +16 -0
- package/docs/demos/gis-view-demo/GisViewDemoHeading.vue +16 -0
- package/docs/demos/gis-view-demo/GisViewDemoPanelStack.vue +71 -0
- package/docs/demos/gis-view-demo/GisViewDemoStage.vue +219 -0
- package/docs/demos/gis-view-demo/demo.css +1230 -0
- package/docs/demos/gis-view-demo/demoContext.js +12 -0
- package/docs/demos/gis-view-demo/panels/DataPanels.vue +303 -0
- package/docs/demos/gis-view-demo/panels/OpsPanels.vue +174 -0
- package/docs/demos/gis-view-demo/panels/StylePanels.vue +561 -0
- package/docs/demos/gis-view-demo/panels/ViewPanels.vue +396 -0
- package/docs/demos/gis-view-demo/useGisViewDemo.js +6188 -0
- package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +3 -2
- package/docs/index.html +41 -4
- package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +1 -1
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +3 -2
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +104 -16
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +15 -14
- 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 +40 -3
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +50 -3
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +6 -2
- 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 +39 -7
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +13 -4
- package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +3 -0
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +121 -49
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +37 -3
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +17 -8
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +45 -25
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +91 -20
- 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/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +25 -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 +11 -8
- package/docs/steps/README.md +17 -11
- package/docs/steps/selftest-actions.html +3 -5
- package/docs/steps/selftest-demo.html +27 -18
- package/docs/steps/selftest-demos.html +409 -32
- package/docs/steps/selftest-docs.html +33 -4
- package/docs/steps/selftest-effects.html +200 -0
- package/docs/steps/selftest-features.html +603 -21
- package/docs/steps/selftest-labels.html +74 -0
- package/docs/steps/selftest-perf.html +293 -20
- package/docs/steps/selftest-scale.html +150 -29
- package/docs/steps/selftest-select.html +90 -0
- package/docs/steps/step-10-full.js +1 -1
- package/docs/steps/step-12-events.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/**
|
|
3
|
+
* 接口能力:把最近几轮新增、但不容易在单个业务场景里看全的 API 放在一页。
|
|
4
|
+
*
|
|
5
|
+
* 四块:
|
|
6
|
+
* ① 级联高亮的三种触发(单击 / 多选 / 框选)可以独立开关;
|
|
7
|
+
* ② selectFeatures / selectWaypoint 的程序化选中(节点、线路、通道、分组、拐点);
|
|
8
|
+
* ③ focusOn 的 point / line / channel / group 四种显式类型;
|
|
9
|
+
* ④ lineFollowsNode 开关,隐藏端点节点时对照线路是否跟随;
|
|
10
|
+
* 另外补几个常用只读接口(坐标互转、视口与渲染器、场景快照、下一帧等待等)的现场读数。
|
|
11
|
+
*/
|
|
12
|
+
import { nextTick, onBeforeUnmount, onMounted, ref, shallowRef } from 'vue'
|
|
13
|
+
import { createGisView } from '../../src/index.js'
|
|
14
|
+
import { buildDemoData } from './shared/demoData.js'
|
|
15
|
+
import './shared/demoStyle.css'
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits(['notice'])
|
|
18
|
+
const container = ref(null)
|
|
19
|
+
const view = shallowRef(null)
|
|
20
|
+
const dataset = buildDemoData({ count: 24 })
|
|
21
|
+
|
|
22
|
+
const triggerOnClick = ref(true)
|
|
23
|
+
const triggerOnMultiSelect = ref(true)
|
|
24
|
+
const triggerOnBoxSelect = ref(true)
|
|
25
|
+
const lineFollowsNode = ref(true)
|
|
26
|
+
const endpointHidden = ref(false)
|
|
27
|
+
const channelId = 'channel-a'
|
|
28
|
+
let groupId = ''
|
|
29
|
+
|
|
30
|
+
const cascadeReadout = ref('级联高亮:点节点 / 线路 / 通道回线试试;框选也会触发')
|
|
31
|
+
const selectionReadout = ref('尚未选中')
|
|
32
|
+
const focusReadout = ref('尚未聚焦')
|
|
33
|
+
const visibilityReadout = ref('')
|
|
34
|
+
const apiReadout = ref('点一个只读接口看结果')
|
|
35
|
+
const advancedReadout = ref('这里集中演示样式、装饰、通道写入、历史与运行时清理接口')
|
|
36
|
+
const styleApplied = ref(false)
|
|
37
|
+
const geoBackgroundOn = ref(false)
|
|
38
|
+
let timer = null
|
|
39
|
+
|
|
40
|
+
const toggleStyleApis = () => {
|
|
41
|
+
const current = view.value
|
|
42
|
+
if (!current) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
if (styleApplied.value) {
|
|
46
|
+
current.updatePointStyle({ color: '#fff', size: 6, strokeColor: '#4c8dff', strokeWidth: 1.4 })
|
|
47
|
+
current.updateLineStyle({ color: 'rgba(100, 150, 220, 0.55)', width: 1.5 })
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
current.updatePointStyle({ color: '#fde68a', size: 8, strokeColor: '#f59e0b', strokeWidth: 2 })
|
|
51
|
+
current.updateLineStyle({ color: '#fbbf24', width: 2.6 })
|
|
52
|
+
}
|
|
53
|
+
styleApplied.value = !styleApplied.value
|
|
54
|
+
const options = current.getOptions()
|
|
55
|
+
advancedReadout.value = `updatePointStyle / updateLineStyle 字段级合并:点 size=${options.pointStyle?.size ?? '—'} / 线 width=${options.lineStyle?.width ?? '—'} · ${styleApplied.value ? '已应用高亮样式' : '已恢复演示样式'}`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const exercisePointDecorationApis = () => {
|
|
59
|
+
const current = view.value
|
|
60
|
+
if (!current) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
const before = current.getPointDecorations('node-0').length
|
|
64
|
+
const added = current.addPointDecoration('node-0', {
|
|
65
|
+
symbol: 'square',
|
|
66
|
+
color: '#fbbf24',
|
|
67
|
+
strokeColor: '#0f172a',
|
|
68
|
+
strokeWidth: 1,
|
|
69
|
+
size: 18,
|
|
70
|
+
offset: [20, 0]
|
|
71
|
+
})
|
|
72
|
+
const updated = current.updatePointDecoration('node-0', before, { size: 22 })
|
|
73
|
+
const afterUpdate = current.getPointDecorations('node-0').length
|
|
74
|
+
const removed = current.removePointDecoration('node-0', before)
|
|
75
|
+
advancedReadout.value = `节点装饰 CRUD:addPointDecoration=${added ? 'ok' : 'null'} · updatePointDecoration size=${updated?.size ?? '—'} · getPointDecorations=${afterUpdate} 条 · removePointDecoration=${removed ? 'ok' : 'null'}`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const exerciseLineDecorationApis = () => {
|
|
79
|
+
const current = view.value
|
|
80
|
+
if (!current) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
const before = current.getLineDecorations('line-0').length
|
|
84
|
+
const added = current.addLineDecoration('line-0', {
|
|
85
|
+
symbol: 'square',
|
|
86
|
+
color: '#22d3ee',
|
|
87
|
+
size: 14,
|
|
88
|
+
position: 0.5
|
|
89
|
+
})
|
|
90
|
+
const updated = current.updateLineDecoration('line-0', before, { size: 20 })
|
|
91
|
+
const removed = current.removeLineDecoration('line-0', before)
|
|
92
|
+
advancedReadout.value = `线路装饰 CRUD:addLineDecoration=${added ? 'ok' : 'null'} · updateLineDecoration size=${updated?.size ?? '—'} · removeLineDecoration=${removed ? 'ok' : 'null'}`
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const exerciseChannelLineApis = () => {
|
|
96
|
+
const current = view.value
|
|
97
|
+
if (!current) {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
const between = current.getChannelBetween('node-0', 'node-7')
|
|
101
|
+
current.setChannelLineWidth(channelId, 3.4)
|
|
102
|
+
const added = current.addLines({ id: 'api-temp-line', from: 'node-1', to: 'node-2', color: '#fb923c' })
|
|
103
|
+
current.clearChangedLines()
|
|
104
|
+
current.updateLine('api-temp-line', { color: '#22d3ee', width: 3 })
|
|
105
|
+
const changed = current.getChangedLines({ reasons: ['fields'] })
|
|
106
|
+
current.clearChangedLines()
|
|
107
|
+
const removed = current.removeLines('api-temp-line')
|
|
108
|
+
current.setChannelLineWidth(channelId, 2.6)
|
|
109
|
+
advancedReadout.value = `通道 / 线路写入:getChannelBetween=${between?.hasChannel ? `已有 ${between.groups.length} 组` : '无'} · setChannelLineWidth=3.4→2.6 · addLines=${added.count} 条 · getChangedLines=${changed.length} 条 · clearChangedLines + removeLines=${removed.ok ? 'ok' : 'failed'}`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const exerciseVisibilityApis = () => {
|
|
113
|
+
const current = view.value
|
|
114
|
+
const entry = current?.listPoints().find(item => String(item.item?.id) === 'node-2')
|
|
115
|
+
if (!current || !entry) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
const hiddenOutcome = current.setFeatureVisible('point', 'node-2', false)
|
|
119
|
+
const hidden = current.isPointVisible(entry.index)
|
|
120
|
+
const shownOutcome = current.setFeatureVisible('point', 'node-2', true)
|
|
121
|
+
const shown = current.isPointVisible(entry.index)
|
|
122
|
+
advancedReadout.value = `setFeatureVisible / isPointVisible:隐藏 ${hiddenOutcome.ok ? 'ok' : 'failed'} → isPointVisible=${hidden} · 恢复 ${shownOutcome.ok ? 'ok' : 'failed'} → isPointVisible=${shown}`
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const toggleGeoBackgroundApis = () => {
|
|
126
|
+
const current = view.value
|
|
127
|
+
if (!current) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
if (geoBackgroundOn.value) {
|
|
131
|
+
const labels = current.getBackgroundLabels()
|
|
132
|
+
const projection = current.getGeoProjection()
|
|
133
|
+
current.updateBackground({})
|
|
134
|
+
geoBackgroundOn.value = false
|
|
135
|
+
advancedReadout.value = `移开演示背景:清理前 getBackgroundLabels=${labels?.length ?? 0} 条 · getGeoProjection=${projection ? '有' : 'null'} · updateBackground({}) 已恢复透明底`
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
const bounds = current.getBounds()
|
|
139
|
+
const ring = [
|
|
140
|
+
[bounds.minX, bounds.minY],
|
|
141
|
+
[bounds.maxX, bounds.minY],
|
|
142
|
+
[bounds.maxX, bounds.maxY],
|
|
143
|
+
[bounds.minX, bounds.maxY],
|
|
144
|
+
[bounds.minX, bounds.minY]
|
|
145
|
+
]
|
|
146
|
+
current.updateBackground({
|
|
147
|
+
color: '#081321',
|
|
148
|
+
geoJson: {
|
|
149
|
+
type: 'FeatureCollection',
|
|
150
|
+
features: [{
|
|
151
|
+
type: 'Feature',
|
|
152
|
+
properties: { name: '接口演示范围' },
|
|
153
|
+
geometry: { type: 'Polygon', coordinates: [ring] }
|
|
154
|
+
}]
|
|
155
|
+
},
|
|
156
|
+
geoJsonStyle: {
|
|
157
|
+
areaColor: '#12253f',
|
|
158
|
+
borderColor: '#38bdf8',
|
|
159
|
+
borderWidth: 1.5,
|
|
160
|
+
fillOpacity: 0.18,
|
|
161
|
+
aspectScale: 1,
|
|
162
|
+
label: { show: true, formatter: '{name}', fontSize: 12, color: '#bae6fd' }
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
geoBackgroundOn.value = true
|
|
166
|
+
const labels = current.getBackgroundLabels()
|
|
167
|
+
const projection = current.getGeoProjection()
|
|
168
|
+
const mapped = current.mapDataToGeoJson(current.getData(), { mode: 'lngLat' })
|
|
169
|
+
advancedReadout.value = `updateBackground / GeoJSON:getBackgroundLabels=${labels?.length ?? 0} 条 · getGeoProjection=${projection ? '有' : 'null'} · mapDataToGeoJson=${mapped?.points?.length ?? 0} 个节点 / ${mapped?.lines?.length ?? 0} 条线路`
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const exerciseHistoryApis = () => {
|
|
173
|
+
const current = view.value
|
|
174
|
+
if (!current) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
const original = current.getData().points.find(item => String(item.id) === 'node-1')
|
|
178
|
+
current.updatePoint('node-1', { name: `接口历史 ${Date.now()}` })
|
|
179
|
+
const undone = current.undo()
|
|
180
|
+
const redoReady = current.canRedo()
|
|
181
|
+
const redone = current.redo()
|
|
182
|
+
const restored = current.getData().points.find(item => String(item.id) === 'node-1')
|
|
183
|
+
const mergeCommitted = current.commitPointMerge()
|
|
184
|
+
const mergeReverted = current.revertPointMerge({ silent: true })
|
|
185
|
+
const preview = current.setDropPreview({ kind: 'point', world: [original?.x ?? 0, original?.y ?? 0], item: { size: 18, color: '#22d3ee' } })
|
|
186
|
+
const previewReadout = current.getDropPreview()
|
|
187
|
+
current.setDropPreview(null)
|
|
188
|
+
advancedReadout.value = `历史 / 收尾:undo=${undone ? 'ok' : 'null'} → canRedo=${redoReady} → redo=${redone ? 'ok' : 'null'} → name=${restored?.name ?? '—'} · isLayoutReady=${current.isLayoutReady()} · commitPointMerge=${mergeCommitted} / revertPointMerge=${mergeReverted ? 'payload' : 'null'} · getDropPreview=${previewReadout?.kind ?? 'null'}(${preview ? '已写入' : '失败'},随后清掉)`
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const exerciseOnceApi = () => {
|
|
192
|
+
const current = view.value
|
|
193
|
+
if (!current) {
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
let fired = 0
|
|
197
|
+
const stop = current.once('cameraChange', () => { fired += 1 })
|
|
198
|
+
current.zoomBy(1.01)
|
|
199
|
+
current.zoomBy(1 / 1.01)
|
|
200
|
+
stop()
|
|
201
|
+
advancedReadout.value = `once('cameraChange'):两次相机变化只触发 ${fired} 次(订阅在第一次回调后自动摘掉,返回的 stop 可提前取消)`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const exerciseCopyApis = () => {
|
|
205
|
+
const current = view.value
|
|
206
|
+
const source = current?.getData().points.find(item => String(item.id) === 'node-2')
|
|
207
|
+
if (!current || !source) {
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
const started = current.beginCopyPoint({
|
|
211
|
+
id: 'node-2',
|
|
212
|
+
coordinate: [Number(source.x) + 0.2, Number(source.y) + 0.2],
|
|
213
|
+
autoCommit: false,
|
|
214
|
+
via: 'api-demo'
|
|
215
|
+
})
|
|
216
|
+
const updated = current.updateCopyPoint({ name: '接口复制预览' })
|
|
217
|
+
const committed = current.commitPointCopy()
|
|
218
|
+
const copyId = committed?.item?.id
|
|
219
|
+
if (copyId !== undefined && copyId !== null) {
|
|
220
|
+
current.removePoints([copyId])
|
|
221
|
+
}
|
|
222
|
+
advancedReadout.value = `复制收尾:beginCopyPoint=${started ? 'preview' : 'null'} · updateCopyPoint=${updated?.name ?? 'null'} · commitPointCopy=${committed ? 'ok' : 'null'} · 清理 removePoints=${copyId !== undefined && copyId !== null ? 'ok' : 'skipped'}`
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const exerciseRemoveApis = () => {
|
|
226
|
+
const current = view.value
|
|
227
|
+
const anchor = current?.getData().points.find(item => String(item.id) === 'node-3')
|
|
228
|
+
if (!current || !anchor) {
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
current.addPoints({ id: 'api-remove-node', x: Number(anchor.x) + 0.08, y: Number(anchor.y) + 0.08, name: '待删除临时节点' })
|
|
232
|
+
current.selectFeatures('api-remove-node')
|
|
233
|
+
current.requestRemove()
|
|
234
|
+
const pending = current.getRemoveRequest()
|
|
235
|
+
pending?.cancel()
|
|
236
|
+
const canceled = current.getRemoveRequest()
|
|
237
|
+
const removed = current.removeSelected()
|
|
238
|
+
advancedReadout.value = `删除两条路:requestRemove → getRemoveRequest=${pending ? 'pending' : 'null'} → cancel=${canceled ? 'still pending' : 'cleared'} · removeSelected=${removed.ok ? `${removed.pointIds.length} 个节点` : removed.reason ?? 'failed'}`
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const exerciseWebGLApis = () => {
|
|
242
|
+
const current = view.value
|
|
243
|
+
if (!current) {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
current.addWebGLLayer({ id: 'api-temp-layer', order: 999, draw() {} })
|
|
247
|
+
const before = current.getWebGLLayers().length
|
|
248
|
+
const cleared = current.clearWebGLLayers()
|
|
249
|
+
const after = current.getWebGLLayers().length
|
|
250
|
+
advancedReadout.value = `WebGL 层:addWebGLLayer → ${before} 层 · clearWebGLLayers=${cleared} · 剩余 ${after} 层`
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const exerciseRegistryApis = () => {
|
|
254
|
+
const current = view.value
|
|
255
|
+
if (!current) {
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
const disposeSymbol = current.registerSymbol('api-ring', () => {})
|
|
259
|
+
const disposeOperator = current.registerOperator('api-even', value => candidate => Number(candidate) % 2 === Number(value))
|
|
260
|
+
const symbols = current.listSymbols()
|
|
261
|
+
const operators = current.listOperators()
|
|
262
|
+
const removedSymbol = current.unregisterSymbol('api-ring')
|
|
263
|
+
const removedOperator = current.unregisterOperator('api-even')
|
|
264
|
+
disposeSymbol()
|
|
265
|
+
disposeOperator()
|
|
266
|
+
advancedReadout.value = `注册表:registerSymbol → listSymbols=${symbols.join(', ') || '空'} → unregisterSymbol=${removedSymbol} · registerOperator → listOperators 含 api-even=${operators.includes('api-even')} → unregisterOperator=${removedOperator}`
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const readInitialOptions = () => {
|
|
270
|
+
const options = view.value?.getOptions()
|
|
271
|
+
if (!options) {
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
advancedReadout.value = `初始化 options:ariaLabel=${options.ariaLabel ?? '—'} · labelRender=${options.labelRender ?? 'auto'} · gridSize=${options.gridSize ?? '—'} · fitOnInit=${options.fitOnInit} · progressiveInit=${options.progressiveInit} · pixelRatio=${options.pixelRatio ?? 'devicePixelRatio'} · maxCanvasPixels=${options.maxCanvasPixels} · waypointStyle=${options.waypointStyle ? '已配置' : '缺省'} · zoomBaseScale=${options.zoomBaseScale ?? '首次适应'} · webglLayers=${options.webglLayers?.length ?? 0}`
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const cascadeOptions = () => ({
|
|
278
|
+
scope: 'both',
|
|
279
|
+
triggerOnClick: triggerOnClick.value,
|
|
280
|
+
triggerOnMultiSelect: triggerOnMultiSelect.value,
|
|
281
|
+
triggerOnBoxSelect: triggerOnBoxSelect.value,
|
|
282
|
+
pointStyle: { color: '#ecfeff', strokeColor: '#06b6d4', strokeWidth: 2.5, sizeScale: 1.2 },
|
|
283
|
+
lineStyle: { color: '#06b6d4', width: 3, opacity: 0.95 },
|
|
284
|
+
clearOnEmptyClick: true
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
const describeCascade = (payload) => {
|
|
288
|
+
if (!payload) {
|
|
289
|
+
return '级联高亮:已收起(空白处、来源失效或数据刷新)'
|
|
290
|
+
}
|
|
291
|
+
const origin = payload.origin?.target === 'channel'
|
|
292
|
+
? `通道 ${payload.origin.channelId ?? '—'} / 分组 ${payload.origin.groupId ?? '—'}`
|
|
293
|
+
: payload.origin?.target === 'line'
|
|
294
|
+
? `线路 ${payload.origin.item?.id ?? payload.origin.index}`
|
|
295
|
+
: `节点 ${payload.origin?.item?.id ?? payload.origin?.index}`
|
|
296
|
+
return `${origin} → ${payload.pointIndexes.length} 个节点 / ${payload.lineIndexes.length} 条线路(与选中集独立)`
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const refreshSelection = () => {
|
|
300
|
+
const data = view.value?.getSelectionData()
|
|
301
|
+
if (!data?.count) {
|
|
302
|
+
selectionReadout.value = '尚未选中'
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
const parts = []
|
|
306
|
+
if (data.pointCount) parts.push(`节点 ${data.pointCount}`)
|
|
307
|
+
if (data.lineCount) parts.push(`线路 ${data.lineCount}`)
|
|
308
|
+
if (data.channelCount) parts.push(`通道 ${data.channelCount}`)
|
|
309
|
+
selectionReadout.value = `${data.count} 项:${parts.join(' / ')}${data.ids.length ? ` · ids ${data.ids.slice(0, 6).join(', ')}` : ''}`
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const refreshVisibility = () => {
|
|
313
|
+
const current = view.value
|
|
314
|
+
if (!current) {
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
const pointCount = current.listPoints({ visibleOnly: true }).length
|
|
318
|
+
const lineCount = current.listLines({ visibleOnly: true }).length
|
|
319
|
+
visibilityReadout.value = `${pointCount} 个可见节点 / ${lineCount} 条可见线路 · 端点 node-0 ${endpointHidden.value ? '已隐藏' : '可见'} · lineFollowsNode=${lineFollowsNode.value}`
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const scheduleRefreshVisibility = () => {
|
|
323
|
+
window.clearTimeout(timer)
|
|
324
|
+
timer = window.setTimeout(refreshVisibility, 120)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const applyCascadeTriggers = () => {
|
|
328
|
+
view.value?.updateOptions({ cascadeHighlight: cascadeOptions() })
|
|
329
|
+
cascadeReadout.value = `级联触发:单击 ${triggerOnClick.value ? '开' : '关'} · 多选 ${triggerOnMultiSelect.value ? '开' : '关'} · 框选 ${triggerOnBoxSelect.value ? '开' : '关'}`
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const applyLineFollowsNode = () => {
|
|
333
|
+
view.value?.updateOptions({ lineFollowsNode: lineFollowsNode.value })
|
|
334
|
+
scheduleRefreshVisibility()
|
|
335
|
+
emit('notice', `lineFollowsNode 已切换为 ${lineFollowsNode.value}`)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const toggleEndpoint = () => {
|
|
339
|
+
endpointHidden.value = !endpointHidden.value
|
|
340
|
+
view.value?.updatePoint('node-0', { visible: !endpointHidden.value })
|
|
341
|
+
scheduleRefreshVisibility()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const selectNode = () => {
|
|
345
|
+
view.value?.selectFeatures('node-0')
|
|
346
|
+
refreshSelection()
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const selectLine = () => {
|
|
350
|
+
view.value?.selectFeatures({ type: 'line', id: 'line-0' })
|
|
351
|
+
refreshSelection()
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const selectChannel = () => {
|
|
355
|
+
view.value?.selectFeatures({ type: 'channel', channelId })
|
|
356
|
+
refreshSelection()
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const selectGroup = () => {
|
|
360
|
+
if (!groupId) {
|
|
361
|
+
groupId = view.value?.getChannelGroups(channelId)?.[0]?.id ?? ''
|
|
362
|
+
}
|
|
363
|
+
if (groupId !== '') {
|
|
364
|
+
view.value?.selectFeatures({ type: 'group', channelId, groupId })
|
|
365
|
+
}
|
|
366
|
+
refreshSelection()
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const selectWaypoint = () => {
|
|
370
|
+
const current = view.value
|
|
371
|
+
if (!current) {
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
const entry = current.listLines().find(line => String(line.item?.id) === 'line-0')
|
|
375
|
+
if (!entry) {
|
|
376
|
+
selectionReadout.value = '没有找到 line-0'
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
if (!current.getWaypoints(entry.index).length) {
|
|
380
|
+
const data = current.getData()
|
|
381
|
+
const line = data.lines.find(item => String(item.id) === 'line-0')
|
|
382
|
+
const from = data.points.find(point => String(point.id) === String(line?.from))
|
|
383
|
+
const to = data.points.find(point => String(point.id) === String(line?.to))
|
|
384
|
+
if (from && to) {
|
|
385
|
+
current.addWaypoint('line-0', [(Number(from.x) + Number(to.x)) / 2 + 0.16, (Number(from.y) + Number(to.y)) / 2 + 0.16])
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
current.selectWaypoint({ lineIndex: entry.index, pathIndex: 1 })
|
|
389
|
+
refreshSelection()
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const clearSelection = () => {
|
|
393
|
+
view.value?.selectFeatures(null)
|
|
394
|
+
refreshSelection()
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const focusTarget = (target, label) => {
|
|
398
|
+
const current = view.value
|
|
399
|
+
if (!current) {
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
current.focusOn(target, { zoom: true, padding: 64, maxZoom: 20 })
|
|
403
|
+
const camera = current.getCamera()
|
|
404
|
+
focusReadout.value = `${label}:scale ${camera.scale.toPrecision(5)} · 中心 [${camera.center.map(value => Number(value).toFixed(3)).join(', ')}]`
|
|
405
|
+
refreshSelection()
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const focusNode = () => focusTarget({ type: 'point', id: 'node-0' }, 'focusOn(point node-0)')
|
|
409
|
+
const focusLine = () => focusTarget({ type: 'line', id: 'line-0' }, 'focusOn(line line-0)')
|
|
410
|
+
const focusChannel = () => focusTarget({ type: 'channel', channelId }, 'focusOn(channel channel-a)')
|
|
411
|
+
const focusGroup = () => {
|
|
412
|
+
if (!groupId) {
|
|
413
|
+
groupId = view.value?.getChannelGroups(channelId)?.[0]?.id ?? ''
|
|
414
|
+
}
|
|
415
|
+
if (groupId !== '') {
|
|
416
|
+
focusTarget({ type: 'group', channelId, groupId }, 'focusOn(group channel-a 第一组)')
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const readCoordinateRoundTrip = () => {
|
|
421
|
+
const current = view.value
|
|
422
|
+
const point = current?.getData().points.find(item => String(item.id) === 'node-0')
|
|
423
|
+
if (!current || !point) {
|
|
424
|
+
return
|
|
425
|
+
}
|
|
426
|
+
const screen = current.worldToClient(point.x, point.y)
|
|
427
|
+
const world = current.clientToWorld(screen[0], screen[1])
|
|
428
|
+
const lngLat = current.worldToLngLat(point.x, point.y)
|
|
429
|
+
const back = current.lngLatToWorld(lngLat[0], lngLat[1])
|
|
430
|
+
apiReadout.value = `worldToClient → [${screen.map(value => value.toFixed(2)).join(', ')}] · clientToWorld → [${world.map(value => value.toFixed(4)).join(', ')}] · lngLat → [${lngLat.map(value => value.toFixed(5)).join(', ')}] · lngLatToWorld → [${back.map(value => value.toFixed(4)).join(', ')}]`
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const readViewportAndRenderer = () => {
|
|
434
|
+
const current = view.value
|
|
435
|
+
if (!current) {
|
|
436
|
+
return
|
|
437
|
+
}
|
|
438
|
+
const viewport = current.getViewport()
|
|
439
|
+
const renderer = current.getRendererInfo()
|
|
440
|
+
apiReadout.value = `getViewport ${viewport.cssWidth}×${viewport.cssHeight} CSS / ${viewport.width}×${viewport.height} 像素 · getRendererInfo keys=[${Object.keys(renderer).slice(0, 8).join(', ')}]`
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const readSceneSnapshot = () => {
|
|
444
|
+
const snapshot = view.value?.snapshotScene({ background: false })
|
|
445
|
+
apiReadout.value = snapshot
|
|
446
|
+
? `snapshotScene:${snapshot.points?.length ?? 0} 个节点 / ${snapshot.lines?.length ?? 0} 条线路 / ${snapshot.labels?.length ?? 0} 个标签 / ${snapshot.decorations?.length ?? 0} 个装饰`
|
|
447
|
+
: 'snapshotScene 没有返回'
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const readMiscellaneous = () => {
|
|
451
|
+
const current = view.value
|
|
452
|
+
if (!current) {
|
|
453
|
+
return
|
|
454
|
+
}
|
|
455
|
+
const filter = current.getFilter()
|
|
456
|
+
const parallelGroups = current.getParallelGroups()
|
|
457
|
+
const hover = current.getHover()
|
|
458
|
+
apiReadout.value = `getFilter=${filter ? '有' : 'null'} · getParallelGroups=${parallelGroups?.length ?? 0} 组 · getHover=${hover?.target ?? 'null'}`
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const waitForRendered = async () => {
|
|
462
|
+
const current = view.value
|
|
463
|
+
if (!current) {
|
|
464
|
+
return
|
|
465
|
+
}
|
|
466
|
+
const started = performance.now()
|
|
467
|
+
await current.whenDataRendered()
|
|
468
|
+
apiReadout.value = `whenDataRendered 已兑现 · ${(performance.now() - started).toFixed(1)} ms`
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const countPointsInView = () => {
|
|
472
|
+
let count = 0
|
|
473
|
+
view.value?.forEachPointInView(() => { count += 1 })
|
|
474
|
+
apiReadout.value = `forEachPointInView:${count} 个节点落在当前视口`
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
onMounted(async () => {
|
|
478
|
+
view.value = createGisView(container.value, {
|
|
479
|
+
points: dataset.points,
|
|
480
|
+
lines: dataset.lines,
|
|
481
|
+
channels: dataset.channels,
|
|
482
|
+
features: {
|
|
483
|
+
pan: true,
|
|
484
|
+
zoom: true,
|
|
485
|
+
hover: true,
|
|
486
|
+
select: true,
|
|
487
|
+
boxSelect: true,
|
|
488
|
+
drag: true,
|
|
489
|
+
waypoint: true,
|
|
490
|
+
contextMenu: true,
|
|
491
|
+
keyboard: true
|
|
492
|
+
},
|
|
493
|
+
boxSelect: true,
|
|
494
|
+
multiSelect: true,
|
|
495
|
+
copy: { enabled: true, autoCommit: false },
|
|
496
|
+
history: { limit: 30 },
|
|
497
|
+
labelRender: 'dom',
|
|
498
|
+
gridSize: 96,
|
|
499
|
+
fitOnInit: true,
|
|
500
|
+
progressiveInit: true,
|
|
501
|
+
pixelRatio: Math.min(window.devicePixelRatio || 1, 2),
|
|
502
|
+
maxCanvasPixels: 4_000_000,
|
|
503
|
+
ariaLabel: '接口能力演示画布:支持选中、框选、聚焦与只读接口读数',
|
|
504
|
+
waypointStyle: { size: 8, color: '#fff', strokeColor: '#38bdf8', strokeWidth: 1.5 },
|
|
505
|
+
zoomBaseScale: 1,
|
|
506
|
+
webglLayers: [],
|
|
507
|
+
clickable: true,
|
|
508
|
+
draggable: true,
|
|
509
|
+
waypointEditable: true,
|
|
510
|
+
waypointVisibility: 'always',
|
|
511
|
+
lineFollowsNode: true,
|
|
512
|
+
channelWidth: 2.6,
|
|
513
|
+
pointStyle: { size: 6, color: '#fff', strokeColor: '#4c8dff', strokeWidth: 1.4 },
|
|
514
|
+
lineStyle: { width: 1.5, color: 'rgba(100, 150, 220, 0.55)' },
|
|
515
|
+
fitPadding: 48,
|
|
516
|
+
cascadeHighlight: cascadeOptions(),
|
|
517
|
+
onReady: () => emit('notice', '接口能力演示已就绪'),
|
|
518
|
+
onSelect: (payload) => {
|
|
519
|
+
refreshSelection()
|
|
520
|
+
if (payload) {
|
|
521
|
+
selectionReadout.value += ` · cause=${payload.cause ?? '—'} / mode=${payload.mode ?? '—'}`
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
onCascadeHighlight: (payload) => {
|
|
525
|
+
cascadeReadout.value = describeCascade(payload)
|
|
526
|
+
}
|
|
527
|
+
})
|
|
528
|
+
groupId = String(view.value.getChannelGroups(channelId)?.[0]?.id ?? '')
|
|
529
|
+
await nextTick()
|
|
530
|
+
view.value.fitView(48)
|
|
531
|
+
refreshVisibility()
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
onBeforeUnmount(() => {
|
|
535
|
+
window.clearTimeout(timer)
|
|
536
|
+
view.value?.destroy()
|
|
537
|
+
view.value = null
|
|
538
|
+
})
|
|
539
|
+
</script>
|
|
540
|
+
|
|
541
|
+
<template>
|
|
542
|
+
<section class="dv-demo">
|
|
543
|
+
<div class="dv-stage">
|
|
544
|
+
<div ref="container" class="dv-canvas"></div>
|
|
545
|
+
<div class="dv-toolbar">
|
|
546
|
+
<button type="button" class="dv-button" @click="view?.fitView(48)">适应全部</button>
|
|
547
|
+
<button type="button" class="dv-button" @click="clearSelection">清空选中</button>
|
|
548
|
+
</div>
|
|
549
|
+
</div>
|
|
550
|
+
<aside class="dv-panel">
|
|
551
|
+
<div class="dv-group">
|
|
552
|
+
<h3>级联高亮三种触发</h3>
|
|
553
|
+
<div class="dv-row">
|
|
554
|
+
<label><input v-model="triggerOnClick" data-demo="cascade-trigger-click" type="checkbox" @change="applyCascadeTriggers" /> triggerOnClick</label>
|
|
555
|
+
<label><input v-model="triggerOnMultiSelect" data-demo="cascade-trigger-multi" type="checkbox" @change="applyCascadeTriggers" /> triggerOnMultiSelect</label>
|
|
556
|
+
<label><input v-model="triggerOnBoxSelect" data-demo="cascade-trigger-box" type="checkbox" @change="applyCascadeTriggers" /> triggerOnBoxSelect</label>
|
|
557
|
+
</div>
|
|
558
|
+
<p class="dv-readout">{{ cascadeReadout }}</p>
|
|
559
|
+
<p class="dv-hint">单击、多选点击和框选各走各的开关。开着框选触发后,画布上 Shift + 拖一个框,级联会把框内邻接节点与线路一起亮起;关掉时只改选中集。</p>
|
|
560
|
+
</div>
|
|
561
|
+
|
|
562
|
+
<div class="dv-group">
|
|
563
|
+
<h3>程序化选中</h3>
|
|
564
|
+
<div class="dv-row">
|
|
565
|
+
<button type="button" class="dv-button" @click="selectNode">选中节点</button>
|
|
566
|
+
<button type="button" class="dv-button" @click="selectLine">选中线路</button>
|
|
567
|
+
<button type="button" class="dv-button" data-demo="select-channel" @click="selectChannel">选中通道</button>
|
|
568
|
+
<button type="button" class="dv-button" data-demo="select-group" @click="selectGroup">选中分组</button>
|
|
569
|
+
<button type="button" class="dv-button" data-demo="select-waypoint" @click="selectWaypoint">选中拐点</button>
|
|
570
|
+
</div>
|
|
571
|
+
<p class="dv-readout">{{ selectionReadout }}</p>
|
|
572
|
+
<p class="dv-hint">选中拐点会补一个中间点,并把所在线路一起选上;选中通道 / 分组的读数来自 getSelectionData()。</p>
|
|
573
|
+
</div>
|
|
574
|
+
|
|
575
|
+
<div class="dv-group">
|
|
576
|
+
<h3>focusOn 显式类型</h3>
|
|
577
|
+
<div class="dv-row">
|
|
578
|
+
<button type="button" class="dv-button" @click="focusNode">point</button>
|
|
579
|
+
<button type="button" class="dv-button" @click="focusLine">line</button>
|
|
580
|
+
<button type="button" class="dv-button" data-demo="focus-channel" @click="focusChannel">channel</button>
|
|
581
|
+
<button type="button" class="dv-button" @click="focusGroup">group</button>
|
|
582
|
+
</div>
|
|
583
|
+
<p class="dv-readout">{{ focusReadout }}</p>
|
|
584
|
+
<p class="dv-hint">四种类型都走同一套包围盒适配;这里的 maxZoom 只是示例参数,组件不会额外更改放大倍率。</p>
|
|
585
|
+
</div>
|
|
586
|
+
|
|
587
|
+
<div class="dv-group">
|
|
588
|
+
<h3>线路是否跟随节点</h3>
|
|
589
|
+
<div class="dv-row">
|
|
590
|
+
<label><input v-model="lineFollowsNode" data-demo="line-follows-node" type="checkbox" @change="applyLineFollowsNode" /> lineFollowsNode</label>
|
|
591
|
+
<button type="button" class="dv-button" data-demo="toggle-endpoint" @click="toggleEndpoint">{{ endpointHidden ? '显示端点' : '隐藏端点' }}</button>
|
|
592
|
+
</div>
|
|
593
|
+
<p class="dv-readout" data-demo="visibility-readout">{{ visibilityReadout }}</p>
|
|
594
|
+
<p class="dv-hint">先隐藏 node-0 看对比,再勾掉 lineFollowsNode:开启时绑定线路一起退场;关闭后只收起节点,线路和通道停在最后坐标。</p>
|
|
595
|
+
</div>
|
|
596
|
+
|
|
597
|
+
<div class="dv-group">
|
|
598
|
+
<h3>只读接口</h3>
|
|
599
|
+
<div class="dv-row">
|
|
600
|
+
<button type="button" class="dv-button" @click="readCoordinateRoundTrip">坐标互转</button>
|
|
601
|
+
<button type="button" class="dv-button" @click="readViewportAndRenderer">视口 / 渲染器</button>
|
|
602
|
+
<button type="button" class="dv-button" @click="readSceneSnapshot">场景快照</button>
|
|
603
|
+
<button type="button" class="dv-button" @click="readMiscellaneous">筛选 / 分组 / 悬停</button>
|
|
604
|
+
<button type="button" class="dv-button" @click="waitForRendered">下一帧 Promise</button>
|
|
605
|
+
<button type="button" class="dv-button" @click="countPointsInView">视口内节点</button>
|
|
606
|
+
</div>
|
|
607
|
+
<p class="dv-readout" data-demo="api-readout">{{ apiReadout }}</p>
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
<div class="dv-group">
|
|
611
|
+
<h3>样式、装饰与通道写入</h3>
|
|
612
|
+
<div class="dv-row">
|
|
613
|
+
<button type="button" class="dv-button" data-demo="style-apis" @click="toggleStyleApis">{{ styleApplied ? '恢复字段级样式' : 'updatePointStyle / updateLineStyle' }}</button>
|
|
614
|
+
<button type="button" class="dv-button" data-demo="point-decoration-apis" @click="exercisePointDecorationApis">节点装饰 CRUD</button>
|
|
615
|
+
<button type="button" class="dv-button" data-demo="line-decoration-apis" @click="exerciseLineDecorationApis">线路装饰 CRUD</button>
|
|
616
|
+
<button type="button" class="dv-button" data-demo="channel-line-apis" @click="exerciseChannelLineApis">通道线宽 / 线路对账</button>
|
|
617
|
+
<button type="button" class="dv-button" data-demo="visibility-apis" @click="exerciseVisibilityApis">setFeatureVisible / isPointVisible</button>
|
|
618
|
+
</div>
|
|
619
|
+
<p class="dv-readout" data-demo="advanced-readout">{{ advancedReadout }}</p>
|
|
620
|
+
</div>
|
|
621
|
+
|
|
622
|
+
<div class="dv-group">
|
|
623
|
+
<h3>背景、投影与初始化 options</h3>
|
|
624
|
+
<div class="dv-row">
|
|
625
|
+
<button type="button" class="dv-button" data-demo="geo-background-apis" @click="toggleGeoBackgroundApis">{{ geoBackgroundOn ? '移除 GeoJSON 背景' : 'updateBackground / GeoJSON 投影' }}</button>
|
|
626
|
+
<button type="button" class="dv-button" data-demo="initial-options" @click="readInitialOptions">读取初始化 options</button>
|
|
627
|
+
</div>
|
|
628
|
+
<p class="dv-hint">GeoJSON 按钮会临时铺一块包住数据的区域:可读 getBackgroundLabels / getGeoProjection / mapDataToGeoJson,再点一次恢复透明底。初始化 options 在创建时生效,运行时按键读取当前值。</p>
|
|
629
|
+
</div>
|
|
630
|
+
|
|
631
|
+
<div class="dv-group">
|
|
632
|
+
<h3>历史、复制与运行时清理</h3>
|
|
633
|
+
<div class="dv-row">
|
|
634
|
+
<button type="button" class="dv-button" data-demo="history-apis" @click="exerciseHistoryApis">历史 / 收尾 / 落点预览</button>
|
|
635
|
+
<button type="button" class="dv-button" data-demo="once-api" @click="exerciseOnceApi">once 事件</button>
|
|
636
|
+
<button type="button" class="dv-button" data-demo="copy-apis" @click="exerciseCopyApis">复制预览收尾</button>
|
|
637
|
+
<button type="button" class="dv-button" data-demo="remove-apis" @click="exerciseRemoveApis">删除请求 / removeSelected</button>
|
|
638
|
+
<button type="button" class="dv-button" data-demo="webgl-apis" @click="exerciseWebGLApis">WebGL 层清理</button>
|
|
639
|
+
<button type="button" class="dv-button" data-demo="registry-apis" @click="exerciseRegistryApis">图元 / 运算符注销</button>
|
|
640
|
+
</div>
|
|
641
|
+
</div>
|
|
642
|
+
</aside>
|
|
643
|
+
</section>
|
|
644
|
+
</template>
|