График-пончик

This commit is contained in:
Alexey 2021-07-21 15:36:08 +05:00
parent 2381eb9029
commit 17cbe374ad
6 changed files with 326 additions and 8 deletions

View File

@ -40,7 +40,7 @@ export function AnalysisDepthToInterval() {
AnalyticsService.getWellDepthToInterval(id, chartInterval)
.then(handleReceiveDepthToIntervalData)
.catch(error => {
notify(`Не удалось получить данные для Анализа Глубина-День по скважине "${id}"`,
notify(`Не удалось получить данные для Анализа скорость проходки-интервал "${id}"`,
'warning')
console.log(error)
})

View File

@ -0,0 +1,60 @@
import { useParams } from "react-router-dom"
import { DatePicker, ConfigProvider } from 'antd';
import notify from "../components/notify"
import { useState, useEffect } from 'react'
import { AnalyticsService } from '../services/api'
import { ChartOperationTime } from './charts/ChartOperationTime'
import locale from "antd/lib/locale/ru_RU";
const { RangePicker } = DatePicker
const lines = [{ labelAccessorName: "processName", pieceAccessorName: "duration" }]
export function AnalysisOperationTime() {
let { id } = useParams()
const [operationTimeData, setOperationTimeData] = useState([])
const [loader, setLoader] = useState(false)
const [range, setRange] = useState([])
let begin = null
let end = null
const onChangeRange = (range) => {
setRange(range)
}
const handleReceiveOperationTimeData = (data) => {
setOperationTimeData(data)
}
useEffect(() => {
setLoader(true)
if (range?.length > 1) {
begin = range[0].toISOString()
end = range[1].toISOString()
}
AnalyticsService.getOperationsSummary(id, begin, end)
.then(handleReceiveOperationTimeData)
.catch(error => {
notify(`Не удалось получить данные для Анализа Операция-Время по скважине "${id}" за период с ${begin} по ${end}`,
'warning')
console.log(error)
})
.finally(setLoader(false))
}, [id, range])
return (
<>
<ConfigProvider locale={locale}>
<RangePicker
showTime
onChange={onChangeRange}
/>
</ConfigProvider>
<ChartOperationTime
data={operationTimeData}
lines={lines}
/>
</>
)
}

View File

@ -220,8 +220,5 @@ export const ChartDepthToIntervalBase: React.FC<ChartTimeBaseProps> = ({ options
chart.update()
}, [chart, dataParams, options])
return (<canvas style={{
width: "100px",
height: "100px"
}} ref={chartRef} />)
return (<canvas ref={chartRef} />)
}

View File

@ -0,0 +1,67 @@
import { useEffect, useState } from 'react'
import { ChartOpertationTimeBase } from './ChartOperationTimeBase'
import moment from 'moment'
export const CreateLabels = (lineConfig) => {
let labels = []
return labels
}
export const CreateData = (lineConfig) => {
let datasets = {
label: lineConfig.label,
data: [],
backgroundColor: [
'#f00', '#ff0', '#f0f', '#0ff', '#00f', '#0f0'
],
}
return datasets
}
export const ChartOperationTime = ({ lines, data, rangeDate }) => {
const [opertationTimeDataParams, setOpertationTimeDataParams] = useState({ data: { labels: [], datasets: [] } })
useEffect(() => {
if ((!lines)
|| (!data))
return
let newLabels = lines.map(lineCfg => {
let labels = CreateLabels(lineCfg)
if (data.length !== 0)
labels = data.map(dataItem => {
return dataItem[lineCfg.labelAccessorName]
})
return labels
})
let newDatasets = lines.map(lineCfg => {
let datasets = CreateData(lineCfg)
if (data.length !== 0)
datasets.data = data.map(dataItem => {
return dataItem[lineCfg.pieceAccessorName]
})
return datasets
})
let interval = rangeDate ? (rangeDate[1] - rangeDate[0]) / 1000 : null
let startDate = rangeDate ? rangeDate[0] : moment()
let newParams = {
xInterval: interval,
xStart: startDate,
data: {
labels: newLabels,
datasets: newDatasets
}
}
setOpertationTimeDataParams(newParams)
console.log(newParams)
}, [data, lines, rangeDate])
return (<>
<ChartOpertationTimeBase dataParams={opertationTimeDataParams} />
</>
)
}

View File

@ -0,0 +1,177 @@
import { useEffect, useRef, useState } from 'react'
import {
Chart,
ArcElement,
TimeScale,
Legend,
PointElement,
ChartData,
ChartTypeRegistry,
ChartOptions,
DoughnutController,
} from 'chart.js'
import 'chartjs-adapter-moment'
import ChartDataLabels from 'chartjs-plugin-datalabels'
Chart.register(TimeScale, DoughnutController, PointElement, ArcElement, Legend, ChartDataLabels)
const defaultOptions = {
responsive: true,
title: {
display: true,
position: "top",
text: "Doughnut Chart",
fontSize: 18,
fontColor: "#111"
},
legend: {
display: true,
position: "bottom",
labels: {
fontColor: "#333",
fontSize: 16
}
}
}
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
x: String;
label: number;
y: number;
}[], unknown>
export type ChartTimeDataParams = {
data: ChartTimeData,
xStart?: Date,
xInterval?: number,
displayLabels?: Boolean,
}
export type ChartTimeBaseProps = {
dataParams: ChartTimeDataParams,
// TODO: Create good type for options
options?: ChartOptions<keyof ChartTypeRegistry> | any,
}
export type TimeParams = {
unit: String
stepSize: number
}
const linesPerInterval = 32
export const timeUnitByInterval = (intervalSec: number): String => {
if (intervalSec <= 60)
return 'millisecond'
if (intervalSec <= 32 * 60)
return 'second'
if (intervalSec <= 32 * 60 * 60)
return 'minute'
if (intervalSec <= 32 * 12 * 60 * 60)
return 'hour'
if (intervalSec <= 32 * 24 * 60 * 60)
return 'day'
if (intervalSec <= 32 * 7 * 24 * 60 * 60)
return 'week'
if (intervalSec <= 32 * 30.4375 * 24 * 60 * 60)
return 'month'
if (intervalSec <= 32 * 121.75 * 24 * 60 * 60)
return 'quarter'
else
return 'year'
}
export const timeParamsByInterval = (intervalSec: number): TimeParams => {
let stepSize = intervalSec
let unit = timeUnitByInterval(intervalSec)
switch (unit) {
case "millisecond":
stepSize *= 1000
break;
case "second":
//stepSize *= 1
break;
case "minute":
stepSize /= 60
break;
case "hour":
stepSize /= 60 * 60
break;
case "day":
stepSize /= 24 * 60 * 60
break;
case "week":
stepSize /= 7 * 24 * 60 * 60
break;
case "month":
stepSize /= 30 * 24 * 60 * 60
break;
case "quarter":
stepSize /= 91 * 24 * 60 * 60
break;
case "year":
stepSize /= 365.25 * 24 * 60 * 60
break;
}
stepSize = Math.round(stepSize / linesPerInterval)
stepSize = stepSize > 0 ? stepSize : 1;
return { unit, stepSize }
}
export const ChartOpertationTimeBase: React.FC<ChartTimeBaseProps> = ({ options, dataParams }) => {
const chartRef = useRef<HTMLCanvasElement>(null)
const [chart, setChart] = useState<any>()
useEffect(() => {
if ((chartRef.current) && (!chart)) {
let thisOptions = {}
Object.assign(thisOptions, defaultOptions, options)
let newChart = new Chart(chartRef.current, {
type: 'doughnut',
plugins: [ChartDataLabels],
options: thisOptions,
data: dataParams.data
})
setChart(newChart)
return () => chart?.destroy()
}
}, [chart, options, dataParams])
useEffect(() => {
if (!chart)
return
chart.data = dataParams.data
chart.options.aspectRatio = options?.aspectRatio
if (dataParams.xStart) {
let interval = Number(dataParams.xInterval ?? 600)
let start = new Date(dataParams.xStart)
let end = new Date(dataParams.xStart)
end.setSeconds(end.getSeconds() + interval)
let { unit, stepSize } = timeParamsByInterval(interval)
if (chart.options.scales?.x) {
chart.options.scales.x.max = end.getTime()
chart.options.scales.x.min = start.getTime()
chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true
chart.options.scales.x.time.unit = unit
chart.options.scales.x.time.stepSize = stepSize
}
}
chart.update()
}, [chart, dataParams, options])
return (<canvas ref={chartRef} />)
}

View File

@ -1,13 +1,30 @@
import { AnalysisDepthToDay } from '../components/AnalysisDepthToDay'
import { AnalysisDepthToInterval } from '../components/AnalysisDepthToInterval'
import { AnalysisOperationTime } from '../components/AnalysisOperationTime'
import { Row, Col } from 'antd'
export default function Analysis() {
return (
<>
<Row>
<Col span={10}>
<AnalysisDepthToDay />
<div style={{ marginTop: '100px' }}>&nbsp;</div>
</Col>
<Col span={2}></Col>
<Col span={10}>
<AnalysisDepthToInterval />
</Col>
</Row>
<Row>
<Col span={10}>
<AnalysisOperationTime />
</Col>
<Col span={2}></Col>
<Col span={10}>
<AnalysisDepthToDay />
</Col>
</Row>
</>
)
}