效果
Index.vue
<script setup>
import {ref} from 'vue'
import Tab from './Tab.vue'
import TabItem from './TabItem.vue'
const activeTab = ref('a')
function handleClick(name) {
}
</script>
<template>
<tab v-model="activeTab" @click="handleClick">
<tab-item label='标签一' name='a'>This is tab a</tab-item>
<tab-item label='标签二' name='b'>This is tab b</tab-item>
<tab-item label='标签三' name='c'>This is tab c</tab-item>
</tab>
</template>
Tab.vue
<script setup>
import {defineComponent, reactive, toRefs, ref, computed, watch, toRef, useSlots} from 'vue'
const props = defineProps({
modelValue: [String, Number]
})
const emit = defineEmits(['update:modelValue', 'click'])
const slots = useSlots()
const tabList = computed(() => {
if (!slots.default) return []
return slots.default().map(item => ({
name: item.props.name,
label: item.props.label
}))
})
const modelValue = toRef(props, 'modelValue')
function handleClick(name) {
emit('update:modelValue', name)
emit('click', name)
}
</script>
<template>
<div>
<ul class="title-wrap">
<li
v-for="item in tabList"
:key="item"
:class="{active: modelValue == item.name}"
@click.stop="handleClick(item.name)"
>{{item.label}}</li>
</ul>
<div class="content-wrap">
<slot />
</div>
</div>
</template>
<style scoped lang='scss'>
.title-wrap{
padding: 0;
margin: 0 auto;
list-style: none;
display: flex;
flex-direction: row;
& > li {
min-width: 40px;
padding: 8px;
margin: 0 10px 0 0;
border: 1px solid #666;
&:last-child{
margin: 0;
}
&.active {
border: 1px solid rgba(12, 143, 230, 0.856);
color: rgba(12, 143, 230, 0.856);
}
}
}
</style>
TabItem.vue
<script setup>
import {getCurrentInstance, computed} from 'vue'
const props = defineProps({
label: {
type: String,
required: true,
},
name: {
type: [String, Number],
required: true,
}
})
const parentModelValue = computed(() => getCurrentInstance().parent.props.modelValue)
</script>
<template>
<div v-show="parentModelValue == props.name">
<slot/>
</div>
</template>