# 选项卡

分隔内容上有关联但属于不同类别的数据集合。

# 平台差异

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>