From c77c8086067113158d42c43c7fa71236a5cdb246 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Thu, 9 Jul 2026 10:59:49 +0800 Subject: [PATCH] fix: support titlebar double click maximize --- docs/06-tasks.md | 2 +- docs/current-state.md | 12 ++++++-- frontend/src/layouts/MainLayout.vue | 43 ++++++++++++++++++++++++++--- progress.md | 19 +++++++++++++ 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/docs/06-tasks.md b/docs/06-tasks.md index 39b5ba5..b53ed08 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -52,7 +52,7 @@ | T-033 | 完成一次模板 smoke 清单 | T-023 | 登录、设置、业务样例、日志、统计、组件示例、托盘、热键都按清单验证并记录结果 | DONE | | T-034 | 添加 Windows Wails 快捷脚本 | T-001 | `dev.bat` 一行切到项目根目录、补 Go bin PATH、自动选择空闲端口并运行 `wails3 dev`;`build.bat` 一行切到项目根目录、补 Go bin PATH 并运行 `wails3 build`;README、技术栈、当前状态和执行流水已同步 | DONE | | T-035 | 修复主窗口最大化黑边与布局填充 | T-011 | 点击右上角最大化后窗口占满 Windows 可用区域;主窗口背景、根容器和主布局铺满;侧边导航贴齐左边;内容区填满剩余区域;不出现左右黑边;恢复窗口后布局正常 | DONE | -| T-036 | 支持 Windows 主窗口标题栏双击最大化 | T-035 | Windows 无边框主窗口顶部拖拽区域双击可切换最大化/还原;右上角按钮区、登录入口和打开第二窗口入口双击不误触发;第二窗口不受影响;窗口状态持久化继续生效 | TODO | +| T-036 | 支持 Windows 主窗口标题栏双击最大化 | T-035 | Windows 无边框主窗口顶部拖拽区域双击可切换最大化/还原;右上角按钮区、登录入口和打开第二窗口入口双击不误触发;第二窗口不受影响;窗口状态持久化继续生效 | DONE | ## EC 分支 · 电商场景 diff --git a/docs/current-state.md b/docs/current-state.md index 777f811..762ecc2 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -33,7 +33,7 @@ - 当前主应用模块路由:`/#/login`、`/#/dashboard`、`/#/products`、`/#/image-optimizer`、`/#/logs`、`/#/statistics`、`/#/settings`、`/#/components`。 - 当前兼容重定向路由:`/#/business/image-optimizer`、`/#/logs/audit`、`/#/logs/app`。 - 当前第二窗口路由:`/#/second` -- 已实现主窗口策略:默认不最大化,初始 `1280x800`,最小 `1024x720`,居中,可调整大小;启动后可按 `window.*` 配置恢复尺寸和最大化状态。 +- 已实现主窗口策略:默认不最大化,初始 `1280x800`,最小 `1024x720`,居中,可调整大小;启动后可按 `window.*` 配置恢复尺寸和最大化状态;Windows 无边框主窗口顶部拖拽区域双击可切换最大化/还原。 - 已实现页面布局策略:根容器 `width: 100%`、`height: 100vh`,内部 flex/grid 填满窗口;主窗口最大化后背景、根容器和主布局铺满可用区域,不出现左右黑边。 - 已实现内容区策略:阅读型内容保留最大宽度;列表、仪表盘、设置页自适应拉伸。 - 已保留第二窗口策略:保持小窗,不参与主窗口最大化和尺寸恢复逻辑。 @@ -89,7 +89,6 @@ wails3 build - OCR 和热键依赖平台实现,跨平台修改需要分别验证。 - 当前前端以构建检查为主,暂未引入专门的前端单元测试。 - bindings 生成依赖本机安装的 `wails3`。 -- 待修复:Windows 无边框主窗口顶部拖拽区域双击不能像原生标题栏一样切换最大化/还原。 - Windows 默认打包路径依赖 NSIS `makensis`;当前本机 PATH 未检测到 `makensis`。 - 当前项目未配置 `wails.json`,MSIX 暂不作为可用打包路径。 - 托盘菜单点击行为仍建议在真实桌面环境人工复核,本轮已确认初始化代码路径和应用启动 smoke。 @@ -258,9 +257,16 @@ wails3 build - `go test ./...`、`cd frontend && npm run build`、`go build -o bin\cmbone.exe .` 和 `cmd /c build.bat` 通过。 - 启动 `bin\cmbone.exe` 后程序化最大化窗口,左右边缘采样 `11264` 个像素,纯黑像素数为 `0`。 +同日完成 Windows 主窗口标题栏双击最大化: + +- 主窗口标题栏使用短时间、小距离的双击检测,不依赖 WebView 是否合成 `dblclick` 事件。 +- 右上角控件区继续保持 `no-drag`,双击不误触发标题栏最大化逻辑。 +- `go test ./...`、`cd frontend && npm run build`、`go build -o bin\cmbone.exe .` 和 `cmd /c build.bat` 通过。 +- 启动 `bin\cmbone.exe` 后自动化双击顶部中间区域,窗口从普通状态进入最大化;双击右侧 `no-drag` 控件区后窗口未最大化。 + ## 下一步 -先领取 [`06-tasks.md`](06-tasks.md) 的 `T-036`:支持 Windows 主窗口标题栏双击最大化;完成后继续 `EC-002` 建立商品后端模型。 +继续领取 [`06-tasks.md`](06-tasks.md) 的 `EC-002`:建立商品后端模型。 ## 当前 blocker diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue index 0fb1c17..80e5579 100644 --- a/frontend/src/layouts/MainLayout.vue +++ b/frontend/src/layouts/MainLayout.vue @@ -33,6 +33,11 @@ const isMacOS = ref(false) const isMaximized = ref(false) const sessionUsername = ref('未登录') const sessionProvider = ref('Auth Provider') +const titlebarDoubleClickMaxDelayMs = 500 +const titlebarDoubleClickMaxDistancePx = 8 +let lastTitlebarClickAt = 0 +let lastTitlebarClickX = 0 +let lastTitlebarClickY = 0 const navItems: NavItem[] = [ { path: '/dashboard', label: '工作台', detail: '指标与近期任务', icon: DashboardOutlined }, @@ -55,6 +60,35 @@ async function toggleMaximize() { isMaximized.value = await Window.IsMaximised() } +function isNoDragTarget(target: EventTarget | null) { + return target instanceof Element && target.closest('.no-drag') +} + +async function handleTitlebarMouseDown(event: MouseEvent) { + if (!isWindows.value || event.button !== 0 || isNoDragTarget(event.target)) { + return + } + + const now = window.performance.now() + const isDoubleClick = + now - lastTitlebarClickAt <= titlebarDoubleClickMaxDelayMs && + Math.abs(event.clientX - lastTitlebarClickX) <= titlebarDoubleClickMaxDistancePx && + Math.abs(event.clientY - lastTitlebarClickY) <= titlebarDoubleClickMaxDistancePx + + lastTitlebarClickAt = now + lastTitlebarClickX = event.clientX + lastTitlebarClickY = event.clientY + + if (!isDoubleClick) { + return + } + + lastTitlebarClickAt = 0 + + event.preventDefault() + await toggleMaximize() +} + onMounted(async () => { await OS_READY const osName = getOS() @@ -123,8 +157,9 @@ onMounted(async () => {

@@ -132,7 +167,7 @@ onMounted(async () => {

-
+
@@ -163,12 +198,12 @@ onMounted(async () => {