蓝桥第五课-上传组件的封装调用以及新增、修改商品信息的开发

  • 上节课说到,我们要实现新增商品缺少了一个【cover】字段,我们需要封装一个上传组件来实现图片上传
  • 基于【element-ui】的【el-upload】来实现封装
  • 在【src/components】文件夹下新建一个公共文件夹【Common】,在这个文件内新建一个公共文件【UploadImg.vue
  • 然后在需要引入的页面,引入这个图片上传组件

    

import UploadImg from '@/components/Common/UploadImg.vue';
components: { UploadImg }
  • 下面开始封装图片上传组件
  • 先敲【vbase】快速生成组件模版





  • 然后把【el-upload】组件copy过来
 
      
 
  • 首先来解释下参数的定义
  • action 是必选参数,上传的地址
  • 但是我们要实现自己的上传方法就要用到http-request
  • http-request是覆盖默认的上传行为,可以自定义上传的实现
  • http-request定义的一个上传方法uploadImgMainImg,当执行上传的时候会调用此方法
  • file-list是上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}]
  • accept是规定上传的文件格式
  • limit是最大允许上传个数
  • list-type是文件列表的类型,也就是组件的样式
  • on-remove是移除已上传文件触发的钩子函数
  • on-change是文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
  • 其中on-removeon-change两个钩子函数会回调两个参数,一个是当前上传的文件file,一个是已经上传的文件列表fileList
  • :class="{ hide: hideUploadEdit }"是动态绑定class
  • hideUploadEdit是一个变量,或者是true或者是false,当为true的时候,给这个组件的dom上绑定一个class类名叫hide
  • 是一个icon图标插槽

  • 接下来,根据浏览器报错来解决报错
image.png
  • 看上图报错"hideUploadEdit" is not defined,是因为组件身上使用了一个hideUploadEdit变量,但是在data内我们没有定义这个变量,所以导致报错
  • 需要在data中定义下
data() {
      return {
        hideUploadEdit:false,//让他默认为false
      }
  },
image.png
  • 看上图报错"fileList" is not defined,是因为组件身上使用了一个fileList变量,但是在data内我们没有定义这个变量,所以导致报错
  • 需要在data中定义下
data() {
      return {
        fileList: [],,//让他默认为空数组
      }
  },
image.png
  • 看上图报错"limit" is not defined,是因为组件身上使用了一个limit变量,但是在data内我们没有定义这个变量,所以导致报错
  • 需要在data中定义下
data() {
     return {
       limit:1,//让他默认为1,就是只能上传一张图片
     }
 },
image.png
  • 看上图报错"handleRemove" is not defined,是因为组件身上定义了一个handleRemove方法,但是在methods内我们没有映射这个方法,所以导致报错
  • 需要在methods中映射下
methods: {
      /** 文件移除触发的钩子函数 */
      handleRemove(file,fileList){ 
          
      }
    },
image.png
  • 看上图报错"handleEditChange" is not defined,是因为组件身上定义了一个handleEditChange方法,但是在methods内我们没有映射这个方法,所以导致报错
  • 需要在methods中映射下
methods: {
      /** 文件上传触发的钩子函数 */
      handleEditChange(file,fileList){ 
          
      }
    },
image.png
  • 看上图报错"uploadImgMainImg" is not defined,是因为组件身上定义了一个uploadImgMainImg方法,但是在methods内我们没有映射这个方法,所以导致报错
  • 需要在methods中映射下
methods: {
      /** 文件上传触发的自定义方法 ,回调file文件对象,在这个方法内执行上传方法 */
      uploadImgMainImg(file){ 
          alert("开始上传")
      }
    },
  • 可以先来测试下,执行上传后触发的handleEditChange方法和uploadImgMainImg方法的回调参数是什么?
image.png

image.png
  • 当我们上传大于等于limit张数的图片时,即1张,上传按钮不应该在出现菜合理
image.png
  • 这个时候,我们需要处理下这种情况
  • 就是在on-change回调的方法里,通过判断上传的图片的张数是否大于等于limit的张数,如果条件成立,就把hideUploadEdit设置为true
methods:{
  handleEditChange(file,fileList){
        console.log(file);
        console.log(fileList);
        this.hideUploadEdit = fileList.length>=this.limit
    },
}
  • 这时候,就给上传组件的dom元素上绑定了一个class类名hide
image.png
  • 我们可以通过display:none来动态隐藏这个上传按钮区域,记住要根据hide这个class类名来走位
.hide{
  ::v-deep .el-upload--picture-card{
    display: none;
  }
}
image.png
  • 然而,当删除图片时,上传按钮区域还要在显示出来,不然没办法再次上传了,其实很简单,只需要在:on-remove="handleRemove"定义的方法里,将hideUploadEdit改为false即可
methods:{
   handleRemove(file,fileList){
        console.log(file);
        console.log(fileList);
        this.hideUploadEdit = false
      }
}
  • 这样就实现了我们的需求

  • 下面我们就来实现上传
  • 先说下上传背景 :我们采用的是客户端直传七牛云服务器,但是需要后端提供一个上传token,即uploadToken,所以需要请求一个接口来获取上传token
  • 然后前端还需要安装七牛云js插件
cnpm install qiniu-js --save
  • 然后再来定义一个API接口方法,打开接口文档https://www.showdoc.com.cn/2059631189527964/9403651044295056,找到获取上传图片token,复制api接口路径,在【src/api/user.js】文件内新建方法
/**
 * 
 * @returns  获取上传token
 */
export function getQiniuUpToken(params) {
  return request({
    url: '/api/getQiniuUpToken',
    method: 'get',
    params
  })
}
  • 然后在页面内引入api方法和七牛云js插件
import {getQiniuUpToken} from '@/api/user';
import * as qiniu from 'qiniu-js';
image.png
  • :http-request="uploadImgMainImg"触发的方法内直接请求获取上传token的接口方法,拿到【图片``baseUrl``图片路径前缀】,和【七牛云上传token】,然后在根据七牛云上传流程调用qiniu-js的方法实现上传,代码如下
methods:{
   uploadImgMainImg(file) {
      console.log(file);
      let _this = this;
      let baseUrl;
      let config = { useCdnDomain: true, region: qiniu.region.z0 };
      let putExtra = { fname: file.file.name, params: {}, mimeType: null };
      //开始上传  token 需要找后端获取(单独的方法)
      getQiniuUpToken().then((res) => {
        let upToken = res.data.uploadToken;
        baseUrl = res.data.baseUrl;
        let headers = qiniu.getHeadersForMkFile(upToken);
        //file 是获取到的文件对象
        //key 是文件名字,传null将使用hash值来当作文件名
        let observable = qiniu.upload(
          file.file,
          file.file.name,
          upToken,
          headers,
          putExtra,
          config
        );
        this.subscription = observable.subscribe(observe);
      });
      let observe = {
        next(res) {
          // console.log('已上传大小,单位为字节:' + res.total.loaded)
          // console.log('本次上传的总量控制信息,单位为字节:' + res.total.size)
          // console.log('当前上传进度,范围:0~100:' + res.total.percent);
          console.log(res);
        },
        error(err) {
          // console.log(err.code)
          // console.log(err.message)
          // console.log(err.isRequestError)
          // console.log(err.reqId)
          console.log(err);
        },
        /*完成后的操作*/
        complete(res) {
          //上传成功以后会返回key 和 hash  key就是文件名了!
          console.log(res);
          let fileUrl = baseUrl + res.key;
          _this.$message.success("上传成功");
          console.log(fileUrl);
        },
      };
    },
}
  • 这样我们就可以拿到了上传进度和上传成功后的url
image.png
  • 下面想要把这个图片url传递给父组件的ruleFrom的cover字段,需要用到自定义事件,供父组件调用
  • 在刚才的上传回调监听complete内自定义事件
 complete(res) {
     //上传成功以后会返回key 和 hash  key就是文件名了!
     console.log(res);
     let fileUrl = baseUrl + res.key;
     _this.$message.success("上传成功");
     console.log(fileUrl);
     _this.$emit("onSuccessFun", fileUrl);
},
image.png
  • 然后父组件可以触发onSuccessFun这个自定义事件,接收到图片链接,并赋值给cover

     

methods:{
  handleSuccessFun(url){
      this.ruleForm.cover = url
  }
}
  • 上传成功后,ruleForm内的cover就有值了
image.png
  • 再次请求上传方法就会上传成功
image.png
  • 然后处理下提交表单的方法,弹出新增成功提示,并回退到列表页面
submitForm(formName) {
      // this.$refs.ruleForm
      this.$refs.ruleForm.validate((valid) => {
        if (valid) {
          // alert('submit!');
          // 接口请求
          // console.log(this.ruleForm);
          addShop(this.ruleForm)
            .then((res) => {
              console.log(res);
              this.$message.success("新增成功")
              this.$router.go(-1)
            })
            .catch((err) => {
              console.log(err);
            });
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
  • 最后要完善一点,就是在上传图片成功后,如果用户发现上传错了,需要删除重新上传,这时候,就需要处理下这种情况,因为如果只是单纯的删掉上传区域的图片,ruleFormcover的值并没有被删掉,如果直接提交新增的话,会带着被删掉的图,新增成功,很明显,这是不合理的,怎么处理呢?
  • 就是要在上传组件的:on-remove="handleRemove"触发的方法内,也自定义一个事件 this.$emit("onRemoveFun")
methods:{
   handleRemove(file, fileList) {
      console.log(file);
      console.log(fileList);
      this.hideUploadEdit = false;
      this.$emit("onRemoveFun")
    },
}
  • 父组件调用这个方法 @onRemoveFun="handleRemoveFun"

    

  • 在methods里也映射下handleRemoveFun
handleRemoveFun(){
   this.ruleForm.cover = "";
}
  • 这样就完全搞定了~

  • 下面实现编辑修改商品信息
  • 先找到【编辑】按钮,给它定义一个点击事件@click="handleUpdate(scope.row)",因为要拿到当前行的数据ID,所以要传参数,然后在methods里映射这个方法
image.png
  • 然后跳转页面,跳转到新增商品的页面,因为页面都是一样的布局,我们完全可以复用一个页面,只不过要传递当前行的id过去
methods:{
   /** 点击编辑按钮触发的方法 */
    handleUpdate(row){
      this.$router.push("/shopModel/addShop?id="+row._id)
    }
}
  • 跳转过去后,地址栏会有一个id参数,我们需要获取到这个id参数来请求接口,【进行数据回显】,也是通过地址栏是否有id这个参数来判定当前页面是新增商品页面还是修改商品信息页面
image.png
  • 然后在mounted生命周期里获取地址栏参数,能获取到就请求获取指定商品详情的接口,在这之前,先根据接口文档,新建api接口方法
/**
 * 
 * @returns 获取指定商品信息 /api/query/goods/:id
 */
 export function getShopDetail(id) {
  return request({
    url: '/api/query/goods/'+id,
    method: 'get',
  })
}
  • 然后在addShop页面内引入
import { getShopDetail } from "@/api/user";
  • 然后在mounted生命周期里获取地址栏参数,能获取到就请求获取指定商品详情的接口
image.png
mounted() {
    let id = this.$route.query.id
    console.log(id);
  },
image.png
  • 然后通过if判断
mounted() {
    this.getShopMenuListFun();
    let id = this.$route.query.id
    console.log(id);
    if(id){
      getShopDetail(id).then(res=>{
        console.log(res);
      })
    }
  },
  • 请求成功拿到商品信息
image.png
  • 然后做数据回显,将res.data赋值给ruleForm,但是图片要单独处理下
mounted() {
    this.getShopMenuListFun();
    let id = this.$route.query.id
    console.log(id);
    if(id){
      getShopDetail(id).then(res=>{
        console.log(res);
        this.ruleForm = res.data //将`res.data`赋值给`ruleForm`
      })
    }
  },
image.png
  • 上传图片组件的数据回显,要用到fileList,直接拿到该组件的fileList,然后赋值即可,前提是先获取到上传组件的fileList
  • 要想获取到上传组件的fileList,就要先获取到这个组件
  • 通过【ref】来获取组件,在通过this.$refs.ref的名称.fileList来获取

     

image.png
 mounted() {
    this.getShopMenuListFun();
    let id = this.$route.query.id
    console.log(id);
    if(id){
      getShopDetail(id).then(res=>{
        console.log(res);
        this.ruleForm = res.data
        this.$refs.uploadCom.fileList = [{name:'key.png',url:res.data.cover}]
      })
    }
  },
image.png
  • 可以直接将上传组件的hideUploadEdit设置成true即可
mounted() {
    this.getShopMenuListFun();
    let id = this.$route.query.id
    console.log(id);
    if(id){
      getShopDetail(id).then(res=>{
        console.log(res);
        this.ruleForm = res.data
        this.$refs.uploadCom.fileList = [{name:'key.png',url:res.data.cover}]
        this.$refs.uploadCom.hideUploadEdit = true
      })
    }
  },
  • 这样就搞定了
image.png
  • 再往下,就处理下【立即创建】按钮,修改信息不能调用新增按钮,要根据id来区分,如果地址栏有id,就要调用修改商品信息的接口
  • 修改商品信息的接口,我们之前已经创建好了,直接引入到这个页面来
import { updateShop } from "@/api/user";
image.png
 
      立即创建
      立即修改
      重置

  • 在methods里映射submitUpdate方法
methods:{
  submitUpdate(){
    updateShop(this.ruleForm,this.$route.query.id).then(res=>{
        this.$message.success("修改成功");
        this.$router.go(-1)
      })
  }
}
  • 大功告成!!!

你可能感兴趣的:(蓝桥第五课-上传组件的封装调用以及新增、修改商品信息的开发)