Navbar.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <template>
  2. <div class="navbar" :class="'nav' + settingsStore.navType">
  3. <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
  4. <breadcrumb v-if="settingsStore.navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
  5. <top-nav v-if="settingsStore.navType == 2" id="topmenu-container" class="topmenu-container" />
  6. <template v-if="settingsStore.navType == 3">
  7. <logo v-show="settingsStore.sidebarLogo" :collapse="false"></logo>
  8. <top-bar id="topbar-container" class="topbar-container" />
  9. </template>
  10. <div class="right-menu">
  11. <template v-if="appStore.device !== 'mobile'">
  12. <header-search id="header-search" class="right-menu-item" />
  13. <el-tooltip content="源码地址" effect="dark" placement="bottom">
  14. <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
  15. </el-tooltip>
  16. <el-tooltip content="文档地址" effect="dark" placement="bottom">
  17. <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
  18. </el-tooltip>
  19. <screenfull id="screenfull" class="right-menu-item hover-effect" />
  20. <el-tooltip content="主题模式" effect="dark" placement="bottom">
  21. <div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme">
  22. <svg-icon v-if="settingsStore.isDark" icon-class="sunny" />
  23. <svg-icon v-if="!settingsStore.isDark" icon-class="moon" />
  24. </div>
  25. </el-tooltip>
  26. <el-tooltip content="布局大小" effect="dark" placement="bottom">
  27. <size-select id="size-select" class="right-menu-item hover-effect" />
  28. </el-tooltip>
  29. </template>
  30. <el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover">
  31. <div class="avatar-wrapper">
  32. <img :src="userStore.avatar" class="user-avatar" />
  33. <span class="user-nickname"> {{ userStore.nickName }} </span>
  34. </div>
  35. <template #dropdown>
  36. <el-dropdown-menu>
  37. <router-link to="/user/profile">
  38. <el-dropdown-item>个人中心</el-dropdown-item>
  39. </router-link>
  40. <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
  41. <span>布局设置</span>
  42. </el-dropdown-item>
  43. <el-dropdown-item divided command="logout">
  44. <span>退出登录</span>
  45. </el-dropdown-item>
  46. </el-dropdown-menu>
  47. </template>
  48. </el-dropdown>
  49. </div>
  50. </div>
  51. </template>
  52. <script setup>
  53. import { ElMessageBox } from 'element-plus'
  54. import Breadcrumb from '@/components/Breadcrumb'
  55. import TopNav from '@/components/TopNav'
  56. import TopBar from './TopBar'
  57. import Logo from './Sidebar/Logo'
  58. import Hamburger from '@/components/Hamburger'
  59. import Screenfull from '@/components/Screenfull'
  60. import SizeSelect from '@/components/SizeSelect'
  61. import HeaderSearch from '@/components/HeaderSearch'
  62. import RuoYiGit from '@/components/RuoYi/Git'
  63. import RuoYiDoc from '@/components/RuoYi/Doc'
  64. import useAppStore from '@/store/modules/app'
  65. import useUserStore from '@/store/modules/user'
  66. import useSettingsStore from '@/store/modules/settings'
  67. const appStore = useAppStore()
  68. const userStore = useUserStore()
  69. const settingsStore = useSettingsStore()
  70. function toggleSideBar() {
  71. appStore.toggleSideBar()
  72. }
  73. function handleCommand(command) {
  74. switch (command) {
  75. case "setLayout":
  76. setLayout()
  77. break
  78. case "logout":
  79. logout()
  80. break
  81. default:
  82. break
  83. }
  84. }
  85. function logout() {
  86. ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
  87. confirmButtonText: '确定',
  88. cancelButtonText: '取消',
  89. type: 'warning'
  90. }).then(() => {
  91. userStore.logOut().then(() => {
  92. location.href = '/index'
  93. })
  94. }).catch(() => { })
  95. }
  96. const emits = defineEmits(['setLayout'])
  97. function setLayout() {
  98. emits('setLayout')
  99. }
  100. async function toggleTheme(event) {
  101. const x = event?.clientX || window.innerWidth / 2
  102. const y = event?.clientY || window.innerHeight / 2
  103. const wasDark = settingsStore.isDark
  104. const isReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches
  105. const isSupported = document.startViewTransition && !isReducedMotion
  106. if (!isSupported) {
  107. settingsStore.toggleTheme()
  108. return
  109. }
  110. try {
  111. const transition = document.startViewTransition(async () => {
  112. await new Promise((resolve) => setTimeout(resolve, 10))
  113. settingsStore.toggleTheme()
  114. await nextTick()
  115. })
  116. await transition.ready
  117. const endRadius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y))
  118. const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`]
  119. document.documentElement.animate(
  120. {
  121. clipPath: !wasDark ? [...clipPath].reverse() : clipPath
  122. }, {
  123. duration: 650,
  124. easing: "cubic-bezier(0.4, 0, 0.2, 1)",
  125. fill: "forwards",
  126. pseudoElement: !wasDark ? "::view-transition-old(root)" : "::view-transition-new(root)"
  127. }
  128. )
  129. await transition.finished
  130. } catch (error) {
  131. console.warn("View transition failed, falling back to immediate toggle:", error)
  132. settingsStore.toggleTheme()
  133. }
  134. }
  135. </script>
  136. <style lang='scss' scoped>
  137. .navbar.nav3 {
  138. .hamburger-container {
  139. display: none !important;
  140. }
  141. }
  142. .navbar {
  143. height: 50px;
  144. overflow: hidden;
  145. position: relative;
  146. background: var(--navbar-bg);
  147. box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  148. display: flex;
  149. align-items: center;
  150. // padding: 0 8px;
  151. box-sizing: border-box;
  152. .hamburger-container {
  153. line-height: 46px;
  154. height: 100%;
  155. cursor: pointer;
  156. transition: background 0.3s;
  157. -webkit-tap-highlight-color: transparent;
  158. display: flex;
  159. align-items: center;
  160. flex-shrink: 0;
  161. margin-right: 8px;
  162. &:hover {
  163. background: rgba(0, 0, 0, 0.025);
  164. }
  165. }
  166. .breadcrumb-container {
  167. flex-shrink: 0;
  168. }
  169. .topmenu-container {
  170. position: absolute;
  171. left: 50px;
  172. }
  173. .topbar-container {
  174. flex: 1;
  175. min-width: 0;
  176. display: flex;
  177. align-items: center;
  178. overflow: hidden;
  179. margin-left: 8px;
  180. }
  181. .errLog-container {
  182. display: inline-block;
  183. vertical-align: top;
  184. }
  185. .right-menu {
  186. height: 100%;
  187. line-height: 50px;
  188. display: flex;
  189. align-items: center;
  190. margin-left: auto;
  191. &:focus {
  192. outline: none;
  193. }
  194. .right-menu-item {
  195. display: inline-block;
  196. padding: 0 8px;
  197. height: 100%;
  198. font-size: 18px;
  199. color: #5a5e66;
  200. vertical-align: text-bottom;
  201. &.hover-effect {
  202. cursor: pointer;
  203. transition: background 0.3s;
  204. &:hover {
  205. background: rgba(0, 0, 0, 0.025);
  206. }
  207. }
  208. &.theme-switch-wrapper {
  209. display: flex;
  210. align-items: center;
  211. svg {
  212. transition: transform 0.3s;
  213. &:hover {
  214. transform: scale(1.15);
  215. }
  216. }
  217. }
  218. }
  219. .avatar-container {
  220. margin-right: 0px;
  221. padding-right: 0px;
  222. .avatar-wrapper {
  223. margin-top: 10px;
  224. right: 8px;
  225. position: relative;
  226. .user-avatar {
  227. cursor: pointer;
  228. width: 30px;
  229. height: 30px;
  230. margin-right: 8px;
  231. border-radius: 50%;
  232. }
  233. .user-nickname{
  234. position: relative;
  235. left: 0px;
  236. bottom: 10px;
  237. font-size: 14px;
  238. font-weight: bold;
  239. }
  240. i {
  241. cursor: pointer;
  242. position: absolute;
  243. right: -20px;
  244. top: 25px;
  245. font-size: 12px;
  246. }
  247. }
  248. }
  249. }
  250. }
  251. </style>