Browse Source

已读未读显示(完成)

master
xie.bx 2 years ago
parent
commit
c000c2e7ab
  1. 10
      im-client/src/main/java/com/bx/imclient/sender/IMSender.java
  2. 4
      im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java
  3. 1
      im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java
  4. 2
      im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java
  5. 19
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  6. 2
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  7. 2
      im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java
  8. 2
      im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java
  9. 49
      im-ui/src/components/chat/ChatBox.vue
  10. 4
      im-ui/src/components/chat/ChatMessageItem.vue
  11. 52
      im-ui/src/store/chatStore.js
  12. 25
      im-ui/src/view/Home.vue
  13. 166
      im-uniapp/App.vue
  14. 23
      im-uniapp/common/emotion.js
  15. 11
      im-uniapp/common/enums.js
  16. 3
      im-uniapp/common/request.js
  17. 55
      im-uniapp/components/chat-message-item/chat-message-item.vue
  18. 91
      im-uniapp/components/loading/loading.vue
  19. 83
      im-uniapp/pages/chat/chat-box.vue
  20. 26
      im-uniapp/pages/chat/chat.vue
  21. 9
      im-uniapp/pages/login/login.vue
  22. 141
      im-uniapp/store/chatStore.js

10
im-client/src/main/java/com/bx/imclient/sender/IMSender.java

@ -14,8 +14,6 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Service
public class IMSender {
@ -34,7 +32,7 @@ public class IMSender {
Integer serverId = (Integer)redisTemplate.opsForValue().get(key);
// 如果对方在线,将数据存储至redis,等待拉取推送
if (serverId != null) {
String sendKey = String.join(":", IMRedisKey.IM_UNREAD_PRIVATE_QUEUE, serverId.toString());
String sendKey = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, serverId.toString());
IMRecvInfo recvInfo = new IMRecvInfo();
recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
recvInfo.setSendResult(message.getSendResult());
@ -63,7 +61,7 @@ public class IMSender {
Integer serverId = (Integer)redisTemplate.opsForValue().get(key);
// 如果终端在线,将数据存储至redis,等待拉取推送
if (serverId != null) {
String sendKey = String.join(":", IMRedisKey.IM_UNREAD_PRIVATE_QUEUE, serverId.toString());
String sendKey = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, serverId.toString());
IMRecvInfo recvInfo = new IMRecvInfo();
// 自己的消息不需要回推消息结果
recvInfo.setSendResult(false);
@ -112,7 +110,7 @@ public class IMSender {
recvInfo.setSendResult(message.getSendResult());
recvInfo.setData(message.getData());
// 推送至队列
String key = String.join(":", IMRedisKey.IM_UNREAD_GROUP_QUEUE, entry.getKey().toString());
String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, entry.getKey().toString());
redisTemplate.opsForList().rightPush(key, recvInfo);
}
// 对离线用户回复消息状态
@ -144,7 +142,7 @@ public class IMSender {
// 自己的消息不需要回推消息结果
recvInfo.setSendResult(false);
recvInfo.setData(message.getData());
String sendKey = String.join(":", IMRedisKey.IM_UNREAD_GROUP_QUEUE, serverId.toString());
String sendKey = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, serverId.toString());
redisTemplate.opsForList().rightPush(sendKey, recvInfo);
}
}

4
im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java

@ -7,9 +7,9 @@ public class IMRedisKey {
// 用户ID所连接的IM-server的ID
public final static String IM_USER_SERVER_ID = "im:user:server_id";
// 未读私聊消息队列
public final static String IM_UNREAD_PRIVATE_QUEUE = "im:unread:private";
public final static String IM_MESSAGE_PRIVATE_QUEUE = "im:message:private";
// 未读群聊消息队列
public final static String IM_UNREAD_GROUP_QUEUE = "im:unread:group";
public final static String IM_MESSAGE_GROUP_QUEUE = "im:message:group";
// 私聊消息发送结果队列
public final static String IM_RESULT_PRIVATE_QUEUE = "im:result:private";
// 群聊消息发送结果队列

1
im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java

@ -22,6 +22,7 @@ public class GroupMessageListener implements MessageListener<GroupMessageVO> {
@Override
public void process(IMSendResult<GroupMessageVO> result){
GroupMessageVO messageInfo = result.getData();
// todo 删除
// 保存该用户已拉取的最大消息id
if(result.getCode().equals(IMSendCode.SUCCESS.code())) {
String key = String.join(":",RedisKey.IM_GROUP_READED_POSITION,messageInfo.getGroupId().toString(),result.getReceiver().getId().toString());

2
im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java

@ -79,7 +79,7 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
@Override
public void delFriend(Long friendId) {
long userId = SessionContext.getSession().getUserId();
// 互相解除好友关系
// 互相解除好友关系,走代理清理缓存
FriendServiceImpl proxy = (FriendServiceImpl)AopContext.currentProxy();
proxy.unbindFriend(userId,friendId);
proxy.unbindFriend(friendId,userId);

19
im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java

@ -1,5 +1,6 @@
package com.bx.implatform.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -27,6 +28,7 @@ import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.dto.GroupMessageDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@ -50,7 +52,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
private IMClient imClient;
/**
* 发送群聊消息(与mysql所有交换都要进行缓存)
* 发送群聊消息(高并发接口查询mysql接口都要进行缓存)
*
* @param dto 群聊消息
* @return 群聊id
@ -218,8 +220,8 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
Integer sendMaxId = Objects.isNull(o) ? -1 : (Integer) o;
vos.stream().filter(vo -> vo.getGroupId().equals(id)).forEach(vo -> {
if (vo.getId() <= sendMaxId) {
// 已推送过
vo.setStatus(MessageStatus.SENDED.code());
// 已
vo.setStatus(MessageStatus.READED.code());
} else {
// 未推送
vo.setStatus(MessageStatus.UNSEND.code());
@ -250,6 +252,17 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
imClient.sendGroupMessage(sendMessage);
// 记录已读位置
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.eq(GroupMessage::getGroupId, groupId)
.orderByDesc(GroupMessage::getId)
.last("limit 1")
.select(GroupMessage::getId);
GroupMessage message = this.getOne(wrapper);
String key = StrUtil.join(":",RedisKey.IM_GROUP_READED_POSITION,groupId,session.getUserId());
redisTemplate.opsForValue().set(key, message.getId());
}
/**

2
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -45,7 +45,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
private IMClient imClient;
/**
* 发送私聊消息
* 发送私聊消息(高并发接口查询mysql接口都要进行缓存)
*
* @param dto 私聊消息
* @return 消息id

2
im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java

@ -23,7 +23,7 @@ public class PullUnreadGroupMessageTask extends AbstractPullMessageTask {
@Override
public void pullMessage() {
// 从redis拉取未读消息
String key = String.join(":", IMRedisKey.IM_UNREAD_GROUP_QUEUE,IMServerGroup.serverId+"");
String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE,IMServerGroup.serverId+"");
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(jsonObject != null){
IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class);

2
im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java

@ -25,7 +25,7 @@ public class PullUnreadPrivateMessageTask extends AbstractPullMessageTask {
@Override
public void pullMessage() {
// 从redis拉取未读消息
String key = String.join(":", IMRedisKey.IM_UNREAD_PRIVATE_QUEUE ,IMServerGroup.serverId+"");
String key = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE,IMServerGroup.serverId+"");
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(jsonObject!=null){
IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class);

49
im-ui/src/components/chat/ChatBox.vue

@ -8,11 +8,11 @@
<el-main style="padding: 0;">
<el-container>
<el-container class="content-box">
<el-main class="im-chat-main" id="chatScrollBox">
<el-main class="im-chat-main" id="chatScrollBox" @scroll="handleScroll">
<div class="im-chat-box">
<ul>
<li v-for="(msgInfo,idx) in chat.messages" :key="idx">
<chat-message-item :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)"
<chat-message-item v-show="idx>=showMinIdx" :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)"
:showName="showName(msgInfo)" :msgInfo="msgInfo" @delete="deleteMessage"
@recall="recallMessage">
</chat-message-item>
@ -115,7 +115,8 @@
y: 0
},
showHistory: false, //
lockMessage: false //
lockMessage: false, //
showMinIdx: 0 // showMinIdx
}
},
methods: {
@ -236,6 +237,22 @@
handleCloseSide() {
this.showSide = false;
},
handleScrollToTop() {
// 10
this.showMinIdx = this.showMinIdx > 10 ? this.showMinIdx - 10 : 0;
},
handleScroll(e) {
let scrollElement = e.target
let scrollTop = scrollElement.scrollTop
if (scrollTop <30 ) { // 在顶部,不滚动的情况
console.log("next")
// 20
this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
}
},
switchEmotionBox() {
this.showEmotion = !this.showEmotion;
let width = this.$refs.emotion.offsetWidth;
@ -329,7 +346,6 @@
},
sendTextMessage() {
if (!this.sendText.trim()) {
this.$message.error("不能发送空白信息");
return
}
let msgInfo = {
@ -396,16 +412,16 @@
});
},
readedMessage() {
if(this.chat.type == "GROUP"){
if (this.chat.type == "GROUP") {
var url = `/message/group/readed?groupId=${this.chat.targetId}`
}else{
} else {
url = `/message/private/readed?friendId=${this.chat.targetId}`
}
this.$http({
url: url,
method: 'put'
}).then(() => {
this.$store.commit("resetUnreadCount",this.chat)
this.$store.commit("resetUnreadCount", this.chat)
this.scrollToBottom();
})
},
@ -457,7 +473,7 @@
},
scrollToBottom() {
this.$nextTick(() => {
const div = document.getElementById("chatScrollBox");
let div = document.getElementById("chatScrollBox");
div.scrollTop = div.scrollHeight;
});
}
@ -490,8 +506,9 @@
watch: {
chat: {
handler(newChat, oldChat) {
if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || newChat.targetId != oldChat
.targetId)) {
if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type ||
newChat.targetId != oldChat.targetId)) {
if (this.chat.type == "GROUP") {
this.loadGroup(this.chat.targetId);
} else {
@ -499,6 +516,9 @@
}
this.scrollToBottom();
this.sendText = "";
// 30
let size = this.chat.messages.length;
this.showMinIdx = size > 30 ? size - 30 : 0;
//
this.$nextTick(() => {
this.$refs.sendBox.focus();
@ -509,12 +529,16 @@
},
unreadCount: {
handler(newCount, oldCount) {
if(newCount > 0){
if (newCount > 0) {
//
this.readedMessage()
}
}
}
},
mounted() {
let div = document.getElementById("chatScrollBox");
div.addEventListener('scroll', this.handleScroll)
}
}
</script>
@ -606,8 +630,7 @@
color: black;
background-color: #f8f8f8 !important;
outline-color: rgba(83, 160, 231, 0.61);
text-align: left;
border: 0;
}
.send-image-area {

4
im-ui/src/components/chat/ChatMessageItem.vue

@ -342,10 +342,10 @@
color: #f23c0f;
font-weight: 600;
}
.chat-readed {
font-size: 10px;
color: #aaa;
color: #888;
font-weight: 600;
}
}

52
im-ui/src/store/chatStore.js

@ -15,22 +15,16 @@ export default {
},
mutations: {
initChats(state, chats) {
state.chats = chats||[];
initChats(state, chatsData) {
state.chats = chatsData.chats || [];
state.privateMsgMaxId = chatsData.privateMsgMaxId || 0;
state.groupMsgMaxId = chatsData.groupMsgMaxId || 0;
// 防止图片一直处在加载中状态
state.chats.forEach((chat) => {
chat.messages.forEach((msg) => {
// 防止图片一直处在加载中状态
if (msg.loadStatus == "loading") {
msg.loadStatus = "fail"
}
// 记录最大私聊消息id
if(chat.type == "PRIVATE" && msg.id && msg.id>state.privateMsgMaxId){
state.privateMsgMaxId = msg.id;
}
// 记录最大群聊消息id
if(chat.type == "GROUP" && msg.id && msg.id>state.groupMsgMaxId){
state.groupMsgMaxId = msg.id;
}
})
})
},
@ -76,17 +70,17 @@ export default {
},
resetUnreadCount(state, chatInfo) {
for (let idx in state.chats) {
if (state.chats[idx].type == chatInfo.type
&& state.chats[idx].targetId == chatInfo.targetId) {
state.chats[idx].unreadCount=0;
if (state.chats[idx].type == chatInfo.type &&
state.chats[idx].targetId == chatInfo.targetId) {
state.chats[idx].unreadCount = 0;
}
}
this.commit("saveToStorage");
},
readedMessage(state, friendId) {
for (let idx in state.chats) {
if (state.chats[idx].type == 'PRIVATE'
&& state.chats[idx].targetId == friendId) {
if (state.chats[idx].type == 'PRIVATE' &&
state.chats[idx].targetId == friendId) {
state.chats[idx].messages.forEach((m) => {
if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) {
m.status = MESSAGE_STATUS.READED
@ -154,12 +148,11 @@ export default {
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED) {
chat.unreadCount++;
}
// 记录消息的最大id
if (msgInfo.id && type=="PRIVATE" && msgInfo.id > state.privateMsgMaxId) {
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > state.privateMsgMaxId) {
state.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type=="GROUP" && msgInfo.id > state.groupMsgMaxId) {
}
if (msgInfo.id && type == "GROUP" && msgInfo.id > state.groupMsgMaxId) {
state.groupMsgMaxId = msgInfo.id;
}
// 如果是已存在消息,则覆盖旧的消息数据
@ -239,17 +232,22 @@ export default {
}
this.commit("saveToStorage");
},
loadingPrivateMsg(state,loadding){
loadingPrivateMsg(state, loadding) {
state.loadingPrivateMsg = loadding;
},
loadingGroupMsg(state,loadding){
loadingGroupMsg(state, loadding) {
state.loadingGroupMsg = loadding;
},
saveToStorage(state) {
let userId = userStore.state.userInfo.id;
let key = "chats-" + userId;
localStorage.setItem(key, JSON.stringify(state.chats));
let chatsData = {
privateMsgMaxId: state.privateMsgMaxId,
groupMsgMaxId: state.groupMsgMaxId,
chats: state.chats
}
localStorage.setItem(key, JSON.stringify(chatsData));
},
clear(state) {
state.activeIndex = -1;
@ -262,8 +260,10 @@ export default {
let userId = userStore.state.userInfo.id;
let key = "chats-" + userId;
let item = localStorage.getItem(key)
let chats = JSON.parse(localStorage.getItem(key));
context.commit("initChats", chats);
if (item) {
let chatsData = JSON.parse(item);
context.commit("initChats", chatsData);
}
resolve();
})
}

25
im-ui/src/view/Home.vue

@ -74,13 +74,13 @@
data() {
return {
showSettingDialog: false,
lastPlayAudioTime: new Date()-1000
}
},
methods: {
init() {
this.$store.dispatch("load").then(() => {
//
// 线
this.loadPrivateMessage(this.$store.state.chatStore.privateMsgMaxId);
this.loadGroupMessage(this.$store.state.chatStore.groupMsgMaxId);
// ws
@ -202,8 +202,9 @@
//
this.$store.commit("insertMessage", msg);
//
!msg.selfSend && this.playAudioTip();
if(!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED){
this.playAudioTip();
}
},
handleGroupMessage(msg) {
//
@ -236,7 +237,9 @@
//
this.$store.commit("insertMessage", msg);
//
!msg.selfSend && this.playAudioTip();
if(!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED){
this.playAudioTip();
}
},
handleExit() {
this.$wsApi.close();
@ -244,10 +247,14 @@
location.href = "/";
},
playAudioTip() {
let audio = new Audio();
let url = require(`@/assets/audio/tip.wav`);
audio.src = url;
audio.play();
if(new Date() - this.lastPlayAudioTime > 1000){
this.lastPlayAudioTime = new Date();
let audio = new Audio();
let url = require(`@/assets/audio/tip.wav`);
audio.src = url;
audio.play();
}
},
showSetting() {
this.showSettingDialog = true;

166
im-uniapp/App.vue

@ -18,6 +18,9 @@
this.initAudit();
// websocket
this.initWebSocket();
// 线
this.loadPrivateMessage(store.state.chatStore.privateMsgMaxId);
this.loadGroupMessage(store.state.chatStore.groupMsgMaxId);
}).catch((e) => {
console.log(e);
this.exit();
@ -28,10 +31,7 @@
let userId = store.state.userStore.userInfo.id;
wsApi.init(process.env.WS_URL, loginInfo.accessToken);
wsApi.connect();
wsApi.onOpen(()=>{
//
this.pullUnreadMessage();
})
wsApi.onOpen()
wsApi.onMessage((cmd, msgInfo) => {
if (cmd == 2) {
// 线
@ -41,58 +41,85 @@
})
this.exit();
} else if (cmd == 3) {
//
msgInfo.selfSend = userId == msgInfo.sendId;
//
//
this.handlePrivateMessage(msgInfo);
} else if (cmd == 4) {
//
msgInfo.selfSend = userId == msgInfo.sendId;
//
//
this.handleGroupMessage(msgInfo);
}
});
wsApi.onClose((res)=>{
wsApi.onClose((res) => {
// 10063000APP
if(res.code == 1006){
if (res.code == 1006) {
uni.showToast({
title: '连接已断开,请重新登录',
icon: 'none',
})
this.exit();
}else if(res.code != 3000){
} else if (res.code != 3000) {
//
wsApi.connect();
}
})
},
pullUnreadMessage() {
//
loadPrivateMessage(minId) {
store.commit("loadingPrivateMsg", true)
http({
url: "/message/private/pullUnreadMessage",
method: 'POST'
});
//
url: "/message/private/loadMessage?minId=" + minId,
method: 'get'
}).then((msgInfos) => {
msgInfos.forEach((msgInfo) => {
this.handlePrivateMessage(msgInfo);
})
if (msgInfos.length == 100) {
//
this.loadPrivateMessage(msgInfos[99].id);
} else {
store.commit("loadingPrivateMsg", false)
}
})
},
loadGroupMessage(minId) {
store.commit("loadingGroupMsg", true)
http({
url: "/message/group/pullUnreadMessage",
method: 'POST'
});
url: "/message/group/loadMessage?minId=" + minId,
method: 'get'
}).then((msgInfos) => {
msgInfos.forEach((msgInfo) => {
this.handleGroupMessage(msgInfo);
})
if (msgInfos.length == 100) {
//
this.loadGroupMessage(msgInfos[99].id);
} else {
store.commit("loadingGroupMsg", false)
}
})
},
handlePrivateMessage(msg) {
//
msg.selfSend = msg.sendId == store.state.userStore.userInfo.id;
// id
let friendId = msg.selfSend ? msg.recvId : msg.sendId;
let friend = store.state.friendStore.friends.find((f) => f.id == friendId);
if (!friend) {
http({
url: `/friend/find/${msg.sendId}`,
method: 'GET'
}).then((friend) => {
this.insertPrivateMessage(friend, msg);
store.commit("addFriend", friend);
})
} else {
//
this.insertPrivateMessage(friend, msg);
//
if (msg.type == enums.MESSAGE_TYPE.READED) {
if (msg.selfSend) {
//
let chatInfo = {
type: 'PRIVATE',
targetId: friendId
}
store.commit("resetUnreadCount", chatInfo)
} else {
//
store.commit("readedMessage", friendId)
}
return;
}
this.loadFriendInfo(friendId).then((friend) => {
this.insertPrivateMessage(friend, msg);
})
},
insertPrivateMessage(friend, msg) {
@ -115,19 +142,23 @@
},
handleGroupMessage(msg) {
let group = store.state.groupStore.groups.find((g) => g.id == msg.groupId);
if (!group) {
http({
url: `/group/find/${msg.groupId}`,
method: 'get'
}).then((group) => {
this.insertGroupMessage(group, msg);
store.commit("addGroup", group);
})
} else {
//
this.insertGroupMessage(group, msg);
//
msg.selfSend = msg.sendId == store.state.userStore.userInfo.id;
let groupId = msg.groupId;
//
if (msg.type == enums.MESSAGE_TYPE.READED) {
//
let chatInfo = {
type: 'GROUP',
targetId: groupId
}
store.commit("resetUnreadCount", chatInfo)
return;
}
this.loadGroupInfo(groupId).then((group) => {
//
this.insertGroupMessage(group, msg);
})
},
insertGroupMessage(group, msg) {
@ -144,11 +175,43 @@
//
!msg.selfSend && this.playAudioTip();
},
loadFriendInfo(id) {
return new Promise((resolve, reject) => {
let friend = store.state.friendStore.friends.find((f) => f.id == id);
if (friend) {
resolve(friend);
} else {
http({
url: `/friend/find/${id}`,
method: 'get'
}).then((friend) => {
store.commit("addFriend", friend);
resolve(friend)
})
}
});
},
loadGroupInfo(id) {
return new Promise((resolve, reject) => {
let group = store.state.groupStore.groups.find((g) => g.id == id);
if (group) {
resolve(group);
} else {
http({
url: `/group/find/${id}`,
method: 'get'
}).then((group) => {
resolve(group)
store.commit("addGroup", group);
})
}
});
},
exit() {
console.log("exit");
wsApi.close();
uni.removeStorageSync("loginInfo");
uni.navigateTo({
uni.reLaunch({
url: "/pages/login/login"
})
store.dispatch("unload");
@ -161,18 +224,18 @@
},
initAudit() {
console.log("initAudit")
if(store.state.userStore.userInfo.type == 1){
if (store.state.userStore.userInfo.type == 1) {
//
uni.setTabBarItem({
index: 2,
text: "群聊"
})
}else{
})
} else {
//
uni.setTabBarItem({
index: 2,
text: "搜索"
})
})
}
}
},
@ -214,5 +277,4 @@
// #endif
background-color: #f8f8f8;
}
</style>

23
im-uniapp/common/emotion.js

@ -7,41 +7,26 @@ const emoTextList = ['憨笑', '媚眼', '开心', '坏笑', '可怜', '爱心',
];
let emoImageUrlList = [];
// 备注:经过测试,小程序的<rich-text>无法显示相对路径的图片,所以在这里对图片提前全部转成绝对路径
// 提前初始化图片的url
for (let i = 0; i < emoTextList.length; i++) {
let path = `/static/emoji2/${i}.gif`;
uni.getImageInfo({
src: path,
success(res) {
emoImageUrlList[i] = res.path
},
fail(res) {
emoImageUrlList = path;
}
});
}
let transform = (content) => {
return content.replace(/\#[\u4E00-\u9FA5]{1,3}\;/gi, textToImg);
}
// 将匹配结果替换表情图片
let textToImg = (emoText) => {
let word = emoText.replace(/\#|\;/gi, '');
let idx = emoTextList.indexOf(word);
if (idx == -1) {
return "";
}
let path = textToPath(emoText);
// #ifdef MP
// 微信小程序不能有前面的'/'
path = path.slice(1);
// #endif
let img = `<img src="${path}" style="with:35px;height:35px;
margin: 0 -2px;vertical-align:bottom;"/>`;
margin: 0 -2px;vertical-align:bottom;"/>`;
return img;
}

11
im-uniapp/common/enums.js

@ -6,6 +6,7 @@ const MESSAGE_TYPE = {
AUDIO:3,
VIDEO:4,
RECALL:10,
READED:11,
TIP_TIME:20,
RTC_CALL: 101,
RTC_ACCEPT: 102,
@ -27,8 +28,16 @@ const TERMINAL_TYPE = {
APP: 1
}
const MESSAGE_STATUS = {
UNSEND: 0,
SENDED: 1,
RECALL:2,
READED:3
}
export {
MESSAGE_TYPE,
USER_STATE,
TERMINAL_TYPE
TERMINAL_TYPE,
MESSAGE_STATUS
}

3
im-uniapp/common/request.js

@ -43,9 +43,6 @@ const request = (options) => {
requestList.forEach(cb => cb());
requestList = [];
isRefreshToken = false;
// 保存token
console.log(res.data.data.accessToken)
// 重新发送刚才的请求
return resolve(request(options))

55
im-uniapp/components/chat-message-item/chat-message-item.vue

@ -2,12 +2,12 @@
<view class="chat-msg-item">
<view class="chat-msg-tip" v-if="msgInfo.type==$enums.MESSAGE_TYPE.RECALL">{{msgInfo.content}}</view>
<view class="chat-msg-tip" v-if="msgInfo.type==$enums.MESSAGE_TYPE.TIP_TIME">
{{$date.toTimeText(msgInfo.sendTime)}}</view>
{{$date.toTimeText(msgInfo.sendTime)}}
</view>
<view class="chat-msg-normal" v-if="msgInfo.type>=0 && msgInfo.type<10"
:class="{'chat-msg-mine':msgInfo.selfSend}">
<head-image class="avatar" :id="msgInfo.sendId" :url="headImage"
:name="showName" :size="80"></head-image>
<head-image class="avatar" :id="msgInfo.sendId" :url="headImage" :name="showName" :size="80"></head-image>
<view class="chat-msg-content" @longpress="onShowMenu($event)">
<view v-if="msgInfo.groupId && !msgInfo.selfSend" class="chat-msg-top">
<text>{{showName}}</text>
@ -18,8 +18,8 @@
:nodes="$emo.transform(msgInfo.content)"></rich-text>
<view class="chat-msg-image" v-if="msgInfo.type==$enums.MESSAGE_TYPE.IMAGE">
<view class="img-load-box">
<image class="send-image" mode="heightFix" :src="JSON.parse(msgInfo.content).thumbUrl" lazy-load="true"
@click.stop="onShowFullImage()">
<image class="send-image" mode="heightFix" :src="JSON.parse(msgInfo.content).thumbUrl"
lazy-load="true" @click.stop="onShowFullImage()">
</image>
<loading v-if="loading"></loading>
</view>
@ -40,6 +40,11 @@
<text title="发送失败" v-if="loadFail" @click="onSendFail"
class="send-fail iconfont icon-warning-circle-fill"></text>
</view>
<text class="chat-readed" v-show="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status==$enums.MESSAGE_STATUS.READED">已读</text>
<text class="chat-unread" v-show="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status!=$enums.MESSAGE_STATUS.READED">未读</text>
<!--
<view class="chat-msg-voice" v-if="msgInfo.type==$enums.MESSAGE_TYPE.AUDIO" @click="onPlayVoice()">
<audio controls :src="JSON.parse(msgInfo.content).url"></audio>
@ -219,12 +224,13 @@
.chat-msg-bottom {
display: inline-block;
padding-right: 80rpx ;
.chat-msg-text {
padding-right: 80rpx;
.chat-msg-text {
position: relative;
line-height: 60rpx;
margin-top: 10rpx;
padding: 10rpx;
padding: 10rpx 20rpx;
background-color: #ebebf5;
border-radius: 10rpx;
color: #333;
@ -234,6 +240,7 @@
word-break: break-all;
white-space: pre-line;
box-shadow: 2px 2px 2px #c0c0f0;
&:after {
content: "";
position: absolute;
@ -244,7 +251,7 @@
border-style: solid dashed dashed;
border-color: #ebebf5 transparent transparent;
overflow: hidden;
border-width: 20rpx;
border-width: 18rpx;
}
}
@ -295,13 +302,14 @@
background-color: #eeeeee;
padding: 10px 15px;
box-shadow: 2px 2px 2px #c0c0c0;
.chat-file-info {
flex: 1;
height: 100%;
text-align: left;
font-size: 14px;
width: 300rpx;
.chat-file-name {
font-size: 16px;
font-weight: 600;
@ -325,14 +333,17 @@
}
.chat-msg-voice {
font-size: 14px;
cursor: pointer;
audio {
height: 45px;
padding: 5px 0;
}
.chat-unread {
font-size: 10px;
color: #f23c0f;
font-weight: 600;
}
.chat-readed {
font-size: 10px;
color: #ccc;
font-weight: 600;
}
}
}
@ -350,15 +361,17 @@
.chat-msg-content {
text-align: right;
.chat-msg-bottom {
padding-left: 80rpx ;
padding-left: 80rpx;
padding-right: 0;
.chat-msg-text {
margin-left: 10px;
background-color: #587ff0;
color: #fff;
box-shadow: 1px 1px 1px #ccc;
&:after {
left: auto;
right: -10px;

91
im-uniapp/components/loading/loading.vue

@ -1,49 +1,64 @@
<template>
<view class="loading-box">
<view class="rotate iconfont icon-loading" ></view>
</view>
<view class="loading-box" :style="loadingStyle">
<view class="rotate iconfont icon-loading" :style="icontStyle"></view>
<slot></slot>
</view>
</template>
<script>
export default {
data() {
return {};
},
methods: {
},
computed: {
}
};
import {
computed
} from "vue"
export default {
data() {
return {}
},
props: {
size: {
type: Number,
default: 100
},
mask: {
type: Boolean,
default: true
}
},
computed: {
icontStyle() {
return `font-size:${this.size}rpx`;
},
loadingStyle() {
return this.mask ? "background: rgba(0, 0, 0, 0.3);" : "";
}
}
}
</script>
<style>
.loading-box {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
position: absolute;
left: 0;
top: 0;
z-index: 10000;
display: flex;
justify-content: center;
align-items: center;
}
<style lang="scss" scoped>
.loading-box {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 10000;
display: flex;
justify-content: center;
align-items: center;
}
.rotate {
animation: rotate 2s ease-in-out infinite;
}
.rotate {
animation: rotate 2s ease-in-out infinite;
font-size: 100rpx;
}
@keyframes rotate {
from {
transform: rotate(0deg)
}
@keyframes rotate {
from {
transform: rotate(0deg)
}
to {
transform: rotate(360deg)
}
}
to {
transform: rotate(360deg)
}
}
</style>

83
im-uniapp/pages/chat/chat-box.vue

@ -6,9 +6,10 @@
<uni-icons class="btn-side right" type="more-filled" size="30" @click="onShowMore()"></uni-icons>
</view>
<view class="chat-msg" @click="switchChatTabBox('none',true)">
<scroll-view class="scroll-box" scroll-y="true" :scroll-into-view="'chat-item-'+scrollMsgIdx">
<scroll-view class="scroll-box" scroll-y="true" @scrolltoupper="onScrollToTop"
:scroll-into-view="'chat-item-'+scrollMsgIdx">
<view v-for="(msgInfo,idx) in chat.messages" :key="idx">
<chat-message-item :headImage="headImage(msgInfo)" :showName="showName(msgInfo)"
<chat-message-item v-if="idx>=showMinIdx" :headImage="headImage(msgInfo)" :showName="showName(msgInfo)"
@recall="onRecallMessage" @delete="onDeleteMessage" @download="onDownloadFile"
:id="'chat-item-'+idx" :msgInfo="msgInfo">
</chat-message-item>
@ -32,8 +33,8 @@
<view class="chat-tab-bar" v-show="chatTabBox!='none' ||showKeyBoard " :style="{height:`${keyboardHeight}px`}">
<view v-if="chatTabBox == 'tools'" class="chat-tools">
<view class="chat-tools-item">
<image-upload :maxCount="9" sourceType="album" :onBefore="onUploadImageBefore" :onSuccess="onUploadImageSuccess"
:onError="onUploadImageFail">
<image-upload :maxCount="9" sourceType="album" :onBefore="onUploadImageBefore"
:onSuccess="onUploadImageSuccess" :onError="onUploadImageFail">
<view class="tool-icon iconfont icon-picture"></view>
</image-upload>
<view class="tool-name">相册</view>
@ -66,8 +67,9 @@
<scroll-view v-if="chatTabBox==='emo'" class="chat-emotion" scroll-y="true">
<view class="emotion-item-list">
<image class="emotion-item" :title="emoText" :src="$emo.textToPath(emoText)" v-for="(emoText, i) in $emo.emoTextList"
:key="i" @click="selectEmoji(emoText)" mode="aspectFit" lazy-load="true"></image>
<image class="emotion-item" :title="emoText" :src="$emo.textToPath(emoText)"
v-for="(emoText, i) in $emo.emoTextList" :key="i" @click="selectEmoji(emoText)" mode="aspectFit"
lazy-load="true"></image>
</view>
</scroll-view>
<view v-if="showKeyBoard"></view>
@ -89,11 +91,12 @@
scrollMsgIdx: 0, //
chatTabBox: 'none',
showKeyBoard: false,
keyboardHeight: 322
keyboardHeight: 322,
showMinIdx: 0 // showMinIdx
}
},
methods: {
showTip(){
showTip() {
uni.showToast({
title: "加班开发中...",
icon: "none"
@ -139,6 +142,7 @@
msgInfo.sendTime = new Date().getTime();
msgInfo.sendId = this.$store.state.userStore.userInfo.id;
msgInfo.selfSend = true;
msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND;
this.$store.commit("insertMessage", msgInfo);
this.sendText = "";
}).finally(() => {
@ -171,6 +175,7 @@
return;
}
this.$nextTick(() => {
console.log("scrollToMsgIdx",this.scrollMsgIdx)
this.scrollMsgIdx = idx;
});
@ -184,7 +189,7 @@
selectEmoji(emoText) {
this.sendText += `#${emoText};`;
},
onNavBack(){
onNavBack() {
uni.switchTab({
url: "/pages/chat/chat"
})
@ -211,7 +216,8 @@
sendTime: new Date().getTime(),
selfSend: true,
type: this.$enums.MESSAGE_TYPE.IMAGE,
loadStatus: "loading"
loadStatus: "loading",
status: this.$enums.MESSAGE_STATUS.UNSEND
}
// id
this.fillTargetId(msgInfo, this.chat.targetId);
@ -254,7 +260,8 @@
sendTime: new Date().getTime(),
selfSend: true,
type: this.$enums.MESSAGE_TYPE.FILE,
loadStatus: "loading"
loadStatus: "loading",
status: this.$enums.MESSAGE_STATUS.UNSEND
}
// id
this.fillTargetId(msgInfo, this.chat.targetId);
@ -318,6 +325,7 @@
msgInfo = JSON.parse(JSON.stringify(msgInfo));
msgInfo.type = this.$enums.MESSAGE_TYPE.RECALL;
msgInfo.content = '你撤回了一条消息';
msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL;
this.$store.commit("insertMessage", msgInfo);
})
}
@ -337,7 +345,7 @@
});
}
},
fail(e){
fail(e) {
console.log(e);
uni.showToast({
title: "文件下载失败",
@ -346,17 +354,37 @@
}
});
},
onShowMore(){
onScrollToTop() {
//
this.scrollToMsgIdx(this.showMinIdx);
// 10
this.showMinIdx = this.showMinIdx > 10 ? this.showMinIdx - 10 : 0;
},
onShowMore() {
if (this.chat.type == "GROUP") {
uni.navigateTo({
url: "/pages/group/group-info?id="+this.group.id
url: "/pages/group/group-info?id=" + this.group.id
})
}else{
} else {
uni.navigateTo({
url: "/pages/common/user-info?id="+this.friend.id
url: "/pages/common/user-info?id=" + this.friend.id
})
}
},
readedMessage() {
if (this.chat.type == "GROUP") {
var url = `/message/group/readed?groupId=${this.chat.targetId}`
} else {
url = `/message/private/readed?friendId=${this.chat.targetId}`
}
this.$http({
url: url,
method: 'PUT'
}).then(() => {
this.$store.commit("resetUnreadCount", this.chat)
this.scrollToBottom();
})
},
loadGroup(groupId) {
this.$http({
url: `/group/find/${groupId}`,
@ -416,6 +444,9 @@
return 0;
}
return this.chat.messages.length;
},
unreadCount() {
return this.chat.unreadCount;
}
},
watch: {
@ -424,15 +455,28 @@
if (newSize > oldSize) {
this.scrollToBottom();
}
},
unreadCount: {
handler(newCount, oldCount) {
if (newCount > 0) {
//
this.readedMessage()
}
}
}
},
onLoad(options) {
//
this.chat = this.$store.state.chatStore.chats[options.chatIdx];
// 30
let size = this.chat.messages.length;
this.showMinIdx = size > 30 ? size - 30 : 0;
//
this.$store.commit("activeChat", options.chatIdx);
//
this.scrollToBottom();
//
this.readedMessage()
//
if (this.chat.type == "GROUP") {
this.loadGroup(this.chat.targetId);
@ -471,16 +515,17 @@
line-height: 60rpx;
font-size: 28rpx;
cursor: pointer;
&.left {
left: 30rpx;
}
&.right {
right: 30rpx;
}
}
}

26
im-uniapp/pages/chat/chat.vue

@ -1,10 +1,16 @@
<template>
<view class="tab-page">
<view class="chat-tip" v-if="$store.state.chatStore.chats.length==0">
<view v-if="loading" class="chat-loading" >
<loading :size="50" :mask="false">
<view>消息接收中...</view>
</loading>
</view>
<view class="chat-tip" v-if="!loading && chatStore.chats.length==0">
温馨提示您现在还没有任何聊天消息快跟您的好友发起聊天吧~
</view>
<scroll-view class="scroll-bar" v-else scroll-with-animation="true" scroll-y="true">
<view v-for="(chat,index) in $store.state.chatStore.chats" :key="index">
<view v-for="(chat,index) in chatStore.chats" :key="index">
<chat-item :chat="chat" :index="index" @longpress.native="onShowMenu($event,index)"></chat-item>
</view>
</scroll-view>
@ -97,12 +103,18 @@
}
},
computed: {
chatStore() {
return this.$store.state.chatStore;
},
unreadCount() {
let count = 0;
this.$store.state.chatStore.chats.forEach(chat => {
this.chatStore.chats.forEach(chat => {
count += chat.unreadCount;
})
return count;
},
loading() {
return this.chatStore.loadingGroupMsg || this.chatStore.loadingPrivateMsg
}
},
watch: {
@ -126,4 +138,12 @@
color: darkblue;
font-size: 30rpx;
}
.chat-loading {
display: block;
height: 100rpx;
background: white;
position: relative;
color: blue;
}
</style>

9
im-uniapp/pages/login/login.vue

@ -20,8 +20,8 @@
return {
loginForm: {
terminal: 1, // APP
userName: 'blue',
password: '123456'
userName: '',
password: ''
},
rules: {
userName: {
@ -48,6 +48,8 @@
}).then(data => {
console.log("登录成功,自动跳转到聊天页面...")
uni.setStorageSync("loginInfo", data);
uni.setStorageSync("userName",this.loginForm.userName)
uni.setStorageSync("password",this.loginForm.password)
// App.vue
getApp().init()
//
@ -58,13 +60,14 @@
}
},
onLoad() {
this.loginForm.userName = uni.getStorageSync("userName");
this.loginForm.password = uni.getStorageSync("password");
let loginInfo = uni.getStorageSync("loginInfo");
if (loginInfo) {
//
uni.switchTab({
url: "/pages/chat/chat"
})
}
}
}

141
im-uniapp/store/chatStore.js

@ -1,23 +1,33 @@
import {MESSAGE_TYPE} from '@/common/enums.js';
import {
MESSAGE_TYPE,
MESSAGE_STATUS
} from '@/common/enums.js';
import userStore from './userStore';
export default {
state: {
chats: []
activeIndex: -1,
chats: [],
privateMsgMaxId: 0,
groupMsgMaxId: 0,
loadingPrivateMsg: false,
loadingGroupMsg: false,
},
mutations: {
initChats(state,chats){
initChats(state, chatsData) {
state.chats = chatsData.chats ||[];
state.privateMsgMaxId = chatsData.privateMsgMaxId||0;
state.groupMsgMaxId = chatsData.groupMsgMaxId||0;
// 防止图片一直处在加载中状态
chats.forEach((chat)=>{
chat.messages.forEach((msg)=>{
if(msg.loadStatus == "loading"){
state.chats.forEach((chat) => {
chat.messages.forEach((msg) => {
if (msg.loadStatus == "loading") {
msg.loadStatus = "fail"
}
})
})
state.chats = chats;
},
openChat(state, chatInfo) {
let chat = null;
@ -49,10 +59,33 @@ export default {
},
activeChat(state, idx) {
state.activeIndex = idx;
if(idx>=0){
if (idx >= 0) {
state.chats[idx].unreadCount = 0;
}
},
resetUnreadCount(state, chatInfo) {
for (let idx in state.chats) {
if (state.chats[idx].type == chatInfo.type &&
state.chats[idx].targetId == chatInfo.targetId) {
state.chats[idx].unreadCount = 0;
}
}
this.commit("saveToStorage");
},
readedMessage(state, friendId) {
for (let idx in state.chats) {
if (state.chats[idx].type == 'PRIVATE' &&
state.chats[idx].targetId == friendId) {
state.chats[idx].messages.forEach((m) => {
console.log("readedMessage")
if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) {
m.status = MESSAGE_STATUS.READED
}
})
}
}
this.commit("saveToStorage");
},
removeChat(state, idx) {
state.chats.splice(idx, 1);
this.commit("saveToStorage");
@ -73,7 +106,7 @@ export default {
}
}
},
moveTop(state,idx){
moveTop(state, idx) {
let chat = state.chats[idx];
// 放置头部
state.chats.splice(idx, 1);
@ -94,41 +127,44 @@ export default {
}
}
// 插入新的数据
if(msgInfo.type == MESSAGE_TYPE.IMAGE){
chat.lastContent = "[图片]";
}else if(msgInfo.type == MESSAGE_TYPE.FILE){
if (msgInfo.type == MESSAGE_TYPE.IMAGE) {
chat.lastContent = "[图片]";
} else if (msgInfo.type == MESSAGE_TYPE.FILE) {
chat.lastContent = "[文件]";
}else if(msgInfo.type == MESSAGE_TYPE.AUDIO){
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = "[语音]";
}else{
chat.lastContent = msgInfo.content;
} else {
chat.lastContent = msgInfo.content;
}
chat.lastSendTime = msgInfo.sendTime;
// 如果不是当前会话,未读加1
if(chatIdx != state.activeIndex){
// 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED) {
chat.unreadCount++;
}
// 自己回复了消息,说明消息已读
if(msgInfo.selfSend){
chat.unreadCount=0;
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > state.privateMsgMaxId) {
state.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type == "GROUP" && msgInfo.id > state.groupMsgMaxId) {
state.groupMsgMaxId = msgInfo.id;
}
// 如果是已存在消息,则覆盖旧的消息数据
for (let idx in chat.messages) {
if(msgInfo.id && chat.messages[idx].id == msgInfo.id){
if (msgInfo.id && chat.messages[idx].id == msgInfo.id) {
Object.assign(chat.messages[idx], msgInfo);
this.commit("saveToStorage");
return;
}
// 正在发送中的消息可能没有id,通过发送时间判断
if(msgInfo.selfSend && chat.messages[idx].selfSend
&& chat.messages[idx].sendTime == msgInfo.sendTime){
if (msgInfo.selfSend && chat.messages[idx].selfSend &&
chat.messages[idx].sendTime == msgInfo.sendTime) {
Object.assign(chat.messages[idx], msgInfo);
this.commit("saveToStorage");
return;
}
}
// 间隔大于10分钟插入时间显示
if(!chat.lastTimeTip || (chat.lastTimeTip < msgInfo.sendTime - 600*1000)){
if (!chat.lastTimeTip || (chat.lastTimeTip < msgInfo.sendTime - 600 * 1000)) {
chat.messages.push({
sendTime: msgInfo.sendTime,
type: MESSAGE_TYPE.TIP_TIME,
@ -138,9 +174,9 @@ export default {
// 新的消息
chat.messages.push(msgInfo);
this.commit("saveToStorage");
},
deleteMessage(state, msgInfo){
deleteMessage(state, msgInfo) {
// 获取对方id或群id
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId;
@ -152,16 +188,16 @@ export default {
break;
}
}
for (let idx in chat.messages) {
// 已经发送成功的,根据id删除
if(chat.messages[idx].id && chat.messages[idx].id == msgInfo.id){
if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
chat.messages.splice(idx, 1);
break;
}
// 正在发送中的消息可能没有id,根据发送时间删除
if(msgInfo.selfSend && chat.messages[idx].selfSend
&&chat.messages[idx].sendTime == msgInfo.sendTime){
if (msgInfo.selfSend && chat.messages[idx].selfSend &&
chat.messages[idx].sendTime == msgInfo.sendTime) {
chat.messages.splice(idx, 1);
break;
}
@ -171,7 +207,7 @@ export default {
updateChatFromFriend(state, friend) {
for (let i in state.chats) {
let chat = state.chats[i];
if (chat.type=='PRIVATE' && chat.targetId == friend.id) {
if (chat.type == 'PRIVATE' && chat.targetId == friend.id) {
chat.headImage = friend.headImageThumb;
chat.showName = friend.nickName;
break;
@ -182,7 +218,7 @@ export default {
updateChatFromGroup(state, group) {
for (let i in state.chats) {
let chat = state.chats[i];
if (chat.type=='GROUP' && chat.targetId == group.id) {
if (chat.type == 'GROUP' && chat.targetId == group.id) {
chat.headImage = group.headImageThumb;
chat.showName = group.remark;
break;
@ -190,35 +226,50 @@ export default {
}
this.commit("saveToStorage");
},
saveToStorage(state){
loadingPrivateMsg(state, loadding) {
state.loadingPrivateMsg = loadding;
},
loadingGroupMsg(state, loadding) {
state.loadingGroupMsg = loadding;
},
saveToStorage(state) {
let userId = userStore.state.userInfo.id;
let key = "chats-" + userId;
let chatsData = {
privateMsgMaxId: state.privateMsgMaxId,
groupMsgMaxId: state.groupMsgMaxId,
chats: state.chats
}
uni.setStorage({
key:"chats-"+userId,
data: state.chats
key: key,
data: chatsData
})
},
clear(state){
clear(state) {
state.chats = [];
state.activeIndex = -1;
state.privateMsgMaxId = 0;
state.groupMsgMaxId = 0;
state.loadingPrivateMsg = false;
state.loadingGroupMsg = false;
}
},
actions:{
},
actions: {
loadChat(context) {
return new Promise((resolve, reject) => {
let userId = userStore.state.userInfo.id;
uni.getStorage({
key:"chats-"+userId,
key: "chats-" + userId,
success(res) {
context.commit("initChats",res.data);
context.commit("initChats", res.data);
resolve()
},
fail(e) {
// 不存在聊天记录,清空聊天列表
context.commit("initChats",[]);
resolve()
}
});
})
}
}
}
}
Loading…
Cancel
Save