<template>
	<view class="v-tabs">
		<scroll-view :id="getDomId" :scroll-x="scroll" :scroll-left="scroll ? scrollLeft : 0"
			:scroll-with-animation="scroll" :style="{ position: fixed ? 'fixed' : 'relative' }">
			<view class="v-tabs__container" :style="{
          display: scroll ? 'inline-flex' : 'flex',
          whiteSpace: scroll ? 'nowrap' : 'normal',
          background: bgColor,
          padding
        }">
				<view :class="['v-tabs__container-item', { disabled: !!v.disabled }, { active: current == i }]"
					v-for="(v, i) in tabs" :key="i" :style="{
            color: current == i ? activeColor : color,
            fontSize: current == i ? fontSize : fontSize,
            fontWeight: bold && current == i ? 'bold' : '',
            justifyContent: !scroll ? 'center' : '',
            flex: scroll ? '' : 1,
            padding: paddingItem
          }" @click="change(i,'tab')">
					<view class="column-c">
						<image class="wh-60 m-tb10 r-100" :class="current == i?'double-border':''"
							style="display: block;" :src="v.icon" mode=""></image>
						<slot :row="v" :index="i">{{ field ? v[field] : v }}</slot>
					</view>
				</view>
				<template v-if="!!tabs.length">

					<view v-if="lineSlot" class="lineSlot"
						:class="['v-tabs__container-line', { animation: lineAnimation }]" :style="{
                transform: `translate3d(${lineLeft}px, 0, 0)`
                }">
						<slot name="line" />
					</view>
					<view class="column-c" v-else-if="!pills"
						:class="['v-tabs__container-line', { animation: lineAnimation }]" :style="{
                width: lineWidth + 'px',
                height: '30rpx',
                borderRadius: lineRadius,
                transform: `translate3d(${lineLeft}px, 0, 0)`
                }">
						<view class="triangle"></view>
					</view>
					<view v-else :class="['v-tabs__container-pills', { animation: lineAnimation }]" :style="{
                background: pillsColor,
                borderRadius: pillsBorderRadius,
                width: currentWidth + 'px',
                transform: `translate3d(${pillsLeft}px, 0, 0)`,
                height
                }" />
				</template>
			</view>
		</scroll-view>
		<!-- fixed 的站位高度 -->
		<view class="v-tabs__placeholder" :style="{ height: fixed ? height : '0', padding }"></view>
		<view class="v-tabs__content" v-if="tabsSwiper">
			<swiper class="activity-swiper" v-if="!swiperType" :current="value" :duration="duration"
				:circular="circular" @change="onSwiperChange"
				:style="{ height: contentHeight ? contentHeight : `calc(100vh - ${height} - ${paddingBottom})`, paddingBottom: paddingBottom, paddingTop: paddingTop}">
				<swiper-item class="activity-swiper-item" v-for="(item,index) in tabs" :key="item.key"
					:item-id="item.key" :style="{ paddingBottom: 0, paddingTop: 0 }">
					<scroll-view scroll-y class="scroll-list" @scrolltolower="onBottom" :style="{ height: '100%' }">
						<slot :index="index" name="swiperContent" />
					</scroll-view>
				</swiper-item>
			</swiper>
			<view class="data_list" @touchstart="touchStart" @touchend="touchEnd" :animation="animationData" v-else>
				<view class="data_list_content" v-for="(item,index) in tabs" :key="index">
					<scroll-view scroll-y class="scroll-list" @scrolltolower="onBottom"
						:style="{ height: contentHeight ? contentHeight : `calc(100vh - ${height} - ${paddingBottom})`, paddingBottom: paddingBottom, paddingTop: paddingTop}">
						<slot :index="index" name="swiperContent" />
					</scroll-view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import {
		startMicroTask
	} from './utils'
	import props from './props'
	/**
	 * v-tabs
	 * @property {Number} value 选中的下标
	 * @property {Array} tabs tabs 列表
	 * @property {String} bgColor = '#fff' 背景颜色
	 * @property {String} color = '#333' 默认颜色
	 * @property {String} activeColor = '#2979ff' 选中文字颜色
	 * @property {String} fontSize = '28rpx' 默认文字大小
	 * @property {String} activeFontSize = '28rpx' 选中文字大小
	 * @property {Boolean} bold = [true | false] 选中文字是否加粗
	 * @property {Boolean} scroll = [true | false] 是否滚动
	 * @property {String} height = '60rpx' tab 的高度
	 * @property {String} lineHeight = '10rpx' 下划线的高度
	 * @property {String} lineColor = '#2979ff' 下划线的颜色
	 * @property {Number} lineScale = 0.5 下划线的宽度缩放比例
	 * @property {String} lineRadius = '10rpx' 下划线圆角
	 * @property {Boolean} pills = [true | false] 是否胶囊样式
	 * @property {String} pillsColor = '#2979ff' 胶囊背景色
	 * @property {String} pillsBorderRadius = '10rpx' 胶囊圆角大小
	 * @property {String} field 如果是对象，显示的键名
	 * @property {Boolean} fixed = [true | false] 是否固定
	 * @property {String} paddingItem = '0 22rpx' 选项的边距
	 * @property {Boolean} lineAnimation = [true | false] 下划线是否有动画
	 * @property {Number} zIndex = 1993 默认层级
	 * @property {Boolean} lineSlot true 是否自定义底部滑块
	 * @property {Boolean} tabsSwiper false 是否使用swiper
	 * @property {Boolean} swiperType false swiper类型
	 * @property {Boolean} circular false 是否采用衔接滑动，即播放到末尾后重新回到开头
	 * @property {String} paddingBottom 0rpx swiper下边padding 
	 * @property {String} paddingTop 0rpx swiper上边padding
	 * @property {Number} duration 500 swiper滑动动画时长
	 * @property {String} contentHeight '' swiper内容高度
	 *
	 * @event {Function(current)} change 改变标签触发
	 */
	export default {
		name: 'kTabsSwiper',
		props,
		data() {
			return {
				lineWidth: 30,
				currentWidth: 0, // 当前选项的宽度
				lineLeft: 0, // 滑块距离左侧的位置
				pillsLeft: 0, // 胶囊距离左侧的位置
				scrollLeft: 0, // 距离左边的位置
				container: {
					width: 0,
					height: 0,
					left: 0,
					right: 0
				}, // 容器的宽高，左右距离
				current: 0, // 当前选中项
				scrollWidth: 0, // 可以滚动的宽度
				lineSlotWidth: 0, //自定义底部的宽度
				startX: 0,
				startY: 0,
				animationData: {}, // 动画
			}
		},
		computed: {
			getDomId() {
				const len = 16
				const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
				const maxPos = $chars.length
				let pwd = ''
				for (let i = 0; i < len; i++) {
					pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
				}
				return `xfjpeter_${pwd}`
			}
		},
		watch: {
			value: {
				immediate: true,
				handler(newVal) {
					this.current = newVal
					this.$nextTick(this.update)
				}
			},
		},
		methods: {
			initScroll() {
				this.scrollY = new Array(10).fill(0);
			},
			touchStart(event) {
				this.startX = event.touches[0].pageX;
				this.startY = event.touches[0].pageY;
			},
			touchEnd(event) {
				let deltaX = event.changedTouches[0].pageX - this.startX;
				let deltaY = event.changedTouches[0].pageY - this.startY;
				if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 60) {
					if (deltaX < 0) { //往左
						if (this.current == this.tabs.length - 1) {
							if (this.circular) {
								this.change(0);
							} else {
								return
							}
						} else {
							this.change(this.current * 1 + 1);
						}

						this.animate();

					} else if (deltaX > 0) { //往右
						if (this.current == 0) {
							if (this.circular) {
								this.change(this.tabs.length - 1);
							} else {
								return
							}
						} else {
							this.change(this.current * 1 - 1);
						}

						this.animate();

					} else { // 挪动距离0

					}
				} else {

				}
			},
			onSwiperChange(e) {
				this.change(e.detail.current);
			},
			onBottom() {
				this.$emit("load", this.current)
			},
			animate() {
				this.animation.translateX(-this.swiperWidthPx * this.current + "px").step({
					duration: this.duration
				})
				this.animationData = this.animation.export()
				setTimeout(() => {
					this.animationData = {}
				}, this.duration)
			},
			// 切换事件
			change(index, source) {

				const isDisabled = !!this.tabs[index].disabled
				if (this.current !== index && !isDisabled) {
					this.current = index
					this.$emit('input', index)
					this.$emit('change', index)
				}
				//如果是tab切换
				if (this.swiperType && source == 'tab') {
					this.animate();
				}
			},
			createQueryHandler() {
				const query = uni
					.createSelectorQuery()
					// #ifndef MP-ALIPAY
					.in(this)
				// #endif

				return query
			},
			update() {
				const _this = this
				startMicroTask(() => {
					// 没有列表的时候，不执行
					if (!this.tabs.length) return
					_this
						.createQueryHandler()
						.select(`#${this.getDomId}`)
						.boundingClientRect(data => {
							const {
								width,
								height,
								left,
								right
							} = data || {}
							// 获取容器的相关属性
							this.container = {
								width,
								height,
								left,
								right: right - width
							}
							_this.lineScrollWidth();
							_this.calcScrollWidth()
							_this.setScrollLeft()
							_this.setLine()
							if (this.swiperType) {
								_this.getSwiperWidth();
								_this.initScroll();
								// 创建动画实例
								_this.animation = uni.createAnimation({
									timingFunction: 'ease',
									duration: 120
								})
							}
						})
						.exec()
				})
			},
			getSwiperWidth() {
				this.createQueryHandler()
					.select(`.v-tabs__content`)
					.boundingClientRect(data => {
						if (!data) return;
						this.swiperWidthPx = data.width;
					})
					.exec()
			},
			// 计算可以滚动的宽度
			calcScrollWidth(callback) {
				const view = this.createQueryHandler().select(`#${this.getDomId}`)
				view.fields({
					scrollOffset: true
				})
				view
					.scrollOffset(res => {
						if (typeof callback === 'function') {
							callback(res)
						} else {
							// 获取滚动条的宽度
							this.scrollWidth = res.scrollWidth
						}
					})
					.exec()
			},
			// 设置滚动条滚动的进度
			setScrollLeft() {
				this.calcScrollWidth(res => {
					// 动态读取 scrollLeft
					let scrollLeft = res.scrollLeft
					this.createQueryHandler()
						.select(`#${this.getDomId} .v-tabs__container-item.active`)
						.boundingClientRect(data => {
							if (!data) return
							// 除开当前选项外容器的一半宽度
							let curHalfWidth = (this.container.width - data.width) / 2
							let scrollDiff = this.scrollWidth - this.container.width
							// 在原有滚动条的基础上 + (当前元素距离左侧的距离 - 计算的一半宽度) - 容器的外边距之类的
							scrollLeft += data.left - curHalfWidth - this.container.left
							// 已经滚动在左侧了
							if (scrollLeft < 0) scrollLeft = 0
							// 已经超出右侧了
							else if (scrollLeft > scrollDiff) scrollLeft = scrollDiff
							this.scrollLeft = scrollLeft
						})
						.exec()
				})
			},
			lineScrollWidth() {
				this.createQueryHandler()
					.select(`#${this.getDomId} .lineSlot`)
					.boundingClientRect(data => {
						if (!data) return;
						this.lineSlotWidth = data.width;
					})
					.exec()
			},
			setLine() {
				this.calcScrollWidth(res => {
					const scrollLeft = res.scrollLeft
					this.createQueryHandler()
						.select(`#${this.getDomId} .v-tabs__container-item.active`)
						.boundingClientRect(data => {
							if (!data) return
							if (this.lineSlot) {
								this.lineWidth = data.width * this.lineScale
								this.lineLeft = scrollLeft + data.left + data.width / 2 - this.lineSlotWidth /
									2 - this.container.left
							} else if (this.pills) {
								this.currentWidth = data.width
								this.pillsLeft = scrollLeft + data.left - this.container.left
							} else {
								this.lineWidth = data.width * this.lineScale
								this.lineLeft = scrollLeft + data.left + (data.width - data.width * this
									.lineScale) / 2 - this.container.left
							}
						})
						.exec()
				})
			}
		}
	}
</script>

<style lang="scss" scoped>
	.v-tabs {
		width: 100%;
		box-sizing: border-box;
		overflow: hidden;
		border-radius: 10px;
		padding: 0 20rpx;
		background: #fff;

		/* #ifdef H5 */
		::-webkit-scrollbar {
			display: none;
		}

		/* #endif */

		&__container {
			min-width: 100%;
			position: relative;
			display: inline-flex;
			align-items: center;
			white-space: nowrap;
			overflow: hidden;
			padding: 20rpx !important;
			border-radius: 10px;
			background: #fff;

			&-item {
				flex-shrink: 0;
				display: flex;
				align-items: center;
				height: 100%;
				position: relative;
				z-index: 10;
				transition: all 0.3s;
				white-space: nowrap;
				padding: 10rpx 40rpx !important;

				&.disabled {
					opacity: 0.5;
					color: #999;
				}
			}

			&-line {
				position: absolute;
				left: 0;
				bottom: -20rpx;
			}

			&-pills {
				position: absolute;
				z-index: 9;
			}

			&-line,
			&-pills {
				&.animation {
					transition: all 0.3s linear;
				}
			}
		}
	}

	.data_list {
		display: flex;

		&_content {
			flex-shrink: 0;
			width: 100%;
		}
	}

	.triangle {
		width: 20rpx;
		height: 20rpx;
		background: #10B261;
		border-radius: 50%;
	}

	.double-border {
		box-shadow: 0 0 0 6rpx #fff, 0 0 0 12rpx #10B261;
	}
</style>