Procházet zdrojové kódy

update 新版直播

dusenyao před 2 roky
rodič
revize
ea3a558556

+ 7 - 7
package-lock.json

@@ -18,7 +18,7 @@
         "element-ui": "^2.15.12",
         "gcls-book-question-ui": "file:../gcls-book-question-ui-0.1.0.tgz",
         "jquery": "^3.6.3",
-        "js-base64": "^3.7.4",
+        "js-base64": "^3.7.5",
         "js-cookie": "^3.0.1",
         "jsplumb": "^2.15.6",
         "md5": "^2.3.0",
@@ -13927,9 +13927,9 @@
       "license": "MIT"
     },
     "node_modules/js-base64": {
-      "version": "3.7.4",
-      "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.4.tgz",
-      "integrity": "sha512-wpM/wi20Tl+3ifTyi0RdDckS4YTD4Lf953mBRrpG8547T7hInHNPEj8+ck4gB8VDcGyeAWFK++Wb/fU1BeavKQ=="
+      "version": "3.7.5",
+      "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.5.tgz",
+      "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA=="
     },
     "node_modules/js-beautify": {
       "version": "1.14.4",
@@ -33339,9 +33339,9 @@
       "integrity": "sha512-JiDODCElVHGrFyjGYwYyNi7zCbKk9va9C77w+zCPMmi4C6ix7zsX2h3ddHugmo4dOTOTCym9++b/wVW9nC0IaA=="
     },
     "js-base64": {
-      "version": "3.7.4",
-      "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.4.tgz",
-      "integrity": "sha512-wpM/wi20Tl+3ifTyi0RdDckS4YTD4Lf953mBRrpG8547T7hInHNPEj8+ck4gB8VDcGyeAWFK++Wb/fU1BeavKQ=="
+      "version": "3.7.5",
+      "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.5.tgz",
+      "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA=="
     },
     "js-beautify": {
       "version": "1.14.4",

+ 1 - 1
package.json

@@ -23,7 +23,7 @@
     "element-ui": "^2.15.12",
     "gcls-book-question-ui": "file:../gcls-book-question-ui-0.1.0.tgz",
     "jquery": "^3.6.3",
-    "js-base64": "^3.7.4",
+    "js-base64": "^3.7.5",
     "js-cookie": "^3.0.1",
     "jsplumb": "^2.15.6",
     "md5": "^2.3.0",

+ 6 - 0
src/icons/svg/live/live-mobile-grouping.svg

@@ -0,0 +1,6 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6 10.3333H12.6667V1.66663" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9.99967 7H3.33301V14.3333" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M14.667 3.66663L12.667 1.66663L10.667 3.66663" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M5.33301 12.3334L3.33301 14.3334L1.33301 12.3334" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 71 - 8
src/views/new_live/common/GroupList.vue

@@ -9,14 +9,14 @@
       }"
     >
       <div
-        v-for="{ student_list, group_id, group_num } in gList[curPage]"
+        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">
           <div>
-            <span>{{ `分组${group_num}` }}</span>
+            <span>{{ `分组${group_num}${is_example === 'true' ? '(示范组)' : ''}` }}</span>
           </div>
           <div>
             <span class="people-number">
@@ -31,12 +31,27 @@
             :key="student_id"
             class="student-item"
           >
-            <el-avatar :size="24" :src="student_image_url" /><span>{{ student_name }}</span>
+            <div class="student-item-left">
+              <el-avatar :size="24" :src="student_image_url" />
+              <span>{{ student_name }}</span>
+            </div>
+            <div class="student-item-right">
+              <svg-icon icon-class="live-mobile-grouping" />
+            </div>
           </div>
         </div>
       </div>
     </div>
-    <div class="group-circle"></div>
+    <div class="group-circle">
+      <template v-if="gList.length > 1">
+        <span
+          v-for="i in gList.length"
+          :key="i"
+          :class="['circle-item', { active: curPage + 1 === i }]"
+          @click="changePage(i - 1)"
+        ></span>
+      </template>
+    </div>
   </div>
 </template>
 
@@ -47,7 +62,7 @@ export default {
 </script>
 
 <script setup>
-import { ref, computed } from 'vue';
+import { ref, computed, watch } from 'vue';
 
 const props = defineProps({
   groupList: {
@@ -63,6 +78,10 @@ const props = defineProps({
 defineEmits(['enterGroup']);
 
 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);
@@ -73,13 +92,17 @@ let isTowLine = computed(() => {
 
   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 - 68 - 8 - 100) / 44);
   const row = Math.floor((window.innerWidth - 116) / 342);
 
   let groupNum = props.groupList.length;
-  let groupSize = props.groupList[0]?.student_list.length;
+  let groupSize = 0; // 每组最多人数
+  props.groupList.forEach(({ student_list }) => {
+    groupSize = Math.max(groupSize, student_list.length);
+  });
   let groupRowNum = Math.ceil(groupSize / col); // 每组需要分成几行
   let gNum = Math.max(Math.floor(row / groupRowNum), 1); // 可以显示几个分组
   let isTowLine = groupRowNum === 1 && col_2 / groupSize > 2 && groupNum > row; // 是否需要两行显示
@@ -95,6 +118,9 @@ let gList = computed(() => {
   }
   return list;
 });
+watch(gList, (newVal) => {
+  if (curPage.value > newVal.length) curPage.value = newVal.length - 1;
+});
 </script>
 
 <style lang="scss" scoped>
@@ -152,14 +178,35 @@ let gList = computed(() => {
 
         .student-item {
           display: flex;
-          column-gap: 8px;
           align-items: center;
+          justify-content: space-between;
           min-width: 300px;
           height: 40px;
           padding: 8px 16px;
           border-radius: 4px;
           writing-mode: horizontal-tb;
 
+          &-left {
+            display: flex;
+            column-gap: 8px;
+            align-items: center;
+          }
+
+          &-right {
+            display: none;
+            align-items: center;
+            justify-content: center;
+            width: 42px;
+            padding: 4px;
+            cursor: pointer;
+            background-color: #fff;
+            border-radius: 15px;
+          }
+
+          &:hover .student-item-right {
+            display: flex;
+          }
+
           &:hover {
             background-color: #f2f4f7;
           }
@@ -169,7 +216,23 @@ let gList = computed(() => {
   }
 
   .group-circle {
-    height: 8px;
+    display: flex;
+    column-gap: 4px;
+    align-items: center;
+    justify-content: center;
+    padding-bottom: 32px;
+
+    .circle-item {
+      width: 8px;
+      height: 8px;
+      cursor: pointer;
+      background-color: #d9d9d9;
+      border-radius: 50%;
+
+      &.active {
+        background-color: #4d4d4d;
+      }
+    }
   }
 }
 </style>

+ 9 - 11
src/views/new_live/common/common.js

@@ -216,6 +216,13 @@ export function useLive() {
     });
   }
 
+  function drawChange(action, value) {
+    rtc.value.drawChange({
+      action,
+      value
+    });
+  }
+
   return {
     rtc,
     getLiveStat,
@@ -235,7 +242,8 @@ export function useLive() {
     publishStream,
     updateMcResult,
     unSubscribeStream,
-    createData
+    createData,
+    drawChange
   };
 }
 
@@ -304,16 +312,6 @@ export function useMemberList() {
 }
 
 /**
- * 变更画笔
- */
-export function drawChange(action, value) {
-  rtc.value.drawChange({
-    action,
-    value
-  });
-}
-
-/**
  * 创建 script 和 link 标签
  */
 export function useCreateLink() {

+ 20 - 13
src/views/new_live/student/components/StudentStream.vue

@@ -13,23 +13,26 @@
           </div>
         </div>
 
-        <div v-show="isLocalStream" class="stream-list-item">
-          <div id="group-local" class="group-box"></div>
-          <div class="live-wrapper-stream">
-            <span>{{ studentSelf.student_name }}</span>
+        <!-- 学生自己 -->
+        <template v-if="!isAudience">
+          <div v-show="isLocalStream" class="stream-list-item">
+            <div id="group-local" class="group-box"></div>
+            <div class="live-wrapper-stream">
+              <span>{{ studentSelf.student_name }}</span>
+            </div>
           </div>
-        </div>
 
-        <div v-show="!isLocalStream" class="stream-list-item no-stream">
-          <el-avatar icon="el-icon-user" :src="studentSelf.student_image_url" :size="122" />
-          <span class="student-card">学生</span>
+          <div v-show="!isLocalStream" class="stream-list-item no-stream">
+            <el-avatar icon="el-icon-user" :src="studentSelf.student_image_url" :size="122" />
+            <span class="student-card">学生</span>
 
-          <div class="live-name">
-            <span class="name nowrap-ellipsis">
-              {{ studentSelf.student_name }}
-            </span>
+            <div class="live-name">
+              <span class="name nowrap-ellipsis">
+                {{ studentSelf.student_name }}
+              </span>
+            </div>
           </div>
-        </div>
+        </template>
 
         <!-- 有流列表 -->
         <div v-for="(item, i) in streamList" :key="item.id()" class="stream-list-item">
@@ -76,6 +79,10 @@ defineProps({
     type: Boolean,
     required: true
   },
+  isAudience: {
+    type: Boolean,
+    required: true
+  },
   isTeacherInGroup: {
     type: Boolean,
     required: true

+ 34 - 13
src/views/new_live/student/group.js

@@ -12,7 +12,8 @@ import {
 
 import i18n from '@/locales/i18n';
 
-const { closeVideo, getHistory, unSubscribeStream, createData, publishStream } = useLive();
+const { closeVideo, getHistory, unSubscribeStream, createData, publishStream, pauseAudio, sendPublishMessage } =
+  useLive();
 
 export let room_id = ref('');
 export let session_id = ref('');
@@ -24,7 +25,8 @@ export let studentSelf = ref({
 export let group_instance_mark = ref(''); // 所在组的实例标记号
 export let audience_list = ref([]); // 旁听学员列表
 export let is_example_group = ref(false); // 是否是示范组
-export let isAudience = ref(false);
+export let isAudience = ref(false); // 是否是旁听学员
+export let room_user_id = ref(''); // 房间用户id
 
 export let isLocalStream = ref(false); // 是否有本地流
 // let hasVideo = ref(false);
@@ -87,6 +89,7 @@ export function useInitListener({ streamList, roomContext, is_teacher_in_group,
     //     console.log(str);
     //   }
     // });
+    if (isAudience.value) return;
     if (studentSelf.value.is_teacher_role_in_room === 'false') {
       rtc.value.handsUp({
         success: (str) => {
@@ -251,6 +254,25 @@ export function useInitListener({ streamList, roomContext, is_teacher_in_group,
   //   }
   //   chatList.value.push(dat);
   // });
+
+  rtc.value.on('publish_message', (data) => {
+    console.log('监听发布消息', data);
+
+    if (data.type === 'estoppel' && room_user_id.value === data.room_user_id) {
+      pauseAudio({
+        streamName: 'picture',
+        success: () => {
+          Message.success(i18n.t('Key435'));
+          sendPublishMessage({ type: 'estoppel-success' });
+        },
+        fail: (str) => {
+          console.log(str);
+          Message.warning(str);
+          sendPublishMessage({ type: 'estoppel-fail' });
+        }
+      });
+    }
+  });
 }
 
 export function useDownloadSDK(fn, fnData) {
@@ -300,7 +322,7 @@ export function useDownloadSDK(fn, fnData) {
       'https://class.csslcloud.net/static/dist/js/classMode.js',
       'https://class.csslcloud.net/static/dist/js/classUpdateChat.js'
     ].forEach((item) => {
-      document.querySelector(`script[src='${item}']`).remove();
+      document.querySelector(`script[src='${item}']`)?.remove();
     });
   }
 
@@ -310,7 +332,6 @@ export function useDownloadSDK(fn, fnData) {
   };
 }
 
-export let room_user_id = ref('');
 export let roomInfo = ref({
   room_id: '',
   video_mode: 1,
@@ -323,10 +344,7 @@ export let roomInfo = ref({
 });
 export let student_list = ref([]); // 直播间学员列表
 export let noStreamList = ref([]); // 无远程流学员列表
-export function useGroupInit(_downloadWebSDK) {
-  const route = useRoute();
-  let task_id = route.query.task_id;
-
+export function useGroupInit(_downloadWebSDK, task_id) {
   GetLiveRoomInfo({ task_id }).then(
     ({ room_id, video_mode, task_name, cs_item_name, course_name, teacher_name, student_count, teacher_image_url }) => {
       roomInfo.value = {
@@ -352,7 +370,10 @@ export function useGroupInit(_downloadWebSDK) {
       audience_list: aList,
       group_instance_mark: gMark
     }) => {
-      const data = sList.find((el) => el.is_self === 'true');
+      let data = sList.find((el) => el.is_self === 'true');
+      if (!data) {
+        data = aList.find((el) => el.is_self === 'true');
+      }
       group_instance_mark.value = gMark;
       session_id.value = data.session_id;
       room_user_id.value = data.room_user_id;
@@ -360,7 +381,7 @@ export function useGroupInit(_downloadWebSDK) {
       room_id.value = rId;
       student_list.value = sList;
       noStreamList.value = sList.filter((item) => item.is_self === 'false');
-      studentSelf.value = sList.find((item) => item.is_self === 'true');
+      studentSelf.value = data;
       audience_list.value = aList;
       _downloadWebSDK();
     }
@@ -405,10 +426,9 @@ export function useStudentGroup(audience) {
           });
         }
 
-        isAudience.value = is_audience === 'true';
-        if (is_audience === 'true') {
+        if (is_audience === 'true' && !isAudience.value) {
+          isAudience.value = is_audience === 'true';
           audience();
-          // <!-- TODO -->
           // router.push({
           //   path: '/live/student/audit',
           //   query: {
@@ -444,6 +464,7 @@ export function useStudentGroup(audience) {
 
   onBeforeUnmount(() => {
     clearInterval(timer);
+    closeVideo('picture');
     StudentExitLiveRoom({ task_id, room_user_id: room_user_id.value });
   });
 

+ 10 - 10
src/views/new_live/student/group.vue

@@ -9,18 +9,18 @@
           :stream-list="streamList"
           :teacher-name="roomInfo.teacher_name"
           :student-self="studentSelf"
+          :is-audience="isAudience"
           @search-student-name="searchStudentName"
         />
-        <MemberList
-          :member-show="memberShow && isAudience"
-          :show-operation="false"
-          :student-list="audience_list"
-          title="旁听列表"
-        />
+        <MemberList :member-show="memberShow" :show-operation="false" :student-list="audience_list" title="旁听列表" />
       </div>
       <div class="group-bottom">
         <div>
-          <div v-show="isAudience" :class="['operation-item', { active: memberShow && isAudience }]" @click="toggle">
+          <div
+            v-show="isAudience || is_example_group"
+            :class="['operation-item', { active: memberShow }]"
+            @click="toggle"
+          >
             <img src="@/assets/live/peoples.png" alt="" />
             <span>旁听列表</span>
           </div>
@@ -95,8 +95,9 @@ watch(streamList, (newVal) => {
 let marginLeft = ref(0);
 
 function audience() {
+  if (isLocalStream.value) closeVideo('picture');
   removeWebSDK();
-  useGroupInit(downloadWebSDK);
+  useGroupInit(downloadWebSDK, task_id);
 }
 
 const { exitRoom } = useStudentGroup(audience);
@@ -114,7 +115,7 @@ const { downloadWebSDK, removeWebSDK } = useDownloadSDK(useInitListener, {
   exitRoom
 });
 
-useGroupInit(downloadWebSDK);
+useGroupInit(downloadWebSDK, task_id);
 
 function searchStudentName(id) {
   const uid = id.split('-')[0];
@@ -135,7 +136,6 @@ onBeforeUnmount(() => {
     },
     fail: (data) => {
       console.log('下麦失败', data);
-      closeVideo('picture');
     }
   });
   streamList.value.forEach((item) => {

+ 3 - 9
src/views/new_live/student/index.vue

@@ -79,7 +79,7 @@ export default {
 </script>
 
 <script setup>
-import { ref, computed, inject, provide, nextTick, onBeforeUnmount, onMounted } from 'vue';
+import { ref, 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';
@@ -421,14 +421,8 @@ function toggle(type) {
 </style>
 
 <style lang="scss">
-#draw {
-  width: 100% !important;
-  height: 100% !important;
-  margin-top: 0 !important;
-}
-
-#draw-parent,
-#draw-box {
+#draw-parent {
   height: 100%;
+  margin: auto;
 }
 </style>

+ 15 - 2
src/views/new_live/teacher/StreamList.vue

@@ -56,7 +56,7 @@
             <el-popover popper-class="group-operation" trigger="click" placement="bottom">
               <div class="operation">
                 <div class="operation-item">移动到...</div>
-                <div class="operation-item">
+                <div class="operation-item" @click="estoppel(room_user_id)">
                   <span>禁言</span>
                 </div>
                 <div class="operation-item" @click="kickOut(room_user_id)">
@@ -85,10 +85,23 @@ export default {
 </script>
 
 <script setup>
-import { useRoute } from 'vue-router/composables';
 import { useTeacherLiveRtc } from './live';
+import { useLive } from '../common/common';
+import { Message } from 'element-ui';
 
 const { kickOut } = useTeacherLiveRtc();
+const { sendPublishMessage } = useLive();
+
+/**
+ * 禁言
+ * @param {String} room_user_id
+ */
+function estoppel(room_user_id) {
+  sendPublishMessage({
+    type: 'estoppel',
+    room_user_id
+  });
+}
 
 defineProps({
   isTeacherStream: {

+ 14 - 21
src/views/new_live/teacher/group.js

@@ -167,6 +167,18 @@ export function useInitListener({ roomData, streamList, roomContext, liveStat, s
     console.log('用户退出房间通知其他人员事件', data);
   });
 
+  rtc.value.on('publish_message', (data) => {
+    console.log('监听发布消息', data);
+
+    if (data.type === 'estoppel-success') {
+      Message.success('禁言成功');
+    }
+
+    if (data.type === 'estoppel-fail') {
+      Message.warning('禁言失败');
+    }
+  });
+
   /**
    * 排麦监听事件
    */
@@ -236,29 +248,11 @@ export function useDownloadSDK(fn, fnData) {
   };
 }
 
-export function useGroupInit(_downloadWebSDK) {
+export function useGroupInit(_downloadWebSDK, GetGroupInfo) {
   const route = useRoute();
   let task_id = route.query.task_id;
 
-  let group_list = ref([]); // 小组成员列表
-  /**
-   * 得到分组讨论信息(教师端)
-   */
-  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;
-
-    const list = [];
-    group_list.value.forEach(({ group_id, student_list }) => {
-      student_list.forEach((item) => {
-        list.push({
-          group_id,
-          ...item
-        });
-      });
-    });
-  });
-
+  GetGroupInfo();
   let noStreamList = ref([]); // 无远程流学员列表
   let student_list = ref([]);
   let isGroup = ref(false);
@@ -335,7 +329,6 @@ export function useGroupInit(_downloadWebSDK) {
   );
 
   return {
-    group_list,
     live_room_sys_user_id,
     room_id,
     session_id,

+ 27 - 15
src/views/new_live/teacher/group.vue

@@ -22,7 +22,7 @@
       <div class="group-bottom" :style="{ 'justify-content': isGroup ? 'space-between' : 'center' }">
         <template v-if="isGroup">
           <button v-show="!isAudit" class="group-button blue" @click="setCurGroupToExample_Teacher">设为示范组</button>
-          <div :class="['operation-item', { active: memberShow && isAudit }]" @click="toggle">
+          <div v-show="isAudit" :class="['operation-item', { active: memberShow && isAudit }]" @click="toggle">
             <img src="@/assets/live/peoples.png" alt="" />
             <span>旁听列表</span>
           </div>
@@ -43,15 +43,24 @@ export default {
 </script>
 
 <script setup>
+import { ref, inject, watch, nextTick, onBeforeUnmount } from 'vue';
 import {
   GetMyGroupInfo_Teacher,
   JoinGroup_Teacher,
   ExitCurGroup_Teacher,
-  SetCurGroupToExample_Teacher
+  SetCurGroupToExample_Teacher,
+  GetGroupInfo_Teacher
 } from '@/api/live';
-import { ref, inject, watch, nextTick, onBeforeUnmount } from 'vue';
 import { useRoute } from 'vue-router/composables';
-import { useGroupInit, useInitListener, useGroup, useDownloadSDK, room_id, session_id } from './group';
+import {
+  useGroupInit,
+  useInitListener,
+  useGroup,
+  useDownloadSDK,
+  room_id,
+  session_id,
+  live_room_sys_user_id
+} from './group';
 import { useLive, useMemberList } from '../common/common';
 import { Message, Loading } from 'element-ui';
 
@@ -114,17 +123,19 @@ const { downloadWebSDK, removeWebSDK } = useDownloadSDK(useInitListener, {
   isTeacherStream
 });
 
-const {
-  audience_list,
-  group_id,
-  group_list,
-  isAudit,
-  isGroup,
-  noStreamList,
-  roomInfo,
-  student_list,
-  searchStudentName
-} = useGroupInit(downloadWebSDK);
+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;
+  });
+}
+
+const { audience_list, group_id, isAudit, isGroup, noStreamList, roomInfo, student_list, searchStudentName } =
+  useGroupInit(downloadWebSDK, getGroupInfo);
 const { stopGroup } = useGroup();
 
 // 加入分组讨论
@@ -169,6 +180,7 @@ function setCurGroupToExample_Teacher() {
     .then(() => {
       isAudit.value = true;
       Message.success($t('Key417'));
+      getGroupInfo();
       return GetMyGroupInfo_Teacher({ task_id });
     })
     .then(({ audience_list: aList }) => {

+ 39 - 18
src/views/new_live/teacher/index.vue

@@ -30,8 +30,39 @@
         </div>
 
         <!-- 画板 -->
-        <div class="draw" :style="{ left: isShowDraw ? '0' : '-3000px' }">
-          <div id="draw-parent"></div>
+        <div class="draw" :style="{ left: isShowDraw ? '0' : `-${innerWidth}px` }">
+          <div id="draw-parent">
+            <div v-show="isDrawSetting" class="draw-setting">
+              <span class="brush-shape" @click="changeDraw('type', 2)">
+                <svg-icon icon-class="brush-shape" />
+              </span>
+              <!-- 画笔颜色 -->
+              <span
+                v-for="item in drawColorList"
+                :key="item"
+                :class="['draw-color', item === curColor ? 'current' : '']"
+                :style="{ 'background-color': item }"
+                @click="changeDraw('color', item)"
+              ></span>
+              <span
+                v-for="item in drawThicknessList"
+                :key="item"
+                class="draw-thickness"
+                @click="changeDraw('thickNess', item)"
+              >
+                <span :style="{ width: item * 2 + 'px', height: item * 2 + 'px' }"></span>
+              </span>
+              <span class="eraser" @click="changeDraw('type', 9)">
+                <svg-icon icon-class="back" />
+              </span>
+              <span class="eraser" @click="changeDraw('type', 10)">
+                <svg-icon icon-class="eraser" />
+              </span>
+              <span class="brush-clear" @click="changeDraw('type', 0)">
+                <svg-icon icon-class="clear" />
+              </span>
+            </div>
+          </div>
         </div>
 
         <!-- 聊天 -->
@@ -161,12 +192,12 @@ export default {
 </script>
 
 <script setup>
-import { ref, computed, provide, inject, nextTick } from 'vue';
+import { ref, computed, 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 } from './live';
+import { useTeacherLiveRtc, useInitListener, useConnect, useGroup, useWhiteboard } from './live';
 import store from '@/store';
 import { app } from '@/store/mutation-types';
 
@@ -221,12 +252,8 @@ let roomData = ref({
 let speakData = ref({});
 let roomContext = ref({});
 
-let isDrawSetting = ref(false);
-let curColor = ref('#343434');
-let drawColorList = ['#FF4747', '#343434', '#628EFF', '#FFCA0E'];
-let drawThicknessList = ['1', '3', '5'];
-
 let isShowDraw = ref(false);
+const innerWidth = window.innerWidth;
 function changeWhiteboard() {
   if (connect.value || callLoading.value) {
     Message.warning('正在连线中');
@@ -235,7 +262,7 @@ function changeWhiteboard() {
   isShowDraw.value = !isShowDraw.value;
   sendPublishMessage({ type: 'changeWhiteboard', isShow: isShowDraw.value });
 }
-
+const { changeDraw, curColor, drawColorList, drawThicknessList, isDrawSetting } = useWhiteboard();
 // 聊天
 let cPage = ref();
 const { chatList, chatShow, sendMsg, toggle: chatToggle } = useChat(cPage);
@@ -593,14 +620,8 @@ function closeLiveRoom() {
   }
 }
 
-#draw {
-  width: 100% !important;
-  height: 100% !important;
-  margin-top: 0 !important;
-}
-
-#draw-parent,
-#draw-box {
+#draw-parent {
   height: 100%;
+  margin: auto;
 }
 </style>

+ 28 - 2
src/views/new_live/teacher/live.js

@@ -1,4 +1,4 @@
-import { reactive } from 'vue';
+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';
@@ -15,7 +15,8 @@ const {
   sendPublishMessage,
   handsDown: commonHandsDown,
   updateMcResult,
-  roomUpdate
+  roomUpdate,
+  drawChange
 } = useLive();
 const { studentExitLiveRoom } = useCommonLive();
 
@@ -643,3 +644,28 @@ export function useGroup(task_id, router) {
     startGroup
   };
 }
+
+export function useWhiteboard() {
+  let isDrawSetting = ref(false);
+  let curColor = ref('#343434');
+  let drawColorList = ['#FF4747', '#343434', '#628EFF', '#FFCA0E'];
+  let drawThicknessList = ['1', '3', '5'];
+  // 画笔变更
+  function changeDraw(action, value) {
+    if (action === 'color') {
+      drawChange(action, parseInt(Number(value.replace('#', '0x'))));
+      curColor.value = value;
+    } else {
+      drawChange(action, value);
+    }
+    isDrawSetting.value = false;
+  }
+
+  return {
+    isDrawSetting,
+    curColor,
+    drawColorList,
+    drawThicknessList,
+    changeDraw
+  };
+}