PageRtcLive.vue 34 KB

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