chore: remove flatPreferences
parent
a344d32169
commit
68229a4d2f
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 是否全屏显示content,不需要侧边、底部、顶部、tab区域
|
* @zh_CN 是否全屏显示content,不需要侧边、底部、顶部、tab区域
|
||||||
*/
|
*/
|
||||||
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() {
|
||||||
* 在tabs可见以及开启keep-alive的情况下才开启
|
* 在tabs可见以及开启keep-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 {
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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)"
|
||||||
>
|
>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
>
|
>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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">
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
>
|
>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
Loading…
Reference in New Issue