Skip to content

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

邮箱: open_teams@163.com