一、拖拽上传-未调用上传接口
1.效果
支持多选、拖拽、删除
2.代码
far.vue
<!-- 导入组件 -->
<UploadDrag :accept="'.xls,.xlsx'" :length="2" :fileListVal="fileList || []" @setFile="setFile"></UploadDrag>
import UploadDrag from '@/components/uploadDrag.vue'
let fileList = ref([]) // 上传的文件
// 上传附件
const setFile = (val) => { fileList.value = val || [] }
uploadDrag.vue
<template>
<div class="page-view wbg">
<div class="drop-area" @dragenter="highlight" @dragover="highlight" @dragleave="unHighlight" @drop="handleDrop"
@click="handleClick">
<el-icon :size="67" color="#9ea1a9" class="el-icon--upload"><upload-filled /></el-icon>
<div class="desc" v-if="accept">请将待上传文件拖至该区域,仅支持上传:{{ accept }}文档类型</div>
<input type="file" ref="fileInput" @change="handleFiles" :multiple="multipleFlag" :accept="accept"
style="display: none;">
</div>
<div class="mt">
<span>已上传文档:{{ fileList.length ? '' : '无' }}</span>
<div v-for="(item, index) in fileList" :key="index">
<el-space>
<span>{{ item.fileName }}</span>
<el-button :icon="Delete" @click="deleteFile(index)" link type="danger"></el-button>
</el-space>
</div>
</div>
<!-- <pre style="max-width: 100px;">{{ fileList }}</pre>
<pre style="max-width: 100px;">{{ inputList }}</pre> -->
</div>
</template>
<script setup>
import { watch } from 'vue'
import { Delete, UploadFilled } from '@element-plus/icons-vue'
// import { singleFileUpload, } from '@/service/index.js'
import _ from 'lodash'
import { ElMessage } from 'element-plus'
import { getIpAndPort } from '@/utils/common.js'
defineOptions({
name: "JgUploadDrag"
})
const props = defineProps({
fileListVal: { type: Array, default: () => [] }, // 父组件传递的文件集合
accept: { type: String, default: () => '' }, // 默认不限制 .jpg,.jpeg,.png,.gif,.bmp,.JPG,.JPEG,.PBG,.GIF
length: { type: Number, default: () => 999999 },
canEdit: { type: Boolean, default: () => true },
})
const emit = defineEmits(["setFile"])
const fileList = ref([]) // 当前组件内的文件集合
const inputList = ref([]) // 这个是input拿到的list 不一定调用上传接口
const fileInput = ref(null); // 上传ref
const multipleFlag = ref(false) // 是否多选
watch([() => props.fileListVal], ([fileListVal]) => {
// console.log('监听', fileListVal);
fileList.value = _.cloneDeep(fileListVal)
inputList.value = _.cloneDeep(fileListVal)
}, { deep: true, immediate: true })
const highlight = (e) => {
e.preventDefault();
e.stopPropagation();
e.target.classList.add('hover');
};
const unHighlight = (e) => {
e.target.classList.remove('hover');
};
// 拖拽
const handleDrop = (e) => {
// console.log(e.dataTransfer.files);
e.preventDefault();
e.stopPropagation();
unHighlight(e);
const files = e.dataTransfer.files;
if (fileList.value.length >= props.length) {
return ElMessage.warning(`最多允许上传${props.length}个文件`)
}
let flagResult = beforeUpload(files[0])
if (!flagResult) {
return false
}
inputList.value = [] // 必须这几步都置空 不然无效
let flag = true
for (let i = 0; i < files.length; i++) {
inputList.value.push({
file: files[i],
name: files[i].name,
size: files[i].size,
type: files[i].type
});
if (!beforeUpload(files[i])) {
flag = false
}
}
if (flag) {
uploadFile();
}
};
// 点击
const handleClick = () => {
if (fileList.value.length >= props.length) {
return ElMessage.warning(`最多允许上传${props.length}个文件`)
}
// console.log(fileInput.value);
fileInput.value.click(); // 使用 ref 的 value 直接访问 DOM 元素
};
// 获取点击文件
const handleFiles = (e) => {
const files = e.target.files || e;
// console.log('files', files, typeof files, files[0]);
inputList.value = [] // 必须这几步都置空 不然无效
let flag = true
for (let i = 0; i < files.length; i++) {
inputList.value.push({
file: files[i],
name: files[i].name,
size: files[i].size,
type: files[i].type
});
if (!beforeUpload(files[i])) {
flag = false
}
}
if (flag) {
uploadFile();
}
};
const beforeUpload = (file) => {
// 1、控制文件数量
if (props.length && fileList.value > props.length) {
ElMessage.warning(`最多允许上传${props.length}个文件`)
// 设置上传的文件为错误状态
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
// 2、控制上传的文件大小
if (file.size > 209715200) {
ElMessage.warning('文件大小超过最大限度200M')
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
// 3、控制上传文件不能为空
if (file.size === 0) {
ElMessage.warning('所选信息中存在空文件或目录,请重新选择')
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
// 4、控制已上传文件不重复
// for (let i = 0; i <= this.fileList.length; i++) {
// const item = this.fileList[i]
// if (item.name === file.name) {
// inputList.value = [] // 必须这几步都置空 不然无效
// ElMessage.warning('不允许重复上传')
// return false
// }
// }
// 5、控制上传文件的类型 arr是上传类型的白名单
if (props.accept && props.accept.length) {
const type = file.name.slice(file.name.lastIndexOf('.') + 1).toLowerCase()
const arr = props.accept.split(',')
if (arr.includes('.' + type)) {
return true
} else {
ElMessage.warning(`不支持以 .${type} 扩展类型的文件上传!`)
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
}
return true
}
// 上传文件
const uploadFile = () => {
// console.log('inputList', inputList.value);
const file = inputList.value[0].file
// console.log('上传接口的文件', file);
// singleFileUpload({ file }).then(v => {
// fileList.value.push({
// fileName: v.fileName,
// uuidName: v.uuidName,
// url: '/file/download?fileName=' + v.uuidName,
// file: file,
// })
// inputList.value = [] // 必须这几步都置空 不然无效
// emit('setFile', fileList.value)
// })
fileList.value.push({
file: file,
fileName: file.name,
})
emit('setFile', fileList.value)
};
// 手动删除
const deleteFile = (index) => {
fileList.value = fileList.value.filter((item, i) => i !== index)
inputList.value = _.cloneDeep(fileList.value)
emit('setFile', fileList.value)
}
</script>
<style lang="less" scoped>
.page-view {
width: 100%;
height: 100%;
}
.wbg {
background-color: #fff;
}
.mt {
margin-top: 16px;
}
.file-box {
display: inline-block;
margin-right: 30px;
}
.drop-area {
width: 440px;
height: 185px;
border: 1px dashed #dcdfe6;
border-radius: 6px;
text-align: center;
cursor: pointer;
padding-top: 40px;
}
.drop-area:hover {
border-color: #11716f;
}
</style>
二、按钮上传-点击即调上传接口
1.效果
2.代码
far.vue
<el-col :span="24">
<el-form-item label="成绩附件:" prop="testScoreFileInfoList">
<Upload :accept="'.jpg,.jpeg,.png'" :fileListVal="formData.testScoreFileInfoList"
@setFile="setFile($event, 'testScoreFileInfoList')">
</Upload>
</el-form-item>
</el-col>
import Upload from '@/components/upload.vue'
// 上传附件
const setFile = (val, key) => {
formData.value[key] = val
ruleFormRef.value.validateField(key) // 手动触发验证
}
Upload.vue
<script setup>
import { ref, onMounted, watch } from 'vue'
import { Delete, UploadFilled } from '@element-plus/icons-vue'
import { singleFileUpload, } from '@/service/index.js'
import _ from 'lodash'
import { ElMessage } from 'element-plus'
import { getIpAndPort } from '@/utils/common.js'
defineOptions({
name: "JgUpload"
})
const props = defineProps({
fileListVal: { type: Array, default: () => [] }, // 父组件传递的文件集合
accept: { type: String, default: () => '' }, // 默认不限制 .jpg,.jpeg,.png,.gif,.bmp,.JPG,.JPEG,.PBG,.GIF
length: { type: Number, default: () => 999999 },
canEdit: { type: Boolean, default: () => true },
})
const emit = defineEmits(["setFile"])
const fileList = ref([]) // 当前组件内的文件集合
const inputList = ref([]) // 这个是input拿到的list 不一定调用上传接口
const fileInput = ref(null); // 上传ref
const multipleFlag = ref(false) // 是否多选
let ipPortUrl = ref(getIpAndPort()) // 前端获取ip端口用于拼接文件地址
watch([() => props.fileListVal], ([fileListVal]) => {
// console.log('监听', fileListVal);
fileList.value = _.cloneDeep(fileListVal)
inputList.value = _.cloneDeep(fileListVal)
}, { deep: true, immediate: true })
const highlight = (e) => {
e.preventDefault();
e.stopPropagation();
e.target.classList.add('hover');
};
const unHighlight = (e) => {
e.target.classList.remove('hover');
};
// 拖拽
const handleDrop = (e) => {
// console.log(e.dataTransfer.files);
e.preventDefault();
e.stopPropagation();
unHighlight(e);
const files = e.dataTransfer.files;
if (fileList.value.length >= props.length) {
return ElMessage.warning(`最多允许上传${props.length}个文件`)
}
let flagResult = beforeUpload(files[0])
if (!flagResult) {
return false
}
inputList.value = [] // 必须这几步都置空 不然无效
let flag = true
for (let i = 0; i < files.length; i++) {
inputList.value.push({
file: files[i],
name: files[i].name,
size: files[i].size,
type: files[i].type
});
if (!beforeUpload(files[i])) {
flag = false
}
}
if (flag) {
uploadFile();
}
};
// 点击
const handleClick = () => {
if (fileList.value.length >= props.length) {
return ElMessage.warning(`最多允许上传${props.length}个文件`)
}
// console.log(fileInput.value);
fileInput.value.click(); // 使用 ref 的 value 直接访问 DOM 元素
};
// 获取点击文件
const handleFiles = (e) => {
const files = e.target.files || e;
// console.log('files', files, typeof files, files[0]);
inputList.value = [] // 必须这几步都置空 不然无效
let flag = true
for (let i = 0; i < files.length; i++) {
inputList.value.push({
file: files[i],
name: files[i].name,
size: files[i].size,
type: files[i].type
});
if (!beforeUpload(files[i])) {
flag = false
}
}
if (flag) {
uploadFile();
}
};
const beforeUpload = (file) => {
console.log(1111, file);
// 0.控制名称长度
let fileName = file.name.split('.')?.[0] || ''
if (fileName.length > 20) {
ElMessage.warning(`文件名称最长为20`)
// 设置上传的文件为错误状态
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
// 1、控制文件数量
if (props.length && fileList.value > props.length) {
ElMessage.warning(`最多允许上传${props.length}个文件`)
// 设置上传的文件为错误状态
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
// 2、控制上传的文件大小
if (file.size > 209715200) {
ElMessage.warning('文件大小超过最大限度200M')
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
// 3、控制上传文件不能为空
if (file.size === 0) {
ElMessage.warning('所选信息中存在空文件或目录,请重新选择')
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
// 4、控制已上传文件不重复
// for (let i = 0; i <= this.fileList.length; i++) {
// const item = this.fileList[i]
// if (item.name === file.name) {
// inputList.value = [] // 必须这几步都置空 不然无效
// ElMessage.warning('不允许重复上传')
// return false
// }
// }
// 5、控制上传文件的类型 arr是上传类型的白名单
if (props.accept && props.accept.length) {
const type = file.name.slice(file.name.lastIndexOf('.') + 1).toLowerCase()
const arr = props.accept.split(',')
if (arr.includes('.' + type)) {
return true
} else {
ElMessage.warning(`不支持以 .${type} 扩展类型的文件上传!`)
inputList.value = [] // 必须这几步都置空 不然无效
return false
}
}
return true
}
// 上传文件
const uploadFile = () => {
console.log('inputList', inputList.value);
// const file = params.file;
const file = inputList.value[0].file
// console.log('上传接口的文件', file);
singleFileUpload({ file }).then(v => {
// fileList.value[fileList.value.length - 1] = {
// fileName: v.fileName,
// uuidName: v.uuidName,
// url: ipPortUrl.value + '/file/download?fileName=' + v.uuidName,
// }
fileList.value.push({
fileName: v.fileName,
uuidName: v.uuidName,
url: ipPortUrl.value + '/file/download?fileName=' + v.uuidName,
})
inputList.value = [] // 必须这几步都置空 不然无效
emit('setFile', fileList.value)
})
};
// 手动删除
const deleteFile = (index) => {
fileList.value = fileList.value.filter((item, i) => i !== index)
inputList.value = _.cloneDeep(fileList.value)
emit('setFile', fileList.value)
}
onMounted(() => {
})
</script>
<template>
<div class="page-view wbg">
<div v-if="(!(props.length === 1 && fileList.length === 1) && props.canEdit)" class="drop-area"
@dragenter="highlight" @dragover="highlight" @dragleave="unHighlight" @drop="handleDrop" @click="handleClick">
<el-button :icon="UploadFilled">上传文件</el-button>
<div class="desc" v-if="accept">支持文件格式:{{ accept }}</div>
<input type="file" ref="fileInput" @change="handleFiles" :multiple="multipleFlag" :accept="accept"
style="display: none;">
</div>
<div class="file-box" v-for="(item, index) in fileList" :key="index">
<el-space v-if="item.uuidName">
<a v-download="ipPortUrl + '/file/download?fileName=' + item.uuidName" class="green"
style="color:#007d7b;cursor:pointer">{{ item.fileName }}</a>
<el-button v-if="props.canEdit" :icon="Delete" @click="deleteFile(index)" link type="danger"></el-button>
</el-space>
</div>
<!-- <pre style="max-width: 100px;">{{ fileList }}</pre>
<pre style="max-width: 100px;">{{ inputList }}</pre> -->
</div>
</template>
<style lang="scss" scoped>
.page-view {
width: 100%;
height: 100%;
}
.wbg {
background-color: #fff;
}
.mt {
margin-top: 16px;
}
.file-box {
display: inline-block;
margin-right: 30px;
}
.drop-area {
text-align: left;
cursor: pointer;
}
.desc {
font-size: 12px;
color: #606266;
}
</style>