refactor: preference manager and export (#7068)

* fix: preferences drawer outline z-index

* refactor: preferencesManager and exports
This commit is contained in:
ppxb
2026-01-06 12:42:32 +08:00
committed by GitHub
parent 6f02181024
commit 24d20ca9ee
3 changed files with 112 additions and 129 deletions

View File

@@ -2,33 +2,17 @@ import type { Preferences } from './types';
import { preferencesManager } from './preferences'; import { preferencesManager } from './preferences';
// 偏好设置(带有层级关系) export const {
const preferences: Preferences = getPreferences,
preferencesManager.getPreferences.apply(preferencesManager);
// 更新偏好设置
const updatePreferences =
preferencesManager.updatePreferences.bind(preferencesManager);
// 重置偏好设置
const resetPreferences =
preferencesManager.resetPreferences.bind(preferencesManager);
const clearPreferencesCache =
preferencesManager.clearCache.bind(preferencesManager);
// 初始化偏好设置
const initPreferences =
preferencesManager.initPreferences.bind(preferencesManager);
export {
clearPreferencesCache,
initPreferences,
preferences,
preferencesManager,
resetPreferences,
updatePreferences, updatePreferences,
}; resetPreferences,
clearCache,
initPreferences,
} = preferencesManager;
export const preferences: Preferences = getPreferences();
export { preferencesManager };
export * from './constants'; export * from './constants';
export type * from './types'; export type * from './types';

View File

@@ -16,168 +16,167 @@ import {
import { defaultPreferences } from './config'; import { defaultPreferences } from './config';
import { updateCSSVariables } from './update-css-variables'; import { updateCSSVariables } from './update-css-variables';
const STORAGE_KEY = 'preferences'; const STORAGE_KEYS = {
const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`; MAIN: 'preferences',
const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`; LOCALE: 'preferences-locale',
THEME: 'preferences-theme',
} as const;
class PreferenceManager { class PreferenceManager {
private cache: null | StorageManager = null; private cache: StorageManager;
// private flattenedState: Flatten<Preferences>; private debouncedSave: (preference: Preferences) => void;
private initialPreferences: Preferences = defaultPreferences; private initialPreferences: Preferences = defaultPreferences;
private isInitialized: boolean = false; private isInitialized = false;
private savePreferences: (preference: Preferences) => void; private state: Preferences;
private state: Preferences = reactive<Preferences>({
...this.loadPreferences(),
});
constructor() { constructor() {
this.cache = new StorageManager(); this.cache = new StorageManager();
this.state = reactive<Preferences>(
// 避免频繁的操作缓存 this.loadFromCache() || { ...defaultPreferences },
this.savePreferences = useDebounceFn( );
(preference: Preferences) => this._savePreferences(preference), this.debouncedSave = useDebounceFn(
(preference) => this.saveToCache(preference),
150, 150,
); );
} }
clearCache() { /**
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => { * 清除所有缓存的偏好设置
this.cache?.removeItem(key); */
}); clearCache = () => {
} Object.values(STORAGE_KEYS).forEach((key) => this.cache.removeItem(key));
};
public getInitialPreferences() {
return this.initialPreferences;
}
public getPreferences() {
return readonly(this.state);
}
/** /**
* 覆盖偏好设置 * 获取初始化偏好设置
* overrides 要覆盖的偏好设置
* namespace 命名空间
*/ */
public async initPreferences({ namespace, overrides }: InitialOptions) { getInitialPreferences = () => {
// 是否初始化过 return this.initialPreferences;
};
/**
* 获取当前偏好设置(只读)
*/
getPreferences = () => {
return readonly(this.state);
};
/**
* 初始化偏好设置
* @param namespace - 命名空间,用于隔离不同应用的配置
* @param overrides - 要覆盖的偏好设置
*/
initPreferences = async ({ namespace, overrides }: InitialOptions) => {
// 防止重复初始化
if (this.isInitialized) { if (this.isInitialized) {
return; return;
} }
// 初始化存储管理器
// 使用命名空间初始化存储管理器
this.cache = new StorageManager({ prefix: namespace }); this.cache = new StorageManager({ prefix: namespace });
// 合并初始偏好设置 // 合并初始偏好设置
this.initialPreferences = merge({}, overrides, defaultPreferences); this.initialPreferences = merge({}, overrides, defaultPreferences);
// 加载并合并当前存储的偏好设置 // 加载缓存的偏好设置并与初始配置合并
const cachedPreferences = this.loadFromCache() || {};
const mergedPreference = merge( const mergedPreference = merge(
{}, {},
// overrides, cachedPreferences,
this.loadCachedPreferences() || {},
this.initialPreferences, this.initialPreferences,
); );
// 更新偏好设置 // 更新偏好设置
this.updatePreferences(mergedPreference); this.updatePreferences(mergedPreference);
// 设置监听器
this.setupWatcher(); this.setupWatcher();
// 初始化平台标识
this.initPlatform(); this.initPlatform();
// 标记为已初始化
this.isInitialized = true; this.isInitialized = true;
} };
/** /**
* 重置偏好设置 * 重置偏好设置到初始状态
* 偏好设置将被重置为初始值,并从 localStorage 中移除。
*
* @example
* 假设 initialPreferences 为 { theme: 'light', language: 'en' }
* 当前 state 为 { theme: 'dark', language: 'fr' }
* this.resetPreferences();
* 调用后state 将被重置为 { theme: 'light', language: 'en' }
* 并且 localStorage 中的对应项将被移除
*/ */
resetPreferences() { resetPreferences = () => {
// 将状态重置为初始偏好设置 // 将状态重置为初始偏好设置
Object.assign(this.state, this.initialPreferences); Object.assign(this.state, this.initialPreferences);
// 保存重置后的偏好设置
this.savePreferences(this.state); // 保存偏好设置至缓存
// 从存储中移除偏好设置项 this.saveToCache(this.state);
[STORAGE_KEY, STORAGE_KEY_THEME, STORAGE_KEY_LOCALE].forEach((key) => {
this.cache?.removeItem(key); // 直接触发 UI 更新
}); this.handleUpdates(this.state);
this.updatePreferences(this.state); };
}
/** /**
* 更新偏好设置 * 更新偏好设置
* @param updates - 要更新的偏好设置 * @param updates - 要更新的偏好设置
*/ */
public updatePreferences(updates: DeepPartial<Preferences>) { updatePreferences = (updates: DeepPartial<Preferences>) => {
// 深度合并更新内容和当前状态
const mergedState = merge({}, updates, markRaw(this.state)); const mergedState = merge({}, updates, markRaw(this.state));
Object.assign(this.state, mergedState); Object.assign(this.state, mergedState);
// 根据更新的值执行相应的操作 // 根据更新的值执行更新
this.handleUpdates(updates); this.handleUpdates(updates);
this.savePreferences(this.state);
} // 保存到缓存
this.debouncedSave(this.state);
};
/** /**
* 保存偏好设置 * 处理更新
* @param {Preferences} preference - 需要保存的偏好设置 * @param updates - 更新的偏好设置
*/
private _savePreferences(preference: Preferences) {
this.cache?.setItem(STORAGE_KEY, preference);
this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale);
this.cache?.setItem(STORAGE_KEY_THEME, preference.theme.mode);
}
/**
* 处理更新的键值
* 根据更新的键值执行相应的操作。
* @param {DeepPartial<Preferences>} updates - 部分更新的偏好设置
*/ */
private handleUpdates(updates: DeepPartial<Preferences>) { private handleUpdates(updates: DeepPartial<Preferences>) {
const themeUpdates = updates.theme || {}; const { theme, app } = updates;
const appUpdates = updates.app || {};
if ( if (
(themeUpdates && Object.keys(themeUpdates).length > 0) || theme &&
Reflect.has(themeUpdates, 'fontSize') (Object.keys(theme).length > 0 || Reflect.has(theme, 'fontSize'))
) { ) {
updateCSSVariables(this.state); updateCSSVariables(this.state);
} }
if ( if (
Reflect.has(appUpdates, 'colorGrayMode') || app &&
Reflect.has(appUpdates, 'colorWeakMode') (Reflect.has(app, 'colorGrayMode') || Reflect.has(app, 'colorWeakMode'))
) { ) {
this.updateColorMode(this.state); this.updateColorMode(this.state);
} }
} }
/**
* 初始化平台标识
*/
private initPlatform() { private initPlatform() {
const dom = document.documentElement; document.documentElement.dataset.platform = isMacOs() ? 'macOs' : 'window';
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
} }
/** /**
* 从缓存加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。 * 从缓存加载偏好设置
* @returns 缓存的偏好设置,如果不存在则返回 null
*/ */
private loadCachedPreferences() { private loadFromCache(): null | Preferences {
return this.cache?.getItem<Preferences>(STORAGE_KEY); return this.cache.getItem<Preferences>(STORAGE_KEYS.MAIN);
} }
/** /**
* 加载偏好设置 * 保存偏好设置到缓存
* @returns {Preferences} 加载的偏好设置 * @param preference - 要保存的偏好设置
*/ */
private loadPreferences(): Preferences { private saveToCache(preference: Preferences) {
return this.loadCachedPreferences() || { ...defaultPreferences }; this.cache.setItem(STORAGE_KEYS.MAIN, preference);
this.cache.setItem(STORAGE_KEYS.LOCALE, preference.app.locale);
this.cache.setItem(STORAGE_KEYS.THEME, preference.theme.mode);
} }
/** /**
* 监听状态和系统偏好设置的变化 * 监听状态和系统偏好设置的变化
*/ */
private setupWatcher() { private setupWatcher() {
if (this.isInitialized) { if (this.isInitialized) {
@@ -187,6 +186,7 @@ class PreferenceManager {
// 监听断点,判断是否移动端 // 监听断点,判断是否移动端
const breakpoints = useBreakpoints(breakpointsTailwind); const breakpoints = useBreakpoints(breakpointsTailwind);
const isMobile = breakpoints.smaller('md'); const isMobile = breakpoints.smaller('md');
watch( watch(
() => isMobile.value, () => isMobile.value,
(val) => { (val) => {
@@ -201,12 +201,13 @@ class PreferenceManager {
window window
.matchMedia('(prefers-color-scheme: dark)') .matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({ matches: isDark }) => { .addEventListener('change', ({ matches: isDark }) => {
// 如果偏好设置中主题模式为auto跟随系统更新 // 仅在自动模式下跟随系统主题
if (this.state.theme.mode === 'auto') { if (this.state.theme.mode === 'auto') {
// 先应用实际的主题
this.updatePreferences({ this.updatePreferences({
theme: { mode: isDark ? 'dark' : 'light' }, theme: { mode: isDark ? 'dark' : 'light' },
}); });
// 恢复为auto模式 // 恢复为 auto 模式,保持跟随系统的状态
this.updatePreferences({ this.updatePreferences({
theme: { mode: 'auto' }, theme: { mode: 'auto' },
}); });
@@ -216,19 +217,17 @@ class PreferenceManager {
/** /**
* 更新页面颜色模式(灰色、色弱) * 更新页面颜色模式(灰色、色弱)
* @param preference * @param preference - 偏好设置
*/ */
private updateColorMode(preference: Preferences) { private updateColorMode(preference: Preferences) {
if (preference.app) { const { colorGrayMode, colorWeakMode } = preference.app;
const { colorGrayMode, colorWeakMode } = preference.app; const dom = document.documentElement;
const dom = document.documentElement;
const COLOR_WEAK = 'invert-mode'; dom.classList.toggle('invert-mode', colorWeakMode);
const COLOR_GRAY = 'grayscale-mode'; dom.classList.toggle('grayscale-mode', colorGrayMode);
dom.classList.toggle(COLOR_WEAK, colorWeakMode);
dom.classList.toggle(COLOR_GRAY, colorGrayMode);
}
} }
} }
const preferencesManager = new PreferenceManager(); const preferencesManager = new PreferenceManager();
export { PreferenceManager, preferencesManager }; export { PreferenceManager, preferencesManager };

View File

@@ -19,7 +19,7 @@ import { computed, ref } from 'vue';
import { Copy, Pin, PinOff, RotateCw } from '@vben/icons'; import { Copy, Pin, PinOff, RotateCw } from '@vben/icons';
import { $t, loadLocaleMessages } from '@vben/locales'; import { $t, loadLocaleMessages } from '@vben/locales';
import { import {
clearPreferencesCache, clearCache,
preferences, preferences,
resetPreferences, resetPreferences,
usePreferences, usePreferences,
@@ -228,7 +228,7 @@ async function handleCopy() {
async function handleClearCache() { async function handleClearCache() {
resetPreferences(); resetPreferences();
clearPreferencesCache(); clearCache();
emit('clearPreferencesAndLogout'); emit('clearPreferencesAndLogout');
} }