主持人参会页面

This commit is contained in:
cxc
2022-05-18 17:25:43 +08:00
parent d22348bd24
commit a9a9dca090
13 changed files with 961 additions and 224 deletions

View File

@ -1,7 +1,28 @@
<template>
<div id="app-container" ref="appContainer">
<div class="meeting-container" ref="meetingContainerRef">
<div id="video-element"></div>
<div id="app-container" v-loading="isMeetingLoading" ref="appContainer">
<div class="row">
<div class="meeting-container" ref="meetingContainerRef">
<div id="video-element" ref="videoElementRef"></div>
<el-button @click="setFullScreen" class="fullscreen-btn">
<!-- <FullScreen></FullScreen> -->
全屏</el-button
>
</div>
<div class="chat-right" ref="chatRightRef">
<messageList :messageList="messages"></messageList>
<div class="option-bar">
<el-input
v-model="editingMessage"
style="margin-right: 15px"
></el-input>
<el-button
type="success"
@click="sendMessage"
:disabled="editingMessage.length === 0"
>发送</el-button
>
</div>
</div>
</div>
<el-tabs class="tabs" type="border-card">
@ -17,15 +38,15 @@
<el-tab-pane label="专家介绍"
><div class="meeting-info expert-info" v-html="expertInfo"></div
></el-tab-pane>
<el-tab-pane label="聊天"
<el-tab-pane label="聊天" class="chat-pane" v-if="screenWidth < 768"
><div class="meeting-info chat">
<messageList :messageList="messages"></messageList>
<div class="option-bar">
<wangEditor
<el-input
v-model="editingMessage"
height="100px"
width="80%"
></wangEditor>
style="margin-right: 15px"
></el-input>
<!-- <svg-icon :icon-class="search" style="height: 30px; width: 16px" /> -->
<el-button
type="success"
@click="sendMessage"
@ -55,58 +76,70 @@
</el-dialog>
<questions
mode="1"
v-if="showExamDialog"
:showDialog="showExamDialog"
@close="showExamDialog = $event"
></questions>
<questions
mode="2"
v-if="showQuestionnaireDialog"
:showDialog="showQuestionnaireDialog"
@close="showQuestionnaireDialog = $event"
></questions>
</div>
</template>
<script setup>
import { computed, reactive, watch, ref, onMounted } from "vue";
import { computed, reactive, watch, ref, onMounted, onUnmounted } from "vue";
import { useStore } from "vuex";
import { signMeeting, generateSignature } from "@/api/meeting";
import wangEditor from "@/components/wangEditor";
import questions from "@/components/questions";
import messageList from "@/components/messageList";
import dayjs from "dayjs";
import ZoomMtgEmbedded from "@zoomus/websdk/embedded";
import axios from "axios";
import _ from "lodash";
// import { FullScreen } from "@element-plus/icons-vue";
import ReconnectingWebSocket from "reconnecting-websocket";
import { ElMessage, ElMessageBox } from "element-plus";
import { useRoute } from "vue-router";
const route = useRoute();
const store = useStore();
const meetingNote = computed(() => store.getters.meetingNote);
const meetingSchedule = computed(() => store.getters.meetingSchedule);
const expertInfo = computed(() => store.getters.expertInfo);
const templateId = ref(1);
console.log(route.name);
const meetingNote = computed(() => store.getters.meetingNote); // 会议介绍
const meetingSchedule = computed(() => store.getters.meetingSchedule); // 会议日程
const expertInfo = computed(() => store.getters.expertInfo); // 专家信息
const isMeetingLoading = ref(true); // 视频课件是否正在加载
const templateId = ref(1); // 布局 id
if (store.getters.templateId) {
templateId.value = store.getters.templateId;
}
const joinAccount = ref("");
joinAccount.value =
store.getters.nickname || store.getters.phone || store.getters.icCard;
const meetingWidth = ref(0);
const meetingHeight = ref(0);
const joinAccount = ref(""); // 参会账号
const joinName = ref(""); // 参会名称
const screenWidth = ref(0); // 屏幕宽度
const meetingWidth = ref(0); // 视频课件容器元素宽度
const meetingHeight = ref(0); // 视频课件容器元素高度
screenWidth.value = document.body.offsetWidth;
if (route.name === "Host") {
joinAccount.value = route.params.username;
joinName.value = route.params.nickname;
} else {
joinAccount.value =
store.getters.icCard || store.getters.phone || store.getters.nickname;
joinName.value =
store.getters.nickname || store.getters.phone || store.getters.icCard;
}
// 设置会议背景大小、图片
const meetingContainerRef = ref(null);
const meetingContainerRef = ref(null); // 会议容器元素(包含背景)
const videoElementRef = ref(null); // 视频课件容器元素
const chatRightRef = ref(null); // 右侧聊天元素
onMounted(() => {
meetingContainerRef.value.style.background = `url(${store.getters.templateBackgroundPic}) 0% 0% / cover no-repeat`;
meetingContainerRef.value.style.background = `url(${store.getters.templateBackgroundPic}) 0% 0% / cover no-repeat`; // 设置背景图片
setTextLabel();
console.log(meetingContainerRef.value.offsetWidth);
meetingWidth.value = meetingContainerRef.value.offsetWidth * 0.8;
meetingHeight.value = (meetingWidth.value * 3) / 4;
document.querySelector("#video-element").style.width =
meetingWidth.value + "px";
document.querySelector("#video-element").style.height =
meetingHeight.value + "px";
meetingWidth.value = meetingContainerRef.value.offsetWidth * 0.95;
meetingHeight.value = (meetingWidth.value * 9) / 16;
videoElementRef.value.style.width = meetingWidth.value + "px";
videoElementRef.value.style.height = meetingHeight.value + "px";
chatRightRef.value.style.height = meetingHeight.value + "px";
});
// 设置文本标签
@ -117,31 +150,38 @@ const setTextLabel = () => {
store.getters.textLabelList.forEach((item) => {
const labelObj = JSON.parse(item.textLabel);
const textEl = document.createElement("div");
console.log(labelObj);
textEl.id = `${_.uniqueId("tag-")}`;
textEl.innerHTML = labelObj.content;
textEl.className = "text-tag";
console.log(labelObj);
// console.log(labelObj);
textEl.style.backgroundColor = labelObj.backgroundColor;
textEl.style.visibility =
labelObj.visibility === "1" ? "visible" : "hidden";
textEl.style.left = `${labelObj.x}`;
textEl.style.top = `${labelObj.y}`;
document.querySelector(".meeting-container").appendChild(textEl);
meetingContainerRef.value.appendChild(textEl);
});
};
// console.log(to.);
// 会议配置
const meetingConfig = reactive({
client: ZoomMtgEmbedded.createClient(),
// This Sample App has been updated to use SDK App type credentials https://marketplace.zoom.us/docs/guides/build/sdk-app
sdkKey: "TUgbQFx4RhPHDPa0OQAKVgPL2dPGQoVqZIuW",
meetingNumber: store.getters.meetingNumber,
passWord: store.state.password,
role: 0,
role: route.name === "Host" ? 1 : 0,
// signatureEndpoint: "http://120.26.107.74:4000",
userEmail: "",
userName: joinAccount.value,
userName: joinName.value,
// pass in the registrant's token if your meeting or webinar requires registration. More info here:
// Meetings: https://marketplace.zoom.us/docs/sdk/native-sdks/web/component-view/meetings#join-registered
// Webinars: https://marketplace.zoom.us/docs/sdk/native-sdks/web/component-view/webinars#join-registered
registrantToken: "",
});
console.log(meetingConfig);
const checkMedia = async () => {
const result = {};
try {
@ -158,94 +198,94 @@ const checkMedia = async () => {
}
return result;
};
// const signature = ref("");
const getSignature = async () => {
const { sign } = await generateSignature({
meetingNumber: store.getters.meetingNumber,
role: meetingConfig.role,
});
// signature.value = sign;
startMeeting(sign);
// const res = await checkMedia();
// if (!res.audio || !res.video) {
// return ElMessageBox.alert(
// `摄像头状态 : ${res.video ? "可用" : "禁用"}\n麦克风状态 : ${
// res.audio ? "可用" : "禁用"
// }`
// );
// }
};
const startMeeting = async (signature) => {
let meetingSDKElement = document.getElementById("video-element");
console.log(meetingConfig);
meetingConfig.client.init({
debug: true,
zoomAppRoot: meetingSDKElement,
language: "zh-CN",
customize: {
video: {
isResizable: false,
popper: {
disableDraggable: true,
},
viewSizes: {
default: {
width: meetingWidth.value,
height: meetingHeight.value,
try {
meetingConfig.client.init({
debug: true,
zoomAppRoot: meetingSDKElement,
language: "zh-CN",
customize: {
video: {
isResizable: false,
popper: {
disableDraggable: true,
},
viewSizes: {
default: {
width: meetingWidth.value,
height: meetingHeight.value - 45,
},
},
},
},
meetingInfo: [
"topic",
"host",
"mn",
"pwd",
"telPwd",
"invite",
"participant",
"dc",
"enctype",
],
// toolbar: {
// buttons: [
// {
// text: "Custom Button",
// className: "CustomButton",
// onClick: () => {
// console.log("custom button");
// },
// },
// ],
// },
},
});
await meetingConfig.client.join({
sdkKey: meetingConfig.sdkKey,
signature: signature,
meetingNumber: meetingConfig.meetingNumber,
password: meetingConfig.passWord,
userName: meetingConfig.userName,
userEmail: meetingConfig.userEmail,
tk: meetingConfig.registrantToken,
});
meetingInfo: [
"topic",
"host",
"mn",
"pwd",
"telPwd",
"invite",
"participant",
"dc",
"enctype",
],
},
});
await meetingConfig.client.join({
sdkKey: meetingConfig.sdkKey,
signature: signature,
meetingNumber: meetingConfig.meetingNumber,
password: meetingConfig.passWord,
userName: meetingConfig.userName,
userEmail: meetingConfig.userEmail,
tk: meetingConfig.registrantToken,
});
initDesktopLayout();
isMeetingLoading.value = false;
} catch (error) {
isMeetingLoading.value = false;
}
// console.log(meetingConfig.client.checkSystemRequirements());
initDesktopLayout();
// document.querySelector("#suspension-view-tab-thumbnail-speaker").click();
};
const setFullScreen = () => {
videoElementRef.value.requestFullscreen();
};
onMounted(() => {
getSignature();
});
// 初始化
const initDesktopLayout = () => {
console.log(meetingHeight.value);
// 切换到 gallery view
document.querySelector("#suspension-view-tab-thumbnail-gallery").click();
// 初始化高度
document.querySelector(".MuiBox-root.jss167.jss161.jss165").style.height = `${
meetingHeight.value - 45
}px`;
const heightEl =
document.querySelector(
".zmwebsdk-MuiBox-root.zmwebsdk-MuiBox-root.zmwebsdk-makeStyles-root-166.zmwebsdk-makeStyles-root-170"
) ||
document.querySelector(
".zmwebsdk-MuiBox-root.zmwebsdk-MuiBox-root.zmwebsdk-makeStyles-root-176.zmwebsdk-makeStyles-root-180"
);
heightEl.style.height = `${meetingHeight.value - 45}px`;
// document.querySelector(
// ".zmwebsdk-MuiBox-root.zmwebsdk-MuiBox-root.zmwebsdk-makeStyles-root-166.zmwebsdk-makeStyles-root-170"
// ).style.height = `${meetingHeight.value - 45}px`;
// 初始化宽度
//
document.querySelector(
".MuiPaper-root.jss25.MuiPaper-elevation1.MuiPaper-rounded"
".zmwebsdk-MuiPaper-root.zmwebsdk-makeStyles-root-21.zmwebsdk-MuiPaper-elevation1.zmwebsdk-MuiPaper-rounded"
).style.width = `${meetingWidth.value - 4}px`;
// 加载完成显示 video element
document.querySelector("#video-element").style.visibility = "visible";
@ -253,44 +293,49 @@ const initDesktopLayout = () => {
// 检测屏幕共享开启状态变化
const inSharing = ref(false); // 是否开启屏幕共享
setInterval(() => {
const elSharing = document.querySelector(".jss51");
const elSharing = document.querySelector(".zmwebsdk-makeStyles-inSharing-46");
// console.log(elSharing);
if (elSharing) {
if (elSharing.className.includes("in-sharing")) {
inSharing.value = true;
} else {
inSharing.value = false;
}
// if (elSharing.className.includes("in-sharing")) {
// inSharing.value = true;
// } else {
// inSharing.value = false;
// }
inSharing.value = true;
} else {
inSharing.value = false;
}
}, 500);
// 根据id设置布局
const setLayout = (templateId) => {
console.log(templateId);
document.querySelector(
".MuiBox-root.jss167.jss161.jss165"
).style.flexDirection = "";
const v_s_wrap_el =
document.querySelector(
".zmwebsdk-MuiBox-root.zmwebsdk-MuiBox-root.zmwebsdk-makeStyles-root-166.zmwebsdk-makeStyles-root-170"
) ||
document.querySelector(
".zmwebsdk-MuiBox-root.zmwebsdk-MuiBox-root.zmwebsdk-makeStyles-root-176.zmwebsdk-makeStyles-root-180"
);
v_s_wrap_el.style.flexDirection = "";
const v_wrap_el =
document.querySelector(
".zmwebsdk-makeStyles-wrap-167.zmwebsdk-makeStyles-wrap-171"
) ||
document.querySelector(
".zmwebsdk-makeStyles-wrap-177.zmwebsdk-makeStyles-wrap-181"
);
if (templateId === "1" && inSharing.value) {
document.querySelector(".jss162.jss166").style.width = `${
meetingWidth.value / 2 - 2
}px`;
v_wrap_el.style.width = `${meetingWidth.value / 2 - 2}px`;
} else if (templateId === "2" && inSharing.value) {
document.querySelector(".jss162.jss166").style.width = `${
meetingWidth.value / 2 - 2
}px`;
document.querySelector(
".MuiBox-root.jss167.jss161.jss165"
).style.flexDirection = "row-reverse";
v_wrap_el.style.width = `${meetingWidth.value / 2 - 2}px`;
v_s_wrap_el.style.flexDirection = "row-reverse";
} else if (templateId === "3" && inSharing.value) {
document.querySelector(".jss162.jss166").style.width = `${
(meetingWidth.value - 4) / 4
}px`;
v_wrap_el.style.width = `${(meetingWidth.value - 4) / 4}px`;
} else if (templateId === "4" && inSharing.value) {
document.querySelector(".jss162.jss166").style.width = `${
(meetingWidth.value - 4) / 4
}px`;
document.querySelector(
".MuiBox-root.jss167.jss161.jss165"
).style.flexDirection = "row-reverse";
v_wrap_el.style.width = `${(meetingWidth.value - 4) / 4}px`;
v_s_wrap_el.style.flexDirection = "row-reverse";
}
};
watch(inSharing, (newVal) => {
@ -304,24 +349,79 @@ watch(inSharing, (newVal) => {
"#suspension-view-tab-thumbnail-gallery"
);
galleryViewButton.click();
document.querySelector(".jss162.jss166").style.width = "";
document.querySelector(".zmwebsdk-makeStyles-wrap-171").style.width = "";
document.querySelector(
".MuiBox-root.jss167.jss161.jss165"
".zmwebsdk-MuiBox-root.zmwebsdk-makeStyles-root-166.zmwebsdk-makeStyles-root-170"
).style.flexDirection = "";
}
});
getSignature();
// 建立websocket连接
const socket = new WebSocket(
`ws://118.195.192.58:1618/websocket/meeting/${store.getters.meetingId}/${joinAccount.value}`
);
socket.addEventListener("open", () => {
// socket.send("Hello Server!");
console.log("websocket,已连接");
});
// const reconnectCount = ref(0); //重连次数
let socket = reactive({});
const initWebSocket = () => {
// 建立websocket连接
socket = new ReconnectingWebSocket(
`wss://meeting.chuhuankj.com/wss/websocket/meeting/${store.getters.meetingId}/${joinAccount.value}`
);
socket.addEventListener("open", () => {
// socket.send("Hello Server!");
console.log("websocket,已连接");
});
// 监听websocket消息
socket.addEventListener("message", async (event) => {
const data = JSON.parse(JSON.parse(event.data));
console.log(data);
// 会议信息更新时
if (data.type === "isRefreshMeeting") {
await store.dispatch("getMeetingInfo", store.getters.meetingId);
meetingContainerRef.value.style.background = ` url(${store.getters.templateBackgroundPic}) 0% 0% / cover no-repeat`;
templateId.value = store.getters.templateId;
setLayout(templateId.value);
setTextLabel();
}
// 开始签到时
else if (data.type === "isStartSign") {
console.log(data);
showSignDialog.value = true;
}
// 签到结束时
else if (data.type === "isEndSign") {
showSignDialog.value = false;
}
// 收到聊天消息时
else if (data.type === "isChat") {
console.log(JSON.parse(data.content));
messages.value.push({
...JSON.parse(data.content),
id: _.uniqueId(),
time: dayjs().format("YYYY-MM-DD HH:mm:ss"),
});
}
// 开始考试时
else if (data.type === "isStartExam") {
showExamDialog.value = true;
}
// 开始问卷时
else if (data.type === "isStartQuestionnaire") {
showQuestionnaireDialog.value = true;
}
// 会议结束时
else if (data.type === "isCloseMeeting") {
showExamDialog.value = false;
showQuestionnaireDialog.value = false;
ElMessageBox.alert("会议已结束");
socket.close();
}
});
socket.addEventListener("close", (event) => {
console.log(event, "close");
});
socket.addEventListener("error", (event) => {
console.log(event, "error");
});
};
initWebSocket();
/* 签到功能 */
// 提交签到
const showSignDialog = ref(false); //是否显示签到窗口
@ -335,52 +435,6 @@ const submitSign = async () => {
ElMessage.success("签到成功");
};
// 监听websocket消息
socket.addEventListener("message", async (event) => {
const data = JSON.parse(JSON.parse(event.data));
console.log(data);
// 会议信息更新时
if (data.type === "isRefreshMeeting") {
await store.dispatch("getMeetingInfo", store.getters.meetingId);
meetingContainerRef.value.style.background = ` url(${store.getters.templateBackgroundPic}) 0% 0% / cover no-repeat`;
templateId.value = store.getters.templateId;
setLayout(templateId.value);
setTextLabel();
}
// 开始签到时
else if (data.type === "isStartSign") {
console.log(data);
showSignDialog.value = true;
}
// 签到结束时
else if (data.type === "isEndSign") {
showSignDialog.value = false;
}
// 收到聊天消息时
else if (data.type === "isChat") {
console.log(JSON.parse(data.content));
messages.value.push({
...JSON.parse(data.content),
id: _.uniqueId(),
time: dayjs().format("YYYY-MM-DD HH:mm:ss"),
});
}
// 开始考试时
else if (data.type === "isStartExam") {
showExamDialog.value = true;
}
// 开始问卷时
else if (data.type === "isStartQuestionnaire") {
showQuestionnaireDialog.value = true;
}
// 会议结束时
else if (data.type === "isCloseMeeting") {
showExamDialog.value = false;
showQuestionnaireDialog.value = false;
ElMessageBox.alert("会议已结束");
}
});
/* 聊天功能 */
const messages = ref([]); // 消息列表
const editingMessage = ref(""); // 正在编辑的内容
@ -390,7 +444,7 @@ const sendMessage = () => {
socket.send(editingMessage.value);
messages.value.push({
id: _.uniqueId(),
account: joinAccount.value,
account: joinName.value,
msg: editingMessage.value,
isMe: true,
time: dayjs().format("YYYY-MM-DD HH:mm:ss"),
@ -398,13 +452,21 @@ const sendMessage = () => {
editingMessage.value = "";
};
const leaveConference = () => {
meetingConfig.client.leaveMeeting();
};
window.addEventListener("beforeunload", leaveConference);
onUnmounted(() => {
window.removeEventListener("beforeunload", leaveConference);
});
// 是否显示考试和问卷弹窗
const showExamDialog = ref(false);
const showQuestionnaireDialog = ref(false);
</script>
<style scoped lang="scss">
#app-container {
width: 85%;
width: 100%;
margin: 0 auto;
:deep(.text-tag) {
position: absolute;
@ -418,14 +480,49 @@ const showQuestionnaireDialog = ref(false);
}
}
}
.row {
width: 100%;
display: flex;
}
.meeting-container {
position: relative;
width: 100%;
width: 80%;
display: flex;
justify-content: center;
}
.chat-right {
width: 20%;
display: flex;
flex-direction: column;
justify-content: space-between;
.option-bar {
margin-top: 0;
margin: 5px;
}
:deep(.message-list) {
overflow-y: scroll;
margin-bottom: 0;
// height: 80%;
flex: 1;
p {
margin: 0;
}
}
}
@media screen and (max-width: 768px) {
.meeting-container {
width: 100%;
}
.chat-right {
display: none;
}
}
#video-element {
visibility: hidden;
// height: 600px;
}
.tabs {
@ -435,25 +532,31 @@ const showQuestionnaireDialog = ref(false);
margin: 0;
}
}
:deep(.el-tabs__content) {
.chat {
.option-bar {
display: flex;
align-items: center;
justify-content: space-around;
}
}
}
// :deep(.el-tabs__content) {
// .chat {
// }
// }
}
:deep(.MuiPaper-root) {
.option-bar {
display: flex;
align-items: center;
justify-content: space-around;
}
:deep(.zmwebsdk-MuiPaper-root) {
background: transparent;
box-shadow: 0 0;
}
:deep(.MuiToolbar-root) {
:deep(.zmwebsdk-MuiToolbar-root) {
display: none;
}
:deep(.MuiPaper-root.jss2.jss9.MuiPaper-elevation1.MuiPaper-rounded) {
:deep(.zmwebsdk-makeStyles-singleView-7) {
// background-color: transparent;
padding: 0;
}
.fullscreen-btn {
position: absolute;
right: 0;
bottom: 0;
}
</style>