index.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. import React, { useState, useEffect, useRef, useCallback } from 'react';
  2. import { FormattedMessage, useModel } from 'umi';
  3. import SearchInput from '@/sagacare_components/SearchInput';
  4. import PageHeader from '@/sagacare_components/PageHeader';
  5. import Map from '@/sagacare_components/map';
  6. import TopNavigator from '@/sagacare_components/topNavigator';
  7. import TopNavRight from './components/topNavRight';
  8. import DeviceModal from './components/deviceModal';
  9. import styles from './index.less';
  10. import mapstyles from '@/sagacare_components/map/index.less';
  11. import cx from 'classnames';
  12. import { Spin, Modal, message } from 'antd';
  13. import moment from 'moment';
  14. import { ExclamationCircleOutlined } from '@ant-design/icons';
  15. import { equipImageMap } from '@/config/sagacare/sagacare_image.js';
  16. import Icon from '@/tenants-ui/SgIcon';
  17. import type { navigatorItem } from '@/pages/Environment/index';
  18. import { projectObj } from '@/config/api.js';
  19. import {
  20. getMapList,
  21. queryEquipStatistics,
  22. queryDeviceTimeManage,
  23. } from '@/services/sagacare_service/environment';
  24. import {
  25. changeCurtain,
  26. changeAir,
  27. changeLight,
  28. getLamp,
  29. getAirInfo,
  30. } from '@/pages/Equipment/equipmentControl';
  31. //设备状态
  32. type equipStatus = {
  33. equipType: string;
  34. status: number;
  35. };
  36. //设备开启数量
  37. type statistics = {
  38. equipType: string;
  39. runCount: number;
  40. };
  41. const Environment: React.FC = () => {
  42. const { changeLastFloorId } = useModel('sagacare_buildFloor');
  43. const { searchSpace } = useModel('sagacare_searchInfo');
  44. const { getSpaceFunc } = useModel('sagacare_spaceFunc');
  45. const { envir_all, equip_air, equip_lamp, envir_curtain } = equipImageMap;
  46. const [showModal, setShowModal] = useState<boolean>(false);
  47. const [showSpace, setShowSpace] = useState<API.MapInfo>({}); //当前弹框选中的空间
  48. const [navigatorList, setNavigatorList] = useState<navigatorItem[]>([
  49. {
  50. name: '全部设备',
  51. id: 'all',
  52. src: envir_all,
  53. //color: '#E5574F',
  54. color: '#E89E32',
  55. colorStr: '',
  56. },
  57. {
  58. name: '空调',
  59. id: 'airConditioner',
  60. src: equip_air,
  61. num: 0,
  62. color: '#5E8BCF',
  63. colorStr: '94,139,207,',
  64. },
  65. {
  66. name: '照明',
  67. id: 'light',
  68. src: equip_lamp,
  69. num: 0,
  70. color: '#FFE823',
  71. colorStr: '255,232,35,',
  72. },
  73. {
  74. name: '窗帘',
  75. id: 'curtain',
  76. src: envir_curtain,
  77. num: 0,
  78. color: '#E89E32',
  79. colorStr: '232, 158, 50,',
  80. },
  81. ]);
  82. const [scale, setScale] = useState<number>(1);
  83. const [mapList, setMapList] = useState<API.MapInfo[]>([]);
  84. const mapListCopy = useRef<any[]>([]);
  85. const [mapSize, setMapSize] = useState<any>({});
  86. const [equipMapList, setEquipMapList] = useState<any[]>([]);
  87. const [timeMapList, setTimeMapList] = useState<any[]>([]);
  88. const [selNav, setSelNav] = useState<navigatorItem>(navigatorList[0]);
  89. const [selFloorId, setSelFloorId] = useState<string>();
  90. const [mapCombineList, setMapCombineList] = useState<any[]>([]);
  91. const [loading, setLoading] = useState<boolean>(false);
  92. const changeFloorId = (data: string) => {
  93. //debugger;
  94. setSelFloorId(data);
  95. };
  96. //导航条切换
  97. const navigatorChange = (item: navigatorItem) => {
  98. setSelNav(item);
  99. };
  100. const showModalClick = (item) => {
  101. // console.log('showChart', item);
  102. setShowModal(true);
  103. setShowSpace(item);
  104. };
  105. //当前设备的状态 all全部开启 part部分开启 close全都关闭 还有类型type 包括airConditioner light curtain
  106. //根据设备状态返回不同的 颜色
  107. const getColorOpacity = (value: number, type?: string): string => {
  108. let colorStr = '';
  109. if (selNav.id == 'all') {
  110. //全部设备时 colorStr赋值
  111. var filterRes = navigatorList.filter(function (item) {
  112. return item.id == type;
  113. });
  114. colorStr = (filterRes[0] || {}).colorStr;
  115. } else {
  116. colorStr = selNav.colorStr;
  117. }
  118. if (value === 2) {
  119. //2 是部分开启
  120. return 'rgba(' + colorStr + '0.4)';
  121. } else if (value === 1) {
  122. //1 是开启
  123. return 'rgba(' + colorStr + '1)';
  124. } else if (value === 0 || value == 'nostatus') {
  125. //0是关闭
  126. return 'rgba(196, 196, 196, 0.6)';
  127. }
  128. };
  129. const { confirm } = Modal;
  130. //单个空间的点击事情
  131. const spaceControl = (item, index: number) => {
  132. //全部设备 或者没有房间类型 则不可以点击
  133. if (selNav.id == 'all' || !item.canClick) return;
  134. //没有设备时
  135. if (item[selNav.id] == 'noHave') return;
  136. if (item[selNav.id] == 'nostatus') {
  137. message.success('该空间设备离线');
  138. return;
  139. }
  140. if (item[selNav.id] === 2) {
  141. //半开时
  142. confirm({
  143. title: '请确认',
  144. icon: <ExclamationCircleOutlined />,
  145. content: `是否进入单空间进行调节?`,
  146. okText: '确认',
  147. cancelText: '取消',
  148. onOk() {
  149. showModalClick(item);
  150. },
  151. onCancel() {
  152. //console.log('Cancel');
  153. },
  154. });
  155. return;
  156. }
  157. var status = item[selNav.id] === 1 ? '关闭' : '打开';
  158. confirm({
  159. title: '请确认',
  160. icon: <ExclamationCircleOutlined />,
  161. content: `确认${status}该${selNav.name}吗?`,
  162. okText: '确认',
  163. cancelText: '取消',
  164. onOk() {
  165. // function getDeviceStatus() {
  166. // var interval = setInterval(() => {
  167. // queryDeviceManage();
  168. // }, 1000);
  169. // setTimeout(() => {
  170. // console.log('setTimeout');
  171. // clearInterval(interval);
  172. // }, 60000);
  173. // }
  174. //现在加上循环调用 所以把手动关了
  175. function callback() {
  176. //这是在手动改变状态
  177. // var mapCopy = JSON.parse(JSON.stringify(mapCombineList));
  178. // mapCopy[index][selNav.id] = item[selNav.id] === 1 ? 0 : 1;
  179. // setMapCombineList(mapCopy);
  180. }
  181. //如果是空调
  182. if (selNav.id == 'airConditioner') {
  183. // function getDeviceStatus() {
  184. // //执行查询函数
  185. // getAirInfo(item, callback, 10, projectId);
  186. // }
  187. //changeAir(item, index, getDeviceStatus);
  188. changeAir(item, index, callback);
  189. }
  190. if (selNav.id == 'light') {
  191. // function getDeviceStatus() {
  192. // //请求状态 10是指循环调10次
  193. // getLamp(item, callback, 10);
  194. // }
  195. //debugger;
  196. // changeLight('one', [item], getDeviceStatus, status);
  197. changeLight('one', [item], callback, status);
  198. }
  199. if (selNav.id == 'curtain') {
  200. changeCurtain('one', [item], callback, status);
  201. }
  202. },
  203. onCancel() {
  204. //console.log('Cancel');
  205. },
  206. });
  207. };
  208. //获取地图 mapList
  209. useEffect(() => {
  210. if (selFloorId) {
  211. changeLastFloorId(selFloorId);
  212. setLoading(true);
  213. getMapList({
  214. //floorId: 'Fl11010802593241ec348ecb4148806b9034c8957454',
  215. floorId: selFloorId,
  216. })
  217. .then((res) => {
  218. setLoading(false);
  219. var data: API.MapInfo[] = (res.data || {}).spaceList || [];
  220. var mapSize = {
  221. width: (res.data || {}).width,
  222. height: (res.data || {}).height,
  223. };
  224. setMapSize(mapSize);
  225. setMapList(data);
  226. mapListCopy.current = data;
  227. //setSelNav(navigatorList[0]); //每当重新请求地图时 让选中导航的默认是第一个
  228. })
  229. .catch(() => {
  230. setLoading(false);
  231. });
  232. } else {
  233. setMapList([]);
  234. }
  235. }, [selFloorId]);
  236. const changeMapList = useCallback(
  237. (scale) => {
  238. setScale(scale < 1 ? scale : 1);
  239. var mapListTemp = JSON.parse(JSON.stringify(mapListCopy.current));
  240. var mapListTemp2 = mapListTemp.map((item, index) => {
  241. if (index == 0) {
  242. // console.log('mapListTemp', scale, item.width, item.width * scale);
  243. }
  244. item.width = item.width * scale;
  245. item.height = item.height * scale;
  246. item.left = item.left * scale;
  247. item.top = item.top * scale;
  248. return item;
  249. });
  250. setMapList(mapListTemp2);
  251. },
  252. [mapListCopy],
  253. );
  254. const queryDeviceManage = () => {
  255. //setLoading(true);
  256. return queryEquipStatistics({
  257. floorId: selFloorId,
  258. projectId: projectObj.projectId,
  259. })
  260. .then((res) => {
  261. // setLoading(false);
  262. if (!setTimer.current) return;
  263. var statistics = res.data.statistics || [];
  264. //赋值 运行中的设备数量
  265. navigatorList.forEach(function (nItem) {
  266. var fres = statistics.filter((sItem: statistics) => {
  267. return sItem.equipType == nItem.id;
  268. });
  269. nItem.num = (fres[0] || {}).runCount;
  270. });
  271. setNavigatorList(navigatorList);
  272. //设备空间信息
  273. var spaceList = res.data.spaceList || [];
  274. spaceList.forEach((item: any) => {
  275. var allType = ['airConditioner', 'light', 'curtain'];
  276. allType.forEach((etype) => {
  277. var filterEquip = (item.equipList || []).filter((eItem: any) => {
  278. return eItem.equipType == etype;
  279. });
  280. //有当前设备
  281. if (filterEquip.length > 0) {
  282. item.equipStatusList = item.equipStatusList ? item.equipStatusList : [];
  283. var filterRes = (item.equipStatusList || []).filter((eItem: equipStatus) => {
  284. return eItem.equipType == etype;
  285. });
  286. //不存时 赋值not 如果没有设备状态 则说明没有设备 1 是开启 0是关闭
  287. if (filterRes.length > 0) {
  288. item[etype] = filterRes[0].status;
  289. } else {
  290. item[etype] = 'nostatus';
  291. item.equipStatusList.push({
  292. equipType: etype,
  293. status: 'nostatus',
  294. });
  295. }
  296. } else {
  297. item[etype] = 'noHave';
  298. }
  299. });
  300. });
  301. //console.log('spaceList', spaceList);
  302. //debugger;
  303. setEquipMapList(spaceList);
  304. return 'next';
  305. })
  306. .catch(() => {
  307. // setLoading(false);
  308. });
  309. };
  310. let setTimer = useRef({});
  311. //获取设备状态 设备种类
  312. useEffect(() => {
  313. // function getDeviceStatus() {
  314. // queryDeviceManage().then((res) => {
  315. // setTimeout(() => {
  316. // getDeviceStatus();
  317. // }, 20000);
  318. // });
  319. // }
  320. if (selFloorId) {
  321. queryDeviceManage(); //第一次执行
  322. if (setTimer.current) {
  323. clearInterval(setTimer.current);
  324. setTimer.current = null;
  325. }
  326. //循环调接口
  327. setTimer.current = setInterval(() => {
  328. queryDeviceManage();
  329. }, 3000);
  330. return () => {
  331. console.log('selFloorId', selFloorId);
  332. clearInterval(setTimer.current);
  333. setTimer.current = null;
  334. };
  335. }
  336. }, [selFloorId]);
  337. //获取时间列表 spaceTimeList
  338. useEffect(() => {
  339. if (selFloorId) {
  340. //setLoading(true);
  341. queryDeviceTimeManage({
  342. floorId: selFloorId,
  343. //floorId: 'Fl11010802593241ec348ecb4148806b9034c8957454',
  344. date: moment().format('YYYYMMDD'),
  345. })
  346. .then(function (res) {
  347. // setLoading(false);
  348. var resList = res.content || [];
  349. setTimeMapList(resList);
  350. })
  351. .catch(() => {
  352. // setLoading(false);
  353. });
  354. }
  355. }, [selFloorId]);
  356. //合并空间设备数据 和 空间数据
  357. useEffect(() => {
  358. var combineList: any = [];
  359. mapList.map(function (mitem) {
  360. var spaceId = mitem.spaceId;
  361. var filterSpace1 = equipMapList.filter((ele) => {
  362. return ele.id == spaceId;
  363. });
  364. var filterSpace2 = timeMapList.filter((ele) => {
  365. return ele.id == spaceId;
  366. });
  367. var combine = Object.assign({}, mitem, filterSpace1[0], filterSpace2[0]);
  368. combineList.push(combine);
  369. });
  370. setMapCombineList(combineList);
  371. console.log('combine');
  372. }, [equipMapList, mapList, timeMapList]);
  373. let firtTime: number = 0;
  374. let lastTime: number = 0;
  375. return (
  376. <>
  377. <PageHeader title={<FormattedMessage id="menu.equipment" />} action={<SearchInput />} />
  378. <TopNavigator
  379. navigatorList={navigatorList}
  380. type={'equipment'}
  381. navigatorChange={navigatorChange}
  382. action={
  383. <TopNavRight
  384. selNavObj={selNav}
  385. navigatorList={navigatorList}
  386. mapList={mapCombineList}
  387. queryDeviceManage={queryDeviceManage}
  388. ></TopNavRight>
  389. }
  390. changeFloorId={changeFloorId}
  391. selParamObj={selNav}
  392. ></TopNavigator>
  393. <div className={styles.maptop}>
  394. {selNav.id !== 'all' && (
  395. <div className={styles.right}>
  396. <div>
  397. <span
  398. className={styles.circle}
  399. style={{ backgroundColor: 'rgba(' + selNav.colorStr + '1)' }}
  400. ></span>
  401. <span>开启</span>
  402. </div>
  403. <div>
  404. <span
  405. className={styles.circle}
  406. style={{ backgroundColor: 'rgba(' + selNav.colorStr + '0.4)' }}
  407. ></span>
  408. <span>部分开启</span>
  409. </div>
  410. <div>
  411. <span
  412. className={styles.circle}
  413. style={{ backgroundColor: 'rgba(196, 196, 196, 0.6)' }}
  414. ></span>
  415. <span>关闭</span>
  416. </div>
  417. </div>
  418. )}
  419. </div>
  420. <Spin spinning={loading} wrapperClassName={mapstyles.spinnclass}>
  421. {mapCombineList.length > 0 && (
  422. <Map
  423. changeMapList={changeMapList}
  424. mapList={mapCombineList}
  425. mapSize={mapSize}
  426. type={'equipment'}
  427. selFloorId={selFloorId}
  428. render={(item, index) => {
  429. return (
  430. <div
  431. className={mapstyles.houseWrap}
  432. key={index + item.id}
  433. style={{
  434. left: item.left,
  435. top: item.top,
  436. width: item.width,
  437. height: item.height,
  438. }}
  439. >
  440. <div
  441. style={{
  442. backgroundColor: item.canClick
  443. ? selNav.id == 'all'
  444. ? 'rgba(196, 196, 196, 0.4)'
  445. : getColorOpacity(item[selNav.id])
  446. : '',
  447. }}
  448. className={cx(mapstyles.house, {
  449. [mapstyles.notclick]:
  450. !item.canClick || (selNav.id !== 'all' && item[selNav.id] == 'noHave'),
  451. [mapstyles.searchSel]: item.spaceId && item.spaceId === searchSpace.spaceId,
  452. })}
  453. onClick={(event) => {
  454. event.stopPropagation();
  455. if (lastTime - firtTime < 200) {
  456. spaceControl(item, index);
  457. }
  458. }}
  459. onMouseDown={(event) => {
  460. //event.stopPropagation();
  461. firtTime = new Date().getTime();
  462. }}
  463. onMouseUp={(event) => {
  464. //event.stopPropagation();
  465. lastTime = new Date().getTime();
  466. }}
  467. >
  468. {/* {selNav.id !== 'all' && item[selNav.id] == 'noHave' && (
  469. <div className={mapstyles.noDevice}>
  470. <span className={mapstyles.noText}>无设备</span>
  471. </div>
  472. style={{ display: item.width > 80 && item.height > 50 ? 'flex' : 'none' }}
  473. )} */}
  474. {selNav.id == 'all' && (
  475. <div
  476. className={mapstyles.allDevice}
  477. style={{
  478. transform: `scale(${scale},${scale})`,
  479. }}
  480. >
  481. {/* 所有设备的图标列表 */}
  482. {(item.equipStatusList || []).map((ditem: equipStatus, dindex: number) => {
  483. return (
  484. <div
  485. key={dindex + 'device'}
  486. className={mapstyles.icon}
  487. style={{
  488. backgroundColor: getColorOpacity(ditem.status, ditem.equipType),
  489. }}
  490. >
  491. <Icon
  492. className=""
  493. type={ditem.equipType}
  494. style={{ color: '#fff', fontWeight: 'bold' }}
  495. ></Icon>
  496. </div>
  497. );
  498. })}
  499. </div>
  500. )}
  501. {item.canClick && (
  502. <div
  503. className={mapstyles.showModal}
  504. onClick={(event) => {
  505. event.stopPropagation();
  506. if (lastTime - firtTime < 200) {
  507. showModalClick(item);
  508. }
  509. }}
  510. onMouseDown={(event) => {
  511. //event.stopPropagation();
  512. firtTime = new Date().getTime();
  513. }}
  514. onMouseUp={(event) => {
  515. //event.stopPropagation();
  516. lastTime = new Date().getTime();
  517. }}
  518. >
  519. <Icon className="" type="hover"></Icon>
  520. </div>
  521. )}
  522. <div className={mapstyles.content}>
  523. <div className={mapstyles.contentDiv}>
  524. <Icon
  525. className=""
  526. type={getSpaceFunc(item.roomFuncType)}
  527. style={{
  528. fontSize: 20,
  529. display: item.width > 40 && item.height > 40 ? 'block' : 'none',
  530. }}
  531. ></Icon>
  532. <div
  533. className={mapstyles.name}
  534. style={{
  535. display:
  536. (item.width > 80 && item.height > 70) ||
  537. item.width > 100 ||
  538. item.height > 90
  539. ? 'block'
  540. : 'none',
  541. }}
  542. >
  543. {item.localName}
  544. </div>
  545. </div>
  546. </div>
  547. </div>
  548. </div>
  549. );
  550. }}
  551. ></Map>
  552. )}
  553. </Spin>
  554. {showModal && (
  555. <DeviceModal
  556. showSpace={showSpace}
  557. onClose={() => {
  558. setShowModal(false);
  559. }}
  560. ></DeviceModal>
  561. )}
  562. </>
  563. );
  564. };
  565. export default Environment;