自己编写vue tabs组件编写

想自己编写标签页组件,点击标签切换显示对应的内容,使用方式如下

<mytabs>
  <mytab-pane label="Notice">
    <notice></notice>
  </mytab-pane>
  <mytab-pane label="Emergency Contact">
    <contact></contact>
  </mytab-pane>
</mytabs>

实现原理

父组件嵌套子组件,子组件内部通过prop属性指定标签名称,通过slot自定义标签要显示的内容
子组件
this.$slots.default 获取slot插入的所有组件vnode
通过vnode.componentOptions.propsData 获取组件内部的props属性
组件内的内容通过v-if绑定data属性控制显示

mytabs.vue

<template>
  <div class="tabs">
    <ul>
      <li
        v-for="(item, index) in labels"
        @click="clickTab(item, index)"
        :key="index"
        :class="[item == currentName ? 'active' : '']"
      >
        {{ item }}
      </li>
    </ul>

    <slot></slot>
  </div>
</template>
<script>
export default {
  name: "mytabs",
  data() {
    return {
      currentName: null,
      panes: [],
      labels: [],
    };
  },
  methods: {
    clickTab(name, index) {
      // debugger;
      //标签名显示选中
      this.currentName = name;
    },
    calcPaneInstances() {
      if (this.$slots.default) {
        debugger;
        const paneSlots = this.$slots.default.filter(
          (vnode) =>
            vnode.tag &&
            vnode.componentOptions &&
            vnode.componentOptions.Ctor.options.name === "mytabPane"
        );
        const panes = paneSlots.map(
          ({ componentInstance }) => componentInstance
        );
        //标签切换 currentName set也会触发updated,所以需要判断slots是不是真的变化了
        const panesChanged = !(
          panes.length === this.panes.length &&
          panes.every((pane, index) => pane === this.panes[index])
        );
        if (panesChanged) {
          this.panes = panes;
        }
      } else if (this.panes.length !== 0) {
        debugger;
        this.panes = [];
      }
      debugger;
    },
  },
  //收集子组件mytabPane的label,不能写在created
  mounted() {
    debugger;
    this.calcPaneInstances();
  },
  updated() {
    // 初次加载的时候
    //页面刚开始的时候还没有获取到用户权限信息,用户权限信息加载完后slot会变化
    debugger;
    //currentName set也会触发updated
    this.calcPaneInstances();
  },
  watch: {
    //页面刚开始的时候还没有获取到用户权限信息,用户权限信息加载完后slot会变化
    panes(newVal, oldVal) {
      debugger;
      this.labels = newVal.map((t) => t.label);
      if (this.labels.length > 0) {
        const contained = this.labels.some((t) => t === this.currentName);
        if (!contained) {
          this.currentName = this.labels[0];
        }
      } else {
        this.currentName = null;
      }
    },
  },
};
</script>
<style lang="scss" scoped>
.tabs {
  ul {
    margin-bottom: 10px;
    li {
      display: inline-block;
      margin: 0 10px;
      padding: 5px 0;
      color: rgb(182, 182, 182);
      cursor: pointer;
    }
    .active {
      border-bottom: 2px solid #931c7f;
      color: rgb(10, 1, 1);
    }
  }
}
</style>

mytabPan.vue

<template>
  <div v-show="active">
    <slot></slot>
  </div>
</template>
<script>
export default {
  name: "mytabPane",
  data() {
    return {
      flag: false,
    };
  },
  props: {
    label: {
      type: String,
      default: null,
    },
    value: {
      type: String,
      default: null,
    },
  },
  computed: {
    active() {
      const active = this.$parent.currentName === this.label;
      return active;
    },
  },
};
</script>
<style scoped>
</style>

使用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值