Home
avatar

.Wang

vxe-table鼠标移入行

近期的一个需求,就是左右分别都有一个表格,当鼠标移入左侧表格的某一行,对应右侧的数据行也会高亮显示。同理,右侧表格行移入之后,左侧对应表格的数据也会高亮显示。

我的想法

因为项目使用vxe-table框架,所有高版本是没有对应的api的,只能通过原生事件去触发。

import { isArray, isFunction, isString } from "lodash-es";

type Tr = HTMLTableRowElement;
type TrList = NodeListOf<Tr>;

type EventType = "mouseenter" | "mouseleave";

type TrEventCallBack = (ops: Omit<IItemTrFunCbParams, "callback">) => void;
interface ITrMouseEnterParams {
	/** 表格tr选择器 */
	selector: string;
	/** 回调函数 */
	callback?: TrEventCallBack;
}

interface IItemTrFunCbParams {
	/** 鼠标事件对象或事件目标元素 */
	row: MouseEvent | { target: Tr };
	/** 行索引 */
	rowIndex: number;
	/** 事件类型 */
	eventType: EventType;
	/** 回调函数 */
	callback?: TrEventCallBack;
}

interface IFindTableTrParams {
	/** 表格tr选择器 */
	selector: string;
	/** 行索引数组 */
	indexes: number[];
	/** 事件类型 */
	eventType: EventType;
}

// 使用WeakMap存储每个元素的事件处理函数引用
const eventHandlersMap = new WeakMap<
	Tr,
	{
		mouseenter: (e: MouseEvent) => void;
		mouseleave: (e: MouseEvent) => void;
	}
>();

class HoverTableUtil {
	static trColor = "#ff880019";

	/**
	 * 查找表格tr元素并绑定鼠标移入移出事件
	 * @param param0 查找表格tr参数
	 * @returns 无返回值
	 */
	findTableTr({ selector, indexes, eventType }: IFindTableTrParams) {
		if (!selector || !isString(selector)) return;
		if (!isArray(indexes)) return;
		for (let i = 0; i < indexes.length; i++) {
			const item = indexes[i];
			const tr = document.querySelectorAll(selector)[item] as Tr;
			if (!tr) continue;
			HoverTableUtil.itemTrFunCb({
				row: { target: tr },
				rowIndex: i,
				eventType,
			});
		}
	}

	/**
	 * 表格tr鼠标移入事件回调函数
	 * @param param0 表格tr鼠标移入事件参数
	 * @returns 无返回值
	 */
	static itemTrFunCb({
		row,
		rowIndex,
		eventType,
		callback,
	}: IItemTrFunCbParams) {
		if (!row) return;
		const target = row.target as Tr;
		target.style.backgroundColor =
			eventType === "mouseenter" ? HoverTableUtil.trColor : "";
		if (isFunction(callback)) callback({ row, rowIndex, eventType });
	}

	/**
	 * 表格tr鼠标移入事件
	 * @param param0 表格tr鼠标移入事件参数
	 * @returns 无返回值
	 */
	trMouseEnter({ selector, callback }: ITrMouseEnterParams) {
		if (!selector || !isString(selector)) return;
		const trList: TrList = document.querySelectorAll(selector);
		for (let i = 0; i < trList.length; i++) {
			const item = trList[i];
			const existingHandlers = eventHandlersMap.get(item);
			if (existingHandlers) {
				item.removeEventListener("mouseenter", existingHandlers.mouseenter);
				item.removeEventListener("mouseleave", existingHandlers.mouseleave);
				eventHandlersMap.delete(item);
			}
			// 创建新的事件处理函数
			const mouseenterHandler = (row: MouseEvent) => {
				HoverTableUtil.itemTrFunCb({
					row,
					rowIndex: i,
					eventType: "mouseenter",
					callback,
				});
			};
			const mouseleaveHandler = (row: MouseEvent) => {
				HoverTableUtil.itemTrFunCb({
					row,
					rowIndex: i,
					eventType: "mouseleave",
					callback,
				});
			};

			// 保存函数引用
			eventHandlersMap.set(item, {
				mouseenter: mouseenterHandler,
				mouseleave: mouseleaveHandler,
			});

			// 挂载事件
			item.addEventListener("mouseenter", mouseenterHandler);
			item.addEventListener("mouseleave", mouseleaveHandler);
		}
	}
}

export const hoverTableUtil = new HoverTableUtil();
// 使用demo

watch(
	() => data1.value,
	val => {
		hoverTableUtil.trMouseEnter({
			selector: ".left-table table tbody tr",
			callback: ({ rowIndex, eventType }) => {
				hoverTableUtil.findTableTr({
					selector: ".right-table table tbody tr",
					indexes: [], // 高亮的索引合集
					eventType,
				});
			},
		});
	}
);

watch(
	() => data2.value,
	val => {
		hoverTableUtil.trMouseEnter({
			selector: ".right-table table tbody tr",
			callback: ({ rowIndex, eventType }) => {
				hoverTableUtil.findTableTr({
					selector: ".left-table table tbody tr",
					indexes: [], // 高亮的索引合集
					eventType,
				});
			},
		});
	}
);
工作总结