rtgk-screen-web/pages/screen/processItem/index.js

810 lines
27 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {useRouter} from "next/router";
import React, {useEffect, useMemo, useRef, useState} from "react";
import {Carousel} from 'antd'
import _ from "lodash";
import Footer from "../../../components/screen/Footer";
import Header from "../../../components/screen/Header";
import Chart from "../../../components/screen/Chart";
import Row from "../../../components/screen/Row";
import Card from '../../../components/screen/Card';
import ProcessTaskCard from "../../components/processTaskCard";
import {useSocket} from "../../../utils/hooks";
import moment from "moment/moment";
/**
* 生产分工序
*/
const socketScreenType = 'BranchOperationType';
const gutter = 20;
const bottom = 0;
const chartsBgColor = '#102242';
//关键生产信息
const getProdOpt = ({xData, yData}) => {
let opt = {
xAxis: {
type: 'category',
data: []
},
title: {
text: "L7车间生产进度",
textStyle: {
fontSize: "14px"
},
top: 10,
left: 60
},
yAxis: {
type: 'value'
},
grid: {
top: 40,
left: 55
},
series: [
{
data: [],
type: 'bar'
}
]
};
opt.xAxis.data = xData
opt.series[0].data = yData
return opt;
};
//车间OEE
const getOEEOpt = (data = {}, params = []) => {
const {oeeOfMonth = {}, oeeOfDay = {}} = data;
// passRate 良品率, performanceRate 性能稼动率, timeRate 时间稼动率, oee OEE
if (params.length > 0) {
params[0].values = [oeeOfDay.timeRate || 0, oeeOfDay.performanceRate || 0, oeeOfDay.passRate || 0] //当日
params[0].result = [oeeOfDay.oee || 0]
params[1].values = [oeeOfMonth.timeRate || 0, oeeOfMonth.performanceRate || 0, oeeOfMonth.passRate || 0] //当日
params[1].result = [oeeOfMonth.oee || 0]
}
let opt = {
title: {
text: "",
left: "center",
top: 180,
},
graphic: {
elements: [
{
type: "text",
left: "center",
top: 85,
style: {
fill: "#fff",
text: "",
fontSize: 16,
fontWeight: "bold",
},
},
],
},
legend: {
// icon: "rect",
data: ["时间稼动率", "性能稼动率", "良品率"],
bottom: 10,
orient: "vertical",
itemHeight: 16,
textStyle: {
fontSize: 14,
lineHeight: 14,
},
},
polar: {
radius: ["25%", "65%"],
center: ["50%", "30%"],
},
angleAxis: {
show: false,
max: 100,
startAngle: 90,
},
radiusAxis: {
show: false,
type: "category",
data: ["%"],
},
tooltip: {
formatter: "{a}",
},
series: [
{
name: "时间稼动率",
type: "bar",
barGap: "0",
barWidth: 14,
data: [12],
coordinateSystem: "polar",
},
{
name: "性能稼动率",
type: "bar",
barGap: "0",
barWidth: 14,
data: [13],
coordinateSystem: "polar",
},
{
name: "良品率",
type: "bar",
barGap: "0",
barWidth: 14,
data: [14],
coordinateSystem: "polar",
},
],
};
let res = [];
params.forEach(({name, values, result}) => {
let newOpt = _.cloneDeep(opt);
newOpt.title.text = name;
newOpt.graphic.elements[0].style.text = result + "%";
const names = ["时间稼动率 ", "性能稼动率 ", "良品率 "];
newOpt.legend.data = names.map(
(name, index) => (name = name + ` ${values[index]}%`)
);
newOpt.series = newOpt.series.map((item, index) => {
item.name = names[index] + ` ${values[index]}%`;
item.data[0] = values[index];
return item;
});
res.push(newOpt);
});
return res;
};
//能耗信息
const getUsedOpt = (data = {}, params = []) => {
const {
energyOfMonth = 0, //电KW/H 今日用量
energyOfDay = 0, // 当月累计用量
gasOfMonth = 0, //气 (m3)
gasOfDay = 0,
waterOfDay = 0, //水T
waterOfMonth = 0,
} = data;
if (params.length > 0) {
params[0].values[0].value = gasOfMonth;
params[0].values[1].value = gasOfDay;
params[1].values[0].value = waterOfMonth;
params[1].values[1].value = waterOfDay;
params[2].values[0].value = energyOfMonth;
params[2].values[1].value = energyOfDay;
}
let opt = {
title: {
text: "",
left: "center",
top: 70,
textStyle: {
fontWeight: "normal",
fontSize: "16px"
},
},
tooltip: {
trigger: "item",
},
legend: {
bottom: 15,
left: "center",
orient: "vertical",
textStyle: {
fontSize: 14,
lineHeight: 14
},
},
series: [
{
type: "pie",
radius: ["45%", "70%"],
center: ["50%", "40%"],
avoidLabelOverlap: false,
label: {
show: false,
position: "center",
},
labelLine: {
show: false,
},
data: [],
},
],
};
let res = [];
let colors = ["#6FE621", "#FF005A", "#4D7BF3", "#4FCCFF"]
params.forEach(({name, unit, values}, index) => {
let newOpt = _.cloneDeep(opt);
newOpt.color = [colors[index], "#FFC760"];
newOpt.title.text = `${name}\n(${unit})`;
newOpt.series[0].data = values;
newOpt.legend.formatter = name => {
let {value = ""} = values.find(item => item.name === name) || {};
return `${name}: ${value}`
}
res.push(newOpt);
});
return res;
};
//设备监控参数
const getEquipMonitorOpt = (data = []) => {
let option = {
type: "equip",
backgroundColor: chartsBgColor,
grid: {
top: 25,
left: 60,
right: "5%"
},
title: {
text: "",
x: "center",
y: "top",
textStyle: {
fontSize: "14px"
}
},
xAxis: {
type: "category",
data: [],
},
yAxis: {
type: "value",
min: 0,
},
series: [
{
symbol: "circle",
symbolSize: 5,
data: [],
type: "line",
},
],
};
let newOpt = []
if (data.length > 0) {
data.forEach((item, index) => {
let opt = _.cloneDeep(option);
opt.title.text = item.name;
opt.xAxis.data = item.time.slice().reverse();
opt.series[0].data = item.data.slice().reverse();
newOpt.push(opt)
})
}
return newOpt;
}
//质量监控参数
const getQualityMonitorOpt = (data = {}) => {
let res = [];
let option = {
type: "quality",
backgroundColor: chartsBgColor,
title: {
show: false,
/* text: '',
x: 'right',
textStyle: {
color: '#ffffff',
fontSize: "14px"
}*/
},
tooltip: {
trigger: 'axis'
},
markLine: {},
legend: {
x: 'left',
y: 'bottom',
// data: ['极差值', 'UCLmr', 'LCLmr', 'CLmr'],
textStyle: { //文字 样式
color: '#ffffff',
fontSize: "12px"
},
itemStyle: { //圆点样式
opacity: 0
},
lineStyle: { //线样式
// type: [4, 4],
dashOffset: 4
}
},
grid: {
top: 15,
left: '3%',
right: '3%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [],
axisLine: {
show: true,
lineStyle: {
type: 'solid',
color: '#ffffff'
}
}
},
yAxis: {
type: 'value',
splitNumber: 8,
//轴线
axisLine: {
show: true,
lineStyle: {
type: 'solid',
color: '#ffffff'
}
},
splitLine: {
lineStyle: {
color: '#ffffff'
}
}
},
series: []
};
if (data.spcBasicDatas && data.topData) {
let {spcBasicDatas = [], topData = {}} = data;
let {
usl = 0, lsl = 0,
uclx = 0, averx = 0, lclx = 0,
uclr = 0, averr = 0, lclr = 0,
} = topData
let xData = [], yValData = [], yMrValData = [];
spcBasicDatas.forEach(item => {
if (!item.seq) item.seq = 0
if (!item.value) item.value = 0
if (!item.mrValue) item.mrValue = 0
xData.push(item.seq)
yValData.push(item.value)
yMrValData.push(item.mrValue)
})
//标准点数据处理
let basicOpt = _.cloneDeep(option);
const basicName = ['数据点', 'USLi', 'UCli', 'CLi', 'LCLi', 'LSLi'];
basicOpt.xAxis.data = xData
let min = _.min(yValData)
basicOpt.yAxis.min = min;
basicName.forEach((item, index) => {
basicOpt.series[index] = {
type: 'line',
smooth: true, //平滑
symbolSize: 4,
symbol: 'circle',
data: [],
lineStyle: {
type: 'dashed', //'dotted'点线 'dashed' 虚线 'solid' 实线
width: 1
},
markLine: {
symbol: 'none',
label: {
show: false
},
lineStyle: {},
data: [{
yAxis: 0,
x: '6.8%'
}]
}
}
basicOpt.series[index].name = basicName[index]
})
basicOpt.series[0].data = yValData;
basicOpt.series[0].lineStyle.type = "solid";
basicOpt.series[1].markLine.data[0].yAxis = usl
basicOpt.series[2].markLine.data[0].yAxis = lsl
basicOpt.series[3].markLine.data[0].yAxis = uclx
basicOpt.series[4].markLine.data[0].yAxis = averx
basicOpt.series[5].markLine.data[0].yAxis = lclx
//极差点数据处理
let mrOpt = _.cloneDeep(option);
const mrName = ['极差点', 'UCLmr', 'CLmr', 'LCLmr'];
mrOpt.xAxis.data = xData
mrName.forEach((item, index) => {
mrOpt.series[index] = {
type: 'line',
smooth: true, //平滑
symbolSize: 4,
data: [],
lineStyle: {
type: 'dashed', //'dashed'点线 'dashed' 虚线 'solid' 实线
width: 1
},
markLine: {
symbol: 'none',
label: {
show: false
},
data: [{
yAxis: 0,
x: '6.8%'
}]
}
}
mrOpt.series[index].name = mrName[index]
})
mrOpt.series[0].data = yMrValData;
mrOpt.series[0].lineStyle.type = "solid";
mrOpt.series[1].markLine.data[0].yAxis = uclr
mrOpt.series[2].markLine.data[0].yAxis = averr
mrOpt.series[3].markLine.data[0].yAxis = lclr
res = _.concat([], [basicOpt], [mrOpt])
}
return res;
}
const ProcessItem = () => {
const tableTimeoutRef = useRef();
const equipMonitorRef = useRef();
const router = useRouter();
const [isClear, setClear] = useState(false)
const [length, setLength] = useState(4) //卡片轮播个数
const [len, setLen] = useState(2) //监控轮播个数
const [height, setHeight] = useState(0);
const [width, setWidth] = useState(0);
const [monitorTitle, setMonitorTitle] = useState("设备")
const [isQuality, setIsQuality] = useState(false)
const {operationCode, workCenterCode, date = moment().format('YYYY-MM-DD')} = router.query;
const {
processItem: {
reloadTime, cardAutoNext, pages = {}, energyParams = [], OEEParams = []
} = {}
} = global.config || {};
//监控类型名,质量、设备
let title = "";
if (operationCode && pages[operationCode]) {
title = pages[operationCode].title;
}
const [
{
keyEnergy = {}, //关键能耗
keyProduction = {}, //关键生产
taskExecution = [], //任务执行状态
oee = {}, //车间OEE
paramMonitor = {} //监控
} = {},
reload,
] = useSocket({socketScreenType, operationCode, workCenterCode, date});
const {iotEquipResultData = [], spcResultData = {}} = paramMonitor; //监控
const {topData = {}} = spcResultData;
//质量监控检测项
let featureName = "";
if (topData.featureName) {
featureName = topData.featureName
}
//工序任务执行状态
const taskExecutData = useMemo(() => {
let res = [];
taskExecution.forEach((item, index) => {
let row = Math.ceil((index + 1) / length);
let rowIndex = index % length;
if (!res[row]) res[row] = [];
res[row][rowIndex] = item;
})
res = res.filter(item => item);
return res;
}, [taskExecution]);
//设备、质量参数监控
const monitorOpt = useMemo(() => {
let equipData = getEquipMonitorOpt(iotEquipResultData);
let qualityData = getQualityMonitorOpt(spcResultData);
let res = [], equipRes = [], qualityRes = [];
//设备参数分组
equipData.forEach((item, index) => {
let row = Math.ceil((index + 1) / len);
let rowIndex = index % len;
if (!equipRes[row]) equipRes[row] = [];
equipRes[row][rowIndex] = item;
})
//质量参数分组
qualityData.forEach((item, index) => {
let row = Math.ceil((index + 1) / len);
let rowIndex = index % len;
if (!qualityRes[row]) qualityRes[row] = [];
qualityRes[row][rowIndex] = item;
})
res = _.concat([], equipRes, qualityRes)
res = res.filter(item => item);
return res
}, [iotEquipResultData, spcResultData])
//关键生产信息
const prodOpt = useMemo(() => {
return getProdOpt(keyProduction)
}, [keyProduction])
//车间能耗信息
const usedOpt = useMemo(() => {
return getUsedOpt(keyEnergy, energyParams);
}, [keyEnergy])
//车间OEE
const OEEOpt = useMemo(() => {
return getOEEOpt(oee, OEEParams);
}, [oee]);
useEffect(() => {
if (operationCode) {
setClear(true);
}
}, [operationCode]);
useEffect(() => {
setTimeout(() => {
//轮播图标的宽高设置
if (equipMonitorRef.current) {
setHeight(equipMonitorRef.current.clientHeight)
setWidth(equipMonitorRef.current.clientWidth)
}
}, 100)
}, [])
//整体刷新
useEffect(() => {
tableTimeoutRef.current = setInterval(() => {
reload()
}, reloadTime);
return () => {
clearInterval(tableTimeoutRef.current)
}
}, [reload])
useEffect(() => {
if (iotEquipResultData.length <= 0 && _.get(spcResultData, "spcBasicDatas.length") > 0) {
setMonitorTitle('质量')
setIsQuality(true)
}
}, [iotEquipResultData, spcResultData])
//当前轮播位置,更新标题
const afterChange = (current) => {
let index = _.findIndex(monitorOpt, item => item[0].type == 'quality');
if (index == current) {
setMonitorTitle('质量')
setIsQuality(true)
} else {
setMonitorTitle('设备')
setIsQuality(false)
}
}
return (
<div className="screen-container">
{isClear && (
<React.Fragment>
<Header title={`${title}工序`}/>
<div className="screen-container-content">
<Row className='height-100' gutter={gutter} bottom={bottom}>
<Row.Col span={5}>
<Row className="height-55">
<Card title={`${title}工序设备示意图`} overVisible={true}
padding={"40px 20px 25px 20px"}>
<div className='screen-img img-gxsb'/>
</Card>
</Row>
<Row className="height-45" style={{paddingTop: `${gutter}px`}}>
<Card title={`${title}工序成品示意图`} overVisible={true}
padding={"40px 20px 25px 20px"}>
<div className='screen-img img-gxzzp'/>
</Card>
</Row>
</Row.Col>
<Row.Col span={12}>
<Row className="height-55">
<Card title="工序任务执行状态" overVisible={true} padding={"40px 20px 25px 20px"}>
<div className='process-task-list'>
<Carousel autoplay dots={false} autoplaySpeed={cardAutoNext}>
{taskExecutData.map((row, rowIndex) => {
return <div key={Math.random()}>
<div className='process-task'>
{row.map((item, index) => {
return <div className='process-task-item'
key={item.code}>
<ProcessTaskCard itemData={item}
operationCode={operationCode}/>
</div>
})}
</div>
</div>
})}
</Carousel>
</div>
</Card>
</Row>
<Row className="height-45" style={{paddingTop: `${gutter}px`}}>
<Card title={`${monitorTitle}参数监控`} overVisible={true}
padding={"35px 16px 16px 16px"}>
{
isQuality &&
<div className='monitor-quality-item'>
{`最新检测项:${featureName}`}
</div>
}
<div className='equip-monitor-list' ref={equipMonitorRef}>
<Row className='height-100'>
<Carousel autoplay dots={false} dotPosition={"right"}
autoplaySpeed={cardAutoNext} adaptiveHeight={true}
afterChange={afterChange}>
{monitorOpt.map((row, rowIndex) => {
return <div>
<div key={Math.random()}
style={{height: height, width: width}}>
{row.map((item, index) => {
return <div className='equip-monitor-item'
key={rowIndex + index}>
<Chart option={item}/>
</div>
})}
</div>
</div>
})}
</Carousel>
</Row>
</div>
</Card>
</Row>
</Row.Col>
<Row.Col span={7}>
<Card>
<div className="used-content">
<Row className="height-30">
<Card title="关键生产信息" titleSpace={true} overVisible={true}
withBorder={false}>
<Chart option={prodOpt}/>
</Card>
</Row>
<Row className="height-30">
<Card title="关键能耗信息" titleSpace={true} overVisible={true}
withBorder={false} padding={"0 16px"}>
<Row className='height-100'>
{usedOpt.map((opt, index) => {
return (
<Row.Col span={8} key={index}>
<Chart option={opt}/>
</Row.Col>
);
})}
</Row>
</Card>
</Row>
<Row className="height-40">
<Card title="车间OEE" titleSpace={true} overVisible={true}
withBorder={false} padding={"0 16px"}>
<Row className='height-100'>
{OEEOpt.map((opt, index) => {
return (
<Row.Col span={12} key={index}>
<Chart option={opt}/>
</Row.Col>
);
})}
</Row>
</Card>
</Row>
</div>
</Card>
</Row.Col>
</Row>
</div>
<Footer/>
</React.Fragment>
)
}
<style jsx>{`
.screen-container {
font-family: PingFangSC-Semibold;
height: 100vh;
min-height: 1080px;
min-width: 1920px;
background: url("/img/bg.png") center center;
position: relative;
padding-top: 96px;
padding-bottom: 82px;
overflow: hidden;
}
.screen-container-content {
height: 100%;
padding: ${gutter}px;
}
.OEE-content {
position: absolute;
width: 900px;
height: 320px;
top: 660px;
left: 460px;
}
.monitor-quality-item {
text-align: right;
margin-top: -22px;
font-weight: bold;
}
.equip-monitor-list {
width: 100%;
height: 100%;
overflow: hidden;
}
.equip-monitor-item {
width: 100%;
height: 50%;;
padding: 4px 0;
}
.process-task-list {
width: 100%;
height: 100%;
overflow: hidden;
}
.process-task {
display: flex;
justify-content: space-around;
}
.process-task-item {
width: calc(100% / 4);
padding: 0 16px;
}
.used-content {
overflow: hidden;
height: 100%;
}
.used-content :global(.screen-card) {
border: none !important;
}
.aaa {
width: 50%;
display: flex;
justify-content: center;
}
.screen-img {
height: 100%;
}
.img-gxsb {
background: url(/img/process/${operationCode}-SB.png) center center no-repeat;
}
.img-gxzzp {
background: url(/img/process/${operationCode}-ZZP.png) center center no-repeat;
}
`}</style>
</div>
);
};
export default ProcessItem;