Files
xinlong-shop-taro/src_bak/pages/user/recharge.tsx
赵忠林 1fa58040f3 feat(user): 新增收货地址管理及售后申请页面
- 新增地址类型定义,增强前端地址数据结构
- 新增地址编辑页面,支持地址智能识别和定位选点功能
- 地址编辑支持省市区选择及默认地址设置
- 新增地址列表页面,支持地址展示、删除、编辑和选择功能
- 实现售后申请页面,支持选择售后类型和退款原因
- 售后申请支持商品选择、退款金额计算和凭证上传
- 新增售后详情页面,支持售后状态展示及申请取消
- 优化页面加载和用户交互体验,增加错误提示和权限处理
2026-07-01 12:11:56 +08:00

214 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react'
import { View, Text, Input, ScrollView } from '@tarojs/components'
import Taro, { useDidShow } from '@tarojs/taro'
import { useRequest } from '@/hooks/useRequest'
import { createUserRecharge, payUserRecharge, type UserRecharge } from '@/api/shop/shopRecharge'
import { getUserBalance } from '@/api/system/user/balance'
definePageConfig({
navigationBarTitleText: '充值',
})
const amounts = [10, 20, 50, 100, 200, 500]
const RechargePage: React.FC = () => {
const [selected, setSelected] = useState(0)
const [customAmount, setCustomAmount] = useState('')
const [balance, setBalance] = useState<string>('')
// 获取当前余额
const { run: runGetBalance } = useRequest(getUserBalance, {
manual: true,
onSuccess: (data) => {
setBalance(data.balance)
}
})
// 页面显示时刷新余额
useDidShow(() => {
runGetBalance()
})
// 发起支付
const handlePay = async (rechargeOrder: UserRecharge) => {
try {
const payParams = await payUserRecharge(rechargeOrder.rechargeId!)
if (payParams) {
// 后端返回已支付(回调丢失自动修复场景)
if ((payParams as Record<string, unknown>).paid === 'true') {
Taro.showToast({ title: '充值成功', icon: 'success' })
try {
const balanceData = await getUserBalance()
setBalance(balanceData.balance)
} catch (e) { }
setTimeout(() => {
Taro.navigateBack()
}, 1500)
return
}
// 调用微信支付
await Taro.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
success: async () => {
Taro.showToast({ title: '充值成功', icon: 'success' })
// 立即刷新余额
try {
const balanceData = await getUserBalance()
setBalance(balanceData.balance)
} catch (e) {
// 忽略刷新失败
}
setTimeout(() => {
Taro.navigateBack()
}, 1500)
},
fail: (err) => {
if (err.errMsg.indexOf('cancel') > -1) {
Taro.showToast({ title: '已取消支付', icon: 'none' })
} else {
Taro.showToast({ title: '支付失败', icon: 'none' })
}
}
})
}
} catch (err: unknown) {
const message = err instanceof Error ? err.message : '支付失败'
Taro.showToast({ title: message, icon: 'none' })
}
}
// 创建充值订单
const { run: runCreate, loading: creating } = useRequest(createUserRecharge, {
manual: true,
onSuccess: (data) => {
if (data) {
handlePay(data)
}
},
onError: (err) => {
Taro.showToast({ title: err.message || '创建订单失败', icon: 'none' })
}
})
const handleRecharge = () => {
const amount = customAmount || String(amounts[selected])
if (!amount || Number(amount) <= 0) {
Taro.showToast({ title: '请选择充值金额', icon: 'none' })
return
}
if (Number(amount) < 1) {
Taro.showToast({ title: '充值金额不能小于1元', icon: 'none' })
return
}
if (Number(amount) > 5000) {
Taro.showToast({ title: '单次充值金额不能超过5000元', icon: 'none' })
return
}
// 创建充值订单
runCreate({
amount: amount,
payType: 0, // 微信支付
})
}
return (
<View className="min-h-screen bg-gray-50 flex flex-col">
<ScrollView scrollY className="flex-1">
<View className="p-4">
{/* 当前余额提示 */}
{balance && (
<View className="bg-gradient-to-r from-orange-500 to-orange-600 rounded-xl p-4 mb-3">
<Text className="text-white text-xs opacity-80 block"></Text>
<Text className="text-white text-2xl font-bold block mt-1">¥{parseFloat(balance).toFixed(2)}</Text>
</View>
)}
{/* 充值金额 */}
<View className="bg-white rounded-xl p-4">
<Text className="text-sm font-medium text-gray-800 mb-3 block"></Text>
<View className="grid grid-cols-3 gap-2">
{amounts.map((amount, idx) => (
<View
key={amount}
className={`py-3 rounded-lg text-center border ${
selected === idx && !customAmount
? 'border-orange-500 bg-orange-50'
: 'border-gray-200'
}`}
onClick={() => { setSelected(idx); setCustomAmount('') }}
>
<Text className={`text-base font-medium ${selected === idx && !customAmount ? 'text-orange-600' : 'text-gray-700'}`}>
¥{amount}
</Text>
</View>
))}
</View>
{/* 自定义金额 */}
<View className="mt-3">
<Text className="text-sm text-gray-600 mb-1 block"></Text>
<View className="flex items-center border border-gray-200 rounded-lg px-3 py-2">
<Text className="text-sm text-gray-500 mr-1">¥</Text>
<Input
type="digit"
className="flex-1 text-sm"
placeholder="请输入金额1-5000"
value={customAmount}
onInput={e => setCustomAmount(e.detail.value)}
/>
</View>
</View>
</View>
{/* 支付方式 */}
<View className="bg-white rounded-xl p-4 mt-3">
<Text className="text-sm font-medium text-gray-800 mb-3 block"></Text>
<View className="flex items-center justify-between py-2">
<View className="flex items-center gap-2">
<Text className="text-lg">💳</Text>
<Text className="text-sm text-gray-700"></Text>
</View>
<View className="w-5 h-5 rounded-full border-2 border-orange-500 flex items-center justify-center">
<View className="w-3 h-3 rounded-full bg-orange-500" />
</View>
</View>
</View>
{/* 充值说明 */}
<View className="bg-white rounded-xl p-4 mt-3">
<Text className="text-sm font-medium text-gray-800 mb-2 block"></Text>
<View className="text-xs text-gray-400 leading-5">
<Text className="block mb-1">1. </Text>
<Text className="block mb-1">2. 使</Text>
<Text className="block mb-1">3. </Text>
<Text className="block">4. </Text>
</View>
</View>
</View>
{/* 底部按钮占位 */}
<View style={{ height: '80px' }} />
</ScrollView>
{/* 底部充值按钮 - flex 布局 */}
<View className="bg-white border-t border-gray-200 px-4 py-3" style={{ paddingBottom: '20px' }}>
<View
className="py-3 rounded-full text-center"
style={{ backgroundColor: '#f97316' }}
onClick={handleRecharge}
>
<Text className="text-white font-medium text-sm">
{creating ? '处理中...' : `立即充值 ¥${customAmount || amounts[selected]}`}
</Text>
</View>
</View>
</View>
)
}
export default RechargePage