<template> <div ref="container" class="call-container" :style="{ width: isFLoat ? '50%' : '100%', left: isFLoat ? '50%' : 0 }" :class="[ participant && participant.length > 1 && rightPanelVisible ? 'call-container-show-right-panel' : '', yardmanType === '2' && memberList.length < 2 ? 'call-container-remote-only' : '', isFullscreen ? 'call-container-full' : '', ]" v-show="dialling" v-drag="{ drag: 'drag-header' }" > <!-- 澶撮儴 --> <div id="drag-header" class="header" @dblclick="handleScreen"> <div class="timeTik">璋冨害鏃堕暱 {{ timeTikFormat }}</div> <div class="item time">{{ currentDatetime }}</div> <i class="item icon" :class="isFullscreen ? 'narrow' : 'fullscreen'" @click="handleScreen"></i> <i class="item icon close" @click.prevent="handleLeave"></i> </div> <!-- 闊宠棰戝尯鍩� 澶т簬绛変簬涓や釜鍙備細浜猴細瀹牸甯冨眬銆佸皬浜�2涓弬浼氫汉锛�1瀵�1甯冨眬锛�1瀵�1甯冨眬鏃跺尯鍒嗘槸鍚﹁棰戜笂鎷� remote-only 銆丆2C --> <div class="container" :class="[ memberList.length >= 2 ? 'container-' + layout : '', memberList.length < 2 && yardmanType === '2' ? 'container-remote-only' : '', memberList.length < 2 && yardmanType === '1' ? 'container-C2C' : '', ]" > <!-- 鏈湴娴佹挱鏀惧尯鍩� --> <div v-show="yardmanType === '1'" class="stream-content"> <el-avatar v-show="type === 'audio' || isMutedVideo" :size="60"> <div class="avatar">{{ currentUserProfile.nick }}</div> </el-avatar> <div class="info"> <span class="name">{{ currentUserProfile.nick }}</span> <i class="micro-status" :class="'micro-status-' + (isMutedAudio ? 'off' : 'on')"></i> </div> <video id="local-video" style="width: 100%; height: 100%; object-fit: fill" autoplay=""></video> </div> <!-- 杩滅娴佸尯鍩� --> <div v-for="item in memberListSort" :key="item.dispatchUid" :id="item.dispatchUid" class="stream-content" :class="[ type === 'audio' ? 'stream-content-audio' : '', item.state ? 'stream-content-' + item.state : 'stream-content-CONNECTING', ]" > <el-avatar v-if="type === 'audio'" v-show="item.state === 'CONNECTED'" :size="60"> <div class="avatar">{{ item.name }}</div> </el-avatar> <div class="info"> <span class="name">{{ item.name }}</span> <!-- 涓涓嶆敮鎸侊紝鍏堥殣钘� --> <!-- <i class="micro-status" :class=" 'micro-status-' + (remoteStreamList.find((x) => x.getUserId() === item.tencentUserId)?.hasAudio() ? 'on' : 'off') " ></i>--> <i v-if="item.handUp" class="icon-hand-up" @click="handup(item.dispatchUid)"></i> </div> <video :id="'remote-video-' + item.dispatchUid" :muted="mute" autoplay="" class="remote_video" :itemIndex="memberList.length" style="width: 100%; height: 100%; object-fit: fill" ></video> </div> </div> <!-- 鍙備笌浜哄垪琛� --> <participated-list v-if="participant && participant.length > 1" :visible.sync="rightPanelVisible" @showAddMember="showAddUsers = true" @muteAudio="handleMuteAudio" @muteVideo="handleMuteVideo" @kickOut="kickOut" @recall="recall" @handup="handup" @memberMute="memberMute" @memberUnmute="memberUnmute" @muteAll="muteAll" @unMuteAll="unMuteAll" ></participated-list> <!-- 搴曢儴 --> <div class="footer"> <div :class="!isMutedAudio ? 'micOn' : 'micOff'" @click="handleMuteAudio"> <i class="icon"></i> <span class="name">楹﹀厠椋�</span> </div> <div v-if="type === 'video' && yardmanType === '1'" :class="!isMutedVideo ? 'videoOn' : 'videoOff'" @click="handleMuteVideo" > <i class="icon"></i> <span class="name">鎽勫儚澶�</span> </div> <div class="refuse" @click="handleLeave"> <i class="icon"></i> <span class="name">缁撴潫</span> </div> <div v-if="memberList.length <= 9" class="layout" @click="switchLayout"> <i class="icon"></i> <span class="name">鍒囨崲甯冨眬</span> <div v-show="showSwitchLayout" class="layout-picker"> <div class="layout-type"> <div class="layout-type-img layout-type-img-3"></div> <el-radio v-model="layout" :label="3" :disabled="memberList.length >= 3">涓夊鏍�</el-radio> </div> <div class="layout-type"> <div class="layout-type-img layout-type-img-6"></div> <el-radio v-model="layout" :label="6" :disabled="memberList.length >= 6">鍏鏍�</el-radio> </div> <div class="layout-type"> <div class="layout-type-img layout-type-img-9"></div> <el-radio v-model="layout" :label="9">涔濆鏍�</el-radio> </div> </div> </div> <div v-if="memberList.length >= 2" class="members" :class="rightPanelVisible ? 'members-active' : ''" @click="setRightPanelVisible" > <i class="icon"></i> <span class="name">鎴愬憳</span> </div> </div> <!-- 娣诲姞鎴愬憳寮规 --> <un-participated-list :visible.sync="showAddUsers" @addMembers="addMembers"></un-participated-list> </div> </template> <script> /** * 闊宠棰戠殑閫昏緫鍦╩ixins:mqtt閲屽鐞嗗氨濂戒簡 * 杩欓噷鍙鐞哢I閫昏緫 */ import mqtt from "@/components/mixins/mqtt"; import screenfull from "screenfull"; import _ from "lodash"; import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; import weekday from "dayjs/plugin/weekday"; import participatedList from "@/components/mqttClient/participatedList.vue"; import UnParticipatedList from "@/components/mqttClient/unParticipatedList.vue"; import { mapMutations, mapState } from "vuex"; import { bus } from "@/components/mqttClient/utils/bus"; dayjs.locale("zh-cn"); // 鏈湴鍖� dayjs.extend(weekday); // 绗竴澶╀负鍛ㄤ竴 export default { name: "mqttClient", mixins: [mqtt], components: { UnParticipatedList, participatedList }, props: { isFLoat: { type: Boolean, default: false, } }, data() { return { dialling: false, // 鏄惁鎷ㄦ墦鐢佃瘽涓� calling: false, // 鏄惁閫氳瘽涓� isDialled: false, // 鏄惁琚懠鍙� rightPanelVisible: true, isFullscreen: false, timer: null, // 褰撳墠鏃堕棿瀹氭椂鍣� currentDatetime: dayjs().format("YYYY-MM-DD dddd HH:mm:ss"), // 褰撳墠鏃堕棿 timeTik: 0, timeTikFormat: "", timingTimer: null, openTime: null, allMembersTimer: null, connectingLimit: 60, // 鎷ㄥ彿鏃堕暱涓婇檺 鍗曚綅绉� 榛樿 1鍒嗛挓锛�60绉掞級 showAddUsers: false, layout: 3, showSwitchLayout: false, mute: false, micUserId: "", currentPage: 0, }; }, computed: { ...mapState({ account: ({ gwsd }) => gwsd.account, memberList: ({ gwsd }) => gwsd.memberList, currentUserProfile: ({ user }) => user.currentUserProfile, participant: ({ gwsd }) => gwsd.participant, yardmanType: ({ gwsd }) => gwsd.yardman.type, type: ({ gwsd }) => gwsd.yardman.callType, audio: ({ gwsd }) => gwsd.yardman.audio, video: ({ gwsd }) => gwsd.yardman.video, isMutedAudio: ({ gwsd }) => gwsd.yardman.isMutedAudio, isMutedVideo: ({ gwsd }) => gwsd.yardman.isMutedVideo, }), memberListSort() { return [...this.memberList].sort((a, b) => { return a?.state?.localeCompare(b?.state); }); }, memberListMoreSort() { return function (num) { return [...this.memberList] .slice((num - 1) * (num <= 2 ? 8 : 9), (num <= 2 ? 8 : 9) * num) .filter((x) => x !== null && x !== undefined) .sort((a, b) => a?.state?.localeCompare(b?.state)); }; }, containerTranslate() { return { transform: `translate(${-this.currentPage * 100}%,0)`, }; }, }, watch: { memberList: { handler(value) { // console.log("memberList", value); const offlineMembers = [...value].filter((x) => x.state === "DISCONNECTED").length; // console.log("offline members", offlineMembers, value.length); if (offlineMembers >= value.length && this.dialling) { this.handleLeave(); } }, deep: true, immediate: true, }, }, methods: { ...mapMutations(["SET_YARDMAN_AUDIO_MUTED", "SET_YARDMAN_VIDEO_MUTED"]), handleScreen() { if (screenfull.isEnabled) { // console.log("screen",this.$refs.container) screenfull.toggle(this.$refs.container); this.isFullscreen = !this.isFullscreen; if (!this.isFullscreen) { this.$refs.container.style.top = "0"; this.$refs.container.style.left = "50%"; this.$refs.container.style.transform = "translate(-50%, 0)"; } } }, setTimer() { this.timer = setTimeout(() => { this.currentDatetime = dayjs().format("YYYY-MM-DD dddd HH:mm:ss"); this.setTimer(); }, 1000); }, setTimingTimer() { this.timingTimer = setTimeout(() => { if (this.dialling) { this.timeTik = dayjs().diff(this.openTime) || 0; this.timeTikFormat = dayjs(this.timeTik).format("mm:ss"); } this.setTimingTimer(); }, 1000); }, setAllMembersTimer() { this.allMembersTimer = setInterval(() => { const memberList = this.memberList.map((member) => { const hasKey = Object.prototype.hasOwnProperty.call(member, "timer"); const beKickOut = member.beKickOut; if (!hasKey || beKickOut || member.state === "CONNECTED") { member.timer = 0; } else { if (member.timer < this.connectingLimit && member.state === "CONNECTING") { member.timer++; member.timeout = false; } else { // 杩涘叆浜嗚秴鏃跺垽鏂� if (member.state !== "DISCONNECTED") { member.timer = 0; member.timeout = true; member.state = "DISCONNECTED"; } } } return member; }); this.SET_MEMBER_LIST(memberList); // 鏇存柊琚皟搴︿汉鏁扮粍 // console.log( // "%c缇よ亰瀹氭椂鍣� %c鎴愬憳鍒楄〃", // "background: #E6A23C; color: #fff; padding:2px", // "background: #00cc00; color: #fff; padding:2px", // this.memberList // ); }, 1000); }, clearTimer() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } if (this.timingTimer) { clearTimeout(this.timingTimer); this.timingTimer = null; } if (this.allMembersTimer) { clearInterval(this.allMembersTimer); this.allMembersTimer = null; } this.timeTikFormat = ""; }, handleLeave: _.debounce(function () { this.clearTimer(); this.leave().then(() => { this.dialling = false; this.SET_YARDMAN_VIDEO_MUTED(false); // this.isMutedAudio = false; this.SET_YARDMAN_AUDIO_MUTED(false); this.rightPanelVisible = true; this.rightPanelActiveName = "1"; // this.$bus.$off("mqtt-call", this.videoCalling); }); }, 500), // 鍙戣捣閫氳瘽 async videoCalling() { this.layout = 3; if (this.memberList.length >= 3) { this.layout = 6; if (this.memberList.length >= 6) { this.layout = 9; } } console.log("videoCalling---", this.account); this.initClient(this.account).then(() => { try { this.dialling = true; // 鏄剧ず绐楀彛 this.initListener(); } catch (e) { console.log("videoCalling error", e); } }); }, initListener(init = true) { // 杩炴帴闊宠棰戞湇鍔″櫒鎴愬姛 this.client.on("connect", () => { if (!this.dialling) return; // 寮€濮嬩細璇濇椂寮€濮嬭鏃� this.openTime = dayjs(); this.setTimingTimer(); // 鍒嗗埆瀵规墍鏈夊弬浼氫汉璁℃椂 this.setAllMembersTimer(); // 鎵撳紑鏈湴娴� this.initLocalStream({ audio: this.audio, video: this.video, }); }); // 鍙戝竷鏈湴娴佹垚鍔� this.client.on("localVideoOpen", ({ userId, mediaId }) => { console.log("鍙戝竷鏈湴娴佹垚鍔�", userId, mediaId); // 閭€璇峰叆浼� console.log( "%c鍒氬缓绔嬩細璁嚜鍔ㄩ個璇� %c鎴愬憳鍒楄〃", "background: #E6A23C; color: #fff; padding:2px", "background: #00cc00; color: #fff; padding:2px", this.memberList ); if (init) this.inviteRemote(this.memberList, { audioonly: this.type === "audio", // 闊抽璋冨害 }); }); // 鍙戝竷鏈湴娴佸け璐� this.client.on("localVideoError", (err) => { this.$message.error(err); }); }, pagePre() { this.currentPage--; if (this.currentPage < 0) { this.currentPage = Math.ceil(this.memberList.length / 9) - 1; } }, pageNext() { this.currentPage++; if (this.currentPage > Math.ceil((this.memberList.length + 1) / 9) - 1) { this.currentPage = 0; } }, // 澶勭悊鏈湴绂侀害銆佸彇娑堢楹� handleMuteAudio: _.debounce(function () { if (this.isMutedAudio) { this.unmuteAudio(); } else { this.muteAudio(); } }, 500), handleMuteVideo: _.debounce(function () { if (this.isMutedVideo) { this.unmuteVideo(); } else { this.muteVideo(); } }, 500), switchLayout: _.debounce(function () { this.showSwitchLayout = !this.showSwitchLayout; }, 500), setRightPanelVisible: _.debounce(function () { this.rightPanelVisible = !this.rightPanelVisible; }, 500), // 鏂板鎴愬憳鍚庡洖璋� addMembers(memberList) { this.inviteRemote(memberList, { audioonly: this.type === "audio", // 闊抽璋冨害鎴� }); if (this.memberList.length >= 3) { this.layout = 6; if (this.memberList.length >= 6) { this.layout = 9; } } }, memberMute(id) { this.muteRemoteAudio(id); }, memberUnmute(id) { this.unmuteRemoteAudio(id); }, muteAll() { this.memberList.forEach((item) => { this.muteRemoteAudio(item.dispatchUid); }); }, unMuteAll() { this.memberList.forEach((item) => { this.unmuteRemoteAudio(item.dispatchUid); }); }, kickOut(id) { this.kickOutRemote(id); }, recall(dispatchUid) { this.inviteRemote([{ dispatchUid }], { audioonly: this.type === "audio", // 闊抽璋冨害 }); }, handup(dispatchUid) { this.handUpRemote(dispatchUid); }, }, created() { this.setTimer(); }, mounted() { this.$bus.$on("mqtt-call", this.videoCalling); // 鍙戣捣閫氳瘽 // 杩滅娴佸姞鍏� bus.$on("pull_video_message_new_remote", (ack) => { console.log("pull_video_message_new_remote", ack); if (ack.length > 0) { this.pullVideoFromServer(ack[0].id); } }); // 鐩戝惉杩滅绂诲紑 bus.$on("remote_HangUp", (ack) => { console.log("鐩戝惉杩滅绂诲紑", ack); // 杩欓噷ack閲岄潰鏄繙绔寮€鐨剈id 鐒跺悗浣犲彲浠ュ啓濂圭寮€鐨勯€昏緫 this.pullVideoLeave(ack); }); }, beforeDestroy() { this.$bus.$off("mqtt-call", this.videoCalling); bus.$off("pull_video_message_new_remote"); bus.$off("remote_HangUp"); }, destroyed() { this.clearTimer(); }, }; </script> <style lang="scss" scoped> @import "~@/styles/rtc-client.scss"; </style>