feat: 添加未选择页面的空承载页.
This commit is contained in:
@@ -9,16 +9,19 @@ import type {
|
|||||||
} from './types';
|
} from './types';
|
||||||
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
|
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
|
||||||
|
import type { HostQueryResponse } from '@/api/asset/host';
|
||||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||||
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { getPreference, updatePreference } from '@/api/user/preference';
|
import { getPreference, updatePreference } from '@/api/user/preference';
|
||||||
|
import { nextSessionId } from '@/utils';
|
||||||
import { Message } from '@arco-design/web-vue';
|
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 TerminalTabManager from '@/views/host/terminal/handler/terminal-tab-manager';
|
||||||
import TerminalSessionManager from '@/views/host/terminal/handler/terminal-session-manager';
|
import TerminalSessionManager from '@/views/host/terminal/handler/terminal-session-manager';
|
||||||
|
|
||||||
// 偏好项
|
// 终端偏好项
|
||||||
export const PreferenceItem = {
|
export const TerminalPreferenceItem = {
|
||||||
// 新建连接类型
|
// 新建连接类型
|
||||||
NEW_CONNECTION_TYPE: 'newConnectionType',
|
NEW_CONNECTION_TYPE: 'newConnectionType',
|
||||||
// 终端主题
|
// 终端主题
|
||||||
@@ -62,7 +65,7 @@ export default defineStore('terminal', {
|
|||||||
const { data: themes } = await getTerminalThemes();
|
const { data: themes } = await getTerminalThemes();
|
||||||
data.theme = themes[0];
|
data.theme = themes[0];
|
||||||
// 更新默认主题偏好
|
// 更新默认主题偏好
|
||||||
await this.updateTerminalPreference(PreferenceItem.THEME, data.theme);
|
await this.updateTerminalPreference(TerminalPreferenceItem.THEME, data.theme);
|
||||||
}
|
}
|
||||||
// 选择赋值
|
// 选择赋值
|
||||||
const keys = Object.keys(this.preference);
|
const keys = Object.keys(this.preference);
|
||||||
@@ -105,10 +108,28 @@ export default defineStore('terminal', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 添加到最近连接列表
|
// 打开终端
|
||||||
addToLatestConnect(hostId: number) {
|
openTerminal(record: HostQueryResponse) {
|
||||||
this.hosts.latestHosts = [...new Set([hostId, ...this.hosts.latestHosts])];
|
// 添加到最近连接
|
||||||
}
|
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
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export const resetObject = (obj: any, ignore: string[] = []) => {
|
|||||||
export const objectTruthKeyCount = (obj: any, ignore: string[] = []) => {
|
export const objectTruthKeyCount = (obj: any, ignore: string[] = []) => {
|
||||||
return Object.keys(obj)
|
return Object.keys(obj)
|
||||||
.filter(s => !ignore.includes(s))
|
.filter(s => !ignore.includes(s))
|
||||||
.reduce(function(acc, curr) {
|
.reduce(function (acc, curr) {
|
||||||
const currVal = obj[curr];
|
const currVal = obj[curr];
|
||||||
return acc + ~~(currVal !== undefined && currVal !== null && currVal?.length !== 0 && currVal !== '');
|
return acc + ~~(currVal !== undefined && currVal !== null && currVal?.length !== 0 && currVal !== '');
|
||||||
}, 0);
|
}, 0);
|
||||||
@@ -196,13 +196,20 @@ export function detectZoom() {
|
|||||||
* 获取唯一的 UUID
|
* 获取唯一的 UUID
|
||||||
*/
|
*/
|
||||||
export function getUUID() {
|
export function getUUID() {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
const r = Math.random() * 16 | 0;
|
const r = Math.random() * 16 | 0;
|
||||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||||
return v.toString(16);
|
return v.toString(16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取会话id
|
||||||
|
*/
|
||||||
|
export const nextSessionId = (len: number): string => {
|
||||||
|
return getUUID().replaceAll('-', '').substring(0, len);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除 xss
|
* 清除 xss
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,25 +4,16 @@
|
|||||||
<!-- 组合容器 -->
|
<!-- 组合容器 -->
|
||||||
<div class="combined-container">
|
<div class="combined-container">
|
||||||
<!-- 新建连接 -->
|
<!-- 新建连接 -->
|
||||||
<div class="combined-handler">
|
<div class="combined-handler" v-for="(handler, index) in combinedHandlers"
|
||||||
|
:key="index"
|
||||||
|
@click="clickHandlerItem(handler)">
|
||||||
<!-- 图标 -->
|
<!-- 图标 -->
|
||||||
<div class="combined-handler-icon">
|
<div class="combined-handler-icon">
|
||||||
<icon-plus />
|
<component :is="handler.icon" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 内容 -->
|
<!-- 内容 -->
|
||||||
<div class="combined-handler-text">
|
<div class="combined-handler-text">
|
||||||
新建连接
|
{{ handler.title }}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 主机列表 -->
|
|
||||||
<div class="combined-handler">
|
|
||||||
<!-- 图标 -->
|
|
||||||
<div class="combined-handler-icon">
|
|
||||||
<icon-desktop />
|
|
||||||
</div>
|
|
||||||
<!-- 内容 -->
|
|
||||||
<div class="combined-handler-text">
|
|
||||||
历史1
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,20 +28,83 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { TerminalTabItem, CombinedHandlerItem } from '../../types/terminal.type';
|
||||||
|
import type { HostQueryResponse } from '@/api/asset/host';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
|
import { InnerTabs, TerminalTabType } from '../../types/terminal.const';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
|
||||||
const { tabManager, hosts } = useTerminalStore();
|
const totalCount = 8;
|
||||||
|
const { tabManager, hosts, openTerminal } = useTerminalStore();
|
||||||
|
|
||||||
|
const combinedHandlers = ref<Array<CombinedHandlerItem>>([{
|
||||||
|
title: InnerTabs.NEW_CONNECTION.title,
|
||||||
|
settingTab: InnerTabs.NEW_CONNECTION,
|
||||||
|
type: TerminalTabType.SETTING,
|
||||||
|
icon: InnerTabs.NEW_CONNECTION.icon
|
||||||
|
}]);
|
||||||
|
|
||||||
|
// 点击组合操作元素
|
||||||
|
const clickHandlerItem = (item: CombinedHandlerItem) => {
|
||||||
|
if (item.type === TerminalTabType.SETTING) {
|
||||||
|
// 打开内置 tab
|
||||||
|
tabManager.openTab(item.settingTab as TerminalTabItem);
|
||||||
|
} else {
|
||||||
|
// 打开终端
|
||||||
|
openTerminal(item.host as HostQueryResponse);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 组合主机列表
|
||||||
|
onMounted(() => {
|
||||||
|
// 推荐的主机 tab
|
||||||
|
const combinedHosts = [
|
||||||
|
...new Set([
|
||||||
|
...hosts.latestHosts,
|
||||||
|
...hosts.hostList.filter(s => s.favorite).map(s => s.id),
|
||||||
|
...hosts.hostList.map(s => s.id)
|
||||||
|
])
|
||||||
|
].slice(0, totalCount - 1)
|
||||||
|
.map(s => hosts.hostList.find(t => t.id === s) as HostQueryResponse)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(s => {
|
||||||
|
return {
|
||||||
|
title: `${s.alias || s.name} (${s.address})`,
|
||||||
|
type: TerminalTabType.TERMINAL,
|
||||||
|
host: s,
|
||||||
|
icon: 'icon-desktop'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// 插入主机列表
|
||||||
|
combinedHandlers.value.push(...combinedHosts);
|
||||||
|
// 不足显示的行数用设置补充
|
||||||
|
if (totalCount - 1 - combinedHosts.length > 0) {
|
||||||
|
const fillTabs = Object.keys(InnerTabs)
|
||||||
|
.filter(s => s !== 'NEW_CONNECTION')
|
||||||
|
.map(s => get(InnerTabs, s) as TerminalTabItem)
|
||||||
|
.slice(0, totalCount - 1 - combinedHosts.length)
|
||||||
|
.map(s => {
|
||||||
|
return {
|
||||||
|
title: s.title,
|
||||||
|
settingTab: s,
|
||||||
|
type: TerminalTabType.SETTING,
|
||||||
|
icon: s.icon as string
|
||||||
|
};
|
||||||
|
});
|
||||||
|
combinedHandlers.value.push(...fillTabs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@handler-height: 48px;
|
@handler-height: 44px;
|
||||||
|
|
||||||
.combined-container {
|
.combined-container {
|
||||||
padding: 8px;
|
padding: 12px;
|
||||||
margin: 64px auto;
|
margin: 64px auto;
|
||||||
width: 448px;
|
width: 424px;
|
||||||
height: 448px;
|
height: 448px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -70,6 +124,12 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--color-content-text-1);
|
color: var(--color-content-text-1);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.04);
|
||||||
|
}
|
||||||
|
|
||||||
&-icon {
|
&-icon {
|
||||||
width: @handler-height;
|
width: @handler-height;
|
||||||
@@ -77,14 +137,22 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-text {
|
&-text {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: calc(100% - @handler-height - 12px);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
|
&-wrapper {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: pre;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
:key="tab.key"
|
:key="tab.key"
|
||||||
:title="tab.title">
|
:title="tab.title">
|
||||||
<!-- 设置 -->
|
<!-- 设置 -->
|
||||||
<template v-if="tab.type === TabType.SETTING">
|
<template v-if="tab.type === TerminalTabType.SETTING">
|
||||||
<!-- 新建连接 -->
|
<!-- 新建连接 -->
|
||||||
<new-connection-view v-if="tab.key === InnerTabs.NEW_CONNECTION.key" />
|
<new-connection-view v-if="tab.key === InnerTabs.NEW_CONNECTION.key" />
|
||||||
<!-- 显示设置 -->
|
<!-- 显示设置 -->
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<terminal-general-setting v-else-if="tab.key === InnerTabs.TERMINAL_SETTING.key" />
|
<terminal-general-setting v-else-if="tab.key === InnerTabs.TERMINAL_SETTING.key" />
|
||||||
</template>
|
</template>
|
||||||
<!-- 终端 -->
|
<!-- 终端 -->
|
||||||
<template v-else-if="tab.type === TabType.TERMINAL">
|
<template v-else-if="tab.type === TerminalTabType.TERMINAL">
|
||||||
<terminal-view :tab="tab" />
|
<terminal-view :tab="tab" />
|
||||||
</template>
|
</template>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TabType, InnerTabs } from '../../types/terminal.const';
|
import { TerminalTabType, InnerTabs } from '../../types/terminal.const';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
import EmptyRecommend from './empty-recommend.vue';
|
import EmptyRecommend from './empty-recommend.vue';
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
// 修改标题
|
// 修改标题
|
||||||
document.title = tab.title;
|
document.title = tab.title;
|
||||||
// terminal 自动聚焦
|
// terminal 自动聚焦
|
||||||
if (tab?.type === TabType.TERMINAL) {
|
if (tab?.type === TerminalTabType.TERMINAL) {
|
||||||
sessionManager.getSession(active)?.focus();
|
sessionManager.getSession(active)?.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
// 顶部操作
|
// 顶部操作
|
||||||
const topActions: Array<SidebarAction> = [
|
const topActions: Array<SidebarAction> = [
|
||||||
{
|
{
|
||||||
icon: 'icon-plus',
|
icon: InnerTabs.NEW_CONNECTION.icon,
|
||||||
content: InnerTabs.NEW_CONNECTION.title,
|
content: InnerTabs.NEW_CONNECTION.title,
|
||||||
click: () => tabManager.openTab(InnerTabs.NEW_CONNECTION)
|
click: () => tabManager.openTab(InnerTabs.NEW_CONNECTION)
|
||||||
},
|
},
|
||||||
@@ -37,22 +37,22 @@
|
|||||||
// 底部操作
|
// 底部操作
|
||||||
const bottomActions: Array<SidebarAction> = [
|
const bottomActions: Array<SidebarAction> = [
|
||||||
{
|
{
|
||||||
icon: 'icon-command',
|
icon: InnerTabs.SHORTCUT_SETTING.icon,
|
||||||
content: InnerTabs.SHORTCUT_SETTING.title,
|
content: InnerTabs.SHORTCUT_SETTING.title,
|
||||||
click: () => tabManager.openTab(InnerTabs.SHORTCUT_SETTING)
|
click: () => tabManager.openTab(InnerTabs.SHORTCUT_SETTING)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'icon-desktop',
|
icon: InnerTabs.DISPLAY_SETTING.icon,
|
||||||
content: InnerTabs.DISPLAY_SETTING.title,
|
content: InnerTabs.DISPLAY_SETTING.title,
|
||||||
click: () => tabManager.openTab(InnerTabs.DISPLAY_SETTING)
|
click: () => tabManager.openTab(InnerTabs.DISPLAY_SETTING)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'icon-palette',
|
icon: InnerTabs.THEME_SETTING.icon,
|
||||||
content: InnerTabs.THEME_SETTING.title,
|
content: InnerTabs.THEME_SETTING.title,
|
||||||
click: () => tabManager.openTab(InnerTabs.THEME_SETTING)
|
click: () => tabManager.openTab(InnerTabs.THEME_SETTING)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'icon-settings',
|
icon: InnerTabs.TERMINAL_SETTING.icon,
|
||||||
content: InnerTabs.TERMINAL_SETTING.title,
|
content: InnerTabs.TERMINAL_SETTING.title,
|
||||||
click: () => tabManager.openTab(InnerTabs.TERMINAL_SETTING)
|
click: () => tabManager.openTab(InnerTabs.TERMINAL_SETTING)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -168,7 +168,7 @@
|
|||||||
import { dataColor } from '@/utils';
|
import { dataColor } from '@/utils';
|
||||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||||
import { updateHostAlias } from '@/api/asset/host-extra';
|
import { updateHostAlias } from '@/api/asset/host-extra';
|
||||||
import { nextSessionId, openSshModalKey, TabType } from '../../types/terminal.const';
|
import { openSshModalKey } from '../../types/terminal.const';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
emptyValue: string
|
emptyValue: string
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { tabManager, addToLatestConnect } = useTerminalStore();
|
const { openTerminal } = useTerminalStore();
|
||||||
const { toggle: toggleFavorite, loading: favoriteLoading } = useFavorite('HOST');
|
const { toggle: toggleFavorite, loading: favoriteLoading } = useFavorite('HOST');
|
||||||
|
|
||||||
const aliasNameInput = ref();
|
const aliasNameInput = ref();
|
||||||
@@ -213,29 +213,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 打开终端
|
|
||||||
const openTerminal = (record: HostQueryResponse) => {
|
|
||||||
// 添加到最近连接
|
|
||||||
addToLatestConnect(record.id);
|
|
||||||
// 获取 seq
|
|
||||||
const tabSeqArr = tabManager.items
|
|
||||||
.map(s => s.seq)
|
|
||||||
.filter(Boolean)
|
|
||||||
.map(Number);
|
|
||||||
const nextSeq = tabSeqArr.length
|
|
||||||
? Math.max(...tabSeqArr) + 1
|
|
||||||
: 1;
|
|
||||||
// 打开 tab
|
|
||||||
tabManager.openTab({
|
|
||||||
type: TabType.TERMINAL,
|
|
||||||
key: nextSessionId(),
|
|
||||||
seq: nextSeq,
|
|
||||||
title: `(${nextSeq}) ${record.alias || record.name}`,
|
|
||||||
hostId: record.id,
|
|
||||||
address: record.address
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 打开配置
|
// 打开配置
|
||||||
const openSetting = inject<(record: HostQueryResponse) => void>(openSshModalKey) as any;
|
const openSetting = inject<(record: HostQueryResponse) => void>(openSshModalKey) as any;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="usn"
|
class="usn"
|
||||||
:options="toRadioOptions(newConnectionTypeKey)"
|
:options="toRadioOptions(newConnectionTypeKey)"
|
||||||
@change="s => updateTerminalPreference(PreferenceItem.NEW_CONNECTION_TYPE, s as string, true)" />
|
@change="s => updateTerminalPreference(TerminalPreferenceItem.NEW_CONNECTION_TYPE, s as string, true)" />
|
||||||
<!-- 过滤 -->
|
<!-- 过滤 -->
|
||||||
<a-auto-complete v-model="filterValue"
|
<a-auto-complete v-model="filterValue"
|
||||||
class="host-filter"
|
class="host-filter"
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
import { onBeforeMount, ref } from 'vue';
|
import { onBeforeMount, ref } from 'vue';
|
||||||
import { NewConnectionType, newConnectionTypeKey } from '../../types/terminal.const';
|
import { NewConnectionType, newConnectionTypeKey } from '../../types/terminal.const';
|
||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import { PreferenceItem } from '@/store/modules/terminal';
|
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||||
import { dataColor } from '@/utils';
|
import { dataColor } from '@/utils';
|
||||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||||
import HostsView from './hosts-view.vue';
|
import HostsView from './hosts-view.vue';
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
import type { SidebarAction } from '../../types/terminal.type';
|
import type { SidebarAction } from '../../types/terminal.type';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { PreferenceItem } from '@/store/modules/terminal';
|
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||||
import { ActionBarItems } from '../../types/terminal.const';
|
import { ActionBarItems } from '../../types/terminal.const';
|
||||||
import IconActions from '../layout/icon-actions.vue';
|
import IconActions from '../layout/icon-actions.vue';
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 同步
|
// 同步
|
||||||
updateTerminalPreference(PreferenceItem.ACTION_BAR_SETTING, formModel.value, true);
|
updateTerminalPreference(TerminalPreferenceItem.ACTION_BAR_SETTING, formModel.value, true);
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
// 右侧操作
|
// 右侧操作
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import { fontFamilyKey, fontSizeKey, fontWeightKey, fontFamilySuffix, cursorStyleKey } from '../../types/terminal.const';
|
import { fontFamilyKey, fontSizeKey, fontWeightKey, fontFamilySuffix, cursorStyleKey } from '../../types/terminal.const';
|
||||||
import { labelFilter } from '@/types/form';
|
import { labelFilter } from '@/types/form';
|
||||||
import { PreferenceItem } from '@/store/modules/terminal';
|
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||||
import TerminalExample from '../view-setting/terminal-example.vue';
|
import TerminalExample from '../view-setting/terminal-example.vue';
|
||||||
|
|
||||||
const { toOptions, toRadioOptions } = useDictStore();
|
const { toOptions, toRadioOptions } = useDictStore();
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 同步
|
// 同步
|
||||||
updateTerminalPreference(PreferenceItem.DISPLAY_SETTING, formModel.value, true);
|
updateTerminalPreference(TerminalPreferenceItem.DISPLAY_SETTING, formModel.value, true);
|
||||||
// 聚焦
|
// 聚焦
|
||||||
previewTerminal.value.term.focus();
|
previewTerminal.value.term.focus();
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
import type { TerminalInteractSetting } from '@/store/modules/terminal/types';
|
import type { TerminalInteractSetting } from '@/store/modules/terminal/types';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { PreferenceItem } from '@/store/modules/terminal';
|
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||||
import BlockSettingItem from './block-setting-item.vue';
|
import BlockSettingItem from './block-setting-item.vue';
|
||||||
|
|
||||||
const { preference, updateTerminalPreference } = useTerminalStore();
|
const { preference, updateTerminalPreference } = useTerminalStore();
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 同步
|
// 同步
|
||||||
updateTerminalPreference(PreferenceItem.INTERACT_SETTING, formModel.value, true);
|
updateTerminalPreference(TerminalPreferenceItem.INTERACT_SETTING, formModel.value, true);
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
import type { TerminalPluginsSetting } from '@/store/modules/terminal/types';
|
import type { TerminalPluginsSetting } from '@/store/modules/terminal/types';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { PreferenceItem } from '@/store/modules/terminal';
|
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||||
import BlockSettingItem from './block-setting-item.vue';
|
import BlockSettingItem from './block-setting-item.vue';
|
||||||
|
|
||||||
const { preference, updateTerminalPreference } = useTerminalStore();
|
const { preference, updateTerminalPreference } = useTerminalStore();
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 同步
|
// 同步
|
||||||
updateTerminalPreference(PreferenceItem.PLUGINS_SETTING, formModel.value, true);
|
updateTerminalPreference(TerminalPreferenceItem.PLUGINS_SETTING, formModel.value, true);
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
import type { TerminalSessionSetting } from '@/store/modules/terminal/types';
|
import type { TerminalSessionSetting } from '@/store/modules/terminal/types';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import { PreferenceItem } from '@/store/modules/terminal';
|
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||||
import { terminalEmulationTypeKey } from '../../types/terminal.const';
|
import { terminalEmulationTypeKey } from '../../types/terminal.const';
|
||||||
import BlockSettingItem from './block-setting-item.vue';
|
import BlockSettingItem from './block-setting-item.vue';
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 同步
|
// 同步
|
||||||
updateTerminalPreference(PreferenceItem.SESSION_SETTING, formModel.value, true);
|
updateTerminalPreference(TerminalPreferenceItem.SESSION_SETTING, formModel.value, true);
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -56,12 +56,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { PreferenceItem } from '@/store/modules/terminal';
|
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
||||||
import TerminalExample from './terminal-example.vue';
|
|
||||||
import { getPreference } from '@/api/user/preference';
|
import { getPreference } from '@/api/user/preference';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
|
import TerminalExample from './terminal-example.vue';
|
||||||
|
|
||||||
const { updateTerminalPreference } = useTerminalStore();
|
const { updateTerminalPreference } = useTerminalStore();
|
||||||
const { loading, setLoading } = useLoading();
|
const { loading, setLoading } = useLoading();
|
||||||
@@ -72,14 +72,14 @@
|
|||||||
// 选择主题
|
// 选择主题
|
||||||
const selectTheme = async (theme: TerminalTheme) => {
|
const selectTheme = async (theme: TerminalTheme) => {
|
||||||
currentThemeName.value = theme.name;
|
currentThemeName.value = theme.name;
|
||||||
await updateTerminalPreference(PreferenceItem.THEME, theme);
|
await updateTerminalPreference(TerminalPreferenceItem.THEME, theme);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载用户主题
|
// 加载用户主题
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await getPreference<Record<string, any>>('TERMINAL', [PreferenceItem.THEME]);
|
const { data } = await getPreference<Record<string, any>>('TERMINAL', [TerminalPreferenceItem.THEME]);
|
||||||
currentThemeName.value = data[PreferenceItem.THEME]?.name;
|
currentThemeName.value = data[TerminalPreferenceItem.THEME]?.name;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ export default class TerminalTabManager implements ITerminalTabManager {
|
|||||||
public items: Array<TerminalTabItem>;
|
public items: Array<TerminalTabItem>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// fixme
|
this.active = InnerTabs.NEW_CONNECTION.key;
|
||||||
// this.active = InnerTabs.NEW_CONNECTION.key;
|
|
||||||
this.active = undefined as unknown as string;
|
|
||||||
this.items = [InnerTabs.NEW_CONNECTION];
|
this.items = [InnerTabs.NEW_CONNECTION];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { getUUID } from '@/utils';
|
|
||||||
|
|
||||||
// tab 类型
|
// tab 类型
|
||||||
export const TabType = {
|
export const TerminalTabType = {
|
||||||
SETTING: 'setting',
|
SETTING: 'setting',
|
||||||
TERMINAL: 'terminal',
|
TERMINAL: 'terminal',
|
||||||
};
|
};
|
||||||
@@ -11,27 +9,32 @@ export const InnerTabs = {
|
|||||||
NEW_CONNECTION: {
|
NEW_CONNECTION: {
|
||||||
key: 'newConnection',
|
key: 'newConnection',
|
||||||
title: '新建连接',
|
title: '新建连接',
|
||||||
type: TabType.SETTING
|
icon: 'icon-plus',
|
||||||
|
type: TerminalTabType.SETTING
|
||||||
},
|
},
|
||||||
SHORTCUT_SETTING: {
|
SHORTCUT_SETTING: {
|
||||||
key: 'shortcutSetting',
|
key: 'shortcutSetting',
|
||||||
title: '快捷键设置',
|
title: '快捷键设置',
|
||||||
type: TabType.SETTING
|
icon: 'icon-command',
|
||||||
|
type: TerminalTabType.SETTING
|
||||||
},
|
},
|
||||||
DISPLAY_SETTING: {
|
DISPLAY_SETTING: {
|
||||||
key: 'displaySetting',
|
key: 'displaySetting',
|
||||||
title: '显示设置',
|
title: '显示设置',
|
||||||
type: TabType.SETTING
|
icon: 'icon-dice',
|
||||||
|
type: TerminalTabType.SETTING
|
||||||
},
|
},
|
||||||
THEME_SETTING: {
|
THEME_SETTING: {
|
||||||
key: 'themeSetting',
|
key: 'themeSetting',
|
||||||
title: '主题设置',
|
title: '主题设置',
|
||||||
type: TabType.SETTING
|
icon: 'icon-palette',
|
||||||
|
type: TerminalTabType.SETTING
|
||||||
},
|
},
|
||||||
TERMINAL_SETTING: {
|
TERMINAL_SETTING: {
|
||||||
key: 'terminalSetting',
|
key: 'terminalSetting',
|
||||||
title: '终端设置',
|
title: '终端设置',
|
||||||
type: TabType.SETTING
|
icon: 'icon-settings',
|
||||||
|
type: TerminalTabType.SETTING
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -124,11 +127,6 @@ export const ActionBarItems = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// 获取会话id
|
|
||||||
export const nextSessionId = (): string => {
|
|
||||||
return getUUID().replaceAll('-', '').substring(0, 10);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 打开 sshModal key
|
// 打开 sshModal key
|
||||||
export const openSshModalKey = Symbol();
|
export const openSshModalKey = Symbol();
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import type { WebLinksAddon } from 'xterm-addon-web-links';
|
|||||||
import type { SearchAddon } from 'xterm-addon-search';
|
import type { SearchAddon } from 'xterm-addon-search';
|
||||||
import type { ImageAddon } from 'xterm-addon-image';
|
import type { ImageAddon } from 'xterm-addon-image';
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
|
import type { HostQueryResponse } from '@/api/asset/host';
|
||||||
|
|
||||||
// 终端 tab 元素
|
// 终端 tab 元素
|
||||||
export interface TerminalTabItem {
|
export interface TerminalTabItem {
|
||||||
key: string;
|
key: string;
|
||||||
title: string;
|
title: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
icon?: string;
|
||||||
|
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
@@ -27,6 +29,15 @@ export interface SidebarAction {
|
|||||||
click: () => void;
|
click: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 组合操作元素
|
||||||
|
export interface CombinedHandlerItem {
|
||||||
|
icon: string,
|
||||||
|
type: string,
|
||||||
|
title: string;
|
||||||
|
settingTab?: TerminalTabItem;
|
||||||
|
host?: HostQueryResponse;
|
||||||
|
}
|
||||||
|
|
||||||
// ssh 额外配置
|
// ssh 额外配置
|
||||||
export interface SshExtraModel {
|
export interface SshExtraModel {
|
||||||
authType?: string;
|
authType?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user