一. 效果图

请添加图片描述

二. 代码

文件 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>
		<div
			class="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) {
						// 若没有找到tab
						console.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 {
				// 没有找到值的时候找default
				const 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>

四. 补充

  1. 这个组件的宽度是基于父元素给的,
  2. 传递的 active 是v-model的
  3. 个人代码水平一般, 如果有什么不合理的地方欢迎大佬们留言
  4. 组件的父元素如果是可变大小的可能会产生样式错误, 比如父元素宽度使用vh, vw这种,
GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐