使用easyui前端框架快速构建一个crud应用

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

easyui是博主最喜欢的前端框架,没有之一,因为它提供了多种主题,而且有圆润的各种组件。

目录

一、快速开始

二、准备工作

三、开始使用

1、主题资源

2、常用组件

datagrid

textbox

passwordbox

dialog

四、表格渲染

1、后端代码

添加跨域配置

添加controller接口

2、前端代码

method属性

loadFilter属性

fitColumns属性

striped属性

pagination属性

height属性

五、构建应用

1、完成分页功能

后端代码

前端代码

2、增删改功能

添加、修改功能

基于对话框

创建表单

表单渲染

添加、修改功能

基于行内编辑

准备工作

添加功能

修改功能

其他功能

删除功能

3、条件查询功能

前端代码

后端代码

FilterRule

Pager

StringToListOfFilterRuleConverter

Pager

SongServiceImpl


一、快速开始

easyui的官网地址:

JQuery EasyUI中文网icon-default.png?t=N7T8https://www.jeasyui.net/点击上方链接访问easyui中文官网,下载easyui。

使用easyui前端框架快速构建一个crud应用_第1张图片

在下载页面点击下载对应的版本,本篇文章将使用jquery easyui

使用easyui前端框架快速构建一个crud应用_第2张图片

选择下载免费版

使用easyui前端框架快速构建一个crud应用_第3张图片

二、准备工作

下载完成后,得到一个压缩包jquery-easyui-1.7.0.zip。

使用easyui前端框架快速构建一个crud应用_第4张图片

然后把这个压缩包解压出来,我们需要的是红框内的几个文件及文件夹。

  • locale目录下是常用的一些js文件
  • themes目录下是easyui的样式文件

使用easyui前端框架快速构建一个crud应用_第5张图片

通过HBuilderx创建一个基本的html项目

使用easyui前端框架快速构建一个crud应用_第6张图片

接着,把themes文件夹复制到项目的css目录下,把locale/easyui-lang-zh_CN.js和红框内的两个js文件复制到项目的js目录下。

使用easyui前端框架快速构建一个crud应用_第7张图片

三、开始使用

完成前面两步之后,就可以开始愉快地使用easyui了。

1、主题资源

如图,themes下面提供了多种主题样式的资源文件,喜欢哪个主题,引入对应包下的easyui.css即可。

使用easyui前端框架快速构建一个crud应用_第8张图片

2、常用组件

datagrid

easyui里用的最多的莫过于数据表格了,datagrid是easyui的表格组件,支持分页功能。只需要在表格渲染的js代码中添加选项pagenation: true即可开启分页功能。

打开easyui的文档页面,找到通过javascript渲染表格的案例代码。

使用easyui前端框架快速构建一个crud应用_第9张图片

官网提供的渲染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。

passwordbox

密码框,带了easyui样式的input密码框

dialog

对话框,通常会在对话框内嵌表单,实现数据的添加和修改功能。

四、表格渲染

为了方便快速学会datagird的使用,这里就直接拿之前写的springboot crud案例项目作为后端项目,演示datagird通过ajax异步加载表格数据。

项目git地址如下:

https://gitee.com/he-yunlin/springboot-crud/tree/springboot-crud1.0/icon-default.png?t=N7T8https://gitee.com/he-yunlin/springboot-crud/tree/springboot-crud1.0/

1、后端代码

添加跨域配置

首先要添加跨域配置,防止使用过程中出现cors问题。

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中添加一个接口方法

    @RequestMapping(value = "/selectList", method = RequestMethod.GET)
    public JsonResult> selectList() {
        List list = songService.selectList();

        return JsonResult.success("查询成功", list);
    }

对应地,在SongService接口添加selectList()方法

List selectList();

SongServiceImpl

    @Override
    public List selectList() {
        return songMapper.selectList(null);
    }

2、前端代码

在前端的easyui项目下创建html目录,在html目录下创建index.html。

修改表格的数据加载地址url为selectList接口的访问地址http://localhost:8083/song/selectList



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

然后选择通过firefox运行,打开看到一个空白页面

使用easyui前端框架快速构建一个crud应用_第10张图片

F12打开浏览器控制台,刷新页面,发现请求接口发生了异常,不支持post请求。

使用easyui前端框架快速构建一个crud应用_第11张图片

这是因为easyui的datagrid默认是通过ajax post请求加载数据.

使用easyui前端框架快速构建一个crud应用_第12张图片

使用easyui前端框架快速构建一个crud应用_第13张图片

打开之前的文档页面,往下滚动,找到数据网格属性。

使用easyui前端框架快速构建一个crud应用_第14张图片

如图,method属性就是设置请求的类型,而这个属性的默认值是post,我们把它设置成get

使用easyui前端框架快速构建一个crud应用_第15张图片

method属性



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

页面代码修改完成之后,发现只显示了表头,表格数据没有显示出来,而且报了一个错,rows is undefined。

使用easyui前端框架快速构建一个crud应用_第16张图片

为什么会这样呢,其实问题就在于后端返回的数据不是一个list,而是封装的一个JsonResult对象,list放到这个对象的data里了。所以,这里要对返回的数据进行简单的处理,得到data里的list。

loadFilter属性

就是它了,loadFilter属性是一个方法,用于请求url过滤返回的数据。

使用easyui前端框架快速构建一个crud应用_第17张图片

注意:我们在后端封装一个JsonResult对象返回是为了能够带上一个请求的状态码code,当这个状态码为200时,表示请求被正确地执行了。

因此,这个过滤方法应该是下面这样:

loadFilter: function(res) {
    if (res.code == 200) {
        return res.data;
    } else {
        return null;
    }
},

最后,正确的页面代码如下:



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

而此时,页面的数据终于显示出来了,一共800多条数据。

使用easyui前端框架快速构建一个crud应用_第18张图片

fitColumns属性

上面的页面看起来非常丑,如果表格能占满整个页面会更好看一点,因此,easyui也实现了这种效果,只需要设置fitColumns属性的值为true即可,表格的列宽会自适应当前页面。

使用easyui前端框架快速构建一个crud应用_第19张图片

于是,在原来的代码基础上添加fitColumns属性,并设置为true



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

修改页面代码后的效果,比原来看起来舒服的多。

使用easyui前端框架快速构建一个crud应用_第20张图片

striped属性

页面看起来差不多了,但是总感觉表格也太单调了,全是一种颜色,看起来总感觉怪怪的,能不能再美化一下呢。

答案是:当然可以,上面的页面很单调,是表格全部数据都是一个颜色,如果能给表格的行记录颜色不一样,那就完美了。

于是,striped属性腾空出世,这个属性的作用就是显示条纹,不出所料,这个属性默认值也是false。

使用easyui前端框架快速构建一个crud应用_第21张图片

把它设置成true看一下效果。



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

修改页面之后,视觉效果还不错,有了一点bootstrap的感觉了~

使用easyui前端框架快速构建一个crud应用_第22张图片

pagination属性

上面的表格外观已经很完美了,作为一个后端开发人员来说,这样的样式已经无可挑剔,但是,之前已经说过了,一共有800多条数据,这还算少的了,假如有几万条数据呢?如果一次性全部查询出来,每次查询的时候,后端服务的压力是很大的。

所以,一般数据量大的时候都会分页查询,每次只查询一部分数据。

easyui的datagrid支持分页功能,只需要设置pagination属性为true,而常用的分页属性还有另外两个pageSize和pageList。

使用easyui前端框架快速构建一个crud应用_第23张图片

修改前端页面代码,添加pagination属性为true。



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

此时,页面好像没有什么区别,好像也没有分页

使用easyui前端框架快速构建一个crud应用_第24张图片

其实,页面已经变了,只是在当前页面可浏览范围之外,页面滚动到末尾,会发现表格底部多了一个分页栏。

使用easyui前端框架快速构建一个crud应用_第25张图片

并且,请求携带了额外的参数page和rows

使用easyui前端框架快速构建一个crud应用_第26张图片

height属性

基于上面的的问题(需要拉到页面底部才能看到分页栏),现在给表格设置一个固定的高度,让它刚好够显示10条数据。通过不断调整,发现高度400比较适合。



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

再次查看页面效果

使用easyui前端框架快速构建一个crud应用_第27张图片

五、构建应用

1、完成分页功能

上个章节,已经完成了基本的页面样式的调整,但是能发现,其实并没有分页,这是因为后端没有处理easyui框架传的两个参数page和rows。

这个部分首先需要解决的就是这个问题,要使用mybatis-plus的分页功能,需要添加分页插件。

后端代码

新增mp配置类,添加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.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * MP配置类
 */
@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.example.springboot.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 防全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        return interceptor;
    }

}

前端代码

我们把请求数据的接口改一下,改成selectByPage。



	
		
		datagrid案例代码
		
		
		
		
		
	
	
	
		

页面效果

使用easyui前端框架快速构建一个crud应用_第28张图片

至此,分页功能完成~

2、增删改功能

一般页面的js代码和html是分离的,在这个部分,为了方便修改文件,把js代码单独保存到一个js文件,然后通过外部引入的方式引入。

html/index.html



	
		
		datagrid案例代码
		
		
		
		
		
        
	
	
	
		

 js/index.js

let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];

$(document).ready(function() {						
	$("#song_list").datagrid({
		url: base + "/song/selectByPage",
		title: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		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},
		]]
	});

});

接下来来实现数据的增、删、改功能。

使用easyui前端框架快速构建一个crud应用_第29张图片

给表格添加头部工具栏,新增添加、修改、删除三个按钮。

let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];

function addHandler() {
	// todo
}

function editHandler() {
	// todo
}

function deleteHandler() {
    // todo
}

$(document).ready(function() {						
	$("#song_list").datagrid({
		url: base + "/song/selectByPage",
		title: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		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(添加/修改),在对话框中添加一个保存按钮,点击保存按钮则提交post请求到requestUrl,完成数据保存

  • 方案一:把行数据填充到一个对话框内包含的表单中,点击对话框中的保存按钮把修改后的数据提交到后台保存数据;
  • 方案二:开启行内编辑,在表格的行内编辑数据,在表格头部具栏增加两个按钮:保存和取消。

接下来就介绍两种实现方案的具体实现。

基于对话框

既然是基于对话框,就要先提前创建好一个对话框,在对话框中嵌套一个表单,表单内定义需要通过前端提交的歌曲信息。

创建表单

html/index.html



	
		
		datagrid案例代码
		
		
		
		
		
		
	
	
	
		

如上所示,通过一个隐藏域保存歌曲的id字段的值,只有修改的时候才需要提交这个id字段。

表单渲染

 js/index.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: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		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 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: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		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 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() {
				let bool = $("#song_form").form("validate");
				
				if (!bool) {
					$.messager.alert("系统提示", "请填写正确的表单项", "warning");
				} else {
					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");
				}
			}
		}, {
			text: '取消',
			iconCls: 'icon-cancel',
			handler: function() {
				$("#song_form").form('clear');
				$('#song_dialog').dialog('close');
			}
		}]
	});

    $("#song_list").datagrid({
		url: base + "/song/selectByPage",
		title: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		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表单实现了数据的添加、修改功能,这个部分,会通过更高级的方式来实现,在表格的行内开启编辑。

准备工作

为了保存两种编辑风格,在前端项目的根目录下额外创建一个easyui-crud.html和js/easyui-crud.js

easyui-crud.html



	
		
		datagrid案例代码
		
		
		
		
		
		
	
	
	
		

js/easyui-crud.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: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		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 () {
		$("#song_list").datagrid("reload");
	}, "json");
}

修改功能

这个功能相对复杂,需要依赖easyui datagrid的行编辑结束事件。

定义JSON对象保存修改后的数据

// 定义一个json对象保存歌曲数据
let data = {
	name: "",
	singer: "",
	note: ""
};

给歌曲列表添加表格行结束编辑事件

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");
	let row = datagrid.datagrid("getSelected");

	if (editingId != "") {
		datagrid.datagrid("selectRow", editingId);
	} else {
		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) {
            $.messager.show({
                title: '系统消息',
                timeout: 5000,
                showType: 'slide',
                msg: res.message,
            });
            
            editingId = "";
        }, "json");
    }
}

取消

function cancelHandler() {
	if (editingId != "") {
		$("#song_list").datagrid("cancelEdit", editingId);
	
		editingId = "";
	}
}

到了这里,基于行内编辑的表格数据的添加、修改功能就完成了~

let editingId;
let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];

// 定义一个json对象保存歌曲数据
let data = {};

function addHandler() {
	requestUrl = "/song/insert";
	
	$.post(base + requestUrl, {
		name: "*****",
		singer: "*****",
		note: "*****"
	}, function () {
		$("#song_list").datagrid("reload");
	}, "json");
}

function editHandler() {
	let datagrid = $("#song_list");
	let row = datagrid.datagrid("getSelected");

	if (editingId != "") {
		datagrid.datagrid("selectRow", editingId);
	} else {
		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(requestUrl, data, function (res) {
			$.messager.show({
				title: '系统消息',
				timeout: 5000,
				showType: 'slide',
				msg: res.message,
			});
			
			editingId = "";
		}, "json");
	}
}

function cancelHandler() {
	if (editingId != "") {
		$("#song_list").datagrid("cancelEdit", editingId);
	
		editingId = "";
	}
}

function deleteHandler() {
	// todo
}

$(document).ready(function() {
	$("#song_list").datagrid({
		url: base + "/song/selectByPage",
		title: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		singleSelect: true,
		pagination: true,
		remoteFilter: true,
		clientPaging: false,
		pageSize: pageList[0],
		pageList: pageList,
		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-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},
		]]
	});

});

删除功能

删除功能功能很简单,选中一行,点击删除按钮即可,删除之前弹出询问框,点击确定就会发送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) {
					$.messager.show({
						title: '系统消息',
						timeout: 5000,
						showType: 'slide',
						msg: res.message,
					});
				
					$("#song_list").datagrid("reload");
				}, "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文件。

使用easyui前端框架快速构建一个crud应用_第30张图片

前端代码

博主下载下来之后,花了点时间看了一下datagrid-filter.js的源代码,发现一个关键的属性remoteFilter,意思是远程过滤,而通过页面的说明,发现这个属性的默认值为false。

使用easyui前端框架快速构建一个crud应用_第31张图片

根据我使用easyui的经验猜测,如果设置成true,应该会在加载表格数据的时候额外发送请求参数。于是照着葫芦画瓢,给歌曲列表开启行过滤,并设置remoteFilter属性值为true。

let requestUrl;
let base = "http://localhost:8083";
let pageList = [20, 50, 100, 500, 1000];

$(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() {
				let bool = $("#song_form").form("validate");
				
				if (!bool) {
					$.messager.alert("系统提示", "请填写正确的表单项", "warning");
				} else {
					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");
				}
			}
		}, {
			text: '取消',
			iconCls: 'icon-cancel',
			handler: function() {
				$("#song_form").form('clear');
				$('#song_dialog').dialog('close');
			}
		}]
	});
						
	let datagrid = $("#song_list").datagrid({
		url: base + "/song/selectByPage",
		title: "歌曲列表",
		height: 810,
		striped: true,
		fitColumns: true,
		pagination: true,
		remoteFilter: true,
		clientPaging: false,
		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() {
				requestUrl = "/song/insert";
				
				$('#song_dialog').dialog('open');
			}
		}, '-', {
			iconCls: 'icon-edit',
			text: '修改',
			handler: function() {
				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");
				}
			}
		}, '-', {
			iconCls: 'icon-delete',
			text: '删除',
			handler: function() {
				let rowData = $("#song_list").datagrid("getSelected");
				
				if (rowData) {
					$.messager.confirm("提示", "删除后数据无法恢复,是否确认删除?", function(bool) {
						if (bool) {
							$.get(base + "/song/deleteById/" + rowData.id, {}, function(res) {
								$.messager.show({
									title: '系统消息',
									timeout: 5000,
									showType: 'slide',
									msg: res.message,
								});
			
								$("#song_list").datagrid("reload");
							}, "json");
						}
					});
				} else {
					$.messager.alert("请选择要删除的数据!", "warning");
				}
			}
		}],
		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},
		]]
	});
	
	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参数。

使用easyui前端框架快速构建一个crud应用_第32张图片

后端代码

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;
}

并且考虑到这里过滤的比较符有多个,创建一个枚举来保存所有比较符。

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类型。

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 extends Sorter {
    /**
     * 页数
     */
    private Integer page = 1;

    /**
     * 每页记录数
     */
    private Integer rows = 10;

    /**
     * 过滤规则
     */
    private List filterRules;

    /**
     * 根据Pager创建Page对象
     * @param pager Pager
     * @return Page
     */
    public static  Page ofPage(Pager pager) {
        return new Page<>(pager.getPage(), pager.getRows());
    }
}

使用easyui前端框架快速构建一个crud应用_第33张图片

测试请求一下,发现请求返回400状态码,参数转换异常。

使用easyui前端框架快速构建一个crud应用_第34张图片

具体的错误提示如下:不能从String类型转换到List类型,这也是意料之中的事。

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的转换。这里使用alibaba的fastjson来实现

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> {

    @Override
    public List convert(String source) {
        return JSON.parseArray(source, FilterRule.class);
    }

}

完成以上修改之后,请求总算是通了,正常返回了数据。

使用easyui前端框架快速构建一个crud应用_第35张图片

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 lombok.EqualsAndHashCode;

import java.util.List;

/**
 * 基础分页参数对象,包含页数和每页的记录数
 * @author heyunlin
 * @version 1.0
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class Pager extends Sorter {
    /**
     * 页数
     */
    private Integer page = 1;

    /**
     * 每页记录数
     */
    private Integer rows = 10;

    /**
     * 过滤规则
     */
    private List filterRules;

    /**
     * 根据Pager创建Page对象
     * @param pager Pager
     * @return Page
     */
    public static  Page ofPage(Pager pager) {
        return new Page<>(pager.getPage(), pager.getRows());
    }

    /**
     * 根据Pager创建QueryWrapper对象
     * @param pager Pager
     * @return QueryWrapper
     */
    public static  QueryWrapper getQueryWrapper(Pager pager) {
        List filterRules = pager.getFilterRules();

        if (filterRules != null && !filterRules.isEmpty()) {
            QueryWrapper wrapper = new QueryWrapper<>();

            for (FilterRule filterRule : filterRules) {
                String field = 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;
    }

}

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 selectByPage(SongPagerDTO pagerDTO) {
        QueryWrapper wrapper = Pager.getQueryWrapper(pagerDTO);
        Page page = Pager.ofPage(pagerDTO);

        return songMapper.selectPage(page, wrapper);
    }

}

至此,所有功能都已经完成。

好了,文章就分享到这里了,看完不要忘了点赞+收藏哦~

前端项目地址如下,可按需获取

https://gitcode.net/heyl163_/easyui.giticon-default.png?t=N7T8https://gitcode.net/heyl163_/easyui.git

你可能感兴趣的:(easyui前端框架,前端框架,easyui,前端)