diff --git a/src/components/MobileActionSheet.tsx b/src/components/MobileActionSheet.tsx index 6127b1d..d1903ce 100644 --- a/src/components/MobileActionSheet.tsx +++ b/src/components/MobileActionSheet.tsx @@ -51,6 +51,13 @@ const MobileActionSheet: React.FC = ({ const [isTitleOverflowing, setIsTitleOverflowing] = useState(false); const backdropPressStarted = useRef(false); const titleRef = useRef(null); + const actionsListRef = useRef(null); + + // 操作区可滚动:有可用播放源最多显示 4 项,否则最多 6 项 + const hasPlaySources = Boolean(isAggregate && sources && sources.length > 0); + // 单项 py-4(2rem) + 内容 h-6(1.5rem) = 3.5rem + const actionsMaxVisible = hasPlaySources ? 4 : 6; + const actionsMaxHeight = `calc(3.5rem * ${actionsMaxVisible})`; // 确保组件在客户端挂载后才渲染 Portal useEffect(() => { @@ -166,6 +173,31 @@ const MobileActionSheet: React.FC = ({ }; }, [isVisible, title]); + // 操作列表:用非 passive 的 wheel 监听,保证鼠标滚轮可滚动且不穿透到背景 + useEffect(() => { + if (!isVisible) return; + const el = actionsListRef.current; + if (!el) return; + + const handleWheel = (e: WheelEvent) => { + e.stopPropagation(); + const { scrollTop, scrollHeight, clientHeight } = el; + if (scrollHeight <= clientHeight) { + e.preventDefault(); + return; + } + const maxScrollTop = scrollHeight - clientHeight; + const next = Math.min(maxScrollTop, Math.max(0, scrollTop + e.deltaY)); + if (next !== scrollTop) { + el.scrollTop = next; + } + e.preventDefault(); + }; + + el.addEventListener('wheel', handleWheel, { passive: false }); + return () => el.removeEventListener('wheel', handleWheel); + }, [isVisible, actionsMaxVisible]); + // ESC键关闭 useEffect(() => { const handleEsc = (e: KeyboardEvent) => { @@ -337,62 +369,75 @@ const MobileActionSheet: React.FC = ({ - {/* 操作列表 */} + {/* 操作列表:有源最多 4 项 / 无源最多 6 项,超出可滑动(支持触控与鼠标滚轮) */}
- {actions.map((action, index) => ( -
- + + {/* 分割线 - 最后一项不显示 */} + {index < actions.length - 1 && ( +
)} - - - - - {/* 分割线 - 最后一项不显示 */} - {index < actions.length - 1 && ( -
- )} -
- ))} +
+ ))} + {/* 播放源信息展示区域 */}