当前位置: 首页 > 后端技术 > Node.js

使用Taro小程序框架开发一个学习、刷题、论坛、聊天交流的微信小程序

时间:2023-04-03 16:43:12 Node.js

使用Taro小程序框架开发一个微信小程序,用于学习、答疑、论坛、聊天交流Pages等,管理端使用ReactHook+TypeScript开发项目。引言当代大学生课堂缺乏积极性,学习效率低下。作为一名大学生,我深有体会。因此,我们特地开发了这样一款学习微信小程序,帮助同学们学习和巩固知识,同时加入对战PK模块,增强同学们的学习积极性。这是一个微信小程序,为同学们提供在线学习课程、题库练习、考试答题、知识问答、班级签到、资料回顾、成绩分析等功能,希望大家路过,给个star~https://github.com/zhcxk1998/School-Partners由于学术任务繁重,目前开发不完善。目前比较完整的小程序只有习题、课程、论坛、聊天室。管理端也开始开发,现在题库管理已经完成,新增题库,修改题库,登录功能视频演示http://cdn.algbb.cn/School-Pa。..技术选型前端:Taro+微信小程序+Echarts后端:Node.js+MySql+websocket其他:七牛云存储项目功能在线学习课程专题题库练习课程考试答案知识趣味赛课签到系统专业资料查看学生成绩分析管理终端登录注册题库管理与操作截图小程序1.首页2.个人中心3.课程详情4.习题5.学习交流群6.聊天室7.课程列表8.习题列表9.排行榜清单10.论坛11.活动任务管理终端1.登录界面2.题库管理3.修改题库项目分析项目采用前后端分离技术。前端采用Taro微信小程序框架。因为我比较喜欢React,所以我用的是Taro,一个类似React的语法框架。后端使用Node.js,koa2框架。聊天室页面使用websocket连接。今天先说说聊天室用到的小技巧(不是)首先,我们后台数据库用的是mysql,建了一张聊天记录表(新手勿喷)~)1.在数据库的后端部分,我们将所有的聊天记录存储在一张表中,方便管理,因为我们有多个聊天组,我们如何区分这些不同的聊天组呢?答案是通过room_name来区分。获取聊天记录时,直接查询群名即可,这样就不用开很多表,将不同的群聊记录存储在不同的表中!同时,由于我们需要在聊天记录中存储emoji等信息,因此需要将数据库的字符集调整为utf8mb4——UTF-8Unicode,排序规则选择utf8mb4_unicode_ci。这个可以通过百度或者navicat来设置。然后我们设置数据表和字段类型为utf8mb4,方便后端存储emoji信息和处理聊天记录。router.get('/chatlog/:to',async(ctx)=>{constto=ctx.params.toconstresponse=[]constres=awaitquery(`SELECT*FROMchatlogWHEREroom_name='${to}'ORDERBYcurrent_timeDESC`);res.map((item,index)=>{const{room_name,user_name,user_avatar,current_time,message}=itemresponse[index]={to:room_name,userName:user_name,userAvatar:user_avatar,currentTime:formatTime(current_time),message,messageId:`msg${current_time}${Math.ceil(Math.random()*100)}`}})ctx.response.body=parse(response)})这是获取指定群聊的后台接口,to代表群名,使用get方法可以获取指定群聊的聊天记录!继续说我们如何向所有连接到聊天室的网友发送消息。这里我们使用的是broadcast方式,区别于socket.io中已经封装好的broadcast方式。小程序规定只能使用websockets,所以我粗略的封装了广播(非常难看的代码)=socketMessageonlineUserSocket[socketId]=wsonlineUserInfo[socketId]={userName,userAvatar}ws.socketId=socketId}//广播消息constbroadcast=(message)=>{const{from,userName}=messageObject.values(onlineUserSocket).forEach((socket)=>{socket.send(JSON.stringify({...message,isMyself:userName===onlineUserInfo[socket.socketId].userName}))})}当我们再次登录时,我们会将前端传来的消息存储到对象及其socket对象中,然后在广播的时候,可以遍历所有的socket对象,为所有在线用户广播消息。其中,isMyself代表你是否是你自己。比如我发送消息时,自己的socket对象收到广播时为true别人的是假的,这个是为了方便区分自己的聊天消息和接收方的聊天消息2.前端部分接下来说一下前端的聊天室部分handleSocketMessage():void{const{socketTask}=thissocketTask.onMessage(async({data})=>{constmessageInfo:ReceiveMessageInfo=JSON.parse(data)const{to,messageId,isMyself,userName,userAvatar,currentTime,message}=messageInfoconsttime:string=formatTime(currentTime)this.messageList[to].push({...messageInfo,currentTime:time})/*设置群组的最新消息*/this.contactsList.filter(contacts=>contacts.contactsId===to)[0].latestMessage={userName,message,currentTime:time}this.scrollViewId=isMyself?messageId:''awaitTaro.request({url:'http://localhost:3000/chatlog',method:'PUT',data:{to,userName,userAvatar,currentTime,message,}})})}我们先接受消息,然后用指定的群名更新聊天群的聊天记录,然后使用PUT方法访问接口将聊天记录添加到数据中图书馆可以看到我们的聊天记录分为左右两部分,右边是我们自己发的消息。我们可以通过一个简单的弹性布局来实现这一点。//这里是覆盖默认样式显示自己的样式messages.myself{justify-content:flex-end;.avatar{订单:1;}.info{显示:flex;弹性方向:列;对齐项目:flex-end;.header{证明内容:flex-end;:1;右边距:0!重要;左边距:.5em;}}.content{颜色:#333!important;边框:#e7e7e71px实心;背景:#fff!重要;框阴影:08px20px-8px#d7d7d7;}}}//下面是默认样式,也就是左边的样式。消息包装{显示:flex;边距:20px0;.avatar{宽度:14vw;身高:14vw;边距:10px;边界-半径:50%;背景图像:线性渐变(120deg,#a1c4fd0%,#c2e9fb100%);}.info{.header{显示:flex;对齐项目:居中;最大宽度:40vw;填充:10px0;颜色:#666;字体大小:.8em;.username{溢出:隐藏;文本-溢出:省略号;空白:nowrap;最大宽度:40vw;右边距:.5em;颜色:#555;字体大小:1.2em;字体粗细:粗体;}}.content{显示:内联块;最大宽度:60vw;填充:10px20px;颜色:#fff;分词:break-all;边界半径:20px;背景:#66a6ff;甚至handleSocketClose():void{const{socketTask}=thissocketTask.onClose((msg)=>{this.socketTask=nullthis.socketReconnect()console.log('onClose:',msg)})}handleSocketError():void{const{socketTask}=thissocketTask.onError(()=>{this.socketTask=nullthis.socketReconnect()console.log('Error!')})}先听下websocket关闭或者异常的情况,调用重连方法,并清除socketTask对象,紧接着是重连方法socketConnect(){//生成一个随机唯一的socketIdthis.generateSocketId()/*使用then方法正确触发onOpen方法,暂时不知道为什么*/太郎.connectSocket({url:'ws://localhost:3000',}).then(task=>{this.socketTask=taskthis.handleSocketOpen()this.handleSocketMessage()this.handleSocketClose()this.handleSocketError()})}socketReconnect():void{this.isReconnected=trueclearTimeout(this.timer)/*3s延迟重连,减轻压力*/this.timer=setTimeout(()=>{this.socketConnect()},3000)}我们每三秒调用一次socket连接方法,重新设置socketId,和socketTask,重新监听各种方法这里有个奇怪的地方,就是Taro的connectSocket方法,不能使用async/await方法获取socketTask,也就是说constsocketTask=awaitTaro.connectSocket({...})不能获取socketTask,只有通过then的方法才能得到。本人拙见,暂时不知道如何解决这个问题。。。聊天界面有个emoji表情按钮,点进去会弹出emoji栏。实现起来比较简单。首先,定义一个变量emojiOpened来判断用户是否点击了emoji按钮,如果是,则在输入框添加一个类名来控制弹出样式同时在scss.emoji-open中设置弹出样式{transform:translateY(-30vh);转换:所有.2s轻松;}...&-input-container{位置:固定;左:0;底部:-30vh;宽度:100vw;高度:40vh;背景:#fff;z-索引:1;过渡:所有.2s轻松;...}具体后续请关注我的github,我会持续更新项目!戳星星~