PageRtcLive.vue 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. <template>
  2. <div id="PageRtcLive">
  3. <div class="member_number">
  4. <div class="members"></div>
  5. <span>{{ user_list.length }}观看</span>
  6. </div>
  7. <chat v-show="chatShow" :chatList="chatList" :user_info="user_info"></chat>
  8. <Trtccom
  9. :audioMuted="audioMuted"
  10. :videoMuted="videoMuted"
  11. v-if="isRunRTC"
  12. />
  13. <div class="contorlBar" v-if="!showInput">
  14. <div
  15. v-if="connectStatus == 1"
  16. :class="{ disabled: !user_info.IsWords }"
  17. class="saySomething"
  18. @click="onFocus"
  19. >
  20. <!-- <i class="speakIcon"
  21. :class="{'dis':!user_info.IsWords}"></i> -->
  22. <span v-if="user_info.IsWords">说点什么</span>
  23. <span v-if="!user_info.IsWords">已被禁言</span>
  24. <div
  25. class="disSpeakBtn"
  26. @click.stop="chatShow = !chatShow"
  27. :class="{ dis: !chatShow }"
  28. ></div>
  29. </div>
  30. <div style="text-align: right; width: 100%" v-if="connectStatus == 0">
  31. 连接中...
  32. </div>
  33. <div v-if="connectStatus == 1" class="contorl_btn">
  34. <div
  35. v-if="isBrushes && user_info.Role == 'leader'"
  36. @click="onDrawUndo"
  37. class="brushesBack"
  38. :class="{ disabled: !canUndo }"
  39. ></div>
  40. <div
  41. v-if="user_info.Role == 'leader'"
  42. @click="onDraw(!isBrushes)"
  43. :class="{ brushesed: isBrushes }"
  44. class="brushes"
  45. ></div>
  46. <div
  47. v-if="user_list.length < 50"
  48. class="invitation"
  49. @click="onClickShare"
  50. ></div>
  51. <div v-if="role == 'leader'" class="members" @click="openMember"></div>
  52. <template v-if="role == 'leader'">
  53. <div
  54. v-if="!disableMic"
  55. @click="handleMuteAduio"
  56. :class="{ mic_off: audioMuted, disabled: !audioDeviceId }"
  57. class="mic_on"
  58. ></div>
  59. <div
  60. v-if="disableMic"
  61. class="mic_no"
  62. :class="{ disabled: !audioDeviceId }"
  63. ></div>
  64. </template>
  65. <!-- <div
  66. v-if="role == 'leader'"
  67. @click="handleMuteVideo"
  68. :class="[
  69. videoMuted ? 'video_no' : 'video_on',
  70. videoDeviceId ? '' : 'disabled',
  71. ]"
  72. ></div> -->
  73. <div class="exit" @click="openDialog('dialogIndex')"></div>
  74. </div>
  75. </div>
  76. <div class="layer" v-if="showInput" @click="closeInput">
  77. <div class="inputBox" @click.stop>
  78. <div class="msgBox">
  79. <input
  80. id="input_msg"
  81. type="text"
  82. maxlength="200"
  83. v-model.trim="text"
  84. :placeholder="`说点什么~`"
  85. />
  86. <span
  87. class="iconsend_icon"
  88. :class="{ disable: text == '' }"
  89. @click.stop="sendText"
  90. >发送</span
  91. >
  92. </div>
  93. </div>
  94. </div>
  95. <!-- 成員 -->
  96. <div class="layer" v-if="showMember" @click.self="closeMember">
  97. <div
  98. class="memberContent animated"
  99. :class="animateActive ? 'fadeInUpBig' : 'fadeOutDownBig'"
  100. >
  101. <div class="blurBox"></div>
  102. <div class="content">
  103. <div class="memberHeader">
  104. <span> 成员管理({{ user_list.length }})</span>
  105. <i class="iconfont"></i>
  106. </div>
  107. <div class="memberList">
  108. <div class="memberItem">
  109. <div class="userMsg">
  110. <div class="avatar">
  111. <img
  112. :src="
  113. require('@/assets/images/rtcLive/avatar_small@2x.png')
  114. "
  115. alt=""
  116. />
  117. </div>
  118. <div class="name" v-if="user_info.Role == 'leader'">
  119. {{ user_info.Nickname }} (主持人,我)
  120. </div>
  121. <div class="name" v-else>{{ user_info.Nickname }} (我)</div>
  122. </div>
  123. <!-- <div class="button" v-if="user_info.Role == 'leader'">
  124. <div
  125. @click="setAllMuted(!all_mute_mic)"
  126. class="micBtn mute_all_mic"
  127. :class="{ open_all_mic: all_mute_mic }"
  128. ></div>
  129. </div> -->
  130. </div>
  131. <div
  132. v-show="user_info.UserId != i.UserId && i.Role != 'leader'"
  133. class="memberItem"
  134. v-for="(i, idx) in user_list"
  135. :key="idx"
  136. >
  137. <div class="userMsg">
  138. <div class="avatar">
  139. <img
  140. :src="
  141. require('@/assets/images/rtcLive/avatar_small@2x.png')
  142. "
  143. alt=""
  144. />
  145. </div>
  146. <div class="name">{{ i.Nickname }}</div>
  147. </div>
  148. <!-- <div class="button" v-if="user_info.Role == 'leader'">
  149. <div
  150. class="micBtn"
  151. :class="i.IsWords ? 'ban_speak_on' : 'ban_speak_off'"
  152. @click="userCanSpeak(i)"
  153. ></div>
  154. <div
  155. class="micBtn"
  156. :class="i.IsMuted ? 'mute_one_mic_off' : 'mute_one_mic_on'"
  157. @click="onMemberMuted(i)"
  158. ></div>
  159. </div> -->
  160. </div>
  161. </div>
  162. </div>
  163. </div>
  164. </div>
  165. </div>
  166. <teleport :to="`#app`">
  167. <div v-if="showShare" @click="showShare = false" class="sharetip">
  168. <img
  169. @click.stop
  170. :style="`right:${isMP ? '16%' : '6%'}`"
  171. :src="require('@/assets/images/icon/img_scene_share.png')"
  172. alt=""
  173. />
  174. </div>
  175. </teleport>
  176. </template>
  177. <script setup>
  178. import {
  179. onMounted,
  180. onUnmounted,
  181. watch,
  182. defineEmits,
  183. ref,
  184. reactive,
  185. computed,
  186. nextTick,
  187. } from "vue";
  188. import { useApp, getApp } from "@/app";
  189. import { useStore } from "vuex";
  190. import { Dialog } from "@/global_components/";
  191. import common from "@/utils/common";
  192. import { mapGetters } from "vuex";
  193. import chat from "./chat/chat.vue";
  194. import Trtccom from "./Trtccom.vue";
  195. import browser from "@/utils/browser";
  196. import wxShare from "@/utils/wxshare";
  197. const emit = defineEmits(["openDialog", "closeSocket"]);
  198. const store = useStore();
  199. let jumpNewScene = (sceneFirstView) => {
  200. let url = window.location.href;
  201. if (!browser.hasURLParam("pose")) {
  202. url += `&${sceneFirstView.sceneview}`;
  203. } else {
  204. url = browser.replaceQueryString(
  205. url,
  206. "pose",
  207. sceneFirstView.sceneview.replace("pose=", "")
  208. );
  209. }
  210. url = browser.replaceQueryString(url, "m", sceneFirstView.num);
  211. return url;
  212. };
  213. let chatAutoScroll = () => {
  214. let el = document.getElementById("chat");
  215. let client_h = document.getElementById("chat").clientHeight;
  216. let all = document.getElementById("contents").clientHeight;
  217. el.scrollTo(0, client_h + all);
  218. };
  219. let createSocket = (config) => {
  220. var socket = io(process.env.VUE_APP_SOCKET_URL, {
  221. path: "/ws-sync",
  222. transports: ["websocket"],
  223. });
  224. return socket;
  225. };
  226. store.commit("rtc/setSocket", createSocket());
  227. let getUrl = (href, queryArr) => {
  228. queryArr.forEach((item) => {
  229. if (!browser.hasURLParam(item.key)) {
  230. let ttt = href.split("index.html?");
  231. href = `${ttt[0]}index.html?${item.key}=${item.val}&${ttt[1]}`;
  232. console.log(
  233. href,
  234. "------index.htmlindex.htmlindex.htmlindex.htmlindex.htmlindex.htmlindex.html----------"
  235. );
  236. } else {
  237. href = browser.replaceQueryString(href, item.key, item.val);
  238. }
  239. });
  240. return href;
  241. };
  242. const videoDeviceId = computed(() => store.getters["rtc/videoDeviceId"]);
  243. const audioDeviceId = computed(() => store.getters["rtc/audioDeviceId"]);
  244. const connectStatus = ref(0);
  245. const isBrushes = ref(false);
  246. const showInput = ref(false);
  247. const showMember = ref(false);
  248. const animateActive = ref(false);
  249. const showShare = ref(false);
  250. const isMP = ref(false);
  251. const audioMuted = ref(false);
  252. const videoMuted = ref(false);
  253. const socket = computed(() => store.getters["rtc/socket"]);
  254. const myVideoShow = ref(false);
  255. const userVideoShow = ref(false);
  256. const user_info = ref({});
  257. const user_list = ref([]);
  258. const mode = ref(browser.getURLParam("mode"));
  259. const role = ref(browser.getURLParam("role"));
  260. const userId = computed(() => store.getters["rtc/userId"]);
  261. const roomId = computed(() => store.getters["rtc/roomId"]);
  262. const isJoined = ref(false);
  263. const isRunRTC = ref(false);
  264. const paint = reactive({});
  265. const chatList = ref([]);
  266. const text = ref("");
  267. const shareLink = ref("");
  268. const canUndo = ref(false);
  269. const audioDevices = ref([1]);
  270. const videoDevices = ref([1]);
  271. const disableMic = ref(false);
  272. const chatShow = ref(true);
  273. const all_mute_mic = ref(false);
  274. const tags = computed(() => {
  275. return store.getters["tag/tags"] || [];
  276. });
  277. const onClickShare = () => {
  278. openDialog("dialogShare", shareLink.value);
  279. };
  280. const userGetOut = (item, i) => {
  281. socket.value &&
  282. socket.value.emit("action", {
  283. type: "getout",
  284. data: {
  285. id: item.UserId,
  286. },
  287. });
  288. user_list.value = user_list.value.splice(i, 1);
  289. };
  290. const setUserWords = (res) => {
  291. if (res.userId == user_info.value.UserId) {
  292. user_info.value.IsWords = res.words;
  293. Dialog.toast({
  294. content: !user_info.value.IsWords
  295. ? `主持人设置了禁言`
  296. : `主持人已解除禁言`,
  297. });
  298. }
  299. };
  300. const handleMuteVideo = () => {
  301. videoMuted.value = !videoMuted.value;
  302. };
  303. const handleMuteAduio = () => {
  304. audioMuted.value = !audioMuted.value;
  305. if (role.value == "leader") {
  306. socket.value.emit("action", {
  307. type: "users-muted",
  308. muted: audioMuted.value,
  309. userId: user_info.value.UserId,
  310. });
  311. }
  312. };
  313. const setUserMuted = (res) => {
  314. if (res.userId) {
  315. if (res.userId == user_info.value.UserId && role.value == "customer") {
  316. user_info.value.IsMuted = res.muted;
  317. Dialog.toast({
  318. content: !user_info.value.IsMuted
  319. ? `主持人设置了开麦`
  320. : `主持人设置了静音`,
  321. });
  322. disableMic.value = res.muted;
  323. audioMuted.value = res.muted;
  324. }
  325. } else {
  326. onAllMuted(res);
  327. }
  328. };
  329. const setAllMuted = (data) => {
  330. console.log(data, "IsMuted");
  331. socket.value.emit("action", { type: "users-muted", muted: data });
  332. };
  333. const onAllMuted = (res) => {
  334. user_list.value = res.members.reduce(function (tempArr, item) {
  335. if (tempArr.findIndex((ele) => ele.UserId === item.UserId) === -1) {
  336. tempArr.push(item);
  337. }
  338. return tempArr;
  339. }, []);
  340. if (role.value == "leader") {
  341. all_mute_mic.value = res.muted;
  342. }
  343. user_list.value.forEach((item) => {
  344. user_info.value.IsMuted = res.muted;
  345. item.IsMuted = res.muted;
  346. if (role.value == "customer") {
  347. Dialog.toast({
  348. content: !user_info.value.IsMuted
  349. ? `主持人设置了开麦`
  350. : `主持人设置了静音`,
  351. });
  352. disableMic.value = res.muted;
  353. audioMuted.value = res.muted;
  354. }
  355. });
  356. };
  357. //用戶加入
  358. const setUserJoin = async (res) => {
  359. console.log("有人进来了", res);
  360. // self.user_info = res.user;
  361. user_list.value = res.members.reduce(function (tempArr, item) {
  362. if (tempArr.findIndex((ele) => ele.UserId === item.UserId) === -1) {
  363. tempArr.push(item);
  364. }
  365. return tempArr;
  366. }, []);
  367. // self.chekcLeaderInfo();
  368. let name = res.user.Nickname;
  369. if (res.user.Role == "leader") {
  370. name = "主持人";
  371. Dialog.toast({ content: `主持人进入房间` });
  372. socket.value.emit("action", { type: "user-init" });
  373. }
  374. let data = {
  375. role: res.user.Role,
  376. mode: mode.value,
  377. Nickname: name,
  378. UserId: res.user.UserId,
  379. text: "进入房间",
  380. };
  381. chatList.value.push(data);
  382. await nextTick();
  383. try {
  384. chatAutoScroll();
  385. } catch (error) {}
  386. };
  387. const onDrawUndo = async () => {
  388. let app = await getApp();
  389. app.Connect.paint.undo();
  390. canUndo.value = app.Connect.paint.records.length > 0;
  391. console.log(app.Connect.paint.records, "app.Connect.paint.records");
  392. };
  393. // 畫筆開啟
  394. const onDraw = async (status) => {
  395. isBrushes.value = status;
  396. if (isBrushes.value) {
  397. await getApp().Connect.paint.show({
  398. role: role.value,
  399. paint: role.value == "leader" ? true : false,
  400. });
  401. if (role.value == "leader") {
  402. socket.value.emit("action", { type: "user-paint", open: true });
  403. }
  404. } else {
  405. await getApp().Connect.paint.hide();
  406. if (role.value == "leader") {
  407. socket.value.emit("action", { type: "user-paint", open: false });
  408. }
  409. }
  410. };
  411. //打開輸入框
  412. const onFocus = () => {
  413. showInput.value = true;
  414. nextTick(() => {
  415. let input_msg = document.getElementById("input_msg");
  416. input_msg.focus();
  417. });
  418. };
  419. //發彈幕
  420. const sendText = async () => {
  421. if (text.value == "") {
  422. return;
  423. }
  424. let data = {
  425. role: role.value,
  426. mode: mode.value,
  427. Nickname: user_info.value.Nickname,
  428. UserId: user_info.value.UserId,
  429. text: text.value,
  430. };
  431. socket.value &&
  432. socket.value.emit("action", {
  433. type: "danmumsg",
  434. data,
  435. });
  436. chatList.value.push(data);
  437. await nextTick();
  438. try {
  439. chatAutoScroll();
  440. let input_msg = document.getElementById("input_msg");
  441. input_msg.blur();
  442. } catch (error) {}
  443. closeInput();
  444. };
  445. //接收消息
  446. const setReceiveMsg = async (res) => {
  447. console.log(res);
  448. if (res.role == "leader") {
  449. res.Nickname = "主持人";
  450. }
  451. chatList.value.push(res);
  452. await nextTick();
  453. try {
  454. chatAutoScroll();
  455. } catch (error) {}
  456. };
  457. // 關閉輸入框
  458. const closeInput = () => {
  459. showInput.value = false;
  460. text.value = "";
  461. };
  462. // 開啟成員
  463. const openMember = () => {
  464. showMember.value = true;
  465. animateActive.value = true;
  466. };
  467. // 關閉成員
  468. const closeMember = () => {
  469. animateActive.value = false;
  470. let t = setTimeout(() => {
  471. clearTimeout(t);
  472. showMember.value = false;
  473. }, 200);
  474. };
  475. const openDialog = (str, link) => {
  476. emit("openDialog", str, link);
  477. };
  478. const onMemberMuted = (item) => {
  479. item.IsMuted = !item.IsMuted;
  480. socket.value.emit("action", {
  481. type: "users-muted",
  482. muted: item.IsMuted,
  483. userId: item.UserId,
  484. });
  485. };
  486. const onMemberLeave = async (res) => {
  487. console.log("有人离开了", res);
  488. user_list.value = res.members.reduce(function (tempArr, item) {
  489. if (tempArr.findIndex((ele) => ele.UserId === item.UserId) === -1) {
  490. tempArr.push(item);
  491. }
  492. return tempArr;
  493. }, []);
  494. let name = res.user.Nickname;
  495. if (res.user.Role == "leader") {
  496. name = "主持人";
  497. Dialog.toast({ content: `主持人离开了房间` });
  498. }
  499. let data = {
  500. role: res.user.Role,
  501. mode: mode.value,
  502. Nickname: name,
  503. UserId: res.user.UserId,
  504. text: "离开房间",
  505. };
  506. chatList.value.push(data);
  507. await nextTick();
  508. try {
  509. chatAutoScroll();
  510. } catch (error) {}
  511. };
  512. const userCanSpeak = (item) => {
  513. item.IsWords = !item.IsWords;
  514. socket.value.emit("action", {
  515. type: "users-words",
  516. words: item.IsWords,
  517. userId: item.UserId,
  518. });
  519. };
  520. const onGetOuT = (data) => {
  521. if (data.id == user_info.value.UserId) {
  522. emit("closeSocket");
  523. Dialog.toast({ content: `您已被移除` });
  524. }
  525. };
  526. watch(
  527. user_list,
  528. () => {
  529. if (role.value == "leader") {
  530. all_mute_mic.value = !user_list.value.some(
  531. (item) => !item.IsMuted && item.Role == "customer"
  532. );
  533. }
  534. },
  535. {
  536. deep: true,
  537. }
  538. );
  539. const startFollow = (app) => {
  540. app.Connect.follow.start({ follow: role.value == "customer" });
  541. store.commit(
  542. "rtc/setUserId",
  543. browser.getURLParam("vruserId") ||
  544. `user_${role.value}${Math.floor(Math.random() * 100000000)}`
  545. );
  546. store.commit(
  547. "rtc/setRoomId",
  548. browser.getURLParam("roomId") ||
  549. `room_${Math.floor(Math.random() * 100000000)}`
  550. );
  551. socket.value.on("connect", (e) => {
  552. socket.value.emit("join", {
  553. userId: userId.value,
  554. roomId: roomId.value,
  555. role: role.value || "leader",
  556. nickname: browser.getURLParam("name"),
  557. });
  558. });
  559. // 加入房間成功
  560. socket.value.on("join", (data) => {
  561. let meblist = data.members.reduce(function (tempArr, item) {
  562. if (tempArr.findIndex((ele) => ele.UserId === item.UserId) === -1) {
  563. tempArr.push(item);
  564. }
  565. return tempArr;
  566. }, []);
  567. if (meblist.length > 50 && role.value == "customer") {
  568. Dialog.toast({ content: `房间已满员` });
  569. emit("closeSocket");
  570. return;
  571. }
  572. connectStatus.value = 1;
  573. if (role.value == "customer") {
  574. socket.value.emit("action", { type: "ask-currentscene" });
  575. setTimeout(() => {
  576. socket.value.emit("action", { type: "user-init" });
  577. }, 1500);
  578. if (data.user.IsMuted) {
  579. disableMic.value = true;
  580. audioMuted.value = true;
  581. }
  582. } else {
  583. if (data.user.IsMuted) {
  584. audioMuted.value = true;
  585. }
  586. }
  587. isJoined.value = true;
  588. setTimeout(() => {
  589. isRunRTC.value = true;
  590. }, 3000);
  591. user_info.value = data.user;
  592. user_list.value = meblist;
  593. //更新分享鏈接
  594. shareLink.value = getUrl(window.location.href, [
  595. {
  596. key: "mode",
  597. val: mode.value,
  598. },
  599. {
  600. key: "name",
  601. val: "",
  602. },
  603. {
  604. key: "vruserId",
  605. val: "",
  606. },
  607. {
  608. key: "role",
  609. val: "customer",
  610. },
  611. {
  612. key: "roomId",
  613. val: user_info.value.RoomId,
  614. },
  615. ]);
  616. let tmp = "";
  617. if (user_info.value.Role == "leader") {
  618. tmp = getUrl(window.location.href, [
  619. {
  620. key: "roomId",
  621. val: user_info.value.RoomId,
  622. },
  623. {
  624. key: "vruserId",
  625. val: user_info.value.UserId,
  626. },
  627. ]);
  628. } else {
  629. tmp = getUrl(window.location.href, [
  630. {
  631. key: "vruserId",
  632. val: user_info.value.UserId,
  633. },
  634. ]);
  635. }
  636. store.commit("rtc/setRole", user_info.value.Role);
  637. history.replaceState(null, null, tmp);
  638. });
  639. socket.value.on("action", (data) => {
  640. console.log(data, "=============");
  641. if (data.type == "error") {
  642. Dialog.toast({ content: `房间未找到`, type: "error" });
  643. emit("closeSocket");
  644. } else if (data.type == "danmumsg") {
  645. setReceiveMsg(data.data);
  646. } else if (data.type == "getout") {
  647. onGetOuT(data.data);
  648. } else if (data.type == "user-init") {
  649. app.Connect.follow.sync();
  650. } else if (data.type == "user-paint") {
  651. onDraw(data.open);
  652. if (role.value == "customer") {
  653. if (data.open) {
  654. Dialog.toast({ content: `主持人开启画笔` });
  655. } else {
  656. Dialog.toast({ content: `主持人关闭画笔` });
  657. }
  658. }
  659. } else if (data.type == "user-join") {
  660. setUserJoin(data);
  661. } else if (data.type == "users-muted") {
  662. setUserMuted(data);
  663. //閉麥
  664. } else if (data.type == "users-words") {
  665. setUserWords(data);
  666. //禁言
  667. } else if (data.type == "user-leave") {
  668. // 房間解散
  669. onMemberLeave(data);
  670. } else if (data.type == "leader-dismiss") {
  671. emit("closeSocket");
  672. Dialog.toast({ content: `主持人已解散房间` });
  673. } else if (data.type == "tagclick") {
  674. if (role.value == "customer") {
  675. let item = tags.value.find((item) => item.sid == data.data.sid);
  676. if (item.type == "commodity") {
  677. store.commit("tag/setTagClickType", {
  678. type: "goodlist",
  679. data: item,
  680. });
  681. } else if (item.type == "waterfall") {
  682. store.commit("tag/setTagClickType", {
  683. type: "waterfall",
  684. data: item,
  685. });
  686. } else if (item.type == "link_scene") {
  687. let sceneFirstView = item.hotContent.sceneFirstView;
  688. window.location.href = jumpNewScene(sceneFirstView);
  689. }
  690. // document.querySelector(`[data-tag-id="${data.data.sid}"]`).click();
  691. // console.log(data.data.sid);
  692. }
  693. } else if (data.type == "tagclose") {
  694. if (role.value == "customer") {
  695. store.commit("tag/setTagClickType", {
  696. type: "",
  697. data: {},
  698. });
  699. }
  700. } else if (data.type == "ask-currentscene") {
  701. if (role.value == "leader") {
  702. socket.value.emit("action", {
  703. type: "answer-currentscene",
  704. data: {
  705. scene: browser.getURLParam("m"),
  706. pose: browser.getURLParam("pose"),
  707. },
  708. });
  709. }
  710. } else if (data.type == "answer-currentscene") {
  711. if (role.value == "customer") {
  712. if (data.data.scene != browser.getURLParam("m")) {
  713. let url1 = browser.replaceQueryString(
  714. window.location.href,
  715. "m",
  716. data.data.scene
  717. );
  718. let url = browser.replaceQueryString(url1, "pose", data.data.pose);
  719. location.replace(url);
  720. }
  721. }
  722. }
  723. });
  724. // 同屏帶看
  725. socket.value.on("sync", (data) => {
  726. if (role.value == "customer") {
  727. app.Connect.follow.receive(data);
  728. }
  729. });
  730. // 畫筆
  731. socket.value.on("paint", (data) => {
  732. if (role.value == "customer") {
  733. app.Connect.paint.receive(data);
  734. }
  735. });
  736. };
  737. let onfollowData = (data) => {
  738. if (isJoined.value) {
  739. socket.value.emit("sync", data);
  740. }
  741. };
  742. let onfollowPaint = async (data) => {
  743. canUndo.value = (await getApp().Connect.paint.records.length) > 0;
  744. socket.value.emit("paint", data);
  745. };
  746. onMounted(async () => {
  747. let app = await getApp();
  748. startFollow(app);
  749. app.Connect.follow.on("data", onfollowData);
  750. app.Connect.paint.on("data", onfollowPaint);
  751. });
  752. onUnmounted(async () => {
  753. let app = await getApp();
  754. app.Connect.follow.off("data", onfollowData);
  755. app.Connect.follow.off("data", onfollowData);
  756. });
  757. </script>
  758. <style scoped lang="scss">
  759. #PageRtcLive {
  760. position: absolute;
  761. left: 0;
  762. bottom: 0;
  763. // height: 7.31rem;
  764. width: 100%;
  765. z-index: 999;
  766. // background: rgba(0, 0, 0, 0.1);
  767. padding: 0 0.44rem;
  768. box-sizing: border-box;
  769. // pointer-events: none;
  770. .member_number {
  771. // width: 1.67rem;
  772. height: 0.5rem;
  773. background: rgba(0, 0, 0, 0.3);
  774. border-radius: 0.25rem;
  775. position: fixed;
  776. top: 0.56rem;
  777. right: 0.44rem;
  778. padding: 0.07rem 0.17rem;
  779. display: flex;
  780. align-items: center;
  781. justify-content: center;
  782. .members {
  783. width: 0.42rem;
  784. height: 0.42rem;
  785. background: url(~@/assets/images/rtcLive/members@2x.png);
  786. background-size: 100% 100%;
  787. margin-right: 0.07rem;
  788. }
  789. span {
  790. font-size: 0.24rem;
  791. color: #fff;
  792. }
  793. }
  794. .contorlBar {
  795. width: 9.51rem;
  796. height: 1.173333rem;
  797. background: rgba(0, 0, 0, 0.5);
  798. border-radius: 0.67rem;
  799. border: 0.03rem solid rgba(255, 255, 255, 0.1);
  800. margin: 0.67rem auto 0;
  801. // position: absolute;
  802. position: fixed;
  803. z-index: 9999;
  804. left: 50%;
  805. transform: translateX(-50%);
  806. padding: 0 0.266667rem;
  807. box-sizing: border-box;
  808. bottom: 0.94rem;
  809. display: flex;
  810. align-items: center;
  811. justify-content: space-between;
  812. .saySomething {
  813. color: #fff;
  814. // font-size: 0.42rem;
  815. height: 0.8rem;
  816. position: relative;
  817. display: flex;
  818. align-items: center;
  819. justify-content: center;
  820. background: rgba(0, 0, 0, 0.5);
  821. padding: 0 0.2rem 0 0.2rem;
  822. border-radius: 0.64rem;
  823. width: 100%;
  824. display: flex;
  825. align-items: center;
  826. justify-content: space-between;
  827. .disSpeakBtn {
  828. width: 0.533333rem;
  829. height: 0.533333rem;
  830. background: url(~@/assets/images/rtcLive/pop-up_screen_on@2x.png)
  831. no-repeat;
  832. background-size: 100% 100%;
  833. cursor: pointer;
  834. &.dis {
  835. background: url(~@/assets/images/rtcLive/pop-up_screen_off@2x.png)
  836. no-repeat;
  837. background-size: 100% 100%;
  838. }
  839. }
  840. .speakIcon {
  841. width: 0.32rem;
  842. height: 0.32rem;
  843. background: url(~@/assets/images/rtcLive/Input_norma@2x.png);
  844. background-size: 100% 100%;
  845. display: block;
  846. margin-right: 0.1rem;
  847. &.dis {
  848. background: url(~@/assets/images/rtcLive/Input_disabled@2x.png);
  849. background-size: 100% 100%;
  850. }
  851. }
  852. span {
  853. font-size: 0.266667rem;
  854. }
  855. // &::after {
  856. // content: "";
  857. // position: absolute;
  858. // width: 0.04rem;
  859. // height: 0.44rem;
  860. // background: #ffffff;
  861. // border-radius: 0.03rem;
  862. // top: 50%;
  863. // transform: translateY(-50%);
  864. // right: -0.28rem;
  865. // }
  866. }
  867. .contorl_btn {
  868. display: flex;
  869. align-items: center;
  870. justify-content: space-between;
  871. margin-left: 0.2rem;
  872. > div {
  873. // width: 0.56rem;
  874. // height: 0.56rem;
  875. width: 0.65rem;
  876. height: 0.65rem;
  877. // font-size: 0.56rem;
  878. // background: #FD5151;
  879. margin-right: 0.33rem;
  880. // &.iconexit {
  881. // // width: 0.56rem;
  882. // // height: 0.56rem;
  883. // color: #fd5151;
  884. // // background: #fff;
  885. // // border-radius: 50%;
  886. // // overflow: hidden;
  887. // }
  888. &.brushesBack {
  889. background: url(~@/assets/images/rtcLive/revocation@2x.png);
  890. background-size: 100% 100%;
  891. }
  892. &.brushes {
  893. // background: url(~@/assets/images/rtcLive/brushes@2x.png);
  894. background: url(~@/assets/images/rtcLive/brushes@2x.png);
  895. background-size: 100% 100%;
  896. }
  897. &.brushesed {
  898. background: url(~@/assets/images/rtcLive/brushes_selected@2_1.png);
  899. background-size: 100% 100%;
  900. }
  901. &.invitation {
  902. background: url(~@/assets/images/rtcLive/invitation@2x.png);
  903. background-size: 100% 100%;
  904. }
  905. &.members {
  906. background: url(~@/assets/images/rtcLive/members@2x.png);
  907. background-size: 100% 100%;
  908. }
  909. &.mic_on {
  910. background: url(~@/assets/images/rtcLive/mic_on@2x.png);
  911. background-size: 100% 100%;
  912. }
  913. &.mic_no {
  914. background: url(~@/assets/images/rtcLive/mic_off_50@2x.png);
  915. background-size: 100% 100%;
  916. }
  917. &.mic_off {
  918. background: url(~@/assets/images/rtcLive/mic_off@2x.png);
  919. background-size: 100% 100%;
  920. }
  921. &.video_on {
  922. background: url(~@/assets/images/rtcLive/video_on@2x.png);
  923. background-size: 100% 100%;
  924. }
  925. &.video_no {
  926. background: url(~@/assets/images/rtcLive/video_off_50@2x.png);
  927. background-size: 100% 100%;
  928. }
  929. &.video_off {
  930. background: url(~@/assets/images/rtcLive/video_off@2x.png);
  931. background-size: 100% 100%;
  932. }
  933. &.exit {
  934. background: url(~@/assets/images/rtcLive/exit@2x.png);
  935. background-size: 100% 100%;
  936. }
  937. &:last-child {
  938. margin-right: 0;
  939. }
  940. }
  941. }
  942. }
  943. .layer {
  944. width: 100vw;
  945. height: 100vh;
  946. background: rgba(0, 0, 0, 0.1);
  947. z-index: 10000;
  948. position: fixed;
  949. bottom: 0;
  950. left: 0;
  951. // right: unset;
  952. // top: unset;
  953. .inputBox {
  954. width: 100vw;
  955. height: 1.39rem;
  956. // background: #f2f2f2;
  957. border: 1px solid rgba(255, 255, 255, 0.1);
  958. background: rgba(0, 0, 0, 0.5);
  959. position: absolute;
  960. bottom: 0;
  961. left: 0;
  962. padding: 0 0.44rem;
  963. box-sizing: border-box;
  964. display: flex;
  965. align-items: center;
  966. justify-content: center;
  967. .msgBox {
  968. width: 9.53rem;
  969. height: 0.94rem;
  970. position: absolute;
  971. > input {
  972. width: 9.53rem;
  973. height: 0.94rem;
  974. background: rgba(0, 0, 0, 0.5);
  975. border-radius: 0.56rem;
  976. font-size: 0.42rem;
  977. padding: 0 1.6rem 0 0.44rem;
  978. box-sizing: border-box;
  979. border: none;
  980. outline: none;
  981. color: #fff;
  982. resize: none;
  983. line &::placeholder {
  984. color: rgba(255, 255, 255, 0.5);
  985. }
  986. }
  987. .iconfont {
  988. // width: 0.56rem;
  989. // height: 0.56rem;
  990. // background: #ed5d18;
  991. font-size: 0.56rem;
  992. position: absolute;
  993. top: 50%;
  994. transform: translateY(-50%);
  995. right: 0.78rem;
  996. color: #fff;
  997. &.active {
  998. color: #ed5d18;
  999. }
  1000. }
  1001. .iconsend_icon {
  1002. position: absolute;
  1003. top: 50%;
  1004. transform: translateY(-50%);
  1005. // right: 0.78rem;
  1006. right: 0.106667rem;
  1007. width: 1.28rem;
  1008. height: 0.693333rem;
  1009. background: #ed5d18;
  1010. color: #fff;
  1011. font-size: 0.32rem;
  1012. text-align: center;
  1013. line-height: 0.693333rem;
  1014. border-radius: 0.533333rem;
  1015. // width: 0.67rem;
  1016. // height: 0.67rem;
  1017. // background-image: url(~@/assets/images/rtcLive/send_selected@2x.png);
  1018. // background-size: 100% 100%;
  1019. }
  1020. }
  1021. }
  1022. .memberContent {
  1023. height: auto;
  1024. width: 100%;
  1025. // background: #ffffff;
  1026. border-radius: 0.28rem 0.28rem 0px 0px;
  1027. position: absolute;
  1028. left: 0;
  1029. bottom: 0;
  1030. &.animated {
  1031. animation-duration: 0.3s;
  1032. }
  1033. .blurBox {
  1034. background: rgba(0, 0, 0, 0.9);
  1035. border-radius: 0.14rem 0.14rem 0px 0px;
  1036. filter: blur(1px);
  1037. position: absolute;
  1038. width: 100%;
  1039. height: 100%;
  1040. z-index: 1;
  1041. top: 0;
  1042. left: 0;
  1043. }
  1044. .content {
  1045. position: relative;
  1046. width: 100%;
  1047. height: 100%;
  1048. z-index: 2;
  1049. top: 0;
  1050. left: 0;
  1051. }
  1052. .memberHeader {
  1053. width: 100%;
  1054. height: 1.28rem;
  1055. text-align: center;
  1056. line-height: 1.28rem;
  1057. > span {
  1058. font-size: 0.42rem;
  1059. color: #fff;
  1060. }
  1061. }
  1062. .memberList {
  1063. width: 100%;
  1064. height: 8.33rem;
  1065. border-top-style: solid;
  1066. border-top-color: rgba(0, 0, 0, 0.1);
  1067. border-top-width: 1px;
  1068. border-bottom-style: solid;
  1069. border-bottom-color: rgba(0, 0, 0, 0.1);
  1070. border-bottom-width: 1px;
  1071. box-sizing: border-box;
  1072. overflow-y: auto;
  1073. &::-webkit-scrollbar {
  1074. display: none;
  1075. }
  1076. .memberItem {
  1077. width: 100%;
  1078. height: 1.39rem;
  1079. display: flex;
  1080. align-items: center;
  1081. justify-content: space-between;
  1082. padding: 0.19rem 0.44rem;
  1083. .userMsg {
  1084. display: flex;
  1085. align-items: center;
  1086. justify-content: center;
  1087. .avatar {
  1088. width: 1rem;
  1089. height: 1rem;
  1090. border-radius: 50%;
  1091. // overflow: hidden;
  1092. margin-right: 0.28rem;
  1093. > img {
  1094. // width: 100%;
  1095. // height: 100%;
  1096. width: 1rem;
  1097. height: 1rem;
  1098. }
  1099. }
  1100. .name {
  1101. font-size: 0.39rem;
  1102. color: #fff;
  1103. }
  1104. }
  1105. .button {
  1106. display: flex;
  1107. align-items: center;
  1108. justify-content: center;
  1109. > div {
  1110. // width: 0.56rem;
  1111. // height: 0.56rem;
  1112. // background: #fc6970;
  1113. font-size: 0.65rem;
  1114. color: #fff;
  1115. &.micBtn {
  1116. width: 0.65rem;
  1117. height: 0.65rem;
  1118. margin-right: 0.3rem;
  1119. &.mute_all_mic {
  1120. background-image: url(~@/assets/images/rtcLive/mic_all_on@2x.png);
  1121. background-size: 100% 100%;
  1122. }
  1123. &.open_all_mic {
  1124. background-image: url(~@/assets/images/rtcLive/mic_all_off@2x.png);
  1125. background-size: 100% 100%;
  1126. }
  1127. &.mute_one_mic_on {
  1128. background-image: url(~@/assets/images/rtcLive/mic_on@2x.png);
  1129. background-size: 100% 100%;
  1130. }
  1131. &.mute_one_mic_off {
  1132. background-image: url(~@/assets/images/rtcLive/mic_off@2x.png);
  1133. background-size: 100% 100%;
  1134. }
  1135. &.ban_speak_on {
  1136. background-image: url(~@/assets/images/rtcLive/chat_on@2x.png);
  1137. background-size: 100% 100%;
  1138. }
  1139. &.ban_speak_off {
  1140. background-image: url(~@/assets/images/rtcLive/chat_off@2x.png);
  1141. background-size: 100% 100%;
  1142. }
  1143. }
  1144. &.outBtn {
  1145. margin-right: 0.3rem;
  1146. width: 0.65rem;
  1147. height: 0.65rem;
  1148. &.icon_remove {
  1149. background-image: url(~@/assets/images/rtcLive/remove@2x.png);
  1150. background-size: 100% 100%;
  1151. }
  1152. }
  1153. }
  1154. }
  1155. }
  1156. }
  1157. .memberBottom {
  1158. width: 100%;
  1159. padding: 0 0.44rem;
  1160. box-sizing: border-box;
  1161. display: flex;
  1162. align-items: center;
  1163. justify-content: center;
  1164. padding: 0.28rem 0;
  1165. box-sizing: border-box;
  1166. > div {
  1167. width: 4.61rem;
  1168. height: 1.11rem;
  1169. color: #ed5d18;
  1170. border-radius: 0.11rem;
  1171. border: 0.03rem solid #ed5d18;
  1172. display: flex;
  1173. align-items: center;
  1174. justify-content: center;
  1175. font-size: 0.39rem;
  1176. &.mute_all {
  1177. margin-right: 0.31rem;
  1178. }
  1179. }
  1180. }
  1181. }
  1182. }
  1183. .chat__area-layoutBox {
  1184. display: block;
  1185. width: 100%;
  1186. height: auto;
  1187. position: fixed;
  1188. bottom: 0;
  1189. left: 0;
  1190. right: unset;
  1191. top: unset;
  1192. background: #fff;
  1193. // padding-bottom: 0.821256rem;
  1194. .chat__area-layoutBox_bg {
  1195. width: 100vw;
  1196. height: 100vh;
  1197. position: fixed;
  1198. z-index: 1;
  1199. top: 0;
  1200. left: 0;
  1201. }
  1202. .chat__area-l-content {
  1203. display: -webkit-box;
  1204. display: -ms-flexbox;
  1205. display: flex;
  1206. -webkit-box-orient: horizontal;
  1207. -webkit-box-direction: normal;
  1208. -ms-flex-direction: row;
  1209. flex-direction: row;
  1210. -webkit-box-align: center;
  1211. -ms-flex-align: center;
  1212. align-items: center;
  1213. -webkit-box-pack: center;
  1214. -ms-flex-pack: center;
  1215. justify-content: center;
  1216. min-height: 0.821256rem;
  1217. padding: 0.241546rem 0;
  1218. width: 100%;
  1219. position: relative;
  1220. z-index: 100;
  1221. textarea {
  1222. width: calc(100% - 0.96618rem);
  1223. -ms-flex-preferred-size: calc(100% - 0.96618rem);
  1224. flex-basis: calc(100% - 0.96618rem);
  1225. background: none;
  1226. border: none;
  1227. border-radius: 0;
  1228. color: #000;
  1229. font-size: 0.386473rem;
  1230. line-height: 0.483092rem;
  1231. padding: 0 0.241546rem;
  1232. resize: none;
  1233. -webkit-user-select: text;
  1234. caret-color: #ff4500;
  1235. overflow: hidden !important;
  1236. }
  1237. }
  1238. }
  1239. @keyframes myAnimation {
  1240. 0% {
  1241. background-position: 0px 0px;
  1242. background-size: 13.3344rem auto;
  1243. }
  1244. 100% {
  1245. background-position: -13.1104rem 0px;
  1246. background-size: 13.3344rem auto;
  1247. }
  1248. }
  1249. }
  1250. .sharetip {
  1251. position: fixed;
  1252. z-index: 99999;
  1253. width: 100%;
  1254. height: 100%;
  1255. left: 0;
  1256. right: 0;
  1257. bottom: 0;
  1258. top: 0;
  1259. background-color: rgba($color: #000000, $alpha: 0.66);
  1260. > img {
  1261. position: absolute;
  1262. right: 16%;
  1263. top: 0;
  1264. max-width: 70vw;
  1265. }
  1266. }
  1267. </style>