|
|
|
@ -1,20 +1,40 @@ |
|
|
|
<template> |
|
|
|
<view class="upload-images"> |
|
|
|
<view class="upload-image-item" |
|
|
|
:style="{order: uploadButton == 'front' ? '1' : '2'}" |
|
|
|
@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="upload-image-item" |
|
|
|
:style="{order: uploadButton == 'front' ? '2' : '1'}" |
|
|
|
v-for="(item, index) in image_list" :key="index" |
|
|
|
@click="lookImage(index)"> |
|
|
|
<image :src="item" mode="aspectFill"></image> |
|
|
|
<view class="remove-image" @click.stop="removeInage(index)" v-if="showDelete"> |
|
|
|
<text class="lf-iconfont lf-icon-shanchu"></text> |
|
|
|
<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> |
|
|
|
|
|
|
|
@ -40,15 +60,76 @@ |
|
|
|
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 { |
|
|
|
image_list: [] |
|
|
|
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 |
|
|
|
} |
|
|
|
}, |
|
|
|
onLoad(){ |
|
|
|
|
|
|
|
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: { |
|
|
|
// 上传凭证图片 |
|
|
|
@ -76,14 +157,16 @@ |
|
|
|
overstep = true; |
|
|
|
} |
|
|
|
}) |
|
|
|
this.image_list.push(...image_list); |
|
|
|
if(overstep){ |
|
|
|
uni.showModal({ |
|
|
|
title: '温馨提示', |
|
|
|
content: '您上传的图片含有超出10M大小的限制,请优化大小后再上传!', |
|
|
|
showCancel: false |
|
|
|
}) |
|
|
|
}else{ |
|
|
|
this.init(); // TODO 优化每次上传都会造成重新初始化导致闪烁 |
|
|
|
} |
|
|
|
this.image_list.push(...image_list); |
|
|
|
} |
|
|
|
}) |
|
|
|
}, |
|
|
|
@ -99,11 +182,213 @@ |
|
|
|
}, |
|
|
|
// 移除图片 |
|
|
|
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) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -113,14 +398,14 @@ |
|
|
|
.upload-images{ |
|
|
|
display: flex; |
|
|
|
flex-wrap: wrap; |
|
|
|
margin-top: 30rpx; |
|
|
|
margin-bottom: 18rpx; |
|
|
|
.upload-image-item{ |
|
|
|
width: 220rpx; |
|
|
|
height: 220rpx; |
|
|
|
// width: 220rpx; |
|
|
|
// height: 220rpx; |
|
|
|
background: #DDDDDD; |
|
|
|
position: relative; |
|
|
|
margin-right: 12rpx; |
|
|
|
// margin-right: 12rpx; |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
&:nth-child(3n){ |
|
|
|
margin-right: 0rpx; |
|
|
|
} |
|
|
|
@ -151,4 +436,71 @@ |
|
|
|
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> |