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

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

一、概述

在Vue项目中,我们经常需要使用到上传文件的功能,为了提高代码的可复用性并减少冗余代码,我们可以封装一个通用的上传文件组件。本篇攻略将介绍如何封装上传文件组件以及如何在Vue项目中使用该组件。

二、上传文件组件的封装

  1. 创建 UploadFile.vue 组件文件,并添加如下代码:
<template>
  <div>
    <input type="file" :multiple="multiple" @change="upload">
  </div>
</template>

<script>
export default {
  name: "UploadFile",
  props: {
    multiple: Boolean, // 是否可多选,默认为false
    uploadUrl: String //文件上传接口地址
  },
  methods: {
    upload(e) {
      const formData = new FormData();
      const files = Array.from(e.target.files);
      for (let i = 0; i < files.length; i++) {
        formData.append("files", files[i]);
      }
      // 文件上传接口
      fetch(this.uploadUrl, {
        method: "POST",
        body: formData,
      })
        .then(function (response) {
          return response.json();
        })
        .then(function (data) {
          console.log("文件上传成功");
        })
        .catch(function (error) {
          console.log(error);
        });
    },
  },
};
</script>
  1. 编写更加灵活的 UploadFile.vue 组件,代码如下:
<template>
  <div>
    <input type="file" :multiple="multiple" @change="upload">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "UploadFile",
  props: {
    multiple: Boolean,
    uploadUrl: String,
    fileTypes: {
      type: Array,
      default: function () {
        return [".jpg", ".png", ".gif", ".jpeg", ".bmp"]; //文件类型限制,默认是图片类型
      },
    },
    maxCount: {
      type: Number,
      default: 1, //默认为最多上传1个文件
    },
  },
  methods: {
    beforeUpload: function (file) {
      const fileType = file.name.slice(file.name.lastIndexOf("."));
      const isFileTypeAllowed = this.fileTypes.includes(fileType);
      if (!isFileTypeAllowed) {
        alert("文件类型不支持");
        return false;
      }
      if (this.maxCount === 1) {
        return true;
      }
      if (this.maxCount && this.maxCount > this.uploadedFiles.length) {
        return true;
      } else {
        alert(`最多只能上传${this.maxCount}个文件`);
        return false;
      }
    },
    upload(e) {
      const formData = new FormData();
      const files = Array.from(e.target.files);
      if (!files.length) return;
      for (let i = 0; i < files.length; i++) {
        if (this.beforeUpload(files[i])) {
          formData.append("files", files[i]);
        }
      }
      // 文件上传接口
      fetch(this.uploadUrl, {
        method: "POST",
        body: formData,
      })
        .then(function (response) {
          return response.json();
        })
        .then((data) => {
          if (data.success) {
            this.$emit("success", data);
          } else {
            this.$emit("fail", data);
          }
          this.$refs.fileInput.value = ""; //清空input的value值,避免上传同一个文件的时候无法触发change事件
        })
        .catch(function (error) {
          console.log(error);
        });
    },
    getUploadedFiles: function () {
      const files = Array.from(this.$refs.fileInput.files);
      const uploadedFiles = [];
      for (let i = 0; i < files.length; i++) {
        uploadedFiles.push({
          name: files[i].name,
          url: URL.createObjectURL(files[i]),
        });
      }
      this.uploadedFiles = uploadedFiles;
    },
    handleRemove(file) {
      this.uploadedFiles.splice(this.uploadedFiles.indexOf(file), 1);
    },
  },
  data() {
    return {
      uploadedFiles: [], //已上传的文件
    };
  },
  mounted() {
    if (this.$slots.default) {
      this.$slots.default.forEach((vnode) => {
        if (vnode.tag) {
          vnode.elm.addEventListener("click", () => {
            this.$refs.fileInput.click();
          });
        }
      });
    }
  },
};
</script>

<style>
.hide-input-file {
  position: absolute;
  visibility: hidden;
  width: 0;
  height: 0;
}
.uploaded-file {
  margin-right: 10px;
  display: inline-block;
  vertical-align: middle;
  position: relative;
  cursor: pointer;
}
.uploaded-file .file-name {
  font-size: 14px;
  max-width: 200px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-block;
  vertical-align: middle;
  margin-left: 5px;
  cursor: pointer;
  -webkit-touch-callout: none; /* iOS Safari禁止长按链接与图片弹出上下文菜单 */
}
.uploaded-file .close-icon {
  display: inline-block;
  vertical-align: middle;
  position: absolute;
  top: 0;
  right: -10px;
  width: 20px;
  height: 20px;
  text-align: center;
  line-height: 20px;
  background-color: rgba(0, 0, 0, 0.4);
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  transition: all 0.3s;
  user-select: none;
  -webkit-touch-callout: none;
}
.uploaded-file .close-icon:hover {
  background-color: rgba(0, 0, 0, 0.6);
  transform: scale(1.2);
}
</style>

三、使用示例

  1. 在Vue项目中引入UploadFile.vue 组件
<template>
  <div>
    <upload-file
      :multiple="true"
      :uploadUrl="'/file/upload' + '?access_token=' + accessToken"
      @success="handleUploadSuccess"
      @fail="handleUploadFail"
    >
      <div class="upload-file-btn">上传文件</div>
    </upload-file>
  </div>
</template>

<script>
import UploadFile from "@/components/UploadFile.vue";
export default {
  name: "FileUploadPage",
  components: { UploadFile },
  data() {
    return {
      accessToken: "",
    };
  },
  methods: {
    handleUploadSuccess(data) {
      console.log("上传成功", data);
    },
    handleUploadFail(data) {
      console.log("上传失败", data);
    },
  },
  mounted() {
    this.accessToken = localStorage.getItem("access_token");
  },
};
</script>

在以上示例中,我们引入了 UploadFile 组件,并给组件传递多个 props 值,它们分别是:multiple(是否可多选,默认为 false)、uploadUrl(文件上传接口地址)。我们通过添加额外的 slot 到组件中,来定制自己的上传按钮。

  1. 使用 UploadFile.vue 组件上传文件限制类型和数量
<template>
  <div>
    <upload-file
      :multiple="true"
      :uploadUrl="'/file/upload' + '?access_token=' + accessToken"
      :max-count="3"
      :file-types="['.jpg', '.png']"
      @success="handleUploadSuccess"
      @fail="handleUploadFail"
    >
      <div class="upload-file-btn">上传图片(最多三张)</div>
    </upload-file>
    <div>
      <div class="uploaded-file" v-for="file in uploadedFiles" :key="file.name">
        <img :src="file.url" alt="上传的图片" width="100" height="100" />
        <div class="file-name">{{ file.name }}</div>
        <div class="close-icon" @click="handleRemove(file)">X</div>
      </div>
    </div>
  </div>
</template>

<script>
import UploadFile from "@/components/UploadFile.vue";
export default {
  name: "FileUploadPage",
  components: { UploadFile },
  data() {
    return {
      uploadedFiles: [],
      accessToken: "",
    };
  },
  methods: {
    handleUploadSuccess(data) {
      console.log("上传成功", data);
      this.getUploadedFiles();
    },
    handleUploadFail(data) {
      console.log("上传失败", data);
    },
    getUploadedFiles() {
      this.$refs.uploadedFiles = this.$refs.uploadFile.uploadedFiles;
      this.uploadedFiles = this.$refs.uploadFile.uploadedFiles;
    },
    handleRemove(file) {
      this.$refs.uploadFile.handleRemove(file);
      this.uploadedFiles = this.$refs.uploadFile.uploadedFiles;
    },
  },
  mounted() {
    this.accessToken = localStorage.getItem("access_token");
  },
};
</script>

在以上示例中,我们除了给 UploadFile 组件传送常规的属性外,额外传递了两个属性:file-types和max-count,它们表示的是文件上传时允许的文件类型和文件数量。在页面中,我们绑定了success和fail事件,可以根据上传结果做出相应的处理。在上传完成之后,我们还会根据上传成功的数据,动态展示出已经上传的文件。

至此,我们就完成了一个通用的上传文件组件,并实现了在Vue项目中使用该组件的示例。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue开发之封装上传文件组件与用法示例 - Python技术站

(0)
上一篇 2023年5月28日
下一篇 2023年5月28日

相关文章

  • Vue中v-on的基础用法、参数传递和修饰符的示例详解

    下面我会详细讲解“Vue中v-on的基础用法、参数传递和修饰符的示例详解”。 1. v-on的基础用法 v-on是Vue的事件绑定指令,它可以监听指定的DOM事件,并在事件触发时执行指定的Vue方法。v-on的基础用法格式为:v-on:事件名=”方法名”,其中事件名可以是任意合法的DOM事件名,方法名则是Vue实例的一个方法名。示例代码: <butto…

    Vue 2023年5月28日
    00
  • vue常用指令代码实例总结

    Vue常用指令代码实例总结攻略 什么是Vue指令 Vue指令是一种特殊的HTML属性, 它们以v-前缀开头,用于渲染模板。指令中可以包含绑定表达式,该表达式的值被动态计算,并且可以自动响应数据变化。 Vue指令有很多种,下面我们来详细讲解一些常用指令的代码实例。 v-if指令 v-if 指令用于根据表达式的值来有条件地渲染某个元素,如果表达式的值为false…

    Vue 2023年5月27日
    00
  • Vue高级特性概念原理详细分析

    Vue高级特性概念原理详细分析 概述 Vue.js是一款流行的JavaScript框架,具有简单易学、高效快捷、灵活可靠等优势,能够为开发人员提供完善的Web应用程序开发解决方案。本文将对Vue.js高级特性概念原理进行详细分析,包括但不限于: Vuex状态管理模式 Vue Router路由器 自定义指令 mixin混合 render 函数 异步组件 Vue…

    Vue 2023年5月27日
    00
  • Vue使用provide各种传值后inject获取undefined的问题及解决

    在Vue中,可以通过provide和inject实现非父子组件之间的数据传递。但是,在实际使用中,会遇到使用provide传递的值在inject中获取时出现undefined的情况。本文将详细讲解这个问题以及解决方案。 问题表现 使用provide向子组件提供数据,但在子组件中使用inject获取时,却获取到了undefined。 问题原因 在Vue中,pr…

    Vue 2023年5月28日
    00
  • 在vue中给后台接口传的值为数组的格式代码

    在Vue中向后台接口传递值需要通过HTTP请求发送数据,一般的格式都是以JSON格式发送。如果要发送一个数组到后台,则需要将该数组转换为JSON格式,再通过HTTP请求发送数据。下面是用数组给后台传值的详细攻略,包含两个示例说明。 将数组转换成JSON 在Vue中需要将数组转换为JSON字符串格式,以便HTTP请求进行发送。使用JSON.stringify(…

    Vue 2023年5月28日
    00
  • Vue EventBus自定义组件事件传递

    Vue EventBus自定义组件事件传递可以让我们在Vue项目中进行组件间的数据传递,在实现一些特殊的功能时非常有用。下面是Vue EventBus自定义组件事件传递的完整攻略。 步骤一:创建EventBus EventBus是一个全局的事件总线,用于在组件间传递数据。我们需要在Vue项目中创建一个新的js文件来实现EventBus。 import Vue…

    Vue 2023年5月28日
    00
  • Vue3插槽(slot)使用方法详解

    Vue3中插槽(slot)是指在组件内部定义好一些模板代码,并在组件使用时通过插槽嵌入到组件内部的技术。本文将详细讲解Vue3插槽的使用方法。 插槽的基本概念 插槽是Vue3中一个重要的特性,它允许组件与它的子组件在编译期间动态传递内容。在Vue2中,插槽分为具名插槽和匿名插槽两种,但在Vue3中只有一种插槽。一个基本的插槽包括两个部分:插槽定义和插槽内容。…

    Vue 2023年5月28日
    00
  • Vue实现万年日历的示例详解

    下面是“Vue实现万年日历的示例详解”的完整攻略。 什么是万年日历? 万年历是一种用于了解日期和节气变化的工具。它可以显示某一年的每个月份的日历,同时也可以显示节气和一些重要的农历节日。在日常生活中,万年历常常被用于查询特定日期的星期几、农历日期等信息。 如何使用Vue实现万年日历? 以下是使用Vue实现万年日历的步骤: 第一步:创建Vue应用程序 在htm…

    Vue 2023年5月29日
    00
合作推广
合作推广
分享本页
返回顶部