背景
项目中用到各种选择文件,且有很大一部分操作几乎相同,想要尽可能的简化选择上传文件的步骤,因此选择脚本生成 <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 = this
let $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.length
vm.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 = this
vm.focusNum++
let timer = setTimeout(_ => {
if (vm.focusNum == 3) {
//判断文件
if (vm.currSelectedLength) {
console.log('选中', vm.currSelectedLength)
} else {
console.log('取消')
}
vm.focusNum = 0
vm.currSelectedLength = 0
e.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>