mdm-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -0
- package/index.js +35 -0
- package/package.json +41 -0
- package/src/App.vue +246 -0
- package/src/assets/audio/moon_light.ogg +0 -0
- package/src/assets/font/DINPro-Medium.otf +0 -0
- package/src/assets/font/FZZhengHeiS-B-GB.ttf +0 -0
- package/src/assets/font/PingFang Regular.otf +0 -0
- package/src/assets/font/SourceHanSansCN-Regular.otf +0 -0
- package/src/assets/image/common/add_other_icon.png +0 -0
- package/src/assets/image/common/bottomFrame.png +0 -0
- package/src/assets/image/common/broadcastOn.png +0 -0
- package/src/assets/image/common/broadcastTask.png +0 -0
- package/src/assets/image/common/call.png +0 -0
- package/src/assets/image/common/call_user_mic_off_icon.png +0 -0
- package/src/assets/image/common/call_user_mic_on_icon.png +0 -0
- package/src/assets/image/common/cam-off.png +0 -0
- package/src/assets/image/common/cam-on.png +0 -0
- package/src/assets/image/common/cam_off_small.png +0 -0
- package/src/assets/image/common/camera.png +0 -0
- package/src/assets/image/common/cameraBtn1.png +0 -0
- package/src/assets/image/common/cameraBtn2.png +0 -0
- package/src/assets/image/common/cameraBtn3.png +0 -0
- package/src/assets/image/common/cameraBtn4.png +0 -0
- package/src/assets/image/common/cameraBtn5.png +0 -0
- package/src/assets/image/common/cameraBtn6.png +0 -0
- package/src/assets/image/common/cameraBtn7.png +0 -0
- package/src/assets/image/common/cameraClose.png +0 -0
- package/src/assets/image/common/cameraFull.png +0 -0
- package/src/assets/image/common/cameraOff.png +0 -0
- package/src/assets/image/common/cancel_icon.png +0 -0
- package/src/assets/image/common/card_blue.png +0 -0
- package/src/assets/image/common/card_grey.png +0 -0
- package/src/assets/image/common/chosen_icon.png +0 -0
- package/src/assets/image/common/chosen_icon_slided.png +0 -0
- package/src/assets/image/common/custom_layout_equipment_avatar.png +0 -0
- package/src/assets/image/common/custom_layout_user_avatar.png +0 -0
- package/src/assets/image/common/default_avatar.png +0 -0
- package/src/assets/image/common/default_avatar_mini.png +0 -0
- package/src/assets/image/common/delete-number.svg +3 -0
- package/src/assets/image/common/deviceIcon.png +0 -0
- package/src/assets/image/common/fourOff.png +0 -0
- package/src/assets/image/common/fourOn.png +0 -0
- package/src/assets/image/common/group.png +0 -0
- package/src/assets/image/common/groupManage.png +0 -0
- package/src/assets/image/common/histroy_meeting_icon.png +0 -0
- package/src/assets/image/common/icon-device.svg +12 -0
- package/src/assets/image/common/icon-monitor.svg +12 -0
- package/src/assets/image/common/icon-/345/260/217/347/250/213/345/272/217.svg +5 -0
- package/src/assets/image/common/icon-/345/272/224/347/255/224.svg +5 -0
- package/src/assets/image/common/icon-/350/247/206/351/242/221.svg +7 -0
- package/src/assets/image/common/input_search_icon.png +0 -0
- package/src/assets/image/common/launch_icon.png +0 -0
- package/src/assets/image/common/layout-active1.png +0 -0
- package/src/assets/image/common/layout-active2.png +0 -0
- package/src/assets/image/common/layout1.png +0 -0
- package/src/assets/image/common/layout2.png +0 -0
- package/src/assets/image/common/loading.png +0 -0
- package/src/assets/image/common/login/checked_icon.png +0 -0
- package/src/assets/image/common/login/login_bg.png +0 -0
- package/src/assets/image/common/login/login_form_left_icon.png +0 -0
- package/src/assets/image/common/login/pwd_icon.png +0 -0
- package/src/assets/image/common/login/user_icon.png +0 -0
- package/src/assets/image/common/login/verify_icon.png +0 -0
- package/src/assets/image/common/logo.png +0 -0
- package/src/assets/image/common/ltypeOff.png +0 -0
- package/src/assets/image/common/ltypeOn.png +0 -0
- package/src/assets/image/common/manager_cam_off_icon.png +0 -0
- package/src/assets/image/common/manager_mic_off_icon.png +0 -0
- package/src/assets/image/common/map-cicle-icon.svg +6 -0
- package/src/assets/image/common/map-icon-search-info.svg +11 -0
- package/src/assets/image/common/map-location.svg +37 -0
- package/src/assets/image/common/map.png +0 -0
- package/src/assets/image/common/mic-off.png +0 -0
- package/src/assets/image/common/mic-on.png +0 -0
- package/src/assets/image/common/mic_off_small.png +0 -0
- package/src/assets/image/common/minum_cam_off_icon.png +0 -0
- package/src/assets/image/common/minum_cam_on_icon.png +0 -0
- package/src/assets/image/common/minum_expand_icon.png +0 -0
- package/src/assets/image/common/minum_mic_off_icon.png +0 -0
- package/src/assets/image/common/minum_mic_on_icon.png +0 -0
- package/src/assets/image/common/minum_reset_icon.png +0 -0
- package/src/assets/image/common/minum_slide_icon.png +0 -0
- package/src/assets/image/common/more_icon.png +0 -0
- package/src/assets/image/common/mute.png +0 -0
- package/src/assets/image/common/newFolder.png +0 -0
- package/src/assets/image/common/newTask.png +0 -0
- package/src/assets/image/common/nineOff.png +0 -0
- package/src/assets/image/common/nineOn.png +0 -0
- package/src/assets/image/common/none.png +0 -0
- package/src/assets/image/common/offline.png +0 -0
- package/src/assets/image/common/oneOff.png +0 -0
- package/src/assets/image/common/oneOn.png +0 -0
- package/src/assets/image/common/online.png +0 -0
- package/src/assets/image/common/output_off_small.png +0 -0
- package/src/assets/image/common/playOff.png +0 -0
- package/src/assets/image/common/playOn.png +0 -0
- package/src/assets/image/common/playStop.png +0 -0
- package/src/assets/image/common/preview_icon.png +0 -0
- package/src/assets/image/common/preview_meet_icon.png +0 -0
- package/src/assets/image/common/scan-map.png +0 -0
- package/src/assets/image/common/screen_blue.png +0 -0
- package/src/assets/image/common/screen_gray.png +0 -0
- package/src/assets/image/common/screen_white.png +0 -0
- package/src/assets/image/common/select_item_check.png +0 -0
- package/src/assets/image/common/select_item_checked.png +0 -0
- package/src/assets/image/common/selector.png +0 -0
- package/src/assets/image/common/selectorOn.png +0 -0
- package/src/assets/image/common/share_icon.png +0 -0
- package/src/assets/image/common/signal_good.png +0 -0
- package/src/assets/image/common/signal_poor.png +0 -0
- package/src/assets/image/common/sixteenOff.png +0 -0
- package/src/assets/image/common/sixteenOn.png +0 -0
- package/src/assets/image/common/slide-bth-expand.png +0 -0
- package/src/assets/image/common/slide_btn.png +0 -0
- package/src/assets/image/common/speak.png +0 -0
- package/src/assets/image/common/speakOn.png +0 -0
- package/src/assets/image/common/speaker-off.png +0 -0
- package/src/assets/image/common/speaker-on.png +0 -0
- package/src/assets/image/common/speaking.png +0 -0
- package/src/assets/image/common/speed-left.svg +5 -0
- package/src/assets/image/common/speed-right.svg +5 -0
- package/src/assets/image/common/tree_checked_icon.png +0 -0
- package/src/assets/image/common/tree_expand_icon.png +0 -0
- package/src/assets/image/common/tree_slide_icon.png +0 -0
- package/src/assets/image/common/tree_uncheck_icon.png +0 -0
- package/src/assets/image/common/unchosen_icon.png +0 -0
- package/src/assets/image/common/up.png +0 -0
- package/src/assets/image/common/volume.png +0 -0
- package/src/assets/image/common/warning.png +0 -0
- package/src/assets/image/screenBlue/a1.png +0 -0
- package/src/assets/image/screenBlue/a2.png +0 -0
- package/src/assets/image/screenBlue/a3.png +0 -0
- package/src/assets/image/screenBlue/a4.png +0 -0
- package/src/assets/image/screenBlue/a5.png +0 -0
- package/src/assets/image/screenBlue/a6.png +0 -0
- package/src/assets/image/screenBlue/add.png +0 -0
- package/src/assets/image/screenBlue/add_group_icon.png +0 -0
- package/src/assets/image/screenBlue/add_to_group_icon.png +0 -0
- package/src/assets/image/screenBlue/arrow_icon.png +0 -0
- package/src/assets/image/screenBlue/audio_level_icon.png +0 -0
- package/src/assets/image/screenBlue/b1.png +0 -0
- package/src/assets/image/screenBlue/b2.png +0 -0
- package/src/assets/image/screenBlue/b3.png +0 -0
- package/src/assets/image/screenBlue/b4.png +0 -0
- package/src/assets/image/screenBlue/b5.png +0 -0
- package/src/assets/image/screenBlue/b6.png +0 -0
- package/src/assets/image/screenBlue/bottom_footer_bg.png +0 -0
- package/src/assets/image/screenBlue/call_clear_icon.png +0 -0
- package/src/assets/image/screenBlue/call_duration_icon.png +0 -0
- package/src/assets/image/screenBlue/call_fullscreen_icon.png +0 -0
- package/src/assets/image/screenBlue/call_mini_icon.png +0 -0
- package/src/assets/image/screenBlue/call_video_icon.png +0 -0
- package/src/assets/image/screenBlue/call_voice_icon.png +0 -0
- package/src/assets/image/screenBlue/cam_on_small.png +0 -0
- package/src/assets/image/screenBlue/close_icon.png +0 -0
- package/src/assets/image/screenBlue/copy-icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_drag_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_grid16_active_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_grid16_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_grid4_active_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_grid4_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_grid9_active_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_grid9_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_header_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_placeholder_bg.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_refresh_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_rightside_active_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_rightside_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_ring_active_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_ring_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_topside_active_icon.png +0 -0
- package/src/assets/image/screenBlue/custom_layout_topside_icon.png +0 -0
- package/src/assets/image/screenBlue/date_picker_icon.png +0 -0
- package/src/assets/image/screenBlue/dialog_check_icon.png +0 -0
- package/src/assets/image/screenBlue/emoji-logo.png +0 -0
- package/src/assets/image/screenBlue/header_alert_icon.png +0 -0
- package/src/assets/image/screenBlue/manager_cam_on_icon.png +0 -0
- package/src/assets/image/screenBlue/manager_mic_on_icon.png +0 -0
- package/src/assets/image/screenBlue/manager_more_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_board_bg.png +0 -0
- package/src/assets/image/screenBlue/meeting_chat_emoji_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_chat_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_chat_image_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_chat_logo.png +0 -0
- package/src/assets/image/screenBlue/meeting_copy_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_invite_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_layout_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_member_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_mode_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_record_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_setting_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_share_icon.png +0 -0
- package/src/assets/image/screenBlue/meeting_slide_small_icon.png +0 -0
- package/src/assets/image/screenBlue/mic_on_small.png +0 -0
- package/src/assets/image/screenBlue/module_bg1.png +0 -0
- package/src/assets/image/screenBlue/module_bg2.png +0 -0
- package/src/assets/image/screenBlue/monitor/circle data.png +0 -0
- package/src/assets/image/screenBlue/monitor/circle.png +0 -0
- package/src/assets/image/screenBlue/output_on_small.png +0 -0
- package/src/assets/image/screenBlue/page_bg.png +0 -0
- package/src/assets/image/screenBlue/pic-logo.png +0 -0
- package/src/assets/image/screenBlue/preview_date_icon.png +0 -0
- package/src/assets/image/screenBlue/preview_empty_icon.png +0 -0
- package/src/assets/image/screenBlue/preview_more_icon.png +0 -0
- package/src/assets/image/screenBlue/receive_hang_off_icon.png +0 -0
- package/src/assets/image/screenBlue/receive_hang_on_icon.png +0 -0
- package/src/assets/image/screenBlue/receive_video_icon.png +0 -0
- package/src/assets/image/screenBlue/receive_voice_icon.png +0 -0
- package/src/assets/image/screenBlue/send-logo.png +0 -0
- package/src/assets/image/screenBlue/slide_menu_bg.png +0 -0
- package/src/assets/image/screenBlue/top_header_bg.png +0 -0
- package/src/assets/image/screenBlue/yq.png +0 -0
- package/src/assets/json/emoji.json +222 -0
- package/src/assets/style/base.scss +217 -0
- package/src/assets/style/elForm.scss +80 -0
- package/src/assets/style/font.scss +16 -0
- package/src/assets/style/index.scss +5 -0
- package/src/assets/style/math.scss +11 -0
- package/src/assets/style/mixin.scss +69 -0
- package/src/components/LiveCallBoard/LiveCallBoard.vue +237 -0
- package/src/components/LiveInviteReceive/LiveInviteReceive.vue +150 -0
- package/src/components/LiveMulti/LiveMulti.vue +303 -0
- package/src/components/LiveMultipleMeeting/LiveMultipleMeeting.vue +4469 -0
- package/src/components/LiveMultipleMeeting/style/index.scss +337 -0
- package/src/components/LivePoint/LivePoint.vue +372 -0
- package/src/components/LivePointMeeting/LivePointMeeting.vue +1134 -0
- package/src/components/LivePointMeeting/style/index.scss +202 -0
- package/src/components/MeetingReadyDialog/MeetingReadyDialog.vue +583 -0
- package/src/components/MiniumVideoDialog/MiniumVideoDialog.vue +449 -0
- package/src/components/other/LayoutPlaceholder.vue +184 -0
- package/src/components/other/addressBook.vue +1121 -0
- package/src/components/other/appointDialog.vue +208 -0
- package/src/components/other/callBoard.vue +191 -0
- package/src/components/other/chatArea.vue +727 -0
- package/src/components/other/customGroupDialog.vue +180 -0
- package/src/components/other/customLayout.vue +1112 -0
- package/src/components/other/editGroupDialog.vue +290 -0
- package/src/components/other/inviteNonContactDialog.vue +160 -0
- package/src/components/other/layoutSwitch.vue +183 -0
- package/src/components/other/leaveOptionDialog.vue +90 -0
- package/src/components/other/memberManage.vue +502 -0
- package/src/components/other/moreOptionDialog.vue +291 -0
- package/src/components/other/screenShareBoard.vue +121 -0
- package/src/components/other/selectDialog.vue +279 -0
- package/src/components/other/selectSpecialDialog.vue +234 -0
- package/src/components/other/settingDialog.vue +756 -0
- package/src/components/other/themeDialog.vue +180 -0
- package/src/components/other/updateNameDialog.vue +162 -0
- package/src/directive/clickOutside.js +58 -0
- package/src/directive/drag.js +165 -0
- package/src/directive/scale.js +22 -0
- package/src/directive/throttle.js +77 -0
- package/src/main.js +21 -0
- package/src/request/index.js +27 -0
- package/src/utils/api.js +82 -0
- package/src/utils/index.js +4 -0
- package/src/utils/livekit/live-client-esm-old.js +1 -0
- package/src/utils/livekit/live-client-esm.js +1 -0
- package/src/utils/message.js +24 -0
- package/src/utils/mitt.js +4 -0
- package/src/utils/tool.js +154 -0
|
@@ -0,0 +1,4469 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="meeting-room" id="multiple-meeting" ref="rootElm">
|
|
3
|
+
<!-- 会议顶部栏 -->
|
|
4
|
+
<div class="meeting-room-top-bar" v-drag="'multiple-meeting'">
|
|
5
|
+
<div class="bar-group">
|
|
6
|
+
<div class="meeting-theme">
|
|
7
|
+
<span style="white-space: nowrap">会议主题</span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="meeting-name" @click="themeDialogShow = !themeDialogShow">
|
|
10
|
+
<span class="meeting-name-text">{{ meetingName }}</span>
|
|
11
|
+
<div
|
|
12
|
+
:class="['meeting-name-icon', { 'meeting-name-icon-expanded': themeDialogShow }]"
|
|
13
|
+
></div>
|
|
14
|
+
<ThemeDialog
|
|
15
|
+
v-if="themeDialogShow"
|
|
16
|
+
:meetingName="meetingName"
|
|
17
|
+
:meetingNum="meetingNum"
|
|
18
|
+
:meetingCreator="meetingHost.ownerName"
|
|
19
|
+
></ThemeDialog>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="meeting-duration">
|
|
23
|
+
<div class="meeting-duration-logo"></div>
|
|
24
|
+
<span>{{ meetingDuration }}</span>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="bar-group">
|
|
27
|
+
<div
|
|
28
|
+
class="btn custom-layout"
|
|
29
|
+
v-if="judgeParticipantIsHost(localIdentity) || judgeParticipantIsCoHost(localIdentity)"
|
|
30
|
+
@click="customLayoutShow = !customLayoutShow"
|
|
31
|
+
></div>
|
|
32
|
+
<span
|
|
33
|
+
v-if="judgeParticipantIsHost(localIdentity) || judgeParticipantIsCoHost(localIdentity)"
|
|
34
|
+
class="bar-group-text"
|
|
35
|
+
@click="customLayoutShow = !customLayoutShow"
|
|
36
|
+
>自定义布局</span
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
:class="['btn', layoutSwitchShow ? 'layout-active' : 'layout']"
|
|
40
|
+
@click="layoutSwitchShow = !layoutSwitchShow"
|
|
41
|
+
v-if="judgeParticipantIsHost(localIdentity) || judgeParticipantIsCoHost(localIdentity)"
|
|
42
|
+
>
|
|
43
|
+
<LayoutSwitch
|
|
44
|
+
v-if="layoutSwitchShow"
|
|
45
|
+
:currentLayout="currentLayout"
|
|
46
|
+
:layoutList="layoutList"
|
|
47
|
+
:layoutLabels="layoutLabels"
|
|
48
|
+
:currentRoomMode="currentRoomMode"
|
|
49
|
+
@setLayout="manualSetLayout"
|
|
50
|
+
@mouseenter.native="layoutSwitchShow = true"
|
|
51
|
+
@mouseleave.native="layoutSwitchShow = false"
|
|
52
|
+
></LayoutSwitch>
|
|
53
|
+
</div>
|
|
54
|
+
<span
|
|
55
|
+
class="bar-group-text"
|
|
56
|
+
v-if="judgeParticipantIsHost(localIdentity) || judgeParticipantIsCoHost(localIdentity)"
|
|
57
|
+
>布局</span
|
|
58
|
+
>
|
|
59
|
+
<div class="btn slide-small" @click="minumDialog"></div>
|
|
60
|
+
<div class="btn full-screen" @click="switchFullScreen($event)"></div>
|
|
61
|
+
<div class="bar-group-split"></div>
|
|
62
|
+
<div
|
|
63
|
+
class="btn close-meeting-icon"
|
|
64
|
+
@click="isInMeeting ? openLeaveOptionDialog2() : exitPage()"
|
|
65
|
+
></div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- 会议主体内容 -->
|
|
70
|
+
<div class="meeting-room-contain">
|
|
71
|
+
<div
|
|
72
|
+
:class="[
|
|
73
|
+
'meeting-room-contain-left',
|
|
74
|
+
{ 'meeting-room-contain-left-slide': memberManageShow || chatShow },
|
|
75
|
+
]"
|
|
76
|
+
v-drag="'multiple-meeting'"
|
|
77
|
+
>
|
|
78
|
+
<div
|
|
79
|
+
:class="['meeting', layout, { 'meeting-slide': pageFooterVisible }]"
|
|
80
|
+
id="room"
|
|
81
|
+
@mousemove="setPageFooterVisible(5, $event)"
|
|
82
|
+
>
|
|
83
|
+
<ScreenShareBoard
|
|
84
|
+
v-if="isScreenShareEnabled"
|
|
85
|
+
:screenShareCaptureOption="screenShareCaptureOption"
|
|
86
|
+
@screenShareOptionChange="screenShareOptionChange"
|
|
87
|
+
@stopScreenShare="stopScreenShare"
|
|
88
|
+
></ScreenShareBoard>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<!-- 会议控制栏 -->
|
|
92
|
+
<div
|
|
93
|
+
:class="['meeting-control-bar', { 'meeting-control-bar-show': pageFooterVisible }]"
|
|
94
|
+
@mousemove="handleClick"
|
|
95
|
+
>
|
|
96
|
+
<!-- 控制按钮组 -->
|
|
97
|
+
<div class="bar-group">
|
|
98
|
+
<div class="custom-select">
|
|
99
|
+
<div
|
|
100
|
+
:class="['custom-select-btn', isMicrophoneEnabled ? 'mic-on' : 'mic-off']"
|
|
101
|
+
@click="changeLocalMicrophoneStatus"
|
|
102
|
+
></div>
|
|
103
|
+
<div class="custom-select-right" @click="audioSelectShow = !audioSelectShow">
|
|
104
|
+
<div class="custom-select-right-text">麦克风</div>
|
|
105
|
+
<div class="arrow-icon"></div>
|
|
106
|
+
</div>
|
|
107
|
+
<SelectSpecialDialog
|
|
108
|
+
v-if="audioSelectShow"
|
|
109
|
+
dialogType="audio"
|
|
110
|
+
:audioDevices="audioDevicesRef"
|
|
111
|
+
:outputDevices="outputDevicesRef"
|
|
112
|
+
:activeDevice="activeDevice"
|
|
113
|
+
@chooseAudioInputDevice="chooseAudioInputDevice"
|
|
114
|
+
@chooseAudioOutputDevice="chooseAudioOutputDevice"
|
|
115
|
+
@detectMicroAndOutput="detectMicroAndOutput"
|
|
116
|
+
@openSettingDialog="openSettingDialog"
|
|
117
|
+
></SelectSpecialDialog>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="custom-select">
|
|
120
|
+
<div
|
|
121
|
+
:class="['custom-select-btn', isCameraEnabled ? 'cam-on' : 'cam-off']"
|
|
122
|
+
@click="changeLocalCameraStatus"
|
|
123
|
+
></div>
|
|
124
|
+
<div class="custom-select-right" @click="videoSelectShow = !videoSelectShow">
|
|
125
|
+
<div class="custom-select-right-text">摄像头</div>
|
|
126
|
+
<div class="arrow-icon"></div>
|
|
127
|
+
</div>
|
|
128
|
+
<SelectSpecialDialog
|
|
129
|
+
v-if="videoSelectShow"
|
|
130
|
+
:videoDevices="videoDevicesRef"
|
|
131
|
+
:activeDevice="activeDevice"
|
|
132
|
+
dialogType="video"
|
|
133
|
+
@chooseVideoDevice="chooseVideoDevice"
|
|
134
|
+
@openSettingDialog="openSettingDialog"
|
|
135
|
+
>
|
|
136
|
+
</SelectSpecialDialog>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<!-- 功能按钮组 -->
|
|
141
|
+
<div class="bar-group">
|
|
142
|
+
<div
|
|
143
|
+
class="custom-select"
|
|
144
|
+
v-if="
|
|
145
|
+
judgeParticipantIsHost(localIdentity) || judgeParticipantIsCoHost(localIdentity)
|
|
146
|
+
"
|
|
147
|
+
>
|
|
148
|
+
<div class="custom-select-btn theme-icon"></div>
|
|
149
|
+
<div class="custom-select-right" @click="modeSelectShow = !modeSelectShow">
|
|
150
|
+
<div class="custom-select-right-text">会议模式</div>
|
|
151
|
+
<div class="arrow-icon"></div>
|
|
152
|
+
</div>
|
|
153
|
+
<SelectDialog
|
|
154
|
+
v-if="modeSelectShow"
|
|
155
|
+
:currentRoomMode="currentRoomMode"
|
|
156
|
+
dialogType="other"
|
|
157
|
+
@chooseOtherItem="chooseMeetingMode"
|
|
158
|
+
@close="modeSelectShow = false"
|
|
159
|
+
>
|
|
160
|
+
</SelectDialog>
|
|
161
|
+
</div>
|
|
162
|
+
<div
|
|
163
|
+
:class="['t-btn', { 't-btn-hide': isBtnTextHide }]"
|
|
164
|
+
v-if="!isScreenShareEnabled"
|
|
165
|
+
@click="changeScreenShareStatus"
|
|
166
|
+
>
|
|
167
|
+
<div class="t-btn-icon screen-share"></div>
|
|
168
|
+
<span>共享屏幕</span>
|
|
169
|
+
</div>
|
|
170
|
+
<div
|
|
171
|
+
:class="['t-btn', { 't-btn-hide': isBtnTextHide }]"
|
|
172
|
+
v-else
|
|
173
|
+
@click="changeScreenShareStatus"
|
|
174
|
+
>
|
|
175
|
+
<div class="t-btn-icon screen-share-off"></div>
|
|
176
|
+
<span>结束共享</span>
|
|
177
|
+
</div>
|
|
178
|
+
<div :class="['t-btn', { 't-btn-hide': isBtnTextHide }]" @click="chatShow = !chatShow">
|
|
179
|
+
<div class="t-btn-icon chat"></div>
|
|
180
|
+
<span>聊天</span>
|
|
181
|
+
</div>
|
|
182
|
+
<div
|
|
183
|
+
:class="['t-btn', { 't-btn-hide': isBtnTextHide }]"
|
|
184
|
+
v-if="!isRecording"
|
|
185
|
+
@click="startRecord"
|
|
186
|
+
>
|
|
187
|
+
<div class="t-btn-icon record"></div>
|
|
188
|
+
<span>录制</span>
|
|
189
|
+
</div>
|
|
190
|
+
<div :class="['t-btn', { 't-btn-hide': isBtnTextHide }]" v-else @click="stopRecord">
|
|
191
|
+
<div class="t-btn-icon record-off"></div>
|
|
192
|
+
<span>结束录制</span>
|
|
193
|
+
</div>
|
|
194
|
+
<template
|
|
195
|
+
v-if="
|
|
196
|
+
judgeParticipantIsHost(localIdentity) || judgeParticipantIsCoHost(localIdentity)
|
|
197
|
+
"
|
|
198
|
+
>
|
|
199
|
+
<div
|
|
200
|
+
:class="['t-btn', { 't-btn-hide': isBtnTextHide }]"
|
|
201
|
+
@click.self="inviteWaysSelectShow = !inviteWaysSelectShow"
|
|
202
|
+
>
|
|
203
|
+
<div
|
|
204
|
+
class="t-btn-icon invite"
|
|
205
|
+
@click.self="inviteWaysSelectShow = !inviteWaysSelectShow"
|
|
206
|
+
></div>
|
|
207
|
+
<span @click.self="inviteWaysSelectShow = !inviteWaysSelectShow">邀请</span>
|
|
208
|
+
<SelectDialog
|
|
209
|
+
v-if="inviteWaysSelectShow"
|
|
210
|
+
:otherOptionList="inviteWaysList"
|
|
211
|
+
dialogType="other"
|
|
212
|
+
@chooseOtherItem="chooseInviteWay"
|
|
213
|
+
@close="inviteWaysSelectShow = false"
|
|
214
|
+
></SelectDialog>
|
|
215
|
+
<CallBoard
|
|
216
|
+
v-if="callBoardShow"
|
|
217
|
+
:position="callBoardOffset"
|
|
218
|
+
@closeCallBoard="callBoardShow = false"
|
|
219
|
+
@phoneCall="phoneCall"
|
|
220
|
+
@miniCall="miniCall"
|
|
221
|
+
></CallBoard>
|
|
222
|
+
</div>
|
|
223
|
+
</template>
|
|
224
|
+
<div
|
|
225
|
+
:class="['t-btn', { 't-btn-hide': isBtnTextHide }]"
|
|
226
|
+
@click="memberManageShow = !memberManageShow"
|
|
227
|
+
>
|
|
228
|
+
<div class="t-btn-icon member"></div>
|
|
229
|
+
<span>成员管理({{ participantNum }})</span>
|
|
230
|
+
</div>
|
|
231
|
+
<template
|
|
232
|
+
v-if="
|
|
233
|
+
judgeParticipantIsHost(localIdentity) || judgeParticipantIsCoHost(localIdentity)
|
|
234
|
+
"
|
|
235
|
+
>
|
|
236
|
+
<div
|
|
237
|
+
:class="['t-btn', { 't-btn-hide': isBtnTextHide }]"
|
|
238
|
+
@click="openSettingDialog('1')"
|
|
239
|
+
>
|
|
240
|
+
<div class="t-btn-icon setting"></div>
|
|
241
|
+
<span>设置</span>
|
|
242
|
+
</div>
|
|
243
|
+
</template>
|
|
244
|
+
</div>
|
|
245
|
+
<div class="exit-btn" @click="isInMeeting ? openLeaveOptionDialog($event) : exitPage()">
|
|
246
|
+
{{ judgeParticipantIsHost(localIdentity) ? "结束会议" : "离开会议" }}
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<AppointDialog
|
|
251
|
+
v-if="appointDialogShow"
|
|
252
|
+
:participants="participants"
|
|
253
|
+
:meetingHost="meetingHost"
|
|
254
|
+
@appointHost="appointHost"
|
|
255
|
+
@closeDialog="appointDialogShow = false"
|
|
256
|
+
>
|
|
257
|
+
</AppointDialog>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<!-- 右侧边栏 -->
|
|
261
|
+
<div class="meeting-room-contain-right">
|
|
262
|
+
<MemberManage
|
|
263
|
+
ref="memeberManageRef"
|
|
264
|
+
v-if="memberManageShow"
|
|
265
|
+
:joinedList="joinedList"
|
|
266
|
+
:participants="participants"
|
|
267
|
+
:localIdentity="localIdentity"
|
|
268
|
+
:unJoinedList="unJoinedList"
|
|
269
|
+
:meetingNum="meetingNum"
|
|
270
|
+
:meetingName="meetingName"
|
|
271
|
+
:meetingHost="meetingHost"
|
|
272
|
+
:meetingCoHost="meetingCoHost"
|
|
273
|
+
@close="
|
|
274
|
+
memberManageShow = false;
|
|
275
|
+
moreDialogShow = false;
|
|
276
|
+
"
|
|
277
|
+
@closeMoreDialog="closeMoreDialog"
|
|
278
|
+
@participantCameraSwitch="switchParticipantCameraStatus"
|
|
279
|
+
@participantMicrophoneSwitch="switchParticipantMicrophoneStatus"
|
|
280
|
+
@allCameraClose="closeAllCamera"
|
|
281
|
+
@allMicrophoneClose="closeAllMicrophone"
|
|
282
|
+
@openMoreDialog="moreOptionClick"
|
|
283
|
+
@appendMemberInvite="appendMemberInvite"
|
|
284
|
+
@removeUnjoinItem="removeUnjoinItem"
|
|
285
|
+
@filterJoinedList="filterJoinedList"
|
|
286
|
+
></MemberManage>
|
|
287
|
+
<ChatArea
|
|
288
|
+
ref="chatAreaRef"
|
|
289
|
+
v-if="chatShow"
|
|
290
|
+
:localIdentity="localIdentity"
|
|
291
|
+
:messageList="messageList"
|
|
292
|
+
:baseUrl="baseUrl"
|
|
293
|
+
:token="token"
|
|
294
|
+
@close="chatShow = false"
|
|
295
|
+
@messageSend="messageSend"
|
|
296
|
+
></ChatArea>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<CustomLayout
|
|
300
|
+
v-if="customLayoutShow"
|
|
301
|
+
:participantList="participants"
|
|
302
|
+
:meetingNum="meetingNum"
|
|
303
|
+
@closeCustomLayout="customLayoutShow = false"
|
|
304
|
+
></CustomLayout>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<!-- 弹窗组件 -->
|
|
308
|
+
<LeaveOptionDialog
|
|
309
|
+
v-if="leaveOptionShow"
|
|
310
|
+
:position="leaveOptionOffset"
|
|
311
|
+
@cancelMeeting="cancelMeeting"
|
|
312
|
+
@leaveMeeting="openAppointDialog"
|
|
313
|
+
@optionClose="leaveOptionShow = false"
|
|
314
|
+
></LeaveOptionDialog>
|
|
315
|
+
<MoreOptionDialog
|
|
316
|
+
v-if="moreDialogShow"
|
|
317
|
+
:curBlur="curBlurIdentity"
|
|
318
|
+
:identity="optionIdentity"
|
|
319
|
+
:optionDialogOffset="optionDialogOffset"
|
|
320
|
+
:localIdentity="localIdentity"
|
|
321
|
+
:participants="participants"
|
|
322
|
+
:meetingHost="meetingHost"
|
|
323
|
+
:meetingCoHost="meetingCoHost"
|
|
324
|
+
@close="moreDialogShow = false"
|
|
325
|
+
@setToBlur="setMemberBlur"
|
|
326
|
+
@removeFromBlur="removeCurBlur"
|
|
327
|
+
@openMicro="openMicro"
|
|
328
|
+
@closeMicro="closeMicro"
|
|
329
|
+
@openCamera="openCamera"
|
|
330
|
+
@closeCamera="closeCamera"
|
|
331
|
+
@setToHost="setToHost"
|
|
332
|
+
@setToDeafness="setToDeafness"
|
|
333
|
+
@cancelDeafness="cancelDeafness"
|
|
334
|
+
@updateName="updateName"
|
|
335
|
+
@remove="removeMember"
|
|
336
|
+
@setToCoHost="setToCoHost"
|
|
337
|
+
@removeFromCoHost="removeFromCoHost"
|
|
338
|
+
>
|
|
339
|
+
</MoreOptionDialog>
|
|
340
|
+
<AddressBook
|
|
341
|
+
:inviteList="inviteList"
|
|
342
|
+
:baseUrl="baseUrl"
|
|
343
|
+
:token="token"
|
|
344
|
+
@appendInvitePeople="appendInvitePeople"
|
|
345
|
+
@appendInviteDevice="appendInviteDevice"
|
|
346
|
+
></AddressBook>
|
|
347
|
+
<UpdateNameDialog @updateNameConfirm="updateNameConfirm"></UpdateNameDialog>
|
|
348
|
+
<SettingDialog
|
|
349
|
+
ref="settingDialogRef"
|
|
350
|
+
:activeDevice="activeDevice"
|
|
351
|
+
:audioDevices="audioDevicesRef"
|
|
352
|
+
:videoDevices="videoDevicesRef"
|
|
353
|
+
:outputDevices="outputDevicesRef"
|
|
354
|
+
></SettingDialog>
|
|
355
|
+
</div>
|
|
356
|
+
</template>
|
|
357
|
+
|
|
358
|
+
<script>
|
|
359
|
+
import LiveClient from "@/utils/livekit/live-client-esm";
|
|
360
|
+
import { ShowMessage, calculateTime, mittBus } from "@/utils";
|
|
361
|
+
import dayjs from "dayjs";
|
|
362
|
+
import ThemeDialog from "../other/themeDialog.vue";
|
|
363
|
+
import LayoutSwitch from "../other/layoutSwitch.vue";
|
|
364
|
+
import SelectDialog from "../other/selectDialog.vue";
|
|
365
|
+
import MemberManage from "../other/memberManage.vue";
|
|
366
|
+
import ChatArea from "../other/chatArea.vue";
|
|
367
|
+
import LeaveOptionDialog from "../other/leaveOptionDialog.vue";
|
|
368
|
+
import AppointDialog from "../other/appointDialog.vue";
|
|
369
|
+
import MoreOptionDialog from "../other/moreOptionDialog.vue";
|
|
370
|
+
import CallBoard from "../other/callBoard.vue";
|
|
371
|
+
import AddressBook from "../other/addressBook.vue";
|
|
372
|
+
import UpdateNameDialog from "../other/updateNameDialog.vue";
|
|
373
|
+
import SelectSpecialDialog from "../other/selectSpecialDialog.vue";
|
|
374
|
+
import ScreenShareBoard from "../other/screenShareBoard.vue";
|
|
375
|
+
import SettingDialog from "../other/settingDialog.vue";
|
|
376
|
+
import CustomLayout from "../other/customLayout.vue";
|
|
377
|
+
|
|
378
|
+
export default {
|
|
379
|
+
name: "LiveMultipleMeeting",
|
|
380
|
+
components: {
|
|
381
|
+
ThemeDialog,
|
|
382
|
+
LayoutSwitch,
|
|
383
|
+
SelectDialog,
|
|
384
|
+
MemberManage,
|
|
385
|
+
ChatArea,
|
|
386
|
+
LeaveOptionDialog,
|
|
387
|
+
AppointDialog,
|
|
388
|
+
MoreOptionDialog,
|
|
389
|
+
CallBoard,
|
|
390
|
+
AddressBook,
|
|
391
|
+
UpdateNameDialog,
|
|
392
|
+
SelectSpecialDialog,
|
|
393
|
+
ScreenShareBoard,
|
|
394
|
+
SettingDialog,
|
|
395
|
+
CustomLayout,
|
|
396
|
+
},
|
|
397
|
+
props: {
|
|
398
|
+
tempMeetingName: {
|
|
399
|
+
type: String,
|
|
400
|
+
default: "",
|
|
401
|
+
},
|
|
402
|
+
tempMeetingNum: {
|
|
403
|
+
type: String,
|
|
404
|
+
default: "",
|
|
405
|
+
},
|
|
406
|
+
tempCameraStatus: {
|
|
407
|
+
type: Boolean,
|
|
408
|
+
},
|
|
409
|
+
tempMicroStatus: {
|
|
410
|
+
type: Boolean,
|
|
411
|
+
},
|
|
412
|
+
tempOutputStatus: {
|
|
413
|
+
type: Boolean,
|
|
414
|
+
},
|
|
415
|
+
tempActiveDevice: {
|
|
416
|
+
type: Object,
|
|
417
|
+
default: () => ({}),
|
|
418
|
+
},
|
|
419
|
+
tempInviteList: {
|
|
420
|
+
type: Array,
|
|
421
|
+
default: () => [],
|
|
422
|
+
},
|
|
423
|
+
deviceList: {
|
|
424
|
+
type: Array,
|
|
425
|
+
default: () => [],
|
|
426
|
+
},
|
|
427
|
+
userData: {
|
|
428
|
+
type: Object,
|
|
429
|
+
default: () => ({}),
|
|
430
|
+
},
|
|
431
|
+
org: {
|
|
432
|
+
type: String,
|
|
433
|
+
default: "default",
|
|
434
|
+
},
|
|
435
|
+
token: {
|
|
436
|
+
type: String,
|
|
437
|
+
default: "",
|
|
438
|
+
},
|
|
439
|
+
joinType: {
|
|
440
|
+
type: String,
|
|
441
|
+
default: "launch",
|
|
442
|
+
},
|
|
443
|
+
isCustomizeMiniInvitations: {
|
|
444
|
+
type: Boolean,
|
|
445
|
+
default: false,
|
|
446
|
+
},
|
|
447
|
+
miniPagePath: {
|
|
448
|
+
type: String,
|
|
449
|
+
default: "",
|
|
450
|
+
},
|
|
451
|
+
defaultInviteWay: {
|
|
452
|
+
type: String,
|
|
453
|
+
default: "identity",
|
|
454
|
+
},
|
|
455
|
+
isInMeeting: {
|
|
456
|
+
type: Boolean,
|
|
457
|
+
default: false,
|
|
458
|
+
},
|
|
459
|
+
baseUrl: {
|
|
460
|
+
type: String,
|
|
461
|
+
default: "",
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
data() {
|
|
465
|
+
return {
|
|
466
|
+
// 会议基础数据
|
|
467
|
+
meetingName: "",
|
|
468
|
+
meetingNum: "",
|
|
469
|
+
meetingHost: {
|
|
470
|
+
ownerId: "",
|
|
471
|
+
ownerName: "",
|
|
472
|
+
},
|
|
473
|
+
meetingCoHost: [],
|
|
474
|
+
roomMetadata: {},
|
|
475
|
+
localMetadata: {},
|
|
476
|
+
localIdentity: "",
|
|
477
|
+
localName: "",
|
|
478
|
+
|
|
479
|
+
// 参与者数据
|
|
480
|
+
inviteList: [],
|
|
481
|
+
tempInvitedList: [],
|
|
482
|
+
participants: [],
|
|
483
|
+
joinedList: [],
|
|
484
|
+
unJoinedList: [],
|
|
485
|
+
filterJoinVal: "",
|
|
486
|
+
|
|
487
|
+
// 设备数据
|
|
488
|
+
meetingQuality: "",
|
|
489
|
+
meetingTTL: "",
|
|
490
|
+
audioDevicesRef: [],
|
|
491
|
+
videoDevicesRef: [],
|
|
492
|
+
outputDevicesRef: [],
|
|
493
|
+
activeDevice: {
|
|
494
|
+
audioInputDevice: "",
|
|
495
|
+
audioOutputDevice: "",
|
|
496
|
+
videoDevice: "",
|
|
497
|
+
},
|
|
498
|
+
|
|
499
|
+
// 消息数据
|
|
500
|
+
messageList: [],
|
|
501
|
+
meetingWXLink: "",
|
|
502
|
+
|
|
503
|
+
// 布局数据
|
|
504
|
+
currentLayout: "grid",
|
|
505
|
+
currentRoomMode: "normal",
|
|
506
|
+
layoutList: ["grid", "rightSide"],
|
|
507
|
+
layoutLabels: ["宫格", "右侧边栏"],
|
|
508
|
+
|
|
509
|
+
// 状态数据
|
|
510
|
+
duration: 0,
|
|
511
|
+
memberManageShow: false,
|
|
512
|
+
chatShow: false,
|
|
513
|
+
themeDialogShow: false,
|
|
514
|
+
layoutSwitchShow: false,
|
|
515
|
+
audioSelectShow: false,
|
|
516
|
+
videoSelectShow: false,
|
|
517
|
+
modeSelectShow: false,
|
|
518
|
+
isMicrophoneEnabled: false,
|
|
519
|
+
isCameraEnabled: false,
|
|
520
|
+
isScreenShareEnabled: false,
|
|
521
|
+
isOutputEnabled: false,
|
|
522
|
+
isRecording: false,
|
|
523
|
+
recordStreamUrl: null,
|
|
524
|
+
|
|
525
|
+
// 技术数据
|
|
526
|
+
pageEventList: [],
|
|
527
|
+
trackList: [],
|
|
528
|
+
curBlurIdentity: "",
|
|
529
|
+
curHostIdentity: null,
|
|
530
|
+
moreDialogShow: false,
|
|
531
|
+
optionDialogOffset: {},
|
|
532
|
+
optionIdentity: "",
|
|
533
|
+
pageFooterVisible: false,
|
|
534
|
+
footerVisibleDuration: 3,
|
|
535
|
+
messageTemplate: "测试短信",
|
|
536
|
+
isDeleteRoom: false,
|
|
537
|
+
isLeaveRoom: false,
|
|
538
|
+
leaveOptionShow: false,
|
|
539
|
+
leaveOptionOffset: {},
|
|
540
|
+
appointDialogShow: false,
|
|
541
|
+
callBoardShow: false,
|
|
542
|
+
callBoardOffset: {},
|
|
543
|
+
isScreenShareChange: false,
|
|
544
|
+
screenShareChangeOldLayout: "",
|
|
545
|
+
isDurationCalc: true,
|
|
546
|
+
isFullScreen: false,
|
|
547
|
+
inviteWaysSelectShow: false,
|
|
548
|
+
inviteWaysList: [
|
|
549
|
+
{
|
|
550
|
+
label: "通讯录邀请",
|
|
551
|
+
value: "addressbook",
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
label: "电话呼叫",
|
|
555
|
+
value: "videoCall",
|
|
556
|
+
},
|
|
557
|
+
],
|
|
558
|
+
screenShareCaptureOption: {
|
|
559
|
+
systemAudio: "exclude",
|
|
560
|
+
resolution: {
|
|
561
|
+
width: 1920,
|
|
562
|
+
height: 1080,
|
|
563
|
+
frameRate: 60,
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
screenShareChangeOldBlur: null,
|
|
567
|
+
isBatchUpdating: false,
|
|
568
|
+
isInitializing: false,
|
|
569
|
+
domOperationQueue: [],
|
|
570
|
+
watchEnabled: {
|
|
571
|
+
layout: true,
|
|
572
|
+
mode: true,
|
|
573
|
+
host: true,
|
|
574
|
+
blur: true,
|
|
575
|
+
},
|
|
576
|
+
customLayoutShow: false,
|
|
577
|
+
placeholderTemplate: `
|
|
578
|
+
<div class="board">
|
|
579
|
+
<div class="board-icon"></div>
|
|
580
|
+
</div>
|
|
581
|
+
<div class="describe">
|
|
582
|
+
<div class="identity">空缺席位</div>
|
|
583
|
+
</div>
|
|
584
|
+
`,
|
|
585
|
+
showMessage: null,
|
|
586
|
+
liveClient: null,
|
|
587
|
+
meetingInterval: null,
|
|
588
|
+
timeInterval: null,
|
|
589
|
+
durationInterval: null,
|
|
590
|
+
footerInterval: null,
|
|
591
|
+
unjoinParticipantInterval: null,
|
|
592
|
+
};
|
|
593
|
+
},
|
|
594
|
+
computed: {
|
|
595
|
+
meetingDuration() {
|
|
596
|
+
return calculateTime(this.duration);
|
|
597
|
+
},
|
|
598
|
+
participantNum() {
|
|
599
|
+
return this.participants.length;
|
|
600
|
+
},
|
|
601
|
+
participantIdentities() {
|
|
602
|
+
return this.participantNum > 0 ? this.participants.map((item) => item.identity) : [];
|
|
603
|
+
},
|
|
604
|
+
inviteNum() {
|
|
605
|
+
return this.inviteList.length;
|
|
606
|
+
},
|
|
607
|
+
deviceNum() {
|
|
608
|
+
return this.deviceList.length;
|
|
609
|
+
},
|
|
610
|
+
invitedNum() {
|
|
611
|
+
return this.tempInvitedList?.length || 0;
|
|
612
|
+
},
|
|
613
|
+
hasScreenShare() {
|
|
614
|
+
if (this.participantNum > 0) {
|
|
615
|
+
const index = this.participants.findIndex((item) => {
|
|
616
|
+
return item.isScreenShareEnabled;
|
|
617
|
+
});
|
|
618
|
+
return index;
|
|
619
|
+
} else {
|
|
620
|
+
return -1;
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
isBtnTextHide() {
|
|
624
|
+
return this.memberManageShow || this.chatShow;
|
|
625
|
+
},
|
|
626
|
+
layout() {
|
|
627
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
628
|
+
return "point-turn";
|
|
629
|
+
} else {
|
|
630
|
+
if (this.currentLayout === "grid") {
|
|
631
|
+
if (this.participantNum <= 1) {
|
|
632
|
+
return "grid1";
|
|
633
|
+
} else if (this.participantNum <= 2) {
|
|
634
|
+
return "grid2";
|
|
635
|
+
} else if (this.participantNum <= 4) {
|
|
636
|
+
return "grid3";
|
|
637
|
+
} else if (this.participantNum <= 6) {
|
|
638
|
+
return "grid4";
|
|
639
|
+
} else if (this.participantNum <= 9) {
|
|
640
|
+
return "grid5";
|
|
641
|
+
} else if (this.participantNum <= 12) {
|
|
642
|
+
return "grid6";
|
|
643
|
+
} else if (this.participantNum <= 16) {
|
|
644
|
+
return "grid7";
|
|
645
|
+
} else {
|
|
646
|
+
return "grid8";
|
|
647
|
+
}
|
|
648
|
+
} else {
|
|
649
|
+
return this.currentLayout;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
watch: {
|
|
655
|
+
footerVisibleDuration(newVal) {
|
|
656
|
+
if (newVal <= 0) {
|
|
657
|
+
this.pageFooterVisible = false;
|
|
658
|
+
if (this.footerInterval) {
|
|
659
|
+
clearInterval(this.footerInterval);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
currentLayout: {
|
|
664
|
+
handler(newLayout, oldLayout) {
|
|
665
|
+
if (!this.watchEnabled.layout || this.isBatchUpdating) {
|
|
666
|
+
console.log("布局watch被禁用,跳过DOM操作");
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
this.$nextTick(() => {
|
|
670
|
+
this.executeLayoutChange(newLayout, oldLayout);
|
|
671
|
+
});
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
curHostIdentity: {
|
|
675
|
+
handler(newVal, oldVal) {
|
|
676
|
+
if (!this.watchEnabled.host || this.isBatchUpdating) {
|
|
677
|
+
console.log("主持人watch被禁用,跳过DOM操作");
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
this.$nextTick(() => {
|
|
681
|
+
this.executeHostChange(newVal, oldVal);
|
|
682
|
+
});
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
curBlurIdentity: {
|
|
686
|
+
handler(newVal, oldVal) {
|
|
687
|
+
if (!this.watchEnabled.blur || this.isBatchUpdating) {
|
|
688
|
+
console.log("焦点用户watch被禁用,跳过DOM操作");
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
this.executeBlurChange(newVal, oldVal);
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
currentRoomMode: {
|
|
695
|
+
handler(newMode) {
|
|
696
|
+
if (!this.watchEnabled.mode || this.isBatchUpdating) {
|
|
697
|
+
console.log("模式watch被禁用,跳过DOM操作");
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
this.$nextTick(() => {
|
|
701
|
+
this.executeModeChange(newMode);
|
|
702
|
+
});
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
created() {
|
|
707
|
+
this.showMessage = new ShowMessage();
|
|
708
|
+
this.liveClient = null;
|
|
709
|
+
},
|
|
710
|
+
async mounted() {
|
|
711
|
+
await this.getDeviceList();
|
|
712
|
+
this.initData();
|
|
713
|
+
if (this.joinType === "launch") {
|
|
714
|
+
this.initLiveClient();
|
|
715
|
+
this.liveClient.roomName = this.tempMeetingName;
|
|
716
|
+
await this.createRoom();
|
|
717
|
+
} else {
|
|
718
|
+
this.initLiveClient();
|
|
719
|
+
this.meetingNum = this.tempMeetingNum;
|
|
720
|
+
await this.joinRoom();
|
|
721
|
+
}
|
|
722
|
+
this.setPageFooterVisible(5);
|
|
723
|
+
this.initGlobleEvent();
|
|
724
|
+
},
|
|
725
|
+
beforeDestroy() {
|
|
726
|
+
this.stopUnjoinParticipantPolling();
|
|
727
|
+
this.dispatchLiveClientEvent();
|
|
728
|
+
this.removeGlobleEvent();
|
|
729
|
+
this.liveClient = null;
|
|
730
|
+
},
|
|
731
|
+
methods: {
|
|
732
|
+
// DOM操作管理函数
|
|
733
|
+
async batchUpdateStates(updateFn) {
|
|
734
|
+
this.isBatchUpdating = true;
|
|
735
|
+
|
|
736
|
+
// 暂停所有watch监听器的DOM操作
|
|
737
|
+
this.watchEnabled = {
|
|
738
|
+
layout: false,
|
|
739
|
+
mode: false,
|
|
740
|
+
host: false,
|
|
741
|
+
blur: false,
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
try {
|
|
745
|
+
// 执行状态更新
|
|
746
|
+
await updateFn();
|
|
747
|
+
|
|
748
|
+
this.$nextTick(async () => {
|
|
749
|
+
await this.processDOMQueue();
|
|
750
|
+
});
|
|
751
|
+
} finally {
|
|
752
|
+
// 重新启用watch监听器
|
|
753
|
+
this.watchEnabled = {
|
|
754
|
+
layout: true,
|
|
755
|
+
mode: true,
|
|
756
|
+
host: true,
|
|
757
|
+
blur: true,
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
this.isBatchUpdating = false;
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
// 处理DOM操作队列
|
|
764
|
+
async processDOMQueue() {
|
|
765
|
+
if (this.domOperationQueue.length === 0) return;
|
|
766
|
+
|
|
767
|
+
console.log("执行DOM操作队列,队列长度:", this.domOperationQueue.length);
|
|
768
|
+
|
|
769
|
+
// 根据操作类型和优先级排序
|
|
770
|
+
const sortedQueue = [...this.domOperationQueue].sort((a, b) => {
|
|
771
|
+
const priority = { mode: 1, layout: 2, host: 3, blur: 4 };
|
|
772
|
+
return priority[a.type] - priority[b.type];
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
// 清空队列
|
|
776
|
+
this.domOperationQueue = [];
|
|
777
|
+
|
|
778
|
+
// 依次执行DOM操作
|
|
779
|
+
for (const operation of sortedQueue) {
|
|
780
|
+
try {
|
|
781
|
+
console.log(`开始执行DOM操作: ${operation.type}`);
|
|
782
|
+
await operation.execute();
|
|
783
|
+
this.$nextTick(() => {
|
|
784
|
+
console.log(`DOM操作完成: ${operation.type}`);
|
|
785
|
+
});
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error(`DOM操作执行失败:`, operation.type, error);
|
|
788
|
+
// 确保错误不会阻止后续操作
|
|
789
|
+
if (error.name === "NotFoundError" && error.message.includes("removeChild")) {
|
|
790
|
+
console.warn(`DOM元素已被移除,跳过此操作: ${operation.type}`);
|
|
791
|
+
} else {
|
|
792
|
+
console.error(`其他DOM操作错误:`, error);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
// 添加DOM操作到队列
|
|
798
|
+
addDOMOperation(type, executeFn) {
|
|
799
|
+
// 移除同类型的旧操作,只保留最新的
|
|
800
|
+
this.domOperationQueue = this.domOperationQueue.filter((op) => op.type !== type);
|
|
801
|
+
|
|
802
|
+
// 添加操作到队列
|
|
803
|
+
this.domOperationQueue.push({
|
|
804
|
+
type,
|
|
805
|
+
execute: async () => {
|
|
806
|
+
try {
|
|
807
|
+
// 在执行前检查DOM容器是否存在
|
|
808
|
+
const container = document.getElementById("room");
|
|
809
|
+
if (!container) {
|
|
810
|
+
console.warn(`DOM容器不存在,跳过${type}操作`);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// 执行实际的DOM操作
|
|
815
|
+
await executeFn();
|
|
816
|
+
} catch (error) {
|
|
817
|
+
console.error(`执行${type}DOM操作时发生错误:`, error);
|
|
818
|
+
throw error; // 重新抛出错误以便上层处理
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
timestamp: Date.now(),
|
|
822
|
+
});
|
|
823
|
+
},
|
|
824
|
+
// 动态等待与会者首次渲染完成
|
|
825
|
+
async waitForParticipantsRender() {
|
|
826
|
+
const minDelay = 1000; // 最小延迟1000ms,确保有时间开始渲染
|
|
827
|
+
const checkInterval = 100; // 每100ms检查一次
|
|
828
|
+
const stableTime = 300; // 稳定时间300ms,参与者数量不变则认为渲染完成
|
|
829
|
+
const maxWaitTime = 5000; // 最大等待5秒,避免无限等待
|
|
830
|
+
|
|
831
|
+
console.log(`开始动态等待,当前参与者数量: ${this.participantNum}`);
|
|
832
|
+
|
|
833
|
+
// 先等待最小延迟
|
|
834
|
+
await new Promise((resolve) => setTimeout(resolve, minDelay));
|
|
835
|
+
|
|
836
|
+
let lastParticipantNum = this.participantNum;
|
|
837
|
+
let stableStartTime = Date.now();
|
|
838
|
+
let totalStartTime = Date.now();
|
|
839
|
+
|
|
840
|
+
while (true) {
|
|
841
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
842
|
+
|
|
843
|
+
const currentParticipantNum = this.participantNum;
|
|
844
|
+
const currentTime = Date.now();
|
|
845
|
+
|
|
846
|
+
// 如果参与者数量发生变化,重置稳定计时器
|
|
847
|
+
if (currentParticipantNum !== lastParticipantNum) {
|
|
848
|
+
console.log(`参与者数量变化: ${lastParticipantNum} → ${currentParticipantNum}`);
|
|
849
|
+
lastParticipantNum = currentParticipantNum;
|
|
850
|
+
stableStartTime = currentTime;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// 检查是否达到稳定状态(参与者数量在stableTime内没有变化)
|
|
854
|
+
if (currentTime - stableStartTime >= stableTime) {
|
|
855
|
+
console.log(
|
|
856
|
+
`参与者数量稳定${stableTime}ms,当前数量: ${currentParticipantNum},认为首次渲染完成`
|
|
857
|
+
);
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// 检查是否超过最大等待时间
|
|
862
|
+
if (currentTime - totalStartTime >= maxWaitTime) {
|
|
863
|
+
console.log(
|
|
864
|
+
`达到最大等待时间${maxWaitTime}ms,强制结束等待,当前参与者数量: ${currentParticipantNum}`
|
|
865
|
+
);
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const totalWaitTime = Date.now() - totalStartTime;
|
|
871
|
+
console.log(
|
|
872
|
+
`动态等待完成,总等待时间: ${totalWaitTime}ms,最终参与者数量: ${this.participantNum}`
|
|
873
|
+
);
|
|
874
|
+
},
|
|
875
|
+
// 计算MoreOptionDialog选项数量的辅助函数
|
|
876
|
+
getOptionCount(identity) {
|
|
877
|
+
const participantItem = this.getUserItemByIdentity(identity);
|
|
878
|
+
if (!participantItem || !participantItem.metadata) return 8; // 默认最大选项数
|
|
879
|
+
|
|
880
|
+
const metadata = participantItem.metadata;
|
|
881
|
+
const isLocalParticipant = identity === this.localIdentity;
|
|
882
|
+
|
|
883
|
+
let optionCount = 3; // 基础选项:设为主屏、麦克风、摄像头
|
|
884
|
+
|
|
885
|
+
if (this.judgeParticipantIsHost(this.localIdentity)) {
|
|
886
|
+
// 本地为主持人
|
|
887
|
+
if (metadata?.platformID == 2 || metadata?.platformID == 5 || metadata?.platformID == 6) {
|
|
888
|
+
// 设备
|
|
889
|
+
optionCount += isLocalParticipant ? 1 : 2; // 修改名称 + 移出会议(非本地)
|
|
890
|
+
} else if (metadata?.platformID == 4) {
|
|
891
|
+
// volte手机端
|
|
892
|
+
optionCount += isLocalParticipant ? 1 : 3; // 置聋 + 修改名称 + 移出会议(非本地)
|
|
893
|
+
} else {
|
|
894
|
+
// app手机端
|
|
895
|
+
optionCount += isLocalParticipant ? 1 : 5; // 置聋 + 设为主持人 + 联席主持人 + 修改名称 + 移出会议(非本地)
|
|
896
|
+
}
|
|
897
|
+
} else if (this.judgeParticipantIsCoHost(this.localIdentity)) {
|
|
898
|
+
// 本地为联席主持人
|
|
899
|
+
if (metadata?.platformID == 2 || metadata?.platformID == 5 || metadata?.platformID == 6) {
|
|
900
|
+
optionCount += isLocalParticipant ? 1 : 2;
|
|
901
|
+
} else if (metadata?.platformID == 4) {
|
|
902
|
+
optionCount += isLocalParticipant ? 1 : 3;
|
|
903
|
+
} else {
|
|
904
|
+
optionCount += isLocalParticipant ? 1 : 4; // 置聋 + 联席主持人 + 修改名称 + 移出会议(非本地)
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return Math.min(optionCount, 8); // 限制最大选项数
|
|
909
|
+
},
|
|
910
|
+
// 弹窗定位工具函数
|
|
911
|
+
calculateDialogPosition(event, options = {}) {
|
|
912
|
+
const {
|
|
913
|
+
dialogWidth = 200,
|
|
914
|
+
dialogHeight = 300,
|
|
915
|
+
containerId = "multiple-meeting",
|
|
916
|
+
offsetX = 0,
|
|
917
|
+
offsetY = 20,
|
|
918
|
+
preferredPosition = "bottom-left", // 'bottom-left', 'bottom-right', 'top-left', 'top-right'
|
|
919
|
+
optionCount = null, // 动态计算高度的选项数量
|
|
920
|
+
} = options;
|
|
921
|
+
|
|
922
|
+
// 如果提供了选项数量,动态计算弹窗高度
|
|
923
|
+
let actualDialogHeight = dialogHeight;
|
|
924
|
+
if (optionCount && optionCount > 0) {
|
|
925
|
+
// MoreOptionDialog 每个选项36px高度 + 6px间距 + 8px padding (上下各4px)
|
|
926
|
+
actualDialogHeight = optionCount * 36 + (optionCount - 1) * 6 + 8;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// 获取容器的位置信息
|
|
930
|
+
const container = document.getElementById(containerId);
|
|
931
|
+
const containerRect = container ? container.getBoundingClientRect() : { left: 0, top: 0 };
|
|
932
|
+
|
|
933
|
+
// 计算相对于容器的坐标
|
|
934
|
+
const relativeX = event.clientX - containerRect.left;
|
|
935
|
+
const relativeY = event.clientY - containerRect.top;
|
|
936
|
+
|
|
937
|
+
// 获取容器尺寸
|
|
938
|
+
const containerWidth = container ? container.clientWidth : window.innerWidth;
|
|
939
|
+
const containerHeight = container ? container.clientHeight : window.innerHeight;
|
|
940
|
+
|
|
941
|
+
let left, top;
|
|
942
|
+
|
|
943
|
+
// 根据首选位置计算初始位置
|
|
944
|
+
switch (preferredPosition) {
|
|
945
|
+
case "bottom-right":
|
|
946
|
+
left = relativeX + offsetX;
|
|
947
|
+
top = relativeY + offsetY;
|
|
948
|
+
break;
|
|
949
|
+
case "top-left":
|
|
950
|
+
left = relativeX - dialogWidth - offsetX;
|
|
951
|
+
top = relativeY - actualDialogHeight - offsetY;
|
|
952
|
+
break;
|
|
953
|
+
case "top-right":
|
|
954
|
+
left = relativeX + offsetX;
|
|
955
|
+
top = relativeY - actualDialogHeight - offsetY;
|
|
956
|
+
break;
|
|
957
|
+
case "bottom-left":
|
|
958
|
+
default:
|
|
959
|
+
left = relativeX - dialogWidth - offsetX;
|
|
960
|
+
top = relativeY + offsetY;
|
|
961
|
+
break;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// 边界检查和自动调整
|
|
965
|
+
const margin = 10; // 距离边界的最小距离
|
|
966
|
+
|
|
967
|
+
// 检查左右边界
|
|
968
|
+
if (left < margin) {
|
|
969
|
+
left = relativeX + offsetX; // 切换到右侧
|
|
970
|
+
} else if (left + dialogWidth > containerWidth - margin) {
|
|
971
|
+
left = relativeX - dialogWidth - offsetX; // 切换到左侧
|
|
972
|
+
if (left < margin) {
|
|
973
|
+
left = containerWidth - dialogWidth - margin; // 贴右边界
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// 检查上下边界
|
|
978
|
+
if (top < margin) {
|
|
979
|
+
top = relativeY + offsetY; // 切换到下方
|
|
980
|
+
} else if (top + actualDialogHeight > containerHeight - margin) {
|
|
981
|
+
top = relativeY - actualDialogHeight - offsetY; // 切换到上方
|
|
982
|
+
if (top < margin) {
|
|
983
|
+
top = margin; // 贴上边界
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// 确保位置不为负数
|
|
988
|
+
left = Math.max(margin, left);
|
|
989
|
+
top = Math.max(margin, top);
|
|
990
|
+
|
|
991
|
+
return {
|
|
992
|
+
left: left + "px",
|
|
993
|
+
top: top + "px",
|
|
994
|
+
};
|
|
995
|
+
},
|
|
996
|
+
// 取消屏幕共享自动还原的逻辑
|
|
997
|
+
cancelScreenShareAutoRestore(reason) {
|
|
998
|
+
if (this.isScreenShareChange) {
|
|
999
|
+
console.log(`检测到屏幕共享期间${reason},取消自动还原`);
|
|
1000
|
+
this.isScreenShareChange = false;
|
|
1001
|
+
this.screenShareChangeOldLayout = null;
|
|
1002
|
+
this.screenShareChangeOldBlur = null;
|
|
1003
|
+
}
|
|
1004
|
+
},
|
|
1005
|
+
// 手动切换布局(供LayoutSwitch组件调用)
|
|
1006
|
+
async manualSetLayout(layout) {
|
|
1007
|
+
// 仅主持人和联席主持人可以切换布局
|
|
1008
|
+
if (
|
|
1009
|
+
!this.judgeParticipantIsHost(this.localIdentity) &&
|
|
1010
|
+
!this.judgeParticipantIsCoHost(this.localIdentity)
|
|
1011
|
+
) {
|
|
1012
|
+
this.showMessage.message("warning", "仅主持人和联席主持人可以切换布局");
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// 手动切换布局时取消自动还原
|
|
1017
|
+
this.cancelScreenShareAutoRestore("手动切换布局");
|
|
1018
|
+
|
|
1019
|
+
await this.setLayout(layout);
|
|
1020
|
+
},
|
|
1021
|
+
// 设置布局(内部使用,包括自动切换)
|
|
1022
|
+
async setLayout(layout) {
|
|
1023
|
+
// 仅主持人和联席主持人可以切换布局
|
|
1024
|
+
if (
|
|
1025
|
+
!this.judgeParticipantIsHost(this.localIdentity) &&
|
|
1026
|
+
!this.judgeParticipantIsCoHost(this.localIdentity)
|
|
1027
|
+
) {
|
|
1028
|
+
this.showMessage.message("warning", "仅主持人和联席主持人可以切换布局");
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
await this.liveClient.setRoomLayout(layout);
|
|
1032
|
+
},
|
|
1033
|
+
// 判断与会者是否为主持人
|
|
1034
|
+
judgeParticipantIsHost(identity) {
|
|
1035
|
+
return this.meetingHost.ownerId === identity;
|
|
1036
|
+
},
|
|
1037
|
+
// 判断与会者是否为联席主持人
|
|
1038
|
+
judgeParticipantIsCoHost(identity) {
|
|
1039
|
+
if (this.meetingCoHost.length > 0) {
|
|
1040
|
+
return this.meetingCoHost.some((item) => item.coHostId === identity);
|
|
1041
|
+
} else {
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
},
|
|
1045
|
+
// 判断是否已经在与会者列表中
|
|
1046
|
+
judgeParticipantInMeeting(identity) {
|
|
1047
|
+
if (this.participantNum > 0) {
|
|
1048
|
+
let index = this.participants.findIndex((item) => {
|
|
1049
|
+
return item.identity === identity;
|
|
1050
|
+
});
|
|
1051
|
+
if (index >= 0) {
|
|
1052
|
+
return index;
|
|
1053
|
+
} else {
|
|
1054
|
+
return -1;
|
|
1055
|
+
}
|
|
1056
|
+
} else {
|
|
1057
|
+
return -1;
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
// 添加用户到与会者列表
|
|
1061
|
+
addToParticipantList(userItem) {
|
|
1062
|
+
if (this.participantNum <= 0) {
|
|
1063
|
+
this.participants.push(userItem);
|
|
1064
|
+
} else {
|
|
1065
|
+
const index = this.judgeParticipantInMeeting(userItem.identity);
|
|
1066
|
+
if (index !== -1) {
|
|
1067
|
+
// 更新数组中与会者
|
|
1068
|
+
this.participants.splice(index, 1, userItem);
|
|
1069
|
+
} else {
|
|
1070
|
+
this.participants.push(userItem);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
removeFromParticipantList(identity) {
|
|
1075
|
+
if (this.participantNum > 0) {
|
|
1076
|
+
let index = this.judgeParticipantInMeeting(identity);
|
|
1077
|
+
if (index !== -1) {
|
|
1078
|
+
this.participants.splice(index, 1);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1082
|
+
// 查找匹配搜索值的与会者列表
|
|
1083
|
+
filterParticipantList() {
|
|
1084
|
+
this.joinedList = [];
|
|
1085
|
+
this.participants.forEach((item) => {
|
|
1086
|
+
if (item.name.includes(this.filterJoinVal)) {
|
|
1087
|
+
this.joinedList.push({
|
|
1088
|
+
identity: item?.identity,
|
|
1089
|
+
name: item.name,
|
|
1090
|
+
audioLevel: item.audioLevel,
|
|
1091
|
+
isCameraEnabled: item.isCameraEnabled,
|
|
1092
|
+
isLocal: item.isLocal,
|
|
1093
|
+
isMicrophoneEnabled: item.isMicrophoneEnabled,
|
|
1094
|
+
isScreenShareEnabled: item.isScreenShareEnabled,
|
|
1095
|
+
isSpeaking: item.isSpeaking,
|
|
1096
|
+
sid: item.sid,
|
|
1097
|
+
metadata: item.metadata,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
},
|
|
1102
|
+
// 将用户从邀请列表中移除
|
|
1103
|
+
removeFromInviteList(identity) {
|
|
1104
|
+
if (this.inviteNum > 0) {
|
|
1105
|
+
const index = this.inviteList.findIndex((item) => {
|
|
1106
|
+
if (item?.loginCode) {
|
|
1107
|
+
return item.loginCode === identity;
|
|
1108
|
+
} else {
|
|
1109
|
+
return item.phone === identity;
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
1112
|
+
if (index !== -1) {
|
|
1113
|
+
this.inviteList.splice(index, 1);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
},
|
|
1117
|
+
// 添加用户到邀请列表中
|
|
1118
|
+
addToInviteList(userItem) {
|
|
1119
|
+
if (this.inviteNum <= 0) {
|
|
1120
|
+
this.inviteList.push(userItem);
|
|
1121
|
+
} else {
|
|
1122
|
+
if (this.judgePersonIsInvited(userItem.id) < 0) {
|
|
1123
|
+
this.inviteList.push(userItem);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
// 判断是否已经在邀请列表中
|
|
1128
|
+
judgePersonIsInvited(id) {
|
|
1129
|
+
if (this.inviteNum > 0) {
|
|
1130
|
+
let index = this.inviteList.findIndex((item) => {
|
|
1131
|
+
return item.id === id;
|
|
1132
|
+
});
|
|
1133
|
+
return index;
|
|
1134
|
+
} else {
|
|
1135
|
+
return -1;
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
// 判断是否有除自身外其他与会者共享屏幕
|
|
1139
|
+
judgeOtherScreenShare(identity) {
|
|
1140
|
+
if (this.participantNum > 0) {
|
|
1141
|
+
const index = this.participants.findIndex((item) => {
|
|
1142
|
+
return item.isScreenShareEnabled && item.identity !== identity;
|
|
1143
|
+
});
|
|
1144
|
+
return index;
|
|
1145
|
+
} else {
|
|
1146
|
+
return -1;
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
// 将消息添加到消息队列
|
|
1150
|
+
addToMessageList(message) {
|
|
1151
|
+
this.messageList.push(message);
|
|
1152
|
+
},
|
|
1153
|
+
// 根据与会者identity返回与会者
|
|
1154
|
+
getUserItemByIdentity(identity) {
|
|
1155
|
+
if (this.participantNum > 0) {
|
|
1156
|
+
const index = this.participants.findIndex((item) => {
|
|
1157
|
+
return item.identity === identity;
|
|
1158
|
+
});
|
|
1159
|
+
if (index > -1) {
|
|
1160
|
+
return this.participants[index];
|
|
1161
|
+
} else {
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
} else {
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1168
|
+
// 返回本地与会者
|
|
1169
|
+
getLocalParticipant() {
|
|
1170
|
+
if (this.participantNum > 0) {
|
|
1171
|
+
const index = this.participants.findIndex((item) => {
|
|
1172
|
+
return item.isLocal;
|
|
1173
|
+
});
|
|
1174
|
+
if (index > -1) {
|
|
1175
|
+
return this.participants[index];
|
|
1176
|
+
} else {
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
} else {
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
},
|
|
1183
|
+
|
|
1184
|
+
// 基础方法
|
|
1185
|
+
async cancelMeeting() {
|
|
1186
|
+
if (this.isFullScreen) {
|
|
1187
|
+
this.isDeleteRoom = true;
|
|
1188
|
+
this.leaveRoom();
|
|
1189
|
+
} else {
|
|
1190
|
+
this.$confirm("点击结束会议后会议将被解散,您确定要这么做么?", "警告", {
|
|
1191
|
+
confirmButtonText: "确定",
|
|
1192
|
+
cancelButtonText: "取消",
|
|
1193
|
+
type: "warning",
|
|
1194
|
+
})
|
|
1195
|
+
.then(() => {
|
|
1196
|
+
this.isDeleteRoom = true;
|
|
1197
|
+
this.leaveRoom();
|
|
1198
|
+
})
|
|
1199
|
+
.catch(() => {
|
|
1200
|
+
void 0;
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
},
|
|
1204
|
+
|
|
1205
|
+
openAppointDialog() {
|
|
1206
|
+
if (this.judgeParticipantIsHost(this.localIdentity)) {
|
|
1207
|
+
if (this.participantNum <= 1) {
|
|
1208
|
+
this.cancelMeeting();
|
|
1209
|
+
} else {
|
|
1210
|
+
const index = this.participants.findIndex((participant) => {
|
|
1211
|
+
return (
|
|
1212
|
+
(participant.metadata.platformID == 0 || participant.metadata.platformID == 1) &&
|
|
1213
|
+
participant.identity !== this.localIdentity
|
|
1214
|
+
);
|
|
1215
|
+
});
|
|
1216
|
+
if (index !== -1) {
|
|
1217
|
+
this.appointDialogShow = true;
|
|
1218
|
+
} else {
|
|
1219
|
+
this.$confirm("当前会议没有pc端和app端与会者, 无法指派主持人, 将直接解散会议", "警告", {
|
|
1220
|
+
confirmButtonText: "确定",
|
|
1221
|
+
cancelButtonText: "取消",
|
|
1222
|
+
type: "warning",
|
|
1223
|
+
})
|
|
1224
|
+
.then(() => {
|
|
1225
|
+
this.isDeleteRoom = true;
|
|
1226
|
+
this.leaveRoom();
|
|
1227
|
+
})
|
|
1228
|
+
.catch(() => {
|
|
1229
|
+
void 0;
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
},
|
|
1235
|
+
initData() {
|
|
1236
|
+
this.participants = [];
|
|
1237
|
+
this.isCameraEnabled = this.tempCameraStatus;
|
|
1238
|
+
this.isMicrophoneEnabled = this.tempMicroStatus;
|
|
1239
|
+
this.isOutputEnabled = this.tempOutputStatus;
|
|
1240
|
+
},
|
|
1241
|
+
|
|
1242
|
+
async getDeviceList() {
|
|
1243
|
+
try {
|
|
1244
|
+
const { audioDevices, videoDevices, outputDevices } = await LiveClient.getDeviceList();
|
|
1245
|
+
this.audioDevicesRef = audioDevices.map((item) => {
|
|
1246
|
+
return {
|
|
1247
|
+
label: item.label,
|
|
1248
|
+
value: item.deviceId,
|
|
1249
|
+
};
|
|
1250
|
+
});
|
|
1251
|
+
this.videoDevicesRef = videoDevices.map((item) => {
|
|
1252
|
+
return {
|
|
1253
|
+
label: item.label,
|
|
1254
|
+
value: item.deviceId,
|
|
1255
|
+
};
|
|
1256
|
+
});
|
|
1257
|
+
this.outputDevicesRef = outputDevices.map((item) => {
|
|
1258
|
+
return {
|
|
1259
|
+
label: item.label,
|
|
1260
|
+
value: item.deviceId,
|
|
1261
|
+
};
|
|
1262
|
+
});
|
|
1263
|
+
} catch (err) {
|
|
1264
|
+
this.showMessage.message("error", "获取设备列表失败");
|
|
1265
|
+
}
|
|
1266
|
+
},
|
|
1267
|
+
// 保存liveClient监听的事件到数组中,方便离开页面时解除绑定
|
|
1268
|
+
addEventToList(eventName) {
|
|
1269
|
+
if (this.pageEventList.indexOf(eventName) < 0) {
|
|
1270
|
+
this.pageEventList.push(eventName);
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
clearPageInterval() {
|
|
1274
|
+
if (this.meetingInterval) {
|
|
1275
|
+
clearInterval(this.meetingInterval);
|
|
1276
|
+
this.meetingInterval = null;
|
|
1277
|
+
}
|
|
1278
|
+
if (this.durationInterval) {
|
|
1279
|
+
clearInterval(this.durationInterval);
|
|
1280
|
+
this.durationInterval = null;
|
|
1281
|
+
}
|
|
1282
|
+
if (this.footerInterval) {
|
|
1283
|
+
clearInterval(this.footerInterval);
|
|
1284
|
+
this.footerInterval = null;
|
|
1285
|
+
}
|
|
1286
|
+
if (this.timeInterval) {
|
|
1287
|
+
clearInterval(this.timeInterval);
|
|
1288
|
+
this.timeInterval = null;
|
|
1289
|
+
}
|
|
1290
|
+
if (this.unjoinParticipantInterval) {
|
|
1291
|
+
clearInterval(this.unjoinParticipantInterval);
|
|
1292
|
+
this.unjoinParticipantInterval = null;
|
|
1293
|
+
}
|
|
1294
|
+
},
|
|
1295
|
+
clearAllTrack() {
|
|
1296
|
+
if (this.trackList.length > 0) {
|
|
1297
|
+
this.trackList.forEach((track) => {
|
|
1298
|
+
track?.detach();
|
|
1299
|
+
track?.stop();
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
},
|
|
1303
|
+
initLiveClientEvent() {
|
|
1304
|
+
if (!this.liveClient) {
|
|
1305
|
+
this.showMessage.message("error", "liveClient初始化失败");
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
this.liveClient.on("errorAlert", (e) => {
|
|
1309
|
+
console.log("errorAlert", e);
|
|
1310
|
+
this.addEventToList("errorAlert");
|
|
1311
|
+
if (e.severity == "high" || e.severity == "critical") {
|
|
1312
|
+
if (e?.context?.originalMessage == "could not establish pc connection") {
|
|
1313
|
+
this.showMessage.message("error", "无法建立连接,请检查udp端口是否启用");
|
|
1314
|
+
} else {
|
|
1315
|
+
this.showMessage.message("error", e.message);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
this.liveClient.on("roomCreatedSuccess", async (e) => {
|
|
1320
|
+
this.addEventToList("roomCreatedSuccess");
|
|
1321
|
+
this.showMessage.message("success", "会议房间创建成功");
|
|
1322
|
+
this.meetingNum = e;
|
|
1323
|
+
await this.joinRoom();
|
|
1324
|
+
});
|
|
1325
|
+
this.liveClient.on("joinRoomError", (e) => {
|
|
1326
|
+
this.addEventToList("joinRoomError");
|
|
1327
|
+
console.error("加入房间失败", e.message);
|
|
1328
|
+
this.$emit("meetingEnd");
|
|
1329
|
+
if (e.message.includes("unknown websocket error")) {
|
|
1330
|
+
this.showMessage.message("error", `加入房间失败: 请检查会议内websocket连接是否正常`);
|
|
1331
|
+
} else if (e.message.includes("could not establish pc connection")) {
|
|
1332
|
+
this.showMessage.message("error", `加入房间失败: 请检查udp端口是否启用`);
|
|
1333
|
+
} else {
|
|
1334
|
+
this.showMessage.message("error", `加入房间失败: ${e.message}`);
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
this.liveClient.on("roomMetadataChanged", (e) => {
|
|
1338
|
+
const metadata = JSON.parse(e);
|
|
1339
|
+
this.roomMetadataChange(metadata);
|
|
1340
|
+
});
|
|
1341
|
+
this.liveClient.on("roomDataReceived", (e) => {
|
|
1342
|
+
this.addEventToList("roomDataReceived");
|
|
1343
|
+
console.log("roomDataReceived", e);
|
|
1344
|
+
this.handleRoomDataReceived(e);
|
|
1345
|
+
});
|
|
1346
|
+
this.liveClient.on("participantDataReceived", (e) => {
|
|
1347
|
+
this.addEventToList("participantDataReceived");
|
|
1348
|
+
console.log("participantDataReceived", e);
|
|
1349
|
+
});
|
|
1350
|
+
this.liveClient.on("roomConnected", async (e) => {
|
|
1351
|
+
this.addEventToList("roomConnected");
|
|
1352
|
+
if (this.liveClient.room) {
|
|
1353
|
+
const metadata = JSON.parse(this.liveClient.room.metadata);
|
|
1354
|
+
// 初始化部分会议信息
|
|
1355
|
+
this.initRoomMetadata(metadata);
|
|
1356
|
+
}
|
|
1357
|
+
if (this.userData?.username) {
|
|
1358
|
+
// 如果用户已经登录,则将入会名称修改为用户名
|
|
1359
|
+
this.liveClient.updateParticipantName(this.userData.userId, this.userData.username);
|
|
1360
|
+
}
|
|
1361
|
+
this.meetingInterval = setInterval(() => {
|
|
1362
|
+
this.meetingTTL = navigator.connection.rtt;
|
|
1363
|
+
}, 1000);
|
|
1364
|
+
console.log("joinType", this.joinType);
|
|
1365
|
+
// 判断当前是发起者还是加入者,是发起者则发送邀请信息
|
|
1366
|
+
this.$emit("meetingStart");
|
|
1367
|
+
if (this.joinType == "launch") {
|
|
1368
|
+
// 发送邀请信息
|
|
1369
|
+
this.sendInviteMessage([], this.defaultInviteWay == "mini" ? 3 : 0);
|
|
1370
|
+
// 拉取监控设备
|
|
1371
|
+
this.deviceNum > 0 &&
|
|
1372
|
+
this.deviceList.forEach((item) => {
|
|
1373
|
+
if (item.source == "监控") {
|
|
1374
|
+
this.pullMonitorDevice(item.monitorID, item.label);
|
|
1375
|
+
} else if (item.source == "设备") {
|
|
1376
|
+
this.pullMonitorDevice(item.equipmentID, item.label);
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
// 启动获取未入会和邀请人员轮询
|
|
1381
|
+
this.startUnjoinParticipantPolling();
|
|
1382
|
+
});
|
|
1383
|
+
this.liveClient.on("roomDisconnected", () => {
|
|
1384
|
+
this.addEventToList("roomDisconnected");
|
|
1385
|
+
this.showMessage.message("info", "房间链接已断开");
|
|
1386
|
+
this.$emit("meetingEnd");
|
|
1387
|
+
this.stopUnjoinParticipantPolling();
|
|
1388
|
+
this.clearPageInterval();
|
|
1389
|
+
this.clearAllTrack();
|
|
1390
|
+
if (this.isDeleteRoom) {
|
|
1391
|
+
this.liveClient
|
|
1392
|
+
.deleteRoom(this.meetingNum)
|
|
1393
|
+
.then((res) => {
|
|
1394
|
+
if (res.code == 200) {
|
|
1395
|
+
this.exitPage();
|
|
1396
|
+
} else {
|
|
1397
|
+
this.showMessage.message("error", "删除会议失败");
|
|
1398
|
+
}
|
|
1399
|
+
})
|
|
1400
|
+
.catch((err) => {
|
|
1401
|
+
this.showMessage.message("error", "删除会议失败");
|
|
1402
|
+
this.exitPage();
|
|
1403
|
+
});
|
|
1404
|
+
} else {
|
|
1405
|
+
this.exitPage();
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
this.liveClient.on("localCameraChange", (e) => {
|
|
1409
|
+
this.addEventToList("localCameraChange");
|
|
1410
|
+
this.isCameraEnabled = e;
|
|
1411
|
+
if (this.isCameraEnabled) {
|
|
1412
|
+
this.showMessage.message("success", "摄像头已开启");
|
|
1413
|
+
} else {
|
|
1414
|
+
this.showMessage.message("success", "摄像头已关闭");
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
this.liveClient.on("localMicrophoneChange", (e) => {
|
|
1418
|
+
this.addEventToList("localMicrophoneChange");
|
|
1419
|
+
this.isMicrophoneEnabled = e;
|
|
1420
|
+
if (this.isMicrophoneEnabled) {
|
|
1421
|
+
this.showMessage.message("success", "麦克风已开启");
|
|
1422
|
+
} else {
|
|
1423
|
+
this.showMessage.message("success", "麦克风已关闭");
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
this.liveClient.on("localScreenShareChange", (e) => {
|
|
1427
|
+
this.addEventToList("localScreenShareChange");
|
|
1428
|
+
this.isScreenShareEnabled = e;
|
|
1429
|
+
if (this.isScreenShareEnabled) {
|
|
1430
|
+
this.showMessage.message("success", "屏幕共享已开启");
|
|
1431
|
+
} else {
|
|
1432
|
+
this.showMessage.message("success", "屏幕共享已关闭");
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
this.liveClient.on("startRecording", (e) => {
|
|
1436
|
+
this.addEventToList("startRecording");
|
|
1437
|
+
this.isRecording = true;
|
|
1438
|
+
this.recordStreamUrl = e;
|
|
1439
|
+
this.showMessage.message("success", "开始录制");
|
|
1440
|
+
});
|
|
1441
|
+
this.liveClient.on("stopRecording", (e) => {
|
|
1442
|
+
this.addEventToList("stopRecording");
|
|
1443
|
+
this.isRecording = false;
|
|
1444
|
+
this.showMessage.message("success", "结束录制");
|
|
1445
|
+
});
|
|
1446
|
+
this.liveClient.on("mediaDeviceChange", (e) => {
|
|
1447
|
+
this.addEventToList("mediaDeviceChange");
|
|
1448
|
+
// showMessage.message("success", "设备切换成功");
|
|
1449
|
+
console.log("设备类型" + e.kind, e.deviceId);
|
|
1450
|
+
if (e.kind == "audioinput") {
|
|
1451
|
+
this.activeDevice.audioInputDevice = e.deviceId;
|
|
1452
|
+
} else if (e.kind == "videoinput") {
|
|
1453
|
+
this.activeDevice.videoDevice = e.deviceId;
|
|
1454
|
+
} else {
|
|
1455
|
+
this.activeDevice.audioOutputDevice = e.deviceId;
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
this.liveClient.on("videoCallRender", (e) => {
|
|
1459
|
+
this.addEventToList("videoCallRender");
|
|
1460
|
+
this.renderVideoItem(e);
|
|
1461
|
+
});
|
|
1462
|
+
this.liveClient.on("activeSpeakerChange", (e) => {
|
|
1463
|
+
this.addEventToList("activeSpeakerChange");
|
|
1464
|
+
if (e.length > 0) {
|
|
1465
|
+
let maxAudioLevel = 0,
|
|
1466
|
+
maxAudioLevelIndex = -1;
|
|
1467
|
+
e.forEach((speaker, index) => {
|
|
1468
|
+
if (speaker.audioLevel > maxAudioLevel) {
|
|
1469
|
+
maxAudioLevel = speaker.audioLevel;
|
|
1470
|
+
maxAudioLevelIndex = index;
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
if (maxAudioLevelIndex >= 0) {
|
|
1474
|
+
this.switchActiveSpeakerToFirst(e[maxAudioLevelIndex]);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
});
|
|
1478
|
+
},
|
|
1479
|
+
handleRoomDataReceived(e) {
|
|
1480
|
+
switch (e.topic) {
|
|
1481
|
+
case "MsgEvent":
|
|
1482
|
+
// 聊天消息
|
|
1483
|
+
this.handleChatMessage(e);
|
|
1484
|
+
break;
|
|
1485
|
+
case "MuteEvent":
|
|
1486
|
+
// 麦克风/摄像头/置聋/禁言消息
|
|
1487
|
+
this.handleMuteMessage(e);
|
|
1488
|
+
break;
|
|
1489
|
+
case "UpdateNameEvent":
|
|
1490
|
+
// 更新名称消息
|
|
1491
|
+
this.handleUpdateNameMessage(e);
|
|
1492
|
+
break;
|
|
1493
|
+
case "HostEvent":
|
|
1494
|
+
// 主持人/联席主持人消息
|
|
1495
|
+
this.handleHostMessage(e);
|
|
1496
|
+
break;
|
|
1497
|
+
default:
|
|
1498
|
+
break;
|
|
1499
|
+
}
|
|
1500
|
+
},
|
|
1501
|
+
handleUpdateNameMessage(e) {
|
|
1502
|
+
const message = JSON.parse(this.liveClient.decodeMessage(e.data));
|
|
1503
|
+
console.log("更新名称消息", message);
|
|
1504
|
+
|
|
1505
|
+
if (message.operationSource === "1") {
|
|
1506
|
+
this.showMessage.message("info", "主持人将您的名称更新为" + message.Name);
|
|
1507
|
+
} else if (message.operationSource === "2") {
|
|
1508
|
+
this.showMessage.message("info", "联席主持人将您的名称更新为" + message.Name);
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
handleHostMessage(e) {
|
|
1512
|
+
const message = JSON.parse(this.liveClient.decodeMessage(e.data));
|
|
1513
|
+
console.log("主持人/联席主持人消息", message);
|
|
1514
|
+
|
|
1515
|
+
const operationMessages = {
|
|
1516
|
+
1: {
|
|
1517
|
+
// 主持人
|
|
1518
|
+
1: "您被设为主持人",
|
|
1519
|
+
2: "您被移除主持人",
|
|
1520
|
+
},
|
|
1521
|
+
2: {
|
|
1522
|
+
// 联席主持人
|
|
1523
|
+
1: "您被设为联席主持人",
|
|
1524
|
+
2: "您被移除联席主持人",
|
|
1525
|
+
},
|
|
1526
|
+
};
|
|
1527
|
+
|
|
1528
|
+
const messageText = operationMessages[message.input]?.[message.operationType];
|
|
1529
|
+
|
|
1530
|
+
if (messageText) {
|
|
1531
|
+
this.showMessage.message("info", messageText);
|
|
1532
|
+
}
|
|
1533
|
+
},
|
|
1534
|
+
|
|
1535
|
+
handleChatMessage(e) {
|
|
1536
|
+
const message = JSON.parse(this.liveClient.decodeMessage(e.data));
|
|
1537
|
+
// message.timestamp = dayjs().format("HH:mm");
|
|
1538
|
+
const messageHandled = {
|
|
1539
|
+
origin: message.sendID,
|
|
1540
|
+
fromName: message.senderNickname,
|
|
1541
|
+
content: message.contentType == 1 ? message.content.content : message.content.sourcePath,
|
|
1542
|
+
contentType: message.contentType,
|
|
1543
|
+
timestamp: dayjs(new Date(message.sendTime)).format("HH:mm"),
|
|
1544
|
+
srcList: message.contentType == 2 ? [message.content.sourcePath] : [],
|
|
1545
|
+
};
|
|
1546
|
+
console.log("聊天消息", messageHandled);
|
|
1547
|
+
this.addToMessageList(messageHandled);
|
|
1548
|
+
},
|
|
1549
|
+
handleMuteMessage(e) {
|
|
1550
|
+
const message = JSON.parse(this.liveClient.decodeMessage(e.data));
|
|
1551
|
+
console.log("麦克风/摄像头/置聋/禁言消息", message);
|
|
1552
|
+
// 定义操作类型和输入类型的映射
|
|
1553
|
+
const operationMessages = {
|
|
1554
|
+
1: {
|
|
1555
|
+
// 主持人
|
|
1556
|
+
1: {
|
|
1557
|
+
// 请求打开
|
|
1558
|
+
1: "主持人请求打开您的麦克风",
|
|
1559
|
+
2: "主持人请求打开您的摄像头",
|
|
1560
|
+
3: "主持人将您取消置聋",
|
|
1561
|
+
4: "主持人将您取消禁言",
|
|
1562
|
+
},
|
|
1563
|
+
2: {
|
|
1564
|
+
// 关闭
|
|
1565
|
+
1: "主持人关闭您的麦克风",
|
|
1566
|
+
2: "主持人关闭您的摄像头",
|
|
1567
|
+
3: "主持人将您置聋",
|
|
1568
|
+
4: "主持人将您禁言",
|
|
1569
|
+
},
|
|
1570
|
+
},
|
|
1571
|
+
2: {
|
|
1572
|
+
// 联席主持人
|
|
1573
|
+
1: {
|
|
1574
|
+
// 请求打开
|
|
1575
|
+
1: "联席主持人请求打开您的麦克风",
|
|
1576
|
+
2: "联席主持人请求打开您的摄像头",
|
|
1577
|
+
3: "联席主持人将您取消置聋",
|
|
1578
|
+
4: "联席主持人将您取消禁言",
|
|
1579
|
+
},
|
|
1580
|
+
2: {
|
|
1581
|
+
// 关闭
|
|
1582
|
+
1: "联席主持人关闭您的麦克风",
|
|
1583
|
+
2: "联席主持人关闭您的摄像头",
|
|
1584
|
+
3: "联席主持人将您置聋",
|
|
1585
|
+
4: "联席主持人将您禁言",
|
|
1586
|
+
},
|
|
1587
|
+
},
|
|
1588
|
+
};
|
|
1589
|
+
|
|
1590
|
+
const messageText =
|
|
1591
|
+
operationMessages[message.operationSource]?.[message.operationType]?.[message.input];
|
|
1592
|
+
|
|
1593
|
+
if (messageText) {
|
|
1594
|
+
this.showMessage.message("info", messageText);
|
|
1595
|
+
}
|
|
1596
|
+
},
|
|
1597
|
+
async initRoomMetadata(metadata) {
|
|
1598
|
+
console.log("roomMetadata", metadata);
|
|
1599
|
+
|
|
1600
|
+
// 标记为初始化状态
|
|
1601
|
+
this.isInitializing = true;
|
|
1602
|
+
|
|
1603
|
+
await this.batchUpdateStates(async () => {
|
|
1604
|
+
this.meetingName = metadata.roomTheme;
|
|
1605
|
+
this.meetingHost = {
|
|
1606
|
+
ownerId: metadata.ownerID,
|
|
1607
|
+
ownerName: metadata?.ownerName ?? "暂无",
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
if (metadata.coHosts && metadata.coHosts.length > 0) {
|
|
1611
|
+
this.meetingCoHost = metadata.coHosts.map((item) => {
|
|
1612
|
+
return {
|
|
1613
|
+
coHostId: item.coHostID,
|
|
1614
|
+
};
|
|
1615
|
+
});
|
|
1616
|
+
} else {
|
|
1617
|
+
this.meetingCoHost = [];
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
this.roomMetadata = metadata;
|
|
1621
|
+
|
|
1622
|
+
// 批量设置关键状态
|
|
1623
|
+
const newHostIdentity = this.meetingHost.ownerId;
|
|
1624
|
+
const newBlurIdentity = metadata.blurID;
|
|
1625
|
+
const newLayout = metadata.frontLayout || "grid";
|
|
1626
|
+
const newMode = metadata.roomMode || "normal";
|
|
1627
|
+
|
|
1628
|
+
// 记录变化,但先不触发watch
|
|
1629
|
+
const changes = {
|
|
1630
|
+
host: { old: this.curHostIdentity, new: newHostIdentity },
|
|
1631
|
+
blur: { old: this.curBlurIdentity, new: newBlurIdentity },
|
|
1632
|
+
layout: { old: this.currentLayout, new: newLayout },
|
|
1633
|
+
mode: { old: this.currentRoomMode, new: newMode },
|
|
1634
|
+
};
|
|
1635
|
+
|
|
1636
|
+
// 如果是加入会议,在设置新值前等待renderVideoItem首次渲染完成
|
|
1637
|
+
if (this.joinType !== "launch") {
|
|
1638
|
+
console.log("加入会议模式:动态等待renderVideoItem首次渲染完成,然后设置关键状态");
|
|
1639
|
+
await this.waitForParticipantsRender();
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// 设置新值
|
|
1643
|
+
this.curHostIdentity = newHostIdentity;
|
|
1644
|
+
this.curBlurIdentity = newBlurIdentity;
|
|
1645
|
+
this.currentLayout = newLayout;
|
|
1646
|
+
this.currentRoomMode = newMode;
|
|
1647
|
+
|
|
1648
|
+
// 添加需要的DOM操作到队列
|
|
1649
|
+
if (changes.mode.old !== changes.mode.new) {
|
|
1650
|
+
this.addDOMOperation("mode", () => this.executeModeChange(changes.mode.new));
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
if (changes.layout.old !== changes.layout.new) {
|
|
1654
|
+
this.addDOMOperation("layout", () =>
|
|
1655
|
+
this.executeLayoutChange(changes.layout.new, changes.layout.old)
|
|
1656
|
+
);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
if (changes.host.old !== changes.host.new) {
|
|
1660
|
+
this.addDOMOperation("host", () =>
|
|
1661
|
+
this.executeHostChange(changes.host.new, changes.host.old)
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
if (changes.blur.old !== changes.blur.new) {
|
|
1666
|
+
this.addDOMOperation("blur", () =>
|
|
1667
|
+
this.executeBlurChange(changes.blur.new, changes.blur.old)
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
this.isInitializing = false;
|
|
1673
|
+
this.initRoomDuration(metadata);
|
|
1674
|
+
},
|
|
1675
|
+
async roomMetadataChange(metadata) {
|
|
1676
|
+
console.log("roomMetadataChange", metadata);
|
|
1677
|
+
|
|
1678
|
+
await this.batchUpdateStates(async () => {
|
|
1679
|
+
this.meetingName = metadata.roomTheme;
|
|
1680
|
+
this.meetingHost = {
|
|
1681
|
+
ownerId: metadata.ownerID,
|
|
1682
|
+
ownerName: metadata?.ownerName ?? "暂无",
|
|
1683
|
+
};
|
|
1684
|
+
|
|
1685
|
+
if (metadata.coHosts && metadata.coHosts.length > 0) {
|
|
1686
|
+
this.meetingCoHost = metadata.coHosts.map((item) => {
|
|
1687
|
+
return {
|
|
1688
|
+
coHostId: item.coHostID,
|
|
1689
|
+
};
|
|
1690
|
+
});
|
|
1691
|
+
} else {
|
|
1692
|
+
this.meetingCoHost = [];
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
this.roomMetadata = metadata;
|
|
1696
|
+
|
|
1697
|
+
// 批量设置关键状态
|
|
1698
|
+
const newHostIdentity = this.meetingHost.ownerId;
|
|
1699
|
+
const newBlurIdentity = metadata.blurID;
|
|
1700
|
+
const newLayout = metadata.frontLayout || "grid";
|
|
1701
|
+
const newMode = metadata.roomMode || "normal";
|
|
1702
|
+
|
|
1703
|
+
// 记录变化
|
|
1704
|
+
const changes = {
|
|
1705
|
+
host: { old: this.curHostIdentity, new: newHostIdentity },
|
|
1706
|
+
blur: { old: this.curBlurIdentity, new: newBlurIdentity },
|
|
1707
|
+
layout: { old: this.currentLayout, new: newLayout },
|
|
1708
|
+
mode: { old: this.currentRoomMode, new: newMode },
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1711
|
+
// 设置新值
|
|
1712
|
+
this.curHostIdentity = newHostIdentity;
|
|
1713
|
+
this.curBlurIdentity = newBlurIdentity;
|
|
1714
|
+
this.currentLayout = newLayout;
|
|
1715
|
+
this.currentRoomMode = newMode;
|
|
1716
|
+
|
|
1717
|
+
// 添加需要的DOM操作到队列
|
|
1718
|
+
if (changes.mode.old !== changes.mode.new) {
|
|
1719
|
+
this.addDOMOperation("mode", () => this.executeModeChange(changes.mode.new));
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
if (changes.layout.old !== changes.layout.new) {
|
|
1723
|
+
this.addDOMOperation("layout", () =>
|
|
1724
|
+
this.executeLayoutChange(changes.layout.new, changes.layout.old)
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
if (changes.host.old !== changes.host.new) {
|
|
1729
|
+
this.addDOMOperation("host", () =>
|
|
1730
|
+
this.executeHostChange(changes.host.new, changes.host.old)
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
if (changes.blur.old !== changes.blur.new) {
|
|
1735
|
+
this.addDOMOperation("blur", () =>
|
|
1736
|
+
this.executeBlurChange(changes.blur.new, changes.blur.old)
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
this.initRoomDuration(metadata);
|
|
1742
|
+
},
|
|
1743
|
+
initRoomDuration(metadata) {
|
|
1744
|
+
if (this.isDurationCalc) {
|
|
1745
|
+
if (this.durationInterval) {
|
|
1746
|
+
clearInterval(this.durationInterval);
|
|
1747
|
+
this.durationInterval = null;
|
|
1748
|
+
}
|
|
1749
|
+
this.durationInterval = setInterval(() => {
|
|
1750
|
+
if (new Date(metadata.roomStartTime).getTime() > 0) {
|
|
1751
|
+
this.duration = Math.floor(
|
|
1752
|
+
(Date.now() - new Date(metadata.roomStartTime).getTime()) / 1000
|
|
1753
|
+
);
|
|
1754
|
+
this.isDurationCalc = false;
|
|
1755
|
+
} else {
|
|
1756
|
+
this.duration++;
|
|
1757
|
+
}
|
|
1758
|
+
}, 1000);
|
|
1759
|
+
}
|
|
1760
|
+
},
|
|
1761
|
+
startUnjoinParticipantPolling() {
|
|
1762
|
+
// 先清除可能存在的定时器
|
|
1763
|
+
if (this.unjoinParticipantInterval) {
|
|
1764
|
+
clearInterval(this.unjoinParticipantInterval);
|
|
1765
|
+
this.unjoinParticipantInterval = null;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
// 立即执行一次
|
|
1769
|
+
this.getUnjoinParticipant();
|
|
1770
|
+
this.queryAllInviteParticipant();
|
|
1771
|
+
// 设置5秒间隔的轮询
|
|
1772
|
+
this.unjoinParticipantInterval = setInterval(() => {
|
|
1773
|
+
this.getUnjoinParticipant();
|
|
1774
|
+
this.queryAllInviteParticipant();
|
|
1775
|
+
}, 6000);
|
|
1776
|
+
},
|
|
1777
|
+
initLiveClient() {
|
|
1778
|
+
this.liveClient = window["liveClient"];
|
|
1779
|
+
this.initLiveClientEvent();
|
|
1780
|
+
},
|
|
1781
|
+
sendInviteMessage(tempList = [], inviteWay = 0) {
|
|
1782
|
+
if (tempList.length <= 0) {
|
|
1783
|
+
if (this.tempInviteList.length > 0) {
|
|
1784
|
+
this.liveClient
|
|
1785
|
+
.inviteParticipant(
|
|
1786
|
+
this.meetingNum,
|
|
1787
|
+
this.tempInviteList.map((item) => {
|
|
1788
|
+
return {
|
|
1789
|
+
userName: item.label,
|
|
1790
|
+
identity: item?.loginCode ? item.loginCode : item.phone,
|
|
1791
|
+
phone: item.phone,
|
|
1792
|
+
};
|
|
1793
|
+
}),
|
|
1794
|
+
false,
|
|
1795
|
+
inviteWay
|
|
1796
|
+
)
|
|
1797
|
+
.then((res) => {
|
|
1798
|
+
if (res.code == 200) {
|
|
1799
|
+
this.showMessage.message("success", "成功发送邀请");
|
|
1800
|
+
this.tempInviteList.forEach((item) => {
|
|
1801
|
+
this.addToInviteList(item);
|
|
1802
|
+
});
|
|
1803
|
+
this.getUnjoinParticipant();
|
|
1804
|
+
this.queryAllInviteParticipant();
|
|
1805
|
+
} else {
|
|
1806
|
+
this.showMessage.message("error", res?.msg);
|
|
1807
|
+
}
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
} else {
|
|
1811
|
+
this.liveClient
|
|
1812
|
+
.inviteParticipant(
|
|
1813
|
+
this.meetingNum,
|
|
1814
|
+
tempList.map((item) => {
|
|
1815
|
+
return {
|
|
1816
|
+
userName: item.label,
|
|
1817
|
+
identity: item?.loginCode ? item.loginCode : item.phone,
|
|
1818
|
+
phone: item.phone,
|
|
1819
|
+
};
|
|
1820
|
+
}),
|
|
1821
|
+
false,
|
|
1822
|
+
inviteWay
|
|
1823
|
+
)
|
|
1824
|
+
.then((res) => {
|
|
1825
|
+
if (res.code == 200) {
|
|
1826
|
+
this.showMessage.message("success", "成功发送邀请");
|
|
1827
|
+
this.getUnjoinParticipant();
|
|
1828
|
+
this.queryAllInviteParticipant();
|
|
1829
|
+
} else {
|
|
1830
|
+
this.showMessage.message("error", res?.msg);
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
},
|
|
1835
|
+
queryAllInviteParticipant() {
|
|
1836
|
+
this.liveClient.getAllInviteParticipant(this.meetingNum).then((res) => {
|
|
1837
|
+
if (res.code == 200) {
|
|
1838
|
+
this.tempInvitedList = res?.data || [];
|
|
1839
|
+
} else {
|
|
1840
|
+
this.showMessage.message("error", res?.msg);
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
},
|
|
1844
|
+
async createRoom() {
|
|
1845
|
+
if (!this.liveClient) {
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
await this.liveClient.createRoom();
|
|
1849
|
+
},
|
|
1850
|
+
|
|
1851
|
+
async joinRoom() {
|
|
1852
|
+
let joinParam = {
|
|
1853
|
+
echoCancellation: true,
|
|
1854
|
+
noiseSuppression: true,
|
|
1855
|
+
roomNum: this.meetingNum,
|
|
1856
|
+
cameraStatus: this.isCameraEnabled,
|
|
1857
|
+
microphoneStatus: this.isMicrophoneEnabled,
|
|
1858
|
+
audioDeviceId: this.activeDevice.audioInputDevice,
|
|
1859
|
+
videoDeviceId: this.activeDevice.videoDevice,
|
|
1860
|
+
outputDeviceId: this.activeDevice.audioOutputDevice,
|
|
1861
|
+
};
|
|
1862
|
+
let tempActiveDevice = null;
|
|
1863
|
+
try {
|
|
1864
|
+
tempActiveDevice = await this.liveClient.joinRoom(joinParam);
|
|
1865
|
+
if (this.activeDevice) {
|
|
1866
|
+
this.activeDevice.audioInputDevice = tempActiveDevice?.audioInputDevice;
|
|
1867
|
+
this.activeDevice.audioOutputDevice = tempActiveDevice?.audioOutputDevice;
|
|
1868
|
+
this.activeDevice.videoDevice = tempActiveDevice?.videoInputDevice;
|
|
1869
|
+
console.log("activeDevice:", this.activeDevice);
|
|
1870
|
+
}
|
|
1871
|
+
} catch (e) {
|
|
1872
|
+
console.error("加入会议失败", e);
|
|
1873
|
+
}
|
|
1874
|
+
},
|
|
1875
|
+
switchActiveSpeakerToFirst(speaker) {
|
|
1876
|
+
if (speaker && speaker?.identity) {
|
|
1877
|
+
const speakerElm = document.getElementById(`participant-${speaker.identity}`);
|
|
1878
|
+
if (speakerElm) {
|
|
1879
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
1880
|
+
// 当前为点调模式
|
|
1881
|
+
if (
|
|
1882
|
+
!(
|
|
1883
|
+
speaker.identity === this.curHostIdentity ||
|
|
1884
|
+
speaker.identity === this.curBlurIdentity
|
|
1885
|
+
)
|
|
1886
|
+
) {
|
|
1887
|
+
// 仅当讲话人不为当前主持人和焦点用户时切换
|
|
1888
|
+
const pointTurnTop = document.querySelector("#room .point-turn-top");
|
|
1889
|
+
const pointTurnBottom = document.querySelector("#room .point-turn-bottom");
|
|
1890
|
+
const pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
1891
|
+
if (!pointTurnTop) {
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
if (!pointTurnBottom) {
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
if (!pointTurnOther) {
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
if (speakerElm.parentElement) {
|
|
1901
|
+
if (speakerElm.parentElement.className === "point-turn-top") {
|
|
1902
|
+
if (speakerElm.id === pointTurnTop.firstElementChild.id) {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
pointTurnTop.insertBefore(speakerElm, pointTurnTop.firstChild ?? null);
|
|
1906
|
+
} else if (speakerElm.parentElement.className === "point-turn-bottom") {
|
|
1907
|
+
if (document.querySelectorAll(".point-turn-top .participant").length >= 5) {
|
|
1908
|
+
pointTurnOther.insertBefore(
|
|
1909
|
+
pointTurnTop.lastElementChild,
|
|
1910
|
+
pointTurnOther.firstChild ?? null
|
|
1911
|
+
);
|
|
1912
|
+
pointTurnTop.insertBefore(speakerElm, pointTurnTop.firstChild ?? null);
|
|
1913
|
+
pointTurnBottom.insertBefore(
|
|
1914
|
+
pointTurnOther.firstChild,
|
|
1915
|
+
pointTurnBottom.firstChild ?? null
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
} else {
|
|
1919
|
+
if (document.querySelectorAll(".point-turn-top .participant").length >= 5) {
|
|
1920
|
+
if (document.querySelectorAll(".point-turn-bottom .participant").length >= 5) {
|
|
1921
|
+
pointTurnOther.insertBefore(
|
|
1922
|
+
pointTurnBottom.lastElementChild,
|
|
1923
|
+
pointTurnOther.firstChild ?? null
|
|
1924
|
+
);
|
|
1925
|
+
pointTurnBottom.insertBefore(
|
|
1926
|
+
pointTurnTop.lastElementChild,
|
|
1927
|
+
pointTurnBottom.firstChild ?? null
|
|
1928
|
+
);
|
|
1929
|
+
pointTurnTop.insertBefore(speakerElm, pointTurnTop.firstChild ?? null);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
} else {
|
|
1936
|
+
const container = document.getElementById("room");
|
|
1937
|
+
if (this.currentLayout === "grid") {
|
|
1938
|
+
// 当前为会议模式的常规布局
|
|
1939
|
+
if (speakerElm.id === container.firstElementChild.id) {
|
|
1940
|
+
return;
|
|
1941
|
+
} else {
|
|
1942
|
+
container.insertBefore(speakerElm, container.firstChild ?? null);
|
|
1943
|
+
}
|
|
1944
|
+
} else if (this.currentLayout === "rightSide") {
|
|
1945
|
+
// 当前为会议模式的焦点布局
|
|
1946
|
+
const layoutRightSideEle = document.querySelector("#room .layout-rightside");
|
|
1947
|
+
const layoutLeftSideEle = document.querySelector("#room .layout-leftside");
|
|
1948
|
+
if (!layoutLeftSideEle) {
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
if (!layoutRightSideEle) {
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
if (speaker.identity !== this.curBlurIdentity) {
|
|
1955
|
+
if (
|
|
1956
|
+
speakerElm.id === layoutLeftSideEle.firstElementChild.id ||
|
|
1957
|
+
speakerElm.id === layoutRightSideEle.firstElementChild.id
|
|
1958
|
+
) {
|
|
1959
|
+
return;
|
|
1960
|
+
} else {
|
|
1961
|
+
layoutRightSideEle.insertBefore(
|
|
1962
|
+
speakerElm,
|
|
1963
|
+
layoutRightSideEle.firstElementChild ?? null
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
} else {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
},
|
|
1974
|
+
constructParticipantDom(videoItem) {
|
|
1975
|
+
let videoDom = document.createElement("div");
|
|
1976
|
+
videoDom.id = `participant-${videoItem.identity}`;
|
|
1977
|
+
videoDom.className = videoItem.isLocal ? "participant local" : "participant";
|
|
1978
|
+
if (videoItem.isScreenShareEnabled) {
|
|
1979
|
+
videoDom.innerHTML = `
|
|
1980
|
+
<video id="video-${
|
|
1981
|
+
videoItem.identity
|
|
1982
|
+
}" class="p-video" autoplay webkit-playsinline playsinline x5-video-player-type="h5"></video>
|
|
1983
|
+
<div id="screenshare-${videoItem.identity}" class="screen-share-suspension">
|
|
1984
|
+
${videoItem.name} 正在共享屏幕
|
|
1985
|
+
</div>
|
|
1986
|
+
<div class="describe">
|
|
1987
|
+
<div id="microphone-${videoItem.identity}" class="microphone"></div>
|
|
1988
|
+
<div id="${videoItem.identity}" class="identity">${
|
|
1989
|
+
videoItem.isLocal ? videoItem.name + "(我)" : videoItem.name
|
|
1990
|
+
}</div>
|
|
1991
|
+
</div>
|
|
1992
|
+
`;
|
|
1993
|
+
} else if (videoItem.isCameraEnabled) {
|
|
1994
|
+
videoDom.innerHTML = `
|
|
1995
|
+
<video id="video-${
|
|
1996
|
+
videoItem.identity
|
|
1997
|
+
}" class="p-video" autoplay webkit-playsinline playsinline x5-video-player-type="h5"></video>
|
|
1998
|
+
<div id="signal-${videoItem.identity}" class="signal-icon signal-icon-good"></div>
|
|
1999
|
+
<div id="more-${videoItem.identity}" class="more-icon"></div>
|
|
2000
|
+
<div class="describe">
|
|
2001
|
+
<div id="microphone-${videoItem.identity}" class="microphone"></div>
|
|
2002
|
+
<div id="${videoItem.identity}" class="identity">${
|
|
2003
|
+
videoItem.isLocal ? videoItem.name + "(我)" : videoItem.name
|
|
2004
|
+
}</div>
|
|
2005
|
+
</div>
|
|
2006
|
+
`;
|
|
2007
|
+
} else {
|
|
2008
|
+
videoDom.innerHTML = `
|
|
2009
|
+
<video id="video-${
|
|
2010
|
+
videoItem.identity
|
|
2011
|
+
}" class="p-video" autoplay webkit-playsinline playsinline x5-video-player-type="h5"></video>
|
|
2012
|
+
<div id="board-${videoItem.identity}" class="board">
|
|
2013
|
+
<div class="board-icon"></div>
|
|
2014
|
+
</div>
|
|
2015
|
+
<div id="signal-${videoItem.identity}" class="signal-icon signal-icon-good"></div>
|
|
2016
|
+
<div id="more-${videoItem.identity}" class="more-icon"></div>
|
|
2017
|
+
<div class="describe">
|
|
2018
|
+
<div id="microphone-${videoItem.identity}" class="microphone"></div>
|
|
2019
|
+
<div id="${videoItem.identity}" class="identity">${
|
|
2020
|
+
videoItem.isLocal ? videoItem.name + "(我)" : videoItem.name
|
|
2021
|
+
}</div>
|
|
2022
|
+
</div>
|
|
2023
|
+
`;
|
|
2024
|
+
}
|
|
2025
|
+
return videoDom;
|
|
2026
|
+
},
|
|
2027
|
+
constructRoomLayout(container, videoDiv, videoItem) {
|
|
2028
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
2029
|
+
// 点调模式下用户第一次入会
|
|
2030
|
+
let pointTurnTop = document.querySelector("#room .point-turn-top");
|
|
2031
|
+
let pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
2032
|
+
let pointTurnBottom = document.querySelector("#room .point-turn-bottom");
|
|
2033
|
+
let pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
2034
|
+
if (!pointTurnTop) {
|
|
2035
|
+
pointTurnTop = document.createElement("div");
|
|
2036
|
+
pointTurnTop.className = "point-turn-top";
|
|
2037
|
+
container.insertBefore(pointTurnTop, container.firstChild ?? null);
|
|
2038
|
+
}
|
|
2039
|
+
if (!pointTurnCenter) {
|
|
2040
|
+
pointTurnCenter = document.createElement("div");
|
|
2041
|
+
pointTurnCenter.className = "point-turn-center";
|
|
2042
|
+
container.appendChild(pointTurnCenter);
|
|
2043
|
+
}
|
|
2044
|
+
if (!pointTurnBottom) {
|
|
2045
|
+
pointTurnBottom = document.createElement("div");
|
|
2046
|
+
pointTurnBottom.className = "point-turn-bottom";
|
|
2047
|
+
container.appendChild(pointTurnBottom);
|
|
2048
|
+
}
|
|
2049
|
+
if (!pointTurnOther) {
|
|
2050
|
+
pointTurnOther = document.createElement("div");
|
|
2051
|
+
pointTurnOther.className = "point-turn-other";
|
|
2052
|
+
container.appendChild(pointTurnOther);
|
|
2053
|
+
}
|
|
2054
|
+
if (this.curHostIdentity === videoItem.identity) {
|
|
2055
|
+
// 作为主持人入会
|
|
2056
|
+
if (pointTurnCenter.hasChildNodes()) {
|
|
2057
|
+
let placeHolderElms = document.querySelectorAll(".point-turn-center .participant");
|
|
2058
|
+
if (placeHolderElms.length > 0) {
|
|
2059
|
+
if (placeHolderElms.length === 1) {
|
|
2060
|
+
pointTurnCenter.insertBefore(videoDiv, pointTurnCenter.firstChild ?? null);
|
|
2061
|
+
} else {
|
|
2062
|
+
if (!placeHolderElms[0].id) {
|
|
2063
|
+
pointTurnCenter.removeChild(placeHolderElms[0]);
|
|
2064
|
+
pointTurnCenter.insertBefore(videoDiv, pointTurnCenter.firstChild ?? null);
|
|
2065
|
+
} else {
|
|
2066
|
+
if (document.querySelectorAll(".point-turn-top .participant").length >= 5) {
|
|
2067
|
+
if (document.querySelectorAll(".point-turn-bottom .participant").length >= 5) {
|
|
2068
|
+
pointTurnOther.appendChild(placeHolderElms[0]);
|
|
2069
|
+
} else {
|
|
2070
|
+
pointTurnBottom.appendChild(placeHolderElms[0]);
|
|
2071
|
+
}
|
|
2072
|
+
} else {
|
|
2073
|
+
pointTurnTop.appendChild(placeHolderElms[0]);
|
|
2074
|
+
}
|
|
2075
|
+
pointTurnCenter.insertBefore(videoDiv, pointTurnCenter.firstChild ?? null);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
} else {
|
|
2079
|
+
pointTurnCenter.insertBefore(videoDiv, pointTurnCenter.firstChild ?? null);
|
|
2080
|
+
}
|
|
2081
|
+
} else {
|
|
2082
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
2083
|
+
}
|
|
2084
|
+
} else if (this.curBlurIdentity === videoItem.identity) {
|
|
2085
|
+
// 作为焦点视频用户入会
|
|
2086
|
+
if (pointTurnCenter.hasChildNodes()) {
|
|
2087
|
+
let placeHolderElms = document.querySelectorAll(".point-turn-center .participant");
|
|
2088
|
+
if (placeHolderElms.length > 0) {
|
|
2089
|
+
if (placeHolderElms.length === 1) {
|
|
2090
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
2091
|
+
} else {
|
|
2092
|
+
if (!placeHolderElms[1].id) {
|
|
2093
|
+
pointTurnCenter.removeChild(placeHolderElms[1]);
|
|
2094
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
2095
|
+
} else {
|
|
2096
|
+
if (document.querySelectorAll(".point-turn-top .participant").length >= 5) {
|
|
2097
|
+
if (document.querySelectorAll(".point-turn-bottom .participant").length >= 5) {
|
|
2098
|
+
pointTurnOther.appendChild(placeHolderElms[1]);
|
|
2099
|
+
} else {
|
|
2100
|
+
pointTurnBottom.appendChild(placeHolderElms[1]);
|
|
2101
|
+
}
|
|
2102
|
+
} else {
|
|
2103
|
+
pointTurnTop.appendChild(placeHolderElms[1]);
|
|
2104
|
+
}
|
|
2105
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
} else {
|
|
2109
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
2110
|
+
}
|
|
2111
|
+
} else {
|
|
2112
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
2113
|
+
}
|
|
2114
|
+
} else {
|
|
2115
|
+
// 作为普通与会者入会
|
|
2116
|
+
if (document.querySelectorAll(".point-turn-top .participant").length >= 5) {
|
|
2117
|
+
if (document.querySelectorAll(".point-turn-bottom .participant").length >= 5) {
|
|
2118
|
+
pointTurnOther.appendChild(videoDiv);
|
|
2119
|
+
} else {
|
|
2120
|
+
pointTurnBottom.appendChild(videoDiv);
|
|
2121
|
+
}
|
|
2122
|
+
} else {
|
|
2123
|
+
pointTurnTop.appendChild(videoDiv);
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
} else {
|
|
2127
|
+
if (this.currentLayout === "grid") {
|
|
2128
|
+
// 宫格布局
|
|
2129
|
+
if (videoItem.isLocal) {
|
|
2130
|
+
// 本地与会者插入到最前面
|
|
2131
|
+
container.insertBefore(videoDiv, container.firstChild ?? null);
|
|
2132
|
+
} else {
|
|
2133
|
+
container.appendChild(videoDiv);
|
|
2134
|
+
}
|
|
2135
|
+
} else if (this.currentLayout === "rightSide") {
|
|
2136
|
+
// 右侧边栏布局
|
|
2137
|
+
// 1、初始化左右侧外层元素
|
|
2138
|
+
let layoutRightSideEle = document.querySelector("#room .layout-rightside");
|
|
2139
|
+
let layoutLeftSideEle = document.querySelector("#room .layout-leftside");
|
|
2140
|
+
if (!layoutLeftSideEle) {
|
|
2141
|
+
layoutLeftSideEle = document.createElement("div");
|
|
2142
|
+
layoutLeftSideEle.className = "layout-leftside";
|
|
2143
|
+
container.insertBefore(layoutLeftSideEle, container.firstChild ?? null);
|
|
2144
|
+
}
|
|
2145
|
+
if (!layoutRightSideEle) {
|
|
2146
|
+
layoutRightSideEle = document.createElement("div");
|
|
2147
|
+
layoutRightSideEle.className = "layout-rightside";
|
|
2148
|
+
container.appendChild(layoutRightSideEle);
|
|
2149
|
+
}
|
|
2150
|
+
// 2、添加元素到左右侧外层元素
|
|
2151
|
+
if (this.curBlurIdentity && this.curBlurIdentity === videoItem.identity) {
|
|
2152
|
+
if (layoutLeftSideEle.hasChildNodes()) {
|
|
2153
|
+
layoutRightSideEle.insertBefore(
|
|
2154
|
+
layoutLeftSideEle.firstChild,
|
|
2155
|
+
layoutRightSideEle.firstChild ?? null
|
|
2156
|
+
);
|
|
2157
|
+
layoutLeftSideEle.appendChild(videoDiv);
|
|
2158
|
+
}
|
|
2159
|
+
} else {
|
|
2160
|
+
if (videoItem.isLocal) {
|
|
2161
|
+
if (layoutLeftSideEle.hasChildNodes()) {
|
|
2162
|
+
layoutRightSideEle.insertBefore(
|
|
2163
|
+
layoutLeftSideEle.firstChild,
|
|
2164
|
+
layoutRightSideEle.firstChild ?? null
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
layoutLeftSideEle.appendChild(videoDiv);
|
|
2168
|
+
} else {
|
|
2169
|
+
layoutRightSideEle.appendChild(videoDiv);
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
return container;
|
|
2175
|
+
},
|
|
2176
|
+
renderVideoItem(videoItem) {
|
|
2177
|
+
console.log(`与会者渲染 ${videoItem.identity}`, videoItem);
|
|
2178
|
+
// 是否本人
|
|
2179
|
+
if (videoItem.isLocal) {
|
|
2180
|
+
this.localIdentity = videoItem.identity;
|
|
2181
|
+
this.localName = videoItem.name;
|
|
2182
|
+
this.localMetadata = videoItem.metadata;
|
|
2183
|
+
this.isCameraEnabled = videoItem.isCameraEnabled;
|
|
2184
|
+
this.isMicrophoneEnabled = videoItem.isMicrophoneEnabled;
|
|
2185
|
+
this.isScreenShareEnabled = videoItem.isScreenShareEnabled;
|
|
2186
|
+
}
|
|
2187
|
+
let container = document.getElementById("room");
|
|
2188
|
+
if (!container) {
|
|
2189
|
+
this.showMessage.message("error", "会议容器不存在");
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
let videoDiv = document.getElementById(`participant-${videoItem.identity}`);
|
|
2193
|
+
// 当与会者第一次入会
|
|
2194
|
+
if (!videoDiv && !videoItem.remove) {
|
|
2195
|
+
// 构建与会者dom元素
|
|
2196
|
+
videoDiv = this.constructParticipantDom(videoItem);
|
|
2197
|
+
// 构建会议室布局
|
|
2198
|
+
container = this.constructRoomLayout(container, videoDiv, videoItem);
|
|
2199
|
+
// 添加到participant数组
|
|
2200
|
+
this.addToParticipantList(videoItem);
|
|
2201
|
+
}
|
|
2202
|
+
// 与会者状态变更
|
|
2203
|
+
// 视频元素
|
|
2204
|
+
let videoElm = document.getElementById(`video-${videoItem.identity}`);
|
|
2205
|
+
// 摄像头关闭后展板元素
|
|
2206
|
+
let boardElm = document.getElementById(`board-${videoItem.identity}`);
|
|
2207
|
+
// 底部麦克风图标元素
|
|
2208
|
+
let microElm = document.getElementById(`microphone-${videoItem.identity}`);
|
|
2209
|
+
// 与会者网络状态图标元素
|
|
2210
|
+
let signalElm = document.getElementById(`signal-${videoItem.identity}`);
|
|
2211
|
+
// 与会者更多操作按钮元素
|
|
2212
|
+
let moreElm = document.getElementById(`more-${videoItem.identity}`);
|
|
2213
|
+
// 声明麦克风按钮点击事件回调
|
|
2214
|
+
const unableMicrophone = () => {
|
|
2215
|
+
this.liveClient.changeParticipantMicrophoneStatus(videoItem.identity, true);
|
|
2216
|
+
};
|
|
2217
|
+
const enableMicrophone = () => {
|
|
2218
|
+
this.liveClient.changeParticipantMicrophoneStatus(videoItem.identity, false);
|
|
2219
|
+
};
|
|
2220
|
+
// 声明与会者元素鼠标事件回调
|
|
2221
|
+
const signalAndMoreShow = () => {
|
|
2222
|
+
// 每次执行时重新获取最新的元素
|
|
2223
|
+
const currentSignalElm = document.getElementById(`signal-${videoItem.identity}`);
|
|
2224
|
+
const currentMoreElm = document.getElementById(`more-${videoItem.identity}`);
|
|
2225
|
+
currentSignalElm && (currentSignalElm.style.visibility = "visible");
|
|
2226
|
+
currentMoreElm && (currentMoreElm.style.visibility = "visible");
|
|
2227
|
+
};
|
|
2228
|
+
const signalAndMoreHide = () => {
|
|
2229
|
+
// 每次执行时重新获取最新的元素
|
|
2230
|
+
const currentSignalElm = document.getElementById(`signal-${videoItem.identity}`);
|
|
2231
|
+
const currentMoreElm = document.getElementById(`more-${videoItem.identity}`);
|
|
2232
|
+
currentSignalElm && (currentSignalElm.style.visibility = "hidden");
|
|
2233
|
+
currentMoreElm && (currentMoreElm.style.visibility = "hidden");
|
|
2234
|
+
};
|
|
2235
|
+
// 声明操作按钮元素点击事件回调
|
|
2236
|
+
const moreElmClick = (event) => {
|
|
2237
|
+
// 动态计算选项数量以获得精确的弹窗高度
|
|
2238
|
+
const optionCount = this.getOptionCount(videoItem.identity);
|
|
2239
|
+
|
|
2240
|
+
// 使用工具函数计算弹窗位置(使用真实的MoreOptionDialog尺寸)
|
|
2241
|
+
this.optionDialogOffset = this.calculateDialogPosition(event, {
|
|
2242
|
+
dialogWidth: 130,
|
|
2243
|
+
dialogHeight: 350, // 备用高度
|
|
2244
|
+
optionCount: optionCount, // 动态计算精确高度
|
|
2245
|
+
preferredPosition: "bottom-left",
|
|
2246
|
+
});
|
|
2247
|
+
|
|
2248
|
+
if (this.optionIdentity === videoItem.identity && this.moreDialogShow) {
|
|
2249
|
+
this.moreDialogShow = false;
|
|
2250
|
+
} else {
|
|
2251
|
+
this.moreDialogShow = false;
|
|
2252
|
+
this.optionIdentity = videoItem.identity;
|
|
2253
|
+
this.moreDialogShow = true;
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
// 为麦克风元素绑定事件
|
|
2257
|
+
if (microElm && videoItem.isMicrophoneEnabled) {
|
|
2258
|
+
microElm.className = "microphone microphone-active";
|
|
2259
|
+
microElm.onclick = unableMicrophone;
|
|
2260
|
+
} else if (microElm && !videoItem.isMicrophoneEnabled) {
|
|
2261
|
+
microElm.className = "microphone microphone-inactive";
|
|
2262
|
+
microElm.onclick = enableMicrophone;
|
|
2263
|
+
}
|
|
2264
|
+
// 为与会者元素绑定事件
|
|
2265
|
+
if (videoDiv) {
|
|
2266
|
+
videoDiv.onmouseenter = signalAndMoreShow;
|
|
2267
|
+
videoDiv.onmouseleave = signalAndMoreHide;
|
|
2268
|
+
}
|
|
2269
|
+
// 根据视频宽高比设定元素样式
|
|
2270
|
+
// if(videoItem?.dimensions) {
|
|
2271
|
+
// const { width, height } = videoItem.dimensions;
|
|
2272
|
+
// const ratio = width / height;
|
|
2273
|
+
// if(ratio > (1 / 1) && videoItem?.source == "camera") {
|
|
2274
|
+
// videoElm && (videoElm.style.objectFit = "cover");
|
|
2275
|
+
// } else {
|
|
2276
|
+
// videoElm && (videoElm.style.objectFit = "contain");
|
|
2277
|
+
// }
|
|
2278
|
+
// }
|
|
2279
|
+
// 为操作按钮元素绑定事件
|
|
2280
|
+
if (moreElm) {
|
|
2281
|
+
moreElm.onclick = moreElmClick;
|
|
2282
|
+
}
|
|
2283
|
+
if (signalElm) {
|
|
2284
|
+
if (videoItem.connectionQuality === "excellent" || videoItem.connectionQuality === "good") {
|
|
2285
|
+
signalElm.className = "signal-icon signal-icon-good";
|
|
2286
|
+
} else {
|
|
2287
|
+
signalElm.className = "signal-icon signal-icon-poor";
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
let screenShareElm = document.getElementById(`screenshare-${videoItem.identity}`);
|
|
2291
|
+
// 与会者dom元素内部样式重新渲染(摄像头或屏幕共享切换后)
|
|
2292
|
+
if (videoItem.isScreenShareEnabled) {
|
|
2293
|
+
if (!screenShareElm) {
|
|
2294
|
+
screenShareElm = document.createElement("div");
|
|
2295
|
+
screenShareElm.id = `screenshare-${videoItem.identity}`;
|
|
2296
|
+
screenShareElm.className = "screen-share-suspension";
|
|
2297
|
+
screenShareElm.innerHTML = `${videoItem.name} 正在共享屏幕`;
|
|
2298
|
+
videoDiv.appendChild(screenShareElm);
|
|
2299
|
+
}
|
|
2300
|
+
if (boardElm) {
|
|
2301
|
+
// 之前为关闭摄像头状态,此时需要移除board元素
|
|
2302
|
+
videoDiv.removeChild(boardElm);
|
|
2303
|
+
boardElm = null;
|
|
2304
|
+
}
|
|
2305
|
+
// 去除信号图标元素和操作按钮元素
|
|
2306
|
+
if (signalElm) {
|
|
2307
|
+
videoDiv.removeChild(signalElm);
|
|
2308
|
+
signalElm = null;
|
|
2309
|
+
}
|
|
2310
|
+
if (moreElm) {
|
|
2311
|
+
videoDiv.removeChild(moreElm);
|
|
2312
|
+
moreElm = null;
|
|
2313
|
+
}
|
|
2314
|
+
} else if (videoItem.isCameraEnabled) {
|
|
2315
|
+
if (screenShareElm) {
|
|
2316
|
+
// 之前为屏幕共享状态,此时需要移除screenshare元素
|
|
2317
|
+
videoDiv.removeChild(screenShareElm);
|
|
2318
|
+
screenShareElm = null;
|
|
2319
|
+
}
|
|
2320
|
+
if (boardElm) {
|
|
2321
|
+
// 之前为关闭摄像头状态,此时需要移除board元素
|
|
2322
|
+
videoDiv.removeChild(boardElm);
|
|
2323
|
+
boardElm = null;
|
|
2324
|
+
}
|
|
2325
|
+
if (!signalElm) {
|
|
2326
|
+
// 构建信号图标元素
|
|
2327
|
+
signalElm = document.createElement("div");
|
|
2328
|
+
signalElm.id = `signal-${videoItem.identity}`;
|
|
2329
|
+
if (
|
|
2330
|
+
videoItem.connectionQuality === "excellent" ||
|
|
2331
|
+
videoItem.connectionQuality === "good"
|
|
2332
|
+
) {
|
|
2333
|
+
signalElm.className = "signal-icon signal-icon-good";
|
|
2334
|
+
} else {
|
|
2335
|
+
signalElm.className = "signal-icon signal-icon-poor";
|
|
2336
|
+
}
|
|
2337
|
+
videoDiv.appendChild(signalElm);
|
|
2338
|
+
}
|
|
2339
|
+
// if (!moreElm) {
|
|
2340
|
+
// // 构建更多操作按钮元素
|
|
2341
|
+
// moreElm = document.createElement("div");
|
|
2342
|
+
// moreElm.id = `more-${videoItem.identity}`;
|
|
2343
|
+
// moreElm.className = "more-icon";
|
|
2344
|
+
// videoDiv.appendChild(moreElm);
|
|
2345
|
+
// }
|
|
2346
|
+
if (this.localMetadata?.isOwner || this.localMetadata?.isCoHost) {
|
|
2347
|
+
if (!moreElm) {
|
|
2348
|
+
moreElm = document.createElement("div");
|
|
2349
|
+
moreElm.id = `more-${videoItem.identity}`;
|
|
2350
|
+
moreElm.className = "more-icon";
|
|
2351
|
+
videoDiv && videoDiv.appendChild(moreElm);
|
|
2352
|
+
moreElm.onclick = moreElmClick;
|
|
2353
|
+
}
|
|
2354
|
+
} else {
|
|
2355
|
+
if (moreElm) {
|
|
2356
|
+
videoDiv.removeChild(moreElm);
|
|
2357
|
+
moreElm = null;
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
} else {
|
|
2361
|
+
if (!boardElm) {
|
|
2362
|
+
// 之前为屏幕共享状态或摄像头开启状态
|
|
2363
|
+
boardElm = document.createElement("div");
|
|
2364
|
+
boardElm.id = `board-${videoItem.identity}`;
|
|
2365
|
+
boardElm.className = "board";
|
|
2366
|
+
let avatarElm = document.createElement("div");
|
|
2367
|
+
avatarElm.className = "board-icon";
|
|
2368
|
+
boardElm.appendChild(avatarElm);
|
|
2369
|
+
if (videoDiv) {
|
|
2370
|
+
videoDiv?.appendChild(boardElm);
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
if (screenShareElm) {
|
|
2374
|
+
videoDiv.removeChild(screenShareElm);
|
|
2375
|
+
screenShareElm = null;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
// 当与会者断开会议链接即remove为true
|
|
2379
|
+
if (videoItem.remove) {
|
|
2380
|
+
console.log("参会者离开会议:", videoItem.identity);
|
|
2381
|
+
|
|
2382
|
+
if (videoElm) {
|
|
2383
|
+
videoElm.srcObject = null;
|
|
2384
|
+
videoElm.src = "";
|
|
2385
|
+
videoElm.remove();
|
|
2386
|
+
}
|
|
2387
|
+
if (videoDiv) {
|
|
2388
|
+
videoDiv.remove();
|
|
2389
|
+
}
|
|
2390
|
+
this.removeFromParticipantList(videoItem.identity);
|
|
2391
|
+
this.filterParticipantList();
|
|
2392
|
+
|
|
2393
|
+
// 处理焦点用户离开的情况
|
|
2394
|
+
if (this.curBlurIdentity === videoItem.identity) {
|
|
2395
|
+
console.log("焦点用户离开会议:", videoItem.identity);
|
|
2396
|
+
// 调用房间焦点用户移除方法
|
|
2397
|
+
this.removeBlurParticipant();
|
|
2398
|
+
if (this.currentRoomMode === "normal") {
|
|
2399
|
+
// normal模式下,这里do nothing
|
|
2400
|
+
void 0;
|
|
2401
|
+
} else if (this.currentRoomMode === "pointTurn") {
|
|
2402
|
+
// pointTurn模式下,在原本焦点用户所在的中间右侧位置添加占位符
|
|
2403
|
+
// const pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
2404
|
+
// if (pointTurnCenter && pointTurnCenter.children.length < 2) {
|
|
2405
|
+
// const placeHolderElm = document.createElement("div");
|
|
2406
|
+
// placeHolderElm.className = "participant";
|
|
2407
|
+
// placeHolderElm.innerHTML = placeholderTemplate;
|
|
2408
|
+
// pointTurnCenter.appendChild(placeHolderElm);
|
|
2409
|
+
// }
|
|
2410
|
+
// do nothing
|
|
2411
|
+
void 0;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
// 处理主持人离开的情况
|
|
2416
|
+
if (videoItem.identity === this.curHostIdentity) {
|
|
2417
|
+
this.curHostIdentity = null;
|
|
2418
|
+
// 主持人离开后具体的逻辑处理放到watch中处理
|
|
2419
|
+
}
|
|
2420
|
+
// pointTurn模式下,主持人离开处理
|
|
2421
|
+
// if (currentRoomMode.value === "pointTurn" && videoItem.identity === curHostIdentity.value) {
|
|
2422
|
+
// 当前离开与会者为会议主持人
|
|
2423
|
+
// const pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
2424
|
+
// if (pointTurnCenter) {
|
|
2425
|
+
// // 会议中没有其他主持人,显示默认占位元素
|
|
2426
|
+
// const placeHolderElm = document.createElement("div");
|
|
2427
|
+
// placeHolderElm.className = "participant";
|
|
2428
|
+
// placeHolderElm.innerHTML = placeholderTemplate;
|
|
2429
|
+
// pointTurnCenter.insertBefore(placeHolderElm, pointTurnCenter.firstChild ?? null);
|
|
2430
|
+
// }
|
|
2431
|
+
// }
|
|
2432
|
+
// pointTurn模式下的其他用户离开处理
|
|
2433
|
+
if (
|
|
2434
|
+
this.currentRoomMode === "pointTurn" &&
|
|
2435
|
+
videoItem.identity !== this.curHostIdentity &&
|
|
2436
|
+
videoItem.identity !== this.curBlurIdentity
|
|
2437
|
+
) {
|
|
2438
|
+
// 当前离开为一般与会者,递补填充空位
|
|
2439
|
+
const pointTurnTop = document.querySelector("#room .point-turn-top");
|
|
2440
|
+
const pointTurnBottom = document.querySelector("#room .point-turn-bottom");
|
|
2441
|
+
const pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
2442
|
+
|
|
2443
|
+
if (pointTurnTop && pointTurnBottom && pointTurnOther) {
|
|
2444
|
+
const pointTurnTopChildNum = pointTurnTop.children.length;
|
|
2445
|
+
const pointTurnBottomChildNum = pointTurnBottom.children.length;
|
|
2446
|
+
const pointTurnOtherChildNum = pointTurnOther.children.length;
|
|
2447
|
+
|
|
2448
|
+
if (pointTurnTopChildNum < 5) {
|
|
2449
|
+
if (pointTurnBottomChildNum > 0) {
|
|
2450
|
+
pointTurnTop.appendChild(pointTurnBottom.firstElementChild);
|
|
2451
|
+
} else if (pointTurnOtherChildNum > 0) {
|
|
2452
|
+
pointTurnTop.appendChild(pointTurnOther.firstChild);
|
|
2453
|
+
}
|
|
2454
|
+
} else if (pointTurnBottomChildNum < 5) {
|
|
2455
|
+
if (pointTurnOtherChildNum > 0) {
|
|
2456
|
+
pointTurnBottom.appendChild(pointTurnOther.firstChild);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// 音视频轨道绑定到video元素
|
|
2463
|
+
if (videoItem?.videoTrack) {
|
|
2464
|
+
videoItem.videoTrack.detach();
|
|
2465
|
+
}
|
|
2466
|
+
if (videoItem?.audioTrack) {
|
|
2467
|
+
videoItem.audioTrack.detach();
|
|
2468
|
+
}
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2471
|
+
// 与会者屏幕共享布局切换
|
|
2472
|
+
if (videoItem.isScreenShareEnabled) {
|
|
2473
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
2474
|
+
// 点调模式下不做处理
|
|
2475
|
+
void 0;
|
|
2476
|
+
} else {
|
|
2477
|
+
// 当前模式为常规会议模式,且仅当前用户为主持人时才进行布局操作
|
|
2478
|
+
if (this.judgeParticipantIsHost(this.localIdentity)) {
|
|
2479
|
+
if (this.currentLayout === "grid") {
|
|
2480
|
+
if (this.judgeOtherScreenShare(videoItem.identity) !== -1) {
|
|
2481
|
+
// 会议中存在其他人正在共享屏幕, 什么都不做
|
|
2482
|
+
void 0;
|
|
2483
|
+
} else {
|
|
2484
|
+
// 会议中并无其他人共享屏幕,仅在未设置屏幕共享状态时保存布局状态
|
|
2485
|
+
if (!this.isScreenShareChange) {
|
|
2486
|
+
this.isScreenShareChange = true;
|
|
2487
|
+
// 保存屏幕共享前的布局和焦点用户状态
|
|
2488
|
+
this.screenShareChangeOldLayout = this.currentLayout;
|
|
2489
|
+
this.screenShareChangeOldBlur = this.curBlurIdentity;
|
|
2490
|
+
console.log("开始屏幕共享,保存状态:", {
|
|
2491
|
+
oldLayout: this.screenShareChangeOldLayout,
|
|
2492
|
+
oldBlur: this.screenShareChangeOldBlur,
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
// 设置屏幕共享用户为焦点
|
|
2496
|
+
this.setMemberBlur(videoItem.identity);
|
|
2497
|
+
}
|
|
2498
|
+
} else if (this.currentLayout === "rightSide") {
|
|
2499
|
+
if (this.judgeOtherScreenShare(videoItem.identity) !== -1) {
|
|
2500
|
+
// 会议中存在其他人正在共享屏幕, 什么都不做
|
|
2501
|
+
void 0;
|
|
2502
|
+
} else {
|
|
2503
|
+
// 会议中并无其他人共享屏幕,仅在未设置屏幕共享状态时保存布局状态
|
|
2504
|
+
if (!this.isScreenShareChange) {
|
|
2505
|
+
this.isScreenShareChange = true;
|
|
2506
|
+
// 保存屏幕共享前的布局和焦点用户状态
|
|
2507
|
+
this.screenShareChangeOldLayout = this.currentLayout;
|
|
2508
|
+
this.screenShareChangeOldBlur = this.curBlurIdentity;
|
|
2509
|
+
console.log("开始屏幕共享,保存状态:", {
|
|
2510
|
+
oldLayout: this.screenShareChangeOldLayout,
|
|
2511
|
+
oldBlur: this.screenShareChangeOldBlur,
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2514
|
+
// 设置屏幕共享用户为焦点
|
|
2515
|
+
this.setMemberBlur(videoItem.identity);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
// 网络质量监测
|
|
2522
|
+
this.detectUserCommuniQuality();
|
|
2523
|
+
// 音视频轨道绑定到video元素
|
|
2524
|
+
if (videoItem?.videoTrack) {
|
|
2525
|
+
videoItem.videoTrack.attach(videoElm);
|
|
2526
|
+
}
|
|
2527
|
+
if (!videoItem.isLocal) {
|
|
2528
|
+
if (videoItem?.audioTrack) {
|
|
2529
|
+
videoItem.audioTrack.attach(videoElm);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
// 更新与会者名称
|
|
2533
|
+
let nameDom = document.getElementById(videoItem.identity);
|
|
2534
|
+
if (nameDom) {
|
|
2535
|
+
nameDom.innerHTML = videoItem.isLocal ? videoItem.name + "(我)" : videoItem.name;
|
|
2536
|
+
}
|
|
2537
|
+
// 更新与会者数组
|
|
2538
|
+
this.addToParticipantList(videoItem);
|
|
2539
|
+
this.filterParticipantList();
|
|
2540
|
+
// 与会者结束共享,切换回到之前布局(仅主持人执行)
|
|
2541
|
+
if (this.currentRoomMode === "normal" && this.judgeParticipantIsHost(this.localIdentity)) {
|
|
2542
|
+
console.log(
|
|
2543
|
+
"normal模式下,主持人结束屏幕共享,准备还原布局",
|
|
2544
|
+
this.hasScreenShare,
|
|
2545
|
+
this.isScreenShareChange
|
|
2546
|
+
);
|
|
2547
|
+
if (this.hasScreenShare === -1 && this.isScreenShareChange) {
|
|
2548
|
+
console.log("结束屏幕共享,准备还原布局:", {
|
|
2549
|
+
oldLayout: this.screenShareChangeOldLayout,
|
|
2550
|
+
oldBlur: this.screenShareChangeOldBlur,
|
|
2551
|
+
currentLayout: this.currentLayout,
|
|
2552
|
+
});
|
|
2553
|
+
|
|
2554
|
+
if (
|
|
2555
|
+
this.screenShareChangeOldLayout &&
|
|
2556
|
+
this.screenShareChangeOldLayout !== this.currentLayout
|
|
2557
|
+
) {
|
|
2558
|
+
// 恢复到共享前的布局
|
|
2559
|
+
console.log("还原布局:", this.screenShareChangeOldLayout);
|
|
2560
|
+
this.setLayout(this.screenShareChangeOldLayout);
|
|
2561
|
+
}
|
|
2562
|
+
// 恢复到共享前的焦点用户
|
|
2563
|
+
if (this.screenShareChangeOldBlur) {
|
|
2564
|
+
console.log("还原焦点用户:", this.screenShareChangeOldBlur);
|
|
2565
|
+
this.setMemberBlur(this.screenShareChangeOldBlur);
|
|
2566
|
+
} else {
|
|
2567
|
+
// 如果共享前没有焦点用户,则移除焦点
|
|
2568
|
+
console.log("移除焦点用户");
|
|
2569
|
+
this.removeBlurParticipant();
|
|
2570
|
+
}
|
|
2571
|
+
// 重置状态
|
|
2572
|
+
console.log("重置屏幕共享状态");
|
|
2573
|
+
this.isScreenShareChange = false;
|
|
2574
|
+
this.screenShareChangeOldBlur = null;
|
|
2575
|
+
this.screenShareChangeOldLayout = null;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
},
|
|
2579
|
+
async leaveRoom() {
|
|
2580
|
+
if (!this.liveClient) {
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
await this.liveClient.leaveRoom();
|
|
2584
|
+
},
|
|
2585
|
+
|
|
2586
|
+
exitPage() {
|
|
2587
|
+
this.isLeaveRoom = true;
|
|
2588
|
+
this.$emit("multiMeetingClose");
|
|
2589
|
+
},
|
|
2590
|
+
|
|
2591
|
+
setPageFooterVisible(time = 3, e) {
|
|
2592
|
+
this.footerVisibleDuration = time;
|
|
2593
|
+
this.pageFooterVisible = true;
|
|
2594
|
+
if (this.footerInterval) {
|
|
2595
|
+
clearInterval(this.footerInterval);
|
|
2596
|
+
}
|
|
2597
|
+
this.footerInterval = setInterval(() => {
|
|
2598
|
+
if (this.footerVisibleDuration > 0) {
|
|
2599
|
+
this.footerVisibleDuration--;
|
|
2600
|
+
}
|
|
2601
|
+
}, 1000);
|
|
2602
|
+
},
|
|
2603
|
+
|
|
2604
|
+
// 暂时空实现,避免模板报错
|
|
2605
|
+
minumDialog() {
|
|
2606
|
+
if (!this.isInMeeting) {
|
|
2607
|
+
this.showMessage.message("error", "请先进入会议");
|
|
2608
|
+
return;
|
|
2609
|
+
}
|
|
2610
|
+
if (this.localIdentity) {
|
|
2611
|
+
const index = this.participants.findIndex((item) => item.identity === this.localIdentity);
|
|
2612
|
+
if (index < 0) {
|
|
2613
|
+
this.showMessage.message("error", "获取本地与会者视频流失败");
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
this.$emit("multiMeetingMinum", {
|
|
2617
|
+
identity: this.localIdentity,
|
|
2618
|
+
name: this.participants[index].name,
|
|
2619
|
+
videoTrack: this.participants[index]?.videoTrack,
|
|
2620
|
+
});
|
|
2621
|
+
this.$refs.rootElm.style.visibility = "hidden";
|
|
2622
|
+
} else {
|
|
2623
|
+
this.showMessage.message("error", "获取本地与会者视频流失败");
|
|
2624
|
+
}
|
|
2625
|
+
},
|
|
2626
|
+
resetDialog() {
|
|
2627
|
+
this.$refs.rootElm.style.visibility = "visible";
|
|
2628
|
+
},
|
|
2629
|
+
async executeLayoutChange(newLayout, oldLayout) {
|
|
2630
|
+
console.log("执行布局变化", newLayout, "->", oldLayout);
|
|
2631
|
+
console.log("新布局", newLayout);
|
|
2632
|
+
console.log("旧布局", oldLayout);
|
|
2633
|
+
this.$nextTick(() => {
|
|
2634
|
+
// 仅当会议模式下才可切换布局
|
|
2635
|
+
if (this.currentRoomMode == "normal") {
|
|
2636
|
+
let container = document.getElementById("room");
|
|
2637
|
+
if (!container) {
|
|
2638
|
+
this.showMessage.warning("会议容器不存在");
|
|
2639
|
+
return;
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
if (oldLayout === "grid" && newLayout === "rightSide") {
|
|
2643
|
+
// 从宫格布局切换到焦点布局
|
|
2644
|
+
|
|
2645
|
+
// 先清理可能残留的布局容器
|
|
2646
|
+
const existingRightSide = document.querySelector("#room .layout-rightside");
|
|
2647
|
+
const existingLeftSide = document.querySelector("#room .layout-leftside");
|
|
2648
|
+
if (existingRightSide) {
|
|
2649
|
+
console.warn("发现残留的右侧布局容器,正在清理");
|
|
2650
|
+
existingRightSide.remove();
|
|
2651
|
+
}
|
|
2652
|
+
if (existingLeftSide) {
|
|
2653
|
+
console.warn("发现残留的左侧布局容器,正在清理");
|
|
2654
|
+
existingLeftSide.remove();
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
let layoutRightSideEle = document.querySelector("#room .layout-rightside");
|
|
2658
|
+
let layoutLeftSideEle = document.querySelector("#room .layout-leftside");
|
|
2659
|
+
|
|
2660
|
+
// 创建布局容器
|
|
2661
|
+
if (!layoutLeftSideEle) {
|
|
2662
|
+
layoutLeftSideEle = document.createElement("div");
|
|
2663
|
+
layoutLeftSideEle.className = "layout-leftside";
|
|
2664
|
+
}
|
|
2665
|
+
if (!layoutRightSideEle) {
|
|
2666
|
+
layoutRightSideEle = document.createElement("div");
|
|
2667
|
+
layoutRightSideEle.className = "layout-rightside";
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
let focusVideoItem = null;
|
|
2671
|
+
let blurDom = null;
|
|
2672
|
+
|
|
2673
|
+
// 确定焦点用户 - 优先级:当前焦点用户 > 主持人 > 本地用户
|
|
2674
|
+
if (this.curBlurIdentity) {
|
|
2675
|
+
focusVideoItem = this.getUserItemByIdentity(this.curBlurIdentity);
|
|
2676
|
+
if (focusVideoItem) {
|
|
2677
|
+
blurDom = document.getElementById(`participant-${focusVideoItem.identity}`);
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
// 如果没有有效的焦点用户,设置主持人为焦点用户
|
|
2682
|
+
if (!blurDom && this.curHostIdentity) {
|
|
2683
|
+
focusVideoItem = this.getUserItemByIdentity(this.curHostIdentity);
|
|
2684
|
+
if (focusVideoItem) {
|
|
2685
|
+
blurDom = document.getElementById(`participant-${focusVideoItem.identity}`);
|
|
2686
|
+
// 注意:这里不需要调用autoSetHostAsBlur(),因为curBlurIdentity的变化会触发watch监听器
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
// 如果主持人也不存在,则设置本地用户为焦点
|
|
2691
|
+
if (!blurDom) {
|
|
2692
|
+
focusVideoItem = this.getLocalParticipant();
|
|
2693
|
+
if (focusVideoItem) {
|
|
2694
|
+
blurDom = document.getElementById(`participant-${focusVideoItem.identity}`);
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
if (blurDom) {
|
|
2699
|
+
// 清空左侧容器并移动其子元素到右侧
|
|
2700
|
+
if (layoutLeftSideEle.hasChildNodes()) {
|
|
2701
|
+
let child;
|
|
2702
|
+
while ((child = layoutLeftSideEle.firstChild)) {
|
|
2703
|
+
if (child) {
|
|
2704
|
+
layoutRightSideEle.appendChild(child);
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
// 将焦点用户放入左侧容器
|
|
2710
|
+
layoutLeftSideEle.appendChild(blurDom);
|
|
2711
|
+
|
|
2712
|
+
// 将容器中的其他元素移动到右侧容器
|
|
2713
|
+
if (container.hasChildNodes()) {
|
|
2714
|
+
let child;
|
|
2715
|
+
let maxIterations = container.children.length + 10; // 设置最大循环次数防止死循环
|
|
2716
|
+
let iterations = 0;
|
|
2717
|
+
|
|
2718
|
+
while ((child = container.firstElementChild) && iterations < maxIterations) {
|
|
2719
|
+
iterations++;
|
|
2720
|
+
|
|
2721
|
+
if (child && child !== layoutLeftSideEle && child !== layoutRightSideEle) {
|
|
2722
|
+
const prevFirstChild = container.firstElementChild;
|
|
2723
|
+
layoutRightSideEle.appendChild(child);
|
|
2724
|
+
|
|
2725
|
+
// 检查元素是否成功移动,如果没有移动则强制退出循环
|
|
2726
|
+
if (container.firstElementChild === prevFirstChild) {
|
|
2727
|
+
console.warn("元素移动失败,强制退出循环", child);
|
|
2728
|
+
break;
|
|
2729
|
+
}
|
|
2730
|
+
} else {
|
|
2731
|
+
// 如果遇到布局容器本身,说明有问题,强制移除
|
|
2732
|
+
console.warn("发现布局容器残留,强制移除", child);
|
|
2733
|
+
container.removeChild(child);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
if (iterations >= maxIterations) {
|
|
2738
|
+
console.error("while循环达到最大次数,可能存在死循环");
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
// 将布局容器添加到主容器
|
|
2743
|
+
container.insertBefore(layoutLeftSideEle, container.firstChild ?? null);
|
|
2744
|
+
container.appendChild(layoutRightSideEle);
|
|
2745
|
+
} else {
|
|
2746
|
+
this.showMessage.warning("无法找到有效的焦点用户");
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
if (oldLayout === "grid" && newLayout === "ring") {
|
|
2751
|
+
// 从宫格布局切换到环状布局
|
|
2752
|
+
// TODO: 实现环状布局逻辑
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
if (oldLayout === "grid" && newLayout === "downLSide") {
|
|
2756
|
+
// 从宫格布局切换到下L侧边栏布局
|
|
2757
|
+
// TODO: 实现下L侧边栏布局逻辑
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
if (oldLayout === "rightSide" && newLayout === "grid") {
|
|
2761
|
+
// 从焦点布局切换到宫格布局
|
|
2762
|
+
let layoutRightSideEle = document.querySelector("#room .layout-rightside");
|
|
2763
|
+
let layoutLeftSideEle = document.querySelector("#room .layout-leftside");
|
|
2764
|
+
console.log("layoutSideEle", layoutRightSideEle, layoutLeftSideEle);
|
|
2765
|
+
|
|
2766
|
+
const fragment = document.createDocumentFragment();
|
|
2767
|
+
|
|
2768
|
+
// 处理右侧容器的元素
|
|
2769
|
+
if (layoutRightSideEle && layoutRightSideEle.hasChildNodes()) {
|
|
2770
|
+
let child;
|
|
2771
|
+
while ((child = layoutRightSideEle.firstChild)) {
|
|
2772
|
+
fragment.appendChild(child);
|
|
2773
|
+
}
|
|
2774
|
+
// 检查元素是否仍然是 container 的子元素,然后移除
|
|
2775
|
+
if (container.contains(layoutRightSideEle)) {
|
|
2776
|
+
container.removeChild(layoutRightSideEle);
|
|
2777
|
+
} else {
|
|
2778
|
+
console.warn("layoutRightSideEle is no longer a child of container");
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
// 处理左侧容器的元素(焦点用户)
|
|
2783
|
+
if (layoutLeftSideEle && layoutLeftSideEle.hasChildNodes()) {
|
|
2784
|
+
let child;
|
|
2785
|
+
while ((child = layoutLeftSideEle.firstChild)) {
|
|
2786
|
+
console.log("layoutLeftSideEleChild", layoutLeftSideEle, child);
|
|
2787
|
+
container.insertBefore(child, container.firstChild ?? null);
|
|
2788
|
+
}
|
|
2789
|
+
// 检查元素是否仍然是 container 的子元素,然后移除
|
|
2790
|
+
if (container.contains(layoutLeftSideEle)) {
|
|
2791
|
+
container.removeChild(layoutLeftSideEle);
|
|
2792
|
+
} else {
|
|
2793
|
+
console.warn("layoutLeftSideEle is no longer a child of container");
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
// 添加其他元素
|
|
2798
|
+
container.appendChild(fragment);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
});
|
|
2802
|
+
},
|
|
2803
|
+
async switchFullScreen() {
|
|
2804
|
+
if (
|
|
2805
|
+
document.fullscreenElement ||
|
|
2806
|
+
document.msFullscreenElement ||
|
|
2807
|
+
document.mozFullScreenElement ||
|
|
2808
|
+
document.webkitFullscreenElement
|
|
2809
|
+
? true
|
|
2810
|
+
: false
|
|
2811
|
+
) {
|
|
2812
|
+
if (document.exitFullscreen) {
|
|
2813
|
+
document.exitFullscreen();
|
|
2814
|
+
} else if (document.mozCancelFullScreen) {
|
|
2815
|
+
document.mozCancelFullScreen();
|
|
2816
|
+
} else if (document.webkitCancelFullScreen) {
|
|
2817
|
+
document.webkitCancelFullScreen();
|
|
2818
|
+
} else if (document.msExitFullscreen) {
|
|
2819
|
+
document.msExitFullscreen();
|
|
2820
|
+
}
|
|
2821
|
+
} else {
|
|
2822
|
+
this.$nextTick(() => {
|
|
2823
|
+
if (this.$refs.rootElm.requestFullscreen) {
|
|
2824
|
+
this.$refs.rootElm.requestFullscreen();
|
|
2825
|
+
}
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
},
|
|
2829
|
+
openLeaveOptionDialog(event) {
|
|
2830
|
+
console.log('aaaaaaaaa');
|
|
2831
|
+
|
|
2832
|
+
if (this.judgeParticipantIsHost(this.localIdentity)) {
|
|
2833
|
+
if (this.leaveOptionShow) {
|
|
2834
|
+
this.leaveOptionShow = false;
|
|
2835
|
+
} else {
|
|
2836
|
+
this.leaveOptionOffset = {
|
|
2837
|
+
left: event.target.offsetLeft - 40 + "px",
|
|
2838
|
+
bottom: event.target.offsetTop + 60 + "px",
|
|
2839
|
+
};
|
|
2840
|
+
this.leaveOptionShow = true;
|
|
2841
|
+
}
|
|
2842
|
+
} else {
|
|
2843
|
+
this.isDeleteRoom = false;
|
|
2844
|
+
this.leaveRoom();
|
|
2845
|
+
}
|
|
2846
|
+
},
|
|
2847
|
+
openLeaveOptionDialog2(event) {
|
|
2848
|
+
if (this.judgeParticipantIsHost(this.localIdentity)) {
|
|
2849
|
+
if (this.leaveOptionShow) {
|
|
2850
|
+
this.leaveOptionShow = false;
|
|
2851
|
+
} else {
|
|
2852
|
+
this.leaveOptionOffset = {
|
|
2853
|
+
left: event.target.offsetLeft - 150 + "px",
|
|
2854
|
+
bottom: event.target.offsetTop + 60 + "px",
|
|
2855
|
+
};
|
|
2856
|
+
this.leaveOptionShow = true;
|
|
2857
|
+
}
|
|
2858
|
+
} else {
|
|
2859
|
+
this.isDeleteRoom = false;
|
|
2860
|
+
this.leaveRoom();
|
|
2861
|
+
}
|
|
2862
|
+
},
|
|
2863
|
+
closeMoreDialog() {
|
|
2864
|
+
this.moreDialogShow = false;
|
|
2865
|
+
},
|
|
2866
|
+
getUnjoinParticipant() {
|
|
2867
|
+
this.liveClient.getUnjoinParticipant(this.meetingNum).then((res) => {
|
|
2868
|
+
if (res.code == 200) {
|
|
2869
|
+
this.unJoinedList = res?.data || [];
|
|
2870
|
+
} else {
|
|
2871
|
+
this.showMessage.message("error", res?.msg);
|
|
2872
|
+
}
|
|
2873
|
+
});
|
|
2874
|
+
},
|
|
2875
|
+
moreOptionClick(e) {
|
|
2876
|
+
// 动态计算选项数量以获得精确的弹窗高度
|
|
2877
|
+
const optionCount = this.getOptionCount(e.identity);
|
|
2878
|
+
|
|
2879
|
+
// 使用工具函数计算弹窗位置(使用真实的MoreOptionDialog尺寸)
|
|
2880
|
+
this.optionDialogOffset = this.calculateDialogPosition(e, {
|
|
2881
|
+
dialogWidth: 130,
|
|
2882
|
+
dialogHeight: 350, // 备用高度
|
|
2883
|
+
optionCount: optionCount, // 动态计算精确高度
|
|
2884
|
+
preferredPosition: "bottom-left",
|
|
2885
|
+
});
|
|
2886
|
+
|
|
2887
|
+
if (this.optionIdentity === e.identity && this.moreDialogShow) {
|
|
2888
|
+
this.moreDialogShow = false;
|
|
2889
|
+
} else {
|
|
2890
|
+
this.moreDialogShow = false;
|
|
2891
|
+
this.optionIdentity = e.identity;
|
|
2892
|
+
this.moreDialogShow = true;
|
|
2893
|
+
}
|
|
2894
|
+
},
|
|
2895
|
+
removeUnjoinItem(item) {
|
|
2896
|
+
this.liveClient.deleteUnjoinParticipant(this.meetingNum, item.identity).then((res) => {
|
|
2897
|
+
if (res.code == 200) {
|
|
2898
|
+
this.showMessage.message("success", "成功删除未入会人员");
|
|
2899
|
+
this.getUnjoinParticipant();
|
|
2900
|
+
this.removeFromInviteList(item.identity);
|
|
2901
|
+
} else {
|
|
2902
|
+
this.showMessage.message("error", res?.msg);
|
|
2903
|
+
}
|
|
2904
|
+
});
|
|
2905
|
+
},
|
|
2906
|
+
switchParticipantCameraStatus(e) {
|
|
2907
|
+
if (this.liveClient) {
|
|
2908
|
+
this.liveClient.changeParticipantCameraStatus(e.identity, e.isMute);
|
|
2909
|
+
}
|
|
2910
|
+
},
|
|
2911
|
+
switchParticipantMicrophoneStatus(e) {
|
|
2912
|
+
if (this.liveClient) {
|
|
2913
|
+
this.liveClient.changeParticipantMicrophoneStatus(e.identity, e.isMute);
|
|
2914
|
+
}
|
|
2915
|
+
},
|
|
2916
|
+
async closeAllCamera() {
|
|
2917
|
+
if (this.liveClient) {
|
|
2918
|
+
await this.liveClient.muteAllParticipants("2");
|
|
2919
|
+
}
|
|
2920
|
+
},
|
|
2921
|
+
async closeAllMicrophone() {
|
|
2922
|
+
if (this.liveClient) {
|
|
2923
|
+
await this.liveClient.muteAllParticipants("1");
|
|
2924
|
+
}
|
|
2925
|
+
},
|
|
2926
|
+
appendMemberInvite(e) {
|
|
2927
|
+
this.sendAppendInviteMessage([e]);
|
|
2928
|
+
},
|
|
2929
|
+
async changeActiveDevice(kind, deviceId) {
|
|
2930
|
+
if (!deviceId) {
|
|
2931
|
+
this.showMessage.message("error", "设备id不存在");
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2934
|
+
if (kind == "audioinput") {
|
|
2935
|
+
await this.liveClient.changeAudioDevice(deviceId);
|
|
2936
|
+
} else if (kind == "audiooutput") {
|
|
2937
|
+
await this.liveClient.changeOutputDevice(deviceId);
|
|
2938
|
+
} else if (kind == "videoinput") {
|
|
2939
|
+
await this.liveClient.changeVideoDevice(deviceId);
|
|
2940
|
+
} else {
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2943
|
+
},
|
|
2944
|
+
filterJoinedList(e) {
|
|
2945
|
+
this.filterJoinVal = e;
|
|
2946
|
+
this.filterParticipantList();
|
|
2947
|
+
},
|
|
2948
|
+
messageSend(e) {
|
|
2949
|
+
const msg = {
|
|
2950
|
+
content: e.content,
|
|
2951
|
+
contentType: e.contentType,
|
|
2952
|
+
};
|
|
2953
|
+
this.liveClient.sendChatMessage(msg);
|
|
2954
|
+
},
|
|
2955
|
+
appointHost(e) {
|
|
2956
|
+
this.liveClient
|
|
2957
|
+
.setParticipantToHost(e.ownerId)
|
|
2958
|
+
.then((res) => {
|
|
2959
|
+
if (res.code == 200) {
|
|
2960
|
+
this.showMessage.message("success", `成功指派 ${e.ownerName} 为主持人`);
|
|
2961
|
+
this.isDeleteRoom = false;
|
|
2962
|
+
this.leaveRoom();
|
|
2963
|
+
} else {
|
|
2964
|
+
this.showMessage.message("error", res?.msg);
|
|
2965
|
+
}
|
|
2966
|
+
})
|
|
2967
|
+
.catch((err) => {
|
|
2968
|
+
this.showMessage.message("error", `指派 ${e.ownerName} 失败`);
|
|
2969
|
+
});
|
|
2970
|
+
},
|
|
2971
|
+
detectUserCommuniQuality() {
|
|
2972
|
+
if (!this.liveClient || !this.liveClient?.room) {
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
let quality = null;
|
|
2976
|
+
switch (this.liveClient.room?.localParticipant.connectionQuality) {
|
|
2977
|
+
case "excellent":
|
|
2978
|
+
case "good":
|
|
2979
|
+
quality = "良好";
|
|
2980
|
+
break;
|
|
2981
|
+
case "poor":
|
|
2982
|
+
quality = "较差";
|
|
2983
|
+
break;
|
|
2984
|
+
case "lost":
|
|
2985
|
+
case "unknown":
|
|
2986
|
+
quality = "极差";
|
|
2987
|
+
break;
|
|
2988
|
+
}
|
|
2989
|
+
this.meetingQuality = quality ?? "暂无";
|
|
2990
|
+
},
|
|
2991
|
+
|
|
2992
|
+
async setMemberBlur(e) {
|
|
2993
|
+
if (
|
|
2994
|
+
!this.judgeParticipantIsHost(this.localIdentity) &&
|
|
2995
|
+
!this.judgeParticipantIsCoHost(this.localIdentity)
|
|
2996
|
+
) {
|
|
2997
|
+
// showMessage.message("warning", "仅主持人和联席主持人可以设置焦点");
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
// 检查是否为手动设置焦点(非屏幕共享用户)且当前正在屏幕共享状态
|
|
3002
|
+
// if (isScreenShareChange.value) {
|
|
3003
|
+
// const targetUser = getUserItemByIdentity(e);
|
|
3004
|
+
// if (!targetUser || !targetUser.isScreenShareEnabled) {
|
|
3005
|
+
// // 当前正在屏幕共享状态,但设置的焦点用户不是屏幕共享用户,说明是手动设置,取消自动还原
|
|
3006
|
+
// cancelScreenShareAutoRestore("手动设置焦点用户");
|
|
3007
|
+
// }
|
|
3008
|
+
// }
|
|
3009
|
+
|
|
3010
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
3011
|
+
// 当前为点调模式
|
|
3012
|
+
if (this.curHostIdentity && this.curHostIdentity === e) {
|
|
3013
|
+
this.showMessage.message("info", "当前与会者已被设置为主持人,暂无法设为焦点");
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
if (this.curBlurIdentity && this.curBlurIdentity === e) {
|
|
3017
|
+
this.showMessage.message("info", "当前与会者已被设置为焦点");
|
|
3018
|
+
return;
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
// 只设置焦点用户,布局切换逻辑交给 watch 处理
|
|
3022
|
+
await this.liveClient.setBlurParticipant(e);
|
|
3023
|
+
} else {
|
|
3024
|
+
// 会议模式
|
|
3025
|
+
if (this.curBlurIdentity && this.curBlurIdentity === e) {
|
|
3026
|
+
this.showMessage.message("info", "当前与会者已被设置为焦点");
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
// 只设置焦点用户,布局切换逻辑交给 watch 处理
|
|
3031
|
+
await this.liveClient.setBlurParticipant(e);
|
|
3032
|
+
}
|
|
3033
|
+
},
|
|
3034
|
+
removeCurBlur() {
|
|
3035
|
+
// 仅主持人和联席主持人可以取消设为焦点
|
|
3036
|
+
if (
|
|
3037
|
+
!this.judgeParticipantIsHost(this.localIdentity) &&
|
|
3038
|
+
!this.judgeParticipantIsCoHost(this.localIdentity)
|
|
3039
|
+
) {
|
|
3040
|
+
this.showMessage.message("warning", "仅主持人和联席主持人可以取消设为焦点");
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
if (!this.curBlurIdentity) {
|
|
3045
|
+
this.showMessage.warning("当前没有焦点用户");
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
// 如果当前正在屏幕共享状态且手动取消焦点,取消自动还原
|
|
3050
|
+
this.cancelScreenShareAutoRestore("手动取消焦点用户");
|
|
3051
|
+
|
|
3052
|
+
const videoDiv = document.getElementById(`participant-${this.curBlurIdentity}`);
|
|
3053
|
+
if (!videoDiv) {
|
|
3054
|
+
this.showMessage.warning("焦点用户视频元素不存在:", this.curBlurIdentity);
|
|
3055
|
+
this.removeBlurParticipant();
|
|
3056
|
+
return;
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
3060
|
+
const pointTurnTop = document.querySelector("#room .point-turn-top");
|
|
3061
|
+
const pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
3062
|
+
const pointTurnBottom = document.querySelector("#room .point-turn-bottom");
|
|
3063
|
+
const pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
3064
|
+
|
|
3065
|
+
if (!pointTurnTop || !pointTurnCenter || !pointTurnBottom || !pointTurnOther) {
|
|
3066
|
+
this.showMessage.warning("点调模式容器不存在");
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
// 将焦点用户移动到合适的位置
|
|
3071
|
+
if (pointTurnTop.children.length < 5) {
|
|
3072
|
+
pointTurnTop.appendChild(videoDiv);
|
|
3073
|
+
} else if (pointTurnBottom.children.length < 5) {
|
|
3074
|
+
pointTurnBottom.appendChild(videoDiv);
|
|
3075
|
+
} else {
|
|
3076
|
+
pointTurnOther.appendChild(videoDiv);
|
|
3077
|
+
}
|
|
3078
|
+
|
|
3079
|
+
// 在中心区域添加占位符
|
|
3080
|
+
const placeHolderElm = document.createElement("div");
|
|
3081
|
+
placeHolderElm.className = "participant";
|
|
3082
|
+
placeHolderElm.innerHTML = this.placeholderTemplate;
|
|
3083
|
+
pointTurnCenter.appendChild(placeHolderElm);
|
|
3084
|
+
} else if (this.currentLayout === "rightSide") {
|
|
3085
|
+
const layoutRightSideEle = document.querySelector("#room .layout-rightside");
|
|
3086
|
+
const layoutLeftSideEle = document.querySelector("#room .layout-leftside");
|
|
3087
|
+
|
|
3088
|
+
if (!layoutLeftSideEle || !layoutRightSideEle) {
|
|
3089
|
+
this.showMessage.warning("焦点布局容器不存在");
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
// 将左侧容器的元素(焦点用户)移动到右侧容器
|
|
3094
|
+
let child = null;
|
|
3095
|
+
while ((child = layoutLeftSideEle.firstChild)) {
|
|
3096
|
+
if (child) {
|
|
3097
|
+
layoutRightSideEle.appendChild(child);
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
// 清空焦点用户标识
|
|
3103
|
+
this.removeBlurParticipant();
|
|
3104
|
+
},
|
|
3105
|
+
async removeBlurParticipant() {
|
|
3106
|
+
if (
|
|
3107
|
+
this.judgeParticipantIsHost(this.localIdentity) ||
|
|
3108
|
+
this.judgeParticipantIsCoHost(this.localIdentity)
|
|
3109
|
+
) {
|
|
3110
|
+
try {
|
|
3111
|
+
await this.liveClient.removeBlurParticipant();
|
|
3112
|
+
} catch (err) {
|
|
3113
|
+
console.error("删除焦点失败", err);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
},
|
|
3117
|
+
async autoSetHostAsBlur() {
|
|
3118
|
+
if (!this.curBlurIdentity && this.curHostIdentity) {
|
|
3119
|
+
try {
|
|
3120
|
+
await this.liveClient.setBlurParticipant(this.curHostIdentity);
|
|
3121
|
+
console.log("自动设置主持人为焦点用户:", this.curHostIdentity);
|
|
3122
|
+
} catch (err) {
|
|
3123
|
+
console.error("自动设置主持人为焦点失败", err);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
},
|
|
3127
|
+
async openMicro(e) {
|
|
3128
|
+
if (this.liveClient) {
|
|
3129
|
+
await this.liveClient.changeParticipantMicrophoneStatus(e, false);
|
|
3130
|
+
}
|
|
3131
|
+
},
|
|
3132
|
+
async closeMicro(e) {
|
|
3133
|
+
if (this.liveClient) {
|
|
3134
|
+
await this.liveClient.changeParticipantMicrophoneStatus(e, true);
|
|
3135
|
+
}
|
|
3136
|
+
},
|
|
3137
|
+
async openCamera(e) {
|
|
3138
|
+
if (this.liveClient) {
|
|
3139
|
+
await this.liveClient.changeParticipantCameraStatus(e, true);
|
|
3140
|
+
}
|
|
3141
|
+
},
|
|
3142
|
+
async closeCamera(e) {
|
|
3143
|
+
if (this.liveClient) {
|
|
3144
|
+
await this.liveClient.changeParticipantCameraStatus(e, false);
|
|
3145
|
+
}
|
|
3146
|
+
},
|
|
3147
|
+
async setToHost(e) {
|
|
3148
|
+
if (this.judgeParticipantIsHost(e)) {
|
|
3149
|
+
return;
|
|
3150
|
+
} else {
|
|
3151
|
+
await this.liveClient.setParticipantToHost(e);
|
|
3152
|
+
}
|
|
3153
|
+
},
|
|
3154
|
+
async setToDeafness(e) {
|
|
3155
|
+
if (this.liveClient) {
|
|
3156
|
+
await this.liveClient.setParticipantDeafness(e);
|
|
3157
|
+
}
|
|
3158
|
+
},
|
|
3159
|
+
async cancelDeafness(e) {
|
|
3160
|
+
if (this.liveClient) {
|
|
3161
|
+
await this.liveClient.cancelParticipantDeafness(e);
|
|
3162
|
+
}
|
|
3163
|
+
},
|
|
3164
|
+
updateName(e) {
|
|
3165
|
+
mittBus.emit("updateName", {
|
|
3166
|
+
identity: e,
|
|
3167
|
+
});
|
|
3168
|
+
},
|
|
3169
|
+
async removeMember(e) {
|
|
3170
|
+
if (liveClient) {
|
|
3171
|
+
await liveClient.moveoutParticipant(e);
|
|
3172
|
+
}
|
|
3173
|
+
},
|
|
3174
|
+
async setToCoHost(e) {
|
|
3175
|
+
if (this.liveClient) {
|
|
3176
|
+
await this.liveClient.addCoHost(e);
|
|
3177
|
+
}
|
|
3178
|
+
},
|
|
3179
|
+
async removeFromCoHost(e) {
|
|
3180
|
+
if (this.liveClient) {
|
|
3181
|
+
await this.liveClient.removeCoHost(e);
|
|
3182
|
+
}
|
|
3183
|
+
},
|
|
3184
|
+
async appendInvitePeople(e) {
|
|
3185
|
+
console.log("追加邀请", e);
|
|
3186
|
+
if (e && e.length > 0) {
|
|
3187
|
+
e.forEach((item) => {
|
|
3188
|
+
this.addToInviteList(item);
|
|
3189
|
+
});
|
|
3190
|
+
}
|
|
3191
|
+
let tempList = [];
|
|
3192
|
+
let index = -1;
|
|
3193
|
+
this.inviteList.forEach((item) => {
|
|
3194
|
+
if (this.invitedNum > 0) {
|
|
3195
|
+
index = this.tempInvitedList.findIndex((ele) => {
|
|
3196
|
+
if (item?.loginCode) {
|
|
3197
|
+
return ele.identity === item.loginCode;
|
|
3198
|
+
} else {
|
|
3199
|
+
return ele.identity === item.phone;
|
|
3200
|
+
}
|
|
3201
|
+
});
|
|
3202
|
+
if (index < 0) {
|
|
3203
|
+
// 当前人员尚未被邀请
|
|
3204
|
+
tempList.push(item);
|
|
3205
|
+
}
|
|
3206
|
+
} else {
|
|
3207
|
+
tempList.push(item);
|
|
3208
|
+
}
|
|
3209
|
+
});
|
|
3210
|
+
|
|
3211
|
+
this.sendInviteMessage(tempList, this.defaultInviteWay == "mini" ? 3 : 0);
|
|
3212
|
+
},
|
|
3213
|
+
async appendInviteDevice(e) {
|
|
3214
|
+
console.log("通讯录邀请设备", e);
|
|
3215
|
+
if (e && e.length > 0) {
|
|
3216
|
+
e.forEach((item) => {
|
|
3217
|
+
this.pullMonitorDevice(item?.equipmentID || item?.monitorID, item.label);
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
},
|
|
3221
|
+
async updateNameConfirm(e) {
|
|
3222
|
+
if (this.liveClient) {
|
|
3223
|
+
await this.liveClient.updateParticipantName(e.identity, e.name);
|
|
3224
|
+
}
|
|
3225
|
+
},
|
|
3226
|
+
async changeLocalMicrophoneStatus() {
|
|
3227
|
+
await this.liveClient.changeMicrophoneStatus();
|
|
3228
|
+
},
|
|
3229
|
+
async changeLocalCameraStatus() {
|
|
3230
|
+
await this.liveClient.changeCameraStatus();
|
|
3231
|
+
},
|
|
3232
|
+
async chooseAudioInputDevice(e) {
|
|
3233
|
+
await this.changeActiveDevice("audioinput", e);
|
|
3234
|
+
this.audioSelectShow = false;
|
|
3235
|
+
},
|
|
3236
|
+
async chooseAudioOutputDevice(e) {
|
|
3237
|
+
await this.changeActiveDevice("audiooutput", e);
|
|
3238
|
+
this.audioSelectShow = false;
|
|
3239
|
+
},
|
|
3240
|
+
detectMicroAndOutput() {
|
|
3241
|
+
this.$refs.settingDialogRef.open({
|
|
3242
|
+
type: "1",
|
|
3243
|
+
});
|
|
3244
|
+
},
|
|
3245
|
+
openSettingDialog(type) {
|
|
3246
|
+
this.$refs.settingDialogRef.open({
|
|
3247
|
+
type,
|
|
3248
|
+
});
|
|
3249
|
+
},
|
|
3250
|
+
async chooseVideoDevice(e) {
|
|
3251
|
+
await this.changeActiveDevice("videoinput", e);
|
|
3252
|
+
this.videoSelectShow = false;
|
|
3253
|
+
},
|
|
3254
|
+
async chooseMeetingMode(e) {
|
|
3255
|
+
// 仅主持人和联席主持人可以切换会议模式
|
|
3256
|
+
if (
|
|
3257
|
+
!this.judgeParticipantIsHost(this.localIdentity) &&
|
|
3258
|
+
!this.judgeParticipantIsCoHost(this.localIdentity)
|
|
3259
|
+
) {
|
|
3260
|
+
this.showMessage.message("warning", "仅主持人和联席主持人可以切换会议模式");
|
|
3261
|
+
return;
|
|
3262
|
+
}
|
|
3263
|
+
await this.liveClient.setRoomMode(e);
|
|
3264
|
+
this.modeSelectShow = false;
|
|
3265
|
+
},
|
|
3266
|
+
async changeScreenShareStatus() {
|
|
3267
|
+
if (this.judgeOtherScreenShare(this.localIdentity) > -1) {
|
|
3268
|
+
this.showMessage.info("会议中存在其他人正在共享屏幕");
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
await this.liveClient.changeScreenShareStatus(this.screenShareCaptureOption);
|
|
3272
|
+
},
|
|
3273
|
+
screenShareOptionChange(e) {
|
|
3274
|
+
console.log("屏幕共享设置变更", e);
|
|
3275
|
+
},
|
|
3276
|
+
stopScreenShare() {
|
|
3277
|
+
this.changeScreenShareStatus(this.screenShareCaptureOption);
|
|
3278
|
+
},
|
|
3279
|
+
async startRecord() {
|
|
3280
|
+
await this.liveClient.startRecord();
|
|
3281
|
+
},
|
|
3282
|
+
async stopRecord() {
|
|
3283
|
+
await this.liveClient.stopRecord();
|
|
3284
|
+
},
|
|
3285
|
+
openCallBoard() {
|
|
3286
|
+
if (this.callBoardShow) {
|
|
3287
|
+
this.callBoardShow = false;
|
|
3288
|
+
} else {
|
|
3289
|
+
this.callBoardShow = true;
|
|
3290
|
+
// callBoardOffset.value = {
|
|
3291
|
+
// bottom: event.target.offsetTop + 50 + "px",
|
|
3292
|
+
// left: event.target.clientLeft - 122 + "px",
|
|
3293
|
+
// };
|
|
3294
|
+
}
|
|
3295
|
+
},
|
|
3296
|
+
chooseInviteWay(e) {
|
|
3297
|
+
if (e === "addressbook") {
|
|
3298
|
+
this.openAddressBook();
|
|
3299
|
+
} else if (e === "videoCall") {
|
|
3300
|
+
this.openCallBoard();
|
|
3301
|
+
}
|
|
3302
|
+
},
|
|
3303
|
+
// 打开通讯录
|
|
3304
|
+
openAddressBook() {
|
|
3305
|
+
mittBus.emit("getaddressBookUser", {
|
|
3306
|
+
isInner: true,
|
|
3307
|
+
});
|
|
3308
|
+
},
|
|
3309
|
+
appendInvite(e, inviteWay) {
|
|
3310
|
+
let tempList = [];
|
|
3311
|
+
let tempDeviceList = [];
|
|
3312
|
+
let index = -1;
|
|
3313
|
+
if (e && e.length > 0) {
|
|
3314
|
+
e.forEach((item) => {
|
|
3315
|
+
if (item.source == "人员") {
|
|
3316
|
+
if (this.invitedNum > 0) {
|
|
3317
|
+
index = this.tempInvitedList.findIndex((ele) => {
|
|
3318
|
+
if (item?.loginCode) {
|
|
3319
|
+
return ele.identity === item.loginCode;
|
|
3320
|
+
} else {
|
|
3321
|
+
return ele.identity === item.phone;
|
|
3322
|
+
}
|
|
3323
|
+
});
|
|
3324
|
+
if (index < 0) {
|
|
3325
|
+
// 当前人员尚未被邀请
|
|
3326
|
+
tempList.push(item);
|
|
3327
|
+
}
|
|
3328
|
+
} else {
|
|
3329
|
+
tempList.push(item);
|
|
3330
|
+
}
|
|
3331
|
+
this.addToInviteList(item);
|
|
3332
|
+
} else {
|
|
3333
|
+
tempDeviceList.push(item);
|
|
3334
|
+
}
|
|
3335
|
+
});
|
|
3336
|
+
console.log("本次追加邀请人员", tempList);
|
|
3337
|
+
console.log("本次追加邀请设备", tempDeviceList);
|
|
3338
|
+
this.sendInviteMessage(tempList, inviteWay == "mini" ? 3 : 0);
|
|
3339
|
+
if (tempDeviceList.length > 0) {
|
|
3340
|
+
tempDeviceList.forEach((item) => {
|
|
3341
|
+
if (item.source == "设备") {
|
|
3342
|
+
this.pullMonitorDevice(item.equipmentID, item.label);
|
|
3343
|
+
} else if (item.source == "监控") {
|
|
3344
|
+
this.pullMonitorDevice(item.monitorID, item.label);
|
|
3345
|
+
}
|
|
3346
|
+
});
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
},
|
|
3350
|
+
pullMonitorDevice(monitorID, monitorName) {
|
|
3351
|
+
this.liveClient.judgeUserInMeeting(this.meetingNum, monitorID).then((res) => {
|
|
3352
|
+
if (res && res?.code == 200) {
|
|
3353
|
+
if (res.data == 1) {
|
|
3354
|
+
this.showMessage.message("error", "该监控设备已进入会议");
|
|
3355
|
+
return;
|
|
3356
|
+
} else {
|
|
3357
|
+
this.liveClient.makeCall(monitorName, monitorID, 1);
|
|
3358
|
+
}
|
|
3359
|
+
} else {
|
|
3360
|
+
this.showMessage.message("error", "获取监控设备进会状态失败");
|
|
3361
|
+
}
|
|
3362
|
+
});
|
|
3363
|
+
},
|
|
3364
|
+
|
|
3365
|
+
async sendAppendInviteMessage(uninviteList = []) {
|
|
3366
|
+
// 政协项目新增
|
|
3367
|
+
if (this.isCustomizeMiniInvitations) {
|
|
3368
|
+
this.$emit("inviteMessageSend", {
|
|
3369
|
+
inviteUserList: uninviteList,
|
|
3370
|
+
meetingName: this.meetingName,
|
|
3371
|
+
meetingNum: this.meetingNum,
|
|
3372
|
+
fromUser: this.userData.username,
|
|
3373
|
+
});
|
|
3374
|
+
} else {
|
|
3375
|
+
if (uninviteList.length > 0) {
|
|
3376
|
+
this.liveClient
|
|
3377
|
+
.inviteParticipant(
|
|
3378
|
+
this.meetingNum,
|
|
3379
|
+
uninviteList.map((item) => {
|
|
3380
|
+
return {
|
|
3381
|
+
userName: item.userName,
|
|
3382
|
+
identity: item.identity,
|
|
3383
|
+
phone: item.phone,
|
|
3384
|
+
};
|
|
3385
|
+
})
|
|
3386
|
+
)
|
|
3387
|
+
.then((res) => {
|
|
3388
|
+
if (res.code == 200) {
|
|
3389
|
+
this.showMessage.message("success", "成功发送邀请");
|
|
3390
|
+
this.getUnjoinParticipant();
|
|
3391
|
+
} else {
|
|
3392
|
+
this.showMessage.message("error", res?.msg);
|
|
3393
|
+
}
|
|
3394
|
+
});
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
},
|
|
3398
|
+
phoneCall(num) {
|
|
3399
|
+
this.liveClient.makeCall(num, num);
|
|
3400
|
+
},
|
|
3401
|
+
async miniCall(num) {
|
|
3402
|
+
if (this.isCustomizeMiniInvitations) {
|
|
3403
|
+
this.$emit("miniInviteSend", {
|
|
3404
|
+
inviteMobile: num,
|
|
3405
|
+
fromUser: this.userData.username,
|
|
3406
|
+
meetingName: this.meetingName,
|
|
3407
|
+
meetingNum: this.meetingNum,
|
|
3408
|
+
});
|
|
3409
|
+
} else {
|
|
3410
|
+
this.showMessage.message("info", "当前功能暂未实现");
|
|
3411
|
+
}
|
|
3412
|
+
},
|
|
3413
|
+
sleep(waitTimeInMs) {
|
|
3414
|
+
return new Promise((resolve) => setTimeout(resolve, waitTimeInMs));
|
|
3415
|
+
},
|
|
3416
|
+
|
|
3417
|
+
handleClick() {
|
|
3418
|
+
console.log("点击", this.footerInterval);
|
|
3419
|
+
if (this.footerInterval) {
|
|
3420
|
+
clearInterval(this.footerInterval);
|
|
3421
|
+
}
|
|
3422
|
+
this.pageFooterVisible = true;
|
|
3423
|
+
},
|
|
3424
|
+
clickAway(event) {
|
|
3425
|
+
console.log("点击事件", event.target.className);
|
|
3426
|
+
if (!event.target.className.includes("meeting-name")) {
|
|
3427
|
+
this.themeDialogShow = false;
|
|
3428
|
+
}
|
|
3429
|
+
if (
|
|
3430
|
+
!(
|
|
3431
|
+
event.target.className.includes("exit-btn") ||
|
|
3432
|
+
event.target.className.includes("close-meeting-icon")
|
|
3433
|
+
)
|
|
3434
|
+
) {
|
|
3435
|
+
this.leaveOptionShow = false;
|
|
3436
|
+
}
|
|
3437
|
+
if (
|
|
3438
|
+
!(
|
|
3439
|
+
event.target.className.includes("member-item") ||
|
|
3440
|
+
event.target.className.includes("appoint-dialog") ||
|
|
3441
|
+
event.target.className.includes("member-list") ||
|
|
3442
|
+
event.target.className.includes("control-list-item")
|
|
3443
|
+
)
|
|
3444
|
+
) {
|
|
3445
|
+
this.appointDialogShow = false;
|
|
3446
|
+
}
|
|
3447
|
+
if (
|
|
3448
|
+
!(
|
|
3449
|
+
event.target.className.includes("icon-btn-option") ||
|
|
3450
|
+
event.target.className.includes("more-icon")
|
|
3451
|
+
)
|
|
3452
|
+
) {
|
|
3453
|
+
this.moreDialogShow = false;
|
|
3454
|
+
}
|
|
3455
|
+
},
|
|
3456
|
+
verifyLeavePage(event) {
|
|
3457
|
+
console.log("beforeunload", event);
|
|
3458
|
+
if (!this.isLeaveRoom) {
|
|
3459
|
+
// 不是用户手动点击结束导致离开页面
|
|
3460
|
+
event.preventDefault();
|
|
3461
|
+
}
|
|
3462
|
+
},
|
|
3463
|
+
async executeHostChange(newVal, oldVal) {
|
|
3464
|
+
console.log("执行主持人变化", newVal, "->", oldVal);
|
|
3465
|
+
console.log("修改前主持人", oldVal);
|
|
3466
|
+
console.log("修改后主持人", newVal);
|
|
3467
|
+
if (!newVal) return;
|
|
3468
|
+
|
|
3469
|
+
// 如果新主持人与当前焦点用户相同,清空焦点用户
|
|
3470
|
+
if (this.curBlurIdentity === newVal) {
|
|
3471
|
+
console.log("新主持人与焦点用户相同,清空焦点用户");
|
|
3472
|
+
this.removeBlurParticipant();
|
|
3473
|
+
}
|
|
3474
|
+
const hostVideoElm = document.getElementById(`participant-${newVal}`);
|
|
3475
|
+
|
|
3476
|
+
if (!hostVideoElm) {
|
|
3477
|
+
console.error("主持人视频元素不存在:", newVal);
|
|
3478
|
+
return;
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
3482
|
+
const pointTurnTop = document.querySelector("#room .point-turn-top");
|
|
3483
|
+
const pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
3484
|
+
const pointTurnBottom = document.querySelector("#room .point-turn-bottom");
|
|
3485
|
+
const pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
3486
|
+
|
|
3487
|
+
if (!pointTurnTop || !pointTurnCenter || !pointTurnBottom || !pointTurnOther) {
|
|
3488
|
+
this.showMessage.warning("点调模式容器元素不存在");
|
|
3489
|
+
return;
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
let pointTurnTopChildNum = pointTurnTop.children.length;
|
|
3493
|
+
let pointTurnBottomChildNum = pointTurnBottom.children.length;
|
|
3494
|
+
let pointTurnOtherChildNum = pointTurnOther.children.length;
|
|
3495
|
+
|
|
3496
|
+
if (pointTurnCenter.hasChildNodes()) {
|
|
3497
|
+
let childNodes = pointTurnCenter.children;
|
|
3498
|
+
console.log("childNodes", childNodes);
|
|
3499
|
+
|
|
3500
|
+
// 处理中心区域的第一个元素(如果存在且有ID)
|
|
3501
|
+
if (childNodes[0] && childNodes[0].id) {
|
|
3502
|
+
// 将原有元素移动到合适位置
|
|
3503
|
+
if (pointTurnTopChildNum >= 5) {
|
|
3504
|
+
if (pointTurnBottomChildNum >= 5) {
|
|
3505
|
+
pointTurnOther.appendChild(childNodes[0]);
|
|
3506
|
+
} else {
|
|
3507
|
+
pointTurnBottom.appendChild(childNodes[0]);
|
|
3508
|
+
}
|
|
3509
|
+
} else {
|
|
3510
|
+
pointTurnTop.appendChild(childNodes[0]);
|
|
3511
|
+
}
|
|
3512
|
+
} else if (childNodes[0] && !childNodes[0].id) {
|
|
3513
|
+
// 移除占位符
|
|
3514
|
+
pointTurnCenter.removeChild(childNodes[0]);
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
// 将新主持人移动到中心区域第一个位置
|
|
3518
|
+
pointTurnCenter.insertBefore(hostVideoElm, pointTurnCenter.firstElementChild ?? null);
|
|
3519
|
+
|
|
3520
|
+
// 如果新主持人不是焦点用户,需要添加占位符
|
|
3521
|
+
// if (newVal !== curBlurIdentity.value) {
|
|
3522
|
+
// let placeHolderElm = document.createElement("div");
|
|
3523
|
+
// placeHolderElm.className = "participant";
|
|
3524
|
+
// placeHolderElm.innerHTML = placeholderTemplate;
|
|
3525
|
+
// pointTurnCenter.appendChild(placeHolderElm);
|
|
3526
|
+
// }
|
|
3527
|
+
} else {
|
|
3528
|
+
// 中心区域为空,直接添加主持人
|
|
3529
|
+
pointTurnCenter.insertBefore(hostVideoElm, pointTurnCenter.firstElementChild ?? null);
|
|
3530
|
+
|
|
3531
|
+
// 如果新主持人不是焦点用户,需要添加占位符
|
|
3532
|
+
// if (newVal !== curBlurIdentity.value) {
|
|
3533
|
+
// let placeHolderElm = document.createElement("div");
|
|
3534
|
+
// placeHolderElm.className = "participant";
|
|
3535
|
+
// placeHolderElm.innerHTML = placeholderTemplate;
|
|
3536
|
+
// pointTurnCenter.appendChild(placeHolderElm);
|
|
3537
|
+
// }
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
// 递补填充空位
|
|
3541
|
+
if (pointTurnTopChildNum < 5) {
|
|
3542
|
+
if (pointTurnBottomChildNum > 0) {
|
|
3543
|
+
pointTurnTop.appendChild(pointTurnBottom.firstElementChild);
|
|
3544
|
+
} else if (pointTurnOtherChildNum > 0) {
|
|
3545
|
+
pointTurnTop.appendChild(pointTurnOther.firstChild);
|
|
3546
|
+
}
|
|
3547
|
+
} else if (pointTurnBottomChildNum < 5) {
|
|
3548
|
+
if (pointTurnOtherChildNum > 0) {
|
|
3549
|
+
pointTurnBottom.appendChild(pointTurnOther.firstChild);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
},
|
|
3554
|
+
async executeBlurChange(newVal, oldVal) {
|
|
3555
|
+
console.log("执行焦点用户变化", newVal, "->", oldVal);
|
|
3556
|
+
console.log("修改前焦点用户", oldVal);
|
|
3557
|
+
console.log("修改后焦点用户", newVal);
|
|
3558
|
+
|
|
3559
|
+
if (!newVal) {
|
|
3560
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
3561
|
+
const pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
3562
|
+
if (pointTurnCenter) {
|
|
3563
|
+
if (pointTurnCenter.children.length < 2) {
|
|
3564
|
+
// 此时中心区域有空位,直接添加占位符
|
|
3565
|
+
const placeHolderElm = document.createElement("div");
|
|
3566
|
+
placeHolderElm.className = "participant";
|
|
3567
|
+
placeHolderElm.innerHTML = this.placeholderTemplate;
|
|
3568
|
+
pointTurnCenter.appendChild(placeHolderElm);
|
|
3569
|
+
} else {
|
|
3570
|
+
// 此时中心区域已满,需要替换
|
|
3571
|
+
const lastChild = pointTurnCenter.lastChild;
|
|
3572
|
+
if (lastChild && lastChild.id) {
|
|
3573
|
+
// 最后一个是真实用户,移动到其他位置,并使用占位符补位,优先移动到top,若top已满,则移动到bottom,若bottom已满,则移动到other
|
|
3574
|
+
const pointTurnTop = document.querySelector("#room .point-turn-top");
|
|
3575
|
+
const pointTurnBottom = document.querySelector("#room .point-turn-bottom");
|
|
3576
|
+
const pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
3577
|
+
if (pointTurnTop && pointTurnTop.children.length < 5) {
|
|
3578
|
+
pointTurnTop.appendChild(lastChild);
|
|
3579
|
+
} else if (pointTurnBottom && pointTurnBottom.children.length < 5) {
|
|
3580
|
+
pointTurnBottom.appendChild(lastChild);
|
|
3581
|
+
} else if (pointTurnOther && pointTurnOther.children.length < 5) {
|
|
3582
|
+
pointTurnOther.appendChild(lastChild);
|
|
3583
|
+
}
|
|
3584
|
+
// 使用占位符补位
|
|
3585
|
+
const placeHolderElm = document.createElement("div");
|
|
3586
|
+
placeHolderElm.className = "participant";
|
|
3587
|
+
placeHolderElm.innerHTML = this.placeholderTemplate;
|
|
3588
|
+
pointTurnCenter.appendChild(placeHolderElm);
|
|
3589
|
+
} else if (lastChild && !lastChild.id) {
|
|
3590
|
+
// 最后一个是占位符,do nothing
|
|
3591
|
+
void 0;
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
} else {
|
|
3596
|
+
if (this.currentLayout === "grid") {
|
|
3597
|
+
// 宫格布局下移除焦点用户,则do nothing
|
|
3598
|
+
void 0;
|
|
3599
|
+
} else if (this.currentLayout === "rightSide") {
|
|
3600
|
+
// 焦点布局下移除焦点用户,则切换到宫格布局
|
|
3601
|
+
this.setLayout("grid");
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
} else {
|
|
3605
|
+
// 在点调模式下的处理
|
|
3606
|
+
if (this.currentRoomMode === "pointTurn") {
|
|
3607
|
+
// 如果焦点用户和主持人是同一人,需要特殊处理
|
|
3608
|
+
if (this.curHostIdentity === newVal) {
|
|
3609
|
+
console.log("焦点用户和主持人为同一人,无需额外处理");
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
const pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
3614
|
+
const videoDiv = document.getElementById(`participant-${newVal}`);
|
|
3615
|
+
|
|
3616
|
+
if (!pointTurnCenter) {
|
|
3617
|
+
this.showMessage.warning("点调模式中心容器不存在");
|
|
3618
|
+
return;
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
if (!videoDiv) {
|
|
3622
|
+
this.showMessage.warning("焦点用户视频元素不存在:", newVal);
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
let videoDivParentEle = videoDiv.parentElement;
|
|
3627
|
+
console.log("焦点元素的父元素", videoDivParentEle, videoDivParentEle?.className);
|
|
3628
|
+
|
|
3629
|
+
if (pointTurnCenter.children.length < 2) {
|
|
3630
|
+
// 中心区域有空位,直接添加
|
|
3631
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
3632
|
+
|
|
3633
|
+
// 从其他区域补充元素到原位置
|
|
3634
|
+
if (videoDivParentEle && videoDivParentEle.className !== "point-turn-other") {
|
|
3635
|
+
const pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
3636
|
+
if (pointTurnOther && pointTurnOther.children.length > 0) {
|
|
3637
|
+
videoDivParentEle.appendChild(pointTurnOther.firstChild);
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
} else {
|
|
3641
|
+
// 中心区域已满,需要替换
|
|
3642
|
+
const lastChild = pointTurnCenter.lastChild;
|
|
3643
|
+
if (lastChild && lastChild.id) {
|
|
3644
|
+
// 最后一个是真实用户,移动到原位置
|
|
3645
|
+
if (videoDivParentEle) {
|
|
3646
|
+
videoDivParentEle.appendChild(lastChild);
|
|
3647
|
+
}
|
|
3648
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
3649
|
+
} else if (lastChild && !lastChild.id) {
|
|
3650
|
+
// 最后一个是占位符,直接替换
|
|
3651
|
+
pointTurnCenter.removeChild(lastChild);
|
|
3652
|
+
pointTurnCenter.appendChild(videoDiv);
|
|
3653
|
+
|
|
3654
|
+
// 从其他区域补充元素
|
|
3655
|
+
if (videoDivParentEle && videoDivParentEle.className !== "point-turn-other") {
|
|
3656
|
+
const pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
3657
|
+
if (pointTurnOther && pointTurnOther.children.length > 0) {
|
|
3658
|
+
videoDivParentEle.appendChild(pointTurnOther.firstChild);
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
} else {
|
|
3664
|
+
// 会议模式下的处理
|
|
3665
|
+
console.log("11111");
|
|
3666
|
+
if (this.currentLayout === "grid") {
|
|
3667
|
+
console.log("222222");
|
|
3668
|
+
// 在宫格布局下设置焦点用户,自动切换到焦点布局
|
|
3669
|
+
console.log("在宫格布局下设置焦点用户,自动切换到焦点布局");
|
|
3670
|
+
this.setLayout("rightSide");
|
|
3671
|
+
} else if (this.currentLayout === "rightSide") {
|
|
3672
|
+
// 在焦点布局下切换焦点用户
|
|
3673
|
+
const layoutRightSideEle = document.querySelector("#room .layout-rightside");
|
|
3674
|
+
const layoutLeftSideEle = document.querySelector("#room .layout-leftside");
|
|
3675
|
+
const videoDiv = document.getElementById(`participant-${newVal}`);
|
|
3676
|
+
|
|
3677
|
+
if (!layoutLeftSideEle || !layoutRightSideEle) {
|
|
3678
|
+
this.showMessage.warning("焦点布局容器不存在");
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
if (!videoDiv) {
|
|
3683
|
+
this.showMessage.warning("焦点用户视频元素不存在:", newVal);
|
|
3684
|
+
return;
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
// 将左侧容器的元素移动到右侧
|
|
3688
|
+
let child = null;
|
|
3689
|
+
while ((child = layoutLeftSideEle.firstChild)) {
|
|
3690
|
+
if (child) {
|
|
3691
|
+
layoutRightSideEle.appendChild(child);
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
|
|
3695
|
+
// 将新焦点用户放入左侧容器
|
|
3696
|
+
layoutLeftSideEle.appendChild(videoDiv);
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
},
|
|
3701
|
+
async executeModeChange(newMode) {
|
|
3702
|
+
console.log("执行模式变化", newMode);
|
|
3703
|
+
this.$nextTick(() => {
|
|
3704
|
+
let container = document.getElementById("room");
|
|
3705
|
+
|
|
3706
|
+
if (!container) {
|
|
3707
|
+
this.showMessage.warning("会议容器不存在");
|
|
3708
|
+
return;
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
if (newMode === "pointTurn") {
|
|
3712
|
+
// 会议模式切换到点调模式
|
|
3713
|
+
|
|
3714
|
+
// 首先检查并清理已存在的点调容器,防止嵌套问题
|
|
3715
|
+
const existingPointTurnElements = container.querySelectorAll(
|
|
3716
|
+
".point-turn-top, .point-turn-center, .point-turn-bottom, .point-turn-other"
|
|
3717
|
+
);
|
|
3718
|
+
|
|
3719
|
+
if (existingPointTurnElements.length > 0) {
|
|
3720
|
+
console.log(
|
|
3721
|
+
"发现已存在的点调容器,开始清理:",
|
|
3722
|
+
Array.from(existingPointTurnElements).map((el) => el.className)
|
|
3723
|
+
);
|
|
3724
|
+
|
|
3725
|
+
existingPointTurnElements.forEach((element) => {
|
|
3726
|
+
if (container.contains(element)) {
|
|
3727
|
+
console.log(`清理容器 ${element.className},子元素数量:`, element.children.length);
|
|
3728
|
+
|
|
3729
|
+
// 将现有点调容器中的子元素移回主容器
|
|
3730
|
+
const childrenToMove = Array.from(element.children);
|
|
3731
|
+
childrenToMove.forEach((child) => {
|
|
3732
|
+
if (child.id) {
|
|
3733
|
+
console.log(`移动元素 ${child.id || child.className} 回主容器`);
|
|
3734
|
+
container.appendChild(child);
|
|
3735
|
+
} else {
|
|
3736
|
+
console.log(`移除占位符元素 ${child.className}`);
|
|
3737
|
+
child.remove();
|
|
3738
|
+
}
|
|
3739
|
+
});
|
|
3740
|
+
|
|
3741
|
+
// 移除空的点调容器
|
|
3742
|
+
container.removeChild(element);
|
|
3743
|
+
console.log(`已移除容器 ${element.className}`);
|
|
3744
|
+
}
|
|
3745
|
+
});
|
|
3746
|
+
|
|
3747
|
+
console.log(
|
|
3748
|
+
"点调容器清理完成,当前主容器子元素:",
|
|
3749
|
+
Array.from(container.children).map((el) => el.className || el.id)
|
|
3750
|
+
);
|
|
3751
|
+
} else {
|
|
3752
|
+
console.log("未发现已存在的点调容器,继续创建新布局");
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3755
|
+
// 初始化点调模式下元素
|
|
3756
|
+
let pointTurnTop = document.createElement("div");
|
|
3757
|
+
pointTurnTop.className = "point-turn-top";
|
|
3758
|
+
let pointTurnCenter = document.createElement("div");
|
|
3759
|
+
pointTurnCenter.className = "point-turn-center";
|
|
3760
|
+
let pointTurnBottom = document.createElement("div");
|
|
3761
|
+
pointTurnBottom.className = "point-turn-bottom";
|
|
3762
|
+
let pointTurnOther = document.createElement("div");
|
|
3763
|
+
pointTurnOther.className = "point-turn-other";
|
|
3764
|
+
|
|
3765
|
+
// 创建占位符元素
|
|
3766
|
+
let placeHolderElm1 = document.createElement("div");
|
|
3767
|
+
let placeHolderElm2 = document.createElement("div");
|
|
3768
|
+
placeHolderElm1.className = "participant";
|
|
3769
|
+
placeHolderElm1.innerHTML = this.placeholderTemplate;
|
|
3770
|
+
placeHolderElm2.className = "participant";
|
|
3771
|
+
placeHolderElm2.innerHTML = this.placeholderTemplate;
|
|
3772
|
+
|
|
3773
|
+
// 预先添加占位符
|
|
3774
|
+
pointTurnCenter.appendChild(placeHolderElm1);
|
|
3775
|
+
pointTurnCenter.appendChild(placeHolderElm2);
|
|
3776
|
+
|
|
3777
|
+
if (this.currentLayout === "grid") {
|
|
3778
|
+
// 从宫格布局到点调模式
|
|
3779
|
+
// 收集所有参与者元素,避免在循环中操作DOM结构导致的问题
|
|
3780
|
+
const participantElements = Array.from(container.children).filter(
|
|
3781
|
+
(child) =>
|
|
3782
|
+
child.classList.contains("participant") && child.id.startsWith("participant-")
|
|
3783
|
+
);
|
|
3784
|
+
|
|
3785
|
+
console.log(
|
|
3786
|
+
"找到的参与者元素:",
|
|
3787
|
+
participantElements.map((el) => el.id)
|
|
3788
|
+
);
|
|
3789
|
+
|
|
3790
|
+
// 处理参与者元素分配到各个位置
|
|
3791
|
+
participantElements.forEach((child) => {
|
|
3792
|
+
if (this.curHostIdentity && child.id === `participant-${this.curHostIdentity}`) {
|
|
3793
|
+
// 主持人放在中心区域第一个位置
|
|
3794
|
+
pointTurnCenter.removeChild(placeHolderElm1);
|
|
3795
|
+
pointTurnCenter.insertBefore(child, pointTurnCenter.firstChild ?? null);
|
|
3796
|
+
} else if (
|
|
3797
|
+
this.curBlurIdentity &&
|
|
3798
|
+
child.id === `participant-${this.curBlurIdentity}`
|
|
3799
|
+
) {
|
|
3800
|
+
if (this.curBlurIdentity === this.curHostIdentity) {
|
|
3801
|
+
// 当焦点用户和主持人为同一人,什么都不做
|
|
3802
|
+
void 0;
|
|
3803
|
+
} else {
|
|
3804
|
+
// 焦点用户放在中心区域第二个位置
|
|
3805
|
+
pointTurnCenter.removeChild(placeHolderElm2);
|
|
3806
|
+
pointTurnCenter.appendChild(child);
|
|
3807
|
+
}
|
|
3808
|
+
} else {
|
|
3809
|
+
// 其他用户按顺序放入top、bottom、other区域
|
|
3810
|
+
if (pointTurnTop.children.length >= 5) {
|
|
3811
|
+
if (pointTurnBottom.children.length >= 5) {
|
|
3812
|
+
pointTurnOther.appendChild(child);
|
|
3813
|
+
} else {
|
|
3814
|
+
pointTurnBottom.appendChild(child);
|
|
3815
|
+
}
|
|
3816
|
+
} else {
|
|
3817
|
+
pointTurnTop.appendChild(child);
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
});
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3823
|
+
if (this.currentLayout === "rightSide") {
|
|
3824
|
+
// 从焦点布局到点调模式
|
|
3825
|
+
let layoutRightSideEle = document.querySelector("#room .layout-rightside");
|
|
3826
|
+
let layoutLeftSideEle = document.querySelector("#room .layout-leftside");
|
|
3827
|
+
|
|
3828
|
+
// 处理左侧容器(焦点用户)
|
|
3829
|
+
if (layoutLeftSideEle) {
|
|
3830
|
+
let child = null;
|
|
3831
|
+
while ((child = layoutLeftSideEle.firstChild)) {
|
|
3832
|
+
if (child) {
|
|
3833
|
+
if (this.curHostIdentity && child.id === `participant-${this.curHostIdentity}`) {
|
|
3834
|
+
// 主持人存在且当前与会者为主持人
|
|
3835
|
+
pointTurnCenter.removeChild(placeHolderElm1);
|
|
3836
|
+
pointTurnCenter.insertBefore(child, pointTurnCenter.firstChild ?? null);
|
|
3837
|
+
} else if (
|
|
3838
|
+
this.curBlurIdentity &&
|
|
3839
|
+
child.id === `participant-${this.curBlurIdentity}`
|
|
3840
|
+
) {
|
|
3841
|
+
if (this.curBlurIdentity === this.curHostIdentity) {
|
|
3842
|
+
// 当焦点用户和主持人为同一人,什么都不做
|
|
3843
|
+
void 0;
|
|
3844
|
+
} else {
|
|
3845
|
+
pointTurnCenter.removeChild(placeHolderElm2);
|
|
3846
|
+
pointTurnCenter.appendChild(child);
|
|
3847
|
+
}
|
|
3848
|
+
} else {
|
|
3849
|
+
// 分配到其他区域
|
|
3850
|
+
if (pointTurnTop.children.length >= 5) {
|
|
3851
|
+
if (pointTurnBottom.children.length >= 5) {
|
|
3852
|
+
pointTurnOther.appendChild(child);
|
|
3853
|
+
} else {
|
|
3854
|
+
pointTurnBottom.appendChild(child);
|
|
3855
|
+
}
|
|
3856
|
+
} else {
|
|
3857
|
+
pointTurnTop.appendChild(child);
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
// 检查元素是否仍然是 container 的子元素,然后移除
|
|
3863
|
+
if (container.contains(layoutLeftSideEle)) {
|
|
3864
|
+
container.removeChild(layoutLeftSideEle);
|
|
3865
|
+
} else {
|
|
3866
|
+
console.warn("layoutLeftSideEle is no longer a child of container");
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
// 处理右侧容器
|
|
3871
|
+
if (layoutRightSideEle) {
|
|
3872
|
+
let child = null;
|
|
3873
|
+
while ((child = layoutRightSideEle.firstChild)) {
|
|
3874
|
+
if (child) {
|
|
3875
|
+
if (this.curHostIdentity && child.id === `participant-${this.curHostIdentity}`) {
|
|
3876
|
+
pointTurnCenter.removeChild(placeHolderElm1);
|
|
3877
|
+
pointTurnCenter.insertBefore(child, pointTurnCenter.firstChild ?? null);
|
|
3878
|
+
} else if (
|
|
3879
|
+
this.curBlurIdentity &&
|
|
3880
|
+
child.id === `participant-${this.curBlurIdentity}`
|
|
3881
|
+
) {
|
|
3882
|
+
if (this.curBlurIdentity === this.curHostIdentity) {
|
|
3883
|
+
// 当焦点用户和主持人为同一人,什么都不做
|
|
3884
|
+
void 0;
|
|
3885
|
+
} else {
|
|
3886
|
+
pointTurnCenter.removeChild(placeHolderElm2);
|
|
3887
|
+
pointTurnCenter.appendChild(child);
|
|
3888
|
+
}
|
|
3889
|
+
} else {
|
|
3890
|
+
// 分配到其他区域
|
|
3891
|
+
if (pointTurnTop.children.length >= 5) {
|
|
3892
|
+
if (pointTurnBottom.children.length >= 5) {
|
|
3893
|
+
pointTurnOther.appendChild(child);
|
|
3894
|
+
} else {
|
|
3895
|
+
pointTurnBottom.appendChild(child);
|
|
3896
|
+
}
|
|
3897
|
+
} else {
|
|
3898
|
+
pointTurnTop.appendChild(child);
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
// 检查元素是否仍然是 container 的子元素,然后移除
|
|
3904
|
+
if (container.contains(layoutRightSideEle)) {
|
|
3905
|
+
container.removeChild(layoutRightSideEle);
|
|
3906
|
+
} else {
|
|
3907
|
+
console.warn("layoutRightSideEle is no longer a child of container");
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
|
|
3912
|
+
// 添加点调容器之前的最终检查
|
|
3913
|
+
const remainingLayoutElements = container.querySelectorAll(
|
|
3914
|
+
".layout-leftside, .layout-rightside, .point-turn-top, .point-turn-center, .point-turn-bottom, .point-turn-other"
|
|
3915
|
+
);
|
|
3916
|
+
if (remainingLayoutElements.length > 0) {
|
|
3917
|
+
console.warn(
|
|
3918
|
+
"发现遗留的布局元素,强制清理:",
|
|
3919
|
+
Array.from(remainingLayoutElements).map((el) => el.className)
|
|
3920
|
+
);
|
|
3921
|
+
remainingLayoutElements.forEach((element) => {
|
|
3922
|
+
if (container.contains(element)) {
|
|
3923
|
+
// 将子元素移回主容器
|
|
3924
|
+
Array.from(element.children).forEach((child) => {
|
|
3925
|
+
if (child.classList.contains("participant")) {
|
|
3926
|
+
container.appendChild(child);
|
|
3927
|
+
}
|
|
3928
|
+
});
|
|
3929
|
+
container.removeChild(element);
|
|
3930
|
+
}
|
|
3931
|
+
});
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
// 点调模式下元素添加到container下
|
|
3935
|
+
console.log(
|
|
3936
|
+
"添加点调容器到主容器,当前container子元素:",
|
|
3937
|
+
Array.from(container.children).map((el) => el.className || el.tagName)
|
|
3938
|
+
);
|
|
3939
|
+
|
|
3940
|
+
container.insertBefore(pointTurnTop, container.firstChild ?? null);
|
|
3941
|
+
container.appendChild(pointTurnCenter);
|
|
3942
|
+
container.appendChild(pointTurnBottom);
|
|
3943
|
+
container.appendChild(pointTurnOther);
|
|
3944
|
+
|
|
3945
|
+
console.log("点调容器添加完成,最终结构:", {
|
|
3946
|
+
containerChildren: Array.from(container.children).map((el) => el.className),
|
|
3947
|
+
pointTurnTopChildren: Array.from(pointTurnTop.children).map(
|
|
3948
|
+
(el) => el.className || el.id
|
|
3949
|
+
),
|
|
3950
|
+
pointTurnCenterChildren: Array.from(pointTurnCenter.children).map(
|
|
3951
|
+
(el) => el.className || el.id
|
|
3952
|
+
),
|
|
3953
|
+
pointTurnBottomChildren: Array.from(pointTurnBottom.children).map(
|
|
3954
|
+
(el) => el.className || el.id
|
|
3955
|
+
),
|
|
3956
|
+
pointTurnOtherChildren: Array.from(pointTurnOther.children).map(
|
|
3957
|
+
(el) => el.className || el.id
|
|
3958
|
+
),
|
|
3959
|
+
});
|
|
3960
|
+
} else {
|
|
3961
|
+
// 点调模式切换到会议模式
|
|
3962
|
+
let pointTurnTop = document.querySelector("#room .point-turn-top");
|
|
3963
|
+
let pointTurnCenter = document.querySelector("#room .point-turn-center");
|
|
3964
|
+
let pointTurnBottom = document.querySelector("#room .point-turn-bottom");
|
|
3965
|
+
let pointTurnOther = document.querySelector("#room .point-turn-other");
|
|
3966
|
+
|
|
3967
|
+
console.log("currentLayout", this.currentLayout);
|
|
3968
|
+
|
|
3969
|
+
if (this.currentLayout === "grid") {
|
|
3970
|
+
// 从点调模式到宫格布局
|
|
3971
|
+
const processContainer = (containerEle) => {
|
|
3972
|
+
if (!containerEle) {
|
|
3973
|
+
console.warn("processContainer: containerEle is null");
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
// 检查元素是否真的是 container 的子元素
|
|
3978
|
+
if (!container.contains(containerEle)) {
|
|
3979
|
+
console.warn("processContainer: containerEle is not a child of container");
|
|
3980
|
+
return;
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
if (containerEle.children.length > 0) {
|
|
3984
|
+
let child = null;
|
|
3985
|
+
while ((child = containerEle.firstChild)) {
|
|
3986
|
+
if (child) {
|
|
3987
|
+
if (child.id) {
|
|
3988
|
+
// 有ID的是真实用户元素
|
|
3989
|
+
container.appendChild(child);
|
|
3990
|
+
} else {
|
|
3991
|
+
// 无ID的是占位符,直接移除
|
|
3992
|
+
child.remove();
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3998
|
+
// 再次检查元素是否仍然是 container 的子元素,然后移除
|
|
3999
|
+
if (container.contains(containerEle)) {
|
|
4000
|
+
container.removeChild(containerEle);
|
|
4001
|
+
} else {
|
|
4002
|
+
console.warn("processContainer: containerEle is no longer a child of container");
|
|
4003
|
+
}
|
|
4004
|
+
};
|
|
4005
|
+
|
|
4006
|
+
processContainer(pointTurnTop);
|
|
4007
|
+
processContainer(pointTurnCenter);
|
|
4008
|
+
processContainer(pointTurnBottom);
|
|
4009
|
+
processContainer(pointTurnOther);
|
|
4010
|
+
}
|
|
4011
|
+
|
|
4012
|
+
if (this.currentLayout === "rightSide") {
|
|
4013
|
+
// 从点调模式到焦点布局
|
|
4014
|
+
let layoutLeftSideEle = document.createElement("div");
|
|
4015
|
+
layoutLeftSideEle.className = "layout-leftside";
|
|
4016
|
+
let layoutRightSideEle = document.createElement("div");
|
|
4017
|
+
layoutRightSideEle.className = "layout-rightside";
|
|
4018
|
+
|
|
4019
|
+
const processContainerForRightSide = (containerEle) => {
|
|
4020
|
+
if (!containerEle) {
|
|
4021
|
+
console.warn("processContainerForRightSide: containerEle is null");
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
|
|
4025
|
+
// 检查元素是否真的是 container 的子元素
|
|
4026
|
+
if (!container.contains(containerEle)) {
|
|
4027
|
+
console.warn(
|
|
4028
|
+
"processContainerForRightSide: containerEle is not a child of container"
|
|
4029
|
+
);
|
|
4030
|
+
return;
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
// 处理容器中的子元素(如果有的话)
|
|
4034
|
+
if (containerEle.children.length > 0) {
|
|
4035
|
+
let child = null;
|
|
4036
|
+
while ((child = containerEle.firstChild)) {
|
|
4037
|
+
if (child) {
|
|
4038
|
+
if (child.id) {
|
|
4039
|
+
// 有ID的是真实用户元素
|
|
4040
|
+
if (
|
|
4041
|
+
this.curBlurIdentity &&
|
|
4042
|
+
child.id === `participant-${this.curBlurIdentity}`
|
|
4043
|
+
) {
|
|
4044
|
+
layoutLeftSideEle.appendChild(child);
|
|
4045
|
+
} else if (
|
|
4046
|
+
this.localIdentity &&
|
|
4047
|
+
child.id === `participant-${this.localIdentity}`
|
|
4048
|
+
) {
|
|
4049
|
+
layoutLeftSideEle.appendChild(child);
|
|
4050
|
+
} else {
|
|
4051
|
+
layoutRightSideEle.appendChild(child);
|
|
4052
|
+
}
|
|
4053
|
+
} else {
|
|
4054
|
+
// 无ID的是占位符,直接移除
|
|
4055
|
+
child.remove();
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
// 再次检查元素是否仍然是 container 的子元素,然后移除
|
|
4062
|
+
if (container.contains(containerEle)) {
|
|
4063
|
+
container.removeChild(containerEle);
|
|
4064
|
+
} else {
|
|
4065
|
+
console.warn(
|
|
4066
|
+
"processContainerForRightSide: containerEle is no longer a child of container"
|
|
4067
|
+
);
|
|
4068
|
+
}
|
|
4069
|
+
};
|
|
4070
|
+
|
|
4071
|
+
processContainerForRightSide(pointTurnTop);
|
|
4072
|
+
processContainerForRightSide(pointTurnCenter);
|
|
4073
|
+
processContainerForRightSide(pointTurnBottom);
|
|
4074
|
+
processContainerForRightSide(pointTurnOther);
|
|
4075
|
+
|
|
4076
|
+
// 添加布局容器
|
|
4077
|
+
container.insertBefore(layoutLeftSideEle, container.firstChild ?? null);
|
|
4078
|
+
container.appendChild(layoutRightSideEle);
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
});
|
|
4082
|
+
},
|
|
4083
|
+
|
|
4084
|
+
initGlobleEvent() {
|
|
4085
|
+
window.addEventListener("click", this.clickAway);
|
|
4086
|
+
window.addEventListener("beforeunload", this.verifyLeavePage);
|
|
4087
|
+
document.addEventListener("fullscreenchange", this.fullscreenChangeHandler);
|
|
4088
|
+
},
|
|
4089
|
+
removeGlobleEvent() {
|
|
4090
|
+
window.removeEventListener("click", this.clickAway);
|
|
4091
|
+
document.removeEventListener("fullscreenchange", this.fullscreenChangeHandler);
|
|
4092
|
+
},
|
|
4093
|
+
fullscreenChangeHandler() {
|
|
4094
|
+
if (document.fullscreenElement) {
|
|
4095
|
+
this.isFullScreen = true;
|
|
4096
|
+
} else {
|
|
4097
|
+
this.isFullScreen = false;
|
|
4098
|
+
}
|
|
4099
|
+
},
|
|
4100
|
+
|
|
4101
|
+
stopUnjoinParticipantPolling() {
|
|
4102
|
+
if (this.unjoinParticipantInterval) {
|
|
4103
|
+
clearInterval(this.unjoinParticipantInterval);
|
|
4104
|
+
this.unjoinParticipantInterval = null;
|
|
4105
|
+
}
|
|
4106
|
+
},
|
|
4107
|
+
dispatchLiveClientEvent() {
|
|
4108
|
+
if (this.liveClient) {
|
|
4109
|
+
if (this.pageEventList.length > 0) {
|
|
4110
|
+
this.pageEventList.forEach((eventName) => {
|
|
4111
|
+
console.log("解除绑定事件:", eventName);
|
|
4112
|
+
this.liveClient.off(eventName);
|
|
4113
|
+
});
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
},
|
|
4117
|
+
},
|
|
4118
|
+
};
|
|
4119
|
+
</script>
|
|
4120
|
+
|
|
4121
|
+
<style lang="scss" scoped>
|
|
4122
|
+
.meeting-room {
|
|
4123
|
+
width: 1354px;
|
|
4124
|
+
height: 916px;
|
|
4125
|
+
position: fixed;
|
|
4126
|
+
inset: 0;
|
|
4127
|
+
margin: auto;
|
|
4128
|
+
z-index: 2000;
|
|
4129
|
+
overflow: hidden;
|
|
4130
|
+
border-radius: 6px;
|
|
4131
|
+
|
|
4132
|
+
&-top-bar {
|
|
4133
|
+
width: 100%;
|
|
4134
|
+
height: 48px;
|
|
4135
|
+
display: flex;
|
|
4136
|
+
justify-content: space-between;
|
|
4137
|
+
align-items: center;
|
|
4138
|
+
background: var(--dialog-title-bg);
|
|
4139
|
+
padding: 0 20px;
|
|
4140
|
+
border-bottom: 1px solid var(--dialog-border-color);
|
|
4141
|
+
|
|
4142
|
+
.bar-group {
|
|
4143
|
+
display: flex;
|
|
4144
|
+
align-items: center;
|
|
4145
|
+
flex-wrap: nowrap;
|
|
4146
|
+
|
|
4147
|
+
.meeting-theme {
|
|
4148
|
+
position: relative;
|
|
4149
|
+
flex-shrink: 0;
|
|
4150
|
+
text-align: center;
|
|
4151
|
+
width: 80px;
|
|
4152
|
+
height: 28px;
|
|
4153
|
+
background: var(--theme-color);
|
|
4154
|
+
border-radius: 4px;
|
|
4155
|
+
display: flex;
|
|
4156
|
+
align-items: center;
|
|
4157
|
+
justify-content: center;
|
|
4158
|
+
font-weight: 400;
|
|
4159
|
+
font-size: 14px;
|
|
4160
|
+
margin-right: 20px;
|
|
4161
|
+
color: var(--theme-font-color);
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4164
|
+
.meeting-name {
|
|
4165
|
+
cursor: pointer;
|
|
4166
|
+
flex-shrink: 0;
|
|
4167
|
+
display: flex;
|
|
4168
|
+
flex-wrap: nowrap;
|
|
4169
|
+
align-items: center;
|
|
4170
|
+
font-weight: 400;
|
|
4171
|
+
font-size: 16px;
|
|
4172
|
+
color: var(--theme-font-color);
|
|
4173
|
+
position: relative;
|
|
4174
|
+
|
|
4175
|
+
&-text {
|
|
4176
|
+
flex-shrink: 0;
|
|
4177
|
+
white-space: nowrap;
|
|
4178
|
+
margin-right: 12px;
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
&-icon {
|
|
4182
|
+
flex-shrink: 0;
|
|
4183
|
+
width: 10px;
|
|
4184
|
+
height: 5px;
|
|
4185
|
+
background: var(--ready-arrow-icon) no-repeat center / 140% 140%;
|
|
4186
|
+
transition: transform 0.2s ease;
|
|
4187
|
+
&-expanded {
|
|
4188
|
+
transform: rotate(180deg);
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
.btn {
|
|
4194
|
+
width: 16px;
|
|
4195
|
+
height: 16px;
|
|
4196
|
+
cursor: pointer;
|
|
4197
|
+
position: relative;
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
.layout {
|
|
4201
|
+
background: var(--meeting-layout-icon) no-repeat center / 100% 100%;
|
|
4202
|
+
|
|
4203
|
+
&-active {
|
|
4204
|
+
background: var(--meeting-layout-icon) no-repeat center / 100% 100%;
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
.custom-layout {
|
|
4208
|
+
background: var(--custom-layout-header-icon) no-repeat center / 100% 100%;
|
|
4209
|
+
}
|
|
4210
|
+
.slide-small {
|
|
4211
|
+
background: var(--meeting-slide-small-icon) no-repeat center / 100% 100%;
|
|
4212
|
+
margin-right: 12px;
|
|
4213
|
+
}
|
|
4214
|
+
.full-screen {
|
|
4215
|
+
background: var(--call-fullscreen-icon) no-repeat center / 100% 100%;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
.close-meeting-icon {
|
|
4219
|
+
background: var(--close-icon) no-repeat center / 100% 100%;
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
&-text {
|
|
4223
|
+
cursor: pointer;
|
|
4224
|
+
font-weight: 400;
|
|
4225
|
+
font-size: 12px;
|
|
4226
|
+
color: var(--theme-font-color);
|
|
4227
|
+
margin: 0 12px 0 6px;
|
|
4228
|
+
}
|
|
4229
|
+
&-split {
|
|
4230
|
+
width: 1px;
|
|
4231
|
+
height: 20px;
|
|
4232
|
+
background: var(--dialog-border-color);
|
|
4233
|
+
margin: 0 20px;
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
|
|
4237
|
+
.meeting-duration {
|
|
4238
|
+
font-weight: 500;
|
|
4239
|
+
font-size: 16px;
|
|
4240
|
+
color: #ffffff;
|
|
4241
|
+
display: flex;
|
|
4242
|
+
align-items: center;
|
|
4243
|
+
justify-content: center;
|
|
4244
|
+
flex-wrap: nowrap;
|
|
4245
|
+
|
|
4246
|
+
&-logo {
|
|
4247
|
+
width: 16px;
|
|
4248
|
+
height: 16px;
|
|
4249
|
+
background: var(--call-duration-icon) no-repeat center / 100% 100%;
|
|
4250
|
+
margin-right: 7px;
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
span {
|
|
4254
|
+
white-space: nowrap;
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
&-contain {
|
|
4260
|
+
width: 100%;
|
|
4261
|
+
height: calc(100% - 48px);
|
|
4262
|
+
display: flex;
|
|
4263
|
+
align-items: flex-start;
|
|
4264
|
+
flex-wrap: nowrap;
|
|
4265
|
+
position: relative;
|
|
4266
|
+
|
|
4267
|
+
&-left {
|
|
4268
|
+
flex-shrink: 0;
|
|
4269
|
+
width: 100%;
|
|
4270
|
+
height: 100%;
|
|
4271
|
+
position: relative;
|
|
4272
|
+
|
|
4273
|
+
&-slide {
|
|
4274
|
+
width: calc(100% - 347px);
|
|
4275
|
+
}
|
|
4276
|
+
|
|
4277
|
+
.meeting {
|
|
4278
|
+
width: 100%;
|
|
4279
|
+
height: 100%;
|
|
4280
|
+
background: var(--meeting-bg);
|
|
4281
|
+
padding: 5px;
|
|
4282
|
+
overflow-y: auto;
|
|
4283
|
+
position: relative;
|
|
4284
|
+
|
|
4285
|
+
&::-webkit-scrollbar {
|
|
4286
|
+
display: none;
|
|
4287
|
+
}
|
|
4288
|
+
&-slide {
|
|
4289
|
+
height: calc(100% - 66px);
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
.meeting-control-bar {
|
|
4294
|
+
width: 100%;
|
|
4295
|
+
height: 66px;
|
|
4296
|
+
padding: 10px 20px 20px;
|
|
4297
|
+
background: var(--dialog-bg);
|
|
4298
|
+
display: flex;
|
|
4299
|
+
align-items: center;
|
|
4300
|
+
justify-content: space-between;
|
|
4301
|
+
position: absolute;
|
|
4302
|
+
z-index: 100;
|
|
4303
|
+
bottom: -66px;
|
|
4304
|
+
left: 0;
|
|
4305
|
+
transition: all 0.2s ease;
|
|
4306
|
+
|
|
4307
|
+
&-show {
|
|
4308
|
+
bottom: 0;
|
|
4309
|
+
}
|
|
4310
|
+
|
|
4311
|
+
.bar-group {
|
|
4312
|
+
display: flex;
|
|
4313
|
+
align-items: center;
|
|
4314
|
+
flex-wrap: nowrap;
|
|
4315
|
+
|
|
4316
|
+
.custom-select {
|
|
4317
|
+
position: relative;
|
|
4318
|
+
background: var(--ready-dialog-select-bg);
|
|
4319
|
+
border-radius: 6px;
|
|
4320
|
+
height: 36px;
|
|
4321
|
+
display: flex;
|
|
4322
|
+
flex-wrap: nowrap;
|
|
4323
|
+
align-items: center;
|
|
4324
|
+
padding: 0 12px;
|
|
4325
|
+
cursor: pointer;
|
|
4326
|
+
&:not(:last-child) {
|
|
4327
|
+
margin-right: 12px;
|
|
4328
|
+
}
|
|
4329
|
+
|
|
4330
|
+
&-btn {
|
|
4331
|
+
width: 20px;
|
|
4332
|
+
height: 20px;
|
|
4333
|
+
margin-right: 6px;
|
|
4334
|
+
position: relative;
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
&-right {
|
|
4338
|
+
display: flex;
|
|
4339
|
+
align-items: center;
|
|
4340
|
+
flex-wrap: nowrap;
|
|
4341
|
+
position: relative;
|
|
4342
|
+
font-weight: 400;
|
|
4343
|
+
font-size: 14px;
|
|
4344
|
+
color: var(--theme-font-color);
|
|
4345
|
+
cursor: pointer;
|
|
4346
|
+
&-text {
|
|
4347
|
+
white-space: nowrap;
|
|
4348
|
+
flex: 1 0;
|
|
4349
|
+
margin-right: 12px;
|
|
4350
|
+
}
|
|
4351
|
+
.arrow-icon {
|
|
4352
|
+
width: 8px;
|
|
4353
|
+
height: 4px;
|
|
4354
|
+
flex: 1 0;
|
|
4355
|
+
background: var(--ready-arrow-icon) no-repeat center / 100% 100%;
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
.mic-on {
|
|
4360
|
+
background: var(--ready-mic-on-icon) no-repeat center / 100% 100%;
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
.mic-off {
|
|
4364
|
+
background: url("../../assets/image/common/mic_off_small.png") no-repeat center / 100%
|
|
4365
|
+
100%;
|
|
4366
|
+
}
|
|
4367
|
+
|
|
4368
|
+
.cam-on {
|
|
4369
|
+
background: var(--ready-cam-on-icon) no-repeat center / 100% 100%;
|
|
4370
|
+
}
|
|
4371
|
+
|
|
4372
|
+
.cam-off {
|
|
4373
|
+
background: url("../../assets/image/common/cam_off_small.png") no-repeat center / 100%
|
|
4374
|
+
100%;
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
.theme-icon {
|
|
4378
|
+
background: var(--meeting-mode-icon) no-repeat center / 100% 100%;
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
|
|
4382
|
+
.t-btn {
|
|
4383
|
+
position: relative;
|
|
4384
|
+
cursor: pointer;
|
|
4385
|
+
display: flex;
|
|
4386
|
+
flex-wrap: nowrap;
|
|
4387
|
+
align-items: center;
|
|
4388
|
+
padding: 0 12px;
|
|
4389
|
+
height: 36px;
|
|
4390
|
+
background: var(--ready-dialog-select-bg);
|
|
4391
|
+
border-radius: 6px;
|
|
4392
|
+
font-weight: 400;
|
|
4393
|
+
font-size: 14px;
|
|
4394
|
+
color: var(--theme-font-color);
|
|
4395
|
+
|
|
4396
|
+
&:not(:last-child) {
|
|
4397
|
+
margin-right: 12px;
|
|
4398
|
+
}
|
|
4399
|
+
|
|
4400
|
+
span {
|
|
4401
|
+
white-space: nowrap;
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
&-icon {
|
|
4405
|
+
width: 20px;
|
|
4406
|
+
height: 20px;
|
|
4407
|
+
margin-right: 6px;
|
|
4408
|
+
}
|
|
4409
|
+
&-hide {
|
|
4410
|
+
.t-btn-icon {
|
|
4411
|
+
margin-right: 0;
|
|
4412
|
+
}
|
|
4413
|
+
span {
|
|
4414
|
+
display: none;
|
|
4415
|
+
}
|
|
4416
|
+
}
|
|
4417
|
+
.screen-share {
|
|
4418
|
+
background: var(--meeting-share-icon) no-repeat center / 100% 100%;
|
|
4419
|
+
}
|
|
4420
|
+
.screen-share-off {
|
|
4421
|
+
background: var(--meeting-share-icon) no-repeat center / 100% 100%;
|
|
4422
|
+
}
|
|
4423
|
+
.chat {
|
|
4424
|
+
background: var(--meeting-chat-icon) no-repeat center / 100% 100%;
|
|
4425
|
+
}
|
|
4426
|
+
.record {
|
|
4427
|
+
background: var(--meeting-record-icon) no-repeat center / 100% 100%;
|
|
4428
|
+
}
|
|
4429
|
+
.record-off {
|
|
4430
|
+
background: var(--meeting-record-icon) no-repeat center / 100% 100%;
|
|
4431
|
+
}
|
|
4432
|
+
.invite {
|
|
4433
|
+
background: var(--meeting-invite-icon) no-repeat center / 100% 100%;
|
|
4434
|
+
}
|
|
4435
|
+
.setting {
|
|
4436
|
+
background: var(--meeting-setting-icon) no-repeat center / 100% 100%;
|
|
4437
|
+
}
|
|
4438
|
+
.member {
|
|
4439
|
+
background: var(--meeting-member-icon) no-repeat center / 100% 100%;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
.exit-btn {
|
|
4445
|
+
cursor: pointer;
|
|
4446
|
+
width: 112px;
|
|
4447
|
+
height: 36px;
|
|
4448
|
+
line-height: 36px;
|
|
4449
|
+
background: var(--btn-danger-bg);
|
|
4450
|
+
border-radius: 6px;
|
|
4451
|
+
text-align: center;
|
|
4452
|
+
font-weight: 500;
|
|
4453
|
+
font-size: 16px;
|
|
4454
|
+
color: #ffffff;
|
|
4455
|
+
border: 1px solid var(--btn-danger-bg);
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
&-right {
|
|
4461
|
+
flex-shrink: 0;
|
|
4462
|
+
height: 100%;
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
</style>
|
|
4467
|
+
<style lang="scss">
|
|
4468
|
+
@import "./style/index.scss";
|
|
4469
|
+
</style>
|