From fc7c162e2f6ebe9c67fdaa7516a9fe3f1cf087ee Mon Sep 17 00:00:00 2001 From: landaiqing Date: Thu, 20 Nov 2025 23:07:12 +0800 Subject: [PATCH] :recycle: Refactor theme module --- .../voidraft/internal/models/models.ts | 307 +------- .../internal/services/themeservice.ts | 34 +- frontend/src/stores/themeStore.ts | 189 ++--- frontend/src/views/editor/theme/presets.ts | 52 ++ .../views/settings/pages/AppearancePage.vue | 171 ++-- .../src/views/settings/pages/EditingPage.vue | 39 +- internal/models/theme.go | 742 +----------------- internal/services/theme_service.go | 319 ++------ 8 files changed, 388 insertions(+), 1465 deletions(-) create mode 100644 frontend/src/views/editor/theme/presets.ts diff --git a/frontend/bindings/voidraft/internal/models/models.ts b/frontend/bindings/voidraft/internal/models/models.ts index b4dc1be..1ceb050 100644 --- a/frontend/bindings/voidraft/internal/models/models.ts +++ b/frontend/bindings/voidraft/internal/models/models.ts @@ -1170,7 +1170,7 @@ export class Theme { this["type"] = ("" as ThemeType); } if (!("colors" in $$source)) { - this["colors"] = (new ThemeColorConfig()); + this["colors"] = ({} as ThemeColorConfig); } if (!("isDefault" in $$source)) { this["isDefault"] = false; @@ -1199,303 +1199,9 @@ export class Theme { } /** - * ThemeColorConfig 主题颜色配置(与前端 ThemeColors 接口保持一致) + * ThemeColorConfig 使用与前端 ThemeColors 相同的结构,存储任意主题键值 */ -export class ThemeColorConfig { - /** - * 主题基本信息 - * 主题名称 - */ - "name": string; - - /** - * 是否为深色主题 - */ - "dark": boolean; - - /** - * 基础色调 - * 主背景色 - */ - "background": string; - - /** - * 次要背景色(用于代码块交替背景) - */ - "backgroundSecondary": string; - - /** - * 面板背景 - */ - "surface": string; - - /** - * 下拉菜单背景 - */ - "dropdownBackground": string; - - /** - * 下拉菜单边框 - */ - "dropdownBorder": string; - - /** - * 文本颜色 - * 主文本色 - */ - "foreground": string; - - /** - * 次要文本色 - */ - "foregroundSecondary": string; - - /** - * 注释色 - */ - "comment": string; - - /** - * 语法高亮色 - 核心 - * 关键字 - */ - "keyword": string; - - /** - * 字符串 - */ - "string": string; - - /** - * 函数名 - */ - "function": string; - - /** - * 数字 - */ - "number": string; - - /** - * 操作符 - */ - "operator": string; - - /** - * 变量 - */ - "variable": string; - - /** - * 类型 - */ - "type": string; - - /** - * 语法高亮色 - 扩展 - * 常量 - */ - "constant": string; - - /** - * 存储类型(如 static, const) - */ - "storage": string; - - /** - * 参数 - */ - "parameter": string; - - /** - * 类名 - */ - "class": string; - - /** - * 标题(Markdown等) - */ - "heading": string; - - /** - * 无效内容/错误 - */ - "invalid": string; - - /** - * 正则表达式 - */ - "regexp": string; - - /** - * 界面元素 - * 光标 - */ - "cursor": string; - - /** - * 选中背景 - */ - "selection": string; - - /** - * 失焦选中背景 - */ - "selectionBlur": string; - - /** - * 当前行高亮 - */ - "activeLine": string; - - /** - * 行号 - */ - "lineNumber": string; - - /** - * 活动行号颜色 - */ - "activeLineNumber": string; - - /** - * 边框和分割线 - * 边框色 - */ - "borderColor": string; - - /** - * 浅色边框 - */ - "borderLight": string; - - /** - * 搜索和匹配 - * 搜索匹配 - */ - "searchMatch": string; - - /** - * 匹配括号 - */ - "matchingBracket": string; - - /** Creates a new ThemeColorConfig instance. */ - constructor($$source: Partial = {}) { - if (!("name" in $$source)) { - this["name"] = ""; - } - if (!("dark" in $$source)) { - this["dark"] = false; - } - if (!("background" in $$source)) { - this["background"] = ""; - } - if (!("backgroundSecondary" in $$source)) { - this["backgroundSecondary"] = ""; - } - if (!("surface" in $$source)) { - this["surface"] = ""; - } - if (!("dropdownBackground" in $$source)) { - this["dropdownBackground"] = ""; - } - if (!("dropdownBorder" in $$source)) { - this["dropdownBorder"] = ""; - } - if (!("foreground" in $$source)) { - this["foreground"] = ""; - } - if (!("foregroundSecondary" in $$source)) { - this["foregroundSecondary"] = ""; - } - if (!("comment" in $$source)) { - this["comment"] = ""; - } - if (!("keyword" in $$source)) { - this["keyword"] = ""; - } - if (!("string" in $$source)) { - this["string"] = ""; - } - if (!("function" in $$source)) { - this["function"] = ""; - } - if (!("number" in $$source)) { - this["number"] = ""; - } - if (!("operator" in $$source)) { - this["operator"] = ""; - } - if (!("variable" in $$source)) { - this["variable"] = ""; - } - if (!("type" in $$source)) { - this["type"] = ""; - } - if (!("constant" in $$source)) { - this["constant"] = ""; - } - if (!("storage" in $$source)) { - this["storage"] = ""; - } - if (!("parameter" in $$source)) { - this["parameter"] = ""; - } - if (!("class" in $$source)) { - this["class"] = ""; - } - if (!("heading" in $$source)) { - this["heading"] = ""; - } - if (!("invalid" in $$source)) { - this["invalid"] = ""; - } - if (!("regexp" in $$source)) { - this["regexp"] = ""; - } - if (!("cursor" in $$source)) { - this["cursor"] = ""; - } - if (!("selection" in $$source)) { - this["selection"] = ""; - } - if (!("selectionBlur" in $$source)) { - this["selectionBlur"] = ""; - } - if (!("activeLine" in $$source)) { - this["activeLine"] = ""; - } - if (!("lineNumber" in $$source)) { - this["lineNumber"] = ""; - } - if (!("activeLineNumber" in $$source)) { - this["activeLineNumber"] = ""; - } - if (!("borderColor" in $$source)) { - this["borderColor"] = ""; - } - if (!("borderLight" in $$source)) { - this["borderLight"] = ""; - } - if (!("searchMatch" in $$source)) { - this["searchMatch"] = ""; - } - if (!("matchingBracket" in $$source)) { - this["matchingBracket"] = ""; - } - - Object.assign(this, $$source); - } - - /** - * Creates a new ThemeColorConfig instance from a string or object. - */ - static createFrom($$source: any = {}): ThemeColorConfig { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new ThemeColorConfig($$parsedSource as Partial); - } -} +export type ThemeColorConfig = { [_: string]: any }; /** * ThemeType 主题类型枚举 @@ -1636,6 +1342,11 @@ var $$createType6 = (function $$initCreateType6(...args): any { }); const $$createType7 = $Create.Map($Create.Any, $Create.Any); const $$createType8 = HotkeyCombo.createFrom; -const $$createType9 = ThemeColorConfig.createFrom; +var $$createType9 = (function $$initCreateType9(...args): any { + if ($$createType9 === $$initCreateType9) { + $$createType9 = $$createType7; + } + return $$createType9(...args); +}); const $$createType10 = GithubConfig.createFrom; const $$createType11 = GiteaConfig.createFrom; diff --git a/frontend/bindings/voidraft/internal/services/themeservice.ts b/frontend/bindings/voidraft/internal/services/themeservice.ts index 92b8bc5..e472d72 100644 --- a/frontend/bindings/voidraft/internal/services/themeservice.ts +++ b/frontend/bindings/voidraft/internal/services/themeservice.ts @@ -18,23 +18,10 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic import * as models$0 from "../models/models.js"; /** - * GetAllThemes 获取所有主题 + * GetThemeByName 通过名称获取主题覆盖,若不存在则返回 nil */ -export function GetAllThemes(): Promise<(models$0.Theme | null)[]> & { cancel(): void } { - let $resultPromise = $Call.ByID(2425053076) as any; - let $typingPromise = $resultPromise.then(($result: any) => { - return $$createType2($result); - }) as any; - $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); - return $typingPromise; -} - -/** - * GetThemeByID 根据ID或名称获取主题 - * 如果 id > 0,按ID查询;如果 id = 0,按名称查询 - */ -export function GetThemeByIdOrName(id: number, ...name: string[]): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(127385338, id, name) as any; +export function GetThemeByName(name: string): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(1938954770, name) as any; let $typingPromise = $resultPromise.then(($result: any) => { return $$createType1($result); }) as any; @@ -43,10 +30,10 @@ export function GetThemeByIdOrName(id: number, ...name: string[]): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(1806334457, id, name) as any; +export function ResetTheme(name: string): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(1806334457, name) as any; return $resultPromise; } @@ -59,7 +46,7 @@ export function ServiceShutdown(): Promise & { cancel(): void } { } /** - * ServiceStartup 服务启动时初始化 + * ServiceStartup 服务启动 */ export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { let $resultPromise = $Call.ByID(2915959937, options) as any; @@ -67,14 +54,13 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(70189749, id, colors) as any; +export function UpdateTheme(name: string, colors: models$0.ThemeColorConfig): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(70189749, name, colors) as any; return $resultPromise; } // Private type creation functions const $$createType0 = models$0.Theme.createFrom; const $$createType1 = $Create.Nullable($$createType0); -const $$createType2 = $Create.Array($$createType1); diff --git a/frontend/src/stores/themeStore.ts b/frontend/src/stores/themeStore.ts index 3e2a308..55787ef 100644 --- a/frontend/src/stores/themeStore.ts +++ b/frontend/src/stores/themeStore.ts @@ -1,191 +1,156 @@ import { defineStore } from 'pinia'; import { computed, ref } from 'vue'; -import {SystemThemeType, ThemeType, Theme, ThemeColorConfig} from '@/../bindings/voidraft/internal/models/models'; +import { SystemThemeType, ThemeType, ThemeColorConfig } from '@/../bindings/voidraft/internal/models/models'; import { ThemeService } from '@/../bindings/voidraft/internal/services'; import { useConfigStore } from './configStore'; import { useEditorStore } from './editorStore'; import type { ThemeColors } from '@/views/editor/theme/types'; +import { cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap } from '@/views/editor/theme/presets'; + +type ThemeOption = { name: string; type: ThemeType }; + +const resolveThemeName = (name?: string) => + name && themePresetMap[name] ? name : FALLBACK_THEME_NAME; + +const createThemeOptions = (type: ThemeType): ThemeOption[] => + themePresetList + .filter(preset => preset.type === type) + .map(preset => ({ name: preset.name, type: preset.type })); + +const darkThemeOptions = createThemeOptions(ThemeType.ThemeTypeDark); +const lightThemeOptions = createThemeOptions(ThemeType.ThemeTypeLight); + +const cloneColors = (colors: ThemeColorConfig): ThemeColors => + JSON.parse(JSON.stringify(colors)) as ThemeColors; + +const getPresetColors = (name: string): ThemeColors => { + const preset = themePresetMap[name] ?? themePresetMap[FALLBACK_THEME_NAME]; + const colors = cloneThemeColors(preset.colors); + colors.themeName = name; + return colors; +}; + +const fetchThemeColors = async (themeName: string): Promise => { + const safeName = resolveThemeName(themeName); + try { + const theme = await ThemeService.GetThemeByName(safeName); + if (theme?.colors) { + const colors = cloneColors(theme.colors); + colors.themeName = safeName; + return colors; + } + } catch (error) { + console.error('Failed to load theme override:', error); + } + return getPresetColors(safeName); +}; -/** - * 主题管理 Store - * 职责:管理主题状态、颜色配置和预设主题列表 - */ export const useThemeStore = defineStore('theme', () => { const configStore = useConfigStore(); - - // 所有主题列表 - const allThemes = ref([]); - - // 当前主题的颜色配置 const currentColors = ref(null); - - // 计算属性:当前系统主题模式 - const currentTheme = computed(() => - configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto + + const currentTheme = computed( + () => configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto ); - // 计算属性:当前是否为深色模式 - const isDarkMode = computed(() => - currentTheme.value === SystemThemeType.SystemThemeDark || - (currentTheme.value === SystemThemeType.SystemThemeAuto && - window.matchMedia('(prefers-color-scheme: dark)').matches) + const isDarkMode = computed( + () => + currentTheme.value === SystemThemeType.SystemThemeDark || + (currentTheme.value === SystemThemeType.SystemThemeAuto && + window.matchMedia('(prefers-color-scheme: dark)').matches) ); - // 计算属性:根据类型获取主题列表 - const darkThemes = computed(() => - allThemes.value.filter(t => t.type === ThemeType.ThemeTypeDark) - ); - - const lightThemes = computed(() => - allThemes.value.filter(t => t.type === ThemeType.ThemeTypeLight) - ); - - // 计算属性:当前可用的主题列表 - const availableThemes = computed(() => - isDarkMode.value ? darkThemes.value : lightThemes.value + const availableThemes = computed(() => + isDarkMode.value ? darkThemeOptions : lightThemeOptions ); - // 应用主题到 DOM const applyThemeToDOM = (theme: SystemThemeType) => { const themeMap = { [SystemThemeType.SystemThemeAuto]: 'auto', [SystemThemeType.SystemThemeDark]: 'dark', - [SystemThemeType.SystemThemeLight]: 'light' + [SystemThemeType.SystemThemeLight]: 'light', }; document.documentElement.setAttribute('data-theme', themeMap[theme]); }; - // 从数据库加载所有主题 - const loadAllThemes = async () => { - try { - const themes = await ThemeService.GetAllThemes(); - allThemes.value = (themes || []).filter((t): t is Theme => t !== null); - return allThemes.value; - } catch (error) { - console.error('Failed to load themes from database:', error); - allThemes.value = []; - return []; - } + const loadThemeColors = async (themeName?: string) => { + const targetName = resolveThemeName( + themeName || configStore.config?.appearance?.currentTheme + ); + currentColors.value = await fetchThemeColors(targetName); }; - // 初始化主题颜色 - const initThemeColors = async () => { - // 加载所有主题 - await loadAllThemes(); - - // 从配置获取当前主题名称并加载 - const currentThemeName = configStore.config?.appearance?.currentTheme || 'default-dark'; - - const theme = allThemes.value.find(t => t.name === currentThemeName); - - if (!theme) { - console.error(`Theme not found: ${currentThemeName}`); - return; - } - - // 直接设置当前主题颜色 - currentColors.value = theme.colors as ThemeColors; - }; - - // 初始化主题 const initializeTheme = async () => { - const theme = currentTheme.value; - applyThemeToDOM(theme); - await initThemeColors(); + applyThemeToDOM(currentTheme.value); + await loadThemeColors(); }; - // 设置系统主题模式(深色/浅色/自动) const setTheme = async (theme: SystemThemeType) => { await configStore.setSystemTheme(theme); applyThemeToDOM(theme); refreshEditorTheme(); }; - - // 切换到指定的预设主题 + const switchToTheme = async (themeName: string) => { - const theme = allThemes.value.find(t => t.name === themeName); - if (!theme) { + if (!themePresetMap[themeName]) { console.error('Theme not found:', themeName); return false; } - // 直接设置当前主题颜色 - currentColors.value = theme.colors as ThemeColors; - - // 持久化到配置 + await loadThemeColors(themeName); await configStore.setCurrentTheme(themeName); - - // 刷新编辑器 refreshEditorTheme(); return true; }; - - // 更新当前主题的颜色配置 + const updateCurrentColors = (colors: Partial) => { if (!currentColors.value) return; Object.assign(currentColors.value, colors); }; - - // 保存当前主题颜色到数据库 + const saveCurrentTheme = async () => { if (!currentColors.value) { throw new Error('No theme selected'); } - const theme = allThemes.value.find(t => t.name === currentColors.value!.themeName); - if (!theme) { - throw new Error('Theme not found'); - } - - await ThemeService.UpdateTheme(theme.id, currentColors.value as ThemeColorConfig); + const themeName = resolveThemeName(currentColors.value.themeName); + currentColors.value.themeName = themeName; + + await ThemeService.UpdateTheme(themeName, currentColors.value as unknown as ThemeColorConfig); + + await loadThemeColors(themeName); + refreshEditorTheme(); return true; }; - - // 重置当前主题为预设配置 + const resetCurrentTheme = async () => { if (!currentColors.value) { throw new Error('No theme selected'); } - - // 调用后端重置 - await ThemeService.ResetTheme(0, currentColors.value.themeName); - - // 重新加载所有主题 - await loadAllThemes(); - const updatedTheme = allThemes.value.find(t => t.name === currentColors.value!.themeName); - - if (updatedTheme) { - currentColors.value = updatedTheme.colors as ThemeColors; - } - + const themeName = resolveThemeName(currentColors.value.themeName); + await ThemeService.ResetTheme(themeName); + + await loadThemeColors(themeName); refreshEditorTheme(); return true; }; - - // 刷新编辑器主题 + const refreshEditorTheme = () => { applyThemeToDOM(currentTheme.value); - + const editorStore = useEditorStore(); editorStore?.applyThemeSettings(); }; return { - // 状态 - allThemes, - darkThemes, - lightThemes, availableThemes, currentTheme, currentColors, isDarkMode, - - // 方法 setTheme, switchToTheme, initializeTheme, - loadAllThemes, updateCurrentColors, saveCurrentTheme, resetCurrentTheme, diff --git a/frontend/src/views/editor/theme/presets.ts b/frontend/src/views/editor/theme/presets.ts new file mode 100644 index 0000000..885705c --- /dev/null +++ b/frontend/src/views/editor/theme/presets.ts @@ -0,0 +1,52 @@ +import type {ThemeColors} from './types'; +import {ThemeType} from '@/../bindings/voidraft/internal/models/models'; +import {defaultDarkColors} from './dark/default-dark'; +import {defaultLightColors} from './light/default-light'; +import {config as draculaColors} from './dark/dracula'; +import {config as auraColors} from './dark/aura'; +import {config as githubDarkColors} from './dark/github-dark'; +import {config as materialDarkColors} from './dark/material-dark'; +import {config as oneDarkColors} from './dark/one-dark'; +import {config as solarizedDarkColors} from './dark/solarized-dark'; +import {config as tokyoNightColors} from './dark/tokyo-night'; +import {config as tokyoNightStormColors} from './dark/tokyo-night-storm'; +import {config as githubLightColors} from './light/github-light'; +import {config as materialLightColors} from './light/material-light'; +import {config as solarizedLightColors} from './light/solarized-light'; +import {config as tokyoNightDayColors} from './light/tokyo-night-day'; + +export interface ThemePreset { + name: string; + type: ThemeType; + colors: ThemeColors; +} + +export const FALLBACK_THEME_NAME = defaultDarkColors.themeName; + +export const themePresetList: ThemePreset[] = [ + {name: defaultDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: defaultDarkColors}, + {name: draculaColors.themeName, type: ThemeType.ThemeTypeDark, colors: draculaColors}, + {name: auraColors.themeName, type: ThemeType.ThemeTypeDark, colors: auraColors}, + {name: githubDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: githubDarkColors}, + {name: materialDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: materialDarkColors}, + {name: oneDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: oneDarkColors}, + {name: solarizedDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: solarizedDarkColors}, + {name: tokyoNightColors.themeName, type: ThemeType.ThemeTypeDark, colors: tokyoNightColors}, + {name: tokyoNightStormColors.themeName, type: ThemeType.ThemeTypeDark, colors: tokyoNightStormColors}, + {name: defaultLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: defaultLightColors}, + {name: githubLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: githubLightColors}, + {name: materialLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: materialLightColors}, + {name: solarizedLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: solarizedLightColors}, + {name: tokyoNightDayColors.themeName, type: ThemeType.ThemeTypeLight, colors: tokyoNightDayColors}, +]; + +export const themePresetMap: Record = themePresetList.reduce( + (map, preset) => { + map[preset.name] = preset; + return map; + }, + {} as Record +); + +export const cloneThemeColors = (colors: ThemeColors): ThemeColors => + JSON.parse(JSON.stringify(colors)) as ThemeColors; diff --git a/frontend/src/views/settings/pages/AppearancePage.vue b/frontend/src/views/settings/pages/AppearancePage.vue index 662cd71..ffe64ba 100644 --- a/frontend/src/views/settings/pages/AppearancePage.vue +++ b/frontend/src/views/settings/pages/AppearancePage.vue @@ -2,7 +2,7 @@ import { useConfigStore } from '@/stores/configStore'; import { useThemeStore } from '@/stores/themeStore'; import { useI18n } from 'vue-i18n'; -import { computed, watch, onMounted, ref } from 'vue'; +import { computed, watch, onMounted, ref, nextTick } from 'vue'; import SettingSection from '../components/SettingSection.vue'; import SettingItem from '../components/SettingItem.vue'; import { SystemThemeType, LanguageType } from '@/../bindings/voidraft/internal/models/models'; @@ -50,7 +50,10 @@ const resetButtonState = ref({ // 当前选中的主题名称 const currentThemeName = computed({ - get: () => themeStore.currentColors?.name || '', + get: () => + themeStore.currentColors?.themeName || + configStore.config.appearance.currentTheme || + '', set: async (themeName: string) => { await themeStore.switchToTheme(themeName); syncTempColors(); @@ -89,21 +92,39 @@ onMounted(() => { // 从 ThemeColors 接口自动提取所有颜色字段 const colorKeys = computed(() => { if (!tempColors.value) return []; - - // 获取所有字段,排除 name 和 dark(这两个是元数据) - return Object.keys(tempColors.value).filter(key => - key !== 'name' && key !== 'dark' - ); + + return Object.keys(tempColors.value) + .filter(key => key !== 'themeName' && key !== 'dark') + .sort((a, b) => a.localeCompare(b)); }); -// 颜色配置列表 -const colorList = computed(() => +const colorList = computed(() => colorKeys.value.map(colorKey => ({ key: colorKey, - label: t(`settings.themeColors.${colorKey}`) + label: colorKey })) ); +const colorSearch = ref(''); +const showSearch = ref(false); +const searchInputRef = ref(null); + +const filteredColorList = computed(() => { + const keyword = colorSearch.value.trim().toLowerCase(); + if (!keyword) return colorList.value; + return colorList.value.filter(color => color.key.toLowerCase().includes(keyword)); +}); + +const toggleSearch = async () => { + showSearch.value = !showSearch.value; + if (showSearch.value) { + await nextTick(); + searchInputRef.value?.focus(); + } else { + colorSearch.value = ''; + } +}; + // 处理重置按钮点击 const handleResetClick = () => { if (resetButtonState.value.confirming) { @@ -193,7 +214,7 @@ const updateSystemTheme = async (event: Event) => { await themeStore.setTheme(selectedSystemTheme); const availableThemes = themeStore.availableThemes; - const currentThemeName = currentColors.value?.name; + const currentThemeName = currentColors.value?.themeName; const isCurrentThemeAvailable = availableThemes.some(t => t.name === currentThemeName); if (!isCurrentThemeAvailable && availableThemes.length > 0) { @@ -242,7 +263,7 @@ const handlePickerClose = () => { @@ -251,14 +272,27 @@ const handlePickerClose = () => { - \ No newline at end of file + diff --git a/frontend/src/views/settings/pages/EditingPage.vue b/frontend/src/views/settings/pages/EditingPage.vue index 3a3c463..3e70bbc 100644 --- a/frontend/src/views/settings/pages/EditingPage.vue +++ b/frontend/src/views/settings/pages/EditingPage.vue @@ -19,16 +19,17 @@ onMounted(async () => { // 字体选择选项 const fontFamilyOptions = computed(() => configStore.fontOptions); -const currentFontFamily = computed(() => configStore.config.editing.fontFamily); - -// 字体选择 -const handleFontFamilyChange = async (event: Event) => { - const target = event.target as HTMLSelectElement; - const fontFamily = target.value; - if (fontFamily) { - await configStore.setFontFamily(fontFamily); +const fontFamilyModel = computed({ + get: () => + configStore.config.editing.fontFamily || + fontFamilyOptions.value[0]?.value || + '', + set: async (fontFamily: string) => { + if (fontFamily) { + await configStore.setFontFamily(fontFamily); + } } -}; +}); // 字体粗细选项 const fontWeightOptions = [ @@ -44,10 +45,14 @@ const fontWeightOptions = [ ]; // 字体粗细选择 -const handleFontWeightChange = async (event: Event) => { - const target = event.target as HTMLSelectElement; - await configStore.setFontWeight(target.value); -}; +const fontWeightModel = computed({ + get: () => configStore.config.editing.fontWeight || fontWeightOptions[0].value, + set: async (value: string) => { + if (value) { + await configStore.setFontWeight(value); + } + } +}); // 行高控制 const increaseLineHeight = async () => { @@ -118,8 +123,7 @@ const handleAutoSaveDelayChange = async (event: Event) => { >