From 5e2a3e29aaa4cd48ccd41a30db35390a929e8dfb Mon Sep 17 00:00:00 2001 From: jillion <439912596@qq.com> Date: Thu, 8 Oct 2026 23:39:20 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=B5=E8=84=91=E4=BC=81=E5=BE=AE=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=88=90=E5=8A=9F=E5=90=8E=E5=85=B3=E4=B8=8D=E6=8E=89?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E9=A1=B5=EF=BC=8C=E5=88=86=E6=9E=90=E7=9C=8B?= =?UTF-8?q?=E6=9D=BF=E7=BA=BF=E8=B7=AF=E7=AD=9B=E9=80=89=E8=A2=AB=E6=8C=A4?= =?UTF-8?q?=E7=AA=84=E3=80=81=E5=BE=80=E4=B8=8B=E4=B9=9F=E6=BB=9A=E4=B8=8D?= =?UTF-8?q?=E5=8A=A8=E3=80=82=E6=8C=89=E9=94=80=E5=94=AE=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E8=A1=A5=E4=B8=8A=E5=85=B3=E7=AA=97=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E8=AE=A9=E7=9C=8B=E6=9D=BF=E6=95=B4=E9=A1=B5=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E6=BB=9A=E5=8A=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- inquiry-agent/agent/channel/h5/wecom_close.py | 59 +++++++++++-------- inquiry-agent/tests/test_air_route_options.py | 5 +- .../tests/test_attachment_upload_h5.py | 6 +- .../src/views/inquiry/dashboard/index.vue | 2 + .../views/inquiry/styles/admin-fidelity.css | 48 ++++++++++++++- .../inquiry/styles/dashboard-prototype.css | 11 +++- 6 files changed, 99 insertions(+), 32 deletions(-) diff --git a/inquiry-agent/agent/channel/h5/wecom_close.py b/inquiry-agent/agent/channel/h5/wecom_close.py index 22ea8ee..492b7e2 100644 --- a/inquiry-agent/agent/channel/h5/wecom_close.py +++ b/inquiry-agent/agent/channel/h5/wecom_close.py @@ -4,7 +4,8 @@ 本文件职责:给附件上传成功、选线路成功页一段共用脚本。 手机端只调一次 WeixinJSBridge.closeWindow,关掉当前 H5 后留在询价对话里。 再调 ww/wx/window.close 会把询价小助手整个退出,回到企业微信首页。 -电脑企微即使注入了 WeixinJSBridge,这一下也关不掉当前页,必须按电脑走 JS-SDK。 +电脑企微 5.x 内置页经常加载不了 JS-SDK,也不会打签名接口。 +这时 ww.closeWindow 不会执行。电脑改用 WeixinJSBridge.invoke,再补 window.close,只关当前页。 失败、过期页不要挂这段。 副作用:只产出 HTML 片段;关窗签名走浏览器短请求,不占智能体请求线程做慢活。 """ @@ -24,8 +25,9 @@ def wecom_auto_close_script() -> str: 成功页脚:加载企微 JS-SDK,register 后关掉当前 H5 窗口。 线程:浏览器端执行。签名接口与复制页共用,ticket 已缓存在 Redis。 - 为何这样写:电脑企微也会带上 WeixinJSBridge,但关不掉当前页。 - 若一发现桥就只走手机关闭,成功页会停在「正在返回企微」。手机仍只关一次。 + 为何这样写:21:27 的上传日志里,成功页已经返回,但没有 jsapi 签名请求。 + 电脑企微没跑起 ww.register,只调 ww.closeWindow 等于没关。 + 电脑改为 invoke + window.close,不依赖签名先成功。手机仍只关一次。 """ settings = get_settings() corp = escape((settings.wecom_corp_id or "").strip(), quote=True) @@ -51,7 +53,11 @@ def wecom_auto_close_script() -> str: var closed = false; function isPhone() {{ - return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent || ""); + var ua = navigator.userAgent || ""; + if (/wxwork/i.test(ua) && /Windows|Macintosh/i.test(ua) && !/Android|iPhone|iPad|iPod/i.test(ua)) {{ + return false; + }} + return /Android|iPhone|iPad|iPod/i.test(ua); }} function closeOnce(fn) {{ @@ -65,13 +71,22 @@ def wecom_auto_close_script() -> str: closeOnce(function () {{ WeixinJSBridge.call("closeWindow"); }}); }} - // 电脑企微即使有 WeixinJSBridge,也关不掉当前页。注册 JS-SDK 后再关一次。 - // 不要调 window.close:手机会把询价小助手整个退出。 + // 电脑企微:call 关不掉,JS-SDK 又经常没装上。invoke 关内置页,window.close 关独立窗口。 function closeDesktop() {{ - closeOnce(function () {{ - if (window.ww && typeof ww.closeWindow === "function") ww.closeWindow(); - else if (window.wx && typeof wx.closeWindow === "function") wx.closeWindow(); - }}); + try {{ + if (typeof WeixinJSBridge !== "undefined" && typeof WeixinJSBridge.invoke === "function") {{ + WeixinJSBridge.invoke("closeWindow", {{}}, function () {{}}); + }} + }} catch (e) {{}} + try {{ + if (window.ww && typeof ww.closeWindow === "function") {{ + var ret = ww.closeWindow(); + if (ret && ret.catch) ret.catch(function () {{}}); + }} else if (window.wx && typeof wx.closeWindow === "function") {{ + wx.closeWindow(); + }} + }} catch (e) {{}} + try {{ window.close(); }} catch (e) {{}} }} function boot() {{ @@ -85,21 +100,15 @@ def wecom_auto_close_script() -> str: }}, false); return; }} - var ready = Promise.resolve(); - if (window.ww && ww.register && corpId) {{ - ready = ww.register({{ - corpId: corpId, - agentId: agentId, - jsApiList: ["closeWindow"], - getConfigSignature: sign("corp"), - getAgentConfigSignature: sign("agent") - }}).catch(function () {{ return null; }}); - }} - var timed = Promise.race([ - ready, - new Promise(function (resolve) {{ setTimeout(resolve, 2500); }}) - ]); - timed.then(function () {{ + setTimeout(closeDesktop, {delay}); + if (!(window.ww && ww.register && corpId)) return; + ww.register({{ + corpId: corpId, + agentId: agentId, + jsApiList: ["closeWindow"], + getConfigSignature: sign("corp"), + getAgentConfigSignature: sign("agent") + }}).then(function () {{ setTimeout(closeDesktop, {delay}); }}).catch(function () {{ setTimeout(closeDesktop, {delay}); diff --git a/inquiry-agent/tests/test_air_route_options.py b/inquiry-agent/tests/test_air_route_options.py index 0c6ffd1..47a2e08 100644 --- a/inquiry-agent/tests/test_air_route_options.py +++ b/inquiry-agent/tests/test_air_route_options.py @@ -407,7 +407,10 @@ class AirRouteOptionsTests(unittest.TestCase): self.assertIn("closeWindow", ok) self.assertIn("wecom-jssdk", ok) self.assertIn("jsapi-signature", ok) - self.assertNotIn("window.close()", ok) + mobile = ok.split("function closeMobile()")[1].split("function closeDesktop()")[0] + desktop = ok.split("function closeDesktop()")[1].split("function boot()")[0] + self.assertNotIn("window.close()", mobile) + self.assertIn("window.close()", desktop) self.assertNotIn("closeWindow", dead) diff --git a/inquiry-agent/tests/test_attachment_upload_h5.py b/inquiry-agent/tests/test_attachment_upload_h5.py index ae457b9..42983ae 100644 --- a/inquiry-agent/tests/test_attachment_upload_h5.py +++ b/inquiry-agent/tests/test_attachment_upload_h5.py @@ -109,7 +109,11 @@ class AttachmentUploadH5Tests(unittest.TestCase): self.assertIn("wecom-jssdk", ok) self.assertIn("jsapi-signature", ok) self.assertIn("function isPhone()", ok) - self.assertNotIn("window.close()", ok) + self.assertIn("WeixinJSBridge.invoke", ok) + mobile = ok.split("function closeMobile()")[1].split("function closeDesktop()")[0] + desktop = ok.split("function closeDesktop()")[1].split("function boot()")[0] + self.assertNotIn("window.close()", mobile) + self.assertIn("window.close()", desktop) self.assertNotIn("closeWindow", page) self.assertNotIn("closeWindow", dead) diff --git a/inquiry-backend/src/views/inquiry/dashboard/index.vue b/inquiry-backend/src/views/inquiry/dashboard/index.vue index 413a409..7161215 100644 --- a/inquiry-backend/src/views/inquiry/dashboard/index.vue +++ b/inquiry-backend/src/views/inquiry/dashboard/index.vue @@ -666,9 +666,11 @@ onUnmounted(() => document.removeEventListener("click", onDocClick)); } .dash-route-menu { + /* 盖过图表区;宽度在 dashboard-prototype.css 里写成 360px,避免按按钮宽度收缩 */ position: absolute; right: 0; top: calc(100% + 8px); + z-index: 120; } .dash-cost-footnote { diff --git a/inquiry-backend/src/views/inquiry/styles/admin-fidelity.css b/inquiry-backend/src/views/inquiry/styles/admin-fidelity.css index 4de51f6..f0f106c 100644 --- a/inquiry-backend/src/views/inquiry/styles/admin-fidelity.css +++ b/inquiry-backend/src/views/inquiry/styles/admin-fidelity.css @@ -43,10 +43,16 @@ body { padding: 16px 16px 40px !important; gap: 12px !important; min-width: 0 !important; - overflow-x: hidden; - flex: 1 1 auto !important; + /* + * 主区被锁成一屏。flex-basis 用 auto 时,格子会按内容把内容区撑高, + * 再被主区裁掉,页面上就没有滚动条,鼠标也滚不动。 + * basis 取 0、高度先归零,再由 flex 撑满顶栏下方,多出来的内容在这里滚。 + */ + flex: 1 1 0% !important; + height: 0 !important; min-height: 0 !important; - overflow: auto !important; + overflow-x: hidden !important; + overflow-y: auto !important; } /* @@ -127,6 +133,7 @@ body { padding: 0 14px !important; gap: 12px !important; min-width: 0 !important; + flex-shrink: 0 !important; /* 允许用户下拉露出;业务 Teleport 区单独 overflow:hidden 防幽灵点击层 */ overflow: visible !important; } @@ -1070,6 +1077,7 @@ h6 { .admin-content:has(.ref-surface--metrics) { padding: 14px 14px 36px !important; gap: 10px !important; + align-content: start !important; } /* ---- 话术 ---- */ @@ -1523,3 +1531,37 @@ textarea.field.field-tall { background: #f8fbff !important; } +/* + * 分析看板比一屏高。整页被 html / 主区锁死时,内容区即使设了 overflow, + * 格子也会跟着内容长高,再被主区裁掉,滚轮没有可滚的对象。 + * 看板单独放开文档滚动;列表页仍锁在一屏里,表格自己滚。 + */ +html:has(.ref-surface--metrics), +body:has(.ref-surface--metrics) { + height: auto !important; + min-height: 100% !important; + overflow-x: hidden !important; + overflow-y: auto !important; +} + +#app:has(.ref-surface--metrics), +.admin-shell:has(.ref-surface--metrics) { + height: auto !important; + min-height: 100vh !important; + overflow: visible !important; +} + +.main-stage:has(.ref-surface--metrics) { + height: auto !important; + max-height: none !important; + min-height: 100vh !important; + overflow: visible !important; +} + +.admin-content:has(.ref-surface--metrics) { + height: auto !important; + max-height: none !important; + flex: none !important; + overflow: visible !important; +} + diff --git a/inquiry-backend/src/views/inquiry/styles/dashboard-prototype.css b/inquiry-backend/src/views/inquiry/styles/dashboard-prototype.css index 27faf24..beaf665 100644 --- a/inquiry-backend/src/views/inquiry/styles/dashboard-prototype.css +++ b/inquiry-backend/src/views/inquiry/styles/dashboard-prototype.css @@ -776,9 +776,13 @@ color: #155eef; } .ref-route-filter__menu { - position: fixed; + /* 必须用固定宽度。百分比会按「线路筛选」按钮的宽度算,面板只剩一字宽,中文被挤成竖排,全选也被裁掉。 */ + position: absolute; + right: 0; + top: calc(100% + 8px); z-index: 120; - width: min(360px, calc(100% - 24px)); + width: 360px; + max-width: calc(100vw - 48px); border: 1px solid #dfe5ee; border-radius: 12px; background: #fff; @@ -797,12 +801,14 @@ color: #0f172a; font-size: 11px; font-weight: 900; + white-space: nowrap; } .ref-route-filter__menu-sub { margin-top: 2px; color: #64748b; font-size: 9px; font-weight: 700; + white-space: nowrap; } .ref-route-filter__actions.tabs-pill { flex-shrink: 0; @@ -861,6 +867,7 @@ } .ref-route-chip__text { line-height: 1.2; + white-space: nowrap; } .month-range-picker { position: relative;