自定义input组件实现拖拽文件上传

vue部分:

<tag-input
 id="uploadTag"
 ref="uploadTag"
 v-model="fileNameList"
 size="small"
 @input="removeFile"
></tag-input>

逻辑部分:

页面加载时监听拖拽事件,监听后将文件放置下发fileList参数列表中

  mounted() {
    setTimeout(() => {
      this.$nextTick(() => {
        if (this.$refs.uploadTag) {
          let dropEle = this.$refs.uploadTag.$el

          // 禁止拖拽文件后打开文件
          dropEle.addEventListener('drop', e => {
            e.preventDefault();
            e.stopPropagation();
          }, false)

          dropEle.addEventListener('dragover', e => {
            e.preventDefault();
            e.stopPropagation();
          }, false)

          dropEle.addEventListener('dragleave', e => {
            e.preventDefault();
            e.stopPropagation();
          }, false)

          // 处理拖拽文件的逻辑
          dropEle.addEventListener('drop', e => this.watchFileUpload(e))
        }
      })
    }, 1000)
  }
  // 拖拽上传
  private watchFileUpload(e) {
    e.preventDefault();
    e.stopPropagation();

    var df = e.dataTransfer;
    var dropFiles = []; // 拖拽的文件,会放到这里
    var dealFileCnt = 0; // 读取文件是个异步的过程,需要记录处理了多少个文件了
    var allFileLen = df.files.length; // 所有的文件的数量,给非Chrome浏览器使用的变量

    // 检测是否已经把所有的文件都遍历过了
    function checkDropFinish() {
      dealFileCnt++;
    }

    if (df.items !== undefined) {
      // Chrome拖拽文件逻辑
      for (var i = 0; i < df.items.length; i++) {
        var item = df.items[i];
        if (item.kind === "file" && item.webkitGetAsEntry().isFile) {
          var file = item.getAsFile();
          dropFiles.push(file);
        }
      }
    } else {
      // 非Chrome拖拽文件逻辑
      for (var i = 0; i < allFileLen; i++) {
        var dropFile = df.files[i];
        if (dropFile.type) {
          dropFiles.push(dropFile);
          checkDropFinish();
        } else {
          try {
            var fileReader = new FileReader();
            fileReader.readAsDataURL(dropFile.slice(0, 3));

            fileReader.addEventListener('load', function (e) {
              console.log(e, 'load');
              dropFiles.push(dropFile);
              checkDropFinish();
            }, false);

            fileReader.addEventListener('error', function (e) {
              console.log(e, 'error,不可以上传文件夹');
              checkDropFinish();
            }, false);

          } catch (e) {
            console.log(e, 'catch error,不可以上传文件夹');
            checkDropFinish();
          }
        }
      }
    }
    dropFiles.forEach(item => {
      this.fileList.push(item)
    })
    this.fileNameList = this.fileList.map(item => {
      if (item.name) {
        return item.name
      }
      if (item.fileName) {
        return item.fileName
      }
    });
  }

删除当前文件:

  // 附件删除 下拉框
  private removeFile(nameList, name) {
    // 记录删除的附件信息
    this.fileList.splice(this.fileList.findIndex(item => item.fileName === name || item.name === name), 1)
    this.fileNameList = this.fileList.map(item => item.name || item.fileName);
  }

封装的tag-input组件:

<template>
  <div
    class="yh-input-tag input-tag-wrapper"
    ref="InputTag"
    @click="foucusTagInput"
  >
    <el-tag
      v-for="(tag, idx) in innerTags"
      :key="tag"
      :size="size"
      :closable="!readonly"
      :disable-transitions="false"
      @close="remove(tag, idx)"
      >{{ tag }}</el-tag
    >
    <input
      :readonly="readonly || readonlyIpt"
      class="tag-input"
      :class="[size ? 'yh-input-tag--' + size : '']"
      :style="widthStyle"
      :placeholder="isplaceholder"
      v-model="newTag"
      @keydown.delete.stop="removeLastTag"
      @keydown="addNew"
      @blur="blurTagInput"
    />
  </div>
</template>

<script>

export default {
  name: 'InputTag',
  props: {
    value: {
      type: Array,
      default: () => []
    },
    addTagOnKeys: {
      type: Array,
      default: () => [13, 188, 9]
    },
    readonly: {
      type: Boolean,
      default: false
    },
    // 输入框只读
    readonlyIpt: {
      type: Boolean,
      default: false
    },
    size: String,
    placeholder: {
      type: String,
      default: '请输入'
    }
  },
  inject: {
    elForm: {
      default: ''
    },
    elFormItem: {
      default: ''
    }
  },
  data () {
    return {
      newTag: '',
      innerTags: [...this.value],
      currentTag: null,
      widthStyle: {
        minWidth: '10px'
      }
    }
  },
  computed: {
    isplaceholder () {
      let str = ''
      if(this.value?.length > 0) {
        this.$nextTick(() => {
          if (this.$refs.yhInputTag) {
            this.$refs.InputTag.style.padding = '0'
          }
        })
        str = ''
      } else {
        this.$nextTick(() => {
          if (this.$refs.yhInputTag) {
            this.$refs.InputTag.style.padding = '0 15px'
          }
        })
        str = this.placeholder
      }
      return str
    },
    // 表单禁用关联
    inputDisabled() {
      return this.disabled || (this.elForm || {}).disabled;
    }
  },
  watch: {
    value: {
      handler(newVal, oldVal) {
        if (this.elForm && oldVal !== undefined && newVal !== oldVal) {
          this.elForm.validateField(this.elFormItem.prop)
        }
        if (newVal) {
          this.innerTags = [...newVal]
        }
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    foucusTagInput () {
      if (this.readonly || this.readonlyIpt || !this.$el.querySelector('.tag-input')) {
        return
      } else {
        this.$el.querySelector('.tag-input').focus()
        this.widthStyle = {
          minWidth: '10px'
        }
      }
    },
    blurTagInput (e) {
      this.addNew(e)
      this.widthStyle = {
        width: '0px'
      }
    },
    addNew (e) {
      if (e && (!this.addTagOnKeys.includes(e.keyCode)) && (e.type !== 'blur')) {
        return
      }
      if (e) {
        e.stopPropagation()
        e.preventDefault()
      }
      let addSuucess = false
      if (this.newTag.includes(',')) {
        this.newTag.split(',').forEach(item => {
        if (this.addTag(item.trim())) {
            addSuucess = true
          }
        })
      } else {
        if (this.addTag(this.newTag.trim())) {
          addSuucess = true
        }
      }
      if (addSuucess) {
        this.tagChange()
        this.newTag = ''
      }
    },
    addTag (tag) {
      tag = tag.trim()
      if (tag && !this.innerTags.includes(tag)) {
        this.innerTags.push(tag)
        return true
      }
      return false
    },
    remove (tag, index) {
      this.innerTags.splice(index, 1)
      this.currentTag = tag
      this.tagChange()
    },
    removeLastTag () {
      if (this.newTag) {
        return
      }
      this.innerTags.pop()
      this.tagChange()
    },
    tagChange () {
      this.$forceUpdate()
      this.$emit('input', JSON.parse(JSON.stringify(this.innerTags)), this.currentTag)
    }
  }
}
</script>

<style scoped>
.input-tag-wrapper {
  position: relative;
  font-size: 14px;
  background-color: #fff;
  background-image: none;
  border-radius: 4px;
  border: 1px solid #DCDFE6;
  box-sizing: border-box;
  color: #575757;
  display: inline-block;
  cursor: text;
  outline: none;
  padding: 0 15px;
  transition: border-color .2s cubic-bezier(.645,.045,.355,1);
  width: 100%;
  line-height: normal;
  &:hover{
    border-color: #C5C6C7;
  }
  &:focus{
    border-color: #d32f2f;
  }
  .el-tag{
    box-sizing: border-box;
    border-color: transparent;
    margin: 2px 0 2px 6px;
    background-color: #f0f2f5;
    display: inline-flex;
    max-width: 100%;
    align-items: center;
  }
}

.tag-input {
  background: transparent;
  border: 0;
  font-size: 14px;
  outline: none;
  padding-left: 0;
  height: 26px;
  &::placeholder {
    color: #C8C9CA;
  }
}
.yh-input-tag--mini{
  height: 26px;
  line-height: 26px;
  .tag {
    height: 16px;
  }
}

.yh-input-tag--small{
  height: 30px;
  line-height: 30px;
  .tag {
    height: 20px;
  }
}

.yh-input-tag--medium{
  height: 34px;
  line-height: 34px;
  .tag {
    height: 24px;
  }
}

// 表单标签选择器必填样式
.el-form-item.is-error .input-tag-wrapper,
.el-form-item.is-error .input-tag-wrapper:focus {
  border-color: #bc1126 !important;
}
</style>

最后实现的效果:

可支持手动拖拽上传 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值