feat: update agreements and refine miniapp layouts and integrations

This commit is contained in:
zhouzhibo committed 2026-10-21 20:33:50 +08:00
1 parent 38fc4187ea
commit 7871114c50
26 files changed
+2018 -120

No files matched your search

+16 -17
View File
@@ -4,6 +4,8 @@ import { Button, Image, Text, View } from '@tarojs/components'
import { deityCatalog, getDeity, isTiangongDemo, tiangongMock } from '../services/tiangong'
import { DeityId } from '../utils/tiangongMock'
import { useTiangongSnapshot } from '../hooks/useTiangongSnapshot'
import { resolveAssetUrl } from '../services/request'
import { openWechatCustomerService } from '../services/customerService'
import './DeityBinding.scss'
export function BindingGate({ onBind, lock = true }: { onBind: () => void; lock?: boolean }) {
@@ -13,7 +15,7 @@ export function BindingGate({ onBind, lock = true }: { onBind: () => void; lock?
<Text>须绑定神仙</Text><Text>开启你的守护之旅</Text>
<View className='binding-gate-actions'>
<Button className='binding-primary' onClick={onBind}>绑定神仙</Button>
<Button className='binding-outline' onClick={() => Taro.showToast({ title: '企业微信入口待配置', icon: 'none' })}>联系我们</Button>
<Button className='binding-outline' onClick={openWechatCustomerService}>联系我们</Button>
</View>
</View>
</View>
@@ -26,43 +28,40 @@ export function DeityCardDialog({ deityId, mode = 'view', onClose }: { deityId:
const [menu, setMenu] = useState(false)
const [confirmUnbind, setConfirmUnbind] = useState(false)
const [error, setError] = useState('')
const [failedImage, setFailedImage] = useState('')
const working = useRef(false)
if (!isTiangongDemo()) return <View className='binding-dialog-mask' catchMove><View className='binding-picker' role='dialog' aria-label='神仙信息'>
<Text className='binding-confirm-title'>{deity.name}</Text>
<Image className='binding-card-art' src={deity.icon} mode='aspectFit' />
<Text className='binding-picker-note'>{state.unlocked ? `已守护 ${state.days} 天 · 持有记录 ${state.holdingId}` : '请通过 NFC 绑定神仙。此处不模拟建立真实绑定关系。'}</Text>
<Button className='binding-outline binding-picker-cancel' onClick={onClose}>关闭</Button>
</View></View>
const demo = isTiangongDemo()
const art = demo ? deity.icon : resolveAssetUrl(state.iconUrl)
const canShowArt = !!art && failedImage !== art
const perform = (action: () => void) => {
if (working.current) return
if (!demo || working.current) return
working.current = true
try { action(); onClose() } catch (cause) { setError(cause instanceof Error ? cause.message : '模拟操作失败') }
finally { working.current = false }
}
return <View className='binding-dialog-mask' catchMove>
<View className='binding-dialog-stack'>
<View className='binding-gold-card' role='dialog' aria-label={mode === 'bind' ? '确认绑定神仙' : '已绑定神仙详情'}>
<View className='binding-gold-card' role='dialog' aria-label={demo && mode === 'bind' ? '确认绑定神仙' : state.unlocked ? '已绑定神仙详情' : '神仙信息'}>
<View className='binding-card-hook' /><View className='binding-card-corner top' /><View className='binding-card-corner bottom' />
<View className='binding-card-title-row'><Text className='binding-card-spark'>✦</Text><Text className='binding-card-name'>{deity.name}</Text><Text className='binding-card-spark'>✦</Text>
{mode === 'view' && <Button className='binding-more' aria-label='神仙更多操作' onClick={() => setMenu(!menu)}>•••</Button>}
{demo && mode === 'view' && <Button className='binding-more' aria-label='神仙更多操作' onClick={() => setMenu(!menu)}>•••</Button>}
</View>
{menu && <Button className='binding-unbind-menu' onClick={() => { setMenu(false); setConfirmUnbind(true) }}>解绑神仙</Button>}
<View className='binding-art-frame'><Image className='binding-card-art' src={deity.icon} mode='aspectFit' /></View>
{demo && menu && <Button className='binding-unbind-menu' onClick={() => { setMenu(false); setConfirmUnbind(true) }}>解绑神仙</Button>}
<View className='binding-art-frame'>{canShowArt ? <Image className='binding-card-art' src={art} mode='aspectFit' onError={() => setFailedImage(art)} /> : <Text className='binding-card-image-note'>{art ? '图片加载失败' : '图片暂未配置'}</Text>}</View>
<Text className='binding-card-subtitle'>守护神仙 · {deity.name}</Text>
<Text className='binding-card-code'>唯一 ID:DEMO-{deityId.toUpperCase()}-001</Text>
<Text className='binding-card-note'>Mock 原型演示 · 不连接真实 NFC</Text>
{mode === 'bind' ? <>
{demo ? <><Text className='binding-card-code'>唯一 ID:DEMO-{deityId.toUpperCase()}-001</Text><Text className='binding-card-note'>Mock 原型演示 · 不连接真实 NFC</Text></> : state.holdingId ? <Text className='binding-card-code'>持有记录:{state.holdingId}</Text> : null}
{demo && mode === 'bind' ? <>
<Text className='binding-card-warning'>每位神仙只能绑定一个微信号哦</Text>
<Button className='binding-primary binding-card-confirm' onClick={() => perform(() => {
tiangongMock.invite(deityId)
Taro.showToast({ title: '模拟绑定成功', icon: 'none' })
})}>确认绑定</Button>
</> : <View className='binding-guarded'><View className='binding-shield'>✓</View><Text>已守护 <Text className='binding-days'>{state.days}</Text> 天</Text></View>}
</> : state.unlocked ? <View className='binding-guarded'><View className='binding-shield'>✓</View><Text>已守护 <Text className='binding-days'>{state.days}</Text> 天</Text></View> : <Text className='binding-card-note'>请通过 NFC 绑定神仙。</Text>}
{error && <Text className='binding-error'>{error}</Text>}
</View>
<Button className='binding-card-close' aria-label='关闭神仙卡片' onClick={onClose}>×</Button>
</View>
{confirmUnbind && <View className='binding-confirm-mask'>
{demo && confirmUnbind && <View className='binding-confirm-mask'>
<View className='binding-confirm-panel' role='dialog' aria-label='解绑神仙确认'>
<Text className='binding-confirm-title'><Text className='binding-alert'>ⓘ</Text> 确定要解绑吗?</Text>
<View className='binding-confirm-copy'>