el-upload组件上传单/多张图片

文章讲述了在Vue项目中使用Element-UI的上传组件遇到的问题,包括图片过大导致的上传失败和因缺少特定请求头(如token)而无法成功上传。解决方案包括在upload组件中设置headers属性以包含必要的认证信息,并调整:action属性以避免重复上传。此外,还提供了多张和单张图片上传的配置示例。

补充1:图片太大时候上传失败,报错,直接没走到上传接口,要添加headers属性!

 项目在上传文件时,后端请求没有返回结果,但是在谷歌浏览器控制台会报错net::ERR_CONNECTION_RESET

重新设置upload标签的请求头是最重要的,因为element-ui组件库中的upload这个组件上传图片并没有使用我们自己在项目中定义的Axios请求,而是使用了组件库中自己封装的。所以需要在upload中重新设置符合项目需求的请求头。

例如:在vue项目项目开发中,登录注册一个应用,进行后续相关网页访问都是需要携带token,才能有权限继续访问的。所以在upload这个组建中也需要携带这个特定的请求头。upload中的headers就是用来重新设置请求头的。

:headers="headerOdj"   // 重要!!

          <el-upload
            :headers="headerOdj"   // 重要!!
            ref="upload"
            :action="adminUrl"
            list-type="picture-card"
            :before-upload="beforeAvatarUpload"
            :on-success="handlePictureCardPreview"
            :on-remove="handleRemove"
            :file-list="imgList"
          >
            <i slot="default" class="el-icon-plus"></i>
          </el-upload>
 // 放data()中
 headerOdj: {
                Authorization: window.sessionStorage.getItem('token')
            },

补充2:图片上传时候一下会走2次图片上传接口且上传2张一样的图片,因为 属性 :action="adminUrl" 定义有问题,不能使用注释的方式。

      data(){
          return{
            // adminUrl: this.GLOBAL.imgUrl + "/system/entranceguard/uploadImg", //后台请求地址,
              adminUrl: this.GLOBAL.imgUrl, //后台请求地址,
        }
            
        }

修改前:2次请求都是成功的,会上传2张一样图片

修改后:就第二次请求走通,上传一张图片

-----------------------------------------------------------------------------------------------------------------

一、多张图片

注意:style需取消el-upload组件中图片动画样式,不然显示会异常

action 图片上传后台请求地址(必填)

list-type="picture-card" 照片墙显示

before-upload 图片上传前

on-success 图片上传成功时候

on-remove 图片删除时候

file-list 组件默认显示图片的数组,有具体格式[{url:"xxxxx图片地址" }]

:limit="9" 限制上传的图片张数为9

html和数据定义部分:

        <el-form-item>
          <!-- -----------图片上传----------------- -->
          <el-upload
            ref="upload"
            :action="adminUrl"
            list-type="picture-card"
            :before-upload="beforeAvatarUpload"
            :on-success="handlePictureCardPreview"
            :on-remove="handleRemove"
            :file-list="imgList"
          >
            <i slot="default" class="el-icon-plus"></i>
            <div class="el-upload__tip" slot="tip" style="font-weight: bold">
              建议尺寸大小为192*128,大小不超过2M
            </div>
          </el-upload>
        </el-form-item>

import {  uploadImgTbt } from "@/api/system/tbt"; // 上传图片接口

 data() {
    return {
      // 组件el-upload组件 file-list属性 默认显示的图片组数,格式如下
      imgList: [
        // {
        //   url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
        // },
        // {
        //   url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
        // },
      ],
      adminUrl: this.GLOBAL.imgUrl , //图片上传后台地址,
      form:{
        contextImg:[] // 实际表单要提交的 需通过图片接口转换过 图片列表信息
      }
    };
  },

函数方法部分: 

 methods: {
    // 上传图片文件前
    beforeAvatarUpload(file) {
      const isJPG = file.type === "image/jpeg" || "image/png";
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isJPG) {
        this.$message.error("上传头像图片只能是 JPG/PNG 格式!");
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!");
      }
      return isJPG && isLt2M;
    },
    // 图片上传--移除
    handleRemove(file, fileList) {
      // console.log("##file", file.url);
      // 1.把删除的图片url地址 去掉http://192.168.xxx.xx:8080 头部
      // 2.去实际上传的 form.contextImg 去掉这条数据
      const str = file.url;
      let delStr = str.replace(this.GLOBAL.imgUrl, "");
      // console.log("delStr=>", delStr);
      for (let i = 0; i < this.form.contextImg.length; i++) {
        if (this.form.contextImg[i] == delStr) {
          this.form.contextImg.splice(i, 1);
        }
      }
      // console.log("删除时候form.contextImg", this.form.contextImg);
    },
    //-图片上传成功时候数组
    handlePictureCardPreview(response, file, fileList) {
      if (file != null) {
        // 接口上传 需要formData 文件类型
        let formData = new FormData();
        // "uploadImage" 这个字段需要看后端需提供详细
        formData.append("uploadImage", file.raw);
        uploadImgTbt(formData).then((response) => {
          // 如果form没有这个 contextImg属性 那么就加一个
          if (!this.form.contextImg) {
            this.form.contextImg = [];
          }
          // console.log("###form.contextImg", this.form.contextImg);
          // 把上传返回的数据拼接到图像数组中
          this.form.contextImg.push(response.msg);
          // console.log("###form.contextImg", this.form.contextImg);
        });
      }
    },
    
    // 表单重置
    reset() {
      this.imgList = [];
      this.form = {
        id: null,
        title: null,
        context: null,
        initTime: null,
      };
      this.resetForm("form");
    },

    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.title = "添加xxx";
    },

    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const id = row.id || this.ids;
      getTbt(id).then((response) => {
        this.form = response.data;
        this.title = "修改xxx";
        // console.log("###form", this.form);
        // 1.把原来form.contextImg JSON数组 转换回数组形式 (提交时候再转回Json格式)
        // 2.把el-upload 中的file-list要回显的图片拼接完形式用 imgList存储
        var jsonArray2 = eval(this.form.contextImg);
        this.form.contextImg = jsonArray2;
        this.imgList = [];
        // console.log("form.contextImg###", this.form.contextImg);
        for (let i = 0; i < this.form.contextImg.length; i++) {
          const obj = {};
          obj.url = this.GLOBAL.imgUrl + this.form.contextImg[i];
          this.imgList.push(obj);
        }
      });
    },  
}

axios请求文件:

// 上传图片信息 要用file类型传
export function uploadImgTbt(file) {
  return request({
    url: '/xxx/xxx/uploadImg',
    method: 'post',
    data: file
  })
}

定义全局 this.GLOBAL.imgUrl 路径:

1.创建Global.vue的文件

<script>

    const imgUrl = 'http://192.168.xxx.xx:8081'; //请求地址

    export default {

        imgUrl,

    }

</script>

2.main.js文件中引入

// 全局定义服务器地址链接
import global from '@/views/xxx/xxx/Global'   // 相对路径
Vue.prototype.GLOBAL = global;

 style样式文件:

<style scoped>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
/*  去掉动画,不然会影响图片显示 */
::v-deep .el-upload-list__item {
  transition: none !important;
  -webkit-transition: nonne !important;
}
::v-deep .el-upload-list__item-name {
  transition: none !important;
  -webkit-transition: nonne !important;
}
</style>

二、单张图片

 添加单张人脸信息,点击图片替换人脸,没有删除功能

list-type="picture-card" 照片墙格式

:show-file-list="false" 不允许多张图片显示,只一张

img标签 :src="imageUrl" 默认显示图片 因为是单图,字符串就行,v-if控制是新增还是修改

html和数据定义部分:

<el-form-item label="人脸信息" prop="faceImg">
          <el-form-item>
            <!-- -----------图片上传-  单张图片上传---------------- -->
            <el-upload
              class="avatar-uploader"
              list-type="picture-card"
              :action="adminUrl"
              :show-file-list="false"
              :before-upload="beforeAvatarUpload"
              :on-success="handlePictureCardPreview"
              :on-remove="handleRemove"
            >
              <img
                v-if="imageUrl"
                :src="imageUrl"
                title="点击重新上传图片"
                class="avatar"
              />
              <i v-else class="el-icon-plus avatar-uploader-icon"></i>
              <!-- <div
                v-if="imageUrl"
                class="el-upload__tip"
                slot="tip"
                style="font-weight: bold"
              >
                点击重新上传图片
              </div> -->
            </el-upload>
          </el-form-item>
        </el-form-item>


import { uploadImgEntranceguard } from "@/api/system/entranceguard"; // 图片上传接口


 data() {
    return {
      imageUrl: "", //  组件el-upload组件单张图片显示 imageUrl属性 默认显示的图片
      adminUrl: this.GLOBAL.imgUrl, //后台请求地址,
      // 表单参数
      form: {
        faceImg:"", // 实际提交表单要的转换过的图片信息
      },
    };
  },

函数方法部分:

  methods: {
    // 上传图片文件前
    beforeAvatarUpload(file) {
      const isJPG = file.type === "image/jpeg" || "image/png";
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isJPG) {
        this.$message.error("上传头像图片只能是 JPG/PNG 格式!");
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!");
      }
      return isJPG && isLt2M;
    },
    // 图片上传--移除
    handleRemove(file, fileList) {
      // console.log("##file", file);
      // 一张图的话直接清空form.faceImg的数据
      this.form.faceImg = "";
      // console.log("删除时候form.faceImg", this.form.faceImg);
    },
    //-图片上传成功时候数组
    handlePictureCardPreview(response, file, fileList) {
      if (file != null) {
        // 接口上传 需要formData 文件类型
        let formData = new FormData();
        // "uploadImage" 这个字段需要看后端需提供详细
        formData.append("uploadImage", file.raw);
        uploadImgEntranceguard(formData).then((response) => {
          // console.log("####", response);
          // 如果form没有这个 faceImg属性 那么就加一个
          if (!this.form.faceImg) {
            this.form.faceImg = "";
          }
          // console.log("###form.faceImg", this.form.faceImg);
          // 把上传返回的数据拼接到图像数组中
          this.form.faceImg = response.msg;
          // 把上传组件默认显示图片赋值
          this.imageUrl = this.GLOBAL.imgUrl + response.msg;
          // console.log("###form.faceImg", this.form.faceImg);
        });
      }
    },

    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加xxx";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const id = row.id || this.ids;
      getEntranceguard(id).then((response) => {
        this.form = response.data;
        // console.log("form###", this.form);
        if (this.form.faceImg) {
          // 默认显示图片
          this.imageUrl = this.GLOBAL.imgUrl + this.form.faceImg;
          // console.log(this.imageUrl);
        }
        this.open = true;
        this.title = "修改xxx";
      });
    },
    
  }

axios请求文件:

// 上传图片信息 文件类型需file类型
export function uploadImgEntranceguard(file) {
  return request({
    url: '/xxx/xxx/uploadImg',
    method: 'post',
    data: file
  })
}

css样式文件:

<style scoped>
::v-deep .el-upload--picture-card {
  width: 176px;
  height: 178px;
}
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
/* 单人 */
.avatar {
  width: 170px;
  height: 170px;
  display: block;
  margin-left: 2px;
  margin-top: 2px;
}
/*  去掉动画  */
::v-deep .el-upload-list__item {
  transition: none !important;
  -webkit-transition: nonne !important;
}
::v-deep .el-upload-list__item-name {
  transition: none !important;
  -webkit-transition: nonne !important;
}
</style>

解决下面代码addressIds回显值问题:<template> <div class="container"> <el-tabs type="border-card"> <el-tab-pane :label="title"> <el-form ref="form" :model="form" :rules="rules" label-width="120px"> <el-form-item label="介绍图片:" prop="cover"> <div style="display: flex;"> <div class="flex-row"> <el-upload class="avatar-uploader" action :http-request="selectPicUpload" :show-file-list="false" :before-upload="beforeAvatarUpload" :on-change="handleAvatarChange"> <img v-if="form.cover" :src="form.cover" class="avatar"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </div> </div> </el-form-item> <el-row> <el-col :span="12"> <el-form-item label="团购名称:" prop="name"> <el-input class="input" v-model="form.name" placeholder="请输入团购名称" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="自动截止:" prop="isAutoEnd"> <el-switch v-model="form.isAutoEnd" :active-value="true" :inactive-value="false" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="团购内容:" prop="detailInfo"> <el-input class="input" v-model="form.detailInfo" placeholder="请输入团购内容" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="开始时间:" prop="startTime"> <el-date-picker clearable v-model="form.startTime" type="datetime" default-time="12:00:00" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择活动开始时间"> </el-date-picker> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="截止时间:" prop="endTime"> <el-date-picker clearable v-model="form.endTime" type="datetime" default-time="12:00:00" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择活动截止时间"> </el-date-picker> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="最大接龙人数" prop="maxJoinNum"> <el-input class="input" v-model="form.maxJoinNum" placeholder="请输入" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="最小成交金额" prop="minAmount"> <el-input class="input" v-model="form.minAmount" placeholder="请输入" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="提货地址:" prop="addressIds"> <el-select v-model="form.addressIds" filterable placeholder="请选择"> <el-option v-for="item in addressoptions" :key="item.id" :label="item.addressName" :value="item.id"> </el-option> </el-select> </el-form-item> </el-col> </el-row> <el-form-item label="其它信息:"> <el-checkbox-group v-model="form.selectedItemsOther"> <el-checkbox label="isGetRealName">真实姓名</el-checkbox> <el-checkbox label="isGetPhone">手机号码</el-checkbox> </el-checkbox-group> </el-form-item> <el-form-item label="功能选择:"> <el-checkbox-group v-model="form.selectedItemsFunction"> <el-checkbox label="isHideList">发布最新接龙列表</el-checkbox> <el-checkbox label="isHideOrderAmount">接龙金额仅创建人可见</el-checkbox> <el-checkbox label="canCancel">禁止用户取消订单</el-checkbox> </el-checkbox-group> </el-form-item> <div> <el-form-item label="商品信息:"> </el-form-item> <el-table :data="form.products" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column label="商品名称" align="center" prop="product.name"> <template slot-scope="{row,$index}"> <el-input v-model="row.name" placeholder="请输入名称"></el-input> </template>" </el-table-column> <el-table-column label="商品价格" align="center" prop="products.price"> <template slot-scope="{row,$index}"> <el-input v-model="row.price" placeholder="请输入名称"></el-input> </template>" </el-table-column> <el-table-column label="商品数量" align="center" prop="products.soldNum"> <template slot-scope="{row,$index}"> <el-input v-model="row.soldNum" placeholder="请输入名称"></el-input> </template> </el-table-column> <el-table-column label="每单限制购买量" align="center" prop="products.limitNum" width="180"> <template slot-scope="{row,$index}"> <el-input v-model="row.limitNum" placeholder="请输入名称"></el-input> </template> </el-table-column> <el-table-column label="商品图片" align="center" prop="image" width="100"> <template slot-scope="{row,$index}"> <el-upload class="avatar-uploader" action :http-request="(fileObj) => selectProductUpload(fileObj, $index)" :show-file-list="false" :before-upload="beforeAvatarUpload"> <img v-if="row.image" :src="row.image" class="avatar"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </template> </el-table-column> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <!-- <el-button size="mini" type="text" v-if="scope.row.status != '4'" icon="el-icon-edit" @click="openApproval(scope.row)" v-hasPermi="['solitaire:list:order']">添加 </el-button > --> <el-button @click="addproduct()"> 添加</el-button> <el-button type="danger" icon="el-icon-delete" @click="removeRow($index)">删除</el-button> </template> </el-table-column> </el-table> </div> </el-form> <!-- 修改/新增 --> <div class="dialog-footer"> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="handleBack">取 消</el-button> </div> </el-tab-pane> </el-tabs> </div> </template> <script> import { imgUrl } from '@/utils/request' import { getISOTime, checkLongitudeValidator, checkPointIsNumber } from '@/utils/parsing' import { checkPhone } from '@/utils/validate' import mapView from '@/components/MapContainer/index.vue' import { addGroupShop, editGroupBuy, getGroupBuyAddressPage, getGroupBuyDetail } from '@/api/solitaire/solitaire' import { uploadPic } from '@/api/activity/activity' import { onMounted, beforeDestroy } from 'vue' import { reset } from 'echarts/lib/visual/seriesColor' export default { components: { mapView }, onMounted() { }, data() { return { addressoptions: [], addressList: [], currentTime: new Date().toLocaleTimeString(), timer: null, // 第一个复选框组绑定值 // 第二个复选框组绑定值 selectedItemsFunction: [], checkList: ['选中且禁用', '复选框 A'], checked: true, // 单个复选框的绑定 dataList: [], title: "新增接龙", // 封面图片 selectImgUrl: '', queryParams: {}, currentImgIndex: 0, // 封面图片类型选择 imgDialogVisible: false, // 表单数据 form: { selectedItemsOther: [], selectedItemsFunction: [], ids: null, // addressIds: ['1'], addressIds: [], canCancel: 0, cover: '', detailInfo: null, endTime: null, id: 0, isAutoEnd: 0, isHideList: false, isHideOrderAmount: false, isJoin: 0, maxJoinNum: 0, minAmount: 0, name: "", products: [ { id: 0, image: imgUrl, limitNum: 0, name: "", parentId: 0, price: 0, soldNum: 0, totalNum: 0 } ], realEndTime: "", startTime: "", status: 0, }, // 表单校验 rules: { cover: [ { required: true, message: '请选择活动图片', trigger: ['change', 'blur'] } ], name: [ { required: true, message: '请输入团购名称', trigger: 'change' } ], othermessage: [ { type: 'array', required: true, message: '请选择其它信息', trigger: 'change' } ], selectability: [ { type: 'array', required: true, message: '请选择功能', trigger: 'change' }, ], serviceSafeguard: [ { type: 'array', required: true, message: '请选择志愿者保障', trigger: 'change' } ], detailInfo: [ { required: true, message: '请输入团购内容', trigger: 'change' } ], maxJoinNum: [ { required: true, message: '最大人数不能为空', trigger: 'change' } ], minAmount: [ { required: true, message: '最小成交金额不能为空', trigger: 'change' } ], endTime: [ { required: true, message: '结束时间必须选择', trigger: 'change' } ], startTime: [ { required: true, message: '开始时间不能为空', trigger: 'change' } ], description: [ { required: true, message: '活动开始内容不能为空', trigger: 'change' } ], address: [ { required: true, message: '地点不能为空', trigger: 'change' } ], conditionRequire: [ { required: true, message: '请输入条件说明', trigger: 'change' } ], // signStartDatetime: [ // { required: true, message: '报名开始时间必须选择', trigger: 'change' } // ], signEndDatetime: [ { required: true, message: '活动截止时间必须选择', trigger: 'change' } ], jobUserVOLists: [ { type: 'array', required: true, message: '请添加岗位需求', trigger: 'change' } ], 'organizer[0].name': [ { required: true, message: '请添加活动主办方姓名', trigger: 'change' } ], 'organizer[0].linkmanName': [ { required: true, message: '请添加活动负责人联系人', trigger: 'change' } ], 'organizer[0].phone': [ { required: true, message: '请添加活动负责人电话', trigger: 'change' }, { validator: checkPhone, trigger: 'blur' } ] }, // 图片前缀 cover: imgUrl, // //分页 column: { pageSize: null, pageNum: null }, // 队伍列表 // 岗位表单校验 jobRules: { jobName: [ { required: true, message: '请输入岗位名称', trigger: 'change' } ], jobCount: [ { required: true, message: '请输入岗位人数', trigger: 'change' } ], workHour: [ { required: true, message: '请输入工时', trigger: 'change' } ], pointPercent: [ { required: true, message: '请输入积分(每人每小时)', trigger: 'change' }, { validator: checkPointIsNumber, message: '积分必须为数字' } ], checkInTime: [ { required: true, message: '请输入签到时间', trigger: 'change' } ], checkOutTime: [ { required: true, message: '请输入签退时间', trigger: 'change' } ], checkLocation: [ { required: true, message: '请选择签到地点', trigger: 'change' } ] }, } }, created() { this.getAddressList() this.queryParams = this.$route.query; console.log(this.queryParams.id, '888') if (this.queryParams.id) { // 编辑 this.title = '修改接龙' getGroupBuyDetail(this.queryParams.id).then(res => { const boolConvert = val => val === 1; console.log(res, '---') const { cover, id } = res; console.log(res.isGetRealName) this.form = { ...res, isAutoEnd: res.isAutoEnd = 1 ? true : false, id, cover, addressIds: [], selectedItemsOther: [], selectedItemsFunction: [], } if (res.isGetRealName === 1) { this.form.selectedItemsOther.push('isGetRealName') } if (res.isGetPhone === 1) { this.form.selectedItemsOther.push('isGetPhone') } if (res.isHideList === 1) { this.form.selectedItemsFunction.push('isHideList') } if (res.isHideOrderAmount === 1) { this.form.selectedItemsFunction.push('isHideOrderAmount') } if (res.canCancel === 1) { this.form.selectedItemsFunction.push('canCancel') } }) } }, methods: { onMounted() { }, getAddressList() { getGroupBuyAddressPage().then(res => { this.addressoptions = res.pageData }) }, beforeDestroy() { clearInterval(this.timer); }, removeRow(index) { if (this.form.products.length > 1) { this.form.products.splice(index, 1); } else { this.$message.warning('至少保留一行数据'); } }, addproduct() { this.form.products.push({ id: '', image: '', limitNum: '', name: "", parentId: '', price: '', soldNum: '', totalNum: '' }) }, handleProductAvatarChange(file) { if (!file || !file.raw) return; const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { // 限制最大尺寸(合理值:800×600) const MAX_SIZE = 800; let width = img.width; let height = img.height; // 按比例缩放到最大尺寸 if (width > MAX_SIZE || height > MAX_SIZE) { const scale = Math.min(MAX_SIZE / width, MAX_SIZE / height); width *= scale; height *= scale; } // 画布压缩处理 const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); // 关键优化:降低质量并生成更短Data URL this.form.products.image = canvas.toDataURL(file.type, 0.01); // 压缩质量65% }; img.src = e.target.result; }; reader.readAsDataURL(file.raw); }, handleAvatarChange(file) { if (!file || !file.raw) return; const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { // 限制最大尺寸(合理值:800×600) const MAX_SIZE = 800; let width = img.width; let height = img.height; // 按比例缩放到最大尺寸 if (width > MAX_SIZE || height > MAX_SIZE) { const scale = Math.min(MAX_SIZE / width, MAX_SIZE / height); width *= scale; height *= scale; } // 画布压缩处理 const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); // 关键优化:降低质量并生成更短Data URL this.form.cover = canvas.toDataURL(file.type, 0.01); // 压缩质量65% }; img.src = e.target.result; }; reader.readAsDataURL(file.raw); }, beforeAvatarUpload(file) { const validTypes = ['image/jpeg', 'image/png', 'image/webp']; // 移除GIF减小潜在数据量 const MAX_SIZE_MB = 1.2; // 更严格的大小限制 const isImage = validTypes.includes(file.type); const isSizeValid = file.size / 1024 / 1024 < MAX_SIZE_MB; if (!isImage) { this.$message.error('仅支持 JPG/PNG/WEBP 格式'); return false; } if (!isSizeValid) { this.$message.error(`图片大小不能超过 ${MAX_SIZE_MB}MB`); return false; } return true; }, // updateOtherInfo() { // this.form.isGetRealName = this.selectedItemsOther.includes('真实姓名'); // this.form.isGetPhone = this.selectedItemsOther.includes('手机号码'); // }, // 更新"功能选择"表单值 updateFunctionSelection() { this.form.isHideList = this.selectedItemsFunction.includes('发布最新接龙列表'); this.form.isHideOrderAmount = this.selectedItemsFunction.includes('接龙金额仅创建人可见'); // "禁止用户取消订单"需要特殊处理:选中时canCancel=false this.form.canCancel = !this.selectedItemsFunction.includes('禁止用户取消订单'); }, // 获取活动详情 // getDetail(status) { // getGroupBuyDetail(this.queryParams.id, status).then(response => { // this.queryParams.id, status // this.form = response // console.log(response, '789') // }) // }, // 表单重置 reset() { this.form = { addressIds: [], cover: null, canCancel: 0, ids: null, detailInfo: null, endTime: null, id: 0, isAutoEnd: 0, isGetPhone: 0, isGetRealName: 0, isHideList: 0, isHideOrderAmount: 0, isJoin: 0, maxJoinNum: 0, minAmount: 0, name: "", products: [ { id: 0, image: imgUrl, limitNum: 0, name: "", parentId: 0, price: 0, soldNum: 0, totalNum: 0 } ], realEndTime: "", startTime: "", status: 0, } }, selectPicUpload(obj) { let fd = new FormData() //参数的格式是formData格式的 fd.append('file', obj.file) //参数 uploadPic(fd, { modularName: 'ACTIVITY' }).then(res => { this.form.cover = res }) }, selectProductUpload(obj, index) { let fd = new FormData(); fd.append('file', obj.file); uploadPic(fd, { modularName: 'ACTIVITY' }).then(res => { // 直接更新对应索引位置的图片URL this.$set(this.form.products, index, { ...this.form.products[index], image: res }); }).catch(error => { console.error('商品图片上传失败:', error); this.$message.error('商品图片上传失败'); }); }, // // 取消关闭弹窗 // closeDialog() { // this.currentImgIndex = 0 // this.selectImgUrl = '' // this.imgDialogVisible = false // }, // // 确认默认图片关闭弹窗 // submitPic() { // this.form.cover = this.selectImgUrl // this.imgDialogVisible = false // }, hasCommonValue(arr) { let seen = new Set(); // 用于存储已经遇到的值 for (let obj of arr) { for (let value of obj.jobUsers) { // 如果值已经存在于 set 中,返回 true if (seen.has(value.userId)) { return true; } // 将当前值加入 set seen.add(value.userId); } } return false; // 如果没有重复的值,返回 false }, /** 提交按钮 */ submitForm() { console.log(this.form.products.image, '000') let newForm = { ids: this.form.ids, addressIds: this.form.addressIds.map(item=>({id:item.id })), canCancel: this.form.selectedItemsFunction.includes('canCancel') ? 1 : 0, cover: this.form.cover, detailInfo: this.form.detailInfo, endTime: this.form.endTime, id: this.form.id, isAutoEnd: this.form.isAutoEnd == true ? 1 : 0, isGetPhone: this.form.selectedItemsOther.includes('isGetPhone') ? 1 : 0, isGetRealName: this.form.selectedItemsOther.includes('isGetRealName') ? 1 : 0, isHideList: this.form.selectedItemsFunction.includes('isHideList') ? 0 : 1, isHideOrderAmount: this.selectedItemsFunction.includes('isHideOrderAmount') ? 1 : 0, isJoin: this.form.isJoin, maxJoinNum: this.form.maxJoinNum, minAmount: this.form.minAmount, name: this.form.name, // 遍历数组中的每个对象 products: this.form.products.map(item => ({ id: item.id, image: item.image, limitNum: item.limitNum, name: item.name, parentId: item.parentId, price: item.price, soldNum: item.soldNum, totalNum: item.totalNum })), realEndTime: this.form.realEndTime, startTime: this.form.startTime, status: this.form.status, // 修正为this.form.status }; console.log(newForm, this.form.id) //判断每个时间段是否有岗位 // let isJobEmpty = this.form.jobUserVOLists.some(item => item.jobList.length == 0) this.$refs['form'].validate(valid => { if (valid) { if (this.form.id) { editGroupBuy(newForm).then(response => { console.log(response, '00') this.$modal.msgSuccess('修改成功') this.$router.back(-1) }) } else { if (this.form.name != '' && this.form.cover != '') { addGroupShop(newForm).then(response => { console.log(response) this.$modal.msgSuccess('新增成功') this.$router.back(-1) }) } else { console.log(this.form.ids, '09') this.$modal.msgError('请完善信息') } } } }) }, handleBack() { this.$router.back(-1) }, // handleSelectionChange(selection) { this.ids = selection.map(item => item.pkId) this.single = selection.length !== 1 this.multiple = !selection.length }, // 新增时间段 changeStatus() { if (this.currentTime) { this.timer = setInterval(() => { this.currentTime = new Date().toLocaleTimeString(); console.log(this.currentTime, '90') }, 1000); } }, // 改变时间段的初始时间 changeStartTime(value, timeIndex) { console.log(this.form.jobUserVOLists[timeIndex].jobList) if (this.form.jobUserVOLists[timeIndex].jobList) { this.form.jobUserVOLists[timeIndex].jobList.forEach(item => { item.checkInTime = value if (item.checkOutTime && item.checkInTime) { item.workHour = getISOTime(item.checkInTime, item.checkOutTime) if (item.jobUsers) { item.jobUsers.forEach(itemson => { itemson.workHour = item.workHour itemson.point = parseInt(item.pointPercent) * item.workHour }) } } }) } }, // 改变时间段的初始时间 changeEndTime(value, timeIndex) { if (this.form.jobUserVOLists[timeIndex].jobList) { this.form.jobUserVOLists[timeIndex].jobList.forEach(item => { item.checkOutTime = value if (item.checkOutTime && item.checkInTime) { item.workHour = getISOTime(item.checkInTime, item.checkOutTime) if (item.jobUsers) { item.jobUsers.forEach(itemson => { itemson.workHour = item.workHour itemson.point = parseInt(item.pointPercent) * item.workHour }) } } }) } }, // 改变时间段的签到纬度 changeLatitude(value, timeIndex) { if (this.form.jobUserVOLists[timeIndex].jobList) { this.form.jobUserVOLists[timeIndex].jobList.forEach(item => { item.checkLatitude = value }) } }, // 改变时间段的签到经度 changeLongitude(value, timeIndex) { if (this.form.jobUserVOLists[timeIndex].jobList) { this.form.jobUserVOLists[timeIndex].jobList.forEach(item => { item.checkLongitude = value }) } }, // 跳转 toPrck() { window.open('https://lbs.amap.com/tools/picker') }, // 删除时间段 deleteTime(timeIndex) { this.$modal.confirm('是否确认该时间段?').then(function () { }).then(() => { this.form.jobUserVOLists.splice(timeIndex, 1) this.changeNumber() }).catch(() => { }) }, // 删除岗位按钮 // 活动最大人数改变 changeNumber() { this.form.maxCount = 0 this.form.jobUserVOLists.forEach(item => { item.jobList.forEach(itemson => { this.form.maxCount += itemson.jobUsers.length }) }) }, // 岗位人数改变 //岗位志愿者选择 } } </script> <style scoped> .container { background-color: #f5f5f5; padding: 1%; } .title_type { font-size: 24px; font-weight: 700; margin-bottom: 20px; } .dialog-footer { display: flex; flex-direction: row; justify-content: center; align-items: center; padding-bottom: 30px; } .avatar-uploader .el-upload { border: 1px solid #f8f3f3; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; } .avatar-uploader .el-upload:hover { border-color: #409EFF; } .avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 120px; height: 120px; line-height: 120px; text-align: center; } .coverImage { width: 214px; height: 129px; border-radius: 6px; cursor: pointer; } .select_mask { position: absolute; z-index: 2; background-color: rgba(127, 125, 121, 0.5); text-align: center; top: 0; bottom: 0; left: 0; right: 0; width: 214px; height: 129px; display: flex; justify-content: center; align-items: center; } .avatar { width: 214px; height: 129px; display: block; } .margin-right-10 { margin-right: 10px; } .job_box { /* border: 1px solid #ccc; */ background-color: #F8F8F8; padding: 46px 10px 10px; margin-bottom: 10px; margin-left: 50px; border-radius: 10px; } </style>
10-30
要解决 el-upload 组件多张图片编辑时显示不了的问题,可从以下方面着手: ### 1. 确保 `file-list` 绑定正确 回显图片需将默认的 `file-list` 设置为 data 中的属性,以此设置默认值。示例代码如下: ```vue <el-upload action="" list-type="picture-card" multiple :on-change="handlChange" :file-list="imgList" :on-error="handleErr" ref="upload" :limit="10" accept=".jpg,.png,.jpeg" :on-exceed="handleMaxNum" :auto-upload="false" > <i slot="default" class="el-icon-plus"></i> <div slot="file" slot-scope="{file}"> <img class="el-upload-list__item-thumbnail" :src="file.url" alt=""> <span class="el-upload-list__item-actions"> <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)"> <i class="el-icon-zoom-in"></i> </span> <span class="el-upload-list__item-delete" @click="handleRemove(file)"> <i class="el-icon-delete"></i> </span> </span> </div> </el-upload> ``` 在 Vue 实例的 `data` 里,`imgList` 要包含图片信息对象,每个对象至少有 `url` 属性,此属性指向图片的地址: ```javascript data() { return { imgList: [ { url: 'https://example.com/image1.jpg' }, { url: 'https://example.com/image2.jpg' } ] }; } ``` ### 2. 检查图片地址的有效性 要保证 `file-list` 中每个文件对象的 `url` 属性指向的图片地址是有效的。可在浏览器中直接访问这些地址,验证其是否能正常显示图片。 ### 3. 处理图片加载错误 可添加 `on-error` 事件处理函数,当图片加载失败时进行相应处理: ```vue <el-upload ... :on-error="handleErr" ... > ... </el-upload> ``` ```javascript methods: { handleErr(file, response, uploader) { console.log('图片加载失败:', file); // 可在此处添加其他处理逻辑,如显示错误提示 } } ``` ### 4. 检查样式问题 有时候样式问题可能会致使图片显示异常。可检查是否有 CSS 规则影响了图片的显示。若点击编辑图片会闪动上一张图片,可在 CSS 中加入以下代码: ```css <style scoped lang="scss"> ::v-deep .el-upload-list__item { transition: none !important; } </style> ``` ### 5. 确保数据更新 若图片数据是异步获取的,要保证在数据获取完成后更新 `file-list` 绑定的数据。可在数据获取成功的回调函数中更新 `imgList`: ```javascript async mounted() { try { const response = await axios.get('https://example.com/api/images'); this.imgList = response.data.map(url => ({ url })); } catch (error) { console.error('获取图片数据失败:', error); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值