diff --git a/src/assets/css/global.scss b/src/assets/css/global.scss index 00c39156ce..6974ce4675 100644 --- a/src/assets/css/global.scss +++ b/src/assets/css/global.scss @@ -40,6 +40,7 @@ --color-text: #000; --color-primary: #335eea; --color-primary-bg: #eaeffd; + --color-primary-gradient: var(--color-primary); --color-secondary: #7a7a7b; --color-secondary-bg: #f5f5f7; --color-navbar-bg: rgba(255, 255, 255, 0.86); @@ -53,6 +54,7 @@ --color-text: #ffffff; --color-primary: #335eea; --color-primary-bg: #bbcdff; + --color-primary-gradient: var(--color-primary); --color-secondary: #7a7a7b; --color-secondary-bg: #323232; --color-navbar-bg: rgba(34, 34, 34, 0.86); diff --git a/src/components/Modal.vue b/src/components/Modal.vue index 6657369b85..567b9c8213 100644 --- a/src/components/Modal.vue +++ b/src/components/Modal.vue @@ -167,7 +167,7 @@ export default { } button.primary { color: var(--color-primary-bg); - background: var(--color-primary); + background: var(--color-primary-gradient); font-weight: 500; } button.block { diff --git a/src/locale/lang/en.js b/src/locale/lang/en.js index 4bf4b59fd6..4e1c947d3e 100644 --- a/src/locale/lang/en.js +++ b/src/locale/lang/en.js @@ -169,6 +169,13 @@ export default { light: 'Light', dark: 'Dark', }, + themeColor: { + text: 'Theme Color', + default: 'Original', + sunset: 'Sunset Gradient', + ocean: 'Ocean Gradient', + forest: 'Forest Gradient', + }, trayIcon: { text: 'Tray Icon Color', auto: 'Auto', diff --git a/src/locale/lang/tr.js b/src/locale/lang/tr.js index d820629807..8def25779d 100644 --- a/src/locale/lang/tr.js +++ b/src/locale/lang/tr.js @@ -164,6 +164,13 @@ export default { light: 'Aydınlık', dark: 'Karanlık', }, + themeColor: { + text: 'Tema rengi', + default: 'Orijinal', + sunset: 'Gün batımı geçişi', + ocean: 'Okyanus geçişi', + forest: 'Orman geçişi', + }, trayIcon: { text: 'Tepsi Simgesi Rengi', auto: 'Otomatik', diff --git a/src/locale/lang/zh-CN.js b/src/locale/lang/zh-CN.js index 4e1cd3f78b..968e02077b 100644 --- a/src/locale/lang/zh-CN.js +++ b/src/locale/lang/zh-CN.js @@ -170,6 +170,13 @@ export default { light: '浅色', dark: '深色', }, + themeColor: { + text: '主题颜色', + default: '原始颜色', + sunset: '日落渐变', + ocean: '海洋渐变', + forest: '森林渐变', + }, trayIcon: { text: '托盘图标颜色', auto: '自动', diff --git a/src/locale/lang/zh-TW.js b/src/locale/lang/zh-TW.js index 1b46985bcd..e91bc4a640 100644 --- a/src/locale/lang/zh-TW.js +++ b/src/locale/lang/zh-TW.js @@ -166,6 +166,13 @@ export default { light: '淺色', dark: '深色', }, + themeColor: { + text: '主題顏色', + default: '原始顏色', + sunset: '日落漸變', + ocean: '海洋漸變', + forest: '森林漸變', + }, trayIcon: { text: '工作列圖示顏色', auto: '自動', diff --git a/src/store/index.js b/src/store/index.js index d4804d63ae..ae44b1db43 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -3,7 +3,7 @@ import Vuex from 'vuex'; import state from './state'; import mutations from './mutations'; import actions from './actions'; -import { changeAppearance } from '@/utils/common'; +import { changeAppearance, changeThemeColor } from '@/utils/common'; import Player from '@/utils/Player'; // vuex 自定义插件 import saveToLocalStorage from './plugins/localStorage'; @@ -42,12 +42,14 @@ if ([undefined, null].includes(store.state.settings.lang)) { } changeAppearance(store.state.settings.appearance); +changeThemeColor(store.state.settings.themeColor); window .matchMedia('(prefers-color-scheme: dark)') .addEventListener('change', () => { if (store.state.settings.appearance === 'auto') { changeAppearance(store.state.settings.appearance); + changeThemeColor(store.state.settings.themeColor); } }); diff --git a/src/store/initLocalStorage.js b/src/store/initLocalStorage.js index 4718ecd3cc..1f0bb5792c 100644 --- a/src/store/initLocalStorage.js +++ b/src/store/initLocalStorage.js @@ -12,6 +12,7 @@ let localStorage = { lang: null, musicLanguage: 'all', appearance: 'auto', + themeColor: 'default', musicQuality: 320000, lyricFontSize: 28, outputDevice: 'default', diff --git a/src/utils/common.js b/src/utils/common.js index 8aa95baf9e..1ad79cb9ad 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -3,6 +3,86 @@ import { refreshCookie } from '@/api/auth'; import dayjs from 'dayjs'; import store from '@/store'; +const themeColorPresets = { + default: { + light: { + primary: '#335eea', + primaryBg: '#eaeffd', + primaryBgForTransparent: 'rgba(189, 207, 255, 0.28)', + primaryGradient: '#335eea', + }, + dark: { + primary: '#335eea', + primaryBg: '#bbcdff', + primaryBgForTransparent: 'rgba(255, 255, 255, 0.12)', + primaryGradient: '#335eea', + }, + }, + sunset: { + light: { + primary: '#dd2476', + primaryBg: '#ffe1ed', + primaryBgForTransparent: 'rgba(221, 36, 118, 0.2)', + primaryGradient: 'linear-gradient(135deg, #ff512f 0%, #dd2476 100%)', + }, + dark: { + primary: '#ff6fa5', + primaryBg: '#3d182b', + primaryBgForTransparent: 'rgba(255, 111, 165, 0.24)', + primaryGradient: 'linear-gradient(135deg, #ff512f 0%, #dd2476 100%)', + }, + }, + ocean: { + light: { + primary: '#0072ff', + primaryBg: '#e0f2ff', + primaryBgForTransparent: 'rgba(0, 114, 255, 0.18)', + primaryGradient: 'linear-gradient(135deg, #00c6ff 0%, #0072ff 100%)', + }, + dark: { + primary: '#5aa2ff', + primaryBg: '#15233a', + primaryBgForTransparent: 'rgba(90, 162, 255, 0.24)', + primaryGradient: 'linear-gradient(135deg, #00c6ff 0%, #0072ff 100%)', + }, + }, + forest: { + light: { + primary: '#56ab2f', + primaryBg: '#e6f7da', + primaryBgForTransparent: 'rgba(86, 171, 47, 0.18)', + primaryGradient: 'linear-gradient(135deg, #56ab2f 0%, #a8e063 100%)', + }, + dark: { + primary: '#7cd957', + primaryBg: '#1b3020', + primaryBgForTransparent: 'rgba(124, 217, 87, 0.24)', + primaryGradient: 'linear-gradient(135deg, #56ab2f 0%, #a8e063 100%)', + }, + }, +}; + +export function changeThemeColor(themeColor, appearance) { + if (typeof document === 'undefined') return; + const resolvedAppearance = + appearance || document.body?.getAttribute('data-theme') || 'light'; + const resolvedTheme = themeColorPresets[themeColor] ? themeColor : 'default'; + const preset = themeColorPresets[resolvedTheme] || themeColorPresets.default; + const theme = preset[resolvedAppearance] || preset.light || preset; + if (!theme) return; + const target = document.body || document.documentElement; + target.style.setProperty('--color-primary', theme.primary); + target.style.setProperty('--color-primary-bg', theme.primaryBg); + target.style.setProperty( + '--color-primary-bg-for-transparent', + theme.primaryBgForTransparent + ); + target.style.setProperty( + '--color-primary-gradient', + theme.primaryGradient || theme.primary + ); +} + export function isTrackPlayable(track) { let result = { playable: true, diff --git a/src/views/library.vue b/src/views/library.vue index 01381e47d8..5b17498a1a 100644 --- a/src/views/library.vue +++ b/src/views/library.vue @@ -478,7 +478,7 @@ h1 { align-items: center; height: 44px; width: 44px; - background: var(--color-primary); + background: var(--color-primary-gradient); border-radius: 50%; transition: 0.2s; box-shadow: 0 6px 12px -4px rgba(0, 0, 0, 0.2); diff --git a/src/views/settings.vue b/src/views/settings.vue index a1670c8e4c..08f23d2b14 100644 --- a/src/views/settings.vue +++ b/src/views/settings.vue @@ -56,6 +56,27 @@ +