mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 00:26:20 +08:00
199 lines
5.1 KiB
TypeScript
199 lines
5.1 KiB
TypeScript
import type {
|
|
DeepPartial,
|
|
Preference,
|
|
PreferenceKeys,
|
|
} from '@vben-core/typings';
|
|
|
|
import { convertToHslCssVar, merge } from '@vben-core/toolkit';
|
|
|
|
import type { Ref } from 'vue';
|
|
|
|
import { breakpointsTailwind, useBreakpoints, useCssVar } from '@vueuse/core';
|
|
import { markRaw, reactive, ref, watch } from 'vue';
|
|
|
|
import { defaultPreference } from './config';
|
|
|
|
import type { PreferenceCacheType } from './cache';
|
|
|
|
/**
|
|
* 当前偏好设置
|
|
*/
|
|
const currentPreference: Preference = reactive(defaultPreference);
|
|
/**
|
|
* 当前偏好设置原始值
|
|
*/
|
|
const initialPreference: Ref<Preference> = ref(defaultPreference);
|
|
|
|
let preferenceCache: PreferenceCacheType;
|
|
|
|
/**
|
|
* 是否监听过系统设置变化
|
|
*/
|
|
let isRegisterListen = false;
|
|
|
|
function updatePreference(key: keyof Preference, value: boolean | string): void;
|
|
function updatePreference(
|
|
preference: DeepPartial<Preference>,
|
|
value?: undefined,
|
|
): void;
|
|
|
|
/**
|
|
* 更新偏好设置
|
|
* @param preference - 一个部分偏好设置对象,它将被合并到当前偏好设置中。
|
|
*/
|
|
function updatePreference(preference: any, value: any) {
|
|
if (typeof preference === 'string') {
|
|
updatePreference({ [preference]: value }, value);
|
|
} else {
|
|
const updateKeys = Object.keys(preference) as PreferenceKeys[];
|
|
|
|
const mergePreference = merge(preference, markRaw(currentPreference));
|
|
|
|
Object.assign(currentPreference, mergePreference);
|
|
|
|
// 当修改到颜色变量时,更新 css 变量
|
|
if (updateKeys.includes('colorPrimary')) {
|
|
updateCssVar(currentPreference);
|
|
}
|
|
|
|
// 更新主题
|
|
if (updateKeys.includes('theme')) {
|
|
updateTheme(currentPreference);
|
|
}
|
|
|
|
// 更新页面颜色模式(灰色、色弱)
|
|
if (
|
|
updateKeys.includes('colorGrayMode') ||
|
|
updateKeys.includes('colorWeakMode')
|
|
) {
|
|
updateColorMode(currentPreference);
|
|
}
|
|
|
|
preferenceCache.set(currentPreference);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新 CSS 变量
|
|
* @param preference - 当前偏好设置对象,它的颜色值将被转换成 HSL 格式并设置为 CSS 变量。
|
|
*/
|
|
function updateCssVar(preference: Preference) {
|
|
for (const [key, value] of Object.entries(preference)) {
|
|
if (['colorPrimary'].includes(key)) {
|
|
const cssVarKey = key.replaceAll(/([A-Z])/g, '-$1').toLowerCase();
|
|
const cssVarValue = useCssVar(`--${cssVarKey}`);
|
|
cssVarValue.value = convertToHslCssVar(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新主题
|
|
* @param preference - 当前偏好设置对象,它的主题值将被用来设置文档的主题。
|
|
*/
|
|
function updateTheme(preference: Preference) {
|
|
// 当修改到颜色变量时,更新 css 变量
|
|
const root = document.documentElement;
|
|
if (root) {
|
|
const dark = isDarkTheme(preference.theme);
|
|
root.classList.toggle('dark', dark);
|
|
}
|
|
|
|
// 只需要监听一次即可
|
|
listenOnce(preference);
|
|
}
|
|
|
|
/**
|
|
* 更新页面颜色模式(灰色、色弱)
|
|
* @param preference
|
|
*/
|
|
function updateColorMode(preference: Preference) {
|
|
const { colorGrayMode, colorWeakMode } = preference;
|
|
const body = document.body;
|
|
const COLOR_WEAK = 'invert';
|
|
const COLOR_GRAY = 'grayscale';
|
|
colorWeakMode
|
|
? body.classList.add(COLOR_WEAK)
|
|
: body.classList.remove(COLOR_WEAK);
|
|
colorGrayMode
|
|
? body.classList.add(COLOR_GRAY)
|
|
: body.classList.remove(COLOR_GRAY);
|
|
}
|
|
|
|
/**
|
|
* 1. 监听系统主题偏好设置变化
|
|
* 2. 监听断点,判断是否移动端
|
|
* @param preference - 当前偏好设置对象,当系统主题偏好变化时,它的主题值会被更新。
|
|
*/
|
|
function listenOnce(preference: Preference) {
|
|
if (isRegisterListen) {
|
|
return;
|
|
}
|
|
isRegisterListen = true;
|
|
// 监听系统主题偏好设置变化
|
|
window
|
|
.matchMedia('(prefers-color-scheme: dark)')
|
|
.addEventListener('change', ({ matches: isDark }) => {
|
|
preference.theme = isDark ? 'dark' : 'light';
|
|
updateTheme(preference);
|
|
});
|
|
|
|
// 监听断点,判断是否移动端
|
|
const breakpoints = useBreakpoints(breakpointsTailwind);
|
|
const isMobile = breakpoints.smaller('md');
|
|
watch(
|
|
() => isMobile.value,
|
|
(val) => {
|
|
currentPreference.isMobile = val;
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 重置偏好设置
|
|
* 偏好设置将被重置为初始值,并从 localStorage 中移除。
|
|
*/
|
|
function resetPreference() {
|
|
Object.assign(currentPreference, initialPreference.value);
|
|
updatePreference(currentPreference);
|
|
preferenceCache.remove();
|
|
}
|
|
|
|
/**
|
|
* 配置当前app默认的偏好配置
|
|
* @param overrides
|
|
*/
|
|
function overridesPreference(
|
|
overrides: DeepPartial<Preference>,
|
|
cache: PreferenceCacheType,
|
|
) {
|
|
preferenceCache = cache;
|
|
/**
|
|
* 重置状态时用到的原始值
|
|
*/
|
|
initialPreference.value = merge(overrides, defaultPreference);
|
|
const mergedPreference = merge(
|
|
overrides,
|
|
preferenceCache.get(defaultPreference),
|
|
);
|
|
updatePreference(mergedPreference);
|
|
}
|
|
|
|
function isDarkTheme(theme: string) {
|
|
let dark = theme === 'dark';
|
|
if (theme === 'auto') {
|
|
dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
}
|
|
return dark;
|
|
}
|
|
|
|
export {
|
|
currentPreference,
|
|
initialPreference,
|
|
isDarkTheme,
|
|
overridesPreference,
|
|
resetPreference,
|
|
updatePreference,
|
|
};
|