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

* Установлены 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 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 { RangePicker } = DatePicker
const lines = [{ labelAccessorName: "processName", pieceAccessorName: "duration" }] const lines = [{ labelAccessorName: 'processName', pieceAccessorName: 'duration' }]
export function AnalysisOperationTime() { export const AnalysisOperationTime = () => {
let { id } = useParams() const { id } = useParams()
const [operationTimeData, setOperationTimeData] = useState([]) const [operationTimeData, setOperationTimeData] = useState([])
const [loader, setLoader] = useState(false) const [loader, setLoader] = useState(false)
const [range, setRange] = useState([moment().subtract(1,'days'), moment()]) const [range, setRange] = useState([moment().subtract(1,'days'), moment()])
const onChangeRange = (range) => { useEffect(() => invokeWebApiWrapperAsync(
setRange(range) async () => {
} const begin = range?.length > 1 ? range[0].toISOString() : null
const end = range?.length > 1 ? range[1].toISOString() : null
const handleReceiveOperationTimeData = (data) => { const summary = await TelemetryAnalyticsService.getOperationsSummary(id, begin, end)
setOperationTimeData(data) setOperationTimeData(summary)
} },
setLoader,
useEffect(() => { `Не удалось получить данные для Анализа Операция-Время по скважине '${id}' за период с ${begin} по ${end}`
setLoader(true) ), [id, range])
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])
return ( return (
<LoaderPortal show={loader}> <LoaderPortal show={loader}>
<RangePicker <RangePicker showTime onChange={(range) => setRange(range)} />
showTime <ChartOperationTime data={operationTimeData} lines={lines} />
onChange={onChangeRange}
/>
<ChartOperationTime
data={operationTimeData}
lines={lines}
/>
</LoaderPortal> </LoaderPortal>
) )
} }
export default AnalysisOperationTime

View File

@ -2,13 +2,13 @@ import { memo, useState } from 'react'
import { useForm } from 'antd/lib/form/Form' import { useForm } from 'antd/lib/form/Form'
import { Form, Input, Modal, FormProps } from 'antd' import { Form, Input, Modal, FormProps } from 'antd'
import { AuthService, UserDto } from '../services/api' import { AuthService, UserDto } from '@api'
import { passwordRules, createPasswordRules } from '../utils/validationRules' import { getUserId, getUserLogin } from '@utils/storage'
import { passwordRules, createPasswordRules } from '@utils/validationRules'
import LoaderPortal from './LoaderPortal' import LoaderPortal from './LoaderPortal'
import { invokeWebApiWrapperAsync } from './factory' import { invokeWebApiWrapperAsync } from './factory'
import { UserView } from './views' import { UserView } from './views'
import { getUserId, getUserLogin } from '../utils/storage'
const formLayout: FormProps = { labelCol: { span: 11 }, wrapperCol: { span: 16 } } 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 {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) Number.isInteger(format) && Number.isFinite(value)
? (+value).toFixed(format) ? (+value).toFixed(format)
: (+value).toPrecision(4) : (+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 [val, setVal] = useState('---')
const [arrowState, setArrowState] = useState({ const [arrowState, setArrowState] = useState({
preVal: NaN, preVal: NaN,
preTimestamp: Date.now(), preTimestamp: Date.now(),
direction: 0, direction: 0,
}) })
useEffect(()=>{ useEffect(() => {
if(value === undefined || value === null || value === '-' || value === '--'){ setVal((preVal) => {
setVal('---') if ((value ?? '-') === '-' || value === '--') return '---'
return if (enumeration?.[value]) return enumeration[value]
}
if(enumeration && enumeration[value]){ if (Number.isFinite(+value)) {
setVal(enumeration[value]) if (isArrowVisible && (arrowState.preTimestamp + 1000 < Date.now())) {
return let direction = 0
} if (value > arrowState.preVal)
direction = 1
if (value < arrowState.preVal)
direction = -1
if(Number.isFinite(+value)){ setArrowState({
if ((isArrowVisible) && (arrowState.preTimestamp + 1000 < Date.now())) preVal: value,
{ preTimestamp: Date.now(),
let direction = 0 direction: direction,
if (value > arrowState.preVal) })
direction = 1 }
if (value < arrowState.preVal)
direction = -1
setArrowState({ return formatNumber(value, format)
preVal: value,
preTimestamp: Date.now(),
direction: direction,
})
} }
setVal(formatNumber(value, format)) if (value.length > 4) {
return const valueDate = moment(value)
} if (valueDate.isValid())
return valueDate.format(format)
if(value.length > 4){
let valueDate = moment(value)
if(valueDate.isValid()){
setVal(valueDate.format(format))
return
} }
}
setVal(value) return value
})
},[value, isArrowVisible, arrowState, format, enumeration]) },[value, isArrowVisible, arrowState, format, enumeration])
let arrow = null let arrow = null
if(isArrowVisible) if(isArrowVisible)
switch (arrowState.direction){ switch (arrowState.direction){
case 0: case 0:
arrow = <CaretRightOutlined style={{color:"#0008"}}/> arrow = <CaretRightOutlined style={iconStyle}/>
break break
case 1: case 1:
arrow = <CaretUpOutlined style={{color:"#0008"}}/> arrow = <CaretUpOutlined style={iconStyle}/>
break break
case -1: case -1:
arrow = <CaretDownOutlined style={{color:"#0008"}}/> arrow = <CaretDownOutlined style={iconStyle}/>
break break
default: default:
break 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)=>{ export const Display = memo(({ className, label, ...other })=> (
const {label} = props <div className={className}>
<div className={'display_label'}>{label}</div>
return <div className={props.className}> <div style={displayValueStyle}>
<div className='display_label'>{label}</div> <ValueDisplay {...other}/>
<div style={{display:"flex", flexGrow:1}}>
<ValueDisplay {...props}/>
</div>
</div> </div>
</div>
} ))

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
import { memo, ReactNode } from 'react' import { memo, ReactNode } from 'react'
import { Layout, LayoutProps } from 'antd' import { Layout, LayoutProps } from 'antd'
import PageHeader from '../PageHeader' import PageHeader from '@components/PageHeader'
import WellTreeSelector from '../WellTreeSelector' import WellTreeSelector from '@components/WellTreeSelector'
export type LayoutPortalProps = LayoutProps & { export type LayoutPortalProps = LayoutProps & {
title?: ReactNode 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' import Loader from './Loader'
type LoaderPortalProps = { type LoaderPortalProps = HTMLAttributes<HTMLDivElement> & {
show?: boolean, show?: boolean,
fade?: 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-container'} {...other}>
<div className={'loader-content'}>{children}</div> <div className={'loader-content'}>{children}</div>
{show && fade && <div className={'loader-fade'}/>} {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 export const defaultPeriod = 600
@ -13,19 +14,18 @@ const timePeriodCollection = [
{ value: 86400, label: '24 часа' } { value: 86400, label: '24 часа' }
] ]
type PeriodPickerProps = { export type PeriodPickerProps<VT = number> = SelectProps<VT> & {
defaultValue?: number defaultValue?: VT
onChange?: (value: number) => void onChange?: (value: VT) => void
[other: string]: any
} }
export const PeriodPicker = ({ defaultValue = defaultPeriod, onChange, ...other }: PeriodPickerProps) => ( export const PeriodPicker = memo<PeriodPickerProps>(({ defaultValue = defaultPeriod, onChange, ...other }) => (
<Select <Select
{...other} {...other}
defaultValue={defaultValue} defaultValue={defaultValue}
onChange={(value) => onChange?.(Number(value))} onChange={(value) => onChange?.(Number(value))}
options={timePeriodCollection} options={timePeriodCollection}
/> />
) ))
export default PeriodPicker export default PeriodPicker

View File

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

View File

@ -1,15 +1,17 @@
import { Form, Table, Button, Popconfirm } from 'antd' import { Form, Table, Button, Popconfirm } from 'antd'
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons' import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { EditableCell } from './EditableCell' import { EditableCell } from './EditableCell'
import { invokeWebApiWrapperAsync } from '../factory'
const newRowKeyValue = 'newRow' const newRowKeyValue = 'newRow'
const actions = [ const actions = [
[['insert'], (data) => [data]], [['insert'], (data) => [data]],
[['insertRange'], (data) => [[data].flat(1)]], [['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]], [['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 { 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 { OptionsType } from 'rc-select/lib/interface'
import { Rule } from 'rc-field-form/lib/interface'
import { tryAddKeys } from './EditableTable' import { tryAddKeys } from './EditableTable'
import { makeNumericSorter, makeStringSorter } from './sorters' 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 { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters'
export { EditableTable, makeActionHandler } from './EditableTable' export { EditableTable, makeActionHandler } from './EditableTable'
export { DatePickerWrapper } from './DatePickerWrapper' export { DatePickerWrapper } from './DatePickerWrapper'
export { SelectFromDictionary } from './SelectFromDictionary'
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/ export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
@ -17,7 +18,7 @@ export const defaultPagination = {
showSizeChanger: true, showSizeChanger: true,
} }
export const makeNumericRender = (fixed?: number) => (value: any, row: object): ReactNode => { export const makeNumericRender = (fixed?: number) => (value: any, _: object): ReactNode => {
let val = '-' let val = '-'
if ((value ?? null) !== null && Number.isFinite(+value)) { if ((value ?? null) !== null && Number.isFinite(+value)) {
val = (fixed ?? null) !== null 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<{ const makeTagInput = <T extends Record<string, any>>(value_key: string, label_key: string) => memo<{
options: T[], options: T[],
value?: T[], value?: T[],
onChange?: (values: T[]) => void onChange?: (values: T[]) => void,
}>(({ options, value, onChange }) => { other?: SelectProps<SelectValue>,
}>(({ options, value, onChange, other }) => {
const [selectOptions, setSelectOptions] = useState<OptionsType>([]) const [selectOptions, setSelectOptions] = useState<OptionsType>([])
const [selectedValue, setSelectedValue] = useState<SelectValue>([]) const [selectedValue, setSelectedValue] = useState<SelectValue>([])
@ -259,6 +261,7 @@ const makeTagInput = <T extends Record<string, any>>(value_key: string, label_ke
return ( return (
<Select <Select
{...other}
mode={'tags'} mode={'tags'}
options={selectOptions} options={selectOptions}
value={selectedValue} value={selectedValue}
@ -273,14 +276,15 @@ export const makeTagColumn = <T extends Record<string, any>>(
options: T[], options: T[],
value_key: string, value_key: string,
label_key: string, label_key: string,
other?: columnPropsOther other?: columnPropsOther,
tagOther?: SelectProps<SelectValue>
) => { ) => {
const InputComponent = makeTagInput<T>(value_key, label_key) const InputComponent = makeTagInput<T>(value_key, label_key)
return makeColumn(title, dataIndex, { return makeColumn(title, dataIndex, {
...other, ...other,
render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm) ?? elm[label_key]}</Tag>) ?? '-', 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)) current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
}) })
interface TableContainer { export type TableContainer = TableProps<any> & {
dataSource: any[] dataSource: any[]
children?: any children?: ReactNode
} }
export const Table = ({dataSource, children, ...other}: TableContainer) => ( export const Table = ({dataSource, children, ...other}: TableContainer) => (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,17 @@
import { LoadingOutlined } from '@ant-design/icons' 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 }}> <Flex style={{ justifyContent: 'center', alignItems: 'center', minHeight: '400px', ...style }}>
<LoadingOutlined /> <LoadingOutlined />
<div style={{ marginLeft: '10px' }}>Страница загружается, пожалуйста, подождите...</div> <div style={{ marginLeft: '10px' }}>Страница загружается, пожалуйста, подождите...</div>
</Flex> </Flex>
) ))
export default SuspenseFallback

View File

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

View File

@ -1,64 +1,62 @@
import { memo } from 'react'
const styleBase = { const styleBase = {
stroke: "none", stroke: 'none',
strokeLinecap: "butt", strokeLinecap: 'butt',
strokeLinejoin: "miter", strokeLinejoin: 'miter',
} }
const styleCasing = { const styleCasing = {
...styleBase, ...styleBase,
fill: "#808080", fill: '#808080',
}; };
const styleGround = { const styleGround = {
...styleBase, ...styleBase,
fill: "#deaa87", fill: '#deaa87',
}; };
const styleTower = { const styleTower = {
...styleBase, ...styleBase,
fill: "#999999", fill: '#999999',
}; };
const styleWell = { const styleWell = {
...styleBase, ...styleBase,
fill: "#37abc8", fill: '#37abc8',
}; };
const styleBitDrive = { const styleBitDrive = {
...styleBase, ...styleBase,
fill: "#ff8080", fill: '#ff8080',
}; };
const styleBitTool = { const styleBitTool = {
...styleBase, ...styleBase,
fill: "#f9f9f9", fill: '#f9f9f9',
}; };
const styleBlock = { const styleBlock = {
...styleBase, ...styleBase,
fill: "#ffdd55", fill: '#ffdd55',
}; };
// const stylePump1 = { // const stylePump1 = {
// ...styleBase, // ...styleBase,
// fill: "#5fd35f", // fill: '#5fd35f',
// }; // };
// const stylePump2 = { // const stylePump2 = {
// ...styleBase, // ...styleBase,
// fill: "#b3b3b3", // fill: '#b3b3b3',
// }; // };
export default function RigMnemo({ export const RigMnemo = memo(({ blockPosition, bitPosition, wellDepth }) => {
blockPosition,
bitPosition,
wellDepth
}) {
let blockPositionY = 0 let blockPositionY = 0
let bitPositionY = 0 let bitPositionY = 0
const isBlockVisible = blockPosition != null && Number.isFinite(blockPosition) const isBlockVisible = Number.isFinite(blockPosition)
const isBitVisible = wellDepth && bitPosition != null && Number.isFinite(bitPosition) && Number.isFinite(wellDepth) const isBitVisible = Number.isFinite(bitPosition) && Number.isFinite(wellDepth)
if (isBlockVisible) if (isBlockVisible)
blockPositionY = -35 + (50 * (30 - +blockPosition) / 30) blockPositionY = -35 + (50 * (30 - +blockPosition) / 30)
@ -76,133 +74,91 @@ export default function RigMnemo({
bitPositionY = -59 + 59 * bitPositionLocal / wellDepthLocal bitPositionY = -59 + 59 * bitPositionLocal / wellDepthLocal
if( wellDepthLocal > 20 && (wellDepthLocal - bitPositionLocal) > 2 && bitPositionProc > 0.96) if( wellDepthLocal > 20 && (wellDepthLocal - bitPositionLocal) > 2 && bitPositionProc > 0.96)
bitPositionY = -2 bitPositionY = -2
} }
const blockTranslate = `translate(-75, ${blockPositionY})` const blockTranslate = `translate(-75, ${blockPositionY})`
const bitTranslate = `translate(0, ${bitPositionY})` const bitTranslate = `translate(0, ${bitPositionY})`
return ( return (
<svg <svg width={'170'} height={'540'} viewBox={'20 0 40 145'} version={'1.1'}>
width="170" <g id={'Casing'} style={styleCasing}>
height="540" <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'} />
viewBox="20 0 40 145" <path d={'M 31.75,97.895832 V 67.468749 h 9.260416 v 30.427083 z'} />
version="1.1" <path d={'m 30.95625,83.34375 0,1.058333 h 10.847917 l -10e-7,-1.058333 z'} />
id="svg8" <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'} />
<g id="Casing" style={styleCasing}> <path d={'m 30.95625,71.966666 v 1.058333 h 10.847918 v -1.058333 z'} />
<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>
<g id="Ground"> <g id={'Ground'}>
<path <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'} />
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> </g>
<g id="Tower" style={styleTower}> <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 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 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 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 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.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 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.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 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 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 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 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 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 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.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 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 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 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 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 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 " /> <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"> <g id={'g985'}>
<path d="m 42.333333,48.947917 -11.90625,-10e-7" /> <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 " /> <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>
<g> <g>
<path d="M 41.539583,42.333333 H 31.220833" /> <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 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> </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 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 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 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 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 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 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>
<g id="Well" style={styleWell}> <g id={'Well'} style={styleWell}>
<path d="M 33.072916,97.895832 H 39.6875 V 142.875 h -6.614584 z" /> <path d={'M 33.072916,97.895832 H 39.6875 V 142.875 h -6.614584 z'} />
</g> </g>
{isBitVisible && {isBitVisible && (
<g id="Bit" <g id={'Bit'} transform={bitTranslate}>
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 <path style={styleBitTool} d={'m 34.395833,138.90625 v -3.96875 h 3.96875 v 3.96875 z'} />
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> </g>
} )}
{isBlockVisible && {isBlockVisible && (
<g id="Block" <g id={'Block'} transform={blockTranslate} style={styleBlock}>
transform={blockTranslate} <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'} />
style={styleBlock} <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'} />
<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>
} )}
{/* <g id="Pump1" transform="translate(-0.79375,2.6066667e-4)"> {/*
<circle <g id={'Pump1'} transform={'translate(-0.79375,2.6066667e-4)'}>
style={stylePump1} <circle style={stylePump1} id={'PumpCircle'} cx={'51.59375'} cy={'80.698891'} r={'1.984375'} />
id="PumpCircle" <path id={'PumpTriangle'} d={'m 51.064583,81.492642 1e-6,-1.587499 1.322916,0.79375 v -10e-7 z'} />
cx="51.59375" <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'} />
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> </g>
<g id="Pump2" transform="translate(4.7625,-5.205e-4)"> <g id={'Pump2'} transform={'translate(4.7625,-5.205e-4)'}>
<circle <circle id={'PumpCircle'} style={stylePump2} cx={'51.59375'} cy={'80.698891'} r={'1.984375'} />
style={stylePump2} <path id={'PumpTriangle'} d={'m 51.064583,81.492642 1e-6,-1.587499 1.322916,0.79375 v -10e-7 z'} />
id="PumpCircle" <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'} />
cx="51.59375" </g>
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> */}
</svg> </svg>
); )
} })
export default RigMnemo

View File

@ -1,18 +1,20 @@
import {Switch} from 'antd' import { Switch } from 'antd'
import {useState} from 'react' import { useState } from 'react'
import { ErrorFetch } from '../../components/ErrorFetch' import { ErrorFetch } from '@components/ErrorFetch'
import {UploadForm} from '../../components/UploadForm' 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 [deleteBeforeImport, setDeleteBeforeImport] = useState(false)
const [errorText, setErrorText] = useState('') const [errorText, setErrorText] = useState('')
const url = `/api/well/${idWell}/wellOperations/import` const url = `/api/well/${idWell}/wellOperations/import${deleteBeforeImport ? '/1' : '/0'}`
const onUploadSuccess = () => { const onUploadSuccess = () => {
setErrorText('') setErrorText('')
if(onDone) onDone?.()
onDone()
} }
const onUploadError = (error) => { const onUploadError = (error) => {
@ -22,20 +24,18 @@ export const ImportOperations = ({idWell, onDone}) =>{
setErrorText(`Не удалось импортировать.`) setErrorText(`Не удалось импортировать.`)
} }
const getUrl = () => deleteBeforeImport return (
? url + '/1' <div>
: url + '/0' <p>Загрузить файл excel с операциями на сервер</p>
<span>Очистить список операций перед импортом&nbsp;</span>
return <div> <Switch onChange={setDeleteBeforeImport} checked={deleteBeforeImport}/>
<p>Загрузить файл excel с операциями на сервер</p> <UploadForm
<span>Очистить список операций перед импортом&nbsp;</span> url={url}
<Switch onChange={setDeleteBeforeImport} checked={deleteBeforeImport}/> style={uploadFormStyle}
<UploadForm onUploadSuccess={onUploadSuccess}
url={getUrl()} onUploadError={onUploadError}
style={{marginTop:'24px'}}
onUploadSuccess={onUploadSuccess}
onUploadError={onUploadError}
/> />
<span style={{color:'red', fontWeight:'bold'}}>{errorText}</span> <span style={errorTextStyle}>{errorText}</span>
</div> </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 { useState, useEffect } from 'react'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { Table, makeColumn, makeColumnsPlanFact } from '../../components/Table' import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table'
import { OperationStatService } from '../../services/api' import { OperationStatService } from '@api'
import { calcDuration } from '../../utils/datetime' import { calcDuration } from '@utils/datetime'
const makeNumberRender = value => { const numericRender = makeNumericRender(2)
const v = +value
if(Number.isFinite(v))
return v.toFixed(2)
return '-'
}
const columns = [ const columns = [
makeColumn('Тип секции', 'sectionType'), makeColumn('Тип секции', 'sectionType'),
makeColumnsPlanFact('Глубина, м' ,'wellDepth', { render: makeNumberRender }), makeColumnsPlanFact('Глубина, м' ,'wellDepth', { render: numericRender }),
makeColumnsPlanFact('Часы' ,'duration', { render: makeNumberRender }), makeColumnsPlanFact('Часы' ,'duration', { render: numericRender }),
makeColumnsPlanFact('МСП, м/ч' ,'rop', { render: makeNumberRender }), makeColumnsPlanFact('МСП, м/ч' ,'rop', { render: numericRender }),
makeColumnsPlanFact('Рейсовая скорость, м/ч','routeSpeed', { render: makeNumberRender }), makeColumnsPlanFact('Рейсовая скорость, м/ч','routeSpeed', { render: numericRender }),
makeColumnsPlanFact('Подъем КНБК, м/ч' ,'bhaUpSpeed', { render: makeNumberRender }), makeColumnsPlanFact('Подъем КНБК, м/ч' ,'bhaUpSpeed', { render: numericRender }),
makeColumnsPlanFact('Спуск КНБК, м/ч' ,'bhaDownSpeed', { render: makeNumberRender }), makeColumnsPlanFact('Спуск КНБК, м/ч' ,'bhaDownSpeed', { render: numericRender }),
makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: makeNumberRender }), makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: numericRender }),
] ]
export const WellSectionsStat = ({idWell}) => { export const WellSectionsStat = ({idWell}) => {
@ -33,27 +28,26 @@ export const WellSectionsStat = ({idWell}) => {
const sectionsResponse = await OperationStatService.getStatWell(idWell) const sectionsResponse = await OperationStatService.getStatWell(idWell)
if(sectionsResponse?.sections){ if(sectionsResponse?.sections){
const sections = sectionsResponse.sections const sections = sectionsResponse.sections.map(s => ({
.map(s => ({ key: s.id,
key: s.id, sectionType: s.caption,
sectionType: s.caption, wellDepthPlan: s.plan?.wellDepthEnd,
wellDepthPlan: s.plan?.wellDepthEnd, durationPlan: calcDuration(s.plan?.start, s.plan?.end),
durationPlan: calcDuration(s.plan?.start, s.plan?.end), ropPlan: s.plan?.rop,
ropPlan: s.plan?.rop, routeSpeedPlan: s.plan?.routeSpeed,
routeSpeedPlan: s.plan?.routeSpeed, bhaUpSpeedPlan: s.plan?.bhaUpSpeed,
bhaUpSpeedPlan: s.plan?.bhaUpSpeed, bhaDownSpeedPlan: s.plan?.bhaDownSpeed,
bhaDownSpeedPlan: s.plan?.bhaDownSpeed, casingDownSpeedPlan: s.plan?.casingDownSpeed,
casingDownSpeedPlan: s.plan?.casingDownSpeed,
wellDepthFact: s.fact?.wellDepthEnd, wellDepthFact: s.fact?.wellDepthEnd,
durationFact: calcDuration(s.fact?.start, s.fact?.end), durationFact: calcDuration(s.fact?.start, s.fact?.end),
ropFact: s.fact?.rop, ropFact: s.fact?.rop,
routeSpeedFact: s.fact?.routeSpeed, routeSpeedFact: s.fact?.routeSpeed,
bhaUpSpeedFact: s.fact?.bhaUpSpeed, bhaUpSpeedFact: s.fact?.bhaUpSpeed,
bhaDownSpeedFact: s.fact?.bhaDownSpeed, bhaDownSpeedFact: s.fact?.bhaDownSpeed,
casingDownSpeedFact: s.fact?.casingDownSpeed, casingDownSpeedFact: s.fact?.casingDownSpeed,
})) }))
.sort((a,b) => a.wellDepthPlan - b.wellDepthPlan) sections.sort((a,b) => a.wellDepthPlan - b.wellDepthPlan)
setSections(sections) setSections(sections)
} }
}, },

View File

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