feat. 终端主题设置.

This commit is contained in:
lijiahang
2023-12-08 19:05:00 +08:00
parent 73dd7cd3d5
commit fc81c78849
20 changed files with 267 additions and 142 deletions

View File

@@ -27,7 +27,7 @@
</script>
<script lang="ts" setup>
import type { SidebarAction } from '../../types/terminal.type';
import type { SidebarAction } from '../../types/terminal.const';
import type { PropType } from 'vue';
defineProps({

View File

@@ -8,9 +8,7 @@
<!-- 设置 -->
<template v-if="tab.type === TabType.SETTING">
<!-- 主题设置 -->
<terminal-theme-setting v-if="tab.key === InnerTabs.THEME_SETTING.key"
:preference="preference"
@emitter="dispatchEmitter" />
<terminal-theme-setting v-if="tab.key === InnerTabs.THEME_SETTING.key" />
<span v-else>
{{ tab.title }}
</span>
@@ -35,11 +33,11 @@
<script lang="ts" setup>
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 { TabType, InnerTabs } from '../../types/terminal.type';
import useEmitter from '@/hooks/emitter';
import TerminalThemeSetting from '../terminal-theme-setting.vue';
import { TabType, InnerTabs } from '../../types/terminal.const';
import TerminalThemeSetting from '../theme-setting/terminal-theme-setting.vue';
const props = defineProps({
modelValue: {
@@ -50,16 +48,8 @@
type: Array as PropType<Array<TabItem>>,
required: true
},
preference: {
type: Object as PropType<TerminalPreference>,
required: true
},
});
const emits = defineEmits(['changeDarkTheme']);
const { dispatchEmitter } = useEmitter(emits);
const activeKey = computed<String>({
get() {
return props.modelValue;

View File

@@ -46,7 +46,7 @@
</script>
<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 { useFullscreen } from '@vueuse/core';
import { computed } from 'vue';

View File

@@ -18,8 +18,8 @@
</script>
<script lang="ts" setup>
import type { SidebarAction, } from '../../types/terminal.type';
import { InnerTabs } from '../../types/terminal.type';
import type { SidebarAction } from '../../types/terminal.const';
import { InnerTabs } from '../../types/terminal.const';
import IconActions from './icon-actions.vue';
const emits = defineEmits(['switchTab', 'copyAddress']);
@@ -47,14 +47,9 @@
},
{
icon: 'icon-palette',
content: '主题设置',
content: '外观设置',
click: () => emits('switchTab', InnerTabs.THEME_SETTING)
},
{
icon: 'icon-tool',
content: '显示设置',
click: () => emits('switchTab', InnerTabs.VIEW_SETTING)
},
];
</script>

View File

@@ -18,7 +18,7 @@
</script>
<script lang="ts" setup>
import type { SidebarAction } from '../../types/terminal.type';
import type { SidebarAction } from '../../types/terminal.const';
import IconActions from './icon-actions.vue';
const emits = defineEmits(['openSnippet', 'openSftp', 'openTransfer', 'openHistory', 'screenshot']);

View File

@@ -1,5 +1,5 @@
<template>
<div id="" class="terminal-example" ref="terminal"></div>
<div class="terminal-example" ref="terminal"></div>
</template>
<script lang="ts">
@@ -9,9 +9,9 @@
</script>
<script lang="ts" setup>
import type { TerminalTheme } from '@/store/modules/terminal/types';
import { Terminal } from '@xterm/xterm';
import { onMounted, onUnmounted, ref } from 'vue';
import { TerminalTheme } from '../types/terminal.theme';
const props = defineProps<{
theme: TerminalTheme
@@ -22,13 +22,13 @@
onMounted(() => {
term.value = new Terminal({
theme: props.theme,
theme: { ...props.theme, cursor: props.theme.background },
cols: 47,
rows: 6,
fontSize: 15,
convertEol: true,
cursorBlink: false,
cursorInactiveStyle: 'none'
cursorInactiveStyle: 'none',
});
term.value.open(terminal.value);
@@ -42,6 +42,8 @@
);
});
defineExpose({ term });
onUnmounted(() => {
term.value?.dispose();
});

View File

@@ -1,16 +1,25 @@
<template>
<div class="terminal-setting-container theme-setting-container">
<div class="theme-setting-wrapper">
<!-- 标题 -->
<!-- 标题 -->
<h2 class="terminal-setting-title">
主题设置
外观设置
</h2>
<!-- 切换主题 -->
<!-- 基础设置 -->
<div class="terminal-setting-block">
<!-- 顶部 -->
<div class="theme-subtitle-wrapper">
<h3 class="terminal-setting-subtitle">
主题选择
字体设置
</h3>
</div>
</div>
<!-- 主题设置 -->
<div class="terminal-setting-block">
<!-- 顶部 -->
<div class="theme-subtitle-wrapper">
<h3 class="terminal-setting-subtitle">
主题设置
</h3>
<a-radio-group :default-value="preference.darkTheme"
size="mini"
@@ -59,38 +68,38 @@
</script>
<script lang="ts" setup>
import type { TerminalTheme } from '../types/terminal.theme';
import type { TerminalPreference } from '../types/terminal.type';
import { DarkTheme, darkThemeKey } from '../types/terminal.type';
import ThemeSchema from '../types/terminal.theme';
import useEmitter from '@/hooks/emitter';
import type { TerminalPreference, TerminalTheme } from '@/store/modules/terminal/types';
import { DarkTheme, DarkThemeChangeSymbol, darkThemeKey } from '../../types/terminal.const';
import ThemeSchema from '../../types/terminal.theme';
import { useDebounceFn } from '@vueuse/core';
import { useDictStore } from '@/store';
import { Message } from '@arco-design/web-vue';
import TerminalExample from './terminal-example.vue';
import { updatePreferencePartial } from '@/api/user/preference';
import { inject, ref } from 'vue';
const props = defineProps<{
preference: TerminalPreference
}>();
const preference = ref<TerminalPreference>({
darkTheme: 'auto',
terminalTheme: {} as TerminalTheme,
});
const emits = defineEmits(['emitter']);
const changeLayoutTheme = inject(DarkThemeChangeSymbol) as (s: boolean) => void;
const { bubblesEmitter } = useEmitter(emits);
const { toOptions } = useDictStore();
//
const changeDarkTheme = (value: string) => {
props.preference.darkTheme = value;
preference.value.darkTheme = value;
if (value === DarkTheme.DARK) {
//
bubblesEmitter('changeDarkTheme', true);
changeLayoutTheme(true);
} else if (value === DarkTheme.LIGHT) {
//
bubblesEmitter('changeDarkTheme', false);
changeLayoutTheme(false);
} else if (value === DarkTheme.AUTO) {
//
bubblesEmitter('changeDarkTheme', props.preference.terminalTheme.dark);
changeLayoutTheme(preference.value.terminalTheme.dark);
}
//
sync();
@@ -98,10 +107,10 @@
//
const checkTheme = (theme: TerminalTheme) => {
props.preference.terminalTheme = theme;
preference.value.terminalTheme = theme;
//
if (props.preference.darkTheme === DarkTheme.AUTO) {
bubblesEmitter('changeDarkTheme', theme.dark);
if (preference.value.darkTheme === DarkTheme.AUTO) {
changeLayoutTheme(theme.dark);
}
//
sync();
@@ -112,7 +121,7 @@
try {
await updatePreferencePartial({
type: 'TERMINAL',
config: props.preference
config: preference
});
Message.success('同步成功');
} catch (e) {

View File

@@ -16,9 +16,7 @@
<!-- 内容区域 -->
<div class="host-layout-content">
<terminal-content v-model="activeKey"
:tabs="tabs"
:preference="preference as TerminalPreference"
@change-dark-theme="changeLayoutTheme" />
:tabs="tabs" />
</div>
<!-- 右侧操作栏 -->
<div class="host-layout-right">
@@ -35,12 +33,13 @@
</script>
<script lang="ts" setup>
import type { TabItem, TerminalPreference } from './types/terminal.type';
import { ref, onBeforeMount } from 'vue';
import type { TerminalPreference } from '@/store/modules/terminal/types';
import type { TabItem } from './types/terminal.const';
import { ref, onBeforeMount, provide } from 'vue';
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 { useDictStore } from '@/store';
import { useDictStore, useTerminalStore } from '@/store';
import { getPreference } from '@/api/user/preference';
import { Message } from '@arco-design/web-vue';
import TerminalHeader from './components/layout/terminal-header.vue';
@@ -51,14 +50,15 @@
import '@xterm/xterm/css/xterm.css';
// 系统主题
const darkTheme = useDark({
selector: 'body',
attribute: 'terminal-theme',
valueDark: DarkTheme.DARK,
valueLight: DarkTheme.LIGHT,
initialValue: DarkTheme.DARK as any,
storageKey: null
});
// const darkTheme = useDark({
// selector: 'body',
// attribute: 'terminal-theme',
// valueDark: DarkTheme.DARK,
// valueLight: DarkTheme.LIGHT,
// initialValue: DarkTheme.DARK as any,
// storageKey: null
// });
const terminalStore = useTerminalStore();
const dictStore = useDictStore();
const render = ref(false);
@@ -75,9 +75,11 @@
// 切换系统主题
const changeLayoutTheme = (dark: boolean) => {
darkTheme.value = dark;
terminalStore.changeDarkTheme(dark);
};
provide(DarkThemeChangeSymbol, changeLayoutTheme);
// 点击 tab
const clickTab = (key: string) => {
activeKey.value = key;
@@ -104,24 +106,8 @@
// 加载用户终端偏好
onBeforeMount(async () => {
try {
const { data } = await getPreference<TerminalPreference>('TERMINAL');
// 设置默认终端主题
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('配置加载失败');
}
await terminalStore.fetchPreference();
render.value = true;
});
// 加载字典值
@@ -136,6 +122,7 @@
width: 100%;
height: 100vh;
position: relative;
color: var(--color-content-text-2);
&-header {
width: 100%;

View File

@@ -1,5 +1,4 @@
import type { CSSProperties } from 'vue';
import type { TerminalTheme } from './terminal.theme';
// 暗色主题
export const DarkTheme = {
@@ -8,12 +7,6 @@ export const DarkTheme = {
AUTO: 'auto'
};
// 用户终端偏好
export interface TerminalPreference {
darkTheme: string,
terminalTheme: TerminalTheme
}
// sidebar 操作类型
export interface SidebarAction {
icon: string;
@@ -46,11 +39,6 @@ export const InnerTabs = {
title: '主题设置',
type: TabType.SETTING
},
VIEW_SETTING: {
key: 'viewSetting',
title: '显示设置',
type: TabType.SETTING
},
};
// tab 元素
@@ -62,6 +50,9 @@ export interface TabItem {
[key: string]: unknown;
}
// 暗色主题切换标识
export const DarkThemeChangeSymbol = Symbol('DARK_THEME_CHANGE');
// 终端暗色模式 字典项
export const darkThemeKey = 'terminalDarkTheme';

View File

@@ -1,37 +1,8 @@
// 主题
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;
}
import type { TerminalTheme } from '@/store/modules/terminal/types';
// 默认配色
export const DEFAULT_SCHEMA = {
name: 'frappe',
name: 'Frappe',
dark: true,
background: '#303446',
foreground: '#C6D0F5',
@@ -61,7 +32,7 @@ export const DEFAULT_SCHEMA = {
export default [
DEFAULT_SCHEMA,
{
name: 'latte',
name: 'Latte',
dark: false,
background: '#EFF1F5',
foreground: '#4C4F69',
@@ -88,7 +59,7 @@ export default [
brightWhite: '#BCC0CC'
},
{
name: 'macchiato',
name: 'Macchiato',
dark: true,
background: '#24273A',
foreground: '#CAD3F5',
@@ -115,7 +86,7 @@ export default [
brightWhite: '#A5ADCB'
},
{
name: 'mocha',
name: 'Mocha',
dark: true,
background: '#1E1E2E',
foreground: '#CDD6F4',
@@ -142,7 +113,7 @@ export default [
brightWhite: '#A6ADC8'
},
{
name: 'AtomOneLight',
name: 'Atom One Light',
dark: false,
background: '#F9F9F9',
foreground: '#2A2C33',
@@ -164,7 +135,7 @@ export default [
brightWhite: '#FFFFFF'
},
{
name: 'OneHalfDark',
name: 'One Half Dark',
dark: true,
background: '#282C34',
foreground: '#DCDFE4',
@@ -186,7 +157,7 @@ export default [
brightWhite: '#DCDFE4'
},
{
name: 'dracula',
name: 'Dracula',
dark: true,
background: '#282A36',
foreground: '#F8F8F2',

View File

@@ -60,5 +60,4 @@
</script>
<style lang="less" scoped>
</style>