feat: 版本时间戳统一 UTC+8; 适配全面屏安全区; 页面切换加过渡动画
- vite buildId 统一按 Asia/Shanghai 生成, CI(UTC) 与本地时区不再漂移 - index.html 加 viewport-fit=cover (env(safe-area-inset-*) 才生效) - app-shell 顶部 +20px+safe inset, bottom-nav 底部 +20px+safe inset 防状态栏/手势条遮内容 - 页面跳转加淡入上滑动画 (Layout 内嵌页面按 pathname 重挂载, Landing/Login 同款)
This commit is contained in:
+2
-2
@@ -39,7 +39,7 @@ function RootGate() {
|
||||
const effective = user ?? getStoredUser();
|
||||
return <Navigate to={effective ? "/home" : "/login"} replace />;
|
||||
}
|
||||
return <Landing />;
|
||||
return <div className="page-enter"><Landing /></div>;
|
||||
}
|
||||
|
||||
function RouterGate() {
|
||||
@@ -65,7 +65,7 @@ function RouterGate() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<RootGate />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/login" element={<div className="page-enter"><Login /></div>} />
|
||||
<Route element={<ProtectedRoute><Layout /></ProtectedRoute>}>
|
||||
<Route path="/home" element={<Home />} />
|
||||
<Route path="/closet" element={<Closet />} />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Outlet, useNavigate, useLocation } from "react-router-dom";
|
||||
import { useAppStore } from "../stores/app";
|
||||
|
||||
@@ -12,6 +13,12 @@ export default function Layout() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const user = useAppStore((s) => s.user);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 切换页面时回到顶部 + 触发过渡动画 (key 随路径重挂载)
|
||||
useEffect(() => {
|
||||
contentRef.current?.scrollTo({ top: 0 });
|
||||
}, [location.pathname]);
|
||||
|
||||
const isActive = (path: string) =>
|
||||
path === "/home" ? location.pathname === "/home" || location.pathname === "/" : location.pathname.startsWith(path);
|
||||
@@ -23,8 +30,10 @@ export default function Layout() {
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<div className="app-content">
|
||||
<Outlet />
|
||||
<div className="app-content" ref={contentRef}>
|
||||
<div key={location.pathname} className="page-enter">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<nav className="bottom-nav">
|
||||
<button className={"nav-item" + (isActive("/home") ? " active" : "")} onClick={() => navigate("/home")}>
|
||||
|
||||
+14
-1
@@ -61,6 +61,8 @@ textarea {
|
||||
height: 100%;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
/* 顶部避开状态栏/刘海, 再多让出一个行高 (edge-to-edge 时 env() 才生效) */
|
||||
padding-top: calc(env(safe-area-inset-top) + 20px);
|
||||
}
|
||||
|
||||
.app-content {
|
||||
@@ -92,7 +94,8 @@ textarea {
|
||||
background: var(--bg-card);
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 6px 8px;
|
||||
padding-bottom: calc(6px + env(safe-area-inset-bottom));
|
||||
/* 底部避开手势条, 再多让出一个行高, 避免文字被遮挡 */
|
||||
padding-bottom: calc(20px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
@@ -270,4 +273,14 @@ textarea {
|
||||
@keyframes toast-in {
|
||||
from { opacity: 0; transform: translateX(-50%) translateY(-8px); }
|
||||
to { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
}
|
||||
|
||||
/* 页面切换动画 (Layout 内嵌页面与 Landing/Login 共用) */
|
||||
.page-enter {
|
||||
animation: page-in 0.26s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes page-in {
|
||||
from { opacity: 0; transform: translateY(12px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
Reference in New Issue
Block a user