大屏样式

This commit is contained in:
朱菊兰 2024-08-19 14:44:16 +08:00
parent bda855430d
commit bbb7de918e
20 changed files with 998 additions and 191 deletions

11
package-lock.json generated
View File

@ -36,6 +36,7 @@
"browserslist": "^4.18.1",
"camelcase": "^6.2.1",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"classnames": "^2.5.1",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.2.0",
"dotenv": "^10.0.0",
@ -5939,6 +5940,11 @@
"resolved": "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
"integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA=="
},
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
},
"node_modules/clean-css": {
"version": "5.3.2",
"resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.2.tgz",
@ -21198,6 +21204,11 @@
"resolved": "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
"integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA=="
},
"classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
},
"clean-css": {
"version": "5.3.2",
"resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.2.tgz",

View File

@ -31,6 +31,7 @@
"browserslist": "^4.18.1",
"camelcase": "^6.2.1",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"classnames": "^2.5.1",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.2.0",
"dotenv": "^10.0.0",

View File

@ -11,41 +11,11 @@ import {
Observable,
Vector3,
} from "@babylonjs/core";
import {
GlassAnimation1_1,
GlassAnimation1_2D,
GlassAnimation1_2U,
GlassAnimation1_3,
GlassAnimation1_4,
GlassAnimation2_1,
GlassAnimation2_2D,
GlassAnimation2_2U,
GlassAnimation2_3,
GlassAnimation2_4,
GlassAnimation3_1,
GlassAnimation3_2D,
GlassAnimation3_2U,
GlassAnimation3_3,
GlassAnimation3_4,
GlassAnimation4_1,
GlassAnimation4_2D,
GlassAnimation4_2U,
GlassAnimation4_3,
GlassAnimation4_4,
} from "./GlassAnimation";
import { useAppSelector } from "../store/hooks";
import {
GlassStatus,
selectGlassStatus,
} from "../store/ProductionMonitoringEntity";
import "../page/style/standard.css";
import { MyObservable } from "../context/MyObservable";
import { Button, ButtonGroup } from "@mui/material";
import intl from "react-intl-universal";
import { EquStatusInterface, selectEquStatus } from "../store/EquStatusEntity";
import EquMap from "./EquMap";
const onMainCamObservable = new Observable();
const onEquObservable = new Observable();
const myStyle = {
@ -54,57 +24,18 @@ const myStyle = {
outline: "none",
};
const DetailCamera = {
alpha: BABYLON.Tools.ToRadians(270),
beta: BABYLON.Tools.ToRadians(25),
radius: 120,
target: new BABYLON.Vector3(-110, 0, -8),
// Part_1: {
// alpha: BABYLON.Tools.ToRadians(270),
// beta: BABYLON.Tools.ToRadians(25),
// radius: 85,
// target: new BABYLON.Vector3(-110, 0, -8),
// },
// Part_2: {
// alpha: BABYLON.Tools.ToRadians(270),
// beta: BABYLON.Tools.ToRadians(25),
// radius: 85,
// target: new BABYLON.Vector3(-40, 0, -8),
// },
// Part_3: {
// alpha: BABYLON.Tools.ToRadians(270),
// beta: BABYLON.Tools.ToRadians(25),
// radius: 85,
// target: new BABYLON.Vector3(0, 0, -8),
// },
// Part_4: {
// alpha: BABYLON.Tools.ToRadians(270),
// beta: BABYLON.Tools.ToRadians(25),
// radius: 85,
// target: new BABYLON.Vector3(110, 0, -8),
// },
};
interface MybabylonJSProps {
modelPath: string; // 明确 modelPath 属性的类型为 string
}
function MybabylonJS({ modelPath }: MybabylonJSProps) {
const onGlassObservable = useContext(MyObservable);
const EquStatus = useAppSelector(selectEquStatus);
const canvasRef = useRef(null);
onEquObservable.notifyObservers(EquStatus);
// const thisLineGlassStatus = useAppSelector(selectGlassStatus)
// onGlassObservable.notifyObservers(thisLineGlassStatus)
const [SelectedMeshName, setSelectedMeshName] = useState<string | null>(null);
interface MybabylonJSProps {
modelPath: string;
}
// 使用 useRef 来存储当前加载的模型引用
const currentMeshesRef = useRef<Array<BABYLON.AbstractMesh>>([]);
useEffect(() => {
// 确保 canvas 引用存在
if (!canvasRef.current) return;
@ -120,19 +51,6 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0, 0, 0, 0);
const light = new BABYLON.DirectionalLight(
"light",
new BABYLON.Vector3(20, 20, 100),
scene
);
const light2 = new BABYLON.PointLight(
"light2",
new BABYLON.Vector3(20, 20, 100),
scene
);
const Glass1_1 = new BABYLON.TransformNode("Glass1_1");
const baseLight = new HemisphericLight(
"hemiLight",
new Vector3(-1, 1, 0),
@ -148,9 +66,14 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
"camera",
BABYLON.Tools.ToRadians(245),
BABYLON.Tools.ToRadians(25),
90,
modelPath.slice(-1) === "1"
? 110
: modelPath.slice(-3) === "5-2"
? 100
: 80,
new BABYLON.Vector3(-13, 0, 0)
);
console.log("camera", camera);
camera.lowerRadiusLimit = 10;
camera.upperRadiusLimit = 600;
@ -194,8 +117,6 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
let hl = new BABYLON.HighlightLayer("hl1", scene);
let hl2 = new BABYLON.HighlightLayer("hl2", scene);
// var LOD0MESH = await BABYLON.SceneLoader.ImportMeshAsync('', '/test/', `${modelPath}.babylon`, scene);
// 定义一个函数来加载或重新加载模型
const loadOrReloadModel = async () => {
// 在加载新模型之前卸载已加载的模型
@ -213,11 +134,6 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
`${modelPath}.babylon`,
scene
);
// var LOD0MESH = await BABYLON.SceneLoader.ImportMeshAsync('', '/test/', `line1.babylon`, scene);
// var LOD1MESH = await BABYLON.SceneLoader.ImportMeshAsync('', '/test/', `line2.babylon`, scene);
// var LOD2MESH = await BABYLON.SceneLoader.ImportMeshAsync('', '/test/', `line3.babylon`, scene);
// var LOD3MESH = await BABYLON.SceneLoader.ImportMeshAsync('', '/test/', `line4.babylon`, scene);
// var LOD4MESH = await BABYLON.SceneLoader.ImportMeshAsync('', '/test/', `line5.babylon`, scene);
// 将新加载的模型添加到 currentMeshesRef 中
currentMeshesRef.current.push(...LOD0MESH.meshes);
@ -264,7 +180,6 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
};
});
/////////////////////////////////////////////////////////////////////////////////////////////////
onEquObservable.add((eventData, eventState) => {
LOD0MESH.meshes.find((mesh) => {
// @ts-ignore
@ -285,8 +200,6 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
}
});
});
/////////////////////////////////////////////////////////////////////////////////////////////////
} catch (error) {
console.error("加载模型失败:", error);
}
@ -299,50 +212,22 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
camera.target = new BABYLON.Vector3(-13, 0, 0);
camera.alpha = BABYLON.Tools.ToRadians(245);
camera.beta = BABYLON.Tools.ToRadians(25);
camera.radius = 90;
camera.radius =
modelPath.slice(-1) === "1"
? 110
: modelPath.slice(-3) === "5-2"
? 100
: 80;
}
let resetCamera = setTimeout(reset, 15000);
scene.onPointerObservable.add((pointerInfo) => {
// console.log(camera.target, camera.alpha * 180 / 3.14, camera.beta * 180 / 3.14, camera.radius)
switch (pointerInfo.type) {
case BABYLON.PointerEventTypes.POINTERMOVE:
clearTimeout(resetCamera);
resetCamera = setTimeout(reset, 15000);
}
});
// onMainCamObservable.add((eventData, eventState) => {
// clearTimeout(resetCamera);
// resetCamera = setTimeout(reset, 5000);
// switch (eventData) {
// case 1:
// camera.target = DetailCamera.Part_1.target;
// camera.alpha = DetailCamera.Part_1.alpha;
// camera.beta = DetailCamera.Part_1.beta;
// camera.radius = DetailCamera.Part_1.radius;
// break;
// case 2:
// camera.target = DetailCamera.Part_2.target;
// camera.alpha = DetailCamera.Part_2.alpha;
// camera.beta = DetailCamera.Part_2.beta;
// camera.radius = DetailCamera.Part_2.radius;
// break;
// case 3:
// camera.target = DetailCamera.Part_3.target;
// camera.alpha = DetailCamera.Part_3.alpha;
// camera.beta = DetailCamera.Part_3.beta;
// camera.radius = DetailCamera.Part_3.radius;
// break;
// case 4:
// camera.target = DetailCamera.Part_4.target;
// camera.alpha = DetailCamera.Part_4.alpha;
// camera.beta = DetailCamera.Part_4.beta;
// camera.radius = DetailCamera.Part_4.radius;
// break;
// }
// });
return scene;
};
@ -375,52 +260,8 @@ function MybabylonJS({ modelPath }: MybabylonJSProps) {
};
}, [modelPath]);
// const handleClick1 = () => {
// onMainCamObservable.notifyObservers(1);
// };
// const handleClick2 = () => {
// onMainCamObservable.notifyObservers(2);
// };
// const handleClick3 = () => {
// onMainCamObservable.notifyObservers(3);
// };
// const handleClick4 = () => {
// onMainCamObservable.notifyObservers(4);
// };
return (
<div style={myStyle}>
{/* <ButtonGroup
variant="contained"
aria-label="outlined button group"
className={"btnArea"}
>
<h2>: {SelectedMeshName}</h2>
<Button
sx={{ backgroundColor: "rgba(86, 244, 231, 0.69)" }}
onClick={handleClick1}
>
{intl.get("Part1")}
</Button>
<Button
sx={{ backgroundColor: "rgba(86, 244, 231, 0.69)" }}
onClick={handleClick2}
>
{intl.get("Part2")}
</Button>
<Button
sx={{ backgroundColor: "rgba(86, 244, 231, 0.69)" }}
onClick={handleClick3}
>
{intl.get("Part3")}
</Button>
<Button
sx={{ backgroundColor: "rgba(86, 244, 231, 0.69)" }}
onClick={handleClick4}
>
{intl.get("Part4")}
</Button>
</ButtonGroup> */}
<canvas ref={canvasRef} style={myStyle} />
</div>
);

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

@ -1,16 +1,60 @@
import TitleBox from "../Component/TitleBox";
import LineChart from "./LineChart";
import ScrollBoard from "./../../Component/ScrollBoard";
import SwitchButton from "../Component/SwitchButton";
import { useState } from "react";
function CenterDown() {
const nameList = [{ name: "天" }, { name: "周" }, { name: "月" }];
const [activeName, setActiveName] = useState<string>(nameList[0].name);
const config = {
header: ["序号", "报警时间", "报警编码", "设备状态"],
headerHeight: 36,
rowNum: 6,
headerBGC: "rgba(79, 114, 136, 0.3)",
oddRowBGC: "rgba(79, 114, 136, 0.3)",
evenRowBGC: "rgba(76, 97, 123, 0.1)",
columnWidth: [80, 137, 137, 137],
data: [
["1", "行1列1", "行1列2", "<span style='color:#FF1E1E'>行1列3</span>"],
["2", "行2列1", "行2列2", "<span style='color:#FF1E1E'>行2列3</span>"],
["3", "行3列1", "行3列2", "<span style='color:#FF1E1E'>行3列3</span>"],
["4", "行4列1", "行4列2", "<span style='color:#FFB40F'>行4列3</span>"],
["5", "行5列1", "行5列2", "<span style='color:#FF1E1E'>行5列3</span>"],
["6", "行6列1", "行6列2", "<span style='color:#FFB40F'>行6列3</span>"],
["7", "行7列1", "行7列2", "<span style='color:#FF1E1E'>行7列3</span>"],
["8", "行8列1", "行8列2", "<span style='color:#FF1E1E'>行8列3</span>"],
["9", "行9列1", "行9列2", "<span style='color:#FF1E1E'>行9列3</span>"],
[
"10",
"行10列1",
"行10列2",
"<span style='color:#FFB40F'>行10列3</span>",
],
],
};
return (
<div className="center_down flex-row">
<div className="center_down_inner flex-col left-box">
<TitleBox title={"center_down_left"} />
<span className="alarm_num_title"> </span>
<div className="alarm_num">321,343</div>
<div style={{ padding: 10, height: "270px" }}>
<ScrollBoard
config={config}
style={{ width: "492px", height: "250px" }}
/>
</div>
</div>
{/* 产线成品率 */}
<div className="center_down_inner flex-col right_box">
<TitleBox title={"center_down_right"} />
<div className="left_up_switch">
<SwitchButton
nameList={nameList}
activeName={activeName}
setActiveName={setActiveName}
/>
</div>
<div className="chart_box">
<LineChart />
</div>

View File

@ -1,8 +1,11 @@
import LinePageBabylon from "../../../babylonjs/LinePageBabylon";
import { useParams } from "react-router-dom";
function CenterUp() {
const { LineID } = useParams();
const lineID = LineID?.toString() || "1-1";
return (
<div className="center_up">
<LinePageBabylon modelPath={`Line1-2`} />
<LinePageBabylon modelPath={`Line${lineID}`} />
</div>
);
}

View File

@ -70,31 +70,44 @@ export default function getOptions() {
},
},
},
tooltip: {},
tooltip: {
trigger: "axis",
className: "luoyang-chart-tooltip",
},
series: [
{
name: "产线1",
type: "line",
symbol: "circle",
symbolSize: 4,
data: [20, 32, 10, 34, 90, 30, 20],
},
{
name: "产线2",
type: "line",
symbol: "circle",
symbolSize: 4,
data: [22, 82, 91, 34, 90, 33, 31],
},
{
name: "产线3",
type: "line",
symbol: "circle",
symbolSize: 4,
data: [50, 32, 20, 54, 19, 33, 41],
},
{
name: "产线4",
type: "line",
symbol: "circle",
symbolSize: 4,
data: [30, 32, 30, 34, 90, 33, 32],
},
{
name: "产线5",
type: "line",
symbol: "circle",
symbolSize: 4,
data: [20, 92, 91, 94, 90, 30, 53],
},
],

View File

@ -49,7 +49,13 @@ export default function getOptions() {
},
},
},
tooltip: {},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
className: "luoyang-chart-tooltip",
},
series: [
{
data: [120, 200, 150, 80, 70, 110],

View File

@ -1,10 +1,32 @@
import TitleBox from "../Component/TitleBox";
import SwitchButton from "../Component/SwitchButton";
import BarChart from "./BarChart";
import ScrollBoard from "./../../Component/ScrollBoard";
import { useState } from "react";
function LeftDown() {
const nameList = [{ name: "表单" }, { name: "柱状" }];
const [activeName, setActiveName] = useState<string>(nameList[0].name);
const config = {
header: ["序号", "缺陷种类", "缺陷数量"],
headerHeight: 30,
rowNum: 4,
headerBGC: "rgba(79, 114, 136, 0.3)",
oddRowBGC: "rgba(79, 114, 136, 0.3)",
evenRowBGC: "rgba(76, 97, 123, 0.1)",
columnWidth: [73, 117, 190],
data: [
["行1列1", "行1列2", "行1列3"],
["行2列1", "行2列2", "行2列3"],
["行3列1", "行3列2", "行3列3"],
["行4列1", "行4列2", "行4列3"],
["行5列1", "行5列2", "行5列3"],
["行6列1", "行6列2", "行6列3"],
["行7列1", "行7列2", "行7列3"],
["行8列1", "行8列2", "行8列3"],
["行9列1", "行9列2", "行9列3"],
["行10列1", "行10列2", "行10列3"],
],
};
return (
<div className="left_down">
<TitleBox title={"left_down"} />
@ -22,7 +44,14 @@ function LeftDown() {
<div className="left_down_box2 flex-col" />
</div>
<div className="left_down_content">
{activeName === "表单" ? "表单" : <BarChart />}
{activeName === "表单" ? (
<ScrollBoard
config={config}
style={{ width: "380px", height: "150px" }}
/>
) : (
<BarChart />
)}
</div>
<div className="left_down_title flex-row">
<div className="left_down_box1 flex-col" />
@ -30,7 +59,14 @@ function LeftDown() {
<div className="left_down_box2 flex-col" />
</div>
<div className="left_down_content">
{activeName === "表单" ? "表单" : <BarChart />}
{activeName === "表单" ? (
<ScrollBoard
config={config}
style={{ width: "380px", height: "150px" }}
/>
) : (
<BarChart />
)}
</div>
<div className="left_down_title flex-row">
<div className="left_down_box1 flex-col" />
@ -38,7 +74,14 @@ function LeftDown() {
<div className="left_down_box2 flex-col" />
</div>
<div className="left_down_content">
{activeName === "表单" ? "表单" : <BarChart />}
{activeName === "表单" ? (
<ScrollBoard
config={config}
style={{ width: "380px", height: "150px" }}
/>
) : (
<BarChart />
)}
</div>
</div>
</div>

View File

@ -69,7 +69,13 @@ export default function getOptions() {
},
},
},
tooltip: {},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
className: "luoyang-chart-tooltip",
},
dataset: {
source: [
["product", "产线1", "产线2", "产线3", "产线4", "产线5"],

View File

@ -70,18 +70,25 @@ export default function getOptions() {
},
},
},
tooltip: {},
series: [
{
name: "投入",
type: "line",
symbol: "circle",
symbolSize: 4,
data: [20, 32, 10, 34, 90, 30, 20],
},
{
name: "产出",
type: "line",
symbol: "circle",
symbolSize: 4,
data: [22, 82, 91, 34, 90, 33, 31],
},
],
tooltip: {
trigger: "axis",
className: "luoyang-chart-tooltip",
},
};
}

View File

@ -1,10 +1,32 @@
import TitleBox from "../Component/TitleBox";
import SwitchButton from "../Component/SwitchButton";
import LineChart from "./LineChart";
import ScrollBoard from "./../../Component/ScrollBoard";
import { useState } from "react";
function RightDown() {
const nameList = [{ name: "表单" }, { name: "折线" }];
const [activeName, setActiveName] = useState<string>(nameList[0].name);
const config = {
header: ["时间", "投入数量", "产出数量"],
headerHeight: 30,
rowNum: 5,
headerBGC: "rgba(79, 114, 136, 0.3)",
oddRowBGC: "rgba(79, 114, 136, 0.3)",
evenRowBGC: "rgba(76, 97, 123, 0.1)",
columnWidth: [120, 130, 130],
data: [
["行1列1", "行1列2", "行1列3"],
["行2列1", "行2列2", "行2列3"],
["行3列1", "行3列2", "行3列3"],
["行4列1", "行4列2", "行4列3"],
["行5列1", "行5列2", "行5列3"],
["行6列1", "行6列2", "行6列3"],
["行7列1", "行7列2", "行7列3"],
["行8列1", "行8列2", "行8列3"],
["行9列1", "行9列2", "行9列3"],
["行10列1", "行10列2", "行10列3"],
],
};
return (
<div className="right_down">
<TitleBox title={"right_down"} />
@ -22,7 +44,14 @@ function RightDown() {
<div className="left_down_box2 flex-col" />
</div>
<div className="right_down_content">
{activeName === "表单" ? "表单" : <LineChart />}
{activeName === "表单" ? (
<ScrollBoard
config={config}
style={{ width: "380px", height: "180px" }}
/>
) : (
<LineChart />
)}
</div>
<div className="left_down_title flex-row">
<div className="left_down_box1 flex-col" />
@ -30,7 +59,14 @@ function RightDown() {
<div className="left_down_box2 flex-col" />
</div>
<div className="right_down_content">
{activeName === "表单" ? "表单" : <LineChart />}
{activeName === "表单" ? (
<ScrollBoard
config={config}
style={{ width: "380px", height: "180px" }}
/>
) : (
<LineChart />
)}
</div>
<div className="left_down_title flex-row">
<div className="left_down_box1 flex-col" />
@ -38,7 +74,14 @@ function RightDown() {
<div className="left_down_box2 flex-col" />
</div>
<div className="right_down_content">
{activeName === "表单" ? "表单" : <LineChart />}
{activeName === "表单" ? (
<ScrollBoard
config={config}
style={{ width: "380px", height: "180px" }}
/>
) : (
<LineChart />
)}
</div>
</div>
</div>

View File

@ -1,8 +1,36 @@
import TitleBox from "../Component/TitleBox";
import ScrollBoard from "./../../Component/ScrollBoard";
function RightUp() {
const config = {
header: ["产线", "上片数据量", "成品下片数量", "成品下片"],
headerHeight: 32,
rowNum: 5,
headerBGC: "rgba(79, 114, 136, 0.3)",
oddRowBGC: "rgba(79, 114, 136, 0.3)",
evenRowBGC: "rgba(76, 97, 123, 0.1)",
columnWidth: [73, 100, 117, 90],
data: [
["1", "行1列1", "行1列2", "行1列3"],
["2", "行2列1", "行2列2", "行2列3"],
["3", "行3列1", "行3列2", "行3列3"],
["4", "行4列1", "行4列2", "行4列3"],
["5", "行5列1", "行5列2", "行5列3"],
["6", "行6列1", "行6列2", "行6列3"],
["7", "行7列1", "行7列2", "行7列3"],
["8", "行8列1", "行8列2", "行8列3"],
["9", "行9列1", "行9列2", "行9列3"],
["10", "行10列1", "行10列2", "行10列3"],
],
};
return (
<div className="right_up">
<TitleBox title={"right_up"} />
<div style={{ padding: "10px", height: "213px" }}>
<ScrollBoard
config={config}
style={{ width: "380px", height: "193px" }}
/>
</div>
</div>
);
}

View File

@ -76,6 +76,7 @@
background: url(../../../public/png/rect/lp_center_down.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
position: relative;
}
.center_down .left-box {
margin-right: 15px;
@ -129,6 +130,7 @@
.left_down_title {
height: 18px;
justify-content: center;
margin-bottom: 4px;
}
.left_down_box1 {
width: 56px;
@ -166,3 +168,22 @@
height: 185px;
/* padding-bottom: 5px; */
}
.dv-scroll-board .header .header-item,
.dv-scroll-board .rows .ceil {
border-right: 1px solid #0d1728;
}
.dv-scroll-board .header .header-item:last-child,
.dv-scroll-board .rows .ceil:last-child {
border-right: none;
border: none;
}
.luoyang-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.luoyang-chart-tooltip * {
color: #fff !important;
}

View File

@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
@ -18,9 +14,8 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
"jsx": "react-jsx",
"downlevelIteration": true
},
"include": [
"src"
],
"include": ["src"]
}