多文件上传是Vue应用的常见操作。操作要求:(1)允许反复拖放多个文件到待上传区域(2)自动过滤掉重复拖放的文件(3)拖放后,形成待上传文件列表的简易缩略图(4)双击文件名,可移去某个文件。具体效果,如下图所示。
我们可将拖放操作设计为一个插件指令dragDropFile,以供全局使用:
import {h, render} from 'vue'
import {filter, fromEvent, mergeWith, Observable, scan, switchMap, tap} from 'rxjs'
export default {
name: 'app.plugins',
install: (app, _) => {
app.directive('dragDropFile', { //定义dragDropFile指令
mounted: (element, binding) => {
binding.value.splice(0) //清空数组中的旧文件数据
new Observable().pipe(
//合并拖、放事件
mergeWith(fromEvent(element, 'dragover'), fromEvent(element,'drop')),
tap(event => event.preventDefault()), //阻止浏览器默认的打开文件行为
switchMap(event => event.dataTransfer.files), //获取拖放的文件
//过滤掉重复拖放的文件
filter(file => binding.value.every(f => f.name !== file.name)),
scan((_, file) => { //扫描用户拖放的所有文件
binding.value.push(file) //当前文件加入到待上传文件数组中
//创建一个虚拟的节点对象
const fragment = document.createDocumentFragment()
render(h('div', [ //用div在页面上构建待上传文件的简易缩略图
h('img', { //在待上传文件前面放置一个小图标
src: 'image/file.gif',
style: {verticalAlign: 'middle'}
}),
h('span', { //显示待上传文件的文件名、文件大小
innerText: `${file.name}\u3000\u3000${(file.size / 1024).toFixed(2) + 'KB'}`,
style: {cursor: 'pointer'},
ondblclick: () => { //添加双击事件,双击时移去该文件
let index = binding.value.indexOf(file)
element.removeChild(element.children[index])
binding.value.splice(index, 1) //删除
}
})
]), fragment) //将div层渲染到虚拟节点fragment
element.appendChild(fragment) //将fragment添加到element
}, 0)
).subscribe()
}
})
}
}
该指令的应用形式为:v-drag-drop-file,在需要使用的地方直接使用即可:
<div id="files-list" v-drag-drop-file="myFiles"></div>
这么优雅的处理方式,知道的人,极少!
标签:文件,Vue,binding,element,file,上传,拖放 From: https://blog.csdn.net/acoease/article/details/143460694