<template>
	<view>
		<view class="upload-images" :style="{'overflow-x': overOnePage ? 'hidden' : 'initial'}">
			<!-- 上传按钮 TODO 应该移到和图片同一列 -->
			<view class="upload-image-item" 
				:style="{order: uploadButton == 'front' ? '1' : '2', height: '222rpx', 'margin-bottom': '6px'}" 
				@click="uploadImage" 
				v-if="(count == -1) || (image_list.length < count)">
				<text class="lf-iconfont lf-icon-jia upload-image-item-after"></text>
			</view>
			<!-- 图片列表 -->
			<view class="item-wrap" :style="{'height': itemWrapHeight +'px', order: uploadButton == 'front' ? '2' : '1'}">
				<view class="item"
					  :class="{'cur': cur == index, 'zIndex': curZ == index, 'itemTransition': itemTransition}"
					  v-for="(item, index) in list"
					  :key="index"
					  :id="'item'+index"
					  :data-key="item.key"
					  :data-index="index"
					  :style="[itemStyle(index, item)]"
					  @longpress="longPress"
					  @touchmove.stop="touchMove"
					  @touchend.stop="touchEnd">
					<view class="info">
						<view class="upload-image-item" @click="lookImage(index)">
							<image :src="item.data" mode="aspectFill"></image>
							<view class="remove-image" @click.stop="removeInage(index)" v-if="showDelete">
								<text class="lf-iconfont lf-icon-shanchu"></text>
							</view>
						</view>
					</view>
				</view>
			</view>
		</view>
		<view v-if="overOnePage" class="indicator">
			<view>滑动此区域滚动页面</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			uploadButton: {
				type: String, // 上传按钮显示在哪里，front图片前面，behind图片后面
				default: 'front'
			},
			showDelete: {
				type: Boolean, // 是否可删除图片
				default: true
			},
			count: {
				type: Number, // 可上传多少张图, 默认6张
				default: 6
			},
			size: {
				type: Number, // 限制单张图上传大小，单位M
				default: 10
			},
			drag: {
				type: Boolean, // TODO 是否可拖拽排序图片
				default: false
			},
			padding: {
				type: Number, // 图片与图片之间的间距，默认5像素，单位px
				default: 6
			},
			width: {
				type: String, // 每张图片的宽度，支持百分比，像素，bisection表示等分
				default: 'bisection'
			},
			height: {
				type: String, // 每张图片高度
				default: '222rpx'
			}
		},
		data(){
			return {
				touch: false,
				startX: 0,
				startY: 0,
				columns: 3,
				item: {},
				tranX: 0,
				tranConstX: 0,
				itemWrap: {},
				tranY: 0,
				teanConstY: 0,
				overOnePage: false,
				windowHeight: 0,
				originKey: null,
				list: [],
				cur: -1,
				curZ: -1,
				image_list: [],
				itemWrapHeight: 0
			}
		},
		computed: {
			itemStyle(){
				let that = this;
				return function(index, item){
					let style_obj = {
						height: that.$props.height
					}
					
					let width = that.$props.width;
					if(width == 'bisection'){
						width = `calc(${100 / that.columns +'%'} - 4px)`
					}
					style_obj.width = width;
					
					let str = 'translate3d(';
					if(index === that.cur){
						str += that.tranX +'px';
						str += ', ';
						str += that.tranY +'px';
					}else{
						str += item.tranX +'px';
						str += ', ';
						str += item.tranY +'px';
					}
					str += ', 0px)';
					style_obj.transform = str;
					return style_obj;
				}
			}
		},
		mounted(){
			// 'https://picsum.photos/200',  
			// 'https://picsum.photos/200/300'
			this.init();
		},
		methods: {
			// 上传凭证图片
			uploadImage(){
				let current_count = this.$props.count - this.image_list.length;
				if(this.$props.count == -1){
					current_count = 9;
				}
				if(current_count == 0) return;
				uni.chooseImage({
					count: current_count,
					complete: result => {
						if(result.errMsg == "chooseImage:fail cancel"){
							return; // 取消选择图片
						}
						let tempFiles = result.tempFiles;
						let image_list = [];
						let overstep = false;
						tempFiles.map(item => {
							// 上传的图片大小
							let size = Math.floor((Number(this.$props.size) || 1) * 1024 * 1024);
							if(item.size < size){
								image_list.push(item.path);
							}else{
								overstep = true;
							}
						})
						this.image_list.push(...image_list);
						if(overstep){
							uni.showModal({
								title: '温馨提示',
								content: '您上传的图片含有超出10M大小的限制，请优化大小后再上传！',
								showCancel: false
							})
						}else{
							this.init(); // TODO 优化每次上传都会造成重新初始化导致闪烁
						}
					}
				})
			},
			// 预览图片
			lookImage(current){
				if(this.image_list.length <= 0) return;
				this.$u.throttle(() => {
					uni.previewImage({
						urls: this.image_list,
						current: current
					});
				}, 500);
			},
			// 移除图片
			removeInage(current){
				// 移除已上传的图片
				this.image_list.splice(current, 1);
				// 移除定位的元素(重新初始化)
				let list = this.image_list.map((item, index) => {
				    return {
				        key: index,
				        tranX: 0,
				        tranY: 0,
				        data: item
				    }
				}) || [];
				this.list = list;
				this.getPosition(list, false); // 重新定位
				// 重新计算大盒子高度
				let rows = Math.ceil(list.length / this.columns);
				let itemWrapHeight = rows * this.item.height;
				itemWrapHeight += rows * this.$props.padding - this.$props.padding;
				this.itemWrapHeight = itemWrapHeight;
			},
			// 返回已上传的图片列表
			getUploadImage(){
				return this.image_list;
			},
			// 长按触发移动排序
			longPress(e) {
				this.touch = true;
			    this.startX = e.changedTouches[0].pageX
			    this.startY = e.changedTouches[0].pageY
			    let index = e.currentTarget.dataset.index;
			    if(this.columns === 1) { // 单列时候X轴初始不做位移
			        this.tranConstX = 0;
			    } else {  // 多列的时候计算X轴初始位移, 使 item 水平中心移动到点击处
			        this.tranConstX = this.startX - this.item.width / 2 - this.itemWrap.left;
			    }
			    // 计算Y轴初始位移, 使 item 垂直中心移动到点击处
			    this.teanConstY = this.startY - this.item.height / 2 - this.itemWrap.top;
				this.tranY = this.teanConstY;
				this.tranX = this.tranConstX;
				this.cur = index;
				this.curZ = index;
			    // #ifndef H5
			    uni.vibrateShort();
			    // #endif
			},
			// 拖拽中
			touchMove(e) {
			    if (!this.touch) return;
			    let tranX = e.touches[0].pageX - this.startX + this.tranConstX; // TODO 原作者写的逻辑，但不知道为什么计算不对？？？
			    let tranY = e.touches[0].pageY - this.startY + this.teanConstY;
			    let overOnePage = this.overOnePage;
			
			    // 判断是否超过一屏幕, 超过则需要判断当前位置动态滚动page的位置
			    if(overOnePage) {
			        if(e.touches[0].clientY > this.windowHeight - this.item.height) {
			            uni.pageScrollTo({
			                scrollTop: e.touches[0].pageY + this.item.height - this.windowHeight,
			                duration: 300
			            });
			        } else if(e.touches[0].clientY < this.item.height) {
			            uni.pageScrollTo({
			                scrollTop: e.touches[0].pageY - this.item.height,
			                duration: 300
			            });
			        }
			    }
			
				this.tranX = tranX;
				this.tranY = tranY;
			    let originKey = e.currentTarget.dataset.key;
			    let endKey = this.calculateMoving(tranX, tranY);
			
			    // 防止拖拽过程中发生乱序问题
			    if (originKey == endKey || this.originKey == originKey) return;
			    this.originKey = originKey;
			    this.insert(originKey, endKey);
			},
			// 根据当前的手指偏移量计算目标key
			calculateMoving(tranX, tranY) {
			    let rows = Math.ceil(this.list.length / this.columns) - 1;
			    let i = Math.round(tranX / this.item.width);
			    let j = Math.round(tranY / this.item.height);
			
			    i = i > (this.columns - 1) ? (this.columns - 1) : i;
			    i = i < 0 ? 0 : i;
			
			    j = j < 0 ? 0 : j;
			    j = j > rows ? rows : j;
			
			    let endKey = i + this.columns * j;
			    endKey = endKey >= this.list.length ? this.list.length - 1 : endKey;
			    return endKey
			},
			// 根据起始key和目标key去重新计算每一项的新的key
			insert(origin, end) {
			    let list;
			    if (origin < end) {
			        list = this.list.map((item) => {
			            if (item.key > origin && item.key <= end) {
			                item.key = item.key - 1;
			            } else if (item.key == origin) {
			                item.key = end;
			            }
			            return item
			        });
			        this.getPosition(list);
			    } else if (origin > end) {
			        list = this.list.map((item) => {
			            if (item.key >= end && item.key < origin) {
			                item.key = item.key + 1;
			            } else if (item.key == origin) {
			                item.key = end;
			            }
			            return item
			        });
			        this.getPosition(list);
			    }
			},
			// 根据排序后 list 数据进行位移计算
			getPosition(data, vibrate = true) {
			    let list = data.map((item, index) => {
					// X轴距离
					let tranX = this.item.width * (item.key % this.columns);
					let rows = Math.ceil((item.key+1) / this.columns);
					let numX = item.key;
					for(let i=0; i<rows-1; i++){
						numX -= this.columns;
					}
					numX = numX * this.$props.padding;
					tranX = tranX != 0 ? tranX + numX : tranX;
			        item.tranX = tranX;
					// Y轴距离
					let tranY = Math.floor(item.key / this.columns) * this.item.height;
					if(rows > 1){
						let numY = (rows - 1) * this.$props.padding;
						tranY = tranY + numY;
					}
			        item.tranY = tranY;
			        return item
			    });
			
				this.list = list;
			    if(!vibrate) return;
				this.itemTransition = true;
				// #ifndef H5
			    uni.vibrateShort();
				// #endif
			
			    let image_list = [];
			    list.forEach((item) => {
			        image_list[item.key] = item.data
			    });
			
			    // 元素位置发生改变了，触发change事件告诉父元素更新
				this.$emit('change', {image_list: image_list});
			},
			// 拖拽结束
			touchEnd() {
			    if (!this.touch) return;
			    this.clearData();
			},
			// 清除参数
			clearData() {
			    this.originKey = -1;
				this.touch = false;
				this.cur = -1;
				this.tranX = 0;
				this.tranY = 0;
			
			    // 延迟清空
			    setTimeout(() => {
					this.curZ = -1;
			    }, 300)
			},
			// 初始化加载
			init() {
				// 拦截image_list为空未上传图片的情况
				if(!this.image_list.length) return;
			    // 遍历数据源增加扩展项, 以用作排序使用
			    let list = this.image_list.map((item, index) => {
			        return {
			            key: index,
			            tranX: 0,
			            tranY: 0,
			            data: item
			        }
			    });
				this.list = list;
				this.itemTransition = false;
			    this.windowHeight = uni.getSystemInfoSync().windowHeight;
			
				setTimeout(() => {
					// 获取每一项的宽高等属性
					this.createSelectorQuery().select(".item").boundingClientRect((res) => {
					    let rows = Math.ceil(this.list.length / this.columns);
					    this.item = res;
					    this.getPosition(this.list, false);
					    let itemWrapHeight = rows * res.height; // TODO 有时候会报res.height undefined
						itemWrapHeight += rows * this.$props.padding - this.$props.padding;
						this.itemWrapHeight = itemWrapHeight;
					    
						this.createSelectorQuery().select(".item-wrap").boundingClientRect((res) => {
					        this.itemWrap = res;
					        let overOnePage = itemWrapHeight + res.top > this.windowHeight;
							this.overOnePage = overOnePage;
					    }).exec();
					}).exec();
				}, 300)
			}
		}
	}
</script>

<style lang="scss" scoped="scoped">
	.upload-images{
		display: flex;
		flex-wrap: wrap;
		.upload-image-item{
			// width: 220rpx;
			// height: 220rpx;
			background: #DDDDDD;
			position: relative;
			// margin-right: 12rpx;
			width: 100%;
			height: 100%;
			&:nth-child(3n){
				margin-right: 0rpx;
			}
			&:nth-child(n+4){
				margin-top: 12rpx;
			}
			image{
				width: 100%;
				height: 100%;
			}
			.remove-image{
				position: absolute;
				right: -4rpx;
				top: -18rpx;
				color: #e74c3c;
				font-size: 40rpx;
				padding: 8rpx;
			}
		}
		.upload-image-item-after{
			position: absolute;
			width: 100%;
			height: 100%;
			display: flex;
			justify-content: center;
			align-items: center;
			font-size: 60rpx;
			color: #999999;
		}
	}
	
	.item-wrap {
		position: relative;
		width: 100%;
		.item {
			position: absolute;
			width: 100%;
			z-index: 1;
			&.itemTransition {
				transition: transform 0.3s;
			}
			&.zIndex {
				z-index: 2;
			}
			&.cur {
				background: #1998FE;
				transition: initial;
			}
		}
	}
	
	.info {
		position: relative;
		// padding-top: 100%;
		background: #ffffff;
		// margin-right: 5px;
		width: auto;
		height: 100%;
		&:nth-child(3n){
			margin-right: 0px;
		}
		& > view {
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			overflow: hidden;
			box-sizing: border-box;
			image {
				width: 100%;
				height: 100%;
			}
		}
	}
	
	.indicator {
		position: fixed;
		z-index: 99999;
		right: 0rpx;
		top: 50%;
		margin-top: -250rpx;
		padding: 20rpx;
		& > view {
			width: 36rpx;
			height: 500rpx;
			background: #ffffff;
			border-radius: 30rpx;
			box-shadow: 0 0 10rpx -4rpx rgba(0, 0, 0, 0.5);
			color: pink;
			padding-top: 90rpx;
			box-sizing: border-box;
			font-size: 24rpx;
			text-align: center;
			opacity: 0.8;
		}
	}
</style>
