feat: 添加用户登录检测和再生成功能

- 在首页添加用户会话检测机制
- 新增再来一张按钮功能,允许用户快速重新生成图片
- 完善平台抽象层,支持字节跳动小程序用户信息接口
- 优化下载区域组件,支持更多交互功能
- 修复错误提示组件文本显示问题
This commit is contained in:
imeepos 2025-09-02 12:29:15 +08:00
parent cef1c697a5
commit a38eab405c
9 changed files with 155 additions and 11 deletions

View File

@ -6,6 +6,7 @@ import './app.css'
function App({ children }: PropsWithChildren<any>) { function App({ children }: PropsWithChildren<any>) {
useLaunch(() => { useLaunch(() => {
console.log('App launched.') console.log('App launched.')
}) })
// children 是将要会渲染的页面 // children 是将要会渲染的页面

View File

@ -42,6 +42,27 @@
opacity: 1; opacity: 1;
} }
.regenerate-btn {
width: 80%;
height: 100rpx;
background: linear-gradient(45deg, #52c41a, #73d13d);
color: white;
border: none;
border-radius: 50rpx;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: opacity 0.2s;
}
.regenerate-btn:active {
opacity: 0.8;
}
.download-tip { .download-tip {
color: #fff; color: #fff;
font-size: 24rpx; font-size: 24rpx;

View File

@ -4,10 +4,11 @@ import './index.css'
interface DownloadSectionProps { interface DownloadSectionProps {
onDownload: () => void onDownload: () => void
onRegenerate?: () => void
loading: boolean loading: boolean
} }
const DownloadSection: React.FC<DownloadSectionProps> = memo(({ onDownload, loading }) => { const DownloadSection: React.FC<DownloadSectionProps> = memo(({ onDownload, onRegenerate, loading }) => {
return ( return (
<View className="download-section"> <View className="download-section">
<View <View
@ -16,6 +17,11 @@ const DownloadSection: React.FC<DownloadSectionProps> = memo(({ onDownload, load
> >
<Text>{loading ? '广告加载中...' : '📱 看广告下载到相册'}</Text> <Text>{loading ? '广告加载中...' : '📱 看广告下载到相册'}</Text>
</View> </View>
{onRegenerate && (
<View className="regenerate-btn" onClick={onRegenerate}>
<Text>🎨 </Text>
</View>
)}
<Text className="download-tip">广</Text> <Text className="download-tip">广</Text>
</View> </View>
) )

View File

@ -28,9 +28,11 @@
font-size: 40rpx; font-size: 40rpx;
font-weight: bold; font-weight: bold;
margin-bottom: 20rpx; margin-bottom: 20rpx;
display: block;
} }
.error-hint { .error-hint {
font-size: 28rpx; font-size: 28rpx;
opacity: 0.8; opacity: 0.8;
display: block;
} }

View File

@ -6,6 +6,7 @@ import { useSdk } from '../../hooks/index'
import UploadButton from '../../components/UploadButton' import UploadButton from '../../components/UploadButton'
import LoadingOverlay from '../../components/LoadingOverlay' import LoadingOverlay from '../../components/LoadingOverlay'
import ErrorOverlay from '../../components/ErrorOverlay' import ErrorOverlay from '../../components/ErrorOverlay'
import { createPlatformFactory } from '../../platforms'
type PageStep = 'upload' | 'loading' | 'error' type PageStep = 'upload' | 'loading' | 'error'
@ -23,6 +24,9 @@ export default function Index() {
const chooseAndGenerateImage = async () => { const chooseAndGenerateImage = async () => {
try { try {
const platformFactory = createPlatformFactory()
await platformFactory.createUserInfo().checkSession().catch(e => console.error(e))
// 选择图片选择完成后会自动触发loading状态 // 选择图片选择完成后会自动触发loading状态
const task_id = await sdk.chooseAndGenerateImage({ const task_id = await sdk.chooseAndGenerateImage({
onImageSelected: () => { onImageSelected: () => {
@ -30,7 +34,7 @@ export default function Index() {
setState({ step: 'loading', error: null }) setState({ step: 'loading', error: null })
} }
}) })
// 等待生成完成 // 等待生成完成
await new Promise((resolve) => setTimeout(resolve, 5000)) await new Promise((resolve) => setTimeout(resolve, 5000))
const urls = await sdk.getTaskStatus(task_id) const urls = await sdk.getTaskStatus(task_id)
@ -39,7 +43,7 @@ export default function Index() {
navigateTo({ navigateTo({
url: `/pages/result/index?images=${encodeURIComponent(JSON.stringify(urls))}` url: `/pages/result/index?images=${encodeURIComponent(JSON.stringify(urls))}`
}) })
// 重置状态 // 重置状态
setState({ step: 'upload', error: null }) setState({ step: 'upload', error: null })
} catch (error) { } catch (error) {
@ -54,13 +58,13 @@ export default function Index() {
const renderCurrentStep = () => { const renderCurrentStep = () => {
switch (state.step) { switch (state.step) {
case 'upload': case 'upload':
return <UploadButton onUpload={chooseAndGenerateImage} /> return <UploadButton onUpload={chooseAndGenerateImage} />
case 'loading': case 'loading':
return <LoadingOverlay /> return <LoadingOverlay />
case 'error': case 'error':
return <ErrorOverlay onRetry={resetToUpload} /> return <ErrorOverlay onRetry={resetToUpload} />
default: default:
return <UploadButton onUpload={chooseAndGenerateImage} /> return <UploadButton onUpload={chooseAndGenerateImage} />
} }
} }

View File

@ -77,6 +77,11 @@ const ResultPage: React.FC = memo(() => {
} }
} }
// 再来一张
const handleRegenerate = () => {
navigateBack()
}
if (!images.length) { if (!images.length) {
return null return null
} }
@ -96,7 +101,7 @@ const ResultPage: React.FC = memo(() => {
<Image className="result-image" src={images[0]} mode="aspectFit" /> <Image className="result-image" src={images[0]} mode="aspectFit" />
</View> </View>
</View> </View>
<DownloadSection onDownload={handleDownloadImages} loading={adLoading} /> <DownloadSection onDownload={handleDownloadImages} onRegenerate={handleRegenerate} loading={adLoading} />
</View> </View>
) )
}) })

View File

@ -123,4 +123,44 @@ export abstract class RewardedVideoAd {
* @returns Promise<void> Promise * @returns Promise<void> Promise
*/ */
abstract preload(): Promise<void>; abstract preload(): Promise<void>;
}
/**
*
*/
export interface IUserInfo {
avatarUrl: string;
city: string;
country: string;
gender: number;
language: string;
nickName: string;
province: string;
}
export interface IUserProfile {
userInfo: IUserInfo;
cloudId: string;
encryptedData: string;
errMsg: string;
iv: string;
rawData: string;
signature: string;
}
export interface ILogin {
anonymousCode: string;
code: string;
errMsg: string;
isLogin: boolean;
}
export interface ICheckSession {
errMsg: string;
}
export abstract class UserInfo {
abstract getUserProfile(): Promise<IUserProfile>;
abstract login(): Promise<ILogin>;
abstract checkSession(): Promise<ICheckSession>;
} }

View File

@ -1,5 +1,5 @@
import { RewardedVideoAdTT } from "./tt"; import { RewardedVideoAdTT, UserInfoTT } from "./tt";
import { RewardedVideoAd, RewardedVideoAdOptions } from "./core"; import { RewardedVideoAd, RewardedVideoAdOptions, UserInfo } from "./core";
/** /**
* *
@ -54,6 +54,24 @@ export class PlatformFactory {
} }
} }
createUserInfo(): UserInfo {
switch (this.platform) {
case 'tt':
return new UserInfoTT();
case 'weapp':
throw new Error(`微信小程序平台暂未实现`);
case 'alipay':
throw new Error(`支付宝小程序平台暂未实现`);
case 'swan':
throw new Error(`百度智能小程序平台暂未实现`);
case 'qq':
throw new Error(`QQ 小程序平台暂未实现`);
default:
throw new Error(`不支持的平台类型: ${this.platform}`);
}
}
/** /**
* *
* @returns Platform * @returns Platform

View File

@ -3,7 +3,11 @@ import {
RewardedVideoCloseCb, RewardedVideoCloseCb,
RewardedVideoErrorCb, RewardedVideoErrorCb,
RewardedVideoLoadCb, RewardedVideoLoadCb,
RewardedVideoAdOptions RewardedVideoAdOptions,
UserInfo,
IUserInfo,
IUserProfile,
ICheckSession
} from "./core"; } from "./core";
/** /**
@ -194,4 +198,47 @@ export class RewardedVideoAdTT extends RewardedVideoAd {
getNativeAd(): any { getNativeAd(): any {
return this.ad; return this.ad;
} }
}
export class UserInfoTT extends UserInfo {
getUserProfile(): Promise<IUserProfile> {
return new Promise((resolve, reject) => {
tt.getUserProfile({
success: (res: any) => {
console.log({ res })
resolve(res)
},
fail: (res: any) => {
console.log({ res })
reject(res)
}
})
})
}
login(): Promise<any> {
return new Promise((resolve, reject) => {
tt.login({
success: (res: any) => {
resolve(res)
},
fail: (res: any) => {
reject(res)
}
})
})
}
checkSession(): Promise<ICheckSession> {
return new Promise((resolve, reject) => {
tt.checkSession({
success: (res: any) => {
resolve(res)
},
fail: (res: any) => {
reject(res)
}
})
})
}
} }