前言
这到了现在,基本算是已经完成的差不多了。 就还剩下关闭的实现,以及页面的缓存。
关于关闭的实现,因为我们项目没说使用这个组件,所以我就不写了。
页面的缓存,现在是没有生效,再慢慢研究。
最后:不建议使用我这个组件,原因呢,是因为前面写左侧菜单栏的时候,封装的数据结构问题,导致耦合度太高了。所以不推荐使用。以后有机会了,再写吧。
最后,贴上代码,以后说不定会用到。每块代码的作用就不单独写了,全部删了,因为逻辑有点多。
参考文档
- https://blog.csdn.net/keovady/article/details/89813582
很久之前做过一次这种东西,但是后来因为一点样式上的小问题,就删了
今天看到这个tagsView
,想着用一下,结果不太兼容。
准备自己做一个试试
实现代码-第一个版本
实现代码-第二个版本
实现代码-最终版本
源码(如果使用,则需要根据代码,补充相对应的vuex里面的信息)
<template>
<div class="tags-view">
<el-tabs v-model="activeName" type="card" @tab-click="_clickTag">
<el-tab-pane :name="item.url" v-for="(item, index) in publicInfo.tagsView" :key="item.url">
<span slot="label">
<i class="isCurrent" :class="item.isActive ? 'true' : ''"></i>
{{ item.title }}
<i class="fa fa-close light" @click="_delTag(index)"></i>
</span>
<el-card class="box-card">
<keep-alive v-if="$route.meta.keepAlive">
<router-view v-if="item.url===activeName"></router-view>
</keep-alive>
<router-view v-else-if="item.url===activeName"></router-view>
</el-card>
</el-tab-pane>
</el-tabs>
<ul class="contextmenu" v-show=" visible" :style="{ left: left + 'px', top: top + 'px' }">
<li @click="_clickClose($event, 1)">关闭当前</li>
<li @click="_clickClose($event, 2)">关闭其他</li>
<li @click="_clickClose($event, 3)">关闭所有</li>
</ul>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
data() {
return {
isis: false,
top: 0,
left: 0,
visible: false,
activeName: this.$store.state.StoreModuleBase.publicInfo.defaultActive
}
},
props: {},
components: {},
computed: {
...mapState('StoreModuleBase', ['publicInfo']),
overloadHandle() {
return this.publicInfo.tagsView
}
},
created() { },
onload() {
this._addContextmenu()
},
onShow() {
this._addContextmenu()
},
mounted() {
this._addContextmenu()
},
watch: {
overloadHandle(enwVal, oldVal) {
this._addContextmenu()
}
},
methods: {
...mapActions('StoreModuleBase', ['setPublicInfo']),
_clickClose(e, index) {
let msg = '也不用,就不做了'
// 关闭逻辑待补充
if (index === 1) {
console.log(msg)
} else if (index === 2) {
console.log(msg)
} else if (index === 3) {
console.log(msg)
}
// let delIndex = -1
// this._delTag(delIndex, e)
this.visible = false
},
_addOnBlur(dom) {
// 添加失去焦点事件(如果当前元素或者其父元素包含xx类,则添加失去焦点事件)
let that = this
if (dom) {
if (that.$jsUtil.hasClassFun(dom, 'el-tabs__item')) {
} else if (that.$jsUtil.hasClassFun(dom.parent(), 'el-tabs__item')) {
dom = dom.parent()
}
dom.addEventListener('blur', function (e) {
e.preventDefault()
that._onBlur()
})
}
},
_optTag(e, dom) {
this.visible = true
const offsetLeft = this.$el.getBoundingClientRect().left
this.left = e.clientX - offsetLeft + 230
this.top = e.clientY
this._addOnBlur(dom)
},
// 获取vuex
_getPublic() {
let _publicInfo = JSON.parse(JSON.stringify(this.publicInfo))
return _publicInfo
},
// 删除菜单标签
_delTag(delIndex, e) {
let _publicInfo = this._getPublic()
if (
_publicInfo &&
_publicInfo.tagsView &&
_publicInfo.tagsView.length > 1
) {
// 首先,获取关闭的标签信息
let currentIsSelected = _publicInfo.tagsView[delIndex].isActive
let currentIsFirst = delIndex === 0
// 然后,删除
_publicInfo.tagsView.splice(delIndex, 1)
let newIndex
if (currentIsSelected) {
if (currentIsFirst) {
newIndex = 0
} else {
newIndex = delIndex - 1
}
} else {
newIndex = null
}
// 接着,更新选中信息
if (newIndex != null) {
_publicInfo.activeNameTab = _publicInfo.tagsView[newIndex].url
_publicInfo.defaultActive = _publicInfo.tagsView[newIndex].url
_publicInfo.tagsView.forEach((item, index) => {
if (index === newIndex) {
item.isActive = true
} else {
item.isActive = false
}
})
}
// 最后,跳转到当前新的的地址,以及更新tab值
this.activeName = _publicInfo.activeNameTab
this.$router.push({ path: _publicInfo.defaultActive })
// 更新vuex
this.setPublicInfo(_publicInfo)
window.event ? (window.event.cancelBubble = true) : e.stopPropagation()
}
},
_onBlur() {
this.visible = false
},
// 点击标签
_clickTag(tab, event) {
// 首先,获取到被点击标签的标题和地址
let url = tab.name
// 然后,获取public
let _publicInfo = this._getPublic()
// 接着,更新vuex里面菜单默认选中的信息
_publicInfo.defaultActive = url
_publicInfo.activeNameTab = url
_publicInfo.tagsView.forEach(element => {
if (element.url === url) {
element.isActive = true
} else {
element.isActive = false
}
})
this.setPublicInfo(_publicInfo)
this.activeName = _publicInfo.activeNameTab
// 最后,跳转到当前选中的地址
this.$router.push({ path: url })
},
_addContextmenu() {
let that = this
let elTabsItems = document
.getElementsByClassName('tags-view')[0]
.getElementsByClassName('el-tabs__item')
if (elTabsItems && elTabsItems.length > 0) {
elTabsItems = [].slice.call(elTabsItems)
elTabsItems.forEach(item => {
// 添加右键事件
item.addEventListener('contextmenu', function (e) {
e.preventDefault()
that._optTag(e, item)
})
})
}
}
}
}
</script>
<style lang="less">
.tags-view {
.el-tabs {
background-color: #ffffff;
margin: 0;
padding: 0;
.el-tabs__header {
margin: 0;
.el-tabs__nav-prev,
.el-tabs__nav-next {
line-height: 34px;
}
.el-tabs__nav {
border-radius: 0;
height: 34px;
border-top-color: #ffffff;
.el-tabs__item {
height: 34px;
line-height: 34px;
color: #999;
&.is-active {
border-bottom-color: #eee;
vertical-align: 1px;
}
&:hover {
color: rgba(73, 80, 96, 1);
.fa.fa-close {
color: red;
-webkit-transition: all 0.5s;
transition: all 0.5s;
}
}
span {
.isCurrent {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
vertical-align: 2px;
&.true {
background-color: #409eff;
}
}
.fa {
&.fa-close {
border-radius: 50%;
font-size: 10px;
width: 12px;
height: 12px;
text-align: center;
line-height: 12px;
// 图标变细
-webkit-text-stroke: 1px #fff;
-moz-text-stroke: 1px #fff;
-o-text-stroke: 1px#fff;
text-stroke: 1px #fff;
z-index: 999;
color: rgba(0, 0, 0, 0);
&:hover {
background-color: #d0c8c8;
}
}
}
}
}
}
}
.el-tabs__content {
.el-card {
margin: 5px 8px !important;
}
}
}
}
.contextmenu {
margin: 0;
background: #fff;
z-index: 100;
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, 0.3);
li {
margin: 0;
padding: 7px 16px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
</style>
vuex信息
vuex的结构我是做成了模块化
store-module-base.js
export default {
// 增加命名空间
namespaced: 'StoreModuleBase',
// 初始化状态值--一定要有该属性对象
state: {
// 系统信息
publicInfo: {
// 标签页
tagsView: [],
// 菜单开关状态
menuSwitchState: false,
// 当前选中的地址
activeNameTab: ''
}
},
// 自定义改变state初始值的方法--一定要有该属性对象
mutations: {
SETPUBLICINFO(state, param) {
state.publicInfo = param
},
SETMENUSWITCHSTATE(state, param) {
state.publicInfo.menuSwitchState = param
}
},
// 状态计算属性
getters: {},
// 异步操作状态
actions: {
setPublicInfo({ commit }, param) {
commit('SETPUBLICINFO', param)
},
clearPublicInfo({ commit, state }) {
commit('SETPUBLICINFO', {})
}
}
}
index.js
// 引入Vue、和Vuex(固定写法)
import Vue from 'vue'
import Vuex from 'vuex'
// 引入persistedState
import persistedState from 'vuex-persistedstate'
// module
import StoreModuleBase from './modules/store-module-base'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
// 数据持久化,默认localStorage 参考:https://www.jianshu.com/p/c22861ec5f21
plugins: [
persistedState({
storage: window.sessionStorage
})
],
modules: {
StoreModuleBase
},
strict: debug
})