feat: refine tiangong flows and integrate profile avatars

This commit is contained in:
zhouzhibo committed 2026-10-17 22:44:09 +08:00
1 parent 4608dbd5cf
commit 5be2ca6b50
65 files changed
+2099 -532

No files matched your search

+33 -74
View File
@@ -1,82 +1,41 @@
import { useEffect, useState } from 'react'
import { useState } from 'react'
import Taro from '@tarojs/taro'
import { Button, Image, Text, View } from '@tarojs/components'
import avatarAsset from '../../assets/fortune/avatar.jpg'
import fortuneAsset from '../../assets/tiangong/fortune.png'
import longevityAsset from '../../assets/tiangong/longevity.png'
import matchmakerAsset from '../../assets/tiangong/matchmaker.png'
import wealthAsset from '../../assets/tiangong/wealth.png'
import wenchangAsset from '../../assets/tiangong/wenchang.png'
import { api, TiangongItem } from '../../services/request'
import { AppTabBar } from '../../components/AppTabBar'
import { TiangongHeader, TiangongMockNote, TiangongModal } from '../../components/TiangongUI'
import { deityCatalog, tiangongMock } from '../../services/tiangong'
import { useTiangongSnapshot } from '../../hooks/useTiangongSnapshot'
import { DeityId } from '../../utils/tiangongMock'
import './index.scss'
const mockTiangong: TiangongItem[] = [
{ id: 'wealth', name: '财神', imageUrl: wealthAsset, unlocked: true },
{ id: 'fortune', name: '禄星', imageUrl: fortuneAsset, unlocked: false },
{ id: 'longevity', name: '寿星', imageUrl: longevityAsset, unlocked: false },
{ id: 'wenchang', name: '文昌星', imageUrl: wenchangAsset, unlocked: false },
{ id: 'matchmaker', name: '月老', imageUrl: matchmakerAsset, unlocked: false }
]
export default function FortunePage() {
const [items, setItems] = useState<TiangongItem[]>(mockTiangong)
useEffect(() => {
api.tiangong()
.then(setItems)
.catch(() => setItems(mockTiangong))
}, [])
const go = (url: string) => Taro.redirectTo({ url })
return (
<View className='tiangong-page'>
<View className='tiangong-content'>
<Text className='tiangong-title'>天宫</Text>
<Text className='tiangong-subtitle'>供奉神祇,收集属于你的福运</Text>
<View className='deity-grid'>
{items.map((item) => (
<Button
className={item.unlocked ? 'deity-card unlocked' : 'deity-card locked'}
key={item.id}
onClick={() => {
if (item.unlocked) {
Taro.navigateTo({ url: `/pages/tiangong-detail/index?id=${item.id}` })
return
}
Taro.showToast({
title: `${item.name}暂未解锁`,
icon: 'none'
})
}}
>
<Image className='deity-image' src={item.imageUrl} mode='aspectFit' />
<View className='deity-name'>
<Text>{item.name}</Text>
</View>
</Button>
))}
</View>
</View>
<View className='tiangong-tabbar'>
<Button className='tabbar-item' onClick={() => go('/pages/home/index')}>
<View className='home-tab-icon' />
<Text>首页</Text>
const snapshot = useTiangongSnapshot()
const items = deityCatalog.map(item => ({ ...item, ...snapshot.deities[item.id] }))
const [selected, setSelected] = useState<DeityId | null>(null)
const [settings, setSettings] = useState(false)
const balance = snapshot.balance
const open = (id: DeityId, invite = false) => Taro.navigateTo({ url: `/pages/tiangong-detail/index?id=${id}${invite ? '&mode=invite' : ''}` })
return <View className='tiangong-page'>
<TiangongHeader title='天宫' /><TiangongMockNote />
<View className='deity-grid'>
{items.map(item => <View className='deity-card-wrap' key={item.id}>
<Button className={`deity-card ${item.unlocked ? 'unlocked' : 'locked'}`} aria-label={`${item.name},${item.unlocked ? '已恭请' : '未恭请'}`} onClick={() => item.unlocked ? open(item.id) : setSelected(selected === item.id ? null : item.id)}>
<Image className='deity-image' src={item.icon} mode='aspectFill' /><Text className='deity-name'>{item.name}</Text>
</Button>
<Button
className='center-tab active'
onClick={() => Taro.showToast({ title: '天宫', icon: 'none' })}
>
<Image className='center-avatar' src={avatarAsset} mode='aspectFill' />
<Text>天宫</Text>
</Button>
<Button className='tabbar-item' onClick={() => go('/pages/my/index')}>
<View className='mine-tab-icon' />
<Text>我的</Text>
</Button>
</View>
{selected === item.id && !item.unlocked && <View className='deity-invite-prompt'>
<Button className='deity-prompt-close' aria-label='关闭恭请提示' onClick={() => setSelected(null)}>×</Button>
<Text>您还未恭请此神</Text><Text>是否恭请</Text>
<Button className='deity-invite-button' onClick={() => { setSelected(null); open(item.id, true) }}>恭请</Button>
</View>}
</View>)}
</View>
)
<Button className='tg-demo-settings' onClick={() => setSettings(true)}>Mock 演示设置</Button>
{settings && <TiangongModal title='Mock 演示设置' onClose={() => setSettings(false)} variant='plain'>
<Text className='tg-modal-title'>Mock 演示设置</Text><Text className='tg-modal-copy'>当前模拟爱心:{balance}</Text>
<Text className='tg-modal-caption'>仅切换演示余额,不是充值。刷新网页或重新启动小程序后恢复初始样例。</Text>
<View className='tg-modal-actions'>{([0, 100] as const).map(value => <Button className='tg-button' key={value} onClick={() => tiangongMock.setDemoBalance(value)}>设为 {value}</Button>)}</View>
<View className='tg-modal-actions'><Button className='tg-button' onClick={() => { tiangongMock.reset(); setSelected(null); setSettings(false) }}>重置全部演示</Button></View>
</TiangongModal>}
<AppTabBar active='fortune' />
</View>
}