fix(webui): 修「通信页面完全无法上下滑动」+ 日历左右滑动手势
## 滑动(用户:「通信页面的各个子页面还是无法滑动啊,我真服了」)
根因不是我第一次以为的 `overflow: hidden`,而是**我把列表包进了一个 flex 列**:
- `MailList` 的根是 `w-full lg:w-[320px] shrink-0 ... flex flex-col` —— 这个 `shrink-0`
是给**行**方向布局写的(控制宽度),放进列方向后它作用在**高度**上:列表连高度
都不肯让 ⇒ 内部的 `flex-1 overflow-y-auto` 永远拿不到可用高度 ⇒ 滚不动。
- 表现极具误导性:容器**自己也没有溢出**,所以连滚动条都不出现,看起来像"页面被裁掉了"。
- 为什么以前没坏:列表原先直接挂在窄屏覆盖层(`absolute inset-0 flex`,**行**方向)下,
行方向的高度来自 `align-items: stretch`,不需要 `min-height:0`。
修法:`.comm-pane > *:not([data-testid='comm-tabs']) { flex: 1 1 0%; min-height: 0 }`
—— 让子面板既能压缩、也填满这一列。对收件/发件/授权/联系人一视同仁,避免只修好一个。
**实测**(390×844,11 行邮件):修复前 `scrollerCount: 0`(一个可滚动容器都没有);
修复后滚动容器 = `flex-1 overflow-y-auto p-2.5`、`overflow-y: auto`、可滚范围 236px、
`scrollTop` 实际移动 200px。导航仍完整可见(bottom=834 ≤ 844)。
## 我自己的两个方法错误(都写在这里,避免下次再犯)
1. **判据没断言前置条件**:前三次探针都报"没有滚动容器",其实是 gui-lab 收件箱太短
(12 封全落进同一个会话 ⇒ 只显示 1 组)⇒ 内容根本没溢出 ⇒ 我量的对象不存在。
最后用 10 个**独立会话**把列表撑高才复现出来。用户报的缺陷是真的,是我的探针没到位。
2. 第一条修复(去掉 `overflow:hidden`)方向不对,但我当时**差点把它当成修好了** ——
因为探针依旧是 0 滚动容器,只是我没深究。结论必须是三态,不能把"量不到"当"没问题"。
## 日历左右滑动(用户:「日历页面还不支持左右滑动手势」)
在日历主体上挂 touchstart/touchend,**复用 `shift()`**(与"上一月/下一月"按钮同一套翻页
逻辑);阈值:水平位移 ≥40px、且 ≥1.5×垂直位移、且 <600ms —— 否则会把纵向滚动误判成翻页。
**这条我还没验成功**:探针取日历标题的方式不对(返回 null ⇒ 判不了),
不能说它好了。修好探针再补验。
This commit is contained in:
@ -118,6 +118,34 @@ export default function CalendarView() {
|
||||
else setAnchor(a => addDays(a, dir));
|
||||
}
|
||||
|
||||
/*
|
||||
* 左右滑动手势(2026-09-14 用户:「日历页面还不支持左右滑动手势」)。
|
||||
*
|
||||
* 复用 shift() —— 手势与「上一月/下一月」按钮必须是同一套翻页逻辑,
|
||||
* 各写一遍的话阈值、边界、"周/日/月"三种刻度的行为迟早分叉。
|
||||
*
|
||||
* 判据:水平位移 ≥ 40px 且明显大于垂直位移(否则会把纵向滚动列表误判成翻页,
|
||||
* 那是移动端最容易犯的手势错误);同时要求时间 < 600ms,避免"慢慢拖"也翻页。
|
||||
*/
|
||||
const touch = useRef<{ x: number; y: number; t: number } | null>(null);
|
||||
const onTouchStart = (e: React.TouchEvent) => {
|
||||
const t = e.touches[0];
|
||||
if (!t) return;
|
||||
touch.current = { x: t.clientX, y: t.clientY, t: Date.now() };
|
||||
};
|
||||
const onTouchEnd = (e: React.TouchEvent) => {
|
||||
const start = touch.current;
|
||||
touch.current = null;
|
||||
const t = e.changedTouches[0];
|
||||
if (!start || !t) return;
|
||||
const dx = t.clientX - start.x;
|
||||
const dy = t.clientY - start.y;
|
||||
const fast = Date.now() - start.t < 600;
|
||||
if (Math.abs(dx) < 40 || Math.abs(dx) < Math.abs(dy) * 1.5 || !fast) return;
|
||||
// 左滑 = 看下一段(与翻页按钮的"下一月"同向),右滑 = 上一段
|
||||
shift(dx < 0 ? 1 : -1);
|
||||
};
|
||||
|
||||
function goToday() {
|
||||
const now = new Date();
|
||||
setAnchor(now);
|
||||
@ -199,7 +227,11 @@ export default function CalendarView() {
|
||||
|
||||
// ─── 左栏:工具条 + 网格 ───
|
||||
const gridPane = (
|
||||
<div className="flex-1 min-w-0 flex flex-col bg-white min-h-0">
|
||||
<div
|
||||
className="flex-1 min-w-0 flex flex-col bg-white min-h-0"
|
||||
onTouchStart={onTouchStart}
|
||||
onTouchEnd={onTouchEnd}
|
||||
>
|
||||
<div className="flex items-center gap-2 px-3 py-2.5 border-b border-gray-200 shrink-0 flex-wrap">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user