现象: 直装 APK 时头部正常让开状态栏; 经 OTA 升级到 V0.2 前端后, 页面从屏幕最顶部开始, 与电量/信号栏重叠. 根因: 顶部让位一直只依赖 antd-mobile 的 <SafeArea position="top" />, 它只是一个空 div, 高度完全来自 env(safe-area-inset-top). 而 Android WebView (Tauri) 下该变量常返回 0. 旧版每页顶部都有 45px 的 antd-mobile <NavBar>, 视觉上兜住了状态栏, 掩盖了 inset 失效; V0.2 把 NavBar 换成 23px 的 PageHeader (「我」页甚至无头部), 兜底消失后问题暴露. 与 OTA 机制本身无关. 修复: - MobileShell: <SafeArea> 换成 .app-safe-top, 高度由 --safe-top 控制 - main.jsx: 新增 calibrateSafeTop(), 用临时元素测 env(safe-area-inset-top), 并与 screen.height - visualViewport.height 交叉校验, 取较大值写入 --safe-top; 两者都不可用时保底 24px; 监听旋转/视口变化重算 - globals.css: --safe-top 保留 env() 表达式, 新增 .app-safe-top 样式 这样无论 WebView 是否上报 inset, 顶部都能正确让位.
37 lines
1.5 KiB
React
37 lines
1.5 KiB
React
import { useEffect, useState } from 'react'
|
|
import { Outlet, useLocation } from 'react-router-dom'
|
|
import BottomNav from '../components/BottomNav.jsx'
|
|
|
|
// 移动端应用外壳
|
|
// - 顶部安全区: 用 --safe-top 撑开 (由 main.jsx 的 calibrateSafeTop 实测校准)
|
|
// - 中间 <main>: flex:1 可滚动主区
|
|
// - 底部 TabBar: antd-mobile TabBar 自带底部安全区
|
|
//
|
|
// 注意: 这里不再用 antd-mobile 的 <SafeArea position="top" />.
|
|
// 它只是一个空 div, 高度完全依赖 env(safe-area-inset-top); 而 Android WebView
|
|
// (Tauri) 下该变量常为 0, 会导致内容顶到状态栏底下 —— V0.2 把 45px 的 NavBar
|
|
// 换成 23px 的 PageHeader 后这个问题就暴露了. 改用可控高度的 div + JS 校准.
|
|
export default function MobileShell() {
|
|
const location = useLocation()
|
|
// pathname 变化时给页面容器加动画 class (仅播放入场动画, 不重挂载子树,
|
|
// 避免破坏 MacroPage 等页内子路由的挂载状态与滚动位置)
|
|
const [animating, setAnimating] = useState(false)
|
|
useEffect(() => {
|
|
setAnimating(true)
|
|
const t = setTimeout(() => setAnimating(false), 320)
|
|
return () => clearTimeout(t)
|
|
}, [location.pathname])
|
|
|
|
return (
|
|
<div className="app-root">
|
|
<div className="app-safe-top" aria-hidden="true" />
|
|
<main className="app-main">
|
|
<div className={'page-transition' + (animating ? ' active' : '')}>
|
|
<Outlet />
|
|
</div>
|
|
</main>
|
|
<BottomNav />
|
|
</div>
|
|
)
|
|
}
|