* Пробельные символы разрешены в серение логина

* Установлены 2 знака после запятой для страниц композитной скважины и куста
* Удалена старая версия tvd
* Расставлено memo
* Стилистические исправления
* Улучшены типы
* Добавлены экспорты по умолчанию
* SelectFromDictionary удалён за ненадобностью
* DatePickerWrapper получил свойство isUTC меняющее обработку даты
* Относительные пути заменены на алиасы
This commit is contained in:
Александр Сироткин 2022-01-24 21:16:50 +05:00
parent 8043c5c5e2
commit 183017a72d
30 changed files with 476 additions and 937 deletions

View File

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

View File

@ -2,13 +2,13 @@ import { memo, useState } from 'react'
import { useForm } from 'antd/lib/form/Form'
import { Form, Input, Modal, FormProps } from 'antd'
import { AuthService, UserDto } from '../services/api'
import { passwordRules, createPasswordRules } from '../utils/validationRules'
import { AuthService, UserDto } from '@api'
import { getUserId, getUserLogin } from '@utils/storage'
import { passwordRules, createPasswordRules } from '@utils/validationRules'
import LoaderPortal from './LoaderPortal'
import { invokeWebApiWrapperAsync } from './factory'
import { UserView } from './views'
import { getUserId, getUserLogin } from '../utils/storage'
const formLayout: FormProps = { labelCol: { span: 11 }, wrapperCol: { span: 16 } }

View File

@ -1,90 +1,86 @@
import { useState, useEffect } from 'react';
import moment from 'moment'
import { useState, useEffect, memo } from 'react'
import {CaretUpOutlined, CaretDownOutlined, CaretRightOutlined} from '@ant-design/icons'
import moment from 'moment';
import '../styles/display.css'
export const formatNumber = (value, format) =>
import '@styles/display.css'
export const formatNumber = (value, format) =>
Number.isInteger(format) && Number.isFinite(value)
? (+value).toFixed(format)
: (+value).toPrecision(4)
export const ValueDisplay = ({prefix, value, suffix, isArrowVisible, format, enumeration}) => {
const iconStyle = { color:'#0008' }
const displayValueStyle = { display: 'flex', flexGrow: 1 }
export const ValueDisplay = ({ prefix, value, suffix, isArrowVisible, format, enumeration }) => {
const [val, setVal] = useState('---')
const [arrowState, setArrowState] = useState({
const [arrowState, setArrowState] = useState({
preVal: NaN,
preTimestamp: Date.now(),
direction: 0,
})
useEffect(()=>{
if(value === undefined || value === null || value === '-' || value === '--'){
setVal('---')
return
}
useEffect(() => {
setVal((preVal) => {
if ((value ?? '-') === '-' || value === '--') return '---'
if (enumeration?.[value]) return enumeration[value]
if(enumeration && enumeration[value]){
setVal(enumeration[value])
return
}
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
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,
})
}
setArrowState({
preVal: value,
preTimestamp: Date.now(),
direction: direction,
})
return formatNumber(value, format)
}
setVal(formatNumber(value, format))
return
}
if(value.length > 4){
let valueDate = moment(value)
if(valueDate.isValid()){
setVal(valueDate.format(format))
return
if (value.length > 4) {
const valueDate = moment(value)
if (valueDate.isValid())
return valueDate.format(format)
}
}
setVal(value)
return value
})
},[value, isArrowVisible, arrowState, format, enumeration])
let arrow = null
if(isArrowVisible)
switch (arrowState.direction){
case 0:
arrow = <CaretRightOutlined style={{color:"#0008"}}/>
arrow = <CaretRightOutlined style={iconStyle}/>
break
case 1:
arrow = <CaretUpOutlined style={{color:"#0008"}}/>
arrow = <CaretUpOutlined style={iconStyle}/>
break
case -1:
arrow = <CaretDownOutlined style={{color:"#0008"}}/>
arrow = <CaretDownOutlined style={iconStyle}/>
break
default:
break
}
}
return(<span className='display_value'>{prefix} {val} {suffix}{arrow}</span>)
return(
<span className={'display_value'}>
{prefix} {val} {suffix}{arrow}
</span>
)
}
export const Display = (props)=>{
const {label} = props
return <div className={props.className}>
<div className='display_label'>{label}</div>
<div style={{display:"flex", flexGrow:1}}>
<ValueDisplay {...props}/>
</div>
export const Display = memo(({ className, label, ...other })=> (
<div className={className}>
<div className={'display_label'}>{label}</div>
<div style={displayValueStyle}>
<ValueDisplay {...other}/>
</div>
}
</div>
))

View File

@ -1,7 +1,9 @@
export class ErrorFetch extends Error {
constructor(status, message) {
super(message);
this.name = "ErrorFetch"
super(message)
this.name = 'ErrorFetch'
this.status = status
}
}
}
export default ErrorFetch

View File

@ -1,34 +1,28 @@
import React from 'react'
import React, { HTMLAttributes, memo } from 'react'
interface ComponentProps {
children?: React.ReactNode
style?: React.CSSProperties
[other: string]: any
}
export type ComponentProps = HTMLAttributes<HTMLDivElement>
interface GridItemProps extends ComponentProps {
children?: React.ReactNode
style?: React.CSSProperties
export type GridItemProps = ComponentProps & {
row: number
col: number
rowSpan?: number
colSpan?: number
}
const gridDefaultStyle = {
export const gridDefaultStyle = {
display: 'grid',
margin: '8px',
justifyItems: 'stretch',
alignItems: 'stretch',
}
export const Grid = ({ children, style, ...other }: ComponentProps) => (
export const Grid = memo<ComponentProps>(({ children, style, ...other }) => (
<div style={{...gridDefaultStyle, ...style}} {...other}>
{children}
</div>
)
))
export const GridItem = ({ children, row, col, rowSpan, colSpan, style, ...other }: GridItemProps) => {
export const GridItem = memo<GridItemProps>(({ children, row, col, rowSpan, colSpan, style, ...other }) => {
const localRow = +row
const localCol = +col
const localColSpan = colSpan ? colSpan - 1 : 0
@ -42,13 +36,15 @@ export const GridItem = ({ children, row, col, rowSpan, colSpan, style, ...other
...style,
}
return <div style={gridItemStyle} {...other}>
{children}
</div>
}
return (
<div style={gridItemStyle} {...other}>
{children}
</div>
)
})
export const Flex = ({ children, style, ...other } : ComponentProps) => (
export const Flex = memo<ComponentProps>(({ children, style, ...other }) => (
<div style={{ display: 'flex', ...style }} {...other}>
{children}
</div>
)
))

View File

@ -2,7 +2,7 @@ import { memo, ReactNode } from 'react'
import { Link } from 'react-router-dom'
import { Button, Layout, LayoutProps } from 'antd'
import PageHeader from '../PageHeader'
import PageHeader from '@components/PageHeader'
export type AdminLayoutPortalProps = LayoutProps & {
title?: ReactNode

View File

@ -1,8 +1,8 @@
import { memo, ReactNode } from 'react'
import { Layout, LayoutProps } from 'antd'
import PageHeader from '../PageHeader'
import WellTreeSelector from '../WellTreeSelector'
import PageHeader from '@components/PageHeader'
import WellTreeSelector from '@components/WellTreeSelector'
export type LayoutPortalProps = LayoutProps & {
title?: ReactNode

View File

@ -1,4 +0,0 @@
/* original from https://loading.io/css/ */
export default function Loader(){
return(<div className="lds-ripple"><div></div><div></div></div>)
}

View File

@ -0,0 +1,9 @@
import { memo, HTMLAttributes } from 'react'
export const Loader = memo<HTMLAttributes<HTMLDivElement>>(({ ...other }) => (
<div className={'lds-ripple'} {...other}>
<div></div><div></div>
</div>
))
export default Loader

View File

@ -1,13 +1,13 @@
import { HTMLAttributes, ReactNode } from 'react'
import Loader from './Loader'
type LoaderPortalProps = {
type LoaderPortalProps = HTMLAttributes<HTMLDivElement> & {
show?: boolean,
fade?: boolean,
children: any,
[other: string]: any
}
export const LoaderPortal: React.FC<LoaderPortalProps> = ({show, fade=true, children, ...other}) => (
export const LoaderPortal: React.FC<LoaderPortalProps> = ({ show, fade = true, children, ...other }) => (
<div className={'loader-container'} {...other}>
<div className={'loader-content'}>{children}</div>
{show && fade && <div className={'loader-fade'}/>}

View File

@ -1,4 +1,5 @@
import { Select } from 'antd'
import { memo } from 'react'
import { Select, SelectProps } from 'antd'
export const defaultPeriod = 600
@ -13,19 +14,18 @@ const timePeriodCollection = [
{ value: 86400, label: '24 часа' }
]
type PeriodPickerProps = {
defaultValue?: number
onChange?: (value: number) => void
[other: string]: any
export type PeriodPickerProps<VT = number> = SelectProps<VT> & {
defaultValue?: VT
onChange?: (value: VT) => void
}
export const PeriodPicker = ({ defaultValue = defaultPeriod, onChange, ...other }: PeriodPickerProps) => (
export const PeriodPicker = memo<PeriodPickerProps>(({ defaultValue = defaultPeriod, onChange, ...other }) => (
<Select
{...other}
defaultValue={defaultValue}
onChange={(value) => onChange?.(Number(value))}
options={timePeriodCollection}
/>
)
))
export default PeriodPicker

View File

@ -1,23 +1,26 @@
import { memo } from 'react'
import { DatePicker } from 'antd'
import moment from 'moment'
import { defaultFormat } from '../../utils'
import { PickerDateProps } from 'antd/lib/date-picker/generatePicker'
import moment, { Moment } from 'moment'
export type DatePickerWrapperProps = {
value: moment.Moment,
onChange: (date: moment.Moment | null) => any
[other: string]: any
import { defaultFormat } from '@utils'
export type DatePickerWrapperProps = PickerDateProps<Moment> & {
value: Moment,
onChange: (date: Moment | null) => any
isUTC?: boolean
}
export const DatePickerWrapper: React.FC<DatePickerWrapperProps> = ({value, onChange, ...other}) => (
export const DatePickerWrapper = memo<DatePickerWrapperProps>(({ value, onChange, isUTC, ...other }) => (
<DatePicker
allowClear={false}
defaultValue={moment()}
value={moment.utc(value).local()}
format={defaultFormat}
showTime
allowClear={false}
format={defaultFormat}
defaultValue={moment()}
onChange={(date) => onChange(date)}
value={isUTC ? moment.utc(value).local() : moment(value)}
{...other}
/>
)
))
export default DatePickerWrapper

View File

@ -1,15 +1,17 @@
import { Form, Table, Button, Popconfirm } from 'antd'
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
import { useState, useEffect } from 'react'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { EditableCell } from './EditableCell'
import { invokeWebApiWrapperAsync } from '../factory'
const newRowKeyValue = 'newRow'
const actions = [
[['insert'], (data) => [data]],
[['insertRange'], (data) => [[data].flat(1)]],
[['update', 'put'], (data) => data.id && [data.id, data]],
[['edit', 'update', 'put'], (data) => data.id && [data.id, data]],
[['delete'], (data) => data.id && [data.id]],
]

View File

@ -1,23 +0,0 @@
import {Select} from 'antd'
const { Option } = Select
interface StandardComponentProps {
dictionary: Map<any, any>,
}
export const SelectFromDictionary = ({dictionary, ...other}: StandardComponentProps) =>{
const options: any[] = []
dictionary.forEach((value, key) => {
options.push(
<Option
key={key}
value={key}>
{value}
</Option>)
})
return <Select {...other}>
{options}
</Select>
}

View File

@ -1,14 +1,15 @@
import { memo, useEffect, useState, ReactNode } from 'react'
import { InputNumber, Select, Table as RawTable, Tag, SelectProps } from 'antd'
import { InputNumber, Select, Table as RawTable, Tag, SelectProps, TableProps } from 'antd'
import { SelectValue } from 'antd/lib/select'
import { OptionsType } from 'rc-select/lib/interface'
import { Rule } from 'rc-field-form/lib/interface'
import { tryAddKeys } from './EditableTable'
import { makeNumericSorter, makeStringSorter } from './sorters'
import { Rule } from 'rc-field-form/lib/interface'
import { SelectValue } from 'antd/lib/select'
export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters'
export { EditableTable, makeActionHandler } from './EditableTable'
export { DatePickerWrapper } from './DatePickerWrapper'
export { SelectFromDictionary } from './SelectFromDictionary'
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
@ -17,7 +18,7 @@ export const defaultPagination = {
showSizeChanger: true,
}
export const makeNumericRender = (fixed?: number) => (value: any, row: object): ReactNode => {
export const makeNumericRender = (fixed?: number) => (value: any, _: object): ReactNode => {
let val = '-'
if ((value ?? null) !== null && Number.isFinite(+value)) {
val = (fixed ?? null) !== null
@ -225,8 +226,9 @@ export const makeSelectColumn = <T extends unknown = string>(
const makeTagInput = <T extends Record<string, any>>(value_key: string, label_key: string) => memo<{
options: T[],
value?: T[],
onChange?: (values: T[]) => void
}>(({ options, value, onChange }) => {
onChange?: (values: T[]) => void,
other?: SelectProps<SelectValue>,
}>(({ options, value, onChange, other }) => {
const [selectOptions, setSelectOptions] = useState<OptionsType>([])
const [selectedValue, setSelectedValue] = useState<SelectValue>([])
@ -259,6 +261,7 @@ const makeTagInput = <T extends Record<string, any>>(value_key: string, label_ke
return (
<Select
{...other}
mode={'tags'}
options={selectOptions}
value={selectedValue}
@ -273,14 +276,15 @@ export const makeTagColumn = <T extends Record<string, any>>(
options: T[],
value_key: string,
label_key: string,
other?: columnPropsOther
other?: columnPropsOther,
tagOther?: SelectProps<SelectValue>
) => {
const InputComponent = makeTagInput<T>(value_key, label_key)
return makeColumn(title, dataIndex, {
...other,
render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm) ?? elm[label_key]}</Tag>) ?? '-',
input: <InputComponent options={options} />,
input: <InputComponent options={options} other={tagOther} />,
})
}
@ -298,9 +302,9 @@ export const makePaginationObject = (сontainer: PaginationContainer, ...other:
current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
})
interface TableContainer {
export type TableContainer = TableProps<any> & {
dataSource: any[]
children?: any
children?: ReactNode
}
export const Table = ({dataSource, children, ...other}: TableContainer) => (

View File

@ -1,62 +1,58 @@
import { memo, useState } from 'react'
import { Upload, Button } from 'antd'
import { UploadOutlined } from '@ant-design/icons'
import { useState } from 'react'
import { upload } from './factory'
import { ErrorFetch } from './ErrorFetch'
export const UploadForm = ({url, accept, style, formData, onUploadStart, onUploadSuccess, onUploadComplete, onUploadError}) => {
export const UploadForm = memo(({ url, accept, style, formData, onUploadStart, onUploadSuccess, onUploadComplete, onUploadError }) => {
const [fileList, setfileList] = useState([])
const handleFileSend = async () => {
if(onUploadStart)
onUploadStart()
onUploadStart?.()
try {
const formDataLocal = new FormData()
fileList.forEach((val) => {
formDataLocal.append("files", val.originFileObj)
})
fileList.forEach((val) => formDataLocal.append('files', val.originFileObj))
if(formData)
for(var propName in formData)
for(const propName in formData)
formDataLocal.append(propName, formData[propName])
const response = await upload(url, formDataLocal)
if(!response.ok)
{
const errorText = await response.text()
if (!response.ok) {
const errorText = await response.text()
const error = new ErrorFetch(response.status, errorText)
throw error
}
else{
if(onUploadSuccess)
onUploadSuccess()
} else {
onUploadSuccess?.()
}
} catch(error) {
if(onUploadError)
onUploadError(error)
onUploadError?.(error)
} finally {
setfileList([])
if(onUploadComplete)
onUploadComplete()
onUploadComplete?.()
}
}
const isSendButtonEnabled = fileList.length > 0
return(
<div style={{display: 'flex', ...style}}>
<Upload
name ="file"
fileList={fileList}
<div style={{ display: 'flex', ...style }}>
<Upload
name={'file'}
accept={accept}
onChange={(props) => setfileList(props.fileList)}>
fileList={fileList}
onChange={(props) => setfileList(props.fileList)}
>
<Button icon={<UploadOutlined/>}>Загрузить файл</Button>
</Upload>
<Button type="primary"
<Button
type={'primary'}
onClick={handleFileSend}
disabled={!isSendButtonEnabled}
style={{marginLeft: '10px', display:isSendButtonEnabled?'':'none'}}
onClick={handleFileSend}>
style={{ marginLeft: '10px', display: isSendButtonEnabled ? '' : 'none' }}
>
Отправить
</Button>
</div>
)
}
})

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import { ChartOpertationTimeBase } from './ChartOperationTimeBase'
import moment from 'moment'
import { useEffect, useState } from 'react'
import { ChartOpertationTimeBase } from './ChartOperationTimeBase'
export const CreateLabels = () => {
let labels = []

View File

@ -19,25 +19,25 @@ const defaultOptions = {
responsive: true,
title: {
display: true,
position: "top",
text: "Doughnut Chart",
position: 'top',
text: 'Doughnut Chart',
fontSize: 18,
fontColor: "#111"
fontColor: '#111'
},
legend: {
display: true,
position: "bottom",
position: 'bottom',
labels: {
fontColor: "#333",
fontColor: '#333',
fontSize: 16
}
}
}
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
x: String;
label: number;
y: number;
x: String
label: number
y: number
}[], unknown>
export type ChartTimeDataParams = {
@ -93,37 +93,37 @@ export const timeParamsByInterval = (intervalSec: number): TimeParams => {
let unit = timeUnitByInterval(intervalSec)
switch (unit) {
case "millisecond":
case 'millisecond':
stepSize *= 1000
break;
case "second":
break
case 'second':
//stepSize *= 1
break;
case "minute":
break
case 'minute':
stepSize /= 60
break;
case "hour":
break
case 'hour':
stepSize /= 60 * 60
break;
case "day":
break
case 'day':
stepSize /= 24 * 60 * 60
break;
case "week":
break
case 'week':
stepSize /= 7 * 24 * 60 * 60
break;
case "month":
break
case 'month':
stepSize /= 30 * 24 * 60 * 60
break;
case "quarter":
break
case 'quarter':
stepSize /= 91 * 24 * 60 * 60
break;
case "year":
break
case 'year':
stepSize /= 365.25 * 24 * 60 * 60
break;
break
}
stepSize = Math.round(stepSize / linesPerInterval)
stepSize = stepSize > 0 ? stepSize : 1;
stepSize = stepSize > 0 ? stepSize : 1
return { unit, stepSize }
}
@ -174,4 +174,4 @@ export const ChartOpertationTimeBase: React.FC<ChartTimeBaseProps> = ({ options,
}, [chart, dataParams, options])
return (<canvas ref={chartRef} />)
}
}

View File

@ -1,4 +1,4 @@
import {useEffect, useRef, useState} from 'react';
import { useEffect, useRef, useState } from 'react'
import {
Chart,
TimeScale,
@ -8,30 +8,30 @@ import {
PointElement,
LineElement
} from 'chart.js'
import 'chartjs-adapter-moment';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import zoomPlugin from 'chartjs-plugin-zoom';
import 'chartjs-adapter-moment'
import ChartDataLabels from 'chartjs-plugin-datalabels'
import zoomPlugin from 'chartjs-plugin-zoom'
Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin);
Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin)
const defaultOptions = {
responsive: true,
aspectRatio: 2.45,
scales: {
x:{
x: {
type: 'time',
time: {
unit: 'hour',
displayFormats: {
'hour': 'MM.DD'
hour: 'MM.DD'
},
tooltipFormat: 'DD T'
},
},
y:{
type:'linear',
position:'top',
reverse:true,
y: {
type: 'linear',
position: 'top',
reverse: true,
// ticks: {
// // forces step size to be 50 units
// stepSize: 50
@ -42,20 +42,20 @@ const defaultOptions = {
xAxisKey: 'date',
yAxisKey: 'depth'
},
elements:{
point:{
radius:1.7,
hoverRadius:5,
elements: {
point: {
radius: 1.7,
hoverRadius: 5,
},
},
plugins:{
plugins: {
datalabels: {
display: false,
},
}
}
const makeDataset = (data, label, color, width=1.5, dash) => ({
const makeDataset = (data, label, color, width = 1.5, dash) => ({
label: label,
data: data,
backgroundColor: color,
@ -64,23 +64,23 @@ const makeDataset = (data, label, color, width=1.5, dash) => ({
borderDash: dash,
})
export const ChartTelemetryDepthToDay = ({depthData, bitPositionData}) => {
export const ChartTelemetryDepthToDay = ({ depthData, bitPositionData }) => {
const chartRef = useRef(null)
const [chart, setChart] = useState()
useEffect(() => {
let data = {
const data = {
datasets: [
makeDataset(depthData, 'Глубина', '#0A0'),
makeDataset(bitPositionData, 'Положение долота', 'blue')
makeDataset(bitPositionData, 'Положение долота', 'blue'),
]
}
if((chartRef.current)&&(!chart)) {
let thisOptions = {}
if(chartRef.current && !chart) {
const thisOptions = {}
Object.assign(thisOptions, defaultOptions)
let newChart = new Chart(chartRef.current, {
const newChart = new Chart(chartRef.current, {
type: 'line',
plugins: [ChartDataLabels],
options: thisOptions,
@ -90,10 +90,10 @@ export const ChartTelemetryDepthToDay = ({depthData, bitPositionData}) => {
return () => chart?.destroy()
} else {
chart.data = data
chart.data = data
chart.update()
}
}, [chart, depthData, bitPositionData])
return(<canvas ref={chartRef} />)
}
}

View File

@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from 'react'
import { Chart, registerables } from 'chart.js';
import { Chart, registerables } from 'chart.js'
Chart.register(...registerables);
Chart.register(...registerables)
const defaultOptions = {
responsive: true,
@ -32,49 +32,43 @@ export const ChartTelemetryDepthToInterval = ({ depthToIntervalData }) => {
const [chart, setChart] = useState()
const calculateBarWidth = (dataLength) => {
if (dataLength < 3)
return 150
else if (dataLength < 16)
return 70
else
return 10
if (dataLength < 3) return 150
if (dataLength < 16) return 70
return 10
}
useEffect(() => {
const xData = depthToIntervalData.map(el => new Date(el.intervalStartDate).toLocaleString())
const yData = depthToIntervalData.map(el => el.intervalDepthProgress.toFixed(3))
let data = {
const data = {
labels: xData,
datasets: [
{
label: 'Скорость проходки за интервал',
data: yData,
borderColor: '#00b300',
borderWidth: 2,
backgroundColor: '#0A0',
barThickness: calculateBarWidth(xData.length)
}
]
datasets: [{
label: 'Скорость проходки за интервал',
data: yData,
borderColor: '#00b300',
borderWidth: 2,
backgroundColor: '#0A0',
barThickness: calculateBarWidth(xData.length)
}]
}
let thisOptions = {}
const thisOptions = {}
Object.assign(thisOptions, defaultOptions)
if ((chartRef.current) && (!chart)) {
let newChart = new Chart(chartRef.current, {
if (chartRef.current && !chart) {
const newChart = new Chart(chartRef.current, {
type: 'bar',
options: thisOptions,
data: data
})
setChart(newChart)
} else {
chart.data = data
chart.data = data
chart.options = thisOptions
chart.update()
}
}, [chart, depthToIntervalData])
return (<canvas ref={chartRef} />)
}
}

View File

@ -1,14 +1,14 @@
import { useEffect, useRef, useState } from 'react'
import { Chart, registerables } from 'chart.js';
import { Chart, registerables } from 'chart.js'
Chart.register(...registerables);
Chart.register(...registerables)
const transformSecondsToHoursString = (seconds) => {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const s = seconds - (hours * 3600) - (minutes * 60)
return hours + ' ч.' + minutes + ' мин.' + s + ' сек.'
return `${hours} ч.${minutes} мин.${s} сек.`
}
const transformSecondsToTimeString = (seconds) => {
@ -17,8 +17,8 @@ const transformSecondsToTimeString = (seconds) => {
else if(seconds < 60)
return seconds + ' сек.'
else if (seconds < 3600)
return Math.floor(seconds / 60) + ' мин. ' + (0.6 * (seconds % 60)).toFixed() + ' сек.'
else
return Math.floor(seconds / 60) + ' мин. ' + (0.6 * (seconds % 60)).toFixed() + ' сек.'
else
return transformSecondsToHoursString(seconds)
}
@ -34,32 +34,29 @@ const defaultOptions = {
}
},
tooltip: {
callbacks: {
label: function(tooltipItem) {
return transformSecondsToTimeString(tooltipItem.parsed);
},
}
callbacks: {
label: (tooltipItem) => transformSecondsToTimeString(tooltipItem.parsed),
}
}
}
}
const chartPartsColors = [
'#54a60c', '#0ca68a', '#0c8aa6', '#0c57a6', '#0c33a6',
'#6f10d5', '#d510a1', '#f1bc41', '#c5f141', '#41f196',
'#41cbf1', '#4196f1', '#bf41f1', '#41f1c5', '#cbf141',
'#f1ce41', '#f17f41', '#f14141', '#34b40e', '#420eb4'
]
export const ChartTelemetryOperationsSummary = ({ operationsData }) => {
const chartRef = useRef(null)
const [chart, setChart] = useState()
useEffect(() => {
const namesData = operationsData?.map(el => el.operationName) ?? ['Нет операций']
const durationsData = operationsData?.map(el => el.duration) ?? [1]
let chartPartsColors = [
'#54a60c', '#0ca68a', '#0c8aa6', '#0c57a6', '#0c33a6',
'#6f10d5', '#d510a1', '#f1bc41', '#c5f141', '#41f196',
'#41cbf1', '#4196f1', '#bf41f1', '#41f1c5', '#cbf141',
'#f1ce41', '#f17f41', '#f14141', '#34b40e', '#420eb4'
]
let data = {
const data = {
labels: namesData,
datasets: [
{
@ -71,22 +68,22 @@ export const ChartTelemetryOperationsSummary = ({ operationsData }) => {
]
}
let thisOptions = {}
const thisOptions = {}
Object.assign(thisOptions, defaultOptions)
if ((chartRef.current) && (!chart)) {
let newChart = new Chart(chartRef.current, {
if (chartRef.current && !chart) {
const newChart = new Chart(chartRef.current, {
type: 'doughnut',
options: thisOptions,
data: data
})
setChart(newChart)
} else {
chart.data = data
chart.data = data
chart.options = thisOptions
chart.update()
}
}, [chart, operationsData])
return (<canvas ref={chartRef} />)
}
}

View File

@ -1,16 +1,14 @@
/* eslint-disable react-hooks/exhaustive-deps */
import moment from 'moment'
import { DatePicker } from 'antd'
import { useState, useEffect } from 'react'
import { useState, useEffect, memo } from 'react'
import { Flex } from '../../components/Grid'
import { makeDateSorter } from '../../components/Table'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { PeriodPicker, defaultPeriod } from '../../components/PeriodPicker'
import { TelemetryDataSaubService } from '../../services/api'
import { Flex } from '@components/Grid'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DatePickerWrapper, makeDateSorter } from '@components/Table'
import { PeriodPicker, defaultPeriod } from '@components/PeriodPicker'
import { TelemetryDataSaubService } from '@api'
import { normalizeData } from '../TelemetryView'
import { normalizeData } from '@pages/TelemetryView'
import { ArchiveDisplay, cutData } from './ArchiveDisplay'
const DATA_COUNT = 2048 // Колличество точек на подгрузку графика
@ -63,7 +61,7 @@ const range = (start, end) => {
return result
}
export default function Archive({idWell}) {
export const Archive = memo(({ idWell }) => {
const [dataSaub, setDataSaub] = useState([])
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
const [chartInterval, setChartInterval] = useState(parseInt(defaultPeriod) * 1000)
@ -157,13 +155,11 @@ export default function Archive({idWell}) {
<Flex style={{margin: '8px 8px 0'}}>
<div>
Начальная дата:&nbsp;
<DatePicker
showTime
allowClear={false}
onChange={(startDate) => setStartDate(new Date(startDate))}
value={moment(startDate)}
<DatePickerWrapper
value={startDate}
disabledDate={isDateDisabled}
disabledTime={isDateTimeDisabled}
onChange={(startDate) => setStartDate(new Date(startDate))}
/>
</div>
<div style={{ marginLeft: '1rem' }}>
@ -181,4 +177,6 @@ export default function Archive({idWell}) {
</LoaderPortal>
</>
)
}
})
export default Archive

View File

@ -2,18 +2,18 @@ import { useEffect, useState } from 'react'
import { FileExcelOutlined } from '@ant-design/icons'
import { Popconfirm, Button, Tooltip, Typography } from 'antd'
import { Flex } from '../../components/Grid'
import { MarkView, UserView } from '../../components/views'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync, download, formatBytes } from '../../components/factory'
import { DrillingProgramService, WellService } from '../../services/api'
import { Flex } from '@components/Grid'
import LoaderPortal from '@components/LoaderPortal'
import { MarkView, UserView } from '@components/views'
import { invokeWebApiWrapperAsync, download, formatBytes } from '@components/factory'
import { DrillingProgramService, WellService } from '@api'
import DocumentsTemplate from './DocumentsTemplate'
import DocumentsTemplate from '@pages/Documents/DocumentsTemplate'
const idFileCategoryDrillingProgramItems = 13
const {Text} = Typography
const { Text } = Typography
export default function DrillingProgram({ idWell }) {
export const DrillingProgram = ({ idWell }) => {
const [downloadButtonEnabled, selDownloadButtonEnabled] = useState(false)
const [showLoader, setShowLoader] = useState(false)
const [tooltip, setTooltip] = useState('нет файлов для формирования')
@ -27,15 +27,13 @@ export default function DrillingProgram({ idWell }) {
setWellLabel(well.caption ?? `${idWell}`)
},
setShowLoader,
`Не удалось загрузить название скважины '${idWell}'`
`Не удалось загрузить название скважины "${idWell}"`
), [idWell])
const urlDownloadProgram = `/api/well/${idWell}/drillingProgram`
const downloadProgram = () => invokeWebApiWrapperAsync(
async () => await download(urlDownloadProgram),
async () => await download(`/api/well/${idWell}/drillingProgram`),
setShowLoader,
'Не удалось загрузить программу бурения'
`Не удалось загрузить программу бурения для скважины "${idWell}"`
)
const openProgramPreview = () => invokeWebApiWrapperAsync(
@ -151,15 +149,17 @@ export default function DrillingProgram({ idWell }) {
return (
<LoaderPortal show={showLoader}>
<DocumentsTemplate
beforeTable={lastUpdatedFileView}
key={childKey}
idWell={idWell}
idCategory={idFileCategoryDrillingProgramItems}
accept={'.xlsx'}
headerChild={downloadButton}
onChange={filesUpdated}
customColumns = {customColumns}
key = {childKey}
headerChild={downloadButton}
customColumns={customColumns}
beforeTable={lastUpdatedFileView}
idCategory={idFileCategoryDrillingProgramItems}
/>
</LoaderPortal>
)
}
export default DrillingProgram

View File

@ -1,9 +1,17 @@
import { LoadingOutlined } from '@ant-design/icons'
import { Flex } from '../components/Grid'
import { CSSProperties, memo } from 'react'
export const SuspenseFallback = ({ style }) => (
import { Flex } from '@components/Grid'
export type SuspenseFallbackProps = {
style?: CSSProperties
}
export const SuspenseFallback = memo<SuspenseFallbackProps>(({ style }) => (
<Flex style={{ justifyContent: 'center', alignItems: 'center', minHeight: '400px', ...style }}>
<LoadingOutlined />
<div style={{ marginLeft: '10px' }}>Страница загружается, пожалуйста, подождите...</div>
</Flex>
)
))
export default SuspenseFallback

View File

@ -1,6 +1,3 @@
export const TelemetryAnalysisOperationsToInterval = ({ idWell }) => (<>123</>)
export default function TelemetryAnalysisOperationsToInterval({idWell}){
return (<>123</>)
}
export default TelemetryAnalysisOperationsToInterval

View File

@ -1,64 +1,62 @@
import { memo } from 'react'
const styleBase = {
stroke: "none",
strokeLinecap: "butt",
strokeLinejoin: "miter",
stroke: 'none',
strokeLinecap: 'butt',
strokeLinejoin: 'miter',
}
const styleCasing = {
...styleBase,
fill: "#808080",
fill: '#808080',
};
const styleGround = {
...styleBase,
fill: "#deaa87",
fill: '#deaa87',
};
const styleTower = {
...styleBase,
fill: "#999999",
fill: '#999999',
};
const styleWell = {
...styleBase,
fill: "#37abc8",
fill: '#37abc8',
};
const styleBitDrive = {
...styleBase,
fill: "#ff8080",
fill: '#ff8080',
};
const styleBitTool = {
...styleBase,
fill: "#f9f9f9",
fill: '#f9f9f9',
};
const styleBlock = {
...styleBase,
fill: "#ffdd55",
fill: '#ffdd55',
};
// const stylePump1 = {
// ...styleBase,
// fill: "#5fd35f",
// fill: '#5fd35f',
// };
// const stylePump2 = {
// ...styleBase,
// fill: "#b3b3b3",
// fill: '#b3b3b3',
// };
export default function RigMnemo({
blockPosition,
bitPosition,
wellDepth
}) {
export const RigMnemo = memo(({ blockPosition, bitPosition, wellDepth }) => {
let blockPositionY = 0
let bitPositionY = 0
const isBlockVisible = blockPosition != null && Number.isFinite(blockPosition)
const isBitVisible = wellDepth && bitPosition != null && Number.isFinite(bitPosition) && Number.isFinite(wellDepth)
const isBlockVisible = Number.isFinite(blockPosition)
const isBitVisible = Number.isFinite(bitPosition) && Number.isFinite(wellDepth)
if (isBlockVisible)
blockPositionY = -35 + (50 * (30 - +blockPosition) / 30)
@ -76,133 +74,91 @@ export default function RigMnemo({
bitPositionY = -59 + 59 * bitPositionLocal / wellDepthLocal
if( wellDepthLocal > 20 && (wellDepthLocal - bitPositionLocal) > 2 && bitPositionProc > 0.96)
bitPositionY = -2
bitPositionY = -2
}
const blockTranslate = `translate(-75, ${blockPositionY})`
const bitTranslate = `translate(0, ${bitPositionY})`
return (
<svg
width="170"
height="540"
viewBox="20 0 40 145"
version="1.1"
id="svg8"
>
<g id="Casing" style={styleCasing}>
<path d="m 29.897917,67.46875 v -1.322917 l 4.497916,-10e-7 v -1.322916 h 3.96875 v 1.322916 l 4.497917,10e-7 v 1.322917 z" />
<path d="M 31.75,97.895832 V 67.468749 h 9.260416 v 30.427083 z" />
<path d="m 30.95625,83.34375 0,1.058333 h 10.847917 l -10e-7,-1.058333 z" />
<path d="m 30.95625,85.195833 v 1.058333 h 10.847917 v -1.058333 z" />
<path d="m 30.95625,70.114583 v 1.058333 h 10.847918 l -10e-7,-1.058333 z" />
<path d="m 30.95625,71.966666 v 1.058333 h 10.847918 v -1.058333 z" />
<svg width={'170'} height={'540'} viewBox={'20 0 40 145'} version={'1.1'}>
<g id={'Casing'} style={styleCasing}>
<path d={'m 29.897917,67.46875 v -1.322917 l 4.497916,-10e-7 v -1.322916 h 3.96875 v 1.322916 l 4.497917,10e-7 v 1.322917 z'} />
<path d={'M 31.75,97.895832 V 67.468749 h 9.260416 v 30.427083 z'} />
<path d={'m 30.95625,83.34375 0,1.058333 h 10.847917 l -10e-7,-1.058333 z'} />
<path d={'m 30.95625,85.195833 v 1.058333 h 10.847917 v -1.058333 z'} />
<path d={'m 30.95625,70.114583 v 1.058333 h 10.847918 l -10e-7,-1.058333 z'} />
<path d={'m 30.95625,71.966666 v 1.058333 h 10.847918 v -1.058333 z'} />
</g>
<g id="Ground">
<path
style={styleGround}
d="m 62.177083,83.34375 -19.84375,0 V 97.895833 H 39.6875 V 142.875 H 33.072917 V 97.895833 H 30.427083 V 83.34375 l -10.583333,0 v 62.17708 h 42.333333 z"
id="path849"
/>
<g id={'Ground'}>
<path style={styleGround} d={'m 62.177083,83.34375 -19.84375,0 V 97.895833 H 39.6875 V 142.875 H 33.072917 V 97.895833 H 30.427083 V 83.34375 l -10.583333,0 v 62.17708 h 42.333333 z'} />
</g>
<g id="Tower" style={styleTower}>
<path d="m 29.104166,83.343749 v -2.645833 h -3.96875 v 2.645833 z" />
<path d="m 43.656249,83.343749 v -2.645833 h 3.96875 v 2.645833 z" />
<path d="m 26.458333,80.697916 v -11.90625 h 1.322917 v 11.90625 z" />
<path d="m 44.979166,80.697917 v -11.90625 h 1.322917 v 11.90625 z" />
<path d="m 22.489583,68.791666 h 27.78125 v -1.322917 h -27.78125 z" />
<path d="M 22.357422 66.013672 L 22.357422 67.601562 L 22.490234 67.601562 L 50.402344 67.601562 L 50.402344 66.013672 L 50.271484 66.013672 C 47.662479 66.013673 45.393545 66.029632 41.142578 66.013672 L 40.878906 66.013672 L 31.882812 66.013672 L 31.75 66.013672 C 31.693076 66.013672 31.673038 66.013672 31.617188 66.013672 C 27.779098 66.013674 26.402061 66.013673 22.490234 66.013672 L 22.357422 66.013672 z M 22.621094 66.277344 C 26.405208 66.277345 27.834127 66.277346 31.617188 66.277344 L 31.617188 67.335938 L 22.621094 67.335938 L 22.621094 66.277344 z M 31.882812 66.277344 L 40.878906 66.277344 L 40.878906 67.335938 L 31.882812 67.335938 L 31.882812 66.277344 z M 41.142578 66.277344 C 45.330989 66.293059 47.579975 66.277812 50.138672 66.277344 L 50.138672 67.335938 L 41.142578 67.335938 L 41.142578 66.277344 z " />
<path d="M 34.263672 2.5136719 L 34.263672 2.6464844 L 34.263672 6.7460938 L 38.496094 6.7460938 L 38.496094 2.5136719 L 34.263672 2.5136719 z M 34.527344 2.7773438 L 38.232422 2.7773438 L 38.232422 6.4824219 L 34.527344 6.4824219 L 34.527344 2.7773438 z " />
<path d="m 34.395833,4.4979166 h 3.96875 v 0.2645833 h -3.96875 z" />
<path d="M 36.247916,2.6458333 V 6.6145832 H 36.5125 V 2.6458333 Z" />
<path d="M 34.395833,6.6145833 V 7.9375 h 3.96875 V 6.6145833 Z" />
<path d="m 26.458333,68.791666 7.9375,-60.8541661 h 1.322917 l -7.9375,60.8541661 z" />
<path d="m 44.979166,68.791666 -7.9375,-60.8541661 h 1.322917 l 7.9375,60.8541661 z" />
<path d="M 42.333333,26.458333 H 30.427083 L 27.78125,25.135416 h 17.197916 z" />
<path d="M 34.396484 18.388672 L 34.396484 18.652344 L 38.365234 18.652344 L 38.365234 18.388672 L 34.396484 18.388672 z " />
<path d="M 34.925781 13.097656 L 34.925781 13.361328 L 37.835938 13.361328 L 37.835938 13.097656 L 34.925781 13.097656 z " />
<path d="M 37.724609 13.15625 L 34.285156 18.449219 L 34.505859 18.59375 L 37.947266 13.300781 L 37.724609 13.15625 z " />
<path d="M 35.841797 7.8886719 L 35.595703 7.9863281 L 37.712891 13.277344 L 37.958984 13.179688 L 35.841797 7.8886719 z " />
<path d="M 34.501953 18.441406 L 34.291016 18.601562 L 39.318359 25.214844 L 39.527344 25.054688 L 34.501953 18.441406 z " />
<path d="M 28.574219 62.044922 L 28.574219 62.308594 L 44.185547 62.308594 L 44.185547 62.044922 L 28.574219 62.044922 z " />
<path d="M 29.369141 55.429688 L 29.369141 55.695312 L 43.392578 55.695312 L 43.392578 55.429688 L 29.369141 55.429688 z " />
<g id="g985">
<path d="m 42.333333,48.947917 -11.90625,-10e-7" />
<path d="M 30.427734 48.816406 L 30.427734 49.080078 L 42.333984 49.080078 L 42.333984 48.816406 L 30.427734 48.816406 z " />
<g id={'Tower'} style={styleTower}>
<path d={'m 29.104166,83.343749 v -2.645833 h -3.96875 v 2.645833 z'} />
<path d={'m 43.656249,83.343749 v -2.645833 h 3.96875 v 2.645833 z'} />
<path d={'m 26.458333,80.697916 v -11.90625 h 1.322917 v 11.90625 z'} />
<path d={'m 44.979166,80.697917 v -11.90625 h 1.322917 v 11.90625 z'} />
<path d={'m 22.489583,68.791666 h 27.78125 v -1.322917 h -27.78125 z'} />
<path d={'M 22.357422 66.013672 L 22.357422 67.601562 L 22.490234 67.601562 L 50.402344 67.601562 L 50.402344 66.013672 L 50.271484 66.013672 C 47.662479 66.013673 45.393545 66.029632 41.142578 66.013672 L 40.878906 66.013672 L 31.882812 66.013672 L 31.75 66.013672 C 31.693076 66.013672 31.673038 66.013672 31.617188 66.013672 C 27.779098 66.013674 26.402061 66.013673 22.490234 66.013672 L 22.357422 66.013672 z M 22.621094 66.277344 C 26.405208 66.277345 27.834127 66.277346 31.617188 66.277344 L 31.617188 67.335938 L 22.621094 67.335938 L 22.621094 66.277344 z M 31.882812 66.277344 L 40.878906 66.277344 L 40.878906 67.335938 L 31.882812 67.335938 L 31.882812 66.277344 z M 41.142578 66.277344 C 45.330989 66.293059 47.579975 66.277812 50.138672 66.277344 L 50.138672 67.335938 L 41.142578 67.335938 L 41.142578 66.277344 z '} />
<path d={'M 34.263672 2.5136719 L 34.263672 2.6464844 L 34.263672 6.7460938 L 38.496094 6.7460938 L 38.496094 2.5136719 L 34.263672 2.5136719 z M 34.527344 2.7773438 L 38.232422 2.7773438 L 38.232422 6.4824219 L 34.527344 6.4824219 L 34.527344 2.7773438 z '} />
<path d={'m 34.395833,4.4979166 h 3.96875 v 0.2645833 h -3.96875 z'} />
<path d={'M 36.247916,2.6458333 V 6.6145832 H 36.5125 V 2.6458333 Z'} />
<path d={'M 34.395833,6.6145833 V 7.9375 h 3.96875 V 6.6145833 Z'} />
<path d={'m 26.458333,68.791666 7.9375,-60.8541661 h 1.322917 l -7.9375,60.8541661 z'} />
<path d={'m 44.979166,68.791666 -7.9375,-60.8541661 h 1.322917 l 7.9375,60.8541661 z'} />
<path d={'M 42.333333,26.458333 H 30.427083 L 27.78125,25.135416 h 17.197916 z'} />
<path d={'M 34.396484 18.388672 L 34.396484 18.652344 L 38.365234 18.652344 L 38.365234 18.388672 L 34.396484 18.388672 z '} />
<path d={'M 34.925781 13.097656 L 34.925781 13.361328 L 37.835938 13.361328 L 37.835938 13.097656 L 34.925781 13.097656 z '} />
<path d={'M 37.724609 13.15625 L 34.285156 18.449219 L 34.505859 18.59375 L 37.947266 13.300781 L 37.724609 13.15625 z '} />
<path d={'M 35.841797 7.8886719 L 35.595703 7.9863281 L 37.712891 13.277344 L 37.958984 13.179688 L 35.841797 7.8886719 z '} />
<path d={'M 34.501953 18.441406 L 34.291016 18.601562 L 39.318359 25.214844 L 39.527344 25.054688 L 34.501953 18.441406 z '} />
<path d={'M 28.574219 62.044922 L 28.574219 62.308594 L 44.185547 62.308594 L 44.185547 62.044922 L 28.574219 62.044922 z '} />
<path d={'M 29.369141 55.429688 L 29.369141 55.695312 L 43.392578 55.695312 L 43.392578 55.429688 L 29.369141 55.429688 z '} />
<g id={'g985'}>
<path d={'m 42.333333,48.947917 -11.90625,-10e-7'} />
<path d={'M 30.427734 48.816406 L 30.427734 49.080078 L 42.333984 49.080078 L 42.333984 48.816406 L 30.427734 48.816406 z '} />
</g>
<g>
<path d="M 41.539583,42.333333 H 31.220833" />
<path d="M 31.220703 42.201172 L 31.220703 42.464844 L 41.539062 42.464844 L 41.539062 42.201172 L 31.220703 42.201172 z " />
<path d={'M 41.539583,42.333333 H 31.220833'} />
<path d={'M 31.220703 42.201172 L 31.220703 42.464844 L 41.539062 42.464844 L 41.539062 42.201172 L 31.220703 42.201172 z '} />
</g>
<path d="M 31.326172 42.253906 L 31.115234 42.414062 L 36.142578 49.027344 L 36.248047 49.080078 L 36.511719 49.080078 L 36.617188 49.027344 L 41.644531 42.414062 L 41.433594 42.253906 L 36.447266 48.816406 L 36.314453 48.816406 L 31.326172 42.253906 z " />
<path d="M 33.460938 26.412109 L 33.212891 26.503906 L 36.15625 34.527344 L 36.605469 34.527344 L 39.546875 26.503906 L 39.298828 26.412109 L 36.419922 34.263672 L 36.339844 34.263672 L 33.460938 26.412109 z " />
<path d="M 32.398438 34.335938 L 32.160156 34.455078 L 36.166016 42.464844 L 36.59375 42.464844 L 40.599609 34.455078 L 40.363281 34.335938 L 36.431641 42.201172 L 36.330078 42.201172 L 32.398438 34.335938 z " />
<path d="M 30.527344 48.861328 L 30.328125 49.035156 L 36.1875 55.695312 L 36.572266 55.695312 L 42.433594 49.035156 L 42.234375 48.861328 L 36.453125 55.429688 L 36.308594 55.429688 L 30.527344 48.861328 z " />
<path d="M 36.199219 55.429688 L 28.488281 62.076172 L 28.662109 62.277344 L 36.296875 55.695312 L 36.462891 55.695312 L 44.099609 62.277344 L 44.271484 62.076172 L 36.5625 55.429688 L 36.199219 55.429688 z " />
<path d="M 32.279297 34.263672 L 32.279297 34.527344 L 40.480469 34.527344 L 40.480469 34.263672 L 32.279297 34.263672 z " />
<path d={'M 31.326172 42.253906 L 31.115234 42.414062 L 36.142578 49.027344 L 36.248047 49.080078 L 36.511719 49.080078 L 36.617188 49.027344 L 41.644531 42.414062 L 41.433594 42.253906 L 36.447266 48.816406 L 36.314453 48.816406 L 31.326172 42.253906 z '} />
<path d={'M 33.460938 26.412109 L 33.212891 26.503906 L 36.15625 34.527344 L 36.605469 34.527344 L 39.546875 26.503906 L 39.298828 26.412109 L 36.419922 34.263672 L 36.339844 34.263672 L 33.460938 26.412109 z '} />
<path d={'M 32.398438 34.335938 L 32.160156 34.455078 L 36.166016 42.464844 L 36.59375 42.464844 L 40.599609 34.455078 L 40.363281 34.335938 L 36.431641 42.201172 L 36.330078 42.201172 L 32.398438 34.335938 z '} />
<path d={'M 30.527344 48.861328 L 30.328125 49.035156 L 36.1875 55.695312 L 36.572266 55.695312 L 42.433594 49.035156 L 42.234375 48.861328 L 36.453125 55.429688 L 36.308594 55.429688 L 30.527344 48.861328 z '} />
<path d={'M 36.199219 55.429688 L 28.488281 62.076172 L 28.662109 62.277344 L 36.296875 55.695312 L 36.462891 55.695312 L 44.099609 62.277344 L 44.271484 62.076172 L 36.5625 55.429688 L 36.199219 55.429688 z '} />
<path d={'M 32.279297 34.263672 L 32.279297 34.527344 L 40.480469 34.527344 L 40.480469 34.263672 L 32.279297 34.263672 z '} />
</g>
<g id="Well" style={styleWell}>
<path d="M 33.072916,97.895832 H 39.6875 V 142.875 h -6.614584 z" />
<g id={'Well'} style={styleWell}>
<path d={'M 33.072916,97.895832 H 39.6875 V 142.875 h -6.614584 z'} />
</g>
{isBitVisible &&
<g id="Bit"
transform={bitTranslate}>
<path
style={styleBitDrive}
d="m 33.072916,142.875 1.322917,-3.96875 h 3.96875 L 39.6875,142.875 h -6.614584"
/>
<path
style={styleBitTool}
d="m 34.395833,138.90625 v -3.96875 h 3.96875 v 3.96875 z"
/>
{isBitVisible && (
<g id={'Bit'} transform={bitTranslate}>
<path style={styleBitDrive} d={'m 33.072916,142.875 1.322917,-3.96875 h 3.96875 L 39.6875,142.875 h -6.614584'} />
<path style={styleBitTool} d={'m 34.395833,138.90625 v -3.96875 h 3.96875 v 3.96875 z'} />
</g>
}
{isBlockVisible &&
<g id="Block"
transform={blockTranslate}
style={styleBlock}
>
<path d="m 111.125,45.772916 c 0,0 -0.79375,-1.102431 -0.79375,-1.322917 0,-0.220486 -0.0952,-0.338724 0,-0.529166 0.0952,-0.190442 0.29053,-0.433713 0.52917,-0.529167 0.23864,-0.09545 0.55512,-0.09545 0.79375,0 0.23863,0.09545 0.43394,0.338726 0.52916,0.529167 0.0952,0.190441 0,0.440972 0,0.529166 0,0.08819 -0.79375,1.322917 -0.79375,1.322917 z" />
<path d="m 111.38958,45.772916 c 0,0 0.17047,0.398584 0.26459,0.529167 0.0941,0.130583 0.22384,0.166227 0.26458,0.264583 0.0407,0.09836 0.0328,0.390283 0,0.529167 -0.0328,0.138884 -0.10568,0.166378 -0.26458,0.264584 -0.1589,0.09821 -0.37027,0.09821 -0.52917,0 -0.1589,-0.09821 -0.23179,-0.1257 -0.26458,-0.264584 -0.0328,-0.138884 0,-0.529167 0,-0.529167 0,0 0.10568,0.430961 0.26458,0.529167 0.1589,0.09821 0.40444,0.124726 0.52917,0 0.12472,-0.124725 0.0576,-0.39007 0,-0.529167 -0.0576,-0.139097 -0.17047,-0.133999 -0.26459,-0.264583 -0.0941,-0.130584 -0.26458,-0.529167 -0.26458,-0.529167 z" />
<path d="m 110.33125,47.360416 v 2.38125 l 0.79375,10e-7 v 0.529166 h -0.26458 V 50.8 l 1.32291,-10e-7 v -0.529166 h -0.26458 v -0.529167 l 0.79375,10e-7 v -2.38125 z" />
)}
{isBlockVisible && (
<g id={'Block'} transform={blockTranslate} style={styleBlock}>
<path d={'m 111.125,45.772916 c 0,0 -0.79375,-1.102431 -0.79375,-1.322917 0,-0.220486 -0.0952,-0.338724 0,-0.529166 0.0952,-0.190442 0.29053,-0.433713 0.52917,-0.529167 0.23864,-0.09545 0.55512,-0.09545 0.79375,0 0.23863,0.09545 0.43394,0.338726 0.52916,0.529167 0.0952,0.190441 0,0.440972 0,0.529166 0,0.08819 -0.79375,1.322917 -0.79375,1.322917 z'} />
<path d={'m 111.38958,45.772916 c 0,0 0.17047,0.398584 0.26459,0.529167 0.0941,0.130583 0.22384,0.166227 0.26458,0.264583 0.0407,0.09836 0.0328,0.390283 0,0.529167 -0.0328,0.138884 -0.10568,0.166378 -0.26458,0.264584 -0.1589,0.09821 -0.37027,0.09821 -0.52917,0 -0.1589,-0.09821 -0.23179,-0.1257 -0.26458,-0.264584 -0.0328,-0.138884 0,-0.529167 0,-0.529167 0,0 0.10568,0.430961 0.26458,0.529167 0.1589,0.09821 0.40444,0.124726 0.52917,0 0.12472,-0.124725 0.0576,-0.39007 0,-0.529167 -0.0576,-0.139097 -0.17047,-0.133999 -0.26459,-0.264583 -0.0941,-0.130584 -0.26458,-0.529167 -0.26458,-0.529167 z'} />
<path d={'m 110.33125,47.360416 v 2.38125 l 0.79375,10e-7 v 0.529166 h -0.26458 V 50.8 l 1.32291,-10e-7 v -0.529166 h -0.26458 v -0.529167 l 0.79375,10e-7 v -2.38125 z'} />
</g>
}
{/* <g id="Pump1" transform="translate(-0.79375,2.6066667e-4)">
<circle
style={stylePump1}
id="PumpCircle"
cx="51.59375"
cy="80.698891"
r="1.984375"
/>
<path
d="m 51.064583,81.492642 1e-6,-1.587499 1.322916,0.79375 v -10e-7 z"
id="PumpTriangle"
/>
<path
style={stylePump1}
d="m 48.948828,78.052995 v 5.292056 h 0.13086 l 5.158984,-2.6e-4 v -5.292057 z m 0.263672,0.263672 4.7625,-2.61e-4 v 4.762761 l -4.7625,2.6e-4 z"
id="PumpSquare"
/>
)}
{/*
<g id={'Pump1'} transform={'translate(-0.79375,2.6066667e-4)'}>
<circle style={stylePump1} id={'PumpCircle'} cx={'51.59375'} cy={'80.698891'} r={'1.984375'} />
<path id={'PumpTriangle'} d={'m 51.064583,81.492642 1e-6,-1.587499 1.322916,0.79375 v -10e-7 z'} />
<path id={'PumpSquare'} style={stylePump1} d={'m 48.948828,78.052995 v 5.292056 h 0.13086 l 5.158984,-2.6e-4 v -5.292057 z m 0.263672,0.263672 4.7625,-2.61e-4 v 4.762761 l -4.7625,2.6e-4 z'} />
</g>
<g id="Pump2" transform="translate(4.7625,-5.205e-4)">
<circle
style={stylePump2}
id="PumpCircle"
cx="51.59375"
cy="80.698891"
r="1.984375"
/>
<path
d="m 51.064583,81.492642 1e-6,-1.587499 1.322916,0.79375 v -10e-7 z"
id="PumpTriangle"
/>
<path
style={stylePump2}
d="m 48.948828,78.052995 v 5.292056 h 0.13086 l 5.158984,-2.6e-4 v -5.292057 z m 0.263672,0.263672 4.7625,-2.61e-4 v 4.762761 l -4.7625,2.6e-4 z"
id="PumpSquare"
/>
</g> */}
<g id={'Pump2'} transform={'translate(4.7625,-5.205e-4)'}>
<circle id={'PumpCircle'} style={stylePump2} cx={'51.59375'} cy={'80.698891'} r={'1.984375'} />
<path id={'PumpTriangle'} d={'m 51.064583,81.492642 1e-6,-1.587499 1.322916,0.79375 v -10e-7 z'} />
<path id={'PumpSquare'} style={stylePump2} d={'m 48.948828,78.052995 v 5.292056 h 0.13086 l 5.158984,-2.6e-4 v -5.292057 z m 0.263672,0.263672 4.7625,-2.61e-4 v 4.762761 l -4.7625,2.6e-4 z'} />
</g>
*/}
</svg>
);
}
)
})
export default RigMnemo

View File

@ -1,18 +1,20 @@
import {Switch} from 'antd'
import {useState} from 'react'
import { ErrorFetch } from '../../components/ErrorFetch'
import {UploadForm} from '../../components/UploadForm'
import { Switch } from 'antd'
import { useState } from 'react'
import { ErrorFetch } from '@components/ErrorFetch'
import { UploadForm } from '@components/UploadForm'
export const ImportOperations = ({idWell, onDone}) =>{
const errorTextStyle = { color: 'red', fontWeight: 'bold' }
const uploadFormStyle = { marginTop: '24px' }
export const ImportOperations = ({ idWell, onDone }) =>{
const [deleteBeforeImport, setDeleteBeforeImport] = useState(false)
const [errorText, setErrorText] = useState('')
const url = `/api/well/${idWell}/wellOperations/import`
const url = `/api/well/${idWell}/wellOperations/import${deleteBeforeImport ? '/1' : '/0'}`
const onUploadSuccess = () => {
setErrorText('')
if(onDone)
onDone()
onDone?.()
}
const onUploadError = (error) => {
@ -22,20 +24,18 @@ export const ImportOperations = ({idWell, onDone}) =>{
setErrorText(`Не удалось импортировать.`)
}
const getUrl = () => deleteBeforeImport
? url + '/1'
: url + '/0'
return <div>
<p>Загрузить файл excel с операциями на сервер</p>
<span>Очистить список операций перед импортом&nbsp;</span>
<Switch onChange={setDeleteBeforeImport} checked={deleteBeforeImport}/>
<UploadForm
url={getUrl()}
style={{marginTop:'24px'}}
onUploadSuccess={onUploadSuccess}
onUploadError={onUploadError}
return (
<div>
<p>Загрузить файл excel с операциями на сервер</p>
<span>Очистить список операций перед импортом&nbsp;</span>
<Switch onChange={setDeleteBeforeImport} checked={deleteBeforeImport}/>
<UploadForm
url={url}
style={uploadFormStyle}
onUploadSuccess={onUploadSuccess}
onUploadError={onUploadError}
/>
<span style={{color:'red', fontWeight:'bold'}}>{errorText}</span>
</div>
}
<span style={errorTextStyle}>{errorText}</span>
</div>
)
}

View File

@ -1,366 +0,0 @@
// import { useEffect, useRef, useState } from "react";
// import {
// Chart,
// TimeScale,
// LinearScale,
// Legend,
// LineController,
// PointElement,
// LineElement,
// } from "chart.js";
// import "chartjs-adapter-moment";
// import ChartDataLabels from "chartjs-plugin-datalabels";
// import zoomPlugin from "chartjs-plugin-zoom";
// import { invokeWebApiWrapperAsync } from "../../components/factory";
// import LoaderPortal from "../../components/LoaderPortal";
// import { WellOperationDtoPlanFactPredictBase } from '../../services/api'
// const defaultDataPlan = [
// {"depth": 0, "date": 0.00},
// {"depth": 0, "date": 0.00},
// {"depth": 50, "date": 0.08},
// {"depth": 50, "date": 0.10},
// {"depth": 50, "date": 0.15},
// {"depth": 50, "date": 0.17},
// {"depth": 50, "date": 0.19},
// {"depth": 50, "date": 0.23},
// {"depth": 50, "date": 0.25},
// {"depth": 50, "date": 0.38},
// {"depth": 50, "date": 0.40},
// {"depth": 50, "date": 0.44},
// {"depth": 50, "date": 0.52},
// {"depth": 50, "date": 0.94},
// {"depth": 50, "date": 1.06},
// {"depth": 50, "date": 1.08},
// {"depth": 50, "date": 1.13},
// {"depth": 50, "date": 1.19},
// {"depth": 1324, "date": 3.02},
// {"depth": 1324, "date": 3.10},
// {"depth": 1324, "date": 3.27},
// {"depth": 1324, "date": 3.35},
// {"depth": 1324, "date": 3.65},
// {"depth": 1324, "date": 3.73},
// {"depth": 1324, "date": 3.90},
// {"depth": 1324, "date": 3.98},
// {"depth": 1324, "date": 4.27},
// {"depth": 1324, "date": 4.31},
// {"depth": 1324, "date": 4.81},
// {"depth": 1324, "date": 4.90},
// {"depth": 1324, "date": 4.94},
// {"depth": 1324, "date": 5.10},
// {"depth": 1324, "date": 5.60},
// {"depth": 1324, "date": 6.10},
// {"depth": 1324, "date": 6.23},
// {"depth": 1324, "date": 6.40},
// {"depth": 1324, "date": 6.65},
// {"depth": 1324, "date": 6.69},
// {"depth": 1324, "date": 6.73},
// {"depth": 1324, "date": 6.81},
// {"depth": 1324, "date": 6.85},
// {"depth": 2600, "date": 8.85},
// {"depth": 2600, "date": 8.94},
// {"depth": 2600, "date": 9.15},
// {"depth": 2600, "date": 9.35},
// {"depth": 2600, "date": 9.52},
// {"depth": 2600, "date": 9.60},
// {"depth": 3283, "date": 11.85},
// {"depth": 3283, "date": 11.98},
// {"depth": 3283, "date": 12.35},
// {"depth": 3283, "date": 12.48},
// {"depth": 3283, "date": 12.60},
// {"depth": 3283, "date": 12.85},
// {"depth": 3283, "date": 13.10},
// {"depth": 3283, "date": 13.27},
// {"depth": 3283, "date": 13.35},
// {"depth": 3500, "date": 15.02},
// {"depth": 3500, "date": 15.15},
// {"depth": 3500, "date": 15.23},
// {"depth": 3500, "date": 15.31},
// {"depth": 3500, "date": 15.48},
// {"depth": 3500, "date": 15.56},
// {"depth": 3780, "date": 17.40},
// {"depth": 3780, "date": 17.52},
// {"depth": 3780, "date": 17.94},
// {"depth": 3780, "date": 18.44},
// {"depth": 3780, "date": 18.60},
// {"depth": 3780, "date": 18.69},
// {"depth": 4050, "date": 20.69},
// {"depth": 4050, "date": 20.81},
// {"depth": 4050, "date": 21.06},
// {"depth": 4050, "date": 21.23},
// {"depth": 4050, "date": 21.31},
// {"depth": 4050, "date": 23.31},
// {"depth": 4327, "date": 23.44},
// {"depth": 4327, "date": 23.60},
// {"depth": 4327, "date": 23.77},
// {"depth": 4327, "date": 23.90},
// {"depth": 4327, "date": 24.35},
// {"depth": 4327, "date": 24.48},
// {"depth": 4327, "date": 25.73},
// {"depth": 4327, "date": 25.85},
// {"depth": 4327, "date": 26.31},
// {"depth": 4327, "date": 26.48},
// {"depth": 4327, "date": 26.94},
// {"depth": 4327, "date": 27.06},
// {"depth": 4327, "date": 27.15},
// {"depth": 4327, "date": 27.48},
// {"depth": 4327, "date": 27.60},
// {"depth": 4327, "date": 27.67},
// {"depth": 4327, "date": 28.42},
// {"depth": 4327, "date": 28.63},
// {"depth": 4327, "date": 28.67},
// {"depth": 4327, "date": 28.88},
// {"depth": 4327, "date": 29.29},
// {"depth": 4327, "date": 29.29}
// ]
// const defaultDataFact = [
// { "depth": 0, "date": 0.00 },
// { "depth": 55, "date": 0.04 },
// { "depth": 55, "date": 0.08 },
// { "depth": 55, "date": 0.14 },
// { "depth": 55, "date": 0.26 },
// { "depth": 55, "date": 0.71 },
// { "depth": 55, "date": 0.81 },
// { "depth": 55, "date": 0.83 },
// { "depth": 264, "date": 1.06 },
// { "depth": 547, "date": 1.39 },
// { "depth": 547, "date": 1.43 },
// { "depth": 947, "date": 2.06 },
// { "depth": 1285, "date": 2.51 },
// { "depth": 1285, "date": 2.58 },
// { "depth": 1285, "date": 2.81 },
// { "depth": 1285, "date": 2.87 },
// { "depth": 1285, "date": 3.07 },
// { "depth": 1285, "date": 3.08 },
// { "depth": 1285, "date": 3.10 },
// { "depth": 1285, "date": 3.26 },
// { "depth": 1285, "date": 3.34 },
// { "depth": 1285, "date": 3.60 },
// { "depth": 1285, "date": 3.65 },
// { "depth": 1285, "date": 3.90 },
// { "depth": 1285, "date": 3.91 },
// { "depth": 1285, "date": 4.00 },
// { "depth": 1285, "date": 4.06 },
// { "depth": 1285, "date": 4.08 },
// { "depth": 1285, "date": 4.26 },
// { "depth": 1285, "date": 4.76 },
// { "depth": 1285, "date": 4.87 },
// { "depth": 1285, "date": 5.01 },
// { "depth": 1285, "date": 5.03 },
// { "depth": 1285, "date": 5.08 },
// { "depth": 1285, "date": 5.12 },
// { "depth": 1285, "date": 5.14 },
// { "depth": 1285, "date": 5.16 },
// { "depth": 1285, "date": 5.20 },
// { "depth": 1289, "date": 5.21 },
// { "depth": 1289, "date": 5.23 },
// { "depth": 1289, "date": 5.27 },
// { "depth": 1872, "date": 6.08 },
// { "depth": 2357, "date": 7.08 },
// { "depth": 2614, "date": 7.73 },
// { "depth": 2614, "date": 7.81 },
// { "depth": 2614, "date": 7.98 },
// { "depth": 2614, "date": 8.03 },
// { "depth": 2614, "date": 8.08 },
// { "depth": 2614, "date": 8.11 },
// { "depth": 2614, "date": 8.14 },
// { "depth": 2918, "date": 9.08 },
// {"depth": 3230, "date": 9.12},
// ]
// const defaultDataForecast = [
// {"depth": 3230, "date": 9.12},
// {"depth": 3283, "date": 9.12},
// {"depth": 3283, "date": 9.21},
// {"depth": 3283, "date": 9.58},
// {"depth": 3283, "date": 9.71},
// {"depth": 3283, "date": 9.83},
// {"depth": 3283, "date": 10.08},
// {"depth": 3283, "date": 10.33},
// {"depth": 3283, "date": 10.50},
// {"depth": 3283, "date": 10.58},
// {"depth": 3500, "date": 12.25},
// {"depth": 3500, "date": 12.38},
// {"depth": 3500, "date": 12.46},
// {"depth": 3500, "date": 12.33},
// {"depth": 3500, "date": 12.71},
// {"depth": 3500, "date": 12.79},
// {"depth": 3780, "date": 14.63},
// {"depth": 3780, "date": 14.75},
// {"depth": 3780, "date": 15.17},
// {"depth": 3780, "date": 15.67},
// {"depth": 3780, "date": 15.83},
// {"depth": 3780, "date": 15.92},
// {"depth": 4050, "date": 17.92},
// {"depth": 4050, "date": 18.04},
// {"depth": 4050, "date": 18.29},
// {"depth": 4050, "date": 18.46},
// {"depth": 4050, "date": 18.54},
// {"depth": 4050, "date": 20.54},
// {"depth": 4327, "date": 20.67},
// {"depth": 4327, "date": 20.83},
// {"depth": 4327, "date": 21.00},
// {"depth": 4327, "date": 21.13},
// {"depth": 4327, "date": 21.58},
// {"depth": 4327, "date": 21.71},
// {"depth": 4327, "date": 22.96},
// {"depth": 4327, "date": 23.08},
// {"depth": 4327, "date": 23.54},
// {"depth": 4327, "date": 23.71},
// {"depth": 4327, "date": 24.17},
// {"depth": 4327, "date": 24.29},
// {"depth": 4327, "date": 24.61},
// {"depth": 4327, "date": 24.71},
// {"depth": 4327, "date": 24.83},
// {"depth": 4327, "date": 24.90},
// {"depth": 4327, "date": 25.65},
// {"depth": 4327, "date": 25.86},
// {"depth": 4327, "date": 25.90},
// {"depth": 4327, "date": 26.11},
// {"depth": 4327, "date": 26.52},
// {"depth": 4327, "date": 26.52}
// ]
// Chart.register(
// TimeScale,
// LinearScale,
// LineController,
// LineElement,
// PointElement,
// Legend,
// ChartDataLabels,
// zoomPlugin
// );
// const defaultOptions = {
// responsive: true,
// aspectRatio: 2.35,
// interaction: {
// intersect: false,
// mode: "index",
// },
// scales: {
// x: {
// display: true,
// title: {
// display: true,
// },
// type: "linear", // был 'time' + его конфиг ниже
// grid: {
// drawTicks: true,
// },
// ticks: {
// //count:24,
// stepSize: 3,
// major: { enabled: true },
// z: 1,
// display: true,
// textStrokeColor: "#fff",
// textStrokeWidth: 2,
// color: "#000",
// },
// },
// y: {
// type: "linear",
// position: "top",
// reverse: true,
// display: true,
// title: {
// display: true,
// text: "Value",
// },
// },
// },
// parsing: {
// xAxisKey: "date",
// yAxisKey: "depth",
// },
// elements: {
// point: {
// radius: 1.7,
// },
// },
// plugins: {
// legend: {
// display: true,
// },
// datalabels: {
// display: false,
// },
// tooltip: {
// enabled: true,
// callbacks: {
// label(tooltipItem) {
// return tooltipItem.yLabel;
// },
// },
// },
// },
// };
// const makeDataset = (data, label, color, width = 1.5, dash) => ({
// label: label,
// data: data,
// backgroundColor: color,
// borderColor: color,
// borderWidth: width,
// borderDash: dash,
// });
// export const TvdOldVersion = ({ idWell }) => {
// const chartRef = useRef(null);
// const [chart, setChart] = useState();
// const [data, setData] = useState({ datasets: [] });
// const [showLoader, setShowLoader] = useState(false);
// useEffect(
// () =>
// invokeWebApiWrapperAsync(
// async () => {
// const dataPlan = [];
// const dataFact = [];
// const dataPredict = [];
// const data = {
// datasets: [
// makeDataset(dataPlan, "План", "#C004", 4),
// makeDataset(dataFact, "Факт", "#0A0"),
// makeDataset(dataPredict, "Прогноз", "purple", 1, [7, 3]),
// ],
// };
// setData(data);
// },
// setShowLoader,
// `Не удалось загрузить данные TVD по скважине ${idWell}`
// ),
// [idWell]
// );
// useEffect(() => {
// if (chartRef.current && !chart) {
// let thisOptions = {};
// Object.assign(thisOptions, defaultOptions);
// let newChart = new Chart(chartRef.current, {
// type: "line",
// plugins: [ChartDataLabels],
// options: thisOptions,
// data: data,
// });
// setChart(newChart);
// return () => chart?.destroy();
// } else {
// chart.data = data;
// chart.update();
// }
// }, [chart, data]);
// return (
// <LoaderPortal show={showLoader}>
// <canvas ref={chartRef} />
// </LoaderPortal>
// );
// };

View File

@ -1,27 +1,22 @@
import { useState, useEffect } from 'react'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { Table, makeColumn, makeColumnsPlanFact } from '../../components/Table'
import { OperationStatService } from '../../services/api'
import { calcDuration } from '../../utils/datetime'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table'
import { OperationStatService } from '@api'
import { calcDuration } from '@utils/datetime'
const makeNumberRender = value => {
const v = +value
if(Number.isFinite(v))
return v.toFixed(2)
return '-'
}
const numericRender = makeNumericRender(2)
const columns = [
makeColumn('Тип секции', 'sectionType'),
makeColumnsPlanFact('Глубина, м' ,'wellDepth', { render: makeNumberRender }),
makeColumnsPlanFact('Часы' ,'duration', { render: makeNumberRender }),
makeColumnsPlanFact('МСП, м/ч' ,'rop', { render: makeNumberRender }),
makeColumnsPlanFact('Рейсовая скорость, м/ч','routeSpeed', { render: makeNumberRender }),
makeColumnsPlanFact('Подъем КНБК, м/ч' ,'bhaUpSpeed', { render: makeNumberRender }),
makeColumnsPlanFact('Спуск КНБК, м/ч' ,'bhaDownSpeed', { render: makeNumberRender }),
makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: makeNumberRender }),
makeColumnsPlanFact('Глубина, м' ,'wellDepth', { render: numericRender }),
makeColumnsPlanFact('Часы' ,'duration', { render: numericRender }),
makeColumnsPlanFact('МСП, м/ч' ,'rop', { render: numericRender }),
makeColumnsPlanFact('Рейсовая скорость, м/ч','routeSpeed', { render: numericRender }),
makeColumnsPlanFact('Подъем КНБК, м/ч' ,'bhaUpSpeed', { render: numericRender }),
makeColumnsPlanFact('Спуск КНБК, м/ч' ,'bhaDownSpeed', { render: numericRender }),
makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: numericRender }),
]
export const WellSectionsStat = ({idWell}) => {
@ -33,27 +28,26 @@ export const WellSectionsStat = ({idWell}) => {
const sectionsResponse = await OperationStatService.getStatWell(idWell)
if(sectionsResponse?.sections){
const sections = sectionsResponse.sections
.map(s => ({
key: s.id,
sectionType: s.caption,
wellDepthPlan: s.plan?.wellDepthEnd,
durationPlan: calcDuration(s.plan?.start, s.plan?.end),
ropPlan: s.plan?.rop,
routeSpeedPlan: s.plan?.routeSpeed,
bhaUpSpeedPlan: s.plan?.bhaUpSpeed,
bhaDownSpeedPlan: s.plan?.bhaDownSpeed,
casingDownSpeedPlan: s.plan?.casingDownSpeed,
const sections = sectionsResponse.sections.map(s => ({
key: s.id,
sectionType: s.caption,
wellDepthPlan: s.plan?.wellDepthEnd,
durationPlan: calcDuration(s.plan?.start, s.plan?.end),
ropPlan: s.plan?.rop,
routeSpeedPlan: s.plan?.routeSpeed,
bhaUpSpeedPlan: s.plan?.bhaUpSpeed,
bhaDownSpeedPlan: s.plan?.bhaDownSpeed,
casingDownSpeedPlan: s.plan?.casingDownSpeed,
wellDepthFact: s.fact?.wellDepthEnd,
durationFact: calcDuration(s.fact?.start, s.fact?.end),
ropFact: s.fact?.rop,
routeSpeedFact: s.fact?.routeSpeed,
bhaUpSpeedFact: s.fact?.bhaUpSpeed,
bhaDownSpeedFact: s.fact?.bhaDownSpeed,
casingDownSpeedFact: s.fact?.casingDownSpeed,
}))
.sort((a,b) => a.wellDepthPlan - b.wellDepthPlan)
wellDepthFact: s.fact?.wellDepthEnd,
durationFact: calcDuration(s.fact?.start, s.fact?.end),
ropFact: s.fact?.rop,
routeSpeedFact: s.fact?.routeSpeed,
bhaUpSpeedFact: s.fact?.bhaUpSpeed,
bhaDownSpeedFact: s.fact?.bhaDownSpeed,
casingDownSpeedFact: s.fact?.casingDownSpeed,
}))
sections.sort((a,b) => a.wellDepthPlan - b.wellDepthPlan)
setSections(sections)
}
},

View File

@ -3,46 +3,43 @@ import { Rule } from 'rc-field-form/lib/interface'
const _min1: Rule = {
min: 1,
max: 255,
message: 'Допустимая длина 1-255 символов'
message: 'Допустимая длина 1-255 символов',
}
export const min1: Rule[] = [_min1]
export const createLoginRules: Rule[] = [_min1, {
pattern: /^[A-Za-zА-Яа-я\s][А-Яа-я\w.-\s]+$/,
message: 'Логин должен начинаться с русской или латинской буквы, содержать только (А-яA-z0-9_-.)!'
}, {
pattern: /^[^\s]+$/,
message: 'Логин не должен содержать пробельных символов',
pattern: /^\s*[A-Za-zА-Яа-я][А-Яа-я\w.-]+\s*$/,
message: 'Логин должен начинаться с русской или латинской буквы и содержать только символы (А-яA-z0-9_-.)!'
}]
export const loginRules: Rule[] = [...createLoginRules, {
required: true,
message: 'Пожалуйста, введите имя пользователя'
message: 'Пожалуйста, введите логин',
}]
export const nameRules: Rule[] = [_min1, {
pattern: /^[A-zА-я][A-zА-я-]*$/,
message: 'Допустимые символы: А-яA-z-'
message: 'Допустимые символы: А-яA-z-',
}]
export const phoneRules: Rule[] = [{
pattern: /^(?:\+7|8)\s?(?:\(\d{3}\)|\d{3})\s?\d{3}-?\d{2}-?\d{2}$/,
message: 'Номер телефона должен иметь вид: +7 (xxx) xxx-xx-xx'
message: 'Номер телефона должен иметь вид: +7 (xxx) xxx-xx-xx',
}]
export const emailRules: Rule[] = [{
type: 'email',
message: 'E-mail должен иметь вид: "user@site.domain"'
message: 'E-mail должен иметь вид: "user@site.domain"',
}]
export const passwordRules: Rule[] = [{
required: true,
message: 'Пожалуйста, введите пароль!'
message: 'Пожалуйста, введите пароль!',
}]
export const createPasswordRules: Rule[] = [{
min: 8,
max: 255,
message: 'Допустимая длина пароля 8-255'
message: 'Допустимая длина пароля 8-255',
}]