refactor: 终端操作抽象.
This commit is contained in:
@@ -5,6 +5,7 @@ import type {
|
|||||||
TerminalPluginsSetting,
|
TerminalPluginsSetting,
|
||||||
TerminalPreference,
|
TerminalPreference,
|
||||||
TerminalSessionSetting,
|
TerminalSessionSetting,
|
||||||
|
TerminalShortcutSetting,
|
||||||
TerminalState
|
TerminalState
|
||||||
} from './types';
|
} from './types';
|
||||||
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
@@ -55,6 +56,10 @@ export default defineStore('terminal', {
|
|||||||
interactSetting: {} as TerminalInteractSetting,
|
interactSetting: {} as TerminalInteractSetting,
|
||||||
pluginsSetting: {} as TerminalPluginsSetting,
|
pluginsSetting: {} as TerminalPluginsSetting,
|
||||||
sessionSetting: {} as TerminalSessionSetting,
|
sessionSetting: {} as TerminalSessionSetting,
|
||||||
|
shortcutSetting: {
|
||||||
|
enabled: true,
|
||||||
|
keys: []
|
||||||
|
} as TerminalShortcutSetting,
|
||||||
},
|
},
|
||||||
hosts: {} as AuthorizedHostQueryResponse,
|
hosts: {} as AuthorizedHostQueryResponse,
|
||||||
tabManager: new TerminalTabManager(),
|
tabManager: new TerminalTabManager(),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export interface TerminalPreference {
|
|||||||
interactSetting: TerminalInteractSetting;
|
interactSetting: TerminalInteractSetting;
|
||||||
pluginsSetting: TerminalPluginsSetting;
|
pluginsSetting: TerminalPluginsSetting;
|
||||||
sessionSetting: TerminalSessionSetting;
|
sessionSetting: TerminalSessionSetting;
|
||||||
|
shortcutSetting: TerminalShortcutSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示设置
|
// 显示设置
|
||||||
@@ -66,3 +67,18 @@ export interface TerminalSessionSetting {
|
|||||||
terminalEmulationType: string;
|
terminalEmulationType: string;
|
||||||
scrollBackLine: number;
|
scrollBackLine: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 终端快捷键设置
|
||||||
|
export interface TerminalShortcutSetting {
|
||||||
|
enabled: boolean;
|
||||||
|
keys: Array<TerminalShortcutKey>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 终端快捷键
|
||||||
|
export interface TerminalShortcutKey {
|
||||||
|
option: string;
|
||||||
|
ctrlKey: boolean;
|
||||||
|
shiftKey: boolean;
|
||||||
|
altKey: boolean;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO 快捷键逻辑 主机加载逻辑 加载中逻辑
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ITerminalSession, TerminalTabItem, SidebarAction } from '../../types/terminal.type';
|
import type { ITerminalSession, TerminalTabItem, SidebarAction, ITerminalSessionHandler } from '../../types/terminal.type';
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
tab: TerminalTabItem
|
tab: TerminalTabItem
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { copy, readText } = useCopy();
|
const { copy } = useCopy();
|
||||||
const { getDictValue } = useDictStore();
|
const { getDictValue } = useDictStore();
|
||||||
const { preference, tabManager, sessionManager } = useTerminalStore();
|
const { preference, tabManager, sessionManager } = useTerminalStore();
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
const session = ref<ITerminalSession>();
|
const session = ref<ITerminalSession>();
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// 设置快捷键 粘贴逻辑 禁用
|
// 设置快捷键
|
||||||
// 截屏
|
// 截屏
|
||||||
// sftp
|
// sftp
|
||||||
|
|
||||||
@@ -134,48 +134,19 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 执行终端操作
|
||||||
|
const doTerminalHandle = (handle: string) => {
|
||||||
|
// 处理器
|
||||||
|
const handler = session.value?.handler[handle as keyof ITerminalSessionHandler] as () => void;
|
||||||
|
handler && handler.call(session.value?.handler);
|
||||||
|
};
|
||||||
|
|
||||||
// 操作点击逻辑
|
// 操作点击逻辑
|
||||||
const actionsClickHandler: Record<string, () => void> = {
|
const actionsClickHandler: Record<string, () => void> = {
|
||||||
// 去顶部
|
|
||||||
toTop: () => session.value?.toTop(),
|
|
||||||
// 去底部
|
|
||||||
toBottom: () => session.value?.toBottom(),
|
|
||||||
// 全选
|
|
||||||
checkAll: () => session.value?.selectAll(),
|
|
||||||
// 搜索
|
// 搜索
|
||||||
search: () => searchModal.value.toggle(),
|
search: () => searchModal.value.toggle(),
|
||||||
// 复制选中部分
|
|
||||||
copy: () => session.value?.copySelection(),
|
|
||||||
// 粘贴
|
|
||||||
paste: async () => session.value?.pasteTrimEnd(await readText()),
|
|
||||||
// ctrl + c
|
|
||||||
interrupt: () => session.value?.paste(String.fromCharCode(3)),
|
|
||||||
// 回车
|
|
||||||
enter: () => session.value?.paste(String.fromCharCode(13)),
|
|
||||||
// 增大字号
|
|
||||||
fontSizePlus: () => {
|
|
||||||
if (session.value) {
|
|
||||||
session.value.setOption('fontSize', session.value.getOption('fontSize') + 1);
|
|
||||||
if (session.value.connected) {
|
|
||||||
session.value.fit();
|
|
||||||
session.value.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 减小字号
|
|
||||||
fontSizeSubtract: () => {
|
|
||||||
if (session.value) {
|
|
||||||
session.value.setOption('fontSize', session.value.getOption('fontSize') - 1);
|
|
||||||
if (session.value.connected) {
|
|
||||||
session.value.fit();
|
|
||||||
session.value.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 命令编辑器
|
// 命令编辑器
|
||||||
commandEditor: () => editorModal.value.open('', ''),
|
commandEditor: () => editorModal.value.open('', ''),
|
||||||
// 清空
|
|
||||||
clear: () => session.value?.clear(),
|
|
||||||
// 断开连接
|
// 断开连接
|
||||||
disconnect: () => session.value?.disconnect(),
|
disconnect: () => session.value?.disconnect(),
|
||||||
// 关闭
|
// 关闭
|
||||||
@@ -189,10 +160,8 @@
|
|||||||
icon: s.icon,
|
icon: s.icon,
|
||||||
content: s.content,
|
content: s.content,
|
||||||
visible: preference.actionBarSetting[s.item] !== false,
|
visible: preference.actionBarSetting[s.item] !== false,
|
||||||
disabled: actionsEnabledStatus.value[s.item] === false,
|
disabled: session.value?.handler.enabledStatus(s.item) === false,
|
||||||
click: () => {
|
click: () => doTerminalHandle(s.item)
|
||||||
actionsClickHandler[s.item] && actionsClickHandler[s.item]();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -200,7 +169,11 @@
|
|||||||
// 初始化会话
|
// 初始化会话
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 创建终端处理器
|
// 创建终端处理器
|
||||||
session.value = await sessionManager.openSession(props.tab, terminalRef.value);
|
session.value = await sessionManager.openSession(props.tab, {
|
||||||
|
el: terminalRef.value,
|
||||||
|
editorModal: editorModal.value,
|
||||||
|
searchModal: searchModal.value
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 会话
|
// 会话
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
||||||
|
import type { ITerminalSession, ITerminalSessionHandler, ITerminalTabManager, TerminalDomRef } from '../types/terminal.type';
|
||||||
|
import type { Terminal } from 'xterm';
|
||||||
|
import { useTerminalStore } from '@/store';
|
||||||
|
import useCopy from '@/hooks/copy';
|
||||||
|
|
||||||
|
const { copy: copyValue, readText } = useCopy();
|
||||||
|
|
||||||
|
// 终端会话处理器实现
|
||||||
|
export default class TerminalSessionHandler implements ITerminalSessionHandler {
|
||||||
|
|
||||||
|
private readonly domRef: TerminalDomRef;
|
||||||
|
|
||||||
|
private readonly inst: Terminal;
|
||||||
|
|
||||||
|
private readonly session: ITerminalSession;
|
||||||
|
|
||||||
|
private readonly preference: TerminalPreference;
|
||||||
|
|
||||||
|
private readonly tabManager: ITerminalTabManager;
|
||||||
|
|
||||||
|
constructor(session: ITerminalSession,
|
||||||
|
domRef: TerminalDomRef) {
|
||||||
|
this.session = session;
|
||||||
|
this.inst = session.inst;
|
||||||
|
this.domRef = domRef;
|
||||||
|
const { preference, tabManager } = useTerminalStore();
|
||||||
|
this.preference = preference;
|
||||||
|
this.tabManager = tabManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用状态
|
||||||
|
enabledStatus(option: string): boolean {
|
||||||
|
switch (option) {
|
||||||
|
case 'paste':
|
||||||
|
case 'interrupt':
|
||||||
|
case 'enter':
|
||||||
|
case 'commandEditor':
|
||||||
|
return this.session.canWrite;
|
||||||
|
case 'disconnect':
|
||||||
|
return this.session.connected;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制选中
|
||||||
|
copy() {
|
||||||
|
let selection = this.inst.getSelection();
|
||||||
|
if (selection) {
|
||||||
|
// 去除尾部空格
|
||||||
|
if (this.preference.interactSetting.copyAutoTrim) {
|
||||||
|
selection = selection.trimEnd();
|
||||||
|
}
|
||||||
|
// 复制
|
||||||
|
copyValue(selection, false);
|
||||||
|
}
|
||||||
|
// 聚焦
|
||||||
|
this.inst.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 粘贴
|
||||||
|
paste() {
|
||||||
|
if (this.enabledStatus('paste')) {
|
||||||
|
readText().then(s => this.pasteTrimEnd(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 粘贴并且去除尾部空格 (如果配置)
|
||||||
|
pasteTrimEnd(value: string) {
|
||||||
|
if (this.enabledStatus('paste')) {
|
||||||
|
if (this.preference.interactSetting.pasteAutoTrim) {
|
||||||
|
// 粘贴前去除尾部空格
|
||||||
|
this.inst.paste(value.trimEnd());
|
||||||
|
} else {
|
||||||
|
this.inst.paste(value);
|
||||||
|
}
|
||||||
|
this.inst.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中全部
|
||||||
|
selectAll() {
|
||||||
|
this.inst.selectAll();
|
||||||
|
this.inst.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去顶部
|
||||||
|
toTop(): void {
|
||||||
|
this.inst.scrollToTop();
|
||||||
|
this.inst.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去底部
|
||||||
|
toBottom(): void {
|
||||||
|
this.inst.scrollToBottom();
|
||||||
|
this.inst.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开搜索
|
||||||
|
search() {
|
||||||
|
this.domRef.searchModal?.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 增大字号
|
||||||
|
fontSizePlus() {
|
||||||
|
this.fontSizeAdd(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 减小字号
|
||||||
|
fontSizeSubtract() {
|
||||||
|
this.fontSizeAdd(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字号增加
|
||||||
|
private fontSizeAdd(addSize: number) {
|
||||||
|
this.inst.options['fontSize'] = this.inst.options['fontSize'] as number + addSize;
|
||||||
|
if (this.session.connected) {
|
||||||
|
this.session.fit();
|
||||||
|
this.inst.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开命令编辑器
|
||||||
|
commandEditor() {
|
||||||
|
if (this.enabledStatus('commandEditor')) {
|
||||||
|
this.domRef.editorModal?.open('', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctrl + c
|
||||||
|
interrupt() {
|
||||||
|
if (this.enabledStatus('interrupt')) {
|
||||||
|
this.inst.paste(String.fromCharCode(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回车
|
||||||
|
enter() {
|
||||||
|
if (this.enabledStatus('enter')) {
|
||||||
|
this.inst.paste(String.fromCharCode(13));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
clear() {
|
||||||
|
this.inst.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 断开连接
|
||||||
|
disconnect() {
|
||||||
|
if (this.enabledStatus('disconnect')) {
|
||||||
|
this.session.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
close() {
|
||||||
|
this.tabManager.deleteTab(this.session.sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 聚焦
|
||||||
|
focus() {
|
||||||
|
this.inst.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ITerminalChannel, ITerminalSession, ITerminalSessionManager, TerminalTabItem } from '../types/terminal.type';
|
import type { ITerminalChannel, ITerminalSession, ITerminalSessionManager, TerminalDomRef, TerminalTabItem } from '../types/terminal.type';
|
||||||
import { sleep } from '@/utils';
|
import { sleep } from '@/utils';
|
||||||
import { InputProtocol } from '../types/terminal.protocol';
|
import { InputProtocol } from '../types/terminal.protocol';
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
@@ -24,7 +24,8 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 打开终端会话
|
// 打开终端会话
|
||||||
async openSession(tab: TerminalTabItem, dom: HTMLElement) {
|
async openSession(tab: TerminalTabItem,
|
||||||
|
domRef: TerminalDomRef) {
|
||||||
const sessionId = tab.key;
|
const sessionId = tab.key;
|
||||||
const hostId = tab.hostId as number;
|
const hostId = tab.hostId as number;
|
||||||
// 初始化客户端
|
// 初始化客户端
|
||||||
@@ -36,7 +37,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
this.channel
|
this.channel
|
||||||
);
|
);
|
||||||
// 初始化
|
// 初始化
|
||||||
session.init(dom);
|
session.init(domRef);
|
||||||
// 等待前端渲染完成
|
// 等待前端渲染完成
|
||||||
await sleep(100);
|
await sleep(100);
|
||||||
// 添加会话
|
// 添加会话
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { UnwrapRef } from 'vue';
|
import type { UnwrapRef } from 'vue';
|
||||||
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
||||||
import type { ITerminalChannel, ITerminalSession, TerminalAddons } from '../types/terminal.type';
|
import type { ITerminalChannel, ITerminalSession, ITerminalSessionHandler, TerminalAddons, TerminalDomRef } from '../types/terminal.type';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { fontFamilySuffix, TerminalStatus } from '../types/terminal.const';
|
import { fontFamilySuffix, TerminalStatus } from '../types/terminal.const';
|
||||||
import { InputProtocol } from '../types/terminal.protocol';
|
import { InputProtocol } from '../types/terminal.protocol';
|
||||||
@@ -13,13 +13,17 @@ import { CanvasAddon } from 'xterm-addon-canvas';
|
|||||||
import { WebglAddon } from 'xterm-addon-webgl';
|
import { WebglAddon } from 'xterm-addon-webgl';
|
||||||
import { playBell } from '@/utils/bell';
|
import { playBell } from '@/utils/bell';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
|
import TerminalShortcutDispatcher from './terminal-shortcut-dispatch';
|
||||||
|
import TerminalSessionHandler from './terminal-session-handler';
|
||||||
|
|
||||||
const copy = useCopy();
|
const copy = useCopy();
|
||||||
|
|
||||||
// 终端会话实现
|
// 终端会话实现
|
||||||
export default class TerminalSession implements ITerminalSession {
|
export default class TerminalSession implements ITerminalSession {
|
||||||
|
|
||||||
public hostId: number;
|
public readonly hostId: number;
|
||||||
|
|
||||||
|
public sessionId: string;
|
||||||
|
|
||||||
public inst: Terminal;
|
public inst: Terminal;
|
||||||
|
|
||||||
@@ -29,7 +33,7 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
|
|
||||||
public status: number;
|
public status: number;
|
||||||
|
|
||||||
private readonly sessionId: string;
|
public handler: ITerminalSessionHandler;
|
||||||
|
|
||||||
private readonly channel: ITerminalChannel;
|
private readonly channel: ITerminalChannel;
|
||||||
|
|
||||||
@@ -45,11 +49,12 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
this.canWrite = false;
|
this.canWrite = false;
|
||||||
this.status = TerminalStatus.CONNECTING;
|
this.status = TerminalStatus.CONNECTING;
|
||||||
this.inst = undefined as unknown as Terminal;
|
this.inst = undefined as unknown as Terminal;
|
||||||
|
this.handler = undefined as unknown as ITerminalSessionHandler;
|
||||||
this.addons = {} as TerminalAddons;
|
this.addons = {} as TerminalAddons;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
init(dom: HTMLElement): void {
|
init(domRef: TerminalDomRef): void {
|
||||||
const { preference } = useTerminalStore();
|
const { preference } = useTerminalStore();
|
||||||
// 初始化实例
|
// 初始化实例
|
||||||
this.inst = new Terminal({
|
this.inst = new Terminal({
|
||||||
@@ -62,17 +67,39 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
wordSeparator: preference.interactSetting.wordSeparator,
|
wordSeparator: preference.interactSetting.wordSeparator,
|
||||||
scrollback: preference.sessionSetting.scrollBackLine,
|
scrollback: preference.sessionSetting.scrollBackLine,
|
||||||
});
|
});
|
||||||
|
// 处理器
|
||||||
|
this.handler = new TerminalSessionHandler(this, domRef);
|
||||||
// 注册快捷键
|
// 注册快捷键
|
||||||
|
this.registerShortcut(preference);
|
||||||
// 注册事件
|
// 注册事件
|
||||||
this.registerEvent(dom, preference);
|
this.registerEvent(domRef.el, preference);
|
||||||
// 注册插件
|
// 注册插件
|
||||||
this.registerAddions(preference);
|
this.registerAddons(preference);
|
||||||
// 打开终端
|
// 打开终端
|
||||||
this.inst.open(dom);
|
this.inst.open(domRef.el);
|
||||||
// 自适应
|
// 自适应
|
||||||
this.addons.fit.fit();
|
this.addons.fit.fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注册快捷键
|
||||||
|
private registerShortcut(preference: UnwrapRef<TerminalPreference>) {
|
||||||
|
const dispatcher = new TerminalShortcutDispatcher(this, preference.shortcutSetting.keys);
|
||||||
|
// 处理自定义按键
|
||||||
|
this.inst.attachCustomKeyEventHandler((e: KeyboardEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// 未开启
|
||||||
|
if (!preference.shortcutSetting.enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 只监听 keydown 事件
|
||||||
|
if (e.type !== 'keydown') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 调度快捷键
|
||||||
|
return dispatcher.dispatch(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 注册事件
|
// 注册事件
|
||||||
private registerEvent(dom: HTMLElement, preference: UnwrapRef<TerminalPreference>) {
|
private registerEvent(dom: HTMLElement, preference: UnwrapRef<TerminalPreference>) {
|
||||||
// 注册输入事件
|
// 注册输入事件
|
||||||
@@ -127,7 +154,7 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 注册插件
|
// 注册插件
|
||||||
private registerAddions(preference: UnwrapRef<TerminalPreference>) {
|
private registerAddons(preference: UnwrapRef<TerminalPreference>) {
|
||||||
this.addons.fit = new FitAddon();
|
this.addons.fit = new FitAddon();
|
||||||
this.addons.search = new SearchAddon();
|
this.addons.search = new SearchAddon();
|
||||||
// 超链接插件
|
// 超链接插件
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import type { TerminalShortcutKey } from '@/store/modules/terminal/types';
|
||||||
|
import type { ITerminalSession, ITerminalShortcutDispatcher } from '../types/terminal.type';
|
||||||
|
import useCopy from '@/hooks/copy';
|
||||||
|
|
||||||
|
const { readText } = useCopy();
|
||||||
|
|
||||||
|
// 终端快捷键调度实现
|
||||||
|
export default class TerminalShortcutDispatch implements ITerminalShortcutDispatcher {
|
||||||
|
|
||||||
|
private readonly session: ITerminalSession;
|
||||||
|
|
||||||
|
private readonly keys: Array<TerminalShortcutKey>;
|
||||||
|
|
||||||
|
constructor(session: ITerminalSession, keys: Array<TerminalShortcutKey>) {
|
||||||
|
this.session = session;
|
||||||
|
// this.keys = keys;
|
||||||
|
this.keys = [
|
||||||
|
{
|
||||||
|
option: 'copy',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'C'
|
||||||
|
}, {
|
||||||
|
option: 'paste',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'V'
|
||||||
|
}, {
|
||||||
|
option: 'toTop',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'ArrowUp'
|
||||||
|
}, {
|
||||||
|
option: 'toBottom',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'ArrowDown'
|
||||||
|
}, {
|
||||||
|
option: 'selectAll',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'A'
|
||||||
|
}, {
|
||||||
|
option: 'search',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'F'
|
||||||
|
}, {
|
||||||
|
option: 'fontSizePlus',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: '+'
|
||||||
|
}, {
|
||||||
|
option: 'fontSizeSubtract',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: '_'
|
||||||
|
}, {
|
||||||
|
option: 'commandEditor',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'O'
|
||||||
|
}, {
|
||||||
|
option: 'close',
|
||||||
|
ctrlKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
altKey: false,
|
||||||
|
key: 'W'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调度快捷键
|
||||||
|
dispatch(e: KeyboardEvent): boolean {
|
||||||
|
console.log(e);
|
||||||
|
for (const key of this.keys) {
|
||||||
|
if (key.altKey === e.altKey
|
||||||
|
&& key.shiftKey === e.shiftKey
|
||||||
|
&& key.ctrlKey === e.ctrlKey
|
||||||
|
&& key.key === e.key) {
|
||||||
|
const runner = this[key.option as keyof this] as () => void;
|
||||||
|
runner && runner.apply(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制
|
||||||
|
private copy(): void {
|
||||||
|
this.session.copySelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 粘贴
|
||||||
|
private paste(): void {
|
||||||
|
// FIXME status
|
||||||
|
readText().then((e) => {
|
||||||
|
this.session.pasteTrimEnd(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去顶部
|
||||||
|
private toTop(): void {
|
||||||
|
this.session.toTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去底部
|
||||||
|
private toBottom(): void {
|
||||||
|
this.session.toBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全选
|
||||||
|
private selectAll(): void {
|
||||||
|
this.session.selectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
private search(): void {
|
||||||
|
// fixme
|
||||||
|
}
|
||||||
|
|
||||||
|
// 增大字号
|
||||||
|
private fontSizePlus(): void {
|
||||||
|
this.fontSizeAdd(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 减小字号
|
||||||
|
private fontSizeSubtract(): void {
|
||||||
|
this.fontSizeAdd(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字号增加
|
||||||
|
private fontSizeAdd(addSize: number) {
|
||||||
|
this.session.setOption('fontSize', this.session.getOption('fontSize') + addSize);
|
||||||
|
if (this.session.connected) {
|
||||||
|
this.session.fit();
|
||||||
|
this.session.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 命令编辑器
|
||||||
|
private commandEditor(): void {
|
||||||
|
// fixme
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭终端
|
||||||
|
private close(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private to(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换 tab
|
||||||
|
// 打开 新
|
||||||
|
|
||||||
|
}
|
||||||
@@ -77,7 +77,7 @@ export const ActionBarItems = [
|
|||||||
icon: 'icon-down',
|
icon: 'icon-down',
|
||||||
content: '去底部',
|
content: '去底部',
|
||||||
}, {
|
}, {
|
||||||
item: 'checkAll',
|
item: 'selectAll',
|
||||||
icon: 'icon-expand',
|
icon: 'icon-expand',
|
||||||
content: '全选',
|
content: '全选',
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -77,6 +77,13 @@ export interface OutputPayload {
|
|||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 终端 dom 元素引用
|
||||||
|
export interface TerminalDomRef {
|
||||||
|
el: HTMLElement;
|
||||||
|
searchModal: any;
|
||||||
|
editorModal: any;
|
||||||
|
}
|
||||||
|
|
||||||
// 终端 tab 管理器定义
|
// 终端 tab 管理器定义
|
||||||
export interface ITerminalTabManager {
|
export interface ITerminalTabManager {
|
||||||
// 当前 tab
|
// 当前 tab
|
||||||
@@ -97,7 +104,7 @@ export interface ITerminalTabManager {
|
|||||||
// 终端会话管理器定义
|
// 终端会话管理器定义
|
||||||
export interface ITerminalSessionManager {
|
export interface ITerminalSessionManager {
|
||||||
// 打开终端会话
|
// 打开终端会话
|
||||||
openSession: (tab: TerminalTabItem, dom: HTMLElement) => Promise<ITerminalSession>;
|
openSession: (tab: TerminalTabItem, domRef: TerminalDomRef) => Promise<ITerminalSession>;
|
||||||
// 获取终端会话
|
// 获取终端会话
|
||||||
getSession: (sessionId: string) => ITerminalSession;
|
getSession: (sessionId: string) => ITerminalSession;
|
||||||
// 关闭终端会话
|
// 关闭终端会话
|
||||||
@@ -145,6 +152,7 @@ export interface TerminalAddons {
|
|||||||
// 终端会话定义
|
// 终端会话定义
|
||||||
export interface ITerminalSession {
|
export interface ITerminalSession {
|
||||||
hostId: number;
|
hostId: number;
|
||||||
|
sessionId: string;
|
||||||
// terminal 实例
|
// terminal 实例
|
||||||
inst: Terminal;
|
inst: Terminal;
|
||||||
// 是否已连接
|
// 是否已连接
|
||||||
@@ -153,9 +161,11 @@ export interface ITerminalSession {
|
|||||||
canWrite: boolean;
|
canWrite: boolean;
|
||||||
// 状态
|
// 状态
|
||||||
status: number;
|
status: number;
|
||||||
|
// 处理器
|
||||||
|
handler: ITerminalSessionHandler;
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
init: (dom: HTMLElement) => void;
|
init: (domRef: TerminalDomRef) => void;
|
||||||
// 连接
|
// 连接
|
||||||
connect: () => void;
|
connect: () => void;
|
||||||
// 设置是否可写
|
// 设置是否可写
|
||||||
@@ -191,3 +201,47 @@ export interface ITerminalSession {
|
|||||||
// 关闭
|
// 关闭
|
||||||
close: () => void;
|
close: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 终端会话处理器定义
|
||||||
|
export interface ITerminalSessionHandler {
|
||||||
|
// 启用状态
|
||||||
|
enabledStatus: (option: string) => boolean;
|
||||||
|
// 复制选中
|
||||||
|
copy: () => void;
|
||||||
|
// 粘贴
|
||||||
|
paste: () => void;
|
||||||
|
// 粘贴并且去除尾部空格 (如果配置)
|
||||||
|
pasteTrimEnd: (value: string) => void;
|
||||||
|
// 选中全部
|
||||||
|
selectAll: () => void;
|
||||||
|
// 去顶部
|
||||||
|
toTop: () => void;
|
||||||
|
// 去底部
|
||||||
|
toBottom: () => void;
|
||||||
|
// 打开搜索
|
||||||
|
search: () => void;
|
||||||
|
// 增大字号
|
||||||
|
fontSizePlus: () => void;
|
||||||
|
// 减小字号
|
||||||
|
fontSizeSubtract: () => void;
|
||||||
|
// 打开命令编辑器
|
||||||
|
commandEditor: () => void;
|
||||||
|
// 中断
|
||||||
|
interrupt: () => void;
|
||||||
|
// 回车
|
||||||
|
enter: () => void;
|
||||||
|
// 清空
|
||||||
|
clear: () => void;
|
||||||
|
// 断开连接
|
||||||
|
disconnect: () => void;
|
||||||
|
// 关闭
|
||||||
|
close: () => void;
|
||||||
|
// 聚焦
|
||||||
|
focus: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 终端快捷键调度定义
|
||||||
|
export interface ITerminalShortcutDispatcher {
|
||||||
|
// 调度快捷键
|
||||||
|
dispatch(e: KeyboardEvent): boolean;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user