diff --git a/apps/viewer/e2e/dbg-outside.spec.ts b/apps/viewer/e2e/dbg-outside.spec.ts index 5dfe190..02b7e45 100644 --- a/apps/viewer/e2e/dbg-outside.spec.ts +++ b/apps/viewer/e2e/dbg-outside.spec.ts @@ -91,6 +91,15 @@ test('outside release debug', async ({ page }: { page: Page }) => { return `reasons=[${d ? [...d.reasons].join(',') : '?'}] userPaused=${d?.userPaused.value}`; })); + // 手动在 iframe window 派发 pointerup——验证组件监听是否注册生效 + await f!.evaluate(() => { + window.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, clientX: 0, clientY: 0, pointerId: 1 })); + }); + await page.waitForTimeout(1500); + console.log('[2b after manual pointerup]', await f!.evaluate(() => { + const d = (window as unknown as { __embedDebug?: { reasons: Set } }).__embedDebug; + return `reasons=[${d ? [...d.reasons].join(',') : '?'}]`; + })); const s1 = await offset(); await page.waitForTimeout(15000); console.log('[3 15s moved]', Math.abs((await offset()) - s1).toFixed(0), 'px'); diff --git a/apps/viewer/src/pages/embed/EmbedStream.vue b/apps/viewer/src/pages/embed/EmbedStream.vue index a4ad291..6f3d8f8 100644 --- a/apps/viewer/src/pages/embed/EmbedStream.vue +++ b/apps/viewer/src/pages/embed/EmbedStream.vue @@ -277,7 +277,6 @@ function onPointerUp(event: PointerEvent): void { // 不会再来)hover 原因同样会永久残留,这里按坐标判定一并清除;窗内正常 // 释放不清(点击后悬停继续暂停是合理行为)。 const rect = viewportEl.value?.getBoundingClientRect(); - console.log('[dbg-up]', event.type, event.clientX, event.clientY, 'rect', rect ? `${rect.left}-${rect.right}` : 'none', 'hoverBefore', pauseReasons.has('hover')); if ( rect !== undefined && (event.clientX < rect.left || @@ -397,7 +396,6 @@ function onFocusOut(): void { // window 级兜底(滚动卡死修复 2026-09-23):pointerup 落在窗外/别的元素、 // 或整个 iframe 失焦(弹框抢焦点)时也要清 pointer 原因,避免暂停态永久卡住。 function onWindowPointerEnd(event: PointerEvent): void { - console.log('[dbg-wpe]', event.type, 'pointerDownY =', pointerDownY); if (pointerDownY !== null) onPointerUp(event); } function onWindowBlur(): void { @@ -410,8 +408,11 @@ function onWindowBlur(): void { onMounted(() => { document.addEventListener('visibilitychange', onVisibilityChange); - window.addEventListener('pointerup', onWindowPointerEnd); - window.addEventListener('pointercancel', onWindowPointerEnd); + // 捕获阶段注册(滚动卡死修复 2026-09-23):pointercancel 的派发路径在拖出 + // 窗外场景不可靠(target 漂移/分发中断,冒泡监听实测收不到);window 捕获 + // 是最先必达的阶段,兜底清理不依赖事件到达路径。 + window.addEventListener('pointerup', onWindowPointerEnd, true); + window.addEventListener('pointercancel', onWindowPointerEnd, true); window.addEventListener('blur', onWindowBlur); onVisibilityChange(); if (typeof IntersectionObserver !== 'undefined' && viewportEl.value !== null) { @@ -430,8 +431,8 @@ onBeforeUnmount(() => { if (pointerWatchdog !== null) clearTimeout(pointerWatchdog); layerEl.value?.clear(); document.removeEventListener('visibilitychange', onVisibilityChange); - window.removeEventListener('pointerup', onWindowPointerEnd); - window.removeEventListener('pointercancel', onWindowPointerEnd); + window.removeEventListener('pointerup', onWindowPointerEnd, true); + window.removeEventListener('pointercancel', onWindowPointerEnd, true); window.removeEventListener('blur', onWindowBlur); visibilityObserver?.disconnect(); if (resumeTimer !== null) clearTimeout(resumeTimer); diff --git a/infra/deploy/nginx.conf.template b/infra/deploy/nginx.conf.template index baff8ed..27cf29e 100644 --- a/infra/deploy/nginx.conf.template +++ b/infra/deploy/nginx.conf.template @@ -53,19 +53,6 @@ server { proxy_send_timeout 300s; } -; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - # WS(/v1/realtime)升级;普通 HTTP 请求不受影响。 - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_read_timeout 75s; - proxy_send_timeout 75s; - } - # admin(hash 路由:静态直达即工作;try_files 兜底异常直链)。 location ^~ /admin/ { alias /usr/share/nginx/html/admin/;