vue3-multi-tab-swiper
Version:
### 介绍
244 lines (198 loc) • 7.22 kB
Markdown
# Vue3-multi-tab-swiper
### 介绍
`MultiTabSwiper` 是一个用于 Vue 3 的多功能组件,它提供了丰富的交互特性,包括上拉加载更多数据、下拉刷新列表、顶部标签栏吸顶,以及水平滑动切换标签时的位置记忆功能。[github 仓库](https://github.com/Eureka-M/vue3-multi-tab-swiper)。实现参考:[匠心打造多 tab 自动吸顶下的多滚动容器](https://zhuanlan.zhihu.com/p/410913113)
### 安装
```
npm i vue3-multi-tab-swiper
```
### 导入
```
import { createApp } from "vue";
import App from "./App.vue";
import MultiTabSwiper from "vue3-multi-tab-swiper";
import "vue3-multi-tab-swiper/lib/style.css";
let app = createApp(App);
app.use(MultiTabSwiper);
app.mount("#app");
```
### 使用示例
##### 上拉加载
<img src="上拉加载.GIF" alt="上拉加载" style="zoom:67%;" />
##### 下拉刷新
<img src="下拉刷新.GIF" alt="下拉刷新" style="zoom:67%;" />
##### 水平滑动位置记忆
<img src="横滑.GIF" alt="横滑" style="zoom:67%;" />
##### 代码
```
<template>
<multi-tab-swiper
:tabs="tabs"
:loadingEnd="isLoadingEnd"
:hasMore="hasMoreData"
@pulldownRefresh="pulldownRefresh"
@pullupLoadMore="pullupLoadMore"
@activeChange="activeChange"
>
<template #banner-content>
<div class="banner-box">顶部内容区</div>
</template>
<template #refresh-indicator="{ state }">
<div v-if="state === 'pulling'" class="refresh-text">下拉刷新...</div>
<div v-if="state === 'releasing'" class="refresh-text">释放刷新...</div>
<div v-if="state === 'refreshing'" class="refresh-text">正在刷新...</div>
<div v-if="state === 'refreshed'" class="refresh-text">刷新完成!</div>
</template>
<template #load-more-indicator="{ state }">
<div v-if="state === 'loadMore'" class="load-text">加载更多...</div>
<div v-if="state === 'loading'" class="load-text">加载中...</div>
<div v-if="state === 'noMoreData'" class="load-text">暂无更多数据...</div>
</template>
<div class="tab-content">
<div class="data-item" v-for="(item, index) in data1" :key="index">
This is Tab-1, Current data is {{ index + 1 }}
</div>
</div>
<div class="tab-content">
<div class="data-item" v-for="(item, index) in data2" :key="index">
This is Tab-2, Current data is {{ index + 1 }}
</div>
</div>
<div class="tab-content">
<div class="data-item" v-for="(item, index) in data3" :key="index">
This is Tab-3, Current data is {{ index + 1 }}
</div>
</div>
<div class="tab-content">
<div class="data-item" v-for="(item, index) in data4" :key="index">
This is Tab-4, Current data is {{ index + 1 }}
</div>
</div>
<div class="tab-content">
<div class="data-item" v-for="(item, index) in data5" :key="index">
This is Tab-5, Current data is {{ index + 1 }}
</div>
</div>
<div class="tab-content">
<div class="data-item" v-for="(item, index) in data6" :key="index">
This is Tab-6, Current data is {{ index + 1 }}
</div>
</div>
</multi-tab-swiper>
</template>
<script setup>
import { ref } from "vue";
let tabIndex = 0;
const tabs = ref(["tab1", "tab2", "tab3", "tab4", "tab5", "tab6"]);
const data1 = ref(new Array(20).fill(0));
const data2 = ref(new Array(20).fill(0));
const data3 = ref(new Array(20).fill(0));
const data4 = ref(new Array(20).fill(0));
const data5 = ref(new Array(20).fill(0));
const data6 = ref(new Array(20).fill(0));
let isLoadingEnd = ref(false);
let hasMoreData = ref(true);
const pulldownRefresh = () => {
isLoadingEnd.value = false;
setTimeout(() => {
console.log("下拉刷新数据");
let data = ref(getCurrentTabData(tabIndex));
data.value = new Array(20).fill(0);
isLoadingEnd.value = true;
}, 1000);
};
const pullupLoadMore = () => {
isLoadingEnd.value = false;
hasMoreData.value = true;
setTimeout(() => {
console.log("上拉加载数据");
let newData = new Array(10).fill(0);
let data = ref(getCurrentTabData(tabIndex));
data.value = data.value.concat(newData);
isLoadingEnd.value = true;
hasMoreData.value = data.value.length < 40;
}, 1000);
};
const getCurrentTabData = (index) => {
switch (index) {
case 0:
return data1;
case 1:
return data2;
case 2:
return data3;
case 3:
return data4;
case 4:
return data5;
case 5:
return data6;
default:
return data1;
}
};
const activeChange = (index) => {
tabIndex = index;
};
</script>
<style scoped lang="less">
.banner-box {
height: 150px;
display: flex;
align-items: center;
justify-content: center;
background: #f58391;
}
:deep(.tabs) {
background: pink;
.tab-item.active {
background: #fff ;
}
}
.tab-content {
padding: 20px;
.data-item {
display: flex;
justify-content: center;
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
margin-bottom: 10px;
font-size: 14px;
border-radius: 4px;
background: #ffe9ed;
&:last-child {
margin-bottom: 0;
}
}
}
.main-content {
background: #fde7ea;
}
.refresh-text,
.load-text {
margin-bottom: 10px;
}
</style>
```
### API 参考
##### props
| 名称 | 类型 | 是否必须 | 描述 |
| ---------- | ------- | -------- | -------------------- |
| tabs | Array | 是 | tabbar 上显示的标签 |
| loadingEnd | Boolean | 是 | 请求完成之后通知组件 |
| hasMore | Boolean | 是 | 列表是否还有数据 |
##### Slots
| 名称 | 描述 |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| - | 默认插槽,用于渲染每个 tab 项的内容。这里的第一层级 dom 数要和 tabs 数组项数对齐。 |
| banner-content | 顶部内容区插槽 |
| refresh-indicator | 下拉刷新插槽,是一个作用域插槽。抛出来的 state 的值:'pulling' \|\| 'releasing' \|\| 'refreshing' \|\| 'refreshed' |
| load-more-indicator | 上拉加载插槽,是一个作用域插槽。抛出来的 state 的值:'loadMore' \|\| 'loading' \|\| 'noMoreData' |
##### Events
| 名称 | 参数 | 描述 |
| --------------- | ----- | -------------------------------------------- |
| pulldownRefresh | - | 当用户执行下拉刷新操作时触发。 |
| pullupLoadMore | - | 当用户滚动到底部并且启用上拉加载更多时触发。 |
| activeChange | index | 当用户点击 tab 或者横滑结束后触发。 |
### 目前还存在的问题
左右滑动时,两侧出现的弹动问题。包括 tab 贴顶后,上滑 tab 出现的弹动问题。