二手回收后台,师傅端,用户端
zjk
2026-09-04 943143722090e9fb6893aae359e01fa203e259eb
feat: 优化多个后台页面的功能与展示逻辑

- 完善加盟充值列表页:新增充值方式展示列与筛选器,添加线上/线下充值合计统计,修复导出接口参数
- 优化加盟详情页:支持虚拟充值撤回操作,新增支付银行、流水号等表单字段,调整弹窗标题与操作错误提示
- 重构订单统计页:优化城市筛选逻辑,新增待预约、已取消订单统计图表,修复图表点击跳转逻辑,调整页面布局样式
3个文件已修改
172 ■■■■■ 已修改文件
management/src/pages/order/statistics/index.jsx 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/toJoin/joinDetail.jsx 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/toJoin/payList.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/statistics/index.jsx
@@ -58,18 +58,20 @@
    setCurrent(1);
    setParams({ pageSize: size, pageNum: 1 })
    setTimeout(() => {
      getAllList({ ...values, pageSize: size, pageNum: 1 })
      getAllList({ ...values, pageSize: size, pageNum: 1, cityList: city })
    }, 1);
  };
  const save = () => {
    form.validateFields().then(async (values) => {
      console.log('value', values)
      setValues(values)
      const { city: citySelections, ...filters } = values
      const selectedCity = extractCityCodes(citySelections)
      setValues(filters)
      setCity(selectedCity)
      setCurrent(1);
      setParams({ pageSize: 10, pageNum: 1 })
      setTimeout(() => {
        getAllList({ ...values, pageSize: 10, pageNum: 1, cityList: city })
        getAllList({ ...filters, pageSize: 10, pageNum: 1, cityList: selectedCity })
      }, 1);
    })
  }
@@ -85,21 +87,14 @@
  }
  const handleCityChange = (e) => {
    if (e.length > 0) {
      let arr = []
      if (JSON.parse(localStorage.getItem('userInfo')).franchiseeId) {
        arr = e.map(item => {
          return item[0]
        })
      } else {
        arr = e.map(item => {
          return item[1]
        })
      }
      setCity(arr)
    } else {
      setCity([])
    }
    setCity(extractCityCodes(e))
  }
  const extractCityCodes = (selections = []) => {
    const isFranchisee = JSON.parse(localStorage.getItem('userInfo') || '{}').franchiseeId
    return selections
      .map(item => item[isFranchisee ? 0 : Math.min(1, item.length - 1)])
      .filter(Boolean)
  }
  useEffect(() => {
@@ -115,10 +110,14 @@
        let list1 = []
        let list2 = []
        let list3 = []
        let list4 = []
        let list5 = []
        let list = (res.data.records || []).map(item => {
          list1.unshift(item.toBeCompletedNumber || 0)
          list2.unshift(item.completedNumber || 0)
          list3.unshift(item.reInvestment || 0)
          list4.unshift(item.toBeReservedNumber || 0)
          list5.unshift(item.cancelledNumber || 0)
          return {
            name: maskString(item.realName),
            realName: item.realName,
@@ -127,17 +126,16 @@
          }
        })
        setTimeout(() => {
          console.log('list1, list2, list3', list1, list2, list3)
          setTotal(res.data.total)
          setInfo(list)
          const newHeight = calculateChartHeight(list.length);
          setChartHeight(newHeight);
          showEchart(list1, list2, list3, list)
          showEchart(list1, list2, list3, list4, list5, list)
        }, 10);
      }
    })
  }
  const showEchart = (value1, value2, value3, value4) => {
  const showEchart = (value1, value2, value3, value4, value5, workers) => {
    let chartDom = document.getElementById('main');
    let myChart = echarts.init(chartDom);
    let option;
@@ -161,6 +159,7 @@
        }
      },
      series: [{
        name: '待完工',
        data: value1,
        type: 'bar',
        barMaxWidth: 32,
@@ -173,6 +172,7 @@
          formatter: '{c}' // 格式化标签内容为数值
        }
      }, {
        name: '已完工',
        data: value2,
        type: 'bar',
        barMaxWidth: 32,
@@ -185,6 +185,7 @@
          formatter: '{c}' // 格式化标签内容为数值
        }
      }, {
        name: '再投单',
        data: value3,
        type: 'bar',
        barMaxWidth: 32,
@@ -196,11 +197,37 @@
          position: 'right', // 设置标签显示在柱状条右侧
          formatter: '{c}' // 格式化标签内容为数值
        }
      }, {
        name: '待预约',
        data: value4,
        type: 'bar',
        barMaxWidth: 32,
        itemStyle: {
          color: 'rgba(81, 137, 219, 0.48)'
        },
        label: {
          show: true,
          position: 'right',
          formatter: '{c}'
        }
      }, {
        name: '已取消',
        data: value5,
        type: 'bar',
        barMaxWidth: 32,
        itemStyle: {
          color: 'rgba(128, 128, 128, 0.45)'
        },
        label: {
          show: true,
          position: 'right',
          formatter: '{c}'
        }
      }]
    };
    option && myChart.setOption(option);
    // 监听窗口大小变化
    window.addEventListener('resize', () => {
      myChart.resize();
@@ -214,9 +241,10 @@
    myChart.on('click', function (params) {
      // params.name 是点击的柱体的名称(即 xAxis.data 中的值)
      // params.value 是点击的柱体的值(即 series.data 中的值)
      let status = params.componentIndex == 0 ? '1' : params.componentIndex == 1 ? '3' : '6'
      let leng = (value4 || []).length - Number(params.name) - 1
      const worker = value4[leng] || {}
      const statusBySeries = ['1', '3', '6', '7', '4']
      let status = statusBySeries[params.componentIndex]
      let leng = (workers || []).length - Number(params.name) - 1
      const worker = workers[leng] || {}
      history.push(`/order/list?workerId=${worker.workerId || ''}&workerName=${encodeURIComponent(worker.realName || '')}&status=${status}`)
      console.log('value4', value4)
      console.log('点击了柱体:', params.name, '值:', params.value, 'params params', params, 'info', leng, 'value4[leng]', value4[leng]);
@@ -224,7 +252,7 @@
  }
  return <div>
    <PageContainer title='订单统计' className='order-statistics'>
    <PageContainer title='订单统计' className='order-statistics' contentWidth="Fluid">
      <Card>
        <Form scrollToFirstError form={form} >
          <Row>
@@ -261,7 +289,7 @@
                <Input placeholder="请输入" style={{ width: "80%" }} />
              </Form.Item>
            </Col>
            <Col span={4}>
              <Space size='large' style={{ alignItems: "center" }}>
                <Button onClick={() => reset()}>重置</Button>
@@ -271,7 +299,7 @@
          </Row>
        </Form>
      </Card>
      <Card style={{ marginTop: '20px', overflow: 'auto', height: '563px' }}>
      <Card style={{ marginTop: '20px', overflow: 'auto', minHeight: '563px', width: '100%' }}>
        <div style={{ margin: '14px 0', display: 'flex' }}>
          <div style={{ display: 'flex', marginRight: '40px' }}>
            <div style={{ width: '42px', height: '20px', marginRight: '10px', background: '#E99D42 39%' }}></div>
@@ -285,6 +313,14 @@
            <div style={{ width: '42px', height: '20px', marginRight: '10px', background: '#DE868F 56%' }}></div>
            <div>再投单</div>
          </div>
          <div style={{ display: 'flex', marginRight: '40px' }}>
            <div style={{ width: '42px', height: '20px', marginRight: '10px', background: '#5189DB' }}></div>
            <div>待预约</div>
          </div>
          <div style={{ display: 'flex', marginRight: '40px' }}>
            <div style={{ width: '42px', height: '20px', marginRight: '10px', background: '#808080' }}></div>
            <div>已取消</div>
          </div>
        </div>
        <div>统计列表</div>
        <div style={{ height: '1px', borderTop: '1px solid #BBBBBB', width: '100%', marginBottom: '20px' }}></div>
management/src/pages/toJoin/joinDetail.jsx
@@ -44,6 +44,7 @@
  // 撤回
  const [revocationVisible, setRevocationVisible] = useState(false)
  const goToOrderDetail = async (code) => {
    try {
      const orderListRes = await getOrderList({
@@ -210,9 +211,10 @@
            >
              详情
            </Button>
            {record.type == 1 && (
            {[1, 5].includes(Number(record.type)) && (
              <Button
                type="link"
                disabled={revocationSubmitting}
                onClick={() => {
                  setSelectItem(record)
@@ -321,6 +323,8 @@
          handleVirtualVisible(false)
          getInfo()
          actionRef.current.reload()
        } else {
          message.error(res.msg || '操作失败')
        }
      }).finally(() => {
        setVirtualSubmitting(false)
@@ -431,7 +435,7 @@
          <Button onClick={() => history.back()}>返回</Button>
        </div>
        <Modal
          title="余额扣除撤回"
          title={Number(selectItem.type) === 5 ? '虚拟充值撤回' : '余额扣除撤回'}
          // closable={{ 'aria-label': 'Custom Close Button' }}
          open={revocationVisible}
          onOk={handleOk}
@@ -497,8 +501,20 @@
                  <div>{'虚拟充值'}</div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', marginBottom: '20px' }}>
                  <div style={{ marginRight: '20px', minWidth: '80px' }}>加盟商名称</div>
                  <div>{virtualType.franchiseeName}</div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', marginBottom: '20px' }}>
                  <div style={{ marginRight: '20px', minWidth: '80px' }}>金额(元)</div>
                  <div>{virtualType.amount}</div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', marginBottom: '20px' }}>
                  <div style={{ marginRight: '20px', minWidth: '80px' }}>支付银行</div>
                  <div>{virtualType.paymentBank}</div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', marginBottom: '20px' }}>
                  <div style={{ marginRight: '20px', minWidth: '80px' }}>支付流水号</div>
                  <div>{virtualType.virtualRechargeSerial || virtualType.code}</div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', marginBottom: '20px' }}>
                  <div style={{ marginRight: '20px', minWidth: '80px' }}>时间</div>
@@ -529,6 +545,9 @@
              </div>
            ) : (
              <Form layout="horizontal" {...formItemLayout} form={form}>
                <Form.Item label="加盟商名称">
                  <Input value={joinDetail.name || ''} disabled style={{ width: '280px' }} />
                </Form.Item>
                <Form.Item
                  name="amount"
                  label="充值金额"
@@ -536,6 +555,14 @@
                >
                  <InputNumber min={0} decimalSeparator=","
                    precision={2} placeholder="虚拟充值金额" style={{ width: '280px' }} />
                </Form.Item>
                <Form.Item
                  name="paymentBank"
                  label="支付银行"
                  rules={[{ required: true, message: '请输入支付银行' }]}
                >
                  <Input placeholder="请输入支付银行" style={{ width: '280px' }} />
                </Form.Item>
                <Form.Item
@@ -549,6 +576,17 @@
                  <Input placeholder="请输入操作人姓名" style={{ width: '280px' }} />
                </Form.Item>
                <Form.Item
                  name="virtualRechargeSerial"
                  label="支付流水号"
                  rules={[
                    { required: true, message: '请输入支付流水号' },
                    { pattern: /^[A-Za-z0-9]+$/, message: '只能输入英文和数字' },
                  ]}
                >
                  <Input maxLength={64} placeholder="请输入支付流水号" style={{ width: '280px' }} />
                </Form.Item>
                <Form.Item
                  name="remark"
management/src/pages/toJoin/payList.jsx
@@ -1,6 +1,6 @@
import { buildProTableDataSource, sendRequest, showDelConfirm } from '@/utils/antdUtils';
import { PageContainer, ProTable } from '@ant-design/pro-components';
import { Button, Space, DatePicker, Switch, message, Form, Row, Col, Input, Card,Image } from 'antd';
import { Button, Space, DatePicker, Switch, message, Form, Row, Col, Input, Card, Image, Select  } from 'antd';
import {useEffect,useRef, useState } from 'react';
import { history, useAccess, Access } from 'umi';
import { getMoneyList, del, enable ,getMoney} from './service';
@@ -37,6 +37,12 @@
      render: (_, record) => {
        return record.remark || record.payStr || '拉卡拉支付'
      }
    },
    {
      title: '充值方式',
      dataIndex: 'rechargeMethod',
      width: 160,
      render: (_, record) => record.rechargeMethod || (record.type === 5 ? '线下充值' : '线上充值'),
    },
    {
      title: '流水单号',
@@ -115,7 +121,19 @@
                <DatePicker.RangePicker  style={{ width: "380px" }} />
              </Form.Item>
            </Col>
            <Col span={8} style={{ textAlign: "end", paddingRight: '100px' }}>
             <Col span={4}>
              <Form.Item label="充值方式" name="rechargeChannel">
                <Select
                  allowClear
                  placeholder="全部"
                  options={[
                    { value: 1, label: '线上充值' },
                    { value: 2, label: '线下充值' },
                  ]}
                />
              </Form.Item>
            </Col>
            <Col span={4} style={{ textAlign: "end", paddingRight: '20px' }}>
              <Space size='large' >
                <Button type='primary' onClick={save}>查询</Button>
                <Button onClick={() => reset()}>重置</Button>
@@ -125,10 +143,18 @@
          </Row>
        </Form>
      </Card>
      <div style={{ padding: '20px 100px 20px 0', display: 'flex', alignItems: 'center', justifyContent: "space-between" }}>
      <div style={{ padding: '20px 100px 20px 0', display: 'flex', alignItems: 'center', gap: 80 }}>
        <div>
          <div style={{ display: 'flex', alignItems: 'center' }}><span style={{ fontSize: "36px" }}>{moneyDetail?.balance3||0}</span> 元</div>
          <div style={{ marginTop: '11px', fontSize: '16px' }}>充值合计</div>
        </div>
        <div>
          <div style={{ display: 'flex', alignItems: 'center' }}><span style={{ fontSize: "36px" }}>{moneyDetail?.onlineRechargeTotal||0}</span> 元</div>
          <div style={{ marginTop: '11px', fontSize: '16px' }}>线上合计</div>
        </div>
        <div>
          <div style={{ display: 'flex', alignItems: 'center' }}><span style={{ fontSize: "36px" }}>{moneyDetail?.offlineRechargeTotal||0}</span> 元</div>
          <div style={{ marginTop: '11px', fontSize: '16px' }}>线下合计</div>
        </div>
      </div>
      <ProTable
@@ -151,7 +177,7 @@
            ...searchForm,
            type:2,
          }
          setSearchForm(params)
          return buildProTableDataSource(getMoneyList, params)
        }}
@@ -170,7 +196,7 @@
                  //     actionRef.current.reload();
                  //   }
                  // }, '确认删除所选角色吗?');
                  let status = exportExcell('充值列表', { ...searchForm }, '/franchisee/recent/money/list/export')
                  let status = exportExcell('充值列表', { ...searchForm, type: 2 }, '/franchisee/recent/money/list/export')
                  if (status) {
                    actionRef.current.reload();
                  }