vue自定义Tab组件

2019-06-14  本文已影响0人  漫漫江雪

一个普通的tab标签大致的结构通常是如此:

<div class="tabs-container">
    <ul class="tabs">
        <li class="active">Tab1</li>
        <li>Tab2</li>
        <li>Tab3</li>
    </ul>
    <div class="tab-content">
        被激活标签项的内容
    </div>
</div>

转换成vue组件,想象中的样子

<tabs class="tabs-container">
    <tab class="active" :label="'Tab1'">Tab1内容</tab>
    <tab :label="'Tab2'">Tab2内容</tab>
    <tab :label="'Tab3'">Tab3内容</tab>
</tabs>

1. 外层 tabs容器组件 tabs.vue

<template>
  <div class="tab-container">
    <ul class="tab-head">
      <slot></slot>
    </ul>
    <content-container :panels="panels" />
  </div>
</template>

<script>
  import ContentContainer from './content-container.vue' // 用于展示tab内容的组件
  export default {
    name: 'tabs',
    data() {
      return {
        panels: [] // 子组件mounted的时候将自己push到该数组中
      }
    },
    props: {
      value: {
        type: [String, Number],
        required: true
      }
    },
    methods: {
      tabChange(index) {
        this.$emit('tabChange',index)
      }
    },
    components: {
      ContentContainer
    }
  }
</script>

<style scoped>
.tab-container{
  width: 500px;  
}
.tab-head {
  padding: 0 15px;
  border-bottom: 2px solid #ccc;
  font-size: 0;
}
</style>

2. 单个标签项 tab组件 (用了render function) tab.vue

<script>
  export default {
    name: 'tab',
    props: {
      index: {
        type: [String,Number],
        required: true
      },
      label: {
        type: String
      }
    },
    mounted() {
      this.$parent.panels.push(this)
    },
    render(h)  {
      const tab = this.$slots.label || h('span',this.label)  // 如果用name=label的slot,则用slot,否则展示label prop
      return h('li', {
        class: this.classes,
        on: {
          click: this.activeCurTab
        }
      },[ tab ])
    },
    computed: {
      active() {
        return this.index ===this.$parent.value
      },
      classes() {
        return ['tab', this.active? 'active': '']
      } 
    },
    methods: {
      activeCurTab() {
        this.$parent.tabChange(this.index)
      }
    }
  }
</script>

<style lang="scss" scoped>
.tab {
  display: inline-block;
  font-size: 14px;
  padding: 0 10px;
  height: 40px;
  line-height: 38px;
  position: relative;
  bottom: -2px;
  cursor: pointer;
  margin: 0 5px;
  border-bottom: 2px solid rgba(255,255,255,0);
  &.active {
    color: #f60;
    border-bottom: 2px solid #f60;
  }
}
</style>

3. tab内容展示容器组件 content-container.vue

<script>
  export default {
    props: {
      panels: {
        type: Array,
        default: function() {
          return []
        }
      }
    },
    render(h) {
      const contents = this.panels.map(tab => {
        return tab.active? tab.$slots.default: null
      })
      return h('div', {
        class: 'tab-content'
      }, contents)
    }
  }
</script>

<style scoped>
.tab-content {
  margin-top: 8px;
  border: 1px solid #ccc;
  padding: 15px;
  min-height: 100px;
}
</style>

4. 注册为全局组件 (和上面组件同目录的index.js)

import tabs from './tabs.vue'
import tab from './tab.vue'

export default (Vue) => {
  Vue.component(tabs.name, tabs)
  Vue.component(tab.name, tab)
}

接着要在main.js中引入并use一下

import tabs from './components/tabs'
Vue.use(tabs)

5. 使用组件

<template>
  <div class="cus-tab">
    <tabs :value="curTab" @tabChange="tabChange">
      <tab :index="0" :label="'Tab1'">
        <div>
          Tab1的内容
          <notify :content="'自定义组件测试'"></notify>
        </div>
      </tab>
      <tab :index="1" :label="'Tab2'">
        <span slot="label">我是tab2</span>
        <div>Tab2的内容</div>
      </tab>
      <tab :index="2" :label="'Tab3'">
        <div>Tab3的内容</div>
      </tab>
    </tabs>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        curTab: 0 // 当前激活的tab索引
      }
    },
    methods: {
      tabChange(index) {
        this.curTab = index
      }
    }
  }
</script>

6. 效果

tab-component.gif
上一篇 下一篇

猜你喜欢

热点阅读