From f7c875acda6dfbe0ab7811b6509d6a42c9fa18f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=98=AD?= <81747598+lan-yonghui@users.noreply.github.com> Date: Fri, 3 Jan 2025 10:30:07 +0800 Subject: [PATCH] feat: Save the theme color scheme to the client (#7626) Refs #7074 --- .../views/setting/panel/theme-color/index.vue | 192 +++++++++++++----- 1 file changed, 142 insertions(+), 50 deletions(-) diff --git a/frontend/src/views/setting/panel/theme-color/index.vue b/frontend/src/views/setting/panel/theme-color/index.vue index fc25839f7..22e2893cc 100644 --- a/frontend/src/views/setting/panel/theme-color/index.vue +++ b/frontend/src/views/setting/panel/theme-color/index.vue @@ -16,29 +16,23 @@
- - - - - - +
@@ -48,38 +42,23 @@
- - - - - - - - - +
@@ -110,7 +89,7 @@ import { initFavicon, updateXpackSettingByKey } from '@/utils/xpack'; import { setPrimaryColor } from '@/utils/theme'; import { GlobalStore } from '@/store'; -const emit = defineEmits<{ (e: 'search'): void }>(); +const emit = defineEmits<(e: 'search') => void>(); const drawerVisible = ref(); const loading = ref(); @@ -132,11 +111,25 @@ const form = reactive({ }); const formRef = ref(); -const predefineColors = ref([ +const globalStore = GlobalStore(); + +const STORAGE_KEY = 'theme-predefine-colors'; +const lightPredefineColors = ref([ '#005eeb', + '#238636', '#3D8EFF', '#F0BE96', + '#00ced1', + '#c71585', + '#ff4500', + '#ff8c00', + '#ffd700', +]); +const darkPredefineColors = ref([ '#238636', + '#3D8EFF', + '#005eeb', + '#F0BE96', '#00ced1', '#c71585', '#ff4500', @@ -144,16 +137,111 @@ const predefineColors = ref([ '#ffd700', ]); -const globalStore = GlobalStore(); +const defaultDarkColors = [ + { color: '#3D8EFF', label: 'xpack.theme.classicBlue' }, + { color: '#F0BE96', label: 'xpack.theme.lingXiaGold' }, + { color: '#238636', label: 'xpack.theme.freshGreen' }, +]; + +const defaultLightColors = [ + { color: '#005eeb', label: 'xpack.theme.classicBlue' }, + { color: '#238636', label: 'xpack.theme.freshGreen' }, +]; + +let darkColors = [...defaultDarkColors]; + +let lightColors = [...defaultLightColors]; + +const addColorToTheme = ( + colors: { color: string; label: string }[], + newColor: { color: string; label: string }, + baseCount = 3, + maxNewColors = 2, +): { color: string; label: string }[] => { + const updatedColors = [...colors]; + const extraCount = Math.max(0, colors.length - baseCount); + if (extraCount >= maxNewColors) { + updatedColors.splice(baseCount, 1); + } + updatedColors.push(newColor); + return updatedColors; +}; + +const defaultColors = { + light: lightPredefineColors.value, + dark: darkPredefineColors.value, +}; + +const initThemeColors = () => { + try { + const storedColors = localStorage.getItem(STORAGE_KEY); + themeColors.value = storedColors ? JSON.parse(storedColors) : { ...defaultColors }; + } catch (error) { + console.error('Failed to parse theme colors from localStorage:', error); + themeColors.value = { ...defaultColors }; + } finally { + localStorage.setItem(STORAGE_KEY, JSON.stringify(themeColors.value)); + } +}; + +const themeColors = ref({ ...defaultColors }); const acceptParams = (params: DialogProps): void => { form.themeColor = params.themeColor; form.theme = params.theme; form.dark = form.themeColor.dark; form.light = form.themeColor.light; + initThemeColors(); + lightPredefineColors.value = themeColors.value['light']; + darkPredefineColors.value = themeColors.value['dark']; + lightPredefineColors.value.slice(0, 2).forEach((color) => { + const exists = lightColors.some((item) => item.color === color); + if (!exists) { + lightColors.push({ + color, + label: `xpack.theme.customColor`, + }); + } + }); + darkPredefineColors.value.slice(0, 2).forEach((color) => { + const exists = darkColors.some((item) => item.color === color); + if (!exists) { + darkColors.push({ + color, + label: `xpack.theme.customColor`, + }); + } + }); drawerVisible.value = true; }; +const updateThemeColors = (theme: 'light' | 'dark', newColors: string[]) => { + themeColors.value[theme] = newColors; + localStorage.setItem(STORAGE_KEY, JSON.stringify(themeColors.value)); + lightPredefineColors.value = themeColors.value['light']; + darkPredefineColors.value = themeColors.value['dark']; +}; + +const addAndRemoveColor = (theme: 'light' | 'dark', newColor: string) => { + const colors = [...themeColors.value[theme]]; + colors.unshift(newColor); + colors.pop(); + updateThemeColors(theme, colors); +}; + +const changeThemeColor = (theme: 'light' | 'dark', color: string) => { + if (theme === 'light') { + form.light = color; + const newLightColor = { color: color, label: 'xpack.theme.customColor' }; + lightColors = addColorToTheme(lightColors, newLightColor, 2); + } else { + form.dark = color; + const newDarkColor = { color: color, label: 'xpack.theme.customColor' }; + darkColors = addColorToTheme(darkColors, newDarkColor); + } + addAndRemoveColor(theme, color); +}; + const changeLightColor = (color: string) => { form.light = color; }; @@ -202,6 +290,10 @@ const onReSet = async () => { }).then(async () => { form.themeColor = { light: '#005eeb', dark: '#F0BE96' }; if (globalStore.isProductPro) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultColors)); + themeColors.value = { ...defaultColors }; + darkColors = [...defaultDarkColors]; + lightColors = [...defaultLightColors]; await updateXpackSettingByKey('ThemeColor', JSON.stringify(form.themeColor)); MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); loading.value = false;