From 4270973652992f9de034714219cc274cd0dc9a4f Mon Sep 17 00:00:00 2001 From: imeepos Date: Fri, 16 Jan 2026 12:34:27 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=92=8C=E6=B7=BB=E5=8A=A0=E7=B1=BB=E5=9E=8B=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 统一 OWNER_ID 导入,从 lib/auth 导入而非环境变量 - 添加 type-checks 脚本用于 TypeScript 类型检查 - 对接 my 页面后端接口,使用 useTemplateGenerations - 添加 MySkeleton 骨架屏组件 - 添加下拉刷新功能 - 添加空状态提示文案 Co-Authored-By: Claude --- app/(tabs)/my.tsx | 282 +++++++++++---------------- components/skeleton/HomeSkeleton.tsx | 78 ++++++++ hooks/use-activates.ts | 2 +- hooks/use-categories.ts | 2 +- hooks/use-template-generations.ts | 2 +- locales/en-US.json | 3 +- locales/zh-CN.json | 3 +- package.json | 1 + 8 files changed, 205 insertions(+), 168 deletions(-) create mode 100644 components/skeleton/HomeSkeleton.tsx diff --git a/app/(tabs)/my.tsx b/app/(tabs)/my.tsx index 82198db..f0410c8 100644 --- a/app/(tabs)/my.tsx +++ b/app/(tabs)/my.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useCallback } from 'react' import { View, Text, @@ -7,6 +7,7 @@ import { Dimensions, Pressable, StatusBar as RNStatusBar, + RefreshControl, } from 'react-native' import { StatusBar } from 'expo-status-bar' import { SafeAreaView } from 'react-native-safe-area-context' @@ -18,41 +19,47 @@ import EditProfileDrawer from '@/components/drawer/EditProfileDrawer' import Dropdown from '@/components/ui/dropdown' import Toast from '@/components/ui/Toast' import { signOut } from '@/lib/auth' +import { useTemplateGenerations, type TemplateGeneration } from '@/hooks' +import { MySkeleton } from '@/components/skeleton/MySkeleton' const { width: screenWidth } = Dimensions.get('window') const GALLERY_GAP = 2 const GALLERY_HORIZONTAL_PADDING = 0 -// 计算每个卡片的宽度:屏幕宽度 - 左右padding - 2个间距,然后除以3,使用 Math.floor 确保整数像素 const GALLERY_ITEM_SIZE = Math.floor( (screenWidth - GALLERY_HORIZONTAL_PADDING * 2 - GALLERY_GAP * 2) / 3 ) -// status状态有running pending completed -const works = [ - { id: 1, status: 'running' as const, count: 1 }, - { id: 2, status: 'completed' as const, count: 1 }, - { id: 3, status: 'completed' as const, count: 1 }, - { id: 4, status: 'completed' as const, count: 2 }, - { id: 5, status: 'pending' as const, count: 1 }, - { id: 6, status: 'pending' as const, count: 1 }, - { id: 7, status: 'pending' as const, count: 1 }, - { id: 8, status: 'pending' as const, count: 1 }, - { id: 9, status: 'completed' as const, count: 1 }, - { id: 10, status: 'completed' as const, count: 1 }, - { id: 11, status: 'completed' as const, count: 1 }, - { id: 12, status: 'completed' as const, count: 1 }, - { id: 13, status: 'completed' as const, count: 1 }, - { id: 14, status: 'completed' as const, count: 1 }, - { id: 15, status: 'completed' as const, count: 1 }, - { id: 16, status: 'completed' as const, count: 1 }, - { id: 17, status: 'completed' as const, count: 1 }, - -] + +// 获取作品封面图 +const getCoverUrl = (item: TemplateGeneration) => + item.resultUrl?.[0] || item.template?.coverImageUrl export default function My() { const router = useRouter() const { t, i18n } = useTranslation() const [editDrawerVisible, setEditDrawerVisible] = useState(false) const [profileName, setProfileName] = useState('乔乔乔乔') + const [refreshing, setRefreshing] = useState(false) + + // 使用 useTemplateGenerations hook 获取用户作品列表 + const { + generations, + loading, + error, + execute: loadGenerations, + refetch, + } = useTemplateGenerations() + + // 初始化加载作品列表 + useEffect(() => { + loadGenerations({ page: 1, limit: 50 }) + }, []) + + // 处理下拉刷新 + const handleRefresh = useCallback(async () => { + setRefreshing(true) + await refetch({ page: 1, limit: 50 }) + setRefreshing(false) + }, [refetch]) // 处理设置菜单选择 const handleSettingsSelect = async (value: string) => { @@ -172,70 +179,86 @@ export default function My() { {/* 作品九宫格 */} - - - {works.map((item, index) => ( - { - // 只有已完成的作品才能点击进入详情页 - if (item.status === 'completed') { - router.push({ - pathname: '/generationRecord' as any, - params: { id: item.id.toString() }, - }) - } - }} - disabled={item.status !== 'completed'} - > - + {loading && !refreshing ? ( + + ) : ( + + } + > + + {generations.map((item, index) => ( + { + if (item.status === 'completed') { + router.push({ + pathname: '/generationRecord' as any, + params: { id: item.id }, + }) + } + }} + disabled={item.status !== 'completed'} + > + - {/* 生成中遮罩 */} - {item.status != 'completed' && ( - - )} + {/* 遮罩:非完成状态 */} + {item.status !== 'completed' && ( + + )} - {/* 右上角作品数量角标 */} - {item.status === 'completed'&& - - {item.count} + {/* 数量角标:已完成且有结果 */} + {item.status === 'completed' && (item.resultUrl?.length || 0) > 0 && ( + + + {item.resultUrl?.length || 1} + + + )} + + {/* 状态角标 */} + {item.status === 'running' && ( + + {t('my.generating')} + + )} + {item.status === 'pending' && ( + + {t('my.queuing')} + + )} + + ))} + + {/* 空状态提示 */} + {!loading && generations.length === 0 && ( + + + {t('my.noWorks')} - } - - {/* "生成中"角标:覆盖在图片左下角 */} - {item.status === 'running' && ( - - - {t('my.generating')} - - - )} - {item.status === 'pending' && ( - - - {t('my.queuing')} - - - )} - - ))} - - + + )} + + + )} {/* 编辑资料抽屉 */} + {/* 标签骨架屏 */} + + {[1, 2, 3, 4].map((_, index) => ( + + ))} + + + {/* 卡片骨架屏 */} + + {[1, 2, 3, 4, 5, 6].map((_, index) => ( + + + + ))} + + + ) +} + +const styles = StyleSheet.create({ + container: { + backgroundColor: '#090A0B', + }, + tabsContainer: { + flexDirection: 'row', + paddingHorizontal: 16, + paddingVertical: 12, + gap: 20, + marginBottom: 18, + }, + tabSkeleton: { + marginBottom: 8, + }, + gridContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + paddingHorizontal: 8, + justifyContent: 'space-between', + }, + cardSkeleton: { + marginBottom: 12, + }, + cardLeft: { + marginRight: 0, + }, + cardRight: { + marginLeft: 0, + }, +}) diff --git a/hooks/use-activates.ts b/hooks/use-activates.ts index 6b07705..e235a48 100644 --- a/hooks/use-activates.ts +++ b/hooks/use-activates.ts @@ -4,9 +4,9 @@ import { useState } from 'react' import { type ApiError } from '@/lib/types' +import { OWNER_ID } from '@/lib/auth' import { handleError } from './use-error' -const OWNER_ID = process.env.EXPO_PUBLIC_OWNER_ID || '' export const useActivates = () => { const [loading, setLoading] = useState(false) diff --git a/hooks/use-categories.ts b/hooks/use-categories.ts index 6bd2f41..e5b0a8b 100644 --- a/hooks/use-categories.ts +++ b/hooks/use-categories.ts @@ -4,9 +4,9 @@ import { useState } from 'react' import { type ApiError } from '@/lib/types' +import { OWNER_ID } from '@/lib/auth' import { handleError } from './use-error' -const OWNER_ID = process.env.EXPO_PUBLIC_OWNER_ID || '' export const useCategories = () => { const [loading, setLoading] = useState(false) diff --git a/hooks/use-template-generations.ts b/hooks/use-template-generations.ts index 727725f..78a2bc2 100644 --- a/hooks/use-template-generations.ts +++ b/hooks/use-template-generations.ts @@ -11,7 +11,6 @@ import { type ApiError } from '@/lib/types' import { handleError } from './use-error' -const OWNER_ID = process.env.EXPO_PUBLIC_OWNER_ID || '' export const useTemplateGenerations = () => { const [loading, setLoading] = useState(false) @@ -106,3 +105,4 @@ export const useTemplateGenerations = () => { } export type { TemplateGeneration } + diff --git a/locales/en-US.json b/locales/en-US.json index a660a77..89933d0 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -8,7 +8,8 @@ "language": "Language", "languageSwitch": "Language: 中文", "languageSwitchEn": "Language: English", - "logout": "Logout" + "logout": "Logout", + "noWorks": "No works yet" }, "changePassword": { "title": "Change Password", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index ceccb0c..7071f0e 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -8,7 +8,8 @@ "language": "语言", "languageSwitch": "语言: 中文", "languageSwitchEn": "Language: English", - "logout": "退出登录" + "logout": "退出登录", + "noWorks": "暂无作品" }, "changePassword": { "title": "修改密码", diff --git a/package.json b/package.json index 1963919..9f1ff08 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "prebuild:android": "expo prebuild --platform android", "reset-project": "node ./scripts/reset-project.js", "claude": "claude --dangerously-skip-permissions", + "type-checks": "tsc --noEmit", "android": "expo run:android", "ios": "expo run:ios", "web": "expo start --web",