目录
2.1.1、创建 SpringBoot 项目并配置 application.yaml
学习视频:【绝妙搭配】Spring Boot+Layui 快速搭建后台管理系统(单体应用+前后端分离)_哔哩哔哩_bilibili
1、layui 数据表格组件
这里使用的表格:Layui table 模块,开启合计行 - 在线演示
关于 table.render
table.render({
elem: '#test' // id选择器
,url:'data.json' // 数据来源的请求地址,这里已经将json数据保存到本地
,toolbar: '#toolbarDemo'
,title: '用户数据表' // 导出为excel时的标题
,totalRow: true // 是否统计总数
,cols: [
[
{type: 'checkbox', fixed: 'left'} // 复选框
/*
* field:列
* title:列名
* sort:是否开启升序/降序排列
* totalRowText:统计总数的名字
* templet:追加标签
* totalRow:是否统计该列的总数
*/
,{field:'id', title:'商品ID', width:150, fixed: 'left', sort: true, totalRowText: '合计'}
,{field:'name', title:'商品名称', width:120}
,{field:'description', title:'商品描述', width:150}
,{field:'price', title:'商品价格', width:150, sort: true, totalRow:true}
,{field:'stock', title:'商品库存', width:150, sort: true, totalRow:true}
,{field:'categorylevelone', title:'一级分类', width:150}
,{field:'categoryleveltwo', title:'二级分类', width:150}
,{field:'categorylevelthree', title:'三级分类', width:150}
,{field:'file_name', title:'商品图片', width:150}
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150} // 与操作按钮绑定,每行都会出现编辑、删除按钮
]
]
,page: true // 是否开启分页条
});
2、案例
2.1、准备工作
2.1.1、创建 SpringBoot 项目并配置 application.yaml
使用 Spring Initializr 创建项目,选择添加的依赖如下图
创建完成后在 pom.xml 手动导入 mybatis-plus 的依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
配置 application.yaml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/layui
username: root
password: 123456
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL语句
2.1.2、准备数据库相关
创建所需的数据库和数据表
CREATE DATABASE `layui`;
USE `layui`;
DROP TABLE IF EXISTS `product`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `product` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` VARCHAR(200) NOT NULL COMMENT '名称',
`description` VARCHAR(1024) DEFAULT NULL COMMENT '描述',
`price` FLOAT NOT NULL COMMENT '价格',
`stock` INT NOT NULL COMMENT '库存',
`categorylevelone_id` INT DEFAULT NULL COMMENT '分类1',
`categoryleveltwo_id` INT DEFAULT NULL COMMENT '分类2',
`categorylevelthree_id` INT DEFAULT NULL COMMENT '分类3',
`file_name` VARCHAR(200) DEFAULT NULL COMMENT '文件名称',
PRIMARY KEY (`id`),
UNIQUE KEY `PK__EASYBUY___94F6E55132E0915F` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=777 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `product`
--
LOCK TABLES `product` WRITE;
/*!40000 ALTER TABLE `product` DISABLE KEYS */;
INSERT INTO `product` VALUES (733,'香奈儿','好闻的香水!!!',152,978,548,654,655,'baby_1.jpg'),(734,'洗面奶','',152.5,10,548,654,655,'baby_2.jpg'),(735,'啫喱水','',152,959,548,654,655,'baby_3.jpg'),(736,'香水','',152,1000,548,654,655,'baby_4.jpg'),(737,'香水','',152,111,548,654,655,'baby_5.jpg'),(738,'润肤露','',45,99,548,654,655,'baby_6.jpg'),(739,'洁面装','',156,33,548,654,655,'bk_2.jpg'),(740,'电饭锅','',158,94,628,656,659,'bk_1.jpg'),(741,'婴儿喂奶装','',569,100,632,637,653,'bk_3.jpg'),(742,'坚果套餐','',158,1000,660,661,662,'bk_4.jpg'),(743,'超甜蜜崭','',589,1000,660,661,663,'bk_5.jpg'),(744,'华为2566','',589,1000,670,671,672,'de1.jpg'),(745,'荣耀3C','',589,92,670,671,672,'de2.jpg'),(746,'小米手环','',963,98,670,674,675,'de3.jpg'),(747,'华为2265','',896,1000,670,671,673,'de4.jpg'),(748,'越南坚果','',520,1,660,661,662,'de5.jpg'),(749,'日本进口马桶','',5866,100,628,657,0,'food_1.jpg'),(750,'联想Y系列','',569,894,670,690,691,'food_2.jpg'),(751,'脑白金1号','',589,1000,676,677,680,'food_3.jpg'),(752,'莫里斯按','',589,1000,676,678,0,'food_4.jpg'),(753,'三鹿好奶粉','',859,100,676,679,0,'food_5.jpg'),(754,'儿童牛奶','',5896,100,676,679,0,'food_6.jpg'),(755,'软沙发','',8596,99,628,696,0,'food_b1.jpg'),(756,'收纳盒','',5966,100,628,696,0,'food_b2.jpg'),(757,'洗衣液','',58,1000,628,696,0,'food_r.jpg'),(758,'红短沙发','',596,113,628,696,0,'fre_1.jpg'),(759,'新西兰奶粉','',5896,100,676,679,0,'fre_2.jpg'),(760,'婴儿车','',11000,100,681,682,687,'fre_3.jpg'),(761,'夏款婴儿车','',963,99,681,682,688,'fre_4.jpg'),(762,'抗压旅行箱','',569,1000,681,683,685,'fre_5.jpg'),(763,'透明手提箱','',8596,1000,681,683,684,'fre_6.jpg'),(764,'婴儿果粉','',5896,999,660,661,662,'milk_1.jpg'),(765,'椰子粉','',5963,1000,660,661,662,'milk_2.jpg'),(766,'坚果蛋糕','',200,98,660,661,663,'milk_3.jpg'),(767,'编制手提箱','',5896,1000,681,682,688,'milk_4.jpg'),(768,'纸箱','',5896,3,681,682,687,'milk_5.jpg'),(769,'健胃液','',152,1000,676,679,0,'milk_6.jpg'),(770,'联想NTC','',8596,100,670,671,673,'milk_7.jpg'),(771,'香水1',NULL,100,100,548,654,655,'milk_8.jpg'),(772,'香水2',NULL,100,100,548,654,655,'pro1.jpg'),(773,'香水3',NULL,100,100,548,654,655,'pro2.jpg'),(774,'香水4',NULL,100,100,548,654,655,'pro3.jpg'),(775,'香水5',NULL,100,100,548,654,655,'pro4.jpg'),(776,'香水6',NULL,1,1,548,654,655,'pro5.jpg');
/*!40000 ALTER TABLE `product` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `product_category`
--
DROP TABLE IF EXISTS `product_category`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `product_category` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` VARCHAR(20) NOT NULL COMMENT '名称',
`parent_id` INT NOT NULL COMMENT '父级目录id',
`type` INT DEFAULT NULL COMMENT '级别(1:一级 2:二级 3:三级)',
PRIMARY KEY (`id`),
UNIQUE KEY `PK__EASYBUY___9EC2A4E236B12243` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=697 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `product_category`
--
LOCK TABLES `product_category` WRITE;
/*!40000 ALTER TABLE `product_category` DISABLE KEYS */;
INSERT INTO `product_category` VALUES (548,'化妆品',0,1),(628,'家用商品',0,1),(654,'面部护理',548,2),(655,'少女派',654,3),(656,'餐具',628,2),(657,'卫具',628,2),(658,'叉子',656,3),(659,'锅',656,3),(660,'进口食品',0,1),(661,'零食/糖果/巧克力',660,2),(662,'坚果',661,3),(663,'蜜饯',661,3),(669,'孕期教育',546,3),(670,'电子商品',0,1),(671,'手机',670,2),(672,'华为手机',671,3),(673,'联想手机',671,3),(674,'手环',670,2),(675,'小米手环',674,3),(676,'保健食品',0,1),(677,'老年保健品',676,2),(678,'中年营养品',676,2),(679,'儿童保健品',676,2),(680,'脑白金',677,3),(681,'箱包',0,1),(682,'旅行箱',681,2),(683,'手提箱',681,2),(684,'大型',683,3),(685,'小型',683,3),(686,'中型',683,3),(687,'大型',682,3),(688,'中型',682,3),(689,'小型',682,3),(690,'电脑',670,2),(691,'联想电脑',690,3),(692,'刀叉',656,3),(693,'碗筷',656,3),(696,'客厅专用',628,2);
/*!40000 ALTER TABLE `product_category` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `order_detail`
--
DROP TABLE IF EXISTS `order_detail`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `order_detail` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
`order_id` INT NOT NULL COMMENT '订单主键',
`product_id` INT NOT NULL COMMENT '商品主键',
`quantity` INT NOT NULL COMMENT '数量',
`cost` FLOAT NOT NULL COMMENT '消费',
PRIMARY KEY (`id`),
UNIQUE KEY `PK__EASYBUY___66E1BD8E2F10007B` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=123 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `order_detail`
--
LOCK TABLES `order_detail` WRITE;
/*!40000 ALTER TABLE `order_detail` DISABLE KEYS */;
INSERT INTO `order_detail` VALUES (115,133,735,4,608),(116,133,745,3,1767),(117,134,738,10,450),(118,134,739,66,10296),(119,134,758,10,5960),(120,134,735,33,5016),(121,134,740,6,948),(122,134,750,100,56900);
/*!40000 ALTER TABLE `order_detail` ENABLE KEYS */;
UNLOCK TABLES;
2.1.3、创建与数据表对应的实体类、mapper
Product 实体类
package com.zyj.layui.entity;
import lombok.Data;
/**
* @Author zhang
* @Date 2022/6/9 - 19:43
* @Version 1.0
*/
@Data
public class Product {
private Integer id;
private String name;
private String description;
private Integer stock;
private Float price;
private Integer categoryleveloneId;
private Integer categoryleveltwoId;
private Integer categorylevelthreeId;
private String fileName;
}
ProductMapper
package com.zyj.layui.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zyj.layui.entity.Product;
/**
* @Author zhang
* @Date 2022/6/9 - 19:46
* @Version 1.0
*/
public interface ProductMapper extends BaseMapper<Product> {
}
2.1.4、在主程序类扫描 mapper
@MapperScan("com.zyj.layui.mapper")
2.1.5、配置 application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/layui
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL语句
2.1.6、测试
package com.zyj.layui.mapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
/**
* @Author zhang
* @Date 2022/6/9 - 19:50
* @Version 1.0
*/
@SpringBootTest
class ProductMapperTest {
@Autowired
private ProductMapper productMapper;
@Test
public void test(){
productMapper.selectList(null).forEach(System.out::println);
}
}
2.2、准备查询数据的接口
2.2.1、准备两个表对应的实体类
Product
@Data
public class Product {
private Integer id;
private String name;
private String description;
private Float price;
private Integer categoryleveloneId;
private Integer categoryleveltwoId;
private Integer categorylevelthreeId;
private String fileName;
}
ProductCategory
@Data
public class ProductCategory {
private Integer id;
private String name;
}
2.2.2、准备返回给浏览器的JSON格式的实体类
由于浏览器显示的一级、二级、三级分类是字符串格式,这里创建 ProductVO,将一级、二级、三级分类设置为 String
ProductVO
@Data
public class ProductVO {
private Integer id;
private String name;
private String description;
private Float price;
private String categorylevelone;
private String categoryleveltwo;
private String categorylevelthree;
private String fileName;
}
DataVO
@Data
public class DataVO<T> {
private Integer code;
private String msg;
private Integer count;
private List<T> data;
}
2.2.3、准备 mapper
ProductMapper
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
ProductCategoryMapper
@Repository
public interface ProductCategoryMapper extends BaseMapper<ProductCategory> {
}
2.2.4、准备 service 及其实现类
ProductService
public interface ProductService {
/**
* 获取全部数据(JSON格式)
* @return
*/
public DataVO<ProductVO> findData();
}
ProductServiceImpl
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private ProductCategoryMapper productCategoryMapper;
/**
* 获取全部数据(JSON格式)
* @return
*/
@Override
public DataVO<ProductVO> findData() {
DataVO<ProductVO> dataVO = new DataVO<>();
dataVO.setCode(0);
dataVO.setMsg("");
dataVO.setCount(productMapper.selectCount(null));
List<Product> productList = productMapper.selectList(null);
List<ProductVO> productVOList = new ArrayList<>();
for (Product product : productList) {
// 获取对应商品的一、二、三级分类的字符串并封装
ProductVO productVO = new ProductVO();
BeanUtils.copyProperties(product,productVO);
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("id",product.getCategoryleveloneId());
ProductCategory productCategory = productCategoryMapper.selectOne(wrapper);
if(productCategory!=null){
productVO.setCategorylevelone(productCategory.getName());
}
wrapper = new QueryWrapper();
wrapper.eq("id",product.getCategoryleveltwoId());
productCategory = productCategoryMapper.selectOne(wrapper);
if(productCategory!=null) {
productVO.setCategoryleveltwo(productCategory.getName());
}
wrapper = new QueryWrapper();
wrapper.eq("id",product.getCategorylevelthreeId());
productCategory = productCategoryMapper.selectOne(wrapper);
if(productCategory!=null) {
productVO.setCategorylevelthree(productCategory.getName());
}
productVOList.add(productVO);
}
dataVO.setData(productVOList);
return dataVO;
}
}
2.2.5、测试
@SpringBootTest
class ProductMapperTest {
@Autowired
private ProductMapper productMapper;
@Test
public void test(){
productMapper.selectList(null).forEach(System.out::println);
}
}
2.2.6、准备 controller
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/list")
public DataVO<ProductVO> list(){
return productService.findData();
}
}
2.3、页面查看数据
2.3.1、准备页面
页面及静态资源位置如下
页面内容
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="shortcut icon" href="../resources/favicon.ico" th:href="@{/static/favicon.ico}"/>
<!-- 注意:项目正式环境请勿引用该地址 -->
<link href="layui/css/layui.css" rel="stylesheet">
</head>
<body>
<table class="layui-hide" id="test" lay-filter="test"></table>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="getCheckData">获取选中行数据</button>
<button class="layui-btn layui-btn-sm" lay-event="getCheckLength">获取选中数目</button>
<button class="layui-btn layui-btn-sm" lay-event="isAll">验证是否全选</button>
</div>
</script>
<script type="text/html" id="barDemo">
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<!-- 注意:项目正式环境请勿引用该地址 -->
<script src="layui/layui.js"></script>
<script>
layui.use('table', function(){
var table = layui.table;
//温馨提示:默认由前端自动合计当前行数据。从 layui 2.5.6 开始: 若接口直接返回了合计行数据,则优先读取接口合计行数据。
//详见:/docs/modules/index.html#totalRow
table.render({
elem: '#test' // id选择器
,url:'http://localhost:8080/list' // 数据来源的请求地址,这里已经将json数据保存到本地
,toolbar: '#toolbarDemo'
,title: '用户数据表' // 导出为excel时的标题
,totalRow: true // 是否统计总数
,cols: [
[
{type: 'checkbox', fixed: 'left'} // 复选框
/*
* field:列
* title:列名
* sort:是否开启升序/降序排列
* totalRowText:统计总数的名字
* templet:追加标签
* totalRow:是否统计该列的总数
*/
,{field:'id', title:'商品ID', width:150, fixed: 'left', sort: true, totalRowText: '合计'}
,{field:'name', title:'商品名称', width:120}
,{field:'description', title:'商品描述', width:150}
,{field:'price', title:'商品价格', width:150, sort: true, totalRow:true}
,{field:'stock', title:'商品库存', width:150, sort: true, totalRow:true}
,{field:'categorylevelone', title:'一级分类', width:150}
,{field:'categoryleveltwo', title:'二级分类', width:150}
,{field:'categorylevelthree', title:'三级分类', width:150}
,{field:'fileName', title:'商品图片', width:150}
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150} // 与操作按钮绑定,每行都会出现编辑、删除按钮
]
]
,page: true // 是否开启分页条
});
//工具栏事件
table.on('toolbar(test)', function(obj){
var checkStatus = index.checkStatus(obj.config.id);
switch(obj.event){
case 'getCheckData':
var data = checkStatus.data; // 获取选中的行
layer.alert(JSON.stringify(data));
break;
case 'getCheckLength':
var data = checkStatus.data;
layer.msg('选中了:'+ data.length + ' 个');
break;
case 'isAll':
layer.msg(checkStatus.isAll ? '全选': '未全选')
break;
};
});
//工具条事件,这里是通过表格的 lay-filter="test" 属性绑定
table.on('tool(test)', function(obj){
var data = obj.data;
if(obj.event === 'detail'){
layer.msg('ID:'+ data.id + ' 的查看操作');
} else if(obj.event === 'del'){
layer.confirm('真的删除行么', function(index){
obj.del();
layer.close(index);
});
} else if(obj.event === 'edit'){
layer.alert('编辑行:<br>'+ JSON.stringify(data))
}
});
});
</script>
</body>
</html>
2.3.2、跳转到页面的 controller
@Controller
public class MyController {
@RequestMapping("/{url}")
public String toMain(@PathVariable("url") String url){
return url;
}
}
至此,运行后访问 localhost:8080/index 即可在页面获得数据
2.4、分页查看数据
2.4.1、页面分页请求地址格式
layui 使用点击分页栏时,会自动生成请求地址,例如 每页10条数据,请求第二页 时,请求地址为 http://localhost:8080/list?page=2&limit=10,如下图所示
这样就可以通过编写对应接口实现分页
2.5、显示图片
注:若图片无法展开可以将表格的 width 改成一样
2.5.1、创建图片存放的目录
2.5.2、修改页面表格代码
方法一:
,{field:'fileName', title:'商品图片', templet: function (res){
return '<img src="/images/' + res.fileName + '"/>';
}}
方法二:
① 定义一个标签
<script type="text/html" id="img">
<img src="images/{{d.fileName}}"/>
</script>
② 修改表格
,{field:'fileName', title:'商品图片', width:150, templet: '#img'}