417 lines
14 KiB
JavaScript
417 lines
14 KiB
JavaScript
import {useRouter} from "next/router";
|
|
import React, {useMemo, useState, useRef, useEffect} from "react";
|
|
import _ from "lodash";
|
|
import moment from 'moment';
|
|
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 Table from "../../../components/screen/Table";
|
|
import ProcessCard from "../../components/ProcessCard";
|
|
import {useSocket} from "../../../utils/hooks";
|
|
|
|
/**
|
|
* 生产工序总图
|
|
*/
|
|
const socketScreenType = 'AllOperationType';
|
|
const gutter = 20;
|
|
const bottom = 0;
|
|
|
|
//关键生产信息
|
|
const getProdOpt = (workCenter, data) => {
|
|
let {xData = [], yData = []} = data;
|
|
let opt = {
|
|
xAxis: {
|
|
type: 'category',
|
|
data: []
|
|
},
|
|
title: {
|
|
text: `${workCenter}日生产进度`,
|
|
textStyle: {
|
|
fontSize: "14px"
|
|
},
|
|
top: 15,
|
|
left: 60
|
|
},
|
|
yAxis: {
|
|
type: 'value'
|
|
},
|
|
grid: {
|
|
top: 50,
|
|
left: 55
|
|
},
|
|
series: [
|
|
{
|
|
data: yData,
|
|
type: 'bar'
|
|
}
|
|
]
|
|
};
|
|
let newXData = []
|
|
xData.forEach(item => {
|
|
let date = (new Date(item).getMonth() + 1) + "." + new Date(item).getDate();
|
|
newXData.push(date)
|
|
})
|
|
opt.xAxis.data = newXData;
|
|
return opt;
|
|
};
|
|
|
|
//关键能耗信息
|
|
const getUsedOpt = (data = {}, params = []) => {
|
|
let {
|
|
energyOfDay = 0, //电
|
|
energyOfMonth = 0,
|
|
gasOfDay = 0, //气
|
|
gasOfMonth = 0,
|
|
waterOfDay = 0,//水
|
|
waterOfMonth = 0,
|
|
} = data;
|
|
if (params.length > 0) {
|
|
params[0].values[0].value = energyOfMonth;
|
|
params[0].values[1].value = energyOfDay;
|
|
params[1].values[0].value = waterOfMonth;
|
|
params[1].values[1].value = waterOfDay;
|
|
params[2].values[0].value = gasOfMonth;
|
|
params[2].values[1].value = gasOfDay;
|
|
}
|
|
|
|
let opt = {
|
|
title: {
|
|
text: "",
|
|
left: "center",
|
|
top: 70,
|
|
textStyle: {
|
|
fontWeight: "normal",
|
|
fontSize: "16px"
|
|
},
|
|
},
|
|
tooltip: {
|
|
trigger: "item",
|
|
},
|
|
legend: {
|
|
bottom: 10,
|
|
left: "center",
|
|
orient: "vertical",
|
|
textStyle: {
|
|
fontSize: 13,
|
|
lineHeight: 16
|
|
},
|
|
},
|
|
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 prodTableColumns = [
|
|
{title: "任务编码", code: "code"},
|
|
{title: "维保类型", code: "type"},
|
|
{title: "设备名称", code: "equipSerialName"},
|
|
{title: "任务状态", code: "status"},
|
|
]
|
|
|
|
const ProcessBoard = () => {
|
|
const [isClear, setClear] = useState(true)
|
|
const router = useRouter();
|
|
const productTableRef = useRef();
|
|
const tableTimeoutRef = useRef();
|
|
const spacing = [30, 50]; //工序流程图间距
|
|
const {
|
|
processBoard: {
|
|
workCenter,
|
|
taskTableNext,
|
|
reloadTime,
|
|
pages = {},
|
|
energyParams = []
|
|
} = {}
|
|
} = global.config || {};
|
|
const {workCenterCode, date = moment().format('YYYY-MM-DD')} = router.query;
|
|
const title = pages['title'];
|
|
|
|
const [{productionRecords = {}, maintenance = [], coreProcess = [], energy = {}} = {},
|
|
reload,
|
|
] = useSocket({socketScreenType, workCenterCode, date});
|
|
|
|
//核心工序数据分组
|
|
const [ruleLength, setRuleLength] = useState(6); //一行摆放多少个
|
|
const memoData = useMemo(() => {
|
|
let res = [];
|
|
if (coreProcess.length > 0) {
|
|
let newCoreProcess = _.cloneDeep(coreProcess) || [];
|
|
let ccIndex = _.findIndex(coreProcess, item => item.code == "L7-CC"); //除磁->双层除磁
|
|
newCoreProcess[ccIndex].code = 'L7-SCCC'
|
|
newCoreProcess[ccIndex].name = '双层除磁'
|
|
let xmIndex = _.findIndex(coreProcess, item => item.code == "L7-XM"); //细磨
|
|
newCoreProcess.splice(xmIndex + 1, 0, coreProcess[ccIndex]) //细磨后面添加除磁
|
|
|
|
newCoreProcess.forEach((item, index) => {
|
|
let row = Math.ceil((index + 1) / ruleLength);
|
|
let rowIndex = index % ruleLength;
|
|
if (!res[row]) res[row] = [];
|
|
res[row][rowIndex] = item;
|
|
})
|
|
}
|
|
return res;
|
|
}, [coreProcess, ruleLength]);
|
|
console.log("memoData", memoData)
|
|
|
|
//关键生产信息
|
|
const prodData = useMemo(() => {
|
|
return getProdOpt(workCenter, productionRecords);
|
|
}, [productionRecords]);
|
|
|
|
//关键能耗信息
|
|
const usedInfo = useMemo(() => {
|
|
return getUsedOpt(energy, energyParams);
|
|
}, [energy])
|
|
|
|
//表格定时刷新
|
|
useEffect(() => {
|
|
tableTimeoutRef.current = setInterval(() => {
|
|
productTableRef.current && productTableRef.current.nextPage();
|
|
}, taskTableNext);
|
|
return () => {
|
|
clearInterval(tableTimeoutRef.current)
|
|
}
|
|
}, [])
|
|
|
|
//整体刷新
|
|
useEffect(() => {
|
|
tableTimeoutRef.current = setInterval(() => {
|
|
reload()
|
|
}, reloadTime);
|
|
return () => {
|
|
clearInterval(tableTimeoutRef.current)
|
|
}
|
|
}, [reload])
|
|
|
|
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={17}>
|
|
<Card title="核心工序流程图" overVisible={true}>
|
|
<div className='process-flow-list'>
|
|
<div className="list-flex">
|
|
{memoData.map((row, rowIndex) => {
|
|
let reverse = rowIndex % 2 > 0;
|
|
return (
|
|
<div className={`list-flex-row ${reverse ? '' : 'reverse'}`}
|
|
key={rowIndex}>
|
|
{row.map(item => {
|
|
return (
|
|
<div
|
|
className={"list-flex-item"}
|
|
key={item['code']}>
|
|
<ProcessCard itemData={item}/>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</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={prodData}/>
|
|
</Card>
|
|
</Row>
|
|
|
|
<Row className="height-30">
|
|
<Card title="关键能耗信息" titleSpace={true} overVisible={true}
|
|
withBorder={false} padding={"0 16px"}>
|
|
<Row className='height-100'>
|
|
{usedInfo.map((opt, index) => {
|
|
return (
|
|
<Row.Col span={8} key={index}>
|
|
<Chart option={opt}/>
|
|
</Row.Col>
|
|
);
|
|
})}
|
|
</Row>
|
|
</Card>
|
|
</Row>
|
|
|
|
<Row className="height-40">
|
|
<Card title="车间维保计划" titleSpace={true} overVisible={true}
|
|
withBorder={false} padding={"16px"}>
|
|
<Table
|
|
ref={productTableRef}
|
|
mainKey="taskCode"
|
|
columns={prodTableColumns}
|
|
dataSource={maintenance}
|
|
/>
|
|
</Card>
|
|
</Row>
|
|
</div>
|
|
</Card>
|
|
</Row.Col>
|
|
</Row>
|
|
</div>
|
|
|
|
<Footer/>
|
|
</React.Fragment>
|
|
)}
|
|
<style jsx>{`
|
|
.screen-container {
|
|
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;
|
|
}
|
|
|
|
.process-flow-list {
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 50px 30px 30px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.list-flex {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.list-flex-row {
|
|
display: flex;
|
|
margin: 0 -${spacing[0] / 2}px;
|
|
}
|
|
|
|
.list-flex-row.reverse {
|
|
flex-direction: row-reverse;
|
|
}
|
|
|
|
.list-flex-item {
|
|
width: ${100 / ruleLength}%;
|
|
padding: 0 ${spacing[0] / 2}px;
|
|
margin-bottom: ${spacing[1]}px;
|
|
position: relative;
|
|
}
|
|
|
|
.list-flex-row:last-child .list-flex-item {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.list-flex-item::after {
|
|
content: '';
|
|
display: block;
|
|
position: absolute;
|
|
}
|
|
|
|
// 右箭头
|
|
.list-flex-row:not(.reverse) .list-flex-item::after {
|
|
width: 42px;
|
|
height: 42px;
|
|
right: 0;
|
|
left: unset;
|
|
bottom: unset;
|
|
top: 50%;
|
|
transform: translate(50%, -50%);
|
|
background: url('/img/arrow.png') center center no-repeat;
|
|
z-index: 9;
|
|
}
|
|
|
|
.reverse {
|
|
justify-content: space-between;
|
|
}
|
|
|
|
// 左箭头
|
|
.list-flex-row.reverse .list-flex-item::after {
|
|
width: 42px;
|
|
height: 42px;
|
|
left: -${spacing[1] / 2}px;
|
|
right: unset;
|
|
top: 50%;
|
|
bottom: unset;
|
|
transform: translate(-50%, -50%) rotate(180deg);
|
|
transform-origin: 50% 50%;
|
|
background: url('/img/arrow.png') center center no-repeat;
|
|
z-index: 9;
|
|
}
|
|
|
|
// 下箭头
|
|
.list-flex-row .list-flex-item:last-child::after {
|
|
width: 42px;
|
|
height: 42px;
|
|
left: unset;
|
|
right: calc(50% - 44px);
|
|
top: unset;
|
|
bottom: -${(spacing[1] - 44) / 2}px;
|
|
transform: translate(-50%, 100%) rotate(90deg);
|
|
transform-origin: 50% 50%;
|
|
background: url('/img/arrow.png') center center no-repeat;
|
|
z-index: 9;
|
|
}
|
|
|
|
// 无箭头
|
|
.list-flex-row:last-child .list-flex-item:last-child::after {
|
|
display: none;
|
|
}
|
|
|
|
.used-content {
|
|
overflow: hidden;
|
|
height: 100%;
|
|
}
|
|
|
|
.used-content :global(.screen-card) {
|
|
border: none !important;
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ProcessBoard;
|