668 lines
18 KiB
JavaScript
668 lines
18 KiB
JavaScript
import React, { useRef, useEffect, useCallback } from 'react';
|
||
import _ from 'lodash';
|
||
import { withRouter } from 'next/router';
|
||
import Footer from '../../../components/screen/Footer';
|
||
import Header from '../../../components/screen/Header';
|
||
import Row from '../../../components/screen/Row';
|
||
import Card from '../../../components/screen/Card';
|
||
import Chart from '../../../components/screen/Chart';
|
||
import Table from '../../../components/screen/Table';
|
||
import TableProgress from '../../../components/screen/TableProgress';
|
||
import io from '../../../utils/socket.io.js';
|
||
|
||
|
||
const gutter = 20;
|
||
const bottom = 20;
|
||
|
||
// 返回结果是个字符串,需要转一下
|
||
let testData = {
|
||
"code": "0000",
|
||
"data": {
|
||
//今日生产总数
|
||
"productCount": {
|
||
"planQty": 3162.000000, //计划总数
|
||
"productQty": 199.000000, //生产总数
|
||
"unFinishedQty": 2963.000000 //未完成数量
|
||
},
|
||
//今日生产统计
|
||
"productDayCountList": [
|
||
{
|
||
"productQty": 0.000000, //生产数量
|
||
"qualityQty": 0.000000, //合格数量
|
||
"time": "00:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "01:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "02:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "03:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "04:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "05:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "06:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "07:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "08:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "09:00"
|
||
},
|
||
{
|
||
"productQty": 69.000000,
|
||
"qualityQty": 17.000000,
|
||
"time": "10:00"
|
||
},
|
||
{
|
||
"productQty": 12.000000,
|
||
"qualityQty": 12.000000,
|
||
"time": "11:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "12:00"
|
||
},
|
||
{
|
||
"productQty": 15.000000,
|
||
"qualityQty": 15.000000,
|
||
"time": "13:00"
|
||
},
|
||
{
|
||
"productQty": 25.000000,
|
||
"qualityQty": 25.000000,
|
||
"time": "14:00"
|
||
},
|
||
{
|
||
"productQty": 26.000000,
|
||
"qualityQty": 26.000000,
|
||
"time": "15:00"
|
||
},
|
||
{
|
||
"productQty": 52.000000,
|
||
"qualityQty": 51.000000,
|
||
"time": "16:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "17:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "18:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "19:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "20:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "21:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "22:00"
|
||
},
|
||
{
|
||
"productQty": 0.000000,
|
||
"qualityQty": 0.000000,
|
||
"time": "23:00"
|
||
}
|
||
],
|
||
//日产能概况
|
||
"productDayRecordList": [
|
||
{
|
||
"code": "WO202203300001", //工单号
|
||
"materialCode": "L5CP", //产品编码
|
||
"materialName": "L5车间成品", //产品名称
|
||
"planQty": 1000.000000, //计划数量
|
||
"productQty": 10.000000, //生产数量
|
||
"productRate": 0.010, //生产进度
|
||
"productStatus": "已完工", //生产状态
|
||
"unQualityQty": 0.000000, //不合格数量
|
||
"unQualityRate": 0.000, //不合格率
|
||
"workCenterCode": "L5", //产线编码
|
||
"workCenterName": "L5车间" //产线名称
|
||
},
|
||
{
|
||
"code": "WO202204010001",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 100.000000,
|
||
"productQty": 15.000000,
|
||
"productRate": 0.150,
|
||
"productStatus": "已创建",
|
||
"unQualityQty": 0.000000,
|
||
"unQualityRate": 0.000,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "WO202204010002",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 10.000000,
|
||
"productQty": 15.000000,
|
||
"productRate": 1.500,
|
||
"productStatus": "生产中",
|
||
"unQualityQty": 0.000000,
|
||
"unQualityRate": 0.000,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "WO202204010003",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 10.000000,
|
||
"productQty": 12.000000,
|
||
"productRate": 1.200,
|
||
"productStatus": "生产中",
|
||
"unQualityQty": 0.000000,
|
||
"unQualityRate": 0.000,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "WO202204060001",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 1000.000000,
|
||
"productQty": 14.000000,
|
||
"productRate": 0.014,
|
||
"productStatus": "生产中",
|
||
"unQualityQty": 0.000000,
|
||
"unQualityRate": 0.000,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "WO202204060002",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 10.000000,
|
||
"productQty": 12.000000,
|
||
"productRate": 1.200,
|
||
"productStatus": "生产中",
|
||
"unQualityQty": 0.000000,
|
||
"unQualityRate": 0.000,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "WO202204060003",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 10.000000,
|
||
"productQty": 14.000000,
|
||
"productRate": 1.400,
|
||
"productStatus": "生产中",
|
||
"unQualityQty": 0.000000,
|
||
"unQualityRate": 0.000,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "WO202204070001",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 10.000000,
|
||
"productQty": 52.000000,
|
||
"productRate": 5.200,
|
||
"productStatus": "生产中",
|
||
"unQualityQty": 1.000000,
|
||
"unQualityRate": 0.019,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "WO202204080001",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 12.000000,
|
||
"productQty": 55.000000,
|
||
"productRate": 4.583,
|
||
"productStatus": "生产中",
|
||
"unQualityQty": 52.000000,
|
||
"unQualityRate": 0.945,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
},
|
||
{
|
||
"code": "RcQuickWearStr",
|
||
"materialCode": "L5CP",
|
||
"materialName": "L5车间成品",
|
||
"planQty": 1000.000000,
|
||
"productQty": 0.000000,
|
||
"productRate": 0.000,
|
||
"productStatus": "待生产",
|
||
"unQualityQty": 0.000000,
|
||
"unQualityRate": 0.000,
|
||
"workCenterCode": "L5",
|
||
"workCenterName": "L5车间"
|
||
}
|
||
],
|
||
// 今日生产合格数
|
||
"qualityDayCount": {
|
||
"qualityQty": 146.000000, //合格数量
|
||
"qualityRate": 0.734, //合格率
|
||
"unQualityQty": 53.000000 //不合格数量
|
||
}
|
||
},
|
||
"success": true
|
||
};
|
||
|
||
const polarBarOpt = {
|
||
title: {
|
||
text: '计划总数',
|
||
left: 'center',
|
||
bottom: 24,
|
||
textStyle: {
|
||
fontSize: 20,
|
||
}
|
||
},
|
||
legend: {
|
||
show: false
|
||
},
|
||
radiusAxis: {
|
||
show: false,
|
||
type: 'category',
|
||
data: ['%']
|
||
},
|
||
tooltip: {
|
||
show: false,
|
||
},
|
||
series: {
|
||
type: 'gauge',
|
||
center: ['50%', "40%"],
|
||
radius: "70%",
|
||
startAngle: 90,
|
||
endAngle: -270,
|
||
pointer: {
|
||
show: false
|
||
},
|
||
progress: {
|
||
show: true,
|
||
overlap: false,
|
||
roundCap: false,
|
||
clip: false,
|
||
itemStyle: {
|
||
borderWidth: 0,
|
||
},
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
width: 20,
|
||
color: [[1, 'rgba(40, 163, 255, 0.2)']],
|
||
}
|
||
},
|
||
splitLine: {
|
||
show: false,
|
||
},
|
||
axisTick: {
|
||
show: false
|
||
},
|
||
axisLabel: {
|
||
show: false,
|
||
},
|
||
data: [{
|
||
value: 0,
|
||
name: '生产总数'
|
||
}],
|
||
title: {
|
||
offsetCenter: ['0%', '20'],
|
||
color: "#fff",
|
||
fontSize: 20,
|
||
},
|
||
detail: {
|
||
show: true,
|
||
offsetCenter: ['0%', '-20'],
|
||
width: 'auto',
|
||
height: 32,
|
||
fontSize: 32,
|
||
color: '#00E1FA',
|
||
borderColor: 'auto',
|
||
borderWidth: 0,
|
||
formatter: '{value}'
|
||
}
|
||
}
|
||
};
|
||
const setNumOpt = (total, done, title) => {
|
||
let res = _.cloneDeep(polarBarOpt);
|
||
if(title) res.title.text = `${title}:${total}`;
|
||
let percent = (100 * done / total).toFixed(2);
|
||
res.series.data[0].name = "生产总数";
|
||
res.series.data[0].value = percent;
|
||
res.series.detail.formatter = () => done;
|
||
return res;
|
||
}
|
||
const setOkNumOpt = (total, done, title) => {
|
||
let res = _.cloneDeep(polarBarOpt);
|
||
let percent = (100 * done / (total === 0 ? 1 : total)).toFixed(2);
|
||
if(isNaN(percent)) percent = 0;
|
||
if(title) res.title.text = `${title}:${percent}%`;
|
||
res.series.data[0].value = percent;
|
||
res.series.data[0].name = "合格数量";
|
||
res.series.detail.formatter = () => done;
|
||
res.series.detail.color = "#6FE621";
|
||
res.series.progress.itemStyle.color = "#6FE621";
|
||
return res;
|
||
}
|
||
|
||
const barOpt = {
|
||
legend: {
|
||
show: true,
|
||
data: ['生产数量', '合格数量'],
|
||
top: 10,
|
||
right: 10,
|
||
textStyle: {
|
||
fontSize: 14,
|
||
},
|
||
},
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
}
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
axisTick: {
|
||
lineStyle: {opacity: 0.3},
|
||
alignWithLabel: true,
|
||
},
|
||
axisLine: {
|
||
lineStyle: {opacity: 0.3},
|
||
},
|
||
axisLabel: {
|
||
color: 'rgba(255, 255, 255, 0.8)'
|
||
},
|
||
data: []
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
axisTick: {
|
||
lineStyle: {opacity: 0.3}
|
||
},
|
||
axisLine: {
|
||
lineStyle: {opacity: 0.3}
|
||
},
|
||
axisLabel: {
|
||
color: 'rgba(255, 255, 255, 0.8)'
|
||
},
|
||
},
|
||
grid: {
|
||
bottom: 40,
|
||
top: 60,
|
||
left: 50,
|
||
},
|
||
barWidth: 20,
|
||
series: [{
|
||
name: '生产数量',
|
||
type: 'bar',
|
||
barGap: -1,
|
||
data: []
|
||
},
|
||
{
|
||
name: '合格数量',
|
||
itemStyle: {
|
||
color: "#6FE621",
|
||
},
|
||
type: 'bar',
|
||
data: []
|
||
}]
|
||
}
|
||
const setBarOpt = (data) => {
|
||
let res = _.cloneDeep(barOpt);
|
||
data.forEach(({productQty, qualityQty, time}, index) => {
|
||
res.xAxis.data.push(time);
|
||
res.series[0].data[index] = productQty;
|
||
res.series[1].data[index] = qualityQty;
|
||
})
|
||
return res;
|
||
};
|
||
|
||
const productTableColumns = [
|
||
{title: "机台", code: "workCenterName"},
|
||
{title: "生产状态", code: "productStatus",
|
||
color: val => {
|
||
if(val === '待生产') return "#d5542d";
|
||
if(val === '已创建') return "#1a7cc6";
|
||
if(val === '生产中') return "#0aac61";
|
||
if(val === '已完工') return "#0db5a4";
|
||
if(val === '已关闭') return "#c1c1c1";
|
||
if(val === '已取消') return "#c1c1c1";
|
||
},
|
||
render: (text, config, rowData) => {
|
||
const {color} = config;
|
||
let resColor = color(text);
|
||
return (
|
||
<div style={{color: resColor}}>
|
||
<span style={{
|
||
background: resColor,
|
||
width: '8px',
|
||
height: '8px',
|
||
display: 'inline-block',
|
||
borderRadius: '50%',
|
||
verticalAlign: 'middle',
|
||
}}></span>
|
||
<span style={{
|
||
verticalAlign: 'middle',
|
||
marginLeft: '6px',
|
||
}}>{text}</span>
|
||
</div>
|
||
)
|
||
},
|
||
},
|
||
{title: "产品编码", code: "materialCode"},
|
||
{title: "产品名称", code: "materialName"},
|
||
{title: "工单编号", code: "code"},
|
||
{title: "生产数量", code: "productQty"},
|
||
{title: "计划数量", code: "planQty"},
|
||
{title: "生产进度", code: "productRate",
|
||
width: 200,
|
||
render: (value, config, rowData) => {
|
||
return <TableProgress percent={value}/>
|
||
},
|
||
},
|
||
{title: "不良品数量", code: "unQualityQty"},
|
||
{title: "不良品率", code: "unQualityRate",
|
||
render: (value, config, rowData) => {
|
||
return <span>{value * 100}%</span>
|
||
},
|
||
},
|
||
]
|
||
|
||
function PlanBoard(props) {
|
||
const {config : {ioSocketUrl, planBoard = {}} = {}} = global;
|
||
const tableToNextPage = planBoard.productTableNext;
|
||
|
||
const {router} = props;
|
||
|
||
const planNumRef = useRef();
|
||
const okNumRef = useRef();
|
||
const productNumRef = useRef();
|
||
const productTableRef = useRef();
|
||
|
||
const socket = useRef();
|
||
const workCenterCodeRef = useRef();
|
||
const tableTimeoutRef = useRef();
|
||
const getQueryVariable = (variable) => {
|
||
let query = window.location.search.substring(1);
|
||
let vars = query.split("&");
|
||
for (let i = 0; i < vars.length; i++) {
|
||
let pair = vars[i].split("=");
|
||
if (pair[0] == variable) { return pair[1]; }
|
||
}
|
||
return (false);
|
||
}
|
||
const getData = () => {
|
||
let workCenterCode = workCenterCodeRef.current
|
||
let date = getQueryVariable('date');
|
||
|
||
console.log("fetch", workCenterCode)
|
||
if(!workCenterCode) return;
|
||
let query = {
|
||
workCenterCode,
|
||
screenType: "ProductPlanType",
|
||
}
|
||
if (date) query.date = date;
|
||
else{
|
||
query.date = new Date().toJSON().split('T').join(' ').substr(0,10)
|
||
}
|
||
socket.current.emit('timePerformanceGoodProduct', 'channel', {query})
|
||
setTimeGetData()
|
||
};
|
||
const setTimeGetData = () => {
|
||
setTimeout(() => {
|
||
getData()
|
||
}, 300000)
|
||
}
|
||
const pageFinished = useCallback(() => {
|
||
console.log("finished")
|
||
clearInterval(tableTimeoutRef.current);
|
||
getData();
|
||
})
|
||
const setTableNextPage = useCallback(() => {
|
||
tableTimeoutRef.current = setInterval(() => {
|
||
productTableRef.current.nextPage();
|
||
}, tableToNextPage);
|
||
}, [])
|
||
|
||
const update = (data) => {
|
||
try{ data = JSON.parse(data) }catch(e){console.log(e)}
|
||
// if(!data) data = testData;
|
||
const {productCount, productDayCountList, qualityDayCount, productDayRecordList} = data.data;
|
||
console.log("data", data.data)
|
||
|
||
planNumRef.current.setOption(setNumOpt(
|
||
productCount.planQty,
|
||
productCount.productQty,
|
||
"计划总数"
|
||
));
|
||
okNumRef.current.setOption(setOkNumOpt(
|
||
qualityDayCount.qualityQty,
|
||
qualityDayCount.qualityQty - qualityDayCount.unQualityQty,
|
||
"合格率"
|
||
));
|
||
|
||
productNumRef.current.setOption(setBarOpt(productDayCountList));
|
||
|
||
productTableRef.current.setData(productDayRecordList);
|
||
setTableNextPage();
|
||
};
|
||
|
||
useEffect(() => {
|
||
const {workCenterCode} = router.query;
|
||
if(!workCenterCode) return;
|
||
workCenterCodeRef.current = workCenterCode;
|
||
}, [router])
|
||
|
||
useEffect(() => {
|
||
socket.current = io.connect(ioSocketUrl, {transports:['websocket']});
|
||
|
||
socket.current.on('connect', function() {
|
||
console.log("msg页面连接成功!");
|
||
getData();
|
||
});
|
||
|
||
socket.current.on('timePerformanceGoodProduct', function(data) {
|
||
update(data)
|
||
});
|
||
|
||
// update();
|
||
}, [])
|
||
|
||
return (
|
||
<div className='screen-container'>
|
||
<Header title={"计划看板"} />
|
||
<div className="screen-container-content">
|
||
<Row className='height-45' gutter={gutter} bottom={bottom}>
|
||
<Row.Col span={5}>
|
||
<Card title="今日生产总数" titleSpace={true} overVisible={true}>
|
||
<Chart ref={planNumRef} />
|
||
</Card>
|
||
</Row.Col>
|
||
<Row.Col span={14}>
|
||
<Card title="今日生产统计" overVisible={true}>
|
||
<Chart ref={productNumRef} />
|
||
</Card>
|
||
</Row.Col>
|
||
<Row.Col span={5}>
|
||
<Card title="今日生产合格总数" titleSpace={true} overVisible={true}>
|
||
<Chart ref={okNumRef} />
|
||
</Card>
|
||
</Row.Col>
|
||
</Row>
|
||
<Row className='height-55' gutter={gutter}>
|
||
<Row.Col span={24}>
|
||
<Card title="生产概况" titleSpace={true} padding={`15px ${gutter}px`}>
|
||
<Table
|
||
ref={productTableRef}
|
||
mainKey="code"
|
||
columns={productTableColumns}
|
||
autoNext={false}
|
||
pageFinished={pageFinished}
|
||
/>
|
||
</Card>
|
||
</Row.Col>
|
||
</Row>
|
||
</div>
|
||
<Footer />
|
||
<style jsx>{`
|
||
.screen-container{
|
||
height: 100vh;
|
||
overflow: auto;
|
||
background: url('/img/bg.png') center center;
|
||
position: relative;
|
||
padding-top: 96px;
|
||
padding-bottom: 82px;
|
||
}
|
||
.screen-container-content{
|
||
height: 100%;
|
||
padding: 0 ${gutter}px;
|
||
}
|
||
`}</style>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default withRouter(PlanBoard) |