refactor: 分离终端配置.
This commit is contained in:
@@ -31,7 +31,6 @@ import com.orion.ops.module.asset.enums.HostSshAuthTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.config.model.HostSshConfigModel;
|
||||
import com.orion.ops.module.asset.handler.host.extra.model.HostSshExtraModel;
|
||||
import com.orion.ops.module.asset.service.HostConfigService;
|
||||
import com.orion.ops.module.asset.service.HostConnectLogService;
|
||||
import com.orion.ops.module.asset.service.HostExtraService;
|
||||
import com.orion.ops.module.asset.service.HostTerminalService;
|
||||
import com.orion.ops.module.infra.api.DataPermissionApi;
|
||||
@@ -57,7 +56,7 @@ import java.util.Optional;
|
||||
@Service
|
||||
public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
|
||||
private static final String TERMINAL_PATH = "/template/theme/terminal.theme.json";
|
||||
private static final String TERMINAL_PATH = "/theme/terminal.theme.json";
|
||||
|
||||
@Resource
|
||||
private HostConfigService hostConfigService;
|
||||
@@ -68,9 +67,6 @@ public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
@Resource
|
||||
private AssetAuthorizedDataServiceImpl assetAuthorizedDataService;
|
||||
|
||||
@Resource
|
||||
private HostConnectLogService hostConnectLogService;
|
||||
|
||||
@Resource
|
||||
private HostDAO hostDAO;
|
||||
|
||||
@@ -89,6 +85,7 @@ public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
@Override
|
||||
public JSONArray getTerminalThemes() {
|
||||
try (InputStream in = HostTerminalService.class.getResourceAsStream(TERMINAL_PATH)) {
|
||||
Valid.notNull(in, ErrorMessage.CONFIG_ABSENT);
|
||||
byte[] bytes = StreamReaders.readAllBytes(in);
|
||||
return JSONArray.parseArray(new String(bytes));
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"background": "#212121",
|
||||
"foreground": "#F8F8F2",
|
||||
"cursor": "#ECEFF4",
|
||||
"selectionBackground": "#F8F8F2",
|
||||
"selectionBackground": "#44475A",
|
||||
"black": "#21222C",
|
||||
"red": "#FF5555",
|
||||
"green": "#50FA7B",
|
||||
@@ -286,5 +286,53 @@
|
||||
"brightCyan": "#3AD5CE",
|
||||
"brightWhite": "#EEEEEC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Calamity",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#2F2833",
|
||||
"foreground": "#D5CED9",
|
||||
"cursor": "#D5CED9",
|
||||
"selectionBackground": "#7E6C88",
|
||||
"black": "#2F2833",
|
||||
"red": "#FC644D",
|
||||
"green": "#A5F69C",
|
||||
"yellow": "#E9D7A5",
|
||||
"blue": "#3B79C7",
|
||||
"cyan": "#74D3DE",
|
||||
"white": "#D5CED9",
|
||||
"brightBlack": "#7E6C88",
|
||||
"brightRed": "#FC644D",
|
||||
"brightGreen": "#A5F69C",
|
||||
"brightYellow": "#E9D7A5",
|
||||
"brightBlue": "#3B79C7",
|
||||
"brightCyan": "#74D3DE",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Tomorrow",
|
||||
"dark": false,
|
||||
"schema": {
|
||||
"background": "#FFFFFF",
|
||||
"foreground": "#4D4D4C",
|
||||
"cursor": "#4D4D4C",
|
||||
"selectionBackground": "#D6D6D6",
|
||||
"black": "#000000",
|
||||
"red": "#C82829",
|
||||
"green": "#718C00",
|
||||
"yellow": "#EAB700",
|
||||
"blue": "#4271AE",
|
||||
"cyan": "#3E999F",
|
||||
"white": "#FFFFFF",
|
||||
"brightBlack": "#000000",
|
||||
"brightRed": "#C82829",
|
||||
"brightGreen": "#718C00",
|
||||
"brightYellow": "#EAB700",
|
||||
"brightBlue": "#4271AE",
|
||||
"brightCyan": "#3E999F",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -16,13 +16,13 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 终端主题拉取 __META__
|
||||
* 终端主题生成 __META__
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/7 10:52
|
||||
*/
|
||||
public class TerminalThemeFetcher {
|
||||
public class TerminalThemeGenerator {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<File> files = Files1.listFiles("D:\\idea-project\\iTerm2-Color-Schemes\\vhs");
|
||||
@@ -33,7 +33,8 @@ public class TerminalThemeFetcher {
|
||||
"Dracula", "Dracula+",
|
||||
"Apple System Colors", "Builtin Tango Light",
|
||||
"Duotone Dark", "BlulocoLight",
|
||||
"Chester", "CLRS"
|
||||
"Chester", "CLRS",
|
||||
"Calamity", "Tomorrow"
|
||||
);
|
||||
// 颜色大写
|
||||
ValueFilter colorFilter = (Object object, String name, Object value) -> {
|
||||
@@ -13,6 +13,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -50,8 +51,10 @@ public class PreferenceController {
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "查询用户偏好")
|
||||
@Parameter(name = "type", description = "type", required = true)
|
||||
public Map<String, Object> getPreference(@RequestParam("type") String type) {
|
||||
return preferenceService.getPreferenceByType(type);
|
||||
@Parameter(name = "items", description = "items")
|
||||
public Map<String, Object> getPreference(@RequestParam("type") String type,
|
||||
@RequestParam(name = "items", required = false) List<String> items) {
|
||||
return preferenceService.getPreferenceByType(type, items);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdatePart
|
||||
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest;
|
||||
import com.orion.ops.module.infra.enums.PreferenceTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
@@ -34,10 +35,11 @@ public interface PreferenceService {
|
||||
/**
|
||||
* 查询用户偏好
|
||||
*
|
||||
* @param type type
|
||||
* @param type type
|
||||
* @param items items
|
||||
* @return rows
|
||||
*/
|
||||
Map<String, Object> getPreferenceByType(String type);
|
||||
Map<String, Object> getPreferenceByType(String type, List<String> items);
|
||||
|
||||
/**
|
||||
* 获取用户偏好
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.orion.ops.module.infra.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.orion.lang.function.Functions;
|
||||
import com.orion.lang.utils.Refs;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisMaps;
|
||||
@@ -125,10 +126,17 @@ public class PreferenceServiceImpl implements PreferenceService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getPreferenceByType(String type) {
|
||||
public Map<String, Object> getPreferenceByType(String type, List<String> items) {
|
||||
Long userId = SecurityUtils.getLoginUserId();
|
||||
PreferenceTypeEnum typeEnum = Valid.valid(PreferenceTypeEnum::of, type);
|
||||
return this.getPreferenceByCache(userId, typeEnum);
|
||||
// 查询缓存
|
||||
Map<String, Object> preference = this.getPreferenceByCache(userId, typeEnum);
|
||||
if (Lists.isEmpty(items)) {
|
||||
return preference;
|
||||
}
|
||||
Map<String, Object> partial = Maps.newMap();
|
||||
items.forEach(s -> partial.put(s, preference.get(s)));
|
||||
return partial;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
|
||||
type PreferenceType = 'SYSTEM' | 'TERMINAL'
|
||||
|
||||
@@ -36,7 +37,15 @@ export function updatePreferencePartial(request: PreferenceUpdatePartialRequest)
|
||||
/**
|
||||
* 查询用户偏好
|
||||
*/
|
||||
export function getPreference<T>(type: PreferenceType) {
|
||||
return axios.get<T>('/infra/preference/get', { params: { type } });
|
||||
export function getPreference<T>(type: PreferenceType, items: Array<string> | undefined = undefined) {
|
||||
return axios.get<T>('/infra/preference/get', {
|
||||
params: {
|
||||
type,
|
||||
items
|
||||
},
|
||||
paramsSerializer: params => {
|
||||
return qs.stringify(params, { arrayFormat: 'comma' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,27 @@
|
||||
import type { TerminalDisplaySetting, TerminalPreference, TerminalState, TerminalThemeSchema } from './types';
|
||||
import type { TerminalDisplaySetting, TerminalPreference, TerminalState } from './types';
|
||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
||||
import { defineStore } from 'pinia';
|
||||
import { getPreference, updatePreference } from '@/api/user/preference';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useDark } from '@vueuse/core';
|
||||
import TerminalTabManager from '@/views/host/terminal/handler/terminal-tab-manager';
|
||||
import TerminalSessionManager from '@/views/host/terminal/handler/terminal-session-manager';
|
||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
||||
|
||||
// 暗色主题
|
||||
export const DarkTheme = {
|
||||
DARK: 'dark',
|
||||
LIGHT: 'light',
|
||||
AUTO: 'auto'
|
||||
// 偏好项
|
||||
export const PreferenceItem = {
|
||||
// 新建连接类型
|
||||
NEW_CONNECTION_TYPE: 'newConnectionType',
|
||||
// 终端主题
|
||||
THEME: 'theme',
|
||||
// 显示设置
|
||||
DISPLAY_SETTING: 'displaySetting',
|
||||
};
|
||||
|
||||
export default defineStore('terminal', {
|
||||
state: (): TerminalState => ({
|
||||
isDarkTheme: useDark({
|
||||
selector: 'body',
|
||||
attribute: 'terminal-theme',
|
||||
valueDark: DarkTheme.DARK,
|
||||
valueLight: DarkTheme.LIGHT,
|
||||
initialValue: DarkTheme.DARK as any,
|
||||
storageKey: null
|
||||
}),
|
||||
preference: {
|
||||
darkTheme: 'auto',
|
||||
newConnectionType: 'group',
|
||||
displaySetting: {} as TerminalDisplaySetting,
|
||||
themeSchema: {} as TerminalThemeSchema,
|
||||
theme: {} as TerminalTheme
|
||||
},
|
||||
tabManager: new TerminalTabManager(),
|
||||
@@ -46,6 +38,8 @@ export default defineStore('terminal', {
|
||||
if (!data.theme) {
|
||||
const { data: themes } = await getTerminalThemes();
|
||||
data.theme = themes[0];
|
||||
// 更新默认主题偏好
|
||||
await this.updateTerminalPreference(PreferenceItem.THEME, data.theme);
|
||||
}
|
||||
this.preference = data;
|
||||
} catch (e) {
|
||||
@@ -53,51 +47,11 @@ export default defineStore('terminal', {
|
||||
}
|
||||
},
|
||||
|
||||
// 修改暗色主题
|
||||
// FIXME 删除 terminalDarkTheme
|
||||
async changeDarkTheme(darkTheme: string) {
|
||||
this.preference.darkTheme = darkTheme;
|
||||
if (darkTheme === DarkTheme.DARK) {
|
||||
// 暗色
|
||||
this.isDarkTheme = true;
|
||||
} else if (darkTheme === DarkTheme.LIGHT) {
|
||||
// 亮色
|
||||
this.isDarkTheme = false;
|
||||
} else if (darkTheme === DarkTheme.AUTO) {
|
||||
// 自动配色
|
||||
this.isDarkTheme = this.preference.themeSchema.dark;
|
||||
}
|
||||
// 同步配置
|
||||
await this.updateTerminalPreference('darkTheme', darkTheme);
|
||||
},
|
||||
|
||||
// 修改显示配置
|
||||
async changeDisplaySetting(displaySetting: TerminalDisplaySetting) {
|
||||
this.preference.displaySetting = displaySetting;
|
||||
// 同步配置
|
||||
await this.updateTerminalPreference('displaySetting', displaySetting);
|
||||
},
|
||||
|
||||
// 选择终端主题
|
||||
async changeThemeSchema(themeSchema: TerminalThemeSchema) {
|
||||
this.preference.themeSchema = themeSchema;
|
||||
// 切换主题配色
|
||||
if (this.preference.darkTheme === DarkTheme.AUTO) {
|
||||
this.isDarkTheme = themeSchema.dark;
|
||||
}
|
||||
// 同步配置
|
||||
await this.updateTerminalPreference('themeSchema', themeSchema);
|
||||
},
|
||||
|
||||
// 切换新建连接类型
|
||||
async changeNewConnectionType(newConnectionType: string) {
|
||||
this.preference.newConnectionType = newConnectionType;
|
||||
// 同步配置
|
||||
await this.updateTerminalPreference('newConnectionType', newConnectionType);
|
||||
},
|
||||
|
||||
// 更新终端偏好
|
||||
async updateTerminalPreference(item: string, value: any) {
|
||||
async updateTerminalPreference(item: string, value: any, setLocal = false) {
|
||||
if (setLocal) {
|
||||
this.preference[item as keyof TerminalPreference] = value;
|
||||
}
|
||||
try {
|
||||
// 修改配置
|
||||
await updatePreference({
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import type { Ref } from 'vue';
|
||||
import type { ITerminalSessionManager, ITerminalTabManager } from '@/views/host/terminal/types/terminal.type';
|
||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||
|
||||
export interface TerminalState {
|
||||
isDarkTheme: Ref<boolean>;
|
||||
preference: TerminalPreference;
|
||||
tabManager: ITerminalTabManager;
|
||||
sessionManager: ITerminalSessionManager;
|
||||
@@ -11,11 +9,9 @@ export interface TerminalState {
|
||||
|
||||
// 终端配置
|
||||
export interface TerminalPreference {
|
||||
darkTheme: string;
|
||||
newConnectionType: string;
|
||||
displaySetting: TerminalDisplaySetting;
|
||||
theme: TerminalTheme;
|
||||
themeSchema: TerminalThemeSchema;
|
||||
}
|
||||
|
||||
// 显示设置
|
||||
@@ -29,34 +25,3 @@ export interface TerminalDisplaySetting {
|
||||
cursorBlink?: boolean;
|
||||
}
|
||||
|
||||
// 终端主题
|
||||
export interface TerminalThemeSchema {
|
||||
name: string;
|
||||
dark: boolean;
|
||||
background: string;
|
||||
foreground: string;
|
||||
cursor: string;
|
||||
cursorAccent?: string;
|
||||
selectionBackground?: string;
|
||||
selectionForeground?: string;
|
||||
selectionInactiveBackground?: string;
|
||||
black: string;
|
||||
red: string;
|
||||
green: string;
|
||||
yellow: string;
|
||||
blue: string;
|
||||
magenta: string;
|
||||
cyan: string;
|
||||
white: string;
|
||||
brightBlack: string;
|
||||
brightRed: string;
|
||||
brightGreen: string;
|
||||
brightYellow: string;
|
||||
brightBlue: string;
|
||||
brightMagenta: string;
|
||||
brightCyan: string;
|
||||
brightWhite: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<template v-if="tab.type === TabType.SETTING">
|
||||
<!-- 新建连接 -->
|
||||
<new-connection-view v-if="tab.key === InnerTabs.NEW_CONNECTION.key" />
|
||||
<!-- 显示设置 -->
|
||||
<terminal-view-setting v-else-if="tab.key === InnerTabs.VIEW_SETTING.key" />
|
||||
<!-- 主题设置 -->
|
||||
<terminal-theme-setting v-else-if="tab.key === InnerTabs.THEME_SETTING.key" />
|
||||
</template>
|
||||
<!-- 终端 -->
|
||||
<template v-else-if="tab.type === TabType.TERMINAL">
|
||||
@@ -31,10 +31,10 @@
|
||||
import { TabType, InnerTabs } from '../../types/terminal.const';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { watch } from 'vue';
|
||||
import TerminalViewSetting from '../view-setting/terminal-view-setting.vue';
|
||||
import TerminalThemeSetting from '../view-setting/terminal-theme-setting.vue';
|
||||
import NewConnectionView from '../new-connection/new-connection-view.vue';
|
||||
|
||||
import TerminalView from '../xterm/terminal-view.vue';
|
||||
|
||||
const { tabManager, sessionManager } = useTerminalStore();
|
||||
|
||||
// 监听 tab 修改
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
const topActions: Array<SidebarAction> = [
|
||||
{
|
||||
icon: 'icon-plus',
|
||||
content: '新建连接',
|
||||
content: InnerTabs.NEW_CONNECTION.title,
|
||||
click: () => tabManager.openTab(InnerTabs.NEW_CONNECTION)
|
||||
},
|
||||
];
|
||||
@@ -38,13 +38,19 @@
|
||||
const bottomActions: Array<SidebarAction> = [
|
||||
{
|
||||
icon: 'icon-command',
|
||||
content: '快捷键设置',
|
||||
content: InnerTabs.SHORTCUT_SETTING.title,
|
||||
visible: false,
|
||||
click: () => tabManager.openTab(InnerTabs.SHORTCUT_SETTING)
|
||||
},
|
||||
{
|
||||
icon: 'icon-tool',
|
||||
content: InnerTabs.TOOL_SETTING.title,
|
||||
click: () => tabManager.openTab(InnerTabs.TOOL_SETTING)
|
||||
},
|
||||
{
|
||||
icon: 'icon-palette',
|
||||
content: '外观设置',
|
||||
click: () => tabManager.openTab(InnerTabs.VIEW_SETTING)
|
||||
content: InnerTabs.THEME_SETTING.title,
|
||||
click: () => tabManager.openTab(InnerTabs.THEME_SETTING)
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
type="button"
|
||||
class="usn"
|
||||
:options="toRadioOptions(newConnectionTypeKey)"
|
||||
@change="s => changeNewConnectionType(s as string)" />
|
||||
@change="s => updateTerminalPreference(PreferenceItem.NEW_CONNECTION_TYPE, s as string, true)" />
|
||||
<!-- 过滤 -->
|
||||
<a-auto-complete v-model="filterValue"
|
||||
class="host-filter"
|
||||
@@ -82,6 +82,7 @@
|
||||
import { NewConnectionType, newConnectionTypeKey } from '../../types/terminal.const';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { useAppStore, useDictStore, useTerminalStore } from '@/store';
|
||||
import { PreferenceItem } from '@/store/modules/terminal';
|
||||
import { dataColor } from '@/utils';
|
||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||
import { getLatestConnectHostId } from '@/api/asset/host-connect-log';
|
||||
@@ -89,7 +90,7 @@
|
||||
|
||||
const { loading, setLoading } = useLoading();
|
||||
const { toRadioOptions } = useDictStore();
|
||||
const { preference, changeNewConnectionType } = useTerminalStore();
|
||||
const { preference, updateTerminalPreference } = useTerminalStore();
|
||||
|
||||
const newConnectionType = ref(preference.newConnectionType || NewConnectionType.GROUP);
|
||||
const filterValue = ref('');
|
||||
|
||||
@@ -79,8 +79,8 @@
|
||||
<div class="terminal-example">
|
||||
<span class="terminal-example-label">预览效果</span>
|
||||
<div class="terminal-example-wrapper"
|
||||
:style="{ background: preference.themeSchema?.background }">
|
||||
<terminal-example :theme="preference.themeSchema"
|
||||
:style="{ background: preference.theme.schema.background }">
|
||||
<terminal-example :schema="preference.theme.schema"
|
||||
ref="previewTerminal" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,16 +100,17 @@
|
||||
import { useDictStore, useTerminalStore } from '@/store';
|
||||
import { fontFamilyKey, fontSizeKey, fontWeightKey, fontFamilySuffix, cursorStyleKey } from '../../types/terminal.const';
|
||||
import { labelFilter } from '@/types/form';
|
||||
import { PreferenceItem } from '@/store/modules/terminal';
|
||||
import TerminalExample from '../view-setting/terminal-example.vue';
|
||||
|
||||
const { toOptions, toRadioOptions } = useDictStore();
|
||||
const { preference, changeDisplaySetting } = useTerminalStore();
|
||||
const { preference, updateTerminalPreference } = useTerminalStore();
|
||||
|
||||
const previewTerminal = ref();
|
||||
const formModel = ref<TerminalDisplaySetting>({ ...preference.displaySetting });
|
||||
|
||||
// 监听主题变化 动态修改预览样式
|
||||
watch(() => preference.themeSchema, (v) => {
|
||||
watch(() => preference.theme, (v) => {
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
@@ -135,7 +136,7 @@
|
||||
}
|
||||
});
|
||||
// 同步
|
||||
changeDisplaySetting(formModel.value);
|
||||
updateTerminalPreference(PreferenceItem.DISPLAY_SETTING, formModel.value);
|
||||
// 聚焦
|
||||
previewTerminal.value.term.focus();
|
||||
}, { deep: true });
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TerminalThemeSchema } from '@/store/modules/terminal/types';
|
||||
import type { TerminalThemeSchema } from '@/api/asset/host-terminal';
|
||||
import { Terminal } from 'xterm';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
theme: TerminalThemeSchema | Record<string, any>
|
||||
schema: TerminalThemeSchema | Record<string, any>
|
||||
}>();
|
||||
|
||||
const terminal = ref();
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
onMounted(() => {
|
||||
term.value = new Terminal({
|
||||
theme: props.theme,
|
||||
theme: { ...props.schema, cursor: props.schema.background },
|
||||
cols: 42,
|
||||
rows: 6,
|
||||
fontSize: 15,
|
||||
@@ -33,9 +33,8 @@
|
||||
'[1;94m[root[0m@[1;96mOrionServer usr]#[0m\r\n' +
|
||||
'dr-xr-xr-x. 2 root root [0m[01;34mbin[0m\r\n' +
|
||||
'dr-xr-xr-x. 2 root root [01;34msbin[0m\r\n' +
|
||||
'drwxr-xr-x. 89 root root [01;34mshare[0m\r\n' +
|
||||
'drwxr-xr-x. 4 root root [01;34msrc[0m\r\n' +
|
||||
'lrwxrwxrwx. 1 root root [01;36mtmp[0m -> [30;42m../var/tmp[0m'
|
||||
'lrwxrwxrwx. 1 root root [01;36mtmp[0m -> [30;42m../var/tmp[0m '
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,41 +5,42 @@
|
||||
<h3 class="terminal-setting-subtitle">
|
||||
主题设置
|
||||
</h3>
|
||||
<!-- 暗色选择 -->
|
||||
<a-radio-group v-model="preference.darkTheme"
|
||||
class="usn"
|
||||
size="mini"
|
||||
type="button"
|
||||
:options="toRadioOptions(darkThemeKey)"
|
||||
@change="s => changeDarkTheme(s as string)">
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<!-- 加载中 -->
|
||||
<a-skeleton v-if="loading"
|
||||
class="skeleton-wrapper"
|
||||
:animation="true">
|
||||
<a-skeleton-line :rows="8" />
|
||||
</a-skeleton>
|
||||
<!-- 内容区域 -->
|
||||
<div class="terminal-setting-body terminal-theme-container">
|
||||
<div v-else class="terminal-setting-body terminal-theme-container">
|
||||
<!-- 提示 -->
|
||||
<a-alert class="mb16">选择后会立刻保存, 刷新页面生效</a-alert>
|
||||
<!-- 终端主题 -->
|
||||
<div class="theme-row"
|
||||
v-for="rowIndex in ThemeSchema.length / 2"
|
||||
v-for="rowIndex in themes.length / 2"
|
||||
:key="rowIndex">
|
||||
<a-card v-for="(theme, colIndex) in [ThemeSchema[(rowIndex - 1) * 2], ThemeSchema[(rowIndex - 1) * 2 + 1]]"
|
||||
<a-card v-for="(theme, colIndex) in [themes[(rowIndex - 1) * 2], themes[(rowIndex - 1) * 2 + 1]]"
|
||||
:key="theme.name"
|
||||
class="terminal-theme-card simple-card"
|
||||
:class="{
|
||||
'terminal-theme-card-check': theme.name === preference.themeSchema.name
|
||||
'terminal-theme-card-check': theme.name === currentThemeName
|
||||
}"
|
||||
:title="theme.name"
|
||||
:style="{
|
||||
background: theme.background,
|
||||
background: theme.schema.background,
|
||||
marginRight: colIndex === 0 ? '16px' : 0
|
||||
}"
|
||||
:header-style="{
|
||||
color: theme.dark ? 'rgba(255, 255, 255, .8)' : 'rgba(0, 0, 0, .8)',
|
||||
userSelect: 'none'
|
||||
}"
|
||||
@click="changeThemeSchema(theme)">
|
||||
@click="selectTheme(theme)">
|
||||
<!-- 样例 -->
|
||||
<terminal-example :theme="{ ...theme, cursor: theme.background }" />
|
||||
<terminal-example :schema="theme.schema" />
|
||||
<!-- 选中按钮 -->
|
||||
<icon-check class="theme-check-icon"
|
||||
v-show="theme.name === preference.themeSchema.name" />
|
||||
v-show="theme.name === currentThemeName" />
|
||||
</a-card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -53,13 +54,48 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { darkThemeKey } from '../../types/terminal.const';
|
||||
import ThemeSchema from '../../types/terminal.theme';
|
||||
import { useDictStore, useTerminalStore } from '@/store';
|
||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { PreferenceItem } from '@/store/modules/terminal';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
||||
import TerminalExample from './terminal-example.vue';
|
||||
import { getPreference } from '@/api/user/preference';
|
||||
import useLoading from '@/hooks/loading';
|
||||
|
||||
const { changeThemeSchema, changeDarkTheme, preference } = useTerminalStore();
|
||||
const { toRadioOptions } = useDictStore();
|
||||
const { updateTerminalPreference } = useTerminalStore();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const currentThemeName = ref();
|
||||
const themes = ref<Array<TerminalTheme>>([]);
|
||||
|
||||
// 选择主题
|
||||
const selectTheme = async (theme: TerminalTheme) => {
|
||||
currentThemeName.value = theme.name;
|
||||
await updateTerminalPreference(PreferenceItem.THEME, theme);
|
||||
};
|
||||
|
||||
// 加载用户主题
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const { data } = await getPreference<Record<string, any>>('TERMINAL', [PreferenceItem.THEME]);
|
||||
currentThemeName.value = data[PreferenceItem.THEME]?.name;
|
||||
} catch (e) {
|
||||
}
|
||||
});
|
||||
|
||||
// 加载主题列表
|
||||
onMounted(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 加载全部主题
|
||||
const { data } = await getTerminalThemes();
|
||||
themes.value = data;
|
||||
} catch (e) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="terminal-setting-container">
|
||||
<div class="terminal-setting-wrapper">
|
||||
<!-- 主标题 -->
|
||||
<h2 class="terminal-setting-title">主题设置</h2>
|
||||
<!-- 主题设置 -->
|
||||
<terminal-theme-block />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'TerminalThemeSetting'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TerminalThemeBlock from './terminal-theme-block.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -5,8 +5,6 @@
|
||||
<h2 class="terminal-setting-title">外观设置</h2>
|
||||
<!-- 显示设置 -->
|
||||
<terminal-display-block />
|
||||
<!-- 主题设置 -->
|
||||
<terminal-theme-block />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -19,7 +17,6 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TerminalDisplayBlock from './terminal-display-block.vue';
|
||||
import TerminalThemeBlock from './terminal-theme-block.vue';
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<!-- 终端 -->
|
||||
<div class="terminal-wrapper"
|
||||
:style="{
|
||||
background: themeSchema.background
|
||||
background: preference.theme.schema.background
|
||||
}">
|
||||
<div class="terminal-inst" ref="terminalRef" />
|
||||
</div>
|
||||
@@ -45,7 +45,7 @@
|
||||
<shell-editor-modal ref="modal"
|
||||
:closable="false"
|
||||
:body-style="{ padding: '16px 16px 16px 0' }"
|
||||
:dark="themeSchema.dark"
|
||||
:dark="preference.theme.dark"
|
||||
cancel-text="关闭"
|
||||
@ok="writeCommand(modal.getValue())"
|
||||
@cancel="focus" />
|
||||
@@ -64,9 +64,9 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { useDictStore, useTerminalStore } from '@/store';
|
||||
import useCopy from '@/hooks/copy';
|
||||
import IconActions from '@/views/host/terminal/components/layout/icon-actions.vue';
|
||||
import { connectStatusKey } from '../../types/terminal.const';
|
||||
import { adjustColor } from '@/utils';
|
||||
import IconActions from '../layout/icon-actions.vue';
|
||||
import ShellEditorModal from '@/components/view/shell-editor/shell-editor-modal.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -79,7 +79,6 @@
|
||||
|
||||
const modal = ref();
|
||||
const commandInput = ref();
|
||||
const themeSchema = preference.themeSchema;
|
||||
const terminalRef = ref();
|
||||
const session = ref<ITerminalSession>();
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class TerminalSession implements ITerminalSession {
|
||||
// 初始化实例
|
||||
this.inst = new Terminal({
|
||||
...(preference.displaySetting as any),
|
||||
theme: preference.themeSchema,
|
||||
theme: preference.theme.schema,
|
||||
fastScrollModifier: 'shift',
|
||||
fontFamily: preference.displaySetting.fontFamily + fontFamilySuffix,
|
||||
});
|
||||
|
||||
@@ -29,9 +29,14 @@ export const InnerTabs = {
|
||||
title: '快捷键设置',
|
||||
type: TabType.SETTING
|
||||
},
|
||||
VIEW_SETTING: {
|
||||
key: 'viewSetting',
|
||||
title: '外观设置',
|
||||
TOOL_SETTING: {
|
||||
key: 'toolSetting',
|
||||
title: '终端设置',
|
||||
type: TabType.SETTING
|
||||
},
|
||||
THEME_SETTING: {
|
||||
key: 'themeSetting',
|
||||
title: '主题设置',
|
||||
type: TabType.SETTING
|
||||
},
|
||||
};
|
||||
@@ -83,9 +88,6 @@ export const openSshModalKey = Symbol();
|
||||
// 字体后缀 兜底
|
||||
export const fontFamilySuffix = ',courier-new, courier, monospace';
|
||||
|
||||
// 终端暗色模式 字典项
|
||||
export const darkThemeKey = 'terminalDarkTheme';
|
||||
|
||||
// 终端字体样式
|
||||
export const fontFamilyKey = 'terminalFontFamily';
|
||||
|
||||
@@ -109,7 +111,7 @@ export const connectStatusKey = 'terminalConnectStatus';
|
||||
|
||||
// 加载的字典值
|
||||
export const dictKeys = [
|
||||
darkThemeKey, fontFamilyKey,
|
||||
fontFamilyKey,
|
||||
fontSizeKey, fontWeightKey,
|
||||
cursorStyleKey, newConnectionTypeKey,
|
||||
extraSshAuthTypeKey, connectStatusKey
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
import type { TerminalThemeSchema } from '@/store/modules/terminal/types';
|
||||
|
||||
// 默认配色
|
||||
export const DEFAULT_SCHEMA = {
|
||||
name: 'Frappe',
|
||||
dark: true,
|
||||
background: '#303446',
|
||||
foreground: '#C6D0F5',
|
||||
cursor: '#F2D5CF',
|
||||
cursorAccent: '#232634',
|
||||
selectionBackground: '#C9DDF0',
|
||||
selectionForeground: '#303446',
|
||||
// selectionInactiveBackground: 'rgba(98, 104, 128, 0.30078125)',
|
||||
black: '#51576D',
|
||||
red: '#E78284',
|
||||
green: '#A6D189',
|
||||
yellow: '#E5C890',
|
||||
blue: '#8CAAEE',
|
||||
magenta: '#F4B8E4',
|
||||
cyan: '#81C8BE',
|
||||
white: '#B5BFE2',
|
||||
brightBlack: '#626880',
|
||||
brightRed: '#E78284',
|
||||
brightGreen: '#A6D189',
|
||||
brightYellow: '#E5C890',
|
||||
brightBlue: '#8CAAEE',
|
||||
brightMagenta: '#F4B8E4',
|
||||
brightCyan: '#81C8BE',
|
||||
brightWhite: '#A5ADCE'
|
||||
};
|
||||
|
||||
export default [
|
||||
DEFAULT_SCHEMA,
|
||||
{
|
||||
name: 'Latte',
|
||||
dark: false,
|
||||
background: '#EFF1F5',
|
||||
foreground: '#4C4F69',
|
||||
cursor: '#DC8A78',
|
||||
cursorAccent: '#EFF1F5',
|
||||
selectionBackground: '#6C6F85',
|
||||
selectionForeground: '#EFF1F5',
|
||||
// selectionInactiveBackground: 'rgba(172, 176, 190, 0.30078125)',
|
||||
black: '#5C5F77',
|
||||
red: '#D20F39',
|
||||
green: '#40A02B',
|
||||
yellow: '#DF8E1D',
|
||||
blue: '#1E66F5',
|
||||
magenta: '#EA76CB',
|
||||
cyan: '#179299',
|
||||
white: '#ACB0BE',
|
||||
brightBlack: '#6C6F85',
|
||||
brightRed: '#D20F39',
|
||||
brightGreen: '#40A02B',
|
||||
brightYellow: '#DF8E1D',
|
||||
brightBlue: '#1E66F5',
|
||||
brightMagenta: '#EA76CB',
|
||||
brightCyan: '#179299',
|
||||
brightWhite: '#BCC0CC'
|
||||
},
|
||||
{
|
||||
name: 'Macchiato',
|
||||
dark: true,
|
||||
background: '#24273A',
|
||||
foreground: '#CAD3F5',
|
||||
cursor: '#F4DBD6',
|
||||
cursorAccent: '#181926',
|
||||
selectionBackground: '#A5ADCB',
|
||||
selectionForeground: '#24273A',
|
||||
// selectionInactiveBackground: 'rgba(91, 96, 120, 0.30078125)',
|
||||
black: '#494D64',
|
||||
red: '#ED8796',
|
||||
green: '#A6DA95',
|
||||
yellow: '#EED49F',
|
||||
blue: '#8AADF4',
|
||||
magenta: '#F5BDE6',
|
||||
cyan: '#8BD5CA',
|
||||
white: '#B8C0E0',
|
||||
brightBlack: '#5B6078',
|
||||
brightRed: '#ED8796',
|
||||
brightGreen: '#A6DA95',
|
||||
brightYellow: '#EED49F',
|
||||
brightBlue: '#8AADF4',
|
||||
brightMagenta: '#F5BDE6',
|
||||
brightCyan: '#8BD5CA',
|
||||
brightWhite: '#A5ADCB'
|
||||
},
|
||||
{
|
||||
name: 'Mocha',
|
||||
dark: true,
|
||||
background: '#1E1E2E',
|
||||
foreground: '#CDD6F4',
|
||||
cursor: '#F5E0DC',
|
||||
cursorAccent: '#11111B',
|
||||
selectionBackground: '#A6ADC8',
|
||||
selectionForeground: '#1E1E2E',
|
||||
// selectionInactiveBackground: 'rgba(88, 91, 112, 0.30078125)',
|
||||
black: '#45475A',
|
||||
red: '#F38BA8',
|
||||
green: '#A6E3A1',
|
||||
yellow: '#F9E2AF',
|
||||
blue: '#89B4FA',
|
||||
magenta: '#F5C2E7',
|
||||
cyan: '#94E2D5',
|
||||
white: '#BAC2DE',
|
||||
brightBlack: '#585B70',
|
||||
brightRed: '#F38BA8',
|
||||
brightGreen: '#A6E3A1',
|
||||
brightYellow: '#F9E2AF',
|
||||
brightBlue: '#89B4FA',
|
||||
brightMagenta: '#F5C2E7',
|
||||
brightCyan: '#94E2D5',
|
||||
brightWhite: '#A6ADC8'
|
||||
},
|
||||
{
|
||||
name: 'Atom One Light',
|
||||
dark: false,
|
||||
background: '#F9F9F9',
|
||||
foreground: '#2A2C33',
|
||||
cursor: '#BBBBBB',
|
||||
selectionBackground: '#EDEDED',
|
||||
black: '#000000',
|
||||
red: '#DE3E35',
|
||||
green: '#3F953A',
|
||||
yellow: '#D2B67C',
|
||||
blue: '#2F5AF3',
|
||||
cyan: '#3F953A',
|
||||
white: '#BBBBBB',
|
||||
brightBlack: '#000000',
|
||||
brightRed: '#DE3E35',
|
||||
brightGreen: '#3F953A',
|
||||
brightYellow: '#D2B67C',
|
||||
brightBlue: '#2F5AF3',
|
||||
brightCyan: '#3F953A',
|
||||
brightWhite: '#FFFFFF'
|
||||
},
|
||||
{
|
||||
name: 'One Half Dark',
|
||||
dark: true,
|
||||
background: '#282C34',
|
||||
foreground: '#DCDFE4',
|
||||
cursor: '#A3B3CC',
|
||||
selectionBackground: '#474E5D',
|
||||
black: '#282C34',
|
||||
red: '#E06C75',
|
||||
green: '#98C379',
|
||||
yellow: '#E5C07B',
|
||||
blue: '#61AFEF',
|
||||
cyan: '#56B6C2',
|
||||
white: '#DCDFE4',
|
||||
brightBlack: '#282C34',
|
||||
brightRed: '#E06C75',
|
||||
brightGreen: '#98C379',
|
||||
brightYellow: '#E5C07B',
|
||||
brightBlue: '#61AFEF',
|
||||
brightCyan: '#56B6C2',
|
||||
brightWhite: '#DCDFE4'
|
||||
},
|
||||
{
|
||||
name: 'Dracula',
|
||||
dark: true,
|
||||
background: '#282A36',
|
||||
foreground: '#F8F8F2',
|
||||
cursor: '#F8F8F2',
|
||||
cursorAccent: '#282A36',
|
||||
selectionBackground: '#50FA7B',
|
||||
selectionForeground: '#44475A',
|
||||
black: '#21222C',
|
||||
red: '#FF5555',
|
||||
green: '#50FA7B',
|
||||
yellow: '#F1FA8C',
|
||||
blue: '#BD93F9',
|
||||
magenta: '#FF79C6',
|
||||
cyan: '#8BE9FD',
|
||||
white: '#F8F8F2',
|
||||
brightBlack: '#6272A4',
|
||||
brightRed: '#FF6E6E',
|
||||
brightGreen: '#69FF94',
|
||||
brightYellow: '#FFFFA5',
|
||||
brightBlue: '#D6ACFF',
|
||||
brightMagenta: '#FF92DF',
|
||||
brightCyan: '#A4FFFF',
|
||||
brightWhite: '#FFFFFF'
|
||||
},
|
||||
{
|
||||
name: 'Solarized Light',
|
||||
dark: false,
|
||||
background: '#FDF6E3',
|
||||
foreground: '#657B83',
|
||||
cursor: '#657B83',
|
||||
selectionBackground: '#E6DDC3',
|
||||
black: '#073642',
|
||||
red: '#DC322F',
|
||||
green: '#859900',
|
||||
yellow: '#B58900',
|
||||
blue: '#268BD2',
|
||||
cyan: '#2AA198',
|
||||
white: '#EEE8D5',
|
||||
brightBlack: '#002B36',
|
||||
brightRed: '#CB4B16',
|
||||
brightGreen: '#586E75',
|
||||
brightYellow: '#657B83',
|
||||
brightBlue: '#839496',
|
||||
brightCyan: '#93A1A1',
|
||||
brightWhite: '#FDF6E3'
|
||||
},
|
||||
{
|
||||
name: 'Material Design',
|
||||
dark: true,
|
||||
background: '#1D262A',
|
||||
foreground: '#E7EBED',
|
||||
cursor: '#EAEAEA',
|
||||
selectionBackground: '#4E6A78',
|
||||
black: '#435B67',
|
||||
red: '#FC3841',
|
||||
green: '#5CF19E',
|
||||
yellow: '#FED032',
|
||||
blue: '#37B6FF',
|
||||
cyan: '#59FFD1',
|
||||
white: '#FFFFFF',
|
||||
brightBlack: '#A1B0B8',
|
||||
brightRed: '#FC746D',
|
||||
brightGreen: '#ADF7BE',
|
||||
brightYellow: '#FEE16C',
|
||||
brightBlue: '#70CFFF',
|
||||
brightCyan: '#9AFFE6',
|
||||
brightWhite: '#FFFFFF'
|
||||
},
|
||||
{
|
||||
name: 'Duotone Dark',
|
||||
dark: true,
|
||||
background: '#1F1D27',
|
||||
foreground: '#B7A1FF',
|
||||
cursor: '#FF9839',
|
||||
selectionBackground: '#353147',
|
||||
black: '#1F1D27',
|
||||
red: '#D9393E',
|
||||
green: '#2DCD73',
|
||||
yellow: '#D9B76E',
|
||||
blue: '#FFC284',
|
||||
cyan: '#2488FF',
|
||||
white: '#B7A1FF',
|
||||
brightBlack: '#353147',
|
||||
brightRed: '#D9393E',
|
||||
brightGreen: '#2DCD73',
|
||||
brightYellow: '#D9B76E',
|
||||
brightBlue: '#FFC284',
|
||||
brightCyan: '#2488FF',
|
||||
brightWhite: '#EAE5FF'
|
||||
}
|
||||
] as Array<TerminalThemeSchema>;
|
||||
Reference in New Issue
Block a user