SpringBoot(五) -- SpringBootWeb登录示例
一.解决index.html访问
在SpringBoot中默认访问的首页是静态资源文件夹下的index.html,无法被Thymeleaf模板引擎解析,因此我们可以定义一个controller将默认请求定位到模板引擎下的index.html:
1 /*让SpringBoot可以查找到我们当前真实的index.html,而不是默认静态资源文件夹下的,因为静态资源文件夹下的
2 * index.html将无法被模板引擎解析*/
3 @RequestMapping({"/", "/index.html"})
4 public String index() {
5 return "index";
6 }
--但是我们一直使用这样一个方法来解决资源映射的问题显得十分麻烦,我们可以使用自动以ViewCOntroller的方法来进行请求映射:
1 package com.zhiyun.springboot.web_restfulcrud.config;
2
3 import org.springframework.context.annotation.Bean;
4 import org.springframework.context.annotation.Configuration;
5 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
6 import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
7 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
8
9 /**
10 * @author : S K Y
11 * @version :0.0.1
12 * 扩展SpringMVC的功能
13 */
14 @Configuration
15 public class MyMvcConfig extends WebMvcConfigurerAdapter {
16
17 //所有的WebMvcConfigurerAdapter组件都会一起起作用
18 @Bean //将组件注册在容器中
19 public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
20 return new WebMvcConfigurerAdapter() {
21 @Override
22 public void addViewControllers(ViewControllerRegistry registry) {
23 String[] all = {
24 "/", "/index", "index.html"
25 };
26 for (String content : all) {
27 registry.addViewController(content).setViewName("index");
28 }
29 }
30 };
31 }
32 }
二.登录界面的国际化
在以往的SpringMVC开发中,需要完成以下步骤:
1.编写国际化配置文件
2.使用ResourcesBundleMessageSource来管理国际化资源文件
3.在页面使用fmt:message取出国际化的内容
在SpringBoot中完成国际化配置:
1.编写国际化配置文件,抽取页面所需要显示的国际化内容
2.SpringBoot自动配置好了管理国际化资源文件的组件
1 /**
2 * Comma-separated list of basenames (essentially a fully-qualified classpath
3 * location), each following the ResourceBundle convention with relaxed support for
4 * slash based locations. If it doesn't contain a package qualifier (such as
5 * "org.mypackage"), it will be resolved from the classpath root.
6 */
7 private String basename = "messages"; //我们的的配置文件可以放在类路径下的classpath:messages.properties中
8 @Bean
9 public MessageSource messageSource() {
10 ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
11 if (StringUtils.hasText(this.basename)) {
12 messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
13 StringUtils.trimAllWhitespace(this.basename)));
14 }
15 if (this.encoding != null) {
16 messageSource.setDefaultEncoding(this.encoding.name());
17 }
18 messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
19 messageSource.setCacheSeconds(this.cacheSeconds);
20 messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
21 return messageSource;
22 }
--指定基础名: 1 # 指定基础名 2 spring: 3 messages: 4 basename: i18n.login
--页面获取国际化文件的值:
1 <body class="text-center">
2 <form class="form-signin" action="dashboard.html">
3 <img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt=""
4 width="72" height="72">
5 <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign inh1>
6 <label class="sr-only" th:text="#{login.username}">Usernamelabel>
7 <input type="text" class="form-control" placeholder="Username" required="" autofocus=""
8 th:placeholder="#{login.username}">
9 <label class="sr-only" th:text="#{login.password}"> Passwordlabel>
10 <input type="password" class="form-control" placeholder="Password" required=""
11 th:placeholder="#{login.password}">
12 <div class="checkbox mb-3">
13 <label>
14 <input type="checkbox" value="remember-me"> [[ #{login.remember} ]]
15 label>
16 div>
17 <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign inbutton>
18 <p class="mt-5 mb-3 text-muted">? 2017-2018p>
19 <a class="btn btn-sm">中文a>
20 <a class="btn btn-sm">Englisha>
21 form>
--修改文件编码防止乱码:
3.实现中英文自行切换的功能:
国际化Locale(区域信息对象):LocaleResolver 获取区域信息对象:
1 @Bean
2 @ConditionalOnMissingBean
3 @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
4 public LocaleResolver localeResolver() {
5 if (this.mvcProperties
6 .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
7 return new FixedLocaleResolver(this.mvcProperties.getLocale());
8 }
9 AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
10 localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
11 return localeResolver;
12 }
--默认的区域信息解析器是根据请求头带来的区域信息获取locale来进行国际化:
--编写自定义的LocaleResolver,我们可以在请求参数中携带当前的地域信息:
1 <a class="btn btn-sm" th:href="@{/index.html(language=zh_CN)}">中文a>
2 <a class="btn btn-sm" th:href="@{/index.html(language=us_EN)}">Englisha>
1 package com.zhiyun.springboot.web_restfulcrud.component;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.springframework.util.StringUtils;
6 import org.springframework.web.servlet.LocaleResolver;
7
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10 import java.util.Locale;
11
12 /**
13 * @author : S K Y
14 * @version :0.0.1
15 * 我们可以在连接上携带区域信息
16 */
17 public class MyLocaleResolver implements LocaleResolver {
18 private Logger logger = LoggerFactory.getLogger(this.getClass());
19
20 @Override
21 public Locale resolveLocale(HttpServletRequest request) {
22 String language = request.getParameter("language");
23 logger.debug("当前的区域信息: " + language);
24 Locale locale = null;
25 if (!StringUtils.isEmpty(language)) {
26 String[] all = language.split("_");
27 locale = new Locale(all[1], all[0]);
28 logger.debug("locale: " + locale);
29 } else {
30 locale = Locale.getDefault(); //获取系统默认的
31 }
32 return locale;
33 }
34
35 @Override
36 public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
37
38 }
39 }
--将自定义的LocaleResolver添加到Spring容器中
1 //使用自定义的LocaleResolver
2 @Bean
3 public LocaleResolver localeResolver() {
4 return new MyLocaleResolver();
5 }
三.实现登录功能
1.在开发期间,为方便开发,我们可以实现禁用缓存,实现热部署:在springBoot配置文件中使用spring.thymeleaf.cache=fasle禁用缓存,而后我们在修改代码之后可以使用ctrl+F9实现重新编译
2.登录错误消息的显示:
1 @PostMapping("/user/login")
2 public String login(@RequestParam("username") String username,
3 @RequestParam("password") String password,
4 Map map) {
5 if (!StringUtils.isEmpty(username) && "123456".equals(password)) {
6 //登录成功
7 return "dashboard";
8 } else {
9 //登录失败
10 map.put("message", "用户名密码错误");
11 return "index";
12 }
13 }
1
2 <p style="color: red" th:text="${message}" th:if="${ not #strings.isEmpty(message)}">p>
3.当我们正确登录后,跳转页面后使用F5刷新界面,会提示是否重复提交表单,我们可以使用重定向的方式来防止表单的重复提交:
1 @Bean //将组件注册在容器中
2 public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
3 return new WebMvcConfigurerAdapter() {
4 @Override
5 public void addViewControllers(ViewControllerRegistry registry) {
6 //将以下路径都映射到登录界面
7 String[] all = {
8 "/", "/index", "index.html"
9 };
10 for (String content : all) {
11 registry.addViewController(content).setViewName("index");
12 }
13 //将以下路径映射到主页面
14 String[] main = {
15 "main.html"
16 };
17 for (String content : main) {
18 registry.addViewController(content).setViewName("dashboard");
19 }
20 }
21 };
22 }
1 @PostMapping("/user/login")
2 public String login(@RequestParam("username") String username,
3 @RequestParam("password") String password,
4 Map map) {
5 if (!StringUtils.isEmpty(username) && "123456".equals(password)) {
6 //登录成功,为了防止表单重复提交,可以重定向到主页
7 return "redirect:/main.html";
8 } else {
9 //登录失败
10 map.put("message", "用户名密码错误");
11 return "index";
12 }
13 }
4.但是此时如果我们直接访问/main.html就能访问到我们的地址,这样一来所做的登录操作就显得没有什么意义,我们可以使用拦截器来进行请求拦截,进行登录检查:
1 package com.zhiyun.springboot.web_restfulcrud.component;
2
3 import org.springframework.web.servlet.HandlerInterceptor;
4 import org.springframework.web.servlet.ModelAndView;
5
6 import javax.servlet.http.HttpServletRequest;
7 import javax.servlet.http.HttpServletResponse;
8 import javax.servlet.http.HttpSession;
9
10 /**
11 * @author : S K Y
12 * @version :0.0.1
13 * 进行登录检查,没有登录的用户就无法访问后台的主页进行crud操作
14 */
15 public class LoginHandlerInterceptor implements HandlerInterceptor {
16 //目标方法执行之前
17 @Override
18 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
19 HttpSession session = request.getSession();
20 Object loginUser = session.getAttribute("loginUser");
21 if (loginUser == null) { //当前未登录,返回登录页面
22 //将请求转发到登录界面
23 request.setAttribute("message", "请先登录!");
24 request.getRequestDispatcher("/index.html").forward(request, response);
25 return false;
26 } else { //以登录
27 return true;
28 }
29
30 }
31
32 @Override
33 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
34
35 }
36
37 @Override
38 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
39
40 }
41 }
1 package com.zhiyun.springboot.web_restfulcrud.config;
2
3 import com.zhiyun.springboot.web_restfulcrud.component.LoginHandlerInterceptor;
4 import com.zhiyun.springboot.web_restfulcrud.component.MyLocaleResolver;
5 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration;
7 import org.springframework.web.servlet.LocaleResolver;
8 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
9 import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
10 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
11
12 /**
13 * @author : S K Y
14 * @version :0.0.1
15 * 扩展SpringMVC的功能
16 */
17 @Configuration
18 public class MyMvcConfig extends WebMvcConfigurerAdapter {
19
20 //所有的WebMvcConfigurerAdapter组件都会一起起作用
21 @Bean //将组件注册在容器中
22 public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
23 return new WebMvcConfigurerAdapter() {
24 @Override
25 public void addViewControllers(ViewControllerRegistry registry) {
26 //将以下路径都映射到登录界面
27 String[] all = {
28 "/", "/index", "index.html"
29 };
30 for (String content : all) {
31 registry.addViewController(content).setViewName("index");
32 }
33 //将以下路径映射到主页面
34 String[] main = {
35 "main.html"
36 };
37 for (String content : main) {
38 registry.addViewController(content).setViewName("dashboard");
39 }
40 }
41 };
42 }
43
44 //使用自定义的LocaleResolver
45 @Bean
46 public LocaleResolver localeResolver() {
47 return new MyLocaleResolver();
48 }
49
50 //注册拦截器
51 @Override
52 public void addInterceptors(InterceptorRegistry registry) {
53 //SpringBoot已经做好了静态资源映射,不需要来处理静态资源
54 registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
55 .excludePathPatterns("/index.html", "/", "/index", "/user/login");
56 }
57 }
四.主页CRUD--员工列表
1.Restful风格的crud:要满足Restful风格,即路径是URL格式:/资源名称/资源标识,HTTP的请求方法区分对资源的CRUD操作.
2.查询: 普通crud(uri来区分操作,getEmp) restful(emp-GET)
3.添加: 普通crud(addEmp?xxx) restful(emp-POST)
4.修改: 普通crud(updateEmp?id=xxx&xxx=xxx) restful(emp-PUT)
5.删除: 普通crud(deleteEmp?id=xxx) restful(emp-DELETE)
本例的请求架构:
请求类型 请求的URI 请求方式
查询所有员工 emps GET
查询某个员工(单个) emp/{id} GET
来到添加页面 emp GET
添加员工 emp POST
来到修改页面(查+信息回显) emp/{id} GET
修改员工 emp PUT
删除员工 emp/{id} DELETE
--员工列表:使用Thymeleaf公共页面元素抽取
1.抽取公共片段:
内容
2.引用公共片段(~{...}可以省略)
~{templatename :: selector} 模板名::选择器
~{templatename :: fragmentname} 模板名::片段名
--我们可以发现会在其外层给嵌套一个div,这样的结构改变可能会造成一些不可预料的错误,三种引入公共片段的th属性:
1.th:insert: 会在外部包裹当前的标签
2.th:replace: 使用元素替换,即自定义便签将会替换成代码片段
3.th:include: 包含,自定义标签中加入被引入标签的内部元素(被引入便签就会被删除)
--引入片段的时候传入参数:
--commons/bar.html
1 DOCTYPE html>
2 <html lang="en" xmlns:th="http://www.thymeleaf.org">
3 <head>
4 <meta charset="UTF-8">
5 <title>Titletitle>
6 head>
7 <body>
8
9 <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0"
10 th:fragment="topbar">
11 <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#" th:href="@{/main.html}">[[
12 ${session.loginUser} ]]a>
13 <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
14 <ul class="navbar-nav px-3">
15 <li class="nav-item text-nowrap">
16 <a class="nav-link" href="#">Sign outa>
17 li>
18 ul>
19 nav>
20
21 <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
22 <div class="sidebar-sticky">
23 <ul class="nav flex-column">
24 <li class="nav-item">
25 <a class="nav-link" th:class="${activeUri=='main.html'?'nav-link active':'nav-link'}"
26 href="#" th:href="@{/main.html}">
27 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
28 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
29 stroke-linejoin="round" class="feather feather-home">
30 <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z">path>
31 <polyline points="9 22 9 12 15 12 15 22">polyline>
32 svg>
33 Dashboard <span class="sr-only">(current)span>
34 a>
35 li>
36 <li class="nav-item">
37 <a class="nav-link"
38 href="#">
39 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
40 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
41 stroke-linejoin="round" class="feather feather-file">
42 <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z">path>
43 <polyline points="13 2 13 9 20 9">polyline>
44 svg>
45 Orders
46 a>
47 li>
48 <li class="nav-item">
49 <a class="nav-link" href="#">
50 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
51 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
52 stroke-linejoin="round" class="feather feather-shopping-cart">
53 <circle cx="9" cy="21" r="1">circle>
54 <circle cx="20" cy="21" r="1">circle>
55 <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6">path>
56 svg>
57 Products
58 a>
59 li>
60 <li class="nav-item">
61 <a class="nav-link " href="#" th:href="@{/emps}"
62 th:class="${activeUri=='emps'?'nav-link active':'nav-link'}">
63 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
64 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
65 stroke-linejoin="round" class="feather feather-users">
66 <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2">path>
67 <circle cx="9" cy="7" r="4">circle>
68 <path d="M23 21v-2a4 4 0 0 0-3-3.87">path>
69 <path d="M16 3.13a4 4 0 0 1 0 7.75">path>
70 svg>
71 员工管理
72 a>
73 li>
74 <li class="nav-item">
75 <a class="nav-link" href="#">
76 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
77 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
78 stroke-linejoin="round" class="feather feather-bar-chart-2">
79 <line x1="18" y1="20" x2="18" y2="10">line>
80 <line x1="12" y1="20" x2="12" y2="4">line>
81 <line x1="6" y1="20" x2="6" y2="14">line>
82 svg>
83 Reports
84 a>
85 li>
86 <li class="nav-item">
87 <a class="nav-link" href="#">
88 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
89 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
90 stroke-linejoin="round" class="feather feather-layers">
91 <polygon points="12 2 2 7 12 12 22 7 12 2">polygon>
92 <polyline points="2 17 12 22 22 17">polyline>
93 <polyline points="2 12 12 17 22 12">polyline>
94 svg>
95 Integrations
96 a>
97 li>
98 ul>
99
100 <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
101 <span>Saved reportsspan>
102 <a class="d-flex align-items-center text-muted"
103 href="#">
104 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
105 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
106 stroke-linejoin="round" class="feather feather-plus-circle">
107 <circle cx="12" cy="12" r="10">circle>
108 <line x1="12" y1="8" x2="12" y2="16">line>
109 <line x1="8" y1="12" x2="16" y2="12">line>
110 svg>
111 a>
112 h6>
113 <ul class="nav flex-column mb-2">
114 <li class="nav-item">
115 <a class="nav-link" href="#">
116 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
117 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
118 stroke-linejoin="round" class="feather feather-file-text">
119 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
120 <polyline points="14 2 14 8 20 8">polyline>
121 <line x1="16" y1="13" x2="8" y2="13">line>
122 <line x1="16" y1="17" x2="8" y2="17">line>
123 <polyline points="10 9 9 9 8 9">polyline>
124 svg>
125 Current month
126 a>
127 li>
128 <li class="nav-item">
129 <a class="nav-link" href="#">
130 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
131 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
132 stroke-linejoin="round" class="feather feather-file-text">
133 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
134 <polyline points="14 2 14 8 20 8">polyline>
135 <line x1="16" y1="13" x2="8" y2="13">line>
136 <line x1="16" y1="17" x2="8" y2="17">line>
137 <polyline points="10 9 9 9 8 9">polyline>
138 svg>
139 Last quarter
140 a>
141 li>
142 <li class="nav-item">
143 <a class="nav-link" href="#">
144 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
145 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
146 stroke-linejoin="round" class="feather feather-file-text">
147 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
148 <polyline points="14 2 14 8 20 8">polyline>
149 <line x1="16" y1="13" x2="8" y2="13">line>
150 <line x1="16" y1="17" x2="8" y2="17">line>
151 <polyline points="10 9 9 9 8 9">polyline>
152 svg>
153 Social engagement
154 a>
155 li>
156 <li class="nav-item">
157 <a class="nav-link" href="#">
158 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
159 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
160 stroke-linejoin="round" class="feather feather-file-text">
161 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
162 <polyline points="14 2 14 8 20 8">polyline>
163 <line x1="16" y1="13" x2="8" y2="13">line>
164 <line x1="16" y1="17" x2="8" y2="17">line>
165 <polyline points="10 9 9 9 8 9">polyline>
166 svg>
167 Year-end sale
168 a>
169 li>
170 ul>
171 div>
172 nav>
173 body>
174 html>
--dashboard.html
1 DOCTYPE html>
2
3 <html lang="en" xmlns:th="http://www.thymeleaf.org">
4 <head>
5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7 <meta name="description" content="">
8 <meta name="author" content="">
9
10 <title>Dashboard Template for Bootstraptitle>
11
12 <link href="asserts/css/bootstrap.min.css" th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">
13
14
15 <link href="asserts/css/dashboard.css" th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
16 <style type="text/css">
17 /* Chart.js */
18
19 @-webkit-keyframes chartjs-render-animation {
20 from {
21 opacity: 0.99
22 }
23 to {
24 opacity: 1
25 }
26 }
27
28 @keyframes chartjs-render-animation {
29 from {
30 opacity: 0.99
31 }
32 to {
33 opacity: 1
34 }
35 }
36
37 .chartjs-render-monitor {
38 -webkit-animation: chartjs-render-animation 0.001s;
39 animation: chartjs-render-animation 0.001s;
40 }
41 style>
42 head>
43
44 <body>
45
46 <div th:replace="~{commons/bar :: topbar}">div>
47 <div class="container-fluid">
48 <div class="row">
49
50 <div th:replace="~{commons/bar :: sidebar(activeUri='main.html')}">div>
51 <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
52 <div class="chartjs-size-monitor"
53 style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;">
54 <div class="chartjs-size-monitor-expand"
55 style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
56 <div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0">div>
57 div>
58 <div class="chartjs-size-monitor-shrink"
59 style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
60 <div style="position:absolute;width:200%;height:200%;left:0; top:0">div>
61 div>
62 div>
63 <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
64 <h1 class="h2">Dashboardh1>
65 <div class="btn-toolbar mb-2 mb-md-0">
66 <div class="btn-group mr-2">
67 <button class="btn btn-sm btn-outline-secondary">Sharebutton>
68 <button class="btn btn-sm btn-outline-secondary">Exportbutton>
69 div>
70 <button class="btn btn-sm btn-outline-secondary dropdown-toggle">
71 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
72 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
73 stroke-linejoin="round" class="feather feather-calendar">
74 <rect x="3" y="4" width="18" height="18" rx="2" ry="2">rect>
75 <line x1="16" y1="2" x2="16" y2="6">line>
76 <line x1="8" y1="2" x2="8" y2="6">line>
77 <line x1="3" y1="10" x2="21" y2="10">line>
78 svg>
79 This week
80 button>
81 div>
82 div>
83
84 <canvas class="my-4 chartjs-render-monitor" id="myChart" width="1076" height="454"
85 style="display: block; width: 1076px; height: 454px;">canvas>
86 main>
87 div>
88 div>
89
90
92
93 <script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js"
94 th:src="@{/asserts/js/jquery-3.2.1.slim.min.js}">script>
95 <script type="text/javascript" src="asserts/js/popper.min.js" th:src="@{/asserts/js/popper.min.js}">script>
96 <script type="text/javascript" src="asserts/js/bootstrap.min.js"
97 th:src="@{/asserts/js/bootstrap.min.js}">script>
98
99
100 <script type="text/javascript" src="asserts/js/feather.min.js"
101 th:src="@{/asserts/js/feather.min.js}">script>
102 <script>
103 feather.replace()
104 script>
105
106
107 <script type="text/javascript" src="asserts/js/Chart.min.js"
108 th:src="@{/asserts/js/Chart.min.js}">script>
109 <script>
110 var ctx = document.getElementById("myChart");
111 var myChart = new Chart(ctx, {
112 type: 'line',
113 data: {
114 labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
115 datasets: [{
116 data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
117 lineTension: 0,
118 backgroundColor: 'transparent',
119 borderColor: '#007bff',
120 borderWidth: 4,
121 pointBackgroundColor: '#007bff'
122 }]
123 },
124 options: {
125 scales: {
126 yAxes: [{
127 ticks: {
128 beginAtZero: false
129 }
130 }]
131 },
132 legend: {
133 display: false
134 }
135 }
136 });
137 script>
138
139 body>
140
141 html>
--emp/list.html
1 DOCTYPE html>
2
3 <html lang="en" xmlns:th="http://www.thymeleaf.org">
4
5 <head>
6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8 <meta name="description" content="">
9 <meta name="author" content="">
10
11 <title>Dashboard Template for Bootstraptitle>
12
13 <link href="asserts/css/bootstrap.min.css" rel="stylesheet">
14
15
16 <link href="asserts/css/dashboard.css" rel="stylesheet">
17 <style type="text/css">
18 /* Chart.js */
19
20 @-webkit-keyframes chartjs-render-animation {
21 from {
22 opacity: 0.99
23 }
24 to {
25 opacity: 1
26 }
27 }
28
29 @keyframes chartjs-render-animation {
30 from {
31 opacity: 0.99
32 }
33 to {
34 opacity: 1
35 }
36 }
37
38 .chartjs-render-monitor {
39 -webkit-animation: chartjs-render-animation 0.001s;
40 animation: chartjs-render-animation 0.001s;
41 }
42 style>
43 head>
44
45 <body>
46
47
48 <div th:replace="~{commons/bar :: topbar}">div>
49
50 <div class="container-fluid">
51 <div class="row">
52
53 <div th:replace="~{ commons/bar :: sidebar(activeUri='emps') }">div>
54 <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
55 <h2>员工列表 <button class="btn btn-sm btn-success">添加button>h2>
56 <div class="table-responsive">
57 <table class="table table-striped table-sm">
58 <thead>
59 <tr>
60 <th>姓名th>
61 <th>邮箱th>
62 <th>性别th>
63 <th>部门th>
64 <th>生日th>
65 <th>操作th>
66 tr>
67 thead>
68 <tbody>
69 <tr th:each="item:${employees}">
70 <td th:text="${item.lastName}">td>
71 <td th:text="${item.email}">td>
72 <td th:text="${item.gender == 0 ? '女':'男'}">td>
73 <td th:text="${item.department.departmentName}">td>
74
75 <td th:text="${#dates.format(item.birth,'yyyy-MM-dd HH:mm:ss')}">td>
76 <td>
77 <button class="btn btn-sm btn-primary">编辑button>
78 <button class="btn btn-sm btn-danger">删除button>
79 td>
80 tr>
81 tbody>
82 table>
83 div>
84 main>
85 div>
86 div>
87
88
90
91 <script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js">script>
92 <script type="text/javascript" src="asserts/js/popper.min.js">script>
93 <script type="text/javascript" src="asserts/js/bootstrap.min.js">script>
94
95
96 <script type="text/javascript" src="asserts/js/feather.min.js">script>
97 <script>
98 feather.replace()
99 script>
100
101
102 <script type="text/javascript" src="asserts/js/Chart.min.js">script>
103 <script>
104 var ctx = document.getElementById("myChart");
105 var myChart = new Chart(ctx, {
106 type: 'line',
107 data: {
108 labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
109 datasets: [{
110 data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
111 lineTension: 0,
112 backgroundColor: 'transparent',
113 borderColor: '#007bff',
114 borderWidth: 4,
115 pointBackgroundColor: '#007bff'
116 }]
117 },
118 options: {
119 scales: {
120 yAxes: [{
121 ticks: {
122 beginAtZero: false
123 }
124 }]
125 },
126 legend: {
127 display: false,
128 }
129 }
130 });
131 script>
132
133 body>
134
135 html>
五.实现新员工的添加
在进行员工添加的时候,一个核心的问题就是日期格式的转化,在SpringBoot中有如下的自动装配机制:
--我们可以通过修改配置文件以'-'的日期格式来提交
--add.html
1 DOCTYPE html>
2
3 <html lang="en" xmlns:th="http://www.thymeleaf.org">
4
5 <head>
6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8 <meta name="description" content="">
9 <meta name="author" content="">
10
11 <title>Dashboard Template for Bootstraptitle>
12
13 <link href="asserts/css/bootstrap.min.css" th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">
14
15
16 <link href="asserts/css/dashboard.css" th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
17 <style type="text/css">
18 /* Chart.js */
19
20 @-webkit-keyframes chartjs-render-animation {
21 from {
22 opacity: 0.99
23 }
24 to {
25 opacity: 1
26 }
27 }
28
29 @keyframes chartjs-render-animation {
30 from {
31 opacity: 0.99
32 }
33 to {
34 opacity: 1
35 }
36 }
37
38 .chartjs-render-monitor {
39 -webkit-animation: chartjs-render-animation 0.001s;
40 animation: chartjs-render-animation 0.001s;
41 }
42 style>
43 head>
44
45 <body>
46
47
48 <div th:replace="~{commons/bar :: topbar}">div>
49
50 <div class="container-fluid">
51 <div class="row">
52
53 <div th:replace="~{ commons/bar :: sidebar(activeUri='emps') }">div>
54 <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
55 <form th:action="@{/emp}" method="POST">
56 <div class="form-group"><label>LastNamelabel>
57 <input type="text" class="form-control"
58 placeholder="zhangsan" name="lastName">
59 div>
60 <div class="form-group"><label>Emaillabel>
61 <input type="email" class="form-control"
62 placeholder="zhangsan@atguigu.com"
63 name="email">div>
64 <div class="form-group"><label>Genderlabel><br/>
65 <div class="form-check form-check-inline">
66 <input class="form-check-input" type="radio"
67 name="gender" value="1"> <label
68 class="form-check-label">男label>div>
69 <div class="form-check form-check-inline">
70 <input class="form-check-input" type="radio"
71 name="gender" value="0"> <label
72 class="form-check-label">女label>div>
73 div>
74 <div class="form-group"><label>departmentlabel>
75 <select class="form-control"
76 name="department.id">
77
78 <option th:each="item:${depts}" th:text="${item.departmentName}" th:value="${item.id}">1
79 option>
80 select>div>
81 <div class="form-group"><label>Birthlabel>
82
86 <input type="text" class="form-control"
87 placeholder="zhangsan" name="birth">div>
88 <button type="submit" class="btn btn-primary">添加button>
89 form>
90 main>
91 div>
92 div>
93
94
96
97 <script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js">script>
98 <script type="text/javascript" src="asserts/js/popper.min.js">script>
99 <script type="text/javascript" src="asserts/js/bootstrap.min.js">script>
100
101
102 <script type="text/javascript" src="asserts/js/feather.min.js">script>
103 <script>
104 feather.replace()
105 script>
106
107
108 <script type="text/javascript" src="asserts/js/Chart.min.js">script>
109 <script>
110 var ctx = document.getElementById("myChart");
111 var myChart = new Chart(ctx, {
112 type: 'line',
113 data: {
114 labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
115 datasets: [{
116 data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
117 lineTension: 0,
118 backgroundColor: 'transparent',
119 borderColor: '#007bff',
120 borderWidth: 4,
121 pointBackgroundColor: '#007bff'
122 }]
123 },
124 options: {
125 scales: {
126 yAxes: [{
127 ticks: {
128 beginAtZero: false
129 }
130 }]
131 },
132 legend: {
133 display: false,
134 }
135 }
136 });
137 script>
138
139 body>
140
141 html>
1 //来到员工添加页面
2 @GetMapping("/emp")
3 public String toAddPage(Model model) {
4 //查出所有的部门
5 model.addAttribute("depts", departmentDao.getDepartments());
6 return "emp/add";
7 }
8
9 //添加员工
10 //SpringMVC自动将请求参数和入参对象的属性名一一绑定,请求参数的名字和javaBean入参的属性名是一样的
11 @PostMapping("/emp")
12 public String addEmp(Employee employee) {
13 logger.debug("当前的新员工: " + employee);
14 //保存员工
15 employeeDao.save(employee);
16 //来到员工列表
17 //redirect: 表示重定向到一个地址
18 //forward: 表示转发到一个地址
19 return "redirect:/emps";
20 }
六.员工修改
我们期望在员工列表页面点击编辑则可以进行员工的修改操作:
1
2
6
7 <form th:action="@{/emp}" method="POST">
8 <input th:type="hidden" th:name="_method" th:value="PUT" th:if="${employee != null}"/>
9
10 <input th:type="hidden" name="id" th:if="${employee != null}" th:value="${employee.id}"/>
11
12 <div class="form-group"><label>LastNamelabel>
13 <input type="text" class="form-control"
14 placeholder="zhangsan" name="lastName"
15 th:value="${employee != null} ? ${employee.lastName}">
16 div>
17 <div class="form-group"><label>Emaillabel>
18 <input type="email" class="form-control"
19 placeholder="zhangsan@atguigu.com"
20 name="email" th:value="${employee != null} ? ${employee.email}">div>
21 <div class="form-group"><label>Genderlabel><br/>
22 <div class="form-check form-check-inline">
23 <input class="form-check-input" type="radio"
24 name="gender" value="1"
25 th:checked="${employee != null} ? ${employee.gender == 1}"> <label
26 class="form-check-label">男label>div>
27 <div class="form-check form-check-inline">
28 <input class="form-check-input" type="radio"
29 th:checked="${employee != null} ? ${employee.gender == 0}"
30 name="gender" value="0"> <label
31 class="form-check-label">女label>div>
32 div>
33 <div class="form-group"><label>departmentlabel>
34 <select class="form-control"
35 name="department.id">
36
37 <option th:each="item:${depts}" th:text="${item.departmentName}" th:value="${item.id}"
38 th:selected="${employee != null} ? ${employee.department.id == item.id}">1
39 option>
40 select>div>
41 <div class="form-group"><label>Birthlabel>
42
46 <input type="text" class="form-control"
47 placeholder="zhangsan" name="birth"
48 th:value="${employee != null} ? ${#dates.format(employee.birth,'yyyy-MM-dd')}">div>
49 <button type="submit" class="btn btn-primary" th:text=" ${employee != null ? '修改':'添加'}">添加
50 button>
51 form>
1 @PutMapping("/emp")
2 public String updateEmployee(Employee employee) {
3 logger.debug("修改后的员工数据: " + employee);
4 employeeDao.save(employee);
5 return "redirect:/emps";
6 }
七.员工删除
1 <script>
2 $(".deleteButton").click(function () {
3 var delete_uri = $(this).attr("delete_uri");
4 $("#deleteEmpForm").attr("action", delete_uri).submit();
5 //取消按钮的默认行为
6 return false;
7 })
8 script>
1 <td>
2 <a class="btn btn-sm btn-primary" th:href="@{/emp/} + ${item.id}" href="#">编辑a>
3 <a type="submit" class="btn btn-sm btn-danger deleteButton"
4 th:attr="delete_uri=@{/emp/} + ${item.id}">删除a>
5 td>
6 tr>
7 tbody>
8 table>
9 div>
10 main>
11 <form method="post" id="deleteEmpForm">
12 <input type="hidden" name="_method" value="DELETE"/>
13 form>
1 @DeleteMapping("/emp/{id}")
2 public String deleteEmployee(@PathVariable("id") Integer id) {
3 logger.debug("当前需要删除的员工id: " + id);
4 employeeDao.delete(id);
5 return "redirect:/emps";
6 }