diff --git a/package.json b/package.json index 7f67ffd..c5d3670 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "antd": "^4.15.0", "chart.js": "^3.0.2", "chartjs-adapter-date-fns": "^1.1.0-beta.1", - "chartjs-plugin-datalabels": "^2.0.0-beta.1", + "chartjs-plugin-datalabels": "^2.0.0-rc.1", "craco-less": "^1.17.1", "date-fns": "^2.20.0", "moment": "^2.29.1", diff --git a/src/components/ChartTimeOnlineFooter.jsx b/src/components/ChartTimeOnlineFooter.jsx index 4864e67..674caeb 100644 --- a/src/components/ChartTimeOnlineFooter.jsx +++ b/src/components/ChartTimeOnlineFooter.jsx @@ -32,7 +32,7 @@ export const ChartTimeOnlineFooter = (props) =>{ if(popContent) spField = -
+
{spField}
diff --git a/src/components/Display.jsx b/src/components/Display.jsx index 763b634..e39a9b7 100644 --- a/src/components/Display.jsx +++ b/src/components/Display.jsx @@ -1,11 +1,10 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import {CaretUpOutlined, CaretDownOutlined} from '@ant-design/icons' export const ValueDisplay = ({prefix, value, suffix, isArrowVisible}) =>{ const [oldVal, setOldVal] = useState(NaN) const [val, setVal] = useState('---') - - let arrow = null + const arrowRef = useRef(null); useEffect(()=>{ if(value) @@ -14,16 +13,16 @@ export const ValueDisplay = ({prefix, value, suffix, isArrowVisible}) =>{ if (isArrowVisible) { if (value > oldVal) - arrow = + arrowRef.current = () else if (value < oldVal) - arrow = + arrowRef.current = () setOldVal(value) } } else setVal(value) },[value]) - return({prefix} {val} {suffix}{arrow}) + return({prefix} {val} {suffix}{arrowRef.current}) } export const Display = (props)=>{ diff --git a/src/components/ModeDisplay.jsx b/src/components/ModeDisplay.jsx index f887aca..bc63063 100644 --- a/src/components/ModeDisplay.jsx +++ b/src/components/ModeDisplay.jsx @@ -20,8 +20,8 @@ export const ModeDisplay = (props)=>{ value = modeNames[index] ?? index } - return(<> + return(
Режим: {value} - ) +
) } \ No newline at end of file diff --git a/src/components/Notification.jsx b/src/components/Notification.jsx new file mode 100644 index 0000000..1608951 --- /dev/null +++ b/src/components/Notification.jsx @@ -0,0 +1,24 @@ +import { notification } from 'antd'; + +const typeDictionary = { + 'error': 'Ошибка', + 'warning': 'Предупрежение', + 'info': 'Информация' + +} +/** + * Вызов оповещений всплывающим окошком. + * @param body string или ReactNode + * @param type для параметра типа. Допустимые значение 'error', 'warning', 'info' + */ +export default function Notification(body, type='info'){ + + notification.info({ + description: body, + message: typeDictionary[type], + type, + placement: "bottomRight", + duration: 10, + }) + +} \ No newline at end of file diff --git a/src/components/Well.jsx b/src/components/Well.jsx index 918465d..ee45576 100644 --- a/src/components/Well.jsx +++ b/src/components/Well.jsx @@ -18,7 +18,7 @@ export default function Well() { }> Мониторинг @@ -41,7 +41,7 @@ export default function Well() { - + diff --git a/src/components/WellTreeSelector.jsx b/src/components/WellTreeSelector.jsx index 32e8531..d78c03a 100644 --- a/src/components/WellTreeSelector.jsx +++ b/src/components/WellTreeSelector.jsx @@ -46,7 +46,6 @@ export default function WellTreeSelector(props) { try { let newWells = (await WellService.getWells()).map(w => { return { key: w.id, ...w } }) let wellsTree = groupBy(newWells, 'deposit', 'cluster', 'caption') - // setWells( newWells ) setWellsTree(wellsTree) } catch (e) { diff --git a/src/components/charts/ChartTimeArchive.tsx b/src/components/charts/ChartTimeArchive.tsx new file mode 100644 index 0000000..5928d00 --- /dev/null +++ b/src/components/charts/ChartTimeArchive.tsx @@ -0,0 +1,83 @@ +import { useEffect, useState} from 'react'; +import {ChartTimeBase, ChartTimeData, ChartTimeDataParams} from './ChartTimeBase' + +const GetRandomColor = () => "#" + Math.floor(Math.random()*16777215).toString(16) + + +function GetOrCreateDatasetByLineConfig(data: ChartTimeData, lineConfig: LineConfig) { + let dataset = data?.datasets.find(d => d.label === lineConfig.label) + if (!dataset) { + let color = lineConfig.borderColor + ?? lineConfig.backgroundColor + ?? lineConfig.color + ?? GetRandomColor() + + dataset = { + label: lineConfig.label, + data: [], + backgroundColor: lineConfig.backgroundColor ?? color, + borderColor: lineConfig.borderColor ?? color, + borderWidth: lineConfig.borderWidth ?? 1, + borderDash: lineConfig.dash ?? [], + } + data.datasets.push(dataset); + } + return dataset +} + +export type LineConfig = { + type?: string + label: string + units?: string + xAccessorName: string + yAccessorName: string + color?: string + borderColor?: string + backgroundColor?: string + borderWidth?: number + dash?: number[] + labels?: any[] +} + +export type ChartTimeProps = { + label?: string, + yDisplay: Boolean, + lines: LineConfig[], + data: any[], + interval: number, +} + +export const ChartTimeArchive: React.FC = (props) => { + const [dataParams, setDataParams] = useState({ data: { datasets: [] }, yStart: new Date(), }) + + useEffect(() => { + if ((!props?.lines) + || (!props?.data) + || (props.lines.length === 0) + || (props.data.length === 0)) + return + + setDataParams((preDataParams) => { + props.lines.forEach(lineCfg => { + let dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, lineCfg) + let points = props.data.map(dataItem => { + return { + x: dataItem[lineCfg.xAccessorName], + label:0, + y: new Date(dataItem[lineCfg.yAccessorName]) + } + }) + dataset.data = points; + }); + + preDataParams.yStart = new Date() + preDataParams.yStart.setSeconds(preDataParams.yStart.getSeconds() - props.interval) + preDataParams.yInterval = props.interval + preDataParams.displayLabels = props.yDisplay + return preDataParams + }) + + }, [props.data, props.lines, props.interval, props.yDisplay]) + + return () +} \ No newline at end of file diff --git a/src/components/charts/ChartTimeBase.tsx b/src/components/charts/ChartTimeBase.tsx index 9fc607b..98e9755 100644 --- a/src/components/charts/ChartTimeBase.tsx +++ b/src/components/charts/ChartTimeBase.tsx @@ -11,8 +11,9 @@ import { ChartTypeRegistry, ChartOptions} from 'chart.js' import 'chartjs-adapter-date-fns'; +import ChartDataLabels from 'chartjs-plugin-datalabels'; -Chart.register( TimeScale, LinearScale, LineController, LineElement, PointElement, Legend ); +Chart.register( TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels ); const defaultOptions = { //maintainAspectRatio: false, @@ -63,15 +64,11 @@ const defaultOptions = { hoverRadius:5, }, }, - plugins:{ - legend:{ - display: false, - } - } } export type ChartTimeData = ChartData @@ -84,7 +81,8 @@ export type ChartTimeDataParams = { export type ChartTimeBaseProps = { dataParams: ChartTimeDataParams, - options?: ChartOptions, + // TODO: Create good type for options + options?: ChartOptions | any, } const timeUnitByInterval = (intervalSec:number):String =>{ @@ -110,7 +108,8 @@ export const ChartTimeBase: React.FC = ({options, dataParams Object.assign(thisOptions, defaultOptions, options) let newChart = new Chart(chartRef.current, { - type: 'line', + type: 'line', + plugins: [ChartDataLabels], options: thisOptions, data: dataParams.data }) diff --git a/src/components/charts/ChartTimeOnline.tsx b/src/components/charts/ChartTimeOnline.tsx index a1e85ab..99ebfff 100644 --- a/src/components/charts/ChartTimeOnline.tsx +++ b/src/components/charts/ChartTimeOnline.tsx @@ -19,6 +19,7 @@ function GetOrCreateDatasetByLineConfig (data: ChartTimeData, lineConfig: LineC borderColor: lineConfig.borderColor ?? color, borderWidth: lineConfig.borderWidth ?? 1, borderDash: lineConfig.dash ?? [], + showLine: lineConfig.showLine, } data.datasets.push(dataset); } @@ -37,6 +38,8 @@ export type LineConfig = { borderWidth?: number dash?:number[] labels?: any[] + showLine: boolean + xConstValue?: number|null } export type ChartTimeProps = { @@ -64,7 +67,8 @@ export const ChartTimeOnline: React.FC = (props) => { props.lines.forEach(lineCfg => { let dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, lineCfg) let points = props.data.map(dataItem => {return{ - x: dataItem[lineCfg.xAccessorName], + x: lineCfg.xConstValue ?? dataItem[lineCfg.xAccessorName], + label: dataItem[lineCfg.xAccessorName], y: new Date(dataItem[lineCfg.yAccessorName])} }) //dataset.data = [ ...dataset.data, ...points,].slice(-1024) @@ -75,7 +79,7 @@ export const ChartTimeOnline: React.FC = (props) => { data.splice(0, (1024 - data.length)) dataset.data = data; }); - + preDataParams.yStart = new Date() preDataParams.yStart.setSeconds(preDataParams.yStart.getSeconds() - props.interval) preDataParams.yInterval = props.interval @@ -85,6 +89,31 @@ export const ChartTimeOnline: React.FC = (props) => { }, [ props.data, props.lines, props.interval, props.yDisplay]) - return() + const chartPluginsOptions = { + plugins:{ + legend:{ + display: false, + }, + datalabels: { + backgroundColor: 'transparent', + borderRadius: 4, + color: '#000B', + display: function(context:any) { + return context.dataset.label === 'wellDepth' + ? 'auto' + : false + }, + formatter: function(value: any, context: any) { + return `${value.y.toLocaleTimeString()} ${value.label.toPrecision(4)}` + }, + padding: 6, + align: 'left', + anchor: 'center', + clip: true + } + } + } + + return() } diff --git a/src/pages/Analysis.jsx b/src/pages/Analysis.jsx index 57ba47b..17186de 100644 --- a/src/pages/Analysis.jsx +++ b/src/pages/Analysis.jsx @@ -1,3 +1,11 @@ -export default function Analysis(props){ - return(

Анализ

) - } \ No newline at end of file +// import {UserOfWells} from "../components/UserOfWells"; + +export default function Analysis(props) { + return ( +
+

Анализ

+ {/**/} +
+
+ ) +} \ No newline at end of file diff --git a/src/pages/Files.jsx b/src/pages/Files.jsx index 05d39e7..9ec760b 100644 --- a/src/pages/Files.jsx +++ b/src/pages/Files.jsx @@ -1,3 +1,7 @@ export default function Files(props) { - return (

Файлы

) + return ( +
+

Файлы

+
+ ) } \ No newline at end of file diff --git a/src/pages/Header.jsx b/src/pages/Header.jsx index 3170b0d..34b04e0 100644 --- a/src/pages/Header.jsx +++ b/src/pages/Header.jsx @@ -1,6 +1,5 @@ -import { useState } from 'react'; import { Layout, Button } from 'antd' -import { UserOutlined, MenuOutlined } from '@ant-design/icons' +import { UserOutlined } from '@ant-design/icons' import logo from '../images/logo_32.png' import { Link } from "react-router-dom" import WellTreeSelector from '../components/WellTreeSelector' @@ -8,7 +7,6 @@ import WellTreeSelector from '../components/WellTreeSelector' const { Header } = Layout export default function PageHeader(props){ - const [sidebarVisible, setSidebarVisible] = useState(true) const login = localStorage['login'] let handleLogout = () => { @@ -18,8 +16,7 @@ export default function PageHeader(props){ return( -
-