Skip to content

Commit e8259eb

Browse files
author
yigeyufu
committed
框选功能优化
1 parent d303442 commit e8259eb

File tree

1 file changed

+124
-58
lines changed

1 file changed

+124
-58
lines changed

src/src/view/prompt_box/prompt_index.vue

Lines changed: 124 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2345,9 +2345,18 @@ const handleMouseDown = (event) => {
23452345
23462346
// 如果点击的是空白区域,记录起始位置但不立即创建选择框
23472347
if (tokensContainer && !tokenItem) {
2348+
// 重置所有相关状态变量,确保开始新的框选操作前状态是干净的
2349+
isPotentialBoxSelection.value = false
2350+
isSelecting.value = false
2351+
isUpdatingSelectionBox.value = false
2352+
isUpdatingSelectedTokens.value = false
2353+
23482354
// 在开始新的框选前,先关闭任何已存在的操作菜单
23492355
closeSelectionActions()
23502356
2357+
// 移除任何可能残留的选择框
2358+
removeSelectionBox()
2359+
23512360
// 获取容器的位置,将选择框限制在容器内
23522361
const containerRect = tokensContainer.getBoundingClientRect()
23532362
selectionStart.value = {
@@ -2425,7 +2434,8 @@ const handleMouseMove = (event) => {
24252434
24262435
// 分离选择框更新和标签选中状态更新
24272436
// 1. 选择框更新:使用requestAnimationFrame保持流畅动画,不使用节流
2428-
if (!isUpdatingSelectionBox.value) {
2437+
// 添加防御性检查,确保即使isUpdatingSelectionBox被卡住也能继续更新
2438+
if (!isUpdatingSelectionBox.value || (Date.now() - lastUpdateTime.value > 100)) {
24292439
isUpdatingSelectionBox.value = true
24302440
requestAnimationFrame(() => {
24312441
updateSelectionBox()
@@ -2455,8 +2465,11 @@ const handleMouseUp = () => {
24552465
// 先保存选中的标签数量,然后才结束框选模式
24562466
const selectedCount = selectedTokens.value.length
24572467
2468+
// 确保所有状态变量都被正确重置
24582469
isSelecting.value = false
24592470
isBoxSelectMode.value = false // 退出框选模式
2471+
isUpdatingSelectionBox.value = false
2472+
isUpdatingSelectedTokens.value = false
24602473
24612474
// 移除选择框
24622475
removeSelectionBox()
@@ -2470,66 +2483,84 @@ const handleMouseUp = () => {
24702483
24712484
// 创建选择框元素 - 持久稳定版
24722485
const createSelectionBox = () => {
2473-
// 先检查是否已经有选择框存在
2474-
let selectionBox = document.getElementById(selectionBoxId)
2475-
if (selectionBox) {
2476-
// 如果存在,先移除它
2477-
document.body.removeChild(selectionBox)
2486+
try {
2487+
// 先检查是否已经有选择框存在
2488+
let selectionBox = document.getElementById(selectionBoxId)
2489+
if (selectionBox) {
2490+
// 如果存在,先移除它
2491+
document.body.removeChild(selectionBox)
2492+
}
2493+
2494+
// 创建新的选择框
2495+
selectionBox = document.createElement('div')
2496+
selectionBox.id = selectionBoxId
2497+
2498+
// 增强选择框的可见性,使用更醒目的样式
2499+
selectionBox.style.cssText = `
2500+
position: fixed;
2501+
background-color: rgba(66, 133, 244, 0.4);
2502+
border: 2px dashed #4285f4;
2503+
box-shadow: 0 0 12px rgba(66, 133, 244, 0.6);
2504+
pointer-events: none;
2505+
z-index: 99999;
2506+
transition: none;
2507+
opacity: 1;
2508+
display: block;
2509+
`
2510+
2511+
document.body.appendChild(selectionBox)
2512+
console.log('选择框已创建')
2513+
updateSelectionBox()
2514+
} catch (error) {
2515+
console.error('创建选择框失败:', error)
2516+
// 重置状态,确保后续操作不受影响
2517+
isUpdatingSelectionBox.value = false
24782518
}
2479-
2480-
// 创建新的选择框
2481-
selectionBox = document.createElement('div')
2482-
selectionBox.id = selectionBoxId
2483-
2484-
// 增强选择框的可见性,使用更醒目的样式
2485-
selectionBox.style.cssText = `
2486-
position: fixed;
2487-
background-color: rgba(66, 133, 244, 0.4);
2488-
border: 2px dashed #4285f4;
2489-
box-shadow: 0 0 12px rgba(66, 133, 244, 0.6);
2490-
pointer-events: none;
2491-
z-index: 99999;
2492-
transition: none;
2493-
opacity: 1;
2494-
display: block;
2495-
`
2496-
2497-
document.body.appendChild(selectionBox)
2498-
console.log('选择框已创建')
2499-
updateSelectionBox()
25002519
}
25012520
25022521
// 更新选择框位置和大小 - 增强版
25032522
const updateSelectionBox = () => {
2504-
let selectionBox = document.getElementById(selectionBoxId)
2505-
// 如果选择框不存在,重新创建它
2506-
if (!selectionBox && isSelecting.value) {
2507-
createSelectionBox()
2508-
selectionBox = document.getElementById(selectionBoxId)
2509-
if (!selectionBox) return
2523+
try {
2524+
let selectionBox = document.getElementById(selectionBoxId)
2525+
// 如果选择框不存在,重新创建它
2526+
if (!selectionBox && isSelecting.value) {
2527+
createSelectionBox()
2528+
selectionBox = document.getElementById(selectionBoxId)
2529+
if (!selectionBox) return
2530+
}
2531+
2532+
const left = Math.min(selectionStart.value.x, selectionEnd.value.x)
2533+
const top = Math.min(selectionStart.value.y, selectionEnd.value.y)
2534+
const width = Math.abs(selectionEnd.value.x - selectionStart.value.x)
2535+
const height = Math.abs(selectionEnd.value.y - selectionStart.value.y)
2536+
2537+
// 确保选择框有足够的大小可见
2538+
const minSize = 5
2539+
const effectiveWidth = Math.max(width, minSize)
2540+
const effectiveHeight = Math.max(height, minSize)
2541+
2542+
if (selectionBox) {
2543+
selectionBox.style.left = `${left}px`
2544+
selectionBox.style.top = `${top}px`
2545+
selectionBox.style.width = `${effectiveWidth}px`
2546+
selectionBox.style.height = `${effectiveHeight}px`
2547+
}
2548+
} catch (error) {
2549+
console.error('更新选择框失败:', error)
2550+
// 重置状态,确保后续操作不受影响
2551+
isUpdatingSelectionBox.value = false
25102552
}
2511-
2512-
const left = Math.min(selectionStart.value.x, selectionEnd.value.x)
2513-
const top = Math.min(selectionStart.value.y, selectionEnd.value.y)
2514-
const width = Math.abs(selectionEnd.value.x - selectionStart.value.x)
2515-
const height = Math.abs(selectionEnd.value.y - selectionStart.value.y)
2516-
2517-
// 确保选择框有足够的大小可见
2518-
const minSize = 5
2519-
const effectiveWidth = Math.max(width, minSize)
2520-
const effectiveHeight = Math.max(height, minSize)
2521-
2522-
selectionBox.style.left = `${left}px`
2523-
selectionBox.style.top = `${top}px`
2524-
selectionBox.style.width = `${effectiveWidth}px`
2525-
selectionBox.style.height = `${effectiveHeight}px`
25262553
}
25272554
25282555
// 移除选择框元素
25292556
const removeSelectionBox = () => {
2530-
const selectionBox = document.getElementById(selectionBoxId)
2531-
if (selectionBox) {
2532-
document.body.removeChild(selectionBox)
2557+
try {
2558+
const selectionBox = document.getElementById(selectionBoxId)
2559+
if (selectionBox) {
2560+
document.body.removeChild(selectionBox)
2561+
}
2562+
} catch (error) {
2563+
console.error('移除选择框失败:', error)
25332564
}
25342565
}
25352566
@@ -2583,14 +2614,14 @@ const applySelectedStyle = () => {
25832614
25842615
// 批量更新DOM,减少重排重绘
25852616
toSelect.forEach(box => {
2586-
// 为整个标签容器添加视觉反馈
2587-
box.style.border = '2px solid #4285f4'
2588-
box.style.boxShadow = '0 0 5px rgba(66, 133, 244, 0.5)'
2589-
box.style.backgroundColor = 'rgba(66, 133, 244, 0.1)'
2617+
// 为整个标签容器添加选中类,通过CSS优先级确保显示在禁用样式之上
2618+
box.classList.add('token-item-box-selected')
25902619
})
25912620
25922621
toDeselect.forEach(box => {
25932622
// 移除视觉反馈
2623+
box.classList.remove('token-item-box-selected')
2624+
// 清除之前可能设置的内联样式
25942625
box.style.border = ''
25952626
box.style.boxShadow = ''
25962627
box.style.backgroundColor = box.dataset.originalBgColor || ''
@@ -2743,19 +2774,54 @@ const bulkDeleteSelectedTokens = () => {
27432774
27442775
// 清除选中的标签
27452776
const clearSelectedTokens = () => {
2746-
// 只处理有选中样式的标签,减少DOM操作量
2747-
const styledBoxes = document.querySelectorAll('.token-item-box[style*="border: 2px solid"]')
2748-
styledBoxes.forEach(box => {
2777+
// 处理所有带有选中类的标签,确保移除所有选中样式
2778+
const selectedBoxes = document.querySelectorAll('.token-item-box-selected')
2779+
selectedBoxes.forEach(box => {
2780+
// 移除选中类
2781+
box.classList.remove('token-item-box-selected')
2782+
// 清除可能的内联样式
27492783
box.style.border = ''
27502784
box.style.boxShadow = ''
27512785
box.style.backgroundColor = box.dataset.originalBgColor || ''
27522786
})
2787+
2788+
// 清空选中状态数组
27532789
selectedTokens.value = []
2790+
2791+
// 如果操作菜单可见,也关闭它
2792+
if (showSelectionActions.value) {
2793+
showSelectionActions.value = false
2794+
document.removeEventListener('click', closeSelectionActionsOnClickOutside)
2795+
}
2796+
}
2797+
2798+
// 处理点击空白区域取消框选状态
2799+
const handleClickToClearSelection = (event) => {
2800+
// 只有当有选中项时才需要处理
2801+
if (selectedTokens.value.length === 0) return;
2802+
2803+
// 检查点击目标是否在标签容器内且不是标签本身、控制元素或操作菜单
2804+
const tokenItem = event.target.closest('.token-item-box, .token-item, .token-controls, .delete-btn, .weight-control, .bracket-btn, .translate-button, .tag-tips-box');
2805+
const tokensContainer = tokensContainerRef.value;
2806+
2807+
// 如果点击的是文本框或完全在标签容器外部的空白区域,则清除选中状态
2808+
if (event.target === inputAreaRef.value ||
2809+
(!tokenItem && (!tokensContainer || !tokensContainer.contains(event.target)))) {
2810+
clearSelectedTokens();
2811+
}
27542812
}
27552813
2814+
// 组件挂载时添加事件监听
2815+
onMounted(() => {
2816+
// 添加点击事件监听,点击空白处或文本框时清除框选状态
2817+
document.addEventListener('click', handleClickToClearSelection);
2818+
})
2819+
27562820
// 组件卸载时清理事件监听
27572821
onUnmounted(() => {
27582822
document.removeEventListener('click', handleClickOutside)
2823+
// 移除点击清除框选的事件监听
2824+
document.removeEventListener('click', handleClickToClearSelection)
27592825
if (historyTimer.value) {
27602826
clearTimeout(historyTimer.value);
27612827
}

0 commit comments

Comments
 (0)