gis-view-webgl2 0.1.1 → 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 +13 -1
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +255 -3
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +65 -0
- package/docs/GisView.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 +58 -0
- package/docs/demos/ChannelDemo.vue +566 -6
- package/docs/demos/CopyMergeDemo.vue +57 -6
- package/docs/demos/LabelDemo.vue +67 -11
- package/docs/demos/api-index.js +13 -3
- 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 +4 -2
- package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +5 -4
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +7 -2
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +21 -5
- 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 +100 -0
- package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +38 -0
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +5 -5
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +58 -8
- 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 +27 -2
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +45 -1
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +7 -1
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +8 -0
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +28 -0
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +3 -0
- 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 +1 -1
- package/docs/steps/README.md +8 -6
- package/docs/steps/selftest-demos.html +194 -29
- package/docs/steps/selftest-docs.html +7 -6
- package/docs/steps/selftest-features.html +213 -1
- package/docs/steps/selftest-labels.html +249 -4
- package/docs//346/216/245/345/217/243/345/215/217/350/256/256.md +17 -11
- package/package.json +2 -2
|
@@ -33,11 +33,16 @@ const channelInset = ref(0.3)
|
|
|
33
33
|
const offsetMode = ref('tangent')
|
|
34
34
|
/** 通道选中模式:'line' 只选点中的回线 / 'group' 连所在分组 / 'channel' 连整条通道 */
|
|
35
35
|
const channelSelectMode = ref('line')
|
|
36
|
+
/** 分组拐点拖动模式:'parallel' 共用骨架 / 'vertex' 只移同组同序号拐点 */
|
|
37
|
+
const channelWaypointDragMode = ref('parallel')
|
|
38
|
+
const waypointDragText = ref('分组拐点拖动:先点「准备两分组拐点」,再拖中间拐点比较两种模式')
|
|
36
39
|
/** 待确认的删除请求('remove' 事件的 pending 载荷):确认框照着它渲染 */
|
|
37
40
|
const removeRequest = ref(null)
|
|
38
41
|
const removeText = ref('删除:按 Delete 或点上面的按钮,先出载荷、确认之后才动数据')
|
|
39
42
|
const channelsText = ref('—')
|
|
40
43
|
const groupsText = ref('—')
|
|
44
|
+
const visibilityText = ref('隐藏 / 显示:—')
|
|
45
|
+
const changedText = ref('最近变化:—')
|
|
41
46
|
const exportText = ref('')
|
|
42
47
|
const logLines = ref([])
|
|
43
48
|
const backendMode = ref('success')
|
|
@@ -223,6 +228,7 @@ const handleWaypointBackend = (payload) => {
|
|
|
223
228
|
const groupId = payload.groupId ?? payload.group_id
|
|
224
229
|
const group = view.getChannelGroupWaypoints(payload.channelId, groupId)
|
|
225
230
|
const path = pathJson(group?.path)
|
|
231
|
+
const waypointOffsets = { ...(group?.waypointOffsets ?? {}) }
|
|
226
232
|
if (!group) {
|
|
227
233
|
waypointSyncText.value = `${phaseLabel}:分组 ${String(groupId ?? '—')} 的整份骨架读不出来,请求没有发出`
|
|
228
234
|
return
|
|
@@ -236,6 +242,7 @@ const handleWaypointBackend = (payload) => {
|
|
|
236
242
|
channelId: payload.channelId,
|
|
237
243
|
groupId,
|
|
238
244
|
path,
|
|
245
|
+
waypointOffsets,
|
|
239
246
|
phase: payload.phase,
|
|
240
247
|
scope: group.scope ?? 'group'
|
|
241
248
|
}
|
|
@@ -299,6 +306,8 @@ const buildOptions = () => ({
|
|
|
299
306
|
features: { pan: true, zoom: true, hover: true, select: true, contextMenu: true, keyboard: true, drag: true, channelMenu: true, remove: true },
|
|
300
307
|
// 通道选中模式:点一条回线选它自己 / 连所在分组 / 连整条通道
|
|
301
308
|
channelSelectMode: channelSelectMode.value,
|
|
309
|
+
// 分组拐点拖动:共用骨架 / 只移同组同序号拐点
|
|
310
|
+
channelWaypointDragMode: channelWaypointDragMode.value,
|
|
302
311
|
// 删除配置:confirm 缺省(null)表示组件不弹界面、停在待确认 —— 这一页订阅 'remove' 事件自己画确认框
|
|
303
312
|
remove: { scope: 'lines' },
|
|
304
313
|
// 交互框里的「加拐点 / 删拐点」动的是分组骨架,要有这一项才不是灰的(Ctrl + 点击加点、双击删点也一起开了)
|
|
@@ -318,8 +327,12 @@ const buildOptions = () => ({
|
|
|
318
327
|
const channel = view?.getLineChannel(entry.index)
|
|
319
328
|
groupsText.value = channel
|
|
320
329
|
? `线路 ${entry.item?.id ?? entry.index} → ${channel.channelId} / 分组 ${channel.groupId}(共 ${channel.group?.lineIds?.length ?? 0} 回)`
|
|
321
|
-
|
|
330
|
+
: `线路 ${entry.item?.id ?? entry.index} 不在通道里`
|
|
322
331
|
}
|
|
332
|
+
},
|
|
333
|
+
onDataChange: () => {
|
|
334
|
+
syncReadouts()
|
|
335
|
+
syncChangedChannels()
|
|
323
336
|
}
|
|
324
337
|
})
|
|
325
338
|
|
|
@@ -346,6 +359,37 @@ const syncReadouts = () => {
|
|
|
346
359
|
groupsText.value = firstChannelId === undefined ? '—' : describeGroups(firstChannelId)
|
|
347
360
|
// 面板上改过分组之后,开着的右键框也要跟着对上(对不上就收掉)
|
|
348
361
|
syncMenu()
|
|
362
|
+
syncChangedChannels()
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** 最近变化通道:分组 / 成员 / 拐点 / 端点 / 可见性 / 其余声明字段 */
|
|
366
|
+
const syncChangedChannels = () => {
|
|
367
|
+
const labels = { groups: '分组', members: '成员', waypoints: '拐点', endpoints: '端点', visibility: '可见性', fields: '字段' }
|
|
368
|
+
const changed = view?.getChangedChannels() ?? []
|
|
369
|
+
changedText.value = changed.length
|
|
370
|
+
? `最近变化 ${changed.length} 条:${changed.slice(0, 4).map(item => `${String(item.id)}(${item.reasons.map(reason => labels[reason] ?? reason).join(' / ')})`).join(' · ')}`
|
|
371
|
+
: '最近变化:没有(改分组 / 拖拐点 / 隐藏后再看)'
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const setFirstFeatureVisible = (kind, visible) => {
|
|
375
|
+
const options = view?.getOptions()
|
|
376
|
+
const item = kind === 'point' ? options?.points?.[0] : kind === 'line' ? options?.lines?.[0] : options?.channels?.[0]
|
|
377
|
+
const ref = item?.id ?? (item ? 0 : null)
|
|
378
|
+
const kindLabel = kind === 'point' ? '节点' : kind === 'line' ? '线路' : '通道'
|
|
379
|
+
if (!view || ref === null) return
|
|
380
|
+
const outcome = visible ? view.showFeatures(kind, ref) : view.hideFeatures(kind, ref)
|
|
381
|
+
visibilityText.value = outcome.ok
|
|
382
|
+
? `${visible ? '显示' : '隐藏'}${kindLabel} ${String(ref)}:${outcome.indexes.length} 条${outcome.skipped.length ? ` · 跳过 ${outcome.skipped.length}` : ''}`
|
|
383
|
+
: `${visible ? '显示' : '隐藏'}${kindLabel}失败:${outcome.reason}`
|
|
384
|
+
pushLog(visible ? 'showFeatures' : 'hideFeatures', visibilityText.value)
|
|
385
|
+
syncReadouts()
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const clearChannelChanges = () => {
|
|
389
|
+
view?.clearChangedChannels()
|
|
390
|
+
syncChangedChannels()
|
|
391
|
+
visibilityText.value = '通道对账:已把当前声明记成新基准'
|
|
392
|
+
pushLog('clearChangedChannels', visibilityText.value)
|
|
349
393
|
}
|
|
350
394
|
|
|
351
395
|
const describeGroups = (channelId) => {
|
|
@@ -460,6 +504,127 @@ const splitPickedIntoGroup = () => {
|
|
|
460
504
|
syncReadouts()
|
|
461
505
|
}
|
|
462
506
|
|
|
507
|
+
/**
|
|
508
|
+
* 线路分组弹框:参考业务页的「分组卡片 + 行内移入目标」。
|
|
509
|
+
* 行内「确认移入」只改本地草稿,底部「确定分组」才调用一次 splitChannelGroups:
|
|
510
|
+
* 一次把整份 groups 声明换掉,避免每挪一条回线都发一次请求 / 记一步历史。
|
|
511
|
+
*/
|
|
512
|
+
const groupDialog = ref(null)
|
|
513
|
+
const groupDialogTargets = ref({})
|
|
514
|
+
const groupDialogText = ref('线路分组:改完所有移动后,点「确定分组」一次提交')
|
|
515
|
+
const sameGroupId = (left, right) => String(left) === String(right)
|
|
516
|
+
|
|
517
|
+
const dialogLineRowOf = (lineId) => {
|
|
518
|
+
const line = view?.getLine(lineId)
|
|
519
|
+
const from = line?.from === undefined || line?.from === null ? null : view?.getPoint(line.from)
|
|
520
|
+
const to = line?.to === undefined || line?.to === null ? null : view?.getPoint(line.to)
|
|
521
|
+
const endpointName = from?.name && to?.name ? `${from.name}_${to.name}` : String(lineId)
|
|
522
|
+
return {
|
|
523
|
+
id: lineId,
|
|
524
|
+
label: line?.name || endpointName,
|
|
525
|
+
meta: line ? [line.from, line.to].filter(id => id !== undefined && id !== null).map(String).join(' → ') : String(lineId)
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** 弹框副标题沿用截图里的「场站对标标识」:优先用通道两端,读不到再退回第一条回线两端 */
|
|
530
|
+
const dialogStationKeyOf = (channelId) => {
|
|
531
|
+
const declared = view?.getOptions().channels?.find(channel => sameGroupId(channel.id, channelId))
|
|
532
|
+
const ends = [declared?.from, declared?.to].filter(id => id !== undefined && id !== null)
|
|
533
|
+
if (ends.length) {
|
|
534
|
+
return ends.map(String).join('|')
|
|
535
|
+
}
|
|
536
|
+
const firstLineId = view?.getChannelGroups(channelId)?.flatMap(group => group.lineIds ?? [])[0]
|
|
537
|
+
const line = firstLineId === undefined ? null : view?.getLine(firstLineId)
|
|
538
|
+
const lineEnds = [line?.from, line?.to].filter(id => id !== undefined && id !== null)
|
|
539
|
+
return lineEnds.length ? lineEnds.map(String).join('|') : String(channelId)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const groupDialogGroups = computed(() => (groupDialog.value?.groups ?? []).map((group, index) => ({
|
|
543
|
+
...group,
|
|
544
|
+
index,
|
|
545
|
+
rows: group.lineIds.map(dialogLineRowOf)
|
|
546
|
+
})))
|
|
547
|
+
|
|
548
|
+
const groupDialogTargetsFor = sourceIndex => groupDialogGroups.value.filter(group => group.index !== sourceIndex)
|
|
549
|
+
|
|
550
|
+
const openGroupDialog = (channelId, hitLineId) => {
|
|
551
|
+
const wanted = channelId ?? channelMenu.value?.channelId ?? firstChannelGroups()?.channelId
|
|
552
|
+
if (!view || wanted === undefined || wanted === null) {
|
|
553
|
+
pushLog('线路分组:当前没有可编辑的通道')
|
|
554
|
+
return
|
|
555
|
+
}
|
|
556
|
+
const groups = view.getChannelGroups(wanted)
|
|
557
|
+
if (!groups.length) {
|
|
558
|
+
pushLog('线路分组:通道 ' + wanted + ' 暂时读不到分组')
|
|
559
|
+
return
|
|
560
|
+
}
|
|
561
|
+
groupDialog.value = {
|
|
562
|
+
channelId: wanted,
|
|
563
|
+
stationKey: dialogStationKeyOf(wanted),
|
|
564
|
+
hitLineId: hitLineId ?? null,
|
|
565
|
+
groups: groups.map(group => ({ id: group.id, lineIds: [...(group.lineIds ?? [])] })),
|
|
566
|
+
moves: 0
|
|
567
|
+
}
|
|
568
|
+
groupDialogTargets.value = {}
|
|
569
|
+
groupDialogText.value = String(wanted) + ':' + groups.length + ' 组 / ' + groups.reduce((sum, group) => sum + group.lineIds.length, 0) + ' 回线。可以连续移动,确定时一次提交。'
|
|
570
|
+
if (channelMenu.value) {
|
|
571
|
+
closeChannelMenu()
|
|
572
|
+
}
|
|
573
|
+
pushLog('线路分组:打开 ' + wanted + ' 的整组编辑弹框')
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const moveGroupDialogLine = (lineId) => {
|
|
577
|
+
const draft = groupDialog.value
|
|
578
|
+
if (!draft) {
|
|
579
|
+
return
|
|
580
|
+
}
|
|
581
|
+
const rawTarget = groupDialogTargets.value[String(lineId)]
|
|
582
|
+
const sourceIndex = draft.groups.findIndex(group => group.lineIds.some(id => sameGroupId(id, lineId)))
|
|
583
|
+
const targetIndex = draft.groups.findIndex(group => sameGroupId(group.id, rawTarget))
|
|
584
|
+
if (sourceIndex < 0) {
|
|
585
|
+
groupDialogText.value = '这条回线已经不在草稿里,重新打开弹框再试'
|
|
586
|
+
return
|
|
587
|
+
}
|
|
588
|
+
if (targetIndex < 0) {
|
|
589
|
+
groupDialogText.value = '先给这条回线选一个目标分组'
|
|
590
|
+
return
|
|
591
|
+
}
|
|
592
|
+
const sourceLabel = '分组 ' + (sourceIndex + 1)
|
|
593
|
+
const targetLabel = '分组 ' + (targetIndex + 1)
|
|
594
|
+
const nextGroups = draft.groups.map(group => ({ id: group.id, lineIds: [...group.lineIds] }))
|
|
595
|
+
nextGroups[sourceIndex].lineIds = nextGroups[sourceIndex].lineIds.filter(id => !sameGroupId(id, lineId))
|
|
596
|
+
nextGroups[targetIndex].lineIds.push(lineId)
|
|
597
|
+
draft.groups = nextGroups.filter(group => group.lineIds.length)
|
|
598
|
+
draft.moves += 1
|
|
599
|
+
delete groupDialogTargets.value[String(lineId)]
|
|
600
|
+
groupDialogText.value = '草稿第 ' + draft.moves + ' 处:' + dialogLineRowOf(lineId).label + ' ' + sourceLabel + ' → ' + targetLabel + '。数据还没动,点「确定分组」一次提交。'
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const closeGroupDialog = () => {
|
|
604
|
+
groupDialog.value = null
|
|
605
|
+
groupDialogTargets.value = {}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const confirmGroupDialog = () => {
|
|
609
|
+
const draft = groupDialog.value
|
|
610
|
+
if (!view || !draft) {
|
|
611
|
+
return
|
|
612
|
+
}
|
|
613
|
+
if (!draft.moves) {
|
|
614
|
+
groupDialogText.value = '还没有移动任何回线;可以继续编辑,或点「取消」退出'
|
|
615
|
+
return
|
|
616
|
+
}
|
|
617
|
+
const groups = draft.groups
|
|
618
|
+
.filter(group => group.lineIds.length)
|
|
619
|
+
.map(group => ({ id: group.id, lineIds: [...group.lineIds] }))
|
|
620
|
+
view.splitChannelGroups(draft.channelId, groups)
|
|
621
|
+
const lineCount = groups.reduce((sum, group) => sum + group.lineIds.length, 0)
|
|
622
|
+
pushLog('splitChannelGroups:' + draft.channelId + ' 一次提交 ' + draft.moves + ' 处移动(' + groups.length + ' 组 / ' + lineCount + ' 回线)')
|
|
623
|
+
groupDialogText.value = '已一次提交:' + groups.length + ' 组 / ' + lineCount + ' 回线'
|
|
624
|
+
closeGroupDialog()
|
|
625
|
+
syncReadouts()
|
|
626
|
+
}
|
|
627
|
+
|
|
463
628
|
/**
|
|
464
629
|
* 右键交互框:右键一条通道回线时 onChannelContextMenu(payload) 把「这条通道几组 / 当前是哪一组 /
|
|
465
630
|
* 这一组能做哪些交互」一次交出来,界面照着 groups 与 actions 渲染就行 —— 核心是四件事:
|
|
@@ -593,6 +758,78 @@ const applyChannelSelectMode = () => {
|
|
|
593
758
|
pushLog('通道选中模式 → ' + channelSelectMode.value + '(' + labels[channelSelectMode.value] + ')')
|
|
594
759
|
}
|
|
595
760
|
|
|
761
|
+
/** 分组拐点拖动模式:只影响下一次拖动,updateOptions 不重排场景 */
|
|
762
|
+
const applyChannelWaypointDragMode = () => {
|
|
763
|
+
view?.updateOptions({ channelWaypointDragMode: channelWaypointDragMode.value })
|
|
764
|
+
const labels = {
|
|
765
|
+
parallel: '共用骨架 + 平行偏移(和现有逻辑一样)',
|
|
766
|
+
vertex: '只移动同组同序号拐点,其他拐点不动'
|
|
767
|
+
}
|
|
768
|
+
waypointDragText.value = '分组拐点拖动:' + labels[channelWaypointDragMode.value]
|
|
769
|
+
pushLog('分组拐点拖动 → ' + channelWaypointDragMode.value + ':' + labels[channelWaypointDragMode.value])
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* 把第一条通道拆成两组,并给每组加两个错开的拐点:
|
|
774
|
+
* 拖动中间拐点时,parallel 模式会重算整条平行骨架;vertex 模式只平移四根线上同一个序号的顶点。
|
|
775
|
+
*/
|
|
776
|
+
const prepareWaypointDragDemo = () => {
|
|
777
|
+
if (!view) {
|
|
778
|
+
return
|
|
779
|
+
}
|
|
780
|
+
const channel = view.getChannels()[0]
|
|
781
|
+
if (!channel) {
|
|
782
|
+
return
|
|
783
|
+
}
|
|
784
|
+
const channelId = channel.channelId
|
|
785
|
+
let groups = view.getChannelGroups(channelId)
|
|
786
|
+
const lineIds = groups.flatMap(group => group.lineIds)
|
|
787
|
+
if (lineIds.length < 2) {
|
|
788
|
+
waypointDragText.value = '这条通道只有一回线,拆不出两组可比较的分组拐点'
|
|
789
|
+
return
|
|
790
|
+
}
|
|
791
|
+
const splitAt = Math.ceil(lineIds.length / 2)
|
|
792
|
+
view.splitChannelGroups(channelId, [lineIds.slice(0, splitAt), lineIds.slice(splitAt)])
|
|
793
|
+
groups = view.getChannelGroups(channelId)
|
|
794
|
+
// 先把旧拐点清干净,重复点按钮也能得到同一份演示数据
|
|
795
|
+
for (const group of groups) {
|
|
796
|
+
let live = view.getChannelGroups(channelId).find(item => String(item.id) === String(group.id))
|
|
797
|
+
let guard = 0
|
|
798
|
+
while (live && live.skeleton.length > 2 && guard < 64) {
|
|
799
|
+
view.removeChannelGroupWaypoint(channelId, group.id, live.skeleton.length - 2)
|
|
800
|
+
live = view.getChannelGroups(channelId).find(item => String(item.id) === String(group.id))
|
|
801
|
+
guard += 1
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
groups = view.getChannelGroups(channelId)
|
|
805
|
+
groups.forEach((group, groupIndex) => {
|
|
806
|
+
const start = group.skeleton[0]
|
|
807
|
+
const end = group.skeleton[group.skeleton.length - 1]
|
|
808
|
+
if (!start || !end) {
|
|
809
|
+
return
|
|
810
|
+
}
|
|
811
|
+
const dx = end[0] - start[0]
|
|
812
|
+
const dy = end[1] - start[1]
|
|
813
|
+
const length = Math.hypot(dx, dy)
|
|
814
|
+
if (length < 1e-9) {
|
|
815
|
+
return
|
|
816
|
+
}
|
|
817
|
+
const normal = [-dy / length, dx / length]
|
|
818
|
+
const side = groupIndex === 0 ? 1 : -1
|
|
819
|
+
const offset = Math.min(Math.max(length * 0.06, 0.05), 0.16) * side
|
|
820
|
+
for (const ratio of [0.33, 0.66]) {
|
|
821
|
+
view.addChannelGroupWaypoint(channelId, group.id, [
|
|
822
|
+
start[0] + dx * ratio + normal[0] * offset,
|
|
823
|
+
start[1] + dy * ratio + normal[1] * offset
|
|
824
|
+
])
|
|
825
|
+
}
|
|
826
|
+
})
|
|
827
|
+
view.updateOptions({ channelSelectMode: channelSelectMode.value, channelWaypointDragMode: channelWaypointDragMode.value })
|
|
828
|
+
syncReadouts()
|
|
829
|
+
waypointDragText.value = `已准备 ${groups.length} 组 / 每组 2 个拐点:拖动中间拐点比较「共用骨架」和「只移同序号拐点」`
|
|
830
|
+
pushLog('分组拐点演示就绪:' + groups.map(group => group.id + '(' + group.lineIds.length + ' 回)').join(' / '))
|
|
831
|
+
}
|
|
832
|
+
|
|
596
833
|
/** 删除载荷的中文读数:要删什么、连带什么 */
|
|
597
834
|
const describeRemove = (plan) => {
|
|
598
835
|
const counts = plan.counts ?? {}
|
|
@@ -655,7 +892,14 @@ const cancelRemove = () => {
|
|
|
655
892
|
|
|
656
893
|
/** Esc 收框:框是演示自己画的,组件只负责把载荷交出来 */
|
|
657
894
|
const onWindowKeyDown = (event) => {
|
|
658
|
-
if (event.key
|
|
895
|
+
if (event.key !== 'Escape') {
|
|
896
|
+
return
|
|
897
|
+
}
|
|
898
|
+
if (groupDialog.value) {
|
|
899
|
+
closeGroupDialog()
|
|
900
|
+
return
|
|
901
|
+
}
|
|
902
|
+
if (channelMenu.value) {
|
|
659
903
|
closeChannelMenu()
|
|
660
904
|
}
|
|
661
905
|
}
|
|
@@ -760,6 +1004,9 @@ onBeforeUnmount(() => {
|
|
|
760
1004
|
<span>{{ group.lineCount }} 回{{ group.waypointCount ? ' / ' + group.waypointCount + ' 拐点' : '' }}</span>
|
|
761
1005
|
</li>
|
|
762
1006
|
</ul>
|
|
1007
|
+
<button type="button" class="dv-channel-menu__open-dialog" data-demo="channel-group-dialog-open-menu" @click="openGroupDialog(channelMenu.channelId, channelMenu.lineId)">
|
|
1008
|
+
线路分组…
|
|
1009
|
+
</button>
|
|
763
1010
|
<div v-if="menuLineRows.length" class="dv-channel-menu__lines">
|
|
764
1011
|
<p class="dv-channel-menu__subtitle">线路分配 · 每条回线都能拆成新分组或并进别的分组</p>
|
|
765
1012
|
<ul>
|
|
@@ -788,6 +1035,65 @@ onBeforeUnmount(() => {
|
|
|
788
1035
|
命中回线 {{ channelMenu.lineId }} · 这些都在 onChannelContextMenu(payload) 里;点空白处 / Esc / × 收框
|
|
789
1036
|
</p>
|
|
790
1037
|
</div>
|
|
1038
|
+
<div v-if="groupDialog" class="dv-group-dialog" data-demo="channel-group-dialog" @click.self="closeGroupDialog">
|
|
1039
|
+
<section class="dv-group-dialog__panel" role="dialog" aria-modal="true" aria-labelledby="dv-channel-group-title">
|
|
1040
|
+
<header class="dv-group-dialog__head">
|
|
1041
|
+
<div>
|
|
1042
|
+
<h3 id="dv-channel-group-title">线路分组</h3>
|
|
1043
|
+
<p>场站对标标识:{{ groupDialog.stationKey }}</p>
|
|
1044
|
+
</div>
|
|
1045
|
+
<button type="button" title="取消并关闭(Esc)" @click="closeGroupDialog">×</button>
|
|
1046
|
+
</header>
|
|
1047
|
+
<div class="dv-group-dialog__body">
|
|
1048
|
+
<section
|
|
1049
|
+
v-for="group in groupDialogGroups"
|
|
1050
|
+
:key="String(group.id)"
|
|
1051
|
+
class="dv-group-dialog__card"
|
|
1052
|
+
data-demo="channel-group-dialog-card"
|
|
1053
|
+
:data-group-id="String(group.id)"
|
|
1054
|
+
>
|
|
1055
|
+
<header>
|
|
1056
|
+
<strong>分组 {{ group.index + 1 }}</strong>
|
|
1057
|
+
<span>{{ group.lineIds.length }} 条线路</span>
|
|
1058
|
+
<code>{{ group.id }}</code>
|
|
1059
|
+
</header>
|
|
1060
|
+
<ul>
|
|
1061
|
+
<li
|
|
1062
|
+
v-for="row in group.rows"
|
|
1063
|
+
:key="String(row.id)"
|
|
1064
|
+
class="dv-group-dialog__line"
|
|
1065
|
+
data-demo="channel-group-dialog-line"
|
|
1066
|
+
:data-line-id="String(row.id)"
|
|
1067
|
+
:data-group-id="String(group.id)"
|
|
1068
|
+
>
|
|
1069
|
+
<div class="dv-group-dialog__line-text">
|
|
1070
|
+
<strong>{{ row.label }}</strong>
|
|
1071
|
+
<span>{{ row.meta }}</span>
|
|
1072
|
+
</div>
|
|
1073
|
+
<select v-model="groupDialogTargets[String(row.id)]" data-demo="channel-group-dialog-target">
|
|
1074
|
+
<option value="">移入其他组</option>
|
|
1075
|
+
<option v-for="target in groupDialogTargetsFor(group.index)" :key="String(target.id)" :value="String(target.id)">
|
|
1076
|
+
分组 {{ target.index + 1 }}({{ target.lineIds.length }} 条)
|
|
1077
|
+
</option>
|
|
1078
|
+
</select>
|
|
1079
|
+
<button type="button" :disabled="!groupDialogTargets[String(row.id)]" data-demo="channel-group-dialog-move" @click="moveGroupDialogLine(row.id)">
|
|
1080
|
+
确认移入
|
|
1081
|
+
</button>
|
|
1082
|
+
</li>
|
|
1083
|
+
</ul>
|
|
1084
|
+
</section>
|
|
1085
|
+
</div>
|
|
1086
|
+
<footer class="dv-group-dialog__footer">
|
|
1087
|
+
<p data-demo="channel-group-dialog-text">{{ groupDialogText }}</p>
|
|
1088
|
+
<div>
|
|
1089
|
+
<button type="button" data-demo="channel-group-dialog-cancel" @click="closeGroupDialog">取消</button>
|
|
1090
|
+
<button type="button" class="dv-button--primary" data-demo="channel-group-dialog-confirm" :disabled="!groupDialog.moves" @click="confirmGroupDialog">
|
|
1091
|
+
确定分组
|
|
1092
|
+
</button>
|
|
1093
|
+
</div>
|
|
1094
|
+
</footer>
|
|
1095
|
+
</section>
|
|
1096
|
+
</div>
|
|
791
1097
|
<div v-if="removeRequest" class="dv-remove-box">
|
|
792
1098
|
<div class="dv-channel-menu__head">
|
|
793
1099
|
<strong>确认删除</strong>
|
|
@@ -848,6 +1154,7 @@ onBeforeUnmount(() => {
|
|
|
848
1154
|
<div class="dv-row">
|
|
849
1155
|
<button type="button" class="dv-button" @click="splitFirstChannel">第一条通道:每回线拆一组</button>
|
|
850
1156
|
<button type="button" class="dv-button" @click="mergeFirstChannel">并回一组</button>
|
|
1157
|
+
<button type="button" class="dv-button" data-demo="channel-group-dialog-open" @click="openGroupDialog()">线路分组…</button>
|
|
851
1158
|
</div>
|
|
852
1159
|
<p class="dv-readout">{{ channelsText }}</p>
|
|
853
1160
|
<div class="dv-row dv-split" data-demo="add-channel-group-picks">
|
|
@@ -872,14 +1179,14 @@ onBeforeUnmount(() => {
|
|
|
872
1179
|
<p class="dv-hint">勾几条回线点「勾上的拆成一组」= addChannelGroup(channelId, lineIds, { id?, path? }):一次把这几条挪进单独一组(数据里没写 groups 的通道也是这么拆,被挪空的那一组整组消失);「新这一组的拐点」两项演示 options.path(照抄来源组当前的骨架 / 写 [] 直连),组 id 留空按 通道 id::N 排。一条一条挪走 moveChannelLine,整份重排走 splitChannelGroups。</p>
|
|
873
1180
|
<p class="dv-readout">{{ groupsText }}</p>
|
|
874
1181
|
<p class="dv-hint">选中一条通道回线也会刷新读数:onSelect 载荷里的 items 带上 index,再用 getLineChannel(index) 问出它属于哪条通道、哪一组。</p>
|
|
875
|
-
<p class="dv-hint">右键一条通道回线出交互框:点分组就是 setCurrentChannelGroup(顺带选中它的回线),每条回线可以拆成新分组 / 并进别的分组,下面一排按钮交给 runChannelAction
|
|
1182
|
+
<p class="dv-hint">右键一条通道回线出交互框:点分组就是 setCurrentChannelGroup(顺带选中它的回线),每条回线可以拆成新分组 / 并进别的分组,下面一排按钮交给 runChannelAction。「线路分组…」弹框把多次移入攒成一份草稿,确定时只调用一次 splitChannelGroups 换整份分组声明。</p>
|
|
876
1183
|
</div>
|
|
877
1184
|
|
|
878
1185
|
<div class="dv-group">
|
|
879
1186
|
<h3>通道选中模式<span class="dv-chip">{{ channelSelectMode }}</span></h3>
|
|
880
1187
|
<div class="dv-row">
|
|
881
1188
|
<label>点一条回线
|
|
882
|
-
<select v-model="channelSelectMode" @change="applyChannelSelectMode">
|
|
1189
|
+
<select v-model="channelSelectMode" data-demo="channel-select-mode" @change="applyChannelSelectMode">
|
|
883
1190
|
<option value="line">line(只选它自己)</option>
|
|
884
1191
|
<option value="group">group(连所在分组)</option>
|
|
885
1192
|
<option value="channel">channel(连整条通道)</option>
|
|
@@ -888,7 +1195,49 @@ onBeforeUnmount(() => {
|
|
|
888
1195
|
</div>
|
|
889
1196
|
<p class="dv-hint">
|
|
890
1197
|
channelSelectMode 只决定下一次点选怎么摊开:line 单条、group 连这一组、channel 整条通道;
|
|
891
|
-
在 group / channel 下 Ctrl +
|
|
1198
|
+
在 group / channel 下 Ctrl + 点击是整批取反,Ctrl + 点击线路加点也按这一档选中并高亮。
|
|
1199
|
+
切换走 updateOptions,场景不用重排。
|
|
1200
|
+
</p>
|
|
1201
|
+
</div>
|
|
1202
|
+
|
|
1203
|
+
<div class="dv-group">
|
|
1204
|
+
<h3>分组拐点拖动<span class="dv-chip">{{ channelWaypointDragMode }}</span></h3>
|
|
1205
|
+
<div class="dv-row">
|
|
1206
|
+
<label>拖动模式
|
|
1207
|
+
<select v-model="channelWaypointDragMode" data-demo="channel-waypoint-drag-mode" @change="applyChannelWaypointDragMode">
|
|
1208
|
+
<option value="parallel">parallel(共用骨架,和现有逻辑一样)</option>
|
|
1209
|
+
<option value="vertex">vertex(只移同组同序号拐点)</option>
|
|
1210
|
+
</select>
|
|
1211
|
+
</label>
|
|
1212
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="channel-waypoint-drag-prepare" @click="prepareWaypointDragDemo">准备两分组拐点</button>
|
|
1213
|
+
</div>
|
|
1214
|
+
<p class="dv-readout" data-demo="channel-waypoint-drag-text">{{ waypointDragText }}</p>
|
|
1215
|
+
<p class="dv-hint">
|
|
1216
|
+
channelWaypointDragMode 只影响下一次拖动:parallel 改共用骨架,组内回线按平行偏移整体重算;vertex 只把同组同一
|
|
1217
|
+
pathIndex 的拐点按相同世界增量平移,相邻拐点与其它分组不动。vertex 的结果写进 groups[].waypointOffsets,
|
|
1218
|
+
键是骨架顶点序号(只认中间拐点),格式 { "1": [dx, dy] };不写或空对象就是普通平行骨架。
|
|
1219
|
+
</p>
|
|
1220
|
+
</div>
|
|
1221
|
+
|
|
1222
|
+
<div class="dv-group">
|
|
1223
|
+
<h3>隐藏 / 显示与最近变化</h3>
|
|
1224
|
+
<div class="dv-row">
|
|
1225
|
+
<button type="button" class="dv-button" data-demo="point-hide" @click="setFirstFeatureVisible('point', false)">隐藏第一个节点</button>
|
|
1226
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="point-show" @click="setFirstFeatureVisible('point', true)">显示第一个节点</button>
|
|
1227
|
+
<button type="button" class="dv-button" data-demo="line-hide" @click="setFirstFeatureVisible('line', false)">隐藏第一条线路</button>
|
|
1228
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="line-show" @click="setFirstFeatureVisible('line', true)">显示第一条线路</button>
|
|
1229
|
+
</div>
|
|
1230
|
+
<div class="dv-row">
|
|
1231
|
+
<button type="button" class="dv-button" data-demo="channel-hide" @click="setFirstFeatureVisible('channel', false)">隐藏第一条通道</button>
|
|
1232
|
+
<button type="button" class="dv-button dv-button--primary" data-demo="channel-show" @click="setFirstFeatureVisible('channel', true)">显示第一条通道</button>
|
|
1233
|
+
<button type="button" class="dv-button" data-demo="channel-clear-changed" @click="clearChannelChanges">通道对账</button>
|
|
1234
|
+
</div>
|
|
1235
|
+
<p class="dv-readout" data-demo="channel-visibility">{{ visibilityText }}</p>
|
|
1236
|
+
<p class="dv-readout" data-demo="channel-changed">{{ changedText }}</p>
|
|
1237
|
+
<p class="dv-hint">
|
|
1238
|
+
节点 / 线路 / 通道都能用 hideFeatures / showFeatures 收起再恢复,只改 visible,数据本身不删;通道收起时成员回线、分组、拐点与编辑状态都保留;
|
|
1239
|
+
getChangedChannels 会把分组、成员、拐点、端点、可见性与其余字段的变化逐条列出来,
|
|
1240
|
+
clearChangedChannels 把当前声明记成新的对账基准。
|
|
892
1241
|
</p>
|
|
893
1242
|
</div>
|
|
894
1243
|
|
|
@@ -928,7 +1277,7 @@ onBeforeUnmount(() => {
|
|
|
928
1277
|
<p class="dv-readout" data-demo="channel-waypoint-status">{{ waypointSyncText }}</p>
|
|
929
1278
|
<p class="dv-readout" data-demo="channel-backend-readout">{{ backendText }}</p>
|
|
930
1279
|
<p class="dv-hint">
|
|
931
|
-
{{ backendModeHint }}。请求头带 `Idempotency-Key` / `If-Match`,body 带 baseVersion 与 opId;拐点只接 add / remove / end,start / move 的中间态一条都不发;拖动结束读整份 path 覆盖。
|
|
1280
|
+
{{ backendModeHint }}。请求头带 `Idempotency-Key` / `If-Match`,body 带 baseVersion 与 opId;拐点只接 add / remove / end,start / move 的中间态一条都不发;拖动结束读整份 path 与 waypointOffsets 覆盖。
|
|
932
1281
|
通道里的拐点走 `PATCH /gis/channels/{id}/groups/waypoints`,独立线路走 `PATCH /gis/lines/{id}/path`;
|
|
933
1282
|
分组变化走 `PATCH /gis/channels/{id}/groups`,body 是整份 groups 声明,不按 delta 猜被挪空的组。
|
|
934
1283
|
成功响应统一为 `{ code, message, requestId, traceId, data: { operation, version, idempotent, transactionId, result } }`,
|
|
@@ -1103,6 +1452,217 @@ onBeforeUnmount(() => {
|
|
|
1103
1452
|
line-height: 1.6;
|
|
1104
1453
|
}
|
|
1105
1454
|
|
|
1455
|
+
.dv-channel-menu__open-dialog {
|
|
1456
|
+
width: 100%;
|
|
1457
|
+
margin-top: 6px;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/* 线路分组弹框:草稿式整组编辑,底部确定才调用一次 splitChannelGroups */
|
|
1461
|
+
.dv-group-dialog {
|
|
1462
|
+
position: absolute;
|
|
1463
|
+
z-index: 12;
|
|
1464
|
+
inset: 0;
|
|
1465
|
+
display: flex;
|
|
1466
|
+
align-items: center;
|
|
1467
|
+
justify-content: center;
|
|
1468
|
+
padding: 20px;
|
|
1469
|
+
background: rgba(18, 34, 56, 0.28);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
.dv-group-dialog__panel {
|
|
1473
|
+
display: flex;
|
|
1474
|
+
flex-direction: column;
|
|
1475
|
+
width: min(760px, 100%);
|
|
1476
|
+
max-height: 100%;
|
|
1477
|
+
overflow: hidden;
|
|
1478
|
+
border: 1px solid #dce5f0;
|
|
1479
|
+
border-radius: 14px;
|
|
1480
|
+
background: #fff;
|
|
1481
|
+
box-shadow: 0 24px 64px rgba(16, 39, 70, 0.28);
|
|
1482
|
+
color: #314964;
|
|
1483
|
+
font-size: 12px;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
.dv-group-dialog__head {
|
|
1487
|
+
display: flex;
|
|
1488
|
+
align-items: flex-start;
|
|
1489
|
+
justify-content: space-between;
|
|
1490
|
+
gap: 12px;
|
|
1491
|
+
padding: 17px 20px 13px;
|
|
1492
|
+
border-bottom: 1px solid #edf1f6;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
.dv-group-dialog__head h3 {
|
|
1496
|
+
margin: 0;
|
|
1497
|
+
color: #17233d;
|
|
1498
|
+
font-size: 17px;
|
|
1499
|
+
font-weight: 650;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
.dv-group-dialog__head p {
|
|
1503
|
+
margin: 5px 0 0;
|
|
1504
|
+
color: #7f8fa5;
|
|
1505
|
+
font-size: 11px;
|
|
1506
|
+
word-break: break-all;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
.dv-group-dialog__head > button {
|
|
1510
|
+
padding: 0 4px;
|
|
1511
|
+
border: 0;
|
|
1512
|
+
background: transparent;
|
|
1513
|
+
color: #7d8ca1;
|
|
1514
|
+
font-size: 22px;
|
|
1515
|
+
line-height: 1;
|
|
1516
|
+
cursor: pointer;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.dv-group-dialog__body {
|
|
1520
|
+
display: flex;
|
|
1521
|
+
flex-direction: column;
|
|
1522
|
+
gap: 11px;
|
|
1523
|
+
padding: 15px 20px;
|
|
1524
|
+
overflow: auto;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
.dv-group-dialog__card {
|
|
1528
|
+
overflow: hidden;
|
|
1529
|
+
border: 1px solid #e3e9f1;
|
|
1530
|
+
border-radius: 9px;
|
|
1531
|
+
background: #f7f9fc;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
.dv-group-dialog__card > header {
|
|
1535
|
+
display: flex;
|
|
1536
|
+
align-items: baseline;
|
|
1537
|
+
gap: 8px;
|
|
1538
|
+
padding: 10px 14px;
|
|
1539
|
+
border-bottom: 1px solid #e9eef5;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
.dv-group-dialog__card > header strong {
|
|
1543
|
+
color: #253c58;
|
|
1544
|
+
font-size: 13px;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
.dv-group-dialog__card > header span {
|
|
1548
|
+
color: #8a99ad;
|
|
1549
|
+
font-size: 11px;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
.dv-group-dialog__card > header code {
|
|
1553
|
+
margin-left: auto;
|
|
1554
|
+
color: #9aa8b9;
|
|
1555
|
+
font-size: 10px;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
.dv-group-dialog__card ul {
|
|
1559
|
+
margin: 0;
|
|
1560
|
+
padding: 2px 12px 4px;
|
|
1561
|
+
list-style: none;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
.dv-group-dialog__line {
|
|
1565
|
+
display: flex;
|
|
1566
|
+
align-items: center;
|
|
1567
|
+
gap: 8px;
|
|
1568
|
+
padding: 9px 2px;
|
|
1569
|
+
border-bottom: 1px solid #ebeff5;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
.dv-group-dialog__line:last-child {
|
|
1573
|
+
border-bottom: 0;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
.dv-group-dialog__line-text {
|
|
1577
|
+
display: flex;
|
|
1578
|
+
flex: 1;
|
|
1579
|
+
flex-direction: column;
|
|
1580
|
+
min-width: 0;
|
|
1581
|
+
gap: 2px;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
.dv-group-dialog__line-text strong {
|
|
1585
|
+
overflow: hidden;
|
|
1586
|
+
color: #314964;
|
|
1587
|
+
font-size: 12px;
|
|
1588
|
+
text-overflow: ellipsis;
|
|
1589
|
+
white-space: nowrap;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
.dv-group-dialog__line-text span {
|
|
1593
|
+
overflow: hidden;
|
|
1594
|
+
color: #93a0b2;
|
|
1595
|
+
font-size: 10px;
|
|
1596
|
+
text-overflow: ellipsis;
|
|
1597
|
+
white-space: nowrap;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
.dv-group-dialog select {
|
|
1601
|
+
min-width: 138px;
|
|
1602
|
+
padding: 5px 7px;
|
|
1603
|
+
border: 1px solid #d6e0ec;
|
|
1604
|
+
border-radius: 6px;
|
|
1605
|
+
background: #fff;
|
|
1606
|
+
color: #3c5878;
|
|
1607
|
+
font-size: 11px;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
.dv-group-dialog button {
|
|
1611
|
+
padding: 5px 10px;
|
|
1612
|
+
border: 1px solid #d1deed;
|
|
1613
|
+
border-radius: 6px;
|
|
1614
|
+
background: #f7faff;
|
|
1615
|
+
color: #2f5e94;
|
|
1616
|
+
font-size: 11px;
|
|
1617
|
+
cursor: pointer;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.dv-group-dialog button:disabled {
|
|
1621
|
+
opacity: 0.45;
|
|
1622
|
+
cursor: not-allowed;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
.dv-group-dialog__line button {
|
|
1626
|
+
white-space: nowrap;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
.dv-group-dialog__footer {
|
|
1630
|
+
display: flex;
|
|
1631
|
+
align-items: center;
|
|
1632
|
+
justify-content: space-between;
|
|
1633
|
+
gap: 14px;
|
|
1634
|
+
padding: 13px 20px 15px;
|
|
1635
|
+
border-top: 1px solid #edf1f6;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
.dv-group-dialog__footer p {
|
|
1639
|
+
flex: 1;
|
|
1640
|
+
margin: 0;
|
|
1641
|
+
color: #7f8fa5;
|
|
1642
|
+
font-size: 11px;
|
|
1643
|
+
line-height: 1.55;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
.dv-group-dialog__footer > div {
|
|
1647
|
+
display: flex;
|
|
1648
|
+
gap: 8px;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
.dv-group-dialog__footer button {
|
|
1652
|
+
padding: 7px 15px;
|
|
1653
|
+
font-size: 12px;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
.dv-group-dialog__footer .dv-button--primary {
|
|
1657
|
+
border-color: #2f78d0;
|
|
1658
|
+
background: #347fd8;
|
|
1659
|
+
color: #fff;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
.dv-group-dialog__footer .dv-button--primary:disabled {
|
|
1663
|
+
background: #a8c4e6;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1106
1666
|
/* 删除确认框:组件只把载荷交出来(要删什么 / 连带什么 / 能按哪些范围删),框是这一页自己按载荷画的 */
|
|
1107
1667
|
.dv-remove-box {
|
|
1108
1668
|
position: absolute;
|