Browse Source

!159 优化离线消息拉取效率

Merge pull request !159 from blue/v_3.0.0
master
blue 8 months ago
committed by Gitee
parent
commit
ccaeafeebb
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
  1. 4
      im-platform/src/main/java/com/bx/implatform/controller/FileController.java
  2. 9
      im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java
  3. 9
      im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java
  4. 2
      im-platform/src/main/java/com/bx/implatform/service/FileService.java
  5. 8
      im-platform/src/main/java/com/bx/implatform/service/GroupMessageService.java
  6. 8
      im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java
  7. 6
      im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java
  8. 2
      im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java
  9. 83
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  10. 36
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  11. 3
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  12. 93
      im-uniapp/App.vue
  13. 1
      im-uniapp/common/request.js
  14. 2
      im-uniapp/components/chat-item/chat-item.vue
  15. 2
      im-uniapp/components/group-item/group-item.vue
  16. 7
      im-uniapp/components/image-upload/image-upload.vue
  17. 1
      im-uniapp/main.js
  18. 1
      im-uniapp/package.json
  19. 42
      im-uniapp/pages/chat/chat-box.vue
  20. 2
      im-uniapp/pages/chat/chat.vue
  21. 2
      im-uniapp/pages/common/user-info.vue
  22. 2
      im-uniapp/pages/friend/friend-add.vue
  23. 11
      im-uniapp/pages/friend/friend.vue
  24. 18
      im-uniapp/pages/group/group-edit.vue
  25. 2
      im-uniapp/pages/group/group-info.vue
  26. 2
      im-uniapp/pages/mine/mine-edit.vue
  27. 86
      im-uniapp/store/chatStore.js
  28. 11
      im-web/src/components/chat/ChatBox.vue
  29. 2
      im-web/src/components/chat/ChatMessageItem.vue
  30. 5
      im-web/src/components/common/FileUpload.vue
  31. 2
      im-web/src/components/common/UserInfo.vue
  32. 2
      im-web/src/components/friend/AddFriend.vue
  33. 2
      im-web/src/components/group/GroupItem.vue
  34. 2
      im-web/src/components/setting/Setting.vue
  35. 80
      im-web/src/store/chatStore.js
  36. 37
      im-web/src/store/friendStore.js
  37. 2
      im-web/src/view/Chat.vue
  38. 2
      im-web/src/view/Friend.vue
  39. 4
      im-web/src/view/Group.vue
  40. 82
      im-web/src/view/Home.vue

4
im-platform/src/main/java/com/bx/implatform/controller/FileController.java

@ -25,8 +25,8 @@ public class FileController {
@Operation(summary = "上传图片", description = "上传图片,上传后返回原图和缩略图的url")
@PostMapping("/image/upload")
public Result<UploadImageVO> uploadImage(@RequestParam("file") MultipartFile file,
@RequestParam(defaultValue = "true") Boolean isPermanent) {
return ResultUtils.success(fileService.uploadImage(file,isPermanent));
@RequestParam(defaultValue = "true") Boolean isPermanent, @RequestParam(defaultValue = "50") Long thumbSize) {
return ResultUtils.success(fileService.uploadImage(file, isPermanent,thumbSize));
}
@Operation(summary = "上传文件", description = "上传文件,上传后返回文件url")

9
im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java

@ -35,12 +35,19 @@ public class GroupMessageController {
}
@GetMapping("/pullOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送")
@Operation(summary = "拉取离线消息(已废弃)", description = "拉取离线消息,消息将通过webscoket异步推送")
public Result pullOfflineMessage(@RequestParam Long minId) {
groupMessageService.pullOfflineMessage(minId);
return ResultUtils.success();
}
@GetMapping(value = "/loadOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息")
public Result<List<GroupMessageVO>> loadOfflineMessage(@RequestParam Long minId) {
return ResultUtils.success(groupMessageService.loadOffineMessage(minId));
}
@PutMapping("/readed")
@Operation(summary = "消息已读", description = "将群聊中的消息状态置为已读")
public Result readedMessage(@RequestParam Long groupId) {

9
im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java

@ -35,12 +35,19 @@ public class PrivateMessageController {
}
@GetMapping("/pullOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送")
@Operation(summary = "拉取离线消息(已废弃)", description = "拉取离线消息,消息将通过webscoket异步推送")
public Result pullOfflineMessage(@RequestParam Long minId) {
privateMessageService.pullOfflineMessage(minId);
return ResultUtils.success();
}
@GetMapping(value = "/loadOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息")
public Result<List<PrivateMessageVO>> loadOfflineMessage(@RequestParam Long minId) {
return ResultUtils.success(privateMessageService.loadOfflineMessage(minId));
}
@PutMapping("/readed")
@Operation(summary = "消息已读", description = "将会话中接收的消息状态置为已读")
public Result readedMessage(@RequestParam Long friendId) {

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

@ -9,7 +9,7 @@ public interface FileService extends IService<FileInfo> {
String uploadFile(MultipartFile file);
UploadImageVO uploadImage(MultipartFile file,Boolean isPermanent);
UploadImageVO uploadImage(MultipartFile file,Boolean isPermanent,Long thumbSize);
}

8
im-platform/src/main/java/com/bx/implatform/service/GroupMessageService.java

@ -31,6 +31,14 @@ public interface GroupMessageService extends IService<GroupMessage> {
*/
void pullOfflineMessage(Long minId);
/**
* 拉取离线消息只能拉取最近1个月的消息
*
* @param minId 消息起始id
*/
List<GroupMessageVO> loadOffineMessage(Long minId);
/**
* 消息已读,同步其他终端清空未读数量
*

8
im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java

@ -43,6 +43,14 @@ public interface PrivateMessageService extends IService<PrivateMessage> {
*/
void pullOfflineMessage(Long minId);
/**
* 拉取离线消息只能拉取最近1个月的消息
*
* @param minId 消息起始id
*/
List<PrivateMessageVO> loadOfflineMessage(Long minId);
/**
* 消息已读,将整个会话的消息都置为已读状态
*

6
im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java

@ -92,7 +92,7 @@ public class FileServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> imple
@Transactional
@Override
public UploadImageVO uploadImage(MultipartFile file, Boolean isPermanent) {
public UploadImageVO uploadImage(MultipartFile file, Boolean isPermanent,Long thumbSize) {
try {
Long userId = SessionContext.getSession().getUserId();
// 大小校验
@ -129,9 +129,9 @@ public class FileServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> imple
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败");
}
vo.setOriginUrl(generUrl(FileType.IMAGE, fileName));
if (file.getSize() > 50 * 1024) {
if (file.getSize() > thumbSize * 1024) {
// 大于50K的文件需上传缩略图
byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30);
byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), thumbSize);
String thumbFileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getImagePath(),
file.getOriginalFilename(), imageByte, file.getContentType());
if (StringUtils.isEmpty(thumbFileName)) {

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

@ -132,7 +132,7 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
friend.setUserId(userId);
friend.setFriendId(friendId);
User friendInfo = userMapper.selectById(friendId);
friend.setFriendHeadImage(friendInfo.getHeadImage());
friend.setFriendHeadImage(friendInfo.getHeadImageThumb());
friend.setFriendNickName(friendInfo.getNickName());
friend.setDeleted(false);
this.saveOrUpdate(friend);

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

@ -80,6 +80,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
msg.setSendTime(new Date());
msg.setSendNickName(member.getShowNickName());
msg.setAtUserIds(CommaTextUtils.asText(dto.getAtUserIds()));
msg.setStatus(MessageStatus.PENDING.code());
// 过滤内容中的敏感词
if (MessageType.TEXT.code().equals(dto.getType())) {
msg.setContent(sensitiveFilterUtil.filter(dto.getContent()));
@ -164,10 +165,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
wrapper.gt(GroupMessage::getSendTime, minDate);
wrapper.in(GroupMessage::getGroupId, groupIds);
wrapper.orderByDesc(GroupMessage::getId);
if (minId <= 0) {
// 首次拉取限制消息数量大小,防止内存溢出
wrapper.last("limit 100000");
}
wrapper.last("limit 50000");
List<GroupMessage> messages = this.list(wrapper);
// 通过群聊对消息进行分组
Map<Long, List<GroupMessage>> messageGroupMap =
@ -254,6 +252,83 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
});
}
@Override
public List<GroupMessageVO> loadOffineMessage(Long minId) {
UserSession session = SessionContext.getSession();
// 查询用户加入的群组
List<GroupMember> members = groupMemberService.findByUserId(session.getUserId());
Map<Long, GroupMember> groupMemberMap = CollStreamUtil.toIdentityMap(members, GroupMember::getGroupId);
Set<Long> groupIds = groupMemberMap.keySet();
// 只能拉取最近1个月的消息
Date minDate = DateUtils.addMonths(new Date(), -1);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.gt(GroupMessage::getId, minId);
wrapper.gt(GroupMessage::getSendTime, minDate);
wrapper.in(GroupMessage::getGroupId, groupIds);
wrapper.orderByDesc(GroupMessage::getId);
wrapper.last("limit 50000");
List<GroupMessage> messages = this.list(wrapper);
// 退群前的消息
List<GroupMember> quitMembers = groupMemberService.findQuitInMonth(session.getUserId());
for (GroupMember quitMember : quitMembers) {
wrapper = Wrappers.lambdaQuery();
wrapper.gt(GroupMessage::getId, minId);
wrapper.between(GroupMessage::getSendTime, minDate, quitMember.getQuitTime());
wrapper.eq(GroupMessage::getGroupId, quitMember.getGroupId());
wrapper.orderByDesc(GroupMessage::getId);
wrapper.last("limit 1000");
List<GroupMessage> groupMessages = this.list(wrapper);
if (!groupMessages.isEmpty()) {
messages.addAll(groupMessages);
groupMemberMap.put(quitMember.getGroupId(), quitMember);
}
}
// 通过群聊对消息进行分组
Map<Long, List<GroupMessage>> messageGroupMap =
messages.stream().collect(Collectors.groupingBy(GroupMessage::getGroupId));
List<GroupMessageVO> vos = new LinkedList<>();
for (Map.Entry<Long, List<GroupMessage>> entry : messageGroupMap.entrySet()) {
Long groupId = entry.getKey();
List<GroupMessage> groupMessages = entry.getValue();
// 填充消息状态
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
Object o = redisTemplate.opsForHash().get(key, session.getUserId().toString());
long readedMaxId = Objects.isNull(o) ? -1 : Long.parseLong(o.toString());
Map<Object, Object> maxIdMap = null;
for (GroupMessage m : groupMessages) {
// 排除加群之前的消息
GroupMember member = groupMemberMap.get(m.getGroupId());
if (DateUtil.compare(member.getCreatedTime(), m.getSendTime()) > 0) {
continue;
}
// 排除不需要接收的消息
List<String> recvIds = CommaTextUtils.asList(m.getRecvIds());
if (!recvIds.isEmpty() && !recvIds.contains(session.getUserId().toString())) {
continue;
}
// 组装vo
GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class);
// 被@用户列表
List<String> atIds = CommaTextUtils.asList(m.getAtUserIds());
vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
// 填充状态
vo.setStatus(readedMaxId >= m.getId() ? MessageStatus.READED.code() : MessageStatus.PENDING.code());
// 针对回执消息填充已读人数
if (m.getReceipt()) {
if (Objects.isNull(maxIdMap)) {
maxIdMap = redisTemplate.opsForHash().entries(key);
}
int count = getReadedUserIds(maxIdMap, m.getId(), m.getSendId()).size();
vo.setReadedCount(count);
}
vos.add(vo);
}
}
// 排序
return vos.stream().sorted(Comparator.comparing(GroupMessageVO::getId)).collect(Collectors.toList());
}
@Override
public void readedMessage(Long groupId) {
UserSession session = SessionContext.getSession();

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

@ -32,6 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.stream.Collectors;
@ -140,9 +141,8 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
UserSession session = SessionContext.getSession();
// 获取当前用户的消息
LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
// 只能拉取最近3个月的消息,移动端只拉取一个月消息
int months = session.getTerminal().equals(IMTerminalType.APP.code()) ? 1 : 3;
Date minDate = DateUtils.addMonths(new Date(), -months);
// 只能拉取最近1个月的消息
Date minDate = DateUtils.addMonths(new Date(), -1);
wrapper.gt(PrivateMessage::getId, minId);
wrapper.ge(PrivateMessage::getSendTime, minDate);
wrapper.and(wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).or()
@ -175,6 +175,36 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
});
}
@Override
public List<PrivateMessageVO> loadOfflineMessage(Long minId) {
UserSession session = SessionContext.getSession();
// 获取当前用户的消息
LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
// 只能拉取最近1个月的消息
Date minDate = DateUtils.addMonths(new Date(), -1);
wrapper.gt(PrivateMessage::getId, minId);
wrapper.ge(PrivateMessage::getSendTime, minDate);
wrapper.and(wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).or()
.eq(PrivateMessage::getRecvId, session.getUserId()));
wrapper.orderByAsc(PrivateMessage::getId);
List<PrivateMessage> messages = this.list(wrapper);
// 更新消息为送达状态
List<Long> messageIds =
messages.stream().filter(m -> m.getStatus().equals(MessageStatus.PENDING.code())).map(PrivateMessage::getId)
.collect(Collectors.toList());
if (!messageIds.isEmpty()) {
LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.in(PrivateMessage::getId, messageIds);
updateWrapper.set(PrivateMessage::getStatus, MessageStatus.DELIVERED.code());
update(updateWrapper);
}
// 转换vo
List<PrivateMessageVO> vos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class))
.collect(Collectors.toList());
log.info("拉取私聊消息,用户id:{},数量:{},minId:{}", session.getUserId(), messages.size(), minId);
return vos;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void readedMessage(Long friendId) {

3
im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java

@ -151,9 +151,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
if(!vo.getNickName().equals(sensitiveFilterUtil.filter(vo.getNickName()))){
throw new GlobalException("昵称包含敏感字符");
}
if(!vo.getSignature().equals(sensitiveFilterUtil.filter(vo.getSignature()))){
throw new GlobalException("签名内容包含敏感字符");
}
if (!session.getUserId().equals(vo.getId())) {
throw new GlobalException("不允许修改其他用户的信息");
}

93
im-uniapp/App.vue

@ -11,7 +11,9 @@ export default {
return {
isExit: false, // 退
audioTip: null,
reconnecting: false //
reconnecting: false, //
privateMessagesBuffer: [],
groupMessagesBuffer: [],
}
},
methods: {
@ -36,8 +38,7 @@ export default {
this.onReconnectWs();
} else {
// 线
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId);
this.pullOfflineMessage();
this.configStore.setAppInit(true);
}
});
@ -50,11 +51,21 @@ export default {
})
this.exit();
} else if (cmd == 3) {
//
this.handlePrivateMessage(msgInfo);
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.privateMessagesBuffer.push(msgInfo);
} else {
//
this.handlePrivateMessage(msgInfo);
}
} else if (cmd == 4) {
//
this.handleGroupMessage(msgInfo);
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.privateMessagesBuffer.push(msgInfo);
} else {
//
this.handleGroupMessage(msgInfo);
}
} else if (cmd == 5) {
//
this.handleSystemMessage(msgInfo);
@ -84,30 +95,43 @@ export default {
this.configStore.clear();
this.userStore.clear();
},
pullOfflineMessage() {
this.chatStore.setLoading(true);
const promises = [];
promises.push(this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId));
promises.push(this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId));
Promise.all(promises).then(messages => {
// 线
messages[0].forEach(m => this.handlePrivateMessage(m));
messages[1].forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer.forEach(m => this.handlePrivateMessage(m));
this.groupMessagesBuffer.forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer = [];
this.groupMessagesBuffer = [];
// 线
this.chatStore.setLoading(false);
//
this.chatStore.refreshChats();
}).catch((e) => {
console.log(e)
this.$message.error("拉取离线消息失败");
this.onExit();
})
},
pullPrivateOfflineMessage(minId) {
this.chatStore.setLoadingPrivateMsg(true)
http({
url: "/message/private/pullOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
uni.showToast({
title: "消息拉取失败,请重新登陆",
icon: 'none'
})
this.exit()
return this.$http({
url: "/message/private/loadOfflineMessage?minId=" + minId,
method: 'GET',
timeout: 60000
})
},
pullGroupOfflineMessage(minId) {
this.chatStore.setLoadingGroupMsg(true)
http({
url: "/message/group/pullOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
uni.showToast({
title: "消息拉取失败,请重新登陆",
icon: 'none'
})
this.exit()
return this.$http({
url: "/message/group/loadOfflineMessage?minId=" + minId,
method: 'GET',
timeout: 60000
})
},
handlePrivateMessage(msg) {
@ -120,11 +144,6 @@ export default {
type: 'PRIVATE',
targetId: friendId
}
//
if (msg.type == enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingPrivateMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == enums.MESSAGE_TYPE.READED) {
this.chatStore.resetUnreadCount(chatInfo);
@ -203,8 +222,7 @@ export default {
//
this.chatStore.insertMessage(msg, chatInfo);
//
this.chatStore.insertMessage(msg, chatInfo);
if (!friend.isDnd && !this.chatStore.isLoading() &&
if (!friend.isDnd && !this.chatStore.loading &&
!msg.selfSend && msgType.isNormal(msg.type) &&
msg.status != enums.MESSAGE_STATUS.READED) {
this.playAudioTip();
@ -220,11 +238,6 @@ export default {
type: 'GROUP',
targetId: msg.groupId
}
//
if (msg.type == enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingGroupMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == enums.MESSAGE_TYPE.READED) {
//
@ -323,7 +336,7 @@ export default {
//
this.chatStore.insertMessage(msg, chatInfo);
//
if (!group.isDnd && !this.chatStore.isLoading() &&
if (!group.isDnd && !this.chatStore.loading &&
!msg.selfSend && msgType.isNormal(msg.type) &&
msg.status != enums.MESSAGE_STATUS.READED) {
this.playAudioTip();

1
im-uniapp/common/request.js

@ -17,6 +17,7 @@ const request = (options) => {
method: options.method || 'GET',
header: header,
data: options.data || {},
timeout: options.timeout || 3000,
async success(res) {
if (res.data.code == 200) {
return resolve(res.data.data)

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

@ -45,7 +45,7 @@ export default {
methods: {
showChatBox() {
//
if (!this.configStore.appInit || this.chatStore.isLoading()) {
if (!this.configStore.appInit || this.chatStore.loading) {
uni.showToast({
title: "正在初始化页面,请稍后...",
icon: 'none'

2
im-uniapp/components/group-item/group-item.vue

@ -1,6 +1,6 @@
<template>
<view class="group-item" @click="showGroupInfo()">
<head-image :name="group.showGroupName" :url="group.headImage" size="small"></head-image>
<head-image :name="group.showGroupName" :url="group.headImageThumb" size="small"></head-image>
<view class="group-name">
<view>{{ group.showGroupName }}</view>
</view>

7
im-uniapp/components/image-upload/image-upload.vue

@ -33,6 +33,10 @@ export default {
type: Boolean,
default: false
},
thumbSize: {
type: Number,
default: 50
},
onBefore: {
type: Function,
default: null
@ -63,8 +67,9 @@ export default {
})
},
uploadImage(file) {
let action = `/image/upload?isPermanent=${this.isPermanent}&thumbSize=${this.thumbSize}`
uni.uploadFile({
url: UNI_APP.BASE_URL + '/image/upload?isPermanent=' + this.isPermanent,
url: UNI_APP.BASE_URL + action,
header: {
accessToken: uni.getStorageSync("loginInfo").accessToken
},

1
im-uniapp/main.js

@ -23,6 +23,7 @@ import switchBar from '@/components/bar/switch-bar'
import * as recorder from './common/recorder-h5';
import ImageResize from "quill-image-resize-mp";
import Quill from "quill";
import 'default-passive-events';
// 以下组件用于兼容部分手机聊天边框无法输入的问题
window.Quill = Quill;
window.ImageResize = { default: ImageResize };

1
im-uniapp/package.json

@ -4,6 +4,7 @@
"scripts": {}
},
"dependencies": {
"default-passive-events": "^4.0.0",
"pinyin-pro": "^3.23.1",
"quill": "^1.3.7",
"quill-image-resize-mp": "^3.0.1",

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

@ -257,12 +257,12 @@ export default {
this.atUserIds = atUserIds;
},
onLongPressHead(msgInfo) {
if (!msgInfo.selfSend && this.chat.type == "GROUP" && this.atUserIds.indexOf(msgInfo.sendId) < 0) {
if (!msgInfo.selfSend && this.isGroup && this.atUserIds.indexOf(msgInfo.sendId) < 0) {
this.atUserIds.push(msgInfo.sendId);
}
},
headImage(msgInfo) {
if (this.chat.type == 'GROUP') {
if (this.isGroup) {
let member = this.groupMembers.find((m) => m.userId == msgInfo.sendId);
return member ? member.headImage : "";
} else {
@ -270,7 +270,7 @@ export default {
}
},
showName(msgInfo) {
if (this.chat.type == 'GROUP') {
if (this.isGroup) {
let member = this.groupMembers.find((m) => m.userId == msgInfo.sendId);
return member ? member.showNickName : "";
} else {
@ -325,7 +325,6 @@ export default {
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
this.moveChatToTop();
this.sendMessageRequest(msgInfo).then((m) => {
//
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
@ -358,7 +357,7 @@ export default {
return atText;
},
fillTargetId(msgInfo, targetId) {
if (this.chat.type == "GROUP") {
if (this.isGroup) {
msgInfo.groupId = targetId;
} else {
msgInfo.recvId = targetId;
@ -542,7 +541,7 @@ export default {
//
this.chatStore.deleteMessage(msgInfo, chat);
//
msgInfo.temId = this.generateId();
msgInfo.tmpId = this.generateId();
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
this.moveChatToTop();
@ -676,7 +675,7 @@ export default {
}, 50)
},
onShowMore() {
if (this.chat.type == "GROUP") {
if (this.isGroup) {
uni.navigateTo({
url: "/pages/group/group-info?id=" + this.group.id
})
@ -729,7 +728,7 @@ export default {
readedMessage() {
if (this.unreadCount > 0) {
let url = ""
if (this.chat.type == "GROUP") {
if (this.isGroup) {
url = `/message/group/readed?groupId=${this.chat.targetId}`
} else {
url = `/message/private/readed?friendId=${this.chat.targetId}`
@ -920,7 +919,7 @@ export default {
sendTime: new Date().getTime(),
type: this.$enums.MESSAGE_TYPE.TIP_TEXT
}
if (this.chat.type == "PRIVATE") {
if (this.isPrivate) {
msgInfo.recvId = this.mine.id
msgInfo.content = "该用户已被管理员封禁,原因:" + this.userInfo.reason
} else {
@ -970,7 +969,7 @@ export default {
return "";
}
let title = this.chat.showName;
if (this.chat.type == "GROUP") {
if (this.isGroup) {
let size = this.groupMembers.filter(m => !m.quit).length;
title += `(${size})`;
}
@ -992,8 +991,8 @@ export default {
return this.chat.unreadCount;
},
isBanned() {
return (this.chat.type == "PRIVATE" && this.userInfo.isBanned) ||
(this.chat.type == "GROUP" && this.group.isBanned)
return (this.isPrivate && this.userInfo.isBanned) ||
(this.isGroup && this.group.isBanned)
},
atUserItems() {
let atUsers = [];
@ -1014,6 +1013,15 @@ export default {
},
memberSize() {
return this.groupMembers.filter(m => !m.quit).length;
},
isGroup() {
return this.chat.type == 'GROUP';
},
isPrivate() {
return this.chat.type == 'PRIVATE';
},
loading() {
return this.chatStore.loading;
}
},
watch: {
@ -1039,6 +1047,14 @@ export default {
this.readedMessage()
}
}
},
loading: {
handler(newLoading, oldLoading) {
// 线
if (!newLoading && this.isPrivate) {
this.loadReaded(this.chat.targetId)
}
}
}
},
onLoad(options) {
@ -1050,7 +1066,7 @@ export default {
//
this.readedMessage()
//
if (this.chat.type == "GROUP") {
if (this.isGroup) {
this.loadGroup(this.chat.targetId);
} else {
this.loadFriend(this.chat.targetId);

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

@ -111,7 +111,7 @@ export default {
return count;
},
loading() {
return this.chatStore.isLoading();
return this.chatStore.loading;
},
initializing() {
return !this.configStore.appInit;

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

@ -61,7 +61,7 @@ export default {
type: 'PRIVATE',
targetId: this.userInfo.id,
showName: this.userInfo.nickName,
headImage: this.userInfo.headImage,
headImage: this.userInfo.headImageThumb,
};
if (this.isFriend) {
chat.isDnd = this.friendInfo.isDnd;

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

@ -60,7 +60,7 @@ export default {
let friend = {
id: user.id,
nickName: user.nickName,
headImage: user.headImage,
headImage: user.headImageThumb,
online: user.online,
delete: false
}

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

@ -12,7 +12,7 @@
温馨提示您现在还没有任何好友快点击右上方'+'按钮添加好友吧~
</view>
<view class="friend-items" v-else>
<up-index-list :index-list="friendIdx">
<up-index-list :index-list="friendIdx" :sticky="false" :custom-nav-height="50">
<template v-for="(friends, i) in friendGroups">
<up-index-item>
<up-index-anchor :text="friendIdx[i] == '*' ? '在线' : friendIdx[i]"></up-index-anchor>
@ -118,15 +118,6 @@ export default {
color: $im-text-color !important;
}
:deep(.u-index-list__letter__item) {
width: 40rpx !important;
height: 40rpx !important;
}
:deep(.u-index-list__letter__item__index) {
font-size: $im-font-size-small !important;
}
.friend-tip {
position: absolute;
top: 400rpx;

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

@ -1,11 +1,11 @@
<template>
<view class="page group-edit">
<view class="page group-edit">
<nav-bar back>修改群资料</nav-bar>
<view class="form">
<view class="form-item">
<view class="label">群聊头像</view>
<view class="value"></view>
<image-upload v-if="isOwner" :isPermanent="true" :onSuccess="onUnloadImageSuccess">
<image-upload v-if="isOwner" :isPermanent="true" :thumbSize="20" :onSuccess="onUnloadImageSuccess">
<image :src="group.headImageThumb" class="group-image"></image>
</image-upload>
<head-image v-else class="group-image" :name="group.showGroupName" :url="group.headImageThumb"
@ -13,19 +13,22 @@
</view>
<view class="form-item">
<view class="label">群聊名称</view>
<input class="input" :class="isOwner?'':'disable'" maxlength="20" v-model="group.name" :disabled="!isOwner" placeholder="请输入群聊名称"/>
<input class="input" :class="isOwner?'':'disable'" maxlength="20" v-model="group.name"
:disabled="!isOwner" placeholder="请输入群聊名称" />
</view>
<view class="form-item">
<view class="label">群聊备注</view>
<input class="input" maxlength="20" v-model="group.remarkGroupName" :placeholder="group.name"/>
<input class="input" maxlength="20" v-model="group.remarkGroupName" :placeholder="group.name" />
</view>
<view class="form-item">
<view class="label">我在本群的昵称</view>
<input class="input" maxlength="20" v-model="group.remarkNickName" :placeholder="userStore.userInfo.nickName"/>
<input class="input" maxlength="20" v-model="group.remarkNickName"
:placeholder="userStore.userInfo.nickName" />
</view>
<view class="form-item">
<view class="label">群公告</view>
<textarea class="notice" :class="isOwner?'':'disable'" maxlength="512" :disabled="!isOwner" v-model="group.notice" :placeholder="isOwner?'请输入群公告':''"></textarea>
<textarea class="notice" :class="isOwner?'':'disable'" maxlength="512" :disabled="!isOwner"
v-model="group.notice" :placeholder="isOwner?'请输入群公告':''"></textarea>
</view>
</view>
<button class="bottom-btn" type="primary" @click="submit()">提交</button>
@ -162,7 +165,7 @@ export default {
white-space: nowrap;
}
.value{
.value {
flex: 1;
}
@ -193,5 +196,4 @@ export default {
}
}
}
</style>

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

@ -122,7 +122,7 @@ export default {
type: 'GROUP',
targetId: this.group.id,
showName: this.group.showGroupName,
headImage: this.group.headImage,
headImage: this.group.headImageThumb,
isDnd: this.group.isDnd
};
this.chatStore.openChat(chat);

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

@ -4,7 +4,7 @@
<view class="form">
<view class="form-item">
<view class="label">头像</view>
<image-upload class="value" :isPermanent="true" :onSuccess="onUnloadImageSuccess">
<image-upload class="value" :isPermanent="true" :thumbSize="20" :onSuccess="onUnloadImageSuccess">
<image :src="userInfo.headImageThumb" class="head-image"></image>
</image-upload>
</view>

86
im-uniapp/store/chatStore.js

@ -3,7 +3,6 @@ import { MESSAGE_TYPE, MESSAGE_STATUS } from '@/common/enums.js';
import useFriendStore from './friendStore.js';
import useGroupStore from './groupStore.js';
import useUserStore from './userStore';
import useConfigStore from './configStore.js';
let cacheChats = [];
export default defineStore('chatStore', {
@ -12,8 +11,7 @@ export default defineStore('chatStore', {
chats: [],
privateMsgMaxId: 0,
groupMsgMaxId: 0,
loadingPrivateMsg: false,
loadingGroupMsg: false
loading: false
}
},
actions: {
@ -140,7 +138,7 @@ export default defineStore('chatStore', {
}
},
moveTop(idx) {
if (this.isLoading()) {
if (this.loading) {
return;
}
let chats = this.curChats;
@ -156,20 +154,18 @@ export default defineStore('chatStore', {
insertMessage(msgInfo, chatInfo) {
// 获取对方id或群id
let type = chatInfo.type;
// 完成初始化之前不能修改消息最大id,否则可能导致拉不到离线消息
if (useConfigStore().appInit) {
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
this.groupMsgMaxId = msgInfo.id;
}
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
this.groupMsgMaxId = msgInfo.id;
}
// 如果是已存在消息,则覆盖旧的消息数据
let chat = this.findChat(chatInfo);
let message = this.findMessage(chat, msgInfo);
if (message) {
console.log("message:", message)
Object.assign(message, msgInfo);
chat.stored = false;
this.saveToStorage();
@ -218,24 +214,8 @@ export default defineStore('chatStore', {
});
chat.lastTimeTip = msgInfo.sendTime;
}
// 根据id顺序插入,防止消息乱序
let insertPos = chat.messages.length;
// 防止 图片、文件 在发送方 显示 在顶端 因为还没存库,id=0
if (msgInfo.id && msgInfo.id > 0) {
for (let idx in chat.messages) {
if (chat.messages[idx].id && msgInfo.id < chat.messages[idx].id) {
insertPos = idx;
console.log(`消息出现乱序,位置:${chat.messages.length},修正至:${insertPos}`);
break;
}
}
}
if (insertPos == chat.messages.length) {
// 这种赋值效率最高
chat.messages[insertPos] = msgInfo;
} else {
chat.messages.splice(insertPos, 0, msgInfo);
}
// 插入消息
chat.messages.push(msgInfo);
chat.stored = false;
this.saveToStorage();
},
@ -345,17 +325,8 @@ export default defineStore('chatStore', {
this.saveToStorage();
}
},
setLoadingPrivateMsg(loading) {
this.loadingPrivateMsg = loading;
if (!this.isLoading()) {
this.refreshChats()
}
},
setLoadingGroupMsg(loading) {
this.loadingGroupMsg = loading;
if (!this.isLoading()) {
this.refreshChats()
}
setLoading(loading) {
this.loading = loading;
},
setDnd(chatInfo, isDnd) {
let chat = this.findChat(chatInfo);
@ -406,7 +377,7 @@ export default defineStore('chatStore', {
},
saveToStorage(withColdMessage) {
// 加载中不保存,防止卡顿
if (this.isLoading()) {
if (this.loading) {
return;
}
const userStore = useUserStore();
@ -493,11 +464,8 @@ export default defineStore('chatStore', {
}
},
getters: {
isLoading: (state) => () => {
return state.loadingPrivateMsg || state.loadingGroupMsg
},
curChats: (state) => {
if (cacheChats && state.isLoading()) {
if (cacheChats && state.loading) {
return cacheChats;
}
return state.chats;
@ -529,17 +497,29 @@ export default defineStore('chatStore', {
if (!chat) {
return null;
}
for (let idx in chat.messages) {
for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
// 通过id判断
if (msgInfo.id && chat.messages[idx].id == msgInfo.id) {
return chat.messages[idx];
if (msgInfo.id && chat.messages[idx].id) {
if (msgInfo.id == chat.messages[idx].id) {
return chat.messages[idx];
}
// 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.id > chat.messages[idx].id) {
break;
}
}
// 正在发送中的消息可能没有id,只有tmpId
if (msgInfo.tmpId && chat.messages[idx].tmpId &&
chat.messages[idx].tmpId == msgInfo.tmpId) {
return chat.messages[idx];
if (msgInfo.tmpId && chat.messages[idx].tmpId) {
if (msgInfo.tmpId == chat.messages[idx].tmpId) {
return chat.messages[idx];
}
// 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.tmpId > chat.messages[idx].tmpId) {
break;
}
}
}
return null;
}
}
});

11
im-web/src/components/chat/ChatBox.vue

@ -784,6 +784,9 @@ export default {
},
isPrivate() {
return this.chat.type == 'PRIVATE';
},
loading() {
return this.chatStore.loading;
}
},
watch: {
@ -833,6 +836,14 @@ export default {
}
}
}
},
loading: {
handler(newLoading, oldLoading) {
// 线
if (!newLoading && this.isPrivate) {
this.loadReaded(this.chat.targetId)
}
}
}
},
mounted() {

2
im-web/src/components/chat/ChatMessageItem.vue

@ -320,7 +320,7 @@ export default {
.message-image {
border-radius: 8px;
border: 3px solid var(--im-color-primary-light-8);
border: 2px solid var(--im-color-primary-light-9);
overflow: hidden;
cursor: pointer;
}

5
im-web/src/components/common/FileUpload.vue

@ -55,8 +55,11 @@ export default {
}
let formData = new FormData()
formData.append('file', file.file)
let url = this.action;
url += this.action.includes("?") ? "&" : "?"
url += 'isPermanent=' + this.isPermanent;
this.$http({
url: this.action + '?isPermanent=' + this.isPermanent,
url: url,
data: formData,
method: 'post',
headers: {

2
im-web/src/components/common/UserInfo.vue

@ -65,7 +65,7 @@ export default {
type: 'PRIVATE',
targetId: user.id,
showName: user.nickName,
headImage: user.headImage
headImage: user.headImageThumb
};
if (this.isFriend) {
chat.isDnd = this.friendInfo.isDnd;

2
im-web/src/components/friend/AddFriend.vue

@ -79,7 +79,7 @@ export default {
let friend = {
id: user.id,
nickName: user.nickName,
headImage: user.headImage,
headImage: user.headImageThumb,
online: user.online,
deleted: false
}

2
im-web/src/components/group/GroupItem.vue

@ -1,7 +1,7 @@
<template>
<div class="group-item" :class="active ? 'active' : ''">
<div class="group-avatar">
<head-image :size="42" :name="group.showGroupName" :url="group.headImage"> </head-image>
<head-image :size="42" :name="group.showGroupName" :url="group.headImageThumb"> </head-image>
</div>
<div class="group-name">
<div>{{ group.showGroupName }}</div>

2
im-web/src/components/setting/Setting.vue

@ -87,7 +87,7 @@ export default {
},
computed: {
imageAction() {
return `/image/upload`;
return `/image/upload?thumbSize=20`;
}
},
watch: {

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

@ -3,7 +3,6 @@ import { MESSAGE_TYPE, MESSAGE_STATUS } from "../api/enums.js"
import useFriendStore from './friendStore.js';
import useGroupStore from './groupStore.js';
import useUserStore from './userStore.js';
import useConfigStore from './configStore.js';
import localForage from 'localforage';
/**
@ -28,11 +27,10 @@ export default defineStore('chatStore', {
state: () => {
return {
activeChat: null,
chats: [],
privateMsgMaxId: 0,
groupMsgMaxId: 0,
loadingPrivateMsg: false,
loadingGroupMsg: false,
chats: []
loading: false
}
},
actions: {
@ -141,7 +139,7 @@ export default defineStore('chatStore', {
},
moveTop(idx) {
// 加载中不移动,很耗性能
if (this.isLoading()) {
if (this.loading) {
return;
}
if (idx > 0) {
@ -156,15 +154,12 @@ export default defineStore('chatStore', {
},
insertMessage(msgInfo, chatInfo) {
let type = chatInfo.type;
// 完成初始化之前不能修改消息最大id,否则可能导致拉不到离线消息
if (useConfigStore().appInit) {
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
this.groupMsgMaxId = msgInfo.id;
}
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
this.groupMsgMaxId = msgInfo.id;
}
// 如果是已存在消息,则覆盖旧的消息数据
let chat = this.findChat(chatInfo);
@ -218,19 +213,7 @@ export default defineStore('chatStore', {
});
chat.lastTimeTip = msgInfo.sendTime;
}
// 根据id顺序插入,防止消息乱序
let insertPos = chat.messages.length;
// 防止 图片、文件 在发送方 显示 在顶端 因为还没存库,id=0
if (msgInfo.id && msgInfo.id > 0) {
for (let idx in chat.messages) {
if (chat.messages[idx].id && msgInfo.id < chat.messages[idx].id) {
insertPos = idx;
console.log(`消息出现乱序,位置:${chat.messages.length},修正至:${insertPos}`);
break;
}
}
}
chat.messages.splice(insertPos, 0, msgInfo);
chat.messages.push(msgInfo);
chat.stored = false;
this.saveToStorage();
},
@ -340,17 +323,8 @@ export default defineStore('chatStore', {
this.saveToStorage()
}
},
setLoadingPrivateMsg(loading) {
this.loadingPrivateMsg = loading;
if (!this.isLoading()) {
this.refreshChats();
}
},
setLoadingGroupMsg(loading) {
this.loadingGroupMsg = loading;
if (!this.isLoading()) {
this.refreshChats();
}
setLoading(loading) {
this.loading = loading;
},
setDnd(chatInfo, isDnd) {
let chat = this.findChat(chatInfo);
@ -399,7 +373,7 @@ export default defineStore('chatStore', {
},
saveToStorage(withColdMessage) {
// 加载中不保存,防止卡顿
if (this.isLoading()) {
if (this.loading) {
return;
}
const userStore = useUserStore();
@ -456,6 +430,9 @@ export default defineStore('chatStore', {
cacheChats = []
this.chats = [];
this.activeChat = null;
this.privateMsgMaxId = 0;
this.groupMsgMaxId = 0;
this.loading = false;
},
loadChat() {
return new Promise((resolve, reject) => {
@ -511,7 +488,7 @@ export default defineStore('chatStore', {
return state.loadingPrivateMsg || state.loadingGroupMsg
},
findChats: (state) => () => {
if (cacheChats && state.isLoading()) {
if (cacheChats && state.loading) {
return cacheChats;
}
return state.chats;
@ -545,15 +522,26 @@ export default defineStore('chatStore', {
if (!chat) {
return null;
}
for (let idx in chat.messages) {
for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
// 通过id判断
if (msgInfo.id && chat.messages[idx].id == msgInfo.id) {
return chat.messages[idx];
if (msgInfo.id && chat.messages[idx].id) {
if (msgInfo.id == chat.messages[idx].id) {
return chat.messages[idx];
}
// 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.id > chat.messages[idx].id) {
break;
}
}
// 正在发送中的消息可能没有id,只有tmpId
if (msgInfo.tmpId && chat.messages[idx].tmpId &&
chat.messages[idx].tmpId == msgInfo.tmpId) {
return chat.messages[idx];
if (msgInfo.tmpId && chat.messages[idx].tmpId) {
if (msgInfo.tmpId == chat.messages[idx].tmpId) {
return chat.messages[idx];
}
// 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.tmpId > chat.messages[idx].tmpId) {
break;
}
}
}
}

37
im-web/src/store/friendStore.js

@ -33,14 +33,36 @@ export default defineStore('friendStore', {
this.friends.unshift(friend);
}
},
updateOnlineStatus(onlineData) {
let friend = this.findFriend(onlineData.userId);
if (onlineData.terminal == TERMINAL_TYPE.WEB) {
friend.onlineWeb = onlineData.online;
} else if (onlineData.terminal == TERMINAL_TYPE.APP) {
friend.onlineApp = onlineData.online;
setOnlineStatus(onlineTerminals) {
this.friends.forEach((f) => {
let userTerminal = onlineTerminals.find((o) => f.id == o.userId);
if (userTerminal) {
f.online = true;
f.onlineWeb = userTerminal.terminals.indexOf(TERMINAL_TYPE.WEB) >= 0
f.onlineApp = userTerminal.terminals.indexOf(TERMINAL_TYPE.APP) >= 0
} else {
f.online = false;
f.onlineWeb = false;
f.onlineApp = false;
}
});
},
refreshOnlineStatus() {
let userIds = this.friends.filter((f) => !f.deleted).map((f) => f.id);
if (userIds.length == 0) {
return;
}
friend.online = friend.onlineWeb || friend.onlineApp;
http({
url: '/user/terminal/online?userIds=' + userIds.join(','),
method: 'GET'
}).then((onlineTerminals) => {
this.setOnlineStatus(onlineTerminals);
})
// 30s后重新拉取
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.refreshOnlineStatus();
}, 30000)
},
setDnd(id, isDnd) {
let friend = this.findFriend(id);
@ -58,6 +80,7 @@ export default defineStore('friendStore', {
method: 'GET'
}).then(async (friends) => {
this.setFriends(friends);
this.refreshOnlineStatus();
resolve();
}).catch(e => {
reject(e);

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

@ -89,7 +89,7 @@ export default {
},
computed: {
loading() {
return this.chatStore.loadingGroupMsg || this.chatStore.loadingPrivateMsg
return this.chatStore.loading;
}
}
}

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

@ -118,7 +118,7 @@ export default {
let friend = {
id: user.id,
nickName: user.nickName,
headImage: user.headImage,
headImage: user.headImageThumb,
online: user.online
}
this.friendStore.addFriend(friend);

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

@ -240,7 +240,7 @@ export default {
type: 'GROUP',
targetId: this.activeGroup.id,
showName: this.activeGroup.showGroupName,
headImage: this.activeGroup.headImage,
headImage: this.activeGroup.headImageThumb,
isDnd: this.activeGroup.isDnd
};
this.chatStore.openChat(chat);
@ -295,7 +295,7 @@ export default {
return this.activeGroup.ownerId == this.userStore.userInfo.id;
},
imageAction() {
return `/image/upload`;
return `/image/upload?thumbSize=20`;
},
groupMap() {
//

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

@ -78,7 +78,9 @@ export default {
showSettingDialog: false,
lastPlayAudioTime: new Date().getTime() - 1000,
isFullscreen: true,
reconnecting: false
reconnecting: false,
privateMessagesBuffer: [],
groupMessagesBuffer: []
}
},
methods: {
@ -108,8 +110,7 @@ export default {
this.onReconnectWs();
} else {
// 线
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId);
this.pullOfflineMessage();
this.configStore.setAppInit(true);
}
});
@ -124,13 +125,22 @@ export default {
location.href = "/";
}
});
} else if (cmd == 3) {
//
this.handlePrivateMessage(msgInfo);
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.privateMessagesBuffer.push(msgInfo);
} else {
//
this.handlePrivateMessage(msgInfo);
}
} else if (cmd == 4) {
//
this.handleGroupMessage(msgInfo);
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.groupMessagesBuffer.push(msgInfo);
} else {
//
this.handleGroupMessage(msgInfo);
}
} else if (cmd == 5) {
//
this.handleSystemMessage(msgInfo);
@ -170,8 +180,7 @@ export default {
promises.push(this.groupStore.loadGroup());
Promise.all(promises).then(() => {
// 线
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId);
this.pullOfflineMessage();
this.configStore.setAppInit(true)
this.$message.success("重新连接成功");
}).catch(() => {
@ -194,23 +203,42 @@ export default {
this.groupStore.clear();
this.chatStore.clear();
this.userStore.clear();
},
pullOfflineMessage() {
this.chatStore.setLoading(true);
const promises = [];
promises.push(this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId));
promises.push(this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId));
Promise.all(promises).then(messages => {
// 线
messages[0].forEach(m => this.handlePrivateMessage(m));
messages[1].forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer.forEach(m => this.handlePrivateMessage(m));
this.groupMessagesBuffer.forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer = [];
this.groupMessagesBuffer = [];
// 线
this.chatStore.setLoading(false);
//
this.chatStore.refreshChats();
}).catch((e) => {
console.log(e)
this.$message.error("拉取离线消息失败");
this.onExit();
})
},
pullPrivateOfflineMessage(minId) {
this.chatStore.setLoadingPrivateMsg(true)
this.$http({
url: "/message/private/pullOfflineMessage?minId=" + minId,
return this.$http({
url: "/message/private/loadOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
this.chatStore.setLoadingPrivateMsg(false)
})
},
pullGroupOfflineMessage(minId) {
this.chatStore.setLoadingGroupMsg(true)
this.$http({
url: "/message/group/pullOfflineMessage?minId=" + minId,
return this.$http({
url: "/message/group/loadOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
this.chatStore.setLoadingGroupMsg(false)
})
},
handlePrivateMessage(msg) {
@ -223,11 +251,6 @@ export default {
type: 'PRIVATE',
targetId: friendId
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingPrivateMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.READED) {
this.chatStore.resetUnreadCount(chatInfo)
@ -285,7 +308,7 @@ export default {
//
this.chatStore.insertMessage(msg, chatInfo);
//
if (!friend.isDnd && !this.chatStore.isLoading() && !msg.selfSend && this.$msgType.isNormal(msg.type) &&
if (!friend.isDnd && !this.chatStore.loading && !msg.selfSend && this.$msgType.isNormal(msg.type) &&
msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip();
}
@ -297,11 +320,6 @@ export default {
type: 'GROUP',
targetId: msg.groupId
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingGroupMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.READED) {
//
@ -367,7 +385,7 @@ export default {
//
this.chatStore.insertMessage(msg, chatInfo);
//
if (!group.isDnd && !this.chatStore.isLoading() &&
if (!group.isDnd && !this.chatStore.loading &&
!msg.selfSend && this.$msgType.isNormal(msg.type)
&& msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip();

Loading…
Cancel
Save