feat: supports specifying the position of the preference button (#4154)

This commit is contained in:
Vben 2024-08-14 23:02:39 +08:00 committed by GitHub
parent 9c6e059aac
commit 30223f18db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 121 additions and 21 deletions

View File

@ -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 是否开启水印
*/

View File

@ -38,7 +38,7 @@ export {
RotateCw,
Search,
SearchX,
Settings2,
Settings,
Sun,
SunMoon,
SwatchBook,

View File

@ -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,
};

View File

@ -19,6 +19,7 @@ const defaultPreferences: Preferences = {
locale: 'zh-CN',
loginExpiredMode: 'modal',
name: 'Vben Admin',
preferencesButtonPosition: 'fixed',
watermark: false,
},
breadcrumb: {

View File

@ -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
*/

View File

@ -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" />

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,2 +1,3 @@
export { default as Preferences } from './preferences.vue';
export { default as PreferencesButton } from './preferences-button.vue';
export * from './use-open-preferences';

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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",

View File

@ -172,6 +172,11 @@
"dynamicTitle": "动态标题",
"watermark": "水印",
"checkUpdates": "定时检查更新",
"position": {
"title": "偏好设置位置",
"header": "顶栏",
"fixed": "固定"
},
"sidebar": {
"title": "侧边栏",
"width": "宽度",