forked from ddrilling/asb_cloud_front
Добавлены страницы ЕСО и статистики по операциям на ГГД
This commit is contained in:
parent
a031ee9d8b
commit
765e2e820e
151
src/pages/WellOperations/Tvd/TLChart.jsx
Normal file
151
src/pages/WellOperations/Tvd/TLChart.jsx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import { memo, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useElementSize } from 'usehooks-ts'
|
||||||
|
import { Empty } from 'antd'
|
||||||
|
import moment from 'moment'
|
||||||
|
import * as d3 from 'd3'
|
||||||
|
|
||||||
|
import { useIdWell } from '@asb/context'
|
||||||
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import { DetectedOperationService } from '@api'
|
||||||
|
import { formatDate } from '@utils'
|
||||||
|
|
||||||
|
const defaultOffset = { left: 40, right: 20, top: 20, bottom: 20 }
|
||||||
|
const zeroDate = moment('2000-01-01 00:00:00')
|
||||||
|
|
||||||
|
const applyTime = (date) => moment(`${zeroDate.format('YYYY-MM-DD')} ${date.format('HH:mm:ss')}`)
|
||||||
|
|
||||||
|
const splitByDate = (startTime, endTime) => {
|
||||||
|
if (startTime.isSame(endTime, 'day'))
|
||||||
|
return [{ startTime, endTime }]
|
||||||
|
|
||||||
|
const out = []
|
||||||
|
let date = moment(startTime).startOf('day').add(1, 'day')
|
||||||
|
|
||||||
|
out.push({ startTime, endTime: moment(date).subtract(1, 'ms') })
|
||||||
|
|
||||||
|
while(!date.isSame(endTime, 'day')) {
|
||||||
|
const newDate = moment(date).add(1, 'day')
|
||||||
|
out.push({ startTime: date, endTime: moment(newDate).subtract(1, 'ms') })
|
||||||
|
date = newDate
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push({ startTime: date, endTime })
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TLChart = memo(({
|
||||||
|
backgroundColor = '#0000',
|
||||||
|
barHeight = 15,
|
||||||
|
offset = defaultOffset,
|
||||||
|
color,
|
||||||
|
}) => {
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [svgRef, setSvgRef] = useState()
|
||||||
|
const [data, setData] = useState()
|
||||||
|
|
||||||
|
const [rootRef, { width, height }] = useElementSize()
|
||||||
|
|
||||||
|
const idWell = useIdWell()
|
||||||
|
|
||||||
|
const dates = useMemo(() => {
|
||||||
|
if (!data || data.length <= 0) return [0, 0]
|
||||||
|
return [
|
||||||
|
d3.min(data, (d) => moment(d.dateStart)).startOf('day'),
|
||||||
|
d3.max(data, (d) => moment(d.dateEnd)).endOf('day'),
|
||||||
|
]
|
||||||
|
}, [data])
|
||||||
|
|
||||||
|
const xAxis = useMemo(() => d3.scaleTime()
|
||||||
|
.range([0, width - offset.left - offset.right])
|
||||||
|
.domain([zeroDate, moment(zeroDate).endOf('day')])
|
||||||
|
, [width, offset])
|
||||||
|
|
||||||
|
const yAxis = useMemo(() => d3.scaleTime()
|
||||||
|
.range([0, height - offset.top - offset.bottom - barHeight])
|
||||||
|
.domain(dates)
|
||||||
|
, [height, offset, barHeight, dates])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
invokeWebApiWrapperAsync(
|
||||||
|
async () => {
|
||||||
|
const { operations } = await DetectedOperationService.get(idWell)
|
||||||
|
setData(operations.map((raw) => {
|
||||||
|
const startTime = moment(raw.dateStart)
|
||||||
|
const endTime = moment(raw.dateEnd)
|
||||||
|
return splitByDate(startTime, endTime).map((dt) => ({
|
||||||
|
...raw,
|
||||||
|
startTime: dt.startTime,
|
||||||
|
endTime: dt.endTime,
|
||||||
|
}))
|
||||||
|
}).flat())
|
||||||
|
},
|
||||||
|
setIsLoading,
|
||||||
|
'Не удалось загрузить список операций'
|
||||||
|
)
|
||||||
|
}, [idWell])
|
||||||
|
|
||||||
|
useEffect(() => { // Рисуем ось X
|
||||||
|
const xAxisArea = d3.select(svgRef).select('.axis.x')
|
||||||
|
xAxisArea.call(d3.axisTop(xAxis)
|
||||||
|
.tickSize(offset.top + offset.bottom - height)
|
||||||
|
.tickFormat((d) => formatDate(d, undefined, 'HH:mm:ss'))
|
||||||
|
.ticks(d3.timeHour.every(3))
|
||||||
|
)
|
||||||
|
|
||||||
|
xAxisArea.selectAll('.tick line')
|
||||||
|
.attr('stroke', 'black')
|
||||||
|
.attr('stroke-dasharray', [5, 3])
|
||||||
|
}, [svgRef, xAxis, height, offset])
|
||||||
|
|
||||||
|
useEffect(() => { // Рисуем ось Y
|
||||||
|
d3.select(svgRef)
|
||||||
|
.select('.axis.y')
|
||||||
|
.call(d3.axisLeft(yAxis)
|
||||||
|
.tickSize(0)
|
||||||
|
.ticks(d3.timeDay.every(1))
|
||||||
|
.tickFormat((d) => moment(d).format('DD.MM'))
|
||||||
|
)
|
||||||
|
}, [svgRef, yAxis, height, offset, dates])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data) return
|
||||||
|
const elms = d3.select(svgRef).select('.chart-area').selectAll('rect').data(data)
|
||||||
|
elms.exit().remove()
|
||||||
|
const newElms = elms.enter().append('rect')
|
||||||
|
elms.merge(newElms)
|
||||||
|
.attr('x', (d) => xAxis(applyTime(d.startTime)))
|
||||||
|
.attr('y', (d) => yAxis(moment(d.startTime).startOf('day')) - barHeight / 2)
|
||||||
|
.attr('width', (d) => xAxis(d.endTime) - xAxis(d.startTime))
|
||||||
|
.attr('height', barHeight)
|
||||||
|
.attr('fill', (d) => color ? color(d.idCategory) : '#0008')
|
||||||
|
}, [svgRef, xAxis, yAxis, data, color])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'tvd-right'} ref={rootRef}>
|
||||||
|
<LoaderPortal show={isLoading} style={{ width: '100%', flexGrow: 1 }}>
|
||||||
|
{data ? (
|
||||||
|
<svg ref={setSvgRef} width={'100%'} height={'100%'}>
|
||||||
|
<g className={'axis x'} transform={`translate(${offset.left}, ${offset.top})`} />
|
||||||
|
<g className={'axis y'} transform={`translate(${offset.left}, ${offset.top + barHeight})`} />
|
||||||
|
<g className={'chart-area'} transform={`translate(${offset.left}, ${offset.top + barHeight})`} stroke={'none'} />
|
||||||
|
<rect
|
||||||
|
x={offset.left}
|
||||||
|
y={offset.top}
|
||||||
|
width={Math.max(width - offset.left - offset.right, 0)}
|
||||||
|
height={Math.max(height - offset.top - offset.bottom, 0)}
|
||||||
|
fill={backgroundColor}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<div style={{ height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
<Empty />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</LoaderPortal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default TLChart
|
116
src/pages/WellOperations/Tvd/TLPie.jsx
Normal file
116
src/pages/WellOperations/Tvd/TLPie.jsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { memo, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useElementSize } from 'usehooks-ts'
|
||||||
|
import { Empty } from 'antd'
|
||||||
|
import * as d3 from 'd3'
|
||||||
|
|
||||||
|
import { useIdWell } from '@asb/context'
|
||||||
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import { DetectedOperationService } from '@api'
|
||||||
|
|
||||||
|
export const TLPie = memo(({ color }) => {
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [svgRef, setSvgRef] = useState()
|
||||||
|
const [stats, setStats] = useState([])
|
||||||
|
|
||||||
|
const [rootRef, { width, height }] = useElementSize()
|
||||||
|
|
||||||
|
const idWell = useIdWell()
|
||||||
|
|
||||||
|
const pie = useMemo(() => d3.pie().value((d) => d.count), [])
|
||||||
|
const data = useMemo(() => stats ? pie(stats) : null, [stats, pie])
|
||||||
|
|
||||||
|
const radius = useMemo(() => Math.min(width, height) / 2 - 100, [width, height])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
invokeWebApiWrapperAsync(
|
||||||
|
async () => {
|
||||||
|
const stats = await DetectedOperationService.getStat(idWell)
|
||||||
|
setStats(stats)
|
||||||
|
},
|
||||||
|
setIsLoading,
|
||||||
|
'Не удалось загрузить статистику автоопределённых операций'
|
||||||
|
)
|
||||||
|
}, [idWell])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data) return
|
||||||
|
const slices = d3.select(svgRef)
|
||||||
|
.select('.slices')
|
||||||
|
.selectAll('path')
|
||||||
|
.data(data)
|
||||||
|
|
||||||
|
slices.exit().remove()
|
||||||
|
const newSlices = slices.enter().append('path')
|
||||||
|
|
||||||
|
slices.merge(newSlices)
|
||||||
|
.attr('d', d3.arc().innerRadius(radius * 0.4).outerRadius(radius * 0.8))
|
||||||
|
.attr('fill', (d) => color ? color(d.data.idCategory) : '#0008')
|
||||||
|
}, [svgRef, data, color, radius])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data) return
|
||||||
|
const innerArc = d3.arc().innerRadius(radius * 0.4).outerRadius(radius * 0.8)
|
||||||
|
const outerArc = d3.arc().innerRadius(radius * 0.9).outerRadius(radius * 0.9)
|
||||||
|
|
||||||
|
const lines = d3.select(svgRef)
|
||||||
|
.select('.lines')
|
||||||
|
.selectAll('polyline')
|
||||||
|
.data(data, (d) => d.data.category)
|
||||||
|
|
||||||
|
lines.exit().remove()
|
||||||
|
const newLines = lines.enter().append('polyline')
|
||||||
|
|
||||||
|
const abovePi = (d) => (d.startAngle + d.endAngle) / 2 < Math.PI
|
||||||
|
|
||||||
|
lines.merge(newLines)
|
||||||
|
.attr('points', (d) => {
|
||||||
|
const pos = outerArc.centroid(d)
|
||||||
|
pos[0] = radius * 0.95 * (abovePi(d) ? 1 : -1)
|
||||||
|
return [innerArc.centroid(d), outerArc.centroid(d), pos]
|
||||||
|
})
|
||||||
|
|
||||||
|
const lables = d3.select(svgRef)
|
||||||
|
.select('.labels')
|
||||||
|
.selectAll('text')
|
||||||
|
.data(data, (d) => d.data.category)
|
||||||
|
|
||||||
|
lables.exit().remove()
|
||||||
|
const newLabels = lables.enter()
|
||||||
|
.append('text')
|
||||||
|
.attr('dy', '.35em')
|
||||||
|
|
||||||
|
lables.merge(newLabels)
|
||||||
|
.attr('transform', (d) => {
|
||||||
|
const pos = outerArc.centroid(d)
|
||||||
|
pos[0] = radius * 0.95 * (abovePi(d) ? 1 : -1)
|
||||||
|
return `translate(${pos})`
|
||||||
|
})
|
||||||
|
.style('text-anchor', (d) => abovePi(d) ? 'start' : 'end')
|
||||||
|
.attr('width', radius * 0.4)
|
||||||
|
.text((d) => `${d.data.category} (${d.data.count})`)
|
||||||
|
|
||||||
|
}, [svgRef, data, radius])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={rootRef} className={'tvd-right'}>
|
||||||
|
<LoaderPortal show={isLoading} style={{ width: '100%', flexGrow: 1 }}>
|
||||||
|
{data ? (
|
||||||
|
<svg ref={setSvgRef} style={{ width: '100%', height: '100%' }}>
|
||||||
|
<g transform={`translate(${width / 2}, ${height / 2})`}>
|
||||||
|
<g className={'slices'} stroke={'#0005'} />
|
||||||
|
<g className={'labels'} fill={'black'} />
|
||||||
|
<g className={'lines'} fill={'none'} stroke={'black'} />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<div style={{ height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
<Empty />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</LoaderPortal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default TLPie
|
@ -1,15 +1,18 @@
|
|||||||
import { DoubleLeftOutlined, DoubleRightOutlined, LineChartOutlined, LinkOutlined } from '@ant-design/icons'
|
import { LineChartOutlined, LinkOutlined } from '@ant-design/icons'
|
||||||
import { memo, useState, useEffect, useCallback, useMemo } from 'react'
|
import { memo, useState, useEffect, useMemo } from 'react'
|
||||||
|
import { Switch, Segmented } from 'antd'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Switch, Button } from 'antd'
|
import * as d3 from 'd3'
|
||||||
import { timeDay } from 'd3'
|
|
||||||
|
|
||||||
import { useIdWell } from '@asb/context'
|
import { useIdWell } from '@asb/context'
|
||||||
import { D3Chart } from '@components/d3'
|
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 { DetectedOperationService } from '@api'
|
||||||
|
|
||||||
|
import TLPie from './TLPie'
|
||||||
|
import TLChart from './TLChart'
|
||||||
import NptTable from './NptTable'
|
import NptTable from './NptTable'
|
||||||
import NetGraphExport from './NetGraphExport'
|
import NetGraphExport from './NetGraphExport'
|
||||||
import AdditionalTables from './AdditionalTables'
|
import AdditionalTables from './AdditionalTables'
|
||||||
@ -17,6 +20,12 @@ import AdditionalTables from './AdditionalTables'
|
|||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
import '@styles/tvd.less'
|
import '@styles/tvd.less'
|
||||||
|
|
||||||
|
const colorArray = [
|
||||||
|
'#1abc9c', '#16a085', '#2ecc71', '#27ae60', '#3498db', '#2980b9', '#9b59b6', '#8e44ad', '#34495e', '#2c3e50',
|
||||||
|
'#f1c40f', '#f39c12', '#e67e22', '#d35400', '#e74c3c', '#c0392b', '#ecf0f1', '#bdc3c7', '#95a5a6', '#7f8c8d',
|
||||||
|
]
|
||||||
|
|
||||||
|
const Item = ({ label, children, ...other }) => (<div className={'tvd-input-group'} {...other}><span>{label}: </span>{children}</div>)
|
||||||
|
|
||||||
const numericRender = (d) => d && Number.isFinite(+d) ? (+d).toFixed(2) : '-'
|
const numericRender = (d) => d && Number.isFinite(+d) ? (+d).toFixed(2) : '-'
|
||||||
|
|
||||||
@ -85,7 +94,7 @@ const ticks = {
|
|||||||
date: {
|
date: {
|
||||||
x: {
|
x: {
|
||||||
visible: true,
|
visible: true,
|
||||||
count: timeDay.every(1),
|
count: d3.timeDay.every(1),
|
||||||
format: (d, i) => i % 2 === 0 ? formatDate(d, undefined, 'YYYY-MM-DD') : '',
|
format: (d, i) => i % 2 === 0 ? formatDate(d, undefined, 'YYYY-MM-DD') : '',
|
||||||
},
|
},
|
||||||
y: { visible: true },
|
y: { visible: true },
|
||||||
@ -134,17 +143,13 @@ const makeDataset = (key, label, color, width, radius, dash) => ({
|
|||||||
const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
||||||
const [xLabel, setXLabel] = useState('day')
|
const [xLabel, setXLabel] = useState('day')
|
||||||
const [operations, setOperations] = useState({})
|
const [operations, setOperations] = useState({})
|
||||||
const [tableVisible, setTableVisible] = useState(false)
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [pointsEnabled, setPointsEnabled] = useState(true)
|
const [pointsEnabled, setPointsEnabled] = useState(true)
|
||||||
|
const [selectedTab, setSelectedTab] = useState('Скрыть')
|
||||||
|
const [color, setColor] = useState()
|
||||||
|
|
||||||
const idWellContext = useIdWell()
|
const idWellContext = useIdWell()
|
||||||
const idWell = useMemo(() => wellId ?? idWellContext, [wellId, idWellContext])
|
const idWell = useMemo(() => wellId ?? idWellContext, [wellId, idWellContext])
|
||||||
|
|
||||||
const toogleTable = useCallback(() => {
|
|
||||||
setOperations(pre => ({ ...pre }))
|
|
||||||
setTableVisible(v => !v)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
const withoutNpt = []
|
const withoutNpt = []
|
||||||
@ -160,15 +165,6 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
|
|
||||||
return { ...operations, withoutNpt }
|
return { ...operations, withoutNpt }
|
||||||
}, [operations])
|
}, [operations])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
invokeWebApiWrapperAsync(
|
|
||||||
async () => setOperations(await getOperations(idWell)),
|
|
||||||
setIsLoading,
|
|
||||||
`Не удалось загрузить операции по скважине "${idWell}"`,
|
|
||||||
'Получение списка опервций по скважине'
|
|
||||||
)
|
|
||||||
}, [idWell])
|
|
||||||
|
|
||||||
const datasets = useMemo(() => {
|
const datasets = useMemo(() => {
|
||||||
const radius = pointsEnabled ? 6 : 1
|
const radius = pointsEnabled ? 6 : 1
|
||||||
@ -181,29 +177,57 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
]
|
]
|
||||||
}, [pointsEnabled])
|
}, [pointsEnabled])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
invokeWebApiWrapperAsync(
|
||||||
|
async () => setOperations(await getOperations(idWell)),
|
||||||
|
setIsLoading,
|
||||||
|
`Не удалось загрузить операции по скважине "${idWell}"`,
|
||||||
|
'Получение списка опервций по скважине'
|
||||||
|
)
|
||||||
|
}, [idWell])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
invokeWebApiWrapperAsync(
|
||||||
|
async () => {
|
||||||
|
const cats = await DetectedOperationService.getCategories()
|
||||||
|
const color = d3.scaleOrdinal()
|
||||||
|
.domain(cats.map((cat) => cat.id))
|
||||||
|
.range(colorArray)
|
||||||
|
setColor(() => color)
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
'Не удалось получить список типов операций'
|
||||||
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'container tvd-page'} {...other}>
|
<div className={'container tvd-page'} {...other}>
|
||||||
<div className={'tvd-top'}>
|
<div className={'tvd-top'}>
|
||||||
<h2>{title || 'График Глубина-день'}</h2>
|
<div className={'tvd-inputs'}>
|
||||||
<div>
|
<h2>{title || 'График Глубина-день'}</h2>
|
||||||
<Switch
|
<Item label={'Ось времени'} style={{ marginLeft: 50 }}>
|
||||||
defaultChecked
|
<Segmented
|
||||||
checkedChildren={'С рисками'}
|
options={[{ label: 'Дата', value: 'date' }, { label: 'Дни со старта', value: 'day' }]}
|
||||||
unCheckedChildren={'Без рисок'}
|
onChange={setXLabel}
|
||||||
onChange={(checked) => setPointsEnabled(checked)}
|
value={xLabel}
|
||||||
style={{ marginRight: 20 }}
|
title={'Нажмите для переключения горизонтальной оси'}
|
||||||
title={'Нажмите для переключения видимости засечек на графиках'}
|
/>
|
||||||
/>
|
</Item>
|
||||||
<Switch
|
<Item label={'Риски'}>
|
||||||
checkedChildren={'Дата'}
|
<Switch
|
||||||
unCheckedChildren={'Дни со старта'}
|
defaultChecked
|
||||||
loading={isLoading}
|
onChange={(checked) => setPointsEnabled(checked)}
|
||||||
onChange={(checked) => setXLabel(checked ? 'date' : 'day')}
|
title={'Нажмите для переключения видимости засечек на графиках'}
|
||||||
style={{ marginRight: '20px' }}
|
/>
|
||||||
title={'Нажмите для переключения горизонтальной оси'}
|
</Item>
|
||||||
/>
|
</div>
|
||||||
|
<div className={'tvd-inputs'}>
|
||||||
<NetGraphExport idWell={idWell} />
|
<NetGraphExport idWell={idWell} />
|
||||||
<Button icon={tableVisible ? <DoubleRightOutlined /> : <DoubleLeftOutlined />} onClick={toogleTable}>НПВ</Button>
|
<Segmented
|
||||||
|
options={['НПВ', 'ЕСО', 'Статистика', 'Скрыть']}
|
||||||
|
value={selectedTab}
|
||||||
|
onChange={setSelectedTab}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<LoaderPortal show={isLoading} style={{ flex: 1 }}>
|
<LoaderPortal show={isLoading} style={{ flex: 1 }}>
|
||||||
@ -221,7 +245,9 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
animDurationMs={0}
|
animDurationMs={0}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{tableVisible && <NptTable operations={operations?.fact} />}
|
{selectedTab === 'НПВ' && <NptTable operations={operations?.fact} />}
|
||||||
|
{selectedTab === 'ЕСО' && <TLChart color={color} />}
|
||||||
|
{selectedTab === 'Статистика' && <TLPie color={color} />}
|
||||||
</div>
|
</div>
|
||||||
</LoaderPortal>
|
</LoaderPortal>
|
||||||
</div>
|
</div>
|
||||||
@ -229,7 +255,7 @@ const Tvd = memo(({ idWell: wellId, title, ...other }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
export default wrapPrivateComponent(Tvd, {
|
export default wrapPrivateComponent(Tvd, {
|
||||||
requirements: [ 'OperationStat.get' ],
|
requirements: [ 'OperationStat.get', 'DetectedOperation.get' ],
|
||||||
title: 'TVD',
|
title: 'TVD',
|
||||||
route: 'tvd',
|
route: 'tvd',
|
||||||
})
|
})
|
||||||
|
@ -9,6 +9,21 @@
|
|||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.tvd-inputs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.tvd-input-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 15px;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tvd-main {
|
.tvd-main {
|
||||||
|
Loading…
Reference in New Issue
Block a user