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è)面了。
拖拽上傳思路:通過(guò)給拖拽框dropbox綁定拖拽事件,當(dāng)組件銷毀時(shí)解綁事件。在拖拽結(jié)束,通過(guò)event.dataTransfer.files獲取上傳的文件信息。然后在對(duì)文件進(jìn)行上傳服務(wù)器操作。
接下來(lái)請(qǐng)?jiān)试S我簡(jiǎn)單介紹一下各個(gè)組件:
upload.vue封裝了點(diǎn)擊上傳的邏輯,而進(jìn)度條則沒(méi)有做,后期可基于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),上傳過(guò)程中禁用上傳按鈕
// 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,用來(lái)保存需要上傳的文件
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+ElementUI table實(shí)現(xiàn)表格分頁(yè)
這篇文章主要為大家詳細(xì)介紹了Vue+ElementUI table實(shí)現(xiàn)表格分頁(yè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
vue excel上傳預(yù)覽和table內(nèi)容下載到excel文件中
這篇文章主要介紹了vue excel上傳預(yù)覽和table內(nèi)容下載到excel文件中,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12
Vue Echarts實(shí)現(xiàn)可視化世界地圖代碼實(shí)例
這篇文章主要介紹了Vue Echarts實(shí)現(xiàn)可視化世界地圖,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
Vue組件庫(kù)ElementUI實(shí)現(xiàn)表格加載樹形數(shù)據(jù)教程
這篇文章主要為大家詳細(xì)介紹了Vue組件庫(kù)ElementUI實(shí)現(xiàn)表格加載樹形數(shù)據(jù)教程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06

