You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js 3中如何检测鼠标悬停元素时Shift键是否按下?

Vue3 多选组件批量选择实现方案

核心思路

放弃依赖@mouseover.shift这类修饰符,转而全局跟踪Shift键的按下/释放状态,结合鼠标悬停的选项实时计算高亮区间,再配合Shift+Click完成批量勾选。这种方式能覆盖「鼠标已在选项内时按下Shift」的场景,逻辑更完整。

具体实现步骤

1. 全局跟踪Shift键状态

在组件内定义响应式变量记录Shift键状态,通过监听全局键盘事件更新:

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const isShiftPressed = ref(false)
const lastSelectedIndex = ref(-1) // 记录最后一次选中的选项索引
const selectedItems = ref([]) // 已选中项ID集合
const options = ref([/* 你的选项数据,比如 {id: 1, label: '选项1'} */])

const handleKeyDown = (e) => {
  if (e.key === 'Shift') {
    isShiftPressed.value = true
  }
}

const handleKeyUp = (e) => {
  if (e.key === 'Shift') {
    isShiftPressed.value = false
  }
}

onMounted(() => {
  window.addEventListener('keydown', handleKeyDown)
  window.addEventListener('keyup', handleKeyUp)
})

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeyDown)
  window.removeEventListener('keyup', handleKeyUp)
})
</script>

2. 鼠标悬停时高亮区间选项

给每个选项绑定鼠标悬停事件,结合Shift状态和最后选中索引,计算并标记需要高亮的项:

<template>
  <div 
    v-for="(item, index) in options" 
    :key="item.id"
    class="option-item"
    :class="{ 'highlight': shouldHighlight(index) }"
    @mouseover="hoveredIndex = index"
    @click="handleClick(index)"
  >
    <input type="checkbox" :checked="selectedItems.includes(item.id)">
    <span>{{ item.label }}</span>
  </div>
</template>

<script setup>
// ... 保留之前的代码
const hoveredIndex = ref(-1)

const shouldHighlight = (index) => {
  if (!isShiftPressed.value || lastSelectedIndex.value === -1) return false
  const start = Math.min(lastSelectedIndex.value, hoveredIndex.value)
  const end = Math.max(lastSelectedIndex.value, hoveredIndex.value)
  return index >= start && index <= end
}
</script>

<style scoped>
.option-item {
  padding: 8px 12px;
  cursor: pointer;
  transition: background-color 0.2s;
}
.highlight {
  background-color: #e6f7ff; /* 自定义高亮背景色 */
}
</style>

3. Shift+Click 批量勾选

在点击事件中区分普通点击和Shift+Click,实现单个切换或批量选中逻辑:

<script setup>
// ... 保留之前的代码
const handleClick = (index) => {
  const itemId = options.value[index].id
  if (isShiftPressed.value && lastSelectedIndex.value !== -1) {
    // 批量选中区间内的所有项
    const start = Math.min(lastSelectedIndex.value, index)
    const end = Math.max(lastSelectedIndex.value, index)
    const newSelected = new Set(selectedItems.value)
    
    for (let i = start; i <= end; i++) {
      newSelected.add(options.value[i].id)
    }
    
    selectedItems.value = Array.from(newSelected)
  } else {
    // 普通点击:切换单个项的选中状态
    const idx = selectedItems.value.indexOf(itemId)
    if (idx > -1) {
      selectedItems.value.splice(idx, 1)
    } else {
      selectedItems.value.push(itemId)
    }
    // 更新最后选中的索引
    lastSelectedIndex.value = index
  }
}
</script>

可选优化

  • 支持反选:可以在Shift+Click时判断区间内的项是否已全部选中,若已全选则批量移除。
  • 边界处理:当没有任何选中项时,Shift+Click直接选中当前项并更新最后选中索引。

内容的提问来源于stack exchange,提问作者jschpmr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 02:06:25