index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. import { getPendingList } from '@/services/message';
  2. import { queryConditionSnapshot } from '@/services/SmartOps';
  3. import { getToken, UnityAction } from '@/utils/utils';
  4. import { LoadingOutlined } from '@ant-design/icons';
  5. import { connect, useParams, useRequest } from '@umijs/max';
  6. import { Popover } from 'antd';
  7. import dayjs from 'dayjs';
  8. import { useEffect, useMemo, useState } from 'react';
  9. import { getScadaPage } from '../../services/OperationManagement';
  10. import styles from './index.less';
  11. const HomePage = (props) => {
  12. const { projectId } = useParams();
  13. const { data } = useRequest(queryConditionSnapshot, {
  14. defaultParams: [{ project_id: projectId }],
  15. pollingInterval: 10 * 1000,
  16. });
  17. const getPositionPst = (e) => {
  18. const width = document.body.clientWidth;
  19. const height = document.body.clientHeight;
  20. const xPst = (e.clientX / width).toFixed(2);
  21. const yPst = (e.clientY / height).toFixed(2);
  22. return xPst + '-' + yPst;
  23. };
  24. const webMouseEvent = {
  25. onMouseUp: (e) => {
  26. UnityAction.sendMsg('PointerOut', getPositionPst(e));
  27. },
  28. onMouseDown: (e) => {
  29. UnityAction.sendMsg('PointerIn', getPositionPst(e));
  30. },
  31. };
  32. useEffect(() => {
  33. localStorage.width = document.documentElement.getBoundingClientRect().width;
  34. window.refreshRem();
  35. document.body.style.backgroundColor = 'transparent';
  36. }, []);
  37. return (
  38. <div className={styles.content} {...webMouseEvent}>
  39. <CenterContent data={data} />
  40. <LeftContent data={data} />
  41. <RightContent data={data} />
  42. </div>
  43. );
  44. };
  45. const LeftContent = (props) => {
  46. const { data } = props;
  47. return (
  48. <div className={styles.left}>
  49. {/* <SmartWork data={data} /> */}
  50. <WaterAmt data={data} />
  51. <WaterQuality data={data} />
  52. <Backlog />
  53. {/* <Backlog /> */}
  54. </div>
  55. );
  56. };
  57. const CenterContent = (props) => {
  58. const { data } = props;
  59. return (
  60. <div className={styles.centerBox}>
  61. <SmartWork data={data} />
  62. <SelfInspection />
  63. {/* <Backlog />
  64. <Scada /> */}
  65. </div>
  66. );
  67. };
  68. const RightContent = (props) => {
  69. const { data } = props;
  70. return (
  71. <div className={styles.right}>
  72. {/* <SelfInspection /> */}
  73. <Electric data={data} />
  74. <Medicine data={data} />
  75. <Scada />
  76. {/* <Scada /> */}
  77. </div>
  78. );
  79. };
  80. export const getValue = (str) => {
  81. const result = str?.match(/.*?(\d+(?:\.\d+)?)\D*$/);
  82. if (result && result[1]) return result[1];
  83. return 0;
  84. };
  85. // 水厂工况
  86. const SmartWork = (props) => {
  87. const { data } = props;
  88. return (
  89. <div
  90. className={styles.smartWork}
  91. onClick={() => UnityAction.sendMsg('menuItem', '工况管理')}
  92. >
  93. <Title title="水厂工况" />
  94. <div className={styles.scoreBox}>
  95. <div className={styles.circle}>
  96. <div className={styles.score}>{data?.score}</div>
  97. <div className={styles.grade}>{data?.grade}</div>
  98. </div>
  99. <div className={styles.scoreTitle}>
  100. 当前运行{data?.grade},可继续优化
  101. </div>
  102. </div>
  103. <div className={styles.time}>
  104. {dayjs(data?.clac_time).format('YYYY-MM-DD HH:mm')}
  105. </div>
  106. </div>
  107. );
  108. };
  109. // 水量监测
  110. const WaterAmt = (props) => {
  111. const { data } = props;
  112. const status = useMemo(() => {
  113. switch (data?.dwa_status) {
  114. case 1:
  115. return '当前处于外供水高峰期';
  116. case 2:
  117. return '当前处于外供水平时期';
  118. case 3:
  119. return '当前处于外供水低峰期';
  120. }
  121. }, [data?.dwa_status]);
  122. return (
  123. <div
  124. className={styles.waterAmt}
  125. onClick={() => UnityAction.sendMsg('menuItem', '水量监测')}
  126. >
  127. <Title title="水量监测" />
  128. <div className={styles.boxTip}>{status}</div>
  129. <ul>
  130. <li>
  131. <div className={styles.value}>{getValue(data?.fwa)}</div>
  132. <div className={styles.btn1}>进水量(m³/h)</div>
  133. </li>
  134. <li>
  135. <div className={styles.value}>{getValue(data?.dwa)}</div>
  136. <div className={styles.btn2}>产水量(m³/h)</div>
  137. </li>
  138. </ul>
  139. </div>
  140. );
  141. };
  142. // 水质监测
  143. const WaterQuality = (props) => {
  144. const { data } = props;
  145. const status = useMemo(() => {
  146. switch (data?.water_quality_status) {
  147. case 1:
  148. return '当前水质良好';
  149. case 2:
  150. return '当前水质较好';
  151. }
  152. }, [data?.water_quality_status]);
  153. return (
  154. <div
  155. className={styles.waterQuality}
  156. onClick={() => UnityAction.sendMsg('menuItem', '水质监测')}
  157. >
  158. <Title title="水质监测" />
  159. <div className={styles.boxTip}>{status}</div>
  160. <ul>
  161. <li style={{ width: '60%' }}>
  162. <div className={styles.valueLong}>{getValue(data?.dtds)}</div>
  163. <div className={styles.btn1}>外供水电导率(µs/cm)</div>
  164. </li>
  165. <li style={{ width: '40%' }}>
  166. <div className={styles.valueLong}>{data?.dph || 0}</div>
  167. <div className={styles.btn2}>外供水(PH)</div>
  168. </li>
  169. </ul>
  170. </div>
  171. );
  172. };
  173. // 系统自检
  174. const SelfInspection = connect(({ eqSelfInspection, loading }) => ({
  175. autoReport: eqSelfInspection.autoReport,
  176. loading: loading.models['eqSelfInspection'],
  177. }))((props) => {
  178. const { autoReport, dispatch, loading } = props;
  179. const { projectId } = useParams();
  180. const renderStatus = () => {
  181. if (loading) return <LoadingOutlined />;
  182. if (autoReport.Status > 0) {
  183. return (
  184. <span className={styles.text} style={{ color: '#FE5850' }}>
  185. 异常
  186. </span>
  187. );
  188. }
  189. return (
  190. <span className={styles.text} style={{ color: '' }}>
  191. 正常
  192. </span>
  193. );
  194. };
  195. useEffect(() => {
  196. dispatch({
  197. type: 'eqSelfInspection/getAutoPatrol',
  198. payload: {
  199. projectId,
  200. },
  201. });
  202. }, []);
  203. return (
  204. <div
  205. className={styles.selfInspection}
  206. onClick={() => UnityAction.sendMsg('menuItem', '系统自检')}
  207. >
  208. <Title title="系统自检" />
  209. <div className={styles.selfCon}>
  210. <div className={styles.circle}>{renderStatus()}</div>
  211. <div className={styles.texting}>自检中</div>
  212. </div>
  213. <div className={styles.time}>
  214. {dayjs(autoReport.CreatedTime).format('YYYY-MM-DD HH:mm')}
  215. </div>
  216. {/* <div className={styles.insTag}>自检中</div> */}
  217. {/* <div className={styles.insStatus}>{renderStatus()}</div>
  218. <div className={styles.time} style={{ marginBottom: 30 }}>
  219. {dayjs(autoReport.CreatedTime).format('YYYY-MM-DD HH:mm')}
  220. </div> */}
  221. </div>
  222. );
  223. });
  224. // 能耗监测
  225. const Electric = (props) => {
  226. const { data } = props;
  227. const [open, setOpen] = useState(false);
  228. const elec = useMemo(() => {
  229. return getValue(data?.elec_unit);
  230. }, [data?.elec_unit]);
  231. const status = useMemo(() => {
  232. if (!data) return '';
  233. if (elec > data.elec_unit_theory) {
  234. return '当前电耗高于理论值';
  235. }
  236. if (elec == data.elec_unit_theory) {
  237. return '当前电耗持平理论值';
  238. }
  239. if (elec < data.elec_unit_theory) {
  240. return '当前电耗低于理论值';
  241. }
  242. }, [data]);
  243. const content = (
  244. <div className={styles.popoverContent}>
  245. <p>理论值规则:</p>
  246. <p>分为高/中/低温3档。则高温为≥25℃,低温为<20℃,中温为≥20且<25℃;</p>
  247. <p>
  248. 吨水电耗理论值:高温阶段理论值暂定为0.77Kwh/m3;高温阶段理论值暂定为0.83Kwh/m3;低温阶段理论值暂定为0.89Kwh/m3。
  249. </p>
  250. </div>
  251. );
  252. return (
  253. <div
  254. className={styles.electric}
  255. onClick={() => UnityAction.sendMsg('menuItem', '能耗监测')}
  256. >
  257. <Title title={'能耗监测'} />
  258. <div className={styles.boxTip}>{status}</div>
  259. <div
  260. className={`password-eye ${styles.eye} ${open ? 'open' : ''}`}
  261. onClick={(e) => {
  262. e.stopPropagation();
  263. setOpen(!open);
  264. }}
  265. ></div>
  266. <ul>
  267. <li>
  268. <div className={styles.value}>{open ? elec : '*****'}</div>
  269. <div className={styles.btn1}>吨水电耗(KWh/m³)</div>
  270. </li>
  271. <li>
  272. <div className={styles.value}>
  273. {/* {open ? getValue(data?.elec) : '*****'} */}
  274. {open ? data?.elec_unit_theory || '-' : '*****'}
  275. </div>
  276. <Popover title={content}>
  277. <div className={styles.btn1} onClick={(e) => e.stopPropagation()}>
  278. 理论值(KWh/m³)
  279. <i className={styles.iconAlert}></i>
  280. </div>
  281. </Popover>
  282. </li>
  283. </ul>
  284. </div>
  285. );
  286. };
  287. // 药耗监测
  288. const Medicine = (props) => {
  289. const { data } = props;
  290. const [open, setOpen] = useState(false);
  291. const otc_cost = useMemo(() => {
  292. return getValue(data?.otc_cost_unit);
  293. }, [data?.otc_cost_unit]);
  294. const status = useMemo(() => {
  295. if (!data) return '';
  296. if (otc_cost > data.otc_unit_theory) {
  297. return '当前药耗高于理论值';
  298. }
  299. if (otc_cost == data.otc_unit_theory) {
  300. return '当前药耗持平理论值';
  301. }
  302. if (otc_cost < data.otc_unit_theory) {
  303. return '当前药耗低于理论值';
  304. }
  305. }, [data]);
  306. const content = (
  307. <div className={styles.popoverContent}>
  308. <p>理论值规则:</p>
  309. <p>分为高/中/低温3档。则高温为≥25℃,低温为<20℃,中温为≥20且<25℃;</p>
  310. <p>
  311. 吨水药耗理论值:高温阶段理论值暂定为0.165元/m3;中温阶段理论值暂定为0.177元/m3;低温阶段理论值暂定为0.189元/m3。
  312. </p>
  313. </div>
  314. );
  315. return (
  316. <div
  317. className={styles.medicine}
  318. onClick={() => UnityAction.sendMsg('menuItem', '药耗监测')}
  319. >
  320. <Title title={'药耗监测'} />
  321. <div className={styles.boxTip}>{status}</div>
  322. <div
  323. className={`password-eye ${styles.eye} ${open ? 'open' : ''}`}
  324. onClick={(e) => {
  325. e.stopPropagation();
  326. setOpen(!open);
  327. }}
  328. ></div>
  329. <ul>
  330. <li>
  331. <div className={styles.valueLong}>{open ? otc_cost : '*****'}</div>
  332. <div className={styles.btn1}>吨水药成本(元/m³)</div>
  333. </li>
  334. <li>
  335. <div className={styles.valueLong}>
  336. {open ? data?.otc_unit_theory || '-' : '*****'}
  337. </div>
  338. <Popover title={content}>
  339. <div className={styles.btn1} onClick={(e) => e.stopPropagation()}>
  340. 理论值(元/m³)
  341. <i className={styles.iconAlert}></i>
  342. </div>
  343. </Popover>
  344. </li>
  345. </ul>
  346. </div>
  347. );
  348. };
  349. // 工艺监控
  350. const Scada = () => {
  351. const { projectId } = useParams();
  352. const { data } = useRequest(getScadaPage, {
  353. defaultParams: [{ project_id: projectId }],
  354. formatResult: (res) => {
  355. let domain = location.host.includes('pad.greentech.com.cn')
  356. ? 'https://metawant.greentech.com.cn/'
  357. : 'http://47.96.12.136:8788/';
  358. const token = getToken();
  359. const pageList = res?.filter((item) => item.hide);
  360. const urls = pageList.map(
  361. (item) =>
  362. `${domain}smart-water/scada/index.html#/3dview/${projectId}/${item.id}?JWT-TOKEN=${token}&hideTitle=true&pauseLoadRealTimeData=true`,
  363. );
  364. return urls.splice(0, 1);
  365. },
  366. });
  367. return (
  368. <div
  369. className={styles.scadaMain}
  370. onClick={() => UnityAction.sendMsg('menuItem', '工艺监控')}
  371. >
  372. <Title title="工艺监控" />
  373. <div style={{ width: '707px', height: '279px' }}>
  374. {data?.map((url) => (
  375. <iframe
  376. key={url}
  377. style={{
  378. width: '707px',
  379. height: '279px',
  380. borderRadius: '0 0 44px 0',
  381. }}
  382. frameBorder="0"
  383. src={url}
  384. />
  385. ))}
  386. <div className={styles.mask}></div>
  387. </div>
  388. </div>
  389. );
  390. };
  391. // 待办事项
  392. const Backlog = (props) => {
  393. const { projectId } = useParams();
  394. const { data, loading } = useRequest(getPendingList, {
  395. defaultParams: [{ project_id: projectId }],
  396. });
  397. const handleClick = (item) => {
  398. if (item.type === 0) {
  399. // task
  400. UnityAction.sendMsg('OpenTaskModal', `mandate_id=${item.origin_id}`);
  401. } else {
  402. // order
  403. UnityAction.sendMsg(
  404. 'OpenWorkOrderModal',
  405. `order_id=${item.origin_id}&order_type=${item.origin_type}`,
  406. );
  407. }
  408. };
  409. return (
  410. <div
  411. className={styles.backlogMain}
  412. onClick={() => UnityAction.sendMsg('menuItem', '待办事项')}
  413. >
  414. <Title title={<div style={{ display: 'flex' }}>待办事项</div>} />
  415. <div className={styles.backlog}>
  416. <div>
  417. {data?.map((item) => (
  418. <div
  419. key={item.connect}
  420. className={styles.item}
  421. onClick={(e) => {
  422. e.stopPropagation();
  423. handleClick(item);
  424. }}
  425. >
  426. <div className={styles.createTime}>
  427. {dayjs(item.time).format('MM-DD HH:mm')}
  428. </div>
  429. <div className={styles.titleCon}>
  430. <div className={styles.point} />
  431. <div className={styles.titleText}>{item.title}</div>
  432. </div>
  433. <div className={styles.bottomCon}>{item.content}</div>
  434. </div>
  435. ))}
  436. </div>
  437. </div>
  438. </div>
  439. );
  440. };
  441. const Title = ({ title }) => {
  442. return (
  443. <div className={styles.titleContent}>
  444. <div className={styles.line} />
  445. <div className={styles.boxTitle}>{title}</div>
  446. </div>
  447. );
  448. };
  449. const Box = ({ title, children, onClick, small }) => {
  450. return (
  451. <div
  452. className={`${styles.box} ${small ? '' : styles.boxH}`}
  453. onClick={onClick}
  454. >
  455. <div className={styles.titleContent}>
  456. <div className={styles.line} />
  457. <div className={styles.boxTitle}>{title}</div>
  458. </div>
  459. {/* <span className={styles.boxTitle}>
  460. <div className={styles.line}></div>
  461. {title}
  462. </span> */}
  463. {children}
  464. </div>
  465. );
  466. };
  467. export default HomePage;