refactor: 重构主题修改部分.

This commit is contained in:
lijiahangmax
2023-12-08 22:23:13 +08:00
parent fc81c78849
commit b9e38a85f8
12 changed files with 292 additions and 268 deletions

View File

@@ -1,11 +1,17 @@
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 const DarkTheme = {
DARK: 'dark',
LIGHT: 'light',
AUTO: 'auto'
};
export default defineStore('terminal', {
state: (): TerminalState => ({
isDarkTheme: useDark({
@@ -50,15 +56,13 @@ export default defineStore('terminal', {
},
// 更新终端偏好
async updatePreference(preference: TerminalPreference) {
async updatePreference() {
try {
// 修改配置
await updatePreferencePartial({
type: 'TERMINAL',
config: preference
config: this.preference
});
this.preference = preference;
Message.success('同步成功');
} catch (e) {
Message.error('同步失败');
}

View File

@@ -11,13 +11,6 @@ export interface TerminalPreference {
terminalTheme: TerminalTheme,
}
// 暗色主题
export const DarkTheme = {
DARK: 'dark',
LIGHT: 'light',
AUTO: 'auto'
};
// 终端主题
export interface TerminalTheme {
name: string;

View File

@@ -222,8 +222,19 @@ body[terminal-theme='dark'] .host-layout {
margin-bottom: 18px;
}
.terminal-setting-subtitle-wrapper {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.terminal-setting-subtitle {
margin: 0 0 16px 0;
margin: 0;
color: var(--color-content-text-3);
}
.terminal-setting-body {
margin: 16px 0 24px 0;
}
}

View File

@@ -7,8 +7,8 @@
:title="tab.title">
<!-- 设置 -->
<template v-if="tab.type === TabType.SETTING">
<!-- 主题设置 -->
<terminal-theme-setting v-if="tab.key === InnerTabs.THEME_SETTING.key" />
<!-- 显示设置 -->
<terminal-view-setting v-if="tab.key === InnerTabs.THEME_SETTING.key" />
<span v-else>
{{ tab.title }}
</span>
@@ -34,10 +34,9 @@
<script lang="ts" setup>
import type { PropType } from 'vue';
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.const';
import TerminalThemeSetting from '../theme-setting/terminal-theme-setting.vue';
import TerminalViewSetting from '../theme-setting/terminal-view-setting.vue';
const props = defineProps({
modelValue: {

View File

@@ -220,9 +220,9 @@
}
.arco-tabs-tab-title {
padding: 11px 16px;
padding: 11px 18px;
background: var(--color-header-tabs-bg);
font-size: 13px;
font-size: 12px;
display: flex;
align-items: center;

View File

@@ -14,7 +14,7 @@
import { onMounted, onUnmounted, ref } from 'vue';
const props = defineProps<{
theme: TerminalTheme
theme: TerminalTheme | Record<string, any>
}>();
const terminal = ref();

View File

@@ -0,0 +1,74 @@
<template>
<div class="terminal-setting-block">
<!-- 顶部 -->
<div class="terminal-setting-subtitle-wrapper">
<h3 class="terminal-setting-subtitle">
字体设置
</h3>
</div>
<!-- 内容区域 -->
<div class="terminal-setting-body">
<div class="terminal-setting-form">
123
</div>
<!-- 预览区域 -->
<div class="terminal-example">
<div class="terminal-example-wrapper"
:style="{ background: terminalStore.preference.terminalTheme.background }">
<terminal-example :theme="terminalStore.preference.terminalTheme"
ref="previewTerminal" />
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'TerminalFontBlock'
};
</script>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import TerminalExample from '../theme-setting/terminal-example.vue';
import { useTerminalStore } from '@/store';
const terminalStore = useTerminalStore();
const previewTerminal = ref();
watch(() => terminalStore.preference.terminalTheme, (v) => {
if (!v) {
return;
}
const options = previewTerminal.value?.term?.options;
options && (options.theme = v);
});
</script>
<style lang="less" scoped>
@terminal-width: 458px;
@terminal-height: 138px;
.terminal-setting-body {
height: 248px;
width: 100%;
border: 1px solid var(--color-border-2);
border-radius: 4px;
display: flex;
justify-content: space-between;
}
.terminal-example {
margin: auto 16px 16px 0;
&-wrapper {
border-radius: 4px;
width: calc(@terminal-width - 16px);
height: @terminal-height;
}
}
</style>

View File

@@ -0,0 +1,157 @@
<template>
<div class="terminal-setting-block">
<!-- 顶部 -->
<div class="terminal-setting-subtitle-wrapper">
<h3 class="terminal-setting-subtitle">
主题设置
</h3>
<!-- 暗色选择 -->
<a-radio-group :default-value="preference.darkTheme"
size="mini"
type="button"
@change="checkDarkTheme"
:options="toOptions(darkThemeKey)">
</a-radio-group>
</div>
<!-- 内容区域 -->
<div class="terminal-setting-body">
<div class="theme-row"
v-for="index in ThemeSchema.length / 2"
:key="index">
<a-card v-for="(theme, index) in [ThemeSchema[(index - 1) * 2], ThemeSchema[(index - 1) * 2 + 1]]"
:key="theme.name"
class="terminal-theme-card simple-card"
:class="{
'terminal-theme-card-check': theme.name === preference.terminalTheme.name
}"
:title="theme.name"
:style="{
background: theme.background,
marginRight: index === 0 ? '16px' : 0
}"
:header-style="{
color: theme.dark ? 'rgba(255, 255, 255, .8)' : 'rgba(0, 0, 0, .8)'
}"
@click="checkTheme(theme)">
<!-- 样例 -->
<terminal-example :theme="theme" />
<!-- 选中按钮 -->
<icon-check class="theme-check-icon"
v-show="theme.name === preference.terminalTheme.name" />
</a-card>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'TerminalThemeBlock'
};
</script>
<script lang="ts" setup>
import type { TerminalTheme } from '@/store/modules/terminal/types';
import { darkThemeKey } from '../../types/terminal.const';
import ThemeSchema from '../../types/terminal.theme';
import { useDebounceFn } from '@vueuse/core';
import { useDictStore, useTerminalStore } from '@/store';
import { DarkTheme } from '@/store/modules/terminal';
import TerminalExample from './terminal-example.vue';
const { updatePreference, changeDarkTheme, preference } = useTerminalStore();
const { toOptions } = useDictStore();
// 同步用户偏好 - 防抖函数
const sync = useDebounceFn(updatePreference, 1500);
// 修改暗色主题
const checkDarkTheme = (value: string) => {
preference.darkTheme = value;
if (value === DarkTheme.DARK) {
// 暗色
changeDarkTheme(true);
} else if (value === DarkTheme.LIGHT) {
// 亮色
changeDarkTheme(false);
} else if (value === DarkTheme.AUTO) {
// 自动配色
changeDarkTheme(preference.terminalTheme.dark);
}
// 同步用户偏好
sync();
};
// 选择终端主题
const checkTheme = (theme: TerminalTheme) => {
preference.terminalTheme = theme;
// 切换主题配色
if (preference.darkTheme === DarkTheme.AUTO) {
changeDarkTheme(theme.dark);
}
// 同步用户偏好
sync();
};
</script>
<style lang="less" scoped>
@terminal-width: 458px;
@terminal-height: 138px;
.theme-row {
display: flex;
margin-bottom: 16px;
}
.terminal-theme-card {
width: @terminal-width;
height: calc(@terminal-height + 44px);
border: 2px solid var(--color-border);
cursor: pointer;
:deep(.arco-card-header) {
padding: 4px 16px;
height: 40px;
border-bottom: .5px solid var(--color-border-2);
&-title {
color: unset;
font-size: 16px;
font-weight: 600;
}
}
:deep(.arco-card-body) {
height: @terminal-height;
padding: 0;
display: flex;
position: relative;
.theme-check-icon {
display: flex;
position: absolute;
color: #FFF;
right: 0;
bottom: 0;
z-index: 10;
}
}
&-check, &:hover {
border: 2px solid rgb(var(--blue-6));
}
&-check::after {
content: '';
position: absolute;
right: 0;
bottom: 0;
width: 0;
height: 0;
border-bottom: 28px solid rgb(var(--blue-6));
border-left: 28px solid transparent;
}
}
</style>

View File

@@ -1,214 +0,0 @@
<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"
type="button"
@change="changeDarkTheme"
:options="toOptions(darkThemeKey)">
</a-radio-group>
</div>
<!-- 内容区域 -->
<div class="theme-list">
<div class="theme-row"
v-for="index in ThemeSchema.length / 2"
:key="index">
<a-card v-for="(theme, index) in [ThemeSchema[(index - 1) * 2], ThemeSchema[(index - 1) * 2 + 1]]"
:key="theme.name"
class="terminal-theme-card simple-card"
:class="{
'terminal-theme-card-check': theme.name === preference.terminalTheme.name
}"
:title="theme.name"
:style="{
background: theme.background,
marginRight: index === 0 ? '16px' : 0
}"
:header-style="{
color: theme.dark ? 'rgba(255, 255, 255, .8)' : 'rgba(0, 0, 0, .8)'
}"
@click="checkTheme(theme)">
<!-- 样例 -->
<terminal-example :theme="theme" />
<icon-check class="theme-check-icon" :style="{
display: theme.name === preference.terminalTheme.name ? 'flex': 'none'
}" />
</a-card>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'TerminalThemeSetting'
};
</script>
<script lang="ts" setup>
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 preference = ref<TerminalPreference>({
darkTheme: 'auto',
terminalTheme: {} as TerminalTheme,
});
const emits = defineEmits(['emitter']);
const changeLayoutTheme = inject(DarkThemeChangeSymbol) as (s: boolean) => void;
const { toOptions } = useDictStore();
// 修改暗色主题
const changeDarkTheme = (value: string) => {
preference.value.darkTheme = value;
if (value === DarkTheme.DARK) {
// 暗色
changeLayoutTheme(true);
} else if (value === DarkTheme.LIGHT) {
// 亮色
changeLayoutTheme(false);
} else if (value === DarkTheme.AUTO) {
// 自动配色
changeLayoutTheme(preference.value.terminalTheme.dark);
}
// 同步用户偏好
sync();
};
// 选择终端主题
const checkTheme = (theme: TerminalTheme) => {
preference.value.terminalTheme = theme;
// 切换主题配色
if (preference.value.darkTheme === DarkTheme.AUTO) {
changeLayoutTheme(theme.dark);
}
// 同步用户偏好
sync();
};
// 同步用户偏好
const syncUserPreference = async () => {
try {
await updatePreferencePartial({
type: 'TERMINAL',
config: preference
});
Message.success('同步成功');
} catch (e) {
Message.error('同步失败');
}
};
// 同步用户偏好防抖
const sync = useDebounceFn(syncUserPreference, 1500);
</script>
<style lang="less" scoped>
@terminal-width: 458px;
@terminal-height: 182px;
@wrapper-width: @terminal-width * 2 + 16;
.theme-setting-wrapper {
width: @wrapper-width;
user-select: none;
}
.theme-subtitle-wrapper {
display: flex;
justify-content: space-between;
align-items: flex-start;
.terminal-setting-subtitle {
margin: 0;
}
}
.theme-list {
margin-top: 16px;
.theme-row {
display: flex;
margin-bottom: 16px;
}
}
.terminal-theme-card {
width: @terminal-width;
height: @terminal-height;
border: 2px solid var(--color-border);
cursor: pointer;
:deep(.arco-card-header) {
padding: 4px 16px;
height: 40px;
border-bottom: .5px solid #DEDEDE;
&-title {
color: unset;
font-size: 16px;
font-weight: 600;
}
}
:deep(.arco-card-body) {
height: calc(@terminal-height - 44px);
padding: 0;
display: flex;
position: relative;
.theme-check-icon {
position: absolute;
color: #FFF;
right: 0;
bottom: 0;
z-index: 10;
}
}
&-check, &:hover {
border: 2px solid rgb(var(--blue-6));
}
&-check::after {
content: '';
position: absolute;
right: 0;
bottom: 0;
width: 0;
height: 0;
border-bottom: 28px solid rgb(var(--blue-6));
border-left: 28px solid transparent;
}
}
</style>

View File

@@ -0,0 +1,32 @@
<template>
<div class="terminal-setting-container view-setting-container">
<div class="view-setting-wrapper">
<!-- 主标题 -->
<h2 class="terminal-setting-title">外观设置</h2>
<!-- 字体设置 -->
<terminal-font-block />
<!-- 主题设置 -->
<terminal-theme-block />
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'TerminalViewSetting'
};
</script>
<script lang="ts" setup>
import TerminalFontBlock from './terminal-font-block.vue';
import TerminalThemeBlock from './terminal-theme-block.vue';
</script>
<style lang="less" scoped>
.view-setting-wrapper {
width: 932px;
user-select: none;
}
</style>

View File

@@ -33,15 +33,10 @@
</script>
<script lang="ts" setup>
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, DarkThemeChangeSymbol } from './types/terminal.const';
import { DEFAULT_SCHEMA } from './types/terminal.theme';
import { ref, onBeforeMount } from 'vue';
import { TabType, InnerTabs, dictKeys } from './types/terminal.const';
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';
import TerminalLeftSidebar from './components/layout/terminal-left-sidebar.vue';
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
@@ -49,22 +44,12 @@
import './assets/styles/layout.less';
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 terminalStore = useTerminalStore();
const dictStore = useDictStore();
const render = ref(false);
const activeKey = ref(InnerTabs.THEME_SETTING.key);
const tabs = ref<Array<TabItem>>([InnerTabs.THEME_SETTING]);
const preference = ref<TerminalPreference>();
for (let i = 0; i < 3; i++) {
tabs.value.push({
key: `host${i}`,
@@ -73,13 +58,6 @@
});
}
// 切换系统主题
const changeLayoutTheme = (dark: boolean) => {
terminalStore.changeDarkTheme(dark);
};
provide(DarkThemeChangeSymbol, changeLayoutTheme);
// 点击 tab
const clickTab = (key: string) => {
activeKey.value = key;

View File

@@ -1,12 +1,5 @@
import type { CSSProperties } from 'vue';
// 暗色主题
export const DarkTheme = {
DARK: 'dark',
LIGHT: 'light',
AUTO: 'auto'
};
// sidebar 操作类型
export interface SidebarAction {
icon: string;
@@ -50,9 +43,6 @@ export interface TabItem {
[key: string]: unknown;
}
// 暗色主题切换标识
export const DarkThemeChangeSymbol = Symbol('DARK_THEME_CHANGE');
// 终端暗色模式 字典项
export const darkThemeKey = 'terminalDarkTheme';