WorkAnalysisDetail.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. import PageContent from '@/components/PageContent';
  2. import PageTitle from '@/components/PageTitle';
  3. import TabsContent from '@/components/TabsContent';
  4. import {
  5. queryBackwash,
  6. queryBackwashList,
  7. queryDesignNob,
  8. queryDesignNobList,
  9. queryDesignWash,
  10. queryDesignWashList,
  11. queryDrug,
  12. queryDrugList,
  13. queryMembrane,
  14. queryMembraneList,
  15. queryPump,
  16. queryPumpList,
  17. } from '@/services/SmartOps';
  18. import { UnityAction } from '@/utils/utils';
  19. import {
  20. useNavigate,
  21. useParams,
  22. useRequest,
  23. useSearchParams,
  24. } from '@umijs/max';
  25. import { Collapse, DatePicker, Empty, Form, Spin } from 'antd';
  26. import dayjs from 'dayjs';
  27. import * as echarts from 'echarts';
  28. import { useEffect, useMemo, useRef, useState } from 'react';
  29. import styles from './WorkAnalysisDetail.less';
  30. const TYPE = {
  31. td_uf: {
  32. name: '超滤膜组',
  33. device: (params) => queryMembraneList({ ...params, type: 'uf' }),
  34. chart: (params) => queryMembrane({ ...params, type: 'uf' }),
  35. },
  36. td_mf: {
  37. name: '微滤膜',
  38. device: (params) => queryMembraneList({ ...params, type: 'mf' }),
  39. chart: (params) => queryMembrane({ ...params, type: 'mf' }),
  40. },
  41. td_nf: {
  42. name: '纳滤膜',
  43. device: (params) => queryMembraneList({ ...params, type: 'nf' }),
  44. chart: (params) => queryMembrane({ ...params, type: 'nf' }),
  45. },
  46. td_ro: {
  47. name: '反渗透膜',
  48. device: (params) => queryMembraneList({ ...params, type: 'ro' }),
  49. chart: (params) => queryMembrane({ ...params, type: 'ro' }),
  50. },
  51. tdr_pac: {
  52. name: '絮凝剂投加',
  53. device: (params) => queryDrugList({ ...params, type: 'pac' }),
  54. chart: (params) => queryDrug({ ...params, type: 'pac' }),
  55. },
  56. tdr_hci: {
  57. name: 'HCI投加',
  58. device: (params) => queryDrugList({ ...params, type: 'hci' }),
  59. chart: (params) => queryDrug({ ...params, type: 'hci' }),
  60. },
  61. tdr_nob: {
  62. name: '非氧化杀菌剂投加',
  63. device: (params) => queryDrugList({ ...params, type: 'nob' }),
  64. chart: (params) => queryDrug({ ...params, type: 'nob' }),
  65. },
  66. tt_backwash: {
  67. name: '反冲洗记录',
  68. device: queryBackwashList,
  69. chart: queryBackwash,
  70. },
  71. tt_wash: {
  72. name: '大水量冲洗记录',
  73. device: queryDesignWashList,
  74. chart: queryDesignWash,
  75. },
  76. tt_nob: {
  77. name: '非氧化杀菌记录',
  78. device: queryDesignNobList,
  79. chart: queryDesignNob,
  80. },
  81. td_pump_nf: {
  82. name: '纳滤水泵',
  83. device: (params) => queryPumpList({ ...params, stage: 'td_pump_nf' }),
  84. chart: (params) => queryPump({ ...params, stage: 'td_pump_nf' }),
  85. },
  86. td_pump_uf: {
  87. name: '超滤水泵',
  88. device: (params) => queryPumpList({ ...params, stage: 'td_pump_uf' }),
  89. chart: (params) => queryPump({ ...params, stage: 'td_pump_uf' }),
  90. },
  91. td_pump_ro: {
  92. // td_pump: {
  93. name: '反渗透水泵',
  94. device: (params) => queryPumpList({ ...params, stage: 'td_pump_ro' }),
  95. chart: (params) => queryPump({ ...params, stage: 'td_pump_ro' }),
  96. },
  97. td_pump_mf: {
  98. name: '微滤水泵',
  99. device: (params) => queryPumpList({ ...params, stage: 'td_pump_mf' }),
  100. chart: (params) => queryPump({ ...params, stage: 'td_pump_mf' }),
  101. },
  102. td_pump_nf_drug: {
  103. name: '加药泵',
  104. device: (params) => queryPumpList({ ...params, stage: 'td_pump_nf_drug' }),
  105. chart: (params) => queryPump({ ...params, stage: 'td_pump_nf_drug' }),
  106. },
  107. td_pump_uf_drug: {
  108. name: '加药泵',
  109. device: (params) => queryPumpList({ ...params, stage: 'td_pump_uf_drug' }),
  110. chart: (params) => queryPump({ ...params, stage: 'td_pump_uf_drug' }),
  111. },
  112. td_pump_ro_drug: {
  113. // td_pump: {
  114. name: '加药泵',
  115. device: (params) => queryPumpList({ ...params, stage: 'td_pump_ro_drug' }),
  116. chart: (params) => queryPump({ ...params, stage: 'td_pump_ro_drug' }),
  117. },
  118. td_pump_mf_drug: {
  119. name: '加药泵',
  120. device: (params) => queryPumpList({ ...params, stage: 'td_pump_mf_drug' }),
  121. chart: (params) => queryPump({ ...params, stage: 'td_pump_mf_drug' }),
  122. },
  123. };
  124. const { Panel } = Collapse;
  125. const { RangePicker } = DatePicker;
  126. const DateTab = [
  127. {
  128. value: 'day',
  129. label: '今日',
  130. },
  131. {
  132. value: 'week',
  133. label: '本周',
  134. },
  135. {
  136. value: 'month',
  137. label: '本月',
  138. },
  139. ];
  140. function WorkAnalysisDetail(props) {
  141. const navigate = useNavigate();
  142. const { projectId } = useParams();
  143. const [searchParams, setSearchParams] = useSearchParams();
  144. const eTime = searchParams.get('eTime');
  145. const workAnalysis = useMemo(() => {
  146. let workAnalysis = {
  147. technologys: [],
  148. optimizationItems: {},
  149. optimizationNumber: 0,
  150. };
  151. try {
  152. workAnalysis = JSON.parse(sessionStorage.workAnalysis);
  153. workAnalysis.technologys = workAnalysis.technologys.filter((item) => {
  154. if (!TYPE[item]) console.log(item);
  155. return TYPE[item];
  156. });
  157. } catch (error) {
  158. console.error(error);
  159. }
  160. return workAnalysis;
  161. }, []);
  162. const {
  163. technologys,
  164. optimizationItems,
  165. optimizationNumber,
  166. parentName,
  167. name,
  168. } = workAnalysis;
  169. const [active, setActive] = useState(technologys[0]);
  170. const [current, setCurrent] = useState([]);
  171. const [selected, setSelected] = useState('');
  172. const handleBackClick = () => {
  173. UnityAction.sendMsg('ProcessAnalysisDetailBack');
  174. navigate(-1);
  175. };
  176. useEffect(() => {
  177. UnityAction.addEventListener('SynDev', (deviceCode) =>
  178. setCurrent([deviceCode]),
  179. );
  180. return () => UnityAction.off('SynDev');
  181. }, []);
  182. return (
  183. <PageContent closeable={false}>
  184. <PageTitle onReturn={handleBackClick}>返回</PageTitle>
  185. <div className={styles.pageContent}>
  186. <div className={styles.title}>
  187. {parentName}:{name}
  188. <span>
  189. {optimizationNumber > 0 && `${optimizationNumber}项待优化`}
  190. </span>
  191. </div>
  192. <TabsContent
  193. center={false}
  194. small
  195. defaultActiveKey={active}
  196. items={technologys.map((item) => ({
  197. label: TYPE[item]?.name,
  198. key: item,
  199. children: (
  200. <ListContent
  201. tab={active}
  202. current={active == item ? current : []}
  203. setCurrent={setCurrent}
  204. selected={selected}
  205. setSelected={setSelected}
  206. name={name}
  207. active={item}
  208. optimizationItems={optimizationItems}
  209. projectId={projectId}
  210. eTime={eTime}
  211. />
  212. ),
  213. }))}
  214. onChange={(key) => {
  215. setCurrent([]);
  216. setActive(key);
  217. }}
  218. ></TabsContent>
  219. </div>
  220. </PageContent>
  221. );
  222. }
  223. const ListContent = (props) => {
  224. const {
  225. tab,
  226. projectId,
  227. active,
  228. optimizationItems = {},
  229. name,
  230. current,
  231. setCurrent,
  232. eTime,
  233. selected,
  234. setSelected,
  235. } = props;
  236. const { data, loading, run } = useRequest(
  237. () => {
  238. let params = {
  239. page: 1,
  240. page_size: 99999,
  241. project_id: projectId,
  242. };
  243. return TYPE[active]?.device(params);
  244. },
  245. {
  246. formatResult: (res) => {
  247. return res.data.list;
  248. },
  249. },
  250. );
  251. useEffect(() => {
  252. if (tab == active && data) {
  253. sendMsg();
  254. }
  255. UnityAction.on('SynDev', selectedItem);
  256. return () => UnityAction.off('SynDev');
  257. }, [tab, active, data]);
  258. const selectedItem = (e) => {
  259. setSelected(e);
  260. setCurrent((current) => [...current, `${active}&&${e}`]);
  261. };
  262. //通知unity切换tab发送tab数据
  263. const sendMsg = () => {
  264. const SysDevs = {};
  265. data?.forEach((item) => {
  266. SysDevs[item.device_code] =
  267. optimizationItems?.[`${active}:${item.device_code}`] || 0;
  268. });
  269. const msg = { SysName: name, SysDevs };
  270. UnityAction.sendMsg('ProcessAnalysisDetail', JSON.stringify(msg));
  271. };
  272. // const activeKeys = useMemo(() => {
  273. // if (!data) return [];
  274. // return current.filter((deviceCode) =>
  275. // data.some((item) => item.device_code == deviceCode),
  276. // );
  277. // }, [data, current]);
  278. const handleClick = (keys) => {
  279. // 判断是开启还是关闭
  280. // 设置当前选中行
  281. if (keys.length > current.length) {
  282. const code = keys[keys.length - 1];
  283. UnityAction.sendMsg('SynDev', code.split('&&')[1]);
  284. setSelected(code);
  285. } else if (current.length > 0) {
  286. const item = current.find(
  287. (item) => keys.findIndex((cur) => cur == item) == -1,
  288. );
  289. setSelected(item);
  290. }
  291. setCurrent(keys);
  292. };
  293. const getExtra = (deviceCode) => {
  294. if (optimizationItems?.[`${active}:${deviceCode}`]) {
  295. return <span style={{ color: '#F5A623' }}>可优化</span>;
  296. } else {
  297. return <span style={{ color: '#11EEE4' }}>暂无优化</span>;
  298. }
  299. };
  300. const getStyle = (code) => {
  301. return code == selected ? { backgroundColor: '#1b73c5' } : null;
  302. };
  303. return (
  304. <Spin spinning={loading} style={{ minHeight: 300 }}>
  305. <Collapse
  306. activeKey={current}
  307. expandIconPosition="right"
  308. onChange={handleClick}
  309. >
  310. {data?.map((item) => (
  311. <Panel
  312. style={getStyle(item.device_code)}
  313. header={`${item.device_name}(${item.device_code})`}
  314. key={`${active}&&${item.device_code}`}
  315. extra={getExtra(item.device_code)}
  316. >
  317. <ChartContent
  318. active={active}
  319. deviceCode={item.device_code}
  320. projectId={projectId}
  321. eTime={eTime}
  322. />
  323. </Panel>
  324. ))}
  325. </Collapse>
  326. </Spin>
  327. );
  328. };
  329. const ChartContent = (props) => {
  330. const { deviceCode, projectId, active, eTime } = props;
  331. const [visible, setVisible] = useState(false);
  332. const [params, setParams] = useState(null);
  333. const [time, setTime] = useState([
  334. dayjs(eTime).subtract(10, 'minute'),
  335. dayjs(eTime),
  336. ]);
  337. const [dateActive, setDateActive] = useState();
  338. const timerRef = useRef({
  339. s_time: time[0].format('YYYY-MM-DD HH:mm:ss'),
  340. e_time: time[1].format('YYYY-MM-DD HH:mm:ss'),
  341. });
  342. const domRef = useRef(null);
  343. const chartRef = useRef(null);
  344. const { data, loading, run } = useRequest(() => {
  345. let params = {
  346. device_code: deviceCode,
  347. page: 1,
  348. page_size: 9999,
  349. project_id: projectId,
  350. ...timerRef.current,
  351. };
  352. return TYPE[active].chart(params);
  353. });
  354. const optimization = useMemo(() => {
  355. const result = data?.list?.filter((item) => item.optimization);
  356. if (result?.length > 0) {
  357. const lastItem = result[result.length - 1];
  358. const arr = [
  359. 'ro_wash_interval',
  360. 'peb_interval',
  361. 'pac_fr',
  362. 'ro_nob_interva',
  363. ];
  364. const valueList = Object.entries(lastItem.optimization)
  365. .map(([key, item]) => {
  366. if (!arr.includes(key)) return null;
  367. return item.remark;
  368. })
  369. .filter((item) => item);
  370. if (valueList.length == 0) return '';
  371. return `【${lastItem.c_time}】${valueList.join(',')}`;
  372. }
  373. return '';
  374. }, [data]);
  375. const searchTime = (type) => {
  376. setDateActive(type);
  377. let time = [dayjs().startOf(type), dayjs()];
  378. onSearch?.(time);
  379. };
  380. const onSearch = (time) => {
  381. console.log(time);
  382. if (time && time.length == 2) {
  383. let s_time, e_time;
  384. s_time = time[0].format('YYYY-MM-DD HH:mm:ss');
  385. e_time = time[1].format('YYYY-MM-DD HH:mm:ss');
  386. timerRef.current = { s_time, e_time };
  387. // setTime(time);
  388. run();
  389. }
  390. };
  391. useEffect(() => {
  392. if (data && data.list && domRef.current) {
  393. chartRef.current = echarts.init(domRef.current);
  394. chartRef.current.clear();
  395. let options = getOption(data.list, active);
  396. chartRef.current.setOption(options, true);
  397. chartRef.current.resize();
  398. }
  399. return () => {
  400. if (chartRef.current) {
  401. chartRef.current.dispose();
  402. }
  403. };
  404. }, [data, domRef.current]);
  405. return (
  406. <div className={styles.chartBox}>
  407. <Form layout="inline" className={styles.form}>
  408. <RangePicker
  409. inputReadOnly
  410. style={{ width: 250 }}
  411. allowClear={false}
  412. defaultValue={time}
  413. // value={time}
  414. onChange={onSearch}
  415. format="YYYY-MM-DD HH:mm:ss"
  416. ></RangePicker>
  417. <div className={styles.dateTabs}>
  418. {DateTab.map((item) => (
  419. <div
  420. key={item.value}
  421. className={`${styles.dateTabsItem} ${
  422. item.value == dateActive ? styles.active : ''
  423. }`}
  424. onClick={() => searchTime(item.value)}
  425. >
  426. {item.label}
  427. </div>
  428. ))}
  429. </div>
  430. </Form>
  431. <div>
  432. <Spin spinning={loading}>{!data?.list && <Empty />}</Spin>
  433. {data?.list && (
  434. <div
  435. ref={domRef}
  436. style={{
  437. height: 300,
  438. width: '100%',
  439. }}
  440. onResize={() => console.log(1)}
  441. />
  442. )}
  443. </div>
  444. {optimization && (
  445. <div
  446. style={{
  447. fontSize: 22,
  448. color: '#000',
  449. textIndent: 30,
  450. padding: '10px 0',
  451. flexShrink: 1,
  452. }}
  453. >
  454. {optimization}
  455. </div>
  456. )}
  457. {/* <MembraneModal visible={visible} onCancel={() => setVisible(false)} params={params} /> */}
  458. </div>
  459. );
  460. };
  461. function getOption(data = [], active) {
  462. let formatter,
  463. yAxisName = '',
  464. y2AxisName = '',
  465. series = [],
  466. xAxis = [];
  467. var data1 = [],
  468. data2 = [],
  469. data3 = [],
  470. data4 = [];
  471. switch (active) {
  472. case 'tt_backwash':
  473. yAxisName = '清洗周期/Min';
  474. formatter = (params) => {
  475. let item = data[params[0].dataIndex];
  476. let content = '';
  477. if (item.bw_type == 1) {
  478. // PEB
  479. content += 'PEB 反洗开始时间:' + item.peb_st;
  480. content += '<br />PEB 反洗结束时间:' + item.peb_et;
  481. } else {
  482. // CEB
  483. content += 'CEB 反洗开始时间:' + item.ceb_st;
  484. content += '<br />CEB 反洗结束时间:' + item.ceb_et;
  485. content += '<br />CEB 清洗剂浓度' + item.ceb_ppm;
  486. }
  487. return content;
  488. };
  489. data?.forEach((item) => {
  490. let time = 0;
  491. if (item.bw_type == 1) {
  492. if (item.peb_et && item.peb_st) {
  493. time = dayjs(item.peb_st).diff(dayjs(item.peb_et), 'minutes');
  494. }
  495. // 实际冲洗
  496. data1.push(time);
  497. data2.push(null);
  498. xAxis.push(dayjs(item.peb_st).format('YYYY-MM-DD HH:mm:ss'));
  499. } else {
  500. if (item.ceb_st && item.ceb_et) {
  501. time = dayjs(item.ceb_st).diff(dayjs(item.ceb_et), 'minutes');
  502. }
  503. data1.push(null);
  504. data2.push(time);
  505. xAxis.push(dayjs(item.ceb_st).format('YYYY-MM-DD HH:mm:ss'));
  506. }
  507. });
  508. series = [
  509. {
  510. name: '物理实际冲洗',
  511. type: 'bar',
  512. barMaxWidth: '20px',
  513. data: data1,
  514. },
  515. {
  516. name: '物理模拟冲洗',
  517. type: 'bar',
  518. barMaxWidth: '20px',
  519. data: data2,
  520. },
  521. {
  522. name: '化学实际冲洗',
  523. type: 'bar',
  524. barMaxWidth: '20px',
  525. data: data3,
  526. },
  527. {
  528. name: '化学模拟冲洗',
  529. type: 'bar',
  530. barMaxWidth: '20px',
  531. data: data4,
  532. },
  533. ];
  534. break;
  535. case 'tt_wash':
  536. yAxisName = '清洗周期/Min';
  537. formatter = (params) => {
  538. let item = data[params[0].dataIndex];
  539. let content = '';
  540. content += '反洗开始时间:' + item.st;
  541. content += '<br />反洗结束时间:' + item.et;
  542. return content;
  543. };
  544. data?.forEach((item) => {
  545. // 实际冲洗
  546. data1.push(Math.ceil(item.interval / 60));
  547. // TODO:模拟冲洗
  548. data2.push(Math.ceil(item.interval / 60));
  549. xAxis.push(dayjs(item.st).format('YYYY-MM-DD HH:mm:ss'));
  550. });
  551. series = [
  552. {
  553. name: '实际冲洗',
  554. type: 'bar',
  555. barMaxWidth: '20px',
  556. data: data1,
  557. },
  558. {
  559. name: '模拟冲洗',
  560. type: 'bar',
  561. barMaxWidth: '20px',
  562. data: data2,
  563. },
  564. ];
  565. break;
  566. case 'tt_nob':
  567. yAxisName = '杀菌周期/Min';
  568. formatter = (params) => {
  569. let item = data[params[0].dataIndex];
  570. let content = '';
  571. content += '杀菌开始时间:' + (item.st || '-');
  572. content += '<br />杀菌结束时间:' + (item.et || '-');
  573. return content;
  574. };
  575. var data1 = [],
  576. data2 = [];
  577. data?.forEach((item) => {
  578. // 实际冲洗
  579. data1.push(Math.ceil(item.interval / 60));
  580. // TODO:模拟冲洗
  581. data2.push(Math.ceil(item.interval / 60));
  582. xAxis.push(dayjs(item.st).format('YYYY-MM-DD HH:mm:ss'));
  583. });
  584. series = [
  585. {
  586. name: '实际',
  587. type: 'bar',
  588. barMaxWidth: '20px',
  589. data: data1,
  590. },
  591. {
  592. name: '模拟',
  593. type: 'bar',
  594. barMaxWidth: '20px',
  595. data: data2,
  596. },
  597. ];
  598. break;
  599. case 'tdr_hci':
  600. case 'tdr_nob':
  601. case 'tdr_pac':
  602. yAxisName = '投加量';
  603. data?.forEach((item) => {
  604. // 实际冲洗
  605. data1.push(Math.ceil(item.fr / 60));
  606. // TODO:模拟冲洗
  607. data2.push(Math.ceil(item.fcoa / 60));
  608. xAxis.push(dayjs(item.c_time).format('YYYY-MM-DD HH:mm:ss'));
  609. });
  610. series = [
  611. {
  612. name: '实际物理投加量',
  613. type: 'bar',
  614. barMaxWidth: '20px',
  615. data: data1,
  616. },
  617. {
  618. name: '理论物理投加量',
  619. type: 'bar',
  620. barMaxWidth: '20px',
  621. data: data2,
  622. },
  623. ];
  624. break;
  625. case 'td_uf':
  626. case 'td_mf':
  627. case 'td_nf':
  628. yAxisName = '渗透率';
  629. data?.forEach((item) => {
  630. // 实际跨膜压差
  631. data1.push(item.std_tmp);
  632. // 模拟跨膜压差
  633. data2.push(item.std_permeability);
  634. xAxis.push(dayjs(item.c_time).format('YYYY-MM-DD HH:mm:ss'));
  635. });
  636. series = [
  637. {
  638. name: '标准跨膜压差',
  639. type: 'line',
  640. data: data1,
  641. showSymbol: false,
  642. },
  643. {
  644. name: '标准渗透率',
  645. type: 'line',
  646. data: data2,
  647. showSymbol: false,
  648. },
  649. ];
  650. break;
  651. case 'td_ro':
  652. yAxisName = '跨膜压差';
  653. data?.forEach((item) => {
  654. // 实际跨膜压差
  655. data1.push(item.extend['1st_Stage_DP']);
  656. data2.push(item.extend['2nd_Stage_DP']);
  657. // 模拟跨膜压差
  658. data3.push(item.stabilize_extend['1st_Stage_DP']);
  659. data4.push(item.stabilize_extend['2nd_Stage_DP']);
  660. xAxis.push(dayjs(item.c_time).format('YYYY-MM-DD HH:mm:ss'));
  661. });
  662. series = [
  663. {
  664. name: '实际一段跨膜压差',
  665. type: 'line',
  666. data: data1,
  667. showSymbol: false,
  668. },
  669. {
  670. name: '实际二段跨膜压差',
  671. type: 'line',
  672. data: data2,
  673. showSymbol: false,
  674. },
  675. {
  676. name: '模拟一段跨膜压差',
  677. type: 'line',
  678. data: data3,
  679. showSymbol: false,
  680. },
  681. {
  682. name: '模拟二段跨膜压差',
  683. type: 'line',
  684. data: data4,
  685. showSymbol: false,
  686. },
  687. ];
  688. break;
  689. case 'td_pump_nf':
  690. case 'td_pump_uf':
  691. case 'td_pump_ro':
  692. // case 'td_pump':
  693. case 'td_pump_mf':
  694. case 'td_pump_nf_drug':
  695. case 'td_pump_mf_drug':
  696. case 'td_pump_mf_drug':
  697. case 'td_pump_mf_drug':
  698. yAxisName = '频率 Hz';
  699. y2AxisName = '电流 A';
  700. data?.forEach((item) => {
  701. // 实际跨膜压差
  702. data1.push(item.frequency);
  703. data2.push(item.current);
  704. xAxis.push(dayjs(item.c_time).format('YYYY-MM-DD HH:mm:ss'));
  705. });
  706. series = [
  707. {
  708. name: '频率',
  709. type: 'line',
  710. data: data1,
  711. showSymbol: false,
  712. },
  713. {
  714. name: '电流',
  715. type: 'line',
  716. data: data2,
  717. yAxisIndex: 1,
  718. showSymbol: false,
  719. },
  720. ];
  721. break;
  722. }
  723. // 过滤失效数据
  724. xAxis.forEach((time, index) => {
  725. if (time === 'Invalid date') {
  726. xAxis[index] = null;
  727. data1[index] = null;
  728. data2[index] = null;
  729. data3[index] = null;
  730. data4[index] = null;
  731. }
  732. });
  733. xAxis = xAxis.filter((item) => item);
  734. data1 = data1.filter((item) => !isNaN(item));
  735. data2 = data2.filter((item) => !isNaN(item));
  736. data3 = data3.filter((item) => !isNaN(item));
  737. data4 = data4.filter((item) => !isNaN(item));
  738. const option = {
  739. color: ['#FFC800', '#30EDFD', '#4096ff', '#ff4d4f', '#ffa940'],
  740. tooltip: {
  741. trigger: 'axis',
  742. axisPointer: {
  743. type: 'shadow',
  744. },
  745. textStyle: {
  746. fontSize: 24,
  747. },
  748. formatter,
  749. },
  750. legend: {
  751. textStyle: {
  752. // color: '#fff',
  753. fontSize: 24,
  754. },
  755. type: 'scroll',
  756. },
  757. grid: {
  758. top: 80,
  759. left: '3%',
  760. right: '4%',
  761. bottom: '3%',
  762. containLabel: true,
  763. },
  764. xAxis: {
  765. type: 'category',
  766. data: xAxis,
  767. nameTextStyle: {
  768. fontSize: 24,
  769. },
  770. axisLabel: {
  771. fontSize: 24,
  772. },
  773. },
  774. yAxis: {
  775. name: yAxisName,
  776. type: 'value',
  777. boundaryGap: [0, 0.01],
  778. nameTextStyle: {
  779. fontSize: 24,
  780. },
  781. axisLabel: {
  782. fontSize: 24,
  783. },
  784. splitNumber: 5,
  785. },
  786. series,
  787. };
  788. if (y2AxisName) {
  789. let y1 = option.yAxis;
  790. let y2 = JSON.parse(JSON.stringify(y1));
  791. y1.max = getMax(data1) || 1;
  792. y1.interval = y1.max / 5;
  793. y2.name = y2AxisName;
  794. y2.max = getMax(data2) || 1;
  795. y2.interval = y2.max / 5;
  796. option.yAxis = [y1, y2];
  797. }
  798. console.log(option);
  799. return option;
  800. }
  801. function getMax(arr) {
  802. const max = Math.max(...arr);
  803. const exponent = Math.floor(Math.log10(max));
  804. const base = Math.pow(10, exponent);
  805. const remainder = max % base;
  806. const maxRoundUp = max - remainder + base;
  807. const maxFixed = maxRoundUp.toFixed(1); // 将最大值的小数位数限制为 1
  808. return Number(maxFixed); // 将字符串转换为数字并返回
  809. }
  810. export default WorkAnalysisDetail;