Vue电商项目—商品管理—商品列表模块-09

1.1 商品管理概述

商品管理模块用于维护电商平台的商品信息,
包括商品的类型、参数、图片、详情等信息。通过商品管理模块
可以实现商品的添加、修改、展示和删除等功能。

在这里插入图片描述

1.2 商品列表

 实现商品列表布局效果
 调用后台接口获取商品列表数据

      // 根据分页获取对应的商品列表数据
      async  getGoodsList() {

            // 发送请求
            const {data: res} = await this.$http.get('goods', {
               params: this.queryInfo
            });

            if(res.meta.status !== 200) 
            return this.$message.error('获取商品列表数据失败!');

            this.$message.success('获取商品列表数据成功!');
            this.goodslist = res.data.goods;
            this.total = res.data.total;

            // console.log(this.goodslist);
            // console.log(this.total);
        }

1.3 添加商品

1. 基本布局与分布条效果

 添加商品基本布局
 分布条组件用法

在这里插入图片描述

<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="添加商品信息" show-icon center :closable='false'></el-alert>

           <!-- 步骤条 -->
           <!-- active 激活项索引 Number -->
           <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center>
                <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>


            <!-- 添加表单 -->
            <!-- label-position label标签位置 -->
            <el-form :model="addForm" :rules="addRules" ref="addRef" label-width="100px" label-position='top'>
                <!-- 侧边导航栏 -->
                <!-- v-model 绑定值,选中选项卡的name String -->
                 <!-- before-leave 切换标签之前的钩子,
                 若返回 false 或者返回 Promise 且被 reject,则阻止切换。 类型:函数 -->
                <el-tabs v-model="activeIndex"  tab-position="left" :before-leave='beforeTabLeave' @tab-click='tabClicked'> 
                    <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-form-item label="商品分类" prop="goods_cat">
                            <!-- 商品分类级联选择器 -->
                            <el-cascader
                                expand-trigger="hover"
                                :options="cateList"
                                :props="catePropos"
                                v-model="addForm.goods_cat"
                                @change="handleChange" >
                            </el-cascader>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品参数" name="1">
                        <el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id">
                            <!-- 复选框组 -->
                             <el-checkbox-group v-model="item.attr_vals">
                                <el-checkbox :label="cb" v-for='(cb,i) in item.attr_vals' :key="i" border></el-checkbox>
                            </el-checkbox-group>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品属性" name="2">
                        <el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id">
                            <el-input v-model="item.attr_vals"></el-input>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品图片" name="3">
                        <!-- 上传商品图片区域 -->
                        <!-- action 文件上传路径 -->
                        <!-- on-preview 指令预览事件 -->
                        <!-- on-remove  文件列表移除文件时的钩子-->
                        <!-- headers 设置上传的请求头部 object -->
                        <!-- on-success 文件上传成功时的钩子 -->
                        <!-- list-type 文件呈现方式  -->
                        <el-upload
                        :action="uploadURL"
                        :on-preview="handlePreview"
                        :on-remove="handleRemove"
                        :headers="headerObj"
                        :on-success='hadleSuccess'
                        list-type="picture">
                        <el-button size="small" type="primary">点击上传</el-button>
                        </el-upload>
                    </el-tab-pane>
                    <el-tab-pane label="商品内容" name="4">
                        <!-- 富文本编辑器组件 -->
                        <quill-editor v-model="addForm.goods_introduce"></quill-editor>
                        <!-- 添加商品按钮 -->
                        <el-button type="primary" @click="add">添加商品</el-button>
                    </el-tab-pane>
                </el-tabs>
            </el-form>
        </el-card>

        <!-- 图片预览 -->
        <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%">
            <img :src="previewPath" class="previewImg">
        </el-dialog>
    </div>

2. 商品信息选项卡Tab布局效果

Tab 组件的基本使用

<el-tabs tab-position="left" v-model="activeName" :before-leave="beforeTabLeave">
 <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>

3. Steps 步骤条 与 Tabs 标签的联动效果

 // 设置当前激活步骤
 activeIndex: '0'
 <!-- active 激活项索引 Number -->
 <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center>
     <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>
                <!-- 侧边导航栏 -->
                <!-- v-model 绑定值,选中选项卡的name String -->
                 <!-- before-leave 切换标签之前的钩子,
                 若返回 false 或者返回 Promise 且被 reject,则阻止切换。 类型:函数 -->
                <el-tabs v-model="activeIndex"  tab-position="left" :before-leave='beforeTabLeave' @tab-click='tabClicked'> 
                    <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-form-item label="商品分类" prop="goods_cat">
                            <!-- 商品分类级联选择器 -->
                            <el-cascader
                                expand-trigger="hover"
                                :options="cateList"
                                :props="catePropos"
                                v-model="addForm.goods_cat"
                                @change="handleChange" >
                            </el-cascader>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品参数" name="1">
                        <el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id">
                            <!-- 复选框组 -->
                             <el-checkbox-group v-model="item.attr_vals">
                                <el-checkbox :label="cb" v-for='(cb,i) in item.attr_vals' :key="i" border></el-checkbox>
                            </el-checkbox-group>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品属性" name="2">
                        <el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id">
                            <el-input v-model="item.attr_vals"></el-input>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品图片" name="3">
                        <!-- 上传商品图片区域 -->
                        <!-- action 文件上传路径 -->
                        <!-- on-preview 指令预览事件 -->
                        <!-- on-remove  文件列表移除文件时的钩子-->
                        <!-- headers 设置上传的请求头部 object -->
                        <!-- on-success 文件上传成功时的钩子 -->
                        <!-- list-type 文件呈现方式  -->
                        <el-upload
                        :action="uploadURL"
                        :on-preview="handlePreview"
                        :on-remove="handleRemove"
                        :headers="headerObj"
                        :on-success='hadleSuccess'
                        list-type="picture">
                        <el-button size="small" type="primary">点击上传</el-button>
                        </el-upload>
                    </el-tab-pane>
                    <el-tab-pane label="商品内容" name="4">
                        <!-- 富文本编辑器组件 -->
                        <quill-editor v-model="addForm.goods_introduce"></quill-editor>
                        <!-- 添加商品按钮 -->
                        <el-button type="primary" @click="add">添加商品</el-button>
                    </el-tab-pane>
                </el-tabs>

4. 商品分类信息

 商品分类布局
 商品分类数据加载

<!-- 商品分类级联选择器 -->
 <el-cascader 
             expand-trigger="hover" 
             :options="cateList" 
             :props="catePropos" 
             v-model="addForm.goods_cat" 
             @change="handleChange" >2
</el-cascader>
       // 获取商品分类数据
       async getCateList() {

            // 发送请求
            const {data: res} = await this.$http.get('categories');

            if(res.meta.status !== 200) return this.$message.error('获取商品分类数据失败!');

            this.cateList = res.data;

            console.log(this.cateList);
        }

5. 未选择任何商品分类,阻止tabs导航栏的切换

<!-- 侧边导航栏 -->
<!-- v-model 绑定值,选中选项卡的name String -->
<!-- before-leave 切换标签之前的钩子, 若返回 false 或者返回 Promise 且被 reject,则阻止切换。 类型:函数 -->
<el-tabs 
        v-model="activeIndex" 
        tab-position="left" 
        :before-leave='beforeTabLeave'
        @tab-click='tabClicked'> 
</el-tabs>

在这里插入图片描述

       // 切换标签之前的钩子
       // activeName 即将进入的标签页名称
       // oldActiveName 即将离开的标签页名称
        beforeTabLeave(activeName, oldActiveName) {
        
            // 即将离开的,是否为基本信息页,且根据选择商品分类的数组长度,判断是否选中了三级分类
            if(oldActiveName === '0' && this.addForm.goods_cat.length !== 3)
            {
                this.$message.error('请先选择商品分类!');
                // 只要返回 false,就会阻止标签页的切换
                return false
            }
        }

6. 商品动态参数

 获取商品动态参数数据
 商品动态参数布局

在这里插入图片描述

      // 侧边栏tab点击事件
       async tabClicked() {
            
            // 证明访问的是动态参数面板
            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('获取动态参数列表失败!');
                
                // 将动态参数中的 attr_vals,转换为 数组,供 多选框组 使用
              res.data.forEach(item => {
                  item.attr_vals = item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ');
              });

                this.manyTableData = res.data;

                // console.log(this.manyTableData);
            }

        }

7. 商品静态属性

 获取商品静态属性数据
 商品静态属性布局

在这里插入图片描述

       // 侧边栏tab点击事件
       async tabClicked() {
            
            // 证明访问的是动态参数面板
            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('获取动态参数列表失败!');
                
                // 将动态参数中的 attr_vals,转换为 数组,供 多选框组 使用
              res.data.forEach(item => {
                  item.attr_vals = item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ');
              });

                this.manyTableData = res.data;

                // console.log(this.manyTableData);
            }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('获取静态属性列表失败!');

                this.onlyTableData = res.data;

                // console.log(this.onlyTableData);
            } 

        }

8. 商品图片上传

图片上传组件基本使用

<!-- 上传商品图片区域 -->
<!-- action 文件上传路径 -->
<!-- on-preview 指令预览事件 -->
<!-- on-remove  文件列表移除文件时的钩子-->
<!-- headers 设置上传的请求头部 object -->
<!-- on-success 文件上传成功时的钩子 -->
<!-- list-type 文件呈现方式  -->
<el-upload
          :action="uploadURL"
          :on-preview="handlePreview"
          :on-remove="handleRemove"
          :headers="headerObj"
          :on-success='hadleSuccess'
          list-type="picture">
    <el-button size="small" type="primary">点击上传</el-button>
</el-upload>

main.js入口文件中,设置了 自定义属性的请求头,
而图片上传有自带的一套发送请求的方式,
因为请求头缺少 Authorization属性,导致上传不成功

在这里插入图片描述

// 导入axios
import axios from 'axios'
// 配置请求的根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/';
// 通过axios请求拦截器添加token,保证拥有获取数据的权限
// axios请求拦截
// 在 request 拦截器中,展示进度条 NProgress.start()
axios.interceptors.request.use(config => {

    // 显示进度条
    NProgress.start();

    // 为请求头对象,添加token 验证的Authorization字段
    config.headers.Authorization = window.sessionStorage.getItem('token');

    return config;
});
            // 上传图片的URL地址
            uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
            // 因为upload组件有自己的一套Ajax,不走我们axios设置的
            // 设置图片上传组件的headers请求头对象
            headerObj: {
                Authorization: window.sessionStorage.getItem('token')
            },
            // 预览图片展示路径
            previewPath: '',
            // 控制图片预览对话框的显示与隐藏
            previewVisible: false

在这里插入图片描述

// 添加商品的表单数据对象
addForm: {
           goods_name: '',
           goods_price: 0,
           goods_weight: 0,
           goods_number: 0,
           goods_cat: [],
           // 图片路径的数组
           pics: [],
           // 商品介绍
           goods_introduce: '',
           // 商品参数,包括 动态参数manyTableData 和 静态属性onlyTableData
           attrs: []
          }

在这里插入图片描述

        // 监听图片上传成功事件
        hadleSuccess(response) {
            // console.log(response);

            // 1、将虚拟路径,拼接得到一个图片信息对象
            const picInco = {pic: response.data.tmp_path}

            // 2、将图片信息对象,push 到 pics 数组中
            this.addForm.pics.push(picInco);

            // console.log(this.addForm);
        }

图片预览

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

        // 处理图片预览效果
        handlePreview(file) {

            // 保存图片预览的真实路径
           this.previewPath = file.response.data.url;

            // 显示图片预览对话框
           this.previewVisible = true;
        }

图片删除

        // 图片移除操作
        handleRemove(file) {
            
            // 1、获取将要删除的图片的临时路径
            const filePath = file.response.data.tmp_path;
           
            // 2、从 pics 数组中,找到这个图片对应的索引值
            const index = this.addForm.pics.findIndex(x => x.pic === filePath);

            // 3、调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除
            this.addForm.pics.splice(index, 1);

            console.log(this.addForm);
        }

9. 商品详情

富文本编辑器基本使用

在这里插入图片描述

在main.js 入口文件中

// 导入富文本编辑器
import VueQuillEditor from 'vue-quill-editor'
//  require styles 导入富文本编辑器对应的样式
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme

// 将富文本编辑器,注册为全局可用的组件
Vue.use(VueQuillEditor);

使用

<!-- 富文本编辑器组件 -->
<quill-editor v-model="addForm.goods_introduce"></quill-editor>

10. 添加商品

注意点一: 级联选择器v-model数据绑定值goods_cat,必须是 数组

 // 添加商品的表单数据对象
addForm: {
           goods_name: '',
           goods_price: 0,
           goods_weight: 0,
           goods_number: 0,
           goods_cat: [],
           // 图片路径的数组
           pics: [],
           // 商品介绍
           goods_introduce: '',
           // 商品参数,包括 动态参数manyTableData 和 静态属性onlyTableData
           attrs: []
         }
 <el-form-item label="商品分类" prop="goods_cat">
     <!-- 商品分类级联选择器 -->
     <el-cascader
                 expand-trigger="hover"
                 :options="cateList"
                 :props="catePropos"
                 v-model="addForm.goods_cat"
                 @change="handleChange" >
     </el-cascader>
 </el-form-item>

注意点二:而提交表单参数的 goods_cat值,是 字符串

在这里插入图片描述

1.请求参数中,商品分类参数处理

如果在 添加商品时,将 goods_cat转换成 字符串,会导致页面报错,
因为, 级联选择器v-model数据绑定值goods_cat,必须是 数组

在这里插入图片描述
在这里插入图片描述

解决方案: 通过lodash.cloneDeep()方法,将对象进行 深拷贝

在这里插入图片描述

// 导入lodash,用于深拷贝
// 官方提倡命名为 _
import _ from 'lodash'
// 验证通过
// this.addForm.goods_cat 供级联选择器使用 类型必须是数组
// 提交参数中的 goods_cat,必须是字符串
// 通过深拷贝,解决这个问题
const form = _.cloneDeep(this.addForm);
form.goods_cat = form.goods_cat.join(',');
2. 请求参数中,商品参数处理
 // 添加商品的表单数据对象
addForm: {
           goods_name: '',
           goods_price: 0,
           goods_weight: 0,
           goods_number: 0,
           goods_cat: [],
           // 图片路径的数组
           pics: [],
           // 商品介绍
           goods_introduce: '',
           // 商品参数,包括 动态参数manyTableData 和 静态属性onlyTableData
           attrs: []
         }
// 动态参数列表数据
manyTableData: [],
// 静态属性列表数据
onlyTableData: []

在这里插入图片描述

在这里插入图片描述

动态参数列表中的 atrr_vals 是数组

在这里插入图片描述

            // 处理动态参数
            this.manyTableData.forEach(item => {
                const newInfo = {
                    attr_id: item.attr_id,
                    attr_vals: item.attr_vals.join(' ')
                }
                this.addForm.attrs.push(newInfo);
            });

             // 处理静态属性
            this.onlyTableData.forEach(item => {
                const newInfo = {
                    attr_id: item.attr_id,
                    attr_vals: item.attr_vals
                }
                this.addForm.attrs.push(newInfo);
            });
			
			// 将处理好的addForm.attrs,赋值给 深拷贝的 form对象
            form.attrs = this.addForm.attrs;

发送请求

            // 发送请求
            // 商品的名称,必须是唯一的
            const {data: res} = await this.$http.post('goods', form);

            if(res.meta.status !== 201) {
                return this.$message.error('添加商品失败!');
            }

            this.$message.success('添加商品成功!');
            // 使用路由导航跳转到,商品列表页面
            this.$router.push('/goods');
           });

该模块的完整代码

<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="添加商品信息" show-icon center :closable='false'></el-alert>

           <!-- 步骤条 -->
           <!-- active 激活项索引 Number -->
           <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center>
                <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>


            <!-- 添加表单 -->
            <!-- label-position label标签位置 -->
            <el-form :model="addForm" :rules="addRules" ref="addRef" label-width="100px" label-position='top'>
                <!-- 侧边导航栏 -->
                <!-- v-model 绑定值,选中选项卡的name String -->
                 <!-- before-leave 切换标签之前的钩子,
                 若返回 false 或者返回 Promise 且被 reject,则阻止切换。 类型:函数 -->
                <el-tabs v-model="activeIndex"  tab-position="left" :before-leave='beforeTabLeave' @tab-click='tabClicked'> 
                    <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-form-item label="商品分类" prop="goods_cat">
                            <!-- 商品分类级联选择器 -->
                            <el-cascader
                                expand-trigger="hover"
                                :options="cateList"
                                :props="catePropos"
                                v-model="addForm.goods_cat"
                                @change="handleChange" >
                            </el-cascader>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品参数" name="1">
                        <el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id">
                            <!-- 复选框组 -->
                             <el-checkbox-group v-model="item.attr_vals">
                                <el-checkbox :label="cb" v-for='(cb,i) in item.attr_vals' :key="i" border></el-checkbox>
                            </el-checkbox-group>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品属性" name="2">
                        <el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id">
                            <el-input v-model="item.attr_vals"></el-input>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品图片" name="3">
                        <!-- 上传商品图片区域 -->
                        <!-- action 文件上传路径 -->
                        <!-- on-preview 指令预览事件 -->
                        <!-- on-remove  文件列表移除文件时的钩子-->
                        <!-- headers 设置上传的请求头部 object -->
                        <!-- on-success 文件上传成功时的钩子 -->
                        <!-- list-type 文件呈现方式  -->
                        <el-upload
                        :action="uploadURL"
                        :on-preview="handlePreview"
                        :on-remove="handleRemove"
                        :headers="headerObj"
                        :on-success='hadleSuccess'
                        list-type="picture">
                        <el-button size="small" type="primary">点击上传</el-button>
                        </el-upload>
                    </el-tab-pane>
                    <el-tab-pane label="商品内容" name="4">
                        <!-- 富文本编辑器组件 -->
                        <quill-editor v-model="addForm.goods_introduce"></quill-editor>
                        <!-- 添加商品按钮 -->
                        <el-button type="primary" @click="add">添加商品</el-button>
                    </el-tab-pane>
                </el-tabs>
            </el-form>
        </el-card>

        <!-- 图片预览 -->
        <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%">
            <img :src="previewPath" class="previewImg">
        </el-dialog>
    </div>
</template>

<script>
// 导入lodash,用于深拷贝
import _ from 'lodash'
export default {
    data() {
        return {
            // 设置当前激活步骤
            activeIndex: '0',
            // 添加商品的表单数据对象
            addForm: {
                goods_name: '',
                goods_price: 0,
                goods_weight: 0,
                goods_number: 0,
                goods_cat: [],
                // 图片路径的数组
                pics: [],
                // 商品介绍
                goods_introduce: '',
                // 商品参数,包括 动态参数manyTableData 和 静态属性onlyTableData
                attrs: []
            },
            // 添加商品的表单数据验证规则
            addRules: {
                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: [],
            // 配置级联选择器 
            catePropos: {
                label: 'cat_name',
                value: 'cat_id',
                children: 'children'
            },
            // 动态参数列表数据
            manyTableData: [],
            // 静态属性列表数据
            onlyTableData: [],
            // 上传图片的URL地址
            uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
            // 因为upload组件有自己的一套Ajax,不走我们axios设置的
            // 设置图片上传组件的headers请求头对象
            headerObj: {
                Authorization: window.sessionStorage.getItem('token')
            },
            // 预览图片展示路径
            previewPath: '',
            // 控制图片预览对话框的显示与隐藏
            previewVisible: false
           
        }
    },
    created() {
         // 获取商品分类数据
        this.getCateList();
    },
    computed: {
        cateId() {
            if(this.addForm.goods_cat.length === 3) {
                return this.addForm.goods_cat[2];
            }
            return null;
        }
    },
    methods: {
        // 获取商品分类数据
       async getCateList() {

            // 发送请求
            const {data: res} = await this.$http.get('categories');

            if(res.meta.status !== 200) return this.$message.error('获取商品分类数据失败!');

            this.cateList = res.data;

            console.log(this.cateList);
        },
        // 级联选择器选中项变化,触发函数
        handleChange() {

            // 限制级联选择器,只能选中最后一项
            if(this.addForm.goods_cat.length !== 3) {
                this.addForm.goods_cat = [];
            }
            // console.log(this.addForm.goods_cat);
        },
        // 切换标签之前的钩子
        beforeTabLeave(activeName, oldActiveName) {
            
            if(oldActiveName === '0' && this.addForm.goods_cat.length !== 3)
            {
                this.$message.error('请先选择商品分类!');
                return false
            }
        },
        // 侧边栏tab点击事件
       async tabClicked() {
            
            // 证明访问的是动态参数面板
            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('获取动态参数列表失败!');
                
                // 将静态参数中的 attr_vals,转换为 数组,供 多选框组 使用
              res.data.forEach(item => {
                  item.attr_vals = item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ');
              });

                this.manyTableData = res.data;

                // console.log(this.manyTableData);
            }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('获取静态属性列表失败!');

                this.onlyTableData = res.data;

                // console.log(this.onlyTableData);
            } 

        },
        // 处理图片预览效果
        handlePreview(file) {

            // 保存图片预览的真实路径
           this.previewPath = file.response.data.url;

            // 显示图片预览对话框
           this.previewVisible = true;
        },
        // 图片移除操作
        handleRemove(file) {
            
            // 1、获取将要删除的图片的临时路径
            const filePath = file.response.data.tmp_path;
           
            // 2、从 pics 数组中,找到这个图片对应的索引值
            const index = this.addForm.pics.findIndex(x => x.pic === filePath);

            // 3、调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除
            this.addForm.pics.splice(index, 1);

            console.log(this.addForm);
        },
        // 监听图片上传成功事件
        hadleSuccess(response) {
            // console.log(response);

            // 1、将虚拟路径,拼接得到一个图片信息对象
            const picInco = {pic: response.data.tmp_path}

            // 2、将图片信息对象,push 到 pics 数组中
            this.addForm.pics.push(picInco);

            // console.log(this.addForm);
        },
        // 添加商品点击事件
        add() {
            // 表单预验证
           this.$refs.addRef.validate( async valid => {

            // 验证失败
            if(!valid) return this.$message.error('请填写必要的表单项!');

            // 验证通过
            // this.addForm.goods_cat 供级联选择器使用 类型必须是数组
            // 提交参数中的 goods_cat,必须是字符串
            // 通过深拷贝,解决这个问题
           const form = _.cloneDeep(this.addForm);
           form.goods_cat = form.goods_cat.join(',');

            // 处理动态参数
            this.manyTableData.forEach(item => {
                const newInfo = {
                    attr_id: item.attr_id,
                    attr_vals: item.attr_vals.join(' ')
                }
                this.addForm.attrs.push(newInfo);
            });

             // 处理静态属性
            this.onlyTableData.forEach(item => {
                const newInfo = {
                    attr_id: item.attr_id,
                    attr_vals: item.attr_vals
                }
                this.addForm.attrs.push(newInfo);
            });

            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('添加商品成功!');
            // 使用路由导航跳转到,商品列表页面
            this.$router.push('/goods');
           });
        }
    }
}
</script>

<style lang="less" scoped>
.el-steps {
    margin: 15px 0;
}
.el-checkbox {
    margin: 0 10px 0 0!important;
}
// 预览的图片
.previewImg {
    width: 100%;
}
.quill-editor {
    margin-bottom: 10px;
}
</style>
Logo

前往低代码交流专区

更多推荐