自己编写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>
使用
还没有评论,来说两句吧...