feat. 终端主题设置.
This commit is contained in:
@@ -20,6 +20,11 @@ public enum PreferenceTypeEnum {
|
|||||||
*/
|
*/
|
||||||
SYSTEM("systemPreferenceStrategy"),
|
SYSTEM("systemPreferenceStrategy"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端偏好
|
||||||
|
*/
|
||||||
|
TERMINAL("terminalPreferenceStrategy"),
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
PreferenceTypeEnum(String beanName) {
|
PreferenceTypeEnum(String beanName) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import lombok.NoArgsConstructor;
|
|||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class AppPreferenceModel implements PreferenceModel {
|
public class SystemPreferenceModel implements PreferenceModel {
|
||||||
|
|
||||||
@Schema(description = "是否使用侧边菜单")
|
@Schema(description = "是否使用侧边菜单")
|
||||||
private Boolean menu;
|
private Boolean menu;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.orion.ops.module.infra.handler.preference.model;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端偏好模型
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/12/8 14:46
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class TerminalPreferenceModel implements PreferenceModel {
|
||||||
|
|
||||||
|
@Schema(description = "暗色主题")
|
||||||
|
private String darkTheme;
|
||||||
|
|
||||||
|
@Schema(description = "终端主题")
|
||||||
|
private JSONObject terminalTheme;
|
||||||
|
|
||||||
|
@Schema(description = "显示设置")
|
||||||
|
private JSONObject viewSetting;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.orion.ops.module.infra.handler.preference.strategy;
|
package com.orion.ops.module.infra.handler.preference.strategy;
|
||||||
|
|
||||||
import com.orion.ops.module.infra.handler.preference.model.AppPreferenceModel;
|
import com.orion.ops.module.infra.handler.preference.model.SystemPreferenceModel;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,11 +11,11 @@ import org.springframework.stereotype.Component;
|
|||||||
* @since 2023/10/8 13:48
|
* @since 2023/10/8 13:48
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class SystemPreferenceStrategy implements IPreferenceStrategy<AppPreferenceModel> {
|
public class SystemPreferenceStrategy implements IPreferenceStrategy<SystemPreferenceModel> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AppPreferenceModel getDefault() {
|
public SystemPreferenceModel getDefault() {
|
||||||
return AppPreferenceModel.builder()
|
return SystemPreferenceModel.builder()
|
||||||
.menu(true)
|
.menu(true)
|
||||||
.topMenu(false)
|
.topMenu(false)
|
||||||
.navbar(true)
|
.navbar(true)
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.orion.ops.module.infra.handler.preference.strategy;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.orion.ops.module.infra.handler.preference.model.TerminalPreferenceModel;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端偏好处理策略
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/12/8 14:46
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class TerminalPreferenceStrategy implements IPreferenceStrategy<TerminalPreferenceModel> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalPreferenceModel getDefault() {
|
||||||
|
return TerminalPreferenceModel.builder()
|
||||||
|
.darkTheme("dark")
|
||||||
|
.terminalTheme(new JSONObject())
|
||||||
|
.viewSetting(new JSONObject())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import useTabBarStore from './modules/tab-bar';
|
|||||||
import useCacheStore from './modules/cache';
|
import useCacheStore from './modules/cache';
|
||||||
import useTipsStore from './modules/tips';
|
import useTipsStore from './modules/tips';
|
||||||
import useDictStore from './modules/dict';
|
import useDictStore from './modules/dict';
|
||||||
|
import useTerminalStore from './modules/terminal';
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ export {
|
|||||||
useCacheStore,
|
useCacheStore,
|
||||||
useTipsStore,
|
useTipsStore,
|
||||||
useDictStore,
|
useDictStore,
|
||||||
|
useTerminalStore,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pinia;
|
export default pinia;
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ export type CacheType = 'users' | 'menus' | 'roles'
|
|||||||
export default defineStore('cache', {
|
export default defineStore('cache', {
|
||||||
state: (): CacheState => ({}),
|
state: (): CacheState => ({}),
|
||||||
|
|
||||||
getters: {},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
// 设置
|
// 设置
|
||||||
set(name: CacheType, value: any) {
|
set(name: CacheType, value: any) {
|
||||||
|
|||||||
68
orion-ops-ui/src/store/modules/terminal/index.ts
Normal file
68
orion-ops-ui/src/store/modules/terminal/index.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import type { TerminalPreference, TerminalState, TerminalTheme } from './types';
|
||||||
|
import { DarkTheme } from './types';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { getPreference, updatePreferencePartial } from '@/api/user/preference';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import { useDark } from '@vueuse/core';
|
||||||
|
import { DEFAULT_SCHEMA } from '@/views/host-ops/terminal/types/terminal.theme';
|
||||||
|
|
||||||
|
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',
|
||||||
|
terminalTheme: {} as TerminalTheme,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
// 修改暗色主题
|
||||||
|
changeDarkTheme(dark: boolean) {
|
||||||
|
this.isDarkTheme = dark;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载终端偏好
|
||||||
|
async fetchPreference() {
|
||||||
|
try {
|
||||||
|
const { data } = await getPreference<TerminalPreference>('TERMINAL');
|
||||||
|
// 设置默认终端主题
|
||||||
|
if (!data.config.terminalTheme?.name) {
|
||||||
|
data.config.terminalTheme = DEFAULT_SCHEMA;
|
||||||
|
}
|
||||||
|
this.preference = data.config;
|
||||||
|
// 设置暗色主题
|
||||||
|
const userDarkTheme = data.config.darkTheme;
|
||||||
|
if (userDarkTheme === DarkTheme.AUTO) {
|
||||||
|
this.isDarkTheme = data.config.terminalTheme?.dark === true;
|
||||||
|
} else {
|
||||||
|
this.isDarkTheme = userDarkTheme === DarkTheme.DARK;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Message.error('配置加载失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新终端偏好
|
||||||
|
async updatePreference(preference: TerminalPreference) {
|
||||||
|
try {
|
||||||
|
// 修改配置
|
||||||
|
await updatePreferencePartial({
|
||||||
|
type: 'TERMINAL',
|
||||||
|
config: preference
|
||||||
|
});
|
||||||
|
this.preference = preference;
|
||||||
|
Message.success('同步成功');
|
||||||
|
} catch (e) {
|
||||||
|
Message.error('同步失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
50
orion-ops-ui/src/store/modules/terminal/types.ts
Normal file
50
orion-ops-ui/src/store/modules/terminal/types.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
|
export interface TerminalState {
|
||||||
|
isDarkTheme: Ref<boolean>;
|
||||||
|
preference: TerminalPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 终端配置
|
||||||
|
export interface TerminalPreference {
|
||||||
|
darkTheme: string,
|
||||||
|
terminalTheme: TerminalTheme,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暗色主题
|
||||||
|
export const DarkTheme = {
|
||||||
|
DARK: 'dark',
|
||||||
|
LIGHT: 'light',
|
||||||
|
AUTO: 'auto'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 终端主题
|
||||||
|
export interface TerminalTheme {
|
||||||
|
name: string;
|
||||||
|
dark: boolean;
|
||||||
|
background: string;
|
||||||
|
foreground: string;
|
||||||
|
cursor: string;
|
||||||
|
cursorAccent?: string;
|
||||||
|
selectionInactiveBackground?: string;
|
||||||
|
selectionBackground?: string;
|
||||||
|
selectionForeground?: 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;
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SidebarAction } from '../../types/terminal.type';
|
import type { SidebarAction } from '../../types/terminal.const';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
<!-- 设置 -->
|
<!-- 设置 -->
|
||||||
<template v-if="tab.type === TabType.SETTING">
|
<template v-if="tab.type === TabType.SETTING">
|
||||||
<!-- 主题设置 -->
|
<!-- 主题设置 -->
|
||||||
<terminal-theme-setting v-if="tab.key === InnerTabs.THEME_SETTING.key"
|
<terminal-theme-setting v-if="tab.key === InnerTabs.THEME_SETTING.key" />
|
||||||
:preference="preference"
|
|
||||||
@emitter="dispatchEmitter" />
|
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ tab.title }}
|
{{ tab.title }}
|
||||||
</span>
|
</span>
|
||||||
@@ -35,11 +33,11 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import type { TabItem, TerminalPreference } from '../../types/terminal.type';
|
import type { TabItem } from '../../types/terminal.const';
|
||||||
|
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { TabType, InnerTabs } from '../../types/terminal.type';
|
import { TabType, InnerTabs } from '../../types/terminal.const';
|
||||||
import useEmitter from '@/hooks/emitter';
|
import TerminalThemeSetting from '../theme-setting/terminal-theme-setting.vue';
|
||||||
import TerminalThemeSetting from '../terminal-theme-setting.vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@@ -50,16 +48,8 @@
|
|||||||
type: Array as PropType<Array<TabItem>>,
|
type: Array as PropType<Array<TabItem>>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
preference: {
|
|
||||||
type: Object as PropType<TerminalPreference>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['changeDarkTheme']);
|
|
||||||
|
|
||||||
const { dispatchEmitter } = useEmitter(emits);
|
|
||||||
|
|
||||||
const activeKey = computed<String>({
|
const activeKey = computed<String>({
|
||||||
get() {
|
get() {
|
||||||
return props.modelValue;
|
return props.modelValue;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SidebarAction, TabItem } from '../../types/terminal.type';
|
import type { SidebarAction, TabItem } from '../../types/terminal.const';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { useFullscreen } from '@vueuse/core';
|
import { useFullscreen } from '@vueuse/core';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SidebarAction, } from '../../types/terminal.type';
|
import type { SidebarAction } from '../../types/terminal.const';
|
||||||
import { InnerTabs } from '../../types/terminal.type';
|
import { InnerTabs } from '../../types/terminal.const';
|
||||||
import IconActions from './icon-actions.vue';
|
import IconActions from './icon-actions.vue';
|
||||||
|
|
||||||
const emits = defineEmits(['switchTab', 'copyAddress']);
|
const emits = defineEmits(['switchTab', 'copyAddress']);
|
||||||
@@ -47,14 +47,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'icon-palette',
|
icon: 'icon-palette',
|
||||||
content: '主题设置',
|
content: '外观设置',
|
||||||
click: () => emits('switchTab', InnerTabs.THEME_SETTING)
|
click: () => emits('switchTab', InnerTabs.THEME_SETTING)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: 'icon-tool',
|
|
||||||
content: '显示设置',
|
|
||||||
click: () => emits('switchTab', InnerTabs.VIEW_SETTING)
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SidebarAction } from '../../types/terminal.type';
|
import type { SidebarAction } from '../../types/terminal.const';
|
||||||
import IconActions from './icon-actions.vue';
|
import IconActions from './icon-actions.vue';
|
||||||
|
|
||||||
const emits = defineEmits(['openSnippet', 'openSftp', 'openTransfer', 'openHistory', 'screenshot']);
|
const emits = defineEmits(['openSnippet', 'openSftp', 'openTransfer', 'openHistory', 'screenshot']);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="" class="terminal-example" ref="terminal"></div>
|
<div class="terminal-example" ref="terminal"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { TerminalTheme } from '@/store/modules/terminal/types';
|
||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { TerminalTheme } from '../types/terminal.theme';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
theme: TerminalTheme
|
theme: TerminalTheme
|
||||||
@@ -22,13 +22,13 @@
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
term.value = new Terminal({
|
term.value = new Terminal({
|
||||||
theme: props.theme,
|
theme: { ...props.theme, cursor: props.theme.background },
|
||||||
cols: 47,
|
cols: 47,
|
||||||
rows: 6,
|
rows: 6,
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
convertEol: true,
|
convertEol: true,
|
||||||
cursorBlink: false,
|
cursorBlink: false,
|
||||||
cursorInactiveStyle: 'none'
|
cursorInactiveStyle: 'none',
|
||||||
});
|
});
|
||||||
term.value.open(terminal.value);
|
term.value.open(terminal.value);
|
||||||
|
|
||||||
@@ -42,6 +42,8 @@
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineExpose({ term });
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
term.value?.dispose();
|
term.value?.dispose();
|
||||||
});
|
});
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="terminal-setting-container theme-setting-container">
|
<div class="terminal-setting-container theme-setting-container">
|
||||||
<div class="theme-setting-wrapper">
|
<div class="theme-setting-wrapper">
|
||||||
<!-- 大标题 -->
|
<!-- 主标题 -->
|
||||||
<h2 class="terminal-setting-title">
|
<h2 class="terminal-setting-title">
|
||||||
主题设置
|
外观设置
|
||||||
</h2>
|
</h2>
|
||||||
<!-- 切换主题 -->
|
<!-- 基础设置 -->
|
||||||
<div class="terminal-setting-block">
|
<div class="terminal-setting-block">
|
||||||
<!-- 顶部 -->
|
<!-- 顶部 -->
|
||||||
<div class="theme-subtitle-wrapper">
|
<div class="theme-subtitle-wrapper">
|
||||||
<h3 class="terminal-setting-subtitle">
|
<h3 class="terminal-setting-subtitle">
|
||||||
主题选择
|
字体设置
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 主题设置 -->
|
||||||
|
<div class="terminal-setting-block">
|
||||||
|
<!-- 顶部 -->
|
||||||
|
<div class="theme-subtitle-wrapper">
|
||||||
|
<h3 class="terminal-setting-subtitle">
|
||||||
|
主题设置
|
||||||
</h3>
|
</h3>
|
||||||
<a-radio-group :default-value="preference.darkTheme"
|
<a-radio-group :default-value="preference.darkTheme"
|
||||||
size="mini"
|
size="mini"
|
||||||
@@ -59,38 +68,38 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TerminalTheme } from '../types/terminal.theme';
|
import type { TerminalPreference, TerminalTheme } from '@/store/modules/terminal/types';
|
||||||
import type { TerminalPreference } from '../types/terminal.type';
|
import { DarkTheme, DarkThemeChangeSymbol, darkThemeKey } from '../../types/terminal.const';
|
||||||
import { DarkTheme, darkThemeKey } from '../types/terminal.type';
|
import ThemeSchema from '../../types/terminal.theme';
|
||||||
import ThemeSchema from '../types/terminal.theme';
|
|
||||||
import useEmitter from '@/hooks/emitter';
|
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import TerminalExample from './terminal-example.vue';
|
import TerminalExample from './terminal-example.vue';
|
||||||
import { updatePreferencePartial } from '@/api/user/preference';
|
import { updatePreferencePartial } from '@/api/user/preference';
|
||||||
|
import { inject, ref } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const preference = ref<TerminalPreference>({
|
||||||
preference: TerminalPreference
|
darkTheme: 'auto',
|
||||||
}>();
|
terminalTheme: {} as TerminalTheme,
|
||||||
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['emitter']);
|
const emits = defineEmits(['emitter']);
|
||||||
|
const changeLayoutTheme = inject(DarkThemeChangeSymbol) as (s: boolean) => void;
|
||||||
|
|
||||||
const { bubblesEmitter } = useEmitter(emits);
|
|
||||||
const { toOptions } = useDictStore();
|
const { toOptions } = useDictStore();
|
||||||
|
|
||||||
// 修改暗色主题
|
// 修改暗色主题
|
||||||
const changeDarkTheme = (value: string) => {
|
const changeDarkTheme = (value: string) => {
|
||||||
props.preference.darkTheme = value;
|
preference.value.darkTheme = value;
|
||||||
if (value === DarkTheme.DARK) {
|
if (value === DarkTheme.DARK) {
|
||||||
// 暗色
|
// 暗色
|
||||||
bubblesEmitter('changeDarkTheme', true);
|
changeLayoutTheme(true);
|
||||||
} else if (value === DarkTheme.LIGHT) {
|
} else if (value === DarkTheme.LIGHT) {
|
||||||
// 亮色
|
// 亮色
|
||||||
bubblesEmitter('changeDarkTheme', false);
|
changeLayoutTheme(false);
|
||||||
} else if (value === DarkTheme.AUTO) {
|
} else if (value === DarkTheme.AUTO) {
|
||||||
// 自动配色
|
// 自动配色
|
||||||
bubblesEmitter('changeDarkTheme', props.preference.terminalTheme.dark);
|
changeLayoutTheme(preference.value.terminalTheme.dark);
|
||||||
}
|
}
|
||||||
// 同步用户偏好
|
// 同步用户偏好
|
||||||
sync();
|
sync();
|
||||||
@@ -98,10 +107,10 @@
|
|||||||
|
|
||||||
// 选择终端主题
|
// 选择终端主题
|
||||||
const checkTheme = (theme: TerminalTheme) => {
|
const checkTheme = (theme: TerminalTheme) => {
|
||||||
props.preference.terminalTheme = theme;
|
preference.value.terminalTheme = theme;
|
||||||
// 切换主题配色
|
// 切换主题配色
|
||||||
if (props.preference.darkTheme === DarkTheme.AUTO) {
|
if (preference.value.darkTheme === DarkTheme.AUTO) {
|
||||||
bubblesEmitter('changeDarkTheme', theme.dark);
|
changeLayoutTheme(theme.dark);
|
||||||
}
|
}
|
||||||
// 同步用户偏好
|
// 同步用户偏好
|
||||||
sync();
|
sync();
|
||||||
@@ -112,7 +121,7 @@
|
|||||||
try {
|
try {
|
||||||
await updatePreferencePartial({
|
await updatePreferencePartial({
|
||||||
type: 'TERMINAL',
|
type: 'TERMINAL',
|
||||||
config: props.preference
|
config: preference
|
||||||
});
|
});
|
||||||
Message.success('同步成功');
|
Message.success('同步成功');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -16,9 +16,7 @@
|
|||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<div class="host-layout-content">
|
<div class="host-layout-content">
|
||||||
<terminal-content v-model="activeKey"
|
<terminal-content v-model="activeKey"
|
||||||
:tabs="tabs"
|
:tabs="tabs" />
|
||||||
:preference="preference as TerminalPreference"
|
|
||||||
@change-dark-theme="changeLayoutTheme" />
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 右侧操作栏 -->
|
<!-- 右侧操作栏 -->
|
||||||
<div class="host-layout-right">
|
<div class="host-layout-right">
|
||||||
@@ -35,12 +33,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TabItem, TerminalPreference } from './types/terminal.type';
|
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
||||||
import { ref, onBeforeMount } from 'vue';
|
import type { TabItem } from './types/terminal.const';
|
||||||
|
import { ref, onBeforeMount, provide } from 'vue';
|
||||||
import { useDark } from '@vueuse/core';
|
import { useDark } from '@vueuse/core';
|
||||||
import { TabType, InnerTabs, DarkTheme, dictKeys } from './types/terminal.type';
|
import { TabType, InnerTabs, DarkTheme, dictKeys, DarkThemeChangeSymbol } from './types/terminal.const';
|
||||||
import { DEFAULT_SCHEMA } from './types/terminal.theme';
|
import { DEFAULT_SCHEMA } from './types/terminal.theme';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import { getPreference } from '@/api/user/preference';
|
import { getPreference } from '@/api/user/preference';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import TerminalHeader from './components/layout/terminal-header.vue';
|
import TerminalHeader from './components/layout/terminal-header.vue';
|
||||||
@@ -51,14 +50,15 @@
|
|||||||
import '@xterm/xterm/css/xterm.css';
|
import '@xterm/xterm/css/xterm.css';
|
||||||
|
|
||||||
// 系统主题
|
// 系统主题
|
||||||
const darkTheme = useDark({
|
// const darkTheme = useDark({
|
||||||
selector: 'body',
|
// selector: 'body',
|
||||||
attribute: 'terminal-theme',
|
// attribute: 'terminal-theme',
|
||||||
valueDark: DarkTheme.DARK,
|
// valueDark: DarkTheme.DARK,
|
||||||
valueLight: DarkTheme.LIGHT,
|
// valueLight: DarkTheme.LIGHT,
|
||||||
initialValue: DarkTheme.DARK as any,
|
// initialValue: DarkTheme.DARK as any,
|
||||||
storageKey: null
|
// storageKey: null
|
||||||
});
|
// });
|
||||||
|
const terminalStore = useTerminalStore();
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
|
|
||||||
const render = ref(false);
|
const render = ref(false);
|
||||||
@@ -75,9 +75,11 @@
|
|||||||
|
|
||||||
// 切换系统主题
|
// 切换系统主题
|
||||||
const changeLayoutTheme = (dark: boolean) => {
|
const changeLayoutTheme = (dark: boolean) => {
|
||||||
darkTheme.value = dark;
|
terminalStore.changeDarkTheme(dark);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
provide(DarkThemeChangeSymbol, changeLayoutTheme);
|
||||||
|
|
||||||
// 点击 tab
|
// 点击 tab
|
||||||
const clickTab = (key: string) => {
|
const clickTab = (key: string) => {
|
||||||
activeKey.value = key;
|
activeKey.value = key;
|
||||||
@@ -104,24 +106,8 @@
|
|||||||
|
|
||||||
// 加载用户终端偏好
|
// 加载用户终端偏好
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
try {
|
await terminalStore.fetchPreference();
|
||||||
const { data } = await getPreference<TerminalPreference>('TERMINAL');
|
render.value = true;
|
||||||
// 设置默认终端主题
|
|
||||||
if (!data.config.terminalTheme?.name) {
|
|
||||||
data.config.terminalTheme = DEFAULT_SCHEMA;
|
|
||||||
}
|
|
||||||
preference.value = data.config;
|
|
||||||
// 设置暗色主题
|
|
||||||
const userDarkTheme = data.config.darkTheme;
|
|
||||||
if (userDarkTheme === DarkTheme.AUTO) {
|
|
||||||
changeLayoutTheme(data.config.terminalTheme?.dark === true);
|
|
||||||
} else {
|
|
||||||
changeLayoutTheme(userDarkTheme === DarkTheme.DARK);
|
|
||||||
}
|
|
||||||
render.value = true;
|
|
||||||
} catch (e) {
|
|
||||||
Message.error('配置加载失败');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 加载字典值
|
// 加载字典值
|
||||||
@@ -136,6 +122,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: var(--color-content-text-2);
|
||||||
|
|
||||||
&-header {
|
&-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
import type { TerminalTheme } from './terminal.theme';
|
|
||||||
|
|
||||||
// 暗色主题
|
// 暗色主题
|
||||||
export const DarkTheme = {
|
export const DarkTheme = {
|
||||||
@@ -8,12 +7,6 @@ export const DarkTheme = {
|
|||||||
AUTO: 'auto'
|
AUTO: 'auto'
|
||||||
};
|
};
|
||||||
|
|
||||||
// 用户终端偏好
|
|
||||||
export interface TerminalPreference {
|
|
||||||
darkTheme: string,
|
|
||||||
terminalTheme: TerminalTheme
|
|
||||||
}
|
|
||||||
|
|
||||||
// sidebar 操作类型
|
// sidebar 操作类型
|
||||||
export interface SidebarAction {
|
export interface SidebarAction {
|
||||||
icon: string;
|
icon: string;
|
||||||
@@ -46,11 +39,6 @@ export const InnerTabs = {
|
|||||||
title: '主题设置',
|
title: '主题设置',
|
||||||
type: TabType.SETTING
|
type: TabType.SETTING
|
||||||
},
|
},
|
||||||
VIEW_SETTING: {
|
|
||||||
key: 'viewSetting',
|
|
||||||
title: '显示设置',
|
|
||||||
type: TabType.SETTING
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// tab 元素
|
// tab 元素
|
||||||
@@ -62,6 +50,9 @@ export interface TabItem {
|
|||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 暗色主题切换标识
|
||||||
|
export const DarkThemeChangeSymbol = Symbol('DARK_THEME_CHANGE');
|
||||||
|
|
||||||
// 终端暗色模式 字典项
|
// 终端暗色模式 字典项
|
||||||
export const darkThemeKey = 'terminalDarkTheme';
|
export const darkThemeKey = 'terminalDarkTheme';
|
||||||
|
|
||||||
@@ -1,37 +1,8 @@
|
|||||||
// 主题
|
import type { TerminalTheme } from '@/store/modules/terminal/types';
|
||||||
export interface TerminalTheme {
|
|
||||||
name: string;
|
|
||||||
dark: boolean;
|
|
||||||
background: string;
|
|
||||||
foreground: string;
|
|
||||||
cursor: string;
|
|
||||||
cursorAccent?: string;
|
|
||||||
selectionInactiveBackground?: string;
|
|
||||||
selectionBackground?: string;
|
|
||||||
selectionForeground?: 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认配色
|
// 默认配色
|
||||||
export const DEFAULT_SCHEMA = {
|
export const DEFAULT_SCHEMA = {
|
||||||
name: 'frappe',
|
name: 'Frappe',
|
||||||
dark: true,
|
dark: true,
|
||||||
background: '#303446',
|
background: '#303446',
|
||||||
foreground: '#C6D0F5',
|
foreground: '#C6D0F5',
|
||||||
@@ -61,7 +32,7 @@ export const DEFAULT_SCHEMA = {
|
|||||||
export default [
|
export default [
|
||||||
DEFAULT_SCHEMA,
|
DEFAULT_SCHEMA,
|
||||||
{
|
{
|
||||||
name: 'latte',
|
name: 'Latte',
|
||||||
dark: false,
|
dark: false,
|
||||||
background: '#EFF1F5',
|
background: '#EFF1F5',
|
||||||
foreground: '#4C4F69',
|
foreground: '#4C4F69',
|
||||||
@@ -88,7 +59,7 @@ export default [
|
|||||||
brightWhite: '#BCC0CC'
|
brightWhite: '#BCC0CC'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'macchiato',
|
name: 'Macchiato',
|
||||||
dark: true,
|
dark: true,
|
||||||
background: '#24273A',
|
background: '#24273A',
|
||||||
foreground: '#CAD3F5',
|
foreground: '#CAD3F5',
|
||||||
@@ -115,7 +86,7 @@ export default [
|
|||||||
brightWhite: '#A5ADCB'
|
brightWhite: '#A5ADCB'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mocha',
|
name: 'Mocha',
|
||||||
dark: true,
|
dark: true,
|
||||||
background: '#1E1E2E',
|
background: '#1E1E2E',
|
||||||
foreground: '#CDD6F4',
|
foreground: '#CDD6F4',
|
||||||
@@ -142,7 +113,7 @@ export default [
|
|||||||
brightWhite: '#A6ADC8'
|
brightWhite: '#A6ADC8'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'AtomOneLight',
|
name: 'Atom One Light',
|
||||||
dark: false,
|
dark: false,
|
||||||
background: '#F9F9F9',
|
background: '#F9F9F9',
|
||||||
foreground: '#2A2C33',
|
foreground: '#2A2C33',
|
||||||
@@ -164,7 +135,7 @@ export default [
|
|||||||
brightWhite: '#FFFFFF'
|
brightWhite: '#FFFFFF'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'OneHalfDark',
|
name: 'One Half Dark',
|
||||||
dark: true,
|
dark: true,
|
||||||
background: '#282C34',
|
background: '#282C34',
|
||||||
foreground: '#DCDFE4',
|
foreground: '#DCDFE4',
|
||||||
@@ -186,7 +157,7 @@ export default [
|
|||||||
brightWhite: '#DCDFE4'
|
brightWhite: '#DCDFE4'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dracula',
|
name: 'Dracula',
|
||||||
dark: true,
|
dark: true,
|
||||||
background: '#282A36',
|
background: '#282A36',
|
||||||
foreground: '#F8F8F2',
|
foreground: '#F8F8F2',
|
||||||
|
|||||||
@@ -60,5 +60,4 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user