diff --git a/package.json b/package.json
index 883d2d0..23ce52f 100644
--- a/package.json
+++ b/package.json
@@ -63,6 +63,7 @@
"braft-editor": "^2.3.9",
"classnames": "^2.2.6",
"cos-js-sdk-v5": "^1.3.5",
+ "echarts": "^5.3.3",
"js-export-excel": "^1.1.4",
"linq": "^4.0.0",
"lodash": "^4.17.11",
diff --git a/src/pages/order/order-member-statement/index.jsx b/src/pages/order/order-member-statement/index.jsx
index 8c62138..a4ed26e 100644
--- a/src/pages/order/order-member-statement/index.jsx
+++ b/src/pages/order/order-member-statement/index.jsx
@@ -1,28 +1,317 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
-import { Button, Card, DatePicker } from 'antd';
+import { Button, Card, DatePicker, Col, Row, TreeSelect, Spin, message } from 'antd';
import styles from './index.less';
+import moment from 'moment';
+import marketAPI from "./service";
+import * as echarts from 'echarts';
const { RangePicker } = DatePicker;
+
/**
* 会员报表
* @returns
*/
export default function Index() {
+ //日期选择下标:0:今天、1:昨天、2:近7天、3、近30天
+ const [searchDayIndex, setSearchDayIndex] = useState(0);
+ //日期选择器
+ const [timeRange, setTimeRange] = useState([
+ moment(moment(new Date(Date.now())).format('YYYY-MM-DD 00:00:00')),
+ moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59'))
+ ]);
+ const [orgTree, setOrgTree] = useState([]);
+ const [showLoading, setShowLoading] = useState(false);
+ const [currentOrg, setCurrentOrg] = useState("");
+
+ //会员图表
+ const [memberReport, setMemberReport] = useState({
+ userCount: 0,
+ newAddUser: [],
+ repurChase: []
+ });
+
+ //新增会员图表实例
+ let newMemberObj = null;
+ //复购图表实例
+ let repurChaseObj = null;
+
+ //切换时间
+ const onChangeTime = (dayIndex) => {
+ setSearchDayIndex(dayIndex);
+ let tempDate = [];
+ switch (dayIndex) {
+ case 0:
+ tempDate = [
+ moment(moment(new Date(Date.now())).format('YYYY-MM-DD 00:00:00')),
+ moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59'))
+ ]
+ break;
+ case 1:
+ tempDate = [
+ moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000)).format('YYYY-MM-DD 00:00:00')),
+ moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000)).format('YYYY-MM-DD 23:59:59'))
+ ]
+ break;
+ case 2:
+ tempDate = [
+ moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000 * 7)).format('YYYY-MM-DD 00:00:00')),
+ moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
+ ]
+ break;
+ case 3:
+ tempDate = [
+ moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000 * 30)).format('YYYY-MM-DD 00:00:00')),
+ moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
+ ]
+ break;
+ }
+ setTimeRange(tempDate);
+ }
+
+ //获取组织树
+ const onGetOrgTree = async () => {
+ setShowLoading(true);
+ const response = await marketAPI.getOrgTree();
+ setShowLoading(false);
+ if (response.statusCode === 200) {
+ const originTree = response.data;
+ onSetOrgTreeStatus(originTree);
+ setOrgTree(originTree);
+ } else {
+ message.error(response.errors || '获取组织树出错');
+ }
+ }
+
+ //设置组织树不可选择状态
+ const onSetOrgTreeStatus = (originTree) => {
+ originTree.forEach(treeItem => {
+ if (treeItem.children && treeItem.children.length > 0) {
+ onSetOrgTreeStatus(treeItem.children);
+ } else {
+ if (treeItem.type === 2 || treeItem.type === 3) {
+ treeItem.disabled = false;
+ } else {
+ treeItem.disabled = true;
+ }
+ }
+ });
+ }
+
+ const LoadingCard = () => {
+ return (
+
+
+
+ )
+ }
+
+ //初始化会员报表数据
+ const initMemberReport = async () => {
+ const jsonData = {
+ "startTime": timeRange[0],
+ "endTime": timeRange[1]
+ }
+ setShowLoading(true);
+ const response = await marketAPI.getMemberReport(jsonData);
+ setShowLoading(false);
+ if (response.statusCode === 200) {
+ setMemberReport(response.data);
+ } else {
+ message.error(response.errors || '获取会员报表失败');
+ }
+ }
+
+
+ //初始化新增会员图表
+ const initNewMemberCharts = () => {
+ const chartDom = document.getElementById('new-member');
+ if (!newMemberObj) {
+ newMemberObj = echarts.init(chartDom);
+ }
+ const xAxisData = [];
+ const seriesData = [];
+ memberReport.newAddUser.forEach(item => {
+ const date = new Date(item.userKey);
+ xAxisData.push(date.toLocaleString());
+ seriesData.push(item.userValue);
+ });
+ const option = {
+ title: {
+ text: '新增会员'
+ },
+ xAxis: {
+ type: 'category',
+ data: xAxisData
+ },
+ yAxis: {
+ type: 'value'
+ },
+ series: [
+ {
+ data: seriesData,
+ type: 'bar'
+ }
+ ]
+ };
+ newMemberObj.setOption(option);
+ }
+
+ //复购次数图表
+ const initRepurChaseCharts = () => {
+ const chartDom = document.getElementById('repur-chase');
+ if (!repurChaseObj) {
+ repurChaseObj = echarts.init(chartDom);
+ }
+ const xAxisData = [];
+ const seriesData = [];
+ memberReport.repurChase.forEach(item => {
+ xAxisData.push(item.userKey);
+ seriesData.push(item.userValue);
+ });
+ const option = {
+ title: {
+ text: '复购次数'
+ },
+ xAxis: {
+ type: 'category',
+ data: xAxisData
+ },
+ yAxis: {
+ type: 'value'
+ },
+ series: [
+ {
+ data: seriesData,
+ type: 'line',
+ smooth: true
+ }
+ ]
+ };
+ repurChaseObj.setOption(option);
+ }
+
+ //重置
+ const onResetSearch = () => {
+ setCurrentOrg("");
+ }
+
+ useEffect(() => {
+ onGetOrgTree();
+ initMemberReport();
+ }, []);
+
+ useEffect(() => {
+ initNewMemberCharts();
+ initRepurChaseCharts();
+ window.onresize = () => {
+ if (newMemberObj && repurChaseObj) {
+ newMemberObj.resize();
+ repurChaseObj.resize();
+ }
+ }
+ }, [memberReport]);
+
return (
+ {showLoading ? : null}
-
-
-
- 会员报表
+
+
+ {
+ let tempDate = [
+ moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD 00:00:00')),
+ moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD 23:59:59')),
+ ]
+ setTimeRange(tempDate);
+ }} />
+
+
+ onChangeTime(0)}>
+ 今天
+
+ onChangeTime(1)}>
+ 昨天
+
+ onChangeTime(2)}>
+ 近7天
+
+ onChangeTime(3)}>
+ 近30天
+
+
+
+ {
+ if (node.type === 2 || node === 3) {
+ setCurrentOrg(node);
+ } else {
+ setCurrentOrg("");
+ }
+ }}
+ placeholder="请选择组织架构"
+ treeDefaultExpandAll
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 会员总量
+
+
+ {memberReport.userCount}
+
+
+
+
+ 新增会员
+
+
+ {memberReport.newAddUser.length}
+
+
+
+
+ 复购数量
+
+
+ {memberReport.repurChase.length}
+
+
+
+
+
+
+ {/* 新增会员图表 */}
+
+
+
+
+
+
+
+ {/* 复购次数图表 */}
+
+
+
+
)
}
diff --git a/src/pages/order/order-member-statement/index.less b/src/pages/order/order-member-statement/index.less
index 74d0c92..5b5dbc1 100644
--- a/src/pages/order/order-member-statement/index.less
+++ b/src/pages/order/order-member-statement/index.less
@@ -2,15 +2,92 @@
margin-bottom: 20px;
}
-.data-search-box {
+.data-search-row {
+ margin-bottom: 10px;
+}
+
+.data-search-item {
display: flex;
align-items: center;
- justify-content: space-between;
+ margin-bottom: 10px;
+}
+
+.data-search-prefix {
+ margin-right: 10px;
+ font-size: 18px;
}
.search-btn-item {
- margin-left: 20px;
+ margin-right: 20px;
+}
+
+.data-search-day {
+ padding: 5px 20px;
+ border: 1px solid #dedede;
+ cursor: pointer;
+}
+
+.data-search-day:nth-child(1), .data-search-day:nth-child(2), .data-search-day:nth-child(3) {
+ border-right: none;
+}
+
+.search-day-selected {
+ border: 1px solid #FA541C !important;
+ color: #FA541C;
+}
+
+.data-search-sufixx {
+ width: 100%;
+}
+
+// 加载中
+.loading-card {
+ position: fixed;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100vw;
+ height: 100vh;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 999;
+ background-color: rgba(0, 0, 0, 0.5);
+}
+
+.member-card-box {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 5px;
+}
+
+.member-card-prefix {
+ font-size: 20px;
+ font-weight: 700;
+ color: #999;
+ font-family: '楷体';
+}
+
+.member-card-sufixx {
+ font-size: 22px;
+}
+
+.new-member {
+ width: 100%;
+ height: 250px;
+}
+
+.repur-chase {
+ width: 100%;
+ height: 250px;
+}
+
+.member-card {
+ height: 300px;
}
-.table-card {
+.member-row-common {
+ margin-bottom: 10px;
}
\ No newline at end of file
diff --git a/src/pages/order/order-member-statement/service.js b/src/pages/order/order-member-statement/service.js
new file mode 100644
index 0000000..063d1c6
--- /dev/null
+++ b/src/pages/order/order-member-statement/service.js
@@ -0,0 +1,18 @@
+import { request } from 'umi';
+
+export default {
+ //获取组织架构
+ getOrgTree() {
+ return request(`/kitchen/api/report-statistics/org-tree`, {
+ method: 'GET',
+ });
+ },
+
+ //获取会员报表
+ getMemberReport(data) {
+ return request(`/kitchen/api/report-statistics/user-report`, {
+ method: 'POST',
+ data
+ });
+ }
+};