From 6f3d14d31465fa17f44db1a2e9fa82384416adc6 Mon Sep 17 00:00:00 2001 From: brother7 <7brother7@gmail.com> Date: Fri, 31 Jul 2026 17:48:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8D=95=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=85=AC=E7=A4=BA=E5=B1=8F=E5=B8=83=E5=B1=80=E9=87=8D=E5=8F=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:/display/YYHHC 在 1280x720 下主卡片、右侧预计和最近叫号区域错位重叠。 实现:重置主内容网格行,统一底栏高度与预留空间,优化侧栏字号和窄屏页头;完成三种视口及前端全量验证。 --- findings.md | 10 ++++++++++ progress.md | 10 ++++++++++ task_plan.md | 26 +++++++++++++++++++++++++- web/src/styles.css | 38 ++++++++++++++++++++++++++------------ 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/findings.md b/findings.md index 99d132c..5256a36 100644 --- a/findings.md +++ b/findings.md @@ -609,3 +609,13 @@ - 项目编码规则为 2–24 位大写字母、数字、下划线或连字符;可复用既有校验规则,并对 URL 输入统一转大写。 - 已有有效 Token 最短 40 位,不会与最长 24 位的项目编码冲突,因此可在同一个快照接口中安全区分两类标识。 - 后台新建项目不生成公示 Token,但项目编码必有值;改用项目编码后,新建项目也能直接打开单项目只读大屏。 + +# 2026-07-31 单项目公示屏排版问题 + +- 线上 `/display/YYHHC` 在 1280×720 下稳定复现:当前叫号卡片范围为 `top=140, bottom≈692`,最近叫号栏为 `top=532, bottom=702`,实际纵向重叠约 160px;页面总高度约 1203px。 +- 生效样式中,外层网格被重定义为三行,但旧规则仍把 `.display-current` 放在第 3 行、`.display-secondary` 放在第 4 行,导致右侧“后续号段预计”落到主卡片下方。 +- `.display-secondary .display-history` 又使用 `position: fixed` 固定在视口底部;当主卡片被旧 `min-height: 520px` 撑高时,固定底栏直接覆盖主卡片。 +- 正确结构应重置当前卡片和右侧摘要到同一第 2 行,并在矮屏断点下压缩固定底栏高度、号码区最小高度和上下留白。 +- 修复后 1280×720:当前卡片与右侧摘要均为 `top=124, bottom=542`,最近叫号栏为 `top=558, bottom=702`,重叠量为 0,页面宽高与视口一致。 +- 修复后 1920×1080:主内容与底栏重叠量为 0,页面无横纵向溢出;右侧号码区间和等待时间的测量间距为 18px,不再互相覆盖。 +- 390×844 下页头切换为两行居中布局,日期和时间均落在视口内;内容使用普通纵向滚动,无横向溢出。 diff --git a/progress.md b/progress.md index aca7316..056edb1 100644 --- a/progress.md +++ b/progress.md @@ -698,3 +698,13 @@ - 已实现项目编码规范化与查询分支:合法短编码按 `projects.code` 查询,其他标识仍按原 Token 长度与哈希规则处理。 - 已补充单元测试、前端 API 路径测试及 PostgreSQL 项目编码路由集成回归。 - 全量验证通过:`go test ./... -count=1`、`go vet ./...`、`go build ./...`;前端 18 个测试文件共 61 项通过,TypeScript 检查与 Vite 生产构建成功;`git diff --check` 无格式错误。 + +# Session: 2026-07-31(单项目公示屏排版修复) + +- 已通过线上截图确认 `/display/YYHHC` 的最近叫号栏覆盖当前叫号卡片,且右侧预计区域落到首屏以下。 +- 已用浏览器布局坐标建立 1280×720 复现信号,并确认页面纵向溢出。 +- 已定位根因:后续视觉覆盖层没有重置早期 CSS 的 `grid-row`,固定底栏与 520px 主卡片最小高度进一步放大冲突。 +- 已修正主内容网格行,使用同一响应式变量协调底栏高度与预留空间,并让主号码区随视口高度收缩。 +- 已提高右侧摘要标题对比度、将统计信息改为稳定双行,并限制号码区间字号以适应窄侧栏。 +- 已使用本地页面代理真实 `YYHHC` 数据完成 1280×720、1920×1080 与 390×844 视觉验收;临时配置和本地服务均已清理。 +- 前端 18 个测试文件共 61 项全部通过,TypeScript 类型检查及 Vite 生产构建成功。 diff --git a/task_plan.md b/task_plan.md index 140cca5..aa36b51 100644 --- a/task_plan.md +++ b/task_plan.md @@ -4,7 +4,7 @@ 在已确认的产品、技术与设计基线上,交付可运行的景区排队叫号系统纵向切片,并以自动化测试验证多项目隔离、幂等叫号与隐私边界。 ## Current Phase -Phase 46(单项目大屏支持项目编码路由) +Phase 47(单项目公示屏排版修复) ## Phases @@ -593,3 +593,27 @@ Phase 46(单项目大屏支持项目编码路由) |---|---:|---| | 新增回归测试在旧实现下找不到 `normalizeDisplayProjectCode` | 1 | 作为项目编码解析缺口的失败基线,随后实现解析函数 | | 解析函数对非法编码返回了规范化文本和 `false`,与失败契约不一致 | 1 | 非法输入统一返回空字符串和 `false`,避免调用方误用无效编码 | + +### Phase 47: 单项目公示屏排版修复(2026-07-31) +- [x] 在线上 1280×720 视口复现卡片重叠与纵向溢出 +- [x] 定位旧网格行号与固定底栏共同导致的布局冲突 +- [x] 修正主内容网格行并压缩矮屏垂直节奏 +- [x] 完成桌面、矮屏与窄屏视觉回归 +- [x] 通过前端测试、类型检查与生产构建 +- **Status:** complete + +#### Confirmed Decisions +| Decision | Result | Why it matters | +|---|---|---| +| 修复范围 | 只调整单项目公示屏生效中的 CSS,不改叫号数据和播报逻辑 | 避免把视觉问题扩大为业务改动 | +| 桌面结构 | 页头占第一行,当前叫号与后续预计并排占第二行,最近叫号固定在底部 | 恢复设计原意,并避免右栏落到主卡片下方 | +| 矮屏策略 | 使用高度断点压缩间距、卡片标题和号码区最小高度 | 兼容 1280×720 等现场常见分辨率 | + +#### Errors Encountered +| Error | Attempt | Resolution | +|---|---:|---| +| 应用内浏览器首次导航在页面资源加载期间超时 | 1 | 读取超时后已完成页面的截图和布局坐标,成功建立重叠复现 | +| 切换 1920×1080 视口时浏览器会话超时并重置 | 1 | 不重复同一路径;改用源码网格分析与后续本地构建进行多尺寸验证 | +| `planning-with-files-zh` 会话恢复脚本缺少本机 Python | 1 | 记录后跳过脚本,直接读取现有三份规划文件并核对 Git 状态 | +| 将启动服务与轮询拼在同一 PowerShell 命令时被策略拒绝 | 1 | 拆分为显式 `Start-Process -WindowStyle Hidden` 和独立就绪检查 | +| 通过运行时 `pnpm` 包装器启动时触发依赖安装,服务未按时就绪 | 1 | 终止本轮创建的进程,改用项目本地 `node_modules/.bin/vite.cmd` 直接启动 | diff --git a/web/src/styles.css b/web/src/styles.css index 5e34bfc..8ac8eb3 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -3850,17 +3850,24 @@ tbody tr:hover { .display-clock { flex-direction: row; gap: 24px; align-items: center; } .display-clock strong { color: #063b20; font-size: clamp(1.7rem, 2.8vw, 3rem); } .display-status { border: 0; background: transparent; color: #086333; font-size: 1.2rem; } -.display-page:not(.display-page--state) { display: grid; grid-template-columns: minmax(0, 2.35fr) minmax(280px, .95fr); grid-template-rows: auto 1fr auto; gap: 16px 20px; } +.display-page:not(.display-page--state) { + --display-history-height: clamp(136px, 20vh, 170px); + display: grid; + grid-template-columns: minmax(0, 2.35fr) minmax(280px, .95fr); + grid-template-rows: auto minmax(0, 1fr); + gap: 16px 20px; + padding-bottom: calc(var(--display-history-height) + 34px); +} .display-page .display-header { grid-column: 1 / -1; } -.display-current { grid-column: 1; min-height: 520px; border: 2px solid #075d31; border-radius: 16px; padding: 0 54px 28px; background: rgba(255,255,255,.9); box-shadow: none; } -.display-current > p { margin: 0 -54px 32px; border-radius: 13px 13px 0 0; padding: 24px 50px; background: linear-gradient(105deg,#064c26,#076735); color: #fff; font-size: clamp(1.8rem,3vw,3.5rem); font-weight: 900; } -.display-numbers { display: flex; flex-wrap: wrap; justify-content: center; align-content: center; gap: 24px 54px; min-height: 300px; } +.display-current { grid-column: 1; grid-row: 2; min-height: 0; border: 2px solid #075d31; border-radius: 16px; padding: 0 54px clamp(16px, 2.6vh, 28px); background: rgba(255,255,255,.9); box-shadow: none; } +.display-current > p { margin: 0 -54px clamp(12px, 2vh, 32px); border-radius: 13px 13px 0 0; padding: clamp(14px, 2.2vh, 24px) 50px; background: linear-gradient(105deg,#064c26,#076735); color: #fff; font-size: clamp(1.8rem,3vw,3.5rem); font-weight: 900; } +.display-numbers { display: flex; flex-wrap: wrap; justify-content: center; align-content: center; gap: 24px 54px; min-height: clamp(180px, 30vh, 300px); } .display-numbers strong { color: #064624; font-size: clamp(4.4rem,7.3vw,8.5rem); line-height: .9; letter-spacing: .02em; text-shadow: none; } -.display-current__meta { justify-content: center; gap: 28px; border-top: 1px solid #b9cec0; padding-top: 24px; color: #173423; font-size: 1.35rem; } -.display-secondary { grid-column: 2; display: block; } +.display-current__meta { justify-content: center; gap: 28px; margin-top: clamp(12px, 2vh, 24px); border-top: 1px solid #b9cec0; padding-top: clamp(12px, 2vh, 24px); color: #173423; font-size: clamp(1rem, 1.5vw, 1.35rem); } +.display-secondary { grid-column: 2; grid-row: 2; min-height: 0; display: block; } .display-queue-summary { min-height: 100%; border: 1px solid #c6d2ca; border-radius: 16px; padding: 28px 34px; background: rgba(255,255,255,.86); } .display-forecast__heading { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; border-bottom: 1px solid #d3ddd6; padding-bottom: 24px; } -.display-forecast__heading h2 { margin: 0; font-size: 1.7rem; } +.display-forecast__heading h2 { margin: 0; color: #0c2b1a; font-size: 1.7rem; } .display-forecast__heading span { color: #607068; font-size: 1.1rem; font-weight: 800; } .display-forecast { display: grid; margin: 0; padding: 0; list-style: none; } .display-forecast li { display: grid; grid-template-columns: minmax(0,1.35fr) auto; align-items: baseline; gap: 18px; border-bottom: 1px solid #d3ddd6; padding: clamp(20px,3vh,34px) 0; animation: display-forecast-in .45s ease both; } @@ -3874,8 +3881,7 @@ tbody tr:hover { .display-queue-summary footer { display: flex; justify-content: space-between; align-items: baseline; gap: 18px; border-top: 0; margin-top: 20px; padding-top: 0; color: #53635a; } .display-queue-summary footer strong { color: #07512b; font-size: clamp(1.1rem,1.5vw,1.45rem); } .display-history { grid-column: 1 / -1; } -.display-secondary .display-history { position: fixed; right: 24px; bottom: 18px; left: 24px; height: 170px; border: 1px solid #c9d4cd; border-radius: 14px; padding: 18px 24px; background: rgba(255,255,255,.94); } -.display-page:not(.display-page--state) { padding-bottom: 204px; } +.display-secondary .display-history { position: fixed; right: 24px; bottom: 18px; left: 24px; height: var(--display-history-height); border: 1px solid #c9d4cd; border-radius: 14px; padding: 18px 24px; background: rgba(255,255,255,.94); } .display-history h2 { margin-bottom: 12px; color: #0c2b1a; font-size: 1.35rem; } .display-history ol { display: grid; grid-template-columns: repeat(5,1fr); } .display-history li { display: grid; justify-items: center; gap: 6px; border-right: 1px solid #d6dfd9; } @@ -3886,6 +3892,13 @@ tbody tr:hover { @media (max-width: 900px) { .display-page:not(.display-page--state) { display: block; padding: 14px; } + .display-header { display: grid; min-height: 0; gap: 12px; padding: 0 0 12px; } + .display-brand { justify-content: center; gap: 14px; } + .display-brand img { width: 112px; height: 48px; } + .display-brand h1 { padding-left: 14px; font-size: 1.35rem; } + .display-clock { width: 100%; flex-wrap: wrap; justify-content: center; gap: 8px 14px; } + .display-clock strong { font-size: 1.8rem; } + .display-clock time { padding-left: 14px; font-size: .9rem; } .display-current { min-height: auto; padding: 0 18px 22px; } .display-current > p { margin: 0 -18px 22px; padding: 18px; } .display-secondary { display: grid; gap: 14px; margin-top: 14px; } @@ -3984,9 +3997,10 @@ body { min-width: 0; } /* Public display forecast overrides the legacy queue-summary metrics. */ .display-queue-summary .display-forecast { display: grid; margin: 0; padding: 0; list-style: none; } .display-queue-summary .display-forecast li { display: grid; grid-template-columns: minmax(0, 1.35fr) auto; align-items: baseline; gap: 18px; border-bottom: 1px solid #d3ddd6; padding: clamp(20px, 3vh, 34px) 0; } -.display-queue-summary .display-forecast li strong { color: #063f22; font-size: clamp(1.7rem, 3vw, 3.4rem); letter-spacing: .02em; } -.display-queue-summary .display-forecast li span { color: #075d31; font-size: clamp(1.35rem, 2vw, 2.2rem); font-weight: 900; white-space: nowrap; } -.display-queue-summary footer { display: flex; justify-content: space-between; align-items: baseline; gap: 18px; border-top: 0; margin-top: 20px; padding-top: 0; } +.display-queue-summary .display-forecast li strong { color: #063f22; font-size: clamp(1.55rem, 2.2vw, 2.65rem); letter-spacing: .02em; } +.display-queue-summary .display-forecast li span { color: #075d31; font-size: clamp(1.2rem, 1.5vw, 1.65rem); font-weight: 900; white-space: nowrap; } +.display-queue-summary footer { display: grid; gap: 6px; align-items: start; border-top: 0; margin-top: 20px; padding-top: 0; } +.display-queue-summary footer span { white-space: nowrap; } @media (max-width: 900px) { .display-queue-summary .display-forecast li { grid-template-columns: 1fr auto; }