<template>
	<view
		class="data-list-components safe-area-inset-bottom"
		:class="{ 'safe-area-inset-bottom': safeAreaInsetBottom }"
		:style="[dataListStyle]"
		@touchmove.stop.prevent
	>
		<scroll-view
			class="scroll-view"
			:scroll-top="scrollTop"
			:scroll-y="scrollingEnabled"
			:lower-threshold="lowerThreshold"
			:enable-back-to-top="enableBackToTop"
			@scroll="handleScroll"
			@scrolltolower="handleScrolltolower"
			@touchstart="handleTouchStart"
			@touchmove="handleTouchMove"
			@touchend="handleTouchEnd"
		>
			<view class="scroll-content" :style="[scrollContentStyle]">
				<!-- 下拉 -->
				<view class="pull-down-view">
					<slot name="refresh" v-if="$slots.refresh"></slot>
					<view class="refresh-view" v-else>
						<!-- <view class="loading-animation"></view> -->
						<!-- <text class="pull-down-text">{{ refreshText[refreshStatus] }}</text> -->
					<text :class="' pull-down-text icon-ymt '+(refreshStatus == 'soon'?' to-blod-down ':refreshStatus == 'sure'?' to-blod-up ':' ') " >{{ refreshText[refreshStatus] }}</text>
					</view>
				</view>
				
				<view class="scroll-list"><slot></slot></view>
				
				<view class="loading-view" v-if="loadingMask">
					<view class="loading-animation"></view>
					<text class="loading-text">加载中...</text>
				</view>
				
				<!-- 空 -->
				<view class="empty-view" v-if="showEmpty">
					<slot name="empty" v-if="$slots.empty"></slot>
					<view class="empty-content" v-else>
						
						<image class="empty-image" :src="emptyImage || defaultEmptyImage" mode=""></image>
						<text class="empty-text">{{ emptyText }}</text>
					</view>
				</view>
				
				<!-- 底部上滑 -->
				<view class="pull-up-view" v-else>
					<slot name="load" v-if="$slots.load"></slot>
					<view class="load-view" v-else>
						<view class="loading-animation" v-if="loadStatus == 'loading'"></view>
						<text :class="' loading-text icon-ymt '+(loadStatus == 'soon'?' to-blod-up ':loadStatus == 'sure'?' to-blod-down ':' ') " >{{ loadText[loadStatus] }}</text>
					</view>
				</view>
			</view>
		</scroll-view>
	</view>
</template>

<script>
/**
 * data-list 数据列表
 * @description 数据列表展示，上拉加载更多数据，下拉刷新数据
 * @tips 如果父组件有设置padding，可能造成组件高度不正确的问题
 * @property {Number, String} page 默认分页
 * @property {Number, String} size 默认分页大小
 * @property {Boolean} loading 是否显示首次加载时的loading遮罩层
 * @property {Boolean} safeAreaInsetBottom 是否开启底部安全区域适配
 * @property {String} emptyImage 空数据提示图片
 * @property {String} emptyText 空数据提示文字
 * @property {Boolean} enableBackToTop iOS点击顶部状态栏、安卓双击标题栏时，滚动条返回顶部
 * @property {Number, String} refresherThreshold 下拉刷新阈值（单位px），触发 refresherrefresh 事件
 * @property {Number, String} lowerThreshold 距底部多远时（单位px），触发 scrolltolower 事件
 * @methods {Function} loadData 加载数据方法
 * @methods {Function} refreshData 刷新数据方法
 * @methods {Function} computedHeight 重新计算高度方法（有时候页面某些地方布局被改变了，会造成组件高度不能撑满的问题，就调用此方法）
 * @event {Function} load 加载 事件（参数一：分页请求参数，参数二：回调方法，需要将列表和数据总数回调回去计算 {list:数据列表对象,total:后端数据总数}）
 * @event {Function} refresh 刷新 事件（参数一：分页请求参数，参数二：回调方法，需要将列表和数据总数回调回去计算 {list:数据列表对象,total:后端数据总数}）
 * @event {Function} scroll 滚动 事件
 * @event {Function} scrolltolower 上拉触底 事件
 * @event {Function} refresherpulling 正在下拉 事件
 * @event {Function} refresherrestore 下拉刷新被复位 事件
 * @event {Function} refresherabort 下拉刷新被终止 事件
 * @event {Function} refresherrefresh 下拉刷新被触发 事件
 * @example <data-list class="data-list" ref="list" @load="handleLoad" @refresh="handleRefresh" @scroll="handleScroll"></data-list>
 */
import emptyImages from './emptyImage.js';
export default {
	name: 'data-list',
	props: {
		page: {
			type: [Number, String],
			default: 1
		},
		size: {
			type: [Number, String],
			default: 15
		},
		loading: {
			type: Boolean,
			default: false
		},
		safeAreaInsetBottom: {
			type: Boolean,
			default: true
		},
		emptyImage: {
			type: String
		},
		emptyText: {
			type: String,
			default: '暂无数据~'
		},
		enableBackToTop: {
			type: Boolean,
			default: false
		},
		refresherThreshold: {
			type: [Number, String],
			default: 50
		},
		lowerThreshold: {
			type: [Number, String],
			default: 50
		},
		selectObject:{
			type:Object,
			default:{
				selectIndex:0,
				length:0
			}
		}
	},
	data() {
		return {
			list: [], // 数据列表
			total: -1, // 数据总数
			currentPage: 1, // 当前分页页码
			currentSize: 10, // 当前分页大小
			showEmpty: false, // 是否显示空数据
			waiting: false, // 是否等待加载中
			loadingMask: this.loading, // 是否显示加载中遮罩
			autoLoad: false, // 是否自动加载
			scrollTop: 0, // 设置竖向滚动条位置
			currentScrollTop: 0, // 当前竖向滚动条位置
			scrollingEnabled: true, // 是否启用竖向滚动
			pullingDown: false, // 是否正在下拉
			pullDownHeight: 0, // 下拉高度
			pullDownTimeStamp: 0, // 下拉时的时间戳
			
			pullingUp: false, // 是否正在上拉
			pullUpHeight: 0, // 上拉高度
			pullUpTimeStamp: 0, // 上拉时的时间戳
			
			maxDistance:100,
			touchDirection:'top',
			
			loadStatus: 'loading',
			loadText: {
				loading: '加载中~',
				nomore: '没有更多啦~',
				
				soon: '上滑查看下一分类',
				sure: '释放查看下一分类',
			},
			refreshStatus: 'soon',
			refreshText: {
				soon: '下滑查看上一分类',
				sure: '释放查看上一分类',
				
				nomore: '没有更多啦~',
				refreshing: '正在刷新中~'
			},
			defaultEmptyImage: emptyImages.data,
			componentsHeight: 'auto' // 组件高度属性
		};
	},
	watch:{
		selectObject:{
			handler(n, o) {
				if(n.selectIndex == n.length-1){
					this.loadStatus = "nomore"
				}else{
					this.loadStatus = "soon"
				}
				if(n.selectIndex == 0){
					this.refreshStatus = "nomore"
				}else{
					this.refreshStatus = "soon"
				}
				
			},
			deep:true, // 深度监听
			immediate:false, // 初始化监听
		},
		
	},
	computed: {
		dataListStyle() {
			const { componentsHeight } = this;
			return {
				height: componentsHeight
			};
		},
		scrollContentStyle() {
			const style = {};
			const { pullDownHeight, pullingDown,pullUpHeight, pullingUp } = this;
			
			// if(this.touchDirection == 'top'){
				style.transform = pullingDown ? `translateY(${pullDownHeight}px)` : `translateY(0px)`;
				style.transition = pullingDown ? `transform .1s linear` : `transform 0.3s cubic-bezier(0.19,1.64,0.42,0.72)`;
			// }else if(this.touchDirection == 'bottom'){
			// 	style.transform = pullingUp ? `translateY(0px)` : `translateY(0px)`;
				
				
			// 	style.transition = pullingUp ? `transform .1s linear` : `transform 0.3s cubic-bezier(0.19,1.64,0.42,0.72)`;
			// }

			return style;
		}
	},
	mounted() {
		this.handleInit();
	},
	methods: {
		// 下滑触底后 数据加载
		loadData() {
			this.waiting = true;
			
			this.$emit('load', { page: this.currentPage, size: this.currentSize }, res => {
				this.waiting = false;
				// this.loadingMask = false;
				this.list = res.list;
				this.total = res.total;
				this.currentPage++;
				this.handleComputeData();
				this.computedHeight();
			});
		},
		
		// 计算高度
		computedHeight() {
			this.getRect('.data-list-components').then(res => {
				// #ifdef H5
				this.componentsHeight = `calc(100vh - ${res.top}px - var(--window-top))`;
				// #endif
				// #ifndef H5
				this.componentsHeight = `calc(100vh - ${res.top}px)`;
				// #endif
				
			});
		},
		// 计算数据
		handleComputeData() {
			const len = this.list.length;
			const total = this.total;
			if (len == 0 || total == 0) {
				this.autoLoad = false;
				this.showEmpty = true;
			} else if (len >= this.total) {
				this.autoLoad = false;
				this.showEmpty = false;
				this.loadStatus = 'soon';
	
			} else if (len < this.total) {
				this.showEmpty = false;
				
				this.loadStatus = 'loading';
				// console.log(len,this.total,this.loadStatus)
			}
			
		},
		// 下滑触底事件
		handleScrolltolower(e) {
			this.$emit('scrolltolower', e);
			if (this.loadStatus == 'nomore' || this.waiting) return;
			this.loadData();
		},
		// 滚动事件
		handleScroll(e) {
			this.currentScrollTop = e.detail.scrollTop;
			
			if (e.detail.scrollTop <= 20) {
				this.touchDirection = 'top';
			} else {
				this.touchDirection = 'bottom';
			} 
			this.$emit('scroll', e);
		},
		// 触摸按下处理
		handleTouchStart(e) {
			
			if(this.touchDirection=='top'){
				this.pullingDown = true;
			}else if(this.touchDirection=='bottom'){
				this.pullingUp =true;
			}

			this.currentTouchStartY = e.touches[0].clientY;
		},
		// 触摸按下滑动处理
		handleTouchMove(e) {
			
			// this.$emit('refresherpulling', e);
			// 顶部下滑
			if (e.touches[0].clientY >= this.currentTouchStartY){
				// const shakeInterval = 1000 / 60;
				// const currentTimeStamp = new Date().getTime();
				// if (this.pullDownTimeStamp && currentTimeStamp - this.pullDownTimeStamp <= shakeInterval) return;
				// this.pullDownTimeStamp = currentTimeStamp;
				
				const currentTouchMoveY = e.touches[0].clientY;
				let movingDistance = (currentTouchMoveY - this.currentTouchStartY) ;
				if(this.selectObject.selectIndex == 0){
					this.refreshStatus = 'nomore'
				}
				else if(movingDistance > this.refresherThreshold){
					this.refreshStatus = 'sure';// 释放
				}else{
					// movingDistance = 0;
					this.refreshStatus = 'soon';// 划
				}
				let computeDistance = movingDistance;
				
				
				if(computeDistance > this.maxDistance){
					computeDistance = this.maxDistance;
				}
				
				this.pullDownHeight = computeDistance ;
			}else{
				
				// 底部上滑
				
				const currentTouchMoveY = e.touches[0].clientY;
				let movingDistance = (this.currentTouchStartY - currentTouchMoveY ) ;
				if(this.selectObject.selectIndex == this.selectObject.length-1){
					this.loadStatus = 'nomore'
				}else if (movingDistance >= this.lowerThreshold) {
					this.loadStatus = 'sure';//释放
				
				} else {
					this.loadStatus = 'soon';//划
					
				}
				let computeDistance = movingDistance;
				if(computeDistance > this.maxDistance){
					computeDistance = this.maxDistance;
				}
				this.pullUpHeight = computeDistance;
						
			}
			
			
		},
		// 触摸松开处理
		handleTouchEnd(e) {			
			// 顶部下滑
			if(this.touchDirection == 'top'){				
				if(this.pullDownHeight>=this.refresherThreshold&&this.selectObject.selectIndex > 0){
					this.$emit('refresherrefresh', "prev");
				}
				this.pullingDown = false;
				this.pullDownHeight = 0;
			// 底部上滑
			}else if(this.touchDirection == 'bottom'){
				if(this.pullUpHeight>=this.lowerThreshold&&this.selectObject.selectIndex < (this.selectObject.length-1)){
					this.$emit('refresherrefresh', "next");
				}
				this.pullingUp = false;
				this.pullUpHeight = 0;	
			}
		},
		// 初始化
		handleInit() {
			this.currentPage = this.page;
			this.currentSize = this.size;
			this.computedHeight();
		},
		// 查询dom节点信息
		getRect(selector, all) {
			return new Promise(resolve => {
				uni.createSelectorQuery()
					.in(this)
					[all ? 'selectAll' : 'select'](selector)
					.boundingClientRect(rect => {
						if (all && Array.isArray(rect) && rect.length) {
							resolve(rect);
						}
						if (!all && rect) {
							resolve(rect);
						}
					})
					.exec();
			});
		}
	}
};
</script>

<style scoped lang="scss">
.data-list-components {
	height: 100%;
	.scroll-view {
		height: 100%;
		.scroll-content {
			width: 100%;
			height: 100%;
			position: relative;
			.empty-view {
				width: 100%;
				height: 100%;
				display: flex;
				align-items: center;
				justify-content: center;
				.empty-content {
					margin: auto;
					display: flex;
					align-items: center;
					flex-direction: column;
					.empty-image {
						width: 400rpx;
						height: 400rpx;
					}
					.empty-text {
						color: #606266;
						margin-top: 20rpx;
					}
				}
			}
			.pull-down-view {
				top: 0;
				left: 0;
				width: 100%;
				padding: 30rpx 0;
				display: flex;
				position: absolute;
				align-items: center;
				flex-direction: column;
				justify-content: center;
				transform: translateY(-100%);
				.refresh-view {
					display: flex;
					align-items: center;
					flex-direction: column;
					.loading-animation {
						width: 50rpx;
						height: 50rpx;
					}
					.pull-down-text {
						color: #606266;
						font-size: 24rpx;
						margin-top: 10rpx;
					}
				}
			}
			.scroll-list {
				display: flex;
				flex-direction: column;
			}
			.loading-view {
				width: 100%;
				height: 100%;
				display: flex;
				align-items: center;
				flex-direction: column;
				justify-content: center;
				.loading-animation {
					width: 40rpx;
					height: 40rpx;
				}
				.loading-text {
					color: #606266;
					font-size: 24rpx;
					margin-top: 10rpx;
				}
			}
			.pull-up-view {
				width: 100%;
				padding: 15rpx 0;
				display: flex;
				align-items: center;
				justify-content: center;
				.load-view {
					display: flex;
					align-items: center;
					.loading-animation {
						width: 30rpx;
						height: 30rpx;
					}
					.loading-text {
						color: #606266;
						font-size: 24rpx;
						margin-left: 20rpx;
					}
				}
			}
			.loading-animation {
				position: relative;
				&::after,
				&::before {
					left: 0;
					width: 100%;
					position: absolute;
					border: 0 solid currentColor;
					background-color: #b7b7b7;
				}
				&::after {
					content: '';
					top: -25%;
					z-index: 1;
					height: 100%;
					border-radius: 10%;
					animation: spin 0.6s -0.1s linear infinite;
				}
				&::before {
					content: '';
					bottom: -9%;
					height: 10%;
					background: #000;
					border-radius: 50%;
					opacity: 0.2;
					animation: shadow 0.6s -0.1s linear infinite;
				}
				@keyframes spin {
					17% {
						border-bottom-right-radius: 10%;
					}
					25% {
						transform: translateY(25%) rotate(22.5deg);
					}
					50% {
						border-bottom-right-radius: 100%;
						transform: translateY(50%) scale(1, 0.9) rotate(45deg);
					}
					75% {
						transform: translateY(25%) rotate(67.5deg);
					}
					100% {
						transform: translateY(0) rotate(90deg);
					}
				}
				@keyframes shadow {
					50% {
						transform: scale(1.25, 1);
					}
				}
			}
		}
	}
}
</style>
