练手小项目 - bill-manager(springboot、tk mybatis、thymeleaf、PageHelper的简单应用)零基础版

记账管理项目

项目git地址:https://gitee.com/pdh_gitee/bill-manager.git

此项目简单的应用到的技术栈:springboot,mybatis和tk mybatis(持久化),thymeleaf(前端页面展示)和一些简单的前端基础知识。零基础即可入门,实现对springboot,mybatis和tk mybatis(持久化),thymeleaf的了解,也可在此基础之上,查看相关的文档,更进一步的了解学习这些知识。下面给两个文档:

SpringBoot中文文档:http://felord.cn/_doc/_springboot/2.1.5.RELEASE/_book/

MyBatis中文文档:https://mybatis.org/mybatis-3/zh/index.html


0.项目介绍

基于springboot,mybatis和tk mybatis(持久化),thymeleaf(前端页面展示),实现简单的记账管理功能,数据表有两张(bill和bill_type),bill表包括编号,标题,记账时间,账单类型,金额,解释;bill_type表应包括账单类型编号,账单类型名称。两表的详细信息如下:

0.1 bill_type表

bill_type表的详细信息

字段名中文类型长度约束默认值备注
id类型编号bigint20主键、自增、非空-
name类型名称varchar100null

bill_type表索引

索引名字段索引类型索引方法注释
id(默认字段)idPRIMARYBTREE创建主键时自动生成,无需手动创建

建表语句

CREATE TABLE `bill_type` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT   COMMENT '类型编号',
  `name` varchar(100) DEFAULT NULL          COMMENT '类型名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;

0.2 bill_type表

bill表的详细信息

字段名中文类型长度约束默认值备注
id账单编号bigint20主键、自增、非空-
title账单标题varchar100null
bill_time记账时间date-null
type_id账单类型bigint20null
price金额double10null长度为10,小数2位
explain说明varchar100null

bill_type表索引

foreign key:外键约束,其中bill表中的type_id是bill_type中的id主键。

索引名字段索引类型索引方法注释
id(默认字段)idPRIMARYBTREE创建主键时自动生成,无需手动创建
fk_type_billtype_idKEYBTREE普通索引
fk_type_billtype_id、idCONSTRAINTBTREE外键约束,type_id对应bill_type中的id

建表语句

CREATE TABLE `bill` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT   COMMENT '账单编号',
  `title` varchar(100) DEFAULT NULL         COMMENT '账单标题',
  `bill_time` date DEFAULT NULL             COMMENT '记账时间',
  `type_id` bigint(20) DEFAULT NULL         COMMENT '账单类型',
  `price` double(10,2) DEFAULT NULL         COMMENT '金额',
  `explain` varchar(100) DEFAULT NULL       COMMENT '说明',
  PRIMARY KEY (`id`),
  KEY `fk_type_bill` (`type_id`),
  CONSTRAINT `fk_type_bill` FOREIGN KEY (`type_id`) REFERENCES `bill_type` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;

1.项目搭建

我的环境:Win10、IDEA、JDK11、MAVEN,有网络。

新建一个SpringBoot项目(启动类存在,若不存在得先搭建),命名为bill-manager,下面是pom.xml配置文件中的依赖:

<dependencies>
    <!--web依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--mybatis通用mapper,引入tk.mybatis就不需要再引入mybatis依赖,因为tk.mybatis包括mybatis了-->
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>2.0.3</version>
    </dependency>

    <!--mysql依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!--页面的模板引擎thymeleaf-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <!-- 分页插件-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>

之后在java包下建立entity、dao、service、controller、utils包,存放相关的类,若resouce包下没有自动生成static和templates包,手动创建(详细的查看项目源码)。


2.连接数据库

数据库yml配置,application.yml文件,放在resources包下

# mysql连接池 我的数据库没设密码
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password:
    url: jdbc:mysql://localhost:3306/bill-manager
  # 关掉thymeleaf缓存
  thymeleaf:
    cache: false

# 整合mybatis
mybatis:
  type-aliases-package: com.pdh.entity #别名搜索配置
  mapper-locations: classpath:/mybatis/*.xml # 放置配置文件

之后就是执行sql脚本,bill-manager.sql文件在登陆数据库之后执行即可(会自动建库、建表、插入数据)。


3.创建实体类

实体类与数据库对应,在entity包下,还需生成getter/setter方法:

BillType类

package com.pdh.entity;
import javax.persistence.*;
import java.util.Date;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 10:17
 * @Description:
 */
@Table(name = "bill")
public class Bill {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "title")
    private String title;
    @Column(name = "bill_time")
    private Date billTime;
    @Column(name = "type_id")
    private Long typeId;
    @Column(name = "price")
    private Double price;
    @Column(name = "explain")
    private String explain;

    /**
     * @Transient 瞬时属性,与字段没有映射
     * 类型名称:用于查询
     */
    @Transient
    private String typeName;

    /**
     * 开始时间:用于查询
     */
    @Transient
    private Date dateBegin;

    /**
     * 结束时间:用于查询
     */
    @Transient
    private Date dateEnd;
}

Bill类

package com.pdh.entity;
import javax.persistence.*;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 9:59
 * @Description:
 */
@Table(name = "bill_type")
public class BillType {
    /**
     * @Id  表示为主键字段
     * @GeneratedValue(strategy = GenerationType.IDENTITY)  自增主键策略
     * @Column(name = "id_")    与数据库中的字段匹配
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name")
    private String name;
}


4.编写dao层

这里采用动态sql语句实现。dao层继承Maper类,减少个人代码开发。

4.1 BillMapper和TypeMapper接口

package com.pdh.dao;
import com.pdh.entity.Bill;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 10:36
 * @Description:
 */
public interface BillMapper extends Mapper<Bill> {
    public List<Bill> select(Bill bill);
}
package com.pdh.dao;
import com.pdh.entity.BillType;
import tk.mybatis.mapper.common.Mapper;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 10:34
 * @Description: 目前还没有加入方法,有需求可以加入
 */
public interface TypeMapper extends Mapper<BillType> {

}


4.2 BillMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pdh.dao.BillMapper">
    <!--原生sql语句-->
    <sql id="selectSql">
        SELECT
            b.id as id,
            b.title as title,
            b.bill_time as billTime,
            b.type_id as typeId,
            b.price as price,
            b.explain as `explain`,/*explain是mysql关键字,使用的话必须的使用``括住*/
            t.name as typeName
        FROM
            bill as b
        left join
            bill_type as t
        on
            b.type_id = t.id
    </sql>

    <!--插入where条件,实现动态sql-->
    <select id="select" resultType="com.pdh.entity.Bill">
        <include refid="selectSql"/>
        <where>
            /*判断 为true就执行,false就跳过*/
            <if test="typeId !=null">
                b.type_id = #{typeId}
            </if>
            <if test="title !=null">
                and b.title like '%${title}%'
            </if>
            <if test="dateBegin !=null">
                and b.bill_time &gt;= #{dateBegin}
            </if>
            <if test="dateEnd !=null">
                and b.bill_time &lt;= #{dateEnd}
            </if>
        </where>
    </select>
</mapper>

编写service层

5.编写service层

此层作为dao层和controller层的中间层,作用就是减少dao和controller层的耦合。有两个service类:

5.1 BillService类

package com.pdh.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.pdh.dao.BillMapper;
import com.pdh.entity.Bill;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 11:02
 * @Description:
 */
@Service
public class BillService {
    @Autowired
    private BillMapper billMapper;

    /**
     * 查询
     * @param bill
     * @return
     */
    public List<Bill> list(Bill bill){
        return billMapper.select(bill);
    }

    /**
     * 添加Bill数据
     * @param bill
     * @return
     */
    public int add(Bill bill){
        //调用Mapper类的方法,内置的常用方法
        return billMapper.insert(bill);
    }

    /**
     * 根据主键id获取数
     * @param id
     * @return
     */
    public Bill get(Long id) {
        return billMapper.selectByPrimaryKey(id);
    }

    /**
     * 更新单个数据
     * @param bill
     * @return
     */
    public int update(Bill bill){
        return billMapper.updateByPrimaryKey(bill);
    }

    /**
     * 删除数据
     * @param id
     * @return
     */
    public int delete(Long id){
        return billMapper.deleteByPrimaryKey(id);
    }

    /**
     * 分页操作
     * @param bill
     * @param pageNum
     * @param pageSize
     * @return
     */
    public PageInfo<Bill> listPage(Bill bill,int pageNum,int pageSize){
        /**
         * 使用PageHelper进行后端分页,获取到指定的数据。比如
         *      指定bill类中的某一数据
         *      指定页数
         *      指定某一页的数据量
         */
        return PageHelper.startPage(pageNum,pageSize).doSelectPageInfo(() -> {
            billMapper.select(bill);
        });
    }
}

5.2 TypeService类

package com.pdh.service;

import com.pdh.dao.TypeMapper;
import com.pdh.entity.BillType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 11:09
 * @Description:
 */
@Service
public class TypeService {
    @Autowired
    private TypeMapper typeMapper;

    /**
     * 获取BillType的列表
     * @return
     */
    public List<BillType> list(){
        //直接调用tk.mybatis内置接口,实现查询操作
        return typeMapper.selectAll();
    }
}

6.编写controller类

在编写controller之前,先来了解一下下面的知识:

1.Model接口的作用:从控制层直接返回前端所需要的数据。spring自动为Model创建实例,并作为controller的入参,可以结合下面的写法理解运用。

2.重定向和转发:两者是页面跳转的两种方式。重定向会丢失request的信息,浏览器会打开一个新的地址(新的请求);转发不会丢失request信息,可在多个页面交互过程中实现数据共享。

BillController类

  1. @RestController等于@Controller+@ResponseBody,表示返回json数据,不能跳转页面,所以要跳转页面,就不能使用@RestController。
  2. 另外,@RequestMapping()匹配所有类型的请求,建议使用@GetMapping()或者@PostMapping()注解指定的请求,符合之后的Swagger接口测试要求。
package com.pdh.contoller;
import com.github.pagehelper.PageInfo;
import com.pdh.entity.Bill;
import com.pdh.entity.BillType;
import com.pdh.service.BillService;
import com.pdh.service.TypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 13:38
 * @Description: 后端处理差不多就是这样,跟多的是前端处理
 */
@Controller
@RequestMapping("/bill") //根注解
public class BillController {
    @Autowired
    private TypeService typeService;
    @Autowired
    private BillService billService;

    /**
     * 查询
     * @param bill 接收页面请求的参数
     * @param model 把查询的结果放到model中,此model会响应到前端数据
     * @return
     */
    @RequestMapping("/list")
    public String list(Bill bill, Model model){
        List<BillType> types = typeService.list();
        //在数据展示的时候,需要直接获取到类型列表
        model.addAttribute("types",types);

        //获取账单列表
        List<Bill> bills = billService.list(bill);
        model.addAttribute("bills",bills);

        /**
         * 跳转到此路径进行数据显示,相当于发起此请求
         * 遵循HTML5规范,不能写成
         *      return "/bill/list";
         */
        return "bill/list";
    }

    /**
     * 分页查询
     * @param bill 接收页面请求的参数
     * @param model 把查询的结果放到model中
     * @return
     */
    @RequestMapping("/list-page")
    public String listPage(
            /**
             * RequestParam:匹配请求中的参数,传递的名称与之对应即可
             * 接收前端发过来的name属性,且字符必须得一一对应
             */
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            Bill bill,
            Model model){
        List<BillType> types = typeService.list();
        model.addAttribute("types",types);
        PageInfo page = billService.listPage(bill,pageNum,pageSize);
        model.addAttribute("page",page);

        return "bill/list-page";
    }

    /**
     * 跳转到添加页面
     * @param model 获取对应的数据存储在model中,model拿到数据后进行响应
     * @return
     */
    @RequestMapping("/toAdd")
    public String toAdd(Model model){
        List<BillType> types = typeService.list();
        model.addAttribute("types",types);
        return "bill/add";
    }

    /**
     * 添加
     * 添加完就跳转到list页面
     * @param bill
     * @return
     */
    @RequestMapping("/add")
    public String add(Bill bill){
        billService.add(bill);
        //这里是重定向到此页面
        return "redirect:/bill/list-page";
    }

    /**
     * 删除
     * 传入id
     * @param id
     * @return
     */
    @RequestMapping("/delete/{id}") // delete/{id} 这种请求格式,用占位符传参
    public String delete(@PathVariable("id") Long id){
        billService.delete(id);
        return "redirect:/bill/list-page";
    }


    /**
     * 修改
     * 跳转到修改页面
     * @param id
     * @param model
     * @return
     */
    @RequestMapping("/toUpdate/{id}")
    public String toUpdate(
            //@PathVariable注解匹配请求中传递的参数
            @PathVariable("id") Long id
            ,Model model){
        List<BillType> types = typeService.list();
        model.addAttribute("types",types);
        Bill bill = billService.get(id);
        model.addAttribute("bill",bill);

        return "bill/update";
    }

    /**
     * 修改
     * @param bill
     * @return
     */
    @RequestMapping("/update")
    public String update(Bill bill){
        billService.update(bill);
        return "redirect:/bill/list-page";
    }
}

7.日期处理器工具

DateConverterConfig(日期转换配置)

package com.pdh.utils;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author: 彭_德华
 * @Date: 2021-08-30 15:03
 * @Description:
 */
@Component
public class DateConverterConfig implements Converter<String, Date> {
    private static final List<String> formarts = new ArrayList<>(4);

    static {
        formarts.add("yyyy-MM");
        formarts.add("yyyy-MM-dd");
        formarts.add("yyyy-MM-dd hh:mm");
        formarts.add("yyyy-MM-dd hh:mm:ss");
    }

    @Override
    public Date convert(String source) {
        String value = source.trim();
        if ("".equals(value)) {
            return null;
        }
        if (source.matches("^\\d{4}-\\d{1,2}$")) {
            return parseDate(source, formarts.get(0));
        } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
            return parseDate(source, formarts.get(1));
        } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
            return parseDate(source, formarts.get(2));
        } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
            return parseDate(source, formarts.get(3));
        } else {
            throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
        }
    }

    /**
     * 格式化日期
     *
     * @param dateStr String 字符型日期
     * @param format  String 格式
     * @return Date 日期
     */
    public Date parseDate(String dateStr, String format) {
        Date date = null;
        try {
            DateFormat dateFormat = new SimpleDateFormat(format);
            date = dateFormat.parse(dateStr);
        } catch (Exception e) {
        }
        return date;
    }
}

8.前端页面处理

对前端页面的处理也没有多少说的,看源码就理解了。

增删改查处理出来即可,前端给到我们页面后,我们只需要加上对应的回显操作即可。这里使用到thymeleaf。先得导入static静态资源css和js包。

说一下这个目录的问题,静态的数据都放在resource包下的static包下,页面放在templates包下(controller会自动跳转),其它资源都得规范化

list.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>userList</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
    <script type="text/javascript" th:src="@{/js/My97DatePicker/WdatePicker.js}"></script>
    <script type="text/javascript" src="/js/My97DatePicker/lang/zh-cn.js"></script>
    <script type="text/javascript" th:src="@{/js/jquery/jquery-1.10.2.min.js}"></script>
</head>
<body class="container">
<br/>
<h1>账单列表</h1>
<br/><br/>

<div class="with:80%">
	<!-- TODO 回显查询数据 ,已解决-->
    <form class="form-inline" id="qf" method="post">
        <div class="form-group">
            <label for="typeId" class="control-label">类型</label>
            <select name="typeId" id="typeId" class="form-control">
                <option value="">全部</option>
                <!--显示下拉框-->
                <option th:each="t:${types}" th:value="${t.id}" th:text="${t.name}" th:selected="${t.id} +'' == ${#strings.trim(param.typeId)}"></option>
            </select>
        </div>
        <div class="form-group">
            <label for="dateBegin" class="control-label" >开始时间</label>
            <!--添加th:value="${param.dateBegin}" 支持数据回显-->
            <input type="text" class="form-control" name="dateBegin" id="dateBegin" th:value="${param.dateBegin}" placeholder="开始时间" onclick="WdatePicker()"/>
        </div>
        <div class="form-group">
            <label for="dateEnd" class="control-label">结束时间</label>
            <input type="text" class="form-control" name="dateEnd"  id="dateEnd" th:value="${param.dateEnd}"  placeholder="结束时间" onclick="WdatePicker()"/>
        </div>
        <div class="form-group">
            <input type="submit" value="查询" class="btn btn-info" />
            &nbsp; &nbsp;
            <input type="reset" value="重置" class="btn btn-info" />
            &nbsp; &nbsp;
            <a href="/bill/toAdd" th:href="@{/bill/toAdd}" class="btn btn-info">添加</a>
        </div>
    </form>
</div>
<br/>


<div class="with:80%">
    <table class="table table-striped table-bordered">
        <thead>
        <tr>
            <th>#</th>
            <th>标题</th>
            <th>时间</th>
            <th>金额</th>
            <th>类别</th>
            <th>说明</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody >
		<!-- TODO 回显查询结果 , 已解决-->
        <!--使用thymeleaf显示数据-->
        <tr th:each="b,status: ${bills}" th:object="${b}">
            <td th:text="${b.id}">id</td>
            <td th:text="*{title}">title</td>
            <td th:text="${#dates.format(b.billTime,'yyyy-MM-dd')}">time</td>
            <td th:text="${b.price}">price</td>
            <td th:text="${b.typeName}">typeName</td>
            <td th:text="${b.explain}">explain</td>
            <td>
                <a th:href="|/bill/delete/*{id}|">删除</a>
                <a th:href="|/bill/toUpdate/*{id}|">修改</a>
                <a th:href="|/bill/list-page|">进入分页页面</a>
            </td>
        </tr>
        </tbody>
    </table>
</div>

</body>
</html>

之后的页面就是参看一下源码。


9.分页操作

分为前端和后端分页处理,前后端同时实现分页。后端使用pagehelper分页插件,前端使用thymeleaf实现。

9.1 后端分页实现

引入pageHelper分页插件starter,注意:必须引入pagehelper的启动器,不能直接引入pagehelper。分页插件提供了目前分页会用到的许多数据,直接获取数据即可实现分页功能。

<!-- 分页插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.3</version>
</dependency>

之后可以编写一个测试类对分页效果进行测试,在测试类中,得到这些常用的数据进行展示。直接测试Service即可:

@Test
void listPage() {
    Bill bill = new Bill();
    PageInfo<Bill> page = PageHelper.startPage(2,10).doSelectPageInfo(() -> {
        service.list(bill);
    });

    page.getList().forEach(b -> {
        System.out.println(b.getId()+":"+b.getTitle());
    });
    System.out.println("总行数=" + page.getTotal());
    System.out.println("当前页=" + page.getPageNum());
    System.out.println("每页行数=" + page.getPageSize());
    System.out.println("总页数=" + page.getPages());
    System.out.println("起始行数=" + page.getStartRow());
    System.out.println("是第一页=" + page.isIsFirstPage());
    System.out.println("是最后页=" + page.isIsLastPage());
    System.out.println("还有下一页=" + page.isHasNextPage());
    System.out.println("还有上一页=" + page.isHasPreviousPage());
    System.out.println("页码列表" + Arrays.toString(page.getNavigatepageNums()));
}

下面就直接开始分页的应用了。在service中加入分页方法listPage

/**
     * 分页操作
     * @param b
     * @param pageNum
     * @param pageSize
     * @return
     */
public PageInfo<Bill> listPage(Bill b,int pageNum,int pageSize){
    return PageHelper.startPage(pageNum,pageSize).doSelectPageInfo(() -> {
        billMapper.select(b);
    });
}

编写controller类中分页操作

前后端参数名必须一一对应,会自动接收数据

/**
     * 分页查询
     * @param bill 接收页面请求的参数
     * @param model 把查询的结果放到model中
     * @return
     */
@RequestMapping("/list-page")
public String listPage(
    /**
             * RequestParam:匹配请求中的参数,传递的名称与之对应即可
             * 接收前端发过来的name属性,且字符必须得一一对应
             */
    @RequestParam(defaultValue = "1") int pageNum,
    @RequestParam(defaultValue = "10") int pageSize,
    Bill bill,
    Model model){
    List<BillType> types = typeService.list();
    model.addAttribute("types",types);
    PageInfo page = billService.listPage(bill,pageNum,pageSize);
    model.addAttribute("page",page);

    return "bill/list-page";
}

下面就是在前端页面处理这个分页问题了。


9.2 前端分页

list-page.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>userList</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
    <script type="text/javascript" th:src="@{/js/My97DatePicker/WdatePicker.js}"></script>
    <script type="text/javascript" src="/js/My97DatePicker/lang/zh-cn.js"></script>
    <script type="text/javascript" th:src="@{/js/jquery/jquery-1.10.2.min.js}"></script>
</head>
<body class="container">
<br/>
<h1>账单列表</h1>
<br/><br/>
<div class="with:80%">
    <form class="form-inline" id="qf" th:action="@{/bill/list-page}"  method="post">
        <!--隐藏这两个数据-->
        <input name="pageNum" id="pageNum" type="hidden" th:value="${page.pageNum}">
        <input name="pageSize" id="pageSize" type="hidden" th:value="${page.pageSize}">

		<!--分页相关参数-->
        <div class="form-group">
            <label for="typeId" class="control-label">类型</label>
            <select name="typeId" id="typeId" class="form-control">
                <option value="">全部</option>
                <option th:each="t:${types}" th:value="${t.id}" th:text="${t.name}" th:selected="${t.id} +'' == ${#strings.trim(param.typeId)}"></option>
            </select>
        </div>

        <div class="form-group">
            <label for="dateBegin" class="control-label" >开始时间</label>
            <input type="text" class="form-control" name="dateBegin" id="dateBegin" th:value="${param.dateBegin}"  placeholder="开始时间" onclick="WdatePicker()"/>
        </div>
        <div class="form-group">
            <label for="dateEnd" class="control-label">结束时间</label>
            <input type="text" class="form-control" name="dateEnd"  id="dateEnd"  th:value="${param.dateEnd}" placeholder="结束时间" onclick="WdatePicker()"/>
        </div>
        <div class="form-group">
            <input type="submit" value="查询" class="btn btn-info" />
            &nbsp; &nbsp;
            <input type="reset" value="重置" class="btn btn-info" />
            &nbsp; &nbsp;
            <a href="/bill/toAdd" th:href="@{/bill/toAdd}" class="btn btn-info">添加</a>
        </div>
    </form>
</div>
<br/>

<div class="with:80%">
    <table class="table table-striped table-bordered">
        <thead>
        <tr>
            <th>#</th>
            <th>标题</th>
            <th>时间</th>
            <th>金额</th>
            <th>类别</th>
            <th>说明</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
		<!-- TODO 页面循环-->
        <!--使用thymeleaf显示数据-->
        <tr th:each="b, bstatus : ${page.list}" th:style="${bstatus.odd} ? 'background-color: #FFCE44'">
            <th scope="row" th:text="${b.id}">id</th>
            <td th:text="${b.title}">name</td>
            <td th:text="${b.billTime} ? ${#dates.format(b.billTime, 'yyyy-MM-dd')}">time</td>
            <td th:text="${b.price}">price</td>
            <td th:text="${b.typeName}">typeName</td>
            <td th:text="${b.explain}">explain</td>
            <td>
                <a th:href="|/bill/toUpdate/${b.id}|">修改</a>
                <a th:href="|/bill/delete/*{b.id}|">删除</a>
                <a th:href="|/bill/list|">进入未分页页面</a>
            </td>
        </tr>
        </tbody>
    </table>
</div>
<!-- TODO 分页工具类-->
<ul class="pagination">
    <li><button class="btn btn-default" id="first">第一页</button></li>
    <li><button class="btn btn-default" id="prev">上一页</button></li>
    <li th:each="p:${page.navigatepageNums}"> <!--navigatepageNums:导航页-->
        <!--显示页码, name很重要-->
        <button class="btn btn-default" id="bottomPageNum" name="pn" th:text="${p}" th:disabled="(${p} == ${page.pageNum})"></button>
    </li>
    <li><button class="btn btn-default" id="next">下一页</button></li>
    <li><button class="btn btn-default" id="last">最后页</button></li>
</ul>

<!-- TODO 分页的js代码-->
<script>
    $(function (){
        //通过内联js得到分页相关数据
        var pageNum = [[${page.pageNum}]]; //当前页
        var pageCount = [[${page.pages}]]; //总页数
        var hasNextPage = [[${page.hasNextPage}]]; // true 有下一页
        var hasPreviousPage = [[${page.hasPreviousPage}]]; // true 有上一页

        //按钮事件
        // 下一页
        $("#next").click(function (){
            $("#pageNum").val(pageNum + 1);
            $("#qf").submit();
        });
        // 上一页
        $("#prev").click(function (){
            $("#pageNum").val(pageNum - 1);
            $("#qf").submit();
        });
        // 第一页
        $("#first").click(function (){
            $("#pageNum").val(1);
            $("#qf").submit();
        });
        // 最后一页
        $("#last").click(function (){
            $("#pageNum").val(pageCount);
            $("#qf").submit();
        });

        //点击页码跳转
        $("button[name='pn']").click(function (){
            $("#pageNum").val($(this).html());
            $("#qf").submit();
        });

        //控制按钮状态
        if(!hasNextPage){
            $("#last").prop("disabled",true);
            $("#next").prop("disabled",true);
        }
        if(!hasPreviousPage){
            $("#first").prop("disabled",true);
            $("#prev").prop("disabled",true);
        }
    })
</script>
</body>
</html>

之后修改更新、添加数据后跳转到此请求即可进行显示。


10.结语

springboot,mybatis和tk mybatis(持久化),thymeleaf(前端页面展示)知识点非常的多,但是在刚刚开始学习的时候,无从下手最让人头疼,如果还有实现分页需求,作为一个后端方向的学生,实在是不好学习这些知识点啊。这里就用一个极其简单的项目,入门了解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值