mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-29 14:46:33 +08:00
优化控制台与设置页切换时的 WebRTC 会话保活与恢复逻辑
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, watch } from 'vue'
|
||||
import { KeepAlive, onMounted, watch } from 'vue'
|
||||
import { RouterView, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useSystemStore } from '@/stores/system'
|
||||
@@ -56,5 +56,10 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<KeepAlive v-if="authStore.isAuthenticated">
|
||||
<component :is="Component" v-if="route.name === 'Console'" />
|
||||
</KeepAlive>
|
||||
<component :is="Component" v-if="route.name !== 'Console' || !authStore.isAuthenticated" />
|
||||
</RouterView>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed, watch, nextTick } from 'vue'
|
||||
import { ref, onMounted, onUnmounted, onActivated, onDeactivated, computed, watch, nextTick } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useSystemStore } from '@/stores/system'
|
||||
@@ -137,6 +137,8 @@ let accumulatedDelta = { x: 0, y: 0 } // For relative mode: accumulate deltas be
|
||||
|
||||
// Cursor visibility (from localStorage, updated via storage event)
|
||||
const cursorVisible = ref(localStorage.getItem('hidShowCursor') !== 'false')
|
||||
let interactionListenersBound = false
|
||||
const isConsoleActive = ref(false)
|
||||
|
||||
function syncMouseModeFromConfig() {
|
||||
const mouseAbsolute = configStore.hid?.mouse_absolute
|
||||
@@ -610,6 +612,7 @@ function waitForVideoFirstFrame(el: HTMLVideoElement, timeoutMs = 2000): Promise
|
||||
|
||||
function shouldSuppressAutoReconnect(): boolean {
|
||||
return videoMode.value === 'mjpeg'
|
||||
|| !isConsoleActive.value
|
||||
|| videoSession.localSwitching.value
|
||||
|| videoSession.backendSwitching.value
|
||||
|| videoRestarting.value
|
||||
@@ -1951,6 +1954,10 @@ function handlePointerLockError() {
|
||||
isPointerLocked.value = false
|
||||
}
|
||||
|
||||
function handleFullscreenChange() {
|
||||
isFullscreen.value = !!document.fullscreenElement
|
||||
}
|
||||
|
||||
function handleBlur() {
|
||||
pressedKeys.value = []
|
||||
activeModifierMask.value = 0
|
||||
@@ -2003,6 +2010,71 @@ function handleMouseSendIntervalStorage(e: StorageEvent) {
|
||||
setMouseMoveSendInterval(loadMouseMoveSendIntervalFromStorage())
|
||||
}
|
||||
|
||||
function registerInteractionListeners() {
|
||||
if (interactionListenersBound) return
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
window.addEventListener('keyup', handleKeyUp)
|
||||
window.addEventListener('blur', handleBlur)
|
||||
window.addEventListener('mouseup', handleWindowMouseUp)
|
||||
window.addEventListener('hidCursorVisibilityChanged', handleCursorVisibilityChange as EventListener)
|
||||
window.addEventListener('hidMouseSendIntervalChanged', handleMouseSendIntervalChange as EventListener)
|
||||
window.addEventListener('storage', handleMouseSendIntervalStorage)
|
||||
|
||||
document.addEventListener('pointerlockchange', handlePointerLockChange)
|
||||
document.addEventListener('pointerlockerror', handlePointerLockError)
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
||||
|
||||
interactionListenersBound = true
|
||||
}
|
||||
|
||||
function unregisterInteractionListeners() {
|
||||
if (!interactionListenersBound) return
|
||||
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
window.removeEventListener('keyup', handleKeyUp)
|
||||
window.removeEventListener('blur', handleBlur)
|
||||
window.removeEventListener('mouseup', handleWindowMouseUp)
|
||||
window.removeEventListener('hidCursorVisibilityChanged', handleCursorVisibilityChange as EventListener)
|
||||
window.removeEventListener('hidMouseSendIntervalChanged', handleMouseSendIntervalChange as EventListener)
|
||||
window.removeEventListener('storage', handleMouseSendIntervalStorage)
|
||||
|
||||
document.removeEventListener('pointerlockchange', handlePointerLockChange)
|
||||
document.removeEventListener('pointerlockerror', handlePointerLockError)
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
||||
|
||||
interactionListenersBound = false
|
||||
}
|
||||
|
||||
async function activateConsoleView() {
|
||||
isConsoleActive.value = true
|
||||
registerInteractionListeners()
|
||||
|
||||
if (videoMode.value !== 'mjpeg' && webrtc.videoTrack.value) {
|
||||
await nextTick()
|
||||
await rebindWebRTCVideo()
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
videoMode.value !== 'mjpeg'
|
||||
&& !webrtc.isConnected.value
|
||||
&& !webrtc.isConnecting.value
|
||||
&& !videoSession.localSwitching.value
|
||||
&& !videoSession.backendSwitching.value
|
||||
&& !initialModeRestoreInProgress
|
||||
) {
|
||||
await connectWebRTCOnly(videoMode.value)
|
||||
}
|
||||
}
|
||||
|
||||
function deactivateConsoleView() {
|
||||
isConsoleActive.value = false
|
||||
handleBlur()
|
||||
exitPointerLock()
|
||||
unregisterInteractionListeners()
|
||||
}
|
||||
|
||||
// ActionBar handlers
|
||||
// (MSD and Settings are now handled by ActionBar component directly)
|
||||
|
||||
@@ -2074,30 +2146,12 @@ onMounted(async () => {
|
||||
syncMouseModeFromConfig()
|
||||
}).catch(() => {})
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
window.addEventListener('keyup', handleKeyUp)
|
||||
window.addEventListener('blur', handleBlur)
|
||||
window.addEventListener('mouseup', handleWindowMouseUp)
|
||||
|
||||
setMouseMoveSendInterval(loadMouseMoveSendIntervalFromStorage())
|
||||
|
||||
// Listen for cursor visibility changes from HidConfigPopover
|
||||
window.addEventListener('hidCursorVisibilityChanged', handleCursorVisibilityChange as EventListener)
|
||||
window.addEventListener('hidMouseSendIntervalChanged', handleMouseSendIntervalChange as EventListener)
|
||||
window.addEventListener('storage', handleMouseSendIntervalStorage)
|
||||
|
||||
watch(() => configStore.hid?.mouse_absolute, () => {
|
||||
syncMouseModeFromConfig()
|
||||
})
|
||||
|
||||
// Pointer Lock event listeners
|
||||
document.addEventListener('pointerlockchange', handlePointerLockChange)
|
||||
document.addEventListener('pointerlockerror', handlePointerLockError)
|
||||
|
||||
document.addEventListener('fullscreenchange', () => {
|
||||
isFullscreen.value = !!document.fullscreenElement
|
||||
})
|
||||
|
||||
const storedTheme = localStorage.getItem('theme')
|
||||
if (storedTheme === 'dark' || (!storedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
isDark.value = true
|
||||
@@ -2118,7 +2172,17 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
void activateConsoleView()
|
||||
})
|
||||
|
||||
onDeactivated(() => {
|
||||
deactivateConsoleView()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
deactivateConsoleView()
|
||||
|
||||
// Reset initial device info flag
|
||||
initialDeviceInfoReceived = false
|
||||
initialModeRestoreDone = false
|
||||
@@ -2154,18 +2218,6 @@ onUnmounted(() => {
|
||||
|
||||
// Exit pointer lock if active
|
||||
exitPointerLock()
|
||||
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
window.removeEventListener('keyup', handleKeyUp)
|
||||
window.removeEventListener('blur', handleBlur)
|
||||
window.removeEventListener('mouseup', handleWindowMouseUp)
|
||||
window.removeEventListener('hidCursorVisibilityChanged', handleCursorVisibilityChange as EventListener)
|
||||
window.removeEventListener('hidMouseSendIntervalChanged', handleMouseSendIntervalChange as EventListener)
|
||||
window.removeEventListener('storage', handleMouseSendIntervalStorage)
|
||||
|
||||
// Remove pointer lock event listeners
|
||||
document.removeEventListener('pointerlockchange', handlePointerLockChange)
|
||||
document.removeEventListener('pointerlockerror', handlePointerLockError)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useSystemStore } from '@/stores/system'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
@@ -83,6 +84,7 @@ import {
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const { t, te, locale } = useI18n()
|
||||
const route = useRoute()
|
||||
const systemStore = useSystemStore()
|
||||
const configStore = useConfigStore()
|
||||
const authStore = useAuthStore()
|
||||
@@ -92,6 +94,21 @@ const activeSection = ref('appearance')
|
||||
const mobileMenuOpen = ref(false)
|
||||
const loading = ref(false)
|
||||
const saved = ref(false)
|
||||
const SETTINGS_SECTION_IDS = new Set([
|
||||
'appearance',
|
||||
'account',
|
||||
'access',
|
||||
'video',
|
||||
'hid',
|
||||
'msd',
|
||||
'atx',
|
||||
'environment',
|
||||
'ext-ttyd',
|
||||
'ext-rustdesk',
|
||||
'ext-rtsp',
|
||||
'ext-remote-access',
|
||||
'about',
|
||||
])
|
||||
|
||||
// Navigation structure
|
||||
const navGroups = computed(() => [
|
||||
@@ -135,6 +152,10 @@ function selectSection(id: string) {
|
||||
mobileMenuOpen.value = false
|
||||
}
|
||||
|
||||
function normalizeSettingsSection(value: unknown): string | null {
|
||||
return typeof value === 'string' && SETTINGS_SECTION_IDS.has(value) ? value : null
|
||||
}
|
||||
|
||||
// Theme
|
||||
const theme = ref<'light' | 'dark' | 'system'>('system')
|
||||
|
||||
@@ -1850,6 +1871,13 @@ watch(() => config.value.hid_backend, () => {
|
||||
otgSelfCheckResult.value = null
|
||||
otgSelfCheckError.value = ''
|
||||
})
|
||||
|
||||
watch(() => route.query.tab, (tab) => {
|
||||
const section = normalizeSettingsSection(tab)
|
||||
if (section && activeSection.value !== section) {
|
||||
selectSection(section)
|
||||
}
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user