粘贴、拖拽、点击上传组件(vue2)

这是一个使用Vue2和Element-UI构建的文件上传组件,支持通过粘贴、拖拽和点击方式上传图片。组件具有图片预览功能,能验证上传文件的类型和大小,并限制最大上传数量。同时,提供了删除图片的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

粘贴、拖拽、点击上传组件(vue2)

效果图

  1. 未上传
    未上传
  2. 上传中
    上传中
  3. 展示等
    展示和删除

代码

  <template>
    <div
      class="copy-box"
      v-loading="fileLoading"
      :disabled="type == 'detail'"
      :element-loading-text="remove ? '删除中' : '上传中'"
    >
      <div
        id="preview"
        ref="drop-box"
        v-if="type != 'detail'"
        @dragover="fileDragover"
        @drop="dropEvent"
        v-on:paste="handlePaste"
      >
        <p>
          将文件粘贴、拖拽到此处或<el-button
            type="text"
            class="upload-btn"
            @click="$refs.evfile.click()"
            >点击上传</el-button
          >
        </p>
        <input
          type="file"
          ref="evfile"
          multiple
          @change="uploadFile"
          style="display: none"
        />
      </div>
      <div class="demo-image__preview" v-if="fileList.length > 0">
        <div
          class="img-list"
          v-for="(item, index) in fileList"
          :key="index + 1"
          @mouseover="imgNum = index + 1"
          @mouseout="imgNum = 0"
        >
          <div
            v-show="imgNum == index + 1 && type != 'detail'"
            class="img-remove"
            @click="handleImgRemove(index, fileList)"
          >
            ×
          </div>
          <el-image
            style="width: 100%; height: 100%"
            :src="item"
            fit="cover"
            :preview-src-list="fileList"
          >
          </el-image>
        </div>
      </div>
    </div>
  </template>

  <script lang="ts">
  import { Component, Vue, Prop, Watch } from "vue-property-decorator";
  import { Image } from "element-ui";
  import { uploadBatchFile, deleteFile } from "@/api/task";

  @Component({
    components: {
      [Image.name]: Image,
    },
  })
  export default class UploadImg extends Vue {
    public $refs!: {
      [propName: string]: any;
    };
    @Prop({ type: Number, default: 5 }) maxSize; // 限制的图片个数
    @Prop({ type: String, default: "edit" }) type; //当前状态
    @Prop({ type: String, default: "" }) data; //图片数据
    @Watch("data", { deep: true, immediate: true })
    handleWatchData(val) {
      console.log(val);
      if (val && val.length) {
        this.fileList = val.split(",");
        // this.fileList = val;
      }
    }
    fileLoading = false;
    private fileList = [];
    private imgNum = 0;
    private remove = false; // 当前删除的状态
    // 验证是否是图片及大小
    private verifyFun(list: any) {
      if (list.length == 0) return false;
      let that = this;
      for (let i = 0; i < list.length; i++) {
        if (list[i].type.indexOf("image") == -1) {
          that.$message("请选择图片上传");
          return false;
        }
        if (list[i].size >= 10485760) {
          that.$message("请上传小于10M的文件");
          return false;
        }
      }
      if (!that.verifyLength(list.length)) {
        that.$message(`所选超过${that.maxSize}`);
        return false;
      }
      return true;
    }
    // 验证传入图片的长度是否超过maxSize
    private verifyLength(list) {
      if (this.fileList.length + list > this.maxSize) {
        return false;
      } else if (this.fileList.length + list <= this.maxSize) {
        return true;
      }
    }

    private handlePaste($event) {
      console.log($event);
      const items = ($event.clipboardData || (window as any).clipboardData).items;
      let file = [];
      if (!items || items.length === 0) {
        this.$message.error("当前浏览器不支持本地");
        return;
      }
      if (!this.verifyFun(items)) {
        return false;
      }
      // 搜索剪切板items
      for (let i = 0; i < items.length; i++) {
        let f = items[i].getAsFile();
        const type = f.type.split("/");
        const renameFile = new File([f], new Date().getTime() + "." + type[1], {
          type: f.type,
        });

        file.push(renameFile);
      }
      this.updata(file);
    }
    private uploadFile(evfile) {
      let file = evfile.target.files;
      if (!this.verifyFun(file)) {
        return false;
      } else {
        this.updata(file);
      }
    }
    private dropEvent(e) {
      e.preventDefault();
      console.log(e.dataTransfer.files, e.dataTransfer.files.length);
      const file = e.dataTransfer.files;
      if (!this.verifyFun(file)) {
        return false;
      } else {
        this.updata(file);
      }
    }
    private updata(fileList: any) {
      console.log(fileList);
      this.fileLoading = true;
      let formData = new FormData();
      for (let x = 0; x < fileList.length; x++) {
        formData.append("file", fileList[x]);
      }
      this.upLoadFun(formData)
        .then((res) => {
          this.$message.success("上传成功");
          this.$emit("successFile", this.fileList.join(","));
          this.$refs.evfile.value = "";
          this.fileLoading = false;
        })
        .catch((e) => {
          this.$emit("errorFile", e);
          this.fileLoading = false;
        });
    }
    private upLoadFun(file) {
      return new Promise((res, rej) => {
        uploadBatchFile(file)
          .then(({ data }) => {
            let d = this.fileList.concat(data);
            this.$set(this, "fileList", d);
            // this.$set(this.form, "verificationScreenshot", d.join(","));
            res(data);
          })
          .catch((e) => {
            rej(e);
          });
      });
    }
    // 删除图片
    handleImgRemove(index, file) {
      this.remove = true;
      this.fileLoading = true;
      console.log(index, file);
      deleteFile(file[index])
        .then((res) => {
          file.splice(index, 1);
          // this.$message.success("删除成功");
          this.fileLoading = false;
          this.remove = false;
          this.$emit("successFile", file.join(","));
        })
        .catch(() => {
          this.fileLoading = false;
          this.remove = false;
        });
    }
    fileDragover(e) {
      e.preventDefault();
    }
  }
  </script>

  <style lang="scss" scoped>
  .copy-box {
    width: 100%;
    text-align: center;
    display: flex;
    flex-direction: column;
    max-height: 300px;
    min-height: 0;
    overflow-y: auto;
    // .upload-btn {
    //   font-size: 14px;
    // }
    #preview {
      min-height: 150px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #409eff;
      font-size: 16px;
      border: 1px dashed #d9d9d9;
      border-radius: 5px;
      color: #606266;
      font-weight: 200;
      font-size: 14px;
    }
    #preview:hover {
      border-color: #409eff;
    }
    .demo-image__preview {
      text-align: left;
      padding-top: 5px;
      .img-list {
        width: 100px;
        height: 100px;
        display: inline-block;
        margin-left: 10px;
        overflow: hidden;
        position: relative;
      }
      .img-list:first-child {
        margin-left: 0;
      }
      .img-list:nth-child(4n + 1) {
        margin-left: 0;
      }
    }
    .img-remove {
      background-color: rgba(0, 0, 0, 0.6);
      color: #fff;
      position: absolute;
      right: 0;
      top: 0;
      cursor: pointer;
      width: 20px;
      height: 20px;
      border-bottom-left-radius: 5px;
      text-align: center;
      line-height: 18px;
      z-index: 10;
    }
    .upload-demo {
      flex-shrink: 0;
    }
  }
  </style>

备注

  1. 给组件外套个盒子调整内容
  2. 里面的代码看自己需求修改
  3. 里面用到了element的Image组件 所以需要注册一下 全局的话 删了就行
  4. 别喷我写的不好,喷就你对 -_- 。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

D丶bird

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值