这段代码是一个 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"
: 判断用户是否有头像, 如果有头像,则拼接oss
和avatar
作为头像的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-block
和vertical-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-block
、vertical-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>