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,137 @@
import React from 'react'
import { View, Text, Image } from '@tarojs/components'
import Taro from '@tarojs/taro'
import type { ShopOrder } from '@/api/shop/shopOrder/model'
import Price from '../Price'
interface OrderCardProps {
order: ShopOrder
onClick?: () => void
}
/** 综合 payStatus + deliveryStatus + orderStatus 计算列表卡片状态文案 */
const getCardStatus = (order: ShopOrder): { text: string; color: string } => {
const { payStatus, deliveryStatus, orderStatus } = order
if (orderStatus === 2) return { text: '已取消', color: '#999' }
if (orderStatus === 3) return { text: '取消中', color: '#ff7d00' }
if (orderStatus === 6) return { text: '已退款', color: '#999' }
if (orderStatus === 4 || orderStatus === 7) return { text: '退款申请中', color: '#ee0a24' }
if (orderStatus === 5) return { text: '退款被拒绝', color: '#ee0a24' }
if (!payStatus) return { text: '待付款', color: '#ee0a24' }
if (deliveryStatus === 10) return { text: '待发货', color: '#4b9cf5' }
if (deliveryStatus === 20 || deliveryStatus === 30) return { text: '待收货', color: '#4b9cf5' }
if (orderStatus === 1) return { text: '已完成', color: '#0e932e' }
return { text: '已付款', color: '#0e932e' }
}
const OrderCard: React.FC<OrderCardProps> = ({ order, onClick }) => {
const handleClick = () => {
if (onClick) {
onClick()
return
}
Taro.navigateTo({ url: `/pages/order/detail?id=${order.orderId}` })
}
const { text: statusText, color: statusColor } = getCardStatus(order)
// 获取首个商品图片作为封面,或显示默认占位
const coverImage = order.orderGoods?.[0]?.image || ''
const hasGoods = (order.orderGoods?.length || 0) > 0
return (
<View className='bg-white rounded-lg p-3 mb-3 shadow-sm' onClick={handleClick}>
{/* 头部:订单号 + 状态 */}
<View className='flex justify-between items-center mb-3'>
<Text className='text-sm font-medium text-gray-800'>
: {order.orderNo}
</Text>
<Text className='text-sm font-medium' style={{ color: statusColor }}>
{statusText}
</Text>
</View>
{/* 商品信息区域 */}
{hasGoods ? (
<View className='mb-3'>
{order.orderGoods?.map((item, idx) => (
<View key={idx} className='flex flex-row items-start mb-2 last:mb-0'>
{/* 商品缩略图 */}
<View className='w-16 h-16 rounded-md bg-gray-100 flex-shrink-0 overflow-hidden'>
{item.image ? (
<Image
className='w-full h-full'
src={item.image}
mode='aspectFill'
/>
) : (
<View className='w-full h-full flex items-center justify-center'>
<Text className='text-xs text-gray-400'></Text>
</View>
)}
</View>
{/* 商品信息 */}
<View className='flex-1 ml-2 flex flex-col justify-between h-16'>
<View>
<Text className='text-sm text-gray-800 font-medium line-clamp-1'>
{item.goodsName || '未知商品'}
</Text>
{item.spec ? (
<Text className='text-xs text-gray-500 mt-1'>
: {item.spec}
</Text>
) : null}
</View>
<View className='flex justify-between items-center'>
<Price price={item.price || '0'} size='small' />
<Text className='text-xs text-gray-500'>
x{item.totalNum || 1}
</Text>
</View>
</View>
</View>
))}
</View>
) : (
/* 无商品信息时的 fallback */
<View className='flex flex-row items-start mb-3'>
<View className='w-16 h-16 rounded-md bg-gray-100 flex-shrink-0 flex items-center justify-center overflow-hidden'>
{coverImage ? (
<Image className='w-full h-full' src={coverImage} mode='aspectFill' />
) : (
<Text className='text-xs text-gray-400'></Text>
)}
</View>
<View className='flex-1 ml-2 flex flex-col justify-center h-16'>
<Text className='text-sm text-gray-800 font-medium line-clamp-2'>
{order.title || '订单商品'}
</Text>
<Text className='text-xs text-gray-500 mt-1'>
{order.totalNum || 0}
</Text>
</View>
</View>
)}
{/* 底部:日期 + 金额汇总 */}
<View className='flex justify-between items-center pt-2 border-t border-gray-50'>
<Text className='text-xs text-gray-500'>
{order.createTime?.slice(0, 10)}
</Text>
<View className='flex items-center'>
<Text className='text-xs text-gray-500 mr-1'>
{order.totalNum || order.orderGoods?.reduce((sum, g) => sum + (g.totalNum || 1), 0) || 0}
</Text>
<Text className='text-xs text-gray-500 mr-1'></Text>
<Price price={order.payPrice || order.totalPrice || '0'} size='small' />
</View>
</View>
</View>
)
}
export default OrderCard