diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue index 3b74354c..e85dd1cf 100644 --- a/apps/web-antd/src/layouts/basic.vue +++ b/apps/web-antd/src/layouts/basic.vue @@ -231,6 +231,7 @@ watch( description="ann.vben@gmail.com" tag-text="Pro" @clear-preferences-and-logout="handleLogout" + @logout="handleLogout" /> + diff --git a/packages/effects/layouts/src/basic/layout.vue b/packages/effects/layouts/src/basic/layout.vue index e83f5a4c..d8646a6c 100644 --- a/packages/effects/layouts/src/basic/layout.vue +++ b/packages/effects/layouts/src/basic/layout.vue @@ -369,9 +369,6 @@ const headerSlots = computed(() => { - diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/draggable-list.vue b/packages/effects/layouts/src/widgets/preferences/blocks/draggable-list.vue new file mode 100644 index 00000000..db85f45f --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/draggable-list.vue @@ -0,0 +1,140 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue index 39cd14e5..e0b084a9 100644 --- a/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue @@ -1,31 +1,70 @@ diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue index 66e09a71..c1b6ec8e 100644 --- a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue +++ b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue @@ -174,15 +174,34 @@ const shortcutKeysGlobalLockScreen = defineModel( 'shortcutKeysGlobalLockScreen', ); -const widgetGlobalSearch = defineModel('widgetGlobalSearch'); -const widgetFullscreen = defineModel('widgetFullscreen'); -const widgetLanguageToggle = defineModel('widgetLanguageToggle'); -const widgetNotification = defineModel('widgetNotification'); -const widgetThemeToggle = defineModel('widgetThemeToggle'); -const widgetSidebarToggle = defineModel('widgetSidebarToggle'); -const widgetLockScreen = defineModel('widgetLockScreen'); -const widgetRefresh = defineModel('widgetRefresh'); -const widgetTimezone = defineModel('widgetTimezone'); +const widgetGlobalSearchButtonPosition = defineModel( + 'widgetGlobalSearchButtonPosition', +); +const widgetFullscreenButtonPosition = defineModel( + 'widgetFullscreenButtonPosition', +); +const widgetLanguageToggleButtonPosition = defineModel( + 'widgetLanguageToggleButtonPosition', +); +const widgetNotificationButtonPosition = defineModel( + 'widgetNotificationButtonPosition', +); +const widgetThemeToggleButtonPosition = defineModel( + 'widgetThemeToggleButtonPosition', +); +const widgetLockScreenButtonPosition = defineModel( + 'widgetLockScreenButtonPosition', +); +const widgetLogoutButtonPosition = defineModel( + 'widgetLogoutButtonPosition', +); +const widgetOrder = defineModel('widgetOrder', { required: true }); +const widgetRefreshButtonPosition = defineModel( + 'widgetRefreshButtonPosition', +); +const widgetTimezoneButtonPosition = defineModel( + 'widgetTimezoneButtonPosition', +); const { customPreferences, @@ -483,15 +502,34 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) { v-model:app-preferences-button-position=" appPreferencesButtonPosition " - v-model:widget-fullscreen="widgetFullscreen" - v-model:widget-global-search="widgetGlobalSearch" - v-model:widget-language-toggle="widgetLanguageToggle" - v-model:widget-lock-screen="widgetLockScreen" - v-model:widget-notification="widgetNotification" - v-model:widget-refresh="widgetRefresh" - v-model:widget-sidebar-toggle="widgetSidebarToggle" - v-model:widget-theme-toggle="widgetThemeToggle" - v-model:widget-timezone="widgetTimezone" + v-model:widget-fullscreen-button-position=" + widgetFullscreenButtonPosition + " + v-model:widget-global-search-button-position=" + widgetGlobalSearchButtonPosition + " + v-model:widget-language-toggle-button-position=" + widgetLanguageToggleButtonPosition + " + v-model:widget-lock-screen-button-position=" + widgetLockScreenButtonPosition + " + v-model:widget-logout-button-position=" + widgetLogoutButtonPosition + " + v-model:widget-order="widgetOrder" + 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 + " /> diff --git a/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue b/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue index 2047b950..7a0f293a 100644 --- a/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue +++ b/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue @@ -3,13 +3,27 @@ import type { Component } from 'vue'; import type { AnyFunction } from '@vben/types'; -import { useTemplateRef, watch } from 'vue'; +import { computed, useTemplateRef, watch } from 'vue'; 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 { 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 { Badge, DropdownMenu, @@ -17,11 +31,15 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, + DropdownMenuShortcut, DropdownMenuTrigger, VbenAvatar, VbenIcon, } from '@vben-core/shadcn-ui'; +import { useMagicKeys, whenever } from '@vueuse/core'; + +import { LockScreenModal } from '../lock-screen'; import { Preferences } from '../preferences'; interface Props { @@ -70,9 +88,22 @@ const props = withDefaults(defineProps(), { 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 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() { 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(); + } + }); + } +}