文章目录
- 1. 项目设计
- 2. 效果展示
- 3. 创建项目并配置文件
- 1.1 创建 Spring 项目
- 1.2 配置文件
- 4. 数据库实现用户和博客管理
- 4.1 设计数据库
- 4.2 使用 MyBatis 操作数据库
- UserMapper.xml
- BlogMapper.xml
- User 实体类 和 Blog 实体类
- UserMapper 接口 和 BlogMapper 接口
- UserService 类 和 BlogService 类
- 5. 前后端交互接口设计
- 6. 导入前端代码
- 7. 实现博客主页
- 7.1 实现后端代码
- 7.2 实现前端代码
- 7.3 测试代码
- 7.4 解决页面内容太多超出当前浏览器
- 7.5 解决页面顺序不是按最新时间排序
- 7.6 解决内容太多, 导致显示的时候占位太多.
- 7.7 再次测试代码
- 8. 实现博客详情页
- 8.1 实现后端代码
- 8.2 实现前端代码
- 8.3 测试代码
- 8.4 这里展示为markdown语法的正文
- 9. 实现博客登录界面
- 9.1 实现后端代码
- 9.2 实现前端代码
- 10. 实现登录判断 --- 拦截器
- 10.1 实现自定义拦截器
- 10.2 将自定义拦截器加入到系统配置
- 11. 实现注册功能
- 11.1 实现后端代码
- 11.2 实现前端代码
- 12. 实现注销功能
- 12.1 实现后端代码
- 13. 实现博客编辑页
- 13.1 实现后端代码
- 13.2 实现前端代码
- 14. 实现博客个人主页
- 14.1 实现后端代码
- 14.2 实现前端代码
- 15. 实现展示用户信息的功能
- 15.1 实现后端代码
- 15.2 实现前端代码
- 16. 实现博客的删除功能
- 16.1 改进代码
- 16.2 实现后端代码
- 17. 实现博客的修改功能
- 17.1 实现后端代码
- 17.2 实现前端代码
1. 项目设计
前端使用 HTML+CSS+Javascript+JQuery
后端使用 Spring MVC+Spring Boot+MyBatis
2. 效果展示
3. 创建项目并配置文件
1.1 创建 Spring 项目
1.2 配置文件
application.properties
配置内容
spring.profiles.active=dev
application-dev.properties
配置内容
spring.datasource.url=jdbc:mysql://localhost:3306/MyBlogSystem?characterEncoding=utf8&useSSL=truespring.datasource.username=rootspring.datasource.password=0000spring.datasource.driver-class-name=com.mysql.cj.jdbc.Drivermybatis.mapper-locations=classpath:mapper*.js") .excludePathPatterns("*.jpg") .excludePathPatterns("*.css") .excludePathPatterns("*.png") .excludePathPatterns("login.html") .excludePathPatterns("register.html") .excludePathPatterns("login") .excludePathPatterns("register"); }}
11. 实现注册功能
这里的交互是 交互9
11.1 实现后端代码
实现一个 类 Register 来接收前端返回来的数据
class Register { public String username; public String password1; public String password2;}
这里后端需要判断当前用户名是否已经被使用.
@RestControllerpublic class RegisterController { @Autowired private UserService userService; @RequestMapping("/register") public Object userRegister(@RequestBody Register register) { HashMap<String,Object> map = new HashMap<>(); User user = userService.selectByName(register.username); if (user != null) { map.put("message","当前用户名已经存在了, 请更换!"); return map; } User user1 = new User(); user1.setUsername(register.username); user1.setPassword(register.password1); userService.addUser(user1); return map; }}
11.2 实现前端代码
要对输入内容去除前后空格,并且判空
let submit = document.querySelector('.button'); submit.onclick = function() { let username = document.querySelector('.user'); let password1 = document.querySelector('.password1'); let password2 = document.querySelector('.password2'); if(username.value.trim() == ""){ alert("请先输入用户名!"); username.focus(); return; } if(password1.value.trim() == ""){ alert('请先输入密码!'); password1.focus(); return; } if(password2.value.trim() == ""){ alert('请再次输入密码!'); password2.focus(); return; } if(password1.value.trim() != password2.value.trim()) { alert('两次输入的密码不同!'); passwrod1.value=""; password2.value=""; return; } $.ajax({ url: "register", method: "POST", data: JSON.stringify({username: username.value.trim(), password1: password1.value.trim(),password2: password2.value.trim()}), contentType: "application/json;charset=utf-8", success: function(data,status){ if(data.message != null){ alert(data.message); username.value=""; password1.value=""; password2.value=""; username.focus(); }else{ location.assign('login.html'); } } }) }
12. 实现注销功能
这里的交互是 交互10
12.1 实现后端代码
因为 注销功能是点击注销的时候, 触发一个logout的url, 然后发送一个请求.
这里只需要实现后端代码既可
@Controllerpublic class LogoutController { @RequestMapping("/logout") public void userLogout(HttpServletRequest request, HttpServletResponse response) throws IOException { HttpSession session = request.getSession(false); // 拦截器的拦截, 所以不可能出现session为空的情况 session.removeAttribute("user"); response.sendRedirect("login.html"); }}
13. 实现博客编辑页
这里的交互是 交互1
13.1 实现后端代码
@RestControllerpublic class EditController { @Autowired private BlogService blogService; @RequestMapping("/edit") public void postBlog(@RequestBody Blog blog, @SessionAttribute(value = "user",required = false)User user){ blog.setPostTime(new Timestamp(System.currentTimeMillis())); blog.setUserId(user.getUserId()); blogService.postBlog(blog); }}
13.2 实现前端代码
let submit = document.querySelector('.publish'); submit.onclick = function() { let title = document.querySelector('.title'); let content = document.querySelector('.content'); if(title.value.trim() == ""){ alert('当前文章标题为空,请输入!'); title.focus(); return; } if(content.value.trim() == ""){ alert('当前文章内容为空,请输入!'); content.focus(); return; } $.ajax({ url: "edit", method: "POST", data: JSON.stringify({title: title.value.trim(), content: content.value.trim()}), contentType: "application/json;charset=utf-8", success: function(data,status) { location.assign('home.html'); } }) }
14. 实现博客个人主页
这里的交互是 交互7
这里的前端页面主要就是主页页面的改进
14.1 实现后端代码
@RestControllerpublic class PersonController { @Autowired private BlogService blogService; @RequestMapping("/person") public List<Blog> getMyBlog(@SessionAttribute(value = "user",required = false)User user) { List<Blog> blogs = blogService.getAllBlogById(user.getUserId()); for (Blog blog : blogs) { if (blog.getContent().length() > 80) { blog.setContent(blog.getContent().substring(0,80) + " ..."); } } return blogs; }}
14.2 实现前端代码
$.ajax({ url: "person", method: "GET", success: function(data,status) { buildBlogs(data); } }) function buildBlogs(blogs){ let rightDiv = document.querySelector('.right'); for(let blog of blogs){ let blogDiv = document.createElement('div'); blogDiv.className = 'article'; // 创建 title let h2 = document.createElement('h2'); h2.className = 'title'; h2.innerHTML = blog.title; blogDiv.appendChild(h2); // 创建 postTime let postTime = document.createElement('span'); postTime.className = 'date'; postTime.innerHTML = DateFormat(blog.postTime); blogDiv.appendChild(postTime); // 创建 content let content = document.createElement('div'); content.className = 'desc'; content.innerHTML = blog.content; blogDiv.appendChild(content); // 创建 详情页的超链接 let detailA = document.createElement('a'); detailA.className = 'more'; detailA.href = 'art.html?blogId=' + blog.blogId; detailA.innerHTML = '查看全文>>'; blogDiv.appendChild(detailA); // 加入到 right 中 rightDiv.appendChild(blogDiv); } } // 把毫秒级时间戳转化成格式化日期 function DateFormat(timeStampMS) { var date = new Date(timeStampMS); var year = date.getFullYear(), month = date.getMonth()+1,//月份是从0开始的 day = date.getDate(), hour = date.getHours(), min = date.getMinutes(), sec = date.getSeconds(); var newTime = year + '-' + (month < 10? '0' + month : month) + '-' + (day < 10? '0' + day : day) + ' ' + (hour < 10? '0' + hour : hour) + ':' + (min < 10? '0' + min : min) + ':' + (sec < 10? '0' + sec : sec); return newTime; }
15. 实现展示用户信息的功能
这里的交互是 交互11
这里需要分情况考虑, 展示个人信息主要是 主页页面, 详情页面, 个人主页页面.
以带不带blogId来区分
15.1 实现后端代码
这里判断了 blogId丢失的情况以及,文章作者丢失情况(数据库表数据被删除的时候会出现这种错误)
@RestControllerpublic class UserController { @Autowired private UserService userService; @Autowired private BlogService blogService; @RequestMapping("/user") public Object getUser(Integer blogId, @SessionAttribute(value = "user",required = false)User user){ if(blogId == null) { return user; }else { HashMap<String,Object> map = new HashMap<>(); Blog blog = blogService.getBlogByBid(blogId); if(blog == null) { map.put("message","不存在当前blogId的文章"); return map; } User author = userService.selectById(blog.getUserId()); if(author == null){ map.put("message","当前文章作者出错"); return map; } return author; } }}
15.2 实现前端代码
详情页的情况:
$.ajax({ url: "user"+location.search, method: "GET", success: function(data,status) { if(data.message == null){ let username = document.querySelector('.name'); username.innerHTML = data.username; }else{ alert(data.message); location.assign('home.html'); } } })
个人主页和主页的情况
$.ajax({ url: "user", method: "GET", success: function(data,status){ let username = document.querySelector('.name'); username.innerHTML = data.username; } })
16. 实现博客的删除功能
这里需要用到 交互2
这里在详情页的时候进行构建, 在Blog实体类中加一项 isAuthor
, 为1的时候就是当前文章就是作者.
前端接收到这个的时候, 进行判断, 如果为1就显示删除的按钮.
16.1 改进代码
16.2 实现后端代码
@Controllerpublic class DeleteController { @Autowired private BlogService blogService; @RequestMapping("/delete") public Object deleteBlog(Integer blogId) { blogService.deleteBlog(blogId); return "/home.html"; }}
17. 实现博客的修改功能
这里的交互是 交互3
和 交互4
交互3是在新的页面进行加载
17.1 实现后端代码
@RestControllerpublic class UpdateController { @Autowired private BlogService blogService; @RequestMapping("/updateLoad") public Object updateLoad(Integer blogId){ HashMap<String, Object> map = new HashMap<>(); if(blogId == null) { map.put("message","blogId丢失!"); return map; } Blog blog = blogService.getBlogByBid(blogId); if(blog == null) { map.put("blog","不存在当前blog的文章!"); return map; } return blog; } @RequestMapping("/update") public Object Update(Integer blogId, @RequestBody Blog blog, @SessionAttribute(value = "user",required = false)User user) { HashMap<String, Object> map = new HashMap<>(); if(blogId == null) { map.put("message","blogId丢失!"); return map; } blog.setBlogId(blogId); blog.setUserId(user.getUserId()); blogService.updateBlog(blog); return map; }}
17.2 实现前端代码
$.ajax({ url: "updateLoad"+location.search, method: "GET", success: function(data,status) { if(data.message == null) { let title = document.querySelector('.title'); title.value=data.title; let content = document.querySelector('.content'); content.value=data.content; }else{ alert(data.message); location.assign('home.html'); } } }) // 初始化编辑器 var editor = editormd("editor", { // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. width: "100%", // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度 height: "calc(100% - 60px)", // 指定 editor.md 依赖的插件路径 path: "editor.md/lib/", // 放到 textarea中 saveHTMLTotextarea: true }); let submit = document.querySelector('.publish'); submit.onclick = function() { let title = document.querySelector('.title'); let content = document.querySelector('.content'); if(title.value.trim() == ""){ alert('当前文章标题为空,请输入!'); title.focus(); return; } if(content.value.trim() == ""){ alert('当前文章内容为空,请输入!'); content.focus(); return; } $.ajax({ url: "update"+location.search, method: "POST", data: JSON.stringify({title: title.value.trim(), content: content.value.trim()}), contentType: "application/json;charset=utf-8", success: function(data,status) { if(data.message == null){ location.assign('home.html'); }else{ alert(data.message); location.assign('home.html'); } } }) }