基于qiniu-js封装图片上传,实现图片库,减少资源浪费

在开发中,有这样一个需求,点击上传logo,点击上传封面,点击上传头像等等,每次都上传很浪费存储资源,故要实现一个图片库的功能,点击先在图片库选择,如果图片库没有在执行上传。废话少说:上图
image.png

image.png

image.png

image.png
1. 图片库页面代码
<template>
  <div class="fileDialog">
    <el-dialog :append-to-body='appendToBody' title="选择图片" :visible.sync="imgFileDialogFlag.show" width="60%" destroy-on-close @open="openDialog"
               @close="closeDialog" :close-on-click-modal="false" :close-on-press-escape="false" :modal-append-to-body="false">
      <!-- 上传图片按钮区域 -->
      <div class="header_top">
        <span>
          <el-button size="mini" type="success" @click="vExampleAdd">上传图片</el-button>
          <form ref="vExample">
            <input type="file" style="display:none;" :accept="accept" ref="vExampleFile" @change="vExampleUpload" />
          </form>
        </span>
        <span>
          <el-input size="mini" placeholder="请输入图片名称" v-model="searchKey" @change="search" clearable>
            <i slot="prefix" class="el-input__icon el-icon-search"></i>
          </el-input>
        </span>
      </div>
      <!-- 表格 -->
      <div class="table">
        <el-table element-loading-text="正在为您拼命加载中..." :data="tableData" ref="multipleTable" style="width: 100%" @row-click="clickRow"
                  v-loading='loading' :header-cell-style="{background:'#eef1f6',color:'#38a4bd'}">
          <el-table-column label="" width="65">
            <template slot-scope="scope">
              <el-radio class="radio" v-model="templateSelection" :label="scope.$index"><br></el-radio>
            </template>
          </el-table-column>
          <!-- 图片 -->
          <el-table-column prop="videoUrl" label="图片" align="left">
            <template slot-scope="scope">
              <div class="imgBox">
                <div class="l">
                  <!-- <img :src="scope.row.videoUrl" alt="" width="100%"> -->
                  <el-image :src="scope.row.videoUrl" :preview-src-list="[scope.row.videoUrl]" fit="cover" lazy class="el-avatar">
                    <div slot="error" class="image-slot">
                      <i class="el-icon-picture-outline"></i>
                    </div>
                  </el-image>
                </div>
                <div class="r">
                  <span>{{scope.row.videoName}}</span>
                  <span>{{scope.row.width}}*{{scope.row.height}}</span>
                </div>
              </div>
            </template>
          </el-table-column>
          <!-- 大小 -->
          <el-table-column prop="material_size" label="大小" align="center">
            <template slot-scope="scope">
              <span>{{scope.row.videoSize}}KB</span>
            </template>
          </el-table-column>
          <!-- 上传时间 -->
          <el-table-column prop="createTime" show-overflow-tooltip label="上传时间" align="right">
          </el-table-column>
        </el-table>
      </div>
      <!-- 分页 -->
      <Pagination :page="page" :total="total" :page_size="page_size" :background="true" @handleCurrentChange="handleCurrentChange"
                  @handleSizeChange="handleSizeChange" />
      <span slot="footer" class="dialog-footer">
        <el-button size="mini" @click="imgFileDialogFlag.show = false">取 消</el-button>
        <el-button size="mini" type="primary" @click="selectImage">确 定</el-button>
      </span>
      <el-dialog width="30%" title="上传进度" :visible.sync="innerVisible" append-to-body :show-close='false'>
        <div class="loading">
          <el-progress :text-inside="true" :stroke-width="20" :percentage="uploadPercent" status="exception"></el-progress>
        </div>

      </el-dialog>
    </el-dialog>
  </div>
</template>

<script>
import * as qiniu from 'qiniu-js'
import { getFileAuth, getFileList, addFile } from "@/api/common";
export default {
  props: {
    imgFileDialogFlag: Object,
    appendToBody: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      uploadPercent: 0,//上传进度
      innerVisible: false,
      tableHeight: document.documentElement.clientHeight - 300 || document.body.clientHeight - 300,
      searchKey: "",
      baseUrl: "",
      accept: 'image/*',
      loading: false,
      templateSelection: "",//选中的图片数据索引
      templateRadioUrl: "",//选中的图片url
      tableData: [],
      upToken: "",//上传token
      page: 1,
      page_size: 5,
      total: 0,
      imgSize: "",//图片大小
      imgWidth: "",//图片宽度
      imgHeight: "",//图片高度
      imgName: "",//图片名称
      imgType: "",//图片类型
    }
  },
  methods: {
    // 实现点击整行选中复选框
    clickRow(row) {
      this.templateSelection = this.tableData.indexOf(row);
      this.templateRadioUrl = row.videoUrl;
    },
    // 上传按钮触发
    vExampleAdd() {
      this.$refs.vExampleFile.click()
    },
    // input change 事件 上传图片
    vExampleUpload() {
      let _this = this
      let file = _this.$refs.vExampleFile.files[0]
      _this.imgName = file.name
      _this.imgSize = (file.size / 1024).toFixed(2)
      _this.imgType = file.type
      let config = {
        useCdnDomain: true,
        region: qiniu.region.z0
      };
      let putExtra = {
        fname: file,
        params: {},
        mimeType: null
      };
      let observe = {
        next(res) {
          // // console.log('已上传大小,单位为字节:' + res.total.loaded)
          // // console.log('本次上传的总量控制信息,单位为字节:' + res.total.size)
          // // console.log('当前上传进度,范围:0~100:' + res.total.percent);
          // _this.$emit("uploadPercent", res.total.percent)
          _this.uploadPercent = res.total.percent
          _this.innerVisible = true
        },
        error(err) {
          // console.log(err.code)
          console.log(err.message)
          // console.log(err.isRequestError)
          // console.log(err.reqId)
          // _this.$emit('uploaderr')
          _this.$message.error('上传失败!请勿重复上传')
          _this.innerVisible = false
        },
        complete(res) {
          //完成后的操作
          //上传成功以后会返回key 和 hash  key就是文件名了!
          let imageUrl = (_this.baseUrl + '/' + res.key)
          let img = new Image()
          img.src = imageUrl
          img.onload = function() {
            _this.imgWidth = img.width
            _this.imgHeight = img.height
            addFile({ width: _this.imgWidth, height: _this.imgHeight, categoryId: 3, videoName: _this.imgName, videoUrl: imageUrl, videoSize: _this.imgSize, mimeTypeName: _this.imgType }).then(res => {
              console.log(res);
              _this.innerVisible = false
              _this.$message.success('上传成功!')
              _this.templateSelection = ''
              _this.templateRadioUrl = ''
              _this.getFileList()
            })
          }
        }
      };
      //开始上传  token 需要找后端获取(单独的方法)
      getFileAuth().then(res => {
        this.upToken = res.data.auth
        this.baseUrl = res.data.bucket
        let key = res.data.uuid; // 文件资源名
        let headers = qiniu.getHeadersForMkFile(this.upToken)
        let observable = qiniu.upload(file, key, this.upToken, headers, putExtra, config)
        let subscription = observable.subscribe(observe)
      })
    },
    // 确认选择按钮触发
    selectImage() {
      this.$emit('selectImage', this.templateRadioUrl)
      this.templateSelection = ''
      this.templateRadioUrl = ""
    },
    // 获取图片列表
    getFileList() {
      this.loading = true
      getFileList({ pageSize: this.page_size, pageNum: this.page, mimeType: 3, videoName: this.searchKey }).then(res => {
        this.tableData = res.data.records
        this.total = res.data.total
        this.loading = false
      })
    },
    // 分页所需
    handleCurrentChange(val) {
      this.page = val;
      this.getFileList(this.searchKey);
      this.templateSelection = ''
      this.templateRadioUrl = ""
    },
    // 分页所需
    handleSizeChange(val) {
      this.page_size = val;
      // this.page = 1;
      this.getFileList(this.searchKey);
      this.templateSelection = ''
      this.templateRadioUrl = ""
    },
    // 打开弹窗触发
    openDialog() {
      this.getFileList()
    },
    closeDialog() { },
    // 搜索触发
    search() {
      this.getFileList()
    }
  }
}
</script>

<style lang="scss" scoped>
.header_top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 0;
}
.table {
  .imgBox {
    display: flex;
    .l {
      img {
        width: 68px;
        height: 48px;
        object-fit: scale-down;
        background: #fafafa;
      }
    }
    .r {
      display: flex;
      flex-direction: column;
      margin-left: 10px;
      overflow: hidden;
      justify-content: space-between;
      span {
        text-align: left;
        &:first-child {
          font-weight: bold;
          display: block;
          text-overflow: ellipsis;
          white-space: nowrap;
          overflow: hidden;
          color: #333;
        }
      }
    }
  }
}
::v-deep .el-dialog__header {
  background: #fafbfc;
  border-radius: 2px 2px 0 0;
  border-bottom: 1px solid #f2f2f2;
}
::v-deep .el-dialog__body {
  padding: 0px 20px;
}
.loading {
  padding-bottom: 20px;
}
</style>

2. 组件内用到了封装的分页组件,代码如下

<template>
  <div class="pagination">
    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :page-sizes="[1,5, 10, 20, 30, 40, 50]" :layout="layout"
                   :current-page.sync="page" :page-size="page_size" :total="total" :background="background" small>
    </el-pagination>
  </div>
</template>

<script>
export default {
  name: "pagination",
  data() {
    return {
      page: 1
    }
  },
  props: {
    // 避免直接改变prop属性
    // 'currentPage': {
    //   required: false,
    //   default: 1
    // },
    'layout': {
      required: false,
      default: 'total, prev, pager, next, sizes'
    },
    'total': {
      required: false,
      default: 0
    },
    'page_size': {
      required: false,
      default: 10
    },
    'background': {
      required: false,
      type: Boolean,
      default: false
    }
  },
  watch: {
    currentPage(val) {
      // 改变这个值并不会触发 handleCurrentChange
      if (typeof val === "number") {
        // console.log('prop currentPage!!!');
        this.currentPage = val;
      }
    },
  },
  methods: {
    // 当前页变化
    handleCurrentChange(val) {
      this.$emit("handleCurrentChange", val);
    },
    // size变化
    handleSizeChange(val) {
      this.currentPage = 1;
      this.$emit('handleSizeChange', val);
    },
  }
}
</script>

<style lang="scss" scoped>
.pagination {
  margin: 10px 0 0 0;
  text-align: right;
}
::v-deep .el-pagination .el-select .el-input {
  margin-top: -3px;
}
::v-deep .el-pagination.is-background .el-pager li {
  border-radius: 50%;
}
::v-deep .el-pagination.is-background .el-pager li:not(.disabled).active {
  background-color: #38a4bd;
}
::v-deep .el-pagination.is-background.el-pagination--small .btn-prev {
  border-radius: 50%;
}
::v-deep .el-pagination.is-background.el-pagination--small .btn-next {
  border-radius: 50%;
}
</style>

3. 写个容器调用组件
 <el-form-item label="LOGO">
            <div class="flex">
              <el-image :src="formDataHandle.orgLogo" :preview-src-list="[formDataHandle.orgLogo]" fit="cover" lazy class="el-avatar">
                <div slot="error" class="image-slot" @click="showImgFileDialog">
                  <i class="el-icon-picture-outline"></i>
                </div>
              </el-image>
              <div class="dot" v-if="formDataHandle.orgLogo != ''">
                <el-button class="closeicon" type="danger" circle icon="el-icon-delete" @click="delLogo"></el-button>
              </div>
            </div>

          </el-form-item>
<ImgFileDialog :appendToBody='true' @selectImage='selectImage' :imgFileDialogFlag='imgFileDialogFlag'></ImgFileDialog>

data(){
  return{
    imgFileDialogFlag: {
        show: false
      },
  }
}
methods(){
//打开图片库
    showImgFileDialog() {
      this.imgFileDialogFlag = {
        show: true
      }
    },
  selectImage(url) {
      this.formDataHandle.orgLogo = url //赋值
      this.imgFileDialogFlag = {
        show: false
      }
    },
//删除图片重新选择
    delLogo() {
      this.formDataHandle.orgLogo = ''
    }
}

推荐阅读更多精彩内容