前言
本文示例是官方的 vite 脚手架
新建文件,例:src/components/tabs/Tabs.vue
provide 传递对象数据会失去响应式,使用 toRef 可以保持响应式
<template>
<div class="custom-tabs">
<ul class="tabs-nav">
<li
v-for="(item, index) in tabs"
:key="index"
class="nav-item"
:class="`${item?.props?.name === modelValue ? 'nav-active-item' : ''}`"
@click="handleClick(item?.props?.name, $event)"
>
{{ item?.props?.label }}
</li>
</ul>
<div class="tabs-content">
<slot></slot>
</div>
</div>
</template>
<script setup>
import { provide, toRef, useSlots, reactive, onMounted } from 'vue';
const emits = defineEmits(['update:modelValue', 'tabClick']);
const props = defineProps({
modelValue: {
type: String,
default: ''
}
});
provide('modelValue', toRef(props, 'modelValue'));
const slots = useSlots();
const tabs = reactive([]);
const calcTabItemInstances = () => {
if (!slots.default) {
return;
}
slots.default().forEach(item => {
tabs.push(item);
});
};
onMounted(() => {
calcTabItemInstances();
});
const handleClick = (name, event) => {
if (!props.modelValue || props.modelValue === name) {
return;
}
emits('update:modelValue', name);
emits('tabClick', name, event);
};
</script>
<style scoped>
.tabs-nav {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.nav-item {
line-height: 40px;
margin-right: 22px;
font-size: 16px;
color: #606266;
cursor: pointer;
}
.nav-item:hover {
color: #409eff;
}
.nav-active-item {
position: relative;
color: #409eff;
}
.nav-active-item::after {
content: '';
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 33px;
height: 3px;
background-color: #386fff;
border-radius: 4px;
}
.tabs-content {
padding-top: 22px;
}
</style>
新建文件,例:src/components/tabs/TabPane.vue
<template>
<div v-show="name === modeleValue" class="tab-pane">
<slot></slot>
</div>
</template>
<script setup>
import { inject } from 'vue';
defineProps({
label: {
type: String,
default: '',
required: true
},
name: {
type: String,
default: '',
required: true
}
});
const modeleValue = inject('modelValue');
</script>
组件使用
<template>
<Tabs v-model="activeName" @tab-click="handleClick">
<TabPane label="基础信息" name="first">基础信息</TabPane>
<TabPane label="策略信息" name="second">策略信息</TabPane>
<TabPane label="今日概况" name="third">今日概况</TabPane>
<TabPane label="历史概况" name="fourth">历史概况</TabPane>
</Tabs>
</template>
<script setup>
import { ref } from 'vue';
import Tabs from './components/tabs/Tabs.vue';
import TabPane from './components/tabs/TabPane.vue';
const activeName = ref('first');
const handleClick = (tab, event) => {
console.log(tab, event);
};
</script>
评论区