vue 实现上传组件

2022-04-15 0 844
目录
  • 1.介绍
  • 2.思路
    • 文件上传的两种实现方式
  • 3.生命周期
    • 4.代码草稿
      • 5.具体实现

        1.介绍

        效果如下图

        vue 实现上传组件 

        2.思路

        文件上传的两种实现方式

        1.From形式

        <form 
          method="post" 
          enctype="multipart/from-data"
          action="api/upload"
        >
          <input type="file name="file">
          <button type="submit">Submit</button>
        </form>
        

        form的method属性指定为 “post” 请求,通过HTML表单发送数据给服务器,并返回服务器的修改结果,在这种情况下Content-Type是通过在<form>元素中设置正确的enctype属性。

        form的enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。

        • application/x-www-form-urlencoded(默认值):表示在发送前编码所有字符,数据被编码成以”&”分隔的键值对,同时以”=”分隔键和值,(“name=seven&age=19”)。不支持二进制数据。
        • multipart/form-data:支持二进制数据(上传文件时必须指定)

        2.JavaScript异步请求形式

        我们知道 FormData 接口提供了一种表示表单数据的键值对 key/value 的构造方式,并且可以轻松的将数据通过XMLHttpRequest.send()方法发送出去,本接口和此方法都相当简单直接。如果送出时的编码类型被设为 “multipart/form-data”,它会使用和表单一样的格式。

        var formdata = new FormData(); // 创建FormData对象
        formdata.append("name","laotie"); // 通过append()方法添加新的属性值
        ... // 更多方法请点下面链接
        

        FormData接口

        3.生命周期

        上传组件也有它的生命周期

        beforeUpload –> uploading –> fileUploaded 或者 uploadedError

        4.代码草稿

        本例中采用js异步请求的方式开发上传组件

        <input type="file" name="file" @change.prevent="handleFileChange">
        // 创建一个file类型的input,用于触发文件上传,后面可以把input隐藏掉,自定义好看的样式
        // 自定义样式的时候可以用slot区分不同上传状态的样式(loading,success,defult)
        
        const handleFileChange = (e:Event)=>{
          const target = e.target as HTMLInputElement
          const files = Array.from(target.files)// 注意这里取得的是一个类数组
          if(files){
            // 取得文件
            const uploadedFile = files[0]
            
            if(!validateFormat) return
            // ...这里只是提供一种思路,具体校验不再讲述
            // 在这里做一些上传文件前的校验,比如文件格式,大小等,
            // 不符合要求的话就不在继续发送请求
            
            const formData = new FormData()
            formData.append(uploadedFile.name,uploadedFile)
            
            axios.post('/upload',formData,{
              headers:{
                 // 注意设置编码类型
                'Content-Type': 'multipart/form-data'
              }
            }).then(res=>{
              console.log('上传成功')
            }).catch(error =>{
              // 文件上传失败
            }).finally(()=>{
              // 文件上传完成,无论成功还是失败
              // 这里可以清除一下input.value
            })
          }
        }
        

        5.具体实现

        // Upload.vue
        <template>
          <div class="upload-container">
            <div class="upload-box" @click.prevent="triggerUpload" v-bind="$attrs">
              <slot name="loading" v-if="fileStatus==='loading'">
                <button class="btn btn-primary">上传中</button>
              </slot>
              <slot name="uploaded" v-else-if="fileStatus==='success'" :uploadedData="fileData">
                <button class="btn btn-primary">上传成功</button>
              </slot>
              <slot v-else name="default">
                <button class="btn btn-primary">点击上传</button>
              </slot>
            </div>
            <input type="file" class="file-input d-none" name="file" ref="uploadInput" @change="hanldeInput"/>
          </div>
        </template>
        <script lang="ts">
        import { defineComponent, ref, PropType, watch } from 'vue'
        import axios from 'axios'
        type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
        type FunctionProps = (file:File) => boolean
        export default defineComponent({
          name: 'Upload',
          inheritAttrs: false,
          props: {
            // 上传的url
            action: {
              type: String,
              required: true
            },
            // 上传之前的校验,是一个返回布尔值的函数
            beforeUpload: {
              type: Function as PropType<FunctionProps>
            },
            // 上传好的数据,用来判断状态或做初始化展示
            uploadedData: {
              type: Object
            }
          },
          emits: ['file-uploaded-success', 'file-uploaded-error'],
          setup(props, ctx) {
            const uploadInput = ref<null | HTMLInputElement>(null)
            const fileStatus = ref<UploadStatus>(props.uploadedData ? 'success' : 'ready')
            const fileData = ref(props.uploadedData)
            watch(() => props.uploadedData, (val) => {
              if (val) {
                fileStatus.value = 'success'
                fileData.value = val
              }
            })
            const triggerUpload = () => {
              if (uploadInput.value) {
                uploadInput.value.click()
              }
            }
            const hanldeInput = (e:Event) => {
              const target = e.target as HTMLInputElement
              const files = target.files
              console.log(target)
              if (files) {
                const uploadFile = Array.from(files)
                const validateFormat = props.beforeUpload ? props.beforeUpload(uploadFile[0]) : true
                if (!validateFormat) return
                fileStatus.value = 'loading'
                const formData = new FormData()
                formData.append('file', uploadFile[0])
                axios.post(props.action, formData, {
                  headers: {
                    'Content-Type': 'multipart/form-data'
                  }
                }).then(res => {
                  console.log('文件上传成功', res)
                  fileStatus.value = 'success'
                  fileData.value = res.data
                  ctx.emit('file-uploaded-success', res.data)
                }).catch(error => {
                  console.log('文件上传失败', error)
                  fileStatus.value = 'error'
                  ctx.emit('file-uploaded-error', error)
                }).finally(() => {
                  console.log('文件上传完成')
                  if (uploadInput.value) {
                    uploadInput.value.value = ''
                  }
                })
              }
            }
        
            return {
              uploadInput,
              triggerUpload,
              hanldeInput,
              fileStatus,
              fileData
            }
          }
        })
        </script>
        
        

        使用示例:

        <template>
          <div class="create-post-page">
            <upload
              action="/upload"
              :beforeUpload="beforeUpload"
              :uploadedData="uploadedData"
              @file-uploaded-success="hanldeUploadSuccess"
              class="d-flex align-items-center justify-content-center bg-light text-secondary w-100 my-4"
              >
              <template #uploaded="slotProps">
                <div class="uploaded-area">
                  <img :src="slotProps.uploadedData.data.url"/>
                  <h3>点击重新上传</h3>
                </div>
               </template>
               <template #default>
                 <h2>点击上传头图</h2>
               </template>
               <template #loading>
                 <div class="d-flex">
                  <div class="spinner-border text-secondary" role="status">
                    <span class="sr-only"></span>
                  </div>
                 </div>
               </template>
            </upload>
          </div>
        </template>
        <script lang="ts">
        import { defineComponent, ref, onMounted } from 'vue'
        import Upload from '../components/Upload.vue'
        import createMessage from '../components/createMessage'
        
        export default defineComponent({
          name: 'CreatePost',
          components: { Upload },
          setup() {
            const uploadedData = ref() //创建一个响应式数据
            let imageId = ''
            onMounted(() => {
              ....
              // 这里有逻辑省略了,取到初始化数据image
              if (image) {
                uploadedData.value = { data: image }
              }
            })
            // 上传前校验,返回布尔值
            const beforeUpload = (file:File) => {
              const res = beforeUploadCheck(file, {
                format: ['image/jpeg', 'image/png'],
                size: 1
              })
              const { error, passed } = res
              if (error === 'format') {
                createMessage('上传图片只能是JPG/PNG格式!', 'error')
              }
              if (error === 'size') {
                createMessage('上传图片大小不能超过1MB', 'error')
              }
              return passed
            }
            // 上传成功后拿到imageId就可以进行后续处理,创建表单啥的
            const hanldeUploadSuccess = (res:ResponeseProps<ImageProps>) => {
              createMessage(`上传图片ID ${res.data._id}`, 'success')
              if (res.data._id) {
                imageId = res.data._id
              }
            }
            return {
              beforeUpload,
              hanldeUploadSuccess,
              uploadedData
            }
          }
        })
        </script>
        <style>
        .create-post-page{
          padding:0 20px 20px;
        }
        .create-post-page .upload-box{
          height:200px;
          cursor: pointer;
          overflow: hidden;
        }
        .create-post-page .upload-box img{
          width: 100%;
          height: 100%;
          object-fit: cover;
        }
        .uploaded-area{
          position: relative;
        }
        .uploaded-area:hover h3{
          display: block;
        }
        .uploaded-area h3{
          display: none;
          position: absolute;
          color: #999;
          text-align: center;
          width: 100%;
          top:50%
        }
        </style>
        
        

        以上就是vue 实现上传组件的详细内容,更多关于vue 上传组件的资料请关注NICE源码其它相关文章!

        免责声明:
        1、本网站所有发布的源码、软件和资料均为收集各大资源网站整理而来;仅限用于学习和研究目的,您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 不得使用于非法商业用途,不得违反国家法律。否则后果自负!

        2、本站信息来自网络,版权争议与本站无关。一切关于该资源商业行为与www.niceym.com无关。
        如果您喜欢该程序,请支持正版源码、软件,购买注册,得到更好的正版服务。
        如有侵犯你版权的,请邮件与我们联系处理(邮箱:skknet@qq.com),本站将立即改正。

        NICE源码网 JavaScript vue 实现上传组件 https://www.niceym.com/28085.html