Tabs
一个轻量级且易于使用的 Vue 3 标签页组件,支持动态切换、自定义插槽和动画效果。
特性
- 🚀 轻量级 - 简洁高效的实现
- 🎨 动画效果 - 平滑的滑动指示器动画
- 🖼️ 图标支持 - 支持标签图标显示
- 🔧 自定义插槽 - 完全自定义标签内容
- 📱 响应式 - 自适应不同屏幕尺寸
- 💫 Vue 3 - 基于 Composition API 构建
安装
bash
npm install vue3-xm
在线演示
基础演示
基础标签页
这是一个基础的标签页示例,展示默认配置和基本功能。
折线图
表格视图
- 图表分析
- 数据统计
当前选中
选中的标签值:line
功能说明
- 🖱️ 点击标签切换
- 🎨 动态滑动指示器
- 🖼️ 支持图标显示
- 🔧 自定义插槽内容
查看代码
vue
<template>
<div class="demo-container">
<h3>基础标签页</h3>
<p>这是一个基础的标签页示例,展示默认配置和基本功能。</p>
<div class="tabs-wrapper">
<Tabs v-model="activeTab" :tabs="basicTabs" @change="onTabChange" />
</div>
<div class="info-panel">
<h4>当前选中</h4>
<p>
选中的标签值:<strong>{{ activeTab }}</strong>
</p>
<h4>功能说明</h4>
<ul>
<li>🖱️ 点击标签切换</li>
<li>🎨 动态滑动指示器</li>
<li>🖼️ 支持图标显示</li>
<li>🔧 自定义插槽内容</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { Tabs } from "vue3-xm";
// 当前选中的标签
const activeTab = ref("line");
// 基础标签数据
const basicTabs = ref([
{
value: "line",
label: "折线图",
url: "https://imagecdn.ymm56.com/ymmfile/static/resource/aecd539e-515f-41e8-b126-7ac38102cafd.webp",
},
{
value: "table",
label: "表格视图",
url: "https://imagecdn.ymm56.com/ymmfile/static/resource/13428291-7a45-4430-8fa9-70f8d0dea937.webp",
},
{
value: "chart",
label: "图表分析",
},
{
value: "data",
label: "数据统计",
},
]);
// 标签切换事件
const onTabChange = (value) => {
console.log("标签切换到:", value);
};
</script>
<style scoped>
.demo-container {
padding: 24px;
border: 1px solid #e0e0e0;
border-radius: 12px;
margin: 20px 0;
background: #f8f9fa;
}
.tabs-wrapper {
margin: 20px 0;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.info-panel {
margin-top: 20px;
padding: 16px;
background: white;
border-radius: 8px;
border-left: 4px solid #007bff;
}
.info-panel h4 {
margin: 0 0 12px 0;
color: #333;
font-size: 16px;
}
.info-panel p {
margin: 8px 0;
color: #666;
}
.info-panel ul {
margin: 0;
padding-left: 20px;
}
.info-panel li {
margin: 4px 0;
color: #666;
}
</style>
高级配置演示
高级配置演示
这个示例展示了自定义插槽、动态标签和事件处理等高级功能。
- 首页
- 关于我们
- 联系我们
标签内容区域
🏠 首页内容
这里是首页的内容区域,可以放置任何你想要的内容。
当前状态
- 当前选中:home
- 标签总数:3
- 自定义模板:关闭
查看代码
vue
<template>
<div class="demo-container">
<h3>高级配置演示</h3>
<p>这个示例展示了自定义插槽、动态标签和事件处理等高级功能。</p>
<div class="controls">
<button @click="addTab" class="control-btn">➕ 添加标签</button>
<button @click="removeTab" class="control-btn" :disabled="customTabs.length <= 1">➖ 删除标签</button>
<button @click="toggleTemplate" class="control-btn">🎨 切换模板</button>
</div>
<div class="tabs-wrapper">
<Tabs v-model="activeCustomTab" :tabs="customTabs" @change="onCustomTabChange">
<template #default="{ item, index }" v-if="useCustomTemplate">
<div class="custom-tab-content">
<span class="tab-badge">{{ index + 1 }}</span>
<div class="tab-info">
<div class="tab-title">{{ item.label }}</div>
<div class="tab-subtitle">{{ item.subtitle || "标签页" }}</div>
</div>
</div>
</template>
</Tabs>
</div>
<div class="content-area">
<h4>标签内容区域</h4>
<div class="tab-content">
<div v-if="activeCustomTab === 'home'" class="content-panel">
<h5>🏠 首页内容</h5>
<p>这里是首页的内容区域,可以放置任何你想要的内容。</p>
</div>
<div v-else-if="activeCustomTab === 'about'" class="content-panel">
<h5>📖 关于我们</h5>
<p>这里是关于页面的内容,介绍公司或产品信息。</p>
</div>
<div v-else-if="activeCustomTab === 'contact'" class="content-panel">
<h5>📞 联系方式</h5>
<p>这里是联系页面的内容,提供联系方式和表单。</p>
</div>
<div v-else class="content-panel">
<h5>🔥 {{ getCurrentTabLabel() }}</h5>
<p>这是动态添加的标签页内容。</p>
</div>
</div>
</div>
<div class="info-panel">
<h4>当前状态</h4>
<ul>
<li>
当前选中:<strong>{{ activeCustomTab }}</strong>
</li>
<li>
标签总数:<strong>{{ customTabs.length }}</strong>
</li>
<li>
自定义模板:<strong>{{ useCustomTemplate ? "开启" : "关闭" }}</strong>
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { Tabs } from "vue3-xm";
// 当前选中的标签
const activeCustomTab = ref("home");
// 是否使用自定义模板
const useCustomTemplate = ref(false);
// 标签计数器
let tabCounter = 1;
// 自定义标签数据
const customTabs = ref([
{
value: "home",
label: "首页",
subtitle: "主页面",
},
{
value: "about",
label: "关于我们",
subtitle: "公司介绍",
},
{
value: "contact",
label: "联系我们",
subtitle: "联系方式",
},
]);
// 标签切换事件
const onCustomTabChange = (value) => {
console.log("自定义标签切换到:", value);
};
// 添加标签
const addTab = () => {
tabCounter++;
const newTab = {
value: `tab${tabCounter}`,
label: `标签${tabCounter}`,
subtitle: `动态标签${tabCounter}`,
};
customTabs.value.push(newTab);
};
// 删除标签
const removeTab = () => {
if (customTabs.value.length > 1) {
const removed = customTabs.value.pop();
// 如果删除的是当前选中的标签,切换到第一个
if (removed && activeCustomTab.value === removed.value) {
activeCustomTab.value = customTabs.value[0].value;
}
}
};
// 切换模板
const toggleTemplate = () => {
useCustomTemplate.value = !useCustomTemplate.value;
};
// 获取当前标签的标题
const getCurrentTabLabel = () => {
const currentTab = customTabs.value.find((tab) => tab.value === activeCustomTab.value);
return currentTab ? currentTab.label : "";
};
</script>
<style scoped>
.demo-container {
padding: 24px;
border: 1px solid #e0e0e0;
border-radius: 12px;
margin: 20px 0;
background: #f8f9fa;
}
.controls {
display: flex;
gap: 12px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.control-btn {
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
color: #333;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.control-btn:hover:not(:disabled) {
background: #007bff;
color: white;
border-color: #007bff;
}
.control-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.tabs-wrapper {
margin: 20px 0;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.custom-tab-content {
display: flex;
align-items: center;
gap: 8px;
}
.tab-badge {
background: #007bff;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
}
.tab-info {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.tab-title {
font-weight: 500;
font-size: 14px;
}
.tab-subtitle {
font-size: 12px;
color: #666;
margin-top: 2px;
}
.content-area {
margin: 20px 0;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.content-area h4 {
margin: 0;
padding: 16px 20px;
background: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
color: #333;
}
.tab-content {
padding: 20px;
}
.content-panel h5 {
margin: 0 0 12px 0;
color: #333;
font-size: 18px;
}
.content-panel p {
margin: 0;
color: #666;
line-height: 1.5;
}
.info-panel {
margin-top: 20px;
padding: 16px;
background: white;
border-radius: 8px;
border-left: 4px solid #28a745;
}
.info-panel h4 {
margin: 0 0 12px 0;
color: #333;
font-size: 16px;
}
.info-panel ul {
margin: 0;
padding-left: 20px;
}
.info-panel li {
margin: 4px 0;
color: #666;
}
</style>
API
Props
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
modelValue | string | "line" | v-model 当前选中的标签值 |
tabs | TabItem[] | [] | 标签页数据数组 |
TabItem 接口
typescript
interface TabItem {
value: string; // 标签的唯一值
label: string; // 标签显示文本
url?: string; // 可选的图标URL
}
Events
事件名 | 参数 | 说明 |
---|---|---|
update:modelValue | (value: string) | 当前选中值发生变化时触发 |
change | (value: string) | 标签切换时触发 |
Slots
插槽名 | 参数 | 说明 |
---|---|---|
default | { item: TabItem, index: number } | 自定义标签内容 |
基础用法
vue
<template>
<Tabs v-model="activeTab" :tabs="tabs" @change="onTabChange" />
</template>
<script setup>
import { ref } from "vue";
import { Tabs } from "vue3-xm";
const activeTab = ref("tab1");
const tabs = ref([
{
value: "tab1",
label: "标签1",
url: "https://example.com/icon1.png",
},
{
value: "tab2",
label: "标签2",
url: "https://example.com/icon2.png",
},
]);
const onTabChange = (value) => {
console.log("切换到标签:", value);
};
</script>
自定义插槽
vue
<template>
<Tabs v-model="activeTab" :tabs="tabs">
<template #default="{ item, index }">
<div class="custom-tab">
<span class="badge">{{ index + 1 }}</span>
<div class="tab-info">
<div class="title">{{ item.label }}</div>
<div class="subtitle">标签页 {{ item.value }}</div>
</div>
</div>
</template>
</Tabs>
</template>
样式定制
组件提供了以下 CSS 类名供自定义样式:
css
/* 主容器 */
.tabs-wraper {
background: #f5f5f5;
position: relative;
}
/* 标签项 */
.item-tab {
height: 32px;
margin: 10px;
white-space: nowrap;
color: #8d8d8d;
position: relative;
z-index: 2;
}
/* 激活状态 */
.item-tab.active {
color: #161616;
}
/* 悬停状态 */
.item-tab:hover {
color: #161616;
}
注意事项
- 唯一值: 确保每个标签的
value
是唯一的 - 动画性能: 滑动指示器使用 CSS transform 实现,性能优秀
- 图标大小: 建议图标尺寸为 16x16 像素
- 响应式: 组件会自动适应容器宽度
浏览器兼容性
- Chrome >= 60
- Firefox >= 60
- Safari >= 12
- Edge >= 79