@@ -28,7 +27,7 @@
+ @click="showFullImageBox()" loading="lazy" />
@@ -51,14 +50,14 @@
-
-
-
+
+
+
{{msgInfo.content}}
-
+
已读
-
-
-
-
- {{rtcInfo.friend.nickName}}
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/im-ui/src/components/chat/ChatVoice.vue b/im-ui/src/components/chat/ChatRecord.vue
similarity index 92%
rename from im-ui/src/components/chat/ChatVoice.vue
rename to im-ui/src/components/chat/ChatRecord.vue
index de9f116..89fa3bb 100644
--- a/im-ui/src/components/chat/ChatVoice.vue
+++ b/im-ui/src/components/chat/ChatRecord.vue
@@ -1,12 +1,12 @@
-
+
-
{{stateTip}}
+
{{stateTip}}
时长: {{state=='STOP'?0:parseInt(rc.duration)}}s
-
+
开始录音
暂停录音
继续录音
@@ -27,7 +27,7 @@
import Recorder from 'js-audio-recorder';
export default {
- name: 'chatVoice',
+ name: 'chatRecord',
props: {
visible: {
type: Boolean
@@ -126,13 +126,13 @@
\ No newline at end of file
diff --git a/im-ui/src/components/common/HeadImage.vue b/im-ui/src/components/common/HeadImage.vue
index eccba60..91eec68 100644
--- a/im-ui/src/components/common/HeadImage.vue
+++ b/im-ui/src/components/common/HeadImage.vue
@@ -28,6 +28,16 @@
type: Number,
default: 50
},
+ width: {
+ type: Number
+ },
+ height: {
+ type: Number
+ },
+ radius:{
+ type: String,
+ default: "10%"
+ },
url: {
type: String
},
@@ -54,12 +64,18 @@
}
},
computed:{
- avatarImageStyle(){
- return `width:${this.size}px; height:${this.size}px;`
+ avatarImageStyle() {
+ 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(){
- return `width: ${this.size}px;height:${this.size}px;
- color:${this.textColor};font-size:${this.size*0.6}px;`
+ avatarTextStyle() {
+ let w = this.width ? this.width : this.size;
+ 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(){
let hash = 0;
@@ -79,7 +95,7 @@
.avatar-image {
position: relative;
overflow: hidden;
- border-radius: 10%;
+ display: block;
}
.avatar-text{
diff --git a/im-ui/src/components/group/AddGroupMember.vue b/im-ui/src/components/group/AddGroupMember.vue
index d60be28..d41f815 100644
--- a/im-ui/src/components/group/AddGroupMember.vue
+++ b/im-ui/src/components/group/AddGroupMember.vue
@@ -136,33 +136,7 @@
border-radius: 5px;
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 {
margin-right: 20px;
}
diff --git a/im-ui/src/components/group/GroupMemberItem.vue b/im-ui/src/components/group/GroupMemberItem.vue
new file mode 100644
index 0000000..1af7943
--- /dev/null
+++ b/im-ui/src/components/group/GroupMemberItem.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
{{ member.aliasName }}
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/components/group/GroupMemberSelector.vue b/im-ui/src/components/group/GroupMemberSelector.vue
new file mode 100644
index 0000000..138d79b
--- /dev/null
+++ b/im-ui/src/components/group/GroupMemberSelector.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
已勾选{{checkedMembers.length}}位成员
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-ui/src/components/rtc/RtcGroupJoin.vue b/im-ui/src/components/rtc/RtcGroupJoin.vue
new file mode 100644
index 0000000..7d09b10
--- /dev/null
+++ b/im-ui/src/components/rtc/RtcGroupJoin.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
{{'发起人:'+rtcInfo.host.nickName}}
+
+
+
{{rtcInfo.userInfos.length+'人正在通话中'}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-ui/src/components/rtc/RtcGroupVideo.vue b/im-ui/src/components/rtc/RtcGroupVideo.vue
new file mode 100644
index 0000000..d5d028d
--- /dev/null
+++ b/im-ui/src/components/rtc/RtcGroupVideo.vue
@@ -0,0 +1,42 @@
+
+
+
+
+ 多人音视频通话为付费功能,有需要请联系作者...
+
+
+ 点击下方文档了解详细信息:
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-ui/src/components/rtc/RtcPrivateAcceptor.vue b/im-ui/src/components/rtc/RtcPrivateAcceptor.vue
new file mode 100644
index 0000000..6ffae6b
--- /dev/null
+++ b/im-ui/src/components/rtc/RtcPrivateAcceptor.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-ui/src/components/rtc/RtcPrivateVideo.vue b/im-ui/src/components/rtc/RtcPrivateVideo.vue
new file mode 100644
index 0000000..0c63a72
--- /dev/null
+++ b/im-ui/src/components/rtc/RtcPrivateVideo.vue
@@ -0,0 +1,496 @@
+
+
+
+
+
+
+
+ {{friend.nickName}}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-ui/src/main.js b/im-ui/src/main.js
index 9ced174..cf6411c 100644
--- a/im-ui/src/main.js
+++ b/im-ui/src/main.js
@@ -6,6 +6,7 @@ import 'element-ui/lib/theme-chalk/index.css';
import './assets/iconfont/iconfont.css';
import httpRequest from './api/httpRequest';
import * as socketApi from './api/wssocket';
+import * as messageType from './api/messageType';
import emotion from './api/emotion.js';
import element from './api/element.js';
import store from './store';
@@ -16,11 +17,13 @@ import './utils/directive/dialogDrag';
Vue.use(ElementUI);
// 挂载全局
Vue.prototype.$wsApi = socketApi;
+Vue.prototype.$msgType = messageType
Vue.prototype.$date = date;
Vue.prototype.$http = httpRequest // http请求方法
Vue.prototype.$emo = emotion; // emo表情
Vue.prototype.$elm = element; // 元素操作
Vue.prototype.$enums = enums; // 枚举
+Vue.prototype.$eventBus = new Vue(); // 全局事件
Vue.config.productionTip = false;
new Vue({
diff --git a/im-ui/src/store/chatStore.js b/im-ui/src/store/chatStore.js
index 9a240be..b8e8774 100644
--- a/im-ui/src/store/chatStore.js
+++ b/im-ui/src/store/chatStore.js
@@ -142,11 +142,13 @@ export default {
chat.lastContent = "[文件]";
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = "[语音]";
- } else if (msgInfo.type == MESSAGE_TYPE.TEXT || msgInfo.type == MESSAGE_TYPE.RECALL) {
+ } else if (msgInfo.type == MESSAGE_TYPE.TEXT
+ || msgInfo.type == MESSAGE_TYPE.RECALL
+ || msgInfo.type == MESSAGE_TYPE.TIP_TEXT ) {
chat.lastContent = msgInfo.content;
- } else if (msgInfo.type == MESSAGE_TYPE.RT_VOICE) {
+ } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) {
chat.lastContent = "[语音通话]";
- } else if (msgInfo.type == MESSAGE_TYPE.RT_VIDEO) {
+ } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) {
chat.lastContent = "[视频通话]";
}
chat.lastSendTime = msgInfo.sendTime;
@@ -239,14 +241,14 @@ export default {
this.commit("saveToStorage");
},
- loadingPrivateMsg(state, loadding) {
- state.loadingPrivateMsg = loadding;
+ loadingPrivateMsg(state, loading) {
+ state.loadingPrivateMsg = loading;
if (!state.loadingPrivateMsg && !state.loadingGroupMsg) {
this.commit("sort")
}
},
- loadingGroupMsg(state, loadding) {
- state.loadingGroupMsg = loadding;
+ loadingGroupMsg(state, loading) {
+ state.loadingGroupMsg = loading;
if (!state.loadingPrivateMsg && !state.loadingGroupMsg) {
this.commit("sort")
}
diff --git a/im-ui/src/store/configStore.js b/im-ui/src/store/configStore.js
new file mode 100644
index 0000000..6debf5a
--- /dev/null
+++ b/im-ui/src/store/configStore.js
@@ -0,0 +1,32 @@
+import http from '../api/httpRequest.js'
+
+export default {
+ state: {
+ webrtc: {}
+ },
+ mutations: {
+ setConfig(state, config) {
+ state.webrtc = config.webrtc;
+ },
+ clear(state){
+ state.webrtc = {};
+ }
+ },
+ actions:{
+ loadConfig(context){
+ return new Promise((resolve, reject) => {
+ http({
+ url: '/system/config',
+ method: 'GET'
+ }).then((config) => {
+ console.log("系统配置",config)
+ context.commit("setConfig",config);
+ resolve();
+ }).catch((res)=>{
+ reject(res);
+ });
+ })
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/im-ui/src/store/index.js b/im-ui/src/store/index.js
index 6b136bf..37426b5 100644
--- a/im-ui/src/store/index.js
+++ b/im-ui/src/store/index.js
@@ -4,12 +4,13 @@ import chatStore from './chatStore.js';
import friendStore from './friendStore.js';
import userStore from './userStore.js';
import groupStore from './groupStore.js';
+import configStore from './configStore.js';
import uiStore from './uiStore.js';
Vue.use(Vuex)
export default new Vuex.Store({
- modules: {chatStore,friendStore,userStore,groupStore,uiStore},
+ modules: {chatStore,friendStore,userStore,groupStore,configStore,uiStore},
state: {},
mutations: {
},
@@ -20,6 +21,7 @@ export default new Vuex.Store({
promises.push(this.dispatch("loadFriend"));
promises.push(this.dispatch("loadGroup"));
promises.push(this.dispatch("loadChat"));
+ promises.push(this.dispatch("loadConfig"));
return Promise.all(promises);
})
},
diff --git a/im-ui/src/view/Chat.vue b/im-ui/src/view/Chat.vue
index f7b729b..c0ea8bc 100644
--- a/im-ui/src/view/Chat.vue
+++ b/im-ui/src/view/Chat.vue
@@ -6,7 +6,7 @@
-
@@ -85,7 +85,7 @@
}
- .chat-list-loadding{
+ .chat-list-loading{
height: 50px;
background-color: #eee;
diff --git a/im-ui/src/view/Home.vue b/im-ui/src/view/Home.vue
index 1ea3f07..7feb810 100644
--- a/im-ui/src/view/Home.vue
+++ b/im-ui/src/view/Home.vue
@@ -7,7 +7,7 @@
@click.native="showSettingDialog = true">
-
+
@@ -40,8 +40,8 @@
@close="$store.commit('closeUserInfoBox')">
-
-
+
+
@@ -50,8 +50,9 @@
import Setting from '../components/setting/Setting.vue';
import UserInfo from '../components/common/UserInfo.vue';
import FullImage from '../components/common/FullImage.vue';
- import ChatPrivateVideo from '../components/chat/ChatPrivateVideo.vue';
- import ChatVideoAcceptor from '../components/chat/ChatVideoAcceptor.vue';
+ import RtcPrivateVideo from '../components/rtc/RtcPrivateVideo.vue';
+ import RtcPrivateAcceptor from '../components/rtc/RtcPrivateAcceptor.vue';
+ import RtcGroupVideo from '../components/rtc/RtcGroupVideo.vue';
export default {
components: {
@@ -59,8 +60,9 @@
Setting,
UserInfo,
FullImage,
- ChatPrivateVideo,
- ChatVideoAcceptor
+ RtcPrivateVideo,
+ RtcPrivateAcceptor,
+ RtcGroupVideo
},
data() {
return {
@@ -70,6 +72,15 @@
},
methods: {
init() {
+ this.$eventBus.$on('openPrivateVideo', (rctInfo) => {
+ // 进入单人视频通话
+ this.$refs.rtcPrivateVideo.open(rctInfo);
+ });
+ this.$eventBus.$on('openGroupVideo', (rctInfo) => {
+ // 进入多人视频通话
+ this.$refs.rtcGroupVideo.open(rctInfo);
+ });
+
this.$store.dispatch("load").then(() => {
// ws初始化
this.$wsApi.connect(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken"));
@@ -125,7 +136,7 @@
},
handlePrivateMessage(msg) {
// 消息加载标志
- if (msg.type == this.$enums.MESSAGE_TYPE.LOADDING) {
+ if (msg.type == this.$enums.MESSAGE_TYPE.LOADING) {
this.$store.commit("loadingPrivateMsg", JSON.parse(msg.content))
return;
}
@@ -146,6 +157,11 @@
}
// 标记这条消息是不是自己发的
msg.selfSend = msg.sendId == this.$store.state.userStore.userInfo.id;
+ // 单人webrtc 信令
+ if (this.$msgType.isRtcPrivate(msg.type)) {
+ this.$refs.rtcPrivateVideo.onRTCMessage(msg)
+ return;
+ }
// 好友id
let friendId = msg.selfSend ? msg.recvId : msg.sendId;
this.loadFriendInfo(friendId).then((friend) => {
@@ -153,21 +169,6 @@
})
},
insertPrivateMessage(friend, msg) {
- // webrtc 信令
- if (msg.type >= this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE &&
- msg.type <= this.$enums.MESSAGE_TYPE.RTC_CANDIDATE) {
- let rtcInfo = this.$store.state.userStore.rtcInfo;
- // 呼叫
- if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE ||
- msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL_VIDEO ||
- rtcInfo.state == this.$enums.RTC_STATE.FREE ||
- rtcInfo.state == this.$enums.RTC_STATE.WAIT_ACCEPT) {
- this.$refs.videoAcceptor.onRTCMessage(msg,friend)
- } else {
- this.$refs.privateVideo.onRTCMessage(msg)
- }
- return;
- }
let chatInfo = {
type: 'PRIVATE',
@@ -180,13 +181,14 @@
// 插入消息
this.$store.commit("insertMessage", msg);
// 播放提示音
- if (!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED) {
+ if (!msg.selfSend && this.$msgType.isNormal(msg.type) &&
+ msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip();
}
},
handleGroupMessage(msg) {
// 消息加载标志
- if (msg.type == this.$enums.MESSAGE_TYPE.LOADDING) {
+ if (msg.type == this.$enums.MESSAGE_TYPE.LOADING) {
this.$store.commit("loadingGroupMsg", JSON.parse(msg.content))
return;
}
@@ -214,12 +216,20 @@
}
// 标记这条消息是不是自己发的
msg.selfSend = msg.sendId == this.$store.state.userStore.userInfo.id;
+ // 群视频信令
+ if (this.$msgType.isRtcGroup(msg.type)) {
+ this.$nextTick(() => {
+ this.$refs.rtcGroupVideo.onRTCMessage(msg);
+ })
+ return;
+ }
this.loadGroupInfo(msg.groupId).then((group) => {
// 插入群聊消息
this.insertGroupMessage(group, msg);
})
},
insertGroupMessage(group, msg) {
+
let chatInfo = {
type: 'GROUP',
targetId: group.id,
@@ -231,7 +241,8 @@
// 插入消息
this.$store.commit("insertMessage", msg);
// 播放提示音
- if (!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED) {
+ if (!msg.selfSend && msg.type <= this.$enums.MESSAGE_TYPE.VIDEO &&
+ msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip();
}
},
@@ -335,15 +346,16 @@
background-color: #19082f !important;
padding: 0 !important;
text-align: center;
+
.link {
text-decoration: none;
-
+
&.router-link-active .icon {
color: #ba785a;
}
}
-
- .icon {
+
+ .icon {
font-size: 26px !important;
color: #ddd;
}
@@ -376,7 +388,7 @@
.icon {
font-size: 28px;
}
-
+
&:hover {
color: white;
}
diff --git a/im-ui/src/view/Login.vue b/im-ui/src/view/Login.vue
index 5e14eda..2ab0468 100644
--- a/im-ui/src/view/Login.vue
+++ b/im-ui/src/view/Login.vue
@@ -8,35 +8,20 @@
加入uniapp移动端,支持移动端和web端同时在线,多端消息同步
目前uniapp移动端支持安卓、ios、h5、微信小程序
聊天窗口支持粘贴截图、@群成员、已读未读显示
- 支持群聊已读显示(回执消息)
- 语雀文档
- 盒子IM详细介绍文档 ,目前限时免费开放中
+ 语雀文档:
+ 盒子IM详细介绍文档
-
最近更新(2024-03-17):
+
最近更新(2024-06-22):
- web端音视频功能优化:支持语音呼叫、会话中加入通话状态消息
- uniapp端支持音视频通话,并与web端打通
- uniapp端音视频源码通话源码暂未开源,需付费获取:
- uniapp端音视频通源码购买说明
+ 群语音通话功能上线,且同时支持web端和uniapp端
+ 音视频通话部分源码未开源,可付费获取:
+ 音视频源码购买说明
-
-
-
最近更新(2024-04-27):
-
- uniapp端加载离线消息慢以及卡顿问题优化
- web端样式风格调整
-
-
如果本项目对您有帮助,请在gitee上帮忙点个star
diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue
index 9c5ceb2..7bd7d24 100644
--- a/im-uniapp/App.vue
+++ b/im-uniapp/App.vue
@@ -15,8 +15,6 @@
init() {
// 加载数据
store.dispatch("load").then(() => {
- // 审核
- this.initAudit();
// 初始化websocket
this.initWebSocket();
}).catch((e) => {
@@ -50,6 +48,7 @@
}
});
wsApi.onClose((res) => {
+ console.log("ws断开",res);
// 1000是客户端正常主动关闭
if (res.code != 1000) {
// 重新连接
@@ -82,7 +81,7 @@
},
handlePrivateMessage(msg) {
// 消息加载标志
- if (msg.type == enums.MESSAGE_TYPE.LOADDING) {
+ if (msg.type == enums.MESSAGE_TYPE.LOADING) {
store.commit("loadingPrivateMsg", JSON.parse(msg.content))
return;
}
@@ -109,32 +108,32 @@
},
insertPrivateMessage(friend, msg) {
- // webrtc 信令
- if (msg.type >= enums.MESSAGE_TYPE.RTC_CALL_VOICE &&
- msg.type <= enums.MESSAGE_TYPE.RTC_CANDIDATE) {
+ // 单人视频信令
+ if (this.$msgType.isRtcPrivate(msg.type)) {
// #ifdef MP-WEIXIN
// 小程序不支持音视频
return;
// #endif
// 被呼叫,弹出视频页面
+ let delayTime = 100;
if(msg.type == enums.MESSAGE_TYPE.RTC_CALL_VOICE
|| msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO){
let mode = msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO? "video":"voice";
let pages = getCurrentPages();
let curPage = pages[pages.length-1].route;
- if(curPage != "pages/chat/chat-video"){
+ if(curPage != "pages/chat/chat-private-video"){
const friendInfo = encodeURIComponent(JSON.stringify(friend));
uni.navigateTo({
- url: `/pages/chat/chat-video?mode=${mode}&friend=${friendInfo}&isHost=false`
+ url: `/pages/chat/chat-private-video?mode=${mode}&friend=${friendInfo}&isHost=false`
})
+ delayTime = 500;
}
}
setTimeout(() => {
- uni.$emit('WS_RTC',msg);
- },500)
+ uni.$emit('WS_RTC_PRIVATE',msg);
+ },delayTime)
return;
}
-
let chatInfo = {
type: 'PRIVATE',
targetId: friend.id,
@@ -146,12 +145,12 @@
// 插入消息
store.commit("insertMessage", msg);
// 播放提示音
- !msg.selfSend && this.playAudioTip();
+ this.playAudioTip();
},
handleGroupMessage(msg) {
// 消息加载标志
- if (msg.type == enums.MESSAGE_TYPE.LOADDING) {
+ if (msg.type == enums.MESSAGE_TYPE.LOADING) {
store.commit("loadingGroupMsg",JSON.parse(msg.content))
return;
}
@@ -186,6 +185,35 @@
},
insertGroupMessage(group, msg) {
+ // 群视频信令
+ if (this.$msgType.isRtcGroup(msg.type)) {
+ // #ifdef MP-WEIXIN
+ // 小程序不支持音视频
+ return;
+ // #endif
+ // 被呼叫,弹出视频页面
+ let delayTime = 100;
+ if(msg.type == enums.MESSAGE_TYPE.RTC_GROUP_SETUP){
+ let pages = getCurrentPages();
+ let curPage = pages[pages.length-1].route;
+ if(curPage != "pages/chat/chat-group-video"){
+ const userInfos = encodeURIComponent(msg.content);
+ const inviterId = msg.sendId;
+ const groupId = msg.groupId
+ uni.navigateTo({
+ url: `/pages/chat/chat-group-video?groupId=${groupId}&isHost=false
+ &inviterId=${inviterId}&userInfos=${userInfos}`
+ })
+ delayTime = 500;
+ }
+ }
+ // 消息转发到chat-group-video页面进行处理
+ setTimeout(() => {
+ uni.$emit('WS_RTC_GROUP',msg);
+ },delayTime)
+ return;
+ }
+
let chatInfo = {
type: 'GROUP',
targetId: group.id,
@@ -197,7 +225,7 @@
// 插入消息
store.commit("insertMessage", msg);
// 播放提示音
- !msg.selfSend && this.playAudioTip();
+ this.playAudioTip();
},
loadFriendInfo(id) {
return new Promise((resolve, reject) => {
@@ -251,21 +279,6 @@
return true;
}
return loginInfo.expireTime < new Date().getTime();
- },
- initAudit() {
- if (store.state.userStore.userInfo.type == 1) {
- // 显示群组功能
- uni.setTabBarItem({
- index: 2,
- text: "群聊"
- })
- } else {
- // 隐藏群组功能
- uni.setTabBarItem({
- index: 2,
- text: "搜索"
- })
- }
}
},
onLaunch() {
diff --git a/im-uniapp/common/enums.js b/im-uniapp/common/enums.js
index b695b21..6dddc3b 100644
--- a/im-uniapp/common/enums.js
+++ b/im-uniapp/common/enums.js
@@ -5,14 +5,14 @@ const MESSAGE_TYPE = {
FILE:2,
AUDIO:3,
VIDEO:4,
- RT_VOICE:5,
- RT_VIDEO:6,
RECALL:10,
READED:11,
RECEIPT:12,
TIP_TIME:20,
TIP_TEXT:21,
- LOADDING:30,
+ LOADING:30,
+ ACT_RT_VOICE:40,
+ ACT_RT_VIDEO:41,
RTC_CALL_VOICE: 100,
RTC_CALL_VIDEO: 101,
RTC_ACCEPT: 102,
@@ -20,7 +20,19 @@ const MESSAGE_TYPE = {
RTC_CANCEL: 104,
RTC_FAILED: 105,
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 USER_STATE = {
diff --git a/im-uniapp/common/messageType.js b/im-uniapp/common/messageType.js
new file mode 100644
index 0000000..4ab602c
--- /dev/null
+++ b/im-uniapp/common/messageType.js
@@ -0,0 +1,40 @@
+
+// 是否普通消息
+let isNormal = function(type){
+ return type>=0 && type < 10;
+}
+
+// 是否状态消息
+let isStatus = function(type){
+ return type>=10 && type < 20;
+}
+
+// 是否提示消息
+let isTip = function(type){
+ return type>=20 && type < 30;
+}
+
+// 操作交互类消息
+let isAction = function(type){
+ return type>=40 && type < 50;
+}
+
+// 单人通话信令
+let isRtcPrivate = function(type){
+ return type>=100 && type < 300;
+}
+
+// 多人通话信令
+let isRtcGroup = function(type){
+ return type>=200 && type < 400;
+}
+
+
+export {
+ isNormal,
+ isStatus,
+ isTip,
+ isAction,
+ isRtcPrivate,
+ isRtcGroup
+}
\ No newline at end of file
diff --git a/im-uniapp/components/chat-message-item/chat-message-item.vue b/im-uniapp/components/chat-message-item/chat-message-item.vue
index 7fc701e..6520813 100644
--- a/im-uniapp/components/chat-message-item/chat-message-item.vue
+++ b/im-uniapp/components/chat-message-item/chat-message-item.vue
@@ -7,7 +7,7 @@
{{$date.toTimeText(msgInfo.sendTime)}}
-
@@ -52,13 +52,13 @@
-
-
-
+
+
{{msgInfo.content}}
-
+
已读
+
+
+ 选择成员
+ 清空
+ 确定({{checkedIds.length}})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ m.aliasName}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-uniapp/components/group-rtc-join/group-rtc-join.vue b/im-uniapp/components/group-rtc-join/group-rtc-join.vue
new file mode 100644
index 0000000..0c0081f
--- /dev/null
+++ b/im-uniapp/components/group-rtc-join/group-rtc-join.vue
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
{{rtcInfo.userInfos.length+'人正在通话中'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-uniapp/components/user-search/user-search.vue b/im-uniapp/components/user-search/user-search.vue
deleted file mode 100644
index fadd7f6..0000000
--- a/im-uniapp/components/user-search/user-search.vue
+++ /dev/null
@@ -1,118 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {{ user.nickName}}
-
- 加为好友
- 已添加
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/im-uniapp/hybrid/html/rtc-group/index.html b/im-uniapp/hybrid/html/rtc-group/index.html
new file mode 100644
index 0000000..be64c86
--- /dev/null
+++ b/im-uniapp/hybrid/html/rtc-group/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+ 语音通话
+
+
+ 音视频通话为付费功能,有需要请联系作者...
+
+
\ No newline at end of file
diff --git a/im-uniapp/hybrid/html/index.html b/im-uniapp/hybrid/html/rtc-private/index.html
similarity index 100%
rename from im-uniapp/hybrid/html/index.html
rename to im-uniapp/hybrid/html/rtc-private/index.html
diff --git a/im-uniapp/main.js b/im-uniapp/main.js
index f3f3774..fb1398c 100644
--- a/im-uniapp/main.js
+++ b/im-uniapp/main.js
@@ -4,6 +4,7 @@ import emotion from './common/emotion.js';
import * as enums from './common/enums.js';
import * as date from './common/date';
import * as socketApi from './common/wssocket';
+import * as messageType from './common/messageType';
import store from './store';
import { createSSRApp } from 'vue'
// #ifdef H5
@@ -19,6 +20,7 @@ export function createApp() {
app.use(store);
app.config.globalProperties.$http = request;
app.config.globalProperties.$wsApi = socketApi;
+ app.config.globalProperties.$msgType = messageType;
app.config.globalProperties.$emo = emotion;
app.config.globalProperties.$enums = enums;
app.config.globalProperties.$date = date;
diff --git a/im-uniapp/manifest.json b/im-uniapp/manifest.json
index e5d7646..d69c7ad 100644
--- a/im-uniapp/manifest.json
+++ b/im-uniapp/manifest.json
@@ -100,7 +100,7 @@
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wxda94f40bfad0262c",
- "libVersion": "latest",
+ "libVersion" : "latest",
"setting" : {
"urlCheck" : false
},
diff --git a/im-uniapp/pages.json b/im-uniapp/pages.json
index 520b23c..4a4f870 100644
--- a/im-uniapp/pages.json
+++ b/im-uniapp/pages.json
@@ -17,7 +17,9 @@
}, {
"path": "pages/chat/chat-box"
},{
- "path": "pages/chat/chat-video"
+ "path": "pages/chat/chat-private-video"
+ },{
+ "path": "pages/chat/chat-group-video"
}, {
"path": "pages/friend/friend-add"
}, {
@@ -61,7 +63,7 @@
"pagePath": "pages/group/group",
"iconPath": "static/tarbar/group.png",
"selectedIconPath": "static/tarbar/group_active.png",
- "text": "搜索"
+ "text": "群聊"
},
{
"pagePath": "pages/mine/mine",
diff --git a/im-uniapp/pages/chat/chat-box.vue b/im-uniapp/pages/chat/chat-box.vue
index 08702a6..bb906e7 100644
--- a/im-uniapp/pages/chat/chat-box.vue
+++ b/im-uniapp/pages/chat/chat-box.vue
@@ -6,14 +6,13 @@
-
-
-
+
+
+
@@ -31,14 +30,14 @@
-
+
-
+
@@ -82,11 +81,15 @@
-
+
视频通话
-
+
+
+ 语音通话
+
+
语音通话
@@ -101,8 +104,14 @@
+
+
+
+
@@ -124,17 +133,18 @@
keyboardHeight: 322,
atUserIds: [],
recordText: "",
+ needScrollToBottom: false, // 需要滚动到底部
showMinIdx: 0 // 下标小于showMinIdx的消息不显示,否则可能很卡
}
},
methods: {
onRecorderInput() {
this.showRecord = true;
- this.switchChatTabBox('none',true);
+ this.switchChatTabBox('none', true);
},
onKeyboardInput() {
this.showRecord = false;
- this.switchChatTabBox('none',false);
+ this.switchChatTabBox('none', false);
},
onSendRecord(data) {
let msgInfo = {
@@ -161,26 +171,66 @@
// 滚动到底部
this.scrollToBottom();
this.isReceipt = false;
-
+
})
},
onRtCall(msgInfo) {
- if (msgInfo.type == this.$enums.MESSAGE_TYPE.RT_VOICE) {
- this.onVoiceCall();
- } else if (msgInfo.type == this.$enums.MESSAGE_TYPE.RT_VIDEO) {
- this.onVideoCall();
+ if (msgInfo.type == this.$enums.MESSAGE_TYPE.ACT_RT_VOICE) {
+ this.onPriviteVoice();
+ } else if (msgInfo.type == this.$enums.MESSAGE_TYPE.ACT_RT_VIDEO) {
+ this.onPriviteVideo();
}
},
- onVideoCall() {
+ onPriviteVideo() {
const friendInfo = encodeURIComponent(JSON.stringify(this.friend));
uni.navigateTo({
- url: `/pages/chat/chat-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));
uni.navigateTo({
- url: `/pages/chat/chat-video?mode=voice&friend=${friendInfo}&isHost=true`
+ url: `/pages/chat/chat-private-video?mode=voice&friend=${friendInfo}&isHost=true`
+ })
+ },
+ 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];
+ this.$refs.selBox.init(ids, ids);
+ this.$refs.selBox.open();
+ }
+ })
+ },
+ onInviteOk(ids) {
+ if(ids.length < 2){
+ return;
+ }
+ let users = [];
+ ids.forEach(id => {
+ let m = this.groupMembers.find(m => m.userId == id);
+ // 只取部分字段,压缩url长度
+ users.push({
+ id: m.userId,
+ nickName: m.aliasName,
+ headImage: m.headImage,
+ isCamera: false,
+ isMicroPhone: true
+ })
+ })
+ const groupId = this.group.id;
+ const inviterId = this.mine.id;
+ const userInfos = encodeURIComponent(JSON.stringify(users));
+ uni.navigateTo({
+ url: `/pages/chat/chat-group-video?groupId=${groupId}&isHost=true
+ &inviterId=${inviterId}&userInfos=${userInfos}`
})
},
moveChatToTop() {
@@ -302,13 +352,13 @@
});
},
- onShowEmoChatTab(){
+ onShowEmoChatTab() {
this.showRecord = false;
- this.switchChatTabBox('emo',true)
+ this.switchChatTabBox('emo', true)
},
- onShowToolsChatTab(){
+ onShowToolsChatTab() {
this.showRecord = false;
- this.switchChatTabBox('tools',true)
+ this.switchChatTabBox('tools', true)
},
switchChatTabBox(chatTabBox, hideKeyBoard) {
this.chatTabBox = chatTabBox;
@@ -496,11 +546,11 @@
});
},
onScrollToTop() {
- if(this.showMinIdx==0){
+ if (this.showMinIdx == 0) {
console.log("消息已滚动到顶部")
return;
}
-
+
// #ifndef H5
// 防止滚动条定格在顶部,不能一直往上滚
this.scrollToMsgIdx(this.showMinIdx);
@@ -541,7 +591,8 @@
});
},
readedMessage() {
- if(this.unreadCount == 0){
+ console.log("readedMessage")
+ if (this.unreadCount == 0) {
return;
}
let url = ""
@@ -642,7 +693,14 @@
messageSize: function(newSize, oldSize) {
// 接收到消息时滚动到底部
if (newSize > oldSize) {
- this.scrollToBottom();
+ console.log("messageSize",newSize,oldSize)
+ let pages = getCurrentPages();
+ let curPage = pages[pages.length-1].route;
+ if(curPage == "pages/chat/chat-box"){
+ this.scrollToBottom();
+ }else {
+ this.needScrollToBottom = true;
+ }
}
},
unreadCount: {
@@ -673,11 +731,16 @@
this.$store.commit("activeChat", options.chatIdx);
// 复位回执消息
this.isReceipt = false;
- // 页面滚到底部
- this.scrollToBottom();
},
onUnload() {
this.$store.commit("activeChat", -1);
+ },
+ onShow(){
+ if(this.needScrollToBottom){
+ // 页面滚到底部
+ this.scrollToBottom();
+ this.needScrollToBottom = false;
+ }
}
}
@@ -718,7 +781,6 @@
}
}
-
.chat-msg {
flex: 1;
padding: 0;
diff --git a/im-uniapp/pages/chat/chat-group-video.vue b/im-uniapp/pages/chat/chat-group-video.vue
new file mode 100644
index 0000000..de42480
--- /dev/null
+++ b/im-uniapp/pages/chat/chat-group-video.vue
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-uniapp/pages/chat/chat-video.vue b/im-uniapp/pages/chat/chat-private-video.vue
similarity index 80%
rename from im-uniapp/pages/chat/chat-video.vue
rename to im-uniapp/pages/chat/chat-private-video.vue
index ab8b2e7..b72d22c 100644
--- a/im-uniapp/pages/chat/chat-video.vue
+++ b/im-uniapp/pages/chat/chat-private-video.vue
@@ -1,6 +1,6 @@
-
-
+
+
@@ -20,16 +20,6 @@
onMessage(e) {
this.onWebviewMessage(e.detail.data[0]);
},
- onInsertMessage(msgInfo){
- let chat = {
- type: 'PRIVATE',
- targetId: this.friend.id,
- showName: this.friend.nickName,
- headImage: this.friend.headImage,
- };
- this.$store.commit("openChat",chat);
- this.$store.commit("insertMessage", msgInfo);
- },
onWebviewMessage(event) {
console.log("来自webview的消息:" + JSON.stringify(event))
switch (event.key) {
@@ -39,9 +29,6 @@
case "WV_CLOSE":
uni.navigateBack();
break;
- case "INSERT_MESSAGE":
- this.onInsertMessage(event.data);
- break;
}
},
sendMessageToWebView(key, message) {
@@ -72,20 +59,21 @@
// #endif
},
initUrl(){
- this.url = "/hybrid/html/index.html";
+ this.url = "/hybrid/html/rtc-private/index.html";
this.url += "?mode="+this.mode;
this.url += "&isHost="+this.isHost;
this.url += "&baseUrl="+UNI_APP.BASE_URL;
this.url += "&loginInfo="+JSON.stringify(uni.getStorageSync("loginInfo"));
this.url += "&userInfo="+JSON.stringify(this.$store.state.userStore.userInfo);
this.url += "&friend="+JSON.stringify(this.friend);
+ this.url += "&config=" + JSON.stringify(this.$store.state.configStore.webrtc);
},
},
onBackPress() {
this.sendMessageToWebView("NAV_BACK",{})
},
onLoad(options) {
- uni.$on('WS_RTC', msg => {
+ uni.$on('WS_RTC_PRIVATE', msg => {
// 推送给web-view处理
this.sendMessageToWebView("RTC_MESSAGE", msg);
})
@@ -104,7 +92,7 @@
this.initUrl();
},
onUnload() {
- uni.$off('WS_RTC')
+ uni.$off('WS_RTC_PRIVATE')
}
}
diff --git a/im-uniapp/pages/group/group.vue b/im-uniapp/pages/group/group.vue
index a215665..19ba5a8 100644
--- a/im-uniapp/pages/group/group.vue
+++ b/im-uniapp/pages/group/group.vue
@@ -1,5 +1,5 @@
-
+
@@ -19,10 +19,6 @@
-
-
-
-