temp jitsi
This commit is contained in:
@@ -47,18 +47,59 @@ $brand-color-hover: #004488;
|
||||
|
||||
// ==================== Jitsi Meet会议容器 ====================
|
||||
.meeting-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
height: 400px;
|
||||
background: #000;
|
||||
border-bottom: 2px solid $brand-color;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e5e7eb;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.meeting-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.close-meeting-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 12px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.meeting-iframe {
|
||||
width: 100%;
|
||||
height: calc(100% - 48px);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮禁用状态
|
||||
.action-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
border-color: #e2e8f0;
|
||||
color: #64748b;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,14 @@
|
||||
|
||||
<!-- Jitsi Meet会议iframe -->
|
||||
<div v-if="showMeeting && meetingUrl" class="meeting-container">
|
||||
<IframeView :src="meetingUrl" />
|
||||
<div class="meeting-header">
|
||||
<span>视频会议进行中</span>
|
||||
<button class="close-meeting-btn" @click="handleEndMeeting">
|
||||
<X :size="20" />
|
||||
结束会议
|
||||
</button>
|
||||
</div>
|
||||
<IframeView :src="meetingUrl" class="meeting-iframe" />
|
||||
</div>
|
||||
|
||||
<!-- 聊天消息列表 -->
|
||||
@@ -72,12 +79,16 @@
|
||||
<footer class="input-area">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="action-buttons">
|
||||
<!-- 发起会议按钮(始终显示) -->
|
||||
<button class="action-btn" @click="$emit('start-meeting')">
|
||||
<!-- 发起会议按钮 -->
|
||||
<button
|
||||
class="action-btn"
|
||||
:disabled="meetingLoading || showMeeting"
|
||||
@click="handleStartMeeting"
|
||||
>
|
||||
<Video :size="18" />
|
||||
发起会议
|
||||
{{ showMeeting ? '会议进行中' : '发起会议' }}
|
||||
</button>
|
||||
|
||||
|
||||
<!-- 额外的操作按钮插槽 -->
|
||||
<slot name="action-area"></slot>
|
||||
</div>
|
||||
@@ -125,17 +136,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { FileText, Video, Paperclip, Send } from 'lucide-vue-next'
|
||||
import { ref, nextTick, onMounted } from 'vue'
|
||||
import { FileText, Video, Paperclip, Send, X } from 'lucide-vue-next'
|
||||
import IframeView from 'shared/components/iframe/IframeView.vue'
|
||||
import type { ChatRoomMessageVO } from '@/types/workcase'
|
||||
import { createVideoMeeting, getActiveMeeting, endVideoMeeting } from '@/api/workcase/meeting'
|
||||
|
||||
interface Props {
|
||||
messages: ChatRoomMessageVO[]
|
||||
currentUserId: string
|
||||
roomId: string
|
||||
roomName?: string
|
||||
meetingUrl?: string
|
||||
showMeeting?: boolean
|
||||
workcaseId?: string
|
||||
fileDownloadUrl?: string
|
||||
hasMore?: boolean
|
||||
loadingMore?: boolean
|
||||
@@ -143,7 +155,6 @@ interface Props {
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
roomName: '聊天室',
|
||||
showMeeting: false,
|
||||
fileDownloadUrl: '',
|
||||
hasMore: true,
|
||||
loadingMore: false
|
||||
@@ -153,11 +164,76 @@ const FILE_DOWNLOAD_URL = props.fileDownloadUrl
|
||||
|
||||
const emit = defineEmits<{
|
||||
'send-message': [content: string, files: File[]]
|
||||
'start-meeting': []
|
||||
'download-file': [fileId: string]
|
||||
'load-more': []
|
||||
}>()
|
||||
|
||||
// 会议相关状态
|
||||
const showMeeting = ref(false)
|
||||
const meetingUrl = ref('')
|
||||
const currentMeetingId = ref('')
|
||||
const meetingLoading = ref(false)
|
||||
|
||||
// 创建并加入会议
|
||||
const handleStartMeeting = async () => {
|
||||
try {
|
||||
meetingLoading.value = true
|
||||
|
||||
// 创建会议
|
||||
const createRes = await createVideoMeeting({
|
||||
roomId: props.roomId,
|
||||
workcaseId: props.workcaseId,
|
||||
meetingName: `工单 ${props.workcaseId || props.roomId} 技术支持`,
|
||||
maxParticipants: 10
|
||||
})
|
||||
|
||||
if (createRes.code === 0 && createRes.data) {
|
||||
currentMeetingId.value = createRes.data.meetingId
|
||||
meetingUrl.value = createRes.data.iframeUrl
|
||||
showMeeting.value = true
|
||||
} else {
|
||||
console.error('创建会议失败:', createRes.message)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建会议异常:', error)
|
||||
} finally {
|
||||
meetingLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 结束会议
|
||||
const handleEndMeeting = async () => {
|
||||
if (!currentMeetingId.value) return
|
||||
|
||||
try {
|
||||
await endVideoMeeting(currentMeetingId.value)
|
||||
showMeeting.value = false
|
||||
meetingUrl.value = ''
|
||||
currentMeetingId.value = ''
|
||||
} catch (error) {
|
||||
console.error('结束会议失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有活跃会议
|
||||
const checkActiveMeeting = async () => {
|
||||
try {
|
||||
const res = await getActiveMeeting(props.roomId)
|
||||
if (res.code === 0 && res.data) {
|
||||
currentMeetingId.value = res.data.meetingId
|
||||
meetingUrl.value = res.data.iframeUrl
|
||||
showMeeting.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('无活跃会议')
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时检查是否有活跃会议
|
||||
onMounted(() => {
|
||||
checkActiveMeeting()
|
||||
})
|
||||
|
||||
// 滚动到顶部加载更多
|
||||
const handleScroll = (e: Event) => {
|
||||
const target = e.target as HTMLElement
|
||||
@@ -293,7 +369,9 @@ const renderMarkdown = (text: string): string => {
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
scrollToBottom
|
||||
scrollToBottom,
|
||||
handleStartMeeting,
|
||||
handleEndMeeting
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user