593 lines
16 KiB
JavaScript
593 lines
16 KiB
JavaScript
|
import React, { useRef, useEffect, useState, useMemo } from "react";
|
|||
|
import * as echarts from "echarts";
|
|||
|
import { SearchOutlined } from "@ant-design/icons";
|
|||
|
import _, { indexOf } from "lodash";
|
|||
|
import moment from "moment";
|
|||
|
import "antd/dist/antd.css";
|
|||
|
import { Select, Button, DatePicker, Checkbox, message, Spin } from "antd";
|
|||
|
import {
|
|||
|
getFactory,
|
|||
|
getEquipType,
|
|||
|
getEquip,
|
|||
|
getEquipmentLocation,
|
|||
|
getEquipmentLocationEchart,
|
|||
|
} from "../../../reportUtils/getQueryData";
|
|||
|
const { Option } = Select;
|
|||
|
import theme from "../../../reportUtils/theme.json";
|
|||
|
|
|||
|
function EquipmentCurve() {
|
|||
|
const dataZoomRef = useRef();
|
|||
|
let refinput = useRef();
|
|||
|
|
|||
|
echarts.registerTheme("black", theme);
|
|||
|
// 设备类型集合
|
|||
|
const [equipType, setEquipType] = useState([]);
|
|||
|
// 车间集合
|
|||
|
const [factory, setFactory] = useState([]);
|
|||
|
// 设备集合
|
|||
|
const [equip, setEquip] = useState([]);
|
|||
|
//设备类型值
|
|||
|
const [equipTypeValue, setEquipTypeValue] = useState("");
|
|||
|
//车间值
|
|||
|
const [factoryValue, setFactoryValue] = useState("");
|
|||
|
//设备值
|
|||
|
const [equipValue, setEquipValue] = useState("");
|
|||
|
// 点位集合
|
|||
|
const [locations, setLocations] = useState([]);
|
|||
|
// 点位变化值
|
|||
|
const [locationValue, setLocationValue] = useState([]);
|
|||
|
// 时间变化值
|
|||
|
const [dateValue, setDateValue] = useState("");
|
|||
|
// 存放chartData
|
|||
|
const [chartData, setChartData] = useState({});
|
|||
|
// 存放chartref
|
|||
|
const [chartRef, setChartRef] = useState([]);
|
|||
|
const [spin, setSpin] = useState(false);
|
|||
|
const equipTypeChange = (value) => {
|
|||
|
setEquipTypeValue(value);
|
|||
|
};
|
|||
|
const factoryChange = (value) => {
|
|||
|
setFactoryValue(value);
|
|||
|
};
|
|||
|
const equipChange = (value = "") => {
|
|||
|
if (value && equipValue !== value) {
|
|||
|
setLocationValue([]);
|
|||
|
setChartData({});
|
|||
|
setChartRef([]);
|
|||
|
getEquipmentLocation(value).then((data) => {
|
|||
|
let locationArr = data
|
|||
|
.sort((a, b) => (a.iotfieldsort > b.iotfieldsort ? 1 : -1))
|
|||
|
.map((item) => {
|
|||
|
return {
|
|||
|
label: item.iotfielddescribe,
|
|||
|
value: item.iotField,
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
setLocations(locationArr);
|
|||
|
});
|
|||
|
} else {
|
|||
|
setLocationValue([]);
|
|||
|
setChartData({});
|
|||
|
setChartRef([]);
|
|||
|
setLocations([]);
|
|||
|
}
|
|||
|
setEquipValue(value);
|
|||
|
};
|
|||
|
const locationChange = (checkValue) => {
|
|||
|
setLocationValue(checkValue);
|
|||
|
if (checkValue.length > 0) {
|
|||
|
doSearch(checkValue);
|
|||
|
} else {
|
|||
|
setChartRef([]);
|
|||
|
}
|
|||
|
};
|
|||
|
const dateChange = (date, dateString) => {
|
|||
|
setDateValue(dateString);
|
|||
|
};
|
|||
|
const handleCancelClick = () => {
|
|||
|
setLocationValue([]);
|
|||
|
setChartRef([]);
|
|||
|
};
|
|||
|
const handleClick = () => {
|
|||
|
if (!equipValue) {
|
|||
|
message.warning("请选择需要查询的设备!");
|
|||
|
} else if (!dateValue) {
|
|||
|
message.warning("请选择需要查询的时间!");
|
|||
|
} else if (locationValue.length === 0) {
|
|||
|
message.warning("请勾选需要查询的点位!");
|
|||
|
} else {
|
|||
|
setSpin(true);
|
|||
|
doSearch();
|
|||
|
}
|
|||
|
};
|
|||
|
// 查询设备类型,查询车间,
|
|||
|
useEffect(() => {
|
|||
|
getEquipType().then((data) => {
|
|||
|
setEquipType(data);
|
|||
|
});
|
|||
|
getFactory().then((data) => {
|
|||
|
setFactory(data);
|
|||
|
});
|
|||
|
}, []);
|
|||
|
// 查询设备 监听设备类型
|
|||
|
useEffect(() => {
|
|||
|
getEquip({ equipTypeValue, factoryValue }).then((data) => {
|
|||
|
setEquip(data);
|
|||
|
setEquipValue("");
|
|||
|
setLocations([]);
|
|||
|
setLocationValue([]);
|
|||
|
setChartData({});
|
|||
|
setChartRef([]);
|
|||
|
});
|
|||
|
}, [equipTypeValue, factoryValue]);
|
|||
|
useEffect(() => {
|
|||
|
let dataZoomChart = echarts.init(dataZoomRef.current);
|
|||
|
dataZoomChart.clear();
|
|||
|
if (locationValue.length > 0) {
|
|||
|
if (!dataZoomRef.current) {
|
|||
|
return;
|
|||
|
}
|
|||
|
let dataZoomData = {};
|
|||
|
let flag = true;
|
|||
|
for (let i = 0; i < locationValue.length; i++) {
|
|||
|
if (flag && chartData[locationValue[i]]) {
|
|||
|
dataZoomData = chartData[locationValue[i]];
|
|||
|
flag = false;
|
|||
|
}
|
|||
|
}
|
|||
|
let dataZoomChart = echarts.init(dataZoomRef.current);
|
|||
|
let option = {
|
|||
|
xAxis: {
|
|||
|
type: "category",
|
|||
|
data: dataZoomData["times"] || [],
|
|||
|
},
|
|||
|
yAxis: {
|
|||
|
type: "value",
|
|||
|
scale: true,
|
|||
|
},
|
|||
|
legend: {
|
|||
|
show: false,
|
|||
|
height: "10",
|
|||
|
width: "2000",
|
|||
|
},
|
|||
|
dataZoom: {
|
|||
|
top: "10",
|
|||
|
left: "0",
|
|||
|
type: "slider",
|
|||
|
width: "92%",
|
|||
|
height: "30px",
|
|||
|
},
|
|||
|
series: [
|
|||
|
{
|
|||
|
data: dataZoomData["values"] || [],
|
|||
|
type: "line",
|
|||
|
},
|
|||
|
],
|
|||
|
};
|
|||
|
dataZoomChart.setOption(option);
|
|||
|
// 动态生成echart
|
|||
|
let childsEchart = [];
|
|||
|
|
|||
|
for (let i = 0; i < chartRef.length; i++) {
|
|||
|
if (!chartData[chartRef[i]]) {
|
|||
|
break;
|
|||
|
}
|
|||
|
let dom = document.getElementById(chartRef[i]);
|
|||
|
let echatItem = echarts.init(dom, "black");
|
|||
|
let optionItem = {
|
|||
|
grid: {
|
|||
|
top: "30",
|
|||
|
height: "125",
|
|||
|
},
|
|||
|
title: {
|
|||
|
text: `${chartData[chartRef[i]]["title"]}`,
|
|||
|
x: "center",
|
|||
|
y: "top",
|
|||
|
textAlign: "left",
|
|||
|
},
|
|||
|
tooltip: {
|
|||
|
trigger: "axis",
|
|||
|
axisPointer: {
|
|||
|
// link: null,
|
|||
|
animation: true,
|
|||
|
type: "cross",
|
|||
|
},
|
|||
|
formatter: function (params) {
|
|||
|
//在此处直接用 formatter 属性
|
|||
|
let showdata = params[0];
|
|||
|
// 根据自己的需求返回数据
|
|||
|
return `
|
|||
|
<div>时间:${showdata.axisValueLabel}</div>
|
|||
|
<div>数据:<a style="color: #00E8D7">${showdata.data}</a></div>
|
|||
|
`;
|
|||
|
},
|
|||
|
},
|
|||
|
xAxis: {
|
|||
|
type: "category",
|
|||
|
data: chartData[chartRef[i]]["times"],
|
|||
|
},
|
|||
|
yAxis: {
|
|||
|
type: "value",
|
|||
|
},
|
|||
|
legend: {
|
|||
|
show: false,
|
|||
|
height: "10",
|
|||
|
width: "2000",
|
|||
|
},
|
|||
|
dataZoom: {
|
|||
|
type: "inside",
|
|||
|
zoomOnMouseWheel: false,
|
|||
|
},
|
|||
|
series: [
|
|||
|
{
|
|||
|
symbol: "circle",
|
|||
|
symbolSize: 5,
|
|||
|
data: chartData[chartRef[i]]["values"],
|
|||
|
type: "line",
|
|||
|
},
|
|||
|
],
|
|||
|
};
|
|||
|
echatItem.setOption(optionItem);
|
|||
|
childsEchart.push(echatItem);
|
|||
|
}
|
|||
|
echarts.connect([dataZoomChart].concatchildsEchart);
|
|||
|
}
|
|||
|
}, [chartRef]);
|
|||
|
return (
|
|||
|
<div>
|
|||
|
<Spin spinning={spin}>
|
|||
|
<div className="header">
|
|||
|
<div className="search">
|
|||
|
<div className="workShop">
|
|||
|
<span>车间:</span>
|
|||
|
<Select
|
|||
|
allowClear
|
|||
|
size="small"
|
|||
|
style={{ width: 120 }}
|
|||
|
onChange={factoryChange}
|
|||
|
value={factoryValue}
|
|||
|
>
|
|||
|
{factory.map((item) => {
|
|||
|
return (
|
|||
|
<Option value={item.code} key={item.gid}>
|
|||
|
{item.name}
|
|||
|
</Option>
|
|||
|
);
|
|||
|
})}
|
|||
|
</Select>
|
|||
|
</div>
|
|||
|
<div className="equipType">
|
|||
|
<span>设备类型:</span>
|
|||
|
<Select
|
|||
|
allowClear
|
|||
|
size="small"
|
|||
|
style={{ width: 120 }}
|
|||
|
onChange={equipTypeChange}
|
|||
|
value={equipTypeValue}
|
|||
|
>
|
|||
|
{equipType.map((item) => {
|
|||
|
return (
|
|||
|
<Option value={item.equipTypeGid} key={item.gid}>
|
|||
|
{item.name}
|
|||
|
</Option>
|
|||
|
);
|
|||
|
})}
|
|||
|
</Select>
|
|||
|
</div>
|
|||
|
<div className="equipNo">
|
|||
|
<span>设备编号:</span>
|
|||
|
<Select
|
|||
|
showSearch
|
|||
|
allowClear
|
|||
|
size="small"
|
|||
|
onChange={equipChange}
|
|||
|
optionFilterProp="children"
|
|||
|
style={{ width: 150 }}
|
|||
|
value={equipValue}
|
|||
|
filterOption={(input, option) =>
|
|||
|
option.children.toLowerCase().includes(input.toLowerCase())
|
|||
|
}
|
|||
|
>
|
|||
|
{equip.map((item) => {
|
|||
|
return (
|
|||
|
<Option
|
|||
|
value={item.serialNumber}
|
|||
|
key={item.serialNumber + item.name}
|
|||
|
>
|
|||
|
{item.name}
|
|||
|
</Option>
|
|||
|
);
|
|||
|
})}
|
|||
|
</Select>
|
|||
|
</div>
|
|||
|
<div className="equipDate">
|
|||
|
<span>查询日期:</span>
|
|||
|
<DatePicker
|
|||
|
style={{ width: 150 }}
|
|||
|
size="small"
|
|||
|
onChange={dateChange}
|
|||
|
/>
|
|||
|
</div>
|
|||
|
<div>
|
|||
|
<Button
|
|||
|
type="primary"
|
|||
|
icon={<SearchOutlined />}
|
|||
|
size="small"
|
|||
|
shape="round"
|
|||
|
onClick={handleClick}
|
|||
|
>
|
|||
|
查询
|
|||
|
</Button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div className="dataZoom">
|
|||
|
<div className="zoomTitle">时间窗口选择框:</div>
|
|||
|
<div
|
|||
|
className="zoomContent"
|
|||
|
ref={dataZoomRef}
|
|||
|
style={{
|
|||
|
width: "90%",
|
|||
|
height: "150px",
|
|||
|
overflow: "hidden",
|
|||
|
}}
|
|||
|
></div>
|
|||
|
</div>
|
|||
|
<div className="toolBar flex flex-row">
|
|||
|
<div className=" text-xs ">
|
|||
|
{locations.length > 0 ? (
|
|||
|
<Button
|
|||
|
type="primary"
|
|||
|
className="ml-2 mt-2"
|
|||
|
size="small"
|
|||
|
onClick={handleCancelClick}
|
|||
|
>
|
|||
|
取消全选
|
|||
|
</Button>
|
|||
|
) : (
|
|||
|
<></>
|
|||
|
)}
|
|||
|
</div>
|
|||
|
<div className="legend overflow-y-auto h-32 ">
|
|||
|
<Checkbox.Group
|
|||
|
ref={refinput}
|
|||
|
value={locationValue}
|
|||
|
options={locations}
|
|||
|
onChange={locationChange}
|
|||
|
/>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div className="flex h-2"></div>
|
|||
|
<div className="content ">
|
|||
|
{chartRef.map((item) => {
|
|||
|
return (
|
|||
|
<div
|
|||
|
id={item}
|
|||
|
key={item}
|
|||
|
style={{
|
|||
|
width: "100%",
|
|||
|
height: "200px",
|
|||
|
marginBottom: "16px",
|
|||
|
}}
|
|||
|
></div>
|
|||
|
);
|
|||
|
})}
|
|||
|
</div>
|
|||
|
</Spin>
|
|||
|
<div className={spin ? "mask" : "unmask"}></div>
|
|||
|
<style jsx>{`
|
|||
|
.ant-checkbox-group-item {
|
|||
|
width: 200px !important;
|
|||
|
}
|
|||
|
* {
|
|||
|
margin: 0;
|
|||
|
padding: 0;
|
|||
|
}
|
|||
|
.header {
|
|||
|
position: relative;
|
|||
|
width: 100%;
|
|||
|
height: 35px;
|
|||
|
box-shadow: 5px 2px 20px -4px rgb(0, 0, 0, 0.3);
|
|||
|
}
|
|||
|
.search {
|
|||
|
display: flex;
|
|||
|
height: 35px;
|
|||
|
margin: auto 15px;
|
|||
|
}
|
|||
|
.search div {
|
|||
|
line-height: 35px;
|
|||
|
margin-right: 20px;
|
|||
|
}
|
|||
|
.search .workShop {
|
|||
|
margin-left: 15px;
|
|||
|
}
|
|||
|
.search .equipDate {
|
|||
|
margin-right: 80px;
|
|||
|
}
|
|||
|
.dataZoom {
|
|||
|
position: relative;
|
|||
|
width: 100%;
|
|||
|
height: 50px;
|
|||
|
overflow: hidden;
|
|||
|
}
|
|||
|
.zoomTitle {
|
|||
|
position: absolute;
|
|||
|
top: 50%;
|
|||
|
left: 10px;
|
|||
|
margin-top: -8px;
|
|||
|
}
|
|||
|
.zoomContent {
|
|||
|
position: absolute;
|
|||
|
top: -2px;
|
|||
|
left: 140px;
|
|||
|
bottom: 0;
|
|||
|
right: 0;
|
|||
|
}
|
|||
|
.content {
|
|||
|
width: 100%;
|
|||
|
height: 66vh;
|
|||
|
overflow-y: auto;
|
|||
|
overflow-x: hidden;
|
|||
|
box-sizing: border-box;
|
|||
|
}
|
|||
|
.toolBar {
|
|||
|
position: relative;
|
|||
|
border-width: 1px 0 1px 0;
|
|||
|
border-color: #d3d3d3;
|
|||
|
background: #f8f8f8;
|
|||
|
min-height: 32px;
|
|||
|
}
|
|||
|
|
|||
|
.legend {
|
|||
|
font-size: 12px;
|
|||
|
color: white;
|
|||
|
padding-left: 6%;
|
|||
|
display: flex;
|
|||
|
position: relative;
|
|||
|
flex-wrap: wrap;
|
|||
|
}
|
|||
|
.mask {
|
|||
|
position: fixed;
|
|||
|
top: 0;
|
|||
|
left: 0;
|
|||
|
width: 100%;
|
|||
|
height: 100%;
|
|||
|
background-color: rgba(0, 0, 0, 0.5);
|
|||
|
z-index: 200;
|
|||
|
}
|
|||
|
.unmask {
|
|||
|
display: none;
|
|||
|
}
|
|||
|
`}</style>
|
|||
|
</div>
|
|||
|
);
|
|||
|
|
|||
|
function doSearch(checkValue) {
|
|||
|
let _locationValue = ``;
|
|||
|
if (checkValue) {
|
|||
|
_locationValue = checkValue;
|
|||
|
} else {
|
|||
|
_locationValue = locationValue;
|
|||
|
}
|
|||
|
let params = {
|
|||
|
workCenterCode:`${factoryValue}`,
|
|||
|
startTime: `${dateValue} 00:00:00`,
|
|||
|
endTime: `${dateValue} 23:59:59`,
|
|||
|
equipCode: equipValue,
|
|||
|
fields: _locationValue,
|
|||
|
};
|
|||
|
getEquipmentLocationEchart(params).then((data) => {
|
|||
|
if (data && data.length > 0) {
|
|||
|
createCharts();
|
|||
|
}
|
|||
|
else{
|
|||
|
createChartsEmp();
|
|||
|
message.warning("未能查询到数据!");
|
|||
|
|
|||
|
}
|
|||
|
setSpin(false);
|
|||
|
|
|||
|
function createChartsEmp() {
|
|||
|
let locationsValues = _.cloneDeep(locations);
|
|||
|
let processData = {};
|
|||
|
let commonTimes = [];
|
|||
|
_locationValue.map(key=> {
|
|||
|
if (key !== "time") {
|
|||
|
if (!processData.hasOwnProperty(key)) {
|
|||
|
let index = _.findIndex(locationsValues, function (o) {
|
|||
|
return o.value === key;
|
|||
|
});
|
|||
|
|
|||
|
let title = index >= 0 ? locationsValues[index]["label"] : "";
|
|||
|
processData[key] = {
|
|||
|
title: title,
|
|||
|
key: key,
|
|||
|
times: [],
|
|||
|
values: [],
|
|||
|
};
|
|||
|
}
|
|||
|
// processData[key]["values"].push([]);
|
|||
|
}
|
|||
|
// let time = moment(data[i]["time"]).format("YYYY-MM-DD HH:mm:ss");
|
|||
|
// commonTimes.push(time);
|
|||
|
})
|
|||
|
let domCharts = [];
|
|||
|
commonTimes = _.reverse(commonTimes);
|
|||
|
for (let key in processData) {
|
|||
|
processData[key]["times"] = commonTimes;
|
|||
|
}
|
|||
|
for (let i = 0; i < _locationValue.length; i++) {
|
|||
|
if (processData[_locationValue[i]]) {
|
|||
|
domCharts.push(_locationValue[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
setChartData(processData);
|
|||
|
setChartRef(domCharts);
|
|||
|
}
|
|||
|
|
|||
|
function createCharts() {
|
|||
|
let locationsValues = _.cloneDeep(locations);
|
|||
|
let processData = {};
|
|||
|
let commonTimes = [];
|
|||
|
for (let i = 0; i < data.length; i++) {
|
|||
|
for (let key in data[i]) {
|
|||
|
if (key !== "time") {
|
|||
|
if (!processData.hasOwnProperty(key)) {
|
|||
|
let index = _.findIndex(locationsValues, function (o) {
|
|||
|
return o.value === key;
|
|||
|
});
|
|||
|
|
|||
|
let title = index >= 0 ? locationsValues[index]["label"] : "";
|
|||
|
processData[key] = {
|
|||
|
title: title,
|
|||
|
key: key,
|
|||
|
times: [],
|
|||
|
values: [],
|
|||
|
};
|
|||
|
}
|
|||
|
processData[key]["values"].push(data[i][key]);
|
|||
|
}
|
|||
|
}
|
|||
|
let time = moment(data[i]["time"]).format("YYYY-MM-DD HH:mm:ss");
|
|||
|
commonTimes.push(time);
|
|||
|
}
|
|||
|
let domCharts = [];
|
|||
|
commonTimes = _.reverse(commonTimes);
|
|||
|
for (let key in processData) {
|
|||
|
processData[key]["times"] = commonTimes;
|
|||
|
}
|
|||
|
for (let i = 0; i < _locationValue.length; i++) {
|
|||
|
if (processData[_locationValue[i]]) {
|
|||
|
domCharts.push(_locationValue[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
_locationValue.map(x=>{
|
|||
|
if(domCharts.indexOf(x)===-1){
|
|||
|
domCharts.push(x);
|
|||
|
|
|||
|
let index = _.findIndex(locationsValues, function (o) {
|
|||
|
return o.value === x;
|
|||
|
});
|
|||
|
|
|||
|
let title = index >= 0 ? locationsValues[index]["label"] : "";
|
|||
|
|
|||
|
processData[x] = {
|
|||
|
title: title,
|
|||
|
key: x,
|
|||
|
times: [],
|
|||
|
values: [],
|
|||
|
};
|
|||
|
processData[x]["times"] = commonTimes;
|
|||
|
}
|
|||
|
})
|
|||
|
setChartData(processData);
|
|||
|
setChartRef(domCharts);
|
|||
|
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
export default EquipmentCurve;
|