From d6cc74ab00a855d195bf576e5fc8f17ca6293fac Mon Sep 17 00:00:00 2001 From: imeepos Date: Wed, 28 Jan 2026 17:30:01 +0800 Subject: [PATCH] fix: bug --- app/(tabs)/my.tsx | 332 ++++++++++++------------ app/auth.tsx | 22 +- app/changePassword.tsx | 9 +- app/generateVideo.tsx | 125 +++++---- components/DynamicForm.tsx | 98 +++---- components/drawer/EditProfileDrawer.tsx | 158 +++++------ components/drawer/PointsDrawer.tsx | 11 - components/drawer/TopUpDrawer.tsx | 2 +- locales/en-US.json | 8 +- locales/zh-CN.json | 8 +- stores/editProfileStore.ts | 41 +++ 11 files changed, 439 insertions(+), 375 deletions(-) create mode 100644 stores/editProfileStore.ts diff --git a/app/(tabs)/my.tsx b/app/(tabs)/my.tsx index 5105947..5ac04f0 100644 --- a/app/(tabs)/my.tsx +++ b/app/(tabs)/my.tsx @@ -21,11 +21,12 @@ import { PointsIcon, SearchIcon, SettingsIcon } from '@/components/icon' import EditProfileDrawer from '@/components/drawer/EditProfileDrawer' import Dropdown from '@/components/ui/dropdown' import Toast from '@/components/ui/Toast' -import { signOut , useSession } from '@/lib/auth' +import { signOut, useSession } from '@/lib/auth' import { useTemplateGenerations, type TemplateGeneration } from '@/hooks' import { MySkeleton } from '@/components/skeleton/MySkeleton' import { useUserBalance } from '@/hooks/use-user-balance' +import { KeyboardAwareScrollView } from 'react-native-keyboard-controller' const { width: screenWidth } = Dimensions.get('window') const GALLERY_GAP = 2 @@ -173,7 +174,7 @@ export default function My() { return t('my.languageSwitchEn') } } - + const settingsOptions = [ { label: t('my.changePassword'), value: 'changePassword' }, { label: getLanguageLabel(), value: 'language' }, @@ -182,168 +183,175 @@ export default function My() { return ( - - - - {/* 顶部积分与设置 */} - - router.push('/membership' as any)} - > - - {balance} - - handleSettingsSelect(value)} - renderTrigger={(selectedOption, isOpen, toggle) => ( - - - - )} - dropdownStyle={{ - minWidth: 160, - right: 10, - backgroundColor: '#2A2A2A80', + + + + + + {/* 顶部积分与设置 */} + + router.push('/membership' as any)} + > + + {balance} + + handleSettingsSelect(value)} + renderTrigger={(selectedOption, isOpen, toggle) => ( + + + + )} + dropdownStyle={{ + minWidth: 160, + right: 10, + backgroundColor: '#2A2A2A80', + }} + /> + + + {/* 个人信息区 */} + + + + {profileName} + ID {session?.user?.id?.slice(0, 8) || '--------'} + + { + console.log('[my.tsx] Edit Profile button pressed') + setEditDrawerVisible(true) + }} + > + {t('my.editProfile')} + + + + {/* "生成作品" 标题行 */} + + {t('my.generatedWorks')} + router.push('/worksList' as any)} + > + + + + + {/* 作品九宫格 */} + {loading ? ( + + ) : ( + + } + > + + {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.resultUrl?.length || 0) > 0 && ( + + + {item.resultUrl?.length || 1} + + + )} + + {/* 状态角标 */} + {item.status === 'running' && ( + + {t('my.generating')} + + )} + {item.status === 'pending' && ( + + {t('my.queuing')} + + )} + + ))} + + {/* 加载更多指示器 */} + {loadingMore && ( + + + + )} + + {/* 空状态提示 */} + {!loading && generations.length === 0 && ( + + + {t('my.noWorks')} + + + )} + + + )} + + {/* 编辑资料抽屉 */} + + setEditDrawerVisible(false)} + initialName={profileName} + initialAvatar={session?.user?.image} + onSave={(data) => { + setProfileName(data.name) }} /> - - - {/* 个人信息区 */} - - - - {profileName} - ID {session?.user?.id?.slice(0, 8) || '--------'} - - setEditDrawerVisible(true)} - > - {t('my.editProfile')} - - - - {/* "生成作品" 标题行 */} - - {t('my.generatedWorks')} - router.push('/worksList' as any)} - > - - - - - {/* 作品九宫格 */} - {loading ? ( - - ) : ( - - } - > - - {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.resultUrl?.length || 0) > 0 && ( - - - {item.resultUrl?.length || 1} - - - )} - - {/* 状态角标 */} - {item.status === 'running' && ( - - {t('my.generating')} - - )} - {item.status === 'pending' && ( - - {t('my.queuing')} - - )} - - ))} - - {/* 加载更多指示器 */} - {loadingMore && ( - - - - )} - - {/* 空状态提示 */} - {!loading && generations.length === 0 && ( - - - {t('my.noWorks')} - - - )} - - - )} - - {/* 编辑资料抽屉 */} - setEditDrawerVisible(false)} - initialName={profileName} - initialAvatar={session?.user?.image} - onSave={(data) => { - setProfileName(data.name) - }} - /> + ) } diff --git a/app/auth.tsx b/app/auth.tsx index 8e6fb95..6e0cc01 100644 --- a/app/auth.tsx +++ b/app/auth.tsx @@ -4,6 +4,7 @@ import { StatusBar } from 'expo-status-bar' import { SafeAreaView } from 'react-native-safe-area-context' import { useRouter } from 'expo-router' import { LinearGradient } from 'expo-linear-gradient' +import { KeyboardAwareScrollView } from 'react-native-keyboard-controller' import { AuthForm } from '@/components/blocks/AuthForm' import { useSession } from '@/lib/auth' @@ -48,9 +49,15 @@ export default function Auth() { - - - + + + + + ) @@ -66,10 +73,17 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: 'transparent', }, - content: { + scrollView: { flex: 1, + }, + scrollContent: { + flexGrow: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 20, }, + content: { + width: '100%', + alignItems: 'center', + }, }) diff --git a/app/changePassword.tsx b/app/changePassword.tsx index 0ba7d99..2c66b0d 100644 --- a/app/changePassword.tsx +++ b/app/changePassword.tsx @@ -3,7 +3,6 @@ import { View, Text, StyleSheet, - ScrollView, Pressable, Platform, TextInput, @@ -14,6 +13,7 @@ import { SafeAreaView } from 'react-native-safe-area-context' import { useRouter } from 'expo-router' import { StatusBar as RNStatusBar } from 'react-native' import { useTranslation } from 'react-i18next' +import { KeyboardAwareScrollView } from 'react-native-keyboard-controller' import { LeftArrowIcon } from '@/components/icon' import { LinearGradient } from 'expo-linear-gradient' import { useChangePassword } from '@/hooks/use-change-password' @@ -140,11 +140,10 @@ export default function ChangePasswordScreen() { {t('changePassword.title')} - @@ -257,7 +256,7 @@ export default function ChangePasswordScreen() { - + ) } diff --git a/app/generateVideo.tsx b/app/generateVideo.tsx index a73e10d..182ace8 100644 --- a/app/generateVideo.tsx +++ b/app/generateVideo.tsx @@ -8,13 +8,13 @@ import { Pressable, StatusBar as RNStatusBar, Platform, - KeyboardAvoidingView, } from 'react-native' import { StatusBar } from 'expo-status-bar' import { SafeAreaView } from 'react-native-safe-area-context' import { Image } from 'expo-image' import { useRouter, useLocalSearchParams } from 'expo-router' import { useTranslation } from 'react-i18next' +import { KeyboardAwareScrollView } from 'react-native-keyboard-controller' import { LeftArrowIcon, WhitePointsIcon } from '@/components/icon' import UploadReferenceImageDrawer from '@/components/drawer/UploadReferenceImageDrawer' @@ -95,75 +95,69 @@ export default function GenerateVideoScreen() { translucent /> )} - - - {/* 顶部导航栏 */} - - router.back()} - > - - - + {/* 顶部导航栏 */} + + router.back()} + > + + + - {/* 主要内容区域 */} - - {/* 模板预览卡片 */} - - - - - {templateDetail?.title} - {templateDetail?.description} - - - {/* 价格标签 */} - - - {templateDetail?.price || 10} + {/* 主要内容区域 */} + + {/* 模板预览卡片 */} + + + + + {templateDetail?.title} + {templateDetail?.description} - - {/* 动态表单 */} - - {templateLoading ? ( - - {t('generateVideo.loading') || '加载中...'} - - ) : formSchema.startNodes && formSchema.startNodes.length > 0 ? ( - - ) : ( - - - {t('generateVideo.noFormFields') || '暂无可填写的表单项'} - - - )} + {/* 价格标签 */} + + + {templateDetail?.price || 10} - - + + {/* 动态表单 */} + + {templateLoading ? ( + + {t('generateVideo.loading') || '加载中...'} + + ) : formSchema.startNodes && formSchema.startNodes.length > 0 ? ( + + ) : ( + + + {t('generateVideo.noFormFields') || '暂无可填写的表单项'} + + + )} + + + {/* 图片上传抽屉 */} void points?: number + /** 当外层已有滚动视图时,禁用内部滚动 */ + disableScroll?: boolean } export const DynamicForm = forwardRef( - function DynamicForm({ formSchema, onSubmit, loading = false, onOpenDrawer, points }, ref) { + function DynamicForm({ formSchema, onSubmit, loading = false, onOpenDrawer, points, disableScroll = false }, ref) { const { t } = useTranslation() const startNodes = formSchema.startNodes || [] @@ -456,55 +457,60 @@ export const DynamicForm = forwardRef( ) } - return ( - - - {startNodes.map(renderField)} + const formContent = ( + <> + {startNodes.map(renderField)} - - - - - + + + + + ) + + // 当外层已有滚动视图时,不使用内部滚动 + if (disableScroll) { + return ( + + {formContent} + + ) + } + + return ( + + {formContent} + ) } ) const styles = StyleSheet.create({ - keyboardAvoidingView: { - flex: 1, - }, scrollView: { flex: 1, }, diff --git a/components/drawer/EditProfileDrawer.tsx b/components/drawer/EditProfileDrawer.tsx index 36852c3..205a6a1 100644 --- a/components/drawer/EditProfileDrawer.tsx +++ b/components/drawer/EditProfileDrawer.tsx @@ -4,21 +4,19 @@ import { Text, StyleSheet, Pressable, - TextInput, - Dimensions, Platform, Keyboard, - ScrollView, ActivityIndicator, } from 'react-native' import { Image } from 'expo-image' -import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useTranslation } from 'react-i18next' -import BottomSheet, { BottomSheetModal, BottomSheetView, BottomSheetBackdrop, BottomSheetScrollView } from '@gorhom/bottom-sheet' +import { BottomSheetModal, BottomSheetBackdrop, BottomSheetTextInput, BottomSheetView } from '@gorhom/bottom-sheet' import { CloseIcon, AvatarUploadIcon } from '@/components/icon' import { LinearGradient } from 'expo-linear-gradient' import { type ImagePickerAsset } from 'expo-image-picker' import { useUpdateProfile } from '@/hooks' +import { KeyboardAwareScrollView, useKeyboardContext, useKeyboardState } from 'react-native-keyboard-controller' +import { TextInput } from 'react-native-gesture-handler' interface EditProfileDrawerProps { @@ -41,13 +39,14 @@ export default function EditProfileDrawer({ const [name, setName] = useState(initialName) const [avatar, setAvatar] = useState(initialAvatar) const [selectedImage, setSelectedImage] = useState(null) - const insets = useSafeAreaInsets() + + const keyboardState = useKeyboardState() const { loading, pickImage, updateProfile } = useUpdateProfile() // 增加高度以避免被底部 Tab 栏遮挡,考虑底部安全区域 // 使用百分比让 BottomSheet 在键盘弹出时能够自动调整 - const snapPoints = useMemo(() => ['50%'], []) + const snapPoints = useMemo(() => ['80%'], []) useEffect(() => { if (visible) { @@ -56,11 +55,21 @@ export default function EditProfileDrawer({ setName(initialName) setAvatar(initialAvatar) setSelectedImage(null) + } else { bottomSheetRef.current?.dismiss() } }, [visible, initialName, initialAvatar]) + + useEffect(() => { + if (keyboardState && keyboardState.height) { + bottomSheetRef.current?.expand() + } else { + bottomSheetRef.current?.collapse() + } + }, [keyboardState]) + const handleSheetChanges = useCallback((index: number) => { if (index === -1) { onClose() @@ -132,48 +141,42 @@ export default function EditProfileDrawer({ keyboardBlurBehavior="restore" android_keyboardInputMode="adjustResize" > - - - {/* 顶部关闭按钮 */} - - - - {/* 头像区域 */} - - - - - - {loading ? ( - - ) : ( - - )} - - - + + {/* 顶部关闭按钮 */} + + + + {/* 头像区域 */} + + + + + + {loading ? ( + + ) : ( + + )} + + + - {/* 输入框 */} - + /> */} - {/* 保存按钮 */} - + + {/* 保存按钮 */} + + - - {loading ? ( - - ) : ( - {t('editProfile.save')} - )} - - - - + {loading ? ( + + ) : ( + {t('editProfile.save')} + )} + + + + ) } @@ -222,15 +233,8 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: '#1C1E22', paddingHorizontal: 16, - }, - scrollView: { - flex: 1, - }, - scrollContent: { paddingTop: 32, paddingBottom: Platform.OS === 'ios' ? 25 : 17, - paddingHorizontal: 0, - flexGrow: 1, }, closeButton: { position: 'absolute', diff --git a/components/drawer/PointsDrawer.tsx b/components/drawer/PointsDrawer.tsx index a83c1d1..dd3dcf6 100644 --- a/components/drawer/PointsDrawer.tsx +++ b/components/drawer/PointsDrawer.tsx @@ -110,17 +110,6 @@ export default function PointsDrawer({ {totalPoints} - - {/* 积分类型细分 */} - - {t('pointsDrawer.subscriptionPoints')} - {subscriptionPoints} - - - {t('pointsDrawer.topUpPoints')} - {topUpPoints} - - {/* 底部按钮 */} diff --git a/components/drawer/TopUpDrawer.tsx b/components/drawer/TopUpDrawer.tsx index e279710..b5b48d9 100644 --- a/components/drawer/TopUpDrawer.tsx +++ b/components/drawer/TopUpDrawer.tsx @@ -103,7 +103,7 @@ export default function TopUpDrawer({ // 获取余额 store 的方法 const { load: loadBalance, restartPolling } = useUserBalanceStore() - const snapPoints = useMemo(() => [420], []) + const snapPoints = useMemo(() => [500], []) useEffect(() => { if (visible) { diff --git a/locales/en-US.json b/locales/en-US.json index 1996b15..2beba43 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -222,7 +222,13 @@ "agreementText": "I have read and agree to", "terms": "Terms of Service", "privacy": "Privacy Policy", - "agreementAnd": "and" + "agreementAnd": "and", + "alipayNotInstalled": "Alipay SDK not installed", + "createOrderFailed": "Failed to create order", + "paymentSuccess": "Payment successful! Points are being credited...", + "paymentCancelled": "Payment cancelled", + "paymentFailed": "Payment failed, please try again", + "paymentError": "Payment error" }, "tabs": { "home": "Home", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 80750a8..ff33dd0 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -222,7 +222,13 @@ "agreementText": "我已阅读并同意", "terms": "服务条款", "privacy": "隐私协议", - "agreementAnd": "与" + "agreementAnd": "与", + "alipayNotInstalled": "支付宝 SDK 未安装", + "createOrderFailed": "创建订单失败", + "paymentSuccess": "支付成功!积分正在到账中...", + "paymentCancelled": "支付已取消", + "paymentFailed": "支付失败,请重试", + "paymentError": "支付出错" }, "tabs": { "home": "首页", diff --git a/stores/editProfileStore.ts b/stores/editProfileStore.ts new file mode 100644 index 0000000..cc06320 --- /dev/null +++ b/stores/editProfileStore.ts @@ -0,0 +1,41 @@ +import { create } from 'zustand' + +interface EditProfileState { + visible: boolean + initialName: string + initialAvatar?: string + onSaveCallback?: (data: { name: string; avatar?: string }) => void +} + +interface EditProfileActions { + open: (params: { + initialName?: string + initialAvatar?: string + onSave?: (data: { name: string; avatar?: string }) => void + }) => void + close: () => void +} + +type EditProfileStore = EditProfileState & EditProfileActions + +export const useEditProfileStore = create((set) => ({ + visible: false, + initialName: '', + initialAvatar: undefined, + onSaveCallback: undefined, + + open: ({ initialName = '', initialAvatar, onSave }) => { + set({ + visible: true, + initialName, + initialAvatar, + onSaveCallback: onSave, + }) + }, + + close: () => { + set({ + visible: false, + }) + }, +}))