feat(user): 新增收货地址管理及售后申请页面

- 新增地址类型定义,增强前端地址数据结构
- 新增地址编辑页面,支持地址智能识别和定位选点功能
- 地址编辑支持省市区选择及默认地址设置
- 新增地址列表页面,支持地址展示、删除、编辑和选择功能
- 实现售后申请页面,支持选择售后类型和退款原因
- 售后申请支持商品选择、退款金额计算和凭证上传
- 新增售后详情页面,支持售后状态展示及申请取消
- 优化页面加载和用户交互体验,增加错误提示和权限处理
This commit is contained in:
2026-07-01 12:11:56 +08:00
parent bf6ed504cc
commit 1fa58040f3
636 changed files with 58878 additions and 716 deletions

View File

@@ -8,7 +8,7 @@ import { useScrollHeight } from '@/hooks/useScrollHeight'
import { getMyAvailableCoupons } from '@/api/shop/shopUserCoupon'
import AddressCard from '@/components/business/AddressCard'
import CouponCard from '@/components/business/CouponCard'
import { createOrder, type WxPayResult } from '@/api/shop/shopOrder'
import { createOrder } from '@/api/shop/shopOrder'
import type { ShopOrder } from '@/api/shop/shopOrder/model'
import { listShopGoods } from '@/api/shop/shopGoods'
import type { ShopGoods } from '@/api/shop/shopGoods/model'
@@ -124,8 +124,7 @@ const CheckoutPage: React.FC = () => {
const [rushBuyProducts, setRushBuyProducts] = useState<ShopGoods[]>([])
// 支付方式相关状态
// payType: 1=微信支付, 0=货到付款(默认)
const [payType, setPayType] = useState<number>(0)
const [payType, setPayType] = useState<number>(0) // 0: 货到付款
// 从本地存储获取立即购买的数据
const buyNowItems = useMemo(() => {
@@ -217,7 +216,7 @@ const CheckoutPage: React.FC = () => {
}
}
// 加载凑单推荐商品
// 选择地址
const handleSelectAddress = () => {
Taro.navigateTo({
url: '/pages/user/address-list?from=checkout',
@@ -271,21 +270,18 @@ const CheckoutPage: React.FC = () => {
const res = await createOrder(orderParams)
// 清理购物车数据(无论哪种支付方式都需清理)
// 货到付款 - 只需创建订单,无需调用支付接口
console.log('订单创建成功', res)
// 清理数据
if (fromBuyNow) {
Taro.removeStorageSync('buy_now')
} else {
await removeSelected()
}
// 根据支付方式处理
if (payType === 1) {
// 微信支付 - res 是 WxPayResult
await handleWxPay(res as WxPayResult)
}
// 货到付款(payType=0)无需在线支付,直接下单成功
Taro.showToast({ title: payType === 0 ? '下单成功' : '支付成功', icon: 'success' })
// 显示成功提示
Taro.showToast({ title: '订单已提交', icon: 'success' })
setTimeout(() => {
Taro.switchTab({ url: '/pages/order/list' })
}, 1500)
@@ -296,25 +292,6 @@ const CheckoutPage: React.FC = () => {
}
}
// 微信支付
const handleWxPay = async (payData: WxPayResult & { paid?: string }) => {
// 后端返回已支付(回调丢失自动修复场景)
if (payData.paid === 'true') {
return // 直接视为支付成功
}
return new Promise<void>((resolve, reject) => {
Taro.requestPayment({
timeStamp: payData.timeStamp,
nonceStr: payData.nonceStr,
package: payData.package,
signType: payData.signType,
paySign: payData.paySign,
success: () => resolve(),
fail: (err) => reject(new Error('支付取消'))
})
})
}
// 安全的数字格式化
const formatPrice = (price: number | string): string => {
const num = typeof price === 'string' ? parseFloat(price) : price
@@ -462,49 +439,23 @@ const CheckoutPage: React.FC = () => {
<View className='bg-white rounded-lg mt-3 p-3'>
<Text className='text-sm font-medium text-gray-800 mb-3 block'></Text>
{/* 微信支付 */}
{/*<View*/}
{/* className='flex items-center py-3 px-2 rounded-lg mb-2'*/}
{/* style={{ backgroundColor: payType === 1 ? '#f0fdf4' : '#f9fafb' }}*/}
{/* onClick={() => setPayType(1)}*/}
{/*>*/}
{/* <View className='w-8 h-8 rounded-full bg-green-50 flex items-center justify-center mr-3'>*/}
{/* <Text className='text-sm'>微</Text>*/}
{/* </View>*/}
{/* <View className='flex-1'>*/}
{/* <Text className='text-sm text-gray-800 block'>微信支付</Text>*/}
{/* <Text className='text-xs text-gray-400 block'>推荐使用</Text>*/}
{/* </View>*/}
{/* <View*/}
{/* className='w-5 h-5 rounded-full border-2 flex items-center justify-center'*/}
{/* style={{ borderColor: payType === 1 ? '#22c55e' : '#d1d5db' }}*/}
{/* >*/}
{/* {payType === 1 && (*/}
{/* <View className='w-2 h-2 rounded-full' style={{ backgroundColor: '#22c55e' }} />*/}
{/* )}*/}
{/* </View>*/}
{/*</View>*/}
{/* 货到付款 */}
<View
className='flex items-center py-3 px-2 rounded-lg'
style={{ backgroundColor: payType === 0 ? '#f0fdf4' : '#f9fafb' }}
onClick={() => setPayType(0)}
style={{ backgroundColor: '#f0fdf4' }}
>
<View className='w-8 h-8 rounded-full bg-blue-50 flex items-center justify-center mr-3'>
<Text className='text-sm'></Text>
</View>
<View className='flex-1'>
<Text className='text-sm text-gray-800 block'></Text>
<Text className='text-xs text-gray-400 block'></Text>
<Text className='text-xs text-gray-400 block'></Text>
</View>
<View
className='w-5 h-5 rounded-full border-2 flex items-center justify-center'
style={{ borderColor: payType === 0 ? '#22c55e' : '#d1d5db' }}
style={{ borderColor: '#22c55e' }}
>
{payType === 0 && (
<View className='w-2 h-2 rounded-full' style={{ backgroundColor: '#22c55e' }} />
)}
<View className='w-2 h-2 rounded-full' style={{ backgroundColor: '#22c55e' }} />
</View>
</View>
</View>