day08_分类品牌管理商品规格管理商品管理

news/发布时间2024/9/20 8:13:32

文章目录

  • 1 分类品牌管理
    • 1.1 菜单添加
    • 1.2 表结构介绍
    • 1.3 页面制作
    • 1.4 品牌列表加载
      • 1.4.1 后端接口
        • BrandController
        • BrandService
        • BrandMapper
        • BrandMapper.xml
      • 1.4.2 前端对接
        • brand.js
        • categoryBrand.vue
    • 1.5 分类数据加载
    • 1.6 列表查询
      • 1.6.1 需求说明
      • 1.6.2 后端接口
        • 需求分析
        • CategoryBrandDto
        • CategoryBrand
        • CategoryBrandController
        • CategoryBrandService
        • CategoryBrandMapper
        • CategoryBrandMapper.xml
      • 1.6.3 前端对接
        • categoryBrand.js
        • categoryBrand.vue
    • 1.7 添加功能
      • 1.7.1 需求说明
      • 1.7.2 页面制作
      • 1.7.3 后端接口
        • CategoryBrandController
        • CategoryBrandService
        • CategoryBrandMapper
        • CategoryBrandMapper.xml
      • 1.7.4 前端对接
        • 实现思路
        • categoryBrand.js
        • categoryBrand.vue
    • 1.8 修改功能
      • 1.8.1 需求说明
      • 1.8.2 数据回显
      • 1.8.3 提交修改
        • 后端接口
          • CategoryBrandController
          • CategoryBrandService
          • CategoryBrandMapper
          • CategoryBrandMapper.xml
        • 前端对接
          • categoryBrand.js
          • categoryBrand.vue
    • 1.9 删除功能
      • 1.9.1 需求说明
      • 1.9.2 后端接口
        • CategoryBrandController
        • CategoryBrandService
        • CategoryBrandMapper
        • CategoryBrandMapper.xml
      • 1.9.3 前端对接
        • categoryBrand.js
        • categoryBrand.vue
  • 2 商品规格管理
    • 2.1 商品规格介绍
    • 2.2 菜单添加
    • 2.3 表结构介绍
    • 2.4 页面制作
    • 2.5 列表查询
      • 2.5.1 后端接口
        • ProductSpec
        • ProductSpecController
        • ProductSpecService
        • ProductSpecMapper
        • ProductSpecMapper.xml
      • 2.5.2 前端对接
        • productSpec.js
        • productSpec.vue
    • 2.6 添加功能
      • 2.6.1 需求说明
      • 2.6.2 页面制作
      • 2.6.3 后端接口
        • ProductSpecController
        • ProductSpecService
        • ProductSpecMapper
        • ProductSpecMapper.xml
      • 2.6.4 前端对接
        • 实现思路
        • productSpec.js
        • productSpec.vue
    • 2.7 修改功能
      • 2.7.1 需求说明
      • 2.7.2 数据回显
      • 2.7.4 后端接口
        • ProductSpecController
        • ProductSpecService
        • ProductSpecMapper
        • ProductSpecMapper.xml
      • 2.7.5 前端对接
        • productSpec.js
        • productSpec.vue
    • 2.8 删除功能
      • 2.8.1 需求说明
      • 2.8.2 后端接口
        • ProductSpecController
        • ProductSpecService
        • ProductSpecMapper
        • ProductSpecMapper.xml
      • 2.8.2 前端对接
        • productSpec.js
        • productSpec.vue
  • 3 商品管理
    • 3.1 菜单添加
    • 3.2 表结构介绍
    • 3.3 页面制作
    • 3.4 列表查询
      • 3.4.1 需求说明
      • 3.4.2 后端接口
        • 需求分析
        • ProductDto
        • Product
        • ProductController
        • ProductService
        • ProductMapper
        • ProductMapper.xml
        • ProductMapper.xml

1 分类品牌管理

分类品牌管理就是将分类的数据和品牌的数据进行关联,分类数据和品牌数据之间的关系是多对多的关系,因此需要单独使用一张数据表来存储该数据。

1.1 菜单添加

首先在系统中分类品牌管理的菜单,具体步骤如下所示:

1、在后台管理系统中通过系统管理的菜单管理添加分类品牌管理的相关菜单,如下所示:

在这里插入图片描述

2、给系统管理员角色分配分类品牌管理菜单访问权限:

在这里插入图片描述

3、在前端项目中创建对应的页面,以及配置对应的异步路由

在src/views/product的文件夹,在该文件夹中加入分类管理页面文件,如下所示

在这里插入图片描述

在src/router/modules文件夹下创建product.js路由文件,文件内容如下所示:

const Layout = () => import('@/layout/index.vue')
const category = () => import('@/views/product/category.vue')
const brand = () => import('@/views/product/brand.vue')
const categoryBrand = () => import('@/views/product/categoryBrand.vue')export default [{path: '/product',component: Layout,name: 'product',meta: {title: '商品管理',},icon: 'Histogram',children: [{path: '/category',name: 'category',component: category,meta: {title: '分类管理',},},{path: '/brand',name: 'brand',component: brand,meta: {title: '品牌管理',},},{path: '/categoryBrand',name: 'categoryBrand',component: categoryBrand,meta: {title: '分类品牌',},},],},
]

1.2 表结构介绍

分类品牌数据所对应的表结构如下所示:

CREATE TABLE `category_brand` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',`brand_id` bigint DEFAULT NULL COMMENT '品牌ID',`category_id` bigint DEFAULT NULL COMMENT '分类ID',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:不可用 1:可用)',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分类品牌';

1.3 页面制作

对比如下页面结构,使用Element Plus制作出对应的页面,数据可以暂时使用假数据。

在这里插入图片描述

该页面可以将其分为4部分:

1、搜索表单

2、添加按钮

2、数据表格

3、分页组件

代码实现如下所示:

<template><div class="search-div"><el-form label-width="70px" size="small"><el-row><el-col :span="12"><el-form-item label="品牌"><el-selectclass="m-2"placeholder="选择品牌"size="small"style="width: 100%"><el-optionv-for="item in brandList":key="item.id":label="item.name":value="item.id"/></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="分类"><el-cascader:props="categoryProps"style="width: 100%"/></el-form-item></el-col></el-row><el-row style="display:flex"><el-button type="primary" size="small">搜索</el-button><el-button size="small">重置</el-button></el-row></el-form></div><div class="tools-div"><el-button type="success" size="small">添 加</el-button></div><el-table :data="list" style="width: 100%"><el-table-column prop="categoryName" label="分类" /><el-table-column prop="brandName" label="品牌" /><el-table-column prop="logo" label="品牌图标" #default="scope"><img :src="scope.row.logo" width="50" /></el-table-column><el-table-column prop="createTime" label="创建时间" /><el-table-column label="操作" align="center" width="200" ><el-button type="primary" size="small" >修改</el-button><el-button type="danger" size="small">删除</el-button></el-table-column></el-table><el-pagination:page-sizes="[10, 20, 50, 100]"layout="total, sizes, prev, pager, next":total="total"/></template><script setup>
import { ref } from 'vue'// ================数据模型定义  start ===============================================// 定义搜索表单数据模型
const brandList = ref([{"id": 2,"createTime": "2023-05-06 09:31:19","name": "华为","logo": "http://139.198.127.41:9000/sph/20230506/华为.png"},{"id": 1,"createTime": "2023-05-06 09:30:27","name": "小米","logo": "http://139.198.127.41:9000/sph/20230506/小米.png"}
])const props = {lazy: true,value: 'id',label: 'name',leaf: 'leaf',lazyLoad(node, resolve) {   // 加载数据的方法const data = [{"id": 643,"createTime": "2023-05-22 15:31:18","name": "玩具乐器","imageUrl": "https://lilishop-oss.oss-cn-beijing.aliyuncs.com/0f423fb60f084b2caade164fae25a9a0.png","parentId": 0,"status": 1,"orderNum": 10,"hasChildren": true,"children": null},{"id": 576,"createTime": "2023-05-22 15:31:13","name": "汽车用品","imageUrl": "https://lilishop-oss.oss-cn-beijing.aliyuncs.com/665dd952b54e4911b99b5e1eba4b164f.png","parentId": 0,"status": 1,"orderNum": 10,"hasChildren": true,"children": null},]resolve(data)  // 返回数据}
};
const categoryProps = ref(props)// 定义表格数据模型
const list = ref([{"id": 2,"createTime": "2023-05-24 15:19:24","brandId": 1,"categoryId": 99,"categoryName": "定制服务","brandName": "小米","logo": "http://139.198.127.41:9000/sph/20230506/小米.png"},{"id": 1,"createTime": "2023-05-06 10:59:08","brandId": 2,"categoryId": 76,"categoryName": "UPS电源\t","brandName": "华为","logo": "http://139.198.127.41:9000/sph/20230506/华为.png"}
])// 分页条数据模型
const total = ref(0)// =========   数据模型定义 end======================================================================</script><style scoped>
.search-div {margin-bottom: 10px;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}
.tools-div {margin: 10px 0;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}
</style>

1.4 品牌列表加载

需求:当页面加载完毕以后需要请求后端接口查询出系统中所有的品牌数据,将品牌数据在搜索表单的品牌下拉框中进行展示

1.4.1 后端接口

BrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@GetMapping("/findAll")
public Result findAll() {List<Brand> list = brandService.findAll();return Result.build(list , ResultCodeEnum.SUCCESS) ;
}
BrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public List<Brand> findAll() {return brandMapper.findAll();
}
BrandMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface BrandMapper {public abstract List<Brand> findAll();
}
BrandMapper.xml

在BrandMapper.xml映射文件中添加如下的sql语句:

<select id="findAll" resultMap="brandMap">select <include refid="columns" />from brandwhere is_deleted = 0order by id desc
</select>

1.4.2 前端对接

brand.js

在src/api目录下添加brand.js文件,内容如下所示:

// 查询所有的品牌数据
export const FindAllBrand = () => {return request({url: `${api_name}/findAll`,method: 'get',})
}
categoryBrand.vue

修改categoryBrand.vue文件内容,如下所示:

<script setup>
import { ref , onMounted } from 'vue'
import { FindAllBrand } from '@/api/brand.js'// onMounted钩子函数
onMounted(() => {selectAllBrandList() // 查询所有的品牌数据
})const selectAllBrandList = async () => {const { data } = await FindAllBrand()brandList.value = data
}</script>

1.5 分类数据加载

需求:当页面加载完毕以后需要查询出系统中所对应的所有的一级分类数据,将一级分类数据在搜索表单的分类下拉框中进行展示,当用户选择某一个一级分类的时候,需要将该一级分类下所对应的二级分类查询出来…

分析:根据一个分类的id查询该分类下所对应的子分类数据该接口已经编写过了,因此只需要针对前端页面进行修改接口

代码实现:

<script setup>
import { FindCategoryByParentId } from '@/api/category.js'const props = {lazy: true,value: 'id',label: 'name',leaf: 'leaf',async lazyLoad(node, resolve) {   // 加载数据的方法if (typeof node.value == 'undefined') node.value = 0const { data } = await FindCategoryByParentId(node.value)data.forEach(function(item) {       //hasChildren判断是否有子节点item.leaf = !item.hasChildren})resolve(data)  // 返回数据  }
};const categoryProps = ref(props)
</script>    

1.6 列表查询

1.6.1 需求说明

需求说明:

1、如果在搜索表单中选择了某一个品牌以及分类,那么此时就需要按照品牌id和分类id进行查询

2、搜索的时候需要进行分页搜索

1.6.2 后端接口

需求分析

1、前端提交请求参数的时候包含了两部分的参数:搜索条件参数、分页参数。搜索条件参数可以通过?拼接到请求路径后面,分页参数【当前页码、每页显示的数据条数】可以让前端通过请求路径传递过来

2、后端查询完毕以后需要给前端返回一个分页对象,分页对象中就封装了分页相关的参数(当前页数据、总记录数、总页数…)

3、前端进行参数传递的时候,不一定会传递搜索条件,因此sql语句的编写需要使用到动态sql

CategoryBrandDto

定义一个用来封装搜索条件的实体类:

// com.atguigu.spzx.model.dto.product
@Data
public class CategoryBrandDto {private Long brandId;private Long categoryId;
}
CategoryBrand

针对当前要操作的数据库表定义一个与之对应的实体类:

// com.atguigu.spzx.model.entity.product
@Data
public class CategoryBrand extends BaseEntity {private Long brandId;private Long categoryId;// 扩展的属性用来封装前端所需要的数据private String categoryName;private String brandName;private String logo;}
CategoryBrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value = "/admin/product/categoryBrand")
public class CategoryBrandController {@Autowiredprivate CategoryBrandService categoryBrandService ;@GetMapping("{/page}/{limit}")public Result<PageInfo<CategoryBrand>> findByPage(@PathVariable Integer page, @PathVariable Integer limit, CategoryBrandDto CategoryBrandDto) {PageInfo<CategoryBrand> pageInfo = categoryBrandService.findByPage(page, limit, CategoryBrandDto);return Result.build(pageInfo , ResultCodeEnum.SUCCESS) ;}}
CategoryBrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Service
public class CategoryBrandServiceImpl implements CategoryBrandService {@Autowiredprivate CategoryBrandMapper categoryBrandMapper ;@Overridepublic PageInfo<CategoryBrand> findByPage(Integer page, Integer limit, CategoryBrandDto CategoryBrandDto) {PageHelper.startPage(page , limit) ;List<CategoryBrand> categoryBrandList = categoryBrandMapper.findByPage(CategoryBrandDto) ;return new PageInfo<>(categoryBrandList);}}
CategoryBrandMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper;
@Mapper
public interface CategoryBrandMapper {public abstract List<CategoryBrand> findByPage(CategoryBrandDto CategoryBrandDto);
}
CategoryBrandMapper.xml

在CategoryBrandMapper映射文件中添加如下sql语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.spzx.manager.mapper.CategoryBrandMapper"><resultMap id="categoryBrandMap" type="com.atguigu.spzx.model.entity.product.CategoryBrand" autoMapping="true"></resultMap><!-- 用于select查询公用抽取的列 --><sql id="columns">id,brand_id,category_id,create_time,update_time,is_deleted</sql><sql id="findPageWhere"><where><if test="brandId != null and brandId != ''">and cb.brand_id = #{brandId}</if><if test="categoryId != null and categoryId != ''">and cb.category_id = #{categoryId}</if>and cb.is_deleted = 0</where></sql><select id="findByPage" resultMap="categoryBrandMap">selectcb.id,cb.brand_id,cb.category_id,cb.create_time,cb.update_time,c.name as categoryName,b.name as brandName, b.logofrom category_brand cbleft join category c on c.id = cb.category_idleft join brand b  on b.id = cb.brand_id<include refid="findPageWhere"/>order by cb.id desc</select></mapper>

1.6.3 前端对接

categoryBrand.js

在src/api文件夹下创建categoryBrand.js文件,如下所示:

import request from '@/utils/request'const api_name = '/admin/product/categoryBrand'// 分页列表
export const GetCategoryBrandPageList = (page, limit, searchObj) => {return request({url: `${api_name}/${page}/${limit}`,method: 'get',params: searchObj,})
}
categoryBrand.vue

修改categoryBrand.vue文件中的内容,如下所示:

<div class="search-div"><el-form label-width="70px" size="small"><el-row><el-col :span="12"><el-form-item label="品牌"><el-selectclass="m-2"placeholder="选择品牌"size="small"style="width: 100%"v-model="queryDto.brandId"><el-optionv-for="item in brandList":key="item.id":label="item.name":value="item.id"/></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="分类"><el-cascader:props="categoryProps"style="width: 100%"v-model="searchCategoryIdList"/></el-form-item></el-col></el-row><el-row style="display:flex"><el-button type="primary" size="small" @click="fetchData()">搜索</el-button><el-button size="small" @click="resetData">重置</el-button></el-row></el-form>
</div><script setup>
import { GetCategoryBrandPageList } from '@/api/categoryBrand.js'// 搜索表单数据模型
const queryDto = ref({ brandId: '', categoryId: '' })
const searchCategoryIdList = ref([])//分页条数据模型
const pageParamsForm = {page: 1,   // 页码limit: 10, // 每页记录数
}
const pageParams = ref(pageParamsForm)    // onMounted钩子函数
onMounted(() => {selectAllBrandList() // 查询所有的品牌数据fetchData()
})//重置
const resetData = () => {queryDto.value = { brandId: '', categoryId: '' }fetchData()
}//页面变化
const handleSizeChange = size => {pageParams.value.limit = sizefetchData()
}
const handleCurrentChange = number => {pageParams.value.page = numberfetchData()
}// 分页列表查询
//分页列表
const fetchData = async () => {if (searchCategoryIdList.value.length == 3) {queryDto.value.categoryId = searchCategoryIdList.value[2]}const { data } = await GetCategoryBrandPageList( pageParams.value.page, pageParams.value.limit, queryDto.value)list.value = data.listtotal.value = data.total
}</script>

1.7 添加功能

1.7.1 需求说明

当用户点击添加按钮的时候,那么此时就弹出对话框,在该对话框中需要展示添加分类品牌表单。当用户在该表单中点击提交按钮的时候那么此时就需要将表单进行提交,在后端需要将提交过来的表单数据保存到数据库中即可。

如下所示:

在这里插入图片描述

1.7.2 页面制作

具体代码如下所示:

<div class="tools-div"><el-button type="success" size="small" @click="addShow">添 加</el-button>
</div><el-dialog v-model="dialogVisible" title="添加或修改" width="30%"><el-form label-width="120px"><el-form-item label="品牌"><el-selectclass="m-2"placeholder="选择品牌"size="small"><el-optionv-for="item in brandList":key="item.id":label="item.name":value="item.id"/></el-select></el-form-item><el-form-item label="分类"><el-cascader:props="categoryProps"/></el-form-item><el-form-item><el-button type="primary">提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>//进入添加
const addShow = () => {dialogVisible.value = true
}</script>

1.7.3 后端接口

CategoryBrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@PostMapping("/save")
public Result save(@RequestBody CategoryBrand categoryBrand) {categoryBrandService.save(categoryBrand);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
CategoryBrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public void save(CategoryBrand categoryBrand) {categoryBrandMapper.save(categoryBrand) ;
}
CategoryBrandMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper;
@Mapper
public interface CategoryBrandMapper {public abstract void save(CategoryBrand categoryBrand);
}
CategoryBrandMapper.xml

在CategoryBrandMapper映射文件中添加如下sql语句:

<insert id="save">insert into category_brand (id,brand_id,category_id,create_time,update_time ,is_deleted) values (#{id},#{brandId},#{categoryId},now(),now(),0)
</insert>

1.7.4 前端对接

实现思路

1、给表单绑定数据模型

2、给提交按钮绑定点击事件

3、点击按钮请求后端地址

categoryBrand.js

在src/api文件夹下创建categoryBrand.js文件,如下所示:

// 保存信息
export const SaveCategoryBrand = categoryBrand => {return request({url: `${api_name}/save`,method: 'post',data: categoryBrand,})
}
categoryBrand.vue

修改categoryBrand.vue文件中的内容,如下所示:

<el-dialog v-model="dialogVisible" title="添加或修改" width="30%"><el-form label-width="120px"><el-form-item label="品牌"><el-selectclass="m-2"placeholder="选择品牌"size="small"v-model="categoryBrand.brandId"><el-optionv-for="item in brandList":key="item.id":label="item.name":value="item.id"/></el-select></el-form-item><el-form-item label="分类"><el-cascader:props="categoryProps"v-model="categoryBrand.categoryId"/></el-form-item><el-form-item><el-button type="primary" @click="saveOrUpdate">提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>
import { SaveCategoryBrand } from '@/api/categoryBrand.js'
import { ElMessage, ElMessageBox } from 'element-plus'const defaultForm = {       //页面表单数据id: '',brandId: '',categoryId: '',
}
const categoryBrand = ref(defaultForm)    //提交保存与修改
const saveOrUpdate = () => {if (categoryBrand.value.brandId == '') {ElMessage.info('品牌信息必须选择')return}//categoryId为数组:[1,2,3]if (categoryBrand.value.categoryId.length != 3) {ElMessage.info('分类信息必须选择')return}//系统只需要三级分类idcategoryBrand.value.categoryId = categoryBrand.value.categoryId[2]if (!categoryBrand.value.id) {saveData()} 
}// 新增
const saveData = async () => {await SaveCategoryBrand(categoryBrand.value)dialogVisible.value = falseElMessage.success('操作成功')fetchData()
}
</script>

1.8 修改功能

1.8.1 需求说明

当用户点击修改按钮的时候,那么此时就弹出对话框,在该对话框中需要将当前行所对应的分类品牌数据在该表单页面进行展示。当用户在该表单中点击提交按钮的时候那么此时就需要将表单进行提交,在后端需要提交过来的表单数据修改数据库中的即可。

效果如下所示:

在这里插入图片描述

1.8.2 数据回显

分析:

1、使用添加数据的表单即可

2、要将当前操作行的数据展示在表单中,那么此时需要用到插槽

代码如下所示:

<el-table-column label="操作" align="center" width="200" ><el-button type="primary" size="small" @click="editShow(scope.row)">修改</el-button>
</el-table-column><script setup>//进入修改
const editShow = row => {// ????categoryBrand.value = rowdialogVisible.value = true
}</script>

1.8.3 提交修改

后端接口
CategoryBrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@PutMapping("updateById")
public Result updateById(@RequestBody CategoryBrand categoryBrand) {categoryBrandService.updateById(categoryBrand);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
CategoryBrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public void updateById(CategoryBrand categoryBrand) {categoryBrandMapper.updateById(categoryBrand) ;
}
CategoryBrandMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper;
@Mapper
public interface CategoryBrandMapper {public abstract void updateById(CategoryBrand categoryBrand);
}
CategoryBrandMapper.xml

在CategoryBrandMapper映射文件中添加如下sql语句:

<update id="updateById" >update category_brand set<if test="brandId != null and brandId != ''">brand_id = #{brandId},</if><if test="categoryId != null and categoryId != ''">category_id = #{categoryId},</if>update_time =  now()whereid = #{id}
</update>
前端对接
categoryBrand.js

在src/api文件夹下创建categoryBrand.js文件,如下所示:

// 修改信息
export const UpdateCategoryBrandById = categoryBrand => {return request({url: `${api_name}/updateById`,method: 'put',data: categoryBrand,})
}
categoryBrand.vue

修改categoryBrand.vue文件中的内容,如下所示:

<script setup>
import { UpdateCategoryBrandById } from '@/api/categoryBrand.js'//提交保存与修改
const saveOrUpdate = () => {...//系统只需要三级分类idcategoryBrand.value.categoryId = categoryBrand.value.categoryId[2]if (!categoryBrand.value.id) {saveData()} else {updateData() }
}// 修改
const updateData = async () => {await UpdateCategoryBrandById(categoryBrand.value)dialogVisible.value = falseElMessage.success('操作成功')fetchData() 
}
</script>

1.9 删除功能

1.9.1 需求说明

当点击删除按钮的时候此时需要弹出一个提示框,询问是否需要删除数据?如果用户点击是,那么此时向后端发送请求传递id参数,后端接收id参数进行逻辑删除。

效果如下所示:

在这里插入图片描述

1.9.2 后端接口

CategoryBrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@DeleteMapping("/deleteById/{id}")
public Result deleteById(@PathVariable Long id) {categoryBrandService.deleteById(id);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
CategoryBrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public void deleteById(Long id) {categoryBrandMapper.deleteById(id) ;
}
CategoryBrandMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper;
@Mapper
public interface CategoryBrandMapper {public abstract void deleteById(Long id);
}
CategoryBrandMapper.xml

在CategoryBrandMapper映射文件中添加如下sql语句:

<update id="deleteById">update category_brand setupdate_time = now() ,is_deleted = 1whereid = #{id}
</update>

1.9.3 前端对接

categoryBrand.js

在src/api文件夹下创建categoryBrand.js文件,如下所示:

// 根据id删除数据
export const DeleteCategoryBrandById = id => {return request({url: `${api_name}/deleteById/${id}`,method: 'delete',})
}
categoryBrand.vue

修改categoryBrand.vue文件中的内容,如下所示:

<el-table-column label="操作" align="center" width="200" #default="scope"><el-button type="danger" size="small" @click="remove(scope.row.id)">删除</el-button>
</el-table-column><script setup>
import { DeleteCategoryBrandById } from '@/api/categoryBrand.js'
import { ElMessage, ElMessageBox } from 'element-plus'//删除
const remove = async id => {ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(async () => {await DeleteCategoryBrandById(id)ElMessage.success('删除成功')fetchData()}).catch(() => {ElMessage.info('取消删除')})
}
</script>

2 商品规格管理

2.1 商品规格介绍

在电商项目中,商品规格指的是商品属性、型号、尺寸、颜色等具体描述商品特点和属性的标准化信息。

以手机为例子,它的规格可能包括以下几个方面:

1、操作系统:Android、iOS、HarmonyOS 等。

2、屏幕尺寸:5.5 吋、6.7 吋等。

3、分辨率:1920x1080、2960x1440、2532x1170 等。

4、运行内存:6GB、8GB、12GB 等。

5、存储容量:64GB、128GB、256GB 等。

6、摄像头:单摄、双摄、四摄等。

7、电池容量:3500mAh、4500mAh、5000mAh 等。

以T恤衫举例子,它的规格可能包括以下几个方面:

1、颜色:白色

2、尺码:S、M、L、XL等

3、款式:圆领、V领、印花等

2.2 菜单添加

首先在系统中添加商品规格管理的菜单,具体步骤如下所示:

1、在后台管理系统中通过系统管理的菜单管理添加商品规格管理的相关菜单,如下所示:

在这里插入图片描述

2、给系统管理员角色分配商品规格管理菜单访问权限:

在这里插入图片描述

3、在前端项目中创建对应的页面,以及配置对应的异步路由

在src/views/product的文件夹中加入商品规格管理页面文件,如下所示:

在这里插入图片描述

在src/router/modules文件夹下创建product.js路由文件,文件内容如下所示:

const Layout = () => import('@/layout/index.vue')
const category = () => import('@/views/product/category.vue')
const brand = () => import('@/views/product/brand.vue')
const categoryBrand = () => import('@/views/product/categoryBrand.vue')
const productSpec = () => import('@/views/product/productSpec.vue')export default [{path: '/product',component: Layout,name: 'product',meta: {title: '商品管理',},icon: 'Histogram',children: [{path: '/category',name: 'category',component: category,meta: {title: '分类管理',},},{path: '/brand',name: 'brand',component: brand,meta: {title: '品牌管理',},},{path: '/categoryBrand',name: 'categoryBrand',component: categoryBrand,meta: {title: '分类品牌',},},{path: '/productSpec',name: 'productSpec',component: productSpec,meta: {title: '商品规格',},},],},
]

2.3 表结构介绍

产品规格数据所对应的表结构如下所示:

CREATE TABLE `product_spec` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',`spec_name` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT '规格名称',`spec_value` text CHARACTER SET utf8mb3 COLLATE utf8_general_ci COMMENT '规格值:"[{"key":"颜色","valueList":["蓝","白","红"]]"',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:不可用 1:可用)',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品规格';

2.4 页面制作

对比如下页面结构,使用Element Plus制作出对应的页面,数据可以暂时使用假数据。

在这里插入图片描述

该页面可以将其分为3部分:

1、添加按钮

2、数据表格

3、分页条

代码实现如下所示:

<template><div class="tools-div"><el-button type="success" size="small">添 加</el-button></div><el-table :data="list" style="width: 100%"><el-table-column prop="specName" label="规格名称" /><el-table-column label="规格值" #default="scope"><divv-for="(item1, index1) in scope.row.specValue":key="index1"style="padding: 5px; margin: 0;width: 100%;">{{ item1.key }}:<spanv-for="(item2, index2) in item1.valueList":key="index2"class="div-atrr">{{ item2 }}</span></div></el-table-column><el-table-column prop="createTime" label="创建时间" /><el-table-column label="操作" align="center" width="200"><el-button type="primary" size="small" >修改</el-button><el-button type="danger" size="small">删除</el-button></el-table-column></el-table><el-pagination:page-sizes="[10, 20, 50, 100]"layout="total, sizes, prev, pager, next":total="total"/></template><script setup>
import { ref } from 'vue'// 表格数据模型
const list = ref([{"id": 2,"createTime": "2023-05-06 12:56:08","specName": "笔记本电脑","specValue": [{"key":"内存","valueList":["8G","18G","32G"]}]},{"id": 1,"createTime": "2023-05-06 12:40:22","specName": "小米手机","specValue": [{"key":"颜色","valueList":["白色","红色","黑色"]},{"key":"内存","valueList":["8G","18G"]}]}
])// 分页条数据模型
const total = ref(0)</script><style scoped>
.tools-div {margin: 10px 0;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}.div-atrr {padding: 5px 10px;margin: 0 10px;background-color: powderblue;border-radius: 10px;
}</style>

2.5 列表查询

需求说明:当产品规格管理页面加载完毕以后就向后端发送分页查询请求,后端进行分页查询,返回分页结果数据。

2.5.1 后端接口

ProductSpec

创建一个与数据库表相对应的实体类,如下所示:

// com.atguigu.spzx.model.entity.product
@Data
public class ProductSpec extends BaseEntity {private String specName;   // 规格名称private String specValue;  // 规格值}
ProductSpecController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value="/admin/product/productSpec")
public class ProductSpecController {@Autowiredprivate ProductSpecService productSpecService ;@GetMapping("/{page}/{limit}")public Result<PageInfo<ProductSpec>> findByPage( @PathVariable Integer page, @PathVariable Integer limit) {PageInfo<ProductSpec> pageInfo = productSpecService.findByPage(page, limit);return Result.build(pageInfo , ResultCodeEnum.SUCCESS) ;}}
ProductSpecService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Service
public class ProductSpecServiceImpl implements ProductSpecService {@Autowiredprivate ProductSpecMapper productSpecMapper ;@Overridepublic PageInfo<ProductSpec> findByPage(Integer page, Integer limit) {PageHelper.startPage(page , limit) ;List<ProductSpec> productSpecList = productSpecMapper.findByPage() ;return new PageInfo<>(productSpecList);}}
ProductSpecMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface ProductSpecMapper {public abstract List<ProductSpec> findByPage();
}
ProductSpecMapper.xml

在ProductSpecMapper.xml映射文件中添加如下sql语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.spzx.manager.mapper.ProductSpecMapper"><resultMap id="productSpecMap" type="com.atguigu.spzx.model.entity.product.ProductSpec" autoMapping="true"></resultMap><!-- 用于select查询公用抽取的列 --><sql id="columns">id,spec_name,spec_value,create_time,update_time,is_deleted</sql><select id="findByPage" resultMap="productSpecMap">select <include refid="columns" />from product_specwhere is_deleted = 0order by id desc</select></mapper>

2.5.2 前端对接

productSpec.js

在src/api目录下添加productSpec.js文件,内容如下所示:

import request from '@/utils/request'const api_name = '/admin/product/productSpec'// 分页列表
export const GetProductSpecPageList = (page, limit) => {return request({url: `${api_name}/${page}/${limit}`,method: 'get'})
}
productSpec.vue

修改productSpec.vue文件,内容如下所示:

<el-paginationv-model:current-page="pageParams.page"v-model:page-size="pageParams.limit":page-sizes="[10, 20, 50, 100]"layout="total, sizes, prev, pager, next":total="total"@size-change="handleSizeChange"@current-change="handleCurrentChange"
/><script setup>
import { ref , onMounted } from 'vue'
import { GetProductSpecPageList } from '@/api/productSpec.js'//分页条数据模型
const pageParamsForm = {page: 1, // 页码limit: 10, // 每页记录数
}
const pageParams = ref(pageParamsForm)// 钩子函数
onMounted(()=> {fetchData()
})//页面变化
const handleSizeChange = size => {pageParams.value.limit = sizefetchData()
}
const handleCurrentChange = number => {pageParams.value.page = numberfetchData()
}// 分页查询
const fetchData = async () => {const { data } = await GetProductSpecPageList(pageParams.value.page , pageParams.value.limit) data.list.forEach(item => {item.specValue = JSON.parse(item.specValue)       // 将规格字符串转换成规格js对象});list.value = data.listtotal.value = data.total
}</script>

2.6 添加功能

2.6.1 需求说明

用户点击添加按钮,此时需要展示一个添加数据的表单对话框,用户填写表单数据,点击提交按钮,请求后端接口完成数据的保存操作。效果如下所示:

在这里插入图片描述

2.6.2 页面制作

页面代码如下所示:

<div class="tools-div"><el-button type="success" size="small" @click="addShow">添 加</el-button>
</div><el-dialog v-model="dialogVisible" title="添加或修改" width="40%"><el-form label-width="120px"><el-form-item label="规格名称"><el-input /></el-form-item><el-form-item><el-button size="default" type="success">添加新规格</el-button></el-form-item><el-form-item label="" v-for="(item , index) in productSpec.specValue" :key="index"><el-row><el-col :span="10"><el-inputv-model="item.key"placeholder="规格"style="width: 90%;"/></el-col><el-col :span="10"><el-inputv-model="item.valueList"placeholder="规格值(如:白色,红色)"style="width: 90%;"/></el-col><el-col :span="4"><el-button size="default" type="danger">删除</el-button></el-col></el-row>  </el-form-item><el-form-item align="right"><el-button type="primary">提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>
// 定义数据模型
const dialogVisible = ref(false)// 添加表单数据模型
const defaultForm = {id: '',specName: '',specValue: [{"key": "颜色","valueList": ["白色","红色","黑色"]},{"key": "内存","valueList": ["8G","18G"]}]
}
const productSpec = ref(defaultForm)//进入添加
const addShow = () => {dialogVisible.value = true
}</script>

2.6.3 后端接口

ProductSpecController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@PostMapping("save")
public Result save(@RequestBody ProductSpec productSpec) {productSpecService.save(productSpec);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
ProductSpecService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public void save(ProductSpec productSpec) {productSpecMapper.save(productSpec) ;
}
ProductSpecMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface ProductSpecMapper {public abstract void save(ProductSpec productSpec);
}
ProductSpecMapper.xml

在ProductSpecMapper.xml映射文件中添加如下sql语句:

<insert id="save">insert into product_spec (id,spec_name,spec_value,create_time,update_time,is_deleted) values (#{id},#{specName},#{specValue},now(),now(),0)
</insert>

2.6.4 前端对接

实现思路

1、给表单绑定数据模型

2、给提交按钮绑定点击事件

3、点击按钮请求后端地址

productSpec.js

在src/api目录下添加productSpec.js文件,内容如下所示:

// 保存信息
export const SaveProductSpec = productSpec => {return request({url: `${api_name}/save`,method: 'post',data: productSpec,})
}
productSpec.vue

修改productSpec.vue文件,内容如下所示:

<el-dialog v-model="dialogVisible" title="添加或修改" width="40%"><el-form label-width="120px"><el-form-item label="规格名称"><el-input v-model="productSpec.specName"/></el-form-item><el-form-item><el-button size="default" type="success" @click="addSpec">添加新规格</el-button></el-form-item><el-form-item label="" v-for="(item , index) in productSpec.specValue" :key="index"><el-row><el-col :span="10"><el-inputv-model="item.key"placeholder="规格"style="width: 90%;"/></el-col><el-col :span="10"><el-inputv-model="item.valueList"placeholder="规格值(如:白色,红色)"style="width: 90%;"/></el-col><el-col :span="4"><el-button size="default" type="danger" @click="delSpec(index)">删除</el-button></el-col></el-row>  </el-form-item><el-form-item align="right"><el-button type="primary" @click="saveOrUpdate">提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>
import { SaveProductSpec } from '@/api/productSpec.js'
import { ElMessage, ElMessageBox } from 'element-plus'//进入添加
const addShow = () => {dialogVisible.value = trueproductSpec.value = {id: '',specName: '',specValue: []}
}// 页面添加规格操作
const addSpec = () => {productSpec.value.specValue.push({})
}// 页面删除规格元素
const delSpec = (index) => {productSpec.value.specValue.splice(index , 1)
} // 提交表单
const saveOrUpdate = async () => {if(!productSpec.value.id)  {saveData()}}// 保存数据
const saveData = async () => {// 需要将productSpec.value.specValue转换成json字符串提交到后端,通过clone一个新的对象进行实现const productSpecClone = JSON.parse(JSON.stringify(productSpec.value))// 将productSpecClone.specValue.valueList转换成数组,因为后端需要的数组格式的数据[{"key":"内存","valueList":["8G","18G","32G"]}]// v-model绑定的数据模型为字符串productSpecClone.specValue.forEach(item => {     item.valueList = item.valueList.split(",")})productSpecClone.specValue = JSON.stringify(productSpecClone.specValue)console.log(productSpecClone);// 提交表单await SaveProductSpec(productSpecClone)dialogVisible.value = falseElMessage.success('操作成功')fetchData()}
</script>

2.7 修改功能

2.7.1 需求说明

当用户点击修改按钮的时候,那么此时就弹出对话框,在该对话框中需要将当前行所对应的产品规格数据在该表单页面进行展示。当用户在该表单中点击提交按钮的时候那么此时就需要将表单进行提交,在后端需要提交过来的表单数据修改数据库中的即可。

效果如下所示:

在这里插入图片描述

2.7.2 数据回显

分析:

1、使用添加数据的表单即可

2、要将当前操作行的数据展示在表单中,那么此时需要用到插槽

代码如下所示:

<el-table-column label="操作" align="center" width="200" #default="scope"><el-button type="primary" size="small" @click="editShow(scope.row)">修改</el-button>
</el-table-column><script setup>//进入修改
const editShow = row => {productSpec.value = row dialogVisible.value = true
}</script>

2.7.4 后端接口

ProductSpecController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@PutMapping("updateById")
public Result updateById(@RequestBody ProductSpec productSpec) {productSpecService.updateById(productSpec);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
ProductSpecService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public void updateById(ProductSpec productSpec) {productSpecMapper.updateById(productSpec);
}
ProductSpecMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface ProductSpecMapper {public abstract void updateById(ProductSpec productSpec);
}
ProductSpecMapper.xml

在ProductSpecMapper.xml映射文件中添加如下sql语句:

<update id="updateById" >update product_spec set<if test="specName != null and specName != ''">spec_name = #{specName},</if><if test="specValue != null and specValue != ''">spec_value = #{specValue},</if>update_time =  now()whereid = #{id}
</update>

2.7.5 前端对接

productSpec.js

在src/api目录下添加productSpec.js文件,内容如下所示:

// 修改信息
export const UpdateProductSpecById = productSpec => {return request({url: `${api_name}/updateById`,method: 'put',data: productSpec,})
}
productSpec.vue

修改productSpec.vue文件,内容如下所示:

<script setup>
import { UpdateProductSpecById } from '@/api/productSpec.js'// 提交表单
const saveOrUpdate = async () => {if(!productSpec.value.id)  {saveData()}else {updateData()}}// 保存修改
const updateData = async () => {// 需要将productSpec.value.specValue转换成json字符串提交到后端,通过clone一个新的对象进行实现const productSpecClone = JSON.parse(JSON.stringify(productSpec.value))// 将productSpecClone.specValue.valueList转换成数组,因为后端需要的数组格式的数据[{"key":"内存","valueList":["8G","18G","32G"]}]// v-model绑定的数据模型为字符串productSpecClone.specValue.forEach(item => {  console.log(typeof item.valueList)if(typeof item.valueList === 'string') {   // 针对规格数据修改完毕以后数据类型有可能会变成string,针对string类型的数据将其转换成数组item.valueList = item.valueList.split(",")}   })productSpecClone.specValue = JSON.stringify(productSpecClone.specValue)// 提交表单await UpdateProductSpecById(productSpecClone)dialogVisible.value = falseElMessage.success('操作成功')fetchData()}
</script>

2.8 删除功能

2.8.1 需求说明

当点击删除按钮的时候此时需要弹出一个提示框,询问是否需要删除数据?如果用户点击是,那么此时向后端发送请求传递id参数,后端接收id参数进行逻辑删除。

效果如下所示:

在这里插入图片描述

2.8.2 后端接口

ProductSpecController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@DeleteMapping("/deleteById/{id}")
public Result removeById(@PathVariable Long id) {productSpecService.deleteById(id);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
ProductSpecService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public void deleteById(Long id) {productSpecMapper.deleteById(id);
}
ProductSpecMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface ProductSpecMapper {public abstract void deleteById(Long id);
}
ProductSpecMapper.xml

在ProductSpecMapper.xml映射文件中添加如下sql语句:

<update id="deleteById">update product_spec setupdate_time = now() ,is_deleted = 1whereid = #{id}
</update>

2.8.2 前端对接

productSpec.js

在src/api目录下添加productSpec.js文件,内容如下所示:

// 根据id删除数据
export const DeleteProductSpecById = id => {return request({url: `${api_name}/deleteById/${id}`,method: 'delete',})
}
productSpec.vue

修改productSpec.vue文件,内容如下所示:

<el-table-column label="操作" align="center" width="200" #default="scope"><el-button type="danger" size="small" @click="remove(scope.row.id)">删除</el-button>
</el-table-column><script setup>
import { DeleteProductSpecById } from '@/api/productSpec.js'//删除
const remove = async id => {ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(async () => {await DeleteProductSpecById(id)ElMessage.success('删除成功')fetchData()}).catch(() => {ElMessage.info('取消删除')})
}</script>

3 商品管理

商品管理就是对电商项目中所涉及到的商品数据进行维护。

3.1 菜单添加

首先在系统中添加商品管理的菜单,具体步骤如下所示:

1、在后台管理系统中通过系统管理的菜单管理添加商品管理的相关菜单,如下所示:

在这里插入图片描述

2、给系统管理员角色分配商品商品管理菜单访问权限:

在这里插入图片描述

3、在前端项目中创建对应的页面,以及配置对应的异步路由

在src/views/product的文件夹中加入商品管理页面文件,如下所示:

在这里插入图片描述

在src/router/modules文件夹下创建product.js路由文件,文件内容如下所示:

const Layout = () => import('@/layout/index.vue')
const category = () => import('@/views/product/category.vue')
const brand = () => import('@/views/product/brand.vue')
const categoryBrand = () => import('@/views/product/categoryBrand.vue')
const productSpec = () => import('@/views/product/productSpec.vue')
const product = () => import('@/views/product/product.vue')export default [{path: '/product',component: Layout,name: 'product',meta: {title: '商品管理',},icon: 'Histogram',children: [{path: '/category',name: 'category',component: category,meta: {title: '分类管理',},},{path: '/brand',name: 'brand',component: brand,meta: {title: '品牌管理',},},{path: '/categoryBrand',name: 'categoryBrand',component: categoryBrand,meta: {title: '分类品牌',},},{path: '/productSpec',name: 'productSpec',component: productSpec,meta: {title: '商品规格',},},{path: '/product',name: 'product',component: product,meta: {title: '商品管理',},},],},
]

3.2 表结构介绍

商品数据所对应的表结构如下所示:

CREATE TABLE `product` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',`name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT '商品名称',`brand_id` bigint DEFAULT NULL COMMENT '品牌ID',`category1_id` bigint DEFAULT NULL COMMENT '一级分类id',`category2_id` bigint DEFAULT NULL COMMENT '二级分类id',`category3_id` bigint DEFAULT NULL COMMENT '三级分类id',`unit_name` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT '计量单位',`slider_urls` text COMMENT '轮播图',`spec_value` varchar(255) DEFAULT NULL COMMENT '商品规格json',`status` tinyint NOT NULL DEFAULT '0' COMMENT '线上状态:0-初始值,1-上架,-1-自主下架',`audit_status` tinyint NOT NULL DEFAULT '0' COMMENT '审核状态:0-初始值,1-通过,-1-未通过',`audit_message` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT '审核信息',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:不可用 1:可用)',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品';

3.3 页面制作

直接导入资料中所提供的product.vue页面,以及对应的product.js

3.4 列表查询

3.4.1 需求说明

需求说明:

1、如果在搜索表单中选择了某一个品牌以及分类,那么此时就需要按照品牌id和分类id进行查询

2、搜索的时候需要进行分页搜索

3.4.2 后端接口

需求分析

1、前端提交请求参数的时候包含了两部分的参数:搜索条件参数、分页参数。搜索条件参数可以通过?拼接到请求路径后面,分页参数【当前页码、每页显示的数据条数】可以让前端通过请求路径传递过来

2、后端查询完毕以后需要给前端返回一个分页对象,分页对象中就封装了分页相关的参数(当前页数据、总记录数、总页数…)

3、前端进行参数传递的时候,不一定会传递搜索条件,因此sql语句的编写需要使用到动态sql

ProductDto

定义一个用来封装请求参数的Dto对象,如下所示:

// com.atguigu.spzx.model.dto.product;
@Data
public class ProductDto extends BaseEntity {private Long brandId;private Long category1Id;private Long category2Id;private Long category3Id;}
Product

针对当前要操作的数据库表定义一个与之对应的实体类:

// com.atguigu.spzx.model.entity.product
@Data
public class Product extends BaseEntity {private String name;					// 商品名称private Long brandId;					// 品牌IDprivate Long category1Id;				// 一级分类idprivate Long category2Id;				// 二级分类idprivate Long category3Id;				// 三级分类idprivate String unitName;				// 计量单位private String sliderUrls;				// 轮播图private String specValue;				// 商品规格值json串private Integer status;					// 线上状态:0-初始值,1-上架,-1-自主下架private Integer auditStatus;			// 审核状态private String auditMessage;			// 审核信息// 扩展的属性,用来封装响应的数据private String brandName;				// 品牌private String category1Name;			// 一级分类private String category2Name;			// 二级分类private String category3Name;			// 三级分类}
ProductController

表现层代码实现

// com.atguigu.spzx.manager.controller;
@RestController
@RequestMapping(value="/admin/product/product")
public class ProductController {@Autowiredprivate ProductService productService ;@GetMapping(value = "/{page}/{limit}")public Result<PageInfo<Product>> findByPage(@PathVariable Integer page, @PathVariable Integer limit, ProductDto productDto) {PageInfo<Product> pageInfo = productService.findByPage(page, limit, productDto);return Result.build(pageInfo , ResultCodeEnum.SUCCESS) ;}}
ProductService

业务层代码实现

//  com.atguigu.spzx.manager.service.impl;
@Service
public class ProductServiceImpl implements ProductService {@Autowiredprivate ProductMapper productMapper ;@Overridepublic PageInfo<Product> findByPage(Integer page, Integer limit, ProductDto productDto) {PageHelper.startPage(page , limit) ;List<Product> productList =  productMapper.findByPage(productDto) ;return new PageInfo<>(productList);}}
ProductMapper

持久层代码实现

@Mapper
public interface ProductMapper {public abstract List<Product> findByPage(ProductDto productDto);  
}
ProductMapper.xml

在ProductMapper.xml映射文件中添加如下的sql语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.spzx.manager.mapper.ProductMapper"><resultMap id="productMap" type="com.atguigu.spzx.model.entity.product.Product" autoMapping="true"></resultMap><!-- 用于select查询公用抽取的列 --><sql id="columns">id,name,brand_id,category1_id,category2_id,category3_id,unit_name,slider_urls,spec_value,status,audit_status,audit_message,create_time,update_time,is_deleted</sql><sql id="findPageWhere"><where><if test="brandId != null and brandId != ''">and p.brand_id = #{brandId}</if><if test="category1Id != null and category1Id != ''">and p.category1_id = #{category1Id}</if><if test="category2Id != null and category2Id != ''">and p.category2_id = #{category2Id}</if><if test="category3Id != null and category3Id != ''">and p.category3_id = #{category3Id}</if>and p.is_deleted = 0</where></sql><select id="findByPage" resultMap="productMap">selectp.id, p.name , p.brand_id , p.category1_id , p.category2_id , p.category3_id, p.unit_name,p.slider_urls , p.spec_value , p.status , p.audit_status , p.audit_message , p.create_time , p.update_time , p.is_deleted ,b.name brandName , c1.name category1Name , c2.name category2Name , c2.name category3Namefrom product pLEFT JOIN brand b on b.id = p.brand_idLEFT JOIN category c1 on c1.id = p.category1_idLEFT JOIN category c2 on c2.id = p.category2_idLEFT JOIN category c3 on c3.id = p.category2_id<include refid="findPageWhere"/>order by id desc</select></mapper>

age , limit) ;
List productList = productMapper.findByPage(productDto) ;
return new PageInfo<>(productList);
}

}


#### ProductMapper持久层代码实现```java
@Mapper
public interface ProductMapper {public abstract List<Product> findByPage(ProductDto productDto);  
}
ProductMapper.xml

在ProductMapper.xml映射文件中添加如下的sql语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.spzx.manager.mapper.ProductMapper"><resultMap id="productMap" type="com.atguigu.spzx.model.entity.product.Product" autoMapping="true"></resultMap><!-- 用于select查询公用抽取的列 --><sql id="columns">id,name,brand_id,category1_id,category2_id,category3_id,unit_name,slider_urls,spec_value,status,audit_status,audit_message,create_time,update_time,is_deleted</sql><sql id="findPageWhere"><where><if test="brandId != null and brandId != ''">and p.brand_id = #{brandId}</if><if test="category1Id != null and category1Id != ''">and p.category1_id = #{category1Id}</if><if test="category2Id != null and category2Id != ''">and p.category2_id = #{category2Id}</if><if test="category3Id != null and category3Id != ''">and p.category3_id = #{category3Id}</if>and p.is_deleted = 0</where></sql><select id="findByPage" resultMap="productMap">selectp.id, p.name , p.brand_id , p.category1_id , p.category2_id , p.category3_id, p.unit_name,p.slider_urls , p.spec_value , p.status , p.audit_status , p.audit_message , p.create_time , p.update_time , p.is_deleted ,b.name brandName , c1.name category1Name , c2.name category2Name , c2.name category3Namefrom product pLEFT JOIN brand b on b.id = p.brand_idLEFT JOIN category c1 on c1.id = p.category1_idLEFT JOIN category c2 on c2.id = p.category2_idLEFT JOIN category c3 on c3.id = p.category2_id<include refid="findPageWhere"/>order by id desc</select></mapper>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.bcls.cn/Ovht/10365.shtml

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

亿道信息轻工业三防EM-T195,零售、制造、仓储一网打尽

厚度仅10.5mm&#xff0c;重量仅0.65千克的EM-T195&#xff0c;其紧凑而纤薄的设计为以往加固型平板带来了全新的轻薄概念。尽管设计时尚、轻薄&#xff0c;但经过军用认证的强固性仍然能够承受所有具有挑战性的环境条件。随身携带无负担的轻便性加上抗震功能使其成为餐厅、酒店…

人脸识别技术应用哪些地方?

人脸识别技术&#xff0c;作为现代科技的一大突破&#xff0c;已经在众多领域找到了应用之地。从公共安全到商业营销&#xff0c;从医疗健康到教育娱乐&#xff0c;人脸识别技术以其独特的优势&#xff0c;正在逐渐改变我们的生活方式。 在公共安全领域&#xff0c;人脸识别技术…

鸿蒙 渲染控制

前提&#xff1a;基于官网3.1/4.0文档。参考官网文档 基于Android开发体系来进行比较和思考。&#xff08;或有偏颇&#xff0c;自行斟酌&#xff09; 1.概念 ArkUI通过自定义组件的build()函数和builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了…

npm与包

包 包的概念 Node.js中的第三方模块又叫做包。包的来源 由第三方个人或团队开发出来的&#xff0c;免费提供给所有人使用。为什么需要包 由于Node.js内置模块仅提供了一些底层的API&#xff0c;导致在基于内置模块进行项目开发时&#xff0c;效率很低。包是基于内置模块封装出…

Vue.js的双向绑定原理

Vue的双向绑定 vue双向绑定是其最重要的核心亮点&#xff0c;其原理也很简单&#xff0c;这里做个简单总结 vue2的双向绑定是利用的Object.definePropertyvue3的双向绑定是利用的 ES6Porxy中的defineProperty(target, propKey, propDesc 其作用类似于Object.defineProperty …

【脑切片图像分割】MATLAB 图像处理 源码

1. 简单图像处理 加载图像 Brain.jpg&#xff0c;使用直方图和颜色分割成区域这些区域有不同的颜色。 这是一个更高级的问题&#xff0c;有多个解决它的方法。 例如&#xff0c;您可以计算具有特定数字的图像的直方图&#xff08;例如 16 - 32&#xff09;&#xff0c;找到直方…

在 Rust 中实现 TCP : 1. 联通内核与用户空间的桥梁

内核-用户空间鸿沟 构建自己的 TCP栈是一项极具挑战的任务。通常&#xff0c;当用户空间应用程序需要互联网连接时&#xff0c;它们会调用操作系统内核提供的高级 API。这些 API 帮助应用程序 连接网络创建、发送和接收数据&#xff0c;从而消除了直接处理原始数据包的复杂性。…

SpringBoot底层原理

SpringBoot底层原理 一 配置优先级 1.配置方式 Springboot中支持三种配置方式&#xff0c;分别为&#xff1a; application.propertiesapplication.ymlapplication.yaml 2.配置优先级 当存在多份配置文件时&#xff0c;配置文件会按照它们的优先级生效。 优先级从高到底…

uniapp 编译微信小程序的离谱报错

上图&#xff1a; 如图所示&#xff1a;在同一个元素上同时出现了 wx&#xff1a;if 和 wx&#xff1a;else 就很离谱

LeetCode215.数组中的第K个最大元素

题目 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 输入: [3,2,1,5,6,4], …

RISC-V特权架构 - 机器模式下的异常处理

RISC-V特权架构 - 机器模式下的异常处理 1 进入异常1.1 从mtvec 定义的PC 地址开始执行1.2 更新CSR 寄存器mcause1.3 更新CSR 寄存器mepc1.4 更新CSR 寄存器mtval1.5 更新CSR 寄存器mstatus 2 退出异常2.1 从mepc 定义的PC 地址开始执行2.2 更新CSR 寄存器mstatus 3 异常服务程…

使用 Haproxy 搭建Web群集

Haproxy是目前比较流行的一种群集调度工具&#xff0c;同类群集调度工具有很多&#xff0c;如LVS 和Nginx。相比较而言&#xff0c;LVS.牲能最好&#xff0e;但是搭建相对复杂:Nginx的upstream模块支持群集功能&#xff0e;但是对群集节点健康检查功能不强&#xff0c;性能没有…

进程操作(Win32, C++)

CProcessUtils.h #pragma once#include <wtypesbase.h> #include <tchar.h> #include <vector> #include <map> #include <string>#ifdef _UNICODE using _tstring std::wstring; #else using _tstring std::string; #endif// 进程信息 typed…

Java JDBC JDBC事务管理 JDBC连接池(阿里巴巴Druid连接池、C3P0连接池) JDBC工具类

Java数据库连接 Java DataBase Connectivity。JDBC 规范定义接口&#xff0c;具体的实现由各大数据库厂商来实现。 JDBC可让Java通过程序操作关系型数据库&#xff0c;JDBC基于驱动程序实现与数据库的连接与操作。 JDBC 是 Java 访问数据库的标准规范&#xff0c;真正怎么操作…

gRPC知识归档

文章目录 gRPC知识归档gRPC原理什么是gRPCgRPC的特性gRPC支持语言gRPC使用场景gRPC设计的动机和原则 数据封装和数据传输问题网络传输中的内容封装和数据体积问题JSONProtobuf&#xff08;微服务之间的服务器调用&#xff0c;一般采用二进制序列化&#xff0c;比如protobuf&…

基于springboot实现图书馆管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现图书馆管理系统演示 摘要 电脑的出现是一个时代的进步&#xff0c;不仅仅帮助人们解决了一些数学上的难题&#xff0c;如今电脑的出现&#xff0c;更加方便了人们在工作和生活中对于一些事物的处理。应用的越来越广泛&#xff0c;通过互联网我们可以更方便地…

浅谈 Linux 网络编程 - Server 端模型、sockaddr、sockaddr_in 结构体

文章目录 前言前置知识Server 端核心模型 【重点】相关函数 【重点】socket 函数bind 函数listen 函数accept 函数close 函数 sockaddr 数据结构 【重点】 前言 本文主要是对 Linux 网络编程中&#xff0c;Server 端的模型、相关函数 以及 sockaddr、sockaddr_in 结构体做介绍…

Apache Echarts介绍与入门

介绍 Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 官网地址&#xff1a;https://echarts.apache.org/zh/index.html 入门案例 Apache Echarts官方提供的快…

物联网技术助力智慧城市安全建设:构建全方位、智能化的安全防护体系

一、引言 随着城市化进程的加速和信息技术的迅猛发展&#xff0c;智慧城市已成为现代城市发展的重要方向。在智慧城市建设中&#xff0c;安全是不可或缺的一环。物联网技术的快速发展为智慧城市安全建设提供了有力支持&#xff0c;通过构建全方位、智能化的安全防护体系&#…

HCIA-HarmonyOS设备开发V2.0证书

目录 一、不墨迹&#xff0c;上证书二、考试总结三、习题四、知识点五、坚持就有收获 HCIA-HarmonyOS Device Developer V2.0 开发者能力认证考试已通过。 一、不墨迹&#xff0c;上证书 一个多月的努力&#xff0c;验证了自己的学习成果&#xff0c;也认识到自己有待提升之处…
推荐文章