使用easyui前端框架构建一个crud应用(看完这篇就够了)

news2024/11/19 1:56:33

easyui是博主最喜欢的前端框架,因为它提供了多种主题样式和各种好看的ui组件。

这篇文章将会详细地介绍easyui前端框架的使用,通过创建一个crud应用来帮助大家快速掌握easyui的使用。

一、快速开始

这个章节主要介绍easyui前端框架的下载,easyui的官网地址如下:

JQuery EasyUI中文网icon-default.png?t=N7T8https://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实现简单的增、删、查、改icon-default.png?t=N7T8https://blog.csdn.net/heyl163_/article/details/132197201

springboot+mybatis+mybatis-plus对crud项目进行改进icon-default.png?t=N7T8https://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)icon-default.png?t=N7T8https://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应用icon-default.png?t=N7T8https://gitcode.net/heyl163_/easyui-crud.git

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode—680.验证回文串II【简单】

2023每日刷题&#xff08;二十七&#xff09; Leetcode—680.验证回文串II 实现代码 class Solution { public:bool judgeFunc(string s, int left, int right) {while(left < right) {if(s[left] ! s[right]) {return false;}left;right--;}return true;}bool validPalin…

按键精灵中的字符串常用的场景

在使用按键精灵编写脚本时&#xff0c;与字符串有关的场景有以下几种&#xff1a; 1. 用时间字符串记录脚本使用截止使用时间 Dim localTime "2023-11-12 00:15:14" Dim networkTime GetNetworkTime() TracePrint networkTime If networkTime > localTime The…

LeetCode200.岛屿数量

看完题目我还感觉这道题目有点难&#xff0c;没想到20分钟不到就完全靠自己给写出来了。我就是按照自己的想法来&#xff0c;我用一个等大的visit数组来表示grid数组中的这个元素是否被访问过&#xff08;是否已经被判断了是不是岛屿&#xff09;。 先用一个大的循环对grid数组…

threejs(12)-着色器打造烟雾水云效果

一、自己封装水波纹效果 src/main/main01.js import * as THREE from "three";import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import gsap from "gsap"; import * as dat from "dat.gui"; import ver…

解决springboot接受buffer文件为null(从picgo上传buffer看springmvc处理过程)

1. 前言&#xff1a; picgo插件的简单开发 上篇文章我们简单写了picgo上传插件&#xff0c;但是当我们测试的时候&#xff0c;发现问题了&#xff0c;后端MultipartFile file接受到的文件为null。 2. 排查问题&#xff1a; 参考的文档 picgo api列表关于multipart form-data中…

U-Mail邮件系统三大安全措施,防止信息泄露!

在当信息化高速发展的今天&#xff0c;国内很多企业业务流程对OA系统、CRM系统、ERP系统、邮件系统等办公应用依赖度越来越高。这些办公应用给企业带来便利的同时也伴随着越来越多的信息安全问题&#xff0c;而在日常的办公场景中&#xff0c;由于内部员工非法泄漏或黑客入侵导…

Qt实现TCP调试助手 - 简述如何在Qt中实现TCP多并发

简介 软件开发中&#xff0c;可能经常会用到TCP调试工具。本人使用QT开发了一款TCP调试工具&#xff0c;方便大家使用。本文章主要介绍下&#xff0c;该工具的功能&#xff0c;以及如何在Qt中实现TCP服务器的并发。 界面展示 安装界面 桌面图标。安装后会生成桌面图标&#…

(三)正点原子I.MX6ULL kernel6.1挂根文件系统

一、概述 移植NXP官方最新的linux kernel&#xff08;linux-imx-lf-6.1.y&#xff09; 移植方法基本参照正点原子教程 移植开发板&#xff1a;正点原子阿尔法2.1 二、添加开发板到内核 进入内核目录下&#xff0c;先修改Makefile 打开终端&#xff1a; cp arch/arm/configs/im…

fastbins_Double Free调试

我大哥给我出了一题pwn heap题&#xff0c;当时现场的时候没解出来&#xff0c;想岔了。事后感概自己还是理解的不够透彻。决定认真再次调试下。用的是2.23版本的how2heap中fastbins_dup.c。 简单用gcc编译下&#xff0c;然后带源码调试&#xff1a; 生成一个a.out的文件&#…

C++ 运算符重载(Complex复数类)

Complex复数类 Complex.h #ifndef COMPLEX_H #define COMPLEX_H#include <iostream> using namespace std;class Complex { private:double real 0; //复数的实部double imag 0; //复数的虚部 public:Complex() default; /*无参构造*/Complex(double r); …

No186.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

解决XFCE桌面VirtualBox透明背景的问题

1 背景 笔者用的是ManjaroXFCE的组合&#xff0c;但是在使用VirtualBox时&#xff0c;会出现透明背景的问题&#xff1a; 然后发现这其实和Kvantum主题有关。下面将进行设置操作&#xff0c;去修正Kvantum相关配置修复该问题。 2 操作流程 打开Kvantum Manager&#xff1a; …

No184.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

Django中如何创建表关系,请求生命周期流程图

Django中ORM创建表关系 如何创建表关系(一对一 &#xff0c; 一对多 &#xff0c; 多对多) 图书表&#xff0c;出版社表&#xff0c;作者表&#xff0c;作者详情表 换位思考法判断表关系 图书表和出版社表 >>> 一对多 >>> 图书表是多&#xff0c;出…

数据挖掘:关联规则,异常检测,挖掘的标准流程,评估指标,误差,聚类,决策树

数据挖掘&#xff1a;关联规则 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sql要…

混合A Star算法

混合A Star算法就是将A*与Lattice Graph相关联。 使用栅格地图&#xff0c;保持在栅格地图的这个节点方格里都只记录一个机器人的State&#xff0c;例如图上的弯弯的线&#xff0c;进行剪枝&#xff0c;看一个节点的Cost&#xff08;时间、消耗的能量、路径的长度、终端状态惩…

​软考-高级-系统架构设计师教程(清华第2版)【第3章 信息系统基础知识(p120~159)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第3章 信息系统基础知识(p120~159)-思维导图】 课本里章节里所有蓝色字体的思维导图

无需公网IP,贝锐花生壳内网穿透远程访问NAS

群晖DSM 7.0及以上版本 1.1 安装运行花生壳套件 &#xff08;1&#xff09;通过浏览器输入群晖NAS的内网地址&#xff0c;登录进去后&#xff0c;点击【套件中心】&#xff0c;搜索【花生壳】&#xff0c;并点击【安装套件】&#xff1b; &#xff08;2&#xff09; 勾选我接…

git命令汇总

1.git是基于ssh的代码管理工具,所以在git使用之前需要配置好ssh ssh配置教程 2.先创建仓库 3. git init在目标的git目录下创建仓库 4.git add .(或者写文件名) 5.git commit -m "标记信息" 持久化 6.git remote add origin gitgit.acwing.com:yaoaolong/11_5.git初次…

异步编程工具Promise与Async/Await:解决前端开发中的嵌套回调地狱

文章目录 Promise&#xff1a;处理异步操作的基本工具Promise.all async/await&#xff1a;更简洁的异步编程方式Promise与async/await的比较结论 当谈及JavaScript中的异步编程时&#xff0c;两个非常常见且强大的工具是Promise和async/await。在本文中&#xff0c;我们将以实…