Vue 标签页组件

by deniro

HTML

<div id="app" v-cloak>
    <tabs v-model="activeIndex">
        <pane label="科技" closable="true">
            火星疑似发现“外星人墓地”?至今无法解释
        </pane>
        <pane label="体育">
            全美沸腾!湖人队4年1.2亿迎顶级后卫,詹姆斯:有他就能夺冠
        </pane>
        <pane label="娱乐" closable="true">
            阿米尔汗谈中国武侠 想拍印度版《鹿鼎记》
        </pane>
    </tabs>
</div>

CSS

[v-cloak] {
    display: none;
}

.tabs {
    font-size: 14px;
    color: #657180;
}

.tabs-bar:after {
    content: '';
    display: block;
    width: 100%;
    height: 1px;
    background: #d7dde4;
    margin-top: -1px;
}

.tabs-tab {
    display: inline-block;
    padding: 4px 16px;
    margin-right: 6px;
    background: #fff;
    border: 1px solid #d7dde4;
    cursor: pointer;
    position: relative;
}

.tabs-tab:hover {
    color: #336699;
    font-weight: bolder;
}

.tabs-tab-active {
    color: #336699;
    border-top: 1px solid #336699;
    border-bottom: 1px solid #fff;
    transform:translateY(-1px);
    transition: transform 0.5s;
}

.tabs-tab-active:before {
    content: '';
    display: block;
    height: 1px;
    background: #3399ff;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
}

.tabs_content {
    padding: 8px 0;
}

.pane {
    margin-top: 26px;
    font-size: 16px;
    line-height: 24px;
    color: #333;
    text-align: justify;
}

.close{
    color: #FF6666;
}
.close::before {
    content: "\2716";
}

.close:hover {
    color: #990033;
    font-weight: bolder;
}

Vue

/**
 * 子窗口组件
 */
Vue.component('pane', {
    name: 'pane',
    template: '\
    <div class="pane" v-show="isShow">\
        <slot></slot>\
    </div>\
    ',
    props: {
        //标题
        label: {
            type: String,
            default: ''
        },
        //是否可关闭
        closable: {
            type: Boolean,
            default: false
        }
    },
    data: function () {
        return {
            //显示或隐藏
            isShow: true
        }
    },
    methods: {
        //通知父组件,更新标题
        init() {
            this.$parent.init();
        }
    },
    watch: {
        //当 label 值发生变化时,更新标题
        label() {
            this.init();
        }
    },
    //挂载时,更新标题
    mounted() {
        this.init();
    }
});

/**
 * 标签页组件
 */
Vue.component('tabs', {
    template: '\
    <div class="tabs">\
        <div class="tabs-bar">\
            <!-- 标签页标题-->\
            <div :class="tabClass(item)"\
                v-for="(item, index) in titleList"\
                @click="change(index)">\
                {{ item.label }}\
                <span v-if="item.closable" class="close" @click="close(index,item.name)"></span>\
                </div>\
            </div>\
            <div class="tabs-content">\
             <!-- pane 组件位置-->\
                <slot></slot>\
            </div>\
           </div>',
    props: {
        value: {
            type: [String, Number]
        }
    },
    data: function () {
        return {
            currentIndex: this.value,
            titleList: []//存放标题
        }
    },
    methods: {
        //设置样式
        tabClass: function (item) {
            return ['tabs-tab', {
                //为当前选中的 tab 添加选中样式
                'tabs-tab-active': (item.name === this.currentIndex)
            }]

        },
        //获取定义的所有 pane 组件
        getTabs() {
            return this.$children.filter(function (item) {
                return (item.$options.name === 'pane');
            })
        },
        //更新 pane 是否显示状态
       ...