feat: 顶栏按钮支持位置选择(header/user-dropdown/none)

为顶栏所有部件按钮添加独立的位置配置,支持在顶栏、用户下拉菜单、不显示
三种选项间切换,每个按钮可独立设置。

- 新增 7 个位置偏好字段:globalSearchButtonPosition、
  themeToggleButtonPosition、languageToggleButtonPosition、
  fullscreenButtonPosition、notificationButtonPosition、
  refreshButtonPosition、timezoneButtonPosition
- 合并 lockScreenButtonPosition、logoutButtonPosition
- header.vue 按位置条件过滤按钮显示
- user-dropdown.vue 新增对应菜单项,支持在用户下拉菜单中显示
- 偏好设置界面仅保留位置选择器(不再需要独立的开关)
- 更新中英文国际化文案

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
This commit is contained in:
金毛88
2026-07-18 22:46:31 +08:00
parent b71ebbd673
commit 0cd87c170f
15 changed files with 518 additions and 79 deletions

View File

@@ -231,6 +231,7 @@ watch(
description="ann.vben@gmail.com" description="ann.vben@gmail.com"
tag-text="Pro" tag-text="Pro"
@clear-preferences-and-logout="handleLogout" @clear-preferences-and-logout="handleLogout"
@logout="handleLogout"
/> />
</template> </template>
<template #notification> <template #notification>

View File

@@ -231,6 +231,7 @@ watch(
description="ann.vben@gmail.com" description="ann.vben@gmail.com"
tag-text="Pro" tag-text="Pro"
@clear-preferences-and-logout="handleLogout" @clear-preferences-and-logout="handleLogout"
@logout="handleLogout"
/> />
</template> </template>
<template #notification> <template #notification>

View File

@@ -231,6 +231,7 @@ watch(
description="ann.vben@gmail.com" description="ann.vben@gmail.com"
tag-text="Pro" tag-text="Pro"
@clear-preferences-and-logout="handleLogout" @clear-preferences-and-logout="handleLogout"
@logout="handleLogout"
/> />
</template> </template>
<template #notification> <template #notification>

View File

@@ -231,6 +231,7 @@ watch(
description="ann.vben@gmail.com" description="ann.vben@gmail.com"
tag-text="Pro" tag-text="Pro"
@clear-preferences-and-logout="handleLogout" @clear-preferences-and-logout="handleLogout"
@logout="handleLogout"
/> />
</template> </template>
<template #notification> <template #notification>

View File

@@ -231,6 +231,7 @@ watch(
description="ann.vben@gmail.com" description="ann.vben@gmail.com"
tag-text="Pro" tag-text="Pro"
@clear-preferences-and-logout="handleLogout" @clear-preferences-and-logout="handleLogout"
@logout="handleLogout"
/> />
</template> </template>
<template #notification> <template #notification>

View File

@@ -137,14 +137,23 @@ const defaultPreferences: Preferences = {
}, },
widget: { widget: {
fullscreen: true, fullscreen: true,
fullscreenButtonPosition: 'header',
globalSearch: true, globalSearch: true,
globalSearchButtonPosition: 'header',
languageToggle: true, languageToggle: true,
languageToggleButtonPosition: 'header',
lockScreen: true, lockScreen: true,
lockScreenButtonPosition: 'header',
logoutButtonPosition: 'header',
notification: true, notification: true,
notificationButtonPosition: 'header',
refresh: true, refresh: true,
refreshButtonPosition: 'header',
sidebarToggle: true, sidebarToggle: true,
themeToggle: true, themeToggle: true,
themeToggleButtonPosition: 'header',
timezone: true, timezone: true,
timezoneButtonPosition: 'header',
}, },
}; };

View File

@@ -365,22 +365,40 @@ interface TransitionPreferences {
interface WidgetPreferences { interface WidgetPreferences {
/** 是否启用全屏部件 */ /** 是否启用全屏部件 */
fullscreen: boolean; fullscreen: boolean;
/** 全屏按钮位置 */
fullscreenButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 是否启用全局搜索部件 */ /** 是否启用全局搜索部件 */
globalSearch: boolean; globalSearch: boolean;
/** 全局搜索按钮位置 */
globalSearchButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 是否启用语言切换部件 */ /** 是否启用语言切换部件 */
languageToggle: boolean; languageToggle: boolean;
/** 语言切换按钮位置 */
languageToggleButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 是否开启锁屏功能 */ /** 是否开启锁屏功能 */
lockScreen: boolean; lockScreen: boolean;
/** 锁屏按钮位置 */
lockScreenButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 退出登录按钮位置 */
logoutButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 是否显示通知部件 */ /** 是否显示通知部件 */
notification: boolean; notification: boolean;
/** 通知按钮位置 */
notificationButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 显示刷新按钮 */ /** 显示刷新按钮 */
refresh: boolean; refresh: boolean;
/** 刷新按钮位置 */
refreshButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 是否显示侧边栏显示/隐藏部件 */ /** 是否显示侧边栏显示/隐藏部件 */
sidebarToggle: boolean; sidebarToggle: boolean;
/** 是否显示主题切换部件 */ /** 是否显示主题切换部件 */
themeToggle: boolean; themeToggle: boolean;
/** 主题切换按钮位置 */
themeToggleButtonPosition: 'header' | 'none' | 'user-dropdown';
/** 是否显示时区部件 */ /** 是否显示时区部件 */
timezone: boolean; timezone: boolean;
/** 时区按钮位置 */
timezoneButtonPosition: 'header' | 'none' | 'user-dropdown';
} }
interface Preferences { interface Preferences {

View File

@@ -122,7 +122,10 @@ type SlotItem = { index: number; name: string };
const rightSlots = computed(() => { const rightSlots = computed(() => {
const list: Array<SlotItem> = []; const list: Array<SlotItem> = [];
// 全局搜索 // 全局搜索
if (preferences.widget.globalSearch) { if (
preferences.widget.globalSearch &&
preferences.widget.globalSearchButtonPosition === 'header'
) {
list.push({ list.push({
index: REFERENCE_VALUE, index: REFERENCE_VALUE,
name: 'global-search', name: 'global-search',
@@ -137,50 +140,70 @@ const rightSlots = computed(() => {
} }
// 主题、语言、时区等子功能由各自的 widget 开关独立控制, // 主题、语言、时区等子功能由各自的 widget 开关独立控制,
// 不应跟随偏好设置按钮的显示与否(如 enablePreferences 为 false 或按钮位于其他位置时) // 不应跟随偏好设置按钮的显示与否(如 enablePreferences 为 false 或按钮位于其他位置时)
if (preferences.widget.themeToggle) { if (
preferences.widget.themeToggle &&
preferences.widget.themeToggleButtonPosition === 'header'
) {
list.push({ list.push({
index: REFERENCE_VALUE + 20, index: REFERENCE_VALUE + 20,
name: 'theme-toggle', name: 'theme-toggle',
}); });
} }
if (preferences.widget.languageToggle) { if (
preferences.widget.languageToggle &&
preferences.widget.languageToggleButtonPosition === 'header'
) {
list.push({ list.push({
index: REFERENCE_VALUE + 30, index: REFERENCE_VALUE + 30,
name: 'language-toggle', name: 'language-toggle',
}); });
} }
if (preferences.widget.timezone) { if (
preferences.widget.timezone &&
preferences.widget.timezoneButtonPosition === 'header'
) {
list.push({ list.push({
index: REFERENCE_VALUE + 40, index: REFERENCE_VALUE + 40,
name: 'timezone', name: 'timezone',
}); });
} }
// 全屏 // 全屏
if (preferences.widget.fullscreen) { if (
preferences.widget.fullscreen &&
preferences.widget.fullscreenButtonPosition === 'header'
) {
list.push({ list.push({
index: REFERENCE_VALUE + 50, index: REFERENCE_VALUE + 50,
name: 'fullscreen', name: 'fullscreen',
}); });
} }
// 消息通知 // 消息通知
if (preferences.widget.notification) { if (
preferences.widget.notification &&
preferences.widget.notificationButtonPosition === 'header'
) {
list.push({ list.push({
index: REFERENCE_VALUE + 60, index: REFERENCE_VALUE + 60,
name: 'notification', name: 'notification',
}); });
} }
// 锁定屏幕 // 锁定屏幕(仅在 header 位置显示)
if (preferences.widget.lockScreen) { if (
preferences.widget.lockScreen &&
preferences.widget.lockScreenButtonPosition === 'header'
) {
list.push({ list.push({
index: REFERENCE_VALUE + 70, index: REFERENCE_VALUE + 70,
name: 'lock-screen-btn', name: 'lock-screen-btn',
}); });
} }
// 退出登录 // 退出登录(仅在 header 位置显示)
list.push({ if (preferences.widget.logoutButtonPosition === 'header') {
index: REFERENCE_VALUE + 80, list.push({
name: 'logout-btn', index: REFERENCE_VALUE + 80,
}); name: 'logout-btn',
});
}
Object.keys(slots).forEach((key) => { Object.keys(slots).forEach((key) => {
// 适配插槽名称,例如第一个插槽名:header-right-1 // 适配插槽名称,例如第一个插槽名:header-right-1
@@ -201,7 +224,10 @@ const rightSlots = computed(() => {
const leftSlots = computed(() => { const leftSlots = computed(() => {
const list: Array<SlotItem> = []; const list: Array<SlotItem> = [];
// 刷新 // 刷新
if (preferences.widget.refresh) { if (
preferences.widget.refresh &&
preferences.widget.refreshButtonPosition === 'header'
) {
list.push({ list.push({
index: 0, index: 0,
name: 'refresh', name: 'refresh',

View File

@@ -6,24 +6,41 @@ import { computed } from 'vue';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import SelectItem from '../select-item.vue'; import SelectItem from '../select-item.vue';
import SwitchItem from '../switch-item.vue';
defineOptions({ defineOptions({
name: 'PreferenceInterfaceControl', name: 'PreferenceInterfaceControl',
}); });
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch'); const widgetGlobalSearchButtonPosition = defineModel<string>(
const widgetFullscreen = defineModel<boolean>('widgetFullscreen'); 'widgetGlobalSearchButtonPosition',
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle'); );
const widgetNotification = defineModel<boolean>('widgetNotification'); const widgetFullscreenButtonPosition = defineModel<string>(
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle'); 'widgetFullscreenButtonPosition',
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle'); );
const widgetLockScreen = defineModel<boolean>('widgetLockScreen'); const widgetLanguageToggleButtonPosition = defineModel<string>(
'widgetLanguageToggleButtonPosition',
);
const widgetNotificationButtonPosition = defineModel<string>(
'widgetNotificationButtonPosition',
);
const widgetThemeToggleButtonPosition = defineModel<string>(
'widgetThemeToggleButtonPosition',
);
const widgetLockScreenButtonPosition = defineModel<string>(
'widgetLockScreenButtonPosition',
);
const widgetLogoutButtonPosition = defineModel<string>(
'widgetLogoutButtonPosition',
);
const appPreferencesButtonPosition = defineModel<string>( const appPreferencesButtonPosition = defineModel<string>(
'appPreferencesButtonPosition', 'appPreferencesButtonPosition',
); );
const widgetRefresh = defineModel<boolean>('widgetRefresh'); const widgetRefreshButtonPosition = defineModel<string>(
const widgetTimezone = defineModel<boolean>('widgetTimezone'); 'widgetRefreshButtonPosition',
);
const widgetTimezoneButtonPosition = defineModel<string>(
'widgetTimezoneButtonPosition',
);
const positionItems = computed((): SelectOption[] => [ const positionItems = computed((): SelectOption[] => [
{ {
@@ -43,36 +60,75 @@ const positionItems = computed((): SelectOption[] => [
value: 'user-dropdown', value: 'user-dropdown',
}, },
]); ]);
const buttonPositionItems = computed((): SelectOption[] => [
{
label: $t('preferences.widget.header'),
value: 'header',
},
{
label: $t('preferences.widget.userDropdown'),
value: 'user-dropdown',
},
{
label: $t('common.notShow'),
value: 'none',
},
]);
</script> </script>
<template> <template>
<SwitchItem v-model="widgetGlobalSearch"> <SelectItem
{{ $t('preferences.widget.globalSearch') }} v-model="widgetGlobalSearchButtonPosition"
</SwitchItem> :items="buttonPositionItems"
<SwitchItem v-model="widgetThemeToggle"> >
{{ $t('preferences.widget.themeToggle') }} {{ $t('preferences.widget.globalSearchPosition') }}
</SwitchItem> </SelectItem>
<SwitchItem v-model="widgetLanguageToggle"> <SelectItem
{{ $t('preferences.widget.languageToggle') }} v-model="widgetThemeToggleButtonPosition"
</SwitchItem> :items="buttonPositionItems"
<SwitchItem v-model="widgetFullscreen"> >
{{ $t('preferences.widget.fullscreen') }} {{ $t('preferences.widget.themeTogglePosition') }}
</SwitchItem> </SelectItem>
<SwitchItem v-model="widgetNotification"> <SelectItem
{{ $t('preferences.widget.notification') }} v-model="widgetLanguageToggleButtonPosition"
</SwitchItem> :items="buttonPositionItems"
<SwitchItem v-model="widgetLockScreen"> >
{{ $t('preferences.widget.lockScreen') }} {{ $t('preferences.widget.languageTogglePosition') }}
</SwitchItem> </SelectItem>
<SwitchItem v-model="widgetSidebarToggle"> <SelectItem
{{ $t('preferences.widget.sidebarToggle') }} v-model="widgetFullscreenButtonPosition"
</SwitchItem> :items="buttonPositionItems"
<SwitchItem v-model="widgetRefresh"> >
{{ $t('preferences.widget.refresh') }} {{ $t('preferences.widget.fullscreenPosition') }}
</SwitchItem> </SelectItem>
<SwitchItem v-model="widgetTimezone"> <SelectItem
{{ $t('preferences.widget.timezone') }} v-model="widgetNotificationButtonPosition"
</SwitchItem> :items="buttonPositionItems"
>
{{ $t('preferences.widget.notificationPosition') }}
</SelectItem>
<SelectItem
v-model="widgetLockScreenButtonPosition"
:items="buttonPositionItems"
>
{{ $t('preferences.widget.lockScreenPosition') }}
</SelectItem>
<SelectItem v-model="widgetLogoutButtonPosition" :items="buttonPositionItems">
{{ $t('preferences.widget.logoutButtonPosition') }}
</SelectItem>
<SelectItem
v-model="widgetRefreshButtonPosition"
:items="buttonPositionItems"
>
{{ $t('preferences.widget.refreshPosition') }}
</SelectItem>
<SelectItem
v-model="widgetTimezoneButtonPosition"
:items="buttonPositionItems"
>
{{ $t('preferences.widget.timezonePosition') }}
</SelectItem>
<SelectItem v-model="appPreferencesButtonPosition" :items="positionItems"> <SelectItem v-model="appPreferencesButtonPosition" :items="positionItems">
{{ $t('preferences.position.title') }} {{ $t('preferences.position.title') }}
</SelectItem> </SelectItem>

View File

@@ -174,15 +174,33 @@ const shortcutKeysGlobalLockScreen = defineModel<boolean>(
'shortcutKeysGlobalLockScreen', 'shortcutKeysGlobalLockScreen',
); );
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch'); const widgetGlobalSearchButtonPosition = defineModel<string>(
const widgetFullscreen = defineModel<boolean>('widgetFullscreen'); 'widgetGlobalSearchButtonPosition',
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle'); );
const widgetNotification = defineModel<boolean>('widgetNotification'); const widgetFullscreenButtonPosition = defineModel<string>(
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle'); 'widgetFullscreenButtonPosition',
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle'); );
const widgetLockScreen = defineModel<boolean>('widgetLockScreen'); const widgetLanguageToggleButtonPosition = defineModel<string>(
const widgetRefresh = defineModel<boolean>('widgetRefresh'); 'widgetLanguageToggleButtonPosition',
const widgetTimezone = defineModel<boolean>('widgetTimezone'); );
const widgetNotificationButtonPosition = defineModel<string>(
'widgetNotificationButtonPosition',
);
const widgetThemeToggleButtonPosition = defineModel<string>(
'widgetThemeToggleButtonPosition',
);
const widgetLockScreenButtonPosition = defineModel<string>(
'widgetLockScreenButtonPosition',
);
const widgetLogoutButtonPosition = defineModel<string>(
'widgetLogoutButtonPosition',
);
const widgetRefreshButtonPosition = defineModel<string>(
'widgetRefreshButtonPosition',
);
const widgetTimezoneButtonPosition = defineModel<string>(
'widgetTimezoneButtonPosition',
);
const { const {
customPreferences, customPreferences,
@@ -483,15 +501,33 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) {
v-model:app-preferences-button-position=" v-model:app-preferences-button-position="
appPreferencesButtonPosition appPreferencesButtonPosition
" "
v-model:widget-fullscreen="widgetFullscreen" v-model:widget-fullscreen-button-position="
v-model:widget-global-search="widgetGlobalSearch" widgetFullscreenButtonPosition
v-model:widget-language-toggle="widgetLanguageToggle" "
v-model:widget-lock-screen="widgetLockScreen" v-model:widget-global-search-button-position="
v-model:widget-notification="widgetNotification" widgetGlobalSearchButtonPosition
v-model:widget-refresh="widgetRefresh" "
v-model:widget-sidebar-toggle="widgetSidebarToggle" v-model:widget-language-toggle-button-position="
v-model:widget-theme-toggle="widgetThemeToggle" widgetLanguageToggleButtonPosition
v-model:widget-timezone="widgetTimezone" "
v-model:widget-lock-screen-button-position="
widgetLockScreenButtonPosition
"
v-model:widget-logout-button-position="
widgetLogoutButtonPosition
"
v-model:widget-notification-button-position="
widgetNotificationButtonPosition
"
v-model:widget-refresh-button-position="
widgetRefreshButtonPosition
"
v-model:widget-theme-toggle-button-position="
widgetThemeToggleButtonPosition
"
v-model:widget-timezone-button-position="
widgetTimezoneButtonPosition
"
/> />
</Block> </Block>
<Block :title="$t('preferences.footer.title')"> <Block :title="$t('preferences.footer.title')">

View File

@@ -3,13 +3,27 @@ import type { Component } from 'vue';
import type { AnyFunction } from '@vben/types'; import type { AnyFunction } from '@vben/types';
import { useTemplateRef, watch } from 'vue'; import { computed, useTemplateRef, watch } from 'vue';
import { useHoverToggle } from '@vben/hooks'; import { useHoverToggle } from '@vben/hooks';
import { Settings } from '@vben/icons'; import {
Bell,
Fullscreen,
Languages,
LockKeyhole,
LogOut,
RotateCw,
Search,
Settings,
Sun,
SunMoon,
} from '@vben/icons';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { usePreferences } from '@vben/preferences'; import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { isWindowsOs } from '@vben/utils';
import { useVbenModal } from '@vben-core/popup-ui';
import { import {
Badge, Badge,
DropdownMenu, DropdownMenu,
@@ -17,11 +31,15 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuLabel, DropdownMenuLabel,
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger, DropdownMenuTrigger,
VbenAvatar, VbenAvatar,
VbenIcon, VbenIcon,
} from '@vben-core/shadcn-ui'; } from '@vben-core/shadcn-ui';
import { useMagicKeys, whenever } from '@vueuse/core';
import { LockScreenModal } from '../lock-screen';
import { Preferences } from '../preferences'; import { Preferences } from '../preferences';
interface Props { interface Props {
@@ -70,9 +88,22 @@ const props = withDefaults(defineProps<Props>(), {
hoverDelay: 500, hoverDelay: 500,
}); });
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>(); const emit = defineEmits<{ clearPreferencesAndLogout: []; logout: [] }>();
const { preferencesButtonPosition } = usePreferences(); const {
globalLockScreenShortcutKey,
globalLogoutShortcutKey,
preferencesButtonPosition,
} = usePreferences();
const accessStore = useAccessStore();
const [LockModal, lockModalApi] = useVbenModal({
connectedComponent: LockScreenModal,
});
const [LogoutModal, logoutModalApi] = useVbenModal({
onConfirm() {
handleSubmitLogout();
},
});
const refTrigger = useTemplateRef('refTrigger'); const refTrigger = useTemplateRef('refTrigger');
const refContent = useTemplateRef('refContent'); const refContent = useTemplateRef('refContent');
@@ -96,13 +127,157 @@ watch(
}, },
); );
const showLockInDropdown = computed(
() =>
preferences.widget.lockScreen &&
preferences.widget.lockScreenButtonPosition === 'user-dropdown',
);
const showLogoutInDropdown = computed(
() => preferences.widget.logoutButtonPosition === 'user-dropdown',
);
const showGlobalSearchInDropdown = computed(
() =>
preferences.widget.globalSearch &&
preferences.widget.globalSearchButtonPosition === 'user-dropdown',
);
const showThemeToggleInDropdown = computed(
() =>
preferences.widget.themeToggle &&
preferences.widget.themeToggleButtonPosition === 'user-dropdown',
);
const showLanguageToggleInDropdown = computed(
() =>
preferences.widget.languageToggle &&
preferences.widget.languageToggleButtonPosition === 'user-dropdown',
);
const showTimezoneInDropdown = computed(
() =>
preferences.widget.timezone &&
preferences.widget.timezoneButtonPosition === 'user-dropdown',
);
const showFullscreenInDropdown = computed(
() =>
preferences.widget.fullscreen &&
preferences.widget.fullscreenButtonPosition === 'user-dropdown',
);
const showNotificationInDropdown = computed(
() =>
preferences.widget.notification &&
preferences.widget.notificationButtonPosition === 'user-dropdown',
);
const showRefreshInDropdown = computed(
() =>
preferences.widget.refresh &&
preferences.widget.refreshButtonPosition === 'user-dropdown',
);
const hasAnyInDropdown = computed(
() =>
showLockInDropdown.value ||
showLogoutInDropdown.value ||
showGlobalSearchInDropdown.value ||
showThemeToggleInDropdown.value ||
showLanguageToggleInDropdown.value ||
showTimezoneInDropdown.value ||
showFullscreenInDropdown.value ||
showNotificationInDropdown.value ||
showRefreshInDropdown.value,
);
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
const enableLogoutShortcutKey = computed(() => {
return showLogoutInDropdown.value && globalLogoutShortcutKey.value;
});
const enableLockScreenShortcutKey = computed(() => {
return showLockInDropdown.value && globalLockScreenShortcutKey.value;
});
const enableShortcutKey = computed(() => {
return (
(showLockInDropdown.value || showLogoutInDropdown.value) &&
preferences.shortcutKeys.enable
);
});
function handleOpenLock() {
lockModalApi.open();
}
function handleSubmitLock(lockScreenPassword: string) {
lockModalApi.close();
accessStore.lockScreen(lockScreenPassword);
}
function handleLogout() {
logoutModalApi.open();
openPopover.value = false;
}
function handleSubmitLogout() {
emit('logout');
logoutModalApi.close();
}
// 设置 - 打开偏好设置抽屉 // 设置 - 打开偏好设置抽屉
function handleOpenSettings() { function handleOpenSettings() {
refPreferences.value?.open(); refPreferences.value?.open();
} }
if (enableShortcutKey.value) {
const keys = useMagicKeys();
const logoutKey = keys['Alt+KeyQ'];
const lockKey = keys['Alt+KeyL'];
if (logoutKey) {
whenever(logoutKey, () => {
if (enableLogoutShortcutKey.value) {
handleLogout();
}
});
}
if (lockKey) {
whenever(lockKey, () => {
if (enableLockScreenShortcutKey.value) {
handleOpenLock();
}
});
}
}
</script> </script>
<template> <template>
<LockModal
v-if="showLockInDropdown"
:avatar="avatar"
:text="text"
@submit="handleSubmitLock"
/>
<LogoutModal
v-if="showLogoutInDropdown"
:cancel-text="$t('common.cancel')"
:confirm-text="$t('common.confirm')"
:fullscreen-button="false"
:title="$t('common.prompt')"
centered
content-class="px-8 min-h-10"
footer-class="border-none mb-3 mr-3"
header-class="border-none"
>
{{ $t('ui.widgets.logoutTip') }}
</LogoutModal>
<Preferences <Preferences
v-if="preferencesButtonPosition.userDropdown" v-if="preferencesButtonPosition.userDropdown"
ref="refPreferences" ref="refPreferences"
@@ -159,7 +334,97 @@ function handleOpenSettings() {
<VbenIcon :icon="menu.icon" class="mr-2 size-4" /> <VbenIcon :icon="menu.icon" class="mr-2 size-4" />
{{ menu.text }} {{ menu.text }}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator v-if="preferencesButtonPosition.userDropdown" /> <template v-if="showLockInDropdown || showLogoutInDropdown">
<DropdownMenuSeparator v-if="showLockInDropdown" />
<DropdownMenuItem
v-if="showLockInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleOpenLock"
>
<LockKeyhole class="mr-2 size-4" />
{{ $t('ui.widgets.lockScreen.title') }}
<DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
{{ altView }} L
</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator v-if="showLogoutInDropdown" />
<DropdownMenuItem
v-if="showLogoutInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleLogout"
>
<LogOut class="mr-2 size-4" />
{{ $t('common.logout') }}
<DropdownMenuShortcut v-if="enableLogoutShortcutKey">
{{ altView }} Q
</DropdownMenuShortcut>
</DropdownMenuItem>
</template>
<template
v-if="
showGlobalSearchInDropdown ||
showThemeToggleInDropdown ||
showLanguageToggleInDropdown ||
showTimezoneInDropdown ||
showFullscreenInDropdown ||
showNotificationInDropdown ||
showRefreshInDropdown
"
>
<DropdownMenuSeparator />
<DropdownMenuItem
v-if="showGlobalSearchInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
>
<Search class="mr-2 size-4" />
{{ $t('preferences.widget.globalSearch') }}
</DropdownMenuItem>
<DropdownMenuItem
v-if="showThemeToggleInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
>
<Sun class="mr-2 size-4" />
{{ $t('preferences.widget.themeToggle') }}
</DropdownMenuItem>
<DropdownMenuItem
v-if="showLanguageToggleInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
>
<Languages class="mr-2 size-4" />
{{ $t('preferences.widget.languageToggle') }}
</DropdownMenuItem>
<DropdownMenuItem
v-if="showTimezoneInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
>
<SunMoon class="mr-2 size-4" />
{{ $t('preferences.widget.timezone') }}
</DropdownMenuItem>
<DropdownMenuItem
v-if="showFullscreenInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
>
<Fullscreen class="mr-2 size-4" />
{{ $t('preferences.widget.fullscreen') }}
</DropdownMenuItem>
<DropdownMenuItem
v-if="showNotificationInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
>
<Bell class="mr-2 size-4" />
{{ $t('preferences.widget.notification') }}
</DropdownMenuItem>
<DropdownMenuItem
v-if="showRefreshInDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
>
<RotateCw class="mr-2 size-4" />
{{ $t('preferences.widget.refresh') }}
</DropdownMenuItem>
</template>
<DropdownMenuSeparator
v-if="hasAnyInDropdown || preferencesButtonPosition.userDropdown"
/>
<DropdownMenuItem <DropdownMenuItem
v-if="preferencesButtonPosition.userDropdown" v-if="preferencesButtonPosition.userDropdown"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8" class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"

View File

@@ -21,5 +21,6 @@
"yes": "Yes", "yes": "Yes",
"no": "No", "no": "No",
"showSearchPanel": "Show search panel", "showSearchPanel": "Show search panel",
"hideSearchPanel": "Hide search panel" "hideSearchPanel": "Hide search panel",
"notShow": "Don't Show"
} }

View File

@@ -193,14 +193,25 @@
"widget": { "widget": {
"title": "Widget", "title": "Widget",
"globalSearch": "Enable Global Search", "globalSearch": "Enable Global Search",
"globalSearchPosition": "Global Search Position",
"fullscreen": "Enable Fullscreen", "fullscreen": "Enable Fullscreen",
"fullscreenPosition": "Fullscreen Position",
"themeToggle": "Enable Theme Toggle", "themeToggle": "Enable Theme Toggle",
"themeTogglePosition": "Theme Toggle Position",
"languageToggle": "Enable Language Toggle", "languageToggle": "Enable Language Toggle",
"languageTogglePosition": "Language Toggle Position",
"notification": "Enable Notification", "notification": "Enable Notification",
"notificationPosition": "Notification Position",
"sidebarToggle": "Enable Sidebar Toggle", "sidebarToggle": "Enable Sidebar Toggle",
"lockScreen": "Enable Lock Screen", "lockScreen": "Enable Lock Screen",
"lockScreenPosition": "Lock Screen Button Position",
"logoutButtonPosition": "Logout Button Position",
"header": "Header",
"userDropdown": "User Dropdown",
"refresh": "Enable Refresh", "refresh": "Enable Refresh",
"timezone": "Enable Timezone" "refreshPosition": "Refresh Position",
"timezone": "Enable Timezone",
"timezonePosition": "Timezone Position"
}, },
"antd": { "antd": {
"tabLabel": "Antd Extension", "tabLabel": "Antd Extension",

View File

@@ -21,5 +21,6 @@
"yes": "是", "yes": "是",
"no": "否", "no": "否",
"showSearchPanel": "显示搜索面板", "showSearchPanel": "显示搜索面板",
"hideSearchPanel": "隐藏搜索面板" "hideSearchPanel": "隐藏搜索面板",
"notShow": "不显示"
} }

View File

@@ -193,14 +193,25 @@
"widget": { "widget": {
"title": "小部件", "title": "小部件",
"globalSearch": "启用全局搜索", "globalSearch": "启用全局搜索",
"globalSearchPosition": "全局搜索位置",
"fullscreen": "启用全屏", "fullscreen": "启用全屏",
"fullscreenPosition": "全屏按钮位置",
"themeToggle": "启用主题切换", "themeToggle": "启用主题切换",
"themeTogglePosition": "主题切换位置",
"languageToggle": "启用语言切换", "languageToggle": "启用语言切换",
"languageTogglePosition": "语言切换位置",
"notification": "启用通知", "notification": "启用通知",
"notificationPosition": "通知位置",
"sidebarToggle": "启用侧边栏切换", "sidebarToggle": "启用侧边栏切换",
"lockScreen": "启用锁屏", "lockScreen": "启用锁屏",
"lockScreenPosition": "锁屏按钮位置",
"logoutButtonPosition": "退出按钮位置",
"header": "顶栏",
"userDropdown": "用户下拉窗",
"refresh": "启用刷新", "refresh": "启用刷新",
"timezone": "启用时区" "refreshPosition": "刷新按钮位置",
"timezone": "启用时区",
"timezonePosition": "时区按钮位置"
}, },
"antd": { "antd": {
"tabLabel": "Antd 拓展", "tabLabel": "Antd 拓展",