首页 > 其他分享 >移动端 element + vue 二次封装上传图片的组件

移动端 element + vue 二次封装上传图片的组件

时间:2022-10-08 10:45:54浏览次数:59  
标签:el vue 封装 url fileList element item file return

功能:支持预览、长按图片保存、删除

项目中使用:

 1 <el-form-item label="上传照片" class="ml30">
 2          <e-upload-image
 3             v-model="image"
 4             @change="fileList => handleImageChange(fileList)"
 5             tip="(注:最多上传10张图片,每张大小不超过20M)"
 6             :acceptData="{ maxsize: 1024 * 1024 * 20, maxsize_text: '20M' }"
 7           >
 8        </e-upload-image>
 9 </el-form-item>
10 
11 
12 handleImageChange(fileList) {
13       this.image = fileList
14 }

 

注册组件 src/components/index.js

1 import EUploadImage from './EUploadImage'
2 
3 export default {
4    install(Vue) {
5      Vue.component('EUploadImage', EUploadImage)
6    }
7 }

 

main.js引入自定义组件:

import Components from '@/components/index'
Vue.use(Components)

 

二次封装好的上传图片的组件 EUploadImage.vue:

  1 <template>
  2   <!-- 上传图片,支持预览、下载、删除 -->
  3   <div class="any-upload-image">
  4     <el-upload
  5       :action="uploadApi()"
  6       list-type="picture-card"
  7       :data="acceptData"
  8       :limit="limit"
  9       multiple
 10       :accept="accept"
 11       :file-list="fileList"
 12       :on-exceed="handleExceed"
 13       :on-success="(response, file, fileList) => handleSuccess(response, file, fileList)"
 14       :before-upload="file => beforeUpload(file)"
 15     >
 16       <i class="el-icon-plus"></i>
 17       <div slot="file" slot-scope="{ file }" class="file-container">
 18         <img class="el-upload-list__item-thumbnail" :src="getFileAccessHttpUrl(file.url)" alt="" />
 19         <span class="el-upload-list__item-actions">
 20           <!-- 预览 -->
 21           <span class="el-upload-list__item-preview" @click="handlePreview(file)">
 22             <i class="el-icon-zoom-in"></i>
 23           </span>
 24           <!-- 删除 -->
 25           <span v-if="!acceptData.disabledRemove" class="el-upload-list__item-delete" @click="handleRemove(file)">
 26             <i class="el-icon-delete"></i>
 27           </span>
 28         </span>
 29       </div>
 30     </el-upload>
 31 
 32     <!-- 预览图片 -->
 33     <div v-show="previewVisible" class="custom-box-img" :class="typeof dark !== 'undefined' ? 'dark' : ''">
 34       <i class="el-icon-close" @click="previewVisible = false"></i>
 35       <div class="img-con custom-mobile-scroll">
 36         <img :src="imageUrl" @touchstart="down(imageUrl, currentFile)" @touchmove="up" @touchend="up" />
 37         <!-- 保存 -->
 38         <div v-if="!acceptData.disabledDownload" class="download-icon" @click="handleDownload(currentFile)">
 39           <el-button type="primary" size="mini" icon="el-icon-download">保存</el-button>
 40         </div>
 41       </div>
 42     </div>
 43     <!-- 提示 -->
 44     <el-row class="limit-tip">
 45       <span>{{ tip }}</span>
 46     </el-row>
 47   </div>
 48 </template>
 49 
 50  
 51 <script>
 52 const uidGenerator = () => {
 53   return '-' + parseInt(Math.random() * 10000 + 1, 10)
 54 }
 55 
 56 
 57 const getFileName = path => {
 58   if (path.lastIndexOf('\\') >= 0) {
 59     let reg = new RegExp('\\\\', 'g')
 60     path = path.replace(reg, '/')
 61   }
 62   return path.substring(path.lastIndexOf('/') + 1)
 63 }
 64 
 65 export default {
 66   name: 'EUploadImage',
 67   props: {
 68     value: {
 69       type: [String, Array],
 70       required: false
 71     },
 72 
 73     // 文件类型
 74     accept: {
 75       type: String,
 76       default: 'image/png, image/jpg, image/gif, image/jpeg'
 77     },
 78 
 79     // 上传时附带的额外参数
 80     acceptData: {
 81       type: Object,
 82       default: () => {
 83         return {
 84           maxsize: 1024 * 1024 * 20,
 85           maxsize_text: '20M',
 86           disabledDownload: false,
 87           disabledRemove: false
 88         }
 89       }
 90     },
 91 
 92     // 提示
 93     tip: {
 94       type: String,
 95       default: ''
 96     },
 97 
 98     // 最大数量
 99     limit: {
100       type: Number,
101       default: 10
102     },
103 
104     // 黑色模式
105     dark: {}
106   },
107 
108   watch: {
109     value: {
110       handler(val, oldValue) {
111         this.initFileList(val)
112       },
113       // 立刻执行handler
114       immediate: true
115     }
116   },
117 
118   data() {
119     return {
120       previewVisible: false, // 预览图片弹框显隐
121       imageUrl: '', // 预览图片的地址
122       fileList: [],
123       currentFile: ''
124     }
125   },
126 
127   methods: {
128     initFileList(array) {
129       if (!array || array.length == 0) {
130         this.fileList = []
131         return
132       }
133 
134       let fileList = []
135       let arr = array
136       for (let a = 0; a < arr.length; a++) {
137         let url = this.getFileAccessHttpUrl(arr[a].url)
138         fileList.push({
139           uid: uidGenerator(),
140           name: getFileName(arr[a].name),
141           status: 'done',
142           url: url,
143           response: {
144             status: 'history',
145             message: url
146           }
147         })
148       }
149       this.fileList = fileList
150     },
151 
152     getFileAccessHttpUrl(url) {
153       if (url.indexOf('/dispatch') > -1) {
154         return url
155       } else {
156         return '/dispatch' + url
157       }
158     },
159 
160     // 上传文件地址
161     uploadApi() {
162       return '/dispatch/upload'
163     },
164 
165     // 其他附件上传大小限制处理
166     beforeUpload(file) {
167       const isLessThanMaxSize = file.size < this.acceptData.maxsize
168       if (!isLessThanMaxSize) {
169         this.$toast(`上传文件大小不能超过${this.acceptData.maxsize_text}!`)
170       }
171       return isLessThanMaxSize
172     },
173 
174     // 文件个数限制
175     handleExceed() {
176       this.$toast(`当前限制最多上传 ${this.limit} 个文件!`)
177     },
178 
179     // 上传图片成功
180     handleSuccess(response, file, fileList) {
181       if (response.code == 200) {
182         this.$toast('上传成功')
183         let attachment = fileList.map(item => {
184           let url = ''
185           if (item.response) {
186             if (item.response.data) {
187               url =  item.response.data || ''
188             } else {
189               url = item.url || ''
190             }
191           } else {
192             url = item.url || ''
193           }
194           return { name: item.name, url: url, uid: item.uid }
195         })
196         this.fileList = attachment
197         // 回显给业务,不携带 /dispatch 前缀
198         this.$emit('change', this.fileList)
199       }
200    },
201 
202     // 图片预览
203     handlePreview(file) {
204       // 预览时要携带 /dispatch 前缀
205       this.imageUrl = this.getFileAccessHttpUrl(file.url)
206       this.previewVisible = true
207       this.currentFile = file
208     },
209 
210     // 下载
211     handleDownload(file) {
212       const link = document.createElement('a')
213       link.href = file.url
214       link.download = file.name
215       link.click()
216       URL.revokeObjectURL(link.href)
217     },
218 
219     // 删除
220     handleRemove(file) {
221       let index
222       this.fileList.find((item, idx) => {
223         if (item.uid === file.uid) {
224           index = idx
225           return
226         }
227       })
228       if (typeof index !== 'undefined') {
229         this.fileList.splice(index, 1)
230       }
231       // 回显给业务
232       this.$emit('change', this.fileList)
233    },
234 
235     // 长按保存图片
236     down(url, file) {
237       this.time = setTimeout(() => {
238         // 这里就按照chrome等新版浏览器来处理
239         const link = document.createElement('a')
240         link.href = url
241         link.setAttribute('download', file.name)
242         link.click()
243       }, 1000)
244    },
245 
246     up() {
247       clearTimeout(this.time)
248     }
249   }
250 
251 }
252 
253 </script>
254 
255  
256 <style lang="scss" scoped>
257 .custom-box-img {
258   position: fixed;
259   z-index: 1002;
260   left: 0;
261   right: 0;
262   top: 1rem;
263   display: flex;
264   bottom: 0;
265   background-color: #ffffff;
266   align-items: center;
267   &.dark {
268     background-color: #000000;
269     .el-icon-close {
270       color: #ffffff;
271     }
272   }
273 
274   .el-icon-close {
275     position: absolute;
276     z-index: 1003;
277     right: 10px;
278     top: 10px;
279     font-size: 18px;
280     color: #000000;
281     &:before {
282       font-size: 30px;
283     }
284   }
285 
286   .img-con {
287     width: 100%;
288     text-align: center;
289     overflow: auto;
290     img {
291       max-width: 100%;
292       height: auto;
293       vertical-align: middle;
294     }
295 
296     video {
297       vertical-align: middle;
298     }
299   }
300 
301   .download-icon {
302     margin: 0 auto;
303     z-index: 1003;
304     color: #ffffff;
305     font-size: 18px;
306     margin-top: 10px;
307     ::v-deep .el-icon-download:before {
308       font-size: 20px;
309       color: #ffffff;
310       vertical-align: middle;
311     }
312 
313     .el-button--primary.is-disabled {
314       background-color: #409eff;
315       border-color: #409eff;
316     }
317 
318   }
319 
320   .el-carousel__container {
321     height: calc(100vh - 26px);
322   }
323 
324   .el-carousel__arrow {
325     background-color: rgba(31, 45, 61, 0.5);
326     line-height: 36px;
327   }
328 
329   .el-carousel__indicators--outside {
330     bottom: 0;
331     line-height: 26px;
332   }
333 }
334 
335 /deep/ .el-upload-list--picture-card .el-upload-list__item {
336   width: 2rem;
337   height: 2rem;
338 }
339 
340 .file-container,
341 /deep/ .el-upload-list--picture-card .el-upload-list__item-thumbnail {
342   width: 100%;
343   height: 100%;
344 }

 

标签:el,vue,封装,url,fileList,element,item,file,return
From: https://www.cnblogs.com/buluzombie/p/16768222.html

相关文章

  • Java基础——封装
    一、什么是封装封装就是对象的属性和操作结合为一个整体,和并尽可能隐藏对象内部的细节。二、为什么要封装1.为了安全,提高程序的复用性,封装就是该漏的,该藏得藏。2.如果......
  • 狂神说学习笔记:Vue
    Vue1、概述Vue(读音/vjuː/,类似于view)是一套用于构建用户界面的渐进式JavaScript框架,发布于2014年2月。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用......
  • Vue全局公共服务类mixin
    首先,简单介绍下mixin:Mixin是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类Mixin类通常作为功能模块使用,在需要......
  • Vue2.0使用elementUI
    引入elementUI第一种:全部引入安装elementuinpminstallelement-ui--save1.在main.js中引入2.使用//引入element-uiimportelementUIfrom'element-ui';//引入......
  • Java基础——封装
    一、什么是封装封装就是对象的属性和操作结合为一个整体,和并尽可能隐藏对象内部的细节。二、为什么要封装1.为了安全,提高程序的复用性,封装就是该漏的,该藏得藏。2.如果......
  • vue3 导出excel文件
    在中后台页面的开发中少不了excel表格导出功能,以此做个记录:1.后端返回下载地址:直接:window.open("文件地址")    或者:window.location.href= "文件地址"......
  • Vue学习(三)
    ajax请求xhr一般不直接使用,层层封装jquery现在基本不用axios通过promise实现对ajax技术的一种封装fetchpromise封装异步操作newpromise(resolve,reject),然后......
  • vueuse 核心api
     供自己学习使用。代码来源于elementplus。后续看情况是否增加说明  isClient  useStorageconstuserPrefer=useStorage<boolean|string>(......
  • 如何理解vue中的v-bind?
    如果你写过vue,对v-bind这个指令一定不陌生。下面我将从源码层面去带大家剖析一下v-bind背后的原理。会从以下几个方面去探索:v-bind关键源码分析v-bind化的属性统一存储在哪......
  • 还在为写.vue文件烦恼吗?快来用dot-vue-cli交互式生成吧!
    写过vue的同学都知道,单文件组件.vue在开发中使用频率是非常高的。如果不想再手写或者CV的话,不妨尝试一下我写的这个小工具,支持交互式生成.vue文件,生成的过程只需要回答一些......