Fms-Java版开发实录:09 重构目前为止的页面结构

Fms-Java版开发实录:09 重构目前为止的页面结构

在继续开发之前,先把模板体系创建好。

看完Thymeleaf的模板之后,来重构一下页面。

目前的页面结构与基础页面设计

使用整体居中效果的页面是login.htmllogout.htmlregister.html
这三个页面的共同特点是Head部分都使用了headforLoginandLogout并且传入title,然后body的主体部分都是一个main标签。这就意味这实际每个页面不同的部分只有两处,即titlemain,此外,这三个页面的headforLoginandLogout相比标准的head部分,多了自定义的style部分,因此这个也可以当做一个变量。对于title,则可以写在各个页面的title里传进去。除此之外,还需要有一个传入script标签的变量。

一个四变量的模板

按照前边的设想,这个模板有四个变量,分别是titlestylemainscript部分,编写如下:

<!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,要注意的修改点如下:

  1. 样式部分从原来的headforLoginandLogout中复制到login.html的head标签内
  2. 发现页面在body属性上添加了class="text-center",现在修改.form-signin这个样式类,添加class="text-center",去掉body标签的样式类。
  3. 传入的参数分别是:~{::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 &copy;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 &copy;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,并在standardbody标签引入,这样就都解耦了。

LICENSED UNDER CC BY-NC-SA 4.0
Comment