| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- <template>
- <div class="navbar" :class="'nav' + settingsStore.navType">
- <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
- <breadcrumb v-if="settingsStore.navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
- <top-nav v-if="settingsStore.navType == 2" id="topmenu-container" class="topmenu-container" />
- <template v-if="settingsStore.navType == 3">
- <logo v-show="settingsStore.sidebarLogo" :collapse="false"></logo>
- <top-bar id="topbar-container" class="topbar-container" />
- </template>
- <div class="right-menu">
- <template v-if="appStore.device !== 'mobile'">
- <header-search id="header-search" class="right-menu-item" />
- <el-tooltip content="源码地址" effect="dark" placement="bottom">
- <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
- </el-tooltip>
- <el-tooltip content="文档地址" effect="dark" placement="bottom">
- <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
- </el-tooltip>
- <screenfull id="screenfull" class="right-menu-item hover-effect" />
- <el-tooltip content="主题模式" effect="dark" placement="bottom">
- <div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme">
- <svg-icon v-if="settingsStore.isDark" icon-class="sunny" />
- <svg-icon v-if="!settingsStore.isDark" icon-class="moon" />
- </div>
- </el-tooltip>
- <el-tooltip content="布局大小" effect="dark" placement="bottom">
- <size-select id="size-select" class="right-menu-item hover-effect" />
- </el-tooltip>
- </template>
- <el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover">
- <div class="avatar-wrapper">
- <img :src="userStore.avatar" class="user-avatar" />
- <span class="user-nickname"> {{ userStore.nickName }} </span>
- </div>
- <template #dropdown>
- <el-dropdown-menu>
- <router-link to="/user/profile">
- <el-dropdown-item>个人中心</el-dropdown-item>
- </router-link>
- <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
- <span>布局设置</span>
- </el-dropdown-item>
- <el-dropdown-item divided command="logout">
- <span>退出登录</span>
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </div>
- </div>
- </template>
- <script setup>
- import { ElMessageBox } from 'element-plus'
- import Breadcrumb from '@/components/Breadcrumb'
- import TopNav from '@/components/TopNav'
- import TopBar from './TopBar'
- import Logo from './Sidebar/Logo'
- import Hamburger from '@/components/Hamburger'
- import Screenfull from '@/components/Screenfull'
- import SizeSelect from '@/components/SizeSelect'
- import HeaderSearch from '@/components/HeaderSearch'
- import RuoYiGit from '@/components/RuoYi/Git'
- import RuoYiDoc from '@/components/RuoYi/Doc'
- import useAppStore from '@/store/modules/app'
- import useUserStore from '@/store/modules/user'
- import useSettingsStore from '@/store/modules/settings'
- const appStore = useAppStore()
- const userStore = useUserStore()
- const settingsStore = useSettingsStore()
- function toggleSideBar() {
- appStore.toggleSideBar()
- }
- function handleCommand(command) {
- switch (command) {
- case "setLayout":
- setLayout()
- break
- case "logout":
- logout()
- break
- default:
- break
- }
- }
- function logout() {
- ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(() => {
- userStore.logOut().then(() => {
- location.href = '/index'
- })
- }).catch(() => { })
- }
- const emits = defineEmits(['setLayout'])
- function setLayout() {
- emits('setLayout')
- }
- async function toggleTheme(event) {
- const x = event?.clientX || window.innerWidth / 2
- const y = event?.clientY || window.innerHeight / 2
- const wasDark = settingsStore.isDark
- const isReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches
- const isSupported = document.startViewTransition && !isReducedMotion
- if (!isSupported) {
- settingsStore.toggleTheme()
- return
- }
- try {
- const transition = document.startViewTransition(async () => {
- await new Promise((resolve) => setTimeout(resolve, 10))
- settingsStore.toggleTheme()
- await nextTick()
- })
- await transition.ready
- const endRadius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y))
- const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`]
- document.documentElement.animate(
- {
- clipPath: !wasDark ? [...clipPath].reverse() : clipPath
- }, {
- duration: 650,
- easing: "cubic-bezier(0.4, 0, 0.2, 1)",
- fill: "forwards",
- pseudoElement: !wasDark ? "::view-transition-old(root)" : "::view-transition-new(root)"
- }
- )
- await transition.finished
- } catch (error) {
- console.warn("View transition failed, falling back to immediate toggle:", error)
- settingsStore.toggleTheme()
- }
- }
- </script>
- <style lang='scss' scoped>
- .navbar.nav3 {
- .hamburger-container {
- display: none !important;
- }
- }
- .navbar {
- height: 50px;
- overflow: hidden;
- position: relative;
- background: var(--navbar-bg);
- box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
- display: flex;
- align-items: center;
- // padding: 0 8px;
- box-sizing: border-box;
- .hamburger-container {
- line-height: 46px;
- height: 100%;
- cursor: pointer;
- transition: background 0.3s;
- -webkit-tap-highlight-color: transparent;
- display: flex;
- align-items: center;
- flex-shrink: 0;
- margin-right: 8px;
- &:hover {
- background: rgba(0, 0, 0, 0.025);
- }
- }
- .breadcrumb-container {
- flex-shrink: 0;
- }
- .topmenu-container {
- position: absolute;
- left: 50px;
- }
- .topbar-container {
- flex: 1;
- min-width: 0;
- display: flex;
- align-items: center;
- overflow: hidden;
- margin-left: 8px;
- }
- .errLog-container {
- display: inline-block;
- vertical-align: top;
- }
- .right-menu {
- height: 100%;
- line-height: 50px;
- display: flex;
- align-items: center;
- margin-left: auto;
- &:focus {
- outline: none;
- }
- .right-menu-item {
- display: inline-block;
- padding: 0 8px;
- height: 100%;
- font-size: 18px;
- color: #5a5e66;
- vertical-align: text-bottom;
- &.hover-effect {
- cursor: pointer;
- transition: background 0.3s;
- &:hover {
- background: rgba(0, 0, 0, 0.025);
- }
- }
- &.theme-switch-wrapper {
- display: flex;
- align-items: center;
- svg {
- transition: transform 0.3s;
-
- &:hover {
- transform: scale(1.15);
- }
- }
- }
- }
- .avatar-container {
- margin-right: 0px;
- padding-right: 0px;
- .avatar-wrapper {
- margin-top: 10px;
- right: 8px;
- position: relative;
- .user-avatar {
- cursor: pointer;
- width: 30px;
- height: 30px;
- margin-right: 8px;
- border-radius: 50%;
- }
- .user-nickname{
- position: relative;
- left: 0px;
- bottom: 10px;
- font-size: 14px;
- font-weight: bold;
- }
- i {
- cursor: pointer;
- position: absolute;
- right: -20px;
- top: 25px;
- font-size: 12px;
- }
- }
- }
- }
- }
- </style>
|