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>) {
useLaunch(() => {
console.log('App launched.')
})
// children 是将要会渲染的页面

View File

@ -42,6 +42,27 @@
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 {
color: #fff;
font-size: 24rpx;

View File

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

View File

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

View File

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

View File

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

View File

@ -123,4 +123,44 @@ export abstract class RewardedVideoAd {
* @returns Promise<void> Promise
*/
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 { RewardedVideoAd, RewardedVideoAdOptions } from "./core";
import { RewardedVideoAdTT, UserInfoTT } from "./tt";
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

View File

@ -3,7 +3,11 @@ import {
RewardedVideoCloseCb,
RewardedVideoErrorCb,
RewardedVideoLoadCb,
RewardedVideoAdOptions
RewardedVideoAdOptions,
UserInfo,
IUserInfo,
IUserProfile,
ICheckSession
} from "./core";
/**
@ -194,4 +198,47 @@ export class RewardedVideoAdTT extends RewardedVideoAd {
getNativeAd(): any {
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)
}
})
})
}
}