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 (
{data && ( ({ label: TYPE[item]?.name, key: item, children:
, }))} onChange={(value) => { setActive(value); setCurrent(null); }} >
)}
); }; 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 (
{DateTab.map((item) => (
searchTime(item.value)} > {item.label}
))}
{active == 1 && ( setVisible(true)} /> )}
setVisible(false)} params={params} />
); }; 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 ( ); }; 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 += '
PEB 反洗结束时间:' + item.peb_et; } else { // CEB content += 'CEB 反洗开始时间:' + item.ceb_st; content += '
CEB 反洗结束时间:' + item.ceb_et; content += '
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 += '
反洗结束时间:' + 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 += '
杀菌结束时间:' + (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 += '
最高加药浓度' + item.dosh + 'g/m3'; // content += '
最低加药浓度:' + item.dosl + 'g/m3'; // content += '
最高加药浊度:' + item.tubh + 'g/m3'; // content += '
最低加药浊度:' + item.tubl + 'g/m3'; // content += '
实际进水浊度:' + 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 (
); }; 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 (
{dayjs(data.c_time).format('YYYY-MM-DD HH:mm')}
调整内容
{Object.entries(data.optimization).map(([key, item]) => (
【{NAME_MAP[key]}】{item.remark}
))}
); }; export default SimulateDetail;