新增功能
This commit is contained in:
@@ -1,15 +1,21 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="`与 ${patientName} 聊天`"
|
||||
width="900px"
|
||||
top="10vh"
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
:destroy-on-close="true"
|
||||
class="chat-dialog"
|
||||
>
|
||||
<div class="chat-container">
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-show="visible"
|
||||
ref="chatWindowRef"
|
||||
class="chat-floating-window"
|
||||
:style="windowStyle"
|
||||
>
|
||||
<div
|
||||
class="chat-window-header"
|
||||
@mousedown="onHeaderMouseDown"
|
||||
>
|
||||
<span class="chat-window-title">与 {{ patientName }} 聊天</span>
|
||||
<el-button type="danger" link class="chat-close-btn" @click="handleClose">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="chat-container">
|
||||
<div v-if="!isReady" class="loading-container">
|
||||
<el-icon class="is-loading"><Loading /></el-icon>
|
||||
<span>{{ loadingText }}</span>
|
||||
@@ -49,20 +55,35 @@
|
||||
</UIKitProvider>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 挂载 TUICallKit 组件(固定居中显示) -->
|
||||
<TUICallKit
|
||||
v-if="isCallReady"
|
||||
:allowedMinimized="true"
|
||||
:allowedFullScreen="true"
|
||||
class="chat-dialog-call-kit"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 挂载 TUICallKit 组件(可拖动,仅在通话时显示) -->
|
||||
<div
|
||||
v-if="isCallReady && showCallKitWindow"
|
||||
ref="callKitWrapperRef"
|
||||
class="chat-dialog-call-kit-wrapper"
|
||||
:style="callKitStyle"
|
||||
>
|
||||
<div
|
||||
class="call-kit-drag-handle"
|
||||
@mousedown="onCallKitDragStart"
|
||||
>
|
||||
<span class="drag-handle-text">视频通话</span>
|
||||
<el-icon class="drag-handle-icon"><Rank /></el-icon>
|
||||
</div>
|
||||
<div class="call-kit-content">
|
||||
<TUICallKit
|
||||
:allowedMinimized="true"
|
||||
:allowedFullScreen="true"
|
||||
class="chat-dialog-call-kit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick, watch } from 'vue'
|
||||
import { Loading } from '@element-plus/icons-vue'
|
||||
import { ref, nextTick, watch, computed } from 'vue'
|
||||
import { Loading, Close, Rank } from '@element-plus/icons-vue'
|
||||
import { getCallSignature } from '@/api/tcm'
|
||||
import feedback from '@/utils/feedback'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
@@ -94,6 +115,7 @@ const loadingText = ref('正在初始化...')
|
||||
const patientUserId = ref('')
|
||||
const conversationId = ref('')
|
||||
const isCallReady = ref(false)
|
||||
const showCallKitWindow = ref(false) // 仅在通话中显示视频窗口
|
||||
const { login, logout } = useLoginState()
|
||||
const { setActiveConversation, createC2CConversation, activeConversation } = useConversationListState()
|
||||
const userStore = useUserStore()
|
||||
@@ -166,12 +188,17 @@ watch(() => activeConversation.value, async (newConversation) => {
|
||||
|
||||
const open = async (data: { patientId: number; patientName: string; diagnosisId?: number }) => {
|
||||
visible.value = true
|
||||
posX.value = 100
|
||||
posY.value = 80
|
||||
callKitX.value = Math.max(0, (window.innerWidth - 600) / 2)
|
||||
callKitY.value = Math.max(0, (window.innerHeight - 436) / 2)
|
||||
patientName.value = data.patientName
|
||||
patientId.value = data.patientId
|
||||
diagnosisId.value = data.diagnosisId || null
|
||||
error.value = ''
|
||||
isReady.value = false
|
||||
isCallReady.value = false
|
||||
showCallKitWindow.value = false
|
||||
loadingText.value = '正在获取签名...'
|
||||
|
||||
try {
|
||||
@@ -220,6 +247,13 @@ const open = async (data: { patientId: number; patientName: string; diagnosisId?
|
||||
}
|
||||
}
|
||||
isCallReady.value = !!(window as any).__TUICallKitInited__
|
||||
if (isCallReady.value) {
|
||||
// 仅在通话时显示视频窗口,无通话时隐藏
|
||||
TUICallKitServer.setCallback({
|
||||
beforeCalling: () => { showCallKitWindow.value = true },
|
||||
afterCalling: () => { showCallKitWindow.value = false }
|
||||
})
|
||||
}
|
||||
console.log('Chat UIKit 登录成功,患者用户ID:', patientUserId.value)
|
||||
|
||||
loadingText.value = '加载聊天界面...'
|
||||
@@ -308,7 +342,90 @@ const startGroupVideoCall = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 浮动窗口位置与拖动
|
||||
const chatWindowRef = ref<HTMLElement>()
|
||||
const posX = ref(100)
|
||||
const posY = ref(80)
|
||||
const isDragging = ref(false)
|
||||
const dragStartX = ref(0)
|
||||
const dragStartY = ref(0)
|
||||
const posStartX = ref(0)
|
||||
const posStartY = ref(0)
|
||||
|
||||
// TUICallKit 视频窗口拖动
|
||||
const callKitWrapperRef = ref<HTMLElement>()
|
||||
const callKitX = ref(0)
|
||||
const callKitY = ref(0)
|
||||
const isCallKitDragging = ref(false)
|
||||
const callKitDragStartX = ref(0)
|
||||
const callKitDragStartY = ref(0)
|
||||
const callKitPosStartX = ref(0)
|
||||
const callKitPosStartY = ref(0)
|
||||
|
||||
const callKitStyle = computed(() => ({
|
||||
left: `${callKitX.value}px`,
|
||||
top: `${callKitY.value}px`
|
||||
}))
|
||||
|
||||
const onCallKitDragStart = (e: MouseEvent) => {
|
||||
isCallKitDragging.value = true
|
||||
callKitDragStartX.value = e.clientX
|
||||
callKitDragStartY.value = e.clientY
|
||||
callKitPosStartX.value = callKitX.value
|
||||
callKitPosStartY.value = callKitY.value
|
||||
document.addEventListener('mousemove', onCallKitDragMove)
|
||||
document.addEventListener('mouseup', onCallKitDragEnd)
|
||||
}
|
||||
|
||||
const onCallKitDragMove = (e: MouseEvent) => {
|
||||
if (!isCallKitDragging.value) return
|
||||
callKitX.value = Math.max(0, callKitPosStartX.value + e.clientX - callKitDragStartX.value)
|
||||
callKitY.value = Math.max(0, callKitPosStartY.value + e.clientY - callKitDragStartY.value)
|
||||
}
|
||||
|
||||
const onCallKitDragEnd = () => {
|
||||
isCallKitDragging.value = false
|
||||
document.removeEventListener('mousemove', onCallKitDragMove)
|
||||
document.removeEventListener('mouseup', onCallKitDragEnd)
|
||||
}
|
||||
|
||||
const windowStyle = computed(() => ({
|
||||
left: `${posX.value}px`,
|
||||
top: `${posY.value}px`
|
||||
}))
|
||||
|
||||
const onHeaderMouseDown = (e: MouseEvent) => {
|
||||
if ((e.target as HTMLElement).closest('.chat-close-btn')) return
|
||||
isDragging.value = true
|
||||
dragStartX.value = e.clientX
|
||||
dragStartY.value = e.clientY
|
||||
posStartX.value = posX.value
|
||||
posStartY.value = posY.value
|
||||
document.addEventListener('mousemove', onHeaderMouseMove)
|
||||
document.addEventListener('mouseup', onHeaderMouseUp)
|
||||
}
|
||||
|
||||
const onHeaderMouseMove = (e: MouseEvent) => {
|
||||
if (!isDragging.value) return
|
||||
posX.value = Math.max(0, posStartX.value + e.clientX - dragStartX.value)
|
||||
posY.value = Math.max(0, posStartY.value + e.clientY - dragStartY.value)
|
||||
}
|
||||
|
||||
const onHeaderMouseUp = () => {
|
||||
isDragging.value = false
|
||||
document.removeEventListener('mousemove', onHeaderMouseMove)
|
||||
document.removeEventListener('mouseup', onHeaderMouseUp)
|
||||
}
|
||||
|
||||
const handleClose = async () => {
|
||||
// 关闭时如有通话中/拨通中,自动挂断
|
||||
if (isCallReady.value && showCallKitWindow.value) {
|
||||
try {
|
||||
await TUICallKitServer.hangup()
|
||||
} catch (err) {
|
||||
console.warn('挂断通话时出错:', err)
|
||||
}
|
||||
}
|
||||
try {
|
||||
// 登出 Chat
|
||||
// await logout()
|
||||
@@ -317,6 +434,7 @@ const handleClose = async () => {
|
||||
}
|
||||
|
||||
visible.value = false
|
||||
showCallKitWindow.value = false
|
||||
// isReady.value = false
|
||||
// error.value = ''
|
||||
// patientId.value = null
|
||||
@@ -329,31 +447,47 @@ defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.chat-dialog {
|
||||
:deep(.el-dialog__body) {
|
||||
padding: 0;
|
||||
height: 75vh;
|
||||
overflow: visible;
|
||||
/* 浮动窗口:无遮罩,不遮挡页面,可任意拖动 */
|
||||
.chat-floating-window {
|
||||
position: fixed;
|
||||
width: 900px;
|
||||
height: 75vh;
|
||||
min-height: 400px;
|
||||
max-height: 90vh;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
|
||||
z-index: 2501;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-window-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
background: #fff;
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
|
||||
.chat-window-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.el-dialog__header) {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
// 确保对话框有合适的 z-index
|
||||
:deep(.el-overlay) {
|
||||
z-index: 2500;
|
||||
}
|
||||
|
||||
// 确保对话框本身的 z-index 低于 TUICallKit
|
||||
:deep(.el-dialog) {
|
||||
z-index: 2501;
|
||||
|
||||
.chat-close-btn {
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
height: 75vh;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -415,18 +549,61 @@ defineExpose({ open })
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* TUICallKit 容器样式 - 限制在对话框内拖动 */
|
||||
/* TUICallKit 可拖动容器 */
|
||||
.chat-dialog-call-kit-wrapper {
|
||||
position: fixed;
|
||||
width: 600px;
|
||||
height: 436px;
|
||||
max-width: 90vw;
|
||||
z-index: 300000;
|
||||
background: #1a1a1a;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.call-kit-drag-handle {
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
background: #2a2a2a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
|
||||
.drag-handle-text {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.drag-handle-icon {
|
||||
color: #999;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.call-kit-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chat-dialog-call-kit {
|
||||
position: fixed !important;
|
||||
top: 50% !important;
|
||||
left: 50% !important;
|
||||
transform: translate(-50%, -50%) !important;
|
||||
width: 600px !important;
|
||||
height: 400px !important;
|
||||
max-width: 90% !important;
|
||||
max-height: 80% !important;
|
||||
z-index: 300000 !important;
|
||||
pointer-events: auto !important;
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
border-radius: 0 0 12px 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -444,8 +621,8 @@ body > div[data-reka-focus-guard] {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
/* 确保聊天对话框内的弹出层也能正确显示 */
|
||||
.chat-dialog [data-reka-popper-content-wrapper] {
|
||||
/* 确保聊天窗口内的弹出层也能正确显示 */
|
||||
.chat-floating-window [data-reka-popper-content-wrapper] {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user