easyui是博主最喜欢的前端框架,因为它提供了多种主题样式和各种好看的ui组件。
这篇文章将会详细地介绍easyui前端框架的使用,通过创建一个crud应用来帮助大家快速掌握easyui的使用。
一、快速开始
这个章节主要介绍easyui前端框架的下载,easyui的官网地址如下:
JQuery EasyUI中文网https://www.jeasyui.net/点击上方链接访问easyui中文官网,点击红框内的链接跳转到到下载页面。
在下载页面点击下载对应的版本,本篇文章将使用jquery easyui,点击红框内的按钮。
选择下载免费版即可~
二、准备工作
下载完成后,得到一个压缩包jquery-easyui-1.7.0.zip。
然后把这个压缩包解压出来,我们需要的是红框内的几个文件及文件夹。
- locale目录下是常用的一些js文件
- themes目录下是easyui的样式文件
通过HBuilderx创建一个基本的html项目,项目名为easyui-crud
接着,把themes文件夹复制到项目的css目录下,把locale/easyui-lang-zh_CN.js和红框内的两个js文件复制到项目的js目录下。如图:
三、开始使用
完成前面两步之后,就可以开始愉快地使用easyui了。
1、主题资源
如图,themes下面提供了多种主题样式的资源文件,喜欢哪个主题,引入对应包下的easyui.css即可。
2、常用组件
datagrid
easyui里用的最多的莫过于数据表格了,datagrid是easyui的表格组件,支持分页功能。只需要在表格渲染的js代码中添加选项pagenation: true即可开启分页功能。
打开easyui的文档页面,找到通过javascript渲染表格的案例代码。
以下是官网提供的渲染easyui datagrid的javascript代码,
- url是加载表格数据的地址
- columns是表格的列信息
- #dg表示的是表格元素的选择器,这是id选择器,表示id为dg的DOM对象
$('#dg').datagrid({
url:'datagrid_data.json',
columns:[[
{field:'code',title:'Code',width:100},
{field:'name',title:'Name',width:100},
{field:'price',title:'Price',width:100,align:'right'}
]]
});
textbox
文本框,就是带了easyui样式的input输入框,与之对应的还有passwordbox。
通过标签创建
<input class="easyui-textbox" style="width:150px;" />
使用Javascript创建
<input id="tb" type="text" />
$('#tb').textbox({
width: 150
});
passwordbox
密码框,带了easyui样式的input密码框<input type="password"></input>
通过标签创建
<input class="easyui-passwordbox" style="width:150px;" />
使用Javascript创建
<input id="tb" type="text" />
$('#tb').passwordbox({
width: 150
});
dialog
对话框,通常会在对话框内嵌表单,实现数据的添加和修改功能。
四、表格渲染
为了方便快速学会datagird的使用,这里就直接拿之前写的springboot-crud项目作为后端项目,演示datagird通过ajax异步加载表格数据。
相关的文章:
springboot+mybatis实现简单的增、删、查、改https://blog.csdn.net/heyl163_/article/details/132197201
springboot+mybatis+mybatis-plus对crud项目进行改进https://blog.csdn.net/heyl163_/article/details/132215660在springboot-crud1.0的基础上创建一个新的代码分支springboot-crud2.0
1、后端代码
添加跨域配置
首先要添加跨域配置,防止请求出现cors问题。在config包下创建SpringMvcConfig.java
package com.example.springboot.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* springmvc配置类
* @author heyunlin
* @version 1.0
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
/**
* 解决跨域问题
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(5000);
}
}
添加controller接口
在SongController中添加一个selectList()接口方法
package com.example.springboot.controller;
import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import com.example.springboot.restful.JsonResult;
import com.example.springboot.service.SongService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
@RestController
@RequestMapping(path = "/song", produces="application/json;charset=utf-8")
public class SongController {
private final SongService songService;
@Autowired
public SongController(SongService songService) {
this.songService = songService;
}
@RequestMapping(value = "/insert", method = RequestMethod.POST)
public JsonResult<Void> insert(@Validated SongInsertDTO insertDTO) {
songService.insert(insertDTO);
return JsonResult.success("添加成功");
}
@RequestMapping(value = "/deleteById/{id}", method = RequestMethod.GET)
public JsonResult<Void> deleteById(@PathVariable("id") String id) {
songService.deleteById(id);
return JsonResult.success("删除成功");
}
@RequestMapping(value = "/updateById", method = RequestMethod.POST)
public JsonResult<Void> updateById(@Valid SongUpdateDTO updateDTO) {
songService.updateById(updateDTO);
return JsonResult.success("修改成功");
}
@RequestMapping(value = "/selectById/{id}", method = RequestMethod.GET)
public Song selectById(@PathVariable("id") String id) {
return songService.selectById(id);
}
@RequestMapping(value = "/selectList", method = RequestMethod.GET)
public JsonResult<List<Song>> selectList() {
List<Song> list = songService.selectList();
return JsonResult.success("查询成功", list);
}
}
对应地,在SongService接口添加selectList()方法
package com.example.springboot.service;
import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
public interface SongService {
void insert(SongInsertDTO songInsertDTO);
void deleteById(String id);
void updateById(SongUpdateDTO updateDTO);
Song selectById(String id);
List<Song> selectList();
}
SongServiceImpl
package com.example.springboot.service.impl;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import com.example.springboot.mapper.SongMapper;
import com.example.springboot.service.SongService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
@Service
public class SongServiceImpl implements SongService {
private final SongMapper songMapper;
@Autowired
public SongServiceImpl(SongMapper songMapper) {
this.songMapper = songMapper;
}
@Override
public void insert(SongInsertDTO insertDTO) {
Song song = new Song();
song.setId(uuid());
song.setName(insertDTO.getName());
song.setSinger(insertDTO.getSinger());
if (StringUtils.isNotEmpty(insertDTO.getNote())) {
song.setNote(insertDTO.getNote());
}
songMapper.insert(song);
}
@Override
public void deleteById(String id) {
songMapper.deleteById(id);
}
@Override
public void updateById(SongUpdateDTO updateDTO) {
Song song = new Song();
song.setId(updateDTO.getId());
song.setName(updateDTO.getName());
song.setSinger(updateDTO.getSinger());
if (StringUtils.isNotEmpty(updateDTO.getNote())) {
song.setNote(updateDTO.getNote());
}
song.setLastUpdateTime(LocalDateTime.now());
songMapper.updateById(song);
}
@Override
public Song selectById(String id) {
return songMapper.selectById(id);
}
@Override
public List<Song> selectList() {
return songMapper.selectList(null);
}
/**
* 根据当前时间生成UUID
* @return String
*/
private static String uuid() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
LocalDateTime localDate = LocalDateTime.now();
return localDate.format(formatter);
}
}
2、前端代码
修改index.html文件的内容,引入必要的css和js文件,修改表格的数据加载地址url为selectList接口的访问地址。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="css/themes/icon.css" />
<link rel="stylesheet" href="css/themes/material/easyui.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.easyui.min.js"></script>
<script src="js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectList",
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
然后选择通过firefox运行,打开看到表格没有显示数据,F12打开控制台,查看到请求发生异常,不支持post请求。
这是因为easyui的datagrid默认是通过ajax post请求加载数据.
打开之前的文档页面,往下滚动,找到数据网格属性。
如图,method属性就是设置请求的类型,而这个属性的默认值是post,我们把它设置成get
method属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="css/themes/icon.css" />
<link rel="stylesheet" href="css/themes/material/easyui.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.easyui.min.js"></script>
<script src="js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectList",
method: "get",
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
页面代码修改完成之后,发现只显示了表头,表格数据没有显示出来,而且报了一个错,rows is undefined。
为什么会这样呢?
其实是因为后端返回的数据不是一个list,而是封装的一个JsonResult对象,list放到这个对象的data里了。
loadFilter属性
所以,这里要对返回的数据进行简单的处理,得到data里的list,loadFilter属性是一个方法,用于过滤请求url返回的数据。
后端封装一个JsonResult对象返回是为了能够带上一个请求的状态码code,当这个状态码为200时,表示请求被正确地执行了。
因此,这个过滤方法应该是下面这样:
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
最后,添加loadFilter,调整表格的列信息
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="../css/themes/icon.css" />
<link rel="stylesheet" href="../css/themes/material/easyui.css" />
<script src="../js/jquery.min.js"></script>
<script src="../js/jquery.easyui.min.js"></script>
<script src="../js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectList",
method: "get",
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
此时,页面的数据显示出来了,一共800多条数据。
fitColumns属性
上面的页面看起来非常丑,如果表格能占满整个页面会更好看一点,因此,easyui也实现了这种效果,只需要设置fitColumns属性的值为true即可,表格的列宽会自适应当前页面。
于是,在原来的代码基础上添加fitColumns属性,并设置为true
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="css/themes/icon.css" />
<link rel="stylesheet" href="css/themes/material/easyui.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.easyui.min.js"></script>
<script src="js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectList",
method: "get",
fitColumns: true,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
修改页面代码后的效果,比原来看起来舒服的多。
striped属性
页面看起来差不多了,但是总感觉表格太单调了,全是一种颜色,能不能再美化一下呢。
答案是:当然可以,上面的页面很单调,是表格全部数据都是一个颜色,如果能给表格的行记录颜色不一样,那就完美了。
于是,striped属性腾空出世,这个属性的作用就是显示条纹,不出所料,这个属性默认值也是false。
把它设置成true看一下效果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="css/themes/icon.css" />
<link rel="stylesheet" href="css/themes/material/easyui.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.easyui.min.js"></script>
<script src="js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectList",
method: "get",
fitColumns: true,
striped: true,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
修改页面之后,视觉效果还不错,有了一点bootstrap的感觉了~
pagination属性
上面的表格外观已经很完美了,作为一个后端开发人员来说,这样的样式已经无可挑剔,但是,之前已经说过了,一共有800多条数据,这还算少的了,假如有几万条数据呢?如果一次性全部查询出来,每次查询的时候,后端服务的压力是很大的。
所以,一般数据量大的时候都会分页查询,每次只查询一部分数据。
easyui的datagrid支持分页功能,只需要设置pagination属性为true,而常用的分页属性还有另外两个pageSize和pageList。
修改前端页面代码,添加pagination属性为true。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="../css/themes/icon.css" />
<link rel="stylesheet" href="../css/themes/material/easyui.css" />
<script src="../js/jquery.min.js"></script>
<script src="../js/jquery.easyui.min.js"></script>
<script src="../js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectList",
method: "get",
striped: true,
fitColumns: true,
pagination: true,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
此时,页面好像没有什么区别,好像也没有分页。其实,页面已经变了,只是在当前页面可浏览范围之外,页面滚动到末尾,会发现表格底部多了一个分页栏。
并且,请求携带了额外的参数page和rows
height属性
基于上面的的问题(需要拉到页面底部才能看到分页栏),现在给表格设置一个固定的高度,让它刚好够显示20条数据。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="css/themes/icon.css" />
<link rel="stylesheet" href="css/themes/material/easyui.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.easyui.min.js"></script>
<script src="js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
let pageList = [20, 50, 100, 500, 1000];
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectList",
method: "get",
fitColumns: true,
striped: true,
height: 710,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
再次查看页面效果
五、构建应用
1、分页功能
上个章节,已经完成了基本的页面样式的调整,但是能发现,其实并没有分页,这是因为后端没有处理easyui框架传的两个参数page和rows。
这个部分首先需要解决的就是这个问题,要使用mybatis-plus的分页功能,需要添加分页插件。
后端代码
MybatisPlusConfig
config包下新增mybatis-plus配置类MybatisPlusConfig,添加mybatis-plus分页插件。
package com.example.springboot.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Mybatis-Plus配置类
* @author heyunlin
* @version 1.0
*/
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 防全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
JsonResult
JsonResult添加restPage()方法
public static JsonResult<Map<String, Object>> restPage(Page<Song> page) {
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("total", page.getTotal());
resultMap.put("rows", page.getRecords());
return success("请求成功", resultMap);
}
完整的代码JsonResult.java
package com.example.springboot.restful;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springboot.entity.Song;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 响应对象
* @author heyunlin
* @version 1.0
*/
@Data
public class JsonResult<T> {
/**
* 响应状态码
*/
private Integer code;
/**
* 响应提示信息
*/
private String message;
/**
* 响应数据
*/
private T data;
public static JsonResult<Void> success() {
return success(null);
}
public static JsonResult<Void> success(String message) {
return success(message, null);
}
public static <T> JsonResult<T> success(String message, T data) {
JsonResult<T> jsonResult = new JsonResult<>();
jsonResult.setCode(ResponseCode.OK.getValue());
jsonResult.setMessage(message);
jsonResult.setData(data);
return jsonResult;
}
public static JsonResult<Void> error(String message) {
JsonResult<Void> jsonResult = new JsonResult<>();
jsonResult.setCode(ResponseCode.ERROR.getValue());
jsonResult.setMessage(message);
return jsonResult;
}
public static JsonResult<Void> error(ResponseCode responseCode, Throwable e) {
return error(responseCode, e.getMessage() != null ? e.getMessage() : "系统发生异常,请联系管理员!");
}
public static JsonResult<Void> error(ResponseCode responseCode, String message) {
JsonResult<Void> jsonResult = new JsonResult<>();
jsonResult.setCode(responseCode.getValue());
jsonResult.setMessage(message);
return jsonResult;
}
public static JsonResult<Map<String, Object>> restPage(Page<Song> page) {
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("total", page.getTotal());
resultMap.put("rows", page.getRecords());
return success("请求成功", resultMap);
}
}
Pager
定义一个包含分页参数的对象Pager,继承自该类的实体类就拥有了这两个属性。
package com.example.springboot.base;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
/**
* 基础分页参数对象,包含页数和每页的记录数
* @author heyunlin
* @version 1.0
*/
@Data
public class Pager<T> {
/**
* 页数
*/
private Integer page = 1;
/**
* 每页记录数
*/
private Integer rows = 10;
/**
* 根据Pager创建Page对象
* @param pager Pager
* @return Page
*/
public static <T> Page<T> ofPage(Pager<T> pager) {
return new Page<>(pager.getPage(), pager.getRows());
}
}
SongController
SongController中添加一个selectByPage()接口方法
package com.example.springboot.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongPagerDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import com.example.springboot.restful.JsonResult;
import com.example.springboot.service.SongService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
/**
* @author heyunlin
* @version 1.0
*/
@RestController
@RequestMapping(path = "/song", produces="application/json;charset=utf-8")
public class SongController {
private final SongService songService;
@Autowired
public SongController(SongService songService) {
this.songService = songService;
}
@RequestMapping(value = "/insert", method = RequestMethod.POST)
public JsonResult<Void> insert(@Validated SongInsertDTO insertDTO) {
songService.insert(insertDTO);
return JsonResult.success("添加成功");
}
@RequestMapping(value = "/deleteById/{id}", method = RequestMethod.GET)
public JsonResult<Void> deleteById(@PathVariable("id") String id) {
songService.deleteById(id);
return JsonResult.success("删除成功");
}
@RequestMapping(value = "/updateById", method = RequestMethod.POST)
public JsonResult<Void> updateById(@Valid SongUpdateDTO updateDTO) {
songService.updateById(updateDTO);
return JsonResult.success("修改成功");
}
@RequestMapping(value = "/selectById/{id}", method = RequestMethod.GET)
public Song selectById(@PathVariable("id") String id) {
return songService.selectById(id);
}
@RequestMapping(value = "/selectList", method = RequestMethod.GET)
public JsonResult<List<Song>> selectList() {
List<Song> list = songService.selectList();
return JsonResult.success("查询成功", list);
}
@RequestMapping(value = "/selectByPage", method = RequestMethod.POST)
public JsonResult<Map<String, Object>> selectByPage(SongPagerDTO songPagerDTO) {
Page<Song> page = songService.selectByPage(songPagerDTO);
return JsonResult.restPage(page);
}
}
SongService
定义一个对应的抽象方法selectByPage()
package com.example.springboot.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongPagerDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
public interface SongService {
void insert(SongInsertDTO songInsertDTO);
void deleteById(String id);
void updateById(SongUpdateDTO updateDTO);
Song selectById(String id);
List<Song> selectList();
Page<Song> selectByPage(SongPagerDTO songPagerDTO);
}
SongServiceImpl
在实现类中实现分页查询功能
package com.example.springboot.service.impl;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springboot.base.Pager;
import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongPagerDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import com.example.springboot.mapper.SongMapper;
import com.example.springboot.service.SongService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
@Service
public class SongServiceImpl implements SongService {
private final SongMapper songMapper;
@Autowired
public SongServiceImpl(SongMapper songMapper) {
this.songMapper = songMapper;
}
@Override
public void insert(SongInsertDTO insertDTO) {
Song song = new Song();
song.setId(uuid());
song.setName(insertDTO.getName());
song.setSinger(insertDTO.getSinger());
if (StringUtils.isNotEmpty(insertDTO.getNote())) {
song.setNote(insertDTO.getNote());
}
songMapper.insert(song);
}
@Override
public void deleteById(String id) {
songMapper.deleteById(id);
}
@Override
public void updateById(SongUpdateDTO updateDTO) {
Song song = new Song();
song.setId(updateDTO.getId());
song.setName(updateDTO.getName());
song.setSinger(updateDTO.getSinger());
if (StringUtils.isNotEmpty(updateDTO.getNote())) {
song.setNote(updateDTO.getNote());
}
song.setLastUpdateTime(LocalDateTime.now());
songMapper.updateById(song);
}
@Override
public Song selectById(String id) {
return songMapper.selectById(id);
}
@Override
public List<Song> selectList() {
return songMapper.selectList(null);
}
@Override
public Page<Song> selectByPage(SongPagerDTO songPagerDTO) {
Page<Song> page = Pager.ofPage(songPagerDTO);
return songMapper.selectPage(page, null);
}
/**
* 根据当前时间生成UUID
* @return String
*/
private static String uuid() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
LocalDateTime localDate = LocalDateTime.now();
return localDate.format(formatter);
}
}
前端代码
我们把请求数据的接口改成/song/selectByPage,删除methid属性,使用默认的post请求。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="css/themes/icon.css" />
<link rel="stylesheet" href="css/themes/material/easyui.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.easyui.min.js"></script>
<script src="js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
let pageList = [20, 50, 100, 500, 1000];
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectByPage",
fitColumns: true,
striped: true,
height: 710,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
至此,分页功能完成~
2、增删改功能
一般页面的js代码和html是分离的,在这个部分,为了方便修改文件,把js代码单独保存到一个js文件,然后通过外部引入的方式引入。
接下来来实现数据的增、删、改功能。
给表格添加头部工具栏,新增添加、修改、删除三个按钮。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>datagrid案例代码</title>
<link rel="stylesheet" href="css/themes/icon.css" />
<link rel="stylesheet" href="css/themes/material/easyui.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.easyui.min.js"></script>
<script src="js/easyui-lang-zh_CN.js"></script>
</head>
<body>
<div id="song_list"></div>
<script>
let pageList = [20, 50, 100, 500, 1000];
function addHandler() {
// todo
}
function editHandler() {
// todo
}
function deleteHandler() {
// todo
}
$(document).ready(function() {
$("#song_list").datagrid({
url: "http://localhost:8083/song/selectByPage",
title: "歌曲列表",
fitColumns: true,
striped: true,
height: 777,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
</script>
</body>
</html>
添加、修改功能
这个部分的功能通过选中一行数据,并点击修改按钮的事件分为有两种实现方案。
具体实现思路:根据点击的按钮动态设置请求地址requestUrl(添加/修改),在对话框中添加一个保存按钮,点击保存按钮则提交post请求到requestUrl,完成数据保存
- 方案一:把行数据填充到一个对话框内包含的表单中,点击对话框中的保存按钮把修改后的数据提交到后台保存数据;
- 方案二:开启行内编辑,在表格的行内编辑数据,在表格头部具栏增加两个按钮:保存和取消。
接下来就介绍两种实现方案的具体实现。
基于对话框
复制一份index.html,命名为easyui-crud-dialog.html
创建表单
既然是基于对话框,就要先提前创建好一个对话框,在对话框中嵌套一个表单,表单内定义需要通过前端提交的歌曲信息。
修改easyui-crud-dialog.html,添加一个div作为对话框
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>easyui crud应用-基于对话框</title>
<link rel="stylesheet" href="../css/themes/icon.css" />
<link rel="stylesheet" href="../css/themes/default/easyui.css" />
<script src="../js/jquery.min.js"></script>
<script src="../js/jquery.easyui.min.js"></script>
<script src="../js/easyui-lang-zh_CN.js"></script>
<script src="../js/easyui-crud-dialog.js"></script>
</head>
<body>
<table id="song_list"></table>
<div id="song_dialog" style="display:none;">
<form id="song_form">
<input type="hidden" id="id" name="id" />
<table>
<tr>
<td>name:</td>
<td><input id="name" name="name" /></td>
<td>singer:</td>
<td><input id="singer" name="singer" /></td>
</tr>
<tr>
<td>note:</td>
<td colspan="3"><input id="note" name="note" /></td>
</tr>
</table>
</form>
</div>
</body>
</html>
如上所示,通过一个隐藏域保存歌曲的id字段的值,只有修改的时候才需要提交这个id字段。
表单渲染
js/easyui-crud-dialog.js
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];
function addHandler() {
// todo
}
function editHandler() {
// todo
}
function deleteHandler() {
// todo
}
$(document).ready(function() {
$('#name').textbox({
width: 150,
required: true
});
$('#singer').textbox({
width: 150,
required: true
});
$('#note').textbox({
width: 366,
height: 100,
required: true,
multiline: true
});
$('#song_dialog').dialog({
title: '歌曲信息',
closed: true,
cache: false,
modal: true,
toolbar:[{
text: '保存',
iconCls: 'icon-save',
handler: function() {
// todo
}
}, {
text: '取消',
iconCls: 'icon-cancel',
handler: function() {
// todo
}
}]
});
$("#song_list").datagrid({
url: base + "/song/selectByPage",
title: "歌曲列表",
fitColumns: true,
striped: true,
height: 777,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
添加、修改功能
所有都准备好了,接下来开始实现具体的功能。添加一个全局属性requestUrl,这个requestUrl属性会根据点击不同的按钮动态地设置。
- 点击添加按钮时,设置为/song/insert
- 点击修改按钮时,设置为/song/updateById
在点击添加和修改按钮时,都要打开对话框,修改的时候需要填充表格数据到表单对应字段的输入框中。
js/index.js
let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];
function addHandler() {
requestUrl = "/song/insert";
$('#song_dialog').dialog('open');
}
function editHandler() {
let row = $("#song_list").datagrid('getSelected');
if(row) {
requestUrl = "/song/updateById";
$('#id').val(row.id);
$('#name').textbox('setValue', row.name);
$('#singer').textbox('setValue', row.singer);
$('#note').textbox('setValue', row.note);
$('#song_dialog').dialog('open');
} else {
$.messager.alert("系统提示", "请选择要修改的数据!", "warning");
}
}
function deleteHandler() {
// todo
}
$(document).ready(function() {
$('#name').textbox({
width: 150,
required: true
});
$('#singer').textbox({
width: 150,
required: true
});
$('#note').textbox({
width: 366,
height: 100,
required: true,
multiline: true
});
$('#song_dialog').dialog({
title: '歌曲信息',
closed: true,
cache: false,
modal: true,
toolbar:[{
text: '保存',
iconCls: 'icon-save',
handler: function() {
// todo
}
}, {
text: '取消',
iconCls: 'icon-cancel',
handler: function() {
// todo
}
}]
});
$("#song_list").datagrid({
url: base + "/song/selectByPage",
title: "歌曲列表",
fitColumns: true,
striped: true,
height: 777,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
点击对话框的保存按钮,会请求requestUrl,把表单的数据提高到后台接口;如果请求正确返回了,则提示操作结果,清空表单数据,并关闭对话框。
点击对话框的取消按钮,则清空表单数据,并关闭对话框。
let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];
function addHandler() {
requestUrl = "/song/insert";
$('#song_dialog').dialog('open');
}
function editHandler() {
let row = $("#song_list").datagrid('getSelected');
if(row) {
requestUrl = "/song/updateById";
$('#id').val(row.id);
$('#name').textbox('setValue', row.name);
$('#singer').textbox('setValue', row.singer);
$('#note').textbox('setValue', row.note);
$('#song_dialog').dialog('open');
} else {
$.messager.alert("系统提示", "请选择要修改的数据!", "warning");
}
}
function deleteHandler() {
// todo
}
$(document).ready(function() {
$('#name').textbox({
width: 150,
required: true
});
$('#singer').textbox({
width: 150,
required: true
});
$('#note').textbox({
width: 366,
height: 100,
required: true,
multiline: true
});
$('#song_dialog').dialog({
title: '歌曲信息',
closed: true,
cache: false,
modal: true,
buttons: [{
text: '保存',
iconCls: 'icon-save',
handler: function() {
let bool = $("#song_form").form("validate");
if (bool) {
let data = $("#song_form").serialize();
$.post(base + requestUrl, data, function(res) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$('#song_dialog').dialog('close');
$("#song_list").datagrid("reload");
}, "json");
} else {
$.messager.alert("系统提示", "请填写正确的表单项", "warning");
}
}
}, {
text: '取消',
iconCls: 'icon-cancel',
handler: function() {
$("#song_form").form('clear');
$('#song_dialog').dialog('close');
}
}]
});
$("#song_list").datagrid({
url: base + "/song/selectByPage",
title: "歌曲列表",
fitColumns: true,
striped: true,
height: 777,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
至此,基于对话框实现的添加和修改功能就完成了~
基于行内编辑
前面已经实现了基于对话框内镶嵌一个form表单实现了数据的添加、修改功能,这个部分,会通过更高级的方式来实现,在表格的行内开启编辑。
准备工作
项目的根目录的html目录下创建一个easyui-crud-inline.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>easyui crud应用-基于行内编辑</title>
<link rel="stylesheet" href="../css/themes/icon.css" />
<link rel="stylesheet" href="../css/themes/default/easyui.css" />
<script src="../js/jquery.min.js"></script>
<script src="../js/jquery.easyui.min.js"></script>
<script src="../js/easyui-lang-zh_CN.js"></script>
<script src="../js/easyui-crud-inline.js"></script>
</head>
<body>
<table id="song_list"></table>
</body>
</html>
js目录下创建easyui-crud-inline.js
let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];
function addHandler() {
// todo
}
function editHandler() {
// todo
}
function saveHandler() {
// todo
}
function cancelHandler() {
// todo
}
function deleteHandler() {
// todo
}
$(document).ready(function() {
$("#song_list").datagrid({
url: base + "/song/selectByPage",
title: "歌曲列表",
fitColumns: true,
striped: true,
height: 777,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
},
}, "-", {
iconCls: "icon-save",
text: "保存",
handler: function() {
saveHandler();
}
}, "-", {
iconCls: "icon-cancel",
text: "取消",
handler: function() {
cancelHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200},
{field: 'singer', title: 'singer', width: 200},
{field: 'note', title: 'note', width: 200},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200},
]]
});
});
添加功能
添加功能的实现思路相对修改功能较为简单,点击添加按钮,会请求后端接口,插入一条默认的数据。
function addHandler() {
requestUrl = "/song/insert";
$.post(base + requestUrl, {
name: "*****",
singer: "*****",
note: "*****"
}, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
修改功能
这个功能相对复杂,需要依赖easyui datagrid的行编辑结束事件。
定义JSON对象保存修改后的数据
// 定义一个json对象保存歌曲数据
let data = {};
给歌曲列表添加表格行结束编辑事件
onAfterEdit: function (rowIndex, rowData, changes) { // 结束行内编辑事件
data = {
id: rowData.id,
name: changes.name ? changes.name : rowData.name,
note: changes.note ? changes.note : rowData.note,
singer: changes.singer ? changes.singer : rowData.singer
};
},
实现修改功能
function editHandler() {
let datagrid = $("#song_list");
if (editingId != null && editingId !== "") {
datagrid.datagrid("selectRow", editingId);
} else {
let row = datagrid.datagrid("getSelected");
if (row) {
// 获取行索引,这个索引从0开始
let rowIndex = datagrid.datagrid("getRowIndex", row);
editingId = rowIndex;
requestUrl = "/song/updateById";
datagrid.datagrid("beginEdit", rowIndex);
}
}
}
其他功能
接下来就剩下保存和取消了
保存
function saveHandler() {
if (editingId) {
// 只有结束编辑才能获取到最新的值
$("#song_list").datagrid("endEdit", editingId);
$.post(base + requestUrl, data, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
editingId = "";
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
}
取消
function cancelHandler() {
// editingId != null条件防止刷新页面带来的问题
if (editingId != null && editingId !== "") {
$("#song_list").datagrid("cancelEdit", editingId);
editingId = "";
}
}
到了这里,基于行内编辑的表格数据的添加、修改功能就完成了~
let editingId;
let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];
let data = {};
function addHandler() {
requestUrl = "/song/insert";
$.post(base + requestUrl, {
name: "*****",
singer: "*****",
note: "*****"
}, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
function editHandler() {
let datagrid = $("#song_list");
if (editingId != null && editingId !== "") {
datagrid.datagrid("selectRow", editingId);
} else {
let row = datagrid.datagrid("getSelected");
if (row) {
// 获取行索引,这个索引从0开始
let rowIndex = datagrid.datagrid("getRowIndex", row);
editingId = rowIndex;
requestUrl = "/song/updateById";
datagrid.datagrid("beginEdit", rowIndex);
}
}
}
function saveHandler() {
if (editingId != null && editingId !== "") {
// 只有结束编辑才能获取到最新的值
$("#song_list").datagrid("endEdit", editingId);
$.post(base + requestUrl, data, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
editingId = "";
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
}
function cancelHandler() {
// editingId != null条件防止刷新页面带来的问题
if (editingId != null && editingId !== "") {
$("#song_list").datagrid("cancelEdit", editingId);
editingId = "";
}
}
function exportHandler() {
location.href = base + "/song/export";
}
function deleteHandler() {
let rowData = $("#song_list").datagrid("getSelected");
if (rowData) {
$.messager.confirm("系统确认", "删除后数据无法恢复,是否确认删除?", function(bool) {
if (bool) {
$.get(base + "/song/deleteById/" + rowData.id, {}, function(res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
});
} else {
$.messager.alert("系统提示", "请选择要删除的数据!", "warning");
}
}
$(document).ready(function() {
let datagrid = $("#song_list").datagrid({
url: base + "/song/selectByPage",
title: "歌曲列表",
fitColumns: true,
striped: true,
height: 810,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
remoteFilter: true,
clientPaging: false,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
onAfterEdit: function (rowIndex, rowData, changes) { // 结束行内编辑事件
data = {
id: rowData.id,
name: changes.name ? changes.name : rowData.name,
note: changes.note ? changes.note : rowData.note,
singer: changes.singer ? changes.singer : rowData.singer
};
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
},
}, "-", {
iconCls: "icon-save",
text: "保存",
handler: function() {
saveHandler();
}
}, "-", {
iconCls: "icon-cancel",
text: "取消",
handler: function() {
cancelHandler();
}
}, '-', {
iconCls: 'icon-ok',
text: '导出',
handler: function() {
exportHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}, '-', {
iconCls: 'icon-reload',
text: '切换版本',
handler: function() {
location.href = "./easyui-crud-dialog.html";
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200, editor: "textbox"},
{field: 'singer', title: 'singer', width: 200, editor: "textbox"},
{field: 'note', title: 'note', width: 200, editor: "textbox"},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200}
]]
});
datagrid.datagrid('enableFilter', [{
field: 'name',
type: 'textbox',
op: ['equal', 'contains']
}, {
field: 'singer',
type: 'textbox',
op: ['equal', 'contains'],
}, {
field: 'note',
type: 'textbox',
op: ['equal', 'contains']
}]);
});
删除功能
删除功能功能很简单,选中一行,点击删除按钮即可,删除之前弹出询问框,点击确定就会发送ajax get请求到后台接口,完成数据删除,删除完成后接口正常返回200,则刷新表格。
function deleteHandler() {
let rowData = $("#song_list").datagrid("getSelected");
if (rowData) {
$.messager.confirm("系统确认", "删除后数据无法恢复,是否确认删除?", function(bool) {
if (bool) {
$.get(base + "/song/deleteById/" + rowData.id, {}, function(res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
});
} else {
$.messager.alert("系统提示", "请选择要删除的数据!", "warning");
}
}
3、条件查询功能
最后,要实现表格数据的条件查询功能,在这里通过表格过滤来实现这个功能,需要引入额外的easyui插件。
更多详情可参考以下链接:
EasyUI 数据网格行过滤(DataGrid Filter Row)https://www.jeasyui.net/extension/192.html访问以上地址,页面拉到底部,把这个压缩包下载下来,我们需要它里面的datagrid-filter.js文件。
前端代码
博主下载下来之后,花了点时间看了一下datagrid-filter.js的源代码,发现一个关键的属性remoteFilter,意思是远程过滤,而通过页面的说明,发现这个属性的默认值为false。
根据我使用easyui的经验猜测,如果设置成true,应该会在加载表格数据的时候额外发送请求参数。于是照着葫芦画瓢,给歌曲列表开启行过滤,并设置remoteFilter属性值为true。
let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];
function addHandler() {
requestUrl = "/song/insert";
$.post(base + requestUrl, {
name: "*****",
singer: "*****",
note: "*****"
}, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
function editHandler() {
let datagrid = $("#song_list");
if (editingId != null && editingId !== "") {
datagrid.datagrid("selectRow", editingId);
} else {
let row = datagrid.datagrid("getSelected");
if (row) {
// 获取行索引,这个索引从0开始
let rowIndex = datagrid.datagrid("getRowIndex", row);
editingId = rowIndex;
requestUrl = "/song/updateById";
datagrid.datagrid("beginEdit", rowIndex);
}
}
}
function saveHandler() {
if (editingId) {
// 只有结束编辑才能获取到最新的值
$("#song_list").datagrid("endEdit", editingId);
$.post(base + requestUrl, data, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
editingId = "";
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
}
function cancelHandler() {
// editingId != null条件防止刷新页面带来的问题
if (editingId != null && editingId !== "") {
$("#song_list").datagrid("cancelEdit", editingId);
editingId = "";
}
}
function deleteHandler() {
let rowData = $("#song_list").datagrid("getSelected");
if (rowData) {
$.messager.confirm("系统确认", "删除后数据无法恢复,是否确认删除?", function(bool) {
if (bool) {
$.get(base + "/song/deleteById/" + rowData.id, {}, function(res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
});
} else {
$.messager.alert("系统提示", "请选择要删除的数据!", "warning");
}
}
$(document).ready(function() {
let datagrid = $("#song_list").datagrid({
url: base + "/song/selectByPage",
title: "歌曲列表",
fitColumns: true,
striped: true,
height: 777,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
remoteFilter: true,
clientPaging: false,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
},
}, "-", {
iconCls: "icon-save",
text: "保存",
handler: function() {
saveHandler();
}
}, "-", {
iconCls: "icon-cancel",
text: "取消",
handler: function() {
cancelHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200, editor: "textbox"},
{field: 'singer', title: 'singer', width: 200, editor: "textbox"},
{field: 'note', title: 'note', width: 200, editor: "textbox"},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200}
]]
});
datagrid.datagrid('enableFilter', [{
field: 'name',
type: 'textbox',
op: ['equal', 'contains']
}, {
field: 'singer',
type: 'textbox',
op: ['equal', 'contains'],
}, {
field: 'note',
type: 'textbox',
op: ['equal', 'contains']
}]);
});
果然,刷新页面,发现发送的请求中多了一个filterRules参数。
后端代码
FilterRule
于是在后端创建与之对应的java实体类FilterRule
package com.example.springboot.base;
import com.example.springboot.enums.Operator;
import lombok.Data;
/**
* 过滤规则
* @author heyunlin
* @version 1.0
*/
@Data
public class FilterRule {
/**
* 字段名
*/
private String field;
/**
* 比较符
*/
private Operator op;
/**
* 字段值
*/
private String value;
}
Operator
考虑到这里过滤的比较符有多个,创建一个枚举来保存所有比较符。
package com.example.springboot.enums;
/**
* 比较符
* @author heyunlin
* @version 1.0
*/
public enum Operator {
/**
* 包含
*/
contains,
/**
* 等于
*/
equal,
/**
* 不等于
*/
notequal,
/**
* 以...开始
*/
beginwith,
/**
* 以...结尾
*/
endwith,
/**
* 小于
*/
less,
/**
* 小于或等于
*/
lessorequal,
/**
* 大于
*/
greater,
/**
* 大于或等于
*/
greaterorequal
}
Pager
最后在Pager类上额外添加一个filterRules属性,让controller接口接收前端传递的filterRules参数,并将其转换为List<FilterRule>类型。
package com.example.springboot.base;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 基础分页参数对象,包含页数和每页的记录数
* @author heyunlin
* @version 1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Pager<T> extends Sorter {
/**
* 页数
*/
private Integer page = 1;
/**
* 每页记录数
*/
private Integer rows = 10;
/**
* 过滤规则
*/
private List<FilterRule> filterRules;
/**
* 根据Pager创建Page对象
* @param pager Pager
* @return Page<T>
*/
public static <T> Page<T> ofPage(Pager<T> pager) {
return new Page<>(pager.getPage(), pager.getRows());
}
}
测试请求一下,发现请求返回400状态码,参数转换异常。
具体的错误提示如下:不能从String类型转换到List<FilterRule>类型,这也是意料之中的事。
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'songPagerDTO' on field 'filterRules': rejected value [[{"field":"name","op":"contains","value":"宠坏"}]]; codes [typeMismatch.songPagerDTO.filterRules,typeMismatch.filterRules,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [songPagerDTO.filterRules,filterRules]; arguments []; default message [filterRules]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'filterRules'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.springboot.base.FilterRule' for property 'filterRules[0]': no matching editors or conversion strategy found]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:175)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
StringToListOfFilterRuleConverter
于是添加一个类型转换器,完成String到List<FilterRule>的转换。这里使用alibaba的fastjson来实现。
添加fastjson依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.9</version>
<relativePath />
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-crud</name>
<description>springboot+mybatis+mybatis-plus实现增删查改项目</description>
<properties>
<java.version>1.8</java.version>
<mysql.version>8.0.28</mysql.version>
<druid.version>1.1.21</druid.version>
<lombok.version>1.18.22</lombok.version>
<mybatis.version>2.2.2</mybatis.version>
<fastjson.version>2.0.8</fastjson.version>
<mybatis-plus.version>3.5.1</mybatis-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--lombok:自动生成getter、setter工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--spring boot整合mybatis的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--spring boot整合mybatis-plus的依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
StringToListOfFilterRuleConverter.java
package com.example.springboot.base;
import com.alibaba.fastjson.JSON;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
@Component
public class StringToListOfFilterRuleConverter implements Converter<String, List<FilterRule>> {
@Override
public List<FilterRule> convert(String source) {
return JSON.parseArray(source, FilterRule.class);
}
}
完成以上修改之后,请求总算是通了,正常返回了数据。
Pager
最后,在后端实现数据过滤功能,根据filterRules参数动态添加查询条件。在Pager类值统一处理过滤
package com.example.springboot.base;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import java.util.List;
/**
* 基础分页参数对象,包含页数和每页的记录数
* @author heyunlin
* @version 1.0
*/
@Data
public class Pager<T> {
/**
* 页数
*/
private Integer page = 1;
/**
* 每页记录数
*/
private Integer rows = 10;
/**
* 过滤规则
*/
private List<FilterRule> filterRules;
/**
* 根据Pager创建Page对象
* @param pager Pager
* @return Page
*/
public static <T> Page<T> ofPage(Pager<T> pager) {
return new Page<>(pager.getPage(), pager.getRows());
}
/**
* 根据Pager创建QueryWrapper对象
* @param pager Pager
* @return QueryWrapper<T>
*/
public static <T> QueryWrapper<T> getQueryWrapper(Pager<T> pager) {
List<FilterRule> filterRules = pager.getFilterRules();
if (filterRules != null && !filterRules.isEmpty()) {
QueryWrapper<T> wrapper = new QueryWrapper<>();
for (FilterRule filterRule : filterRules) {
// 字段名:转为小写字母+下划线的格式
String field = toLower(filterRule.getField());
// 字段值
String value = filterRule.getValue();
switch (filterRule.getOp()) {
case less:
wrapper.lt(field, value);
break;
case equal:
wrapper.eq(field, value);
break;
case greater:
wrapper.gt(field, value);
break;
case notequal:
wrapper.ne(field, value);
break;
case lessorequal:
wrapper.le(field, value);
break;
case greaterorequal:
wrapper.ge(field, value);
break;
case beginwith:
wrapper.likeLeft(field, value);
break;
case endwith:
wrapper.likeRight(field, value);
break;
case contains:
wrapper.like(field, value);
break;
default:
break;
}
}
return wrapper;
}
return null;
}
/**
* 驼峰命名转下划线命名
* @param str 待转换的字符串
* @return String
*/
private static String toLower(String str) {
// 小写和大写紧挨一起的地方加上分隔符_,然后全部转为小写
str = str.replaceAll("([a-z])([A-Z])", "$1_$2");
return str.toLowerCase();
}
}
SongServiceImpl
最后修改一下selectByPage()方法的具体实现,从Pager类获取QueryWrapper对象,而不是自己构建。
package com.example.springboot.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springboot.base.Pager;
import com.example.springboot.dto.SongInsertDTO;
import com.example.springboot.dto.SongPagerDTO;
import com.example.springboot.dto.SongUpdateDTO;
import com.example.springboot.entity.Song;
import com.example.springboot.exception.GlobalException;
import com.example.springboot.mapper.SongMapper;
import com.example.springboot.restful.ResponseCode;
import com.example.springboot.service.SongService;
import com.example.springboot.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
@Service
public class SongServiceImpl implements SongService {
private final SongMapper songMapper;
@Autowired
public SongServiceImpl(SongMapper songMapper) {
this.songMapper = songMapper;
}
// ...其它代码
@Override
public Page<Song> selectByPage(SongPagerDTO songPagerDTO) {
QueryWrapper<Song> wrapper = Pager.getQueryWrapper(songPagerDTO);
Page<Song> page = Pager.ofPage(songPagerDTO);
return songMapper.selectPage(page, wrapper);
}
}
4、字段排序功能
easyui同样支持字段排序功能,而且支持多个字段的排序。
修改前端的代码,在需要排序的列后面增加sortable: true的选项
例如,开启lastUpdateTime字段的排序,排序时请求会额外添加两个参数sort和order。
当多个字段同时排序时,sort的值为多个字段通过逗号拼接成的字符串,order的值为多个字段的排序方式通过逗号拼接成的字符串。 比如:
{
sort: "name,singer",
order: "asc,desc"
}
前端代码
在表格的lastUpdateTime字段选项添加sortable: true
let editingId;
let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];
let data = {};
function addHandler() {
requestUrl = "/song/insert";
$.post(base + requestUrl, {
name: "*****",
singer: "*****",
note: "*****"
}, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
function editHandler() {
let datagrid = $("#song_list");
if (editingId != null && editingId !== "") {
datagrid.datagrid("selectRow", editingId);
} else {
let row = datagrid.datagrid("getSelected");
if (row) {
// 获取行索引,这个索引从0开始
let rowIndex = datagrid.datagrid("getRowIndex", row);
editingId = rowIndex;
requestUrl = "/song/updateById";
datagrid.datagrid("beginEdit", rowIndex);
}
}
}
function saveHandler() {
if (editingId != null && editingId !== "") {
// 只有结束编辑才能获取到最新的值
$("#song_list").datagrid("endEdit", editingId);
$.post(base + requestUrl, data, function (res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
editingId = "";
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
}
function cancelHandler() {
// editingId != null条件防止刷新页面带来的问题
if (editingId != null && editingId !== "") {
$("#song_list").datagrid("cancelEdit", editingId);
editingId = "";
}
}
function exportHandler() {
location.href = base + "/song/export";
}
function deleteHandler() {
let rowData = $("#song_list").datagrid("getSelected");
if (rowData) {
$.messager.confirm("系统确认", "删除后数据无法恢复,是否确认删除?", function(bool) {
if (bool) {
$.get(base + "/song/deleteById/" + rowData.id, {}, function(res) {
if(res.code === 200) {
$.messager.show({
title: '系统消息',
timeout: 5000,
showType: 'slide',
msg: res.message,
});
$("#song_list").datagrid("reload");
} else {
$.messager.alert("系统提示", res.message, "error");
}
}, "json");
}
});
} else {
$.messager.alert("系统提示", "请选择要删除的数据!", "warning");
}
}
$(document).ready(function() {
let datagrid = $("#song_list").datagrid({
url: base + "/song/selectByPage",
title: "歌曲列表",
height: 810,
striped: true,
fitColumns: true,
singleSelect: true,
pagination: true,
pageSize: pageList[0],
pageList: pageList,
remoteFilter: true,
remoteSort: true,
multiSort: true,
clientPaging: false,
loadFilter: function(res) {
if (res.code === 200) {
return res.data;
} else {
return null;
}
},
onAfterEdit: function (rowIndex, rowData, changes) { // 结束行内编辑事件
data = {
id: rowData.id,
name: changes.name ? changes.name : rowData.name,
note: changes.note ? changes.note : rowData.note,
singer: changes.singer ? changes.singer : rowData.singer
};
},
toolbar: [{
iconCls: 'icon-add',
text: '添加',
handler: function() {
addHandler();
}
}, '-', {
iconCls: 'icon-edit',
text: '修改',
handler: function() {
editHandler();
},
}, "-", {
iconCls: "icon-save",
text: "保存",
handler: function() {
saveHandler();
}
}, "-", {
iconCls: "icon-cancel",
text: "取消",
handler: function() {
cancelHandler();
}
}, '-', {
iconCls: 'icon-ok',
text: '导出',
handler: function() {
exportHandler();
}
}, '-', {
iconCls: 'icon-delete',
text: '删除',
handler: function() {
deleteHandler();
}
}, '-', {
iconCls: 'icon-reload',
text: '切换版本',
handler: function() {
location.href = "./easyui-crud-dialog.html";
}
}],
columns: [[
{field: 'id', title: 'id', width: 200},
{field: 'name', title: 'name', width: 200, editor: "textbox"},
{field: 'singer', title: 'singer', width: 200, editor: "textbox"},
{field: 'note', title: 'note', width: 200, editor: "textbox"},
{field: 'lastUpdateTime', title: 'lastUpdateTime', width: 200, sortable: true}
]]
});
datagrid.datagrid('enableFilter', [{
field: 'name',
type: 'textbox',
op: ['equal', 'contains']
}, {
field: 'singer',
type: 'textbox',
op: ['equal', 'contains'],
}, {
field: 'note',
type: 'textbox',
op: ['equal', 'contains']
}]);
});
后端代码
根据请求的参数格式,定义一个Sorter类,该类中定义sort和order两个属性,并实现处理排序的方法。
Sorter
package com.example.springboot.base;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.springboot.exception.GlobalException;
import com.example.springboot.restful.ResponseCode;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 基础排序对象,包含排序字段和排序方式
* @author heyunlin
* @version 1.0
*/
@Data
public class Sorter {
/**
* 空字符串
*/
private static final String EMPTY_STR = "";
/**
* 分割符
*/
private static final String SEPARATOR = ",";
/**
* 排序方式
*/
private static final List<String> ORDER_STYLES = new ArrayList<>(2);
static {
ORDER_STYLES.add("asc");
ORDER_STYLES.add("desc");
}
/**
* 排序字段
*/
private String sort;
/**
* 排序方式:asc/desc
*/
private String order;
/**
* 根据查询条件拼接得到order by语句
* @param sorter 分页查询条件
* @return String
*/
public static String getStatement(Sorter sorter) {
String sort;
String sortColumn = sorter.getSort();
// 处理排序字段
String[] sortArray = {};
if (StringUtils.isNotEmpty(sortColumn)) {
// 驼峰命名转为下划线
sort = toLower(sortColumn);
if (sort.contains(SEPARATOR)) {
sortArray = sort.split(SEPARATOR);
}
} else {
return EMPTY_STR;
}
// 处理排序方式
String[] orderArray = {};
String order = sorter.getOrder();
if (StringUtils.isNotEmpty(order)) {
if (order.contains(SEPARATOR)) {
orderArray = order.split(SEPARATOR);
}
} else {
return EMPTY_STR;
}
StringBuilder statement = new StringBuilder();
if (sortArray.length > 0 && orderArray.length > 0) {
int length = sortArray.length;
for (int i = 0; i < length; i++) {
String pagerSort = sortArray[i];
String pagerOrder = orderArray[i];
boolean result = validate(pagerSort, pagerOrder);
if (result) {
statement.append(pagerSort);
statement.append(" ");
statement.append(pagerOrder);
if (i < length - 1 ) {
statement.append(", ");
}
}
}
} else {
// " #{sort} #{order}“
statement.append(sort);
statement.append(" ");
statement.append(order);
}
return statement.toString();
}
/**
* 根据查询条件拼接得到order by语句
* @param sorter 分页查询条件
* @return String
*/
public static String getOrderByStatement(Sorter sorter) {
String statement = getStatement(sorter);
if (StringUtils.isNotEmpty(statement)) {
return " order by " + statement;
} else {
return EMPTY_STR;
}
}
/**
* 往Pager的排序字段中添加排序
* @param pager Pager Pager对象
* @param sort String 排序字段
* @param order String 排序方式
* @return Pager<?> 返回重新设置排序字段和排序方式后的Pager对象
*/
public static Pager<?> append(Pager<?> pager, String sort, String order) {
boolean result = validatePager(pager);
if (result) {
String pagerSort = pager.getSort();
String pagerOrder = pager.getOrder();
pager.setSort(pagerSort.concat(SEPARATOR).concat(sort));
pager.setOrder(pagerOrder.concat(SEPARATOR).concat(order));
return pager;
}
return null;
}
/**
* 验证Pager对象的sort和order的值是否合法
* @param pager Pager<?>
* @return boolean
*/
private static boolean validatePager(Pager<?> pager) {
String sort = pager.getSort();
String order = pager.getOrder();
return validate(sort, order);
}
/**
* 验证sort和order的值是否合法
* @param sort 排序字段
* @param order 排序方式
* @return boolean
*/
private static boolean validate(String sort, String order) {
if (StringUtils.isEmpty(sort)) {
throw new GlobalException(ResponseCode.FORBIDDEN, "排序字段不允许为空!");
} else if (StringUtils.isEmpty(order)) {
throw new GlobalException(ResponseCode.FORBIDDEN, "排序方式不允许为空!");
} else if(!ORDER_STYLES.contains(order.toLowerCase())) {
throw new GlobalException(ResponseCode.FORBIDDEN, "排序方式不合法!");
}
return true;
}
/**
* 驼峰命名转下划线命名
* @param str 待转换的字符串
* @return String
*/
private static String toLower(String str) {
// 小写和大写紧挨一起的地方加上分隔符_,然后全部转为小写
str = str.replaceAll("([a-z])([A-Z])", "$1_$2");
return str.toLowerCase();
}
}
Pager
让Pager类继承自Sorter
package com.example.springboot.base;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 基础分页参数对象,包含页数和每页的记录数
* @author heyunlin
* @version 1.0
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class Pager<T> extends Sorter {
/**
* 页数
*/
private Integer page = 1;
/**
* 每页记录数
*/
private Integer rows = 10;
/**
* 过滤规则
*/
private List<FilterRule> filterRules;
/**
* 根据Pager创建Page对象
* @param pager Pager
* @return Page
*/
public static <T> Page<T> ofPage(Pager<T> pager) {
return new Page<>(pager.getPage(), pager.getRows());
}
/**
* 根据Pager创建QueryWrapper对象
* @param pager Pager
* @return QueryWrapper<T>
*/
public static <T> QueryWrapper<T> getQueryWrapper(Pager<T> pager) {
List<FilterRule> filterRules = pager.getFilterRules();
if (filterRules != null && !filterRules.isEmpty()) {
QueryWrapper<T> wrapper = new QueryWrapper<>();
for (FilterRule filterRule : filterRules) {
// 字段名:转为小写字母+下划线的格式
String field = toLower(filterRule.getField());
// 字段值
String value = filterRule.getValue();
switch (filterRule.getOp()) {
case less:
wrapper.lt(field, value);
break;
case equal:
wrapper.eq(field, value);
break;
case greater:
wrapper.gt(field, value);
break;
case notequal:
wrapper.ne(field, value);
break;
case lessorequal:
wrapper.le(field, value);
break;
case greaterorequal:
wrapper.ge(field, value);
break;
case beginwith:
wrapper.likeLeft(field, value);
break;
case endwith:
wrapper.likeRight(field, value);
break;
case contains:
wrapper.like(field, value);
break;
default:
break;
}
}
return wrapper;
}
return null;
}
/**
* 驼峰命名转下划线命名
* @param str 待转换的字符串
* @return String
*/
private static String toLower(String str) {
// 小写和大写紧挨一起的地方加上分隔符_,然后全部转为小写
str = str.replaceAll("([a-z])([A-Z])", "$1_$2");
return str.toLowerCase();
}
}
SongServiceImpl
最后,修改分页查询的实现代码,添加获取order by语句,并设置到查询条件的代码。
@Override
public Page<Song> selectByPage(SongPagerDTO songPagerDTO) {
QueryWrapper<Song> wrapper = Pager.getQueryWrapper(songPagerDTO);
Page<Song> page = Pager.ofPage(songPagerDTO);
// 得到order by语句
if (wrapper != null) {
String statement = Sorter.getOrderByStatement(songPagerDTO);
wrapper.last(statement);
}
return songMapper.selectPage(page, wrapper);
}
至此,字段排序功能也完成了,但是测试发现了一个小问题,并没有排序
,原来是因为Pager的getQueryWrapper()方法在没有过滤条件的时候会返回null,导致排序语句并没有设置进去。
Pager
于是再次修改Pager.getQueryWrapper(),让方法返回一个非空的对象。
package com.example.springboot.base;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 基础分页参数对象,包含页数和每页的记录数
* @author heyunlin
* @version 1.0
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class Pager<T> extends Sorter {
/**
* 页数
*/
private Integer page = 1;
/**
* 每页记录数
*/
private Integer rows = 10;
/**
* 过滤规则
*/
private List<FilterRule> filterRules;
/**
* 根据Pager创建Page对象
* @param pager Pager
* @return Page
*/
public static <T> Page<T> ofPage(Pager<T> pager) {
return new Page<>(pager.getPage(), pager.getRows());
}
/**
* 根据Pager创建QueryWrapper对象
* @param pager Pager
* @return QueryWrapper<T>
*/
public static <T> QueryWrapper<T> getQueryWrapper(Pager<T> pager) {
List<FilterRule> filterRules = pager.getFilterRules();
QueryWrapper<T> wrapper = new QueryWrapper<>();
if (filterRules != null && !filterRules.isEmpty()) {
for (FilterRule filterRule : filterRules) {
// 字段名:转为小写字母+下划线的格式
String field = toLower(filterRule.getField());
// 字段值
String value = filterRule.getValue();
switch (filterRule.getOp()) {
case less:
wrapper.lt(field, value);
break;
case equal:
wrapper.eq(field, value);
break;
case greater:
wrapper.gt(field, value);
break;
case notequal:
wrapper.ne(field, value);
break;
case lessorequal:
wrapper.le(field, value);
break;
case greaterorequal:
wrapper.ge(field, value);
break;
case beginwith:
wrapper.likeLeft(field, value);
break;
case endwith:
wrapper.likeRight(field, value);
break;
case contains:
wrapper.like(field, value);
break;
default:
break;
}
}
}
return wrapper;
}
/**
* 驼峰命名转下划线命名
* @param str 待转换的字符串
* @return String
*/
private static String toLower(String str) {
// 小写和大写紧挨一起的地方加上分隔符_,然后全部转为小写
str = str.replaceAll("([a-z])([A-Z])", "$1_$2");
return str.toLowerCase();
}
}
SongServiceImpl
同时,删除SongServiceImpl中null判断的代码
@Override
public Page<Song> selectByPage(SongPagerDTO songPagerDTO) {
QueryWrapper<Song> wrapper = Pager.getQueryWrapper(songPagerDTO);
Page<Song> page = Pager.ofPage(songPagerDTO);
// 得到order by语句
String statement = Sorter.getOrderByStatement(songPagerDTO);
wrapper.last(statement);
return songMapper.selectPage(page, wrapper);
}
至此,排序功能圆满完成~
代码改进
基于上面的功能,又设置了一个小小的改进,提高代码复用。
Pager
添加一个参数,控制要不要在这里处理排序。
为什么要设置这样一个参数呢?
- 提高扩展性:有些情况下未必在Pager的这个方法内就能确定需要通过哪些字段排序,比如我在某个查询接口想要设置默认的字段排序,而每个表的字段不相同,可能设置的默认排序字段就无法动态确定了,只有在实际业务的Service层代码中指定。
- 提高代码复用:当不需要设置默认排序时,enableSort设置为true即可,此时就实现了一个排序功能的统一处理。
/**
* 根据Pager创建QueryWrapper对象
* @param pager Pager
* @return QueryWrapper<T>
*/
public static <T> QueryWrapper<T> getQueryWrapper(Pager<T> pager, boolean enableSort) {
List<FilterRule> filterRules = pager.getFilterRules();
QueryWrapper<T> wrapper = new QueryWrapper<>();
if (filterRules != null && !filterRules.isEmpty()) {
for (FilterRule filterRule : filterRules) {
// 字段名:转为小写字母+下划线的格式
String field = toLower(filterRule.getField());
// 字段值
String value = filterRule.getValue();
switch (filterRule.getOp()) {
case less:
wrapper.lt(field, value);
break;
case equal:
wrapper.eq(field, value);
break;
case greater:
wrapper.gt(field, value);
break;
case notequal:
wrapper.ne(field, value);
break;
case lessorequal:
wrapper.le(field, value);
break;
case greaterorequal:
wrapper.ge(field, value);
break;
case beginwith:
wrapper.likeLeft(field, value);
break;
case endwith:
wrapper.likeRight(field, value);
break;
case contains:
wrapper.like(field, value);
break;
default:
break;
}
}
}
if (enableSort) {
// 得到order by语句
String statement = getOrderByStatement(pager);
wrapper.last(statement);
}
return wrapper;
}
SongServiceImpl
修改了Pager的方法,对应的selectByPage()代码也要多传一个参数。
并且删除排序的处理代码:
@Override
public Page<Song> selectByPage(SongPagerDTO songPagerDTO) {
QueryWrapper<Song> wrapper = Pager.getQueryWrapper(songPagerDTO, true);
Page<Song> page = Pager.ofPage(songPagerDTO);
return songMapper.selectPage(page, wrapper);
}
最后的页面效果如下:
好了,文章就分享到这里了,博主也是非常用心的创作,这篇文章总结了easyui实现数据管理所涉及的几乎所有常用功能,看完这篇文章不要忘了点赞+收藏哦~
end:前端项目地址如下,可按需获取~
使用easyui前端框架构建一个crud应用https://gitcode.net/heyl163_/easyui-crud.git