vue实现文件上传纯js

30次阅读

共计 7525 个字符,预计需要花费 19 分钟才能阅读完成。

在实现公司业务时,打算自己一个文件上传组件,于是着手写了一下,下面是实现步骤:
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 的原因

正文完
 0