• Vue项目实战之电商后台管理系统(五) 商品分类模块


    前言

    一、商品列表模块

    1.1 新建商品列表组件

    在src-components-goods中新建List.vue组件,然后打开router.js添加对应的路由规则
    
    • 1

    1.2 商品列表模块效果图

    在这里插入图片描述

    1.3 商品列表模块基本布局

    <div>
    	<!-- 面包屑导航 -->
    	<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-card>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.4 获取商品列表数据并展示

    页面布局:

    <!-- 卡片视图区域 -->
    <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"></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="70px"></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="130px">
    <template slot-scope="scope">
    <el-button type="primary" icon="el-icon-edit" size="mini"></el-button>
    <el-button type="danger" icon="el-icon-delete" size="mini" @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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    绑定数据以及添加方法:

    <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('获取商品列表成功')
    			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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    1.5 实现删除商品功能

    <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeById(scope.row.goods_id)"></el-button>
    
    async removeById(id) {
    	const confirmResult = await this.$confirm(
    		'此操作将永久删除该商品, 是否继续?',
    		'提示',
    		{
    			confirmButtonText: '确定',
    			cancelButtonText: '取消',
    			type: 'warning'
    		}
    	).catch(err => err)
    	if (confirmResult !== '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()
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    1.6 添加时间格式过滤器

    在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-table-column label="创建时间" prop="add_time" width="140px">
    	<template slot-scope="scope">
    		{{scope.row.add_time | dateFormat}}
    	</template>
    </el-table-column>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    二、添加商品模块

    2.1 新建添加商品模块组件

    在src-components-goods中新建Add.vue组件,当点击商品列表模块的添加商品按钮时跳转到该模块
    所以要设置编程式导航完成跳转:
    //在List.vue中添加编程式导航
    <el-col :span="4">
    	<el-button type="primary" @click="goAddpage">添加商品</el-button>
    </el-col>
    
    goAddpage() {
    	this.$router.push('/goods/add')
    }
    
    最后在router.js中引入goods/Add.vue,并添加路由规则:
    import GoodAdd from './components/goods/Add.vue'
    
    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: GoodList  },
      { path: "/goods/add", component: GoodAdd  }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2.2 添加商品模块效果图

    在这里插入图片描述

    2.3 添加商品模块的基本布局

    <div>
    	<!-- 面包屑导航 -->
    	<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-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>
    注意:布局过程中需要使用Steps组件,在element.js中引入并注册该组件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    2.4 基本信息子标签布局及获取分类数据

    布局代码如下:

    <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="cateProps" v-model="addForm.goods_cat" @change="handleChange"></el-cascader>
    	</el-form-item>
    </el-tab-pane>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    绑定数据以及添加方法:

    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    2.5 添加tab栏切换验证

    原理是当用户点击其他tab栏时,判断是否填写了基本信息,若没填写则无法跳转到其他tab栏并提示错误
    首先给tabs添加tab切换前事件beforeTabLeave
    <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClicked">
    
    //再到methods编写事件函数beforeTabLeave
    beforeTabLeave(activeName,oldActiveName){
      //在tab栏切换之前触发,两个形参为切换前,后的tab栏name
      if(oldActiveName === '0'){
          //在第一个标签页的时候
          if(this.addForm.goods_cat.length !== 3){
              this.$message.error('请选择商品的分类')
              return false
          }else if(this.addForm.goods_name.trim() === ''){
              this.$message.error('请输入商品名称')
              return false
          }else if(this.addForm.goods_price.trim() === '0'){
              this.$message.error('请输入商品价格')
              return false
          }else if(this.addForm.goods_weight.trim() === '0'){
              this.$message.error('请输入商品重量')
              return false
          }else if(this.addForm.goods_number.trim() === '0'){
              this.$message.error('请输入商品数量')
              return false
          }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2.6 商品参数和商品属性子标签布局

    商品参数子标签可以使用el-checkbox,el-checkbox-group复选框组件,打开element.js引入组件并注册组件:

    <el-tab-pane label="商品参数" name="1">
    	<!-- 渲染表单的Item项 -->
    	<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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在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
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    2.7 商品图片子标签布局

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

    <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" list-type="picture" :headers="headerObj" :on-success="handleSuccess">
    		<el-button size="small" type="primary">点击上传</el-button>
    	</el-upload>
    </el-tab-pane>
    
    <!-- 图片预览对话框 -->
    <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%">
    	<img :src="previewPath" alt="" class="previewImg">
    </el-dialog>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在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
      }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在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 })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    总结

    今天主要完成了展示商品列表页面,添加商品两大页面的制作,代码较多,逻辑也比较复杂,值得细细品味

  • 相关阅读:
    Linux下 man命令的使用 及 中文man手册的安装
    SSM整合(二)
    若依vue ruoyi-vue ant design版本使用
    【C++】STL08关联容器-map
    Hash 哈希表和算法思路详解
    SpringBoot统一返回处理出现cannot be cast to java.lang.String异常
    指针数组、数组指针和传参的相关问题
    离线数仓(10):ODS层实现之业务数据核对
    Chaes恶意软件的新Python变种以银行和物流业为目标
    python获取线程返回值
  • 原文地址:https://blog.csdn.net/qq_40652101/article/details/126507176