mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-03 14:20:50 +08:00
@@ -15,6 +15,8 @@ export default {
|
||||
basic: {
|
||||
themeMode: 'Theme Mode',
|
||||
themeModeDesc: 'Switch between light/dark theme',
|
||||
autoTheme: 'Follow System',
|
||||
manualTheme: 'Manual Switch',
|
||||
language: 'Language Settings',
|
||||
languageDesc: 'Change display language',
|
||||
font: 'Font Settings',
|
||||
|
||||
@@ -15,6 +15,8 @@ export default {
|
||||
basic: {
|
||||
themeMode: '主题模式',
|
||||
themeModeDesc: '切换日间/夜间主题',
|
||||
autoTheme: '跟随系统',
|
||||
manualTheme: '手动切换',
|
||||
language: '语言设置',
|
||||
languageDesc: '切换显示语言',
|
||||
font: '字体设置',
|
||||
|
||||
@@ -15,6 +15,8 @@ export default {
|
||||
basic: {
|
||||
themeMode: '主題模式',
|
||||
themeModeDesc: '切換日間/夜間主題',
|
||||
autoTheme: '跟隨系統',
|
||||
manualTheme: '手動切換',
|
||||
language: '語言設定',
|
||||
languageDesc: '切換顯示語言',
|
||||
font: '字體設定',
|
||||
|
||||
@@ -25,5 +25,7 @@
|
||||
"enableMusicUnblock": true,
|
||||
"enabledMusicSources": ["migu", "kugou", "pyncmd", "bilibili"],
|
||||
"showTopAction": false,
|
||||
"contentZoomFactor": 1
|
||||
"contentZoomFactor": 1,
|
||||
"autoTheme": false,
|
||||
"manualTheme": "light"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ref } from 'vue';
|
||||
|
||||
import setDataDefault from '@/../main/set.json';
|
||||
import { isElectron } from '@/utils';
|
||||
import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme';
|
||||
import { applyTheme, getCurrentTheme, getSystemTheme, watchSystemTheme, ThemeType } from '@/utils/theme';
|
||||
|
||||
export const useSettingsStore = defineStore('settings', () => {
|
||||
const theme = ref<ThemeType>(getCurrentTheme());
|
||||
@@ -18,6 +18,9 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
]);
|
||||
const showDownloadDrawer = ref(false);
|
||||
|
||||
// 系统主题监听器清理函数
|
||||
let systemThemeCleanup: (() => void) | null = null;
|
||||
|
||||
// 先声明 setData ref 但不初始化
|
||||
const setData = ref<any>({});
|
||||
|
||||
@@ -56,8 +59,57 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
setData.value = getInitialSettings();
|
||||
|
||||
const toggleTheme = () => {
|
||||
theme.value = theme.value === 'dark' ? 'light' : 'dark';
|
||||
applyTheme(theme.value);
|
||||
if (setData.value.autoTheme) {
|
||||
// 如果是自动模式,切换到手动模式并设置相反的主题
|
||||
const newTheme = theme.value === 'dark' ? 'light' : 'dark';
|
||||
setSetData({
|
||||
autoTheme: false,
|
||||
manualTheme: newTheme
|
||||
});
|
||||
theme.value = newTheme;
|
||||
applyTheme(newTheme);
|
||||
// 停止监听系统主题
|
||||
if (systemThemeCleanup) {
|
||||
systemThemeCleanup();
|
||||
systemThemeCleanup = null;
|
||||
}
|
||||
} else {
|
||||
// 手动模式下正常切换
|
||||
const newTheme = theme.value === 'dark' ? 'light' : 'dark';
|
||||
theme.value = newTheme;
|
||||
setSetData({ manualTheme: newTheme });
|
||||
applyTheme(newTheme);
|
||||
}
|
||||
};
|
||||
|
||||
const setAutoTheme = (auto: boolean) => {
|
||||
setSetData({ autoTheme: auto });
|
||||
|
||||
if (auto) {
|
||||
// 启用自动模式
|
||||
const systemTheme = getSystemTheme();
|
||||
theme.value = systemTheme;
|
||||
applyTheme(systemTheme);
|
||||
|
||||
// 开始监听系统主题变化
|
||||
systemThemeCleanup = watchSystemTheme((newTheme) => {
|
||||
if (setData.value.autoTheme) {
|
||||
theme.value = newTheme;
|
||||
applyTheme(newTheme);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 切换到手动模式
|
||||
const manualTheme = setData.value.manualTheme || 'light';
|
||||
theme.value = manualTheme;
|
||||
applyTheme(manualTheme);
|
||||
|
||||
// 停止监听系统主题
|
||||
if (systemThemeCleanup) {
|
||||
systemThemeCleanup();
|
||||
systemThemeCleanup = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setMiniMode = (value: boolean) => {
|
||||
@@ -106,7 +158,14 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
};
|
||||
|
||||
const initializeTheme = () => {
|
||||
applyTheme(theme.value);
|
||||
// 根据设置初始化主题
|
||||
if (setData.value.autoTheme) {
|
||||
setAutoTheme(true);
|
||||
} else {
|
||||
const manualTheme = setData.value.manualTheme || getCurrentTheme();
|
||||
theme.value = manualTheme;
|
||||
applyTheme(manualTheme);
|
||||
}
|
||||
};
|
||||
|
||||
const initializeSystemFonts = async () => {
|
||||
@@ -133,6 +192,7 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
showDownloadDrawer,
|
||||
setSetData,
|
||||
toggleTheme,
|
||||
setAutoTheme,
|
||||
setMiniMode,
|
||||
setShowUpdateModal,
|
||||
setShowArtistDrawer,
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
export type ThemeType = 'dark' | 'light';
|
||||
|
||||
// 检测系统主题
|
||||
export const getSystemTheme = (): ThemeType => {
|
||||
if (typeof window !== 'undefined' && window.matchMedia) {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
return 'light';
|
||||
};
|
||||
|
||||
// 应用主题
|
||||
export const applyTheme = (theme: ThemeType) => {
|
||||
// 使用 Tailwind 的暗色主题类
|
||||
@@ -17,3 +25,21 @@ export const applyTheme = (theme: ThemeType) => {
|
||||
export const getCurrentTheme = (): ThemeType => {
|
||||
return (localStorage.getItem('theme') as ThemeType) || 'light';
|
||||
};
|
||||
|
||||
// 监听系统主题变化
|
||||
export const watchSystemTheme = (callback: (theme: ThemeType) => void) => {
|
||||
if (typeof window !== 'undefined' && window.matchMedia) {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const handler = (e: MediaQueryListEvent) => {
|
||||
callback(e.matches ? 'dark' : 'light');
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener('change', handler);
|
||||
|
||||
// 返回清理函数
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', handler);
|
||||
};
|
||||
}
|
||||
return () => {};
|
||||
};
|
||||
|
||||
@@ -25,10 +25,25 @@
|
||||
<div class="set-item-title">{{ t('settings.basic.themeMode') }}</div>
|
||||
<div class="set-item-content">{{ t('settings.basic.themeModeDesc') }}</div>
|
||||
</div>
|
||||
<n-switch v-model:value="isDarkTheme">
|
||||
<template #checked><i class="ri-moon-line"></i></template>
|
||||
<template #unchecked><i class="ri-sun-line"></i></template>
|
||||
</n-switch>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<n-switch v-model:value="setData.autoTheme" @update:value="handleAutoThemeChange">
|
||||
<template #checked><i class="ri-smartphone-line"></i></template>
|
||||
<template #unchecked><i class="ri-settings-line"></i></template>
|
||||
</n-switch>
|
||||
<span class="text-sm text-gray-500">
|
||||
{{ setData.autoTheme ? t('settings.basic.autoTheme') : t('settings.basic.manualTheme') }}
|
||||
</span>
|
||||
</div>
|
||||
<n-switch
|
||||
v-model:value="isDarkTheme"
|
||||
:disabled="setData.autoTheme"
|
||||
:class="{ 'opacity-50': setData.autoTheme }"
|
||||
>
|
||||
<template #checked><i class="ri-moon-line"></i></template>
|
||||
<template #unchecked><i class="ri-sun-line"></i></template>
|
||||
</n-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 语言设置 -->
|
||||
@@ -590,6 +605,10 @@ const isDarkTheme = computed({
|
||||
set: () => settingsStore.toggleTheme()
|
||||
});
|
||||
|
||||
const handleAutoThemeChange = (value: boolean) => {
|
||||
settingsStore.setAutoTheme(value);
|
||||
};
|
||||
|
||||
const openAuthor = () => {
|
||||
window.open(setData.value.authorUrl);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user