feat: 对接 templateDetail 页面后端接口

- 创建 hooks/use-error.ts 统一错误处理
- 创建 hooks/use-template-detail.ts 获取模板详情
- 创建 hooks/use-template-generations.ts 获取模板生成记录
- 更新 hooks 使用 handleError 统一错误处理
- 优化 SearchResultsGrid 组件,复用 TemplateGeneration 类型
- 删除不必要的类型转换和重复代码

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
imeepos
2026-01-16 11:56:38 +08:00
parent fb719c6ea2
commit ae120f24d3
9 changed files with 487 additions and 92 deletions

View File

@@ -12,17 +12,14 @@ import { useRouter } from 'expo-router'
import { useTranslation } from 'react-i18next'
import { WhiteStarIcon } from '@/components/icon'
import type { TemplateGeneration } from '@/hooks'
const { width: screenWidth } = Dimensions.get('window')
interface SearchResultItem {
id: number
title: string
image: any
interface SearchResultItem extends TemplateGeneration {
height: number
videoUrl?: any
thumbnailUrl?: any
duration?: string
title: string
image: string
}
interface SearchResultsGridProps {
@@ -33,27 +30,24 @@ export default function SearchResultsGrid({ results }: SearchResultsGridProps) {
const { t } = useTranslation()
const router = useRouter()
const [gridWidth, setGridWidth] = useState(screenWidth)
const horizontalPadding = 8 * 2 // gridContainer 的左右 padding
const cardGap = 5 // 两个卡片之间的间距
const horizontalPadding = 8 * 2
const cardGap = 5
const cardWidth = (gridWidth - horizontalPadding - cardGap) / 2
const handleSameStylePress = (item: SearchResultItem) => {
// 构建模板数据,传递给 generateVideo 页面
const templateData = {
id: item.id,
videoUrl: item.videoUrl || item.image, // 如果没有 videoUrl使用 image
thumbnailUrl: item.thumbnailUrl || item.image, // 如果没有 thumbnailUrl使用 image
title: item.title,
duration: item.duration || '00:00', // 默认时长
id: item.template?.id,
videoUrl: item.resultUrl?.[0] || item.originalUrl,
thumbnailUrl: item.template?.coverImageUrl,
title: item.template?.title || item.template?.titleEn || '',
}
router.push({
pathname: '/generateVideo' as any,
params: {
template: JSON.stringify(templateData),
},
params: { template: JSON.stringify(templateData) },
} as any)
}
if (results.length === 0) {
return (
<View style={styles.emptyContainer}>
@@ -61,6 +55,7 @@ export default function SearchResultsGrid({ results }: SearchResultsGridProps) {
</View>
)
}
return (
<ScrollView
style={styles.scrollView}
@@ -84,16 +79,9 @@ export default function SearchResultsGrid({ results }: SearchResultsGridProps) {
]}
>
<View
style={[
styles.cardImageContainer,
{ height: item.height },
]}
style={[styles.cardImageContainer, { height: item.height }]}
>
<Image
source={item.image}
style={styles.cardImage}
contentFit="cover"
/>
<Image source={item.image} style={styles.cardImage} contentFit="cover" />
</View>
<Text style={styles.cardTitle} numberOfLines={1}>
{item.title}
@@ -116,9 +104,6 @@ const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
scrollContent: {
// paddingBottom: 100,
},
gridContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
@@ -142,18 +127,23 @@ const styles = StyleSheet.create({
borderRadius: 12,
overflow: 'hidden',
marginBottom: 8,
position: 'relative',
},
cardImage: {
width: '100%',
height: '100%',
},
cardTitle: {
color: '#FFFFFF',
fontSize: 12,
fontWeight: '500',
paddingHorizontal: 8,
},
sameStyleButton: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
backgroundColor: '#262A31',
height:32,
height: 32,
marginHorizontal: 8,
marginTop: 10,
marginBottom: 8,
@@ -166,12 +156,6 @@ const styles = StyleSheet.create({
fontWeight: '500',
lineHeight: 17,
},
cardTitle: {
color: '#FFFFFF',
fontSize: 12,
fontWeight: '500',
paddingHorizontal: 8,
},
emptyContainer: {
flex: 1,
backgroundColor: '#090A0B',