Browse Source

已读未读显示(开发中)

master
xie.bx 2 years ago
parent
commit
77da9edb61
  1. 11
      im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java
  2. 12
      im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java
  3. 10
      im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java
  4. 11
      im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java
  5. 6
      im-commom/src/main/java/com/bx/imcommon/model/IMGroupMessage.java
  6. 18
      im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java
  7. 15
      im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java
  8. 2
      im-platform/src/main/java/com/bx/implatform/controller/UserController.java
  9. 12
      im-platform/src/main/java/com/bx/implatform/enums/FileType.java
  10. 17
      im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java
  11. 11
      im-platform/src/main/java/com/bx/implatform/enums/MessageType.java
  12. 23
      im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java
  13. 4
      im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java
  14. 4
      im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java
  15. 3
      im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java
  16. 167
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  17. 99
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  18. 28
      im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java
  19. 30
      im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java
  20. 3
      im-ui/src/api/emotion.js
  21. 12
      im-ui/src/api/enums.js
  22. 52
      im-ui/src/components/chat/ChatBox.vue
  23. 37
      im-ui/src/components/chat/ChatMessageItem.vue
  24. 153
      im-ui/src/store/chatStore.js
  25. 39
      im-ui/src/store/friendStore.js
  26. 52
      im-ui/src/store/groupStore.js
  27. 27
      im-ui/src/store/index.js
  28. 1
      im-ui/src/store/uiStore.js
  29. 28
      im-ui/src/store/userStore.js
  30. 24
      im-ui/src/view/Chat.vue
  31. 222
      im-ui/src/view/Home.vue

11
im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java

@ -1,7 +1,9 @@
package com.bx.imcommon.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum IMCmdType {
LOGIN(0,"登陆"),
@ -11,15 +13,10 @@ public enum IMCmdType {
GROUP_MESSAGE(4,"群发消息");
private Integer code;
private String desc;
IMCmdType(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static IMCmdType fromCode(Integer code){
for (IMCmdType typeEnum:values()) {
@ -31,10 +28,6 @@ public enum IMCmdType {
}
public String description() {
return desc;
}
public Integer code(){
return this.code;
}

12
im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java

@ -1,5 +1,8 @@
package com.bx.imcommon.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum IMListenerType{
ALL(0,"全部消息"),
PRIVATE_MESSAGE(1,"私聊消息"),
@ -9,15 +12,6 @@ public enum IMListenerType{
private String desc;
IMListenerType(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public String description() {
return desc;
}
public Integer code(){

10
im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java

@ -1,6 +1,8 @@
package com.bx.imcommon.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum IMSendCode {
SUCCESS(0,"发送成功"),
@ -11,11 +13,6 @@ public enum IMSendCode {
private Integer code;
private String desc;
// 构造方法
IMSendCode(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static IMSendCode fromCode(Integer code){
for (IMSendCode typeEnum:values()) {
@ -27,9 +24,6 @@ public enum IMSendCode {
}
public String description() {
return desc;
}
public Integer code(){

11
im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java

@ -1,9 +1,12 @@
package com.bx.imcommon.enums;
import lombok.AllArgsConstructor;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
public enum IMTerminalType {
WEB(0,"web"),
@ -13,10 +16,6 @@ public enum IMTerminalType {
private String desc;
IMTerminalType(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static IMTerminalType fromCode(Integer code){
for (IMTerminalType typeEnum:values()) {
@ -31,10 +30,6 @@ public enum IMTerminalType {
return Arrays.stream(values()).map(IMTerminalType::code).collect(Collectors.toList());
}
public String description() {
return desc;
}
public Integer code(){
return this.code;
}

6
im-commom/src/main/java/com/bx/imcommon/model/IMGroupMessage.java

@ -3,6 +3,8 @@ package com.bx.imcommon.model;
import com.bx.imcommon.enums.IMTerminalType;
import lombok.Data;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@Data
@ -14,9 +16,9 @@ public class IMGroupMessage<T> {
private IMUserInfo sender;
/**
* 接收者id列表(群成员列表)
* 接收者id列表(群成员列表,为空则不会推送)
*/
private List<Long> recvIds;
private List<Long> recvIds = Collections.EMPTY_LIST;
/**

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

@ -6,6 +6,7 @@ import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.IGroupMessageService;
import com.bx.implatform.dto.GroupMessageDTO;
import com.bx.implatform.vo.PrivateMessageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@ -38,6 +39,7 @@ public class GroupMessageController {
return ResultUtils.success();
}
// todo 删除
@PostMapping("/pullUnreadMessage")
@ApiOperation(value = "拉取未读消息",notes="拉取未读消息")
public Result pullUnreadMessage(){
@ -45,6 +47,22 @@ public class GroupMessageController {
return ResultUtils.success();
}
@GetMapping("/loadMessage")
@ApiOperation(value = "拉取消息",notes="拉取消息,一次最多拉取100条")
public Result<List<GroupMessageVO>> loadMessage(@RequestParam Long minId){
return ResultUtils.success(groupMessageService.loadMessage(minId));
}
@PutMapping("/readed")
@ApiOperation(value = "消息已读",notes="将群聊中的消息状态置为已读")
public Result readedMessage(@RequestParam Long groupId){
groupMessageService.readedMessage(groupId);
return ResultUtils.success();
}
@GetMapping("/history")
@ApiOperation(value = "查询聊天记录",notes="查询聊天记录")
public Result<List<GroupMessageVO>> recallMessage(@NotNull(message = "群聊id不能为空") @RequestParam Long groupId,

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

@ -37,7 +37,7 @@ public class PrivateMessageController {
return ResultUtils.success();
}
// todo 删除
@PostMapping("/pullUnreadMessage")
@ApiOperation(value = "拉取未读消息",notes="拉取未读消息")
public Result pullUnreadMessage(){
@ -46,6 +46,19 @@ public class PrivateMessageController {
}
@GetMapping("/loadMessage")
@ApiOperation(value = "拉取消息",notes="拉取消息,一次最多拉取100条")
public Result<List<PrivateMessageVO>> loadMessage(@RequestParam Long minId){
return ResultUtils.success(privateMessageService.loadMessage(minId));
}
@PutMapping("/readed")
@ApiOperation(value = "消息已读",notes="将会话中接收的消息状态置为已读")
public Result readedMessage(@RequestParam Long friendId){
privateMessageService.readedMessage(friendId);
return ResultUtils.success();
}
@GetMapping("/history")
@ApiOperation(value = "查询聊天记录",notes="查询聊天记录")
public Result<List<PrivateMessageVO>> recallMessage(@NotNull(message = "好友id不能为空") @RequestParam Long friendId,

2
im-platform/src/main/java/com/bx/implatform/controller/UserController.java

@ -54,7 +54,7 @@ public class UserController {
@GetMapping("/find/{id}")
@ApiOperation(value = "查找用户",notes="根据id查找用户")
public Result findById(@NotEmpty @PathVariable("id") Long id){
public Result<UserVO> findById(@NotEmpty @PathVariable("id") Long id){
return ResultUtils.success(userService.findUserById(id));
}

12
im-platform/src/main/java/com/bx/implatform/enums/FileType.java

@ -1,5 +1,8 @@
package com.bx.implatform.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum FileType {
FILE(0,"文件"),
@ -13,15 +16,6 @@ public enum FileType {
private final String desc;
FileType(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public String description() {
return desc;
}
public Integer code(){
return this.code;

17
im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java

@ -1,24 +1,19 @@
package com.bx.implatform.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum MessageStatus {
UNREAD(0,"未读"),
ALREADY_READ(1,"已读"),
RECALL(2,"已撤回");
UNSEND(0,"未送达"),
SENDED(1,"送达"),
RECALL(2,"撤回"),
READED(3,"已读");
private final Integer code;
private final String desc;
MessageStatus(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public String description() {
return desc;
}
public Integer code(){
return this.code;

11
im-platform/src/main/java/com/bx/implatform/enums/MessageType.java

@ -1,6 +1,8 @@
package com.bx.implatform.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum MessageType {
TEXT(0,"文字"),
@ -9,6 +11,7 @@ public enum MessageType {
AUDIO(3,"音频"),
VIDEO(4,"视频"),
RECALL(10,"撤回"),
READED(11, "已读"),
RTC_CALL(101,"呼叫"),
RTC_ACCEPT(102,"接受"),
@ -22,14 +25,6 @@ public enum MessageType {
private final String desc;
MessageType(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public String description() {
return desc;
}
public Integer code(){
return this.code;

23
im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java

@ -1,5 +1,8 @@
package com.bx.implatform.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 响应码枚举
*
@ -7,6 +10,8 @@ package com.bx.implatform.enums;
* @date 2020/10/19
*
**/
@Getter
@AllArgsConstructor
public enum ResultCode {
SUCCESS(200,"成功"),
NO_LOGIN(400,"未登录"),
@ -21,24 +26,6 @@ public enum ResultCode {
private int code;
private String msg;
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}

4
im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java

@ -29,8 +29,8 @@ public class PrivateMessageListener implements MessageListener<PrivateMessageVO>
if(result.getCode().equals(IMSendCode.SUCCESS.code())){
UpdateWrapper<PrivateMessage> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(PrivateMessage::getId,messageInfo.getId())
.eq(PrivateMessage::getStatus, MessageStatus.UNREAD.code())
.set(PrivateMessage::getStatus, MessageStatus.ALREADY_READ.code());
.eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code())
.set(PrivateMessage::getStatus, MessageStatus.SENDED.code());
privateMessageService.update(updateWrapper);
log.info("消息已读,消息id:{},发送者:{},接收者:{},终端:{}",messageInfo.getId(),result.getSender().getId(),result.getReceiver().getId(),result.getReceiver().getTerminal());
}

4
im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java

@ -17,5 +17,9 @@ public interface IGroupMessageService extends IService<GroupMessage> {
void pullUnreadMessage();
List<GroupMessageVO> loadMessage(Long minId);
void readedMessage(Long groupId);
List<GroupMessageVO> findHistoryMessage(Long groupId, Long page, Long size);
}

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

@ -18,4 +18,7 @@ public interface IPrivateMessageService extends IService<PrivateMessage> {
void pullUnreadMessage();
List<PrivateMessageVO> loadMessage(Long minId);
void readedMessage(Long friendId);
}

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

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.contant.IMConstant;
import com.bx.implatform.util.DateTimeUtils;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.imcommon.model.IMGroupMessage;
import com.bx.imcommon.model.IMUserInfo;
@ -33,6 +34,7 @@ import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@ -43,7 +45,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
@Autowired
private IGroupMemberService groupMemberService;
@Autowired
private RedisTemplate<String,Object> redisTemplate;
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private IMClient imClient;
@ -57,16 +59,16 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
public Long sendMessage(GroupMessageDTO dto) {
UserSession session = SessionContext.getSession();
Group group = groupService.getById(dto.getGroupId());
if(group == null){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"群聊不存在");
if (group == null) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "群聊不存在");
}
if(group.getDeleted()){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"群聊已解散");
if (group.getDeleted()) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "群聊已解散");
}
// 判断是否在群里
List<Long> userIds = groupMemberService.findUserIdsByGroupId(group.getId());
if(!userIds.contains(session.getUserId())){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊里面,无法发送消息");
if (!userIds.contains(session.getUserId())) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不在群聊里面,无法发送消息");
}
// 保存消息
GroupMessage msg = BeanUtils.copyProperties(dto, GroupMessage.class);
@ -74,21 +76,19 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
msg.setSendTime(new Date());
this.save(msg);
// 不用发给自己
userIds = userIds.stream().filter(id->!session.getUserId().equals(id)).collect(Collectors.toList());
userIds = userIds.stream().filter(id -> !session.getUserId().equals(id)).collect(Collectors.toList());
// 群发
GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class);
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvIds(userIds);
sendMessage.setData(msgInfo);
imClient.sendGroupMessage(sendMessage);
log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}",session.getUserId(),dto.getGroupId(),dto.getContent());
log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}", session.getUserId(), dto.getGroupId(), dto.getContent());
return msg.getId();
}
/**
* 撤回消息
*
@ -98,19 +98,19 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
public void recallMessage(Long id) {
UserSession session = SessionContext.getSession();
GroupMessage msg = this.getById(id);
if(msg == null){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"消息不存在");
if (msg == null) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "消息不存在");
}
if(!msg.getSendId().equals(session.getUserId())){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"这条消息不是由您发送,无法撤回");
if (!msg.getSendId().equals(session.getUserId())) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "这条消息不是由您发送,无法撤回");
}
if(System.currentTimeMillis() - msg.getSendTime().getTime() > IMConstant.ALLOW_RECALL_SECOND * 1000){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"消息已发送超过5分钟,无法撤回");
if (System.currentTimeMillis() - msg.getSendTime().getTime() > IMConstant.ALLOW_RECALL_SECOND * 1000) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "消息已发送超过5分钟,无法撤回");
}
// 判断是否在群里
GroupMember member = groupMemberService.findByGroupAndUserId(msg.getGroupId(),session.getUserId());
if(member == null){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊里面,无法撤回消息");
GroupMember member = groupMemberService.findByGroupAndUserId(msg.getGroupId(), session.getUserId());
if (member == null) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不在群聊里面,无法撤回消息");
}
// 修改数据库
msg.setStatus(MessageStatus.RECALL.code());
@ -118,15 +118,15 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 群发
List<Long> userIds = groupMemberService.findUserIdsByGroupId(msg.getGroupId());
// 不用发给自己
userIds = userIds.stream().filter(uid->!session.getUserId().equals(uid)).collect(Collectors.toList());
userIds = userIds.stream().filter(uid -> !session.getUserId().equals(uid)).collect(Collectors.toList());
GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class);
msgInfo.setType(MessageType.RECALL.code());
String content = String.format("'%s'撤回了一条消息",member.getAliasName());
String content = String.format("'%s'撤回了一条消息", member.getAliasName());
msgInfo.setContent(content);
msgInfo.setSendTime(new Date());
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvIds(userIds);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
@ -139,40 +139,39 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
sendMessage.setRecvIds(Collections.emptyList());
sendMessage.setRecvTerminals(Collections.emptyList());
imClient.sendGroupMessage(sendMessage);
log.info("撤回群聊消息,发送id:{},群聊id:{},内容:{}",session.getUserId(),msg.getGroupId(),msg.getContent());
log.info("撤回群聊消息,发送id:{},群聊id:{},内容:{}", session.getUserId(), msg.getGroupId(), msg.getContent());
}
/**
* 异步拉取群聊消息通过websocket异步推送
*
*/
@Override
public void pullUnreadMessage() {
UserSession session = SessionContext.getSession();
List<GroupMember> members = groupMemberService.findByUserId(session.getUserId());
for(GroupMember member:members){
for (GroupMember member : members) {
// 获取群聊已读的最大消息id,只推送未读消息
String key = String.join(":",RedisKey.IM_GROUP_READED_POSITION,member.getGroupId().toString(),session.getUserId().toString());
Integer maxReadedId = (Integer)redisTemplate.opsForValue().get(key);
String key = String.join(":", RedisKey.IM_GROUP_READED_POSITION, member.getGroupId().toString(), session.getUserId().toString());
Integer maxReadedId = (Integer) redisTemplate.opsForValue().get(key);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.eq(GroupMessage::getGroupId,member.getGroupId())
.gt(GroupMessage::getSendTime,member.getCreatedTime())
wrapper.eq(GroupMessage::getGroupId, member.getGroupId())
.gt(GroupMessage::getSendTime, member.getCreatedTime())
.ne(GroupMessage::getSendId, session.getUserId())
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code());
if(maxReadedId!=null){
wrapper.gt(GroupMessage::getId,maxReadedId);
if (maxReadedId != null) {
wrapper.gt(GroupMessage::getId, maxReadedId);
}
wrapper.last("limit 100");
List<GroupMessage> messages = this.list(wrapper);
if(messages.isEmpty()){
if (messages.isEmpty()) {
continue;
}
// 推送
for (GroupMessage message:messages ){
for (GroupMessage message : messages) {
GroupMessageVO msgInfo = BeanUtils.copyProperties(message, GroupMessageVO.class);
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
// 只推给自己当前终端
sendMessage.setRecvIds(Collections.singletonList(session.getUserId()));
sendMessage.setRecvTerminals(Collections.singletonList(session.getTerminal()));
@ -180,41 +179,109 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
imClient.sendGroupMessage(sendMessage);
}
// 发送消息
log.info("拉取未读群聊消息,用户id:{},群聊id:{},数量:{}",session.getUserId(),member.getGroupId(),messages.size());
log.info("拉取未读群聊消息,用户id:{},群聊id:{},数量:{}", session.getUserId(), member.getGroupId(), messages.size());
}
}
/**
* 拉取消息只能拉取最近3个月的消息一次拉取100条
*
* @param minId 消息起始id
* @return 聊天消息列表
*/
@Override
public List<GroupMessageVO> loadMessage(Long minId) {
UserSession session = SessionContext.getSession();
List<GroupMember> members = groupMemberService.findByUserId(session.getUserId());
List<Long> ids = members.stream().map(GroupMember::getGroupId).collect(Collectors.toList());
// 只能拉取最近3个月的
Date minDate = DateTimeUtils.addMonths(new Date(), -1);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.gt(GroupMessage::getId, minId)
.gt(GroupMessage::getSendTime, minDate)
.in(GroupMessage::getGroupId, ids)
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code())
.last("limit 100");
List<GroupMessage> messages = this.list(wrapper);
// 转成vo
List<GroupMessageVO> vos = messages.stream().map(m -> BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList());
// 消息状态,数据库没有存群聊的消息状态,需要从redis取
List<String> keys = ids.stream()
.map(id -> String.join(":", RedisKey.IM_GROUP_READED_POSITION, id.toString(), session.getUserId().toString()))
.collect(Collectors.toList());
List<Object> sendPos = redisTemplate.opsForValue().multiGet(keys);
int idx = 0;
for (Long id : ids) {
Object o = sendPos.get(idx);
Integer sendMaxId = Objects.isNull(o) ? -1 : (Integer) o;
vos.stream().filter(vo -> vo.getGroupId().equals(id)).forEach(vo -> {
if (vo.getId() <= sendMaxId) {
// 已推送过
vo.setStatus(MessageStatus.SENDED.code());
} else {
// 未推送
vo.setStatus(MessageStatus.UNSEND.code());
}
});
idx++;
}
return vos;
}
/**
* 消息已读,同步其他终端清空未读数量
*
* @param groupId 群聊
*/
@Override
public void readedMessage(Long groupId) {
UserSession session = SessionContext.getSession();
// 推送消息给自己的其他终端
GroupMessageVO msgInfo = new GroupMessageVO();
msgInfo.setType(MessageType.READED.code());
msgInfo.setSendTime(new Date());
msgInfo.setSendId(session.getUserId());
msgInfo.setGroupId(groupId);
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setSendToSelf(true);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
imClient.sendGroupMessage(sendMessage);
}
/**
* 拉取历史聊天记录
*
* @param groupId 群聊id
* @param page 页码
* @param size 页码大小
* @param page 页码
* @param size 页码大小
* @return 聊天记录列表
*/
@Override
public List<GroupMessageVO> findHistoryMessage(Long groupId, Long page, Long size) {
page = page > 0 ? page:1;
size = size > 0 ? size:10;
page = page > 0 ? page : 1;
size = size > 0 ? size : 10;
Long userId = SessionContext.getSession().getUserId();
long stIdx = (page-1)* size;
long stIdx = (page - 1) * size;
// 群聊成员信息
GroupMember member = groupMemberService.findByGroupAndUserId(groupId,userId);
if(member == null || member.getQuit()){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊中");
GroupMember member = groupMemberService.findByGroupAndUserId(groupId, userId);
if (member == null || member.getQuit()) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不在群聊中");
}
// 查询聊天记录,只查询加入群聊时间之后的消息
QueryWrapper<GroupMessage> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(GroupMessage::getGroupId,groupId)
.gt(GroupMessage::getSendTime,member.getCreatedTime())
wrapper.lambda().eq(GroupMessage::getGroupId, groupId)
.gt(GroupMessage::getSendTime, member.getCreatedTime())
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code())
.orderByDesc(GroupMessage::getId)
.last("limit "+stIdx + ","+size);
.last("limit " + stIdx + "," + size);
List<GroupMessage> messages = this.list(wrapper);
List<GroupMessageVO> messageInfos = messages.stream().map(m->BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList());
log.info("拉取群聊记录,用户id:{},群聊id:{},数量:{}",userId,groupId,messageInfos.size());
List<GroupMessageVO> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList());
log.info("拉取群聊记录,用户id:{},群聊id:{},数量:{}", userId, groupId, messageInfos.size());
return messageInfos;
}

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

@ -2,6 +2,7 @@ package com.bx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
@ -9,6 +10,7 @@ import com.bx.imcommon.contant.IMConstant;
import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.implatform.entity.Friend;
import com.bx.implatform.util.DateTimeUtils;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.implatform.entity.PrivateMessage;
import com.bx.implatform.enums.MessageStatus;
@ -25,6 +27,7 @@ import com.bx.implatform.dto.PrivateMessageDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.Date;
@ -40,6 +43,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
@Autowired
private IMClient imClient;
/**
* 发送私聊消息
*
@ -56,13 +60,13 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
// 保存消息
PrivateMessage msg = BeanUtils.copyProperties(dto, PrivateMessage.class);
msg.setSendId(session.getUserId());
msg.setStatus(MessageStatus.UNREAD.code());
msg.setStatus(MessageStatus.UNSEND.code());
msg.setSendTime(new Date());
this.save(msg);
// 推送消息
PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendToSelf(true);
sendMessage.setData(msgInfo);
@ -99,7 +103,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
msgInfo.setContent("对方撤回了一条消息");
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendToSelf(false);
sendMessage.setData(msgInfo);
@ -147,7 +151,6 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
/**
* 异步拉取私聊消息通过websocket异步推送
*
*/
@Override
public void pullUnreadMessage() {
@ -158,22 +161,22 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
}
List<Friend> friends = friendService.findFriendByUserId(session.getUserId());
if(friends.isEmpty()){
if (friends.isEmpty()) {
return;
}
List<Long> friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList());
// 获取当前用户所有未读消息
LambdaQueryWrapper<PrivateMessage> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(PrivateMessage::getRecvId, session.getUserId())
.eq(PrivateMessage::getStatus, MessageStatus.UNREAD)
.in(PrivateMessage::getSendId,friendIds);
.eq(PrivateMessage::getStatus, MessageStatus.UNSEND)
.in(PrivateMessage::getSendId, friendIds);
List<PrivateMessage> messages = this.list(queryWrapper);
// 上传至redis,等待推送
for(PrivateMessage message:messages){
for (PrivateMessage message : messages) {
PrivateMessageVO msgInfo = BeanUtils.copyProperties(message, PrivateMessageVO.class);
// 推送消息
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.setRecvTerminals(Collections.singletonList(session.getTerminal()));
sendMessage.setSendToSelf(false);
@ -183,4 +186,82 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
log.info("拉取未读私聊消息,用户id:{},数量:{}", session.getUserId(), messages.size());
}
/**
* 拉取消息只能拉取最近3个月的消息一次拉取100条
*
* @param minId 消息起始id
* @return 聊天消息列表
*/
@Override
public List<PrivateMessageVO> loadMessage(Long minId) {
UserSession session = SessionContext.getSession();
List<Friend> friends = friendService.findFriendByUserId(session.getUserId());
if (friends.isEmpty()) {
return Collections.EMPTY_LIST;
}
List<Long> friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList());
// 获取当前用户的消息
LambdaQueryWrapper<PrivateMessage> queryWrapper = Wrappers.lambdaQuery();
// 只能拉取最近6个月的
Date minDate = DateTimeUtils.addMonths(new Date(), -1);
queryWrapper.gt(PrivateMessage::getId, minId)
.ge(PrivateMessage::getSendTime, minDate)
.ne(PrivateMessage::getStatus, MessageStatus.RECALL.code())
.and(wrap -> wrap.and(
wp -> wp.eq(PrivateMessage::getSendId, session.getUserId())
.in(PrivateMessage::getRecvId, friendIds))
.or(wp -> wp.eq(PrivateMessage::getRecvId, session.getUserId())
.in(PrivateMessage::getSendId, friendIds)))
.last("limit 100");
List<PrivateMessage> messages = this.list(queryWrapper);
// 更新发送状态
List<Long> ids = messages.stream()
.filter(m -> !m.getSendId().equals(session.getUserId()) && m.getStatus().equals(MessageStatus.UNSEND.code()))
.map(PrivateMessage::getId)
.collect(Collectors.toList());
if (!ids.isEmpty()) {
LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.in(PrivateMessage::getId, ids)
.set(PrivateMessage::getStatus, MessageStatus.SENDED.code());
this.update(updateWrapper);
}
log.info("拉取消息,用户id:{},数量:{}", session.getUserId(), messages.size());
return messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class)).collect(Collectors.toList());
}
/**
* 消息已读,将整个会话的消息都置为已读状态
*
* @param friendId 好友id
*/
@Transactional
@Override
public void readedMessage(Long friendId) {
UserSession session = SessionContext.getSession();
// 推送消息
PrivateMessageVO msgInfo = new PrivateMessageVO();
msgInfo.setType(MessageType.READED.code());
msgInfo.setSendTime(new Date());
msgInfo.setSendId(session.getUserId());
msgInfo.setRecvId(friendId);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(friendId);
sendMessage.setSendToSelf(true);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
imClient.sendPrivateMessage(sendMessage);
// 修改消息状态为已读
LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(PrivateMessage::getSendId,friendId)
.eq(PrivateMessage::getRecvId,session.getUserId())
.eq(PrivateMessage::getStatus,MessageStatus.SENDED.code())
.set(PrivateMessage::getStatus,MessageStatus.READED.code());
this.update(updateWrapper);
log.info("消息已读,接收方id:{},发送方id:{}", session.getUserId(),friendId);
}
}

28
im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java

@ -2,6 +2,7 @@ package com.bx.implatform.vo;
import com.bx.imcommon.serializer.DateToLongSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ -9,34 +10,25 @@ import java.util.Date;
@Data
public class GroupMessageVO {
/*
* 消息id
*/
@ApiModelProperty(value = "消息id")
private Long id;
/*
* 群聊id
*/
@ApiModelProperty(value = "群聊id")
private Long groupId;
/*
* 发送者id
*/
@ApiModelProperty(value = " 发送者id")
private Long sendId;
/*
* 消息内容
*/
@ApiModelProperty(value = "消息内容")
private String content;
/*
* 消息内容类型 具体枚举值由应用层定义
*/
@ApiModelProperty(value = "消息内容类型 具体枚举值由应用层定义")
private Integer type;
/**
* 发送时间
*/
@ApiModelProperty(value = " 状态")
private Integer status;
@ApiModelProperty(value = "发送时间")
@JsonSerialize(using = DateToLongSerializer.class)
private Date sendTime;
}

30
im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java

@ -2,41 +2,35 @@ package com.bx.implatform.vo;
import com.bx.imcommon.serializer.DateToLongSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("私聊消息VO")
public class PrivateMessageVO {
/*
* 消息id
*/
@ApiModelProperty(value = " 消息id")
private long id;
/*
* 发送者id
*/
@ApiModelProperty(value = " 发送者id")
private Long sendId;
/*
* 接收者id
*/
@ApiModelProperty(value = " 接收者id")
private Long recvId;
/*
* 发送内容
*/
@ApiModelProperty(value = " 发送内容")
private String content;
/*
* 消息内容类型 IMCmdType
*/
@ApiModelProperty(value = "消息内容类型 IMCmdType")
private Integer type;
/**
* 发送时间
*/
@ApiModelProperty(value = " 状态")
private Integer status;
@ApiModelProperty(value = " 发送时间")
@JsonSerialize(using = DateToLongSerializer.class)
private Date sendTime;
}

3
im-ui/src/api/emotion.js

@ -15,6 +15,9 @@ let transform = (content) => {
let textToImg = (emoText) => {
let word = emoText.replace(/\#|\;/gi, '');
let idx = emoTextList.indexOf(word);
if(idx==-1){
return "";
}
let url = require(`@/assets/emoji/${idx}.gif`);
return `<img src="${url}" style="width:40px;height:40px;vertical-align:bottom;"/>`
}

12
im-ui/src/api/enums.js

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

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

@ -48,10 +48,10 @@
<div class="send-content-area">
<textarea v-show="!sendImageUrl" v-model="sendText" ref="sendBox" class="send-text-area"
:disabled="lockMessage" @keydown.enter="sendTextMessage()" @paste="handlePaste"
placeholder="温馨提示:可以粘贴截图了哦~"></textarea>
placeholder="温馨提示:可以粘贴截图到这里了哦~"></textarea>
<div v-show="sendImageUrl" class="send-image-area">
<div class="send-image-box">
<div v-show="sendImageUrl" class="send-image-area">
<div class="send-image-box">
<img class="send-image" :src="sendImageUrl" />
<span class="send-image-close el-icon-close" title="删除"
@click="removeSendImage()"></span>
@ -120,7 +120,6 @@
},
methods: {
handlePaste(e) {
console.log(e);
let txt = event.clipboardData.getData('Text')
if (typeof(txt) == 'string') {
this.sendText += txt
@ -172,7 +171,8 @@
sendTime: new Date().getTime(),
selfSend: true,
type: 1,
loadStatus: "loading"
loadStatus: "loading",
status: this.$enums.MESSAGE_STATUS.UNSEND
}
// id
this.fillTargetId(msgInfo, this.chat.targetId);
@ -221,7 +221,8 @@
sendTime: new Date().getTime(),
selfSend: true,
type: 2,
loadStatus: "loading"
loadStatus: "loading",
status: this.$enums.MESSAGE_STATUS.UNSEND
}
// id
this.fillTargetId(msgInfo, this.chat.targetId);
@ -256,7 +257,6 @@
this.showVoice = false;
},
showVideoBox() {
console.log(this.friend)
this.$store.commit("showChatPrivateVideoBox", {
friend: this.friend,
master: true
@ -280,11 +280,11 @@
method: 'post',
data: msgInfo
}).then((id) => {
this.$message.success("发送成功");
msgInfo.id = id;
msgInfo.sendTime = new Date().getTime();
msgInfo.sendId = this.$store.state.userStore.userInfo.id;
msgInfo.selfSend = true;
msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND;
this.$store.commit("insertMessage", msgInfo);
//
this.$refs.sendBox.focus();
@ -323,7 +323,7 @@
this.handleImageSuccess(res, file);
})
this.sendImageFile = null;
this.sendImageUrl= "";
this.sendImageUrl = "";
this.$nextTick(() => this.$refs.sendBox.focus());
this.scrollToBottom();
},
@ -344,12 +344,12 @@
method: 'post',
data: msgInfo
}).then((id) => {
this.$message.success("发送成功");
this.sendText = "";
msgInfo.id = id;
msgInfo.sendTime = new Date().getTime();
msgInfo.sendId = this.$store.state.userStore.userInfo.id;
msgInfo.selfSend = true;
msgInfo.status = this.$enums.MESSAGE_STATUS.UNSEND;
this.$store.commit("insertMessage", msgInfo);
}).finally(() => {
//
@ -390,10 +390,24 @@
msgInfo = JSON.parse(JSON.stringify(msgInfo));
msgInfo.type = 10;
msgInfo.content = '你撤回了一条消息';
msgInfo.status = this.$enums.MESSAGE_STATUS.RECALL;
this.$store.commit("insertMessage", msgInfo);
})
});
},
readedMessage() {
if(this.chat.type == "GROUP"){
var url = `/message/group/readed?groupId=${this.chat.targetId}`
}else{
url = `/message/private/readed?friendId=${this.chat.targetId}`
}
this.$http({
url: url,
method: 'put'
}).then(() => {
this.$store.commit("resetUnreadCount",this.chat)
this.scrollToBottom();
})
},
loadGroup(groupId) {
this.$http({
@ -420,7 +434,6 @@
method: 'get'
}).then((friend) => {
this.friend = friend;
console.log(this.friend)
this.$store.commit("updateChatFromFriend", friend);
this.$store.commit("updateFriend", friend);
})
@ -469,12 +482,16 @@
},
messageAction() {
return `/message/${this.chat.type.toLowerCase()}/send`;
},
unreadCount() {
return this.chat.unreadCount;
}
},
watch: {
chat: {
handler(newChat, oldChat) {
if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || newChat.targetId != oldChat.targetId)) {
if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || newChat.targetId != oldChat
.targetId)) {
if (this.chat.type == "GROUP") {
this.loadGroup(this.chat.targetId);
} else {
@ -489,6 +506,14 @@
}
},
immediate: true
},
unreadCount: {
handler(newCount, oldCount) {
if(newCount > 0){
//
this.readedMessage()
}
}
}
}
}
@ -617,7 +642,6 @@
}
.send-btn-area {
padding: 10px;
position: absolute;
bottom: 0;

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

@ -1,11 +1,13 @@
<template>
<div class="chat-msg-item">
<div class="chat-msg-tip" v-show="msgInfo.type==$enums.MESSAGE_TYPE.RECALL">{{msgInfo.content}}</div>
<div class="chat-msg-tip" v-show="msgInfo.type==$enums.MESSAGE_TYPE.TIP_TIME">{{$date.toTimeText(msgInfo.sendTime)}}</div>
<div class="chat-msg-tip" v-show="msgInfo.type==$enums.MESSAGE_TYPE.TIP_TIME">
{{$date.toTimeText(msgInfo.sendTime)}}
</div>
<div class="chat-msg-normal" v-show="msgInfo.type>=0 && msgInfo.type<10" :class="{'chat-msg-mine':mine}">
<div class="head-image">
<head-image :name="showName" :size="40" :url="headImage" :id="msgInfo.sendId"></head-image>
<head-image :name="showName" :size="40" :url="headImage" :id="msgInfo.sendId"></head-image>
</div>
<div class="chat-msg-content">
<div v-show="mode==1 && msgInfo.groupId && !msgInfo.selfSend" class="chat-msg-top">
@ -21,7 +23,7 @@
<div class="chat-msg-image" v-if="msgInfo.type==$enums.MESSAGE_TYPE.IMAGE">
<div class="img-load-box" v-loading="loading" element-loading-text="上传中.."
element-loading-background="rgba(0, 0, 0, 0.4)">
<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl"
<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl"
@click="showFullImageBox()" />
</div>
<span title="发送失败" v-show="loadFail" @click="handleSendFail"
@ -45,6 +47,10 @@
@click="handlePlayVoice()">
<audio controls :src="JSON.parse(msgInfo.content).url"></audio>
</div>
<span class="chat-readed" v-show="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status==$enums.MESSAGE_STATUS.READED">已读</span>
<span class="chat-unread" v-show="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status!=$enums.MESSAGE_STATUS.READED">未读</span>
</div>
</div>
@ -65,9 +71,9 @@
RightMenu
},
props: {
mode:{
mode: {
type: Number,
default :1
default: 1
},
mine: {
type: Boolean,
@ -168,9 +174,6 @@
}
return items;
}
},
mounted() {
console.log(this.msgInfo);
}
}
</script>
@ -230,7 +233,7 @@
line-height: 30px;
margin-top: 3px;
padding: 7px;
background-color: rgb(235,235,245);
background-color: rgb(235, 235, 245);
border-radius: 10px;
color: black;
display: block;
@ -239,6 +242,7 @@
white-space: pre-wrap;
word-break: break-all;
box-shadow: 2px 2px 2px #c0c0f0;
&:after {
content: "";
position: absolute;
@ -247,7 +251,7 @@
width: 0;
height: 0;
border-style: solid dashed dashed;
border-color: rgb(235,235,245) transparent transparent;
border-color: rgb(235, 235, 245) transparent transparent;
overflow: hidden;
border-width: 10px;
}
@ -332,6 +336,18 @@
padding: 5px 0;
}
}
.chat-unread {
font-size: 10px;
color: #f23c0f;
font-weight: 600;
}
.chat-readed {
font-size: 10px;
color: #aaa;
font-weight: 600;
}
}
}
@ -368,6 +384,7 @@
color: #fff;
vertical-align: top;
box-shadow: 2px 2px 1px #ccc;
&:after {
left: auto;
right: -10px;

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

@ -1,25 +1,42 @@
import {MESSAGE_TYPE} from "../api/enums.js"
import {
MESSAGE_TYPE,
MESSAGE_STATUS
} from "../api/enums.js"
import userStore from './userStore';
export default {
state: {
activeIndex: -1,
privateMsgMaxId: 0,
groupMsgMaxId: 0,
loadingPrivateMsg: false,
loadingGroupMsg: false,
chats: []
},
mutations: {
initChatStore(state) {
// 防止图片一直处在加载中状态
state.chats.forEach((chat)=>{
chat.messages.forEach((msg)=>{
if(msg.loadStatus == "loading"){
initChats(state, chats) {
state.chats = chats||[];
state.chats.forEach((chat) => {
chat.messages.forEach((msg) => {
// 防止图片一直处在加载中状态
if (msg.loadStatus == "loading") {
msg.loadStatus = "fail"
}
// 记录最大私聊消息id
if(chat.type == "PRIVATE" && msg.id && msg.id>state.privateMsgMaxId){
state.privateMsgMaxId = msg.id;
}
// 记录最大群聊消息id
if(chat.type == "GROUP" && msg.id && msg.id>state.groupMsgMaxId){
state.groupMsgMaxId = msg.id;
}
})
})
},
openChat(state, chatInfo) {
let chat = null;
let activeChat = state.activeIndex>=0?state.chats[state.activeIndex]:null;
let activeChat = state.activeIndex >= 0 ? state.chats[state.activeIndex] : null;
for (let i in state.chats) {
if (state.chats[i].type == chatInfo.type &&
state.chats[i].targetId === chatInfo.targetId) {
@ -45,10 +62,10 @@ export default {
state.chats.unshift(chat);
}
// 选中会话保持不变
if(activeChat){
state.chats.forEach((chat,idx)=>{
if(activeChat.type == chat.type
&& activeChat.targetId == chat.targetId){
if (activeChat) {
state.chats.forEach((chat, idx) => {
if (activeChat.type == chat.type &&
activeChat.targetId == chat.targetId) {
state.activeIndex = idx;
}
})
@ -56,20 +73,42 @@ export default {
},
activeChat(state, idx) {
state.activeIndex = idx;
state.chats[idx].unreadCount = 0;
},
resetUnreadCount(state, chatInfo) {
for (let idx in state.chats) {
if (state.chats[idx].type == chatInfo.type
&& state.chats[idx].targetId == chatInfo.targetId) {
state.chats[idx].unreadCount=0;
}
}
this.commit("saveToStorage");
},
readedMessage(state, friendId) {
for (let idx in state.chats) {
if (state.chats[idx].type == 'PRIVATE'
&& state.chats[idx].targetId == friendId) {
state.chats[idx].messages.forEach((m) => {
if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) {
m.status = MESSAGE_STATUS.READED
}
})
}
}
this.commit("saveToStorage");
},
removeChat(state, idx) {
state.chats.splice(idx, 1);
if (state.activeIndex >= state.chats.length) {
state.activeIndex = state.chats.length - 1;
}
this.commit("saveToStorage");
},
moveTop(state,idx){
moveTop(state, idx) {
let chat = state.chats[idx];
// 放置头部
state.chats.splice(idx, 1);
state.chats.unshift(chat);
this.commit("saveToStorage");
},
removeGroupChat(state, groupId) {
for (let idx in state.chats) {
@ -79,15 +118,16 @@ export default {
}
}
},
removePrivateChat(state, userId) {
removePrivateChat(state, friendId) {
for (let idx in state.chats) {
if (state.chats[idx].type == 'PRIVATE' &&
state.chats[idx].targetId == userId) {
state.chats[idx].targetId == friendId) {
this.commit("removeChat", idx);
}
}
},
insertMessage(state, msgInfo) {
// 获取对方id或群id
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId;
@ -100,36 +140,45 @@ export default {
}
}
// 插入新的数据
if(msgInfo.type == MESSAGE_TYPE.IMAGE ){
chat.lastContent = "[图片]";
}else if(msgInfo.type == MESSAGE_TYPE.FILE){
if (msgInfo.type == MESSAGE_TYPE.IMAGE) {
chat.lastContent = "[图片]";
} else if (msgInfo.type == MESSAGE_TYPE.FILE) {
chat.lastContent = "[文件]";
}else if(msgInfo.type == MESSAGE_TYPE.AUDIO){
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = "[语音]";
}else{
chat.lastContent = msgInfo.content;
} else {
chat.lastContent = msgInfo.content;
}
chat.lastSendTime = msgInfo.sendTime;
// 如果不是当前会话,未读加1
chat.unreadCount++;
if(msgInfo.selfSend){
chat.unreadCount=0;
// 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED) {
chat.unreadCount++;
}
// 记录消息的最大id
if (msgInfo.id && type=="PRIVATE" && msgInfo.id > state.privateMsgMaxId) {
state.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type=="GROUP" && msgInfo.id > state.groupMsgMaxId) {
state.groupMsgMaxId = msgInfo.id;
}
// 如果是已存在消息,则覆盖旧的消息数据
for (let idx in chat.messages) {
if(msgInfo.id && chat.messages[idx].id == msgInfo.id){
if (msgInfo.id && chat.messages[idx].id == msgInfo.id) {
Object.assign(chat.messages[idx], msgInfo);
this.commit("saveToStorage");
return;
}
// 正在发送中的消息可能没有id,通过发送时间判断
if(msgInfo.selfSend && chat.messages[idx].selfSend
&& chat.messages[idx].sendTime == msgInfo.sendTime){
if (msgInfo.selfSend && chat.messages[idx].selfSend &&
chat.messages[idx].sendTime == msgInfo.sendTime) {
Object.assign(chat.messages[idx], msgInfo);
this.commit("saveToStorage");
return;
}
}
// 间隔大于10分钟插入时间显示
if(!chat.lastTimeTip || (chat.lastTimeTip < msgInfo.sendTime - 600*1000)){
if (!chat.lastTimeTip || (chat.lastTimeTip < msgInfo.sendTime - 600 * 1000)) {
chat.messages.push({
sendTime: msgInfo.sendTime,
type: MESSAGE_TYPE.TIP_TIME,
@ -138,9 +187,9 @@ export default {
}
// 新的消息
chat.messages.push(msgInfo);
this.commit("saveToStorage");
},
deleteMessage(state, msgInfo){
deleteMessage(state, msgInfo) {
// 获取对方id或群id
let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId;
@ -155,42 +204,68 @@ export default {
for (let idx in chat.messages) {
// 已经发送成功的,根据id删除
if(chat.messages[idx].id && chat.messages[idx].id == msgInfo.id){
if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
chat.messages.splice(idx, 1);
break;
}
// 正在发送中的消息可能没有id,根据发送时间删除
if(msgInfo.selfSend && chat.messages[idx].selfSend
&&chat.messages[idx].sendTime == msgInfo.sendTime){
if (msgInfo.selfSend && chat.messages[idx].selfSend &&
chat.messages[idx].sendTime == msgInfo.sendTime) {
chat.messages.splice(idx, 1);
break;
}
}
this.commit("saveToStorage");
},
updateChatFromFriend(state, friend) {
for (let i in state.chats) {
let chat = state.chats[i];
if (chat.type=='PRIVATE' && chat.targetId == friend.id) {
if (chat.type == 'PRIVATE' && chat.targetId == friend.id) {
chat.headImage = friend.headImageThumb;
chat.showName = friend.nickName;
break;
}
}
this.commit("saveToStorage");
},
updateChatFromGroup(state, group) {
for (let i in state.chats) {
let chat = state.chats[i];
if (chat.type=='GROUP' && chat.targetId == group.id) {
if (chat.type == 'GROUP' && chat.targetId == group.id) {
chat.headImage = group.headImageThumb;
chat.showName = group.remark;
break;
}
}
this.commit("saveToStorage");
},
loadingPrivateMsg(state,loadding){
state.loadingPrivateMsg = loadding;
},
resetChatStore(state) {
loadingGroupMsg(state,loadding){
state.loadingGroupMsg = loadding;
},
saveToStorage(state) {
let userId = userStore.state.userInfo.id;
let key = "chats-" + userId;
localStorage.setItem(key, JSON.stringify(state.chats));
},
clear(state) {
state.activeIndex = -1;
state.chats = [];
}
},
actions: {
loadChat(context) {
return new Promise((resolve, reject) => {
let userId = userStore.state.userInfo.id;
let key = "chats-" + userId;
let item = localStorage.getItem(key)
let chats = JSON.parse(localStorage.getItem(key));
context.commit("initChats", chats);
resolve();
})
}
}
}

39
im-ui/src/store/friendStore.js

@ -1,4 +1,4 @@
import httpRequest from '../api/httpRequest.js'
import http from '../api/httpRequest.js'
import {TERMINAL_TYPE} from "../api/enums.js"
export default {
@ -9,16 +9,6 @@ export default {
timer: null
},
mutations: {
initFriendStore(state) {
httpRequest({
url: '/friend/list',
method: 'get'
}).then((friends) => {
this.commit("setFriends",friends);
this.commit("refreshOnlineStatus");
})
},
setFriends(state, friends) {
state.friends = friends;
},
@ -50,7 +40,7 @@ export default {
return;
}
state.friends.forEach((f)=>{userIds.push(f.id)});
httpRequest({
http({
url: '/user/terminal/online',
method: 'get',
params: {userIds: userIds.join(',')}
@ -90,7 +80,6 @@ export default {
return 0;
});
console.log(state.friends)
// 重新排序后,activeIndex指向的好友可能会变化,需要重新指定
if(state.activeIndex >=0){
state.friends.forEach((f,i)=>{
@ -99,7 +88,29 @@ export default {
}
})
}
},
clear(state) {
clearTimeout(state.timer);
state.friends = [];
state.timer = null;
state.activeIndex = -1;
}
},
actions: {
loadFriend(context) {
return new Promise((resolve, reject) => {
http({
url: '/friend/list',
method: 'GET'
}).then((friends) => {
context.commit("setFriends", friends);
context.commit("refreshOnlineStatus");
console.log("loadFriend")
resolve()
}).catch((res) => {
reject();
})
});
}
}
}

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

@ -1,4 +1,4 @@
import httpRequest from '../api/httpRequest.js'
import http from '../api/httpRequest.js'
export default {
@ -7,41 +7,53 @@ export default {
activeIndex: -1,
},
mutations: {
initGroupStore(state) {
httpRequest({
url: '/group/list',
method: 'get'
}).then((groups) => {
this.commit("setGroups",groups);
})
},
setGroups(state,groups){
setGroups(state, groups) {
state.groups = groups;
},
activeGroup(state,index){
activeGroup(state, index) {
state.activeIndex = index;
},
addGroup(state,group){
addGroup(state, group) {
state.groups.unshift(group);
},
removeGroup(state,groupId){
state.groups.forEach((g,index)=>{
if(g.id==groupId){
removeGroup(state, groupId) {
state.groups.forEach((g, index) => {
if (g.id == groupId) {
state.groups.splice(index, 1);
if(state.activeIndex >= state.groups.length){
state.activeIndex = state.groups.length-1;
if (state.activeIndex >= state.groups.length) {
state.activeIndex = state.groups.length - 1;
}
}
})
},
updateGroup(state,group){
state.groups.forEach((g,idx)=>{
if(g.id==group.id){
updateGroup(state, group) {
state.groups.forEach((g, idx) => {
if (g.id == group.id) {
// 拷贝属性
Object.assign(state.groups[idx], group);
}
})
},
clear(state){
state.groups = [];
state.activeGroup = -1;
}
},
actions: {
loadGroup(context) {
return new Promise((resolve, reject) => {
http({
url: '/group/list',
method: 'GET'
}).then((groups) => {
context.commit("setGroups", groups);
console.log("loadGroup")
resolve();
}).catch((res) => {
reject(res);
})
});
}
}
}

27
im-ui/src/store/index.js

@ -5,27 +5,28 @@ import friendStore from './friendStore.js';
import userStore from './userStore.js';
import groupStore from './groupStore.js';
import uiStore from './uiStore.js';
import VuexPersistence from 'vuex-persist'
const vuexLocal = new VuexPersistence({
storage: window.localStorage,
modules: ["userStore","chatStore"]
})
Vue.use(Vuex)
export default new Vuex.Store({
modules: {chatStore,friendStore,userStore,groupStore,uiStore},
state: {},
plugins: [vuexLocal.plugin],
mutations: {
initStore(state){
this.commit("initFriendStore");
this.commit("initGroupStore");
this.commit("initChatStore");
},
actions: {
load(context) {
console.log("load")
return this.dispatch("loadUser").then(() => {
const promises = [];
promises.push(this.dispatch("loadFriend"));
promises.push(this.dispatch("loadGroup"));
promises.push(this.dispatch("loadChat"));
return Promise.all(promises);
})
},
unload(context){
context.commit("clear");
}
},
strict: process.env.NODE_ENV !== 'production'
})

1
im-ui/src/store/uiStore.js

@ -20,7 +20,6 @@ export default {
},
videoAcceptor:{ // 视频呼叫选择
show: false,
friend:{}
}

28
im-ui/src/store/userStore.js

@ -1,4 +1,5 @@
import {USER_STATE} from "../api/enums.js"
import http from '../api/httpRequest.js'
export default {
@ -11,16 +12,29 @@ export default {
mutations: {
setUserInfo(state, userInfo) {
// 切换用户后,清理缓存
if(userInfo.id != state.userInfo.id){
console.log("用户切换")
this.commit("resetChatStore");
}
state.userInfo = userInfo;
state.userInfo = userInfo
},
setUserState(state, userState) {
state.state = userState;
},
clear(state){
state.userInfo = {};
state.state = USER_STATE.FREE;
}
},
actions:{
loadUser(context){
return new Promise((resolve, reject) => {
http({
url: '/user/self',
method: 'GET'
}).then((userInfo) => {
context.commit("setUserInfo",userInfo);
resolve();
}).catch((res)=>{
reject(res);
});
})
}
}
}

24
im-ui/src/view/Chat.vue

@ -6,14 +6,14 @@
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
<el-scrollbar class="l-chat-list" >
<div class="l-chat-loadding" v-if="loading" v-loading="true" element-loading-text="消息接收中..."
element-loading-spinner="el-icon-loading" element-loading-background="#eee">
</div>
<el-scrollbar class="l-chat-list">
<div v-for="(chat,index) in chatStore.chats" :key="index">
<chat-item v-show="chat.showName.startsWith(searchText)"
:chat="chat" :index="index"
@click.native="handleActiveItem(index)"
@delete="handleDelItem(index)"
@top="handleTop(index)"
:active="index === chatStore.activeIndex"></chat-item>
<chat-item v-show="chat.showName.startsWith(searchText)" :chat="chat" :index="index"
@click.native="handleActiveItem(index)" @delete="handleDelItem(index)" @top="handleTop(index)"
:active="index === chatStore.activeIndex"></chat-item>
</div>
</el-scrollbar>
</el-aside>
@ -70,6 +70,9 @@
messages: []
}
return emptyChat;
},
loading(){
return this.chatStore.loadingGroupMsg || this.chatStore.loadingPrivateMsg
}
}
}
@ -90,7 +93,12 @@
line-height: 50px;
}
.l-friend-ist{
.l-chat-loadding{
height: 50px;
background-color: #eee;
}
.l-friend-ist {
flex: 1;
}
}

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

@ -3,8 +3,8 @@
<el-aside width="80px" class="navi-bar">
<div class="user-head-image">
<head-image :name="$store.state.userStore.userInfo.nickName"
:url="$store.state.userStore.userInfo.headImageThumb"
:size="60" @click.native="showSettingDialog=true">
:url="$store.state.userStore.userInfo.headImageThumb" :size="60"
@click.native="showSettingDialog=true">
</head-image>
</div>
@ -39,13 +39,16 @@
<router-view></router-view>
</el-main>
<setting :visible="showSettingDialog" @close="closeSetting()"></setting>
<user-info v-show="uiStore.userInfo.show" :pos="uiStore.userInfo.pos" :user="uiStore.userInfo.user" @close="$store.commit('closeUserInfoBox')"></user-info>
<full-image :visible="uiStore.fullImage.show" :url="uiStore.fullImage.url" @close="$store.commit('closeFullImageBox')"></full-image>
<chat-private-video ref="privateVideo" :visible="uiStore.chatPrivateVideo.show" :friend="uiStore.chatPrivateVideo.friend"
:master="uiStore.chatPrivateVideo.master" :offer="uiStore.chatPrivateVideo.offer" @close="$store.commit('closeChatPrivateVideoBox')">
<user-info v-show="uiStore.userInfo.show" :pos="uiStore.userInfo.pos" :user="uiStore.userInfo.user"
@close="$store.commit('closeUserInfoBox')"></user-info>
<full-image :visible="uiStore.fullImage.show" :url="uiStore.fullImage.url"
@close="$store.commit('closeFullImageBox')"></full-image>
<chat-private-video ref="privateVideo" :visible="uiStore.chatPrivateVideo.show"
:friend="uiStore.chatPrivateVideo.friend" :master="uiStore.chatPrivateVideo.master"
:offer="uiStore.chatPrivateVideo.offer" @close="$store.commit('closeChatPrivateVideoBox')">
</chat-private-video>
<chat-video-acceptor ref="videoAcceptor" v-show="uiStore.videoAcceptor.show" :friend="uiStore.videoAcceptor.friend"
@close="$store.commit('closeVideoAcceptorBox')">
<chat-video-acceptor ref="videoAcceptor" v-show="uiStore.videoAcceptor.show"
:friend="uiStore.videoAcceptor.friend" @close="$store.commit('closeVideoAcceptorBox')">
</chat-video-acceptor>
</el-container>
</template>
@ -75,80 +78,107 @@
}
},
methods: {
init(userInfo) {
this.$store.commit("setUserInfo", userInfo);
this.$store.commit("setUserState", this.$enums.USER_STATE.FREE);
this.$store.commit("initStore");
this.$wsApi.init(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken"));
this.$wsApi.connect();
this.$wsApi.onOpen(() => {
this.pullUnreadMessage();
});
this.$wsApi.onMessage((cmd, msgInfo) => {
if (cmd == 2) {
// 线
this.$message.error("您已在其他地方登陆,将被强制下线");
setTimeout(() => {
init() {
this.$store.dispatch("load").then(() => {
//
this.loadPrivateMessage(this.$store.state.chatStore.privateMsgMaxId);
this.loadGroupMessage(this.$store.state.chatStore.groupMsgMaxId);
// ws
this.$wsApi.init(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken"));
this.$wsApi.connect();
this.$wsApi.onOpen();
this.$wsApi.onMessage((cmd, msgInfo) => {
if (cmd == 2) {
// 线
this.$message.error("您已在其他地方登陆,将被强制下线");
setTimeout(() => {
location.href = "/";
}, 1000)
} else if (cmd == 3) {
//
this.handlePrivateMessage(msgInfo);
} else if (cmd == 4) {
//
this.handleGroupMessage(msgInfo);
}
})
this.$wsApi.onClose((e) => {
console.log(e);
if (e.code == 1006) {
//
this.$message.error("连接已断开,请重新登录");
location.href = "/";
}, 1000)
} else if (cmd == 3) {
//
msgInfo.selfSend = msgInfo.sendId==this.$store.state.userStore.userInfo.id;
//
this.handlePrivateMessage(msgInfo);
} else if (cmd == 4) {
//
msgInfo.selfSend = msgInfo.sendId==this.$store.state.userStore.userInfo.id;
//
this.handleGroupMessage(msgInfo);
}
} else {
this.$wsApi.connect();
}
});
}).catch((e) => {
console.log("初始化失败",e);
})
this.$wsApi.onClose((e) => {
console.log(e);
if(e.code == 1006){
//
this.$message.error("连接已断开,请重新登录");
location.href = "/";
},
loadPrivateMessage(minId) {
this.$store.commit("loadingPrivateMsg",true)
this.$http({
url: "/message/private/loadMessage?minId=" + minId,
method: 'get'
}).then((msgInfos) => {
msgInfos.forEach((msgInfo) => {
this.handlePrivateMessage(msgInfo);
})
if (msgInfos.length == 100) {
//
this.loadPrivateMessage(msgInfos[99].id);
}else{
this.$wsApi.connect();
this.$store.commit("loadingPrivateMsg",false)
}
});
})
},
pullUnreadMessage() {
//
this.$http({
url: "/message/private/pullUnreadMessage",
method: 'post'
});
//
loadGroupMessage(minId) {
this.$store.commit("loadingGroupMsg",true)
this.$http({
url: "/message/group/pullUnreadMessage",
method: 'post'
});
url: "/message/group/loadMessage?minId=" + minId,
method: 'get'
}).then((msgInfos) => {
msgInfos.forEach((msgInfo) => {
this.handleGroupMessage(msgInfo);
})
if (msgInfos.length == 100) {
//
this.loadGroupMessage(msgInfos[99].id);
}else{
this.$store.commit("loadingGroupMsg",false)
}
})
},
handlePrivateMessage(msg) {
//
let friendId = msg.selfSend?msg.recvId:msg.sendId;
let friend = this.$store.state.friendStore.friends.find((f) => f.id == friendId);
if (friend) {
this.insertPrivateMessage(friend, msg);
//
msg.selfSend = msg.sendId == this.$store.state.userStore.userInfo.id;
// id
let friendId = msg.selfSend ? msg.recvId : msg.sendId;
//
if (msg.type == this.$enums.MESSAGE_TYPE.READED) {
if (msg.selfSend) {
//
let chatInfo = {
type: 'PRIVATE',
targetId: friendId
}
this.$store.commit("resetUnreadCount", chatInfo)
} else {
//
this.$store.commit("readedMessage", friendId)
}
return;
}
//
this.$http({
url: `/friend/find/${msg.sendId}`,
method: 'get'
}).then((friend) => {
this.loadFriendInfo(friendId).then((friend) => {
this.insertPrivateMessage(friend, msg);
this.$store.commit("addFriend", friend);
})
},
insertPrivateMessage(friend, msg) {
// webrtc
if (msg.type >= this.$enums.MESSAGE_TYPE.RTC_CALL &&
msg.type <= this.$enums.MESSAGE_TYPE.RTC_CANDIDATE) {
//
if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL ||
msg.type == this.$enums.MESSAGE_TYPE.RTC_CANCEL) {
@ -176,19 +206,22 @@
},
handleGroupMessage(msg) {
//
let group = this.$store.state.groupStore.groups.find((g) => g.id == msg.groupId);
if (group) {
this.insertGroupMessage(group, msg);
//
msg.selfSend = msg.sendId == this.$store.state.userStore.userInfo.id;
let groupId = msg.groupId;
//
if (msg.type == this.$enums.MESSAGE_TYPE.READED) {
//
let chatInfo = {
type: 'GROUP',
targetId: groupId
}
this.$store.commit("resetUnreadCount", chatInfo)
return;
}
//
this.$http({
url: `/group/find/${msg.groupId}`,
method: 'get'
}).then((group) => {
this.loadGroupInfo(groupId).then((group) => {
//
this.insertGroupMessage(group, msg);
this.$store.commit("addGroup", group);
})
},
insertGroupMessage(group, msg) {
@ -221,6 +254,38 @@
},
closeSetting() {
this.showSettingDialog = false;
},
loadFriendInfo(id) {
return new Promise((resolve, reject) => {
let friend = this.$store.state.friendStore.friends.find((f) => f.id == id);
if (friend) {
resolve(friend);
} else {
this.$http({
url: `/friend/find/${id}`,
method: 'get'
}).then((friend) => {
this.$store.commit("addFriend", friend);
resolve(friend)
})
}
});
},
loadGroupInfo(id) {
return new Promise((resolve, reject) => {
let group = this.$store.state.groupStore.groups.find((g) => g.id == id);
if (group) {
resolve(group);
} else {
this.$http({
url: `/group/find/${id}`,
method: 'get'
}).then((group) => {
resolve(group)
this.$store.commit("addGroup", group);
})
}
});
}
},
computed: {
@ -246,12 +311,7 @@
}
},
mounted() {
this.$http({
url: "/user/self",
methods: 'get'
}).then((userInfo) => {
this.init(userInfo);
})
this.init();
},
unmounted() {
this.$wsApi.close();

Loading…
Cancel
Save