gis-view-webgl2 0.1.0 → 0.1.2
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 +40 -8
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1036 -249
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +194 -0
- package/docs/GisView.md +9 -8
- package/docs/GisView/345/256/236/347/216/260/345/216/237/347/220/206.md +2 -2
- 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 +172 -0
- package/docs/api/index.html +545 -59
- package/docs/demos/BackendSyncDemo.vue +10 -8
- package/docs/demos/BasicDemo.vue +101 -101
- package/docs/demos/BatchAddDemo.vue +5 -5
- package/docs/demos/ChannelDemo.vue +617 -10
- package/docs/demos/ConnectDemo.vue +392 -0
- package/docs/demos/CopyMergeDemo.vue +980 -929
- package/docs/demos/DecorationDraftDemo.vue +1300 -0
- package/docs/demos/DemoShell.vue +569 -240
- package/docs/demos/DragDropDemo.vue +11 -11
- package/docs/demos/EffectsDemo.vue +6 -6
- package/docs/demos/EventDemo.vue +327 -327
- package/docs/demos/ExtensionDemo.vue +458 -0
- package/docs/demos/GisViewDemo.vue +61 -48
- package/docs/demos/GraphicDemo.vue +338 -0
- package/docs/demos/LabelDemo.vue +553 -497
- package/docs/demos/MinimapDemo.vue +358 -0
- package/docs/demos/PerfDemo.vue +29 -0
- package/docs/demos/SearchFilterDemo.vue +412 -0
- package/docs/demos/StyleDemo.vue +121 -121
- package/docs/demos/api-index.js +98 -77
- package/docs/demos/demos.js +159 -135
- package/docs/demos/shared/demoData.js +107 -107
- package/docs/demos/shared/demoStyle.css +249 -249
- package/docs/demos/shared/fakeBackend.js +93 -6
- package/docs/format/03-/346/213/220/347/202/271/350/243/205/351/245/260/344/270/216/346/240/267/345/274/217.md +28 -8
- package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +5 -4
- package/docs/format/06-/345/257/274/345/205/245/345/257/274/345/207/272/344/270/216/346/240/207/345/207/206/346/226/207/346/241/243.md +8 -6
- 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 +6 -6
- package/docs/guide/sample-document.html +4 -4
- package/docs/index.html +180 -9
- package/docs/internals/11-/350/246/206/347/233/226/345/261/202/346/240/207/347/255/276/344/270/216/346/217/220/347/244/272.md +1 -1
- package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +3 -2
- package/docs/internals/15-03-/351/200/220/350/275/256/350/256/260/345/275/225-/346/200/247/350/203/275/344/270/216/344/277/256/345/244/215-/344/270/213.md +16 -0
- package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +11 -0
- package/docs/internals/17-/350/260/203/350/257/225/344/270/216/346/211/251/345/261/225/345/273/272/350/256/256.md +1 -1
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +18 -4
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +110 -25
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +13 -8
- package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +111 -10
- package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +47 -9
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +17 -15
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +61 -11
- 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 +74 -7
- package/docs/manual/11-/346/240/267/345/274/217/344/270/216/350/247/204/345/210/231.md +3 -2
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +260 -3
- package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +6 -5
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +27 -14
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +142 -49
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +31 -29
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +55 -39
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +189 -64
- 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 +4 -4
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +42 -2
- package/docs/manual/21-/350/257/264/346/230/216/344/270/216/345/270/270/350/247/201/351/227/256/351/242/230.md +22 -6
- package/docs/playground/index.html +5 -5
- package/docs/steps/README.md +14 -10
- package/docs/steps/check-doc-truth.mjs +1 -1
- package/docs/steps/selftest-actions.html +10 -10
- package/docs/steps/selftest-data.html +96 -14
- package/docs/steps/selftest-decoration.html +577 -0
- package/docs/steps/selftest-demo.html +6 -5
- package/docs/steps/selftest-demos.html +2201 -1612
- package/docs/steps/selftest-docs.html +89 -16
- package/docs/steps/selftest-effects.html +5 -5
- package/docs/steps/selftest-features.html +757 -86
- package/docs/steps/selftest-full.html +583 -583
- package/docs/steps/selftest-labels.html +1034 -652
- package/docs/steps/selftest-perf.html +1899 -1844
- package/docs/steps/selftest-scale.html +117 -7
- package/docs/steps/selftest-select.html +24 -17
- package/docs/steps/step-10-full.js +1 -1
- package/docs//346/216/245/345/217/243/345/215/217/350/256/256.md +167 -17
- package/docs//346/225/260/346/215/256/346/240/274/345/274/217.md +1 -1
- package/package.json +43 -43
- package/docs/demos/HistoryDemo.vue +0 -230
- package/docs/demos/InterfaceDemo.vue +0 -309
- package/docs/demos/SampleDemo.vue +0 -283
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/**
|
|
3
|
+
* 可视化连线:只靠 points + lines 也能独立成图,再把「新增一条线」的几种常见配置摆在一起。
|
|
4
|
+
*
|
|
5
|
+
* 这一页的初始数据明确是 channels: [] —— 没有任何通道,节点与线路照样能渲染、选中与新增。
|
|
6
|
+
* 「开始连线」走 beginAddChannel(kind 为 line 或 channel),两个节点选完先出草稿:
|
|
7
|
+
* - routing: 'orthogonal' 让预览自动补一个先横后纵的 90° 拐点;
|
|
8
|
+
* - channelDraftStyle 直接交给 beginAddChannel,草稿期间用 updateAddChannelStyle 原地改箭头位置 / 间距 / 配色;
|
|
9
|
+
* - 取消只收草稿,数据一个字节不动;
|
|
10
|
+
* - 选好起点后点节点以外的位置也会直接取消,事件里的 reason 是 click-outside-node;
|
|
11
|
+
* - 默认添加走 commitAddChannel;
|
|
12
|
+
* - 另外两颗按钮绕开提交,用 addLines / addChannels 把同一份草稿真正落地。
|
|
13
|
+
*
|
|
14
|
+
* 预览与落地共用 resolveRoutedPath:预览上看到的折线、箭头与提交后的 path / direction 对得上。
|
|
15
|
+
*/
|
|
16
|
+
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
17
|
+
import { createGisView } from '../../src/index.js'
|
|
18
|
+
import { buildDemoData } from './shared/demoData.js'
|
|
19
|
+
import './shared/demoStyle.css'
|
|
20
|
+
|
|
21
|
+
const emit = defineEmits(['notice'])
|
|
22
|
+
const container = ref(null)
|
|
23
|
+
const linkKind = ref('line')
|
|
24
|
+
const routing = ref('orthogonal')
|
|
25
|
+
const lineColor = ref('#2f6fd0')
|
|
26
|
+
const lineWidth = ref(2.4)
|
|
27
|
+
const arrow = ref(true)
|
|
28
|
+
const arrowSize = ref(12)
|
|
29
|
+
const arrowPositionsText = ref('0.5')
|
|
30
|
+
const circuitSpacing = ref(12)
|
|
31
|
+
const draft = ref(null)
|
|
32
|
+
const actionText = ref('配置好线条之后点「开始连线」,依次点两个节点')
|
|
33
|
+
const dataText = ref('正在创建视图…')
|
|
34
|
+
const cancelText = ref('尚未取消:选好起点后点一下节点以外的空白处,应收到 reason = click-outside-node')
|
|
35
|
+
const logLines = ref([])
|
|
36
|
+
const logText = computed(() => logLines.value.length
|
|
37
|
+
? logLines.value.map(line => `${line.name}: ${line.text}`).join(' | ')
|
|
38
|
+
: '还没有操作')
|
|
39
|
+
let view = null
|
|
40
|
+
let sequence = 0
|
|
41
|
+
let logSeq = 0
|
|
42
|
+
|
|
43
|
+
const source = buildDemoData({ count: 12, channels: 0 })
|
|
44
|
+
const points = source.points
|
|
45
|
+
|
|
46
|
+
/** 初始线路只有普通 lines,不挂任何通道;其中一条故意折一道弯,另一条带方向箭头 */
|
|
47
|
+
const createInitialLines = () => {
|
|
48
|
+
const bend = [
|
|
49
|
+
Number(((points[6].x + points[9].x) / 2).toFixed(5)),
|
|
50
|
+
Number(((points[6].y + points[9].y) / 2 - 0.42).toFixed(5))
|
|
51
|
+
]
|
|
52
|
+
const seed = (index, from, to, extra = {}) => ({
|
|
53
|
+
id: `connect-seed-${index}`,
|
|
54
|
+
name: `示例连线 ${index + 1}`,
|
|
55
|
+
from: points[from].id,
|
|
56
|
+
to: points[to].id,
|
|
57
|
+
...extra
|
|
58
|
+
})
|
|
59
|
+
return [
|
|
60
|
+
seed(0, 0, 1, { color: '#4f8fdf', width: 2, direction: { count: 1, size: 11 } }),
|
|
61
|
+
seed(1, 2, 5, { color: '#4f8fdf', width: 1.8, direction: { count: 1, size: 10 } }),
|
|
62
|
+
seed(2, 6, 9, {
|
|
63
|
+
color: '#13a17c',
|
|
64
|
+
width: 2.4,
|
|
65
|
+
path: [[points[6].x, points[6].y], bend, [points[9].x, points[9].y]],
|
|
66
|
+
direction: { count: 1, size: 12 }
|
|
67
|
+
}),
|
|
68
|
+
seed(3, 8, 11, { color: '#d0893b', width: 1.8, lineType: 'dashed', direction: { count: 1, size: 10 } })
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const isChannel = computed(() => linkKind.value !== 'line')
|
|
73
|
+
const circuits = computed(() => linkKind.value === 'channel-3' ? 3 : linkKind.value === 'channel-2' ? 2 : 1)
|
|
74
|
+
const arrowPositions = computed(() => arrowPositionsText.value.split(',').map(value => Number(value.trim())).filter(value => Number.isFinite(value)))
|
|
75
|
+
const isDraftLive = computed(() => Boolean(draft.value) && ['start', 'from', 'ready', 'restart', 'pick'].includes(draft.value.phase))
|
|
76
|
+
const canFinish = computed(() => draft.value?.phase === 'ready')
|
|
77
|
+
const draftText = computed(() => {
|
|
78
|
+
const current = draft.value
|
|
79
|
+
if (!current || !['start', 'from', 'ready', 'restart', 'pick'].includes(current.phase)) {
|
|
80
|
+
return '草稿未开始(或已取消 / 已落地)'
|
|
81
|
+
}
|
|
82
|
+
const kind = current.kind === 'line' ? '独立线路' : `通道 · ${current.circuits} 回`
|
|
83
|
+
if (!current.from) {
|
|
84
|
+
return `${kind} · ${current.routing === 'orthogonal' ? '正交折线' : '直线'} · 等待点起点`
|
|
85
|
+
}
|
|
86
|
+
const from = current.from.item?.name ?? current.from.id
|
|
87
|
+
if (!current.to) {
|
|
88
|
+
return `${kind} · 起点 ${from} · 等待点终点`
|
|
89
|
+
}
|
|
90
|
+
const to = current.to.item?.name ?? current.to.id
|
|
91
|
+
const bends = current.path?.length ?? 0
|
|
92
|
+
return `${kind} · ${from} → ${to} · ${current.routing === 'orthogonal' ? '正交折线' : '直线'} · 中间拐点 ${bends} · 箭头 ${current.style?.arrow ? current.style.arrowPositions.map(value => `${Math.round(value * 100)}%`).join('/') : '关'} · 回线间距 ${current.style?.circuitSpacing ?? 0}px`
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const pushLog = (name, text) => {
|
|
96
|
+
logSeq += 1
|
|
97
|
+
logLines.value = [{ id: logSeq, name, text }, ...logLines.value].slice(0, 16)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const refreshState = () => {
|
|
101
|
+
if (!view) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
const data = view.getData()
|
|
105
|
+
const channels = view.getOptions().channels ?? data.channels ?? []
|
|
106
|
+
dataText.value = `${data.points.length} 节点 / ${data.lines.length} 线路 / ${channels.length} 通道${channels.length ? '' : '(纯点 + 线模式)'}`
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const currentDraftStyle = () => ({
|
|
110
|
+
color: lineColor.value,
|
|
111
|
+
width: lineWidth.value,
|
|
112
|
+
dot: 4,
|
|
113
|
+
arrow: arrow.value,
|
|
114
|
+
arrowSize: arrowSize.value,
|
|
115
|
+
arrowPositions: arrowPositions.value,
|
|
116
|
+
circuitSpacing: circuitSpacing.value
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
/** 预览与真实落地共用同一份线条字段:颜色 / 线宽 / 方向箭头都从这里出发 */
|
|
120
|
+
const currentLineProps = () => ({
|
|
121
|
+
color: lineColor.value,
|
|
122
|
+
width: lineWidth.value,
|
|
123
|
+
direction: arrow.value ? { count: 1, size: arrowSize.value } : false
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const startDraft = () => {
|
|
127
|
+
if (!view) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
view.endAddChannel()
|
|
131
|
+
view.beginAddChannel({
|
|
132
|
+
kind: isChannel.value ? 'channel' : 'line',
|
|
133
|
+
circuits: circuits.value,
|
|
134
|
+
routing: routing.value,
|
|
135
|
+
select: true,
|
|
136
|
+
channelDraftStyle: currentDraftStyle(),
|
|
137
|
+
lineProps: currentLineProps()
|
|
138
|
+
})
|
|
139
|
+
actionText.value = `开始连线:先点起点,再点终点(${isChannel.value ? `通道 ${circuits.value} 回` : '独立线路'} / ${routing.value === 'orthogonal' ? '正交折线' : '直线'})`
|
|
140
|
+
pushLog('beginAddChannel', actionText.value)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const cancelDraft = () => {
|
|
144
|
+
if (!view) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
const before = view.getData()
|
|
148
|
+
const beforeChannels = view.getOptions().channels ?? before.channels ?? []
|
|
149
|
+
const beforeCount = `${before.lines.length} 线路 / ${beforeChannels.length} 通道`
|
|
150
|
+
view.cancelAddChannel()
|
|
151
|
+
draft.value = null
|
|
152
|
+
refreshState()
|
|
153
|
+
const after = view.getData()
|
|
154
|
+
const afterChannels = view.getOptions().channels ?? after.channels ?? []
|
|
155
|
+
const afterCount = `${after.lines.length} 线路 / ${afterChannels.length} 通道`
|
|
156
|
+
actionText.value = `cancelAddChannel:草稿已收起,数据${beforeCount === afterCount ? '未变' : '发生了变化'}(${afterCount})`
|
|
157
|
+
pushLog('cancelAddChannel', actionText.value)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const commitDraft = () => {
|
|
161
|
+
if (!view || !canFinish.value) {
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
const result = view.commitAddChannel()
|
|
165
|
+
refreshState()
|
|
166
|
+
if (!result.ok) {
|
|
167
|
+
actionText.value = `commitAddChannel 没有落地:${result.reason ?? '未知原因'}`
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
const lines = result.lines?.length ?? (result.line ? 1 : 0)
|
|
171
|
+
actionText.value = `commitAddChannel → ${result.kind === 'line' ? '独立线路' : '通道'} ${String(result.channelId ?? result.lineId ?? '')} · ${lines} 回线`
|
|
172
|
+
if (result.lineIds?.length) {
|
|
173
|
+
view.selectFeatures(result.lineIds)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
pushLog('commitAddChannel', actionText.value)
|
|
177
|
+
emit('notice', actionText.value)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const middlePathOf = current => (current.path?.length ? current.path.map(point => [point[0], point[1]]) : null)
|
|
181
|
+
const fullPathOf = current => {
|
|
182
|
+
const middle = middlePathOf(current)
|
|
183
|
+
return middle ? [current.from.coordinate, ...middle, current.to.coordinate] : null
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const addWithLines = () => {
|
|
187
|
+
if (!view || !canFinish.value) {
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
const current = view.getAddChannelDraft()
|
|
191
|
+
if (!current?.from || !current?.to) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
sequence += 1
|
|
195
|
+
const fullPath = fullPathOf(current)
|
|
196
|
+
const result = view.addLines([{
|
|
197
|
+
id: `connect-line-${sequence}`,
|
|
198
|
+
name: `可视化线路 ${sequence}`,
|
|
199
|
+
from: current.from.id,
|
|
200
|
+
to: current.to.id,
|
|
201
|
+
...(fullPath ? { path: fullPath } : {}),
|
|
202
|
+
...currentLineProps()
|
|
203
|
+
}])
|
|
204
|
+
if (result.ok) {
|
|
205
|
+
view.endAddChannel()
|
|
206
|
+
draft.value = null
|
|
207
|
+
view.selectFeatures(result.items.map(item => item.id))
|
|
208
|
+
actionText.value = `addLines → ${result.items.map(item => item.id).join(' / ')}${fullPath ? ` · path ${fullPath.length} 点` : ' · 直线'}`
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
actionText.value = `addLines 没有落地:${result.reason ?? '未知原因'}`
|
|
212
|
+
}
|
|
213
|
+
refreshState()
|
|
214
|
+
pushLog('addLines', actionText.value)
|
|
215
|
+
emit('notice', actionText.value)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const addWithChannels = () => {
|
|
219
|
+
if (!view || !canFinish.value) {
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
const current = view.getAddChannelDraft()
|
|
223
|
+
if (!current?.from || !current?.to) {
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
sequence += 1
|
|
227
|
+
const middle = middlePathOf(current)
|
|
228
|
+
const kind = current.kind === 'line' ? 'line' : 'channel'
|
|
229
|
+
const result = view.addChannels([{
|
|
230
|
+
id: `connect-${kind}-${sequence}`,
|
|
231
|
+
name: `${kind === 'line' ? '独立线路' : '可视化通道'} ${sequence}`,
|
|
232
|
+
from: current.from.id,
|
|
233
|
+
to: current.to.id,
|
|
234
|
+
kind,
|
|
235
|
+
...(kind === 'channel' ? { circuits: current.circuits } : {}),
|
|
236
|
+
...(middle ? { path: middle } : {}),
|
|
237
|
+
lineProps: currentLineProps()
|
|
238
|
+
}])
|
|
239
|
+
refreshState()
|
|
240
|
+
if (result.ok) {
|
|
241
|
+
view.endAddChannel()
|
|
242
|
+
draft.value = null
|
|
243
|
+
if (result.lineIds.length) {
|
|
244
|
+
view.selectFeatures(result.lineIds)
|
|
245
|
+
}
|
|
246
|
+
actionText.value = `addChannels → ${result.count} 条 · ${result.lineIds.length} 回线${result.channelIds.length ? ` · 通道 ${result.channelIds.join(' / ')}` : ' · 未新建通道'}${middle ? ` · path ${middle.length} 拐点` : ''}`
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
actionText.value = `addChannels 没有落地:${result.reason ?? result.results?.[0]?.reason ?? '未知原因'}`
|
|
250
|
+
}
|
|
251
|
+
pushLog('addChannels', actionText.value)
|
|
252
|
+
emit('notice', actionText.value)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const resetScene = () => {
|
|
256
|
+
if (!view) {
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
view.endAddChannel()
|
|
260
|
+
view.updateData({ points, lines: createInitialLines(), channels: [] })
|
|
261
|
+
view.clearHistory()
|
|
262
|
+
view.fitView(56)
|
|
263
|
+
draft.value = null
|
|
264
|
+
sequence = 0
|
|
265
|
+
actionText.value = '已恢复为 channels: [] 的纯点 + 线场景'
|
|
266
|
+
refreshState()
|
|
267
|
+
pushLog('reset', actionText.value)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
watch([lineColor, lineWidth, arrow, arrowSize, arrowPositionsText, circuitSpacing], () => {
|
|
271
|
+
if (view && isDraftLive.value) {
|
|
272
|
+
view.updateAddChannelStyle(currentDraftStyle())
|
|
273
|
+
draft.value = view.getAddChannelDraft()
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
onMounted(() => {
|
|
278
|
+
view = createGisView(container.value, {
|
|
279
|
+
points,
|
|
280
|
+
lines: createInitialLines(),
|
|
281
|
+
channels: [],
|
|
282
|
+
features: { pan: true, zoom: true, channelDraft: true, select: true, hover: true },
|
|
283
|
+
pointStyle: { size: 9, color: '#ffffff', strokeColor: '#3678bf', strokeWidth: 2 },
|
|
284
|
+
lineStyle: { color: 'rgba(86, 122, 170, 0.45)', width: 1.4 },
|
|
285
|
+
fitPadding: 56,
|
|
286
|
+
onReady: () => {
|
|
287
|
+
refreshState()
|
|
288
|
+
emit('notice', '可视化连线:初始 channels 为空,节点与线路仍可独立使用')
|
|
289
|
+
},
|
|
290
|
+
onChannelDraft: payload => {
|
|
291
|
+
draft.value = payload
|
|
292
|
+
if (payload.phase === 'cancel') {
|
|
293
|
+
cancelText.value = `取消:phase = ${payload.phase} · reason = ${payload.reason ?? '未提供'} · 起点 ${payload.from?.item?.name ?? payload.from?.id ?? '—'}`
|
|
294
|
+
actionText.value = payload.reason === 'click-outside-node'
|
|
295
|
+
? '选好起点后点了空白:草稿已取消,数据没有变化;可以重新开始连线'
|
|
296
|
+
: `草稿已取消:${payload.reason ?? '由 cancelAddChannel / Esc 触发'}`
|
|
297
|
+
pushLog('onChannelDraft(cancel)', cancelText.value)
|
|
298
|
+
emit('notice', actionText.value)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
onBeforeUnmount(() => {
|
|
305
|
+
view?.destroy()
|
|
306
|
+
view = null
|
|
307
|
+
})
|
|
308
|
+
</script>
|
|
309
|
+
|
|
310
|
+
<template>
|
|
311
|
+
<section class="dv-demo">
|
|
312
|
+
<div class="dv-stage">
|
|
313
|
+
<div ref="container" class="dv-canvas"></div>
|
|
314
|
+
<div class="dv-toolbar">
|
|
315
|
+
<button type="button" @click="view?.fitView(56)">适应全部</button>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
<aside class="dv-panel">
|
|
319
|
+
<div class="dv-group">
|
|
320
|
+
<h3>连线配置</h3>
|
|
321
|
+
<div class="dv-row">
|
|
322
|
+
<label>类型
|
|
323
|
+
<select v-model="linkKind" data-demo="connect-kind" :disabled="isDraftLive">
|
|
324
|
+
<option value="line">独立线路(不挂通道)</option>
|
|
325
|
+
<option value="channel-2">通道 · 2 回</option>
|
|
326
|
+
<option value="channel-3">通道 · 3 回</option>
|
|
327
|
+
</select>
|
|
328
|
+
</label>
|
|
329
|
+
<label>路径
|
|
330
|
+
<select v-model="routing" data-demo="connect-routing" :disabled="isDraftLive">
|
|
331
|
+
<option value="straight">直线</option>
|
|
332
|
+
<option value="orthogonal">正交折线</option>
|
|
333
|
+
</select>
|
|
334
|
+
</label>
|
|
335
|
+
</div>
|
|
336
|
+
<div class="dv-row">
|
|
337
|
+
<label>线色 <input v-model="lineColor" type="color" data-demo="connect-color" /></label>
|
|
338
|
+
<label>线宽 <input v-model.number="lineWidth" type="range" min="1" max="6" step="0.2" data-demo="connect-width" /> {{ lineWidth.toFixed(1) }}</label>
|
|
339
|
+
</div>
|
|
340
|
+
<div class="dv-row">
|
|
341
|
+
<label><input v-model="arrow" type="checkbox" data-demo="connect-arrow" /> 箭头</label>
|
|
342
|
+
<label>箭头大小 <input v-model.number="arrowSize" type="range" min="7" max="22" step="1" data-demo="connect-arrow-size" /> {{ arrowSize }}</label>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="dv-row">
|
|
345
|
+
<label>箭头位置
|
|
346
|
+
<select v-model="arrowPositionsText" data-demo="connect-arrow-positions">
|
|
347
|
+
<option value="0.25">25%</option>
|
|
348
|
+
<option value="0.5">50%</option>
|
|
349
|
+
<option value="0.75">75%</option>
|
|
350
|
+
<option value="0.25,0.75">25% + 75%</option>
|
|
351
|
+
<option value="0.15,0.5,0.85">15% + 50% + 85%</option>
|
|
352
|
+
</select>
|
|
353
|
+
</label>
|
|
354
|
+
<label>回线间距 <input v-model.number="circuitSpacing" type="range" min="6" max="28" step="1" data-demo="connect-spacing" /> {{ circuitSpacing }}px</label>
|
|
355
|
+
</div>
|
|
356
|
+
<p class="dv-hint">草稿样式直接传给 <code>beginAddChannel({ channelDraftStyle })</code>;连线期间改颜色 / 线宽 / 箭头位置 / 回线间距会走 <code>updateAddChannelStyle()</code>,预览立即更新。正交折线会自动补一个 90 度拐点;落地线路用完整 path,通道只写中间拐点。</p>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<div class="dv-group">
|
|
360
|
+
<h3>草稿与落地</h3>
|
|
361
|
+
<div class="dv-row">
|
|
362
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="connect-start" :disabled="isDraftLive" @click="startDraft">开始连线</button>
|
|
363
|
+
<button type="button" class="dv-button dv-button--danger" data-demo="connect-cancel" :disabled="!isDraftLive" @click="cancelDraft">取消添加</button>
|
|
364
|
+
</div>
|
|
365
|
+
<div class="dv-row">
|
|
366
|
+
<button type="button" class="dv-button" data-demo="connect-default" :disabled="!canFinish" @click="commitDraft">默认添加(commitAddChannel)</button>
|
|
367
|
+
<button type="button" class="dv-button" data-demo="connect-add-lines" :disabled="!canFinish" @click="addWithLines">用 addLines 添加</button>
|
|
368
|
+
<button type="button" class="dv-button" data-demo="connect-add-channels" :disabled="!canFinish" @click="addWithChannels">用 addChannels 添加</button>
|
|
369
|
+
</div>
|
|
370
|
+
<p class="dv-readout" data-demo="connect-draft-readout">{{ draftText }}</p>
|
|
371
|
+
<p class="dv-readout" data-demo="connect-data-readout">{{ dataText }}</p>
|
|
372
|
+
<p class="dv-readout" data-demo="connect-action-readout">{{ actionText }}</p>
|
|
373
|
+
<p class="dv-readout" data-demo="connect-cancel-readout">{{ cancelText }}</p>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<div class="dv-group">
|
|
377
|
+
<h3>操作日志</h3>
|
|
378
|
+
<ul class="dv-log" data-demo="connect-log">
|
|
379
|
+
<li>{{ logText }}</li>
|
|
380
|
+
</ul>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<div class="dv-group">
|
|
384
|
+
<h3>场景</h3>
|
|
385
|
+
<div class="dv-row">
|
|
386
|
+
<button type="button" class="dv-button" data-demo="connect-reset" @click="resetScene">恢复纯点 + 线初始数据</button>
|
|
387
|
+
</div>
|
|
388
|
+
<p class="dv-hint">初始 channels 恒为 [];线路的 from / to 照样绑节点,拖节点时端点会跟着走。想验证取消:开始连线后先点一个节点,再点画布空白处,草稿会立即收起并发出 <code>reason: 'click-outside-node'</code> 的 cancel。</p>
|
|
389
|
+
</div>
|
|
390
|
+
</aside>
|
|
391
|
+
</section>
|
|
392
|
+
</template>
|