项目使用vue+element-ui,实现了表单多图上传图片,上传视频,以及表单图片回显,视频回显,表格渲染图片等功能

效果图

上传后

图片可回显,视频可播放,,这时候全部缓存在页面,并没有提交到后端服务器,只要到用户提交的那一步才确定上传,减低不必要的服务器开支

图片上传

前端缓存base64方便回显,以及后台上传,视频上传则使用file类型去上传(base64对视频编码会导致请求参数过长)

<!--
	描述:图片上传,  基于 element-ui 组件
-->
<template>
  <div>
    <div class="upload">
      <div
        class="img_mode"
        v-for="(item, index) in path_list"
        :key="index"
        :style="'height:' + height + ';width:' + width"
        @mouseenter.stop="over(index)"
        @mouseleave="out"
      >
        <img :src="https://www.atool.online/article/item.path" />
        <transition name="el-fade-in-linear">
          <div v-show="curIndex == index" class="transition-box">
            <div class="mask">
              <i class="el-icon-delete" @click="remove(index)"></i>
              <i
                class="el-icon-zoom-in"
                @click="enlarge(index)"
                v-if="bigBox"
              ></i>
            </div>
          </div>
        </transition>
      </div>
      <div
        class="select_mode"
        :style="'height:' + height + ';width:' + width"
        v-if="isLimit"
        v-loading="load"
      >
        <input
          type="file"
          @change="change($event)"
          ref="file"
          :multiple="isMultiple"
        />
        <img
          :src="https://www.atool.online/article/selectImgPath || selectImg"
          :width="selectImgWidth || '50%'"
        />
      </div>
    </div>
    <el-dialog :visible.sync="isShow" center>
      <div class="big_img_mode">
        <img :src="https://www.atool.online/article/bigImg" />
      </div>
    </el-dialog>
  </div>
</template>
<script>
//这里是图片上传时候的图标,可以自行替换
import selectImg from "../../assets/img/selectedImg.png";
export default {
  props: {
    height: String,
    width: String, // 组件预览图片大小
    selectImgPath: String, // 未选择图片时显示的图片
    selectImgWidth: String,
    bigBox: Boolean, // 是否显示图片放大按钮
    /* 多图上传 */
    isMultiple: Boolean, // 是否可多选图片
    limit_size: Number, // 限制上传数量
    quality: Number, // 图片压缩率
    limit: Number, // 图片超过 (limit * 1024)kb 将进行压缩
    isCompress: Boolean, // 是否开启图片压缩
    /* 单图上传 */
    isChangeUpload: false, // 是否选择文件后触发上传
    action: "", // 单图上传路径,
    param: "", // 上传的参数名
    data: Object, // 单图上传附带的参数
    success: Function, // 单图上传成功后的回调函数
  },
  data() {
    return {
      selectImg,
      path_list: [],
      curIndex: -1,
      bigImg: "",
      isShow: false,
      isLimit: true,
      load: false,
    };
  },
  watch: {
    path_list() {
      if (this.path_list.length >= this.limit_size) {
        this.isLimit = false;
      } else {
        this.isLimit = true;
      }
      this.load = false;
      this.curIndex = -1;
    },
  },
  mounted() {},
  methods: {
    // 鼠标移入
    over(index) {
      this.curIndex = index;
    },
    // 鼠标移出
    out() {
      this.curIndex = -1;
    },
    // 选择图片
    change() {
      this.load = true;
      var That = this;
      let fileList = this.$refs.file.files;
      if (!fileList.length) {
        this.load = false;
        return;
      }
      for (var i = 0; i < fileList.length; i++) {
        var file_temp = fileList[i];
        let name = file_temp.name.toLowerCase();
        if (!/\.(jpg|jpeg|image|img|png|bmp|)$/.test(name)) {
          this.$message.warning("请上传图片");
          this.clean();
        }
        let reader = new FileReader(); //html5读文件
        reader.fileName = file_temp.name;
        reader.readAsDataURL(file_temp);
        reader.onload = (data) => {
          //读取完毕后调用接口
          // 图片压缩
          this.canvasDataURL(
            data.currentTarget.result,
            { fileSize: data.total, quality: this.quality },
            (baseCode) => {
              if (this.isChangeUpload) {
                this.changeUpload(baseCode, reader.fileName);
              } else {
                this.path_list.push({
                  path: baseCode,
                  fileName: reader.fileName,
                });
              }
            }
          );
        };
      }
      this.$emit("change", this.path_list);
    },
    // 移除图片
    remove(index) {
      this.path_list.splice(index, 1);
    },
    //清空图片
    clean() {
      this.path_list = [];
    },
    //后台添加图片
    addPathList(urls) {
      console.log(urls);
      let arr = urls.split(",");
      if (arr.length == 1) {
        let obj = { path: this.$common.getUrl() + arr[0] };
        this.path_list.splice(0, 1, obj);
      } else {
        arr.forEach((item, index) => {
          let obj2 = { path: this.$common.getUrl() + item };
          this.path_list.splice(index, 1, obj2);
        });
      }
    },
    //后台添加图片
    addUrlPathList(urls) {
      console.log(urls);
      let arr = urls.split(";");
      console.log("数组" + arr);
      if (arr.length == 1) {
        let obj = { path: this.$common.getUrl() + arr[0] };
        this.path_list.splice(0, 1, obj);
      } else {
        arr.forEach((item, index) => {
          let obj2 = { path: this.$common.getUrl() + item };
          this.path_list.splice(index, 1, obj2);
        });
      }
    },
    //重新图片
    clearImgs() {
      this.path_list = [];
    },
    // 放大图片
    enlarge(index) {
      this.isShow = true;
      this.bigImg = this.path_list[index].path;
    },
    // 单图上传
    changeUpload(baseCode, fileName) {
      let formData = new FormData();
      formData.append(this.param, baseCode);
      formData.append("fileName", fileName);
      for (var item in this.data) {
        formData.append(item, this.data[item]);
      }
      this.$axios
        .post(this.action, formData)
        .then((response) => {
          if (response.data.message == "succ") {
            this.success(); // 上传成功后的回调函数
          }
          this.load = false;
        })
        .catch((e) => {
          console.log(e);
          this.load = false;
        });
    },
    /**
     * @uploadPath 上传的路径
     * @path_list  base64 集合, 为空则调用 this.path_list
     * @callback 上传成功后的回调函数, 返回上传成功后的路径
     */
    upload(uploadPath, path_list, callback) {
      var formData = new FormData();
      if (!path_list) {
        this.path_list.forEach((item) => {
          formData.append(
            "files",
            this.convertBase64UrlToBlob(item.path),
            item.fileName
          );
        });
      } else {
        path_list.forEach((item) => {
          formData.append(
            "files",
            this.convertBase64UrlToBlob(item.path),
            item.fileName
          );
        });
      }
      let headers = { headers: { "Content-Type": "multipart/form-data" } };
      this.$axios
        .post(uploadPath, formData, headers)
        .then((response) => {
          if (response.data.message == "succ") {
            // 回调函数返回上传成功后的路径
            callback("succ", response.data.result);
          } else {
            this.$message.error("文件上传失败");
            callback("error");
          }
        })
        .catch((error) => {
          if (error == "timeout") {
            this.$message.error("文件上传超时");
          }
          this.$message.error("文件上传异常");
          console.log(error);
          callback("error");
        });
    },
    getName() {
      return this.path_list.fileName;
    },
    // 获取base64 集合
    getPaths() {
      let paths = [];
      if (this.path_list.length == 1) {
        return this.path_list[0].path;
      } else if (this.path_list.length == 0) {
        return "";
      } else {
        for (var i = 0; i < this.path_list.length; i++) {
          paths.push(this.path_list[i].path);
          // if(i == 0){
          // 	paths = this.path_list[i].path
          // }else{
          // 	arr.push(this.path_list[i].path)
          // }
        }
      }
      return paths;
    },
    photoCompress() {},
    // 图片压缩
    canvasDataURL(path, obj, callback) {
      var suffix = path.match(/\/(\S*);/)[1];
      var img = new Image();
      img.src = path;
      img.onload = function () {
        var that = this;
        // 默认按比例压缩
        var w = that.width,
          h = that.height,
          scale = w / h;
        w = obj.width || w;
        h = obj.height || w / scale;
        var quality = 0.7; // 默认图片质量为0.7
        this.limit = this.limit ? this.limit : 1; // 默认为超过1M 进行压缩
        //生成canvas
        var canvas = document.createElement("canvas");
        var ctx = canvas.getContext("2d");
        // 创建属性节点
        var anw = document.createAttribute("width");
        anw.nodeValue = w;
        var anh = document.createAttribute("height");
        anh.nodeValue = h;
        canvas.setAttributeNode(anw);
        canvas.setAttributeNode(anh);
        ctx.drawImage(that, 0, 0, w, h);
        // 图像质量
        if (obj.quality && obj.quality <= 1 && obj.quality > 0)
          quality = obj.quality;
        var base64 = "";
        if (this.isCompress) {
          // quality值越小,所绘制出的图像越模糊
          base64 =
            obj.fileSize > this.limit * 1024
              ? canvas.toDataURL("image" + suffix, quality)
              : canvas.toDataURL("image" + suffix);
        } else {
          base64 = canvas.toDataURL("image/" + suffix, quality);
        }
        // 回调函数返回base64的值
        callback(base64);
      };
    },
    // base64 转 blob 对象
    convertBase64UrlToBlob(urlData) {
      //去掉url的头,并转换为byte
      var bytes = window.atob(urlData.split(",")[1]);
      //处理异常,将ascii码小于0的转换为大于0
      var ab = new ArrayBuffer(bytes.length);
      var ia = new Uint8Array(ab);
      for (var i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
      }
      return new Blob([ab], { type: "image/png" });
    },
  },
};
</script>
<style scoped="upload">
.upload {
  width: 100%;
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  padding: 10px 4px;
  box-sizing: border-box;
}
.select_mode {
  width: 120px;
  height: 120px;
  display: flex;
  justify-content: center;
  align-items: center;
  /* border: 1px dashed #cfcfcf; */
  border-radius: 10px;
  position: relative;
  box-sizing: border-box;
  /* background-color: #fcfbfe; */
}
.select_mode:hover {
  /* border: 1px dashed #00A2E9; */
}
.select_mode img {
  width: 100%;
  position: absolute;
  z-index: 1;
  cursor: pointer;
}
.select_mode input[type="file"] {
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  z-index: 2;
}
.img_mode {
  margin-right: 10px;
  margin-bottom: 10px;
  box-shadow: 0px 1px 5px #cccccc;
  position: relative;
}
.mask {
  background: rgb(0, 0, 0, 0.5);
  height: 100%;
  width: 100%;
  position: absolute;
  top: 0px;
  left: 0px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.img_mode,
.img_mode img {
  height: 120px;
  max-width: 200px;
}
.mask i {
  font-family: element-icons !important;
  color: #ffffff;
  font-size: 25px;
  margin: 0px 8px;
  cursor: pointer;
}
.big_img_mode {
  text-align: center;
  width: 100%;
  height: 100%;
  max-height: 400px;
}
.big_img_mode img {
  max-width: 400px;
  max-height: 400px;
}
</style>

使用方法:

引入上面的模块,然后在 components里面注册,即可用标签使用

import Upload from "../../../components/utils/Upload.vue";
components: { editor: editor, Upload },
<el-form-item label="轮播图:" prop="img">
//这里面的参数在上面的模块里面去看,ref就是你绑定的对象
    <upload
       ref="addBanner"
       :limit_size="4"
       :isCompress="true"
       :bigBox="true"
     ></upload>
</el-form-item>

上传之前获取base64图片编码,存到表单中,然后直接提交给后台

this.addForm.img = this.$refs.addBanner.getPaths();

注意:这里上传后会是一段base64字符串,如果是多图,会是一段字符串数组,后端可以直接用jsonArray接收

视频上传

//表单
 <el-form-item label="视频:" prop="video">        
   <video class="video" controls v-if="videoShow" :src="https://www.atool.online/article/videoShow" />
   <input
     ref="videoFile"
     @change="fileChange($event)"
     type="file"
     id="video_file"
     accept="video/*"
   />
</el-form-item>
//方法
fileChange(e) {
      var files = e.target.files || e.dataTransfer.files;
      if (!files.length) return;
      let name = files[0].name.toLowerCase();
      if (
        !/\.(avi|wmv|mpeg|mp4|mov|mkv|flv|f4v|m4v|rmvb|rm|3gp|dat|ts|mts|vob)$/.test(
          name
        )
      ) {
        this.$message.warning("请上传视频");
        return;
      }
      if (files[0].size > 1024 * 1024 * 20) {
        this.$message.warning("视频大小不能大于20M");
        return;
      }
      //这里是file文件
      this.addForm.video = files[0];      
      var reader = new FileReader();
      reader.readAsDataURL(files[0]);
      reader.onload = () => {
      //这里是一段base64,用于视频回显用
        this.videoShow = reader.result;
      };  
},

表格渲染相关

表格内多图渲染

这段代码的意思是渲染当前行img字段的列表,遍历生成img图片标签

因为img字段返回的是一个数组,不能直接渲染,所以加上一个JSON.parse解析一下

另外加了一个判断最多渲染不超过三张,防止界面过长超出

<el-table-column prop="name" label="轮播图" align="center"  width="300px">
          <template slot-scope="scope">
            <el-image
              v-for="(item,index) in JSON.parse(scope.row.img)"
              :src="https://www.atool.online/article/item" 
              v-if="index<3"
              style="width: 30%;height: 100%;margin-right: 5px"
            ></el-image>
          </template>
</el-table-column>

以上为个人经验,希望能给大家一个参考,也希望大家多多支持阿兔在线工具。 

点赞(0)

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部