uniapp自定义tabs加刷新

uniapp自定义tabs加刷新

1.插件

https://ext.dcloud.net.cn/plugin?id=343

2.main.js引入

3.示例

<template>
	<view>
		<!-- 菜单 -->
		<view class="top-warp">
			<view class="tip" @click="tabChange()">每次切换菜单及时刷新列表,不缓存数据</view>
			<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
		</view>

		<!-- top="xxx"下拉布局往下偏移,防止被悬浮菜单遮住 -->
		<mescroll-body ref="mescrollRef" @init="mescrollInit" top="120" @down="downCallback" :up="upOption" @up="upCallback"
		 @emptyclick="emptyClick">
			<!-- 数据列表 -->
			<!-- <good-list :list="goods"></good-list> -->
			<view v-if="this.tabIndex==0">hao</view>
			<view v-if="this.tabIndex==1">hao1</view>
			<view v-if="this.tabIndex==2">hao2</view>
			<view v-if="this.tabIndex==3">hao3</view>
		</mescroll-body>
	</view>
</template>

<script>
	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
	import {
		apiSearch
	} from "@/api/mock.js"

	export default {
		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
		data() {
			return {
				upOption: {
					// page: {
					// 	num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
					// 	size: 10 // 每页数据的数量
					// },
					noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
					empty: {
						tip: '~ 搜索无数据 ~', // 提示
						btnText: '去看看'
					}
				},
				goods: [], //列表数据
				tabs: ['全部', '奶粉', '面膜', '图书'],
				tabIndex: 0 // tab下标
			}
		},
		methods: {
			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
			upCallback(page) {
				//联网加载数据
				let keyword = this.tabs[this.tabIndex]
				apiSearch(page.num, page.size, keyword).then(curPageData => {
					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
					this.mescroll.endSuccess(curPageData.length);
					//设置列表数据
					if (page.num == 1) this.goods = []; //如果是第一页需手动制空列表
					this.goods = this.goods.concat(curPageData); //追加新数据
				}).catch(() => {
					//联网失败, 结束加载
					this.mescroll.endErr();
				})
			},
			//点击空布局按钮的回调
			emptyClick() {
				uni.showToast({
					title: '点击了按钮,具体逻辑自行实现'
				})
			},

			// 切换菜单
			tabChange() {
				this.goods = [] // 先置空列表,显示加载进度
				this.mescroll.resetUpScroll() // 再刷新列表数据
			}
		}
	}
</script>

<style>
	.top-warp {
		z-index: 9990;
		position: fixed;
		top: --window-top;
		/* css变量 */
		left: 0;
		width: 100%;
		height: 120upx;
		background-color: white;
	}

	.top-warp .tip {
		font-size: 28upx;
		height: 60upx;
		line-height: 60upx;
		text-align: center;
	}
</style>

4.自定义tabs样式

<!-- tab组件: <me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs> -->
<template>
	<view class="me-tabs" :class="{'tabs-fixed': fixed}" :style="{height: tabHeightVal}">
		<scroll-view v-if="tabs.length" :id="viewId" :scroll-left="scrollLeft" scroll-x scroll-with-animation
		 :scroll-animation-duration="300">
			<view class="tabs-item" :class="{'tabs-flex':!isScroll, 'tabs-scroll':isScroll}">
				<!-- tab -->
				<view class="tab-item" :style="{width: tabWidthVal, height: tabHeightVal, 'line-height':tabHeightVal}" v-for="(tab, i) in tabs"
				 :class="{'active': value===i}" :key="i" @click="tabClick(i)">
					<view style="position: relative; height: 0rpx; top: -10rpx; left: 30rpx;">6</view>
					{{getTabName(tab)}}
				</view>
				<!-- 下划线 -->
				<view class="tabs-line" :style="{left:lineLeft}"></view>
			</view>
		</scroll-view>
	</view>
</template>

<script>
	export default {
		props: {
			tabs: { // 支持格式: ['全部', '待付款'] 或 [{name:'全部'}, {name:'待付款'}]
				type: Array,
				default () {
					return []
				}
			},
			nameKey: { // 取name的字段
				type: String,
				default: 'name'
			},
			value: { // 当前显示的下标 (使用v-model语法糖: 1.props需为value; 2.需回调input事件)
				type: [String, Number],
				default: 0
			},
			fixed: Boolean, // 是否悬浮,默认false
			tabWidth: Number, // 每个tab的宽度,默认不设置值,为flex平均分配; 如果指定宽度,则不使用flex,每个tab居左,超过则水平滑动(单位默认rpx)
			height: { // 高度,单位rpx
				type: Number,
				default: 64
			}
		},
		data() {
			return {
				viewId: 'id_' + Math.random().toString(36).substr(2, 16),
				scrollLeft: 0
			}
		},
		computed: {
			isScroll() {
				return this.tabWidth && this.tabs.length // 指定了tabWidth的宽度,则支持水平滑动
			},
			tabHeightPx() {
				return uni.upx2px(this.height)
			},
			tabHeightVal() {
				return this.tabHeightPx + 'px'
			},
			tabWidthPx() {
				return uni.upx2px(this.tabWidth)
			},
			tabWidthVal() {
				return this.isScroll ? this.tabWidthPx + 'px' : ''
			},
			lineLeft() {
				if (this.isScroll) {
					return this.tabWidthPx * this.value + this.tabWidthPx / 2 + 'px' // 需转为px (用rpx的话iOS真机显示有误差)
				} else {
					return 100 / this.tabs.length * (this.value + 1) - 100 / (this.tabs.length * 2) + '%'
				}
			}
		},
		watch: {
			tabs() {
				this.warpWidth = null; // 重新计算容器宽度
				this.scrollCenter(); // 水平滚动到中间
			},
			value() {
				this.scrollCenter(); // 水平滚动到中间
			}
		},
		methods: {
			getTabName(tab) {
				return typeof tab === "object" ? tab[this.nameKey] : tab
			},
			tabClick(i) {
				if (this.value != i) {
					this.$emit("input", i);
					this.$emit("change", i);
				}
			},
			async scrollCenter() {
				if (!this.isScroll) return;
				if (!this.warpWidth) { // tabs容器的宽度
					let rect = await this.initWarpRect()
					this.warpWidth = rect ? rect.width : uni.getSystemInfoSync().windowWidth; // 某些情况下取不到宽度,暂时取屏幕宽度
				}
				let tabLeft = this.tabWidthPx * this.value + this.tabWidthPx / 2; // 当前tab中心点到左边的距离
				let diff = tabLeft - this.warpWidth / 2 // 如果超过tabs容器的一半,则滚动差值
				this.scrollLeft = diff;
				// #ifdef MP-TOUTIAO
				this.scrollTimer && clearTimeout(this.scrollTimer)
				this.scrollTimer = setTimeout(() => { // 字节跳动小程序,需延时再次设置scrollLeft,否则tab切换跨度较大时不生效
					this.scrollLeft = Math.ceil(diff)
				}, 400)
				// #endif
			},
			initWarpRect() {
				return new Promise(resolve => {
					setTimeout(() => { // 延时确保dom已渲染, 不使用$nextclick
						let query = uni.createSelectorQuery();
						// #ifndef MP-ALIPAY
						query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
						// #endif
						query.select('#' + this.viewId).boundingClientRect(data => {
							resolve(data)
						}).exec();
					}, 20)
				})
			}
		},
		mounted() {
			this.scrollCenter() // 滚动到当前下标
		}
	}
</script>

<style lang="scss">
	.me-tabs {
		position: relative;
		font-size: 24rpx;
		background-color: #fff;
		border-bottom: 1rpx solid #eee;
		box-sizing: border-box;
		overflow-y: hidden;
		background-color: #fff;

		&.tabs-fixed {
			z-index: 990;
			position: fixed;
			top: var(--window-top);
			left: 0;
			width: 100%;
		}

		.tabs-item {
			position: relative;
			white-space: nowrap;
			padding-bottom: 30rpx; // 撑开高度,再配合me-tabs的overflow-y: hidden,以达到隐藏滚动条的目的
			box-sizing: border-box;

			.tab-item {
				position: relative;
				text-align: center;
				box-sizing: border-box;

				&.active {
					font-weight: bold;
					color: red;
				}
			}
		}

		// 平分的方式显示item
		.tabs-flex {
			display: flex;

			.tab-item {
				flex: 1;
			}
		}

		// 居左显示item,支持水平滑动
		.tabs-scroll {
			.tab-item {
				display: inline-block;
			}
		}

		// 选中tab的线
		.tabs-line {
			z-index: 1;
			position: absolute;
			bottom: 30rpx; // 至少与.tabs-item的padding-bottom一致,才能保证在底部边缘
			width: 20rpx;
			height: 6rpx;
			transform: translateX(-50%);
			border-radius: 4rpx;
			transition: left .3s;
			background: red;
		}
	}
</style>

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值