看完Thymeleaf的模板之后,来重构一下页面。
目前的页面结构与基础页面设计
使用整体居中效果的页面是login.html
,logout.html
和register.html
。
这三个页面的共同特点是Head
部分都使用了headforLoginandLogout
并且传入title
,然后body
的主体部分都是一个main
标签。这就意味这实际每个页面不同的部分只有两处,即title
和main
,此外,这三个页面的headforLoginandLogout
相比标准的head
部分,多了自定义的style
部分,因此这个也可以当做一个变量。对于title
,则可以写在各个页面的title
里传进去。除此之外,还需要有一个传入script
标签的变量。
一个四变量的模板
按照前边的设想,这个模板有四个变量,分别是title
、style
、main
、script
部分,编写如下:
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" th:fragment="layout(title, style, content, script)">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link href="/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link href="/css/all.min.css" th:href="@{/css/all.min.css}" rel="stylesheet">
<link rel="shortcut icon" th:href="@{/img/favicon.ico}"/>
<title th:replace="${title}">财务管理系统</title>
<style th:replace="${style}"></style>
</head>
<body>
<th:block th:replace="${content}"/>
<script th:fragment="javascript" src="/js/bootstrap.bundle.min.js" th:src="@{/js/bootstrap.bundle.min.js}"></script>
<script th:replace="${script}"></script>
</body>
</html>
这个模板我起名叫standard.html
,放在/templates/shared
目录之下。
改造login.html
将login.html直接继承standard.html
,要注意的修改点如下:
- 样式部分从原来的headforLoginandLogout中复制到
login.html
的head标签内 - 发现页面在body属性上添加了
class="text-center"
,现在修改.form-signin
这个样式类,添加class="text-center"
,去掉body
标签的样式类。 - 传入的参数分别是:
~{::title}
即页面的title标签部分,~{::style}
即页面的style
标签部分(可以传入多个style
标签),~{::main}
即页面的main
标签部分。只要注意不要有多个main
标签就可以了,如果不使用main
标签,也可以用div
,在参数上使用#
来按照id
传入标签即可,改造后的页面如下:
<!doctype html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
th:replace="~{/shared/standard::layout(~{::title}, ~{::style}, ~{::main},_)}">
<head>
<title>登录</title>
<style>
html,
body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
text-align: center;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: -1px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
</style>
</head>
<body class="text-center">
<main class="form-signin">
<form action="/account/auth" th:action="@{/account/auth}" method="post">
<img class="mb-4" src="/img/sinochem.png" th:src="@{/img/sinochem.png}" alt="中化集团">
<h1 class="h3 mb-3 fw-normal">请登录</h1>
<div th:if="${failed} != null" class="alert alert-danger" role="alert">
用户名或密码错误,或用户未激活,请<a href="mailto:liyiming@sinochem.com" style="text-decoration: none">联系管理员</a>。
</div>
<div class="form-floating">
<input type="text" class="form-control" id="floatingInput" placeholder="用户名" name="username">
<label for="floatingInput">用户名</label>
</div>
<div class="form-floating">
<input type="password" name="password" class="form-control" id="floatingPassword" placeholder="密码">
<label for="floatingPassword">密码</label>
</div>
<p class="mt-3 mb-3">还没有账号?请先<a href="/account/register" th:href="@{/account/register}"
style="text-decoration: none">注册账号</a></p>
<p th:if="${failed} == null" class="mb-3 text-muted">首次登录请 <a href="mailto:liyiming@sinochem.com"
style="text-decoration: none">联系管理员</a> 激活账号
</p>
<button class="w-100 btn btn-lg btn-primary" type="submit">登录</button>
<p class="mt-3 mb-3 text-muted">Copyrights ©2021
<a href="https://conyli.cc" target="_blank" style="text-decoration: none">https://conyli.cc</a>
</p>
<p class="mt-3 mb-3 text-muted">All Rights Reserved</p>
</form>
</main>
</body>
</html>
改造logout.html
这个页面和login
几乎完全一样:
<!doctype html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
th:replace="~{/shared/standard::layout(~{::title}, ~{::style}, ~{::main},_)}">
<head>
<title>登出</title>
<style>
......
</style>
</head>
<body>
<main class="form-signin">
<form action="/account/signout" th:action="@{/account/signout}" method="post">
<h1 class="h3 mb-3 fw-normal">确认登出</h1>
<button class="w-100 btn btn-lg btn-primary" type="submit">登出</button>
</form>
</main>
</body>
</html>
改造register.html
也是同样的套路:
<!doctype html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
th:replace="~{/shared/standard::layout(~{::title}, ~{::style}, ~{::main},_)}">
<head>
<title>用户注册</title>
<style>
......
</style>
</head>
<body>
<main class="form-signin">
<form action="/account/auth" th:action="@{/account/register}" method="post" th:object="${userRegistration}">
<h1 class="h3 mb-3 fw-normal">用户注册</h1>
<div class="form-floating">
<input type="text" maxlength="150" required class="form-control" th:field="*{username}" id="username"
placeholder="用户名">
<label for="username">用户名</label>
<div th:if="${#fields.hasErrors('username')}" class="alert alert-danger" role="alert"
th:errors="*{username}"></div>
<div th:if="${userExists}" class="alert alert-danger" role="alert" th:text="${userExists}"></div>
</div>
<div class="form-floating">
<input type="email" maxlength="255" required class="form-control" th:field="*{email}" id="email"
placeholder="电子邮件">
<label for="email">电子邮件</label>
<div th:if="${#fields.hasErrors('email')}" class="alert alert-danger" role="alert"
th:errors="*{email}"></div>
</div>
<div class="form-floating">
<input type="password" maxlength="128" required class="form-control" th:field="*{password1}" id="password1"
placeholder="密码">
<label for="password1">密码</label>
<div th:if="${#fields.hasErrors('password1')}" class="alert alert-danger" role="alert"
th:errors="*{password1}"></div>
</div>
<div class="form-floating">
<input type="password" maxlength="128" required class="form-control" th:field="*{password2}" id="password2"
placeholder="确认密码">
<label for="password2">确认密码</label>
<div th:if="${#fields.hasErrors('password2')}" class="alert alert-danger" role="alert"
th:errors="*{password2}"></div>
<div th:if="${passwordError}" class="alert alert-danger" role="alert" th:text="${passwordError}"></div>
</div>
<p class="mt-2 mb-2 text-muted">注册后请 <a href="mailto:liyiming@sinochem.com"
style="text-decoration: none">联系管理员</a> 激活账号</p>
<button class="w-100 btn btn-lg btn-primary mt-3" type="submit">提交</button>
<p class="mt-3 mb-3 text-muted">Copyrights ©2021
<a href="https://conyli.cc" target="_blank" style="text-decoration: none">https://conyli.cc</a>
</p>
<p class="mt-3 mb-3 text-muted">All Rights Reserved</p>
</form>
</main>
</body>
</html>
error.html
最后不要忘记了错误页面:
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
th:replace="~{/shared/standard::layout(~{::title}, _, ~{::main},_)}">
<head>
<title>错误</title>
</head>
<body>
<main class="container">
<h3 class="text-center mt-3 mb-3">出错啦</h3>
<ul class="list-group">
<li class="list-group-item">状态:[[${status}]]</li>
<li class="list-group-item">路径:[[${path}]]</li>
<li class="list-group-item">异常:[[${error}]]</li>
<li class="list-group-item">时间:[[${timestamp}]]</li>
<li class="list-group-item">类名:[[${exception}]]</li>
<li class="list-group-item">信息:[[${message}]]</li>
</ul>
<p th:if="${status} == '404'" class="text-center mt-2">访问的页面不存在</p>
<p th:if="${status} == '500'" class="text-center mt-2">服务器错误,请将错误信息<a href="mailto:liyiming@sinochem.com">发送给管理员</a></p>
<p th:if="${status} == '403'" class="text-center mt-2">不具备权限,请<a href="mailto:liyiming@sinochem.com">联系管理员</a></p>
<p class="text-center"><a class="btn btn-primary" href="/" th:href="@{/}">返回首页</a></p>
</main>
</body>
</html>
收尾工作
从目前来看,我们的title
基本上不需要动态传入了,除非特殊情况,每个模板单独写在head
标签里的title
足够使用了,而且这个title
作为参数组装到最终的模板中,非常好用。
如果有自行编写的样式表和JavaScript
代码,也都可以方便的插入到standard
模板中。Thymeleaf
这个模板功能真的比Django
模板还要强大啊。
由于暂时不使用动态title
,因此可以把构造器中向视图传入的title
全部去掉了。
现在页面继承关系也准备好了,可以继续编写剩下的部分了。以后的导航条组件可以单独定义一个fragment
,并在standard
中body
标签引入,这样就都解耦了。