Browse Source

!97 前端性能优化

Merge pull request !97 from blue/v_3.0.0
master
blue 2 years ago
committed by Gitee
parent
commit
30fbd5a931
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
  1. 3
      db/im-platform.sql
  2. 2
      im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java
  3. 20
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  4. 30
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java
  5. 72
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  6. 128
      im-uniapp/App.vue
  7. 4
      im-uniapp/common/request.js
  8. 4
      im-uniapp/components/chat-at-box/chat-at-box.vue
  9. 2
      im-uniapp/components/chat-group-readed/chat-group-readed.vue
  10. 15
      im-uniapp/components/chat-item/chat-item.vue
  11. 2
      im-uniapp/components/group-rtc-join/group-rtc-join.vue
  12. 22
      im-uniapp/main.js
  13. 63
      im-uniapp/pages/chat/chat-box.vue
  14. 4
      im-uniapp/pages/chat/chat-group-video.vue
  15. 4
      im-uniapp/pages/chat/chat-private-video.vue
  16. 35
      im-uniapp/pages/chat/chat.vue
  17. 22
      im-uniapp/pages/common/user-info.vue
  18. 8
      im-uniapp/pages/friend/friend-add.vue
  19. 5
      im-uniapp/pages/friend/friend.vue
  20. 26
      im-uniapp/pages/group/group-edit.vue
  21. 23
      im-uniapp/pages/group/group-info.vue
  22. 9
      im-uniapp/pages/group/group-invite.vue
  23. 6
      im-uniapp/pages/group/group-member.vue
  24. 4
      im-uniapp/pages/group/group.vue
  25. 4
      im-uniapp/pages/login/login.vue
  26. 4
      im-uniapp/pages/mine/mine-edit.vue
  27. 5
      im-uniapp/pages/mine/mine.vue
  28. 352
      im-uniapp/store/chatStore.js
  29. 34
      im-uniapp/store/configStore.js
  30. 112
      im-uniapp/store/friendStore.js
  31. 67
      im-uniapp/store/groupStore.js
  32. 35
      im-uniapp/store/index.js
  33. 42
      im-uniapp/store/userStore.js
  34. 171
      im-web/src/store/chatStore.js
  35. 2
      im-web/src/view/Chat.vue
  36. 2
      im-web/src/view/Friend.vue
  37. 6
      im-web/src/view/Group.vue
  38. 4
      im-web/src/view/Home.vue

3
db/im-platform.sql

@ -35,7 +35,8 @@ create table `im_private_message`(
`type` tinyint(1) NOT NULL comment '消息类型 0:文字 1:图片 2:文件 3:语音 4:视频 21:提示', `type` tinyint(1) NOT NULL comment '消息类型 0:文字 1:图片 2:文件 3:语音 4:视频 21:提示',
`status` tinyint(1) NOT NULL comment '状态 0:未读 1:已读 2:撤回 3:已读', `status` tinyint(1) NOT NULL comment '状态 0:未读 1:已读 2:撤回 3:已读',
`send_time` datetime DEFAULT CURRENT_TIMESTAMP comment '发送时间', `send_time` datetime DEFAULT CURRENT_TIMESTAMP comment '发送时间',
key `idx_send_recv_id` (`send_id`,`recv_id`) key `idx_send_id` (`send_id`),
key `idx_recv_id` (`recv_id`)
)ENGINE=InnoDB CHARSET=utf8mb4 comment '私聊消息'; )ENGINE=InnoDB CHARSET=utf8mb4 comment '私聊消息';

2
im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java

@ -74,7 +74,7 @@ public class GroupMember extends Model<GroupMember> {
private Date quitTime; private Date quitTime;
public String getShowNickName() { public String getShowNickName() {
return StrUtil.isEmpty(remarkNickName) ? userNickName : remarkNickName; return StrUtil.blankToDefault(remarkNickName, userNickName);
} }
} }

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

@ -72,9 +72,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
msg.setSendId(session.getUserId()); msg.setSendId(session.getUserId());
msg.setSendTime(new Date()); msg.setSendTime(new Date());
msg.setSendNickName(member.getShowNickName()); msg.setSendNickName(member.getShowNickName());
if (CollectionUtil.isNotEmpty(dto.getAtUserIds())) { msg.setAtUserIds(CommaTextUtils.asText(dto.getAtUserIds()));
msg.setAtUserIds(StrUtil.join(",", dto.getAtUserIds()));
}
this.save(msg); this.save(msg);
// 过滤内容中的敏感词 // 过滤内容中的敏感词
if(MessageType.TEXT.code().equals(dto.getType())){ if(MessageType.TEXT.code().equals(dto.getType())){
@ -160,13 +158,14 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 开启加载中标志 // 开启加载中标志
this.sendLoadingMessage(true); this.sendLoadingMessage(true);
// 只能拉取最近3个月的,最多拉取3000条 // 只能拉取最近3个月的,最多拉取3000条
Date minDate = DateUtils.addMonths(new Date(), -3); int months = session.getTerminal().equals(IMTerminalType.APP.code()) ? 1 : 3;
Date minDate = DateUtils.addMonths(new Date(), -months);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.gt(GroupMessage::getId, minId) wrapper.gt(GroupMessage::getId, minId)
.gt(GroupMessage::getSendTime, minDate) .gt(GroupMessage::getSendTime, minDate)
.in(GroupMessage::getGroupId, groupIds) .in(GroupMessage::getGroupId, groupIds)
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code()) .ne(GroupMessage::getStatus, MessageStatus.RECALL.code())
.orderByDesc(GroupMessage::getId).last("limit 3000"); .orderByAsc(GroupMessage::getId);
List<GroupMessage> messages = this.list(wrapper); List<GroupMessage> messages = this.list(wrapper);
// 通过群聊对消息进行分组 // 通过群聊对消息进行分组
Map<Long, List<GroupMessage>> messageGroupMap = messages.stream().collect(Collectors.groupingBy(GroupMessage::getGroupId)); Map<Long, List<GroupMessage>> messageGroupMap = messages.stream().collect(Collectors.groupingBy(GroupMessage::getGroupId));
@ -178,8 +177,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
.between(GroupMessage::getSendTime, minDate,quitMember.getQuitTime()) .between(GroupMessage::getSendTime, minDate,quitMember.getQuitTime())
.eq(GroupMessage::getGroupId, quitMember.getGroupId()) .eq(GroupMessage::getGroupId, quitMember.getGroupId())
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code()) .ne(GroupMessage::getStatus, MessageStatus.RECALL.code())
.orderByDesc(GroupMessage::getId) .orderByAsc(GroupMessage::getId);
.last("limit 100");
List<GroupMessage> groupMessages = this.list(wrapper); List<GroupMessage> groupMessages = this.list(wrapper);
messageGroupMap.put(quitMember.getGroupId(),groupMessages); messageGroupMap.put(quitMember.getGroupId(),groupMessages);
groupMemberMap.put(quitMember.getGroupId(),quitMember); groupMemberMap.put(quitMember.getGroupId(),quitMember);
@ -187,8 +185,6 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 推送消息 // 推送消息
AtomicInteger sendCount = new AtomicInteger(); AtomicInteger sendCount = new AtomicInteger();
messageGroupMap.forEach((groupId, groupMessages) -> { messageGroupMap.forEach((groupId, groupMessages) -> {
// id从小到大排序
CollectionUtil.reverse(groupMessages);
// 填充消息状态 // 填充消息状态
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId); String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
Object o = redisTemplate.opsForHash().get(key, session.getUserId().toString()); Object o = redisTemplate.opsForHash().get(key, session.getUserId().toString());
@ -208,10 +204,8 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 组装vo // 组装vo
GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class); GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class);
// 被@用户列表 // 被@用户列表
if (StringUtils.isNotBlank(m.getAtUserIds()) && Objects.nonNull(vo)) { List<String> atIds = CommaTextUtils.asList(m.getAtUserIds());
List<String> atIds = Splitter.on(",").trimResults().splitToList(m.getAtUserIds()); vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
}
// 填充状态 // 填充状态
vo.setStatus(readedMaxId >= m.getId() ? MessageStatus.READED.code() : MessageStatus.UNSEND.code()); vo.setStatus(readedMaxId >= m.getId() ? MessageStatus.READED.code() : MessageStatus.UNSEND.code());
// 针对回执消息填充已读人数 // 针对回执消息填充已读人数

30
im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java

@ -70,8 +70,8 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
groupMemberService.save(member); groupMemberService.save(member);
// 返回 // 返回
vo.setId(group.getId()); vo.setId(group.getId());
vo.setShowNickName(StrUtil.isEmpty(vo.getRemarkNickName()) ? member.getUserNickName() : vo.getRemarkNickName()); vo.setShowNickName(member.getShowNickName());
vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? group.getName() : vo.getRemarkGroupName()); vo.setShowGroupName(StrUtil.blankToDefault(member.getRemarkGroupName(), group.getName()));
log.info("创建群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName()); log.info("创建群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName());
return vo; return vo;
} }
@ -96,8 +96,8 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
group = BeanUtils.copyProperties(vo, Group.class); group = BeanUtils.copyProperties(vo, Group.class);
this.updateById(group); this.updateById(group);
} }
vo.setShowNickName(StrUtil.isEmpty(vo.getRemarkNickName()) ? member.getUserNickName() : vo.getRemarkNickName()); vo.setShowNickName(member.getShowNickName());
vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? group.getName() : vo.getRemarkGroupName()); vo.setShowGroupName(StrUtil.blankToDefault(member.getRemarkGroupName(), group.getName()));
log.info("修改群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName()); log.info("修改群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName());
return vo; return vo;
} }
@ -139,7 +139,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId); String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
redisTemplate.opsForHash().delete(key, userId.toString()); redisTemplate.opsForHash().delete(key, userId.toString());
// 推送退出群聊提示 // 推送退出群聊提示
this.sendTipMessage(groupId, Arrays.asList(userId), "您已退出群聊"); this.sendTipMessage(groupId, List.of(userId), "您已退出群聊");
log.info("退出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId); log.info("退出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
} }
@ -159,7 +159,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId); String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
redisTemplate.opsForHash().delete(key, userId.toString()); redisTemplate.opsForHash().delete(key, userId.toString());
// 推送踢出群聊提示 // 推送踢出群聊提示
this.sendTipMessage(groupId, Arrays.asList(userId), "您已被移出群聊"); this.sendTipMessage(groupId, List.of(userId), "您已被移出群聊");
log.info("踢出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId); log.info("踢出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
} }
@ -178,7 +178,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
vo.setRemarkGroupName(member.getRemarkGroupName()); vo.setRemarkGroupName(member.getRemarkGroupName());
vo.setRemarkNickName(member.getRemarkNickName()); vo.setRemarkNickName(member.getRemarkNickName());
vo.setShowNickName(member.getShowNickName()); vo.setShowNickName(member.getShowNickName());
vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? group.getName() : vo.getRemarkGroupName()); vo.setShowGroupName(StrUtil.blankToDefault(member.getRemarkGroupName(), group.getName()));
vo.setQuit(member.getQuit()); vo.setQuit(member.getQuit());
return vo; return vo;
} }
@ -215,12 +215,12 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
groupWrapper.in(Group::getId, ids); groupWrapper.in(Group::getId, ids);
List<Group> groups = this.list(groupWrapper); List<Group> groups = this.list(groupWrapper);
// 转vo // 转vo
return groups.stream().map(g -> { return groups.stream().map(group -> {
GroupVO vo = BeanUtils.copyProperties(g, GroupVO.class); GroupVO vo = BeanUtils.copyProperties(group, GroupVO.class);
GroupMember member = groupMembers.stream().filter(m -> g.getId().equals(m.getGroupId())).findFirst().get(); GroupMember member =
vo.setShowNickName( groupMembers.stream().filter(m -> group.getId().equals(m.getGroupId())).findFirst().get();
StrUtil.isEmpty(vo.getRemarkNickName()) ? session.getNickName() : vo.getRemarkNickName()); vo.setShowNickName(StrUtil.blankToDefault(member.getRemarkNickName(), session.getNickName()));
vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? g.getName() : vo.getRemarkGroupName()); vo.setShowGroupName(StrUtil.blankToDefault(member.getRemarkGroupName(), group.getName()));
vo.setQuit(member.getQuit()); vo.setQuit(member.getQuit());
return vo; return vo;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
@ -244,7 +244,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
List<Friend> friends = friendsService.findFriendByUserId(session.getUserId()); List<Friend> friends = friendsService.findFriendByUserId(session.getUserId());
List<Friend> friendsList = vo.getFriendIds().stream() List<Friend> friendsList = vo.getFriendIds().stream()
.map(id -> friends.stream().filter(f -> f.getFriendId().equals(id)).findFirst().get()) .map(id -> friends.stream().filter(f -> f.getFriendId().equals(id)).findFirst().get())
.collect(Collectors.toList()); .toList();
if (friendsList.size() != vo.getFriendIds().size()) { if (friendsList.size() != vo.getFriendIds().size()) {
throw new GlobalException("部分用户不是您的好友,邀请失败"); throw new GlobalException("部分用户不是您的好友,邀请失败");
} }
@ -282,7 +282,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
return members.stream().map(m -> { return members.stream().map(m -> {
GroupMemberVO vo = BeanUtils.copyProperties(m, GroupMemberVO.class); GroupMemberVO vo = BeanUtils.copyProperties(m, GroupMemberVO.class);
vo.setShowNickName(m.getShowNickName()); vo.setShowNickName(m.getShowNickName());
vo.setShowGroupName(StrUtil.isEmpty(m.getRemarkGroupName()) ? group.getName() : m.getRemarkGroupName()); vo.setShowGroupName(StrUtil.blankToDefault(m.getRemarkGroupName(), group.getName()));
vo.setOnline(onlineUserIds.contains(m.getUserId())); vo.setOnline(onlineUserIds.contains(m.getUserId()));
return vo; return vo;
}).sorted((m1, m2) -> m2.getOnline().compareTo(m1.getOnline())).collect(Collectors.toList()); }).sorted((m1, m2) -> m2.getOnline().compareTo(m1.getOnline())).collect(Collectors.toList());

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

@ -37,8 +37,8 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper, PrivateMessage> implements public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper, PrivateMessage>
PrivateMessageService { implements PrivateMessageService {
private final FriendService friendService; private final FriendService friendService;
private final IMClient imClient; private final IMClient imClient;
@ -58,7 +58,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
msg.setSendTime(new Date()); msg.setSendTime(new Date());
this.save(msg); this.save(msg);
// 过滤内容中的敏感词 // 过滤内容中的敏感词
if(MessageType.TEXT.code().equals(dto.getType())){ if (MessageType.TEXT.code().equals(dto.getType())) {
msg.setContent(sensitiveFilterUtil.filter(dto.getContent())); msg.setContent(sensitiveFilterUtil.filter(dto.getContent()));
} }
// 推送消息 // 推送消息
@ -112,7 +112,6 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
log.info("撤回私聊消息,发送id:{},接收id:{},内容:{}", msg.getSendId(), msg.getRecvId(), msg.getContent()); log.info("撤回私聊消息,发送id:{},接收id:{},内容:{}", msg.getSendId(), msg.getRecvId(), msg.getContent());
} }
@Override @Override
public List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page, Long size) { public List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page, Long size) {
page = page > 0 ? page : 1; page = page > 0 ? page : 1;
@ -120,17 +119,16 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
Long userId = SessionContext.getSession().getUserId(); Long userId = SessionContext.getSession().getUserId();
long stIdx = (page - 1) * size; long stIdx = (page - 1) * size;
QueryWrapper<PrivateMessage> wrapper = new QueryWrapper<>(); QueryWrapper<PrivateMessage> wrapper = new QueryWrapper<>();
wrapper.lambda().and(wrap -> wrap.and( wrapper.lambda().and(
wp -> wp.eq(PrivateMessage::getSendId, userId) wrap -> wrap.and(wp -> wp.eq(PrivateMessage::getSendId, userId).eq(PrivateMessage::getRecvId, friendId))
.eq(PrivateMessage::getRecvId, friendId)) .or(wp -> wp.eq(PrivateMessage::getRecvId, userId).eq(PrivateMessage::getSendId, friendId)))
.or(wp -> wp.eq(PrivateMessage::getRecvId, userId) .ne(PrivateMessage::getStatus, MessageStatus.RECALL.code()).orderByDesc(PrivateMessage::getId)
.eq(PrivateMessage::getSendId, friendId)))
.ne(PrivateMessage::getStatus, MessageStatus.RECALL.code())
.orderByDesc(PrivateMessage::getId)
.last("limit " + stIdx + "," + size); .last("limit " + stIdx + "," + size);
List<PrivateMessage> messages = this.list(wrapper); List<PrivateMessage> messages = this.list(wrapper);
List<PrivateMessageVO> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class)).collect(Collectors.toList()); List<PrivateMessageVO> messageInfos =
messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class))
.collect(Collectors.toList());
log.info("拉取聊天记录,用户id:{},好友id:{},数量:{}", userId, friendId, messageInfos.size()); log.info("拉取聊天记录,用户id:{},好友id:{},数量:{}", userId, friendId, messageInfos.size());
return messageInfos; return messageInfos;
} }
@ -138,7 +136,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
@Override @Override
public void pullOfflineMessage(Long minId) { public void pullOfflineMessage(Long minId) {
UserSession session = SessionContext.getSession(); UserSession session = SessionContext.getSession();
if(!imClient.isOnline(session.getUserId())){ if (!imClient.isOnline(session.getUserId())) {
throw new GlobalException("网络连接失败,无法拉取离线消息"); throw new GlobalException("网络连接失败,无法拉取离线消息");
} }
// 查询用户好友列表 // 查询用户好友列表
@ -153,28 +151,22 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
List<Long> friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList()); List<Long> friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList());
// 获取当前用户的消息 // 获取当前用户的消息
LambdaQueryWrapper<PrivateMessage> queryWrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<PrivateMessage> queryWrapper = Wrappers.lambdaQuery();
// 只能拉取最近3个月的3000条消息 // 只能拉取最近3个月的消息,移动端只拉取一个月消息
Date minDate = DateUtils.addMonths(new Date(), -3); int months = session.getTerminal().equals(IMTerminalType.APP.code()) ? 1 : 3;
queryWrapper.gt(PrivateMessage::getId, minId) Date minDate = DateUtils.addMonths(new Date(), -months);
.ge(PrivateMessage::getSendTime, minDate) queryWrapper.gt(PrivateMessage::getId, minId).ge(PrivateMessage::getSendTime, minDate)
.ne(PrivateMessage::getStatus, MessageStatus.RECALL.code()) .ne(PrivateMessage::getStatus, MessageStatus.RECALL.code()).and(wrap -> wrap.and(
.and(wrap -> wrap.and( wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).in(PrivateMessage::getRecvId, friendIds))
wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()) .or(wp -> wp.eq(PrivateMessage::getRecvId, session.getUserId()).in(PrivateMessage::getSendId, friendIds)))
.in(PrivateMessage::getRecvId, friendIds)) .orderByAsc(PrivateMessage::getId);
.or(wp -> wp.eq(PrivateMessage::getRecvId, session.getUserId())
.in(PrivateMessage::getSendId, friendIds)))
.orderByDesc(PrivateMessage::getId)
.last("limit 3000");
List<PrivateMessage> messages = this.list(queryWrapper); List<PrivateMessage> messages = this.list(queryWrapper);
// 消息顺序从小到大
CollectionUtil.reverse(messages);
// 推送消息 // 推送消息
for(PrivateMessage m:messages ){ for (PrivateMessage m : messages) {
PrivateMessageVO vo = BeanUtils.copyProperties(m, PrivateMessageVO.class); PrivateMessageVO vo = BeanUtils.copyProperties(m, PrivateMessageVO.class);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>(); IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(m.getSendId(), IMTerminalType.WEB.code())); sendMessage.setSender(new IMUserInfo(m.getSendId(), IMTerminalType.WEB.code()));
sendMessage.setRecvId(session.getUserId()); sendMessage.setRecvId(session.getUserId());
sendMessage.setRecvTerminals(Arrays.asList(session.getTerminal())); sendMessage.setRecvTerminals(List.of(session.getTerminal()));
sendMessage.setSendToSelf(false); sendMessage.setSendToSelf(false);
sendMessage.setData(vo); sendMessage.setData(vo);
sendMessage.setSendResult(true); sendMessage.setSendResult(true);
@ -214,42 +206,36 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
imClient.sendPrivateMessage(sendMessage); imClient.sendPrivateMessage(sendMessage);
// 修改消息状态为已读 // 修改消息状态为已读
LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate(); LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(PrivateMessage::getSendId, friendId) updateWrapper.eq(PrivateMessage::getSendId, friendId).eq(PrivateMessage::getRecvId, session.getUserId())
.eq(PrivateMessage::getRecvId, session.getUserId())
.eq(PrivateMessage::getStatus, MessageStatus.SENDED.code()) .eq(PrivateMessage::getStatus, MessageStatus.SENDED.code())
.set(PrivateMessage::getStatus, MessageStatus.READED.code()); .set(PrivateMessage::getStatus, MessageStatus.READED.code());
this.update(updateWrapper); this.update(updateWrapper);
log.info("消息已读,接收方id:{},发送方id:{}", session.getUserId(), friendId); log.info("消息已读,接收方id:{},发送方id:{}", session.getUserId(), friendId);
} }
@Override @Override
public Long getMaxReadedId(Long friendId) { public Long getMaxReadedId(Long friendId) {
UserSession session = SessionContext.getSession(); UserSession session = SessionContext.getSession();
LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
wrapper.eq(PrivateMessage::getSendId, session.getUserId()) wrapper.eq(PrivateMessage::getSendId, session.getUserId()).eq(PrivateMessage::getRecvId, friendId)
.eq(PrivateMessage::getRecvId, friendId) .eq(PrivateMessage::getStatus, MessageStatus.READED.code()).orderByDesc(PrivateMessage::getId)
.eq(PrivateMessage::getStatus, MessageStatus.READED.code()) .select(PrivateMessage::getId).last("limit 1");
.orderByDesc(PrivateMessage::getId)
.select(PrivateMessage::getId)
.last("limit 1");
PrivateMessage message = this.getOne(wrapper); PrivateMessage message = this.getOne(wrapper);
if(Objects.isNull(message)){ if (Objects.isNull(message)) {
return -1L; return -1L;
} }
return message.getId(); return message.getId();
} }
private void sendLoadingMessage(Boolean isLoadding) {
private void sendLoadingMessage(Boolean isLoadding){
UserSession session = SessionContext.getSession(); UserSession session = SessionContext.getSession();
PrivateMessageVO msgInfo = new PrivateMessageVO(); PrivateMessageVO msgInfo = new PrivateMessageVO();
msgInfo.setType(MessageType.LOADING.code()); msgInfo.setType(MessageType.LOADING.code());
msgInfo.setContent(isLoadding.toString()); msgInfo.setContent(isLoadding.toString());
IMPrivateMessage sendMessage = new IMPrivateMessage<>(); IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(session.getUserId()); sendMessage.setRecvId(session.getUserId());
sendMessage.setRecvTerminals(Arrays.asList(session.getTerminal())); sendMessage.setRecvTerminals(List.of(session.getTerminal()));
sendMessage.setData(msgInfo); sendMessage.setData(msgInfo);
sendMessage.setSendToSelf(false); sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false); sendMessage.setSendResult(false);

128
im-uniapp/App.vue

@ -1,5 +1,5 @@
<script> <script>
import store from './store'; import App from './App'
import http from './common/request'; import http from './common/request';
import * as msgType from './common/messageType'; import * as msgType from './common/messageType';
import * as enums from './common/enums'; import * as enums from './common/enums';
@ -18,7 +18,7 @@
init() { init() {
this.isExit = false; this.isExit = false;
// //
store.dispatch("load").then(() => { this.loadStore().then(() => {
// websocket // websocket
this.initWebSocket(); this.initWebSocket();
}).catch((e) => { }).catch((e) => {
@ -40,8 +40,8 @@
}) })
} }
// 线 // 线
this.pullPrivateOfflineMessage(store.state.chatStore.privateMsgMaxId); this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(store.state.chatStore.groupMsgMaxId); this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId);
}); });
wsApi.onMessage((cmd, msgInfo) => { wsApi.onMessage((cmd, msgInfo) => {
if (cmd == 2) { if (cmd == 2) {
@ -66,36 +66,53 @@
console.log("ws断开", res); console.log("ws断开", res);
// //
this.reconnectWs(); this.reconnectWs();
})
},
loadStore() {
return this.userStore.loadUser().then(() => {
const promises = [];
promises.push(this.friendStore.loadFriend());
promises.push(this.groupStore.loadGroup());
promises.push(this.chatStore.loadChat());
promises.push(this.configStore.loadConfig());
return Promise.all(promises);
}) })
}, },
unloadStore(){
this.friendStore.clear();
this.groupStore.clear();
this.chatStore.clear();
this.configStore.clear();
this.userStore.clear();
},
pullPrivateOfflineMessage(minId) { pullPrivateOfflineMessage(minId) {
store.commit("loadingPrivateMsg", true) this.chatStore.setLoadingPrivateMsg(true)
http({ http({
url: "/message/private/pullOfflineMessage?minId=" + minId, url: "/message/private/pullOfflineMessage?minId=" + minId,
method: 'GET' method: 'GET'
}).catch(() => { }).catch(() => {
store.commit("loadingPrivateMsg", false) this.chatStore.setLoadingPrivateMsg(false)
}) })
}, },
pullGroupOfflineMessage(minId) { pullGroupOfflineMessage(minId) {
store.commit("loadingGroupMsg", true) this.chatStore.setLoadingGroupMsg(true)
http({ http({
url: "/message/group/pullOfflineMessage?minId=" + minId, url: "/message/group/pullOfflineMessage?minId=" + minId,
method: 'GET' method: 'GET'
}).catch(() => { }).catch(() => {
store.commit("loadingGroupMsg", false) this.chatStore.setLoadingGroupMsg(false)
}) })
}, },
handlePrivateMessage(msg) { handlePrivateMessage(msg) {
// //
if (msg.type == enums.MESSAGE_TYPE.LOADING) { if (msg.type == enums.MESSAGE_TYPE.LOADING) {
store.commit("loadingPrivateMsg", JSON.parse(msg.content)) this.chatStore.setLoadingPrivateMsg(JSON.parse(msg.content))
return; return;
} }
// //
if (msg.type == enums.MESSAGE_TYPE.READED) { if (msg.type == enums.MESSAGE_TYPE.READED) {
store.commit("resetUnreadCount", { this.chatStore.resetUnreadCount({
type: 'PRIVATE', type: 'PRIVATE',
targetId: msg.recvId targetId: msg.recvId
}) })
@ -103,19 +120,18 @@
} }
// , // ,
if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { if (msg.type == enums.MESSAGE_TYPE.RECEIPT) {
store.commit("readedMessage", { this.chatStore.readedMessage({
friendId: msg.sendId friendId: msg.sendId
}) })
return; return;
} }
// //
msg.selfSend = msg.sendId == store.state.userStore.userInfo.id; msg.selfSend = msg.sendId == this.userStore.userInfo.id;
// id // id
let friendId = msg.selfSend ? msg.recvId : msg.sendId; let friendId = msg.selfSend ? msg.recvId : msg.sendId;
this.loadFriendInfo(friendId).then((friend) => { this.loadFriendInfo(friendId, (friend) => {
this.insertPrivateMessage(friend, msg); this.insertPrivateMessage(friend, msg);
}) })
}, },
insertPrivateMessage(friend, msg) { insertPrivateMessage(friend, msg) {
// //
@ -151,9 +167,9 @@
headImage: friend.headImage headImage: friend.headImage
}; };
// //
store.commit("openChat", chatInfo); this.chatStore.openChat(chatInfo);
// //
store.commit("insertMessage", msg); this.chatStore.insertMessage(msg);
// //
this.playAudioTip(); this.playAudioTip();
@ -161,7 +177,7 @@
handleGroupMessage(msg) { handleGroupMessage(msg) {
// //
if (msg.type == enums.MESSAGE_TYPE.LOADING) { if (msg.type == enums.MESSAGE_TYPE.LOADING) {
store.commit("loadingGroupMsg", JSON.parse(msg.content)) this.chatStore.setLoadingGroupMsg(JSON.parse(msg.content))
return; return;
} }
// //
@ -171,7 +187,7 @@
type: 'GROUP', type: 'GROUP',
targetId: msg.groupId targetId: msg.groupId
} }
store.commit("resetUnreadCount", chatInfo) this.chatStore.resetUnreadCount(chatInfo)
return; return;
} }
// //
@ -183,12 +199,12 @@
readedCount: msg.readedCount, readedCount: msg.readedCount,
receiptOk: msg.receiptOk receiptOk: msg.receiptOk
}; };
store.commit("updateMessage", msgInfo) this.chatStore.updateMessage(msgInfo)
return; return;
} }
// //
msg.selfSend = msg.sendId == store.state.userStore.userInfo.id; msg.selfSend = msg.sendId == this.userStore.userInfo.id;
this.loadGroupInfo(msg.groupId).then((group) => { this.loadGroupInfo(msg.groupId, (group) => {
// //
this.insertGroupMessage(group, msg); this.insertGroupMessage(group, msg);
}) })
@ -241,43 +257,39 @@
headImage: group.headImageThumb headImage: group.headImageThumb
}; };
// //
store.commit("openChat", chatInfo); this.chatStore.openChat(chatInfo);
// //
store.commit("insertMessage", msg); this.chatStore.insertMessage(msg);
// //
this.playAudioTip(); this.playAudioTip();
}, },
loadFriendInfo(id) { loadFriendInfo(id, callback) {
return new Promise((resolve, reject) => { let friend = this.friendStore.findFriend(id);
let friend = store.getters.findFriend(id); if (friend) {
if (friend) { callback(friend);
resolve(friend); } else {
} else { http({
http({ url: `/friend/find/${id}`,
url: `/friend/find/${id}`, method: 'GET'
method: 'GET' }).then((friend) => {
}).then((friend) => { this.friendStore.addFriend(friend);
store.commit("addFriend", friend); callback(friend)
resolve(friend) })
}) }
}
});
}, },
loadGroupInfo(id) { loadGroupInfo(id, callback) {
return new Promise((resolve, reject) => { let group = this.groupStore.findGroup(id);
let group = store.state.groupStore.groups.find((g) => g.id == id); if (group) {
if (group) { callback(group);
resolve(group); } else {
} else { http({
http({ url: `/group/find/${id}`,
url: `/group/find/${id}`, method: 'GET'
method: 'GET' }).then((group) => {
}).then((group) => { this.groupStore.addGroup(group);
resolve(group) callback(group)
store.commit("addGroup", group); })
}) }
}
});
}, },
exit() { exit() {
console.log("exit"); console.log("exit");
@ -287,7 +299,7 @@
uni.reLaunch({ uni.reLaunch({
url: "/pages/login/login" url: "/pages/login/login"
}) })
store.dispatch("unload"); this.unloadStore();
}, },
playAudioTip() { playAudioTip() {
// //
@ -314,7 +326,7 @@
title: '连接已断开,尝试重新连接...', title: '连接已断开,尝试重新连接...',
icon: 'none', icon: 'none',
}) })
store.commit("setUserInfo", userInfo); this.userStore.setUserInfo(userInfo);
// //
let loginInfo = uni.getStorageSync("loginInfo") let loginInfo = uni.getStorageSync("loginInfo")
wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken); wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken);
@ -333,10 +345,10 @@
} }
}, },
onLaunch() { onLaunch() {
this.$mountStore();
// //
let loginInfo = uni.getStorageSync("loginInfo") let loginInfo = uni.getStorageSync("loginInfo")
if (!this.isExpired(loginInfo)) { if (!this.isExpired(loginInfo)) {
console.log("初始化")
// //
this.init(); this.init();
// //
@ -367,7 +379,7 @@
.tab-page { .tab-page {
// #ifdef H5 // #ifdef H5
height: calc(100vh - 50px); // h5100vh height: calc(100vh - 50px); // h5100vh
// #endif // #endif
// #ifndef H5 // #ifndef H5
height: calc(100vh); height: calc(100vh);

4
im-uniapp/common/request.js

@ -21,7 +21,7 @@ const request = (options) => {
if (res.data.code == 200) { if (res.data.code == 200) {
return resolve(res.data.data) return resolve(res.data.data)
} else if (res.data.code == 400) { } else if (res.data.code == 400) {
getApp().exit(); getApp().$vm.exit();
} else if (res.data.code == 401) { } else if (res.data.code == 401) {
console.log("token失效,尝试重新获取") console.log("token失效,尝试重新获取")
if (isRefreshToken) { if (isRefreshToken) {
@ -38,7 +38,7 @@ const request = (options) => {
requestList = []; requestList = [];
isRefreshToken = false; isRefreshToken = false;
console.log("刷新token失败") console.log("刷新token失败")
getApp().exit(); getApp().$vm.exit();
return; return;
} }
let newInfo = res.data.data; let newInfo = res.data.data;

4
im-uniapp/components/chat-at-box/chat-at-box.vue

@ -56,7 +56,7 @@
methods: { methods: {
init(atUserIds) { init(atUserIds) {
this.showMembers = []; this.showMembers = [];
let userId = this.$store.state.userStore.userInfo.id; let userId = this.userStore.userInfo.id;
if(this.ownerId == userId){ if(this.ownerId == userId){
this.showMembers.push({ this.showMembers.push({
userId:-1, userId:-1,
@ -64,7 +64,7 @@
}) })
} }
this.members.forEach((m) => { this.members.forEach((m) => {
if(m.userId != userId){ if(!m.quit && m.userId != userId){
m.checked = atUserIds.indexOf(m.userId) >= 0; m.checked = atUserIds.indexOf(m.userId) >= 0;
this.showMembers.push(m); this.showMembers.push(m);
} }

2
im-uniapp/components/chat-group-readed/chat-group-readed.vue

@ -79,7 +79,7 @@
this.items[0] = `已读(${this.readedMembers.length})`; this.items[0] = `已读(${this.readedMembers.length})`;
this.items[1] = `未读(${this.unreadMembers.length})`; this.items[1] = `未读(${this.unreadMembers.length})`;
// //
this.$store.commit("updateMessage", { this.chatStore.updateMessage({
id: this.msgInfo.id, id: this.msgInfo.id,
groupId: this.msgInfo.groupId, groupId: this.msgInfo.groupId,
readedCount: this.readedMembers.length readedCount: this.readedMembers.length

15
im-uniapp/components/chat-item/chat-item.vue

@ -17,7 +17,7 @@
</view> </view>
<view class="chat-content"> <view class="chat-content">
<view class="chat-at-text">{{atText}}</view> <view class="chat-at-text">{{atText}}</view>
<view class="chat-send-name" v-show="chat.sendNickName">{{chat.sendNickName+':&nbsp;'}}</view> <view class="chat-send-name" v-if="isShowSendName">{{chat.sendNickName+':&nbsp;'}}</view>
<rich-text class="chat-content-text" :nodes="$emo.transform(chat.lastContent)"></rich-text> <rich-text class="chat-content-text" :nodes="$emo.transform(chat.lastContent)"></rich-text>
<uni-badge v-if="chat.unreadCount>0" size="small" :max-num="99" :text="chat.unreadCount" /> <uni-badge v-if="chat.unreadCount>0" size="small" :max-num="99" :text="chat.unreadCount" />
</view> </view>
@ -44,6 +44,7 @@
} }
}, },
methods: { methods: {
showChatBox() { showChatBox() {
uni.navigateTo({ uni.navigateTo({
url: "/pages/chat/chat-box?chatIdx=" + this.index url: "/pages/chat/chat-box?chatIdx=" + this.index
@ -51,6 +52,18 @@
} }
}, },
computed: { computed: {
isShowSendName() {
if (!this.chat.sendNickName) {
return false;
}
let size = this.chat.messages.length;
if (size == 0) {
return false;
}
//
let lastMsg = this.chat.messages[size - 1];
return this.$msgType.isNormal(lastMsg.type)
},
atText() { atText() {
if (this.chat.atMe) { if (this.chat.atMe) {
return "[有人@我]" return "[有人@我]"

2
im-uniapp/components/group-rtc-join/group-rtc-join.vue

@ -41,7 +41,7 @@
}, },
onOk() { onOk() {
let users = this.rtcInfo.userInfos; let users = this.rtcInfo.userInfos;
let mine = this.$store.state.userStore.userInfo; let mine = this.userStore.userInfo;
// //
if(!users.find((user)=>user.id==mine.id)){ if(!users.find((user)=>user.id==mine.id)){
users.push({ users.push({

22
im-uniapp/main.js

@ -5,9 +5,15 @@ import * as enums from './common/enums.js';
import * as date from './common/date'; import * as date from './common/date';
import * as socketApi from './common/wssocket'; import * as socketApi from './common/wssocket';
import * as messageType from './common/messageType'; import * as messageType from './common/messageType';
import store from './store';
import { createSSRApp } from 'vue' import { createSSRApp } from 'vue'
import uviewPlus from '@/uni_modules/uview-plus' import uviewPlus from '@/uni_modules/uview-plus'
import * as pinia from 'pinia';
import useChatStore from '@/store/chatStore.js'
import useFriendStore from '@/store/friendStore.js'
import useGroupStore from '@/store/groupStore.js'
import useConfigStore from '@/store/configStore.js'
import useUserStore from '@/store/userStore.js'
// #ifdef H5 // #ifdef H5
import * as recorder from './common/recorder-h5'; import * as recorder from './common/recorder-h5';
// #endif // #endif
@ -16,10 +22,11 @@ import * as recorder from './common/recorder-app';
// #endif // #endif
export function createApp() { export function createApp() {
const app = createSSRApp(App) const app = createSSRApp(App)
app.use(store);
app.use(uviewPlus); app.use(uviewPlus);
app.use(pinia.createPinia());
app.config.globalProperties.$http = request; app.config.globalProperties.$http = request;
app.config.globalProperties.$wsApi = socketApi; app.config.globalProperties.$wsApi = socketApi;
app.config.globalProperties.$msgType = messageType; app.config.globalProperties.$msgType = messageType;
@ -27,7 +34,16 @@ export function createApp() {
app.config.globalProperties.$enums = enums; app.config.globalProperties.$enums = enums;
app.config.globalProperties.$date = date; app.config.globalProperties.$date = date;
app.config.globalProperties.$rc = recorder; app.config.globalProperties.$rc = recorder;
// 初始化时再挂载store对象
app.config.globalProperties.$mountStore = ()=>{
app.config.globalProperties.chatStore = useChatStore();
app.config.globalProperties.friendStore = useFriendStore();
app.config.globalProperties.groupStore = useGroupStore();
app.config.globalProperties.configStore = useConfigStore();
app.config.globalProperties.userStore = useUserStore();
}
return { return {
app app,
pinia
} }
} }

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

@ -9,7 +9,7 @@
<scroll-view class="scroll-box" scroll-y="true" upper-threshold="200" @scrolltoupper="onScrollToTop" <scroll-view class="scroll-box" scroll-y="true" upper-threshold="200" @scrolltoupper="onScrollToTop"
:scroll-into-view="'chat-item-'+scrollMsgIdx"> :scroll-into-view="'chat-item-'+scrollMsgIdx">
<view v-if="chat" v-for="(msgInfo,idx) in chat.messages" :key="idx"> <view v-if="chat" v-for="(msgInfo,idx) in chat.messages" :key="idx">
<chat-message-item v-if="idx>=showMinIdx&&!msgInfo.delete" :headImage="headImage(msgInfo)" <chat-message-item v-if="idx>=showMinIdx" :headImage="headImage(msgInfo)"
@call="onRtCall(msgInfo)" :showName="showName(msgInfo)" @recall="onRecallMessage" @call="onRtCall(msgInfo)" :showName="showName(msgInfo)" @recall="onRecallMessage"
@delete="onDeleteMessage" @longPressHead="onLongPressHead(msgInfo)" @download="onDownloadFile" @delete="onDeleteMessage" @longPressHead="onLongPressHead(msgInfo)" @download="onDownloadFile"
:id="'chat-item-'+idx" :msgInfo="msgInfo" :groupMembers="groupMembers"> :id="'chat-item-'+idx" :msgInfo="msgInfo" :groupMembers="groupMembers">
@ -108,15 +108,18 @@
<chat-at-box ref="atBox" :ownerId="group.ownerId" :members="groupMembers" <chat-at-box ref="atBox" :ownerId="group.ownerId" :members="groupMembers"
@complete="onAtComplete"></chat-at-box> @complete="onAtComplete"></chat-at-box>
<!-- 群语音通话时选择成员 --> <!-- 群语音通话时选择成员 -->
<!-- #ifndef MP-WEIXIN -->
<group-member-selector ref="selBox" :members="groupMembers" <group-member-selector ref="selBox" :members="groupMembers"
:maxSize="$store.state.configStore.webrtc.maxChannel" :maxSize="configStore.webrtc.maxChannel"
@complete="onInviteOk"></group-member-selector> @complete="onInviteOk"></group-member-selector>
<group-rtc-join ref="rtcJoin" :groupId="group.id"></group-rtc-join> <group-rtc-join ref="rtcJoin" :groupId="group.id"></group-rtc-join>
<!-- #endif -->
</view> </view>
</template> </template>
<script> <script>
import UNI_APP from '@/.env.js'; import UNI_APP from '@/.env.js';
export default { export default {
data() { data() {
return { return {
@ -157,7 +160,7 @@
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
this.sendMessageRequest(msgInfo).then((m) => { this.sendMessageRequest(msgInfo).then((m) => {
m.selfSend = true; m.selfSend = true;
this.$store.commit("insertMessage", m); this.chatStore.insertMessage(m);
// //
this.moveChatToTop(); this.moveChatToTop();
// //
@ -226,8 +229,8 @@
}) })
}, },
moveChatToTop() { moveChatToTop() {
let chatIdx = this.$store.getters.findChatIdx(this.chat); let chatIdx = this.chatStore.findChatIdx(this.chat);
this.$store.commit("moveTop", chatIdx); this.chatStore.moveTop(chatIdx);
}, },
switchReceipt() { switchReceipt() {
this.isReceipt = !this.isReceipt; this.isReceipt = !this.isReceipt;
@ -261,6 +264,7 @@
} }
}, },
sendTextMessage() { sendTextMessage() {
const timeStamp = new Date().getTime();
if (!this.sendText.trim() && this.atUserIds.length == 0) { if (!this.sendText.trim() && this.atUserIds.length == 0) {
return uni.showToast({ return uni.showToast({
title: "不能发送空白信息", title: "不能发送空白信息",
@ -279,8 +283,10 @@
// id // id
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
this.sendMessageRequest(msgInfo).then((m) => { this.sendMessageRequest(msgInfo).then((m) => {
console.log("请求耗时:",new Date().getTime()-timeStamp)
m.selfSend = true; m.selfSend = true;
this.$store.commit("insertMessage", m); this.chatStore.insertMessage(m);
console.log("insertMessage耗时:",new Date().getTime()-timeStamp)
// //
this.moveChatToTop(); this.moveChatToTop();
}).finally(() => { }).finally(() => {
@ -387,7 +393,7 @@
// id // id
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
// //
this.$store.commit("insertMessage", msgInfo); this.chatStore.insertMessage(msgInfo);
// //
this.moveChatToTop(); this.moveChatToTop();
// file // file
@ -404,13 +410,13 @@
msgInfo.loadStatus = 'ok'; msgInfo.loadStatus = 'ok';
msgInfo.id = m.id; msgInfo.id = m.id;
this.isReceipt = false; this.isReceipt = false;
this.$store.commit("insertMessage", msgInfo); this.chatStore.insertMessage(msgInfo);
}) })
}, },
onUploadImageFail(file, err) { onUploadImageFail(file, err) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.$store.commit("insertMessage", msgInfo); this.chatStore.insertMessage(msgInfo);
}, },
onUploadFileBefore(file) { onUploadFileBefore(file) {
let data = { let data = {
@ -433,7 +439,7 @@
// id // id
this.fillTargetId(msgInfo, this.chat.targetId); this.fillTargetId(msgInfo, this.chat.targetId);
// //
this.$store.commit("insertMessage", msgInfo); this.chatStore.insertMessage(msgInfo);
// //
this.moveChatToTop(); this.moveChatToTop();
// file // file
@ -455,13 +461,13 @@
msgInfo.loadStatus = 'ok'; msgInfo.loadStatus = 'ok';
msgInfo.id = m.id; msgInfo.id = m.id;
this.isReceipt = false; this.isReceipt = false;
this.$store.commit("insertMessage", msgInfo); this.chatStore.insertMessage(msgInfo);
}) })
}, },
onUploadFileFail(file, res) { onUploadFileFail(file, res) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.loadStatus = 'fail'; msgInfo.loadStatus = 'fail';
this.$store.commit("insertMessage", msgInfo); this.chatStore.insertMessage(msgInfo);
}, },
onDeleteMessage(msgInfo) { onDeleteMessage(msgInfo) {
uni.showModal({ uni.showModal({
@ -469,7 +475,7 @@
content: '确认删除消息?', content: '确认删除消息?',
success: (res) => { success: (res) => {
if (!res.cancel) { if (!res.cancel) {
this.$store.commit("deleteMessage", msgInfo); this.chatStore.deleteMessage(msgInfo);
uni.showToast({ uni.showToast({
title: "删除成功", title: "删除成功",
icon: "none" icon: "none"
@ -493,7 +499,7 @@
msgInfo.type = this.$enums.MESSAGE_TYPE.RECALL; msgInfo.type = this.$enums.MESSAGE_TYPE.RECALL;
msgInfo.content = '你撤回了一条消息'; msgInfo.content = '你撤回了一条消息';
msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL; msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL;
this.$store.commit("insertMessage", msgInfo); this.chatStore.insertMessage(msgInfo);
}) })
} }
} }
@ -553,13 +559,13 @@
}) })
} }
}, },
loadReaded(fId) { loadReaded(fid) {
this.$http({ this.$http({
url: `/message/private/maxReadedId?friendId=${fId}`, url: `/message/private/maxReadedId?friendId=${fid}`,
method: 'get' method: 'get'
}).then((id) => { }).then((id) => {
this.$store.commit("readedMessage", { this.chatStore.readedMessage({
friendId: fId, friendId: fid,
maxId: id maxId: id
}); });
}); });
@ -578,7 +584,7 @@
url: url, url: url,
method: 'PUT' method: 'PUT'
}).then(() => { }).then(() => {
this.$store.commit("resetUnreadCount", this.chat) this.chatStore.resetUnreadCount(this.chat)
this.scrollToBottom(); this.scrollToBottom();
}) })
}, },
@ -588,8 +594,8 @@
method: 'GET' method: 'GET'
}).then((group) => { }).then((group) => {
this.group = group; this.group = group;
this.$store.commit("updateChatFromGroup", group); this.chatStore.updateChatFromGroup(group);
this.$store.commit("updateGroup", group); this.groupStore.updateGroup(group);
}); });
this.$http({ this.$http({
@ -606,8 +612,8 @@
method: 'GET' method: 'GET'
}).then((friend) => { }).then((friend) => {
this.friend = friend; this.friend = friend;
this.$store.commit("updateChatFromFriend", friend); this.chatStore.updateChatFromFriend(friend);
this.$store.commit("updateFriend", friend); this.friendStore.updateFriend(friend);
}) })
}, },
rpxTopx(rpx) { rpxTopx(rpx) {
@ -649,7 +655,7 @@
}, },
computed: { computed: {
mine() { mine() {
return this.$store.state.userStore.userInfo; return this.userStore.userInfo;
}, },
title() { title() {
if (!this.chat) { if (!this.chat) {
@ -719,7 +725,7 @@
}, },
onLoad(options) { onLoad(options) {
// //
this.chat = this.$store.state.chatStore.chats[options.chatIdx]; this.chat = this.chatStore.chats[options.chatIdx];
// 20 // 20
let size = this.messageSize; let size = this.messageSize;
this.showMinIdx = size > 20 ? size - 20 : 0; this.showMinIdx = size > 20 ? size - 20 : 0;
@ -733,13 +739,10 @@
this.loadReaded(this.chat.targetId) this.loadReaded(this.chat.targetId)
} }
// //
this.$store.commit("activeChat", options.chatIdx); this.chatStore.activeChat(options.chatIdx);
// //
this.isReceipt = false; this.isReceipt = false;
}, },
onUnload() {
this.$store.commit("activeChat", -1);
},
onShow(){ onShow(){
if(this.needScrollToBottom){ if(this.needScrollToBottom){
// //
@ -835,7 +838,7 @@
margin-bottom: 10rpx; margin-bottom: 10rpx;
border: #dddddd solid 1px; border: #dddddd solid 1px;
background-color: #f7f8fd; background-color: #f7f8fd;
height: 80rpx;
.iconfont { .iconfont {
font-size: 68rpx; font-size: 68rpx;
margin: 6rpx; margin: 6rpx;

4
im-uniapp/pages/chat/chat-group-video.vue

@ -67,12 +67,12 @@
this.url = "/hybrid/html/rtc-group/index.html?"; this.url = "/hybrid/html/rtc-group/index.html?";
this.url += "baseUrl=" + UNI_APP.BASE_URL; this.url += "baseUrl=" + UNI_APP.BASE_URL;
this.url += "&groupId=" + this.groupId; this.url += "&groupId=" + this.groupId;
this.url += "&userId=" + this.$store.state.userStore.userInfo.id; this.url += "&userId=" + this.userStore.userInfo.id;
this.url += "&inviterId=" + this.inviterId; this.url += "&inviterId=" + this.inviterId;
this.url += "&isHost=" + this.isHost; this.url += "&isHost=" + this.isHost;
this.url += "&loginInfo=" + JSON.stringify(uni.getStorageSync("loginInfo")); this.url += "&loginInfo=" + JSON.stringify(uni.getStorageSync("loginInfo"));
this.url += "&userInfos=" + JSON.stringify(this.userInfos); this.url += "&userInfos=" + JSON.stringify(this.userInfos);
this.url += "&config=" + JSON.stringify(this.$store.state.configStore.webrtc); this.url += "&config=" + JSON.stringify(this.configStore.webrtc);
}, },
}, },
onBackPress() { onBackPress() {

4
im-uniapp/pages/chat/chat-private-video.vue

@ -62,9 +62,9 @@
this.url += "&isHost="+this.isHost; this.url += "&isHost="+this.isHost;
this.url += "&baseUrl="+UNI_APP.BASE_URL; this.url += "&baseUrl="+UNI_APP.BASE_URL;
this.url += "&loginInfo="+JSON.stringify(uni.getStorageSync("loginInfo")); this.url += "&loginInfo="+JSON.stringify(uni.getStorageSync("loginInfo"));
this.url += "&userInfo="+JSON.stringify(this.$store.state.userStore.userInfo); this.url += "&userInfo="+JSON.stringify(this.userStore.userInfo);
this.url += "&friend="+JSON.stringify(this.friend); this.url += "&friend="+JSON.stringify(this.friend);
this.url += "&config=" + JSON.stringify(this.$store.state.configStore.webrtc); this.url += "&config=" + JSON.stringify(this.configStore.webrtc);
}, },
}, },
onBackPress() { onBackPress() {

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

@ -14,11 +14,11 @@
温馨提示您现在还没有任何聊天消息快跟您的好友发起聊天吧~ 温馨提示您现在还没有任何聊天消息快跟您的好友发起聊天吧~
</view> </view>
<scroll-view class="scroll-bar" v-else scroll-with-animation="true" scroll-y="true"> <scroll-view class="scroll-bar" v-else scroll-with-animation="true" scroll-y="true">
<view v-for="(chatPos,i) in chatsPos" :key="i"> <view v-for="(chat,index) in chatStore.chats" :key="index">
<pop-menu v-if="isShowChat(chatStore.chats[chatPos.idx])" :items="menu.items" <pop-menu v-if="isShowChat(chat)" :items="menu.items"
@select="onSelectMenu($event,chatPos.idx)"> @select="onSelectMenu($event,index)">
<chat-item :chat="chatStore.chats[chatPos.idx]" <chat-item :chat="chat" :index="index"
:active="menu.chatIdx==chatPos.idx" :index="chatPos.idx"></chat-item> :active="menu.chatIdx==index"></chat-item>
</pop-menu> </pop-menu>
</view> </view>
</scroll-view> </scroll-view>
@ -26,6 +26,8 @@
</template> </template>
<script> <script>
import useChatStore from '@/store/chatStore.js'
export default { export default {
data() { data() {
return { return {
@ -65,10 +67,10 @@
this.menu.show = false; this.menu.show = false;
}, },
removeChat(chatIdx) { removeChat(chatIdx) {
this.$store.commit("removeChat", chatIdx); this.chatStore.removeChat(chatIdx);
}, },
moveToTop(chatIdx) { moveToTop(chatIdx) {
this.$store.commit("moveTop", chatIdx); this.chatStore.moveTop(chatIdx);
}, },
isShowChat(chat){ isShowChat(chat){
if(chat.delete){ if(chat.delete){
@ -87,29 +89,10 @@
index: 0, index: 0,
complete: () => {} complete: () => {}
}) })
} }
} }
}, },
computed: { computed: {
chatsPos() {
//
let chatsPos = [];
let chats = this.chatStore.chats;
chats.forEach((chat, idx) => {
chatsPos.push({
idx: idx,
sendTime: chat.lastSendTime
})
})
chatsPos.sort((chatPos1, chatPos2) => {
return chatPos2.sendTime - chatPos1.sendTime;
});
return chatsPos;
},
chatStore() {
return this.$store.state.chatStore;
},
unreadCount() { unreadCount() {
let count = 0; let count = 0;
this.chatStore.chats.forEach(chat => { this.chatStore.chats.forEach(chat => {

22
im-uniapp/pages/common/user-info.vue

@ -1,7 +1,7 @@
<template> <template>
<view class="page user-info"> <view class="page user-info">
<view class="content"> <view class="content">
<head-image :name="userInfo.nickName" :url="userInfo.headImage" <head-image :name="userInfo.nickName" :url="userInfo.headImageThumb"
:size="160" @click="onShowFullImage()"></head-image> :size="160" @click="onShowFullImage()"></head-image>
<view class="info-item"> <view class="info-item">
@ -54,8 +54,8 @@
showName: this.userInfo.nickName, showName: this.userInfo.nickName,
headImage: this.userInfo.headImage, headImage: this.userInfo.headImage,
}; };
this.$store.commit("openChat", chat); this.chatStore.openChat(chat);
let chatIdx = this.$store.getters.findChatIdx(chat); let chatIdx = this.chatStore.findChatIdx(chat);
uni.navigateTo({ uni.navigateTo({
url:"/pages/chat/chat-box?chatIdx=" + chatIdx url:"/pages/chat/chat-box?chatIdx=" + chatIdx
}) })
@ -71,7 +71,7 @@
headImage: this.userInfo.headImageThumb, headImage: this.userInfo.headImageThumb,
online: this.userInfo.online online: this.userInfo.online
} }
this.$store.commit("addFriend", friend); this.friendStore.addFriend(friend);
uni.showToast({ uni.showToast({
title: '对方已成为您的好友', title: '对方已成为您的好友',
icon: 'none' icon: 'none'
@ -81,7 +81,7 @@
onDelFriend(){ onDelFriend(){
uni.showModal({ uni.showModal({
title: "确认删除", title: "确认删除",
content: `确认删除 '${this.userInfo.nickName}'的好友关系吗?`, content: `确认删除 '${this.userInfo.nickName}',并删除聊天记录吗?`,
success: (res)=> { success: (res)=> {
if(res.cancel) if(res.cancel)
return; return;
@ -89,8 +89,8 @@
url: `/friend/delete/${this.userInfo.id}`, url: `/friend/delete/${this.userInfo.id}`,
method: 'delete' method: 'delete'
}).then((data) => { }).then((data) => {
this.$store.commit("removeFriend", this.userInfo.id); this.friendStore.removeFriend(this.userInfo.id);
this.$store.commit("removePrivateChat", this.userInfo.id); this.chatStore.removePrivateChat(this.userInfo.id);
uni.showToast({ uni.showToast({
title: `与 '${this.userInfo.nickName}'的好友关系已解除`, title: `与 '${this.userInfo.nickName}'的好友关系已解除`,
icon: 'none' icon: 'none'
@ -110,9 +110,9 @@
data: friend data: friend
}).then(() => { }).then(() => {
// //
this.$store.commit("updateFriend", friend); this.friendStore.updateFriend(friend);
// //
this.$store.commit("updateChatFromFriend", this.userInfo); this.chatStore.updateChatFromFriend(this.userInfo);
}) })
}, },
loadUserInfo(id){ loadUserInfo(id){
@ -131,10 +131,10 @@
}, },
computed: { computed: {
isFriend() { isFriend() {
return this.friendInfo&&!this.friendInfo.delete; return !!this.friendInfo;
}, },
friendInfo(){ friendInfo(){
let friends = this.$store.state.friendStore.friends; let friends = this.friendStore.friends;
let friend = friends.find((f) => f.id == this.userInfo.id); let friend = friends.find((f) => f.id == this.userInfo.id);
return friend; return friend;
} }

8
im-uniapp/pages/friend/friend-add.vue

@ -6,7 +6,7 @@
</view> </view>
<view class="user-items"> <view class="user-items">
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> <scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true">
<view v-for="(user) in users" :key="user.id" v-show="user.id != $store.state.userStore.userInfo.id"> <view v-for="(user) in users" :key="user.id" v-show="user.id != userStore.userInfo.id">
<view class="user-item"> <view class="user-item">
<head-image :id="user.id" :name="user.nickName" <head-image :id="user.id" :name="user.nickName"
:online="user.online" :url="user.headImage" :online="user.online" :url="user.headImage"
@ -55,7 +55,7 @@
headImage: user.headImage, headImage: user.headImage,
online: user.online online: user.online
} }
this.$store.commit("addFriend", friend); this.friendStore.addFriend(friend);
uni.showToast({ uni.showToast({
title: "添加成功,对方已成为您的好友", title: "添加成功,对方已成为您的好友",
icon: "none" icon: "none"
@ -68,9 +68,9 @@
}) })
}, },
isFriend(userId) { isFriend(userId) {
let friends = this.$store.state.friendStore.friends; let friends = this.friendStore.friends;
let friend = friends.find((f) => f.id == userId); let friend = friends.find((f) => f.id == userId);
return friend&&!friend.delete; return !!friend;
} }
} }
} }

5
im-uniapp/pages/friend/friend.vue

@ -58,15 +58,12 @@
}, },
computed: { computed: {
friends() { friends() {
return this.$store.state.friendStore.friends; return this.friendStore.friends;
}, },
friendGroupMap(){ friendGroupMap(){
// //
let groupMap = new Map(); let groupMap = new Map();
this.friends.forEach((f) => { this.friends.forEach((f) => {
if (f.delete) {
return;
}
if(this.searchText && !f.nickName.includes(this.searchText)){ if(this.searchText && !f.nickName.includes(this.searchText)){
return; return;
} }

26
im-uniapp/pages/group/group-edit.vue

@ -1,13 +1,13 @@
<template> <template>
<view v-if="$store.state.userStore.userInfo.type == 1" class="page group-edit"> <view v-if="userStore.userInfo.type == 1" class="page group-edit">
<uni-forms ref="form" :modelValue="group" :rules="rules" validate-trigger="bind" label-position="top" <uni-forms ref="form" :modelValue="group" :rules="rules" validate-trigger="bind" label-position="top"
label-width="100%"> label-width="100%">
<uni-forms-item label="群聊头像:" name="headImage"> <uni-forms-item label="群聊头像:" name="headImage">
<image-upload v-show="isOwner" :onSuccess="onUnloadImageSuccess"> <image-upload v-if="isOwner" :onSuccess="onUnloadImageSuccess">
<image :src="group.headImage" class="group-image"></image> <image :src="group.headImageThumb" class="group-image"></image>
</image-upload> </image-upload>
<head-image v-show="!isOwner" :name="group.showGroupName" <head-image v-if="!isOwner" :name="group.showGroupName"
:url="group.headImage" :size="200"></head-image> :url="group.headImageThumb" :size="200"></head-image>
</uni-forms-item> </uni-forms-item>
<uni-forms-item label="群聊名称:" name="name" :required="true"> <uni-forms-item label="群聊名称:" name="name" :required="true">
<uni-easyinput type="text" v-model="group.name" :disabled="!isOwner" placeholder="请输入群聊名称" /> <uni-easyinput type="text" v-model="group.name" :disabled="!isOwner" placeholder="请输入群聊名称" />
@ -16,7 +16,7 @@
<uni-easyinput v-model="group.remarkGroupName" type="text" :placeholder="group.name" /> <uni-easyinput v-model="group.remarkGroupName" type="text" :placeholder="group.name" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item label="我在本群的昵称:" name="remarkNickName"> <uni-forms-item label="我在本群的昵称:" name="remarkNickName">
<uni-easyinput v-model="group.remarkNickName" type="text" :placeholder="$store.state.userStore.userInfo.nickName" /> <uni-easyinput v-model="group.remarkNickName" type="text" :placeholder="userStore.userInfo.nickName" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item label="群公告:" name="notice"> <uni-forms-item label="群公告:" name="notice">
<uni-easyinput type="textarea" v-model="group.notice" :disabled="!isOwner" placeholder="请输入群公告" /> <uni-easyinput type="textarea" v-model="group.notice" :disabled="!isOwner" placeholder="请输入群公告" />
@ -61,7 +61,7 @@
method: "PUT", method: "PUT",
data: this.group data: this.group
}).then((group) => { }).then((group) => {
this.$store.commit("updateGroup", group); this.groupStore.updateGroup(group);
uni.showToast({ uni.showToast({
title: "修改群聊信息成功", title: "修改群聊信息成功",
icon: 'none' icon: 'none'
@ -81,7 +81,7 @@
method: 'POST', method: 'POST',
data: this.group data: this.group
}).then((group) => { }).then((group) => {
this.$store.commit("addGroup", group); this.groupStore.addGroup(group);
uni.showToast({ uni.showToast({
title: `群聊创建成功,快邀请小伙伴进群吧`, title: `群聊创建成功,快邀请小伙伴进群吧`,
icon: 'none', icon: 'none',
@ -102,25 +102,25 @@
}).then((group) => { }).then((group) => {
this.group = group; this.group = group;
// //
this.$store.commit("updateChatFromGroup", group); this.chatStore.updateChatFromGroup(group);
// //
this.$store.commit("updateGroup", group); this.groupStore.updateGroup(group);
}); });
}, },
initNewGroup() { initNewGroup() {
let userInfo = this.$store.state.userStore.userInfo; let userInfo = this.userStore.userInfo;
this.group = { this.group = {
name: `${userInfo.userName}创建的群聊`, name: `${userInfo.userName}创建的群聊`,
headImage: userInfo.headImage, headImage: userInfo.headImage,
headImageThumb: userInfo.headImageThumb, headImageThumb: userInfo.headImageThumb,
ownerId: this.$store.state.userStore.userInfo.id ownerId: this.userStore.userInfo.id
} }
} }
}, },
computed: { computed: {
isOwner() { isOwner() {
return this.$store.state.userStore.userInfo.id == this.group.ownerId return this.userStore.userInfo.id == this.group.ownerId
} }
}, },
onLoad(options) { onLoad(options) {

23
im-uniapp/pages/group/group-info.vue

@ -1,5 +1,5 @@
<template> <template>
<view v-if="$store.state.userStore.userInfo.type == 1" class="page group-info"> <view v-if="userStore.userInfo.type == 1" class="page group-info">
<view v-if="!group.quit" class="group-members"> <view v-if="!group.quit" class="group-members">
<view class="member-items"> <view class="member-items">
<view v-for="(member,idx) in groupMembers" :key="idx"> <view v-for="(member,idx) in groupMembers" :key="idx">
@ -31,7 +31,7 @@
<uni-section title="群名备注:" titleFontSize="14px"> <uni-section title="群名备注:" titleFontSize="14px">
<template v-slot:right> <template v-slot:right>
<text class="detail-text"> {{group.showGroupName}}</text> <text class="detail-text"> {{group.remarkGroupName}}</text>
</template> </template>
</uni-section> </uni-section>
<uni-section title="我在本群的昵称:" titleFontSize="14px"> <uni-section title="我在本群的昵称:" titleFontSize="14px">
@ -85,8 +85,8 @@
showName: this.group.showGroupName, showName: this.group.showGroupName,
headImage: this.group.headImage, headImage: this.group.headImage,
}; };
this.$store.commit("openChat", chat); this.chatStore.openChat(chat);
let chatIdx = this.$store.getters.findChatIdx(chat); let chatIdx = this.chatStore.findChatIdx(chat);
uni.navigateTo({ uni.navigateTo({
url: "/pages/chat/chat-box?chatIdx=" + chatIdx url: "/pages/chat/chat-box?chatIdx=" + chatIdx
}) })
@ -111,8 +111,8 @@
uni.switchTab({ uni.switchTab({
url:"/pages/group/group" url:"/pages/group/group"
}); });
this.$store.commit("removeGroup", this.groupId); this.groupStore.removeGroup(this.groupId);
this.$store.commit("removeGroupChat",this.groupId); this.chatStore.removeGroupChat(this.groupId);
},100) },100)
} }
}) })
@ -121,7 +121,6 @@
}); });
}, },
onDissolveGroup() { onDissolveGroup() {
console.log(this.group.name)
uni.showModal({ uni.showModal({
title: '确认解散?', title: '确认解散?',
content: `确认要解散群聊'${this.group.name}'吗?`, content: `确认要解散群聊'${this.group.name}'吗?`,
@ -141,8 +140,8 @@
uni.switchTab({ uni.switchTab({
url:"/pages/group/group" url:"/pages/group/group"
}); });
this.$store.commit("removeGroup", this.groupId); this.groupStore.removeGroup(this.groupId);
this.$store.commit("removeGroupChat",this.groupId); this.chatStore.removeGroupChat(this.groupId);
},100) },100)
} }
}) })
@ -158,9 +157,9 @@
}).then((group) => { }).then((group) => {
this.group = group; this.group = group;
// //
this.$store.commit("updateChatFromGroup", group); this.chatStore.updateChatFromGroup(group);
// //
this.$store.commit("updateGroup", group); this.groupStore.updateGroup(group);
}); });
}, },
@ -180,7 +179,7 @@
return member && member.showNickName; return member && member.showNickName;
}, },
isOwner() { isOwner() {
return this.group.ownerId == this.$store.state.userStore.userInfo.id; return this.group.ownerId == this.userStore.userInfo.id;
} }
}, },
onLoad(options) { onLoad(options) {

9
im-uniapp/pages/group/group-invite.vue

@ -1,5 +1,5 @@
<template> <template>
<view v-if="$store.state.userStore.userInfo.type == 1" class="page group-invite"> <view class="page group-invite">
<view class="search-bar"> <view class="search-bar">
<uni-search-bar v-model="searchText" radius="100" cancelButton="none" placeholder="输入好友昵称搜索"></uni-search-bar> <uni-search-bar v-model="searchText" radius="100" cancelButton="none" placeholder="输入好友昵称搜索"></uni-search-bar>
</view> </view>
@ -17,7 +17,6 @@
<radio :checked="friend.checked" :disabled="friend.disabled" @click.stop="onSwitchChecked(friend)"/> <radio :checked="friend.checked" :disabled="friend.disabled" @click.stop="onSwitchChecked(friend)"/>
</view> </view>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
@ -78,15 +77,11 @@
if (!friend.disabled) { if (!friend.disabled) {
friend.checked = !friend.checked; friend.checked = !friend.checked;
} }
console.log(this.inviteSize)
}, },
initFriendItems() { initFriendItems() {
this.friendItems = []; this.friendItems = [];
let friends = this.$store.state.friendStore.friends; let friends = this.friendStore.friends;
friends.forEach((f => { friends.forEach((f => {
if(f.delete){
return
}
let item = { let item = {
id: f.id, id: f.id,
headImage: f.headImage, headImage: f.headImage,

6
im-uniapp/pages/group/group-member.vue

@ -1,5 +1,5 @@
<template> <template>
<view v-if="$store.state.userStore.userInfo.type == 1" class="page group-member"> <view class="page group-member">
<view class="search-bar"> <view class="search-bar">
<uni-search-bar v-model="searchText" radius="100" cancelButton="none" placeholder="输入成员昵称搜索"></uni-search-bar> <uni-search-bar v-model="searchText" radius="100" cancelButton="none" placeholder="输入成员昵称搜索"></uni-search-bar>
</view> </view>
@ -79,12 +79,12 @@
}) })
}, },
isSelf(userId) { isSelf(userId) {
return this.$store.state.userStore.userInfo.id == userId return this.userStore.userInfo.id == userId
} }
}, },
computed: { computed: {
isOwner() { isOwner() {
return this.$store.state.userStore.userInfo.id == this.group.ownerId; return this.userStore.userInfo.id == this.group.ownerId;
} }
}, },
onLoad(options) { onLoad(options) {

4
im-uniapp/pages/group/group.vue

@ -9,12 +9,12 @@
<uni-icons type="personadd" size="35"></uni-icons> <uni-icons type="personadd" size="35"></uni-icons>
</view> </view>
</view> </view>
<view class="group-tip" v-if="$store.state.groupStore.groups.length==0"> <view class="group-tip" v-if="groupStore.groups.length==0">
温馨提示您现在还没有加入任何群聊点击右上方'+'按钮可以创建群聊哦~ 温馨提示您现在还没有加入任何群聊点击右上方'+'按钮可以创建群聊哦~
</view> </view>
<view class="group-items" v-else> <view class="group-items" v-else>
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> <scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true">
<view v-for="group in $store.state.groupStore.groups" :key="group.id"> <view v-for="group in groupStore.groups" :key="group.id">
<group-item v-if="!group.quit&&group.showGroupName.includes(searchText)" <group-item v-if="!group.quit&&group.showGroupName.includes(searchText)"
:group="group"></group-item> :group="group"></group-item>
</view> </view>

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

@ -54,7 +54,7 @@
loginInfo.expireTime = new Date().getTime() + loginInfo.refreshTokenExpiresIn * 1000; loginInfo.expireTime = new Date().getTime() + loginInfo.refreshTokenExpiresIn * 1000;
uni.setStorageSync("loginInfo", loginInfo); uni.setStorageSync("loginInfo", loginInfo);
// App.vue // App.vue
getApp().init() getApp().$vm.init()
// //
uni.switchTab({ uni.switchTab({
url: "/pages/chat/chat" url: "/pages/chat/chat"
@ -72,8 +72,6 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.login { .login {
.title { .title {
padding-top: 150rpx; padding-top: 150rpx;
padding-bottom: 50rpx; padding-bottom: 50rpx;

4
im-uniapp/pages/mine/mine-edit.vue

@ -48,7 +48,7 @@
method: "PUT", method: "PUT",
data: this.userInfo data: this.userInfo
}).then(()=>{ }).then(()=>{
this.$store.commit("setUserInfo",this.userInfo); this.userStore.setUserInfo(this.userInfo);
uni.showToast({ uni.showToast({
title:"修改成功", title:"修改成功",
icon: 'none' icon: 'none'
@ -61,7 +61,7 @@
}, },
onLoad() { onLoad() {
// //
let mine = this.$store.state.userStore.userInfo; let mine = this.userStore.userInfo;
this.userInfo = JSON.parse(JSON.stringify(mine)); this.userInfo = JSON.parse(JSON.stringify(mine));
} }
} }

5
im-uniapp/pages/mine/mine.vue

@ -52,7 +52,8 @@
title: '确认退出?', title: '确认退出?',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
getApp().exit() console.log(getApp())
getApp().$vm.exit()
} }
} }
}); });
@ -60,7 +61,7 @@
}, },
computed: { computed: {
userInfo() { userInfo() {
return this.$store.state.userStore.userInfo; return this.userStore.userInfo;
} }
} }

352
im-uniapp/store/chatStore.js

@ -1,48 +1,34 @@
import { import { defineStore } from 'pinia';
MESSAGE_TYPE, import { MESSAGE_TYPE, MESSAGE_STATUS } from '@/common/enums.js';
MESSAGE_STATUS import useUserStore from './userStore';
} from '@/common/enums.js';
import userStore from './userStore';
/*
uniapp性能优化
1.由于uniapp渲染消息性能非常拉胯,所以先把离线消息存储到cacheChats,
待所有离线消息拉取完成后再统一进行渲染
2.在vuex中对数组进行unshift,splice特别卡所以删除会话会话置顶
除消息等操作进行优化不通过unshift,splice实现改造方案如下
删除会话 通过delete标志判断是否删除
删除消息通过delete标志判断是否删除
会话置顶通过lastSendTime排序确定会话顺序
*/
let cacheChats = []; let cacheChats = [];
export default { export default defineStore('chatStore', {
state: { state: () => {
chats: [], return {
privateMsgMaxId: 0, chats: [],
groupMsgMaxId: 0, privateMsgMaxId: 0,
loadingPrivateMsg: false, groupMsgMaxId: 0,
loadingGroupMsg: false, loadingPrivateMsg: false,
loadingGroupMsg: false
}
}, },
actions: {
mutations: { initChats(chatsData) {
initChats(state, chatsData) {
cacheChats = []; cacheChats = [];
state.chats = []; this.chats = [];
for (let chat of chatsData.chats) { for (let chat of chatsData.chats) {
// 已删除的会话直接丢弃
if (chat.delete) {
continue;
}
// 暂存至缓冲区 // 暂存至缓冲区
chat.stored = false;
cacheChats.push(JSON.parse(JSON.stringify(chat))); cacheChats.push(JSON.parse(JSON.stringify(chat)));
// 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间 // 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间
if (state.chats.length < 15) { if (this.chats.length < 15) {
chat.messages = []; chat.messages = [];
state.chats.push(chat); this.chats.push(chat);
} }
} }
state.privateMsgMaxId = chatsData.privateMsgMaxId || 0; this.privateMsgMaxId = chatsData.privateMsgMaxId || 0;
state.groupMsgMaxId = chatsData.groupMsgMaxId || 0; this.groupMsgMaxId = chatsData.groupMsgMaxId || 0;
// 防止图片一直处在加载中状态 // 防止图片一直处在加载中状态
cacheChats.forEach((chat) => { cacheChats.forEach((chat) => {
chat.messages.forEach((msg) => { chat.messages.forEach((msg) => {
@ -52,16 +38,15 @@ export default {
}) })
}) })
}, },
openChat(state, chatInfo) { openChat(chatInfo) {
let chats = this.getters.findChats(); let chats = this.curChats;
let chat = null; let chat = null;
for (let idx in chats) { for (let idx in chats) {
if (chats[idx].type == chatInfo.type && if (chats[idx].type == chatInfo.type &&
chats[idx].targetId === chatInfo.targetId) { chats[idx].targetId === chatInfo.targetId) {
chat = chats[idx]; chat = chats[idx];
chat.delete = false;
// 放置头部 // 放置头部
this.commit("moveTop", idx) this.moveTop(idx)
break; break;
} }
} }
@ -78,97 +63,106 @@ export default {
messages: [], messages: [],
atMe: false, atMe: false,
atAll: false, atAll: false,
delete: false stored: false
}; };
chats.push(chat); chats.unshift(chat);
this.commit("moveTop", chats.length - 1) this.saveToStorage();
} }
}, },
activeChat(state, idx) { activeChat(idx) {
let chats = this.getters.findChats(); let chats = this.curChats;
if (idx >= 0) { if (idx >= 0) {
chats[idx].unreadCount = 0; chats[idx].unreadCount = 0;
} }
}, },
resetUnreadCount(state, chatInfo) { resetUnreadCount(chatInfo) {
let chats = this.getters.findChats(); let chats = this.curChats;
for (let idx in chats) { for (let idx in chats) {
if (chats[idx].type == chatInfo.type && if (chats[idx].type == chatInfo.type &&
chats[idx].targetId == chatInfo.targetId) { chats[idx].targetId == chatInfo.targetId) {
chats[idx].unreadCount = 0; chats[idx].unreadCount = 0;
chats[idx].atMe = false; chats[idx].atMe = false;
chats[idx].atAll = false; chats[idx].atAll = false;
chats[idx].stored = false;
this.saveToStorage();
} }
} }
this.commit("saveToStorage");
}, },
readedMessage(state, pos) { readedMessage(pos) {
let chats = this.getters.findChats(); let chat = this.findChatByFriend(pos.friendId);
for (let idx in chats) { chat.messages.forEach((m) => {
if (chats[idx].type == 'PRIVATE' && if (m.id && m.selfSend && m.status < MESSAGE_STATUS.RECALL) {
chats[idx].targetId == pos.friendId) { // pos.maxId为空表示整个会话已读
chats[idx].messages.forEach((m) => { if (!pos.maxId || m.id <= pos.maxId) {
if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) { m.status = MESSAGE_STATUS.READED
// pos.maxId为空表示整个会话已读 chat.stored = false;
if (!pos.maxId || m.id <= pos.maxId) { }
m.status = MESSAGE_STATUS.READED
}
}
})
} }
})
if(!chat.stored){
this.saveToStorage();
} }
this.commit("saveToStorage");
}, },
removeChat(state, idx) { removeChat(idx) {
let chats = this.getters.findChats(); let chats = this.curChats;
chats[idx].delete = true; chats[idx].delete = true;
this.commit("saveToStorage"); chats[idx].stored = false;
this.saveToStorage();
}, },
removePrivateChat(state, userId) { removePrivateChat(userId) {
let chats = this.getters.findChats(); let chats = this.curChats;
for (let idx in chats) { for (let idx in chats) {
if (chats[idx].type == 'PRIVATE' && if (chats[idx].type == 'PRIVATE' &&
chats[idx].targetId == userId) { chats[idx].targetId == userId) {
this.commit("removeChat", idx); this.removeChat(idx);
} }
} }
}, },
removeGroupChat(state, groupId) { removeGroupChat(groupId) {
let chats = this.getters.findChats(); let chats = this.curChats;
for (let idx in chats) { for (let idx in chats) {
if (chats[idx].type == 'GROUP' && if (chats[idx].type == 'GROUP' &&
chats[idx].targetId == groupId) { chats[idx].targetId == groupId) {
this.commit("removeChat", idx); this.removeChat(idx);
} }
} }
}, },
moveTop(state, idx) { moveTop(idx) {
let chats = this.getters.findChats(); if (this.isLoading()) {
let chat = chats[idx]; return;
// 最新的时间会显示在顶部 }
chat.lastSendTime = new Date().getTime(); let chats = this.curChats;
this.commit("saveToStorage"); if (idx > 0) {
let chat = chats[idx];
chats.splice(idx, 1);
chats.unshift(chat);
chat.lastSendTime = new Date().getTime();
chat.stored = false;
this.saveToStorage();
}
}, },
insertMessage(state, msgInfo) { insertMessage(msgInfo) {
// 获取对方id或群id // 获取对方id或群id
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
// 记录消息的最大id // 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > state.privateMsgMaxId) { if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
state.privateMsgMaxId = msgInfo.id; this.privateMsgMaxId = msgInfo.id;
} }
if (msgInfo.id && type == "GROUP" && msgInfo.id > state.groupMsgMaxId) { if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
state.groupMsgMaxId = msgInfo.id; this.groupMsgMaxId = msgInfo.id;
} }
// 如果是已存在消息,则覆盖旧的消息数据 // 如果是已存在消息,则覆盖旧的消息数据
let chat = this.getters.findChat(msgInfo); let chat = this.findChat(msgInfo);
let message = this.getters.findMessage(chat, msgInfo); let message = this.findMessage(chat, msgInfo);
if (message) { if (message) {
Object.assign(message, msgInfo); Object.assign(message, msgInfo);
// 撤回消息需要显示 // 撤回消息需要显示
if (msgInfo.type == MESSAGE_TYPE.RECALL) { if (msgInfo.type == MESSAGE_TYPE.RECALL) {
chat.lastContent = msgInfo.content; chat.lastContent = msgInfo.content;
} }
this.commit("saveToStorage"); chat.stored = false;
this.saveToStorage();
return; return;
} }
// 会话列表内容 // 会话列表内容
@ -178,16 +172,17 @@ export default {
chat.lastContent = "[文件]"; chat.lastContent = "[文件]";
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) { } else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = "[语音]"; chat.lastContent = "[语音]";
} else if (msgInfo.type == MESSAGE_TYPE.TEXT || msgInfo.type == MESSAGE_TYPE.RECALL) {
chat.lastContent = msgInfo.content;
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) { } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) {
chat.lastContent = "[语音通话]"; chat.lastContent = "[语音通话]";
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) { } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) {
chat.lastContent = "[视频通话]"; chat.lastContent = "[视频通话]";
} else if (msgInfo.type == MESSAGE_TYPE.TEXT ||
msgInfo.type == MESSAGE_TYPE.RECALL ||
msgInfo.type == MESSAGE_TYPE.TIP_TEXT) {
chat.lastContent = msgInfo.content;
} }
chat.lastSendTime = msgInfo.sendTime; chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = msgInfo.sendNickName; chat.sendNickName = msgInfo.sendNickName;
// 未读加1 // 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED && if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED &&
msgInfo.type != MESSAGE_TYPE.TIP_TEXT) { msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
@ -196,7 +191,8 @@ export default {
// 是否有人@我 // 是否有人@我
if (!msgInfo.selfSend && chat.type == "GROUP" && msgInfo.atUserIds && if (!msgInfo.selfSend && chat.type == "GROUP" && msgInfo.atUserIds &&
msgInfo.status != MESSAGE_STATUS.READED) { msgInfo.status != MESSAGE_STATUS.READED) {
let userId = userStore.state.userInfo.id; const userStore = useUserStore();
let userId = userStore.userInfo.id;
if (msgInfo.atUserIds.indexOf(userId) >= 0) { if (msgInfo.atUserIds.indexOf(userId) >= 0) {
chat.atMe = true; chat.atMe = true;
} }
@ -230,70 +226,71 @@ export default {
} else { } else {
chat.messages.splice(insertPos, 0, msgInfo); chat.messages.splice(insertPos, 0, msgInfo);
} }
this.commit("saveToStorage"); chat.stored = false;
this.saveToStorage();
}, },
updateMessage(state, msgInfo) { updateMessage(msgInfo) {
// 获取对方id或群id // 获取对方id或群id
let chat = this.getters.findChat(msgInfo); let chat = this.findChat(msgInfo);
let message = this.getters.findMessage(chat, msgInfo); let message = this.findMessage(chat, msgInfo);
if (message) { if (message) {
// 属性拷贝 // 属性拷贝
Object.assign(message, msgInfo); Object.assign(message, msgInfo);
this.commit("saveToStorage"); chat.stored = false;
this.saveToStorage();
} }
}, },
deleteMessage(state, msgInfo) { deleteMessage(msgInfo) {
// 获取对方id或群id // 获取对方id或群id
let chat = this.getters.findChat(msgInfo); let chat = this.findChat(msgInfo);
for (let idx in chat.messages) { for (let idx in chat.messages) {
// 已经发送成功的,根据id删除 // 已经发送成功的,根据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[idx].delete = true; chat.messages.splice(idx, 1);
break; break;
} }
// 正在发送中的消息可能没有id,根据发送时间删除 // 正在发送中的消息可能没有id,根据发送时间删除
if (msgInfo.selfSend && chat.messages[idx].selfSend && if (msgInfo.selfSend && chat.messages[idx].selfSend &&
chat.messages[idx].sendTime == msgInfo.sendTime) { chat.messages[idx].sendTime == msgInfo.sendTime) {
chat.messages[idx].delete = true; chat.messages.splice(idx, 1);
break; break;
} }
} }
this.commit("saveToStorage"); chat.stored = false;
this.saveToStorage();
}, },
updateChatFromFriend(state, friend) { updateChatFromFriend(friend) {
let chats = this.getters.findChats(); let chat = this.findChatByFriend(friend.id)
for (let i in chats) { if (chat && (chat.headImage != friend.headImageThumb ||
let chat = chats[i]; chat.showName != friend.nickName)) {
if (chat.type == 'PRIVATE' && chat.targetId == friend.id) { // 更新会话中的群名和头像
chat.headImage = friend.headImageThumb; chat.headImage = friend.headImageThumb;
chat.showName = friend.nickName; chat.showName = friend.nickName;
break; chat.stored = false;
} this.saveToStorage();
} }
this.commit("saveToStorage");
}, },
updateChatFromGroup(state, group) { updateChatFromGroup(group) {
let chats = this.getters.findChats(); let chat = this.findChatByGroup(group.id);
for (let i in chats) { if (chat && (chat.headImage != group.headImageThumb ||
let chat = chats[i]; chat.showName != group.showGroupName)) {
if (chat.type == 'GROUP' && chat.targetId == group.id) { // 更新会话中的群名称和头像
chat.headImage = group.headImageThumb; chat.headImage = group.headImageThumb;
chat.showName = group.showGroupName; chat.showName = group.showGroupName;
break; chat.stored = false;
} this.saveToStorage();
} }
this.commit("saveToStorage");
}, },
loadingPrivateMsg(state, loading) { setLoadingPrivateMsg(loading) {
state.loadingPrivateMsg = loading; this.loadingPrivateMsg = loading;
if (!this.getters.isLoading()) { if (!this.isLoading()) {
this.commit("refreshChats") this.refreshChats()
} }
}, },
loadingGroupMsg(state, loading) { setLoadingGroupMsg(loading) {
state.loadingGroupMsg = loading; this.loadingGroupMsg = loading;
if (!this.getters.isLoading()) { if (!this.isLoading()) {
this.commit("refreshChats") this.refreshChats()
} }
}, },
refreshChats(state) { refreshChats(state) {
@ -302,51 +299,70 @@ export default {
return chat2.lastSendTime - chat1.lastSendTime; return chat2.lastSendTime - chat1.lastSendTime;
}); });
// 将消息一次性装载回来 // 将消息一次性装载回来
state.chats = cacheChats; this.chats = cacheChats;
// 断线重连后不能使用缓存模式,否则会导致聊天窗口的消息不刷新 // 断线重连后不能使用缓存模式,否则会导致聊天窗口的消息不刷新
cacheChats = state.chats; cacheChats = this.chats;
this.commit("saveToStorage"); this.saveToStorage();
}, },
saveToStorage(state) { saveToStorage(state) {
// 加载中不保存,防止卡顿 // 加载中不保存,防止卡顿
if (this.getters.isLoading()) { if (this.isLoading()) {
return; return;
} }
let userId = userStore.state.userInfo.id; const userStore = useUserStore();
let userId = userStore.userInfo.id;
let key = "chats-app-" + userId; let key = "chats-app-" + userId;
let chatKeys = [];
// 按会话为单位存储,只存储有改动的会话
this.chats.forEach((chat)=>{
let chatKey = `${key}-${chat.type}-${chat.targetId}`
if(!chat.stored){
if(chat.delete){
uni.removeStorageSync(chatKey);
}else{
uni.setStorageSync(chatKey,chat);
}
chat.stored = true;
}
if(!chat.delete){
chatKeys.push(chatKey);
}
})
// 会话核心信息
let chatsData = { let chatsData = {
privateMsgMaxId: state.privateMsgMaxId, privateMsgMaxId: this.privateMsgMaxId,
groupMsgMaxId: state.groupMsgMaxId, groupMsgMaxId: this.groupMsgMaxId,
chats: state.chats chatKeys: chatKeys
} }
uni.setStorage({ uni.setStorageSync(key, chatsData)
key: key,
data: chatsData ,
})
}, },
clear(state) { clear(state) {
cacheChats = []; cacheChats = [];
state.chats = []; this.chats = [];
state.privateMsgMaxId = 0; this.privateMsgMaxId = 0;
state.groupMsgMaxId = 0; this.groupMsgMaxId = 0;
state.loadingPrivateMsg = false; this.loadingPrivateMsg = false;
state.loadingGroupMsg = false; this.loadingGroupMsg = false;
} },
},
actions: {
loadChat(context) { loadChat(context) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let userId = userStore.state.userInfo.id; let userStore = useUserStore();
uni.getStorage({ let userId = userStore.userInfo.id;
key: "chats-app-" + userId, let chatsData = uni.getStorageSync("chats-app-" + userId)
success(res) { if(chatsData){
context.commit("initChats", res.data); if(chatsData.chatKeys){
resolve() let time = new Date().getTime();
}, chatsData.chats = [];
fail(e) { chatsData.chatKeys.forEach(key=>{
resolve() let chat = uni.getStorageSync(key);
if(chat){
chatsData.chats.push(chat);
}
})
} }
}); this.initChats(chatsData);
}
resolve()
}) })
} }
}, },
@ -354,11 +370,11 @@ export default {
isLoading: (state) => () => { isLoading: (state) => () => {
return state.loadingPrivateMsg || state.loadingGroupMsg return state.loadingPrivateMsg || state.loadingGroupMsg
}, },
findChats: (state, getters) => () => { curChats: (state) => {
return getters.isLoading() ? cacheChats : state.chats; return state.isLoading() ? cacheChats : state.chats;
}, },
findChatIdx: (state, getters) => (chat) => { findChatIdx: (state) => (chat) => {
let chats = getters.findChats(); let chats = state.curChats;
for (let idx in chats) { for (let idx in chats) {
if (chats[idx].type == chat.type && if (chats[idx].type == chat.type &&
chats[idx].targetId === chat.targetId) { chats[idx].targetId === chat.targetId) {
@ -367,11 +383,12 @@ export default {
} }
} }
}, },
findChat: (state, getters) => (msgInfo) => { findChat: (state) => (msgInfo) => {
let chats = getters.findChats(); let chats = state.curChats;
// 获取对方id或群id // 获取对方id或群id
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId; let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo
.sendId;
let chat = null; let chat = null;
for (let idx in chats) { for (let idx in chats) {
if (chats[idx].type == type && if (chats[idx].type == type &&
@ -382,6 +399,14 @@ export default {
} }
return chat; return chat;
}, },
findChatByFriend: (state) => (fid) => {
return state.curChats.find(chat => chat.type == 'PRIVATE' &&
chat.targetId == fid)
},
findChatByGroup: (state) => (gid) => {
return state.curChats.find(chat => chat.type == 'GROUP' &&
chat.targetId == gid)
},
findMessage: (state) => (chat, msgInfo) => { findMessage: (state) => (chat, msgInfo) => {
if (!chat) { if (!chat) {
return null; return null;
@ -394,10 +419,9 @@ export default {
// 正在发送中的消息可能没有id,只有tmpId // 正在发送中的消息可能没有id,只有tmpId
if (msgInfo.tmpId && chat.messages[idx].tmpId && if (msgInfo.tmpId && chat.messages[idx].tmpId &&
chat.messages[idx].tmpId == msgInfo.tmpId) { chat.messages[idx].tmpId == msgInfo.tmpId) {
console.log("chat.messages[idx].tmpId == msgInfo.tmpId")
return chat.messages[idx]; return chat.messages[idx];
} }
} }
} }
} }
} });

34
im-uniapp/store/configStore.js

@ -1,32 +1,32 @@
import { defineStore } from 'pinia';
import http from '../common/request' import http from '../common/request'
export default { export default defineStore('configStore', {
state: { state: () => {
webrtc: {} return {
}, webrtc: {}
mutations: {
setConfig(state, config) {
state.webrtc = config.webrtc;
},
clear(state){
state.webrtc = {};
} }
}, },
actions:{ actions: {
loadConfig(context){ setConfig(config) {
this.webrtc = config.webrtc;
},
clear() {
this.webrtc = {};
},
loadConfig() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
http({ http({
url: '/system/config', url: '/system/config',
method: 'GET' method: 'GET'
}).then((config) => { }).then((config) => {
console.log("系统配置",config) console.log("系统配置", config)
context.commit("setConfig",config); this.setConfig(config);
resolve(); resolve();
}).catch((res)=>{ }).catch((res) => {
reject(res); reject(res);
}); });
}) })
} }
} }
})
}

112
im-uniapp/store/friendStore.js

@ -1,95 +1,85 @@
import { defineStore } from 'pinia';
import http from '../common/request' import http from '../common/request'
import {TERMINAL_TYPE} from '../common/enums.js' import { TERMINAL_TYPE } from '../common/enums.js'
export default { export default defineStore('friendStore', {
state: () => {
state: { return {
friends: [], friends: [],
timer: null timer: null
}
}, },
mutations: { actions: {
setFriends(state, friends) { setFriends(friends) {
friends.forEach((f)=>{ friends.forEach((f) => {
f.online = false; f.online = false;
f.onlineWeb = false; f.onlineWeb = false;
f.onlineApp = false; f.onlineApp = false;
}) })
state.friends = friends; this.friends = friends;
}, },
updateFriend(state, friend) { updateFriend(friend) {
state.friends.forEach((f, index) => { let f = this.findFriend(friend.id);
if (!f.delete && f.id == friend.id) { let copy = JSON.parse(JSON.stringify(f));
// 拷贝属性 Object.assign(f, friend);
let online = state.friends[index].online; f.online = copy.online;
Object.assign(state.friends[index], friend); f.onlineWeb = copy.onlineWeb;
state.friends[index].online = online; f.onlineApp = copy.onlineApp;
},
removeFriend(id) {
this.friends.forEach((f, idx) => {
if (f.id == id) {
this.friends.splice(idx, 1)
} }
}) })
}, },
removeFriend(state, id) { addFriend(friend) {
let friend = this.getters.findFriend(id); this.friends.push(friend);
if(friend){
friend.delete = true;
}
}, },
addFriend(state, friend) { setOnlineStatus(onlineTerminals) {
let f = this.getters.findFriend(friend.id); this.friends.forEach((f) => {
if(f){ let userTerminal = onlineTerminals.find((o) => f.id == o.userId);
Object.assign(f, friend); if (userTerminal) {
f.delete = false;
}else{
state.friends.push(friend);
}
},
setOnlineStatus(state, onlineTerminals) {
state.friends.forEach((f)=>{
let userTerminal = onlineTerminals.find((o)=> f.id==o.userId);
if(userTerminal){
f.online = true; f.online = true;
f.onlineWeb = userTerminal.terminals.indexOf(TERMINAL_TYPE.WEB)>=0 f.onlineWeb = userTerminal.terminals.indexOf(TERMINAL_TYPE.WEB) >= 0
f.onlineApp = userTerminal.terminals.indexOf(TERMINAL_TYPE.APP)>=0 f.onlineApp = userTerminal.terminals.indexOf(TERMINAL_TYPE.APP) >= 0
}else{ } else {
f.online = false; f.online = false;
f.onlineWeb = false; f.onlineWeb = false;
f.onlineApp = false; f.onlineApp = false;
} }
}); });
}, },
refreshOnlineStatus(state) { refreshOnlineStatus() {
if (state.friends.length > 0) { if (this.friends.length > 0) {
let userIds = []; let userIds = [];
state.friends.forEach((f) => { this.friends.forEach(f => userIds.push(f.id));
userIds.push(f.id)
});
http({ http({
url: '/user/terminal/online?userIds=' + userIds.join(','), url: '/user/terminal/online?userIds=' + userIds.join(','),
method: 'GET' method: 'GET'
}).then((onlineTerminals) => { }).then((onlineTerminals) => {
this.commit("setOnlineStatus", onlineTerminals); this.setOnlineStatus(onlineTerminals);
}) })
} }
// 30s后重新拉取 // 30s后重新拉取
clearTimeout(state.timer); clearTimeout(this.timer);
state.timer = setTimeout(() => { this.timer = setTimeout(() => {
this.commit("refreshOnlineStatus"); this.refreshOnlineStatus();
}, 30000) }, 30000)
}, },
clear(state) { clear() {
clearTimeout(state.timer); clearTimeout(this.timer);
state.friends = []; this.friends = [];
state.timer = null; this.timer = null;
} },
}, loadFriend() {
actions: {
loadFriend(context) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
http({ http({
url: '/friend/list', url: '/friend/list',
method: 'GET' method: 'GET'
}).then((friends) => { }).then((friends) => {
context.commit("setFriends", friends); this.setFriends(friends);
context.commit("refreshOnlineStatus"); this.refreshOnlineStatus();
resolve() resolve()
}).catch((res) => { }).catch((res) => {
reject(); reject();
@ -97,9 +87,9 @@ export default {
}); });
} }
}, },
getters:{ getters: {
findFriend: (state) => (id) => { findFriend: (state) => (id) => {
return state.friends.find((f)=>f.id==id); return state.friends.find((f) => f.id == id);
} }
} }
} })

67
im-uniapp/store/groupStore.js

@ -1,57 +1,58 @@
import { defineStore } from 'pinia';
import http from '@/common/request'; import http from '@/common/request';
export default { export default defineStore('groupStore', {
state: { state: () => {
groups: [], return {
activeIndex: -1, groups: [],
activeIndex: -1
}
}, },
mutations: { actions: {
setGroups(state, groups) { setGroups(groups) {
state.groups = groups; this.groups = groups;
}, },
activeGroup(state, index) { activeGroup(index) {
state.activeIndex = index; this.activeIndex = index;
}, },
addGroup(state, group) { addGroup(group) {
state.groups.unshift(group); this.groups.unshift(group);
}, },
removeGroup(state, groupId) { removeGroup(groupId) {
state.groups.forEach((g, index) => { this.groups.forEach((g, index) => {
if (g.id == groupId) { if (g.id == groupId) {
state.groups.splice(index, 1); this.groups.splice(index, 1);
if (state.activeIndex >= state.groups.length) { if (this.activeIndex >= this.groups.length) {
state.activeIndex = state.groups.length - 1; this.activeIndex = this.groups.length - 1;
} }
} }
}) })
}, },
updateGroup(state, group) { updateGroup(group) {
state.groups.forEach((g, idx) => { let g = this.findGroup(group.id);
if (g.id == group.id) { Object.assign(g, group);
// 拷贝属性
Object.assign(state.groups[idx], group);
}
})
}, },
clear(state){ clear() {
state.groups = []; this.groups = [];
state.activeGroup = -1; this.activeGroup = -1;
} },
}, loadGroup() {
actions: {
loadGroup(context) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
http({ http({
url: '/group/list', url: '/group/list',
method: 'GET' method: 'GET'
}).then((groups) => { }).then((groups) => {
context.commit("setGroups", groups); this.setGroups(groups);
resolve(); resolve();
}).catch((res) => { }).catch((res) => {
reject(res); reject(res);
}) })
}); });
} }
},
getters: {
findGroup: (state) => (id) => {
return state.groups.find((g) => g.id == id);
}
} }
} })

35
im-uniapp/store/index.js

@ -1,35 +0,0 @@
import chatStore from './chatStore.js';
import friendStore from './friendStore.js';
import userStore from './userStore.js';
import groupStore from './groupStore.js';
import configStore from './configStore.js';
import { createStore } from 'vuex';
const store = createStore({
modules: {
chatStore,
friendStore,
userStore,
groupStore,
configStore
},
state: {},
actions: {
load(context) {
return this.dispatch("loadUser").then(() => {
const promises = [];
promises.push(this.dispatch("loadFriend"));
promises.push(this.dispatch("loadGroup"));
promises.push(this.dispatch("loadChat"));
promises.push(this.dispatch("loadConfig"));
return Promise.all(promises);
})
},
unload(context){
context.commit("clear");
}
},
strict: true
})
export default store;

42
im-uniapp/store/userStore.js

@ -1,44 +1,32 @@
import {USER_STATE} from "../common/enums" import { defineStore } from 'pinia';
import http from '../common/request' import http from '../common/request'
export default defineStore('userStore', {
export default { state: () => {
state: { return {
userInfo: {}, userInfo: {}
config:{ }
webrtc:{}
},
state: USER_STATE.FREE
}, },
actions: {
mutations: { setUserInfo(userInfo) {
setUserInfo(state, userInfo) { this.userInfo = userInfo;
// 使用深拷贝方式,否则小程序页面不刷新
Object.assign(state.userInfo, userInfo);
}, },
setUserState(state, userState) { clear() {
state.state = userState; this.userInfo = {};
}, },
clear(state){ loadUser(context) {
state.userInfo = {};
state.state = USER_STATE.FREE;
}
},
actions:{
loadUser(context){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
http({ http({
url: '/user/self', url: '/user/self',
method: 'GET' method: 'GET'
}).then((userInfo) => { }).then((userInfo) => {
console.log(userInfo) console.log(userInfo)
context.commit("setUserInfo",userInfo); this.setUserInfo(userInfo);
resolve(); resolve();
}).catch((res)=>{ }).catch((res) => {
reject(res); reject(res);
}); });
}) })
} }
} }
})
}

171
im-web/src/store/chatStore.js

@ -21,7 +21,7 @@ export default {
state.chats = []; state.chats = [];
state.privateMsgMaxId = chatsData.privateMsgMaxId || 0; state.privateMsgMaxId = chatsData.privateMsgMaxId || 0;
state.groupMsgMaxId = chatsData.groupMsgMaxId || 0; state.groupMsgMaxId = chatsData.groupMsgMaxId || 0;
cacheChats = chatsData.chats||[]; cacheChats = chatsData.chats || [];
// 防止图片一直处在加载中状态 // 防止图片一直处在加载中状态
cacheChats.forEach((chat) => { cacheChats.forEach((chat) => {
chat.messages.forEach((msg) => { chat.messages.forEach((msg) => {
@ -55,7 +55,9 @@ export default {
unreadCount: 0, unreadCount: 0,
messages: [], messages: [],
atMe: false, atMe: false,
atAll: false atAll: false,
stored: false,
delete: false
}; };
chats.unshift(chat); chats.unshift(chat);
} }
@ -72,26 +74,23 @@ export default {
chats[idx].unreadCount = 0; chats[idx].unreadCount = 0;
chats[idx].atMe = false; chats[idx].atMe = false;
chats[idx].atAll = false; chats[idx].atAll = false;
chats[idx].stored = false;
this.commit("saveToStorage");
break;
} }
} }
this.commit("saveToStorage");
}, },
readedMessage(state, pos) { readedMessage(state, pos) {
let chats = this.getters.findChats(); let chat = this.getters.findChatByFriend(pos.friendId);
for (let idx in chats) { chat.messages.forEach((m) => {
if (chats[idx].type == 'PRIVATE' && if (m.id && m.selfSend && m.status < MESSAGE_STATUS.RECALL) {
chats[idx].targetId == pos.friendId) { // pos.maxId为空表示整个会话已读
chats[idx].messages.forEach((m) => { if (!pos.maxId || m.id <= pos.maxId) {
if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) { m.status = MESSAGE_STATUS.READED
// pos.maxId为空表示整个会话已读 chat.stored = false;
if (!pos.maxId || m.id <= pos.maxId) { }
m.status = MESSAGE_STATUS.READED
}
}
})
} }
} })
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
removeChat(state, idx) { removeChat(state, idx) {
@ -99,9 +98,30 @@ export default {
if (chats[idx] == state.activeChat) { if (chats[idx] == state.activeChat) {
state.activeChat = null; state.activeChat = null;
} }
chats.splice(idx, 1); chats[idx].delete = true;
chats[idx].stored = false;
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
removePrivateChat(state,friendId){
let chats = this.getters.findChats();
for (let idx in chats) {
if (chats[idx].type == 'PRIVATE' &&
chats[idx].targetId === friendId) {
this.commit("removeChat",idx)
break;
}
}
},
removeGroupChat(state,groupId){
let chats = this.getters.findChats();
for (let idx in chats) {
if (chats[idx].type == 'GROUP' &&
chats[idx].targetId === groupId) {
this.commit("removeChat",idx)
break;
}
}
},
moveTop(state, idx) { moveTop(state, idx) {
// 加载中不移动,很耗性能 // 加载中不移动,很耗性能
if (this.getters.isLoading()) { if (this.getters.isLoading()) {
@ -112,18 +132,11 @@ export default {
let chat = chats[idx]; let chat = chats[idx];
chats.splice(idx, 1); chats.splice(idx, 1);
chats.unshift(chat); chats.unshift(chat);
chat.lastSendTime = new Date().getTime();
chat.stored = false;
this.commit("saveToStorage"); this.commit("saveToStorage");
} }
}, },
removePrivateChat(state, friendId) {
let chats = this.getters.findChats();
for (let idx in chats) {
if (chats[idx].type == 'PRIVATE' &&
chats[idx].targetId == friendId) {
this.commit("removeChat", idx);
}
}
},
insertMessage(state, msgInfo) { insertMessage(state, msgInfo) {
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE'; let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
// 记录消息的最大id // 记录消息的最大id
@ -142,6 +155,7 @@ export default {
if (msgInfo.type == MESSAGE_TYPE.RECALL) { if (msgInfo.type == MESSAGE_TYPE.RECALL) {
chat.lastContent = msgInfo.content; chat.lastContent = msgInfo.content;
} }
chat.stored = false;
this.commit("saveToStorage"); this.commit("saveToStorage");
return; return;
} }
@ -152,14 +166,14 @@ export default {
chat.lastContent = "[文件]"; chat.lastContent = "[文件]";
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) { } else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = "[语音]"; chat.lastContent = "[语音]";
} else if (msgInfo.type == MESSAGE_TYPE.TEXT ||
msgInfo.type == MESSAGE_TYPE.RECALL ||
msgInfo.type == MESSAGE_TYPE.TIP_TEXT) {
chat.lastContent = msgInfo.content;
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) { } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) {
chat.lastContent = "[语音通话]"; chat.lastContent = "[语音通话]";
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) { } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) {
chat.lastContent = "[视频通话]"; chat.lastContent = "[视频通话]";
} else if (msgInfo.type == MESSAGE_TYPE.TEXT ||
msgInfo.type == MESSAGE_TYPE.RECALL ||
msgInfo.type == MESSAGE_TYPE.TIP_TEXT) {
chat.lastContent = msgInfo.content;
} }
chat.lastSendTime = msgInfo.sendTime; chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = msgInfo.sendNickName; chat.sendNickName = msgInfo.sendNickName;
@ -199,6 +213,7 @@ export default {
} }
} }
chat.messages.splice(insertPos, 0, msgInfo); chat.messages.splice(insertPos, 0, msgInfo);
chat.stored = false;
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
updateMessage(state, msgInfo) { updateMessage(state, msgInfo) {
@ -208,6 +223,7 @@ export default {
if (message) { if (message) {
// 属性拷贝 // 属性拷贝
Object.assign(message, msgInfo); Object.assign(message, msgInfo);
chat.stored = false;
this.commit("saveToStorage"); this.commit("saveToStorage");
} }
}, },
@ -226,31 +242,30 @@ export default {
break; break;
} }
} }
chat.stored = false;
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
updateChatFromFriend(state, friend) { updateChatFromFriend(state, friend) {
let chats = this.getters.findChats(); let chat = this.getters.findChatByFriend(friend.id);
for (let i in chats) { // 更新会话中的群名和头像
let chat = chats[i]; if (chat && (chat.headImage != friend.headImageThumb ||
if (chat.type == 'PRIVATE' && chat.targetId == friend.id) { chat.showName != friend.nickName)) {
chat.headImage = friend.headImageThumb; chat.headImage = friend.headImageThumb;
chat.showName = friend.nickName; chat.showName = friend.nickName;
break; chat.stored = false;
} this.commit("saveToStorage")
} }
this.commit("saveToStorage");
}, },
updateChatFromGroup(state, group) { updateChatFromGroup(state, group) {
let chats = this.getters.findChats(); let chat = this.getters.findChatByGroup(group.id);
for (let i in chats) { if (chat && (chat.headImage != group.headImageThumb ||
let chat = chats[i]; chat.showName != group.showGroupName)) {
if (chat.type == 'GROUP' && chat.targetId == group.id) { // 更新会话中的群名称和头像
chat.headImage = group.headImageThumb; chat.headImage = group.headImageThumb;
chat.showName = group.showGroupName; chat.showName = group.showGroupName;
break; chat.stored = false;
} this.commit("saveToStorage")
} }
this.commit("saveToStorage");
}, },
loadingPrivateMsg(state, loading) { loadingPrivateMsg(state, loading) {
state.loadingPrivateMsg = loading; state.loadingPrivateMsg = loading;
@ -282,10 +297,29 @@ export default {
} }
let userId = userStore.state.userInfo.id; let userId = userStore.state.userInfo.id;
let key = "chats-" + userId; let key = "chats-" + userId;
let chatKeys = [];
// 按会话为单位存储,
state.chats.forEach((chat) => {
// 只存储有改动的会话
let chatKey = `${key}-${chat.type}-${chat.targetId}`
if (!chat.stored) {
console.log(chatKey)
if (chat.delete) {
localForage.removeItem(chatKey);
} else {
localForage.setItem(chatKey, chat);
}
chat.stored = true;
}
if (!chat.delete) {
chatKeys.push(chatKey);
}
})
// 会话核心信息
let chatsData = { let chatsData = {
privateMsgMaxId: state.privateMsgMaxId, privateMsgMaxId: state.privateMsgMaxId,
groupMsgMaxId: state.groupMsgMaxId, groupMsgMaxId: state.groupMsgMaxId,
chats: state.chats chatKeys: chatKeys
} }
localForage.setItem(key, chatsData) localForage.setItem(key, chatsData)
}, },
@ -293,7 +327,6 @@ export default {
cacheChats = [] cacheChats = []
state.chats = []; state.chats = [];
state.activeChat = null; state.activeChat = null;
} }
}, },
actions: { actions: {
@ -301,17 +334,27 @@ export default {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let userId = userStore.state.userInfo.id; let userId = userStore.state.userInfo.id;
let key = "chats-" + userId; let key = "chats-" + userId;
localForage.getItem(key).then((item)=>{ localForage.getItem(key).then((chatsData) => {
let chatsData = item; if (!chatsData) {
// 兼容历史数据,以后要删除 resolve();
if(!chatsData){
chatsData = JSON.parse(localStorage.getItem(key));
} }
if (chatsData) { else if(chatsData.chats){
// 兼容旧版本
context.commit("initChats", chatsData); context.commit("initChats", chatsData);
resolve();
}else if (chatsData.chatKeys) {
const promises = [];
chatsData.chatKeys.forEach(key => {
promises.push(localForage.getItem(key))
})
Promise.all(promises).then(chats => {
chatsData.chats = chats.filter(o => o);
context.commit("initChats", chatsData);
resolve();
})
} }
resolve(); }).catch((e) => {
}).catch(()=>{ console.log("加载消息失败")
reject(); reject();
}) })
}) })
@ -349,6 +392,16 @@ export default {
} }
return chat; return chat;
}, },
findChatByFriend: (state, getters) => (fid) => {
let chats = getters.findChats();
return chats.find(chat => chat.type == 'PRIVATE' &&
chat.targetId == fid)
},
findChatByGroup: (state, getters) => (gid) => {
let chats = getters.findChats();
return chats.find(chat => chat.type == 'GROUP' &&
chat.targetId == gid)
},
findMessage: (state) => (chat, msgInfo) => { findMessage: (state) => (chat, msgInfo) => {
if (!chat) { if (!chat) {
return null; return null;

2
im-web/src/view/Chat.vue

@ -12,7 +12,7 @@
</div> </div>
<el-scrollbar class="chat-list-items"> <el-scrollbar class="chat-list-items">
<div v-for="(chat,index) in chatStore.chats" :key="index"> <div v-for="(chat,index) in chatStore.chats" :key="index">
<chat-item v-show="chat.showName.includes(searchText)" :chat="chat" :index="index" <chat-item v-show="!chat.delete&&chat.showName.includes(searchText)" :chat="chat" :index="index"
@click.native="onActiveItem(index)" @delete="onDelItem(index)" @top="onTop(index)" @click.native="onActiveItem(index)" @delete="onDelItem(index)" @top="onTop(index)"
:active="chat === chatStore.activeChat"></chat-item> :active="chat === chatStore.activeChat"></chat-item>
</div> </div>

2
im-web/src/view/Friend.vue

@ -89,7 +89,7 @@
this.loadUserInfo(friend, idx); this.loadUserInfo(friend, idx);
}, },
onDelItem(friend, idx) { onDelItem(friend, idx) {
this.$confirm(`确认要解除与 '${friend.nickName}'的好友关系吗?`, '确认解除?', { this.$confirm(`确认删除'${friend.nickName}',并清空聊天记录吗?`, '确认解除?', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'

6
im-web/src/view/Group.vue

@ -172,7 +172,7 @@
}); });
}, },
onDissolve() { onDissolve() {
this.$confirm('确认要解散群聊吗?', '确认解散?', { this.$confirm(`确认要解散'${this.activeGroup.name}'吗?`, '确认解散?', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
@ -183,6 +183,7 @@
}).then(() => { }).then(() => {
this.$message.success(`群聊'${this.activeGroup.name}'已解散`); this.$message.success(`群聊'${this.activeGroup.name}'已解散`);
this.$store.commit("removeGroup", this.activeGroup.id); this.$store.commit("removeGroup", this.activeGroup.id);
this.$store.commit("removeGroupChat", this.activeGroup.id);
this.reset(); this.reset();
}); });
}) })
@ -208,7 +209,7 @@
}, },
onQuit() { onQuit() {
this.$confirm('退出群聊后将不再接受群里的消息,确认退出吗?', '确认退出?', { this.$confirm(`确认退出'${this.activeGroup.showGroupName}',并清空聊天记录吗?`, '确认退出?', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
@ -217,6 +218,7 @@
url: `/group/quit/${this.activeGroup.id}`, url: `/group/quit/${this.activeGroup.id}`,
method: 'delete' method: 'delete'
}).then(() => { }).then(() => {
this.$message.success(`您已退出'${this.activeGroup.name}'`);
this.$store.commit("removeGroup", this.activeGroup.id); this.$store.commit("removeGroup", this.activeGroup.id);
this.$store.commit("removeGroupChat", this.activeGroup.id); this.$store.commit("removeGroupChat", this.activeGroup.id);
this.reset(); this.reset();

4
im-web/src/view/Home.vue

@ -336,7 +336,9 @@
let unreadCount = 0; let unreadCount = 0;
let chats = this.$store.state.chatStore.chats; let chats = this.$store.state.chatStore.chats;
chats.forEach((chat) => { chats.forEach((chat) => {
unreadCount += chat.unreadCount if(!chat.delete){
unreadCount += chat.unreadCount
}
}); });
return unreadCount; return unreadCount;
} }

Loading…
Cancel
Save