|
|
@@ -1,8 +1,13 @@
|
|
|
import { PageContainer } from '@ant-design/pro-components';
|
|
|
-import { Button, Card, Col, DatePicker, Form, Row, Space } from 'antd';
|
|
|
+import { Button, Card, Col, DatePicker, Form, Row, Space, Spin } from 'antd';
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
|
import styles from './Welcome.less';
|
|
|
-import { getStatisticData } from '@/services/statistic';
|
|
|
+import {
|
|
|
+ getAppUserData,
|
|
|
+ getDailyActiveData,
|
|
|
+ getNewUserData,
|
|
|
+ getStatisticData,
|
|
|
+} from '@/services/statistic';
|
|
|
import { ReloadOutlined, SearchOutlined } from '@ant-design/icons';
|
|
|
import moment from 'moment/moment';
|
|
|
import * as echarts from 'echarts';
|
|
|
@@ -23,16 +28,42 @@ const Welcome: React.FC = () => {
|
|
|
const [form] = Form.useForm();
|
|
|
const [data, setData] = useState<any>(null);
|
|
|
const [searchData, setSearchData] = useState<object | null>({});
|
|
|
+ const [userData, setUserData] = useState<any>(null);
|
|
|
+ const [appUserData, setAppUserData] = useState<any>(null);
|
|
|
+ const [dailyActiveData, setDailyActiveData] = useState<any>(null);
|
|
|
+ const [deviceTypeData, setDeviceType] = useState<any>(null);
|
|
|
const chartRef: any = useRef();
|
|
|
|
|
|
// 获取数据
|
|
|
- const getList = () => {
|
|
|
+ const getDeviceData = () => {
|
|
|
const params = {
|
|
|
...searchData,
|
|
|
};
|
|
|
getStatisticData(params).then((res) => {
|
|
|
if (res && res.code === 0) {
|
|
|
setData(res.data);
|
|
|
+ // 设备类型统计数据
|
|
|
+ if (
|
|
|
+ res.data &&
|
|
|
+ res.data.ffx &&
|
|
|
+ res.data.wuheng &&
|
|
|
+ res.data.xfcs &&
|
|
|
+ res.data.xfjh &&
|
|
|
+ res.data.xfjs
|
|
|
+ ) {
|
|
|
+ const deviceName = ['分风箱', '五恒一体机', '新风除湿', '新风净化', '新风加湿'];
|
|
|
+ const deviceTypeValue = [
|
|
|
+ res.data.ffx,
|
|
|
+ res.data.wuheng,
|
|
|
+ res.data.xfcs,
|
|
|
+ res.data.xfjh,
|
|
|
+ res.data.xfjs,
|
|
|
+ ];
|
|
|
+ setDeviceType({ deviceName, deviceTypeValue });
|
|
|
+ } else {
|
|
|
+ setDeviceType(null);
|
|
|
+ }
|
|
|
+ // 渲染环形图
|
|
|
const chart = echarts.init(chartRef.current);
|
|
|
const region = JSON.parse(res.data.region);
|
|
|
const newData = Object.entries(region).map(([name, value]) => ({ value, name }));
|
|
|
@@ -98,13 +129,56 @@ const Welcome: React.FC = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+ // 新增用户数统计
|
|
|
+ const getUserData = () => {
|
|
|
+ getNewUserData().then((res) => {
|
|
|
+ if (res && res.code === 0) {
|
|
|
+ setUserData(res.data);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // app使用次数统计
|
|
|
+ const getAppCount = () => {
|
|
|
+ getAppUserData().then((res) => {
|
|
|
+ if (res && res.code === 0) {
|
|
|
+ setAppUserData(res.data);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取日活用户统计数据
|
|
|
+ const getDailyActiveUser = () => {
|
|
|
+ getDailyActiveData().then((res) => {
|
|
|
+ if (res && res.code === 0) {
|
|
|
+ const list = res.data || [];
|
|
|
+ if (list && list.length > 0) {
|
|
|
+ // 把x轴的数据和y轴的数据进行匹配
|
|
|
+ const xData = list.map(
|
|
|
+ (el: { date: string }) => moment(el.date).format('MM-DD') || '暂无',
|
|
|
+ );
|
|
|
+ const yData = list.map((el: { dau: number }) => el.dau);
|
|
|
+ setDailyActiveData({ xData, yData });
|
|
|
+ } else {
|
|
|
+ setDailyActiveData(null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
// 初始化
|
|
|
useEffect(() => {
|
|
|
- getList();
|
|
|
+ getDeviceData();
|
|
|
+ // 用户数据
|
|
|
+ getUserData();
|
|
|
+ // app使用次数统计
|
|
|
+ getAppCount();
|
|
|
+ // 获取日活用户统计
|
|
|
+ getDailyActiveUser();
|
|
|
}, []);
|
|
|
|
|
|
useEffect(() => {
|
|
|
- getList();
|
|
|
+ getDeviceData();
|
|
|
}, [searchData]);
|
|
|
|
|
|
// 搜索
|
|
|
@@ -125,12 +199,12 @@ const Welcome: React.FC = () => {
|
|
|
};
|
|
|
|
|
|
// 千分号格式化
|
|
|
- // const formatNumber = (num: any) => {
|
|
|
- // if (num === null || num === undefined || isNaN(num)) return '';
|
|
|
- // return parseFloat(num).toLocaleString(undefined, {
|
|
|
- // maximumFractionDigits: 20,
|
|
|
- // });
|
|
|
- // };
|
|
|
+ const formatNumber = (num: any) => {
|
|
|
+ if (num === null || num === undefined || isNaN(num)) return '';
|
|
|
+ return parseFloat(num).toLocaleString(undefined, {
|
|
|
+ maximumFractionDigits: 20,
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
return (
|
|
|
<PageContainer>
|
|
|
@@ -163,7 +237,7 @@ const Welcome: React.FC = () => {
|
|
|
<Card className={styles.dataItem} bordered={false}>
|
|
|
<div className={styles.dataTitle}>设备总数</div>
|
|
|
<div className={styles.dataValue}>
|
|
|
- <span className={styles.dataNum}>{data?.total}</span>
|
|
|
+ <span className={styles.dataNum}>{data?.total || 0}</span>
|
|
|
<span style={{ color: 'gray' }}>台</span>
|
|
|
</div>
|
|
|
</Card>
|
|
|
@@ -172,7 +246,7 @@ const Welcome: React.FC = () => {
|
|
|
<Card className={styles.dataItem} bordered={false}>
|
|
|
<div className={styles.dataTitle}>总在线数</div>
|
|
|
<div className={styles.dataValue}>
|
|
|
- <span className={styles.dataNum}>{data?.online}</span>
|
|
|
+ <span className={styles.dataNum}>{data?.online || 0}</span>
|
|
|
<span style={{ color: 'gray' }}>台</span>
|
|
|
</div>
|
|
|
</Card>
|
|
|
@@ -181,7 +255,7 @@ const Welcome: React.FC = () => {
|
|
|
<Card className={styles.dataItem} bordered={false}>
|
|
|
<div className={styles.dataTitle}>五恒在线数</div>
|
|
|
<div className={styles.dataValue}>
|
|
|
- <span className={styles.dataNum}>{data?.wuheng_online}</span>
|
|
|
+ <span className={styles.dataNum}>{data?.wuheng_online || 0}</span>
|
|
|
<span style={{ color: 'gray' }}>台</span>
|
|
|
</div>
|
|
|
</Card>
|
|
|
@@ -190,7 +264,7 @@ const Welcome: React.FC = () => {
|
|
|
<Card className={styles.dataItem} bordered={false}>
|
|
|
<div className={styles.dataTitle}>激活五恒数</div>
|
|
|
<div className={styles.dataValue}>
|
|
|
- <span className={styles.dataNum}>{data?.wuheng}</span>
|
|
|
+ <span className={styles.dataNum}>{data?.wuheng || 0}</span>
|
|
|
<span style={{ color: 'gray' }}>台</span>
|
|
|
</div>
|
|
|
</Card>
|
|
|
@@ -199,7 +273,7 @@ const Welcome: React.FC = () => {
|
|
|
<Card className={styles.dataItem} bordered={false}>
|
|
|
<div className={styles.dataTitle}>分风箱在线数</div>
|
|
|
<div className={styles.dataValue}>
|
|
|
- <span className={styles.dataNum}>{data?.ffx_online}</span>
|
|
|
+ <span className={styles.dataNum}>{data?.ffx_online || 0}</span>
|
|
|
<span style={{ color: 'gray' }}>台</span>
|
|
|
</div>
|
|
|
</Card>
|
|
|
@@ -212,12 +286,19 @@ const Welcome: React.FC = () => {
|
|
|
<Col span={16}>
|
|
|
<div>
|
|
|
<Row gutter={[20, 20]}>
|
|
|
- <Col span={6}>
|
|
|
+ <Col flex="1">
|
|
|
<Card hoverable>
|
|
|
<div className={styles.userDataItem}>
|
|
|
<div>
|
|
|
<div className={styles.dataItemTitle}>用户数</div>
|
|
|
- <div className={styles.dataItemValue}>222</div>
|
|
|
+ <div>
|
|
|
+ <span className={styles.dataItemValue}>
|
|
|
+ {userData?.total && userData?.total.length > 3
|
|
|
+ ? formatNumber(userData?.total)
|
|
|
+ : userData?.total || 0}
|
|
|
+ </span>
|
|
|
+ <span style={{ color: 'gray' }}>人</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div className={styles.dataItemIcon}>
|
|
|
<img src={userIcon} alt="用户icon" />
|
|
|
@@ -225,12 +306,15 @@ const Welcome: React.FC = () => {
|
|
|
</div>
|
|
|
</Card>
|
|
|
</Col>
|
|
|
- <Col span={6}>
|
|
|
+ <Col flex="1">
|
|
|
<Card hoverable>
|
|
|
<div className={styles.userDataItem}>
|
|
|
<div>
|
|
|
- <div className={styles.dataItemTitle}>今日新增</div>
|
|
|
- <div className={styles.dataItemValue}>222</div>
|
|
|
+ <div className={styles.dataItemTitle}>今日新增用户数</div>
|
|
|
+ <div>
|
|
|
+ <span className={styles.dataItemValue}>{userData?.tnu || 0}</span>
|
|
|
+ <span style={{ color: 'gray' }}>人</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div className={styles.dataItemIcon}>
|
|
|
<img src={todayIcon} alt="今日新增icon" />
|
|
|
@@ -238,12 +322,15 @@ const Welcome: React.FC = () => {
|
|
|
</div>
|
|
|
</Card>
|
|
|
</Col>
|
|
|
- <Col span={6}>
|
|
|
+ <Col flex="1">
|
|
|
<Card hoverable>
|
|
|
<div className={styles.userDataItem}>
|
|
|
<div>
|
|
|
- <div className={styles.dataItemTitle}>本月新增</div>
|
|
|
- <div className={styles.dataItemValue}>222</div>
|
|
|
+ <div className={styles.dataItemTitle}>本月新增用户数</div>
|
|
|
+ <div>
|
|
|
+ <span className={styles.dataItemValue}>{userData?.mnu || 0}</span>
|
|
|
+ <span style={{ color: 'gray' }}>人</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div className={styles.dataItemIcon}>
|
|
|
<img src={monthIcon} alt="本月新增icon" />
|
|
|
@@ -251,12 +338,31 @@ const Welcome: React.FC = () => {
|
|
|
</div>
|
|
|
</Card>
|
|
|
</Col>
|
|
|
- <Col span={6}>
|
|
|
+ <Col flex="1">
|
|
|
+ <Card hoverable>
|
|
|
+ <div className={styles.userDataItem}>
|
|
|
+ <div>
|
|
|
+ <div className={styles.dataItemTitle}>今日App使用次数</div>
|
|
|
+ <div>
|
|
|
+ <span className={styles.dataItemValue}>{appUserData?.total || 0}</span>
|
|
|
+ <span style={{ color: 'gray' }}>次</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className={styles.dataItemIcon}>
|
|
|
+ <img src={appIcon} alt="App统计次数icon" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+ </Col>
|
|
|
+ <Col flex="1">
|
|
|
<Card hoverable>
|
|
|
<div className={styles.userDataItem}>
|
|
|
<div>
|
|
|
- <div className={styles.dataItemTitle}>App统计次数</div>
|
|
|
- <div className={styles.dataItemValue}>222</div>
|
|
|
+ <div className={styles.dataItemTitle}>本月日均使用APP次数</div>
|
|
|
+ <div>
|
|
|
+ <span className={styles.dataItemValue}>{appUserData?.month_avg || 0}</span>
|
|
|
+ <span style={{ color: 'gray' }}>次</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div className={styles.dataItemIcon}>
|
|
|
<img src={appIcon} alt="App统计次数icon" />
|
|
|
@@ -269,13 +375,33 @@ const Welcome: React.FC = () => {
|
|
|
<Col span={12}>
|
|
|
<Card hoverable>
|
|
|
<div style={{ marginBottom: '20px', fontWeight: 'bold' }}>日活用户统计</div>
|
|
|
- <LineChart color="#1890ff" height="450px" width="700px" xData={[]} yData={[]} />
|
|
|
+ {dailyActiveData && dailyActiveData.xData && dailyActiveData.yData ? (
|
|
|
+ <LineChart
|
|
|
+ color="#1890ff"
|
|
|
+ height="450px"
|
|
|
+ width="700px"
|
|
|
+ xData={dailyActiveData?.xData}
|
|
|
+ yData={dailyActiveData?.yData}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <Spin />
|
|
|
+ )}
|
|
|
</Card>
|
|
|
</Col>
|
|
|
<Col span={12}>
|
|
|
<Card hoverable>
|
|
|
<div style={{ marginBottom: '20px', fontWeight: 'bold' }}>设备类型统计</div>
|
|
|
- <BarChart color="#1890ff" height="450px" width="700px" xData={[]} yData={[]} />
|
|
|
+ {deviceTypeData && deviceTypeData.deviceName && deviceTypeData.deviceTypeValue ? (
|
|
|
+ <BarChart
|
|
|
+ color="#1890ff"
|
|
|
+ height="450px"
|
|
|
+ width="700px"
|
|
|
+ seriesData={deviceTypeData?.deviceTypeValue}
|
|
|
+ yData={deviceTypeData?.deviceName}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <Spin />
|
|
|
+ )}
|
|
|
</Card>
|
|
|
</Col>
|
|
|
</Row>
|