index.tsx 15 KB

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