htz-image-upload.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. <template>
  2. <view class="htz-image-upload-list">
  3. <view class="htz-image-upload-Item" v-for="(item,index) in uploadLists" :key="index">
  4. <image class="img" :src="item" @click="imgPreview(index)"></image>
  5. <view class="htz-image-upload-Item-del" v-if="remove" @click="imgDel(index)">×</view>
  6. </view>
  7. <view class="htz-image-upload-Item htz-image-upload-Item-add" v-if="uploadLists.length<max" @click="imgAdd">
  8. +
  9. </view>
  10. </view>
  11. </template>
  12. <script>
  13. export default {
  14. name: 'htz-image-upload',
  15. props: {
  16. max: { //展示图片最大值
  17. type: Number,
  18. default: 1,
  19. },
  20. chooseNum: { //选择图片数
  21. type: Number,
  22. default: 9,
  23. },
  24. name: { //发到后台的文件参数名
  25. type: String,
  26. default: 'file',
  27. },
  28. remove: { //是否展示删除按钮
  29. type: Boolean,
  30. default: true,
  31. },
  32. sourceType: { //选择照片来源 【ps:H5就别费劲了,设置了也没用。不是我说的,官方文档就这样!!!】
  33. type: Array,
  34. default: () => ['album', 'camera'],
  35. },
  36. action: { //上传地址
  37. type: String,
  38. default: '',
  39. },
  40. headers: { //上传的请求头部
  41. type: Object,
  42. default: () => {},
  43. },
  44. formData: { //HTTP 请求中其他额外的 form data
  45. type: Object,
  46. // default: () => {},
  47. },
  48. compress: { //是否需要压缩
  49. type: Boolean,
  50. default: true,
  51. },
  52. quality: { //压缩质量,范围0~100
  53. type: Number,
  54. default: 80,
  55. },
  56. value: { //受控图片列表
  57. type: Array,
  58. default: () => [],
  59. },
  60. uploadSuccess: {
  61. default: (res) => {
  62. return {
  63. success: false,
  64. url: ''
  65. }
  66. },
  67. }
  68. },
  69. data() {
  70. return {
  71. uploadLists: [],
  72. }
  73. },
  74. mounted: function() {
  75. this.$nextTick(function() {
  76. this.uploadLists = this.value;
  77. });
  78. },
  79. watch: {
  80. value(val, oldVal) {
  81. console.log('value',val, oldVal)
  82. this.uploadLists = val;
  83. },
  84. },
  85. methods: {
  86. imgDel(index) {
  87. uni.showModal({
  88. title: '提示',
  89. content: '您确定要删除么?',
  90. success: (res) => {
  91. if (res.confirm) {
  92. // this.uploadLists.splice(index, 1)
  93. // this.$emit("input", this.uploadLists);
  94. // this.$emit("imgDelete", this.uploadLists);
  95. let delUrl=this.uploadLists[index]
  96. this.uploadLists.splice(index, 1)
  97. this.$emit("input", this.uploadLists);
  98. this.$emit("imgDelete", {del:delUrl,tempFilePaths:this.uploadLists});
  99. } else if (res.cancel) {}
  100. }
  101. });
  102. },
  103. imgPreview(index) {
  104. console.log('imgPreview', index)
  105. console.log(this.uploadLists)
  106. uni.previewImage({
  107. urls: this.uploadLists,
  108. current: index,
  109. loop: true,
  110. });
  111. },
  112. imgAdd() {
  113. let nowNum = Math.abs(this.uploadLists.length - this.max);
  114. let thisNum = (this.chooseNum > nowNum ? nowNum : this.chooseNum) //可选数量
  115. // #ifdef APP-PLUS
  116. if (this.sourceType.length > 1) {
  117. uni.showActionSheet({
  118. itemList: ['从手机相册选择', '取消'],
  119. success: (res) => {
  120. if (res.tapIndex == 0) {
  121. this.appGallery(thisNum);
  122. } else if (res.tapIndex == 2) {
  123. this.appCamera();
  124. // this.appGallery(thisNum);
  125. }
  126. },
  127. fail: (res) => {
  128. console.log(res.errMsg);
  129. }
  130. });
  131. }
  132. if (this.sourceType.length == 1 && this.sourceType.indexOf('album') > -1) {
  133. this.appGallery(thisNum);
  134. }
  135. if (this.sourceType.length == 1 && this.sourceType.indexOf('camera') > -1) {
  136. this.appCamera();
  137. }
  138. // #endif
  139. //#ifndef APP-PLUS
  140. uni.chooseImage({
  141. count: thisNum,
  142. sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
  143. sourceType: this.sourceType,
  144. success: (res) => {
  145. this.chooseSuccessMethod(res.tempFilePaths)
  146. //console.log('tempFiles', res)
  147. // if (this.action == '') { //未配置上传路径
  148. // this.$emit("chooseSuccess", res.tempFilePaths);
  149. // } else {
  150. if (this.compress && (res.tempFiles[0].size / 1024 > 1025)) { //设置了需要压缩 并且 文件大于1M,进行压缩上传
  151. this.imgCompress(res.tempFilePaths);
  152. console.log('压缩文件')
  153. } else {
  154. this.imgUpload(res.tempFilePaths);
  155. console.log('直接上传')
  156. }
  157. // }
  158. }
  159. });
  160. // #endif
  161. },
  162. appCamera() {
  163. var cmr = plus.camera.getCamera();
  164. var res = cmr.supportedImageResolutions[0];
  165. var fmt = cmr.supportedImageFormats[0];
  166. //console.log("Resolution: " + res + ", Format: " + fmt);
  167. cmr.captureImage((path)=> {
  168. //alert("Capture image success: " + path);
  169. this.chooseSuccessMethod([path])
  170. },
  171. (error) =>{
  172. //alert("Capture image failed: " + error.message);
  173. console.log("Capture image failed: " + error.message)
  174. }, {
  175. resolution: res,
  176. format: fmt
  177. }
  178. );
  179. },
  180. appGallery(maxNum) {
  181. plus.gallery.pick((res) => {
  182. this.chooseSuccessMethod(res.files)
  183. }, function(e) {
  184. //console.log("取消选择图片");
  185. }, {
  186. filter: "image",
  187. multiple: true,
  188. maximum: maxNum
  189. });
  190. },
  191. chooseSuccessMethod(filePaths) {
  192. if (this.action == '') { //未配置上传路径
  193. this.$emit("chooseSuccess", filePaths);
  194. } else {
  195. console.log(this.compress,"-------------压缩")
  196. //#ifdef APP-PLUS
  197. this.imgUpload(filePaths);
  198. //hbuilder运行手机调试基座的时候去掉下边
  199. // if (this.compress) { //设置了需要压缩
  200. // this.imgCompress(filePaths);
  201. // console.log('11111111111111111')
  202. // } else {
  203. // this.imgUpload(filePaths);
  204. // console.log('oooooooooooooooooooooooooo')
  205. // }
  206. //#endif
  207. }
  208. },
  209. imgCompress(tempFilePaths) {
  210. console.log('CESJOJO')
  211. uni.showLoading({
  212. title: '压缩中...'
  213. });
  214. let compressImgs = [];
  215. let results = [];
  216. tempFilePaths.forEach((item, index) => {
  217. console.log(tempFilePaths,'========================')
  218. compressImgs.push(new Promise((resolve, reject) => {
  219. uni.compressImage({
  220. src: item,
  221. quality: this.quality,
  222. success: res => {
  223. //console.log('compressImage', res.tempFilePath)
  224. results.push(res.tempFilePath);
  225. console.log(1,'========================')
  226. resolve(res.tempFilePath);
  227. },
  228. fail: (err) => {
  229. //console.log(err.errMsg);
  230. reject(err);
  231. },
  232. complete: () => {
  233. //uni.hideLoading();
  234. }
  235. })
  236. this.canvasDataURL(item, {
  237. quality: this.quality / 100
  238. }, (base64Codes) => {
  239. //this.imgUpload(base64Codes);
  240. results.push(base64Codes);
  241. resolve(base64Codes);
  242. })
  243. }))
  244. })
  245. Promise.all(compressImgs) //执行所有需请求的接口
  246. .then((results) => {
  247. uni.hideLoading();
  248. this.imgUpload(results);
  249. })
  250. .catch((res, object) => {
  251. uni.hideLoading();
  252. });
  253. },
  254. imgUpload(tempFilePaths) {
  255. // if (this.action == '') {
  256. // uni.showToast({
  257. // title: '未配置上传地址',
  258. // icon: 'none',
  259. // duration: 2000
  260. // });
  261. // return false;
  262. // }
  263. console.log("测试上传次数")
  264. uni.showLoading({
  265. title: '上传中'
  266. });
  267. //console.log('imgUpload',tempFilePaths)
  268. let uploadImgs = [];
  269. tempFilePaths.forEach((item, index) => {
  270. uploadImgs.push(new Promise((resolve, reject) => {
  271. const uploadTask = uni.uploadFile({
  272. url: this.action, //仅为示例,非真实的接口地址
  273. filePath: item,
  274. name: this.name,
  275. fileType: 'image',
  276. formData: {
  277. file_type:'img'
  278. },
  279. header: this.headers,
  280. success: (uploadFileRes) => {
  281. console.log('first-------------------')
  282. // uni.hideLoading();
  283. //console.log(typeof this.uploadSuccess)
  284. //console.log('')
  285. if (typeof this.uploadSuccess == 'function') {
  286. if (this.uploadSuccess(uploadFileRes).success) {
  287. this.value.push(this.uploadSuccess(uploadFileRes).url)
  288. this.$emit("input", this.uploadLists);
  289. }
  290. console.log('成功')
  291. }
  292. resolve(uploadFileRes);
  293. this.$emit("uploadSuccess", uploadFileRes);
  294. },
  295. fail: (err) => {
  296. console.log(err);
  297. //uni.hideLoading();
  298. reject(err);
  299. this.$emit("uploadFail", err);
  300. },
  301. complete: () => {
  302. //uni.hideLoading();
  303. }
  304. });
  305. }))
  306. })
  307. Promise.all(uploadImgs) //执行所有需请求的接口
  308. .then((results) => {
  309. uni.hideLoading();
  310. })
  311. .catch((res, object) => {
  312. uni.hideLoading();
  313. this.$emit("uploadFail", res);
  314. });
  315. // uploadTask.onProgressUpdate((res) => {
  316. // //console.log('',)
  317. // uni.showLoading({
  318. // title: '上传中' + res.progress + '%'
  319. // });
  320. // if (res.progress == 100) {
  321. // uni.hideLoading();
  322. // }
  323. // });
  324. },
  325. canvasDataURL(path, obj, callback) {
  326. var img = new Image();
  327. img.src = path;
  328. img.onload = function() {
  329. var that = this;
  330. // 默认按比例压缩
  331. var w = that.width,
  332. h = that.height,
  333. scale = w / h;
  334. w = obj.width || w;
  335. h = obj.height || (w / scale);
  336. var quality = 0.8; // 默认图片质量为0.8
  337. //生成canvas
  338. var canvas = document.createElement('canvas');
  339. var ctx = canvas.getContext('2d');
  340. // 创建属性节点
  341. var anw = document.createAttribute("width");
  342. anw.nodeValue = w;
  343. var anh = document.createAttribute("height");
  344. anh.nodeValue = h;
  345. canvas.setAttributeNode(anw);
  346. canvas.setAttributeNode(anh);
  347. ctx.drawImage(that, 0, 0, w, h);
  348. // 图像质量
  349. if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
  350. quality = obj.quality;
  351. }
  352. // quality值越小,所绘制出的图像越模糊
  353. var base64 = canvas.toDataURL('image/jpeg', quality);
  354. // 回调函数返回base64的值
  355. callback(base64);
  356. }
  357. },
  358. }
  359. }
  360. </script>
  361. <style>
  362. .htz-image-upload-list {
  363. display: flex;
  364. flex-wrap: wrap;
  365. }
  366. .htz-image-upload-Item {
  367. width: 200rpx;
  368. height:200rpx;
  369. margin: 13rpx;
  370. border-radius: 10rpx;
  371. line-height: 180rpx;
  372. position: relative;
  373. }
  374. .htz-image-upload-Item:first-child{
  375. margin-left: 5upx!important;
  376. }
  377. .htz-image-upload-Item:nth-child(4n){
  378. margin-left: 5upx!important;
  379. }
  380. .htz-image-upload-Item:nth-child(3n){
  381. margin-right: 5upx!important;
  382. }
  383. .htz-image-upload-Item image {
  384. width: 100%;
  385. height: 100%;
  386. border-radius: 10rpx;
  387. border: 1px solid #FF0000;
  388. }
  389. .htz-image-upload-Item-add {
  390. font-size: 105rpx;
  391. /* line-height: 160rpx; */
  392. text-align: center;
  393. /* border: 1px dashed #d9d9d9;
  394. color: #d9d9d9; */
  395. color: #e61916;
  396. border: 1px dashed #e61916;
  397. }
  398. .htz-image-upload-Item-del {
  399. background-color: #f5222d;
  400. font-size: 24rpx;
  401. position: absolute;
  402. width: 35rpx;
  403. height: 35rpx;
  404. line-height: 35rpx;
  405. text-align: center;
  406. top: -12rpx;
  407. right: -12rpx;
  408. border-radius: 50%;
  409. z-index: 100;
  410. color: #fff;
  411. }
  412. </style>