feat:更新dev代码
This commit is contained in:
43
.gitea/workflows/deploy.yaml
Normal file
43
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: Deploy To Dev
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'dev'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: gx-dev01
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Copy specific directory to runner's host machine
|
||||||
|
run: |
|
||||||
|
TARGET_DIR="./dist"
|
||||||
|
|
||||||
|
# 检查 dist 目录是否存在
|
||||||
|
if [ ! -d "$TARGET_DIR" ]; then
|
||||||
|
echo "Error: The source directory '$TARGET_DIR' does not exist in the repository."
|
||||||
|
# 如果目录不存在,报错并退出当前步骤
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cp -r ./dist/* /data/xsy-www/
|
||||||
|
|
||||||
|
- name: Find and restart the app container
|
||||||
|
run: |
|
||||||
|
# 1. 使用 docker ps 过滤包含 'xsy-lighttpd' 服务的容器
|
||||||
|
# 2. 提取容器 ID 或名称
|
||||||
|
CONTAINER_ID=$(docker ps -a --filter "name=xsy-lighttpd" --format "{{.ID}}")
|
||||||
|
|
||||||
|
if [ -z "$CONTAINER_ID" ]; then
|
||||||
|
echo "Error: Could not find any container matching name 'app1'."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Found container ID: $CONTAINER_ID. Restarting..."
|
||||||
|
# 使用标准的 docker restart 命令
|
||||||
|
docker restart "$CONTAINER_ID"
|
||||||
|
echo "Container restarted successfully."
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -83,8 +83,7 @@ const clickJoin = async () => {
|
|||||||
inviteDialog.value = false
|
inviteDialog.value = false
|
||||||
router.push({
|
router.push({
|
||||||
path: '/conferencingRoom',
|
path: '/conferencingRoom',
|
||||||
query:{
|
query:{
|
||||||
type:2,//创建房间,加入房间 2
|
|
||||||
room_uid:socketInformation.value.room_uid
|
room_uid:socketInformation.value.room_uid
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -348,8 +348,7 @@ function handleFileUploadMessage(payload, topic){
|
|||||||
try {
|
try {
|
||||||
const messageStr = payload.toString()
|
const messageStr = payload.toString()
|
||||||
const data = JSON.parse(messageStr)
|
const data = JSON.parse(messageStr)
|
||||||
// emitter.emit('fileUploadStatus')
|
// emitter.emit('fileUploadStatus')
|
||||||
console.log(data,'data文件转换完成,预览通知')
|
|
||||||
const userId = JSON.parse(sessionStorage.getItem('userData'))?.uid
|
const userId = JSON.parse(sessionStorage.getItem('userData'))?.uid
|
||||||
if(dialogFileVisible.value){
|
if(dialogFileVisible.value){
|
||||||
dialogFileVisible.value = false
|
dialogFileVisible.value = false
|
||||||
@@ -422,7 +421,7 @@ function handlePdfMessage(payload, topic){
|
|||||||
case 'converting':
|
case 'converting':
|
||||||
break
|
break
|
||||||
case 'completed':
|
case 'completed':
|
||||||
getConvertedFile(data.task_id)
|
getConvertedFile(data.task_id,data.room_uid)
|
||||||
break
|
break
|
||||||
case 'failed':
|
case 'failed':
|
||||||
break
|
break
|
||||||
@@ -436,12 +435,12 @@ function handlePdfMessage(payload, topic){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取转换后的文件
|
// 获取转换后的文件
|
||||||
const getConvertedFile = async (taskId) => {
|
const getConvertedFile = async (taskId,roomId) => {
|
||||||
try {
|
try {
|
||||||
if (!taskId) {
|
if (!taskId) {
|
||||||
throw new Error('任务ID不存在')
|
throw new Error('任务ID不存在')
|
||||||
}
|
}
|
||||||
const fileRes = await getConvertStatusApi(taskId,props.roomId)
|
const fileRes = await getConvertStatusApi(taskId,roomId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取转换文件失败:', err)
|
console.error('获取转换文件失败:', err)
|
||||||
handleError('获取转换文件失败', err)
|
handleError('获取转换文件失败', err)
|
||||||
|
|||||||
@@ -457,6 +457,7 @@ const loading = ref(false);
|
|||||||
let lastPublishTime = 0;
|
let lastPublishTime = 0;
|
||||||
const publishThrottleTime = 100; // 100ms 节流
|
const publishThrottleTime = 100; // 100ms 节流
|
||||||
|
|
||||||
|
|
||||||
// 鼠标状态跟踪
|
// 鼠标状态跟踪
|
||||||
const mouseState = reactive({
|
const mouseState = reactive({
|
||||||
isDrawing: false,
|
isDrawing: false,
|
||||||
@@ -1148,8 +1149,9 @@ async function initMqttFilePreview(){
|
|||||||
const clientId = `filePreview_${Date.now()}`
|
const clientId = `filePreview_${Date.now()}`
|
||||||
await mqttClient.connect(clientId)
|
await mqttClient.connect(clientId)
|
||||||
isMqttFilePreview.value = true
|
isMqttFilePreview.value = true
|
||||||
// 订阅主题
|
// 订阅主题
|
||||||
emitter.emit('subscribeToFilePreviewTopic',{roomId:roomId.value})
|
emitter.emit('subscribeToFilePreviewTopic',{roomId:roomId.value})
|
||||||
|
emitter.emit('roomces',{roomId:roomId.value})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('MQTT连接失败:', error)
|
console.error('MQTT连接失败:', error)
|
||||||
ElMessage.error('文件上传服务连接失败')
|
ElMessage.error('文件上传服务连接失败')
|
||||||
@@ -1903,11 +1905,8 @@ function handleRemoteWhiteboardOpen(data) {
|
|||||||
|
|
||||||
// 处理远程关闭白板
|
// 处理远程关闭白板
|
||||||
function handleRemoteWhiteboardClose(data) {
|
function handleRemoteWhiteboardClose(data) {
|
||||||
ElMessage.info(`${data.senderName || data.sender} 关闭了白板`);
|
ElMessage.info(`${data.senderName || data.sender} 关闭了白板`);
|
||||||
// if(data.roomType == '1'){
|
//当远程用户关闭白板时,如果本地有屏幕共享,确保屏幕共享重新显示
|
||||||
// isWhiteboardActive.value = false;
|
|
||||||
// }
|
|
||||||
//当远程用户关闭白板时,如果本地有屏幕共享,确保屏幕共享重新显示
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (hasActiveScreenShare.value && !isWhiteboardActive.value) {
|
if (hasActiveScreenShare.value && !isWhiteboardActive.value) {
|
||||||
activateLayer('screenVideo',false);
|
activateLayer('screenVideo',false);
|
||||||
@@ -1952,8 +1951,7 @@ async function handleConfirmSelection(userInfo){
|
|||||||
function publishWhiteboardMessage(type, payload = {}) {
|
function publishWhiteboardMessage(type, payload = {}) {
|
||||||
try {
|
try {
|
||||||
const message = {
|
const message = {
|
||||||
type,
|
type,
|
||||||
roomType: route.query.type,//用于判断是参与者还是发起者
|
|
||||||
roomId: roomId.value,
|
roomId: roomId.value,
|
||||||
sender: hostUid.value,
|
sender: hostUid.value,
|
||||||
senderName: hostUid.value,
|
senderName: hostUid.value,
|
||||||
@@ -2115,22 +2113,34 @@ function setupRoomListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 事件处理函数
|
// 事件处理函数
|
||||||
|
/**
|
||||||
|
* 处理房间连接成功事件
|
||||||
|
* 1. 初始化房间ID
|
||||||
|
* 2. 初始化各种MQTT连接(白板、激光笔、文件上传等)
|
||||||
|
* 3. 更新连接状态
|
||||||
|
* 4. 初始化已存在的远程参与者
|
||||||
|
*/
|
||||||
async function handleConnected() {
|
async function handleConnected() {
|
||||||
|
// 设置当前房间ID
|
||||||
roomId.value = room.name
|
roomId.value = room.name
|
||||||
await initMqtt();
|
|
||||||
await initToLaserPointerMqtt();
|
// 初始化各种MQTT服务
|
||||||
await initMqttFileUploadSucc() ;
|
await initMqtt(); // 白板MQTT
|
||||||
await initMqttFileConversionStatus();
|
await initToLaserPointerMqtt(); // 激光笔MQTT
|
||||||
await initMqttFilePreview();
|
await initMqttFileUploadSucc(); // 文件上传成功MQTT
|
||||||
await initMqttEnlargeVideo();
|
await initMqttFileConversionStatus(); // 文件转换状态MQTT
|
||||||
|
await initMqttFilePreview(); // 文件预览MQTT
|
||||||
|
await initMqttEnlargeVideo(); // 视频放大MQTT
|
||||||
|
|
||||||
|
// 更新连接状态
|
||||||
status.value = false;
|
status.value = false;
|
||||||
ElMessage.success('已成功连接到房间');
|
ElMessage.success('已成功连接到房间');
|
||||||
// 初始化现有远程参与者
|
|
||||||
|
// 初始化已存在的远程参与者
|
||||||
room.remoteParticipants.forEach(participant => {
|
room.remoteParticipants.forEach(participant => {
|
||||||
addRemoteParticipant(participant);
|
addRemoteParticipant(participant); // 添加参与者
|
||||||
setupParticipantListeners(participant);
|
setupParticipantListeners(participant); // 设置监听器
|
||||||
// 立即检查并更新参与者的轨道状态
|
updateParticipantTracks(participant); // 更新轨道状态
|
||||||
updateParticipantTracks(participant);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2838,35 +2848,7 @@ function handleScreenShareEnded() {
|
|||||||
ElMessage.info('屏幕共享已停止');
|
ElMessage.info('屏幕共享已停止');
|
||||||
// 移除事件监听器
|
// 移除事件监听器
|
||||||
room.localParticipant.off('screenShareEnded', handleScreenShareEnded);
|
room.localParticipant.off('screenShareEnded', handleScreenShareEnded);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function joinRoomBtn() {
|
|
||||||
try {
|
|
||||||
loading.value = true; // 开始加载
|
|
||||||
status.value = true; // 确保显示加载状态
|
|
||||||
|
|
||||||
const res = await getRoomToken({max_participants: 20});
|
|
||||||
if(res.meta.code != 200){
|
|
||||||
ElMessage.error(res.meta.message);
|
|
||||||
loading.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const token = res.data.access_token;
|
|
||||||
roomName.value = res.data.room.name
|
|
||||||
if (!token) {
|
|
||||||
throw new Error('获取 token 失败');
|
|
||||||
}
|
|
||||||
setupRoomListeners();
|
|
||||||
await room.connect(wsURL, token, {
|
|
||||||
autoSubscribe: true,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error(`连接失败: ${error.message}`);
|
|
||||||
status.value = true;
|
|
||||||
} finally {
|
|
||||||
loading.value = false; // 无论成功失败都结束加载
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 离开会议函数
|
// 离开会议函数
|
||||||
async function leaveRoom() {
|
async function leaveRoom() {
|
||||||
@@ -3105,19 +3087,15 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if(route.query.type == '1'){
|
try {
|
||||||
await joinRoomBtn()
|
loading.value = true; // 开始加载
|
||||||
hostUid.value = roomStore.userUid
|
status.value = true; // 确保显示加载状态
|
||||||
// 邀请用户参与房间
|
|
||||||
if(room.name){
|
|
||||||
await getInvite(room.name,{participants:[{user_uid:roomStore.detailUid,display_name:roomStore.detailName}], participant_role: "participant"})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const res = await getTokenApi(route.query.room_uid)
|
const res = await getTokenApi(route.query.room_uid)
|
||||||
if(res.meta.code == 200){
|
if(res.meta.code == 200){
|
||||||
const token = res.data.access_token;
|
const token = res.data.access_token;
|
||||||
hostUid.value = res.data.user_uid
|
hostUid.value = res.data.user_uid
|
||||||
await nextTick();
|
roomName.value = res.data.room_name
|
||||||
|
await nextTick();
|
||||||
setupRoomListeners();
|
setupRoomListeners();
|
||||||
await room.connect(wsURL, token, {
|
await room.connect(wsURL, token, {
|
||||||
autoSubscribe: true,
|
autoSubscribe: true,
|
||||||
@@ -3125,7 +3103,12 @@ onMounted(async () => {
|
|||||||
}else{
|
}else{
|
||||||
ElMessage.error(res.meta.message);
|
ElMessage.error(res.meta.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(`连接失败: ${error.message}`);
|
||||||
|
status.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false; // 无论成功失败都结束加载
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<div class="screen-share-video">
|
<div class="screen-share-video">
|
||||||
<!-- 放大视频模式 -->
|
<!-- 放大视频模式 -->
|
||||||
<div v-if="enlargedParticipant" class="enlarged-video-container">
|
<div v-if="enlargedParticipant" class="enlarged-video-container">
|
||||||
<div class="video-wrapper enlarged-video-wrapper" style='border:1px solid red'>
|
<div class="video-wrapper enlarged-video-wrapper">
|
||||||
<video
|
<video
|
||||||
v-if="enlargedParticipant.hasCameraTrack"
|
v-if="enlargedParticipant.hasCameraTrack"
|
||||||
:ref="el => setEnlargedVideoRef(el)"
|
:ref="el => setEnlargedVideoRef(el)"
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="showLogin">
|
<div v-if="showLogin">
|
||||||
<!-- 登录界面 -->
|
<!-- 登录界面 -->
|
||||||
<Login @loginSuccess="handleLoginSuccess" />
|
<Login @loginSuccess="handleLoginSuccess" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="loading-container">
|
||||||
|
<div class="loading-content">
|
||||||
|
<el-icon class="loading-icon"><Loading /></el-icon>
|
||||||
|
<p>正在创建房间,请稍候...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 音频元素 -->
|
<!-- 音频元素 -->
|
||||||
<audio ref="localAudio" autoplay muted class="audio-element"></audio>
|
<audio ref="localAudio" autoplay muted class="audio-element"></audio>
|
||||||
<div id="audio"></div>
|
<div id="audio"></div>
|
||||||
@@ -11,24 +19,16 @@
|
|||||||
<div v-if="!status" class="meeting-container">
|
<div v-if="!status" class="meeting-container">
|
||||||
<!-- 视频容器 -->
|
<!-- 视频容器 -->
|
||||||
<div class="video-layout" :class="{
|
<div class="video-layout" :class="{
|
||||||
'screen-sharing-active': hasActiveScreenShare || isWhiteboardActive || enlargedParticipant,
|
'screen-sharing-active': hasActiveContent,
|
||||||
'enlarged-mode': enlargedParticipant
|
'enlarged-mode': enlargedParticipant
|
||||||
}">
|
}">
|
||||||
<!-- 左侧共享屏幕/白板/放大视频区域 -->
|
<!-- 左侧共享屏幕/白板/放大视频区域 -->
|
||||||
<div class="screen-share-area" v-if="hasActiveScreenShare || isWhiteboardActive || enlargedParticipant">
|
<div class="screen-share-area" v-if="hasActiveContent">
|
||||||
<div class="screen-share-header">
|
<div class="screen-share-header">
|
||||||
<h3 v-if="enlargedParticipant">
|
<h3>{{ currentTopLayerTitle }}</h3>
|
||||||
<span v-if="enlargedParticipant.identity === hostUid">我的放大视频</span>
|
<div class="sharing-user" v-if="screenSharingUser">
|
||||||
<span v-else>放大视图 - {{ enlargedParticipant.identity }}</span>
|
<span>由 {{ screenSharingUser }} 共享</span>
|
||||||
</h3>
|
|
||||||
<h3 v-else-if="isWhiteboardActive">共享白板</h3>
|
|
||||||
<h3 v-else>共享屏幕</h3>
|
|
||||||
<div class="sharing-user" v-if="!enlargedParticipant">
|
|
||||||
<span v-if="isWhiteboardActive || hasActiveScreenShare">
|
|
||||||
由 {{ screenSharingUser }} 共享
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- v-if="enlargedParticipant && enlargedParticipant.identity === hostUid" -->
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="enlargedParticipant && enlargedParticipant.identity === hostUid"
|
v-if="enlargedParticipant && enlargedParticipant.identity === hostUid"
|
||||||
@click="closeEnlargedView"
|
@click="closeEnlargedView"
|
||||||
@@ -36,100 +36,109 @@
|
|||||||
size="small"
|
size="small"
|
||||||
class="close-enlarge-btn"
|
class="close-enlarge-btn"
|
||||||
>
|
>
|
||||||
<!-- 关闭放大 -->
|
|
||||||
<img src="@/assets/images/shrink.png" style='width:16px;height:15px' alt="" />
|
<img src="@/assets/images/shrink.png" style='width:16px;height:15px' alt="" />
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="screen-share-video">
|
<div class="screen-share-video">
|
||||||
<!-- 放大视频模式 -->
|
<!-- 内容层级容器 -->
|
||||||
<div v-if="enlargedParticipant" class="enlarged-video-container">
|
<div class="content-layers-container">
|
||||||
<div class="video-wrapper enlarged-video-wrapper">
|
<!-- 桌面共享或放大视频层 -->
|
||||||
<!-- 放大视频激光笔 Canvas -->
|
<div v-if="hasScreenShareOrEnlarged"
|
||||||
<canvas
|
class="content-layer screen-video-layer"
|
||||||
v-if="enlargedParticipant"
|
:class="{ 'active-layer': isScreenVideoTopLayer }">
|
||||||
ref="enlargedLaserPointerCanvas"
|
|
||||||
class="laser-pointer-canvas enlarged-laser-canvas"
|
<!-- 放大视频模式 -->
|
||||||
@dblclick="handleEnlargedCanvasDoubleClick"
|
<div v-if="enlargedParticipant" class="enlarged-video-container">
|
||||||
@mousedown="handleEnlargedCanvasMouseDown"
|
<div class="video-wrapper enlarged-video-wrapper">
|
||||||
@mousemove="handleEnlargedCanvasMouseMove"
|
<!-- 放大视频激光笔 Canvas -->
|
||||||
@mouseup="handleEnlargedCanvasMouseUp"
|
<canvas
|
||||||
@mouseleave="handleEnlargedCanvasMouseLeave"
|
v-if="enlargedParticipant"
|
||||||
></canvas>
|
ref="enlargedLaserPointerCanvas"
|
||||||
<video
|
class="laser-pointer-canvas enlarged-laser-canvas"
|
||||||
v-if="enlargedParticipant.hasCameraTrack"
|
@dblclick="handleEnlargedCanvasDoubleClick"
|
||||||
:ref="el => setEnlargedVideoRef(el)"
|
@mousedown="handleEnlargedCanvasMouseDown"
|
||||||
autoplay
|
@mousemove="handleEnlargedCanvasMouseMove"
|
||||||
playsinline
|
@mouseup="handleEnlargedCanvasMouseUp"
|
||||||
class="enlarged-video-element"
|
@mouseleave="handleEnlargedCanvasMouseLeave"
|
||||||
@loadedmetadata="handleEnlargedVideoLoaded">
|
></canvas>
|
||||||
</video>
|
<video
|
||||||
<!-- 如果没有视频轨道,显示提示 -->
|
v-if="enlargedParticipant.hasCameraTrack"
|
||||||
<div v-if="!enlargedParticipant.hasCameraTrack" class="video-placeholder">
|
:ref="el => setEnlargedVideoRef(el)"
|
||||||
<i class="el-icon-user"></i>
|
autoplay
|
||||||
<span>暂无视频流</span>
|
playsinline
|
||||||
|
class="enlarged-video-element"
|
||||||
|
@loadedmetadata="handleEnlargedVideoLoaded">
|
||||||
|
</video>
|
||||||
|
<!-- 如果没有视频轨道,显示提示 -->
|
||||||
|
<div v-if="!enlargedParticipant.hasCameraTrack" class="video-placeholder">
|
||||||
|
<i class="el-icon-user"></i>
|
||||||
|
<span>暂无视频流</span>
|
||||||
|
</div>
|
||||||
|
<div class="video-overlay">
|
||||||
|
<span class="participant-name">
|
||||||
|
{{ enlargedParticipant.identity === hostUid ? '我' : enlargedParticipant.identity }}
|
||||||
|
</span>
|
||||||
|
<span class="audio-indicator" :class="{ 'muted': !enlargedParticipant.audioEnabled }">
|
||||||
|
<i :class="enlargedParticipant.audioEnabled ? 'el-icon-microphone' : 'el-icon-turn-off-microphone'"></i>
|
||||||
|
</span>
|
||||||
|
<!-- 激光笔状态指示 -->
|
||||||
|
<span v-if="isLaserPointerActive && enlargedParticipant" class="laser-pointer-indicator">
|
||||||
|
<i class="el-icon-aim"></i> 激光笔模式中
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="video-overlay">
|
|
||||||
<span class="participant-name">
|
<!-- 屏幕共享模式 -->
|
||||||
{{ enlargedParticipant.identity === hostUid ? '我' : enlargedParticipant.identity }}
|
<div v-else class="video-wrapper screen-share-wrapper">
|
||||||
</span>
|
<!-- 激光笔 Canvas -->
|
||||||
<span class="audio-indicator" :class="{ 'muted': !enlargedParticipant.audioEnabled }">
|
<canvas
|
||||||
<i :class="enlargedParticipant.audioEnabled ? 'el-icon-microphone' : 'el-icon-turn-off-microphone'"></i>
|
ref="laserPointerCanvas"
|
||||||
</span>
|
class="laser-pointer-canvas"
|
||||||
<!-- 激光笔状态指示 -->
|
@dblclick="handleCanvasDoubleClick"
|
||||||
<span v-if="isLaserPointerActive && enlargedParticipant" class="laser-pointer-indicator">
|
@mousedown="handleCanvasMouseDown"
|
||||||
<i class="el-icon-aim"></i> 激光笔模式中
|
@mousemove="handleCanvasMouseMove"
|
||||||
</span>
|
@mouseup="handleCanvasMouseUp"
|
||||||
</div>
|
@mouseleave="handleCanvasMouseLeave"
|
||||||
</div>
|
></canvas>
|
||||||
</div>
|
<div class="video-tracks">
|
||||||
|
<!-- 屏幕共享视频 -->
|
||||||
<!-- 白板模式 -->
|
<video
|
||||||
<div v-else-if="isWhiteboardActive" class="whiteboard-container">
|
v-if="activeScreenShareTrack"
|
||||||
<tabulaRase
|
:ref="el => setScreenShareVideoRef(el)"
|
||||||
ref="whiteboardRef"
|
autoplay
|
||||||
:roomId="roomId"
|
playsinline
|
||||||
:userId="hostUid"
|
class="screen-share-element"
|
||||||
class="whiteboard-component"
|
@loadedmetadata="handleScreenShareLoaded">
|
||||||
/>
|
</video>
|
||||||
</div>
|
<!-- 如果没有屏幕共享,显示提示 -->
|
||||||
|
<div v-if="!activeScreenShareTrack" class="video-placeholder">
|
||||||
<!-- 屏幕共享模式 -->
|
<i class="el-icon-monitor"></i>
|
||||||
<div v-else class="video-wrapper screen-share-wrapper">
|
<span>暂无屏幕共享</span>
|
||||||
<!-- 激光笔 Canvas -->
|
</div>
|
||||||
<canvas
|
</div>
|
||||||
ref="laserPointerCanvas"
|
<div class="video-overlay">
|
||||||
class="laser-pointer-canvas"
|
<span class="participant-name">{{ screenSharingUser }}</span>
|
||||||
@dblclick="handleCanvasDoubleClick"
|
<!-- 激光笔状态指示 -->
|
||||||
@mousedown="handleCanvasMouseDown"
|
<span v-if="isLaserPointerActive" class="laser-pointer-indicator">
|
||||||
@mousemove="handleCanvasMouseMove"
|
<i class="el-icon-aim"></i> 激光笔模式中
|
||||||
@mouseup="handleCanvasMouseUp"
|
</span>
|
||||||
@mouseleave="handleCanvasMouseLeave"
|
</div>
|
||||||
></canvas>
|
|
||||||
<div class="video-tracks">
|
|
||||||
<!-- 屏幕共享视频 -->
|
|
||||||
<video
|
|
||||||
v-if="activeScreenShareTrack"
|
|
||||||
:ref="el => setScreenShareVideoRef(el)"
|
|
||||||
autoplay
|
|
||||||
playsinline
|
|
||||||
class="screen-share-element"
|
|
||||||
@loadedmetadata="handleScreenShareLoaded">
|
|
||||||
</video>
|
|
||||||
<!-- 如果没有屏幕共享,显示提示 -->
|
|
||||||
<div v-if="!activeScreenShareTrack" class="video-placeholder">
|
|
||||||
<i class="el-icon-monitor"></i>
|
|
||||||
<span>暂无屏幕共享</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="video-overlay">
|
|
||||||
<span class="participant-name">{{ screenSharingUser }}</span>
|
<!-- 白板层 -->
|
||||||
<!-- 激光笔状态指示 -->
|
<div v-if="isWhiteboardActive"
|
||||||
<span v-if="isLaserPointerActive" class="laser-pointer-indicator">
|
class="content-layer whiteboard-layer"
|
||||||
<i class="el-icon-aim"></i> 激光笔模式中
|
:class="{ 'active-layer': isWhiteboardTopLayer }">
|
||||||
</span>
|
<tabulaRase
|
||||||
</div>
|
ref="whiteboardRef"
|
||||||
|
:roomId="roomId"
|
||||||
|
:userId="hostUid"
|
||||||
|
class="whiteboard-component"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -212,7 +221,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="video-wrapper">
|
<div class="video-wrapper">
|
||||||
<div class="video-tracks">
|
<div class="video-tracks">
|
||||||
<!-- 摄像头视频 - 使用 ref 绑定 -->
|
<!-- 摄像头视频 -->
|
||||||
<video
|
<video
|
||||||
v-if="participant.hasCameraTrack"
|
v-if="participant.hasCameraTrack"
|
||||||
:ref="el => setParticipantVideoRef(el, participant.identity, 'camera')"
|
:ref="el => setParticipantVideoRef(el, participant.identity, 'camera')"
|
||||||
@@ -248,6 +257,7 @@
|
|||||||
<!-- 固定在底部的控制按钮 -->
|
<!-- 固定在底部的控制按钮 -->
|
||||||
<div class="fixed-controls">
|
<div class="fixed-controls">
|
||||||
<div class="controls-container">
|
<div class="controls-container">
|
||||||
|
<!-- 摄像头和麦克风 -->
|
||||||
<div class="microphone-control-group">
|
<div class="microphone-control-group">
|
||||||
<el-button @click="toggleCamera" :type="cameraEnabled ? 'danger' : 'info'" class="control-btn microphone-btn" size="large">
|
<el-button @click="toggleCamera" :type="cameraEnabled ? 'danger' : 'info'" class="control-btn microphone-btn" size="large">
|
||||||
{{ cameraEnabled ? '关闭摄像头' : '开启摄像头' }}
|
{{ cameraEnabled ? '关闭摄像头' : '开启摄像头' }}
|
||||||
@@ -306,6 +316,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 屏幕共享按钮 -->
|
||||||
<el-button
|
<el-button
|
||||||
@click="toggleScreenShare"
|
@click="toggleScreenShare"
|
||||||
:type="isScreenSharing ? 'danger' : (isGlobalScreenSharing ? 'primary' : 'info')"
|
:type="isScreenSharing ? 'danger' : (isGlobalScreenSharing ? 'primary' : 'info')"
|
||||||
@@ -317,15 +329,16 @@
|
|||||||
<span v-else-if="isGlobalScreenSharing">他人共享中</span>
|
<span v-else-if="isGlobalScreenSharing">他人共享中</span>
|
||||||
<span v-else>共享屏幕</span>
|
<span v-else>共享屏幕</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
|
<!-- 白板按钮 - 移除禁用条件 -->
|
||||||
<el-button
|
<el-button
|
||||||
@click="toggleWhiteboard"
|
@click="toggleWhiteboard"
|
||||||
:type="isWhiteboardActive ? 'danger' : 'info'"
|
:type="isWhiteboardActive ? 'danger' : 'info'"
|
||||||
:disabled="cameraEnabled || !canWhiteboardShare"
|
|
||||||
class="control-btn"
|
class="control-btn"
|
||||||
size="large"
|
size="large"
|
||||||
>
|
>
|
||||||
{{ isWhiteboardActive ? '退出白板' : '共享白板' }}
|
{{ isWhiteboardActive ? '退出白板' : '共享白板' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
@click="toggleLaserPointer"
|
@click="toggleLaserPointer"
|
||||||
:type="isLaserPointerActive ? 'danger' : 'info'"
|
:type="isLaserPointerActive ? 'danger' : 'info'"
|
||||||
@@ -438,10 +451,13 @@ const enlargedVideo = ref(null); // 放大视频元素引用
|
|||||||
const enlargedLaserPointerCanvas = ref(null); // 放大视频激光笔 Canvas
|
const enlargedLaserPointerCanvas = ref(null); // 放大视频激光笔 Canvas
|
||||||
const enlargedLaserPointerContext = ref(null); // 放大视频激光笔上下文
|
const enlargedLaserPointerContext = ref(null); // 放大视频激光笔上下文
|
||||||
|
|
||||||
|
const lastActivatedLayer = ref(''); // 记录最后激活的图层:'screenVideo' 或 'whiteboard'
|
||||||
|
const loading = ref(false);
|
||||||
// 路径更新节流处理
|
// 路径更新节流处理
|
||||||
let lastPublishTime = 0;
|
let lastPublishTime = 0;
|
||||||
const publishThrottleTime = 100; // 100ms 节流
|
const publishThrottleTime = 100; // 100ms 节流
|
||||||
|
|
||||||
|
|
||||||
// 鼠标状态跟踪
|
// 鼠标状态跟踪
|
||||||
const mouseState = reactive({
|
const mouseState = reactive({
|
||||||
isDrawing: false,
|
isDrawing: false,
|
||||||
@@ -467,7 +483,8 @@ const laserPointerConfig = reactive({
|
|||||||
const WHITEBOARD_MESSAGE_TYPES = {
|
const WHITEBOARD_MESSAGE_TYPES = {
|
||||||
OPEN: 'open_whiteboard',
|
OPEN: 'open_whiteboard',
|
||||||
CLOSE: 'close_whiteboard',
|
CLOSE: 'close_whiteboard',
|
||||||
SYNC: 'sync_whiteboard'
|
SYNC: 'sync_whiteboard',
|
||||||
|
ACTIVATE_LAYER: 'activate_layer'//图层激活消息
|
||||||
};
|
};
|
||||||
// 在 script 中添加激光笔消息类型和同步功能
|
// 在 script 中添加激光笔消息类型和同步功能
|
||||||
const LASER_POINTER_MESSAGE_TYPES = {
|
const LASER_POINTER_MESSAGE_TYPES = {
|
||||||
@@ -480,6 +497,52 @@ const VIDEO_ENLARGE_MESSAGE_TYPES = {
|
|||||||
SHRINK: 'shrink_video'
|
SHRINK: 'shrink_video'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//判断是否有活动内容
|
||||||
|
const hasActiveContent = computed(() => {
|
||||||
|
return hasScreenShareOrEnlarged.value || isWhiteboardActive.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 是否有桌面共享或放大视频
|
||||||
|
const hasScreenShareOrEnlarged = computed(() => {
|
||||||
|
return hasActiveScreenShare.value || enlargedParticipant.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断各图层是否为顶层
|
||||||
|
const isScreenVideoTopLayer = computed(() => {
|
||||||
|
return hasScreenShareOrEnlarged.value && lastActivatedLayer.value === 'screenVideo';
|
||||||
|
});
|
||||||
|
|
||||||
|
const isWhiteboardTopLayer = computed(() => {
|
||||||
|
return isWhiteboardActive.value && lastActivatedLayer.value === 'whiteboard';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前顶层标题
|
||||||
|
const currentTopLayerTitle = computed(() => {
|
||||||
|
if (hasScreenShareOrEnlarged.value && isScreenVideoTopLayer.value) {
|
||||||
|
if (enlargedParticipant.value) {
|
||||||
|
if (enlargedParticipant.value.identity === hostUid.value) {
|
||||||
|
return '我的放大视频';
|
||||||
|
} else {
|
||||||
|
return `放大视图 - ${enlargedParticipant.value.identity}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return '共享屏幕';
|
||||||
|
}
|
||||||
|
} else if (isWhiteboardActive.value && isWhiteboardTopLayer.value) {
|
||||||
|
return '共享白板';
|
||||||
|
} else {
|
||||||
|
// 默认显示第一个活动的内容
|
||||||
|
if (hasScreenShareOrEnlarged.value) {
|
||||||
|
if (enlargedParticipant.value) return '放大视频';
|
||||||
|
return '共享屏幕';
|
||||||
|
}
|
||||||
|
if (isWhiteboardActive.value) return '共享白板';
|
||||||
|
return '共享内容';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const participantCount = computed(() => {
|
const participantCount = computed(() => {
|
||||||
return remoteParticipants.value.size + 1; // 包括自己
|
return remoteParticipants.value.size + 1; // 包括自己
|
||||||
});
|
});
|
||||||
@@ -508,7 +571,8 @@ const canScreenShare = computed(() => {
|
|||||||
|
|
||||||
// 是否允许白板共享
|
// 是否允许白板共享
|
||||||
const canWhiteboardShare = computed(() => {
|
const canWhiteboardShare = computed(() => {
|
||||||
return !enlargedParticipant.value;
|
// return !enlargedParticipant.value;
|
||||||
|
return true; // 白板现在完全独立,没有限制
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加一个计算属性来显示屏幕共享状态提示
|
// 添加一个计算属性来显示屏幕共享状态提示
|
||||||
@@ -556,6 +620,38 @@ const room = new Room({
|
|||||||
|
|
||||||
emitter.on('whiteboardFailed',whiteboardFailedHandle);
|
emitter.on('whiteboardFailed',whiteboardFailedHandle);
|
||||||
|
|
||||||
|
// 激活图层函数
|
||||||
|
function activateLayer(layerType, isLocalAction = true) {
|
||||||
|
const oldLayer = lastActivatedLayer.value;
|
||||||
|
lastActivatedLayer.value = layerType;
|
||||||
|
// 如果是本地操作,通过MQTT通知其他用户
|
||||||
|
if (isLocalAction) {
|
||||||
|
publishLayerActivation(layerType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 发布图层激活消息
|
||||||
|
function publishLayerActivation(layerType) {
|
||||||
|
try {
|
||||||
|
const message = {
|
||||||
|
type: WHITEBOARD_MESSAGE_TYPES.ACTIVATE_LAYER,
|
||||||
|
roomId: roomId.value,
|
||||||
|
sender: hostUid.value,
|
||||||
|
senderName: hostUid.value,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
payload: {
|
||||||
|
layerType: layerType,
|
||||||
|
hasScreenShare: hasActiveScreenShare.value,
|
||||||
|
hasEnlargedVideo: !!enlargedParticipant.value,
|
||||||
|
hasWhiteboard: isWhiteboardActive.value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mqttClient.publish(`xSynergy/shareWhiteboard/${room.name}`, message);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发布图层激活消息失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 登录成功回调 */
|
/** 登录成功回调 */
|
||||||
function handleLoginSuccess() {
|
function handleLoginSuccess() {
|
||||||
showLogin.value = false;
|
showLogin.value = false;
|
||||||
@@ -589,10 +685,10 @@ function enlargeParticipant(participant) {
|
|||||||
ElMessage.info('已自动停止屏幕共享,开启视频放大模式');
|
ElMessage.info('已自动停止屏幕共享,开启视频放大模式');
|
||||||
}
|
}
|
||||||
// 如果正在使用白板,自动退出
|
// 如果正在使用白板,自动退出
|
||||||
if (isWhiteboardActive.value) {
|
// if (isWhiteboardActive.value) {
|
||||||
exitWhiteboard();
|
// exitWhiteboard();
|
||||||
ElMessage.info('已自动退出白板,开启视频放大模式');
|
// ElMessage.info('已自动退出白板,开启视频放大模式');
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 如果正在放大其他用户,先关闭
|
// 如果正在放大其他用户,先关闭
|
||||||
if (enlargedParticipant.value && enlargedParticipant.value.identity !== hostUid.value) {
|
if (enlargedParticipant.value && enlargedParticipant.value.identity !== hostUid.value) {
|
||||||
@@ -600,6 +696,7 @@ function enlargeParticipant(participant) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enlargedParticipant.value = participant;
|
enlargedParticipant.value = participant;
|
||||||
|
activateLayer('screenVideo', true); // 激活屏幕视频图层
|
||||||
ElMessage.success(`已放大您的视频`);
|
ElMessage.success(`已放大您的视频`);
|
||||||
|
|
||||||
// 发布放大消息给其他用户
|
// 发布放大消息给其他用户
|
||||||
@@ -1053,7 +1150,9 @@ async function initMqttFilePreview(){
|
|||||||
await mqttClient.connect(clientId)
|
await mqttClient.connect(clientId)
|
||||||
isMqttFilePreview.value = true
|
isMqttFilePreview.value = true
|
||||||
// 订阅主题
|
// 订阅主题
|
||||||
|
console.log('执行了一次')
|
||||||
emitter.emit('subscribeToFilePreviewTopic',{roomId:roomId.value})
|
emitter.emit('subscribeToFilePreviewTopic',{roomId:roomId.value})
|
||||||
|
emitter.emit('roomces',{roomId:roomId.value})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('MQTT连接失败:', error)
|
console.error('MQTT连接失败:', error)
|
||||||
ElMessage.error('文件上传服务连接失败')
|
ElMessage.error('文件上传服务连接失败')
|
||||||
@@ -1762,6 +1861,9 @@ function handleWhiteboardMessage(payload, topic) {
|
|||||||
case WHITEBOARD_MESSAGE_TYPES.SYNC:
|
case WHITEBOARD_MESSAGE_TYPES.SYNC:
|
||||||
handleWhiteboardSync(data);
|
handleWhiteboardSync(data);
|
||||||
break;
|
break;
|
||||||
|
case WHITEBOARD_MESSAGE_TYPES.ACTIVATE_LAYER:
|
||||||
|
handleRemoteLayerActivation(data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn('未知的白板消息类型:', data.type);
|
console.warn('未知的白板消息类型:', data.type);
|
||||||
}
|
}
|
||||||
@@ -1770,19 +1872,36 @@ function handleWhiteboardMessage(payload, topic) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理远程图层激活
|
||||||
|
function handleRemoteLayerActivation(data) {
|
||||||
|
const { layerType, hasScreenShare, hasEnlargedVideo, hasWhiteboard } = data.payload;
|
||||||
|
// 根据远程状态更新本地图层激活
|
||||||
|
if (layerType === 'screenVideo' && (hasScreenShare || hasEnlargedVideo)) {
|
||||||
|
// 只有当远程用户确实有屏幕共享或放大视频时才激活
|
||||||
|
activateLayer('screenVideo', false);
|
||||||
|
// console.log('远程激活屏幕视频层');
|
||||||
|
} else if (layerType === 'whiteboard' && hasWhiteboard) {
|
||||||
|
// 只有当远程用户确实有白板时才激活
|
||||||
|
activateLayer('whiteboard', false);
|
||||||
|
// console.log('远程激活白板层');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理远程打开白板
|
// 处理远程打开白板
|
||||||
function handleRemoteWhiteboardOpen(data) {
|
function handleRemoteWhiteboardOpen(data) {
|
||||||
ElMessage.info(`${data.senderName || data.sender} 开启了白板`);
|
ElMessage.info(`${data.senderName || data.sender} 开启了白板`);
|
||||||
isWhiteboardActive.value = true;
|
isWhiteboardActive.value = true;
|
||||||
|
// 当远程用户开启白板时,激活白板图层
|
||||||
|
activateLayer('whiteboard', false);
|
||||||
// 如果正在屏幕共享,自动停止
|
// 如果正在屏幕共享,自动停止
|
||||||
if (isScreenSharing.value) {
|
// if (isScreenSharing.value) {
|
||||||
room.localParticipant.setScreenShareEnabled(false);
|
// room.localParticipant.setScreenShareEnabled(false);
|
||||||
isScreenSharing.value = false;
|
// isScreenSharing.value = false;
|
||||||
}
|
// }
|
||||||
// 如果正在放大视图,自动关闭
|
// 如果正在放大视图,自动关闭
|
||||||
if (enlargedParticipant.value) {
|
// if (enlargedParticipant.value) {
|
||||||
closeEnlargedView();
|
// closeEnlargedView();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理远程关闭白板
|
// 处理远程关闭白板
|
||||||
@@ -1791,6 +1910,17 @@ function handleRemoteWhiteboardClose(data) {
|
|||||||
// if(data.roomType == '1'){
|
// if(data.roomType == '1'){
|
||||||
// isWhiteboardActive.value = false;
|
// isWhiteboardActive.value = false;
|
||||||
// }
|
// }
|
||||||
|
//当远程用户关闭白板时,如果本地有屏幕共享,确保屏幕共享重新显示
|
||||||
|
nextTick(() => {
|
||||||
|
if (hasActiveScreenShare.value && !isWhiteboardActive.value) {
|
||||||
|
activateLayer('screenVideo',false);
|
||||||
|
|
||||||
|
// 确保屏幕共享视频元素重新附加轨道
|
||||||
|
if (activeScreenShareTrack.value && screenShareVideo.value) {
|
||||||
|
attachTrackToVideo(screenShareVideo.value, activeScreenShareTrack.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理白板同步消息
|
// 处理白板同步消息
|
||||||
@@ -1814,9 +1944,12 @@ async function handleConfirmSelection(userInfo){
|
|||||||
if(userInfo.length < 0){
|
if(userInfo.length < 0){
|
||||||
ElMessage.error('请选择加入房间的人员')
|
ElMessage.error('请选择加入房间的人员')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const joinUserIds = userInfo.map(item => item.uid)
|
const joinUserInfo = userInfo.map(item => ({
|
||||||
await getInvite(room.name,{user_uids:joinUserIds, participant_role: "participant"})
|
user_uid: item.uid,
|
||||||
|
display_name: item.name
|
||||||
|
}));
|
||||||
|
await getInvite(room.name,{participants:joinUserInfo, participant_role: "participant"})
|
||||||
}
|
}
|
||||||
|
|
||||||
function publishWhiteboardMessage(type, payload = {}) {
|
function publishWhiteboardMessage(type, payload = {}) {
|
||||||
@@ -1851,10 +1984,10 @@ async function toggleWhiteboard() {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// roomId.value = room.name
|
// roomId.value = room.name
|
||||||
if (isWhiteboardActive.value) {
|
if (isWhiteboardActive.value) {
|
||||||
// 如果白板已经激活,点击则退出白板
|
// 如果白板已经激活,点击则退出白板
|
||||||
await exitWhiteboard();
|
await exitWhiteboard();
|
||||||
} else {
|
} else {
|
||||||
// 否则开启白板
|
// 否则开启白板
|
||||||
await startWhiteboard();
|
await startWhiteboard();
|
||||||
}
|
}
|
||||||
@@ -1863,18 +1996,19 @@ async function toggleWhiteboard() {
|
|||||||
async function startWhiteboard() {
|
async function startWhiteboard() {
|
||||||
try {
|
try {
|
||||||
// 如果正在屏幕共享,先停止
|
// 如果正在屏幕共享,先停止
|
||||||
if (isScreenSharing.value) {
|
// if (isScreenSharing.value) {
|
||||||
await room.localParticipant.setScreenShareEnabled(false);
|
// await room.localParticipant.setScreenShareEnabled(false);
|
||||||
isScreenSharing.value = false;
|
// isScreenSharing.value = false;
|
||||||
ElMessage.info('已停止屏幕共享,开启白板');
|
// ElMessage.info('已停止屏幕共享,开启白板');
|
||||||
}
|
// }
|
||||||
// 如果正在放大视图,先关闭
|
// 如果正在放大视图,先关闭
|
||||||
if (enlargedParticipant.value) {
|
// if (enlargedParticipant.value) {
|
||||||
closeEnlargedView();
|
// closeEnlargedView();
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 激活白板状态
|
// 激活白板状态
|
||||||
isWhiteboardActive.value = true;
|
isWhiteboardActive.value = true;
|
||||||
|
activateLayer('whiteboard', true); // 激活白板图层
|
||||||
const success = publishWhiteboardMessage(WHITEBOARD_MESSAGE_TYPES.OPEN, {
|
const success = publishWhiteboardMessage(WHITEBOARD_MESSAGE_TYPES.OPEN, {
|
||||||
action: 'open',
|
action: 'open',
|
||||||
whiteboardId: roomId.value,
|
whiteboardId: roomId.value,
|
||||||
@@ -1905,6 +2039,10 @@ async function exitWhiteboard() {
|
|||||||
if (whiteboardRef.value && whiteboardRef.value.cleanup) {
|
if (whiteboardRef.value && whiteboardRef.value.cleanup) {
|
||||||
whiteboardRef.value.cleanup();
|
whiteboardRef.value.cleanup();
|
||||||
}
|
}
|
||||||
|
// 如果关闭的是当前顶层,且还有屏幕共享或放大视频,则激活屏幕视频层
|
||||||
|
if (lastActivatedLayer.value === 'whiteboard' && hasScreenShareOrEnlarged.value) {
|
||||||
|
activateLayer('screenVideo',false);
|
||||||
|
}
|
||||||
ElMessage.success('已退出白板');
|
ElMessage.success('已退出白板');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error !== 'cancel') {
|
if (error !== 'cancel') {
|
||||||
@@ -1980,22 +2118,34 @@ function setupRoomListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 事件处理函数
|
// 事件处理函数
|
||||||
|
/**
|
||||||
|
* 处理房间连接成功事件
|
||||||
|
* 1. 初始化房间ID
|
||||||
|
* 2. 初始化各种MQTT连接(白板、激光笔、文件上传等)
|
||||||
|
* 3. 更新连接状态
|
||||||
|
* 4. 初始化已存在的远程参与者
|
||||||
|
*/
|
||||||
async function handleConnected() {
|
async function handleConnected() {
|
||||||
|
// 设置当前房间ID
|
||||||
roomId.value = room.name
|
roomId.value = room.name
|
||||||
await initMqtt();
|
|
||||||
await initToLaserPointerMqtt();
|
// 初始化各种MQTT服务
|
||||||
await initMqttFileUploadSucc() ;
|
await initMqtt(); // 白板MQTT
|
||||||
await initMqttFileConversionStatus();
|
await initToLaserPointerMqtt(); // 激光笔MQTT
|
||||||
await initMqttFilePreview();
|
await initMqttFileUploadSucc(); // 文件上传成功MQTT
|
||||||
await initMqttEnlargeVideo();
|
await initMqttFileConversionStatus(); // 文件转换状态MQTT
|
||||||
|
await initMqttFilePreview(); // 文件预览MQTT
|
||||||
|
await initMqttEnlargeVideo(); // 视频放大MQTT
|
||||||
|
|
||||||
|
// 更新连接状态
|
||||||
status.value = false;
|
status.value = false;
|
||||||
ElMessage.success('已成功连接到房间');
|
ElMessage.success('已成功连接到房间');
|
||||||
// 初始化现有远程参与者
|
|
||||||
|
// 初始化已存在的远程参与者
|
||||||
room.remoteParticipants.forEach(participant => {
|
room.remoteParticipants.forEach(participant => {
|
||||||
addRemoteParticipant(participant);
|
addRemoteParticipant(participant); // 添加参与者
|
||||||
setupParticipantListeners(participant);
|
setupParticipantListeners(participant); // 设置监听器
|
||||||
// 立即检查并更新参与者的轨道状态
|
updateParticipantTracks(participant); // 更新轨道状态
|
||||||
updateParticipantTracks(participant);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2026,6 +2176,10 @@ function handleTrackSubscribed(track, publication, participant) {
|
|||||||
// 设置全局屏幕共享用户
|
// 设置全局屏幕共享用户
|
||||||
globalScreenSharingUser.value = participant.identity;
|
globalScreenSharingUser.value = participant.identity;
|
||||||
isGlobalScreenSharing.value = true;
|
isGlobalScreenSharing.value = true;
|
||||||
|
// 如果是远程用户的屏幕共享,激活屏幕视频层
|
||||||
|
if (participant.identity !== hostUid.value) {
|
||||||
|
activateLayer('screenVideo', false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 立即附加到视频元素(如果元素已存在)
|
// 立即附加到视频元素(如果元素已存在)
|
||||||
@@ -2135,7 +2289,7 @@ function handleLocalTrackUnpublished(publication) {
|
|||||||
if (screenSharingUser.value === room.localParticipant.identity) {
|
if (screenSharingUser.value === room.localParticipant.identity) {
|
||||||
isScreenSharing.value = false;
|
isScreenSharing.value = false;
|
||||||
screenSharingUser.value = '';
|
screenSharingUser.value = '';
|
||||||
activeScreenShareTrack.value = null;
|
activeScreenShareTrack.value = null;
|
||||||
// 清除全局屏幕共享状态
|
// 清除全局屏幕共享状态
|
||||||
if (globalScreenSharingUser.value === room.localParticipant.identity) {
|
if (globalScreenSharingUser.value === room.localParticipant.identity) {
|
||||||
globalScreenSharingUser.value = '';
|
globalScreenSharingUser.value = '';
|
||||||
@@ -2208,6 +2362,10 @@ function updateScreenShareState(participant, track) {
|
|||||||
// 有新的屏幕共享轨道
|
// 有新的屏幕共享轨道
|
||||||
screenSharingUser.value = participant.identity;
|
screenSharingUser.value = participant.identity;
|
||||||
activeScreenShareTrack.value = track;
|
activeScreenShareTrack.value = track;
|
||||||
|
// 重要:确保屏幕共享层被激活(如果白板已关闭)
|
||||||
|
if (!isWhiteboardActive.value) {
|
||||||
|
activateLayer('screenVideo',false);
|
||||||
|
}
|
||||||
// 附加到屏幕共享视频元素
|
// 附加到屏幕共享视频元素
|
||||||
if (screenShareVideo.value) {
|
if (screenShareVideo.value) {
|
||||||
attachTrackToVideo(screenShareVideo.value, track);
|
attachTrackToVideo(screenShareVideo.value, track);
|
||||||
@@ -2658,10 +2816,10 @@ async function toggleScreenShare() {
|
|||||||
ElMessage.error('当前处于视频放大模式,无法进行屏幕共享');
|
ElMessage.error('当前处于视频放大模式,无法进行屏幕共享');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(isWhiteboardActive.value){
|
// if(isWhiteboardActive.value){
|
||||||
ElMessage.error('请先关闭白板');
|
// ElMessage.error('请先关闭白板');
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
// 检查是否已经有其他用户在共享屏幕
|
// 检查是否已经有其他用户在共享屏幕
|
||||||
if (!isScreenSharing.value && isGlobalScreenSharing.value && globalScreenSharingUser.value !== hostUid.value) {
|
if (!isScreenSharing.value && isGlobalScreenSharing.value && globalScreenSharingUser.value !== hostUid.value) {
|
||||||
ElMessage.error(`当前 ${globalScreenSharingUser.value} 正在共享屏幕,请等待其结束后再共享`);
|
ElMessage.error(`当前 ${globalScreenSharingUser.value} 正在共享屏幕,请等待其结束后再共享`);
|
||||||
@@ -2682,6 +2840,7 @@ async function toggleScreenShare() {
|
|||||||
// 设置全局屏幕共享状态
|
// 设置全局屏幕共享状态
|
||||||
globalScreenSharingUser.value = hostUid.value;
|
globalScreenSharingUser.value = hostUid.value;
|
||||||
isGlobalScreenSharing.value = true;
|
isGlobalScreenSharing.value = true;
|
||||||
|
activateLayer('screenVideo', true);// 激活屏幕视频图层
|
||||||
ElMessage.success('屏幕共享已开始');
|
ElMessage.success('屏幕共享已开始');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2694,29 +2853,7 @@ function handleScreenShareEnded() {
|
|||||||
ElMessage.info('屏幕共享已停止');
|
ElMessage.info('屏幕共享已停止');
|
||||||
// 移除事件监听器
|
// 移除事件监听器
|
||||||
room.localParticipant.off('screenShareEnded', handleScreenShareEnded);
|
room.localParticipant.off('screenShareEnded', handleScreenShareEnded);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function joinRoomBtn() {
|
|
||||||
try {
|
|
||||||
const res = await getRoomToken({max_participants: 20});
|
|
||||||
if(res.meta.code != 200){
|
|
||||||
ElMessage.error(res.meta.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const token = res.data.access_token;
|
|
||||||
roomName.value = res.data.room.name
|
|
||||||
if (!token) {
|
|
||||||
throw new Error('获取 token 失败');
|
|
||||||
}
|
|
||||||
setupRoomListeners();
|
|
||||||
await room.connect(wsURL, token, {
|
|
||||||
autoSubscribe: true,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error(`连接失败: ${error.message}`);
|
|
||||||
status.value = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 离开会议函数
|
// 离开会议函数
|
||||||
async function leaveRoom() {
|
async function leaveRoom() {
|
||||||
@@ -2854,7 +2991,7 @@ watch([() => hasActiveScreenShare.value, () => isScreenSharing.value], ([newHasA
|
|||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
// 在摄像头关闭时,如果正在放大自己的视频,自动关闭放大视图
|
// 在摄像头关闭时,如果正在放大自己的视频,自动关闭放大视图
|
||||||
watch(cameraEnabled, (newVal) => {
|
watch(cameraEnabled, (newVal) => {
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
// 摄像头关闭时,如果正在放大自己的视频,自动关闭
|
// 摄像头关闭时,如果正在放大自己的视频,自动关闭
|
||||||
if (enlargedParticipant.value && enlargedParticipant.value.identity === hostUid.value) {
|
if (enlargedParticipant.value && enlargedParticipant.value.identity === hostUid.value) {
|
||||||
@@ -2891,7 +3028,7 @@ watch(isLaserPointerActive, (newVal) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 监听放大视频状态变化,初始化激光笔
|
// 监听放大视频状态变化,初始化激光笔
|
||||||
watch(enlargedParticipant, (newVal) => {
|
watch(enlargedParticipant, (newVal) => {
|
||||||
if (newVal && isLaserPointerActive.value) {
|
if (newVal && isLaserPointerActive.value) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
initEnlargedLaserPointerCanvas();
|
initEnlargedLaserPointerCanvas();
|
||||||
@@ -2900,6 +3037,30 @@ watch(enlargedParticipant, (newVal) => {
|
|||||||
cleanupEnlargedLaserPointer();
|
cleanupEnlargedLaserPointer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听屏幕共享或放大视频状态变化
|
||||||
|
watch(hasScreenShareOrEnlarged, (newVal) => {
|
||||||
|
if (newVal && !lastActivatedLayer.value) {
|
||||||
|
// 如果首次有屏幕共享或放大视频,且没有激活图层,则激活
|
||||||
|
activateLayer('screenVideo',false);
|
||||||
|
} else if (!newVal && lastActivatedLayer.value === 'screenVideo') {
|
||||||
|
// 如果屏幕视频层关闭且当前激活的是它,则检查是否有白板
|
||||||
|
if (isWhiteboardActive.value) {
|
||||||
|
activateLayer('whiteboard',false);
|
||||||
|
} else {
|
||||||
|
lastActivatedLayer.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听白板状态变化
|
||||||
|
watch(isWhiteboardActive, (newVal) => {
|
||||||
|
if (newVal && !lastActivatedLayer.value) {
|
||||||
|
// 如果首次有白板,且没有激活图层,则激活
|
||||||
|
activateLayer('whiteboard',false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 添加专门的关闭激光笔函数
|
// 添加专门的关闭激光笔函数
|
||||||
function closeLaserPointer() {
|
function closeLaserPointer() {
|
||||||
isLaserPointerActive.value = false;
|
isLaserPointerActive.value = false;
|
||||||
@@ -2914,6 +3075,34 @@ function closeLaserPointer() {
|
|||||||
ElMessage.info('屏幕共享已结束,激光笔已自动关闭');
|
ElMessage.info('屏幕共享已结束,激光笔已自动关闭');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function joinRoomBtn() {
|
||||||
|
try {
|
||||||
|
loading.value = true; // 开始加载
|
||||||
|
status.value = true; // 确保显示加载状态
|
||||||
|
|
||||||
|
const res = await getRoomToken({max_participants: 20});
|
||||||
|
if(res.meta.code != 200){
|
||||||
|
ElMessage.error(res.meta.message);
|
||||||
|
loading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const token = res.data.access_token;
|
||||||
|
roomName.value = res.data.room.name
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('获取 token 失败');
|
||||||
|
}
|
||||||
|
setupRoomListeners();
|
||||||
|
await room.connect(wsURL, token, {
|
||||||
|
autoSubscribe: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(`连接失败: ${error.message}`);
|
||||||
|
status.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false; // 无论成功失败都结束加载
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 在组件卸载时也清理资源
|
// 在组件卸载时也清理资源
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener('resize', resizeLaserPointerCanvas);
|
window.removeEventListener('resize', resizeLaserPointerCanvas);
|
||||||
@@ -2931,32 +3120,155 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if(route.query.type == '1'){
|
|
||||||
await joinRoomBtn()
|
const res = await getTokenApi(route.query.room_uid)
|
||||||
hostUid.value = roomStore.userUid
|
console.log(res,'res++--++')
|
||||||
// 邀请用户参与房间
|
if(res.meta.code == 200){
|
||||||
if(room.name){
|
const token = res.data.access_token;
|
||||||
await getInvite(room.name,{user_uids:[roomStore.detailUid], participant_role: "participant"})
|
hostUid.value = res.data.user_uid
|
||||||
}
|
await nextTick();
|
||||||
} else {
|
setupRoomListeners();
|
||||||
const res = await getTokenApi(route.query.room_uid)
|
await room.connect(wsURL, token, {
|
||||||
if(res.meta.code == 200){
|
autoSubscribe: true,
|
||||||
const token = res.data.access_token;
|
});
|
||||||
hostUid.value = res.data.user_uid
|
}else{
|
||||||
await nextTick();
|
ElMessage.error(res.meta.message);
|
||||||
setupRoomListeners();
|
return;
|
||||||
await room.connect(wsURL, token, {
|
}
|
||||||
autoSubscribe: true,
|
|
||||||
});
|
// if(route.query.type == '1'){
|
||||||
}else{
|
// await joinRoomBtn()
|
||||||
ElMessage.error(res.meta.message);
|
// hostUid.value = roomStore.userUid
|
||||||
return;
|
|
||||||
}
|
// } else {
|
||||||
}
|
// const res = await getTokenApi(route.query.room_uid)
|
||||||
|
// if(res.meta.code == 200){
|
||||||
|
// const token = res.data.access_token;
|
||||||
|
// hostUid.value = res.data.user_uid
|
||||||
|
// await nextTick();
|
||||||
|
// setupRoomListeners();
|
||||||
|
// await room.connect(wsURL, token, {
|
||||||
|
// autoSubscribe: true,
|
||||||
|
// });
|
||||||
|
// }else{
|
||||||
|
// ElMessage.error(res.meta.message);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.loading-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(15, 15, 26, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-content {
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
color: #409eff;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-content p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #a0a0b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
/* 内容层级容器 */
|
||||||
|
.content-layers-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内容图层通用样式 */
|
||||||
|
.content-layer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
/* 默认图层在底层 */
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
/* 激活的图层在最顶层 */
|
||||||
|
&.active-layer {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕视频层(桌面共享或放大视频) */
|
||||||
|
.screen-video-layer {
|
||||||
|
.enlarged-video-wrapper,
|
||||||
|
.screen-share-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.enlarged-video-element,
|
||||||
|
.screen-share-element {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 白板层 */
|
||||||
|
.whiteboard-layer {
|
||||||
|
.whiteboard-component {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 当图层非激活状态时的视觉提示 */
|
||||||
|
.content-layer:not(.active-layer) {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none; /* 非激活图层不接受交互 */
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 激活图层完全显示 */
|
||||||
|
.content-layer.active-layer {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 激光笔 Canvas 样式 */
|
||||||
|
.laser-pointer-canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 20; /* 激光笔在所有内容之上 */
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
|
||||||
.enlarged-laser-canvas {
|
.enlarged-laser-canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -3048,14 +3360,14 @@ onMounted(async () => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
/* 激光笔 Canvas 样式 */
|
/* 激光笔 Canvas 样式 */
|
||||||
.laser-pointer-canvas {
|
// .laser-pointer-canvas {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 0;
|
// top: 0;
|
||||||
left: 0;
|
// left: 0;
|
||||||
z-index: 10;
|
// z-index: 10;
|
||||||
pointer-events: auto;
|
// pointer-events: auto;
|
||||||
cursor: crosshair;
|
// cursor: crosshair;
|
||||||
}
|
// }
|
||||||
/* 激光笔状态指示器 */
|
/* 激光笔状态指示器 */
|
||||||
.laser-pointer-indicator {
|
.laser-pointer-indicator {
|
||||||
color: #ff0000;
|
color: #ff0000;
|
||||||
@@ -3206,7 +3518,7 @@ body {
|
|||||||
}
|
}
|
||||||
.screen-share-element {
|
.screen-share-element {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 96%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
background: #000;
|
background: #000;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -3501,7 +3813,7 @@ body {
|
|||||||
}
|
}
|
||||||
.microphone-control-group .dropdown-btn {
|
.microphone-control-group .dropdown-btn {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
.controls-container {
|
.controls-container {
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
@@ -3516,4 +3828,4 @@ body {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -53,7 +53,6 @@
|
|||||||
@mousemove="handleEnlargedCanvasMouseMove"
|
@mousemove="handleEnlargedCanvasMouseMove"
|
||||||
@mouseup="handleEnlargedCanvasMouseUp"
|
@mouseup="handleEnlargedCanvasMouseUp"
|
||||||
@mouseleave="handleEnlargedCanvasMouseLeave"
|
@mouseleave="handleEnlargedCanvasMouseLeave"
|
||||||
style='border:1px solid red'
|
|
||||||
></canvas>
|
></canvas>
|
||||||
<video
|
<video
|
||||||
v-if="enlargedParticipant.hasCameraTrack"
|
v-if="enlargedParticipant.hasCameraTrack"
|
||||||
|
|||||||
@@ -52,8 +52,7 @@
|
|||||||
@mousedown="handleEnlargedCanvasMouseDown"
|
@mousedown="handleEnlargedCanvasMouseDown"
|
||||||
@mousemove="handleEnlargedCanvasMouseMove"
|
@mousemove="handleEnlargedCanvasMouseMove"
|
||||||
@mouseup="handleEnlargedCanvasMouseUp"
|
@mouseup="handleEnlargedCanvasMouseUp"
|
||||||
@mouseleave="handleEnlargedCanvasMouseLeave"
|
@mouseleave="handleEnlargedCanvasMouseLeave"
|
||||||
style='border:1px solid red'
|
|
||||||
></canvas>
|
></canvas>
|
||||||
<video
|
<video
|
||||||
v-if="enlargedParticipant.hasCameraTrack"
|
v-if="enlargedParticipant.hasCameraTrack"
|
||||||
@@ -99,8 +98,7 @@
|
|||||||
@mousedown="handleCanvasMouseDown"
|
@mousedown="handleCanvasMouseDown"
|
||||||
@mousemove="handleCanvasMouseMove"
|
@mousemove="handleCanvasMouseMove"
|
||||||
@mouseup="handleCanvasMouseUp"
|
@mouseup="handleCanvasMouseUp"
|
||||||
@mouseleave="handleCanvasMouseLeave"
|
@mouseleave="handleCanvasMouseLeave"
|
||||||
style='border:1px solid red'
|
|
||||||
></canvas>
|
></canvas>
|
||||||
<div class="video-tracks">
|
<div class="video-tracks">
|
||||||
<!-- 屏幕共享视频 -->
|
<!-- 屏幕共享视频 -->
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<div class="left-list" v-loading="leftListLoading || loading">
|
<div class="left-list" v-loading="leftListLoading || loading">
|
||||||
<div class="list-tab">
|
<div class="list-tab">
|
||||||
<div
|
<div
|
||||||
@@ -107,7 +106,7 @@
|
|||||||
:key="indexs"
|
:key="indexs"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
items.display_name +
|
(items.display_name || items.user_uid) +
|
||||||
(indexs + 1 == item.all_participants.length
|
(indexs + 1 == item.all_participants.length
|
||||||
? ''
|
? ''
|
||||||
: '、')
|
: '、')
|
||||||
@@ -311,7 +310,9 @@ const updateDetail = (item) => {
|
|||||||
* 触底加载
|
* 触底加载
|
||||||
*/
|
*/
|
||||||
const infinite = () => {
|
const infinite = () => {
|
||||||
|
console.log('外面的加载')
|
||||||
if (state.more) {
|
if (state.more) {
|
||||||
|
console.log(state.more,'state.more执行了')
|
||||||
state.queryFrom.page++
|
state.queryFrom.page++
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,10 +53,10 @@
|
|||||||
:key="indexs"
|
:key="indexs"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
items.display_name +
|
(items.display_name || items.user_uid) +
|
||||||
(indexs + 1 == detail.all_participants.length
|
(indexs + 1 == detail.all_participants.length
|
||||||
? ''
|
? ''
|
||||||
: '、')
|
: '、')
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-content-file" v-if="isShow && tabValue == 1">
|
<div class="right-content-file" v-if="isShow && tabValue == 1" >
|
||||||
<el-row :gutter="15">
|
<el-row :gutter="15">
|
||||||
<el-col :xs="24" :sm="24" :md="16" :lg="18">
|
<el-col :xs="24" :sm="24" :md="16" :lg="18">
|
||||||
<div class="content-file-video">
|
<div class="content-file-video">
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
<div class="content-file-list">
|
<div class="content-file-list">
|
||||||
<el-scrollbar
|
<el-scrollbar
|
||||||
class="file-list"
|
class="file-list"
|
||||||
height="calc(100vh - 380px)"
|
height="calc(100vh - 390px)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="file-list-content"
|
class="file-list-content"
|
||||||
@@ -203,9 +203,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-card-btn">
|
<div class="user-card-btn">
|
||||||
<el-button type="info" @click="clickInitiate">
|
<el-button
|
||||||
发起协作
|
type="info"
|
||||||
</el-button>
|
@click="clickInitiate"
|
||||||
|
:loading="initiateLoading"
|
||||||
|
:disabled="initiateLoading"
|
||||||
|
>
|
||||||
|
{{ initiateLoading ? '正在发起邀请' : '发起协作' }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -214,8 +219,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<!-- <el-dialog
|
<!-- <el-dialog
|
||||||
v-model="inviteDialog"
|
v-model="inviteDialog"
|
||||||
title="远程协作"
|
title="远程协作"
|
||||||
@@ -265,7 +269,7 @@ import leftTab from './components/leftTab/index.vue'
|
|||||||
import AssistWx from './components/assistWx/index.vue'
|
import AssistWx from './components/assistWx/index.vue'
|
||||||
import BrowseFile from '@/views/conferencingRoom/components/fileUpload/browseFile.vue'
|
import BrowseFile from '@/views/conferencingRoom/components/fileUpload/browseFile.vue'
|
||||||
import { getInfo } from '@/api/login.js'
|
import { getInfo } from '@/api/login.js'
|
||||||
import { getStatusApi ,getFileListApi ,getvideoUrlApi} from '@/api/conferencingRoom.js'
|
import { getStatusApi ,getFileListApi ,getvideoUrlApi,getRoomToken,getInvite} from '@/api/conferencingRoom.js'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useRoomStore } from '@/stores/modules/room'
|
import { useRoomStore } from '@/stores/modules/room'
|
||||||
@@ -289,6 +293,7 @@ const state = reactive({
|
|||||||
socketInformation: null,
|
socketInformation: null,
|
||||||
inviteDialog: false,
|
inviteDialog: false,
|
||||||
cooperation: import.meta.env.VITE_APP_COOPERATION_TYPE,
|
cooperation: import.meta.env.VITE_APP_COOPERATION_TYPE,
|
||||||
|
initiateLoading: false,
|
||||||
})
|
})
|
||||||
const userLoading = ref(false); // 用户信息加载状态
|
const userLoading = ref(false); // 用户信息加载状态
|
||||||
//文件预览
|
//文件预览
|
||||||
@@ -299,13 +304,15 @@ const isEmptyObject = (obj) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 发起协作邀请 */
|
/** 发起协作邀请 */
|
||||||
const clickInitiate = () => {
|
const clickInitiate = async () => {
|
||||||
|
state.initiateLoading = true
|
||||||
let userData = null
|
let userData = null
|
||||||
try {
|
try {
|
||||||
userData = JSON.parse(sessionStorage.getItem('userData')) || null
|
try {
|
||||||
} catch (e) {
|
userData = JSON.parse(sessionStorage.getItem('userData')) || null
|
||||||
console.error('解析 userData 失败:', e)
|
} catch (e) {
|
||||||
}
|
console.error('解析 userData 失败:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (isEmptyObject(state.detail)) {
|
if (isEmptyObject(state.detail)) {
|
||||||
@@ -325,12 +332,29 @@ const clickInitiate = () => {
|
|||||||
roomStore.setUserUid(userData.uid)
|
roomStore.setUserUid(userData.uid)
|
||||||
roomStore.setDetailUid(state.detail.uid)
|
roomStore.setDetailUid(state.detail.uid)
|
||||||
roomStore.setDetailName(state.detail.name)
|
roomStore.setDetailName(state.detail.name)
|
||||||
|
|
||||||
|
//创建房间
|
||||||
|
const res = await getRoomToken({max_participants: 20});
|
||||||
|
if(res.meta.code != 200){
|
||||||
|
ElMessage.error(res.meta.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(res.data.room.uid){
|
||||||
|
await getInvite(res.data.room.uid,{participants:[{user_uid:state.detail.uid,display_name:state.detail.name}], participant_role: "participant"})
|
||||||
|
}
|
||||||
router.push({
|
router.push({
|
||||||
path: '/conferencingRoom',
|
path: '/conferencingRoom',
|
||||||
query:{
|
query:{
|
||||||
type:1//创建房间,加入房间 2
|
room_uid:res.data.room.uid
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发起协作失败:', error)
|
||||||
|
ElMessage.error('发起协作失败,请重试')
|
||||||
|
} finally {
|
||||||
|
// 无论成功失败,都重置按钮状态
|
||||||
|
state.initiateLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改展示列表 */
|
/** 修改展示列表 */
|
||||||
@@ -472,7 +496,7 @@ function handlePreview(file) {
|
|||||||
|
|
||||||
|
|
||||||
// 暴露给模板
|
// 暴露给模板
|
||||||
const { detail, weekName, tabValue, isShow, load, loadText, isLinkKnow, socketInformation, inviteDialog, cooperation,isShowLoading } = toRefs(state)
|
const { detail, weekName, tabValue, isShow, load, loadText, isLinkKnow, socketInformation, inviteDialog, cooperation,isShowLoading ,initiateLoading } = toRefs(state)
|
||||||
// onMounted(async () => {
|
// onMounted(async () => {
|
||||||
// await mqttClient.connect(`room${Math.random().toString(16).substr(2, 8)}`);
|
// await mqttClient.connect(`room${Math.random().toString(16).substr(2, 8)}`);
|
||||||
// const res = await userStore.getInfo()
|
// const res = await userStore.getInfo()
|
||||||
@@ -494,10 +518,11 @@ const { detail, weekName, tabValue, isShow, load, loadText, isLinkKnow, socketIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-container {
|
.app-container {
|
||||||
padding: 20px;
|
// padding: 20px;
|
||||||
// margin: 0 17px;
|
// margin: 0 17px;
|
||||||
// height: calc(100vh - 50px);
|
height: calc(100vh - 50px);
|
||||||
}
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.message-null {
|
.message-null {
|
||||||
@extend .flex;
|
@extend .flex;
|
||||||
@@ -505,9 +530,8 @@ const { detail, weekName, tabValue, isShow, load, loadText, isLinkKnow, socketIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
.right-content {
|
.right-content {
|
||||||
height: calc(100vh - 40px);
|
height: calc(100vh - 90px);
|
||||||
background: #f4f9ff;
|
background: #f4f9ff;
|
||||||
|
|
||||||
.right-content-title {
|
.right-content-title {
|
||||||
@extend .flex;
|
@extend .flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -529,7 +553,8 @@ const { detail, weekName, tabValue, isShow, load, loadText, isLinkKnow, socketIn
|
|||||||
|
|
||||||
.right-content-file {
|
.right-content-file {
|
||||||
width: calc(100% - 30px);
|
width: calc(100% - 30px);
|
||||||
margin: 0 15px 15px;
|
height:calc(100% - 230px);
|
||||||
|
margin: 0 15px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-top {
|
.file-top {
|
||||||
@@ -545,7 +570,7 @@ const { detail, weekName, tabValue, isShow, load, loadText, isLinkKnow, socketIn
|
|||||||
.content-file-video {
|
.content-file-video {
|
||||||
.file-video-bottom {
|
.file-video-bottom {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100vh - 350px);
|
height: calc(100vh - 360px);
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user