Files
workspace/code/fms/fms-vue/src/composables/useHotkeys.js
T
2026-08-22 17:18:27 +08:00

57 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { onBeforeUnmount, onMounted } from 'vue'
// 输入态判断:焦点在 input / textarea / 可编辑区域
function isTypingTarget(e) {
const el = e.target
return !!el && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable)
}
// 快捷键匹配:精确匹配(未声明的修饰符按下则不匹配)
function matches(e, s) {
if (e.key.toLowerCase() !== String(s.key).toLowerCase()) return false
// 主修饰键:声明了 mod / ctrl / meta 中任一 → 要求 Ctrl 或 Meta 按下
const needsMod = s.mod === true || s.ctrl === true || s.meta === true
const modPressed = e.ctrlKey || e.metaKey
if (needsMod !== modPressed) return false
// alt / shift 精确匹配(未声明即要求未按下)
if (!!s.alt !== e.altKey) return false
if (!!s.shift !== e.shiftKey) return false
return true
}
/**
* 全局键盘快捷键注册(可组合、可复用)。
*
* 用法(在任意组件 setup 顶层调用):
* useHotkeys([
* { key: 's', mod: true, ignoreWhenTyping: false, handler: save }, // Ctrl/Cmd + S
* { key: '/', handler: focusSearch }, // 单独键(无修饰符)
* { key: 'r', alt: true, handler: reload }, // Alt + R
* ])
*
* 修饰符语义(精确匹配,未声明的修饰符按下则不匹配):
* mod —— 主修饰键:Ctrl 或 Meta(Cmd)任一(跨平台主修饰键)
* ctrl / meta / alt / shift —— 单独指定(与 mod 可叠加,如 { key: 's', mod: true, shift: true })
*
* 其他选项:
* ignoreWhenTyping(默认 true)—— 焦点在 input / textarea / 可编辑区域时忽略;
* 组合保存类快捷键(如 Ctrl+S)通常传 false,保证输入时也能保存。
* handler(e) —— 触发回调,默认已 preventDefault 阻止浏览器默认行为。
*
* 生命周期:onMounted 注册、onBeforeUnmount 卸载,组件销毁后自动解绑。
*/
export function useHotkeys(shortcuts) {
function onKeydown(e) {
for (const s of shortcuts) {
if (!matches(e, s)) continue
if (s.ignoreWhenTyping !== false && isTypingTarget(e)) continue
e.preventDefault()
s.handler(e)
break
}
}
onMounted(() => window.addEventListener('keydown', onKeydown))
onBeforeUnmount(() => window.removeEventListener('keydown', onKeydown))
}