页面顶部导航栏(Navbar)的功能(Navbar/index.vue)

这段代码是一个 Vue.js 组件,实现了页面顶部导航栏(Navbar)的功能。我将分块分析它的各个部分:

模板 (Template):

html 复制代码
<!-- spid-admin/src/layout/components/Navbar/index.vue -->
<template>
  <div class="navbar">
    <hamburger
      id="hamburger-container"
      :is-active="sidebar.opened"
      class="hamburger-container"
      @toggleClick="toggleSideBar"
    />
    <breadcrumb
      id="breadcrumb-container"
      class="breadcrumb-container"
    />
    <!-- <lang-select /> -->
    <div class="right-menu">
      <template v-if="device!=='mobile'">
        <div style="position: relative; display: inline-block; padding: 0 8px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToNotice">
          <el-image :src="noticePic" style="width: 35px; height: 35px; position: relative; top: 7px;" />
        </div>
        <div v-if="isVip" style="position: relative; display: inline-block; padding: 0 4px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToMission">
          <el-image :src="systemTaskPic" style="width: 35px; height: 35px; position: relative; top: 7px;" />
        </div>
        <!-- <header-search class="right-menu-item" /> -->
        <!-- <error-log class="errLog-container right-menu-item hover-effect" /> -->
        <screenfull class="right-menu-item hover-effect" />
        <!-- <el-tooltip
          :content="$t('navbar.size')"
          effect="dark"
          placement="bottom"
        >
          <size-select class="right-menu-item hover-effect" />
        </el-tooltip> -->
        <!-- <lang-select class="right-menu-item hover-effect" /> -->
        <span class="username">{{ username }}</span>
      </template>
      <el-dropdown
        class="avatar-container right-menu-item hover-effect"
        trigger="click"
      >
        <div class="avatar-wrapper">
          <img
            v-if="avatar"
            :src="oss + avatar"
            class="user-avatar"
          >
          <img
            v-else
            :src="require('@/assets/images/logo.jpg')"
            class="user-avatar"
          >
          <i class="el-icon-caret-bottom" />
        </div>
        <el-dropdown-menu slot="dropdown">
          <router-link to="/">
            <el-dropdown-item>
              {{ $t('navbar.dashboard') }}
            </el-dropdown-item>
          </router-link>
          <router-link to="/setting/profile/">
            <el-dropdown-item>
              {{ $t('navbar.profile') }}
            </el-dropdown-item>
          </router-link>
          <router-link to="/subscribe/list/">
            <el-dropdown-item>
              订阅续期
            </el-dropdown-item>
          </router-link>
          <router-link to="/account/child/">
            <el-dropdown-item>
              账号分身
            </el-dropdown-item>
          </router-link>
          <el-dropdown-item
            divided
            @click.native="logout"
          >
            <span style="display:block;">
              {{ $t('navbar.logOut') }}
            </span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>
  • navbar: 导航栏的容器。
  • hamburger: 汉堡按钮,用于切换侧边栏的显示和隐藏,@toggleClick="toggleSideBar" 点击事件触发 toggleSideBar 方法。
  • breadcrumb: 面包屑导航。
  • right-menu: 右侧菜单区域。
    • v-if="device!=='mobile'": 判断当前设备是否为移动设备,如果是,则不显示下列内容。
      • 通知图标: 使用 el-image 显示通知图标,@click="handleToNotice" 点击事件触发 handleToNotice 方法,跳转到通知列表。
    • 任务图标: 使用 el-image 显示任务图标,v-if="isVip" 判断用户是否是vip,如果是vip则显示任务图标。@click="handleToMission" 点击事件触发 handleToMission 方法,跳转到任务列表。
      * screenfull: 全屏切换按钮。
      * username: 显示用户名。
    • el-dropdown: 下拉菜单,用于显示用户头像和一些操作选项。
      • avatar-wrapper: 头像容器。
      • v-if="avatar": 判断用户是否有头像, 如果有头像,则拼接ossavatar 作为头像的url, 否则使用默认的logo作为头像
      • el-dropdown-menu: 下拉菜单项。
        • router-link: 使用 router-link 组件创建路由链接。
        • el-dropdown-item: 下拉菜单项。
        • @click.native="logout": 点击退出登录按钮,触发 logout 方法。

脚本 (Script):

ts 复制代码
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import { AppModule } from '@/store/modules/app'
import { UserModule } from '@/store/modules/user'
import Breadcrumb from '@/components/Breadcrumb/index.vue'
import ErrorLog from '@/components/ErrorLog/index.vue'
import Hamburger from '@/components/Hamburger/index.vue'
import HeaderSearch from '@/components/HeaderSearch/index.vue'
import LangSelect from '@/components/LangSelect/index.vue'
import Screenfull from '@/components/Screenfull/index.vue'
import SizeSelect from '@/components/SizeSelect/index.vue'

@Component({
  name: 'Navbar',
  components: {
    Breadcrumb,
    ErrorLog,
    Hamburger,
    HeaderSearch,
    LangSelect,
    Screenfull,
    SizeSelect
  }
})
export default class extends Vue {
  get sidebar() {
    return AppModule.sidebar
  }

  get device() {
    return AppModule.device.toString()
  }

  get avatar() {
    return UserModule.avatar
  }

  get username() {
    return UserModule.username
  }

  get oss() {
    return AppModule.oss
  }

  get noticeNum() {
    // console.log('改变触发')
    // console.log(AppModule.notices)
    return AppModule.notices
  }

  @Watch('noticeNum')
  watchV(v: any) {
    this.setData()
  }

  public role = UserModule.roles[0]
  public isVip: boolean = JSON.parse(UserModule.token).vip

  public noticePic = require(`@/assets/images/notice-0.png`)
  public systemTaskPic = require(`@/assets/images/system-task.png`)

  private toggleSideBar() {
    AppModule.ToggleSideBar(false)
  }

  private async logout() {
    await UserModule.LogOut()
    this.$router.push(`/login?redirect=${this.$route.fullPath}`)
  }

  private handleToNotice() {
    this.$router.push(`/notice/list`)
  }

  private handleToMission() {
    if (this.role === 'ROLE_SUPER') {
      this.$router.push(`/mission/super-list`)
    } else {
      this.$router.push(`/mission/company-list`)
    }
  }

  private async setData() {
    const num = parseInt(this.noticeNum)
    this.noticePic = require(`@/assets/images/notice-${!num ? '0' : num > 9 ? 'n' : num}.png`)
  }

  created() {
    this.setData()
  }
}
</script>
  • 引入依赖: 引入 vue-property-decorator, AppModule, UserModule, 以及各种组件。
  • 组件定义: 使用 @Component 装饰器定义组件,名称为 Navbar
  • 计算属性:
    • sidebar: 获取 AppModule 中的侧边栏状态。
    • device: 获取 AppModule 中的设备类型。
    • avatar: 获取 UserModule 中的用户头像。
    • username: 获取 UserModule 中的用户名。
    • oss: 获取 AppModule 中的 OSS 地址前缀。
    • noticeNum: 获取 AppModule 中的通知数量。
  • @Watch('noticeNum'): 监听 noticeNum 的变化,如果发生变化,则调用 setData 方法。
  • 数据属性:
    • role: 获取 UserModule 中的用户角色。
    • isVip: 获取 UserModule 中的用户是否是 VIP。
    • noticePic: 通知图标的路径。
    • systemTaskPic: 任务图标的路径。
  • 方法:
    • toggleSideBar(): 调用 AppModule.ToggleSideBar() 方法切换侧边栏的显示和隐藏。
    • logout(): 调用 UserModule.LogOut() 方法退出登录,并跳转到登录页。
    • handleToNotice(): 跳转到通知列表页。
    • handleToMission(): 根据用户角色跳转到对应的任务列表页。
    • setData(): 根据通知数量设置 noticePic 的路径。
  • 生命周期钩子:
    • created(): 调用 setData() 方法,初始化通知图标。

样式 (Style):

css 复制代码
<style lang="scss" scoped>
.navbar {
  // ... 导航栏样式
}
</style>
  • navbar: 导航栏容器的样式,设置高度、背景颜色、阴影等。
  • hamburger-container: 汉堡按钮容器的样式,设置高度、浮动、内边距、光标等。
  • breadcrumb-container: 面包屑导航容器的样式,设置浮动。
  • errLog-container: 错误日志容器的样式,设置 display: inline-blockvertical-align: top
  • right-menu: 右侧菜单容器的样式,设置浮动、高度、行高。
  • right-menu-item: 右侧菜单项的样式,设置 display: inline-block、内边距、高度、字体大小、颜色等。
  • hover-effect: 鼠标悬停效果,设置光标和过渡效果。
  • avatar-container: 头像容器的样式,设置外边距。
  • avatar-wrapper: 头像包装器的样式,设置外边距和相对定位。
  • user-avatar: 用户头像的样式,设置光标、宽度、高度、圆角。
  • el-icon-caret-bottom: 下拉图标的样式,设置光标、绝对定位、字体大小。
  • username: 用户名的样式,设置 display: inline-blockvertical-align: text-bottom、内边距、颜色、字体大小和鼠标悬停效果。

总结:

这段代码实现了一个功能完备的页面顶部导航栏,包括:

  • 汉堡按钮
  • 面包屑导航
  • 通知图标
  • 系统任务图标 (VIP用户专属)
  • 全屏切换按钮
  • 用户名显示
  • 用户头像下拉菜单(包含常用操作和退出登录)

代码结构清晰,使用了 Vuex 管理状态,并使用了 vue-property-decorator 简化组件编写。

可以改进的地方:

  • 组件拆分: 可以将右侧菜单区域拆分成更小的组件,例如将通知图标、任务图标、用户名和头像下拉菜单拆分成独立的组件,提高代码的可复用性和可维护性。
  • 国际化: 可以将硬编码的中文文本替换成国际化资源,例如"订阅续期"、"账号分身"等。
  • 错误处理: 可以添加更详细的错误处理,例如在 logout 方法中捕获错误并提示用户。
  • 代码注释: 可以添加更详细的代码注释,方便其他开发人员理解代码的逻辑。
html 复制代码
<!-- spid-admin/src/layout/components/Navbar/index.vue -->
<template>
  <div class="navbar">
    <hamburger
      id="hamburger-container"
      :is-active="sidebar.opened"
      class="hamburger-container"
      @toggleClick="toggleSideBar"
    />
    <breadcrumb
      id="breadcrumb-container"
      class="breadcrumb-container"
    />
    <!-- <lang-select /> -->
    <div class="right-menu">
      <template v-if="device!=='mobile'">
        <div style="position: relative; display: inline-block; padding: 0 8px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToNotice">
          <el-image :src="noticePic" style="width: 35px; height: 35px; position: relative; top: 7px;" />
        </div>
        <div v-if="isVip" style="position: relative; display: inline-block; padding: 0 4px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToMission">
          <el-image :src="systemTaskPic" style="width: 35px; height: 35px; position: relative; top: 7px;" />
        </div>
        <!-- <header-search class="right-menu-item" /> -->
        <!-- <error-log class="errLog-container right-menu-item hover-effect" /> -->
        <screenfull class="right-menu-item hover-effect" />
        <!-- <el-tooltip
          :content="$t('navbar.size')"
          effect="dark"
          placement="bottom"
        >
          <size-select class="right-menu-item hover-effect" />
        </el-tooltip> -->
        <!-- <lang-select class="right-menu-item hover-effect" /> -->
        <span class="username">{{ username }}</span>
      </template>
      <el-dropdown
        class="avatar-container right-menu-item hover-effect"
        trigger="click"
      >
        <div class="avatar-wrapper">
          <img
            v-if="avatar"
            :src="oss + avatar"
            class="user-avatar"
          >
          <img
            v-else
            :src="require('@/assets/images/logo.jpg')"
            class="user-avatar"
          >
          <i class="el-icon-caret-bottom" />
        </div>
        <el-dropdown-menu slot="dropdown">
          <router-link to="/">
            <el-dropdown-item>
              {{ $t('navbar.dashboard') }}
            </el-dropdown-item>
          </router-link>
          <router-link to="/setting/profile/">
            <el-dropdown-item>
              {{ $t('navbar.profile') }}
            </el-dropdown-item>
          </router-link>
          <router-link to="/subscribe/list/">
            <el-dropdown-item>
              订阅续期
            </el-dropdown-item>
          </router-link>
          <router-link to="/account/child/">
            <el-dropdown-item>
              账号分身
            </el-dropdown-item>
          </router-link>
          <el-dropdown-item
            divided
            @click.native="logout"
          >
            <span style="display:block;">
              {{ $t('navbar.logOut') }}
            </span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import { AppModule } from '@/store/modules/app'
import { UserModule } from '@/store/modules/user'
import Breadcrumb from '@/components/Breadcrumb/index.vue'
import ErrorLog from '@/components/ErrorLog/index.vue'
import Hamburger from '@/components/Hamburger/index.vue'
import HeaderSearch from '@/components/HeaderSearch/index.vue'
import LangSelect from '@/components/LangSelect/index.vue'
import Screenfull from '@/components/Screenfull/index.vue'
import SizeSelect from '@/components/SizeSelect/index.vue'

@Component({
  name: 'Navbar',
  components: {
    Breadcrumb,
    ErrorLog,
    Hamburger,
    HeaderSearch,
    LangSelect,
    Screenfull,
    SizeSelect
  }
})
export default class extends Vue {
  get sidebar() {
    return AppModule.sidebar
  }

  get device() {
    return AppModule.device.toString()
  }

  get avatar() {
    return UserModule.avatar
  }

  get username() {
    return UserModule.username
  }

  get oss() {
    return AppModule.oss
  }

  get noticeNum() {
    // console.log('改变触发')
    // console.log(AppModule.notices)
    return AppModule.notices
  }

  @Watch('noticeNum')
  watchV(v: any) {
    this.setData()
  }

  public role = UserModule.roles[0]
  public isVip: boolean = JSON.parse(UserModule.token).vip

  public noticePic = require(`@/assets/images/notice-0.png`)
  public systemTaskPic = require(`@/assets/images/system-task.png`)

  private toggleSideBar() {
    AppModule.ToggleSideBar(false)
  }

  private async logout() {
    await UserModule.LogOut()
    this.$router.push(`/login?redirect=${this.$route.fullPath}`)
  }

  private handleToNotice() {
    this.$router.push(`/notice/list`)
  }

  private handleToMission() {
    if (this.role === 'ROLE_SUPER') {
      this.$router.push(`/mission/super-list`)
    } else {
      this.$router.push(`/mission/company-list`)
    }
  }

  private async setData() {
    const num = parseInt(this.noticeNum)
    this.noticePic = require(`@/assets/images/notice-${!num ? '0' : num > 9 ? 'n' : num}.png`)
  }

  created() {
    this.setData()
  }
}
</script>

<style lang="scss" scoped>
.navbar {
  height: 50px;
  overflow: hidden;
  position: relative;
  background: #fff;
  box-shadow: 0 1px 4px rgba(0,21,41,.08);

  .hamburger-container {
    line-height: 46px;
    height: 100%;
    float: left;
    padding: 0 15px;
    cursor: pointer;
    transition: background .3s;
    -webkit-tap-highlight-color:transparent;

    &:hover {
      background: rgba(0, 0, 0, .025)
    }
  }

  .breadcrumb-container {
    float: left;
  }

  .errLog-container {
    display: inline-block;
    vertical-align: top;
  }

  .right-menu {
    float: right;
    height: 100%;
    line-height: 50px;

    &:focus {
      outline: none;
    }

    .right-menu-item {
      display: inline-block;
      padding: 0 8px;
      height: 100%;
      font-size: 22px;
      color: #5a5e66;
      vertical-align: text-bottom;

      &.hover-effect {
        cursor: pointer;
        transition: background .3s;

        &:hover {
          background: rgba(0, 0, 0, .025)
        }
      }
    }

    .avatar-container {
      margin-right: 30px;

      .avatar-wrapper {
        margin-top: 5px;
        position: relative;

        .user-avatar {
          cursor: pointer;
          width: 40px;
          height: 40px;
          border-radius: 10px;
        }

        .el-icon-caret-bottom {
          cursor: pointer;
          position: absolute;
          right: -20px;
          top: 25px;
          font-size: 12px;
        }
      }
    }
  }

  .username {
    display: inline-block;
    vertical-align: text-bottom;
    padding: 0 8px;
    color: #1890ff;
    font-size: 12px;

    &:hover {
      color: #46a6ff;
    }
  }
}
</style>
相关推荐
黑匣子~8 个月前
uniapp自定义返回事件(封装)
javascript·uni-app·navbar·返回事件·onbackpress