使用koa+mysql写一个简易论坛(完)
一、本期目标
给本系列文章画上一个相对圆满的句号
- Admin —- 帖子管理的无刷新解决
- Admin —- 客户管理
- Admin —- 小黑屋(相当于回收站)
- User —- 简单的搜索功能
- User —- 留言功能
- User —- 个人信息设置
- User —- 更换头像
- 扩展
二、帖子管理(无刷新) —- Admin
这个其实很简单,说究竟也就是Fetch的使用而已
1、思路
- 点击对应按钮,使用fetch发送数据到后台
- 后台通过该数据进行相关操作,判断要返回什么到前台
- 前台再根据后台返回的数据来进行页面解决
2、实现
- 对页面标签进行解决:将<a>换成<button>,删除href属性,只保留type和class,而后新添加data-id属性,其值设置为topic的id,我们后面要根据data-id的值来进行相关操作 =>
<button type="button" data-id="<%= topic.id %>" ><p>64px*64px</p>'); $('.cropped').append('<img id="img2" src="'+img+'" align="absmiddle" style="width:128px;margin-top:4px;border-radius:128px;box-shadow:0px 0px 12px #7E7E7E;"><p>128px*128px</p>'); $('.cropped').append('<img id="img3" src="'+img+'" align="absmiddle" style="width:180px;margin-top:4px;border-radius:180px;box-shadow:0px 0px 12px #7E7E7E;"><p>180px*180px</p>'); var img1Src = document.getElementById('img3').src; // 获取input, 隐式上传数据到后台 var upload_base = document.getElementById('upload_base'); // 将img的src赋给type为hidden的input,以便将其上传到后台 upload_base.value = img1Src; }); });
后台解决
router.post("/settings/profile/changeImage", upload.single('image'), async (ctx) => { // 获取前台上传过来的base64数据 const value = ctx.req.body.upload_base; // 使用正则表达式截取有用的信息 const base64Data = value.replace(/^data:image\/\w+;base64,/, ""); // 使用Buffer.from()函数解决数据 const dataBuffer = Buffer.from(base64Data, 'base64'); // 获取客户的id const userId = ctx.session.user.id; // 定义客户新头像的存储路径及名称 const newUserPicturePath = `public/uploads/${userId}.png`; // 写文件,保存头像文件 fs.writeFile (newUserPicturePath, dataBuffer, function(err) { if (err) { console.log(err); }else{ console.log("保存成功!"); if(ctx.req.file) { // 删除base64的头像文件,没有这一部分的话,每一次换头像都会多一个你选取的图片文件 const filename = ctx.req.file.filename; const savedFilePath = `public/uploads/${filename}`; fs.unlinkSync(savedFilePath); } } }); // 获取客户id const id = ctx.session.user.id; const data=[newUserPicturePath, id]; // 升级客户头像地址 const resetPicturePromise = editUser.resetPicture(data); await resetPicturePromise; //新的头像路径保存完成,但是要升级session才能使头像立即生效 // 获取客户信息 const getUserInformationPromise = editUser.getUserById(id); const userArray = await getUserInformationPromise; const user = userArray[0]; // 升级session ctx.session.user = user; // 在客户升级头像完成后,我们要将数据库中该客户发表的所有话题的topic_image_path // 换成该客户当前头像的路径,即 --> newUserPicturePath // 根据数据表topic中的每条topic的post_man字段我们可以得到发表该话题的客户,由于客户名唯一 // 其实在这里客户名就是当前客户的username属性,由于session、升级,所以我们也用新的, // 即ctx.session.user.username. 其实它 === user.username const userName = ctx.session.user.username; const data2 = [newUserPicturePath, userName] const updateTopicImagePathByPostManPromise = editTopic.updateTopicPic(data2); await updateTopicImagePathByPostManPromise; // 同上,客户更换头像,该客户留言前的图片也应该换 const data3 = [newUserPicturePath, userName]; const updateMessagePicPromise = editMessage.updateMessagePic(data3); await updateMessagePicPromise; ctx.redirect('/userHome');});
七、User —- 留言功能
一个论坛,当然得有留言评论功能
这里同样使用fetch
思路
1、客户点击按钮
2、获取文本域中客户输入的文本text
3、获取button的data-id的值id,用id作为判断帖子的依据,id为帖子的id
4、使用fetch将text、id上传到后台
5、后台根据这些数据来进行相应的解决:将留言内容存储到数据库中,并指定其属于哪个帖子
6、后台返回数据到前台,前台再进行相应解决:将留言展现到页面,并为新的留言增加样式,删除之前留言的特殊样式
实现代码及解析
// 获取留言的数量,即id="messages"的ul中li的个数 const length = document.getElementById("messages").getElementsByTagName("li").length; $("#btn").click(function(event){ // 获取id , 此id === topic的id let id = $("#btn").attr("data-id"); // 获取客户输入的留言内容 let userMessage = $("#userMessage").val(); const url = '/showTopics/all/' + id; fetch(url, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, body: JSON.stringify({ id: id, message: userMessage, }) }) .then(function(response) { return response.json(); }) .then(function(myJson) { // 假如有留言 if(length !== 0) { // 设置背景颜色为空 document.getElementsByTagName("li")[6].style.background = ""; // 移除字体样式 document.getElementsByTagName("li")[6].classList.remove("text-danger", "font-weight-bold"); } // 新定义一个标签li,用来存储新的留言 const newMessageLi = ` <li class="list-group-item text-danger font-weight-bold" style="background:greenyellow"> <a class="user_avatar pull-left" href="/userHome" style="width:28px;height:28px"> <img src="../../${myJson[3]}" title="${myJson[2]}" style="width:28px;height:28px"> </a> ${myJson[1]} </li> `; // 将留言加入到页面中 $("#messages").prepend(newMessageLi); }); // 使留言的数量 ++ document.getElementById("num_msg").innerHTML ++; // 清空文本域 document.getElementById("userMessage").value = ''; });
八、User —- 简单的搜索功能
思路
1、获取客户在输入框中输入的字符串string
2、获取数据库中所有useful=1的帖子
3、定义一个数组来保存的结果resultArray
4、遍历topic的title,使用indexOf找出title中包含string的帖子topic,将其存入resultArray中
5、而后将结果string与result传输到前台,前台再进行解决
后台
router.post("/allTopic/results", async (ctx) => { // 需求: 拿到客户在搜索框中输入的字符串,将其与Topic表中所有topic的title进行比照, // 有相等的就展现出来,没有则提醒客户说:未找到符合条件的内容,搜索的显示页面可以是另外一个页面 // 拿到客户在搜索框中输入的字符串 const userInputString = ctx.request.body.user_input_string; const listAllTopicFromBBSPromise = editTopic.listAllTopic(); const allTopic = await listAllTopicFromBBSPromise; // 定义一个结果数组,用来存储找到的结果 let resultArray = []; // 将符合条件的增加到results里面 for(let i = 0; i < allTopic.length; i++) { if(allTopic[i].title.indexOf(userInputString) !== -1) { resultArray.push(allTopic[i]); } } const user = ctx.session.user; await ctx.render("/topics/show_results", { user: user, resultArray: resultArray, userInputString: userInputString });});
前台
<% if (resultArray.length === 0) { %> <li class="list-group-item list-group-item-action cell form-inline" style="font-size: 24px"> <h4> 抱歉,找不到和您查询的“ <span class="text-danger"> <%= userInputString %> </span> ”相符的内容或者信息。</h4> <h5> 建议: </h5> <p> ? 请检查输入字词有无错误。 </p> <p> ? 请尝试其余查询词。 </p> <p> ? 请改用较常见的字词。 </p> </li><% } %><% resultArray.forEach(topic => { %> <li class="list-group-item list-group-item-action cell form-inline" style="font-size: 24px"> <a class="user_avatar pull-left" href="/userHome" style="width:28px;height:28px"> <img src="../<%= topic.topic_image_path %>" title="<%= topic.post_man %>" style="width:28px;height:28px"> </a> <span class="text-center bg-primary mr-1"> <%= topic.board_name%> </span> <a class="topic-link text-lg-center" href="/showTopics/all/<%= topic.id %>">话题:<%= topic.title %></a> </li><% })%>
未找到
image.png
九、扩展
1、 帖子后面显示留言数量
实现方法
在帖子标题后新添加一个span,用来展现留言数msg_num
为了方便一点,在topic表新添加字段msg_num,而后在留言的时候升级此字段
实现
image.png
2、客户中心 —- 上次发帖内容可点击跳转到该帖子页面
- 实现方式
将topic的标题用a标签包裹就可,而后设置好href
3、客户中心 —- 上次留言内容可点击跳转到对应的帖子页面,并将客户的上次留言设置为特殊样式
- 新添加留言的时候为其设置好样式,并取消上个留言的特殊样式,代码前面贴出来了,源码里也有
- 在客户中心将留言内容包裹在a标签里,设置好href,使链接到对应的topic页面
- 其实还可以加个页面定位功能,例如点击留言内容,跳转到该页面,而后使留言位于浏览器的中间,不过我没做
4、帖子的所属子论坛显示
- 在表topic中新添加字段board_name,存储对应的子论坛名字,在展现页面的时候将其使用span包裹并渲染出来就可,样式自定 =>
<span class="put_top"><%= topic.board_name %></span>
?? 由于置顶与精华并非子论坛,所以要特殊解决 =>
<span class="put_top">精华</span><span class="put_top">置顶</span>
5、帖子的发表人头像
- 跟所属子论坛差不多,定义一个新的字段,存储头像的路径,而后再渲染出来就可
- PS: 其实这一类问题并不应该这样解决,而是应该使用mysql的关联表查询,大家可以试试,由于现在这种解决方式有点low
6、帖子展现优先级
这个难道是不难,但是在展现页面的时候会显得有点麻烦,由于要用模版语法
1、优先级 置顶 > 精华 > 普通
2、当某个帖子既是精华贴又被置顶,则显示置顶
PS:具体代码就不贴了,有兴趣的看源码吧
image.png
十、结束语
1、概括
此系列文章共计5篇文章,总用时5个星期
2、感想
原本以为写这个很简单,结果完全不是那么回事,我大概预计了下平均每篇文章用时4.5小时,甚至还不止。
期间我有怀疑过写这个有没有意义,想过能否要放弃写这个,毕竟花的时间太长了,好处说是可以梳理自己的知识体系,只不过并没有什么特别大的效果,懂的仍旧懂,印象的深刻程度也没怎样变。
但作为一个有始有终的人,是坚决不会做“太监”的。经过少量思考、搜索、讯问之后,我觉得文章好处一定是有的,只不过短期内不是很显著而已,就跟运动一样,坚持下去,自然会收获。
第一次写这种文章,感觉自己的文笔不咋地、条理不是很清楚、周密性可能也不是很好,还会遗漏不少东西、写点错别字什么的,敬请各位读者大大见谅吧!
假如诸位有什么疑惑或者者建议什么的欢迎留言,或者者联络我也可以,万分感谢!
附:
1、分期项目地址
ShyGodB/Forum-Code-Synchronize-
2、完整项目地址
ShyGodB/BBS-by-Koa-Mysql
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 使用koa+mysql写一个简易论坛(完)