imageUpload.vue 11 KB

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