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

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

作者: 风中凌乱的男子 | 来源:发表于2022-10-18 00:31 被阅读0次
    • 上节课说到,我们要实现新增商品缺少了一个【cover】字段,我们需要封装一个上传组件来实现图片上传
    • 基于【element-ui】的【el-upload】来实现封装
    • 在【src/components】文件夹下新建一个公共文件夹【Common】,在这个文件内新建一个公共文件【UploadImg.vue
    • 然后在需要引入的页面,引入这个图片上传组件
    <el-form-item label="图片上传">
        <UploadImg></UploadImg>
    </el-form-item>
    import UploadImg from '@/components/Common/UploadImg.vue';
    components: { UploadImg }
    
    • 下面开始封装图片上传组件
    • 先敲【vbase】快速生成组件模版
    <template>
      <div>
    
      </div>
    </template>
    
    <script>
      export default {
        
      }
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    
    • 然后把【el-upload】组件copy过来
     <el-upload
          class="upload-demo"
          action=""
          :http-request="uploadImgMainImg"
          :file-list="fileList"
          accept="image/png, image/jpeg, image/jpg"
          :limit="limit"
          list-type="picture-card"
          :on-remove="handleRemove"
          :on-change="handleEditChange"
          :class="{ hide: hideUploadEdit }"
        >
          <i slot="default" class="el-icon-plus"></i>
     </el-upload>
    
    • 首先来解释下参数的定义
    • 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
    • <i slot="default" class="el-icon-plus"></i>是一个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
    
    /**
     * 
     * @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
    <el-form-item label="图片上传">
         <UploadImg @onSuccessFun="handleSuccessFun"></UploadImg>
    </el-form-item>
    
    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"
    <el-form-item label="图片上传">
        <UploadImg @onSuccessFun="handleSuccessFun" @onRemoveFun="handleRemoveFun"></UploadImg>
    </el-form-item>
    
    • 在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来获取
    <el-form-item label="图片上传">
         <UploadImg @onSuccessFun="handleSuccessFun"
         @onRemoveFun="handleRemoveFun" 
         ref="uploadCom"></UploadImg>
    </el-form-item>
    
    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
     <el-form-item>
          <el-button type="primary" v-if="!$route.query.id" @click="submitForm">立即创建</el-button>
          <el-button type="primary" v-if="$route.query.id" @click="submitUpdate">立即修改</el-button>
          <el-button>重置</el-button>
    </el-form-item>
    
    • 在methods里映射submitUpdate方法
    methods:{
      submitUpdate(){
        updateShop(this.ruleForm,this.$route.query.id).then(res=>{
            this.$message.success("修改成功");
            this.$router.go(-1)
          })
      }
    }
    
    • 大功告成!!!

    相关文章

      网友评论

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

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