user-dropdown.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <script setup lang="ts">
  2. import type { AnyFunction } from '@vben/types';
  3. import { IcRoundLogout, IcRoundSettingsSuggest } from '@vben-core/iconify';
  4. import {
  5. Badge,
  6. DropdownMenu,
  7. DropdownMenuContent,
  8. DropdownMenuItem,
  9. DropdownMenuLabel,
  10. DropdownMenuSeparator,
  11. DropdownMenuShortcut,
  12. DropdownMenuTrigger,
  13. VbenAlertDialog,
  14. VbenAvatar,
  15. VbenIcon,
  16. } from '@vben-core/shadcn-ui';
  17. import { isWindowsOs } from '@vben-core/toolkit';
  18. import type { Component } from 'vue';
  19. import { $t } from '@vben/locales';
  20. import { preference } from '@vben/preference';
  21. import { useMagicKeys, whenever } from '@vueuse/core';
  22. import { computed, ref } from 'vue';
  23. import { useOpenPreference } from '../preference/use-open-preference';
  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 { handleOpenPreference } = useOpenPreference();
  66. const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
  67. const shortcutKeys = computed(() => {
  68. return props.enableShortcutKey && preference.shortcutKeys;
  69. });
  70. function handleLogout() {
  71. // emit
  72. openDialog.value = true;
  73. openPopover.value = false;
  74. }
  75. function handleSubmitLogout() {
  76. emit('logout');
  77. openDialog.value = false;
  78. }
  79. if (shortcutKeys.value) {
  80. const keys = useMagicKeys();
  81. whenever(keys['Alt+KeyQ'], () => {
  82. if (shortcutKeys.value) {
  83. handleLogout();
  84. }
  85. });
  86. whenever(keys['Alt+Comma'], () => {
  87. if (shortcutKeys.value) {
  88. handleOpenPreference();
  89. }
  90. });
  91. }
  92. </script>
  93. <template>
  94. <VbenAlertDialog
  95. v-model:open="openDialog"
  96. :content="$t('widgets.logout-tip')"
  97. :title="$t('common.prompt')"
  98. :cancel-text="$t('common.cancel')"
  99. :submit-text="$t('common.confirm')"
  100. @submit="handleSubmitLogout"
  101. />
  102. <DropdownMenu>
  103. <DropdownMenuTrigger>
  104. <div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full p-1.5">
  105. <div class="hover:text-accent-foreground flex-center">
  106. <VbenAvatar :alt="text" :src="avatar" class="size-8" dot />
  107. <!-- <div v-if="text" class="ml-2 text-sm">{{ text }}</div> -->
  108. </div>
  109. </div>
  110. </DropdownMenuTrigger>
  111. <DropdownMenuContent class="mr-2 min-w-[240px] p-0 pb-1">
  112. <DropdownMenuLabel class="flex items-center p-3">
  113. <VbenAvatar
  114. :alt="text"
  115. :src="avatar"
  116. class="size-12"
  117. dot
  118. dot-class="bottom-0 right-1 border-2 size-4 bg-green-500"
  119. />
  120. <div class="ml-2 w-full">
  121. <div
  122. class="text-foreground mb-1 flex items-center text-sm font-medium"
  123. >
  124. {{ text }}
  125. <Badge class="ml-2 text-green-400">
  126. {{ tagText }}
  127. </Badge>
  128. </div>
  129. <div class="text-muted-foreground text-xs font-normal">
  130. {{ description }}
  131. </div>
  132. </div>
  133. </DropdownMenuLabel>
  134. <DropdownMenuSeparator />
  135. <DropdownMenuItem
  136. v-for="menu in menus"
  137. :key="menu.text"
  138. class="lineh mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  139. @click="menu.handler"
  140. >
  141. <VbenIcon :icon="menu.icon" class="mr-2 size-5" />
  142. {{ menu.text }}
  143. </DropdownMenuItem>
  144. <DropdownMenuSeparator />
  145. <DropdownMenuItem
  146. v-if="preference"
  147. class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  148. @click="handleOpenPreference"
  149. >
  150. <IcRoundSettingsSuggest class="mr-2 size-5" />
  151. {{ $t('preference.preferences') }}
  152. <DropdownMenuShortcut v-if="shortcutKeys">
  153. {{ altView }} ,
  154. </DropdownMenuShortcut>
  155. </DropdownMenuItem>
  156. <DropdownMenuSeparator />
  157. <DropdownMenuItem
  158. class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
  159. @click="handleLogout"
  160. >
  161. <IcRoundLogout class="mr-2 size-5" />
  162. {{ $t('common.logout') }}
  163. <DropdownMenuShortcut v-if="shortcutKeys">
  164. {{ altView }} Q
  165. </DropdownMenuShortcut>
  166. </DropdownMenuItem>
  167. </DropdownMenuContent>
  168. </DropdownMenu>
  169. </template>