暂存
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
<template>
|
||||
<div class="jitsi-meeting-view">
|
||||
<!-- 加载中 -->
|
||||
<div v-if="loading" class="loading-container">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-text">正在加载会议...</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误提示 -->
|
||||
<div v-else-if="error" class="error-container">
|
||||
<div class="error-icon">⚠️</div>
|
||||
<div class="error-title">无法加入会议</div>
|
||||
<div class="error-message">{{ error }}</div>
|
||||
<button class="error-btn" @click="retryJoin">重新尝试</button>
|
||||
</div>
|
||||
|
||||
<!-- Jitsi 会议容器 -->
|
||||
<div v-else ref="jitsiContainer" id="jitsi-meet-container" class="meeting-container"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { workcaseChatAPI } from '@/api/workcase'
|
||||
// @ts-ignore
|
||||
import { TokenManager } from 'shared/api'
|
||||
import axios from 'axios'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
const meetingId = ref('')
|
||||
const roomId = ref('')
|
||||
const jitsiContainer = ref<HTMLDivElement | null>(null)
|
||||
let jitsiApi: any = null
|
||||
|
||||
// 从URL获取参数
|
||||
const getMeetingParams = () => {
|
||||
// 优先从query参数获取
|
||||
meetingId.value = route.query.meetingId as string || ''
|
||||
roomId.value = route.query.roomId as string || ''
|
||||
const tokenParam = route.query.token as string || ''
|
||||
|
||||
console.log('[JitsiMeetingView] URL参数:', {
|
||||
meetingId: meetingId.value,
|
||||
roomId: roomId.value,
|
||||
hasToken: !!tokenParam
|
||||
})
|
||||
|
||||
return { meetingId: meetingId.value, roomId: roomId.value, token: tokenParam }
|
||||
}
|
||||
|
||||
// 使用token刷新登录状态
|
||||
const refreshLoginWithToken = async (token: string) => {
|
||||
try {
|
||||
console.log('[JitsiMeetingView] 使用token刷新登录状态...')
|
||||
|
||||
const response = await axios.post(
|
||||
'/api/urban-lifeline/auth/refresh',
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (response.data?.success && response.data?.data) {
|
||||
const loginDomain = response.data.data
|
||||
const newToken = loginDomain.token
|
||||
|
||||
// 保存到localStorage
|
||||
localStorage.setItem('token', newToken)
|
||||
localStorage.setItem('loginDomain', JSON.stringify(loginDomain))
|
||||
|
||||
// 使用TokenManager设置token
|
||||
TokenManager.setToken(newToken)
|
||||
|
||||
console.log('[JitsiMeetingView] 登录状态刷新成功')
|
||||
return true
|
||||
} else {
|
||||
console.error('[JitsiMeetingView] 刷新登录失败:', response.data?.message)
|
||||
return false
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('[JitsiMeetingView] 刷新登录异常:', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载 Jitsi External API 脚本
|
||||
const loadJitsiScript = (): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 检查是否已经加载
|
||||
if ((window as any).JitsiMeetExternalAPI) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = 'http://localhost:8280/external_api.js'
|
||||
script.async = true
|
||||
script.onload = () => {
|
||||
console.log('[JitsiMeetingView] Jitsi External API 脚本加载成功')
|
||||
resolve()
|
||||
}
|
||||
script.onerror = () => {
|
||||
reject(new Error('加载 Jitsi External API 失败'))
|
||||
}
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化 Jitsi Meet
|
||||
const initJitsiMeet = async (jitsiServerUrl: string, roomName: string, jwt: string, displayName: string) => {
|
||||
try {
|
||||
console.log('[JitsiMeetingView] 初始化 Jitsi Meet:', {
|
||||
server: jitsiServerUrl,
|
||||
room: roomName,
|
||||
name: displayName
|
||||
})
|
||||
|
||||
// 加载 External API 脚本
|
||||
await loadJitsiScript()
|
||||
|
||||
const JitsiMeetExternalAPI = (window as any).JitsiMeetExternalAPI
|
||||
|
||||
// 解析 URL 获取协议和域名
|
||||
const urlObj = new URL(jitsiServerUrl)
|
||||
const domain = urlObj.host // 获取 host (localhost:8280)
|
||||
const useHttps = urlObj.protocol === 'https:'
|
||||
|
||||
console.log('[JitsiMeetingView] 解析服务器配置:', {
|
||||
domain,
|
||||
protocol: urlObj.protocol,
|
||||
useHttps
|
||||
})
|
||||
|
||||
// 配置选项 - 关键!指定是否使用 HTTPS
|
||||
// 👇 替换你原有的 options 全部代码,保留原有逻辑,仅修改配置项
|
||||
const options: any = {
|
||||
roomName: roomName,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
parentNode: jitsiContainer.value,
|
||||
jwt: jwt,
|
||||
// ✅ 修复1:核心!正确的顶层配置项是 useHTTPS(不是 https),强制关闭HTTPS
|
||||
useHTTPS: false,
|
||||
// ✅ 修复2:禁用WebSocket的HTTPS,彻底阻止wss://请求(局域网必加)
|
||||
useWebSocket: false,
|
||||
configOverwrite: {
|
||||
startWithAudioMuted: false,
|
||||
startWithVideoMuted: false,
|
||||
enableWelcomePage: false,
|
||||
prejoinPageEnabled: false,
|
||||
disableDeepLinking: true,
|
||||
enableChat: true,
|
||||
enableScreenSharing: true,
|
||||
// ✅ 修复3:叠加禁用,彻底阻断API内部的HTTPS强制逻辑(局域网核心)
|
||||
useHTTPS: false,
|
||||
// ✅ 修复4:禁用第三方HTTPS资源请求,避免混合内容报错
|
||||
disableThirdPartyRequests: false,
|
||||
// ✅ 修复5:关闭服务端的HTTPS重定向检测
|
||||
disableHttpsRedirect: true
|
||||
},
|
||||
interfaceConfigOverwrite: {
|
||||
SHOW_JITSI_WATERMARK: false,
|
||||
SHOW_WATERMARK_FOR_GUESTS: false,
|
||||
DISABLE_JOIN_LEAVE_NOTIFICATIONS: false
|
||||
},
|
||||
userInfo: {
|
||||
displayName: displayName
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[JitsiMeetingView] 创建 JitsiMeetExternalAPI 实例,https=' + useHttps)
|
||||
jitsiApi = new JitsiMeetExternalAPI(domain, options)
|
||||
|
||||
// 监听会议准备就绪事件
|
||||
jitsiApi.addEventListener('videoConferenceJoined', (event: any) => {
|
||||
console.log('[JitsiMeetingView] 用户已加入会议:', event)
|
||||
})
|
||||
|
||||
// 监听用户离开会议事件
|
||||
jitsiApi.addEventListener('videoConferenceLeft', (event: any) => {
|
||||
console.log('[JitsiMeetingView] 用户离开会议:', event)
|
||||
handleLeaveMeeting()
|
||||
})
|
||||
|
||||
// 监听准备关闭事件
|
||||
jitsiApi.addEventListener('readyToClose', () => {
|
||||
console.log('[JitsiMeetingView] Jitsi 准备关闭')
|
||||
handleLeaveMeeting()
|
||||
})
|
||||
|
||||
console.log('[JitsiMeetingView] Jitsi Meet 初始化完成')
|
||||
} catch (err: any) {
|
||||
console.error('[JitsiMeetingView] 初始化 Jitsi Meet 失败:', err)
|
||||
error.value = err.message || '初始化会议失败'
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加入会议
|
||||
const joinMeeting = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
|
||||
const { meetingId: mid, roomId: rid, token } = getMeetingParams()
|
||||
|
||||
if (!mid) {
|
||||
error.value = '缺少会议ID参数'
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有loginDomain
|
||||
const hasLoginDomain = !!localStorage.getItem('loginDomain')
|
||||
const hasToken = !!localStorage.getItem('token') || !!token
|
||||
|
||||
console.log('[JitsiMeetingView] 登录状态检查:', {
|
||||
hasLoginDomain,
|
||||
hasToken
|
||||
})
|
||||
|
||||
// 如果没有loginDomain但有token(小程序或外部链接访问),先刷新登录
|
||||
if (!hasLoginDomain && token) {
|
||||
const refreshed = await refreshLoginWithToken(token)
|
||||
if (!refreshed) {
|
||||
error.value = '登录验证失败,请重新获取会议链接'
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 检查登录状态
|
||||
if (!TokenManager.hasToken()) {
|
||||
error.value = '未登录,请先登录'
|
||||
loading.value = false
|
||||
// 重定向到登录页
|
||||
const currentUrl = window.location.href
|
||||
window.location.href = `/login?redirect=${encodeURIComponent(currentUrl)}`
|
||||
return
|
||||
}
|
||||
|
||||
// 调用后端接口加入会议
|
||||
console.log('[JitsiMeetingView] 正在加入会议:', mid)
|
||||
const result = await workcaseChatAPI.joinVideoMeeting(mid)
|
||||
|
||||
if (result.success && result.data) {
|
||||
const meetingData = result.data
|
||||
|
||||
// 检查会议数据
|
||||
if (!meetingData.jitsiServerUrl || !meetingData.jitsiRoomName || !meetingData.jwtToken) {
|
||||
error.value = '会议数据不完整,无法加入'
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户名
|
||||
const loginDomain = JSON.parse(localStorage.getItem('loginDomain') || '{}')
|
||||
const displayName = loginDomain.user?.userName || '访客'
|
||||
|
||||
// 先设置 loading 为 false,让容器渲染出来
|
||||
loading.value = false
|
||||
|
||||
// 等待下一个 tick,确保 DOM 已渲染
|
||||
await nextTick()
|
||||
|
||||
// 检查容器是否已经渲染
|
||||
if (!jitsiContainer.value) {
|
||||
error.value = '会议容器未准备好'
|
||||
return
|
||||
}
|
||||
|
||||
// 使用 Jitsi External API 初始化会议
|
||||
await initJitsiMeet(
|
||||
meetingData.jitsiServerUrl,
|
||||
meetingData.jitsiRoomName,
|
||||
meetingData.jwtToken,
|
||||
displayName
|
||||
)
|
||||
} else {
|
||||
error.value = result.message || '加入会议失败'
|
||||
loading.value = false
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('[JitsiMeetingView] 加入会议异常:', err)
|
||||
error.value = err.message || '加入会议失败'
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理离开会议(不结束)
|
||||
const handleLeaveMeeting = () => {
|
||||
console.log('[JitsiMeetingView] 处理离开会议,返回聊天室:', roomId.value)
|
||||
|
||||
// 清理 Jitsi API
|
||||
if (jitsiApi) {
|
||||
jitsiApi.dispose()
|
||||
jitsiApi = null
|
||||
}
|
||||
|
||||
// 如果有roomId,返回到对应的聊天室
|
||||
if (roomId.value) {
|
||||
router.push(`/chatRoom?roomId=${roomId.value}`)
|
||||
} else {
|
||||
// 没有roomId,尝试返回上一页或关闭窗口
|
||||
if (window.history.length > 1) {
|
||||
router.back()
|
||||
} else {
|
||||
window.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理结束会议
|
||||
const handleEndMeeting = async () => {
|
||||
try {
|
||||
console.log('[JitsiMeetingView] 处理结束会议:', meetingId.value)
|
||||
|
||||
if (meetingId.value) {
|
||||
// 调用后端接口结束会议
|
||||
const result = await workcaseChatAPI.endVideoMeeting(meetingId.value)
|
||||
console.log('[JitsiMeetingView] 结束会议结果:', result)
|
||||
}
|
||||
|
||||
// 返回上一页或关闭窗口
|
||||
handleLeaveMeeting()
|
||||
} catch (err) {
|
||||
console.error('[JitsiMeetingView] 结束会议失败:', err)
|
||||
// 即使失败也返回上一页
|
||||
handleLeaveMeeting()
|
||||
}
|
||||
}
|
||||
|
||||
// 重新尝试加入
|
||||
const retryJoin = () => {
|
||||
joinMeeting()
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
console.log('[JitsiMeetingView] 组件挂载')
|
||||
joinMeeting()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
console.log('[JitsiMeetingView] 组件卸载')
|
||||
// 清理 Jitsi API
|
||||
if (jitsiApi) {
|
||||
jitsiApi.dispose()
|
||||
jitsiApi = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import url('./JitsiMeetingView.scss')
|
||||
</style>
|
||||
Reference in New Issue
Block a user