基于elementPlus封装上传图片,可延伸上传文件等,基础代码记录,具体上传事件未处理,此处个人记录保存一下,可拖拽改变顺序
第一种
html
<template>
<div class="img-upload-layout">
<!-- 文件列表 -->
<transition-group class="img-upload-list" name="el-fade-in-linear" tag="ul">
<li
class="img-upload-list__item img-upload-list__img"
v-for="(file, index) in fileLists"
:key="file.uid"
@dragstart="handleDragStart($event,file,index)"
@dragenter="handleDragEnter($event,file,index)"
@dragover.prevent="handleDragOver($event,file,index)"
@dragend="handleDragEnd($event, file,index)"
:draggable="true"
>
<el-image
style="width: 120px; height: 120px"
:src="file.url"
:preview-src-list="[file.url]"
fit="contain"
/>
<div class="operation-item-wrap">
<span class="item-delete operation-item" @click="handleRemove(file,index)" >
<el-icon><Delete /></el-icon>
</span>
</div>
</li>
<li class="img-upload-list__item img-upload-list__btn">
<el-upload
ref="imgUploadRef"
class="upload-img-uploader"
list-type="picture-card"
accept="image/*"
:show-file-list="false"
:auto-upload="false"
:multiple="props.multiple"
:action="uploadFileUrl"
:headers="headers"
:file-list="fileLists"
:limit="props.limit"
:on-exceed="handleExceed"
:on-change="handleUploadChange"
:before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
>
<!-- 上传按钮 -->
<el-icon ><Plus /></el-icon>
</el-upload>
</li>
</transition-group>
</div>
</template>
<script setup>
const props = defineProps({
modelValue: [String, Object, Array],
// 上传按钮文字
btnText: {
type: String,
default: '选取文件',
},
// 数量限制
limit: {
type: Number,
default: 300,
},
multiple: {
type: Boolean,
default: true,
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 5,
},
});
const { proxy } = getCurrentInstance();
const emit = defineEmits();
const imgUploadRef = ref(null)
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload");
const uploadFileUrlAuth = ref(import.meta.env.VITE_APP_BASE_API + "/system/uploadPrivate");
// 上传header传参
const headers = ref({ Authorization: "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoiYWI2ODE0ZjItMGExZC00MzEzLTllM2UtMDBlYjQ2YjJiOGFiIiwidXNlcl9rZXkiOiI5ZTEyNThjMS03NzFjLTQ1NWItYWYxNy03YzcwNzVjZmU5ZWMiLCJ1c2VybmFtZSI6InN6YWRtaW4ifQ.z3gy7ogoZU80Qdp2oOHOl6ZTSWmfbTUpC_NPBphVCTkj9Grku8Q0QcN0R_VS3OWLuopsXqsZ1129bb5v7Wqzqg" });
const fileLists = ref([]);
const uploadList = ref([]);
// 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
const handleUploadChange = (file,files) => {
// status = 'ready' | 'uploading' | 'success' | 'fail'
fileLists.value.push(file)
console.log(fileLists.value,'=========handleUploadChange=====',files,file);
}
// 上传前校检
const handleBeforeUpload = (file) => {
console.log('=========handleBeforeUpload=====',file);
// // 校检文件大小
// if (props.fileSize) {
// const isLt = file.size / 1024 / 1024 < props.fileSize;
// if (!isLt) {
// proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
// return false;
// }
// }
return true;
};
// 上传成功回调
const handleUploadSuccess = (res, file) => {
console.log(res,'=======================handleUploadSuccess=======================',file);
if(res && res.code == 200){
uploadList.value.push(file);
if (uploadList.value.length === number.value) {
fileLists.value = fileLists.value.filter(f => f.fileUrl !== undefined).concat(uploadList.value);
uploadList.value = [];
number.value = 0;
emit("update:modelValue", fileLists.value );
}
}else{
proxy.$modal.msgError(res && res.msg ? res.msg : '上传文件失败');
}
};
// 上传失败
const handleUploadError = (err) => {
console.log('=======================handleUploadError=======================',err);
proxy.$modal.msgError("上传文件失败");
};
// 文件个数超出
const handleExceed = () => {
console.log('=======================handleExceed=======================');
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
};
const handleRemove = (file,index) => {
fileLists.value.splice(index, 1);
// fileLists.value = fileLists.value.filter(item => item.uid != file.uid)
console.log(fileLists.value,'=======================handleRemove=======================',file);
};
const handlePreview = (file) => {
console.log('=======================handlePreview=======================',file);
};
// 拖动事件处理
const draggingItem = ref(null)
const draggingIdx = ref(-1)
const handleDragStart = (e,item,index) => {
console.log(e,item,'============================handleDragStart======================', index);
draggingItem.value = item
draggingIdx.value = index
};
// 首先把元素变成可以放置的元素,即重写dragenter/dragover
const handleDragEnter = (e,item,index) => {
console.log(e,item,'============================handleDragEnter======================', index);
e.preventDefault();
if (draggingItem.value && item.uid == draggingItem.value.uid) {
return;
}
const newListItems = [...fileLists.value];
const indexOfFlag = newListItems.indexOf(draggingItem.value);
const dst = newListItems.indexOf(item);
newListItems.splice(dst, 0, ...newListItems.splice(indexOfFlag, 1));
fileLists.value = JSON.parse(JSON.stringify(newListItems));
};
const handleDragOver = (e,item,index) => {
console.log(e,item,'============================handleDragOver======================', index);
e.preventDefault();
};
const handleDragEnd = (e,item,index) => {
console.log(e,item,'============================handleDragEnd======================', index);
draggingItem.value = null
draggingIdx.value = -1
};
</script>
<style lang="scss">
.img-upload-layout{
display: flex;
align-items: flex-start;
justify-content: flex-start;
.upload-img-uploader{
.el-upload--picture-card{
width: 120px;
height: 120px;
}
}
.img-upload-list{
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
&__item{
width: 120px;
height: 120px;
&:hover{
.operation-item-wrap{
display: flex;
}
}
}
&__img{
border: 1px solid #e5e5e5;
border-radius: 8px;
overflow: hidden;
margin: 0 6px 12px 6px !important;
position: relative;
}
.operation-item-wrap{
position: absolute;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 24px;
background-color: rgb(0 0 0 / 40%);
padding: 0 4px;
box-sizing: border-box;
display: flex;
justify-content: flex-end;
display: none;
.el-icon{
font-size: 20px;
color: #ffffff;
}
.operation-item{
cursor: pointer;
margin-right: 4px;
display: flex;
align-items: center;
justify-content: center;
&:hover{
opacity: .85;
}
}
}
}
}
</style>
第二种
javascript
<template>
<div class="img-upload-layout">
<div class="draggable-img-btn">
<el-upload
ref="imgUploadRef"
class="upload-img-uploader"
list-type="picture-card"
accept="image/*"
:show-file-list="false"
:auto-upload="false"
:multiple="props.multiple"
:action="uploadFileUrl"
:headers="headers"
:file-list="fileLists"
:limit="props.limit"
:on-exceed="handleExceed"
:on-change="handleUploadChange"
:before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
>
<!-- 上传按钮 -->
<el-icon ><Plus /></el-icon>
<template #tip>
<div class="el-upload__tip">
请选择大小在{{props.fileSize}}M内的图片
</div>
</template>
</el-upload>
</div>
<!-- 文件列表 -->
<draggable
class="draggable-img-group"
tag="draggable-img-group"
item-key="uid"
v-model="fileLists"
:component-data="{name:'fade'}"
@change="handleDragChange"
@update="handleDragUpdate"
@end="handleDragEnd"
>
<template #item="{element,index}">
<div class="draggable-img-group__item">
<el-image
style="width: 120px; height: 120px"
:src="element.url"
:preview-src-list="[element.url]"
fit="contain"
/>
<div class="operation-item-wrap">
<span class="item-delete operation-item" @click="handleRemove(element,index)" >
<el-icon><Delete /></el-icon>
</span>
</div>
</div>
</template>
</draggable>
</div>
</template>
<script setup>
import draggable from 'vuedraggable'
const props = defineProps({
modelValue: [String, Object, Array],
// 上传按钮文字
btnText: {
type: String,
default: '选取文件',
},
// 数量限制
limit: {
type: Number,
default: 300,
},
multiple: {
type: Boolean,
default: true,
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 5,
},
});
const { proxy } = getCurrentInstance();
const emit = defineEmits();
const imgUploadRef = ref(null)
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload");
const uploadFileUrlAuth = ref(import.meta.env.VITE_APP_BASE_API + "/system/uploadPrivate");
// 上传header传参
const headers = ref({ Authorization: "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoiYWI2ODE0ZjItMGExZC00MzEzLTllM2UtMDBlYjQ2YjJiOGFiIiwidXNlcl9rZXkiOiI5ZTEyNThjMS03NzFjLTQ1NWItYWYxNy03YzcwNzVjZmU5ZWMiLCJ1c2VybmFtZSI6InN6YWRtaW4ifQ.z3gy7ogoZU80Qdp2oOHOl6ZTSWmfbTUpC_NPBphVCTkj9Grku8Q0QcN0R_VS3OWLuopsXqsZ1129bb5v7Wqzqg" });
const fileLists = ref([]);
const uploadList = ref([]);
// 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
const handleUploadChange = (file,files) => {
// status = 'ready' | 'uploading' | 'success' | 'fail'
fileLists.value.push(file)
console.log(fileLists.value,'=========handleUploadChange=====',files,file);
}
// 上传前校检
const handleBeforeUpload = (file) => {
console.log('=========handleBeforeUpload=====',file);
// // 校检文件大小
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) {
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
return false;
}
}
return true;
};
// 上传成功回调
const handleUploadSuccess = (res, file) => {
console.log(res,'=======================handleUploadSuccess=======================',file);
if(res && res.code == 200){
uploadList.value.push(file);
if (uploadList.value.length === number.value) {
fileLists.value = fileLists.value.filter(f => f.fileUrl !== undefined).concat(uploadList.value);
uploadList.value = [];
number.value = 0;
emit("update:modelValue", fileLists.value );
}
}else{
proxy.$modal.msgError(res && res.msg ? res.msg : '上传文件失败');
}
};
// 上传失败
const handleUploadError = (err) => {
console.log('=======================handleUploadError=======================',err);
proxy.$modal.msgError("上传文件失败");
};
// 文件个数超出
const handleExceed = () => {
console.log('=======================handleExceed=======================');
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
};
const handleRemove = (file,index) => {
fileLists.value.splice(index, 1);
console.log(fileLists.value,index,'=======================handleRemove=======================',file);
};
const handleDragEnd = (e) => {
console.log(fileLists.value,'==============handleDragEnd===============',e);
};
</script>
<style lang="scss">
.img-upload-layout{
display: flex;
align-items: flex-start;
justify-content: flex-start;
flex-direction: column;
.draggable-img-btn{
}
.upload-img-uploader{
.el-upload--picture-card{
width: 56px;
height: 56px;
}
}
.draggable-img-group{
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
&__item{
width: 120px;
height: 120px;
border: 1px solid #e5e5e5;
border-radius: 8px;
overflow: hidden;
margin: 0 6px 12px 6px !important;
position: relative;
&:hover{
.operation-item-wrap{
display: flex;
}
}
.operation-item-wrap{
position: absolute;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 24px;
background-color: rgb(0 0 0 / 40%);
padding: 0 4px;
box-sizing: border-box;
display: flex;
justify-content: flex-end;
display: none;
.el-icon{
font-size: 20px;
color: #ffffff;
}
.operation-item{
cursor: pointer;
margin-right: 4px;
display: flex;
align-items: center;
justify-content: center;
&:hover{
opacity: .85;
}
}
}
}
}
}
</style>