user-dropdown.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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 { $t } from '@vben/locales';
  6. import { IcRoundLogout, IcRoundSettingsSuggest } from '@vben-core/iconify';
  7. import { preferences, usePreferences } from '@vben-core/preferences';
  8. import {
  9. Badge,
  10. DropdownMenu,
  11. DropdownMenuContent,
  12. DropdownMenuItem,
  13. DropdownMenuLabel,
  14. DropdownMenuSeparator,
  15. DropdownMenuShortcut,
  16. DropdownMenuTrigger,
  17. VbenAlertDialog,
  18. VbenAvatar,
  19. VbenIcon,
  20. } from '@vben-core/shadcn-ui';
  21. import { isWindowsOs } from '@vben-core/toolkit';
  22. import { useMagicKeys, whenever } from '@vueuse/core';
  23. import { useOpenPreferences } from '../preferences/use-open-preferences';
  24. interface Props {
  25. /**
  26. * 头像
  27. */
  28. avatar?: string;
  29. /**
  30. * @zh_CN 描述
  31. */
  32. description?: string;
  33. /**
  34. * 是否启用快捷键
  35. */
  36. enableShortcutKey?: boolean;
  37. /**
  38. * 菜单数组
  39. */
  40. menus?: Array<{ handler: AnyFunction; icon?: Component; text: string }>;
  41. /**
  42. * 标签文本
  43. */
  44. tagText?: string;
  45. /**
  46. * 文本
  47. */
  48. text?: string;
  49. }
  50. defineOptions({
  51. name: 'UserDropdown',
  52. });
  53. const props = withDefaults(defineProps<Props>(), {
  54. avatar: '',
  55. description: '',
  56. enableShortcutKey: true,
  57. menus: () => [],
  58. showShortcutKey: true,
  59. tagText: '',
  60. text: '',
  61. });
  62. const emit = defineEmits<{ logout: [] }>();
  63. const openPopover = ref(false);
  64. const openDialog = ref(false);
  65. const { globalLogoutShortcutKey, globalPreferencesShortcutKey } =
  66. usePreferences();
  67. const { handleOpenPreference } = useOpenPreferences();
  68. const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
  69. const enableLogoutShortcutKey = computed(() => {
  70. return props.enableShortcutKey && globalLogoutShortcutKey.value;
  71. });
  72. const enableShortcutKey = computed(() => {
  73. return props.enableShortcutKey && preferences.shortcutKeys.enable;
  74. });
  75. const enablePreferencesShortcutKey = computed(() => {
  76. return props.enableShortcutKey && globalPreferencesShortcutKey.value;
  77. });
  78. function handleLogout() {
  79. // emit
  80. openDialog.value = true;
  81. openPopover.value = false;
  82. }
  83. function handleSubmitLogout() {
  84. emit('logout');
  85. openDialog.value = false;
  86. }
  87. if (enableShortcutKey.value) {
  88. const keys = useMagicKeys();
  89. whenever(keys['Alt+KeyQ'], () => {
  90. if (enableLogoutShortcutKey.value) {
  91. handleLogout();
  92. }
  93. });
  94. whenever(keys['Alt+Comma'], () => {
  95. if (enablePreferencesShortcutKey.value) {
  96. handleOpenPreference();
  97. }
  98. });
  99. }
  100. </script>
  101. <template>
  102. <VbenAlertDialog
  103. v-model:open="openDialog"
  104. :cancel-text="$t('common.cancel')"
  105. :content="$t('widgets.logout-tip')"
  106. :submit-text="$t('common.confirm')"
  107. :title="$t('common.prompt')"
  108. @submit="handleSubmitLogout"
  109. />
  110. <DropdownMenu>
  111. <DropdownMenuTrigger>
  112. <div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full p-1.5">
  113. <div class="hover:text-accent-foreground flex-center">
  114. <VbenAvatar :alt="text" :src="avatar" class="size-8" dot />
  115. <!-- <div v-if="text" class="ml-2 text-sm">{{ text }}</div> -->
  116. </div>
  117. </div>
  118. </DropdownMenuTrigger>
  119. <DropdownMenuContent class="mr-2 min-w-[240px] p-0 pb-1">
  120. <DropdownMenuLabel class="flex items-center p-3">
  121. <VbenAvatar
  122. :alt="text"
  123. :src="avatar"
  124. class="size-12"
  125. dot
  126. dot-class="bottom-0 right-1 border-2 size-4 bg-green-500"
  127. />
  128. <div class="ml-2 w-full">
  129. <div
  130. class="text-foreground mb-1 flex items-center text-sm font-medium"
  131. >
  132. {{ text }}
  133. <Badge class="ml-2 text-green-400">
  134. {{ tagText }}
  135. </Badge>
  136. </div>
  137. <div class="text-muted-foreground text-xs font-normal">
  138. {{ description }}
  139. </div>
  140. </div>
  141. </DropdownMenuLabel>
  142. <DropdownMenuSeparator />
  143. <DropdownMenuItem
  144. v-for="menu in menus"
  145. :key="menu.text"
  146. class="lineh mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  147. @click="menu.handler"
  148. >
  149. <VbenIcon :icon="menu.icon" class="mr-2 size-5" />
  150. {{ menu.text }}
  151. </DropdownMenuItem>
  152. <DropdownMenuSeparator />
  153. <DropdownMenuItem
  154. class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  155. @click="handleOpenPreference"
  156. >
  157. <IcRoundSettingsSuggest class="mr-2 size-5" />
  158. {{ $t('preferences.name') }}
  159. <DropdownMenuShortcut v-if="enablePreferencesShortcutKey">
  160. {{ altView }} ,
  161. </DropdownMenuShortcut>
  162. </DropdownMenuItem>
  163. <DropdownMenuSeparator />
  164. <DropdownMenuItem
  165. class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  166. @click="handleLogout"
  167. >
  168. <IcRoundLogout class="mr-2 size-5" />
  169. {{ $t('common.logout') }}
  170. <DropdownMenuShortcut v-if="enableLogoutShortcutKey">
  171. {{ altView }} Q
  172. </DropdownMenuShortcut>
  173. </DropdownMenuItem>
  174. </DropdownMenuContent>
  175. </DropdownMenu>
  176. </template>