快捷键

This commit is contained in:
gaoshuaixing
2021-06-24 18:58:46 +08:00
parent 4cc652f823
commit 35dfbcb563
5 changed files with 361 additions and 2 deletions

View File

@@ -0,0 +1,256 @@
<template>
<div
class="shortcut-key-input"
:class="{ cursor: focus }"
:style="$props.style"
tabindex="0"
@focus="handleFocus"
@blur="focus = false"
@keydown="handleKeydown"
>
<template v-if="list.length">
<template v-for="(item, index) in list">
<span :key="`${item.text}_${index}`"
>{{ item.text }} <i @click="handleDeleteKey(index)"></i
></span>
</template>
</template>
<div v-else class="placeholder">{{ placeholder }}</div>
</div>
</template>
<script>
const CODE_NUMBER = Array.from({ length: 10 }, (v, k) => `Digit${k + 1}`);
const CODE_NUMPAD = Array.from({ length: 10 }, (v, k) => `Numpad${k + 1}`);
const CODE_ABC = Array.from(
{ length: 26 },
(v, k) => `Key${String.fromCharCode(k + 65).toUpperCase()}`
);
const CODE_FN = Array.from({ length: 12 }, (v, k) => `F${k + 1}`);
const CODE_CONTROL = [
"Shift",
"ShiftLeft",
"ShiftRight",
"Control",
"ControlLeft",
"ControlRight",
"Alt",
"AltLeft",
"AltRight",
]; // ShiftKey Control(Ctrl) Alt
export default {
name: "HotKeyInput",
props: {
// 默认绑定值
// 传入 ['Ctrl+d'] 格式时会自动处理成 [{ text: 'Ctrl+d', controlKey: { altKey: false, ctrlKey: true, shiftKey: false, key: 'd', code: 'KeyD } }]
hotkey: {
type: Array,
required: true,
},
// 校验函数 判断是否允许显示快捷键
verify: {
type: Function,
default: () => true,
},
// 无绑定时提示文字
placeholder: {
type: String,
default: "",
},
// 限制最大数量
max: {
type: [String, Number],
default: 0,
},
// 快捷键使用范围
range: {
type: Array,
default: () => ["NUMBER", "NUMPAD", "ABC", "FN"],
},
},
data() {
return {
focus: false,
list: this.hotkey,
keyRange: [],
};
},
watch: {
list: function (list) {
if (list.length) this.focus = false;
this.$emit("update:hotkey", this.list);
},
hotkey: {
handler: function (val) {
if (!val.length) return;
const list = [];
val.forEach((item) => {
const arr = item.split("+");
const controlKey = {
altKey: arr.includes("Alt"),
ctrlKey: arr.includes("Control"),
shiftKey: arr.includes("Shift"),
key: arr[arr.length - 1],
code: `Key${arr[arr.length - 1].toUpperCase()}`,
};
list.push({
text: arr.reduce((text, item, i) => {
if (i) text += "+";
if (controlKey.key === item) text += item.toUpperCase();
else text += item;
return text;
}, ""),
controlKey,
});
});
this.list = list;
},
immediate: true,
},
range: {
handler: function (val) {
const keyRangeList = {
NUMBER: CODE_NUMBER,
NUMPAD: CODE_NUMPAD,
ABC: CODE_ABC,
FN: CODE_FN,
};
val.forEach((item) => {
this.keyRange = this.keyRange.concat(
keyRangeList[item.toUpperCase()]
);
});
},
immediate: true,
},
},
methods: {
handleFocus() {
if (!this.list.length) this.focus = true;
},
handleDeleteKey(index) {
this.list.splice(index, 1);
},
handleKeydown(e) {
const { altKey, ctrlKey, shiftKey, key, code } = e;
if (!CODE_CONTROL.includes(key)) {
if (!this.keyRange.includes(code)) return;
let controlKey = "";
[
{ key: altKey, text: "Alt" },
{ key: ctrlKey, text: "Ctrl" },
{ key: shiftKey, text: "Shift" },
].forEach((curKey) => {
if (curKey.key) {
if (controlKey) controlKey += "+";
controlKey += curKey.text;
}
});
if (key) {
if (controlKey) controlKey += "+";
controlKey += key.toUpperCase();
}
this.addHotkey({
text: controlKey,
controlKey: { altKey, ctrlKey, shiftKey, key, code },
});
}
e.preventDefault();
},
addHotkey(data) {
if (this.list.length && this.list.some((item) => data.text === item.text))
return;
if (
this.list.length &&
this.list.length.toString() === this.max.toString()
)
return;
if (!this.verify(data)) return;
this.list.push(data);
},
},
};
</script>
<style scoped>
@keyframes Blink {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.shortcut-key-input {
position: relative;
border: 1px solid #dcdcdc;
border-radius: 4px;
background-color: #fff;
color: #333;
width: 100%;
height: 40px;
padding: 2px 0;
cursor: text;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.shortcut-key-input:focus {
border-color: #188cff;
box-shadow: 0 0 4px rgba(24, 140, 255, 0.38);
}
.shortcut-key-input.cursor::after {
content: "|";
animation: Blink 1.2s ease 0s infinite;
font-size: 18px;
position: absolute;
top: 7px;
left: 8px;
}
.shortcut-key-input span {
position: relative;
display: inline-block;
box-sizing: border-box;
background-color: #f4f4f5;
border-color: #e9e9eb;
color: #909399;
padding: 0 22px 0 8px;
height: 28px;
font-size: 13px;
line-height: 28px;
border-radius: 4px;
margin: 5px;
}
.shortcut-key-input .placeholder {
position: absolute;
top: 12px;
left: 11px;
color: #c0c4cc;
font-size: 13px;
text-indent: 4px;
font: 400 13.3333px Arial;
}
.shortcut-key-input span i {
position: absolute;
top: 6px;
right: 4px;
content: "";
background: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M512 64C264.58 64 64 264.58 64 512s200.58 448 448 448 448-200.58 448-448S759.42 64 512 64zm0 832c-212.08 0-384-171.92-384-384s171.92-384 384-384 384 171.92 384 384-171.92 384-384 384z' fill='%23909399'/%3E%3Cpath d='M625.14 353.61L512 466.75 398.86 353.61a32 32 0 0 0-45.25 45.25L466.75 512 353.61 625.14a32 32 0 0 0 45.25 45.25L512 557.25l113.14 113.14a32 32 0 0 0 45.25-45.25L557.25 512l113.14-113.14a32 32 0 0 0-45.25-45.25z' fill='%23909399'/%3E%3C/svg%3E")
no-repeat center;
background-size: contain;
width: 14px;
height: 14px;
transform: scale(0.9);
opacity: 0.6;
}
.shortcut-key-input span i:hover {
cursor: pointer;
opacity: 1;
}
</style>

View File

@@ -22,6 +22,11 @@ export const constantRouterMap = [
name: 'Ipc',
component: () => import('@/views/example/Ipc')
},
{
path: 'shortcut',
name: 'Shortcut',
component: () => import('@/views/example/Shortcut')
},
{
path: 'setting',
name: 'Setting',

View File

@@ -68,6 +68,11 @@ export default {
title: '通信',
pageName: 'Ipc',
params: {},
},
'subMenu_4' : {
title: '快捷键',
pageName: 'Shortcut',
params: {},
}
},
'menu_2' : {

View File

@@ -2,7 +2,7 @@
<div>
<div>
<h3 :style="{ marginBottom: '16px' }">
demo3 渲染进程与主进程IPC通信
demo3-1 渲染进程与主进程IPC通信
</h3>
<a-list bordered>
<!-- <a-button @click="helloHandle">打招呼</a-button> -->
@@ -15,7 +15,7 @@
</div>
<div style="margin-top: 20px;">
<h3 :style="{ marginBottom: '16px' }">
demo4 主进程API执行网页函数
demo3-2 主进程API执行网页函数
</h3>
<a-list bordered>
<a-input-search v-model="content2" @search="executeJSHandle">

View File

@@ -0,0 +1,93 @@
<template>
<div>
<h3 :style="{ marginBottom: '16px' }">
demo4 快捷键
</h3>
<a-row :gutter="[16,16]">
<a-col :span="12">
显示窗口
<!-- <HotkeyInput
:hotkey.sync="hotKeyObj.keys"
:verify="handleHotkeyVerify"
placeholder="请按需要绑定的按键,支持组合按键"
/> -->
<a-input-search
style="width: 272px;"
v-model="cmd"
@search="handleSetting"
placeholder="快捷键">
<a-button slot="enterButton">
保存
</a-button>
</a-input-search>
</a-col>
<a-col :span="12">
隐藏窗口
<a-input-search
style="width: 272px;"
v-model="cmd"
@search="handleSetting"
placeholder="快捷键">
<a-button slot="enterButton">
保存
</a-button>
</a-input-search>
</a-col>
</a-row>
<a-row :gutter="[16,16]">
<a-col :span="12">
窗口最小化
<a-input-search
style="width: 272px;"
v-model="cmd"
@search="handleSetting"
placeholder="快捷键">
<a-button slot="enterButton">
保存
</a-button>
</a-input-search>
</a-col>
<a-col :span="12">
窗口还原
<a-input-search
style="width: 272px;"
v-model="cmd"
@search="handleSetting"
placeholder="快捷键">
<a-button slot="enterButton">
保存
</a-button>
</a-input-search>
</a-col>
</a-row>
</div>
</template>
<script>
// import VHotkey from "v-hotkey";
// import { openDir } from '@/api/main'
export default {
components: {},
data() {
return {
cmd: '',
hotKeyObj: {
tab: 'save',
// keys: undefined
keys: ["Ctrl+A", "Alt+D"]
},
};
},
methods: {
handleSetting (value) {
console.log('cmd:', value)
//this.cmd = ''
},
handleHotkeyVerify(hotkey) {
console.log('验证:', hotkey, this.hotKeyObj)
return true;
},
}
};
</script>
<style></style>