1088 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
		
		
			
		
	
	
			1088 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
|  | import React, { | |||
|  |   useRef, | |||
|  |   useEffect, | |||
|  |   useState, | |||
|  |   useCallback, | |||
|  |   useMemo, | |||
|  | } from "react"; | |||
|  | import { withRouter } from "next/router"; | |||
|  | import moment from "moment"; | |||
|  | // import { Liquid, DualAxes } from "@ant-design/plots";
 | |||
|  | import _ from "lodash"; | |||
|  | import Chart from "../../../components/screen/Chart"; | |||
|  | import * as echarts from "echarts"; | |||
|  | 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 Table from "../../../components/screen/Table"; | |||
|  | import TableProgress from "../../../components/screen/TableProgress"; | |||
|  | import io from "../../../utils/socket.io.js"; | |||
|  | 
 | |||
|  | const gutter = 20; | |||
|  | const bottom = 20; | |||
|  | const cardBg = "rgba(6, 36, 109, 0.2)"; | |||
|  | const socketEmit = "timePerformanceGoodProduct"; | |||
|  | const socketScreenType = "QualityDashBoardType"; | |||
|  | // 调试方式: local 本地完整数据调试, server 接口调试, dev 开发期调试
 | |||
|  | const dataType = "server"; | |||
|  | 
 | |||
|  | 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 useSocket = ({ socketEmit, socketScreenType, dataType }) => { | |||
|  |   const { config: { ioSocketUrl } = {} } = global; | |||
|  | 
 | |||
|  |   const [pageData, setPageData] = useState({}); | |||
|  |   const socket = useRef(); | |||
|  | 
 | |||
|  |   const update = useCallback((data) => { | |||
|  |     if (dataType === "local") data = localData; | |||
|  | 
 | |||
|  |     console.log("data", data); | |||
|  |     const { todayQuality } = data; | |||
|  |     // 今日合格率
 | |||
|  |     let todayPassRate = todayQuality.qualityRate; | |||
|  |     // 今日是否合格
 | |||
|  |     let todayIsPass = todayPassRate >= 0.7; | |||
|  |     // 本月不良率TOP5
 | |||
|  |     const { monthDefectRateTop5 = {} } = data; | |||
|  |     let monthUnPass = []; | |||
|  |     Object.keys(monthDefectRateTop5).forEach((name) => { | |||
|  |       monthUnPass.push({ name, value: monthDefectRateTop5[name] }); | |||
|  |     }); | |||
|  |     // 合格率趋势
 | |||
|  |     const { qualityTrend = [] } = data; | |||
|  |     let passTrend = { category: [], data: [] }; | |||
|  |     try { | |||
|  |       let category = []; | |||
|  |       let proData = [[], []]; | |||
|  |       qualityTrend.forEach((item, dateIndex) => { | |||
|  |         category.unshift(item.date); | |||
|  |         ["zhhgl", "ztl"].forEach((type, typeIndex) => { | |||
|  |           Object.keys(item[type]).forEach((proName) => { | |||
|  |             let value = item[type][proName]; | |||
|  |             if (!proData[typeIndex].find((i) => i.name === proName)) { | |||
|  |               proData[typeIndex].push({ name: proName, data: [] }); | |||
|  |             } | |||
|  |             let proIndex = proData[typeIndex].findIndex( | |||
|  |               (i) => i.name === proName | |||
|  |             ); | |||
|  |             proData[typeIndex][proIndex].data[ | |||
|  |               -1 * dateIndex + qualityTrend.length - 1 | |||
|  |             ] = value; | |||
|  |           }); | |||
|  |         }); | |||
|  |       }); | |||
|  |       // 折线图 所有值为0,则消失(过滤这条产品数据)
 | |||
|  |       proData[1] = proData[1].filter(({ data }) => {  | |||
|  |         let total = 0; | |||
|  |         data.forEach(val => total += val); | |||
|  |         return total > 0; | |||
|  |       }) | |||
|  |       let typeData = [ | |||
|  |         { | |||
|  |           key: "zhhgl", | |||
|  |           name: "综合合格率", | |||
|  |           data: proData[0], | |||
|  |         }, | |||
|  |         { | |||
|  |           key: "ztl", | |||
|  |           name: "一次性合格率", | |||
|  |           data: proData[1], | |||
|  |         }, | |||
|  |       ]; | |||
|  |       passTrend = { category, data: typeData }; | |||
|  |     } catch (e) { | |||
|  |       console.log("qualityTrend error", e, qualityTrend); | |||
|  |     } | |||
|  |     // 近三天不良率
 | |||
|  |     const { threeDayReject = [] } = data; | |||
|  |     let unPassRate = threeDayReject; | |||
|  |     // 任务提醒
 | |||
|  |     const { taskRemind = [] } = data; | |||
|  |     let taskReminder = taskRemind; | |||
|  | 
 | |||
|  |     setPageData({ | |||
|  |       todayPassRate, | |||
|  |       todayIsPass, | |||
|  |       monthUnPass, | |||
|  |       passTrend, | |||
|  |       unPassRate, | |||
|  |       taskReminder, | |||
|  |     }); | |||
|  |   }, []); | |||
|  | 
 | |||
|  |   const getData = useCallback(() => { | |||
|  |     let workCenterCode = getQueryVariable("workCenterCode"); | |||
|  |     let date = getQueryVariable("date"); | |||
|  |     if (!workCenterCode) return; | |||
|  | 
 | |||
|  |     let query = { | |||
|  |       workCenterCode, | |||
|  |       screenType: socketScreenType, | |||
|  |     }; | |||
|  |     if (date) query.date = date; | |||
|  |     console.log("fetch", socketEmit, { query }); | |||
|  | 
 | |||
|  |     if (dataType === "server") { | |||
|  |       socket.current.emit(socketEmit, "channel", { query }); | |||
|  |     } else if (dataType === "local") { | |||
|  |       update(); | |||
|  |     } | |||
|  |   }, [update]); | |||
|  | 
 | |||
|  |   useEffect(() => { | |||
|  |     if (dataType === "dev") setPageData(devData); | |||
|  | 
 | |||
|  |     if (dataType === "local") getData(); | |||
|  | 
 | |||
|  |     if (dataType === "server") { | |||
|  |       socket.current = io.connect(ioSocketUrl, {transports:['websocket']}); | |||
|  |       socket.current.on("connect", function () { | |||
|  |         console.log("msg页面连接成功!"); | |||
|  |         getData(); | |||
|  |       }); | |||
|  |       socket.current.on(socketEmit, function (res) { | |||
|  |         try { | |||
|  |           res = JSON.parse(res); | |||
|  |         } catch (e) { | |||
|  |           console.log(e); | |||
|  |         } | |||
|  |         update(res.data); | |||
|  |       }); | |||
|  |     } | |||
|  |   }, [getData, update]); | |||
|  | 
 | |||
|  |   return [pageData, getData]; | |||
|  | }; | |||
|  | 
 | |||
|  | function QualityBoard(props) { | |||
|  |   const { config: { qualityBoard = {} } = {} } = global; | |||
|  |   const { threeDayRejectTableNext = 4000, taskRemindTableNext = 4000} = qualityBoard; | |||
|  | 
 | |||
|  |   const [pageData, getPageData] = useSocket({ | |||
|  |     socketEmit, | |||
|  |     socketScreenType, | |||
|  |     dataType, | |||
|  |   }); | |||
|  | 
 | |||
|  |   // 今日合格率
 | |||
|  |   const todayPassRateOpt = useMemo( | |||
|  |     () => chartConfig("todayPassRateConfig").init(pageData), | |||
|  |     [pageData] | |||
|  |   ); | |||
|  |   // 今日是否合格
 | |||
|  |   const todayIsPass = useMemo(() => !!pageData.todayIsPass, [pageData]); | |||
|  |   // 本月不良率TOP5
 | |||
|  |   const monthUnPassOpt = useMemo( | |||
|  |     () => chartConfig("monthUnPassConfig").init(pageData), | |||
|  |     [pageData] | |||
|  |   ); | |||
|  |   // 合格率趋势
 | |||
|  |   const passTrend = useMemo( | |||
|  |     () => chartConfig("passTrendConfig").init(pageData), | |||
|  |     [pageData] | |||
|  |   ); | |||
|  |   // 近三天不良率
 | |||
|  |   const unPassRateTableRef = useRef(); | |||
|  |   const unPassRateTableColumns = useMemo( | |||
|  |     () => [ | |||
|  |       { | |||
|  |         title: "日期", | |||
|  |         code: "date", | |||
|  |         render: (value) => (value && moment(value).format("MM-DD")) || "", | |||
|  |       }, | |||
|  |       { title: "产品名称", code: "materialName" }, | |||
|  |       { title: "产品等级", code: "productLevel" }, | |||
|  |       { title: "生产批次", code: "scpc" }, | |||
|  |       { title: "产量", code: "cl" }, | |||
|  |       { title: "检验批次", code: "jypc" }, | |||
|  |       { title: "不良批次", code: "blpc" }, | |||
|  |       { | |||
|  |         title: "不良率", | |||
|  |         width: 176, | |||
|  |         code: "bll", | |||
|  |         render: (value) => {  | |||
|  |           if (isNaN(value)) return value; | |||
|  |           if (parseFloat(value) === 0) return value; | |||
|  |           return <TableProgress percent={value} rule={ 'strange' } /> | |||
|  |         }, | |||
|  |       }, | |||
|  |     ], | |||
|  |     [] | |||
|  |   ); | |||
|  |   useEffect(() => { | |||
|  |     const { unPassRate = [] } = pageData; | |||
|  |     unPassRateTableRef.current.setData(unPassRate); | |||
|  |     let getNextPage = () => setTimeout(() => { | |||
|  |       unPassRateTableRef.current.nextPage(); | |||
|  |       timmer = getNextPage(); | |||
|  |     }, threeDayRejectTableNext); | |||
|  |     let timmer = getNextPage(); | |||
|  |     return () => { | |||
|  |       clearTimeout(timmer); | |||
|  |     }; | |||
|  |   }, [pageData]); | |||
|  |   // 任务提醒
 | |||
|  |   const taskReminderTableRef = useRef(); | |||
|  |   // 8小时内含8小时数值显示绿色,8小时以上数值显示橙色,24小时以上数值显示红色。
 | |||
|  |   const getColor = useCallback((ms = 0) => { | |||
|  |     if (ms > 24 * 60 * 60) return "#FF005A"; | |||
|  |     if (ms > 8 * 60 * 60) return "#F5A623"; | |||
|  |     if (ms <= 8 * 60 * 60) return "#58BC17"; | |||
|  |   }, []); | |||
|  |   const billstatusStyle = useCallback( | |||
|  |     (ms) => { | |||
|  |       return { | |||
|  |         fontSize: "16px", | |||
|  |         color: "#fff", | |||
|  |         lineHeight: "28px", | |||
|  |         padding: "0 8px", | |||
|  |         display: "inline-block", | |||
|  |         background: getColor(ms), | |||
|  |       }; | |||
|  |     }, | |||
|  |     [getColor] | |||
|  |   ); | |||
|  |   const taskReminderTableColumns = useMemo( | |||
|  |     () => [ | |||
|  |       { | |||
|  |         title: "任务状态", | |||
|  |         code: "billstatus", | |||
|  |         render: (value, config, rowData) => ( | |||
|  |           <span style={billstatusStyle(rowData.ms)}>{value}</span> | |||
|  |         ), | |||
|  |       }, | |||
|  |       { title: "任务名称", code: "templatename" }, | |||
|  |       { title: "产品名称", code: "materialname" }, | |||
|  |       { title: "产品等级", code: "materialdesc" }, | |||
|  |       { title: "批次号", code: "workordercode" }, | |||
|  |       { title: "工序", code: "processname" }, | |||
|  |       { | |||
|  |         title: "任务产生时间", | |||
|  |         code: "createtime", | |||
|  |         render: (value) => | |||
|  |           (value && moment(value).format("YYYY-MM-DD HH:mm")) || "", | |||
|  |       }, | |||
|  |       { | |||
|  |         title: "持续时长", | |||
|  |         code: "cxsc", | |||
|  |         render: (value, config, rowData) => ( | |||
|  |           <span style={{ color: getColor(rowData.ms) }}>{value}</span> | |||
|  |         ), | |||
|  |       }, | |||
|  |     ], | |||
|  |     [billstatusStyle, getColor] | |||
|  |   ); | |||
|  |   useEffect(() => { | |||
|  |     const { taskReminder = [] } = pageData; | |||
|  |     taskReminderTableRef.current.setData(taskReminder); | |||
|  |     let getNextPage = () => setTimeout(() => { | |||
|  |       taskReminderTableRef.current.nextPage(); | |||
|  |       timmer = getNextPage(); | |||
|  |     }, taskRemindTableNext); | |||
|  |     let timmer = getNextPage(); | |||
|  |     return () => { | |||
|  |       clearTimeout(timmer); | |||
|  |     }; | |||
|  |   }, [pageData]); | |||
|  | 
 | |||
|  |   return ( | |||
|  |     <div className="screen-container"> | |||
|  |       <Header title={"质量看板"} /> | |||
|  |       <div className="screen-container-content"> | |||
|  |         <Row | |||
|  |           style={{ height: `${470 + bottom}px` }} | |||
|  |           bottom={bottom} | |||
|  |           gutter={gutter} | |||
|  |         > | |||
|  |           <Row.Col style={{ width: `${470 + gutter}px` }} bottom={bottom}> | |||
|  |             <Row bottom={bottom}> | |||
|  |               <Row.Col span={24} style={{ height: `${220 + bottom}px` }}> | |||
|  |                 <Card | |||
|  |                   title="今日合格率" | |||
|  |                   titleSpace={true} | |||
|  |                   overVisible={true} | |||
|  |                   withBg={cardBg} | |||
|  |                 > | |||
|  |                   <div className="screen-container-hp"> | |||
|  |                     <Row className="height-100" gutter={gutter} bottom={bottom}> | |||
|  |                       <Row.Col span={11} bottom={0}> | |||
|  |                         <Chart option={todayPassRateOpt} /> | |||
|  |                       </Row.Col> | |||
|  |                       <Row.Col span={13} bottom={0}> | |||
|  |                         <div className="screen-container-hp-top"> | |||
|  |                           <span>检验合格率</span> | |||
|  |                         </div> | |||
|  |                         <div className="screen-container-hp-bottom"> | |||
|  |                           {todayIsPass ? "运行正常" : "运行异常"} | |||
|  |                         </div> | |||
|  |                       </Row.Col> | |||
|  |                     </Row> | |||
|  |                   </div> | |||
|  |                 </Card> | |||
|  |               </Row.Col> | |||
|  |               <Row.Col span={24} style={{ height: `${230}px` }} bottom={0}> | |||
|  |                 <Card title="本月不良率TOP5" titleSpace={true} withBg={cardBg}> | |||
|  |                   <Chart option={monthUnPassOpt} /> | |||
|  |                 </Card> | |||
|  |               </Row.Col> | |||
|  |             </Row> | |||
|  |           </Row.Col> | |||
|  |           <Row.Col | |||
|  |             style={{ width: `${1390 + bottom}px`, height: `${470 + gutter}px` }} | |||
|  |           > | |||
|  |             <Card title="合格率趋势" titleSpace={false} withBg={cardBg}> | |||
|  |               <Chart option={passTrend} /> | |||
|  |             </Card> | |||
|  |           </Row.Col> | |||
|  |         </Row> | |||
|  |         <Row style={{ height: `${404}px` }} gutter={gutter} bottom={0}> | |||
|  |           <Row.Col span={12}> | |||
|  |             <Card | |||
|  |               title="近三天不良率" | |||
|  |               titleSpace={true} | |||
|  |               withBg={cardBg} | |||
|  |               padding={gutter + "px"} | |||
|  |             > | |||
|  |               <Table | |||
|  |                 mainKey="gid" | |||
|  |                 ref={unPassRateTableRef} | |||
|  |                 columns={unPassRateTableColumns} | |||
|  |               /> | |||
|  |             </Card> | |||
|  |           </Row.Col> | |||
|  |           <Row.Col span={12}> | |||
|  |             <Card | |||
|  |               title="任务提醒" | |||
|  |               titleSpace={true} | |||
|  |               withBg={cardBg} | |||
|  |               padding={gutter + "px"} | |||
|  |             > | |||
|  |               <Table | |||
|  |                 mainKey="gid" | |||
|  |                 autoNext={false} | |||
|  |                 pageFinished={getPageData} | |||
|  |                 ref={taskReminderTableRef} | |||
|  |                 columns={taskReminderTableColumns} | |||
|  |               /> | |||
|  |             </Card> | |||
|  |           </Row.Col> | |||
|  |         </Row> | |||
|  |       </div> | |||
|  |       <Footer /> | |||
|  |       <style jsx>{`
 | |||
|  |         .screen-container { | |||
|  |           height: 100vh; | |||
|  |           min-height: 1080px; | |||
|  |           min-width: 1920px; | |||
|  |           overflow: hidden; | |||
|  |           background: url("/img/bg.png") center center; | |||
|  |           position: relative; | |||
|  |           padding-top: 96px; | |||
|  |           padding-bottom: 82px; | |||
|  |         } | |||
|  |         .screen-container-content { | |||
|  |           height: 100%; | |||
|  |           padding: 0 ${gutter}px; | |||
|  |         } | |||
|  |         .screen-container-hp { | |||
|  |           height: 100%; | |||
|  |           overflow: hidden; | |||
|  |         } | |||
|  |         .screen-container-hp-top { | |||
|  |           height: 82px; | |||
|  |           background: url(/img/img1.png); | |||
|  |           background-repeat: no-repeat; | |||
|  |           position: relative; | |||
|  |           background-position: 0 10px; | |||
|  |         } | |||
|  |         .screen-container-hp-top span { | |||
|  |           color: #00f7fb; | |||
|  |           position: absolute; | |||
|  |           font-size: 18px; | |||
|  |           top: 25px; | |||
|  |           left: 46px; | |||
|  |           letter-spacing: 10px; | |||
|  |         } | |||
|  |         .screen-container-hp-bottom { | |||
|  |           font-size: 32px; | |||
|  |           line-height: 38px; | |||
|  |           width: 188px; | |||
|  |           color: #00f7fb; | |||
|  |           text-align: center; | |||
|  |           background-image: linear-gradient( | |||
|  |             180deg, | |||
|  |             rgba(0, 76, 167, 0.2) 0%, | |||
|  |             rgba(36, 53, 184, 0.5) 100% | |||
|  |           ); | |||
|  |           border: 1px solid #0064f1; | |||
|  |           border-radius: 3px; | |||
|  |           margin-top: 12px; | |||
|  |         } | |||
|  |         .screen-container-hp-bottom::before { | |||
|  |           content: ""; | |||
|  |           display: block; | |||
|  |           margin: -8px -10px 0 -10px; | |||
|  |           height: 20px; | |||
|  |           border-top: 1px solid #0064f1; | |||
|  |           border-left: 1px solid #0064f1; | |||
|  |           border-right: 1px solid #0064f1; | |||
|  |           border-top-left-radius: 3px; | |||
|  |           border-top-right-radius: 3px; | |||
|  |         } | |||
|  |         .screen-container-hp-bottom::after { | |||
|  |           content: ""; | |||
|  |           display: block; | |||
|  |           margin: 0 -10px -8px -10px; | |||
|  |           height: 20px; | |||
|  |           border-bottom: 1px solid #0064f1; | |||
|  |           border-left: 1px solid #0064f1; | |||
|  |           border-right: 1px solid #0064f1; | |||
|  |           border-bottom-left-radius: 3px; | |||
|  |           border-bottom-right-radius: 3px; | |||
|  |         } | |||
|  |       `}</style>
 | |||
|  |     </div> | |||
|  |   ); | |||
|  | } | |||
|  | 
 | |||
|  | const devData = { | |||
|  |   // 今日合格率
 | |||
|  |   todayPassRate: 0.855555, | |||
|  |   // 今日是否合格
 | |||
|  |   todayIsPass: true, | |||
|  |   // 本月不良率TOP5
 | |||
|  |   monthUnPass: [ | |||
|  |     { value: 0.0909, name: "硫含量" }, | |||
|  |     { value: 0.3564, name: "DIO" }, | |||
|  |     { value: 0.1203, name: "粉体电阻率" }, | |||
|  |     { value: 0.1253, name: "包装袋" }, | |||
|  |     { value: 0.2005, name: "振实密度" }, | |||
|  |   ], | |||
|  |   // 合格率趋势
 | |||
|  |   passTrend: { | |||
|  |     category: ["08-01", "08-02", "08-03", "08-04", "08-05", "08-06", "08-07"], | |||
|  |     data: [ | |||
|  |       { | |||
|  |         name: "综合合格率", | |||
|  |         data: [ | |||
|  |           { | |||
|  |             name: "E60", | |||
|  |             data: [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], | |||
|  |           }, | |||
|  |           { | |||
|  |             name: "E70", | |||
|  |             data: [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], | |||
|  |           }, | |||
|  |           { | |||
|  |             name: "E80", | |||
|  |             data: [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], | |||
|  |           }, | |||
|  |         ], | |||
|  |       }, | |||
|  |       { | |||
|  |         name: "一次性合格率", | |||
|  |         data: [ | |||
|  |           { | |||
|  |             name: "E60", | |||
|  |             data: [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], | |||
|  |           }, | |||
|  |           { | |||
|  |             name: "E70", | |||
|  |             data: [0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2], | |||
|  |           }, | |||
|  |           { | |||
|  |             name: "E80", | |||
|  |             data: [0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3], | |||
|  |           }, | |||
|  |         ], | |||
|  |       }, | |||
|  |     ], | |||
|  |   }, | |||
|  |   // 近三天不良率
 | |||
|  |   unPassRate: [ | |||
|  |     { | |||
|  |       gid: "1", | |||
|  |       date: "2022-08-06", //日期
 | |||
|  |       materialName: "磷酸铁锂", //产品名称
 | |||
|  |       productLevel: "E80", //产品等级
 | |||
|  |       scpc: 2, //生产批次
 | |||
|  |       cl: 2, //产量
 | |||
|  |       jypc: 2, //检验批次
 | |||
|  |       blpc: 0, //不良批次
 | |||
|  |       bll: "0.123", //不良率
 | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "2", | |||
|  |       date: "2022-08-06", //日期
 | |||
|  |       materialName: "磷酸铁锂", //产品名称
 | |||
|  |       productLevel: "E80", //产品等级
 | |||
|  |       scpc: 2, //生产批次
 | |||
|  |       cl: 2, //产量
 | |||
|  |       jypc: 2, //检验批次
 | |||
|  |       blpc: 0, //不良批次
 | |||
|  |       bll: "无", //不良率
 | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "3", | |||
|  |       date: "2022-08-06", //日期
 | |||
|  |       materialName: "磷酸铁锂", //产品名称
 | |||
|  |       productLevel: "E80", //产品等级
 | |||
|  |       scpc: 2, //生产批次
 | |||
|  |       cl: 2, //产量
 | |||
|  |       jypc: 2, //检验批次
 | |||
|  |       blpc: 0, //不良批次
 | |||
|  |       bll: "1", //不良率
 | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "4", | |||
|  |       date: "2022-08-06", //日期
 | |||
|  |       materialName: "磷酸铁锂", //产品名称
 | |||
|  |       productLevel: "E80", //产品等级
 | |||
|  |       scpc: 2, //生产批次
 | |||
|  |       cl: 2, //产量
 | |||
|  |       jypc: 2, //检验批次
 | |||
|  |       blpc: 0, //不良批次
 | |||
|  |       bll: "1.000", //不良率
 | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "5", | |||
|  |       date: "2022-08-06", //日期
 | |||
|  |       materialName: "磷酸铁锂", //产品名称
 | |||
|  |       productLevel: "E80", //产品等级
 | |||
|  |       scpc: 2, //生产批次
 | |||
|  |       cl: 2, //产量
 | |||
|  |       jypc: 2, //检验批次
 | |||
|  |       blpc: 0, //不良批次
 | |||
|  |       bll: "0", //不良率
 | |||
|  |     }, | |||
|  |   ], | |||
|  |   // 任务提醒
 | |||
|  |   taskReminder: [ | |||
|  |     { | |||
|  |       gid: "1", | |||
|  |       billstatus: "待检验", //任务状态
 | |||
|  |       templatename: "测试工单批次生成质检任务", //任务名称
 | |||
|  |       materialname: "L5车间成品", //产品名称
 | |||
|  |       materialdesc: "E80A", //产品等级
 | |||
|  |       workordercode: "HSR5A22082201", //批次号
 | |||
|  |       processname: "粗磨", //工序
 | |||
|  |       createtime: "2022-08-01 00:00:00", //任务产生时间
 | |||
|  |       cxsc: "50分钟", //持续时长
 | |||
|  |       ms: 1, | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "2", | |||
|  |       billstatus: "待检验", //任务状态
 | |||
|  |       templatename: "测试工单批次生成质检任务", //任务名称
 | |||
|  |       materialname: "L5车间成品", //产品名称
 | |||
|  |       materialdesc: "E80A", //产品等级
 | |||
|  |       workordercode: "HSR5A22082201", //批次号
 | |||
|  |       processname: "粗磨", //工序
 | |||
|  |       createtime: "2022-08-01 00:00:00", //任务产生时间
 | |||
|  |       cxsc: "50分钟", //持续时长
 | |||
|  |       ms: 1000000, | |||
|  |     }, | |||
|  |   ], | |||
|  | }; | |||
|  | const localData = { | |||
|  |   todayQuality: { | |||
|  |     qualityRate: 0.231, | |||
|  |   }, | |||
|  |   monthDefectRateTop5: { | |||
|  |     PH值: 0.3333, | |||
|  |     粒度: 0.3333, | |||
|  |     感应强度: 0.3333, | |||
|  |     包装袋: 0.1203, | |||
|  |     振实密度: 0.2005, | |||
|  |   }, | |||
|  |   qualityTrend: [ | |||
|  |     { | |||
|  |       date: "08-22", | |||
|  |       ztl: { | |||
|  |         E60: 0.42, | |||
|  |         E70: 0.32, | |||
|  |         E80: 0.22, | |||
|  |       }, | |||
|  |       zhhgl: { | |||
|  |         E60: 0.12, | |||
|  |         E70: 0.12, | |||
|  |         E80: 0.12, | |||
|  |       }, | |||
|  |     }, | |||
|  |     { | |||
|  |       date: "08-21", | |||
|  |       ztl: { | |||
|  |         E60: 0.42, | |||
|  |         E70: 0.32, | |||
|  |         E80: 0.22, | |||
|  |       }, | |||
|  |       zhhgl: { | |||
|  |         E60: 0.12, | |||
|  |         E70: 0.12, | |||
|  |         E80: 0.12, | |||
|  |       }, | |||
|  |     }, | |||
|  |     { | |||
|  |       date: "08-20", | |||
|  |       ztl: { | |||
|  |         E60: 0.42, | |||
|  |         E70: 0.32, | |||
|  |         E80: 0.22, | |||
|  |       }, | |||
|  |       zhhgl: { | |||
|  |         E60: 0.12, | |||
|  |         E70: 0.12, | |||
|  |         E80: 0.12, | |||
|  |       }, | |||
|  |     }, | |||
|  |     { | |||
|  |       date: "08-19", | |||
|  |       ztl: { | |||
|  |         E60: 0.42, | |||
|  |         E70: 0.32, | |||
|  |         E80: 0.22, | |||
|  |       }, | |||
|  |       zhhgl: { | |||
|  |         E60: 0.12, | |||
|  |         E70: 0.12, | |||
|  |         E80: 0.12, | |||
|  |       }, | |||
|  |     }, | |||
|  |     { | |||
|  |       date: "08-18", | |||
|  |       ztl: { | |||
|  |         E60: 0.42, | |||
|  |         E70: 0.32, | |||
|  |         E80: 0.22, | |||
|  |       }, | |||
|  |       zhhgl: { | |||
|  |         E60: 0.12, | |||
|  |         E70: 0.12, | |||
|  |         E80: 0.12, | |||
|  |       }, | |||
|  |     }, | |||
|  |     { | |||
|  |       date: "08-17", | |||
|  |       ztl: { | |||
|  |         E60: 0.42, | |||
|  |         E70: 0.32, | |||
|  |         E80: 0.22, | |||
|  |       }, | |||
|  |       zhhgl: { | |||
|  |         E60: 0.12, | |||
|  |         E70: 0.12, | |||
|  |         E80: 0.12, | |||
|  |       }, | |||
|  |     }, | |||
|  |     { | |||
|  |       date: "08-16", | |||
|  |       ztl: { | |||
|  |         E60: 0.42, | |||
|  |         E70: 0.32, | |||
|  |         E80: 0.22, | |||
|  |       }, | |||
|  |       zhhgl: { | |||
|  |         E60: 0.12, | |||
|  |         E70: 0.12, | |||
|  |         E80: 0.12, | |||
|  |       }, | |||
|  |     }, | |||
|  |   ], | |||
|  |   threeDayReject: [ | |||
|  |     { | |||
|  |       gid: "1", | |||
|  |       date: "2022-08-06", //日期
 | |||
|  |       materialName: "磷酸铁锂", //产品名称
 | |||
|  |       productLevel: "E80", //产品等级
 | |||
|  |       scpc: 2, //生产批次
 | |||
|  |       cl: 2, //产量
 | |||
|  |       jypc: 2, //检验批次
 | |||
|  |       blpc: 0, //不良批次
 | |||
|  |       bll: "0.123123123123", //不良率
 | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "2", | |||
|  |       date: "2022-08-06", //日期
 | |||
|  |       materialName: "磷酸铁锂", //产品名称
 | |||
|  |       productLevel: "E80", //产品等级
 | |||
|  |       scpc: 2, //生产批次
 | |||
|  |       cl: 2, //产量
 | |||
|  |       jypc: 2, //检验批次
 | |||
|  |       blpc: 0, //不良批次
 | |||
|  |       bll: "无", //不良率
 | |||
|  |     }, | |||
|  |     { gid: "3" }, | |||
|  |     { gid: "4" }, | |||
|  |     { gid: "5" }, | |||
|  |     { gid: "6" }, | |||
|  |     { gid: "7" }, | |||
|  |     { gid: "8" }, | |||
|  |     { gid: "9" }, | |||
|  |   ], | |||
|  |   taskRemind: [ | |||
|  |     { | |||
|  |       gid: "1", | |||
|  |       billstatus: "待检验", //任务状态
 | |||
|  |       templatename: "测试工单批次生成质检任务", //任务名称
 | |||
|  |       materialname: "L5车间成品", //产品名称
 | |||
|  |       materialdesc: "E80A", //产品等级
 | |||
|  |       workordercode: "HSR5A22082201", //批次号
 | |||
|  |       processname: "粗磨", //工序
 | |||
|  |       createtime: "2022-08-01 00:00:00", //任务产生时间
 | |||
|  |       cxsc: "50分钟", //持续时长
 | |||
|  |       ms: 1, | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "2", | |||
|  |       billstatus: "待检验", //任务状态
 | |||
|  |       templatename: "测试工单批次生成质检任务", //任务名称
 | |||
|  |       materialname: "L5车间成品", //产品名称
 | |||
|  |       materialdesc: "E80A", //产品等级
 | |||
|  |       workordercode: "HSR5A22082201", //批次号
 | |||
|  |       processname: "粗磨", //工序
 | |||
|  |       createtime: "2022-08-01 00:00:00", //任务产生时间
 | |||
|  |       cxsc: "50分钟", //持续时长
 | |||
|  |       ms: 1000000, | |||
|  |     }, | |||
|  |     { | |||
|  |       gid: "3", | |||
|  |       billstatus: "待检验", //任务状态
 | |||
|  |       templatename: "测试工单批次生成质检任务", //任务名称
 | |||
|  |       materialname: "L5车间成品", //产品名称
 | |||
|  |       materialdesc: "E80A", //产品等级
 | |||
|  |       workordercode: "HSR5A22082201", //批次号
 | |||
|  |       processname: "粗磨", //工序
 | |||
|  |       createtime: "2022-08-01 00:00:00", //任务产生时间
 | |||
|  |       cxsc: "50分钟", //持续时长
 | |||
|  |       ms: 30000, | |||
|  |     }, | |||
|  |     { gid: "4" }, | |||
|  |     { gid: "5" }, | |||
|  |     { gid: "6" }, | |||
|  |     { gid: "7" }, | |||
|  |     { gid: "8" }, | |||
|  |     { gid: "9" }, | |||
|  |   ], | |||
|  | }; | |||
|  | 
 | |||
|  | // 今日合格率
 | |||
|  | const todayPassRateConfig = { | |||
|  |   series: { | |||
|  |     type: "liquidFill", | |||
|  |     center: ["50%", "50%"], | |||
|  |     radius: "75%", | |||
|  |     data: [ | |||
|  |       { | |||
|  |         value: 0.855, | |||
|  |         itemStyle: { | |||
|  |           opacity: 0.3, | |||
|  |         }, | |||
|  |       }, | |||
|  |       0.855, | |||
|  |     ], | |||
|  |     itemStyle: {}, | |||
|  |     label: { | |||
|  |       position: ["50%", "30%"], | |||
|  |       formatter: (opt) => `${parseFloat((opt.value * 100).toFixed(1))}%`, | |||
|  |       fontSize: 28, | |||
|  |     }, | |||
|  |     backgroundStyle: { | |||
|  |       color: "none", | |||
|  |     }, | |||
|  |     outline: { | |||
|  |       show: true, | |||
|  |       borderDistance: 2, | |||
|  |       itemStyle: { | |||
|  |         color: "none", | |||
|  |         borderColor: "#f6202f", | |||
|  |         borderWidth: 2, | |||
|  |       }, | |||
|  |     }, | |||
|  |   }, | |||
|  | }; | |||
|  | let todayPassRateColors = { | |||
|  |   red: { | |||
|  |     fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ | |||
|  |       { | |||
|  |         offset: 0, | |||
|  |         color: "hsl(356deg 92% 65%)", | |||
|  |       }, | |||
|  |       { | |||
|  |         offset: 1, | |||
|  |         color: "hsl(356deg 92% 25%)", | |||
|  |       }, | |||
|  |     ]), | |||
|  |     color: "#f6202f", | |||
|  |   }, | |||
|  |   green: { | |||
|  |     fill: "#92D050", | |||
|  |     color: "#92D050", | |||
|  |   }, | |||
|  |   orange: { | |||
|  |     fill: "#FFC000", | |||
|  |     color: "#FFC000", | |||
|  |   }, | |||
|  | }; | |||
|  | todayPassRateConfig.init = (pageData) => { | |||
|  |   let { todayPassRate = 0, todayIsPass } = pageData; | |||
|  |   let opt = _.cloneDeep(todayPassRateConfig); | |||
|  |   opt.series.data[0].value = todayPassRate; | |||
|  |   opt.series.data[1] = todayPassRate; | |||
|  | 
 | |||
|  |   let { orange, green } = todayPassRateColors; | |||
|  |   let { fill, color } = todayIsPass ? green : orange; | |||
|  | 
 | |||
|  |   opt.series.itemStyle.color = fill; | |||
|  |   opt.series.label.color = color; | |||
|  |   opt.series.outline.itemStyle.borderColor = color; | |||
|  |   return opt; | |||
|  | }; | |||
|  | // 本月不良率TOP5
 | |||
|  | const monthUnPassConfig = { | |||
|  |   color: ["#4D7BF3", "#4FCCFF", "#6EE420", "#FFC760", "#FF005A"], | |||
|  |   legend: { | |||
|  |     orient: "vertical", | |||
|  |     left: 10, | |||
|  |     top: "middle", | |||
|  |     itemGap: 16, | |||
|  |     textStyle: { | |||
|  |       rich: { | |||
|  |         0: { opacity: 0.75 }, | |||
|  |         1: { opacity: 1 }, | |||
|  |       }, | |||
|  |     }, | |||
|  |   }, | |||
|  |   series: { | |||
|  |     type: "pie", | |||
|  |     radius: ["40%", "70%"], | |||
|  |     center: ["65%", "45%"], | |||
|  |     label: { | |||
|  |       show: true, | |||
|  |       textBorderColor: "transparent", | |||
|  |       rich: { | |||
|  |         0: { color: "#4D7BF3" }, | |||
|  |         1: { color: "#4FCCFF" }, | |||
|  |         2: { color: "#6EE420" }, | |||
|  |         3: { color: "#FFC760" }, | |||
|  |         4: { color: "#FF005A" }, | |||
|  |       }, | |||
|  |     }, | |||
|  |     labelLine: { | |||
|  |       show: true, | |||
|  |     }, | |||
|  |     data: [], | |||
|  |   }, | |||
|  | }; | |||
|  | monthUnPassConfig.init = (pageData) => { | |||
|  |   let { monthUnPass } = pageData; | |||
|  |   if (!monthUnPass) return; | |||
|  |   let opt = _.cloneDeep(monthUnPassConfig); | |||
|  |   opt.series.data = monthUnPass; | |||
|  |   opt.legend.formatter = (name) => { | |||
|  |     let { value } = monthUnPass.find((i) => i.name === name); | |||
|  |     return `{0|${name}:}{1|${(value * 100).toFixed(2)}%}`; | |||
|  |   }; | |||
|  |   opt.series.label.formatter = (data) => { | |||
|  |     let { name, dataIndex } = data; | |||
|  |     return `{${dataIndex}|${name}}`; | |||
|  |   }; | |||
|  |   return opt; | |||
|  | }; | |||
|  | // 合格率趋势
 | |||
|  | const passTrendConfig = { | |||
|  |   grid: { | |||
|  |     bottom: 40, | |||
|  |     top: 80, | |||
|  |     left: 70, | |||
|  |     right: 70, | |||
|  |   }, | |||
|  |   legend: { | |||
|  |     right: 20, | |||
|  |     top: 20, | |||
|  |   }, | |||
|  |   barMaxWidth: 30, | |||
|  |   xAxis: { | |||
|  |     type: "category", | |||
|  |     data: [], | |||
|  |     axisTick: { | |||
|  |       show: true, | |||
|  |       alignWithLabel: true, | |||
|  |     }, | |||
|  |     axisLine: { | |||
|  |       lineStyle: { | |||
|  |         color: "rgba(255,255,255,0.65)", | |||
|  |       }, | |||
|  |     }, | |||
|  |     axisLabel: { | |||
|  |       color: "rgba(255,255,255,0.85)", | |||
|  |     }, | |||
|  |   }, | |||
|  |   yAxis: [ | |||
|  |     { | |||
|  |       type: "value", | |||
|  |       max: 1, | |||
|  |       axisTick: { | |||
|  |         show: true, | |||
|  |         alignWithLabel: true, | |||
|  |       }, | |||
|  |       axisLine: { | |||
|  |         lineStyle: { | |||
|  |           color: "rgba(255,255,255,0.65)", | |||
|  |         }, | |||
|  |       }, | |||
|  |       axisLabel: { | |||
|  |         color: "rgba(255,255,255,0.85)", | |||
|  |         formatter: (value) => (value * 100).toFixed(0) + "%", | |||
|  |       }, | |||
|  |     }, | |||
|  |     { | |||
|  |       type: "value", | |||
|  |       max: 1, | |||
|  |       axisTick: { | |||
|  |         show: true, | |||
|  |         alignWithLabel: true, | |||
|  |       }, | |||
|  |       axisLine: { | |||
|  |         lineStyle: { | |||
|  |           color: "rgba(255,255,255,0.65)", | |||
|  |         }, | |||
|  |       }, | |||
|  |       axisLabel: { | |||
|  |         color: "rgba(255,255,255,0.85)", | |||
|  |         formatter: (value) => (value * 100).toFixed(0) + "%", | |||
|  |       }, | |||
|  |     }, | |||
|  |   ], | |||
|  |   series: [], | |||
|  | }; | |||
|  | const colors = [[new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(249, 203, 173)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(225, 193, 0)' | |||
|  |   }, { | |||
|  |     offset: 0.63, color: 'rgb(217, 199, 117)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.17, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(169, 209, 142)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(0, 176, 240)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(191, 144, 0)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(237, 125, 49)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(255, 153, 153)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(51, 102, 255)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(0, 255, 153)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }]), new echarts.graphic.LinearGradient(0, 1, 0, 0, [{ | |||
|  |     offset: 1, color: 'rgb(255, 51, 253)' | |||
|  |   }, { | |||
|  |     offset: 0.26, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0.16, color: 'rgb(183, 210, 236)' | |||
|  |   }, { | |||
|  |     offset: 0, color: 'rgb(208, 225, 242)' | |||
|  |   }])], [ | |||
|  |   "#3366FF", "#00CCFF", "#FF6699", "#8497B0", "#C9C9C9", | |||
|  |   "#FFD966","#8FAADC","#ED7D31","#FFFF00","#92D050" | |||
|  |   ], | |||
|  | ] | |||
|  | passTrendConfig.init = (pageData) => { | |||
|  |   let { passTrend } = pageData; | |||
|  |   if (!passTrend) return; | |||
|  |   let opt = _.cloneDeep(passTrendConfig); | |||
|  | 
 | |||
|  |   let { category, data } = passTrend; | |||
|  |   opt.xAxis.data = category; | |||
|  | 
 | |||
|  |   opt.series = []; | |||
|  |   data.forEach((item, yAxisIndex) => { | |||
|  |     let type = yAxisIndex === 0 ? "bar" : "line"; | |||
|  |     let { name: typeName } = item; | |||
|  |     item.data.forEach(({ name: proName, data }, index) => { | |||
|  |       let name = `${proName} ${typeName}`; | |||
|  |       opt.series.push({ name, yAxisIndex, type, data, color: colors[yAxisIndex][index] }); | |||
|  |     }); | |||
|  |   }); | |||
|  |   return opt; | |||
|  | }; | |||
|  | 
 | |||
|  | let chartConfig = (() => { | |||
|  |   let chartOpt = { | |||
|  |     todayPassRateConfig, | |||
|  |     monthUnPassConfig, | |||
|  |     passTrendConfig, | |||
|  |   }; | |||
|  |   let getOpt = (type) => chartOpt[type]; | |||
|  |   return getOpt; | |||
|  | })(); | |||
|  | 
 | |||
|  | export default withRouter(QualityBoard); |