dusenyao 4 gadi atpakaļ
vecāks
revīzija
5ea874eb45

+ 42 - 0
src/views/live/common.js

@@ -194,6 +194,11 @@ export function roomUpdate(option) {
   rtc.roomUpdate(option);
 }
 
+// 自定义消息发送事件
+export function sendPublishMessage(data) {
+  rtc.sendPublishMessage(data);
+}
+
 // 聊天组件
 
 /**
@@ -215,3 +220,40 @@ export function drawChange(action, value) {
     value
   });
 }
+
+export function createScript(url) {
+  let script = document.createElement('script');
+  script.type = 'text/javascript';
+  script.src = url;
+  document.getElementsByTagName('body')[0].appendChild(script);
+  return script;
+}
+
+function createLink(url) {
+  let link = document.createElement('link');
+  link.rel = 'stylesheet';
+  link.href = url;
+  document.getElementsByTagName('body')[0].appendChild(link);
+  return link;
+}
+
+// 加载直播所需 SDK,加载完成后才能初始化
+export function downloadWebSDK(vue) {
+  createLink('https://class.csslcloud.net/static/SDK/docSDK/draw.css').onload = () => {
+    vue.loadedNumber += 1;
+  };
+
+  createScript('https://class.csslcloud.net/static/dist/js/websdk_4.0.js').onload = () => {
+    let scriptArray = [
+      'https://class.csslcloud.net/static/dist/js/classMode.js',
+      'https://class.csslcloud.net/static/dist/js/classUpdateChat.js',
+      'https://image.csslcloud.net/js/dpc.js',
+      'https://class.csslcloud.net/static/js/jquery/jquery.min.js'
+    ];
+    for (let i = 0; i < scriptArray.length; i++) {
+      createScript(scriptArray[i]).onload = () => {
+        vue.loadedNumber += 1;
+      };
+    }
+  };
+}

+ 106 - 77
src/views/live/student/index.vue

@@ -33,7 +33,24 @@
             </div>
           </div>
         </div>
-        <div v-show="connect" id="student"></div>
+        <div v-show="connect" class="student-parent">
+          <div v-show="roomInfo.video_mode === 1 || remoteStreamType === 1" id="student"></div>
+          <template v-if="remoteStreamType !== 1">
+            <template v-if="roomInfo.video_mode === 1">
+              <el-button type="danger" @click="handsDown">
+                <svg-icon icon-class="hang-up" /> 挂断
+              </el-button>
+            </template>
+            <template v-else>
+              <div class="student-audio">
+                <el-avatar icon="el-icon-user" :src="roomInfo.teacher_image_url" />
+                <el-button type="danger" circle @click="handsDown">
+                  <svg-icon icon-class="hang-up" />
+                </el-button>
+              </div>
+            </template>
+          </template>
+        </div>
         <div v-show="isDraw" id="draw-parent">
           <div v-show="isDrawSetting" class="draw-setting">
             <span class="brush-shape">
@@ -85,9 +102,13 @@
       </div>
       <!-- 右侧 -->
       <div class="live-container-right">
-        <div class="live-teacher-lens" @mouseover="liveWrapperOver" @mouseout="liveWrapperOut">
+        <div
+          class="live-teacher-lens"
+          @mouseover="liveMenuShow = true"
+          @mouseout="liveMenuShow = false"
+        >
           <div id="live"></div>
-          <div v-show="liveMenuShow" class="live-wrapper">
+          <div :style="{ bottom: liveMenuShow ? '0' : '-40px' }" class="live-wrapper">
             <div>
               {{ roomInfo.teacher_name }}
             </div>
@@ -152,6 +173,7 @@ export default {
       material_picture_url: '',
       // 定时器
       timer: null,
+      remoteStreamType: -1,
       rtc: null,
       roomData: {
         desc: '直播间标题',
@@ -200,13 +222,21 @@ export default {
   watch: {
     loadedNumber(newVal) {
       if (newVal === 5) {
-        this.initSDK();
-        this.$loading().close();
+        common.createScript(
+          'https://class.csslcloud.net/static/SDK/docSDK/drawSdk_3.0.js'
+        ).onload = () => {
+          this.initSDK();
+          this.$loading().close();
+        };
       }
     }
   },
   created() {
-    this.downloadWebSDK();
+    this.$loading({
+      text: '加载直播所需SDK中...',
+      background: '#fff'
+    });
+    common.downloadWebSDK(this);
     this.getLiveRoomStudentList();
     this.getLiveRoomInfo();
   },
@@ -222,62 +252,11 @@ export default {
     // }
     clearInterval(this.timer);
     StudentExitLiveRoom({ task_id: this.task_id, room_user_id: this.room_user_id });
+    if (this.callLoading || this.connect) {
+      this.handsDown();
+    }
   },
   methods: {
-    // 加载直播所需 SDK,加载完成后才能初始化
-    downloadWebSDK() {
-      this.$loading({
-        text: '加载直播所需SDK中...',
-        background: '#fff'
-      });
-      let script = this.createScript('https://class.csslcloud.net/static/dist/js/websdk_4.0.js');
-      let link = this.createLink('https://class.csslcloud.net/static/SDK/docSDK/draw.css');
-
-      link.onload = () => {
-        this.loadedNumber += 1;
-      };
-
-      script.onload = () => {
-        let scriptArray = [
-          'https://class.csslcloud.net/static/dist/js/classMode.js',
-          'https://class.csslcloud.net/static/dist/js/classUpdateChat.js',
-          'https://image.csslcloud.net/js/dpc.js'
-        ];
-        for (let i = 0; i < scriptArray.length; i++) {
-          let classMode = this.createScript(scriptArray[i]);
-          classMode.onload = () => {
-            this.loadedNumber += 1;
-          };
-        }
-      };
-
-      let jq = this.createScript('https://class.csslcloud.net/static/js/jquery/jquery.min.js');
-      jq.onload = () => {
-        let classMode = this.createScript(
-          'https://class.csslcloud.net/static/SDK/docSDK/drawSdk_3.0.js'
-        );
-        classMode.onload = () => {
-          this.loadedNumber += 1;
-        };
-      };
-    },
-
-    createScript(url) {
-      let script = document.createElement('script');
-      script.type = 'text/javascript';
-      script.src = url;
-      document.getElementsByTagName('body')[0].appendChild(script);
-      return script;
-    },
-
-    createLink(url) {
-      let link = document.createElement('link');
-      link.rel = 'stylesheet';
-      link.href = url;
-      document.getElementsByTagName('body')[0].appendChild(link);
-      return link;
-    },
-
     initSDK() {
       const { live_room_sys_user_id, room_id, session_id } = this.$route.query;
       this.rtc = common.initSDK({
@@ -286,7 +265,6 @@ export default {
         sessionid: session_id
       });
       common.initListener(this); // 注册监听事件
-      window.rtc = this.rtc;
       this.getLiveStat();
     },
 
@@ -299,7 +277,8 @@ export default {
           cs_item_name,
           course_name,
           teacher_name,
-          student_count
+          student_count,
+          teacher_image_url
         }) => {
           this.roomInfo = {
             room_id,
@@ -308,7 +287,8 @@ export default {
             cs_item_name,
             course_name,
             teacher_name,
-            student_count
+            student_count,
+            teacher_image_url
           };
         }
       );
@@ -332,6 +312,21 @@ export default {
       });
     },
 
+    // 下麦
+    handsDown() {
+      common.handsDown({
+        uid: this.room_user_id,
+        success: str => {
+          this.callLoading = false;
+          console.log('下麦成功', str);
+          this.$message.success('下麦成功');
+        },
+        fail: data => {
+          console.log('下麦失败', data);
+        }
+      });
+    },
+
     // 发消息
     sendMsg() {
       common.sendMsg(this.msg);
@@ -343,7 +338,16 @@ export default {
     },
 
     inviteAccept() {
-      common.inviteAccept();
+      common.inviteAccept({
+        success: str => {
+          console.log('接受邀请成功', str);
+        },
+        fail: data => {
+          console.log('接受邀请失败', data);
+          this.$message.warning(`接受邀请失败 ${data.errorMsg}`);
+          this.callLoading = false;
+        }
+      });
     },
 
     getLiveRoomStudentList() {
@@ -364,14 +368,6 @@ export default {
       this.getCurMaterialSent();
     },
 
-    liveWrapperOver() {
-      this.liveMenuShow = true;
-    },
-
-    liveWrapperOut() {
-      this.liveMenuShow = false;
-    },
-
     getCurMaterialSent() {
       const timer = setInterval(() => {
         GetCurMaterialSent({ task_id: this.task_id })
@@ -470,12 +466,45 @@ $live-bc: #3d3938;
         }
       }
 
-      #student {
+      .student-parent {
+        position: relative;
         width: 100%;
         height: 468px;
-        border: 1px solid #ccc;
-        position: relative;
-        background-color: $live-bc;
+
+        #student {
+          width: 100%;
+          height: 468px;
+          border: 1px solid #ccc;
+          position: relative;
+          background-color: $live-bc;
+        }
+
+        > .el-button {
+          position: absolute;
+          bottom: 40px;
+          left: calc(50% - 44px);
+        }
+
+        .student-audio {
+          width: 100%;
+          height: 100%;
+          background-color: #646464;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          align-items: center;
+
+          > .el-avatar {
+            width: 96px;
+            height: 96px;
+            line-height: 96px;
+            margin-bottom: 24px;
+
+            &--icon {
+              font-size: 36px;
+            }
+          }
+        }
       }
 
       #draw-parent {
@@ -655,7 +684,6 @@ $live-bc: #3d3938;
 
         .live-wrapper {
           position: absolute;
-          bottom: 0;
           height: 40px;
           width: 100%;
           background-color: #000;
@@ -663,6 +691,7 @@ $live-bc: #3d3938;
           color: #fff;
           line-height: 40px;
           padding: 0 16px;
+          transition: all 300ms ease-in 0s;
         }
       }
 

+ 24 - 18
src/views/live/student/live.js

@@ -1,22 +1,23 @@
 import { Message } from 'element-ui';
 import { GetLiveRoomInfo } from '@/api/live';
 import { rtc, publishStream, closeVideo } from '@/views/live/common';
-export { initSDK, sendMsg, getLiveStat, getDevice } from '@/views/live/common';
+export {
+  initSDK,
+  sendMsg,
+  getLiveStat,
+  getDevice,
+  downloadWebSDK,
+  createScript,
+  handsDown
+} from '@/views/live/common';
 
 // 连麦
 
 /**
  * 学生端接受老师的上麦邀请,同意上麦
  */
-export function inviteAccept() {
-  rtc.inviteAccept({
-    success: function (str) {
-      console.log('接受邀请成功', str);
-    },
-    fail: function (data) {
-      console.log('接受邀请失败', data);
-    }
-  });
+export function inviteAccept(object) {
+  rtc.inviteAccept(object);
 }
 
 /**
@@ -93,6 +94,7 @@ export function initListener(vue) {
         success: function (stream) {
           // 订阅流成功
           let streamType = stream.streamType();
+          vue.remoteStreamType = streamType;
           console.log('订阅流成功', streamType);
           let id = streamType === 0 ? 'live' : 'student';
           stream.show(id, 'contain'); // 将流显示到指定 id 的盒子中
@@ -135,9 +137,9 @@ export function initListener(vue) {
     vue.liveStat = false;
   });
 
+  // 单个用户配置监听
   rtc.on('switch_user_settings', settingData => {
-    // 单个用户配置监听
-    console.log(settingData);
+    console.log('单个用户配置监听', JSON.parse(settingData));
   });
 
   // 人员列表事件(人员麦序变化时广播)
@@ -147,7 +149,7 @@ export function initListener(vue) {
   });
 
   rtc.on('switch_settings', data => {
-    console.log('房间设置事件', data); // 房间设置事件
+    console.log('房间设置事件', JSON.parse(data)); // 房间设置事件
   });
 
   rtc.on('publishStreamErr', data => {
@@ -160,15 +162,11 @@ export function initListener(vue) {
     console.log('视频无法自动播放', data);
   });
 
-  // 用户退出房间通知其他人员事件
-  rtc.on('exit_room_user', function (data) {
-    console.log('用户退出房间通知其他人员事件', data);
-  });
-
   // 监听通知移除流事件
   rtc.on('stream_removed', function (stream) {
     console.log('监听通知移除流事件');
     vue.connect = false;
+    vue.remoteStreamType = -1;
   });
 
   // 停止订阅流
@@ -196,6 +194,7 @@ export function initListener(vue) {
     // 创建本地流推流
     GetLiveRoomInfo({ task_id: vue.task_id }).then(({ video_mode }) => {
       console.log('创建本地流推流');
+      vue.roomInfo.video_mode = video_mode;
       const createData = {
         video: video_mode === 1,
         audio: true
@@ -244,4 +243,11 @@ export function initListener(vue) {
       message: `全体${msg}成功`
     });
   });
+
+  // 接收自定义消息
+  rtc.on('publish_message', function (data) {
+    if (data.type === 'handsDown-load' && data.uid === vue.room_user_id) {
+      vue.callLoading = false;
+    }
+  });
 }

+ 110 - 80
src/views/live/teacher/index.vue

@@ -28,13 +28,30 @@
           <div class="loading-wrapper">
             <p class="loading-title">正在呼叫,等待对方接通...</p>
             <div>
-              <!-- <el-button type="danger" circle>
+              <el-button type="danger" circle @click="handsDown('loading')">
                 <svg-icon icon-class="hang-up" />
-              </el-button> -->
+              </el-button>
             </div>
           </div>
         </div>
-        <div v-show="connect" id="student"></div>
+        <div v-show="connect" class="student-parent">
+          <div v-show="roomInfo.video_mode === 1 || remoteStreamType === 1" id="student"></div>
+          <template v-if="remoteStreamType !== 1">
+            <template v-if="roomInfo.video_mode === 1">
+              <el-button type="danger" @click="handsDown">
+                <svg-icon icon-class="hang-up" /> 挂断
+              </el-button>
+            </template>
+            <template v-else>
+              <div class="student-audio">
+                <el-avatar icon="el-icon-user" :src="connectStudent.student_image_url" />
+                <el-button type="danger" circle @click="handsDown">
+                  <svg-icon icon-class="hang-up" />
+                </el-button>
+              </div>
+            </template>
+          </template>
+        </div>
         <div v-show="isDraw" id="draw-parent">
           <div v-show="isDrawSetting" class="draw-setting">
             <span class="brush-shape">
@@ -106,7 +123,7 @@
           @mouseout="liveMenuShow = false"
         >
           <div id="live"></div>
-          <div v-show="liveMenuShow" class="live-wrapper">
+          <div :style="{ bottom: liveMenuShow ? '0' : '-40px' }" class="live-wrapper">
             <div>
               {{ roomInfo.teacher_name }}
             </div>
@@ -122,9 +139,8 @@
                 <span class="name">{{ item.student_name }}</span>
               </div>
               <div class="student-list-right">
-                <svg-icon icon-class="video" @click="invite(item.room_user_id, 1)" />
-                <svg-icon icon-class="voice" @click="invite(item.room_user_id, 2)" />
-                <span @click="handsDown(item.room_user_id)"> 下麦 </span>
+                <svg-icon icon-class="video" @click="invite(item, 1)" />
+                <svg-icon icon-class="voice" @click="invite(item, 2)" />
               </div>
             </li>
           </ul>
@@ -150,7 +166,12 @@
 </template>
 
 <script>
-import { GetLiveRoomStudentList, CloseLiveRoom, GetLiveRoomInfo } from '@/api/live';
+import {
+  GetLiveRoomStudentList,
+  CloseLiveRoom,
+  GetLiveRoomInfo,
+  StudentExitLiveRoom
+} from '@/api/live';
 import SelectMaterial from '@/components/live/SelectMaterial.vue';
 import CompleteList from './CompleteList.vue';
 import * as common from './live';
@@ -166,6 +187,7 @@ export default {
       task_id: this.$route.query.task_id,
       // 连麦
       connect: false,
+      connectStudent: {},
       // 等待接通
       callLoading: false,
       dialogVisible: false,
@@ -173,6 +195,7 @@ export default {
       dialogVisibleComplete: false,
       // 定时器
       timer: null,
+      remoteStreamType: -1,
       rtc: null,
       roomData: {
         desc: '直播间标题',
@@ -221,13 +244,21 @@ export default {
   watch: {
     loadedNumber(newVal) {
       if (newVal === 5) {
-        this.initSDK();
-        this.$loading().close();
+        common.createScript(
+          'https://class.csslcloud.net/static/SDK/docSDK/drawSdk_3.0.js'
+        ).onload = () => {
+          this.initSDK();
+          this.$loading().close();
+        };
       }
     }
   },
   created() {
-    this.downloadWebSDK();
+    this.$loading({
+      text: '加载直播所需SDK中...',
+      background: '#fff'
+    });
+    common.downloadWebSDK(this);
     this.getLiveRoomStudentList();
     this.getLiveRoomInfo();
   },
@@ -264,60 +295,6 @@ export default {
     clearInterval(this.timer);
   },
   methods: {
-    // 加载直播所需 SDK,加载完成后才能初始化
-    downloadWebSDK() {
-      this.$loading({
-        text: '加载直播所需SDK中...',
-        background: '#fff'
-      });
-      let script = this.createScript('https://class.csslcloud.net/static/dist/js/websdk_4.0.js');
-      let link = this.createLink('https://class.csslcloud.net/static/SDK/docSDK/draw.css');
-
-      link.onload = () => {
-        this.loadedNumber += 1;
-      };
-
-      script.onload = () => {
-        let scriptArray = [
-          'https://class.csslcloud.net/static/dist/js/classMode.js',
-          'https://class.csslcloud.net/static/dist/js/classUpdateChat.js',
-          'https://image.csslcloud.net/js/dpc.js'
-        ];
-        for (let i = 0; i < scriptArray.length; i++) {
-          let classMode = this.createScript(scriptArray[i]);
-          classMode.onload = () => {
-            this.loadedNumber += 1;
-          };
-        }
-      };
-
-      let jq = this.createScript('https://class.csslcloud.net/static/js/jquery/jquery.min.js');
-      jq.onload = () => {
-        let classMode = this.createScript(
-          'https://class.csslcloud.net/static/SDK/docSDK/drawSdk_3.0.js'
-        );
-        classMode.onload = () => {
-          this.loadedNumber += 1;
-        };
-      };
-    },
-
-    createScript(url) {
-      let script = document.createElement('script');
-      script.type = 'text/javascript';
-      script.src = url;
-      document.getElementsByTagName('body')[0].appendChild(script);
-      return script;
-    },
-
-    createLink(url) {
-      let link = document.createElement('link');
-      link.rel = 'stylesheet';
-      link.href = url;
-      document.getElementsByTagName('body')[0].appendChild(link);
-      return link;
-    },
-
     initSDK() {
       const { live_room_sys_user_id, room_id, session_id } = this.$route.query;
       this.rtc = common.initSDK({
@@ -326,7 +303,6 @@ export default {
         sessionid: session_id
       });
       common.initListener(this); // 注册监听事件
-      window.rtc = this.rtc;
       this.getLiveStat();
     },
 
@@ -388,21 +364,29 @@ export default {
     },
 
     // 老师邀请学生上麦
-    invite(uid, mode) {
+    invite(student, mode) {
+      if (this.connect || this.callLoading) {
+        this.$message.warning('正在连麦中');
+        return;
+      }
       this.callLoading = true;
+      this.connectStudent = student;
       GetLiveRoomInfo({ task_id: this.task_id })
         .then(({ video_mode }) => {
+          let uid = student.room_user_id;
           if (video_mode === mode) {
             common.invite(uid);
           } else {
             common.roomUpdate({
               video_mode: mode,
-              roomUpdateSuccess: function (data) {
+              roomUpdateSuccess: data => {
                 console.log(data, '连麦音视频模式更新请求成功!');
+                this.roomInfo.video_mode = mode;
                 common.invite(uid);
               },
-              roomUpdateFailed: function (data) {
-                console.log(data, '连麦音视频模式更新请求失败! 请稍后再试!');
+              roomUpdateFailed: data => {
+                this.callLoading = false;
+                this.connectStudent = '';
                 this.$message.error('连麦音视频模式更新请求失败! 请稍后再试!');
               }
             });
@@ -410,16 +394,24 @@ export default {
         })
         .catch(() => {
           this.callLoading = false;
+          this.connectStudent = '';
         });
     },
 
     // 下麦
-    handsDown(uid) {
+    handsDown(type) {
       common.handsDown({
-        uid,
+        uid: this.connectStudent.room_user_id,
         success: str => {
+          if (type === 'loading') {
+            common.sendPublishMessage({
+              type: 'handsDown-load',
+              uid: this.connectStudent.room_user_id
+            });
+          }
+
           this.callLoading = false;
-          console.log('下麦成功', str);
+          this.connectStudent = '';
           this.$message.success('下麦成功');
         },
         fail: data => {
@@ -471,6 +463,10 @@ export default {
       }, 5000);
     },
 
+    studentExitLiveRoom(room_user_id) {
+      StudentExitLiveRoom({ task_id: this.task_id, room_user_id });
+    },
+
     // 弹出框方法
     publishStream() {
       common.publishStream('main');
@@ -568,12 +564,45 @@ $live-bc: #3d3938;
         }
       }
 
-      #student {
+      .student-parent {
+        position: relative;
         width: 100%;
         height: 468px;
-        border: 1px solid #ccc;
-        position: relative;
-        background-color: $live-bc;
+
+        #student {
+          width: 100%;
+          height: 468px;
+          border: 1px solid #ccc;
+          position: relative;
+          background-color: $live-bc;
+        }
+
+        > .el-button {
+          position: absolute;
+          bottom: 40px;
+          left: calc(50% - 44px);
+        }
+
+        .student-audio {
+          width: 100%;
+          height: 100%;
+          background-color: #646464;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          align-items: center;
+
+          .el-avatar {
+            width: 96px;
+            height: 96px;
+            line-height: 96px;
+            margin-bottom: 24px;
+
+            &--icon {
+              font-size: 36px;
+            }
+          }
+        }
       }
 
       #draw-parent {
@@ -753,7 +782,6 @@ $live-bc: #3d3938;
 
         .live-wrapper {
           position: absolute;
-          bottom: 0;
           height: 40px;
           width: 100%;
           background-color: #000;
@@ -761,9 +789,11 @@ $live-bc: #3d3938;
           color: #fff;
           line-height: 40px;
           padding: 0 16px;
+          transition: all 300ms ease-in 0s;
         }
       }
 
+      // 学员列表
       .student-list {
         width: 100%;
         padding: 24px 16px;
@@ -782,7 +812,7 @@ $live-bc: #3d3938;
           margin-bottom: 16px;
 
           .student-list-left {
-            flex: 7;
+            flex: 8;
 
             .name {
               vertical-align: super;
@@ -791,7 +821,7 @@ $live-bc: #3d3938;
           }
 
           .student-list-right {
-            flex: 3;
+            flex: 2;
 
             .svg-icon {
               font-size: 18px;

+ 13 - 2
src/views/live/teacher/live.js

@@ -2,13 +2,16 @@ import { Message } from 'element-ui';
 import { rtc, publishStream, createLocalStream } from '@/views/live/common';
 export {
   initSDK,
+  downloadWebSDK,
+  createScript,
   getLiveStat,
   roomUpdate,
   sendMsg,
   drawChange,
   getDevice,
   publishStream,
-  handsDown
+  handsDown,
+  sendPublishMessage
 } from '@/views/live/common';
 
 /**
@@ -63,6 +66,7 @@ export function initListener(vue) {
         success: function (stream) {
           // 订阅流成功
           let streamType = stream.streamType();
+          vue.remoteStreamType = streamType;
           // let hasVideo = stream.hasVideo();
           console.log('订阅流成功', streamType);
           let id = streamType === 0 ? 'live' : 'student';
@@ -144,11 +148,12 @@ export function initListener(vue) {
   rtc.on('stream_removed', function (stream) {
     console.log('监听通知移除流事件');
     vue.connect = false;
+    vue.remoteStreamType = -1;
   });
 
   // 停止订阅流
   rtc.on('unSub', function (stream) {
-    console.log('停止订阅流');
+    console.log('停止订阅流', stream);
     rtc.unSubscribeStream({
       unSubStream: stream,
       success: function (stream) {
@@ -161,6 +166,12 @@ export function initListener(vue) {
     });
   });
 
+  // 用户退出房间通知其他人员事件
+  rtc.on('exit_room_user', function (data) {
+    console.log('用户退出房间通知其他人员事件', data);
+    vue.studentExitLiveRoom(data.id);
+  });
+
   /**
    * 排麦监听事件
    */