主要用到input的webkitdirectory属性
具体代码如下,修改服务器上传路径就可以使用
<script lang="ts" setup>
import { ref } from 'vue'
import $http from '@/http/index.ts'
import { ElMessage } from 'element-plus'
const props = defineProps(['uploadProps'])
const fileUploadRef = ref()
const fileUploadFloderRef = ref()
const isUploading = ref(false)
const oploadedList = ref<any[]>([])
const handleDragOver = event => {
event.preventDefault()
}
const handleDrop = async event => {
event.preventDefault()
const files = []
const promises: any[] = []
for (const item of event.dataTransfer.items) {
const entry: any = item.webkitGetAsEntry()
console.log('[ entry ] >', entry)
promises.push(readFiles(entry))
}
const resultFilesArrays = await Promise.all(promises)
const allFiles = resultFilesArrays.flat()
console.log('[ All files ] >', allFiles)
handleUploadToServer(allFiles)
}
const handleUploadToServer = (files: any[]) => {
isUploading.value = true
Promise.all(
files.map((item: any) => {
const formData = new FormData()
formData.append('file', item, item.name)
return $http.request({
method: 'post',
url: `http://ddddddd.com/api/pictures_upload_image`,
headers: { 'Content-Type': 'multipart/form-data' },
data: formData
})
})
)
.finally(() => (isUploading.value = false))
.then(() => {
oploadedList.value.unshift(...files)
ElMessage.success('上传成功')
})
.catch(() => {
ElMessage.error('上传失败')
})
}
const readFiles = async (item: any) => {
if (item.isDirectory) {
const directoryReader = item.createReader()
const entries: any[] = await new Promise((resolve, reject) => {
directoryReader.readEntries(resolve, reject)
})
let files = []
for (const entry of entries) {
const resultFiles: any = await readFiles(entry)
files = files.concat(resultFiles)
}
return files
} else {
const file = await new Promise((resolve, reject) => {
item.file(resolve, reject)
})
return [file]
}
}
const toUploadFile = () => {
fileUploadRef.value.click()
}
const toUploadFloder = () => {
fileUploadFloderRef.value.click()
}
const handleFileChange = (e: any) => {
handleUploadToServer(Array.from(e.target.files))
}
const handleFloderChange = (e: any) => {
handleUploadToServer(Array.from(e.target.files))
}
const handleDelete = (index: number) => {
oploadedList.value.splice(index, 1)
}
</script>
<template>
<div
class="drag-box"
@dragover="handleDragOver"
@dragleave="handleDragOver"
@drop="handleDrop"
>
<div class="div-text">
<div class="drag-tip">
拖拽文件至此区域
<span class="click-txt" @click="toUploadFile">点击上传</span>
</div>
<div class="btn-wrap">
<el-button @click="toUploadFile">上传文件</el-button>
<el-button @click="toUploadFloder">上传文件夹</el-button>
<input
:style="{ display: 'none' }"
type="file"
ref="fileUploadRef"
@change="handleFileChange"
multiple
/>
<input
:style="{ display: 'none' }"
type="file"
ref="fileUploadFloderRef"
@change="handleFloderChange"
webkitdirectory
multiple
/>
</div>
</div>
<div class="div-tip">支持单次或批量上传,支持文件夹上传</div>
<div class="uploaded-list-wrap">
<div
class="uploaded-item"
v-for="(item, index) in oploadedList"
:key="index"
>
<div class="text-content">{{ item.name }}</div>
<svg
t="1702689906199"
class="icon success-icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="4854"
width="32"
height="32"
>
<path
d="M0 512C0 229.234759 229.234759 0 512 0s512 229.234759 512 512-229.234759 512-512 512S0 794.765241 0 512z m419.310345 194.630621a35.310345 35.310345 0 0 0 49.399172 1.271172l335.518897-311.931586a35.310345 35.310345 0 0 0-48.075035-51.729655l-309.124413 289.544827-145.125518-149.645241a35.310345 35.310345 0 1 0-50.688 49.169655l168.112552 173.320828z"
fill="#389BFF"
p-id="4855"
></path>
</svg>
<svg
@click="handleDelete(index)"
t="1702689838509"
class="icon delete-icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="3620"
width="32"
height="32"
>
<path
d="M511.998977 961.610044c-248.306272 0-449.607998-201.307865-449.607998-449.614138S263.692704 62.389956 511.998977 62.389956c248.364601 0 449.610044 201.299679 449.610044 449.606974S760.363577 961.610044 511.998977 961.610044L511.998977 961.610044zM718.186989 380.921639c8.457626-8.462742 8.457626-22.202675 0-30.658254l-45.927005-45.871747c-8.459672-8.459672-22.138206-8.459672-30.599925 0L511.603981 434.44874 381.546879 304.391638c-8.459672-8.459672-22.1423-8.459672-30.599925 0l-45.927005 45.871747c-8.457626 8.455579-8.457626 22.195511 0 30.658254l130.057101 130.053008L305.019948 641.031748c-8.457626 8.455579-8.457626 22.140253 0 30.599925L350.946954 717.555609c8.457626 8.404414 22.140253 8.404414 30.599925 0l130.057101-130.057101L641.661082 717.555609c8.461719 8.404414 22.140253 8.404414 30.599925 0l45.927005-45.922912c8.457626-8.459672 8.457626-22.144346 0-30.599925L588.129888 510.97567 718.186989 380.921639 718.186989 380.921639z"
fill="#d81e06"
p-id="3621"
></path>
</svg>
</div>
</div>
<div class="progress-bar" v-if="isUploading">
<el-progress :percentage="100" :format="() => ''" :indeterminate="true" />
</div>
</div>
</template>
<style lang="scss" scoped>
.drag-box {
position: relative;
.progress-bar {
position: absolute;
z-index: 100;
width: 100%;
top: 0;
left: 0px;
right: 0px;
bottom: -5px;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(255, 255, 255, 0.8);
:deep(.el-progress.el-progress--line) {
width: 100%;
margin-left: 10px;
}
}
.uploaded-list-wrap {
max-height: 200px;
overflow-y: auto;
.uploaded-item {
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
margin-bottom: 3px;
.text-content {
width: 80%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.icon {
width: 25px;
height: 25px;
}
.success-icon {
display: block;
}
.delete-icon {
display: none;
}
&:hover {
.success-icon {
display: none;
}
.delete-icon {
display: block;
}
}
}
}
}
.div-text {
width: 100%;
height: 250px;
border: 1px dashed #00b7ee;
border-radius: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 18px;
.click-txt {
color: #00b7ee;
cursor: pointer;
}
.btn-wrap {
margin-top: 20px;
}
}
</style>
需要用到的地方引用组件
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import UploadFloderImg from './uploadFloderImg.vue'
const uploadProps = ref({
name: 'file',
multiple: true,
accept: 'image/*',
action: `http://xxxx.com/api/pictures_upload_image`,
onChange: (info: any) => {
console.log(info, '文件上传啦=============')
},
onProgress: (progress: any) => {
console.log(progress, '上传进度啦========')
},
onSuccess: (file: any) => {
console.log(file, '成功啦-==============')
}
})
const showUploadFloder = ref(false)
const openFloderUpload = () => {
showUploadFloder.value = true
}
</script>
<template>
<el-button class="upload-img-container" @click="openFloderUpload">上传目录</el-button>
<el-dialog
v-model="showUploadFloder"
:width="800"
:footer="null"
class="upload-img-wrap"
>
<UploadFloderImg v-if="showUploadFloder" :uploadProps="uploadProps"/>
</el-dialog>
</template>