视频会议

This commit is contained in:
duoaohui
2026-05-23 20:36:15 +08:00
parent 7402e72c08
commit ea1fbde7a2

View File

@@ -1,12 +1,5 @@
<template> <template>
<view class="cloud-law-call" :class="{ 'desktop-window': isDesktopWindow }" :style="windowStyle"> <view class="cloud-law-call">
<view v-if="isDesktopWindow" class="window-titlebar" @mousedown="startWindowDrag">
<text class="window-title">视频咨询</text>
<view class="window-actions">
<text class="window-action" @click.stop="resetWindow">还原</text>
<text class="window-action danger" @click.stop="hangup">挂断</text>
</view>
</view>
<view v-if="!isReady" class="entry-state"> <view v-if="!isReady" class="entry-state">
<view class="entry-avatar">{{ entryInitial }}</view> <view class="entry-avatar">{{ entryInitial }}</view>
<text class="entry-title">{{ entryTitle }}</text> <text class="entry-title">{{ entryTitle }}</text>
@@ -96,7 +89,6 @@
</view> </view>
</view> </view>
</view> </view>
<view v-if="isDesktopWindow" class="window-resizer" @mousedown="startWindowResize"></view>
</view> </view>
</template> </template>
@@ -136,13 +128,9 @@ const startedAt = ref(0)
const nowTs = ref(Date.now()) const nowTs = ref(Date.now())
const ended = ref(false) const ended = ref(false)
const preferSelfMain = ref(false) const preferSelfMain = ref(false)
const isDesktopWindow = ref(false)
const windowRect = ref({ left: 120, top: 80, width: 960, height: 540 })
let timerHandle = null let timerHandle = null
let stateStopWatch = null let stateStopWatch = null
let videoRetryHandle = null let videoRetryHandle = null
let dragState = null
let resizeState = null
const digitsOf = (value) => String(value || '').replace(/\D/g, '').slice(0, 9) const digitsOf = (value) => String(value || '').replace(/\D/g, '').slice(0, 9)
const sameUser = (a, b) => String(a || '') === String(b || '') const sameUser = (a, b) => String(a || '') === String(b || '')
@@ -162,15 +150,6 @@ const speakingMap = computed(() => meetingStore.speakingMap || {})
const isVoiceMode = computed(() => callMode.value === 'voice') const isVoiceMode = computed(() => callMode.value === 'voice')
const cameraIcon = computed(() => videoEnabled.value ? videoOnIcon : videoOffIcon) const cameraIcon = computed(() => videoEnabled.value ? videoOnIcon : videoOffIcon)
const micIcon = computed(() => audioEnabled.value ? voiceOnIcon : voiceOffIcon) const micIcon = computed(() => audioEnabled.value ? voiceOnIcon : voiceOffIcon)
const windowStyle = computed(() => {
if (!isDesktopWindow.value) return {}
return {
left: `${windowRect.value.left}px`,
top: `${windowRect.value.top}px`,
width: `${windowRect.value.width}px`,
height: `${windowRect.value.height}px`
}
})
const entryInitial = computed(() => { const entryInitial = computed(() => {
const name = userStore.userInfo?.nickname || userStore.userInfo?.username || '在' const name = userStore.userInfo?.nickname || userStore.userInfo?.username || '在'
@@ -454,80 +433,6 @@ const swapPrimaryTile = () => {
preferSelfMain.value = !preferSelfMain.value preferSelfMain.value = !preferSelfMain.value
} }
const clampWindowRect = (rect) => {
const viewportWidth = window.innerWidth || 1200
const viewportHeight = window.innerHeight || 800
const width = Math.min(Math.max(rect.width, 420), viewportWidth)
const height = Math.min(Math.max(rect.height, 260), viewportHeight)
return {
width,
height,
left: Math.min(Math.max(rect.left, 0), Math.max(0, viewportWidth - width)),
top: Math.min(Math.max(rect.top, 0), Math.max(0, viewportHeight - height))
}
}
const resetWindow = () => {
if (typeof window === 'undefined') return
const width = Math.min(960, Math.max(420, Math.floor(window.innerWidth * 0.82)))
const height = Math.min(540, Math.max(260, Math.floor(window.innerHeight * 0.72)))
windowRect.value = clampWindowRect({
width,
height,
left: Math.floor((window.innerWidth - width) / 2),
top: Math.floor((window.innerHeight - height) / 2)
})
}
const startWindowDrag = (event) => {
if (!isDesktopWindow.value || event.button !== 0) return
dragState = {
startX: event.clientX,
startY: event.clientY,
rect: { ...windowRect.value }
}
window.addEventListener('mousemove', onWindowDrag)
window.addEventListener('mouseup', stopWindowMove)
}
const onWindowDrag = (event) => {
if (!dragState) return
windowRect.value = clampWindowRect({
...dragState.rect,
left: dragState.rect.left + event.clientX - dragState.startX,
top: dragState.rect.top + event.clientY - dragState.startY
})
}
const startWindowResize = (event) => {
if (!isDesktopWindow.value || event.button !== 0) return
event.stopPropagation()
resizeState = {
startX: event.clientX,
startY: event.clientY,
rect: { ...windowRect.value }
}
window.addEventListener('mousemove', onWindowResize)
window.addEventListener('mouseup', stopWindowMove)
}
const onWindowResize = (event) => {
if (!resizeState) return
windowRect.value = clampWindowRect({
...resizeState.rect,
width: resizeState.rect.width + event.clientX - resizeState.startX,
height: resizeState.rect.height + event.clientY - resizeState.startY
})
}
const stopWindowMove = () => {
dragState = null
resizeState = null
window.removeEventListener('mousemove', onWindowDrag)
window.removeEventListener('mousemove', onWindowResize)
window.removeEventListener('mouseup', stopWindowMove)
}
const onMicToggle = async () => { const onMicToggle = async () => {
if (audioLoading.value || leaving.value) return if (audioLoading.value || leaving.value) return
audioLoading.value = true audioLoading.value = true
@@ -589,10 +494,6 @@ onLoad(query => {
}) })
onMounted(() => { onMounted(() => {
if (typeof window !== 'undefined') {
isDesktopWindow.value = window.innerWidth >= 900
if (isDesktopWindow.value) resetWindow()
}
stateStopWatch = watch(() => meetingStore.localState, s => { stateStopWatch = watch(() => meetingStore.localState, s => {
if (s === MEETING_LOCAL_STATE_CONNECTED) { if (s === MEETING_LOCAL_STATE_CONNECTED) {
startTimer() startTimer()
@@ -620,9 +521,6 @@ onBeforeUnmount(() => {
clearTimeout(videoRetryHandle) clearTimeout(videoRetryHandle)
videoRetryHandle = null videoRetryHandle = null
} }
if (typeof window !== 'undefined') {
stopWindowMove()
}
}) })
onUnload(() => { onUnload(() => {
@@ -643,70 +541,6 @@ onUnload(() => {
color: #ffffff; color: #ffffff;
z-index: 1000; z-index: 1000;
} }
.cloud-law-call.desktop-window {
inset: auto;
border-radius: 10rpx;
box-shadow: 0 12rpx 48rpx rgba(0, 0, 0, 0.35);
}
.desktop-window .call-shell,
.desktop-window .entry-state {
height: calc(100% - 64rpx);
margin-top: 64rpx;
}
.window-titlebar {
position: absolute;
left: 0;
right: 0;
top: 0;
height: 64rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(7, 16, 37, 0.96);
border-bottom: 1rpx solid rgba(255, 255, 255, 0.08);
box-sizing: border-box;
cursor: move;
z-index: 20;
user-select: none;
}
.window-title {
font-size: 26rpx;
font-weight: 600;
color: rgba(255, 255, 255, 0.92);
}
.window-actions {
display: flex;
align-items: center;
gap: 20rpx;
}
.window-action {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.78);
cursor: pointer;
}
.window-action.danger {
color: #ff6b75;
}
.window-resizer {
position: absolute;
right: 0;
bottom: 0;
width: 36rpx;
height: 36rpx;
cursor: nwse-resize;
z-index: 30;
}
.window-resizer::after {
content: '';
position: absolute;
right: 8rpx;
bottom: 8rpx;
width: 18rpx;
height: 18rpx;
border-right: 3rpx solid rgba(255, 255, 255, 0.55);
border-bottom: 3rpx solid rgba(255, 255, 255, 0.55);
}
.entry-state { .entry-state {
height: 100%; height: 100%;
display: flex; display: flex;