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

@@ -0,0 +1,123 @@
import React, { useState, useEffect } from 'react'
import { View, Text } from '@tarojs/components'
import Price from '@/components/common/Price'
interface PayModalProps {
visible: boolean
amount: string
onClose: () => void
onConfirm: (payType: number) => void
}
const PAY_TYPES = [
{ id: 1, name: '微信支付', desc: '推荐使用', icon: 'pay-wechat' },
{ id: 0, name: '货到付款', desc: '使用账户余额', icon: 'pay-balance' },
]
const PayModal: React.FC<PayModalProps> = ({ visible, amount, onClose, onConfirm }) => {
const [selected, setSelected] = useState<number>(1)
const [showOverlay, setShowOverlay] = useState(false)
const [slideUp, setSlideUp] = useState(false)
// 控制动画
useEffect(() => {
if (visible) {
setShowOverlay(true)
setTimeout(() => setSlideUp(true), 10)
} else {
setSlideUp(false)
setTimeout(() => setShowOverlay(false), 300)
}
}, [visible])
const handleClose = () => {
setSlideUp(false)
setTimeout(() => {
setShowOverlay(false)
onClose()
}, 300)
}
const handleConfirm = () => {
handleClose()
onConfirm(selected)
}
if (!showOverlay) return null
return (
<View className='pay-modal-overlay'>
{/* 遮罩层 */}
<View
className={`absolute inset-0 bg-black/50 z-50 ${slideUp ? 'opacity-100' : 'opacity-0'}`}
onClick={handleClose}
/>
{/* 弹窗内容 */}
<View
className={`absolute bottom-0 left-0 right-0 bg-white rounded-t-2xl z-50 ${slideUp ? '' : 'hidden'}`}
>
<View className='p-4'>
{/* 关闭按钮 */}
<View className='flex justify-end mb-2'>
<View
className='w-6 h-6 rounded-full bg-gray-100 flex items-center justify-center'
onClick={handleClose}
>
<Text className='text-gray-400 text-sm'>×</Text>
</View>
</View>
<View className='text-center py-3 border-b border-gray-100'>
<Text className='text-base font-medium'></Text>
<View className='mt-2'>
<Price price={amount} size='large' />
</View>
</View>
<View className='py-3'>
{PAY_TYPES.map((item) => (
<View
key={item.id}
className='flex items-center justify-between py-3 px-2 rounded-lg mb-1'
style={{ backgroundColor: selected === item.id ? '#f0fdf4' : 'transparent' }}
onClick={() => setSelected(item.id)}
>
<View className='flex items-center gap-3'>
<View className='w-8 h-8 rounded-full bg-green-50 flex items-center justify-center'>
<Text className='text-sm'>
{item.id === 1 ? '微' : '余'}
</Text>
</View>
<View>
<Text className='text-sm text-gray-800'>{item.name}</Text>
<Text className='text-xs text-gray-400'>{item.desc}</Text>
</View>
</View>
<View
className={`w-5 h-5 rounded-full border-2 flex items-center justify-center ${
selected === item.id ? 'border-green-500' : 'border-gray-300'
}`}
>
{selected === item.id && (
<View className='w-2 h-2 rounded-full bg-green-500' />
)}
</View>
</View>
))}
</View>
<View
className='w-full py-3 rounded-full text-center text-white text-sm font-medium mt-2'
style={{ backgroundColor: '#0e932e' }}
onClick={handleConfirm}
>
<Text className='text-white'></Text>
</View>
</View>
</View>
</View>
)
}
export default PayModal