封装一个Vue文件上传组件案例详情

目录
  • 前言
  • 1. 子组件
  • 2 父组件使用
  • 3.效果
  • 4.总结

前言

在面向特定用户的项目中,引 其他ui组件库导致打包体积过大,首屏加载缓慢,还需要根据UI设计师设计的样式,重写大量的样式覆盖引入的组件库的样式。因此尝试自己封装一个自己的组件,代码参考了好多前辈的文章

1. 子组件

<template>
    <div class="digital_upload">
        <input
            style="display: none"
            @change="addFile"
            :multiple="multiple"
            type="file"
            :name="name"
            :id="id"
            :accept="accept"
        />
        <label :for="id">
            <slot></slot>
        </label>
    </div>
</template>

<script>
export default {
    name: 'my-upload',
    props: {
        name: String,
        action: {
            type: String,
            required: true
        },
        fileList: {
            type: Array,
            default () {
                return []
            }
        },
        accept: {
            type: String,
            require: true
        },
        id: {
            type: String,
            default: 'my-upload'
        },
        data: Object,
        multiple: Boolean,
        limit: Number,
        onChange: Function,
        onBefore: Function,
        onProgress: Function,
        onSuccess: Function,
        onFailed: Function,
        onFinished: Function
    },
    methods: {
        // input的 chang事件处理方法
        addFile ({ target: { files } }) { // input标签触发onchange事件时,将文件加入待上传列表
            for (let i = 0, l = files.length; i < l; i++) {
                files[i].url = URL.createObjectURL(files[i])// 创建blob地址,不然图片怎么展示?
                files[i].status = 'ready'// 开始想给文件一个字段表示上传进行的步骤的,后面好像也没去用......
            }
            let fileList = [...this.fileList]
            if (this.multiple) { // 多选时,文件全部压如列表末尾
                fileList = [...fileList, ...files]
                const l = fileList.length
                let limit = this.limit
                if (limit && typeof limit === 'number' && Math.ceil(limit) > 0 && l > limit) { // 有数目限制时,取后面limit个文件
                    limit = Math.ceil(limit)
                    // limit = limit > 10 ? 10 : limit;
                    fileList = fileList.slice(l - limit)
                }
            } else { // 单选时,只取最后一个文件。注意这里没写成fileList = files;是因为files本身就有多个元素(比如选择文件时一下子框了一堆)时,也只要一个
                fileList = [files[0]]
            }
            this.onChange(fileList)// 调用父组件方法,将列表缓存到上一级data中的fileList属性
        },
        // 移除某一个文件
        remove (index) {
            const fileList = [...this.fileList]
            if (fileList.length) {
                fileList.splice(index, 1)
                this.onChange(fileList)
            }
        },
        // 检测是否可以提交
        checkIfCanUpload () {
            console.log(this.fileList.length)
            return this.fileList.length ? ((this.onBefore && this.onBefore()) || !this.onBefore) : false
        },
        // 根据情况使用不同的提交的方法
        submit () {
            console.log('开始提交')
            if (this.checkIfCanUpload()) {
                // console.log('开始提交2')
                if (this.onProgress && typeof XMLHttpRequest !== 'undefined') {
                    this.xhrSubmit()
                } else {
                    this.fetchSubmit()
                }
            }
        },
        // fethc 提交
        fetchSubmit () {
            const keys = Object.keys(this.data); const values = Object.values(this.data); const action = this.action
            const promises = this.fileList.map(each => {
                each.status = 'uploading'
                const data = new FormData()
                data.append(this.name || 'file', each)
                keys.forEach((one, index) => data.append(one, values[index]))
                return fetch(action, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    body: data
                }).then(res => res.text()).then(res => JSON.parse(res))// 这里res.text()是根据返回值类型使用的,应该视情况而定
            })
            Promise.all(promises).then(resArray => { // 多线程同时开始,如果并发数有限制,可以使用同步的方式一个一个传,这里不再赘述。
                let success = 0; let failed = 0
                resArray.forEach((res, index) => {
                    if (res.code === 1) {
                        success++ // 统计上传成功的个数,由索引可以知道哪些成功了
                        this.onSuccess(index, res)
                    } else if (res.code === 520) { // 约定失败的返回值是520
                        failed++ // 统计上传失败的个数,由索引可以知道哪些失败了
                        this.onFailed(index, res)
                    }
                })
                return { success, failed } // 上传结束,将结果传递到下文
            }).then(this.onFinished) // 把上传总结果返回
        },
        // xhr 提交
        // xhrSubmit () {
        //     const _this = this
        //     const options = this.fileList.map((rawFile, index) => ({
        //         file: rawFile,
        //         data: _this.data,
        //         filename: _this.name || 'file',
        //         action: _this.action,
        //         headers: {
        //             Authorization: window.sessionStorage.getItem('token')
        //         },
        //         onProgress (e) {
        //             _this.onProgress(index, e)// 闭包,将index存住
        //         },
        //         onSuccess (res) {
        //             _this.onSuccess(index, res)
        //         },
        //         onError (err) {
        //             _this.onFailed(index, err)
        //         },
        //         onFinished (res) { // 这里补充一个配置
        //             _this.onFinished(res) //
        //         }
        //     }))
        //     const l = this.fileList.length
        //     const send = async options => {
        //         for (let i = 0; i < l; i++) {
        //             await _this.sendRequest(options[i])// 这里用了个异步方法,按次序执行this.sendRequest方法,参数为文件列表包装的每个对象,this.sendRequest下面紧接着介绍
        //         }
        //     }
        //     send(options)
        // },
        xhrSubmit () {
            const _this = this
            const options = {
                file: this.fileList,
                data: _this.data,
                filename: _this.name || 'file',
                action: _this.action,
                headers: {
                    Authorization: window.sessionStorage.getItem('token')
                },
                onProgress (e) {
                    _this.onProgress(1, e)// 闭包,将index存住
                },
                onSuccess (res) {
                    _this.onSuccess(1, res)
                },
                onError (err) {
                    _this.onFailed(1, err)
                },
                onFinished (res) { // 这里补充一个配置
                    _this.onFinished(res) //
                }
            }
            // this.fileList.map((rawFile, index) => ({
            //     file: rawFile,
            //     data: _this.data,
            //     filename: _this.name || 'file',
            //     action: _this.action,
            //     headers: {
            //         Authorization: window.sessionStorage.getItem('token')
            //     },
            //     onProgress (e) {
            //         _this.onProgress(index, e)// 闭包,将index存住
            //     },
            //     onSuccess (res) {
            //         _this.onSuccess(index, res)
            //     },
            //     onError (err) {
            //         _this.onFailed(index, err)
            //     },
            //     onFinished (res) { // 这里补充一个配置
            //         _this.onFinished(res) //
            //     }
            // }))
            console.log(options)
            _this.sendRequest(options)
            // const l = this.fileList.length
            // const send = async options => {
            //     for (let i = 0; i < l; i++) {
            //         await _this.sendRequest(options[i])// 这里用了个异步方法,按次序执行this.sendRequest方法,参数为文件列表包装的每个对象,this.sendRequest下面紧接着介绍
            //     }
            // }
            // send(options)
        },
        // 发起上传请求这里借鉴了element-ui的上传源码
        sendRequest (option) {
            // const _this = this
            upload(option)
            function getError (action, option, xhr) {
                // eslint-disable-next-line no-void
                var msg = void 0
                if (xhr.response) {
                    msg = xhr.status + ' ' + (xhr.response.error || xhr.response)
                } else if (xhr.responseText) {
                    msg = xhr.status + ' ' + xhr.responseText
                } else {
                    msg = 'fail to post ' + action + ' ' + xhr.status
                }
                var err = new Error(msg)
                err.status = xhr.status
                err.method = 'post'
                err.url = action
                return err
            }

            function getBody (xhr) {
                var text = xhr.responseText || xhr.response
                if (!text) {
                    return text
                }

                try {
                    return JSON.parse(text)
                } catch (e) {
                    return text
                }
            }

            function upload (option) {
                if (typeof XMLHttpRequest === 'undefined') {
                    return
                }

                var xhr = new XMLHttpRequest()
                var action = option.action

                if (xhr.upload) {
                    xhr.upload.onprogress = function progress (e) {
                        if (e.total > 0) {
                            e.percent = e.loaded / e.total * 100
                        }
                        option.onProgress(e)
                    }
                }
                var formData = new FormData()

                if (option.data) {
                    Object.keys(option.data).map(function (key) {
                        formData.append(key, option.data[key])
                    })
                }

                option.file.forEach(item => {
                    formData.append(option.filename, item)
                })
                // formData.append(option.filename, option.file)

                xhr.onerror = function error (e) {
                    option.onError(e)
                }

                xhr.onload = function onload () {
                    if (xhr.status < 200 || xhr.status >= 300) {
                        return option.onError(getError(action, option, xhr))
                    }

                    option.onSuccess(getBody(xhr))
                }

                xhr.open('post', action, true)

                if (option.withCredentials && 'withCredentials' in xhr) {
                    xhr.withCredentials = true
                }

                var headers = option.headers || {}

                for (var item in headers) {
                    // eslint-disable-next-line no-prototype-builtins
                    if (headers.hasOwnProperty(item) && headers[item] !== null) {
                        xhr.setRequestHeader(item, headers[item])
                    }
                }
                xhr.send(formData)
                return xhr
            }
        }
    }
}
</script>
<style lang="less" scoped>
</style>

2 父组件使用

<template>
    <div id="upload_works">
        <div class="type_info" >
            <div class="info">支持JPG,PNG,GIF格式,大小不超过20M</div>
            <!-- 自己封装的手动上传文件的组件 -->
            <digital-upload
                accept="image/*"
                ref="myUpload"
                action="/api/production/uploadImgList"
                name="files"
                id="my-upload"
                multiple
                :limit="10"
                :file-list="fileList"
                :data="param"
                :on-change="onChange"
                :on-progress="uploadProgress"
                :on-success="uploadSuccess"
                :on-failed="uploadFailed"
                :on-finished="onFinished"
            >
                <div class="normal_button">上传文件</div>
            </digital-upload>
            <div class="three">或拖放到这里</div>
        </div>
    </div>
</template>

<script>
import digitalUpload from '@/digitalComponents/upload/digitalUpload.vue'
export default {
    data () {
        return {
            fileList: [],
            param: { id: null }
        }
    },
    methods: {
        onChange (fileList) { // 监听文件变化,增减文件时都会被子组件调用
            console.log(fileList)
            this.fileList = [...fileList]
        },
        uploadSuccess (index, response) { // 某个文件上传成功都会执行该方法,index代表列表中第index个文件
            console.log('图片上传成功')
            console.log(index, response)
            this.submitWorksCover()
        },
        upload () { // 触发子组件的上传方法
            this.$refs.myUpload.submit()
        },
        removeFile (index) { // 移除某文件
            this.$refs.myUpload.remove(index)
        },
        uploadProgress (index, progress) { // 上传进度,上传时会不断被触发,需要进度指示时会很有用
            const { percent } = progress
            console.log(index, percent)
        },
        uploadFailed (index, err) { // 某文件上传失败会执行,index代表列表中第index个文件
            console.log(index, err)
        },
        onFinished (result) { // 所有文件上传完毕后(无论成败)执行,result: { success: 成功数目, failed: 失败数目 }
            console.log(result)
        }
    },
    components: {
        digitalUpload
    }
}
</script>
<style lang="less" scoped>
</style>

3.效果

4.总结

前端项目如果不是中台项目,自己封装组件是非常有比较的,这里比较麻烦的地方是 父组件引用需要些大量的方法,您也可以再封

到此这篇关于 封装一个Vue文件上传组件案例详情的文章就介绍到这了,更多相关Vue 封装文件上传组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue开发之封装上传文件组件与用法示例

    本文实例讲述了Vue开发之封装上传文件组件与用法.分享给大家供大家参考,具体如下: 使用elementui的 el-upload插件实现图片上传组件 每个项目存在一定的特殊性,所以数据的处理会不同 pictureupload.vue: <template> <div class="pictureupload"> <el-upload :action="baseUrl + '/api/public/image'" list-type=&q

  • Vue封装一个简单轻量的上传文件组件的示例

    一.之前遇到的一些问题 项目中多出有上传文件的需求,使用现有的UI框架实现的过程中,不知道什么原因,总会有一些莫名其妙的bug.比如用某上传组件,明明注明(:multiple="false"),可实际上还是能多选,上传的时候依然发送了多个文件:又比如只要加上了(:file-list="fileList")属性,希望能手动控制上传列表的时候,上传事件this.refs.[upload(组件ref)].submit()就不起作用了,传不了.总之,懒得再看它怎么实现了,我

  • 封装一个Vue文件上传组件案例详情

    目录 前言 1. 子组件 2 父组件使用 3.效果 4.总结 前言 在面向特定用户的项目中,引 其他ui组件库导致打包体积过大,首屏加载缓慢,还需要根据UI设计师设计的样式,重写大量的样式覆盖引入的组件库的样式.因此尝试自己封装一个自己的组件,代码参考了好多前辈的文章 1. 子组件 <template> <div class="digital_upload"> <input style="display: none" @change=&

  • jQuery.uploadify文件上传组件实例讲解

    1.jquery.uploadify简介 在ASP.NET中上传的控件有很多,比如.NET自带的FileUpload,以及SWFUpload,Uploadify等等,尤其后面两个控件的用户体验比较好,无刷新,带上传进度等等.在最近的短信平台开发中,使用Uploadify进行文件上传. Uploadify官网地址是:http://www.uploadify.com/ 可满足项目开发需求. 下载地址:http://www.uploadify.com/wp-content/uploads/files/

  • PHP封装的多文件上传类实例与用法详解

    本文实例讲述了PHP封装的多文件上传类实例与用法.分享给大家供大家参考,具体如下: <?php /**//* * @(#)UploadFile.php * * 可同时处理用户多个上传文件.效验文件有效性后存储至指定目录. * 可返回上传文件的相关有用信息供其它程序使用.(如文件名.类型.大小.保存路径) * 使用方法请见本类底部(UploadFile类使用注释)信息. * */ class UploadFile { var $user_post_file = array(); //用户上传的文件

  • vue iView 上传组件之手动上传功能

    基于 Vue 的 UI 组件库现在已经有很多了,尤其是移动端的.现在又多了一个 iView . iView 主要服务于 PC 界面的中后台业务,是一套高质量的开源 UI 组件库,先上地址: https://github.com/iview/iview 官网是: https://www.iviewui.com/ 特性 使用单文件的 Vue 组件化开发模式 基于 npm + webpack + babel 开发,支持 ES2015 高质量.功能丰富 友好的 API ,自由灵活地使用空间 详细.友好的

  • vue3.0搭配.net core实现文件上传组件

    在开发Web应用程序中,文件上传是经常用到的一个功能. 在Jquery时代,做上传功能,一般找jQuery插件就够了,很少有人去探究上传文件插件到底是怎么做的. 简单列一下我们要做的技术点和功能点 使用技术 客户端使用vue.js 3.0,并使用vue3新增的功能:Composition API ,服务器使用asp.net core 功能点 标签美化 文件预览 文件上传 服务器接收文件 文件选择美化 在标准的html文件选择标签,是十分不美观的.大概就是下图的样子 但是我们的设计师的设计图可不是

  • vue 实现上传组件

    1.介绍 效果如下图 2.思路 文件上传的两种实现方式 1.From形式 <form method="post" enctype="multipart/from-data" action="api/upload" > <input type="file name="file"> <button type="submit">Submit</button>

  • Bootstrap fileinput文件上传组件使用详解

    一.使用方法 1.导入依赖的js和css文件: <link rel="stylesheet" href="css/bootstrap.min.css" /> <link rel="stylesheet" href="css/fileinput.min.css" /> <script type="text/javascript" src="js/jquery-3.2.1

  • BootStrap fileinput.js文件上传组件实例代码

    1.首先我们下载好fileinput插件引入插件 <span style="font-size:14px;"><link type="text/css" rel="stylesheet" href="fileinput/css/fileinput.css" rel="external nofollow" /> <script type="text/javascript

  • 最好用的Bootstrap fileinput.js文件上传组件

    本篇介绍如何使用bootstrap fileinput.js(最好用的文件上传组件)来进行图片的展示,上传,包括springMVC后端文件保存. 一.demo 二.插件引入 <link type="text/css" rel="stylesheet" href="${ctx}/components/fileinput/css/fileinput.css" /> <script type="text/javascript

  • JSP实用教程之简易文件上传组件的实现方法(附源码)

    前言 本文主要给大家介绍的是关于JSP简易文件上传组件的实现方法,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 文件上传,包括但不限于图片上传,是 Web 开发中司空见惯的场景,相信各位或多或少都曾写过这方面相关的代码.Java 界若说文件上传,则言必称 Apache Commons FileUpload,论必及  SmartUpload.更甚者,Servlet 3.0 将文件上传列入 JSR 标准,使得通过几个注解就可以在 Servlet 中配置上传,无须依赖任何组件.使用第

随机推荐