robby-image-upload.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <template>
  2. <view class="imageUploadContainer">
  3. <view class="imageUploadList">
  4. <view class="imageItem" v-bind:key="index" v-for="(path,index) in imageListData">
  5. <image :src="path" :class="{'dragging':isDragging(index)}" draggable="true" @tap="previewImage" :data-index="index" @touchstart="start" @touchmove.stop.prevent="move" @touchend="stop"></image>
  6. <view v-if="isShowDel" class="imageDel" @tap="deleteImage" :data-index="index">x</view>
  7. </view>
  8. <view v-if="isShowAdd" class="imageUpload" @tap="selectImage">+</view>
  9. </view>
  10. <image v-if="showMoveImage" class="moveImage" :style="{left:posMoveImageLeft, top:posMoveImageTop}" :src="moveImagePath"></image>
  11. </view>
  12. </template>
  13. <script>
  14. export default {
  15. name:'robby-image-upload',
  16. props: ['value','enableDel','enableAdd','enableDrag','serverUrl','formData','header', 'limit','fileKeyName','showUploadProgress','serverUrlDeleteImage'],
  17. data() {
  18. return {
  19. imageBasePos:{
  20. x0: -1,
  21. y0: -1,
  22. w:-1,
  23. h:-1,
  24. },
  25. showMoveImage: false,
  26. moveImagePath: '',
  27. moveLeft: 0,
  28. moveTop: 0,
  29. deltaLeft: 0,
  30. deltaTop: 0,
  31. dragIndex: null,
  32. targetImageIndex: null,
  33. imageList: [],
  34. isDestroyed: false
  35. }
  36. },
  37. mounted: function(){
  38. this.imageList = this.value
  39. if(this.showUploadProgress === false){
  40. this.showUploadProgress = false
  41. }else{
  42. this.showUploadProgress = true
  43. }
  44. },
  45. destroyed: function(){
  46. this.isDestroyed = true
  47. },
  48. computed:{
  49. imageListData: function(){
  50. if(this.value){
  51. return this.value
  52. }
  53. },
  54. posMoveImageLeft: function(){
  55. return this.moveLeft + 'px'
  56. },
  57. posMoveImageTop: function(){
  58. return this.moveTop + 'px'
  59. },
  60. isShowDel: function(){
  61. if(this.enableDel === false){
  62. return false
  63. }else{
  64. return true
  65. }
  66. },
  67. isShowAdd: function(){
  68. if(this.enableAdd === false){
  69. return false
  70. }
  71. if(this.limit && this.imageList.length >= this.limit){
  72. return false
  73. }
  74. return true
  75. },
  76. isDragable: function(){
  77. if(this.enableDrag === false){
  78. return false
  79. }else{
  80. return true
  81. }
  82. }
  83. },
  84. methods:{
  85. selectImage: function(){
  86. var _self = this
  87. if(!_self.imageList){
  88. _self.imageList = []
  89. }
  90. uni.chooseImage({
  91. count: _self.limit ? (_self.limit - _self.imageList.length) : 999,
  92. success: function(e){
  93. var imagePathArr = e.tempFilePaths
  94. //如果设置了limit限制,在web上count参数无效,这里做判断控制选择的数量是否合要求
  95. //在非微信小程序里,虽然可以选多张,但选择的结果会被截掉
  96. //在app里,会自动做选择数量的限制
  97. if(_self.limit){
  98. var availableImageNumber = _self.limit - _self.imageList.length
  99. if(availableImageNumber < imagePathArr.length){
  100. uni.showToast({
  101. title: '图片总数限制为'+_self.limit+'张,当前还可以选'+availableImageNumber+'张',
  102. icon:'none',
  103. mask: false,
  104. duration: 2000
  105. });
  106. return
  107. }
  108. }
  109. //检查服务器地址是否设置,设置即表示图片要上传到服务器
  110. if(_self.serverUrl){
  111. uni.showToast({
  112. title: '上传进度:0/' + imagePathArr.length,
  113. icon: 'none',
  114. mask: false
  115. });
  116. var remoteIndexStart = _self.imageList.length - imagePathArr.length
  117. var promiseWorkList = []
  118. var keyname = (_self.fileKeyName ? _self.fileKeyName : 'upload-images')
  119. var completeImages = 0
  120. for(let i=0; i<imagePathArr.length;i++){
  121. promiseWorkList.push(new Promise((resolve, reject)=>{
  122. let remoteUrlIndex = remoteIndexStart + i
  123. uni.uploadFile({
  124. url:_self.serverUrl,
  125. fileType: 'image',
  126. header: _self.header,
  127. formData:_self.formData,
  128. filePath: imagePathArr[i],
  129. name: keyname,
  130. success: function(res){
  131. if(res.statusCode === 200){
  132. if(_self.isDestroyed){
  133. return
  134. }
  135. completeImages ++
  136. if(_self.showUploadProgress){
  137. uni.showToast({
  138. title: '上传进度:' + completeImages + '/' + imagePathArr.length,
  139. icon: 'none',
  140. mask: false,
  141. duration: 500
  142. });
  143. }
  144. console.log('success to upload image: ' + res.data)
  145. resolve(res.data)
  146. }else{
  147. console.log('fail to upload image:'+res.data)
  148. reject('fail to upload image:' + remoteUrlIndex)
  149. }
  150. },
  151. fail: function(res){
  152. console.log('fail to upload image:'+res)
  153. reject('fail to upload image:' + remoteUrlIndex)
  154. }
  155. })
  156. }))
  157. }
  158. Promise.all(promiseWorkList).then((result)=>{
  159. if(_self.isDestroyed){
  160. return
  161. }
  162. for(let i=0; i<result.length;i++){
  163. _self.imageList.push(result[i])
  164. }
  165. _self.$emit('add', {
  166. currentImages: imagePathArr,
  167. allImages: _self.imageList
  168. })
  169. _self.$emit('input', _self.imageList)
  170. })
  171. }else{
  172. for(let i=0; i<imagePathArr.length;i++){
  173. _self.imageList.push(imagePathArr[i])
  174. }
  175. _self.$emit('add', {
  176. currentImages: imagePathArr,
  177. allImages: _self.imageList
  178. })
  179. _self.$emit('input', _self.imageList)
  180. }
  181. }
  182. })
  183. },
  184. deleteImage: function(e){
  185. var imageIndex = e.currentTarget.dataset.index
  186. var deletedImagePath = this.imageList[imageIndex]
  187. this.imageList.splice(imageIndex, 1)
  188. //检查删除图片的服务器地址是否设置,如果设置则调用API,在服务器端删除该图片
  189. if(this.serverUrlDeleteImage){
  190. uni.request({
  191. url: this.serverUrlDeleteImage,
  192. method: 'GET',
  193. data: {
  194. imagePath: deletedImagePath
  195. },
  196. success: res => {
  197. console.log(res.data)
  198. }
  199. });
  200. }
  201. this.$emit('delete',{
  202. currentImage: deletedImagePath,
  203. allImages: this.imageList
  204. })
  205. this.$emit('input', this.imageList)
  206. },
  207. previewImage: function(e){
  208. var imageIndex = e.currentTarget.dataset.index
  209. uni.previewImage({
  210. current: this.imageList[imageIndex],
  211. indicator: "number",
  212. loop: "true",
  213. urls:this.imageList
  214. })
  215. },
  216. initImageBasePos: function(){
  217. let paddingRate = 0.024
  218. var _self = this
  219. //计算图片基准位置
  220. uni.getSystemInfo({
  221. success: function(obj) {
  222. let screenWidth = obj.screenWidth
  223. let leftPadding = Math.ceil(paddingRate * screenWidth)
  224. let imageWidth = Math.ceil((screenWidth - 2*leftPadding)/4)
  225. _self.imageBasePos.x0 = leftPadding
  226. _self.imageBasePos.w = imageWidth
  227. _self.imageBasePos.h = imageWidth
  228. }
  229. })
  230. },
  231. findOverlapImage: function(posX, posY){
  232. let rows = Math.floor((posX-this.imageBasePos.x0)/this.imageBasePos.w)
  233. let cols = Math.floor((posY-this.imageBasePos.y0)/this.imageBasePos.h)
  234. let indx = cols*4 + rows
  235. return indx
  236. },
  237. isDragging: function(indx){
  238. return this.dragIndex === indx
  239. },
  240. start: function(e){
  241. console.log(this.isDragable)
  242. if(!this.isDragable){
  243. return
  244. }
  245. this.dragIndex = e.currentTarget.dataset.index
  246. this.moveImagePath = this.imageList[this.dragIndex]
  247. this.showMoveImage = true
  248. //计算纵向图片基准位置
  249. if(this.imageBasePos.y0 === -1){
  250. this.initImageBasePos()
  251. let basePosY = Math.floor(this.dragIndex / 4) * this.imageBasePos.h
  252. let currentImageOffsetTop = e.currentTarget.offsetTop
  253. this.imageBasePos.y0 = currentImageOffsetTop - basePosY
  254. }
  255. //设置选中图片当前左上角的坐标
  256. this.moveLeft = e.target.offsetLeft
  257. this.moveTop = e.target.offsetTop
  258. },
  259. move: function(e){
  260. if(!this.isDragable){
  261. return
  262. }
  263. const touch = e.touches[0]
  264. this.targetImageIndex = this.findOverlapImage(touch.clientX, touch.clientY)
  265. //初始化deltaLeft/deltaTop
  266. if(this.deltaLeft === 0){
  267. this.deltaLeft = touch.clientX - this.moveLeft
  268. this.deltaTop = touch.clientY - this.moveTop
  269. }
  270. //设置移动图片位置
  271. this.moveLeft = touch.clientX - this.deltaLeft
  272. this.moveTop = touch.clientY - this.deltaTop
  273. },
  274. stop: function(e){
  275. if(!this.isDragable){
  276. return
  277. }
  278. if(this.dragIndex !== null && this.targetImageIndex !== null){
  279. if(this.targetImageIndex<0){
  280. this.targetImageIndex = 0
  281. }
  282. if(this.targetImageIndex>=this.imageList.length){
  283. this.targetImageIndex = this.imageList.length-1
  284. }
  285. //交换图片
  286. if(this.dragIndex !== this.targetImageIndex){
  287. this.imageList[this.dragIndex] = this.imageList[this.targetImageIndex]
  288. this.imageList[this.targetImageIndex] = this.moveImagePath
  289. }
  290. }
  291. this.dragIndex = null
  292. this.targetImageIndex = null
  293. this.deltaLeft = 0
  294. this.deltaTop = 0
  295. this.showMoveImage = false
  296. this.$emit('input', this.imageList)
  297. }
  298. }
  299. }
  300. </script>
  301. <style>
  302. .imageUploadContainer{
  303. padding: 10upx 5upx;
  304. margin: 10upx 5upx;
  305. }
  306. .dragging{
  307. transform: scale(1.2)
  308. }
  309. .imageUploadList{
  310. display: flex;
  311. flex-wrap: wrap;
  312. }
  313. .imageItem, .imageUpload{
  314. width: 160upx;
  315. height: 160upx;
  316. margin: 10upx;
  317. }
  318. .imageDel{
  319. position: relative;
  320. left: 120upx;
  321. bottom: 165upx;
  322. background-color: rgba(0,0,0,0.5);
  323. width: 36upx;
  324. text-align: center;
  325. line-height: 35upx;
  326. border-radius: 17upx;
  327. color: white;
  328. font-size: 30upx;
  329. padding-bottom: 2upx;
  330. }
  331. .imageItem image, .moveImage{
  332. width: 160upx;
  333. height: 160upx;
  334. border-radius: 8upx;
  335. }
  336. .imageUpload{
  337. line-height: 130upx;
  338. text-align: center;
  339. font-size: 150upx;
  340. color: #D9D9D9;
  341. border: 1px solid #D9D9D9;
  342. border-radius: 8upx;
  343. }
  344. .moveImage{
  345. position: absolute;
  346. }
  347. </style>