第一次提交

This commit is contained in:
2025-07-18 17:43:23 +08:00
commit 50de9704a0
106 changed files with 35515 additions and 0 deletions

24
src/App.css Normal file
View File

@@ -0,0 +1,24 @@
.background {
height: 1080px;
width: 3840px;
background: url(/public/image/background.png) 100% no-repeat;
background-size: 100% 100%;
position: relative;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-col {
display: flex;
flex-direction: column;
}
.handan-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.handan-chart-tooltip * {
color: #fff !important;
}

35
src/App.tsx Normal file
View File

@@ -0,0 +1,35 @@
import "./App.css";
import { useAppSelector } from "./store/hooks";
// import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { createHashRouter, RouterProvider } from "react-router-dom";
import ErrorPage from "./page/ErrorPage";
import { MyObservable } from "./context/MyObservable";
import { Observable } from "@babylonjs/core";
import { selectGlassStatus } from "./store/ProductionMonitoringEntity";
import HomePage from "./page/HomePage";
const onGlassObservable = new Observable();
function App() {
const thisLineGlassStatus = useAppSelector(selectGlassStatus);
onGlassObservable.notifyObservers(thisLineGlassStatus);
const router = createHashRouter([
{
path: "/",
element: <HomePage />,
errorElement: <ErrorPage />,
},
]);
return (
<MyObservable.Provider value={onGlassObservable}>
<div className="background">
<RouterProvider router={router} />
</div>
</MyObservable.Provider>
);
}
export default App;

Binary file not shown.

BIN
src/assets/image/chartA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
src/assets/image/chartB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/image/icon1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/assets/image/icon2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/assets/image/icon3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/assets/image/icon4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/assets/image/icon5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/image/icon6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/assets/image/icon7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/image/icon8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/assets/image/t1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
src/assets/image/t2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/assets/image/t3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/image/t4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
src/assets/image/t5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/assets/image/t6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/assets/image/t7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

BIN
src/assets/image/t8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

View File

@@ -0,0 +1,277 @@
import React, { useEffect, useRef, useState, useContext, useMemo } from "react";
import * as BABYLON from "@babylonjs/core";
import "@babylonjs/core/Debug/debugLayer";
import "@babylonjs/inspector";
import "@babylonjs/loaders/glTF";
import { GridMaterial } from "@babylonjs/materials/";
import {
Animatable,
HemisphericLight,
Mesh,
Observable,
Vector3,
} from "@babylonjs/core";
import { useAppSelector } from "../store/hooks";
import { EquStatusInterface, selectEquStatus } from "../store/EquStatusEntity";
import eqInfoIcon from "../assets/image/eqInfoIcon.png";
import axios from "axios";
const onEquObservable = new Observable();
const myStyle = {
width: "2635px",
height: "536px",
outline: "none",
};
interface EqSubListInterface {
eqName: string;
subList: string[];
}
function MybabylonJS() {
const EquStatus = useAppSelector(selectEquStatus);
const canvasRef = useRef(null);
onEquObservable.notifyObservers(EquStatus);
// const [selectedMeshName, setSelectedMeshName] = useState<string | null>(null);
const [eqSubList, setEqSubList] = useState<EqSubListInterface>();
// 使用 useRef 来存储当前加载的模型引用
const currentMeshesRef = useRef<Array<BABYLON.AbstractMesh>>([]);
useEffect(() => {
// 确保 canvas 引用存在
if (!canvasRef.current) return;
const canvas = canvasRef.current;
const engine = new BABYLON.Engine(canvas, true, {
preserveDrawingBuffer: true,
stencil: true,
});
const createScene = async function () {
// This creates a basic Babylon Scene object (non-mesh)
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0, 0, 0, 0);
const baseLight = new HemisphericLight(
"hemiLight",
new Vector3(1, 1, 0),
scene
);
baseLight.intensity = 0.7;
// baseLight.diffuse = new BABYLON.Color3(1, 1, 1);
// baseLight.specular = new BABYLON.Color3(0.25, 0.25, 0.25);
// baseLight.groundColor = new BABYLON.Color3(0.5, 0.5, 0.5);
//add an arcRotateCamera to the scene
const camera = new BABYLON.ArcRotateCamera(
"camera",
-Math.PI / 2,
Math.PI / 2.9,
120,
new BABYLON.Vector3(0, 10, 0)
);
// console.log("camera", camera);
camera.lowerRadiusLimit = 10;
camera.upperRadiusLimit = 600;
camera.fov = 0.4;
// This attaches the camera to the canvas
camera.attachControl(canvas, true);
//创建一个材质
const newMt = new BABYLON.StandardMaterial("newMt");
newMt.diffuseColor = BABYLON.Color3.Blue();
const ground = BABYLON.MeshBuilder.CreateGround(
"ground",
{
width: 1000,
height: 1000,
subdivisions: 1,
},
scene
);
ground.scaling.x = 100;
ground.scaling.z = ground.scaling.x;
ground.isPickable = false;
let grid = new GridMaterial("grid", scene);
grid.majorUnitFrequency = 10;
grid.minorUnitVisibility = 0.3;
grid.gridRatio = 0.04;
grid.backFaceCulling = !1;
grid.mainColor = new BABYLON.Color3(1, 1, 1);
grid.lineColor = new BABYLON.Color3(1, 1, 1);
grid.opacity = 0;
grid.zOffset = 1;
// grid.opacityTexture = new BABYLON.Texture(
// "/public/png/backgroundGround.png",
// scene
// );
ground.material = grid;
let hl = new BABYLON.HighlightLayer("hl1", scene);
let hl2 = new BABYLON.HighlightLayer("hl2", scene);
// 定义一个函数来加载或重新加载模型
const loadOrReloadModel = async () => {
// 在加载新模型之前卸载已加载的模型
currentMeshesRef.current.forEach((mesh) => {
if (mesh && mesh.parent) {
scene.removeMesh(mesh, true);
}
});
currentMeshesRef.current = []; // 重置模型数组
try {
// 使用 ImportMeshAsync 加载新模型
var LOD0MESH = await BABYLON.SceneLoader.ImportMeshAsync(
"",
"/Line/",
"line.babylon",
scene
);
// 将新加载的模型添加到 currentMeshesRef 中
currentMeshesRef.current.push(...LOD0MESH.meshes);
// ...为新加载的模型设置交互逻辑
LOD0MESH.meshes.map((mesh) => {
mesh.isPickable = true;
mesh.actionManager = new BABYLON.ActionManager(scene);
//鼠标移动到物体上亮显
mesh.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(
BABYLON.ActionManager.OnPointerOverTrigger,
() => {
// @ts-ignore
hl.addMesh(mesh, BABYLON.Color3.Green());
}
)
);
mesh.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(
BABYLON.ActionManager.OnPointerOutTrigger,
() => {
// @ts-ignore
hl.removeMesh(mesh);
}
)
);
// mesh._scene.onPointerDown = async (event, _pickResult) => {
// const pickInfo = mesh._scene.pick(
// mesh._scene.pointerX,
// mesh._scene.pointerY
// );
// const clickedPosition = _pickResult.pickedPoint;
// //如果需要获取吗模型根节点,而不是模型中某个组件,请用一下方法
// // getRootNode(pickInfo.pickedMesh as BABYLON.Node) 如上篇文章getRootNode函数
// //判断是否是右键
// if (!(event.buttons === 1 && pickInfo.pickedMesh)) return;
// const MeshId = pickInfo.pickedMesh.metadata.tags;
// if (MeshId) {
// // setSelectedMeshName(pickInfo.pickedMesh.name);
// // axios.get("/wsconfig.json").then((r) => {
// // console.log(r.data.url)
// axios
// .post(
// "api/visual/DataCenter/equipmentSubstrateList",
// { id: MeshId }
// )
// .then((res) => {
// let obj = { eqName: "-", subList: [] };
// obj.eqName = res.data.data.eqName || "-";
// obj.subList = res.data.data.subList || [];
// setEqSubList(obj);
// });
// // });
// }
// };
});
} catch (error) {
console.error("加载模型失败:", error);
}
};
// 调用函数以加载或重新加载模型
loadOrReloadModel();
function reset() {
camera.target = new BABYLON.Vector3(0, 10, 0);
camera.alpha = -Math.PI / 2;
camera.beta = Math.PI / 2.9;
camera.radius = 120;
camera.fov = 0.4;
}
let resetCamera = setTimeout(reset, 15000);
scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case BABYLON.PointerEventTypes.POINTERMOVE:
clearTimeout(resetCamera);
resetCamera = setTimeout(reset, 15000);
}
});
return scene;
};
// call the createScene function
const scene = createScene();
scene.then((scene) => {});
// run the render loop
scene.then(
(scene) => {
engine.runRenderLoop(function () {
scene.render();
});
},
(reason) => {
console.log(reason);
}
);
// Resize
window.addEventListener("resize", function () {
engine.resize();
});
// 组件卸载时的清理逻辑
return () => {
// 清理场景和引擎资源
engine.dispose();
};
}, []);
return (
<div style={myStyle}>
{/* <h1 style={{ position: "absolute", top: "140px", left: "0px" }}>
当前选中的设备是:{selectedMeshName}
</h1> */}
{/* <div className="eq_info_box">
<div>
<img src={eqInfoIcon} alt="" width={34} />
<span className="title">设备名称:{eqSubList?.eqName || "-"}</span>
</div>
<div className="content">
<span className="bg">{eqSubList?.subList[0] || "-"}</span>
<span className="bg">{eqSubList?.subList[1] || "-"}</span>
<span>{eqSubList?.subList[2] || "-"}</span>
<span>{eqSubList?.subList[3] || "-"}</span>
<span className="bg">{eqSubList?.subList[4] || "-"}</span>
<span className="bg">{eqSubList?.subList[5] || "-"}</span>
<span>{eqSubList?.subList[6] || "-"}</span>
<span>{eqSubList?.subList[7] || "-"}</span>
<span className="bg">{eqSubList?.subList[8] || "-"}</span>
<span className="bg">{eqSubList?.subList[9] || "-"}</span>
</div>
</div> */}
<canvas ref={canvasRef} style={myStyle} />
</div>
);
}
export default MybabylonJS;

View File

@@ -0,0 +1,5 @@
import { createContext } from 'react';
import {Observable} from "@babylonjs/core";
const onGlassObservable = new Observable();
export const MyObservable = createContext(onGlassObservable);

View File

@@ -0,0 +1,4 @@
import {createContext} from 'react';
let LineID: string | undefined;
export const ThisLineID = createContext(LineID);

22
src/index.css Normal file
View File

@@ -0,0 +1,22 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
.error-page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: white;
}

27
src/index.tsx Normal file
View File

@@ -0,0 +1,27 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { store } from "./store/store";
import { Provider } from "react-redux";
import UpdateData from "./store/UpdateData";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
// <React.StrictMode>
<Provider store={store}>
<App />
<UpdateData />
</Provider>
// </React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

54
src/locales/en-US.json Normal file
View File

@@ -0,0 +1,54 @@
{
"ProductLine": "Line",
"CLICK": "Click",
"TITLE": "Digital Factory Production Monitor",
"alarmInfo": "Abnormal Alarm Info",
"alarmsNumber": "Number of Alarms",
"serialNo": "No.",
"alarmTime": "Time",
"alarmCode": "Code",
"alarmContent": "Content",
"eachLineInputAndOutput": "Input And Output Line Chart",
"ThisDay": "This Day",
"ThisWeek": "This Week",
"ThisMonth": "This Month",
"ThisDayShort": "Day",
"ThisWeekShort": "Week",
"ThisMonthShort": "Month",
"All": "All",
"YieldLineChart": "Yield Line Chart",
"ProductionLine": "Line",
"Output": "Output",
"InputAndOutputSummaryTable": "Input And Output Summary Table",
"InputAndOutputTable": "Input And Output Table",
"ProductionLineName": "Name",
"InputNum": "InputNum",
"OutputNum": "OutputNum",
"OutputArea": "OutputArea",
"PassRate": "PassRate",
"TITLE_E": "Digital Factory Equipment Monitor",
"AbnormalEquipmentAlarm": "Abnormal Equipment Alarm",
"EquipmentOperationMonitoring": "Equipment Operation Monitoring",
"EquipmentTurnover": "Equipment Turnover(Weekly)",
"EquipmentProcessingQuantity": "Equipment Processing Quantity",
"TITLE_Q": "Digital Factory Quality Monitor",
"EquipmentName": "Name",
"AlarmLevel": "Level",
"WorkRate": "WorkRate",
"StopRate": "StopRate",
"DownRate": "DownRate",
"SectionName": "Section",
"DefectSummary": "Defects Summary",
"LineDefectSummary": "Line Defects Summary",
"DefectType": "Defect Type",
"DefectNumber": "Defect Number",
"DefectSummaryLine1": "Line 1 Defects Summary",
"DefectSummaryLine2": "Line 2 Defects Summary",
"DefectSummaryLine3": "Line 3 Defects Summary",
"DefectSummaryLine4": "Line 4 Defects Summary",
"AutoSwitch": "ATS",
"Part1": "Part1",
"Part2": "Part2",
"Part3": "Part3",
"Part4": "Part4"
}

8
src/locales/locales.tsx Normal file
View File

@@ -0,0 +1,8 @@
import en_US from './en-US.json'
import zh_CN from './zh-CN.json'
const locales = {
'en-US': en_US,
'zh-CN': zh_CN,
};
export default locales;

54
src/locales/zh-CN.json Normal file
View File

@@ -0,0 +1,54 @@
{
"ProductLine": "产线",
"CLICK": "点击",
"TITLE": "数智工厂生产监控驾驶舱",
"alarmInfo": "成产异常报警",
"alarmsNumber": "报警总数",
"serialNo": "序号",
"alarmTime": "报警时间",
"alarmCode": "报警编码",
"alarmContent": "报警内容",
"eachLineInputAndOutput": "各产线投入数量和产出数量",
"ThisDay": "当天",
"ThisWeek": "本周",
"ThisMonth": "本月",
"ThisDayShort": "当天",
"ThisWeekShort": "本周",
"ThisMonthShort": "本月",
"All": "全部",
"YieldLineChart": "成品率折线展示",
"ProductionLine": "产线",
"Output": "产出量",
"InputAndOutputSummaryTable": "每产线总投入数量和产出数量",
"InputAndOutputTable": "各产线投入数量和产出数量",
"ProductionLineName": "产线名",
"InputNum": "上片数据量",
"OutputNum": "下片数据量",
"OutputArea": "成品下片总面积",
"PassRate": "合格率",
"TITLE_E": "数智工厂设备运行智能驾驶舱",
"AbnormalEquipmentAlarm": "设备报警异常",
"EquipmentOperationMonitoring": "设备运行监控",
"EquipmentTurnover": "设备稼动率(周)",
"EquipmentProcessingQuantity": "设备加工数量",
"TITLE_Q": "数智工厂质量管理驾驶舱",
"EquipmentName": "设备名称",
"AlarmLevel": "报警级别",
"WorkRate": "工作比率",
"StopRate": "停机比率",
"DownRate": "故障比率",
"SectionName": "工段",
"DefectSummary": "缺陷汇总",
"LineDefectSummary": "产线缺陷汇总",
"DefectType": "缺陷种类",
"DefectNumber": "缺陷数量",
"DefectSummaryLine1": "产线一缺陷汇总",
"DefectSummaryLine2": "产线二缺陷汇总",
"DefectSummaryLine3": "产线三缺陷汇总",
"DefectSummaryLine4": "产线四缺陷汇总",
"AutoSwitch": "自动",
"Part1": "第一部分",
"Part2": "第二部分",
"Part3": "第三部分",
"Part4": "第四部分"
}

1
src/logo.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,45 @@
.dv-scroll-board {
position: relative;
width: 100%;
height: 100%;
color: #fff;
}
.dv-scroll-board .text {
padding: 0 10px;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dv-scroll-board .header {
display: flex;
flex-direction: row;
font-size: 15px;
}
.dv-scroll-board .header .header-item {
transition: all 0.3s;
padding: 0 10px;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dv-scroll-board .rows {
overflow: hidden;
}
.dv-scroll-board .rows .ceil {
padding: 0 10px;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dv-scroll-board .rows .row-item {
display: flex;
font-size: 14px;
transition: all 0.3s;
}
.dv-scroll-board .rows .row-item .index {
border-radius: 3px;
padding: 0px 3px;
}

View File

@@ -0,0 +1,440 @@
import React, { useEffect, useState, useRef, useMemo, forwardRef } from "react";
import useAutoResize from "./use/autoResize";
import { deepMerge } from "./util";
import { deepClone } from "./util/utils";
import { co } from "./util";
import classnames from "classnames";
import "./index.css";
import { current } from "@reduxjs/toolkit";
interface ScrollBoardProps {
config?: object;
onClick?: () => void;
onMouseOver?: () => void;
className?: string;
style?: object;
}
interface TaskType {
end: () => void;
pause: () => void;
resume: () => void;
}
const defaultConfig = {
/**
* @description Board header
* @type {Array<String>}
* @default header = []
* @example header = ['column1', 'column2', 'column3']
*/
header: [],
/**
* @description Board data
* @type {Array<Array>}
* @default data = []
*/
data: [],
/**
* @description Row num
* @type {Number}
* @default rowNum = 5
*/
rowNum: 5,
/**
* @description Header background color
* @type {String}
* @default headerBGC = '#00BAFF'
*/
headerBGC: "#00BAFF",
/**
* @description Odd row background color
* @type {String}
* @default oddRowBGC = '#003B51'
*/
oddRowBGC: "#003B51",
/**
* @description Even row background color
* @type {String}
* @default evenRowBGC = '#003B51'
*/
evenRowBGC: "#0A2732",
/**
* @description Scroll wait time
* @type {Number}
* @default waitTime = 2000
*/
waitTime: 2000,
/**
* @description Header height
* @type {Number}
* @default headerHeight = 35
*/
headerHeight: 35,
/**
* @description Column width
* @type {Array<Number>}
* @default columnWidth = []
*/
columnWidth: [],
/**
* @description Column align
* @type {Array<String>}
* @default align = []
* @example align = ['left', 'center', 'right']
*/
align: [],
/**
* @description Show index
* @type {Boolean}
* @default index = false
*/
index: false,
/**
* @description index Header
* @type {String}
* @default indexHeader = '#'
*/
indexHeader: "#",
/**
* @description Carousel type
* @type {String}
* @default carousel = 'single'
* @example carousel = 'single' | 'page'
*/
carousel: "single",
/**
* @description Pause scroll when mouse hovered
* @type {Boolean}
* @default hoverPause = true
* @example hoverPause = true | false
*/
hoverPause: true,
};
function calcHeaderData({ header, index, indexHeader }: any) {
if (!header.length) {
return [];
}
header = [...header];
if (index) header.unshift(indexHeader);
return header;
}
function calcRows({ data, index, headerBGC, rowNum }: any) {
if (index) {
data = data.map((row: any, i: any) => {
row = [...row];
const indexTag = `<span class="index" style="background-color: ${headerBGC};">${
i + 1
}</span>`;
row.unshift(indexTag);
return row;
});
}
data = data.map((ceils: any, i: any) => ({ ceils, rowIndex: i }));
const rowLength = data.length;
if (rowLength > rowNum && rowLength < 2 * rowNum) {
data = [...data, ...data];
}
return data.map((d: any, i: any) => ({ ...d, scroll: i }));
}
function calcAligns(mergedConfig: any, header: any) {
const columnNum = header.length;
let aligns = new Array(columnNum).fill("left");
const { align } = mergedConfig;
return deepMerge(aligns, align);
}
const ScrollBoard = forwardRef(
(
{ config, onClick, onMouseOver, className, style }: ScrollBoardProps,
ref
) => {
const { width, height, domRef } = useAutoResize(ref);
const [state, setState] = useState({
mergedConfig: {
align: [],
carousel: "",
columnWidth: [],
data: [],
evenRowBGC: "",
header: [],
headerBGC: "",
headerHeight: 35,
hoverPause: true,
index: false,
oddRowBGC: "",
indexHeader: "#",
rowNum: 5,
waitTime: 2000,
},
header: [],
rows: [],
widths: [],
heights: [],
aligns: [],
});
const { mergedConfig, header, rows, widths, heights, aligns } = state;
const stateRef = useRef({
...state,
rowsData: [],
avgHeight: 0,
animationIndex: 0,
});
Object.assign(stateRef.current, state);
function onResize() {
if (!mergedConfig) return;
const widths = calcWidths(mergedConfig, stateRef.current.rowsData);
const heights = calcHeights(mergedConfig, header);
const data: any = { widths, heights };
Object.assign(stateRef.current, data);
setState((state) => ({ ...state, ...data }));
}
function calcData() {
const mergedConfig = deepMerge(
// deepClone(defaultConfig, true),
deepClone(defaultConfig),
config || {}
);
const header = calcHeaderData(mergedConfig);
const rows = calcRows(mergedConfig);
const widths = calcWidths(mergedConfig, stateRef.current.rowsData);
const heights = calcHeights(mergedConfig, header);
const aligns = calcAligns(mergedConfig, header);
const data: any = {
mergedConfig,
header,
rows,
widths,
aligns,
heights,
};
Object.assign(stateRef.current, data, {
rowsData: rows,
animationIndex: 0,
});
setState((state) => ({ ...state, ...data }));
}
function calcWidths({ columnWidth, header }: any, rowsData: any) {
const usedWidth = columnWidth.reduce((all: any, w: any) => all + w, 0);
let columnNum = 0;
if (rowsData[0]) {
columnNum = rowsData[0].ceils.length;
} else if (header.length) {
columnNum = header.length;
}
const avgWidth = (width - usedWidth) / (columnNum - columnWidth.length);
const widths = new Array(columnNum).fill(avgWidth);
return deepMerge(widths, columnWidth);
}
function calcHeights({ headerHeight, rowNum, data }: any, header: any) {
let allHeight = height;
if (header.length) allHeight -= headerHeight;
const avgHeight = allHeight / rowNum;
Object.assign(stateRef.current, { avgHeight });
return new Array(data.length).fill(avgHeight);
}
function* animation(
start = false
): Generator<Promise<void>, void, unknown> {
let {
avgHeight,
animationIndex,
mergedConfig: { waitTime, carousel, rowNum },
rowsData,
} = stateRef.current;
const rowLength = rowsData.length;
if (start) yield new Promise((resolve) => setTimeout(resolve, waitTime));
const animationNum = carousel === "single" ? 1 : rowNum;
let rows: any = rowsData.slice(animationIndex);
rows.push(...rowsData.slice(0, animationIndex));
rows = rows.slice(0, carousel === "page" ? rowNum * 2 : rowNum + 1);
const heights: any = new Array(rowLength).fill(avgHeight);
setState((state) => ({ ...state, rows, heights }));
yield new Promise((resolve) => setTimeout(resolve, 300));
animationIndex += animationNum;
const back = animationIndex - rowLength;
if (back >= 0) animationIndex = back;
const newHeights: any = [...heights];
newHeights.splice(0, animationNum, ...new Array(animationNum).fill(0));
Object.assign(stateRef.current, { animationIndex });
setState((state) => ({ ...state, heights: newHeights }));
}
function emitEvent(handle: any, ri: any, ci: any, row: any, ceil: any) {
const { ceils, rowIndex } = row;
handle && handle({ row: ceils, ceil, rowIndex, columnIndex: ci });
}
function handleHover(
enter: any,
ri?: any,
ci?: any,
row?: any,
ceil?: any
) {
if (enter) emitEvent(onMouseOver, ri, ci, row, ceil);
if (!mergedConfig.hoverPause) return;
if (task.current) {
const { pause, resume } = task.current;
enter
? (function () {
if (pause) pause();
})()
: (function () {
if (resume) resume();
})();
}
}
const getBackgroundColor = (rowIndex: any) =>
mergedConfig[rowIndex % 2 === 0 ? "evenRowBGC" : "oddRowBGC"];
const task = useRef<TaskType>(null);
useEffect(() => {
calcData();
let start = true;
function* loop() {
while (true) {
yield* animation(start);
start = false;
const { waitTime } = stateRef.current.mergedConfig;
yield new Promise((resolve) => setTimeout(resolve, waitTime - 300));
}
}
const {
mergedConfig: { rowNum },
rows: rowsData,
} = stateRef.current;
const rowLength = rowsData.length;
if (rowNum >= rowLength) return;
// @ts-ignore
task.current = co(loop);
if (task.current) {
return task.current.end;
}
}, [config, domRef.current]);
useEffect(onResize, [width, height, domRef.current]);
const classNames = useMemo(
() => classnames("dv-scroll-board", className),
[className]
);
return (
<div className={classNames} style={style} ref={domRef}>
{!!header.length && !!mergedConfig && (
<div
className="header"
style={{ backgroundColor: `${mergedConfig.headerBGC}` }}
>
{header.map((headerItem, i) => (
<div
className="header-item"
key={`${headerItem}-${i}`}
style={{
height: `${mergedConfig.headerHeight}px`,
lineHeight: `${mergedConfig.headerHeight}px`,
width: `${widths[i]}px`,
}}
// @ts-ignore
align={aligns[i]}
dangerouslySetInnerHTML={{ __html: headerItem }}
/>
))}
</div>
)}
{!!mergedConfig && (
<div
className="rows"
style={{
height: `${
height - (header.length ? mergedConfig.headerHeight : 0)
}px`,
}}
>
{rows.map((row: any, ri) => (
<div
className="row-item"
key={`${row.toString()}-${row.scroll}`}
style={{
height: `${heights[ri]}px`,
lineHeight: `${heights[ri]}px`,
backgroundColor: `${getBackgroundColor(row.rowIndex)}`,
}}
>
{row.ceils.map((ceil: any, ci: any) => (
<div
className="ceil"
key={`${ceil}-${ri}-${ci}`}
style={{ width: `${widths[ci]}px` }}
// @ts-ignore
align={aligns[ci]}
dangerouslySetInnerHTML={{ __html: ceil }}
onClick={() => emitEvent(onClick, ri, ci, row, ceil)}
onMouseEnter={() => handleHover(true, ri, ci, row, ceil)}
onMouseLeave={() => handleHover(false)}
/>
))}
</div>
))}
</div>
)}
</div>
);
}
);
export default ScrollBoard;

View File

@@ -0,0 +1,57 @@
import {
useState,
useCallback,
useEffect,
useRef,
useImperativeHandle,
} from "react";
import { debounce, observerDomResize } from "../util/index";
export default function useAutoResize(ref: any) {
const [state, setState] = useState({ width: 0, height: 0 });
const domRef = useRef(null);
const setWH = useCallback(() => {
const { clientWidth, clientHeight } = domRef.current || {
clientWidth: 0,
clientHeight: 0,
};
setState({ width: clientWidth, height: clientHeight });
if (!domRef.current) {
console.warn(
"DataV: Failed to get dom node, component rendering may be abnormal!"
);
} else if (!clientWidth || !clientHeight) {
console.warn(
"DataV: Component width or height is 0px, rendering abnormality may occur!"
);
}
}, []);
useImperativeHandle(ref, () => ({ setWH }), []);
useEffect(() => {
const debounceSetWHFun = debounce(setWH, 100);
debounceSetWHFun();
const domObserver = observerDomResize(domRef.current, debounceSetWHFun);
window.addEventListener("resize", debounceSetWHFun);
return () => {
window.removeEventListener("resize", debounceSetWHFun);
if (!domObserver) {
return;
}
domObserver.disconnect();
domObserver.takeRecords();
};
}, []);
return { ...state, domRef, setWH };
}

View File

@@ -0,0 +1,57 @@
import {
useState,
useCallback,
useEffect,
useRef,
useImperativeHandle,
} from "react";
import { debounce, observerDomResize } from "../util/index";
export default function useAutoResize(ref) {
const [state, setState] = useState({ width: 0, height: 0 });
const domRef = useRef(null);
const setWH = useCallback(() => {
const { clientWidth, clientHeight } = domRef.current || {
clientWidth: 0,
clientHeight: 0,
};
setState({ width: clientWidth, height: clientHeight });
if (!domRef.current) {
console.warn(
"DataV: Failed to get dom node, component rendering may be abnormal!"
);
} else if (!clientWidth || !clientHeight) {
console.warn(
"DataV: Component width or height is 0px, rendering abnormality may occur!"
);
}
}, []);
useImperativeHandle(ref, () => ({ setWH }), []);
useEffect(() => {
const debounceSetWHFun = debounce(setWH, 100);
debounceSetWHFun();
const domObserver = observerDomResize(domRef.current, debounceSetWHFun);
window.addEventListener("resize", debounceSetWHFun);
return () => {
window.removeEventListener("resize", debounceSetWHFun);
if (!domObserver) {
return;
}
domObserver.disconnect();
domObserver.takeRecords();
};
}, []);
return { ...state, domRef, setWH };
}

View File

@@ -0,0 +1,114 @@
export function co(gen: any) {
let destroyed = false;
// 处理 return 之后 resume 的问题
let stop = false;
let val: any = null;
if (typeof gen === "function") gen = gen();
if (!gen || typeof gen.next !== "function") return () => ({});
Promise.resolve().then(() => {
destroyed || next(gen.next());
});
return {
end() {
destroyed = true;
Promise.resolve().then(() => {
gen.return();
gen = null;
});
},
pause() {
if (!destroyed) {
stop = true;
}
},
resume() {
const oldVal = val;
if (!destroyed && stop) {
stop = false;
Promise.resolve(val).then(function () {
if (!destroyed && !stop && oldVal === val) {
next(gen.next());
}
});
}
},
};
function next(ret: any) {
if (ret.done) return ret.value;
val = ret.value;
return Promise.resolve(ret.value).then(() => {
!destroyed && !stop && next(gen.next());
});
}
}
/**
* @description 将函数转成防抖动函数
* @param {Function} 需要转成防抖动函数的函数
* @param {number} 延迟时间(毫秒数)
* @param {boolean} 是否执行第一次
* @return {undefined} 无返回值
*/
export function debounce(fn: any, delay = 600, runFirstFn = true) {
let timer: any = null;
return function (this: any, ...rest: any) {
// 清除定时器
clearTimeout(timer);
if (runFirstFn) {
fn.apply(this, rest);
runFirstFn = false;
return;
}
// 设置定时器
timer = setTimeout(fn.bind(this, ...rest), delay);
};
}
export function observerDomResize(dom: any, callback: any) {
const MutationObserver = window.MutationObserver;
// window.WebKitMutationObserver ||
// window.MozMutationObserver;
const observer = new MutationObserver(callback);
observer.observe(dom, {
attributes: true,
attributeFilter: ["style"],
attributeOldValue: true,
});
return observer;
}
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _util = require("./../util/utils");
export function deepMerge(target: any, merged: any) {
for (var key in merged) {
if (target[key] && _typeof2["default"](target[key]) === "object") {
deepMerge(target[key], merged[key]);
continue;
}
if (_typeof2["default"](merged[key]) === "object") {
target[key] = _util.deepClone(merged[key], true);
continue;
}
target[key] = merged[key];
}
return target;
}

View File

@@ -0,0 +1,32 @@
/**
* @description Clone an object or array
* @param {Object|Array} object Cloned object
* @param {Boolean} recursion Whether to use recursive cloning
* @return {Object|Array} Clone object
*/
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
export function deepClone(object: any) {
var recursion =
arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!object) return object;
var parse = JSON.parse,
stringify = JSON.stringify;
if (!recursion) return parse(stringify(object));
var clonedObj: any = object instanceof Array ? [] : {};
if (object && _typeof2["default"](object) === "object") {
for (var key in object) {
if (object.hasOwnProperty(key)) {
if (object[key] && _typeof2["default"](object[key]) === "object") {
// clonedObj[key] = deepClone(object[key], true);
clonedObj[key] = deepClone(object[key]);
} else {
clonedObj[key] = object[key];
}
}
}
}
return clonedObj;
}

View File

@@ -0,0 +1,24 @@
.top_block {
width: 3840px;
height: 123px;
background-image: url(/public/image/topTitle.png);
background-size:3840px;
position: relative;
font-weight: 500;
font-size: 26px;
color: #51f0ff;
}
/* .top_left__title {
position: absolute;
left: 2334px;
top: 98px;
} */
.top_right__title {
position: absolute;
right: 955px;
top: 69px;
}
.time {
display: inline-block;
margin-left: 45px;
}

View File

@@ -0,0 +1,30 @@
import "./index.css";
import moment from "moment";
import "moment/locale/zh-cn";
import { useState, useEffect } from "react";
function TopTitle() {
const [date, setDate] = useState<string>(moment().format("YYYY.MM.DD"));
const [week, setWeek] = useState<string>(
moment().locale("zh-cn").format("dddd")
);
const [time, setTime] = useState<string>(moment().format("HH:mm:ss"));
useEffect(() => {
const timer = setInterval(() => {
setDate(moment().format("YYYY.MM.DD"));
setWeek(moment().locale("zh-cn").format("dddd"));
setTime(moment().format("HH:mm:ss"));
}, 1000);
return () => clearInterval(timer);
}, []);
return (
<div className="top_block">
{/* <div className="top_left__title">设计单位:中建材智能自动化研究院</div> */}
<div className="top_right__title">
{date}
{week} <span className="time">{time}</span>
</div>
</div>
);
}
export default TopTitle;

12
src/page/ErrorPage.tsx Normal file
View File

@@ -0,0 +1,12 @@
import "./../index.css";
function ErrorPage() {
return (
<div className="error-page">
<h1>Oops!</h1>
<p>Sorry, an unexpected error has occurred.</p>
</div>
);
}
export default ErrorPage;

View File

@@ -0,0 +1,23 @@
import MybabylonJS from "../../../babylonjs/MybabylonJS";
import Production from "./Production"
import DefectClass from "./DefectClass"
import Power from "./Power"
import TempAndHumidity from "./TempAndHumidity"
import Produce from "./Produce"
function Center() {
return (
<div>
<div className="center_up">
<MybabylonJS />
</div>
<div className="center-down flex-row">
<Production/>
<DefectClass />
<Power />
<TempAndHumidity/>
<Produce />
</div>
</div>
);
}
export default Center;

View File

@@ -0,0 +1,155 @@
function getCoordinates(startArc: number, endArc: number) {
const posi = [
Math.sin(startArc),
-Math.cos(startArc),
Math.sin(endArc),
-Math.cos(endArc),
];
const dx = posi[2] - posi[0];
const dy = posi[3] - posi[1];
return getLocation(dx, dy);
}
function getLocation(dx: number, dy: number) {
const tanV = dx / dy;
const directSign = Math.abs(tanV) < 1;
const t = directSign ? tanV : 1 / tanV;
const sign1 = t > 0 ? 1 : -1;
const sign2 = dx > 0 ? 1 : -1;
const sign = directSign ? sign1 * sign2 : sign2;
const group1 = [0.5 - (sign * t) / 2, 0.5 + (sign * t) / 2];
const group2 = sign > 0 ? [0, 1] : [1, 0];
const group = [...group1, ...group2];
const keys = directSign ? ["x", "x2", "y", "y2"] : ["y", "y2", "x", "x2"];
let res: { [key: string]: any } = {};
keys.forEach((k, idx) => {
res[k] = group[idx];
});
return res;
}
let rangArrValue: any[] = [];
let dataList: any = [];
let totalValue = 0;
const colors = [
"rgb(39, 96, 255)",
"rgb(91, 155, 255)",
"rgb(255, 209, 96)",
"rgb(129, 103, 246)",
"rgb(153, 214, 108)",
"rgb(255, 138, 64)",
"rgb(18, 255, 245)",
];
const getPersonnelList = async (dataProps: any) => {
totalValue = dataProps.reduce(
(total: any, value: any) => total + value.value,
0
);
let cacheNum = 0;
for (let i = 0; i < dataProps.length; i++) {
const endNum = cacheNum + dataProps[i].value;
rangArrValue.push([cacheNum, endNum]);
cacheNum = endNum;
}
const angleArr = rangArrValue.map((arr) =>
arr.map((num: any) => (num / totalValue) * Math.PI * 2)
);
dataList = dataProps.map((item: any, index: any) => {
const range = getCoordinates(angleArr[index][0], angleArr[index][1]);
const startColor = colors[index];
const color = {
type: "linear",
x: range.x,
x2: range.x2,
y: range.y,
y2: range.y2,
colorStops: [
{
offset: 0,
color: startColor, // 起始颜色
},
{
offset: 1,
color: `${startColor.substring(0, startColor.length - 1)}, 0)`, // 终点颜色
},
],
global: false,
};
return {
name: item.name,
value: item.value,
// itemStyle: {
// color: color,
// },
};
});
};
export default function getOptions(dataProps: any) {
if (dataProps.length === 0) return null;
getPersonnelList(dataProps);
const colors = [
"rgb(39, 96, 255)",
"rgb(91, 155, 255)",
"rgb(255, 209, 96)",
"rgb(129, 103, 246)",
"rgb(153, 214, 108)",
"rgb(255, 138, 64)",
"rgb(18, 255, 245)",
];
return {
color: colors,
tooltip: {
trigger: "item",
className: "handan-chart-tooltip",
},
graphic: [
{
type: "text",
left: "center",
top: "44%",
style: {
text: totalValue,
fill: "#fff",
width: 150,
height: 48,
fontSize: 31,
fontWeight: 400,
},
},
{
type: "text",
left: "center",
top: "60%",
style: {
text: "总数",
fill: "rgba(255, 255, 255, 0.70)",
width: 32,
height: 16,
fontSize: 16,
fontWeight: 400,
},
},
],
series: [
{
name: "产线缺陷分类",
type: "pie",
radius: ["60%", "80%"],
center: ["50%", "50%"],
label: {
formatter: "{d}%",
color: "#fff",
// color: function (params: any) {
// // 根据数据项的索引从颜色数组中选取颜色作为标签颜色
// var colorIndex = params.dataIndex;
// return colors[colorIndex];
// },
},
data: dataList,
},
],
};
}

View File

@@ -0,0 +1,75 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectDefectsClassDEFECT } from "../../../../store/HomePageSlice";
import getOptions from "./chart.config";
function DefectClass() {
const data = useAppSelector(selectDefectsClassDEFECT);
// console.log("33333333333333产线缺陷分类", data);
const colors = [
"rgb(39, 96, 255)",
"rgb(91, 155, 255)",
"rgb(255, 209, 96)",
"rgb(129, 103, 246)",
"rgb(153, 214, 108)",
"rgb(255, 138, 64)",
"rgb(18, 255, 245)",
];
let keys = [];
let dataProps: any[] = [];
if (Object.keys(data).length !== 0) {
keys = Object.keys(data);
keys.map((item: any, index) => {
dataProps.push({ name: item, value: data[item], color: colors[index] });
});
}
//@ts-ignore
const options = getOptions(dataProps);
return (
<div className="left_right_up">
<TitleBox title="left_right_up" />
{options && (
<div style={{ width: "240px", height: "220px", marginTop: "10px" }}>
{<ReactECharts option={options} style={{ height: "100%" }} />}
</div>
)}
<div className="right_up_legend">
{dataProps.map((item, index) => {
return (
<div className="item" key={index}>
<span
className="dot"
style={{ backgroundColor: item.color }}
></span>
{item.name}
<span
style={{
color: "rgba(255, 255, 255, 0.70)",
display: "inline-block",
marginLeft: "10px",
}}
>
{item.value}
</span>
</div>
);
})}
</div>
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default DefectClass;

View File

@@ -0,0 +1,120 @@
import * as echarts from "echarts";
export default function getOptions(xData: any, outputList: any, rateList: any) {
if (xData.length === 0 || outputList.length === 0 || rateList.length === 0)
return null;
return {
grid: { top: 30, right: 48, bottom: 26, left: 48 },
legend: {
show: false,
},
xAxis: {
type: "category",
data: xData,
axisLabel: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
interval: 0,
},
axisTick: { show: false },
axisLine: {
lineStyle: {
width: 2,
color: "rgba(69, 97, 174, 1)",
},
},
},
yAxis: [
{
name: "单位/片",
nameTextStyle: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
align: "right",
},
type: "value",
alignTicks: true,
axisLabel: {
color: "rgba(223, 241, 254, 0.65)",
fontSize: 12,
formatter: "{value}",
},
axisLine: {
show: true,
lineStyle: {
width: 2,
color: "rgba(69, 97, 174, 1)",
},
},
splitLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
{
name: "单位/%",
nameTextStyle: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
align: "center",
},
type: "value",
axisLabel: {
color: "rgba(223, 241, 254, 0.65)",
fontSize: 12,
formatter: "{value}",
},
axisLine: {
show: true,
lineStyle: {
width: 2,
color: "rgba(69, 97, 174, 1)",
},
},
splitLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
],
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
className: "handan-chart-tooltip",
},
series: [
{
data: outputList,
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#34C1FF" },
{ offset: 1, color: "#3449FF" },
]),
},
},
{
data: rateList,
color: "rgba(18, 255, 245, 1)",
type: "line",
yAxisIndex: 1,
symbol: "circle",
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(18, 255, 245, 0.6)" },
{ offset: 0.4, color: "rgba(18, 255, 245, 0.1)" },
{ offset: 0.8, color: "rgba(18, 255, 245, 0)" },
{ offset: 1, color: "rgba(18, 255, 245, 0)" },
]),
},
},
],
};
}

View File

@@ -0,0 +1,54 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectPowerDistributionpow } from "../../../../store/HomePageSlice";
import getOptions from "./chart.config";
function Power() {
const data = useAppSelector(selectPowerDistributionpow);
console.log("66666666666FUCH功率分布", data);
let xData: any = [];
let outputList: Number[] = [];
let rateList: Number[] = [];
if (data.length !== 0) {
data.map((item: any) => {
xData.push(item.scope)
outputList.push(item.value);
rateList.push(item.proportion);
});
}else{
xData = [];
outputList = [];
rateList = [];
}
const options = getOptions(xData, outputList, rateList);
return (
<div className="right_up">
<TitleBox title="right_left_up" />
<div className="legend_right">
<span className="dot2"></span>
<span></span>
<span className="dot1"></span>
<span></span>
</div>
{options && (
<div style={{ height: "230px", marginTop: "10px" }}>
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default Power;

View File

@@ -0,0 +1,112 @@
import * as echarts from "echarts";
export default function getOptions(
xData: any,
outputList: any,
powerList: any
) {
if (xData.length === 0 || outputList.length === 0 || powerList.length === 0)
return null;
return {
grid: { top: 10, right: 48, bottom: 26, left: 48 },
legend: {
show: false,
},
xAxis: {
type: "category",
data: xData,
axisLabel: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
interval: 0,
},
axisTick: { show: false },
axisLine: {
lineStyle: {
width: 2,
color: "rgba(69, 97, 174, 1)",
},
},
},
yAxis: [
{
type: "value",
alignTicks: true,
axisLabel: {
color: "rgba(223, 241, 254, 0.65)",
fontSize: 12,
formatter: "{value}",
},
axisLine: {
show: true,
lineStyle: {
width: 2,
color: "rgba(69, 97, 174, 1)",
},
},
splitLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
{
type: "value",
axisLabel: {
color: "rgba(223, 241, 254, 0.65)",
fontSize: 12,
formatter: "{value}",
},
axisLine: {
show: true,
lineStyle: {
width: 2,
color: "rgba(69, 97, 174, 1)",
},
},
splitLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
],
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
className: "handan-chart-tooltip",
},
series: [
{
data: outputList,
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#34C1FF" },
{ offset: 1, color: "#3449FF" },
]),
},
},
{
data: powerList,
color: "rgba(18, 255, 245, 1)",
type: "line",
yAxisIndex: 1,
symbol: "circle",
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(18, 255, 245, 0.6)" },
{ offset: 0.4, color: "rgba(18, 255, 245, 0.1)" },
{ offset: 0.8, color: "rgba(18, 255, 245, 0)" },
{ offset: 1, color: "rgba(18, 255, 245, 0)" },
]),
},
},
],
};
}

View File

@@ -0,0 +1,78 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectOutputFUCH } from "../../../../store/HomePageSlice";
import getOptions from "./chart.config";
function Produce() {
const data = useAppSelector(selectOutputFUCH);
// console.log("7777777777777FUCH产出", data);
let year = new Date().getFullYear();
let xData: any = [];
let outputList: Number[] = [];
let powerList: Number[] = [];
let totalOutput;
let totalPower;
if (Object.keys(data).length !== 0) {
xData = Object.keys(data.monthOutput);
xData.map((item: any) => {
outputList.push(data.monthOutput[item]);
powerList.push(data.monthPower[item]);
});
totalOutput = data.totalOutput;
totalPower = data.totalPower;
}
const options = getOptions(xData, outputList, powerList);
return (
<div className="produce">
<TitleBox title="right_left_down" />
<div className="legend_right">
<span className="dot2"></span>
<span></span>
<span className="dot1"></span>
<span></span>
</div>
<div
style={{ marginLeft: "-5px", marginTop: "10px", marginBottom: "10px" }}
>
<div className="top_unit" style={{marginRight: "5px"}}>/</div>
<div className="top_box" style={{ marginRight: "5px" }}>
<span className="t1">{year}</span>
<span>
{totalOutput ? totalOutput : totalOutput === 0 ? totalOutput : "-"}
</span>
</div>
<div className="top_box">
<span className="t1">{year}</span>
<span>
{totalPower
? totalPower.toFixed(2)
: totalPower === 0
? totalPower
: "-"}
</span>
</div>
<div className="top_unit" style={{marginLeft: "5px"}}>/MW</div>
</div>
{options && (
<div style={{ height: "190px" }}>
{<ReactECharts option={options} style={{ height: "100%" }} />}
</div>
)}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default Produce;

View File

@@ -0,0 +1,82 @@
import * as echarts from "echarts";
export default function getOptions(xData: any, outputList: any) {
const colors = ["#1A99FF", "#FFB70C", "#C69DFF", "#50F4E3", "#E02094"];
if (xData.length === 0 || outputList.length === 0) return null;
return {
color: colors,
grid: { top: 30, right: 20, bottom: 40, left: 48 },
legend: {
show: false,
},
xAxis: {
type: "category",
data: xData,
axisLabel: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 10,
interval: 0,
rotate:25,
},
axisTick: { show: false },
axisLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
yAxis: {
name: "单位/片",
nameTextStyle: {
color: "rgba(223, 241, 254, 0.65)",
fontSize: 12,
align:'right'
},
type: "value",
axisLabel: {
color: "rgba(223, 241, 254, 0.65)",
fontSize: 12,
formatter: "{value}",
},
axisLine: {
show: true,
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
splitLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
className: "handan-chart-tooltip",
},
series: [
{
data: outputList,
type: "bar",
barWidth: 16,
label: {
show: true,
fontSize: 12,
color: "#9CD4FF",
position: "top",
},
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#34C1FF" },
{ offset: 1, color: "#3449FF" },
]),
},
},
],
};
}

View File

@@ -0,0 +1,42 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectProductionBarChart } from "../../../../store/HomePageSlice";
import getOptions from "./chart.config";
function Production() {
const data = useAppSelector(selectProductionBarChart);
// console.log("44444444产量柱状图", data);
let xData: any = [];
let outputList: Number[] = [];
if (Object.keys(data).length !== 0) {
xData = Object.keys(data);
xData.map((item: any) => {
outputList.push(data[item]);
});
}
const options = getOptions(xData, outputList);
return (
<div className="left_right_center">
<TitleBox title="left_right_center" />
{options && (
<div style={{ height: "230px", marginTop: "10px" }}>
{<ReactECharts option={options} style={{ height: "100%" }} />}
</div>
)}
{!options && (
<p
style={{
color: "#ccc",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default Production;

View File

@@ -0,0 +1,69 @@
import Rectangle2 from "../../Component/Rectangle2";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectTempAndHumidityTEMP } from "../../../../store/HomePageSlice";
function TempAndHumidity() {
const data = useAppSelector(selectTempAndHumidityTEMP);
// console.log("55555555镀膜与活化温度", data);
const list1 = [
{ name: "镀膜间温度1", ename: "acoatTemp1", value: "" },
{ name: "镀膜间温度2", ename: "acoatTemp2", value: "" },
{ name: "活化间温度1", ename: "afurTemp1", value: "" },
{ name: "活化间温度2", ename: "afurTemp2", value: "" },
{ name: "层压间温度1", ename: "lamaTemp1", value: "" },
];
const list2 = [
{ name: "镀膜间湿度1", ename: "acoatHum1", value: "" },
{ name: "镀膜间湿度2", ename: "acoatHum2", value: "" },
{ name: "活化间湿度1", ename: "afurHum1", value: "" },
{ name: "活化间湿度2", ename: "afurHum2", value: "" },
{ name: "层压间湿度1", ename: "lamaHum1", value: "" },
];
list1.map((item, index) => {
item.value = data[item.ename] ? data[item.ename] + "℃":'-';
});
list2.map((item, index) => {
item.value = data[item.ename] ? data[item.ename] + "%RH":'-';
});
return (
<div className="left_right_down">
<TitleBox title="left_right_down" />
{Object.keys(data).length !== 0 && (
<div className="box flex-row">
<div className="flex-col" style={{ marginRight: "8px" }}>
{list1.map((item, index) => {
return (
<div className="item" key={index}>
<Rectangle2 name={item.name} value={item.value} />
</div>
);
})}
</div>
<div className="flex-col">
{list2.map((item, index) => {
return (
<div className="item" key={index}>
<Rectangle2 name={item.name} value={item.value} />
</div>
);
})}
</div>
</div>
)}
{Object.keys(data).length === 0 && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default TempAndHumidity;

View File

@@ -0,0 +1,26 @@
.rectangle_box {
width: 246px;
height: 70px;
line-height: 70px;
background: url(../../../../../public/image/homePage/b1.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
font-family: PingFangSC, PingFang SC;
color: rgba(255, 255, 255, 0.9);
font-size: 15px;
letter-spacing: 1px;
font-weight: 500;
}
.rectangle_box img {
width: 32px;
height: 34px;
position: relative;
top: 10px;
margin: 0 8px 0 20px;
}
.rectangle_box .title {
display: inline-block;
width: 120px;
padding-right: 5px;
text-align: right;
}

View File

@@ -0,0 +1,16 @@
import "./index.css";
interface RectangleProps {
name: string;
rate: string;
icon: number;
}
function Rectangle(props: RectangleProps) {
return (
<div className="rectangle_box">
<img src={require(`./../../../../assets/image/icon${props.icon}.png`)} />
<span className="title">{props.name}</span>
<span>{props.rate}</span>
</div>
);
}
export default Rectangle;

View File

@@ -0,0 +1,21 @@
.rectangle2 {
width: 245px;
height: 41px;
line-height: 41px;
background: url(../../../../../public/image/homePage/b2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
font-family: PingFangSC, PingFang SC;
color: rgba(255, 255, 255, 0.8);
font-size: 16px;
letter-spacing: 2px;
font-weight: 500;
}
.rectangle2_title {
display: inline-block;
width: 160px;
text-align: right;
}
.rectangle2_content {
display: inline-block;
}

View File

@@ -0,0 +1,16 @@
import "./index.css";
interface Props {
name: string;
value: string;
}
function Rectangle2(props: Props) {
return (
<div className="rectangle2">
<div>
<span className="rectangle2_title">{props.name}</span>
<span className="rectangle2_content">{props.value}</span>
</div>
</div>
);
}
export default Rectangle2;

View File

@@ -0,0 +1,15 @@
.title_box {
font-family: PingFangSC, PingFang SC;
font-size: 24px;
color: #fff;
font-weight: 500;
letter-spacing: 1px;
}
.title_box img {
width: 24px;
height: 25px;
vertical-align: bottom;
margin-right: 8px;
position: relative;
top: -2px;
}

View File

@@ -0,0 +1,70 @@
import t1 from "./../../../../assets/image/t1.png";
import t2 from "./../../../../assets/image/t2.png";
import t3 from "./../../../../assets/image/t3.png";
import t4 from "./../../../../assets/image/t4.png";
import t5 from "./../../../../assets/image/t5.png";
import t6 from "./../../../../assets/image/t6.png";
import t7 from "./../../../../assets/image/t7.png";
import t8 from "./../../../../assets/image/t8.png";
import "./index.css";
interface titleProps {
title: string;
}
function TitleBox(props: titleProps) {
const filteredTitles = () => {
switch (props.title) {
case "left_left_up":
return {
img: t1,
title: "设备稼动率",
};
case "left_left_down":
return {
img: t2,
title: "封装工单完成情况",
};
case "left_right_up":
return {
img: t3,
title: "产线缺陷分类(当日)",
};
case "left_right_center":
return {
img: t4,
title: "产量柱状图(当日)",
};
case "left_right_down":
return {
img: t5,
title: "温湿度",
};
case "right_left_up":
return {
img: t6,
title: "FUCH功率分布",
};
case "right_left_down":
return {
img: t4,
title: "FUCH产出",
};
case "right_right_up":
return {
img: t7,
title: "前中后段良率",
};
default:
return {
img: t8,
title: "下片分档信息",
};
}
};
return (
<div className="title_box">
<img src={filteredTitles().img} alt="title" />
<span>{filteredTitles().title}</span>
</div>
);
}
export default TitleBox;

View File

@@ -0,0 +1,75 @@
import TitleBox from "../../Component/TitleBox";
import ScrollBoard from "../../../Component/ScrollBoard";
import { useAppSelector } from "../../../../store/hooks";
import { selectOrderCompletionWO } from "../../../../store/HomePageSlice";
function LeftDown() {
const data = useAppSelector(selectOrderCompletionWO);
// console.log("222222封装工单完成情况+", data);
const config = {
header: ["工单号", "目标产量", "实际投入", "实际产出", "完成度"],
headerHeight: 40,
rowNum: 10,
headerBGC: "rgba(4, 74, 132, 0.2)",
oddRowBGC: "rgba(4, 74, 132, 0.2)",
evenRowBGC: "rgba(11, 84, 153, 0.36)",
columnWidth: [140, 100, 100, 100, 90],
data: [],
};
let arr: any = [];
data &&
data.map((item: any, index: any) => {
let arrInner = [];
arrInner.push(
item.workOrderNo ? item.workOrderNo : "-",
item.planQuantity
? item.planQuantity
: item.planQuantity === 0
? item.planQuantity
: "-",
item.actualPutIn
? item.actualPutIn
: item.actualPutIn === 0
? item.actualPutIn
: "-",
item.actualQuantity
? item.actualQuantity
: item.actualQuantity === 0
? item.actualQuantity
: "-",
item.completeness
? item.completeness + "%"
: item.completeness === 0
? item.completeness + "%"
: "-"
);
arr.push(arrInner);
});
config.data = arr;
return (
<div className="left_left_down">
<TitleBox title="left_left_down" />
{data.length !== 0 && (
<div style={{ marginTop: "15px" }}>
<ScrollBoard
config={config}
style={{ width: "500px", height: "440px" }}
/>
</div>
)}
{data.length === 0 && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default LeftDown;

View File

@@ -0,0 +1,82 @@
import Rectangle from "../../Component/Rectangle";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectEqRateAVA } from "../../../../store/HomePageSlice";
function LeftUp() {
const data = useAppSelector(selectEqRateAVA);
console.log("111111111设备稼动率", data);
const eqRate1 = [
{ icon: 1, name: "", rate: "" },
{ icon: 2, name: "", rate: "" },
{ icon: 3, name: "", rate: "" },
{ icon: 4, name: "", rate: "" },
];
const eqRate2 = [
{ icon: 5, name: "", rate: "" },
{ icon: 6, name: "", rate: "" },
{ icon: 7, name: "", rate: "" },
{ icon: 8, name: "", rate: "" },
];
let keys = Object.keys(data);
eqRate1.map((item, index) => {
item.name = keys[index];
})
eqRate2.map((item, index) => {
item.name = keys[index + 4];
})
eqRate1.map((item, index) => {
item.rate = data[item.name] + "%";
});
eqRate2.map((item, index) => {
item.rate = data[item.name] + "%";
});
return (
<div className="left_left_up">
<TitleBox title="left_left_up" />
{Object.keys(data).length !== 0 && (
<div className="box flex-row">
<div className="flex-col" style={{ marginRight: "8px" }}>
{eqRate1.map((item, index) => {
return (
<div className="item" key={index}>
<Rectangle
name={item.name}
rate={item.rate}
icon={item.icon}
/>
</div>
);
})}
</div>
<div className="item flex-col">
{eqRate2.map((item, index) => {
return (
<div className="item" key={index}>
<Rectangle
name={item.name}
rate={item.rate}
icon={item.icon}
/>
</div>
);
})}
</div>
</div>
)}
{Object.keys(data).length === 0 && (
<p
style={{
color: "#ccc",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default LeftUp;

View File

@@ -0,0 +1,11 @@
import LeftUp from "./LeftUp";
import LeftDown from "./LeftDown";
function Left() {
return (
<div className="left_part">
<LeftUp />
<LeftDown />
</div>
);
}
export default Left;

View File

@@ -0,0 +1,60 @@
import TitleBox from "../../Component/TitleBox";
import ScrollBoard from "../../../Component/ScrollBoard";
import { useAppSelector } from "../../../../store/hooks";
import { selectOutputMsgBOX } from "../../../../store/HomePageSlice";
function RightDown() {
const data = useAppSelector(selectOutputMsgBOX);
// console.log("9999999999999下片分档信息", data);
const config = {
header: ["序号", "功率/W", "当日产量", "当前装箱号", "装箱数量"],
headerHeight: 49,
rowNum: 8,
align: ["center", "left", "left", "left", "left"],
headerBGC: "rgba(4, 74, 132, 0.2)",
oddRowBGC: "rgba(4, 74, 132, 0.2)",
evenRowBGC: "rgba(11, 84, 153, 0.36)",
columnWidth: [70, 100, 117, 138, 100],
data: [],
};
let arr: any = [];
data &&
data.map((item: any, index: any) => {
let arrInner = [];
arrInner.push(
index + 1,
item.power ? item.power : item.power === 0 ? item.power : "-",
item.output ? item.output : item.output === 0 ? item.output : "-",
item.boxNo ? item.boxNo : item.boxNo === 0 ? item.boxNo : "-",
item.boxNum ? item.boxNum : item.boxNum === 0 ? item.boxNum : "-"
);
arr.push(arrInner);
});
config.data = arr;
return (
<div className="right_down">
<TitleBox title="right_right_down" />
{data.length !== 0 && (
<div style={{ marginTop: "15px" }}>
<ScrollBoard
config={config}
style={{ width: "498px", height: "435px" }}
/>
</div>
)}
{data.length === 0 && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default RightDown;

View File

@@ -0,0 +1,72 @@
import TitleBox from "../../Component/TitleBox";
import chartA from "./../../../../assets/image/chartA.png";
import chartB from "./../../../../assets/image/chartB.png";
import { useAppSelector } from "../../../../store/hooks";
import { selectYield } from "../../../../store/HomePageSlice";
function RightUp() {
const data = useAppSelector(selectYield);
// console.log("888888888前中后段良率", data);
let before = data?.before || 100;
let middle = data?.middle || 100;
let after = data?.after || 100;
return (
<div className="right_right_up">
<TitleBox title="right_right_up" />
<div className="chart1">
<img
src={chartA}
alt=""
width={"121px"}
height={30 + before + "px"}
style={{ position: "relative", top: "4px" }}
/>
<img src={chartB} alt="" width={"121px"} height={"130px"} />
<span
className="num_percentage"
style={{ top: -61 + before * 0.2 + "px" }}
>
{before}
<span className="small">%</span>
</span>
<span className="title"></span>
</div>
<div className="chart2">
<img
src={chartA}
alt=""
width={"121px"}
height={30 + middle + "px"}
style={{ position: "relative", top: "4px" }}
/>
<img src={chartB} alt="" width={"121px"} height={"130px"} />
<span
className="num_percentage"
style={{ top: -61 + middle * 0.2 + "px" }}
>
{middle}
<span className="small">%</span>
</span>
<span className="title"></span>
</div>
<div className="chart3">
<img
src={chartA}
alt=""
width={"121px"}
height={30 + after + "px"}
style={{ position: "relative", top: "4px" }}
/>
<img src={chartB} alt="" width={"121px"} height={"130px"} />
<span
className="num_percentage"
style={{ top: -61 + after * 0.2 + "px" }}
>
{after}
<span className="small">%</span>
</span>
<span className="title"></span>
</div>
</div>
);
}
export default RightUp;

View File

@@ -0,0 +1,11 @@
import RightUp from "./RightUp";
import RightDown from "./RightDown";
function Right() {
return (
<div className="right_part">
<RightUp />
<RightDown />
</div>
);
}
export default Right;

304
src/page/HomePage/index.css Normal file
View File

@@ -0,0 +1,304 @@
@font-face {
font-family: "DINCondensed";
src: url("../../assets/fonts/DINCondensed.ttf");
font-weight: normal;
font-style: normal;
}
.left_part {
width: 542px;
position: absolute;
top: 110px;
margin-left: 39px;
}
.left_part .left_inner {
width: 626px;
margin-right: 22px;
}
.left_left_up {
width: 542px;
height: 399px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L1.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-bottom: 22px;
padding: 20px 0 0 21px;
}
.left_left_up .box {
margin-top: 15px;
}
.left_left_up .box .item {
margin-bottom: 10px;
}
.left_left_down {
width: 542px;
height: 525px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 20px 0 0 21px;
}
.left_right_up,
.left_right_center{
width: 494px;
height: 306px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L3.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 20px 0 0 18px;
position: relative;
margin-right: 20px;
}
.left_right_down {
width: 536px;
height: 306px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L3.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 20px 0 0 18px;
position: relative;
margin-right: 20px;
}
.right_up_legend {
position: absolute;
right: 20px;
top: 80px;
width: 190px;
height: 200px;
}
.right_up_legend {
display: flex;
flex-flow: row wrap;
}
.right_up_legend .item {
/* border: 1px solid blue; */
width: 235px;
/* height: 20px; */
font-size: 12px;
color: #fff;
}
.right_up_legend .item .dot {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 5px;
}
.left_right_down .box {
margin-top: 10px;
}
.left_right_down .box .item {
margin-bottom: 6px;
}
.right_part {
width: 540px;
position: absolute;
top: 110px;
right: 38px;
}
.right_up {
width: 534px;
height: 304px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L1.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 20px 0 0 21px;
position: relative;
margin-right: 20px;
}
.right_right_up {
width: 540px;
height: 399px;
box-sizing: border-box;
background: url(../../../public/image/homePage/R1.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-bottom: 22px;
padding: 20px 0 0 21px;
position: relative;
}
.right_right_up .chart1,
.chart2,
.chart3 {
position: absolute;
width: 121px;
bottom: 32px;
}
.right_right_up .chart1 {
left: 50px;
}
.right_right_up .chart2 {
left: 210px;
}
.right_right_up .chart3 {
left: 370px;
}
.right_right_up .title {
position: absolute;
width: 80px;
left: 25px;
bottom: 100px;
color: #fff;
font-size: 18px;
}
.right_right_up .num_percentage {
font-family: "DINCondensed";
font-size: 64px;
color: #fff;
text-shadow: 0px 2px 4px #62d8ff;
position: absolute;
left: 19px;
display: block;
width: 85px;
text-align: center;
}
.right_right_up .num_percentage .small {
font-size: 29px;
}
.right_down {
width: 540px;
height: 525px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 20px 0 0 21px;
position: relative;
}
.produce{
width: 494px;
height: 304px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 20px 0 0 21px;
position: relative;
}
.produce .top_unit{
display: inline-block;
font-size: 12px;
color: rgba(223, 241, 254, 0.8);
}
.produce .top_box {
width: 185px;
height: 32px;
line-height: 32px;
display: inline-block;
background: url(../../../public/image/homePage/b3.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
font-size: 16px;
color: rgba(255, 255, 255, 0.9);
}
.produce .top_box .t1 {
display: inline-block;
width: 120px;
text-align: right;
}
.legend_right {
position: absolute;
right: 28px;
top: 28px;
color: rgba(255, 255, 255, 0.8);
font-size: 14px;
}
.legend_right .dot1 {
display: inline-block;
width: 10px;
height: 10px;
background: #12fff5;
border-radius: 5px;
margin: 0 8px 0 16px;
position: relative;
}
.legend_right .dot1::before {
display: inline-block;
content: "";
width: 18px;
height: 2px;
background: #12fff5;
position: absolute;
top: 4px;
left: -4px;
}
.legend_right .dot2 {
display: inline-block;
width: 10px;
height: 10px;
background: #34bdff;
margin-right: 5px;
}
.center_up {
width: 2635px;
height: 536px;
position: absolute;
top: 180px;
left: 600px;
}
.center-down {
width: 2635px;
height: 304px;
position: absolute;
bottom: 23px;
left: 606px;
}
.eq_info_box {
position: absolute;
top: -90px;
left: 16px;
width: 555px;
height: 245px;
background: url("./../../assets/image/eqInfoBg.png") 100% no-repeat;
background-size: 100% 100%;
padding: 15px 13px 15px 15px;
box-sizing: border-box;
}
.eq_info_box .title {
font-size: 24px;
color: #fff;
position: relative;
top: -10px;
}
.eq_info_box .content {
font-size: 18px;
color: #fff;
}
.eq_info_box .content span {
display: inline-block;
width: 260px;
line-height: 30px;
text-align: center;
margin-right: 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.eq_info_box .content .bg {
background: rgba(126, 255, 250, 0.12);
}
.dv-scroll-board {
width: 100%;
height: 100%;
}
.dv-scroll-board .header .header-item,
.dv-scroll-board .rows .ceil {
border-right: 1px solid #0b193e;
}
.dv-scroll-board .header .header-item:last-child,
.dv-scroll-board .rows .ceil:last-child {
border-right: none;
border: none;
}
.dv-scroll-board .header {
font-size: 18px;
}
.dv-scroll-board .rows .row-item {
font-size: 16px;
color: rgba(255, 255, 255, 0.8);
}

View File

@@ -0,0 +1,19 @@
import React from "react";
import TopTitle from "../Component/TopTitle";
import Left from "./Left";
import Center from "./Center/Center";
import Right from "./Right";
import "./index.css";
function HomePage() {
return (
<React.Fragment>
<TopTitle />
<div>
<Left />
<Center />
<Right />
</div>
</React.Fragment>
);
}
export default HomePage;

71
src/react-app-env.d.ts vendored Normal file
View File

@@ -0,0 +1,71 @@
/// <reference types="node" />
/// <reference types="react" />
/// <reference types="react-dom" />
declare namespace NodeJS {
interface ProcessEnv {
readonly NODE_ENV: 'development' | 'production' | 'test';
readonly PUBLIC_URL: string;
}
}
declare module '*.avif' {
const src: string;
export default src;
}
declare module '*.bmp' {
const src: string;
export default src;
}
declare module '*.gif' {
const src: string;
export default src;
}
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.jpeg' {
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.webp' {
const src: string;
export default src;
}
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FunctionComponent<React.SVGProps<
SVGSVGElement
> & { title?: string }>;
const src: string;
export default src;
}
declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.scss' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.sass' {
const classes: { readonly [key: string]: string };
export default classes;
}

15
src/reportWebVitals.ts Normal file
View File

@@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.ts Normal file
View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

37
src/store/ChangeLineID.ts Normal file
View File

@@ -0,0 +1,37 @@
import {createSlice} from "@reduxjs/toolkit";
import type {RootState} from "./store";
const initialState = {
LineID: 1,
}
export const ChangeLineID = createSlice({
name: 'ChangeLineID',
initialState,
reducers: {
UpdateChangeLineID: (state, action) => {
switch (action.payload) {
case '1':
state.LineID = 1;
break;
case '2':
state.LineID = 2;
break;
case '3':
state.LineID = 3;
break;
case '4':
state.LineID = 4;
break;
default:
state.LineID = 1;
break
}
},
}
})
export const {UpdateChangeLineID} = ChangeLineID.actions;
export const selectChangeLineID = (state: RootState) => state.ChangeLineID.LineID;
export default ChangeLineID.reducer;

View File

@@ -0,0 +1,191 @@
import {createSlice} from "@reduxjs/toolkit";
import type {RootState} from "./store";
export interface EquStatusInterface {
CleanAfterEdge1_1: number
CleanAfterEdge1_2: number
CleanAfterEdge2_1: number
CleanAfterEdge2_2: number
CleanAfterEdge3_1: number
CleanAfterEdge3_2: number
CleanAfterEdge4_1: number
CleanAfterEdge4_2: number
CleanAfterTemper1_1: number
CleanAfterTemper2_1: number
CleanAfterTemper3_1: number
CleanAfterTemper4_1: number
CleanBeforCoat1_1: number
CleanBeforCoat1_2: number
CleanBeforCoat4_1: number
CleanBeforCoat4_2: number
Edge1_1: number
Edge1_2: number
Edge2_1: number
Edge2_2: number
Edge3_1: number
Edge3_2: number
Edge4_1: number
Edge4_2: number
FirstCoat1_1: number
FirstCoat1_2: number
FirstCoat2_1: number
FirstCoat2_2: number
FirstCoat3_1: number
FirstCoat3_2: number
FirstCoat4_1: number
FirstCoat4_2: number
Punch1_1: number
Punch1_2: number
Punch4_1: number
Punch4_2: number
SecondCoat1_1: number
SecondCoat1_2: number
SecondCoat2_1: number
SecondCoat2_2: number
SecondCoat3_1: number
SecondCoat3_2: number
SecondCoat4_1: number
SecondCoat4_2: number
Temper1_1: number
Temper2_1: number
Temper3_1: number
Temper4_1: number
Unload1_1: number
Unload1_2: number
Unload2_1: number
Unload2_2: number
Unload3_1: number
Unload3_2: number
Unload4_1: number
Unload4_2: number
}
const initialState: EquStatusInterface = {
CleanAfterEdge1_1: 0,
CleanAfterEdge1_2: 0,
CleanAfterEdge2_1: 0,
CleanAfterEdge2_2: 0,
CleanAfterEdge3_1: 0,
CleanAfterEdge3_2: 0,
CleanAfterEdge4_1: 0,
CleanAfterEdge4_2: 0,
CleanAfterTemper1_1: 0,
CleanAfterTemper2_1: 0,
CleanAfterTemper3_1: 0,
CleanAfterTemper4_1: 0,
CleanBeforCoat1_1: 0,
CleanBeforCoat1_2: 0,
CleanBeforCoat4_1: 0,
CleanBeforCoat4_2: 0,
Edge1_1: 0,
Edge1_2: 0,
Edge2_1: 0,
Edge2_2: 0,
Edge3_1: 0,
Edge3_2: 0,
Edge4_1: 0,
Edge4_2: 0,
FirstCoat1_1: 0,
FirstCoat1_2: 0,
FirstCoat2_1: 0,
FirstCoat2_2: 0,
FirstCoat3_1: 0,
FirstCoat3_2: 0,
FirstCoat4_1: 0,
FirstCoat4_2: 0,
Punch1_1: 0,
Punch1_2: 0,
Punch4_1: 0,
Punch4_2: 0,
SecondCoat1_1: 0,
SecondCoat1_2: 0,
SecondCoat2_1: 0,
SecondCoat2_2: 0,
SecondCoat3_1: 0,
SecondCoat3_2: 0,
SecondCoat4_1: 0,
SecondCoat4_2: 0,
Temper1_1: 0,
Temper2_1: 0,
Temper3_1: 0,
Temper4_1: 0,
Unload1_1: 0,
Unload1_2: 0,
Unload2_1: 0,
Unload2_2: 0,
Unload3_1: 0,
Unload3_2: 0,
Unload4_1: 0,
Unload4_2: 0
}
export const EquStatusEntity = createSlice({
name: 'EquStatusEntity',
initialState,
reducers: {
UpdateEquStatus: (state, action) => {
const JsonData = action.payload;
state.CleanAfterEdge1_1 = JsonData['CleanAfterEdge1-1'].status;
state.CleanAfterEdge1_2 = JsonData['CleanAfterEdge1-2'].status;
state.CleanAfterEdge2_1 = JsonData['CleanAfterEdge2-1'].status;
state.CleanAfterEdge2_2 = JsonData['CleanAfterEdge2-2'].status;
state.CleanAfterEdge3_1 = JsonData['CleanAfterEdge3-1'].status;
state.CleanAfterEdge3_2 = JsonData['CleanAfterEdge3-2'].status;
state.CleanAfterEdge4_1 = JsonData['CleanAfterEdge4-1'].status;
state.CleanAfterEdge4_2 = JsonData['CleanAfterEdge4-2'].status;
state.CleanAfterTemper1_1 = JsonData['CleanAfterTemper1-1'].status;
state.CleanAfterTemper2_1 = JsonData['CleanAfterTemper2-1'].status;
state.CleanAfterTemper3_1 = JsonData['CleanAfterTemper3-1'].status;
state.CleanAfterTemper4_1 = JsonData['CleanAfterTemper4-1'].status;
state.CleanBeforCoat1_1 = JsonData['CleanBeforCoat1-1'].status;
state.CleanBeforCoat1_2 = JsonData['CleanBeforCoat1-2'].status;
state.CleanBeforCoat4_1 = JsonData['CleanBeforCoat4-1'].status;
state.CleanBeforCoat4_2 = JsonData['CleanBeforCoat4-2'].status;
state.Edge1_1 = JsonData['Edge1-1'].status;
state.Edge1_2 = JsonData['Edge1-2'].status;
state.Edge2_1 = JsonData['Edge2-1'].status;
state.Edge2_2 = JsonData['Edge2-2'].status;
state.Edge3_1 = JsonData['Edge3-1'].status;
state.Edge3_2 = JsonData['Edge3-2'].status;
state.Edge4_1 = JsonData['Edge4-1'].status;
state.Edge4_2 = JsonData['Edge4-2'].status;
state.FirstCoat1_1 = JsonData['FirstCoat1-1'].status;
state.FirstCoat1_2 = JsonData['FirstCoat1-2'].status;
state.FirstCoat2_1 = JsonData['FirstCoat2-1'].status;
state.FirstCoat2_2 = JsonData['FirstCoat2-2'].status;
state.FirstCoat3_1 = JsonData['FirstCoat3-1'].status;
state.FirstCoat3_2 = JsonData['FirstCoat3-2'].status;
state.FirstCoat4_1 = JsonData['FirstCoat4-1'].status;
state.FirstCoat4_2 = JsonData['FirstCoat4-2'].status;
state.Punch1_1 = JsonData['Punch1-1'].status;
state.Punch1_2 = JsonData['Punch1-2'].status;
state.Punch4_1 = JsonData['Punch4-1'].status;
state.Punch4_2 = JsonData['Punch4-2'].status;
state.SecondCoat1_1 = JsonData['SecondCoat1-1'].status;
state.SecondCoat1_2 = JsonData['SecondCoat1-2'].status;
state.SecondCoat2_1 = JsonData['SecondCoat2-1'].status;
state.SecondCoat2_2 = JsonData['SecondCoat2-2'].status;
state.SecondCoat3_1 = JsonData['SecondCoat3-1'].status;
state.SecondCoat3_2 = JsonData['SecondCoat3-2'].status;
state.SecondCoat4_1 = JsonData['SecondCoat4-1'].status;
state.SecondCoat4_2 = JsonData['SecondCoat4-2'].status;
state.Temper1_1 = JsonData['Temper1-1'].status;
state.Temper2_1 = JsonData['Temper2-1'].status;
state.Temper3_1 = JsonData['Temper3-1'].status;
state.Temper4_1 = JsonData['Temper4-1'].status;
state.Unload1_1 = JsonData['Unload1-1'].status;
state.Unload1_2 = JsonData['Unload1-2'].status;
state.Unload2_1 = JsonData['Unload2-1'].status;
state.Unload2_2 = JsonData['Unload2-2'].status;
state.Unload3_1 = JsonData['Unload3-1'].status;
state.Unload3_2 = JsonData['Unload3-2'].status;
state.Unload4_1 = JsonData['Unload4-1'].status;
state.Unload4_2 = JsonData['Unload4-2'].status;
}
}
})
export const {UpdateEquStatus} = EquStatusEntity.actions;
export const selectEquStatus = (state: RootState) => state.EquStatusEntity;
export default EquStatusEntity.reducer;

View File

@@ -0,0 +1,93 @@
import { createSlice } from "@reduxjs/toolkit";
import type { RootState } from "./store";
export interface Alarm {
alarmContent: string;
equName: string;
alarmCode: any;
alarmEquipmentId: number;
alarmValue: string;
alarmId: number;
alarmSource: string;
}
export interface TickCount {
equName: string;
tickCount: number;
}
export interface Product {
externalCode: number;
equipmentCode: number;
outputNum: number;
lineName: string;
equipmentId: number;
sectionName: string;
inputNum: number;
recordTime: number;
equipmentName: string;
}
export interface Oee {
workRate: number;
equName: string;
downRate: number;
stopRate: number;
}
export interface EquipmentLine {
equipmentAlarm: Array<Alarm>;
equipmentTickCounts: Array<TickCount>;
equipmentOees: Array<Oee>;
equipmentProductDays: Array<Product>;
equipmentProductWeeks: Array<Product>;
equipmentProductMonths: Array<Product>;
equipmentProductAll: Array<Product>;
}
export interface EquipmentMonitorEntityInterface {
Line_1: EquipmentLine;
Line_2: EquipmentLine;
Line_3: EquipmentLine;
Line_4: EquipmentLine;
}
const emptyEquipmentLine: EquipmentLine = {
equipmentAlarm: [],
equipmentTickCounts: [],
equipmentOees: [],
equipmentProductDays: [],
equipmentProductWeeks: [],
equipmentProductMonths: [],
equipmentProductAll: [],
};
const initialState: EquipmentMonitorEntityInterface = {
Line_1: emptyEquipmentLine,
Line_2: emptyEquipmentLine,
Line_3: emptyEquipmentLine,
Line_4: emptyEquipmentLine,
};
export const EquipmentMonitorEntity = createSlice({
name: "EquipmentMonitorEntity",
initialState,
reducers: {
UpdateEquipmentMonitorEntity: (state, action) => {
if (action.payload.toString().includes("客户端")) {
} else {
const BasicEquipmentMonitorEntity = JSON.parse(action.payload);
state.Line_1 = BasicEquipmentMonitorEntity.Line_1;
state.Line_2 = BasicEquipmentMonitorEntity.Line_2;
state.Line_3 = BasicEquipmentMonitorEntity.Line_3;
state.Line_4 = BasicEquipmentMonitorEntity.Line_4;
}
},
},
});
export const { UpdateEquipmentMonitorEntity } = EquipmentMonitorEntity.actions;
// export const selectAllLineEquipmentData = (state: RootState) => state.EquipmentMonitorEntity;
export default EquipmentMonitorEntity.reducer;

View File

@@ -0,0 +1,88 @@
import { createSlice } from "@reduxjs/toolkit";
import type { RootState } from "./store";
export interface HomePageSliceInterface {
eqRateAVA: any;
orderCompletionWO: any;
defectsClassDEFECT: any;
productionBarChart: any;
tempAndHumidityTEMP: any;
powerDistributionpow: any;
outputFUCH: any;
outputMsgBOX: any;
yield: any;
}
const initialState: HomePageSliceInterface = {
eqRateAVA: {},
orderCompletionWO: [],
defectsClassDEFECT: {},
productionBarChart: [],
tempAndHumidityTEMP: {},
powerDistributionpow: [],
outputFUCH: {},
outputMsgBOX: [],
yield: {},
};
export const HomePageSlice = createSlice({
name: "HomePageSlice",
initialState,
reducers: {
UpdateEqRateAVA: (state, action) => {
state.eqRateAVA = action.payload;
},
UpdateOrderCompletionWO: (state, action) => {
state.orderCompletionWO = action.payload;
},
UpdateDefectsClassDEFECT: (state, action) => {
state.defectsClassDEFECT = action.payload;
},
UpdateProductionBarChart: (state, action) => {
state.productionBarChart = action.payload;
},
UpdateTempAndHumidityTEMP: (state, action) => {
state.tempAndHumidityTEMP = action.payload;
},
UpdatePowerDistributionpow: (state, action) => {
state.powerDistributionpow = action.payload;
},
UpdateOutputFUCH: (state, action) => {
state.outputFUCH = action.payload;
},
UpdateOutputMsgBOX: (state, action) => {
state.outputMsgBOX = action.payload;
},
UpdateYield: (state, action) => {
state.yield = action.payload;
},
},
});
export const {
UpdateEqRateAVA,
UpdateOrderCompletionWO,
UpdateDefectsClassDEFECT,
UpdateProductionBarChart,
UpdateTempAndHumidityTEMP,
UpdatePowerDistributionpow,
UpdateOutputFUCH,
UpdateOutputMsgBOX,
UpdateYield,
} = HomePageSlice.actions;
export const selectEqRateAVA = (state: RootState) =>
state.HomePageSlice.eqRateAVA;
export const selectOrderCompletionWO = (state: RootState) =>
state.HomePageSlice.orderCompletionWO;
export const selectDefectsClassDEFECT = (state: RootState) =>
state.HomePageSlice.defectsClassDEFECT;
export const selectProductionBarChart = (state: RootState) =>
state.HomePageSlice.productionBarChart;
export const selectTempAndHumidityTEMP = (state: RootState) =>
state.HomePageSlice.tempAndHumidityTEMP;
export const selectPowerDistributionpow = (state: RootState) =>
state.HomePageSlice.powerDistributionpow;
export const selectOutputFUCH = (state: RootState) =>
state.HomePageSlice.outputFUCH;
export const selectOutputMsgBOX = (state: RootState) =>
state.HomePageSlice.outputMsgBOX;
export const selectYield = (state: RootState) => state.HomePageSlice.yield;
export default HomePageSlice.reducer;

View File

@@ -0,0 +1,214 @@
import {createSlice} from "@reduxjs/toolkit";
import type {RootState} from "./store";
export interface alarm {
alarmContent: string;
alarmTime: string;
alarmCode: string;
}
export interface ProductionDet {
lineName: string;
inputNum: number;
outputNum: number;
passRate: number;
}
export interface ProductionRate {
lineName: string;
lineId: number;
lineExCode: string;
passRate: number;
time: number;
}
export interface RateLine {
Line_1: Array<ProductionRate>,
Line_2: Array<ProductionRate>,
Line_3: Array<ProductionRate>,
Line_4: Array<ProductionRate>,
}
export interface SingleGlassStatus {
lineViewCode: string,
status: number
}
export interface GlassStatus {
LINE_1_1: number,
LINE_1_2U: number,
LINE_1_2D: number,
LINE_1_3: number,
LINE_1_4: number,
LINE_2_1: number,
LINE_2_2U: number,
LINE_2_2D: number,
LINE_2_3: number,
LINE_2_4: number,
LINE_3_1: number,
LINE_3_2U: number,
LINE_3_2D: number,
LINE_3_3: number,
LINE_3_4: number,
LINE_4_1: number,
LINE_4_2U: number,
LINE_4_2D: number,
LINE_4_3: number,
LINE_4_4: number,
}
const initLineGlassStatus: GlassStatus = {
LINE_1_1: 1,
LINE_1_2U: 1,
LINE_1_2D: 1,
LINE_1_3: 1,
LINE_1_4: 1,
LINE_2_1: 1,
LINE_2_2U: 1,
LINE_2_2D: 1,
LINE_2_3: 1,
LINE_2_4: 1,
LINE_3_1: 1,
LINE_3_2U: 1,
LINE_3_2D: 1,
LINE_3_3: 1,
LINE_3_4: 1,
LINE_4_1: 1,
LINE_4_2U: 1,
LINE_4_2D: 1,
LINE_4_3: 1,
LINE_4_4: 1,
}
export interface ProductionMonitoringEntityInterface {
sumNumber: number;
alarms: Array<alarm>;
sumProductionDets: Array<ProductionDet>;
todayProductionDets: Array<ProductionDet>;
weekProductionDets: Array<ProductionDet>;
monthProductionDets: Array<ProductionDet>;
todayProductionRates: RateLine,
weekProductionRates: RateLine,
monthProductionRates: RateLine,
lineGlassStatus: GlassStatus,
}
const emptyProductionRates: RateLine = {
Line_4: [
{
"lineId": 1,
"lineName": "产线1",
"lineExCode": "Line_1",
"passRate": 101.88,
"time": 1676840400000
},
],
Line_3: [
{
"lineId": 1619974755856867329,
"lineName": "产线3",
"lineExCode": "Line_3",
"passRate": 32.04,
"time": 1676822400000
}
],
Line_2: [
{
"lineId": 1619974755856867329,
"lineName": "产线3",
"lineExCode": "Line_3",
"passRate": 32.04,
"time": 1676822400000
}
],
Line_1: [
{
"lineId": 1619974755856867329,
"lineName": "产线3",
"lineExCode": "Line_3",
"passRate": 32.04,
"time": 1676822400000
}
]
}
const initialState: ProductionMonitoringEntityInterface = {
sumNumber: 0,
alarms: [],
sumProductionDets: [],
todayProductionDets: [],
weekProductionDets: [],
monthProductionDets: [],
todayProductionRates: emptyProductionRates,
weekProductionRates: emptyProductionRates,
monthProductionRates: emptyProductionRates,
lineGlassStatus: initLineGlassStatus,
}
export const ProductionMonitoringEntity = createSlice({
name: 'ProductionMonitoringEntity',
initialState,
reducers: {
UpdateProductionMonitoringEntity: (state, action) => {
if (action.payload.toString().includes('客户端')) {
} else {
const ProductionMonitoringEntityData = JSON.parse(action.payload);
//sumNumber
state.sumNumber = ProductionMonitoringEntityData.alarms.length;
//alarms
state.alarms = [];
for (let i = 0; i < 6; i++) {
state.alarms.push({
alarmContent: ProductionMonitoringEntityData.alarms[i].alarmContent,
alarmCode: ProductionMonitoringEntityData.alarms[i].alarmCode,
alarmTime: new Date(ProductionMonitoringEntityData.alarms[i].alarmTime).toLocaleDateString(),
}
)
}
//sumProductionDets
state.sumProductionDets = ProductionMonitoringEntityData.sumProductionDets;
//todayProductionDets
state.todayProductionDets = ProductionMonitoringEntityData.todayProductionDets;
//weekProductionDets
state.weekProductionDets = ProductionMonitoringEntityData.weekProductionDets;
//monthProductionDets
state.monthProductionDets = ProductionMonitoringEntityData.monthProductionDets;
//todayProductionRates
state.todayProductionRates = ProductionMonitoringEntityData.todayProductionRates;
//weekProductionRates
state.weekProductionRates = ProductionMonitoringEntityData.weekProductionRates;
//monthProductionRates
state.monthProductionRates = ProductionMonitoringEntityData.monthProductionRates;
}
},
UpdateGlassStatus: (state, action) => {
//lineGlassStatus
state.lineGlassStatus = action.payload;
}
}
})
export const {UpdateProductionMonitoringEntity, UpdateGlassStatus} = ProductionMonitoringEntity.actions;
export const selectAlarms = (state: RootState) => state.ProductionMonitoringEntity.alarms;
export const selectSumNumber = (state: RootState) => state.ProductionMonitoringEntity.sumNumber;
export const selectSumProductionDets = (state: RootState) => state.ProductionMonitoringEntity.sumProductionDets;
export const selectTodayProductionDets = (state: RootState) => state.ProductionMonitoringEntity.todayProductionDets;
export const selectWeekProductionDets = (state: RootState) => state.ProductionMonitoringEntity.weekProductionDets;
export const selectMonthProductionDets = (state: RootState) => state.ProductionMonitoringEntity.monthProductionDets;
export const selectTodayProductionRates = (state: RootState) => state.ProductionMonitoringEntity.todayProductionRates;
export const selectWeekProductionRates = (state: RootState) => state.ProductionMonitoringEntity.weekProductionRates;
export const selectMonthProductionRates = (state: RootState) => state.ProductionMonitoringEntity.monthProductionRates;
export const selectGlassStatus = (state: RootState) => state.ProductionMonitoringEntity.lineGlassStatus;
export default ProductionMonitoringEntity.reducer;

112
src/store/UpdateData.tsx Normal file
View File

@@ -0,0 +1,112 @@
import { useState } from "react";
import axios from "axios";
import {
UpdateEqRateAVA,
UpdateOrderCompletionWO,
UpdateDefectsClassDEFECT,
UpdateProductionBarChart,
UpdateTempAndHumidityTEMP,
UpdatePowerDistributionpow,
UpdateOutputFUCH,
UpdateOutputMsgBOX,
UpdateYield,
} from "./HomePageSlice";
import { useAppDispatch } from "./hooks";
function UpdateData() {
// const [myUrl, setUrl] = useState("");
// axios.get("/wsconfig.json").then((r) => {
// setUrl(r.data.url);
// });
const myUrl = window.location.host;
let websocketHome = null;
if ("WebSocket" in window) {
websocketHome = new WebSocket(
"ws://" + myUrl + "/websocket/message?userId=DC" + new Date().getTime()
);
//连接成功建立的回调方法
} else {
alert("Not support websocket");
}
// @ts-ignore
websocketHome.onopen = function (event) {
console.log("websocketHome-open");
};
interface MsgDataType {
type: string;
}
// const [msgData, setMsgData] = useState<MsgDataType | null>(null);
//接收到消息的回调方法
// @ts-ignore
websocketHome.onmessage = function (event) {
// console.log("接收到消息:", event);
let msgData: MsgDataType = { type: "example" };
try {
msgData = JSON.parse(event.data);
} catch (error) {
console.log("websocket: [unable to msgData] : ", event.data);
}
if (!msgData) return;
switch (msgData?.type) {
case "AVA": {
// 设备稼动率
// @ts-ignore
dispatch(UpdateEqRateAVA(msgData.data));
break;
}
case "WO": {
// 封装工单完成情况
// @ts-ignore
dispatch(UpdateOrderCompletionWO(msgData.data));
break;
}
case "DEFECT": {
// 产线缺陷分类
// @ts-ignore
dispatch(UpdateDefectsClassDEFECT(msgData.data));
break;
}
case "OUT": {
// 产量柱状图
// @ts-ignore
dispatch(UpdateProductionBarChart(msgData.data));
break;
}
case "TEMP": {
// 温湿度
// @ts-ignore
dispatch(UpdateTempAndHumidityTEMP(msgData.data));
break;
}
case "POW": {
// FUCH功率分布
// @ts-ignore
dispatch(UpdatePowerDistributionpow(msgData.data));
break;
}
case "FUCH": {
// FUCH产出
// @ts-ignore
dispatch(UpdateOutputFUCH(msgData));
break;
}
case "BOX": {
// 下片分档信息
// @ts-ignore
dispatch(UpdateOutputMsgBOX(msgData.data));
break;
}
case "YIELD": {
// 前中后段良率
// @ts-ignore
dispatch(UpdateYield(msgData.data));
break;
}
default:
}
};
const dispatch = useAppDispatch();
return null;
}
export default UpdateData;

6
src/store/hooks.ts Normal file
View File

@@ -0,0 +1,6 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

19
src/store/store.ts Normal file
View File

@@ -0,0 +1,19 @@
import { configureStore } from "@reduxjs/toolkit";
import ProductionMonitoringEntityReducer from "./ProductionMonitoringEntity";
import ChangeLineIDReducer from "./ChangeLineID";
// import QualityMonitorEntityReducer from "./QualityMonitorEntity";
import EquStatusEntityReducer, { EquStatusEntity } from "./EquStatusEntity";
import HomePageReducer from "./HomePageSlice";
export const store = configureStore({
reducer: {
ProductionMonitoringEntity: ProductionMonitoringEntityReducer,
ChangeLineID: ChangeLineIDReducer,
// QualityMonitorEntity: QualityMonitorEntityReducer,
EquStatusEntity: EquStatusEntityReducer,
HomePageSlice: HomePageReducer,
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;