zjk
2026-08-18 c302b155ff1b655aac27846643df0339b8382795
feat(order): 完成实物订单退款申请全流程开发

新增退款申请后端接口调用方法,统一订单相关页面的字段配置,重构订单详情弹窗组件接入真实退款逻辑,优化全流程交互与展示。具体修改包括:
- 新增`refundApply`接口对接后端退款申请链路
- 适配后端新字段,修正原有订单列表、退款审核页的字段不匹配问题
- 重构详情弹窗,替换占位退款逻辑,新增核销二维码展示,优化状态映射与布局
- 优化审核页面交互,退款操作成功后自动刷新列表并关闭弹窗
- 统一接口提示信息的获取方式,修复提示错误
5个文件已修改
950 ■■■■■ 已修改文件
management/src/pages/order/orderList/components/OrderDetailModal.jsx 392 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/orderList/index.jsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/orderList/service.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/refundAudit/components/RefundDetailModal.jsx 465 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/refundAudit/index.jsx 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/orderList/components/OrderDetailModal.jsx
@@ -1,22 +1,26 @@
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 () => {
@@ -26,15 +30,19 @@
        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]);
@@ -49,49 +57,8 @@
    }
  }, [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 () => {
@@ -119,25 +86,21 @@
      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 {
@@ -148,24 +111,24 @@
  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
@@ -174,125 +137,214 @@
        <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={{
@@ -300,8 +352,8 @@
                      padding: '12px 16px',
                      borderRadius: '4px',
                      display: 'flex',
                      gap: 16,
                      alignItems: 'center',
                      flexWrap: 'wrap',
                      gap: '8px 24px',
                    }}
                  >
                    <span>姓名: {visitor.name || visitor.visitorName || ''}</span>
@@ -313,8 +365,47 @@
            </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 && (
@@ -345,9 +436,6 @@
                    label="手续费"
                    name="handlingFee"
                    style={{ flex: '0 0 300px' }}
                    rules={[
                      { required: true, message: '请输入手续费' },
                    ]}
                  >
                    <InputNumber
                      min={0}
management/src/pages/order/orderList/index.jsx
@@ -53,6 +53,12 @@
    setCurrentOrderId(null);
  };
  // 退款申请成功后关闭弹窗并刷新列表
  const handleRefundSuccess = () => {
    handleCloseDetail();
    actionRef.current?.reload();
  };
  // 列定义
  const columns = [
    {
@@ -65,9 +71,9 @@
    },
    {
      title: '订单编号',
      dataIndex: 'code',
      dataIndex: 'orderNo',
      hideInSearch: true,
      key: 'code',
      key: 'orderNo',
    },
    {
      title: '下单时间',
@@ -99,19 +105,19 @@
    },
    {
      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: '订单状态',
@@ -198,6 +204,7 @@
        visible={detailModalVisible}
        isRefund={isRefundMode}
        onClose={handleCloseDetail}
        onSuccess={handleRefundSuccess}
      />
    </PageContainer>
  );
management/src/pages/order/orderList/service.js
@@ -15,9 +15,17 @@
  });
}
// 申请退款
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',
  });
}
}
management/src/pages/order/refundAudit/components/RefundDetailModal.jsx
@@ -1,54 +1,94 @@
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 () => {
@@ -57,20 +97,21 @@
      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 || '审核失败');
      }
@@ -82,92 +123,189 @@
    }
  };
  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>
              ))}
@@ -175,84 +313,101 @@
          </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>
@@ -267,10 +422,7 @@
                </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="请输入审批意见"
@@ -292,26 +444,25 @@
            </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>
              )}
            </>
@@ -330,11 +481,9 @@
      width={800}
      destroyOnClose
    >
      <Spin spinning={loading}>
        {renderDetailContent()}
      </Spin>
      <Spin spinning={detailLoading}>{renderDetailContent()}</Spin>
    </Modal>
  );
};
export default RefundDetailModal;
export default RefundDetailModal;
management/src/pages/order/refundAudit/index.jsx
@@ -12,19 +12,13 @@
  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 = [
@@ -37,7 +31,7 @@
    },
    {
      title: '订单编号',
      dataIndex: 'code',
      dataIndex: 'orderNo',
      hideInSearch: true,
      width: 180,
      copyable: true,
@@ -81,10 +75,8 @@
      search: false,
    },
    {
      // 与订单列表保持一致的命名:门票信息
      title: '门票信息',
      dataIndex: 'bigGiftPackageName',
      // 支持按照门票信息搜索
      dataIndex: 'ticketInfo',
      width: 200,
      hideInSearch: false,
      fieldProps: {
@@ -93,37 +85,13 @@
      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: '操作',
@@ -138,12 +106,11 @@
              type="link"
              size="small"
              onClick={() => {
                setCurrentId(record.id);
                setCurrentRecord(record);
                setDetailVisible(true);
              }}
            >
              审核
              {record.status === 3 ? '审核' : '详情'}
            </Button>
          </Access>
          <Access accessible={access.refundAudit_del || false}>
@@ -218,15 +185,20 @@
      {/* 详情弹窗 */}
      <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;