第一次提交

This commit is contained in:
2025-07-18 17:39:30 +08:00
commit e81e7f82fd
114 changed files with 34887 additions and 0 deletions

27
src/App.css Normal file
View File

@@ -0,0 +1,27 @@
.background {
height: 1080px;
width: 5760px;
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;
}
.dejin-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.dejin-chart-tooltip * {
color: #fff !important;
}
p {
margin: 0;
}

24
src/App.tsx Normal file
View File

@@ -0,0 +1,24 @@
import "./App.css";
import { createHashRouter, RouterProvider } from "react-router-dom";
import ErrorPage from "./page/ErrorPage";
import HomePage from "./page/HomePage";
function App() {
const router = createHashRouter([
{
path: "/",
element: <HomePage />,
errorElement: <ErrorPage />,
},
]);
return (
<div className="background">
<RouterProvider router={router} />
</div>
);
}
export default App;

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

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;
}

21
src/index.tsx Normal file
View File

@@ -0,0 +1,21 @@
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(
<Provider store={store}>
<App />
<UpdateData />
</Provider>
);
reportWebVitals();

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,30 @@
.top_block {
width: 5760px;
height: 186px;
background-image: url(/public/image/topTitle.png);
background-size:5760px;
position: relative;
font-weight: 500;
font-size: 26px;
color: #51f0ff;
}
.top_center__title {
position: absolute;
left: 2234px;
top: 30px;
font-weight: 500;
font-size: 73px;
color: #6BF2FF;
letter-spacing: 22px;
text-shadow: 0px 0px 8px rgba(221,237,255,0.34);
}
.top_right__title {
font-size: 36px;
position: absolute;
right: 1253px;
top:112px;
}
.time {
display: inline-block;
margin-left: 45px;
}

View File

@@ -0,0 +1,31 @@
import "./index.css";
import moment from "moment";
import "moment/locale/zh-cn";
import { useState, useEffect } from "react";
import img1 from './../../../assets/image/top_right.png'
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_center__title"></div>
<img src={img1} alt="" style={{position:'absolute',width:'870px',right:'1054px',top:'128px'}}/>
<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,52 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartRed/chart.config";
import { generateTimeLabels } from "../../../../utils"
function BPress() {
const data = useAppSelector(selectLineChart).TE361 || [];
let xData: any = [];
let line6: Number[] = [];
let yName = "单位/℃";
if (data.length !== 0) {
xData = generateTimeLabels(data.length)
line6 = data
}else{
xData = [];
line6 = [];
}
const options= getOptions(xData, line6, yName);
return (
<div className="center_down">
<TitleBox title="bpress" />
<div className="legend_right">
<span className="dot" style={{background: '#18C7F3'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#F31868'}}></span>
<span>五线</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default BPress;

View File

@@ -0,0 +1,52 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartRed/chart.config";
import { generateTimeLabels } from "../../../../utils"
function BWater() {
const data = useAppSelector(selectLineChart). || [];
let xData: any = [];
let line6: Number[] = [];
let yName = "单位/m³";
if (data.length !== 0) {
xData = generateTimeLabels(data.length)
line6 = data
}else{
xData = [];
line6 = [];
}
const options= getOptions(xData, line6, yName);
return (
<div className="center_down">
<TitleBox title="bwater" />
<div className="legend_right">
<span className="dot" style={{background: '#18C7F3'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#F31868'}}></span>
<span>五线</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default BWater;

View File

@@ -0,0 +1,34 @@
import Nitrogen from "./Nitrogen"
import Oxygen from "./Oxygen"
import HydrogenGas from "./HydrogenGas"
import KPress from "./KPress"
import KTemp from "./KTemp"
import WPress from "./WPress"
import BWater from "./BWater"
import FlowTemp from "./FlowTemp"
import BPress from "./BPress"
import RecWaterPress from "./RecWaterPress"
import CenterTop from "./CenterTop"
function Center() {
return (
<div>
<div className="center_up_box">
<CenterTop />
</div>
<div className="center_down_box flex-row">
<Nitrogen />
<Oxygen />
<HydrogenGas />
<KPress />
<KTemp />
<WPress />
<BWater />
<FlowTemp />
<BPress />
<RecWaterPress />
</div>
</div>
);
}
export default Center;

View File

@@ -0,0 +1,108 @@
import img1 from './../../../../assets/image/fire.png'
import img2 from './../../../../assets/image/change_fire.png'
import img3 from './../../../../assets/image/time.png'
import eqicon1 from './../../../../assets/image/eq_icon1.png'
import eqicon2 from './../../../../assets/image/eq_icon2.png'
import eqicon3 from './../../../../assets/image/eq_icon3.png'
import eqicon4 from './../../../../assets/image/eq_icon4.png'
import { useAppSelector } from "../../../../store/hooks";
import { selectOther } from "../../../../store/HomePageSlice";
function CenterTop() {
const fireData = useAppSelector(selectOther). || '-';
const timeData = useAppSelector(selectOther). || '-';
const reTime = useAppSelector(selectOther). || '-';
return (
<div className="center_up">
<div className="center_up_top1">
<img src={img1} alt="" style={{width:'71px',position:'absolute',left:'40px',top:'25px'}}/>
<div className='center_up_top1_text'>
<div className='center_up_top1_text_left'>
<p style={{fontSize:'27px',color:'rgba(255, 255, 255, 0.90)'}}>线</p>
<p style={{fontSize:'47px',textAlign:'center'}}>{fireData}</p>
</div>
<span className='center_up_top_text_split'></span>
<div>
<p style={{fontSize:'27px',color:'rgba(255, 255, 255, 0.90)'}}>线</p>
<p style={{fontSize:'47px',textAlign:'center'}}>-</p>
</div>
</div>
</div>
<div className="center_up_top2">
<img src={img2} alt="" style={{width:'71px',position:'absolute',left:'40px',top:'25px'}}/>
<div className='center_up_top2_text'>
<div className='center_up_top2_text_left'>
<p style={{fontSize:'27px',color:'rgba(255, 255, 255, 0.90)'}}>线</p>
<p style={{fontSize:'47px',textAlign:'center'}}>{timeData}</p>
</div>
<span className='center_up_top_text_split'></span>
<div>
<p style={{fontSize:'27px',color:'rgba(255, 255, 255, 0.90)'}}>线</p>
<p style={{fontSize:'47px',textAlign:'center'}}>-</p>
</div>
</div>
</div>
<div className="center_up_top2" style={{left:'2534px'}}>
<img src={img3} alt="" style={{width:'71px',position:'absolute',left:'40px',top:'25px'}}/>
<div className='center_up_top2_text'>
<div className='center_up_top2_text_left'>
<p style={{fontSize:'27px',color:'rgba(255, 255, 255, 0.90)'}}>线.</p>
<p style={{fontSize:'47px',textAlign:'center'}}>{reTime}</p>
</div>
<span className='center_up_top_text_split'></span>
<div>
<p style={{fontSize:'27px',color:'rgba(255, 255, 255, 0.90)'}}>线.</p>
<p style={{fontSize:'47px',textAlign:'center'}}>-</p>
</div>
</div>
</div>
<div>
<div className='line_up'>
<img src={eqicon2} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'90px',top:'3px'}}/>
<img src={eqicon2} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'775px',top:'14px'}}/>
<img src={eqicon1} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'-11px',bottom:'63px'}}/>
<img src={eqicon1} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'692px',bottom:'52px'}}/>
{/* <div className='eqbg2' style={{left:'540px',top:'-34px'}}>
<p className='eqbg_text'>卡脖1</p>
<p className='eqbg_num'>99.9℃</p>
</div>
<div className='eqbg2' style={{left:'953px',top:'-17px'}}>
<p className='eqbg_text'>鼓泡1</p>
<p className='eqbg_num'>99.9℃</p>
</div>
<div className='eqbg1' style={{left:'420px',bottom:'26px'}}>
<p className='eqbg_text'>卡脖1</p>
<p className='eqbg_num'>99.9℃</p>
</div>
<div className='eqbg1' style={{left:'840px',bottom:'26px'}}>
<p className='eqbg_text'>鼓泡1</p>
<p className='eqbg_num'>99.9℃</p>
</div> */}
</div>
<div className='line_down'>
<img src={eqicon2} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'90px',top:'3px'}}/>
<img src={eqicon2} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'775px',top:'14px'}}/>
<img src={eqicon1} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'-11px',bottom:'63px'}}/>
<img src={eqicon1} alt="" style={{width:'57px',height:'51px',position:'absolute',left:'692px',bottom:'52px'}}/>
{/* <div className='eqbg2' style={{left:'580px',top:'-34px'}}>
<p className='eqbg_text'>卡脖1</p>
<p className='eqbg_num'>99.9℃</p>
</div>
<div className='eqbg2' style={{left:'953px',top:'-17px'}}>
<p className='eqbg_text'>鼓泡1</p>
<p className='eqbg_num'>99.9℃</p>
</div>
<div className='eqbg1' style={{left:'420px',bottom:'26px'}}>
<p className='eqbg_text'>卡脖1</p>
<p className='eqbg_num'>99.9℃</p>
</div>
<div className='eqbg1' style={{left:'840px',bottom:'26px'}}>
<p className='eqbg_text'>鼓泡1</p>
<p className='eqbg_num'>99.9℃</p>
</div> */}
</div>
</div>
</div>
)
}
export default CenterTop;

View File

@@ -0,0 +1,52 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartYellow/chart.config";
import { generateTimeLabels } from "../../../../utils"
function FlowTemp() {
const data = useAppSelector(selectLineChart). || [];
let xData: any = [];
let line6: Number[] = [];
let yName = "单位/℃";
if (data.length !== 0) {
xData = generateTimeLabels(data.length)
line6 = data
}else{
xData = [];
line6 = [];
}
const options= getOptions(xData, line6, yName);
return (
<div className="center_down">
<TitleBox title="flow_temp" />
<div className="legend_right">
<span className="dot" style={{background: '#FFD160'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#2760FF'}}></span>
<span>五线</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default FlowTemp;

View File

@@ -0,0 +1,75 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartRedDob/chart.config";
import { generateTimeLabels } from "../../../../utils";
function HydrogenGas() {
const data61 = useAppSelector(selectLineChart)['1#氢气流量'] || [];
const data62 = useAppSelector(selectLineChart)['2#氢气流量'] || [];
let xData: any = [];
let yName = "单位/m³";
let seriesData: any = [
{
name: "六线1",
data: [],
},
{
name: "六线2",
data: [],
}
];
if (data61.length !== 0) {
xData = generateTimeLabels(data61.length)
seriesData[0].data = data61
}else{
seriesData[0].data = [];
if (data62.length !== 0) {
xData = generateTimeLabels(data62.length)
}else{
xData = [];
}
}
if (data62.length !== 0) {
seriesData[1].data = data62
}else{
seriesData[1].data = [];
}
const options = getOptions(xData, seriesData, yName);
return (
<div className="center_down">
<TitleBox title="hydrogen_gas" />
<div className="legend_right">
<span className="dot" style={{background: '#18C7F3'}}></span>
<span>线1</span>
<span className="dot" style={{background: '#2665FE',marginLeft: '8px'}}></span>
<span>线2</span>
{/* <span className="dot" style={{background: '#E718F3',marginLeft: '8px'}}></span>
<span>五线1</span>
<span className="dot" style={{background: '#F31868',marginLeft: '8px'}}></span>
<span>五线2</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default HydrogenGas;

View File

@@ -0,0 +1,52 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartYellow/chart.config";
import { generateTimeLabels } from "../../../../utils"
function KPress() {
const data = useAppSelector(selectLineChart). || [];
let xData: any = [];
let line6: Number[] = [];
let yName = "单位/Kpa";
if (data.length !== 0) {
xData = generateTimeLabels(data.length)
line6 = data
}else{
xData = [];
line6 = [];
}
const options= getOptions(xData, line6, yName);
return (
<div className="center_down">
<TitleBox title="kpress" />
<div className="legend_right">
<span className="dot" style={{background: '#FFD160'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#2760FF'}}></span>
<span>五线</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default KPress;

View File

@@ -0,0 +1,52 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartRed/chart.config";
import { generateTimeLabels } from "../../../../utils"
function KTemp() {
const data = useAppSelector(selectLineChart). || [];
let xData: any = [];
let line6: Number[] = [];
let yName = "单位/℃";
if (data.length !== 0) {
xData = generateTimeLabels(data.length)
line6 = data
}else{
xData = [];
line6 = [];
}
const options= getOptions(xData, line6, yName);
return (
<div className="center_down">
<TitleBox title="ktemp" />
<div className="legend_right">
<span className="dot" style={{background: '#18C7F3'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#F31868'}}></span>
<span>五线</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default KTemp;

View File

@@ -0,0 +1,75 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartRedDob/chart.config";
import { generateTimeLabels } from "../../../../utils";
function Nitrogen() {
const data61 = useAppSelector(selectLineChart)['1#氮气流量'] || [];
const data62 = useAppSelector(selectLineChart)['2#氮气流量'] || [];
let xData: any = [];
let yName = "单位/m³";
let seriesData: any = [
{
name: "六线1",
data: [],
},
{
name: "六线2",
data: [],
}
];
if (data61.length !== 0) {
xData = generateTimeLabels(data61.length)
seriesData[0].data = data61
}else{
seriesData[0].data = [];
if (data62.length !== 0) {
xData = generateTimeLabels(data62.length)
}else{
xData = [];
}
}
if (data62.length !== 0) {
seriesData[1].data = data62
}else{
seriesData[1].data = [];
}
const options = getOptions(xData, seriesData, yName);
return (
<div className="center_down">
<TitleBox title="nitrogen" />
<div className="legend_right">
<span className="dot" style={{background: '#18C7F3'}}></span>
<span>线1</span>
<span className="dot" style={{background: '#2665FE',marginLeft: '8px'}}></span>
<span>线2</span>
{/* <span className="dot" style={{background: '#E718F3',marginLeft: '8px'}}></span>
<span>五线1</span>
<span className="dot" style={{background: '#F31868',marginLeft: '8px'}}></span>
<span>五线2</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default Nitrogen;

View File

@@ -0,0 +1,75 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartRedDob/chart.config";
import { generateTimeLabels } from "../../../../utils";
function Oxygen() {
const data61 = useAppSelector(selectLineChart)['0#氧枪左氧气流量'] || [];
const data62 = useAppSelector(selectLineChart)['0#氧枪右氧气流量'] || [];
let xData: any = [];
let yName = "单位/m³";
let seriesData: any = [
{
name: "六线左氧",
data: [],
},
{
name: "六线右氧",
data: [],
}
];
if (data61.length !== 0) {
xData = generateTimeLabels(data61.length)
seriesData[0].data = data61
}else{
seriesData[0].data = [];
if (data62.length !== 0) {
xData = generateTimeLabels(data62.length)
}else{
xData = [];
}
}
if (data62.length !== 0) {
seriesData[1].data = data62
}else{
seriesData[1].data = [];
}
const options = getOptions(xData, seriesData, yName);
return (
<div className="center_down">
<TitleBox title="oxygen" />
<div className="legend_right">
<span className="dot" style={{background: '#18C7F3'}}></span>
<span>线</span>
<span className="dot" style={{background: '#2665FE',marginLeft: '8px'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#E718F3',marginLeft: '8px'}}></span>
<span>五线1</span>
<span className="dot" style={{background: '#F31868',marginLeft: '8px'}}></span>
<span>五线2</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default Oxygen;

View File

@@ -0,0 +1,52 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartYellow/chart.config";
import { generateTimeLabels } from "../../../../utils"
function RecWaterPress() {
const data = useAppSelector(selectLineChart). || [];
let xData: any = [];
let line6: Number[] = [];
let yName = "单位/℃";
if (data.length !== 0) {
xData = generateTimeLabels(data.length)
line6 = data
}else{
xData = [];
line6 = [];
}
const options= getOptions(xData, line6, yName);
return (
<div className="center_down">
<TitleBox title="rec_water_press" />
<div className="legend_right">
<span className="dot" style={{background: '#FFD160'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#2760FF'}}></span>
<span>五线</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default RecWaterPress;

View File

@@ -0,0 +1,52 @@
import ReactECharts from "echarts-for-react";
import TitleBox from "../../Component/TitleBox";
import { useAppSelector } from "../../../../store/hooks";
import { selectLineChart } from "../../../../store/HomePageSlice";
import getOptions from "../../Component/LineChartYellow/chart.config";
import { generateTimeLabels } from "../../../../utils"
function WPress() {
const data = useAppSelector(selectLineChart). || [];
let xData: any = [];
let line6: Number[] = [];
let yName = "单位/Kpa";
if (data.length !== 0) {
xData = generateTimeLabels(data.length)
line6 = data
}else{
xData = [];
line6 = [];
}
const options= getOptions(xData, line6, yName);
return (
<div className="center_down">
<TitleBox title="wpress" />
<div className="legend_right">
<span className="dot" style={{background: '#FFD160'}}></span>
<span>线</span>
{/* <span className="dot" style={{background: '#2760FF'}}></span>
<span>五线</span> */}
</div>
{
options && (
<div className="chart_blur_bg">
<ReactECharts option={options} style={{ height: "100%" }} />
</div>
)
}
{!options && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default WPress;

View File

@@ -0,0 +1,96 @@
import * as echarts from "echarts";
export default function getOptions(xData: any, line6: any, yName: string) {
if (xData.length === 0 || line6.length === 0)
return null;
return {
grid: { top: 30, right: 0, bottom: 0, left: 10,containLabel: true},
legend: {
show:false
},
xAxis: {
type: "category",
data: xData,
axisLabel: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
interval: 'auto',
rotate:30,
},
axisLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
yAxis:{
name: yName,
nameTextStyle: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
align: "center",
},
type: "value",
alignTicks: true,
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: "dejin-chart-tooltip",
},
series: [
{
name:'六线',
data: line6,
color: "rgba(24, 199, 243, 1)",
type: "line",
symbol: "circle",
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(24, 199, 243, 0.6)" },
{ offset: 0.4, color: "rgba(24, 199, 243, 0.1)" },
{ offset: 0.8, color: "rgba(24, 199, 243, 0)" },
{ offset: 1, color: "rgba(24, 199, 243, 0)" },
]),
},
},
// {
// name:'五线',
// data: line5,
// color: "rgba(243, 24, 104, 1)",
// type: "line",
// symbol: "circle",
// symbolSize: 6,
// areaStyle: {
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
// { offset: 0, color: "rgba(243, 24, 104, 0.6)" },
// { offset: 0.4, color: "rgba(243, 24, 104, 0.1)" },
// { offset: 0.8, color: "rgba(243, 24, 104, 0)" },
// { offset: 1, color: "rgba(243, 24, 104, 0)" },
// ]),
// },
// },
],
};
}

View File

@@ -0,0 +1,112 @@
import * as echarts from "echarts";
export default function getOptions(xData: any, seriesData: any, yName: string) {
if (xData.length === 0)
return null;
return {
grid: { top: 30, right: 0, bottom: 0, left: 10,containLabel: true},
legend: {
show:false
},
xAxis: {
type: "category",
data: xData,
axisLabel: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
interval: 'auto',
rotate:30,
},
axisLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
yAxis:{
name: yName,
nameTextStyle: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
align: "center",
},
type: "value",
alignTicks: true,
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: "dejin-chart-tooltip",
},
series: [
{
name:seriesData[0].name,
data: seriesData[0].data,
color: "rgba(24, 199, 243, 1)",
type: "line",
symbol: "circle",
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(24, 199, 243, 0.6)" },
{ offset: 0.4, color: "rgba(24, 199, 243, 0.1)" },
{ offset: 0.8, color: "rgba(24, 199, 243, 0)" },
{ offset: 1, color: "rgba(24, 199, 243, 0)" },
]),
},
},
{
name:seriesData[1].name,
data: seriesData[1].data,
color: "rgba(38, 101, 254, 1)",
type: "line",
symbol: "circle",
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(38, 101, 254, 0.6)" },
{ offset: 0.4, color: "rgba(38, 101, 254, 0.1)" },
{ offset: 0.8, color: "rgba(38, 101, 254, 0)" },
{ offset: 1, color: "rgba(38, 101, 254, 0)" },
]),
},
},
// {
// name:'五线',
// data: line5,
// color: "rgba(243, 24, 104, 1)",
// type: "line",
// symbol: "circle",
// symbolSize: 6,
// areaStyle: {
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
// { offset: 0, color: "rgba(243, 24, 104, 0.6)" },
// { offset: 0.4, color: "rgba(243, 24, 104, 0.1)" },
// { offset: 0.8, color: "rgba(243, 24, 104, 0)" },
// { offset: 1, color: "rgba(243, 24, 104, 0)" },
// ]),
// },
// },
],
};
}

View File

@@ -0,0 +1,96 @@
import * as echarts from "echarts";
export default function getOptions(xData: any, line6: any, yName: string) {
if (xData.length === 0 || line6.length === 0)
return null;
return {
grid: { top: 30, right: 0, bottom: 0, left: 10,containLabel: true},
legend: {
show:false
},
xAxis: {
type: "category",
data: xData,
axisLabel: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
interval: 'auto',
rotate:30,
},
axisLine: {
lineStyle: {
width: 1,
color: "rgba(69, 97, 174, 1)",
},
},
},
yAxis:{
name: yName,
nameTextStyle: {
color: "rgba(223, 241, 254, 0.8)",
fontSize: 12,
align: "center",
},
type: "value",
alignTicks: true,
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: "dejin-chart-tooltip",
},
series: [
{
name:'六线',
data: line6,
color: "rgba(255, 209, 96, 1)",
type: "line",
symbol: "circle",
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(255, 209, 96, 0.6)" },
{ offset: 0.4, color: "rgba(255, 209, 96, 0.1)" },
{ offset: 0.8, color: "rgba(255, 209, 96, 0)" },
{ offset: 1, color: "rgba(255, 209, 96, 0)" },
]),
},
},
// {
// name:'五线',
// data: line5,
// color: "rgba(39, 96, 255, 1)",
// type: "line",
// symbol: "circle",
// symbolSize: 6,
// areaStyle: {
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
// { offset: 0, color: "rgba(39, 96, 255, 0.6)" },
// { offset: 0.4, color: "rgba(39, 96, 255, 0.1)" },
// { offset: 0.8, color: "rgba(39, 96, 255, 0)" },
// { offset: 1, color: "rgba(39, 96, 255, 0)" },
// ]),
// },
// },
],
};
}

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,14 @@
.title_box {
font-family: PingFangSC, PingFang SC;
font-size: 24px;
color: #72F2FF;
font-weight: 400;
}
.title_box img {
width: 25px;
height: 25px;
vertical-align: bottom;
margin-right: 8px;
position: relative;
top: -2px;
}

View File

@@ -0,0 +1,122 @@
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 t9 from "./../../../../assets/image/t9.png";
import t10 from "./../../../../assets/image/t10.png";
import "./index.css";
interface titleProps {
title: string;
}
function TitleBox(props: titleProps) {
const filteredTitles = () => {
switch (props.title) {
case "running_time6":
return {
img: t1,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "running_time5":
return {
img: t1,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "pressure6":
return {
img: t2,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "pressure5":
return {
img: t2,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "water_temp6":
return {
img: t3,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "water_temp5":
return {
img: t3,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "fan_run6":
return {
img: t4,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "fan_run5":
return {
img: t4,
title: <span><span style={{display: 'inline-block',lineHeight: '23px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</span>,
};
case "nitrogen":
return {
img: t5,
title: "氮气流量",
};
case "oxygen":
return {
img: t5,
title: "氧气流量",
};
case "hydrogen_gas":
return {
img: t5,
title: "氢气流量",
};
case "kpress":
return {
img: t6,
title: "窑炉压力",
};
case "ktemp":
return {
img: t7,
title: "窑炉温度",
};
case "wpress":
return {
img: t8,
title: "总管冷却水压力",
};
case "bwater":
return {
img: t9,
title: "天然气总管流量",
};
case "flow_temp":
return {
img: t7,
title: "流道温度",
};
case "bpress":
return {
img: t8,
title: "液面计冷却水温度",
};
case "rec_water_press":
return {
img: t10,
title: "循环水温度",
};
default:
return {
img: t10,
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,40 @@
import TitleBox from "../../Component/TitleBox";
import img1 from "../../../../assets/image/waterTempUp.png"
// import img2 from "../../../../assets/image/waterTempDown.png"
import { useAppSelector } from "../../../../store/hooks";
import { selectOther } from "../../../../store/HomePageSlice";
function LeftDown1() {
const outputData = useAppSelector(selectOther). || '';
// 数字显示组件
const DigitalDisplay = ({ value }:any) => {
return (
value.split('').map((char:any, index:any) => (
char !== '.'? (
<span key={index} className="left_down1_numbg">
{char}
</span>
):(<span key={index} className="left_down1_dotted"></span>)
))
);
};
return (
<div className="left_down1">
<TitleBox title="water_temp6" />
<div style={{position:'absolute',left:'45px',bottom:'100px'}}>
{
outputData === ''? (
<>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
</>
):<DigitalDisplay value={outputData}/>
}
<span style={{fontSize:'30px',color:'#fff'}}></span>
</div>
<div className="left_down1_tip" style={{bottom:'36px'}}></div>
<img src={img1} alt="" style={{position:'absolute',width:'139px',right:'68px',bottom:'36px'}}/>
</div>
);
}
export default LeftDown1;

View File

@@ -0,0 +1,63 @@
import TitleBox from "../../Component/TitleBox";
import ScrollBoard from "../../../Component/ScrollBoard";
import { useAppSelector } from "../../../../store/hooks";
import { selectFan } from "../../../../store/HomePageSlice";
function LeftDown2() {
const data = useAppSelector(selectFan). || [];
const config = {
header: ["序号", "设备名称", "运行状态", "设备状态"],
headerHeight: 30,
rowNum: 5,
headerBGC: "rgba(4, 74, 132, 0.2)",
oddRowBGC: "rgba(4, 74, 132, 0.2)",
evenRowBGC: "rgba(11, 84, 153, 0.36)",
columnWidth: [90, 220, 110, 110],
align: ["center", "left", "left", "center"],
data:[]
// data: [
// ['1','产线1的名称','<span style=color:#fff>未运行</span>','<span style=color:#3984FF><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#2760FF;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>正常</span>'],
// ['2','产线1的名称','<span style=color:#30E89A>未运行</span>','<span style=color:#FF0C0C><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#FF0C0C;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>故障</span>'],
// ['3','产线1的名称','<span style=color:#fff>未运行</span>','<span style=color:#3984FF><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#2760FF;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>正常</span>'],
// ['4','产线1的名称','<span style=color:#30E89A>未运行</span>','<span style=color:#3984FF><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#2760FF;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>正常</span>'],
// ['5','产线1的名称','<span style=color:#fff>未运行</span>','<span style=color:#FF0C0C><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#FF0C0C;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>故障</span>']
// ],
};
let arr: any = [];
data&&
data.map((item: any, index: any) => {
let arrInner = [];
arrInner.push(
index + 1,
item[0],
index%2 === 0 ? `<span style=color:#fff>${item[1]}</span>`:`<span style=color:#30E89A>${item[1]}</span>`,
item[2]==='正常'?`<span style=color:#3984FF><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#2760FF;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>${item[2]}</span>`:`<span style=color:#FF0C0C><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#FF0C0C;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>${item[2]}</span>`
);
arr.push(arrInner);
});
config.data = arr;
return (
<div className="left_down2">
<TitleBox title="fan_run6" />
{data.length !== 0 && (<div style={{ marginTop: "8px" }}>
<ScrollBoard
config={config}
style={{ width: "476px", height: "155px" }}
/>
</div>)}
{data.length === 0 && (
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
)}
</div>
);
}
export default LeftDown2;

View File

@@ -0,0 +1,72 @@
import { useState, useEffect } from 'react';
import TitleBox from "../../Component/TitleBox";
import moment from 'moment';
function LeftUp1() {
// 时间计算工具函数
const calculateRuntime = (startStr:any, endStr:any) => {
const parseDate = (str:any) => {
const [datePart, timePart] = str.split(' ');
const [year, month, day] = datePart.split('-').map(Number);
const [hours, minutes, seconds] = timePart.split(':').map(Number);
return new Date(year, month - 1, day, hours, minutes, seconds);
};
const start = parseDate(startStr);
const end = parseDate(endStr);
const diff = end.getTime() - start.getTime();
if (diff <= 0) return { days: 0, hours: 0 };
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
return {
days,
hours
};
};
// 数字显示组件
const DigitalDisplay = ({ value }:any) => {
return (
<div style={{ display: 'inline-flex' }}>
{value.split('').map((char:any, index:any) => (
<span key={index} className="left_up1_numbg">
{char}
</span>
))}
</div>
);
};
// 主显示组件
const RuntimeDisplay = ({ start, end }:any) => {
const [runtime, setRuntime] = useState({ days: 0, hours: 0 });
useEffect(() => {
const result = calculateRuntime(start, end);
setRuntime(result);
}, [start, end]);
// 格式化数字为字符串
const formatDays = runtime.days.toString().padStart(4, '0');
const formatHours = runtime.hours.toString().padStart(2, '0');
return (
<div style={{ position: 'absolute', left: '55px', top: '64px' }}>
<DigitalDisplay value={formatDays} />
<span className="left_up1_text" style={{ marginLeft: 4 }}></span>
<DigitalDisplay value={formatHours} />
<span className="left_up1_text" style={{ marginLeft: 4 }}></span>
</div>
);
};
const startDate = '2025-4-18 11:18:00'
const [endDate, setEndDate] = useState(moment().format('YYYY-MM-DD HH:mm:ss'))
useEffect(() => {
const timer = setInterval(() => {
setEndDate(moment().format(moment().format('YYYY-MM-DD HH:mm:ss')));
}, 1000);
return () => clearInterval(timer);
}, []);
return (
<div className="left_up1">
<TitleBox title="running_time6" />
<RuntimeDisplay start={startDate} end={endDate} />
<div className="left_up1_tip"><span style={{display: 'inline-block',lineHeight: '27px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</div>
</div>
);
}
export default LeftUp1;

View File

@@ -0,0 +1,39 @@
import TitleBox from "../../Component/TitleBox";
import img1 from "../../../../assets/image/press_img.png"
import { useAppSelector } from "../../../../store/hooks";
import { selectOther } from "../../../../store/HomePageSlice";
function LeftUp2() {
const data = useAppSelector(selectOther). || '';
// 数字显示组件
const DigitalDisplay = ({ value }:any) => {
return (
value.split('').map((char:any, index:any) => (
char !== '.'? (
<span key={index} className="left_down1_numbg">
{char}
</span>
):(<span key={index} className="left_down1_dotted"></span>)
))
);
};
return (
<div className="left_up1">
<TitleBox title="pressure6" />
<div style={{position:'absolute',left:'26px',top:'64px'}}>
{
data === ''? (
<>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
</>
):<DigitalDisplay value={data}/>
}
<span style={{fontSize:'30px',color:'#fff'}}>Kpa</span>
</div>
<div className="left_up2_tip"></div>
<img src={img1} alt="" style={{position:'absolute',width:'230px',right:'15px',top:'50px'}}/>
</div>
);
}
export default LeftUp2;

View File

@@ -0,0 +1,15 @@
import LeftUp1 from "./LeftUp1";
import LeftUp2 from "./LeftUp2";
import LeftDown1 from "./LeftDown1";
import LeftDown2 from "./LeftDown2";
function Left() {
return (
<div className="left_part">
<LeftUp1 />
<LeftUp2 />
<LeftDown1/>
<LeftDown2/>
</div>
);
}
export default Left;

View File

@@ -0,0 +1,22 @@
import TitleBox from "../../Component/TitleBox";
import img1 from "../../../../assets/image/waterTempUp.png"
// import img2 from "../../../../assets/image/waterTempDown.png"
// import { useAppSelector } from "../../../../store/hooks";
// import { selectOrderCompletionWO } from "../../../../store/HomePageSlice";
function RightDown1() {
// const data = useAppSelector(selectOrderCompletionWO);
return (
<div className="left_down1">
<TitleBox title="water_temp5" />
<div style={{position:'absolute',left:'45px',bottom:'100px'}}>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
<span style={{fontSize:'30px',color:'#fff'}}></span>
</div>
<div className="left_down1_tip" style={{bottom:'36px'}}></div>
<img src={img1} alt="" style={{position:'absolute',width:'139px',right:'68px',bottom:'36px'}}/>
</div>
);
}
export default RightDown1;

View File

@@ -0,0 +1,86 @@
import TitleBox from "../../Component/TitleBox";
import ScrollBoard from "../../../Component/ScrollBoard";
// import { useAppSelector } from "../../../../store/hooks";
// import { selectOrderCompletionWO } from "../../../../store/HomePageSlice";
function RightDown2() {
// const data = useAppSelector(selectOrderCompletionWO);
// console.log("222222封装工单完成情况+", data);
const config = {
header: ["序号", "设备名称", "运行频率", "设备状态"],
headerHeight: 30,
rowNum: 5,
headerBGC: "rgba(4, 74, 132, 0.2)",
oddRowBGC: "rgba(4, 74, 132, 0.2)",
evenRowBGC: "rgba(11, 84, 153, 0.36)",
columnWidth: [110, 180, 120, 120],
align: ["center", "left", "left", "center"],
data: [
['1','产线1的名称','<span style=color:#fff>未运行</span>','<span style=color:#3984FF><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#2760FF;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>正常</span>'],
['2','产线1的名称','<span style=color:#30E89A>未运行</span>','<span style=color:#FF0C0C><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#FF0C0C;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>故障</span>'],
['3','产线1的名称','<span style=color:#fff>未运行</span>','<span style=color:#3984FF><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#2760FF;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>正常</span>'],
['4','产线1的名称','<span style=color:#30E89A>未运行</span>','<span style=color:#3984FF><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#2760FF;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>正常</span>'],
['5','产线1的名称','<span style=color:#fff>未运行</span>','<span style=color:#FF0C0C><span style=display:inline-block;width:7px;height:7px;border-radius:4px;background:#FF0C0C;vertical-align:middle;margin-right:5px;margin-bottom:5px;></span>故障</span>']
],
};
// 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_down2">
<TitleBox title="fan_run5" />
{/* <div style={{ marginTop: "8px" }}>
<ScrollBoard
config={config}
style={{ width: "476px", height: "155px" }}
/>
</div> */}
{/* {data.length !== 0 && (
<div style={{ marginTop: "15px" }}>
<ScrollBoard
config={config}
style={{ width: "500px", height: "440px" }}
/>
</div>
)} */}
<p
style={{
color: "#cccf",
fontSize: "24px",
userSelect: "none",
textAlign: "center",
paddingTop: "72px",
}}
>
</p>
</div>
);
}
export default RightDown2;

View File

@@ -0,0 +1,23 @@
import TitleBox from "../../Component/TitleBox";
// import { useAppSelector } from "../../../../store/hooks";
// import { selectEqRateAVA } from "../../../../store/HomePageSlice";
function RightUp1() {
// const data = useAppSelector(selectEqRateAVA);
return (
<div className="left_up1">
<TitleBox title="running_time5" />
<div style={{position:'absolute',left:'55px',top:'64px'}}>
<span className="left_up1_numbg">-</span>
<span className="left_up1_numbg">-</span>
<span className="left_up1_numbg">-</span>
<span className="left_up1_numbg">-</span>
<span className="left_up1_text"></span>
<span className="left_up1_numbg">-</span>
<span className="left_up1_numbg">-</span>
<span className="left_up1_text"></span>
</div>
<div className="left_up1_tip"><span style={{display: 'inline-block',lineHeight: '27px',verticalAlign: 'top',margin: '0 5px'}}>.</span>线</div>
</div>
);
}
export default RightUp1;

View File

@@ -0,0 +1,21 @@
import TitleBox from "../../Component/TitleBox";
import img1 from "../../../../assets/image/press_img.png"
// import { useAppSelector } from "../../../../store/hooks";
// import { selectEqRateAVA } from "../../../../store/HomePageSlice";
function RightUp2() {
// const data = useAppSelector(selectEqRateAVA);
return (
<div className="left_up1">
<TitleBox title="pressure5" />
<div style={{position:'absolute',left:'26px',top:'64px'}}>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
<span className="left_down1_numbg">-</span>
<span style={{fontSize:'30px',color:'#fff'}}>Kpa</span>
</div>
<div className="left_up2_tip"></div>
<img src={img1} alt="" style={{position:'absolute',width:'230px',right:'15px',top:'50px'}}/>
</div>
);
}
export default RightUp2;

View File

@@ -0,0 +1,15 @@
import RightDown2 from "./RightDown2";
import RightUp1 from "./RightUp1";
import RightUp2 from "./RightUp2";
import RightDown1 from "./RightDown1"
function Right() {
return (
<div className="right_part">
<RightUp1 />
<RightUp2 />
<RightDown1 />
<RightDown2 />
</div>
);
}
export default Right;

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

@@ -0,0 +1,346 @@
@font-face {
font-family: "站酷庆科黄油体";
src: url("../../assets/fonts/站酷庆科黄油体.ttf");
font-weight: normal;
font-style: normal;
}
.left_part {
width: 542px;
position: absolute;
top: 82px;
margin-left: 52px;
}
.left_up1 {
width: 523px;
height: 224px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L1.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-bottom: 24px;
padding: 12px 0 0 20px;
position: relative;
backdrop-filter: blur(2px);
}
.left_up1_numbg{
display: inline-block;
width: 46px;
height: 62px;
background: url(../../../public/image/homePage/num_box2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-right: 5px;
font-family: "站酷庆科黄油体";
font-size: 58px;
color: #FFFFFF;
text-align: center;
line-height: 60px;
vertical-align: bottom;
}
.left_up1_text{
display: inline-block;
width: 46px;
height: 53px;
background: url(../../../public/image/homePage/num_box3.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-right: 5px;
font-family: "站酷庆科黄油体";
font-size: 36px;
color: #FFFFFF;
text-align: center;
line-height: 55px;
vertical-align: bottom;
}
.left_up1_tip {
position: absolute;
left: 110px;
bottom: 30px;
font-size: 33px;
color: #FFFFFF;
letter-spacing: 3px;
}
.left_up1_tip::after {
content: "";
display: inline-block;
width: 430px;
height: 4px;
background: linear-gradient( 270deg, rgba(138,231,248,0) 0%, #6FE2FF 51%, rgba(82,203,239,0) 100%);
filter: blur(1px);
position: absolute;
top: 39px;
left: -60px;
}
.left_up2_tip {
position: absolute;
left: 50px;
bottom: 42px;
font-size: 33px;
color: #FFFFFF;
}
.left_up2_tip::after {
content: "";
display: inline-block;
width: 210px;
height: 4px;
background: linear-gradient( 270deg, rgba(138,231,248,0) 0%, #6FE2FF 51%, rgba(82,203,239,0) 100%);
filter: blur(1px);
position: absolute;
top: 39px;
left: -19px;
}
.left_down1 {
width: 523px;
height: 224px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-bottom: 24px;
padding: 12px 0 0 20px;
position: relative;
backdrop-filter: blur(2px);
}
.left_down1_tip {
position: absolute;
left: 66px;
font-size: 33px;
color: #FFFFFF;
}
.left_down1_numbg{
display: inline-block;
width: 45px;
height: 55px;
background: url(../../../public/image/homePage/num_box1.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-right: 5px;
font-family: "站酷庆科黄油体";
font-size: 58px;
color: #FFFFFF;
text-align: center;
line-height: 55px;
vertical-align: bottom;
}
.left_down1_dotted{
display: inline-block;
width: 7px;
height: 9px;
background: url(../../../public/image/homePage/dotted.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
margin-right: 5px;
vertical-align: bottom;
}
.left_down1_tip::after {
content: "";
display: inline-block;
width: 236px;
height: 4px;
background: linear-gradient( 270deg, rgba(138,231,248,0) 0%, #6FE2FF 51%, rgba(82,203,239,0) 100%);
filter: blur(1px);
position: absolute;
top: 39px;
left: -19px;
}
.left_down2 {
width: 523px;
height: 232px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 12px 0 0 20px;
backdrop-filter: blur(2px);
}
/* 中间部分 */
.center_up_box {
width: 4557px;
height: 624px;
position: absolute;
top: 180px;
left: 602px;
}
.center_up_top1 {
width: 466px;
height: 152px;
box-sizing: border-box;
background: url(../../../public/image/homePage/center_top.png) no-repeat;
background-size: 100% 100%;
position: absolute;
left: 1455px;
backdrop-filter: blur(2px);
}
.center_up_top2 {
width: 574px;
height: 152px;
box-sizing: border-box;
background: url(../../../public/image/homePage/center_top.png) no-repeat;
background-size: 100% 100%;
position: absolute;
left: 1940px;
backdrop-filter: blur(2px);
}
.center_up_top1_text {
width: 355px;
height: 152px;
padding-top:22px;
padding-left: 10px;
box-sizing: border-box;
position: absolute;
right:0;
}
.center_up_top1_text div {
display: inline-block;
color:#fff;
padding:0 15px;
}
.center_up_top_text_split {
display: inline-block;
width: 1px;
height: 85px;
background: linear-gradient(
to bottom,
rgba(167, 169, 171, 1) 0%,
rgba(237, 237, 237, 1) 50%,
rgba(255, 255, 255, 1) 100%
);
}
.center_up_top2_text {
width: 463px;
height: 152px;
padding-top:22px;
padding-left: 10px;
box-sizing: border-box;
position: absolute;
right:0;
}
.center_up_top2_text div {
display: inline-block;
color:#fff;
padding:0 15px;
}
.line_up {
width: 4400px;
height: 280px;
box-sizing: border-box;
background: url(../../../public/image/homePage/line1.png) no-repeat;
background-size: 100% 100%;
position: absolute;
left: 80px;
top:100px
}
.line_down {
width: 4400px;
height: 280px;
box-sizing: border-box;
background: url(../../../public/image/homePage/line2.png) no-repeat;
background-size: 100% 100%;
position: absolute;
left: 80px;
bottom:-20px
}
.center_up .eqbg1 {
width: 175px;
height: 81px;
position:absolute;
background: url(../../../public/image/homePage/eq_bg1.png) no-repeat;
background-size: 100% 100%;
padding: 8px 0 0 25px;
box-sizing: border-box;
backdrop-filter: blur(4px);
}
.center_up .eqbg_text{
font-weight: 500;
font-size: 19px;
color: #FFFFFF;
letter-spacing: 5px;
z-index: 2;
}
.center_up .eqbg_num{
font-weight: 500;
font-size: 41px;
color: #00F4FF;
line-height: 34px;
z-index: 2;
}
.center_up .eqbg2 {
width: 175px;
height: 81px;
position:absolute;
background: url(../../../public/image/homePage/eq_bg2.png) no-repeat;
background-size: 100% 100%;
padding: 8px 0 0 25px;
box-sizing: border-box;
backdrop-filter: blur(4px);
}
.center_down_box {
width: 4600px;
height: 230px;
position: absolute;
bottom: 23px;
left: 602px;
}
.center_down {
width: 440px;
height: 230px;
box-sizing: border-box;
background: url(../../../public/image/homePage/L2.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
padding: 12px 15px 15px;
position:relative;
margin-right: 18px;
backdrop-filter: blur(2px);
}
.chart_blur_bg {
height: 174px;
padding: 5px 10px 0px 5px;
box-sizing: border-box;
background: url(../../../public/image/homePage/cd1.png) no-repeat;
background-size: 100% 100%;
background-position: 0 0;
border-radius: 10px;
}
.legend_right {
position: absolute;
right: 17px;
top: 25px;
color: rgba(255, 255, 255, 0.8);
font-size: 14px;
}
.legend_right .dot {
display: inline-block;
width: 10px;
height: 10px;
margin-right: 5px;
border-radius: 2px;
}
.right_part {
width: 524px;
position: absolute;
top: 82px;
right: 43px;
}
.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';

View File

@@ -0,0 +1,41 @@
import { createSlice } from "@reduxjs/toolkit";
import type { RootState } from "./store";
export interface HomePageSliceInterface {
lineChart: any;
fan:any;
otherMsg:any;
}
const initialState: HomePageSliceInterface = {
lineChart: {},
fan:[],
otherMsg:{}
};
export const HomePageSlice = createSlice({
name: "HomePageSlice",
initialState,
reducers: {
UpdateLineChart: (state, action) => {
state.lineChart = action.payload;
},
UpdateFan: (state, action) => {
state.fan = action.payload;
},
UpdateOther: (state, action) => {
state.otherMsg = action.payload;
}
},
});
export const {
UpdateLineChart,
UpdateFan,
UpdateOther
} = HomePageSlice.actions;
export const selectLineChart = (state: RootState) =>
state.HomePageSlice.lineChart;
export const selectFan = (state: RootState) =>
state.HomePageSlice.fan;
export const selectOther = (state: RootState) =>
state.HomePageSlice.otherMsg;
export default HomePageSlice.reducer;

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

@@ -0,0 +1,68 @@
// import { useState } from "react";
// import axios from "axios";
import {
UpdateLineChart,
UpdateFan,
UpdateOther
} 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;
const myUrl = "192.168.82.45:8081";
let websocketHome = null;
if ("WebSocket" in window) {
websocketHome = new WebSocket(
"ws://" + myUrl + "/dj-screen/websocket/1"
);
//连接成功建立的回调方法
} 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 "one": {
// @ts-ignore
dispatch(UpdateLineChart(msgData.data));
break;
}
case "two": {
// @ts-ignore
dispatch(UpdateFan(msgData.data));
break;
}
case "three": {
// @ts-ignore
dispatch(UpdateOther(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;

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

@@ -0,0 +1,11 @@
import { configureStore } from "@reduxjs/toolkit";
import HomePageReducer from "./HomePageSlice";
export const store = configureStore({
reducer: {
HomePageSlice: HomePageReducer,
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;

12
src/utils/index.ts Normal file
View File

@@ -0,0 +1,12 @@
export function generateTimeLabels (length:any) {
const totalHours = 23; // 0:00 到 23:00
const interval = totalHours / (length - 1); // 小时间隔
return Array.from({ length }, (_, index) => {
const decimalHours = interval * index;
const hours = Math.floor(decimalHours);
const minutes = Math.round((decimalHours % 1) * 60);
const safeHours = Math.min(hours, 23);
const safeMinutes = Math.min(minutes, 59);
return `${String(safeHours).padStart(2, '0')}:${String(safeMinutes).padStart(2, '0')}`;
});
};