From b7d9186570bdf247ebc4f31a40128748a470f324 Mon Sep 17 00:00:00 2001 From: Junhui Chen Date: Sun, 3 Aug 2025 13:58:45 +0800 Subject: [PATCH] =?UTF-8?q?enhance:=20=E4=BC=98=E5=8C=96=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(tabs)/_layout.tsx | 367 +++++++++++++++-------------- app/(tabs)/memo-list.tsx | 10 +- app/(tabs)/owner.tsx | 52 +++- components/layout/ask.tsx | 5 + components/owner/SkeletonOwner.tsx | 92 ++++++++ components/owner/createCount.tsx | 20 +- 6 files changed, 346 insertions(+), 200 deletions(-) create mode 100644 components/owner/SkeletonOwner.tsx diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index f3e8a9a..ff49b19 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -12,6 +12,8 @@ import * as SecureStore from 'expo-secure-store'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Platform } from 'react-native'; +import { TransitionPresets } from '@react-navigation/bottom-tabs'; +import AskNavbar from '@/components/layout/ask'; interface PollingData { title: string; @@ -19,6 +21,7 @@ interface PollingData { content: string; extra: any; } + export default function TabLayout() { const { t } = useTranslation(); const colorScheme = useColorScheme(); @@ -171,192 +174,198 @@ export default function TabLayout() { }, [token]); return ( - - {/* 落地页 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + <> + - {/* 登录 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* 重置密码 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* loading页面 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* 用户信息收集 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* ask页面 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* memo list */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* owner */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* 排行榜 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* 对话详情页 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - {/* 隐私协议 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - - {/* Support Screen */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - - {/* Debug Screen - only in development */} - {process.env.NODE_ENV === 'development' && ( + > + {/* 落地页 */} ( - - ), + title: 'Memo', + tabBarButton: () => null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + {/* 登录 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + {/* 重置密码 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + {/* loading页面 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + {/* 用户信息收集 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + {/* ask页面 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' }, // 确保在标签栏中不显示 + ...TransitionPresets.ShiftTransition, + }} + /> + {/* memo list */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' }, // 确保在标签栏中不显示 + ...TransitionPresets.ShiftTransition, + }} + /> + {/* owner */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' }, // 确保在标签栏中不显示 + ...TransitionPresets.ShiftTransition, + }} + /> + {/* 排行榜 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + {/* 对话详情页 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + {/* 隐私协议 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 }} /> - )} - {/* 下载页面 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> + {/* Support Screen */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> - {/* 购买权益页面 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> + {/* Debug Screen - only in development */} + {process.env.NODE_ENV === 'development' && ( + ( + + ), + }} + /> + )} - {/* 设置页面 */} - null, // 隐藏底部标签栏 - headerShown: false, // 隐藏导航栏 - tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 - }} - /> - + {/* 下载页面 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + + {/* 购买权益页面 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + + {/* 设置页面 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> + + + ); } diff --git a/app/(tabs)/memo-list.tsx b/app/(tabs)/memo-list.tsx index fa21157..9d96226 100644 --- a/app/(tabs)/memo-list.tsx +++ b/app/(tabs)/memo-list.tsx @@ -9,7 +9,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import ChatSvg from '@/assets/icons/svg/chat.svg'; import ErrorBoundary from '@/components/common/ErrorBoundary'; import UploaderProgress from '@/components/file-upload/upload-progress/uploader-progress'; -import AskNavbar from '@/components/layout/ask'; + import SkeletonItem from '@/components/memo/SkeletonItem'; // 类型定义 @@ -176,7 +176,7 @@ const MemoList = () => { {/* 上传进度 */} - + {/* */} ), [insets.top]); @@ -218,9 +218,8 @@ const MemoList = () => { // 如果组件未完全加载,显示骨架屏 if (!isMounted) { return ( - + - ); } @@ -257,9 +256,6 @@ const MemoList = () => { contentContainerStyle={styles.listContent} ItemSeparatorComponent={() => } /> - - {/* 底部导航栏 */} - ); diff --git a/app/(tabs)/owner.tsx b/app/(tabs)/owner.tsx index 2bffecc..5fbe53c 100644 --- a/app/(tabs)/owner.tsx +++ b/app/(tabs)/owner.tsx @@ -1,9 +1,10 @@ import ConversationsSvg from '@/assets/icons/svg/conversations.svg'; import StoriesSvg from '@/assets/icons/svg/stories.svg'; -import AskNavbar from '@/components/layout/ask'; + import CreateCountComponent from '@/components/owner/createCount'; import MemberCard from '@/components/owner/rights/memberCard'; import SettingModal from '@/components/owner/setting'; +import SkeletonOwner from '@/components/owner/SkeletonOwner'; import UserInfo from '@/components/owner/userName'; import { checkAuthStatus } from '@/lib/auth'; import { fetchApi } from '@/lib/server-api-util'; @@ -20,6 +21,9 @@ export default function OwnerPage() { const router = useRouter(); const pathname = usePathname(); + // 添加页面挂载状态 + const [isMounted, setIsMounted] = useState(false); + useEffect(() => { const checkAuth = async () => { const authStatus = await checkAuthStatus(router); @@ -47,11 +51,17 @@ export default function OwnerPage() { // 获取用户信息 const [userInfoDetails, setUserInfoDetails] = useState({} as UserInfoDetails); - const getUserInfo = () => { + + // 优化getUserInfo函数,添加挂载状态检查 + const getUserInfo = useCallback(() => { fetchApi("/membership/personal-center-info").then((res) => { - setUserInfoDetails(res as UserInfoDetails); + // 只有在组件挂载时才更新状态 + if (isMounted) { + setUserInfoDetails(res as UserInfoDetails); + } }) - } + }, [isMounted]); + // 设计轮询获取数量统计 useFocusEffect( useCallback(() => { @@ -70,8 +80,35 @@ export default function OwnerPage() { // 初始化获取用户信息 useEffect(() => { - getUserInfo(); - }, []); + let isActive = true; + + const initialize = async () => { + try { + await getUserInfo(); + } catch (error) { + console.error('初始化失败:', error); + } finally { + if (isActive) { + setIsMounted(true); + } + } + }; + + initialize(); + + return () => { + isActive = false; + }; + }, [getUserInfo]); + + // 如果组件未完全加载,显示骨架屏 + if (!isMounted) { + return ( + + + + ); + } return ( @@ -112,9 +149,6 @@ export default function OwnerPage() { {modalVisible && ( )} - - {/* 导航栏 */} - ); } diff --git a/components/layout/ask.tsx b/components/layout/ask.tsx index 95e9716..e933f68 100644 --- a/components/layout/ask.tsx +++ b/components/layout/ask.tsx @@ -131,6 +131,11 @@ const AskNavbar = () => { } }), [width]); + // 如果当前路径是ask页面,则不渲染导航栏 + if (pathname != '/memo-list' && pathname != '/owner') { + return null; + } + return ( diff --git a/components/owner/SkeletonOwner.tsx b/components/owner/SkeletonOwner.tsx new file mode 100644 index 0000000..333eb77 --- /dev/null +++ b/components/owner/SkeletonOwner.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +// 骨架屏占位组件 +const SkeletonItem = () => ( + +); + +const SkeletonOwner = () => { + const insets = useSafeAreaInsets(); + + return ( + + {/* 用户信息骨架屏 */} + + + + + + + + + + + + + + + {/* 会员卡骨架屏 */} + + + + + {/* 作品数据骨架屏 */} + + + + + + + + {/* 排行榜骨架屏 */} + + + + {Array(3).fill(0).map((_, index) => ( + + ))} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: 'white', + }, + section: { + marginBottom: 16, + }, + skeletonItem: { + backgroundColor: '#E1E1E1', + borderRadius: 8, + overflow: 'hidden', + }, + userInfoHeader: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 16, + }, + userInfoTextContainer: { + flex: 1, + marginLeft: 16, + }, + userInfoStats: { + flexDirection: 'row', + justifyContent: 'space-around', + }, + countContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + gap: 16, + }, + rankingList: { + marginTop: 16, + }, +}); + +export default SkeletonOwner; diff --git a/components/owner/createCount.tsx b/components/owner/createCount.tsx index aa1518b..ab6fae7 100644 --- a/components/owner/createCount.tsx +++ b/components/owner/createCount.tsx @@ -1,3 +1,4 @@ +import React, { useMemo } from "react"; import { StyleProp, StyleSheet, View, ViewStyle } from "react-native"; import { ThemedText } from "../ThemedText"; @@ -7,9 +8,17 @@ interface CreateCountProps { number: number; style?: StyleProp; } -const CreateCountComponent = (props: CreateCountProps) => { + +// 使用React.memo优化组件,避免不必要的重渲染 +const CreateCountComponent = React.memo((props: CreateCountProps) => { + // 预计算样式以提高性能 + const containerStyle = useMemo(() => [ + styles.container, + props.style + ], [props.style]); + return ( - + {props.number} @@ -19,7 +28,7 @@ const CreateCountComponent = (props: CreateCountProps) => { ); -}; +}); const styles = StyleSheet.create({ container: { @@ -39,7 +48,7 @@ const styles = StyleSheet.create({ }, shadowOpacity: 0.25, shadowRadius: 3.84, - elevation: 5, + // elevation: 1, }, header: { width: "100%", @@ -63,5 +72,6 @@ const styles = StyleSheet.create({ textAlign: "center", color: "#4C320C" }, -}) +}); + export default CreateCountComponent;