feat(财务): 新增汇总统计页面及功能
添加财务管理菜单项和汇总统计页面,包含访问量、订单数和金额的统计图表
实现日期范围筛选和数据展示功能
| | |
| | | "path": "/finance/integralSettlement/detail", |
| | | "component": "./finance/integralSettlement/detail", |
| | | "hideInMenu": true |
| | | }, |
| | | { |
| | | "name": "汇总统计", |
| | | "path": "/finance/summaryStatistics", |
| | | "component": "./finance/summaryStatistics" |
| | | } |
| | | ] |
| | | }, |
| | |
| | | 'menu.order.bigGiftPackageOrder': '大礼包订单列表', |
| | | 'menu.order.physicalGoodsOrder': '实物商品订单', |
| | | 'menu.order.refundAudit': '退款申请审核', |
| | | 'menu.finance': '财务管理', |
| | | 'menu.finance.integralSettlement': '积分结算', |
| | | 'menu.finance.summaryStatistics': '汇总统计', |
| | | }; |
| New file |
| | |
| | | import React, { useState, useEffect } from 'react'; |
| | | import { PageContainer } from '@ant-design/pro-components'; |
| | | import { Card, Row, Col, DatePicker, Space, Spin, Button } from 'antd'; |
| | | import { LineChart, ColumnChart } from '@ant-design/plots'; |
| | | import moment from 'moment'; |
| | | import { getSummaryStatistics } from './service'; |
| | | |
| | | // 客户访问量统计组件 |
| | | const VisitStatsCard = ({ data }) => { |
| | | const { visitStats } = data || {}; |
| | | |
| | | if (!visitStats) { |
| | | return null; |
| | | } |
| | | |
| | | const config = { |
| | | data: visitStats.trend || [], |
| | | xField: 'date', |
| | | yField: 'value', |
| | | columnStyle: { |
| | | fill: '#5B8FF9', |
| | | }, |
| | | meta: { |
| | | date: { |
| | | alias: '日期', |
| | | }, |
| | | value: { |
| | | alias: '访问量', |
| | | }, |
| | | }, |
| | | interactions: [ |
| | | { |
| | | type: 'active-region', |
| | | enable: false, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | return ( |
| | | <Card> |
| | | <Row gutter={[16, 16]}> |
| | | <Col span={8}> |
| | | <div style={{ textAlign: 'center' }}> |
| | | <div style={{ fontSize: 24, fontWeight: 'bold', color: '#5B8FF9' }}> |
| | | {visitStats.total?.toLocaleString() || 0} |
| | | </div> |
| | | <div style={{ color: '#666' }}>总量</div> |
| | | </div> |
| | | </Col> |
| | | <Col span={8}> |
| | | <div style={{ textAlign: 'center' }}> |
| | | <div style={{ |
| | | fontSize: 24, |
| | | fontWeight: 'bold', |
| | | color: visitStats.rate >= 0 ? '#52C41A' : '#FF4D4F' |
| | | }}> |
| | | {visitStats.rate >= 0 ? '+' : ''}{visitStats.rate?.toFixed(2) || 0}% |
| | | </div> |
| | | <div style={{ color: '#666' }}>环比增长率</div> |
| | | </div> |
| | | </Col> |
| | | <Col span={8}> |
| | | <div style={{ height: 200 }}> |
| | | <ColumnChart {...config} /> |
| | | </div> |
| | | </Col> |
| | | </Row> |
| | | </Card> |
| | | ); |
| | | }; |
| | | |
| | | // 订单数统计组件(X、Y轴互换) |
| | | const OrderCountCard = ({ data }) => { |
| | | const { orderCountStats } = data || {}; |
| | | |
| | | if (!orderCountStats) { |
| | | return null; |
| | | } |
| | | |
| | | const config = { |
| | | data: orderCountStats.trend || [], |
| | | xField: 'value', |
| | | yField: 'date', |
| | | columnStyle: { |
| | | fill: '#FAC858', |
| | | }, |
| | | meta: { |
| | | date: { |
| | | alias: '日期', |
| | | }, |
| | | value: { |
| | | alias: '订单数', |
| | | }, |
| | | }, |
| | | interactions: [ |
| | | { |
| | | type: 'active-region', |
| | | enable: false, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | return ( |
| | | <Card> |
| | | <Row gutter={[16, 16]}> |
| | | <Col span={8}> |
| | | <div style={{ textAlign: 'center' }}> |
| | | <div style={{ fontSize: 24, fontWeight: 'bold', color: '#FAC858' }}> |
| | | {orderCountStats.total?.toLocaleString() || 0} |
| | | </div> |
| | | <div style={{ color: '#666' }}>总量</div> |
| | | </div> |
| | | </Col> |
| | | <Col span={8}> |
| | | <div style={{ textAlign: 'center' }}> |
| | | <div style={{ |
| | | fontSize: 24, |
| | | fontWeight: 'bold', |
| | | color: orderCountStats.rate >= 0 ? '#52C41A' : '#FF4D4F' |
| | | }}> |
| | | {orderCountStats.rate >= 0 ? '+' : ''}{orderCountStats.rate?.toFixed(2) || 0}% |
| | | </div> |
| | | <div style={{ color: '#666' }}>环比增长率</div> |
| | | </div> |
| | | </Col> |
| | | <Col span={8}> |
| | | <div style={{ height: 200 }}> |
| | | <ColumnChart {...config} /> |
| | | </div> |
| | | </Col> |
| | | </Row> |
| | | </Card> |
| | | ); |
| | | }; |
| | | |
| | | // 订单金额统计组件 |
| | | const OrderAmountCard = ({ data }) => { |
| | | const { orderAmountStats } = data || {}; |
| | | |
| | | if (!orderAmountStats) { |
| | | return null; |
| | | } |
| | | |
| | | const config = { |
| | | data: orderAmountStats.trend || [], |
| | | xField: 'date', |
| | | yField: 'value', |
| | | color: '#91CC75', |
| | | meta: { |
| | | date: { |
| | | alias: '日期', |
| | | }, |
| | | value: { |
| | | alias: '金额', |
| | | }, |
| | | }, |
| | | }; |
| | | |
| | | return ( |
| | | <Card> |
| | | <Row gutter={[16, 16]}> |
| | | <Col span={8}> |
| | | <div style={{ textAlign: 'center' }}> |
| | | <div style={{ fontSize: 24, fontWeight: 'bold', color: '#91CC75' }}> |
| | | ¥{(orderAmountStats.total || 0).toLocaleString()} |
| | | </div> |
| | | <div style={{ color: '#666' }}>总量</div> |
| | | </div> |
| | | </Col> |
| | | <Col span={8}> |
| | | <div style={{ textAlign: 'center' }}> |
| | | <div style={{ |
| | | fontSize: 24, |
| | | fontWeight: 'bold', |
| | | color: orderAmountStats.rate >= 0 ? '#52C41A' : '#FF4D4F' |
| | | }}> |
| | | {orderAmountStats.rate >= 0 ? '+' : ''}{orderAmountStats.rate?.toFixed(2) || 0}% |
| | | </div> |
| | | <div style={{ color: '#666' }}>环比增长率</div> |
| | | </div> |
| | | </Col> |
| | | <Col span={8}> |
| | | <div style={{ height: 200 }}> |
| | | <LineChart {...config} /> |
| | | </div> |
| | | </Col> |
| | | </Row> |
| | | </Card> |
| | | ); |
| | | }; |
| | | |
| | | // 订单月度统计组件 |
| | | const MonthlyStatsCard = ({ data }) => { |
| | | const { monthlyStats } = data || {}; |
| | | |
| | | if (!monthlyStats || monthlyStats.length === 0) { |
| | | return null; |
| | | } |
| | | |
| | | // 为每个月份创建两个数据点(订单数和订单金额) |
| | | const chartData = monthlyStats.map(item => [ |
| | | { month: item.month, type: '订单数', value: item.orderCount }, |
| | | { month: item.month, type: '订单金额', value: item.orderAmount }, |
| | | ]).flat(); |
| | | |
| | | const config = { |
| | | data: chartData, |
| | | xField: 'month', |
| | | yField: 'value', |
| | | seriesField: 'type', |
| | | isGroup: true, |
| | | columnStyle: { |
| | | radius: [4, 4, 0, 0], |
| | | }, |
| | | meta: { |
| | | month: { |
| | | alias: '月份', |
| | | }, |
| | | value: { |
| | | alias: '数量', |
| | | }, |
| | | }, |
| | | interactions: [ |
| | | { |
| | | type: 'active-region', |
| | | enable: false, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | return ( |
| | | <Card title="订单月度统计"> |
| | | <div style={{ height: 400 }}> |
| | | <ColumnChart {...config} /> |
| | | </div> |
| | | </Card> |
| | | ); |
| | | }; |
| | | |
| | | const SummaryStatistics = () => { |
| | | const [loading, setLoading] = useState(false); |
| | | const [data, setData] = useState(null); |
| | | const [dateRange, setDateRange] = useState([null, null]); |
| | | |
| | | // 获取统计数据 |
| | | const fetchData = async () => { |
| | | setLoading(true); |
| | | try { |
| | | const response = await getSummaryStatistics({ |
| | | startDate: dateRange[0], |
| | | endDate: dateRange[1], |
| | | }); |
| | | setData(response); |
| | | } catch (error) { |
| | | console.error('获取统计数据失败:', error); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | fetchData(); |
| | | }, [dateRange]); |
| | | |
| | | const handleDateChange = (dates) => { |
| | | if (dates && dates[0] && dates[1]) { |
| | | setDateRange([dates[0].format('YYYY-MM-DD'), dates[1].format('YYYY-MM-DD')]); |
| | | } else { |
| | | setDateRange([null, null]); |
| | | } |
| | | }; |
| | | |
| | | return ( |
| | | <PageContainer> |
| | | <div style={{ marginBottom: 24 }}> |
| | | <Space> |
| | | <DatePicker.RangePicker |
| | | value={dateRange.map(date => date ? moment(date) : null)} |
| | | onChange={handleDateChange} |
| | | style={{ width: 300 }} |
| | | /> |
| | | <Button onClick={fetchData} type="primary">查询</Button> |
| | | </Space> |
| | | </div> |
| | | |
| | | {loading ? ( |
| | | <div style={{ textAlign: 'center', padding: 100 }}> |
| | | <Spin size="large" /> |
| | | </div> |
| | | ) : ( |
| | | <> |
| | | <Row gutter={[16, 16]} style={{ marginBottom: 24 }}> |
| | | <Col span={8}> |
| | | <VisitStatsCard data={data} /> |
| | | </Col> |
| | | <Col span={8}> |
| | | <OrderCountCard data={data} /> |
| | | </Col> |
| | | <Col span={8}> |
| | | <OrderAmountCard data={data} /> |
| | | </Col> |
| | | </Row> |
| | | |
| | | <MonthlyStatsCard data={data} /> |
| | | </> |
| | | )} |
| | | </PageContainer> |
| | | ); |
| | | }; |
| | | |
| | | export default SummaryStatistics; |
| New file |
| | |
| | | import { request } from '@umijs/max'; |
| | | |
| | | // 获取汇总统计数据 |
| | | export async function getSummaryStatistics(params) { |
| | | return request('/dashboard-statistics/overview', { |
| | | method: 'POST', |
| | | data: params, |
| | | }); |
| | | } |