在实现公司业务时,打算自己一个文件上传组件,于是着手写了一下,下面是实现步骤:
1、先是手动创建一个上传按钮样式,实现如下:
<template>
<div class="preview" >
<span class="removeBtn" @click="removePic"><van-icon name="delete"></van-icon></span>
</div>
<div class="diy-upload">
<div class="plus">+</div>
<input v-if="columnName ===' 视频 '"class="videoInput"type="file"accept="video/*"@change="uploadFile($event)"name="fileTrans">
<input v-if="columnName ===' 文档 '"class="videoInput"type="file"accept=".doc, .docx, .xml, .xlsx"@change="uploadFile($event)"name="fileTrans">
<input v-if="columnName ===' 音频 '"class="videoInput"type="file"accept="audio/*"@change="uploadFile($event)"name="fileTrans">
<input v-if="columnName ===' 图片 '"class="videoInput"accept="image/*"type="file"@change="uploadFile($event)"name="fileTrans">
<input class="diy-submit" type="submit"/>
</div>
</template>
<style lang="less" scoped>
.upload{
padding: 140px 36px 160px 36px;
box-sizing: border-box;
}
.diy-upload{
position: relative;
margin: 0 auto;
width: 160px;
height: 160px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
background-color: #fff;
border: 2px dashed #e5e5e5;
cursor: pointer;
.plus{
color: #969799;
font-size: 24px;
/*z-index: 666;*/
position: absolute;
top: 0;
left: 0;
width: 160px;
height: 160px;
/*background: #333333;*/
text-align: center;
line-height: 160px;
}
.videoInput{
position: absolute;
width: 160px;
height: 160px;
overflow: hidden;
opacity: 0;
}
}
.preview{
position: relative;
margin-top: 10px;
display: none;
/*border: 2px solid #dedede;*/
text-align: center;
span:nth-of-type(1){
width: 32px;
height: 32px;
position: absolute;
right: 0;
bottom: 0;
z-index: 10000;
font-size: 26px;
color: #fff;
}
span:nth-of-type(2){
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
position: absolute;
top: -15px;
right: -15px;
}
}
.diy-detail{
width: 100%;
overflow: hidden;
.btn{
span{margin-bottom: 10px;}
}
.van-cell{
background-color: #F0F0F0;
border-radius: 35px;
font-size: 26px;
height: 69px;
line-height: 69px;
padding: 0 22px;
color: #999;
}
.van-hairline--top-bottom::after, .van-hairline-unset--top-bottom::after {border-width: 0;}
p{
height: 64px;
line-height: 64px;
font-size: 32px;
color: #333;
position: relative;
padding-left: 16px;
}
p::before{
position: absolute;
top: 0;
left: 0;
content: '*';
color: #FF0000;
}
span{
display: inline-block;
width: 157px;
background: #F0F0F0;
border-radius: 35px;
color: #999;
font-size: 26px;
padding: 14px 18px;
margin-right: 28px;
text-align: center;
}
.active{
color: #fff;
background: linear-gradient(to right,#FD5130,#FA6C34);
}
}
.diy-submit {
position: fixed;
height: 150px;
width: 90%;
bottom: 0;
background: #fff;
.van-button {
width: 100%;
height: 90px;
border-radius: 45px;
font-size: 36px;
color: #fff;
background: linear-gradient(to right, #FD5130, #FA6C34);
top: 50%;
transform: translate(0, -50%);
}
.van-button--default {border: none;}
}
</style>
样式如下
功能实现如下:
<script>
import VHeader from '../../common/header'
import {classifyList, uploadFile, addResources} from '../../http/mock'
import Toast from 'vant'
export default {
name: "uploadFile",
components: {VHeader},
data(){
return{tagList: [],
uploadTitle: '',
currentTag: null,
tagId: null,
columnName: localStorage.getItem('columnName'),
fileIdArr: [],
uploadVideoList: [],
videoSrc: '',
uploadDocList: [],
uploadAudioList: [],
uploadPicList: [],
picSrc: '',
}
},
filters: {formatSize(val) {if (val > 0) {return (val / 1024 / 1024).toFixed(2) + 'M';
} else {return '0M';}
},
},
methods: {uploadFile(val){let input = document.querySelector('input');
let preview = document.querySelector('.preview');
let uploadDiv = document.querySelector('.diy-upload');
let timerDesc = document.querySelector('.timer');
uploadDiv.style.display = 'none';
preview.style.display = 'block';
let files = input.files;
if (!files.length){return;}
let size = files[0].size/1024/1024; // 獲取文件的大小
let columnName = localStorage.getItem('columnName')
let formData = new FormData();
let config = {
headers: {'Content-Type': 'multipart/form-data'}
};
if(columnName === '视频'){formData.append(encodeURI(val.target.files[0].name), val.target.files[0]);
let video = document.createElement('video');
video.className = 'addDiv';
video.style.maxHeight = '215px';
video.style.width = '100%';
video.src = files[0].name;
timerDesc.innerHTML = '上傳時間:' + this.$moment(files[0].lastModifiedDate).format('YYYY-MM-DD');
preview.style.background = '#000';
preview.appendChild(video);
video.src = window.URL.createObjectURL(files[0]);
video.autoPlay = 'autoplay';
video.preload = 'auto';
} else if(columnName === '文档'){formData.append(encodeURI(val.target.files[0].name), val.target.files[0]);
preview.style.textAlign = 'left';
let doc = document.createElement('div');
let docImg = document.createElement('img');
let docTitle = document.createElement('span');
let docSize = document.createElement('span');
doc.className = 'addDiv';
docImg.style.display = 'inline-block';
docImg.style.cssFloat = 'left';
docImg.style.width = '28px';
docImg.style.height = '28px';
docImg.style.marginRight = '5px';
docImg.src = require('../../assets/img/resource_doc_b@2x.png');
docTitle.style.cssFloat = 'left';
docTitle.style.fontSize = '14px';
docTitle.style.marginRight = '30px';
docTitle.style.lineHeight= '28px';
docTitle.innerText = files[0].name;
docSize.style.cssFloat = 'left';
docSize.style.lineHeight= '28px';
docSize.innerText = size.toFixed(2) + 'M';
doc.appendChild(docImg);
doc.appendChild(docTitle);
doc.appendChild(docSize);
preview.appendChild(doc);
} else if(columnName === '音频'){formData.append(encodeURI(val.target.files[0].name), val.target.files[0]);
preview.style.textAlign = 'left';
let audioDiv = document.createElement('div');
let audioImg = document.createElement('img');
let audioTitle = document.createElement('span');
let audioSize = document.createElement('span');
audioDiv.className = 'addDiv';
audioImg.style.display = 'inline-block';
audioImg.style.cssFloat = 'left';
audioImg.style.width = '28px';
audioImg.style.height = '28px';
audioImg.style.marginRight = '5px';
audioImg.src = require('../../assets/img/resource_audio@2x.png');
audioTitle.style.cssFloat = 'left';
audioTitle.style.marginRight = '30px';
audioTitle.style.fontSize = '14px';
audioTitle.style.lineHeight = '28px';
audioTitle.innerText = files[0].name;
audioSize.style.cssFloat = 'left';
audioSize.style.lineHeight = '28px';
audioSize.innerText = size.toFixed(2) + 'M';
audioDiv.appendChild(audioImg);
audioDiv.appendChild(audioTitle);
audioDiv.appendChild(audioSize);
preview.appendChild(audioDiv);
}else if(columnName === '图片'){formData.append(encodeURI(val.target.files[0].name), val.target.files[0]);
preview.style.textAlign = 'center';
preview.style.background = '#000';
preview.style.width = '100%'
let picImg = document.createElement('img');
picImg.className = 'addDiv';
picImg.src = window.URL.createObjectURL(files[0]);
console.log(picImg.src);
picImg.style.width = '100%';
picImg.style.maxHeight = '215px';
picImg.style.margin = '0 auto';
preview.appendChild(picImg);
}
this.$api.post(uploadFile, formData, config).then(res => {this.fileIdArr = res.data.data; // 把選中的文件傳送給後台}).catch(err => {Toast('文件上傳失敗!')
})
},
// 移除创建的所有元素,并显示隐藏上传按钮
removePic(){let uploadDiv = document.querySelector('.diy-upload');
let timerDesc = document.querySelector('.timer');
let preview = document.querySelector('.preview');
let image = preview.querySelector('.addDiv');
preview.removeChild(image);
preview.style.display = 'none';
uploadDiv.style.display = 'block';
timerDesc.innerHTML = '';
// console.log('input', document.querySelector('input'));
},
doSubmit(){
let params = {
classify: this.tagId, // 针对视频资源时对应的分类 id
file: this.fileIdArr, // 选择完文件后,调用 uploadFile 这个接口,后端返回的数组
resourceColumnId: JSON.parse(localStorage.getItem('columnId')), // 资源栏目 id(视频、图片、音频、文档)
title: this.uploadTitle // 上传时填写的标题
};
let columnName = localStorage.getItem('columnName')
this.$api.post(addResources, params).then(res => {if(res.data.code === 1001){if(columnName === '视频'){this.$router.push({name: 'myVideo'});
}else {this.$router.push({name: 'myResourceClassify'});
}
}
}).catch(err => {console.log(err)
})
},
},
}
</script>
这段代码有点 bug,就是当选中了上传的文件后,再把选中的文件删除掉,不能再选中前面选中过的,这个 bug 初步判断是因为,再次选中删除的文件,此时文件不能再被选中,导致没有获取到当前文件,还需要再进一步排查解除产生 bug 的原因