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;