vue.js从入门到深入再到随心而用————vue电商项目006(商品管理模块)

商品管理模块其实相对来说是比较复杂的一个模块,在商品列表页面的显示页面上,有一个添加商品的功能,添加商品这个功能在实际的开发过程中直接写成了一个新的组件进行路由。所以对于商品管理模块我们分为两部分完成

1.商品列表

1.1商品列表的初始化

在goods文件夹下创建List.vue组件,添加相应的路由规则

打开List.vue组件,添加下列代码

<template>
    <div>
        <h3>商品列表</h3>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <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-row :gutter="20">
                <el-col :span="8">
                    <el-input placeholder="请输入内容">
                        <el-button slot="append" icon="el-icon-search"></el-button>
                    </el-input>
                </el-col>
                <el-col :span="4">
                    <el-button type="primary">添加商品</el-button>
                </el-col>
            </el-row>
        </el-card>
    </div>
</template>

<script>
export default {
  data() {
    return {}
  },
  created() {},
  methods: {}
}
</script>

<style lang="less" scoped>
</style>

1.2数据展示

关于过滤器如何使用欢迎大家看我的另外一篇介绍:

点击跳转了解过滤器的用法

添加数据表格展示数据以及分页功能的实现,搜索功能的实现
在main.js中添加过滤器:

Vue.filter('dateFormat',function(originVal){
	const dt=new Date(originVal)
	
	const y=dt.getFullYear()
	const m=(dt.getMonth()+1+'').padStart(2,'0')
	
	const d=(dt.getDate()+'').padStart(2,'0')
	
	const hh=(dt.getHours()+'').padStart(2,'0')
	
	const mm=(dt.getMinutes()+'').padStart(2,'0')
	
	const ss=(dt.getSeconds()+'').padStart(2,'0')
	
	return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})
<!-- 卡片视图区域 -->
<el-card>
    <!-- 搜索栏 -->
    <el-row :gutter="20">
        <el-col :span="8">
            <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getGoodsList">
                <el-button slot="append" icon="el-icon-search" @click="getGoodsList"></el-button>
            </el-input>
        </el-col>
        <el-col :span="4">
            <el-button type="primary">添加商品</el-button>
        </el-col>
    </el-row>

    <!-- 表格区域 -->
    <el-table :data="goodsList" border stripe>
        <el-table-column type="index"></el-table-column>
        <el-table-column label="商品名称" prop="goods_name"></el-table-column>
        <el-table-column label="商品价格(元)" prop="goods_price" width="95px"></el-table-column>
        <el-table-column label="商品重量" prop="goods_weight" width="95px"></el-table-column>
        <el-table-column label="创建时间" prop="add_time" width="140px">
            <template slot-scope="scope">
                {{scope.row.add_time | dateFormat}}
            </template>
        </el-table-column>
        <el-table-column label="操作" width="125px">
            <template slot-scope="scope">
                <el-button size="mini" type="primary" icon="el-icon-edit"></el-button>
                <el-button size="mini" type="danger" icon="el-icon-delete"></el-button>
            </template>
        </el-table-column>
    </el-table>

    <!-- 分页 -->
    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
    </el-pagination>
</el-card>

//绑定数据以及添加方法
<script>
export default {
  data() {
    return {
      //查询参数
      queryInfo: {
        query: '',
        pagenum: 1,
        pagesize: 10
      },
      //保存商品列表信息
      goodsList: [],
      //总数据条数
      total: 0
    }
  },
  created() {
    this.getGoodsList()
  },
  methods: {
    async getGoodsList() {
      //   根据分页获取对应的商品列表
      const { data: res } = await this.$http.get('goods', {
        params: this.queryInfo
      })

      if (res.meta.status !== 200) {
        return this.$message.error('获取商品列表失败')
      }
      console.log(res.data)
      this.$message.success('获取商品列表成功')
      this.goodsList = res.data.goods
      this.total = res.data.total
    },
    handleSizeChange(newSize){
        //当页号发生改变时,更改pagesize,重新请求
        this.queryInfo.pagesize = newSize
        this.getGoodsList();
    },
    handleCurrentChange(newPage){
        //当页码发生改变时,更改pagesize,重新请求
        this.queryInfo.pagenum = newPage
        this.getGoodsList();
    }
  }
}
</script>

1.3实现删除商品

//绑定按钮点击事件
<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeGoods(scope.row.goods_id)"></el-button>

//事件函数代码编写
async removeGoods(goods_id) {
  //根据id删除对应的参数或属性
  //弹窗提示用户是否要删除
  const confirmResult = await this.$confirm(
    '请问是否要删除该商品',
    '删除提示',
    {
      confirmButtonText: '确认删除',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).catch(err => err)
  //如果用户点击确认,则confirmResult 为'confirm'
  //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel'
  if (confirmResult != 'confirm') {
    return this.$message.info('已经取消删除')
  }

  //没有取消就是要删除,发送请求完成删除
  const {data:res} = await this.$http.delete(`goods/${goods_id}`)

  if (res.meta.status !== 200) {
    return this.$message.error('删除商品失败')
  }

  this.$message.success('删除商品成功')
  this.getGoodsList()
}

1.4完成后的效果和代码

在这里插入图片描述
完整的代码

<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-row :gutter="20">
				<el-col :span="8">
					<el-input placeholder="请输入内容"  v-model="queryInfo.query" clearable @clear="getGoodsList">
					    <el-button slot="append" icon="el-icon-search" @click="getGoodsList"></el-button>
					  </el-input>
				</el-col>
				<el-col :span="4">
					<el-button type="primary" @click="goAddpage">添加商品</el-button>
				</el-col>
			</el-row>
			
			<!-- table表格区 -->
			<el-table :data="goodsList" border stripe>
				<el-table-column type="index" label="#"></el-table-column>
				<el-table-column  label="商品名称" prop="goods_name"></el-table-column>
				<el-table-column width="95px"  label="商品价格(元)" prop="goods_price"></el-table-column>
				<el-table-column  width="70px" label="商品重量" prop="goods_weight"></el-table-column>
				<el-table-column width="140px"  label="创建时间" prop="add_time">
					<template slot-scope="scope">
						{{scope.row.add_time|dateFormat}}
					</template>
				</el-table-column>
				<el-table-column  width="130px"  label="操作" >
					<template slot-scope="scope"> 
						<el-button size="mini" type="primary" icon="el-icon-edit"></el-button>
						<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeById(scope.row.goods_id)"></el-button>
					</template>
				</el-table-column>
			</el-table>
			<!-- 分页区 -->
			<el-pagination
			      @size-change="handleSizeChange"
			      @current-change="handleCurrentChange"
			      :current-page="queryInfo.pagenum"
			      :page-sizes="[5, 10, 15, 20]"
			      :page-size="queryInfo.pagesize"
			      layout="total, sizes, prev, pager, next, jumper"
			      :total="total"
				  background>
			 </el-pagination>
		</el-card>
	</div>
</template>

<script>
	export default{
		data(){
			return{
				queryInfo:{
					query:'',
					pagenum:1,
					pagesize:10
				},
				// 商品列表
				goodsList:[],
				// 总数据条数
				total:0
			}
		},
		created(){
			this.getGoodsList()
		},
		methods:{
			//根据分页获取对应的商品列表
			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('获取商品列表成功')
				
				console.log(res.data)
				this.goodsList=res.data.goods
				this.total=res.data.total
			},
			handleSizeChange(newSize){
				this.queryInfo.pagesize=newSize
				this.getGoodsList()
			},
			handleCurrentChange(newPage){
				this.queryInfo.pagenum=newPage
				this.getGoodsList()
			},
			 async removeById(id){
				const confir=await this.$confirm('此操作将永久删除该商品, 是否继续?', '提示', {
				          confirmButtonText: '确定',
				          cancelButtonText: '取消',
				          type: 'warning'
				        }).catch(err=>err)
						
						if(confir!=='confirm'){
							return this.$message.info('已取消删除!')
						}
						
						const {data:res}=await this.$http.delete(`goods/${id}`)
						if(res.meta.status!==200){
							return this.$message.error('删除失败!')
						}
						
						this.$message.success('删除成功!')
						
						this.getGoodsList()
						
			},
			//通过编程式导航进行路由
			goAddpage(){
				this.$router.push('/goods/add')
			}
			
		}
	}
</script>

<style lang="less" scoped>
</style>

2.添加商品功能

2.1添加商品功能完成后的效果

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

有如下的要求:

  • 使用el-steps组件,实现进图条的效果
  • 必须选择商品分类才能进行下一步
  • 点击tab栏切时步骤条同步
  • 满足验证所有的验证规则才能添加
  • 完成上传图片的功能
  • 导入富文本编辑器

2.2创建和布局Add.vue组件

在goods文件夹下创建Add.vue组件并且添加想关的路由规则

布局过程中需要使用Steps组件,在element.js中引入并注册该组件,并在global.css中给组件设置全局样式

import {Steps,Step} from 'element-ui'
Vue.use(Step)
Vue.use(Steps)

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

然后再在Add.vue中进行页面布局

<template>
    <div>
        <h3>添加商品</h3>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <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>

            <!-- 步骤条组件 -->
            <!-- align-center(居中效果) -->
            <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>

            <!-- tab栏区域:el-tab-pane必须是el-tabs的子节点
            :tab-position="'left'"(设置tab栏为左右结构tab栏) -->
            <!-- 表单:label-position="top"(设置label在文本框上方) -->
            <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top">
                <el-tabs v-model="activeIndex" :tab-position="'left'">
                    <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 expandTrigger='hover' v-model="addForm.goods_cat" :options="cateList" :props="cateProps" @change="handleChange" clearable></el-cascader>
                        </el-form-item>
                    </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>
        </el-card>
    </div>
</template>

<script>
export default {
  data() {
    return {
      //保存步骤条激活项索引
      activeIndex: '0',
      //添加商品的表单数据对象
      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: {
        value: 'cat_id',
        label: 'cat_name',
        children: 'children'
      }
    }
  },
  created() {
    this.getCateList()
  },
  methods: {
    async getCateList() {
      const { data: res } = await this.$http.get('categories')

      if (res.meta.status !== 200) {
        return this.$message.error('获取商品分类数据失败')
      }
      this.cateList = res.data
    },
    handleChange(){
      //如果用户选择的不是三级分类,该次选择无效,因为必须选择三级分类
      if(this.addForm.goods_cat.length !== 3){
        this.addForm.goods_cat = []
        return
      }
    }
  }
}
</script>

<style lang="less" scoped>
</style>

2.3验证是否选择了商品分类

我这里没有做其他的验证,是在店家提交时判断是否满足了验证规则,好一点的做法是只有当把基本信息全部填写完毕才能跳转到下一步

beforeTabLeave(activaName,oldActiveName){
				if(oldActiveName==='0'&&this.addForm.goods_cat.length!==3){
					this.$message.error('请选择商品分类')
					return false
				}
				
			}

2.4展示信息

展示商品参数信息,商品属性信息
在商品参数信息展示中使用的el-checkbox,el-checkbox-group组件,打开element.js引入组件并注册组件

//在用户点击tab栏时触发事件
<el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClicked">
........

//在参数信息,商品属性面板中添加循环生成结构的代码
<el-tab-pane label="商品参数" name="1">
  <!-- 渲染表单item项 -->
  <el-form-item :label="item.attr_name" :key="item.attr_id" v-for="item in manyTableData">
      <!-- 使用数组渲染复选框组 -->
      <el-checkbox-group v-model="item.attr_vals">
          <el-checkbox border :label="val" v-for="(val,i) in item.attr_vals" :key="i"></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>

//在data数据中添加保存动态参数和静态属性的数组
export default {
  data() {
    return {
      ......
      //动态参数列表
      manyTableData: [],
      //静态属性列表
      onlyTableData:[]
      }
  },methods: {
    .......
    async tabClicked() {
      //当用户点击切换tab栏时触发
      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
      } 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
      }
    }
  },
  //添加 计算属性获取三级分类
  computed: {
    cateId() {
      if (this.addForm.goods_cat.length === 3) {
        return this.addForm.goods_cat[2]
      }
      return null
    }
  }
}

2.5完成图片上传

使用upload组件完成图片上传
在element.js中引入upload组件,并注册
因为upload组件进行图片上传的时候并不是使用axios发送请求
所以,我们需要手动为上传图片的请求添加token,即为upload组件添加headers属性

//在页面中添加upload组件,并设置对应的事件和属性
<el-tab-pane label="商品图片" name="3">
  <!-- 商品图片上传
  action:指定图片上传api接口
  :on-preview : 当点击图片时会触发该事件进行预览操作,处理图片预览
  :on-remove : 当用户点击图片右上角的X号时触发执行
  :on-success:当用户点击上传图片并成功上传时触发
  list-type :设置预览图片的方式
  :headers :设置上传图片的请求头 -->
  <el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" :on-success="handleSuccess" list-type="picture" :headers="headerObj">
    <el-button size="small" type="primary">点击上传</el-button>
  </el-upload>
</el-tab-pane>
//在el-card卡片视图下面添加对话框用来预览图片
<!-- 预览图片对话框 -->
<el-dialog title="图片预览" :visible.sync="previewVisible" width="50%">
  <img :src="previewPath" class="previewImg" />
</el-dialog>

//在data中添加数据
data(){
  return {
    ......
    //添加商品的表单数据对象
    addForm: {
      goods_name: '',
      goods_price: 0,
      goods_weight: 0,
      goods_number: 0,
      goods_cat: [],
      //上传图片数组
      pics: []
    },
    //上传图片的url地址
    uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
    //图片上传组件的headers请求头对象
    headerObj: { Authorization: window.sessionStorage.getItem('token') },
    //保存预览图片的url地址
    previewPath: '',
    //控制预览图片对话框的显示和隐藏
    previewVisible:false
  }
},
//在methods中添加事件处理函数
methods:{
  .......
  handlePreview(file) {
    //当用户点击图片进行预览时执行,处理图片预览
    //形参file就是用户预览的那个文件
    this.previewPath = file.response.data.url
    //显示预览图片对话框
    this.previewVisible = true
  },
  handleRemove(file) {
    //当用户点击X号删除时执行
    //形参file就是用户点击删除的文件
    //获取用户点击删除的那个图片的临时路径
    const filePath = file.response.data.tmp_path
    //使用findIndex来查找符合条件的索引
    const index = this.addForm.pics.findIndex(item => item.pic === filePath)
    //移除索引对应的图片
    this.addForm.pics.splice(index, 1)
  },
  handleSuccess(response) {
    //当上传成功时触发执行
    //形参response就是上传成功之后服务器返回的结果
    //将服务器返回的临时路径保存到addForm表单的pics数组中
    this.addForm.pics.push({ pic: response.data.tmp_path })
  }
}

2.6使用富文本插件

想要使用富文本插件vue-quill-editor,就必须先从依赖安装该插件
引入并注册vue-quill-editor,打开main.js,编写如下代码

//导入vue-quill-editor(富文本编辑器)
import VueQuillEditor from 'vue-quill-editor'
//导入vue-quill-editor的样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
......
//全局注册组件
Vue.component('tree-table', TreeTable)
//全局注册富文本组件
Vue.use(VueQuillEditor)

使用富文本插件vue-quill-editor

<!-- 富文本编辑器组件 -->
<el-tab-pane label="商品内容" name="4">
  <!-- 富文本编辑器组件 -->
  <quill-editor v-model="addForm.goods_introduce"></quill-editor>
  <!-- 添加商品按钮 -->
  <el-button type="primary" class="btnAdd">添加商品</el-button>
</el-tab-pane>

//在数据中添加goods_introduce
//添加商品的表单数据对象
addForm: {
  goods_name: '',
  goods_price: 0,
  goods_weight: 0,
  goods_number: 0,
  goods_cat: [],
  //上传图片数组
  pics: [],
  //商品的详情介绍
  goods_introduce:''
}
//在global.css样式中添加富文本编辑器的最小高度
.ql-editor{
    min-height: 300px;
}
//给添加商品按钮添加间距
.btnAdd{
  margin-top:15px;
}

2.7添加商品

完成添加商品的操作
在添加商品之前,为了避免goods_cat数组转换字符串之后导致级联选择器报错
我们需要打开vue控制条,点击依赖,安装lodash,把addForm进行深拷贝

//打开Add.vue,导入lodash
<script>
//官方推荐将lodash导入为_
import _ from 'lodash'

//给添加商品按钮绑定点击事件
<!-- 添加商品按钮 -->
<el-button type="primary" class="btnAdd" @click="add">添加商品</el-button>
//编写点击事件完成商品添加
add(){
  this.$refs.addFormRef.validate(async valid=>{
    if(!valid) return this.$message.error("请填写必要的表单项!")

    //将addForm进行深拷贝,避免goods_cat数组转换字符串之后导致级联选择器报错
    const form = _.cloneDeep(this.addForm)
    //将goods_cat从数组转换为"1,2,3"字符串形式
    form.goods_cat = form.goods_cat.join(",")
    //处理attrs数组,数组中需要包含商品的动态参数和静态属性
    //将manyTableData(动态参数)处理添加到attrs
    this.manyTableData.forEach(item=>{
      form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals.join(" ") }) 
    })
    //将onlyTableData(静态属性)处理添加到attrs
    this.onlyTableData.forEach(item=>{
      form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals }) 
    })

    //发送请求完成商品的添加,商品名称必须是唯一的
    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>

2.8推送代码

推送goods_list分支到码云
将代码添加到暂存区: git add .
将代码提交到本地仓库: git commit -m “完成商品功能开发”
将代码推送到码云: git push
切换到master主分支: git checkout master
将goods_list分支代码合并到master: git merge goods_list
将master推送到码云: git push

2.9完整的代码

<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-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>

			<!-- tab栏区域 -->
			<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top">
				<el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClick">
					<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 v-model="addForm.goods_cat" :options="cateList" :props="cateProps" @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表示图片上传你的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>
						  <!-- <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div> -->
						</el-upload>
					</el-tab-pane>
					<el-tab-pane label="商品内容" name="4">
						<quill-editor v-model="addForm.goods_introduce">
							
						</quill-editor>
						
						<!-- 添加商品的按钮 -->
						<el-button type="primary" class="btnAdd" @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>
	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:'',
					
					attrs:[]
				},
				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',
					value: 'cat_id',
					label: 'cat_name',
					children: 'children'
				},
				//动态参数列表数组
				manyTableData:[],
				//静态属性列表数组
				onlyTableData:[],
				//上传图片的URL地址
				upLoadURL:'http://127.0.0.1:8888/api/private/v1/upload',
				//图片上传组件的headers请求头对象
				headerObj:{
					Authorization:window.sessionStorage.getItem('token')
				},
				previewPath:'',
				previewVisible:false

			} 
		},
		created() {
			this.getCateList()

		},
		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() {
				//console.log(this.addForm.goods_cat)
				if(this.addForm.goods_cat.length!==3){
					this.addForm.goods_cat=[]
				}
			},
			beforeTabLeave(activaName,oldActiveName){
				if(oldActiveName==='0'&&this.addForm.goods_cat.length!==3){
					this.$message.error('请选择商品分类')
					return false
				}
				
			},
			async tabClick(){
				//证明访问的是动态参数面板
				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)
						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('获取静态属性列表失败')
					}
					//console.log(res.data)
					this.onlyTableData=res.data
				}
				
			},
			//处理图片预览效果
			handlePreview(file){
				this.previewPath=file.response.data.url
				this.previewVisible=true
			},
			//处理移除图片的操作
			handleRemove(file){
				//1.获取将要删除图片的临时路径
				const filePath=file.response.data.tmp_path
				//2.从pics数组中,找到这个图片对应的索引值
				const i=this.addForm.pics.findIndex(x=>x.pic===filePath)
				// 3.调用数组的splice方法,把图片信息对象,从pics数组中移除
				this.addForm.pics.splice(i,1)
				console.log(this.addForm)
				
			},
			//监听图片上传成功的图片
			handleSuccess(response){
				//1.拼接得到一个图片的信息对象
				const picInfo={pic:response.data.tmp_path}
				//2.将图片信息对象push到pics数组中
				this.addForm.pics.push(picInfo)
				
				console.log(this.addForm)
			},
			add(){
				this.$refs.addFormRef.validate(async valid=>{
					if(!valid){
						return this.$message.error('请填写必要的单项!')
					}
					//执行添加的业务逻辑
					//lodash(深拷贝) cloneDeep(obj)
					const form= _.cloneDeep(this.addForm)
					form.goods_cat =form.goods_cat.join(',')
					//处理动态参数和静态属性
					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 =>{
						const newInfo = {attr_id:item.attr_id,attr_value:item.attr_vals}
						this.addForm.attrs.push(newInfo)
					})
					
					form.attrs=this.addForm.attrs
					//console.log(this.addForm)
					//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')
				})
			}
		},
		computed:{
			cateId(){
				if(this.addForm.goods_cat.length===3){
					return this.addForm.goods_cat[2]
				}
				return null
			}
		}

	}
</script>

<style lang="less" scoped>
	.el-checkbox{
		margin: 0 10px 0 0 !important;
	}
	.previewImg{
		width: 100%;
	}
	.btnAdd{
		margin-top: 15px;
	}
</style>

大家如果需要数据库和后端的接口以及有任何问题和建议欢迎大家留言,会持续更新,谢谢支持!!

原创文章 73 获赞 64 访问量 2708

猜你喜欢

转载自blog.csdn.net/qq_42147171/article/details/106124799