# 选项卡
分隔内容上有关联但属于不同类别的数据集合。
# 平台差异
App | H5 | 微信小程序 | 支付宝小程序 | 头条小程序 |
---|---|---|---|---|
√ | √ | √ | √ | √ |
# Tabs Props
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
value / v-model | 绑定值 | string / number | ||
labels | 标签组 | array | ||
loop | 是否循环显示 | boolean | true | |
swipeable | 是否滑动 | boolean | false | |
swipe-threshold | 滑动阈值 | number | 60 | |
sticky | 是否吸顶 | boolean | false | |
sticky-top | 距离顶部位置 | number | 0 | |
scroll-view | 是否内部滚动,必须设置父级的高度 | boolean | true | |
fill | 选项卡是否填充 | boolean | false | |
gutter | 选项卡间隔 | number | 20 | |
border | 是否带有下边框 | boolean | true | |
color | 字体及浮标颜色 | string | 主色 |
# Tabs Methods
事件名称 | 说明 | 参数 |
---|---|---|
refresh | 数据延迟加载时,调用该方法刷新布局 |
# Tabs Events
事件名称 | 说明 | 回调参数 |
---|---|---|
tab-change | 选项卡切换时触发 | name |
# Tab-pane Props
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
label | 选项卡标题 | string | ||
name | 与选项卡绑定值 value 对应的标识符,表示选项卡别名 | string | ||
prefix-icon | 前缀图标 | string | ||
suffix-icon | 后缀图标 | string | ||
lazy | 是否延迟渲染 | boolean | false | |
refresher-enabled | 是否开启下拉刷新 | boolean | false |
TIP
name
不传入时,默认使用下标来区分
# Tab-pane Events
事件名称 | 说明 | 回调参数 |
---|---|---|
pull-refresh | 下拉刷新 | { done } |
loadmore | 加载更多 |
# 示例
默认用法
<template>
<cl-tabs v-model="active" :labels="list"> </cl-tabs>
</template>
<script>
export default {
data() {
return {
active: "fruit",
list: [
{
label: "水果",
name: "fruit"
},
{
label: "蔬菜",
name: "vegetables"
},
{
label: "海鲜",
name: "seafood"
}
]
};
}
};
</script>
选项卡带有内容
TIP
添加标签 <cl-tab-pane />
时不能带上 labels
参数。
<template>
<cl-tabs v-model="active">
<cl-tab-pane label="水果" name="fruit">
<cl-list>
<cl-list-item label="苹果"></cl-list-item>
<cl-list-item label="草莓"></cl-list-item>
<cl-list-item label="菠萝"></cl-list-item>
<cl-list-item label="西瓜"></cl-list-item>
<cl-list-item label="木瓜"></cl-list-item>
</cl-list>
</cl-tab-pane>
<cl-tab-pane label="蔬菜" name="vegetables">
<cl-list>
<cl-list-item label="萝卜"></cl-list-item>
<cl-list-item label="胡萝卜"></cl-list-item>
<cl-list-item label="白菜"></cl-list-item>
</cl-list>
</cl-tab-pane>
<cl-tab-pane label="海鲜" name="seafood">
<cl-list>
<cl-list-item label="大黄鱼"></cl-list-item>
<cl-list-item label="梭子蟹"></cl-list-item>
<cl-list-item label="柽子王"></cl-list-item>
<cl-list-item label="沙丁鱼"></cl-list-item>
</cl-list>
</cl-tab-pane>
</cl-tabs>
</template>
<script>
export default {
data() {
return {
active: "fruit"
};
}
};
</script>
选项卡可滑动
TIP
默认使用 scroll-view
来控制选项卡内容的滑动,必须设置父级的高度。(scroll-view 性能问题请考虑)
TIP
如果想使用原生的页面滚动,设置 scroll-view = false
,同时设置 sticky
吸顶效果,也能达到同样的效果。(切换时滚动位置会重置)
<template>
<cl-tabs v-model="active" swipeable>
<cl-tab-pane v-for="(item, index) in list" :key="index" :name="index" :label="item.label">
<cl-list>
<cl-list-item
v-for="(item2, index2) in item.children"
:key="index2"
:label="`${item.label} - ${item2}`"
>
</cl-list-item>
</cl-list>
</cl-tab-pane>
</cl-tabs>
</template>
<script>
export default {
data() {
return {
active: 0,
list: [
{
label: "抖音视频"
},
{
label: "热点"
},
{
label: "直播"
},
{
label: "图片"
},
{
label: "科技"
},
{
label: "娱乐"
},
{
label: "游戏"
},
{
label: "体育"
},
{
label: "财经"
},
{
label: "数码"
}
]
};
},
created() {
this.list.map((e, i) => {
this.$set(
e,
"children",
new Array(20).fill(1).map(() => parseInt(Math.random() * 100))
);
});
}
};
</script>
WARNING
当前模式是通过滑动去判断上页或者下页,过程中数据面板不会随着手势移动。如有需求,请改用 swiper
标签,参考如下:
<template>
<view class="demo-tabs">
<cl-tabs v-model="current" :labels="labels" :border="false">
<!-- 自定义内容区域 -->
<swiper class="container" @change="onChangeSwiper" :current="current">
<swiper-item v-for="(item, index) in list" :key="index">
<!-- 是否预加载 -->
<template v-if="item.loaded || index == current">
<cl-scroller :ref="`scroller-${index}`" @down="onDown" @up="onUp">
<!-- 首次加载框,可有可无 -->
<cl-loading-mask :loading="loading" text="加载中">
<!-- 列表 -->
<view class="list">
<cl-list-item
v-for="(item2, index2) in item.data"
:key="index2"
:label="`${item.label} - ${index2}`"
>
<cl-icon name="cl-icon-arrow-right"></cl-icon>
</cl-list-item>
<!-- 加载更多 -->
<cl-loadmore
v-if="item.data.length > 0"
:loading="item.loading"
:finish="item.finished"
:divider="false"
></cl-loadmore>
</view>
</cl-loading-mask>
</cl-scroller>
</template>
</swiper-item>
</swiper>
</cl-tabs>
</view>
</template>
<script>
export default {
data() {
const labels = [
{
label: "热门",
value: 1,
loaded: true
},
{
label: "猜你喜欢",
value: 2
},
{
label: "女装",
value: 3
},
{
label: "美妆个护",
value: 4
},
{
label: "食品",
value: 5
},
{
label: "母婴",
value: 6
},
{
label: "数码家电",
value: 7
},
{
label: "家居家装",
value: 8
},
{
label: "内衣",
value: 9
}
];
const list = labels.map(e => {
return {
...e,
status: e.value,
data: [],
finished: false,
loading: false,
pagination: {
page: 1,
size: 20
}
};
});
return {
current: 0,
labels,
list,
loading: true
};
},
mounted() {
this.refresh();
},
methods: {
onDown() {
this.refresh({
page: 1
}).done(() => {
this.$refs[`scroller-${this.current}`][0].end();
});
},
onUp() {
const { pagination, finished } = this.list[this.current];
if (!finished) {
this.refresh({
page: pagination.page + 1
});
}
},
onChangeSwiper(e) {
this.current = e.detail.current;
if (!this.list[this.current].loaded) {
this.loading = true;
this.list[this.current].loaded = true;
}
setTimeout(() => {
this.refresh({
page: 1
});
}, 500);
},
refresh(params = {}) {
const item = this.list[this.current];
let data = {
...item.pagination,
status: item.status,
sort: "desc",
order: "createTime",
...params
};
return new Promise(resolve => {
item.loading = true;
console.log("Refresh");
setTimeout(() => {
item.data = new Array(data.page == 1 ? 10 : data.page * 10).fill(1);
item.pagination.page = data.page;
item.finished = false;
item.loading = false;
this.loading = false;
resolve();
}, 500);
});
}
}
};
</script>
<style lang="scss">
page {
// #ifdef H5
height: 100%;
// #endif
// #ifndef H5
height: 100vh;
// #endif
}
.demo-tabs {
height: 100%;
overflow: hidden;
.container {
height: 100%;
background-color: #f7f7f7;
}
.list {
padding: 20rpx;
/deep/.cl-list-item {
margin-bottom: 20rpx;
border-radius: 10rpx;
}
}
}
</style>