自己动手实现文件上传组件

文件上传

根据正常配置,element ui 的upLoad组件无法使用。配置和一般人的用法一致。但是用原生form表单又可以使用。

目前初步估计是由于后端的配置问题。但是由于后端知识有限,暂不能知道是什么问题。

目前的暂行办法是: 用原生form + iframe实现该功能,但是拖拽是暂时不能实现了。

具体实现

iframe模拟ajax

文件上传的关键是input的类型设置为file,并且将from设置属性为enctype="multipart/form -data"(具体作用未深入)。

但是,这样实现的样式提交的时候会导致浏览器的默认刷新事件,解决办法是通过ajax模拟表单,或者通过iframe。在这里选择用iframe。

若要form和iframe之间产生关联,需要在form标签上增加属性target="nm_iframe",并且在iframe上添加属性name="nm_iframe"

<form class="cf" enctype="multipart/form-data" method="post" :action="baseUrl + '/check/uploadFile'" target="nm_iframe">
    <input style="display: none" type="file" name="files" id="fileInput" @change="handlerUpload" accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"> 
    <label for="fileInput" class="el-button el-button--primary el-button--small">选择文件</label>
</form>

<iframe id="id_iframe" name="nm_iframe"></iframe> 

通过这样的设置,在请求返回成功之后,iframe会重新渲染,触发onload事件,会把返回值渲染到iframe中。

image

根据这个钩子,我们可以做很多事情。

iframeUploadSuccessHandler() {
    const uploadIframe = document.querySelector('#id_iframe');
    uploadIframe.onload = () => {
        let res = JSON.parse(uploadIframe.contentWindow.document.querySelector('body pre').innerHTML)
        const head = res.head;
        if (head.code == 100) {
            this.$message.success(head.message);
            this.allFileList = this.allFileList.concat(this.fileList);
            this.clearWaitFileList()
            return
        }
        this.$message.error(head.message);
    }
}
文件上传的控制

通过input[type=”file”]的change事件可以控制文件的增删。

setTimeout(() => { // 放在队列后面,等待页面加载完毕
    this.fileInput = document.querySelector('#fileInput') 
});

handlerUpload(e) {
    let fileArray = e.target.files;
    this.fileList = Array.from(fileArray); // 将fileLIst转为数组
    this.successUpload = false;
},

deleteFile() {
    this.clearWaitFileList()
},
// 清空待上传文件列表
clearWaitFileList() {
    this.fileInput.value = ''; 
    this.fileList = [];
}

最终代码:

<template>
    <el-dialog title="对账" v-model="dialogCheck" size="tiny" :close-on-click-modal="false" @close="closeDialog" custom-class="dialog-w-400">
        <div>
            <p class="bold">选择对账类型</p>
             <el-checkbox-group v-model="dataType">
                <el-checkbox v-for="(d, k) in dataTypeOpts" :key="k" :label="d.value">{{d.label}}</el-checkbox>
            </el-checkbox-group>
        </div>
        <div>
            <p class="bold">选择对账日期</p>
            <el-date-picker
            v-model="checkDateRange"
            type="daterange"
            :picker-options="finishDateOpts"
            placeholder="选择时间范围">
            </el-date-picker>
        </div>
        <div class="cf">
            <p class="bold">增加数据对账 
                <i class="iconfont cursor font-20 mgr10 inline-block" title="展开" @click="showMore = true" v-show="!showMore">&#xe62f;</i>
                <i class="iconfont cursor font-20 mgr10 inline-block" title="收起" @click="showMore = false" v-show="showMore">&#xe624;</i>
            </p>
            <form class="cf" enctype="multipart/form-data" method="post" :action="baseUrl + '/check/uploadFile'" v-show="showMore" target="nm_iframe">
                <input style="display: none" type="file" name="files" id="fileInput" @change="handlerUpload" accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"> 
                <label for="fileInput" class="el-button el-button--primary el-button--small">选择文件</label>
                <input type="hidden" name="checkId" :value="UUID">
                <input type="submit" class="el-button el-button--success el-button--small mgl10" :class="{'not-allowed': !fileList.length}" name="提交" value="上传到服务器" :disabled="!fileList.length">
                <div class="color-gray font-12">(只能上传excel文件,且不超过10M)</div>
                <div class="font-12 mt10" style="list-style: none">
                    <li class="fong-12 cf" v-for="(d, k) in allFileList" :key="k">
                        <i class="el-icon-document"></i>
                        {{d.name}} 
                        <i class="el-icon-upload-success el-icon-circle-check color-success fr" style="margin-top: 3px" ></i>
                    </li>
                    <li class="fong-12 cf" v-for="(d, k) in fileList" :key="k">
                        <i class="el-icon-document"></i>
                        {{d.name}} 
                        <i class="iconfont cursor font-13 fr" style="margin: 3px -5px 0 0" @click="deleteFile(k)">&#xe65d;</i>
                    </li>
                </div>
            </form>
            <iframe id="id_iframe" name="nm_iframe" style="display:none;></iframe> 
        </div>

        <span slot="footer" class="dialog-footer">
            <el-button @click="closeDialog">取 消</el-button>
            <el-button type="primary" @click="check">开始对账</el-button>
        </span>
    </el-dialog>
</template>

<script>
export default{
    created() {
        this.init();
    },
    mounted() {

    },
    props:{
        dialogCheck: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            dataType: [],
            dataTypeOpts: [
                {
                    value: 1,
                    label: '放款'
                },
                {
                    value: 2,
                    label: '实收'
                }
            ],
            checkDateRange: null,
            pickerOpts: {},
            showMore: false,
            fileList: [],
            allFileList: [],
            UUID: null,
            successUpload: false,
            fileInput: {},
        }
    },
    computed: {
    },
    methods: {
        init() {
            this.finishDateOpts = {
                disabledDate(time) {
                    let thisTime = time.getTime();
                    // 禁用今天及以后
                    return (thisTime > (new Date().setHours(0, 0, 0, 0)) - 1);
                }
            }
        },
        closeDialog() {
            this.dataType = [];
            this.checkDateRange = null;
            this.showMore = false;
            this.successUpload = false;
            this.fileList = [];
            this.allFileList = [];
            this.$emit('close')
        },
        check() {
            let dataType = this.dataType;
            if(!dataType.length) {
                this.$message.error('至少选择一个对账类型')
                return
            }
            if (!this.checkDateRange || !this.checkDateRange[0] || !this.checkDateRange[1]) {
                this.$message.error('请选择对账日期')
                return
            }
            const config = {
                checkTypes: this.dataType.join(','),
                checkId: this.UUID,
                startDate: this.checkDateRange[0].format('yyyy-MM-dd'), 
                endDate: this.checkDateRange[1].format('yyyy-MM-dd'),
            };
            if (dataType.length === 1) {
                config.dataType = this.dataType[0]
            }
            const loadingInstance = this.$loading({
                fullscreen: true,
                text: '对账中'
            })
            this.$http.post('/check/manualCheck', config).then(res => {
                loadingInstance.close();
                this.closeDialog();
                let head = res.data.head || {};
                if(head.code == '100') {
                    this.$message.success(head.message||'对账完毕')
                    this.$emit('update')
                    return;
                }
                this.$message.error(head.message||'对账失败')
            })
        },
        iframeUploadSuccessHandler() {
            const uploadIframe = document.querySelector('#id_iframe');
            uploadIframe.onload = () => {
                let res = JSON.parse(uploadIframe.contentWindow.document.querySelector('body pre').innerHTML)
                const head = res.head;
                if (head.code == 100) {
                    this.$message.success(head.message);
                    this.allFileList = this.allFileList.concat(this.fileList);
                    this.clearWaitFileList()
                    return
                }
                this.$message.error(head.message);
            }
        },
        handlerUpload(e) {
            let fileArray = e.target.files;
            this.fileList = Array.from(fileArray); // 将fileLIst转为数组
            this.successUpload = false;
        },
        deleteFile() {
            this.clearWaitFileList()
        },
        // 清空待上传文件列表
        clearWaitFileList() {
            this.fileInput.value = ''; 
            this.fileList = [];
        }
    },
    watch: {
        dialogCheck(value) {
            if (value) {
                this.UUID = Vue.com.generateUUID();
                setTimeout(() => { // 放在队列后面,等待页面加载完毕
                    this.fileInput = document.querySelector('#fileInput') 
                });

            }

        },
        showMore(value) {
            if (value) {
                this.iframeUploadSuccessHandler();
            } else {
                this.clearWaitFileList()
            }
        }
    }
}
</script>
<style lang="less" scoped>
    #fileInput {
        border: 0;
        display: inline-block;
        line-height: 1;
        white-space: nowrap;
        cursor: pointer;
        background: #fff;
        border: 1px solid #c4c4c4;
        color: #1f2d3d;
        margin: 0;
        padding: 2px 15px;
        border-radius: 4px;
        -webkit-appearance: none;
        -moz-user-select: none;
        -webkit-user-select: none;
        -ms-user-select: none;
        outline: 0;
        text-align: center;
    }
</style>

最终效果:

image

拖拽上传

MDN
有很好的教程。

参考:

  1. wangmeijian
  2. HTML5 DOM的File API
  3. 如何从FileList中删除文件