美文网首页
二次封装ele-ui上传公共组件(支持多选,放大,删除)

二次封装ele-ui上传公共组件(支持多选,放大,删除)

作者: 邹小小白 | 来源:发表于2021-05-11 17:46 被阅读0次

    在compnents新建一个uploader.vue文件
    <template>

      <dir class="element-ui-uploader-extension">

        <div class="file-list row">

          <div class="file" v-for="obj in fileList" :key="obj.uid" :style="previewStyle">

            <img class="img-cover" v-if="obj.status === 'success' && type === 'image'" :src="obj.path">

            <video class="img-cover" v-else-if="obj.status === 'success' && type === 'video'" :src=" obj.path"></video>

            <!-- 默认图 -->

            <img class="img-cover" v-else-if="obj.status === 'uploading'" src="/img/image_default.png">

            <!-- 进度条 -->

            <el-progress v-if="obj.status === 'uploading'" class="file-progress" type="circle" :width="50" :stroke-width="2" :percentage="Number(obj.percentage.toFixed(2))"></el-progress>

            <div v-if="obj.status === 'success'" class="control-mask">

              <i class="el-icon-zoom-in" @click="view(obj)"></i>

              <i v-if="!disabled" class="el-icon-delete" @click="del(obj)"></i>

            </div>

          </div>

          <el-upload v-if="!disabled" ref="upload" v-show="fileList.length < max"  accept=".png, .jpg, .jpeg" :action="uploadUrl" :show-file-list="false" :limit="max" :multiple="multiple" :disabled="disabled" :headers="headers" :on-change="change" :on-exceed="exceed" :on-progress="progress" :before-upload="beforeUpload" :on-success="success" :on-error="error" :on-remove="remove" :before-remove="beforeRemove">

            <div class="placeholder" :style="previewStyle">

              <i class="el-icon-plus" title="上传"></i>

              <span class='placeholder-text'>{{ placeholderText }}</span>

            </div>

          </el-upload>

        </div>

        <el-dialog v-if="dialogVisible" width="800px" :visible.sync="dialogVisible" :close-on-click-modal="false" append-to-body :lock-scroll="false">

          <img style="width: 100%;height:100%;object-fit:cover" v-if="type === 'image'" class="img-cover" :src="dialogImageUrl" alt="">

          <video v-if="type === 'video'" class="img-cover" :src="dialogImageUrl" controls="controls"></video>

        </el-dialog>

      </dir>

    </template>

    <script>

    import {getToken} from '@/util/auth'; // 获取上传token

    export default {

      props: {

        type: {

          // 上传文件类型 [image, video, file]

          type: String,

          default: function () {

            return 'image'

          }

        },

        value: {

          // 表单值 用逗号分隔的资源相对地址

          // type: String,

          required: true

        },

        max: {

          // 上传文件数量

          type: Number,

          default: function () {

            return 1

          }

        },

        maxMB: {

          // 上传文件大小 单位 MB

          type: Number,

          default: 2

        },

        accept: {

          // 上传文件格式

          type: Array,

          default: function () {

            const accept = {

              //  image: [

              //  'jpg',

              //  'jpeg',

              //  'png',

              //  'gif',

              //  'bmp',

              //  'JPG',

              //  'JPEG',

              //  'PNG',

              //  'GIF',

              //  'BMP'

              // ],

              image: [

                'jpg',

                'jpeg',

                'png',

              ],

              // video : ['flv', 'mpg', 'mpeg', 'avi', 'wmv', 'mov', 'asf', 'rm', 'rmvb', 'mkv', 'm4v', 'mp4'],

              // 考虑视频播放兼容性

              video: ['mp4', 'mpg', 'mpeg'],

              file: ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx', 'pdf']

            }

            return accept[this.type]

          }

        },

        disabled: {

          // 禁用

          type: Boolean,

          default() {

            return false

          }

        },

        fileSavePath: String, // 要传给服务端的保存文件夹

        width: Number, // 图片宽度限制 需要配合高度使用,单一设置无效

        height: Number, // 图片高度限制 需要配合宽度使用,单一设置无效

        previewStyle: {

          // 展示样式

          type: Object,

          default() {

            return {

              width: '138px',

              height: '138px'

            }

          }

        },

          /* 提示文字内容 */

          placeholderText: {

              type: String,

              default() {

                  return '大小 < 2M;长宽=3:2'

              }

          }

      },

      created() {

        this.getValue()

      },

      data() {

        return {

          dialogImageUrl: '',

          dialogVisible: false,

          fileList: [],

          uploadUrl: '/api/blade-resource/oss/endpoint/put-file', // 上传地址

        }

      },

      computed: {

        multiple() {

          return this.max > 1

        },

        headers() {

          return {

            'Blade-Auth': 'bearer '+getToken()

          }

        }

      },

      watch: {

        value() {

          this.getValue()

        }

      },

      methods: {

        getUUID() {

          var s = []

          var hexDigits = '0123456789abcdef'

          for (var i = 0; i < 36; i++) {

            s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)

          }

          s[14] = '4'

          s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)

          s[8] = s[13] = s[18] = s[23] = '-'

          return s.join('')

        },

        getValue() {

          this.fileList = []

          if (!this.value) {

              if (this.$refs.upload) {

                  this.$refs.upload.clearFiles()

              }

              return

          }

          const arr = this.value.split(',')

          if (arr.length === 1) {

              this.fileList = [{ name: '图片-1', path: arr[0], status: 'success' }]

          } else if (arr.length > 1) {

              arr.forEach((el, i) => {

                  const obj = this.fileList.find((item) => {

                      return item.path === el

                  })

                  if (!obj && el) {

                      this.fileList.push({

                          name: '图片-' + (i + 1),

                          path: el,

                          uid: this.getUUID(),

                          status: 'success'

                      })

                  }

              })

          } else {

              return

          }

        },

        // 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。

        beforeUpload(file) {

          const maxMB = this.maxMB

          const accept = this.accept

          const format = file.type.split('/')[1]

          const isFormat = accept.indexOf(format) != -1

          const isSize = file.size < maxMB * 1024 * 1024

          // console.log(file, fileList)

          // console.log(format, isFormat, isSize)

          // 检验文件格式

          if (!isFormat) {

            this.$message.error(

              `文件格式错误,请上传${accept.join(',')}格式的文件`

            )

            return false

          }

          // 校验文件大小

          if (!isSize) {

            this.$message.error(`文件太大,请上传${maxMB}MB内的文件`)

            return false

          }

          // 校验图片尺寸

          return this.imgScale(file)

        },

        // 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。

        beforeRemove(file) {

          // console.log(file, fileList)

          if (file.status === 'success') {

            return this.$confirm(`确定移除 ${file.name}?`)

          }

        },

        // 文件超出个数限制时的钩子

        exceed(files, fileList) {

          console.log(files, fileList)

          this.$message.error(`超出数量限制,请选择${this.max}个文件`)

        },

        // 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用

        change(file, fileList) {

          console.log(file, fileList)

        },

        // 文件上传时的钩子

        progress(event, file) {

          // console.log(event, file, fileList, file.status)

          this.fileList = this.fileList.filter((el) => {

            return el.uid !== file.uid

          })

          this.fileList.push(file)

        },

        // 文件上传成功时的钩子

        success(response, file, fileList) {

          console.log(response, file, fileList, file.status)

          this.fileList.forEach((el) => {

            if (el.uid === file.uid) {

              el.path = file.response.data.link

            }

          })

          this.setValue()

        },

        // 文件上传失败时的钩子

        error(err, file, fileList) {

          console.log(err, file, fileList, file.status)

        },

        // 文件列表移除文件时的钩子

        remove(file, fileList) {

          console.log(file, fileList, file.status)

          this.fileList = this.fileList.filter((el) => {

            return el.uid !== file.uid

          })

        },

        // 校验图片尺寸

        imgScale(file) {

          // 没有限制宽高,直接上传

          const width = this.width

          const height = this.height

          if (!width || !height) return true

          return new Promise((resolve, reject) => {

            let reader = new FileReader()

            reader.file = file

            reader.readAsDataURL(file)

            reader.onload = (res) => {

              const result = res.target.result

              const img = new Image()

              img.onload = () => {

                if (

                  img.height !== parseFloat(height) ||

                  img.width !== parseFloat(width)

                ) {

                  this.$message.error(

                    `图片尺寸错误,请上传${this.width}px*${this.height}px的图片`

                  )

                  reject(false)

                } else {

                  resolve(true)

                }

              }

              img.src = result

            }

          })

        },

        // 查看已上传图片

        view(file) {

          const obj = this.fileList.find((el) => {

            return el.uid === file.uid

          })

          this.dialogImageUrl =  obj.path

          this.dialogVisible = true

        },

        // 删除已上传图片

        del(file) {

          this.$confirm(`确定移除?`, { type: 'warning' })

            .then(() => {

              this.fileList = this.fileList.filter((el) => {

                return el.uid !== file.uid

              })

              this.setValue()

              this.$refs.upload.clearFiles()

              this.$message({

                type: 'success',

                message: '删除成功!'

              })

            })

            .catch(() => {

              this.$message({

                type: 'info',

                message: '已取消删除'

              })

            })

          // this.fileList = this.fileList.filter((el) => {

          //  return el.uid !== file.uid

          // })

          // this.setValue()

          // this.$refs.upload.clearFiles()

          // this.$message({

          //  type: 'success',

          //  message: '删除成功!'

          // })

        },

        setValue() {

          const value = this.fileList

            .map((el) => {

              return el.path

            })

            .join(',')

          this.$emit('input', value) // 传入父组件接受处理数据

          this.$emit('checkValidate') // 需要效验就触发对应多validateField(‘name’)

        }

      }

    }

    </script>

    <style lang="scss" scoped>

    .element-ui-uploader-extension {

      padding: 0;

      margin: 0px;

      .placeholder {

        border: 1px dashed #dddddd;

        border-radius: 5px;

        display: flex;

        flex-direction: column;

        align-items: center;

        justify-content: center;

        font-size: 20px;

        color: #aaaaaa;

        margin-right: 10px;

        &:hover {

          border: 1px dashed #409eff;

        }

        .placeholder-text{

          display: inline-block;

          width: 100%;

          font-size: 10px;

          transform: scale(0.8);

          line-height: 14px;

          color: #C0C4D6;

        }

      }

      .file-list {

        display: flex;

        flex-wrap: wrap;

        align-items: center;

      }

      .file {

        position: relative;

        margin-right: 10px;

        border: 1px dashed #dddddd;

        border-radius: 4px;

        overflow: hidden;

        .img-cover{

          width: 100%;

          height: 100%;

          object-fit: cover;

        }

        .file-progress {

          position: absolute;

          left: 0;

          right: 0;

          bottom: 0;

          top: 0;

          margin: auto;

          width: 50px;

          height: 50px;

          .el-progress-bar__outer,

          .el-progress-bar__inner {

            border-radius: 0;

          }

        }

        .control-mask {

          display:none;

          position: absolute;

          left: 0;

          right: 0;

          bottom: 0;

          top: 0;

          width: 100%;

          height: 100%;

          background: rgba(0,0,0, 0.5);

          align-items: center;

          justify-content: space-evenly;

          i {

            font-size: 20px;

            color: #fff;

            // background: rgba(255, 255, 255, 0.8);

            width: 30px;

            height: 30px;

            border-radius: 50%;

            display: flex;

            align-items: center;

            justify-content: center;

            cursor: pointer;

            &:hover {

              // background: rgba(255, 255, 255, 1);

              color: #fff;

            }

          }

        }

        &:hover {

          .control-mask {

            display: flex;

          }

        }

      }

    }

    </style>

    父组件引用

    import uploader from '@/components/form/uploader.vue'


    好了这样一个公共组件就封装完了,工作中用起来就很方便,统一样式管理.

    相关文章

      网友评论

          本文标题:二次封装ele-ui上传公共组件(支持多选,放大,删除)

          本文链接:https://www.haomeiwen.com/subject/pqrndltx.html