gis-view-webgl2 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -11
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +445 -241
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +65 -498
- package/docs/GisView.md +1 -1
- package/docs/GisView/345/256/236/347/216/260/345/216/237/347/220/206.md +1 -1
- package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +114 -478
- package/docs/demos/BasicDemo.vue +144 -27
- package/docs/demos/BatchAddDemo.vue +133 -7
- package/docs/demos/ChannelDemo.vue +61 -0
- package/docs/demos/ConnectDemo.vue +164 -33
- package/docs/demos/CopyMergeDemo.vue +144 -42
- package/docs/demos/EventDemo.vue +29 -24
- package/docs/demos/LabelDemo.vue +5 -5
- package/docs/demos/api-index.js +53 -41
- package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +1 -1
- package/docs/demos/gis-view-demo/GisViewDemoStage.vue +1 -1
- package/docs/demos/gis-view-demo/panels/DataPanels.vue +1 -1
- package/docs/demos/gis-view-demo/useGisViewDemo.js +31 -33
- package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +1 -0
- package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +3 -0
- package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +8 -7
- package/docs/from0/05-/347/233/270/346/234/272/344/270/216/344/272/244/344/272/222.md +1 -1
- package/docs/from0/07-/351/200/232/351/201/223/345/210/206/347/273/204/344/270/216/345/244/232/345/233/236/347/272/277.md +26 -10
- package/docs/index.html +136 -13
- package/docs/internals/15-04-/351/200/220/350/275/256/350/256/260/345/275/225-/347/203/255/345/212/233/345/233/276/344/270/216/346/200/247/350/203/275.md +10 -0
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +1 -1
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +82 -14
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +23 -17
- package/docs/manual/05-/347/224/273/350/264/250/344/270/216/346/211/271/351/207/217/346/233/264/346/226/260.md +65 -0
- package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +9 -10
- package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +100 -40
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +11 -7
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +66 -6
- package/docs/manual/10-/351/200/232/351/201/223/347/274/226/350/276/221/344/270/216/346/226/260/345/242/236.md +226 -493
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +28 -5
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +46 -14
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +5 -2
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +34 -29
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +30 -22
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +168 -81
- package/docs/manual/19-/350/257/273/346/225/260/344/270/216/346/213/226/346/213/275/351/242/204/350/247/210.md +1 -1
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +5 -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 +9 -6
- package/docs/steps/README.md +201 -120
- package/docs/steps/bench-scale.html +1 -1
- package/docs/steps/index.html +222 -6
- package/docs/steps/selftest-actions.html +26 -25
- package/docs/steps/selftest-data.html +1 -1
- package/docs/steps/selftest-demos.html +290 -16
- package/docs/steps/selftest-docs.html +17 -6
- package/docs/steps/selftest-features.html +594 -54
- package/docs/steps/selftest-full.html +3 -3
- package/docs/steps/selftest-labels.html +82 -1
- package/docs/steps/selftest-perf.html +62 -7
- package/docs/steps/selftest-select.html +71 -6
- package/docs/steps/step-05-pick.js +4 -8
- package/docs/steps/step-07-channels.js +3 -5
- package/docs/steps/step-08-labels.js +3 -3
- package/docs/steps/step-10-full.js +3 -3
- package/docs/steps/step-12-events.js +41 -24
- package/docs//344/273/2160/345/210/260/345/256/214/346/225/264/347/232/204/345/256/236/347/216/260.md +1 -0
- package/package.json +1 -1
- package/docs/ai/01-/350/265/267/346/272/220/344/270/216/350/277/231/344/273/275/346/261/207/346/200/273/347/232/204/347/224/261/346/235/245.md +0 -29
- package/docs/ai/02-/346/214/211/345/206/205/345/256/271/345/210/206/347/261/273-/350/277/231/344/270/200/350/267/257/351/203/275/345/234/250/346/224/271/344/273/200/344/271/210.md +0 -66
- package/docs/ai/03-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/200/357/274/211/357/274/232/351/200/211/345/236/213/351/200/232/351/201/223/344/270/216/346/240/267/345/274/217.md +0 -178
- package/docs/ai/04-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/272/214/357/274/211/357/274/232/344/272/244/344/272/222/346/200/247/350/203/275/344/270/216/346/225/260/346/215/256.md +0 -140
- package/docs/ai/05-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/211/357/274/211/357/274/232/345/256/277/344/270/273/346/227/201/346/224/257/344/270/216/346/224/266/345/217/243.md +0 -43
|
@@ -268,6 +268,92 @@
|
|
|
268
268
|
check(`「${spec.tab}」把演示 id 写进地址栏(?demo=${spec.id})`, location.search.includes(`demo=${spec.id}`), location.search)
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
// 2.0.1 基础视图:比例读写与空闲检测都能从面板操作并回读
|
|
272
|
+
if (await clickTab('基础视图')) {
|
|
273
|
+
const ready = await canvasReady()
|
|
274
|
+
await wait(300)
|
|
275
|
+
const demo = document.querySelector('.dv-demo')
|
|
276
|
+
const setup = demo?.__vueParentComponent?.setupState
|
|
277
|
+
const view = await until(() => setup?.view, 8000)
|
|
278
|
+
const canvas = demo?.querySelector('canvas.gis-view__canvas') ?? demo?.querySelector('canvas')
|
|
279
|
+
const scaleInput = document.querySelector('[data-demo=basic-scale-input]')
|
|
280
|
+
const scaleSet = document.querySelector('[data-demo=basic-scale-set]')
|
|
281
|
+
const scaleGet = document.querySelector('[data-demo=basic-scale-get]')
|
|
282
|
+
const scaleRead = document.querySelector('[data-demo=basic-scale-read]')
|
|
283
|
+
const idleToggle = document.querySelector('[data-demo=basic-idle-toggle]')
|
|
284
|
+
const idleRead = document.querySelector('[data-demo=basic-idle-read]')
|
|
285
|
+
check('基础视图:比例与空闲面板控件齐全',
|
|
286
|
+
Boolean(ready && view && canvas && scaleInput && scaleSet && scaleGet && scaleRead && idleToggle && idleRead),
|
|
287
|
+
`${ready ? 'canvas 就绪' : '没有 canvas'} / view=${Boolean(view)} / 比例 ${[scaleInput, scaleSet, scaleGet, scaleRead].filter(Boolean).length}/4 / 空闲 ${[idleToggle, idleRead].filter(Boolean).length}/2`)
|
|
288
|
+
if (ready && view && canvas && scaleInput && scaleSet && scaleGet && scaleRead && idleToggle && idleRead) {
|
|
289
|
+
const targetScale = 96
|
|
290
|
+
scaleInput.value = String(targetScale)
|
|
291
|
+
scaleInput.dispatchEvent(new Event('input', { bubbles: true }))
|
|
292
|
+
scaleSet.click()
|
|
293
|
+
await wait(120)
|
|
294
|
+
check('基础视图:setScale() 改到输入值,getScale() 与面板读数同步',
|
|
295
|
+
Math.abs(view.getScale() - targetScale) < 0.01 && scaleRead.textContent.includes(targetScale.toFixed(2)),
|
|
296
|
+
`getScale=${view.getScale()} / 面板 ${scaleRead.textContent.trim()}`)
|
|
297
|
+
scaleGet.click()
|
|
298
|
+
await wait(80)
|
|
299
|
+
check('基础视图:getScale() 按钮把读到的值写回输入与事件读数',
|
|
300
|
+
Math.abs(Number(scaleInput.value) - view.getScale()) < 0.01 && setup.readouts.event.includes('getScale()'),
|
|
301
|
+
`input=${scaleInput.value} / 事件 ${setup.readouts.event}`)
|
|
302
|
+
|
|
303
|
+
const pointerMove = () => {
|
|
304
|
+
const rect = canvas.getBoundingClientRect()
|
|
305
|
+
canvas.dispatchEvent(new PointerEvent('pointermove', {
|
|
306
|
+
bubbles: true,
|
|
307
|
+
cancelable: true,
|
|
308
|
+
composed: true,
|
|
309
|
+
pointerId: 1,
|
|
310
|
+
pointerType: 'mouse',
|
|
311
|
+
isPrimary: true,
|
|
312
|
+
clientX: rect.left + 12,
|
|
313
|
+
clientY: rect.top + 12
|
|
314
|
+
}))
|
|
315
|
+
}
|
|
316
|
+
pointerMove()
|
|
317
|
+
await wait(280)
|
|
318
|
+
const idleBeforeScale = view.getIdleTime()
|
|
319
|
+
const scaleBeforeProgrammatic = view.getScale()
|
|
320
|
+
view.setScale(scaleBeforeProgrammatic * 1.1)
|
|
321
|
+
await wait(100)
|
|
322
|
+
check('基础视图:程序化 setScale() 不重置空闲计时',
|
|
323
|
+
idleBeforeScale > 200 && view.getIdleTime() >= idleBeforeScale,
|
|
324
|
+
`${idleBeforeScale.toFixed(0)} ms → ${view.getIdleTime().toFixed(0)} ms`)
|
|
325
|
+
|
|
326
|
+
pointerMove()
|
|
327
|
+
await wait(40)
|
|
328
|
+
const idleAfterActivity = view.getIdleTime()
|
|
329
|
+
check('基础视图:真实 pointermove 后 getIdleTime() 归到阈值以内',
|
|
330
|
+
idleAfterActivity < 300,
|
|
331
|
+
`${idleAfterActivity.toFixed(0)} ms`)
|
|
332
|
+
|
|
333
|
+
const idleCallbacksBefore = setup.idleCallbackCount
|
|
334
|
+
const idleEventsBefore = setup.idleEventCount
|
|
335
|
+
await wait(1500)
|
|
336
|
+
await wait(250)
|
|
337
|
+
check('基础视图:停手超过 idleTimeout 后 onIdle 与 idle 各触发一次,读数同步刷新',
|
|
338
|
+
setup.idleCallbackCount === idleCallbacksBefore + 1 &&
|
|
339
|
+
setup.idleEventCount === idleEventsBefore + 1 &&
|
|
340
|
+
view.getIdleTime() > 1200 &&
|
|
341
|
+
idleRead.textContent.includes(`onIdle:${setup.idleCallbackCount} 次`) &&
|
|
342
|
+
idleRead.textContent.includes(`idle 事件:${setup.idleEventCount} 次`),
|
|
343
|
+
`onIdle ${idleCallbacksBefore}→${setup.idleCallbackCount} / idle ${idleEventsBefore}→${setup.idleEventCount} / idleTime ${view.getIdleTime().toFixed(0)} ms`)
|
|
344
|
+
await wait(400)
|
|
345
|
+
check('基础视图:同一空闲段不会重复触发 onIdle / idle',
|
|
346
|
+
setup.idleCallbackCount === idleCallbacksBefore + 1 && setup.idleEventCount === idleEventsBefore + 1,
|
|
347
|
+
`onIdle ${setup.idleCallbackCount} 次 / idle ${setup.idleEventCount} 次`)
|
|
348
|
+
if (!idleToggle.checked) {
|
|
349
|
+
idleToggle.click()
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
check('基础视图:页签在', false, '没找到「基础视图」页签')
|
|
355
|
+
}
|
|
356
|
+
|
|
271
357
|
// 2.0 点聚合:控件齐全、聚合圆点击只发 clusterClick,maxZoom 展开后又回来
|
|
272
358
|
if (await clickTab('点聚合')) {
|
|
273
359
|
const ready = await canvasReady()
|
|
@@ -559,6 +645,85 @@
|
|
|
559
645
|
const afterObjectChannel = view.getData()
|
|
560
646
|
const objectChannel = channelList().at(-1)
|
|
561
647
|
check('可视化连线:commitAddChannel 直接传通道对象即可按 lineIds / groups 落地', Boolean(objectChannelDraft) && channelList().length === 2 && afterObjectChannel.lines.length === beforeObjectChannel.lines.length + 2 && String(objectChannel?.id).startsWith('connect-object-channel-') && objectChannel?.lineIds?.length === 2 && objectChannel.lineIds.every(id => String(id).startsWith('connect-object-line-')), `${channelList().length} 通道 / ${beforeObjectChannel.lines.length} -> ${afterObjectChannel.lines.length} 线 / ${objectChannel?.id ?? '无'} / ${JSON.stringify(objectChannel?.lineIds ?? [])}`)
|
|
648
|
+
|
|
649
|
+
// 单一通道 / 已有分组:预置一条 node-0 ↔ node-1 的通道,再分别验证阻断、新分组、指定分组与 beginAddLine 挂回线
|
|
650
|
+
document.querySelector('[data-demo=connect-seed-existing]').click()
|
|
651
|
+
const seededExisting = await until(() => view.getChannelGroups('connect-existing-channel').length === 2, 3000)
|
|
652
|
+
check('可视化连线:预置已有通道带两个分组,面板读数同步', Boolean(seededExisting) && channelList().length === 3 && text('connect-existing-readout').includes('已有通道'), `${channelList().length} 通道 / ${text('connect-existing-readout')}`)
|
|
653
|
+
const singleChannelInput = document.querySelector('[data-demo=connect-single-channel]')
|
|
654
|
+
const existingModeSelect = document.querySelector('[data-demo=connect-existing-mode]')
|
|
655
|
+
const groupTargetSelect = document.querySelector('[data-demo=connect-group-target]')
|
|
656
|
+
const setExistingMode = async value => {
|
|
657
|
+
existingModeSelect.value = value
|
|
658
|
+
existingModeSelect.dispatchEvent(new Event('change', { bubbles: true }))
|
|
659
|
+
await wait(60)
|
|
660
|
+
}
|
|
661
|
+
const beginPresetPair = async () => {
|
|
662
|
+
startPointSelect.value = 'node-0'
|
|
663
|
+
startPointSelect.dispatchEvent(new Event('change', { bubbles: true }))
|
|
664
|
+
await wait(50)
|
|
665
|
+
document.querySelector('[data-demo=connect-start]').click()
|
|
666
|
+
await wait(70)
|
|
667
|
+
await clickNode('node-1')
|
|
668
|
+
return until(() => {
|
|
669
|
+
const current = view.getAddChannelDraft()
|
|
670
|
+
return current?.phase === 'ready' ? current : null
|
|
671
|
+
}, 2500)
|
|
672
|
+
}
|
|
673
|
+
await setExistingMode('none')
|
|
674
|
+
if (!singleChannelInput.checked) singleChannelInput.click()
|
|
675
|
+
await wait(50)
|
|
676
|
+
const blockedLinesBefore = view.getData().lines.length
|
|
677
|
+
const blockedDraft = await beginPresetPair()
|
|
678
|
+
document.querySelector('[data-demo=connect-default]').click()
|
|
679
|
+
const blockedEventReady = await until(() => text('connect-event-readout').includes('phase=blocked'), 2500)
|
|
680
|
+
check('可视化连线:singleChannel + 已有通道触发 ready blocked 与提交失败事件', Boolean(blockedDraft) && blockedDraft.blocked === true && blockedDraft.existingChannel === 'block' && Boolean(blockedEventReady) && text('connect-event-readout').includes('singleChannel=true') && text('connect-action-readout').includes('blocked'), `${text('connect-event-readout')} / ${view.getData().lines.length} 线`)
|
|
681
|
+
check('可视化连线:阻断失败不改线路 / 通道数据', view.getData().lines.length === blockedLinesBefore && channelList().length === 3, `${blockedLinesBefore} → ${view.getData().lines.length} 线 / ${channelList().length} 通道`)
|
|
682
|
+
|
|
683
|
+
await setExistingMode('newGroup')
|
|
684
|
+
const groupsBeforeNew = view.getChannelGroups('connect-existing-channel').length
|
|
685
|
+
const newGroupLinesBefore = view.getData().lines.length
|
|
686
|
+
const newGroupDraft = await beginPresetPair()
|
|
687
|
+
const newGroupCommitReady = newGroupDraft?.preferred === 'newGroup' && newGroupDraft?.blocked === false
|
|
688
|
+
document.querySelector('[data-demo=connect-default]').click()
|
|
689
|
+
await wait(160)
|
|
690
|
+
check('可视化连线:existingChannel: newGroup 把 2 回新线作为新分组加入已有通道', Boolean(newGroupCommitReady) && view.getChannelGroups('connect-existing-channel').length === groupsBeforeNew + 1 && view.getData().lines.length === newGroupLinesBefore + 2 && channelList().length === 3, `${groupsBeforeNew} → ${view.getChannelGroups('connect-existing-channel').length} 组 / ${newGroupLinesBefore} → ${view.getData().lines.length} 线 / ${text('connect-action-readout')}`)
|
|
691
|
+
|
|
692
|
+
await setExistingMode('joinGroup')
|
|
693
|
+
groupTargetSelect.value = 'connect-existing-group-b'
|
|
694
|
+
groupTargetSelect.dispatchEvent(new Event('change', { bubbles: true }))
|
|
695
|
+
await wait(50)
|
|
696
|
+
const joinGroupBefore = view.getChannelGroups('connect-existing-channel').find(group => group.id === 'connect-existing-group-b')?.lineIds.length ?? 0
|
|
697
|
+
const joinGroupChannelsBefore = channelList().length
|
|
698
|
+
const joinGroupDraft = await beginPresetPair()
|
|
699
|
+
const joinGroupCommitReady = joinGroupDraft?.targetGroupId === 'connect-existing-group-b'
|
|
700
|
+
document.querySelector('[data-demo=connect-default]').click()
|
|
701
|
+
await wait(160)
|
|
702
|
+
const joinGroupAfter = view.getChannelGroups('connect-existing-channel').find(group => group.id === 'connect-existing-group-b')?.lineIds.length ?? 0
|
|
703
|
+
check('可视化连线:existingChannel: joinGroup + groupId 把 2 回新线并入指定分组', Boolean(joinGroupCommitReady) && joinGroupAfter === joinGroupBefore + 2 && channelList().length === joinGroupChannelsBefore, `group-b ${joinGroupBefore} → ${joinGroupAfter} 回 / ${channelList().length} 通道 / ${text('connect-action-readout')}`)
|
|
704
|
+
|
|
705
|
+
kindSelect.value = 'line'
|
|
706
|
+
kindSelect.dispatchEvent(new Event('change', { bubbles: true }))
|
|
707
|
+
await wait(60)
|
|
708
|
+
groupTargetSelect.value = 'connect-existing-group-a'
|
|
709
|
+
groupTargetSelect.dispatchEvent(new Event('change', { bubbles: true }))
|
|
710
|
+
await wait(50)
|
|
711
|
+
const attachGroupBefore = view.getChannelGroups('connect-existing-channel').find(group => group.id === 'connect-existing-group-a')?.lineIds.length ?? 0
|
|
712
|
+
const attachChannelsBefore = channelList().length
|
|
713
|
+
const attachDraft = await beginPresetPair()
|
|
714
|
+
const attachDraftReady = attachDraft?.kind === 'line' && attachDraft?.existingChannel === 'joinGroup' && attachDraft?.targetGroupId === 'connect-existing-group-a'
|
|
715
|
+
document.querySelector('[data-demo=connect-default]').click()
|
|
716
|
+
await wait(160)
|
|
717
|
+
const attachGroupAfter = view.getChannelGroups('connect-existing-channel').find(group => group.id === 'connect-existing-group-a')?.lineIds.length ?? 0
|
|
718
|
+
check('可视化连线:beginAddLine + existingChannel: joinGroup 也能把新回线挂进指定分组,不新建通道', Boolean(attachDraftReady) && attachGroupAfter === attachGroupBefore + 1 && channelList().length === attachChannelsBefore && text('connect-log').includes('beginAddLine'), `${text('connect-log')} / group-a ${attachGroupBefore} → ${attachGroupAfter} 回`)
|
|
719
|
+
|
|
720
|
+
kindSelect.value = 'channel-2'
|
|
721
|
+
kindSelect.dispatchEvent(new Event('change', { bubbles: true }))
|
|
722
|
+
await setExistingMode('auto')
|
|
723
|
+
const autoDraft = await beginPresetPair()
|
|
724
|
+
check('可视化连线:existingChannel: auto 在分组有余量时推荐新分组', autoDraft?.blocked === false && autoDraft?.preferred === 'newGroup' && autoDraft?.existingChannel === 'auto', JSON.stringify({ preferred: autoDraft?.preferred, blocked: autoDraft?.blocked }))
|
|
725
|
+
view.cancelAddChannel()
|
|
726
|
+
await wait(60)
|
|
562
727
|
check('可视化连线:读数面板同步报出纯点 + 线 / 通道数量', text('connect-data-readout').includes('节点') && text('connect-data-readout').includes('通道'), text('connect-data-readout'))
|
|
563
728
|
}
|
|
564
729
|
else {
|
|
@@ -931,7 +1096,7 @@
|
|
|
931
1096
|
const click = key => document.querySelector(`[data-demo="${key}"]`)?.click()
|
|
932
1097
|
const text = key => document.querySelector(`[data-demo="${key}"]`)?.textContent.trim() ?? ''
|
|
933
1098
|
if (ready && view) {
|
|
934
|
-
const groupsOf = () => view.
|
|
1099
|
+
const groupsOf = () => (view.getOptions().channels ?? []).reduce((sum, channel) => sum + view.getChannelGroups(channel.id).length, 0)
|
|
935
1100
|
const lineIndex = view.getData().lines.findIndex(line => String(line.id) === 'line-0')
|
|
936
1101
|
const waypointCount = () => view.getWaypoints(lineIndex).length
|
|
937
1102
|
const pointsBefore = view.getData().points.length
|
|
@@ -950,11 +1115,49 @@
|
|
|
950
1115
|
click('batch-add-channel-groups')
|
|
951
1116
|
const groupsAfter = await until(() => groupsOf() === groupsBefore + 2, 6000)
|
|
952
1117
|
check('批量新增:addChannelGroups 一次补 2 组、4 条回线', Boolean(groupsAfter) && text('batch-add-readout').includes('2 组 / 4 条回线'), `${groupsBefore} → ${groupsOf()} / ${text('batch-add-readout')}`)
|
|
953
|
-
const waypointsBefore = waypointCount()
|
|
954
|
-
click('batch-add-waypoints')
|
|
955
|
-
const waypointsAfter = await until(() => waypointCount() > waypointsBefore, 6000)
|
|
956
|
-
check('批量新增:addWaypoints 一次插多个拐点且不虚报失败坐标', Boolean(waypointsAfter) && text('batch-add-readout').includes('addWaypoints') && !text('batch-add-readout').includes('没有落地'), `${waypointsBefore} → ${waypointCount()} / ${text('batch-add-readout')}`)
|
|
957
|
-
|
|
1118
|
+
const waypointsBefore = waypointCount()
|
|
1119
|
+
click('batch-add-waypoints')
|
|
1120
|
+
const waypointsAfter = await until(() => waypointCount() > waypointsBefore, 6000)
|
|
1121
|
+
check('批量新增:addWaypoints 一次插多个拐点且不虚报失败坐标', Boolean(waypointsAfter) && text('batch-add-readout').includes('addWaypoints') && !text('batch-add-readout').includes('没有落地'), `${waypointsBefore} → ${waypointCount()} / ${text('batch-add-readout')}`)
|
|
1122
|
+
const updatePointBefore = view.getData().points.find(point => String(point.id).startsWith('batch-point-'))
|
|
1123
|
+
const updateLineBefore = view.getData().lines.find(line => String(line.id).startsWith('batch-line-'))
|
|
1124
|
+
click('batch-update-all')
|
|
1125
|
+
const batchUpdated = await until(() => {
|
|
1126
|
+
const point = view.getData().points.find(item => String(item.id).startsWith('batch-point-') && item.batch_updated !== undefined)
|
|
1127
|
+
const line = view.getData().lines.find(item => String(item.id).startsWith('batch-line-') && item.batch_updated !== undefined)
|
|
1128
|
+
const channel = (view.getOptions().channels ?? []).find(item => item.batch_updated !== undefined)
|
|
1129
|
+
return point && line && channel ? { point, line, channel } : null
|
|
1130
|
+
}, 6000)
|
|
1131
|
+
check('批量新增:batch 包住 patchPoints / patchLines / updateChannel,一次改三类数据', Boolean(batchUpdated) && text('batch-add-readout').includes('batch(update)') && text('batch-add-readout').includes('三种 patch 合并为一步历史') && batchUpdated.point.zone_name?.startsWith('批量修改') && batchUpdated.line.color === '#2f6fd0' && Number(batchUpdated.channel.width) >= 2.4, text('batch-add-readout'))
|
|
1132
|
+
view.undo()
|
|
1133
|
+
const batchUpdateUndone = await until(() => {
|
|
1134
|
+
const point = view.getData().points.find(item => item.id === updatePointBefore?.id)
|
|
1135
|
+
const line = view.getData().lines.find(item => item.id === updateLineBefore?.id)
|
|
1136
|
+
const channel = (view.getOptions().channels ?? []).find(item => item.batch_updated !== undefined)
|
|
1137
|
+
return point && point.batch_updated === undefined && line && line.batch_updated === undefined && !channel
|
|
1138
|
+
}, 5000)
|
|
1139
|
+
check('批量新增:三类 patch 只占一步历史,撤销一次整批恢复', Boolean(batchUpdateUndone), `节点 ${updatePointBefore?.id ?? '—'} / 线路 ${updateLineBefore?.id ?? '—'}`)
|
|
1140
|
+
const deletePoint = view.getData().points.find(point => String(point.id).startsWith('batch-point-'))
|
|
1141
|
+
const deleteChannel = (view.getOptions().channels ?? []).find(channel => String(channel.id).startsWith('batch-channel-'))
|
|
1142
|
+
const deleteBefore = { points: view.getData().points.length, lines: view.getData().lines.length, channels: view.getOptions().channels?.length ?? 0 }
|
|
1143
|
+
click('batch-delete-all')
|
|
1144
|
+
const batchDeleted = await until(() => {
|
|
1145
|
+
const after = view.getData()
|
|
1146
|
+
return after.points.length < deleteBefore.points && after.lines.length < deleteBefore.lines && (view.getOptions().channels?.length ?? 0) < deleteBefore.channels ? after : null
|
|
1147
|
+
}, 6000)
|
|
1148
|
+
check('批量新增:batch 内 removeChannel / removePoints / removeLines 一次删三类数据', Boolean(batchDeleted) && text('batch-add-readout').includes('batch(delete)') && text('batch-add-readout').includes('整批只记一步'), `${deleteBefore.points}/${deleteBefore.lines}/${deleteBefore.channels} → ${view.getData().points.length}/${view.getData().lines.length}/${view.getOptions().channels?.length ?? 0} · ${text('batch-add-readout')}`)
|
|
1149
|
+
view.undo()
|
|
1150
|
+
const batchDeleteUndone = await until(() => view.getData().points.length === deleteBefore.points && view.getData().lines.length === deleteBefore.lines && (view.getOptions().channels?.length ?? 0) === deleteBefore.channels, 5000)
|
|
1151
|
+
check('批量新增:删除批同样只占一步历史,撤销后三类数据一起回来', Boolean(batchDeleteUndone) && view.getData().points.some(point => point.id === deletePoint?.id) && (view.getOptions().channels ?? []).some(channel => channel.id === deleteChannel?.id), `${view.getData().points.length}/${view.getData().lines.length}/${view.getOptions().channels?.length ?? 0}`)
|
|
1152
|
+
const groupsBeforeBatchSplit = view.getChannels().length
|
|
1153
|
+
click('batch-group-split')
|
|
1154
|
+
const batchSplit = await until(() => view.getChannels().length > groupsBeforeBatchSplit && text('batch-add-readout').includes('batch(splitChannelGroups)'), 6000)
|
|
1155
|
+
check('批量新增:batch 内逐条 splitChannelGroups,多条通道分组一次重排', Boolean(batchSplit) && text('batch-add-readout').includes('整批一步历史'), `${groupsBeforeBatchSplit} → ${view.getChannels().length} · ${text('batch-add-readout')}`)
|
|
1156
|
+
const groupsBeforeBatchRemove = view.getChannels().length
|
|
1157
|
+
click('batch-group-remove')
|
|
1158
|
+
const batchGroupRemoved = await until(() => view.getChannels().length < groupsBeforeBatchRemove && text('batch-add-readout').includes('batch(removeChannelGroup)'), 6000)
|
|
1159
|
+
check('批量新增:batch 内逐条 removeChannelGroup,回线并回相邻组且数据不丢', Boolean(batchGroupRemoved) && text('batch-add-readout').includes('回线数据未丢'), `${groupsBeforeBatchRemove} → ${view.getChannels().length} · ${text('batch-add-readout')}`)
|
|
1160
|
+
}
|
|
958
1161
|
else {
|
|
959
1162
|
check('批量新增:画布与实例都起来了', false, ready ? '没有 setupState.view' : '没有 canvas')
|
|
960
1163
|
}
|
|
@@ -1066,9 +1269,50 @@
|
|
|
1066
1269
|
const confirmButton = document.querySelector('[data-demo="merge-confirm"]')
|
|
1067
1270
|
const cancelButton = document.querySelector('[data-demo="merge-cancel"]')
|
|
1068
1271
|
check('批量复制与合并:待确认面板给出 resolvePointMerge(true / false) 两个入口', Boolean(confirmButton) && Boolean(cancelButton) && confirmButton.disabled && cancelButton.disabled && confirmButton.textContent.includes('resolvePointMerge(true') && cancelButton.textContent.includes('resolvePointMerge(false'), `${confirmButton?.textContent.trim()} / ${cancelButton?.textContent.trim()}`)
|
|
1069
|
-
setup.resetScene()
|
|
1070
|
-
await wait(300)
|
|
1071
|
-
|
|
1272
|
+
setup.resetScene()
|
|
1273
|
+
await wait(300)
|
|
1274
|
+
click('merge-select-sources')
|
|
1275
|
+
await until(() => view.getSelectionData().pointCount === 4, 5000)
|
|
1276
|
+
setup.mergeTargetId = 'node-7'
|
|
1277
|
+
const maxKeysInput = document.querySelector('[data-demo="merge-max-keys"]')
|
|
1278
|
+
maxKeysInput.value = 'vn_kv'
|
|
1279
|
+
maxKeysInput.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1280
|
+
click('merge-apply-max-keys')
|
|
1281
|
+
const maxKeysApplied = await until(() => setup.mergeMaxKeys === 'vn_kv' && view.getOptions().merge?.maxKeys?.join(',') === 'vn_kv', 2000)
|
|
1282
|
+
const maxKeysMergeBefore = view.getData().points.length
|
|
1283
|
+
click('merge-default')
|
|
1284
|
+
const maxKeysMerged = await until(() => view.getData().points.length === maxKeysMergeBefore - 3, 6000)
|
|
1285
|
+
const maxKeysTarget = view.getData().points.find(point => String(point.id) === 'node-7')
|
|
1286
|
+
check('批量复制与合并:maxKeys 输入应用后只把列出的 vn_kv 取参与场站最高', Boolean(maxKeysApplied) && Boolean(maxKeysMerged) && maxKeysTarget?.vn_kv === 1050 && maxKeysTarget?.name === '变电站 8' && text('merge-readout').includes('maxKeys'), JSON.stringify(maxKeysTarget && { name: maxKeysTarget.name, voltage: maxKeysTarget.vn_kv, readout: text('merge-readout') }))
|
|
1287
|
+
setup.resetScene()
|
|
1288
|
+
await wait(300)
|
|
1289
|
+
click('merge-select-sources')
|
|
1290
|
+
await until(() => view.getSelectionData().pointCount === 4, 5000)
|
|
1291
|
+
setup.mergeTargetId = 'node-7'
|
|
1292
|
+
const defaultMergeBefore = view.getData().points.length
|
|
1293
|
+
click('merge-default')
|
|
1294
|
+
const defaultMerged = await until(() => view.getData().points.length === defaultMergeBefore - 3, 6000)
|
|
1295
|
+
const defaultTarget = view.getData().points.find(point => String(point.id) === 'node-7')
|
|
1296
|
+
check('批量复制与合并:maxKeys 缺省时电压等业务字段取保留节点', Boolean(defaultMerged) && defaultTarget?.vn_kv === 15.7 && defaultTarget?.name === '变电站 8' && defaultTarget?.zone_name === '分区 4' && defaultTarget?.demo_merged === undefined, JSON.stringify(defaultTarget && { name: defaultTarget.name, zone: defaultTarget.zone_name, voltage: defaultTarget.vn_kv, demoMerged: defaultTarget.demo_merged }))
|
|
1297
|
+
check('批量复制与合并:缺省合并后的节点带 mergeInfo 来源与快照', defaultTarget?.mergeInfo?.version === 1 && JSON.stringify(defaultTarget.mergeInfo.sourceIds) === '["node-0","node-5","node-12"]' && defaultTarget.mergeInfo.points?.length === 3 && defaultTarget.mergeInfo.lines?.length > 0 && text('merge-readout').includes('mergeInfo'), JSON.stringify({ sources: defaultTarget?.mergeInfo?.sourceIds, points: defaultTarget?.mergeInfo?.points?.length, lines: defaultTarget?.mergeInfo?.lines?.length }))
|
|
1298
|
+
click('split-selected')
|
|
1299
|
+
const defaultSplitLanded = await until(() => view.getData().points.length === defaultMergeBefore && text('split-readout').includes('自动解除合并'), 6000)
|
|
1300
|
+
const defaultSplitResult = unwrap(setup.lastSplitResult)
|
|
1301
|
+
check('批量复制与合并:Demo 自动解除按 mergeInfo 恢复全部来源并回到合并前节点数', Boolean(defaultSplitLanded) && defaultSplitResult?.autoRestored === true && JSON.stringify(defaultSplitResult.mergeSourceIds) === '["node-0","node-5","node-12"]' && view.getData().points.find(point => point.id === 'node-7')?.vn_kv === 15.7, JSON.stringify({ auto: defaultSplitResult?.autoRestored, sources: defaultSplitResult?.mergeSourceIds, points: view.getData().points.length }))
|
|
1302
|
+
check('批量复制与合并:解除合并默认不选中恢复节点,读数明确「未选中拆分结果」', view.getSelectionData().pointCount === 0 && text('split-readout').includes('未选中拆分结果'), JSON.stringify({ selection: view.getSelectionData(), readout: text('split-readout') }))
|
|
1303
|
+
view.undo()
|
|
1304
|
+
const splitUndoForSelect = await until(() => view.getData().points.length === defaultMergeBefore - 3, 5000)
|
|
1305
|
+
const splitSelectInput = document.querySelector('[data-demo="split-select"]')
|
|
1306
|
+
splitSelectInput.checked = true
|
|
1307
|
+
splitSelectInput.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1308
|
+
const splitSelectEnabled = await until(() => setup.splitSelect === true, 2000)
|
|
1309
|
+
click('split-selected')
|
|
1310
|
+
const selectedSplitLanded = await until(() => view.getData().points.length === defaultMergeBefore && text('split-readout').includes('已选中拆分结果'), 6000)
|
|
1311
|
+
const selectedSplitResult = unwrap(setup.lastSplitResult)
|
|
1312
|
+
check('批量复制与合并:勾选后可配置解除合并选中全部恢复节点', Boolean(splitUndoForSelect) && Boolean(splitSelectEnabled) && Boolean(selectedSplitLanded) && view.getSelectionData().pointCount === selectedSplitResult?.items?.length && view.getSelectionData().pointCount === 4, JSON.stringify({ selected: view.getSelectionData().pointCount, items: selectedSplitResult?.items?.length }))
|
|
1313
|
+
setup.resetScene()
|
|
1314
|
+
await wait(300)
|
|
1315
|
+
const dragCanvas = document.querySelector('.gis-view__canvas')
|
|
1072
1316
|
const dragBox = dragCanvas.getBoundingClientRect()
|
|
1073
1317
|
const dragSend = (type, screen, extra = {}) => {
|
|
1074
1318
|
dragCanvas.dispatchEvent(new PointerEvent(type, {
|
|
@@ -1211,17 +1455,21 @@
|
|
|
1211
1455
|
check('批量复制与合并:409 后 rebase 生成新 opId 并成功落库',
|
|
1212
1456
|
Boolean(copyRebased) && setup.backendSnapshot?.version === conflictVersion + 2 && setup.backendSnapshot?.applied === conflictApplied + 1,
|
|
1213
1457
|
`v${conflictVersion} → v${setup.backendSnapshot?.version} / applied ${conflictApplied} → ${setup.backendSnapshot?.applied} / ${text('copy-backend-readout')}`)
|
|
1214
|
-
//
|
|
1458
|
+
// 解除合并:先选一组,再点演示按钮;有 mergeInfo 时自动恢复全部来源,撤销一步回到合并态
|
|
1215
1459
|
click('copy-select-sources')
|
|
1216
1460
|
const splitSelectionReady = await until(() => view.getSelectionData().pointCount === 4, 5000)
|
|
1217
1461
|
const splitSource = view.getSelectionData().points?.[0] ?? view.getData().points[0]
|
|
1218
1462
|
const pointsBeforeSplit = view.getData().points.length
|
|
1219
1463
|
click('split-selected')
|
|
1220
|
-
const splitLanded = await until(() => view.getData().points.length
|
|
1464
|
+
const splitLanded = await until(() => view.getData().points.length > pointsBeforeSplit && text('split-readout').includes('自动解除合并'), 6000)
|
|
1221
1465
|
const splitReadout = text('split-readout')
|
|
1222
|
-
|
|
1223
|
-
|
|
1466
|
+
const splitResult = unwrap(setup.lastSplitResult)
|
|
1467
|
+
check('批量复制与合并:解除合并优先走 splitPoint 自动恢复,mergeSourceIds / 改线 / 改通道读数落面板',
|
|
1468
|
+
Boolean(splitSelectionReady) && Boolean(splitLanded) && text('copy-action').includes('splitPoint') && splitResult?.autoRestored === true && splitReadout.includes('恢复来源') && splitReadout.includes('改线') && splitReadout.includes('改通道'),
|
|
1224
1469
|
`${pointsBeforeSplit} → ${view.getData().points.length} · ${splitReadout}`)
|
|
1470
|
+
check('批量复制与合并:自动解除恢复该次合并的来源节点,来源 id 与 mergeInfo 对得上',
|
|
1471
|
+
splitResult?.autoRestored === true && JSON.stringify(splitResult.mergeSourceIds) === '["node-7"]' && splitResult.items.length === 2 && splitResult.mergeSourceIds.every(id => view.getData().points.some(point => String(point.id) === String(id))),
|
|
1472
|
+
JSON.stringify({ sources: splitResult?.mergeSourceIds, items: splitResult?.items?.map(item => item.id) }))
|
|
1225
1473
|
view.undo()
|
|
1226
1474
|
const splitUndone = await until(() => view.getData().points.length === pointsBeforeSplit && view.getData().points.some(point => String(point.id) === String(splitSource?.id)), 5000)
|
|
1227
1475
|
check('批量复制与合并:拆分是一步历史,撤销后节点与连线恢复', Boolean(splitUndone), `${view.getData().points.length} 点 / ${splitSource?.id ?? '—'}`)
|
|
@@ -1871,6 +2119,32 @@
|
|
|
1871
2119
|
const channelField = key => channelDemo.querySelector(`[data-demo="${key}"]`)?.textContent.trim() ?? ''
|
|
1872
2120
|
const channelCanvas = channelDemo.querySelector('canvas.gis-view__canvas')
|
|
1873
2121
|
if (channelView && channelCanvas) {
|
|
2122
|
+
const simplifyToggle = channelDemo.querySelector('[data-demo="channel-simplify-enabled"]')
|
|
2123
|
+
const simplifyThresholdInput = channelDemo.querySelector('[data-demo="channel-simplify-threshold"]')
|
|
2124
|
+
const simplifyGroupsBefore = channelView.getChannelGroups()
|
|
2125
|
+
const simplifyGroupSize = group => group.lineIds?.length ?? group.lineIndexes?.length ?? 0
|
|
2126
|
+
const simplifyLineCountBefore = channelView.getData().lines.length
|
|
2127
|
+
const simplifyGroupLineCountBefore = simplifyGroupsBefore.reduce((sum, group) => sum + simplifyGroupSize(group), 0)
|
|
2128
|
+
simplifyThresholdInput.value = '1'
|
|
2129
|
+
simplifyThresholdInput.dispatchEvent(new Event('input', { bubbles: true }))
|
|
2130
|
+
simplifyThresholdInput.dispatchEvent(new Event('change', { bubbles: true }))
|
|
2131
|
+
await until(() => channelSetup.simplifyThreshold === 1, 2000)
|
|
2132
|
+
simplifyToggle.checked = true
|
|
2133
|
+
simplifyToggle.dispatchEvent(new Event('change', { bubbles: true }))
|
|
2134
|
+
const simplifyApplied = await until(() => channelView.getOptions().channelSimplify?.enabled === true && channelView.getOptions().channelSimplify?.threshold === 1 && channelField('channel-simplify-readout').includes('开 · threshold=1'), 5000)
|
|
2135
|
+
const simplifyGroups = channelView.getChannelGroups()
|
|
2136
|
+
const simplifyFolded = simplifyGroups.filter(group => simplifyGroupSize(group) > 1)
|
|
2137
|
+
const simplifyHiddenIndexes = new Set(simplifyFolded.flatMap(group => group.lineIndexes.slice(1)))
|
|
2138
|
+
const simplifyExpectedIndexes = channelView.getData().lines.map((_line, index) => index).filter(index => !simplifyHiddenIndexes.has(index))
|
|
2139
|
+
const simplifyVisibleIndexes = channelView.getData().lines.map((_line, index) => index).filter(index => channelView.getLineStyle(index) !== null)
|
|
2140
|
+
const simplifyRepresentativeStyle = simplifyFolded.length ? channelView.getLineStyle(simplifyFolded[0].lineIndexes[0]) : null
|
|
2141
|
+
check('通道与数据:开启简略后每个超阈值分组只画代表线,无 groups 的通道按一组处理', Boolean(simplifyApplied) && simplifyFolded.length > 0 && JSON.stringify(simplifyVisibleIndexes) === JSON.stringify(simplifyExpectedIndexes) && simplifyGroups.reduce((sum, group) => sum + simplifyGroupSize(group), 0) === simplifyGroupLineCountBefore && channelView.getData().lines.length === simplifyLineCountBefore && channelField('channel-simplify-readout').includes(`原始 ${simplifyGroupLineCountBefore} 条`), channelField('channel-simplify-readout'))
|
|
2142
|
+
check('通道与数据:简略代表线应用自定义颜色 / 线宽,关闭后全部回线恢复', simplifyRepresentativeStyle?.width === 4 && Math.round(simplifyRepresentativeStyle.color[0] * 255) === 255 && Math.round(simplifyRepresentativeStyle.color[1] * 255) === 107 && Math.round(simplifyRepresentativeStyle.color[2] * 255) === 53, JSON.stringify(simplifyRepresentativeStyle))
|
|
2143
|
+
simplifyToggle.checked = false
|
|
2144
|
+
simplifyToggle.dispatchEvent(new Event('change', { bubbles: true }))
|
|
2145
|
+
const simplifyDisabled = await until(() => channelView.getOptions().channelSimplify === false && channelField('channel-simplify-readout').includes('关'), 5000)
|
|
2146
|
+
const simplifyRestored = channelView.getData().lines.every((_line, index) => channelView.getLineStyle(index) !== null)
|
|
2147
|
+
check('通道与数据:关闭简略后全部回线恢复,原始数据 / 分组条数保持不变', Boolean(simplifyDisabled) && simplifyRestored && channelView.getData().lines.length === simplifyLineCountBefore && channelView.getChannelGroups().length === simplifyGroupsBefore.length, `可见 ${channelView.getData().lines.filter((_line, index) => channelView.getLineStyle(index) !== null).length} / ${simplifyLineCountBefore}`)
|
|
1874
2148
|
const visibilityChannelId = channelView.getOptions().channels?.[0]?.id
|
|
1875
2149
|
const groupsBeforeHide = channelView.getChannelGroups(visibilityChannelId).length
|
|
1876
2150
|
const membersBeforeHide = channelView.getChannel(visibilityChannelId)?.lineIndexes?.length ?? 0
|
|
@@ -2493,8 +2767,8 @@
|
|
|
2493
2767
|
await wait(150)
|
|
2494
2768
|
const apiItems = () => [...document.querySelectorAll('.demo-api-list:not(.demo-api-list--options) .demo-api-item')]
|
|
2495
2769
|
const optionItems = () => [...document.querySelectorAll('.demo-api-list--options .demo-api-item')]
|
|
2496
|
-
check('实例方法速查:整份成员清单按分组列全(16 组 /
|
|
2497
|
-
Boolean(ready) && document.querySelectorAll('.demo-api-group').length === 16 && apiItems().length ===
|
|
2770
|
+
check('实例方法速查:整份成员清单按分组列全(16 组 / 273 个成员)',
|
|
2771
|
+
Boolean(ready) && document.querySelectorAll('.demo-api-group').length === 16 && apiItems().length === 273,
|
|
2498
2772
|
`${document.querySelectorAll('.demo-api-group').length} 组 / ${apiItems().length} 个成员`)
|
|
2499
2773
|
const apiNames = apiItems().map(node => node.querySelector('b')?.textContent)
|
|
2500
2774
|
const roundNames = ['registerSymbol', 'unregisterSymbol', 'listSymbols']
|
|
@@ -2513,7 +2787,7 @@
|
|
|
2513
2787
|
check('实例方法速查:这一轮拆出来的方法也在清单里(addChannelGroupLines / joinChannelGroupLines / endAddChannel)',
|
|
2514
2788
|
roundNames1597.every(name => apiNames.includes(name)),
|
|
2515
2789
|
roundNames1597.filter(name => !apiNames.includes(name)).join(' / ') || '三个都在')
|
|
2516
|
-
check('实例方法速查:options 折叠清单列全
|
|
2790
|
+
check('实例方法速查:options 折叠清单列全 100 个键', optionItems().length === 100, `${optionItems().length} 个键`)
|
|
2517
2791
|
const apiSearch = document.querySelector('.demo-api-search input[type="search"]')
|
|
2518
2792
|
apiSearch.value = 'focusOn'
|
|
2519
2793
|
apiSearch.dispatchEvent(new Event('input', { bubbles: true }))
|
|
@@ -110,9 +110,11 @@
|
|
|
110
110
|
if (check('文档中心:挂上了 __gisDocs 钩子(没钩子多半是 file:// 打开的)', Boolean(api))) {
|
|
111
111
|
const doc = docsFrame.contentDocument
|
|
112
112
|
const files = api.docs.map(item => item.file)
|
|
113
|
-
check('文档中心:清单
|
|
113
|
+
check('文档中心:清单 87 份 md(10 个根 + 77 份子文档),含接口协议 / manual/18 / internals/18 / steps/rounds/09', api.docs.length === 87 && files.includes('接口协议.md') && files.includes('manual/18-逐方法示例.md') && files.includes('internals/18-性能实测与WebGPU评估.md') && files.includes('steps/rounds/09-第89-93轮.md'), `${api.docs.length} 份 / ${api.docs.filter(item => item.parent).length} 份挂在父文档下`)
|
|
114
|
+
const aiDocs = api.docs.filter(item => item.file === 'AI对话汇总.md' || item.file.startsWith('ai/'))
|
|
115
|
+
check('文档中心:AI 文档精简为「主汇总 + 维护索引」两个入口,旧 01 ~ 05 已合并', aiDocs.length === 2 && aiDocs.some(item => item.file === 'AI对话汇总.md') && aiDocs.some(item => item.file === 'ai/06-会话索引与复查方法.md' && item.parent === 'AI对话汇总.md') && !files.some(file => /^ai\/(0[1-5])-/.test(file)), aiDocs.map(item => item.file).join(' / '))
|
|
114
116
|
const parents = api.docs.filter(item => item.parent)
|
|
115
|
-
check('文档中心:子文档的 parent 都能对上清单里的父文档,分组标题挂在父条目上(6 组)', parents.length ===
|
|
117
|
+
check('文档中心:子文档的 parent 都能对上清单里的父文档,分组标题挂在父条目上(6 组)', parents.length === 77 && parents.every(item => files.includes(item.parent) && api.docs.some(candidate => candidate.file === item.parent && candidate.groupLabel)) && new Set(parents.map(item => item.parent)).size === 6, [...new Set(parents.map(item => item.parent))].join(' / '))
|
|
116
118
|
check('文档中心:清单 6 个交互页,含 api/index.html / playground/index.html / guide/sample-document.html', api.pages.length === 6 && api.pages.some(item => item.href === 'api/index.html') && api.pages.some(item => item.href === 'playground/index.html') && api.pages.some(item => item.href === 'guide/sample-document.html'), api.pages.map(item => item.href).join(' / '))
|
|
117
119
|
check('文档中心:侧栏按清单渲染(md 与交互页两条都数得对)', doc.querySelectorAll('#doc-list a').length === api.docs.length && doc.querySelectorAll('#page-list a').length === api.pages.length)
|
|
118
120
|
const treeGroups = [...doc.querySelectorAll('#doc-list details.doc-children')]
|
|
@@ -125,6 +127,15 @@
|
|
|
125
127
|
const pathDocs = pathCards.map(card => card.dataset.doc).filter(Boolean)
|
|
126
128
|
const apiPathCard = pathCards.find(card => card.textContent.includes('查方法与参数'))
|
|
127
129
|
check('文档中心:README 顶部有 4 条推荐阅读路径,3 篇文档加 API 页都指向真实目标', pathCards.length === 4 && pathDocs.length === 3 && pathDocs.every(file => files.includes(file)) && (apiPathCard?.getAttribute('href') ?? '').includes('api/index.html') && pathCards.every(card => card.querySelector('.doc-path__badge') && card.textContent.trim().length > 12), pathCards.map(card => card.querySelector('b')?.textContent?.trim()).join(' / '))
|
|
130
|
+
await api.load('manual/08-选中与拐点.md')
|
|
131
|
+
const breadcrumb = doc.querySelector('#content .doc-breadcrumb')
|
|
132
|
+
const crumbParent = breadcrumb?.querySelector('a[data-doc]')
|
|
133
|
+
check('文档中心:正文顶部有当前位置面包屑,父文档可点回索引', Boolean(breadcrumb) && breadcrumb.textContent.includes('文档中心') && breadcrumb.textContent.includes('GisView.md') && breadcrumb.textContent.includes('手册 08') && crumbParent?.dataset.doc === 'GisView.md' && files.includes(crumbParent.dataset.doc), breadcrumb?.textContent.trim() ?? '没有面包屑')
|
|
134
|
+
const pager = doc.querySelector('#content .doc-pager')
|
|
135
|
+
const pagerLinks = [...pager.querySelectorAll('a[data-doc]')]
|
|
136
|
+
check('文档中心:文末有上一份 / 下一份,顺序与侧栏清单一致', pagerLinks.length === 2 && pagerLinks[0].dataset.doc === 'manual/07-指针与要素编辑.md' && pagerLinks[1].dataset.doc === 'manual/09-通道与多回线.md' && pagerLinks.every(link => files.includes(link.dataset.doc)), pagerLinks.map(link => link.dataset.doc).join(' → '))
|
|
137
|
+
const tocCountText = doc.querySelector('#toc-count')?.textContent ?? ''
|
|
138
|
+
check('文档中心:右栏目录标题显示本页小节数,和目录链接数一致', Number(tocCountText) > 0 && Number(tocCountText) === doc.querySelectorAll('#toc nav a[data-id]').length, `${tocCountText} 个小节 / ${doc.querySelectorAll('#toc nav a[data-id]').length} 条目录`)
|
|
128
139
|
check('文档中心:正文链接的解析(相对「当前那份文档」算,解析不到给 null)', api.resolve('docs/GisView.md', '../README.md') === 'GisView.md' && api.resolve('../从0到完整的实现.md', 'steps/README.md') === '从0到完整的实现.md' && api.resolve('../index.html', 'README.md') === null)
|
|
129
140
|
const fixture = api.render('# 文档标题\n\n> 一句话摘要\n\n## 标题\n\n| A | B |\n| --- | --- |\n| 1 | 2 |\n\n```js\nconst a = 1\n```\n\n- 一级\n - 二级\n\n> 正文引用\n\n行内 `code` 与 [链接](GisView.md)\n')
|
|
130
141
|
const tableFixture = api.render('| 字段 | 类型 |\n| --- | --- |\n| `type` | `\'image/png\' \\| \'image/jpeg\' \\| \'svg\'` |\n')
|
|
@@ -520,8 +531,8 @@
|
|
|
520
531
|
if (check('API 速查:挂上了 __gisApi 钩子(数据来自 api-index.js,没钩子多半是 file:// 打开的)', Boolean(apiHook))) {
|
|
521
532
|
const apiDoc = apiFrame.contentDocument
|
|
522
533
|
const summary = apiHook.summary
|
|
523
|
-
check('API 速查:清单来自 api-index(
|
|
524
|
-
summary.members ===
|
|
534
|
+
check('API 速查:清单来自 api-index(273 个成员 / 268 个方法 / 5 个属性 / 100 个 options / 16 个分组)',
|
|
535
|
+
summary.members === 273 && summary.methods === 268 && summary.properties === 5 && summary.options === 100 && summary.sections === 16,
|
|
525
536
|
JSON.stringify(summary))
|
|
526
537
|
const topJumps = [...apiDoc.querySelectorAll('.doc-jump')]
|
|
527
538
|
const jumpTargets = topJumps.map(node => `${node.dataset.docJump}:${node.getAttribute('href')}`)
|
|
@@ -559,8 +570,8 @@
|
|
|
559
570
|
`${apiDoc.querySelectorAll('.api-member').length} 张卡片 / 属性 ${apiDoc.querySelectorAll('.api-member[data-kind="property"]').length} 张`)
|
|
560
571
|
const noExample = [...apiDoc.querySelectorAll('.api-member')].filter(node => Number(node.dataset.examples) < 1)
|
|
561
572
|
check('API 速查:每个成员都带至少一段示例(不出现空卡片)', noExample.length === 0,
|
|
562
|
-
noExample.length ? noExample.map(node => node.dataset.name).join(' / ') : '
|
|
563
|
-
check('API 速查:
|
|
573
|
+
noExample.length ? noExample.map(node => node.dataset.name).join(' / ') : '273 张卡片都有示例')
|
|
574
|
+
check('API 速查:100 个 options 都列全(名字 / 类型 / 默认值 / 说明四件套)',
|
|
564
575
|
apiDoc.querySelectorAll('.api-option').length === summary.options && [...apiDoc.querySelectorAll('.api-option')].every(node => node.querySelector('.name code') && node.querySelector('.type') && node.querySelector('.def') && node.querySelector('.doc')),
|
|
565
576
|
`${apiDoc.querySelectorAll('.api-option').length} 个`)
|
|
566
577
|
const statCards = [...apiDoc.querySelectorAll('#stats .api-stat')]
|