From b46ad761610e798a2f89943d077c5f0f91e0cf1f Mon Sep 17 00:00:00 2001 From: imeepos Date: Wed, 28 Jan 2026 14:15:33 +0800 Subject: [PATCH] fix: bug --- app/(tabs)/index.tsx | 1 + components/DynamicForm.tsx | 4 +- components/blocks/home/TemplateCard.test.tsx | 46 ++++++++++++++++++++ components/blocks/home/TemplateCard.tsx | 12 ++++- hooks/use-tab-navigation.test.ts | 44 +++++++++++++++++++ hooks/use-tab-navigation.ts | 13 ++++-- lib/uploadFile.ts | 5 +++ 7 files changed, 120 insertions(+), 5 deletions(-) diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index cdd22e9..a928615 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -154,6 +154,7 @@ export default function HomeScreen() { ( const handlePickAndUploadImage = useCallback(async (nodeId: string) => { try { const [imageUri] = await imgPicker({ maxImages: 1 }) + console.log('[DynamicForm] 选择图片成功:', imageUri) // 上传图片 const url = await uploadFile({ uri: imageUri }) + console.log('[DynamicForm] 上传成功,URL:', url) updateFormData(nodeId, url) setPreviewImages((prev) => ({ ...prev, [nodeId]: imageUri })) } catch (error: any) { @@ -188,7 +190,7 @@ export const DynamicForm = forwardRef( if (error?.message === '未选择任何图片') { return } - console.error('Pick and upload failed:', error) + console.error('[DynamicForm] 上传失败:', { error, message: error?.message, stack: error?.stack }) Toast.show(t('dynamicForm.uploadFailed') || '上传失败,请重试') } }, [t, updateFormData]) diff --git a/components/blocks/home/TemplateCard.test.tsx b/components/blocks/home/TemplateCard.test.tsx index b3324ff..387c43c 100644 --- a/components/blocks/home/TemplateCard.test.tsx +++ b/components/blocks/home/TemplateCard.test.tsx @@ -77,4 +77,50 @@ describe('TemplateCard Component', () => { expect(typeof TemplateCard).toBe('object') }) }) + + describe('Multi-language support', () => { + it('should display Chinese title when language is zh-CN', () => { + const title = '测试模板' + const titleEn = 'Test Template' + const language = 'zh-CN' + + // 根据语言选择显示的标题 + const displayTitle = language === 'en-US' ? titleEn : title + + expect(displayTitle).toBe('测试模板') + }) + + it('should display English title when language is en-US', () => { + const title = '测试模板' + const titleEn = 'Test Template' + const language = 'en-US' + + // 根据语言选择显示的标题 + const displayTitle = language === 'en-US' ? titleEn : title + + expect(displayTitle).toBe('Test Template') + }) + + it('should fallback to Chinese title when titleEn is missing', () => { + const title = '测试模板' + const titleEn = undefined + const language = 'en-US' + + // 当 titleEn 不存在时,回退到中文标题 + const displayTitle = language === 'en-US' && titleEn ? titleEn : title + + expect(displayTitle).toBe('测试模板') + }) + + it('should fallback to Chinese title when titleEn is empty string', () => { + const title = '测试模板' + const titleEn = '' + const language = 'en-US' + + // 当 titleEn 为空字符串时,回退到中文标题 + const displayTitle = language === 'en-US' && titleEn ? titleEn : title + + expect(displayTitle).toBe('测试模板') + }) + }) }) diff --git a/components/blocks/home/TemplateCard.tsx b/components/blocks/home/TemplateCard.tsx index f49b4c0..9628e70 100644 --- a/components/blocks/home/TemplateCard.tsx +++ b/components/blocks/home/TemplateCard.tsx @@ -2,10 +2,12 @@ import React, { memo, useCallback, useMemo } from 'react' import { Pressable, StyleSheet, Text, View, ViewStyle, ImageStyle } from 'react-native' import { Image } from 'expo-image' import { LinearGradient } from 'expo-linear-gradient' +import { useTranslation } from 'react-i18next' export interface TemplateCardProps { id?: string title: string + titleEn?: string previewUrl?: string webpPreviewUrl?: string coverImageUrl?: string @@ -49,6 +51,7 @@ const GRADIENT_END = { x: 0, y: 1 } const TemplateCardComponent: React.FC = ({ id, title, + titleEn, previewUrl, webpPreviewUrl, coverImageUrl, @@ -57,9 +60,16 @@ const TemplateCardComponent: React.FC = ({ onPress, testID, }) => { + const { i18n } = useTranslation() const aspectRatio = useMemo(() => parseAspectRatio(aspectRatioString), [aspectRatioString]) const imageUri = useMemo(() => getImageUri(webpPreviewUrl, previewUrl, coverImageUrl), [webpPreviewUrl, previewUrl, coverImageUrl]) + // Select display title based on current language + const displayTitle = useMemo(() => { + const isEnglish = i18n.language === 'en-US' + return isEnglish && titleEn ? titleEn : title + }, [i18n.language, title, titleEn]) + // 使用 useCallback 缓存 onPress 回调 const handlePress = useCallback(() => { if (id) { @@ -105,7 +115,7 @@ const TemplateCardComponent: React.FC = ({ style={styles.cardImageGradient} /> - {title} + {displayTitle} diff --git a/hooks/use-tab-navigation.test.ts b/hooks/use-tab-navigation.test.ts index fc552a0..5ea7fa8 100644 --- a/hooks/use-tab-navigation.test.ts +++ b/hooks/use-tab-navigation.test.ts @@ -103,6 +103,13 @@ const mockCategories: Category[] = [ { id: 'cat-3', name: 'Category 3', templates: [] }, ] +// Test data with multi-language support +const mockCategoriesWithI18n: Array = [ + { id: 'cat-1', name: '分类1', nameEn: 'Category 1', templates: [] }, + { id: 'cat-2', name: '分类2', nameEn: 'Category 2', templates: [] }, + { id: 'cat-3', name: '分类3', nameEn: 'Category 3', templates: [] }, +] + describe('useTabNavigation - core logic', () => { describe('initialization', () => { it('should select first category when initialCategoryId is empty', () => { @@ -256,4 +263,41 @@ describe('useTabNavigation - core logic', () => { expect(state.currentCategory).toBeUndefined() }) }) + + describe('multi-language support', () => { + it('should generate tabs with Chinese names when language is zh-CN', () => { + // 模拟中文环境 + const language = 'zh-CN' + const tabs = mockCategoriesWithI18n.map(cat => + language === 'en-US' ? cat.nameEn : cat.name + ) + + expect(tabs).toEqual(['分类1', '分类2', '分类3']) + }) + + it('should generate tabs with English names when language is en-US', () => { + // 模拟英文环境 + const language = 'en-US' + const tabs = mockCategoriesWithI18n.map(cat => + language === 'en-US' ? cat.nameEn : cat.name + ) + + expect(tabs).toEqual(['Category 1', 'Category 2', 'Category 3']) + }) + + it('should fallback to Chinese name when nameEn is missing', () => { + const categoriesWithMissingEn = [ + { id: 'cat-1', name: '分类1', nameEn: 'Category 1' }, + { id: 'cat-2', name: '分类2', nameEn: '' }, // nameEn 为空 + { id: 'cat-3', name: '分类3' }, // 没有 nameEn 字段 + ] + + const language = 'en-US' + const tabs = categoriesWithMissingEn.map(cat => + language === 'en-US' && cat.nameEn ? cat.nameEn : cat.name + ) + + expect(tabs).toEqual(['Category 1', '分类2', '分类3']) + }) + }) }) diff --git a/hooks/use-tab-navigation.ts b/hooks/use-tab-navigation.ts index d218f2d..a5dcddb 100644 --- a/hooks/use-tab-navigation.ts +++ b/hooks/use-tab-navigation.ts @@ -1,9 +1,11 @@ import { useState, useMemo, useCallback, useEffect } from 'react' import { CategoryTemplate } from '@repo/sdk' +import { useTranslation } from 'react-i18next' export interface Category { id: string name: string + nameEn?: string templates?: CategoryTemplate[] } @@ -23,6 +25,7 @@ export interface UseTabNavigationReturn { export function useTabNavigation(options: UseTabNavigationOptions): UseTabNavigationReturn { const { categories, initialCategoryId } = options + const { i18n } = useTranslation() // State for active tab index const [activeIndex, setActiveIndex] = useState(() => { @@ -52,10 +55,14 @@ export function useTabNavigation(options: UseTabNavigationOptions): UseTabNaviga } }, [categories, selectedCategoryId]) - // Generate tabs array from category names + // Generate tabs array from category names with i18n support const tabs = useMemo(() => { - return categories.map(category => category.name) - }, [categories]) + const isEnglish = i18n.language === 'en-US' + return categories.map(category => { + // Use English name if available and language is English, otherwise use Chinese name + return isEnglish && category.nameEn ? category.nameEn : category.name + }) + }, [categories, i18n.language]) // Get current category based on selectedCategoryId const currentCategory = useMemo(() => { diff --git a/lib/uploadFile.ts b/lib/uploadFile.ts index be2d54f..c572fd2 100644 --- a/lib/uploadFile.ts +++ b/lib/uploadFile.ts @@ -6,6 +6,9 @@ import { handleError } from '@/hooks/use-error' export async function uploadFile(params: { uri: string; mimeType?: string; fileName?: string }): Promise { const { uri, mimeType, fileName } = params + + console.log('[uploadFile] 开始上传:', { uri, mimeType, fileName, platform: Platform.OS }) + const formData = new FormData() formData.append('file', { uri: uri, @@ -17,8 +20,10 @@ export async function uploadFile(params: { uri: string; mimeType?: string; fileN const { data, error } = await handleError(async () => await fileController.uploadS3(formData)) if (error || !data?.data) { + console.error('[uploadFile] 上传失败:', { error, data, uri }) throw error || new Error('上传失败') } + console.log('[uploadFile] 上传成功:', data.data) return data.data }