Browse Source

视频聊天功能-开发中

master
xie.bx 3 years ago
parent
commit
1b22de8837
  1. 8
      im-client/src/main/java/com/bx/imclient/sender/IMSender.java
  2. 8
      im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java
  3. 9
      im-commom/src/main/java/com/bx/imcommon/model/SendResult.java
  4. 118
      im-platform/src/main/java/com/bx/implatform/controller/WebrtcController.java
  5. 10
      im-platform/src/main/java/com/bx/implatform/enums/MessageType.java
  6. 4
      im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java
  7. 29
      im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java
  8. 10
      im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java
  9. 10
      im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java
  10. 16
      im-ui/src/assets/iconfont/iconfont.css
  11. BIN
      im-ui/src/assets/iconfont/iconfont.ttf
  12. BIN
      im-ui/src/assets/iconfont/iconfont.woff
  13. BIN
      im-ui/src/assets/iconfont/iconfont.woff2
  14. 56
      im-ui/src/components/chat/ChatBox.vue
  15. 1
      im-ui/src/components/chat/ChatVoice.vue
  16. 4
      im-ui/src/main.js
  17. 30
      im-ui/src/store/uiStore.js
  18. 12
      im-ui/src/store/userStore.js
  19. 40
      im-ui/src/view/Home.vue

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

@ -4,7 +4,7 @@ import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.RedisKey; import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendStatus; import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.GroupMessageInfo; import com.bx.imcommon.model.GroupMessageInfo;
import com.bx.imcommon.model.IMRecvInfo; import com.bx.imcommon.model.IMRecvInfo;
import com.bx.imcommon.model.PrivateMessageInfo; import com.bx.imcommon.model.PrivateMessageInfo;
@ -54,8 +54,7 @@ public class IMSender {
SendResult result = new SendResult(); SendResult result = new SendResult();
result.setMessageInfo(messageInfo); result.setMessageInfo(messageInfo);
result.setRecvId(recvId); result.setRecvId(recvId);
result.setStatus(IMSendStatus.FAIL); result.setCode(IMSendCode.NOT_ONLINE);
result.setFailReason("用户不在线");
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result); listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result);
} }
} }
@ -103,8 +102,7 @@ public class IMSender {
SendResult result = new SendResult(); SendResult result = new SendResult();
result.setMessageInfo(messageInfo); result.setMessageInfo(messageInfo);
result.setRecvId(id); result.setRecvId(id);
result.setStatus(IMSendStatus.FAIL); result.setCode(IMSendCode.NOT_ONLINE);
result.setFailReason("用户不在线");
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE,result); listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE,result);
} }
} }

8
im-commom/src/main/java/com/bx/imcommon/enums/IMSendStatus.java → im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java

@ -1,16 +1,18 @@
package com.bx.imcommon.enums; package com.bx.imcommon.enums;
public enum IMSendStatus { public enum IMSendCode {
SUCCESS(0,"发送成功"), SUCCESS(0,"发送成功"),
FAIL(1,"发送失败"); NOT_ONLINE(1,"对方当前不在线"),
NOT_FIND_CHANNEL(2,"未找到对方的channel"),
UNKONW_ERROR(9999,"未知异常");
private int code; private int code;
private String desc; private String desc;
// 构造方法 // 构造方法
IMSendStatus(int code, String desc) { IMSendCode(int code, String desc) {
this.code = code; this.code = code;
this.desc = desc; this.desc = desc;
} }

9
im-commom/src/main/java/com/bx/imcommon/model/SendResult.java

@ -1,6 +1,6 @@
package com.bx.imcommon.model; package com.bx.imcommon.model;
import com.bx.imcommon.enums.IMSendStatus; import com.bx.imcommon.enums.IMSendCode;
import lombok.Data; import lombok.Data;
@Data @Data
@ -14,12 +14,7 @@ public class SendResult<T> {
/* /*
* 发送状态 * 发送状态
*/ */
private IMSendStatus status; private IMSendCode code;
/*
* 失败原因
*/
private String failReason="";
/* /*
* 消息体(透传) * 消息体(透传)

118
im-platform/src/main/java/com/bx/implatform/controller/WebrtcController.java

@ -0,0 +1,118 @@
package com.bx.implatform.controller;
import com.bx.imclient.IMClient;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.implatform.enums.MessageType;
import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.session.SessionContext;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Api(tags = "webrtc视频单人通话")
@RestController
@RequestMapping("/webrtc/private")
public class WebrtcController {
@Autowired
private IMClient imClient;
@ApiOperation(httpMethod = "POST", value = "呼叫视频通话")
@PostMapping("/call")
public Result call(@RequestParam Long uid, @RequestBody String offer) {
Long userId = SessionContext.getSession().getId();
PrivateMessageInfo message = new PrivateMessageInfo();
message.setType(MessageType.RTC_CALL.code());
message.setRecvId(uid);
message.setSendId(userId);
message.setContent(offer);
imClient.sendPrivateMessage(uid,message);
return ResultUtils.success();
}
@ApiOperation(httpMethod = "POST", value = "接受视频通话")
@PostMapping("/accept")
public Result accept(@RequestParam Long uid,@RequestBody String answer) {
Long userId = SessionContext.getSession().getId();
PrivateMessageInfo message = new PrivateMessageInfo();
message.setType(MessageType.RTC_ACCEPT.code());
message.setRecvId(uid);
message.setSendId(userId);
message.setContent(answer);
imClient.sendPrivateMessage(uid,message);
return ResultUtils.success();
}
@ApiOperation(httpMethod = "POST", value = "拒绝视频通话")
@PostMapping("/reject")
public Result reject(@RequestParam Long uid) {
Long userId = SessionContext.getSession().getId();
PrivateMessageInfo message = new PrivateMessageInfo();
message.setType(MessageType.RTC_REJECT.code());
message.setRecvId(uid);
message.setSendId(userId);
imClient.sendPrivateMessage(uid,message);
return ResultUtils.success();
}
@ApiOperation(httpMethod = "POST", value = "取消呼叫")
@PostMapping("/cancel")
public Result cancel(@RequestParam Long uid) {
Long userId = SessionContext.getSession().getId();
PrivateMessageInfo message = new PrivateMessageInfo();
message.setType(MessageType.RTC_CANCEL.code());
message.setRecvId(uid);
message.setSendId(userId);
imClient.sendPrivateMessage(uid,message);
return ResultUtils.success();
}
@ApiOperation(httpMethod = "POST", value = "呼叫失败")
@PostMapping("/failed")
public Result failed(@RequestParam Long uid,@RequestParam String reason) {
Long userId = SessionContext.getSession().getId();
PrivateMessageInfo message = new PrivateMessageInfo();
message.setType(MessageType.RTC_FAILED.code());
message.setRecvId(uid);
message.setSendId(userId);
message.setContent(reason);
imClient.sendPrivateMessage(uid,message);
return ResultUtils.success();
}
@ApiOperation(httpMethod = "POST", value = "挂断")
@PostMapping("/handup")
public Result leave(@RequestParam Long uid) {
Long userId = SessionContext.getSession().getId();
PrivateMessageInfo message = new PrivateMessageInfo();
message.setType(MessageType.RTC_HANDUP.code());
message.setRecvId(uid);
message.setSendId(userId);
imClient.sendPrivateMessage(uid,message);
return ResultUtils.success();
}
@PostMapping("/candidate")
@ApiOperation(httpMethod = "POST", value = "同步candidate")
public Result candidate(@RequestParam Long uid,@RequestBody String candidate ) {
Long userId = SessionContext.getSession().getId();
PrivateMessageInfo message = new PrivateMessageInfo();
message.setType(MessageType.RTC_CANDIDATE.code());
message.setRecvId(uid);
message.setSendId(userId);
message.setContent(candidate);
imClient.sendPrivateMessage(uid,message);
return ResultUtils.success();
}
}

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

@ -7,7 +7,15 @@ public enum MessageType {
FILE(1,"文件"), FILE(1,"文件"),
IMAGE(2,"图片"), IMAGE(2,"图片"),
VIDEO(3,"视频"), VIDEO(3,"视频"),
TIP(10,"系统提示"); TIP(10,"系统提示"),
RTC_CALL(101,"呼叫"),
RTC_ACCEPT(102,"接受"),
RTC_REJECT(103, "拒绝"),
RTC_CANCEL(104,"取消呼叫"),
RTC_FAILED(105,"呼叫失败"),
RTC_HANDUP(106,"挂断"),
RTC_CANDIDATE(107,"同步candidate");
private Integer code; private Integer code;

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

@ -3,7 +3,7 @@ package com.bx.implatform.listener;
import com.bx.imclient.annotation.IMListener; import com.bx.imclient.annotation.IMListener;
import com.bx.imclient.listener.MessageListener; import com.bx.imclient.listener.MessageListener;
import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendStatus; import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.GroupMessageInfo; import com.bx.imcommon.model.GroupMessageInfo;
import com.bx.imcommon.model.SendResult; import com.bx.imcommon.model.SendResult;
import com.bx.implatform.contant.RedisKey; import com.bx.implatform.contant.RedisKey;
@ -29,7 +29,7 @@ public class GroupMessageListener implements MessageListener {
} }
// 保存该用户已拉取的最大消息id // 保存该用户已拉取的最大消息id
if(result.getStatus().equals(IMSendStatus.SUCCESS)) { if(result.getCode().equals(IMSendCode.SUCCESS)) {
String key = RedisKey.IM_GROUP_READED_POSITION + messageInfo.getGroupId() + ":" + result.getRecvId(); String key = RedisKey.IM_GROUP_READED_POSITION + messageInfo.getGroupId() + ":" + result.getRecvId();
redisTemplate.opsForValue().set(key, messageInfo.getId()); redisTemplate.opsForValue().set(key, messageInfo.getId());
} }

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

@ -1,10 +1,11 @@
package com.bx.implatform.listener; package com.bx.implatform.listener;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bx.imclient.IMClient;
import com.bx.imclient.annotation.IMListener; import com.bx.imclient.annotation.IMListener;
import com.bx.imclient.listener.MessageListener; import com.bx.imclient.listener.MessageListener;
import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendStatus; import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.PrivateMessageInfo; import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.imcommon.model.SendResult; import com.bx.imcommon.model.SendResult;
import com.bx.implatform.entity.PrivateMessage; import com.bx.implatform.entity.PrivateMessage;
@ -14,6 +15,8 @@ import com.bx.implatform.service.IPrivateMessageService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
@Slf4j @Slf4j
@IMListener(type = IMListenerType.PRIVATE_MESSAGE) @IMListener(type = IMListenerType.PRIVATE_MESSAGE)
@ -22,15 +25,33 @@ public class PrivateMessageListener implements MessageListener {
@Autowired @Autowired
private IPrivateMessageService privateMessageService; private IPrivateMessageService privateMessageService;
@Autowired
private IMClient imClient;
@Override @Override
public void process(SendResult result){ public void process(SendResult result){
PrivateMessageInfo messageInfo = (PrivateMessageInfo) result.getMessageInfo(); PrivateMessageInfo messageInfo = (PrivateMessageInfo) result.getMessageInfo();
if(messageInfo.getType().equals(MessageType.TIP)){ // 提示类数据不记录
// 提示类数据不记录 if(messageInfo.getType().equals(MessageType.TIP.code())){
return; return;
} }
// 视频通话信令不记录
if(messageInfo.getType() >= MessageType.RTC_CALL.code() && messageInfo.getType()< MessageType.RTC_CANDIDATE.code()){
// 通知用户呼叫失败了
if(messageInfo.getType() == MessageType.RTC_CALL.code()
&& !result.getCode().equals(IMSendCode.SUCCESS)){
PrivateMessageInfo sendMessage = new PrivateMessageInfo();
sendMessage.setRecvId(messageInfo.getSendId());
sendMessage.setSendId(messageInfo.getRecvId());
sendMessage.setType(MessageType.RTC_FAILED.code());
sendMessage.setContent(result.getCode().description());
sendMessage.setSendTime(new Date());
imClient.sendPrivateMessage(sendMessage.getRecvId(),sendMessage);
}
}
// 更新消息状态 // 更新消息状态
if(result.getStatus().equals(IMSendStatus.SUCCESS)){ if(result.getCode().equals(IMSendCode.SUCCESS)){
UpdateWrapper<PrivateMessage> updateWrapper = new UpdateWrapper<>(); UpdateWrapper<PrivateMessage> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(PrivateMessage::getId,messageInfo.getId()) updateWrapper.lambda().eq(PrivateMessage::getId,messageInfo.getId())
.eq(PrivateMessage::getStatus, MessageStatus.UNREAD.code()) .eq(PrivateMessage::getStatus, MessageStatus.UNREAD.code())

10
im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java

@ -2,7 +2,7 @@ package com.bx.imserver.netty.processor;
import com.bx.imcommon.contant.RedisKey; import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMSendStatus; import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.GroupMessageInfo; import com.bx.imcommon.model.GroupMessageInfo;
import com.bx.imcommon.model.IMRecvInfo; import com.bx.imcommon.model.IMRecvInfo;
import com.bx.imcommon.model.IMSendInfo; import com.bx.imcommon.model.IMSendInfo;
@ -43,7 +43,7 @@ public class GroupMessageProcessor extends MessageProcessor<IMRecvInfo<GroupMes
String key = RedisKey.IM_RESULT_GROUP_QUEUE; String key = RedisKey.IM_RESULT_GROUP_QUEUE;
SendResult sendResult = new SendResult(); SendResult sendResult = new SendResult();
sendResult.setRecvId(recvId); sendResult.setRecvId(recvId);
sendResult.setStatus(IMSendStatus.SUCCESS); sendResult.setCode(IMSendCode.SUCCESS);
sendResult.setMessageInfo(messageInfo); sendResult.setMessageInfo(messageInfo);
redisTemplate.opsForList().rightPush(key,sendResult); redisTemplate.opsForList().rightPush(key,sendResult);
@ -52,8 +52,7 @@ public class GroupMessageProcessor extends MessageProcessor<IMRecvInfo<GroupMes
String key = RedisKey.IM_RESULT_GROUP_QUEUE; String key = RedisKey.IM_RESULT_GROUP_QUEUE;
SendResult sendResult = new SendResult(); SendResult sendResult = new SendResult();
sendResult.setRecvId(recvId); sendResult.setRecvId(recvId);
sendResult.setStatus(IMSendStatus.FAIL); sendResult.setCode(IMSendCode.NOT_FIND_CHANNEL);
sendResult.setFailReason("未找到WS连接");
sendResult.setMessageInfo(messageInfo); sendResult.setMessageInfo(messageInfo);
redisTemplate.opsForList().rightPush(key,sendResult); redisTemplate.opsForList().rightPush(key,sendResult);
log.error("未找到WS连接,发送者:{},群id:{},接收id:{},内容:{}",messageInfo.getSendId(),messageInfo.getGroupId(),recvIds,messageInfo.getContent()); log.error("未找到WS连接,发送者:{},群id:{},接收id:{},内容:{}",messageInfo.getSendId(),messageInfo.getGroupId(),recvIds,messageInfo.getContent());
@ -63,8 +62,7 @@ public class GroupMessageProcessor extends MessageProcessor<IMRecvInfo<GroupMes
String key = RedisKey.IM_RESULT_GROUP_QUEUE; String key = RedisKey.IM_RESULT_GROUP_QUEUE;
SendResult sendResult = new SendResult(); SendResult sendResult = new SendResult();
sendResult.setRecvId(recvId); sendResult.setRecvId(recvId);
sendResult.setStatus(IMSendStatus.FAIL); sendResult.setCode(IMSendCode.UNKONW_ERROR);
sendResult.setFailReason("未知异常");
sendResult.setMessageInfo(messageInfo); sendResult.setMessageInfo(messageInfo);
redisTemplate.opsForList().rightPush(key,sendResult); redisTemplate.opsForList().rightPush(key,sendResult);
log.error("发送消息异常,发送者:{},群id:{},接收id:{},内容:{}",messageInfo.getSendId(),messageInfo.getGroupId(),recvIds,messageInfo.getContent()); log.error("发送消息异常,发送者:{},群id:{},接收id:{},内容:{}",messageInfo.getSendId(),messageInfo.getGroupId(),recvIds,messageInfo.getContent());

10
im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java

@ -2,7 +2,7 @@ package com.bx.imserver.netty.processor;
import com.bx.imcommon.contant.RedisKey; import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMSendStatus; import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.IMRecvInfo; import com.bx.imcommon.model.IMRecvInfo;
import com.bx.imcommon.model.IMSendInfo; import com.bx.imcommon.model.IMSendInfo;
import com.bx.imcommon.model.PrivateMessageInfo; import com.bx.imcommon.model.PrivateMessageInfo;
@ -38,7 +38,7 @@ public class PrivateMessageProcessor extends MessageProcessor<IMRecvInfo<Privat
String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; String key = RedisKey.IM_RESULT_PRIVATE_QUEUE;
SendResult sendResult = new SendResult(); SendResult sendResult = new SendResult();
sendResult.setRecvId(recvId); sendResult.setRecvId(recvId);
sendResult.setStatus(IMSendStatus.SUCCESS); sendResult.setCode(IMSendCode.SUCCESS);
sendResult.setMessageInfo(messageInfo); sendResult.setMessageInfo(messageInfo);
redisTemplate.opsForList().rightPush(key,sendResult); redisTemplate.opsForList().rightPush(key,sendResult);
}else{ }else{
@ -46,8 +46,7 @@ public class PrivateMessageProcessor extends MessageProcessor<IMRecvInfo<Privat
String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; String key = RedisKey.IM_RESULT_PRIVATE_QUEUE;
SendResult sendResult = new SendResult(); SendResult sendResult = new SendResult();
sendResult.setRecvId(recvId); sendResult.setRecvId(recvId);
sendResult.setStatus(IMSendStatus.FAIL); sendResult.setCode(IMSendCode.NOT_FIND_CHANNEL);
sendResult.setFailReason("未找到WS连接");
sendResult.setMessageInfo(messageInfo); sendResult.setMessageInfo(messageInfo);
redisTemplate.opsForList().rightPush(key,sendResult); redisTemplate.opsForList().rightPush(key,sendResult);
log.error("未找到WS连接,发送者:{},接收者:{},内容:{}",messageInfo.getSendId(),recvId,messageInfo.getContent()); log.error("未找到WS连接,发送者:{},接收者:{},内容:{}",messageInfo.getSendId(),recvId,messageInfo.getContent());
@ -57,8 +56,7 @@ public class PrivateMessageProcessor extends MessageProcessor<IMRecvInfo<Privat
String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; String key = RedisKey.IM_RESULT_PRIVATE_QUEUE;
SendResult sendResult = new SendResult(); SendResult sendResult = new SendResult();
sendResult.setRecvId(recvId); sendResult.setRecvId(recvId);
sendResult.setStatus(IMSendStatus.FAIL); sendResult.setCode(IMSendCode.UNKONW_ERROR);
sendResult.setFailReason("未知异常");
sendResult.setMessageInfo(messageInfo); sendResult.setMessageInfo(messageInfo);
redisTemplate.opsForList().rightPush(key,sendResult); redisTemplate.opsForList().rightPush(key,sendResult);
log.error("发送异常,发送者:{},接收者:{},内容:{}",messageInfo.getSendId(),recvId,messageInfo.getContent(),e); log.error("发送异常,发送者:{},接收者:{},内容:{}",messageInfo.getSendId(),recvId,messageInfo.getContent(),e);

16
im-ui/src/assets/iconfont/iconfont.css

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 3776657 */ font-family: "iconfont"; /* Project id 3791506 */
src: url('iconfont.woff2?t=1668665799410') format('woff2'), src: url('iconfont.woff2?t=1669336625993') format('woff2'),
url('iconfont.woff?t=1668665799410') format('woff'), url('iconfont.woff?t=1669336625993') format('woff'),
url('iconfont.ttf?t=1668665799410') format('truetype'); url('iconfont.ttf?t=1669336625993') format('truetype');
} }
.iconfont { .iconfont {
@ -41,3 +41,11 @@
content: "\e953"; content: "\e953";
} }
.icon-phone-reject:before {
content: "\e605";
}
.icon-phone-accept:before {
content: "\e8be";
}

BIN
im-ui/src/assets/iconfont/iconfont.ttf

Binary file not shown.

BIN
im-ui/src/assets/iconfont/iconfont.woff

Binary file not shown.

BIN
im-ui/src/assets/iconfont/iconfont.woff2

Binary file not shown.

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

@ -2,8 +2,7 @@
<el-container class="chat-box"> <el-container class="chat-box">
<el-header height="60px"> <el-header height="60px">
<span>{{title}}</span> <span>{{title}}</span>
<span title="群聊信息" v-show="this.chat.type=='GROUP'" class="btn-side el-icon-more" <span title="群聊信息" v-show="this.chat.type=='GROUP'" class="btn-side el-icon-more" @click="showSide=!showSide"></span>
@click="showSide=!showSide"></span>
</el-header> </el-header>
<el-main style="padding: 0;"> <el-main style="padding: 0;">
<el-container> <el-container>
@ -12,10 +11,8 @@
<div class="im-chat-box"> <div class="im-chat-box">
<ul> <ul>
<li v-for="(msgInfo,idx) in chat.messages" :key="idx"> <li v-for="(msgInfo,idx) in chat.messages" :key="idx">
<message-item :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)" <message-item :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)" :showName="showName(msgInfo)"
:showName="showName(msgInfo)" :msgInfo="msgInfo" :msgInfo="msgInfo" @delete="deleteMessage" @recall="recallMessage">
@delete="deleteMessage"
@recall="recallMessage">
</message-item> </message-item>
</li> </li>
</ul> </ul>
@ -26,24 +23,24 @@
<div title="表情" class="icon iconfont icon-biaoqing" ref="emotion" @click="switchEmotionBox()"> <div title="表情" class="icon iconfont icon-biaoqing" ref="emotion" @click="switchEmotionBox()">
</div> </div>
<div title="发送图片"> <div title="发送图片">
<file-upload :action="imageAction" :maxSize="5*1024*1024" <file-upload :action="imageAction" :maxSize="5*1024*1024" :fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp','image/gif']"
:fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp','image/gif']"
@before="handleImageBefore" @success="handleImageSuccess" @fail="handleImageFail"> @before="handleImageBefore" @success="handleImageSuccess" @fail="handleImageFail">
<i class="el-icon-picture-outline"></i> <i class="el-icon-picture-outline"></i>
</file-upload> </file-upload>
</div> </div>
<div title="发送文件"> <div title="发送文件">
<file-upload :action="fileAction" :maxSize="10*1024*1024" @before="handleFileBefore" <file-upload :action="fileAction" :maxSize="10*1024*1024" @before="handleFileBefore" @success="handleFileSuccess"
@success="handleFileSuccess" @fail="handleFileFail"> @fail="handleFileFail">
<i class="el-icon-wallet"></i> <i class="el-icon-wallet"></i>
</file-upload> </file-upload>
</div> </div>
<div title="发送语音" class="el-icon-microphone" @click="showVoiceBox()"> <div title="发送语音" class="el-icon-microphone" @click="showVoiceBox()">
</div> </div>
<div title="发起视频" class="el-icon-phone-outline" @click="showVideoBox()">
</div>
<div title="聊天记录" class="el-icon-chat-dot-round" @click="showHistoryBox()"></div> <div title="聊天记录" class="el-icon-chat-dot-round" @click="showHistoryBox()"></div>
</div> </div>
<textarea v-model="sendText" ref="sendBox" class="send-text-area" <textarea v-model="sendText" ref="sendBox" class="send-text-area" @keydown.enter="sendTextMessage()"></textarea>
@keydown.enter="sendTextMessage()"></textarea>
<div class="im-chat-send"> <div class="im-chat-send">
<el-button type="primary" @click="sendTextMessage()">发送</el-button> <el-button type="primary" @click="sendTextMessage()">发送</el-button>
</div> </div>
@ -57,9 +54,7 @@
</el-main> </el-main>
<emotion v-show="showEmotion" :pos="emoBoxPos" @emotion="handleEmotion"></Emotion> <emotion v-show="showEmotion" :pos="emoBoxPos" @emotion="handleEmotion"></Emotion>
<chat-voice :visible="showVoice" @close="closeVoiceBox" @send="handleSendVoice"></chat-voice> <chat-voice :visible="showVoice" @close="closeVoiceBox" @send="handleSendVoice"></chat-voice>
<chat-history :visible="showHistory" <chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group" :groupMembers="groupMembers" @close="closeHistoryBox"></chat-history>
:chat="chat" :friend="friend" :group="group" :groupMembers="groupMembers"
@close="closeHistoryBox"></chat-history>
</el-container> </el-container>
</template> </template>
@ -70,8 +65,7 @@
import Emotion from "../common/Emotion.vue"; import Emotion from "../common/Emotion.vue";
import ChatVoice from "./ChatVoice.vue"; import ChatVoice from "./ChatVoice.vue";
import ChatHistory from "./ChatHistory.vue"; import ChatHistory from "./ChatHistory.vue";
export default { export default {
name: "chatPrivate", name: "chatPrivate",
components: { components: {
@ -129,7 +123,7 @@
thumbUrl: url thumbUrl: url
} }
let msgInfo = { let msgInfo = {
id:0, id: 0,
fileId: file.uid, fileId: file.uid,
sendId: this.mine.id, sendId: this.mine.id,
content: JSON.stringify(data), content: JSON.stringify(data),
@ -218,10 +212,17 @@
closeVoiceBox() { closeVoiceBox() {
this.showVoice = false; this.showVoice = false;
}, },
showHistoryBox(){ showVideoBox() {
console.log(this.friend)
this.$store.commit("showChatPrivateVideoBox", {
friend: this.friend,
master: true
});
},
showHistoryBox() {
this.showHistory = true; this.showHistory = true;
}, },
closeHistoryBox(){ closeHistoryBox() {
this.showHistory = false; this.showHistory = false;
}, },
handleSendVoice(data) { handleSendVoice(data) {
@ -292,17 +293,17 @@
return false; return false;
} }
}, },
deleteMessage(msgInfo){ deleteMessage(msgInfo) {
this.$confirm( '确认删除消息?','删除消息', { this.$confirm('确认删除消息?', '删除消息', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
this.$store.commit("deleteMessage",msgInfo); this.$store.commit("deleteMessage", msgInfo);
}); });
}, },
recallMessage(msgInfo){ recallMessage(msgInfo) {
this.$confirm('确认撤回消息?','撤回消息', { this.$confirm('确认撤回消息?', '撤回消息', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
@ -316,10 +317,10 @@
msgInfo = JSON.parse(JSON.stringify(msgInfo)); msgInfo = JSON.parse(JSON.stringify(msgInfo));
msgInfo.type = 10; msgInfo.type = 10;
msgInfo.content = '你撤回了一条消息'; msgInfo.content = '你撤回了一条消息';
this.$store.commit("insertMessage",msgInfo); this.$store.commit("insertMessage", msgInfo);
}) })
}); });
}, },
loadGroup(groupId) { loadGroup(groupId) {
this.$http({ this.$http({
@ -346,6 +347,7 @@
method: 'get' method: 'get'
}).then((friend) => { }).then((friend) => {
this.friend = friend; this.friend = friend;
console.log(this.friend)
this.$store.commit("updateChatFromFriend", friend); this.$store.commit("updateChatFromFriend", friend);
this.$store.commit("updateFriend", friend); this.$store.commit("updateFriend", friend);
}) })

1
im-ui/src/components/chat/ChatVoice.vue

@ -19,7 +19,6 @@
<el-button round type="primary" v-show="state=='COMPLETE'" @click="handleRestartRecord()">重新录音</el-button> <el-button round type="primary" v-show="state=='COMPLETE'" @click="handleRestartRecord()">重新录音</el-button>
<el-button round type="primary" v-show="state=='COMPLETE'" @click="handleSendRecord()">立即发送</el-button> <el-button round type="primary" v-show="state=='COMPLETE'" @click="handleSendRecord()">立即发送</el-button>
</el-row> </el-row>
</el-dialog> </el-dialog>
</template> </template>

4
im-ui/src/main.js

@ -9,7 +9,7 @@ import * as socketApi from './api/wssocket';
import emotion from './api/emotion.js'; import emotion from './api/emotion.js';
import element from './api/element.js'; import element from './api/element.js';
import store from './store'; import store from './store';
import * as enums from './api/enums.js';
Vue.use(ElementUI); Vue.use(ElementUI);
@ -18,7 +18,7 @@ Vue.prototype.$wsApi = socketApi;
Vue.prototype.$http = httpRequest // http请求方法 Vue.prototype.$http = httpRequest // http请求方法
Vue.prototype.$emo = emotion; // emo表情 Vue.prototype.$emo = emotion; // emo表情
Vue.prototype.$elm = element; // 元素操作 Vue.prototype.$elm = element; // 元素操作
Vue.prototype.$enums = enums; // 枚举
Vue.config.productionTip = false; Vue.config.productionTip = false;
new Vue({ new Vue({

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

@ -11,6 +11,17 @@ export default {
fullImage: { // 全屏大图 fullImage: { // 全屏大图
show: false, show: false,
url: "" url: ""
},
chatPrivateVideo:{ // 私人视频聊天
show: false,
master: false, // 是否房主
friend:{},
offer:{} // 对方发起带过过来的sdp信息
},
videoAcceptor:{ // 视频呼叫选择
show: false,
friend:{}
} }
}, },
@ -35,6 +46,23 @@ export default {
}, },
closeFullImageBox(state){ closeFullImageBox(state){
state.fullImage.show = false; state.fullImage.show = false;
},
showChatPrivateVideoBox(state,info){
state.chatPrivateVideo.show = true;
state.chatPrivateVideo.friend = info.friend;
state.chatPrivateVideo.master = info.master;
state.chatPrivateVideo.offer = info.offer;
},
closeChatPrivateVideoBox(state){
state.chatPrivateVideo.show = false;
},
showVideoAcceptorBox(state,friend){
state.videoAcceptor.show = true;
state.videoAcceptor.friend = friend;
},
closeVideoAcceptorBox(state){
state.videoAcceptor.show = false;
} }
}, }
} }

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

@ -1,7 +1,12 @@
import {USER_STATE} from "../api/enums.js"
export default { export default {
state: { state: {
userInfo: {} userInfo: {
},
state: USER_STATE.FREE
}, },
mutations: { mutations: {
@ -12,7 +17,10 @@ export default {
this.commit("resetChatStore"); this.commit("resetChatStore");
} }
state.userInfo = userInfo; state.userInfo = userInfo;
} },
setUserState(state, userState) {
state.state = userState;
},
} }
} }

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

@ -38,6 +38,17 @@
<setting :visible="showSettingDialog" @close="closeSetting()"></setting> <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> <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> <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>
<video-acceptor ref="videoAcceptor"
v-show="uiStore.videoAcceptor.show"
:friend="uiStore.videoAcceptor.friend"
@close="$store.commit('closeVideoAcceptorBox')" >
</video-acceptor>
</el-container> </el-container>
</template> </template>
@ -46,17 +57,23 @@
import Setting from '../components/setting/Setting.vue'; import Setting from '../components/setting/Setting.vue';
import UserInfo from '../components/common/UserInfo.vue'; import UserInfo from '../components/common/UserInfo.vue';
import FullImage from '../components/common/FullImage.vue'; import FullImage from '../components/common/FullImage.vue';
import ChatPrivateVideo from '../components/chat/ChatPrivateVideo.vue';
import VideoAcceptor from '../components/chat/VideoAcceptor.vue';
export default { export default {
components: { components: {
HeadImage, HeadImage,
Setting, Setting,
UserInfo, UserInfo,
FullImage FullImage,
ChatPrivateVideo,
VideoAcceptor
}, },
data() { data() {
return { return {
showSettingDialog: false showSettingDialog: false,
} }
}, },
methods: { methods: {
@ -81,7 +98,7 @@
} else if (cmd == 4) { } else if (cmd == 4) {
// //
this.handleGroupMessage(msgInfo); this.handleGroupMessage(msgInfo);
} }
}) })
}, },
pullUnreadMessage() { pullUnreadMessage() {
@ -113,6 +130,21 @@
}) })
}, },
insertPrivateMessage(friend, msg) { insertPrivateMessage(friend, msg) {
// webrtc
if(msg.type >= this.$enums.MESSAGE_TYPE.RTC_CALL
&& msg.type <= this.$enums.MESSAGE_TYPE.RTC_CANDIDATE){
//
console.log(msg)
if(msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL
|| msg.type == this.$enums.MESSAGE_TYPE.RTC_CANCEL){
this.$store.commit("showVideoAcceptorBox",friend);
this.$refs.videoAcceptor.handleMessage(msg)
}else {
this.$refs.privateVideo.handleMessage(msg)
}
return ;
}
let chatInfo = { let chatInfo = {
type: 'PRIVATE', type: 'PRIVATE',
targetId: friend.id, targetId: friend.id,

Loading…
Cancel
Save