Quellcode durchsuchen

update 新版直播的画板状态保存

dusenyao vor 2 Jahren
Ursprung
Commit
4c353c649c

+ 7 - 7
package-lock.json

@@ -54,7 +54,7 @@
         "html-webpack-plugin": "^5.5.0",
         "postcss": "^8.4.21",
         "postcss-html": "^1.5.0",
-        "prettier": "2.8.3",
+        "prettier": "2.8.4",
         "sass": "^1.58.0",
         "sass-loader": "^10.4.1",
         "script-ext-html-webpack-plugin": "^2.1.5",
@@ -17619,9 +17619,9 @@
       }
     },
     "node_modules/prettier": {
-      "version": "2.8.3",
-      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.3.tgz",
-      "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
+      "version": "2.8.4",
+      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.4.tgz",
+      "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
       "dev": true,
       "bin": {
         "prettier": "bin-prettier.js"
@@ -35873,9 +35873,9 @@
       "dev": true
     },
     "prettier": {
-      "version": "2.8.3",
-      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.3.tgz",
-      "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
+      "version": "2.8.4",
+      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.4.tgz",
+      "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
       "dev": true
     },
     "prettier-linter-helpers": {

+ 1 - 1
package.json

@@ -59,7 +59,7 @@
     "html-webpack-plugin": "^5.5.0",
     "postcss": "^8.4.21",
     "postcss-html": "^1.5.0",
-    "prettier": "2.8.3",
+    "prettier": "2.8.4",
     "sass": "^1.58.0",
     "sass-loader": "^10.4.1",
     "script-ext-html-webpack-plugin": "^2.1.5",

+ 13 - 0
src/api/live.js

@@ -357,3 +357,16 @@ export function AdjustGroup(data) {
     data
   });
 }
+
+/**
+ * 存储白板状态
+ * @param {Object} data
+ */
+export function EnableWhiteboard(data) {
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_LearnWebSI,
+    params: getRequestParams('live_room-live_room_dispatch-EnableWhiteboard'),
+    data
+  });
+}

+ 42 - 35
src/views/new_live/common/GroupList.vue

@@ -11,7 +11,6 @@
       <div
         v-for="{ student_list, group_id, group_num, is_example } in gList[curPage]"
         :key="group_id"
-        :style="{}"
         class="group-item"
       >
         <div class="group-item-title">
@@ -36,30 +35,10 @@
               <span>{{ student_name }}</span>
             </div>
             <div class="student-item-right">
-              <el-popover
-                :disabled="showAdjustGroup"
-                popper-class="student-item-operation"
-                trigger="click"
-                title="移动到..."
-                placement="bottom"
-                @hide="handlePopperHide"
-              >
-                <div class="adjust-group">
-                  <el-select v-model="adjustGroup">
-                    <el-option
-                      v-for="{ group_id: gId, group_name } in adjustGroupList"
-                      :key="gId"
-                      :label="group_name"
-                      :value="gId"
-                    />
-                  </el-select>
-                  <div class="operation-button">
-                    <el-button class="button-cancel" @click="adjustGroupCancel">取消</el-button>
-                    <el-button class="button-confirm">确定</el-button>
-                  </div>
-                </div>
-                <svg-icon slot="reference" icon-class="live-mobile-grouping" />
-              </el-popover>
+              <SinglePopover
+                :adjust-group-list="adjustGroupList"
+                :adjust-group="curry(adjustGroup)(student_id)(group_id)"
+              />
             </div>
           </div>
         </div>
@@ -85,7 +64,12 @@ export default {
 </script>
 
 <script setup>
-import { ref, computed, watch, nextTick } from 'vue';
+import { ref, computed, watch, inject } from 'vue';
+import { curry } from '@/utils/common';
+import { AdjustGroup } from '@/api/live';
+import { Loading, Message } from 'element-ui';
+
+import SinglePopover from './SinglePopover.vue';
 
 const props = defineProps({
   groupList: {
@@ -98,13 +82,14 @@ const props = defineProps({
   }
 });
 
-defineEmits(['enterGroup']);
+const emits = defineEmits(['enterGroup', 'getGroupInfo']);
 
 let curPage = ref(0);
 function changePage(page) {
   curPage.value = page;
 }
 
+// 是否需要两行显示
 let isTowLine = computed(() => {
   const col = Math.floor((window.innerHeight - 192 - 204 - 68 - 4) / 44);
   const col_2 = Math.floor((window.innerHeight - 192 - 204 - 68 - 8 - 100) / 44);
@@ -113,9 +98,10 @@ let isTowLine = computed(() => {
   let groupSize = props.groupList[0]?.student_list.length;
   let groupRowNum = Math.ceil(groupSize / col); // 每组需要分成几行
 
-  return groupRowNum === 1 && col_2 / groupSize > 2 && groupNum > row; // 是否需要两行显示
+  return groupRowNum === 1 && col_2 / groupSize > 2 && groupNum > row;
 });
 
+// 分组列表分页
 let gList = computed(() => {
   const col = Math.floor((window.innerHeight - 192 - 204 - 68 - 4) / 44);
   const col_2 = Math.floor((window.innerHeight - 192 - 204 - 112 - 24 - 8) / 44);
@@ -145,18 +131,39 @@ watch(gList, (newVal) => {
   if (curPage.value > newVal.length) curPage.value = newVal.length - 1;
 });
 
-let adjustGroup = ref('');
+// 调整分组用列表
 let adjustGroupList = computed(() => {
   return props.groupList.map(({ group_id }, i) => {
     return { group_id, group_name: `分组${i + 1}` };
   });
 });
-let showAdjustGroup = ref(false);
-function adjustGroupCancel() {
-  // showAdjustGroup.value = true;
-}
-function handlePopperHide() {
-  // showAdjustGroup.value = false;
+
+const task_id = inject('task_id');
+/**
+ * 调整分组
+ * @param {String} student_id 学员id
+ * @param {String} studentCurGroupId 学员当前分组id
+ * @param {String} group_id 调整到的分组id
+ */
+function adjustGroup(student_id, studentCurGroupId, group_id) {
+  if (studentCurGroupId === group_id) return Message.warning('学员已在该分组中');
+  let loadingInstance = Loading.service({ text: '调整分组中...', background: '#fff' });
+  AdjustGroup({
+    task_id,
+    student_list: [
+      {
+        student_id,
+        group_id
+      }
+    ]
+  })
+    .then(() => {
+      Message.success('调整分组成功');
+      emits('getGroupInfo');
+    })
+    .finally(() => {
+      loadingInstance.close();
+    });
 }
 </script>
 

+ 61 - 0
src/views/new_live/common/SinglePopover.vue

@@ -0,0 +1,61 @@
+<template>
+  <el-popover
+    v-model="isShow"
+    popper-class="student-item-operation"
+    trigger="click"
+    title="移动到..."
+    placement="bottom"
+  >
+    <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="isShow = false"> 取消 </el-button>
+        <el-button class="button-confirm" @click="confirm">确定</el-button>
+      </div>
+    </div>
+    <svg-icon slot="reference" icon-class="live-mobile-grouping" />
+  </el-popover>
+</template>
+
+<script>
+export default {
+  name: 'SinglePopover'
+};
+</script>
+
+<script setup>
+import { ref, watch } from 'vue';
+
+const props = defineProps({
+  adjustGroupList: {
+    type: Array,
+    required: true
+  },
+  adjustGroup: {
+    type: Function,
+    required: true
+  }
+});
+
+let isShow = ref(false);
+let groupId = ref('');
+
+watch(isShow, (val) => {
+  if (val && groupId.value.length <= 0) groupId.value = props.adjustGroupList?.[0].group_id;
+});
+
+function confirm() {
+  if (groupId.value.length === 0) return;
+  isShow.value = false;
+  props.adjustGroup(groupId.value);
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 6 - 3
src/views/new_live/common/common.js

@@ -423,7 +423,8 @@ export function useInit(initListener, initData) {
     course_name: '',
     teacher_name: '',
     student_count: 0,
-    student_connection_info: {}
+    student_connection_info: {},
+    is_enable_whiteboard: false
   });
   // 连麦
   let connect = ref(false);
@@ -444,7 +445,8 @@ export function useInit(initListener, initData) {
         course_name,
         teacher_name,
         student_count,
-        student_connection_info
+        student_connection_info,
+        is_enable_whiteboard: isWhiteboard
       }) => {
         roomInfo.value = {
           room_id,
@@ -454,7 +456,8 @@ export function useInit(initListener, initData) {
           course_name,
           teacher_name,
           student_count,
-          student_connection_info
+          student_connection_info,
+          is_enable_whiteboard: isWhiteboard === 'true'
         };
         connectStudent.value = student_connection_info;
         if (student_connection_info.connection_status === 1) {

+ 8 - 1
src/views/new_live/student/index.vue

@@ -79,7 +79,7 @@ export default {
 </script>
 
 <script setup>
-import { ref, computed, inject, provide, onBeforeUnmount } from 'vue';
+import { ref, watch, computed, inject, provide, onBeforeUnmount } from 'vue';
 import { useRoute } from 'vue-router/composables';
 import { Message } from 'element-ui';
 import { useInit, useChat, useMemberList, useLive } from '../common/common';
@@ -266,6 +266,13 @@ const { callLoading, connect, connectStudent, material_list, roomInfo, student_l
   }
 );
 
+watch(
+  () => roomInfo.value.is_enable_whiteboard,
+  (val) => {
+    isShowDraw.value = val;
+  }
+);
+
 const { handsDown, inviteAccept } = useConnect({
   roomInfo,
   room_user_id,

+ 3 - 4
src/views/new_live/teacher/StreamList.vue

@@ -2,7 +2,7 @@
   <div v-show="isGroup" class="stream">
     <div class="rotation-list">
       <div class="arrow-left">
-        <el-image :src="require('@/assets/live/arrow-left.png')" @click="changeCurPage(curPage - 1, 'pre')" />
+        <el-image :src="require('@/assets/live/arrow-left.png')" @click="changeCurPage(curPage - 1)" />
       </div>
 
       <div class="stream-list">
@@ -70,7 +70,7 @@
       </div>
 
       <div class="arrow-right">
-        <el-image :src="require('@/assets/live/arrow-right.png')" @click="changeCurPage(curPage + 1, 'next')" />
+        <el-image :src="require('@/assets/live/arrow-right.png')" @click="changeCurPage(curPage + 1)" />
       </div>
     </div>
 
@@ -87,7 +87,6 @@ export default {
 <script setup>
 import { useTeacherLiveRtc } from './live';
 import { useLive } from '../common/common';
-import { Message } from 'element-ui';
 
 const { kickOut } = useTeacherLiveRtc();
 const { sendPublishMessage } = useLive();
@@ -128,7 +127,7 @@ defineProps({
 
 defineEmits(['searchStudentName']);
 
-function changeCurPage(page, type) {}
+function changeCurPage(page) {}
 </script>
 
 <style lang="scss" scoped>

+ 2 - 2
src/views/new_live/teacher/group.js

@@ -1,8 +1,7 @@
-import { ref, watch } from 'vue';
+import { ref, watch, provide } from 'vue';
 import {
   GetLiveRoomInfo,
   StopGroup,
-  GetGroupInfo_Teacher,
   CreateEnterLiveRoomSession,
   GetMyGroupInfo_Teacher,
   GetGroupStatus
@@ -251,6 +250,7 @@ export function useDownloadSDK(fn, fnData) {
 export function useGroupInit(_downloadWebSDK, GetGroupInfo) {
   const route = useRoute();
   let task_id = route.query.task_id;
+  provide('task_id', task_id);
 
   GetGroupInfo();
   let noStreamList = ref([]); // 无远程流学员列表

+ 19 - 3
src/views/new_live/teacher/group.vue

@@ -2,7 +2,12 @@
   <BaseLive :room-info="roomInfo">
     <div class="group">
       <div class="group-main">
-        <GroupList :is-group="!isGroup" :group-list="group_list" @enterGroup="enterGroup" />
+        <GroupList
+          :is-group="!isGroup"
+          :group-list="group_list"
+          @enterGroup="enterGroup"
+          @getGroupInfo="getGroupInfo"
+        />
         <StreamList
           :is-group="isGroup"
           :stream-list="streamList"
@@ -76,7 +81,6 @@ const route = useRoute();
 let task_id = route.query.task_id; // 任务id
 const $t = inject('$t');
 
-let marginLeft = ref(0);
 let isTeacherStream = ref(false);
 
 let roomData = ref({
@@ -130,7 +134,6 @@ let group_list = ref([]); // 小组成员列表
 function getGroupInfo() {
   GetGroupInfo_Teacher({ task_id }).then(({ live_room_sys_user_id: liveId, group_list: gList }) => {
     group_list.value = gList;
-
     live_room_sys_user_id.value = liveId;
   });
 }
@@ -153,6 +156,19 @@ function enterGroup(_group_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: '陈秒(学生)'
+      //   });
+      // }
     });
 }
 

+ 19 - 32
src/views/new_live/teacher/index.vue

@@ -192,15 +192,16 @@ export default {
 </script>
 
 <script setup>
-import { ref, computed, provide, inject } from 'vue';
+import { ref, computed, watch, provide, inject } from 'vue';
 import { CloseLiveRoom } from '@/api/live';
 import { Message, MessageBox } from 'element-ui';
 import { useRoute, useRouter } from 'vue-router/composables';
 import { useInit, useChat, useMemberList, useLive } from '../common/common';
-import { useTeacherLiveRtc, useInitListener, useConnect, useGroup, useWhiteboard } from './live';
-import store from '@/store';
+import { useTeacherLiveRtc, useInitListener, useConnect, useGroup, useWhiteboard, usePushMaterial } from './live';
 import { app } from '@/store/mutation-types';
 
+import store from '@/store';
+
 import BaseLive from '../common/BaseLive.vue';
 import SelectDevice from '../common/SelectDevice';
 import ChatPage from '../common/ChatList.vue';
@@ -215,25 +216,6 @@ const task_id = route.query.task_id;
 provide('task_id', task_id);
 const $t = inject('$t');
 
-let dialogVisible = ref(false); // 推送资料
-function dialogClose() {
-  dialogVisible.value = false;
-}
-
-function dialogPush() {
-  dialogVisible.value = false;
-  dialogVisibleComplete.value = true;
-}
-
-let dialogVisibleComplete = ref(false); // 学员完成
-let materialId = ref('');
-let materialType = ref('');
-function dialogCompleteClose() {
-  dialogVisibleComplete.value = false;
-  materialId.value = '';
-  materialType.value = '';
-}
-
 let remoteStreamType = ref(-1);
 let roomData = ref({
   desc: '直播间标题',
@@ -252,18 +234,10 @@ let roomData = ref({
 let speakData = ref({});
 let roomContext = ref({});
 
-let isShowDraw = ref(false);
 const innerWidth = window.innerWidth;
-function changeWhiteboard() {
-  if (connect.value || callLoading.value) {
-    Message.warning('正在连线中');
-    return;
-  }
-  isShowDraw.value = !isShowDraw.value;
-  sendPublishMessage({ type: 'changeWhiteboard', isShow: isShowDraw.value });
-}
 
-const { changeDraw, curColor, drawColorList, drawThicknessList } = useWhiteboard();
+const { dialogClose, dialogCompleteClose, dialogPush, dialogVisible, dialogVisibleComplete, materialId, materialType } =
+  usePushMaterial();
 // 聊天
 let cPage = ref();
 const { chatList, chatShow, sendMsg, toggle: chatToggle } = useChat(cPage);
@@ -422,6 +396,19 @@ const {
   hasVideo
 });
 
+watch(
+  () => roomInfo.value.is_enable_whiteboard,
+  (val) => {
+    changeWhiteboard(val);
+  }
+);
+
+const { changeDraw, changeWhiteboard, isShowDraw, curColor, drawColorList, drawThicknessList } = useWhiteboard(
+  task_id,
+  connect,
+  callLoading
+);
+
 const { handsDown, invite } = useConnect({
   connect,
   callLoading,

+ 60 - 3
src/views/new_live/teacher/live.js

@@ -1,7 +1,13 @@
 import { ref, reactive } from 'vue';
 import { Message, Loading } from 'element-ui';
 import { useLive, useCommonLive, rtc } from '../common/common';
-import { DealStudentConnection, GetStudentInfo_Connection, GetLiveRoomInfo, StartGroup } from '@/api/live';
+import {
+  DealStudentConnection,
+  GetStudentInfo_Connection,
+  GetLiveRoomInfo,
+  StartGroup,
+  EnableWhiteboard
+} from '@/api/live';
 import { useRoute } from 'vue-router/composables';
 
 import i18n from '@/locales/i18n';
@@ -645,7 +651,12 @@ export function useGroup(task_id, router) {
   };
 }
 
-export function useWhiteboard() {
+/**
+ * 白板
+ * @param {String} task_id
+ */
+export function useWhiteboard(task_id, connect, callLoading) {
+  let isShowDraw = ref(false); // 是否显示白板
   let curColor = ref('#343434');
   let drawColorList = ['#FF4747', '#343434', '#628EFF', '#FFCA0E'];
   let drawThicknessList = ['1', '3', '5'];
@@ -659,10 +670,56 @@ export function useWhiteboard() {
     }
   }
 
+  function changeWhiteboard(bool) {
+    if (connect.value || callLoading.value) {
+      Message.warning('正在连线中');
+      return;
+    }
+    isShowDraw.value = typeof bool === 'boolean' ? bool : !isShowDraw.value;
+    sendPublishMessage({ type: 'changeWhiteboard', isShow: isShowDraw.value });
+    EnableWhiteboard({ task_id, is_enable_whiteboard: isShowDraw.value });
+  }
+
   return {
+    isShowDraw,
     curColor,
     drawColorList,
     drawThicknessList,
-    changeDraw
+    changeDraw,
+    changeWhiteboard
+  };
+}
+
+/**
+ * 推送资料与完成列表
+ */
+export function usePushMaterial() {
+  let dialogVisible = ref(false);
+  function dialogClose() {
+    dialogVisible.value = false;
+  }
+
+  let dialogVisibleComplete = ref(false); // 学员完成
+  function dialogPush() {
+    dialogVisible.value = false;
+    dialogVisibleComplete.value = true;
+  }
+
+  let materialId = ref('');
+  let materialType = ref('');
+  function dialogCompleteClose() {
+    dialogVisibleComplete.value = false;
+    materialId.value = '';
+    materialType.value = '';
+  }
+
+  return {
+    dialogVisible,
+    dialogClose,
+    dialogPush,
+    dialogVisibleComplete,
+    dialogCompleteClose,
+    materialId,
+    materialType
   };
 }

+ 4 - 1
src/views/teacher/create_course/step_three/components/data/TaskClassify.js

@@ -1,6 +1,7 @@
-import { ref, provide } from 'vue';
+import { ref, inject, provide } from 'vue';
 import { addTaskList } from './TaskData';
 import { MORE_NAME, taskClassify } from './constant';
+import { Message } from 'element-ui';
 
 /**
  * 获取教学类型某个属性对应值
@@ -52,7 +53,9 @@ export function useSelectTaskClassify(curTaskTypeObj) {
    * 选择任务教学类型
    * @param {Number} teaching_type 任务教学类型
    */
+  let cs_item_id = inject('cs_item_id');
   function selectTaskClassify(teaching_type) {
+    if (!cs_item_id.value) return Message.warning('请先创建课节');
     if (!teaching_type) return;
     visible.value = false;
     selectFn ? selectFn(teaching_type) : addTask(teaching_type);

+ 1 - 0
src/views/teacher/create_course/step_three/index.js

@@ -37,6 +37,7 @@ export function useCSItem(content) {
     GetCSItemListByCourseID({ course_id: id }).then(({ cs_item_list }) => {
       if (cs_item_list.length <= 0) {
         curCSItemIndex.value = 0;
+        csItemList.value = null;
         return;
       }
       if (curCSItemIndex.value >= cs_item_list.length) curCSItemIndex.value = cs_item_list.length - 1;