feat: supports specifying the position of the preference button (#4154)
parent
9c6e059aac
commit
30223f18db
|
@ -189,6 +189,7 @@ const defaultPreferences: Preferences = {
|
|||
locale: 'zh-CN',
|
||||
loginExpiredMode: 'modal',
|
||||
name: 'Vben Admin',
|
||||
preferencesButtonPosition: 'fixed',
|
||||
watermark: false,
|
||||
},
|
||||
breadcrumb: {
|
||||
|
@ -319,6 +320,8 @@ interface AppPreferences {
|
|||
loginExpiredMode: LoginExpiredModeType;
|
||||
/** 应用名 */
|
||||
name: string;
|
||||
/** 偏好设置按钮位置 */
|
||||
preferencesButtonPosition: PreferencesButtonPositionType;
|
||||
/**
|
||||
* @zh_CN 是否开启水印
|
||||
*/
|
||||
|
|
|
@ -38,7 +38,7 @@ export {
|
|||
RotateCw,
|
||||
Search,
|
||||
SearchX,
|
||||
Settings2,
|
||||
Settings,
|
||||
Sun,
|
||||
SunMoon,
|
||||
SwatchBook,
|
||||
|
|
|
@ -7,6 +7,13 @@ type LayoutType =
|
|||
|
||||
type ThemeModeType = 'auto' | 'dark' | 'light';
|
||||
|
||||
/**
|
||||
* 偏好设置按钮位置
|
||||
* fixed 固定在右侧
|
||||
* header 顶栏
|
||||
*/
|
||||
type PreferencesButtonPositionType = 'fixed' | 'header';
|
||||
|
||||
type BuiltinThemeType =
|
||||
| 'custom'
|
||||
| 'deep-blue'
|
||||
|
@ -92,6 +99,7 @@ export type {
|
|||
LoginExpiredModeType,
|
||||
NavigationStyleType,
|
||||
PageTransitionType,
|
||||
PreferencesButtonPositionType,
|
||||
TabsStyleType,
|
||||
ThemeModeType,
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ const defaultPreferences: Preferences = {
|
|||
locale: 'zh-CN',
|
||||
loginExpiredMode: 'modal',
|
||||
name: 'Vben Admin',
|
||||
preferencesButtonPosition: 'fixed',
|
||||
watermark: false,
|
||||
},
|
||||
breadcrumb: {
|
||||
|
|
|
@ -10,6 +10,7 @@ import type {
|
|||
LoginExpiredModeType,
|
||||
NavigationStyleType,
|
||||
PageTransitionType,
|
||||
PreferencesButtonPositionType,
|
||||
TabsStyleType,
|
||||
ThemeModeType,
|
||||
} from '@vben-core/typings';
|
||||
|
@ -49,6 +50,8 @@ interface AppPreferences {
|
|||
loginExpiredMode: LoginExpiredModeType;
|
||||
/** 应用名 */
|
||||
name: string;
|
||||
/** 偏好设置按钮位置 */
|
||||
preferencesButtonPosition: PreferencesButtonPositionType;
|
||||
/**
|
||||
* @zh_CN 是否开启水印
|
||||
*/
|
||||
|
|
|
@ -5,7 +5,12 @@ import { preferences, usePreferences } from '@vben/preferences';
|
|||
import { useAccessStore } from '@vben/stores';
|
||||
import { VbenFullScreen } from '@vben-core/shadcn-ui';
|
||||
|
||||
import { GlobalSearch, LanguageToggle, ThemeToggle } from '../../widgets';
|
||||
import {
|
||||
GlobalSearch,
|
||||
LanguageToggle,
|
||||
PreferencesButton,
|
||||
ThemeToggle,
|
||||
} from '../../widgets';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
|
@ -26,34 +31,44 @@ const accessStore = useAccessStore();
|
|||
const { globalSearchShortcutKey } = usePreferences();
|
||||
const slots = useSlots();
|
||||
const rightSlots = computed(() => {
|
||||
const list = [{ index: 30, name: 'user-dropdown' }];
|
||||
const list = [{ index: 100, name: 'user-dropdown' }];
|
||||
if (preferences.widget.globalSearch) {
|
||||
list.push({
|
||||
index: 5,
|
||||
name: 'global-search',
|
||||
});
|
||||
}
|
||||
if (preferences.widget.themeToggle) {
|
||||
|
||||
if (
|
||||
preferences.app.enablePreferences &&
|
||||
preferences.app.preferencesButtonPosition === 'header'
|
||||
) {
|
||||
list.push({
|
||||
index: 10,
|
||||
name: 'preferences',
|
||||
});
|
||||
}
|
||||
if (preferences.widget.themeToggle) {
|
||||
list.push({
|
||||
index: 15,
|
||||
name: 'theme-toggle',
|
||||
});
|
||||
}
|
||||
if (preferences.widget.languageToggle) {
|
||||
list.push({
|
||||
index: 15,
|
||||
index: 20,
|
||||
name: 'language-toggle',
|
||||
});
|
||||
}
|
||||
if (preferences.widget.fullscreen) {
|
||||
list.push({
|
||||
index: 20,
|
||||
index: 25,
|
||||
name: 'fullscreen',
|
||||
});
|
||||
}
|
||||
if (preferences.widget.notification) {
|
||||
list.push({
|
||||
index: 25,
|
||||
index: 30,
|
||||
name: 'notification',
|
||||
});
|
||||
}
|
||||
|
@ -66,6 +81,7 @@ const rightSlots = computed(() => {
|
|||
});
|
||||
return list.sort((a, b) => a.index - b.index);
|
||||
});
|
||||
|
||||
const leftSlots = computed(() => {
|
||||
const list: any[] = [];
|
||||
|
||||
|
@ -108,8 +124,12 @@ const leftSlots = computed(() => {
|
|||
class="mr-4"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else-if="slot.name === 'preferences'">
|
||||
<PreferencesButton class="mr-2" />
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'theme-toggle'">
|
||||
<ThemeToggle class="mr-2" />
|
||||
<ThemeToggle class="mr-2 mt-[2px]" />
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'language-toggle'">
|
||||
<LanguageToggle class="mr-2" />
|
||||
|
|
|
@ -320,8 +320,14 @@ const headerSlots = computed(() => {
|
|||
<slot v-if="lockStore.isLockScreen" name="lock-screen"></slot>
|
||||
</Transition>
|
||||
|
||||
<template v-if="preferences.app.enablePreferences">
|
||||
<template
|
||||
v-if="
|
||||
preferences.app.enablePreferences &&
|
||||
preferences.app.preferencesButtonPosition === 'fixed'
|
||||
"
|
||||
>
|
||||
<Preferences
|
||||
class="z-100 fixed bottom-20 right-0"
|
||||
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -54,9 +54,6 @@ const styleItems = computed((): SelectOption[] => [
|
|||
<SwitchItem v-model="tabbarDragable" :disabled="!tabbarEnable">
|
||||
{{ $t('preferences.tabbar.dragable') }}
|
||||
</SwitchItem>
|
||||
<SelectItem v-model="tabbarStyleType" :items="styleItems">
|
||||
{{ $t('preferences.tabbar.styleType.title') }}
|
||||
</SelectItem>
|
||||
<SwitchItem v-model="tabbarShowIcon" :disabled="!tabbarEnable">
|
||||
{{ $t('preferences.tabbar.icon') }}
|
||||
</SwitchItem>
|
||||
|
@ -69,4 +66,7 @@ const styleItems = computed((): SelectOption[] => [
|
|||
<SwitchItem v-model="tabbarShowMaximize" :disabled="!tabbarEnable">
|
||||
{{ $t('preferences.tabbar.showMaximize') }}
|
||||
</SwitchItem>
|
||||
<SelectItem v-model="tabbarStyleType" :items="styleItems">
|
||||
{{ $t('preferences.tabbar.styleType.title') }}
|
||||
</SelectItem>
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import type { SelectOption } from '@vben/types';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SelectItem from '../select-item.vue';
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
|
@ -14,6 +19,20 @@ const widgetNotification = defineModel<boolean>('widgetNotification');
|
|||
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
|
||||
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
||||
const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
|
||||
const appPreferencesButtonPosition = defineModel<string>(
|
||||
'appPreferencesButtonPosition',
|
||||
);
|
||||
|
||||
const positionItems = computed((): SelectOption[] => [
|
||||
{
|
||||
label: $t('preferences.position.header'),
|
||||
value: 'header',
|
||||
},
|
||||
{
|
||||
label: $t('preferences.position.fixed'),
|
||||
value: 'fixed',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -38,4 +57,7 @@ const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
|
|||
<SwitchItem v-model="widgetSidebarToggle">
|
||||
{{ $t('preferences.widget.sidebarToggle') }}
|
||||
</SwitchItem>
|
||||
<SelectItem v-model="appPreferencesButtonPosition" :items="positionItems">
|
||||
{{ $t('preferences.position.title') }}
|
||||
</SelectItem>
|
||||
</template>
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export { default as Preferences } from './preferences.vue';
|
||||
export { default as PreferencesButton } from './preferences-button.vue';
|
||||
export * from './use-open-preferences';
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<script lang="ts" setup>
|
||||
import { Settings } from '@vben/icons';
|
||||
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||
|
||||
import Preferences from './preferences.vue';
|
||||
</script>
|
||||
<template>
|
||||
<Preferences>
|
||||
<VbenIconButton>
|
||||
<Settings class="size-4" />
|
||||
</VbenIconButton>
|
||||
</Preferences>
|
||||
</template>
|
|
@ -7,13 +7,14 @@ import type {
|
|||
LayoutHeaderModeType,
|
||||
LayoutType,
|
||||
NavigationStyleType,
|
||||
PreferencesButtonPositionType,
|
||||
ThemeModeType,
|
||||
} from '@vben/types';
|
||||
import type { SegmentedItem } from '@vben-core/shadcn-ui';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { Copy, RotateCw, Settings2 } from '@vben/icons';
|
||||
import { Copy, RotateCw, Settings } from '@vben/icons';
|
||||
import { $t, loadLocaleMessages } from '@vben/locales';
|
||||
import {
|
||||
clearPreferencesCache,
|
||||
|
@ -63,6 +64,9 @@ const appColorWeakMode = defineModel<boolean>('appColorWeakMode');
|
|||
const appContentCompact = defineModel<ContentCompactType>('appContentCompact');
|
||||
const appWatermark = defineModel<boolean>('appWatermark');
|
||||
const appEnableCheckUpdates = defineModel<boolean>('appEnableCheckUpdates');
|
||||
const appPreferencesButtonPosition = defineModel<PreferencesButtonPositionType>(
|
||||
'appPreferencesButtonPosition',
|
||||
);
|
||||
|
||||
const transitionProgress = defineModel<boolean>('transitionProgress');
|
||||
const transitionName = defineModel<string>('transitionName');
|
||||
|
@ -220,19 +224,21 @@ async function handleReset() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="z-100 fixed right-0 top-1/2">
|
||||
<div>
|
||||
<VbenSheet
|
||||
v-model:open="openPreferences"
|
||||
:description="$t('preferences.subtitle')"
|
||||
:title="$t('preferences.title')"
|
||||
>
|
||||
<template #trigger>
|
||||
<VbenButton
|
||||
:title="$t('preferences.title')"
|
||||
class="bg-primary flex-col-center h-10 w-10 cursor-pointer rounded-l-lg rounded-r-none border-none"
|
||||
>
|
||||
<Settings2 class="size-5" />
|
||||
</VbenButton>
|
||||
<slot name="trigger">
|
||||
<VbenButton
|
||||
:title="$t('preferences.title')"
|
||||
class="bg-primary flex-col-center size-10 cursor-pointer rounded-l-lg rounded-r-none border-none"
|
||||
>
|
||||
<Settings class="size-5" />
|
||||
</VbenButton>
|
||||
</slot>
|
||||
</template>
|
||||
<template #extra>
|
||||
<div class="flex items-center">
|
||||
|
@ -358,6 +364,9 @@ async function handleReset() {
|
|||
</Block>
|
||||
<Block :title="$t('preferences.widget.title')">
|
||||
<Widget
|
||||
v-model:app-preferences-button-position="
|
||||
appPreferencesButtonPosition
|
||||
"
|
||||
v-model:widget-fullscreen="widgetFullscreen"
|
||||
v-model:widget-global-search="widgetGlobalSearch"
|
||||
v-model:widget-language-toggle="widgetLanguageToggle"
|
||||
|
|
|
@ -47,5 +47,9 @@ const listen = computed(() => {
|
|||
});
|
||||
</script>
|
||||
<template>
|
||||
<PreferencesSheet v-bind="attrs" v-on="listen" />
|
||||
<PreferencesSheet v-bind="attrs" v-on="listen">
|
||||
<template #trigger>
|
||||
<slot></slot>
|
||||
</template>
|
||||
</PreferencesSheet>
|
||||
</template>
|
||||
|
|
|
@ -172,6 +172,11 @@
|
|||
"dynamicTitle": "Dynamic Title",
|
||||
"watermark": "Watermark",
|
||||
"checkUpdates": "Periodic update check",
|
||||
"position": {
|
||||
"title": "Preferences Postion",
|
||||
"header": "Header",
|
||||
"fixed": "Fixed"
|
||||
},
|
||||
"sidebar": {
|
||||
"title": "Sidebar",
|
||||
"width": "Width",
|
||||
|
|
|
@ -172,6 +172,11 @@
|
|||
"dynamicTitle": "动态标题",
|
||||
"watermark": "水印",
|
||||
"checkUpdates": "定时检查更新",
|
||||
"position": {
|
||||
"title": "偏好设置位置",
|
||||
"header": "顶栏",
|
||||
"fixed": "固定"
|
||||
},
|
||||
"sidebar": {
|
||||
"title": "侧边栏",
|
||||
"width": "宽度",
|
||||
|
|
Loading…
Reference in New Issue