背景
项目中用到各种选择文件,且有很大一部分操作几乎相同,想要尽可能的简化选择上传文件的步骤,因此选择脚本生成 <input type="file"/>
标签的形式完成函数式的上传文件调用,但是如果打开了文件管理器但是并未选择文件而是点击了取消,此时是不会触发input
的的change
事件的,如果想要在取消选择文件后清除一些缓存变量以及回收input
等资源的话,仅凭当前的内容并不足以完成。
方案
解决重点在于如果得知文件管理器关闭了,在文件管理器关闭后根据input.files
判断是否有选择文件。
可以使用窗口焦点
,当有页面内容A
获取焦点的时候,此时打开系统窗口B
会导致焦点转移,当关闭系统窗口B
时,页面内容A
会重新获取焦点且会触发focus
简单效果图
代码
<template><div class="base"><template v-for="temp in temps"><div class="temp-item"><div class="temp-item_text">{{temp.text}}</div><a class="temp-item_download handle-text" @click="downLoadTemp(temp.url)">下载</a></div></template><br><div style="text-align: center"><button class="handle-btn">一键下载所有模板</button></div><br><div style="display: none" ref="fileContainer"></div><button class="file-selector handle-btn" @click="uploadClick" @focus="dealFocus">上传文件</button><div style="max-height: 200px;overflow-y: auto;"><template v-if="files.length"><div class="file-item" v-for="(file,fileIndex) in files"><div class="file-item_text">{{file.text}}</div><div class="file-item_size">{{file.size}}KB</div><div class="file-item_del handle-text" @click="files.splice(fileIndex,1)">移除</div></div></template><div class="file-item" v-else>...未选取文件</div></div><br><div style="text-align: center"><button class="handle-btn">确定上传</button></div></div>
</template><script>
import { downloadBlob, floatFmt } from '@/utils'/*** 重新获取焦点的时候如果没有获取到文件则*/
export default {name: 'index',data() {return {temps: [{ text: '模板用例', url: 'dsd' },{ text: '模板用例', url: 'dsd' },{ text: '模板用例', url: 'dsd' }],files: [// { text: '文件', file: '', size: '0' }],focusNum: 0,currSelectedLength: 0}},methods: {downLoadTemp(url) {// window.open(url)},/*** 点击上传* 1) 生成并添加input-file* 2) 触发文件选择 更新操作索引*/uploadClick() {const vm = thislet $file = document.createElement('input')$file.setAttribute('type', 'file')$file.setAttribute('multiple', 'true')let changeHandle = _ => {if ($file.files.length) {//读取文件并添加显示let addArr = []for (let i = 0, file; (file = $file.files[i]) != null; i++) {addArr.push({text: file.name,size: floatFmt(file.size / 1024, 1),file: file})}vm.currSelectedLength = addArr.lengthvm.files.push(...addArr)}}vm.removeFileEl = _ => {$file.removeEventListener('change', changeHandle)$file.remove()}$file.addEventListener('change', changeHandle)$file.click()//点击延迟标记vm.focusNum++},/*** 获取焦点并更新操作索引* 当操作索引为 2 时表示当前为点击获取焦点,无需做任何操作* 当操作索引为 3 时表示文件选择框关闭页面重新获取焦点,此时需要延时判断文件选择结果*/dealFocus(e) {const vm = thisvm.focusNum++let timer = setTimeout(_ => {if (vm.focusNum == 3) {//判断文件if (vm.currSelectedLength) {console.log('选中', vm.currSelectedLength)} else {console.log('取消')}vm.focusNum = 0vm.currSelectedLength = 0e.target.blur()vm.removeFileEl()}clearTimeout(timer)}, 1000)}}
}
</script><style scoped>.base {width: 400px;margin: 0 auto;padding: 10px;border: 1px solid #ddd;}.temp-item, .file-item {display: flex;align-items: center;margin-top: 5px;cursor: pointer;justify-content: space-between;padding: 4px;}.temp-item:hover {background: #ddd;}.temp-item_text,.file-item_text {width: 200px;padding: 0 5px;}.temp-item_download {border-radius: 3px;background: transparent;border: 0;}.file-item_size {margin-left: auto;}.file-item_del {margin-left: 5px;padding-left: 5px;}.handle-btn {background: #fff;border-radius: 3px;cursor: pointer;border: 1px solid #666;padding: 5px;}.handle-btn:hover {background: rgba(75, 161, 235, 0.53);}.handle-text {color: #4d9de6;}.handle-text:hover {color: #3582dc;text-decoration: underline;}.file-selector:focus {color: #4d9de6;}
</style>