chore: remove flatPreferences

pull/48/MERGE
vben 2024-06-09 12:53:38 +08:00
parent a344d32169
commit 68229a4d2f
20 changed files with 462 additions and 224 deletions

View File

@ -1,5 +1,3 @@
import type { Flatten } from '@vben-core/typings';
import type { Preferences } from './types'; import type { Preferences } from './types';
import { preferencesManager } from './preferences'; import { preferencesManager } from './preferences';
@ -8,8 +6,8 @@ import { preferencesManager } from './preferences';
const preferences: Preferences = preferencesManager.getPreferences(); const preferences: Preferences = preferencesManager.getPreferences();
// 扁平化后的偏好设置 // 扁平化后的偏好设置
const flatPreferences: Flatten<Preferences> = // const flatPreferences: Flatten<Preferences> =
preferencesManager.getFlatPreferences(); // preferencesManager.getFlatPreferences();
// 更新偏好设置 // 更新偏好设置
const updatePreferences = const updatePreferences =
@ -20,7 +18,7 @@ const resetPreferences =
preferencesManager.resetPreferences.bind(preferencesManager); preferencesManager.resetPreferences.bind(preferencesManager);
export { export {
flatPreferences, // flatPreferences,
preferences, preferences,
preferencesManager, preferencesManager,
resetPreferences, resetPreferences,

View File

@ -1,15 +1,10 @@
import type { import type { DeepPartial } from '@vben-core/typings';
DeepPartial,
Flatten,
FlattenObjectKeys,
} from '@vben-core/typings';
import type { Preferences } from './types'; import type { Preferences } from './types';
import { markRaw, reactive, watch } from 'vue'; import { markRaw, reactive, readonly, watch } from 'vue';
import { StorageManager } from '@vben-core/cache'; import { StorageManager } from '@vben-core/cache';
import { flattenObject, nestedObject } from '@vben-core/helpers';
import { convertToHslCssVar, merge } from '@vben-core/toolkit'; import { convertToHslCssVar, merge } from '@vben-core/toolkit';
import { import {
@ -40,7 +35,7 @@ function isDarkTheme(theme: string) {
class PreferenceManager { class PreferenceManager {
private cache: StorageManager | null = null; private cache: StorageManager | null = null;
private flattenedState: Flatten<Preferences>; // private flattenedState: Flatten<Preferences>;
private initialPreferences: Preferences = defaultPreferences; private initialPreferences: Preferences = defaultPreferences;
private isInitialized: boolean = false; private isInitialized: boolean = false;
private savePreferences: (preference: Preferences) => void; private savePreferences: (preference: Preferences) => void;
@ -49,7 +44,7 @@ class PreferenceManager {
}); });
constructor() { constructor() {
this.cache = new StorageManager(); this.cache = new StorageManager();
this.flattenedState = reactive(flattenObject(this.state)); // this.flattenedState = reactive(flattenObject(this.state));
this.savePreferences = useDebounceFn( this.savePreferences = useDebounceFn(
(preference: Preferences) => this._savePreferences(preference), (preference: Preferences) => this._savePreferences(preference),
@ -113,28 +108,28 @@ class PreferenceManager {
return; return;
} }
const debounceWaterState = useDebounceFn(() => { // const debounceWaterState = useDebounceFn(() => {
const newFlattenedState = flattenObject(this.state); // const newFlattenedState = flattenObject(this.state);
for (const k in newFlattenedState) { // for (const k in newFlattenedState) {
const key = k as FlattenObjectKeys<Preferences>; // const key = k as FlattenObjectKeys<Preferences>;
this.flattenedState[key] = newFlattenedState[key]; // this.flattenedState[key] = newFlattenedState[key];
} // }
this.savePreferences(this.state); // this.savePreferences(this.state);
}, 16); // }, 16);
const debounceWaterFlattenedState = useDebounceFn( // const debounceWaterFlattenedState = useDebounceFn(
(val: Flatten<Preferences>) => { // (val: Flatten<Preferences>) => {
this.updateState(val); // this.updateState(val);
this.savePreferences(this.state); // this.savePreferences(this.state);
}, // },
16, // 16,
); // );
// 监听 state 的变化 // 监听 state 的变化
watch(this.state, debounceWaterState, { deep: true }); // watch(this.state, debounceWaterState, { deep: true });
// 监听 flattenedState 的变化并触发 set 方法 // 监听 flattenedState 的变化并触发 set 方法
watch(this.flattenedState, debounceWaterFlattenedState, { deep: true }); // watch(this.flattenedState, debounceWaterFlattenedState, { deep: true });
// 监听断点,判断是否移动端 // 监听断点,判断是否移动端
const breakpoints = useBreakpoints(breakpointsTailwind); const breakpoints = useBreakpoints(breakpointsTailwind);
@ -200,10 +195,10 @@ class PreferenceManager {
* *
* @param {FlattenObject<Preferences>} newValue - * @param {FlattenObject<Preferences>} newValue -
*/ */
private updateState(newValue: Flatten<Preferences>) { // private updateState(newValue: Flatten<Preferences>) {
const nestObj = nestedObject(newValue, 2); // const nestObj = nestedObject(newValue, 2);
Object.assign(this.state, merge(nestObj, this.state)); // Object.assign(this.state, merge(nestObj, this.state));
} // }
/** /**
* *
@ -222,16 +217,16 @@ class PreferenceManager {
} }
} }
public getFlatPreferences() { // public getFlatPreferences() {
return this.flattenedState; // return this.flattenedState;
} // }
public getInitialPreferences() { public getInitialPreferences() {
return this.initialPreferences; return this.initialPreferences;
} }
public getPreferences() { public getPreferences() {
return this.state; return readonly(this.state);
} }
/** /**
@ -291,7 +286,7 @@ class PreferenceManager {
Object.assign(this.state, mergedState); Object.assign(this.state, mergedState);
Object.assign(this.flattenedState, flattenObject(this.state)); // Object.assign(this.flattenedState, flattenObject(this.state));
// 根据更新的键值执行相应的操作 // 根据更新的键值执行相应的操作
this.handleUpdates(updates); this.handleUpdates(updates);

View File

@ -10,7 +10,7 @@ type BreadcrumbStyleType = 'background' | 'normal';
type NavigationStyleType = 'plain' | 'rounded'; type NavigationStyleType = 'plain' | 'rounded';
type PageTransitionType = 'fade-slide'; type PageTransitionType = 'fade' | 'fade-down' | 'fade-slide' | 'fade-up';
type AuthPageLayoutType = 'panel-center' | 'panel-left' | 'panel-right'; type AuthPageLayoutType = 'panel-center' | 'panel-left' | 'panel-right';
@ -132,7 +132,7 @@ interface TransitionPreferences {
/** 页面切换动画是否启用 */ /** 页面切换动画是否启用 */
enable: boolean; enable: boolean;
/** 页面切换动画 */ /** 页面切换动画 */
name: PageTransitionType; name: PageTransitionType | string;
/** 是否开启页面加载进度动画 */ /** 是否开启页面加载进度动画 */
progress: boolean; progress: boolean;
} }
@ -176,6 +176,7 @@ export type {
LayoutType, LayoutType,
LogoPreferences, LogoPreferences,
NavigationPreferences, NavigationPreferences,
NavigationStyleType,
PageTransitionType, PageTransitionType,
Preferences, Preferences,
PreferencesKeys, PreferencesKeys,

View File

@ -6,7 +6,6 @@ import { isDarkTheme, preferencesManager } from './preferences';
function usePreferences() { function usePreferences() {
const preferences = preferencesManager.getPreferences(); const preferences = preferencesManager.getPreferences();
const flatPreferences = preferencesManager.getFlatPreferences();
const initialPreferences = preferencesManager.getInitialPreferences(); const initialPreferences = preferencesManager.getInitialPreferences();
/** /**
* @zh_CN * @zh_CN
@ -15,13 +14,15 @@ function usePreferences() {
return diff(initialPreferences, preferences); return diff(initialPreferences, preferences);
}); });
const appPreferences = computed(() => preferences.app);
/** /**
* @zh_CN * @zh_CN
* @param preferences - * @param preferences -
* @returns true false * @returns true false
*/ */
const isDark = computed(() => { const isDark = computed(() => {
return isDarkTheme(flatPreferences.appThemeMode); return isDarkTheme(appPreferences.value.themeMode);
}); });
const theme = computed(() => { const theme = computed(() => {
@ -32,39 +33,41 @@ function usePreferences() {
* @zh_CN * @zh_CN
*/ */
const layout = computed(() => const layout = computed(() =>
flatPreferences.appIsMobile ? 'side-nav' : flatPreferences.appLayout, appPreferences.value.isMobile ? 'side-nav' : appPreferences.value.layout,
); );
/** /**
* @zh_CN contenttab * @zh_CN contenttab
*/ */
const isFullContent = computed( const isFullContent = computed(
() => flatPreferences.appLayout === 'full-content', () => appPreferences.value.layout === 'full-content',
); );
/** /**
* @zh_CN * @zh_CN
*/ */
const isSideNav = computed(() => flatPreferences.appLayout === 'side-nav'); const isSideNav = computed(() => appPreferences.value.layout === 'side-nav');
/** /**
* @zh_CN * @zh_CN
*/ */
const isSideMixedNav = computed( const isSideMixedNav = computed(
() => flatPreferences.appLayout === 'side-mixed-nav', () => appPreferences.value.layout === 'side-mixed-nav',
); );
/** /**
* @zh_CN * @zh_CN
*/ */
const isHeaderNav = computed( const isHeaderNav = computed(
() => flatPreferences.appLayout === 'header-nav', () => appPreferences.value.layout === 'header-nav',
); );
/** /**
* @zh_CN * @zh_CN
*/ */
const isMixedNav = computed(() => flatPreferences.appLayout === 'mixed-nav'); const isMixedNav = computed(
() => appPreferences.value.layout === 'mixed-nav',
);
/** /**
* @zh_CN * @zh_CN
@ -78,28 +81,28 @@ function usePreferences() {
* tabskeep-alive * tabskeep-alive
*/ */
const keepAlive = computed( const keepAlive = computed(
() => flatPreferences.tabbarKeepAlive && flatPreferences.tabbarEnable, () => preferences.tabbar.enable && preferences.tabbar.keepAlive,
); );
/** /**
* @zh_CN * @zh_CN
*/ */
const authPanelLeft = computed(() => { const authPanelLeft = computed(() => {
return flatPreferences.appAuthPageLayout === 'panel-left'; return appPreferences.value.authPageLayout === 'panel-left';
}); });
/** /**
* @zh_CN * @zh_CN
*/ */
const authPanelRight = computed(() => { const authPanelRight = computed(() => {
return flatPreferences.appAuthPageLayout === 'panel-right'; return appPreferences.value.authPageLayout === 'panel-right';
}); });
/** /**
* @zh_CN * @zh_CN
*/ */
const authPanelCenter = computed(() => { const authPanelCenter = computed(() => {
return flatPreferences.appAuthPageLayout === 'panel-center'; return appPreferences.value.authPageLayout === 'panel-center';
}); });
return { return {

View File

@ -5,7 +5,12 @@ import { computed } from 'vue';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { MdiDockBottom, MdiDockLeft, MdiDockRight } from '@vben-core/iconify'; import { MdiDockBottom, MdiDockLeft, MdiDockRight } from '@vben-core/iconify';
import { preferences, usePreferences } from '@vben-core/preferences'; import {
type AuthPageLayoutType,
preferences,
updatePreferences,
usePreferences,
} from '@vben-core/preferences';
import { VbenDropdownRadioMenu, VbenIconButton } from '@vben-core/shadcn-ui'; import { VbenDropdownRadioMenu, VbenIconButton } from '@vben-core/shadcn-ui';
defineOptions({ defineOptions({
@ -31,12 +36,21 @@ const menus = computed((): VbenDropdownMenuItem[] => [
]); ]);
const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences(); const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences();
function handleUpdate(value: string) {
updatePreferences({
app: {
authPageLayout: value as AuthPageLayoutType,
},
});
}
</script> </script>
<template> <template>
<VbenDropdownRadioMenu <VbenDropdownRadioMenu
v-model="preferences.app.authPageLayout"
:menus="menus" :menus="menus"
:model-value="preferences.app.authPageLayout"
@update:model-value="handleUpdate"
> >
<VbenIconButton> <VbenIconButton>
<MdiDockRight v-if="authPanelRight" class="size-5" /> <MdiDockRight v-if="authPanelRight" class="size-5" />

View File

@ -6,30 +6,30 @@ import SwitchItem from '../switch-item.vue';
defineOptions({ defineOptions({
name: 'PreferenceAnimation', name: 'PreferenceAnimation',
}); });
const pageProgress = defineModel<boolean>('pageProgress', { const transitionProgress = defineModel<boolean>('transitionProgress', {
// //
default: false, default: false,
}); });
const pageTransition = defineModel<string>('pageTransition'); const transitionName = defineModel<string>('transitionName');
const pageTransitionEnable = defineModel<boolean>('pageTransitionEnable'); const transitionEnable = defineModel<boolean>('transitionEnable');
const transitionPreset = ['fade', 'fade-slide', 'fade-up', 'fade-down']; const transitionPreset = ['fade', 'fade-slide', 'fade-up', 'fade-down'];
function handleClick(value: string) { function handleClick(value: string) {
pageTransition.value = value; transitionName.value = value;
} }
</script> </script>
<template> <template>
<SwitchItem v-model="pageProgress"> <SwitchItem v-model="transitionProgress">
{{ $t('preference.page-progress') }} {{ $t('preference.page-progress') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="pageTransitionEnable"> <SwitchItem v-model="transitionEnable">
{{ $t('preference.page-transition') }} {{ $t('preference.page-transition') }}
</SwitchItem> </SwitchItem>
<div <div
v-if="pageTransitionEnable" v-if="transitionEnable"
class="mb-2 mt-3 flex justify-between gap-3 px-2" class="mb-2 mt-3 flex justify-between gap-3 px-2"
> >
<div <div
@ -37,7 +37,7 @@ function handleClick(value: string) {
:key="item" :key="item"
class="outline-box p-2" class="outline-box p-2"
:class="{ :class="{
'outline-box-active': pageTransition === item, 'outline-box-active': transitionName === item,
}" }"
@click="handleClick(item)" @click="handleClick(item)"
> >

View File

@ -11,9 +11,9 @@ defineOptions({
name: 'PreferenceGeneralConfig', name: 'PreferenceGeneralConfig',
}); });
const locale = defineModel<string>('locale'); const appLocale = defineModel<string>('appLocale');
const dynamicTitle = defineModel<boolean>('dynamicTitle'); const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
const shortcutKeys = defineModel<boolean>('shortcutKeys'); const shortcutKeysEnable = defineModel<boolean>('shortcutKeysEnable');
const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({ const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
label: item.text, label: item.text,
@ -22,13 +22,13 @@ const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
</script> </script>
<template> <template>
<SelectItem v-model="locale" :items="localeItems"> <SelectItem v-model="appLocale" :items="localeItems">
{{ $t('preference.language') }} {{ $t('preference.language') }}
</SelectItem> </SelectItem>
<SwitchItem v-model="dynamicTitle"> <SwitchItem v-model="appDynamicTitle">
{{ $t('preference.dynamic-title') }} {{ $t('preference.dynamic-title') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="shortcutKeys"> <SwitchItem v-model="shortcutKeysEnable">
{{ $t('preference.shortcut-key') }} {{ $t('preference.shortcut-key') }}
</SwitchItem> </SwitchItem>
</template> </template>

View File

@ -8,7 +8,7 @@ export { default as Header } from './layout/header.vue';
export { default as Layout } from './layout/layout.vue'; export { default as Layout } from './layout/layout.vue';
export { default as Navigation } from './layout/navigation.vue'; export { default as Navigation } from './layout/navigation.vue';
export { default as Sidebar } from './layout/sidebar.vue'; export { default as Sidebar } from './layout/sidebar.vue';
export { default as Tabs } from './layout/tabs.vue'; export { default as Tabbar } from './layout/tabbar.vue';
export { default as SwitchItem } from './switch-item.vue'; export { default as SwitchItem } from './switch-item.vue';
export { default as ThemeColor } from './theme/color.vue'; export { default as ThemeColor } from './theme/color.vue';
export { default as ColorMode } from './theme/color-mode.vue'; export { default as ColorMode } from './theme/color-mode.vue';

View File

@ -1,6 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SelectListItem } from '@vben/types'; import type { SelectListItem } from '@vben/types';
import { computed } from 'vue';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import SwitchItem from '../switch-item.vue'; import SwitchItem from '../switch-item.vue';
@ -10,46 +12,41 @@ defineOptions({
name: 'PreferenceBreadcrumbConfig', name: 'PreferenceBreadcrumbConfig',
}); });
defineProps<{ disabled: boolean }>(); const props = defineProps<{ disabled?: boolean }>();
const breadcrumbVisible = defineModel<boolean>('breadcrumbVisible'); const breadcrumbEnable = defineModel<boolean>('breadcrumbEnable');
const breadcrumbIcon = defineModel<boolean>('breadcrumbIcon'); const breadcrumbShowIcon = defineModel<boolean>('breadcrumbShowIcon');
const breadcrumbStyle = defineModel<string>('breadcrumbStyle'); const breadcrumbStyleType = defineModel<string>('breadcrumbStyleType');
const breadcrumbHome = defineModel<boolean>('breadcrumbHome'); const breadcrumbShowHome = defineModel<boolean>('breadcrumbShowHome');
const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne'); const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
const typeItems: SelectListItem[] = [ const typeItems: SelectListItem[] = [
{ label: $t('preference.normal'), value: 'normal' }, { label: $t('preference.normal'), value: 'normal' },
{ label: $t('preference.breadcrumb-background'), value: 'background' }, { label: $t('preference.breadcrumb-background'), value: 'background' },
]; ];
const disableItem = computed(() => {
return !breadcrumbEnable.value || props.disabled;
});
</script> </script>
<template> <template>
<SwitchItem v-model="breadcrumbVisible" :disabled="disabled"> <SwitchItem v-model="breadcrumbEnable" :disabled="disabled">
{{ $t('preference.breadcrumb-enable') }} {{ $t('preference.breadcrumb-enable') }}
</SwitchItem> </SwitchItem>
<SwitchItem <SwitchItem v-model="breadcrumbHideOnlyOne" :disabled="disableItem">
v-model="breadcrumbHideOnlyOne"
:disabled="!breadcrumbVisible || disabled"
>
{{ $t('preference.breadcrumb-hide-only-one') }} {{ $t('preference.breadcrumb-hide-only-one') }}
</SwitchItem> </SwitchItem>
<SwitchItem <SwitchItem v-model="breadcrumbShowHome" :disabled="disableItem">
v-model="breadcrumbHome"
:disabled="!breadcrumbVisible || disabled"
>
{{ $t('preference.breadcrumb-home') }} {{ $t('preference.breadcrumb-home') }}
</SwitchItem> </SwitchItem>
<SwitchItem <SwitchItem v-model="breadcrumbShowIcon" :disabled="disableItem">
v-model="breadcrumbIcon"
:disabled="!breadcrumbVisible || disabled"
>
{{ $t('preference.breadcrumb-icon') }} {{ $t('preference.breadcrumb-icon') }}
</SwitchItem> </SwitchItem>
<ToggleItem <ToggleItem
v-model="breadcrumbStyle" v-model="breadcrumbStyleType"
:items="typeItems" :items="typeItems"
:disabled="!breadcrumbVisible || disabled" :disabled="disableItem"
> >
{{ $t('preference.breadcrumb-style') }} {{ $t('preference.breadcrumb-style') }}
</ToggleItem> </ToggleItem>

View File

@ -7,15 +7,15 @@ defineOptions({
name: 'PreferenceBreadcrumbConfig', name: 'PreferenceBreadcrumbConfig',
}); });
const footerVisible = defineModel<boolean>('footerVisible'); const footerEnable = defineModel<boolean>('footerEnable');
const footerFixed = defineModel<boolean>('footerFixed'); const footerFixed = defineModel<boolean>('footerFixed');
</script> </script>
<template> <template>
<SwitchItem v-model="footerVisible"> <SwitchItem v-model="footerEnable">
{{ $t('preference.footer-visible') }} {{ $t('preference.footer-visible') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="footerFixed" :disabled="!footerVisible"> <SwitchItem v-model="footerFixed" :disabled="!footerEnable">
{{ $t('preference.footer-fixed') }} {{ $t('preference.footer-fixed') }}
</SwitchItem> </SwitchItem>
</template> </template>

View File

@ -12,7 +12,7 @@ defineOptions({
defineProps<{ disabled: boolean }>(); defineProps<{ disabled: boolean }>();
const headerVisible = defineModel<boolean>('headerVisible'); const headerEnable = defineModel<boolean>('headerEnable');
const headerMode = defineModel<LayoutHeaderModeType>('headerMode'); const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
const localeItems: SelectListItem[] = [ const localeItems: SelectListItem[] = [
@ -36,13 +36,13 @@ const localeItems: SelectListItem[] = [
</script> </script>
<template> <template>
<SwitchItem v-model="headerVisible" :disabled="disabled"> <SwitchItem v-model="headerEnable" :disabled="disabled">
{{ $t('preference.header-visible') }} {{ $t('preference.header-visible') }}
</SwitchItem> </SwitchItem>
<SelectItem <SelectItem
v-model="headerMode" v-model="headerMode"
:items="localeItems" :items="localeItems"
:disabled="!headerVisible" :disabled="!headerEnable"
> >
{{ $t('preference.mode') }} {{ $t('preference.mode') }}
</SelectItem> </SelectItem>

View File

@ -12,7 +12,7 @@ defineOptions({
defineProps<{ disabled?: boolean; disabledNavigationSplit?: boolean }>(); defineProps<{ disabled?: boolean; disabledNavigationSplit?: boolean }>();
const navigationStyle = defineModel<string>('navigationStyle'); const navigationStyleType = defineModel<string>('navigationStyleType');
const navigationSplit = defineModel<boolean>('navigationSplit'); const navigationSplit = defineModel<boolean>('navigationSplit');
const navigationAccordion = defineModel<boolean>('navigationAccordion'); const navigationAccordion = defineModel<boolean>('navigationAccordion');
@ -24,7 +24,7 @@ const stylesItems: SelectListItem[] = [
<template> <template>
<ToggleItem <ToggleItem
v-model="navigationStyle" v-model="navigationStyleType"
:items="stylesItems" :items="stylesItems"
:disabled="disabled" :disabled="disabled"
> >

View File

@ -9,21 +9,23 @@ defineOptions({
defineProps<{ disabled: boolean }>(); defineProps<{ disabled: boolean }>();
const sideVisible = defineModel<boolean>('sideVisible'); const sidebarEnable = defineModel<boolean>('sidebarEnable');
const sideCollapseShowTitle = defineModel<boolean>('sideCollapseShowTitle'); const sidebarCollapseShowTitle = defineModel<boolean>(
const sideCollapse = defineModel<boolean>('sideCollapse'); 'sidebarCollapseShowTitle',
);
const sidebarCollapse = defineModel<boolean>('sidebarCollapse');
</script> </script>
<template> <template>
<SwitchItem v-model="sideVisible" :disabled="disabled"> <SwitchItem v-model="sidebarEnable" :disabled="disabled">
{{ $t('preference.side-visible') }} {{ $t('preference.side-visible') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="sideCollapse" :disabled="!sideVisible || disabled"> <SwitchItem v-model="sidebarCollapse" :disabled="!sidebarEnable || disabled">
{{ $t('preference.collapse') }} {{ $t('preference.collapse') }}
</SwitchItem> </SwitchItem>
<SwitchItem <SwitchItem
v-model="sideCollapseShowTitle" v-model="sidebarCollapseShowTitle"
:disabled="!sideVisible || disabled" :disabled="!sidebarEnable || disabled"
> >
{{ $t('preference.collapse-show-title') }} {{ $t('preference.collapse-show-title') }}
</SwitchItem> </SwitchItem>

View File

@ -9,15 +9,15 @@ defineOptions({
defineProps<{ disabled?: boolean }>(); defineProps<{ disabled?: boolean }>();
const tabsVisible = defineModel<boolean>('tabsVisible'); const tabbarEnable = defineModel<boolean>('tabbarEnable');
const tabsIcon = defineModel<boolean>('tabsIcon'); const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
</script> </script>
<template> <template>
<SwitchItem v-model="tabsVisible" :disabled="disabled"> <SwitchItem v-model="tabbarEnable" :disabled="disabled">
{{ $t('preference.tabs-visible') }} {{ $t('preference.tabs-visible') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="tabsIcon" :disabled="!tabsVisible"> <SwitchItem v-model="tabbarShowIcon" :disabled="!tabbarEnable">
{{ $t('preference.tabs-icon') }} {{ $t('preference.tabs-icon') }}
</SwitchItem> </SwitchItem>
<!-- <SwitchItem v-model="sideCollapseShowTitle" :disabled="!tabsVisible"> <!-- <SwitchItem v-model="sideCollapseShowTitle" :disabled="!tabsVisible">

View File

@ -7,20 +7,20 @@ defineOptions({
name: 'PreferenceColorMode', name: 'PreferenceColorMode',
}); });
const colorWeakMode = defineModel<boolean>('colorWeakMode', { const appColorWeakMode = defineModel<boolean>('appColorWeakMode', {
default: false, default: false,
}); });
const colorGrayMode = defineModel<boolean>('colorGrayMode', { const appColorGrayMode = defineModel<boolean>('appColorGrayMode', {
default: false, default: false,
}); });
</script> </script>
<template> <template>
<SwitchItem v-model="colorWeakMode"> <SwitchItem v-model="appColorWeakMode">
{{ $t('preference.weak-mode') }} {{ $t('preference.weak-mode') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="colorGrayMode"> <SwitchItem v-model="appColorGrayMode">
{{ $t('preference.gray-mode') }} {{ $t('preference.gray-mode') }}
</SwitchItem> </SwitchItem>
</template> </template>

View File

@ -13,7 +13,7 @@ defineOptions({
}); });
const modelValue = defineModel<string>({ default: 'auto' }); const modelValue = defineModel<string>({ default: 'auto' });
const semiDarkMenu = defineModel<boolean>('semiDarkMenu', { const appSemiDarkMenu = defineModel<boolean>('appSemiDarkMenu', {
default: true, default: true,
}); });
@ -71,7 +71,7 @@ function nameView(name: string) {
</template> </template>
<SwitchItem <SwitchItem
v-model="semiDarkMenu" v-model="appSemiDarkMenu"
:disabled="modelValue !== 'light'" :disabled="modelValue !== 'light'"
class="mt-6" class="mt-6"
> >

View File

@ -1,57 +1,227 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { SupportedLanguagesType } from '@vben/types';
import { loadLocaleMessages } from '@vben/locales'; import { loadLocaleMessages } from '@vben/locales';
import { import {
COLOR_PRIMARY_RESETS, COLOR_PRIMARY_RESETS,
flatPreferences, preferences,
updatePreferences, updatePreferences,
} from '@vben-core/preferences'; } from '@vben-core/preferences';
import Preferences from './preferences.vue'; import Preferences from './preferences.vue';
function updateLocale(value: string) {
const locale = value as SupportedLanguagesType;
updatePreferences({
app: { locale },
});
//
loadLocaleMessages(locale);
}
</script> </script>
<template> <template>
<Preferences <Preferences
v-model:breadcrumb-visible="flatPreferences.breadcrumbEnable"
v-model:breadcrumb-style="flatPreferences.breadcrumbStyleType"
v-model:color-gray-mode="flatPreferences.appColorGrayMode"
v-model:breadcrumb-icon="flatPreferences.breadcrumbShowIcon"
v-model:color-primary="flatPreferences.themeColorPrimary"
v-model:color-weak-mode="flatPreferences.appColorWeakMode"
v-model:content-compact="flatPreferences.appContentCompact"
v-model:breadcrumb-home="flatPreferences.breadcrumbShowHome"
v-model:side-collapse="flatPreferences.sidebarCollapse"
v-model:layout="flatPreferences.appLayout"
v-model:semi-dark-menu="flatPreferences.appSemiDarkMenu"
v-model:side-visible="flatPreferences.sidebarEnable"
v-model:footer-visible="flatPreferences.footerEnable"
v-model:tabs-visible="flatPreferences.tabbarEnable"
v-model:header-visible="flatPreferences.headerEnable"
v-model:header-mode="flatPreferences.headerMode"
v-model:footer-fixed="flatPreferences.footerFixed"
v-model:theme="flatPreferences.appThemeMode"
v-model:dynamic-title="flatPreferences.appDynamicTitle"
v-model:breadcrumb-hide-only-one="flatPreferences.breadcrumbHideOnlyOne"
v-model:page-transition="flatPreferences.transitionName"
v-model:page-progress="flatPreferences.transitionProgress"
v-model:tabs-icon="flatPreferences.tabbarShowIcon"
v-model:navigation-accordion="flatPreferences.navigationAccordion"
v-model:navigation-style="flatPreferences.navigationStyleType"
v-model:shortcut-keys="flatPreferences.shortcutKeysEnable"
v-model:navigation-split="flatPreferences.navigationSplit"
v-model:page-transition-enable="flatPreferences.transitionEnable"
v-model:side-collapse-show-title="flatPreferences.sidebarCollapseShowTitle"
:color-primary-presets="COLOR_PRIMARY_RESETS" :color-primary-presets="COLOR_PRIMARY_RESETS"
:locale="flatPreferences.appLocale" :app-locale="preferences.app.locale"
@update:locale="updateLocale" :app-layout="preferences.app.layout"
:app-dynamic-title="preferences.app.dynamicTitle"
:app-theme-mode="preferences.app.themeMode"
:app-color-gray-mode="preferences.app.colorGrayMode"
:app-color-weak-mode="preferences.app.colorWeakMode"
:app-semi-dark-menu="preferences.app.semiDarkMenu"
:app-content-compact="preferences.app.contentCompact"
:transition-enable="preferences.transition.enable"
:transition-name="preferences.transition.name"
:transition-progress="preferences.transition.progress"
:theme-color-primary="preferences.theme.colorPrimary"
:sidebar-enable="preferences.sidebar.enable"
:sidebar-collapse="preferences.sidebar.collapse"
:sidebar-collapse-show-title="preferences.sidebar.collapseShowTitle"
:header-enable="preferences.header.enable"
:header-mode="preferences.header.mode"
:breadcrumb-enable="preferences.breadcrumb.enable"
:breadcrumb-style="preferences.breadcrumb.styleType"
:breadcrumb-icon="preferences.breadcrumb.showIcon"
:breadcrumb-home="preferences.breadcrumb.showHome"
:breadcrumb-hide-only-one="preferences.breadcrumb.hideOnlyOne"
:tabbar-enable="preferences.tabbar.enable"
:tabbar-show-icon="preferences.tabbar.showIcon"
:navigation-accordion="preferences.navigation.accordion"
:navigation-style-type="preferences.navigation.styleType"
:navigation-split="preferences.navigation.split"
:footer-enable="preferences.footer.enable"
:footer-fixed="preferences.footer.fixed"
:shortcut-keys-enable="preferences.shortcutKeys.enable"
@update:app-locale="
(val) => {
updatePreferences({
app: { locale: val },
});
loadLocaleMessages(val);
}
"
@update:app-layout="
(val) =>
updatePreferences({
app: { layout: val },
})
"
@update:app-dynamic-title="
(val) =>
updatePreferences({
app: { dynamicTitle: val },
})
"
@update:app-theme-mode="
(val) =>
updatePreferences({
app: { themeMode: val },
})
"
@update:app-color-gray-mode="
(val) =>
updatePreferences({
app: { colorGrayMode: val },
})
"
@update:app-color-weak-mode="
(val) =>
updatePreferences({
app: { colorWeakMode: val },
})
"
@update:app-semi-dark-menu="
(val) =>
updatePreferences({
app: { semiDarkMenu: val },
})
"
@update:app-content-compact="
(val) =>
updatePreferences({
app: { contentCompact: val },
})
"
@update:transition-enable="
(val) =>
updatePreferences({
transition: { enable: val },
})
"
@update:transition-name="
(val) =>
updatePreferences({
transition: { name: val },
})
"
@update:transition-progress="
(val) =>
updatePreferences({
transition: { progress: val },
})
"
@update:theme-color-primary="
(val) =>
updatePreferences({
theme: { colorPrimary: val },
})
"
@update:sidebar-enable="
(val) =>
updatePreferences({
sidebar: { enable: val },
})
"
@update:sidebar-collapse="
(val) =>
updatePreferences({
sidebar: { collapse: val },
})
"
@update:sidebar-collapse-show-title="
(val) =>
updatePreferences({
sidebar: { collapseShowTitle: val },
})
"
@update:header-enable="
(val) =>
updatePreferences({
header: { enable: val },
})
"
@update:header-mode="
(val) =>
updatePreferences({
header: { mode: val },
})
"
@update:breadcrumb-enable="
(val) =>
updatePreferences({
breadcrumb: { enable: val },
})
"
@update:breadcrumb-style-type="
(val) =>
updatePreferences({
breadcrumb: { styleType: val },
})
"
@update:breadcrumb-show-icon="
(val) =>
updatePreferences({
breadcrumb: { showIcon: val },
})
"
@update:breadcrumb-show-home="
(val) =>
updatePreferences({
breadcrumb: { showHome: val },
})
"
@update:breadcrumb-hide-only-one="
(val) =>
updatePreferences({
breadcrumb: { hideOnlyOne: val },
})
"
@update:tabbar-enable="
(val) =>
updatePreferences({
tabbar: { enable: val },
})
"
@update:tabbar-show-icon="
(val) =>
updatePreferences({
tabbar: { showIcon: val },
})
"
@update:navigation-accordion="
(val) =>
updatePreferences({
navigation: { accordion: val },
})
"
@update:navigation-style-type="
(val) =>
updatePreferences({
navigation: { styleType: val },
})
"
@update:navigation-split="
(val) =>
updatePreferences({
navigation: { split: val },
})
"
@update:footer-enable="
(val) =>
updatePreferences({
footer: { enable: val },
})
"
@update:footer-fixed="
(val) =>
updatePreferences({
footer: { fixed: val },
})
"
@update:shortcut-keys-enable="
(val) =>
updatePreferences({
shortcutKeys: { enable: val },
})
"
/> />
</template> </template>

View File

@ -1,5 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import type { LayoutHeaderModeType, LayoutType } from '@vben/types'; import type {
ContentCompactType,
LayoutHeaderModeType,
LayoutType,
SupportedLanguagesType,
ThemeModeType,
} from '@vben/types';
import type {
BreadcrumbStyleType,
NavigationStyleType,
} from '@vben-core/preferences';
import type { SegmentedItem } from '@vben-core/shadcn-ui'; import type { SegmentedItem } from '@vben-core/shadcn-ui';
import { computed } from 'vue'; import { computed } from 'vue';
@ -33,7 +43,7 @@ import {
Layout, Layout,
Navigation, Navigation,
Sidebar, Sidebar,
Tabs, Tabbar,
Theme, Theme,
ThemeColor, ThemeColor,
} from './blocks'; } from './blocks';
@ -44,38 +54,54 @@ withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), {
colorPrimaryPresets: () => [], colorPrimaryPresets: () => [],
}); });
const theme = defineModel<string>('theme'); const appThemeMode = defineModel<ThemeModeType>('appThemeMode');
const locale = defineModel<string>('locale'); const appLocale = defineModel<SupportedLanguagesType>('appLocale');
const dynamicTitle = defineModel<boolean>('dynamicTitle'); const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
const semiDarkMenu = defineModel<boolean>('semiDarkMenu'); const appLayout = defineModel<LayoutType>('appLayout');
const breadcrumbVisible = defineModel<boolean>('breadcrumbVisible'); const appColorGrayMode = defineModel<boolean>('appColorGrayMode');
const breadcrumbIcon = defineModel<boolean>('breadcrumbIcon'); const appColorWeakMode = defineModel<boolean>('appColorWeakMode');
const breadcrumbHome = defineModel<boolean>('breadcrumbHome'); const appSemiDarkMenu = defineModel<boolean>('appSemiDarkMenu');
const breadcrumbStyle = defineModel<string>('breadcrumbStyle'); const appContentCompact = defineModel<ContentCompactType>('appContentCompact');
const transitionProgress = defineModel<boolean>('transitionProgress');
const transitionName = defineModel<string>('transitionName');
const transitionEnable = defineModel<boolean>('transitionEnable');
const themeColorPrimary = defineModel<string>('themeColorPrimary');
const sidebarEnable = defineModel<boolean>('sidebarEnable');
const sidebarCollapse = defineModel<boolean>('sidebarCollapse');
const sidebarCollapseShowTitle = defineModel<boolean>(
'sidebarCollapseShowTitle',
);
const headerEnable = defineModel<boolean>('headerEnable');
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
const breadcrumbEnable = defineModel<boolean>('breadcrumbEnable');
const breadcrumbShowIcon = defineModel<boolean>('breadcrumbShowIcon');
const breadcrumbShowHome = defineModel<boolean>('breadcrumbShowHome');
const breadcrumbStyleType = defineModel<BreadcrumbStyleType>(
'breadcrumbStyleType',
);
const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne'); const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
const sideCollapseShowTitle = defineModel<boolean>('sideCollapseShowTitle');
const sideCollapse = defineModel<boolean>('sideCollapse'); const tabbarEnable = defineModel<boolean>('tabbarEnable');
const colorWeakMode = defineModel<boolean>('colorWeakMode'); const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
const colorGrayMode = defineModel<boolean>('colorGrayMode');
const colorPrimary = defineModel<string>('colorPrimary'); const navigationStyleType = defineModel<NavigationStyleType>(
const navigationStyle = defineModel<string>('navigationStyle'); 'navigationStyleType',
);
const navigationSplit = defineModel<boolean>('navigationSplit'); const navigationSplit = defineModel<boolean>('navigationSplit');
const navigationAccordion = defineModel<boolean>('navigationAccordion'); const navigationAccordion = defineModel<boolean>('navigationAccordion');
const pageProgress = defineModel<boolean>('pageProgress');
const pageTransition = defineModel<string>('pageTransition');
const pageTransitionEnable = defineModel<boolean>('pageTransitionEnable');
const layout = defineModel<LayoutType>('layout');
const contentCompact = defineModel<string>('contentCompact');
const sideVisible = defineModel<boolean>('sideVisible');
const shortcutKeys = defineModel<boolean>('shortcutKeys');
const tabsVisible = defineModel<boolean>('tabsVisible');
const tabsIcon = defineModel<boolean>('tabsIcon');
// const logoVisible = defineModel<boolean>('logoVisible'); // const logoVisible = defineModel<boolean>('logoVisible');
const headerVisible = defineModel<boolean>('headerVisible');
const headerMode = defineModel<LayoutHeaderModeType>('headerMode'); const footerEnable = defineModel<boolean>('footerEnable');
const footerVisible = defineModel<boolean>('footerVisible');
const footerFixed = defineModel<boolean>('footerFixed'); const footerFixed = defineModel<boolean>('footerFixed');
const shortcutKeysEnable = defineModel<boolean>('shortcutKeysEnable');
const { const {
diffPreference, diffPreference,
isFullContent, isFullContent,
@ -162,41 +188,44 @@ function handleReset() {
<VbenSegmented :tabs="tabs" default-value="appearance"> <VbenSegmented :tabs="tabs" default-value="appearance">
<template #appearance> <template #appearance>
<Block :title="$t('preference.theme')"> <Block :title="$t('preference.theme')">
<Theme v-model="theme" v-model:semi-dark-menu="semiDarkMenu" /> <Theme
v-model="appThemeMode"
v-model:app-semi-dark-menu="appSemiDarkMenu"
/>
</Block> </Block>
<Block :title="$t('preference.theme-color')"> <Block :title="$t('preference.theme-color')">
<ThemeColor <ThemeColor
v-model="colorPrimary" v-model="themeColorPrimary"
:color-primary-presets="colorPrimaryPresets" :color-primary-presets="colorPrimaryPresets"
/> />
</Block> </Block>
<Block :title="$t('preference.other')"> <Block :title="$t('preference.other')">
<ColorMode <ColorMode
v-model:color-gray-mode="colorGrayMode" v-model:app-color-gray-mode="appColorGrayMode"
v-model:color-weak-mode="colorWeakMode" v-model:app-color-weak-mode="appColorWeakMode"
/> />
</Block> </Block>
</template> </template>
<template #layout> <template #layout>
<Block :title="$t('preference.layout')"> <Block :title="$t('preference.layout')">
<Layout v-model="layout" /> <Layout v-model="appLayout" />
</Block> </Block>
<Block :title="$t('preference.content')"> <Block :title="$t('preference.content')">
<Content v-model="contentCompact" /> <Content v-model="appContentCompact" />
</Block> </Block>
<Block :title="$t('preference.sidebar')"> <Block :title="$t('preference.sidebar')">
<Sidebar <Sidebar
v-model:side-visible="sideVisible" v-model:sidebar-enable="sidebarEnable"
v-model:side-collapse="sideCollapse" v-model:sidebar-collapse="sidebarCollapse"
v-model:side-collapse-show-title="sideCollapseShowTitle" v-model:side-collapse-show-title="sidebarCollapseShowTitle"
:disabled="!isSideMode" :disabled="!isSideMode"
/> />
</Block> </Block>
<Block :title="$t('preference.header')"> <Block :title="$t('preference.header')">
<Header <Header
v-model:header-visible="headerVisible" v-model:headerEnable="headerEnable"
v-model:headerMode="headerMode" v-model:headerMode="headerMode"
:disabled="isFullContent" :disabled="isFullContent"
/> />
@ -204,7 +233,7 @@ function handleReset() {
<Block :title="$t('preference.navigation-menu')"> <Block :title="$t('preference.navigation-menu')">
<Navigation <Navigation
v-model:navigation-style="navigationStyle" v-model:navigation-style-type="navigationStyleType"
v-model:navigation-split="navigationSplit" v-model:navigation-split="navigationSplit"
v-model:navigation-accordion="navigationAccordion" v-model:navigation-accordion="navigationAccordion"
:disabled="isFullContent" :disabled="isFullContent"
@ -214,10 +243,10 @@ function handleReset() {
<Block :title="$t('preference.breadcrumb')"> <Block :title="$t('preference.breadcrumb')">
<Breadcrumb <Breadcrumb
v-model:breadcrumb-visible="breadcrumbVisible" v-model:breadcrumb-enable="breadcrumbEnable"
v-model:breadcrumb-icon="breadcrumbIcon" v-model:breadcrumb-show-icon="breadcrumbShowIcon"
v-model:breadcrumb-style="breadcrumbStyle" v-model:breadcrumb-style-type="breadcrumbStyleType"
v-model:breadcrumb-home="breadcrumbHome" v-model:breadcrumb-show-home="breadcrumbShowHome"
v-model:breadcrumb-hide-only-one="breadcrumbHideOnlyOne" v-model:breadcrumb-hide-only-one="breadcrumbHideOnlyOne"
:disabled=" :disabled="
!showBreadcrumbConfig || !(isSideNav || isSideMixedNav) !showBreadcrumbConfig || !(isSideNav || isSideMixedNav)
@ -226,14 +255,14 @@ function handleReset() {
</Block> </Block>
<Block :title="$t('preference.tabs')"> <Block :title="$t('preference.tabs')">
<Tabs <Tabbar
v-model:tabs-visible="tabsVisible" v-model:tabbar-enable="tabbarEnable"
v-model:tabs-icon="tabsIcon" v-model:tabbar-show-icon="tabbarShowIcon"
/> />
</Block> </Block>
<Block :title="$t('preference.footer')"> <Block :title="$t('preference.footer')">
<Footer <Footer
v-model:footer-visible="footerVisible" v-model:footer-enable="footerEnable"
v-model:footer-fixed="footerFixed" v-model:footer-fixed="footerFixed"
/> />
</Block> </Block>
@ -241,21 +270,21 @@ function handleReset() {
<template #general> <template #general>
<Block :title="$t('preference.general')"> <Block :title="$t('preference.general')">
<General <General
v-model:locale="locale" v-model:app-locale="appLocale"
v-model:dynamic-title="dynamicTitle" v-model:app-dynamic-title="appDynamicTitle"
v-model:shortcut-keys="shortcutKeys" v-model:shortcut-keys-enable="shortcutKeysEnable"
/> />
</Block> </Block>
<Block :title="$t('preference.animation')"> <Block :title="$t('preference.animation')">
<Animation <Animation
v-model:page-progress="pageProgress" v-model:transition-progress="transitionProgress"
v-model:page-transition="pageTransition" v-model:transition-name="transitionName"
v-model:page-transition-enable="pageTransitionEnable" v-model:transition-enable="transitionEnable"
/> />
</Block> </Block>
</template> </template>
<template #shortcutKey> <!-- <template #shortcutKey>
<Block :title="$t('preference.general')"> <Block :title="$t('preference.general')">
<General <General
v-model:locale="locale" v-model:locale="locale"
@ -268,10 +297,10 @@ function handleReset() {
<Animation <Animation
v-model:page-progress="pageProgress" v-model:page-progress="pageProgress"
v-model:page-transition="pageTransition" v-model:page-transition="pageTransition"
v-model:page-transition-enable="pageTransitionEnable" v-model:transition-enable="transitionEnable"
/> />
</Block> </Block>
</template> </template> -->
</VbenSegmented> </VbenSegmented>
</div> </div>

View File

@ -6,7 +6,8 @@ import {
MdiMoonAndStars, MdiMoonAndStars,
} from '@vben-core/iconify'; } from '@vben-core/iconify';
import { import {
flatPreferences, type ThemeModeType,
preferences,
updatePreferences, updatePreferences,
usePreferences, usePreferences,
} from '@vben-core/preferences'; } from '@vben-core/preferences';
@ -63,10 +64,14 @@ const PRESETS = [
/> />
</template> </template>
<ToggleGroup <ToggleGroup
v-model="flatPreferences.appThemeMode" :model-value="preferences.app.themeMode"
type="single" type="single"
variant="outline" variant="outline"
class="gap-2" class="gap-2"
@update:model-value="
(val) =>
updatePreferences({ app: { themeMode: val as ThemeModeType } })
"
> >
<ToggleGroupItem <ToggleGroupItem
v-for="item in PRESETS" v-for="item in PRESETS"

View File

@ -5,7 +5,6 @@ import { PreferencesWidget } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { VbenAdminLayout } from '@vben-core/layout-ui'; import { VbenAdminLayout } from '@vben-core/layout-ui';
import { import {
flatPreferences,
preferences, preferences,
updatePreferences, updatePreferences,
usePreferences, usePreferences,
@ -59,12 +58,13 @@ const logoCollapse = computed(() => {
return false; return false;
} }
const { appIsMobile, sidebarCollapse } = flatPreferences; const { isMobile } = preferences.app;
const { collapse } = preferences.sidebar;
if (!sidebarCollapse && appIsMobile) { if (!collapse && isMobile) {
return false; return false;
} }
return sidebarCollapse || isSideMixedNav.value; return collapse || isSideMixedNav.value;
}); });
const showHeaderNav = computed(() => { const showHeaderNav = computed(() => {
@ -103,9 +103,9 @@ function wrapperMenus(menus: MenuRecordRaw[]) {
<template> <template>
<VbenAdminLayout <VbenAdminLayout
v-model:side-extra-visible="extraVisible" v-model:side-extra-visible="extraVisible"
v-model:side-collapse="flatPreferences.sidebarCollapse" :side-extra-collapse="preferences.sidebar.extraCollapse"
v-model:side-expand-on-hover="flatPreferences.sidebarExpandOnHover" :side-expand-on-hover="preferences.sidebar.expandOnHover"
v-model:side-extra-collapse="flatPreferences.sidebarExtraCollapse" :side-collapse="preferences.sidebar.collapse"
:side-collapse-show-title="preferences.sidebar.collapseShowTitle" :side-collapse-show-title="preferences.sidebar.collapseShowTitle"
:content-compact="preferences.app.contentCompact" :content-compact="preferences.app.contentCompact"
:is-mobile="preferences.app.isMobile" :is-mobile="preferences.app.isMobile"
@ -121,6 +121,30 @@ function wrapperMenus(menus: MenuRecordRaw[]) {
:header-hidden="preferences.header.hidden" :header-hidden="preferences.header.hidden"
:side-width="preferences.sidebar.width" :side-width="preferences.sidebar.width"
:tabs-visible="preferences.tabbar.enable" :tabs-visible="preferences.tabbar.enable"
@update:side-extra-collapse="
(value: boolean) =>
updatePreferences({
sidebar: {
extraCollapse: value,
},
})
"
@update:side-expand-on-hover="
(value: boolean) =>
updatePreferences({
sidebar: {
expandOnHover: value,
},
})
"
@update:side-collapse="
(value: boolean) =>
updatePreferences({
sidebar: {
collapse: value,
},
})
"
@side-mouse-leave="handleSideMouseLeave" @side-mouse-leave="handleSideMouseLeave"
@update:side-visible=" @update:side-visible="
(value: boolean) => (value: boolean) =>