添加 Toast 通知功能,优化鼠标悬浮和键盘导航逻辑,增强用户交互体验。临时禁用鼠标悬浮功能以避免误操作,并在列表边界触发时提供反馈。
This commit is contained in:
@@ -25,10 +25,14 @@ const props = defineProps({
|
||||
frozen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disableHover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['select', 'click', 'contextmenu'])
|
||||
const emit = defineEmits(['select', 'click', 'contextmenu', 'showToast'])
|
||||
|
||||
// 计算列表项实际总高度
|
||||
const totalItemHeight = computed(() =>
|
||||
@@ -133,9 +137,13 @@ const handleScroll = debounce(() => {
|
||||
}
|
||||
}, props.config.scrollDebounceTime)
|
||||
|
||||
// 鼠标移入处理
|
||||
// 添加一个状态来控制是否禁用鼠标悬浮
|
||||
const disableHover = ref(false)
|
||||
let hoverTimer = null
|
||||
|
||||
// 修改鼠标移入处理函数
|
||||
function handleMouseEnter(index) {
|
||||
if (props.frozen) return // 如果列表被冻结,不响应鼠标移入
|
||||
if (props.frozen || disableHover.value || props.disableHover) return // 增加 props.disableHover 的判断
|
||||
if (!isKeyboardNavigating.value) {
|
||||
selectedIndex.value = index
|
||||
emit('select', { ...props.data[index], index })
|
||||
@@ -143,11 +151,17 @@ function handleMouseEnter(index) {
|
||||
}
|
||||
}
|
||||
|
||||
// 键盘事件处理
|
||||
// 添加记录上次到达边界的时间
|
||||
const lastReachBoundaryTime = ref({
|
||||
top: 0,
|
||||
bottom: 0
|
||||
})
|
||||
|
||||
// 修改键盘事件处理函数
|
||||
function handleKeyDown(e) {
|
||||
if (props.frozen) return // 如果列表被冻结,不响应键盘事件
|
||||
if (props.frozen) return
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
// 回车键触发点击事件
|
||||
if (selectedIndex.value >= 0 && selectedIndex.value < props.data.length) {
|
||||
const item = props.data[selectedIndex.value]
|
||||
emit('click', { ...item, index: selectedIndex.value })
|
||||
@@ -157,26 +171,59 @@ function handleKeyDown(e) {
|
||||
|
||||
const isFirstItem = selectedIndex.value === 0
|
||||
const isLastItem = selectedIndex.value === props.data.length - 1
|
||||
const now = Date.now()
|
||||
|
||||
// 如果已经在第一项还按上键,或在最后一项还按下键,则不处理
|
||||
if ((e.key === 'ArrowUp' && isFirstItem) ||
|
||||
(e.key === 'ArrowDown' && isLastItem)) {
|
||||
if (e.key === 'ArrowUp' && isFirstItem) {
|
||||
const timeSinceLastTop = now - lastReachBoundaryTime.value.top
|
||||
if (timeSinceLastTop < 5000) {
|
||||
// 5秒内再次点击,跳转到底部
|
||||
disableHover.value = true // 禁用鼠标悬浮
|
||||
selectedIndex.value = props.data.length - 1
|
||||
emit('select', { ...props.data[selectedIndex.value], index: selectedIndex.value })
|
||||
ensureSelectedItemVisible()
|
||||
// 1秒后恢复鼠标悬浮功能
|
||||
if (hoverTimer) clearTimeout(hoverTimer)
|
||||
hoverTimer = setTimeout(() => {
|
||||
disableHover.value = false
|
||||
}, 1000)
|
||||
} else {
|
||||
lastReachBoundaryTime.value.top = now
|
||||
emit('showToast', '已经到列表顶端,再次点击将回到列表底端')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (e.key === 'ArrowDown' && isLastItem) {
|
||||
const timeSinceLastBottom = now - lastReachBoundaryTime.value.bottom
|
||||
if (timeSinceLastBottom < 5000) {
|
||||
// 5秒内再次点击,跳转到顶部
|
||||
disableHover.value = true // 禁用鼠标悬浮
|
||||
selectedIndex.value = 0
|
||||
emit('select', { ...props.data[selectedIndex.value], index: selectedIndex.value })
|
||||
ensureSelectedItemVisible()
|
||||
// 1秒后恢复鼠标悬浮功能
|
||||
if (hoverTimer) clearTimeout(hoverTimer)
|
||||
hoverTimer = setTimeout(() => {
|
||||
disableHover.value = false
|
||||
}, 1000)
|
||||
} else {
|
||||
lastReachBoundaryTime.value.bottom = now
|
||||
emit('showToast', '已经到列表底端,再次点击将回到列表顶端')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 原有的上下键处理逻辑
|
||||
isKeyboardNavigating.value = true
|
||||
if (keyboardTimer) clearTimeout(keyboardTimer)
|
||||
|
||||
const containerTop = containerRef.value?.scrollTop || 0
|
||||
const containerHeight = containerRef.value?.clientHeight || 0
|
||||
|
||||
if (e.key === 'ArrowUp') {
|
||||
const nextIndex = Math.max(0, selectedIndex.value - 1)
|
||||
selectedIndex.value = nextIndex
|
||||
emit('select', { ...props.data[nextIndex], index: nextIndex })
|
||||
|
||||
const itemTop = nextIndex * totalItemHeight.value
|
||||
if (itemTop < containerTop && containerRef.value) {
|
||||
if (itemTop < containerRef.value?.scrollTop && containerRef.value) {
|
||||
containerRef.value.scrollTop = itemTop
|
||||
}
|
||||
} else {
|
||||
@@ -185,9 +232,9 @@ function handleKeyDown(e) {
|
||||
emit('select', { ...props.data[nextIndex], index: nextIndex })
|
||||
|
||||
const itemBottom = (nextIndex + 1) * totalItemHeight.value
|
||||
const scrollBottom = containerTop + containerHeight
|
||||
const scrollBottom = (containerRef.value?.scrollTop || 0) + containerHeight.value
|
||||
if (itemBottom > scrollBottom && containerRef.value) {
|
||||
containerRef.value.scrollTop = itemBottom - containerHeight
|
||||
containerRef.value.scrollTop = itemBottom - containerHeight.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +244,7 @@ function handleKeyDown(e) {
|
||||
}
|
||||
}
|
||||
|
||||
// 确保选中项可见
|
||||
// 确保选项可见
|
||||
function ensureSelectedItemVisible() {
|
||||
if (!containerRef.value) return
|
||||
|
||||
@@ -259,6 +306,9 @@ onBeforeUnmount(() => {
|
||||
if (keyboardTimer) {
|
||||
clearTimeout(keyboardTimer)
|
||||
}
|
||||
if (hoverTimer) {
|
||||
clearTimeout(hoverTimer)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user