一. 效果图
二. 代码
文件 WqTab.vue:
<template><div ref="wqTabs" class="wq-tab"><template v-for="tab in tabs" :key="tab"><div class="tab-item" :class="{ ac: tabActive === tab.key }" @click="tabActive = tab.key">{{ tab.value || tab.key }}</div></template><divclass="bg":style="{width: bgWidth + 'px',left: bgLeft + 'px',}"></div></div>
</template>
<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue';
import { Tab } from '@/components/WqTab/wqTabType';type Props = {tabs: Tab[];active: string;
};const props = withDefaults(defineProps<Props>(), {});
const emit = defineEmits(['update:active']);const tabActive = computed<string>({get() {return props.active;},set(newValue) {// 改变值emit('update:active', newValue);},
});
// wqTabs 元素
const wqTabs = ref();
// bg宽度
const bgWidth = ref<number>(0);
// bg 位置
const bgLeft = ref<number>(0);
watch(tabActive,(newValue, oldValue) => {// 改变背景nextTick(() => {const tabIndex = props.tabs.findIndex((item) => item.key === newValue);if (tabIndex >= 0) {/*** 当找到值时* 1. 找到相应的元素* 2. 获取元素的当前位置以及大小* 3, 将bg大小进行调整 并移动到相应的位置*/nextTick(() => {const tabs: Element[] | any = wqTabs.value.querySelectorAll('.tab-item');if (!tabs || !tabs.length) {// 若没有找到tabconsole.error('tab dom find error');return;}const tab = tabs[tabIndex];bgLeft.value = tab.offsetLeft as number;bgWidth.value = tab.clientWidth;// console.log('value', bgLeft.value, bgWidth.value);});} else {// 没有找到值的时候找defaultconst defaultTab = props.tabs.find((item) => item.default);tabActive.value = defaultTab?.key || props.tabs[0].key;}});},{ immediate: true }
);
</script>
<style scoped lang="scss">
.wq-tab {//width: 500px;//height: 50px;//width: 100%;margin: auto;display: flex;align-items: center;//justify-content: center;position: relative;border-radius: 25px;border: 1px solid #dfe4ea;overflow: hidden;.tab-item {flex: 1;text-align: center;//width: 100px;//padding: 0 30px;//max-width: 100px;height: 40px;line-height: 40px;position: relative;z-index: 2;//overflow: hidden;cursor: pointer;user-select: none;}.ac {color: #fff;}.bg {position: absolute;left: 0;top: 0;z-index: 1;//width: 150px;height: 50px;background: #b2bec3;transition: all 0.5s;}
}
</style>
文件: wqTabType
export type Tab = {// 唯一值key: string;// 非必需, 如果没有将使用key进行替换value?: string;// 是否为默认选项default?: boolean;
};
三. 使用:
<template><wq-tab v-model:active="tabActive" :tabs="tabs"></wq-tab>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import WqTab from '@/components/WqTab/WqTab.vue';
import { Tab } from '@/components/WqTab/wqTabType';const tabActive = ref('');const tabs: Tab[] = [{key: 'comp',value: '组件',default: true, // 这里是默认值},{key: 'app',value: '应用',},{key: 'web',value: '网站',},
];</script>
四. 补充
- 这个组件的宽度是基于父元素给的,
- 传递的 active 是v-model的
- 个人代码水平一般, 如果有什么不合理的地方欢迎大佬们留言
- 组件的父元素如果是可变大小的可能会产生样式错误, 比如父元素宽度使用vh, vw这种,