vue笔记(八): tabs标签页组件
tabs选项页,这个组件完全是看了elementui之后想模仿才写的
思路
首先获取自定子组件的检测是否有配置约定好的class名,如果有则再次检测是否设置有label的属性值,如果有则获取label的值当做对应的tabs标签名,并同时在当前的子节点上设置自定义的data-tabs-item属性,值为label值,后续显示和隐藏就可以根据标签名字和data-tabs-item的值来控制
自定义参数
type:类型值,默认default,就是上图看到的,还有一个card,是卡片样式
新建tabs.vue
js
<template>
<div class="tabs">
<!-- 默认样式 -->
<ul class="tabs-list tabs-default" v-if=" type == 'default'">
<li v-for="(item,index) in tabs" :class="[options == index ? 'default-active' : '' ]"
@click="onTabs(item,index)"
>{{item}}</li>
</ul>
<!-- 卡片样式 -->
<ul class="tabs-list tabs-card" v-if="type == 'card'">
<li v-for="(item,index) in tabs"
:class="[options == index ? 'card-active' : '',
index == 0 ? 'first':'',index == tabs.length-1 ? 'last':'' ]"
@click="onTabs(item,index)">
<span>{{item}}</span>
<i v-if="options == index" class="bi bi-x-lg"></i>
</li>
</ul>
<!-- 子组件容器div -->
<div class="tabs-content" ref="opentabs">
<slot></slot>
</div>
</div>
</template>
<script>
// 循环节点的函数
import {eachNodesClass} from '../common/utils.js'
export default{
name: 'Tabs',
props:{
type: {
type: String,
default: 'default'
}
},
data(){
return{
tabs: [],//标签名对象
options: 0,//选中标签的下标
cel: ''
}
},
mounted () {
// 获取当前元素对象
let el = this.$refs.opentabs;
//获取子节点集合
this.cel = el.childNodes;
// tabs标签集合临时数组
let arr = [];
//标识
let mark = false;
eachNodesClass(this.cel,(className,node)=>{
//检查子组件calss名是否包含约定的class名
if(className.includes('tabs-item')){
//获取label值
let label = node.getAttribute('label');
//设置data-值
node.setAttribute('data-tabs-item',label);
//添加到标签集合临时数组
arr.push(label);
//默认显示第一个
if(!mark){
node.style.display = "block";
// 显示完第一个就设置为ture
mark = true;
}else{
//其他子节点就隐藏
node.style.display = "none";
}
}else{
// 如果没有约定的calss,则默认不显示组件内容
node.style.display = "none"
}
})
//最后tabs标签临时数组赋值给tabs
this.tabs = arr;
},
methods:{
onTabs(label,index){
this.options = index;
eachNodesClass(this.cel,(className,node)=>{
if(className.includes('tabs-item')){
let dataset = node.dataset.tabsItem;
if(label == dataset){
node.style.display = "block";
}else{
node.style.display = "none";
}
}else{
//如果没有约定的calss,则默认不显示组件内容
node.style.display = "none"
}
})
}
}
}
</script>
<style scoped>
.tabs{
width: 100% ;
color: #333333;
font-size: 14px;
}
.tabs-list{
display: flex;
}
.tabs-list li{
padding: 8px 18px;
cursor: pointer;
}
/*默认风格*/
.default-active{
color: #1890ff;
border-bottom: 2px #1890ff solid;
}
/*卡片风格*/
.tabs-card{
border-bottom: 1px #e4e7ed solid;
}
.first{
border-top-left-radius: 5px;
border-left: 1px #e4e7ed solid;
}
.last{
border-top-right-radius: 5px;
}
.tabs-card li{
border-right: 1px #e4e7ed solid ;
border-top: 1px #e4e7ed solid ;
}
.tabs-card li i{
font-size: 10px;
margin-left: 6px;
}
.tabs-card li i:hover{
color: red;
}
.card-active{
color: #1890ff;
}
.tabs-content{
width: 100%;
box-sizing: border-box;
padding: 10px;
}
</style>
<template>
<div class="tabs">
<!-- 默认样式 -->
<ul class="tabs-list tabs-default" v-if=" type == 'default'">
<li v-for="(item,index) in tabs" :class="[options == index ? 'default-active' : '' ]"
@click="onTabs(item,index)"
>{{item}}</li>
</ul>
<!-- 卡片样式 -->
<ul class="tabs-list tabs-card" v-if="type == 'card'">
<li v-for="(item,index) in tabs"
:class="[options == index ? 'card-active' : '',
index == 0 ? 'first':'',index == tabs.length-1 ? 'last':'' ]"
@click="onTabs(item,index)">
<span>{{item}}</span>
<i v-if="options == index" class="bi bi-x-lg"></i>
</li>
</ul>
<!-- 子组件容器div -->
<div class="tabs-content" ref="opentabs">
<slot></slot>
</div>
</div>
</template>
<script>
// 循环节点的函数
import {eachNodesClass} from '../common/utils.js'
export default{
name: 'Tabs',
props:{
type: {
type: String,
default: 'default'
}
},
data(){
return{
tabs: [],//标签名对象
options: 0,//选中标签的下标
cel: ''
}
},
mounted () {
// 获取当前元素对象
let el = this.$refs.opentabs;
//获取子节点集合
this.cel = el.childNodes;
// tabs标签集合临时数组
let arr = [];
//标识
let mark = false;
eachNodesClass(this.cel,(className,node)=>{
//检查子组件calss名是否包含约定的class名
if(className.includes('tabs-item')){
//获取label值
let label = node.getAttribute('label');
//设置data-值
node.setAttribute('data-tabs-item',label);
//添加到标签集合临时数组
arr.push(label);
//默认显示第一个
if(!mark){
node.style.display = "block";
// 显示完第一个就设置为ture
mark = true;
}else{
//其他子节点就隐藏
node.style.display = "none";
}
}else{
// 如果没有约定的calss,则默认不显示组件内容
node.style.display = "none"
}
})
//最后tabs标签临时数组赋值给tabs
this.tabs = arr;
},
methods:{
onTabs(label,index){
this.options = index;
eachNodesClass(this.cel,(className,node)=>{
if(className.includes('tabs-item')){
let dataset = node.dataset.tabsItem;
if(label == dataset){
node.style.display = "block";
}else{
node.style.display = "none";
}
}else{
//如果没有约定的calss,则默认不显示组件内容
node.style.display = "none"
}
})
}
}
}
</script>
<style scoped>
.tabs{
width: 100% ;
color: #333333;
font-size: 14px;
}
.tabs-list{
display: flex;
}
.tabs-list li{
padding: 8px 18px;
cursor: pointer;
}
/*默认风格*/
.default-active{
color: #1890ff;
border-bottom: 2px #1890ff solid;
}
/*卡片风格*/
.tabs-card{
border-bottom: 1px #e4e7ed solid;
}
.first{
border-top-left-radius: 5px;
border-left: 1px #e4e7ed solid;
}
.last{
border-top-right-radius: 5px;
}
.tabs-card li{
border-right: 1px #e4e7ed solid ;
border-top: 1px #e4e7ed solid ;
}
.tabs-card li i{
font-size: 10px;
margin-left: 6px;
}
.tabs-card li i:hover{
color: red;
}
.card-active{
color: #1890ff;
}
.tabs-content{
width: 100%;
box-sizing: border-box;
padding: 10px;
}
</style>
调用
最后一个子节点div因为没有配置class,所以没有显示
js
<tabs>
<div class="tabs-item" label="选项一">
<div>牛逼plus</div>
<div>牛逼plus</div>
<div>牛逼plus</div>
<div>牛逼plus</div>
</div>
<div class="tabs-item" label="选项二">喝水</div>
<div class="tabs-item" label="选项三">跑步</div>
//没有配置class的节点,不符合约定,默认不显示
<div class="" label="选项三">跑步</div>
</tabs>
<tabs>
<div class="tabs-item" label="选项一">
<div>牛逼plus</div>
<div>牛逼plus</div>
<div>牛逼plus</div>
<div>牛逼plus</div>
</div>
<div class="tabs-item" label="选项二">喝水</div>
<div class="tabs-item" label="选项三">跑步</div>
//没有配置class的节点,不符合约定,默认不显示
<div class="" label="选项三">跑步</div>
</tabs>
运行结果
这次用卡片类型的,默认会显示边框,没有下划线
扩展部分
上面card类型,标签名旁边有删除图标,是为了做后台管理系统使用,不过还没有写删除方法,如果有需要自己动手,把tabs数组删除对应的标签名就行了
不过做后台管理系统多标签时,子组件都是动态添加,除了删除标签名,也记得删除对应的组件
本文到此结束。
反馈信息
INFO