个人组件库文档地址
Tabs 标签页
基础用法
<vp-tabs v-model="tabsActiveName">
<vp-tab-pane label="小卡车" name="n1">小卡车</vp-tab-pane>
<vp-tab-pane label="蜘蛛侠" name="n2">蜘蛛侠</vp-tab-pane>
<vp-tab-pane label="猪猪侠" name="n3">猪猪侠</vp-tab-pane>
</vp-tabs>
<script>
export default {
data() {
return {
tabsActiveName: "n2",
};
},
};
</script>
Tabs 组件代码
<template>
<div class="vp-tabs">
<div class="vp-tabs_item_container">
<div
:class="[
'vp-tabs_item',
value === child.name ? 'vp-tabs_item_active' : '',
]"
@click="handleClickTabItem(child.name, $event)"
v-for="child in $children"
:key="child.name"
>
{{ child.label }}
</div>
<div
class="vp-tabs__active-bar"
:style="{
width: clientWidth + 'px',
transform: `translateX(${scrollleft}px)`,
}"
></div>
</div>
<slot></slot>
</div>
</template>
<script>
export default {
name: "vpTabs",
props: {
value: {
type: String,
default: "",
},
},
watch: {
value: {
handler(newVal) {
this.$nextTick(() => {
let currentIndex = this.$children.findIndex((item) => {
return item.name === newVal;
});
this.currentIndex = currentIndex;
let itemNodeList = this.$el.children[0].children;
let scrollleft = 0;
for (let i = 0; i < itemNodeList.length - 1; i++) {
if (i !== this.currentIndex) {
scrollleft += itemNodeList[i].clientWidth;
} else {
this.clientWidth = itemNodeList[i].clientWidth;
break;
}
}
this.scrollleft = scrollleft;
});
},
immediate: true,
deep: true,
},
},
data() {
return {
currentIndex: 0,
scrollleft: 0,
clientWidth: 0,
};
},
mounted() {
let currentIndex = this.$children.findIndex((item) => {
return item.name === this.value;
});
this.currentIndex = currentIndex;
setTimeout(() => {
let itemlist = this.$el.querySelectorAll(".vp-tabs_item");
this.clientWidth = itemlist[this.currentIndex]?.clientWidth;
let itemNodeList = this.$el.children[0].children;
let scrollleft = 0;
for (let i = 0; i < itemNodeList.length - 1; i++) {
if (i !== this.currentIndex) {
scrollleft += itemNodeList[i].clientWidth;
} else {
this.clientWidth = itemNodeList[i].clientWidth;
break;
}
}
this.scrollleft = scrollleft;
}, 500);
},
methods: {
handleClickTabItem(name, e) {
this.$emit("input", name);
},
},
};
</script>
<style lang="less" scoped>
.vp-tabs {
.vp-tabs_item_container {
display: flex;
position: relative;
z-index: 1;
.vp-tabs__active-bar {
position: absolute;
bottom: 0;
left: 0;
height: 4px;
background-color: #409eff;
z-index: 100;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
list-style: none;
}
.vp-tabs_item {
padding: 10px 20px;
cursor: pointer;
&:hover {
color: #409eff;
}
}
.vp-tabs_item_active {
color: #409eff;
}
::after {
content: "";
position: absolute;
width: 100%;
height: 2px;
background-color: #e4e7ed;
z-index: 2;
left: 0;
bottom: 0;
}
}
}
</style>
Tab-pane 组件代码
<template>
<div class="vp-tab-pane">
<transition name="slide-fade">
<div class="vp-tab-pane_content" v-show="currentTab === name">
<slot></slot>
</div>
</transition>
</div>
</template>
<script>
export default {
name: "vpTabPane",
props: {
label: {
type: String,
default: "",
},
name: {
type: String,
default: "",
},
},
watch: {
"$parent.value": {
handler(newVal) {
this.currentTab = newVal;
},
immediate: true,
},
},
data() {
return {
currentTab: "",
};
},
created() {},
mounted() {},
};
</script>
<style lang="less" scoped>
.vp-tab-pane {
.vp-tab-pane_content {
padding: 10px;
}
}
.slide-fade-enter-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter {
transform: translateX(10px);
opacity: 0;
}
</style>
Tabs Attributes
| 属性名 | 类型 | 属性值 | 描述 | 默认值 |
|---|
| v-model | String | - | 第一个选项卡的 name | - |
Tabs-pane Attributes
| 属性名 | 类型 | 属性值 | 描述 | 默认值 |
|---|
| label | String | - | 标签卡标题 | - |
| name | String | - | 与选项卡绑定值 value 对应的标识符,表示选项卡别名 | - |
Tabs Events
| 事件名 | 描述 | 返回值 |
|---|
| tab-click | 标签页点击 | vnode, event |
.switch-base {
padding: 20px;
border: 1px solid #95a5a6;
border-radius: 5px;
display: flex;
}
.div-row {
margin: 10px;
}