Vue完成标签页组件

很多组件库都有类似组件,但是我觉得对于前端来说,不只是只会使用组件而已。
完成效果
在这里插入图片描述

需要文件
Vuejs_actual_tabs.vue :标签页切换头
Vuejs_actual_pane.vue: 标签页显示内容
style.css
moduleApply.vue : 使用页面

Vuejs_actual_tabs.vue

<template>
  <div class="tabs">
    <div class="tabs-bar">
      <div
        :class="tabCls(item)"
        v-for="(item, index) in navList"
        :key="index"
        @click="handleChange(index)">
        {{ item.label }}
      </div>
    </div>
    <div class="tabs-content">
      <!-- 这里的slot就是嵌套的pane -->
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  // name:"tabs",
  props: {
    value: {
      type: [String, Number]
    }
  },
  data() {
    return {
      // 因为不能修改value,所以复制一份自己维护
      currentValue:this.value,
      // 用于渲染 tabs的标题
      navList:[],
    }
  },
  methods:{
    tabCls(item){
      return [
        'tabs-tab',
        {
          // 给当前选中的tab加一个calss
          'tabs-tab-active': item.name === this.currentValue
        }
      ]
    },
    getTabs(){
      return this.$children.filter((item) => {  // 通过遍历子组件,得到所有的pane组件
        return item.$options.name === 'pane'
      })
    },
    updateNav(){
      this.navList = []
      // 设置对this的引用,在function回调里,this指向的并不是vue实例
      var _this = this
      this.getTabs().forEach((pane, index) => {
        _this.navList.push({
          label: pane.label,
          name: pane.name || index
        })
        // 如果没有给pane设置name,默认设置它的索引
        if (!pane.name) pane.name = index 
        // 设置当前选中的tab的索引,在后面介绍
        if (index === 0) {
          if (!_this.currentValue) {
            _this.currentValue = pane.name || index
          }
        }
      })
      this.updateStatus()
    },
    updateStatus(){
      var tabs = this.getTabs()
      var _this = this
      // 显示当前选中的tab对应的pane组件,隐藏没有选中的
      tabs.forEach((tab) => {
        return tab.show = tab.name === _this.currentValue
      })
    },
    handleChange(index){
      console.log('处罚【hanChange');
      var nav = this.navList[index]
      var name = nav.name
      // 改变当前选中的tab,并出发下面的wath
      this.currentValue = name
      // 更新 value
      this.$emit('input', name);
      // 触发一个自定义事件,供父级使用
      this.$emit('on-click', name)
    },
  },
  watch:{
    value:function (val) {
      this.currentValue = val
    },
    currentValue:function (){
      // 在当前选中的tab发生变化时,更新pane的显示状态
      this.updateStatus()
    }
  }
}
</script>

<style lang="less">
</style>

Vuejs_actual_pane.vue

<template>
  <div class="pane" v-show="show">
      <slot></slot>
  </div>
</template>

<script>
export default {
    name:"pane",    // 不加这个name标签tab展示不出来
    props:{
        name:{
            type:String
        },
        label:{
            type:String,
            default:''
        }
    },
    data() {
        return {
            show:true
        }
    },
    methods:{
        updateNav() {
            this.$parent.updateNav()
        },
    },
    watch:{
        label(){
            this.updateNav()
        }
    },
    mounted(){
        this.updateNav()
    }
}
</script>

<style>
</style>

moduleApply.vue

<template>
    <div id="app" v-cloak>
		<tabs v-model="activeKey">
            <pane label="标签一" name="1">
                标签一的内容
            </pane>
            <pane label="标签二" name="2">
                标签二的内容
            </pane>
            <pane label="标签三" name="3">
                标签三的内容
            </pane>
        </tabs>
    </div>
</template>

<script>
import inpNumber from '@/components/Vuejs_actual_input.vue'
import tabs from '@/components/Vuejs_actual_tabs.vue'
import pane from '@/components/Vuejs_actual_pane.vue'
import '../assets/style.css'
export default {
    // name:'moduleApply',
    components:{
        inpNumber,
        tabs,
        pane
    },
    data() {
        return {
            activeKey:'1'
        }
    },
}
</script>

<style lang="less">
</style>

style.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-active{
    color: #3399ff;
    border-top: 1px solid #3399ff;
    border-bottom:1px solid #fff
}
.tabs-tab-active:before{  
    content: '';
    display: block;
    height: 1px;
    background: #3399ff;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
}
.tabs-content{
    padding: 8px 0;
}

追加一个可关闭按钮功能+内容滑动动画,有点迷,搜索中发现一篇完整的

掘金:https://juejin.cn/post/6844903881965568007

我使用transform动画效果没出来,最后使用了@keyframes

@keyframes example {	// 创建动画并命名
    0% {padding-left: 100%;}
    100% {padding-left: 0px;}
}

.pane {
    width: 100%;
    height: 100px;
    animation-name: example;	// 使用动画
    animation-duration: .5s;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值