diff --git a/orion-ops-ui/src/assets/style/host-space-layout.less b/orion-ops-ui/src/assets/style/host-space-layout.less index 0e51a5a5..3dc648bd 100644 --- a/orion-ops-ui/src/assets/style/host-space-layout.less +++ b/orion-ops-ui/src/assets/style/host-space-layout.less @@ -191,14 +191,14 @@ body[host-space-theme='dark'] .arco-modal-container { } // 侧栏图标 -.terminal-sidebar-icon-wrapper { +.host-space-sidebar-icon-wrapper { width: var(--sidebar-icon-wrapper-size); height: var(--sidebar-icon-wrapper-size); display: flex; align-items: center; justify-content: center; - .terminal-sidebar-icon { + .host-space-sidebar-icon { width: var(--sidebar-icon-size); height: var(--sidebar-icon-size); font-size: var(--sidebar-icon-font-size); @@ -226,44 +226,44 @@ body[host-space-theme='dark'] .arco-modal-container { } // 终端设置容器 -.terminal-setting-container { +.host-space-setting-container { padding: 32px 16px 16px 16px; width: 100%; display: flex; flex-direction: column; - .terminal-setting-wrapper { + .host-space-setting-wrapper { min-width: 932px; max-width: 90%; margin: 0 auto; position: relative; } - .terminal-setting-title { + .host-space-setting-title { margin: 0 0 24px 0; user-select: none; font-size: 1.65em; color: var(--color-content-text-3); } - .terminal-setting-block { + .host-space-setting-block { color: var(--color-content-text-2); margin-bottom: 24px; } - .terminal-setting-subtitle-wrapper { + .host-space-setting-subtitle-wrapper { display: flex; justify-content: space-between; align-items: flex-start; } - .terminal-setting-subtitle { + .host-space-setting-subtitle { margin: 0 0 16px 0; user-select: none; color: var(--color-content-text-3); } - .terminal-setting-body { + .host-space-setting-body { display: flex; &.block-body { @@ -277,13 +277,13 @@ body[host-space-theme='dark'] .arco-modal-container { } // tooltip 内容 -.terminal-tooltip-content { +.host-space-tooltip-content { color: var(--color-sidebar-tooltip-text); background: var(--color-sidebar-tooltip-bg); } -// 终端右键菜单 -.terminal-context-menu { +// 右键菜单 +.host-space-context-menu { .arco-dropdown-option { padding: 0 6px; line-height: 32px; diff --git a/orion-ops-ui/src/store/index.ts b/orion-ops-ui/src/store/index.ts index 5d570f6f..2dfe3bec 100644 --- a/orion-ops-ui/src/store/index.ts +++ b/orion-ops-ui/src/store/index.ts @@ -6,6 +6,7 @@ import useTabBarStore from './modules/tab-bar'; import useCacheStore from './modules/cache'; import useTipsStore from './modules/tips'; import useDictStore from './modules/dict'; +import useHostSpaceStore from './modules/host-space'; import useTerminalStore from './modules/terminal'; const pinia = createPinia(); @@ -19,6 +20,7 @@ export { useTipsStore, useDictStore, useTerminalStore, + useHostSpaceStore, }; export default pinia; diff --git a/orion-ops-ui/src/store/modules/host-space/index.ts b/orion-ops-ui/src/store/modules/host-space/index.ts new file mode 100644 index 00000000..47cdda28 --- /dev/null +++ b/orion-ops-ui/src/store/modules/host-space/index.ts @@ -0,0 +1,179 @@ +import type { + TerminalActionBarSetting, + TerminalDisplaySetting, + TerminalInteractSetting, + TerminalPluginsSetting, + TerminalPreference, + TerminalSessionSetting, + TerminalShortcutSetting, + TerminalState +} from './types'; +import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data'; +import type { HostQueryResponse } from '@/api/asset/host'; +import type { TerminalTheme, TerminalThemeSchema } from '@/api/asset/host-terminal'; +import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data'; +import { getTerminalThemes } from '@/api/asset/host-terminal'; +import { defineStore } from 'pinia'; +import { getPreference, updatePreference } from '@/api/user/preference'; +import { nextSessionId } from '@/utils'; +import { Message } from '@arco-design/web-vue'; +import { TerminalTabType } from '@/views/host/terminal/types/terminal.const'; +import TerminalTabManager from '@/views/host/terminal/handler/terminal-tab-manager'; +import TerminalSessionManager from '@/views/host/terminal/handler/terminal-session-manager'; + +// 终端偏好项 +export const TerminalPreferenceItem = { + // 新建连接类型 + NEW_CONNECTION_TYPE: 'newConnectionType', + // 终端主题 + THEME: 'theme', + // 显示设置 + DISPLAY_SETTING: 'displaySetting', + // 操作栏设置 + ACTION_BAR_SETTING: 'actionBarSetting', + // 右键菜单设置 + RIGHT_MENU_SETTING: 'rightMenuSetting', + // 交互设置 + INTERACT_SETTING: 'interactSetting', + // 插件设置 + PLUGINS_SETTING: 'pluginsSetting', + // 会话设置 + SESSION_SETTING: 'sessionSetting', + // 快捷键设置 + SHORTCUT_SETTING: 'shortcutSetting', +}; + +export default defineStore('hostSpace', { + state: (): TerminalState => ({ + preference: { + newConnectionType: 'group', + theme: { + schema: {} as TerminalThemeSchema + } as TerminalTheme, + displaySetting: {} as TerminalDisplaySetting, + actionBarSetting: {} as TerminalActionBarSetting, + rightMenuSetting: [], + interactSetting: {} as TerminalInteractSetting, + pluginsSetting: {} as TerminalPluginsSetting, + sessionSetting: {} as TerminalSessionSetting, + shortcutSetting: { + enabled: false, + keys: [] + } as TerminalShortcutSetting, + }, + hosts: {} as AuthorizedHostQueryResponse, + tabManager: new TerminalTabManager(), + sessionManager: new TerminalSessionManager() + }), + + actions: { + // 加载终端偏好 + async fetchPreference() { + try { + // 加载偏好 + const { data } = await getPreference('TERMINAL'); + // theme 不存在则默认加载第一个 + if (!data.theme?.name) { + const { data: themes } = await getTerminalThemes(); + data.theme = themes[0]; + // 更新默认主题偏好 + 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]; + if (item) { + this.preference[key as keyof TerminalPreference] = item as any; + } + }); + } catch (e) { + Message.error('配置加载失败'); + } + }, + + // 更新终端偏好 + async updateTerminalPreference(item: string, value: any, setLocal = false) { + if (setLocal) { + this.preference[item as keyof TerminalPreference] = value; + } + try { + // 修改配置 + await updatePreference({ + type: 'TERMINAL', + item, + value + }); + } catch (e) { + Message.error('同步失败'); + } + }, + + // 加载主机列表 + async loadHosts() { + if (this.hosts.hostList?.length) { + return; + } + const { data } = await getCurrentAuthorizedHost(); + Object.keys(data).forEach(k => { + this.hosts[k as keyof AuthorizedHostQueryResponse] = data[k as keyof AuthorizedHostQueryResponse] as any; + }); + }, + + // 打开终端 + openTerminal(record: HostQueryResponse) { + // 添加到最近连接 + this.hosts.latestHosts = [...new Set([record.id, ...this.hosts.latestHosts])]; + // 获取 seq + const tabSeqArr = this.tabManager.items + .map(s => s.seq) + .filter(Boolean) + .map(Number); + const nextSeq = tabSeqArr.length + ? Math.max(...tabSeqArr) + 1 + : 1; + // 打开 tab + this.tabManager.openTab({ + type: TerminalTabType.TERMINAL, + key: nextSessionId(10), + seq: nextSeq, + title: `(${nextSeq}) ${record.alias || record.name}`, + hostId: record.id, + address: record.address + }); + }, + + // 复制并且打开终端 + openCopyTerminal(hostId: number) { + const host = this.hosts.hostList + .find(s => s.id === hostId); + if (host) { + this.openTerminal(host); + } + }, + + // 获取当前终端会话 + getCurrentTerminalSession(tips: boolean = true) { + const tab = this.tabManager.getCurrentTab(); + if (!tab || tab.type !== TerminalTabType.TERMINAL) { + if (tips) { + Message.warning('请切换到终端标签页'); + } + return; + } + // 获取处理器并截图 + return this.sessionManager.getSession(tab.key); + }, + + }, + +}); diff --git a/orion-ops-ui/src/store/modules/host-space/types.ts b/orion-ops-ui/src/store/modules/host-space/types.ts new file mode 100644 index 00000000..99624442 --- /dev/null +++ b/orion-ops-ui/src/store/modules/host-space/types.ts @@ -0,0 +1,97 @@ +import type { ITerminalSessionManager, ITerminalTabManager } from '@/views/host/terminal/types/terminal.type'; +import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data'; +import type { TerminalTheme } from '@/api/asset/host-terminal'; + +export interface TerminalState { + preference: TerminalPreference; + hosts: AuthorizedHostQueryResponse; + tabManager: ITerminalTabManager; + sessionManager: ITerminalSessionManager; +} + +// 终端配置 +export interface TerminalPreference { + newConnectionType: string; + theme: TerminalTheme; + displaySetting: TerminalDisplaySetting; + actionBarSetting: TerminalActionBarSetting; + rightMenuSetting: Array, + interactSetting: TerminalInteractSetting; + pluginsSetting: TerminalPluginsSetting; + sessionSetting: TerminalSessionSetting; + shortcutSetting: TerminalShortcutSetting; +} + +// 显示设置 +export interface TerminalDisplaySetting { + fontFamily?: string; + fontSize?: number; + lineHeight?: number; + fontWeight?: string | number; + fontWeightBold?: string | number; + cursorStyle?: string; + cursorBlink?: boolean; +} + +// 操作栏设置 +export interface TerminalActionBarSetting { + commandInput?: boolean; + connectStatus?: boolean; + + [key: string]: unknown; +} + +// 交互设置 +export interface TerminalInteractSetting { + fastScrollModifier: boolean; + altClickMovesCursor: boolean; + rightClickSelectsWord: boolean; + selectionChangeCopy: boolean; + copyAutoTrim: boolean; + pasteAutoTrim: boolean; + rightClickPaste: boolean; + enableRightClickMenu: boolean; + enableBell: boolean; + wordSeparator: string; +} + +// 插件设置 +export interface TerminalPluginsSetting { + enableWeblinkPlugin: boolean; + enableWebglPlugin: boolean; + enableImagePlugin: boolean; +} + +// 会话设置 +export interface TerminalSessionSetting { + terminalEmulationType: string; + scrollBackLine: number; +} + +// 终端快捷键设置 +export interface TerminalShortcutSetting { + enabled: boolean; + keys: Array; +} + +// 终端快捷键 +export interface ShortcutKey { + ctrlKey: boolean; + shiftKey: boolean; + altKey: boolean; + code: string; +} + +// 终端快捷键 +export interface TerminalShortcutKey extends ShortcutKey { + item: string; + enabled: boolean; +} + +// 终端快捷键编辑 +export interface TerminalShortcutKeyEditable extends TerminalShortcutKey { + editable: boolean; + content: string; + type: number; + shortcutKey?: string; +} diff --git a/orion-ops-ui/src/views/host/command-snippet/components/command-snippet-list-item.vue b/orion-ops-ui/src/views/host/command-snippet/components/command-snippet-list-item.vue index 2751fe2f..e6fda2db 100644 --- a/orion-ops-ui/src/views/host/command-snippet/components/command-snippet-list-item.vue +++ b/orion-ops-ui/src/views/host/command-snippet/components/command-snippet-list-item.vue @@ -1,5 +1,5 @@