提交 f64a291c 编写于 作者: huangwensu's avatar huangwensu

Merge branch 'dev-diagnosis-20210323' of...

Merge branch 'dev-diagnosis-20210323' of http://192.168.110.53/com.pica.cloud.education.frontend/pica-admin-consultation into dev-diagnosis-20210323
...@@ -1270,7 +1270,6 @@ ...@@ -1270,7 +1270,6 @@
"resolved": "http://192.168.110.93:4873/boom/-/boom-2.10.1.tgz", "resolved": "http://192.168.110.93:4873/boom/-/boom-2.10.1.tgz",
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"hoek": "2.x.x" "hoek": "2.x.x"
} }
...@@ -2735,8 +2734,7 @@ ...@@ -2735,8 +2734,7 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
...@@ -2757,14 +2755,12 @@ ...@@ -2757,14 +2755,12 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
...@@ -2779,20 +2775,17 @@ ...@@ -2779,20 +2775,17 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
...@@ -2909,8 +2902,7 @@ ...@@ -2909,8 +2902,7 @@
"inherits": { "inherits": {
"version": "2.0.4", "version": "2.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
...@@ -2922,7 +2914,6 @@ ...@@ -2922,7 +2914,6 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
...@@ -2937,7 +2928,6 @@ ...@@ -2937,7 +2928,6 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
...@@ -2945,14 +2935,12 @@ ...@@ -2945,14 +2935,12 @@
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.9.0", "version": "2.9.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
...@@ -2971,7 +2959,6 @@ ...@@ -2971,7 +2959,6 @@
"version": "0.5.3", "version": "0.5.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "^1.2.5" "minimist": "^1.2.5"
} }
...@@ -3033,8 +3020,7 @@ ...@@ -3033,8 +3020,7 @@
"npm-normalize-package-bin": { "npm-normalize-package-bin": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"npm-packlist": { "npm-packlist": {
"version": "1.4.8", "version": "1.4.8",
...@@ -3062,8 +3048,7 @@ ...@@ -3062,8 +3048,7 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
...@@ -3075,7 +3060,6 @@ ...@@ -3075,7 +3060,6 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
...@@ -3153,8 +3137,7 @@ ...@@ -3153,8 +3137,7 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
...@@ -3190,7 +3173,6 @@ ...@@ -3190,7 +3173,6 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
...@@ -3210,7 +3192,6 @@ ...@@ -3210,7 +3192,6 @@
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
...@@ -3254,14 +3235,12 @@ ...@@ -3254,14 +3235,12 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
} }
} }
}, },
...@@ -3622,8 +3601,7 @@ ...@@ -3622,8 +3601,7 @@
"version": "2.16.3", "version": "2.16.3",
"resolved": "http://192.168.110.93:4873/hoek/-/hoek-2.16.3.tgz", "resolved": "http://192.168.110.93:4873/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true, "dev": true
"optional": true
}, },
"home-or-tmp": { "home-or-tmp": {
"version": "2.0.0", "version": "2.0.0",
...@@ -8079,6 +8057,12 @@ ...@@ -8079,6 +8057,12 @@
"resolved": "http://192.168.110.93:4873/throttle-debounce/-/throttle-debounce-1.1.0.tgz", "resolved": "http://192.168.110.93:4873/throttle-debounce/-/throttle-debounce-1.1.0.tgz",
"integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==" "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg=="
}, },
"tim-js-sdk": {
"version": "2.10.1",
"resolved": "http://192.168.110.93:4873/tim-js-sdk/-/tim-js-sdk-2.10.1.tgz",
"integrity": "sha1-ZJRyjqdXXRJAE1o7oPPZsLYXRTE=",
"dev": true
},
"time-stamp": { "time-stamp": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "http://192.168.110.93:4873/time-stamp/-/time-stamp-2.2.0.tgz", "resolved": "http://192.168.110.93:4873/time-stamp/-/time-stamp-2.2.0.tgz",
...@@ -8187,6 +8171,12 @@ ...@@ -8187,6 +8171,12 @@
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
"dev": true "dev": true
}, },
"trtc-js-sdk": {
"version": "4.9.0",
"resolved": "http://192.168.110.93:4873/trtc-js-sdk/-/trtc-js-sdk-4.9.0.tgz",
"integrity": "sha1-A/Gy957d/zaSBX1+/Sc+6hCyvs8=",
"dev": true
},
"true-case-path": { "true-case-path": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "http://192.168.110.93:4873/true-case-path/-/true-case-path-1.0.3.tgz", "resolved": "http://192.168.110.93:4873/true-case-path/-/true-case-path-1.0.3.tgz",
......
<template>
<div class="diog-wrap" @click.stop="cancle" v-if="show">
<div class="diog-box">
<div class="diog-main" @click.stop="">
<div class="diog-message">
<div class="diog-left">
<img :src="warningImg" alt="" srcset="">
</div>
<div class="diog-right">
<p class="title">{{title}}</p>
</div>
</div>
<div class="btn">
<div @click.stop="confirm" class="confirm-btn">{{confirmTxt}}</div>
<div @click.stop="cancle" class="cancle-btn" v-if="cancleTxt">{{cancleTxt}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
warningImg: require('@/assets/image/live/warning.png'),
show: false,
title: '',
confirmTxt: '确定',
cancleTxt: '',
_promise: null
}
},
created() {},
methods: {
reset() {
this.title = ''
this.confirmTxt = '确定'
this.cancleTxt = ''
this._promise = null
},
init(obj={}) {
Object.assign(this,obj)
this.show = true
return new Promise((resolve,reject) => {
this._promise = {
resolve,
reject
};
})
},
async cancle() {
this.show = false
await this._promise.reject && this._promise.reject()
this.reset()
},
async confirm() {
this.show = false
await this._promise.resolve && this._promise.resolve()
this.reset()
},
hide() {
this.show = false
this.reset()
}
}
}
</script>
<style lang="scss" scoped>
.diog-wrap {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 3000;
.diog-box {
position: absolute;
top: 64px;
right: 0;
left: 255px;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.6);
.diog-main {
width:480px;
padding: 34px 32px 24px 34px;
box-sizing: border-box;
background:rgba(31,31,31,1);
box-shadow:0px 12px 48px 16px rgba(0,0,0,0.12),0px 9px 28px 0px rgba(0,0,0,0.2),0px 6px 16px -8px rgba(0,0,0,0.32);
border-radius:2px;
.diog-message {
display: flex;
.diog-left {
height: 21px;
width: 21px;
margin-right: 17px;
}
.diog-right {
.title {
font-size:16px;
font-family:PingFangSC-Medium,PingFang SC;
font-weight:500;
color:rgba(255,255,255,0.85);
line-height:24px;
}
}
}
.btn {
margin-top: 24px;
display: flex;
flex-direction: row-reverse;
.confirm-btn {
margin-left: 12px;
padding: 0 16px;
height:32px;
background:rgba(47,134,246,1);
border-radius:2px;
font-size:14px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(255,255,255,1);
line-height:32px;
text-align: center;
cursor: pointer;
}
.cancle-btn {
width:60px;
height:32px;
border-radius:2px;
border:1px solid rgba(255,255,255,0.2);
font-size:14px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(255,255,255,0.65);
line-height:32px;
text-align: center;
cursor: pointer;
}
}
}
}
}
</style>
...@@ -13,6 +13,8 @@ import vViewer from 'v-viewer'; ...@@ -13,6 +13,8 @@ import vViewer from 'v-viewer';
import 'viewerjs/dist/viewer.css'; import 'viewerjs/dist/viewer.css';
import ClipboardJS from 'clipboard'; import ClipboardJS from 'clipboard';
import '@/utils/directive'; import '@/utils/directive';
import TRTC from 'trtc-js-sdk';
import TIM from 'tim-js-sdk';
Vue.prototype.ClipboardJS = ClipboardJS; Vue.prototype.ClipboardJS = ClipboardJS;
Vue.use(vViewer, { Vue.use(vViewer, {
...@@ -47,6 +49,11 @@ const router = new VueRouter({ ...@@ -47,6 +49,11 @@ const router = new VueRouter({
} }
}) })
Vue.use(TRTC)
window.TRTC = TRTC
Vue.use(TIM)
window.TIM = TIM
// 加入混合 // 加入混合
Vue.mixin({ Vue.mixin({
...mixins ...mixins
......
export const IM_MESSAGE = {
PLEASE_ACTION: 100, // 请求连麦-------------------------------------------------------------嘉宾
CALL_ACTION: 101, // 连麦通知
CALL_ACCEPT: 1, // 主播同意嘉宾连麦----------------------------------------------------------主播
CALL_REFUSE: 0, // 主播拒绝嘉宾连麦----------------------------------------------------------主播
DOWN_ACTIONL: 102, //下麦通知
DOWN_COMMAND: 102, //让嘉宾下麦-------------------------------------------------------------主播
CUSTOM_ACTION: 301, //自定义通知
CUSTOM_COMMAND_MC: 333, //自己下麦----------------------------------------------------------嘉宾
CUSTOM_COMMAND_IN: 666, // 自已在房间-------------------------------------------------------嘉宾
CUSTOM_COMMAND_OUT: 999, // 离开房间--------------------------------------------------------嘉宾
CUSTOM_COMMAND_ON: 777, //上课--------------------------------------------------------------主播
CUSTOM_COMMAND_DOWN: 888, //下课------------------------------------------------------------主播
CUSTOM_COMMAND_DOWN_MC: 852, //关闭麦克风----------------------------------------------------主播
CUSTOM_COMMAND_DOWN_V: 851, // 关闭摄像头 ---------------------------------------------------主播
CUSTOM_COMMAND_HERE: 123, //嘉宾还在不在-----------------------------------------------------主播
CUSTOM_COMMAND_YES: 321, //嘉宾还在----------------------------------------------------------嘉宾
CUSTOM_COMMAND_INVITE: 741, //邀请嘉宾连麦----------------------------------------------------主播
CUSTOM_COMMAND_INVITE_OK: 742, //嘉宾同意连麦-------------------------------------------------嘉宾
CUSTOM_COMMAND_INVITE_NO: 743, //嘉宾拒绝连麦-------------------------------------------------嘉宾
CUSTOM_COMMAND_INVITE_EMPTY: 745, //主播告诉嘉宾有空位置--------------------------------------主播
CUSTOM_COMMAND_SPEAKER_ON: 201, //主播设置嘉宾为主讲--------------------------------------------主播
CUSTOM_COMMAND_SPEAKER_DOWN: 202, //主播取消嘉主讲----------------------------------------------主播
}
// 主播发送的消息-------------------------------------------------
export const imAgreeCall = () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CALL_ACTION + ',"accept":' + IM_MESSAGE.CALL_ACCEPT + ',"reason":"同意连麦"}', description: '', extension: '' }
return data
}
export const imRefuseCall = () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CALL_ACTION + ',"accept":' + IM_MESSAGE.CALL_REFUSE + ',"reason":"拒绝连麦"}', description: '', extension: '' }
return data
}
export const imCallDown = (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.DOWN_ACTIONL + ', "command":"' + IM_MESSAGE.DOWN_COMMAND + '", "accept":0,"message":"' + nick + '","reason":"下麦"}', description: '', extension: '' }
return data
}
export const imMcDown = (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ',"command": "' + IM_MESSAGE.CUSTOM_COMMAND_DOWN_MC + '","accept":0,"message":"' + nick + '","reason":"主播关闭了您的麦克风"}', description: '', extension: '' }
return data
}
export const imVDown = (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ',"command": "' + IM_MESSAGE.CUSTOM_COMMAND_DOWN_V + '","accept":0,"message":"' + nick + '","reason":"主播关闭了您的摄像头"}', description: '', extension: '' }
return data
}
export const imClassOn = () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_ON + '","accept":0,"reason":"上课啦","message":""}', description: '', extension: '' }
return data
}
export const imClassDown = () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_DOWN + '","accept":0,"reason":"下课啦","message":""}', description: '', extension: '' }
return data
}
export const imJbHere= () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_HERE + '","accept":0,"reason":"在么?","message":""}', description: '', extension: '' }
return data
}
export const imInviteCall = (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_INVITE + '","accept":0,"reason":"主播请您连麦","message":"' + nick + '"}', description: '', extension: '' }
return data
}
export const imInviteEmpty= (nick,replaceId) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_INVITE_EMPTY + '","accept":0,"reason":"有空位置了,可以连麦了","replaceId":"' + replaceId + '","message":"' + nick + '"}', description: '', extension: '' }
return data
}
export const imSetSpeaker= (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_SPEAKER_ON + '","accept":0,"reason":"主播设置您为主讲人","message":"' + nick + '"}', description: '', extension: '' }
return data
}
export const imCancleSpeaker = (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_SPEAKER_DOWN + '","accept":0,"reason":"主播取消您的主讲人身份","message":"' + nick + '"}', description: '', extension: '' }
return data
}
// 嘉宾发的消息----------------------------------------------------------------------
export const imPleaseCall = () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.PLEASE_ACTION + ',"accept":1,"reason":"申请连麦"}', description: '', extension: '' }
return data
}
export const imInhome = (nick, callStatus, pendCalling, faceUrl, loadingTime, isSpeaker) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ',"command":"' + IM_MESSAGE.CUSTOM_COMMAND_IN + '","accept":1,"reason":"我已经在房间了" ,"message":"' + nick + '","callStatus":"' + callStatus + '","pendCalling":"' + pendCalling + '","isSpeaker":"' + isSpeaker + '","loadingTime":"' + loadingTime + '","faceUrl":"' + faceUrl + '"}', description: '', extension: '' }
return data
}
export const imMcDownSelf = () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_MC + '","accept":0,"reason":"我下麦了","message":""}', description: '', extension: '' }
return data
}
export const imJbYes = () => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_YES + '","accept":0,"reason":"在的","message":""}', description: '', extension: '' }
return data
}
export const imInviteOk = (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_INVITE_OK + '","accept":0,"reason":"嘉宾接受主播连麦请求","message":"' + nick + '"}', description: '', extension: '' }
return data
}
export const imInviteNo = (nick) => {
let data = { data: '{"version":"","action":' + IM_MESSAGE.CUSTOM_ACTION + ', "command":"' + IM_MESSAGE.CUSTOM_COMMAND_INVITE_NO + '","accept":0,"reason":"嘉宾拒绝主播连麦请求","message":"' + nick + '"}', description: '', extension: '' }
return data
}
\ No newline at end of file
class WebIMObj {
constructor(params) {
this.accountModel = null;
this.imListener = null;
this.boardNotifyCallback = null;
this.compatSaas = null;
this.groupChatId = null;
this.groupBoardId = null;
this.xmlHttp = null;
this.tim = null;
this.callbackobj = null;
this.roomId = params.roomId
}
init(sdkAppId) {
var options = {
SDKAppID: sdkAppId // 接入时需要将0替换为您的即时通信 IM 应用的 SDKAppID
};
this.tim = window.TIM.create(options);
this.tim.setLogLevel(0); // 普通级别,日志量较多,接入时建议使用
this.tim.registerPlugin({
'cos-js-sdk': window.COS
}); // 注册 COS SDK 插件
}
login(accountModel) {
this.accountModel = accountModel;
this.uninitEvent();
this.initEvent();
return this.tim.login({
userID: String(accountModel.userId),
userSig: String(accountModel.userSig)
});
}
initEvent() {
this.tim.on(window.TIM.EVENT.MESSAGE_RECEIVED, this.onMessageReceived, this) // SDK 收到推送的单聊、群聊、群提示、群系统通知的新消息
// this.tim.on(window.TIM.EVENT.GROUP_SYSTEM_NOTICE_RECEIVED, this.onGroupSystemNoticeReceived, this) // SDK 收到新的群系统通知时触发
this.tim.on(window.TIM.EVENT.KICKED_OUT, this.onKickedOut, this) // 用户被踢下线时触发
}
uninitEvent() {
this.tim.off(window.TIM.EVENT.MESSAGE_RECEIVED, this.onMessageReceived) // SDK 收到推送的单聊、群聊、群提示、群系统通知的新消息
// this.tim.off(window.TIM.EVENT.GROUP_SYSTEM_NOTICE_RECEIVED, this.onGroupSystemNoticeReceived) // SDK 收到新的群系统通知时触发
this.tim.off(window.TIM.EVENT.KICKED_OUT, this.onKickedOut) // 用户被踢下线时触发
}
logout() {
this.uninitEvent();
return this.tim.logout();
}
/**
* 请以一下命名回调方法:
* 有成员进入onRecvJoinMessage
* 有成员退出onRecvOutMessage
* 收到C2C文本消息onRecvTextMessage
* 收到C2C自定义消息onRecvCustomMessage
* 收到群文本消息onRecvGroupTextMessage
* 收到群自定义消息onRecvGroupCustomMessage
* 所有消息onRecvMessage
* 被踢出群组onClassroomDestroy
*/
// 增加回调函数
addEvent(v) {
this.callbackobj = v
}
/**
* @classId 群组id
* @scene 场景
*/
createRoom(groupId) {
groupId = String(groupId);
let groupType = null;
// 创建聊天室
groupType = window.TIM.TYPES.GRP_AVCHATROOM;
var options = {
name: groupId,
groupID: groupId,
type: groupType,
joinOption: window.TIM.TYPES.JOIN_OPTIONS_FREE_ACCESS //自由加入
};
return this.tim.createGroup(options).then(() => { // 创建成功
return Promise.resolve();
}).catch(function () {
if (error.code == 10025) { // 群组 ID 已被使用,并且操作者为群主,可以直接使用。
return Promise.resolve();
} else {
// return Promise.reject(error);
return Promise.resolve();
}
});
}
/**
* 加入群
*/
joinRoom(groupId) {
groupId = String(groupId);
return this.tim.joinGroup({
groupID: groupId,
type: window.TIM.TYPES.GRP_AVCHATROOM,
}).then(res => {
switch (res.data.status) {
case window.TIM.TYPES.JOIN_STATUS_SUCCESS: // 加群成功
case window.TIM.TYPES.JOIN_STATUS_ALREADY_IN_GROUP: // 已经在群中
return Promise.resolve();
case window.TIM.TYPES.JOIN_STATUS_WAIT_APPROVAL: // 等待管理员同意,业务上认为失败
default:
return Promise.reject(res);
}
}, error => {
if (error.code == 10013) { // 被邀请加入的用户已经是群成员,也表示成功
return Promise.resolve();
} else if (error.code == -12) { // Join Group succeed; But the type of group is not AVChatRoom
return Promise.resolve();
}
return Promise.reject(error);
});
}
/**
* 销毁群组
*/
destroyGroup(groupId) {
groupId = String(groupId);
return this.tim.dismissGroup(groupId);
}
/**
* 退出群组
*/
quitGroup(groupId) {
let Id = String(groupId);
return this.tim.quitGroup(Id).then(res => {
return Promise.resolve();
}, error => {
// 群不存在 或者 不在群里了 或者 群id不合法(一般这种情况是课堂销毁了groupId被重置后发生)
if (error.code === 10010 || error.code === 10007 || error.code === 10015) {
return Promise.resolve();
} else if (error.code == 10009) { // 群主自己想退课堂
return Promise.resolve();
} else {
return Promise.reject(error);
}
});
}
onKickedOut(callback) {
callback && callback()
}
onGroupSystemNoticeReceived(event) {
const message = event.data.message; // 群系统通知的消息实例,详见 Message
const payload = message.payload;
switch (payload.operationType) {
case 4: // 被踢出群组
if (event.data.message.to == this.groupChatId) {
this.callbackobj && this.callbackobj.onClassroomDestroy && this.callbackobj.onClassroomDestroy()
}
break;
case 5: // 群组被解散
if (event.data.message.to == this.groupChatId) {
this.callbackobj && this.callbackobj.onClassroomDestroy && this.callbackobj.onClassroomDestroy()
}
break;
case 11: //群已被回收(全员接收)
if (event.data.message.to == this.groupChatId) {
this.callbackobj && this.callbackobj.onClassroomDestroy && this.callbackobj.onClassroomDestroy()
}
break;
case 255:
console.warn('系统消息255:::', event.data.message)
break;
}
}
onMessageReceived(event) {
let messages = event.data;
this.updateLiveCount();
messages.forEach((message) => {
// 群组消息
if (message.conversationType === window.TIM.TYPES.CONV_GROUP) {
if (message.to == this.roomId) { // 如果是当前群组
let elements = message.getElements();
if (elements.length) {
elements.forEach((element) => {
console.log(element.type);
if (element.type === 'TIMGroupTipElem') {
switch (element.content.operationType) {
// 有成员进入
case window.TIM.TYPES.GRP_TIP_MBR_JOIN:
this.callbackobj && this.callbackobj.onRecvJoinMessage && this.callbackobj.onRecvJoinMessage(element.content.userIDList, message.nick)
console.log('js::::有人进来了');
break;
// 有成员退出
case window.TIM.TYPES.GRP_TIP_MBR_QUIT:
console.log('js::::有人退出了');
this.callbackobj && this.callbackobj.onRecvOutMessage && this.callbackobj.onRecvOutMessage(element.content.userIDList, message.nick)
break;
}
} else if (element.type === 'TIMTextElem') {
// 有群组消息
console.log('js::::有群组消息');
this.callbackobj.setMessagesBack(message);
this.callbackobj && this.callbackobj.onRecvGroupTextMessage && this.callbackobj.onRecvGroupTextMessage(message.from, element.content.text, element.content.text.length, message.nick)
} else if (element.type === 'TIMCustomElem') {
// 有群自定义消息
if (element.content.extension === 'TXConferenceExt') {
// 对时消息过滤掉
} else {
console.log('js::::有群自定义消');
console.log(this.callbackobj);
this.callbackobj && this.callbackobj.onRecvGroupCustomMessage && this.callbackobj.onRecvGroupCustomMessage(message.from, element.content.data, element.content.data.length, message.nick)
}
}
});
}
} else {
// 其他群组消息忽略
}
} else if (message.conversationType === window.TIM.TYPES.CONV_C2C) { // C2C消息
let elements = message.getElements();
if (elements.length) {
elements.forEach((element) => {
if (element.type === 'TIMTextElem') {
// C2C文本消息
console.log('js:::: C2C文本消息');
console.log(message)
this.callbackobj && this.callbackobj.onRecvTextMessage && this.callbackobj.onRecvTextMessage(message.from, element.content.text, element.content.text.length, message.nick)
} else if (element.type === 'TIMCustomElem') {
// 自定义消息
console.log('js:::: C2C自定义消息');
console.log(message)
try {
this.callbackobj && this.callbackobj.onRecvCustomMessage && this.callbackobj.onRecvCustomMessage(message.from, message.payload.data, message.nick)
} catch (error) {
console.log('错了:', error)
}
}
});
}
}
});
}
updateLiveCount() {
this.tim.getGroupProfile({ groupID: this.roomId }).then(event => {
let gl = event.data.group;
// debugger
gl.memberNum && this.callbackobj.getImGroupMemberNum(gl.memberNum);
});
}
/**
* 发送C2C文本消息
*/
sendC2CTextMessage(userId, msg) {
let message = this.tim.createTextMessage({
to: userId,
conversationType: window.TIM.TYPES.CONV_C2C,
payload: {
text: msg
}
})
return this.tim.sendMessage(message).then(() => {
return Promise.resolve();
}, (error) => {
return Promise.reject(error);
});
}
/**
* 发送C2C自定义消息
*/
sendC2CCustomMessage(userId, msg) {
let message = this.tim.createCustomMessage({
to: userId,
conversationType: window.TIM.TYPES.CONV_C2C,
payload: {
data: msg.data,
description: msg.description,
extension: msg.extension
}
})
return this.tim.sendMessage(message).then(() => {
return Promise.resolve();
}, (error) => {
return Promise.reject(error);
});
}
/**
* 发送群文本消息
*/
sendGroupTextMessage(msg,id) {
let toGroupId = id;
let message = this.tim.createTextMessage({
to: this.roomId,
conversationType: window.TIM.TYPES.CONV_GROUP,
payload: {
text: msg
}
})
return this.tim.sendMessage(message).then(() => {
this.callbackobj.setMessagesBack(message);
return Promise.resolve();
}, (error) => {
return Promise.reject(error);
});
}
/**
* 发送群组自定义消息
*/
sendGroupCustomMessage(msg, id) {
let toGroupId = id;
let message = this.tim.createCustomMessage({
to: toGroupId,
conversationType: window.TIM.TYPES.CONV_GROUP,
payload: {
data: msg.data,
description: msg.description,
extension: msg.extension
}
})
return this.tim.sendMessage(message).then(() => {
return Promise.resolve();
}, (error) => {
return Promise.reject(error);
});
}
getGroup() {
return this.tim.getGroupProfile({
groupID: this.roomId}).then((p) => {
return Promise.resolve(p);
})
}
updateMyProfile(obj) {
return this.tim.updateMyProfile({
nick: obj.nick,
avatar: obj.avatar
}).then((p) => {
return Promise.resolve(p);
}).catch((error) => {
// 更新资料失败的相关信息
return Promise.reject(error);
})
}
_getXhr() {
if (this.xmlHttp) {
return this.xmlHttp;
}
let xmlHttp = null;
if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {}
}
}
this.xmlHttp = xmlHttp;
return xmlHttp;
}
}
export default WebIMObj;
/* 浏览器兼容提示 */
export const CHECK_BROWSER_TIPS = {
BROWSER_NOT_COMPATIBLE: '您的浏览器不支持直播功能!',
BROWSER_NOT_SUPPORT_SCREEN_SHARE: '您的浏览器不支持直播功能!'
};
export const setLgTenText = time => {
return time < 10 ? `0${time}` : time;
}
export const getLiveTimeText = (lt, name) => {
let hr = parseInt(lt / 1000 / 60 / 60); // 時
let min = parseInt((lt - hr * 1000 * 60 * 60) / 1000 / 60); // 分 ex: 90秒
let sec = parseInt((lt - min * 1000 * 60 - hr * 1000 * 60 * 60) / 1000); // 秒
sessionStorage.setItem(`TIME_${name}`, lt);
return `${setLgTenText(hr)}:${setLgTenText(min)}:${setLgTenText(sec)}`;
}
export const countDown = (endtime) => {
let nowtime = new Date(); //获取当前时间
let time = endtime- nowtime.getTime(); //距离结束时间的毫秒数
if (time > 0) {
let lefth = Math.floor(time / (1000 * 60 * 60) % 24); //计算小时数
let leftm = Math.floor(time / (1000 * 60) % 60); //计算分钟数
let lefts = Math.floor(time / 1000 % 60); //计算秒数
//返回倒计时的字符串
return `${setLgTenText(lefth)}:${setLgTenText(leftm)}:${setLgTenText(lefts)}`;
} else {
return '时间已到'
}
}
function listCompare(property, way) {
return function (obj1, obj2) {
var value1 = obj1[property];
var value2 = obj2[property];
return way ? value1 - value2 : value2 - value1; // 升序
}
}
// 参数为条件(如先排角色,后是通话中)
export const listSort = (one, two, three, four, arr) => {
let newList = [] // 新的列表
let callArr = [] // 正在通话的列表
let inviteArr = [] // 邀请中的列表
arr = JSON.parse(JSON.stringify(arr));
let h = arr.sort(listCompare(four))
let a = h.sort(listCompare(three))
let b = a.sort(listCompare(two))
newList = b.sort(listCompare(one, 1))
newList.forEach(item => {
if (item.isCalling) {
callArr.push(item)
}
if (item.inviteCall) {
inviteArr.push(item)
}
});
return {
newList,
callArr,
inviteArr
}
}
export const isWeixin = () => {
let ua = navigator.userAgent.toLowerCase();
return ua.indexOf('micromessenger') != -1
}
export const getBroswer = () => {
let sys = {};
let ua = navigator.userAgent.toLowerCase();
let s;
(s = ua.match(/edge\/([\d.]+)/)) ? sys.edge = s[1] :
(s = ua.match(/rv:([\d.]+)\) like gecko/)) ? sys.ie = s[1] :
(s = ua.match(/msie ([\d.]+)/)) ? sys.ie = s[1] :
(s = ua.match(/firefox\/([\d.]+)/)) ? sys.firefox = s[1] :
(s = ua.match(/chrome\/([\d.]+)/)) ? sys.chrome = s[1] :
(s = ua.match(/opera.([\d.]+)/)) ? sys.opera = s[1] :
(s = ua.match(/version\/([\d.]+).*safari/)) ? sys.safari = s[1] : 0;
if (sys.edge) return { broswer: "Edge", version: sys.edge };
if (sys.ie) return { broswer: "IE", version: sys.ie };
if (sys.firefox) return { broswer: "Firefox", version: sys.firefox };
if (sys.chrome) return { broswer: "Chrome", version: sys.chrome };
if (sys.opera) return { broswer: "Opera", version: sys.opera };
if (sys.safari) return { broswer: "Safari", version: sys.safari };
return { broswer: "", version: "0" };
}
\ No newline at end of file
import WebIMObj from '@/utils/live/WebIM.js'
module.exports = {
data() {
return {
}
},
created() {
},
methods: {
// 初始化Im
initIM() {
this.WebIM = new WebIMObj({roomId: this.roomId})
this.WebIM.init(this.sdkAppId)
this.WebIM.tim.on(TIM.EVENT.SDK_READY, this.onTimSdkReady)
this.WebIM.tim.on(TIM.EVENT.SDK_NOT_READY, this.onTimSdkNotReady)
this.IMlogin()
},
// TIM SDK 准备完成 注意:这时需要将vue实例传入
onTimSdkReady() {
this.IMJion();
console.log('TIM SDK 准备完成')
// this.createRoom();
// 将vue实例传入,方便消息回调
this.WebIM.addEvent(this)
},
// SDK 准备失败
onTimSdkNotReady(event) {
console.log('TIM SDK 准备失败 => ', event)
},
// Im登录
IMlogin() {
this.WebIM.login({
userId: this.userId,
userSig: this.userSig
}).then(() => {
// this.IMJion();
}, error => {
console.log('抱歉登录失败')
})
},
// WebIM创建聊天房间
createRoom() {
this.WebIM.createRoom(this.roomId).then(() => {
// 创建课堂-end
console.log('创建room成功')
this.IMJion()
}, error => {
console.log('创建room失败')
this.IMJion()
})
},
// WebIM加入房间
IMJion() {
this.WebIM.joinRoom(this.roomId).then(res => {
this.addFriends(this.nickShow)
this.joined = true
this.WebIM.updateMyProfile({
nick: this.nick,
avatar: this.faceUrl
}).then(() => {
console.log('im 个人信息更新成功');
}).catch((errr) => {
console.log('更新资料失败', err)
})
}).catch((err) => {
console.log('加入房间失败', err)
})
},
// 退出
leaveIm() {
vm.WebIM.quitGroup(vm.roomId)
},
//所有消息
onRecvMessage(msg) {
},
// 被踢出房间
onClassroomDestroy() {
},
// WebIm退出
quitClassroom() {
vm.WebIM.quitGroup().then(() => {
}).catch((error) => {
})
},
//销毁WebIm
destroyClassroom(classId, callback) {
vm.WebIM.destroyGroup(classId).then(data => {
// vm.ticWebRTC && vm.ticWebRTC.quit()
}).catch(error => {
})
},
}
}
\ No newline at end of file
class RtcClient {
constructor(options) {
this.sdkAppId_ = options.sdkAppId;
this.userId_ = options.userId;
this.userSig_ = options.userSig;
this.roomId_ = options.roomId;
this.role = options.role; // 用户的角色主播还是观众(这里的指的是trtc定义的anchor和audience)
this.nick = options.nick;
this.vueInstance = options.vueInstance;
this.isJoined_ = false;
this.isPublished_ = false;
this.isAudioMuted = false;
this.isVideoMuted = false;
this.localStream_ = null;
this.remoteStreams_ = [];
this.members_ = new Map();
this.inmembers_ = new Map();
this.viewslist = [];
this.isPushing = 0;
try {
this.client_ = TRTC.createClient({
mode: 'live',
sdkAppId: this.sdkAppId_,
userId: this.userId_,
userSig: this.userSig_
});
this.handleEvents();
} catch (error) {
console.log('创建client失败', error)
}
}
// 加入房间
async join() {
if (this.isJoined_) {
// alert('抱歉,您已经在房间里面为了')
return;
}
try {
await this.client_.join({
roomId: this.roomId_,
role: this.role
});
console.log('加入房间trtc成功');
this.isJoined_ = true;
// 不要指定cameraId/microphoneId以避免过度约束
this.localStream_ = TRTC.createStream({
audio: true,
video: false,
userId: this.userId_,
mirror: true
});
this.startRTC()
} catch (e) {
console.error('加入房间失败 ' + e);
}
}
// 打开摄像头
async startRTC() {
// 连麦观众角色切换为主播,开始推流
// 设置视频分辨率等参数
this.localStream_.setVideoProfile({
width: this.vueInstance.viedoParams.webVideoWidth,
height: this.vueInstance.viedoParams.webVideoHeight,
frameRate: this.vueInstance.viedoParams.webVideoFramerate,
bitrate: this.vueInstance.viedoParams.webVideoBitrate /* kpbs */
});
// 避免重复开摄像头
this.stopPush()
}
// 设置本地流
async setLocalVideo() {
if (!this.isJoined_) {
console.warn('请先加入房间');
return;
}
if (this.isPublished_) {
console.warn('您已经发布过了');
return;
}
try {
this.localStream_.initialize().catch(error => {
this.vueInstance.$message({
message: '打开设备失败,请检查您的设备!',
type: 'error'
});
console.error('failed initialize localStream ' + error);
}).then(() => {
// 本地流在主播放器上播放,并且插入到一个关联的box中
// var localVideoWrapEl = document.getElementById('ask');
// this.localStream_.play(localVideoWrapEl, {
// muted: true
// });
//主播直接推流
if (this.role == 'anchor') {
this.publish()
}
}).catch(error => {
this.vueInstance.$message({
message: '麦克风打开失败!',
type: 'error'
});
});
} catch (e) {
this.isPublished_ = false;
}
this.isPublished_ = true;
}
//发布本地流
async publish() {
this.client_ && this.client_.publish(this.localStream_).then(() => {
console.log('本地流发布成功')
this.isPublished_ = true;
// 手动将麦克风打开
this.unmuteLocalAudio()
this.vueInstance.isMicOn = true
}).catch(error => {
console.log('本地流发布失败')
this.isPublished_ = false;
});
}
/**
* 为避免重复推流,先结束当前推流
*/
async stopPush() {
if (this.localStream_ && this.isPublished_ && this.isJoined_) {
this.client_.unpublish(this.localStream_).then(() => {
this.isPublished_ = false;
this.localStream_.stop();
this.setLocalVideo()
});
} else {
this.setLocalVideo()
}
}
// 退出
async leave() {
if (!this.isJoined_) {
console.warn('leave() - please join() firstly');
return;
}
// 如果是正在发布流,就先停止流
await this.unpublish();
// 离开房间
await this.client_.leave();
if (this.localStream_) {
this.localStream_.stop();
this.localStream_.close();
this.localStream_ = null;
}
this.isJoined_ = false;
}
// 退出本地流
async unpublish() {
if (!this.isJoined_) {
console.warn('unpublish() - please join() firstly');
return;
}
if (!this.isPublished_) {
console.warn('RtcClient.unpublish() called but not published yet');
return;
}
await this.client_.unpublish(this.localStream_);
this.isPublished_ = false;
}
//禁用音频轨道
//对于本地流,调用该方法会停止发送音频,远端会触发 Client.on('mute-audio') 事件。
//对于远端流,调用该方法会停止播放音频,但是仍然接收音频数据。
muteLocalAudio() {
this.localStream_.muteAudio();
}
//启用音频轨道
//对于本地流,调用该方法会触发远端 Client.on('unmute-audio') 事件。
//音频轨道默认是开启的,若你调用 muteAudio() 后可用该方法重新启用音频。
unmuteLocalAudio() {
this.localStream_.unmuteAudio();
}
// 禁用视频轨道
// 对于本地流,调用该方法会停止发送视频,远端会触发 Client.on('mute-video') 事件。
// 如果视频是从摄像头采集,此时摄像头灯仍然是亮着的。若想完全禁用视频轨道(即关闭摄像头) ,
// 可以使用 removeTrack() 删除视频轨道然后调用 MediaStreamTrack.stop() 关闭视频轨道(关闭摄像头)。
// 对于远端流,调用该方法会停止播放视频,但是仍然接收视频数据.
muteLocalVideo() {
this.localStream_.muteVideo();
}
//恢复播放音视频
//在某些版本浏览器上移动传入 play() 的 div 容器可能会导致音视频播放器进入 ‘PAUSED’ 状态,此时 需要调用该接口恢复播放。
//由于浏览器自动播放策略的限制,在 play() 返回 PLAY_NOT_ALLOWED 错误后需要引导用户通过手势 调用该接口恢复播放
resumeStreams() {
this.localStream_.resume();
for (let stream of this.remoteStreams_) {
stream.resume();
}
}
// client的一些事件监听
handleEvents() {
// 报错
this.client_.on('error', err => {
console.log('client 报错了--------------------------------------------------------------')
console.log(err)
// alert(err);
window.onbeforeunload = null
location.reload();
});
// 房间被解散了
this.client_.on('client-banned', err => {
console.log('房间被解散了');
});
// 当一个远程同伴(必须推流)进入房间时触发
this.client_.on('peer-join', evt => {
const userId = evt.userId;
console.log('有远程同伴进入房间:', userId)
});
// 当远处的同伴离开房间时触发(删减好友列表)
this.client_.on('peer-leave', evt => {
const userId = evt.userId;
console.log('有远程同伴离开房间:' + userId);
this.remove(userId)
});
// 在添加远程流时触发
this.client_.on('stream-added', evt => {
const remoteStream = evt.stream;
// 获取流的StreamId
const id = remoteStream.getId();
// 获取UserId
const userId = remoteStream.getUserId();
this.members_.set(userId, remoteStream);
this.inmembers_.set(userId, remoteStream);
console.log(`remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`);
// 我们订阅远端的流
console.log('subscribe to this remote stream');
this.client_.subscribe(remoteStream);
});
// 在订阅远程流时触发
this.client_.on('stream-subscribed', evt => {
const remoteStream = evt.stream;
const id = remoteStream.getId();
const uid = remoteStream.userId_;
this.remoteStreams_.push(remoteStream);
remoteStream.on('player-state-changed', event => {
console.log(`${event.type} player is ${event.state}`);
// 远端流是播放还是暂停状态(显示按钮等画面不同)
if (event.type == 'video' && event.state == 'STOPPED') {
console.log('远端流为停止');
this.changeView(id, 'mask', true)
}
if (event.type == 'video' && event.state == 'PAUSED') {
console.log('远端流为暂停');
this.changeView(id, 'mask', true)
}
if (event.type == 'video' && event.state == 'PLAYING') {
console.log('远端流为播放');
this.changeView(id, 'mask', false)
}
});
// 避免重复加载
for (let i = 0; i < this.viewslist.length; i++) {
if (this.viewslist[i] && uid == this.viewslist[i].userId) {
return
}
}
let isMask = false
// this.viewslist.push({ id: id, userId: uid, nick: uid, mask: isMask, vioce: true })
this.add(id, uid, isMask)
this.vueInstance.addNewMember(uid)
setTimeout(() => {
remoteStream.play(id);
if (!remoteStream.hasVideo()) {
this.changeView(id, 'mask', true)
}
}, 1000)
});
// 当远程流被移除时触发
this.client_.on('stream-removed', evt => {
const remoteStream = evt.stream;
const id = remoteStream.getId();
const uid = remoteStream.userId_;
console.log('远程流被移除时触发');
// 停止播放并删除相应<video>标签
remoteStream.stop();
this.inmembers_.delete(uid)
this.remoteStreams_ = this.remoteStreams_.filter(stream => {
return stream.getId() !== id;
});
this.remove(uid)
});
// 流更新
this.client_.on('stream-updated', evt => {
console.log('=========流更新========stream-updated===================');
console.log(evt);
const remoteStream = evt.stream;
let uid = this.getUidByStreamId(remoteStream.getId());
// remoteStream.hasVideo() // 是否有视频轨道
// remoteStream.hasAudio() //是否有音轨道
// remoteStream.getType() // 主要用于判断一个远端流是主音视频流还是辅路视频流,辅路视频流通常是一个屏幕分享流。
console.log('remoteStream ID: ' + remoteStream.getId() + ' was updated hasAudio: ' +
remoteStream.hasAudio() + ' hasVideo: ' + remoteStream.hasVideo());
});
// 关闭音轨道
this.client_.on('mute-audio', evt => {
this.changeView(evt.userId, 'vioce', false)
console.log(evt.userId + '关闭麦克风============================================');
});
// 打开音轨道
this.client_.on('unmute-audio', evt => {
this.changeView(evt.userId, 'vioce', true)
if (evt.type == 'audio' && evt.state == 'PLAYING' && this.vueInstance.type == 1) {
this.changeView(evt.userId, 'mask', true)
}
});
//关闭视频轨道
this.client_.on('mute-video', evt => {
console.log(evt.userId + '关闭 video==============================================');
this.changeView(evt.userId, 'mask', true);
});
// 打开视频轨道
this.client_.on('unmute-video', evt => {
console.log(evt.userId + '打开 video=============================================');
if (this.members_.get(evt.userId)) {
this.changeView(evt.userId, 'mask', false);
}
});
}
getUidByStreamId(streamId) {
for (let [uid, stream] of this.members_) {
if (stream.getId() == streamId) {
return uid;
}
}
}
// 移除视频数组
remove(userId) {
for(let i = 0; i < this.viewslist.length; i++) {
if (this.viewslist[i] && this.viewslist[i].userId == userId) {
this.viewslist.splice(i, 1)
}
}
this.vueInstance.removeMember(userId)
};
// 改变视频数组属性
changeView(id,attr,val) {
for (let i = 0; i < this.viewslist.length; i++) {
if (this.viewslist[i] && (this.viewslist[i].userId == id || this.viewslist[i].id == id)) {
this.viewslist[i][attr] = val
}
}
}
add(id, uid, isMask) {
this.vueInstance.memberList.forEach((ele,index) => {
if (ele.id == uid) {
this.viewslist[index] = { id: id, userId: uid, nick: uid, mask: isMask, vioce: true }
}
});
}
}
export default RtcClient
\ No newline at end of file
class ShareClient {
constructor(options) {
this.sdkAppId_ = options.sdkAppId;
this.userId_ = options.userId;
this.userSig_ = options.userSig;
this.roomId_ = options.roomId;
this.vueInstance = options.vueInstance;; //vue实例
this.isJoined_ = false;
this.isPublished_ = false;
this.localStream_ = null;
this.client_ = TRTC.createClient({
mode: 'live',
sdkAppId: this.sdkAppId_,
userId: this.userId_,
userSig: this.userSig_
});
//设置是否默认接收远端流。该方法可在 join() 调用前使用,若在进房后调用,会接收不到后续进房的远端用户音视频流。
this.client_.setDefaultMuteRemoteStreams(true);
this.handleEvents();
}
async join() {
// 如果已经加入时报错
if (this.isJoined_) {
// alert('共享失败,请稍后重试');
this.vueInstance.$message({
message: '共享失败,请稍后重试!',
type: 'error'
});
return;
}
try {
await this.client_.join({
roomId: this.roomId_
});
console.log('共享房间进入成功');
this.isJoined_ = true;
// 进入房间就创建共享本地流
this.localStream_ = TRTC.createStream({
// disable audio as RtcClient already enable audio
audio: false,
// enable screen share
screen: true,
userId: this.userId_,
});
// 设置视频分辨率等参数
this.localStream_.setScreenProfile({
width: this.vueInstance.viedoParams.screenProfileWidth,
height: this.vueInstance.viedoParams.screenProfileHeight,
frameRate: this.vueInstance.viedoParams.screenProfileFramerate,
bitrate: this.vueInstance.viedoParams.screenProfileBitrate /* kbps */
});
await this.publish()
return Promise.resolve();
} catch (e) {
console.error('ShareClient join room failed! ' + e);
}
}
// 发布本地流
async publish() {
if (!this.isJoined_) {
console.warn('共享屏幕请加入房间');
return;
}
if (this.isPublished_) {
console.warn('共享屏幕已经发布');
return;
}
try {
await this.stopPush()
try {
await this.localStream_.initialize()
} catch (error) {
throw new Error(error)
}
if (!this.vueInstance.canshare) {
this.vueInstance.$message({
message: '您当前不是主讲人,无屏幕分享权限!',
type: 'error'
});
this.leave();
return
}
// 监听播放状态 'PLAYING':开始播放 'PAUSED':暂停播放
this.localStream_.on('player-state-changed', event => {
console.log(`local stream ${event.type} player is ${event.state}`);
});
// // 监听屏幕分享结束
this.localStream_.on('screen-sharing-stopped', event => {
console.log('共享结束');
this.leave();
});
this.isPublished_ = true;
//创建共享屏幕div
var localVideoWrapEl = document.getElementById('share_video');
if (!localVideoWrapEl) {
localVideoWrapEl = document.createElement('div');
localVideoWrapEl.id = 'share_video';
document.querySelector("#paint_box").insertBefore(localVideoWrapEl, null);
}
if (localVideoWrapEl) {
// 本地流播放
await this.localStream_.play(localVideoWrapEl, {
muted: true,
objectFit: 'contain'
});
await this.client_.publish(this.localStream_);
this.showShare()
}
} catch (e) {
console.log('用户取消');
this.leave();
}
}
/**
* 结束推流
*/
async stopPush() {
if (this.localStream_ && this.isPublished_ && this.isJoined_) {
await this.client_.unpublish(this.localStream_);
this.isPublished_ = false;
if (document.getElementById('share_video')) {
document.getElementById('share_video').remove();
}
this.localStream_.close();
}
}
// 退出
async leave() {
if (!this.isJoined_) {
console.warn('leave() - please join() firstly');
return;
}
// 如果是正在发布流,就先停止流
if (this.isPublished_) {
await this.client_.unpublish(this.localStream_);
this.isPublished_ = false;
}
await this.client_.leave();
if (this.localStream_) {
this.localStream_.close();
this.localStream_ = null;
}
this.isShare = false;
this.isJoined_ = false;
this.hideShare()
}
handleEvents() {
this.client_.on('error', err => {
console.error(err);
this.leave();
// alert(err);
});
this.client_.on('client-banned', err => {
this.leave();
console.error('client has been banned for ' + err);
});
// 嘉宾加入房间时触发
this.client_.on('peer-join', evt => {
const userId = evt.userId;
console.log('嘉宾加入房间时触发 ' + userId);
});
// 嘉宾离开房间时触发
this.client_.on('peer-leave', evt => {
const userId = evt.userId;
console.log('嘉宾离开房间时触发' + userId);
});
// 在添加远程流时触发
this.client_.on('stream-added', evt => {
const remoteStream = evt.stream;
const id = remoteStream.getId();
const userId = remoteStream.getUserId();
console.log(`remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`);
console.log(' 在添加远程流时触发');
});
// 在订阅远程流时触发
this.client_.on('stream-subscribed', evt => {
const uid = evt.userId;
const remoteStream = evt.stream;
const id = remoteStream.getId();
remoteStream.on('player-state-changed', event => {
console.log(`${event.type} player is ${event.state}`);
});
console.log('在订阅远程流时触发');
});
// 当远程流被移除时触发, e.g. the remote user called Client.unpublish()
this.client_.on('stream-removed', evt => {
const remoteStream = evt.stream;
const id = remoteStream.getId();
console.log(`stream-removed ID: ${id} type: ${remoteStream.getType()}`);
console.log('当远程流被移除时触发');
});
// 当远程流被更新时触发
this.client_.on('stream-updated', evt => {
const remoteStream = evt.stream;
console.log('当远程流被更新时触发');
});
this.client_.on('mute-audio', evt => {
console.log(evt.userId + ' mute audio');
});
this.client_.on('unmute-audio', evt => {
console.log(evt.userId + ' unmute audio');
});
this.client_.on('mute-video', evt => {
console.log(evt.userId + ' mute video');
});
this.client_.on('unmute-video', evt => {
console.log(evt.userId + ' unmute video');
});
}
// 显示分享屏幕div
showShare() {
if (this.vueInstance) {
this.vueInstance.isShare = true;
}
}
// 隐藏分享屏幕div
hideShare() {
if (this.vueInstance) {
this.vueInstance.isShare = false;
}
}
}
export default ShareClient
\ No newline at end of file
<template> <template>
<div></div> <div class="livebox">
<div class="top">
<div class="top-left">
<h1 class="title">{{title}}</h1>
<div class="time-message">
<p>设定时长: 30分钟</p>
<p>总时长: {{useTime}}</p>
<p>剩余时长: {{loseTime}}</p>
</div>
</div>
<div class="top-right">
<img :src="isMicOn ? voiceSmallImg : voiceCloseImg" alt="" srcset="" class="icon" @click="taggleM">
<div class="out" @click="leave">退出</div>
</div>
</div>
<div class="main">
<div class="viedo-wrapper" v-for="(item,index) of memberList" :key="item.id">
<div class="text">
<p>{{item.role == 1 ? '问诊医生' : '接诊医生'}} {{item.name}}</p>
</div>
<div class="viedo">
<div :id="rtc.viewslist[index].id" v-if="rtc && rtc.viewslist[index]">
</div>
<!-- 用户声音icon -->
<img :src="rtc && rtc.viewslist[index] && rtc.viewslist[index].vioce ? voiceSmallImg : voiceCloseImg" alt="" srcset="" class="user-icon" v-if="item.status == 2">
<img :src="setImg(item.status)" alt="" srcset="" class="video-icon" v-else>
<div class="mask" v-if="rtc && rtc.viewslist[index] && rtc.viewslist[index].mask">
<img :src="type == 1 ? voiceImg : noCameraImg" alt="" :class="{'vocie': type == 1}">
</div>
</div>
<div class="time">
{{
showText(item.status, item.role)
}}
</div>
</div>
</div>
<div class="close" @click="overFn">
结束会话
</div>
<alert ref='alert'></alert>
</div>
</template> </template>
<script> <script>
import RtcClient from '@/utils/live/rtc-client.js'
import { getLiveTimeText, countDown, getBroswer } from '@/utils/live'
import { openLoading, closeLoading } from "@/utils/utils";
import alert from '@/components/common/alert.vue'
export default { export default {
components: {
alert
},
data() {
return {
closeCallImg: require('../../../assets/image/live/close-call.png'),
voiceCloseImg: require('../../../assets/image/live/voice-close.png'),
voiceSmallImg: require('../../../assets/image/live/voice-small.png'),
voiceImg: require('../../../assets/image/live/voice.png'),
waitingCallImg: require('../../../assets/image/live/waiting-call.png'),
noCameraImg: require('../../../assets/image/live/no-camera.png'),
roleAnchor: 'anchor', // 主播
rtc: null,
roomId: 100000437,
sdkAppId: 1400408299,
userSig: "eJyrVgrxCdZLrSjILEpVsjI2NDU2MzAw0AGLlqUWKVkpGekZKEH4xSnZiQUFmSlKVoYmBgYmBhZGlpYQmcyU1LySzLRMsAZDAwgwtNA1BEEwx8TYHGZKZjpQka*HZ1igQYB2UGZoRkGJSWFOeqCzRZRTSKhvsKtZYnG6T355Rb6-U2VRer4tVGNJZi7QiYZmhmaWlhbmJka1APpwNHs_",
userId: "1000000018-1-1-100000437",
viedoParams: null,
isMicOn: false,
askTime: 0, // 问诊医生接入时长
answerTime: 0, // 接诊医生接入时长
useTime: 0, // 问诊时长
loseTime: 0, // 问诊剩余时长
askTimeFn:null,
answerTimeFn:null,
useTimeFn:null,
loseTimeFn:null,
type: 2, // 1: 语音 2: 视频
startTime: 1617784742,
endTime: 1617791942000,
memberList: [
{
accId: '',
avatarImageUrl: '',
id: '1000005449-1-2-100000436',
name: '',
role: 1, //在当前群组中的角色 1: 问诊医生 2: 接诊医生 3: 居民 4: 其他
type: '',
status: 1, //等待连接还是已经下麦 1为等待 2为进行中 3为下麦
},
{
accId: '',
avatarImageUrl: '',
id: '1000005449-1-2-100000437',
name: '',
role: 2, //在当前群组中的角色 1: 问诊医生 2: 接诊医生 3: 居民 4: 其他
type: '',
status: 1
}
]
}
},
async created() {
openLoading(this)
this.getViedoParams()
// await this.getInfo()
// await this.getAppId()
// await this.getSing()
// await this.clientLogin()
},
computed: {
title(){
return this.type == 1 ? '音频问诊' : '视频问诊'
}
},
methods:{
getInfo() {
let url = `/team-controller/team/detail`;
this.GET(url).then(res => {
if (res.code == "000000") {
}
});
},
// 获取AppId
getAppId() {
let req = {
}
this.GET("coupler/app/trtc/sdkappid", req)
.then(res => {
if (res.code == "000000") {
this.sdkAppId = res.data.sdkAppId
}else if (res.code == '200006' || res.code == '200000') {
if(this.rtc) {
this.leave()
}
this.$router.push(`/login?id=${this.id}`)
}
})
.catch((err) => {
console.log(err);
})
},
// 获取签名
getSing() {
let req = {
sdkAppId: this.sdkAppId,
userId: this.userId
}
this.POST("coupler/usersig/trtc", req).then(res => {
// closeLoading(vm);
if (res.code == "000000") {
this.userSig = res.data.userSig
} else if (res.code == '200006' || res.code == '200000') {
closeLoading(this)
if(this.rtc) {
this.leave()
}
this.$router.go(-1)
}
})
.catch((err) => {
console.log(err);
})
},
clientLogin() {
// 创建trtcClient
let obj = {
roomId: this.roomId,
role: this.roleAnchor,
sdkAppId: this.sdkAppId,
userId: this.userId,
userSig: this.userSig,
vueInstance: this
}
this.rtc = new RtcClient(obj)
this.$nextTick(() => {
this.$refs.alert.init({
confirmTxt: '我知道了',
title: `为了更好的体验请保证您输出设备的正常使用`
})
.then(() => {
this.rtc.join()
this.ispending()
})
.catch((err) => {
this.rtc.join()
})
})
},
// 获取视频参数
getViedoParams() {
this.GET('/coupler/app/config/push/stream/params').then((res) => {
if (res.code == '000000') {
this.viedoParams = res.data
} else if (res.code == '200006' || res.code == '200000') {
closeLoading(this)
if (this.rtc) {
this.leave()
}
}
}).catch((err) => {
console.log('获取视频参数数据失败')
})
},
// 切换话筒
taggleM() {
if (this.isMicOn) {
this.muteLocalAudio()
} else {
this.unmuteLocalAudio()
}
this.isMicOn = !this.isMicOn
},
// 关闭mc
muteLocalAudio() {
this.rtc.muteLocalAudio()
},
// 打开mc
unmuteLocalAudio() {
this.rtc.unmuteLocalAudio()
},
// 显示文案
showText(status,role) {
let text = ''
switch (status) {
case 1:
text = '呼叫中'
break
case 3:
text = '已离线'
break;
default:
text = `已接入: ${ role == 1 ? this.askTime : this.answerTime}`
break
}
return text
},
//设置图像 1为等待 2为进行中 3为下麦
setImg(status) {
let img = null
switch (status) {
case 1:
img = this.waitingCallImg
break
case 3:
img = this.closeCallImg
break;
default:
img = this.closeCallImg
break
}
return img
},
// 用户上线
addNewMember(id) {
this.memberList.forEach(item => {
if (item.id == id) {
item.status = 2
this.setTime(item.role)
}
})
},
// 用户下线
removeMember(id) {
this.memberList.forEach(item => {
if (item.id == id) {
item.status = 3
}
})
},
//设置进行时长 1表示为问诊 2为接诊 3为问诊开始 4为问诊结束
setTime(flag) {
let text = ''
switch (flag) {
case 1:
text = 'askTime'
break
case 2:
text = 'answerTime'
break
case 3:
text = 'useTime'
break
case 4:
text = 'loseTime'
break
default:
break
}
let t = 0
let liveTime = Number(sessionStorage.getItem(`TIME_${text}`) || t)
this[`${text}fn`] = setInterval(() => {
liveTime += 1000
this[text] = getLiveTimeText(liveTime,text)
}, 1000);
},
// 问诊是否进行中
ispending() {
if (this.startTime < new Date() < this.endTime) {
this.setTime(3)
this.loseTimeFn = setInterval(() => {
this.loseTime = countDown(this.endTime)
}, 1000);
} else if (new Date() < this.startTime){
let t = setInterval(() => {
if (new Date() > this.startTime) {
clearInterval(t)
this.ispending()
}
}, 1000);
}
},
// 检查是否为chrome
checkChrome() {
return getBroswer().broswer=='Chrome'
},
// client离开房间
leave() {
this.rtc.leave()
},
// 结束会话
overFn() {
let url = `/admin/diagnose/endCall/{diagnoseLogId}`;
this.POST(url).then(res => {
if (res.code == "000000") {
}
});
}
},
destroyed() {
clearInterval(this.askTimeFn)
clearInterval(this.answerTimeFn)
clearInterval(this.useTimeFn)
clearInterval(this.loseTimeFn)
}
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.livebox {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #000000;
.top {
display: flex;
margin-top: 5%;
justify-content: space-between;
.top-left {
margin-left: 24px;
.title {
width: 72px;
height: 22px;
font-size: 18px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 700;
color: #FFFFFF;
line-height: 22px;
}
.time-message {
margin-top: 20px;
display: flex;
height: 20px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 20px;
p {
margin-right: 20px;
}
}
}
.top-right {
margin: 15px 25px 0 0;
display: flex;
.icon {
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
}
.out {
width: 102px;
height: 32px;
border-radius: 2px;
border: 1px solid #FFFFFF;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
text-align: center;
line-height: 32px;
margin-left: 20px;
cursor: pointer;
}
}
}
.main {
margin-top: 24px;
display: flex;
flex: 1;
.viedo-wrapper {
display: flex;
width: 49%;
height: 90%;
background: #2D2D2D;
&:first-of-type {
margin-right: 2%;
}
.text {
display: flex;
flex-direction: column-reverse;
width: 112px;
margin-left: 24px;
color: #fff;
padding-bottom: 20px;
}
.time {
display: flex;
flex-direction: column-reverse;
padding-bottom: 20px;
width: 100px;
margin-right: 24px;
margin-left: 5px;
color: #fff;
}
.viedo {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.user-icon {
position: absolute;
right: 0;
bottom: 0;
display: block;
width: 32px;
height: 32px;
}
&> div {
height: 100%;
video {
width: 100%;
height: 100%;
}
}
.video-icon {
width: 96px;
height: 96px;
}
.mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #2D2D2D;
display: flex;
justify-content: center;
align-items: center;
img {
display: block;
width: 93px;
height: 125px;
}
.vocie {
width: 93px;
height: 93px;
}
}
}
}
}
.close {
width: 102px;
height: 32px;
background: #FF4D4F;
border-radius: 2px;
font-size: 14px;
color: #FFFFFF;
line-height: 32px;
text-align: center;
margin: 0 auto 6%;
cursor: pointer;
}
}
</style> </style>
\ No newline at end of file
...@@ -82,7 +82,7 @@ export default { ...@@ -82,7 +82,7 @@ export default {
JSON.stringify(vueMenuDtos) JSON.stringify(vueMenuDtos)
); );
// 做当前路由无权限时处理 // 做当前路由无权限时处理
//this.checkAuth(this.$route.path.split("/")[1], vueMenuDtos); // this.checkAuth(this.$route.path.split("/")[1], vueMenuDtos);
} }
}); });
}, },
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册