lex-xin 3 年之前
父節點
當前提交
82fcbc5981

+ 3 - 0
src/components/live-broadcast/action-bar.module.less

@@ -81,6 +81,9 @@
     .el-dropdown-menu__item {
       color: inherit;
     }
+    .is-disabled {
+      opacity: 0.5;
+    }
 
     .el-dropdown-menu__item:not(.is-disabled):focus {
       background-color: var(--live-main-color);

+ 14 - 5
src/components/live-broadcast/action-bar.tsx

@@ -21,11 +21,11 @@ export default defineComponent({
   computed: {
     isCameraDisabled() {
       // @ts-ignore
-      return this.barStatus.camera && runtime.deviceStatus.camera !== 'denied'
+      return this.barStatus.camera && runtime.deviceStatus.camera !== 'denied' && runtime.cameras.length
     },
     isMicrophoneDisabled() {
       // @ts-ignore
-      const isDisabled = this.barStatus.microphone && runtime.deviceStatus.microphone !== 'denied'
+      const isDisabled = this.barStatus.microphone && runtime.deviceStatus.microphone !== 'denied' && runtime.microphones.length
       return isDisabled
     },
     isVolumeDisabled() {
@@ -62,12 +62,14 @@ export default defineComponent({
               />
               <ElDropdown
                 placement="top"
+                // @ts-ignore
+                disabled={runtime.cameras.length === 0}
                 onCommand={RuntimeUtils.setSelectCamera}
                 // @ts-ignore
                 vSlots={{
                   dropdown: () => (
                     <ElDropdownMenu>
-                      {runtime.cameras.map(item => (<ElDropdownItem command={item}>{item.label}</ElDropdownItem>))}
+                      {runtime.cameras.map(item => (<ElDropdownItem disabled={item === runtime.selectedCamera} command={item}>{item.label}</ElDropdownItem>))}
                     </ElDropdownMenu>
                   )
                 }}
@@ -89,7 +91,12 @@ export default defineComponent({
               <SvgIcon
                 onClick={() => {
                   this.barStatus.volume = !this.barStatus.volume;
-                  this.volume = 0;
+                  if(!this.barStatus.volume) {
+                    sessionStorage.getItem('volume') && this.volumeChange(Number(sessionStorage.getItem('volume')))
+                  } else {
+                    sessionStorage.setItem('volume', this.volume.toString())
+                    this.volumeChange(0)
+                  }
                 }}
                 name={this.isVolumeDisabled ? 'bar-volume-disabled' : 'bar-volume'}
                 style={{
@@ -168,13 +175,15 @@ export default defineComponent({
               />
               <ElDropdown
                 placement="top-start"
+                // @ts-ignore
+                disabled={runtime.microphones.length === 0}
                 popper-options={{ boundariesElement: '#action-bar', gpuAcceleration: false }}
                 onCommand={RuntimeUtils.setSelectMicrophone}
                 // @ts-ignore
                 vSlots={{
                   dropdown: () => (
                     <ElDropdownMenu>
-                      {runtime.microphones.map(item => (<ElDropdownItem command={item}>{item.label}</ElDropdownItem>))}
+                      {runtime.microphones.map(item => (<ElDropdownItem disabled={item === runtime.selectedMicrophone} command={item}>{item.label}</ElDropdownItem>))}
                     </ElDropdownMenu>
                   )
                 }}

+ 7 - 10
src/components/live-message/model/index.module.less

@@ -19,20 +19,16 @@
   justify-content: space-between;
   align-items: center;
   padding-top: 3px;
-  padding-bottom: 8px;
   .userName {
-    font-size: 16px;
+    font-size: 14px;
     line-height: 22px;
-    display: flex;
+    // display: flex;
     align-items: center;
     font-weight: 500;
   }
   .name-style {
     line-height: 24px;
-    background: #575B61;
-    border-radius: 12px;
-    color: #fff;
-    padding: 0 8px;
+    color: var(--live-text-color);
   }
   .rightTime {
     font-size: 12px;
@@ -40,9 +36,10 @@
   :global {
     .el-tag--default {
       border-radius: 20px;
-      margin-left: 8px;
+      margin-right: 8px;
+      height: 20px;
+
       // padding: 0 12px;
-      // height: 20px;
       // line-height: 20px;
       // color: var(--live-color);
       // background: var(--message-color);
@@ -51,7 +48,7 @@
   }
 }
 .itemText {
-  font-size: 14px;
+  font-size: 15px;
   line-height: 20px;
   &.active {
     color: var(--live-text-color);

+ 37 - 4
src/components/live-message/model/join-model.tsx

@@ -33,13 +33,25 @@ export default defineComponent({
     },
   },
   mounted() {
+    // userRoomType
+    // 1 普通
+    // 2 老师邀请
+    // 3 学生申请
+    // 4 连麦中
     event.on(LIVE_EVENT_MESSAGE['RC:Chatroom:SeatApply'], this.onSeatApply);
     event.on(LIVE_EVENT_MESSAGE['RC:Chatroom:SeatResponse'], this.onSeatApply);
     event.on(LIVE_EVENT_MESSAGE['RM:RTC:UserLeave'], this.onSeatApply);
     event.on(LIVE_EVENT_MESSAGE['RM:RTC:SwitchRole'], this.onSwitchRole);
+    event.on(LIVE_EVENT_MESSAGE['RC:Chatroom:Leave'], this.onLeave);
   },
   methods: {
+    onLeave(value: any) {
+      // console.log(value)
+      RuntimeModelUtils.removeJoin(value.userId)
+      RuntimeModelUtils.removeLook(value.userId)
+    },
     onSeatApply(evt: any) {
+      console.log(evt, 'onSeatApply joinModel')
       if (Array.isArray(evt)) {
         for (const id of evt) {
           console.log('onSeatApply', id)
@@ -47,19 +59,31 @@ export default defineComponent({
         }
         return
       }
+      const response = evt.$EventMessage.messageType === 'RC:Chatroom:SeatResponse'
+      const userRoomType = response ? 4 : 3
+      // if(evt.$EventMessage.messageType === 'RC:Chatroom:SeatResponse')
       // 申请连麦
       if (evt.type === 3) {
-        RuntimeModelUtils.addJoin(evt.audienceId, {
+        const params = {
           name: evt.audienceName,
           id: evt.audienceId,
+          userRoomType: userRoomType,
           type: evt.type,
-        })
+        }
+        RuntimeModelUtils.addJoin(evt.audienceId, params)
+        RuntimeModelUtils.addLook(evt.audienceId, params)
       }
       // 取消连麦
       if (evt.type === 4) {
+        console.log(evt, '取消连麦')
         if (runtimeModel.joinList[evt.audienceId]) {
           RuntimeModelUtils.removeJoin(evt.audienceId)
         }
+        if (runtimeModel.lookList[evt.audienceId]) {
+          let userLook = runtimeModel.lookList[evt.audienceId]
+          userLook.userRoomType = 1
+          RuntimeModelUtils.addLook(evt.audienceId, userLook)
+        }
       }
     },
     agree(item: any) {
@@ -72,9 +96,11 @@ export default defineComponent({
         audienceId: item.id,
         teacherId: state.user?.id,
         teacherName: state.user?.speakerName,
+        userRoomType: 4,
         type: 1,
       }
       RuntimeModelUtils.addJoin(item.id, data)
+      RuntimeModelUtils.addLook(item.id, data)
       RuntimeUtils.sendMessage(data, 'SeatResponse')
     },
     refuse(item: any) {
@@ -84,15 +110,22 @@ export default defineComponent({
         audienceId: item.id,
         teacherId: state.user?.id,
         teacherName: state.user?.speakerName,
+        userRoomType: 4,
         type: 5,
       }
       RuntimeModelUtils.addJoin(item.id, data)
       RuntimeUtils.sendMessage(data, 'SeatApply')
     },
     onSwitchRole(evt: any) {
+      console.log(evt, 'onSwitchRole')
       if (runtimeModel.joinList[evt.userId] && evt.role === 2) {
         RuntimeModelUtils.removeJoin(evt.userId)
       }
+      if (runtimeModel.lookList[evt.userId] && evt.role === 2) {
+        let userLook = runtimeModel.lookList[evt.userId]
+        userLook.userRoomType = 1
+        RuntimeModelUtils.addLook(evt.userId, userLook)
+      }
     }
   },
   render() {
@@ -105,9 +138,9 @@ export default defineComponent({
               <div class={styles.itemName}>
                 <p class={styles.userName}>
                   <span class={styles['name-style']}>{item.name}</span>
-                  {item.type === 3 ? <p style={{ paddingLeft: '10px' }}>申请连麦</p> : <p style={{ paddingLeft: '10px', color: 'var(--live-text-color)' }}>正在连麦</p>}
+                  {item.userRoomType !== 4 ? <span style={{ paddingLeft: '10px' }}>申请连麦</span> : <span style={{ paddingLeft: '10px', color: 'var(--live-text-color)' }}>正在连麦</span>}
                 </p>
-                {item.type === 3 ? (
+                {item.userRoomType !== 4 ? (
                     <div class={styles.joinText}>
                       <div class={styles.join}>
                         {/* 申请连麦 */}

+ 35 - 24
src/components/live-message/model/look-model.tsx

@@ -12,15 +12,30 @@ import request from "/src/helpers/request";
 export default defineComponent({
   data() {
     return {
-      lookList: [] as any[], // 观看学生列表
       loadingLook: false, // 观看列表状态
     }
   },
+  computed: {
+    count() {
+      let count = 0
+      for (const key in runtimeModel.joinList) {
+        if (Object.prototype.hasOwnProperty.call(runtimeModel.joinList, key)) {
+          const item = runtimeModel.joinList[key];
+          if (item.type === 3) {
+            count += 1
+          }
+          if (count > 3) {
+            break
+          }
+        }
+      }
+      return count
+    },
+  },
   mounted() {
     // this._init()
     this.loadingLook = true
     event.on(LIVE_EVENT_MESSAGE["RC:Chatroom:Welcome"], this.onWelcome);
-    event.on(LIVE_EVENT_MESSAGE['RM:RTC:SwitchRole'], this.onSwitchRole);
     setTimeout(() => {
       this.loadingLook = false;
     })
@@ -46,17 +61,14 @@ export default defineComponent({
         let tempObj = {
           name: value.user.name,
           id: value.user.id,
-          sendTime
+          sendTime,
+          userRoomType: 1,
+          type: 3
         }
         // 判断是否有同一个人
-        let isExist = false;
-        runtimeModel.lookList.forEach((item: any) => {
-          if (item.id === tempObj.id) {
-            isExist = true
-          }
-        })
+        let isExist = runtimeModel.lookList[tempObj.id] ? true : false;
         if (!isExist) {
-          RuntimeModelUtils.addLook(tempObj);
+          RuntimeModelUtils.addLook(tempObj.id, tempObj);
         }
 
         this.loadingLook = false
@@ -68,11 +80,18 @@ export default defineComponent({
         // 操作类型 1 主讲人同意 2 主讲人拒绝 3 观众同意 4 观众拒绝
         // RC:Chatroom:SeatApply type teacherName teacherId audienceName audienceId
         // 操作类型 1 主讲人邀请 2 主讲人取消邀请 3 观众申请 4 观众取消申请 5将麦上观众踢下麦
+
+        // userRoomType
+        // 1 普通
+        // 2 老师邀请
+        // 3 学生申请
+        // 4 连麦中
         const data = {
           audienceName: item.name,
           audienceId: item.id,
           teacherId: state.user?.id,
           teacherName: state.user?.speakerName,
+          userRoomType: 2,
           type: 1
         }
         await RuntimeUtils.sendMessage(data, 'SeatApply')
@@ -95,27 +114,19 @@ export default defineComponent({
       } catch {
       }
     },
-    onSwitchRole(evt: any) {
-      console.log(evt, 'onSwitchRole look-modal')
-      if (evt.role === 2) {
-        runtimeModel.lookList.forEach((item: any) => {
-          if(item.id == evt.userId) {
-            item.type = 1
-          }
-        })
-        this.$forceUpdate()
-      }
-    }
   },
   render() {
+    const list = Object.values(runtimeModel.lookList)
     return (
       <div>
-        {runtimeModel.lookList.length > 0 ? runtimeModel.lookList.map((item : any) => (
+        {list.length > 0 ? list.map((item : any) => (
           <div class={styles.itemContent}>
             <div class={styles.itemInfo} >
               <div class={styles.itemName}>
-                <p class={styles.userName}>{item.name}</p>
-                {item.type !== 1 ? <ElButton size="small" type="primary" class={styles.btn} onClick={() => this.onUpLook(item)}>上麦</ElButton> : <ElButton size="small" plain class={[styles.btn, styles.downBtn]} onClick={() => this.onDownLook(item)}>下麦</ElButton>}
+                <p class={styles.userName}>
+                  <span class={styles['name-style']}>{item.name}</span>
+                </p>
+                {item.userRoomType !== 4 ? <ElButton size="small" type="primary" disabled={this.count > 3} class={styles.btn} onClick={() => this.onUpLook(item)}>上麦</ElButton> : <ElButton size="small" plain class={[styles.btn, styles.downBtn]} onClick={() => this.onDownLook(item)}>下麦</ElButton>}
               </div>
             </div>
           </div>

+ 7 - 6
src/components/live-message/model/message-model.tsx

@@ -84,14 +84,15 @@ export default defineComponent({
           <div class={styles.itemContent}>
             <div class={styles.itemInfo}>
               <div class={styles.itemName}>
-                <p class={styles.userName}>
-                  <span class={styles['name-style']}>{item.name}</span>
-                  {item.isSelf ? <ElTag effect="dark" color="#01A79E">主讲人</ElTag> : null}</p>
+                <div class={styles.userName}>
+                  {item.isSelf ? <ElTag effect="dark" color="#01A79E">主讲人</ElTag> : null}
+                  <span class={styles['name-style']}>{item.name}:</span>
+                  <span class={[styles.itemText, item.isSelf ? styles.active : null]}>
+                    {item.content}
+                  </span>
+                </div>
                 <p class={styles.rightTime}>{item.sendTime}</p>
               </div>
-              <div class={[styles.itemText, item.isSelf ? styles.active : null]}>
-                {item.content}
-              </div>
             </div>
           </div>
         )) : (this.loadingMessage ? <div class={styles.loadingStyle}>

+ 21 - 5
src/components/live-message/model/runtime.ts

@@ -3,7 +3,7 @@ import { reactive, ref } from 'vue'
 const runtime = reactive({
   messageList: [] as Array<any>,
   joinList: {} as { [key: string]: any },
-  lookList: [] as Array<any>,
+  lookList: {} as { [key: string]: any },
 })
 
 export default runtime
@@ -19,6 +19,7 @@ type Message = {
   audienceName?: string,
   audienceId?: string | number,
   teacherId?: string | number,
+  userRoomType?: string | number, // 讲师状态
   teacherName?: string,
 }
 /**
@@ -34,11 +35,26 @@ export const addMessage = (message: Message) => {
 export const addJoin = (key: string, message: Message) => {
   runtime.joinList[key] = message
 }
-
+/**
+ * 移除下麦
+ * @param key 用户编号
+ */
 export const removeJoin = (key: string) => {
   delete runtime.joinList[key]
 }
 
-export const addLook = (message: Message) => {
-  runtime.lookList.push(message)
-}
+/**
+ * 添加观看人数
+ * @param key 用户编号
+ * @param message
+ */
+export const addLook = (key: string,message: Message) => {
+  runtime.lookList[key] = message
+}
+/**
+ * 移除观看人数
+ * @param key 用户编号
+ */
+ export const removeLook = (key: string) => {
+  delete runtime.lookList[key]
+}

+ 13 - 1
src/components/live-message/send-message.tsx

@@ -13,6 +13,11 @@ export default defineComponent({
     }
   },
   methods: {
+    onKeyDown(e: any) {
+      if(e.keyCode === 13 && this.message.trim() !== '') {
+        this.sendMessage()
+      }
+    },
     async sendMessage() {
       try {
         await RuntimeUtils.sendMessage(this.message);
@@ -26,15 +31,22 @@ export default defineComponent({
           sendTime: dayjs(new Date()).format('HH:mm:ss')
         })
         this.message = '';
+        this.scrollToBottom();
       } catch (error) {
       }
+    },
+    scrollToBottom() {
+      // 默认滚动到底部
+      this.$nextTick(() => {
+        document.querySelector('#tabList')?.scrollTo(0, document.querySelector('#messageList')?.scrollHeight || 0)
+      })
     }
   },
   render() {
     return (
       <div class={styles.sendMessage}>
         <div class={styles.form}>
-          <ElInput modelValue={this.message} onInput={val => this.message = val} placeholder="请输入消息" />
+          <ElInput modelValue={this.message} onKeydown={this.onKeyDown} onInput={val => this.message = val} placeholder="请输入消息" />
           <ElButton disabled={!this.message} onClick={this.sendMessage} round type="primary">发送</ElButton>
         </div>
       </div>

+ 1 - 0
src/pages/home/header/index.tsx

@@ -15,6 +15,7 @@ export default defineComponent({
         await request.post('/api-auth/exit', { data: {} });
         RuntimeUtils.closeDevice('camera')
         RuntimeUtils.closeDevice('microphone')
+        state.user = null
         ElMessage.success('退出成功');
         removeToken();
         (this as any).$router.push({

+ 2 - 1
src/pages/login/index.tsx

@@ -2,7 +2,7 @@ import { defineComponent } from "vue"
 import { ElForm, ElFormItem, ElInput, ElMessage } from "element-plus";
 import styles from './index.module.less'
 import request from "/src/helpers/request";
-import { setToken } from "/src/utils/auth";
+import { removeToken, setToken } from "/src/utils/auth";
 import LogoPng from '././../../assets/home/logo.png'
 import FromBg from './images/from-bg.png'
 
@@ -109,6 +109,7 @@ export default defineComponent({
             this.$router.push(this.redirect || '/')
           } catch (error) {
             // console.log(error)
+            removeToken()
           }
         } else {
           return false;