From faefda3ea2e66995829fabeff89b56bc2ace723c Mon Sep 17 00:00:00 2001 From: imeepos Date: Mon, 1 Sep 2025 15:16:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AE=80=E5=8C=96=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=B8=BA4=E4=B8=AA=E6=B8=85=E6=99=B0?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构index页面为步骤式交互:上传→加载→结果→错误 - 优化用户体验:单一焦点,清晰的状态转换 - 美化UI设计:统一卡片容器,渐变背景,现代化按钮 - 完善提示词:更新为中文手办生成专用描述 - 修复SDK模型名称:gemini-2.5-flash-image - 添加类型安全:getTaskStatus返回string[]类型 --- src/pages/index/index.css | 133 +++++++++++++++++++++++++++++++++++++- src/pages/index/index.tsx | 111 ++++++++++++++++++++++++++----- src/sdk/index.ts | 15 ++--- 3 files changed, 233 insertions(+), 26 deletions(-) diff --git a/src/pages/index/index.css b/src/pages/index/index.css index b90956f..5beb7c6 100644 --- a/src/pages/index/index.css +++ b/src/pages/index/index.css @@ -1,3 +1,132 @@ -.index{ - font-size: 1rem; +.index { + min-height: 100vh; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + padding: 40rpx; +} + +/* 通用步骤容器 */ +.step-container { + background: white; + border-radius: 24rpx; + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1); + padding: 80rpx 60rpx; + text-align: center; + width: 100%; + max-width: 600rpx; +} + +/* 标题样式 */ +.step-title { + font-size: 48rpx; + font-weight: bold; + color: #333; + margin-bottom: 30rpx; + display: block; +} + +/* 描述文字 */ +.step-desc { + font-size: 28rpx; + color: #666; + margin-bottom: 60rpx; + line-height: 1.6; + display: block; +} + +/* 上传按钮 */ +.upload-btn { + background: linear-gradient(45deg, #667eea, #764ba2); + color: white; + border: none; + border-radius: 50rpx; + padding: 30rpx 80rpx; + font-size: 32rpx; + font-weight: bold; + box-shadow: 0 6rpx 20rpx rgba(102, 126, 234, 0.4); + min-width: 300rpx; +} + +.upload-btn:active { + transform: translateY(2rpx); + box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.3); +} + +/* 加载动画 */ +.loading-spinner { + width: 80rpx; + height: 80rpx; + border: 6rpx solid #f3f3f3; + border-top: 6rpx solid #667eea; + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 40rpx; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 结果网格 */ +.result-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20rpx; + margin-bottom: 60rpx; +} + +.result-image { + width: 100%; + height: 280rpx; + border-radius: 16rpx; + object-fit: cover; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); +} + +/* 重新生成按钮 */ +.restart-btn { + background: linear-gradient(45deg, #52c41a, #73d13d); + color: white; + border: none; + border-radius: 50rpx; + padding: 24rpx 60rpx; + font-size: 30rpx; + font-weight: bold; + box-shadow: 0 4rpx 16rpx rgba(82, 196, 26, 0.3); +} + +.restart-btn:active { + transform: translateY(2rpx); +} + +/* 错误描述 */ +.error-desc { + font-size: 26rpx; + color: #ff4d4f; + margin-bottom: 50rpx; + background: #fff2f0; + border: 1rpx solid #ffccc7; + border-radius: 12rpx; + padding: 30rpx; + line-height: 1.5; + display: block; +} + +/* 重试按钮 */ +.retry-btn { + background: linear-gradient(45deg, #ff4d4f, #ff7875); + color: white; + border: none; + border-radius: 50rpx; + padding: 24rpx 60rpx; + font-size: 30rpx; + font-weight: bold; + box-shadow: 0 4rpx 16rpx rgba(255, 77, 79, 0.3); +} + +.retry-btn:active { + transform: translateY(2rpx); } \ No newline at end of file diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx index 44fcd03..c4e536b 100644 --- a/src/pages/index/index.tsx +++ b/src/pages/index/index.tsx @@ -1,28 +1,107 @@ -import { View, Text, Button } from '@tarojs/components' -import { useLoad } from '@tarojs/taro' +import { View, Text, Button, Image } from '@tarojs/components' +import { useLoad, showToast } from '@tarojs/taro' +import { useState } from 'react' import './index.css' -import { useAd, useSdk } from '../../hooks/index' +import { useSdk } from '../../hooks/index' + +type PageStep = 'upload' | 'loading' | 'result' | 'error' + +interface AppState { + step: PageStep; + images: string[]; + error: string | null; +} export default function Index() { - const sdk = useSdk() + const [state, setState] = useState({ + step: 'upload', + images: [], + error: null + }) + useLoad(() => { return () => { } }) - return ( - - Hello world Ymm !!!! - ) + + // 2. Loading环节 + const renderLoadingStep = () => ( + + + AI正在生成中... + 请耐心等待,正在为您精心制作 + + ) + + // 3. 结果预览环节 + const renderResultStep = () => ( + + ✨ 生成完成 + + {state.images.map((url, index) => ( + + ))} + + + + ) + + // 4. 错误环节 + const renderErrorStep = () => ( + + ❌ 生成失败 + {state.error} + + + ) + + const renderCurrentStep = () => { + switch (state.step) { + case 'upload': return renderUploadStep() + case 'loading': return renderLoadingStep() + case 'result': return renderResultStep() + case 'error': return renderErrorStep() + default: return renderUploadStep() + } + } + + return ( + + {renderCurrentStep()} + + ) } diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 7be246a..9ef76fc 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -167,11 +167,10 @@ export class BowongAISDK { // 上传第一张图片 const filePath = chooseResult.tempFilePaths[0]; + const prompt = `将画面中的角色重塑为顶级收藏级树脂手办,全身动态姿势,置于角色主题底座;高精度材质,手工涂装,肌肤纹理与服装材质真实分明。戏剧性硬光为主光源,凸显立体感,无过曝;强效补光消除死黑,细节完整可见。背景为窗边景深模糊,侧后方隐约可见产品包装盒。博物馆级摄影质感,全身细节无损,面部结构精准。禁止:任何2D元素或照搬原图、塑料感、面部模糊、五官错位、细节丢失` return await this.generateImage({ img_file: filePath, - prompt: `将画面中的角色重塑为顶级收藏级树脂手办,全身动态姿势,置于角色主题底座;高精度材质,手工涂装,肌肤纹理与服装材质真实分明。 -戏剧性硬光为主光源,凸显立体感,无过曝;强效补光消除死黑,细节完整可见。背景为窗边景深模糊,侧后方隐约可见产品包装盒。 -博物馆级摄影质感,全身细节无损,面部结构精准。禁止:任何2D元素或照搬原图、塑料感、面部模糊、五官错位、细节丢失` + prompt: prompt }); } catch (error) { @@ -230,7 +229,7 @@ export class BowongAISDK { async generateImage(params: GenerateImageParams): Promise { const { prompt, - model_name = 'gemini-2.5-flash-image-preview', + model_name = 'gemini-2.5-flash-image', aspect_ratio = '9:16', mode = 'turbo', webhook_flag = false, @@ -291,7 +290,7 @@ export class BowongAISDK { * @param retryCount 内部重试计数,外部调用不需要传入 * @returns Promise 返回任务状态信息 */ - async getTaskStatus(taskId: string, retryCount: number = 0): Promise { + async getTaskStatus(taskId: string, retryCount: number = 0): Promise { const url = `${this.baseUrl}/api/custom/task/status?task_id=${taskId}`; const maxRetries = 3; @@ -306,7 +305,7 @@ export class BowongAISDK { } const result = response.data as ApiResponse; - + console.log({ result }) if (!result.status) { throw new Error(result.msg || '查询任务状态失败'); } @@ -320,14 +319,14 @@ export class BowongAISDK { } catch (error) { console.error(`查询任务状态失败 (第${retryCount + 1}次尝试):`, error); - + // 如果重试次数未达到最大值,等待5秒后重试 if (retryCount < maxRetries) { console.log(`将在5秒后进行第${retryCount + 2}次重试...`); await new Promise((resolve) => setTimeout(resolve, 5000)); return this.getTaskStatus(taskId, retryCount + 1); } - + // 超过最大重试次数,抛出异常 throw new Error(`查询任务状态失败,已重试${maxRetries}次: ${error instanceof Error ? error.message : String(error)}`); }