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 (
{!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;