mixNav.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <script setup lang="ts">
  2. import extraIcon from "./extraIcon.vue";
  3. import Search from "../search/index.vue";
  4. import Notice from "../notice/index.vue";
  5. import { isAllEmpty } from "@pureadmin/utils";
  6. import { useNav } from "@/layout/hooks/useNav";
  7. import { transformI18n } from "@/plugins/i18n";
  8. import { ref, toRaw, watch, onMounted, nextTick } from "vue";
  9. import { useRenderIcon } from "@/components/ReIcon/src/hooks";
  10. import { getParentPaths, findRouteByPath } from "@/router/utils";
  11. import { useTranslationLang } from "../../hooks/useTranslationLang";
  12. import { usePermissionStoreHook } from "@/store/modules/permission";
  13. import globalization from "@/assets/svg/globalization.svg?component";
  14. import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
  15. import Setting from "@iconify-icons/ri/settings-3-line";
  16. import Check from "@iconify-icons/ep/check";
  17. const menuRef = ref();
  18. const defaultActive = ref(null);
  19. const { t, route, locale, translationCh, translationEn } =
  20. useTranslationLang(menuRef);
  21. const {
  22. device,
  23. logout,
  24. onPanel,
  25. resolvePath,
  26. username,
  27. userAvatar,
  28. getDivStyle,
  29. avatarsStyle,
  30. getDropdownItemStyle,
  31. getDropdownItemClass
  32. } = useNav();
  33. function getDefaultActive(routePath) {
  34. const wholeMenus = usePermissionStoreHook().wholeMenus;
  35. /** 当前路由的父级路径 */
  36. const parentRoutes = getParentPaths(routePath, wholeMenus)[0];
  37. defaultActive.value = !isAllEmpty(route.meta?.activePath)
  38. ? route.meta.activePath
  39. : findRouteByPath(parentRoutes, wholeMenus)?.children[0]?.path;
  40. }
  41. onMounted(() => {
  42. getDefaultActive(route.path);
  43. });
  44. nextTick(() => {
  45. menuRef.value?.handleResize();
  46. });
  47. watch(
  48. () => [route.path, usePermissionStoreHook().wholeMenus],
  49. () => {
  50. getDefaultActive(route.path);
  51. }
  52. );
  53. </script>
  54. <template>
  55. <div
  56. v-if="device !== 'mobile'"
  57. v-loading="usePermissionStoreHook().wholeMenus.length === 0"
  58. class="horizontal-header"
  59. >
  60. <el-menu
  61. ref="menuRef"
  62. router
  63. mode="horizontal"
  64. popper-class="pure-scrollbar"
  65. class="horizontal-header-menu"
  66. :default-active="defaultActive"
  67. >
  68. <el-menu-item
  69. v-for="route in usePermissionStoreHook().wholeMenus"
  70. :key="route.path"
  71. :index="resolvePath(route) || route.redirect"
  72. >
  73. <template #title>
  74. <div
  75. v-if="toRaw(route.meta.icon)"
  76. :class="['sub-menu-icon', route.meta.icon]"
  77. >
  78. <component
  79. :is="useRenderIcon(route.meta && toRaw(route.meta.icon))"
  80. />
  81. </div>
  82. <div :style="getDivStyle">
  83. <span class="select-none">
  84. {{ transformI18n(route.meta.title) }}
  85. </span>
  86. <extraIcon :extraIcon="route.meta.extraIcon" />
  87. </div>
  88. </template>
  89. </el-menu-item>
  90. </el-menu>
  91. <div class="horizontal-header-right">
  92. <!-- 菜单搜索 -->
  93. <Search id="header-search" />
  94. <!-- 通知 -->
  95. <Notice id="header-notice" />
  96. <!-- 国际化 -->
  97. <el-dropdown id="header-translation" trigger="click">
  98. <globalization
  99. class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
  100. />
  101. <template #dropdown>
  102. <el-dropdown-menu class="translation">
  103. <el-dropdown-item
  104. :style="getDropdownItemStyle(locale, 'zh')"
  105. :class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
  106. @click="translationCh"
  107. >
  108. <span v-show="locale === 'zh'" class="check-zh">
  109. <IconifyIconOffline :icon="Check" />
  110. </span>
  111. 简体中文
  112. </el-dropdown-item>
  113. <el-dropdown-item
  114. :style="getDropdownItemStyle(locale, 'en')"
  115. :class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
  116. @click="translationEn"
  117. >
  118. <span v-show="locale === 'en'" class="check-en">
  119. <IconifyIconOffline :icon="Check" />
  120. </span>
  121. English
  122. </el-dropdown-item>
  123. </el-dropdown-menu>
  124. </template>
  125. </el-dropdown>
  126. <!-- 退出登录 -->
  127. <el-dropdown trigger="click">
  128. <span class="el-dropdown-link navbar-bg-hover select-none">
  129. <img :src="userAvatar" :style="avatarsStyle" />
  130. <p v-if="username" class="dark:text-white">{{ username }}</p>
  131. </span>
  132. <template #dropdown>
  133. <el-dropdown-menu class="logout">
  134. <el-dropdown-item @click="logout">
  135. <IconifyIconOffline
  136. :icon="LogoutCircleRLine"
  137. style="margin: 5px"
  138. />
  139. {{ t("buttons.hsLoginOut") }}
  140. </el-dropdown-item>
  141. </el-dropdown-menu>
  142. </template>
  143. </el-dropdown>
  144. <span
  145. class="set-icon navbar-bg-hover"
  146. :title="t('buttons.hssystemSet')"
  147. @click="onPanel"
  148. >
  149. <IconifyIconOffline :icon="Setting" />
  150. </span>
  151. </div>
  152. </div>
  153. </template>
  154. <style lang="scss" scoped>
  155. :deep(.el-loading-mask) {
  156. opacity: 0.45;
  157. }
  158. .translation {
  159. ::v-deep(.el-dropdown-menu__item) {
  160. padding: 5px 40px;
  161. }
  162. .check-zh {
  163. position: absolute;
  164. left: 20px;
  165. }
  166. .check-en {
  167. position: absolute;
  168. left: 20px;
  169. }
  170. }
  171. .logout {
  172. max-width: 120px;
  173. ::v-deep(.el-dropdown-menu__item) {
  174. display: inline-flex;
  175. flex-wrap: wrap;
  176. min-width: 100%;
  177. }
  178. }
  179. </style>