123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725 |
- import TabsContent from '@/components/TabsContent';
- import {
- queryBackwash,
- queryBackwashList,
- queryDesignNob,
- queryDesignNobList,
- queryDesignWash,
- queryDesignWashList,
- queryDrug,
- queryDrugList,
- queryMembrane,
- queryMembraneConditions,
- queryMembraneList,
- queryProjectConfig,
- } from '@/services/SmartOps';
- import { AreaChartOutlined } from '@ant-design/icons';
- import { useRequest } from '@umijs/max';
- import { DatePicker, Modal, Select, Spin } from 'antd';
- import dayjs from 'dayjs';
- import * as echarts from 'echarts';
- import { useEffect, useRef, useState } from 'react';
- import styles from './SimulateDetail.less';
- const { RangePicker } = DatePicker;
- const TYPE = {
- td_uf: {
- name: '超滤膜',
- device: (params) => queryMembraneList({ ...params, type: 'uf' }),
- chart: (params) => queryMembrane({ ...params, type: 'uf' }),
- },
- td_mf: {
- name: '微滤膜',
- device: (params) => queryMembraneList({ ...params, type: 'mf' }),
- chart: (params) => queryMembrane({ ...params, type: 'mf' }),
- },
- td_nf: {
- name: '纳滤膜',
- device: (params) => queryMembraneList({ ...params, type: 'nf' }),
- chart: (params) => queryMembrane({ ...params, type: 'nf' }),
- },
- td_ro: {
- name: '反渗透膜',
- device: (params) => queryMembraneList({ ...params, type: 'ro' }),
- chart: (params) => queryMembrane({ ...params, type: 'ro' }),
- },
- tdr_pac: {
- name: '絮凝剂投加',
- device: (params) => queryDrugList({ ...params, type: 'pac' }),
- chart: (params) => queryDrug({ ...params, type: 'pac' }),
- },
- tdr_hci: {
- name: 'HCI投加',
- device: (params) => queryDrugList({ ...params, type: 'hci' }),
- chart: (params) => queryDrug({ ...params, type: 'hci' }),
- },
- tdr_nob: {
- name: '非氧化杀菌剂投加',
- device: (params) => queryDrugList({ ...params, type: 'nob' }),
- chart: (params) => queryDrug({ ...params, type: 'nob' }),
- },
- tt_backwash: {
- name: '反冲洗记录',
- device: queryBackwashList,
- chart: queryBackwash,
- },
- tt_wash: {
- name: '大水量冲洗记录',
- device: queryDesignWashList,
- chart: queryDesignWash,
- },
- tt_nob: {
- name: '非氧化杀菌记录',
- device: queryDesignNobList,
- chart: queryDesignNob,
- },
- };
- const SimulateDetail = (props) => {
- const { projectId } = props;
- const [active, setActive] = useState();
- const [current, setCurrent] = useState();
- const { data } = useRequest(queryProjectConfig, {
- defaultParams: [projectId],
- onSuccess(data) {
- setActive(data[0]);
- },
- });
- return (
- <div>
- {data && (
- <TabsContent
- center={false}
- defaultActiveKey={data[0]}
- items={data.map((item) => ({
- label: TYPE[item]?.name,
- key: item,
- children: <div></div>,
- }))}
- onChange={(value) => {
- setActive(value);
- setCurrent(null);
- }}
- ></TabsContent>
- )}
- <div className={`${styles.box} card-box`}>
- <ChartContent
- active={active}
- projectId={projectId}
- current={current}
- setCurrent={setCurrent}
- />
- </div>
- </div>
- );
- };
- const DateTab = [
- {
- value: 'day',
- label: '今日',
- },
- {
- value: 'week',
- label: '本周',
- },
- {
- value: 'month',
- label: '本月',
- },
- ];
- const ChartContent = (props) => {
- const { current, projectId, active, setCurrent } = props;
- const [visible, setVisible] = useState(false);
- const [params, setParams] = useState(null);
- const [dateActive, setDateActive] = useState('day');
- const [time, setTime] = useState([dayjs().startOf('day'), dayjs()]);
- const timerRef = useRef({
- s_time: time[0].format('YYYY-MM-DD HH:mm:ss'),
- e_time: time[1].format('YYYY-MM-DD HH:mm:ss'),
- });
- const domRef = useRef(null);
- const chartRef = useRef(null);
- const { data, run, loading } = useRequest(
- () => {
- let params = {
- device_code: current.device_code,
- page: 1,
- page_size: 9999,
- project_id: projectId,
- ...timerRef.current,
- };
- setParams(params);
- return TYPE[active].chart(params);
- },
- {
- manual: true,
- onSuccess(data) {
- chartRef.current.clear();
- let options = getOption(data.list, active);
- chartRef.current.setOption(options, true);
- chartRef.current.resize();
- },
- },
- );
- const searchTime = (type) => {
- setDateActive(type);
- let time = [dayjs().startOf(type), dayjs()];
- onSearch?.(time);
- };
- const onSearch = (time) => {
- if (time && time.length == 2) {
- let s_time, e_time;
- s_time = time[0].format('YYYY-MM-DD HH:mm:ss');
- e_time = time[1].format('YYYY-MM-DD HH:mm:ss');
- timerRef.current = { s_time, e_time };
- setTime(time);
- run();
- }
- };
- useEffect(() => {
- chartRef.current = echarts.init(domRef.current);
- return () => {
- chartRef.current.dispose();
- };
- }, []);
- useEffect(() => {
- if (current?.device_code && active) {
- run();
- } else {
- chartRef.current.clear();
- }
- }, [current, active]);
- return (
- <div className={styles.chartBox}>
- <div className={styles.chartBoxTop}>
- <DeviceList
- active={active}
- projectId={projectId}
- current={current}
- onClick={setCurrent}
- />
- <div style={{ display: 'flex' }}>
- <div className={styles.dateTabs}>
- {DateTab.map((item) => (
- <div
- key={item.value}
- className={`${styles.dateTabsItem} ${
- item.value == dateActive ? styles.active : ''
- }`}
- onClick={() => searchTime(item.value)}
- >
- {item.label}
- </div>
- ))}
- </div>
- {active == 1 && (
- <AreaChartOutlined
- style={{ float: 'right', lineHeight: '56px', marginRight: 20 }}
- onClick={() => setVisible(true)}
- />
- )}
- </div>
- </div>
- <Spin spinning={loading}>
- <div ref={domRef} style={{ height: '30vh' }} />
- </Spin>
- <Optimization data={data?.list?.[0]} />
- <MembraneModal
- visible={visible}
- onCancel={() => setVisible(false)}
- params={params}
- />
- </div>
- );
- };
- const DeviceList = (props) => {
- const { current, onClick, projectId, active } = props;
- const { data, loading, run } = useRequest(
- () => {
- let params = {
- page: 1,
- page_size: 99999,
- project_id: projectId,
- };
- return TYPE[active]?.device(params);
- },
- {
- manual: true,
- onSuccess(data) {
- if (data.list?.[0]) {
- onClick(data.list[0]);
- } else {
- onClick(null);
- }
- },
- },
- );
- const handleChange = (value) => {
- let current = data.list.find((item) => item.device_code == value);
- onClick(current);
- };
- useEffect(() => {
- if (active) run();
- }, [active]);
- return (
- <Select
- style={{ width: 200 }}
- onChange={handleChange}
- value={current?.device_code}
- >
- {data?.list.map((item) => (
- <Option key={item.device_code}>{item.device_code}</Option>
- ))}
- </Select>
- );
- };
- function getOption(data = [], active) {
- let formatter,
- yAxisName = '',
- series = [],
- xAxis = [];
- var data1 = [],
- data2 = [],
- data3 = [],
- data4 = [];
- switch (active) {
- case 'tt_backwash':
- yAxisName = '清洗周期/Min';
- formatter = (params) => {
- let item = data[params[0].dataIndex];
- let content = '';
- if (item.bw_type == 1) {
- // PEB
- content += 'PEB 反洗开始时间:' + item.peb_st;
- content += '<br />PEB 反洗结束时间:' + item.peb_et;
- } else {
- // CEB
- content += 'CEB 反洗开始时间:' + item.ceb_st;
- content += '<br />CEB 反洗结束时间:' + item.ceb_et;
- content += '<br />CEB 清洗剂浓度' + item.ceb_ppm;
- }
- return content;
- };
- data?.forEach((item) => {
- if (item.bw_type == 1) {
- // 实际冲洗
- data1.push(Math.ceil(item.peb_interval / 60));
- // TODO:冲洗
- data2.push(Math.ceil(item.peb_interval / 60));
- data3.push(null);
- data4.push(null);
- xAxis.push(dayjs(item.peb_st).format('YYYY-MM-DD HH:mm:ss'));
- } else {
- data1.push(null);
- data2.push(null);
- data3.push(Math.ceil(item.peb_interval / 60));
- // TODO:模拟冲洗
- data4.push(Math.ceil(item.peb_interval / 60));
- xAxis.push(dayjs(item.ceb_st).format('YYYY-MM-DD HH:mm:ss'));
- }
- });
- series = [
- {
- name: '物理实际冲洗',
- type: 'bar',
- barMaxWidth: '20px',
- data: data1,
- },
- {
- name: '物理模拟冲洗',
- type: 'bar',
- barMaxWidth: '20px',
- data: data2,
- },
- {
- name: '化学实际冲洗',
- type: 'bar',
- barMaxWidth: '20px',
- data: data3,
- },
- {
- name: '化学模拟冲洗',
- type: 'bar',
- barMaxWidth: '20px',
- data: data4,
- },
- ];
- break;
- case 'tt_wash':
- yAxisName = '清洗周期/Min';
- formatter = (params) => {
- let item = data[params[0].dataIndex];
- let content = '';
- content += '反洗开始时间:' + item.st;
- content += '<br />反洗结束时间:' + item.et;
- return content;
- };
- data?.forEach((item) => {
- // 实际冲洗
- data1.push(Math.ceil(item.interval / 60));
- // TODO:模拟冲洗
- data2.push(Math.ceil(item.interval / 60));
- xAxis.push(dayjs(item.st).format('YYYY-MM-DD HH:mm:ss'));
- });
- series = [
- {
- name: '实际冲洗',
- type: 'bar',
- barMaxWidth: '20px',
- data: data1,
- },
- {
- name: '模拟冲洗',
- type: 'bar',
- barMaxWidth: '20px',
- data: data2,
- },
- ];
- break;
- case 'tt_nob':
- yAxisName = '杀菌周期/Min';
- formatter = (params) => {
- let item = data[params[0].dataIndex];
- let content = '';
- content += '杀菌开始时间:' + (item.st || '-');
- content += '<br />杀菌结束时间:' + (item.et || '-');
- return content;
- };
- var data1 = [],
- data2 = [];
- data?.forEach((item) => {
- // 实际冲洗
- data1.push(Math.ceil(item.interval / 60));
- // TODO:模拟冲洗
- data2.push(Math.ceil(item.interval / 60));
- xAxis.push(dayjs(item.st).format('YYYY-MM-DD HH:mm:ss'));
- });
- series = [
- {
- name: '实际',
- type: 'bar',
- barMaxWidth: '20px',
- data: data1,
- },
- {
- name: '模拟',
- type: 'bar',
- barMaxWidth: '20px',
- data: data2,
- },
- ];
- break;
- case 'tdr_hci':
- case 'tdr_nob':
- case 'tdr_pac':
- yAxisName = '投加量';
- // formatter = params => {
- // let content = '';
- // let item = data[params[0].dataIndex];
- // content += item.c_time;
- // content += '<br />最高加药浓度' + item.dosh + 'g/m3';
- // content += '<br />最低加药浓度:' + item.dosl + 'g/m3';
- // content += '<br />最高加药浊度:' + item.tubh + 'g/m3';
- // content += '<br />最低加药浊度:' + item.tubl + 'g/m3';
- // content += '<br />实际进水浊度:' + item.tubr;
- // return content;
- // };
- data?.forEach((item) => {
- // 实际冲洗
- data1.push(Math.ceil(item.fr / 60));
- // TODO:模拟冲洗
- data2.push(Math.ceil(item.fcoa / 60));
- xAxis.push(dayjs(item.c_time).format('YYYY-MM-DD HH:mm:ss'));
- });
- series = [
- {
- name: '实际物理投加量',
- type: 'bar',
- barMaxWidth: '20px',
- data: data1,
- },
- {
- name: '理论物理投加量',
- type: 'bar',
- barMaxWidth: '20px',
- data: data2,
- },
- ];
- break;
- case 'td_uf':
- case 'td_mf':
- case 'td_nf':
- yAxisName = '渗透率';
- data?.forEach((item) => {
- // 实际跨膜压差
- data1.push(item.permeability);
- // 模拟跨膜压差
- data2.push(item.std_permeability);
- xAxis.push(dayjs(item.c_time).format('YYYY-MM-DD HH:mm:ss'));
- });
- series = [
- {
- name: '实际渗透率',
- type: 'line',
- data: data1,
- showSymbol: false,
- },
- {
- name: '模拟渗透率',
- type: 'line',
- data: data2,
- showSymbol: false,
- },
- ];
- break;
- case 'td_ro':
- yAxisName = '跨膜压差';
- data?.forEach((item) => {
- // 实际跨膜压差
- data1.push(item.extend['1st_Stage_DP']);
- data2.push(item.extend['2nd_Stage_DP']);
- // 模拟跨膜压差
- data3.push(item.stabilize_extend['1st_Stage_DP']);
- data4.push(item.stabilize_extend['2nd_Stage_DP']);
- xAxis.push(dayjs(item.c_time).format('YYYY-MM-DD HH:mm:ss'));
- });
- series = [
- {
- name: '实际一段跨膜压差',
- type: 'line',
- data: data1,
- showSymbol: false,
- },
- {
- name: '实际二段跨膜压差',
- type: 'line',
- data: data2,
- showSymbol: false,
- },
- {
- name: '模拟一段跨膜压差',
- type: 'line',
- data: data3,
- showSymbol: false,
- },
- {
- name: '模拟二段跨膜压差',
- type: 'line',
- data: data4,
- showSymbol: false,
- },
- ];
- break;
- }
- const option = {
- color: ['#FFC800', '#30EDFD', '#4096ff', '#ff4d4f', '#ffa940'],
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow',
- },
- formatter,
- },
- legend: {
- textStyle: {
- // color: '#fff',
- fontSize: 24,
- },
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true,
- },
- xAxis: {
- type: 'category',
- data: xAxis,
- nameTextStyle: {
- fontSize: 24,
- },
- axisLabel: {
- fontSize: 24,
- },
- },
- yAxis: {
- name: yAxisName,
- type: 'value',
- boundaryGap: [0, 0.01],
- nameTextStyle: {
- fontSize: 24,
- },
- axisLabel: {
- fontSize: 24,
- },
- },
- series,
- };
- return option;
- }
- const MembraneModal = (props) => {
- const { visible, onCancel, params } = props;
- const domRef = useRef(null);
- const chartRef = useRef(null);
- const { run, loading } = useRequest(queryMembraneConditions, {
- manual: true,
- onSuccess(data) {
- console.log(data);
- let options = getMembraneOption(data.list);
- chartRef.current.setOption(options, true);
- },
- });
- useEffect(() => {
- if (visible) {
- if (!chartRef.current) {
- chartRef.current = echarts.init(document.getElementById('chart'));
- run(params);
- }
- }
- }, [visible]);
- return (
- <Modal
- forceRender
- title="渗透率图表"
- open={visible}
- onCancel={onCancel}
- footer={null}
- >
- <Spin spinning={loading}>
- <div id="chart" style={{ height: '60vh' }} />
- </Spin>
- </Modal>
- );
- };
- function getMembraneOption(data = []) {
- const option = {
- color: ['#FFC800', '#30EDFD', '#4096ff', '#ff4d4f', '#ffa940'],
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow',
- },
- },
- legend: {
- textStyle: {
- // color: '#fff',
- fontSize: 18,
- },
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true,
- },
- xAxis: {
- type: 'category',
- data: data.map((item) => item.c_time),
- axisLine: {
- lineStyle: {
- // color: '#fff',
- },
- },
- splitLine: {
- lineStyle: {
- // color: '#fff',
- },
- },
- axisLabel: {
- // color: '#fff',
- },
- },
- yAxis: {
- name: '渗透率',
- type: 'value',
- boundaryGap: [0, 0.01],
- axisLine: {
- lineStyle: {
- // color: '#fff',
- },
- },
- splitLine: {
- lineStyle: {
- // color: '#fff',
- },
- },
- axisLabel: {
- // color: '#fff',
- },
- },
- series: [
- {
- type: 'line',
- showSymbol: false,
- areaStyle: {
- opacity: 0.1,
- },
- type: 'line',
- smooth: true,
- data: data.map((v) => v.permeability),
- },
- ],
- };
- return option;
- }
- const Optimization = ({ data }) => {
- if (!data?.optimization) return '';
- const NAME_MAP = {
- peb_interval: '反冲洗周期调整',
- pac_fr: '絮凝剂投加建议',
- ceb_residue_count: '超滤微滤, 剩余药洗次数',
- ceb_permeability: '超滤微滤, 渗透率低于阈值, 药洗提醒',
- ceb_time_expire: '超滤微滤药洗提醒',
- ro_pressure_1st: 'ro一段运行状况',
- ro_pressure_2nd: 'ro二段运行状况',
- ro_pressure_3th: 'ro三段运行状况',
- ro_nob_interval: 'ro非氧化杀菌调整',
- ro_wash_interval: 'ro冲洗调整',
- ro_residue_count: 'ro化学清洗后,剩余的可清洗次数',
- };
- return (
- <div className={styles.optimization}>
- <div className={styles.title1}>
- {dayjs(data.c_time).format('YYYY-MM-DD HH:mm')}
- </div>
- <div className={styles.title2}>调整内容</div>
- {Object.entries(data.optimization).map(([key, item]) => (
- <div className={styles.content}>
- 【{NAME_MAP[key]}】{item.remark}
- </div>
- ))}
- </div>
- );
- };
- export default SimulateDetail;
|