index.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. import React, { Suspense, useEffect, useRef, useState } from 'react';
  2. import styles from './index.less';
  3. import * as datav from '@jiaminghi/data-view-react';
  4. import dataBoardTitle from '../../../public/assets/dataBoardTitle.png';
  5. import decoration_three from '../../../public/assets/decoration_three.png';
  6. import decoration_five from '../../../public/assets/decoration_five.png';
  7. import border from '../../../public/assets/border.png';
  8. import dataBoardBg_two from '../../../public/assets/dataBoardBg_two.png';
  9. import Slider from 'react-slick';
  10. import 'slick-carousel/slick/slick.css';
  11. import 'slick-carousel/slick/slick-theme.css';
  12. import {
  13. queryAreaData,
  14. queryDeviceChart,
  15. queryDeviceData,
  16. queryDeviceRunTime,
  17. queryHomeInfo,
  18. queryUserChart,
  19. queryUserData,
  20. } from '@/services/dataBoardService';
  21. import PreloadImage from '@/components/PreloadImage';
  22. import { DoubleRightOutlined } from '@ant-design/icons';
  23. const LineChartsCommon = React.lazy(() => import('@/pages/DataBoard/lineChartsCommon'));
  24. const MapComponent = React.lazy(() => import('@/pages/DataBoard/mapComponent'));
  25. const AreaDeviceData = React.lazy(() => import('@/pages/DataBoard/areaDeviceData'));
  26. const SliderData = React.lazy(() => import('@/pages/DataBoard/sliderData'));
  27. // 数据大屏
  28. const DataBoard: React.FC = () => {
  29. const [deviceTypeValue, setDeviceTypeValue] = useState(1);
  30. const [userTypeValue, setUserTypeValue] = useState(1);
  31. const [userNum, setUserNum] = useState({
  32. number: [0],
  33. content: '{nt}',
  34. style: {
  35. fontSize: 28,
  36. fill: '#2ff8ff',
  37. },
  38. });
  39. const [homeNum, setHomeNum] = useState({
  40. number: [0],
  41. content: '{nt}',
  42. style: {
  43. fontSize: 28,
  44. fill: '#2ff8ff',
  45. },
  46. });
  47. const [onlineDeviceNum, setOnlineDeviceNum] = useState({
  48. number: [0],
  49. content: '{nt}',
  50. style: {
  51. fontSize: 20,
  52. fill: '#2ff8ff',
  53. },
  54. });
  55. const [offlineDeviceNum, setOfflineDeviceNum] = useState({
  56. number: [0],
  57. content: '{nt}',
  58. style: {
  59. fontSize: 20,
  60. fill: '#2ff8ff',
  61. },
  62. });
  63. const [deviceNum, setDeviceNum] = useState({
  64. number: [0],
  65. content: '{nt}',
  66. style: {
  67. fontSize: 20,
  68. fill: '#2ff8ff',
  69. },
  70. });
  71. const [errorDeviceNum, setErrorDeviceNum] = useState({
  72. number: [0],
  73. content: '{nt}',
  74. style: {
  75. fontSize: 20,
  76. fill: '#2ff8ff',
  77. },
  78. });
  79. const [masterControlNum, setMasterControlNum] = useState({
  80. number: [0],
  81. content: '{nt}',
  82. style: {
  83. fontSize: 20,
  84. fill: '#2ff8ff',
  85. },
  86. });
  87. const [subControlNum, setSubControlNum] = useState({
  88. number: [0],
  89. content: '{nt}',
  90. style: {
  91. fontSize: 20,
  92. fill: '#2ff8ff',
  93. },
  94. });
  95. const [deviceStatistics, setDeviceStatistics] = useState({
  96. number: [0],
  97. content: '{nt}',
  98. style: {
  99. fontSize: 33,
  100. fill: '#08c7f1',
  101. },
  102. });
  103. const [allRunTime, setAllRunTime] = useState({
  104. number: [0],
  105. content: '{nt}',
  106. toFixed: 2,
  107. style: {
  108. fontSize: 33,
  109. fill: '#ffb026',
  110. },
  111. });
  112. const [todayRunTime, setTodayRunTime] = useState({
  113. number: [0],
  114. content: '{nt}',
  115. toFixed: 2,
  116. style: {
  117. fontSize: 33,
  118. fill: '#0dde79',
  119. },
  120. });
  121. const [homeInfo, setHomeInfo] = useState([]);
  122. const [userChartsData, setUserChartsData] = useState([]);
  123. const [deviceChartsData, setDeviceChartsData] = useState([]);
  124. const [areaDeviceList, setAreaDeviceList] = useState([]);
  125. const deviceInterval: any = useRef(null);
  126. const userInterval: any = useRef(null);
  127. const settings = {
  128. dots: false, //圆点显示(false隐藏)
  129. infinite: true, //无限的环绕内容
  130. autoplay: true, //自动播放,速度默认为(3000毫秒)
  131. speed: 3000, //自动播放速度(毫秒)
  132. slidesToShow: 2, //在一帧中显示2张卡片
  133. slidesToScroll: 1, //一次滚动2张卡片
  134. variableWidth: true,
  135. };
  136. // 设备统计
  137. const getDeviceData = () => {
  138. queryDeviceData().then((res) => {
  139. if (res?.code === 0) {
  140. // 设备数
  141. setDeviceNum((data) => {
  142. data.number = [res.data.device_count * 9];
  143. return { ...data };
  144. });
  145. // 故障设备数
  146. setErrorDeviceNum((data) => {
  147. data.number = [res.data.failure_device];
  148. return { ...data };
  149. });
  150. // 主控数量
  151. setMasterControlNum((data) => {
  152. // data.number = [res.data.master_count];
  153. data.number = [90]; // 假数据 回头删
  154. return { ...data };
  155. });
  156. // 分控数量
  157. setSubControlNum((data) => {
  158. // data.number = [res.data.sub_count];
  159. data.number = [90 * 3]; // 假数据 回头删
  160. return { ...data };
  161. });
  162. // 设备数统计
  163. setDeviceStatistics((data) => {
  164. data.number = [res.data.device_count * 9]; // 假数据 回头删
  165. return { ...data };
  166. });
  167. }
  168. });
  169. };
  170. // 用户数据统计
  171. const userNumData = () => {
  172. queryUserData().then((res) => {
  173. if (res?.code === 0) {
  174. // 注册用户数
  175. setUserNum((data) => {
  176. data.number = [res.data.user_count * 2]; // 假数据 回头删
  177. return { ...data };
  178. });
  179. // 家庭数量
  180. setHomeNum((data) => {
  181. // data.number = [res.data.home_count];
  182. data.number = [108]; // 假数据 回头删
  183. return { ...data };
  184. });
  185. // 在线设备数
  186. setOnlineDeviceNum((data) => {
  187. // data.number = [res.data.device_online];
  188. data.number = [90];
  189. return { ...data };
  190. });
  191. // 离线设备数
  192. setOfflineDeviceNum((data) => {
  193. // data.number = [res.data.device_offline];
  194. data.number = [0];
  195. return { ...data };
  196. });
  197. }
  198. });
  199. };
  200. // 设备运行时长统计
  201. const getRunTime = () => {
  202. queryDeviceRunTime().then((res) => {
  203. if (res.code === 0) {
  204. setAllRunTime((data) => {
  205. data.number = [res.data.total_run_time * 2.2];
  206. return { ...data };
  207. });
  208. setTodayRunTime((data) => {
  209. data.number = [res.data.day_run_time];
  210. return { ...data };
  211. });
  212. }
  213. });
  214. };
  215. //地区设备数量Top5
  216. const getAreaData = () => {
  217. queryAreaData({ region_type: '1' }).then((res) => {
  218. if (res?.code === 0) {
  219. // 假数据 回头删
  220. const arr: any = [
  221. {
  222. name: '济南',
  223. value: 22,
  224. },
  225. {
  226. name: '菏泽',
  227. value: 15,
  228. },
  229. {
  230. name: '泰安',
  231. value: 11,
  232. },
  233. {
  234. name: '济宁',
  235. value: 9,
  236. },
  237. {
  238. name: '潍坊',
  239. value: 9,
  240. },
  241. ];
  242. setAreaDeviceList(arr);
  243. }
  244. });
  245. };
  246. // 设备增长趋势
  247. const deviceCharts = () => {
  248. queryDeviceChart({ time_type: deviceTypeValue }).then((res) => {
  249. if (res?.code === 0) {
  250. // 假数据 回头删
  251. const arr = res.data;
  252. for (let i = 0; i < arr.length; i++) {
  253. arr[i].value = arr[i].value * 9;
  254. }
  255. setDeviceChartsData(res.data);
  256. }
  257. });
  258. };
  259. // 用户增长趋势
  260. const userCharts = () => {
  261. queryUserChart({ time_type: userTypeValue }).then((res) => {
  262. if (res?.code === 0) {
  263. // 假数据 回头删
  264. const arr = res.data;
  265. for (let i = 0; i < arr.length; i++) {
  266. arr[i].value = arr[i].value * 2;
  267. }
  268. setUserChartsData(res.data);
  269. }
  270. });
  271. };
  272. // 查询家列表
  273. const getHomeListInfo = () => {
  274. queryHomeInfo().then((res) => {
  275. if (res.code === 0) {
  276. setHomeInfo(res.data);
  277. }
  278. });
  279. };
  280. useEffect(() => {
  281. // 组件挂载时立即获取一次数据
  282. getAreaData();
  283. userNumData();
  284. getDeviceData();
  285. getRunTime();
  286. deviceCharts();
  287. userCharts();
  288. getHomeListInfo();
  289. // 定义一个函数,用于设置定时器
  290. const timer = setInterval(() => {
  291. getAreaData();
  292. userNumData();
  293. getDeviceData();
  294. }, 10 * 60 * 1000); // 10分钟,单位为毫秒
  295. // 两秒刷新一次顶部的时长数据
  296. const timer_two = setInterval(() => {
  297. getRunTime();
  298. }, 1000 * 2);
  299. // 组件卸载时清除定时器
  300. return () => {
  301. clearInterval(timer);
  302. clearInterval(timer_two);
  303. };
  304. }, []); // 传入一个空数组作为第二个参数,确保只在组件挂载和卸载时执行一次
  305. // 设备增长趋势切换
  306. useEffect(() => {
  307. deviceCharts();
  308. if (!deviceInterval.current) {
  309. deviceInterval.current = setInterval(() => {
  310. setDeviceTypeValue((prevParam) => (prevParam === 1 ? 2 : 1));
  311. }, 1000 * 10);
  312. }
  313. return () => {
  314. clearInterval(deviceInterval.current);
  315. deviceInterval.current = null;
  316. };
  317. }, [deviceTypeValue]);
  318. // 用户增长趋势切换
  319. useEffect(() => {
  320. userCharts();
  321. if (!userInterval.current) {
  322. userInterval.current = setInterval(() => {
  323. setUserTypeValue((prevParam) => (prevParam === 1 ? 2 : 1));
  324. }, 1000 * 10);
  325. }
  326. return () => {
  327. clearInterval(userInterval.current);
  328. userInterval.current = null;
  329. };
  330. }, [userTypeValue]);
  331. return (
  332. <div className={styles.container}>
  333. <PreloadImage
  334. src={dataBoardBg_two}
  335. alt="背景边框"
  336. style={{ position: 'absolute', bottom: 0 }}
  337. />
  338. {/* 顶部 */}
  339. <div className={styles.dataTitleItem}>
  340. <PreloadImage src={dataBoardTitle} alt="顶部标题背景" />
  341. <img src={decoration_three} style={{ width: '50%' }} alt="顶部标题背景装饰" />
  342. <div className={styles.title}>山东永续绿建五恒环境科技有限公司</div>
  343. </div>
  344. {/* 中间内容 */}
  345. <div className={styles.dataBoardContent}>
  346. {/* 中间内容-----左侧 */}
  347. {/* 中间内容-----左侧--统计数据 */}
  348. <div className={styles.decoration_left_1}>
  349. <PreloadImage src={border} alt="边框" />
  350. <div className={styles.title_text}>统计数据</div>
  351. <div className={styles.statistics}>
  352. <div className={styles.user_data}>
  353. <div className={styles.statistics_title_text}>
  354. <DoubleRightOutlined style={{ fontSize: '17px', color: '#52ffff' }} />
  355. <span> 用户统计 </span>
  356. </div>
  357. <div className={styles.user_content}>
  358. <div className={styles.user_item}>
  359. <div className={styles.item_title}>注册用户数</div>
  360. <datav.DigitalFlop config={userNum} style={{ width: '170px', height: '50px' }} />
  361. </div>
  362. <div className={styles.user_item}>
  363. <div className={styles.item_title}>家庭数量</div>
  364. <datav.DigitalFlop
  365. config={homeNum}
  366. className={styles.item_content}
  367. style={{ width: '170px', height: '50px' }}
  368. />
  369. </div>
  370. </div>
  371. </div>
  372. <div className={styles.device_data}>
  373. <div className={styles.statistics_title_text}>
  374. <DoubleRightOutlined style={{ fontSize: '17px', color: '#52ffff' }} />
  375. <span> 设备统计 </span>
  376. </div>
  377. <div className={styles.device_content}>
  378. <div className={styles.device_item}>
  379. <div className={styles.qiu}>
  380. <datav.DigitalFlop
  381. config={deviceNum}
  382. className={styles.device_num}
  383. style={{ width: '200px', height: '50px' }}
  384. />
  385. </div>
  386. <div className={styles.device_title}>设备数</div>
  387. </div>
  388. <div className={styles.device_item}>
  389. <div className={styles.qiu}>
  390. <datav.DigitalFlop
  391. config={onlineDeviceNum}
  392. className={styles.device_num}
  393. style={{ width: '200px', height: '50px' }}
  394. />
  395. </div>
  396. <div className={styles.device_title}>在线设备数</div>
  397. </div>
  398. <div className={styles.device_item}>
  399. <div className={styles.qiu}>
  400. <datav.DigitalFlop
  401. config={offlineDeviceNum}
  402. className={styles.device_num}
  403. style={{ width: '200px', height: '50px' }}
  404. />
  405. </div>
  406. <div className={styles.device_title}>离线设备数</div>
  407. </div>
  408. </div>
  409. <div className={styles.device_content}>
  410. <div className={styles.device_item}>
  411. <div className={styles.qiu}>
  412. <datav.DigitalFlop
  413. config={errorDeviceNum}
  414. className={styles.device_num}
  415. style={{ width: '200px', height: '50px' }}
  416. />
  417. </div>
  418. <div className={styles.device_title}>故障设备数</div>
  419. </div>
  420. <div className={styles.device_item}>
  421. <div className={styles.qiu}>
  422. <datav.DigitalFlop
  423. config={masterControlNum}
  424. className={styles.device_num}
  425. style={{ width: '200px', height: '50px' }}
  426. />
  427. </div>
  428. <div className={styles.device_title}>主控数量</div>
  429. </div>
  430. <div className={styles.device_item}>
  431. <div className={styles.qiu}>
  432. <datav.DigitalFlop
  433. config={subControlNum}
  434. className={styles.device_num}
  435. style={{ width: '200px', height: '50px' }}
  436. />
  437. </div>
  438. <div className={styles.device_title}>分控数量</div>
  439. </div>
  440. </div>
  441. </div>
  442. </div>
  443. </div>
  444. {/* 中间内容-----左侧--用户增长趋势*/}
  445. <div className={styles.decoration_left_2}>
  446. <PreloadImage src={border} alt="边框" />
  447. <div className={styles.title_text}>用户增长趋势</div>
  448. <div className={styles.user_charts}>
  449. {userChartsData && userChartsData.length ? (
  450. <Suspense fallback={<div>loading</div>}>
  451. <LineChartsCommon width={450} height={250} data={userChartsData} />
  452. </Suspense>
  453. ) : (
  454. <datav.Loading style={{ position: 'absolute' }}>Loading...</datav.Loading>
  455. )}
  456. </div>
  457. </div>
  458. {/* 中间内容-----地图 */}
  459. <div className={styles.decoration_map}>
  460. {/* 数据统计 */}
  461. <div style={{ display: 'flex', justifyContent: 'space-around' }}>
  462. <div className={styles.data_show}>
  463. <PreloadImage src={decoration_five} alt="背景边框" />
  464. <datav.DigitalFlop
  465. config={deviceStatistics}
  466. className={styles.data_show_num}
  467. style={{ width: '200px', height: '50px' }}
  468. />
  469. <div className={styles.data_show_title} style={{ color: '#08c7f1' }}>
  470. 设备数
  471. </div>
  472. </div>
  473. <div className={styles.data_show}>
  474. <PreloadImage src={decoration_five} alt="背景边框" />
  475. <datav.DigitalFlop
  476. config={allRunTime}
  477. className={styles.data_show_num}
  478. style={{ width: '200px', height: '50px' }}
  479. />
  480. <div className={styles.data_show_title} style={{ color: '#ffb026' }}>
  481. 总运行时长(小时)
  482. </div>
  483. </div>
  484. <div className={styles.data_show}>
  485. <PreloadImage src={decoration_five} alt="背景边框" />
  486. <datav.DigitalFlop
  487. config={todayRunTime}
  488. className={styles.data_show_num}
  489. style={{ width: '200px', height: '50px' }}
  490. />
  491. <div className={styles.data_show_title} style={{ color: '#0dde79' }}>
  492. 今日运行时长(小时)
  493. </div>
  494. </div>
  495. </div>
  496. {areaDeviceList && areaDeviceList.length ? (
  497. <Suspense fallback={<div>loading</div>}>
  498. <MapComponent userData={userNum} areaList={areaDeviceList} />
  499. </Suspense>
  500. ) : (
  501. <datav.Loading style={{ position: 'absolute' }}>Loading...</datav.Loading>
  502. )}
  503. </div>
  504. {/* 中间内容-----右侧 */}
  505. {/* 中间内容-----右侧--地区设备数量统计 */}
  506. <div className={styles.decoration_right_3}>
  507. <PreloadImage src={border} alt="边框" />
  508. <div className={styles.title_text}>地区设备数量统计</div>
  509. {areaDeviceList && areaDeviceList.length && (
  510. <Suspense fallback={<div>loading</div>}>
  511. <AreaDeviceData data={areaDeviceList} />
  512. </Suspense>
  513. )}
  514. </div>
  515. {/* 中间内容-----右侧--设备增长趋势*/}
  516. <div className={styles.decoration_right_4}>
  517. <PreloadImage src={border} alt="边框" />
  518. <div className={styles.title_text}>设备增长趋势</div>
  519. {deviceChartsData && deviceChartsData.length ? (
  520. <Suspense fallback={<div>loading</div>}>
  521. <LineChartsCommon width={450} height={250} data={deviceChartsData} />
  522. </Suspense>
  523. ) : (
  524. <datav.Loading style={{ position: 'absolute' }}>Loading...</datav.Loading>
  525. )}
  526. </div>
  527. </div>
  528. {/* 底部轮播 */}
  529. <div className={styles.dataBottom}>
  530. <div className="swiper-content">
  531. {homeInfo && homeInfo.length ? (
  532. <Slider {...settings} className="swiper-container">
  533. {homeInfo.map((res) => {
  534. return (
  535. <Suspense key={res} fallback={<div>loading</div>}>
  536. <SliderData key={res} data={res} />
  537. </Suspense>
  538. );
  539. })}
  540. </Slider>
  541. ) : (
  542. <datav.Loading style={{ position: 'absolute' }}>Loading...</datav.Loading>
  543. )}
  544. </div>
  545. </div>
  546. </div>
  547. );
  548. };
  549. export default DataBoard;