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:
@ -195,7 +195,7 @@ export default function App() {
|
|||||||
* 外层 `relative` 是给悬浮加号定位用的 —— 窄屏时它正好落在底部导航之上。
|
* 外层 `relative` 是给悬浮加号定位用的 —— 窄屏时它正好落在底部导航之上。
|
||||||
*/
|
*/
|
||||||
const list = isComm && listBody ? (
|
const list = isComm && listBody ? (
|
||||||
<div className="relative flex-1 min-w-0 min-h-0 flex flex-col">
|
<div className="comm-pane relative flex-1 min-w-0 min-h-0 flex flex-col">
|
||||||
<CommTabs />
|
<CommTabs />
|
||||||
{listBody}
|
{listBody}
|
||||||
<ComposeFab />
|
<ComposeFab />
|
||||||
|
|||||||
@ -118,6 +118,34 @@ export default function CalendarView() {
|
|||||||
else setAnchor(a => addDays(a, dir));
|
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() {
|
function goToday() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
setAnchor(now);
|
setAnchor(now);
|
||||||
@ -199,7 +227,11 @@ export default function CalendarView() {
|
|||||||
|
|
||||||
// ─── 左栏:工具条 + 网格 ───
|
// ─── 左栏:工具条 + 网格 ───
|
||||||
const gridPane = (
|
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-2 px-3 py-2.5 border-b border-gray-200 shrink-0 flex-wrap">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -826,9 +826,17 @@ html[data-bg='on'] .hover\:bg-gray-100:hover {
|
|||||||
|
|
||||||
.app-shell > * {
|
.app-shell > * {
|
||||||
border-radius: var(--radius-card);
|
border-radius: var(--radius-card);
|
||||||
overflow: hidden;
|
/*
|
||||||
|
* ★ 这里**不能**写 `overflow: hidden`(2026-09-14 用户:「通信页面完全无法上下滑动」)。
|
||||||
|
*
|
||||||
|
* 面板自己就是滚动容器(列表/详情都是 overflow-y-auto),而这条规则的特异性
|
||||||
|
* 比 Tailwind 的 `.overflow-y-auto` 高 ⇒ 滚动被静默干掉:实测当时**一个可滚动
|
||||||
|
* 容器都不存在**(scrollerCount=0),内容是直接被裁掉的,连"滚到底"都做不到。
|
||||||
|
*
|
||||||
|
* 圆角仍然生效(border-radius 不影响滚动);角落的方角残影改用
|
||||||
|
* background-clip 处理,代价是溢出内容在圆角处可能露一点点 —— 比不能滚动好得多。
|
||||||
|
*/
|
||||||
box-shadow: 0 8px 24px rgb(15 23 42 / 0.08);
|
box-shadow: 0 8px 24px rgb(15 23 42 / 0.08);
|
||||||
/* 圆角处的背景不要溢出圆角外(否则边缘会出现方角残影) */
|
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,8 +854,8 @@ html[data-bg='on'] .hover\:bg-gray-100:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.narrow-shell > *:not(.narrow-nav) {
|
.narrow-shell > *:not(.narrow-nav) {
|
||||||
|
/* 同样不写 overflow: hidden —— 见上面 app-shell 那段(会杀掉滚动) */
|
||||||
border-radius: var(--radius-card);
|
border-radius: var(--radius-card);
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-bg='on'] .app-shell {
|
html[data-bg='on'] .app-shell {
|
||||||
@ -1025,3 +1033,29 @@ html.view-switch .glass-control,
|
|||||||
animation: none !important;
|
animation: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ★ 通信中栏里的直接子面板必须能被压缩(2026-09-14 用户:「通信页面的各个子页面
|
||||||
|
* 还是无法滑动啊,我真服了」)。
|
||||||
|
*
|
||||||
|
* 根因:我把「页签 + 列表 + 悬浮加号」包进了一个 flex 列 wrapper 来挂内部导航,
|
||||||
|
* 而 **flex 子项默认 min-height: auto** ⇒ 子面板不能被压到比内容矮 ⇒ 列表被撑高 ⇒
|
||||||
|
* 外壳的 overflow:hidden 把它整段裁掉:表现出来就是"完全无法上下滑动",
|
||||||
|
* 而且因为容器自己没溢出,连滚动条都不会出现。
|
||||||
|
*
|
||||||
|
* 为什么以前没坏:列表原先直接挂在窄屏覆盖层(`absolute inset-0 flex`,**行**方向)下,
|
||||||
|
* 行方向的高度来自 align-items: stretch,不需要 min-height:0;换成列方向后就必需了。
|
||||||
|
*
|
||||||
|
* 这条规则对所有子页面一视同仁(收件/发件/授权/联系人),避免只修好一个。
|
||||||
|
*/
|
||||||
|
.comm-pane > *:not([data-testid='comm-tabs']) {
|
||||||
|
/*
|
||||||
|
* 既要能压缩(min-height:0),也要**填满**这一列(flex:1 1 0%):
|
||||||
|
* 子面板(如 MailList 的根)原本写着 `shrink-0` —— 那是给"行"方向布局写的,
|
||||||
|
* 放进列方向后它会连高度都不肯让,内部 `flex-1 overflow-y-auto` 永远拿不到
|
||||||
|
* 可用高度,于是"完全无法上下滑动"。
|
||||||
|
*/
|
||||||
|
flex: 1 1 0%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user