chore: remove flatPreferences

This commit is contained in:
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 { preferencesManager } from './preferences';
@ -8,8 +6,8 @@ import { preferencesManager } from './preferences';
const preferences: Preferences = preferencesManager.getPreferences();
// 扁平化后的偏好设置
const flatPreferences: Flatten<Preferences> =
preferencesManager.getFlatPreferences();
// const flatPreferences: Flatten<Preferences> =
// preferencesManager.getFlatPreferences();
// 更新偏好设置
const updatePreferences =
@ -20,7 +18,7 @@ const resetPreferences =
preferencesManager.resetPreferences.bind(preferencesManager);
export {
flatPreferences,
// flatPreferences,
preferences,
preferencesManager,
resetPreferences,

View File

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

View File

@ -10,7 +10,7 @@ type BreadcrumbStyleType = 'background' | 'normal';
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';
@ -132,7 +132,7 @@ interface TransitionPreferences {
/** 页面切换动画是否启用 */
enable: boolean;
/** 页面切换动画 */
name: PageTransitionType;
name: PageTransitionType | string;
/** 是否开启页面加载进度动画 */
progress: boolean;
}
@ -176,6 +176,7 @@ export type {
LayoutType,
LogoPreferences,
NavigationPreferences,
NavigationStyleType,
PageTransitionType,
Preferences,
PreferencesKeys,

View File

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

View File

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

View File

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

View File

@ -11,9 +11,9 @@ defineOptions({
name: 'PreferenceGeneralConfig',
});
const locale = defineModel<string>('locale');
const dynamicTitle = defineModel<boolean>('dynamicTitle');
const shortcutKeys = defineModel<boolean>('shortcutKeys');
const appLocale = defineModel<string>('appLocale');
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
const shortcutKeysEnable = defineModel<boolean>('shortcutKeysEnable');
const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
label: item.text,
@ -22,13 +22,13 @@ const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
</script>
<template>
<SelectItem v-model="locale" :items="localeItems">
<SelectItem v-model="appLocale" :items="localeItems">
{{ $t('preference.language') }}
</SelectItem>
<SwitchItem v-model="dynamicTitle">
<SwitchItem v-model="appDynamicTitle">
{{ $t('preference.dynamic-title') }}
</SwitchItem>
<SwitchItem v-model="shortcutKeys">
<SwitchItem v-model="shortcutKeysEnable">
{{ $t('preference.shortcut-key') }}
</SwitchItem>
</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 Navigation } from './layout/navigation.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 ThemeColor } from './theme/color.vue';
export { default as ColorMode } from './theme/color-mode.vue';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,57 +1,227 @@
<script lang="ts" setup>
import type { SupportedLanguagesType } from '@vben/types';
import { loadLocaleMessages } from '@vben/locales';
import {
COLOR_PRIMARY_RESETS,
flatPreferences,
preferences,
updatePreferences,
} from '@vben-core/preferences';
import Preferences from './preferences.vue';
function updateLocale(value: string) {
const locale = value as SupportedLanguagesType;
updatePreferences({
app: { locale },
});
//
loadLocaleMessages(locale);
}
</script>
<template>
<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"
:locale="flatPreferences.appLocale"
@update:locale="updateLocale"
:app-locale="preferences.app.locale"
: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>

View File

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

View File

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

View File

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