day07_分类管理EasyExcel品牌管理

文章目录

  • 1 分类管理
    • 1.1 菜单添加
    • 1.2 表结构介绍
    • 1.3 页面制作
    • 1.4 列表查询
      • 1.4.1 需求分析
      • 1.4.2 后端接口
        • Category
        • CategoryController
        • CategoryService
        • CategoryMapper
        • CategoryMapper.xml
      • 1.4.3 前端对接
        • category.js
        • category.vue
  • 2 EasyExcel
    • 2.1 数据导入导出意义
    • 2.2 EasyExcel简介
    • 2.3 入门案例
      • 2.3.1 解析Excel数据
      • 2.3.2 存储数据到Excel
    • 2.4 导出功能
      • 2.4.1 需求说明
      • 2.4.2 后端接口
        • CategoryController
        • CategoryService
        • CategoryMapper
        • CategoryMapper.xml
      • 2.4.3 前端对接
        • category.js
        • category.vue
    • 2.5 导入功能
      • 2.5.1 需求说明
      • 2.5.2 后端接口
        • CategoryController
        • CategoryService
        • CategoryMapper
        • CategoryMapper.xml
      • 2.5.3 前端对接
  • 3 品牌管理
    • 3.1 菜单添加
    • 3.2 表结构介绍
    • 3.3 页面制作
    • 3.4 列表查询
      • 3.4.1 后端接口
        • Brand
        • BrandController
        • BrandService
        • BrandMapper
        • BrandMapper.xml
      • 3.4.2 前端对接
        • brand.js
        • brand.vue
    • 3.5 品牌添加
      • 3.5.1 需求说明
      • 3.5.2 页面制作
      • 3.5.3 后端接口
        • BrandController
        • BrandService
        • BrandMapper
        • BrandMapper.xml
      • 3.5.4 前端对接
        • brand.js
        • brand.vue
    • 3.6 修改品牌
      • 3.6.1 需求说明
      • 3.6.2 数据回显
      • 3.6.3 提交修改
        • 后端接口
          • BrandController
          • BrandService
          • BrandMapper
          • BrandMapper.xml
        • 前端对接
          • brand.js
          • brand.vue
    • 3.7 删除品牌
      • 3.7.1 需求说明
      • 3.7.2 后端接口
        • BrandController
        • BrandService
        • BrandMapper
        • BrandMapper.xml
      • 3.7.3 前端对接
        • brand.js
        • brand.vue

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')export default [{path: '/product',component: Layout,name: 'product',meta: {title: '商品管理',},icon: 'Histogram',children: [{path: '/category',name: 'category',component: category,meta: {title: '分类管理',},},],},
]

在src/router/index.js中添加异步路由,如下所示:

import product from './modules/product'// 动态菜单
export const asyncRoutes = [...system,...base,...product]

1.2 表结构介绍

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

CREATE TABLE `category` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类id',`name` varchar(50) DEFAULT NULL COMMENT '分类名称',`image_url` varchar(200) DEFAULT NULL,`parent_id` bigint DEFAULT NULL COMMENT '父分类id',`status` tinyint DEFAULT NULL COMMENT '是否显示[0-不显示,1显示]',`order_num` int 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=704 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品分类';

注意:分类数据是具有层级结构的,因此在进行数据展示的时候可以考虑使用树形表格进行展示。在咱们的尚品甄选项目中关于分类的数据只支持三级。

1.3 页面制作

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

在这里插入图片描述

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

1、导入导出按钮

2、分类列表展示【树形表格】

代码实现如下所示:

<template><div class="tools-div"><el-button type="success" size="small" >导出</el-button><el-button type="primary" size="small" >导入</el-button></div><!---懒加载的树形表格--><el-table:data="list"style="width: 100%"row-key="id"borderlazy:load="fetchData":tree-props="{ children: 'children', hasChildren: 'hasChildren' }"><el-table-column prop="name" label="分类名称" /><el-table-column prop="imageUrl" label="图标" #default="scope"><img :src="scope.row.imageUrl" width="50" /></el-table-column><el-table-column prop="orderNum" label="排序" /><el-table-column prop="status" label="状态" #default="scope">{{ scope.row.status == 1 ? '正常' : '停用' }}</el-table-column><el-table-column prop="createTime" label="创建时间" /></el-table></template><script setup>
import { ref } from 'vue';// 定义list属性模型
const list = ref([{"id":1 , "name":"数码" , "orderNum":"1" , "status":1 , "createTime":"2023-05-22" , "hasChildren": true},{"id":2 , "name":"手机" , "orderNum":"1", "status":1, "createTime":"2023-05-22"},
])// 加载数据的方法
const fetchData = (row, treeNode, resolve) => {// 向后端发送请求获取数据const data = [{"id":3 , "name":"智能设备" , "orderNum":"1" , "status":1 , "createTime":"2023-05-22" },{"id":4 , "name":"电子教育" , "orderNum":"2" , "status":1 , "createTime":"2023-05-22" },]// 返回数据resolve(data)}</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 需求分析

当页面初始化完毕以后,此时就需要从请求后端接口查询所有的一级分类数据,一级分类数据的parent_id为0。当用户点击某一个分类前的小箭头,那么此时就需要查询该分类下所对应的所有的子分类数据。对应的sql语句如下所示:

select * from category where parent_id = 0 ;

1.4.2 后端接口

Category

定义一个与数据库表相对应的实体类:

// com.atguigu.spzx.model.entity.product
@Data
public class Category extends BaseEntity {private String name;private String imageUrl;private Long parentId;private Integer status;private Integer orderNum;private Boolean hasChildren;private List<Category> children;}
CategoryController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value="/admin/product/category")
public class CategoryController {@Autowiredprivate CategoryService categoryService;@Operation(summary = "根据parentId获取下级节点")@GetMapping(value = "/findByParentId/{parentId}")public Result<List<Category>> findByParentId(@PathVariable Long parentId) {List<Category> list = categoryService.findByParentId(parentId);return Result.build(list , ResultCodeEnum.SUCCESS) ;}
}
CategoryService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl
@Service
public class CategoryServiceImpl implements CategoryService {@Autowiredprivate CategoryMapper categoryMapper ;@Overridepublic List<Category> findByParentId(Long parentId) {// 根据分类id查询它下面的所有的子分类数据List<Category> categoryList = categoryMapper.selectByParentId(parentId);if(!CollectionUtils.isEmpty(categoryList)) {// 遍历分类的集合,获取每一个分类数据categoryList.forEach(item -> {// 查询该分类下子分类的数量int count = categoryMapper.countByParentId(item.getId());if(count > 0) {item.setHasChildren(true);} else {item.setHasChildren(false);}});}return categoryList;}}
CategoryMapper

持久层代码实现:

@Mapper
public interface CategoryMapper {public abstract List<Category> selectByParentId(Long parentId);public abstract int countByParentId(Long id);
}
CategoryMapper.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.CategoryMapper"><resultMap id="categoryMap" type="com.atguigu.spzx.model.entity.product.Category" autoMapping="true"></resultMap><!-- 用于select查询公用抽取的列 --><sql id="columns">id,name,image_url,parent_id,status,order_num,create_time,update_time,is_deleted</sql><select id="selectByParentId" resultMap="categoryMap">select <include refid="columns" />from categorywhere parent_id = #{parentId}and is_deleted = 0order by id desc</select><select id="countByParentId" resultType="Integer">select count(id)from categorywhere parent_id = #{parentId}and is_deleted = 0</select></mapper>

1.4.3 前端对接

category.js

在src/api文件夹下创建一个category.js文件,文件的内容如下所示:

import request from '@/utils/request'const api_name = '/admin/product/category'// 根据parentId获取下级节点
export const FindCategoryByParentId = parentId => {return request({url: `${api_name}/findByParentId/${parentId}`,method: 'get',})
}
category.vue

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

<script setup>
import { ref , onMounted} from 'vue';
import { FindCategoryByParentId } from '@/api/category.js'// 定义list属性模型
const list = ref([])// 页面初始化完毕以后请求后端接口查询数据
onMounted(async () => {const {code , data , message} = await FindCategoryByParentId(0)list.value = data ; 
})// 加载数据的方法
const fetchData = async (row, treeNode, resolve) => {// 向后端发送请求获取数据const {code , data , message} = await FindCategoryByParentId(row.id)// 返回数据resolve(data)}</script>

2 EasyExcel

2.1 数据导入导出意义

后台管理系统是管理、处理企业业务数据的重要工具,在这样的系统中,数据的导入和导出功能是非常重要的,其主要意义包括以下几个方面:

1、提高数据操作效率:手动逐条添加或修改数据不仅费时费力,而且容易出错,此时就可以将大量数据从Excel等表格软件中导入到系统中时,通过数据导入功能,可以直接将表格中的数据批量导入到系统中,提高了数据操作的效率。

2、实现数据备份与迁移:通过数据导出功能,管理员可以将系统中的数据导出为 Excel 或其他格式的文件,以实现数据备份,避免数据丢失。同时,也可以将导出的数据文件用于数据迁移或其他用途。

3、方便企业内部协作:不同部门可能会使用不同的系统或工具进行数据处理,在这种情况下,通过数据导入和导出功能,可以方便地转换和共享数据,促进企业内部协作。

2.2 EasyExcel简介

官网地址:https://easyexcel.opensource.alibaba.com/

在这里插入图片描述

EasyExcel 的主要特点如下:

1、高性能:EasyExcel 采用了异步导入导出的方式,并且底层使用 NIO 技术实现,使得其在导入导出大数据量时的性能非常高效。

2、易于使用:EasyExcel 提供了简单易用的 API,用户可以通过少量的代码即可实现复杂的 Excel 导入导出操作。

3、增强的功能“EasyExcel 支持多种格式的 Excel 文件导入导出,同时还提供了诸如合并单元格、数据校验、自定义样式等增强的功能。

4、可扩展性好:EasyExcel 具有良好的扩展性,用户可以通过自定义 Converter 对自定义类型进行转换,或者通过继承 EasyExcelListener 来自定义监听器实现更加灵活的需求。

2.3 入门案例

2.3.1 解析Excel数据

需求:对资料中的excel数据进行解析,将其存储到对应的List集合中,并遍历List集合

步骤:

1、在spzx-model的pom.xml文件中添加如下依赖:

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId>
</dependency>
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId>
</dependency>

2、定义一个实体类来封装每一行的数据,如下所示:

// com.atguigu.spzx.model.vo.product
@Data
public class CategoryExcelVo {@ExcelProperty(value = "id" ,index = 0)private Long id;@ExcelProperty(value = "名称" ,index = 1)private String name;@ExcelProperty(value = "图片url" ,index = 2)private String imageUrl ;@ExcelProperty(value = "上级id" ,index = 3)private Long parentId;@ExcelProperty(value = "状态" ,index = 4)private Integer status;@ExcelProperty(value = "排序" ,index = 5)private Integer orderNum;
}

3、定义一个监听器,监听解析到的数据,如下所示:

public class ExcelListener<T> extends AnalysisEventListener<T> {//可以通过实例获取该值private List<T> datas = new ArrayList<>();@Overridepublic void invoke(T o, AnalysisContext analysisContext) {  // 每解析一行数据就会调用一次该方法datas.add(o);//数据存储到list,供批量处理,或后续自己业务逻辑处理。}public List<T> getDatas() {return datas;}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {// excel解析完毕以后需要执行的代码}}

4、编写测试方法

public class EasyExcelTest {@Testpublic void easyExcelReadTest() {String fileName = "E://分类数据.xlsx" ;ExcelListener<CategoryExcelVo> excelListener = new ExcelListener<>();  // 创建一个监听器对象EasyExcel.read(fileName, CategoryExcelVo.class, excelListener).sheet().doRead();    // 解析excel表格List<CategoryExcelVo> excelVoList = excelListener.getDatas();       // 获取解析到的数据excelVoList.forEach(s -> System.out.println(s) );   // 进行遍历操作}}

2.3.2 存储数据到Excel

需求:将如下的集合数据存储到Excel中文件中

List<CategoryExcelVo> list = new ArrayList<>() ;
list.add(new CategoryExcelVo(1L , 0L , "数码办公" , 1 , 1)) ;
list.add(new CategoryExcelVo(2L , 1L , "手机通讯" , 1 , 1)) ;
list.add(new CategoryExcelVo(3L , 2L , "手机" , 1 , 0)) ;

代码实现:

@Test
public void saveDataToExcel() {List<CategoryExcelVo> list = new ArrayList<>() ;list.add(new CategoryExcelVo(1L , 0L , "数码办公" , 1 , 1)) ;list.add(new CategoryExcelVo(2L , 1L , "手机通讯" , 1 , 1)) ;list.add(new CategoryExcelVo(3L , 2L , "手机" , 1 , 0)) ;EasyExcel.write("D://分类数据.xlsx" , CategoryExcelVo.class).sheet("分类数据").doWrite(list);
}

2.4 导出功能

2.4.1 需求说明

当用户点击导出按钮的时候,此时将数据库中的所有的分类的数据导出到一个excel文件中,如下所示:

在这里插入图片描述

2.4.2 后端接口

CategoryController

表现层代码实现:

// com.atguigu.spzx.manager.controller#CategoryController
@GetMapping(value = "/exportData")
public void exportData(HttpServletResponse response) {categoryService.exportData(response);
}
CategoryService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl#CategoryServiceImpl
@Override
public void exportData(HttpServletResponse response) {try {// 设置响应结果类型response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("分类数据", "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");// 查询数据库中的数据List<Category> categoryList = categoryMapper.selectAll();List<CategoryExcelVo> categoryExcelVoList = new ArrayList<>(categoryList.size());// 将从数据库中查询到的Category对象转换成CategoryExcelVo对象for(Category category : categoryList) {CategoryExcelVo categoryExcelVo = new CategoryExcelVo();BeanUtils.copyProperties(category, categoryExcelVo, CategoryExcelVo.class);categoryExcelVoList.add(categoryExcelVo);}// 写出数据到浏览器端EasyExcel.write(response.getOutputStream(), CategoryExcelVo.class).sheet("分类数据").doWrite(categoryExcelVoList);} catch (IOException e) {e.printStackTrace();}
}
CategoryMapper

持久层代码实现:

@Mapper
public interface CategoryMapper {public abstract List<Category> selectAll();
}
CategoryMapper.xml

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

<select id="selectAll" resultMap="categoryMap">select <include refid="columns" />from categorywhere is_deleted = 0order by id
</select>

2.4.3 前端对接

category.js

在src/api文件夹下创建一个category.js文件,文件的内容如下所示:

// 导出方法
export const ExportCategoryData = () => {return request({url: `${api_name}/exportData`,method: 'get',responseType: 'blob'  // 这里指定响应类型为blob类型,二进制数据类型,用于表示大量的二进制数据})
}
category.vue

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

<div class="tools-div"><el-button type="success" size="small" @click="exportData">导出</el-button>
</div><script setup>
import { ExportCategoryData } from '@/api/category.js'const exportData = () => {// 调用 ExportCategoryData() 方法获取导出数据ExportCategoryData().then(res => {// 创建 Blob 对象,用于包含二进制数据const blob = new Blob([res]);             // 创建 a 标签元素,并将 Blob 对象转换成 URLconst link = document.createElement('a'); link.href = window.URL.createObjectURL(blob);// 设置下载文件的名称link.download = '分类数据.xlsx';// 模拟点击下载链接link.click();})  
}
</script>

2.5 导入功能

2.5.1 需求说明

当用户点击导入按钮的时候,此时会弹出一个对话框,让用户选择要导入的excel文件,选择完毕以后将文件上传到服务端,服务端通过easyExcel解析文件的内容,然后将解析的结果存储到category表中。如下所示:

在这里插入图片描述

2.5.2 后端接口

CategoryController

表现层代码实现:

// com.atguigu.spzx.manager.controller#CategoryController
@PostMapping("importData")
public Result importData(MultipartFile file) {categoryService.importData(file);return Result.ok();
}
CategoryService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl#CategoryServiceImpl
@Override
public void importData(MultipartFile file) {// 使用EasyExcel解析数据List<CategoryExcelVo> categoryExcelVoList = ExcelHelper.importExcel(file, CategoryExcelVo.class);List<Category> categoryList = new ArrayList<>();// 如果解析到的数据不为空,那么此时将解析到的对象转换成Category对象if(!CollectionUtils.isEmpty(categoryExcelVoList)) {for(CategoryExcelVo categoryExcelVo : categoryExcelVoList) {Category category = new Category();BeanUtils.copyProperties(categoryExcelVo, category, Category.class);categoryList.add(category);}// 进行数据的批量保存categoryMapper.batchInsert(categoryList);}
}
CategoryMapper

持久层代码实现:

@Mapper
public interface CategoryMapper {public abstract void batchInsert(List<Category> categoryList);
}
CategoryMapper.xml

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

<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">insert into category (id,name,image_url,parent_id,status,order_num,create_time ,update_time ,is_deleted) values<foreach collection="categoryList" item="item" separator="," >(#{item.id},#{item.name},#{item.imageUrl},#{item.parentId},#{item.status},#{item.orderNum},now(),now(),0)</foreach>
</insert>

2.5.3 前端对接

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

<div class="tools-div"><el-button type="primary" size="small" @click="importData">导入</el-button>
</div><el-dialog v-model="dialogImportVisible" title="导入" width="30%"><el-form label-width="120px"><el-form-item label="分类文件"><el-uploadclass="upload-demo"action="http://localhost:8501/admin/product/category/importData":on-success="onUploadSuccess":headers="headers"><el-button type="primary">上传</el-button></el-upload></el-form-item></el-form>
</el-dialog><script setup>
import { useApp } from '@/pinia/modules/app'// 文件上传相关变量以及方法定义
const dialogImportVisible = ref(false)
const headers = {token: useApp().authorization.token // 从pinia中获取token,在进行文件上传的时候将token设置到请求头中
}
const importData = () => {dialogImportVisible.value = true
}// 上传文件成功以后要执行方法
const onUploadSuccess = async (response, file) => {ElMessage.success('操作成功')dialogImportVisible.value = falseconst { data } = await FindCategoryByParentId(0)list.value = data ; 
}
</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')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: '品牌管理',},},],},
]

3.2 表结构介绍

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

CREATE TABLE `brand` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',`name` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT '品牌名称',`logo` 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`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='分类品牌'

3.3 页面制作

对比如下页面结构,使用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="name" 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'// 定义表格数据模型
const list = ref([{"id":1 , "name":"华为" , "logo":"http://139.198.127.41:9000/sph/20230506/华为.png"} , {"id":2 , "name":"小米" , "logo":"http://139.198.127.41:9000/sph/20230506/小米.png"} , 
])// 分页条数据模型
const total = ref(0)</script><style scoped>
.tools-div {margin: 10px 0;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}
</style>

3.4 列表查询

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

3.4.1 后端接口

Brand

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

@Data
public class Brand extends BaseEntity {private String name;private String logo;}
BrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value="/admin/product/brand")
public class BrandController {@Autowiredprivate BrandService brandService ;@GetMapping("/{page}/{limit}")public Result<PageInfo<Brand>> findByPage(@PathVariable Integer page, @PathVariable Integer limit) {PageInfo<Brand> pageInfo = brandService.findByPage(page, limit);return Result.build(pageInfo , ResultCodeEnum.SUCCESS) ;}}
BrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Service
public class BrandServiceImpl implements BrandService {@Autowiredprivate BrandMapper brandMapper ;@Overridepublic PageInfo<Brand> findByPage(Integer page, Integer limit) {PageHelper.startPage(page, limit);List<Brand> brandList = brandMapper.findByPage() ;return new PageInfo(brandList);}
}
BrandMapper

持久层代码实现:

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

在BrandMapper.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.BrandMapper"><resultMap id="brandMap" type="com.atguigu.spzx.model.entity.product.Brand" autoMapping="true"></resultMap><!-- 用于select查询公用抽取的列 --><sql id="columns">id,name,logo,create_time,update_time,is_deleted</sql><select id="findByPage" resultMap="brandMap">select <include refid="columns" />from brandwhere is_deleted = 0order by id desc</select></mapper>

3.4.2 前端对接

brand.js

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

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

修改brand.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 { GetBrandPageList } from '@/api/brand.js'// 定义表格数据模型
const list = ref([])// 分页条数据模型
const total = ref(0)//分页条数据模型
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 {code , message , data} = await GetBrandPageList(pageParams.value.page , pageParams.value.limit) list.value = data.listtotal.value = data.total
}</script>

3.5 品牌添加

3.5.1 需求说明

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

在这里插入图片描述

3.5.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-input /></el-form-item><el-form-item label="品牌图标"><el-uploadclass="avatar-uploader"action="http://localhost:8503/admin/system/fileUpload":show-file-list="false"><img v-if="brand.logo" :src="brand.logo" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></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 defaultForm = {logo: ""
}
const brand = ref(defaultForm)
const dialogVisible = ref(false) // 显示添加品牌表单
const addShow = () => {dialogVisible.value = true 
}</script><style>
.avatar-uploader .el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);
}.avatar-uploader .el-upload:hover {border-color: var(--el-color-primary);
}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;
}
</style>

3.5.3 后端接口

BrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@PostMapping("save")
public Result save(@RequestBody Brand brand) {brandService.save(brand);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
BrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
public void save(Brand brand) {brandMapper.save(brand) ;
}
BrandMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface BrandMapper {public abstract void save(Brand brand);
}
BrandMapper.xml

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

<insert id="save">insert into brand (id,name,logo,create_time ,update_time ,is_deleted) values (#{id},#{name},#{logo},now(),now(),0)
</insert>

3.5.4 前端对接

brand.js

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

// 保存品牌
export const SaveBrand = brand => {return request({url: `${api_name}/save`,method: 'post',data: brand,})
}
brand.vue

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

<el-dialog v-model="dialogVisible" title="添加或修改" width="30%"><el-form label-width="120px"><el-form-item label="品牌名称"><el-input v-model="brand.name"/></el-form-item><el-form-item label="品牌图标"><el-uploadclass="avatar-uploader"action="http://localhost:8503/admin/system/fileUpload":show-file-list="false":on-success="handleAvatarSuccess"><img v-if="brand.logo" :src="brand.logo" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></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 { SaveBrand } from '@/api/brand.js'
import { ElMessage, ElMessageBox } from 'element-plus'// 定义提交表单数据模型
const defaultForm = {id: '',name: '',logo: ""
}
const brand = ref(defaultForm)
const dialogVisible = ref(false) // 显示添加品牌表单
const addShow = () => {brand.value = {}dialogVisible.value = true 
}//上传
const handleAvatarSuccess = (response) => {brand.value.logo = response.data
}// 保存数据
const saveOrUpdate = () => {if (!brand.value.id) {saveData()} 
}// 新增
const saveData = async () => {await SaveBrand(brand.value)dialogVisible.value = falseElMessage.success('操作成功')fetchData()
}
</script>

3.6 修改品牌

3.6.1 需求说明

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

效果如下所示:

在这里插入图片描述

3.6.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 => {brand.value = rowdialogVisible.value = true
}</script>

3.6.3 提交修改

后端接口
BrandController

表现层代码实现:

// com.atguigu.spzx.manager.controller
@PutMapping("updateById")
public Result updateById(@RequestBody Brand brand) {brandService.updateById(brand);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
BrandService

业务层代码实现:

// com.atguigu.spzx.manager.service.impl;
@Override
public void updateById(Brand brand) {brandMapper.updateById(brand) ;
}
BrandMapper

持久层代码实现:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface BrandMapper {public abstract void updateById(Brand brand);
}
BrandMapper.xml

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

<update id="updateById" >update brand set<if test="name != null and name != ''">name = #{name},</if><if test="logo != null and logo != ''">logo = #{logo},</if>update_time =  now()whereid = #{id}
</update>
前端对接
brand.js

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

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

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

<script setup>
import { UpdateBrandById } from '@/api/brand.js'// 保存数据
const saveOrUpdate = () => {if (!brand.value.id) {saveData()} else {updateData() }
}// 修改
const updateData = async () => {await UpdateBrandById(brand.value)dialogVisible.value = falseElMessage.success('操作成功')fetchData()
}</script>

3.7 删除品牌

3.7.1 需求说明

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

效果如下所示:

在这里插入图片描述

3.7.2 后端接口

BrandController

表现层代码实现:

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

业务层代码实现:

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

持久层代码实现:

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

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

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

3.7.3 前端对接

brand.js

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

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

修改brand.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 { DeleteBrandById } from '@/api/brand.js'//删除
const remove = async id => {ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(async () => {await DeleteBrandById(id)ElMessage.success('删除成功')fetchData()}).catch(() => {ElMessage.info('取消删除')})
}</script>

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

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

相关文章

本地maven库缓存导入私库

为了加速编译代码&#xff0c;想将本地maven缓存导入内网私库使用。 脚本网上搜的 #!/bin/bash # copy and run this script to the root of the repository directory containing files # this script attempts to exclude uploading itself explicitly so the script name …

物体检测-系列教程19:YOLOV5 源码解析9 (Focus模块、Model类构造函数)

&#x1f60e;&#x1f60e;&#x1f60e;物体检测-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 13、Focus模块 13.1 基本流程 原始输入图像的格式为&#xff1a;tensor: float32[1,3,64…

Unity(第十八部)物理力学,碰撞,触发、关节和材质

1、重力 刚体组件 英文中文描述RigidBody刚体组件physics->rigidbody &#xff0c;刚体组件使一个物体有了质量&#xff0c;重力等。&#xff0c;use gravity 勾选后&#xff0c;物体才会受到重力&#xff0c;会自动下落&#xff0c;取消勾选就不会。&#xff0c;&#xf…

Unity中URP下实现水体(水面反射)

文章目录 前言一、原理1、法一&#xff1a;使用立方体纹理 CubeMap&#xff0c;作为反射纹理使用2、法二&#xff1a;使用反射探针生成环境反射图&#xff0c;所谓反射的采样纹理 二、实现水面反射1、定义和申明CubeMap2、反射向量需要什么3、计算 N ⃗ \vec{N} N 4、计算 V ⃗…

【力扣白嫖日记】550.游戏玩法分析IV

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 550.游戏玩法分析IV 表&#xff1a;Activity 列名类型player_idintdevice_idintevent_datedategames_played…

C语言--修饰符(auto、extern、static)与变量(局部变量+全局变量)和函数的关系

其中extern功能和用法上&#xff0c;比较特殊。先了解extern修饰全局变量&#xff0c;我总结为以下几点 为了方便描述&#xff0c;我创建了一个工程&#xff0c;工程包含了两个源文件&#xff0c;main.c和database.c **1&#xff09;&#xff1a;database.c中使用extern时用来…

Facebook的元宇宙实践:数字化社交的新前景

近年来&#xff0c;元宇宙&#xff08;Metaverse&#xff09;这一概念备受瞩目&#xff0c;被认为是数字化社交的未来趋势之一。而在众多科技巨头中&#xff0c;Facebook&#xff08;现更名为Meta&#xff09;一直处于元宇宙发展的前沿。在本文中&#xff0c;我们将深入探讨Fac…

Cesium插件系列——3dtiles压平

本系列为自己基于cesium写的一套插件具体实现。 这里是根据Cesium提供的CustomShader来实现的。 在CustomShader的vertexShaderText里&#xff0c;需要定义vertexMain函数&#xff0c;例如下&#xff1a; struct VertexInput {Attributes attributes;FeatureIds featureIds;…

NX二次开发:ListingWindow窗口的应用

一、概述 在NX二次开发的学习中&#xff0c;浏览博客时发现看到[社恐猫]和[王牌飞行员_里海]这两篇博客中写道有关信息窗口内容的打印和将窗口内容保存为txt,个人人为在二次开发项目很有必要&#xff0c;因此做以下记录。 ListingWindow信息窗口发送信息四种位置类型 设置Listi…

C语言学生成绩信息管理系统【结构体+文本】

功能描述&#xff1a; 1、录入成绩 2、显示不及格学生信息 3、统计每档学生数量 4、总成绩统计 代码&#xff1a; #include<stdio.h>#define N 30//结构体&#xff1a;typedef struct STUDENT{char id[10];//学号char name[20];//姓名float score[3];//三门成绩,分别代…

用node或者vscode开启一个简单的本地server服务器,加载html网页

使用Live Server 想要加载本地html页面可以快速能让它在你本地浏览器中打开&#xff0c;可以有好多种方式&#xff0c;如果你有使用vscode&#xff0c;可以安装一个插件&#xff1a;Live Server&#xff0c;然后直接在vscode中直接右键就可以开启这个服务&#xff1a; 安装好之…

新一代湖仓集存储,多模型统一架构,高效挖掘数据价值

星环科技TDH一直致力于给用户带来高性能、高可靠的一站式大数据基础平台&#xff0c;满足对海量数据的存储和复杂业务的处理需求。 同时在易用性方面持续深耕&#xff0c;降低用户开发和运维成本&#xff0c;让数据处理平民化&#xff0c;助力用户以更便捷、高效的方式去挖掘数…

Springboot 项目读取yaml的配置文件信息给静态方法使用,以及通过配置 ResourceBundle 类读取config.properties

读取yaml 的配置文件 配置文件信息 iot_saas_tenement:user_id: 7........8d9bprivate_key: MII.......qQbj_url: http://4.....5:8088project_name: iot_s.......rojectdevice_name: te.....ice 创建一个类 ProxyProperties 读取配置文件信息&#xff0c;并对外提供get方法 …

PaddleOCR 高精度文字识别:丰富多样的前沿算法 | 开源日报 No.187

PaddlePaddle/PaddleOCR Stars: 34.1k License: Apache-2.0 PaddleOCR 是一个丰富、领先和实用的 OCR 工具库&#xff0c;旨在帮助开发者训练更好的模型并将其应用到实际场景中。该项目具有以下特点和优势&#xff1a; 支持多种 OCR 相关前沿算法提供产业级特色模型 PP-OCR、…

大数据毕业设计之前端04:管理系统为什么要自己实现图标组件

关键字&#xff1a;BuildAdmin、Icon、图标、Vue、ElementUI 前言 说到图标&#xff0c;在BuildAdmin中用到的地方很多。比如上一篇中的折叠图标&#xff0c;还有菜单栏图标、导航菜单栏图标等。常见的图标有&#xff1a;ElementUI图标、font-awesome、iconfont阿里图标以及本…

【数据库管理系统】Mysql 8.0.36入门级安装

下载地址 官方网址&#xff1a;MySQL 注意事项 建议不要安装最新版本&#xff0c;一般找mysql5.0或mysql8.0系列版本即可&#xff1b;mysq1官网有.zip和.msi两种安装形式&#xff1b;zip是压缩包&#xff0c;直接解压缩以后使用的&#xff0c;需要自己配置各种东西&#xff…

就业班 2401--3.1 Linux Day9--文件查找和压缩

一、文件查找与打包压缩 grep: 文件内容过滤 [rootqfedu.com ~]# grep root /etc/passwd #从/etc/passwd文件中过滤root字段 grep ^root root$ root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin 查找命令 [rootqfedu.com ~]# which ls ali…

MyBatis 学习(四)之 SQL 映射文件

目录 1 SQL 映射文件介绍 2 select 元素 3 insert 元素 4 update 和 delete 元素 5 sql 元素 6 parameterType 元素 7 resultType 元素 8 resultMap 元素&#xff08;重要&#xff09; 9 参考文档 1 SQL 映射文件介绍 映射器是 MyBatis 中最复杂并且是最重要的…

章鱼网络 Community Call #18|Omnity 将首先支持 Runes 协议资产跨链

香港时间2024年2月8日12点&#xff0c;章鱼网络举行第18期 Community Call。 2024年&#xff0c;我们打开一个良好的局面&#xff1a;$NEAR Restaking 已经完成第三方审计&#xff0c;并且经过几次迭代&#xff0c;进入了正式稳定运行的阶段。更重要的是&#xff0c;我们宣布了…

AI Word Helper (Chorme Extentions) AI单词助手(谷歌浏览器插件)

AI Word Helper (Chorme Extentions) AI单词助手&#xff08;谷歌浏览器插件&#xff09; 英文网站&#xff0c;划词查单词&#xff0c;还是看不懂&#xff1f;因为单词意思那么多&#xff0c;词性搞不清&#xff0c;上下文搞不清&#xff0c;出来的意思就没法用&#xff0c;G…