• Vue + ElementUI的电商管理系统实例14 商品列表-添加商品


    1、通过编程式导航跳转到商品添加页面

    给添加商品按钮添加点击事件:

    <el-button type="primary" @click="goAddpage">添加商品</el-button>

    goAddpage事件函数:

    // 跳转到添加商品页面
    goAddpage() {
          // 路由导航对象.push函数,跳转到指定的路由页面
          this.$router.push('goods/add')
    }

    在goods文件夹下新建Add.vue文件:

    <template>
    <div>
        添加商品组件页面
    </div>
    </template>
    
    <script>
    export default {
    }
    </script>
    
    <style lang="less" scoped>
    
    </style>

    添加路由:

    import Add from '../components/goods/Add.vue'
    
    const routes = [
      { path: '/', redirect: '/login' }, // 重定向
      { path: '/login', component: Login },
      {
        path: '/home',
        component: Home,
        redirect: '/welcome', // 重定向
        children: [ // 子路由
          { path: '/welcome', component: Welcome },
          { path: '/users', component: Users }, // 用户列表
          { path: '/rights', component: Rights }, // 权限列表
          { path: '/roles', component: Roles }, // 角色列表
          { path: '/categories', component: Cate }, // 商品分类
          { path: '/params', component: Params }, // 分类参数
          { path: '/goods', component: List }, // 商品列表
          { path: '/goods/add', component: Add } // 添加商品
        ]
      }
    ]

    点击添加商品按钮,效果图:

    2、渲染添加商品页面的基本结构

    还是面包屑和卡片视图:

    <template>
    <div>
      <!--面包屑导航区域-->
      <el-breadcrumb separator-class="el-icon-arrow-right">
          <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
          <el-breadcrumb-item>商品管理</el-breadcrumb-item>
          <el-breadcrumb-item>添加商品</el-breadcrumb-item>
      </el-breadcrumb>
      <!--卡片视图区域-->
      <el-card>
        <!--警告提示区域-->
        <el-alert title="添加商品信息" type="info" center show-icon :closable="false"></el-alert>
    
      </el-card>
    </div>
    </template>

    然后下面需要用到Steps 步骤条组件,要把Steps 和 step到添加到element.js中

    选择 含状态步骤条 的代码复制:

    <!--步骤条区域-->
    <el-steps :space="200" :active="1" finish-status="success">
          <el-step title="已完成"></el-step>
          <el-step title="进行中"></el-step>
          <el-step title="步骤 3"></el-step>
    </el-steps>

    此时的效果图:

    3、美化步骤条组件

    首先增加3个el-step,然后修改title的值:

    <!--步骤条区域-->
    <el-steps :space="200" :active="1" finish-status="success">
          <el-step title="基本信息"></el-step>
          <el-step title="商品参数"></el-step>
          <el-step title="商品属性"></el-step>
          <el-step title="商品图片"></el-step>
          <el-step title="商品内容"></el-step>
          <el-step title="完成"></el-step>
    </el-steps>

    然后在全局样式中添加:

    .el-steps{margin: 15px 0;}
    .el-step__title{font-size: 13px;}

    align-center 标题和描述都将居中。

    <el-steps :space="200" :active="1" finish-status="success" align-center>

    修改默认激活为第一项:

    <el-steps :space="200" :active="0" finish-status="success" align-center>

    定义一个属性绑定到active:

    <el-steps :space="200" :active="activeIndex" finish-status="success" align-center>
    
    <script>
    export default {
      data() {
        return {
          activeIndex: 0 // 当前激活的索引
        }
      }
    }
    </script>

    此时效果图:

    4、渲染tab栏区域

    添加tabs标签代码,可以通过 tab-position 设置标签的位置:

    <!--tab栏区域-->
    <el-tabs :tab-position="'left'" style="height: 200px;">
          <el-tab-pane label="基本信息">基本信息</el-tab-pane>
          <el-tab-pane label="商品参数">商品参数</el-tab-pane>
          <el-tab-pane label="商品属性">商品属性</el-tab-pane>
          <el-tab-pane label="商品图片">商品图片</el-tab-pane>
          <el-tab-pane label="商品内容">商品内容</el-tab-pane>
    </el-tabs>

    效果图:

    5、实现步骤条和tab栏的数据联动效果

    steps步骤条组件的激活状态是对应的activeIndex索引值

    在tabs组件中,有属性value / v-model :可以绑定选中选项卡的 name值

    所以我们就把选项卡的name分别设置0-4的值,然后把v-model绑定activeIndex索引值,这样每次切换选项卡时,它的name值就会绑定到activeIndex,由此实现联动效果。

    代码:

    <!--tab栏区域-->
    <el-tabs v-model="activeIndex" :tab-position="'left'" style="height: 200px;">
          <el-tab-pane label="基本信息" name="0">基本信息</el-tab-pane>
          <el-tab-pane label="商品参数" name="1">商品参数</el-tab-pane>
          <el-tab-pane label="商品属性" name="2">商品属性</el-tab-pane>
          <el-tab-pane label="商品图片" name="3">商品图片</el-tab-pane>
          <el-tab-pane label="商品内容" name="4">商品内容</el-tab-pane>
    </el-tabs>

    因为v-model绑定得是字符串类型,所以要把activeIndex修改为字符串:

    <script>
    export default {
      data() {
        return {
          activeIndex: '0' // 当前激活的索引
        }
      }
    }
    </script>

    但是steps步骤条组件的active属性绑定的是数字类型,所以要修改下:(隐式类型转换)

    <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center>

    此时刷新,可以实现联动效果,如图:

    6、分拆表单的组成部分

    在tabs标签外面,添加form表单:

    label-position=“top” 表示标签的位置
    <!--tab栏区域-->
    <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top">
    <el-tabs v-model="activeIndex" :tab-position="'left'" style="height: 200px;">
          <el-tab-pane label="基本信息" name="0">基本信息</el-tab-pane>
          <el-tab-pane label="商品参数" name="1">商品参数</el-tab-pane>
          <el-tab-pane label="商品属性" name="2">商品属性</el-tab-pane>
          <el-tab-pane label="商品图片" name="3">商品图片</el-tab-pane>
          <el-tab-pane label="商品内容" name="4">商品内容</el-tab-pane>
    </el-tabs>
    </el-form>
    
    <script>
    export default {
      data() {
          // 添加商品的表单数据对象
          addForm: {},
          // 添加商品表单的验证规则对象
          addFormRules: {}
        }
      }
    }
    </script>

    7、绘制基本信息面板的UI结构

    记得要把tabs组件上的height="200px"删掉,input指定type="number",只能输入数字类型

    添加代码:

    <el-tab-pane label="基本信息" name="0">
            <el-form-item label="商品名称" prop="goods_name">
              <el-input v-model="addForm.goods_name"></el-input>
            </el-form-item>
            <el-form-item label="商品价格" prop="goods_price">
              <el-input v-model="addForm.goods_price" type="number"></el-input>
            </el-form-item>
            <el-form-item label="商品重量" prop="goods_weight">
              <el-input v-model="addForm.goods_weight" type="number"></el-input>
            </el-form-item>
            <el-form-item label="商品数量" prop="goods_number">
              <el-input v-model="addForm.goods_number" type="number"></el-input>
            </el-form-item>
    </el-tab-pane>
    
    <script>
    export default {
      data() {
        return {
          // 添加商品的表单数据对象
          addForm: {
            goods_name: '',
            goods_price: 0,
            goods_weight: 0,
            goods_number: 0
          },
          // 添加商品表单的验证规则对象
          addFormRules: {
            goods_name: [
              { required: true, message: '请输入商品名称', trigger: 'blur' }
            ],
            goods_price: [
              { required: true, message: '请输入商品价格', trigger: 'blur' }
            ],
            goods_weight: [
              { required: true, message: '请输入商品重量', trigger: 'blur' }
            ],
            goods_number: [
              { required: true, message: '请输入商品数量', trigger: 'blur' }
            ]
          }
        }
      }
    }
    </script>

    此时效果图:

    8、获取商品分类数据

    调用api的商品分类数据列表接口,前面商品分类功能有写过

    添加代码:

    <script>
    export default {
      data() {
        return {
          cateList: [] // 商品分类列表数据
        }
      },
      created() {
        this.getCateList()
      },
      methods: {
        // 获取所有商品分类数据
        async getCateList() {
          const { data: res } = await this.$http.get('categories')
          if (res.meta.status !== 200) {
            return this.$message.error('获取商品分类失败')
          }
          console.log(res)
          this.cateList = res.data
        }
      }
    }
    </script>

    9、绘制商品分类的级联选择器

    添加级联选择器代码:

    <el-form-item label="商品分类" prop="goods_cat">
              <!--选择商品分类的级联选择框-->
              <!-- options用来指定数据源  props用来指定配置对象  value/v-model选中项绑定值-->
              <el-cascader
                v-model="addForm.goods_cat"
                :options="cateList"
                :props="cateProps"
                @change="cateChanged"
                clearable
              ></el-cascader>
    </el-form-item>
    
    <script>
    export default {
      data() {
        return {
          // 添加商品的表单数据对象
          addForm: {
            goods_name: '',
            goods_price: 0,
            goods_weight: 0,
            goods_number: 0,
            goods_cat: [] // 商品所属的分类数据
          },
          // 添加商品表单的验证规则对象
          addFormRules: {
            goods_name: [
              { required: true, message: '请输入商品名称', trigger: 'blur' }
            ],
            goods_price: [
              { required: true, message: '请输入商品价格', trigger: 'blur' }
            ],
            goods_weight: [
              { required: true, message: '请输入商品重量', trigger: 'blur' }
            ],
            goods_number: [
              { required: true, message: '请输入商品数量', trigger: 'blur' }
            ],
            goods_cat: [
              { required: true, message: '请选择商品分类', trigger: 'blur' }
            ]
          },
          cateList: [], // 商品分类列表数据
          // 指定级联选择框的配置对象
          cateProps: {
            expandTrigger: 'hover', // 次级菜单的展开方式 click / hover
            value: 'cat_id', // 指定选中值的属性
            label: 'cat_name', // 指定选中标签的名称
            children: 'children' // 指定父子嵌套的属性
          }
        }
      },
      methods: {
        // 级联选择框选中项变化,会触发这个函数
        cateChanged() {
          console.log(this.addForm.goods_cat)
        }
      }
    }
    </script>

    我们这里设置的goods_cat是个数组,后台接口定义的是要接收字符串类型,后面还要进行转译。

    效果图:

    10、只允许选中三级商品分类

    // 级联选择框选中项变化,会触发这个函数
    cateChanged() {
          // 证明选中的不是三级分类
          if (this.addForm.goods_cat.length !== 3) {
            this.addForm.goods_cat = [] // 清空级联选择框的数据
            return
          }
          // 证明选中的是三级分类
          console.log(this.addForm.goods_cat)
    }

    11、阻止标签切换

    在没有填写表单前,不允许切换标签。

    监听标签页的切换事件,在事件的处理函数中,判断是否处于第一个标签中,同时还要判断已选中的商品分类是否为三级商品分类。

    tabs组件中属性:before-leave 切换标签之前的钩子,若返回 false 或者返回 Promise 且被 reject,则阻止切换。返回一个Function(activeName, oldActiveName),其中activeName:代表即将进入的标签页的name名称,oldActiveName:代表即将离开的标签页的name名称。

    添加代码:

    <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave">

    beforeTabLeave事件函数:

    // 阻止标签切换
    beforeTabLeave(activeName, oldActiveName) {
        //   console.log('即将离开的标签页名字是' + oldActiveName)
        //   console.log('即将进入的标签页名字是' + activeName)
          if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) {
            this.$message.error('请先选择商品分类')
            return false
          }
    }

    12、获取动态参数列表数据

    下面要实现的是商品参数标签,需要调用api的参数列表接口,具体前面也有写过

    还需要用到tabs组件的tab-click事件,表示:tab 被选中时触发 

    添加代码:

    <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave"
         @tab-click="tabClicked">

    tabClicked事件函数:

    // tab 被选中时触发
    async tabClicked() {
         console.log(this.activeIndex) 
    }

    这时打印的是当前激活标签的name,所以要判断下当打印为1时,请求参数列表接口,获取参数列表数据

    // tab 被选中时触发
    async tabClicked() {
          console.log(this.activeIndex) // 当前激活标签的name
          // 证明访问的时动态参数面板
          if (this.activeIndex === '1') {
            const { data: res } = await this.$http.get(
              `categories/${this.addForm.goods_cat[2]}/attributes`,
              { params: { sel: 'many' } }
            )
            if (res.meta.status !== 200) {
              return this.$message.error('获取参数列表失败')
            }
            console.log(res)
            this.manyTableData = res.data
         }
    }

    this.addForm.goods_cat[2]这样写的太复杂,也可以定义一个计算属性来实现:

    methods:{
        // tab 被选中时触发
        async tabClicked() {
          console.log(this.activeIndex) // 当前激活标签的name
          // 证明访问的时动态参数面板
          if (this.activeIndex === '1') {
            const { data: res } = await this.$http.get(
              `categories/${this.cateId}/attributes`,
              { params: { sel: 'many' } }
            )
            if (res.meta.status !== 200) {
              return this.$message.error('获取动态参数列表失败')
            }
            console.log(res)
            this.manyTableData = res.data
          }
        }
    },
    computed: {
        // 当前选中的三级分类ID
        cateId() {
          if (this.addForm.goods_cat.length === 3) {
            return this.addForm.goods_cat[2]
          }
          return null
        }
    }

    13、绘制商品参数面板中的复选框组件

    先在商品参数的标签中添加代码:

    <el-tab-pane label="商品参数" name="1">
            <!-- 渲染表单的item项 -->
            <el-form-item v-for="item in manyTableData" :key="item.attr_id"
             :label="item.attr_name">
            </el-form-item>
    </el-tab-pane>

    因为attr.vals是字符串,所以要在获取到参数列表数据时,将数据进行for循环,每循环一次把item项用空格做分隔,重新赋值为数组的形式。

    修改tabClicked函数:

    // tab 被选中时触发
    async tabClicked() {
          console.log(this.activeIndex) // 当前激活标签的name
          // 证明访问的时动态参数面板
          if (this.activeIndex === '1') {
            const { data: res } = await this.$http.get(
              `categories/${this.cateId}/attributes`,
              { params: { sel: 'many' } }
            )
            if (res.meta.status !== 200) {
              return this.$message.error('获取动态参数列表失败')
            }
            console.log(res)
            // 循环data中的每一项,把attr_vals参数的字符串,重新赋值为item.attr_vals数组
            res.data.forEach(item => {
              item.attr_vals = item.attr_vals.length !== 0
                ? item.attr_vals.split(' ') : []
            })
            this.manyTableData = res.data
          }
    }

    然后需要用到Checkbox 多选框组件中,多选框组:适用于多个勾选框绑定到同一个数组的情景,通过是否勾选来表示这一组选项中选中的项。

    checkbox-group元素能把多个 checkbox 管理为一组,只需要在 Group 中使用v-model绑定Array类型的变量即可。 el-checkbox 的 label属性是该 checkbox 对应的值,若该标签中无内容,则该属性也充当 checkbox 按钮后的介绍。label与数组中的元素值相对应,如果存在指定的值则为选中状态,否则为不选中。

    设置border属性可以渲染为带有边框的多选框。

    添加代码:

    <el-tab-pane label="商品参数" name="1">
            <!-- 渲染表单的item项 -->
            <el-form-item v-for="item in manyTableData" :key="item.attr_id"
             :label="item.attr_name">
              <!--复选框组-->
              <el-checkbox-group v-model="item.attr_vals">
                <el-checkbox :label="cb" v-for="(cb, i) in item.attr_vals"
                 :key="i" border size="small"></el-checkbox>
              </el-checkbox-group>
            </el-form-item>
    </el-tab-pane>

    此时效果图:

    14、优化复选框的样式

    每个复选框的间隔过大,而且换行的话,第2行对不齐

    添加样式:

    <style lang="less" scoped>
    .el-checkbox{
    margin: 0 10px 0 0 !important;
    }
    </style>

    效果图:

    15、获取静态属性列表

    还是要调用api的参数列表接口,只不过参数变为静态属性的only

    修改tabClicked函数,添加代码:

    // tab 被选中时触发
    async tabClicked() {
          console.log(this.activeIndex) // 当前激活标签的name
          // 证明访问的是动态参数面板
          if (this.activeIndex === '1') {
            const { data: res } = await this.$http.get(
              `categories/${this.cateId}/attributes`,
              { params: { sel: 'many' } }
            )
            if (res.meta.status !== 200) {
              return this.$message.error('获取动态参数列表失败')
            }
            console.log(res)
            // 循环data中的每一项,把attr_vals参数的字符串,重新赋值为item.attr_vals数组
            res.data.forEach(item => {
              item.attr_vals = item.attr_vals.length !== 0
                ? item.attr_vals.split(' ') : []
            })
            this.manyTableData = res.data
          } else if (this.activeIndex === '2') {
            // 证明访问的是静态属性面板
            const { data: res } = await this.$http.get(
              `categories/${this.cateId}/attributes`,
              { params: { sel: 'only' } }
            )
            if (res.meta.status !== 200) {
              return this.$message.error('获取静态属性列表失败')
            }
            console.log(res)
            this.onlyTableData = res.data
          }
    }

    16、渲染商品属性面板的UI结构

    添加代码:

    <el-tab-pane label="商品属性" name="2">
            <el-form-item v-for="item in onlyTableData" :key="item.attr_id" :label="item.attr_name">
              <el-input v-model="item.attr_vals"></el-input>
            </el-form-item>
    </el-tab-pane>

    效果图:

    17、初步使用upload上传组件

    Upload 上传组件:通过点击或者拖拽上传文件

    通过 slot 你可以传入自定义的上传按钮类型和文字提示。可通过设置limiton-exceed来限制上传文件的个数和定义超出限制时的行为。可通过设置before-remove来阻止文件移除操作。

    action 必选参数,上传的地址
    on-preview 点击文件列表中已上传的文件时的钩子 处理图片预览效果
    on-remove 文件列表移除文件时的钩子
    list-type 文件列表的类型 指定预览文件的类型

    使用图片列表缩略图,复制代码:

    <el-tab-pane label="商品图片" name="3">
            <!--上传组件-->
            <!--action 表示图片要上传到的后台API地址-->
            <el-upload :action="uploadURL" :on-preview="handlePreview"
            :on-remove="handleRemove" list-type="picture">
              <el-button size="small" type="primary">点击上传</el-button>
            </el-upload>
    </el-tab-pane>
    action 表示图片要上传到的后台API地址,需要图片上传接口的路径,请求路径:upload,请求方法:post,请求参数:file    上传文件

    data里添加:

    // 上传图片的URL
    uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload'

    18、手动给upload组件绑定headers请求头

    这时我们打开控制台的Network,然后点击上传按钮,随便选择一张图片上传。这时发现Previwe里的msg显示无效token,状态码400,证明我们上传失败了

    我们最开始的时候在mian.js文件里通过axios的request拦截器,给每一个请求都挂载了一个Authorization字段,里面的值就是'token'。

    这就证明upload组件上传的时候没有用到axios请求。

    查看element的upload文档,有个headers属性:设置上传的请求头部  object对象

    添加代码:

    <!--上传组件-->
    <!--action 表示图片要上传到的后台API地址-->
    <el-upload :action="uploadURL" :on-preview="handlePreview"
            :on-remove="handleRemove" list-type="picture" :headers="headerObj">
              <el-button size="small" type="primary">点击上传</el-button>
    </el-upload>
    
    <script>
    export default {
      data() {
        return {
          // 图片上传组件的headers请求头对象
          headerObj: {
            Authorization: window.sessionStorage.getItem('token')
          }
        }
      }
    }
    </script>

    刷新重新点击上传按钮上传图片,查看控制台效果:

    19、监听upload组件的on-success事件

    查看api接口文档,发现添加商品接口,其中一项参数pics:上传的图片临时路径(对象)

    "pics":[
        {"pic":"/tmp_uploads/30f08d52c551ecb447277eae232304b8"}
    ],

    我们刚才在控制台里看到图片上传成功后服务器返回的信息,tmp_path字段里就是服务器保存的是图片的临时路径。

    我们下面需要做的就是监听图片上传成功之后,把tmp_path的临时路径以对象的形式添加到pics数组中。

    查看element文档的upload组件,有个on-success事件:文件上传成功时的钩子函数,可以指定function(response, file, fileList) ,其中response代表服务器返回的数据,file是文件的信息,fileList是上次组件的文件列表。我们这里只用到response就行了。

    在addForm对象中添加pics数组,然后添加on-success代码:

    <!--上传组件-->
    <!--action 表示图片要上传到的后台API地址-->
    <el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove"
             list-type="picture" :headers="headerObj" :on-success="handleSuccess">
              <el-button size="small" type="primary">点击上传</el-button>
    </el-upload>
    
    <script>
    export default {
      data() {
        return {
          // 添加商品的表单数据对象
          addForm: {
            goods_name: '',
            goods_price: 0,
            goods_weight: 0,
            goods_number: 0,
            goods_cat: [], // 商品所属的分类数据
            pics: [] // 图片的数组
          },
        }
      },
      methods: {
        // 监听图片上传成功的事件
        handleSuccess(response) {
          console.log(response)
          // 1.拼接得到一个图片信息对象
          const picInfo = { pic: response.data.tmp_path }
          // 2.将图片信息对象,push到pics组数中
          this.addForm.pics.push(picInfo)
          console.log(this.addForm)
        }
      }
    }
    </script>

    效果图:

    20、监听upload组件的on-remove事件

    1.获取将要删除的图片的临时路径
    2.从 pics 数组中,找到这个图片对应的索引值
    3.调用数组的 splice 方法,把图片信息对象,从 pics 数组中删除

    代码:

    // 处理移除图片的操作
    handleRemove(file) {
          // console.log(file)
          // 1.获取将要删除的图片的临时路径
          const filePaht = file.response.data.tmp_path
          // 2.从 pics 数组中,找到这个图片对应的索引值
          const i = this.addForm.pics.findIndex(x => x.pic === filePaht) // x 代表数组的每一项
          // 3.调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除
          this.addForm.pics.splice(i, 1)
          console.log(this.addForm)
    },

    21、实现图片的预览效果

    添加代码:

    <!--图片预览对话框-->
    <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%" >
        <!--内容主体区域-->
        <img width="100%" :src="picUrl" alt="">
    </el-dialog>
    
    <script>
    export default {
      data() {
        return {
          picUrl: '', // 图片预览路径
          previewVisible: false // 控制图片预览对话框是否显示
        }
      },
      methods: {
        // 处理图片预览效果
        handlePreview(file) {
          console.log(file)
          this.picUrl = file.response.data.url
          this.previewVisible = true
        },
      }
    }
    </script>

    现在上传图片后,点击图片名称,就可以实现预览了。

    22、安装并配置vue-quill-editor

    Vue-Quill-Editor:

    基于 Quill、适用于 Vue 的富文本编辑器,支持服务端渲染和单页应用。 

    打开可视化工具,在左侧菜单点击依赖,然后点击安装依赖,在运行依赖里面搜索vue-quill-editor,进行安装。

    在全局注册为全局可用的组件,在main.js文件里添加:

    // 导入富文本编辑器
    import VueQuillEditor from 'vue-quill-editor'
    // 导入富文本编辑器对应的样式
    import 'quill/dist/quill.core.css'
    import 'quill/dist/quill.snow.css'
    import 'quill/dist/quill.bubble.css'
    // 将富文本编辑器注册为全局可用的组件
    Vue.use(VueQuillEditor)

    回到Add.vue文件中添加代码:

    <el-tab-pane label="商品内容" name="4">
            <!--富文本编辑器组件-->
            <quill-editor v-model="addForm.goods_introduce"></quill-editor>
    </el-tab-pane>
    
    <script>
    export default {
      data() {
        return {
          // 添加商品的表单数据对象
          addForm: {
            goods_name: '',
            goods_price: 0,
            goods_weight: 0,
            goods_number: 0,
            goods_cat: [], // 商品所属的分类数据
            pics: [], // 图片的数组
            goods_introduce: '' // 商品的详情描述
          },
        }
      }
    }
    </script>

    需要在全局样式golbal.css中指定富文本编辑器的最小高度:

    .ql-editor{min-height: 300px;}

    然后在编辑器下面还需要一个添加商品的按钮:

    <el-tab-pane label="商品内容" name="4">
            <!--富文本编辑器组件-->
            <quill-editor v-model="addForm.goods_introduce"></quill-editor>
            <!--添加商品按钮-->
            <el-button type="primary" @click="addGoods" class="btnAdd">添加商品</el-button>
    </el-tab-pane>
    
    <style lang="less" scoped>
    .btnAdd{margin-top: 15px;}
    </style>

    此时效果图:

    23、实现表单数据的预验证

    给添加商品按钮添加点击事件:

    <!--添加商品按钮-->
    <el-button type="primary" @click="addGoods" class="btnAdd">添加商品</el-button>

    addGoods函数:

    // 点击按钮,添加商品
    addGoods() {this.$refs.addFormRef.validate(async valid => {
            // 预校验
            if (!valid) {
              return this.$message.error('请填写必要的表单项')
            }
            // 执行添加的业务逻辑
          })
    }

    24、把goods_cat从数组转换为字符串

    添加商品需要调用api的添加商品接口,请求路径:goods,请求方法:post
    请求参数:
    goods_name  | 商品名称 | 不能为空 |
    goods_cat  | 以为','分割的分类列表 | 不能为空 |
    goods_price      | 价格 | 不能为空 |
    goods_number     | 数量 | 不能为空 |
    goods_weight  | 重量 | 不能为空 |
    goods_introduce  | 介绍 | 可以为空 |
    pics  | 上传的图片临时路径(对象) | 可以为空 |
    attrs   | 商品的参数(数组),包含 `动态参数` 和 `静态属性` | 可以为空 |

    因为接口文档里,goods_cat参数要以为','分割的字符串类型,我们保存的是数组类型,所以需要转换下

    // 点击按钮,添加商品
    addGoods() {
          this.$refs.addFormRef.validate(async valid => {
            // 预校验
            if (!valid) {
              return this.$message.error('请填写必要的表单项')
            }
            // 执行添加的业务逻辑
            console.log(this.addForm)
            this.addForm.goods_cat = this.addForm.goods_cat.join(',')
          })
    }

    这时已经把goods_cat的值转成了字符串类型,但是在上面级联选择器代码中,v-model="addForm.goods_cat",这里绑定的是数组类型,这里会报错。(新版没报错)

    所以需要lodash cloneDeep(obj)

    _.cloneDeep(value) 这个方法类似 _.clone,除了它会递归拷贝 value。(也叫深拷贝)。

    还是打开可视化工具,添加运行依赖,搜索“lodash”,进行安装。

    在本页面进行导入,并添加代码:

    <script>
    import _ from 'lodash'
    
    export default {
      methods: {
        // 点击按钮,添加商品
        addGoods() {
          this.$refs.addFormRef.validate(async valid => {
            // 预校验
            if (!valid) {
              return this.$message.error('请填写必要的表单项')
            }
            // 执行添加的业务逻辑
            console.log(this.addForm)
            // this.addForm.goods_cat = this.addForm.goods_cat.join(',')
            // lodash cloneDeep(obj) 深拷贝,复制为新对象
            const form = _.cloneDeep(this.addForm)
            form.goods_cat = form.goods_cat.join(',')
            console.log(form)
          })
        }
      }
    }
    </script>

    25、处理attrs数组

    attrs       商品的参数(数组),包含 `动态参数` 和 `静态属性`

    "attrs":[
        {
          "attr_id":15,
          "attr_value":"ddd"
        },
        {
          "attr_id":15,
          "attr_value":"eee"
        }
    ]

    首先要把 attrs 添加到 addForm 对象中,然后分别循环 manyTableData 和 onlyTableData,并把他们中的每一项进行处理,最后push到attrs数组中。

    // 点击按钮,添加商品
    addGoods() {
          this.$refs.addFormRef.validate(async valid => {
            // 预校验
            if (!valid) {
              return this.$message.error('请填写必要的表单项')
            }
            // 执行添加的业务逻辑
            console.log(this.addForm)
            // this.addForm.goods_cat = this.addForm.goods_cat.join(',')
            // lodash cloneDeep(obj) 深拷贝,复制为新对象
            const form = _.cloneDeep(this.addForm)
            form.goods_cat = form.goods_cat.join(',')
            console.log(form)
            // 处理动态参数
            console.log(this.manyTableData)
            this.manyTableData.forEach(item => {
              const newInfo = { attr_id: item.attr_id, attr_value: item.attr_vals.join(' ') }
              this.addForm.attrs.push(newInfo)
            })
            // 处理静态属性
            this.onlyTableData.forEach(item => {
              this.addForm.attrs.push({ attr_id: item.attr_id, attr_value: item.attr_vals })
            })
            form.attrs = this.addForm.attrs
            console.log(form)
          })
    }

    26、完成添加商品的操作

    在上面代码下发起网络请求:

    // 点击按钮,添加商品
    addGoods() {
          this.$refs.addFormRef.validate(async valid => {
            // 预校验
            if (!valid) {
              return this.$message.error('请填写必要的表单项')
            }
            // 执行添加的业务逻辑
            console.log(this.addForm)
            // this.addForm.goods_cat = this.addForm.goods_cat.join(',')
            // lodash cloneDeep(obj) 深拷贝,复制为新对象
            const form = _.cloneDeep(this.addForm)
            form.goods_cat = form.goods_cat.join(',')
            console.log(form)
            // 处理动态参数
            console.log(this.manyTableData)
            this.manyTableData.forEach(item => {
              const newInfo = { attr_id: item.attr_id, attr_value: item.attr_vals.join(' ') }
              this.addForm.attrs.push(newInfo)
            })
            // 处理静态属性
            this.onlyTableData.forEach(item => {
              this.addForm.attrs.push({ attr_id: item.attr_id, attr_value: item.attr_vals })
            })
            form.attrs = this.addForm.attrs
            console.log(form)
            // 请求服务器接口,添加商品
            const { data: res } = await this.$http.post('goods', form)
            if (res.meta.status !== 201) {
              return this.$message.error('添加商品失败!')
            }
            this.$message.success('添加商品成功!')
            // 路由导航对象.push函数,跳转到指定的路由页面
            this.$router.push('/goods')
          })
    }

    添加成功后,跳转到商品列表页,效果图:

    27、将goods_list分支的代码合并,并推送到远程

    goods_list分支提交到本地仓库,并推送到远程:

    git branch
    git add .
    git commit -m "完成了商品功能的开发"
    git push

    合并到master,并推送到远程:

    git branch
    git checkout master
    git merge goods_list
    git push
  • 相关阅读:
    2021.4.4(每周总结)
    2021.4.2
    2021.4.1
    2021.3.31
    2021.3.30
    2021.3.29
    2021.3.28(每周总结)
    2021.3.26
    C语言中指针与取地址符&详解
    使用JDBC连接、操作数据库、实现数据处理
  • 原文地址:https://www.cnblogs.com/joe235/p/12201462.html
Copyright © 2020-2023  润新知