vue实现单页面多标签页
程序员文章站
2024-03-17 17:37:04
...
不废话先上效果图
实现上面的效果,主要是通过改变vue的路由机制实现的
- 在router目录文件下新建TabRouter.js文件,添加以下内容
const route = Object.create(null)
route.install = function (vue) {
// 第一个字符串是 组件名,第二个是组件路径,第三个是包名(如果不指定则已1.js,2.js....n.js命名)
vue.component('home', (resolve) => {require.ensure([], ()=>resolve(require('@/Views/index/adminIndex.vue')), 'home')})
vue.component('userscreate', (resolve) => {require.ensure([], ()=>resolve(require('@/Views/demo/Sitecreate.vue')), 'userscreate')})
vue.component('userslist', (resolve) => {require.ensure([], ()=>resolve(require('@/Views/demo/Sitelists.vue')), 'userslist')})
}
export default route
- 在router目录文件下新建MainConf.js文件,添加如下内容
export default {
// 列表配置
// 菜单列表
menu: [
{
title: '主菜单1', // 一级菜单名称
icon: 'el-icon-location', // 一级菜单 icon
menuId: '1', // 索引(必须唯一,是字符串)
sub: [ // 子菜单(二级菜单)
{
title: '站点列表', // 菜单名称
component: 'userslist', // 点击菜单打开的组件名称
menuId: '1-1' // 索引 (用于识别当前打开的tab,必须唯一,是字符串)
},
{title: '新增站点', component: 'userscreate', menuId: '1-2'}
]
}
],
// 主页 tab 配置
homeTab: {
title: '首页', // tab 显示标题
menuId: 'home', // tab 内部名称(用来识别当前打开的tab)
components: [{path: 'home'}], // tab 对应的组件
}
}
- 引入store文件
import Vue from 'vue'
import Vuex from 'vuex'
import MainStore from '@/router/MainStore'
Vue.use(Vuex)
export default new Vuex.Store({
// 模块化 store, 把需要 store 的数据导入到这里即可
// store 格式参考 /src/Frame/MainStore.js
modules: {
MainStore,
}
})
4.在MainStore中追加一下tab标签实现方法
import store from "@/Store";
import conf from './MainConf'
import {Base64} from 'js-base64'
export default {
state: {
/* page: [], */
currentTabIndex: conf.homeTab.menuId, // 当前显示的 tab
homeTabMenuId: conf.homeTab.menuId, // 主页 tab 的 menuId
openedTabs: [ // 当前打开的 tab 列表
conf.homeTab
],
},
getters: {
// 获取 当前显示的 tab name
GetCurrentTabIndex (state) {
return state.currentTabIndex
},
// 获取 主页 tab 的 menuId
GetHomeTabMenuId (state) {
return state.homeTabMenuId
},
// 获取 当前打开的 tab 列表
GetOpenedTabs (state) {
return state.openedTabs
},
// 获取查询参数
GetQuery (state) {
let res = Object.create(null)
state.openedTabs.map(i => {
if (i.menuId === state.currentTabIndex) {
let end = i.components.length - 1
res = i.components[end].query || {}
return
}
})
return res
},
// 获取当前**的 tab
GetCurrentTab (state) {
return state.openedTabs.find(i => i.menuId === state.currentTabIndex)
}
},
mutations: {
// 设置 hash
SetHash (state) {
let cur = state.openedTabs.find(i => i.menuId === state.currentTabIndex)
location.hash = Base64.encode(JSON.stringify(cur))
},
// 设置 主页 tab
SetHomeTab (state,item) {
state.homeTabMenuId = item.menuId
state.openedTabs[0] = item
},
// 回显 tab
reShowHash (state) {
let url = location.href
let indexOfSharp = url.indexOf('#')
if (indexOfSharp > 0) {
let hash = url.substr(indexOfSharp + 1)
let tab = JSON.parse(Base64.decode(hash))
// let tab = JSON.parse(hash)
if (tab.menuId === state.homeTabMenuId) {
state.openedTabs = state.openedTabs.filter(i => i.menuId !== state.homeTabMenuId)
}
state.openedTabs.push(tab)
state.currentTabIndex = tab.menuId
}
},
// 设置 当前显示的 tab name
SetCurrentTabIndex (state, data) {
state.currentTabIndex = data
store.commit('SetHash')
},
// 添加 tab 到 tab 列表
OpenedTabsPush (state, item) {
// 设置当前要显示的 tab name
state.currentTabIndex = item.menuId
// 判断 tab 项是否已存在
let tabExsit = state.openedTabs.find(i => i.menuId === item.menuId)
/* if (tabExsit == null) {
state.openedTabs.push(item)
} */
if (!tabExsit) {
state.openedTabs.push({
title: item.title, // 显示标题
menuId: item.menuId, // 用于标记当前打开 tab 的 name
components: [{path: item.component}], // tab 对应的组件
})
} else if(item.fromHistory) { // 点击前进后退按钮
state.openedTabs.map(i => {
if(i.menuId === item.menuId) {
i.components = item.components
}
})
}
if (!item.fromHash) store.commit('SetHash')
},
// 当前 tab 内部的跳转
OpenedSubTabsPush (state, item) {
state.openedTabs.map(i => {
if (i.menuId === state.currentTabIndex) {
i.components.push(item)
}
})
store.commit('SetHash')
},
// 返回
OpenedSubTabsBack (state, num = 1) {
if (num < 1) num = 1
state.openedTabs.map(i => {
if (i.menuId === state.currentTabIndex) {
let newLength = i.components.length - ~~num
if (newLength > 0) {
i.components = i.components.slice(0, newLength)
}
}
})
store.commit('SetHash')
},
// 替换当前组件
OpenedSubTabsReplace (state, item) {
let index = state.openedTabs.length - 2
state.openedTabs.map(i => {
if (i.menuId === state.currentTabIndex) {
i.components.splice(index, 1, item)
}
})
store.commit('SetHash')
},
// 从 tab 列表 移除 tab
OpenedTabsRemove (state, menuId) {
state.openedTabs = state.openedTabs.filter(item => {
return item.menuId === state.homeTabMenuId || item.menuId !== menuId
})
// 查询当前标签是否被关闭,如果被关闭,则打开主页标签
let tab = state.openedTabs.find(item => item.menuId === menuId)
if (!tab) state.currentTabIndex = state.homeTabMenuId
store.commit('SetHash')
},
// 关闭 其他的 tab
CloseOthersTabs (state) {
state.openedTabs = state.openedTabs.filter(item => {
return item.menuId === state.homeTabMenuId || item.menuId === state.currentTabIndex
})
store.commit('SetHash')
},
// 关闭所有 tab
CloseAllTabs (state) {
state.openedTabs.length = 1
state.currentTabIndex = state.homeTabMenuId
store.commit('SetHash')
},
},
actions: {},
}
5.创建RouterMethods.js用来做具体的跳转实现
import store from '@/Store/index'
const RouterMethods = Object.create(null)
// 处理 push 和 replace 2个接口的附带参数
function processData (item, payload) {
let newItem = {}
if(typeof item === 'string') {
// 参数是字符串,并且带查询参数
if(item.indexOf('?') > -1) {
let qIndex = item.indexOf('?')
let componentName = item.substr(0,qIndex)
let querySring = item.substr(qIndex + 1)
let queryArray = querySring.split('&')
let query = Object.create(null)
queryArray.map(i => {
let eIndex = i.indexOf('=')
let qKey = i.substr(0,eIndex)
let qValue = i.substr(eIndex + 1)
query[qKey] = qValue
})
newItem.path = componentName
newItem.query = query
}
// 参数是字符串,不带查询参数
else {
newItem.path = item
}
}
// 参数是 JSON 对象
else {
newItem = item
}
// 如果有第二个参数,则会覆盖原本的 query
if(payload) newItem.query = Object.assign(newItem.query,payload)
return newItem
}
RouterMethods.install = function (Vue, options) {
Vue.prototype.$tab = {
// 设置当前显示的 tab name
showTab(data) {
store.commit('SetCurrentTabIndex', data)
},
// 打开新的 tab 项
open (item) {
store.commit('OpenedTabsPush', item)
},
// 删除 tab 项
close (menuId) {
store.commit('OpenedTabsRemove',menuId)
},
// 跳转
push(item,payload) {
let newItem = processData(item,payload)
store.commit('OpenedSubTabsPush',newItem)
},
// 后退
back(num) {
store.commit('OpenedSubTabsBack',num)
},
// 替换
replace(item,payload) {
let newItem = processData(item,payload)
store.commit('OpenedSubTabsReplace',newItem)
},
// 关闭所有 tab
closeAll () {
store.commit('CloseAllTabs')
},
// 关闭其他标签
closeOthers () {
store.commit('CloseOthersTabs')
},
// 根据浏览器的 url 回显 tab
reShow() {
store.commit('reShowHash')
},
// 获取当前组件的查询参数
query () {
return store.getters.GetQuery
},
// 获取当前**的 tab
info () {
return store.getters.GetCurrentTab
},
}
}
export default RouterMethods
6.在main.js中引入TabRoute.js以及store
/* import "babel-polyfill"; */ // 兼容IE
import Vue from 'vue'
import store from '@/Store/index'
import index from './views/MainFrame.vue'
import TabRoute from '@/Router/TabRoute'
import RouterMethods from '@/Router/RouterMethods'
Vue.use(TabRoute)
Vue.use(RouterMethods)
new Vue({
el: '#app',
store,
render: v => v(index)
})
7.在MainFrame.vue中追加tab显示就可以了
<el-menu :default-active="currentTabIndex" :default-openeds="spreadedMenus">
<el-submenu v-for="(item,index) in menuList" :key="index" :index="item.menuId">
<template slot="title">
<i :class="item.icon"></i>
<span>{{item.title}}</span>
</template>
<el-menu-item-group>
<el-menu-item v-for="(subItem,subIndex) in item.sub"
:key="subIndex"
@click="openTab(subItem)"
:index="subItem.menuId">
{{subItem.title}}
</el-menu-item>
具体的源码请参照:
https://download.csdn.net/download/ying940718/11085546
推荐阅读
-
Vue3.0由单页面应用改为多页面开发
-
vue实现单页面多标签页
-
vue 单页应用(spa)前端路由实现原理
-
vue实现:聊天记录,下拉加载下一页数据,并让页面停留在原位置
-
详解vue 单页应用(spa)前端路由实现原理
-
CPropertySheet标签页 实现各个CPropertyPage页面之间的切换_html/css_WEB-ITnose
-
Vue-cli创建项目从单页面到多页面的方法
-
vue如何将单页面改造成多页面应用
-
CPropertySheet标签页 实现各个CPropertyPage页面之间的切换_html/css_WEB-ITnose
-
webpack、vue、node实现单页面代码分享