前言
自己用react+koa实现一个留言板的功能,留言的内容可以是文字、图片、表情、视频、音频。在这里记录和总结。
截图
实现
前台使用了react
和antd
,富文本编辑器使用的是Braft Editor
后台使用的是koa
,后端的上传文件功能参考这里
对于这个项目的留言板的难点我认为主要在富文本编辑器和数据库的设计,对于页面UI渲染和逻辑我认为都不算复杂。
数据库
数据库使用的是mysql
。
留言可以有回复,回复又可以有子回复,所以它是一种树结构的数据。而mysql比较适合存储一些类似表格的扁平化数据,对于树结构的数据不好处理。
我在网上找了很多方法,大部分就是加pid、path字段还有用左右值编码的设计,这类设计要么需要递归、要么就是插入数据会影响很多数据(可以参考这里)。
最后我想了想不需要用树结构去存储留言
我将数据分为两种类型留言
和回复
(都存储在一张表中)
留言数据有留言内容和留言人,pid为-1。
回复数据有回复内容、回复人和被回复人,注意它的pid不是被回复的id,而是在哪条留言底下回复pid就是哪条留言的id,上图中两条回复的pid都是“我是吴彦祖”这条留言的id。
CREATE TABLE `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`type` int(8) DEFAULT NULL COMMENT '0(留言)、1(回复)',
`createTime` bigint(20) DEFAULT NULL COMMENT '创建信息时间',
`content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '信息内容',
`userId` int(11) DEFAULT NULL COMMENT '回复人id',
`userIsAdmin` int(8) DEFAULT NULL COMMENT '回复人是否是管理员',
`userName` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '回复人用户名',
`userAvatar` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '回复人头像',
`targetUserId` int(11) DEFAULT NULL COMMENT '被回复人id',
`targetUserIsAdmin` int(8) DEFAULT NULL COMMENT '被回复人是否是管理员',
`targetUserName` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '被回复人用户名',
`targetUserAvatar` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '被回复人头像',
`pid` int(11) DEFAULT '-1' COMMENT '父id',
`likeNum` int(11) DEFAULT '0' COMMENT '赞的数量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=190 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
我后台查询数据时,先将所有数据查出来,然后再处理数据
首先将留言
数据和回复
数据从查询结果分离出来,然后通过回复数据的pid添加到对应留言下的children属性中。可以去控制台中查看数据
/**
* 获取留言列表
*/
const getMessages = async () => {
const sql = `select * from messages order by createTime DESC` //按时间降序排列
const res = await exec(sql)
//这里可以用sql查两次类型的回复,也可以用js来过滤
//获取留言类型数据,并且给每个数据添加children属性
let list = res.reduce((total, current) => {
if (current.type === 0) {
total.push({
...current,
children: []
})
}
return total
}, [])
//获取回复类型数据
let subList = res.filter(item => item.type === 1)
//将回复类型数据根据pid添加到留言类型数据的children属性中
subList.forEach(item => {
const index = list.findIndex(i => i.id === item.pid)
list[index].children.unshift(item)
})
return new SuccessModel({
data: list
})
}
留言可以发送表情包,但是数据库存储后乱码了,在网上找到解决方法就是设置表的字符集为utf8mb4
,可还是不管用,最后找了很久才发现在node连接数据库时要设置字符集
MYSQL_CONF = {
host: 'localhost',
user: 'root',
password: '1234567890',
port: '3306',
database: 'admin',
charset:'utf8mb4' //字符集一定要写,否则表情包存储不了
}