index.tsx 18 KB

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