feat: 终端全局快捷键.
This commit is contained in:
@@ -57,7 +57,7 @@ export default defineStore('terminal', {
|
||||
pluginsSetting: {} as TerminalPluginsSetting,
|
||||
sessionSetting: {} as TerminalSessionSetting,
|
||||
shortcutSetting: {
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
keys: []
|
||||
} as TerminalShortcutSetting,
|
||||
},
|
||||
@@ -79,7 +79,16 @@ export default defineStore('terminal', {
|
||||
// 更新默认主题偏好
|
||||
await this.updateTerminalPreference(TerminalPreferenceItem.THEME, data.theme);
|
||||
}
|
||||
// 选择赋值
|
||||
// 移除禁用的快捷键
|
||||
if (data.shortcutSetting?.enabled) {
|
||||
data.shortcutSetting.keys = data.shortcutSetting.keys.filter(s => s.enabled);
|
||||
} else {
|
||||
data.shortcutSetting = {
|
||||
enabled: false,
|
||||
keys: []
|
||||
};
|
||||
}
|
||||
// 选择赋值 (不能修改引用)
|
||||
const keys = Object.keys(this.preference);
|
||||
keys.forEach(key => {
|
||||
const item = data[key as keyof TerminalPreference];
|
||||
|
||||
@@ -76,9 +76,10 @@ export interface TerminalShortcutSetting {
|
||||
|
||||
// 终端快捷键
|
||||
export interface TerminalShortcutKey {
|
||||
option: string;
|
||||
item: string;
|
||||
ctrlKey: boolean;
|
||||
shiftKey: boolean;
|
||||
altKey: boolean;
|
||||
code: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
@@ -35,9 +35,10 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { TerminalTabType, InnerTabs } from '../../types/terminal.const';
|
||||
import { TerminalTabType, InnerTabs, TerminalTabShortcutItems } from '../../types/terminal.const';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { watch } from 'vue';
|
||||
import { onMounted, onUnmounted, watch } from 'vue';
|
||||
import { addEventListen, removeEventListen } from '@/utils/event';
|
||||
import EmptyRecommend from './empty-recommend.vue';
|
||||
import NewConnectionView from '../new-connection/new-connection-view.vue';
|
||||
import TerminalDisplaySetting from '../view-setting/terminal-display-setting.vue';
|
||||
@@ -45,29 +46,91 @@
|
||||
import TerminalGeneralSetting from '../view-setting/terminal-general-setting.vue';
|
||||
import TerminalView from '../xterm/terminal-view.vue';
|
||||
|
||||
const { tabManager, sessionManager } = useTerminalStore();
|
||||
const { preference, tabManager, sessionManager } = useTerminalStore();
|
||||
|
||||
// 监听 tab 修改
|
||||
watch(() => tabManager.active, active => {
|
||||
if (!active) {
|
||||
watch(() => tabManager.active, (active, before) => {
|
||||
if (before) {
|
||||
// 失焦已经切换的终端
|
||||
const beforeTab = tabManager.items.find(s => s.key === before);
|
||||
if (beforeTab && beforeTab?.type === TerminalTabType.TERMINAL) {
|
||||
sessionManager.getSession(before)?.blur();
|
||||
}
|
||||
}
|
||||
if (active) {
|
||||
// 获取 activeTab
|
||||
const activeTab = tabManager.items.find(s => s.key === active);
|
||||
if (!activeTab) {
|
||||
return;
|
||||
}
|
||||
// 修改标题
|
||||
document.title = activeTab.title;
|
||||
// 终端自动聚焦
|
||||
if (activeTab?.type === TerminalTabType.TERMINAL) {
|
||||
sessionManager.getSession(active)?.focus();
|
||||
}
|
||||
} else {
|
||||
// 修改标题
|
||||
document.title = '主机终端';
|
||||
return;
|
||||
}
|
||||
// 获取 tab
|
||||
const tab = tabManager.items.find(s => s.key === active);
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
// 修改标题
|
||||
document.title = tab.title;
|
||||
// terminal 自动聚焦
|
||||
if (tab?.type === TerminalTabType.TERMINAL) {
|
||||
sessionManager.getSession(active)?.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO 快捷键逻辑 主机加载逻辑 加载中逻辑
|
||||
// 处理快捷键逻辑
|
||||
const handlerKeyboard = (event: Event) => {
|
||||
// 当前页面非 terminal 的时候再触发快捷键 (terminal 有内置逻辑)
|
||||
if (tabManager.active
|
||||
&& tabManager.items.find(s => s.key === tabManager.active)?.type === TerminalTabType.TERMINAL) {
|
||||
return;
|
||||
}
|
||||
const e = event as KeyboardEvent;
|
||||
// 检测触发的快捷键
|
||||
const key = preference.shortcutSetting.keys.find(key => {
|
||||
return key.code === e.code
|
||||
&& key.altKey === e.altKey
|
||||
&& key.shiftKey === e.shiftKey
|
||||
&& key.ctrlKey === e.ctrlKey;
|
||||
});
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
// 触发逻辑
|
||||
switch (key.item) {
|
||||
case TerminalTabShortcutItems.CLOSE_TAB.item:
|
||||
// 关闭 tab
|
||||
if (tabManager.active) {
|
||||
tabManager.deleteTab(tabManager.active);
|
||||
}
|
||||
break;
|
||||
case TerminalTabShortcutItems.CHANGE_TO_PREV_TAB.item:
|
||||
// 切换至前一个 tab
|
||||
tabManager.changeToPrevTab();
|
||||
break;
|
||||
case TerminalTabShortcutItems.CHANGE_TO_NEXT_TAB.item:
|
||||
// 切换至后一个 tab
|
||||
tabManager.changeToNextTab();
|
||||
break;
|
||||
case TerminalTabShortcutItems.OPEN_NEW_CONNECT_TAB.item:
|
||||
// 切换到新建连接 tab
|
||||
tabManager.openTab(InnerTabs.NEW_CONNECTION);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听键盘事件
|
||||
onMounted(() => {
|
||||
if (preference.shortcutSetting.enabled) {
|
||||
addEventListen(window, 'keydown', handlerKeyboard);
|
||||
}
|
||||
});
|
||||
|
||||
// 移除键盘事件
|
||||
onUnmounted(() => {
|
||||
if (preference.shortcutSetting.enabled) {
|
||||
removeEventListen(window, 'keydown', handlerKeyboard);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
v-model="formModel.copyAutoTrim" />
|
||||
</block-setting-item>
|
||||
<!-- 粘贴去除空格 -->
|
||||
<block-setting-item label="粘贴去除空格" desc="粘贴文本前自动删除尾部空格 如: 命令输入框, 命令编辑器, 右键粘贴, 粘贴按钮, 右键菜单粘贴, 自定义粘贴快捷键. (系统快捷键无法干预 如: ctrl + shift + v, shift + insert)">
|
||||
<block-setting-item label="粘贴去除空格" desc="粘贴文本前自动删除尾部空格 如: 命令输入框, 命令编辑器, 右键粘贴, 粘贴按钮, 右键菜单粘贴, 自定义粘贴快捷键">
|
||||
<a-switch type="round"
|
||||
v-model="formModel.pasteAutoTrim" />
|
||||
</block-setting-item>
|
||||
|
||||
@@ -29,94 +29,7 @@ export default class TerminalSessionHandler implements ITerminalSessionHandler {
|
||||
this.domRef = domRef;
|
||||
const { preference, tabManager } = useTerminalStore();
|
||||
this.interactSetting = preference.interactSetting;
|
||||
// this.shortcutKeys = preference.shortcutSetting.keys;
|
||||
this.shortcutKeys = [
|
||||
{
|
||||
option: 'copy',
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: false,
|
||||
code: 'KeyC'
|
||||
}, {
|
||||
option: 'paste',
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: false,
|
||||
code: 'KeyV'
|
||||
}, {
|
||||
option: 'toTop',
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: false,
|
||||
code: 'ArrowUp'
|
||||
}, {
|
||||
option: 'toBottom',
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: false,
|
||||
code: 'ArrowDown'
|
||||
}, {
|
||||
option: 'selectAll',
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: false,
|
||||
code: 'KeyA'
|
||||
}, {
|
||||
option: 'search',
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: false,
|
||||
code: 'KeyF'
|
||||
}, {
|
||||
option: 'fontSizePlus',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'Equal'
|
||||
}, {
|
||||
option: 'fontSizeSubtract',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'Minus'
|
||||
}, {
|
||||
option: 'commandEditor',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'KeyE'
|
||||
}, {
|
||||
option: 'close',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'KeyW'
|
||||
}, {
|
||||
option: 'changeToPrev',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'ArrowLeft'
|
||||
}, {
|
||||
option: 'changeToNext',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'ArrowRight'
|
||||
}, {
|
||||
option: 'openCopyTerminal',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'KeyO'
|
||||
}, {
|
||||
option: 'openNewConnect',
|
||||
ctrlKey: true,
|
||||
shiftKey: false,
|
||||
altKey: true,
|
||||
code: 'KeyN'
|
||||
},
|
||||
];
|
||||
this.shortcutKeys = preference.shortcutSetting.keys;
|
||||
this.tabManager = tabManager;
|
||||
}
|
||||
|
||||
@@ -158,7 +71,7 @@ export default class TerminalSessionHandler implements ITerminalSessionHandler {
|
||||
});
|
||||
if (key) {
|
||||
// 调用处理方法
|
||||
this.invokeHandle.call(this, key.option);
|
||||
this.invokeHandle.call(this, key.item);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@@ -271,22 +184,22 @@ export default class TerminalSessionHandler implements ITerminalSessionHandler {
|
||||
}
|
||||
|
||||
// 切换到前一个 tab
|
||||
changeToPrev() {
|
||||
this.tabManager.changeToPrev();
|
||||
changeToPrevTab() {
|
||||
this.tabManager.changeToPrevTab();
|
||||
}
|
||||
|
||||
// 切换到后一个 tab
|
||||
changeToNext() {
|
||||
this.tabManager.changeToNext();
|
||||
changeToNextTab() {
|
||||
this.tabManager.changeToNextTab();
|
||||
}
|
||||
|
||||
// 复制终端
|
||||
openCopyTerminal() {
|
||||
// 复制终端 tab
|
||||
openCopyTerminalTab() {
|
||||
useTerminalStore().openCopyTerminal(this.session.hostId);
|
||||
}
|
||||
|
||||
// 打开新建连接页面
|
||||
openNewConnect() {
|
||||
// 打开新建连接 tab
|
||||
openNewConnectTab() {
|
||||
this.tabManager.openTab(InnerTabs.NEW_CONNECTION);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { UnwrapRef } from 'vue';
|
||||
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
||||
import type { ITerminalChannel, ITerminalSession, ITerminalSessionHandler, TerminalAddons, TerminalDomRef } from '../types/terminal.type';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { fontFamilySuffix, TerminalStatus } from '../types/terminal.const';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import { fontFamilySuffix, TerminalStatus } from '../types/terminal.const';
|
||||
import { Terminal } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
@@ -13,6 +13,7 @@ import { CanvasAddon } from 'xterm-addon-canvas';
|
||||
import { WebglAddon } from 'xterm-addon-webgl';
|
||||
import { playBell } from '@/utils/bell';
|
||||
import TerminalSessionHandler from './terminal-session-handler';
|
||||
import { addEventListen } from '@/utils/event';
|
||||
|
||||
// 终端会话实现
|
||||
export default class TerminalSession implements ITerminalSession {
|
||||
@@ -82,16 +83,14 @@ export default class TerminalSession implements ITerminalSession {
|
||||
// 处理自定义按键
|
||||
this.inst.attachCustomKeyEventHandler((e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
// 未开启
|
||||
if (!preference.shortcutSetting.enabled) {
|
||||
return true;
|
||||
// 触发快捷键检测
|
||||
if (e.type === 'keydown'
|
||||
&& preference.shortcutSetting.enabled
|
||||
&& preference.shortcutSetting.keys.length) {
|
||||
// 触发快捷键
|
||||
return this.handler.triggerShortcutKey(e);
|
||||
}
|
||||
// 只监听 keydown 事件
|
||||
if (e.type !== 'keydown') {
|
||||
return true;
|
||||
}
|
||||
// 触发快捷键
|
||||
return this.handler.triggerShortcutKey(e);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,7 +133,7 @@ export default class TerminalSession implements ITerminalSession {
|
||||
});
|
||||
});
|
||||
// 设置右键选项
|
||||
dom.addEventListener('contextmenu', async () => {
|
||||
addEventListen(dom, 'contextmenu', async () => {
|
||||
// 右键粘贴逻辑
|
||||
if (preference.interactSetting.rightClickPaste) {
|
||||
if (!this.canWrite || !this.connected) {
|
||||
@@ -195,16 +194,21 @@ export default class TerminalSession implements ITerminalSession {
|
||||
this.inst.write(value);
|
||||
}
|
||||
|
||||
// 自适应
|
||||
fit(): void {
|
||||
this.addons.fit?.fit();
|
||||
}
|
||||
|
||||
// 聚焦
|
||||
focus(): void {
|
||||
this.inst.focus();
|
||||
}
|
||||
|
||||
// 失焦
|
||||
blur(): void {
|
||||
this.inst.blur();
|
||||
}
|
||||
|
||||
// 自适应
|
||||
fit(): void {
|
||||
this.addons.fit?.fit();
|
||||
}
|
||||
|
||||
// 查找
|
||||
find(word: string, next: boolean, options: ISearchOptions): void {
|
||||
if (next) {
|
||||
@@ -229,7 +233,7 @@ export default class TerminalSession implements ITerminalSession {
|
||||
Object.values(this.addons)
|
||||
.filter(Boolean)
|
||||
.forEach(s => s.dispose());
|
||||
// 卸载实体
|
||||
// 卸载终端
|
||||
this.inst.dispose();
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@ export default class TerminalTabManager implements ITerminalTabManager {
|
||||
}
|
||||
|
||||
// 切换到前一个 tab
|
||||
changeToPrev() {
|
||||
changeToPrevTab() {
|
||||
this.changeToIndex(this.getCurrentTabIndex() - 1);
|
||||
}
|
||||
|
||||
// 切换到后一个 tab
|
||||
changeToNext() {
|
||||
changeToNextTab() {
|
||||
this.changeToIndex(this.getCurrentTabIndex() + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,66 @@ export const ActionBarItems = [
|
||||
}
|
||||
];
|
||||
|
||||
// 终端 tab 快捷键操作
|
||||
export const TerminalTabShortcutItems = {
|
||||
CHANGE_TO_PREV_TAB: {
|
||||
item: 'changeToPrevTab',
|
||||
content: '切换为前一个 tab'
|
||||
},
|
||||
CHANGE_TO_NEXT_TAB: {
|
||||
item: 'changeToNextTab',
|
||||
content: '切换为后一个 tab'
|
||||
},
|
||||
CLOSE_TAB: {
|
||||
item: 'closeTab',
|
||||
content: '关闭当前 tab'
|
||||
},
|
||||
OPEN_NEW_CONNECT_TAB: {
|
||||
item: 'openNewConnectTab',
|
||||
content: '打开新建连接 tab'
|
||||
},
|
||||
OPEN_COPY_TERMINAL_TAB: {
|
||||
item: 'openCopyTerminalTab',
|
||||
content: '复制当前终端 tab'
|
||||
},
|
||||
COPY: {
|
||||
item: 'copy',
|
||||
content: '复制'
|
||||
},
|
||||
PASTE: {
|
||||
item: 'paste',
|
||||
content: '粘贴'
|
||||
},
|
||||
TO_TOP: {
|
||||
item: 'toTop',
|
||||
content: '去顶部'
|
||||
},
|
||||
TO_BOTTOM: {
|
||||
item: 'toBottom',
|
||||
content: '去底部'
|
||||
},
|
||||
SELECT_ALL: {
|
||||
item: 'selectAll',
|
||||
content: '全选'
|
||||
},
|
||||
SEARCH: {
|
||||
item: 'search',
|
||||
content: '搜索'
|
||||
},
|
||||
FONT_SIZE_PLUS: {
|
||||
item: 'fontSizePlus',
|
||||
content: '增大字号'
|
||||
},
|
||||
FONT_SIZE_SUBTRACT: {
|
||||
item: 'fontSizeSubtract',
|
||||
content: '减小字号'
|
||||
},
|
||||
COMMAND_EDITOR: {
|
||||
item: 'commandEditor',
|
||||
content: '命令编辑器'
|
||||
},
|
||||
};
|
||||
|
||||
// 打开 sshModal key
|
||||
export const openSshModalKey = Symbol();
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ export interface ContextMenuItem {
|
||||
content: string;
|
||||
}
|
||||
|
||||
// 快捷键元素
|
||||
export interface ShortcutKeyItem {
|
||||
item: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
// ssh 额外配置
|
||||
export interface SshExtraModel {
|
||||
authType?: string;
|
||||
@@ -98,9 +104,9 @@ export interface ITerminalTabManager {
|
||||
// 打开 tab
|
||||
openTab: (tab: TerminalTabItem) => void;
|
||||
// 切换到前一个 tab
|
||||
changeToPrev: () => void;
|
||||
changeToPrevTab: () => void;
|
||||
// 切换到后一个 tab
|
||||
changeToNext: () => void;
|
||||
changeToNextTab: () => void;
|
||||
// 切换索引 tab
|
||||
changeToIndex: (index: number) => void;
|
||||
// 清空
|
||||
@@ -232,11 +238,11 @@ export interface ITerminalSessionHandler {
|
||||
// 关闭 tab
|
||||
closeTab: () => void;
|
||||
// 切换到前一个 tab
|
||||
changeToPrev: () => void;
|
||||
changeToPrevTab: () => void;
|
||||
// 切换到后一个 tab
|
||||
changeToNext: () => void;
|
||||
// 复制终端
|
||||
openCopyTerminal: () => void;
|
||||
// 打开新建连接页面
|
||||
openNewConnect: () => void;
|
||||
changeToNextTab: () => void;
|
||||
// 复制终端 tab
|
||||
openCopyTerminalTab: () => void;
|
||||
// 打开新建连接 tab
|
||||
openNewConnectTab: () => void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user