forked from ddrilling/asb_cloud_front
* Выполнен базовый перенос TVD на D3
* Добавлена возможность указания отдельных массивов данных для датасетов * Добавлен проп форматирования тиков
This commit is contained in:
parent
571d8de440
commit
ad66af1a10
@ -50,7 +50,7 @@ const createAxis = <DataType,>(config: ChartAxis<DataType>) => {
|
|||||||
export type D3ChartProps<DataType = DefaultDataType> = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
|
export type D3ChartProps<DataType = DefaultDataType> = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
|
||||||
xAxis: ChartAxis<DataType>
|
xAxis: ChartAxis<DataType>
|
||||||
datasets: ChartDataset<DataType>[]
|
datasets: ChartDataset<DataType>[]
|
||||||
data?: DataType[]
|
data?: DataType[] | Record<string, DataType[]>
|
||||||
domain?: Partial<ChartDomain>
|
domain?: Partial<ChartDomain>
|
||||||
width?: number | string
|
width?: number | string
|
||||||
height?: number | string
|
height?: number | string
|
||||||
@ -58,7 +58,7 @@ export type D3ChartProps<DataType = DefaultDataType> = React.DetailedHTMLProps<R
|
|||||||
offset?: Partial<ChartOffset>
|
offset?: Partial<ChartOffset>
|
||||||
animDurationMs?: number
|
animDurationMs?: number
|
||||||
backgroundColor?: Property.Color
|
backgroundColor?: Property.Color
|
||||||
ticks?: ChartTicks
|
ticks?: ChartTicks<DataType>
|
||||||
plugins?: {
|
plugins?: {
|
||||||
menu?: BasePluginSettings & D3ContextMenuSettings
|
menu?: BasePluginSettings & D3ContextMenuSettings
|
||||||
tooltip?: BasePluginSettings & D3TooltipSettings<DataType>
|
tooltip?: BasePluginSettings & D3TooltipSettings<DataType>
|
||||||
@ -104,11 +104,26 @@ export const D3Chart = memo<D3ChartProps<DefaultDataType>>(({
|
|||||||
if (!data) return
|
if (!data) return
|
||||||
|
|
||||||
const xAxis = createAxis(xAxisConfig)
|
const xAxis = createAxis(xAxisConfig)
|
||||||
|
|
||||||
const [minX, maxX] = d3.extent(data, getX)
|
|
||||||
xAxis.domain([domain?.x?.min ?? minX, domain?.x?.max ?? maxX])
|
|
||||||
xAxis.range([0, width - offset.left - offset.right])
|
xAxis.range([0, width - offset.left - offset.right])
|
||||||
|
|
||||||
|
if (domain?.x?.min && domain?.x?.max) {
|
||||||
|
xAxis.domain([domain.x.min, domain.x.max])
|
||||||
|
return xAxis
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
const [minX, maxX] = d3.extent(data, getX)
|
||||||
|
xAxis.domain([domain?.x?.min ?? minX, domain?.x?.max ?? maxX])
|
||||||
|
} else {
|
||||||
|
let [minX, maxX] = [Infinity, -Infinity]
|
||||||
|
for (const key in data) {
|
||||||
|
const [min, max] = d3.extent(data[key], getX)
|
||||||
|
if (min < minX) minX = min
|
||||||
|
if (max > maxX) maxX = max
|
||||||
|
}
|
||||||
|
xAxis.domain([domain?.x?.min ?? minX, domain?.x?.max ?? maxX])
|
||||||
|
}
|
||||||
|
|
||||||
return xAxis
|
return xAxis
|
||||||
}, [xAxisConfig, getX, data, domain, width, offset])
|
}, [xAxisConfig, getX, data, domain, width, offset])
|
||||||
|
|
||||||
@ -127,11 +142,21 @@ export const D3Chart = memo<D3ChartProps<DefaultDataType>>(({
|
|||||||
|
|
||||||
let minY = Infinity
|
let minY = Infinity
|
||||||
let maxY = -Infinity
|
let maxY = -Infinity
|
||||||
charts.forEach(({ y }) => {
|
if (Array.isArray(data)) {
|
||||||
const [min, max] = d3.extent(data, y)
|
charts.forEach(({ y }) => {
|
||||||
if (min && min < minY) minY = min
|
const [min, max] = d3.extent(data, y)
|
||||||
if (max && max > maxY) maxY = max
|
if (min && min < minY) minY = min
|
||||||
})
|
if (max && max > maxY) maxY = max
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
for (const key in data) {
|
||||||
|
const chart = charts.find((chart) => chart.key === key)
|
||||||
|
if (!chart) continue
|
||||||
|
const [min, max] = d3.extent(data[key], chart.y)
|
||||||
|
if (min && min < minY) minY = min
|
||||||
|
if (max && max > maxY) maxY = max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
yAxis.domain([
|
yAxis.domain([
|
||||||
domain?.y?.min ?? minY,
|
domain?.y?.min ?? minY,
|
||||||
@ -149,7 +174,7 @@ export const D3Chart = memo<D3ChartProps<DefaultDataType>>(({
|
|||||||
.duration(animDurationMs)
|
.duration(animDurationMs)
|
||||||
.call(d3.axisBottom(xAxis)
|
.call(d3.axisBottom(xAxis)
|
||||||
.tickSize((ticks?.x?.visible ?? false) ? -height + offset.bottom : 0)
|
.tickSize((ticks?.x?.visible ?? false) ? -height + offset.bottom : 0)
|
||||||
.tickFormat((d) => formatDate(d, undefined, 'YYYY-MM-DD') || 'NaN')
|
.tickFormat((d) => ticks?.x?.format?.(d) ?? String(d))
|
||||||
.ticks(ticks?.x?.count ?? 10) as any // TODO: Исправить тип
|
.ticks(ticks?.x?.count ?? 10) as any // TODO: Исправить тип
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -223,7 +248,8 @@ export const D3Chart = memo<D3ChartProps<DefaultDataType>>(({
|
|||||||
.attr('opacity', chart.opacity ?? 1)
|
.attr('opacity', chart.opacity ?? 1)
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
|
|
||||||
let d = data
|
let d = Array.isArray(data) ? data : data[String(chart.key).split(':')[0]]
|
||||||
|
if (!d) return
|
||||||
let elms
|
let elms
|
||||||
|
|
||||||
switch (chart.type) {
|
switch (chart.type) {
|
||||||
@ -294,15 +320,13 @@ export const D3Chart = memo<D3ChartProps<DefaultDataType>>(({
|
|||||||
.attr('stroke', chart.point?.strokeColor ?? null)
|
.attr('stroke', chart.point?.strokeColor ?? null)
|
||||||
.attr('fill', chart.point?.fillColor ?? null)
|
.attr('fill', chart.point?.fillColor ?? null)
|
||||||
|
|
||||||
elms = chart().selectAll()
|
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
chart.afterDraw?.(elms)
|
chart.afterDraw?.(chart)
|
||||||
})
|
})
|
||||||
}, [charts, data, xAxis, yAxis, height])
|
}, [charts, data, xAxis, yAxis, height])
|
||||||
|
|
||||||
@ -311,15 +335,17 @@ export const D3Chart = memo<D3ChartProps<DefaultDataType>>(({
|
|||||||
}, [redrawCharts])
|
}, [redrawCharts])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoaderPortal show={loading}>
|
<LoaderPortal
|
||||||
|
show={loading}
|
||||||
|
style={{
|
||||||
|
width: givenWidth,
|
||||||
|
height: givenHeight,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
{...other}
|
{...other}
|
||||||
ref={rootRef}
|
ref={rootRef}
|
||||||
className={`asb-d3-chart ${className}`}
|
className={`asb-d3-chart ${className}`}
|
||||||
style={{
|
|
||||||
width: givenWidth,
|
|
||||||
height: givenHeight,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{data ? (
|
{data ? (
|
||||||
<D3ContextMenu {...plugins?.menu} svg={svgRef}>
|
<D3ContextMenu {...plugins?.menu} svg={svgRef}>
|
||||||
|
@ -60,9 +60,13 @@ export type ChartOffset = {
|
|||||||
right: number
|
right: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChartTicks = {
|
export type ChartTicks<DataType> = {
|
||||||
color?: Property.Color
|
color?: Property.Color
|
||||||
x?: { visible?: boolean, count?: number }
|
x?: {
|
||||||
|
visible?: boolean,
|
||||||
|
count?: number,
|
||||||
|
format?: (d: d3.NumberValue) => string,
|
||||||
|
}
|
||||||
y?: { visible?: boolean, count?: number }
|
y?: { visible?: boolean, count?: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { memo, useCallback, useMemo, useState } from 'react'
|
import { memo, useCallback, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { D3Chart } from '@components/d3'
|
import { D3Chart } from '@components/d3'
|
||||||
|
import { formatDate } from '@utils'
|
||||||
|
|
||||||
import '@styles/detected_operations.less'
|
import '@styles/detected_operations.less'
|
||||||
|
|
||||||
@ -45,6 +46,12 @@ const xAxis = {
|
|||||||
accessor: (row) => new Date(row.dateStart),
|
accessor: (row) => new Date(row.dateStart),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ticks = {
|
||||||
|
color: '#F9F2ED',
|
||||||
|
x: { visible: true, format: (d) => formatDate(d, undefined, 'YYYY-MM-DD') },
|
||||||
|
y: { visible: true },
|
||||||
|
}
|
||||||
|
|
||||||
export const OperationsChart = memo(({ data, yDomain, height }) => {
|
export const OperationsChart = memo(({ data, yDomain, height }) => {
|
||||||
const [isChartLoading, setIsChartLoading] = useState(false)
|
const [isChartLoading, setIsChartLoading] = useState(false)
|
||||||
|
|
||||||
@ -58,6 +65,21 @@ export const OperationsChart = memo(({ data, yDomain, height }) => {
|
|||||||
setTimeout(() => setIsChartLoading(false), 2000)
|
setTimeout(() => setIsChartLoading(false), 2000)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const plugins = useMemo(() => ({
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
type: 'nearest',
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
cursor: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
enabled: false,
|
||||||
|
onUpdate: onChartUpdate,
|
||||||
|
}
|
||||||
|
}), [onChartUpdate])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<D3Chart
|
<D3Chart
|
||||||
xAxis={xAxis}
|
xAxis={xAxis}
|
||||||
@ -66,21 +88,8 @@ export const OperationsChart = memo(({ data, yDomain, height }) => {
|
|||||||
data={data}
|
data={data}
|
||||||
loading={isChartLoading}
|
loading={isChartLoading}
|
||||||
height={height}
|
height={height}
|
||||||
plugins={{
|
plugins={plugins}
|
||||||
tooltip: {
|
ticks={ticks}
|
||||||
enabled: true,
|
|
||||||
type: 'nearest',
|
|
||||||
limit: 10,
|
|
||||||
},
|
|
||||||
cursor: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
menu: {
|
|
||||||
enabled: false,
|
|
||||||
onUpdate: onChartUpdate,
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
ticks={{ color: '#F9F2ED', y: { visible: true }, x: { visible: true } }}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,26 +1,14 @@
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { memo, useState, useRef, useEffect, useCallback, useMemo } from 'react'
|
import { memo, useState, useEffect, useCallback, useMemo } from 'react'
|
||||||
import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons'
|
import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons'
|
||||||
import { Switch, Button } from 'antd'
|
import { Switch, Button } from 'antd'
|
||||||
|
|
||||||
import {
|
|
||||||
Chart,
|
|
||||||
TimeScale,
|
|
||||||
LinearScale,
|
|
||||||
Legend,
|
|
||||||
LineController,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Tooltip
|
|
||||||
} from 'chart.js'
|
|
||||||
import 'chartjs-adapter-moment'
|
|
||||||
import zoomPlugin from 'chartjs-plugin-zoom'
|
|
||||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
|
||||||
|
|
||||||
import { useIdWell } from '@asb/context'
|
import { useIdWell } from '@asb/context'
|
||||||
|
import { D3Chart } from '@components/d3'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { formatDate, fractionalSum, wrapPrivateComponent, getOperations } from '@utils'
|
import { formatDate, fractionalSum, wrapPrivateComponent, getOperations } from '@utils'
|
||||||
|
import { unique } from '@utils/filters'
|
||||||
|
|
||||||
import NptTable from './NptTable'
|
import NptTable from './NptTable'
|
||||||
import NetGraphExport from './NetGraphExport'
|
import NetGraphExport from './NetGraphExport'
|
||||||
@ -28,97 +16,96 @@ import AdditionalTables from './AdditionalTables'
|
|||||||
|
|
||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
import '@styles/tvd.less'
|
import '@styles/tvd.less'
|
||||||
import { unique } from '@asb/utils/filters'
|
|
||||||
|
|
||||||
Chart.register(
|
const datasets = [{
|
||||||
TimeScale,
|
key: 'fact',
|
||||||
LinearScale,
|
label: 'Факт',
|
||||||
LineController,
|
type: 'line',
|
||||||
LineElement,
|
color: '#0A0',
|
||||||
PointElement,
|
width: 3,
|
||||||
Legend,
|
yAxis: {
|
||||||
ChartDataLabels,
|
|
||||||
zoomPlugin,
|
|
||||||
Tooltip,
|
|
||||||
)
|
|
||||||
|
|
||||||
const numericRender = (value) => Number.isFinite(value) ? (+value).toFixed(2) : '-'
|
|
||||||
|
|
||||||
const scaleTypes = {
|
|
||||||
day: {
|
|
||||||
min: 0,
|
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
display: true,
|
accessor: (row) => row.depth,
|
||||||
title: { display: false, text: '' },
|
unit: 'м',
|
||||||
ticks: { stepSize: 1 }
|
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
key: 'plan',
|
||||||
|
label: 'План',
|
||||||
|
type: 'line',
|
||||||
|
color: '#F00',
|
||||||
|
width: 3,
|
||||||
|
yAxis: {
|
||||||
|
type: 'linear',
|
||||||
|
accessor: (row) => row.depth,
|
||||||
|
unit: 'м',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
key: 'predict',
|
||||||
|
label: 'Прогноз',
|
||||||
|
type: 'line',
|
||||||
|
color: 'purple',
|
||||||
|
width: 1,
|
||||||
|
afterDraw: (d) => d().selectAll('path').attr('stroke-dasharray', [7, 3]),
|
||||||
|
yAxis: {
|
||||||
|
type: 'linear',
|
||||||
|
accessor: 'depth',
|
||||||
|
unit: 'м',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
key: 'withoutNpt',
|
||||||
|
label: '',
|
||||||
|
type: 'line',
|
||||||
|
color: '#00F',
|
||||||
|
width: 3,
|
||||||
|
yAxis: {
|
||||||
|
type: 'linear',
|
||||||
|
accessor: 'depth',
|
||||||
|
unit: 'м',
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
|
||||||
|
const xAxis = {
|
||||||
date: {
|
date: {
|
||||||
display: true,
|
|
||||||
title: { display: true },
|
|
||||||
type: 'time',
|
type: 'time',
|
||||||
time: { unit: 'day', displayFormats: { day: 'DD.MM.YYYY' } },
|
accessor: (row) => new Date(row.dateStart),
|
||||||
grid: { drawTicks: true },
|
},
|
||||||
ticks: {
|
day: {
|
||||||
stepSize: 3,
|
type: 'linear',
|
||||||
major: { enabled: true },
|
accessor: 'day',
|
||||||
z: 1,
|
unit: 'день',
|
||||||
display: true,
|
|
||||||
textStrokeColor: '#fff',
|
|
||||||
textStrokeWidth: 2,
|
|
||||||
color: '#000',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions = {
|
const ticks = {
|
||||||
responsive: true,
|
day: {
|
||||||
maintainAspectRatio: false,
|
x: {
|
||||||
aspectRatio: false,
|
visible: true,
|
||||||
interaction: { intersect: false, mode: 'point' },
|
format: (d) => d,
|
||||||
scales: {
|
|
||||||
x: scaleTypes.day,
|
|
||||||
y: {
|
|
||||||
type: 'linear',
|
|
||||||
position: 'top',
|
|
||||||
reverse: true,
|
|
||||||
display: true,
|
|
||||||
title: { display: false, text: '' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parsing: { xAxisKey: 'day', yAxisKey: 'depth' },
|
|
||||||
elements: { point: { radius: 1.7 } },
|
|
||||||
plugins: {
|
|
||||||
legend: { display: true },
|
|
||||||
datalabels: { display: false },
|
|
||||||
tooltip: {
|
|
||||||
enabled: true,
|
|
||||||
position: 'nearest',
|
|
||||||
callbacks: {
|
|
||||||
title: (items) => [
|
|
||||||
`Дата: ${formatDate(items[0].raw?.date) ?? '-'}`,
|
|
||||||
`День с начала бурения: ${parseInt(items[0].raw?.day)}`,
|
|
||||||
],
|
|
||||||
afterTitle: (items) => `Глубина: ${numericRender(items[0].raw?.depth)}`,
|
|
||||||
label: (item) => [
|
|
||||||
item.raw.wellSectionTypeName + ': ' + item.raw.categoryName,
|
|
||||||
`Длительность (ч): ${item.raw.nptHours}`
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
y: { visible: true },
|
||||||
},
|
},
|
||||||
|
date: {
|
||||||
|
x: {
|
||||||
|
visible: true,
|
||||||
|
format: (d) => formatDate(d, undefined, 'YYYY-MM-DD'),
|
||||||
|
},
|
||||||
|
y: { visible: true },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeDataset = (data, label, color, borderWidth = 1.5, borderDash) => ({
|
const domain = {
|
||||||
label,
|
date: {
|
||||||
data,
|
y: { min: 4500, max: 0 },
|
||||||
backgroundColor: color,
|
},
|
||||||
borderColor: color,
|
day: {
|
||||||
borderWidth,
|
x: { min: 0 },
|
||||||
borderDash,
|
y: { min: 4500, max: 0 },
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const numericRender = (value) => Number.isFinite(value) ? (+value).toFixed(2) : '-'
|
||||||
|
|
||||||
const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
||||||
const [chart, setChart] = useState()
|
|
||||||
const [xLabel, setXLabel] = useState('day')
|
const [xLabel, setXLabel] = useState('day')
|
||||||
const [operations, setOperations] = useState({})
|
const [operations, setOperations] = useState({})
|
||||||
const [tableVisible, setTableVisible] = useState(false)
|
const [tableVisible, setTableVisible] = useState(false)
|
||||||
@ -127,7 +114,6 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
const idWellContext = useIdWell()
|
const idWellContext = useIdWell()
|
||||||
const idWell = useMemo(() => wellId ?? idWellContext, [wellId, idWellContext])
|
const idWell = useMemo(() => wellId ?? idWellContext, [wellId, idWellContext])
|
||||||
|
|
||||||
const chartRef = useRef(null)
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const onPointClick = useCallback((e) => {
|
const onPointClick = useCallback((e) => {
|
||||||
@ -141,15 +127,11 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
const ids = points.map((p) => p.raw.id).filter(Boolean).filter(unique).join(',')
|
const ids = points.map((p) => p.raw.id).filter(Boolean).filter(unique).join(',')
|
||||||
navigate(`/well/${idWell}/operations/${datasetName}/?selectedId=${ids}`)
|
navigate(`/well/${idWell}/operations/${datasetName}/?selectedId=${ids}`)
|
||||||
}, [idWell, navigate])
|
}, [idWell, navigate])
|
||||||
|
|
||||||
useEffect(() => {
|
const toogleTable = useCallback(() => {
|
||||||
invokeWebApiWrapperAsync(
|
setOperations(pre => ({ ...pre }))
|
||||||
async () => setOperations(await getOperations(idWell)),
|
setTableVisible(v => !v)
|
||||||
setIsLoading,
|
}, [])
|
||||||
`Не удалось загрузить операции по скважине "${idWell}"`,
|
|
||||||
'Получение списка опервций по скважине'
|
|
||||||
)
|
|
||||||
}, [idWell])
|
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
const withoutNpt = []
|
const withoutNpt = []
|
||||||
@ -163,46 +145,17 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return { datasets: [
|
return { ...operations, withoutNpt }
|
||||||
makeDataset(operations?.fact, 'Факт', '#0A0', 3),
|
|
||||||
makeDataset(operations?.predict, 'Прогноз', 'purple', 1, [7, 3]),
|
|
||||||
makeDataset(operations?.plan, 'План', '#F00', 3),
|
|
||||||
makeDataset(withoutNpt, 'Факт без НПВ', '#00F', 3)
|
|
||||||
]}
|
|
||||||
}, [operations])
|
}, [operations])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!chartRef.current) return
|
invokeWebApiWrapperAsync(
|
||||||
const options = {}
|
async () => setOperations(await getOperations(idWell)),
|
||||||
Object.assign(options, defaultOptions)
|
setIsLoading,
|
||||||
|
`Не удалось загрузить операции по скважине "${idWell}"`,
|
||||||
const newChart = new Chart(chartRef.current, {
|
'Получение списка опервций по скважине'
|
||||||
type: 'line',
|
)
|
||||||
options: options,
|
}, [idWell])
|
||||||
plugins: [ChartDataLabels],
|
|
||||||
data: { datasets: [] },
|
|
||||||
})
|
|
||||||
setChart(newChart)
|
|
||||||
|
|
||||||
return () => newChart?.destroy()
|
|
||||||
}, [chartRef])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!chart) return
|
|
||||||
chart.data = chartData
|
|
||||||
chart.options.onClick = onPointClick
|
|
||||||
chart.options.scales.x = scaleTypes[xLabel]
|
|
||||||
chart.options.parsing.xAxisKey = xLabel
|
|
||||||
chart.update()
|
|
||||||
// Обнуление ширины необходимо для уменьшения размена при resize после появления элементов
|
|
||||||
chart.canvas.parentNode.style.width = '0'
|
|
||||||
chart.resize()
|
|
||||||
}, [chart, chartData, xLabel, onPointClick])
|
|
||||||
|
|
||||||
const toogleTable = useCallback(() => {
|
|
||||||
setOperations(pre => ({ ...pre }))
|
|
||||||
setTableVisible(v => !v)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'container tvd-page'} {...other}>
|
<div className={'container tvd-page'} {...other}>
|
||||||
@ -224,7 +177,19 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
<div className={'tvd-main'}>
|
<div className={'tvd-main'}>
|
||||||
<div className={'tvd-left'}>
|
<div className={'tvd-left'}>
|
||||||
<AdditionalTables operations={operations} xLabel={xLabel} setIsLoading={setIsLoading} />
|
<AdditionalTables operations={operations} xLabel={xLabel} setIsLoading={setIsLoading} />
|
||||||
<canvas ref={chartRef} />
|
<D3Chart
|
||||||
|
xAxis={xAxis[xLabel]}
|
||||||
|
data={chartData}
|
||||||
|
domain={domain[xLabel]}
|
||||||
|
datasets={datasets}
|
||||||
|
loading={isLoading}
|
||||||
|
ticks={ticks[xLabel]}
|
||||||
|
plugins={{
|
||||||
|
tooltip: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{tableVisible && <NptTable operations={operations?.fact} />}
|
{tableVisible && <NptTable operations={operations?.fact} />}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
.asb-d3-chart {
|
.asb-d3-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
& .tooltip {
|
& .tooltip {
|
||||||
@color: white;
|
@color: white;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
> div {
|
> .tvd-tr-table, > .tvd-bl-table {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
//pointer-events: none;
|
//pointer-events: none;
|
||||||
transition: opacity .25s ease-out;
|
transition: opacity .25s ease-out;
|
||||||
|
Loading…
Reference in New Issue
Block a user