1. 简介

  layui(谐音:类UI)是一款采用自身模块规范编写的前端UI框架,遵循原生HTML/CSS/JS的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到API的每一处细节都经过精心雕琢,非常适合界面的快速开发。

  (1)为服务端程序员量身定做的低门槛开箱即用的前端UI解决方案;

  (2)兼容IE6/7除外的全部浏览器;

  (3)采用经典模块化,避免工具的复杂配置,回归简单;

  (4)更多请浏览Layui官网:https://www.layui.com/

2. 初始化数据库

  创建数据库layuidemo,并初始化表结构:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0; -- ----------------------------
-- Table structure for t_sys_user
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_user`;
CREATE TABLE `t_sys_user` (
`id` int(0) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户名称',
`nickname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户昵称',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户密码',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统用户' ROW_FORMAT = Dynamic; -- ----------------------------
-- Records of t_sys_user
-- ----------------------------
INSERT INTO `t_sys_user` VALUES (1, 'user', 'C3Stones', '$2a$10$WXEPqxjMwY6d6A0hkeBtGu.acRRWUOJmX7oLUuYMHF1VWWUm4EqOC');
INSERT INTO `t_sys_user` VALUES (2, 'system', '管理员', '$2a$10$dmO7Uk9/lo1D5d1SvCGgWuB050a0E2uuBDNITEpWFiIfCg.3UbA8y'); SET FOREIGN_KEY_CHECKS = 1;

3. 示例代码

  建议下载示例工程,参考搭建自己的示例工程。

  • 创建项目

  • 修改pom.xml
<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>
<groupId>com.c3stones</groupId>
<artifactId>spring-boot-layui-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-layui-demo</name>
<description>SpringBoot+Mybatis-Plus+Layui Demo</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath />
</parent> <dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
  • 创建配置文件application.yml
server:
port: 8080
servlet:
session:
timeout: 1800s spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/layuidemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
username: root
password: 123456
thymeleaf:
prefix: classpath:/view/
suffix: .html
encoding: UTF-8
servlet:
content-type: text/html
# 生产环境设置true
cache: false # Mybatis-plus配置
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
global-config:
db-config:
id-type: AUTO
configuration:
# 打印sql
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 信息安全
security:
web:
excludes:
- /login
- /logout
- /images/**
- /jquery/**
- /layui/**
xss:
enable: true
excludes:
- /login
- /logout
- /images/*
- /jquery/*
- /layui/*
sql:
enable: true
excludes:
- /images/*
- /jquery/*
- /layui/*
csrf:
enable: true
excludes:
  • 创建Mybatis-Plus配置类(配置分页)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; /**
* Mybatis-Plus配置类
*
* @author CL
*
*/
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
public class MybatisPlusConfig { /**
* 注入分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
  • 创建响应实体
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; /**
* 响应实体
*
* @author CL
*
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Response<T> { /**
* 响应码
*/
private int code; /**
* 响应消息体
*/
private String msg; /**
* 响应数据
*/
private T data; /**
* 失败响应
*
* @param msg 响应消息体
* @return
*/
public static <T> Response<T> error(String msg) {
return new Response<T>(500, msg, null);
} /**
* 成功响应
*
* @param data 响应数据
* @return
*/
public static <T> Response<T> success(T data) {
return new Response<T>(200, null, data);
} /**
* 成功响应
*
* @param msg 响应消息体
* @return
*/
public static <T> Response<T> success(String msg) {
return new Response<T>(200, msg, null);
} /**
* 成功响应
*
* @param msg 响应消息体
* @param data 响应数据
* @return
*/
public static <T> Response<T> success(String msg, T data) {
return new Response<T>(200, msg, data);
} }
  • 创建全局异常处理类
import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; /**
* 全局异常处理
*
* @author CL
*
*/
@Controller
public class WebExceptionAdvice implements ErrorController { /**
* 获得异常路径
*/
@Override
public String getErrorPath() {
return "error";
} /**
* 异常处理,跳转到响应的页面
*
* @param request
* @param model
* @return
*/
@RequestMapping(value = "error")
public String handleError(HttpServletRequest request, Model model) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception");
model.addAttribute("message", throwable != null ? throwable.getMessage() : null);
switch (statusCode) {
case 400:
return "error/400";
case 403:
return "error/403";
case 404:
return "error/404";
default:
return "error/500";
}
} }
  • 创建实体
import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; /**
* 系统用户信息
*
* @author CL
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_sys_user")
@EqualsAndHashCode(callSuper = false)
public class User implements Serializable { private static final long serialVersionUID = 1L; /**
* 用户ID
*/
@TableId(type = IdType.AUTO)
private Integer id; /**
* 用户名称
*/
private String username; /**
* 用户昵称
*/
private String nickname; /**
* 用户密码
*/
@JsonIgnore
private String password; }
  • 创建Mapper
import org.apache.ibatis.annotations.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.c3stones.entity.User; /**
* 系统用户Mapper
*
* @author CL
*
*/
@Mapper
public interface UserMapper extends BaseMapper<User> { }
  • 创建Service
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.c3stones.entity.User; /**
* 系统用户Service
*
* @author CL
*
*/
public interface UserService extends IService<User> { /**
* 查询列表数据
*
* @param user 系统用户
* @param current 当前页
* @param size 每页显示条数
* @return
*/
public Page<User> listData(User user, long current, long size); /**
* 检验用户名称是否唯一
*
* @param userName 用户名称
* @return
*/
public Boolean checkUserName(String userName); }
  • 创建Service实现
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.c3stones.entity.User;
import com.c3stones.mapper.UserMapper;
import com.c3stones.service.UserService; import cn.hutool.core.util.StrUtil; /**
* 系统用户Service实现
*
* @author CL
*
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { /**
* 查询列表数据
*
* @param user 系统用户
* @param current 当前页
* @param size 每页显示条数
* @return
*/
@Override
public Page<User> listData(User user, long current, long size) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (null != user.getId()) {
queryWrapper.eq("id", user.getId());
}
if (StrUtil.isNotBlank(user.getUsername())) {
queryWrapper.like("username", user.getUsername());
}
if (StrUtil.isNotBlank(user.getNickname())) {
queryWrapper.like("nickname", user.getNickname());
}
return baseMapper.selectPage(new Page<>(current, size), queryWrapper);
} /**
* 检验用户名称是否唯一
*
* @param userName 用户名称
* @return
*/
@Override
public Boolean checkUserName(String userName) {
if (StrUtil.isNotBlank(userName)) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", userName);
Integer count = baseMapper.selectCount(queryWrapper);
return (count != null && count > 0) ? false : true;
}
return false;
} }
  • 创建登录Controller
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.c3stones.common.Response;
import com.c3stones.entity.User;
import com.c3stones.service.UserService; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt; /**
* 系统登录Controller
*
* @author CL
*
*/
@Controller
public class LoginController { @Autowired
private UserService userService; /**
* 登录页
*
* @return
*/
@GetMapping(value = { "login", "" })
public String login() {
return "login";
} /***
* 登录验证
*
* @param user 系统用户
* @return
*/
@PostMapping(value = "login")
@ResponseBody
public Response<User> login(User user, HttpSession session) {
if (StrUtil.isBlank(user.getUsername()) || StrUtil.isBlank(user.getPassword())) {
return Response.error("用户名或密码不能为空");
}
User queryUser = new User();
queryUser.setUsername(user.getUsername());
queryUser = userService.getOne(new QueryWrapper<>(queryUser));
if (queryUser == null || !StrUtil.equals(queryUser.getUsername(), user.getUsername())
|| !BCrypt.checkpw(user.getPassword(), queryUser.getPassword())) {
return Response.error("用户名或密码错误");
}
session.setAttribute("user", queryUser);
return Response.success("登录成功", queryUser);
} /**
* 登出
*
* @param httpSession
* @return
*/
@GetMapping(value = "logout")
public String logout(HttpSession httpSession) {
httpSession.invalidate();
return "redirect:/login";
} }
  • 创建登录拦截器
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor; /**
* 登录拦截器
*
* @author CL
*
*/
@Component
public class LoginInterceptor implements HandlerInterceptor { /**
* 拦截处理
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Object user = request.getSession().getAttribute("user");
if (null == user) {
response.sendRedirect("/login");
}
return true;
} }
  • 配置登录拦截器
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import lombok.Setter; /**
* Web配置类
*
* @author CL
*
*/
@Configuration
@ConfigurationProperties(prefix = "security.web")
public class WebConfigurer implements WebMvcConfigurer { /**
* 忽略的URL
*/
@Setter
private List<String> excludes; /**
* 配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns(excludes);
} }
  • 创建首页Contrller
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; /**
* 系统首页Controller
*
* @author CL
*
*/
@Controller
public class IndexController { /**
* 首页
*
* @return
*/
@GetMapping(value = "index")
public String index(Model model, HttpSession httpSession) {
model.addAttribute("user", httpSession.getAttribute("user"));
return "index";
} /**
* 控制台
*
* @return
*/
@GetMapping(value = "view")
public String view() {
return "pages/view";
} }
  • 创建启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; /**
* 启动类
*
* @author CL
*
*/
@SpringBootApplication
public class Application { public static void main(String[] args) {
SpringApplication.run(Application.class, args);
} }
  • 拷贝静态资源

      将示例工程的resource目录下的static文件夹及其子文件拷贝到本工程对应文件夹下。
  • 创建前端页面文件夹

      在resource目录下创建view文件夹,工程的所有页面都会写在此文件夹下(和配置文件中的spring.thymeleaf.prefix对应)。
  • 创建登录页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>C3Stones</title>
<link th:href="@{/images/favicon.ico}" rel="icon">
<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
<link th:href="@{/layui/css/login.css}" rel="stylesheet" />
<link th:href="@{/layui/css/view.css}" rel="stylesheet" />
<script th:src="@{/layui/layui.all.js}"></script>
<script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
</head>
<body class="login-wrap">
<div class="login-container">
<form class="login-form">
<div class="input-group text-center text-gray">
<h2>欢迎登录</h2>
</div>
<div class="input-group">
<input type="text" id="username" class="input-field">
<label for="username" class="input-label">
<span class="label-title">用户名</span>
</label>
</div>
<div class="input-group">
<input type="password" id="password" class="input-field">
<label for="password" class="input-label">
<span class="label-title">密码</span>
</label>
</div>
<button type="button" class="login-button">登录<i class="ai ai-enter"></i></button>
</form>
</div>
</body>
</html>
<script>
layui.define(['element'],function(exports){
var $ = layui.$;
$('.input-field').on('change',function(){
var $this = $(this),
value = $.trim($this.val()),
$parent = $this.parent();
if(!isEmpty(value)){
$parent.addClass('field-focus');
}else{
$parent.removeClass('field-focus');
}
})
exports('login');
}); // 登录
var layer = layui.layer;
$(".login-button").click(function() {
var username = $("#username").val();
var password = $("#password").val();
if (isEmpty(username) || isEmpty(password)) {
layer.msg("用户名或密码不能为空", {icon: 2});
return ;
} var loading = layer.load(1, {shade: [0.3, '#fff']});
$.ajax({
url : "[[@{/}]]login",
data : {username : username, password : password},
type : "post",
dataType : "json",
error : function(data) {
},
success : function(data) {
layer.close(loading);
if (data.code == 200) {
location.href = "[[@{/}]]index";
} else {
layer.msg(data.msg, {icon: 2});
}
}
});
}); function isEmpty(n) {
if (n == null || n == '' || typeof(n) == 'undefined') {
return true;
}
return false;
}
</script>
  • 创建系统框架页面,并初始化菜单
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>C3Stones</title>
<link th:href="@{/images/favicon.ico}" rel="icon">
<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
<link th:href="@{/layui/css/admin.css}" rel="stylesheet" />
<script th:src="@{/layui/layui.js}"></script>
<script th:src="@{/layui/js/index.js}" data-main="home"></script>
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
<div class="layui-header custom-header">
<ul class="layui-nav layui-layout-left">
<li class="layui-nav-item slide-sidebar" lay-unselect>
<a href="javascript:;" class="icon-font"><i class="ai ai-menufold"></i></a>
</li>
</ul>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item">
<a href="javascript:;">[[${user?.nickname}]]</a>
<dl class="layui-nav-child">
<dd><a th:href="@{/logout}">退出</a></dd>
</dl>
</li>
</ul>
</div> <div class="layui-side custom-admin">
<div class="layui-side-scroll">
<div class="custom-logo">
<img alt="" th:src="@{/images/logo.jpg}">
<h1>C3Stones</h1>
</div>
<ul id="Nav" class="layui-nav layui-nav-tree">
<li class="layui-nav-item">
<a href="javascript:;">
<i class="layui-icon"></i>
<em>主页</em>
</a>
<dl class="layui-nav-child">
<dd><a th:href="@{/view}">控制台</a></dd>
</dl>
</li>
<li class="layui-nav-item">
<a href="javascript:;">
<i class="layui-icon"></i>
<em>系统管理</em>
</a>
<dl class="layui-nav-child">
<dd><a th:href="@{/user/list}">用户管理</a></dd>
</dl>
</li>
</ul> </div>
</div> <div class="layui-body">
<div class="layui-tab app-container" lay-allowClose="true" lay-filter="tabs">
<ul id="appTabs" class="layui-tab-title custom-tab"></ul>
<div id="appTabPage" class="layui-tab-content"></div>
</div>
</div> <div class="layui-footer">
<p> 2020 - C3Stones Blog : <a href="https://www.cnblogs.com/cao-lei/" target="_blank">https://www.cnblogs.com/cao-lei/</a></p>
</div>
<div class="mobile-mask"></div>
</div>
</body>
</html>
  • 创建控制台页面

      在view文件夹下创建pages文件夹。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
<link th:href="@{/layui/css/view.css}" rel="stylesheet" />
<script th:src="@{/layui/layui.all.js}"></script>
<script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
<title></title>
</head>
<body class="layui-view-body">
<div class="layui-row" style="text-align: center;">
<div class="layui-col-md12" style="padding: 18% 0px 20px 0px;">
<font class="layui-text"><h1>欢迎使用</h1></font>
</div>
</div>
</body>
</html>
  • 登录测试

      浏览器访问:http://127.0.0.1:8080/,输入用户名密码:user/123456,或者system/123456,进行测试正常用户登录,并测试用户不存在、密码错误等异常。
  • 创建用户Controller
import javax.validation.constraints.NotNull;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.c3stones.common.Response;
import com.c3stones.entity.User;
import com.c3stones.service.UserService; import cn.hutool.crypto.digest.BCrypt; /**
* 系统用户Controller
*
* @author CL
*
*/
@Controller
@RequestMapping(value = "user")
public class UserController { @Autowired
private UserService userService; /**
* 查询列表
*
* @return
*/
@RequestMapping(value = "list")
public String list() {
return "pages/userList";
} /**
* 查询列表数据
*
* @param user 系统用户
* @param current 当前页
* @param size 每页显示条数
* @return
*/
@RequestMapping(value = "listData")
@ResponseBody
public Response<Page<User>> listData(User user, @RequestParam(name = "page") long current,
@RequestParam(name = "limit") long size) {
Page<User> page = userService.listData(user, current, size);
return Response.success(page);
} /**
* 删除
*
* @param user 系统用户
* @return
*/
@RequestMapping(value = "delete")
@ResponseBody
public Response<Boolean> delete(User user) {
Assert.notNull(user.getId(), "ID不能为空");
boolean result = userService.removeById(user.getId());
return Response.success(result);
} /**
* 修改
*
* @param user 系统用户
* @param model
* @return
*/
@RequestMapping(value = "edit")
public String edit(User user, Model model) {
Assert.notNull(user.getId(), "ID不能为空");
model.addAttribute("user", userService.getById(user.getId()));
return "pages/userEdit";
} /**
* 检验用户名称是否唯一
*
* @param userName 用户名称
* @return
*/
@RequestMapping(value = "check")
@ResponseBody
public Response<Boolean> checkUserName(@NotNull String username) {
Boolean checkResult = userService.checkUserName(username);
return Response.success(checkResult);
} /**
* 更新
*
* @param user 系统用户
* @return
*/
@RequestMapping(value = "update")
@ResponseBody
public Response<Boolean> update(User user) {
Assert.notNull(user.getId(), "ID不能为空");
boolean result = userService.updateById(user);
return Response.success(result);
} /**
* 新增
*
* @return
*/
@RequestMapping(value = "add")
public String add() {
return "pages/userAdd";
} /**
* 保存
*
* @param user 系统用户
* @return
*/
@RequestMapping(value = "save")
@ResponseBody
public Response<Boolean> save(User user) {
user.setPassword(BCrypt.hashpw(user.getPassword()));
boolean result = userService.save(user);
return Response.success(result);
} }
  • 创建用户列表及新增、编辑页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
<link th:href="@{/layui/css/view.css}" rel="stylesheet" />
<script th:src="@{/layui/layui.all.js}"></script>
<script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
<title></title>
</head>
<body class="layui-view-body">
<div class="layui-content">
<div class="layui-row">
<div class="layui-card">
<div class="layui-card-header">
<i class="layui-icon mr5"></i>用户管理
<button class="layui-btn layui-btn-xs layui-btn-normal pull-right mt10" data-type="add"><i class="layui-icon mr5"></i>新增</button>
</div>
<div class="layui-card-body">
<div class="searchTable">
用户ID:
<div class="layui-inline mr5">
<input class="layui-input" name="id" autocomplete="off">
</div>
用户名称:
<div class="layui-inline mr5">
<input class="layui-input" name="username" autocomplete="off">
</div>
用户昵称:
<div class="layui-inline mr10">
<input class="layui-input" name="nickname" autocomplete="off">
</div>
<button class="layui-btn" data-type="reload">查询</button>
<button class="layui-btn layui-btn-primary" data-type="reset">重置</button>
</div>
<table class="layui-hide" id="userDataTable" lay-filter="config"></table>
<script type="text/html" id="operation">
<a class="layui-btn layui-btn-xs" lay-event="edit">修改</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
</div>
</div>
</div>
</div>
</body>
<script>
var element = layui.element;
var table = layui.table;
var layer = layui.layer;
table.render({
id: 'userTable'
,elem: '#userDataTable'
,url: '[[@{/user/listData}]]'
,page: {
layout: ['prev', 'page', 'next', 'count', 'skip', 'limit']
,groups: 5
,first: false
,last: false
}
,cols: [
[
{field:'id', width: 50, title: 'ID'}
,{field:'username', title: '用户名称', align: 'center'}
,{field:'nickname', title: '用户昵称', align: 'center'}
,{title:'操作', align: 'center', toolbar: '#operation', width:150}
]
]
,response: {
statusCode: 200
}
,parseData: function(res){
return {
"code": res.code
,"msg": res.msg
,"count": res.data.total
,"data": res.data.records
};
}
}); active = {
add: function() {
layer.open({
type: 2,
area: ['80%', '80%'],
title: '新增',
content: '[[@{/}]]user/add'
});
},
reload: function() {
table.reload('userTable', {
page: {
curr: 1
}
,where: {
id : $("input[name='id']").val()
,username : $("input[name='username']").val()
,nickname : $("input[name='nickname']").val()
}
}, 'data');
},
reset: function() {
$(".layui-input").val("");
}
}; // 按钮事件
$('.layui-btn').on('click', function(){
var type = $(this).data('type');
active[type] ? active[type].call(this) : '';
}); //监听行工具事件
table.on('tool(config)', function(obj){
var row = obj.data;
if(obj.event === 'del') {
layer.confirm("确认删除吗?", {icon: 3, title:'提示'}, function(index) {
layer.close(index);
$.ajax({
url : "[[@{/}]]user/delete",
data : {'id': row.id},
type : "post",
dataType : "json",
error : function(data) {
errorHandle(data);
},
success : function(data) {
$(".searchTable .layui-btn").eq(0).click();
}
});
});
} else if (obj.event === 'edit') {
layer.open({
type: 2,
area: ['80%', '80%'],
title: '修改',
content: '[[@{/}]]user/edit?id=' + row.id
});
}
}); //错误处理
function errorHandle(data) {
if (data.status == 404) {
layer.msg("请求资源不存在", {icon: 2});
} else {
layer.msg("服务端异常", {icon: 2});
}
return;
}
</script>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
<link th:href="@{/layui/css/view.css}" rel="stylesheet" />
<script th:src="@{/layui/layui.all.js}"></script>
<script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
<script th:src="@{/jquery/jquery-form.js}"></script>
<title></title>
</head>
<body class="layui-view-body">
<div class="layui-row">
<div class="layui-card">
<form class="layui-form layui-card-body layui-form-pane" method="post" th:action="@{/user/save}">
<div class="layui-form-item">
<label class="layui-form-label"><i>*</i>用户名称</label>
<div class="layui-input-block">
<input type="text" name="username" lay-verify="username" placeholder="6-8位英文字母" maxlength="8" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i>*</i>用户昵称</label>
<div class="layui-input-block">
<input type="text" name="nickname" lay-verify="required" maxlength="15" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i>*</i>用户密码</label>
<div class="layui-input-inline">
<input type="password" name="password" required lay-verify="password" maxlength="8" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">请输入6-8位密码,且只能包含字母或数字</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i>*</i>确认密码</label>
<div class="layui-input-inline">
<input type="password" name="confirmPwd" lay-verify="confirm" maxlength="8" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<button type="submit" class="layui-btn" lay-submit lay-filter="*">提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
</div>
</div>
</body>
<script>
var form = layui.form;
var layer = layui.layer; // 自定义检验
form.verify({
username: function(val) {
if (isEmpty(val)) {
return '必填项不能为空';
}
debugger;
var reg = /^[A-Za-z]{6,8}$/;
if (!reg.test(val)) {
return '用户名称不合法';
}
if (!checkUsername(val)) {
return '用户名称已存在';
}
},
password: [
/^[A-Za-z0-9]{6,8}$/
,'请输入6-8位密码,且只能包含字母或数字'
],
confirm: function(val) {
if (isEmpty(val)) {
return '必填项不能为空';
}
if (val != $("input[name='password']").val()) {
return '确认密码与用户密码不一致';
}
}
}); // 检测用户名称是否唯一
function checkUsername(username) {
var checkResult = true;
$.ajax({
url : "[[@{/}]]user/check",
data : {'username': username},
type : "post",
dataType : "json",
async: false,
error : function(data) {
errorHandle(data);
},
success : function(data) {
checkResult = data.data;
}
});
return checkResult;
} // 提交表单
form.on('submit(*)', function(data){
$(".layui-form").ajaxForm({
error: function(data){
errorHandle(data);
},
success: function(data) {
parent.location.reload();
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
}
});
}); //是否为空
function isEmpty(n) {
if (n == null || n == '' || typeof(n) == 'undefined') {
return true;
}
return false;
} // 错误处理
function errorHandle(data) {
if (data.status == 404) {
layer.msg("请求资源不存在", {icon: 2});
} else {
layer.msg("服务端异常", {icon: 2});
}
return;
}
</script>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
<link th:href="@{/layui/css/view.css}" rel="stylesheet" />
<script th:src="@{/layui/layui.all.js}"></script>
<script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
<script th:src="@{/jquery/jquery-form.js}"></script>
<title></title>
</head>
<body class="layui-view-body">
<div class="layui-row">
<div class="layui-card">
<form class="layui-form layui-card-body layui-form-pane" method="post" th:action="@{/user/update}">
<div class="layui-form-item">
<label class="layui-form-label">用户ID</label>
<div class="layui-input-block">
<input type="text" name="id" th:value="${user?.id}" readonly="readonly" class="layui-input readonly">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i>*</i>用户名称</label>
<div class="layui-input-block">
<input type="text" name="username" th:value="${user?.username}" lay-verify="username" placeholder="6-8位英文字母" maxlength="8" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i>*</i>用户昵称</label>
<div class="layui-input-block">
<input type="text" name="nickname" th:value="${user?.nickname}" lay-verify="required" maxlength="15" lay-reqtext="用户昵称是必填项" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<button type="submit" class="layui-btn" lay-submit lay-filter="*">提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
</div>
</div>
</body>
<script>
var form = layui.form;
var layer = layui.layer; // 自定义检验
form.verify({
username: function(val) {
if (isEmpty(val)) {
return '必填项不能为空';
}
if (val != '[[${user?.username}]]') {
var reg = /^[A-Za-z]{6,8}$/;
if (!reg.test(val)) {
return '用户名称不合法';
}
if (!checkUsername(val)) {
return '用户名称已存在';
}
} }
}); // 检测用户名称是否唯一
function checkUsername(username) {
var checkResult = true;
$.ajax({
url : "[[@{/}]]user/check",
data : {'username': username},
type : "post",
dataType : "json",
async: false,
error : function(data) {
errorHandle(data);
},
success : function(data) {
checkResult = data.data;
}
});
return checkResult;
} // 提交表单
form.on('submit(*)', function(data){
$(".layui-form").ajaxForm({
error: function(data){
errorHandle(data);
},
success: function(data) {
parent.location.reload();
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
}
});
}); // 是否为空
function isEmpty(n) {
if (n == null || n == '' || typeof(n) == 'undefined') {
return true;
}
return false;
} // 错误处理
function errorHandle(data) {
if (data.status == 404) {
layer.msg("请求资源不存在", {icon: 2});
} else {
layer.msg("服务端异常", {icon: 2});
}
return;
}
</script>
</html>
  • 创建全局异常页面(404、500等)

      在view文件夹下创建error文件夹,并创建404.html、500.html等。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>C3Stones</title>
<link th:href="@{/images/favicon.ico}" rel="icon">
<style>
body {
height: 300px;
background: url("/images/404.png") center no-repeat;
}
</style>
</head>
<body class="layui-layout-body">
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>C3Stones</title>
<link th:href="@{/images/favicon.ico}" rel="icon">
<style>
body {
height: 300px;
background: url("/images/500.png") center no-repeat;
}
</style>
</head>
<body class="layui-layout-body">
</body>
</html>

4. 项目地址

  spring-boot-layui-demo

SpringBoot + Layui +Mybatis-plus实现简单后台管理系统(内置安全过滤器)的更多相关文章

  1. springboot学习笔记:11.springboot+shiro+mysql+mybatis(通用mapper)+freemarker+ztree+layui实现通用的java后台管理系统(权限管理+用户管理+菜单管理)

    一.前言 经过前10篇文章,我们已经可以快速搭建一个springboot的web项目: 今天,我们在上一节基础上继续集成shiro框架,实现一个可以通用的后台管理系统:包括用户管理,角色管理,菜单管理 ...

  2. SpringBoot + Layui + JustAuth +Mybatis-plus实现可第三方登录的简单后台管理系统

    1. 简介   在之前博客:SpringBoot基于JustAuth实现第三方授权登录 和 SpringBoot + Layui +Mybatis-plus实现简单后台管理系统(内置安全过滤器)上改造 ...

  3. [.Net Core] 简单使用 Mvc 内置的 Ioc

    简单使用 Mvc 内置的 Ioc 本文基于 .NET Core 2.0. 鉴于网上的文章理论较多,鄙人不才,想整理一份 Hello World(Demo)版的文章. 目录 场景一:简单类的使用 场景二 ...

  4. [.Net Core] 简单使用 Mvc 内置的 Ioc(续)

    简单使用 Mvc 内置的 Ioc(续) 本文基于 .NET Core 2.0. 上一章<[.Net Core] 简单使用 Mvc 内置的 Ioc>已经对日常 Mvc 中的 Ioc 的简单用 ...

  5. 简单使用 Mvc 内置的 Ioc

    简单使用 Mvc 内置的 Ioc 本文基于 .NET Core 2.0. 鉴于网上的文章理论较多,鄙人不才,想整理一份 Hello World(Demo)版的文章. 目录 场景一:简单类的使用 场景二 ...

  6. ASP.NET MVC5 + EF6 + LayUI实战教程,通用后台管理系统框架(3)

    前言 本节将我们自己的CSS样式替换系统自带的 开始搭建 将脚本文件夹删掉,将内容文件夹里的内容删掉,将我们自己的CSS样式文件,全部复制到内容里边 新建家庭控制器 给家庭控制器添加索引视图 指数代码 ...

  7. ASP.NET MVC5+EF6+LayUI实战教程,通用后台管理系统框架(4)- 漂亮的登录界面

    前言 这一讲,给大家添加登录页面 实现 添加Login的Index视图 @{ Layout = null; } <!DOCTYPE html> <html class="l ...

  8. ASP.NET MVC5+EF6+LayUI实战教程,通用后台管理系统框架(1)

    文章转自:http://www.xuboyi.com/298.html 前言 网站运营有一段时间了,记录的内容都是杂七杂八的,思前想后,决定给大家分享一套ASP.Net的系列教程.手把手的做一套通用后 ...

  9. 简单后台管理系统框架--HTML练手项目2【Frameset】

    [本文为原创,转载请注明出处] 技术[HTML]   布局[Frameset] 无步骤 <!DOCTYPE html> <html lang="en"> & ...

  10. 【摸鱼神器】UCode Cms管理系统 内置超好用的代码生成器 解决多表连接痛点

    一.序言 UCode Cms管理系统是面向企业级应用软件开发的脚手架.当前版本1.3.4.快速体验: git clone https://gitee.com/decsa/demo-cms.git (一 ...

随机推荐

  1. Android10_原理机制系列_AMS之AMS的启动

    概述 该篇基于AndroidQ,主要介绍系统启动中的 AMS(ActivityManagerService)的启动过程. AMS对四大组件(AndroidQ将activity移到了ActivityTa ...

  2. LeetCode 中等题解(4)

    40 组合总和 II Question 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates ...

  3. 好学易懂 从零开始的插头DP(一)

    好学易懂 从零开始的插头DP(一) 写在前面 这是一篇,以蒟蒻视角展开的梳理总结.更改了一些顺序,变化了一些细节.方便蒟蒻学习理解(起码本蒟蒻是这样).大佬们可以直接看其它大佬的博客,可以学的更快. ...

  4. locust使用小技巧(v0.13.5)

    Windows下载: pip install locustio==0.13.5; 以下基于locust的0.13.5,写文章时时2019年,没想到2020年就大变样了 locust是基于python的 ...

  5. MySQL常用命令与语句

    目录 Shell命令 查看系统信息 查看系统变量 设置系统变量 数据库操作 查看表信息 修改表语句 操作表 操作索引 操作约束 操作列 查询常用语句 Shell命令 mysql -uroot -p12 ...

  6. H5 ,Css实现了你的logo

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  7. P4771 八百标兵奔北坡

    观察题目中关于北边的定义,发现是以当前点为顶点,向上的倒三角(自己想想为什么). 然后就可以直接 DP 了,令 \(f_{i,j}\) 表示点 \(\left(i,j\right)\) 的答案. \[ ...

  8. C语言讲义——文件操作

    fopen( ) 函数:创建一个新的文件或者打开一个已有的文件 FILE *fopen( const char * filename, const char * mode ); 关于参数mode的取值 ...

  9. mysql 优化数据类型

    1.更小的通常更好 选择不会超过范围的最小类型 2.简单就好 例如,整型比字符操作代价更低,因为字符集和校对规则(排序规则)使字符比较比整形比较更复杂. 3.尽量避免null 如果查询中包含可为nul ...

  10. 第四代Express框架koa简介

    目录 简介 koa和express koa使用介绍 中间件的级联关系 koa的构造函数 启动http server 自定义中间件 异常处理 简介 熟悉Spring MVC的朋友应该都清楚Spring ...