vue實(shí)現(xiàn)拖拽或點(diǎn)擊上傳圖片
本文實(shí)例為大家分享了vue實(shí)現(xiàn)拖拽或點(diǎn)擊上傳圖片的具體代碼,供大家參考,具體內(nèi)容如下
一、預(yù)覽圖
二、實(shí)現(xiàn)
點(diǎn)擊上傳思路:將input的type設(shè)置為“file”類型即可上傳文件。隱藏該input框,同時(shí)點(diǎn)擊按鈕時(shí),調(diào)取該input的點(diǎn)擊上傳功能。剩下的就是css優(yōu)化頁(yè)面了。
拖拽上傳思路:通過給拖拽框dropbox綁定拖拽事件,當(dāng)組件銷毀時(shí)解綁事件。在拖拽結(jié)束,通過event.dataTransfer.files獲取上傳的文件信息。然后在對(duì)文件進(jìn)行上傳服務(wù)器操作。
接下來請(qǐng)?jiān)试S我簡(jiǎn)單介紹一下各個(gè)組件:
upload.vue封裝了點(diǎn)擊上傳的邏輯,而進(jìn)度條則沒有做,后期可基于percent做參數(shù)繼續(xù)完善進(jìn)度條;uploadFormDialog.vue是父盒子,即點(diǎn)擊上傳按鈕后彈出的對(duì)話框,在該組件中需要完成頁(yè)面的布局,拖拽上傳等邏輯;
這樣封裝的意義是為了使得代碼更便于維護(hù)。
upload.vue 點(diǎn)擊上傳組件
<template> <!--upload.vue 點(diǎn)擊上傳組件 --> <div class="file-selector"> <z-btn class="selector-btn" color="primary" @click="handleUpClick"> 選擇文件 </z-btn> <input ref="input" class="file-selector-input" type="file" :multiple="multiple" :accept="accept" @change="handleFiles" /> </div> </template> <script> import {debounce} from 'lodash/function'; export default { data() { return { accept: '.jpg,.jpeg,.png,.gif', multiple: false, list: [], // 已選擇的文件對(duì)象 uploadFinished: true, // 上傳狀態(tài) startIndex: 0, // 開始上傳的下標(biāo),用于追加文件 maxSize: 10 * 1024 * 1024, //10M(size單位為byte) // source: this.$axios.CancelToken.source(), // axios 取消請(qǐng)求 }; }, methods: { // 重置 reset() { this.list = []; this.source.cancel(); this.startIndex = 0; this.uploadFinished = true; this.$refs.input && (this.$refs.input.value = null); }, // 調(diào)用上傳功能 handleUpClick: debounce(function () { // 可在此維護(hù)一個(gè)上傳狀態(tài),上傳過程中禁用上傳按鈕 // if (!this.uploadFinished) this.$message.info('即將覆蓋之前的文件~'); this.$refs.input.click(); }, 300), handleFiles(e) { const files = e?.target?.files; this.readFiles(files); }, // 上傳之前將文件處理為對(duì)象 readFiles(files) { if (!files || files.length <= 0) { return; } for (const file of files) { const url = window.URL.createObjectURL(file); const obj = { title: file.name.replace(/(.*\/)*([^.]+).*/ig, '$2'), // 去掉文件后綴 url, file, fileType: file.type, status: 0, // 狀態(tài) -> 0 等待中,1 完成, 2 正在上傳,3 上傳失敗 percent: 0, // 上傳進(jìn)度 }; // 提前在 data 中定義 list,用來保存需要上傳的文件 this.list.unshift(obj); this.$emit('fileList', this.list); } // 在 data 中定義 startIndex 初始值為 0,上傳完成后更新,用于追加上傳文件 // this.startUpload(this.startIndex); }, } }; </script> <style lang="scss"> .file-selector { .selector-btn { &:hover { background-color: rgba($color: #2976e6, $alpha: 0.8); transition: background 180ms; } } &-input { display: none; } } </style>
uploadFormDialog.vue 上傳對(duì)話框
<template> <!-- 上傳dialog --> <form-dialog v-model="$attrs.value" :title="title" persistent :loading="loading" maxWidth="600px" min-height='400px' @cancel="handleCancle" @confirm="handleSubmit" > <div class="d-flex flex-row justify-space-between"> <z-form style='width: 260px; height: 100%;'> <form-item label="圖片名稱" required> <z-text-field v-model="formData.name" outlined :rules="rules" :disabled='disabled' placeholder="請(qǐng)輸入圖片名稱" > </z-text-field> </form-item> <form-item label="描述" required> <z-textarea v-model="formData.description" outlined :disabled='disabled' placeholder="請(qǐng)輸入描述" style="resize: none;" > </z-textarea> </form-item> </z-form> <div ref="pickerArea" class="rightBox"> <div class="uploadInputs d-flex flex-column justify-center align-center" :class="[ dragging ? 'dragging' : '']"> <div ref="uploadBg" class="uploadBg my-2"></div> <upload ref="uploadBtn" @fileList='fileList' ></upload> <div class="tip mt-2">點(diǎn)擊上傳按鈕,或拖拽文件到框內(nèi)上傳</div> <div class="tinyTip ">請(qǐng)選擇不大于 10M 的文件</div> </div> </div> </div> </form-dialog> </template> <script > import {debounce} from 'lodash/function'; import upload from './upload'; import {uploadImage} from '@/wv-main-admin/apis/image'; export default { components: { upload }, props: ['dialogData'], data() { return { dialogFlag: '', title: '新增/編輯圖片', loading: false, formData: { name: '', description: '' }, disabled: false, rules: [v => !!v || '必填'], data: {}, dragging: true, //是否拖拽 bindDrop: false, fileInfo: {}, }; }, mounted() { }, beforeDestroy() { // 組件銷毀前解綁拖拽事件 try { const dropbox = this.$refs.pickerArea; dropbox.removeEventListener('drop', this.handleDrop); dropbox.removeEventListener('dragleave', this.handleDragLeave); dropbox.removeEventListener('dragover', this.handleDragOver); this.bindDrop = false; } catch (e) { console.log(e, '======我是組件銷毀前解綁拖拽事件的異常'); } }, methods: { //取消 handleCancle() { // 關(guān)閉當(dāng)前彈框 this.$emit('input', false); // 強(qiáng)制組件刷新 this.$forceUpdate(); }, handleSubmit: debounce(function () { // 上傳單個(gè)文件 const flag = this.checkMustsItem(); if (flag) { this.startUpload(); // 上傳完成,強(qiáng)制組件刷新 this.$forceUpdate(); } }, 300), //監(jiān)聽子組件的值 fileList(data) { this.fileInfo = data[0]; this.formData.name = this.fileInfo.title; const uploadBg = this.$refs.uploadBg; //改變背景圖片 uploadBg.style.backgroundImage = `url(${this.fileInfo.url})`; }, bindEvents() { const dropbox = this.$refs.pickerArea; // 防止重復(fù)綁定事件,需要在 data 中初始化 bindDrop 為 false if (!dropbox || this.bindDrop) { return; } // 綁定拖拽事件,在組件銷毀時(shí)解綁 dropbox.addEventListener('drop', this.handleDrop, false); dropbox.addEventListener('dragleave', this.handleDragLeave); dropbox.addEventListener('dragover', this.handleDragOver); this.bindDrop = true; }, // 拖拽到上傳區(qū)域 handleDragOver(e) { e.stopPropagation(); e.preventDefault(); this.dragging = true; }, // 離開上傳區(qū)域 handleDragLeave(e) { e.stopPropagation(); e.preventDefault(); this.dragging = false; }, // 拖拽結(jié)束 handleDrop(e) { e.stopPropagation(); e.preventDefault(); this.dragging = false; const files = e.dataTransfer.files; // 調(diào)用 <upload/> 組件的上傳功能 this.$refs.uploadBtn && this.$refs.uploadBtn.readFiles(files); }, // 上傳前需要校驗(yàn)文件 checkFile(index) { const file = this.list[index]; // 如果文件不存在,即全部文件上傳完成 if (!file) { // 上傳完成,向父組件拋出 success 事件 this.uploadFinished = true; this.$emit('success', this.list); // 清空上傳控件中的值,保證 change 事件能正常觸發(fā) this.$refs.input.value = null; this.startIndex = index > 1 ? index - 1 : 0; return false; } // 校驗(yàn)是否已上傳 if (`${file.status}` === '1') { this.startUpload(++index); return false; } // 校驗(yàn)文件大小 if (this.maxSize && file.file && file.file.size >= this.maxSize) { this.startUpload(++index); return false; } return true; }, checkMustsItem() { if (!this.fileInfo.file) { this.$message.warning('請(qǐng)上傳文件!'); return false; } if (!this.formData.name) { this.$message.warning('請(qǐng)輸入文件名稱!'); return false; } if (!this.formData.description) { this.$message.warning('請(qǐng)輸入文件描述!'); return false; } return true; }, // 上傳單個(gè)文件 startUpload() { this.loading = true; const params = { type: 'image' }; this.$set(params, 'file', this.fileInfo.file); this.$set(params, 'name', this.formData.name); this.$set(params, 'description', this.formData.description); uploadImage(params) .then(res => { this.loading = false; if (res.code === 0) { this.$message.success('上傳成功~'); this.$emit('refreshList', false); this.$emit('input', false); } }) .catch(() => { this.loading = false; }); // this.$axios({ // url: this.url, // 上傳接口,由 props 傳入 // method: 'post', // data, // withCredentials: true, // cancelToken: this.source.token, // 用于取消接口請(qǐng)求 // // 進(jìn)度條 // onUploadProgress: e => { // if (fileObj.status === 1) { return; } // 已上傳 // // 限定最大值為 99% // const p = parseInt((e.loaded / e.total) * 99); // if (e.total) { // fileObj.status = 2; // 正在上傳 // fileObj.percent = p; // 更新上傳進(jìn)度 // } else { // fileObj.status = 3; // 上傳失敗 // } // }, // }) // .then(response => { // if (`${response.code}` === '200') { // fileObj.status = 1; // fileObj.percent = 100; // } else { // fileObj.status = 3; // } // }) // .catch(e => { // console.log(e, '====error'); // fileObj.status = 3; // }) // .finally(e => { // console.log(e, '====error'); // this.startUpload(++index); // }); // 上傳完成 }, }, }; </script> <style lang='scss' scoped> .rightBox { width: 260px; height: 250px; border: 1px solid #ccc; margin-top: 18px; .uploadBg { width: 150px; height: 125px; background: url("../../../../assets/upload.png") no-repeat center center; background-size: contain; } .tip { font-size: 13px; color: rgba(0, 0, 0, 0.87); } .tinyTip { font-size: 12px; color: #8e8f9e; } } </style>
注:以上代碼用到了我們自己封裝的組件庫(kù)和自己封裝的一些方法,請(qǐng)根據(jù)具體場(chǎng)景進(jìn)行相關(guān)的修改。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue 2.0學(xué)習(xí)筆記之Vue中的computed屬性
本篇文章主要介紹了Vue 2.0學(xué)習(xí)筆記之Vue中的computed屬性,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10Element-ui 自帶的兩種遠(yuǎn)程搜索(模糊查詢)用法講解
這篇文章主要介紹了Element-ui 自帶的兩種遠(yuǎn)程搜索(模糊查詢)用法講解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01vue中響應(yīng)式布局如何將字體大小改成自適應(yīng)
這篇文章主要介紹了vue中響應(yīng)式布局如何將字體大小改成自適應(yīng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09Vue2 Element el-table多選表格控制選取的思路解讀
這篇文章主要介紹了Vue2 Element el-table多選表格控制選取的思路解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07詳解Vue中$props、$attrs和$listeners的使用方法
本文主要介紹了Vue中$props、$attrs和$listeners的使用詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12vue中el-tree增加節(jié)點(diǎn)后如何重新刷新
這篇文章主要介紹了vue中el-tree增加節(jié)點(diǎn)后如何重新刷新,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08vue element動(dòng)態(tài)渲染、移除表單并添加驗(yàn)證的實(shí)現(xiàn)
這篇文章主要介紹了vue element動(dòng)態(tài)渲染、移除表單并添加驗(yàn)證的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01vue+element實(shí)現(xiàn)錨點(diǎn)鏈接方式
這篇文章主要介紹了vue+element實(shí)現(xiàn)錨點(diǎn)鏈接方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07