refactor(project): re-adjust the overall folder
This commit is contained in:
7
packages/@core/preferences/build.config.ts
Normal file
7
packages/@core/preferences/build.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: ['src/index'],
|
||||
});
|
36
packages/@core/preferences/package.json
Normal file
36
packages/@core/preferences/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@vben-core/preferences",
|
||||
"version": "5.0.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "packages/@core/preferences"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm unbuild"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"development": "./src/index.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"vue": "^3.4.33"
|
||||
}
|
||||
}
|
108
packages/@core/preferences/src/config.ts
Normal file
108
packages/@core/preferences/src/config.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import type { Preferences } from './types';
|
||||
|
||||
const defaultPreferences: Preferences = {
|
||||
app: {
|
||||
accessMode: 'frontend',
|
||||
authPageLayout: 'panel-right',
|
||||
colorGrayMode: false,
|
||||
colorWeakMode: false,
|
||||
compact: false,
|
||||
contentCompact: 'wide',
|
||||
defaultAvatar:
|
||||
'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/avatar-v1.webp',
|
||||
dynamicTitle: true,
|
||||
enablePreferences: true,
|
||||
isMobile: false,
|
||||
layout: 'sidebar-nav',
|
||||
locale: 'zh-CN',
|
||||
loginExpiredMode: 'page',
|
||||
name: 'Vben Admin',
|
||||
},
|
||||
breadcrumb: {
|
||||
enable: true,
|
||||
hideOnlyOne: false,
|
||||
showHome: false,
|
||||
showIcon: true,
|
||||
styleType: 'normal',
|
||||
},
|
||||
copyright: {
|
||||
companyName: 'Vben Admin',
|
||||
companySiteLink: 'https://www.vben.pro',
|
||||
date: '2024',
|
||||
enable: true,
|
||||
icp: '',
|
||||
icpLink: '',
|
||||
},
|
||||
footer: {
|
||||
enable: true,
|
||||
fixed: false,
|
||||
},
|
||||
header: {
|
||||
enable: true,
|
||||
hidden: false,
|
||||
mode: 'fixed',
|
||||
},
|
||||
logo: {
|
||||
enable: true,
|
||||
source:
|
||||
'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
accordion: true,
|
||||
split: true,
|
||||
styleType: 'rounded',
|
||||
},
|
||||
shortcutKeys: {
|
||||
enable: true,
|
||||
globalLockScreen: true,
|
||||
globalLogout: true,
|
||||
globalPreferences: true,
|
||||
globalSearch: true,
|
||||
},
|
||||
sidebar: {
|
||||
collapsed: false,
|
||||
collapsedShowTitle: false,
|
||||
enable: true,
|
||||
expandOnHover: true,
|
||||
extraCollapse: true,
|
||||
hidden: false,
|
||||
width: 230,
|
||||
},
|
||||
tabbar: {
|
||||
dragable: true,
|
||||
enable: true,
|
||||
height: 36,
|
||||
keepAlive: true,
|
||||
persist: true,
|
||||
showIcon: true,
|
||||
styleType: 'chrome',
|
||||
},
|
||||
theme: {
|
||||
builtinType: 'default',
|
||||
colorDestructive: 'hsl(348 100% 61%)',
|
||||
colorPrimary: 'hsl(231 98% 65%)',
|
||||
colorSuccess: 'hsl(144 57% 58%)',
|
||||
colorWarning: 'hsl(42 84% 61%)',
|
||||
mode: 'dark',
|
||||
radius: '0.5',
|
||||
semiDarkMenu: true,
|
||||
},
|
||||
transition: {
|
||||
enable: true,
|
||||
loading: true,
|
||||
name: 'fade-slide',
|
||||
progress: true,
|
||||
},
|
||||
widget: {
|
||||
aiAssistant: true,
|
||||
fullscreen: true,
|
||||
globalSearch: true,
|
||||
languageToggle: true,
|
||||
lockScreen: true,
|
||||
notification: true,
|
||||
sidebarToggle: true,
|
||||
themeToggle: true,
|
||||
},
|
||||
};
|
||||
|
||||
export { defaultPreferences };
|
107
packages/@core/preferences/src/constants.ts
Normal file
107
packages/@core/preferences/src/constants.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type {
|
||||
BuiltinThemeType,
|
||||
SupportedLanguagesType,
|
||||
} from '@vben-core/typings';
|
||||
|
||||
interface Language {
|
||||
key: SupportedLanguagesType;
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface BuiltinThemePreset {
|
||||
color: string;
|
||||
darkPrimaryColor?: string;
|
||||
primaryColor?: string;
|
||||
type: BuiltinThemeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported languages
|
||||
*/
|
||||
const SUPPORT_LANGUAGES: Language[] = [
|
||||
{
|
||||
key: 'zh-CN',
|
||||
text: '简体中文',
|
||||
},
|
||||
{
|
||||
key: 'en-US',
|
||||
text: 'English',
|
||||
},
|
||||
];
|
||||
|
||||
const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
||||
{
|
||||
color: 'hsl(231 98% 65%)',
|
||||
type: 'default',
|
||||
},
|
||||
{
|
||||
color: 'hsl(245 82% 67%)',
|
||||
type: 'violet',
|
||||
},
|
||||
{
|
||||
color: 'hsl(347 77% 60%)',
|
||||
type: 'pink',
|
||||
},
|
||||
{
|
||||
color: 'hsl(0 75% 42%)',
|
||||
type: 'rose',
|
||||
},
|
||||
{
|
||||
color: 'hsl(212 100% 45%)',
|
||||
type: 'sky-blue',
|
||||
},
|
||||
{
|
||||
color: 'hsl(211 91% 39%)',
|
||||
type: 'deep-blue',
|
||||
},
|
||||
{
|
||||
color: 'hsl(161 90% 43%)',
|
||||
type: 'green',
|
||||
},
|
||||
{
|
||||
color: 'hsl(181 84% 32%)',
|
||||
type: 'deep-green',
|
||||
},
|
||||
{
|
||||
color: 'hsl(18 89% 40%)',
|
||||
type: 'orange',
|
||||
},
|
||||
{
|
||||
color: 'hsl(42 84% 61%)',
|
||||
type: 'yellow',
|
||||
},
|
||||
{
|
||||
color: 'hsl(240 5% 26%)',
|
||||
darkPrimaryColor: 'hsl(0 0% 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'zinc',
|
||||
},
|
||||
{
|
||||
color: 'hsl(0 0% 25%)',
|
||||
darkPrimaryColor: 'hsl(0 0% 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'neutral',
|
||||
},
|
||||
{
|
||||
color: 'hsl(215 25% 27%)',
|
||||
darkPrimaryColor: 'hsl(0 0% 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'slate',
|
||||
},
|
||||
{
|
||||
color: 'hsl(217 19% 27%)',
|
||||
darkPrimaryColor: 'hsl(0 0% 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'gray',
|
||||
},
|
||||
{
|
||||
color: '',
|
||||
type: 'custom',
|
||||
},
|
||||
];
|
||||
|
||||
export const COLOR_PRESETS = [...BUILT_IN_THEME_PRESETS].slice(0, 7);
|
||||
|
||||
export { BUILT_IN_THEME_PRESETS, SUPPORT_LANGUAGES };
|
||||
|
||||
export type { BuiltinThemePreset };
|
35
packages/@core/preferences/src/index.ts
Normal file
35
packages/@core/preferences/src/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { Preferences } from './types';
|
||||
|
||||
import { preferencesManager } from './preferences';
|
||||
|
||||
// 偏好设置(带有层级关系)
|
||||
const preferences: Preferences =
|
||||
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,
|
||||
};
|
||||
|
||||
export * from './constants';
|
||||
export type * from './types';
|
||||
export * from './use-preferences';
|
263
packages/@core/preferences/src/preferences.test.ts
Normal file
263
packages/@core/preferences/src/preferences.test.ts
Normal file
@@ -0,0 +1,263 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { defaultPreferences } from './config';
|
||||
import { isDarkTheme, PreferenceManager } from './preferences';
|
||||
|
||||
describe('preferences', () => {
|
||||
let preferenceManager: PreferenceManager;
|
||||
|
||||
// 模拟 window.matchMedia 方法
|
||||
vi.stubGlobal(
|
||||
'matchMedia',
|
||||
vi.fn().mockImplementation((query) => ({
|
||||
addEventListener: vi.fn(),
|
||||
addListener: vi.fn(), // Deprecated
|
||||
dispatchEvent: vi.fn(),
|
||||
matches: query === '(prefers-color-scheme: dark)',
|
||||
media: query,
|
||||
onchange: null,
|
||||
removeEventListener: vi.fn(),
|
||||
removeListener: vi.fn(), // Deprecated
|
||||
})),
|
||||
);
|
||||
beforeEach(() => {
|
||||
preferenceManager = new PreferenceManager();
|
||||
});
|
||||
|
||||
it('initPreferences should initialize preferences with overrides and namespace', async () => {
|
||||
const overrides = { theme: { colorPrimary: 'hsl(231 98% 65%)' } };
|
||||
const namespace = 'testNamespace';
|
||||
|
||||
await preferenceManager.initPreferences({ namespace, overrides });
|
||||
|
||||
expect(preferenceManager.getPreferences().theme.colorPrimary).toBe(
|
||||
overrides.theme.colorPrimary,
|
||||
);
|
||||
});
|
||||
|
||||
it('loads default preferences if no saved preferences found', () => {
|
||||
const preferences = preferenceManager.getPreferences();
|
||||
expect(preferences).toEqual(defaultPreferences);
|
||||
});
|
||||
|
||||
it('initializes preferences with overrides', async () => {
|
||||
const overrides: any = {
|
||||
app: {
|
||||
locale: 'en-US',
|
||||
},
|
||||
};
|
||||
await preferenceManager.initPreferences({
|
||||
namespace: 'testNamespace',
|
||||
overrides,
|
||||
});
|
||||
|
||||
// 等待防抖动操作完成
|
||||
// await new Promise((resolve) => setTimeout(resolve, 300)); // 等待100毫秒
|
||||
|
||||
const expected = {
|
||||
...defaultPreferences,
|
||||
app: {
|
||||
...defaultPreferences.app,
|
||||
...overrides.app,
|
||||
},
|
||||
};
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(expected);
|
||||
});
|
||||
|
||||
it('updates theme mode correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().theme.mode).toBe('light');
|
||||
});
|
||||
|
||||
it('updates color modes correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { colorGrayMode: true, colorWeakMode: true },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.colorGrayMode).toBe(true);
|
||||
expect(preferenceManager.getPreferences().app.colorWeakMode).toBe(true);
|
||||
});
|
||||
|
||||
it('resets preferences to default', () => {
|
||||
// 先更新一些偏好设置
|
||||
preferenceManager.updatePreferences({
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 然后重置偏好设置
|
||||
preferenceManager.resetPreferences();
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
|
||||
});
|
||||
|
||||
it('updates isMobile correctly', () => {
|
||||
// 模拟移动端状态
|
||||
vi.stubGlobal(
|
||||
'matchMedia',
|
||||
vi.fn().mockImplementation((query) => ({
|
||||
addEventListener: vi.fn(),
|
||||
addListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
matches: query === '(max-width: 768px)',
|
||||
media: query,
|
||||
onchange: null,
|
||||
removeEventListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
})),
|
||||
);
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { isMobile: true },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.isMobile).toBe(true);
|
||||
});
|
||||
|
||||
it('updates the locale preference correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: 'en-US' },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
|
||||
});
|
||||
|
||||
it('updates the sidebar width correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
sidebar: { width: 200 },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().sidebar.width).toBe(200);
|
||||
});
|
||||
it('updates the sidebar collapse state correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
sidebar: { collapsed: true },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().sidebar.collapsed).toBe(true);
|
||||
});
|
||||
it('updates the navigation style type correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
navigation: { styleType: 'flat' },
|
||||
} as any);
|
||||
|
||||
expect(preferenceManager.getPreferences().navigation.styleType).toBe(
|
||||
'flat',
|
||||
);
|
||||
});
|
||||
|
||||
it('resets preferences to default correctly', () => {
|
||||
// 先更新一些偏好设置
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: 'en-US' },
|
||||
sidebar: { collapsed: true, width: 200 },
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 然后重置偏好设置
|
||||
preferenceManager.resetPreferences();
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
|
||||
});
|
||||
|
||||
it('does not update undefined preferences', () => {
|
||||
const originalPreferences = preferenceManager.getPreferences();
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { nonexistentField: 'value' },
|
||||
} as any);
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
|
||||
});
|
||||
|
||||
it('reverts to default when a preference field is deleted', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: 'en-US' },
|
||||
});
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: undefined },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
|
||||
});
|
||||
|
||||
it('ignores updates with invalid preference value types', () => {
|
||||
const originalPreferences = preferenceManager.getPreferences();
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { isMobile: 'true' as unknown as boolean }, // 错误类型
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
|
||||
});
|
||||
|
||||
it('merges nested preference objects correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { name: 'New App Name' },
|
||||
});
|
||||
|
||||
const expected = {
|
||||
...defaultPreferences,
|
||||
app: {
|
||||
...defaultPreferences.app,
|
||||
name: 'New App Name',
|
||||
},
|
||||
};
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(expected);
|
||||
});
|
||||
|
||||
it('applies updates immediately after initialization', async () => {
|
||||
const overrides: any = {
|
||||
app: {
|
||||
locale: 'en-US',
|
||||
},
|
||||
};
|
||||
|
||||
await preferenceManager.initPreferences(overrides);
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
theme: { mode: 'light' },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().theme.mode).toBe('light');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDarkTheme', () => {
|
||||
it('should return true for dark theme', () => {
|
||||
expect(isDarkTheme('dark')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for light theme', () => {
|
||||
expect(isDarkTheme('light')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return system preference for auto theme', () => {
|
||||
vi.spyOn(window, 'matchMedia').mockImplementation((query) => ({
|
||||
addEventListener: vi.fn(),
|
||||
addListener: vi.fn(), // Deprecated
|
||||
dispatchEvent: vi.fn(),
|
||||
matches: query === '(prefers-color-scheme: dark)',
|
||||
media: query,
|
||||
onchange: null,
|
||||
removeEventListener: vi.fn(),
|
||||
removeListener: vi.fn(), // Deprecated
|
||||
}));
|
||||
|
||||
expect(isDarkTheme('auto')).toBe(true);
|
||||
expect(window.matchMedia).toHaveBeenCalledWith(
|
||||
'(prefers-color-scheme: dark)',
|
||||
);
|
||||
});
|
||||
});
|
235
packages/@core/preferences/src/preferences.ts
Normal file
235
packages/@core/preferences/src/preferences.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import type { DeepPartial } from '@vben-core/typings';
|
||||
|
||||
import type { InitialOptions, Preferences } from './types';
|
||||
|
||||
import { markRaw, reactive, readonly, watch } from 'vue';
|
||||
|
||||
import { isMacOs, merge, StorageManager } from '@vben-core/toolkit';
|
||||
|
||||
import {
|
||||
breakpointsTailwind,
|
||||
useBreakpoints,
|
||||
useDebounceFn,
|
||||
} from '@vueuse/core';
|
||||
|
||||
import { defaultPreferences } from './config';
|
||||
import { updateCSSVariables } from './update-css-variables';
|
||||
|
||||
const STORAGE_KEY = 'preferences';
|
||||
const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`;
|
||||
const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`;
|
||||
|
||||
function isDarkTheme(theme: string) {
|
||||
let dark = theme === 'dark';
|
||||
if (theme === 'auto') {
|
||||
dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return dark;
|
||||
}
|
||||
|
||||
class PreferenceManager {
|
||||
private cache: null | StorageManager = null;
|
||||
// private flattenedState: Flatten<Preferences>;
|
||||
private initialPreferences: Preferences = defaultPreferences;
|
||||
private isInitialized: boolean = false;
|
||||
private savePreferences: (preference: Preferences) => void;
|
||||
private state: Preferences = reactive<Preferences>({
|
||||
...this.loadPreferences(),
|
||||
});
|
||||
constructor() {
|
||||
this.cache = new StorageManager();
|
||||
|
||||
this.savePreferences = useDebounceFn(
|
||||
(preference: Preferences) => this._savePreferences(preference),
|
||||
100,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存偏好设置
|
||||
* @param {Preferences} preference - 需要保存的偏好设置
|
||||
*/
|
||||
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>) {
|
||||
const themeUpdates = updates.theme || {};
|
||||
const appUpdates = updates.app || {};
|
||||
if (themeUpdates && Object.keys(themeUpdates).length > 0) {
|
||||
updateCSSVariables(this.state);
|
||||
}
|
||||
|
||||
if (
|
||||
Reflect.has(appUpdates, 'colorGrayMode') ||
|
||||
Reflect.has(appUpdates, 'colorWeakMode')
|
||||
) {
|
||||
this.updateColorMode(this.state);
|
||||
}
|
||||
}
|
||||
|
||||
private initPlatform() {
|
||||
const dom = document.documentElement;
|
||||
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。
|
||||
*/
|
||||
private loadCachedPreferences() {
|
||||
return this.cache?.getItem<Preferences>(STORAGE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载偏好设置
|
||||
* @returns {Preferences} 加载的偏好设置
|
||||
*/
|
||||
private loadPreferences(): Preferences {
|
||||
return this.loadCachedPreferences() || { ...defaultPreferences };
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听状态和系统偏好设置的变化。
|
||||
*/
|
||||
private setupWatcher() {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 监听断点,判断是否移动端
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind);
|
||||
const isMobile = breakpoints.smaller('md');
|
||||
watch(
|
||||
() => isMobile.value,
|
||||
(val) => {
|
||||
this.updatePreferences({
|
||||
app: { isMobile: val },
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 监听系统主题偏好设置变化
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', ({ matches: isDark }) => {
|
||||
this.updatePreferences({
|
||||
theme: { mode: isDark ? 'dark' : 'light' },
|
||||
});
|
||||
updateCSSVariables(this.state);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页面颜色模式(灰色、色弱)
|
||||
* @param preference
|
||||
*/
|
||||
private updateColorMode(preference: Preferences) {
|
||||
if (preference.app) {
|
||||
const { colorGrayMode, colorWeakMode } = preference.app;
|
||||
const dom = document.documentElement;
|
||||
const COLOR_WEAK = 'invert-mode';
|
||||
const COLOR_GRAY = 'grayscale-mode';
|
||||
colorWeakMode
|
||||
? dom.classList.add(COLOR_WEAK)
|
||||
: dom.classList.remove(COLOR_WEAK);
|
||||
colorGrayMode
|
||||
? dom.classList.add(COLOR_GRAY)
|
||||
: dom.classList.remove(COLOR_GRAY);
|
||||
}
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].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) {
|
||||
// 是否初始化过
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
// 初始化存储管理器
|
||||
this.cache = new StorageManager({ prefix: namespace });
|
||||
// 合并初始偏好设置
|
||||
this.initialPreferences = merge({}, overrides, defaultPreferences);
|
||||
|
||||
// 加载并合并当前存储的偏好设置
|
||||
const mergedPreference = merge(
|
||||
{},
|
||||
overrides,
|
||||
this.loadCachedPreferences() || defaultPreferences,
|
||||
);
|
||||
|
||||
// 更新偏好设置
|
||||
this.updatePreferences(mergedPreference);
|
||||
|
||||
this.setupWatcher();
|
||||
|
||||
this.initPlatform();
|
||||
// 标记为已初始化
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置偏好设置
|
||||
* 偏好设置将被重置为初始值,并从 localStorage 中移除。
|
||||
*
|
||||
* @example
|
||||
* 假设 initialPreferences 为 { theme: 'light', language: 'en' }
|
||||
* 当前 state 为 { theme: 'dark', language: 'fr' }
|
||||
* this.resetPreferences();
|
||||
* 调用后,state 将被重置为 { theme: 'light', language: 'en' }
|
||||
* 并且 localStorage 中的对应项将被移除
|
||||
*/
|
||||
resetPreferences() {
|
||||
// 将状态重置为初始偏好设置
|
||||
Object.assign(this.state, this.initialPreferences);
|
||||
// 保存重置后的偏好设置
|
||||
this.savePreferences(this.state);
|
||||
// 从存储中移除偏好设置项
|
||||
[STORAGE_KEY, STORAGE_KEY_THEME, STORAGE_KEY_LOCALE].forEach((key) => {
|
||||
this.cache?.removeItem(key);
|
||||
});
|
||||
this.updatePreferences(this.state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新偏好设置
|
||||
* @param updates - 要更新的偏好设置
|
||||
*/
|
||||
public updatePreferences(updates: DeepPartial<Preferences>) {
|
||||
const mergedState = merge({}, updates, markRaw(this.state));
|
||||
|
||||
Object.assign(this.state, mergedState);
|
||||
|
||||
// 根据更新的键值执行相应的操作
|
||||
this.handleUpdates(updates);
|
||||
this.savePreferences(this.state);
|
||||
}
|
||||
}
|
||||
|
||||
const preferencesManager = new PreferenceManager();
|
||||
export { isDarkTheme, PreferenceManager, preferencesManager };
|
256
packages/@core/preferences/src/types.ts
Normal file
256
packages/@core/preferences/src/types.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import type {
|
||||
AccessModeType,
|
||||
AuthPageLayoutType,
|
||||
BreadcrumbStyleType,
|
||||
BuiltinThemeType,
|
||||
ContentCompactType,
|
||||
DeepPartial,
|
||||
LayoutHeaderModeType,
|
||||
LayoutType,
|
||||
LoginExpiredModeType,
|
||||
NavigationStyleType,
|
||||
PageTransitionType,
|
||||
SupportedLanguagesType,
|
||||
TabsStyleType,
|
||||
ThemeModeType,
|
||||
} from '@vben-core/typings';
|
||||
|
||||
interface AppPreferences {
|
||||
/** 权限模式 */
|
||||
accessMode: AccessModeType;
|
||||
/** 登录注册页面布局 */
|
||||
authPageLayout: AuthPageLayoutType;
|
||||
/** 是否开启灰色模式 */
|
||||
colorGrayMode: boolean;
|
||||
/** 是否开启色弱模式 */
|
||||
colorWeakMode: boolean;
|
||||
/** 是否开启紧凑模式 */
|
||||
compact: boolean;
|
||||
/** 是否开启内容紧凑模式 */
|
||||
contentCompact: ContentCompactType;
|
||||
// /** 应用默认头像 */
|
||||
defaultAvatar: string;
|
||||
// /** 开启动态标题 */
|
||||
dynamicTitle: boolean;
|
||||
/** 是否显示偏好设置 */
|
||||
enablePreferences: boolean;
|
||||
/** 是否移动端 */
|
||||
isMobile: boolean;
|
||||
/** 布局方式 */
|
||||
layout: LayoutType;
|
||||
/** 支持的语言 */
|
||||
locale: SupportedLanguagesType;
|
||||
/** 登录过期模式 */
|
||||
loginExpiredMode: LoginExpiredModeType;
|
||||
/** 应用名 */
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface BreadcrumbPreferences {
|
||||
/** 面包屑是否启用 */
|
||||
enable: boolean;
|
||||
/** 面包屑是否只有一个时隐藏 */
|
||||
hideOnlyOne: boolean;
|
||||
/** 面包屑首页图标是否可见 */
|
||||
showHome: boolean;
|
||||
/** 面包屑图标是否可见 */
|
||||
showIcon: boolean;
|
||||
/** 面包屑风格 */
|
||||
styleType: BreadcrumbStyleType;
|
||||
}
|
||||
|
||||
interface CopyrightPreferences {
|
||||
/** 版权公司名 */
|
||||
companyName: string;
|
||||
/** 版权公司名链接 */
|
||||
companySiteLink: string;
|
||||
/** 版权日期 */
|
||||
date: string;
|
||||
/** 版权是否可见 */
|
||||
enable: boolean;
|
||||
/** 备案号 */
|
||||
icp: string;
|
||||
/** 备案号链接 */
|
||||
icpLink: string;
|
||||
}
|
||||
|
||||
interface FooterPreferences {
|
||||
/** 底栏是否可见 */
|
||||
enable: boolean;
|
||||
/** 底栏是否固定 */
|
||||
fixed: boolean;
|
||||
}
|
||||
|
||||
interface HeaderPreferences {
|
||||
/** 顶栏是否启用 */
|
||||
enable: boolean;
|
||||
/** 顶栏是否隐藏,css-隐藏 */
|
||||
hidden: boolean;
|
||||
/** header显示模式 */
|
||||
mode: LayoutHeaderModeType;
|
||||
}
|
||||
|
||||
interface LogoPreferences {
|
||||
/** logo是否可见 */
|
||||
enable: boolean;
|
||||
/** logo地址 */
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface NavigationPreferences {
|
||||
/** 导航菜单手风琴模式 */
|
||||
accordion: boolean;
|
||||
/** 导航菜单是否切割,只在 layout=mixed-nav 生效 */
|
||||
split: boolean;
|
||||
/** 导航菜单风格 */
|
||||
styleType: NavigationStyleType;
|
||||
}
|
||||
|
||||
interface SidebarPreferences {
|
||||
/** 侧边栏是否折叠 */
|
||||
collapsed: boolean;
|
||||
/** 侧边栏折叠时,是否显示title */
|
||||
collapsedShowTitle: boolean;
|
||||
/** 侧边栏是否可见 */
|
||||
enable: boolean;
|
||||
/** 菜单自动展开状态 */
|
||||
expandOnHover: boolean;
|
||||
/** 侧边栏扩展区域是否折叠 */
|
||||
extraCollapse: boolean;
|
||||
/** 侧边栏是否隐藏 - css */
|
||||
hidden: boolean;
|
||||
/** 侧边栏宽度 */
|
||||
width: number;
|
||||
}
|
||||
|
||||
interface ShortcutKeyPreferences {
|
||||
/** 是否启用快捷键-全局 */
|
||||
enable: boolean;
|
||||
/** 是否启用全局锁屏快捷键 */
|
||||
globalLockScreen: boolean;
|
||||
/** 是否启用全局注销快捷键 */
|
||||
globalLogout: boolean;
|
||||
/** 是否启用全局偏好设置快捷键 */
|
||||
globalPreferences: boolean;
|
||||
/** 是否启用全局搜索快捷键 */
|
||||
globalSearch: boolean;
|
||||
}
|
||||
|
||||
interface TabbarPreferences {
|
||||
/** 是否开启多标签页拖拽 */
|
||||
dragable: boolean;
|
||||
/** 是否开启多标签页 */
|
||||
enable: boolean;
|
||||
/** 标签页高度 */
|
||||
height: number;
|
||||
/** 开启标签页缓存功能 */
|
||||
keepAlive: boolean;
|
||||
/** 是否持久化标签 */
|
||||
persist: boolean;
|
||||
/** 是否开启多标签页图标 */
|
||||
showIcon: boolean;
|
||||
/** 标签页风格 */
|
||||
styleType: TabsStyleType;
|
||||
}
|
||||
|
||||
interface ThemePreferences {
|
||||
/** 内置主题名 */
|
||||
builtinType: BuiltinThemeType;
|
||||
/** 错误色 */
|
||||
colorDestructive: string;
|
||||
/** 主题色 */
|
||||
colorPrimary: string;
|
||||
/** 成功色 */
|
||||
colorSuccess: string;
|
||||
/** 警告色 */
|
||||
colorWarning: string;
|
||||
/** 当前主题 */
|
||||
mode: ThemeModeType;
|
||||
/** 圆角 */
|
||||
radius: string;
|
||||
/** 是否开启半深色菜单(只在theme='light'时生效) */
|
||||
semiDarkMenu: boolean;
|
||||
}
|
||||
|
||||
interface TransitionPreferences {
|
||||
/** 页面切换动画是否启用 */
|
||||
enable: boolean;
|
||||
// /** 是否开启页面加载loading */
|
||||
loading: boolean;
|
||||
/** 页面切换动画 */
|
||||
name: PageTransitionType | string;
|
||||
/** 是否开启页面加载进度动画 */
|
||||
progress: boolean;
|
||||
}
|
||||
|
||||
interface WidgetPreferences {
|
||||
/** 是否开启vben助手部件 */
|
||||
aiAssistant: boolean;
|
||||
/** 是否启用全屏部件 */
|
||||
fullscreen: boolean;
|
||||
/** 是否启用全局搜索部件 */
|
||||
globalSearch: boolean;
|
||||
/** 是否启用语言切换部件 */
|
||||
languageToggle: boolean;
|
||||
/** 是否开启锁屏功能 */
|
||||
lockScreen: boolean;
|
||||
/** 是否显示通知部件 */
|
||||
notification: boolean;
|
||||
/** 是否显示侧边栏显示/隐藏部件 */
|
||||
sidebarToggle: boolean;
|
||||
/** 是否显示主题切换部件 */
|
||||
themeToggle: boolean;
|
||||
}
|
||||
|
||||
interface Preferences {
|
||||
/** 全局配置 */
|
||||
app: AppPreferences;
|
||||
/** 顶栏配置 */
|
||||
breadcrumb: BreadcrumbPreferences;
|
||||
/** 版权配置 */
|
||||
copyright: CopyrightPreferences;
|
||||
/** 底栏配置 */
|
||||
footer: FooterPreferences;
|
||||
/** 面包屑配置 */
|
||||
header: HeaderPreferences;
|
||||
/** logo配置 */
|
||||
logo: LogoPreferences;
|
||||
/** 导航配置 */
|
||||
navigation: NavigationPreferences;
|
||||
/** 快捷键配置 */
|
||||
shortcutKeys: ShortcutKeyPreferences;
|
||||
/** 侧边栏配置 */
|
||||
sidebar: SidebarPreferences;
|
||||
/** 标签页配置 */
|
||||
tabbar: TabbarPreferences;
|
||||
/** 主题配置 */
|
||||
theme: ThemePreferences;
|
||||
/** 动画配置 */
|
||||
transition: TransitionPreferences;
|
||||
/** 功能配置 */
|
||||
widget: WidgetPreferences;
|
||||
}
|
||||
|
||||
type PreferencesKeys = keyof Preferences;
|
||||
|
||||
interface InitialOptions {
|
||||
namespace: string;
|
||||
overrides?: DeepPartial<Preferences>;
|
||||
}
|
||||
export type {
|
||||
AppPreferences,
|
||||
BreadcrumbPreferences,
|
||||
FooterPreferences,
|
||||
HeaderPreferences,
|
||||
InitialOptions,
|
||||
LogoPreferences,
|
||||
NavigationPreferences,
|
||||
Preferences,
|
||||
PreferencesKeys,
|
||||
ShortcutKeyPreferences,
|
||||
SidebarPreferences,
|
||||
TabbarPreferences,
|
||||
ThemePreferences,
|
||||
TransitionPreferences,
|
||||
WidgetPreferences,
|
||||
};
|
118
packages/@core/preferences/src/update-css-variables.ts
Normal file
118
packages/@core/preferences/src/update-css-variables.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { Preferences } from './types';
|
||||
|
||||
import {
|
||||
updateCSSVariables as executeUpdateCSSVariables,
|
||||
generatorColorVariables,
|
||||
} from '@vben-core/toolkit';
|
||||
|
||||
import { BUILT_IN_THEME_PRESETS } from './constants';
|
||||
|
||||
/**
|
||||
* 更新主题的 CSS 变量以及其他 CSS 变量
|
||||
* @param preferences - 当前偏好设置对象,它的主题值将被用来设置文档的主题。
|
||||
*/
|
||||
function updateCSSVariables(preferences: Preferences) {
|
||||
// 当修改到颜色变量时,更新 css 变量
|
||||
const root = document.documentElement;
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = preferences?.theme ?? {};
|
||||
|
||||
const { builtinType, colorPrimary, mode, radius } = theme;
|
||||
|
||||
// html 设置 dark 类
|
||||
if (Reflect.has(theme, 'mode')) {
|
||||
const dark = isDarkTheme(mode);
|
||||
root.classList.toggle('dark', dark);
|
||||
}
|
||||
|
||||
// html 设置 data-theme=[builtinType]
|
||||
if (Reflect.has(theme, 'builtinType')) {
|
||||
const rootTheme = root.dataset.theme;
|
||||
if (rootTheme !== builtinType) {
|
||||
root.dataset.theme = builtinType;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前的内置主题
|
||||
const currentBuiltType = BUILT_IN_THEME_PRESETS.find(
|
||||
(item) => item.type === builtinType,
|
||||
);
|
||||
|
||||
let builtinTypeColorPrimary: string | undefined = '';
|
||||
|
||||
if (currentBuiltType) {
|
||||
const isDark = isDarkTheme(preferences.theme.mode);
|
||||
// 设置不同主题的主要颜色
|
||||
const color = isDark
|
||||
? currentBuiltType.darkPrimaryColor || currentBuiltType.primaryColor
|
||||
: currentBuiltType.primaryColor;
|
||||
builtinTypeColorPrimary = color || currentBuiltType.color;
|
||||
}
|
||||
|
||||
// 如果内置主题颜色和自定义颜色都不存在,则不更新主题颜色
|
||||
if (
|
||||
builtinTypeColorPrimary ||
|
||||
Reflect.has(theme, 'colorPrimary') ||
|
||||
Reflect.has(theme, 'colorDestructive') ||
|
||||
Reflect.has(theme, 'colorSuccess') ||
|
||||
Reflect.has(theme, 'colorWarning')
|
||||
) {
|
||||
preferences.theme.colorPrimary = builtinTypeColorPrimary || colorPrimary;
|
||||
updateMainColorVariables(preferences);
|
||||
}
|
||||
|
||||
// 更新圆角
|
||||
if (Reflect.has(theme, 'radius')) {
|
||||
document.documentElement.style.setProperty('--radius', `${radius}rem`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新主要的 CSS 变量
|
||||
* @param preference - 当前偏好设置对象,它的颜色值将被转换成 HSL 格式并设置为 CSS 变量。
|
||||
*/
|
||||
function updateMainColorVariables(preference: Preferences) {
|
||||
if (!preference.theme) {
|
||||
return;
|
||||
}
|
||||
const { colorDestructive, colorPrimary, colorSuccess, colorWarning } =
|
||||
preference.theme;
|
||||
|
||||
const colorVariables = generatorColorVariables([
|
||||
{ color: colorPrimary, name: 'primary' },
|
||||
{ alias: 'warning', color: colorWarning, name: 'yellow' },
|
||||
{ alias: 'success', color: colorSuccess, name: 'green' },
|
||||
{ alias: 'destructive', color: colorDestructive, name: 'red' },
|
||||
]);
|
||||
|
||||
if (colorPrimary) {
|
||||
document.documentElement.style.setProperty(
|
||||
'--primary',
|
||||
colorVariables['--primary-500'],
|
||||
);
|
||||
}
|
||||
|
||||
if (colorVariables['--green-500']) {
|
||||
colorVariables['--success'] = colorVariables['--green-500'];
|
||||
}
|
||||
if (colorVariables['--yellow-500']) {
|
||||
colorVariables['--warning'] = colorVariables['--yellow-500'];
|
||||
}
|
||||
if (colorVariables['--red-500']) {
|
||||
colorVariables['--destructive'] = colorVariables['--red-500'];
|
||||
}
|
||||
executeUpdateCSSVariables(colorVariables);
|
||||
}
|
||||
|
||||
function isDarkTheme(theme: string) {
|
||||
let dark = theme === 'dark';
|
||||
if (theme === 'auto') {
|
||||
dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return dark;
|
||||
}
|
||||
|
||||
export { updateCSSVariables };
|
184
packages/@core/preferences/src/use-preferences.ts
Normal file
184
packages/@core/preferences/src/use-preferences.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { diff } from '@vben-core/toolkit';
|
||||
|
||||
import { isDarkTheme, preferencesManager } from './preferences';
|
||||
|
||||
function usePreferences() {
|
||||
const preferences = preferencesManager.getPreferences();
|
||||
const initialPreferences = preferencesManager.getInitialPreferences();
|
||||
/**
|
||||
* @zh_CN 计算偏好设置的变化
|
||||
*/
|
||||
const diffPreference = computed(() => {
|
||||
return diff(initialPreferences, preferences);
|
||||
});
|
||||
|
||||
const appPreferences = computed(() => preferences.app);
|
||||
|
||||
const shortcutKeysPreferences = computed(() => preferences.shortcutKeys);
|
||||
|
||||
/**
|
||||
* @zh_CN 判断是否为暗黑模式
|
||||
* @param preferences - 当前偏好设置对象,它的主题值将被用来判断是否为暗黑模式。
|
||||
* @returns 如果主题为暗黑模式,返回 true,否则返回 false。
|
||||
*/
|
||||
const isDark = computed(() => {
|
||||
return isDarkTheme(preferences.theme.mode);
|
||||
});
|
||||
|
||||
const isMobile = computed(() => {
|
||||
return appPreferences.value.isMobile;
|
||||
});
|
||||
|
||||
const theme = computed(() => {
|
||||
return isDark.value ? 'dark' : 'light';
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 布局方式
|
||||
*/
|
||||
const layout = computed(() =>
|
||||
isMobile.value ? 'sidebar-nav' : appPreferences.value.layout,
|
||||
);
|
||||
|
||||
/**
|
||||
* @zh_CN 是否全屏显示content,不需要侧边、底部、顶部、tab区域
|
||||
*/
|
||||
const isFullContent = computed(
|
||||
() => appPreferences.value.layout === 'full-content',
|
||||
);
|
||||
|
||||
/**
|
||||
* @zh_CN 是否侧边导航模式
|
||||
*/
|
||||
const isSideNav = computed(
|
||||
() => appPreferences.value.layout === 'sidebar-nav',
|
||||
);
|
||||
|
||||
/**
|
||||
* @zh_CN 是否侧边混合模式
|
||||
*/
|
||||
const isSideMixedNav = computed(
|
||||
() => appPreferences.value.layout === 'sidebar-mixed-nav',
|
||||
);
|
||||
|
||||
/**
|
||||
* @zh_CN 是否为头部导航模式
|
||||
*/
|
||||
const isHeaderNav = computed(
|
||||
() => appPreferences.value.layout === 'header-nav',
|
||||
);
|
||||
|
||||
/**
|
||||
* @zh_CN 是否为混合导航模式
|
||||
*/
|
||||
const isMixedNav = computed(
|
||||
() => appPreferences.value.layout === 'mixed-nav',
|
||||
);
|
||||
|
||||
/**
|
||||
* @zh_CN 是否包含侧边导航模式
|
||||
*/
|
||||
const isSideMode = computed(() => {
|
||||
return isMixedNav.value || isSideMixedNav.value || isSideNav.value;
|
||||
});
|
||||
|
||||
const sidebarCollapsed = computed(() => {
|
||||
return preferences.sidebar.collapsed;
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 是否开启keep-alive
|
||||
* 在tabs可见以及开启keep-alive的情况下才开启
|
||||
*/
|
||||
const keepAlive = computed(
|
||||
() => preferences.tabbar.enable && preferences.tabbar.keepAlive,
|
||||
);
|
||||
|
||||
/**
|
||||
* @zh_CN 登录注册页面布局是否为左侧
|
||||
*/
|
||||
const authPanelLeft = computed(() => {
|
||||
return appPreferences.value.authPageLayout === 'panel-left';
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 登录注册页面布局是否为左侧
|
||||
*/
|
||||
const authPanelRight = computed(() => {
|
||||
return appPreferences.value.authPageLayout === 'panel-right';
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 登录注册页面布局是否为中间
|
||||
*/
|
||||
const authPanelCenter = computed(() => {
|
||||
return appPreferences.value.authPageLayout === 'panel-center';
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 内容是否已经最大化
|
||||
* 排除 full-content模式
|
||||
*/
|
||||
const contentIsMaximize = computed(() => {
|
||||
const headerIsHidden = preferences.header.hidden;
|
||||
const sidebarIsHidden = preferences.sidebar.hidden;
|
||||
return headerIsHidden && sidebarIsHidden && !isFullContent.value;
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 是否启用全局搜索快捷键
|
||||
*/
|
||||
const globalSearchShortcutKey = computed(() => {
|
||||
const { enable, globalSearch } = shortcutKeysPreferences.value;
|
||||
return enable && globalSearch;
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 是否启用全局注销快捷键
|
||||
*/
|
||||
const globalLogoutShortcutKey = computed(() => {
|
||||
const { enable, globalLogout } = shortcutKeysPreferences.value;
|
||||
return enable && globalLogout;
|
||||
});
|
||||
|
||||
const globalLockScreenShortcutKey = computed(() => {
|
||||
const { enable, globalLockScreen } = shortcutKeysPreferences.value;
|
||||
return enable && globalLockScreen;
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 是否启用全局偏好设置快捷键
|
||||
*/
|
||||
const globalPreferencesShortcutKey = computed(() => {
|
||||
const { enable, globalPreferences } = shortcutKeysPreferences.value;
|
||||
return enable && globalPreferences;
|
||||
});
|
||||
|
||||
return {
|
||||
authPanelCenter,
|
||||
authPanelLeft,
|
||||
authPanelRight,
|
||||
contentIsMaximize,
|
||||
diffPreference,
|
||||
globalLockScreenShortcutKey,
|
||||
globalLogoutShortcutKey,
|
||||
globalPreferencesShortcutKey,
|
||||
globalSearchShortcutKey,
|
||||
isDark,
|
||||
isFullContent,
|
||||
isHeaderNav,
|
||||
isMixedNav,
|
||||
isMobile,
|
||||
isSideMixedNav,
|
||||
isSideMode,
|
||||
isSideNav,
|
||||
keepAlive,
|
||||
layout,
|
||||
sidebarCollapsed,
|
||||
theme,
|
||||
};
|
||||
}
|
||||
|
||||
export { usePreferences };
|
6
packages/@core/preferences/tsconfig.json
Normal file
6
packages/@core/preferences/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Reference in New Issue
Block a user