文章目录

  • 1. 创建项目
  • 2. 数据库设计
  • 3. 前置任务
    • 3.1 拦截器
    • 3.2 统一数据格式
    • 3.3 创建一个 Constant
    • 3.4 统一异常处理
    • 3.5 密码加密
  • 4. 功能实现
    • 4.1 登录功能
    • 4.2 注册功能
    • 4.3 博客列表页 (功能实现)
      • 4.3.1 左侧框
      • 4.3.2 右侧框 (分页功能 + 页面显示)
    • 4.4 博客详情页
    • 4.5 写博客功能
    • 4.6 注销功能


博客系统


前言 : 本文主要 是通过 ssm 搭配之前的 博客系统页面, 来完成一个小项目 .

项目完整代码

1. 创建项目


使用到的技术 :

  1. 后端 : Spring Boot + Spring MVC + MyBatis + 拦截器 / 统一异常处理 + 统一数据返回 (Spring AOP)
  2. 前端 : HTML + CSS + javaScript + jquery

2. 数据库设计


通过我们需要实现的功能 ,能够知道 需要两张表 , 第一张表 用户表 ,用来完成登录 注册 注销等功能 , 第二张表 用来存储 博客 , 查看博客等 .


1. 创建 用户列表

-- 如果存在 这个数据库就删除drop
database if exits mywebsite;-- 创建数据库create
database mywebsite;-- 选中数据库
use
mywebsite-- 创建用户表create table user
(
-- 系统分配id        int primary key auto_increment,
-- 必填username  varchar(255) not null,password  varchar(255) not null,
-- 非必填qq        varchar(255)  default '',address   varchar(255)  default '',crateTime datetime      default now(),sex       varchar(2)    default '男',
-- url 用来存放 用户 头像 图片 如果用户没有上传就使用默认的 .url       varchar(1024) default '阳台.png'
);

2. 创建 blog 表

create table blog
(blogId   int primary key auto_increment,title    varchar(1024) not null,-- 这里 一篇博客的内容可能非常多 使用 varchar可能不够 ,这里就是用 mediumtextcontent  mediumtext,--  用户 iduserid   int,-- 发布时间postTime datetime default now(),-- 类型type     varchar(255)  not null,
);


表创建好了 ,下面就可以完成一些 准备工作 ,比如 配置好环境 , 写好 拦截器 ,统一数据格式 等 .


这里统一数据格式 可以写一个类 , 通过这个类来返回 或者 通过 @ControllerAdvice + ResponseBodyAdvice 来完成 , 这里我会使用 写一个类来返回信息 .

3. 前置任务


这里先来完成 拦截器 , 统一异常处理 , 统一数据格式 .

application.yml

# 配置当前运行的环境 (配置文件)# spring > profiles > active
spring:profiles:active: dev # 使用开发环境的配置文件# 配置 mybatis xml 保存路径
mybatis:mapper-locations: classpath:mybatis/**Mapper.xml# 在公共 yml 文件 来 配置 mybatis 的保存路径

application-dev.yml

# 开发环境的配置文件spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mywebsite?characterEncoding=utf8username: rootpassword: 1234driver-class-name: com.mysql.cj.jdbc.Driver # 在 8.0 之前 是没有点 jc的 -> com.mysql.jdbc.Driver# 设置日志级别
logging:level:com:example:usermanager: debug# 对具体类机型日志级别设定# 开启 MyBatis SQL 打印
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.1 拦截器

这里我们实现拦截器 主要有两部 : 1. 自定义拦截器 , 2. 给拦截器设置规则 (那些 需要拦截 , 那些不需要拦截)

附上代码 :

AppConfig 类

package com.example.usermanager.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 系统配置文件*/@Configuration
public class AppConfig implements WebMvcConfigurer {// 注入拦截器@Autowiredprivate LoginIntercept loginIntercept;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginIntercept).addPathPatterns("/**").excludePathPatterns("/user/login").excludePathPatterns("/insert").excludePathPatterns("/css/**").excludePathPatterns("/fonts/**").excludePathPatterns("/images/**").excludePathPatterns("/js/**").excludePathPatterns("/login.html");}
}


LoginIntercept

package com.example.blog_ssm.config;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;/*** 自定义拦截器*/@Component
public class LoginIntercept implements HandlerInterceptor {/*** true  表示已经登录 ,会继续访问目标方法* false  表示未登录 , 跳转到登录页面*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// false : 如果 没有 session 也不会创建HttpSession session = request.getSession(false);if (session != null && session.getAttribute("user") != null) {// 表示登录成功return true;}// 403 当前你没有资格访问response.setStatus(403);// 重定向response.sendRedirect("/login.html");return false;}
}


拦截器 弄好了 , 我们可以看看效果 :


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.example.blog_ssm.mapper.UserMapper"></mapper>

3.2 统一数据格式


1. 创建一个类用来 统一 数据格式


附上代码 :

package com.example.blog_ssm.util;import lombok.Data;/*** 用来统一数据格式** @param <T>*/
@Data
public class ResponseBodyMessage<T> {// 1. 状态码private Integer status;// 2. 信息描述private String message;// 3. 数据private T data;public ResponseBodyMessage(Integer status, String message, T data) {this.status = status;this.message = message;this.data = data;}
}


2. 使用注解

3.3 创建一个 Constant


之前我们写拦截器的使用 ,通过 session 中的key 获取 user 对象时 ,写了一个 “user” , 这里可以使用一个类 ,在类里面写一个 常量 ,然后 只需要通过这个 来获取 user 即可 。

3.4 统一异常处理

代码 :

package com.example.blog_ssm.config;import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.HashMap;/*** 统一异常的拦截处理类*/@RestControllerAdvice// 使用 @ControllerAdvice 需要再加一个注解 @ResponseBody (返回一个非静态页面)public class MyExceptionAdvice {@ExceptionHandler(Exception.class)public Object exceptionAdvice(Exception e) {HashMap<String, Object> result = new HashMap<>();result.put("status", -1);result.put("message", "程序异常 : " + e.getMessage());result.put("data", "");return result;}
}

3.5 密码加密


在 util 包内 创建 PasswordUtil 类

package com.example.blog_ssm.util;import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import org.springframework.util.StringUtils;/*** 密码工具类*/public class PasswordUtil {/*** 1. 加密 (加盐)*/public  String encrypt(String password) {// 密码 : 随机盐值 + 密码String salt = IdUtil.simpleUUID();String finalPassword = SecureUtil.md5(salt + password);return salt + "$" + finalPassword;}/*** 解密** @param password 要验证的密码 (未加密)* @return 数据库中的加了盐值的密码*/public  boolean decrypt(String password, String securePassword) {boolean result = false;if (StringUtils.hasLength(password) && StringUtils.hasLength(securePassword)) {// 注意 : $ 是特殊字符 , 使用 split 分割时 需要转移if (securePassword.length() == 65 && securePassword.contains("$")) {// 随机盐值 为 32  , md5 加密的 密码 32 加上 $ 1字符 总共 65 字符String[] securePasswordArr = securePassword.split("\\$");// 盐值String salt = securePasswordArr[0];// 根据盐值 加密的密码String finalPassword = securePasswordArr[1];// 根据盐值 对新的密码进行加密password = SecureUtil.md5(salt + password);// 进行对比if (finalPassword.equals(password)) {result = true;}}}return result;}
}


将 工具类 交给 spring 管理 ,后面使用 只需要注入即可 .


到此我们就完成了前置任务, 下面来写我们的功能

4. 功能实现

4.1 登录功能


约定一下 : 请求和响应

请求 : [{post, (登录一般使用 post)/user/login  data:{username : "张三",password :"1234"}}
]响应 : [{"status" : 1  / -1 (1 表示成功 , -1 表示失败) ."message" : "登录成功" / "登录失败","data" : true / false}
]


图一 :


图二 :


此时 后端就完成了 下面就可以来写前端了 :


图一 :


图二 :


代码 :

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录页面</title><link rel="stylesheet" href="./css/common.css"><link rel="stylesheet" href="./css/login.css"><script src="./js/jquery.js"></script>
</head>
<body><!-- 导航栏 -->
<div class="nav"><img src="./imgs/阳台.png"><span class="title">我的博客系统</span><!--    这个标签仅仅用于占位 ,把下面几个a 标签挤到右边--><div class="spacer"></div><a href="#">主页</a><a href="#">写博客</a>
</div><!--正文部分这个 login-container 是贯穿整个页面的容器
-->
<div class="login-container"><!-- 垂直水平居中的登录对话框    --><div class="login-dialog"><h3>登录</h3><div class="row"><span>用户名</span><input type="text" id="username" placeholder="输入用户名"></div><div class="row"><span>密码</span><input type="password" id="password" placeholder="输入密码"></div><div class="row"><button id="submit">提交</button></div><div class="row"><a id="insert" href = "add.html">注册</a></div><div class="a">没有账户? 点击上面进行注册</div></div></div><script>// 通过 id 选中输入框 ,let username = document.querySelector("#username");// 通过 id 选中密码框let password = document.querySelector("#password");let submit = document.querySelector("#submit");// 点击按钮后触发submit.onclick = function () {// 1. 判断 username or passwordif (jQuery.trim(username.value) === '') {alert("请先输入用户名");}if (jQuery.trim(password.value) === '') {alert("请先输入密码")}// 此时 用户 和密码 都有了 ,发送 请求$.ajax({url: "/user/login",type: "POST",data: {"username": username.value,"password": password.value},// 回调函数success: function (result) {if (result != null && result.data.status > 0) {// 登录成功 , 跳转到 博客列表页location.href = "blog_list.html";} else {//登录 登录失败 ,alert("登录失败,请重新输入")}}})}</script></body>
</html>

登录功能搞完 : 下面就可以来弄 注册功能

4.2 注册功能


约定一下请求和响应 :


请求: [{post,url : /user/adddata:{必填"username" : xxx,"password" : xxx,非必填"address" : xxx,......}}
]响应 : [{data:{"status" : 1 / -1  (注册成功 / 注册失败)"message" : "","data" : true / false}}
]


图一 :


图二 :


图三 :


图四 :

附上代码 :

  /*** 2. 注册功能*/@RequestMapping(value = "/add")@Transactionalpublic ResponseBodyMessage<Boolean> addUser(User user, @RequestPart(required = false, value = "filename") MultipartFile file) {if (user == null) {return new ResponseBodyMessage(-1, "注册失败", false);}// 1. 判断 必填参数是否为空 (这里可以不写 ,前端大概率 是会判断的 。 )if ("".equals(user.getUsername())) {return new ResponseBodyMessage<>(-1, "注册失败, 当前用户为输入用户名", false);}if ("".equals(user.getPassword())) {return new ResponseBodyMessage<>(-1, "注册失败,当前用户未输入密码", false);}// 2. 校验用户名的 唯一性 :  如果 用户名已经纯在了 那么就不能注册User user2 = userService.getUserByUserName(user.getUsername());if (user2 != null) {return new ResponseBodyMessage<>(-1, "注册失败, 用户名已存在", false);}// 3. 手动设置 为 '' 的数据if ("".equals(user.getAddress())) {user.setAddress(null);}if ("".equals(user.getQq())) {user.setQq(null);}if ("".equals(user.getSex())) {user.setSex(null);}if ("".equals(user.getUrl())) {user.setUrl(null);}// 4. 对密码进行加密操作user.setPassword(passwordUtil.encrypt(user.getPassword()));// 5. 如果用户 上传了头像 ,可以将图片存入到本地if (file != null) {// 此时上传了头像 :// 获取到文件名 + 类型String fileNameAndType = file.getOriginalFilename();// 比如文件名为 : 阳台.png , 此时可以获取到 . 的 下标int index = fileNameAndType.lastIndexOf(".");// 从 index 位置开始截取String postfix = fileNameAndType.substring(index);// 判断一下 图片的格式是否符合预期要求if (".jpg".equals(postfix) || ".png".equals(postfix)) {// 通过 uuid 来设置文件名String uuid = IdUtil.simpleUUID();String imgFileStr = uuid + postfix;// 创建文件String path = IMAGE_PATH + imgFileStr;File imgFile = new File(path);if (!imgFile.exists()) {imgFile.mkdir();}try {//                指定图片 , 上传之后的存储位置file.transferTo(imgFile);// 文件上传成功 :user.setUrl(imgFileStr);userService.addUser(user);return new ResponseBodyMessage<>(1, "注册成功", true);} catch (IOException e) {//                e.printStackTrace();// 如果 创建失败 , 手动事务回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 此时 注册失败return new ResponseBodyMessage<>(-1, "注册失败", false);} else {return new ResponseBodyMessage<>(-1, "图片格式有误", false);}}// 6. 调用 userService 中的 addUser 方法 进行用户添加 (此时未上传图片 , 图片 为 默认)Integer ret = userService.addUser(user);if (ret != 1) {return new ResponseBodyMessage<>(-1, "注册失败", false);}return new ResponseBodyMessage<>(1, "注册成功", true);}


后端写完, 来完成我们的前端 :


之前并没有完成 注册页面 , 这里直接来拷贝一下


1. add.html

<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head><!-- 指定字符集 --><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>添加用户</title><link href="css/bootstrap.min.css" rel="stylesheet"><style>body {background-image: url("./images/阳台.png");background-repeat: no-repeat;background-size: cover;}</style>
</head>
<body>
<div class="container" style="width: 400px;"><h3 style="text-align: center;">添加用户</h3><div class="form-group"><label for="loginname">登录名:</label><input type="text" class="form-control" id="loginname" name="username" placeholder="请输入登录名"/></div><div class="form-group"><label for="username">姓名:</label><input type="text" class="form-control" id="username" name="name" placeholder="请输入姓名"/></div><div class="form-group"><label for="password">密码:</label><input type="password" class="form-control" id="password" name="password" placeholder="请输入密码"/></div><div class="form-group"><label for="password2">确认密码:</label><input type="password2" class="form-control" id="password2" name="password" placeholder="请输入密码"/></div><div class="form-group"><label>性别:</label><input id="man" type="radio" name="sex" value="男" checked="checked"/>男&nbsp;&nbsp;&nbsp;<input id="women" type="radio" name="sex" value="女"/>女</div><div class="form-group"><label for="age">年龄:</label><input type="number" class="form-control" id="age" name="age" placeholder="请输入年龄"/></div><div class="form-group"><label for="address">籍贯:</label><select name="address" id="address" class="form-control"><option value="北京">北京</option><option value="上海">上海</option><option value="广州">广州</option><option value="深圳">深圳</option><option value="成都">成都</option><option value="杭州">杭州</option><option value="重庆">重庆</option><option value="西安">西安</option><option value="武汉">武汉</option><option value="沧州">沧州</option><option value="江西">江西</option></select></div><div class="form-group"><label for="qq">QQ:</label><input type="text" id="qq" class="form-control" name="qq" placeholder="请输入QQ号码"/></div><div class="form-group"><label for="email">Email:</label><input type="text" id="email" class="form-control" name="email" placeholder="请输入邮箱地址"/></div><!--    style="display: none;"--><div class="form-group" id="adminDiv"><label for="email">管理员:</label><input id="admin_yes" type="radio" name="isadmin" value="1"/>是&nbsp;&nbsp;&nbsp;<input id="admin_no" type="radio" name="isadmin" value="0" checked="checked"/>否</div><div class="form-group" style="text-align: center"><input id="btn_sub" class="btn btn-primary" type="button" value="提交" onclick="mysub()"/><input id="btn_back" class="btn btn-default" type="button" value="返回" onclick="location.href='list.html'"/></div></div></body>
</html>


用到的 css 可以到 我 的资源里面下载 出来 , 比较多 这里就不拷贝到上面 了 .


这里我们需要使用 FormData 来 发送我们的数据 可以看来看一下这 : FormData

代码 :

<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head><!-- 指定字符集 --><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>添加用户</title><link href="./css/bootstrap.min.css" rel="stylesheet"><script src="./js/jquery.js"></script><style>body {background-image: url("./imgs/阳台.png");background-repeat: no-repeat;background-size: cover;}#image {width: 400px;height: 250px;border: 1px solid #eee;}.message {width: 110px;height: 50px;line-height: 50px;font-weight: 600;}</style>
</head>
<body><form enctype="multipart/form-data" id="form1"><div class="container" style="width: 400px;"><h3 style="text-align: center;">添加用户</h3><div class="form-group"><label for="username">姓名:</label><input type="text" class="form-control" id="username" name="name" placeholder="请输入姓名"/></div><div class="form-group"><label for="password">密码:</label><input type="password" class="form-control" id="password" name="password" placeholder="请输入密码 "/></div><div class="form-group"><label for="password2">确认密码:</label><input type="password2" class="form-control" id="password2" name="password" placeholder="请输入密码"/></div><div class="form-group"><label>性别:</label><input id="man" type="radio" name="sex" value="男" checked="checked"/>男&nbsp;&nbsp;&nbsp;<input id="women" type="radio" name="sex" value="女"/>女</div><div class="form-group"><label for="address">籍贯:</label><select name="address" id="address" class="form-control"><option value="">可以不选择</option><option value="北京">北京</option><option value="上海">上海</option><option value="广州">广州</option><option value="深圳">深圳</option><option value="成都">成都</option><option value="杭州">杭州</option><option value="重庆">重庆</option><option value="西安">西安</option><option value="武汉">武汉</option><option value="沧州">沧州</option><option value="江西">江西</option></select></div><div class="form-group"><label for="qq">QQ:</label><input type="text" id="qq" class="form-control" name="qq" placeholder="请输入QQ号码 (非必填) "/></div><div class="form-group"><input type="file" name="filename" id="imgFile"><span class="message">图片样式: </span><img src="" id="image"/></div><div class="form-group" style="text-align: center"><input id="btn_sub" class="btn btn-primary" type="button" value="提交"/><input id="btn_back" class="btn btn-default" type="button" value="返回" onclick="location.href='list.html'"/></div></div></form><!--//   拿到 input  type 为 radio 中的内容 即 获取 男 女let sex = $('input[name=sex]:checked').val();let address = $("#address").val();
--><script>let imgFile = document.querySelector("#imgFile");// 这一部分 : 当我们上传图片后 , 我们的 图片样式 就会将图片显示出来imgFile.onchange = function () {let img = document.querySelector("#image");let image = imgFile.files[0];// let formData = new FormData();if (image) {// formData.append('filename', image);img.src = window.URL.createObjectURL(image);}}// 当点击 提交按钮后 构造数据 , 通过 ajax 发送请求给后端let submit = document.querySelector("#btn_sub");submit.onclick = function () {let username = document.querySelector("#username");let password = document.querySelector("#password");let password2 = document.querySelector("#password2");// 通过 jquery 拿到  性别框里面的内容let sex = jQuery('input[name=sex]:checked').val();let address = document.querySelector('#address').value;let qq = document.querySelector("#qq").value;// jQuery.trim 去掉 前后空格if (jQuery.trim(username.value) === '') {alert("请先输入用户名!!")// 将焦点设置到 id 为 username 的输入 框上username.focus();return false;}if (jQuery.trim(password.value) === '') {alert("请先输入密码");password.focus();return false;}if (jQuery.trim(password2.value)  === '') {alert("请输入确认密码");password2.focus();}if (password.value !== password2.value) {alert("两次密码不同,请重新输入")password.focus();return false;}// 使用 formData 类来返回 let formData = new FormData();formData.append('username', username.value);formData.append('password', password.value);formData.append('qq', qq);formData.append('sex', sex);formData.append('address', address);let img = document.querySelector("#image");// 获取文件let image = imgFile.files[0];// 这里也可以通过 image 判断 if (img.src === '') {console.log('未上传图片 !!! ');formData.append('filename', null);} else {formData.append('filename', image)}// 通过 ajax 发送请求jQuery.ajax({type: "POST",url: "/user/add",data: formData,processData: false,contentType: false,success: function (result) {if (result != null && result.data.status > 0) {alert('注册成功!');location.href = "login.html";}else {alert('注册失败')}},error : function(){alert("出错了, 请稍后再试!!!")}})}</script></body>
</html>

上传文件使用到的 input 标签, 和 使用到的change 事件 : input 标签 change 事件


效果 :


到此我们的注册功能就完成了 , 下面就来写我们的博客列表页 .

4.3 博客列表页 (功能实现)

4.3.1 左侧框


图一 :


AppConfig 类

package com.example.blog_ssm.config;import com.example.blog_ssm.util.PasswordUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class AppConfig implements WebMvcConfigurer {// 1. 注入拦截器@Autowiredprivate LoginIntercept loginIntercept;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/product/**").addResourceLocations("file:D:/ret/");}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginIntercept).addPathPatterns("/**").excludePathPatterns("/user/login").excludePathPatterns("/user/add").excludePathPatterns("/css/**").excludePathPatterns("/js/**").excludePathPatterns("/imgs/**").excludePathPatterns("/login.html").excludePathPatterns("/add.html").excludePathPatterns("/product/**");}@Beanpublic PasswordUtil passwordUtil() {return new PasswordUtil();}
}


图二 :


后端完成了 ,下面就可以来完成我们的前端 :


图三 :


图四 :


图五 :


图六 :


图七 :

<script>// 这个 ajax 获取登录状态 :jQuery.ajax({type: "GET",url: "/user/getuser",success: function (result) {if (result != null && result.data.status > 0) {let image = document.querySelector("#image1");image.src = "product/" + result.data.data.url;let image2 = document.querySelector("#image2");image2.src = "product/" + result.data.data.url;// 将用户名 换上去let username = document.querySelector("#username");username.innerHTML = result.data.data.username;}}})// 这个 ajax 获取 分类jQuery.ajax({type: "GET",url: "/blog/gettype",success: function (result) {if (result != null && result.data.status > 0) {let number1 = document.querySelector("#number1");let number2 = document.querySelector("#number2");number1.innerHTML = result.data.data[0]number2.innerHTML = result.data.data[1];} else {alert("分类设置失败 !!! ")}},error: function () {alert("出错了, 请稍后再试!!!")}})
</script>


左侧部分 就完成了, 下面就来完成我们的右侧部分 ,这里我们可以写一个分页器

4.3.2 右侧框 (分页功能 + 页面显示)


图一 :

图二 :


图三 :

图四 :

这里不好截图 直接看代码 :

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>博客列表页</title><link rel="stylesheet" href="./css/common.css"><link rel="stylesheet" href="./css/blog_list.css"><script src="./js/jquery.js"></script><link href="css/bootstrap.min.css" rel="stylesheet"></head>
<body><!-- 导航栏 -->
<div class="nav"><img src="./imgs/阳台.png" id="image1"><span class="title">我的博客系统</span><!--    这个标签仅仅用于占位 ,把下面几个a 标签挤到右边--><div class="spacer"></div><a href="#">主页</a><a href="#">写博客</a><a href="#">注销</a>
</div><!-- 页面主体部分 -->
<div class="container"><!--    左侧信息--><div class="container-left"><!--        使用 这个 .card 表示用户信息--><div class="card"><img src="./imgs/girl.png" alt="图片显示失败" id="image2"><!--            用户名--><h3 id="username"></h3><a href="#">Gitee 地址</a><div class='counter'><span>文章</span><span>分类</span></div><div class="counter"><span id="number1">1</span><span id="number2">2</span></div></div></div><!--    右侧信息--><div class="container-right"><!--        &lt;!&ndash;--><!--            表示一篇博客--><!--        &ndash;&gt;--><!--        <div class="blog">--><!--            &lt;!&ndash;--><!--                   博客标题--><!--            &ndash;&gt;--><!--            <div class="title">我的第一篇博客</div>--><!--            &lt;!&ndash;--><!--                发布时间--><!--            &ndash;&gt;--><!--            <div class="data">--><!--                2023-03-02--><!--            </div>--><!--            &lt;!&ndash;--><!--                博客的摘要--><!--            &ndash;&gt;--><!--            <div class="desc">--><!--                从今天起 , 我要认真敲代码--><!--                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sint eaque facilis perferendis! Numquam--><!--                neque voluptatum ab vero expedita possimus fuga eos, illo sapiente delectus quidem natus maiores,--><!--                ipsum impedit rerum?--><!--            </div>--><!--            &lt;!&ndash;--><!--                查看全文按钮--><!--            &ndash;&gt;--><!--            <a href="#">查看全文 &gt;&gt;</a>--><!--        </div>--><div id="page"><nav aria-label="Page navigation"><ul id="all" class="pagination"><li class="active"><a href="javascript:firstPage();">首页</a></li><li><a href="javascript:beforePage();">上一页</a></li><li><a href="javascript:nextPage();">下一页</a></li><li><a href="javascript:lastPage();">末页</a></li><span id="pageinfo" style="font-size: 20px;margin-left: 10px;"></span></ul></nav></div></div>
</div><script>// 这个 ajax 获取登录状态 :jQuery.ajax({type: "GET",url: "/user/getuser",success: function (result) {if (result != null && result.data.status > 0) {let image = document.querySelector("#image1");image.src = "product/" + result.data.data.url;let image2 = document.querySelector("#image2");image2.src = "product/" + result.data.data.url;// 将用户名 换上去let username = document.querySelector("#username");username.innerHTML = result.data.data.username;}}})// 这个 ajax 获取 分类jQuery.ajax({type: "GET",url: "/blog/gettype",success: function (result) {if (result != null && result.data.status > 0) {let number1 = document.querySelector("#number1");let number2 = document.querySelector("#number2");number1.innerHTML = result.data.data[0]number2.innerHTML = result.data.data[1];} else {alert("分类设置失败 !!! ")}},error: function () {alert("出错了, 请稍后再试!!!")}})//  分页功能 :// 1. 当前的页码let pIndex = 1;// 2. 每页显示多少篇博客let pSize = 2;// 3. 总页数let totalPage = 0;// 4. 总条数 (当前所有的博客数目)let totalCount = 0;// 通过 ajax 获取 总页数 和 总条数function getList() {jQuery.ajax({type: "GET",url: "/blog/listbypage",data: {"pIndex": pIndex,"pSize": pSize},success: function (result) {if (result != null && result.data.status > 0) {// 总博客数totalCount = result.data.data.count;// ceil 四舍五入并返回大于等于给定数字的最小整数。totalPage = Math.ceil(parseInt(totalCount) / pSize);let size = result.data.data.list.length;// rightDiv  后面创建的 元素需要挂载 rightDiv 上let rightDiv = document.querySelector(".container-right");for (let i = 0; i < size; i++) {let ret = result.data.data.list[i];let blogDiv = document.createElement('div');// 引入 class 属性blogDiv.className = "blog";// 1. 构造标题let titleDiv = document.createElement('div');titleDiv.innerHTML = ret.title;titleDiv.className = "title";// 将 titleDiv 挂到 blogDiv上blogDiv.appendChild(titleDiv);// 2. 构造 发布时间let dataDiv = document.createElement('div');dataDiv.innerHTML = ret.postTime;dataDiv.className = "data";blogDiv.appendChild(dataDiv);// 3. 构造 文章描述let descDiv = document.createElement('div');descDiv.innerHTML = ret.content;descDiv.className = "desc";blogDiv.appendChild(descDiv);// 4. 构造 查看全文按钮let a = document.createElement('a');a.innerHTML = "查看全文 &gt;&gt;";// 重点 : 这里我们点击查看全文 跳转到 博客详情页 ,这里可以 在 url 里面添加一个 博客id ,// 后面在 博客详情页就可以通过这个 blogId 获取到文章.a.href = "blog_detail.html?blogId=" + ret.blogId;blogDiv.appendChild(a);rightDiv.appendChild(blogDiv);}// 将 分页器 挂载 构造好的数据 最后面 .let page = document.querySelector("#page");rightDiv.appendChild(page);} else {alert("获取失败!!!")}},error: function () {alert("出错了,请稍后在尝试!!!")}})}// getList();// 首页function firstPage() {location.href = "blog_list.html?pIndex=1"}// 上一页function beforePage() {if (pIndex > 1) {pIndex = parseInt(pIndex) - 1;location.href = "blog_list.html?pIndex=" + pIndex;} else {alert("已经是首页了!!!")}}// 下一页function nextPage() {if (pIndex < totalPage) {pIndex = parseInt(pIndex) + 1;location.href = "blog_list.html?pIndex=" + pIndex;} else {alert("已经是末页了!!!")}}// 末页function lastPage() {location.href = "blog_list.html?pIndex=" + totalPage;}// 使用这个 方法来初始话页面function initPage() {// 获取 当前页面的 查询字符串  比如 :?pIndex=2let url = location.search;if (url != '') {// 将 问好 去掉 此时就剩下了 pIndex=2 (假设页数是2)url = url.substring(1);// let kvs = url.split("&");let kvs = url.split("=");// 获取到 跳转后的页数 ,pIndex = kvs[1];}// 通过 getList 方法去获取到数据getList();}// 通过 initPage 方法来 初始化页面 .initPage();</script></body>
</html>

Math.ceil() - JavaScript | MDN (mozilla.org)


页面效果:


上面我们的代码其实还有一个没中不足的地方 , 我们的文章 应该是 后发布的在前面, 而不是 以前发布的在前面 ,这里就来修改一下 ,也非常简单 , 就是给我们的SQL 加一个 排序 (按照时间排序即可)

到此我们的博客列表页的内容就完成了 , 下面就来完成我们的 博客详情页

4.4 博客详情页


这里我们主要实现查看 博客的功能 .


图一 :


图二 :


图三 :


图四 :


图五 :


图六 :


图七 :


前端代码 :

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>博客详情页</title><link rel="stylesheet" href="./css/common.css"><link rel="stylesheet" href="./css/blog_detail.css"><script src="./js/jquery.js"></script><!--    引入 editor.md 的依赖--><link rel="stylesheet" href="./editor.md/css/editormd.min.css"><script src="./editor.md/lib/marked.min.js"></script><script src="./editor.md/lib/prettify.min.js"></script><script src="./editor.md/editormd.min.js"></script></head>
<body><!-- 导航栏 -->
<div class="nav"><img src="./imgs/阳台.png"><span class="title">我的博客系统</span><!--    这个标签仅仅用于占位 ,把下面几个a 标签挤到右边--><div class="spacer"></div><a href="#">主页</a><a href="#">写博客</a><a href="#">注销</a>
</div><!-- 页面主体部分 -->
<div class="container"><!--    左侧信息--><div class="container-left"><!--        使用 这个 .card 表示用户信息--><div class="card"><img src="./imgs/girl.png" alt="图片显示失败"><!--            用户名--><h3>牧</h3><a href="#">Gitee 地址</a><div class='counter'><span>文章</span><span>分类</span></div><div class="counter"><span>1</span><span>2</span></div></div></div><!--    右侧信息--><div class="container-right"><!-- 博客标题 --><h3 class="title" id="title">我的第一篇博客</h3><!-- 博客发布时间--><div class="date">2023-03-02</div><!-- 博客正文 --><div id="content"><!--            <P>--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--            </P>--><!--            <P>--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--            </P>--><!--            <P>--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--                从今天开始我要认真敲代码--><!--            </P>--></div></div></div><script>function getBlogDetail() {jQuery.ajax({type: "GET",// location.search 就是 ?blogId=xurl: "/blog/getblog" + location.search,success: function (result) {if (result != null && result.data.status > 0) {let data = result.data.data;// 1. 构造博客标题let title = document.querySelector("#title");title.innerHTML = data.title;// 2. 构造发布时间let dateDiv = document.querySelector(".date");dateDiv.innerHTML = data.postTime;// 3. 构造正文部分// let content = document.querySelector(".content");//// content.innerHTML = data.content;// 使用 editormd.md 自带的方法 对内容进行渲染editormd.markdownToHTML('content', {markdown: data.content})let userid = data.userid;jQuery.ajax({type: "GET",url: "/user/getuserbyid",data: {"userid": userid,},success: function (result) {if (result != null && result.data.status > 0) {let data = result.data.data;// 通过子类选择器选中 img 元素let img = document.querySelector(".card>img");img.src = "product/" + data.user.url;let title = document.querySelector(".card>h3");title.innerHTML = data.user.username;let spanArr = document.querySelectorAll(".counter>span")spanArr[2].innerHTML = data.blogNumber;spanArr[3].innerHTML = data.type;} else {alert("设置错误!!!")}},error: function () {alert("出错了,请稍后再试!!")}})}}})}getBlogDetail();function getLoginUser() {jQuery.ajax({type: "GET",url: "/user/getuser",success: function (result) {if (result != null && result.data.status > 0) {let img = document.querySelector(".nav>img");img.src = "product/" + result.data.data.url;}}})}getLoginUser();</script></body>
</html>

4.5 写博客功能


图一 :


图二 :


图三 :


图四 :


图五 :


最后完成我们的注销 功能 这个 小项目就完成了 .

4.6 注销功能


图一 :


图二 :


到此 这个 小项目就完成了, 其实这个项目还有很多东西可以加 ,这些大家都可以 自由发挥 .

博客项目- SSM 实现相关推荐

  1. 【Vue+SpringBoot】超详细!一周开发一个SpringBoot + Vue+MybatisPlus+Shiro+JWT+Redis前后端分离个人博客项目!!!【项目完结】

    项目目录 资源准备 前后端分离项目 技术栈 Java后端接口开发 1.前言 2.新建Springboot项目 3.整合mybatis plus 3.统一结果封装 4.整合shiro+jwt,并会话共享 ...

  2. SpringCloud开发个人博客项目(框架搭建)

    1. SpringCloud简介 我们先看看springCloud官网(https://spring.io/projects/spring-cloud#overview)上的介绍: Spring Cl ...

  3. 麦司机博客项目技术选型-Java后端

    麦司机博客项目 博客主页:maisiji.cn/ github地址:github.com/fendoudebb/- Java后端技术选型介绍 SpringBoot: 后端框架 项目主页: spring ...

  4. Django之BBS博客项目

    一.登陆功能(验证码) 1 from geetest importGeetestLib2 from django.contrib importauth3 4 #使用极验滑动验证码的登陆 5 deflo ...

  5. 00-基于Vue的博客项目展示

    目录 0 项目开源地址 1 博客页面展示 1.1 首页 1.2 文章页 2 后台管理页面展示 2.1 登录页面 2.2 新建文章-Markdown编辑器 2.3 新建文章-富文本编辑器 2.4 文章列 ...

  6. 码神之路博客项目构建记录

    个人博客项目 Blog 一.项目搭建(2021.10.6) pom文件导入相关依赖 application配置文件配置 Mybatis Plus配置 跨域问题解决 二.首页配置 首页分页显示文章信息 ...

  7. 基于 abp vNext 和 .NET Core 开发博客项目 - 终结篇之发布项目

    基于 abp vNext 和 .NET Core 开发博客项目 - 终结篇之发布项目 转载于:https://github.com/Meowv/Blog 既然开发完成了,还是拿出来溜溜比较好,本篇是本 ...

  8. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(九)

    基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(九) 转载于:https://github.com/Meowv/Blog 终于要接近尾声了,上一篇基本上将文 ...

  9. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(八)

    基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(八) 转载于:https://github.com/Meowv/Blog 上一篇完成了标签模块和友情链接模块 ...

最新文章

  1. 修改开发环境、工程、文件的字符集
  2. 自定义控件:SlidingMenu,侧边栏,侧滑菜单
  3. python中 __str__和__repr__
  4. 【Kafka】Flink kafka 报错 Failed to send data to Kafka: Failed to allocate memory within the config
  5. SQL查询语句精华总结
  6. SecureCRT连接阿里云ECS服务器,经常掉线的解决方案
  7. 返回三级联动的JSON数据
  8. R中读取文件,找不到路径问题 No such file or directory
  9. cisco路由器OSPF基础配置命令
  10. 读书笔记-人月神话7
  11. URLConnection 传入参数
  12. ubuntu20.05+GTX1050Ti驱动+CUDA10.2+CUDNN8+pytorch最后OK
  13. 品牌连锁店5G/4G无线组网方案
  14. 如何判断用户是否关注微信订阅号,亲测实战步骤
  15. 防止用户将表单重复提交的方法汇总
  16. Monokai主题详细配色
  17. mysql高级笔记_MySQL高级学习笔记
  18. 微信公众平台java版本开发
  19. ModemManager 1.10发布
  20. Latex中调整三线表行间距

热门文章

  1. JavaScript----什么是纯函数
  2. linux如何正则匹配删除一行,shell sed命令匹配替换删除最后第一行字符正则表
  3. 好玩的网站“彩蛋”们(程序员视角)...
  4. 数据结构哈希查找的C语言实现
  5. 【TensorFlow】逻辑回归原理与实现(超详细)
  6. 易经玄学诠释人的一生
  7. 解决“源发行版17需要目标发行版17”的警告问题
  8. 谷歌浏览器导出书签方法
  9. 大数加法(C语言)#includestdio.h #includestdlib.h #includestring.h char A[10005]; char B[10005]; int
  10. 命令行进入中文目录并scp拷贝文件到其他计算机