feat: Web端自动刷新画面机制

- DeviceScreen添加无帧超时检测(8秒无帧触发刷新)
- 自动发送REFRESH_SCREEN请求给服务端
- 15秒冷却期防止频繁刷新
This commit is contained in:
wdvipa
2026-02-15 19:45:58 +08:00
parent bec7cd3979
commit 03e4cb8dab
2 changed files with 55 additions and 12 deletions

View File

@@ -7,73 +7,73 @@
"react": {
"src": "../../react/index.js",
"file": "react.js",
"fileHash": "82b88bac",
"fileHash": "b64083dc",
"needsInterop": true
},
"react-dom": {
"src": "../../react-dom/index.js",
"file": "react-dom.js",
"fileHash": "f147bcf0",
"fileHash": "4cc1d72b",
"needsInterop": true
},
"@reduxjs/toolkit": {
"src": "../../@reduxjs/toolkit/dist/redux-toolkit.modern.mjs",
"file": "@reduxjs_toolkit.js",
"fileHash": "e2e532ed",
"fileHash": "229d524d",
"needsInterop": false
},
"react-redux": {
"src": "../../react-redux/dist/react-redux.mjs",
"file": "react-redux.js",
"fileHash": "49e0dcfd",
"fileHash": "9cd5b390",
"needsInterop": false
},
"antd": {
"src": "../../antd/es/index.js",
"file": "antd.js",
"fileHash": "1c827006",
"fileHash": "804ee91a",
"needsInterop": false
},
"socket.io-client": {
"src": "../../socket.io-client/build/esm/index.js",
"file": "socket__io-client.js",
"fileHash": "f1f126a4",
"fileHash": "dfecfa57",
"needsInterop": false
},
"react/jsx-dev-runtime": {
"src": "../../react/jsx-dev-runtime.js",
"file": "react_jsx-dev-runtime.js",
"fileHash": "92554eaf",
"fileHash": "ee0e2149",
"needsInterop": true
},
"react/jsx-runtime": {
"src": "../../react/jsx-runtime.js",
"file": "react_jsx-runtime.js",
"fileHash": "c9146743",
"fileHash": "a8d2b404",
"needsInterop": true
},
"@ant-design/icons": {
"src": "../../@ant-design/icons/es/index.js",
"file": "@ant-design_icons.js",
"fileHash": "5c57aafc",
"fileHash": "02740ed7",
"needsInterop": false
},
"antd/locale/zh_CN": {
"src": "../../antd/locale/zh_CN.js",
"file": "antd_locale_zh_CN.js",
"fileHash": "731388b8",
"fileHash": "5fab095f",
"needsInterop": true
},
"dayjs": {
"src": "../../dayjs/dayjs.min.js",
"file": "dayjs.js",
"fileHash": "581df681",
"fileHash": "ed91b429",
"needsInterop": true
},
"react-dom/client": {
"src": "../../react-dom/client.js",
"file": "react-dom_client.js",
"fileHash": "f34fbc85",
"fileHash": "1733c935",
"needsInterop": true
}
},

View File

@@ -176,6 +176,49 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
}
}, [webSocket, deviceId])
// Auto-refresh: detect no-frame timeout and request screen refresh from device
const lastFrameTimeRef = useRef<number>(Date.now())
const refreshRequestedRef = useRef<boolean>(false)
const NO_FRAME_TIMEOUT = 8000 // 8 seconds without frames triggers refresh
const REFRESH_COOLDOWN = 15000 // minimum 15 seconds between refresh requests
// Update last frame time when screen_data arrives
useEffect(() => {
if (!webSocket || !deviceId) return
const trackFrameTime = (data: any) => {
if (data.deviceId === deviceId) {
lastFrameTimeRef.current = Date.now()
refreshRequestedRef.current = false
}
}
webSocket.on('screen_data', trackFrameTime)
return () => { webSocket.off('screen_data', trackFrameTime) }
}, [webSocket, deviceId])
// Periodic check for frame timeout
useEffect(() => {
if (!webSocket || !deviceId) return
const checkInterval = window.setInterval(() => {
const elapsed = Date.now() - lastFrameTimeRef.current
if (elapsed < NO_FRAME_TIMEOUT) return
if (refreshRequestedRef.current) return
console.log(`[AutoRefresh] No frames for ${Math.round(elapsed / 1000)}s, requesting screen refresh`)
refreshRequestedRef.current = true
lastFrameTimeRef.current = Date.now() - NO_FRAME_TIMEOUT + REFRESH_COOLDOWN
webSocket.emit('client_event', {
type: 'REFRESH_SCREEN',
data: { deviceId }
})
}, 3000) // check every 3 seconds
return () => { clearInterval(checkInterval) }
}, [webSocket, deviceId])
// 控制权管理的独立useEffect只在必要时申请/释放
useEffect(() => {
if (!webSocket || !deviceId) return