v-hover-column
获取鼠标悬浮的在那一列的指令(列数从1开始)
支持多级表头,垂直合并单元格。水平合并单元格会有问题
代码
ts
import type { Directive } from 'vue'
type EventHandler = (e: MouseEvent) => void
type SetHoverColumn = (column: number | null) => void
type Position = { left: number; right: number }
const handlerMap = new WeakMap<
HTMLElement,
{ handleMouseOver: EventHandler; handleMouseLeave: EventHandler }
>()
const directive: Directive<HTMLElement, SetHoverColumn> = {
mounted(el, { value: setHoverColumn }) {
let lastValue: number | null = null
const innerSetHoverColumn = (value: number | null) => {
if (lastValue === value) return
lastValue = value
setHoverColumn(value)
}
const handleMouseOver: EventHandler = (e) => {
const dom = e.target as HTMLElement | null
const cell = dom?.closest<HTMLTableCellElement>('td, th')
if (!cell || (cell.tagName === 'TH' && cell.colSpan > 1)) {
return innerSetHoverColumn(null)
}
const { left, width } = cell.getBoundingClientRect()
const cellCenterX = left + width / 2
const positions = getPositions(el)
if (positions.length === 0) {
return innerSetHoverColumn(null)
}
for (let i = 0; i < positions.length; i++) {
const { left, right } = positions[i]
if (cellCenterX >= left && cellCenterX <= right) {
innerSetHoverColumn(i + 1)
break
}
}
}
const handleMouseLeave: EventHandler = () => {
innerSetHoverColumn(null)
}
el.addEventListener('mouseover', handleMouseOver)
el.addEventListener('mouseleave', handleMouseLeave)
// 保存处理函数以便卸载时移除
handlerMap.set(el, { handleMouseOver, handleMouseLeave })
},
unmounted(el) {
const handler = handlerMap.get(el)
if (handler) {
const { handleMouseOver, handleMouseLeave } = handler
el.removeEventListener('mouseover', handleMouseOver)
el.removeEventListener('mouseleave', handleMouseLeave)
handlerMap.delete(el)
}
},
}
const getPositions = (el: HTMLElement) => {
const colgroup = el.querySelector('colgroup')
if (!colgroup) return []
const startLeft = colgroup.getBoundingClientRect().left
let left = startLeft
return Array.from(colgroup.children).map<Position>((col) => {
// 兼容 col 堆叠在一起的情况
return {
left,
right: (left += col.getBoundingClientRect().width),
}
})
}
export default directive
示例
vue
<template>
<el-table v-hover-column="setHoverColumn" :data="tableData">
<el-table-column label="基本信息">
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" :colspan="2" />
</el-table-column>
<el-table-column label="联系方式">
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="phone" label="电话" />
</el-table-column>
</el-table>
<div>当前悬浮列:{{ hoverColumn }}</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
// 示例数据
const hoverColumn = ref<number | null>(null);
const setHoverColumn = (column: number | null) => {
hoverColumn.value = column
}
const tableData = ref([
{ name: '张三', age: 30, email: 'zhangsan@example.com', phone: '13800138000' },
{ name: '李四', age: 28, email: 'lisi@example.com', phone: '13900139000' }
]);
</script>