feat: 优化多个后台页面的功能与展示逻辑
- 完善加盟充值列表页:新增充值方式展示列与筛选器,添加线上/线下充值合计统计,修复导出接口参数
- 优化加盟详情页:支持虚拟充值撤回操作,新增支付银行、流水号等表单字段,调整弹窗标题与操作错误提示
- 重构订单统计页:优化城市筛选逻辑,新增待预约、已取消订单统计图表,修复图表点击跳转逻辑,调整页面布局样式
| | |
| | | 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); |
| | | }) |
| | | } |
| | |
| | | } |
| | | |
| | | 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(() => { |
| | |
| | | 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, |
| | |
| | | } |
| | | }) |
| | | 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; |
| | |
| | | } |
| | | }, |
| | | series: [{ |
| | | name: '待完工', |
| | | data: value1, |
| | | type: 'bar', |
| | | barMaxWidth: 32, |
| | |
| | | formatter: '{c}' // 格式化标签内容为数值 |
| | | } |
| | | }, { |
| | | name: '已完工', |
| | | data: value2, |
| | | type: 'bar', |
| | | barMaxWidth: 32, |
| | |
| | | formatter: '{c}' // 格式化标签内容为数值 |
| | | } |
| | | }, { |
| | | name: '再投单', |
| | | data: value3, |
| | | type: 'bar', |
| | | barMaxWidth: 32, |
| | |
| | | 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(); |
| | |
| | | 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]); |
| | |
| | | } |
| | | |
| | | return <div> |
| | | <PageContainer title='订单统计' className='order-statistics'> |
| | | <PageContainer title='订单统计' className='order-statistics' contentWidth="Fluid"> |
| | | <Card> |
| | | <Form scrollToFirstError form={form} > |
| | | <Row> |
| | |
| | | <Input placeholder="请输入" style={{ width: "80%" }} /> |
| | | </Form.Item> |
| | | </Col> |
| | | |
| | | |
| | | <Col span={4}> |
| | | <Space size='large' style={{ alignItems: "center" }}> |
| | | <Button onClick={() => reset()}>重置</Button> |
| | |
| | | </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> |
| | |
| | | <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> |
| | |
| | | // 撤回 |
| | | const [revocationVisible, setRevocationVisible] = useState(false) |
| | | |
| | | |
| | | const goToOrderDetail = async (code) => { |
| | | try { |
| | | const orderListRes = await getOrderList({ |
| | |
| | | > |
| | | 详情 |
| | | </Button> |
| | | {record.type == 1 && ( |
| | | {[1, 5].includes(Number(record.type)) && ( |
| | | <Button |
| | | type="link" |
| | | disabled={revocationSubmitting} |
| | | |
| | | onClick={() => { |
| | | setSelectItem(record) |
| | |
| | | handleVirtualVisible(false) |
| | | getInfo() |
| | | actionRef.current.reload() |
| | | } else { |
| | | message.error(res.msg || '操作失败') |
| | | } |
| | | }).finally(() => { |
| | | setVirtualSubmitting(false) |
| | |
| | | <Button onClick={() => history.back()}>返回</Button> |
| | | </div> |
| | | <Modal |
| | | title="余额扣除撤回" |
| | | title={Number(selectItem.type) === 5 ? '虚拟充值撤回' : '余额扣除撤回'} |
| | | // closable={{ 'aria-label': 'Custom Close Button' }} |
| | | open={revocationVisible} |
| | | onOk={handleOk} |
| | |
| | | <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> |
| | |
| | | </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="充值金额" |
| | |
| | | > |
| | | <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 |
| | |
| | | <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" |
| | |
| | | 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'; |
| | |
| | | render: (_, record) => { |
| | | return record.remark || record.payStr || '拉卡拉支付' |
| | | } |
| | | }, |
| | | { |
| | | title: '充值方式', |
| | | dataIndex: 'rechargeMethod', |
| | | width: 160, |
| | | render: (_, record) => record.rechargeMethod || (record.type === 5 ? '线下充值' : '线上充值'), |
| | | }, |
| | | { |
| | | title: '流水单号', |
| | |
| | | <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> |
| | |
| | | </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 |
| | |
| | | ...searchForm, |
| | | type:2, |
| | | } |
| | | |
| | | |
| | | setSearchForm(params) |
| | | return buildProTableDataSource(getMoneyList, params) |
| | | }} |
| | |
| | | // 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(); |
| | | } |