- 将获取 STS Token 的接口地址由 paopao-api 改为 guilixu-api - 更新图片上传接口地址为新的 guilixu-api 域名 - 修改用户推广页面中邀请码链接和二维码接口的域名 - 更改注册页微信登录接口请求的域名为 guilixu-api
295 lines
10 KiB
TypeScript
295 lines
10 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
||
import { View, Text, Input, ScrollView } from '@tarojs/components'
|
||
import Taro from '@tarojs/taro'
|
||
import { useUser } from '@/hooks/useUser'
|
||
import { useRequest } from '@/hooks/useRequest'
|
||
import { useScrollHeight } from '@/hooks/useScrollHeight'
|
||
import { useRechargeCode, pageRechargeCodeRecords } from '@/api/shop/shopRechargeCode'
|
||
import type { ShopRechargeCode } from '@/api/shop/shopRechargeCode/model'
|
||
import { getUserBalance } from '@/api/system/user'
|
||
|
||
definePageConfig({
|
||
navigationBarTitleText: '兑换码充值',
|
||
})
|
||
|
||
const RedeemPage: React.FC = () => {
|
||
const { isLoggedIn } = useUser()
|
||
const maxPopupHeight = useScrollHeight(0)
|
||
|
||
// 兑换码输入
|
||
const [code, setCode] = useState('')
|
||
const [submitting, setSubmitting] = useState(false)
|
||
const [redeemSuccess, setRedeemSuccess] = useState(false)
|
||
const [redeemAmount, setRedeemAmount] = useState('')
|
||
|
||
// 兑换记录
|
||
const [records, setRecords] = useState<ShopRechargeCode[]>([])
|
||
const [loadingRecords, setLoadingRecords] = useState(false)
|
||
const [page, setPage] = useState(1)
|
||
const [hasMore, setHasMore] = useState(true)
|
||
const [loadingMore, setLoadingMore] = useState(false)
|
||
|
||
// 兑换请求
|
||
const { run: runUseCode } = useRequest(useRechargeCode, { manual: true })
|
||
|
||
// 加载兑换记录
|
||
const loadRecords = async (isLoadMore = false) => {
|
||
if (isLoadMore) {
|
||
if (loadingMore || !hasMore) return
|
||
setLoadingMore(true)
|
||
} else {
|
||
setLoadingRecords(true)
|
||
setPage(1)
|
||
}
|
||
|
||
try {
|
||
const res = await pageRechargeCodeRecords({ page: isLoadMore ? page + 1 : 1, limit: 10 })
|
||
const newRecords = res?.records || []
|
||
|
||
if (isLoadMore) {
|
||
setRecords(prev => [...prev, ...newRecords])
|
||
setPage(prev => prev + 1)
|
||
} else {
|
||
setRecords(newRecords)
|
||
}
|
||
|
||
setHasMore(newRecords.length >= 10)
|
||
} catch (err) {
|
||
console.error('加载兑换记录失败', err)
|
||
} finally {
|
||
setLoadingRecords(false)
|
||
setLoadingMore(false)
|
||
}
|
||
}
|
||
|
||
// 初始化加载
|
||
useEffect(() => {
|
||
if (isLoggedIn) {
|
||
loadRecords()
|
||
}
|
||
}, [isLoggedIn])
|
||
|
||
// 兑换
|
||
const handleRedeem = async () => {
|
||
const trimCode = code.trim().toUpperCase()
|
||
if (!trimCode) {
|
||
Taro.showToast({ title: '请输入兑换码', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
setSubmitting(true)
|
||
try {
|
||
const res = await runUseCode(trimCode)
|
||
setRedeemSuccess(true)
|
||
setRedeemAmount(res?.amount || '0')
|
||
Taro.showToast({ title: '兑换成功', icon: 'success' })
|
||
|
||
// 刷新余额(通过事件通知)
|
||
const pages = Taro.getCurrentPages()
|
||
const userPage = pages.find(p => p.route === 'pages/user/wallet')
|
||
if (userPage) {
|
||
userPage.onShow()
|
||
}
|
||
|
||
// 刷新记录
|
||
await loadRecords()
|
||
} catch (err: any) {
|
||
Taro.showToast({ title: err.message || '兑换失败,兑换码无效或已过期', icon: 'none' })
|
||
} finally {
|
||
setSubmitting(false)
|
||
}
|
||
}
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
setCode('')
|
||
setRedeemSuccess(false)
|
||
setRedeemAmount('')
|
||
}
|
||
|
||
// 复制兑换码
|
||
const copyCode = (codeStr: string) => {
|
||
Taro.setClipboardData({
|
||
data: codeStr,
|
||
success: () => {
|
||
Taro.showToast({ title: '已复制', icon: 'success' })
|
||
}
|
||
})
|
||
}
|
||
|
||
// 滚动到底部加载更多
|
||
const handleScrollToLower = () => {
|
||
if (hasMore && !loadingMore) {
|
||
loadRecords(true)
|
||
}
|
||
}
|
||
|
||
// 状态文本
|
||
const getStatusText = (status: number) => {
|
||
const map: Record<number, string> = { 0: '未使用', 1: '已使用', 2: '已过期' }
|
||
return map[status] || '未知'
|
||
}
|
||
|
||
// 状态颜色
|
||
const getStatusColor = (status: number) => {
|
||
const map: Record<number, string> = { 0: 'text-blue-500', 1: 'text-green-500', 2: 'text-gray-400' }
|
||
return map[status] || 'text-gray-400'
|
||
}
|
||
|
||
return (
|
||
<View className='min-h-screen bg-gray-50 flex flex-col'>
|
||
<ScrollView
|
||
scrollY
|
||
className='flex-1'
|
||
onScrollToLower={handleScrollToLower}
|
||
lowerThreshold={100}
|
||
>
|
||
<View className='p-3'>
|
||
{/* 兑换说明卡片 */}
|
||
<View className='bg-white rounded-xl p-4 mb-3'>
|
||
<View className='flex items-start gap-3'>
|
||
<Text className='text-2xl'>🎫</Text>
|
||
<View className='flex-1'>
|
||
<Text className='text-sm font-medium text-gray-800 block'>兑换说明</Text>
|
||
<Text className='text-xs text-gray-500 block mt-1'>
|
||
请输入管理员提供的兑换码,每个兑换码只能使用一次,兑换成功后余额将自动到账。
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 兑换表单 */}
|
||
{!redeemSuccess ? (
|
||
<View className='bg-white rounded-xl p-4'>
|
||
<Text className='text-sm font-medium text-gray-800 mb-3 block'>兑换码充值</Text>
|
||
|
||
<View className='bg-gray-50 rounded-lg px-4 py-3 mb-4'>
|
||
<Input
|
||
className='text-sm'
|
||
placeholder='请输入兑换码'
|
||
value={code}
|
||
onInput={(e) => setCode(e.detail.value)}
|
||
maxlength={32}
|
||
/>
|
||
</View>
|
||
|
||
<View
|
||
className={`py-3 px-4 rounded-lg text-center text-sm font-medium ${
|
||
code.trim() && !submitting
|
||
? 'bg-green-600 text-white'
|
||
: 'bg-gray-200 text-gray-400'
|
||
}`}
|
||
onClick={handleRedeem}
|
||
>
|
||
{submitting ? '兑换中...' : '立即兑换'}
|
||
</View>
|
||
</View>
|
||
) : (
|
||
/* 兑换成功结果 */
|
||
<View className='bg-white rounded-xl p-4'>
|
||
<View className='text-center py-6'>
|
||
<Text className='text-5xl block mb-4'>🎉</Text>
|
||
<Text className='text-lg font-medium text-gray-800 block'>兑换成功!</Text>
|
||
<Text className='text-sm text-gray-500 block mt-2'>
|
||
已成功充值 ¥{redeemAmount},余额已到账
|
||
</Text>
|
||
</View>
|
||
|
||
<View className='flex gap-3'>
|
||
<View
|
||
className='flex-1 py-3 px-4 rounded-lg text-center text-sm font-medium bg-gray-100 text-gray-700'
|
||
onClick={resetForm}
|
||
>
|
||
继续兑换
|
||
</View>
|
||
<View
|
||
className='flex-1 py-3 px-4 rounded-lg text-center text-sm font-medium bg-green-600 text-white'
|
||
onClick={() => Taro.navigateTo({ url: '/pages/user/wallet' })}
|
||
>
|
||
查看余额
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)}
|
||
|
||
{/* 兑换记录 */}
|
||
<View className='bg-white rounded-xl mt-3 overflow-hidden'>
|
||
<View className='p-4 border-b border-gray-100 flex justify-between items-center'>
|
||
<Text className='text-sm font-medium text-gray-800'>兑换记录</Text>
|
||
<Text
|
||
className='text-xs text-gray-400'
|
||
onClick={() => loadRecords()}
|
||
>
|
||
刷新 🔄
|
||
</Text>
|
||
</View>
|
||
|
||
{loadingRecords ? (
|
||
<View className='py-10 text-center'>
|
||
<Text className='text-sm text-gray-400'>加载中...</Text>
|
||
</View>
|
||
) : records.length === 0 ? (
|
||
<View className='py-10 text-center'>
|
||
<Text className='text-4xl block mb-3'>📋</Text>
|
||
<Text className='text-sm text-gray-400'>暂无兑换记录</Text>
|
||
</View>
|
||
) : (
|
||
<View>
|
||
{records.map((record, idx) => (
|
||
<View
|
||
key={record.id}
|
||
className={`p-4 flex justify-between items-center border-b border-gray-50 ${idx === records.length - 1 ? 'border-b-0' : ''}`}
|
||
>
|
||
<View className='flex-1'>
|
||
<View className='flex items-center gap-2'>
|
||
<Text className={`text-xs ${getStatusColor(record.status)}`}>
|
||
{getStatusText(record.status)}
|
||
</Text>
|
||
{record.status === 1 && (
|
||
<Text className='text-xs text-green-500'>
|
||
+¥{Number(record.amount).toFixed(2)}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
<Text
|
||
className='text-xs text-gray-400 mt-1 block'
|
||
onClick={() => copyCode(record.code)}
|
||
>
|
||
码: {record.code} 📋
|
||
</Text>
|
||
<Text className='text-xs text-gray-300 mt-1 block'>
|
||
{record.usedAt || record.createTime}
|
||
</Text>
|
||
</View>
|
||
{record.status === 1 && (
|
||
<Text className='text-base font-bold text-green-500'>
|
||
+¥{Number(record.amount).toFixed(2)}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
))}
|
||
|
||
{loadingMore && (
|
||
<View className='py-3 text-center'>
|
||
<Text className='text-xs text-gray-400'>加载更多...</Text>
|
||
</View>
|
||
)}
|
||
|
||
{!hasMore && records.length > 0 && (
|
||
<View className='py-3 text-center'>
|
||
<Text className='text-xs text-gray-400'>没有更多了</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
)}
|
||
</View>
|
||
|
||
<View className='h-6' />
|
||
</View>
|
||
</ScrollView>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
export default RedeemPage
|