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

810 lines
27 KiB
JavaScript
Raw Permalink Normal View History

2024-06-20 11:26:44 +08:00
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;