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.
Files changed (262) hide show
  1. package/README.md +92 -0
  2. package/index.js +35 -0
  3. package/package.json +41 -0
  4. package/src/App.vue +246 -0
  5. package/src/assets/audio/moon_light.ogg +0 -0
  6. package/src/assets/font/DINPro-Medium.otf +0 -0
  7. package/src/assets/font/FZZhengHeiS-B-GB.ttf +0 -0
  8. package/src/assets/font/PingFang Regular.otf +0 -0
  9. package/src/assets/font/SourceHanSansCN-Regular.otf +0 -0
  10. package/src/assets/image/common/add_other_icon.png +0 -0
  11. package/src/assets/image/common/bottomFrame.png +0 -0
  12. package/src/assets/image/common/broadcastOn.png +0 -0
  13. package/src/assets/image/common/broadcastTask.png +0 -0
  14. package/src/assets/image/common/call.png +0 -0
  15. package/src/assets/image/common/call_user_mic_off_icon.png +0 -0
  16. package/src/assets/image/common/call_user_mic_on_icon.png +0 -0
  17. package/src/assets/image/common/cam-off.png +0 -0
  18. package/src/assets/image/common/cam-on.png +0 -0
  19. package/src/assets/image/common/cam_off_small.png +0 -0
  20. package/src/assets/image/common/camera.png +0 -0
  21. package/src/assets/image/common/cameraBtn1.png +0 -0
  22. package/src/assets/image/common/cameraBtn2.png +0 -0
  23. package/src/assets/image/common/cameraBtn3.png +0 -0
  24. package/src/assets/image/common/cameraBtn4.png +0 -0
  25. package/src/assets/image/common/cameraBtn5.png +0 -0
  26. package/src/assets/image/common/cameraBtn6.png +0 -0
  27. package/src/assets/image/common/cameraBtn7.png +0 -0
  28. package/src/assets/image/common/cameraClose.png +0 -0
  29. package/src/assets/image/common/cameraFull.png +0 -0
  30. package/src/assets/image/common/cameraOff.png +0 -0
  31. package/src/assets/image/common/cancel_icon.png +0 -0
  32. package/src/assets/image/common/card_blue.png +0 -0
  33. package/src/assets/image/common/card_grey.png +0 -0
  34. package/src/assets/image/common/chosen_icon.png +0 -0
  35. package/src/assets/image/common/chosen_icon_slided.png +0 -0
  36. package/src/assets/image/common/custom_layout_equipment_avatar.png +0 -0
  37. package/src/assets/image/common/custom_layout_user_avatar.png +0 -0
  38. package/src/assets/image/common/default_avatar.png +0 -0
  39. package/src/assets/image/common/default_avatar_mini.png +0 -0
  40. package/src/assets/image/common/delete-number.svg +3 -0
  41. package/src/assets/image/common/deviceIcon.png +0 -0
  42. package/src/assets/image/common/fourOff.png +0 -0
  43. package/src/assets/image/common/fourOn.png +0 -0
  44. package/src/assets/image/common/group.png +0 -0
  45. package/src/assets/image/common/groupManage.png +0 -0
  46. package/src/assets/image/common/histroy_meeting_icon.png +0 -0
  47. package/src/assets/image/common/icon-device.svg +12 -0
  48. package/src/assets/image/common/icon-monitor.svg +12 -0
  49. package/src/assets/image/common/icon-/345/260/217/347/250/213/345/272/217.svg +5 -0
  50. package/src/assets/image/common/icon-/345/272/224/347/255/224.svg +5 -0
  51. package/src/assets/image/common/icon-/350/247/206/351/242/221.svg +7 -0
  52. package/src/assets/image/common/input_search_icon.png +0 -0
  53. package/src/assets/image/common/launch_icon.png +0 -0
  54. package/src/assets/image/common/layout-active1.png +0 -0
  55. package/src/assets/image/common/layout-active2.png +0 -0
  56. package/src/assets/image/common/layout1.png +0 -0
  57. package/src/assets/image/common/layout2.png +0 -0
  58. package/src/assets/image/common/loading.png +0 -0
  59. package/src/assets/image/common/login/checked_icon.png +0 -0
  60. package/src/assets/image/common/login/login_bg.png +0 -0
  61. package/src/assets/image/common/login/login_form_left_icon.png +0 -0
  62. package/src/assets/image/common/login/pwd_icon.png +0 -0
  63. package/src/assets/image/common/login/user_icon.png +0 -0
  64. package/src/assets/image/common/login/verify_icon.png +0 -0
  65. package/src/assets/image/common/logo.png +0 -0
  66. package/src/assets/image/common/ltypeOff.png +0 -0
  67. package/src/assets/image/common/ltypeOn.png +0 -0
  68. package/src/assets/image/common/manager_cam_off_icon.png +0 -0
  69. package/src/assets/image/common/manager_mic_off_icon.png +0 -0
  70. package/src/assets/image/common/map-cicle-icon.svg +6 -0
  71. package/src/assets/image/common/map-icon-search-info.svg +11 -0
  72. package/src/assets/image/common/map-location.svg +37 -0
  73. package/src/assets/image/common/map.png +0 -0
  74. package/src/assets/image/common/mic-off.png +0 -0
  75. package/src/assets/image/common/mic-on.png +0 -0
  76. package/src/assets/image/common/mic_off_small.png +0 -0
  77. package/src/assets/image/common/minum_cam_off_icon.png +0 -0
  78. package/src/assets/image/common/minum_cam_on_icon.png +0 -0
  79. package/src/assets/image/common/minum_expand_icon.png +0 -0
  80. package/src/assets/image/common/minum_mic_off_icon.png +0 -0
  81. package/src/assets/image/common/minum_mic_on_icon.png +0 -0
  82. package/src/assets/image/common/minum_reset_icon.png +0 -0
  83. package/src/assets/image/common/minum_slide_icon.png +0 -0
  84. package/src/assets/image/common/more_icon.png +0 -0
  85. package/src/assets/image/common/mute.png +0 -0
  86. package/src/assets/image/common/newFolder.png +0 -0
  87. package/src/assets/image/common/newTask.png +0 -0
  88. package/src/assets/image/common/nineOff.png +0 -0
  89. package/src/assets/image/common/nineOn.png +0 -0
  90. package/src/assets/image/common/none.png +0 -0
  91. package/src/assets/image/common/offline.png +0 -0
  92. package/src/assets/image/common/oneOff.png +0 -0
  93. package/src/assets/image/common/oneOn.png +0 -0
  94. package/src/assets/image/common/online.png +0 -0
  95. package/src/assets/image/common/output_off_small.png +0 -0
  96. package/src/assets/image/common/playOff.png +0 -0
  97. package/src/assets/image/common/playOn.png +0 -0
  98. package/src/assets/image/common/playStop.png +0 -0
  99. package/src/assets/image/common/preview_icon.png +0 -0
  100. package/src/assets/image/common/preview_meet_icon.png +0 -0
  101. package/src/assets/image/common/scan-map.png +0 -0
  102. package/src/assets/image/common/screen_blue.png +0 -0
  103. package/src/assets/image/common/screen_gray.png +0 -0
  104. package/src/assets/image/common/screen_white.png +0 -0
  105. package/src/assets/image/common/select_item_check.png +0 -0
  106. package/src/assets/image/common/select_item_checked.png +0 -0
  107. package/src/assets/image/common/selector.png +0 -0
  108. package/src/assets/image/common/selectorOn.png +0 -0
  109. package/src/assets/image/common/share_icon.png +0 -0
  110. package/src/assets/image/common/signal_good.png +0 -0
  111. package/src/assets/image/common/signal_poor.png +0 -0
  112. package/src/assets/image/common/sixteenOff.png +0 -0
  113. package/src/assets/image/common/sixteenOn.png +0 -0
  114. package/src/assets/image/common/slide-bth-expand.png +0 -0
  115. package/src/assets/image/common/slide_btn.png +0 -0
  116. package/src/assets/image/common/speak.png +0 -0
  117. package/src/assets/image/common/speakOn.png +0 -0
  118. package/src/assets/image/common/speaker-off.png +0 -0
  119. package/src/assets/image/common/speaker-on.png +0 -0
  120. package/src/assets/image/common/speaking.png +0 -0
  121. package/src/assets/image/common/speed-left.svg +5 -0
  122. package/src/assets/image/common/speed-right.svg +5 -0
  123. package/src/assets/image/common/tree_checked_icon.png +0 -0
  124. package/src/assets/image/common/tree_expand_icon.png +0 -0
  125. package/src/assets/image/common/tree_slide_icon.png +0 -0
  126. package/src/assets/image/common/tree_uncheck_icon.png +0 -0
  127. package/src/assets/image/common/unchosen_icon.png +0 -0
  128. package/src/assets/image/common/up.png +0 -0
  129. package/src/assets/image/common/volume.png +0 -0
  130. package/src/assets/image/common/warning.png +0 -0
  131. package/src/assets/image/screenBlue/a1.png +0 -0
  132. package/src/assets/image/screenBlue/a2.png +0 -0
  133. package/src/assets/image/screenBlue/a3.png +0 -0
  134. package/src/assets/image/screenBlue/a4.png +0 -0
  135. package/src/assets/image/screenBlue/a5.png +0 -0
  136. package/src/assets/image/screenBlue/a6.png +0 -0
  137. package/src/assets/image/screenBlue/add.png +0 -0
  138. package/src/assets/image/screenBlue/add_group_icon.png +0 -0
  139. package/src/assets/image/screenBlue/add_to_group_icon.png +0 -0
  140. package/src/assets/image/screenBlue/arrow_icon.png +0 -0
  141. package/src/assets/image/screenBlue/audio_level_icon.png +0 -0
  142. package/src/assets/image/screenBlue/b1.png +0 -0
  143. package/src/assets/image/screenBlue/b2.png +0 -0
  144. package/src/assets/image/screenBlue/b3.png +0 -0
  145. package/src/assets/image/screenBlue/b4.png +0 -0
  146. package/src/assets/image/screenBlue/b5.png +0 -0
  147. package/src/assets/image/screenBlue/b6.png +0 -0
  148. package/src/assets/image/screenBlue/bottom_footer_bg.png +0 -0
  149. package/src/assets/image/screenBlue/call_clear_icon.png +0 -0
  150. package/src/assets/image/screenBlue/call_duration_icon.png +0 -0
  151. package/src/assets/image/screenBlue/call_fullscreen_icon.png +0 -0
  152. package/src/assets/image/screenBlue/call_mini_icon.png +0 -0
  153. package/src/assets/image/screenBlue/call_video_icon.png +0 -0
  154. package/src/assets/image/screenBlue/call_voice_icon.png +0 -0
  155. package/src/assets/image/screenBlue/cam_on_small.png +0 -0
  156. package/src/assets/image/screenBlue/close_icon.png +0 -0
  157. package/src/assets/image/screenBlue/copy-icon.png +0 -0
  158. package/src/assets/image/screenBlue/custom_layout_drag_icon.png +0 -0
  159. package/src/assets/image/screenBlue/custom_layout_grid16_active_icon.png +0 -0
  160. package/src/assets/image/screenBlue/custom_layout_grid16_icon.png +0 -0
  161. package/src/assets/image/screenBlue/custom_layout_grid4_active_icon.png +0 -0
  162. package/src/assets/image/screenBlue/custom_layout_grid4_icon.png +0 -0
  163. package/src/assets/image/screenBlue/custom_layout_grid9_active_icon.png +0 -0
  164. package/src/assets/image/screenBlue/custom_layout_grid9_icon.png +0 -0
  165. package/src/assets/image/screenBlue/custom_layout_header_icon.png +0 -0
  166. package/src/assets/image/screenBlue/custom_layout_placeholder_bg.png +0 -0
  167. package/src/assets/image/screenBlue/custom_layout_refresh_icon.png +0 -0
  168. package/src/assets/image/screenBlue/custom_layout_rightside_active_icon.png +0 -0
  169. package/src/assets/image/screenBlue/custom_layout_rightside_icon.png +0 -0
  170. package/src/assets/image/screenBlue/custom_layout_ring_active_icon.png +0 -0
  171. package/src/assets/image/screenBlue/custom_layout_ring_icon.png +0 -0
  172. package/src/assets/image/screenBlue/custom_layout_topside_active_icon.png +0 -0
  173. package/src/assets/image/screenBlue/custom_layout_topside_icon.png +0 -0
  174. package/src/assets/image/screenBlue/date_picker_icon.png +0 -0
  175. package/src/assets/image/screenBlue/dialog_check_icon.png +0 -0
  176. package/src/assets/image/screenBlue/emoji-logo.png +0 -0
  177. package/src/assets/image/screenBlue/header_alert_icon.png +0 -0
  178. package/src/assets/image/screenBlue/manager_cam_on_icon.png +0 -0
  179. package/src/assets/image/screenBlue/manager_mic_on_icon.png +0 -0
  180. package/src/assets/image/screenBlue/manager_more_icon.png +0 -0
  181. package/src/assets/image/screenBlue/meeting_board_bg.png +0 -0
  182. package/src/assets/image/screenBlue/meeting_chat_emoji_icon.png +0 -0
  183. package/src/assets/image/screenBlue/meeting_chat_icon.png +0 -0
  184. package/src/assets/image/screenBlue/meeting_chat_image_icon.png +0 -0
  185. package/src/assets/image/screenBlue/meeting_chat_logo.png +0 -0
  186. package/src/assets/image/screenBlue/meeting_copy_icon.png +0 -0
  187. package/src/assets/image/screenBlue/meeting_invite_icon.png +0 -0
  188. package/src/assets/image/screenBlue/meeting_layout_icon.png +0 -0
  189. package/src/assets/image/screenBlue/meeting_member_icon.png +0 -0
  190. package/src/assets/image/screenBlue/meeting_mode_icon.png +0 -0
  191. package/src/assets/image/screenBlue/meeting_record_icon.png +0 -0
  192. package/src/assets/image/screenBlue/meeting_setting_icon.png +0 -0
  193. package/src/assets/image/screenBlue/meeting_share_icon.png +0 -0
  194. package/src/assets/image/screenBlue/meeting_slide_small_icon.png +0 -0
  195. package/src/assets/image/screenBlue/mic_on_small.png +0 -0
  196. package/src/assets/image/screenBlue/module_bg1.png +0 -0
  197. package/src/assets/image/screenBlue/module_bg2.png +0 -0
  198. package/src/assets/image/screenBlue/monitor/circle data.png +0 -0
  199. package/src/assets/image/screenBlue/monitor/circle.png +0 -0
  200. package/src/assets/image/screenBlue/output_on_small.png +0 -0
  201. package/src/assets/image/screenBlue/page_bg.png +0 -0
  202. package/src/assets/image/screenBlue/pic-logo.png +0 -0
  203. package/src/assets/image/screenBlue/preview_date_icon.png +0 -0
  204. package/src/assets/image/screenBlue/preview_empty_icon.png +0 -0
  205. package/src/assets/image/screenBlue/preview_more_icon.png +0 -0
  206. package/src/assets/image/screenBlue/receive_hang_off_icon.png +0 -0
  207. package/src/assets/image/screenBlue/receive_hang_on_icon.png +0 -0
  208. package/src/assets/image/screenBlue/receive_video_icon.png +0 -0
  209. package/src/assets/image/screenBlue/receive_voice_icon.png +0 -0
  210. package/src/assets/image/screenBlue/send-logo.png +0 -0
  211. package/src/assets/image/screenBlue/slide_menu_bg.png +0 -0
  212. package/src/assets/image/screenBlue/top_header_bg.png +0 -0
  213. package/src/assets/image/screenBlue/yq.png +0 -0
  214. package/src/assets/json/emoji.json +222 -0
  215. package/src/assets/style/base.scss +217 -0
  216. package/src/assets/style/elForm.scss +80 -0
  217. package/src/assets/style/font.scss +16 -0
  218. package/src/assets/style/index.scss +5 -0
  219. package/src/assets/style/math.scss +11 -0
  220. package/src/assets/style/mixin.scss +69 -0
  221. package/src/components/LiveCallBoard/LiveCallBoard.vue +237 -0
  222. package/src/components/LiveInviteReceive/LiveInviteReceive.vue +150 -0
  223. package/src/components/LiveMulti/LiveMulti.vue +303 -0
  224. package/src/components/LiveMultipleMeeting/LiveMultipleMeeting.vue +4469 -0
  225. package/src/components/LiveMultipleMeeting/style/index.scss +337 -0
  226. package/src/components/LivePoint/LivePoint.vue +372 -0
  227. package/src/components/LivePointMeeting/LivePointMeeting.vue +1134 -0
  228. package/src/components/LivePointMeeting/style/index.scss +202 -0
  229. package/src/components/MeetingReadyDialog/MeetingReadyDialog.vue +583 -0
  230. package/src/components/MiniumVideoDialog/MiniumVideoDialog.vue +449 -0
  231. package/src/components/other/LayoutPlaceholder.vue +184 -0
  232. package/src/components/other/addressBook.vue +1121 -0
  233. package/src/components/other/appointDialog.vue +208 -0
  234. package/src/components/other/callBoard.vue +191 -0
  235. package/src/components/other/chatArea.vue +727 -0
  236. package/src/components/other/customGroupDialog.vue +180 -0
  237. package/src/components/other/customLayout.vue +1112 -0
  238. package/src/components/other/editGroupDialog.vue +290 -0
  239. package/src/components/other/inviteNonContactDialog.vue +160 -0
  240. package/src/components/other/layoutSwitch.vue +183 -0
  241. package/src/components/other/leaveOptionDialog.vue +90 -0
  242. package/src/components/other/memberManage.vue +502 -0
  243. package/src/components/other/moreOptionDialog.vue +291 -0
  244. package/src/components/other/screenShareBoard.vue +121 -0
  245. package/src/components/other/selectDialog.vue +279 -0
  246. package/src/components/other/selectSpecialDialog.vue +234 -0
  247. package/src/components/other/settingDialog.vue +756 -0
  248. package/src/components/other/themeDialog.vue +180 -0
  249. package/src/components/other/updateNameDialog.vue +162 -0
  250. package/src/directive/clickOutside.js +58 -0
  251. package/src/directive/drag.js +165 -0
  252. package/src/directive/scale.js +22 -0
  253. package/src/directive/throttle.js +77 -0
  254. package/src/main.js +21 -0
  255. package/src/request/index.js +27 -0
  256. package/src/utils/api.js +82 -0
  257. package/src/utils/index.js +4 -0
  258. package/src/utils/livekit/live-client-esm-old.js +1 -0
  259. package/src/utils/livekit/live-client-esm.js +1 -0
  260. package/src/utils/message.js +24 -0
  261. package/src/utils/mitt.js +4 -0
  262. package/src/utils/tool.js +154 -0
@@ -0,0 +1,1112 @@
1
+ <template>
2
+ <div class="custom-layout">
3
+ <div class="custom-layout-header">
4
+ <div class="custom-layout-header-left">
5
+ <div class="custom-layout-header-left-icon"></div>
6
+ <div class="custom-layout-header-left-title">
7
+ <span>自定义布局</span>
8
+ </div>
9
+ </div>
10
+ <div class="close-icon" @click="closeCustomLayout"></div>
11
+ </div>
12
+ <div class="custom-layout-content">
13
+ <div class="custom-layout-simulate">
14
+ <div class="custom-layout-simulate-header">
15
+ <div class="custom-layout-simulate-header-options">
16
+ <div class="layout-list">
17
+ <div
18
+ :class="['layout-list-item', item]"
19
+ v-for="item of layoutList"
20
+ :key="item"
21
+ @click="handleLayoutChange(item)"
22
+ >
23
+ <div
24
+ :class="
25
+ ['layout-list-item-icon',
26
+ { 'layout-list-item-icon-active': activeLayout === item }
27
+ ]
28
+ "
29
+ ></div>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ <div class="custom-layout-simulate-header-options">
34
+ <div class="auto-fill">
35
+ <span>画面动态填充</span>
36
+ <el-switch v-model="autoFill" />
37
+ </div>
38
+ </div>
39
+ </div>
40
+ <div :class="['custom-layout-simulate-show', `layout-${activeLayout}`]">
41
+ <div class="layout-container">
42
+ <LayoutPlaceholder
43
+ v-for="index in placeholderCount"
44
+ :key="index"
45
+ :index="index"
46
+ :member="assignedMembers[index]"
47
+ :layout-type="activeLayout"
48
+ :is-hover="hoverPlaceholderIndex === index"
49
+ @clear="handleClearMember(index)"
50
+ />
51
+ </div>
52
+ </div>
53
+ <div class="custom-layout-simulate-footer">
54
+ <div class="btn btn-primary" v-throttle-click:500="handleReset">重置</div>
55
+ <div class="btn btn-primary" v-throttle-click:1000="handleApply">应用</div>
56
+ </div>
57
+ </div>
58
+ <div class="custom-layout-member">
59
+ <div class="custom-layout-member-header">
60
+ <div class="custom-layout-member-header-options">
61
+ <span>参会人员</span>
62
+ </div>
63
+ <div class="custom-layout-member-header-options">
64
+ <div class="refresh-icon" @click="refreshMemberList"></div>
65
+ </div>
66
+ </div>
67
+ <el-input v-model="searchVal" placeholder="请输入名称查询">
68
+ <template slot="suffix">
69
+ <div class="input-suffix-icon"></div>
70
+ </template>
71
+ </el-input>
72
+ <div class="custom-layout-member-list">
73
+ <div
74
+ class="custom-layout-member-list-item"
75
+ :class="{ 'is-dragging': isDragging && currentDragMember && currentDragMember.identity === item.identity }"
76
+ v-for="item of memberList"
77
+ :key="item.identity"
78
+ >
79
+ <div
80
+ class="custom-layout-member-list-item-drag-icon"
81
+ @mousedown="handleMouseDown(item, $event)"
82
+ @touchstart="handleTouchStart(item, $event)"
83
+ ></div>
84
+ <div class="custom-layout-member-list-item-group">
85
+ <div class="custom-layout-member-list-item-group-avatar"></div>
86
+ <div class="custom-layout-member-list-item-group-name">{{ item.name }}</div>
87
+ </div>
88
+ </div>
89
+ </div>
90
+
91
+ <!-- 拖拽时的浮动元素 -->
92
+ <div
93
+ v-if="isDragging && currentDragMember"
94
+ class="drag-ghost"
95
+ :style="dragGhostStyle"
96
+ >
97
+ <div class="drag-ghost-avatar"></div>
98
+ <div class="drag-ghost-name">{{ currentDragMember.name }}</div>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </template>
104
+
105
+ <script>
106
+ import LayoutPlaceholder from "./LayoutPlaceholder.vue";
107
+ import { ShowMessage } from "@/utils/index";
108
+
109
+ export default {
110
+ name: "CustomLayout",
111
+ components: {
112
+ LayoutPlaceholder
113
+ },
114
+ props: {
115
+ participantList: {
116
+ type: Array,
117
+ default: () => [],
118
+ },
119
+ meetingNum: {
120
+ type: String,
121
+ default: "",
122
+ },
123
+ },
124
+ data() {
125
+ return {
126
+ searchVal: "",
127
+ memberList: [],
128
+ autoFill: true,
129
+ layoutList: ["grid4", "grid9", "ring", "topSide", "rightSide"],
130
+ activeLayout: "grid4",
131
+ // 拖拽相关状态
132
+ isDragging: false,
133
+ dragStartTimer: null,
134
+ currentDragMember: null,
135
+ assignedMembers: {},
136
+ dragGhostStyle: {},
137
+ dragOffset: { x: 0, y: 0 },
138
+ hoverPlaceholderIndex: null,
139
+ currentMousePosition: { x: 0, y: 0 },
140
+ liveClient: null,
141
+ showMessage: new ShowMessage()
142
+ };
143
+ },
144
+ computed: {
145
+ participantNum() {
146
+ return this.participantList.length;
147
+ },
148
+ memberNum() {
149
+ return this.memberList.length;
150
+ },
151
+ // 计算不同布局下的占位元素数量
152
+ placeholderCount() {
153
+ return this.getLayoutPlaceholderCount(this.activeLayout);
154
+ }
155
+ },
156
+ watch: {
157
+ searchVal(newVal) {
158
+ this.getMemberList(newVal);
159
+ },
160
+ participantNum(newVal) {
161
+ this.getMemberList();
162
+ }
163
+ },
164
+ mounted() {
165
+ this.getMemberList();
166
+ this.liveClient = window["liveClient"];
167
+ },
168
+ beforeDestroy() {
169
+ this.liveClient = null;
170
+ },
171
+ methods: {
172
+ // 获取指定布局的占位符数量
173
+ getLayoutPlaceholderCount(layout) {
174
+ const layoutCountMap = {
175
+ grid4: 4,
176
+ grid9: 9,
177
+ ring: 8,
178
+ topSide: 4,
179
+ rightSide: 4
180
+ };
181
+ return layoutCountMap[layout] || 4;
182
+ },
183
+
184
+ // 长按开始拖拽 - 鼠标事件
185
+ handleMouseDown(member, event) {
186
+ event.preventDefault();
187
+
188
+ // 使用固定偏移量,让浮动元素在鼠标右下角
189
+ this.dragOffset = { x: 10, y: 10 };
190
+
191
+ // 设置初始鼠标位置
192
+ this.currentMousePosition = { x: event.clientX, y: event.clientY };
193
+
194
+ this.startDragTimer(member);
195
+
196
+ const startX = event.clientX;
197
+ const startY = event.clientY;
198
+
199
+ const handleMouseMove = (e) => {
200
+ // 保存鼠标实际位置
201
+ this.currentMousePosition = { x: e.clientX, y: e.clientY };
202
+
203
+ // 如果鼠标移动超过5px,清除长按定时器
204
+ const deltaX = Math.abs(e.clientX - startX);
205
+ const deltaY = Math.abs(e.clientY - startY);
206
+ if ((deltaX > 5 || deltaY > 5) && !this.isDragging) {
207
+ this.clearDragTimer();
208
+ }
209
+
210
+ if (this.isDragging) {
211
+ this.updateDragPosition(e.clientX, e.clientY);
212
+ }
213
+ };
214
+
215
+ const handleMouseUp = () => {
216
+ this.clearDragTimer();
217
+ this.endDrag();
218
+ document.removeEventListener('mousemove', handleMouseMove);
219
+ document.removeEventListener('mouseup', handleMouseUp);
220
+ };
221
+
222
+ document.addEventListener('mousemove', handleMouseMove);
223
+ document.addEventListener('mouseup', handleMouseUp);
224
+ },
225
+
226
+ // 长按开始拖拽 - 触摸事件
227
+ handleTouchStart(member, event) {
228
+ event.preventDefault();
229
+ const touch = event.touches[0];
230
+
231
+ // 使用固定偏移量,让浮动元素在触摸点右下角
232
+ this.dragOffset = { x: 10, y: 10 };
233
+
234
+ // 设置初始触摸位置
235
+ this.currentMousePosition = { x: touch.clientX, y: touch.clientY };
236
+
237
+ this.startDragTimer(member);
238
+
239
+ const startX = touch.clientX;
240
+ const startY = touch.clientY;
241
+
242
+ const handleTouchMove = (e) => {
243
+ const touch = e.touches[0];
244
+ // 保存触摸实际位置
245
+ this.currentMousePosition = { x: touch.clientX, y: touch.clientY };
246
+
247
+ // 如果触摸移动超过5px,清除长按定时器
248
+ const deltaX = Math.abs(touch.clientX - startX);
249
+ const deltaY = Math.abs(touch.clientY - startY);
250
+ if ((deltaX > 5 || deltaY > 5) && !this.isDragging) {
251
+ this.clearDragTimer();
252
+ }
253
+
254
+ if (this.isDragging) {
255
+ this.updateDragPosition(touch.clientX, touch.clientY);
256
+ }
257
+ };
258
+
259
+ const handleTouchEnd = () => {
260
+ this.clearDragTimer();
261
+ this.endDrag();
262
+ document.removeEventListener('touchmove', handleTouchMove);
263
+ document.removeEventListener('touchend', handleTouchEnd);
264
+ };
265
+
266
+ document.addEventListener('touchmove', handleTouchMove);
267
+ document.addEventListener('touchend', handleTouchEnd);
268
+ },
269
+
270
+ // 开始长按计时器
271
+ startDragTimer(member) {
272
+ this.clearDragTimer();
273
+ this.dragStartTimer = setTimeout(() => {
274
+ this.startDrag(member);
275
+ }, 500);
276
+ },
277
+
278
+ // 清除长按计时器
279
+ clearDragTimer() {
280
+ if (this.dragStartTimer) {
281
+ clearTimeout(this.dragStartTimer);
282
+ this.dragStartTimer = null;
283
+ }
284
+ },
285
+
286
+ // 开始拖拽
287
+ startDrag(member) {
288
+ this.isDragging = true;
289
+ this.currentDragMember = member;
290
+
291
+ // 设置初始浮动元素位置
292
+ const { x, y } = this.currentMousePosition;
293
+ this.updateDragPosition(x, y);
294
+
295
+ console.log('开始拖拽:', member.name);
296
+ },
297
+
298
+ // 更新拖拽位置
299
+ updateDragPosition(clientX, clientY) {
300
+ this.dragGhostStyle = {
301
+ position: 'fixed',
302
+ left: `${clientX + this.dragOffset.x}px`,
303
+ top: `${clientY + this.dragOffset.y}px`,
304
+ pointerEvents: 'none',
305
+ zIndex: 9999
306
+ };
307
+
308
+ // 检测是否悬停在占位符上
309
+ this.checkHoverTarget(clientX, clientY);
310
+ },
311
+
312
+ // 检测悬停目标
313
+ checkHoverTarget(clientX, clientY) {
314
+ // 使用鼠标的实际位置来检测下方元素
315
+ const elementBelow = document.elementFromPoint(clientX, clientY);
316
+ const placeholder = elementBelow?.closest('.layout-placeholder');
317
+
318
+ if (placeholder) {
319
+ const index = Array.from(placeholder.parentNode.children).indexOf(placeholder) + 1;
320
+ if (index > 0 && index <= this.placeholderCount) {
321
+ this.hoverPlaceholderIndex = index;
322
+ } else {
323
+ this.hoverPlaceholderIndex = null;
324
+ }
325
+ } else {
326
+ this.hoverPlaceholderIndex = null;
327
+ }
328
+ },
329
+
330
+ // 结束拖拽
331
+ endDrag() {
332
+ if (this.isDragging) {
333
+ // 检查是否拖拽到了占位符上
334
+ this.checkDropTarget();
335
+
336
+ this.isDragging = false;
337
+ this.currentDragMember = null;
338
+ this.dragGhostStyle = {};
339
+ this.hoverPlaceholderIndex = null;
340
+ this.currentMousePosition = { x: 0, y: 0 };
341
+ }
342
+ },
343
+
344
+ // 检查拖拽目标
345
+ checkDropTarget() {
346
+ // 使用保存的鼠标位置来检测拖拽目标
347
+ const { x, y } = this.currentMousePosition;
348
+
349
+ const elementBelow = document.elementFromPoint(x, y);
350
+ const placeholder = elementBelow?.closest('.layout-placeholder');
351
+
352
+ if (placeholder) {
353
+ const index = Array.from(placeholder.parentNode.children).indexOf(placeholder) + 1;
354
+ if (index > 0 && index <= this.placeholderCount) {
355
+ this.handleDrop(index, this.currentDragMember);
356
+ }
357
+ }
358
+ },
359
+
360
+ // 处理拖拽到占位符
361
+ handleDrop(index, draggedMember) {
362
+ if (draggedMember) {
363
+ // 1. 清除该成员在其他位置的显示
364
+ this.clearMemberFromOtherPositions(draggedMember.identity);
365
+
366
+ // 2. 将成员分配到新位置(直接覆盖原有成员)
367
+ const previousMember = this.assignedMembers[index];
368
+ this.assignedMembers[index] = draggedMember;
369
+
370
+ if (previousMember) {
371
+ console.log('位置替换:', `位置 ${index} 的 ${previousMember.name} 被 ${draggedMember.name} 替换`);
372
+ } else {
373
+ console.log('拖拽成功:', draggedMember.name, '已分配到位置', index);
374
+ }
375
+
376
+ // 可以在这里添加成功提示
377
+ // ElMessage.success(`${draggedMember.name} 已分配到位置 ${index}`);
378
+ }
379
+ },
380
+
381
+ // 清除指定成员在其他位置的显示
382
+ clearMemberFromOtherPositions(memberIdentity) {
383
+ const clearedPositions = [];
384
+ for (const key in this.assignedMembers) {
385
+ if (this.assignedMembers[key]?.identity === memberIdentity) {
386
+ clearedPositions.push(key);
387
+ delete this.assignedMembers[key];
388
+ }
389
+ }
390
+ if (clearedPositions.length > 0) {
391
+ console.log('清除重复显示:', `成员在位置 [${clearedPositions.join(', ')}] 的显示已清除`);
392
+ }
393
+ },
394
+
395
+ // 清空占位符成员
396
+ handleClearMember(index) {
397
+ const member = this.assignedMembers[index];
398
+ if (member) {
399
+ delete this.assignedMembers[index];
400
+ console.log('手动清空:', `位置 ${index} 的 ${member.name} 已清空`);
401
+ }
402
+ },
403
+
404
+ handleLayoutChange(layout) {
405
+ const oldLayout = this.activeLayout;
406
+ this.activeLayout = layout;
407
+
408
+ // 获取新布局的占位符数量
409
+ const newPlaceholderCount = this.getLayoutPlaceholderCount(layout);
410
+
411
+ // 特殊处理:切换到topSide布局时的成员重排
412
+ if (layout === 'topSide' && oldLayout !== 'topSide') {
413
+ this.handleTopSideLayoutTransition();
414
+ }
415
+
416
+ // 特殊处理:切换到rightSide布局时的成员重排
417
+ if (layout === 'rightSide' && oldLayout !== 'rightSide') {
418
+ this.handleRightSideLayoutTransition();
419
+ }
420
+
421
+ // 特殊处理:切换到ring布局时的成员重排
422
+ if (layout === 'ring' && oldLayout !== 'ring') {
423
+ this.handleRingLayoutTransition();
424
+ }
425
+
426
+ // 清空超出新布局占位符数量的成员
427
+ const clearedMembers = [];
428
+ for (const key in this.assignedMembers) {
429
+ const index = parseInt(key);
430
+ if (index > newPlaceholderCount) {
431
+ const member = this.assignedMembers[key];
432
+ clearedMembers.push(`${member.name}(位置${index})`);
433
+ delete this.assignedMembers[key];
434
+ }
435
+ }
436
+
437
+ if (clearedMembers.length > 0) {
438
+ console.log(`布局切换:清空超出范围的成员 [${clearedMembers.join(', ')}]`);
439
+ }
440
+
441
+ console.log(`布局切换:${oldLayout} -> ${layout},保留了 ${Object.keys(this.assignedMembers).length} 个已分配成员`);
442
+ },
443
+
444
+ // 处理切换到topSide布局时的成员重排
445
+ handleTopSideLayoutTransition() {
446
+ // 在topSide布局中,位置1是焦点位置,位置2-4是上排小窗口
447
+ // 如果位置1已有成员,则无需调整;如果位置1没有成员但其他位置有,可以考虑智能重排
448
+
449
+ if (!this.assignedMembers[1]) {
450
+ // 如果焦点位置(位置1)为空,尝试将最重要的成员移到焦点位置
451
+ for (let i = 2; i <= 4; i++) {
452
+ if (this.assignedMembers[i]) {
453
+ console.log(`topSide布局优化:将位置${i}的${this.assignedMembers[i].name}移动到焦点位置`);
454
+ this.assignedMembers[1] = this.assignedMembers[i];
455
+ delete this.assignedMembers[i];
456
+ break;
457
+ }
458
+ }
459
+ }
460
+
461
+ console.log('topSide布局转换完成:位置1为焦点位置,位置2-4为上排小窗口');
462
+ },
463
+
464
+ // 处理切换到rightSide布局时的成员重排
465
+ handleRightSideLayoutTransition() {
466
+ // 在rightSide布局中,位置1是焦点位置,位置2-4是右侧纵向排列
467
+ // 如果位置1已有成员,则无需调整;如果位置1没有成员但其他位置有,可以考虑智能重排
468
+
469
+ if (!this.assignedMembers[1]) {
470
+ // 如果焦点位置(位置1)为空,尝试将最重要的成员移到焦点位置
471
+ for (let i = 2; i <= 4; i++) {
472
+ if (this.assignedMembers[i]) {
473
+ console.log(`rightSide布局优化:将位置${i}的${this.assignedMembers[i].name}移动到焦点位置`);
474
+ this.assignedMembers[1] = this.assignedMembers[i];
475
+ delete this.assignedMembers[i];
476
+ break;
477
+ }
478
+ }
479
+ }
480
+
481
+ console.log('rightSide布局转换完成:位置1为焦点位置,位置2-4为右侧纵向排列');
482
+ },
483
+
484
+ // 处理切换到ring布局时的成员重排
485
+ handleRingLayoutTransition() {
486
+ // 在ring布局中,位置1是焦点位置(左上角大窗口),位置2-8是环状排列
487
+ // 如果位置1已有成员,则无需调整;如果位置1没有成员但其他位置有,按优先级智能重排
488
+
489
+ if (!this.assignedMembers[1]) {
490
+ // 如果焦点位置(位置1)为空,按优先级顺序选择成员移到焦点位置
491
+ // 优先级:位置2 > 位置3 > 位置4 > 位置5 > 位置6 > 位置7 > 位置8
492
+ // 这样可以确保最重要或最早分配的成员优先显示在焦点位置
493
+
494
+ const priorityOrder = [2, 3, 4, 5, 6, 7, 8];
495
+ let movedMember = null;
496
+ let fromPosition = null;
497
+
498
+ for (const position of priorityOrder) {
499
+ if (this.assignedMembers[position]) {
500
+ movedMember = this.assignedMembers[position];
501
+ fromPosition = position;
502
+ break;
503
+ }
504
+ }
505
+
506
+ if (movedMember && fromPosition) {
507
+ console.log(`ring布局优化:将位置${fromPosition}的${movedMember.name}移动到焦点位置`);
508
+ this.assignedMembers[1] = movedMember;
509
+ delete this.assignedMembers[fromPosition];
510
+
511
+ // 启用环状分布优化,让剩余成员排列更加平衡
512
+ this.optimizeRingMemberDistribution();
513
+ } else {
514
+ console.log('ring布局:没有成员需要移动到焦点位置');
515
+ }
516
+ } else {
517
+ console.log(`ring布局:焦点位置已有成员 ${this.assignedMembers[1].name},无需重排`);
518
+ }
519
+
520
+ console.log('ring布局转换完成:位置1为焦点位置,位置2-8为环状排列');
521
+ },
522
+
523
+ // 优化ring布局的成员分布
524
+ optimizeRingMemberDistribution() {
525
+ // 收集所有非焦点位置的成员
526
+ const ringMembers = [];
527
+ for (let i = 2; i <= 8; i++) {
528
+ if (this.assignedMembers[i]) {
529
+ ringMembers.push({
530
+ member: this.assignedMembers[i],
531
+ originalPosition: i
532
+ });
533
+ delete this.assignedMembers[i];
534
+ }
535
+ }
536
+
537
+ if (ringMembers.length === 0) {
538
+ console.log('ring布局优化:没有环状位置的成员需要重新排列');
539
+ return;
540
+ }
541
+
542
+ // 重新分配到环状位置,优先使用前面的位置以保持视觉平衡
543
+ // 这样可以避免成员分布过于分散,让界面看起来更整洁
544
+ const ringPositions = [2, 3, 4, 5, 6, 7, 8];
545
+ let optimizedCount = 0;
546
+
547
+ for (let i = 0; i < ringMembers.length && i < ringPositions.length; i++) {
548
+ const newPosition = ringPositions[i];
549
+ const memberInfo = ringMembers[i];
550
+
551
+ this.assignedMembers[newPosition] = memberInfo.member;
552
+
553
+ if (memberInfo.originalPosition !== newPosition) {
554
+ optimizedCount++;
555
+ }
556
+ }
557
+
558
+ if (optimizedCount > 0) {
559
+ console.log(`ring布局优化:重新排列了${optimizedCount}个成员,使环状分布更加平衡`);
560
+ } else {
561
+ console.log('ring布局优化:成员分布已经是最优状态,无需调整');
562
+ }
563
+ },
564
+
565
+ handleReset() {
566
+ this.activeLayout = "grid4";
567
+ this.autoFill = true;
568
+ this.assignedMembers = {};
569
+ },
570
+
571
+ handleApply() {
572
+ // 获取当前布局的占位符数量
573
+ const currentPlaceholderCount = this.getLayoutPlaceholderCount(this.activeLayout);
574
+
575
+ // 生成布局映射数组
576
+ const layoutArray = this.generateLayoutArray(currentPlaceholderCount);
577
+
578
+ // 验证布局数组
579
+ const validation = this.validateLayoutArray(layoutArray);
580
+
581
+ if (!validation.isValid) {
582
+ console.error('布局验证失败:', validation.errors);
583
+ // 可以在这里显示错误提示给用户
584
+ // ElMessage.error('布局配置有误,请检查后重试');
585
+ return null;
586
+ }
587
+
588
+ if (validation.warnings.length > 0) {
589
+ console.warn('布局验证警告:', validation.warnings);
590
+ // 可以在这里显示警告提示给用户
591
+ // ElMessage.warning('布局配置存在一些问题,建议检查');
592
+ }
593
+
594
+ // 这里可以发送应用布局的事件或API调用
595
+ console.log('应用布局成功:', {
596
+ layout: this.activeLayout,
597
+ autoFill: this.autoFill,
598
+ placeholderCount: currentPlaceholderCount,
599
+ layoutArray: layoutArray,
600
+ assignedMembers: this.assignedMembers,
601
+ validation: validation
602
+ });
603
+
604
+ const handleCurrentLayout = () => {
605
+ let layout = ""
606
+ switch(this.activeLayout) {
607
+ case "grid4":
608
+ layout = "four_grids"
609
+ break;
610
+ case "grid9":
611
+ layout = "nine_grids"
612
+ break;
613
+ case "ring":
614
+ layout = "custom1"
615
+ break;
616
+ case "topSide":
617
+ layout = "custom2"
618
+ break;
619
+ case "rightSide":
620
+ layout = "custom3"
621
+ break;
622
+ default:
623
+ layout = "four_grids"
624
+ break;
625
+ }
626
+ return layout
627
+ };
628
+
629
+ this.liveClient.modifyVolteLayout({
630
+ roomNum: this.meetingNum,
631
+ layout: handleCurrentLayout(),
632
+ identitys: layoutArray.map(item => item && item?.identity ? item.identity : ""),
633
+ autoFill: this.autoFill
634
+ }).then(res => {
635
+ if(res.code == 200) {
636
+ this.showMessage.success("volte融屏应用布局成功")
637
+ this.closeCustomLayout()
638
+ } else {
639
+ this.showMessage.error(res?.msg || "volte融屏应用布局失败")
640
+ }
641
+ }).catch(err => {
642
+ this.showMessage.error(err?.message || "volte融屏应用布局失败")
643
+ });
644
+ },
645
+
646
+ // 生成布局映射数组
647
+ generateLayoutArray(placeholderCount) {
648
+ const layoutArray = [];
649
+
650
+ // 遍历每个位置,构建数组
651
+ for (let i = 1; i <= placeholderCount; i++) {
652
+ const member = this.assignedMembers[i];
653
+
654
+ if (member) {
655
+ // 如果该位置有与会者,添加成员信息
656
+ layoutArray.push(member);
657
+ } else {
658
+ // 如果该位置是空占位符,添加空字符串
659
+ layoutArray.push('');
660
+ }
661
+ }
662
+
663
+ // 输出详细信息
664
+ const statistics = this.getLayoutArrayStatistics(layoutArray);
665
+ console.log('生成布局数组:', {
666
+ layout: this.activeLayout,
667
+ placeholderCount: placeholderCount,
668
+ ...statistics,
669
+ array: layoutArray
670
+ });
671
+
672
+ // 输出格式化的数组显示
673
+ console.log('布局数组格式化显示:', this.formatLayoutArrayDisplay(layoutArray));
674
+
675
+ return layoutArray;
676
+ },
677
+
678
+ // 获取布局数组统计信息
679
+ getLayoutArrayStatistics(layoutArray) {
680
+ const memberCount = layoutArray.filter(item => item !== '').length;
681
+ const emptyCount = layoutArray.filter(item => item === '').length;
682
+ const memberNames = layoutArray
683
+ .filter(item => item !== '')
684
+ .map(member => member.name);
685
+
686
+ return {
687
+ arrayLength: layoutArray.length,
688
+ memberCount: memberCount,
689
+ emptyCount: emptyCount,
690
+ memberNames: memberNames,
691
+ utilizationRate: Math.round((memberCount / layoutArray.length) * 100) + '%'
692
+ };
693
+ },
694
+
695
+ // 格式化布局数组显示
696
+ formatLayoutArrayDisplay(layoutArray) {
697
+ return layoutArray.map((item, index) => {
698
+ const position = index + 1;
699
+ if (item === '') {
700
+ return `位置${position}: [空]`;
701
+ } else {
702
+ return `位置${position}: ${item.name}`;
703
+ }
704
+ }).join('\n');
705
+ },
706
+
707
+ // 验证布局数组
708
+ validateLayoutArray(layoutArray) {
709
+ const validation = {
710
+ isValid: true,
711
+ errors: [],
712
+ warnings: []
713
+ };
714
+
715
+ // 检查数组长度
716
+ const expectedLength = this.getLayoutPlaceholderCount(this.activeLayout);
717
+ if (layoutArray.length !== expectedLength) {
718
+ validation.isValid = false;
719
+ validation.errors.push(`数组长度不匹配:期望${expectedLength},实际${layoutArray.length}`);
720
+ }
721
+
722
+ // 检查重复成员
723
+ const members = layoutArray.filter(item => item !== '');
724
+ const memberIds = members.map(member => member.identity);
725
+ const uniqueIds = [...new Set(memberIds)];
726
+
727
+ if (memberIds.length !== uniqueIds.length) {
728
+ validation.isValid = false;
729
+ validation.errors.push('检测到重复的成员分配');
730
+ }
731
+
732
+ // 检查成员利用率
733
+ const utilizationRate = (members.length / layoutArray.length) * 100;
734
+ if (utilizationRate < 50) {
735
+ validation.warnings.push(`成员利用率较低:${Math.round(utilizationRate)}%`);
736
+ }
737
+
738
+ return validation;
739
+ },
740
+
741
+ getMemberList(val) {
742
+ this.memberList = this.participantList.filter((item) =>
743
+ val ? item.name.includes(val) : item.name.includes(this.searchVal)
744
+ );
745
+ },
746
+
747
+ refreshMemberList() {
748
+ this.getMemberList();
749
+ },
750
+
751
+ closeCustomLayout() {
752
+ this.$emit("closeCustomLayout");
753
+ }
754
+ }
755
+ };
756
+ </script>
757
+
758
+ <style lang="scss" scoped>
759
+ @import "../../assets/style/mixin.scss";
760
+ .custom-layout {
761
+ width: 820px;
762
+ height: 100%;
763
+ position: absolute;
764
+ right: 0;
765
+ top: 0;
766
+ z-index: 2000;
767
+ overflow: hidden;
768
+ color: var(--theme-font-color);
769
+ background: var(--dialog-bg);
770
+ border: 1px solid var(--dialog-border-color);
771
+ padding: 0 18px;
772
+ &-header {
773
+ width: 100%;
774
+ height: 62px;
775
+ display: flex;
776
+ align-items: center;
777
+ justify-content: space-between;
778
+ padding-top: 14px;
779
+ border-bottom: 1px solid var(--dialog-border-color);
780
+ &-left {
781
+ display: flex;
782
+ align-items: center;
783
+ &-icon {
784
+ width: 24px;
785
+ height: 24px;
786
+ background: var(--custom-layout-header-icon) no-repeat center / 100% 100%;
787
+ margin-right: 12px;
788
+ }
789
+ &-title {
790
+ font-weight: 400;
791
+ font-size: 18px;
792
+ font-family: var(--title-font);
793
+ }
794
+ }
795
+ .close-icon {
796
+ width: 17px;
797
+ height: 17px;
798
+ background: var(--close-icon) no-repeat center / 100% 100%;
799
+ cursor: pointer;
800
+ }
801
+ }
802
+ &-content {
803
+ width: 100%;
804
+ height: calc(100% - 62px);
805
+ display: flex;
806
+ align-items: flex-start;
807
+ flex-wrap: nowrap;
808
+ .custom-layout-simulate {
809
+ width: 548px;
810
+ flex: 0 0 auto;
811
+ height: 100%;
812
+ &-header {
813
+ width: 100%;
814
+ height: 58px;
815
+ display: flex;
816
+ align-items: center;
817
+ justify-content: space-between;
818
+ &-options {
819
+ .layout-list {
820
+ display: flex;
821
+ align-items: center;
822
+ &-item {
823
+ width: 29px;
824
+ height: 22px;
825
+ cursor: pointer;
826
+ &-icon {
827
+ width: 100%;
828
+ height: 100%;
829
+ }
830
+ &:not(:last-child) {
831
+ margin-right: 17px;
832
+ }
833
+ }
834
+ .grid4 {
835
+ .layout-list-item-icon {
836
+ background: var(--custom-layout-grid4-icon) no-repeat center / 100% 100%;
837
+ }
838
+ .layout-list-item-icon-active {
839
+ background: var(--custom-layout-grid4-active-icon) no-repeat center / 100% 100%;
840
+ }
841
+ }
842
+ .grid9 {
843
+ .layout-list-item-icon {
844
+ background: var(--custom-layout-grid9-icon) no-repeat center / 100% 100%;
845
+ }
846
+ .layout-list-item-icon-active {
847
+ background: var(--custom-layout-grid9-active-icon) no-repeat center / 100% 100%;
848
+ }
849
+ }
850
+ .grid16 {
851
+ .layout-list-item-icon {
852
+ background: var(--custom-layout-grid16-icon) no-repeat center / 100% 100%;
853
+ }
854
+ .layout-list-item-icon-active {
855
+ background: var(--custom-layout-grid16-active-icon) no-repeat center / 100% 100%;
856
+ }
857
+ }
858
+ .ring {
859
+ .layout-list-item-icon {
860
+ background: var(--custom-layout-ring-icon) no-repeat center / 100% 100%;
861
+ }
862
+ .layout-list-item-icon-active {
863
+ background: var(--custom-layout-ring-active-icon) no-repeat center / 100% 100%;
864
+ }
865
+ }
866
+ .topSide {
867
+ .layout-list-item-icon {
868
+ background: var(--custom-layout-topside-icon) no-repeat center / 100% 100%;
869
+ }
870
+ .layout-list-item-icon-active {
871
+ background: var(--custom-layout-topside-active-icon) no-repeat center / 100% 100%;
872
+ }
873
+ }
874
+ .rightSide {
875
+ .layout-list-item-icon {
876
+ background: var(--custom-layout-rightside-icon) no-repeat center / 100% 100%;
877
+ }
878
+ .layout-list-item-icon-active {
879
+ background: var(--custom-layout-rightside-active-icon) no-repeat center / 100% 100%;
880
+ }
881
+ }
882
+ }
883
+ .auto-fill {
884
+ display: flex;
885
+ align-items: center;
886
+ justify-content: flex-end;
887
+ span {
888
+ font-weight: 400;
889
+ font-size: 12px;
890
+ font-family: var(--main-font);
891
+ margin-right: 6px;
892
+ white-space: nowrap;
893
+ flex: 0 0 auto;
894
+ }
895
+ :deep(.el-switch) {
896
+ @include switch;
897
+ &.is-checked {
898
+ .el-switch__core {
899
+ background: var(--switch-on-color);
900
+ }
901
+ }
902
+ }
903
+ }
904
+ }
905
+ }
906
+ &-show {
907
+ width: 100%;
908
+ height: calc(100% - 139px);
909
+ background: var(--custom-layout-show-bg) no-repeat center / 100% 100%;
910
+ display: flex;
911
+ align-items: center;
912
+ justify-content: center;
913
+ padding: 16px;
914
+
915
+ .layout-container {
916
+ width: 100%;
917
+ max-width: 100%;
918
+ aspect-ratio: 16 / 9;
919
+ display: grid;
920
+ gap: 8px;
921
+ }
922
+
923
+ // 4宫格布局 - 2x2网格
924
+ &.layout-grid4 .layout-container {
925
+ grid-template-columns: 1fr 1fr;
926
+ grid-template-rows: 1fr 1fr;
927
+ }
928
+
929
+ // 9宫格布局 - 3x3网格
930
+ &.layout-grid9 .layout-container {
931
+ grid-template-columns: repeat(3, 1fr);
932
+ grid-template-rows: repeat(3, 1fr);
933
+ }
934
+
935
+ // 环状布局 - 左上角大窗口 + 右侧纵向 + 下侧横向 + 右下角
936
+ &.layout-ring .layout-container {
937
+ grid-template-columns: repeat(4, 1fr);
938
+ grid-template-rows: repeat(4, 1fr);
939
+ grid-template-areas:
940
+ "main main main slot1"
941
+ "main main main slot2"
942
+ "main main main slot3"
943
+ "slot4 slot5 slot6 slot7";
944
+ }
945
+
946
+ // 下焦点布局 - 上排3个小窗口,下方1个大窗口
947
+ &.layout-topSide .layout-container {
948
+ grid-template-columns: repeat(3, 1fr);
949
+ grid-template-rows: 1fr 2fr;
950
+ grid-template-areas:
951
+ "slot2 slot3 slot4"
952
+ "main main main";
953
+ }
954
+
955
+ // 左焦点布局 - 左侧1个大窗口,右侧3个纵向排列
956
+ &.layout-rightSide .layout-container {
957
+ grid-template-columns: 2fr 1fr;
958
+ grid-template-rows: repeat(3, 1fr);
959
+ grid-template-areas:
960
+ "main slot2"
961
+ "main slot3"
962
+ "main slot4";
963
+ }
964
+ }
965
+ &-footer {
966
+ width: 100%;
967
+ height: 81px;
968
+ display: flex;
969
+ padding-top: 20px;
970
+ align-items: flex-start;
971
+ justify-content: center;
972
+ .btn {
973
+ width: 80px;
974
+ height: 36px;
975
+ @include button;
976
+ &:not(:last-child) {
977
+ margin-right: 12px;
978
+ }
979
+ &-primary {
980
+ @include btnPrimary;
981
+ }
982
+ }
983
+ }
984
+ }
985
+ .custom-layout-member {
986
+ margin-left: 16px;
987
+ flex: 1;
988
+ height: 100%;
989
+ display: flex;
990
+ flex-direction: column;
991
+ &-header {
992
+ width: 100%;
993
+ height: 58px;
994
+ display: flex;
995
+ align-items: center;
996
+ justify-content: space-between;
997
+ &-options {
998
+ font-weight: 400;
999
+ font-size: 18px;
1000
+ font-family: var(--title-font);
1001
+ .refresh-icon {
1002
+ width: 16px;
1003
+ height: 16px;
1004
+ background: var(--custom-layout-refresh-icon) no-repeat center / 100% 100%;
1005
+ cursor: pointer;
1006
+ }
1007
+ }
1008
+ }
1009
+ :deep(.el-input) {
1010
+ width: 100%;
1011
+ height: 42px;
1012
+ margin-bottom: 16px;
1013
+ @include elInput;
1014
+ .input-suffix-icon {
1015
+ width: 20px;
1016
+ height: 20px;
1017
+ background: url("../../assets/image/common/input_search_icon.png") no-repeat center / 100% 100%;
1018
+ }
1019
+ }
1020
+ &-list {
1021
+ width: 100%;
1022
+ height: calc(100% - 120px);
1023
+ overflow-y: auto;
1024
+ &-item {
1025
+ width: 100%;
1026
+ height: 32px;
1027
+ display: flex;
1028
+ align-items: center;
1029
+ transition: all 0.2s ease;
1030
+
1031
+ &.is-dragging {
1032
+ opacity: 0.5;
1033
+ background: rgba(255, 255, 255, 0.1);
1034
+ border-radius: 4px;
1035
+ transform: scale(0.95);
1036
+ }
1037
+
1038
+ &-drag-icon {
1039
+ width: 16px;
1040
+ height: 16px;
1041
+ background: var(--custom-layout-drag-icon) no-repeat center / 100% 100%;
1042
+ cursor: pointer;
1043
+ margin-right: 12px;
1044
+ transition: opacity 0.2s ease;
1045
+
1046
+ &:hover {
1047
+ opacity: 0.8;
1048
+ }
1049
+ }
1050
+ &-group {
1051
+ display: flex;
1052
+ align-items: center;
1053
+ &-avatar {
1054
+ width: 32px;
1055
+ height: 32px;
1056
+ background: var(--custom-layout-user-avatar) no-repeat center / 100% 100%;
1057
+ margin-right: 12px;
1058
+ }
1059
+ &-name {
1060
+ font-weight: 400;
1061
+ font-size: 16px;
1062
+ font-family: var(--main-font);
1063
+ white-space: nowrap;
1064
+ overflow: hidden;
1065
+ text-overflow: ellipsis;
1066
+ max-width: 200px;
1067
+ width: fit-content;
1068
+ }
1069
+ }
1070
+ &:not(:last-child) {
1071
+ margin-bottom: 12px;
1072
+ }
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ // 拖拽时的浮动元素样式
1080
+ .drag-ghost {
1081
+ background: var(--meeting-participant-bg);
1082
+ border: 2px solid var(--theme-color);
1083
+ border-radius: 6px;
1084
+ padding: 6px 10px;
1085
+ display: flex;
1086
+ align-items: center;
1087
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
1088
+ opacity: 0.9;
1089
+ pointer-events: none;
1090
+ user-select: none;
1091
+ max-width: 200px;
1092
+
1093
+ &-avatar {
1094
+ width: 20px;
1095
+ height: 20px;
1096
+ background: var(--custom-layout-user-avatar) no-repeat center / cover;
1097
+ border-radius: 50%;
1098
+ margin-right: 6px;
1099
+ flex-shrink: 0;
1100
+ }
1101
+
1102
+ &-name {
1103
+ color: var(--theme-font-color);
1104
+ font-size: 11px;
1105
+ font-weight: 400;
1106
+ font-family: var(--main-font);
1107
+ white-space: nowrap;
1108
+ overflow: hidden;
1109
+ text-overflow: ellipsis;
1110
+ }
1111
+ }
1112
+ </style>