hejianhao
2025-12-25 c5dc20f60ac873fcda172b019f9a99368ca4e2f7
feat: 新增公司信息、大礼包订单和退款记录功能模块

添加公司信息管理页面,包含基本信息展示、人员管理、推广记录和收益明细功能
新增大礼包订单列表页面,支持订单查看和退款申请操作
实现退款申请记录功能,包含列表展示和详情查看
12个文件已添加
2个文件已修改
1569 ■■■■■ 已修改文件
management/config/routes.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/locales/zh-CN/menu.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/bigGiftPackage/components/RefundApplyModal.jsx 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/bigGiftPackage/index.jsx 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/bigGiftPackage/service.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/components/CompanyProfitDetail.jsx 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/components/CompanyPromotionRecord.jsx 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/components/CompanyQrCodeModal.jsx 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/components/CompanyUserManagement.jsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/index.jsx 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/service.js 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/refundRecords/components/RefundDetailModal.jsx 326 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/refundRecords/index.jsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/refundRecords/service.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/config/routes.ts
@@ -250,4 +250,19 @@
      }
    ]
  },
  {
    "name": "公司信息",
    "path": "/company-info",
    "component": "./companyInfo"
  },
  {
    "name": "大礼包订单列表",
    "path": "/bigGiftPackage",
    "component": "./bigGiftPackage"
  },
  {
    "name": "退款申请记录",
    "path": "/refundRecords",
    "component": "./refundRecords/index"
  },
];
management/src/locales/zh-CN/menu.ts
@@ -53,7 +53,10 @@
  'menu.order.bigGiftPackageOrder': '大礼包订单列表',
  'menu.order.physicalGoodsOrder': '实物商品订单',
  'menu.order.refundAudit': '退款申请审核',
  'menu.order.bigGiftPackageRefund': '大礼包订单退款',
  'menu.refundRecords': '退款申请记录',
  'menu.finance': '财务管理',
  'menu.finance.integralSettlement': '积分结算',
  'menu.finance.summaryStatistics': '汇总统计',
  'menu.companyInfo': '公司信息',
};
management/src/pages/bigGiftPackage/components/RefundApplyModal.jsx
New file
@@ -0,0 +1,196 @@
import { Modal, Form, Input, Upload, Button, Space, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useRef, useState } from 'react';
import { customRequest } from '@/utils/utils';
const RefundApplyModal = ({ visible, onCancel, orderId, onSuccess }) => {
  const [form] = Form.useForm();
  const [loading, setLoading] = useState(false);
  const [fileList, setFileList] = useState([]);
  const [previewImage, setPreviewImage] = useState('');
  const [previewVisible, setPreviewVisible] = useState(false);
  // 上传前验证
  const beforeUpload = (file) => {
    const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg';
    if (!isJpgOrPng) {
      message.error('只能上传 JPG/PNG 格式的图片!');
      return Upload.LIST_IGNORE;
    }
    const isLt5M = file.size / 1024 / 1024 < 5;
    if (!isLt5M) {
      message.error('图片大小不能超过 5MB!');
      return Upload.LIST_IGNORE;
    }
    return true;
  };
  // 上传成功回调
  const handleUploadSuccess = (response) => {
    const newFile = {
      uid: response.name + Date.now(),
      name: response.name || `图片${fileList.length + 1}`,
      status: 'done',
      url: IMAGE_URL + response,
      response,
    };
    setFileList([...fileList, newFile]);
  };
  // 删除图片
  const handleRemove = (file) => {
    const newFileList = fileList.filter((item) => item.uid !== file.uid);
    setFileList(newFileList);
  };
  // 预览图片
  const handlePreview = (file) => {
    setPreviewImage(file.url || file.response);
    setPreviewVisible(true);
  };
  const handleSubmit = async () => {
    try {
      const values = await form.validateFields();
      // 如果没有上传图片,使用空字符串
      const voucherPicture = fileList.length > 0
        ? fileList.map(file => file.url || file.response).join(',')
        : '';
      setLoading(true);
      const res = await applyRefund({
        orderId,
        refundReason: values.refundReason,
        voucherPicture,
      });
      if (res?.code === 200) {
        message.success('退款申请提交成功');
        form.resetFields();
        setFileList([]);
        onCancel();
        onSuccess?.();
      } else {
        message.error(res?.message || '退款申请提交失败');
      }
    } catch (error) {
      console.error('退款申请失败:', error);
    } finally {
      setLoading(false);
    }
  };
  const uploadButton = (
    <div style={{
      display: 'flex',
      flexDirection: 'column',
      alignItems: 'center',
      justifyContent: 'center',
      height: '100%',
      color: '#8c8c8c',
      border: '1px dashed #d9d9d9',
      borderRadius: 4,
      backgroundColor: '#fafafa',
    }}>
      <PlusOutlined style={{ fontSize: 24, marginBottom: 8 }} />
      <div style={{ fontSize: 12 }}>上传图片</div>
    </div>
  );
  return (
    <>
      <Modal
        title="申请退款"
        open={visible}
        onCancel={() => {
          form.resetFields();
          setFileList([]);
          onCancel();
        }}
        footer={
          <Space>
            <Button onClick={() => {
              form.resetFields();
              setFileList([]);
              onCancel();
            }}>
              取消
            </Button>
            <Button
              type="primary"
              loading={loading}
              onClick={handleSubmit}
            >
              提交申请
            </Button>
          </Space>
        }
        width={700}
      >
        <Form
          form={form}
          layout="vertical"
          initialValues={{
            refundReason: '',
          }}
        >
          <Form.Item
            name="refundReason"
            label="退款原因"
            rules={[
              { required: true, message: '请输入退款原因' },
              { max: 500, message: '退款原因最多500字符' }
            ]}
          >
            <Input.TextArea
              rows={4}
              placeholder="请详细描述退款原因..."
              showCount
              maxLength={500}
            />
          </Form.Item>
          <Form.Item
            name="voucherPicture"
            label="凭证图片"
            extra="支持上传图片作为退款凭证,可上传多张"
          >
            <Upload
              action="/open/file/upload"
              listType="picture-card"
              fileList={fileList}
              beforeUpload={beforeUpload}
              customRequest={(params) => customRequest(params, 2)}
              onSuccess={handleUploadSuccess}
              onRemove={handleRemove}
              onPreview={handlePreview}
              accept="image/*"
            >
              {fileList.length >= 5 ? null : uploadButton}
            </Upload>
          </Form.Item>
        </Form>
      </Modal>
      {/* 图片预览 */}
      {previewVisible && (
        <Modal
          open={previewVisible}
          footer={null}
          onCancel={() => setPreviewVisible(false)}
          width={600}
        >
          <img
            alt="预览图片"
            style={{ width: '100%' }}
            src={previewImage}
          />
        </Modal>
      )}
    </>
  );
};
export default RefundApplyModal;
management/src/pages/bigGiftPackage/index.jsx
New file
@@ -0,0 +1,220 @@
import { ProTable, Modal, message } from '@ant-design/pro-components';
import { useRef, useState } from 'react';
import { Button, Space } from 'antd';
import { getRefundOrderList } from './service';
import OrderDetailModal from '../order/bigGiftPackageOrder/components/OrderDetailModal';
import RefundApplyModal from './components/RefundApplyModal';
import { buildProTableDataSource } from '@/utils/antdUtils';
const BigGiftPackageRefundOrder = () => {
  const actionRef = useRef();
  const [detailVisible, setDetailVisible] = useState(false);
  const [refundVisible, setRefundVisible] = useState(false);
  const [currentOrderId, setCurrentOrderId] = useState(null);
  // 支付类型映射
  const paymentTypeMap = {
    1: '订金+尾款',
    2: '全款',
  };
  // 订单状态映射
  const statusMap = {
    1: { text: '待付款', color: '#faad14' },
    2: { text: '已付订金', color: '' },
    3: { text: '完成支付', color: '#52c41a' },
    4: { text: '确认收货', color: '#1890ff' },
    5: { text: '已退款', color: '#722ed1' },
  };
  const handleDetail = (id) => {
    setCurrentOrderId(id);
    setDetailVisible(true);
  };
  const handleRefund = (id) => {
    setCurrentOrderId(id);
    setRefundVisible(true);
  };
  const columns = [
    {
      title: '序号',
      dataIndex: 'index',
      hideInSearch: true,
      align: 'center',
      width: 60,
      render: (text, record, index) => index + 1,
    },
    {
      title: '所属公司',
      dataIndex: 'companyName',
      width: 150,
      ellipsis: true,
      search: false,
    },
    {
      title: '订单编号',
      dataIndex: 'code',
      width: 180,
      ellipsis: true,
    },
    {
      title: '下单时间',
      dataIndex: 'createTime',
      valueType: 'dateRange',
      width: 200,
      search: {
        transform: (value) => {
          return {
            time: value ? value.join(' - ') : undefined,
          };
        },
      },
      render: (_, record) => record.createTime || '-',
    },
    {
      title: '下单用户',
      dataIndex: 'userName',
      width: 120,
      ellipsis: true,
      search: false,
    },
    {
      title: '用户手机号',
      dataIndex: 'phone',
      width: 130,
      ellipsis: true,
    },
    {
      title: '订单价格',
      dataIndex: 'orderPrice',
      width: 120,
      hideInSearch: true,
      render: (text) => `¥${text || 0}`,
    },
    {
      title: '商品名称',
      dataIndex: 'bigGiftPackageName',
      width: 200,
      ellipsis: true,
      hideInSearch: true,
    },
    {
      title: '支付方式',
      dataIndex: 'paymentType',
      width: 120,
      hideInSearch: true,
      render: (text) => paymentTypeMap[text] || '-',
    },
    {
      title: '定金',
      dataIndex: 'deposit',
      width: 100,
      hideInSearch: true,
      render: (text) => `¥${text || 0}`,
    },
    {
      title: '尾款',
      dataIndex: 'finalPayment',
      width: 100,
      hideInSearch: true,
      render: (text) => `¥${text || 0}`,
    },
    {
      title: '订单状态',
      dataIndex: 'status',
      valueType: 'select',
      width: 120,
      valueEnum: {
        1: { text: '待付款' },
        2: { text: '已付订金' },
        3: { text: '完成支付' },
        4: { text: '确认收货' },
        5: { text: '已退款' },
      },
      render: (status, record) => {
        const statusInfo = statusMap[record.status] || { text: '未知', color: '#d9d9d9' };
        return (
          <span style={{
            color: statusInfo.color,
            fontWeight: 500
          }}>
            {statusInfo.text}
          </span>
        );
      },
    },
    {
      title: '操作',
      dataIndex: 'action',
      width: 180,
      hideInSearch: true,
      render: (_, record) => (
        <Space>
          <Button
            type="link"
            size="small"
            onClick={() => handleDetail(record.id)}
          >
            详情
          </Button>
          {/* 只有已支付、已付订金、确认收货状态的订单可以申请退款 */}
          {record.status !== 1 && record.status !== 5 && (
            <Button
              type="link"
              size="small"
              onClick={() => handleRefund(record.id)}
            >
              申请退款
            </Button>
          )}
        </Space>
      ),
    },
  ];
  return (
    <div>
      <ProTable
        actionRef={actionRef}
        rowKey="id"
        cardBordered
        search={{
          labelWidth: 'auto',
          defaultCollapsed: false,
        }}
        request={(params) => buildProTableDataSource(getRefundOrderList, params)}
        columns={columns}
        pagination={{
          defaultPageSize: 10,
          showQuickJumper: true,
          showSizeChanger: true,
        }}
        scroll={{ x: 1600 }}
      />
      {/* 订单详情弹窗 */}
      <OrderDetailModal
        visible={detailVisible}
        onCancel={() => setDetailVisible(false)}
        orderId={currentOrderId}
      />
      {/* 退款申请弹窗 */}
      <RefundApplyModal
        visible={refundVisible}
        onCancel={() => setRefundVisible(false)}
        orderId={currentOrderId}
        onSuccess={() => {
          // 刷新列表
          actionRef.current?.reload();
          message.success('退款申请已提交');
        }}
      />
    </div>
  );
};
export default BigGiftPackageRefundOrder;
management/src/pages/bigGiftPackage/service.js
New file
@@ -0,0 +1,24 @@
import { request } from '@umijs/max';
// 获取大礼包订单列表
export const getRefundOrderList = async (data) => {
  return request('/agent/t-big-gift-package-order/pageList', {
    method: 'POST',
    data,
  });
};
// 获取订单详情
export const getOrderDetail = async (id) => {
  return request(`/agent/t-big-gift-package-order/detail/${id}`, {
    method: 'GET',
  });
};
// 申请退款
export const applyRefund = async (data) => {
  return request('/agent/t-big-gift-package-order/applyRefund', {
    method: 'POST',
    data,
  });
};
management/src/pages/companyInfo/components/CompanyProfitDetail.jsx
New file
@@ -0,0 +1,120 @@
import { ProTable } from '@ant-design/pro-components';
import { useRef, useState } from 'react';
import { getProfitPageList } from '../service';
import OrderDetailModal from '@/pages/order/bigGiftPackageOrder/components/OrderDetailModal';
import { buildProTableDataSource } from '@/utils/antdUtils';
const CompanyProfitDetail = ({ companyId }) => {
  const actionRef = useRef();
  const [orderDetailVisible, setOrderDetailVisible] = useState(false);
  const [currentOrderId, setCurrentOrderId] = useState(null);
  const showOrderDetail = (id) => {
    setCurrentOrderId(id);
    setOrderDetailVisible(true);
  };
  const columns = [
    {
      title: '序号',
      dataIndex: 'index',
      align: 'center',
      hideInSearch: true,
      render: (_, __, index) => index + 1,
    },
    {
      title: '订单编号',
      dataIndex: 'code',
      align: 'center',
    },
    {
      title: '下单时间',
      dataIndex: 'createTime',
      align: 'center',
      valueType: 'dateRange',
      hideInTable: true,
      fieldProps: {
        placeholder: ['开始时间', '结束时间'],
        format: 'YYYY-MM-DD',
      },
    },
    {
      title: '下单用户',
      dataIndex: 'userName',
      align: 'center',
      fieldProps: {
        placeholder: '请输入用户姓名',
      },
    },
    {
      title: '用户手机号',
      dataIndex: 'phone',
      align: 'center',
      fieldProps: {
        placeholder: '请输入用户手机号',
      },
    },
    {
      title: '订单价格',
      dataIndex: 'orderPrice',
      align: 'center',
      hideInSearch: true,
      render: (orderPrice) => (
        <span>{orderPrice ? Number(orderPrice).toFixed(2) : '0.00'}</span>
      ),
    },
    {
      title: '本人收益金额',
      dataIndex: 'saleProfit',
      align: 'center',
      hideInSearch: true,
      render: (saleProfit) => (
        <span style={{ color: '#52c41a', fontWeight: 'bold' }}>
          {saleProfit ? Number(saleProfit).toFixed(2) : '0.00'}
        </span>
      ),
    },
    {
      title: '操作',
      dataIndex: 'action',
      align: 'center',
      hideInSearch: true,
      render: (_, record) => (
        <a
          onClick={() => showOrderDetail(record.id)}
          style={{ color: '#1890ff' }}
        >
          详情
        </a>
      ),
    },
  ];
  return (
    <>
      <ProTable
        rowKey="id"
        columns={columns}
        actionRef={actionRef}
        cardBordered
        request={async (params) => buildProTableDataSource(getProfitPageList, { companyId, ...params })}
        search={{
          labelWidth: 'auto',
          defaultCollapsed: false,
        }}
        pagination={{
          defaultPageSize: 10,
          showQuickJumper: true,
        }}
      />
      <OrderDetailModal
        visible={orderDetailVisible}
        onCancel={() => setOrderDetailVisible(false)}
        orderId={currentOrderId}
      />
    </>
  );
};
export default CompanyProfitDetail;
management/src/pages/companyInfo/components/CompanyPromotionRecord.jsx
New file
@@ -0,0 +1,166 @@
import { ProTable } from '@ant-design/pro-components';
import { useRef } from 'react';
import { getPromotionPageList } from '../service';
import { Tag } from 'antd';
import { buildProTableDataSource } from '@/utils/antdUtils';
const CompanyPromotionRecord = ({ companyId }) => {
  const actionRef = useRef();
  const columns = [
    {
      title: '上级用户',
      dataIndex: 'lastUserName',
      align: 'center',
      hideInSearch: true,
    },
    {
      title: '上级用户电话',
      dataIndex: 'lastPhone',
      align: 'center',
      hideInSearch: true,
    },
    {
      title: '上级用户角色',
      dataIndex: 'lastRoleName',
      align: 'center',
      hideInSearch: true,
      render: (lastRoleName) => {
        const roleMap = {
          1: { text: '游客', color: 'default' },
          2: { text: '营销员', color: 'blue' },
          3: { text: '合伙人', color: 'green' },
        };
        const role = roleMap[lastRoleName] || { text: '未知', color: 'default' };
        return <Tag color={role.color}>{role.text}</Tag>;
      },
    },
    {
      title: '上级用户',
      dataIndex: 'lastUserName',
      align: 'center',
      hideInTable: true,
      fieldProps: {
        placeholder: '请输入上级用户姓名',
      },
    },
    {
      title: '上级手机号',
      dataIndex: 'lastPhone',
      align: 'center',
      hideInTable: true,
      fieldProps: {
        placeholder: '请输入上级用户电话',
      },
    },
    {
      title: '头像',
      dataIndex: 'lastAvatar',
      align: 'center',
      hideInSearch: true,
      hideInTable: true,
      render: (_, record) => (
        record.lastAvatar ? (
          <img
            src={record.lastAvatar}
            alt="头像"
            style={{ width: 32, height: 32, borderRadius: '50%' }}
          />
        ) : (
          <span>-</span>
        )
      ),
    },
    {
      title: '用户名',
      dataIndex: 'userName',
      align: 'center',
      fieldProps: {
        placeholder: '请输入用户名',
      },
    },
    {
      title: '联系电话',
      dataIndex: 'phone',
      align: 'center',
      fieldProps: {
        placeholder: '请输入联系电话',
      },
    },
    {
      title: '当前角色',
      dataIndex: 'roleName',
      align: 'center',
      hideInSearch: true,
      render: (roleName) => {
        const roleMap = {
          1: '游客',
          2: '营销员',
          3: '合伙人',
        };
        return roleMap[roleName] || '未知';
      },
    },
    {
      title: '状态',
      dataIndex: 'status',
      align: 'center',
      hideInSearch: true,
      render: (status) => {
        const statusMap = {
          1: { text: '正常', color: 'green' },
          2: { text: '冻结', color: 'red' },
        };
        const statusInfo = statusMap[status] || { text: '未知', color: 'default' };
        return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
      },
    },
    {
      title: '合计消费',
      dataIndex: 'totalConsume',
      align: 'center',
      hideInSearch: true,
      render: (totalConsume) => (
        <span>{totalConsume ? Number(totalConsume).toFixed(2) : '0.00'}</span>
      ),
    },
    {
      title: '合计收益',
      dataIndex: 'totalIncome',
      align: 'center',
      hideInSearch: true,
      render: (totalIncome) => (
        <span>{totalIncome ? Number(totalIncome).toFixed(2) : '0.00'}</span>
      ),
    },
    {
      title: '已结算收益',
      dataIndex: 'totalSettledIncome',
      align: 'center',
      hideInSearch: true,
      render: (totalSettledIncome) => (
        <span>{totalSettledIncome ? Number(totalSettledIncome).toFixed(2) : '0.00'}</span>
      ),
    },
  ];
  return (
    <ProTable
      rowKey="userId"
      columns={columns}
      actionRef={actionRef}
      cardBordered
      request={async (params) => buildProTableDataSource(getPromotionPageList, { companyId, ...params })}
      search={{
        labelWidth: 'auto',
        defaultCollapsed: false,
      }}
      pagination={{
        defaultPageSize: 10,
        showQuickJumper: true,
      }}
    />
  );
};
export default CompanyPromotionRecord;
management/src/pages/companyInfo/components/CompanyQrCodeModal.jsx
New file
@@ -0,0 +1,89 @@
import { Button, Modal, Space, Typography } from 'antd';
import { forwardRef, useImperativeHandle, useState } from 'react';
import { DownloadOutlined } from '@ant-design/icons';
import { request } from '@umijs/max';
const { Title, Paragraph } = Typography;
const CompanyQrCodeModal = ({ visible, onCancel }, ref) => {
  const [loading, setLoading] = useState(false);
  const [qrCodeUrl, setQrCodeUrl] = useState('');
  useImperativeHandle(ref, () => {
    return {
      showQrCode: (url) => {
        setQrCodeUrl(url);
        setVisible(true);
      },
    };
  });
  const downloadQrCode = async () => {
    if (!qrCodeUrl) return;
    setLoading(true);
    try {
      const response = await request(qrCodeUrl, {
        method: 'GET',
        responseType: 'blob',
      });
      const url = window.URL.createObjectURL(new Blob([response]));
      const link = document.createElement('a');
      link.href = url;
      link.setAttribute('download', `推广二维码.png`);
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      window.URL.revokeObjectURL(url);
    } catch (error) {
      console.error('下载二维码失败:', error);
    } finally {
      setLoading(false);
    }
  };
  return (
    <Modal
      getContainer={false}
      width={500}
      centered
      destroyOnHidden
      title="推广二维码"
      open={visible}
      onCancel={onCancel}
      footer={
        <Space>
          <Button onClick={onCancel}>关闭</Button>
          {qrCodeUrl && (
            <Button
              type="primary"
              icon={<DownloadOutlined />}
              loading={loading}
              onClick={downloadQrCode}
            >
              下载二维码
            </Button>
          )}
        </Space>
      }
    >
      <div style={{ textAlign: 'center', padding: '20px 0' }}>
        <Title level={4}>公司推广二维码</Title>
        {qrCodeUrl ? (
          <div style={{ marginTop: 20 }}>
            <img
              src={qrCodeUrl}
              alt="推广二维码"
              style={{ maxWidth: '100%', height: 'auto', border: '1px solid #d9d9d9', borderRadius: 4 }}
            />
          </div>
        ) : (
          <Paragraph type="warning">暂无推广二维码</Paragraph>
        )}
      </div>
    </Modal>
  );
};
export default forwardRef(CompanyQrCodeModal);
management/src/pages/companyInfo/components/CompanyUserManagement.jsx
New file
@@ -0,0 +1,61 @@
import { ProTable } from '@ant-design/pro-components';
import { useRef } from 'react';
import { getCompanyUserList } from '../service';
import { buildProTableDataSource } from '@/utils/antdUtils';
const CompanyUserManagement = () => {
  const actionRef = useRef();
  const columns = [
    {
      title: '人员姓名',
      dataIndex: 'nickName',
      align: 'center',
      width: 120,
    },
    {
      title: '推广ID',
      dataIndex: 'code',
      align: 'center',
      width: 120,
    },
    {
      title: '手机号',
      dataIndex: 'phonenumber',
      align: 'center',
      width: 130,
    },
    {
      title: '创建时间',
      dataIndex: 'createTime',
      align: 'center',
      width: 160,
    },
    {
      title: '角色',
      dataIndex: 'roleName',
      align: 'center',
      width: 100,
      render: (roleName) => (
        <span>{roleName || '-'}</span>
      ),
    },
  ];
  return (
    <ProTable
      rowKey="id"
      columns={columns}
      actionRef={actionRef}
      cardBordered
      request={async (params) => buildProTableDataSource(getCompanyUserList, params)}
      search={false}
      pagination={{
        defaultPageSize: 10,
        showQuickJumper: true,
      }}
    />
  );
};
export default CompanyUserManagement;
management/src/pages/companyInfo/index.jsx
New file
@@ -0,0 +1,131 @@
import { Card, Descriptions, Tabs, Image } from 'antd';
import { PageContainer } from '@ant-design/pro-components';
import { useEffect, useState, useRef } from 'react';
import { getCompanyDetail } from './service';
import CompanyUserManagement from './components/CompanyUserManagement';
import CompanyPromotionRecord from './components/CompanyPromotionRecord';
import CompanyProfitDetail from './components/CompanyProfitDetail';
import CompanyQrCodeModal from './components/CompanyQrCodeModal';
const CompanyInfo = () => {
  const [companyInfo, setCompanyInfo] = useState({});
  const [loading, setLoading] = useState(true);
  const [qrCodeModalVisible, setQrCodeModalVisible] = useState(false);
  const qrCodeModalRef = useRef();
  useEffect(() => {
    fetchCompanyDetail();
  }, []);
  const fetchCompanyDetail = async () => {
    try {
      setLoading(true);
      const res = await getCompanyDetail();
      if (res?.code === 200) {
        setCompanyInfo(res.data);
        // 如果有推广二维码,显示二维码弹窗
        if (res.data.promotionQrCode) {
          qrCodeModalRef.current?.showQrCode(res.data.promotionQrCode);
        }
      }
    } catch (error) {
      console.error('获取公司详情失败:', error);
    } finally {
      setLoading(false);
    }
  };
  const handleQrCodeClick = () => {
    if (companyInfo.promotionQrCode) {
      qrCodeModalRef.current?.showQrCode(companyInfo.promotionQrCode);
    }
  };
  return (
    <PageContainer loading={loading}>
      <div style={{ padding: '0 24px' }}>
        <Card title="公司基本信息" style={{ marginBottom: 24 }}>
          <Descriptions column={3} bordered>
            <Descriptions.Item label="公司名称">
              {companyInfo.name || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="负责人">
              {companyInfo.chargePerson || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="联系方式">
              {companyInfo.chargePersonPhone || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="商户号">
              {companyInfo.merchantNumber || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="商户openId">
              {companyInfo.merchantOpenid || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="推广号">
              {companyInfo.code || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="所在地址" span={3}>
              {companyInfo.address || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="推广二维码" span={3}>
              {companyInfo.promotionQrCode ? (
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <Image
                    src={companyInfo.promotionQrCode}
                    alt="推广二维码"
                    width={80}
                    height={80}
                    style={{ cursor: 'pointer', border: '1px solid #d9d9d9', borderRadius: 4 }}
                    onClick={handleQrCodeClick}
                    preview={false}
                  />
                  <a onClick={handleQrCodeClick} style={{ color: '#1890ff' }}>
                    查看并下载
                  </a>
                </div>
              ) : (
                '-'
              )}
            </Descriptions.Item>
            <Descriptions.Item label="备注" span={3}>
              {companyInfo.remark || '-'}
            </Descriptions.Item>
          </Descriptions>
        </Card>
        <Card>
          <Tabs
            defaultActiveKey="1"
            destroyOnHidden
            items={[
              {
                key: '1',
                label: '管理人员列表',
                children: <CompanyUserManagement companyId={companyInfo.id} />,
              },
              {
                key: '2',
                label: '推广记录',
                children: <CompanyPromotionRecord companyId={companyInfo.id} />,
              },
              {
                key: '3',
                label: '收益明细',
                children: <CompanyProfitDetail companyId={companyInfo.id} />,
              },
            ]}
          />
        </Card>
      </div>
      <CompanyQrCodeModal
        visible={qrCodeModalVisible}
        onCancel={() => setQrCodeModalVisible(false)}
        ref={qrCodeModalRef}
      />
    </PageContainer>
  );
};
export default CompanyInfo;
management/src/pages/companyInfo/service.js
New file
@@ -0,0 +1,32 @@
import { request } from '@umijs/max';
// 获取公司详情
export const getCompanyDetail = async () => {
  return request('/agent/company/detail', {
    method: 'GET',
  });
};
// 获取管理人员列表
export const getCompanyUserList = async (data) => {
  return request('/agent/company/getCompanyUserList', {
    method: 'POST',
    data,
  });
};
// 获取推广记录列表
export const getPromotionPageList = async (data) => {
  return request('/agent/company/promotionPageList', {
    method: 'POST',
    data,
  });
};
// 获取收益明细列表
export const getProfitPageList = async (data) => {
  return request('/agent/company/profitPageList', {
    method: 'POST',
    data,
  });
};
management/src/pages/refundRecords/components/RefundDetailModal.jsx
New file
@@ -0,0 +1,326 @@
import { getRefundRecordsDetail } from '../service';
import { Modal, Descriptions, Image, Table, Typography } from 'antd';
import { useEffect, useState } from 'react';
import moment from 'moment';
const { Text } = Typography;
const RefundDetailModal = ({ visible, onCancel, id }) => {
  const [orderDetail, setOrderDetail] = useState(null);
  const [loading, setLoading] = useState(false);
  // 订单状态映射
  const statusMap = {
    1: '待付款',
    2: '已付订金',
    3: '完成支付',
    4: '确认收货',
    5: '已退款',
    6: '已取消',
  };
  // 审核状态映射
  const auditStatusMap = {
    1: '待审核',
    2: '审核通过',
    3: '审核驳回',
  };
  // 支付类型映射
  const paymentTypeMap = {
    1: '订金+尾款',
    2: '全款',
  };
  // 获取订单详情
  const fetchOrderDetail = async () => {
    if (!id) return;
    setLoading(true);
    try {
      const res = await getRefundRecordsDetail(id);
      if (res.code === 200 && res.data) {
        setOrderDetail(res.data);
      }
    } catch (error) {
      console.error('获取订单详情失败:', error);
    } finally {
      setLoading(false);
    }
  };
  useEffect(() => {
    if (visible && id) {
      fetchOrderDetail();
    }
  }, [visible, id]);
  // 渲染商品信息表格
  const renderProductTable = () => {
    const columns = [
      {
        title: '商品名称',
        dataIndex: 'bigGiftPackageName',
        width: 200,
        ellipsis: true,
      },
      {
        title: '封面图',
        dataIndex: 'bigGiftPackageCover',
        width: 100,
        render: (text) => (
          <Image
            width={60}
            height={60}
            src={text ? IMAGE_URL + text : null}
            style={{ objectFit: 'cover' }}
            placeholder={<div style={{ width: 60, height: 60, background: '#f5f5f5' }} />}
            fallback="/images/default-image.png"
          />
        ),
      },
      {
        title: '商品价格',
        dataIndex: 'bigGiftPackagePrice',
        width: 120,
        render: (text) => `¥${text || 0}`,
      },
      {
        title: '商品简介',
        dataIndex: 'bigGiftPackageIntro',
        ellipsis: true,
      },
      {
        title: '销量',
        dataIndex: 'bigGiftPackageSales',
        width: 80,
      },
    ];
    const dataSource = [{
      key: orderDetail?.id,
      ...orderDetail,
    }];
    return (
      <Table
        columns={columns}
        dataSource={dataSource}
        pagination={false}
        size="small"
      />
    );
  };
  // 渲染详情内容
  const renderDetailContent = () => {
    if (!orderDetail) return null;
    const { status, paymentType, auditStatus } = orderDetail;
    // 基础字段(所有状态都显示)
    const baseItems = [
      {
        label: '订单编号',
        children: orderDetail.code || '-',
      },
      {
        label: '下单时间',
        children: orderDetail.createTime ? moment(orderDetail.createTime).format('YYYY-MM-DD HH:mm:ss') : '-',
      },
      {
        label: '用户昵称',
        children: orderDetail.userName || '-',
      },
      {
        label: '用户手机号',
        children: orderDetail.phone || '-',
      },
      {
        label: '订单状态',
        children: statusMap[status] || '-',
      },
      {
        label: '审核状态',
        children: (
          <span style={{
            color:
              auditStatus === 1 ? '#faad14' :
              auditStatus === 2 ? '#52c41a' :
              '#f5222d'
          }}>
            {auditStatusMap[auditStatus] || auditStatus}
          </span>
        ),
      },
      {
        label: '订单金额',
        children: `¥${orderDetail.orderPrice || 0}`,
      },
      {
        label: '支付方式',
        children: paymentTypeMap[paymentType] || '-',
      },
    ];
    // 根据不同状态添加特定字段
    let specificItems = [];
    if (status >= 2) {
      // 已付定金或更高级状态
      specificItems = [
        {
          label: '定金',
          children: `¥${orderDetail.deposit || 0}`,
        },
        {
          label: '定金支付方式',
          children: '微信支付',
        },
        {
          label: '定金支付时间',
          children: orderDetail.depositTime ? moment(orderDetail.depositTime).format('YYYY-MM-DD HH:mm:ss') : '-',
        },
      ];
    }
    if (status === 3 && paymentType === 1) {
      // 已支付定金+尾款
      specificItems = [
        ...specificItems,
        {
          label: '已付尾款',
          children: `¥${orderDetail.finalPayment || 0}`,
        },
        {
          label: '尾款支付方式',
          children: '微信支付',
        },
        {
          label: '尾款支付时间',
          children: orderDetail.finalPaymentTime ? moment(orderDetail.finalPaymentTime).format('YYYY-MM-DD HH:mm:ss') : '-',
        },
      ];
    } else if (status === 3 && paymentType === 2) {
      // 已支付全款
      specificItems = [
        {
          label: '支付金额',
          children: `¥${orderDetail.paymentPrice || 0}`,
        },
        {
          label: '支付时间',
          children: orderDetail.depositTime ? moment(orderDetail.depositTime).format('YYYY-MM-DD HH:mm:ss') : '-',
        },
        {
          label: '支付方式',
          children: '微信支付',
        },
      ];
    }
    // 退款相关字段
    const refundItems = [
      {
        label: '退款金额',
        children: `¥${orderDetail.refundAmount || '0.00'}`,
      },
      {
        label: '申请退款原因',
        children: orderDetail.refundReason || '-',
      },
    ];
    // 审核相关字段
    const auditItems = [];
    if (auditStatus >= 2) {
      auditItems.push(
        {
          label: '审核结果',
          children: auditStatus === 2 ? '审核通过' : '审核驳回',
        },
        {
          label: '审核时间',
          children: orderDetail.auditTime ? moment(orderDetail.auditTime).format('YYYY-MM-DD HH:mm:ss') : '-',
        },
        {
          label: '审核人',
          children: orderDetail.auditorName || '-',
        },
        {
          label: '审核意见',
          children: orderDetail.auditRemark || '-',
        },
      );
    }
    return (
      <div>
        <Descriptions column={2} bordered loading={loading}>
          {[...baseItems, ...specificItems, ...refundItems, ...auditItems].map((item, index) => (
            <Descriptions.Item key={index} label={item.label}>
              {item.children}
            </Descriptions.Item>
          ))}
        </Descriptions>
        {/* 商品信息 */}
        <div style={{ marginTop: 24 }}>
          <Text strong>商品信息</Text>
          <div style={{ marginTop: 8 }}>
            {renderProductTable()}
          </div>
        </div>
        {/* 图片凭证 */}
        {orderDetail.refundImages && (
          <div style={{ marginTop: 24 }}>
            <Text strong>图片凭证</Text>
            <div style={{ marginTop: 8, display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {orderDetail.refundImages.split(',').map((img, index) => (
                <Image
                  key={index}
                  width={80}
                  height={80}
                  src={img}
                  alt={`凭证${index + 1}`}
                  style={{ objectFit: 'cover' }}
                  fallback="/images/default-image.png"
                />
              ))}
            </div>
          </div>
        )}
        {/* 备注 */}
        {orderDetail.remark && (
          <div style={{ marginTop: 24 }}>
            <Text strong>备注</Text>
            <div style={{ marginTop: 8 }}>
              {orderDetail.remark}
            </div>
          </div>
        )}
      </div>
    );
  };
  return (
    <Modal
      title="退款申请详情"
      open={visible}
      onCancel={onCancel}
      footer={[
        <button key="close" type="button" className="ant-btn" onClick={onCancel}>
          关闭
        </button>,
      ]}
      width={800}
      destroyOnHidden
    >
      {renderDetailContent()}
    </Modal>
  );
};
export default RefundDetailModal;
management/src/pages/refundRecords/index.jsx
New file
@@ -0,0 +1,170 @@
import { Button } from 'antd';
import { useRef, useState } from 'react';
import { PageContainer, ProTable } from '@ant-design/pro-components';
import RefundDetailModal from './components/RefundDetailModal';
import { getRefundRecordsList } from './service';
import { buildProTableDataSource } from '@/utils/antdUtils';
const RefundRecords = () => {
  const actionRef = useRef();
  const [detailVisible, setDetailVisible] = useState(false);
  const [currentId, setCurrentId] = useState(null);
  // 状态映射
  const auditStatusMap = {
    1: { text: '待审核', status: 'Warning' },
    2: { text: '审核通过', status: 'Success' },
    3: { text: '审核驳回', status: 'Error' },
  };
  const paymentTypeMap = {
    1: '订金+尾款',
    2: '全款',
  };
  const columns = [
    {
      title: '序号',
      dataIndex: 'index',
      hideInSearch: true,
      width: 60,
      render: (text, record, index) => index + 1,
    },
    {
      title: '订单编号',
      dataIndex: 'code',
      width: 180,
      copyable: true,
      ellipsis: true,
      order: 4,
      search: {
        transform: (value) => ({
          code: value,
        }),
      },
    },
    {
      title: '下单时间',
      dataIndex: 'createTime',
      valueType: 'dateRange',
      width: 240,
      order: 2,
      search: {
        transform: (value) => ({
          time: value ? value.join(' - ') : undefined,
        }),
      },
    },
    {
      title: '用户手机号',
      dataIndex: 'phone',
      width: 120,
      order: 3,
      search: {
        transform: (value) => ({
          phone: value,
        }),
      },
    },
    {
      title: '下单用户',
      dataIndex: 'userName',
      hideInSearch: true,
      width: 120,
      ellipsis: true,
    },
    {
      title: '订单价格',
      dataIndex: 'orderPrice',
      hideInSearch: true,
      width: 100,
      render: (text) => `¥${text}`,
      search: false,
    },
    {
      title: '商品名称',
      dataIndex: 'bigGiftPackageName',
      width: 200,
      hideInSearch: true,
      ellipsis: true,
      search: {
        transform: (value) => ({
          name: value,
        }),
      },
    },
    {
      title: '支付方式',
      dataIndex: 'paymentType',
      hideInSearch: true,
      valueType: 'select',
      valueEnum: paymentTypeMap,
      width: 120,
    },
    {
      title: '审核状态',
      dataIndex: 'auditStatus',
      valueType: 'select',
      valueEnum: auditStatusMap,
      order: 1,
      width: 120,
      search: {
        transform: (value) => ({
          auditStatus: value,
        }),
      },
    },
    {
      title: '操作',
      dataIndex: 'action',
      valueType: 'option',
      width: 100,
      fixed: 'right',
      render: (_, record) => (
        <Button
          type="link"
          size="small"
          onClick={() => {
            setCurrentId(record.id);
            setDetailVisible(true);
          }}
        >
          详情
        </Button>
      ),
    },
  ];
  return (
    <PageContainer>
      <ProTable
        actionRef={actionRef}
        rowKey="id"
        search={{
          labelWidth: 'auto',
          defaultCollapsed: false,
        }}
        request={(params) => buildProTableDataSource(getRefundRecordsList, params)}
        columns={columns}
        scroll={{ x: 1400 }}
        pagination={{
          showSizeChanger: true,
          showQuickJumper: true,
          defaultPageSize: 10,
        }}
      />
      {/* 详情弹窗 */}
      <RefundDetailModal
        visible={detailVisible}
        onCancel={() => {
          setDetailVisible(false);
          setCurrentId(null);
        }}
        id={currentId}
      />
    </PageContainer>
  );
};
export default RefundRecords;
management/src/pages/refundRecords/service.js
New file
@@ -0,0 +1,16 @@
import { request } from '@umijs/max';
// 获取退款申请记录列表
export async function getRefundRecordsList(params) {
  return request('/agent/t-big-gift-package-order/refundList', {
    method: 'POST',
    data: params,
  });
}
// 获取退款申请记录详情
export async function getRefundRecordsDetail(id) {
  return request(`/agent/t-big-gift-package-order/refundDetail/${id}`, {
    method: 'GET',
  });
}