效果

组件是直接封装好的
<template>
<div class="tabPage flex" id="tabPage">
<div class="leftTab cur" @click="leftClick"><i class="el-icon-arrow-left"></i></div>
<div class="tabMain " id="tabItem" style="">
<div class="tabItem" v-for="item in $store.state.tabPageBox" :class="{isActive:item.isActive}" @click="chooseThis(item)">
<span class="yuanTab"></span>
<span class="name">{{item.meta.title}}</span>
<i class="el-icon-close" @click="closeClick(item)" v-if="item.meta.title!='首页'"></i>
</div>
</div>
<div class="rightTab cur" @click="rightClick"><i class="el-icon-arrow-right"></i></div>
<el-dropdown class="closeTab cur" trigger="click" @command="closeAll">
<span class="el-dropdown-link">
<i class="el-icon-circle-close"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="关闭所有">关闭所有</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
<script>
export default {
components: {
},
data() {
return {
plusWidth:0,//记录实际宽度和本该有的宽度的差值 可以滚动的次数
num:300, //滑动距离
ccRight:1, //第几次右滑动
ccLeft:1, //第几次左滑动
};
},
methods: {
// 点击tab 选择
chooseThis(val){
if(val.isActive){
return false;
}
let arr=JSON.parse(JSON.stringify(this.$store.state.tabPageBox));
for(let i in arr){
// 遍历
if(arr[i].meta.title==val.meta.title){ //改变状态
arr[i].isActive=true;
}
}
this.$store.commit("setTabPageBox",arr) ;
this.$router.push(val.path)
},
// 关闭当前
closeClick(val){
let arr=JSON.parse(JSON.stringify(this.$store.state.tabPageBox));
for(let i in arr){
// 遍历
if(arr[i].meta.title==val.meta.title){ //删除数组里的对应的
arr.splice(i,1);
}
}
// 判断当前是否操作的是否是选中状态
if(val.isActive){
arr[0].isActive=true;
this.$store.commit("setTabPageBox",arr) ;
this.$router.push("/")
}else{
this.$store.commit("setTabPageBox",arr) ;
}
},
// 关闭所有
closeAll(val){
if(val=='关闭所有'){
let arr=[{
path: '/index/homePage/homePage',
name: '首页',
isActive:true,
meta: { title: "首页"},
}]
this.$store.commit("setTabPageBox",arr) ;
this.$router.push("/");
// 滑动重置
this.ccRight=1;
document.getElementById('tabItem').style.marginLeft=0;
}
},
// 移动
leftClick(){
// 获取父级总宽度 offsetWidth
let fatherWidth=document.getElementById('tabPage').offsetWidth;
let sonWidth=fatherWidth-88; //88是左右图标的 总宽度
// 获取实际的子级宽度
let curSonWidth=document.getElementById('tabItem').offsetWidth;
// 比较子级的宽度和实际的区别
if(curSonWidth-sonWidth>0){
// 超过了宽度可以滚动
this.plusWidth=Math.ceil((curSonWidth-sonWidth)/this.num); //+100是为了滑动之后右边流出距离 1此滑动300
if(this.ccRight!=1){
document.getElementById('tabItem').style.marginLeft='-'+this.num*(this.ccRight-2)+'px';
this.ccRight--;
}
}
},
// 右边
rightClick(){
// 获取父级总宽度 offsetWidth
let fatherWidth=document.getElementById('tabPage').offsetWidth;
let sonWidth=fatherWidth-88; //88是左右图标的 总宽度
// 获取实际的子级宽度
let curSonWidth=document.getElementById('tabItem').offsetWidth;
// 比较子级的宽度和实际的区别
if(curSonWidth-sonWidth>0){
// 超过了宽度可以滚动
this.plusWidth=Math.ceil((curSonWidth-sonWidth)/this.num); //+100是为了滑动之后右边流出距离 1此滑动300
if(this.plusWidth>=this.ccRight){
document.getElementById('tabItem').style.marginLeft='-'+this.num*this.ccRight+'px';
this.ccRight++;
}
}
}
},
mounted(){
// 监听窗口变化
const that = this
window.onresize = () => {
return (() => {
// 滑动重置
this.ccRight=1;
document.getElementById('tabItem').style.marginLeft=0;
})()
}
},
created(){
if(this.$store.state.tabPageBox.length==0){
let arr=[{
path: '/index/homePage/homePage',
name: '首页',
isActive:true,
meta: { title: "首页"},
}]
this.$store.commit("setTabPageBox",arr)
}
},
watch: {
$route(toPage,from){
let arr=JSON.parse(JSON.stringify(this.$store.state.tabPageBox))
let newOpen=true; //控制是否新打开页面
for(let i in arr){
// 全部取消选中
arr[i].isActive=false;
// 遍历
if(arr[i].meta.title==toPage.meta.title){ //说明此页面已打开过
arr[i].isActive=true;
newOpen=false;
}
}
if(newOpen){
let obj={
path: toPage.path,
name: toPage.name,
isActive:true,
meta: { title: toPage.meta.title},
}
obj.isActive=true;
arr.push(obj)
}
this.$store.commit("setTabPageBox",arr)
},
},
}
</script>
<style lang="scss" scoped>
.tabPage{
height: 40px;
width: 100%;
position: relative;
}
.leftTab{
position: absolute;
left: 0;
top: 1px;
height: 38px;
width: 28px;
text-align: center;
line-height: 38px;
font-size: 18px;
background: #fff;
i{
color:#000
}
&:hover{
i{
color:$blue
}
}
}
.tabMain{
white-space: nowrap;
cursor: pointer;
padding-left: 33px;
margin:5px 0;
transition: margin-left .5s;
.tabItem{
display: inline-block;
margin-right:4px;
padding:0 12px;
height: 30px;
line-height: 30px;
background: #fff;
border-radius: 4px;
}
.el-icon-close{
margin-left:16px
}
.el-icon-close:hover{
cursor: pointer;
color:$blue;
}
.yuanTab{
vertical-align: -1px;
display: inline-block;
width: 12px;
height: 12px;
border-radius: 100px;
background: #E8EAEC;
margin-right: 8px;
}
}
.isActive .yuanTab{
background: $blue;
}
.rightTab{
position: absolute;
right: 33px;
top: 1px;
height: 38px;
width: 28px;
text-align: center;
line-height: 38px;
font-size: 18px;
background: #fff;
i{
color:#000
}
&:hover{
i{
color:$blue
}
}
}
.closeTab{
position: absolute;
right: 0px;
top: 1px;
height: 38px;
width: 32px;
text-align: center;
line-height: 38px;
font-size: 18px;
background: #fff;
i{
color:#000
}
&:hover{
i{
color:$blue
}
}
}
</style>
store的代码
const state={
tabPageBox:[]
}
const mutations={
setTabPageBox(state,tabPageBox){
console.log(tabPageBox)
state.tabPageBox = tabPageBox;
},
}
实现的效果
效果1
1)点击切换
2)关闭标签页
3)右边x关闭所有
4)点击新页面 标签页自动增加,已存在会自动跳转到存在的标签上
效果二
1)左右动画滚动
2)窗口变化 自动重置
本文介绍了一个基于Vue的标签页组件实现,包括点击切换、关闭标签页、右侧关闭所有标签的功能,以及自动增加新页面标签页并跳转到已存在标签的逻辑。组件还实现了左右动画滚动,并能在窗口变化时自动重置。


580






