1
0

user-dropdown.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <script setup lang="ts">
  2. import type { AnyFunction } from '@vben/types';
  3. import type { Component } from 'vue';
  4. import { computed, ref } from 'vue';
  5. import { LockKeyhole, LogOut } from '@vben/icons';
  6. import { $t } from '@vben/locales';
  7. import { preferences, usePreferences } from '@vben/preferences';
  8. import { useLockStore } from '@vben/stores';
  9. import { isWindowsOs } from '@vben/utils';
  10. import { useVbenModal } from '@vben-core/popup-ui';
  11. import {
  12. Badge,
  13. DropdownMenu,
  14. DropdownMenuContent,
  15. DropdownMenuItem,
  16. DropdownMenuLabel,
  17. DropdownMenuSeparator,
  18. DropdownMenuShortcut,
  19. DropdownMenuTrigger,
  20. VbenAvatar,
  21. VbenIcon,
  22. } from '@vben-core/shadcn-ui';
  23. import { useMagicKeys, whenever } from '@vueuse/core';
  24. import { LockScreenModal } from '../lock-screen';
  25. interface Props {
  26. /**
  27. * 头像
  28. */
  29. avatar?: string;
  30. /**
  31. * @zh_CN 描述
  32. */
  33. description?: string;
  34. /**
  35. * 是否启用快捷键
  36. */
  37. enableShortcutKey?: boolean;
  38. /**
  39. * 菜单数组
  40. */
  41. menus?: Array<{ handler: AnyFunction; icon?: Component; text: string }>;
  42. /**
  43. * 标签文本
  44. */
  45. tagText?: string;
  46. /**
  47. * 文本
  48. */
  49. text?: string;
  50. }
  51. defineOptions({
  52. name: 'UserDropdown',
  53. });
  54. const props = withDefaults(defineProps<Props>(), {
  55. avatar: '',
  56. description: '',
  57. enableShortcutKey: true,
  58. menus: () => [],
  59. showShortcutKey: true,
  60. tagText: '',
  61. text: '',
  62. });
  63. const emit = defineEmits<{ logout: [] }>();
  64. const openPopover = ref(false);
  65. const { globalLockScreenShortcutKey, globalLogoutShortcutKey } =
  66. usePreferences();
  67. const lockStore = useLockStore();
  68. const [LockModal, lockModalApi] = useVbenModal({
  69. connectedComponent: LockScreenModal,
  70. });
  71. const [LogoutModal, logoutModalApi] = useVbenModal({
  72. onConfirm() {
  73. handleSubmitLogout();
  74. },
  75. });
  76. const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
  77. const enableLogoutShortcutKey = computed(() => {
  78. return props.enableShortcutKey && globalLogoutShortcutKey.value;
  79. });
  80. const enableLockScreenShortcutKey = computed(() => {
  81. return props.enableShortcutKey && globalLockScreenShortcutKey.value;
  82. });
  83. const enableShortcutKey = computed(() => {
  84. return props.enableShortcutKey && preferences.shortcutKeys.enable;
  85. });
  86. function handleOpenLock() {
  87. lockModalApi.open();
  88. }
  89. function handleSubmitLock(lockScreenPassword: string) {
  90. lockModalApi.close();
  91. lockStore.lockScreen(lockScreenPassword);
  92. }
  93. function handleLogout() {
  94. // emit
  95. logoutModalApi.open();
  96. openPopover.value = false;
  97. }
  98. function handleSubmitLogout() {
  99. emit('logout');
  100. logoutModalApi.close();
  101. }
  102. if (enableShortcutKey.value) {
  103. const keys = useMagicKeys();
  104. whenever(keys['Alt+KeyQ']!, () => {
  105. if (enableLogoutShortcutKey.value) {
  106. handleLogout();
  107. }
  108. });
  109. whenever(keys['Alt+KeyL']!, () => {
  110. if (enableLockScreenShortcutKey.value) {
  111. handleOpenLock();
  112. }
  113. });
  114. }
  115. </script>
  116. <template>
  117. <LockModal
  118. v-if="preferences.widget.lockScreen"
  119. :avatar="avatar"
  120. :text="text"
  121. @submit="handleSubmitLock"
  122. />
  123. <LogoutModal
  124. :cancel-text="$t('common.cancel')"
  125. :confirm-text="$t('common.confirm')"
  126. :fullscreen-button="false"
  127. :title="$t('common.prompt')"
  128. centered
  129. content-class="px-8 min-h-10"
  130. footer-class="border-none mb-3 mr-3"
  131. header-class="border-none"
  132. >
  133. {{ $t('ui.widgets.logoutTip') }}
  134. </LogoutModal>
  135. <DropdownMenu>
  136. <DropdownMenuTrigger>
  137. <div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full p-1.5">
  138. <div class="hover:text-accent-foreground flex-center">
  139. <VbenAvatar :alt="text" :src="avatar" class="size-8" dot />
  140. </div>
  141. </div>
  142. </DropdownMenuTrigger>
  143. <DropdownMenuContent class="mr-2 min-w-[240px] p-0 pb-1">
  144. <DropdownMenuLabel class="flex items-center p-3">
  145. <VbenAvatar
  146. :alt="text"
  147. :src="avatar"
  148. class="size-12"
  149. dot
  150. dot-class="bottom-0 right-1 border-2 size-4 bg-green-500"
  151. />
  152. <div class="ml-2 w-full">
  153. <div
  154. v-if="tagText || text || $slots.tagText"
  155. class="text-foreground mb-1 flex items-center text-sm font-medium"
  156. >
  157. {{ text }}
  158. <slot name="tagText">
  159. <Badge v-if="tagText" class="ml-2 text-green-400">
  160. {{ tagText }}
  161. </Badge>
  162. </slot>
  163. </div>
  164. <div class="text-muted-foreground text-xs font-normal">
  165. {{ description }}
  166. </div>
  167. </div>
  168. </DropdownMenuLabel>
  169. <DropdownMenuSeparator v-if="menus?.length" />
  170. <DropdownMenuItem
  171. v-for="menu in menus"
  172. :key="menu.text"
  173. class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  174. @click="menu.handler"
  175. >
  176. <VbenIcon :icon="menu.icon" class="mr-2 size-4" />
  177. {{ menu.text }}
  178. </DropdownMenuItem>
  179. <DropdownMenuSeparator />
  180. <DropdownMenuItem
  181. v-if="preferences.widget.lockScreen"
  182. class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  183. @click="handleOpenLock"
  184. >
  185. <LockKeyhole class="mr-2 size-4" />
  186. {{ $t('ui.widgets.lockScreen.title') }}
  187. <DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
  188. {{ altView }} L
  189. </DropdownMenuShortcut>
  190. </DropdownMenuItem>
  191. <DropdownMenuSeparator v-if="preferences.widget.lockScreen" />
  192. <DropdownMenuItem
  193. class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  194. @click="handleLogout"
  195. >
  196. <LogOut class="mr-2 size-4" />
  197. {{ $t('common.logout') }}
  198. <DropdownMenuShortcut v-if="enableLogoutShortcutKey">
  199. {{ altView }} Q
  200. </DropdownMenuShortcut>
  201. </DropdownMenuItem>
  202. </DropdownMenuContent>
  203. </DropdownMenu>
  204. </template>