feat(宏观): 子 tab 整屏左右滑动并与顶部同步
Docker Build and Push / build-image (push) Successful in 4m3s

SwipeTabs 新增 swipeable 开关, Macro 开启, 打卡/监控保持原行为; 使用 swiper 14 实现整屏分页 + 首尾橡皮筋 + 松手吸附 + 方向锁; 滑动/点击/路由双向同步, 子 tab 切换用 replace 不堆历史; 每个 slide 独立竖向滚动, pane 节点缓存避免横滑时重渲染列表; 宏观内部切 tab 不再重复播放整页入场动画
This commit is contained in:
jif2.zhang
2026-11-09 15:12:11 +08:00
parent ec6c9c7677
commit 902c6a0ebe
6 changed files with 151 additions and 29 deletions
+18 -1
View File
@@ -1,8 +1,17 @@
import { useEffect, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import { Outlet, useLocation } from 'react-router-dom'
import BottomNav from '../components/BottomNav.jsx'
import { isOffline, subscribeOffline } from '../api/client.js'
// 宏观页内部子 tab 的列表路径 (整屏横滑只在这些路径之间切换)
// /macro/news · /macro/articles · /macro/finance/:catId
// 详情页 (多一段) 不算, 进出详情仍要播放整页入场动画
function isMacroListPath(p) {
return p === '/macro/news'
|| p === '/macro/articles'
|| /^\/macro\/finance\/[^/]+\/?$/.test(p)
}
// 移动端应用外壳
// - 顶部安全区: 用 --safe-top 撑开 (由 main.jsx 的 calibrateSafeTop 实测校准)
// - 中间 <main>: flex:1 可滚动主区
@@ -18,7 +27,15 @@ export default function MobileShell() {
// pathname 变化时给页面容器加动画 class (仅播放入场动画, 不重挂载子树,
// 避免破坏 MacroPage 等页内子路由的挂载状态与滚动位置)
const [animating, setAnimating] = useState(false)
const prevPathRef = useRef(null)
useEffect(() => {
const prev = prevPathRef.current
prevPathRef.current = location.pathname
// 宏观内部子 tab 切换已由整屏横滑提供动效, 不要再叠一次整页淡入 + 上移
if (prev !== null && isMacroListPath(prev) && isMacroListPath(location.pathname)) {
setAnimating(false)
return
}
setAnimating(true)
const t = setTimeout(() => setAnimating(false), 320)
return () => clearTimeout(t)