From d46ea63f22ade1753ef7345abed011b302e64e47 Mon Sep 17 00:00:00 2001 From: LovieCode Date: Sat, 18 Apr 2026 13:26:40 +0800 Subject: [PATCH 1/3] feat: add theme color customization feature --- dashboard/src/views/Settings.vue | 193 ++++++++++++++++++------------- 1 file changed, 112 insertions(+), 81 deletions(-) diff --git a/dashboard/src/views/Settings.vue b/dashboard/src/views/Settings.vue index 9ddb4e1fec..8e1ba4365e 100644 --- a/dashboard/src/views/Settings.vue +++ b/dashboard/src/views/Settings.vue @@ -1,6 +1,7 @@ - + - + @@ -128,13 +125,9 @@
{{ tm('apiKey.scopes') }}
- + :variant="newApiKeyScopes.includes(scope.value) ? 'flat' : 'tonal'"> {{ scope.label }} @@ -171,33 +164,22 @@ {{ item.key_prefix }} {{ (item.scopes || []).join(', ') }} - - {{ item.is_revoked || item.is_expired ? tm('apiKey.status.inactive') : tm('apiKey.status.active') }} + variant="tonal"> + {{ item.is_revoked || item.is_expired ? tm('apiKey.status.inactive') : + tm('apiKey.status.active') }} {{ formatDate(item.last_used_at) }} {{ formatDate(item.created_at) }} - + {{ tm('apiKey.revoke') }} - + {{ tm('apiKey.delete') }} @@ -214,9 +196,10 @@ - - {{ tm('system.migration.button') }} - + + {{ tm('system.migration.button') + }} + @@ -252,6 +235,29 @@ const getStoredColor = (key, fallback) => { const primaryColor = ref(getStoredColor('themePrimary', PurpleTheme.colors.primary)); const secondaryColor = ref(getStoredColor('themeSecondary', PurpleTheme.colors.secondary)); +const infoColor = ref(getStoredColor('themeInfo', PurpleTheme.colors.info)); + +const colorPresets = [ + { name: 'Indigo', primary: '#6366F1', secondary: '#6366F1', info: '#818CF8' }, + { name: 'Violet', primary: '#8B5CF6', secondary: '#8B5CF6', info: '#A78BFA' }, + { name: 'Rose', primary: '#F43F5E', secondary: '#F43F5E', info: '#FB7185' }, + { name: 'Pink', primary: '#F472B6', secondary: '#F472B6', info: '#F9A8D4' }, + { name: 'Orange', primary: '#F97316', secondary: '#F97316', info: '#FB923C' }, + { name: 'Emerald', primary: '#10B981', secondary: '#10B981', info: '#34D399' }, + { name: 'Sky', primary: '#0EA5E9', secondary: '#0EA5E9', info: '#38BDF8' }, + { name: 'Slate', primary: '#64748B', secondary: '#64748B', info: '#94A3B8' }, + { name: 'Zinc', primary: '#52525B', secondary: '#52525B', info: '#71717A' }, +]; + +const applyPreset = (preset) => { + primaryColor.value = preset.primary; + secondaryColor.value = preset.secondary; + infoColor.value = preset.info; + localStorage.setItem('themePrimary', preset.primary); + localStorage.setItem('themeSecondary', preset.secondary); + localStorage.setItem('themeInfo', preset.info); + applyThemeColors(preset.primary, preset.secondary, preset.info); +}; const resolveThemes = () => { if (theme?.themes?.value) return theme.themes.value; @@ -259,7 +265,7 @@ const resolveThemes = () => { return null; }; -const applyThemeColors = (primary, secondary) => { +const applyThemeColors = (primary, secondary, info) => { const themes = resolveThemes(); if (!themes) return; ['PurpleTheme', 'PurpleThemeDark'].forEach((name) => { @@ -267,23 +273,30 @@ const applyThemeColors = (primary, secondary) => { if (!themeDef?.colors) return; if (primary) themeDef.colors.primary = primary; if (secondary) themeDef.colors.secondary = secondary; + if (info) themeDef.colors.info = info; if (primary && themeDef.colors.darkprimary) themeDef.colors.darkprimary = primary; if (secondary && themeDef.colors.darksecondary) themeDef.colors.darksecondary = secondary; }); }; -applyThemeColors(primaryColor.value, secondaryColor.value); +applyThemeColors(primaryColor.value, secondaryColor.value, infoColor.value); watch(primaryColor, (value) => { if (!value) return; localStorage.setItem('themePrimary', value); - applyThemeColors(value, secondaryColor.value); + applyThemeColors(value, secondaryColor.value, infoColor.value); }); watch(secondaryColor, (value) => { if (!value) return; localStorage.setItem('themeSecondary', value); - applyThemeColors(primaryColor.value, value); + applyThemeColors(primaryColor.value, value, infoColor.value); +}); + +watch(infoColor, (value) => { + if (!value) return; + localStorage.setItem('themeInfo', value); + applyThemeColors(primaryColor.value, secondaryColor.value, value); }); const wfr = ref(null); @@ -482,12 +495,30 @@ const openBackupDialog = () => { const resetThemeColors = () => { primaryColor.value = PurpleTheme.colors.primary; secondaryColor.value = PurpleTheme.colors.secondary; + infoColor.value = PurpleTheme.colors.info; localStorage.removeItem('themePrimary'); localStorage.removeItem('themeSecondary'); - applyThemeColors(primaryColor.value, secondaryColor.value); + localStorage.removeItem('themeInfo'); + applyThemeColors(primaryColor.value, secondaryColor.value, infoColor.value); }; onMounted(() => { loadApiKeys(); }); + + From 410bcfc8672dcb6ba09a755f38c6884b2af04d05 Mon Sep 17 00:00:00 2001 From: LovieCode Date: Sat, 18 Apr 2026 13:27:57 +0800 Subject: [PATCH 2/3] feat: add theme color customization feature --- .../src/i18n/locales/en-US/features/settings.json | 13 +++++++++++++ .../src/i18n/locales/ru-RU/features/settings.json | 15 ++++++++++++++- .../src/i18n/locales/zh-CN/features/settings.json | 15 ++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/dashboard/src/i18n/locales/en-US/features/settings.json b/dashboard/src/i18n/locales/en-US/features/settings.json index b497659ee4..5f84724016 100644 --- a/dashboard/src/i18n/locales/en-US/features/settings.json +++ b/dashboard/src/i18n/locales/en-US/features/settings.json @@ -23,6 +23,19 @@ "title": "Theme Colors", "primary": "Primary Color", "secondary": "Secondary Color", + "info": "Info Color", + "presets": "Presets", + "presetNames": { + "Indigo": "Indigo", + "Violet": "Violet", + "Rose": "Rose", + "Pink": "Pink", + "Orange": "Orange", + "Emerald": "Emerald", + "Sky": "Sky", + "Slate": "Slate", + "Zinc": "Zinc" + }, "reset": "Reset to Default" } }, diff --git a/dashboard/src/i18n/locales/ru-RU/features/settings.json b/dashboard/src/i18n/locales/ru-RU/features/settings.json index d1100435f4..30ac9533c2 100644 --- a/dashboard/src/i18n/locales/ru-RU/features/settings.json +++ b/dashboard/src/i18n/locales/ru-RU/features/settings.json @@ -1,4 +1,4 @@ -{ +{ "network": { "title": "Сеть", "githubProxy": { @@ -23,6 +23,19 @@ "title": "Цвета темы", "primary": "Основной", "secondary": "Дополнительный", + "info": "Информационный", + "presets": "Пресеты", + "presetNames": { + "Indigo": "Индиго", + "Violet": "Фиалка", + "Rose": "Роза", + "Pink": "Розовый", + "Orange": "Оранжевый", + "Emerald": "Изумруд", + "Sky": "Небесный", + "Slate": "Сланец", + "Zinc": "Цинк" + }, "reset": "Сбросить" } }, diff --git a/dashboard/src/i18n/locales/zh-CN/features/settings.json b/dashboard/src/i18n/locales/zh-CN/features/settings.json index 092b4a5d9b..a05bce3335 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/settings.json +++ b/dashboard/src/i18n/locales/zh-CN/features/settings.json @@ -23,6 +23,19 @@ "title": "主题颜色", "primary": "主色", "secondary": "辅助色", + "info": "信息色", + "presets": "预设", + "presetNames": { + "Indigo": "靛蓝", + "Violet": "紫罗兰", + "Rose": "玫瑰", + "Pink": "粉色", + "Orange": "橙色", + "Emerald": "翡翠", + "Sky": "天蓝", + "Slate": "板岩", + "Zinc": "锌灰" + }, "reset": "恢复默认" } }, @@ -211,4 +224,4 @@ "copyFailed": "复制 API Key 失败" } } -} +} \ No newline at end of file From 8b28c3ef250697e76ff72524b23c03db22e8efa2 Mon Sep 17 00:00:00 2001 From: LovieCode Date: Sat, 18 Apr 2026 14:34:38 +0800 Subject: [PATCH 3/3] refactor: optimize theme color watchers to avoid redundant calls --- dashboard/src/views/Settings.vue | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/dashboard/src/views/Settings.vue b/dashboard/src/views/Settings.vue index 8e1ba4365e..89e0909c8c 100644 --- a/dashboard/src/views/Settings.vue +++ b/dashboard/src/views/Settings.vue @@ -47,7 +47,7 @@
{{ tm('theme.customize.presets') - }} + }} @@ -76,7 +76,7 @@ {{ tm('system.restart.button') - }} + }} @@ -198,7 +198,7 @@ {{ tm('system.migration.button') - }} + }}
@@ -253,10 +253,6 @@ const applyPreset = (preset) => { primaryColor.value = preset.primary; secondaryColor.value = preset.secondary; infoColor.value = preset.info; - localStorage.setItem('themePrimary', preset.primary); - localStorage.setItem('themeSecondary', preset.secondary); - localStorage.setItem('themeInfo', preset.info); - applyThemeColors(preset.primary, preset.secondary, preset.info); }; const resolveThemes = () => { @@ -493,13 +489,12 @@ const openBackupDialog = () => { } const resetThemeColors = () => { - primaryColor.value = PurpleTheme.colors.primary; - secondaryColor.value = PurpleTheme.colors.secondary; - infoColor.value = PurpleTheme.colors.info; localStorage.removeItem('themePrimary'); localStorage.removeItem('themeSecondary'); localStorage.removeItem('themeInfo'); - applyThemeColors(primaryColor.value, secondaryColor.value, infoColor.value); + primaryColor.value = PurpleTheme.colors.primary; + secondaryColor.value = PurpleTheme.colors.secondary; + infoColor.value = PurpleTheme.colors.info; }; onMounted(() => {