NavBar (导航栏)
创建 src/layout/components/NavBar.vue
这里优化一下。/layout/components 文件夹的管理
创建src/layout/components/index.js
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
// 引用方式
//import { Navbar, Sidebar } from './components/'
// './components/' 它会优先在 components 文件夹下寻找index.js文件
// 其次会是 index.vue 文件。这样做是方便维护
// 不再是
//import Navbar from './components/Navbar .vue'
//import Sidebar from './components/Sidebar.vue'
src/layout/index.vue。改造后 引入 Navbar组件
主页面
...
样式
接下来是数据的动态化处理
改造src/router/index.js 加入测试的三个路由
加入meta属性,用于传递参数
export const routes = [
{
path: '/',
component: Layout,
name: '主页',
meta: {
title: '主页'
}
// component: () => import('@/views/home/index')
},
{
path: '/dog',
component: Layout,
name: '狗子世界',
meta: {
title: '狗子世界'
},
// component: () => import('@/views/home/index')
children: [
{
path: '/erha',
name: '哈士奇',
meta: {
title: '哈士奇'
},
component: () => import('@/views/home/index')
},
{
path: '/jinmao',
name: '金毛',
meta: {
title: '金毛'
},
component: () => import('@/views/home/index')
},
{
path: '/taidi',
name: '泰迪',
meta: {
title: '泰迪'
},
component: () => import('@/views/home/index')
}
]
}
]
NavBar.vue改造
{{ item.meta.title }}
//script
export default {
name: 'Navbar',
props: {
open: {
type: Boolean,
default: true
}
},
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
switchSidebar() {
this.$emit('switchSidebar')
},
getBreadcrumb() {
// 获取路由对应title && 存在返回右边,不存在返回左边
const matched = this.$route.matched.filter(item => item.meta && item.meta.title)
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
}
}
}
接下来是标签
路由标签
创建/layout/components/ScrollPane.vue 标签多是启动滚动
ScrollPane.vue
创建 src/store/modules/tabs.js。 需要配置Vuex 管理标签的信息
tabs.js
const state = {
visitedViews: [], // 标签组
cachedViews: [] // 需要缓存的标签组,根据这个数组,确定是否缓存页面,暂时没用到
}
const mutations = {
ADD_VISITED_VIEW(state, view) {
// 如果标签跳转的路由存在就不添加
// 名字不同,路径相同的。也加入标签组
if (state.visitedViews.some(v => v.path === view.path)) return
state.visitedViews.push(
Object.assign({}, view, {
title: view.meta.title || 'no-name'
})
)
},
ADD_CACHED_VIEW(state, view) {
// 已存在缓存就不缓存了
if (state.cachedViews.includes(view.name)) return
if (!view.meta.noCache) {
state.cachedViews.push(view.name)
}
}
}
const actions = {
addView({ commit }, view) {
// view == this.$router
commit('ADD_VISITED_VIEW', view)
commit('ADD_CACHED_VIEW', view)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
创建/layout/components/Tabs.vue 路由标签。用于关闭页面,刷新页面
Tabs.vue
{{ tab.title }}
由于加入了标签。所以头部高度将增加32px,所以
layout/index.vue 样式加入margin-top
.app-wrapper {
position: relative;
height: 100%;
width: 100%;
.el-aside{
transition: .5s;
}
.el-header {
padding: 0;
}
.el-main {
margin-top: 32px;
}
}
保存启动项目看看效果
增已经完成了,接下来搭建删以及刷新的功能
删:
src/router/index.js 加入保护属性
{
path: '/',
component: Layout,
name: '主页',
meta: {
title: '主页',
affix: true // 加入保护
}
// component: () => import('@/views/home/index')
},
src/store/modules/tabs.js 加入删除方法
// action
// 删除标签
delView({ commit, state }, view) {
return new Promise(resolve => {
commit('DEL_VISITED_VIEW', view)
commit('DEL_CACHED_VIEW', view)
resolve({
visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews]
})
})
}
// mutations
DEL_VISITED_VIEW(state, view) {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews.splice(i, 1)
}
}
},
DEL_CACHED_VIEW(state, view) {
const index = state.cachedViews.indexOf(view.name)
index > -1 && state.cachedViews.splice(index, 1)
}
// 重构
// ==>
// script 方法区加入 2个方法
methods: {
isAffix(tab) { // 是否受保护,这里保护首页面不被删除。去掉X的按钮
// 在保护的路由 meta 增加 affix属性
return tab.meta && tab.meta.affix
},
closeTabs(tab) {
this.$store.dispatch('tabs/delView', tab).then(({ visitedViews }) => {
// 如果删除的是当前页面,则跳转去下一个页面。
if (this.isActive(tab)) {
if (visitedViews.length) {
// 跳转到最后一个标签
const lastTab = visitedViews[visitedViews.length - 1]
this.$router.push(lastTab.fullPath)
} else {
// 如果没有标签了,则跳去首页
this.$router.push('/')
}
}
})
}
}
标签拓展功能
刷新、关闭、关闭其他、关闭全部
// 加入 contextmenu属性。实现鼠标右键功能
{{ tab.title }}
方法区
...
data() {
return {
visible: false, // 菜单显隐变量
top: 0, // 菜单偏移量x
left: 0, // 菜单偏移量x
selectedTab: {} // 鼠标右击的tab
}
},
...
methods: {
openMenu(tab, e) {
// 计算偏移量
// this.$el = Tabs.vue 这个Dom
// getBoundingClientRect().left 获取tabs 距离窗口左边距离。
// 由于left 根据父元素进行偏移。
// 所以 left = 鼠标在窗口的x坐标 - 侧边栏宽度 15为菜单离鼠标一段距离
this.left = e.clientX - this.$el.getBoundingClientRect().left + 15 // 15: margin right
// top 由于不用适配,所以采用 鼠标在当前元素的相对位置
this.top = e.offsetY
// 显示菜单
this.visible = true
// 功能操作的tab。
this.selectedTab = tab
},
menu(select) {
switch (select) {
case 'refresh':
// 清除该页面缓存,在跳转该路由 达到刷新效果。
this.$store.dispatch('tabs/delCachedView', this.selectedTab).then(() => {
const { fullPath } = this.selectedTab
this.$nextTick(() => {
const { query } = this.$route
this.$router.replace({ path: fullPath, query })
})
})
break
case 'close':
this.closeTabs(this.selectedTab)
break
case 'other':
this.$store.dispatch('tabs/delOtherView', this.selectedTab).then(() => {
// 不是当前激活,删除其他后,跳转到该页面
if (!this.isActive(this.selectedTab)) this.$router.push(this.selectedTab.fullPath)
})
break
case 'all':
this.$store.dispatch('tabs/delAllView').then(() => {
this.$router.push('/')
})
break
}
// 隐藏菜单
this.visible = false
}
}
...
// 样式scss
.contextmenu {
margin: 0;
background: #fff;
z-index: 99;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
li {
margin: 0;
padding: 7px 16px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
src/store/modules/tabs.js 加入刷新、关闭等执行方法
// actions
delCachedView({ commit }, view) {
return new Promise(resolve => {
commit('DEL_CACHED_VIEW', view)
resolve()
})
},
delOtherView({ commit }, view) {
return new Promise(resolve => {
commit('DEL_Other_VIEW', view)
resolve()
})
},
delAllView({ commit }) {
return new Promise(resolve => {
commit('DEL_ALL_VIEW')
resolve()
})
}
// mutations
DEL_CACHED_VIEW(state, view) {
const index = state.cachedViews.indexOf(view.name)
index > -1 && state.cachedViews.splice(index, 1)
},
DEL_Other_VIEW(state, view) {
// 重置页面标签数组和缓存数组
state.visitedViews = [
Object.assign({}, view, {
title: view.meta.title || 'no-name'
})
]
state.cachedViews = [view.name]
},
DEL_ALL_VIEW(state) {
// 重置页面标签数组和缓存数组
state.visitedViews = []
state.cachedViews = []
}
最后再加上左键取消菜单的方法
...
watch: {
$route() {
this.addTab() // 路由一旦变化就会触发
},
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
} else {
document.body.removeEventListener('click', this.closeMenu)
}
}
},
...
methods: {
closeMenu() {
this.visible = false
}
}
到此导航栏 header 部分 配置完了。以上功能可以根据需求进行一些加工。