import PageContent from '@/components/PageContent'; import PageTitle from '@/components/PageTitle'; import TabsContent from '@/components/TabsContent'; import { queryBackwash, queryBackwashList, queryDesignNob, queryDesignNobList, queryDesignWash, queryDesignWashList, queryDrug, queryDrugList, queryMembrane, queryMembraneList, queryPump, queryPumpList, } from '@/services/SmartOps'; import { UnityAction } from '@/utils/utils'; import { useNavigate, useParams, useRequest, useSearchParams, } from '@umijs/max'; import { Collapse, DatePicker, Empty, Form, Spin } from 'antd'; import dayjs from 'dayjs'; import * as echarts from 'echarts'; import { useEffect, useMemo, useRef, useState } from 'react'; import styles from './WorkAnalysisDetail.less'; 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, }, td_pump_nf: { name: '纳滤水泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_nf' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_nf' }), }, td_pump_uf: { name: '超滤水泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_uf' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_uf' }), }, td_pump_ro: { // td_pump: { name: '反渗透水泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_ro' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_ro' }), }, td_pump_mf: { name: '微滤水泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_mf' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_mf' }), }, td_pump_nf_drug: { name: '加药泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_nf_drug' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_nf_drug' }), }, td_pump_uf_drug: { name: '加药泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_uf_drug' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_uf_drug' }), }, td_pump_ro_drug: { // td_pump: { name: '加药泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_ro_drug' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_ro_drug' }), }, td_pump_mf_drug: { name: '加药泵', device: (params) => queryPumpList({ ...params, stage: 'td_pump_mf_drug' }), chart: (params) => queryPump({ ...params, stage: 'td_pump_mf_drug' }), }, }; const { Panel } = Collapse; const { RangePicker } = DatePicker; const DateTab = [ { value: 'day', label: '今日', }, { value: 'week', label: '本周', }, { value: 'month', label: '本月', }, ]; function WorkAnalysisDetail(props) { const navigate = useNavigate(); const { projectId } = useParams(); const [searchParams, setSearchParams] = useSearchParams(); const eTime = searchParams.get('eTime'); const workAnalysis = useMemo(() => { let workAnalysis = { technologys: [], optimizationItems: {}, optimizationNumber: 0, }; try { workAnalysis = JSON.parse(sessionStorage.workAnalysis); workAnalysis.technologys = workAnalysis.technologys.filter((item) => { if (!TYPE[item]) console.log(item); return TYPE[item]; }); } catch (error) { console.error(error); } return workAnalysis; }, []); const { technologys, optimizationItems, optimizationNumber, parentName, name, } = workAnalysis; const [active, setActive] = useState(technologys[0]); const [current, setCurrent] = useState([]); const [selected, setSelected] = useState(''); const handleBackClick = () => { UnityAction.sendMsg('ProcessAnalysisDetailBack'); navigate(-1); }; useEffect(() => { UnityAction.addEventListener('SynDev', (deviceCode) => setCurrent([deviceCode]), ); return () => UnityAction.off('SynDev'); }, []); return ( 返回
{parentName}:{name} {optimizationNumber > 0 && `${optimizationNumber}项待优化`}
({ label: TYPE[item]?.name, key: item, children: ( ), }))} onChange={(key) => { setCurrent([]); setActive(key); }} >
); } const ListContent = (props) => { const { tab, projectId, active, optimizationItems = {}, name, current, setCurrent, eTime, selected, setSelected, } = props; const { data, loading, run } = useRequest( () => { let params = { page: 1, page_size: 99999, project_id: projectId, }; return TYPE[active]?.device(params); }, { formatResult: (res) => { return res.data.list; }, }, ); useEffect(() => { if (tab == active && data) { sendMsg(); } UnityAction.on('SynDev', selectedItem); return () => UnityAction.off('SynDev'); }, [tab, active, data]); const selectedItem = (e) => { setSelected(e); setCurrent((current) => [...current, `${active}&&${e}`]); }; //通知unity切换tab发送tab数据 const sendMsg = () => { const SysDevs = {}; data?.forEach((item) => { SysDevs[item.device_code] = optimizationItems?.[`${active}:${item.device_code}`] || 0; }); const msg = { SysName: name, SysDevs }; UnityAction.sendMsg('ProcessAnalysisDetail', JSON.stringify(msg)); }; // const activeKeys = useMemo(() => { // if (!data) return []; // return current.filter((deviceCode) => // data.some((item) => item.device_code == deviceCode), // ); // }, [data, current]); const handleClick = (keys) => { // 判断是开启还是关闭 // 设置当前选中行 if (keys.length > current.length) { const code = keys[keys.length - 1]; UnityAction.sendMsg('SynDev', code.split('&&')[1]); setSelected(code); } else if (current.length > 0) { const item = current.find( (item) => keys.findIndex((cur) => cur == item) == -1, ); setSelected(item); } setCurrent(keys); }; const getExtra = (deviceCode) => { if (optimizationItems?.[`${active}:${deviceCode}`]) { return 可优化; } else { return 暂无优化; } }; const getStyle = (code) => { return code == selected ? { backgroundColor: '#1b73c5' } : null; }; return ( {data?.map((item) => ( ))} ); }; const ChartContent = (props) => { const { deviceCode, projectId, active, eTime } = props; const [visible, setVisible] = useState(false); const [params, setParams] = useState(null); const [time, setTime] = useState([ dayjs(eTime).subtract(10, 'minute'), dayjs(eTime), ]); const [dateActive, setDateActive] = useState(); 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, loading, run } = useRequest(() => { let params = { device_code: deviceCode, page: 1, page_size: 9999, project_id: projectId, ...timerRef.current, }; return TYPE[active].chart(params); }); const optimization = useMemo(() => { const result = data?.list?.filter((item) => item.optimization); if (result?.length > 0) { const lastItem = result[result.length - 1]; const arr = [ 'ro_wash_interval', 'peb_interval', 'pac_fr', 'ro_nob_interva', ]; const valueList = Object.entries(lastItem.optimization) .map(([key, item]) => { if (!arr.includes(key)) return null; return item.remark; }) .filter((item) => item); if (valueList.length == 0) return ''; return `【${lastItem.c_time}】${valueList.join(',')}`; } return ''; }, [data]); const searchTime = (type) => { setDateActive(type); let time = [dayjs().startOf(type), dayjs()]; onSearch?.(time); }; const onSearch = (time) => { console.log(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(() => { if (data && data.list && domRef.current) { chartRef.current = echarts.init(domRef.current); chartRef.current.clear(); let options = getOption(data.list, active); chartRef.current.setOption(options, true); chartRef.current.resize(); } return () => { if (chartRef.current) { chartRef.current.dispose(); } }; }, [data, domRef.current]); return (
{DateTab.map((item) => (
searchTime(item.value)} > {item.label}
))}
{!data?.list && } {data?.list && (
console.log(1)} /> )}
{optimization && (
{optimization}
)} {/* setVisible(false)} params={params} /> */}
); }; function getOption(data = [], active) { let formatter, yAxisName = '', y2AxisName = '', 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) => { let time = 0; if (item.bw_type == 1) { if (item.peb_et && item.peb_st) { time = dayjs(item.peb_st).diff(dayjs(item.peb_et), 'minutes'); } // 实际冲洗 data1.push(time); data2.push(null); xAxis.push(dayjs(item.peb_st).format('YYYY-MM-DD HH:mm:ss')); } else { if (item.ceb_st && item.ceb_et) { time = dayjs(item.ceb_st).diff(dayjs(item.ceb_et), 'minutes'); } data1.push(null); data2.push(time); xAxis.push(dayjs(item.ceb_st).format('YYYY-MM-DD HH:mm:ss')); } }); series = [ { name: '物理实际冲洗', type: 'bar', barMaxWidth: '0.2rem', data: data1, }, { name: '物理模拟冲洗', type: 'bar', barMaxWidth: '0.2rem', data: data2, }, { name: '化学实际冲洗', type: 'bar', barMaxWidth: '0.2rem', data: data3, }, { name: '化学模拟冲洗', type: 'bar', barMaxWidth: '0.2rem', 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: '0.2rem', data: data1, }, { name: '模拟冲洗', type: 'bar', barMaxWidth: '0.2rem', 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: '0.2rem', data: data1, }, { name: '模拟', type: 'bar', barMaxWidth: '0.2rem', data: data2, }, ]; break; case 'tdr_hci': case 'tdr_nob': case 'tdr_pac': yAxisName = '投加量'; 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: '0.2rem', data: data1, }, { name: '理论物理投加量', type: 'bar', barMaxWidth: '0.2rem', data: data2, }, ]; break; case 'td_uf': case 'td_mf': case 'td_nf': yAxisName = '渗透率'; data?.forEach((item) => { // 实际跨膜压差 data1.push(item.std_tmp); // 模拟跨膜压差 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; case 'td_pump_nf': case 'td_pump_uf': case 'td_pump_ro': // case 'td_pump': case 'td_pump_mf': case 'td_pump_nf_drug': case 'td_pump_mf_drug': case 'td_pump_mf_drug': case 'td_pump_mf_drug': yAxisName = '频率 Hz'; y2AxisName = '电流 A'; data?.forEach((item) => { // 实际跨膜压差 data1.push(item.frequency); data2.push(item.current); 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, yAxisIndex: 1, showSymbol: false, }, ]; break; } // 过滤失效数据 xAxis.forEach((time, index) => { if (time === 'Invalid date') { xAxis[index] = null; data1[index] = null; data2[index] = null; data3[index] = null; data4[index] = null; } }); xAxis = xAxis.filter((item) => item); data1 = data1.filter((item) => !isNaN(item)); data2 = data2.filter((item) => !isNaN(item)); data3 = data3.filter((item) => !isNaN(item)); data4 = data4.filter((item) => !isNaN(item)); const option = { color: ['#FFC800', '#30EDFD', '#4096ff', '#ff4d4f', '#ffa940'], tooltip: { trigger: 'axis', axisPointer: { type: 'shadow', }, textStyle: { fontSize: 12, }, formatter, }, legend: { textStyle: { // color: '#fff', fontSize: 14, }, type: 'scroll', }, grid: { top: 80, left: '3%', right: '4%', bottom: '3%', containLabel: true, }, xAxis: { type: 'category', data: xAxis, nameTextStyle: { fontSize: 12, }, axisLabel: { fontSize: 12, }, }, yAxis: { name: yAxisName, type: 'value', boundaryGap: [0, 0.01], nameTextStyle: { fontSize: 14, }, axisLabel: { fontSize: 12, }, splitNumber: 5, }, series, }; if (y2AxisName) { let y1 = option.yAxis; let y2 = JSON.parse(JSON.stringify(y1)); y1.max = getMax(data1) || 1; y1.interval = y1.max / 5; y2.name = y2AxisName; y2.max = getMax(data2) || 1; y2.interval = y2.max / 5; option.yAxis = [y1, y2]; } console.log(option); return option; } function getMax(arr) { const max = Math.max(...arr); const exponent = Math.floor(Math.log10(max)); const base = Math.pow(10, exponent); const remainder = max % base; const maxRoundUp = max - remainder + base; const maxFixed = maxRoundUp.toFixed(1); // 将最大值的小数位数限制为 1 return Number(maxFixed); // 将字符串转换为数字并返回 } export default WorkAnalysisDetail;