(仿牛客社区项目)Java开发笔记1.1:完成开发首页功能

文章目录

  • 完成仿牛客社区项目开发首页功能
  • 1.实体引入
    • 1.1User类
    • 1.2DiscussPost类
    • 1.3Page类
  • 2.配置文件设定
  • 3.dao层
    • 3.1UserMapper的功能
    • 3.2UserMapper的实现
    • 3.3DiscussPostMapper的功能
    • 3.4DiscussPostMapper的实现
  • 4.Service层
    • 4.1UserService类
    • 4.2DiscussPost类
  • 5.HomeController层
  • 6.View层
  • 7.功能演示

完成仿牛客社区项目开发首页功能

项目按照MVC的编程思想,分为dao层,service层,controller层和view层进行开发,本章实现仿牛客社区的首页访问功能。

1.实体引入

这里对项目目前涉及到的实体类进行介绍,分别为用户User类,帖子DiscussPost类,用于给前端分页封装的bean:分页Page类。

1.1User类

User类用于对注册用户的相关属性进行描述,在工程中创建entity包,创建User类,相关代码见下。

package com.gerrard.community.entity;

import java.util.Date;

public class User {

    private int id;
    private String username;
    private String password;
    private String salt;     //盐,添加在用户password字段后面对密码进行扰动,并将最终结果进行MD5加密,提升安全性
    private String email;   //用户注册的邮箱
    private int type;      //0-普通用户; 1-超级管理员; 2-版主;
    private int status;    //0-未激活; 1-已激活;
    private String activationCode;  //激活码,作用待阐述
    private String headerUrl;  //用户头像Url地址
    private Date createTime;   //用户注册时间

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getActivationCode() {
        return activationCode;
    }

    public void setActivationCode(String activationCode) {
        this.activationCode = activationCode;
    }

    public String getHeaderUrl() {
        return headerUrl;
    }

    public void setHeaderUrl(String headerUrl) {
        this.headerUrl = headerUrl;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", salt='" + salt + '\'' +
                ", email='" + email + '\'' +
                ", type=" + type +
                ", status=" + status +
                ", activationCode='" + activationCode + '\'' +
                ", headerUrl='" + headerUrl + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

1.2DiscussPost类

封装帖子的相关信息,在entity包中创建DiscussPost类,相关代码见下【相关的get,set,toString方法这里省略,后同】。

package com.gerrard.community.entity;

import java.util.Date;

public class DiscussPost {

    private int id;
    private int userId;
    private String title;
    private String content;
    private int type;    //0-普通; 1-置顶;
    private int status;  //0-正常; 1-精华; 2-拉黑;
    private Date createTime;
    private int commentCount;
    private double score;
}

1.3Page类

Page类对分页信息进行封装,方便前端实现分页功能【帖子,评论等需要进行分页之初都可以借助此实体类完成】。

Page类中可以卸载部分计算,根据Page类中的current,limit,rows字段,提供getOffset方法返回当前页中第一条数据在数据表中的行号,提供getTotal方法返回总页数,提供getFrom方法返回"前端页面底部分页栏中多个小格格中第一个格格的序号”,提供getTo方法返回“前端页面底部分页栏中多个小格格中最后一格格的序号“。

在entity包中创建Page类,相关代码见下。

package com.gerrard.community.entity;

//封装分页相关信息
public class Page {
    //当前页码,默认为第一页
    private int current=1;
    //显示上限,每页展示多少条记录,默认为10
    private int limit=10;
    //数据总数(用于计算总页数),数据总行数,一般从MySQL表中查找
    private int rows;
    //所有页码访问的根路径
    private String path;

//    public Page() {
//        System.out.println("construct");
//    }

    public int getCurrent() {
        return current;
    }

    public void setCurrent(int current) {
        if(current>=1){
            this.current = current;
        }
    }

    public int getLimit() {
            return limit;
    }

    public void setLimit(int limit) {
        if(limit>=1 && limit <=100) {             //将合法性判断下沉至entity端
            this.limit = limit;   //超出范围则还是默认一页显示10条
        }
    }

    public int getRows() {
            return rows;
    }

    public void setRows(int rows) {
        if(rows>=0){
            this.rows = rows;
        }
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 获取当前页的起始行
     *
     * @return
     */
    public int getOffset(){
        //当前页的第一条数据记录在多少行
        return (current-1)*limit;
    }

    /**
     * 获取总页数
     *
     * @return
     */
    public int getTotal(){
        // |-- rows/limit--| 向上取整操作
        if(rows % limit ==0){
            return rows/limit;
        }else{
            return rows/limit+1;
        }
    }

    /**
     * 获取起始页码
     *
     * @return
     */
    public int getFrom(){
        int from=current-2;
        return from<1?1:from;
    }

    /**
     * 获取结束页码
     *
     * @return
     */
    public int getTo(){
        int to=current+2;
        int total=getTotal();
        return to>total?total:to;
    }

}

2.配置文件设定

application.properties内容见下。

# ServerProperties
server.port=8080
server.servlet.context-path=/community

# ThymeleafProperties
spring.thymeleaf.cache=false

# DataSourceProperties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000


# MybatisProperties
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.gerrard.community.entity
mybatis.configuration.useGeneratedKeys=true
mybatis.configuration.mapUnderscoreToCamelCase=true

#logger
#logging.level.com.gerrard.community=debug
#logging.file=d:/gerrardProjects/data/gerrard/community.log

3.dao层

3.1UserMapper的功能

dao层主要的功能为与MySQL进行交互,对User MySQL表中的用户信息进行CRUD(create,retrieve,update,delete)操作,

【值得注意的是,本项目不涉及到delete操作,因为这会最终损失用户相关的数据,如果未来开发出某种功能需要用到历史数据,则数据的缺失会影响功能的使用[比如统计用户最近一年来的访问帖子的热度排行]。综上考虑,本项目对删除功能所采取的措施为:修改实体的状态字段,比如用户退出后,将用户的登录凭证状态字段置为1,表示凭证已失效】

UserMapper需要完成如下功能。

1.根据用户的id查询到用户的实体类®;

2.根据用户的username查询到用户的实体类®;

3.根据用户的email查询到用户的实体类®;

4.将用户实体类插入到User MySQL表中©;

5.更新用户的登录状态status(U)【0表示用户账号未激活,1表示用户账号已激活,未来用户点击激活邮件的链接时会使用此方法将登录状态改为1】;

6.更新用户的头像地址headUrl(U);

7.更新用户的密码password(U);

3.2UserMapper的实现

在工程中创建dao包,创建UserMapper接口,代码见下。

package com.gerrard.community.dao;

import com.gerrard.community.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Mapper
//@Repository
public interface UserMapper {

    User selectById(int id);

    User selectByName(String username);

    User selectByEmail(String email);

    int insertUser(User user);

    int updateStatus(@Param("id")int id, @Param("status")int status);

    int updateHeader(@Param("id")int id,@Param("headerUrl")String headerUrl);

    int updatePassword(@Param("id")int id,@Param("password")String password);
}

创建UserMapper.xml,代码见下。


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gerrard.community.dao.UserMapper">
    <sql id="insertFields">
        username,password,salt,email,type,status,activation_code,header_url,create_time
    sql>
    <sql id="selectFields">
        id,username,password,salt,email,type,status,activation_code,header_url,create_time
    sql>
    <select id="selectById" resultType="User">
        select<include refid="selectFields">include>
        from user
        where id=#{id}
    select>

    <select id="selectByName" resultType="User">
        select <include refid="selectFields">include>
        from user
        where username = #{username}
    select>

    <select id="selectByEmail" resultType="User">
        select <include refid="selectFields">include>
        from user
        where email = #{email}
    select>

    <insert id="insertUser" parameterType="User" keyProperty="id">
        insert into user(<include refid="insertFields">include>)
        values(#{username},#{password},#{salt},#{email},#{type},#{status},#{activationCode},#{headerUrl},#{createTime})
    insert>

    <update id="updateStatus">
        update user set status=#{status} where id=#{id}
    update>

    <update id="updateHeader">
        update user set header_url=#{headerUrl} where id=#{id}
    update>

    <update id="updatePassword">
        update user set password=#{password} where id=#{id}
    update>

mapper>

3.3DiscussPostMapper的功能

DiscussPostMapper对DiscussPost MySQL表中的相关数据进行CRUD操作,这里先实现两个功能。

1.根据userId,offset,limit信息查询帖子集合【offset为当前页码第一条数据在MySQL数据库中的行数,limit为查询的消息记录数】,返回对应的实体类集合。

2.根据userId查询帖子数量。

3.4DiscussPostMapper的实现

在dao包中添加DiscussPostMapper接口,相关代码见下。

package com.gerrard.community.dao;

import com.gerrard.community.entity.DiscussPost;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface DiscussPostMapper {

    List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);

    // @Param注解用于给参数取别名,
    // 如果只有一个参数,并且在里使用,则必须加别名.
    int selectDiscussPostRows(@Param("userId") int userId);

}

创建DiscussPostMapper.xml,代码见下。


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gerrard.community.dao.DiscussPostMapper">
    
        
    
    <sql id="selectFields">
        id, user_id, title, content, type, status, create_time, comment_count, score
    sql>
    <sql id="insertFields">
        user_id, title, content, type, status, create_time, comment_count, score
    sql>

    <select id="selectDiscussPosts" resultType="DiscussPost">
        select <include refid="selectFields">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>

4.Service层

4.1UserService类

UserService主要对Mapper的方法进行排列组合,亦或自己提供额外方法来实现特定的功能,这里需要实现的服务为根据与User的id返回User实体类。在工程中创建service包,创建UserService类,代码见下。

package com.gerrard.community.service;

import com.gerrard.community.dao.UserMapper;
import com.gerrard.community.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService implements CommunityConstant {
    
    @Autowired
    private UserMapper userMapper;

    public User findUserById(int id){
        return userMapper.selectById(id);
}

4.2DiscussPost类

DiscussPost类提供与帖子操作相关的服务,在controller层中进行调用,这里需要实现的服务是根据userId,offset,limit信息查询帖子集合;根据userId查询帖子集合总行数。在工程中创建DiscussPost类,相关代码见下。

package com.gerrard.community.service;

import com.gerrard.community.dao.DiscussPostMapper;
import com.gerrard.community.entity.DiscussPost;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DiscussPostService {

    @Autowired
    private DiscussPostMapper discussPostMapper;

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

    public int findDiscussPostRows(int userId) {
        return discussPostMapper.selectDiscussPostRows(userId);
    }

}

5.HomeController层

用户操作浏览器的过程相当于一个”请求-响应“的过程,Controller层正好对应于此块,是连接项目前端和后端的纽带。

UserController这里完成的主要功能为接收浏览器发送的“/index” 的get请求,响应thymeleaf的模板页面index.html,即开发社区的首页页面。在工程中创建controller包,创建HomeController类,相关代码如下。

未登录状态UserId为0,代码将要返回未登陆状态查询到的所有帖子信息。

package com.gerrard.community.controller;

import com.gerrard.community.entity.DiscussPost;
import com.gerrard.community.entity.Page;
import com.gerrard.community.entity.User;
import com.gerrard.community.service.DiscussPostService;
import com.gerrard.community.service.LikeService;
import com.gerrard.community.service.MessageService;
import com.gerrard.community.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.gerrard.community.util.CommunityConstant.ENTITY_TYPE_POST;

@Controller
public class HomeController {
    @Autowired
    private DiscussPostService discussPostService;

    @Autowired
    private UserService userService;

    @RequestMapping(path="/index",method= RequestMethod.GET)
    public String getIndexPage(Model model, Page page){
        // 方法调用钱,SpringMVC会自动实例化Model和Page,并将Page注入Model.
        // 所以,在thymeleaf中可以直接访问Page对象中的数据.
     //   System.out.println(page.hashCode());
        page.setRows(discussPostService.findDiscussPostRows(0));
        page.setPath("/index");

        List<DiscussPost> list=discussPostService.findDiscussPosts(0,page.getOffset(),page.getLimit());
     //对list信息进行增益,可以想象对数据进行纵向扩展,一维->二维
        List<Map<String,Object>> discussPosts=new ArrayList<>();

        if(list!=null){
            for(DiscussPost post:list){
                Map<String,Object> map=new HashMap<>();
                map.put("post",post);
                User user=userService.findUserById(post.getUserId());
                map.put("user",user);
                discussPosts.add(map);
            }
        }
        model.addAttribute("discussPosts",discussPosts);
        return "/index";
    }

}

6.View层

View层用于发送Http get/post请求,Controller层对相应的请求捕获进行处理,与数据库交互运算后将结果传回给前端View层,View层根据后端传回的信息,将相关的信息使用动态网页技术展示在页面中。

这里使用前端模板引擎thymeleaf对数据进行动态展示,将传统的静态网页改造为动态thymeleaf网页一般需要如下几个步骤。以牛客社区首页index.html为例:

1.引入th库

NowCoder_1_1

2.相对路径需要和th绑定;

image-20220716210942628

NowCoder_1_5

3.业务修改相关标签,包含动态展示数据及开发分页功能。

(仿牛客社区项目)Java开发笔记1.1:完成开发首页功能_第1张图片

动态展示帖子数据的代码见下。


			<ul class="list-unstyled">
				<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
					<a th:href="@{|/user/profile/${map.user.id}|}">
						<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 th:href="@{|/discuss/detail/${map.post.id}|}" 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}">寒江雪u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18b>
							<ul class="d-inline float-right">
                                <li class="d-inline ml-2"><span th:text="${map.likeCount}">11span>li>
								<li class="d-inline ml-2">|li>
								<li class="d-inline ml-2">回帖 <i th:text="${map.post.commentCount}">7i>li>
							ul>
						div>
					div>
				li>
			ul>

分页功能的具体代码见下。

解释一下

th:href="@{${page.path}(current=${page.current-1})

浏览器输入/index,发送get请求,请求经过HomeController的getIndexPage方法后,将index.html动态页面返回至浏览器,这个动态页面的底部展示有分页信息,把每页数据请求的Url地址动态写死,下次点击访问别页时会再次经过HomeController的getIndexPage方法,创建Page对象时,会将current信息赋值给Page对象中的current字段,进而影响到查出的数据库帖子集合信息。


			<nav class="mt-5" th:if="${page.rows>0}" th:fragment="pagination">
				<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}">1a>
					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>

改为动态页面后的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>牛客网-首页title>
head>
<body>
<div class="nk-container">
	
	<header class="bg-dark sticky-top" th:fragment="header">
		<div class="container">
			
			<nav class="navbar navbar-expand-lg navbar-dark">
				
				<a class="navbar-brand" href="#">a>
				<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
					<span class="navbar-toggler-icon">span>
				button>
				
				<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" th:href="@{/index}">首页a>
						li>
						<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser!=null}">
							<a class="nav-link position-relative" th:href="@{/letter/list}">消息<span class="badge badge-danger" th:text="${messageUnRead}" th:if="${messageUnRead!=0}">12span>a>
						li>
						<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}">
							<a class="nav-link" th:href="@{/register}">注册a>
						li>
						<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}">
							<a class="nav-link" th:href="@{/login}">登录a>
						li>
						<li class="nav-item ml-3 btn-group-vertical dropdown" th:if="${loginUser!=null}">
							<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
								<img th:src="${loginUser.headerUrl}" class="rounded-circle" style="width:30px;"/>
							a>
							<div class="dropdown-menu" aria-labelledby="navbarDropdown">
								<a class="dropdown-item text-center" th:href="@{|/user/profile/${loginUser.id}|}">个人主页a>
								<a class="dropdown-item text-center" th:href="@{/user/setting}">账号设置a>
								<a class="dropdown-item text-center" th:href="@{/logout}">退出登录a>
								<div class="dropdown-divider">div>
								<span class="dropdown-item text-center text-secondary" th:utext="${loginUser.username}">nowcoderspan>
							div>
						li>
					ul>
					
					<form class="form-inline my-2 my-lg-0" method="get" th:action="@{/search}">
						<input class="form-control mr-sm-2" type="search" aria-label="Search" name="keyword" th:value="${keyword}"/>
						<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 th:class="|nav-link ${orderMode==0?'active':''}|" th:href="@{/index(orderMode=0)}">最新a>
                    li>
                    <li class="nav-item">
                        <a th:class="|nav-link ${orderMode==1?'active':''}|" th:href="@{/index(orderMode=1)}">最热a>
                    li>
				ul>
				<button type="button" class="btn btn-primary btn-sm position-absolute rt-0" data-toggle="modal" data-target="#publishModal" th:if="${loginUser!=null}">我要发布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">×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 th:href="@{|/user/profile/${map.user.id}|}">
						<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 th:href="@{|/discuss/detail/${map.post.id}|}" 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}">寒江雪u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18b>
							<ul class="d-inline float-right">
                                <li class="d-inline ml-2"><span th:text="${map.likeCount}">11span>li>
								<li class="d-inline ml-2">|li>
								<li class="d-inline ml-2">回帖 <i th:text="${map.post.commentCount}">7i>li>
							ul>
						div>
					div>
				li>
			ul>
			
			<nav class="mt-5" th:if="${page.rows>0}" th:fragment="pagination">
				<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}">1a>
					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">
									公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司
								li>
								<li class="nav-item text-white-50">
									联系方式:010-60728802(电话)    [email protected]
								li>
								<li class="nav-item text-white-50">
									牛客科技©2018 All rights reserved
								li>
								<li class="nav-item text-white-50">
									京ICP备14055008号-4     
									<img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />
									京公网安备 11010502036488号
								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>

7.功能演示

运行CommunityApplication.java,在浏览器中输入URL地址,可返回牛客网首页。

(仿牛客社区项目)Java开发笔记1.1:完成开发首页功能_第2张图片

分页功能演示:

(仿牛客社区项目)Java开发笔记1.1:完成开发首页功能_第3张图片

(仿牛客社区项目)Java开发笔记1.1:完成开发首页功能_第4张图片

写在最后:页面中相关的前端代码是本项目最终的代码,前端的代码在项目整个开发过程中经历了好几轮重构与迭代,因此最好用git对本项目进行版本控制。但由于笔者当时开发时时间较为仓促,没有使用git对项目进行版本控制,前端部分的代码和界面演示这一环节采用的是项目完成后的代码,在后期有时间的情况下,笔者会将笔记涉及到这一部分的内容进行优化。

你可能感兴趣的:(Java项目,java,前端,数据库,spring,boot)