萌新分页操作:怎么做社区网站的首页帖子分页展示?

1.开发社区首页,注册 ccc 阅读详情

要进行首页帖子展示,就必须学会分页,而在创建分页之前,我们得先认识到,为什么要进行分页?一个大型网站的数据库将容纳大量的数据,而我们进行展示某部分数据时,为了保证浏览速度,不可能一次性地将所有数据进行传输,更何况,不是全部传输过去的数据第一时间就能有效利用,所以,只要每次将需要的数据传输过去就好,即使是后续需要的数据,那也是后面时间点需要完成的工作,为了做到这一点,必须对一个数据整体进行划分,合理地传输并展示给用户,其中,划分出来的每一部分可被称为一页数据,完成划分工作的就是分页操作。而分页操作在 spingboot 及 mybatis 的环境下,可被分为以下几种分页情况:

分类优点缺点
数组分页(逻辑分页):查询出所有数据,再利用 List 数组截取需要的数据效率高内存占用大
SQL 分页(物理分页):只从数据库中查询当前页的数据占用内存小每次都得书写 limit 语句,较为冗余,无法统一管理
拦截器分页:自定义拦截器实现了拦截所有以 ByPage 结尾的查询语句,并且利用获取到的分页相关参数统一在 SQL 语句后面加上limit分页的相关语句,底层仍是物理分页。方便管理、内存占用小数据量较小的情况可以考虑更高效的分页类型
RowBounds 分页:与数组分页类似,只是无需编写分页逻辑效率高,适合数据量较小的情况,有效地防止内存溢出内存占用大

问题阐述

了解为什么要进行分页以及分页的类型之后,现在如果分发给你一个任务:需要完成社区类型网站的首页以及个人帖子的展示,其中,帖子排序具有以下因素:

  • 帖子类型分为普通(0)和置顶(1)
  • 帖子状态分为正常(0)、精华(1)和拉黑(2)
  • 帖子创建时间:yyyy-MM-dd HH:mm:ss

各个因素顺序越靠前,优先级越高,请将帖子按合理顺序分页且排序,并且在明确当前第几页的情况下,保证当前页码前后两页都可展示并切换(首页、末页为特殊情况),实现首页、上一页、下一页、末页跳转功能即可。

注:为了保证过多无关代码,采用创建时间作为排序因素之一,而不是评价帖子优劣的帖子分数。

相关可运行的代码文件及SQL语句可看如何创建一个社区的首页帖子分页 - yumuing博客:做技术的朝拜者

后端设计

帖子的建表语句如下:

DROP TABLE IF EXISTS `discuss_post`;
SET character_set_client = utf8mb4 ;
CREATE TABLE `discuss_post` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(45) DEFAULT NULL,
  `title` varchar(100) DEFAULT NULL,
  `content` text,
  `type` int(11) DEFAULT NULL COMMENT '0-普通; 1-置顶;',
  `status` int(11) DEFAULT NULL COMMENT '0-正常; 1-精华; 2-拉黑;',
  `create_time` timestamp NULL DEFAULT NULL,
  `comment_count` int(11) DEFAULT NULL,
  `score` double DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

用户的建表语句如下:

DROP TABLE IF EXISTS `user`;
SET character_set_client = utf8mb4 ;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `salt` varchar(50) DEFAULT NULL,
  `email` varchar(100) DEFAULT NULL,
  `type` int(11) DEFAULT NULL COMMENT '0-普通用户; 1-超级管理员; 2-版主;',
  `status` int(11) DEFAULT NULL COMMENT '0-未激活; 1-已激活;',
  `activation_code` varchar(100) DEFAULT NULL,
  `header_url` varchar(200) DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_username` (`username`(20)),
  KEY `index_email` (`email`(20))
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;

相关帖子数据及用户数据可从如何创建一个社区的首页帖子分页 - yumuing博客:做技术的朝拜者此处获取。

由于帖子数量较为庞大,所以,使用属于物理分页的 SQL 分页和拦截器分页,先从 SQL 分页说起解决方案。我们都知道,在 MVC 架构下,常见思路为使用 Mapper 进行数据库操作,将数据库数据映射到实体类,并由 Server 进行逻辑操作,所得到的结果交由 Controller 进行转发到前端。所以,我们先按照常规思路,创建对应实体类。

用户实体类:User.java

//package 和 import 语句

/**
 * 
 * @TableName user
 */
@TableName(value ="user")
@Data
public class User implements Serializable {
    /**
     * 
     */
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private String salt;//加盐
    private String email;
    /**
     * 0-普通用户; 1-超级管理员; 2-版主;
     */
    private Integer type;

    /**
     * 0-未激活; 1-已激活;
     */
    private Integer status;

    /**
     * 激活码
     */
    private String activationCode;

    /**
     * 头像url
     */
    private String headerUrl;
    private Date createTime;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    //toString()、hashCodeequals()、 get() 和 set()方法,完整可看 https://yumuing.top/archives/23.html
    
   

帖子实体类:DiscussPost.java

//package 和 import 语句

/**
 * 
 * @TableName discuss_post
 */
@TableName(value ="discuss_post")
@Data
public class DiscussPost implements Serializable {
    /**
     * 帖子 ID
     */
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 用户 ID
     */
    private String userId;

    /**
     * 帖子标题
     */
    private String title;

    /**
     * 帖子内容
     */
    private String content;

    /**
     * 帖子类型 0-普通; 1-置顶;
     */
    private Integer type;

    /**
     * 帖子状态 0-正常; 1-精华; 2-拉黑;
     */
    private Integer status;

    /**
     * 帖子创建日期
     */
    private Date createTime;

    /**
     * 评论数量
     */
    private Integer commentCount;

    /**
     * 帖子分数
     */
    private Double score;
    
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    //toString()、hashCodeequals()、 get() 和 set()方法,完整可看 https://yumuing.top/archives/23.html
    

分页(信息)实体类:Page.java

该实体类只是用来承担 Server 查询出来的各种参数以及存有计算总结数字的方法的映射实体,方便 Controller 进行调用。

  • getOffset:获取当前页起始行 = 当前页码 * 每页显示上限-每页显示上限 = (current-1) * limit
  • getTotal:获取总页数 = 总行数 / 每页显示上限 = rows / limit (若不能整除,则加一)
  • getFrom:获取起始页码,即当前页码前两页,若为首页及第二页,则为1
  • getTo:获取截止页码,即当前页码后两页,若为尾页及倒数第二页,则为总页数 total
package top.yumuing.community.entity;

// 封装分页信息
public class Page {
	// 当前页码
    private int current = 1;
	// 每页显示上限
    private int limit =10;
	// 数据总数
    private int rows;
	// 查询对应分页路径,复用分页链接
    private String path;

//toString()、hashCodeequals()、 get() 和 set()方法,完整可看 https://yumuing.top/archives/23.html

	// 获取当前页起始行
    public int getOffset(){
        // current * limit - limit
        return (current-1) * limit;
    }

	// 获取总页数
    public int getTotal(){
        // rows / limit
        if (rows % limit == 0){
            return rows / limit;
        }else {
            return rows / limit + 1;
        }
    }
	// 获取起始页码
    public int getFrom(){
        int from = current - 2;
        return from < 1 ? 1 : from;
    }
	// 获取截止页码
    public int getTo(){
        int to = current +2;
        int total = getTotal();
        return to > total ? total : to;
    }
}

三个实体类创建结束,还有两个 mapper :DiscussPostMapper.java、UserMapper.java 以及对应的 xml 代码,如下:

DiscussPostMapper.java

在进行分页查询帖子时,为了与用户查询个人帖子功能复用,确定以下参数:

  • userId:首页默认为 0,而需要查询用户的帖子就填入对应 userId
  • offset:每一页起始行行号
  • limit:每页最多显示多少条数据

注:@Param(“参数别名”):书写参数别名,需要动态拼接 SQL 语句时,传入的参数有且只有一个,参数之前必须增加 @Param(“参数别名”),否则会报错。

//package 和 import 语句
/**
* @author yumuuing
* @description 针对表【discuss_post】的数据库操作Mapper
* @Entity top.yumuing.community.entity.DiscussPost
*/
@Mapper
public interface DiscussPostMapper extends BaseMapper<DiscussPost> {
    List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);

    int selectDiscussPostRows(@Param("userId") int userId);

}

UserMapper.java

//package 和 import 语句
/**
* @author yumuuing
* @description 针对表【user】的数据库操作Mapper
* @Entity top.yumuing.community.entity.User
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
    User selectOneById(@Param("id") int id);
}

UserMapper.xml

selectOneById:通过 ID 查询对应用户

<?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="top.yumuing.community.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="top.yumuing.community.entity.User">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="username" column="username" jdbcType="VARCHAR"/>
            <result property="password" column="password" jdbcType="VARCHAR"/>
            <result property="salt" column="salt" jdbcType="VARCHAR"/>
            <result property="email" column="email" jdbcType="VARCHAR"/>
            <result property="type" column="type" jdbcType="INTEGER"/>
            <result property="status" column="status" jdbcType="INTEGER"/>
            <result property="activationCode" column="activation_code" jdbcType="VARCHAR"/>
            <result property="headerUrl" column="header_url" jdbcType="VARCHAR"/>
            <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,username,password,
        salt,email,type,
        status,activation_code,header_url,
        create_time
    </sql>
    
    <select id="selectOneById" resultType="User">
        select
        <include refid="Base_Column_List"/>
        from user
        where id = #{id}
    </select>
    
</mapper>

DiscussPostMapper.xml

为了实现复用,使用以下代码,进行判断是否加上 and user_id = #{userId}

 <if test="userId!=0">
	and user_id = #{userId}
 </if>

由于帖子状态为 2 时,为拉黑状态,不可展示给对应用户,所以添加上 order by type desc, create_time desc 语句进行判断,并且帖子类型为普通(0)和置顶(1),帖子展示为最新帖子,故两种属性采用倒序状态。selectDiscussPostRows 查询语句为获取该页帖子行数。

<?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="top.yumuing.community.mapper.DiscussPostMapper">

    <resultMap id="BaseResultMap" type="top.yumuing.community.entity.DiscussPost">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="userId" column="user_id" jdbcType="VARCHAR"/>
            <result property="title" column="title" jdbcType="VARCHAR"/>
            <result property="content" column="content" jdbcType="VARCHAR"/>
            <result property="type" column="type" jdbcType="INTEGER"/>
            <result property="status" column="status" jdbcType="INTEGER"/>
            <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
            <result property="commentCount" column="comment_count" jdbcType="INTEGER"/>
            <result property="score" column="score" jdbcType="DOUBLE"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,user_id,title,
        content,type,status,
        create_time,comment_count,score
    </sql>

    <select id="selectDiscussPosts" resultType="DiscussPost">
        select <include refid="Base_Column_List"></include>
        from discuss_post
        where status != 2
        <if test="userId!=0">
            and user_id = #{userId}
        </if>
        order by type  desc, create_time desc
        limit #{offset},#{limit}
    </select>
    
    <select id="selectDiscussPostRows" resultType="int">
        select count(id)
        from discuss_post
        where status != 2
        <if test="userId!=0">
            and user_id = #{userId}
        </if>
    </select>

</mapper>

在 Server 下采用 impl 实现对应接口的方式,这样,以后一旦需要修改数据库时,只需要实现另一个数据库的方法,在修改注入对象即可,无需更改 Controller 实际业务代码:对应接口代码如下:

DiscussPostService.java

//package 和 import 语句

/**
* @author yumuuing
* @description 针对表【discuss_post】的数据库操作Service
*/
@Service
public interface DiscussPostService extends IService<DiscussPost> {
    
    public List<DiscussPost> findDiscussPosts(int userId,int offset,int limit);

    public int findDiscussPostsRows(int userId);
    
}

UserService.java

//package 和 import 语句

/**
* @author yumuuing
* @description 针对表【user】的数据库操作Service
*/
@Service
public interface UserService extends IService<User> {

    public User findUserById(int id);
    
}

mysql 的 impl 实现方法:

DiscussPostServiceImpl.java

  • findDiscussPosts:该分页下帖子的所有信息
  • findDiscussPostsRows:该分页的行数
//package 和 import 语句

/**
* @author yumuuing
* @description 针对表【discuss_post】的数据库操作Service实现
*/
@Service
public class DiscussPostServiceImpl extends ServiceImpl<DiscussPostMapper, DiscussPost>
    implements DiscussPostService{
    @Resource
    public DiscussPostMapper discussPostMapper;

    @Override
    public List<DiscussPost> findDiscussPosts(int userId, int offset, int limit) {
        return discussPostMapper.selectDiscussPosts(userId,offset,limit);
    }

    @Override
    public int findDiscussPostsRows(int userId) {
        return discussPostMapper.selectDiscussPostRows(userId);
    }
}

UserServiceImpl.java

  • findUserById:查询对应用户
//package 和 import 语句

/**
* @author yumuuing
* @description 针对表【user】的数据库操作Service实现
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Resource
    private UserMapper userMapper;

    @Override
    public User findUserById(int id) {
        return userMapper.selectOneById(id);
    }
}

现在我们已经实现了从 DAO 层到 Server 层的分页操作,其所需参数的查询,现在需要到 Controller 层进行转发到前端,进行调用和 UI 展示了。前端采用 thymeleaf 进行开发,不是严格的前后端开发形式,而是混合开发。

首先,我们得先利用 Page 实体类获取对应的分页信息,利用分页信息将数据库内的对应分页的帖子映射实体放入 discussPostList 中,利用 userServiceImpl.findUserById(Integer.parseInt(post.getUserId())) 获取与匹配在discussPostList 中匹配的 user,注意,用户拥有多个帖子,最好以帖子匹配用户,不然会存在重复信息,以 discussPost 为匹配要求,user 为匹配对象。放入同一个 map 对象中,再将所有的 map 对象放入 List 中,以便传输给前端。利用 model.addAttribute(“discussPosts”,discussPosts) 方法进行传输。

//package 和 import 语句

@Controller
public class HomeController {
    
    @Autowired
    @Qualifier("discussPostServiceImpl")
    private DiscussPostService discussPostServiceImpl;

    @Autowired
    @Qualifier("userServiceImpl")
    private UserService userServiceImpl;

    @RequestMapping(path = "/index",method = RequestMethod.GET)
    public String getIndecPage(Model model ,Page page){

        //获取分页
        page.setRows(discussPostServiceImpl.findDiscussPostsRows(0));
        page.setPath("/index");

        // 获取帖子
        List<DiscussPost> discussPostList = discussPostServiceImpl.findDiscussPosts(0,page.getOffset(),page.getLimit());
        List<Map<String,Object>> discussPosts = new ArrayList<>();
        if (discussPostList != null){
            for(DiscussPost post: discussPostList){
                Map<String, Object> map = new HashMap<>();
                map.put("post",post);
                User user = userServiceImpl.findUserById(Integer.parseInt(post.getUserId()));
                map.put("user",user);
                discussPosts.add(map);
            }
        }
        model.addAttribute("discussPosts",discussPosts);
        return "/index";
    }
}

前端调用

注意:xmlns:th=“http://www.thymeleaf.org”:导入 thymeleaf 模板标志

主要看帖子列表以及分页部分

帖子列表

在 thymeleaf 中,# 代表调用方法,$ 代表变量。

  • th:each="map:${discussPosts}:收到后端的传输数据,为 map 集合 List,利用此代码一一遍历,每次遍历出的元素命名为 map

  • ${map.user.headerUrl}、${map.post.title}、${map.user.username};都是获取 map 里对应的数据

  • th:utext=:如果不是使用 utext 可能会出现转义字符不显示为正确符号,而是普通字符

  • ${map.post.type==1}、${map.post.status==1}:前一个判断是否为置顶帖子,后一个判断是否为精华帖子,是则存在该元素,否则取消

  • ${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}:调用 dates.format() 方法格式化从 map 取出的帖子创建时间数据

分页

  • th:if="${page.rows>0}:如果帖子行数为 0 ,分页部分不存在

  • @{${page.path}(current=1)}:表示在域名后面拼接 page.path 变量数据,再加上 ?current=1,括号代表拼接 Get 参数。

  • class="|page-item ${page.current==1?'disabled':''}|":两竖杠代表静态数据,即不会改变数据,而 $ 后代表变量,如果 page.current==1 则无法点击,disabled 在 class 中代表无法点击。其他的类似

  • th:each="i:${#numbers.sequence(page.from,page.to)}":#numbers.sequence() 输出从某个数字到某个数字的顺序数字序列,获得的当前页数字赋给 i

  • th:href="@{${page.path}(current=${i})}" th:text="${i}:点击后进行正确的页数跳转,并显示对应数字

index.html 代码如下:

<!doctype html>  
<html lang="en" 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">
    <link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
    <link rel="stylesheet" th:href="@{/css/global.css}" />
    <title>yumuing 社区-首页</title>
</head>
<body>
<div class="nk-container">
    <!-- 头部 -->
    <header class="bg-dark sticky-top">
        <div class="container">
            <!-- 导航 -->
            <nav class="navbar navbar-expand-lg navbar-dark">
                <!-- 功能 -->
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul class="navbar-nav mr-auto">
                        <li class="nav-item ml-3 btn-group-vertical">
                            <a class="nav-link" href="index.html">首页</a>
                        </li>
                        <li class="nav-item ml-3 btn-group-vertical">
                            <a class="nav-link position-relative" href="#">消息<span class="badge badge-danger">12</span></a>
                        </li>
                        <li class="nav-item ml-3 btn-group-vertical">
                            <a class="nav-link" href="#">注册</a>
                        </li>
                        <li class="nav-item ml-3 btn-group-vertical">
                            <a class="nav-link" href="#">登录</a>
                        </li>
                        <li class="nav-item ml-3 btn-group-vertical dropdown">
                            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                <img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/>
                            </a>
                            <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                                <a class="dropdown-item text-center" href="#">个人主页</a>
                                <a class="dropdown-item text-center" href="#">账号设置</a>
                                <a class="dropdown-item text-center" href="#">退出登录</a>
                                <div class="dropdown-divider"></div>
                                <span class="dropdown-item text-center text-secondary">nowcoder</span>
                            </div>
                        </li>
                    </ul>
                    <!-- 搜索 -->
                    <form class="form-inline my-2 my-lg-0" action="#">
                        <input class="form-control mr-sm-2" type="search" aria-label="Search" />
                        <button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button>
                    </form>
                </div>
            </nav>
        </div>
    </header>

    <!-- 内容 -->
    <div class="main">
        <div class="container">
            <div class="position-relative">
                <!-- 筛选条件 -->
                <ul class="nav nav-tabs mb-3">
                    <li class="nav-item">
                        <a class="nav-link active" href="#">最新</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">最热</a>
                    </li>
                </ul>
                <button type="button" class="btn btn-primary btn-sm position-absolute rt-0" data-toggle="modal" data-target="#publishModal">我要发布</button>
            </div>
            <!-- 弹出框 -->
            <div class="modal fade" id="publishModal" tabindex="-1" role="dialog" aria-labelledby="publishModalLabel" aria-hidden="true">
                <div class="modal-dialog modal-lg" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title" id="publishModalLabel">新帖发布</h5>
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            <form>
                                <div class="form-group">
                                    <label for="recipient-name" class="col-form-label">标题:</label>
                                    <input type="text" class="form-control" id="recipient-name">
                                </div>
                                <div class="form-group">
                                    <label for="message-text" class="col-form-label">正文:</label>
                                    <textarea class="form-control" id="message-text" rows="15"></textarea>
                                </div>
                            </form>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
                            <button type="button" class="btn btn-primary" id="publishBtn">发布</button>
                        </div>
                    </div>
                </div>
            </div>
            <!-- 提示框 -->
            <div class="modal fade" id="hintModal" tabindex="-1" role="dialog" aria-labelledby="hintModalLabel" aria-hidden="true">
                <div class="modal-dialog modal-lg" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title" id="hintModalLabel">提示</h5>
                        </div>
                        <div class="modal-body" id="hintBody">
                            发布完毕!
                        </div>
                    </div>
                </div>
            </div>

            <!-- 帖子列表 -->
            <ul class="list-unstyled">
                <li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
                    <a href="#">
                        <img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;">
                    </a>
                    <div class="media-body">
                        <h6 class="mt-0 mb-3">
                            <a href="#" th:utext="${map.post.title}"></a>
                            <span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}">置顶</span>
                            <span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span>
                        </h6>
                        <div class="text-muted font-size-12">
                            <u class="mr-3" th:utext="${map.user.username}">yumuing</u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}"></b>
                            <ul class="d-inline float-right">
                                <li class="d-inline ml-2">赞 11</li>
                                <li class="d-inline ml-2">|</li>
                                <li class="d-inline ml-2">回帖 7</li>
                            </ul>
                        </div>
                    </div>
                </li>
            </ul>
            <!-- 分页 -->
            <nav class="mt-5" th:if="${page.rows>0}">
                <ul class="pagination justify-content-center">
                    <li class="page-item">
                        <a class="page-link" th:href="@{${page.path}(current=1)}">首页</a>
                    </li>
                    <li th:class="|page-item ${page.current==1?'disabled':''}|">
                        <a class="page-link" th:href="@{${page.path}(current=${page.current-1})}">上一页</a></li>
                    <li th:class="|page-item ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}">
                        <a class="page-link" th:href="@{${page.path}(current=${i})}" th:text="${i}">1</a>
                    </li>
                    <li th:class="|page-item ${page.current==page.total?'disabled':''}|">
                        <a class="page-link" th:href="@{${page.path}(current=${page.current+1})}">下一页</a>
                    </li>
                    <li class="page-item">
                        <a class="page-link" th:href="@{${page.path}(current=${page.total})}">末页</a>
                    </li>
                </ul>
            </nav>
        </div>
    </div>

    <!-- 尾部 -->
    <footer class="bg-dark">
        <div class="container">
            <div class="row">
                <!-- 二维码 -->
                <div class="col-4 qrcode">
                    <img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" />
                </div>
                <!-- 公司信息 -->
                <div class="col-8 detail-info">
                    <div class="row">
                        <div class="col">
                            <ul class="nav">
                                <li class="nav-item">
                                    <a class="nav-link text-light" href="#">关于我们</a>
                                </li>
                                <li class="nav-item">
                                    <a class="nav-link text-light" href="#">加入我们</a>
                                </li>
                                <li class="nav-item">
                                    <a class="nav-link text-light" href="#">意见反馈</a>
                                </li>
                                <li class="nav-item">
                                    <a class="nav-link text-light" href="#">企业服务</a>
                                </li>
                                <li class="nav-item">
                                    <a class="nav-link text-light" href="#">联系我们</a>
                                </li>
                                <li class="nav-item">
                                    <a class="nav-link text-light" href="#">免责声明</a>
                                </li>
                                <li class="nav-item">
                                    <a class="nav-link text-light" href="#">友情链接</a>
                                </li>
                            </ul>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col">
                            <ul class="nav btn-group-vertical company-info">
                                <li class="nav-item text-white-50">
                                    公司地址:#
                                </li>
                                <li class="nav-item text-white-50">
                                    联系方式:#
                                </li>
                                <li class="nav-item text-white-50">
                                    #
                                </li>
                                <li class="nav-item text-white-50">
                                    ######## &nbsp;&nbsp;&nbsp;&nbsp;
                                    <img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />
                                    ######## 号
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </footer>
</div>

<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script>
<script th:src="@{/js/global.js}"></script>
<script th:src="@{js/index.js}"></script>
</body>
</html>

求点赞转发

100小时搭建《明日方舟》社区网站:从数据模型到部署上线完整路线 社区网站是面向特定兴趣群体的内容聚合与交互平台,其开发涉及前端展示、后端服务、数据库设计、内容审核与部署运维等多个环节。从一个真实百小时项目出发,可以拆解出游戏社区网站从0到1的完整工程路径:先规划功能模块与数据模型,再准备环境与部署方案;通过批量任务初始化干员数据,设计用户发帖与审核机制;最后关注性能观测与常见故障排查。无论你是想为喜爱的游戏搭建玩家社区,还是希望掌握全栈项目实践,这条路线都提供了可落地的技术栈选择与工程决策参考。 阅读详情

相关推荐

Python 爬虫实战:Selenium 爬取百度贴吧帖子(分页加载与多线程翻页优化)

百度贴吧是一个拥有海量用户和丰富话题的在线社区,其中包含了各个领域的讨论和信息。对于研究人员、数据分析师以及内容创作者来说,能够高效地获取贴吧帖子数据具有重要的价值。然而,由于百度贴吧的页面采用了分页加载的方式,并且内容是通过 JavaScript 动态渲染的,传统的爬虫方法难以直接获取完整的帖子数据。为了解决这一问题,本文将介绍如何利用 Selenium 实现对百度贴吧帖子的爬取,并通过多线程技术优化翻页过程,提高爬取效率。

yansideyucsdn的博客 1002

完美实现Discuz论坛的分页形式的分页类

完美实现Discuz论坛的分页形式的分页类 思路:1.非第和最后一页,有左右箭头 &lt; 和 &gt; ,就是上一页和下一页,这里使用箭头;2.判断了当前页class为current ;3.总页数小于等于10页时,直接输出 样式为 1.2.3....10 ;4.总页数大于10页的时候,数字分页有两种情况:a.第4页开始,当前页数字往后偏移7,往前偏移2. 样式为 1... &lt; 3.4...

编程浪子的博客 812

抓取Reddit的帖子和评论 - 使用Reddit API抓取帖子和用户评论

Reddit 是全球最大的社交新闻聚合平台之一,用户可以在上面发布各种帖子,分享新闻、观点、图片等内容。Reddit还提供了丰富的社区互动功能,用户可以评论、投票、回复其他用户的帖子。抓取Reddit的帖子和评论是数据分析、自然语言处理、情感分析等任务中的常见需求。利用Reddit API,我们可以方便地获取帖子、评论、投票、用户信息等数据。在这篇博客中,我们将使用 Python 和 Reddit 提供的 API 来抓取帖子和评论数据,并进行简单的数据处理。

2201_76125261的博客 731

php 经典分页实现代码

<br />本款分页类支持显示为   11/20 页次 共400条记录 20条/页  上一页 11 12 13 14 1这种形式,如page标签,用来控制url页。比如说xxx.php教程?pb_page=2中的pb_page,同时支持上页下页尾页首页,同时可自动获取url参数分页。<br /> <br />*/<br />class minupage <br />{<br />/**<br />   * config ,public<br />   */<br />var $page_name="p";/

mailangel123的专栏 529

使用Python爬虫技术抓取虎扑体育论坛帖子数据:分析动态加载的帖子并实现数据提取

发送请求:通过requests库或urllib库向目标网站发送HTTP请求。解析数据:使用或lxml等库解析HTML或JSON响应数据。数据存储:将抓取的数据存储到CSV文件或数据库中。我们可以使用这些基本步骤来抓取网页数据,并通过不同的技术手段解决反爬机制等挑战。本文详细介绍了如何使用Python爬虫技术,爬取豆瓣话题讨论的评论内容。我们从分析豆瓣页面的HTML结构开始,使用requests和等工具抓取页面内容并提取评论数据。然后,我们处理了分页问题,并探讨了如何通过抓取API接口获取评论数据。

2201_76125261的博客 1507

WinCC动态选择报警记录案例

本例中需要确认查询的时间段以及查询的关键字,然后通过一个按钮来执行报警数据查询脚本,并把查询出的结果加载到当前报警控件中。<本例中数据查询脚本的思路是根据选项组所选定的参数加载相应的归档变量到在线趋势控件,根据设定的查询时间范围设定控件时间轴的开始时间及结束时间。本文以 WinCC V8.0为例,介绍如何动态的查询WinCC报警记录,包括关键字查询/时间&关键字查询,如图1所示。<在WinCC变量管理中创建不同的变量组,并在变量组下创建相应的变量,如图2 所示(下图并非实际变量值)。

weixin_45072604的博客 2712

3.2 AJAX发布帖子 显示帖子详情

文章目录AJAX发布帖子 显示帖子详情1、引入fastjson包2、编写方法获取JSON字符串增加插入帖子的方法编写DiscussPostMapper编写DiscussPostService编写DiscussPostController处理js显示帖子详情增加通过id查询帖子的方法在帖子标题上增加访问详情页面的链接处理discuss-detail页面 AJAX发布帖子 显示帖子详情 1、引入fastjson包 <!-- https://mvnrepository.com/artifact/c

Qiuyuguohou的博客 1005

3D Slicer肺部CT病灶体积测量:阈值法实操全流程

在医学影像分析中,病灶体积的精准测量是肿瘤评估、疗效随访和科研统计的重要基础。传统逐层手动勾画不仅耗时,且主观差异大,而基于CT值的阈值分割技术,通过设定灰度区间自动提取目标体素,能够快速生成三维分割结果,具有高效、可重复、边界标准统一的优势,尤其适用于肺部实性结节这类与周围肺实质对比度高的病灶。理解体素与CT值的关系,掌握阈值范围的设定逻辑,并配合连通域分析、边缘修正等工程化操作,即可在3D Slicer中稳定获得可靠的体积数据。本文从技术原理出发,系统梳理了DICOM导入、窗宽窗位调节、阈值选择、三维重

weixin_29050271的博客 251

Flask项目实战——11—(帖子详情展示、后台帖子加精管理、前台评论功能实现、Flask分页功能)

1、帖子详情展示 前台蓝图文件:apps/front/views.py,创建帖子详情页的路由 # -*- encoding: utf-8 -*- """ @File : views.py @Time : 2020/5/11 9:59 @Author : chen 前台蓝图文件:apps/front/views.py """ # 前台的蓝图文件 类视图函数写在这里 from flask import ( Blueprint, render_template, views

weixin_42118531的博客 1964

ROS基础(一)——命令行代码(以小乌龟程序为例)

ROS学习(一)——命令行代码(以小乌龟程序为例)1 基础命令行语句1.1 roscore1.2 rqt_graph(显示系统计算图)1.3 rosnode(显示所有节点相关信息)1.4 rostopic(和话题相关)2 服务类型指令rosservice同时存在两只海龟3 指令记录 1 基础命令行语句 从大二四旋翼无人机编队后就没再接触过ROS了,最近由于科研任务需要重新捡起来,打算系统学一下!学习过程参考古月居老师的视频,有兴趣的同学可以去[BliBli]学习。 学习平台:Ubuntu 18.04 硬件

北理自动化小白 6267

分支界定法(branch and bound)与背包问题的代码实现

分支界定法(branch and bound)与背包问题的代码实现 LastEditTime: - 2021.2.3 不卖茶的吴龙茗茶 分支界定法是一种在整数规划中常用的算法,其主要思想为将大问题分割成小问题进行求解。分支界定法是围绕着一个二叉树进行的,以背包问题为例: 假设小明有一个背包,其总的容积为30,小明有三件东西可以带,三件东西的体积分别为:10,15,20。求问小明如何能够在背包中装下更大体积的物品? .

小吴同学的小屋 1885

3.开发首页 登陆模块

首页开发

REXmama的博客 959

20 Embedding 与向量检索:语义为什么能变成距离

Embedding 是 RAG 语义检索的数学基础。本文用工程视角讲清文本如何变成向量,为什么语义相近会表现为距离相近,Top-K 检索如何工作,以及在模型选择、同模型约束、精确匹配和召回评估上的常见坑。

老于的笔记的专栏 6204

补-社区网站1.5 开发社区首页

  开发流程 (1)1次请求的执行过程   分步实现 (1)开发社区首页,显示前10个帖子 (2)开发分页组件,分页显示所有的帖子   新建DiscussPost类, private int id; private int userId; private String title; private String content; private int type; private int status; private Date createTime;

xiaoshuzi666的博客 366

CAPL学习之路-关键字

在capl中,关键字具有特殊的含义和用途,因此,不得将关键字用作变量或函数的名称 声明数据类型的关键字 可以用不同的关键字,声明或引用不同类型的数据 byte 声明一个字节的变量 word 声明两个字节的变量 dword 声明四个字节的变量 int 声明两个字节的整数 long 声明四个字节的变量 qword 声明八个字节的变量 char 声明一个字符串 float 声明一个浮点数,8个字节 double 声明一个浮点数,8个字节 定义一个结构体或枚举类型 struct struct Data { .

jasonj333 6108

基于哈夫曼编码的图像加密算法matlab仿真

哈夫曼编码是一种常用的无损数据压缩算法,其基本思想是根据字符出现的频率构造一棵哈夫曼树,将出现频率高的字符编码为短码,出现频率低的字符编码为长码,从而实现数据的压缩。在图像加密领域,哈夫曼编码也被广泛应用于图像加密算法中,基于哈夫曼编码的图像加密算法具有加密速度快、加密强度高等优点。基于哈夫曼编码的图像加密算法的基本思路是将待加密的图像分为多个小块,对每个小块进行哈夫曼编码,然后将哈夫曼编码后的结果进行异或运算,最后将异或运算后的结果重新编码,以实现图像加密。具体步骤如下:将待加密的图像分成多个小块。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 263

社区项目(4):首页文章列表展示(导航高亮、文章分页)

显示导航栏 因为导航栏要在所有页面都显示,所以在公共控制器(base.php)中实现 然后在初始化方法中调用 //显示分类导航 protected function showNav() { //1、查询分类表获取所有分类,status1显示,0隐藏 $cateList = ArtCate::where('status', 1)->order('sort', 'asc')->select(); //2、将分类信息赋值给模板 $this->view

weixin_41897680的博客 521

SAP-ABAP:接口多继承与复合接口设计:复杂业务场景下的能力组合实现方案

本文介绍了ABAP中通过接口多继承与复合接口设计实现能力组合的方案。文章首先辨析了类实现多个接口与接口继承接口的核心概念,指出ABAP虽不支持类多继承,但可通过这两种机制灵活组合能力。随后详细讲解了类实现多个接口的语法规则与三种调用方式,并给出了五个基础能力接口的设计示例(日志记录、权限校验、数据导出、消息通知、格式转换)。作者通过对比单继承与接口多重实现的差异,强调后者在解耦和扩展性上的优势,为复杂业务场景下的能力组合提供了实用解决方案。

baidu_35680696的博客 246

社交网站后端项目开发日记(二)

本项目vb.net教程目标是开发一个社区网c#教程站,拥python基础教程有发帖、讨论、搜索、登录等一个java基础教程正常社区拥有sql教程的功能。涉及到的版本参数为: JDK1.8 Maven3.8.1(直接集成到IDEA) Springboot 2.5.1 tomcat 9.0.45 Mybatis Mysql 8.0.15 参考网站(在使用框架过程中可能会看的开发文档): https://mvnrepository.com/查找maven依赖 https://mybatis.o..

chinaherolts2008的博客 1050

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

牛客网项目——项目开发(二):开发社区首页

文章目录1. DAO层开发1.1 数据库信息1.2 在entity中新建 DiscussPost 实体类1.3 在dao中创建 DiscussPostMapper接口1.4 在mapper下创建discusspost-mapper.xml1.5 dao层测试2. SERVICE层开发2.1 在service包下新建业务组件DiscussPostService2.2 在service包下新建业务组件UserService3. 视图层开发3.1 样式文件,图片,js这些静态资源粘贴到static下3.2 mai

平什么阿的博客 2126

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【SpringBoot论坛项目实战】1、初识Spring Boot,开发社区首页

目录1 初识Spring Boot,开发社区首页1.1 课程介绍1.1.1 课程目标1.1.2 项目需求1.1.3 技术架构1.1.4 开发环境1.2 搭建开发环境1.2.2 安装Apache Maven 配套视频:BV1qV411z7K2 1 初识Spring Boot,开发社区首页 1.1 课程介绍 1.1.1 课程目标 学会主流的web开发技术和框架 积累一个真实的web项目的开发经验 掌握热点面试题的答题技巧 1.1.2 项目需求 简化版牛客网首页 可以按最新、最热查看帖子 右上角可以发帖 为了

qq_38939822的博客 1836

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

1.6开发社区首页——一次开发流程详解

因此,开发的时候发开发DAO,然后Service,然后Controller。 https://blog.csdn.net/Mr_zhang66/article/details/115471759 社区首页要实现一个查询帖子的功能,分为两步实现: 1.先显示前十个数据 2.开发分页组件,实现 分页显示所有组件 帖子表在 查看各项属性 comment count 评论数量 score 帖子分数 测试数据: 1.开发DAO,首先写实体类 先把属性写好, 自动写好方法体, 自动写好ToString .

Mr_zhang66的博客 286

用户模块的实现和首页的开发(二)

用户模块的实现和首页的开发

分享学习编程中的遇到的问题,总结平时的笔记 779

程序员如何用1024网站模板快速搭建技术社区?

技术社区需要核心功能包括用户系统、内容发布和互动。提供的1024风格模板,尝试后觉得特别适合快速搭建,这里分享一下我的实战经验。:保留1024的简洁排版,但改用更现代的卡片式设计,调整了字体和间距提升可读性。这个模板节省了我至少两周开发时间,特别适合需要快速验证想法的开发者。最近想搭建一个技术交流社区,但自己从头开发太耗时。上还能直接体验其他用户分享的社区项目,学习不同实现方案。在导航栏增加切换按钮实时生效。

OnyxPanther23的博客 1885

总结-1 首页内容

功能 首页的功能主要是展现帖子,需要做到能分页展示和导航栏根据用户是否登录给出不同的显示 Thymeleaf 配置 spring.thymeleaf.cache=false 表示关闭thymeleaf的缓存,开发过程中要关闭,上线之后可以开启 头部 在标签中使用xmlns申明启用thymeleaf模板引擎 <html lang="en" xmlns:th="http://www.thyme...

qq_41916811的博客 652

这款个人网站太绝了!这可能是开源社区中最强大的个人网站开源项目,3分钟快速搭建特色个人网站和社区

Symphony是一款用Java实现的高颜值、现代化社区系统平台,可用于论坛、BBS、社交网络、博客等场景应用。相比很多传统的社区论坛系统通常存在许多弊端如:界面风格老式、缺少现代化交互元素、功能单一等等,Symphony以下一代社区系统为设计目标,提供了超高颜值的界面,超全面的功能如知识问答,支持用户分享、交友、游戏等,给用户提供更人性化的社区交互和搜索引擎优化,帮助用户搭建独一无二的个人网站平台。1000+优质开源项目更新进度:238/1000。如需更多类型优质项目推荐,请在文章后留言。

513
上一篇: 为什么机器学习能够预测未知数据集?
下一篇: springboot3 解决:Could not autowire. No beans of ‘JavaMailSender‘ type found
yumuing blog
博客等级 码龄6年 9922粉丝 64原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

yumuing blog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值