Browse Source

feat: 多人音视频功能

master
xsx 2 years ago
parent
commit
b135f97ba2
  1. 5
      im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupDeviceDTO.java
  2. 29
      im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java
  3. 3
      im-platform/src/main/java/com/bx/implatform/session/WebrtcUserInfo.java
  4. 27
      im-ui/src/App.vue
  5. 15
      im-ui/src/api/enums.js
  6. 34
      im-ui/src/assets/iconfont/iconfont.css
  7. BIN
      im-ui/src/assets/iconfont/iconfont.ttf
  8. 84
      im-ui/src/components/chat/ChatBox.vue
  9. 24
      im-ui/src/components/common/HeadImage.vue
  10. 26
      im-ui/src/components/group/AddGroupMember.vue
  11. 2
      im-ui/src/components/rtc/RtcPrivateAcceptor.vue
  12. 1
      im-ui/src/main.js
  13. 30
      im-ui/src/view/Home.vue
  14. 22
      im-uniapp/pages/chat/chat-box.vue

5
im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupDeviceDTO.java

@ -21,6 +21,9 @@ public class WebrtcGroupDeviceDTO {
private Long groupId; private Long groupId;
@ApiModelProperty(value = "是否开启摄像头") @ApiModelProperty(value = "是否开启摄像头")
private Boolean isCamera = false; private Boolean isCamera;
@ApiModelProperty(value = "是否开启麦克风")
private Boolean isMicroPhone;
} }

29
im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java

@ -59,6 +59,9 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
@Override @Override
public void setup(WebrtcGroupSetupDTO dto) { public void setup(WebrtcGroupSetupDTO dto) {
UserSession userSession = SessionContext.getSession(); UserSession userSession = SessionContext.getSession();
if(!imClient.isOnline(userSession.getUserId())){
throw new GlobalException("您已断开连接,请重新登陆");
}
if (dto.getUserInfos().size() > webrtcConfig.getMaxChannel()) { if (dto.getUserInfos().size() > webrtcConfig.getMaxChannel()) {
throw new GlobalException("最多支持" + webrtcConfig.getMaxChannel() + "人进行通话"); throw new GlobalException("最多支持" + webrtcConfig.getMaxChannel() + "人进行通话");
} }
@ -78,8 +81,8 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
List<Long> busyUserIds = new LinkedList<>(); List<Long> busyUserIds = new LinkedList<>();
for (WebrtcUserInfo userInfo : dto.getUserInfos()) { for (WebrtcUserInfo userInfo : dto.getUserInfos()) {
if (!imClient.isOnline(userInfo.getId())) { if (!imClient.isOnline(userInfo.getId())) {
//userInfos.add(userInfo); userInfos.add(userInfo);
offlineUserIds.add(userInfo.getId()); //offlineUserIds.add(userInfo.getId());
} else if (userStateUtils.isBusy(userInfo.getId())) { } else if (userStateUtils.isBusy(userInfo.getId())) {
busyUserIds.add(userInfo.getId()); busyUserIds.add(userInfo.getId());
} else { } else {
@ -99,7 +102,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
if (!offlineUserIds.isEmpty()) { if (!offlineUserIds.isEmpty()) {
WebrtcGroupFailedVO vo = new WebrtcGroupFailedVO(); WebrtcGroupFailedVO vo = new WebrtcGroupFailedVO();
vo.setUserIds(offlineUserIds); vo.setUserIds(offlineUserIds);
vo.setReason("用户不在线"); vo.setReason("用户当前不在线");
sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), userInfo, JSON.toJSONString(vo)); sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), userInfo, JSON.toJSONString(vo));
} }
if (!busyUserIds.isEmpty()) { if (!busyUserIds.isEmpty()) {
@ -209,20 +212,24 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
if (Objects.isNull(member) || member.getQuit()) { if (Objects.isNull(member) || member.getQuit()) {
throw new GlobalException("您不在群里中"); throw new GlobalException("您不在群里中");
} }
// 防止重复进入 IMUserInfo mine = findInChatUser(webrtcSession, userSession.getUserId());
if (isInchat(webrtcSession, userSession.getUserId())) { if(!Objects.isNull(mine) && mine.getTerminal() != userSession.getTerminal()){
throw new GlobalException("已在通话"); throw new GlobalException("已在其他设备加入通话");
} }
WebrtcUserInfo userInfo = new WebrtcUserInfo(); WebrtcUserInfo userInfo = new WebrtcUserInfo();
userInfo.setId(userSession.getUserId()); userInfo.setId(userSession.getUserId());
userInfo.setNickName(member.getAliasName()); userInfo.setNickName(member.getAliasName());
userInfo.setHeadImage(member.getHeadImage()); userInfo.setHeadImage(member.getHeadImage());
// 默认是开启麦克风,关闭摄像头
userInfo.setIsCamera(false); userInfo.setIsCamera(false);
userInfo.setIsMicroPhone(true);
// 将当前用户加入通话用户列表中 // 将当前用户加入通话用户列表中
if (!isExist(webrtcSession, userSession.getUserId())) { if (!isExist(webrtcSession, userSession.getUserId())) {
webrtcSession.getUserInfos().add(userInfo); webrtcSession.getUserInfos().add(userInfo);
} }
if (!isInchat(webrtcSession, userSession.getUserId())) {
webrtcSession.getInChatUsers().add(new IMUserInfo(userSession.getUserId(), userSession.getTerminal())); webrtcSession.getInChatUsers().add(new IMUserInfo(userSession.getUserId(), userSession.getTerminal()));
}
saveWebrtcSession(groupId, webrtcSession); saveWebrtcSession(groupId, webrtcSession);
// 进入忙线状态 // 进入忙线状态
userStateUtils.setBusy(userSession.getUserId()); userStateUtils.setBusy(userSession.getUserId());
@ -237,7 +244,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
public void invite(WebrtcGroupInviteDTO dto) { public void invite(WebrtcGroupInviteDTO dto) {
UserSession userSession = SessionContext.getSession(); UserSession userSession = SessionContext.getSession();
WebrtcGroupSession webrtcSession = getWebrtcSession(dto.getGroupId()); WebrtcGroupSession webrtcSession = getWebrtcSession(dto.getGroupId());
if (dto.getUserInfos().size() + dto.getUserInfos().size() > webrtcConfig.getMaxChannel()) { if (webrtcSession.getUserInfos().size() + dto.getUserInfos().size() > webrtcConfig.getMaxChannel()) {
throw new GlobalException("最多支持" + webrtcConfig.getMaxChannel() + "人进行通话"); throw new GlobalException("最多支持" + webrtcConfig.getMaxChannel() + "人进行通话");
} }
if (!groupMemberService.isInGroup(dto.getGroupId(), getRecvIds(dto.getUserInfos()))) { if (!groupMemberService.isInGroup(dto.getGroupId(), getRecvIds(dto.getUserInfos()))) {
@ -259,7 +266,9 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
continue; continue;
} }
if (!imClient.isOnline(userInfo.getId())) { if (!imClient.isOnline(userInfo.getId())) {
offlineUserIds.add(userInfo.getId()); // offlineUserIds.add(userInfo.getId());
userStateUtils.setBusy(userInfo.getId());
newUserInfos.add(userInfo);
} else if (userStateUtils.isBusy(userInfo.getId())) { } else if (userStateUtils.isBusy(userInfo.getId())) {
busyUserIds.add(userInfo.getId()); busyUserIds.add(userInfo.getId());
} else { } else {
@ -275,7 +284,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
if (!offlineUserIds.isEmpty()) { if (!offlineUserIds.isEmpty()) {
WebrtcGroupFailedVO vo = new WebrtcGroupFailedVO(); WebrtcGroupFailedVO vo = new WebrtcGroupFailedVO();
vo.setUserIds(offlineUserIds); vo.setUserIds(offlineUserIds);
vo.setReason("用户不在线"); vo.setReason("用户当前不在线");
IMUserInfo reciver = new IMUserInfo(userSession.getUserId(), userSession.getTerminal()); IMUserInfo reciver = new IMUserInfo(userSession.getUserId(), userSession.getTerminal());
sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo)); sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo));
} }
@ -417,6 +426,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
} }
// 更新设备状态 // 更新设备状态
userInfo.setIsCamera(dto.getIsCamera()); userInfo.setIsCamera(dto.getIsCamera());
userInfo.setIsMicroPhone(dto.getIsMicroPhone());
saveWebrtcSession(dto.getGroupId(), webrtcSession); saveWebrtcSession(dto.getGroupId(), webrtcSession);
// 广播信令 // 广播信令
List<Long> recvIds = getRecvIds(webrtcSession.getUserInfos()); List<Long> recvIds = getRecvIds(webrtcSession.getUserInfos());
@ -446,7 +456,6 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
host.setId(hostId); host.setId(hostId);
host.setNickName(member.getAliasName()); host.setNickName(member.getAliasName());
host.setHeadImage(member.getHeadImage()); host.setHeadImage(member.getHeadImage());
host.setIsCamera(false);
} }
vo.setHost(host); vo.setHost(host);
} }

3
im-platform/src/main/java/com/bx/implatform/session/WebrtcUserInfo.java

@ -23,4 +23,7 @@ public class WebrtcUserInfo {
@ApiModelProperty(value = "是否开启摄像头") @ApiModelProperty(value = "是否开启摄像头")
private Boolean isCamera; private Boolean isCamera;
@ApiModelProperty(value = "是否开启麦克风")
private Boolean isMicroPhone;
} }

27
im-ui/src/App.vue

@ -85,4 +85,31 @@
.el-button { .el-button {
padding: 8px 15px !important; padding: 8px 15px !important;
} }
.el-checkbox {
display: flex;
align-items: center;
//
.el-checkbox__inner {
width: 20px;
height: 20px;
//
&::after {
height: 12px;
left: 7px;
}
}
//
.el-checkbox__input.is-checked+.el-checkbox__label {
color: #333333;
}
.el-checkbox__label {
line-height: 20px;
padding-left: 8px;
}
}
</style> </style>

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

@ -1,4 +1,3 @@
const MESSAGE_TYPE = { const MESSAGE_TYPE = {
TEXT: 0, TEXT: 0,
IMAGE: 1, IMAGE: 1,
@ -20,7 +19,19 @@ const MESSAGE_TYPE = {
RTC_CANCEL: 104, RTC_CANCEL: 104,
RTC_FAILED: 105, RTC_FAILED: 105,
RTC_HANDUP: 106, RTC_HANDUP: 106,
RTC_CANDIDATE: 107 RTC_CANDIDATE: 107,
RTC_GROUP_SETUP: 200,
RTC_GROUP_ACCEPT: 201,
RTC_GROUP_REJECT: 202,
RTC_GROUP_FAILED: 203,
RTC_GROUP_CANCEL: 204,
RTC_GROUP_QUIT: 205,
RTC_GROUP_INVITE: 206,
RTC_GROUP_JOIN: 207,
RTC_GROUP_OFFER: 208,
RTC_GROUP_ANSWER: 209,
RTC_GROUP_CANDIDATE: 210,
RTC_GROUP_DEVICE: 211
} }
const RTC_STATE = { const RTC_STATE = {

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

@ -1,6 +1,6 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 3791506 */ font-family: "iconfont"; /* Project id 3791506 */
src: url('iconfont.ttf?t=1714220334746') format('truetype'); src: url('iconfont.ttf?t=1718373714629') format('truetype');
} }
.iconfont { .iconfont {
@ -11,6 +11,38 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-invite-rtc:before {
content: "\e65f";
}
.icon-quit:before {
content: "\e606";
}
.icon-camera-off:before {
content: "\e6b5";
}
.icon-speaker-off:before {
content: "\ea3c";
}
.icon-microphone-on:before {
content: "\e63b";
}
.icon-speaker-on:before {
content: "\e6a4";
}
.icon-camera-on:before {
content: "\e627";
}
.icon-microphone-off:before {
content: "\efe5";
}
.icon-chat:before { .icon-chat:before {
content: "\e600"; content: "\e600";
} }

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

Binary file not shown.

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

@ -13,11 +13,11 @@
<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">
<chat-message-item v-if="idx >= showMinIdx" <chat-message-item v-if="idx >= showMinIdx" @call="onCall(msgInfo.type)"
@call="onCall(msgInfo.type)" :mine="msgInfo.sendId == mine.id" :headImage="headImage(msgInfo)"
:mine="msgInfo.sendId == mine.id" :showName="showName(msgInfo)" :msgInfo="msgInfo"
:headImage="headImage(msgInfo)" :showName="showName(msgInfo)" :msgInfo="msgInfo" :groupMembers="groupMembers" @delete="deleteMessage"
:groupMembers="groupMembers" @delete="deleteMessage" @recall="recallMessage"> @recall="recallMessage">
</chat-message-item> </chat-message-item>
</li> </li>
</ul> </ul>
@ -36,8 +36,8 @@
</file-upload> </file-upload>
</div> </div>
<div title="发送文件"> <div title="发送文件">
<file-upload :action="'/file/upload'" :maxSize="10 * 1024 * 1024" @before="onFileBefore" <file-upload :action="'/file/upload'" :maxSize="10 * 1024 * 1024"
@success="onFileSuccess" @fail="onFileFail"> @before="onFileBefore" @success="onFileSuccess" @fail="onFileFail">
<i class="el-icon-wallet"></i> <i class="el-icon-wallet"></i>
</file-upload> </file-upload>
</div> </div>
@ -49,6 +49,9 @@
<div title="语音通话" v-show="chat.type == 'PRIVATE'" class="el-icon-phone-outline" <div title="语音通话" v-show="chat.type == 'PRIVATE'" class="el-icon-phone-outline"
@click="showPrivateVideo('voice')"> @click="showPrivateVideo('voice')">
</div> </div>
<div title="语音通话" v-show="chat.type == 'GROUP'" class="el-icon-phone-outline"
@click="onGroupVideo()">
</div>
<div title="视频通话" v-show="chat.type == 'PRIVATE'" class="el-icon-video-camera" <div title="视频通话" v-show="chat.type == 'PRIVATE'" class="el-icon-video-camera"
@click="showPrivateVideo('video')"> @click="showPrivateVideo('video')">
</div> </div>
@ -57,9 +60,10 @@
<div class="send-content-area"> <div class="send-content-area">
<div contenteditable="true" v-show="!sendImageUrl" ref="editBox" class="send-text-area" <div contenteditable="true" v-show="!sendImageUrl" ref="editBox" class="send-text-area"
:disabled="lockMessage" @paste.prevent="onEditorPaste" :disabled="lockMessage" @paste.prevent="onEditorPaste"
@compositionstart="onEditorCompositionStart" @compositionend="onEditorCompositionEnd" @compositionstart="onEditorCompositionStart"
@input="onEditorInput" :placeholder="placeholder" @blur="onEditBoxBlur()" @compositionend="onEditorCompositionEnd" @input="onEditorInput"
@keydown.down="onKeyDown" @keydown.up="onKeyUp" @keydown.enter.prevent="onKeyEnter">x :placeholder="placeholder" @blur="onEditBoxBlur()" @keydown.down="onKeyDown"
@keydown.up="onKeyUp" @keydown.enter.prevent="onKeyEnter">x
</div> </div>
<div v-show="sendImageUrl" class="send-image-area"> <div v-show="sendImageUrl" class="send-image-area">
@ -85,8 +89,10 @@
<chat-at-box ref="atBox" :ownerId="group.ownerId" :members="groupMembers" :search-text="atSearchText" <chat-at-box ref="atBox" :ownerId="group.ownerId" :members="groupMembers" :search-text="atSearchText"
@select="onAtSelect"></chat-at-box> @select="onAtSelect"></chat-at-box>
<chat-voice :visible="showVoice" @close="closeVoiceBox" @send="onSendVoice"></chat-voice> <chat-voice :visible="showVoice" @close="closeVoiceBox" @send="onSendVoice"></chat-voice>
<chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group" :groupMembers="groupMembers" <group-member-selector ref="rtcSel" :groupId="group.id" @complete="onInviteOk"></group-member-selector>
@close="closeHistoryBox"></chat-history> <rtc-group-join ref="rtcJoin" :groupId="group.id"></rtc-group-join>
<chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group"
:groupMembers="groupMembers" @close="closeHistoryBox"></chat-history>
</el-container> </el-container>
</div> </div>
</template> </template>
@ -99,6 +105,9 @@ 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";
import ChatAtBox from "./ChatAtBox.vue" import ChatAtBox from "./ChatAtBox.vue"
import GroupMemberSelector from "../group/GroupMemberSelector.vue"
import RtcGroupJoin from "../rtc/RtcGroupJoin.vue"
export default { export default {
name: "chatPrivate", name: "chatPrivate",
@ -109,7 +118,9 @@ export default {
Emotion, Emotion,
ChatVoice, ChatVoice,
ChatHistory, ChatHistory,
ChatAtBox ChatAtBox,
GroupMemberSelector,
RtcGroupJoin
}, },
props: { props: {
chat: { chat: {
@ -459,6 +470,47 @@ export default {
} }
this.$store.commit("setRtcInfo", rtcInfo); this.$store.commit("setRtcInfo", rtcInfo);
}, },
onGroupVideo() {
this.$http({
url: "/webrtc/group/info?groupId=" + this.group.id,
method: 'GET'
}).then((rtcInfo) => {
if (rtcInfo.isChating) {
//
this.$refs.rtcJoin.open(rtcInfo);
} else {
//
let ids = [this.mine.id];
let maxChannel = this.$store.state.configStore.webrtc.maxChannel;
this.$refs.rtcSel.open(maxChannel, ids, ids);
}
})
},
onInviteOk(members) {
if(members.length < 2){
return;
}
let userInfos = [];
members.forEach(m => {
userInfos.push({
id: m.userId,
nickName: m.aliasName,
headImage: m.headImage,
isCamera: false,
isMicroPhone: true
})
})
let rtcInfo = {
isHost: true,
groupId: this.group.id,
inviterId: this.mine.id,
userInfos: userInfos
}
// home.vue
this.$eventBus.$emit("openGroupVideo", rtcInfo);
},
showHistoryBox() { showHistoryBox() {
this.showHistory = true; this.showHistory = true;
}, },
@ -786,6 +838,7 @@ export default {
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
border-bottom: 1px #ddd solid; border-bottom: 1px #ddd solid;
.btn-side { .btn-side {
position: absolute; position: absolute;
right: 20px; right: 20px;
@ -799,6 +852,7 @@ export default {
.im-chat-main { .im-chat-main {
padding: 0; padding: 0;
background-color: #f8f8f8; background-color: #f8f8f8;
.im-chat-box { .im-chat-box {
>ul { >ul {
padding: 0 20px; padding: 0 20px;
@ -825,6 +879,7 @@ export default {
border-top: #ccc solid 1px; border-top: #ccc solid 1px;
padding: 2px; padding: 2px;
background-color: #E8F2FF; background-color: #E8F2FF;
>div { >div {
font-size: 22px; font-size: 22px;
cursor: pointer; cursor: pointer;
@ -934,4 +989,5 @@ export default {
border: #dddddd solid 1px; border: #dddddd solid 1px;
animation: rtl-drawer-in .3s 1ms; animation: rtl-drawer-in .3s 1ms;
} }
}</style> }
</style>

24
im-ui/src/components/common/HeadImage.vue

@ -28,6 +28,16 @@
type: Number, type: Number,
default: 50 default: 50
}, },
width: {
type: Number
},
height: {
type: Number
},
radius:{
type: String,
default: "10%"
},
url: { url: {
type: String type: String
}, },
@ -55,11 +65,17 @@
}, },
computed:{ computed:{
avatarImageStyle() { avatarImageStyle() {
return `width:${this.size}px; height:${this.size}px;` let w = this.width ? this.width : this.size;
let h = this.height ? this.height : this.size;
return `width:${w}px; height:${h}px;
border-radius: ${this.radius};`
}, },
avatarTextStyle() { avatarTextStyle() {
return `width: ${this.size}px;height:${this.size}px; let w = this.width ? this.width : this.size;
color:${this.textColor};font-size:${this.size*0.6}px;` let h = this.height ? this.height : this.size;
return `width: ${w}px;height:${h}px;
color:${this.textColor};font-size:${w*0.6}px;
border-radius: ${this.radius};`
}, },
textColor(){ textColor(){
let hash = 0; let hash = 0;
@ -79,7 +95,7 @@
.avatar-image { .avatar-image {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-radius: 10%; display: block;
} }
.avatar-text{ .avatar-text{

26
im-ui/src/components/group/AddGroupMember.vue

@ -136,32 +136,6 @@
border-radius: 5px; border-radius: 5px;
overflow: hidden; overflow: hidden;
.el-checkbox {
display: flex;
align-items: center;
//
.el-checkbox__inner {
width: 20px;
height: 20px;
//
&::after {
height: 12px;
left: 7px;
}
}
//
.el-checkbox__input.is-checked+.el-checkbox__label {
color: #333333;
}
.el-checkbox__label {
line-height: 20px;
padding-left: 8px;
}
}
.agm-friend-checkbox { .agm-friend-checkbox {
margin-right: 20px; margin-right: 20px;

2
im-ui/src/components/rtc/RtcPrivateAcceptor.vue

@ -15,7 +15,7 @@
import HeadImage from '../common/HeadImage.vue'; import HeadImage from '../common/HeadImage.vue';
export default { export default {
name: "videoAcceptor", name: "rtcPrivateAcceptor",
components: { components: {
HeadImage HeadImage
}, },

1
im-ui/src/main.js

@ -21,6 +21,7 @@ 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.prototype.$enums = enums; // 枚举
Vue.prototype.$eventBus = new Vue(); // 全局事件
Vue.config.productionTip = false; Vue.config.productionTip = false;
new Vue({ new Vue({

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

@ -42,6 +42,7 @@
@close="$store.commit('closeFullImageBox')"></full-image> @close="$store.commit('closeFullImageBox')"></full-image>
<rtc-private-video ref="rtcPrivateVideo"></rtc-private-video> <rtc-private-video ref="rtcPrivateVideo"></rtc-private-video>
<rtc-private-acceptor ref="rtcPrivateAcceptor"></rtc-private-acceptor> <rtc-private-acceptor ref="rtcPrivateAcceptor"></rtc-private-acceptor>
<rtc-group-video ref="rtcGroupVideo" ></rtc-group-video>
</el-container> </el-container>
</template> </template>
@ -52,6 +53,7 @@
import FullImage from '../components/common/FullImage.vue'; import FullImage from '../components/common/FullImage.vue';
import RtcPrivateVideo from '../components/rtc/RtcPrivateVideo.vue'; import RtcPrivateVideo from '../components/rtc/RtcPrivateVideo.vue';
import RtcPrivateAcceptor from '../components/rtc/RtcPrivateAcceptor.vue'; import RtcPrivateAcceptor from '../components/rtc/RtcPrivateAcceptor.vue';
import RtcGroupVideo from '../components/rtc/RtcGroupVideo.vue';
export default { export default {
components: { components: {
@ -60,7 +62,8 @@
UserInfo, UserInfo,
FullImage, FullImage,
RtcPrivateVideo, RtcPrivateVideo,
RtcPrivateAcceptor RtcPrivateAcceptor,
RtcGroupVideo
}, },
data() { data() {
return { return {
@ -70,6 +73,12 @@
}, },
methods: { methods: {
init() { init() {
this.$eventBus.$on('openGroupVideo', (rctInfo)=>{
//
console.log(this.$refs.rtcGroupVideo)
this.$refs.rtcGroupVideo.open(rctInfo);
});
this.$store.dispatch("load").then(() => { this.$store.dispatch("load").then(() => {
// ws // ws
this.$wsApi.connect(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken")); this.$wsApi.connect(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken"));
@ -153,9 +162,8 @@
}) })
}, },
insertPrivateMessage(friend, msg) { insertPrivateMessage(friend, msg) {
// webrtc // webrtc
if (msg.type >= this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE && if (msg.type >= 100 && msg.type <= 199) {
msg.type <= this.$enums.MESSAGE_TYPE.RTC_CANDIDATE) {
let rtcInfo = this.$store.state.userStore.rtcInfo; let rtcInfo = this.$store.state.userStore.rtcInfo;
// //
if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE || if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE ||
@ -180,7 +188,8 @@
// //
this.$store.commit("insertMessage", msg); this.$store.commit("insertMessage", msg);
// //
if (!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED) { if (!msg.selfSend && msg.type < 10
&& msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip(); this.playAudioTip();
} }
}, },
@ -214,12 +223,20 @@
} }
// //
msg.selfSend = msg.sendId == this.$store.state.userStore.userInfo.id; msg.selfSend = msg.sendId == this.$store.state.userStore.userInfo.id;
//
if (msg.type >= 200 && msg.type <= 299) {
this.$nextTick(()=>{
this.$refs.rtcGroupVideo.onRTCMessage(msg);
})
return;
}
this.loadGroupInfo(msg.groupId).then((group) => { this.loadGroupInfo(msg.groupId).then((group) => {
// //
this.insertGroupMessage(group, msg); this.insertGroupMessage(group, msg);
}) })
}, },
insertGroupMessage(group, msg) { insertGroupMessage(group, msg) {
let chatInfo = { let chatInfo = {
type: 'GROUP', type: 'GROUP',
targetId: group.id, targetId: group.id,
@ -231,7 +248,8 @@
// //
this.$store.commit("insertMessage", msg); this.$store.commit("insertMessage", msg);
// //
if (!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED) { if (!msg.selfSend && msg.type < 10
&& msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip(); this.playAudioTip();
} }
}, },

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

@ -81,11 +81,11 @@
</view> </view>
<!-- #ifndef MP-WEIXIN --> <!-- #ifndef MP-WEIXIN -->
<!-- 音视频不支持小程序 --> <!-- 音视频不支持小程序 -->
<view v-if="chat.type == 'PRIVATE'" class="chat-tools-item" @click="onVideoCall()"> <view v-if="chat.type == 'PRIVATE'" class="chat-tools-item" @click="onPriviteVideo()">
<view class="tool-icon iconfont icon-video"></view> <view class="tool-icon iconfont icon-video"></view>
<view class="tool-name">视频通话</view> <view class="tool-name">视频通话</view>
</view> </view>
<view v-if="chat.type == 'PRIVATE'" class="chat-tools-item" @click="onVoiceCall()"> <view v-if="chat.type == 'PRIVATE'" class="chat-tools-item" @click="onPriviteVoice()">
<view class="tool-icon iconfont icon-call"></view> <view class="tool-icon iconfont icon-call"></view>
<view class="tool-name">语音通话</view> <view class="tool-name">语音通话</view>
</view> </view>
@ -110,7 +110,7 @@
<!-- 群语音通话时选择成员 --> <!-- 群语音通话时选择成员 -->
<group-member-selector ref="selBox" :members="groupMembers" <group-member-selector ref="selBox" :members="groupMembers"
:maxSize="$store.state.configStore.webrtc.maxChannel" :maxSize="$store.state.configStore.webrtc.maxChannel"
@complete="onSelectMember"></group-member-selector> @complete="onInviteOk"></group-member-selector>
<group-rtc-join ref="rtcJoin" :groupId="group.id"></group-rtc-join> <group-rtc-join ref="rtcJoin" :groupId="group.id"></group-rtc-join>
</view> </view>
</template> </template>
@ -175,18 +175,18 @@
}, },
onRtCall(msgInfo) { onRtCall(msgInfo) {
if (msgInfo.type == this.$enums.MESSAGE_TYPE.RT_VOICE) { if (msgInfo.type == this.$enums.MESSAGE_TYPE.RT_VOICE) {
this.onVoiceCall(); this.onPriviteVoice();
} else if (msgInfo.type == this.$enums.MESSAGE_TYPE.RT_VIDEO) { } else if (msgInfo.type == this.$enums.MESSAGE_TYPE.RT_VIDEO) {
this.onVideoCall(); this.onPriviteVideo();
} }
}, },
onVideoCall() { onPriviteVideo() {
const friendInfo = encodeURIComponent(JSON.stringify(this.friend)); const friendInfo = encodeURIComponent(JSON.stringify(this.friend));
uni.navigateTo({ uni.navigateTo({
url: `/pages/chat/chat-private-video?mode=video&friend=${friendInfo}&isHost=true` url: `/pages/chat/chat-private-video?mode=video&friend=${friendInfo}&isHost=true`
}) })
}, },
onVoiceCall() { onPriviteVoice() {
const friendInfo = encodeURIComponent(JSON.stringify(this.friend)); const friendInfo = encodeURIComponent(JSON.stringify(this.friend));
uni.navigateTo({ uni.navigateTo({
url: `/pages/chat/chat-private-video?mode=voice&friend=${friendInfo}&isHost=true` url: `/pages/chat/chat-private-video?mode=voice&friend=${friendInfo}&isHost=true`
@ -208,7 +208,10 @@
} }
}) })
}, },
onSelectMember(ids) { onInviteOk(ids) {
if(ids.length < 2){
return;
}
let users = []; let users = [];
ids.forEach(id => { ids.forEach(id => {
let m = this.groupMembers.find(m => m.userId == id); let m = this.groupMembers.find(m => m.userId == id);
@ -217,7 +220,8 @@
id: m.userId, id: m.userId,
nickName: m.aliasName, nickName: m.aliasName,
headImage: m.headImage, headImage: m.headImage,
isCamera: false isCamera: false,
isMicroPhone: true
}) })
}) })
const groupId = this.group.id; const groupId = this.group.id;

Loading…
Cancel
Save