@@ -417,32 +417,128 @@ export default {
417
417
}
418
418
}
419
419
420
+ /**
421
+ * 检查元素在画布中的可用空间
422
+ * @param {number} top - 选中元素顶部位置
423
+ * @param {number} selectedHeight - 选中元素高度
424
+ * @param {number} canvasHeight - 画布高度
425
+ * @param {number} elementHeight - 要放置元素的高度
426
+ * @returns {{hasTopSpace: boolean, hasBottomSpace: boolean}}
427
+ */
428
+ const checkElementSpace = (top , selectedHeight , canvasHeight , elementHeight ) => {
429
+ return {
430
+ hasTopSpace: top >= elementHeight,
431
+ hasBottomSpace: canvasHeight - top - selectedHeight >= elementHeight
432
+ }
433
+ }
434
+
435
+ /**
436
+ * 根据策略决定元素应该放置在顶部还是底部
437
+ * @param {number} top - 选中元素顶部位置
438
+ * @param {number} elementHeight - 要放置元素的高度
439
+ * @param {boolean} hasTopSpace - 顶部是否有足够空间
440
+ * @param {boolean} hasBottomSpace - 底部是否有足够空间
441
+ * @param {string} strategy - 放置策略 ('topFirst' | 'bottomFirst')
442
+ * @returns {boolean} 是否放置在底部
443
+ */
444
+ const determineElementPosition = (hasTopSpace , hasBottomSpace , strategy = ' topFirst' ) => {
445
+ if (strategy === ' bottomFirst' ) {
446
+ // Option策略:优先底部,或顶部没空间时放底部
447
+ return hasBottomSpace || ! hasTopSpace
448
+ } else {
449
+ // Label策略:顶部没空间且底部有空间才放底部
450
+ return ! hasTopSpace && hasBottomSpace
451
+ }
452
+ }
453
+
454
+ /**
455
+ * 通用的垂直对齐计算函数
456
+ * @param {boolean} isAtBottom - 是否放置在底部
457
+ * @param {number} elementHeight - 元素高度
458
+ * @param {boolean} hasTopSpace - 顶部是否有足够空间
459
+ * @param {boolean} hasBottomSpace - 底部是否有足够空间
460
+ * @param {boolean} bottomFirst - 是否底部优先策略(true: Option策略, false: Label策略)
461
+ * @returns {{alignTop: boolean, verticalValue: number}}
462
+ */
463
+ const calculateVerticalAlignment = (
464
+ isAtBottom,
465
+ elementHeight,
466
+ hasTopSpace,
467
+ hasBottomSpace,
468
+ bottomFirst = false
469
+ ) => {
470
+ const alignTop = ! isAtBottom
471
+
472
+ let verticalValue
473
+ if (bottomFirst) {
474
+ // Option策略:不在底部 OR 底部有空间时偏移
475
+ verticalValue = ! isAtBottom || hasBottomSpace ? - elementHeight : 0
476
+ } else {
477
+ // Label策略:在底部 OR 顶部有空间时偏移
478
+ verticalValue = isAtBottom || hasTopSpace ? - elementHeight : 0
479
+ }
480
+
481
+ return { alignTop, verticalValue }
482
+ }
483
+
484
+ /**
485
+ * 一站式元素对齐计算函数(组合了空间检查、位置决策、对齐计算)
486
+ * @param {number} top - 选中元素顶部位置
487
+ * @param {number} selectedHeight - 选中元素高度
488
+ * @param {number} canvasHeight - 画布高度
489
+ * @param {number} elementHeight - 要放置元素的高度
490
+ * @param {string} strategy - 放置策略 ('topFirst' | 'bottomFirst')
491
+ * @returns {{alignTop: boolean, verticalValue: number}}
492
+ */
493
+ const calculateElementAlignment = (top , selectedHeight , canvasHeight , elementHeight , strategy = ' topFirst' ) => {
494
+ const spaceInfo = checkElementSpace (top, selectedHeight, canvasHeight, elementHeight)
495
+ const isAtBottom = determineElementPosition (spaceInfo .hasTopSpace , spaceInfo .hasBottomSpace , strategy)
496
+ return calculateVerticalAlignment (
497
+ isAtBottom,
498
+ elementHeight,
499
+ spaceInfo .hasTopSpace ,
500
+ spaceInfo .hasBottomSpace ,
501
+ strategy === ' bottomFirst'
502
+ )
503
+ }
504
+
420
505
const getStyleValues = (selectState , canvasSize , labelWidth , optionWidth ) => {
421
506
const { left , top , width , height , doc } = selectState
422
507
const { width: canvasWidth , height: canvasHeight } = canvasSize
423
508
// 标签宽度和工具操作条宽度之和加上间距
424
509
const fullRectWidth = labelWidth + optionWidth + OPTION_SPACE
510
+ const labelAlignment = calculateElementAlignment (
511
+ top,
512
+ height,
513
+ canvasHeight,
514
+ LABEL_HEIGHT ,
515
+ ' topFirst' // Label策略:顶部优先
516
+ )
425
517
426
- // 是否 将label 标签放置到底部,判断 top 距离
427
- const isLabelAtBottom = top < LABEL_HEIGHT
428
518
const labelAlign = new Align ({
429
519
alignLeft: true ,
430
520
horizontalValue: 0 ,
431
- alignTop: ! isLabelAtBottom ,
432
- verticalValue: - LABEL_HEIGHT
521
+ alignTop: labelAlignment . alignTop ,
522
+ verticalValue: labelAlignment . verticalValue
433
523
})
434
524
435
525
if (! doc) {
436
526
return {}
437
527
}
438
528
439
- // 是否将操作栏放置到底部,判断当前选中组件底部与页面底部的距离。
440
- const isOptionAtBottom = canvasHeight - top - height >= OPTION_BAR_HEIGHT
529
+ const optionAlignment = calculateElementAlignment (
530
+ top,
531
+ height,
532
+ canvasHeight,
533
+ OPTION_BAR_HEIGHT ,
534
+ ' bottomFirst' // Option策略:底部优先
535
+ )
536
+
441
537
const optionAlign = new Align ({
442
538
alignLeft: false ,
443
539
horizontalValue: 0 ,
444
- alignTop: ! isOptionAtBottom ,
445
- verticalValue: - OPTION_BAR_HEIGHT
540
+ alignTop: optionAlignment . alignTop ,
541
+ verticalValue: optionAlignment . verticalValue
446
542
})
447
543
448
544
const scrollBarWidth = doc .documentElement .scrollHeight > doc .documentElement .clientHeight ? SCROLL_BAR_WIDTH : 0
@@ -462,7 +558,7 @@ export default {
462
558
optionAlign .align (positions .LEFT )
463
559
}
464
560
465
- if (isLabelAtBottom === isOptionAtBottom ) {
561
+ if (labelAlignment . alignTop === optionAlignment . alignTop ) {
466
562
// 标签框和工具操作框都在顶部或者都在底部
467
563
468
564
if (left + fullRectWidth < canvasWidth) {
@@ -562,6 +658,12 @@ export default {
562
658
pointer- events: none;
563
659
border: 1px solid var (-- te- canvas- container- border- color- checked);
564
660
z- index: 2 ;
661
+ // 禁止文本选择
662
+ - webkit- user- select: none;
663
+ - moz- user- select: none;
664
+ - ms- user- select: none;
665
+ user- select: none;
666
+
565
667
& .absolute {
566
668
pointer- events: all;
567
669
}
0 commit comments