/** * DOM 事件模拟工具 * 封装仿真鼠标事件,用于触发 React/Vue 等框架 UI 组件库的弹出层 * * 【重要】项目中所有需要触发弹出层的点击操作,必须使用此模块导出的 simulateClick, * 禁止直接使用 .click()(普通 click 事件无法触发绑定在 mousedown 上的组件逻辑) */ /** 仿真点击的配置选项 */ interface SimulateClickOptions { /** 是否触发 focus 事件(默认 false) */ focus?: boolean /** 是否触发 pointerdown/pointerup 事件(默认 true,部分组件库监听 pointer 事件) */ pointer?: boolean /** 点击坐标 clientX(默认取元素中心) */ clientX?: number /** 点击坐标 clientY(默认取元素中心) */ clientY?: number } /** * 仿真鼠标点击:完整的 mousedown → mouseup → click 事件链 * 模拟真实用户点击行为,兼容 React / Vue / Shimo Design / Ant Design 等组件库 * * @param el - 目标 DOM 元素 * @param options - 可选配置(focus、pointer 事件、坐标) * * 使用场景: * - 触发下拉选择器弹出层 * - 触发日期选择器面板 * - 触发级联选择器展开 * - 任何需要模拟真实鼠标点击的场景 */ export function simulateClick(el: HTMLElement, options?: SimulateClickOptions): void { const { focus = false, pointer = true, clientX, clientY } = options || {} // 计算点击坐标(默认取元素中心点) const rect = el.getBoundingClientRect() const x = clientX ?? (rect.left + rect.width / 2) const y = clientY ?? (rect.top + rect.height / 2) /** 构造 MouseEvent 的公共参数 */ const mouseEventInit: MouseEventInit = { bubbles: true, cancelable: true, clientX: x, clientY: y, button: 0, buttons: 1, } // 1. focus(可选) if (focus) { el.focus() } // 2. pointer 事件(可选,部分组件库如 Radix UI 监听 pointerdown) if (pointer) { el.dispatchEvent(new PointerEvent("pointerdown", { ...mouseEventInit, pointerId: 1, pointerType: "mouse" })) } // 3. mousedown el.dispatchEvent(new MouseEvent("mousedown", mouseEventInit)) // 4. pointerup(可选) if (pointer) { el.dispatchEvent(new PointerEvent("pointerup", { ...mouseEventInit, pointerId: 1, pointerType: "mouse" })) } // 5. mouseup el.dispatchEvent(new MouseEvent("mouseup", mouseEventInit)) // 6. click el.dispatchEvent(new MouseEvent("click", mouseEventInit)) }