Files
iot-device-management-frontend/packages/@core/preferences/src/preferences.ts

235 lines
5.9 KiB
TypeScript
Raw Normal View History

2024-06-09 12:53:38 +08:00
import type { DeepPartial } from '@vben-core/typings';
2024-06-01 23:15:29 +08:00
import type { InitialOptions, Preferences } from './types';
2024-06-08 19:49:06 +08:00
2024-06-09 12:53:38 +08:00
import { markRaw, reactive, readonly, watch } from 'vue';
2024-06-08 19:49:06 +08:00
import { StorageManager } from '@vben-core/shared/cache';
import { isMacOs, merge } from '@vben-core/shared/utils';
2024-06-01 23:15:29 +08:00
import {
breakpointsTailwind,
useBreakpoints,
useDebounceFn,
} from '@vueuse/core';
import { defaultPreferences } from './config';
import { updateCSSVariables } from './update-css-variables';
2024-06-01 23:15:29 +08:00
const STORAGE_KEYS = {
MAIN: 'preferences',
LOCALE: 'preferences-locale',
THEME: 'preferences-theme',
} as const;
2024-06-01 23:15:29 +08:00
class PreferenceManager {
private cache: StorageManager;
private debouncedSave: (preference: Preferences) => void;
2024-06-01 23:15:29 +08:00
private initialPreferences: Preferences = defaultPreferences;
private isInitialized = false;
private state: Preferences;
2024-06-01 23:15:29 +08:00
constructor() {
this.cache = new StorageManager();
this.state = reactive<Preferences>(
this.loadFromCache() || { ...defaultPreferences },
);
this.debouncedSave = useDebounceFn(
(preference) => this.saveToCache(preference),
150,
2024-06-01 23:15:29 +08:00
);
}
/**
*
*/
clearCache = () => {
Object.values(STORAGE_KEYS).forEach((key) => this.cache.removeItem(key));
};
/**
*
*/
getInitialPreferences = () => {
return this.initialPreferences;
};
/**
*
*/
getPreferences = () => {
return readonly(this.state);
};
/**
*
* @param options -
* @param options.namespace -
* @param options.overrides -
*/
initPreferences = async ({ namespace, overrides }: InitialOptions) => {
// 防止重复初始化
if (this.isInitialized) {
return;
}
// 使用命名空间初始化存储管理器
this.cache = new StorageManager({ prefix: namespace });
// 合并初始偏好设置
this.initialPreferences = merge({}, overrides, defaultPreferences);
// 加载缓存的偏好设置并与初始配置合并
const cachedPreferences = this.loadFromCache() || {};
const mergedPreference = merge(
{},
cachedPreferences,
this.initialPreferences,
);
// 更新偏好设置
this.updatePreferences(mergedPreference);
// 设置监听器
this.setupWatcher();
// 初始化平台标识
this.initPlatform();
this.isInitialized = true;
};
/**
*
*/
resetPreferences = () => {
// 将状态重置为初始偏好设置
Object.assign(this.state, this.initialPreferences);
// 保存偏好设置至缓存
this.saveToCache(this.state);
// 直接触发 UI 更新
this.handleUpdates(this.state);
};
/**
*
* @param updates -
*/
updatePreferences = (updates: DeepPartial<Preferences>) => {
// 深度合并更新内容和当前状态
const mergedState = merge({}, updates, markRaw(this.state));
Object.assign(this.state, mergedState);
// 根据更新的值执行更新
this.handleUpdates(updates);
// 保存到缓存
this.debouncedSave(this.state);
};
2024-06-01 23:15:29 +08:00
/**
*
* @param updates -
2024-06-01 23:15:29 +08:00
*/
private handleUpdates(updates: DeepPartial<Preferences>) {
const { theme, app } = updates;
if (
theme &&
(Object.keys(theme).length > 0 || Reflect.has(theme, 'fontSize'))
) {
updateCSSVariables(this.state);
2024-06-01 23:15:29 +08:00
}
2024-06-29 14:45:02 +08:00
if (
app &&
(Reflect.has(app, 'colorGrayMode') || Reflect.has(app, 'colorWeakMode'))
2024-06-29 14:45:02 +08:00
) {
2024-06-01 23:15:29 +08:00
this.updateColorMode(this.state);
}
}
/**
*
*/
private initPlatform() {
document.documentElement.dataset.platform = isMacOs() ? 'macOs' : 'window';
}
2024-06-02 15:04:37 +08:00
/**
*
* @returns null
2024-06-02 15:04:37 +08:00
*/
private loadFromCache(): null | Preferences {
return this.cache.getItem<Preferences>(STORAGE_KEYS.MAIN);
2024-06-02 15:04:37 +08:00
}
2024-06-01 23:15:29 +08:00
/**
*
* @param preference -
2024-06-01 23:15:29 +08:00
*/
private saveToCache(preference: Preferences) {
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);
2024-06-01 23:15:29 +08:00
}
2024-06-02 15:04:37 +08:00
2024-06-01 23:15:29 +08:00
/**
*
2024-06-01 23:15:29 +08:00
*/
private setupWatcher() {
if (this.isInitialized) {
return;
}
// 监听断点,判断是否移动端
const breakpoints = useBreakpoints(breakpointsTailwind);
const isMobile = breakpoints.smaller('md');
2024-06-01 23:15:29 +08:00
watch(
() => isMobile.value,
(val) => {
this.updatePreferences({
app: { isMobile: val },
});
},
{ immediate: true },
);
// 监听系统主题偏好设置变化
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({ matches: isDark }) => {
// 仅在自动模式下跟随系统主题
if (this.state.theme.mode === 'auto') {
// 先应用实际的主题
this.updatePreferences({
theme: { mode: isDark ? 'dark' : 'light' },
});
// 再恢复为 auto 模式,保持跟随系统的状态
this.updatePreferences({
theme: { mode: 'auto' },
});
}
2024-06-01 23:15:29 +08:00
});
}
/**
*
* @param preference -
2024-06-01 23:15:29 +08:00
*/
private updateColorMode(preference: Preferences) {
const { colorGrayMode, colorWeakMode } = preference.app;
const dom = document.documentElement;
dom.classList.toggle('invert-mode', colorWeakMode);
dom.classList.toggle('grayscale-mode', colorGrayMode);
2024-06-01 23:15:29 +08:00
}
}
const preferencesManager = new PreferenceManager();
export { PreferenceManager, preferencesManager };