fix: 修复首页三栏布局拖拽溢出 — 分隔条最大宽度约束 + 视口缩小自动收窄
问题:切换视口大小或拖拽分隔条时,右侧预览面板甚至中右两栏被挤出视口。 根因:flex 布局左/右列 flexShrink:0,拖拽无上限约束,视口缩小时不自动收窄, 总宽度超出容器后 overflow:hidden 裁剪。 修复(HomeContent.tsx): - 拖拽时计算 maxLeft / maxRight 动态上限,确保中列≥300px、三栏均在视口内 - ResizeObserver 监听容器缩小,优先收右侧面板→不够再收左侧,拖拽中自动跳过 - leftWidthRef / rightWidthRef / draggingRef 避免闭包读到过期值 变更:CHANGELOG.md / TODO.md / WORKLOG.md 同步更新 影响文件:3 个(+60 -3) - src/pages/home/HomeContent.tsx - CHANGELOG.md - TODO.md
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
- 首页三栏分隔条视觉对齐:移除右侧多余 margin,左右完全对称
|
- 首页三栏分隔条视觉对齐:移除右侧多余 margin,左右完全对称
|
||||||
- 轮播图新增关闭按钮:session 级别隐藏,每次启动重新展示
|
- 轮播图新增关闭按钮:session 级别隐藏,每次启动重新展示
|
||||||
- 确立 QT 多窗口模型 → Web 架构映射原则:叠加层走 Modal/Drawer + 事件总线,不走路由
|
- 确立 QT 多窗口模型 → Web 架构映射原则:叠加层走 Modal/Drawer + 事件总线,不走路由
|
||||||
|
- 修复首页三栏布局溢出:拖拽分隔条增加最大宽度约束 + 视口缩小时自动收窄面板,防止右侧面板被挤出视口
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
1
TODO.md
1
TODO.md
@@ -66,6 +66,7 @@
|
|||||||
| localStorage 依赖 | 所有持久化数据依赖 localStorage | 清理浏览器数据会丢失所有配置 |
|
| localStorage 依赖 | 所有持久化数据依赖 localStorage | 清理浏览器数据会丢失所有配置 |
|
||||||
| IPC 监听器 | useUpdater 已改为单例,其他 IPC hook 可能也有类似问题 | 长期运行可能内存泄漏 |
|
| IPC 监听器 | useUpdater 已改为单例,其他 IPC hook 可能也有类似问题 | 长期运行可能内存泄漏 |
|
||||||
| ~~主题切换状态重置~~ | ✅ 已修复 — 根因:设置页 `navigate('/settings')` 导致路由 `/*` 仅匹配 index,HomePage 被卸载重挂载。修复:SettingsPage 改为事件总线驱动的 Modal 居中叠加,不走路由 | ~~用户体验差~~ |
|
| ~~主题切换状态重置~~ | ✅ 已修复 — 根因:设置页 `navigate('/settings')` 导致路由 `/*` 仅匹配 index,HomePage 被卸载重挂载。修复:SettingsPage 改为事件总线驱动的 Modal 居中叠加,不走路由 | ~~用户体验差~~ |
|
||||||
|
| ~~三栏布局拖拽溢出~~ | ✅ 已修复 — 拖拽分隔条增加最大宽度约束(`maxLeft = containerWidth - 36 - 300 - rightWidth`)+ 视口缩小 ResizeObserver 自动收窄 | ~~布局可用性~~ |
|
||||||
| `test.py` | 仓库根目录残留测试文件 | 无实际作用,应清理 |
|
| `test.py` | 仓库根目录残留测试文件 | 无实际作用,应清理 |
|
||||||
| `null` 文件 | 仓库根目录名为 null 的文件 | 构建产物或误操作残留 |
|
| `null` 文件 | 仓库根目录名为 null 的文件 | 构建产物或误操作残留 |
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,12 @@ const LEFT_DEFAULT = 750;
|
|||||||
/** 右列最小/默认宽度 */
|
/** 右列最小/默认宽度 */
|
||||||
const RIGHT_MIN = 300;
|
const RIGHT_MIN = 300;
|
||||||
const RIGHT_DEFAULT = 480;
|
const RIGHT_DEFAULT = 480;
|
||||||
|
/** 中列最小宽度 */
|
||||||
|
const CENTER_MIN = 300;
|
||||||
/** 拖拽分隔条宽度 */
|
/** 拖拽分隔条宽度 */
|
||||||
const DIVIDER_WIDTH = 6;
|
const DIVIDER_WIDTH = 6;
|
||||||
|
/** 布局固定消耗:左右 padding(12*2) + 两个分隔条(6*2) */
|
||||||
|
const LAYOUT_OVERHEAD = 36; // 24 + 12
|
||||||
|
|
||||||
// ---------- 组件 ----------
|
// ---------- 组件 ----------
|
||||||
|
|
||||||
@@ -98,6 +102,14 @@ export function HomeContent() {
|
|||||||
startWidth: number;
|
startWidth: number;
|
||||||
}>({ startX: 0, startWidth: 0 });
|
}>({ startX: 0, startWidth: 0 });
|
||||||
|
|
||||||
|
// 用 ref 追踪最新列宽,避免拖拽 mousemove 闭包中读到过期值
|
||||||
|
const leftWidthRef = useRef(leftWidth);
|
||||||
|
leftWidthRef.current = leftWidth;
|
||||||
|
const rightWidthRef = useRef(rightWidth);
|
||||||
|
rightWidthRef.current = rightWidth;
|
||||||
|
const draggingRef = useRef(dragging);
|
||||||
|
draggingRef.current = dragging;
|
||||||
|
|
||||||
const handleDividerMouseDown = (side: 'left' | 'right') => (e: React.MouseEvent) => {
|
const handleDividerMouseDown = (side: 'left' | 'right') => (e: React.MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// eslint-disable-next-line react-hooks/refs
|
// eslint-disable-next-line react-hooks/refs
|
||||||
@@ -109,14 +121,22 @@ export function HomeContent() {
|
|||||||
const handleMouseMove = (e: MouseEvent) => {
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
if (!dragging) return;
|
if (!dragging) return;
|
||||||
|
|
||||||
|
const containerEl = containerRef.current;
|
||||||
|
if (!containerEl) return;
|
||||||
|
const containerWidth = containerEl.clientWidth;
|
||||||
const ds = dragState.current;
|
const ds = dragState.current;
|
||||||
const delta = e.clientX - ds.startX;
|
const delta = e.clientX - ds.startX;
|
||||||
|
|
||||||
if (dragging === 'left') {
|
if (dragging === 'left') {
|
||||||
const newWidth = Math.max(LEFT_MIN, ds.startWidth + delta);
|
// 左列最大宽度 = 容器宽 - 固定消耗 - 中列最小宽 - 右列当前宽
|
||||||
|
// 确保中列至少保留 CENTER_MIN、右列不被挤出视口
|
||||||
|
const maxLeft = containerWidth - LAYOUT_OVERHEAD - CENTER_MIN - rightWidthRef.current;
|
||||||
|
const newWidth = Math.max(LEFT_MIN, Math.min(maxLeft, ds.startWidth + delta));
|
||||||
setLeftWidth(newWidth);
|
setLeftWidth(newWidth);
|
||||||
} else {
|
} else {
|
||||||
const newWidth = Math.max(RIGHT_MIN, ds.startWidth - delta);
|
// 右列最大宽度同理:不能把中列挤到 CENTER_MIN 以下
|
||||||
|
const maxRight = containerWidth - LAYOUT_OVERHEAD - CENTER_MIN - leftWidthRef.current;
|
||||||
|
const newWidth = Math.max(RIGHT_MIN, Math.min(maxRight, ds.startWidth - delta));
|
||||||
setRightWidth(newWidth);
|
setRightWidth(newWidth);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -134,6 +154,41 @@ export function HomeContent() {
|
|||||||
};
|
};
|
||||||
}, [dragging]);
|
}, [dragging]);
|
||||||
|
|
||||||
|
// ======== 视口缩小自动收窄面板 ========
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = containerRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
|
// 拖拽中由 mousemove handler 处理约束,避免冲突
|
||||||
|
if (draggingRef.current) return;
|
||||||
|
|
||||||
|
const containerWidth = el.clientWidth;
|
||||||
|
const currentLeft = leftWidthRef.current;
|
||||||
|
const currentRight = rightWidthRef.current;
|
||||||
|
|
||||||
|
// 检查当前总宽度是否超出容器
|
||||||
|
const totalNeeded = currentLeft + LAYOUT_OVERHEAD + CENTER_MIN + currentRight;
|
||||||
|
if (totalNeeded <= containerWidth) return;
|
||||||
|
|
||||||
|
// 超出容器 → 优先收缩右侧面板,再收左侧
|
||||||
|
const deficit = totalNeeded - containerWidth;
|
||||||
|
const rightSlack = currentRight - RIGHT_MIN;
|
||||||
|
|
||||||
|
if (rightSlack >= deficit) {
|
||||||
|
setRightWidth(currentRight - deficit);
|
||||||
|
} else {
|
||||||
|
setRightWidth(RIGHT_MIN);
|
||||||
|
const remainingDeficit = deficit - rightSlack;
|
||||||
|
setLeftWidth(Math.max(LEFT_MIN, currentLeft - remainingDeficit));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(el);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
// ======== 分隔条样式工厂 ========
|
// ======== 分隔条样式工厂 ========
|
||||||
|
|
||||||
const dividerStyle = (side: 'left' | 'right'): React.CSSProperties => {
|
const dividerStyle = (side: 'left' | 'right'): React.CSSProperties => {
|
||||||
@@ -232,7 +287,7 @@ export function HomeContent() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 中列 */}
|
{/* 中列 */}
|
||||||
<div style={{ flex: 1, overflow: 'hidden', minWidth: 300 }}>
|
<div style={{ flex: 1, overflow: 'hidden', minWidth: CENTER_MIN }}>
|
||||||
<CenterPanel />
|
<CenterPanel />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user