前言
移动端演示 http://8.146.211.120:8081/#/
前面的文章我们主要完成了笔记的点赞和收藏及留言功能 今天我们讲解点赞关注 im 聊天功能
关注
我们需要有一个关注的操作 这里我们复用个人中心页面
按钮会有三种形式 关注 取消关注 互相关注三种样式
<view class="gui-flex gui-align-items-center gui-justify-content-center" >
<button
v-show="author.isFollow"
@tap="cancelfollowAuthor"
type="default"
class="gui-button-mini xhs-border-radius50 xhs-border-white"
style="width:150rpx;margin-right: 20rpx;background: transparent;backdrop-filter: blur(10px);background-color: rgba(255, 255, 255, 0.1);">
<text class="gui-color-white gui-icons">取消关注</text>
</button>
<button
v-show="!author.isFollow&&author.isFollowMe"
@tap="followAuthor"
type="default"
class="gui-button-mini xhs-border-radius50 "
style="width:150rpx;margin-right: 20rpx;background: transparent;backdrop-filter: blur(10px);background-color: #FF3749;">
<text class="gui-color-white gui-icons">回关</text>
</button>
<button
v-show="!author.isFollow&&!author.isFollowMe"
@tap="followAuthor"
type="default"
class="gui-button-mini xhs-border-radius50 "
style="width:150rpx;margin-right: 20rpx;background: transparent;backdrop-filter: blur(10px);background-color: #FF3749;">
<text class="gui-color-white gui-icons">关注</text>
</button>
关注和点赞功能实现原理大致相同 只不过有一个互相关注
后台先创建一个关注表
CREATE TABLE `business_follow` (
`ID` varchar(32) NOT NULL,
`AUTHOR_ID` varchar(32) DEFAULT NULL COMMENT '被关注id',
`AUTHOR_NAME` varchar(255) DEFAULT NULL COMMENT '被关注名字',
`FOLLOW_ID` varchar(32) DEFAULT NULL COMMENT '关注者',
`FOLLOW_NAME` varchar(255) DEFAULT NULL COMMENT '关注者名字',
`CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='关注表';
关注人id 被关注人id 关注时间 有这三个就能基本实现
点击关注 插入 authorId对方id followId为当前登录人id
但是此时要注意
当你查询粉丝列表时 select xx from business_follow where AUTHOR_ID = #{当前登录人}
这样还不行 因为只查询到了粉丝列表
所以需要加一个子查询 我是否关注了对方 这样才能实现互关效果
<!-- 查询关注我的所有列表信息 并在查询的时候设置子查询 查询当前用户作为关注者的时候有没有关注对方-->
<select id="selectFollowMeList" resultType="com.dd.admin.business.follow.domain.FollowVo">
SELECT
b.AUTHOR_ID,
b.AUTHOR_NAME,
b.AVATAR_URL,
b.DESCRIPTION,
(
SELECT
count(1)
FROM
business_follow
WHERE
follow_id = #{targetId}
AND author_id = b.AUTHOR_ID
) AS isFollow
,
(
SELECT
count(1)
FROM
business_follow
WHERE
follow_id = b.AUTHOR_ID
AND author_id = #{targetId}
) AS isFollowMe
FROM
business_follow a
LEFT JOIN business_author b ON a.follow_id = b.author_id
WHERE
1 = 1
AND a.AUTHOR_ID = #{authorId}
ORDER BY
a.create_time DESC
</select>
有了用户关系 下一步就可以进行聊天了
这是我的聊天记录列表 由于没有通讯录
此列表查询的是所有跟你聊过天的人的列表
下面我们从后台 搭建im 服务器 这里我使用的是tio
pom 加上tio的依赖
<dependency>
<groupId>org.t-io</groupId>
<artifactId>tio-websocket-spring-boot-starter</artifactId>
<!--此版本号跟着tio主版本号一致即可-->
<version>3.3.2.v20190601-RELEASE</version>
</dependency>
配置类
package com.dd.admin.business.webSocket;
import com.alibaba.fastjson.JSON;
import com.dd.admin.common.utils.AddressUtils;
import com.dd.admin.common.utils.IPUtils;
import com.dd.admin.common.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.http.common.HttpResponse;
import org.tio.utils.lock.SetWithLock;
import org.tio.websocket.common.WsRequest;
import org.tio.websocket.server.handler.IWsMsgHandler;
import org.tio.websocket.starter.TioWebSocketServerBootstrap;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Component
public class MyWebSocketMsgHandler implements IWsMsgHandler {
MyWebSocketMsgHandler handler;
private static Logger log = LoggerFactory.getLogger(MyWebSocketMsgHandler.class);
@PostConstruct
public void init() {
handler = this;
}
@Autowired
public TioWebSocketServerBootstrap bootstrap;
@Autowired
Map<String, MsgHandlerInterface> handlerInterfaceMap;
@Override
public HttpResponse handshake(HttpRequest request, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
String authorId = request.getParam("authorId");
String authorName = request.getParam("authorName");
Tio.bindUser(channelContext,authorId);
String ipAddr = request.getClientIp();
String realAddress = AddressUtils.getRealAddress(ipAddr);
System.out.println(authorId+":进入了Tio id:"+authorId+" ip:"+ ipAddr);
SetWithLock<ChannelContext> channelContexts = Tio.getAllChannelContexts(bootstrap.getServerGroupContext());
Set<ChannelContext> contextList = channelContexts.getObj();
System.out.println("当前在线用户:");
for(ChannelContext context:contextList){
System.out.println(context.userid+"\t");
}
Integer count = channelContexts.size();
System.out.println(count);
return httpResponse;
}
@Override
public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
// System.out.println("握手成功进入群组");
}
@Override
public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
System.out.println("接收到bytes消息");
return null;
}
@Override
public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
return null;
}
@Override
public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) throws Exception {
if(text.equals("心跳内容")) return null;
System.out.println("接收到文本消息:"+text);
Map map = JSON.parseObject(text,Map.class);
String handlerType =(String)map.get("handlerType");
if(!StringUtil.isEmpty(handlerType)){
MsgHandlerInterface msgHandler = (MsgHandlerInterface) handlerInterfaceMap.get(handlerType);
if(msgHandler!=null){
msgHandler.handler(map,channelContext);
}else{
log.debug("非法请求...");
}
}else{
log.debug("非法请求...");
}
System.out.println(map);
return null;
}
}
处理点对点聊天的接口
package com.dd.admin.business.webSocket.handler;
import cn.hutool.core.bean.BeanUtil;
import com.dd.admin.business.chat.domain.ChatVo;
import com.dd.admin.business.chat.entity.Chat;
import com.dd.admin.business.chat.service.ChatService;
import com.dd.admin.business.webSocket.MsgHandlerInterface;
import com.dd.admin.business.webSocket.util.TioUtil;
import com.dd.admin.common.utils.AddressUtils;
import com.dd.admin.common.utils.HttpContext;
import com.dd.admin.common.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.utils.lock.SetWithLock;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;
import java.util.Set;
@Component
@Slf4j
@Service("5")
public class P2PMessageHandler implements MsgHandlerInterface {
public static P2PMessageHandler handler;
@Autowired
ChatService chatService;
@Override
public Object handler(Map map, ChannelContext context ){
Chat chat = BeanUtil.toBean(map, Chat.class);
chat.setIpAddress(context.getClientNode().getIp());
chat.setIpRealAddress(AddressUtils.getRealAddress(chat.getIpAddress())); //ip真实地址
chatService.save(chat);
ChatVo chatVo = chatService.selectChat(chat.getChatId());
//t-io支持多点登录,获取的是一个集合,因为此账号可能存在多个连接哦
SetWithLock<ChannelContext> contexts = Tio.getChannelContextsByUserid(context.getGroupContext(), chat.getToId());
//用户在线
if(contexts!=null && contexts.size() > 0) {
Set<ChannelContext> contextList = contexts.getObj();
//t-io支持多点登录,获取的是一个集合,向集合发送聊天信息
for (ChannelContext con : contextList) {
TioUtil.sendMessage(con, "5", chatVo);
}
}
//也要给我自己发用于数据回显
//t-io支持多点登录,获取的是一个集合,因为此账号可能存在多个连接哦
SetWithLock<ChannelContext> contexts1 = Tio.getChannelContextsByUserid(context.getGroupContext(), chat.getFromId());
//用户在线
if(contexts1!=null && contexts1.size() > 0) {
Set<ChannelContext> contextList = contexts1.getObj();
//t-io支持多点登录,获取的是一个集合,向集合发送聊天信息
for (ChannelContext con : contextList) {
TioUtil.sendMessage(con, "5", chatVo);
}
}
return null;
}}
yml端口配置
tio:
websocket:
server:
port: 9326
heartbeat-timeout: 6000
# 集群配置 默认关闭
cluster:
enabled: false
# 集群是通过redis的Pub/Sub实现,所以需要配置Redis
redis:
ip: 127.0.0.1
port: 6379
all: true
group: true
ip: true
user: true
最后在启动类 配置启动即可
这样我们的后台服务就搭建好了
前台怎么跟他 进行交互呢?
我们用apipost的websocket连接测试一下
后台监听到了连接 但是过了一会就断了 因为我们设置的有心跳连接
所以我们封装了一个uniapp 可用的 连接类 实现了心跳连接
import GraceRequestConfig from '@/custom/graceRequestConfig.js';
// 判断socket是否已经连接成功
var socketOpen = false;
var socketUserClose = false; //主动调用 关闭后不在启动
// socket是否已经调用关闭function //自然关闭
var socketClose = false;
// socket发送的消息队列
var socketMsgQueue = [];
// 判断心跳变量
var heart = '';
// 心跳失败次数
var heartBeatFailCount = 0;
// 终止心跳
var heartBeatTimeOut = null;
// 终止重新连接
var connectSocketTimeOut = null;
var user = uni.getStorageSync('user')
// 定义WebSocket相关功能的对象
const webSocket = {
/**
* 创建一个 WebSocket 连接
* @param {options}
* url String 是 开发者服务器接口地址,必须是 wss 协议,且域名必须是后台配置的合法域名
* header Object 否 HTTP Header, header 中不能设置 Referer
* method String 否 默认是GET,有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
* protocols StringArray 否 子协议数组 1.4.0
* success Function 否 接口调用成功的回调函数
* fail Function 否 接口调用失败的回调函数
* complete Function 否 接口调用结束的回调函数(调用成功、失败都会执行)
*/
connectSocket: function (options) {
socketClose = false;
socketUserClose = false;
socketMsgQueue = [];
const wsUrl = GraceRequestConfig.wsUrl + '?authorName=' + user.authorName + "&authorId=" + user.authorId;
console.log('开始连接')
console.log('heartBeatTimeOut')
console.log(heartBeatTimeOut)
console.log('socketUserClose')
console.log(socketUserClose)
if(!socketUserClose){
//重新心跳
webSocket.startHeartBeat();
}
if(socketOpen){
console.log('已连接ws不在重复连接')
}
// const wsUrl = 'ws://192.168.10.98:9326?authorName=' + user.authorName + "&authorId=" + user.authorId;
uni.connectSocket({
url: wsUrl,
success: function (res) {
if (options) {
// 成功回调
options.success && options.success(res);
}
},
fail: function (res) {
if (options) {
// 失败回调
options.fail && options.fail(res);
}
}
});
},
/**
* 通过 WebSocket 连接发送数据
* @param {options}
* data String / ArrayBuffer 是 需要发送的内容
* success Function 否 接口调用成功的回调函数
* fail Function 否 接口调用失败的回调函数
* complete Function 否 接口调用结束的回调函数(调用成功、失败都会执行)
*/
sendSocketMessage: function (options) {
// console.log('发送消息')
if (socketOpen) {
uni.sendSocketMessage({
data: options.msg,
success: function (res) {
if (options) {
options.success && options.success(res);
}
},
fail: function (res) {
if (options) {
options.fail && options.fail(res);
}
}
});
} else {
// app.msg('聊天服务器已断开...')
// socketMsgQueue.push(options.msg);
}
},
/**
* 关闭 WebSocket 连接。
* @param {options}
* code Number 否 一个数字值表示关闭连接的状态号,表示连接被关闭的原因。如果这个参数没有被指定,默认的取值是1000 (表示正常连接关闭)
* reason String 否 一个可读的字符串,表示连接被关闭的原因。这个字符串必须是不长于123字节的UTF-8 文本(不是字符)
* fail Function 否 接口调用失败的回调函数
* complete Function 否 接口调用结束的回调函数(调用成功、失败都会执行)
*/
closeSocket: function (options) {
//关闭重连定时器
if (connectSocketTimeOut) {
clearTimeout(connectSocketTimeOut);
connectSocketTimeOut = null;
}
socketOpen = false;
//主动调用关闭
socketUserClose = true;
socketClose = true;
const self = this;
//关闭心跳了
self.stopHeartBeat();
uni.closeSocket({
success: function (res) {
console.log('WebSocket 已关闭!');
if (options) {
options.success && options.success(res);
}
},
fail: function (res) {
if (options) {
options.fail && options.fail(res);
}
}
});
},
// 开始心跳
startHeartBeat: function () {
console.log('socket开始心跳');
const self = this;
heart = 'heart';
self.heartBeat();
},
// 结束心跳
stopHeartBeat: function () {
console.log('socket结束心跳');
const self = this;
heart = '';
if (heartBeatTimeOut) {
clearTimeout(heartBeatTimeOut);
heartBeatTimeOut = null;
}
if (connectSocketTimeOut) {
clearTimeout(connectSocketTimeOut);
connectSocketTimeOut = null;
}
},
// 心跳
heartBeat: function () {
const self = this;
if (!heart) {
return;
}
self.sendSocketMessage({
msg: "心跳内容",
success: function (res) {
// console.log('socket心跳成功');
if (heart) {
heartBeatTimeOut = setTimeout(() => {
self.heartBeat();
}, 5000);
}
},
fail: function (res) {
console.log('socket心跳失败');
console.log(heartBeatFailCount);
// if (heartBeatFailCount > 2) {
// // 重连
// self.connectSocket();
// }
if (heart) {
heartBeatTimeOut = setTimeout(() => {
self.heartBeat();
}, 5000);
}
heartBeatFailCount++;
},
});
},
onSocketMessageCallback(callback) {
},
};
// 监听WebSocket连接打开事件。callback 回调函数
uni.onSocketOpen(function (res) {
console.log('WebSocket连接已打开!');
// 如果已经调用过关闭function
// 如果已经调用过关闭function
if (socketClose) {
console.log('不再自行关闭')
// webSocket.closeSocket();
} else {
socketOpen = true;
for (var i = 0; i < socketMsgQueue.length; i++) {
uni.sendSocketMessage(socketMsgQueue[i]);
}
socketMsgQueue = [];
webSocket.startHeartBeat();
}
// 发送请求离线消息状态
});
// 监听WebSocket错误。
uni.onSocketError(function (res) {
socketOpen = false
socketClose = true
console.log('WebSocket连接打开失败,请检查!', res);
console.log('异常关闭' + socketClose);
console.log('主动关闭' + socketUserClose);
//如果不是主动关闭的 重新链接
if (!socketUserClose) {
clearTimeout(connectSocketTimeOut);
connectSocketTimeOut = setTimeout(() => {
console.log('不是主动关闭所以重连')
webSocket.connectSocket();
}, 3000);
}
});
// 监听WebSocket接受到服务器的消息事件。
uni.onSocketMessage(function (res) {
// console.log('收到服务器内容:' + res.data)
webSocket.onSocketMessageCallback(res)}
);
// 监听WebSocket关闭。
uni.onSocketClose(function (res) {
console.log('WebSocket 已关闭!===================');
socketOpen = false
socketClose = true
console.log('异常关闭' + socketClose);
console.log('主动关闭' + socketUserClose);
//如果不是主动关闭的 重新链接
if (!socketUserClose) {
clearTimeout(connectSocketTimeOut);
connectSocketTimeOut = setTimeout(() => {
//
console.log('不是主动关闭所以重连 这里进行重连')
webSocket.connectSocket();
}, 3000);
}
});
// 使用export default导出webSocket对象,方便其他模块导入使用
export default webSocket;
目前还不是特别完美 后续我会进行优化
下面就开始了 我们前后端的交互
我们在主页进行websocket连接后
输入信息发送
我们开始对格式进行分析
fromId消息由谁发的
toid 消息发送给谁
handler 类型 我们约定5为点对点聊天
messageType 0 文本聊天
发送到后台
其实这几个字段就是我们的聊天记录表字段
我们通过传到后台的这两个id 分别查询到相应的设备集合
然后进行发送即可 为什么是集合 因为 一个账号可能涉及到多端登录
数据推送到前端后
// 滚动条滚动 [ 有新消息可以自动滚动到底部 ]
pageScroll : function () {
setTimeout(()=>{
uni.pageScrollTo({
scrollTop:999999+Math.random(),
duration:200
})
},200);
},
页面会向下继续生成 进行页面滚动即可
当页面聊天记录过多时 我们考虑分页加载聊天数据
这里和我们的笔记页表相同 下拉时 页数增加 没有数据是提示用户
getChatList(){
app.get('/auth/getChatList', {limit:20,page:this.page,
fromId:this.from.authorId,fromName:this.from.authorName}, '', (res => {
//倒序查询但是正序排列 时间早的在前面
this.chatList.unshift(... res.data.records.reverse());
if(this.page<=res.data.pages){
this.hasMore = true
this.$refs.loadmorecom2.stoploadmore();
if(this.page==1){
setTimeout(()=>{
this.pageScroll()
},300)
}
if(this.page<res.data.pages){
this.page++
}else if(this.page==res.data.pages){
this.$refs.loadmorecom2.nomore();
uni.stopPullDownRefresh();
}
}else{
this.$refs.loadmorecom2.nomore();
this.hasMore = false
app.msg('没有更多记录了')
uni.stopPullDownRefresh();
}
this.buildData()
}))
},
此时因为我们的数据是倒序查询的
10 9 8 7 6 5 4 3 2 1
但是在页面展示 老的数据在前面 所以要执行数组的 .reverse() 反转方法
我们还会看到 我们做了日期的格式化
- 小于2分钟是刚刚
- 今天的数据显示 时分
- 近一周的显示星期数 时分
- 本年的显示月日 时分
- 本年之前的 显示 年月日 时分
- 小于两分钟的数据 创建时间只显示第一条
buildData(){
const records = this.chatList
for (let i = 0; i < records.length; i++) {
const currentRecord = records[i];
const currentCreateTime = new Date(currentRecord.createTime);
let j = i + 1;
while (j < records.length) {
const nextRecord = records[j];
const nextCreateTime = new Date(nextRecord.createTime);
const timeDiff = (nextCreateTime - currentCreateTime) / 1000 / 60; // 计算时间差,单位换算为分钟
if (timeDiff <= 2) {
records[j].createTime = null;
j++;
} else {
break;
}
}
}
console.log(records)
this.chatList = records;
},
messageFormatDate : function (dateStr) {
if (!dateStr) {
return '';
}
const inputDate = new Date(dateStr);
const now = new Date();
const oneDay = 24 * 60 * 60 * 1000; // 一天的毫秒数
const oneHour = 60 * 60 * 1000; // 一小时的毫秒数
const oneMinute = 60 * 1000;
const twoMinutes = 2 * oneMinute;
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const weekStart = new Date(now.getTime() - 7 * oneDay);
const diff = now - inputDate;
if (diff < twoMinutes) {
return "刚刚";
} else if (inputDate >= todayStart) {
const hours = inputDate.getHours().toString().padStart(2, '0');
const minutes = inputDate.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
} else if (inputDate >= weekStart) {
const weekDay = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
const dayIndex = inputDate.getDay();
const hours = inputDate.getHours().toString().padStart(2, '0');
const minutes = inputDate.getMinutes().toString().padStart(2, '0');
return `${weekDay[dayIndex]} ${hours}:${minutes}`;
} else if (inputDate.getFullYear() === now.getFullYear()) {
const month = (inputDate.getMonth() + 1).toString().padStart(2, '0');
const day = inputDate.getDate().toString().padStart(2, '0');
const hours = inputDate.getHours().toString().padStart(2, '0');
const minutes = inputDate.getMinutes().toString().padStart(2, '0');
return `${month}-${day} ${hours}:${minutes}`;
} else {
const year = inputDate.getFullYear();
const month = (inputDate.getMonth() + 1).toString().padStart(2, '0');
const day = inputDate.getDate().toString().padStart(2, '0');
const hours = inputDate.getHours().toString().padStart(2, '0');
const minutes = inputDate.getMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
},
处理完数据后 我们再回头处理我们的聊天列表
对角标也进行了处理
针对这个聊天列表 需要注意的查询条件是
- 首先要查询所有我收到的数据 针对这个条件 加一个子查询我发送到对方的数据(没有回复的情况)
- 子查询加上最后一条记录 (无论双方谁发的)
- 针对这个人我的未读数量
- 需要按form_id进行分组
<select id="selectChatList" resultType="com.dd.admin.business.chat.domain.ChatVo"
parameterType="java.lang.String">
select * from (
SELECT
a.FROM_ID AS authorId,
a.FROM_NAME AS authorName,
b.AVATAR_URL AS authorAvatar,
a.content,
a.create_time,
(
SELECT
count(1)
FROM
business_chat ca
WHERE
ca.FROM_ID = a.FROM_ID
AND ca.to_id = #{authorId}
and ca.MESSAGE_STATUS = 0
) as unReadCount
FROM
business_chat a
LEFT JOIN
business_author b ON a.FROM_ID = b.AUTHOR_ID
WHERE
a.TO_ID = #{authorId}
UNION ALL
SELECT
a.TO_ID AS authorId,
a.TO_NAME AS authorName,
b.AVATAR_URL AS authorAvatar,
a.content,
a.create_time,
0 as unReadCount
FROM
business_chat a
LEFT JOIN
business_author b ON a.TO_ID = b.AUTHOR_ID
WHERE
a.FROM_ID = #{authorId}
ORDER BY
create_time DESC
) a1
GROUP BY a1.authorId
ORDER BY
create_time DESC
</select>
这个sql 稍微优点难度可以分开的进行处理
小红点的处理方法(这里我们只是粗略的实现了)
- 首先写一个查询未读消息的接口
- 当页面加载时请求接口
- 当收到websocket推送消息时 重新请求查询未读消息的接口
- 当点击某个人的聊天页面是 设置该用户的所有消息已读
- 此方法不能写到App.vue会提示无法设置tabbar数量
onShow(){
this.onShowStatus = false
console.log('onshow')
if(this.isLoad){
this.getMessageList()
}
this.$nextTick(()=> {
// 监听WebSocket接受到服务器的消息事件。
websocket.onSocketMessageCallback = (res) => {
console.log('我是message页面收到消息的提示')
app.setMessageTabBarBadge()
this.getMessageList()
}
app.setMessageTabBarBadge()
})
},
setMessageTabBarBadge(){
app.get('/auth/getUnReadCount', '', '', (res => {
if(res.data>0){
uni.setTabBarBadge({
index: 3,
text: res.data.toString()
})
}else{
uni.removeTabBarBadge({
index: 3
})
}
}))
},
其它需要考虑的就是 退出登录时断开websocket 重新登录时连接
连接时断开重连 断网时重连(目前在退出登录时还有问题)后续会进行优化
关注和im聊天功能基本开发完毕 后续进行细节的完善 和个人资料的自定义
代码地址
https://gitee.com/ddeatrr/springboot_vue_xhs