使用vue和element-ui上傳圖片及視頻文件方式
項目使用vue+element-ui,實現(xiàn)了表單多圖上傳圖片,上傳視頻,以及表單圖片回顯,視頻回顯,表格渲染圖片等功能
效果圖
上傳后
圖片可回顯,視頻可播放,,這時候全部緩存在頁面,并沒有提交到后端服務(wù)器,只要到用戶提交的那一步才確定上傳,減低不必要的服務(wù)器開支
圖片上傳
前端緩存base64方便回顯,以及后臺上傳,視頻上傳則使用file類型去上傳(base64對視頻編碼會導(dǎo)致請求參數(shù)過長)
<!-- 描述:圖片上傳, 基于 element-ui 組件 --> <template> <div> <div class="upload"> <div class="img_mode" v-for="(item, index) in path_list" :key="index" :style="'height:' + height + ';width:' + width" @mouseenter.stop="over(index)" @mouseleave="out" > <img :src="item.path" /> <transition name="el-fade-in-linear"> <div v-show="curIndex == index" class="transition-box"> <div class="mask"> <i class="el-icon-delete" @click="remove(index)"></i> <i class="el-icon-zoom-in" @click="enlarge(index)" v-if="bigBox" ></i> </div> </div> </transition> </div> <div class="select_mode" :style="'height:' + height + ';width:' + width" v-if="isLimit" v-loading="load" > <input type="file" @change="change($event)" ref="file" :multiple="isMultiple" /> <img :src="selectImgPath || selectImg" :width="selectImgWidth || '50%'" /> </div> </div> <el-dialog :visible.sync="isShow" center> <div class="big_img_mode"> <img :src="bigImg" /> </div> </el-dialog> </div> </template>
<script> //這里是圖片上傳時候的圖標(biāo),可以自行替換 import selectImg from "../../assets/img/selectedImg.png"; export default { props: { height: String, width: String, // 組件預(yù)覽圖片大小 selectImgPath: String, // 未選擇圖片時顯示的圖片 selectImgWidth: String, bigBox: Boolean, // 是否顯示圖片放大按鈕 /* 多圖上傳 */ isMultiple: Boolean, // 是否可多選圖片 limit_size: Number, // 限制上傳數(shù)量 quality: Number, // 圖片壓縮率 limit: Number, // 圖片超過 (limit * 1024)kb 將進行壓縮 isCompress: Boolean, // 是否開啟圖片壓縮 /* 單圖上傳 */ isChangeUpload: false, // 是否選擇文件后觸發(fā)上傳 action: "", // 單圖上傳路徑, param: "", // 上傳的參數(shù)名 data: Object, // 單圖上傳附帶的參數(shù) success: Function, // 單圖上傳成功后的回調(diào)函數(shù) }, data() { return { selectImg, path_list: [], curIndex: -1, bigImg: "", isShow: false, isLimit: true, load: false, }; }, watch: { path_list() { if (this.path_list.length >= this.limit_size) { this.isLimit = false; } else { this.isLimit = true; } this.load = false; this.curIndex = -1; }, }, mounted() {}, methods: { // 鼠標(biāo)移入 over(index) { this.curIndex = index; }, // 鼠標(biāo)移出 out() { this.curIndex = -1; }, // 選擇圖片 change() { this.load = true; var That = this; let fileList = this.$refs.file.files; if (!fileList.length) { this.load = false; return; } for (var i = 0; i < fileList.length; i++) { var file_temp = fileList[i]; let name = file_temp.name.toLowerCase(); if (!/\.(jpg|jpeg|image|img|png|bmp|)$/.test(name)) { this.$message.warning("請上傳圖片"); this.clean(); } let reader = new FileReader(); //html5讀文件 reader.fileName = file_temp.name; reader.readAsDataURL(file_temp); reader.onload = (data) => { //讀取完畢后調(diào)用接口 // 圖片壓縮 this.canvasDataURL( data.currentTarget.result, { fileSize: data.total, quality: this.quality }, (baseCode) => { if (this.isChangeUpload) { this.changeUpload(baseCode, reader.fileName); } else { this.path_list.push({ path: baseCode, fileName: reader.fileName, }); } } ); }; } this.$emit("change", this.path_list); }, // 移除圖片 remove(index) { this.path_list.splice(index, 1); }, //清空圖片 clean() { this.path_list = []; }, //后臺添加圖片 addPathList(urls) { console.log(urls); let arr = urls.split(","); if (arr.length == 1) { let obj = { path: this.$common.getUrl() + arr[0] }; this.path_list.splice(0, 1, obj); } else { arr.forEach((item, index) => { let obj2 = { path: this.$common.getUrl() + item }; this.path_list.splice(index, 1, obj2); }); } }, //后臺添加圖片 addUrlPathList(urls) { console.log(urls); let arr = urls.split(";"); console.log("數(shù)組" + arr); if (arr.length == 1) { let obj = { path: this.$common.getUrl() + arr[0] }; this.path_list.splice(0, 1, obj); } else { arr.forEach((item, index) => { let obj2 = { path: this.$common.getUrl() + item }; this.path_list.splice(index, 1, obj2); }); } }, //重新圖片 clearImgs() { this.path_list = []; }, // 放大圖片 enlarge(index) { this.isShow = true; this.bigImg = this.path_list[index].path; }, // 單圖上傳 changeUpload(baseCode, fileName) { let formData = new FormData(); formData.append(this.param, baseCode); formData.append("fileName", fileName); for (var item in this.data) { formData.append(item, this.data[item]); } this.$axios .post(this.action, formData) .then((response) => { if (response.data.message == "succ") { this.success(); // 上傳成功后的回調(diào)函數(shù) } this.load = false; }) .catch((e) => { console.log(e); this.load = false; }); }, /** * @uploadPath 上傳的路徑 * @path_list base64 集合, 為空則調(diào)用 this.path_list * @callback 上傳成功后的回調(diào)函數(shù), 返回上傳成功后的路徑 */ upload(uploadPath, path_list, callback) { var formData = new FormData(); if (!path_list) { this.path_list.forEach((item) => { formData.append( "files", this.convertBase64UrlToBlob(item.path), item.fileName ); }); } else { path_list.forEach((item) => { formData.append( "files", this.convertBase64UrlToBlob(item.path), item.fileName ); }); } let headers = { headers: { "Content-Type": "multipart/form-data" } }; this.$axios .post(uploadPath, formData, headers) .then((response) => { if (response.data.message == "succ") { // 回調(diào)函數(shù)返回上傳成功后的路徑 callback("succ", response.data.result); } else { this.$message.error("文件上傳失敗"); callback("error"); } }) .catch((error) => { if (error == "timeout") { this.$message.error("文件上傳超時"); } this.$message.error("文件上傳異常"); console.log(error); callback("error"); }); }, getName() { return this.path_list.fileName; }, // 獲取base64 集合 getPaths() { let paths = []; if (this.path_list.length == 1) { return this.path_list[0].path; } else if (this.path_list.length == 0) { return ""; } else { for (var i = 0; i < this.path_list.length; i++) { paths.push(this.path_list[i].path); // if(i == 0){ // paths = this.path_list[i].path // }else{ // arr.push(this.path_list[i].path) // } } } return paths; }, photoCompress() {}, // 圖片壓縮 canvasDataURL(path, obj, callback) { var suffix = path.match(/\/(\S*);/)[1]; var img = new Image(); img.src = path; img.onload = function () { var that = this; // 默認(rèn)按比例壓縮 var w = that.width, h = that.height, scale = w / h; w = obj.width || w; h = obj.height || w / scale; var quality = 0.7; // 默認(rèn)圖片質(zhì)量為0.7 this.limit = this.limit ? this.limit : 1; // 默認(rèn)為超過1M 進行壓縮 //生成canvas var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); // 創(chuàng)建屬性節(jié)點 var anw = document.createAttribute("width"); anw.nodeValue = w; var anh = document.createAttribute("height"); anh.nodeValue = h; canvas.setAttributeNode(anw); canvas.setAttributeNode(anh); ctx.drawImage(that, 0, 0, w, h); // 圖像質(zhì)量 if (obj.quality && obj.quality <= 1 && obj.quality > 0) quality = obj.quality; var base64 = ""; if (this.isCompress) { // quality值越小,所繪制出的圖像越模糊 base64 = obj.fileSize > this.limit * 1024 ? canvas.toDataURL("image" + suffix, quality) : canvas.toDataURL("image" + suffix); } else { base64 = canvas.toDataURL("image/" + suffix, quality); } // 回調(diào)函數(shù)返回base64的值 callback(base64); }; }, // base64 轉(zhuǎn) blob 對象 convertBase64UrlToBlob(urlData) { //去掉url的頭,并轉(zhuǎn)換為byte var bytes = window.atob(urlData.split(",")[1]); //處理異常,將ascii碼小于0的轉(zhuǎn)換為大于0 var ab = new ArrayBuffer(bytes.length); var ia = new Uint8Array(ab); for (var i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new Blob([ab], { type: "image/png" }); }, }, }; </script>
<style scoped="upload"> .upload { width: 100%; overflow: hidden; display: flex; flex-wrap: wrap; padding: 10px 4px; box-sizing: border-box; } .select_mode { width: 120px; height: 120px; display: flex; justify-content: center; align-items: center; /* border: 1px dashed #cfcfcf; */ border-radius: 10px; position: relative; box-sizing: border-box; /* background-color: #fcfbfe; */ } .select_mode:hover { /* border: 1px dashed #00A2E9; */ } .select_mode img { width: 100%; position: absolute; z-index: 1; cursor: pointer; } .select_mode input[type="file"] { width: 100%; height: 100%; opacity: 0; cursor: pointer; z-index: 2; } .img_mode { margin-right: 10px; margin-bottom: 10px; box-shadow: 0px 1px 5px #cccccc; position: relative; } .mask { background: rgb(0, 0, 0, 0.5); height: 100%; width: 100%; position: absolute; top: 0px; left: 0px; display: flex; justify-content: center; align-items: center; } .img_mode, .img_mode img { height: 120px; max-width: 200px; } .mask i { font-family: element-icons !important; color: #ffffff; font-size: 25px; margin: 0px 8px; cursor: pointer; } .big_img_mode { text-align: center; width: 100%; height: 100%; max-height: 400px; } .big_img_mode img { max-width: 400px; max-height: 400px; } </style>
使用方法:
引入上面的模塊,然后在 components里面注冊,即可用標(biāo)簽使用
import Upload from "../../../components/utils/Upload.vue"; components: { editor: editor, Upload },
<el-form-item label="輪播圖:" prop="img"> //這里面的參數(shù)在上面的模塊里面去看,ref就是你綁定的對象 <upload ref="addBanner" :limit_size="4" :isCompress="true" :bigBox="true" ></upload> </el-form-item>
上傳之前獲取base64圖片編碼,存到表單中,然后直接提交給后臺
this.addForm.img = this.$refs.addBanner.getPaths();
注意:這里上傳后會是一段base64字符串,如果是多圖,會是一段字符串?dāng)?shù)組,后端可以直接用jsonArray接收
視頻上傳
//表單 <el-form-item label="視頻:" prop="video"> <video class="video" controls v-if="videoShow" :src="videoShow" /> <input ref="videoFile" @change="fileChange($event)" type="file" id="video_file" accept="video/*" /> </el-form-item>
//方法 fileChange(e) { var files = e.target.files || e.dataTransfer.files; if (!files.length) return; let name = files[0].name.toLowerCase(); if ( !/\.(avi|wmv|mpeg|mp4|mov|mkv|flv|f4v|m4v|rmvb|rm|3gp|dat|ts|mts|vob)$/.test( name ) ) { this.$message.warning("請上傳視頻"); return; } if (files[0].size > 1024 * 1024 * 20) { this.$message.warning("視頻大小不能大于20M"); return; } //這里是file文件 this.addForm.video = files[0]; var reader = new FileReader(); reader.readAsDataURL(files[0]); reader.onload = () => { //這里是一段base64,用于視頻回顯用 this.videoShow = reader.result; }; },
表格渲染相關(guān)
表格內(nèi)多圖渲染
這段代碼的意思是渲染當(dāng)前行img字段的列表,遍歷生成img圖片標(biāo)簽
因為img字段返回的是一個數(shù)組,不能直接渲染,所以加上一個JSON.parse解析一下
另外加了一個判斷最多渲染不超過三張,防止界面過長超出
<el-table-column prop="name" label="輪播圖" align="center" ?width="300px"> ? ? ? ? ? <template slot-scope="scope"> ? ? ? ? ? ? <el-image ? ? ? ? ? ? ? v-for="(item,index) in JSON.parse(scope.row.img)" ? ? ? ? ? ? ? :src="item"? ? ? ? ? ? ? ? v-if="index<3" ? ? ? ? ? ? ? style="width: 30%;height: 100%;margin-right: 5px" ? ? ? ? ? ? ></el-image> ? ? ? ? ? </template> </el-table-column>
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
elementui中el-input回車搜索實現(xiàn)示例
這篇文章主要介紹了elementui中el-input回車搜索實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06vue中的雙向數(shù)據(jù)綁定原理與常見操作技巧詳解
這篇文章主要介紹了vue中的雙向數(shù)據(jù)綁定原理與常見操作技巧,結(jié)合實例形式詳細(xì)分析了vue中雙向數(shù)據(jù)綁定的概念、原理、常見操作技巧與相關(guān)注意事項,需要的朋友可以參考下2020-03-03vue3 Teleport瞬間移動函數(shù)使用方法詳解
這篇文章主要為大家詳細(xì)介紹了vue3 Teleport瞬間移動函數(shù)使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-03-03