Tabs组件封装
封装到tabs组件,最简单的方式肯定是写几个div,内容和导航栏写在一起。例如
<div :class="{'active':index===1}">1</div>
<div :class="{'active':index===2}">2</div>
<div :class="{'active':index===3}">3</div>
<div :class="{'active':index===4}">4</div>
但这样,逻辑代码和业务逻辑就没有彻底分开。因此,实现tab栏的最佳逻辑就是逻辑代码和业务逻辑相隔离。采用Element-plus的那种方法:
<el-tabs type="border-card">
<el-tab-pane label="用户管理">用户管理</el-tab-pane>
<el-tab-pane label="配置管理">配置管理</el-tab-pane>
<el-tab-pane label="角色管理">角色管理</el-tab-pane>
<el-tab-pane label="定时任务补偿">定时任务补偿</el-tab-pane>
</el-tabs>
下面来自己手动封装一个.
该组件封装的几个关键点:
provide
和inject
。至于为什么不用props传递,因为props会导致数据出现响应式丢失。- useSlots获取到所有插槽的内容信息。
Tabs
组件
<template>
<!-- 我是tabs页面 -->
<div class="tabsShow">
<div
class="item"
v-for="(item, index) in childrenProps"
:key="index"
@click="handleSelectTab(item.name)"
:class="[{ active: active === item.name }]"
>
{{ item.name }}
</div>
</div>
<div class="slot"><slot></slot></div>
</template>
<script setup>
import { useSlots, onMounted, ref, provide } from "vue";
const props = defineProps(["modelValue"]);
const emit = defineEmits(["handleClick"]);
const slots = useSlots();
const childrenProps = ref([]); // 子组件的标题
const active = ref(""); // 下标
provide("active", active);
const handleSelectTab = (name) => {
active.value = name;
emit("handleClick", active.value);
};
onMounted(() => {
console.log(slots.default());
slots.default().forEach((item, index) => {
childrenProps.value.push(item.props);
});
active.value = slots.default()[0].props.name;
console.log(childrenProps.value);
});
console.log(props);
</script>
<style lang="scss" scoped>
div {
box-sizing: border-box;
}
.tabsShow {
cursor: pointer;
display: flex;
height: 50px;
border: 1px solid rgba($color: #000000, $alpha: 0.1);
justify-content: flex-start;
.item {
border-left: 1px solid rgba(0, 0, 0, 0.1);
border-right: 1px solid rgba($color: #000000, $alpha: 0.1);
padding: 5px 10px;
height: 100%;
width: fit-content;
display: flex;
justify-content: center;
align-items: center;
}
.active {
background-color: rgba(0, 0, 0, 0.15);
}
}
.slot {
}
</style>
pane
组件
<template>
<div v-if="activeName == name"><slot></slot></div>
</template>
<script setup>
import { inject, defineProps, ref, watch } from "vue";
const activeName = inject("active");
console.log(activeName);
const props = defineProps(["label", "name"]);
</script>
<style lang="scss" scoped></style>
对此,基本的功能已经完成。如果有样式的需求,可以继续在上面进行封装。