From c6b30a6b6b63c03ef4aac795b9f3cff98b167eab Mon Sep 17 00:00:00 2001 From: DU Date: Mon, 26 Jan 2026 23:20:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=95=86=E5=93=81=E9=80=80=E6=AC=BE=EF=BC=8C?= =?UTF-8?q?=20=E9=80=80=E6=AC=BE=E7=BB=93=E6=9E=9C=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.config.ts | 1 + src/components/GoodsInfoCard/index.scss | 57 ++++++ src/components/GoodsInfoCard/index.tsx | 72 +++++++ src/pagesUser/goodsCons/index.scss | 45 +---- src/pagesUser/goodsCons/index.tsx | 42 ++-- src/pagesUser/goodsOrderDetail/index.scss | 66 +++---- src/pagesUser/goodsOrderDetail/index.tsx | 105 +++++++--- .../goodsRefundResult/index.config.ts | 5 + src/pagesUser/goodsRefundResult/index.scss | 112 +++++++++++ src/pagesUser/goodsRefundResult/index.tsx | 181 ++++++++++++++++++ 10 files changed, 560 insertions(+), 126 deletions(-) create mode 100644 src/components/GoodsInfoCard/index.scss create mode 100644 src/components/GoodsInfoCard/index.tsx create mode 100644 src/pagesUser/goodsRefundResult/index.config.ts create mode 100644 src/pagesUser/goodsRefundResult/index.scss create mode 100644 src/pagesUser/goodsRefundResult/index.tsx diff --git a/src/app.config.ts b/src/app.config.ts index 1fbc021..d74ae74 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -26,6 +26,7 @@ export default defineAppConfig({ 'unsettledOrders/index', // 未结算账单 'goodsCons/index', // 商品消费 'goodsOrderDetail/index', // 商品消费详情 (已支付,未支付) + 'goodsRefundResult/index', // 商品退款结果(成功,失败) 'helpCenter/index', // 帮助中心 'agreement/index', // 协议 'editProfile/index', // 编辑资料 diff --git a/src/components/GoodsInfoCard/index.scss b/src/components/GoodsInfoCard/index.scss new file mode 100644 index 0000000..7470e95 --- /dev/null +++ b/src/components/GoodsInfoCard/index.scss @@ -0,0 +1,57 @@ +.goods_info_card_component { + 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; + padding: 10px 0; + + .goods_name { + font-size: 30px; + color: #000000; + font-weight: 600; + line-height: 1.4; + margin-bottom: 10px; + } + + // 价格行 + .price_row { + display: flex; + align-items: center; + justify-content: space-between; + + // 有间距的情况(用于订单详情页) + // &.with_separator { + margin-top: 56px; // 实现 padding-top: 66px 的效果(66-10=56) + // } + + .price_text { + font-size: 30px; + font-weight: 500; + + .price_label { + color: #000000; + font-weight: 400; + } + } + + // 右侧按钮区域 + .right_button_area { + margin-left: 20px; + flex-shrink: 0; + } + } + } +} diff --git a/src/components/GoodsInfoCard/index.tsx b/src/components/GoodsInfoCard/index.tsx new file mode 100644 index 0000000..26b50c9 --- /dev/null +++ b/src/components/GoodsInfoCard/index.tsx @@ -0,0 +1,72 @@ +/** + * 商品信息卡片组件 + * 用于商品消费、订单详情、退款结果等页面 + */ +import { View, Text } from '@tarojs/components' +import { Image } from '@taroify/core' +import { ReactNode } from 'react' +import './index.scss' + +export interface GoodsInfoCardProps { + // 基础信息 + image: string // 商品图片 + name: string // 商品名称 + + // 金额显示 + priceLabel?: string // 金额标签(如 "退款:") + price: number // 金额 + priceColor?: string // 金额颜色(默认 #FF3A24) + + // 右侧操作按钮(可选) + rightButton?: ReactNode // 自定义右侧按钮 + + // 样式定制 + containerClassName?: string // 容器额外类名 + showPriceSeparator?: boolean // 是否显示价格和名称之间的间距(默认 true) +} + +const GoodsInfoCard: React.FC = ({ + image, + name, + priceLabel, + price, + priceColor = '#FF3A24', + rightButton, + containerClassName = '', + showPriceSeparator = true +}) => { + return ( + + {/* 商品图片 */} + + + {/* 商品信息 */} + + {name} + + {/* 价格/退款金额行 */} + + + {priceLabel && {priceLabel} } + ¥ {price.toFixed(2)} + + + {/* 右侧按钮区域 */} + {rightButton && ( + + {rightButton} + + )} + + + + ) +} + +export default GoodsInfoCard diff --git a/src/pagesUser/goodsCons/index.scss b/src/pagesUser/goodsCons/index.scss index b56f485..6f381ca 100644 --- a/src/pagesUser/goodsCons/index.scss +++ b/src/pagesUser/goodsCons/index.scss @@ -156,51 +156,12 @@ .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; - + + // 退款商品信息特殊样式 + .refund_goods_info { .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; } } } diff --git a/src/pagesUser/goodsCons/index.tsx b/src/pagesUser/goodsCons/index.tsx index 9de0b4f..1cf958f 100644 --- a/src/pagesUser/goodsCons/index.tsx +++ b/src/pagesUser/goodsCons/index.tsx @@ -1,12 +1,13 @@ /** * 商品消费页 */ -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,Image } from '@taroify/core' +import { Tabs, Divider } from '@taroify/core' import { ArrowDown, Arrow } from '@taroify/icons' import EmptyData from '@/components/EmptyData' +import GoodsInfoCard from '@/components/GoodsInfoCard' import FilterPopup from '../orderList/components/FilterPopup' import './index.scss' @@ -165,6 +166,14 @@ const GoodsCons = () => { }) } + // 退款订单点击 + const handleRefundClick = (orderId: string, goodsId: string, refundStatus: 'success' | 'failed') => { + console.log('退款详情', orderId, goodsId, refundStatus) + Taro.navigateTo({ + url: `/pagesUser/goodsRefundResult/index?orderId=${orderId}&goodsId=${goodsId}&type=${refundStatus}` + }) + } + // 立即支付 const handlePay = (orderId: string, e: any) => { e.stopPropagation() // 阻止冒泡到卡片点击事件 @@ -230,28 +239,19 @@ const GoodsCons = () => { handleOrderClick(order.id)} + onClick={() => handleRefundClick(order.id, goods.id, goods.refundStatus)} > {/* 商品信息 */} - {/* 商品图片 */} - - - - - {/* 商品名称和退款金额 */} - - {goods.name} - 退款: ¥ {goods.refundAmount.toFixed(2)} - + {/* 退款状态栏 */} diff --git a/src/pagesUser/goodsOrderDetail/index.scss b/src/pagesUser/goodsOrderDetail/index.scss index c8b127d..c0f85d6 100644 --- a/src/pagesUser/goodsOrderDetail/index.scss +++ b/src/pagesUser/goodsOrderDetail/index.scss @@ -12,50 +12,44 @@ margin-bottom: 30px; .goods_item { - display: flex; - align-items: flex-start; + // 商品项之间的间距由 style 控制 + + // 退款按钮样式 + .refund_btn { + padding: 15px 30px; + border-radius: 50px; + transition: all 0.3s; + + .refund_text { + font-size: 25px; + } - .goods_image { - width: 208px; - height: 208px; - border-radius: 8px; - flex-shrink: 0; - } + // 可申请状态(绿色) + &.available { + background: #03C175; - .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; + .refund_text { + color: #FFFFFF; + } } - .price_container { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: 66px; + // 已申请状态(浅绿色,不可点击) + &.applied { + background: #B8E6D5; + pointer-events: none; - .goods_price { - font-size: 30px; - color: #FF3A24; - font-weight: 500; + .refund_text { + color: #FFFFFF; } + } - .refund_btn { - padding: 10px 30px; - background: #F2F3F5; - border-radius: 25px; + // 超时状态(灰色,不可点击) + &.timeout { + background: #F2F3F5; + pointer-events: none; - .refund_text { - font-size: 25px; - color: #666666; - } + .refund_text { + color: #C8C9CC; } } } diff --git a/src/pagesUser/goodsOrderDetail/index.tsx b/src/pagesUser/goodsOrderDetail/index.tsx index 7de6b97..0a56465 100644 --- a/src/pagesUser/goodsOrderDetail/index.tsx +++ b/src/pagesUser/goodsOrderDetail/index.tsx @@ -5,15 +5,21 @@ import { View, Text, Button, Textarea } from '@tarojs/components' import { useState } from 'react' import Taro, { useLoad } from '@tarojs/taro' import { Image, Popup } from '@taroify/core' +import GoodsInfoCard from '@/components/GoodsInfoCard' import './index.scss' +// 退款超时时间(单位:小时) +const REFUND_TIMEOUT_HOURS = 24 + // 商品信息接口 interface GoodsItem { id: string - image: string // 商品图片 - name: string // 商品名称 - price: number // 商品价格 - canRefund?: boolean // 是否可退款(仅已支付订单) + image: string // 商品图片 + name: string // 商品名称 + price: number // 商品价格 + canRefund?: boolean // 是否可退款(仅已支付订单) + createTime?: number // 创建时间戳(毫秒) + refundStatus?: 'none' | 'applied' // 退款状态:none-未申请,applied-已申请 } // 订单详情接口 @@ -42,27 +48,41 @@ const OrderDetail = () => { // 模拟订单详情数据 const [orderDetail] = useState({ id: '1', - status: 'unpaid', + status: 'paid', goods: [ { id: '1', image: 'https://img01.yzcdn.cn/vant/cat.jpeg', name: '美汁源Minute Maid果粒橙 橙汁饮料 1.25l/瓶', - price: 12.50 + price: 12.50, + canRefund: true, + createTime: Date.now() - 1000 * 60 * 60 * 10, // 10小时前(未超时,可申请) + refundStatus: 'none' }, { id: '2', image: 'https://img01.yzcdn.cn/vant/cat.jpeg', name: '可口可乐 迷你装 200ml/罐', price: 3.00, - canRefund: true + canRefund: true, + createTime: Date.now() - 1000 * 60 * 60 * 10, // 10小时前(未超时,已申请) + refundStatus: 'applied' + }, + { + id: '3', + image: 'https://img01.yzcdn.cn/vant/cat.jpeg', + name: '雪碧 330ml/瓶', + price: 4.00, + canRefund: true, + createTime: Date.now() - 1000 * 60 * 60 * 30, // 30小时前(已超时) + refundStatus: 'none' } ], storeName: 'XXX店', address: 'XXX市XXXXXXXXXXX市XXXXXXXXXXX市XXXXXXXX', orderNo: 'wjekwejrwkjsf1321445255', - amount: 15.50, - goodsCount: 2, + amount: 19.50, + goodsCount: 3, merchantPhone: '16688889999', paymentTime: '2025-01-01 10:00:00', paymentMethod: '微信支付' @@ -81,12 +101,45 @@ const OrderDetail = () => { } }) + // 判断商品是否超时不可退款 + const isRefundTimeout = (createTime?: number) => { + if (!createTime) return false + const now = Date.now() + const diffHours = (now - createTime) / (1000 * 60 * 60) + return diffHours > REFUND_TIMEOUT_HOURS + } + + // 获取退款按钮状态 + const getRefundBtnStatus = (goods: GoodsItem) => { + if (!goods.canRefund) return 'hidden' // 不可退款,隐藏按钮 + if (goods.refundStatus === 'applied') return 'applied' // 已申请 + if (isRefundTimeout(goods.createTime)) return 'timeout' // 超时 + return 'available' // 可申请 + } + + // 获取退款按钮文本 + const getRefundBtnText = (status: string) => { + switch (status) { + case 'applied': + return '已申请' + case 'timeout': + return '申请退款' + default: + return '申请退款' + } + } + // 申请退款 const handleRefund = (goodsId: string) => { console.log('申请退款', goodsId) // 查找商品 const goods = orderDetail.goods.find(item => item.id === goodsId) if (goods) { + const status = getRefundBtnStatus(goods) + // 只有状态为 available 时才能点击 + if (status !== 'available') { + return + } setCurrentRefundGoods(goods) setRefundReason('') setRefundPopupOpen(true) @@ -146,25 +199,23 @@ const OrderDetail = () => { {orderDetail.goods.map((goods, index) => ( 0 ? '30px' : '0' }}> - - - {goods.name} - - ¥ {goods.price.toFixed(2)} - {/* 已支付订单且商品可退款时显示申请退款按钮 */} - {pageType === 'paid' && goods.canRefund && ( - handleRefund(goods.id)}> - 申请退款 + handleRefund(goods.id)} + > + {getRefundBtnText(getRefundBtnStatus(goods))} - )} - - + ) : undefined + } + /> ))} diff --git a/src/pagesUser/goodsRefundResult/index.config.ts b/src/pagesUser/goodsRefundResult/index.config.ts new file mode 100644 index 0000000..0e7f4ed --- /dev/null +++ b/src/pagesUser/goodsRefundResult/index.config.ts @@ -0,0 +1,5 @@ +export default { + navigationBarTitleText: '商品消费', + navigationBarBackgroundColor: '#FFFFFF', + navigationBarTextStyle: 'black', +} diff --git a/src/pagesUser/goodsRefundResult/index.scss b/src/pagesUser/goodsRefundResult/index.scss new file mode 100644 index 0000000..62fdaf3 --- /dev/null +++ b/src/pagesUser/goodsRefundResult/index.scss @@ -0,0 +1,112 @@ +.refund_result_page { + min-height: 100vh; + background-color: #F2F3F5; + padding: 30px; + + // 退款结果状态卡片 + .result_status_card { + background-color: #FFFFFF; + border-radius: 12px; + padding: 50px 30px; + margin-bottom: 30px; + text-align: center; + + .status_title { + display: block; + font-size: 40px; + font-weight: 600; + margin-bottom: 30px; + + &.success { + color: #000000; + } + + &.failed { + color: #EE0A24; + } + } + + // 成功:退回账户信息 + .refund_account { + display: block; + font-size: 28px; + color: #333333; + line-height: 1.5; + } + + // 失败:原因行 + .fail_reason_row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px; + + .reason_label { + font-size: 28px; + color: #333333; + } + + .reason_value { + font-size: 28px; + color: #333333; + } + } + } + + // 商品信息卡片 + .goods_info_card { + background-color: #FFFFFF; + border-radius: 12px; + padding: 30px; + margin-bottom: 30px; + } + + // 退款详细信息卡片 + .refund_detail_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; + } + } + } +} diff --git a/src/pagesUser/goodsRefundResult/index.tsx b/src/pagesUser/goodsRefundResult/index.tsx new file mode 100644 index 0000000..32a0d95 --- /dev/null +++ b/src/pagesUser/goodsRefundResult/index.tsx @@ -0,0 +1,181 @@ +/** + * 商品订单退款结果页 + */ +import { View, Text } from '@tarojs/components' +import { useState } from 'react' +import Taro, { useLoad } from '@tarojs/taro' +import GoodsInfoCard from '@/components/GoodsInfoCard' +import './index.scss' + +// 退款结果类型 +type RefundResultType = 'success' | 'failed' + +// 商品信息接口 +interface GoodsInfo { + id: string + image: string + name: string + refundAmount: number +} + +// 退款结果信息接口 +interface RefundResultInfo { + resultType: RefundResultType // 退款结果类型 + goods: GoodsInfo // 商品信息 + + // 成功时的字段 + refundAccount?: string // 退回账户(微信/支付宝/银行卡) + + // 失败时的字段 + failReason?: string // 失败原因 + + // 通用字段 + refundReason: string // 退款原因 + applyAmount: string // 申请金额 + applyTime: string // 申请时间 + + // 成功时额外字段 + refundCompleteTime?: string // 退款完结时间 + refundNo?: string // 退款编号 + + // 门店信息 + storeName: string // 消费门店 + address: string // 地址 + merchantPhone?: string // 商家电话(失败时显示) +} + +const GoodsRefundResult = () => { + const [pageType, setPageType] = useState('success') + + // 模拟退款结果数据 + const [refundInfo] = useState({ + resultType: 'success', + goods: { + id: '1', + image: 'https://img01.yzcdn.cn/vant/cat.jpeg', + name: '美汁源Minute Maid果粒橙 橙汁饮料 1.25l/瓶', + refundAmount: 12.50 + }, + refundAccount: '微信/支付宝/银行卡(中国银行6666)', + refundReason: '计算错误', + applyAmount: '12.50元', + applyTime: '2025-01-01 12:00:00', + refundCompleteTime: '2025-01-01 12:06:00', + refundNo: 'a202501011206060000', + storeName: 'XXXXXXXXX店', + address: 'XXX市XXXXXXX区XXXXXXXXXX街道XXX大厦101', + merchantPhone: '16688889999', + failReason: '已成功取货' + }) + + useLoad(() => { + console.log('退款结果页面加载') + + // 从 URL 获取页面类型 + const router = Taro.getCurrentInstance().router + if (router?.params) { + const { type } = router.params + if (type === 'success' || type === 'failed') { + setPageType(type as RefundResultType) + } + } + }) + + // 联系商家 + const handleCallMerchant = () => { + if (refundInfo.merchantPhone) { + Taro.makePhoneCall({ + phoneNumber: refundInfo.merchantPhone + }) + } + } + + return ( + + {/* 退款结果状态卡片 */} + + + {pageType === 'success' ? '退款成功' : '退款失败'} + + + {pageType === 'success' ? ( + // 成功:显示退回账户信息 + + 退回{refundInfo.refundAccount} ¥ {refundInfo.goods.refundAmount.toFixed(2)} + + ) : ( + // 失败:显示失败原因 + + 原因 + {refundInfo.failReason} + + )} + + + {/* 商品信息卡片 */} + + + + + {/* 退款详细信息卡片 */} + + + + 退款原因 + {refundInfo.refundReason} + + + 申请金额 + {refundInfo.applyAmount} + + + 申请时间 + {refundInfo.applyTime} + + + {/* 成功时显示退款完结和退款编号 */} + {pageType === 'success' && ( + <> + + 退款完结 + {refundInfo.refundCompleteTime} + + + 退款编号 + {refundInfo.refundNo} + + + )} + + + 消费门店 + {refundInfo.storeName} + + + 地址 + {refundInfo.address} + + + + {/* 失败时显示联系商家 */} + {pageType === 'failed' && refundInfo.merchantPhone && ( + <> + + + 联系商家:{refundInfo.merchantPhone} + + + )} + + + ) +} + +export default GoodsRefundResult