第七章、用户管理

项目部分结构如下

AdminController  用户管理的控制器   访问地址 http://localhost:8080/admins

package com.waylau.spring.boot.blog.controller;

import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.waylau.spring.boot.blog.vo.Menu;
/**
 * 后台管理  控制器
 * @author Administrator
 */
@Controller
@RequestMapping("/admins")
public class AdminController {
	
	/**
	 * 获取后台管理主页面
	 * @return
	 */
	@GetMapping
	public ModelAndView listUsers(Model model) {
		List<Menu> list = new ArrayList<Menu>();
		list.add(new Menu("用户管理", "/users"));
		model.addAttribute("list", list);
		return new ModelAndView("admins/index", "model", model);
	}
}

显示页面如下  此时没有用户的数据

其中Menu是菜单  目前只有一个【用户管理】菜单,页面位于/src/main/resources/templates/admins/index.html

前端页面为index.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head th:replace="~{fragments/header :: header}">
</head>
<body>
<!-- Page Content -->
<div class="container blog-content-container">

    <div class="row">

        <!-- 左侧栏目 -->
        <div class="col-md-4 col-xl-3">
            
	      <!-- 分类 -->
	        <div class="card ">
	            <h5 class="card-header"><i class="fa fa-bars" aria-hidden="true"></i> 菜单</h5>
	
	            <ul style="list-style-type:none;" class="list-group blog-menu" th:each="menu : ${model.list}">
	            	<li>
		            	<a href="javascript:void(0)" class="list-group-item active" title="waylau" 
		            		th:title="${menu.name}" th:text="${menu.name}" th:attr="url=${menu.url}">
		                   	 用户管理xxx
		                </a>
	            	</li>
	            </ul>
	        </div>
 
        </div>
   
        <!-- 右侧栏目(通过点击li里面的超链接异步加载) -->
        <div class="col-md-8 col-xl-9">
            <div class="card" id="rightContainer">
			</div>
        </div>
    </div>
    <!-- /.row -->

</div>
<!-- /.container -->


<div th:replace="~{fragments/footer :: footer}">...</div>

<script src="../../js/admins/main.js" th:src="@{~/js/admins/main.js}"></script>
</body>
</html>

该页面中引入了footer和header两个html页面

footer.html  主要是引入一些js 和页面下方说明  以及锚点的设置及滚动

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
</head>
<body>

<footer class="blog-footer bg-inverse" data-th-fragment="footer">
	<a id="goToTop" href="#">
		<i class="fa fa-chevron-up fa-3x" aria-hidden="true"></i>
	</a>
    <div class="container">
 		<p class="m-0 text-center text-white">© 2020 <a href="https://waylau.com">waylau.com</a></p>
    </div>
	
   	<!-- JavaScript -->
    <script src="../../js/jquery-3.1.1.min.js" th:src="@{/js/jquery-3.1.1.min.js}"></script>
    <script src="../../js/jquery.form.min.js" th:src="@{/js/jquery.form.min.js}"></script>
    <script src="../../js/tether.min.js" th:src="@{/js/tether.min.js}"></script>
    <script src="../../js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
    <script src="../../js/nprogress.js" th:src="@{/js/nprogress.js}"></script>
    <script src="../../js/thinker-md.vendor.min.js" th:src="@{/js/thinker-md.vendor.min.js}"></script>
    <script src="../../js/bootstrap-tagsinput.js" th:src="@{/js/jquery.tagsinput.min.js}"></script>
    <script src="../../js/chosen.jquery.js" th:src="@{/js/chosen.jquery.js}"></script>
    <script src="../../js/toastr.min.js" th:src="@{/js/toastr.min.js}"></script>
    <script src="../../js/cropbox.js" th:src="@{/js/cropbox.js}"></script>
    <script src="../../js/thymeleaf-bootstrap-paginator.js" th:src="@{/js/thymeleaf-bootstrap-paginator.js}"></script>
    <script src="../../js/catalog-generator.js" th:src="@{/js/catalog-generator.js}"></script>
    <script src="../../js/main.js" th:src="@{/js/main.js}"></script>
 </footer>

</body>
</html>

js/main.js 如下:

// DOM 加载完再执行
$(function() {
 
	// 返回顶部的效果事件
    NProgress.start();

    $(window).scroll(function(){  //只要窗口滚动,就触发下面代码
        var scrollt = document.documentElement.scrollTop + document.body.scrollTop; //获取滚动后的高度
        if( scrollt >200 ){  //判断滚动后高度超过200px,就显示
            $("#goToTop").fadeIn(400); //淡出
        }else{
            $("#goToTop").stop().fadeOut(400); //如果返回或者没有超过,就淡入.必须加上stop()停止之前动画,否则会出现闪动
        }
    });
    $("#goToTop").click(function(){ //当点击标签的时候,使用animate在200毫秒的时间内,滚到顶部
        $("html,body").animate({scrollTop:"0px"},200);
    });
    NProgress.done();
});

header.html  引入css  包括  搜索  登录 注册

<!DOCTYPE html>
<html lang="en" data-th-fragment="header" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Tether core CSS -->
    <link href="../../css/tether.min.css" th:href="@{/css/tether.min.css}" rel="stylesheet">

    <!-- Bootstrap CSS -->
    <link href="../../css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet">

    <!-- Font-Awesome CSS -->
    <link href="../../css/font-awesome.min.css" th:href="@{/css/font-awesome.min.css}" rel="stylesheet">

    <!-- NProgress CSS -->
    <link href="../../css/nprogress.css" th:href="@{/css/nprogress.css}" rel="stylesheet">

    <!-- thinker-md CSS -->
    <link href="../../css/thinker-md.vendor.css" th:href="@{/css/thinker-md.vendor.css}" rel="stylesheet">

    <!-- bootstrap tags CSS -->
    <link href="../../css/bootstrap-tagsinput.css" th:href="@{/css/jquery.tagsinput.min.css}" rel="stylesheet">
    
    <!-- bootstrap chosen CSS -->
    <link href="../../css/component-chosen.min.css" th:href="@{/css/component-chosen.min.css}" rel="stylesheet">

    <!-- toastr CSS -->
    <link href="../../css/toastr.min.css" th:href="@{/css/toastr.min.css}" rel="stylesheet">
    
    <!-- jQuery image cropping plugin CSS -->
    <link href="../../css/cropbox.css" th:href="@{/css/cropbox.css}" rel="stylesheet">
    
    <!-- Custom styles -->
    <link href="../../css/style.css" th:href="@{/css/style.css}" rel="stylesheet">
    <link href="../../css/thymeleaf-bootstrap-paginator.css" th:href="@{/css/thymeleaf-bootstrap-paginator.css}" rel="stylesheet">
    <link href="../../css/blog.css" th:href="@{/css/blog.css}" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse bg-inverse navbar-toggleable-md">
    <div class="container">
        <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
                data-target="#navbarsContainer" aria-controls="navbarsExampleContainer" aria-expanded="false"
                aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <a class="navbar-brand" href="/" th:href="@{/}">NewStarBlog</a>

        <div class="collapse navbar-collapse" id="navbarsContainer">

            <ul class="navbar-nav mr-auto">
                <li class="nav-item">
                    <a class="nav-link" href="/blogs?order=new">最新 <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="/blogs?order=hot">最热</a>
                </li>
                <li>
	                <form class="form-inline mt-2 mt-md-0">
	                    <input class="form-control mr-sm-2" type="text" placeholder="搜索">
	                    <a href="/blogs?keyword=ww" class="btn btn-outline-secondary my-2 my-sm-0"><i class="fa fa-search" aria-hidden="true"></i></a>
	                </form>
                </li>
            </ul>

            <a href="/login" class="btn btn-outline-success my-2 my-sm-0" type="submit">登录</a>
            <a href="/register" class="btn btn-outline-success my-2 my-sm-0" type="submit">注册</a>
        </div>

    </div>
</nav>
</body>
</html>

在index.html下方引入了一个js

<script src="../../js/admins/main.js" th:src="@{~/js/admins/main.js}"></script>

代码如下

/**
 * Bolg main JS.
 * Created by waylau.com on 2017/3/9.
 */
"use strict";
//# sourceURL=main.js

// DOM 加载完再执行
$(function() {

    // 菜单事件
    $(".blog-menu .list-group-item").click(function() {//超链接a的点击事件

        var url = $(this).attr("url");

        // 先移除其他的点击样式,再添加当前的点击样式
        $(".blog-menu .list-group-item").removeClass("active");
        $(this).addClass("active");  

        // 加载其他模块的页面到右侧工作区
         $.ajax({ 
             url: url, 
             success: function(data){
                 $("#rightContainer").html(data);
             },
             error : function() {
            	 alert("error");
             }
         });
    });


    // 选中菜单第一项(页面加载完毕后默认触发第一个li的点击事件)
     $(".blog-menu .list-group-item:first").trigger("click");
});

该js对左侧菜单下的超链接绑定单击事件,点击后会给该链接添加背景,其余菜单取消背景  【class=active】

发动ajax请求,加载右侧数据区域  /users  默认异步 GET

进入UserController 控制器  调用list方法  此时后台无任何参数传递

package com.waylau.spring.boot.blog.controller;

import java.util.List;

import javax.validation.ConstraintViolationException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.waylau.spring.boot.blog.domain.User;
import com.waylau.spring.boot.blog.service.UserService;
import com.waylau.spring.boot.blog.util.ConstraintViolationExceptionHandler;
import com.waylau.spring.boot.blog.vo.Response;
/**
 * User 控制器
 * @author Administrator
 *
 */
@RestController
@RequestMapping("/users")
public class UserController {
	
	@Autowired
	private UserService userService;
	
	/**
	 * 查询所有用户
	 * @param async
	 * @param pageIndex
	 * @param pageSize
	 * @param name
	 * @param model
	 * @return
	 */
	@GetMapping
	public ModelAndView list(@RequestParam(value = "async",required = false) boolean async,//是否异步请求
			@RequestParam(value = "pageIndex",required = false,defaultValue = "0") int pageIndex,//分页使用
			@RequestParam(value = "pageSize",required = false,defaultValue = "10") int pageSize,//分页使用
			@RequestParam(value = "name",required = false,defaultValue = "") String name,//模糊查询使用
			Model model){
		
		Pageable pageable = new PageRequest(pageIndex, pageSize);
		Page<User> page = userService.listUsersByNameLike(name, pageable);
		List<User> list = page.getContent();//当前所在页面数据列表
		
		model.addAttribute("page", page);
		model.addAttribute("userList", list);
		//String viewName, String modelName, Object modelObject 
		/**
		 * 若为异步  则返回该list页面的一部分 若是同步则返回整个list页面
		 * 例如  第一次访问的时候 需要返回整个list页面
		 * 后面加载该list页面的时候只需要更新数据区域即可
		 */
		return new ModelAndView(async==true?"users/list :: #mainContainerRepleace":"users/list", "userModel", model);
	}
	
	/**
	 * 获取创建用户表单界面
	 * @param model
	 * @return
	 */
	@GetMapping("/add")
	public ModelAndView createForm(Model model){
		model.addAttribute("user", new User(null,null,null,null));
		return new ModelAndView("users/add", "userModel", model);
	}
	
	/**
	 * 保存或修改用户
	 * @param user
	 * @return
	 */
	@PostMapping
	public ResponseEntity<Response> saveOrUpdateUser(User user){
		try{
			userService.saveOrUpdateUser(user);
		} catch (ConstraintViolationException e) {
			return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e)));
		}
		return ResponseEntity.ok().body(new Response(true, "处理成功", user));
	}
	
	/**
	 * 删除用户
	 * @param id
	 * @param model
	 * @return
	 */
	@DeleteMapping(value = "/{id}")
	public ResponseEntity<Response> delete(@PathVariable("id") Long id, Model model){
		try{
			userService.removeUser(id);
		} catch (Exception e) {
			return ResponseEntity.ok().body(new Response(false, e.getMessage()));
		}	
		return ResponseEntity.ok().body(new Response(true, "处理成功"));
	}
	
	/**
	 * 获取修改用户的界面
	 * @param id
	 * @param model
	 * @return
	 */
	@GetMapping(value = "/edit/{id}")
	public ModelAndView modifyForm(@PathVariable("id") Long id, Model model) {
		User user = userService.getUserById(id);
		model.addAttribute("user", user);
		return new ModelAndView("users/edit", "userModel", model);
	}
}

users/list 代码如下

<div class="card-header bg-dark font-white">
	<div class="input-group col-md-7 col-xl-6">
		<input type="text" class="form-control" id="searchName" placeholder="输入用户名称进行搜索"/> 
		<span class="input-group-btn">
			<button class="btn btn-secondary" type="button" id="searchNameBtn">
				<i class="fa fa-search" aria-hidden="true"></i>
			</button>
		</span>
		<a class="btn btn-primary" data-toggle="modal" data-target="#flipFlop" role="button" id="addUser">
			<i class="fa fa-plus" aria-hidden="true"></i>
		</a>
	</div>
</div>

<div id="mainContainer" class="container">
	<div id="mainContainerRepleace" class="row">
		<table class="table table-striped">
			<thead>
				<tr>
					<th data-field="id">ID</th>
					<th data-field="username">账号</th>
					<th data-field="name">姓名</th>
					<th data-field="email">邮箱</th>
					<th data-field="operation">操作</th>

				</tr>
			</thead>
			<tbody>
				<tr th:each="user : ${userModel.userList}">
					<td th:text="${user.id}">1</td>
					<td th:text="${user.username}">1</td><!-- 账号 -->
					<td th:text="${user.name}">waylau</td><!-- 姓名 -->
					<td th:text="${user.email}">waylau</td>
					<td>
						<div>
							<a class="blog-edit-user" data-toggle="modal" data-target="#flipFlop" role="button" data-th-attr="userId=${user.id}">
								<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
							</a>
							<a class="blog-delete-user" role="button" data-th-attr="userId=${user.id}">
								<i class="fa fa-times" aria-hidden="true"></i>
							</a>
						</div>
					</td>
				</tr>
			</tbody>
		</table>
		<div th:replace="~{fragments/page :: page}">...</div>
	</div>
</div>

<!-- The modal -->
<div class="modal fade" id="flipFlop" tabindex="-1" role="dialog"
	aria-labelledby="modalLabel" aria-hidden="true">
	<div class="modal-dialog" role="document">
		<div class="modal-content">
			<div class="modal-header">
				<h4 class="modal-title" id="modalLabel">新增/编辑</h4>
				<button type="button" class="close" data-dismiss="modal" aria-label="Close">
					<span aria-hidden="true">&times;</span>
				</button>

			</div>
			<div class="modal-body" id="userFormContainer"></div>
			<div class="modal-footer">
				<button class="btn btn-primary" data-dismiss="modal" id="submitEdit">提交</button>
				<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
			</div>
		</div>
	</div>
</div>

<!-- JavaScript -->
<script src="../../js/users/main.js" th:src="@{/js/users/main.js}"></script>

list方法中

async = false pageIndex = '0'  pageSize = '10' name = ''

UserService接口如下

package com.waylau.spring.boot.blog.service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.waylau.spring.boot.blog.domain.User;

/**
 * 用户服务接口
 * @author Administrator
 *
 */
public interface UserService {
	/**
	 * 新增  编辑  保存用户
	 * @param user
	 * @return
	 */
	User saveOrUpdateUser(User user);
	
	/**
	 * 注册用户
	 * @param user
	 * @return
	 */
	User registerUser(User user);
	
	/**
	 * 删除用户
	 * @param user
	 */
	void removeUser(Long id);
	
	/**
	 * 根据id获取用户
	 * @param id
	 * @return
	 */
	User getUserById(Long id);
	
	/**
	 * 根据用户名进行分页模糊查询
	 * @param name
	 * @param pageable
	 * @return
	 */
	Page<User> listUsersByNameLike(String name, Pageable pageable);
	
}

实现类UserServiceImpl如下

package com.waylau.spring.boot.blog.service;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.waylau.spring.boot.blog.domain.User;
import com.waylau.spring.boot.blog.repository.UserRepository;

/**
 * 用户服务接口实现
 * @author Administrator
 *
 */
@Service
public class UserServiceImpl implements UserService{

	@Autowired
	private UserRepository userRepository;
	
	@Override
	@Transactional
	public User saveOrUpdateUser(User user) {
		return userRepository.save(user);
	}

	@Override
	@Transactional
	public User registerUser(User user) {
		return userRepository.save(user);
	}

	@Override
	@Transactional
	public void removeUser(Long id) {
		userRepository.delete(id);
		
	}

	@Override
	public User getUserById(Long id) {
		return userRepository.findOne(id);
	}

	@Override
	public Page<User> listUsersByNameLike(String name, Pageable pageable) {
		//模糊查询
		name = "%" + name + "%";
		Page<User> users = userRepository.findByNameLike(name, pageable);
		return users;
	}
}

UserRepository如下,添加了两个自定义的方法,其余采用JPA提供的方法

package com.waylau.spring.boot.blog.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import com.waylau.spring.boot.blog.domain.User;

/**
 * 用户接口
 * @author Administrator
 */
public interface UserRepository extends JpaRepository<User, Long>{
	/**
	 * 根据用户姓名分页查询用户列表
	 * @param name
	 * @param pageable
	 * @return
	 */
	Page<User> findByNameLike(String name, Pageable pageable);
	
	/**
	 * 根据用户账号查询用户
	 * @param username
	 * @return
	 */
	User findByUsername(String username);
}

index方法调用完毕后,ajax返回分页page和用户列表  以及users/list 整个页面作为数据传递到admins/index页面

model.addAttribute("page", page);
model.addAttribute("userList", list);

list方法中的async = false  此处表示  若异步请求则返回users/list页面中的部分区域 此处是同步  则返回整个list页面

return new ModelAndView(async==true?"users/list :: #mainContainerRepleace":"users/list", "userModel", model);

页面加载后会对第一个菜单触发点击事件

$(".blog-menu .list-group-item:first").trigger("click");

此时会将整个users/list页面及其数据加载到admins/index里面的rightContainer里面

----------------------------------------------------------------

添加用户如下

<a class="btn btn-primary" data-toggle="modal" data-target="#flipFlop" role="button" id="addUser">
	<i class="fa fa-plus" aria-hidden="true"></i>
</a>

其中js位于users/list.html 中的

<script src="../../js/users/main.js" th:src="@{/js/users/main.js}"></script>

如下:

/*!
 * Bolg main JS.
 * 
 * @since: 1.0.0 2017/3/9
 * @author Way Lau <https://waylau.com>
 */
"use strict";
//# sourceURL=main.js
 
// DOM 加载完再执行
//DOM 加载完再执行
$(function() {

    var _pageSize; // 存储用于搜索

    // 根据用户名、页面索引、页面大小获取用户列表
    function getUersByName(pageIndex, pageSize) {
         $.ajax({ 
             url: "/users", 
             contentType : 'application/json',
             data:{
                 "async":true, 
                 "pageIndex":pageIndex,
                 "pageSize":pageSize,
                 "name":$("#searchName").val()
             },
             success: function(data){
                 $("#mainContainer").html(data);
             },
             error : function() {
                 toastr.error("error!");
             }
         });
    }

    // 分页
    $.tbpage("#mainContainer", function (pageIndex, pageSize) {
        getUersByName(pageIndex, pageSize);
        console.log(pageIndex);
        console.log(pageSize);
        _pageSize = pageSize;
    });

    // 搜索
    $("#searchNameBtn").click(function() {
        getUersByName(0, _pageSize);
    });

    // 获取添加用户的界面
    $("#addUser").click(function() {
        $.ajax({ 
             url: "/users/add", 
             success: function(data){
                 $("#userFormContainer").html(data);
             },
             error : function(data) {
                 toastr.error("error!");
             }
         });
    });

    // 获取编辑用户的界面
    $("#rightContainer").on("click",".blog-edit-user", function () { 
        $.ajax({ 
             url: "/users/edit/" + $(this).attr("userId"), 
             success: function(data){
                 $("#userFormContainer").html(data);
             },
             error : function() {
                 toastr.error("error!");
             }
         });
    });

    // 提交变更后,清空表单
    $("#submitEdit").click(function() {
        $.ajax({ 
             url: "/users", 
             type: 'POST',
             data:$('#userForm').serialize(),
             success: function(data){
                 $('#userForm')[0].reset();  

                 if (data.success) {
                     // 从新刷新主界面
                     getUersByName(0, _pageSize);
                 } else {
                     toastr.error(data.message);
                 }

             },
             error : function() {
                 toastr.error("error!");
             }
         });
    });

    // 删除用户
    $("#rightContainer").on("click",".blog-delete-user", function () { 

        $.ajax({ 
             url: "/users/" + $(this).attr("userId") , 
             type: 'DELETE', 
             success: function(data){
                 if (data.success) {
                     // 从新刷新主界面
                     getUersByName(0, _pageSize);
                 } else {
                     toastr.error(data.message);
                 }
             },
             error : function() {
                 toastr.error("error!");
             }
         });
    });
});

添加按钮中js如下

// 获取添加用户的界面
$("#addUser").click(function() {
	$.ajax({ 
		url: "/users/add", 
		success: function(data){
			$("#userFormContainer").html(data);
		},
		error : function(data) {
			toastr.error("error!");
		}
	});
});

进入控制器UserController 中 调用方法

/**
 * 获取创建用户表单界面
 * @param model
 * @return
 */
@GetMapping("/add")
public ModelAndView createForm(Model model){
	model.addAttribute("user", new User(null,null,null,null));
	return new ModelAndView("users/add", "userModel", model);
}

创建一个新用户属性都是 null,并传入页面users/add.html  作为数据页面返回给 users/list.html 中id = 'userFormContainer'的div里面

users/add.html如下

<div class="container">
 
<form th:action="@{/users}" method="post" th:object="${userModel.user}" id="userForm">
	<input type="hidden" name="id" th:value="*{id}">
	
	<div class="form-group ">
		<label for="username" class="col-form-label">账号</label>
		<input type="text" class="form-control" id="username" name="username"  th:value="*{username}" maxlength="50" placeholder="请输入账号,至少3个字符,至多20个">
	</div>
	<div class="form-group">
		<label for="email" class="col-form-label">邮箱</label>
		<input type="email" class="form-control" id="email" name="email" th:value="*{email}" maxlength="50" placeholder="请输入邮箱">
	</div>
	<div class="form-group">
		<label for="name" class="col-form-label">姓名</label>
		<input type="text" class="form-control" id="name" name="name" th:value="*{name}" maxlength="20" placeholder="请输入姓名,至少2个字符,至多20个" >
	</div>
	<div class="form-group">
		<label for="password" class="col-form-label">密码</label>
		<input type="password" class="form-control" id="password" name="password"  th:value="*{password}" maxlength="30" placeholder="请输入密码,字母或特殊符号和数字结合" >
	</div>

</form>
 
</div><!-- /.container -->

之前 users/list.html 中id =  userFormContainer 所在的父节点div  样式display : none 追加了users/add.html后,变为block 可见

这是通过 data-target="#flipFlop" 来实现的    flipFlop是需要显示的DIV 的id

User如下:

package com.waylau.spring.boot.blog.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

/**
 * 用户实体
 * @author Administrator
 * Spring注解 @NotBlank,@NotNull,@NotEmpty
 */
@Entity//实体
public class User {
	
	@Id//主键
	@GeneratedValue(strategy = GenerationType.IDENTITY)//主键的生成策略(自增)
	private Long id;//实体一个唯一标识
	
	@NotEmpty(message = "姓名不能为空")//不能为null,也不能为空
	@Size(min = 2, max = 20)
	@Column(nullable = false, length = 20)//映射为字段 值不能为空
	private String name;
	
	@NotEmpty(message = "邮箱不能为空")
	@Size(max = 50)
	//nullable表示该字段是否可以为null值,默认为true unique表示该字段是否为唯一标识,默认为false
	@Email(message = "邮箱格式不对")
	@Column(nullable = false, length = 50, unique = true)
	private String email;
	
	@NotEmpty(message = "账号不能为空")
	@Size(min = 3, max = 20)
	@Column(nullable = false, length = 20, unique = true)
	private String username;//用户账号  用户登录的唯一标识
	
	@NotEmpty(message = "密码不能为空")
	@Size(max = 100)
	@Column(length = 100)//字段长度
	private String password;//密码
	
	@Column(length = 200)
	private String avatar;//头像信息
	
	protected User(){}//无参构造    设为protected防止直接使用
	
	public User(Long id, String name, String username, String email) {
		this.id = id;
		this.name = name;
		this.email = email;
		this.username = username;
	}

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getAvatar() {
		return avatar;
	}
	public void setAvatar(String avatar) {
		this.avatar = avatar;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", email=" + email + ", username=" + username + ", password="
				+ password + ", avatar=" + avatar + "]";
	}
}

填好表单提交  POST  /users

调用js如下

// 提交变更后,清空表单
$("#submitEdit").click(function() {
	$.ajax({ 
		 url: "/users", 
		 type: 'POST',
		 data:$('#userForm').serialize(),
		 success: function(data){
			 $('#userForm')[0].reset();  

			 if (data.success) {
				 // 从新刷新主界面
				 getUersByName(0, _pageSize);
			 } else {
				 toastr.error(data.message);
			 }

		 },
		 error : function() {
			 toastr.error("error!");
		 }
	 });
});

进入UserController  保存和修改都调用该方法

/**
 * 保存或修改用户
 * @param user
 * @return
 */
@PostMapping
public ResponseEntity<Response> saveOrUpdateUser(User user){
	try{
		userService.saveOrUpdateUser(user);
	} catch (ConstraintViolationException e) {
		return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e)));
	}
	return ResponseEntity.ok().body(new Response(true, "处理成功", user));
}

将返回的内容封装成类Response如下

package com.waylau.spring.boot.blog.vo;
/**
 * 返回对象
 * @author Administrator
 *
 */
public class Response {
	private boolean success;//处理是否成功
	private String message;//处理后的消息提示
	private Object body;//返回的数据
	
	public Response(boolean success, String message) {
		this.success = success;
		this.message = message;
	}
	public Response(boolean success, String message, Object body) {
		this.success = success;
		this.message = message;
		this.body = body;
	}
	public boolean isSuccess() {
		return success;
	}
	public void setSuccess(boolean success) {
		this.success = success;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	public Object getBody() {
		return body;
	}
	public void setBody(Object body) {
		this.body = body;
	}
}

若填写表单中不符合规定,则会在此处抛出异常,自定义bean处理异常如下

package com.waylau.spring.boot.blog.util;

import java.util.ArrayList;
import java.util.List;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.apache.commons.lang3.StringUtils;

/**
 * ConstraintViolationException  bean异常处理器
 * @author Administrator
 *
 */
public class ConstraintViolationExceptionHandler {
	/**
	 * 获取批量异常信息
	 * @param e
	 * @return
	 */
	public static String getMessage(ConstraintViolationException e){
		List<String> msgList = new ArrayList<String>();
		for(ConstraintViolation<?> constraintViolation : e.getConstraintViolations()){
			msgList.add(constraintViolation.getMessage());
		}
		String message = StringUtils.join(msgList.toArray(), ";");
		return message;
	}
}

可能多个异常,以;拼接返回

修改功能和添加功能类似 修改、查询等不再讲解,查看代码即可

关于js中的_pageSize 就是每页显示的条数,前端js中默认是undefined,当切换每页显示条数的时候

就会调用

// 分页
$.tbpage("#mainContainer", function (pageIndex, pageSize) {
	getUersByName(pageIndex, pageSize);
	console.log(pageIndex);
	console.log(pageSize);
	_pageSize = pageSize;
});

给js中的全局变量_pageSize赋值

若直接搜索的时候,可能_pageSize还没有赋值,就是undefined,传入后台

url: "/users", 
contentType : 'application/json',
data:{
	"async":true, 
	"pageIndex":pageIndex,
	"pageSize":pageSize,
	"name":$("#searchName").val()
},

实际传递参数

后台接收参数 中name 为空字符串

true
0
10

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荒--

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值