- 首先需要了解的一些開發(fā)規(guī)則
- 整個(gè)系統(tǒng)支持三種類型的子頁面(在數(shù)據(jù)庫里必須以menu.url字段來存儲):
(1) user/login:這個(gè)是針對的Vue框架下的頁面組件,注意不能以/開頭召边,對應(yīng)views/user/login.vue
(2) iframe:****:這個(gè)是Spring Boot框架提供的一些頁面铺呵,比如swagger和druid監(jiān)控頁面。
(3) http[s]://www.baidu.com隧熙,這個(gè)是第三方頁面片挂。 - 菜單按鈕權(quán)限的獲取被設(shè)計(jì)在路由守衛(wèi)中執(zhí)行初始化。
- 本系統(tǒng)保留動(dòng)態(tài)菜單與路由守衛(wèi)的功能贞盯,但在前期不進(jìn)行啟用音念,啟用的時(shí)機(jī)要看具體的情況。
- 在store/modules下的app.js中添加一個(gè)動(dòng)態(tài)菜單設(shè)置狀態(tài)邻悬、動(dòng)態(tài)菜單症昏、用戶權(quán)限設(shè)置狀態(tài)随闽、用戶權(quán)限父丰。
export default {
state: {
dynamicRouteLoaded: false, // 菜單和路由是否已經(jīng)加載
dynamicRoutes: [], // 用戶的權(quán)限菜單
permissionLoaded: false, // 用戶權(quán)限是否已經(jīng)加載
permissions: [] // 用戶的按鈕權(quán)限
},
getters: {
dynamicRouteLoaded (state) {
return state.dynamicRouteLoaded
},
dynamicRoutes (state) {
return state.dynamicRoutes
},
permissionLoaded (state) {
return state.permissionLoaded
},
permissions (state) {
return state.permissions
}
},
mutations: {
dynamicRouteLoaded (state, dynamicRouteLoaded) { // 改變菜單和路由的加載狀態(tài)
state.dynamicRouteLoaded = dynamicRouteLoaded
},
dynamicRoutes (state, dynamicRoutes) { // 設(shè)置用戶的權(quán)限菜單
state.dynamicRoutes = dynamicRoutes
},
permissionLoaded (state, permissionLoaded) { // 改變用戶權(quán)限的加載狀態(tài)
state.permissionLoaded = permissionLoaded
},
permissions (state, permissions) { // 設(shè)置用戶的按鈕權(quán)限
state.permissions = permissions
}
},
actions: {
}
}
- 重寫登錄方法
login () {
var data = {account: '', password: ''}
this.$api.login.login(data).then(res => {
if (res.code !== 200) {
// 其它處理
} else {
Cookies.set('token', res.data.token) // 放置token到cookie
sessionStorage.setItem('user', data.account) // 保存用戶到本地會話
this.$store.commit('dynamicRouteLoaded', false) // 要求重新加載導(dǎo)航菜單
this.$store.commit('permissionLoaded', false) // 要求重新加載導(dǎo)航菜單
this.$router.push('/') // 登錄成功, 跳轉(zhuǎn)到主頁
}
this.loading = false
})
}
- 導(dǎo)航守衛(wèi)src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import store from '@/store'
import api from '@/http/api'
import { getIFramePath, getIFrameUrl } from '@/utils/iframe'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: '首頁',
component: HelloWorld,
children: [
{
path: '',
name: '系統(tǒng)介紹',
component: HelloWorld,
meta: {
icon: 'fa fa-home fa-lg',
index: 0
}
}
]
}
]
})
/**
* 導(dǎo)航守衛(wèi)
*/
router.beforeEach((to, from, next) => {
// 登錄成功之后,會把用戶信息保存在會話掘宪,用戶信息的存在時(shí)間為會話生命周期蛾扇,頁面關(guān)閉即失效
let userName = sessionStorage.getItem('user')
if (to.path === '/login') {
// 如果是訪問登錄界面,如果用戶會話信息存在魏滚,代表已登錄過镀首,跳轉(zhuǎn)到主頁
if (userName) {
next({ path: '/' })
} else {
next()
}
} else {
if (!userName) {
// 如果訪問非登錄界面,且戶會話信息不存在鼠次,代表未登錄更哄,則跳轉(zhuǎn)到登錄界面
next({ path: '/login' })
} else {
// 加載動(dòng)態(tài)菜單和路由
addDynamicMenuAndRoutes(userName, to, from)
next()
}
}
})
/**
* 加載動(dòng)態(tài)菜單和路由
*/
function addDynamicMenuAndRoutes (userName, to, from) {
// 處理IFrame嵌套頁面
handleIFrameUrl(to.path)
if (store.state.app.menuRouteLoaded) {
return
}
api.menu.findNavTree({'userName': userName}).then(res => {
// 添加動(dòng)態(tài)路由
let dynamicRoutes = addDynamicRoutes(res.data)
// 處理靜態(tài)組件綁定路由
router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)
router.addRoutes(router.options.routes)
// 保存加載狀態(tài)
store.commit('menuRouteLoaded', true)
// 保存菜單樹
store.commit('setNavTree', res.data)
}).then(res => {
api.user.findPermissions({'name': userName}).then(res => {
// 保存用戶權(quán)限標(biāo)識集合
store.commit('setPerms', res.data)
})
}).catch(function (res) {
})
}
/**
* 處理IFrame嵌套頁面
* 此處主要去匹配在store中存儲的iframe路徑列表, 通過匹配Path獲取iframe真正的url,將url存儲到store中
*/
function handleIFrameUrl (path) {
// 嵌套頁面腥寇,保存iframeUrl到store成翩,供IFrame組件讀取展示
let url = path
let length = store.state.iframe.iframeUrls.length
for (let i = 0; i < length; i++) {
let iframe = store.state.iframe.iframeUrls[i]
if (path != null && path.endsWith(iframe.path)) {
url = iframe.url
store.commit('setIFrameUrl', url)
break
}
}
}
/**
* 添加動(dòng)態(tài)(菜單)路由
* @param {*} menuList 菜單列表
* @param {*} routes 遞歸創(chuàng)建的動(dòng)態(tài)(菜單)路由
*/
function addDynamicRoutes (menuList = [], routes = []) {
var temp = []
for (var i = 0; i < menuList.length; i++) {
if (menuList[i].children && menuList[i].children.length >= 1) {
temp = temp.concat(menuList[i].children)
} else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, '')
// 創(chuàng)建路由配置
var route = {
path: menuList[i].url,
component: null,
name: menuList[i].name,
meta: {
icon: menuList[i].icon,
index: menuList[i].id
}
}
let path = getIFramePath(menuList[i].url)
if (path) {
// 如果是嵌套頁面, 通過iframe展示
route['path'] = path
route['component'] = resolve => require([`@/views/IFrame/IFrame`], resolve)
// 存儲嵌套頁面路由路徑和訪問URL
let url = getIFrameUrl(menuList[i].url)
let iFrameUrl = {'path': path, 'url': url}
store.commit('addIFrameUrl', iFrameUrl)
} else {
try {
// 根據(jù)菜單URL動(dòng)態(tài)加載vue組件,這里要求vue組件須按照url路徑存儲
// 如url="sys/user"赦役,則組件路徑應(yīng)是"@/views/sys/user.vue",否則組件加載不到
let array = menuList[i].url.split('/')
let url = ''
for (let i = 0; i < array.length; i++) {
url += array[i].substring(0, 1).toUpperCase() + array[i].substring(1) + '/'
}
url = url.substring(0, url.length - 1)
route['component'] = resolve => require([`@/views/${url}`], resolve)
} catch (e) {}
}
routes.push(route)
}
}
if (temp.length >= 1) {
addDynamicRoutes(temp, routes)
} else {
console.log(routes)
}
return routes
}
export default router
- src\utils\iframe.js
/**
* 嵌套頁面IFrame模塊
*/
import { baseUrl } from '@/utils/global'
/**
* 嵌套頁面URL地址
* iframe:**** -> ****
* http[s]://**** -> ****
* @param {*} url
*/
export function getIFramePath (url) {
let iframeUrl = ''
// 匹配以iframe:開頭的任意長度的字符串
if (/^iframe:.*/.test(url)) {
iframeUrl = url.replace('iframe:', '')
} else if (/^http[s]?:\/\/.*/.test(url)) {
iframeUrl = url.replace('http://', '')
iframeUrl = url.replace('https://', '')
if (iframeUrl.indexOf(':') !== -1) {
iframeUrl = iframeUrl.substring(iframeUrl.lastIndexOf(':') + 1)
}
}
return iframeUrl
}
/**
* 獲取嵌套頁面路由的路徑
* iframe:druid/index.html -> baseUrl + druid/index.html
* http[s]://druid/index.html -> 不變
* @param {*} url
*/
export function getIFrameUrl (url) {
let iframeUrl = ''
if (/^iframe:.*/.test(url)) {
iframeUrl = baseUrl + url.replace('iframe:', '')
} else if (/^http[s]?:\/\/.*/.test(url)) {
iframeUrl = url
}
return iframeUrl
}
- src/views/IFrame/IFrame.js
<template>
<div class="iframe-container">
<iframe :src="src" scrolling="auto" frameborder="0" class="frame" :onload="onloaded()">
</iframe>
</div>
</template>
<script>
export default {
data () {
return {
src: '',
loading: null
}
},
methods: {
// 獲取路徑
resetSrc: function (url) {
this.src = url
this.load()
},
load: function () {
this.loading = this.$loading({
lock: true,
text: 'loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.5)',
// fullscreen: false,
target: document.querySelector('#main-container ')
})
},
onloaded: function () {
if (this.loading) {
this.loading.close()
}
}
},
mounted () {
this.resetSrc(this.$store.state.iframe.iframeUrl)
},
watch: {
$route: {
handler: function (val, oldVal) {
// 如果是跳轉(zhuǎn)到嵌套頁面麻敌,切換iframe的url
this.resetSrc(this.$store.state.iframe.iframeUrl)
}
}
}
}
</script>
<style lang="scss">
.iframe-container {
position: absolute;
top: 0px;
left: 0px;
right: 0px;;
bottom: 0px;
.frame {
width: 100%;
height: 100%;
}
}
</style>
真正投入使用的router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: '首頁',
component: HelloWorld,
children: [
{
path: '',
name: '系統(tǒng)介紹',
component: HelloWorld,
meta: {
icon: 'fa fa-home fa-lg',
index: 0
}
}
]
}
]
})
/**
* 導(dǎo)航守衛(wèi)
*/
router.beforeEach((to, from, next) => {
let userName = sessionStorage.getItem('user')
if (to.path === '/login') {
if (userName) {
next({ path: '/' })
} else {
next()
}
} else {
if (!userName) {
next({ path: '/login' })
} else {
next()
}
}
})
export default router
導(dǎo)航事件:
let path = getIFramePath(menu.url)
if (!path)
path = menu.url
this.$router.push("/" + path);
七、頁面權(quán)限控制
權(quán)限標(biāo)識是對頁面資源進(jìn)行權(quán)限控制的唯一標(biāo)識掂摔,主要是增术羔、刪赢赊、改、查的權(quán)限控制级历。權(quán)限標(biāo)識主要包含四種释移,以用戶管理為例,權(quán)限標(biāo)識包括sys:user:add(新增)鱼喉、sys:user:edit(編輯)秀鞭、sys:user:delete(刪除)、sys:user:view(查看)扛禽。
src\permission/index.js
import store from '@/store'
/**
* 判斷用戶是否擁有操作權(quán)限
* 根據(jù)傳入的權(quán)限標(biāo)識锋边,查看是否存在用戶權(quán)限標(biāo)識集合
* @param perms
*/
export function hasPermission (perms) {
let hasPermission = false
let permissions = store.state.user.perms
for(let i=0, len=permissions.length; i<len; i++) {
if(permissions[i] === perms) {
hasPermission = true;
break
}
}
return hasPermission
}
頁面操作按鈕提供perms屬性綁定權(quán)限標(biāo)識,使用disable屬性綁定權(quán)限判斷方法的返回值编曼,權(quán)限判斷方法hasPerms(perms)通過查找上一步保存的用戶權(quán)限標(biāo)識集合是否包含perms來包含說明用戶擁有此相關(guān)權(quán)限豆巨,否則設(shè)置當(dāng)前操作按鈕為不可用狀態(tài)。
新建一個(gè)權(quán)限按鈕的組件:
<template>
<el-button :size="size" :type="type" :icon="icon"
:loading="loading" :disabled="!hasPerms(perms)" @click="handleClick">
{{label}}
</el-button>
</template>
<script>
import { hasPermission } from '@/permission/index.js'
export default {
name: 'KtButton',
props: {
label: { // 按鈕顯示文本
type: String,
default: 'Button'
},
icon: { // 按鈕顯示圖標(biāo)
type: String,
default: ''
},
size: { // 按鈕尺寸
type: String,
default: 'mini'
},
type: { // 按鈕類型
type: String,
default: null
},
loading: { // 按鈕加載標(biāo)識
type: Boolean,
default: false
},
disabled: { // 按鈕是否禁用
type: Boolean,
default: false
},
perms: { // 按鈕權(quán)限標(biāo)識掐场,外部使用者傳入
type: String,
default: null
}
},
data() {
return {
}
},
methods: {
handleClick: function () {
// 按鈕操作處理函數(shù)
this.$emit('click', {})
},
hasPerms: function (perms) {
// 根據(jù)權(quán)限標(biāo)識和外部指示狀態(tài)進(jìn)行權(quán)限判斷
return hasPermission(perms) & !this.disabled
}
},
mounted() {
}
}
</script>
<style scoped>
</style>
image.png
表格組件
六往扔、框架頁面設(shè)計(jì)
- Home頁設(shè)計(jì)
Home.vue主頁由導(dǎo)航菜單、頭部區(qū)域和主內(nèi)容區(qū)域組成熊户。
<template>
<div class="container">
<!-- 導(dǎo)航菜單欄 -->
<nav-bar></nav-bar>
<!-- 頭部區(qū)域 -->
<head-bar></head-bar>
<!-- 主內(nèi)容區(qū)域 -->
<main-content></main-content>
</div>
</template>
<script>
import HeadBar from "./HeadBar"
import NavBar from "./NavBar"
import MainContent from "./MainContent"
export default {
components:{
HeadBar,
NavBar,
MainContent
}
};
</script>
<style scoped lang="scss">
.container {
position:absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
// background: rgba(224, 234, 235, 0.1);
}
</style>
- 頂部菜單欄設(shè)計(jì)Headbar
<template>
<div class="headbar" :style="{'background':themeColor}"
:class="collapse?'position-collapse-left':'position-left'">
<!-- 導(dǎo)航收縮 -->
<span class="hamburg">
<el-menu class="el-menu-demo" :background-color="themeColor" text-color="#fff"
:active-text-color="themeColor" mode="horizontal">
<el-menu-item index="1" @click="onCollapse">
<hamburger :isActive="collapse"></hamburger>
</el-menu-item>
</el-menu>
</span>
<!-- 導(dǎo)航菜單 -->
<span class="navbar">
<el-menu :default-active="activeIndex" class="el-menu-demo"
:background-color="themeColor" text-color="#fff" active-text-color="#ffd04b" mode="horizontal" @select="selectNavBar()">
<el-menu-item index="1" @click="$router.push('/')">{{$t("common.home")}}</el-menu-item>
<el-menu-item index="2" @click="openWindow('https://gitee.com/liuge1988/kitty/wikis/Home')">{{$t("common.doc")}}</el-menu-item>
<el-menu-item index="3" @click="openWindow('https://www.cnblogs.com/xifengxiaoma/')">{{$t("common.blog")}}</el-menu-item>
</el-menu>
</span>
<!-- 工具欄 -->
<span class="toolbar">
<el-menu class="el-menu-demo" :background-color="themeColor" text-color="#14889A"
:active-text-color="themeColor" mode="horizontal">
<el-menu-item index="1">
<!-- 主題切換 -->
<theme-picker class="theme-picker" :default="themeColor"
@onThemeChange="onThemeChange">
</theme-picker>
</el-menu-item>
<el-menu-item index="2" v-popover:popover-lang>
<!-- 語言切換 -->
<li style="color:#fff;" class="fa fa-language fa-lg"></li>
<el-popover ref="popover-lang" placement="bottom-start" trigger="click" v-model="langVisible">
<div class="lang-item" @click="changeLanguage('zh_cn')">簡體中文</div>
<div class="lang-item" @click="changeLanguage('en_us')">English</div>
</el-popover>
</el-menu-item>
<el-menu-item index="3" v-popover:popover-message>
<!-- 我的私信 -->
<el-badge :value="5" :max="99" class="badge">
<li style="color:#fff;" class="fa fa-envelope-o fa-lg"></li>
</el-badge>
<el-popover ref="popover-message" placement="bottom-end" trigger="click">
<message-panel></message-panel>
</el-popover>
</el-menu-item>
<el-menu-item index="4" v-popover:popover-notice>
<!-- 系統(tǒng)通知 -->
<el-badge :value="4" :max="99" class="badge">
<li style="color:#fff;" class="fa fa-bell-o fa-lg"></li>
</el-badge>
<el-popover ref="popover-notice" placement="bottom-end" trigger="click">
<notice-panel></notice-panel>
</el-popover>
</el-menu-item>
<el-menu-item index="5" v-popover:popover-personal>
<!-- 用戶信息 -->
<span class="user-info"><img :src="user.avatar" />{{user.nickName}}</span>
<el-popover ref="popover-personal" placement="bottom-end" trigger="click" :visible-arrow="false">
<personal-panel :user="user"></personal-panel>
</el-popover>
</el-menu-item>
</el-menu>
</span>
</div>
</template>
<script>
import { mapState } from 'vuex'
import mock from "@/mock/index"
import Hamburger from "@/components/Hamburger"
import ThemePicker from "@/components/ThemePicker"
import NoticePanel from "@/views/Core/NoticePanel"
import MessagePanel from "@/views/Core/MessagePanel"
import PersonalPanel from "@/views/Core/PersonalPanel"
export default {
components:{
Hamburger,
ThemePicker,
NoticePanel,
MessagePanel,
PersonalPanel
},
data() {
return {
user: {
},
activeIndex: '1',
langVisible: false
}
},
methods: {
openWindow(url) {
window.open(url)
},
selectNavBar(key, keyPath) {
console.log(key, keyPath)
},
// 折疊導(dǎo)航欄
onCollapse: function() {
this.$store.commit('onCollapse')
},
// 切換主題
onThemeChange: function(themeColor) {
this.$store.commit('setThemeColor', themeColor)
},
// 語言切換
changeLanguage(lang) {
lang === '' ? 'zh_cn' : lang
this.$i18n.locale = lang
this.langVisible = false
}
},
mounted() {
var user = sessionStorage.getItem("user")
if (user) {
let params = {name:user}
this.$api.user.findByName(params).then((res) => {
if(res.code == 200) {
this.user = res.data
this.user.avatar = require("@/assets/user.png")
}
})
}
},
computed:{
...mapState({
themeColor: state=>state.app.themeColor,
collapse: state=>state.app.collapse
})
}
}
</script>
<style scoped lang="scss">
.headbar {
position: fixed;
top: 0;
right: 0;
z-index: 1030;
height: 60px;
line-height: 60px;
border-color: rgba(180, 190, 190, 0.8);
border-left-width: 1px;
border-left-style: solid;
}
.hamburg {
float: left;
}
.navbar {
float: left;
}
.toolbar {
float: right;
}
.lang-item {
font-size: 16px;
padding-left: 8px;
padding-top: 8px;
padding-bottom: 8px;
cursor: pointer;
}
.lang-item:hover {
font-size: 18px;
background: #b0d6ce4d;
}
.user-info {
font-size: 20px;
color: #fff;
cursor: pointer;
img {
width: 40px;
height: 40px;
border-radius: 10px;
margin: 10px 0px 10px 10px;
float: right;
}
}
.badge {
line-height: 18px;
}
.position-left {
left: 200px;
}
.position-collapse-left {
left: 65px;
}
</style>
- 菜單欄設(shè)計(jì)NavBar.vue
<template>
<div class="menu-bar-container">
<!-- logo -->
<div class="logo" :style="{'background-color':themeColor}" :class="collapse?'menu-bar-collapse-width':'menu-bar-width'"
@click="$router.push('/')">
<img v-if="collapse" src="@/assets/logo.png"/> <div>{{collapse?'':appName}}</div>
</div>
<!-- 導(dǎo)航菜單 -->
<el-menu ref="navmenu" default-active="1" :class="collapse?'menu-bar-collapse-width':'menu-bar-width'"
:collapse="collapse" :collapse-transition="false" :unique-opened="true "
@open="handleopen" @close="handleclose" @select="handleselect">
<!-- 導(dǎo)航菜單樹組件萍膛,動(dòng)態(tài)加載菜單 -->
<menu-tree v-for="item in navTree" :key="item.id" :menu="item"></menu-tree>
</el-menu>
</div>
</template>
<script>
import { mapState } from 'vuex'
import MenuTree from "@/components/MenuTree"
export default {
components:{
MenuTree
},
computed: {
...mapState({
appName: state=>state.app.appName,
themeColor: state=>state.app.themeColor,
collapse: state=>state.app.collapse,
navTree: state=>state.menu.navTree
}),
mainTabs: {
get () { return this.$store.state.tab.mainTabs },
set (val) { this.$store.commit('updateMainTabs', val) }
},
mainTabsActiveName: {
get () { return this.$store.state.tab.mainTabsActiveName },
set (val) { this.$store.commit('updateMainTabsActiveName', val) }
}
},
watch: {
$route: 'handleRoute'
},
created () {
this.handleRoute(this.$route)
},
methods: {
handleopen() {
console.log('handleopen')
},
handleclose() {
console.log('handleclose')
},
handleselect(a, b) {
console.log('handleselect')
},
// 路由操作處理
handleRoute (route) {
// tab標(biāo)簽頁選中, 如果不存在則先添加
var tab = this.mainTabs.filter(item => item.name === route.name)[0]
if (!tab) {
tab = {
name: route.name,
title: route.name,
icon: route.meta.icon
}
this.mainTabs = this.mainTabs.concat(tab)
}
this.mainTabsActiveName = tab.name
// 切換標(biāo)簽頁時(shí)同步更新高亮菜單
if(this.$refs.navmenu != null) {
this.$refs.navmenu.activeIndex = '' + route.meta.index
this.$refs.navmenu.initOpenedMenu()
}
}
}
}
</script>
<style scoped lang="scss">
.menu-bar-container {
position: fixed;
top: 0px;
left: 0;
bottom: 0;
z-index: 1020;
.el-menu {
position:absolute;
top: 60px;
bottom: 0px;
text-align: left;
// background-color: #2968a30c;
}
.logo {
position:absolute;
top: 0px;
height: 60px;
line-height: 60px;
background: #545c64;
cursor:pointer;
img {
width: 40px;
height: 40px;
border-radius: 0px;
margin: 10px 10px 10px 10px;
float: left;
}
div {
font-size: 22px;
color: white;
text-align: left;
padding-left: 20px;
}
}
.menu-bar-width {
width: 200px;
}
.menu-bar-collapse-width {
width: 65px;
}
}
</style>
- 子頁面框架設(shè)計(jì)MainContent
<template>
<div id="main-container" class="main-container" :class="$store.state.app.collapse?'position-collapse-left':'position-left'">
<!-- 標(biāo)簽頁 -->
<div class="tab-container">
<el-tabs class="tabs" :class="$store.state.app.collapse?'position-collapse-left':'position-left'"
v-model="mainTabsActiveName" :closable="true" type="card"
@tab-click="selectedTabHandle" @tab-remove="removeTabHandle">
<el-dropdown class="tabs-tools" :show-timeout="0" trigger="hover">
<div style="font-size:20px;width:50px;"><i class="el-icon-arrow-down"></i></div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="tabsCloseCurrentHandle">關(guān)閉當(dāng)前標(biāo)簽</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseOtherHandle">關(guān)閉其它標(biāo)簽</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseAllHandle">關(guān)閉全部標(biāo)簽</el-dropdown-item>
<el-dropdown-item @click.native="tabsRefreshCurrentHandle">刷新當(dāng)前標(biāo)簽</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-tab-pane v-for="item in mainTabs"
:key="item.name" :label="item.title" :name="item.name">
<span slot="label"><i :class="item.icon"></i> {{item.title}} </span>
</el-tab-pane>
</el-tabs>
</div>
<!-- 主內(nèi)容區(qū)域 -->
<div class="main-content">
<keep-alive>
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</keep-alive>
</div>
</div>
</template>
<script>
export default {
data () {
return {
}
},
computed: {
mainTabs: {
get () { return this.$store.state.tab.mainTabs },
set (val) { this.$store.commit('updateMainTabs', val) }
},
mainTabsActiveName: {
get () { return this.$store.state.tab.mainTabsActiveName },
set (val) { this.$store.commit('updateMainTabsActiveName', val) }
}
},
methods: {
// tabs, 選中tab
selectedTabHandle (tab) {
tab = this.mainTabs.filter(item => item.name === tab.name)
if (tab.length >= 1) {
this.$router.push({ name: tab[0].name })
}
},
// tabs, 刪除tab
removeTabHandle (tabName) {
this.mainTabs = this.mainTabs.filter(item => item.name !== tabName)
if (this.mainTabs.length >= 1) {
// 當(dāng)前選中tab被刪除
if (tabName === this.mainTabsActiveName) {
this.$router.push({ name: this.mainTabs[this.mainTabs.length - 1].name }, () => {
this.mainTabsActiveName = this.$route.name
})
}
} else {
this.$router.push("/")
}
},
// tabs, 關(guān)閉當(dāng)前
tabsCloseCurrentHandle () {
this.removeTabHandle(this.mainTabsActiveName)
},
// tabs, 關(guān)閉其它
tabsCloseOtherHandle () {
this.mainTabs = this.mainTabs.filter(item => item.name === this.mainTabsActiveName)
},
// tabs, 關(guān)閉全部
tabsCloseAllHandle () {
this.mainTabs = []
this.$router.push("/")
},
// tabs, 刷新當(dāng)前
tabsRefreshCurrentHandle () {
var tempTabName = this.mainTabsActiveName
this.removeTabHandle(tempTabName)
this.$nextTick(() => {
this.$router.push({ name: tempTabName })
})
}
}
}
</script>
<style scoped lang="scss">
.main-container {
padding: 0 5px 5px;
position: absolute;
top: 60px;
left: 1px;
right: 1px;
bottom: 0px;
// background: rgba(56, 5, 114, 0.5);
.tabs {
position: fixed;
top: 60px;
right: 50px;
padding-left: 0px;
padding-right: 2px;
z-index: 1020;
height: 40px;
line-height: 40px;
font-size: 14px;
background: rgb(255, 253, 255);
border-color: rgba(200, 206, 206, 0.5);
// border-left-width: 1px;
// border-left-style: solid;
border-bottom-width: 1px;
border-bottom-style: solid;
}
.tabs-tools {
position: fixed;
top: 60px;
right: 0;
z-index: 1020;
height: 40px;
// padding: 0 10px;
font-size: 14px;
line-height: 40px;
cursor: pointer;
border-color: rgba(200, 206, 206, 0.5);
border-left-width: 1px;
border-left-style: solid;
border-bottom-width: 1px;
border-bottom-style: solid;
background: rgba(255, 255, 255, 1);
}
.tabs-tools:hover {
background: rgba(200, 206, 206, 1);
}
.main-content {
position: absolute;
top: 45px;
left: 5px;
right: 5px;
bottom: 5px;
padding: 5px;
// background: rgba(209, 212, 212, 0.5);
}
}
.position-left {
left: 200px;
}
.position-collapse-left {
left: 65px;
}
</style>