From bbb7de918e641fec0fb588b6dab2725887bef537 Mon Sep 17 00:00:00 2001 From: juzi <819872918@qq.com> Date: Mon, 19 Aug 2024 14:44:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E5=B1=8F=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 11 + package.json | 1 + src/babylonjs/LinePageBabylon.tsx | 183 +------- src/page/Component/ScrollBoard/index.css | 45 ++ src/page/Component/ScrollBoard/index.tsx | 440 ++++++++++++++++++ .../Component/ScrollBoard/use/autoResize.ts | 57 +++ .../Component/ScrollBoard/use/autoResize1.js | 57 +++ src/page/Component/ScrollBoard/util/index.ts | 114 +++++ src/page/Component/ScrollBoard/util/utils.ts | 32 ++ src/page/LinePage/Center/CenterDown.tsx | 44 ++ src/page/LinePage/Center/CenterUp.tsx | 5 +- .../LinePage/Center/LineChart/chart.config.ts | 15 +- .../LinePage/Left/BarChart/chart.config.ts | 8 +- src/page/LinePage/Left/LeftDown.tsx | 49 +- .../Left/SummaryBarChart/chart.config.ts | 8 +- .../LinePage/Right/LineChart/chart.config.ts | 9 +- src/page/LinePage/Right/RightDown.tsx | 49 +- src/page/LinePage/Right/RightUp.tsx | 28 ++ src/page/LinePage/index.css | 21 + tsconfig.json | 13 +- 20 files changed, 998 insertions(+), 191 deletions(-) create mode 100644 src/page/Component/ScrollBoard/index.css create mode 100644 src/page/Component/ScrollBoard/index.tsx create mode 100644 src/page/Component/ScrollBoard/use/autoResize.ts create mode 100644 src/page/Component/ScrollBoard/use/autoResize1.js create mode 100644 src/page/Component/ScrollBoard/util/index.ts create mode 100644 src/page/Component/ScrollBoard/util/utils.ts diff --git a/package-lock.json b/package-lock.json index 3ff198f..438014a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 57eeebc..64ae578 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/babylonjs/LinePageBabylon.tsx b/src/babylonjs/LinePageBabylon.tsx index 700d7b7..baec296 100644 --- a/src/babylonjs/LinePageBabylon.tsx +++ b/src/babylonjs/LinePageBabylon.tsx @@ -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(null); - interface MybabylonJSProps { - modelPath: string; - } - // 使用 useRef 来存储当前加载的模型引用 const currentMeshesRef = useRef>([]); - 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 (
- {/* -

当前选择: {SelectedMeshName}

- - - - -
*/}
); diff --git a/src/page/Component/ScrollBoard/index.css b/src/page/Component/ScrollBoard/index.css new file mode 100644 index 0000000..0fd91b7 --- /dev/null +++ b/src/page/Component/ScrollBoard/index.css @@ -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; +} diff --git a/src/page/Component/ScrollBoard/index.tsx b/src/page/Component/ScrollBoard/index.tsx new file mode 100644 index 0000000..703d20c --- /dev/null +++ b/src/page/Component/ScrollBoard/index.tsx @@ -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} + * @default header = [] + * @example header = ['column1', 'column2', 'column3'] + */ + header: [], + /** + * @description Board data + * @type {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} + * @default columnWidth = [] + */ + columnWidth: [], + /** + * @description Column align + * @type {Array} + * @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 = `${ + i + 1 + }`; + + 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, 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(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 ( +
+ {!!header.length && !!mergedConfig && ( +
+ {header.map((headerItem, i) => ( +
+ ))} +
+ )} + + {!!mergedConfig && ( +
+ {rows.map((row: any, ri) => ( +
+ {row.ceils.map((ceil: any, ci: any) => ( +
emitEvent(onClick, ri, ci, row, ceil)} + onMouseEnter={() => handleHover(true, ri, ci, row, ceil)} + onMouseLeave={() => handleHover(false)} + /> + ))} +
+ ))} +
+ )} +
+ ); + } +); + +export default ScrollBoard; diff --git a/src/page/Component/ScrollBoard/use/autoResize.ts b/src/page/Component/ScrollBoard/use/autoResize.ts new file mode 100644 index 0000000..4dfc1dc --- /dev/null +++ b/src/page/Component/ScrollBoard/use/autoResize.ts @@ -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 }; +} diff --git a/src/page/Component/ScrollBoard/use/autoResize1.js b/src/page/Component/ScrollBoard/use/autoResize1.js new file mode 100644 index 0000000..7aae82f --- /dev/null +++ b/src/page/Component/ScrollBoard/use/autoResize1.js @@ -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 }; +} diff --git a/src/page/Component/ScrollBoard/util/index.ts b/src/page/Component/ScrollBoard/util/index.ts new file mode 100644 index 0000000..dadf41b --- /dev/null +++ b/src/page/Component/ScrollBoard/util/index.ts @@ -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; +} diff --git a/src/page/Component/ScrollBoard/util/utils.ts b/src/page/Component/ScrollBoard/util/utils.ts new file mode 100644 index 0000000..7c0e74e --- /dev/null +++ b/src/page/Component/ScrollBoard/util/utils.ts @@ -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; +} diff --git a/src/page/LinePage/Center/CenterDown.tsx b/src/page/LinePage/Center/CenterDown.tsx index 820e407..d52f884 100644 --- a/src/page/LinePage/Center/CenterDown.tsx +++ b/src/page/LinePage/Center/CenterDown.tsx @@ -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(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", "行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 (
— 报警总数 —
321,343
+
+ +
{/* 产线成品率 */}
+
+ +
diff --git a/src/page/LinePage/Center/CenterUp.tsx b/src/page/LinePage/Center/CenterUp.tsx index f4e0c88..ad8d3c0 100644 --- a/src/page/LinePage/Center/CenterUp.tsx +++ b/src/page/LinePage/Center/CenterUp.tsx @@ -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 (
- +
); } diff --git a/src/page/LinePage/Center/LineChart/chart.config.ts b/src/page/LinePage/Center/LineChart/chart.config.ts index 77af700..cb9c8e3 100644 --- a/src/page/LinePage/Center/LineChart/chart.config.ts +++ b/src/page/LinePage/Center/LineChart/chart.config.ts @@ -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], }, ], diff --git a/src/page/LinePage/Left/BarChart/chart.config.ts b/src/page/LinePage/Left/BarChart/chart.config.ts index 7fff553..1ff6774 100644 --- a/src/page/LinePage/Left/BarChart/chart.config.ts +++ b/src/page/LinePage/Left/BarChart/chart.config.ts @@ -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], diff --git a/src/page/LinePage/Left/LeftDown.tsx b/src/page/LinePage/Left/LeftDown.tsx index e2ba671..c3f6193 100644 --- a/src/page/LinePage/Left/LeftDown.tsx +++ b/src/page/LinePage/Left/LeftDown.tsx @@ -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(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 (
@@ -22,7 +44,14 @@ function LeftDown() {
- {activeName === "表单" ? "表单" : } + {activeName === "表单" ? ( + + ) : ( + + )}
@@ -30,7 +59,14 @@ function LeftDown() {
- {activeName === "表单" ? "表单" : } + {activeName === "表单" ? ( + + ) : ( + + )}
@@ -38,7 +74,14 @@ function LeftDown() {
- {activeName === "表单" ? "表单" : } + {activeName === "表单" ? ( + + ) : ( + + )}
diff --git a/src/page/LinePage/Left/SummaryBarChart/chart.config.ts b/src/page/LinePage/Left/SummaryBarChart/chart.config.ts index efddfd7..3e093e7 100644 --- a/src/page/LinePage/Left/SummaryBarChart/chart.config.ts +++ b/src/page/LinePage/Left/SummaryBarChart/chart.config.ts @@ -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"], diff --git a/src/page/LinePage/Right/LineChart/chart.config.ts b/src/page/LinePage/Right/LineChart/chart.config.ts index f518d12..723cbe6 100644 --- a/src/page/LinePage/Right/LineChart/chart.config.ts +++ b/src/page/LinePage/Right/LineChart/chart.config.ts @@ -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", + }, }; } diff --git a/src/page/LinePage/Right/RightDown.tsx b/src/page/LinePage/Right/RightDown.tsx index 38d20d4..9e6a3da 100644 --- a/src/page/LinePage/Right/RightDown.tsx +++ b/src/page/LinePage/Right/RightDown.tsx @@ -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(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 (
@@ -22,7 +44,14 @@ function RightDown() {
- {activeName === "表单" ? "表单" : } + {activeName === "表单" ? ( + + ) : ( + + )}
@@ -30,7 +59,14 @@ function RightDown() {
- {activeName === "表单" ? "表单" : } + {activeName === "表单" ? ( + + ) : ( + + )}
@@ -38,7 +74,14 @@ function RightDown() {
- {activeName === "表单" ? "表单" : } + {activeName === "表单" ? ( + + ) : ( + + )}
diff --git a/src/page/LinePage/Right/RightUp.tsx b/src/page/LinePage/Right/RightUp.tsx index 79f7679..b56b579 100644 --- a/src/page/LinePage/Right/RightUp.tsx +++ b/src/page/LinePage/Right/RightUp.tsx @@ -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 (
+
+ +
); } diff --git a/src/page/LinePage/index.css b/src/page/LinePage/index.css index de19450..9a18902 100644 --- a/src/page/LinePage/index.css +++ b/src/page/LinePage/index.css @@ -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; +} diff --git a/tsconfig.json b/tsconfig.json index d938e0a..cc0a000 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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"] }