# SIK-blog
**Repository Path**: beganing/sik-blog
## Basic Information
- **Project Name**: SIK-blog
- **Description**: SIK个人博客项目!
- **Primary Language**: JavaScript
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-10-18
- **Last Updated**: 2022-12-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 一.需求分析
## 1.主业务流程

## 2.角色功能分析

## 3.原型图
### 3.1 前台
#### 1)首页
封面+文章列表页

#### 2)文章详情页

### 3.2 后台

## 4.需求点
### 4.1 前台
#### 1)首页
> 导航栏
- 默认进入首页页面
- 点击`搜索`、`留言`可弹框或进入对应功能页面
- 点击`登录`,可进入用户登录页面
> 博文信息
- 默认加载最新发布的几篇文章
- 按时间倒序显示博文
- 点击文章,进入文章详情页面,浏览量增加
> 右侧边栏
- 展示平台信息,包括文章量、分类数
- 日历
> 底部
- 页面右下角按钮,点击回到顶部
#### 2)文章详情页
> 导航栏
- 同首页一致
> 头部
- 展示文章标题、发表时间、更新时间、阅读量、评论数等
> 主体
- 展示文章详细内容
> 右侧边栏
- 展示最新的文章
> 底部
- 查看此文章的所有评论
- 可发布对此文章的评论
### 4.2 后台
#### 1)登录页面
- 输入`用户名`和`密码`后进行验证
- 点击`登录`即进入个人页面
- 点击`退出登录`即可返回登录页面
#### 2)首页
- e-charts 饼图:分类名称和分类文章数量统计图
- e-charts 柱状图:时间和文章数量统计图
#### 3)用户管理
- 点击`编辑`按钮,可以切换`用户/管理员`的身份、
- 可以切换用户状态(是否限制用户登录)
#### 4)文章管理
> 文章列表
- 显示文章列表
- 点击新增文章按钮即可新增文章
- 点击删除选中文章按钮即可删除被选定的文章
- 点击编辑按钮即可编辑文章
- 点击删除按钮即可删除文章
- 选择分类可以获取分类下的文章
> 新增文章
- 新增文章
- 上传图片
- 文章内容也可以加入网络图片
#### 5)分类管理
- 显示分类列表
- 点击添加分类按钮即可添加分类
- 点击编辑按钮即可修改分类
- 点击删除按钮即可删除分类
- 输入关键字,可实现模糊搜索
- 选中多个数据,可实现批量删除
#### 6)留言管理
- 显示留言列表
- 点击删除留言即可删除留言
# 二.技术分析
## 1.技术选型
平台
- 操作系统: windows 10
- 开发平台: VSCode
- 测试平台: Chrome/FireFox
- 文档平台: 语雀
框架
- 前端 Vue 2.7
- 后端 Express
技术栈
- 前端 H5+css+stylus+vue2+axios+ElementUI+Echarts
- 后端 express+mysql+JWT(token)+multer
## 2.数据库设计
### 1.创建数据库
> 创建 db_blog 数据库
> blog_user 表
| 字段名 | 类型 | 属性 | 备注 |
| ----------- | --------- | ----------------------------------------- | -------------------------- |
| id | int | 主键,自增,非空,无符号 | 用户 id |
| username | varchar | 非空, 默认值'' | 用户名 |
| password | varchar | 非空, 默认值'' | 用户密码 |
| nickname | varchar | 非空, 默认值'' | 用户昵称 |
| avatar | text | 默认 null | 用户头像 |
| role | varchar | 非空, 默认值 '用户' | 用户角色 |
| ip | varchar | 非空, 默认值'' | 登录 ip |
| address | varchar | 非空, 默认值'' | 登录地址 |
| createdTime | timestamp | 默认'CURRENT_TIMESTAMP' | 创建时间 |
| loginTime | timestamp | 默认'CURRENT_TIMESTAMP'根据当前时间戳更新 | 登录时间 |
| state | int | 非空, 默认值为 0 | 状态,0 表示正常,1 表示禁用 |
> blog_article 文章表
| 字段名 | 类型 | 属性 | 备注 |
| ------------------- | --------- | ----------------------------------------- | ---------------- |
| id | int | 主键,自增,非空,无符号 | 文章 ID |
| article_title | varchar | 非空, 默认值'' | 文章标题 |
| article_content | longtext | 非空, 默认值'' | 文章正文 |
| article_createdTime | timestamp | 默认'CURRENT_TIMESTAMP' | 文章创建时间 |
| article_updatedTime | timestamp | 默认'CURRENT_TIMESTAMP'根据当前时间戳更新 | 文章更新时间 |
| article_deleteTime | timestamp | 默认 null | 文章删除时间 |
| article_views | bigint | 默认值'0' | 文章浏览量 |
| category_id | int | 设为外键,与 blog_sort 表中的主键相连 | 文章分类 ID |
| article_pic | text | 非空, | 文章封面图片路径 |
| article_praise | int | 默认值'0' | 文章点赞量 |
> blog_sort 分类表
| 字段名 | 类型 | 属性 | 备注 |
| ---------------- | --------- | ----------------------------------------- | -------------------------- |
| id | int | 主键,自增,非空,无符号 | 分类 ID |
| name | varchar | 非空, 默认值'' | 分类名称 |
| sort_createdTime | timestamp | 默认'CURRENT_TIMESTAMP' | 分类创建时间 |
| sort_updatedTime | timestamp | 默认'CURRENT_TIMESTAMP'根据当前时间戳更新 | 分类更新时间 |
| sort_deletedTime | timestamp | 默认 null | 分类删除时间 |
| sort_views | int | 默认为 0 | 该分类所有文章的浏览量总和 |
| sort_articles | int | 默认为 0 | 该分类包含的文章数量 |
> blog_comment 留言表
| 字段名 | 类型 | 属性 | 备注 |
| --------------- | --------- | ----------------------------------------- | ------------ |
| id | int | 主键,自增,非空,无符号 | 留言 id |
| comment | text | 默认 null | 留言内容 |
| com_article_id | int | 设为外键,与 blog_article 表中的主键相连 | 文章留言 id |
| com_user_id | int | 设为外键,与 blog_user 表中的主键相连 | 用户留言 id |
| com_createdTime | timestamp | 默认'CURRENT_TIMESTAMP' | 留言创建时间 |
| com_updatedTime | timestamp | 默认'CURRENT_TIMESTAMP'根据当前时间戳更新 | 留言更新时间 |
## 3. 接口设计
### 1) 接口说明
- 接口基准地址`baseURL`:[http://127.0.0.1:3000](http://127.0.0.1:3000)
- 服务端已开启`cors`跨域支持
- 使用 code 标识状态(**0:成功.1:失败**)
- 使用 msg 代表响应信息
- 使用 result 代表响应结果(数据)
- 数据返回格式:JSON 对象
### 2) 用户模块
**/users**
| 请求路径 | 请求方式 | 参数 | 响应成功 | 备注 |
| ---------------------- | -------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------ |
| /users/login | post | `{ username, password, ip, address, }` | `{ code: 0, msg: "登录成功", result: { id, username, password:'', tokenStr } }` | 登录接口 |
| /users/reguser | post | `{ username, password, nickname, }` | `{ code: 0, msg: "注册成功", result: {} }` | 注册接口 |
| /users/updatePwd/:id | put | `{ id, oldPwd, newPwd, }` | `{ code: 0, msg: "更新密码成功", result: {} }` | 修改密码 |
| /users/avatar/:id | get | `{ id,//用户id }` | `{ code: 0, msg: "获取成功", result: { id, username, avatar, } }` | 获取头像 |
| /users/uploadPic/:id | put | `{ id, formData, // 表单对象 }` | `{ code: 0, msg: "更新头像成功", result: { id, username, avatar, } }` | 修改头像 |
| /users/getUsers | get | 无 | `{ code: 0, msg: "获取成功", result: { id, //用户id username, //用户名 nickname,//用户昵称 avater,//头像 role,//用户角色 ip, //登录ip address,//登录地址 createdTime,//创建时间 loginTime//登录时间 } }` | 获取所有用户 |
| /users/updateUsers/:id | put | `{ id, state, role, }` | `{ code: 0, msg: "修改信息成功", result: {} }` | 修改用户信息 |
| /users/deleted/:id | delete | `{ id, }` | `{ code: 0, msg: "删除用户成功", result: {}, }` | 删除用户 |
### 3) 留言模块
**/comments**
| 请求路径 | 请求方式 | 参数 | 响应成功 | 备注 |
| -------------------- | -------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| /comments/ | get | `{ article_id }` | `{ code: 0, msg: "获取留言成功", result: { id, //用户id nickname, //用户昵称 avatar,//用户头像 state// 用户状态 com_createdTime, com_ip, com_address, com_createdTime } }` | 获取留言 |
| /comments/add/:id | post | `{ article_id, user_id }` | `{ code: 0, msg: "添加留言成功", result: {}, }` | 添加留言 |
| /comments/delete/:id | delete | `{ id,//留言id }` | `{ code: 0, msg: "删除留言成功", result: {}, }` | 删除留言 |
### 4) 文章模块
**/article**
| 请求路径 | 请求方式 | 参数 | 响应成功 | 备注 |
| ------------------------- | -------- | ---- | ----------------------------------------------------------------------------------- | ---------------- |
| /article/getArticle | get | 无 | `{ code: 0, msg: "获取文章列表成功", result: { total, rows } }` | 获取所有文章信息 |
| /article/getSingleArt/:id | get | id | `{ code: 0, msg: '获取指定文章成功', result: { ...rows[0] } }` | 获取单个文章信息 |
| /article/getRecommend/:id | get | id | `{ code: 0, msg: '获取文章成功', result: { result } }` | 获取相关推荐 |
| /article/getHotArticle | get | 无 | `{ code: 0, msg: '获取文章成功', result: { result } }` | 获取最热文章 |
| /article/uploadPic/:id | post | id | `{ code: 0, msg: '修改文章成功', result: { result[0] }, }` | 添加文章图片 |
| /article/add | post | | `{ code: 0, msg: '{ code: 0, msg: '添加文章成功', result: { result1[0] },` | 添加文章 |
| /article/update/:id | put | id | `{ code: 0, msg: '修改文章成功', result: { "" }` | 修改文章 |
| /article/deleted | delete | 无 | `{ code: 0, msg: '删除文章成功', result: { "" }` | 删除文章 |
### 5) 分类模块
**/admin/category**
| 请求路径 | 请求方式 | 参数 | 响应成功 | 备注 |
| ------------------- | -------- | ----------------- | ----------------------------------------------------------------------------------------------- | ------------ |
| /admin/category | get | 无 | `{ code: 0, message: "获取所有分类成功", result: { data, }, }` | 获取全部分类 |
| /admin/category/:id | get | { id } | `{ code: 0, message: "获取单个分类成功", result: data, }` | 获取单个分类 |
| /admin/category | post | { sort_name } | `{ code: 0, message: "添加分类成功", result: { id: insertId, sort_name, }, }` | 添加分类 |
| /admin/category/:id | put | { id, sort_name } | `{ code: 0, message: "修改分类成功", result: { id, }, }` | 修改分类 |
| /admin/category/:id | delete | { id } | `{ code: 0, message: "删除分类成功", result: { id, }, }` | 删除分类 |
# 三.WBS
团队三人,纵向分配任务,让每位成员都可以体验前后端完整流程
[项目进度表](https://zhuanguser.yuque.com/yuk9kl/or172b/rlmz38?view=doc_embed)
# 四.项目总结
## 1.前台
> 问题:在文章详情页点击推荐文章,虽然跳转到了该文章详情,但是显示的还是上一个文章,刷新即可正常
由于 vue `router-view`会缓存,第二次进入到文章详情,不会走 mounted 生命周期
解决:
**第一种**: 使用生命周期函数 :`activated`
```javascript
activated() {
this.getArticleList()
},
```
**第二种**: 给 router-view 添加唯一`key`
> 增加一个不同:key 值,这样 vue 就会识别这是不同的了
```javascript
computed: {
key() {
return this.$route.path + Math.random();
}
},
```
## 2.中后台
### 2.1 token 的使用

> JWT(英文全称:JSON Web Token)主要用于跨域认证解决方案。包含三部分如下:
- Header(头部)、Payload(有效荷载)、Signature(签名) (三个部分)
- Payload 部分才是真正的用户信息,是用户信息经过加密之后生成的字符串
- Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。
客户端收到服务器返回的 JWT 之后,通常会将它储存在 `localStorage `或 `sessionStorage中`。 此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中。
- axios 响应拦截器中成功则存 token,返回 401 则清除`localStorage`中的 token,并定位到登录页面
- axios 请求拦截器中,拿到`localStorage`中的 token,挂载到请求头的 Authorization 字段,带到后端
### 2.2 Dialog 对话框内,回车、数据回显引起的表单校验问题
> 背景
- 将`添加`和`编辑`功能在同一个`Dialog`对话框显示
- 给`Dialog`对话框内的`input`输入框添加了回车事件,实现回车添加或修改
- 监听对话框的`close`事件,关闭对话框时重置表单`this.$refs.addFormRef.resetFields()`
> 现象
1. 打开`新增`对话框,输入内容,按下`回车`,添加成功
2. 接着打开刚才新增内容对应的的`编辑`对话框,数据回显成功,但是输入框下提示“请输入分类名”

> 原因

可以看出,`resetFields`方法只对添加有效,但对于添加和编辑同一个表单就不行了
> 解决方法
此时,我们还需要在**对话框打开时**,对表单的校验结果进行移除
在打开修改对话框对应的方法 handleChange 中,添加如下代码:
```vue
this.$nextTick(() => { this.$refs.addFormRef.clearValidate(); });
```
> 为什么不可以直接写`this.$refs.addFormRef.clearValidate()`?
Vue 官方对 ref 的重要说明:
- ref 本身是作为渲染结果被创建的,在初始渲染的时候还不能访问——因为此时还不存在
- $refs 也不是响应式的,因此不应该在模板中做数据绑定
所以通过`nextTick(()=>{})`,等到页面完全渲染完毕之后执行延迟回调
### 2.3 搜索功能异常
> 现象
- 只能在第一页输入搜索关键字才可以搜索到,其余页面不可以
- 第一页搜索完成之后,删除搜索关键字,此时页面渲染不正常,页面内容与页数不匹配
> 原因(不太理解)
- 当前页面`page`变量,绑定多个`prop`
> 解决方法
修改分页器的属性,当前页面`current-page`,加上`.sync`
`:current-page.sync="page"`
```vue
```
# 五.心得体会
## 1. 项目实施前
完善的需求分析十分必要(特别是接口的设计和数据库设计),既是提前准备的过程,也是理清思路的过程,从而更好的把握项目的易、难点,有利于对整个项目的进度以及分工的安排,事半功倍
## 2. 实施过程中
1. 遇到较难的功能,可以适当放放,先完成有思路的功能,以免耽误进度
2. 当在调试时出现 bug,但又没有飘红或报错时,多半是逻辑的问题,首先要找到可能出现的位置,通过'打断点','在控制台打印','查看 network'等相结合,从而找清错误的位置
3. 当发现对之前的知识遗忘了的时候,千万不能懒,不要怕耽误项目的进度,一定要及时翻笔记,及时巩固(不会就要看)
## 3. 合作
项目的完成是否成功,组员之间的合作十分重要,当遇到问题时及时沟通,相互帮助
# 六.项目展示
## 1.前台




## 2.后台




