Sfoglia il codice sorgente

update 新版直播的调整分组

dusenyao 2 anni fa
parent
commit
53f9e9dcb4

+ 4 - 40
src/views/new_live/common/GroupList.vue

@@ -79,6 +79,10 @@ const props = defineProps({
   isGroup: {
     type: Boolean,
     required: true
+  },
+  adjustGroupList: {
+    type: Array,
+    required: true
   }
 });
 
@@ -131,13 +135,6 @@ watch(gList, (newVal) => {
   if (curPage.value > newVal.length) curPage.value = newVal.length - 1;
 });
 
-// 调整分组用列表
-let adjustGroupList = computed(() => {
-  return props.groupList.map(({ group_id }, i) => {
-    return { group_id, group_name: `分组${i + 1}` };
-  });
-});
-
 const task_id = inject('task_id');
 /**
  * 调整分组
@@ -279,36 +276,3 @@ function adjustGroup(student_id, studentCurGroupId, group_id) {
   }
 }
 </style>
-
-<style lang="scss">
-.student-item-operation {
-  padding: 24px;
-
-  .adjust-group {
-    display: flex;
-    flex-direction: column;
-    row-gap: 16px;
-
-    .operation-button {
-      display: flex;
-      justify-content: flex-end;
-
-      .button-cancel {
-        background-color: #f5f5f5;
-        border-color: #f5f5f5;
-      }
-
-      .button-confirm {
-        color: #fff;
-        background-color: #3fa1fb;
-        border-color: #3fa1fb;
-      }
-    }
-  }
-
-  .el-popover__title {
-    font-weight: 600;
-    color: #333;
-  }
-}
-</style>

+ 32 - 1
src/views/new_live/common/SinglePopover.vue

@@ -58,4 +58,35 @@ function confirm() {
 }
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss">
+.student-item-operation {
+  padding: 24px;
+
+  .adjust-group {
+    display: flex;
+    flex-direction: column;
+    row-gap: 16px;
+
+    .operation-button {
+      display: flex;
+      justify-content: flex-end;
+
+      .button-cancel {
+        background-color: #f5f5f5;
+        border-color: #f5f5f5;
+      }
+
+      .button-confirm {
+        color: #fff;
+        background-color: #3fa1fb;
+        border-color: #3fa1fb;
+      }
+    }
+  }
+
+  .el-popover__title {
+    font-weight: 600;
+    color: #333;
+  }
+}
+</style>

+ 5 - 3
src/views/new_live/student/components/StudentStream.vue

@@ -38,7 +38,7 @@
         <div v-for="(item, i) in streamList" :key="item.id()" class="stream-list-item">
           <div :id="`group-${i}`" class="group-box"></div>
           <div class="live-wrapper-stream">
-            <span>{{ $emit('searchStudentName', item.id()) }} </span>
+            <span>{{ searchStudentName(item.id()) }} </span>
           </div>
         </div>
         <!-- 无流列表 -->
@@ -102,10 +102,12 @@ defineProps({
   studentSelf: {
     type: Object,
     required: true
+  },
+  searchStudentName: {
+    type: Function,
+    required: true
   }
 });
-
-defineEmits(['searchStudentName']);
 </script>
 
 <style lang="scss" scoped>

+ 1 - 0
src/views/new_live/student/group.js

@@ -194,6 +194,7 @@ export function useInitListener({ streamList, roomContext, is_teacher_in_group,
 
   rtc.value.on('kick_out', () => {
     console.log('自己被踢出房间');
+    Message.warning('您已被踢出房间');
     exitRoom();
   });
 

+ 23 - 17
src/views/new_live/student/group.vue

@@ -10,7 +10,7 @@
           :teacher-name="roomInfo.teacher_name"
           :student-self="studentSelf"
           :is-audience="isAudience"
-          @search-student-name="searchStudentName"
+          :search-student-name="searchStudentName"
         />
         <MemberList :member-show="memberShow" :show-operation="false" :student-list="audience_list" title="旁听列表" />
       </div>
@@ -72,25 +72,31 @@ let task_id = route.query.task_id;
 const { memberShow, toggle } = useMemberList();
 
 let streamList = ref([]);
-watch(streamList, (newVal) => {
-  const list = student_list.value.filter((item) => {
-    if (item.is_self === 'false') {
-      let isNoStream = true;
-      for (let i = 0; i < newVal.length; i++) {
-        if (newVal[i].id().split('-')[0] === item.room_user_id) isNoStream = false;
+watch(
+  streamList,
+  (newVal) => {
+    const list = student_list.value.filter((item) => {
+      if (item.is_self === 'false') {
+        let isNoStream = true;
+        for (let i = 0; i < newVal.length; i++) {
+          if (newVal[i].id().split('-')[0] === item.room_user_id) isNoStream = false;
+        }
+        return isNoStream;
       }
-      return isNoStream;
-    }
-    return false;
-  });
-  noStreamList.value = list;
-
-  if (newVal.length > 0) {
-    nextTick(() => {
-      newVal[newVal.length - 1].show(`group-${newVal.length - 1}`);
+      return false;
     });
+    noStreamList.value = list;
+
+    if (newVal.length > 0) {
+      nextTick(() => {
+        newVal[newVal.length - 1].show(`group-${newVal.length - 1}`);
+      });
+    }
+  },
+  {
+    deep: true
   }
-});
+);
 
 let marginLeft = ref(0);
 

+ 99 - 9
src/views/new_live/teacher/StreamList.vue

@@ -26,12 +26,12 @@
         <div v-for="(item, i) in streamList" :key="item.id()" class="stream-list-item">
           <div :id="`group-${i}`" class="group-box"></div>
           <div class="live-wrapper-stream">
-            <span>{{ $emit('searchStudentName', item.id()) }} </span>
+            <span>{{ searchStudentName(item.id()) }} </span>
             <el-popover popper-class="group-operation" trigger="click" placement="bottom-start">
               <div class="operation">
-                <div class="operation-item">移动到...</div>
-                <div class="operation-item"><span>禁言</span></div>
-                <div class="operation-item" @click="kickOut(room_user_id)">
+                <div class="operation-item" @click="openDialog(item.id().split('-')[0])">移动到...</div>
+                <div class="operation-item" @click="estoppel(item.id().split('-')[0])"><span>禁言</span></div>
+                <div class="operation-item" @click="kickOut(item.id().split('-')[0])">
                   <span :style="{ color: '#EA5050' }">踢出</span>
                 </div>
               </div>
@@ -53,9 +53,9 @@
               {{ student_name }}
             </span>
             <!-- 弹出操作框 -->
-            <el-popover popper-class="group-operation" trigger="click" placement="bottom">
+            <el-popover popper-class="group-operation" trigger="click" placement="bottom-start">
               <div class="operation">
-                <div class="operation-item">移动到...</div>
+                <div class="operation-item" @click="openDialog(room_user_id)">移动到...</div>
                 <div class="operation-item" @click="estoppel(room_user_id)">
                   <span>禁言</span>
                 </div>
@@ -75,6 +75,23 @@
     </div>
 
     <div class="rotation-circle"></div>
+
+    <el-dialog :visible="visible" width="300px" top="30vh" :title="dialogTitle">
+      <div class="adjust-group">
+        <el-select v-model="groupId">
+          <el-option
+            v-for="{ group_id, group_name } in adjustGroupList"
+            :key="group_id"
+            :label="group_name"
+            :value="group_id"
+          />
+        </el-select>
+        <div class="operation-button">
+          <el-button class="button-cancel" @click="visible = false"> 取消 </el-button>
+          <el-button class="button-confirm" @click="confirm">确定</el-button>
+        </div>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -85,8 +102,11 @@ export default {
 </script>
 
 <script setup>
+import { ref, inject } from 'vue';
 import { useTeacherLiveRtc } from './live';
 import { useLive } from '../common/common';
+import { Message, Loading } from 'element-ui';
+import { AdjustGroup } from '@/api/live';
 
 const { kickOut } = useTeacherLiveRtc();
 const { sendPublishMessage } = useLive();
@@ -102,7 +122,7 @@ function estoppel(room_user_id) {
   });
 }
 
-defineProps({
+const props = defineProps({
   isTeacherStream: {
     type: Boolean,
     required: true
@@ -122,10 +142,58 @@ defineProps({
   noStreamList: {
     type: Array,
     required: true
-  }
+  },
+  adjustGroupList: {
+    type: Array,
+    required: true
+  },
+  curGroupId: {
+    type: String,
+    required: true
+  },
+  studentList: {
+    type: Array,
+    required: true
+  },
+  searchStudentName: { type: Function, required: true }
 });
 
-defineEmits(['searchStudentName']);
+const emits = defineEmits(['getMyGroupInfo', 'getGroupInfo']);
+
+let visible = ref(false);
+let dialogTitle = ref('移动到...');
+let groupId = ref(''); // 选中的分组id
+let studentId = ref('');
+const task_id = inject('task_id');
+
+function openDialog(id) {
+  visible.value = true;
+  dialogTitle.value = `${props.searchStudentName(id)}移动到...`;
+  studentId.value = props.studentList.find(({ room_user_id }) => room_user_id === id)?.student_id;
+}
+function confirm() {
+  if (groupId.value.length === 0) return;
+  visible.value = false;
+  if (props.curGroupId === groupId.value) return Message.warning('学员已在该分组中');
+  let loadingInstance = Loading.service({ text: '调整分组中...', background: '#fff' });
+  AdjustGroup({
+    task_id,
+    student_list: [
+      {
+        student_id: studentId.value,
+        group_id: groupId.value
+      }
+    ]
+  })
+    .then(() => {
+      Message.success('调整分组成功');
+      emits('getMyGroupInfo');
+      emits('getGroupInfo');
+    })
+    .finally(() => {
+      loadingInstance.close();
+    });
+}
 
 function changeCurPage(page) {}
 </script>
@@ -265,6 +333,28 @@ function changeCurPage(page) {}
       }
     }
   }
+
+  :deep .adjust-group {
+    display: flex;
+    flex-direction: column;
+    row-gap: 16px;
+
+    .operation-button {
+      display: flex;
+      justify-content: flex-end;
+
+      .button-cancel {
+        background-color: #f5f5f5;
+        border-color: #f5f5f5;
+      }
+
+      .button-confirm {
+        color: #fff;
+        background-color: #3fa1fb;
+        border-color: #3fa1fb;
+      }
+    }
+  }
 }
 </style>
 

+ 4 - 6
src/views/new_live/teacher/group.js

@@ -247,16 +247,16 @@ export function useDownloadSDK(fn, fnData) {
   };
 }
 
+export let noStreamList = ref([]); // 无远程流学员列表
+export let student_list = ref([]);
 export function useGroupInit(_downloadWebSDK, GetGroupInfo) {
   const route = useRoute();
   let task_id = route.query.task_id;
   provide('task_id', task_id);
 
   GetGroupInfo();
-  let noStreamList = ref([]); // 无远程流学员列表
-  let student_list = ref([]);
   let isGroup = ref(false);
-  let group_id = ref('');
+  let group_id = ref(''); // 当前分组id
   let isAudit = ref(false); // 是否旁听
   let audience_list = ref([]); // 旁听学员列表
   /**
@@ -279,11 +279,11 @@ export function useGroupInit(_downloadWebSDK, GetGroupInfo) {
           session_id.value = sessionId;
           noStreamList.value = sList;
           student_list.value = sList;
-          _downloadWebSDK();
           isGroup.value = true;
           group_id.value = groupId;
           isAudit.value = is_example === 'true';
           audience_list.value = audList;
+          _downloadWebSDK();
         }
       );
     }
@@ -332,8 +332,6 @@ export function useGroupInit(_downloadWebSDK, GetGroupInfo) {
     live_room_sys_user_id,
     room_id,
     session_id,
-    noStreamList,
-    student_list,
     isGroup,
     group_id,
     isAudit,

+ 55 - 44
src/views/new_live/teacher/group.vue

@@ -5,6 +5,7 @@
         <GroupList
           :is-group="!isGroup"
           :group-list="group_list"
+          :adjust-group-list="adjustGroupList"
           @enterGroup="enterGroup"
           @getGroupInfo="getGroupInfo"
         />
@@ -12,9 +13,14 @@
           :is-group="isGroup"
           :stream-list="streamList"
           :no-stream-list="noStreamList"
+          :adjust-group-list="adjustGroupList"
+          :student-list="student_list"
           :is-teacher-stream="isTeacherStream"
           :teacher-name="roomInfo.teacher_name"
-          @searchStudentName="searchStudentName"
+          :cur-group-id="group_id"
+          :search-student-name="searchStudentName"
+          @getMyGroupInfo="getMyGroupInfo_Teacher"
+          @getGroupInfo="getGroupInfo"
         />
         <MemberList
           :member-show="memberShow && isAudit"
@@ -48,7 +54,7 @@ export default {
 </script>
 
 <script setup>
-import { ref, inject, watch, nextTick, onBeforeUnmount } from 'vue';
+import { ref, inject, computed, watch, nextTick, onBeforeUnmount } from 'vue';
 import {
   GetMyGroupInfo_Teacher,
   JoinGroup_Teacher,
@@ -64,6 +70,8 @@ import {
   useDownloadSDK,
   room_id,
   session_id,
+  noStreamList,
+  student_list,
   live_room_sys_user_id
 } from './group';
 import { useLive, useMemberList } from '../common/common';
@@ -79,6 +87,7 @@ const { memberShow, toggle } = useMemberList();
 
 const route = useRoute();
 let task_id = route.query.task_id; // 任务id
+
 const $t = inject('$t');
 
 let isTeacherStream = ref(false);
@@ -101,22 +110,27 @@ let roomData = ref({
 let speakData = ref({});
 let roomContext = ref({});
 let liveStat = ref(false); // 直播状态
+
 let streamList = ref([]);
-watch(streamList, (newVal) => {
-  const list = student_list.value.filter((item) => {
-    let isNoStream = true;
-    for (let i = 0; i < newVal.length; i++) {
-      if (newVal[i].id().split('-')[0] === item.room_user_id) isNoStream = false;
-    }
-    return isNoStream;
-  });
-  noStreamList.value = list;
-  if (newVal.length > 0) {
-    nextTick(() => {
-      newVal[newVal.length - 1].show(`group-${newVal.length - 1}`);
+watch(
+  streamList,
+  (newVal) => {
+    const list = student_list.value.filter((item) => {
+      let isNoStream = true;
+      for (let i = 0; i < newVal.length; i++) {
+        if (newVal[i].id().split('-')[0] === item.room_user_id) isNoStream = false;
+      }
+      return isNoStream;
     });
-  }
-});
+    noStreamList.value = list;
+    if (newVal.length > 0) {
+      nextTick(() => {
+        newVal[newVal.length - 1].show(`group-${newVal.length - 1}`);
+      });
+    }
+  },
+  { deep: true }
+);
 
 const { downloadWebSDK, removeWebSDK } = useDownloadSDK(useInitListener, {
   roomData,
@@ -128,6 +142,12 @@ const { downloadWebSDK, removeWebSDK } = useDownloadSDK(useInitListener, {
 });
 
 let group_list = ref([]); // 小组成员列表
+// 调整分组用列表
+let adjustGroupList = computed(() => {
+  return group_list.value.map(({ group_id }, i) => {
+    return { group_id, group_name: `分组${i + 1}` };
+  });
+});
 /**
  * 得到分组讨论信息(教师端)
  */
@@ -138,38 +158,29 @@ function getGroupInfo() {
   });
 }
 
-const { audience_list, group_id, isAudit, isGroup, noStreamList, roomInfo, student_list, searchStudentName } =
-  useGroupInit(downloadWebSDK, getGroupInfo);
+const { audience_list, group_id, isAudit, isGroup, roomInfo, searchStudentName } = useGroupInit(
+  downloadWebSDK,
+  getGroupInfo
+);
 const { stopGroup } = useGroup();
 
+function getMyGroupInfo_Teacher() {
+  GetMyGroupInfo_Teacher({ task_id }).then(({ student_list: sList }) => {
+    noStreamList.value = sList ?? [];
+    student_list.value = sList ?? [];
+  });
+}
+
 // 加入分组讨论
 function enterGroup(_group_id) {
-  JoinGroup_Teacher({ task_id, group_id: _group_id })
-    .then(({ room_id: rId, session_id: sId }) => {
-      room_id.value = rId;
-      session_id.value = sId;
-      downloadWebSDK();
-      isGroup.value = true;
-      group_id.value = _group_id;
-      return GetMyGroupInfo_Teacher({ task_id });
-    })
-    .then(({ student_list: sList }) => {
-      noStreamList.value = sList;
-      student_list.value = sList;
-      // for (let i = 0; i < 30; i++) {
-      //   student_list.value.push({
-      //     is_mobile: 'false',
-      //     is_self: 'false',
-      //     is_teacher_role_in_room: 'true',
-      //     room_user_id: 'QjxZTgVShaYyUNjl',
-      //     session_id: '3953BA342CFC3AC1DE2BAAC5EF0BCAF76FC545644E10CF40EFF0373DBDFA5BD5',
-      //     student_id: '20210621-1055-0000002',
-      //     student_image_url:
-      //       'https://file-kf.helxsoft.cn/CSFileServer/URL/001/584B22C60E22F90EBCCC9C95108528C420230208173530PTAYLGFPXSTTBHUZSJNYF5G48JVKVQB1VO9HGBFB_00101-20210722-20-M95FFDOU.png',
-      //     student_name: '陈秒(学生)'
-      //   });
-      // }
-    });
+  JoinGroup_Teacher({ task_id, group_id: _group_id }).then(({ room_id: rId, session_id: sId }) => {
+    room_id.value = rId;
+    session_id.value = sId;
+    downloadWebSDK();
+    isGroup.value = true;
+    group_id.value = _group_id;
+    getMyGroupInfo_Teacher();
+  });
 }
 
 // 退出当前分组讨论

+ 1 - 1
src/views/new_live/teacher/index.vue

@@ -267,7 +267,7 @@ function toggle(type) {
 }
 
 const { startLive, stopLive, kickOut, publishShareStream, unPubShareStream } = useTeacherLiveRtc();
-const { pauseVideo, playVideo, pauseAudio, playAudio, closeVideo, createLocalStream, sendPublishMessage } = useLive();
+const { pauseVideo, playVideo, pauseAudio, playAudio, closeVideo, createLocalStream } = useLive();
 const { groupForm, startGroup } = useGroup(task_id, router);
 // 本地视频流画面、声音
 let device = ref({