feat(order): 完成实物订单退款申请全流程开发
新增退款申请后端接口调用方法,统一订单相关页面的字段配置,重构订单详情弹窗组件接入真实退款逻辑,优化全流程交互与展示。具体修改包括:
- 新增`refundApply`接口对接后端退款申请链路
- 适配后端新字段,修正原有订单列表、退款审核页的字段不匹配问题
- 重构详情弹窗,替换占位退款逻辑,新增核销二维码展示,优化状态映射与布局
- 优化审核页面交互,退款操作成功后自动刷新列表并关闭弹窗
- 统一接口提示信息的获取方式,修复提示错误
| | |
| | | import React, { useState, useEffect } from 'react'; |
| | | import { getFileAccessUrl } from '@/utils/file'; |
| | | import { |
| | | Modal, |
| | | Image, |
| | | Typography, |
| | | message, |
| | | Form, |
| | | Input, |
| | | InputNumber, |
| | | Button, |
| | | } from 'antd'; |
| | | import { getPhysicalGoodsOrderDetail } from '../service'; |
| | | import { Modal, Typography, message, Form, Input, InputNumber, Button, QRCode } from 'antd'; |
| | | import { getPhysicalGoodsOrderDetail, refundApply } from '../service'; |
| | | |
| | | const OrderDetailModal = ({ orderId, visible, isRefund = false, onClose }) => { |
| | | const OrderDetailModal = ({ orderId, visible, isRefund = false, onClose, onSuccess }) => { |
| | | const [orderDetail, setOrderDetail] = useState(null); |
| | | const [loading, setLoading] = useState(false); |
| | | const [submitting, setSubmitting] = useState(false); |
| | | const [form] = Form.useForm(); |
| | | |
| | | const orderStatusMap = { |
| | | 0: { text: '待支付', color: '#faad14' }, |
| | | 1: { text: '已支付', color: '#1890ff' }, |
| | | 2: { text: '已完成', color: '#52c41a' }, |
| | | 3: { text: '退款中', color: '#13c2c2' }, |
| | | 4: { text: '退款完成', color: '#722ed1' }, |
| | | 5: { text: '退款驳回', color: '#fa541c' }, |
| | | }; |
| | | |
| | | const ticketStatusMap = { |
| | | 0: '未核销', |
| | | 1: '已核销', |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | const fetchDetail = async () => { |
| | |
| | | if (res.code === 200) { |
| | | setOrderDetail(res.data); |
| | | } else { |
| | | message.error(res.message || '获取订单详情失败'); |
| | | message.error(res.msg || '获取订单详情失败'); |
| | | } |
| | | } catch (error) { |
| | | message.error('获取订单详情失败'); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | setLoading(false); |
| | | }; |
| | | if (visible && orderId) { |
| | | fetchDetail(); |
| | | } |
| | | if (!visible) { |
| | | setOrderDetail(null); |
| | | } |
| | | }, [visible, orderId]); |
| | | |
| | |
| | | } |
| | | }, [visible, isRefund, orderDetail, form]); |
| | | |
| | | // 渲染核销信息(待使用、已使用状态展示) |
| | | const renderVerifyInfo = () => { |
| | | if ( |
| | | ![2, 3].includes(orderDetail?.status) || |
| | | !orderDetail?.verifyList || |
| | | !orderDetail.verifyList.length |
| | | ) { |
| | | return null; |
| | | } |
| | | |
| | | return ( |
| | | <div style={{ marginTop: 24 }}> |
| | | <Typography.Title level={5}>核销信息:</Typography.Title> |
| | | <div style={{ display: 'flex', gap: 32 }}> |
| | | {orderDetail.verifyList.map((item, index) => ( |
| | | <div key={item.id || index} style={{ textAlign: 'center' }}> |
| | | <Image |
| | | width={150} |
| | | height={150} |
| | | src={item.qrCode ? getFileAccessUrl(item.qrCode) : null} |
| | | style={{ objectFit: 'cover' }} |
| | | placeholder={<div style={{ width: 150, height: 150, background: '#f5f5f5' }} />} |
| | | /> |
| | | <div style={{ marginTop: 8 }}>{item.name}</div> |
| | | <div>{item.idCard}</div> |
| | | </div> |
| | | ))} |
| | | </div> |
| | | </div> |
| | | ); |
| | | }; |
| | | |
| | | if (!visible) return null; |
| | | |
| | | const statusMap = { |
| | | 1: { text: '待支付', color: '#faad14' }, |
| | | 2: { text: '待使用', color: '#1890ff' }, |
| | | 3: { text: '已使用', color: '#52c41a' }, |
| | | 4: { text: '退款驳回', color: '#fa541c' }, |
| | | 5: { text: '已退款', color: '#722ed1' }, |
| | | }; |
| | | |
| | | const currentStatus = statusMap[orderDetail?.status] || { text: '未知', color: '#d9d9d9' }; |
| | | const currentStatus = orderStatusMap[orderDetail?.status] || { text: '未知', color: '#d9d9d9' }; |
| | | |
| | | // 提交退款申请 |
| | | const handleSubmitRefund = async () => { |
| | |
| | | |
| | | setSubmitting(true); |
| | | |
| | | // TODO: 调用实际的退款申请接口 |
| | | // 请根据后端提供的接口地址与参数进行调整 |
| | | // const res = await applyRefund({ |
| | | // orderId, |
| | | // refundAmount, |
| | | // handlingFee, |
| | | // refundReason: values.refundReason, |
| | | // }); |
| | | // |
| | | // if (res.code === 200) { |
| | | // message.success('申请退款成功'); |
| | | // onClose?.(); |
| | | // } else { |
| | | // message.error(res.message || '申请退款失败'); |
| | | // } |
| | | // 按后端接口约定提交退款申请 |
| | | const res = await refundApply({ |
| | | id: orderDetail?.id || orderId, |
| | | refundMoney: refundAmount, |
| | | handlingFee, |
| | | refundReason: values.refundReason, |
| | | }); |
| | | |
| | | // 目前先做前端校验与提示,实际提交由后续接入接口 |
| | | message.success('已通过前端校验,请接入后台退款接口完成申请逻辑'); |
| | | onClose?.(); |
| | | if (res.code === 200) { |
| | | message.success(res.msg || '申请退款成功'); |
| | | form.resetFields(); |
| | | onSuccess?.(); |
| | | } else { |
| | | message.error(res.msg || '申请退款失败'); |
| | | } |
| | | } catch (error) { |
| | | // 表单校验未通过无需提示 |
| | | } finally { |
| | |
| | | return ( |
| | | <Modal |
| | | title={isRefund ? '申请退款' : '订单详情'} |
| | | width='60%' |
| | | width="60%" |
| | | visible={visible} |
| | | onCancel={onClose} |
| | | footer={ |
| | | isRefund |
| | | ? [ |
| | | <Button key="cancel" onClick={onClose}> |
| | | 取消 |
| | | </Button>, |
| | | <Button |
| | | key="submit" |
| | | type="primary" |
| | | loading={submitting} |
| | | onClick={handleSubmitRefund} |
| | | > |
| | | 确认提交 |
| | | </Button>, |
| | | ] |
| | | <Button key="cancel" onClick={onClose}> |
| | | 取消 |
| | | </Button>, |
| | | <Button |
| | | key="submit" |
| | | type="primary" |
| | | loading={submitting} |
| | | onClick={handleSubmitRefund} |
| | | > |
| | | 确认提交 |
| | | </Button>, |
| | | ] |
| | | : null |
| | | } |
| | | destroyOnHidden |
| | |
| | | <div style={{ textAlign: 'center', padding: '50px' }}>加载中...</div> |
| | | ) : orderDetail ? ( |
| | | <> |
| | | {/* 订单信息 */} |
| | | {/* 订单信息:按真实详情接口字段展示 */} |
| | | <div style={{ marginBottom: 24 }}> |
| | | <div style={{ display: 'flex', flexWrap: 'wrap', gap: '16px 24px' }}> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>订单编号:</span> |
| | | <span>{orderDetail.code}</span> |
| | | <span>{orderDetail.orderNo || '-'}</span> |
| | | </div> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>下单时间:</span> |
| | | <span>{orderDetail.createTime}</span> |
| | | <span>{orderDetail.createTime || '-'}</span> |
| | | </div> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>订单状态:</span> |
| | | <span style={{ color: currentStatus.color }}>{currentStatus.text}</span> |
| | | </div> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>下单用户:</span> |
| | | <span>{orderDetail.userName}</span> |
| | | <span>{orderDetail.userName || '-'}</span> |
| | | </div> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>用户手机号:</span> |
| | | <span>{orderDetail.phone}</span> |
| | | <span>{orderDetail.phone || '-'}</span> |
| | | </div> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>订单价格:</span> |
| | | <span>¥{orderDetail.orderPrice || 0}</span> |
| | | </div> |
| | | {orderDetail.status !== 1 && ( |
| | | {/* <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>门票信息:</span> |
| | | <span>{orderDetail.ticketInfo || '-'}</span> |
| | | </div> |
| | | <div |
| | | style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }} |
| | | > |
| | | <span style={{ color: '#666' }}>游客数量:</span> |
| | | <span>{orderDetail.tourist || orderDetail.touristList?.length || 0}</span> |
| | | </div> */} |
| | | {orderDetail.paymentPrice !== undefined && orderDetail.paymentPrice !== null && ( |
| | | <> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>已支付:</span> |
| | | <span>¥{orderDetail.paymentPrice || 0}</span> |
| | | </div> |
| | | {orderDetail.paymentTime && ( |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>支付时间:</span> |
| | | <span>{orderDetail.paymentTime}</span> |
| | | </div> |
| | | )} |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>支付方式:</span> |
| | | <span>微信支付</span> |
| | | </div> |
| | | </> |
| | | )} |
| | | {orderDetail.status === 4 && ( |
| | | {orderDetail.status === 5 && ( |
| | | <> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <span style={{ color: '#666' }}>退款结果:</span> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>退款原因:</span> |
| | | <span>{orderDetail.refundReason || '-'}</span> |
| | | </div> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>审核结果:</span> |
| | | <span>驳回</span> |
| | | </div> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <span style={{ color: '#666' }}>审核意见:</span> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>审批意见:</span> |
| | | <span>{orderDetail.auditRemark || orderDetail.refundRemark || '-'}</span> |
| | | </div> |
| | | </> |
| | | )} |
| | | {orderDetail.status === 5 && ( |
| | | {orderDetail.status === 4 && ( |
| | | <> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <span style={{ color: '#666' }}>退款结果:</span> |
| | | <span>已退款</span> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>退款原因:</span> |
| | | <span>{orderDetail.refundReason || '-'}</span> |
| | | </div> |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <span style={{ color: '#666' }}>退款金额:</span> |
| | | <span>¥{orderDetail.refundAmount || orderDetail.paymentPrice || 0}</span> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>审核结果:</span> |
| | | <span>通过</span> |
| | | </div> |
| | | {orderDetail.refundTime && ( |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <span style={{ color: '#666' }}>退款时间:</span> |
| | | <span>{orderDetail.refundTime}</span> |
| | | </div> |
| | | )} |
| | | <div style={{ display: 'flex', gap: 8, minWidth: '200px', flex: '1 1 calc(33.333% - 24px)' }}> |
| | | <span style={{ color: '#666' }}>退款方式:</span> |
| | | <span>原路退回</span> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>退款时间:</span> |
| | | <span>{orderDetail.refundTime || '-'}</span> |
| | | </div> |
| | | {orderDetail.auditRemark || orderDetail.refundRemark && <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | }} |
| | | > |
| | | <span style={{ color: '#666' }}>审批意见:</span> |
| | | <span>{orderDetail.auditRemark || orderDetail.refundRemark || '-'}</span> |
| | | </div>} |
| | | </> |
| | | )} |
| | | </div> |
| | | </div> |
| | | |
| | | {/* 门票信息 */} |
| | | {(orderDetail.ticketList || orderDetail.ticketInfo || orderDetail.physicalGoodsName) && ( |
| | | {orderDetail.ticketInfo && ( |
| | | <div style={{ marginBottom: 24 }}> |
| | | <Typography.Title level={5} style={{ marginBottom: 12 }}>门票信息</Typography.Title> |
| | | <Typography.Title level={5} style={{ marginBottom: 12 }}> |
| | | 门票信息 |
| | | </Typography.Title> |
| | | <div |
| | | style={{ |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '12px 16px', |
| | | borderRadius: '4px', |
| | | display: 'flex', |
| | | gap: 16, |
| | | alignItems: 'center', |
| | | flexWrap: 'wrap', |
| | | gap: '8px 24px', |
| | | }} |
| | | > |
| | | {orderDetail.ticketList && orderDetail.ticketList.length > 0 ? ( |
| | | <> |
| | | <span>{orderDetail.ticketList[0].ticketType || orderDetail.ticketList[0].ticketName || '成人票'}</span> |
| | | <span>{orderDetail.ticketList[0].useDate || orderDetail.ticketList[0].useTime || ''}</span> |
| | | <span>数量: {orderDetail.ticketList.length}</span> |
| | | </> |
| | | ) : ( |
| | | <> |
| | | <span>{orderDetail.ticketType || orderDetail.physicalGoodsName || '成人票'}</span> |
| | | <span>{orderDetail.useDate || orderDetail.useTime || ''}</span> |
| | | <span>数量: {orderDetail.ticketQuantity || orderDetail.quantity || 1}</span> |
| | | </> |
| | | )} |
| | | <span>{orderDetail.ticketInfo}</span> |
| | | <span>数量: {orderDetail.touristList?.length || 0}</span> |
| | | </div> |
| | | </div> |
| | | )} |
| | | |
| | | {/* 游客信息 */} |
| | | {(orderDetail.visitorList || orderDetail.passengerList || orderDetail.verifyList) && ( |
| | | {/* 游客信息:真实字段为 touristList */} |
| | | {orderDetail.touristList?.length > 0 && ( |
| | | <div style={{ marginBottom: 24 }}> |
| | | <Typography.Title level={5} style={{ marginBottom: 12 }}>游客信息</Typography.Title> |
| | | <Typography.Title level={5} style={{ marginBottom: 12 }}> |
| | | 游客信息 |
| | | </Typography.Title> |
| | | <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}> |
| | | {(orderDetail.visitorList || orderDetail.passengerList || orderDetail.verifyList || []).map((visitor, index) => ( |
| | | {orderDetail.touristList.map((visitor, index) => ( |
| | | <div |
| | | key={visitor.id || index} |
| | | style={{ |
| | |
| | | padding: '12px 16px', |
| | | borderRadius: '4px', |
| | | display: 'flex', |
| | | gap: 16, |
| | | alignItems: 'center', |
| | | flexWrap: 'wrap', |
| | | gap: '8px 24px', |
| | | }} |
| | | > |
| | | <span>姓名: {visitor.name || visitor.visitorName || ''}</span> |
| | |
| | | </div> |
| | | )} |
| | | |
| | | {/* 核销信息,仅待使用状态显示 */} |
| | | {renderVerifyInfo()} |
| | | {/* 核销信息:真实字段为 ticketInfoVo,code 直接转二维码 */} |
| | | {orderDetail.ticketInfoVo?.length > 0 && ( |
| | | <div style={{ marginBottom: 24 }}> |
| | | <Typography.Title level={5} style={{ marginBottom: 12 }}> |
| | | 核销信息 |
| | | </Typography.Title> |
| | | <div style={{ display: 'flex', flexWrap: 'wrap', gap: 16 }}> |
| | | {orderDetail.ticketInfoVo.map((ticket, index) => ( |
| | | <div |
| | | key={ticket.id || index} |
| | | style={{ |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '16px', |
| | | borderRadius: '4px', |
| | | width: '240px', |
| | | textAlign: 'center', |
| | | }} |
| | | > |
| | | <div style={{ marginBottom: 12, display: 'flex', justifyContent: 'center' }}> |
| | | <QRCode |
| | | value={ticket.code || '-'} |
| | | size={120} |
| | | bordered={false} |
| | | status="active" |
| | | /> |
| | | </div> |
| | | <div style={{ display: 'flex', justifyContent: 'center', gap: 16, flexWrap: 'wrap' }}> |
| | | <span>{ticket.name || '-'}</span> |
| | | <span>{ticket.code || '-'}</span> |
| | | </div> |
| | | <div style={{ marginTop: 8, color: '#666' }}> |
| | | <span>状态: {ticketStatusMap[ticket.status] || '未知'}</span> |
| | | </div> |
| | | {ticketStatusMap[ticket.status] === '已核销' && <div style={{ marginTop: 4, color: '#666' }}> |
| | | <span>核销时间: {ticket.useTime || '-'}</span> |
| | | </div>} |
| | | </div> |
| | | ))} |
| | | </div> |
| | | </div> |
| | | )} |
| | | |
| | | {/* 退款申请表单,仅在申请退款模式下展示 */} |
| | | {isRefund && ( |
| | |
| | | label="手续费" |
| | | name="handlingFee" |
| | | style={{ flex: '0 0 300px' }} |
| | | rules={[ |
| | | { required: true, message: '请输入手续费' }, |
| | | ]} |
| | | > |
| | | <InputNumber |
| | | min={0} |
| | |
| | | setCurrentOrderId(null); |
| | | }; |
| | | |
| | | // 退款申请成功后关闭弹窗并刷新列表 |
| | | const handleRefundSuccess = () => { |
| | | handleCloseDetail(); |
| | | actionRef.current?.reload(); |
| | | }; |
| | | |
| | | // 列定义 |
| | | const columns = [ |
| | | { |
| | |
| | | }, |
| | | { |
| | | title: '订单编号', |
| | | dataIndex: 'code', |
| | | dataIndex: 'orderNo', |
| | | hideInSearch: true, |
| | | key: 'code', |
| | | key: 'orderNo', |
| | | }, |
| | | { |
| | | title: '下单时间', |
| | |
| | | }, |
| | | { |
| | | title: '门票信息', |
| | | dataIndex: 'physicalGoodsName', |
| | | key: 'physicalGoodsName', |
| | | dataIndex: 'ticketInfo', |
| | | key: 'ticketInfo', |
| | | width: 200, |
| | | fieldProps: { |
| | | placeholder: '请输入', |
| | | }, |
| | | }, |
| | | { |
| | | title: '支付方式', |
| | | dataIndex: 'paymentMethod', |
| | | key: 'paymentMethod', |
| | | title: '游客数量', |
| | | dataIndex: 'tourist', |
| | | key: 'tourist', |
| | | hideInSearch: true, |
| | | render: (text) => text === 1 ? '微信支付' : '其他', |
| | | render: (_, record) => record.tourist || record.touristList?.length || 0, |
| | | }, |
| | | { |
| | | title: '订单状态', |
| | |
| | | visible={detailModalVisible} |
| | | isRefund={isRefundMode} |
| | | onClose={handleCloseDetail} |
| | | onSuccess={handleRefundSuccess} |
| | | /> |
| | | </PageContainer> |
| | | ); |
| | |
| | | }); |
| | | } |
| | | |
| | | // 申请退款 |
| | | export async function refundApply(data) { |
| | | return request('/t-ticket-order/refundApply', { |
| | | method: 'POST', |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // 删除实物商品订单 |
| | | export async function deletePhysicalGoodsOrder(id) { |
| | | return request(`/t-physical-goods-order/delete/${id}`, { |
| | | method: 'DELETE', |
| | | }); |
| | | } |
| | | } |
| | |
| | | import { getRefundDetailById, refundAudit } from '../service'; |
| | | import { Modal, Image, Typography, Form, Input, Button, message, Space, Spin, Radio } from 'antd'; |
| | | import { useEffect, useState } from 'react'; |
| | | import { getPhysicalGoodsOrderDetail } from '../../orderList/service'; |
| | | import { refundAudit } from '../service'; |
| | | import { Modal, Typography, Form, Input, Button, message, Radio, Spin, QRCode } from 'antd'; |
| | | import { useEffect, useMemo, useState } from 'react'; |
| | | import moment from 'moment'; |
| | | |
| | | const { Text, Title } = Typography; |
| | | const { Title } = Typography; |
| | | |
| | | const RefundDetailModal = ({ visible, onCancel, id }) => { |
| | | const [orderDetail, setOrderDetail] = useState(null); |
| | | const [loading, setLoading] = useState(false); |
| | | const RefundDetailModal = ({ visible, orderId, onCancel, onSuccess, record }) => { |
| | | const [auditForm] = Form.useForm(); |
| | | const [auditLoading, setAuditLoading] = useState(false); |
| | | const [detailLoading, setDetailLoading] = useState(false); |
| | | const [detailData, setDetailData] = useState(null); |
| | | |
| | | // 详情接口返回的数据更完整,列表数据中的退款审核字段作为兜底补齐 |
| | | const orderDetail = useMemo(() => { |
| | | if (!record && !detailData) { |
| | | return null; |
| | | } |
| | | |
| | | return { |
| | | ...(record || {}), |
| | | ...(detailData || {}), |
| | | }; |
| | | }, [record, detailData]); |
| | | |
| | | // 订单状态映射 |
| | | const statusMap = { |
| | | 1: '待支付', |
| | | 2: '待使用', |
| | | 3: '已使用', |
| | | 4: '退款驳回', |
| | | 5: '已退款', |
| | | 0: '待支付', |
| | | 1: '已支付', |
| | | 2: '已完成', |
| | | 3: '退款中', |
| | | 4: '退款完成', |
| | | 5: '退款驳回', |
| | | }; |
| | | |
| | | // 审核状态映射 |
| | | const auditStatusMap = { |
| | | 1: '待审核', |
| | | 2: '审核通过', |
| | | 3: '审核驳回', |
| | | 3: '待审核', |
| | | 4: '审核通过', |
| | | 5: '审核驳回', |
| | | }; |
| | | |
| | | // 支付类型映射 |
| | | const paymentTypeMap = { |
| | | 1: '订金+尾款', |
| | | 2: '全款', |
| | | const ticketStatusMap = { |
| | | 0: '未核销', |
| | | 1: '已核销', |
| | | }; |
| | | |
| | | // 获取订单详情 |
| | | const fetchOrderDetail = async () => { |
| | | if (!id) return; |
| | | |
| | | setLoading(true); |
| | | try { |
| | | const res = await getRefundDetailById(id); |
| | | if (res.code === 200 && res.data) { |
| | | setOrderDetail(res.data); |
| | | useEffect(() => { |
| | | const fetchOrderDetail = async () => { |
| | | if (!visible || !orderId) { |
| | | return; |
| | | } |
| | | } catch (error) { |
| | | console.error('获取订单详情失败:', error); |
| | | } finally { |
| | | setLoading(false); |
| | | |
| | | setDetailLoading(true); |
| | | try { |
| | | const res = await getPhysicalGoodsOrderDetail(orderId); |
| | | if (res.code === 200) { |
| | | setDetailData(res.data || {}); |
| | | } else { |
| | | message.error(res.msg || '获取订单详情失败'); |
| | | } |
| | | } catch (error) { |
| | | message.error('获取订单详情失败'); |
| | | } finally { |
| | | setDetailLoading(false); |
| | | } |
| | | }; |
| | | |
| | | fetchOrderDetail(); |
| | | }, [visible, orderId]); |
| | | |
| | | useEffect(() => { |
| | | if (!visible) { |
| | | setDetailData(null); |
| | | } |
| | | }; |
| | | }, [visible]); |
| | | |
| | | useEffect(() => { |
| | | if (visible && orderDetail) { |
| | | auditForm.resetFields(); |
| | | auditForm.setFieldsValue({ |
| | | refundMoney: |
| | | orderDetail.refundMoney ?? |
| | | 0, |
| | | handlingFee: orderDetail.handlingFee ?? 0, |
| | | auditStatus: orderDetail.status === 4 ? 2 : orderDetail.status === 5 ? 3 : undefined, |
| | | auditRemark: orderDetail.auditContent || orderDetail.auditRemark, |
| | | }); |
| | | } |
| | | }, [visible, orderDetail, auditForm]); |
| | | |
| | | // 审核提交 |
| | | const handleAuditSubmit = async () => { |
| | |
| | | setAuditLoading(true); |
| | | |
| | | const params = { |
| | | id, |
| | | id: record?.id || orderDetail?.id, |
| | | auditStatus: values.auditStatus, |
| | | auditRemark: values.auditRemark || '', |
| | | handlingFee: values.handlingFee || 0, |
| | | refundAmount: values.refundAmount || orderDetail?.refundAmount || 0, |
| | | handlingFee: values.handlingFee ?? 0, |
| | | refundAmount: |
| | | values.refundMoney ?? |
| | | 0, |
| | | }; |
| | | |
| | | const res = await refundAudit(params); |
| | | |
| | | if (res.code === 200) { |
| | | message.success('审核成功'); |
| | | fetchOrderDetail(); // 重新获取详情 |
| | | auditForm.resetFields(); |
| | | onCancel(); // 关闭弹窗 |
| | | onSuccess?.(); |
| | | } else { |
| | | message.error(res.msg || '审核失败'); |
| | | } |
| | |
| | | } |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | if (visible && id) { |
| | | fetchOrderDetail(); |
| | | auditForm.resetFields(); |
| | | } |
| | | }, [visible, id]); |
| | | |
| | | // 渲染详情内容 |
| | | const renderDetailContent = () => { |
| | | if (!orderDetail) return null; |
| | | if (!orderDetail) { |
| | | return null; |
| | | } |
| | | |
| | | const { status, auditStatus } = orderDetail; |
| | | const isAuditing = auditStatus === 1; |
| | | const { status } = orderDetail; |
| | | const isAuditing = status === 3; |
| | | const submitTime = orderDetail.refundAuditSubmitTime || null; |
| | | const refundReason = orderDetail.refundReason || '-'; |
| | | const auditRemark = record?.auditContent || orderDetail.auditContent || orderDetail.auditRemark; |
| | | const refundMoney = |
| | | orderDetail.refundMoney ?? |
| | | orderDetail.refundAmount ?? |
| | | orderDetail.paymentPrice ?? |
| | | orderDetail.orderPrice ?? |
| | | 0; |
| | | const handlingFee = orderDetail.handlingFee ?? 0; |
| | | |
| | | return ( |
| | | <div style={{ padding: '0 8px' }}> |
| | | {/* 订单详情 */} |
| | | <div style={{ marginBottom: 24 }}> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span style={{ marginRight: 8 }}>订单编号: {orderDetail.code || '-'}</span> |
| | | </div> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span style={{ marginRight: 8 }}>下单时间: {orderDetail.createTime ? moment(orderDetail.createTime).format('YYYY-MM-DD HH:mm:ss') : '-'}</span> |
| | | </div> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span style={{ marginRight: 8 }}>订单状态: {statusMap[status] || '-'}</span> |
| | | </div> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span style={{ marginRight: 8 }}>下单用户: {orderDetail.userName || '-'}</span> |
| | | </div> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span style={{ marginRight: 8 }}>用户手机号: {orderDetail.phone || '-'}</span> |
| | | </div> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span style={{ marginRight: 8 }}>订单价格: ¥{orderDetail.orderPrice || 0}</span> |
| | | <div style={{ display: 'flex', flexWrap: 'wrap', gap: '16px 24px' }}> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>订单编号:</span> |
| | | <span>{orderDetail.code || orderDetail.orderNo || '-'}</span> |
| | | </div> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>下单时间:</span> |
| | | <span> |
| | | {orderDetail.createTime |
| | | ? moment(orderDetail.createTime).format('YYYY-MM-DD HH:mm:ss') |
| | | : '-'} |
| | | </span> |
| | | </div> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>订单状态:</span> |
| | | <span>{statusMap[status] || '-'}</span> |
| | | </div> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>下单用户:</span> |
| | | <span>{orderDetail.userName || '-'}</span> |
| | | </div> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>用户手机号:</span> |
| | | <span>{orderDetail.phone || '-'}</span> |
| | | </div> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>订单价格:</span> |
| | | <span>¥{orderDetail.orderPrice || 0}</span> |
| | | </div> |
| | | {orderDetail.paymentPrice !== undefined && orderDetail.paymentPrice !== null && ( |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>实付金额:</span> |
| | | <span>¥{orderDetail.paymentPrice || 0}</span> |
| | | </div> |
| | | )} |
| | | {orderDetail.paymentTime && ( |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | gap: 8, |
| | | minWidth: '200px', |
| | | flex: '1 1 calc(33.333% - 24px)', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span>支付时间:</span> |
| | | <span>{orderDetail.paymentTime}</span> |
| | | </div> |
| | | )} |
| | | </div> |
| | | </div> |
| | | |
| | | {/* 门票信息 */} |
| | | {(orderDetail.ticketList || orderDetail.ticketInfo || orderDetail.physicalGoodsName) && ( |
| | | {orderDetail.ticketInfo && ( |
| | | <div style={{ marginBottom: 24 }}> |
| | | <Title level={5} style={{ marginBottom: 12, fontSize: 14, fontWeight: 500 }}>门票信息</Title> |
| | | <Title level={5} style={{ marginBottom: 12, fontSize: 14, fontWeight: 500 }}> |
| | | 门票信息 |
| | | </Title> |
| | | <div |
| | | style={{ |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '12px 16px', |
| | | borderRadius: '4px', |
| | | display: 'flex', |
| | | flexWrap: 'wrap', |
| | | gap: '8px 24px', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | {orderDetail.ticketList && orderDetail.ticketList.length > 0 ? ( |
| | | <> |
| | | <span style={{ marginRight: 16 }}>{orderDetail.ticketList[0].ticketType || orderDetail.ticketList[0].ticketName || '成人票'}</span> |
| | | <span style={{ marginRight: 16 }}>{orderDetail.ticketList[0].useDate || orderDetail.ticketList[0].useTime || ''}</span> |
| | | <span>数量: {orderDetail.ticketList.length}</span> |
| | | </> |
| | | ) : ( |
| | | <> |
| | | <span style={{ marginRight: 16 }}>{orderDetail.ticketType || orderDetail.physicalGoodsName || '成人票'}</span> |
| | | <span style={{ marginRight: 16 }}>{orderDetail.useDate || orderDetail.useTime || ''}</span> |
| | | <span>数量: {orderDetail.ticketQuantity || orderDetail.quantity || 1}</span> |
| | | </> |
| | | )} |
| | | <span>{orderDetail.ticketInfo}</span> |
| | | <span>数量: {orderDetail.touristList?.length || orderDetail.tourist || 0}</span> |
| | | </div> |
| | | </div> |
| | | )} |
| | | |
| | | {/* 游客信息 */} |
| | | {(orderDetail.visitorList || orderDetail.passengerList || orderDetail.verifyList) && ( |
| | | {orderDetail.touristList?.length > 0 && ( |
| | | <div style={{ marginBottom: 24 }}> |
| | | <Title level={5} style={{ marginBottom: 12, fontSize: 14, fontWeight: 500 }}>游客信息</Title> |
| | | <Title level={5} style={{ marginBottom: 12, fontSize: 14, fontWeight: 500 }}> |
| | | 游客信息 |
| | | </Title> |
| | | <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}> |
| | | {(orderDetail.visitorList || orderDetail.passengerList || orderDetail.verifyList || []).map((visitor, index) => ( |
| | | {orderDetail.touristList.map((visitor, index) => ( |
| | | <div |
| | | key={visitor.id || index} |
| | | style={{ |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '12px 16px', |
| | | borderRadius: '4px', |
| | | display: 'flex', |
| | | flexWrap: 'wrap', |
| | | gap: '8px 24px', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | }} |
| | | > |
| | | <span style={{ marginRight: 16 }}>姓名: {visitor.name || visitor.visitorName || ''}</span> |
| | | <span style={{ marginRight: 16 }}>身份证号: {visitor.idCard || visitor.idNumber || ''}</span> |
| | | <span>姓名: {visitor.name || visitor.visitorName || ''}</span> |
| | | <span>身份证号: {visitor.idCard || visitor.idNumber || ''}</span> |
| | | <span>电话: {visitor.phone || visitor.tel || ''}</span> |
| | | </div> |
| | | ))} |
| | |
| | | </div> |
| | | )} |
| | | |
| | | {/* 核销信息 */} |
| | | {orderDetail.verifyList && orderDetail.verifyList.length > 0 && ( |
| | | {orderDetail.ticketInfoVo?.length > 0 && ( |
| | | <div style={{ marginBottom: 24 }}> |
| | | <Title level={5} style={{ marginBottom: 12, fontSize: 14, fontWeight: 500 }}>核销信息</Title> |
| | | <div |
| | | style={{ |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '16px 24px', |
| | | borderRadius: '4px', |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | gap: 24, |
| | | }} |
| | | > |
| | | <Image |
| | | width={120} |
| | | height={120} |
| | | src={orderDetail.verifyList[0].qrCode || ''} |
| | | style={{ objectFit: 'cover' }} |
| | | /> |
| | | <div style={{ fontSize: 14, color: '#333', lineHeight: '24px' }}> |
| | | <div>票号:{orderDetail.verifyList[0].ticketNo || orderDetail.ticketNo || '-'}</div> |
| | | <div>到期时间:{orderDetail.verifyList[0].expireTime || orderDetail.expireTime || '-'}</div> |
| | | </div> |
| | | <Title level={5} style={{ marginBottom: 12, fontSize: 14, fontWeight: 500 }}> |
| | | 核销信息 |
| | | </Title> |
| | | <div style={{ display: 'flex', flexWrap: 'wrap', gap: 16 }}> |
| | | {orderDetail.ticketInfoVo.map((ticket, index) => ( |
| | | <div |
| | | key={ticket.id || index} |
| | | style={{ |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '16px', |
| | | borderRadius: '4px', |
| | | width: '240px', |
| | | textAlign: 'center', |
| | | }} |
| | | > |
| | | <div |
| | | style={{ |
| | | marginBottom: 12, |
| | | display: 'flex', |
| | | justifyContent: 'center', |
| | | }} |
| | | > |
| | | <QRCode |
| | | value={ticket.code || '-'} |
| | | size={120} |
| | | bordered={false} |
| | | status="active" |
| | | /> |
| | | </div> |
| | | <div |
| | | style={{ |
| | | display: 'flex', |
| | | justifyContent: 'center', |
| | | gap: 16, |
| | | flexWrap: 'wrap', |
| | | }} |
| | | > |
| | | <span>{ticket.name || '-'}</span> |
| | | <span>{ticket.code || '-'}</span> |
| | | </div> |
| | | <div style={{ marginTop: 8, color: '#666' }}> |
| | | <span>状态: {ticketStatusMap[ticket.status] || '未知'}</span> |
| | | </div> |
| | | {ticket.status === 1 && ( |
| | | <div style={{ marginTop: 4, color: '#666' }}> |
| | | <span>核销时间: {ticket.useTime || '-'}</span> |
| | | </div> |
| | | )} |
| | | </div> |
| | | ))} |
| | | </div> |
| | | </div> |
| | | )} |
| | | |
| | | {/* 退款申请信息 */} |
| | | <div style={{ marginBottom: 24 }}> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span>提交时间: {orderDetail.refundCreateTime || orderDetail.createTime ? moment(orderDetail.refundCreateTime || orderDetail.createTime).format('YYYY-MM-DD HH:mm:ss') : '-'}</span> |
| | | <span> |
| | | 提交时间: {submitTime ? moment(submitTime).format('YYYY-MM-DD HH:mm:ss') : '-'} |
| | | </span> |
| | | </div> |
| | | |
| | | {/* 审核表单(仅待审核状态显示) */} |
| | | {isAuditing && ( |
| | | <Form |
| | | form={auditForm} |
| | | layout="vertical" |
| | | initialValues={{ |
| | | refundAmount: orderDetail.refundAmount || 0, |
| | | refundMoney, |
| | | }} |
| | | > |
| | | <Form.Item |
| | | name="refundAmount" |
| | | name="refundMoney" |
| | | label={<span style={{ fontSize: 14 }}>退款金额</span>} |
| | | rules={[{ required: true, message: '请输入退款金额' }]} |
| | | > |
| | | <Input |
| | | type="number" |
| | | placeholder="请输入" |
| | | style={{ fontSize: 14 }} |
| | | /> |
| | | <Input type="number" placeholder="请输入" style={{ fontSize: 14 }} /> |
| | | </Form.Item> |
| | | |
| | | <Form.Item |
| | | name="handlingFee" |
| | | label={<span style={{ fontSize: 14 }}>手续费</span>} |
| | | > |
| | | <Input |
| | | type="number" |
| | | placeholder="请输入" |
| | | style={{ fontSize: 14 }} |
| | | /> |
| | | <Form.Item name="handlingFee" label={<span style={{ fontSize: 14 }}>手续费</span>}> |
| | | <Input type="number" placeholder="请输入" style={{ fontSize: 14 }} /> |
| | | </Form.Item> |
| | | |
| | | <Form.Item |
| | | label={<span style={{ fontSize: 14 }}>退款原因</span>} |
| | | > |
| | | <div style={{ |
| | | padding: '12px', |
| | | backgroundColor: '#f5f5f5', |
| | | borderRadius: '4px', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | minHeight: 60, |
| | | }}> |
| | | {orderDetail.refundReason || '-'} |
| | | <Form.Item label={<span style={{ fontSize: 14 }}>退款原因</span>}> |
| | | <div |
| | | style={{ |
| | | padding: '12px', |
| | | backgroundColor: '#f5f5f5', |
| | | borderRadius: '4px', |
| | | fontSize: 14, |
| | | color: '#333', |
| | | minHeight: 60, |
| | | }} |
| | | > |
| | | {refundReason} |
| | | </div> |
| | | </Form.Item> |
| | | |
| | |
| | | </Radio.Group> |
| | | </Form.Item> |
| | | |
| | | <Form.Item |
| | | name="auditRemark" |
| | | label={<span style={{ fontSize: 14 }}>审批意见</span>} |
| | | > |
| | | <Form.Item name="auditRemark" label={<span style={{ fontSize: 14 }}>审批意见</span>}> |
| | | <Input.TextArea |
| | | rows={4} |
| | | placeholder="请输入审批意见" |
| | |
| | | </Form> |
| | | )} |
| | | |
| | | {/* 已审核状态显示 */} |
| | | {!isAuditing && ( |
| | | <> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span>退款金额: ¥{orderDetail.refundAmount || '0.00'}</span> |
| | | <span>退款金额: ¥{refundMoney}</span> |
| | | </div> |
| | | {orderDetail.handlingFee && ( |
| | | {orderDetail.handlingFee !== undefined && orderDetail.handlingFee !== null && ( |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span>手续费: ¥{orderDetail.handlingFee}</span> |
| | | <span>手续费: ¥{handlingFee}</span> |
| | | </div> |
| | | )} |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span>退款原因: {orderDetail.refundReason || '-'}</span> |
| | | <span>退款原因: {refundReason}</span> |
| | | </div> |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span>审核结果: {auditStatusMap[auditStatus] || '-'}</span> |
| | | <span>审核结果: {auditStatusMap[status] || '-'}</span> |
| | | </div> |
| | | {orderDetail.auditRemark && ( |
| | | {auditRemark && ( |
| | | <div style={{ marginBottom: 12, fontSize: 14, color: '#333' }}> |
| | | <span>审批意见: {orderDetail.auditRemark}</span> |
| | | <span>审批意见: {auditRemark}</span> |
| | | </div> |
| | | )} |
| | | </> |
| | |
| | | width={800} |
| | | destroyOnClose |
| | | > |
| | | <Spin spinning={loading}> |
| | | {renderDetailContent()} |
| | | </Spin> |
| | | <Spin spinning={detailLoading}>{renderDetailContent()}</Spin> |
| | | </Modal> |
| | | ); |
| | | }; |
| | | |
| | | export default RefundDetailModal; |
| | | export default RefundDetailModal; |
| | |
| | | const actionRef = useRef(); |
| | | const [searchParams, setSearchParams] = useState({}); |
| | | const [detailVisible, setDetailVisible] = useState(false); |
| | | const [currentId, setCurrentId] = useState(null); |
| | | const [currentRecord, setCurrentRecord] = useState(null); |
| | | |
| | | // 状态映射 |
| | | const auditStatusMap = { |
| | | 1: { text: '待审核', status: 'Warning' }, |
| | | 2: { text: '审核通过', status: 'Success' }, |
| | | 3: { text: '审核驳回', status: 'Error' }, |
| | | }; |
| | | |
| | | const paymentTypeMap = { |
| | | 1: '订金+尾款', |
| | | 2: '全款', |
| | | 3: { text: '待审核', status: 'Warning' }, |
| | | 4: { text: '审核通过', status: 'Success' }, |
| | | 5: { text: '审核驳回', status: 'Error' }, |
| | | }; |
| | | |
| | | const columns = [ |
| | |
| | | }, |
| | | { |
| | | title: '订单编号', |
| | | dataIndex: 'code', |
| | | dataIndex: 'orderNo', |
| | | hideInSearch: true, |
| | | width: 180, |
| | | copyable: true, |
| | |
| | | search: false, |
| | | }, |
| | | { |
| | | // 与订单列表保持一致的命名:门票信息 |
| | | title: '门票信息', |
| | | dataIndex: 'bigGiftPackageName', |
| | | // 支持按照门票信息搜索 |
| | | dataIndex: 'ticketInfo', |
| | | width: 200, |
| | | hideInSearch: false, |
| | | fieldProps: { |
| | |
| | | ellipsis: true, |
| | | }, |
| | | { |
| | | title: '支付方式', |
| | | dataIndex: 'paymentType', |
| | | hideInSearch: true, |
| | | valueType: 'select', |
| | | valueEnum: paymentTypeMap, |
| | | width: 120, |
| | | }, |
| | | { |
| | | title: '定金', |
| | | dataIndex: 'deposit', |
| | | hideInSearch: true, |
| | | width: 100, |
| | | render: (text) => `¥${text}`, |
| | | search: false, |
| | | }, |
| | | { |
| | | title: '退款', |
| | | dataIndex: 'refundAmount', |
| | | hideInSearch: true, |
| | | width: 100, |
| | | render: (text) => `¥${text || '0.00'}`, |
| | | search: false, |
| | | }, |
| | | { |
| | | title: '审核状态', |
| | | dataIndex: 'auditStatus', |
| | | // 审核状态下拉搜索 |
| | | dataIndex: 'status', |
| | | hideInSearch: false, |
| | | valueType: 'select', |
| | | valueEnum: auditStatusMap, |
| | | width: 120, |
| | | render: (_, record) => auditStatusMap[record.status]?.text || '-', |
| | | }, |
| | | { |
| | | title: '操作', |
| | |
| | | type="link" |
| | | size="small" |
| | | onClick={() => { |
| | | setCurrentId(record.id); |
| | | setCurrentRecord(record); |
| | | setDetailVisible(true); |
| | | }} |
| | | > |
| | | 审核 |
| | | {record.status === 3 ? '审核' : '详情'} |
| | | </Button> |
| | | </Access> |
| | | <Access accessible={access.refundAudit_del || false}> |
| | |
| | | {/* 详情弹窗 */} |
| | | <RefundDetailModal |
| | | visible={detailVisible} |
| | | orderId={currentRecord?.id} |
| | | onCancel={() => { |
| | | setDetailVisible(false); |
| | | setCurrentId(null); |
| | | setCurrentRecord(null); |
| | | }} |
| | | id={currentId} |
| | | onSuccess={() => { |
| | | setDetailVisible(false); |
| | | setCurrentRecord(null); |
| | | actionRef.current?.reload(); |
| | | }} |
| | | record={currentRecord} |
| | | /> |
| | | </PageContainer> |
| | | ); |
| | | }; |
| | | |
| | | export default RefundAudit; |
| | | export default RefundAudit; |