feat: 添加图片生视频功能
- 在结果页面添加图片生视频按钮,仅在图片结果时显示 - 集成serverSdk调用executeTemplate接口生成视频 - 实现加载状态和错误处理机制 - 生成成功后跳转到生成页面查看进度 - 扩展DownloadSection组件支持新功能
This commit is contained in:
parent
be157d0bcb
commit
9e39ca842c
|
|
@ -4,12 +4,23 @@ import './index.css'
|
||||||
interface DownloadSectionProps {
|
interface DownloadSectionProps {
|
||||||
onDownload: () => void
|
onDownload: () => void
|
||||||
onRegenerate?: () => void
|
onRegenerate?: () => void
|
||||||
|
onImageToVideo?: () => void
|
||||||
loading: boolean
|
loading: boolean
|
||||||
hasWatchedAd?: boolean
|
hasWatchedAd?: boolean
|
||||||
adAvailable?: boolean
|
adAvailable?: boolean
|
||||||
|
showImageToVideo?: boolean
|
||||||
|
imageToVideoLoading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const DownloadSection: React.FC<DownloadSectionProps> = ({ onDownload, onRegenerate, loading, adAvailable = true }) => {
|
const DownloadSection: React.FC<DownloadSectionProps> = ({
|
||||||
|
onDownload,
|
||||||
|
onRegenerate,
|
||||||
|
onImageToVideo,
|
||||||
|
loading,
|
||||||
|
adAvailable = true,
|
||||||
|
showImageToVideo = false,
|
||||||
|
imageToVideoLoading = false
|
||||||
|
}) => {
|
||||||
const getButtonText = () => {
|
const getButtonText = () => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return '广告加载中...'
|
return '广告加载中...'
|
||||||
|
|
@ -40,6 +51,14 @@ const DownloadSection: React.FC<DownloadSectionProps> = ({ onDownload, onRegener
|
||||||
<Text>🎨 再来一张</Text>
|
<Text>🎨 再来一张</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
{showImageToVideo && onImageToVideo && (
|
||||||
|
<View
|
||||||
|
className={`regenerate-btn ${imageToVideoLoading ? 'disabled' : ''}`}
|
||||||
|
onClick={imageToVideoLoading ? undefined : onImageToVideo}
|
||||||
|
>
|
||||||
|
<Text>{imageToVideoLoading ? '🎬 生成中...' : '🎬 图片生视频'}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<Text className='download-tip'>{getTipText()}</Text>
|
<Text className='download-tip'>{getTipText()}</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
import { View, Image, Video } from '@tarojs/components'
|
import { View, Image, Video } from '@tarojs/components'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { saveImageToPhotosAlbum, saveVideoToPhotosAlbum, downloadFile, showToast, navigateBack, getCurrentInstance, hideToast } from '@tarojs/taro'
|
import { saveImageToPhotosAlbum, saveVideoToPhotosAlbum, navigateTo, downloadFile, showToast, navigateBack, getCurrentInstance, hideToast } from '@tarojs/taro'
|
||||||
import DownloadSection from '../../components/DownloadSection'
|
import DownloadSection from '../../components/DownloadSection'
|
||||||
import { useAd } from '../../hooks/useAd'
|
import { useAd } from '../../hooks/useAd'
|
||||||
|
import { useServerSdk } from '../../hooks/index'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
|
||||||
const ResultPage: React.FC = () => {
|
const ResultPage: React.FC = () => {
|
||||||
const [images, setImages] = useState<string[]>([])
|
const [images, setImages] = useState<string[]>([])
|
||||||
const [mediaType, setMediaType] = useState<'image' | 'video'>('image')
|
const [mediaType, setMediaType] = useState<'image' | 'video'>('image')
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const serverSdk = useServerSdk()
|
||||||
// 集成广告钩子
|
// 集成广告钩子
|
||||||
const { showAd, loading: adLoading, adAvailable } = useAd({
|
const { showAd, loading: adLoading, adAvailable } = useAd({
|
||||||
onReward: () => {
|
onReward: () => {
|
||||||
|
|
@ -162,7 +165,7 @@ const ResultPage: React.FC = () => {
|
||||||
// 提取扩展名
|
// 提取扩展名
|
||||||
const extensionMatch = fileName.match(/\.([^.]+)$/)
|
const extensionMatch = fileName.match(/\.([^.]+)$/)
|
||||||
const extension = extensionMatch ? extensionMatch[1].toLowerCase() : ''
|
const extension = extensionMatch ? extensionMatch[1].toLowerCase() : ''
|
||||||
|
|
||||||
return { fileName, extension, cleanUrl }
|
return { fileName, extension, cleanUrl }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,7 +190,7 @@ const ResultPage: React.FC = () => {
|
||||||
hideToast()
|
hideToast()
|
||||||
// 使用实际的文件路径
|
// 使用实际的文件路径
|
||||||
const filePath = downloadRes.tempFilePath
|
const filePath = downloadRes.tempFilePath
|
||||||
|
|
||||||
showToast({ title: '保存中...', icon: 'loading' })
|
showToast({ title: '保存中...', icon: 'loading' })
|
||||||
// 根据实际文件类型选择保存方法
|
// 根据实际文件类型选择保存方法
|
||||||
if (mediaType === 'video') {
|
if (mediaType === 'video') {
|
||||||
|
|
@ -249,6 +252,49 @@ const ResultPage: React.FC = () => {
|
||||||
navigateBack()
|
navigateBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 图片生视频
|
||||||
|
const handleImageToVideo = async () => {
|
||||||
|
if (mediaType !== 'image' || !images[0]) {
|
||||||
|
showToast({
|
||||||
|
title: '仅支持图片生成视频',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
showToast({
|
||||||
|
title: '正在生成视频...',
|
||||||
|
icon: 'loading',
|
||||||
|
duration: 60000
|
||||||
|
})
|
||||||
|
|
||||||
|
const taskId = await serverSdk.executeTemplate({
|
||||||
|
imageUrl: images[0],
|
||||||
|
templateCode: 'character_figurine_v1'
|
||||||
|
})
|
||||||
|
|
||||||
|
hideToast()
|
||||||
|
|
||||||
|
// 跳转到生成页面
|
||||||
|
navigateTo({
|
||||||
|
url: `/pages/generate/index?taskId=${taskId}&templateCode=character_figurine_v1`
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
hideToast()
|
||||||
|
console.error('图片生视频失败:', error)
|
||||||
|
showToast({
|
||||||
|
title: '生成失败,请重试',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 兼容原有的多图片显示
|
// 兼容原有的多图片显示
|
||||||
if (!images.length) {
|
if (!images.length) {
|
||||||
|
|
@ -287,8 +333,11 @@ const ResultPage: React.FC = () => {
|
||||||
<DownloadSection
|
<DownloadSection
|
||||||
onDownload={showAd}
|
onDownload={showAd}
|
||||||
onRegenerate={handleRegenerate}
|
onRegenerate={handleRegenerate}
|
||||||
|
onImageToVideo={handleImageToVideo}
|
||||||
loading={adLoading}
|
loading={adLoading}
|
||||||
adAvailable={adAvailable}
|
adAvailable={adAvailable}
|
||||||
|
showImageToVideo={mediaType === 'image'}
|
||||||
|
imageToVideoLoading={loading}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue