forked from ddrilling/asb_cloud_front
* Мониторинг переписан на flex
* Блок текущих значений перемещён наверх и переписан * Выделена строка управления графиком * Удалена мнемосхема
This commit is contained in:
parent
b2c34d07a9
commit
11a632c246
@ -1,101 +0,0 @@
|
||||
import moment from 'moment'
|
||||
import { useState, useEffect, memo, ReactNode } from 'react'
|
||||
import {CaretUpOutlined, CaretDownOutlined, CaretRightOutlined} from '@ant-design/icons'
|
||||
|
||||
import '@styles/display.less'
|
||||
|
||||
export const formatNumber = (value?: unknown, format?: number) =>
|
||||
Number.isInteger(format) && Number.isFinite(value)
|
||||
? Number(value).toFixed(format)
|
||||
: Number(value).toPrecision(4)
|
||||
|
||||
const iconStyle = { color:'#0008' }
|
||||
const displayValueStyle = { display: 'flex', flexGrow: 1 }
|
||||
|
||||
export type ValueDisplayProps = {
|
||||
prefix?: ReactNode
|
||||
suffix?: ReactNode
|
||||
format?: number | string | ((arg: string) => ReactNode)
|
||||
isArrowVisible?: boolean
|
||||
enumeration?: Record<string, string>
|
||||
value: string
|
||||
}
|
||||
|
||||
export type DisplayProps = ValueDisplayProps & {
|
||||
className?: string
|
||||
label?: ReactNode
|
||||
}
|
||||
|
||||
export const ValueDisplay = memo<ValueDisplayProps>(({ prefix, value, suffix, isArrowVisible, format, enumeration }) => {
|
||||
const [val, setVal] = useState<ReactNode>('---')
|
||||
const [arrowState, setArrowState] = useState({
|
||||
preVal: NaN,
|
||||
preTimestamp: Date.now(),
|
||||
direction: 0,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setVal((preVal) => {
|
||||
if ((value ?? '-') === '-' || value === '--') return '---'
|
||||
if (typeof format === 'function') return format(enumeration?.[value] ?? value)
|
||||
if (enumeration?.[value]) return enumeration[value]
|
||||
|
||||
if (Number.isFinite(+value)) {
|
||||
if (isArrowVisible && (arrowState.preTimestamp + 1000 < Date.now())) {
|
||||
let direction = 0
|
||||
if (+value > arrowState.preVal)
|
||||
direction = 1
|
||||
if (+value < arrowState.preVal)
|
||||
direction = -1
|
||||
|
||||
setArrowState({
|
||||
preVal: +value,
|
||||
preTimestamp: Date.now(),
|
||||
direction: direction,
|
||||
})
|
||||
}
|
||||
|
||||
return formatNumber(value, Number(format))
|
||||
}
|
||||
|
||||
if (value.length > 4) {
|
||||
const valueDate = moment(value)
|
||||
if (valueDate.isValid())
|
||||
return valueDate.format(String(format))
|
||||
}
|
||||
|
||||
return value
|
||||
})
|
||||
},[value, isArrowVisible, arrowState, format, enumeration])
|
||||
|
||||
let arrow = null
|
||||
if(isArrowVisible)
|
||||
switch (arrowState.direction){
|
||||
case 0:
|
||||
arrow = <CaretRightOutlined style={iconStyle}/>
|
||||
break
|
||||
case 1:
|
||||
arrow = <CaretUpOutlined style={iconStyle}/>
|
||||
break
|
||||
case -1:
|
||||
arrow = <CaretDownOutlined style={iconStyle}/>
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return(
|
||||
<span className={'display_value'}>
|
||||
{prefix} {val} {suffix}{arrow}
|
||||
</span>
|
||||
)
|
||||
})
|
||||
|
||||
export const Display = memo<DisplayProps>(({ className, label, ...other })=> (
|
||||
<div className={className}>
|
||||
<div className={'display_label'}>{label}</div>
|
||||
<div style={displayValueStyle}>
|
||||
<ValueDisplay {...other}/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
@ -73,8 +73,8 @@ export type ChartGroup<DataType extends BaseDataType> = {
|
||||
}
|
||||
|
||||
const defaultOffsets: ChartOffset = {
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 100,
|
||||
right: 20,
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
import moment from 'moment'
|
||||
import { memo } from 'react'
|
||||
import { Tooltip, Typography } from 'antd'
|
||||
|
||||
import { Display } from '@components/Display'
|
||||
|
||||
import RigMnemo from './RigMnemo'
|
||||
|
||||
const getTimeFormat = (value) => {
|
||||
const date = moment(value)
|
||||
|
||||
return (
|
||||
<Tooltip title={`Время последних данных: ${date.format('DD.MM.YYYY HH:mm:ss')}`}>
|
||||
{date.isSame(new Date(), 'day') || (
|
||||
<Typography.Text disabled style={{ fontSize: '12px', marginRight: '5px' }}>{date.format('DD.MM.YYYY')}</Typography.Text>
|
||||
)}
|
||||
{date.format('HH:mm:ss')}
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const params = [
|
||||
{ label: 'Рот., об/мин', accessorName: 'rotorSpeed', isArrowVisible: true },
|
||||
{ label: 'Долото, м', accessorName: 'bitDepth', isArrowVisible: true, format: 2 },
|
||||
{ label: 'Забой, м', accessorName: 'wellDepth', isArrowVisible: true, format: 2 },
|
||||
{ label: 'Расход, м³/ч', accessorName: 'flow', isArrowVisible: true },
|
||||
{ label: 'Расход х.х., м³/ч', accessorName: 'flowIdle', isArrowVisible: true },
|
||||
{ label: 'Время', accessorName: 'date', format: getTimeFormat },
|
||||
{ label: 'MSE, %', accessorName: 'mse', format: 2 },
|
||||
]
|
||||
|
||||
export const CustomColumn = memo(({ data }) => {
|
||||
const dataLast = data[data.length - 1]
|
||||
params.forEach(param => param.value = dataLast?.[param.accessorName] ?? '-')
|
||||
|
||||
return (
|
||||
<>
|
||||
{params.map(param => (
|
||||
<Display
|
||||
className={'border_small display_flex_container'}
|
||||
key={param.label}
|
||||
label={param.label}
|
||||
value={param.value}
|
||||
suffix={param.units}
|
||||
format={param.format}
|
||||
isArrowVisible={param.isArrowVisible}
|
||||
/>
|
||||
))}
|
||||
<RigMnemo
|
||||
wellDepth={dataLast?.wellDepth ?? Number.NaN}
|
||||
bitPosition={dataLast?.bitDepth ?? Number.NaN}
|
||||
blockPosition={dataLast?.blockPosition ?? Number.NaN}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default CustomColumn
|
@ -1,24 +0,0 @@
|
||||
import { memo } from 'react'
|
||||
|
||||
import { Display } from '@components/Display'
|
||||
|
||||
const modeNames = {
|
||||
0: 'Ручной',
|
||||
1: 'Бурение в роторе',
|
||||
2: 'Проработка',
|
||||
3: 'Бурение в слайде',
|
||||
4: 'Спуск СПО',
|
||||
5: 'Подъем СПО',
|
||||
6: 'Подъем с проработкой',
|
||||
|
||||
10: 'БЛОКИРОВКА',
|
||||
}
|
||||
|
||||
export const ModeDisplay = memo(({ data }) => (
|
||||
<Display
|
||||
className={'border_small display_flex_container user_card'}
|
||||
label={'Режим:'}
|
||||
value={data?.[data?.length - 1]?.mode}
|
||||
enumeration={modeNames}
|
||||
/>
|
||||
))
|
107
src/pages/Well/Telemetry/TelemetryView/TelemetrySummary.jsx
Normal file
107
src/pages/Well/Telemetry/TelemetryView/TelemetrySummary.jsx
Normal file
@ -0,0 +1,107 @@
|
||||
import { isValidElement, memo, useEffect, useMemo, useState } from 'react'
|
||||
import { CaretUpOutlined, CaretDownOutlined, CaretRightOutlined } from '@ant-design/icons'
|
||||
import { Tooltip, Typography } from 'antd'
|
||||
import moment from 'moment'
|
||||
|
||||
import { formatDate, isRawDate } from '@utils'
|
||||
|
||||
import '@styles/components/data_summary.less'
|
||||
|
||||
export const parseValue = (value, formatter) => {
|
||||
if (!value || String(value).trim().length <= 0) return '---'
|
||||
if (typeof formatter === 'function') return formatter(value)
|
||||
if (isRawDate(value)) return formatDate(value)
|
||||
const v = +value
|
||||
if (Number.isFinite(v))
|
||||
return Number.isInteger(formatter) ? v.toFixed(formatter) : v.toPrecision(4)
|
||||
return value
|
||||
}
|
||||
|
||||
export const DashboardDisplay = memo(({ label, title, unit, iconRenderer, value, format }) => {
|
||||
const [icon, setIcon] = useState(null)
|
||||
|
||||
const val = useMemo(() => parseValue(value, format), [value, format])
|
||||
|
||||
useEffect(() => setIcon((prev) => iconRenderer?.(value, prev)), [value])
|
||||
|
||||
return (
|
||||
<div className={'dashboard-display'}>
|
||||
<div className={'display-label'}>
|
||||
{title ? (
|
||||
<Tooltip title={title}>{label}</Tooltip>
|
||||
) : (
|
||||
<span>{label}</span>
|
||||
)}
|
||||
<span>{unit}</span>
|
||||
</div>
|
||||
<div className={'display-value'}>
|
||||
<span>{val}</span>
|
||||
{isValidElement(icon) ? icon : icon?.value}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const getTimeFormat = (value) => {
|
||||
const date = moment(value)
|
||||
|
||||
return (
|
||||
<Tooltip title={`Время последних данных: ${date.format('DD.MM.YYYY HH:mm:ss')}`}>
|
||||
{date.isSame(new Date(), 'day') || (
|
||||
<Typography.Text disabled style={{ fontSize: '12px', marginRight: '5px' }}>{date.format('DD.MM.YYYY')}</Typography.Text>
|
||||
)}
|
||||
{date.format('HH:mm:ss')}
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const iconRenderer = (value, prev) => {
|
||||
if (!Number.isFinite(+value)) return null
|
||||
if (prev?.prevDate + 1000 >= Date.now()) return prev
|
||||
const val = +value
|
||||
let Component = CaretRightOutlined
|
||||
if ((prev?.prev ?? null) && val !== prev.prev)
|
||||
Component = val > prev.prev ? CaretUpOutlined : CaretDownOutlined
|
||||
|
||||
return {
|
||||
prev: val,
|
||||
prevDate: Date.now(),
|
||||
value: <Component style={{ color:'#0008' }} />,
|
||||
}
|
||||
}
|
||||
|
||||
const modeNames = {
|
||||
0: 'Ручной',
|
||||
1: 'Бурение в роторе',
|
||||
2: 'Проработка',
|
||||
3: 'Бурение в слайде',
|
||||
4: 'Спуск СПО',
|
||||
5: 'Подъем СПО',
|
||||
6: 'Подъем с проработкой',
|
||||
|
||||
10: 'БЛОКИРОВКА',
|
||||
}
|
||||
|
||||
const params = [
|
||||
{ label: 'Режим', accessorName: 'mode', format: (value) => modeNames[value] || '---' },
|
||||
{ label: 'Пользователь', accessorName: 'user', title: 'Пользователь панели оператора' },
|
||||
{ label: 'Рот.', unit: 'об/мин', accessorName: 'rotorSpeed', iconRenderer },
|
||||
{ label: 'Долото', unit: 'м', accessorName: 'bitDepth', iconRenderer, format: 2 },
|
||||
{ label: 'Забой', unit: 'м', accessorName: 'wellDepth', iconRenderer, format: 2 },
|
||||
{ label: 'Расход', unit: 'м³/ч', accessorName: 'flow', iconRenderer },
|
||||
{ label: 'Расход х.х.', unit: 'м³/ч', accessorName: 'flowIdle', iconRenderer },
|
||||
{ label: 'Время', accessorName: 'date', format: getTimeFormat },
|
||||
{ label: 'MSE', unit: '%', accessorName: 'mse', format: 2 },
|
||||
]
|
||||
|
||||
export const TelemetrySummary = memo(({ data }) => {
|
||||
return (
|
||||
<div className={'data-summary'}>
|
||||
{params.map((param, i) => (
|
||||
<DashboardDisplay key={i} {...param} value={data?.[param.accessorName]} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default TelemetrySummary
|
@ -1,12 +0,0 @@
|
||||
import { Tooltip } from 'antd'
|
||||
import { Display } from '@components/Display'
|
||||
|
||||
export const UserOfWell = ({ data }) => (
|
||||
<Display
|
||||
className={'border_small display_flex_container user_card'}
|
||||
label={<Tooltip title={'Пользователь панели оператора'}>Пользователь</Tooltip>}
|
||||
value={data[data.length - 1]?.user}
|
||||
/>
|
||||
)
|
||||
|
||||
export default UserOfWell
|
@ -1,15 +1,15 @@
|
||||
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
|
||||
import { BehaviorSubject, buffer, throttleTime } from 'rxjs'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { Button, Select } from 'antd'
|
||||
|
||||
import { useWell } from '@asb/context'
|
||||
import { makeDateSorter } from '@components/Table'
|
||||
import { DatePickerWrapper, makeDateSorter } from '@components/Table'
|
||||
import { D3MonitoringCharts } from '@components/d3/monitoring'
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { Grid, GridItem } from '@components/Grid'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
||||
import { formatDate, hasPermission, withPermissions } from '@utils'
|
||||
import { formatDate, hasPermission, isRawDate, range, withPermissions } from '@utils'
|
||||
import { Subscribe } from '@services/signalr'
|
||||
import {
|
||||
DrillFlowChartService,
|
||||
@ -20,10 +20,8 @@ import {
|
||||
|
||||
import { makeChartGroups, yAxis } from './dataset'
|
||||
import ActiveMessagesOnline from './ActiveMessagesOnline'
|
||||
import TelemetrySummary from './TelemetrySummary'
|
||||
import WirelineRunOut from './WirelineRunOut'
|
||||
import { CustomColumn } from './CustomColumn'
|
||||
import { ModeDisplay } from './ModeDisplay'
|
||||
import { UserOfWell } from './UserOfWells'
|
||||
import { Setpoints } from './Setpoints'
|
||||
import { cursorRender } from './cursorRender'
|
||||
|
||||
@ -37,6 +35,14 @@ import '@styles/message.less'
|
||||
|
||||
const { Option } = Select
|
||||
|
||||
const chartProps = {
|
||||
yAxis,
|
||||
chartName: 'monitoring',
|
||||
yTicks: { visible: true, format: (d) => formatDate(d, 'YYYY-MM-DD') },
|
||||
plugins: { menu: { enabled: false }, cursor: { render: cursorRender } },
|
||||
style: { flexGrow: 1, height: 'auto', width: 'auto' },
|
||||
}
|
||||
|
||||
const getLast = (data) => Array.isArray(data) ? data.at(-1) : data
|
||||
|
||||
const isMseEnabled = (dataSaub) => (getLast(dataSaub)?.mseState && 2) > 0
|
||||
@ -54,6 +60,7 @@ export const normalizeData = (data) => data?.map(item => ({
|
||||
})) ?? []
|
||||
|
||||
const dateSorter = makeDateSorter('date')
|
||||
const defaultDate = () => Date.now() - defaultPeriod * 1000
|
||||
|
||||
const makeSubjectSubsription = (subject$, handler) => {
|
||||
const subscribtion = subject$.pipe(
|
||||
@ -64,20 +71,49 @@ const makeSubjectSubsription = (subject$, handler) => {
|
||||
}
|
||||
|
||||
const TelemetryView = memo(() => {
|
||||
const [well, updateWell] = useWell()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
|
||||
const [currentWellId, setCurrentWellId] = useState(null)
|
||||
const [dataSaub, setDataSaub] = useState([])
|
||||
const [dataSpin, setDataSpin] = useState([])
|
||||
const [chartInterval, setChartInterval] = useState(defaultPeriod)
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [flowChartData, setFlowChartData] = useState([])
|
||||
const [rop, setRop] = useState(null)
|
||||
const [domain, setDomain] = useState({})
|
||||
const [chartMethods, setChartMethods] = useState()
|
||||
|
||||
const [well, updateWell] = useWell()
|
||||
const [chartInterval, setChartInterval] = useState(defaultPeriod)
|
||||
const [startDate, setStartDate] = useState(defaultDate)
|
||||
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
|
||||
|
||||
const saubSubject$ = useMemo(() => new BehaviorSubject(), [])
|
||||
const spinSubject$ = useMemo(() => new BehaviorSubject(), [])
|
||||
const [archiveMode, setArchiveMode] = useState(false)
|
||||
|
||||
const onStatusChanged = useCallback((value) => updateWell({ idState: value }), [well])
|
||||
|
||||
const isDateDisabled = useCallback((date) => {
|
||||
if (!date) return false
|
||||
const dt = new Date(date).setHours(0, 0, 0, 0)
|
||||
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||
}, [dateLimit, chartInterval])
|
||||
|
||||
const isDateTimeDisabled = useCallback((date) => ({
|
||||
disabledHours: () => range(24).filter(h => {
|
||||
if (!date) return false
|
||||
const dt = +new Date(date).setHours(h)
|
||||
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||
}),
|
||||
disabledMinutes: () => range(60).filter(m => {
|
||||
if (!date) return false
|
||||
const dt = +new Date(date).setMinutes(m)
|
||||
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||
}),
|
||||
disabledSeconds: () => range(60).filter(s => {
|
||||
if (!date) return false
|
||||
const dt = +new Date(date).setSeconds(s)
|
||||
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||
})
|
||||
}), [dateLimit, chartInterval])
|
||||
|
||||
const handleDataSaub = useCallback((data, replace = false) => {
|
||||
setDataSaub((prev) => {
|
||||
@ -92,7 +128,21 @@ const TelemetryView = memo(() => {
|
||||
|
||||
const handleDataSpin = useCallback((data) => data && setDataSpin((prev) => [...prev, ...data]), [])
|
||||
|
||||
const onStatusChanged = useCallback((value) => updateWell({ idState: value }), [well])
|
||||
const onWheel = useCallback((value) => {
|
||||
if (!archiveMode && value.deltaY < 0) {
|
||||
setArchiveMode(true)
|
||||
// load data
|
||||
} else {
|
||||
// move
|
||||
}
|
||||
}, [archiveMode])
|
||||
|
||||
const spinLast = useMemo(() => dataSpin.at(-1), [dataSpin])
|
||||
const saubLast = useMemo(() => dataSaub.at(-1), [dataSaub])
|
||||
const summaryData = useMemo(() => ({ ...saubLast, ...rop }), [saubLast, rop])
|
||||
|
||||
const saubSubject$ = useMemo(() => new BehaviorSubject(), [])
|
||||
const spinSubject$ = useMemo(() => new BehaviorSubject(), [])
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
let i, j
|
||||
@ -112,6 +162,32 @@ const TelemetryView = memo(() => {
|
||||
|
||||
const chartGroups = useMemo(() => makeChartGroups(flowChartData), [flowChartData])
|
||||
|
||||
useEffect(() => {
|
||||
setArchiveMode(isRawDate(searchParams.get('start')))
|
||||
const interval = parseInt(searchParams.get('range') || defaultPeriod)
|
||||
const date = new Date(searchParams.get('start') || (Date.now() - interval))
|
||||
setChartInterval(interval)
|
||||
setStartDate(date)
|
||||
}, [searchParams])
|
||||
|
||||
useEffect(() => {
|
||||
if (archiveMode) return
|
||||
const subscribtion = saubSubject$.pipe(
|
||||
buffer(saubSubject$.pipe(throttleTime(700)))
|
||||
).subscribe((data) => handleDataSaub(data.flat().filter(Boolean)))
|
||||
|
||||
return () => subscribtion.unsubscribe()
|
||||
}, [saubSubject$, archiveMode])
|
||||
|
||||
useEffect(() => {
|
||||
if (archiveMode) return
|
||||
const subscribtion = spinSubject$.pipe(
|
||||
buffer(spinSubject$.pipe(throttleTime(700)))
|
||||
).subscribe((data) => handleDataSpin(data.flat().filter(Boolean)))
|
||||
|
||||
return () => subscribtion.unsubscribe()
|
||||
}, [spinSubject$, archiveMode])
|
||||
|
||||
useEffect(() => makeSubjectSubsription(saubSubject$, handleDataSaub), [saubSubject$, handleDataSaub])
|
||||
useEffect(() => makeSubjectSubsription(spinSubject$, handleDataSpin), [spinSubject$, handleDataSpin])
|
||||
|
||||
@ -148,6 +224,12 @@ const TelemetryView = memo(() => {
|
||||
async () => {
|
||||
const rop = await OperationStatService.getClusterRopStatByIdWell(well.id)
|
||||
setRop(rop)
|
||||
let dates = await TelemetryDataSaubService.getDataDatesRange(well.id)
|
||||
dates = {
|
||||
from: new Date(dates?.from ?? 0),
|
||||
to: new Date(dates?.to ?? 0)
|
||||
}
|
||||
setDateLimit(dates)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить данные`,
|
||||
@ -156,25 +238,18 @@ const TelemetryView = memo(() => {
|
||||
}, [well])
|
||||
|
||||
useEffect(() => {
|
||||
if (dataSaub.length <= 0) return
|
||||
const last = new Date(dataSaub.at(-1).date)
|
||||
setDomain({
|
||||
min: new Date(+last - chartInterval * 1000),
|
||||
max: last
|
||||
})
|
||||
}, [dataSaub, chartInterval])
|
||||
if (!saubLast || archiveMode) return
|
||||
const last = new Date(saubLast.date)
|
||||
const startDate = new Date(+last - chartInterval * 1000)
|
||||
setStartDate(startDate)
|
||||
setDomain({ min: startDate, max: last })
|
||||
}, [archiveMode, saubLast, chartInterval])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<Grid className={'telemetry-view-page'} style={{ gridTemplateColumns: 'auto repeat(6, 1fr)' }}>
|
||||
<GridItem col={'1'} row={'1'} colSpan={'8'} style={{ marginBottom: '0.5rem' }}>
|
||||
<LoaderPortal show={showLoader} style={{ flexGrow: 1 }}>
|
||||
<div className={'telemetry-view-page'}>
|
||||
<TelemetrySummary data={summaryData} />
|
||||
<div className={'page-top'}>
|
||||
<ModeDisplay data={dataSaub} />
|
||||
<div>
|
||||
Интервал:
|
||||
<PeriodPicker onChange={setChartInterval} />
|
||||
</div>
|
||||
<Button onClick={() => chartMethods?.setSettingsVisible(true)}>Настроить графики</Button>
|
||||
<div>
|
||||
Статус:
|
||||
<Select value={well.idState ?? 0} onChange={onStatusChanged} disabled={!hasPermission('Well.edit')}>
|
||||
@ -184,44 +259,43 @@ const TelemetryView = memo(() => {
|
||||
</Select>
|
||||
</div>
|
||||
<Setpoints />
|
||||
<span style={{ flexGrow: 20 }}> </span>
|
||||
<WirelineRunOut />
|
||||
<div className={'icons'}>
|
||||
<img src={isTorqueStabEnabled(dataSpin) ? MomentStabPicEnabled : MomentStabPicDisabled} alt={'TorqueMaster'} />
|
||||
<img src={isSpinEnabled(dataSpin) ? SpinPicEnabled : SpinPicDisabled} alt={'SpinMaster'} />
|
||||
<h2 style={{ marginBottom: 0, fontWeight: 'bold', color: isMseEnabled(dataSaub) ? 'green' : 'lightgrey' }}>MSE</h2>
|
||||
<img src={isTorqueStabEnabled(spinLast) ? MomentStabPicEnabled : MomentStabPicDisabled} alt={'TorqueMaster'} />
|
||||
<img src={isSpinEnabled(spinLast) ? SpinPicEnabled : SpinPicDisabled} alt={'SpinMaster'} />
|
||||
<h2 style={{ marginBottom: 0, fontWeight: 'bold', color: isMseEnabled(saubLast) ? 'green' : 'lightgrey' }}>MSE</h2>
|
||||
</div>
|
||||
<UserOfWell data={dataSaub} />
|
||||
</div>
|
||||
</GridItem>
|
||||
<GridItem col={'1'} row={'2'} rowSpan={'3'} style={{ minWidth: '260px', width: '0.142fr' }}>
|
||||
<CustomColumn data={dataSaub} />
|
||||
</GridItem>
|
||||
<GridItem col={2} row={2} colSpan={8} rowSpan={2}>
|
||||
<D3MonitoringCharts
|
||||
methods={setChartMethods}
|
||||
chartName={'monitoring'}
|
||||
datasetGroups={chartGroups}
|
||||
data={filteredData}
|
||||
yDomain={domain}
|
||||
yAxis={yAxis}
|
||||
yTicks={{
|
||||
visible: true,
|
||||
format: (d) => formatDate(d, 'YYYY-MM-DD')
|
||||
}}
|
||||
plugins={{
|
||||
menu: { enabled: false },
|
||||
cursor: {
|
||||
render: cursorRender,
|
||||
},
|
||||
}}
|
||||
height={'70vh'}
|
||||
<div className={'page-top'}>
|
||||
<div>
|
||||
Начальная дата:
|
||||
<DatePickerWrapper
|
||||
disabled={!archiveMode}
|
||||
value={startDate}
|
||||
disabledDate={isDateDisabled}
|
||||
disabledTime={isDateTimeDisabled}
|
||||
onChange={(startDate) => setStartDate(new Date(startDate))}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
Интервал:
|
||||
<PeriodPicker onChange={setChartInterval} />
|
||||
</div>
|
||||
<Button onClick={() => chartMethods?.setSettingsVisible(true)}>Настроить графики</Button>
|
||||
<Button onClick={() => setArchiveMode((prev) => !prev)}>
|
||||
{archiveMode ? 'Выйти из архива' : 'Войти в архив'}
|
||||
</Button>
|
||||
</div>
|
||||
<D3MonitoringCharts
|
||||
{...chartProps}
|
||||
yDomain={domain}
|
||||
data={filteredData}
|
||||
methods={setChartMethods}
|
||||
datasetGroups={chartGroups}
|
||||
onWheel={onWheel}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem col={'2'} row={'3'} colSpan={'7'}>
|
||||
<ActiveMessagesOnline well={well} />
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</div>
|
||||
</LoaderPortal>
|
||||
)
|
||||
})
|
||||
|
56
src/styles/components/data_summary.less
Normal file
56
src/styles/components/data_summary.less
Normal file
@ -0,0 +1,56 @@
|
||||
.data-summary {
|
||||
display: flex;
|
||||
gap: .5em;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.dashboard-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
border: .067em solid #D9D9D9;
|
||||
gap: .2em;
|
||||
border-radius: .14em;
|
||||
padding: .3em;
|
||||
min-width: 7.5em;
|
||||
|
||||
& .display-label {
|
||||
gap: 1.5em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: .75em;
|
||||
line-height: 1em;
|
||||
color: rgba(0, 0, 0, .3);
|
||||
}
|
||||
|
||||
& .display-value {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: .1em;
|
||||
font-size: 1.3em;
|
||||
line-height: 1em;
|
||||
font-weight: bold;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1280px) {
|
||||
.data-summary {
|
||||
gap: .35em;
|
||||
}
|
||||
|
||||
.dashboard-display {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1150px) {
|
||||
.data-summary {
|
||||
gap: .2em;
|
||||
}
|
||||
|
||||
.dashboard-display {
|
||||
font-size: 12.5px;
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
|
||||
.display_flex_container{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex: auto;
|
||||
}
|
||||
.display_header {
|
||||
@ -38,37 +37,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
.display_label{
|
||||
.display {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
column-gap: 20px;
|
||||
padding: 1px 1rem;
|
||||
align-items: stretch;
|
||||
|
||||
& .display_label{
|
||||
font-size: 16px;
|
||||
color: rgb(70, 70, 70);
|
||||
text-align: left;
|
||||
justify-content: center;
|
||||
margin: 1px 0 1px 1rem;
|
||||
flex: auto;
|
||||
align-items: baseline;
|
||||
text-overflow: ellipsis;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
height: 30px;
|
||||
}
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.display_value{
|
||||
& .display_value{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: rgb(50, 50, 50);
|
||||
text-align: right;
|
||||
justify-content: flex-end;
|
||||
align-items:baseline;
|
||||
margin: 1px 1rem;
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.display_small_value{
|
||||
color: rgb(50, 50, 50);
|
||||
text-align: right;
|
||||
justify-content: center;
|
||||
margin: 1px 1rem 1px 1rem;
|
||||
flex: auto;
|
||||
@media only screen and (max-width: 1280px) {
|
||||
.display {
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
.display_flex_container {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user