Browse Source

商品消费详情,商品退款tab 盒子

master
DU 6 months ago
parent
commit
cc4c84c5d9
  1. 1
      src/app.config.ts
  2. 107
      src/pagesUser/goodsCons/index.scss
  3. 198
      src/pagesUser/goodsCons/index.tsx
  4. 6
      src/pagesUser/goodsOrderDetail/index.config.ts
  5. 138
      src/pagesUser/goodsOrderDetail/index.scss
  6. 199
      src/pagesUser/goodsOrderDetail/index.tsx

1
src/app.config.ts

@ -25,6 +25,7 @@ export default defineAppConfig({ @@ -25,6 +25,7 @@ export default defineAppConfig({
'orderDetail/index', // 订单详情
'unsettledOrders/index', // 未结算账单
'goodsCons/index', // 商品消费
'goodsOrderDetail/index', // 商品消费详情 (已支付,未支付)
'helpCenter/index', // 帮助中心
'agreement/index', // 协议
'editProfile/index', // 编辑资料

107
src/pagesUser/goodsCons/index.scss

@ -152,6 +152,113 @@ @@ -152,6 +152,113 @@
}
}
// 退款订单卡片特殊样式
.refund_card {
// 商品行
.goods_row {
display: flex;
align-items: flex-start;
margin-bottom: 20px;
.goods_img {
width: 208px;
height: 208px;
border-radius: 8px;
flex-shrink: 0;
&.placeholder {
background-color: #F7F8FA;
display: flex;
align-items: center;
justify-content: center;
border: 1PX dashed #DCDEE0;
.placeholder_icon {
width: 80px;
height: 80px;
background-color: #DCDEE0;
border-radius: 4px;
}
}
}
.goods_info_box {
flex: 1;
margin-left: 30px;
display: flex;
flex-direction: column;
.goods_name {
font-size: 30px;
color: #000000;
font-weight: 600;
line-height: 1.4;
margin-bottom: 10px;
}
.refund_amount {
font-size: 30px;
color: #000000;
margin-top: auto;
padding-top: 66px;
}
}
}
// 退款状态栏
.refund_status_bar {
display: flex;
align-items: center;
height: 88px;
background-color: #F7F8FA;
border-radius: 8px;
padding: 0 30px;
gap: 20px;
.refund_status_text {
font-size: 28px;
font-weight: 500;
}
.payment_method {
font-size: 28px;
color: #646566;
}
.refund_amount_text {
font-size: 28px;
color: #FF8A19;
margin-left: auto;
}
.detail_btn_box {
display: flex;
align-items: center;
gap: 5px;
margin-left: 20px;
.detail_text {
font-size: 28px;
color: #646566;
}
}
// 退款成功状态
&.refund_success {
.refund_status_text {
color: #07C160;
}
}
// 退款失败状态
&.refund_failed {
.refund_status_text {
color: #EE0A24;
}
}
}
}
// 到底了提示
.list-end {
text-align: center;

198
src/pagesUser/goodsCons/index.tsx

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
/**
*
*/
import { View, Text, Button } from '@tarojs/components'
import { View, Text, Button, } from '@tarojs/components'
import { useState } from 'react'
import Taro, { useLoad } from '@tarojs/taro'
import { Tabs, Divider } from '@taroify/core'
import { Tabs, Divider,Image } from '@taroify/core'
import { ArrowDown, Arrow } from '@taroify/icons'
import EmptyData from '@/components/EmptyData'
import FilterPopup from '../orderList/components/FilterPopup'
@ -20,6 +20,19 @@ interface GoodsOrderItem { @@ -20,6 +20,19 @@ interface GoodsOrderItem {
amount: number // 消费金额
goodsCount: number // 商品数量
status: 'unpaid' | 'paid' | 'refund' // 未支付 | 已支付 | 退款
// 退款订单特有字段
refundGoods?: RefundGoodsItem[] // 退款商品列表
}
// 退款商品信息接口
interface RefundGoodsItem {
id: string
image: string // 商品图片
name: string // 商品名称
refundAmount: number // 退款金额
refundStatus: 'success' | 'failed' // 退款状态:成功 | 失败
paymentMethod: string // 支付方式
}
const GoodsCons = () => {
@ -61,7 +74,33 @@ const GoodsCons = () => { @@ -61,7 +74,33 @@ const GoodsCons = () => {
orderNo: 'wjekwejrwkjsf1321445256',
amount: 20.00,
goodsCount: 1,
status: 'refund'
status: 'refund',
refundGoods: [
{
id: '1',
image: 'https://img01.yzcdn.cn/vant/cat.jpeg',
name: '美汁源Minute Maid果粒橙 橙汁饮料 1.25l/瓶',
refundAmount: 12.50,
refundStatus: 'success',
paymentMethod: '微信'
},
{
id: '2',
image: '', // 空图片显示占位图
name: '美汁源Minute Maid果粒橙 橙汁饮料 1.25l/瓶',
refundAmount: 12.50,
refundStatus: 'success',
paymentMethod: '支付宝'
},
{
id: '3',
image: 'https://img01.yzcdn.cn/vant/cat.jpeg',
name: '可口可乐 迷你装 200ml/罐',
refundAmount: 3.00,
refundStatus: 'failed',
paymentMethod: '微信'
}
]
}
])
@ -122,7 +161,7 @@ const GoodsCons = () => { @@ -122,7 +161,7 @@ const GoodsCons = () => {
const handleOrderClick = (orderId: string) => {
console.log('订单详情', orderId)
Taro.navigateTo({
url: `/pagesUser/orderDetail/index?orderId=${orderId}`
url: `/pagesUser/goodsOrderDetail/index?orderId=${orderId}&status=${statusTab === 0 ? 'unpaid' : 'paid'}`
})
}
@ -152,6 +191,16 @@ const GoodsCons = () => { @@ -152,6 +191,16 @@ const GoodsCons = () => {
return ''
}
// 获取退款状态文本
const getRefundStatusText = (status: 'success' | 'failed') => {
return status === 'success' ? '退款成功' : '退款失败'
}
// 获取退款状态样式类
const getRefundStatusClass = (status: 'success' | 'failed') => {
return status === 'success' ? 'refund_success' : 'refund_failed'
}
return (
<View className="goods-cons-page">
{/* 状态 Tab:未支付/已支付/退款 + 筛选 */}
@ -174,58 +223,105 @@ const GoodsCons = () => { @@ -174,58 +223,105 @@ const GoodsCons = () => {
<EmptyData />
) : (
<>
{filteredOrders.map(order => (
<View
key={order.id}
className="order-card"
onClick={() => handleOrderClick(order.id)}
>
{/* 标题行:时间 + 状态 */}
<View className="card-header">
<Text className="time">{order.time}</Text>
<View className="status-with-arrow">
<Text className={`status ${getStatusClass(order.status)}`}>
{getStatusText(order.status)}
</Text>
<Arrow size="16px" color={order.status === 'unpaid' ? '#FF3B30' : '#8B8B8B'} />
</View>
</View>
{filteredOrders.map(order => {
// 退款订单:每个退款商品显示为独立卡片
if (order.status === 'refund' && order.refundGoods) {
return order.refundGoods.map((goods) => (
<View
key={`${order.id}-${goods.id}`}
className="order-card refund_card"
onClick={() => handleOrderClick(order.id)}
>
{/* 商品信息 */}
<View className="goods_row">
{/* 商品图片 */}
<Image
width="208rpx"
height="208rpx"
className="goods_img"
src={goods.image}
mode="aspectFill"
placeholder="加载中..."
fallback="加载失败"
/>
{/* 商品名称和退款金额 */}
<View className="goods_info_box">
<Text className="goods_name">{goods.name}</Text>
<Text className="refund_amount">退: ¥ {goods.refundAmount.toFixed(2)}</Text>
</View>
</View>
{/* 虚线分隔 */}
<Divider dashed={true} />
{/* 订单信息 */}
<View className="card-info">
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.storeName}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.address}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.orderNo}</Text>
{/* 退款状态栏 */}
<View className={`refund_status_bar ${getRefundStatusClass(goods.refundStatus)}`}>
<Text className="refund_status_text">{getRefundStatusText(goods.refundStatus)}</Text>
<Text className="payment_method">{goods.paymentMethod}</Text>
<Text className="refund_amount_text">¥ {goods.refundAmount.toFixed(2)}</Text>
<View className="detail_btn_box">
<Text className="detail_text"></Text>
<Arrow size="16px" color="#646566" />
</View>
</View>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.amount.toFixed(2)}</Text>
))
}
// 普通订单:未支付/已支付,每个订单显示为一个卡片
return (
<View
key={order.id}
className="order-card"
onClick={() => handleOrderClick(order.id)}
>
{/* 标题行:时间 + 状态 */}
<View className="card-header">
<Text className="time">{order.time}</Text>
<View className="status-with-arrow">
<Text className={`status ${getStatusClass(order.status)}`}>
{getStatusText(order.status)}
</Text>
<Arrow size="16px" color={order.status === 'unpaid' ? '#FF3B30' : '#8B8B8B'} />
</View>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.goodsCount}</Text>
{/* 虚线分隔 */}
<Divider dashed={true} />
{/* 订单信息 */}
<View className="card-info">
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.storeName}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.address}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.orderNo}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.amount.toFixed(2)}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.goodsCount}</Text>
</View>
</View>
</View>
{/* 未支付订单显示立即支付按钮 */}
{order.status === 'unpaid' && (
<Button className="pay-btn" onClick={(e) => handlePay(order.id, e)}>
</Button>
)}
</View>
))}
{/* 未支付订单显示立即支付按钮 */}
{order.status === 'unpaid' && (
<Button className="pay-btn" onClick={(e) => handlePay(order.id, e)}>
</Button>
)}
</View>
)
})}
{/* 到底了提示 */}
<View className="list-end">

6
src/pagesUser/goodsOrderDetail/index.config.ts

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
export default definePageConfig({
navigationBarTitleText: '商品消费',
navigationBarBackgroundColor: '#fff',
navigationBarTextStyle: 'black',
backgroundColor: '#F2F3F5',
})

138
src/pagesUser/goodsOrderDetail/index.scss

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
.order_detail_page {
min-height: 100vh;
background-color: #F2F3F5;
padding: 30px;
padding-bottom: 150px; // 为底部按钮留出空间
// 商品卡片
.goods_card {
background-color: #FFFFFF;
border-radius: 12px;
padding: 30px;
margin-bottom: 30px;
.goods_item {
display: flex;
align-items: flex-start;
.goods_image {
width: 208px;
height: 208px;
border-radius: 8px;
flex-shrink: 0;
}
.goods_info {
flex: 1;
margin-left: 30px;
display: flex;
flex-direction: column;
.goods_name {
font-size: 30px;
color: #000000;
line-height: 1.4;
margin-bottom: 10px;
}
.price_container {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 66px;
.goods_price {
font-size: 30px;
color: #FF3A24;
font-weight: 500;
}
.refund_btn {
padding: 10px 30px;
background: #F2F3F5;
border-radius: 25px;
.refund_text {
font-size: 25px;
color: #666666;
}
}
}
}
}
}
// 订单信息卡片
.order_info_card {
background-color: #FFFFFF;
border-radius: 12px;
padding: 30px;
.info_list {
display: flex;
flex-direction: column;
gap: 20px;
.info_row {
display: flex;
align-items: flex-start;
.info_label {
width: 180px;
font-size: 28px;
color: #333333;
flex-shrink: 0;
}
.info_value {
flex: 1;
margin-left: 30px;
font-size: 28px;
color: #595959;
text-align: left;
word-break: break-all;
}
}
}
// 虚线分隔
.divider {
height: 1PX;
border-bottom: 1PX dashed #D3D8DE;
margin: 30px 0;
}
// 联系商家
.merchant_contact {
.contact_text {
font-size: 28px;
color: #333333;
}
}
}
// 页脚支付按钮仅未支付订单
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #FFFFFF;
padding: 30px;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
.pay_btn {
width: 100%;
height: 88px;
background: #03C175;
border-radius: 44px;
border: none;
font-weight: 500;
font-size: 33px;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
}
}
}

199
src/pagesUser/goodsOrderDetail/index.tsx

@ -0,0 +1,199 @@ @@ -0,0 +1,199 @@
/**
*
*/
import { View, Text, Button } from '@tarojs/components'
import { useState } from 'react'
import Taro, { useLoad } from '@tarojs/taro'
import { Image } from '@taroify/core'
import './index.scss'
// 商品信息接口
interface GoodsItem {
id: string
image: string // 商品图片
name: string // 商品名称
price: number // 商品价格
canRefund?: boolean // 是否可退款(仅已支付订单)
}
// 订单详情接口
interface OrderDetail {
id: string
status: 'unpaid' | 'paid' // 未支付 | 已支付
goods: GoodsItem[] // 商品列表
storeName: string // 消费门店
address: string // 地址
orderNo: string // 订单号
amount: number // 消费金额
goodsCount: number // 商品数量
merchantPhone: string // 联系商家电话
// 已支付订单额外字段
paymentTime?: string // 支付时间
paymentMethod?: string // 支付方式
}
const OrderDetail = () => {
const [pageType, setPageType] = useState<'unpaid' | 'paid'>('unpaid')
// 模拟订单详情数据
const [orderDetail] = useState<OrderDetail>({
id: '1',
status: 'unpaid',
goods: [
{
id: '1',
image: 'https://img01.yzcdn.cn/vant/cat.jpeg',
name: '美汁源Minute Maid果粒橙 橙汁饮料 1.25l/瓶',
price: 12.50
},
{
id: '2',
image: 'https://img01.yzcdn.cn/vant/cat.jpeg',
name: '可口可乐 迷你装 200ml/罐',
price: 3.00,
canRefund: true
}
],
storeName: 'XXX店',
address: 'XXX市XXXXXXXXXXX市XXXXXXXXXXX市XXXXXXXX',
orderNo: 'wjekwejrwkjsf1321445255',
amount: 15.50,
goodsCount: 2,
merchantPhone: '16688889999',
paymentTime: '2025-01-01 10:00:00',
paymentMethod: '微信支付'
})
useLoad(() => {
console.log('订单详情页面加载')
// 从 URL 获取页面类型
const router = Taro.getCurrentInstance().router
if (router?.params) {
const { status } = router.params
if (status === 'paid' || status === 'unpaid') {
setPageType(status as 'unpaid' | 'paid')
}
}
})
// 申请退款
const handleRefund = (goodsId: string) => {
console.log('申请退款', goodsId)
Taro.showToast({
title: '申请退款',
icon: 'none'
})
}
// 立即支付
const handlePay = () => {
console.log('立即支付')
Taro.showToast({
title: '跳转支付',
icon: 'none'
})
}
// 联系商家
const handleCallMerchant = () => {
Taro.makePhoneCall({
phoneNumber: orderDetail.merchantPhone
})
}
return (
<View className="order_detail_page">
{/* 商品卡片 */}
<View className="goods_card">
{orderDetail.goods.map((goods, index) => (
<View key={goods.id} className="goods_item" style={{ marginTop: index > 0 ? '30px' : '0' }}>
<Image
className="goods_image"
src={goods.image}
mode="aspectFill"
width={208}
height={208}
/>
<View className="goods_info">
<Text className="goods_name">{goods.name}</Text>
<View className="price_container">
<Text className="goods_price">¥ {goods.price.toFixed(2)}</Text>
{/* 已支付订单且商品可退款时显示申请退款按钮 */}
{pageType === 'paid' && goods.canRefund && (
<View className="refund_btn" onClick={() => handleRefund(goods.id)}>
<Text className="refund_text">退</Text>
</View>
)}
</View>
</View>
</View>
))}
</View>
{/* 订单信息卡片 */}
<View className="order_info_card">
<View className="info_list">
<View className="info_row">
<Text className="info_label"></Text>
<Text className="info_value">{orderDetail.storeName}</Text>
</View>
<View className="info_row">
<Text className="info_label"></Text>
<Text className="info_value">{orderDetail.address}</Text>
</View>
{/* 已支付订单显示支付时间 */}
{pageType === 'paid' && orderDetail.paymentTime && (
<View className="info_row">
<Text className="info_label"></Text>
<Text className="info_value">{orderDetail.paymentTime}</Text>
</View>
)}
<View className="info_row">
<Text className="info_label"></Text>
<Text className="info_value">{orderDetail.orderNo}</Text>
</View>
<View className="info_row">
<Text className="info_label"></Text>
<Text className="info_value">{orderDetail.amount.toFixed(2)}</Text>
</View>
{/* 已支付订单显示支付方式 */}
{pageType === 'paid' && orderDetail.paymentMethod && (
<View className="info_row">
<Text className="info_label"></Text>
<Text className="info_value">{orderDetail.paymentMethod}</Text>
</View>
)}
<View className="info_row">
<Text className="info_label"></Text>
<Text className="info_value">{orderDetail.goodsCount}</Text>
</View>
</View>
{/* 分隔线 */}
<View className="divider" />
{/* 联系商家 */}
<View className="merchant_contact" onClick={handleCallMerchant}>
<Text className="contact_text">{orderDetail.merchantPhone}</Text>
</View>
</View>
{/* 未支付订单显示支付按钮 */}
{pageType === 'unpaid' && (
<View className="footer">
<Button className="pay_btn" onClick={handlePay}>
</Button>
</View>
)}
</View>
)
}
export default OrderDetail
Loading…
Cancel
Save