join-model.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import { defineComponent } from "vue";
  2. import styles from "./index.module.less";
  3. import { ElButton } from "element-plus";
  4. import event, {
  5. LIVE_EVENT_MESSAGE,
  6. } from "/src/components/live-broadcast/event";
  7. import { state } from "/src/state";
  8. import dayjs from "dayjs";
  9. import Empty from "/src/components/empty";
  10. import runtime, * as RuntimeUtils from "/src/components/live-broadcast/runtime";
  11. import runtimeModel, * as RuntimeModelUtils from "/src/components/live-message/model/runtime";
  12. import request from "/src/helpers/request";
  13. export default defineComponent({
  14. data() {
  15. return {
  16. joinList: {} as { [key: string]: any }, // 连麦学生列表
  17. loadingJoin: false, // 连麦列表状态
  18. upStatus: false,
  19. downStatus: false,
  20. refreshStatus: false, // 刷新状态
  21. };
  22. },
  23. computed: {
  24. count() {
  25. let count = 0;
  26. for (const key in runtimeModel.joinList) {
  27. if (Object.prototype.hasOwnProperty.call(runtimeModel.joinList, key)) {
  28. const item = runtimeModel.joinList[key];
  29. if (item.userRoomType === 4) {
  30. count += 1;
  31. }
  32. if (count > 3) {
  33. break;
  34. }
  35. }
  36. }
  37. return count;
  38. },
  39. },
  40. mounted() {
  41. // userRoomType
  42. // 1 普通
  43. // 2 老师邀请
  44. // 3 学生申请
  45. // 4 连麦中
  46. event.on(LIVE_EVENT_MESSAGE["RC:Chatroom:SeatApply"], this.onSeatApply);
  47. event.on(LIVE_EVENT_MESSAGE["RC:Chatroom:SeatResponse"], this.onSeatApply);
  48. event.on(LIVE_EVENT_MESSAGE["RM:RTC:UserLeave"], this.onSeatApply);
  49. event.on(LIVE_EVENT_MESSAGE["RC:Chatroom:downSeat"], this.onDownSeat);
  50. event.on(LIVE_EVENT_MESSAGE["RM:RTC:SwitchRole"], this.onSwitchRole);
  51. // event.on(LIVE_EVENT_MESSAGE["RC:Chatroom:Leave"], this.onLeave); // 移动端接收的消息
  52. event.on(LIVE_EVENT_MESSAGE["RC:LookerLoginOut"], this.onLeave); // 后台接收的消息
  53. },
  54. unmounted() {
  55. event.off(LIVE_EVENT_MESSAGE["RC:Chatroom:SeatApply"], this.onSeatApply);
  56. event.off(LIVE_EVENT_MESSAGE["RC:Chatroom:SeatResponse"], this.onSeatApply);
  57. event.off(LIVE_EVENT_MESSAGE["RM:RTC:UserLeave"], this.onSeatApply);
  58. event.off(LIVE_EVENT_MESSAGE["RM:RTC:SwitchRole"], this.onSwitchRole);
  59. // event.off(LIVE_EVENT_MESSAGE["RC:Chatroom:Leave"], this.onLeave); // 移动端接收的消息
  60. event.off(LIVE_EVENT_MESSAGE["RC:LookerLoginOut"], this.onLeave); // 后台接收的消息
  61. },
  62. methods: {
  63. async onLeave(value: any) {
  64. // 学生离开时处理
  65. const userId = value.userId || value.fromUserId;
  66. if (runtimeModel.joinList[userId]) {
  67. RuntimeModelUtils.removeJoin(userId);
  68. }
  69. if (runtimeModel.lookList[userId]) {
  70. RuntimeModelUtils.removeLook(userId);
  71. // 判断是否有学生
  72. // runtime.lookCount =
  73. // runtime.lookCount - 1 >= 0 ? runtime.lookCount - 1 : 0;
  74. }
  75. // 同步移动端观看人数
  76. await RuntimeUtils.sendMessage(
  77. { count: runtime.lookCount },
  78. "MemberCount"
  79. );
  80. // this.onRefresh();
  81. },
  82. onSeatApply(evt: any) {
  83. // console.log(evt, 'onSeatApply joinModel')
  84. if (Array.isArray(evt)) {
  85. for (const id of evt) {
  86. console.log("onSeatApply", id);
  87. RuntimeModelUtils.removeJoin(id);
  88. }
  89. return;
  90. }
  91. const response =
  92. evt.$EventMessage.messageType === "RC:Chatroom:SeatResponse";
  93. const userRoomType = response ? 4 : 3;
  94. // if(evt.$EventMessage.messageType === 'RC:Chatroom:SeatResponse')
  95. const sendTime = dayjs(evt.$EventMessage.sentTime || new Date()).format(
  96. "HH:mm:ss"
  97. );
  98. let tempObj = {
  99. name: evt.audienceName,
  100. id: String(evt.audienceId),
  101. system: 1,
  102. isSelf: false,
  103. content: "",
  104. sendTime,
  105. };
  106. // 申请连麦
  107. if (evt.type === 3) {
  108. console.log(evt, "申请连麦");
  109. const params = {
  110. name: evt.audienceName,
  111. id: evt.audienceId,
  112. userRoomType: userRoomType,
  113. type: evt.type,
  114. };
  115. RuntimeModelUtils.addJoin(evt.audienceId, params);
  116. RuntimeModelUtils.addLook(evt.audienceId, params);
  117. tempObj.content = response ? "同意了连麦申请" : "发起了连麦申请";
  118. RuntimeModelUtils.addMessage(tempObj);
  119. event.emit("MESSAGE:Change");
  120. }
  121. // 取消连麦
  122. if (evt.type === 4) {
  123. console.log(evt, "取消连麦");
  124. if (runtimeModel.joinList[evt.audienceId]) {
  125. RuntimeModelUtils.removeJoin(evt.audienceId);
  126. }
  127. if (runtimeModel.lookList[evt.audienceId]) {
  128. let userLook = runtimeModel.lookList[evt.audienceId];
  129. userLook.userRoomType = 1;
  130. RuntimeModelUtils.addLook(evt.audienceId, userLook);
  131. }
  132. //
  133. tempObj.content = response ? "拒绝了连麦申请" : "取消了连麦申请";
  134. RuntimeModelUtils.addMessage(tempObj);
  135. event.emit("MESSAGE:Change");
  136. setTimeout(() => {
  137. this.onRefresh();
  138. }, 500);
  139. }
  140. },
  141. agree(item: any) {
  142. if (this.count > 3 || this.upStatus) {
  143. console.log(true, 2323);
  144. return;
  145. }
  146. this.upStatus = true;
  147. const data = {
  148. ...item,
  149. audienceName: item.name,
  150. audienceId: String(item.id),
  151. teacherId: String(state.user?.id),
  152. teacherName: state.user?.speakerName,
  153. userRoomType: 4,
  154. type: 1,
  155. };
  156. RuntimeModelUtils.addJoin(item.id, data);
  157. RuntimeModelUtils.addLook(item.id, data);
  158. RuntimeUtils.sendMessage(data, "SeatResponse");
  159. setTimeout(() => {
  160. this.upStatus = false;
  161. }, 300);
  162. },
  163. refuse(item: any) {
  164. if (this.downStatus) {
  165. return;
  166. }
  167. this.downStatus = true;
  168. const data = {
  169. ...item,
  170. audienceName: item.name,
  171. audienceId: String(item.id),
  172. teacherId: String(state.user?.id),
  173. teacherName: state.user?.speakerName,
  174. userRoomType: 4,
  175. type: 5,
  176. };
  177. RuntimeModelUtils.addJoin(item.id, data);
  178. RuntimeUtils.sendMessage(data, "SeatApply");
  179. setTimeout(() => {
  180. this.downStatus = false;
  181. }, 50000);
  182. },
  183. onDownSeat(evt: any) {
  184. this.downStatus = false;
  185. console.log(evt, "onDownSeat");
  186. if (runtimeModel.joinList[evt.audienceId]) {
  187. const users = runtimeModel.joinList[evt.audienceId];
  188. const sendTime = dayjs(new Date()).format("HH:mm:ss");
  189. console.log(
  190. evt.$EventMessage.senderUserId,
  191. state.user?.speakerId,
  192. "onDownSeat"
  193. );
  194. let message = users.type == 5 ? "被抱下麦" : "取消了连麦申请";
  195. let tempObj = {
  196. name: evt.audienceName,
  197. id: evt.audienceId,
  198. system: 1,
  199. isSelf: false,
  200. content: message,
  201. sendTime,
  202. };
  203. RuntimeModelUtils.addMessage(tempObj);
  204. event.emit("MESSAGE:Change");
  205. RuntimeModelUtils.removeJoin(evt.audienceId);
  206. }
  207. },
  208. onSwitchRole(evt: any) {
  209. console.log(evt, "onSwitchRole");
  210. if (runtimeModel.lookList[evt.userId] && evt.role === 2) {
  211. let userLook = runtimeModel.lookList[evt.userId];
  212. userLook.userRoomType = 1;
  213. RuntimeModelUtils.addLook(evt.userId, userLook);
  214. }
  215. },
  216. async onRefresh() {
  217. // 刷新连麦列表
  218. console.log(runtime.joinedRoom?.getRemoteUserIds(), "remoteUserIds");
  219. this.refreshStatus = true;
  220. const userIds: string[] | undefined =
  221. runtime.joinedRoom?.getRemoteUserIds();
  222. const noJoinUserIds = userIds?.filter((item: string) => {
  223. return !runtimeModel.joinList[item];
  224. });
  225. const noLookList = noJoinUserIds?.filter((item: string) => {
  226. return !runtimeModel.lookList[item];
  227. });
  228. const inLookList = noJoinUserIds?.filter((item: string) => {
  229. return runtimeModel.lookList[item];
  230. });
  231. const joinUserList = inLookList?.map((item: any) => {
  232. const user = runtimeModel.lookList[item];
  233. user.userRoomType = 4;
  234. return user;
  235. });
  236. console.log(joinUserList);
  237. if (noLookList && noLookList?.length > 0) {
  238. const fetchList = await this.FetchUserDetails(noLookList);
  239. console.log(fetchList, "fetchList");
  240. fetchList.forEach((item: any) => {
  241. joinUserList?.push({
  242. name: item.userName,
  243. id: item.userId,
  244. sendTime: dayjs(new Date()).format("HH:mm:ss"),
  245. userRoomType: 4,
  246. type: 1,
  247. });
  248. });
  249. }
  250. // 判断是否在连麦列表中
  251. if (joinUserList && joinUserList?.length > 0) {
  252. joinUserList?.forEach((item: any) => {
  253. RuntimeModelUtils.addJoin(item.id, item);
  254. });
  255. }
  256. setTimeout(() => {
  257. this.refreshStatus = false;
  258. }, 300);
  259. },
  260. async FetchUserDetails(userIds: string[]) {
  261. try {
  262. const res = await request.post(
  263. "/api-web/imLiveBroadcastRoom/queryBaseUserInfo",
  264. {
  265. requestType: "json",
  266. data: userIds,
  267. }
  268. );
  269. return res.data;
  270. } catch (error) {}
  271. return [];
  272. },
  273. },
  274. render() {
  275. const list = Object.values(runtimeModel.joinList);
  276. return (
  277. <div style={{ minHeight: "100%", position: "relative" }}>
  278. {list.length > 0 ? (
  279. list.map((item: any) => (
  280. <div class={styles.itemContent}>
  281. <div class={styles.itemInfo}>
  282. <div class={styles.itemName}>
  283. <p class={styles.userName}>
  284. <span class={styles["name-style"]}>{item.name}</span>
  285. {item.userRoomType !== 4 ? (
  286. <span style={{ paddingLeft: "10px" }}>申请连麦</span>
  287. ) : (
  288. <span
  289. style={{
  290. paddingLeft: "10px",
  291. color: "var(--live-text-color)",
  292. }}
  293. >
  294. 正在连麦
  295. </span>
  296. )}
  297. </p>
  298. {item.userRoomType !== 4 ? (
  299. <div class={styles.joinText}>
  300. <div class={styles.join}>{/* 申请连麦 */}</div>
  301. <ElButton
  302. size="small"
  303. type="primary"
  304. disabled={this.count > 3}
  305. class={styles.btn}
  306. onClick={() => this.agree(item)}
  307. >
  308. 上麦
  309. </ElButton>
  310. </div>
  311. ) : (
  312. <div class={styles.joinText}>
  313. <div class={styles.join}>{/* 正在连麦 */}</div>
  314. <ElButton
  315. loading={this.downStatus}
  316. size="small"
  317. plain
  318. class={[styles.btn, styles.downBtn]}
  319. onClick={() => this.refuse(item)}
  320. >
  321. 下麦
  322. </ElButton>
  323. </div>
  324. )}
  325. </div>
  326. </div>
  327. </div>
  328. ))
  329. ) : this.loadingJoin ? (
  330. <div class={styles.loadingStyle}>
  331. <div
  332. class="el-loading-mask"
  333. style="background-color: rgba(0, 0, 0, 0.8);"
  334. >
  335. <div class="el-loading-spinner">
  336. <svg class="circular" viewBox="25 25 50 50">
  337. <circle
  338. class="path"
  339. cx="50"
  340. cy="50"
  341. r="20"
  342. fill="none"
  343. ></circle>
  344. </svg>
  345. </div>
  346. </div>
  347. </div>
  348. ) : (
  349. <Empty
  350. style={{ paddingTop: "120px" }}
  351. text="暂无学员发起连麦!"
  352. icon="noData-no-join"
  353. />
  354. )}
  355. {/* <div
  356. class={[
  357. styles.refresh,
  358. styles["refresh-animation"],
  359. this.refreshStatus ? styles.refreshStart : styles.refreshStop,
  360. ]}
  361. onClick={this.onRefresh}
  362. >
  363. <SvgIcon
  364. name="message-refresh"
  365. color={"#01A79E"}
  366. style={{ width: "20px", height: "20px" }}
  367. />
  368. </div> */}
  369. </div>
  370. );
  371. },
  372. });