forked from ddrilling/asb_cloud_front
Merge branch 'dev' into feature/CF2-72-AdminPanel
This commit is contained in:
commit
8552a40506
@ -1,6 +1,6 @@
|
||||
import { DatePicker } from 'antd'
|
||||
import moment from 'moment'
|
||||
import { formatDate } from './index'
|
||||
import { defaultFormat } from '../../utils'
|
||||
|
||||
export type DatePickerWrapperProps = {
|
||||
value: moment.Moment,
|
||||
@ -13,7 +13,7 @@ export const DatePickerWrapper: React.FC<DatePickerWrapperProps> = ({value, onCh
|
||||
allowClear={false}
|
||||
defaultValue={moment()}
|
||||
value={moment.utc(value).local()}
|
||||
format={formatDate}
|
||||
format={defaultFormat}
|
||||
showTime
|
||||
onChange={(date) => onChange(date)}
|
||||
{...other}
|
||||
|
@ -6,17 +6,25 @@ import { invokeWebApiWrapperAsync } from '../factory'
|
||||
|
||||
const newRowKeyValue = 'newRow'
|
||||
|
||||
export const makeActionHandler = (action, { service, setLoader, errorMsg, onComplete }) => service && action && (
|
||||
export const makeActionHandler = (action, { idWell, service, setLoader, errorMsg, onComplete }, recordParser) => service && action && (
|
||||
(record) => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const addIdWell = (...params) => idWell ? [idWell, ...params] : params
|
||||
if (typeof recordParser === 'function')
|
||||
record = recordParser(record)
|
||||
|
||||
if (action === 'insert') {
|
||||
record.key = Date.now()
|
||||
await service.insert(record)
|
||||
await service.insert(...addIdWell(record))
|
||||
} else if (action === 'insertRange') {
|
||||
if (!Array.isArray(record))
|
||||
record = [record]
|
||||
await service.insertRange(...addIdWell(record))
|
||||
} else if (record.id) {
|
||||
if (action === 'update')
|
||||
await service.put(record.id, record)
|
||||
await service.put(...addIdWell(record.id, record))
|
||||
else if (action === 'delete')
|
||||
await service.delete(record.id)
|
||||
await service.delete(...addIdWell(record.id))
|
||||
}
|
||||
if (onComplete)
|
||||
await onComplete()
|
||||
|
@ -10,7 +10,6 @@ export { DatePickerWrapper } from './DatePickerWrapper'
|
||||
export { SelectFromDictionary } from './SelectFromDictionary'
|
||||
|
||||
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
|
||||
export const formatDate = 'YYYY.MM.DD HH:mm'
|
||||
|
||||
export const makeNumericRender = (fixed?: number) => (value: any, row: object): ReactNode => {
|
||||
let val = '-'
|
||||
@ -209,7 +208,7 @@ export const makeSelectColumn = <T extends unknown = string>(
|
||||
other?: columnPropsOther
|
||||
) => makeColumn(title, dataIndex, {
|
||||
input: <Select options={options}/>,
|
||||
render: (value) => options.find(option => option?.value === value)?.label ?? defaultValue ?? '--',
|
||||
render: (value) => options?.find(option => option?.value === value)?.label ?? defaultValue ?? value ?? '--',
|
||||
...other
|
||||
})
|
||||
|
||||
|
@ -2,14 +2,17 @@ import { TreeSelect } from 'antd'
|
||||
import { DefaultValueType } from 'rc-tree-select/lib/interface'
|
||||
import { useState, useEffect, ReactNode } from 'react'
|
||||
import { useHistory, useRouteMatch } from 'react-router-dom'
|
||||
import LoaderPortal from './LoaderPortal'
|
||||
|
||||
import { DepositService } from '../services/api'
|
||||
import { invokeWebApiWrapperAsync } from './factory'
|
||||
import { WellIcon, WellIconState } from './icons'
|
||||
import { ReactComponent as DepositIcon } from '../images/DepositIcon.svg'
|
||||
import { ReactComponent as ClusterIcon } from '../images/ClusterIcon.svg'
|
||||
import { DepositDto } from '../services/api'
|
||||
import { RawDate } from '../utils/DateTimeUtils'
|
||||
import { RawDate } from '../utils'
|
||||
|
||||
import LoaderPortal from './LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from './factory'
|
||||
import { WellIcon, WellIconState } from './icons'
|
||||
|
||||
import '../styles/wellTreeSelect.css'
|
||||
|
||||
export const getWellState = (idState?: number): WellIconState => idState === 1 ? 'active' : 'unknown'
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Dispatch, SetStateAction } from "react"
|
||||
import { notification } from 'antd';
|
||||
import { notification } from 'antd'
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
import { FileInfoDto } from '../services/api'
|
||||
|
||||
const notificationTypeDictionary = new Map([
|
||||
['error', {notifyInstance: notification.error, caption: 'Ошибка'}],
|
||||
['warning', {notifyInstance: notification.warning, caption: 'Предупреждение'}],
|
||||
['info', {notifyInstance: notification.info, caption: 'Инфо'}],
|
||||
['open', {notifyInstance: notification.info, caption: ''}],
|
||||
['error' , { notifyInstance: notification.error , caption: 'Ошибка' }],
|
||||
['warning', { notifyInstance: notification.warning, caption: 'Предупреждение' }],
|
||||
['info' , { notifyInstance: notification.info , caption: 'Инфо' }],
|
||||
['open' , { notifyInstance: notification.info , caption: '' }],
|
||||
])
|
||||
|
||||
/**
|
||||
@ -14,30 +14,29 @@ const notificationTypeDictionary = new Map([
|
||||
* @param body string или ReactNode
|
||||
* @param notifyType для параметра типа. Допустимые значение 'error', 'warning', 'info'
|
||||
*/
|
||||
export const notify = (body: string|any, notifyType:string ='info', other?: any) => {
|
||||
if(!body)
|
||||
return
|
||||
const instance = notificationTypeDictionary.get(notifyType) ??
|
||||
export const notify = (body: string | any, notifyType: string = 'info', other?: any) => {
|
||||
if (!body) return
|
||||
|
||||
const instance = notificationTypeDictionary.get(notifyType) ??
|
||||
notificationTypeDictionary.get('open')
|
||||
|
||||
instance?.notifyInstance({
|
||||
description: body,
|
||||
message: instance.caption,
|
||||
placement: "bottomRight",
|
||||
placement: 'bottomRight',
|
||||
duration: 10,
|
||||
...other
|
||||
})
|
||||
}
|
||||
|
||||
type asyncFunction = (...args:any) => Promise<any|void>;
|
||||
type asyncFunction = (...args: any) => Promise<any|void>
|
||||
|
||||
export const invokeWebApiWrapperAsync = async (
|
||||
funcAsync: asyncFunction,
|
||||
setShowLoader: Dispatch<SetStateAction<boolean>>,
|
||||
errorNotifyText: (string | ((ex: unknown) => string))
|
||||
setShowLoader?: Dispatch<SetStateAction<boolean>>,
|
||||
errorNotifyText?: (string | ((ex: unknown) => string))
|
||||
) => {
|
||||
if(setShowLoader)
|
||||
setShowLoader(true)
|
||||
setShowLoader?.(true)
|
||||
try{
|
||||
await funcAsync()
|
||||
} catch (ex) {
|
||||
@ -48,49 +47,43 @@ export const invokeWebApiWrapperAsync = async (
|
||||
notify(errorNotifyText(ex), 'error')
|
||||
else notify(errorNotifyText, 'error')
|
||||
}
|
||||
} finally{
|
||||
if(setShowLoader)
|
||||
setShowLoader(false)
|
||||
} finally {
|
||||
setShowLoader?.(false)
|
||||
}
|
||||
}
|
||||
|
||||
export const download = async (url:string, fileName?:string) => {
|
||||
export const download = async (url: string, fileName?: string) => {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + localStorage['token']
|
||||
Authorization: `Bearer ${localStorage['token']}`
|
||||
},
|
||||
method: 'Get'
|
||||
})
|
||||
if(response.status !== 200)
|
||||
throw response
|
||||
const requestFileName = decodeURI(fileName
|
||||
??response.headers
|
||||
.get('content-disposition')
|
||||
?.split(';')
|
||||
.splice(-1)[0]
|
||||
.split("'")
|
||||
.splice(-1)[0]
|
||||
?? url.replace('\\','/')
|
||||
.split('/')
|
||||
.splice(-1)[0]
|
||||
?? 'file')
|
||||
const requestFileName = decodeURI(
|
||||
fileName
|
||||
?? response.headers.get('content-disposition')?.split(';').pop()?.split(`'`).pop()
|
||||
?? url.replace('\\','/').split('/').pop()
|
||||
?? 'file'
|
||||
)
|
||||
const blob = await response.blob()
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(blob);
|
||||
reader.onload = function (e) {
|
||||
let a = document.createElement('a');
|
||||
a.href = (e.target?.result?.toString() ?? '');
|
||||
a.download = requestFileName;
|
||||
document.body.appendChild(a); // we need to append the element to the dom -> otherwise it will not work in firefox
|
||||
a.click();
|
||||
a.remove();
|
||||
};
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(blob)
|
||||
reader.onload = (e) => {
|
||||
const a = document.createElement('a')
|
||||
a.href = (e.target?.result?.toString() ?? '')
|
||||
a.download = requestFileName
|
||||
document.body.appendChild(a) // we need to append the element to the dom -> otherwise it will not work in firefox
|
||||
a.click()
|
||||
a.remove()
|
||||
}
|
||||
}
|
||||
|
||||
export const upload = async (url:string, formData: FormData) => {
|
||||
let response = await fetch(url, {
|
||||
export const upload = async (url: string, formData: FormData) => {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + localStorage['token']
|
||||
Authorization: `Bearer ${localStorage['token']}`
|
||||
},
|
||||
method: 'Post',
|
||||
body: formData,
|
||||
@ -107,28 +100,11 @@ export const downloadFile = async (fileInfo: FileInfoDto) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const formatBytes = (bytes:number) => {
|
||||
export const formatBytes = (bytes: number) => {
|
||||
if(bytes < 1024)
|
||||
return `${bytes.toFixed(0)}b`
|
||||
if(bytes < 1024*1024)
|
||||
return `${(bytes/1024).toFixed(2)}kb`
|
||||
if(bytes < 1024 * 1024)
|
||||
return `${(bytes / 1024).toFixed(2)}kb`
|
||||
else
|
||||
return `${(bytes/1024/1024).toFixed(2)}Mb`
|
||||
return `${(bytes / 1024 / 1024).toFixed(2)}Mb`
|
||||
}
|
||||
|
||||
export const formatTimespan = (seconds:number) => {
|
||||
const days = Math.floor(seconds / 86400)
|
||||
seconds = seconds % 86400
|
||||
const hours = Math.floor(seconds / 3600)
|
||||
seconds = seconds % 3600
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
seconds = seconds % 60
|
||||
let formatedTimespan = ''
|
||||
if(days > 0)
|
||||
formatedTimespan += days + ' '
|
||||
|
||||
formatedTimespan += hours.toString().padStart(2,'0') + ':' +
|
||||
minutes.toString().padStart(2,'0') + ':' +
|
||||
seconds.toString().padStart(2,'0')
|
||||
return formatedTimespan
|
||||
}
|
@ -55,6 +55,13 @@ export default function ClusterWells({statsWells}) {
|
||||
if (!filtersWellsType.some((el) => el.text === well.wellType))
|
||||
filtersWellsType.push({ text: well.wellType, value: well.wellType,})
|
||||
|
||||
let periodPlanValue = well.total?.plan?.start && well.total?.plan?.end
|
||||
? (new Date(well.total?.plan?.end) - new Date(well.total?.plan?.start)) / DAY_IN_MS
|
||||
: '-'
|
||||
let periodFactValue = well.total?.fact?.start && well.total?.fact?.end
|
||||
? (new Date(well.total?.fact?.end) - new Date(well.total?.fact?.start)) / DAY_IN_MS
|
||||
: '-'
|
||||
|
||||
return {
|
||||
key: well.caption,
|
||||
id: well.id,
|
||||
@ -62,8 +69,8 @@ export default function ClusterWells({statsWells}) {
|
||||
wellType: well.wellType,
|
||||
factStart: well.total?.fact?.start,
|
||||
factEnd: well.total?.fact?.end,
|
||||
periodPlan: (new Date(well.total?.plan?.end) - new Date(well.total?.plan?.start)) / DAY_IN_MS,
|
||||
periodFact: (new Date(well.total?.fact?.end) - new Date(well.total?.fact?.start)) / DAY_IN_MS,
|
||||
periodPlan: periodPlanValue,
|
||||
periodFact: periodFactValue,
|
||||
rateOfPenetrationPlan: well.total?.plan?.rop,
|
||||
rateOfPenetrationFact: well.total?.fact?.rop,
|
||||
routeSpeedPlan: well.total?.plan?.routeSpeed,
|
||||
@ -91,7 +98,9 @@ export default function ClusterWells({statsWells}) {
|
||||
setTableData(data)
|
||||
}, [statsWells])
|
||||
|
||||
const getDate = (str) => Number.isNaN(+new Date(str)) ? '-' : new Date(str).toLocaleString()
|
||||
const getDate = (str) => Number.isNaN(+new Date(str)) || +new Date(str) === 0
|
||||
? '-'
|
||||
: new Date(str).toLocaleString()
|
||||
|
||||
const columns = [
|
||||
makeTextColumn('скв №', 'caption', null, null,
|
||||
@ -110,11 +119,11 @@ export default function ClusterWells({statsWells}) {
|
||||
makeTextColumn('Тип скв.', 'wellType', filtersWellsType, null, (text) => text ?? '-'),
|
||||
makeGroupColumn('Фактические сроки', [
|
||||
makeColumn('начало', 'factStart', { sorter: makeDateSorter('factStart'), render: getDate }),
|
||||
makeColumn('окончание', 'factEnd', { sorter: makeDateSorter('factEnd'), render: getDate })
|
||||
makeColumn('окончание', 'factEnd', { sorter: makeDateSorter('factEnd'), render: getDate }),
|
||||
]),
|
||||
makeNumericColumnPlanFact('Продолжительность', 'period', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('МСП', 'rateOfPenetration', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Рейсовая скорость', 'routeSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Продолжительность, сут', 'period', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('МСП, м/ч', 'rateOfPenetration', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Рейсовая скорость, м/ч', 'routeSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('НПВ, сут', 'notProductiveTime', filtersMinMax, makeFilterMinMaxFunction),
|
||||
{
|
||||
title: 'TVD',
|
||||
|
@ -1,29 +1,38 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Modal } from 'antd'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
|
||||
import { Table } from '../../components/Table'
|
||||
import { formatDate } from './MeasureTable'
|
||||
import { formatDate } from '../../utils'
|
||||
|
||||
import { v } from './columnsCommon'
|
||||
|
||||
export const InclinometryTable = React.memo(({ group, visible, onClose }) => {
|
||||
const tableScroll = { y: 400, x: 1300 }
|
||||
|
||||
const dateColumn = {
|
||||
title: 'Дата',
|
||||
key: 'date',
|
||||
dataIndex: 'date',
|
||||
render: (date) => formatDate(date, true) ?? 'Нет данных',
|
||||
fixed: 'left',
|
||||
width: '8rem',
|
||||
}
|
||||
|
||||
export const InclinometryTable = memo(({ group, visible, onClose }) => {
|
||||
const [tableColumns, setTableColumns] = useState([])
|
||||
const [tableData, setTableData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setTableColumns([{
|
||||
title: 'Дата',
|
||||
key: 'date',
|
||||
dataIndex: 'date',
|
||||
render: (item) => formatDate(item),
|
||||
fixed: 'left',
|
||||
width: '8rem',
|
||||
},
|
||||
...(group?.columns?.map((column) => ({...column, title: v(column.title)})) ?? [])
|
||||
])
|
||||
}, [group?.columns])
|
||||
useEffect(() => setTableColumns([
|
||||
dateColumn,
|
||||
...(group?.columns?.map((column) => ({
|
||||
...column,
|
||||
title: v(column.title)
|
||||
})) ?? [])
|
||||
]), [group?.columns])
|
||||
|
||||
useEffect(() => {
|
||||
setTableData(group?.values?.map(row => ({ date: row.timestamp, ...row.data })))
|
||||
}, [group?.values])
|
||||
useEffect(() => setTableData(group?.values?.map(row => ({
|
||||
date: row.timestamp,
|
||||
...row.data
|
||||
}))), [group?.values])
|
||||
|
||||
return !group?.columns ? null : (
|
||||
<Modal
|
||||
@ -37,14 +46,9 @@ export const InclinometryTable = React.memo(({ group, visible, onClose }) => {
|
||||
<Table
|
||||
dataSource={tableData}
|
||||
columns={tableColumns}
|
||||
scroll={{
|
||||
y: 400,
|
||||
x: 1300
|
||||
}}
|
||||
scroll={tableScroll}
|
||||
bordered
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
})
|
||||
|
||||
export default InclinometryTable
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button, Form, Input, Popconfirm, Timeline } from 'antd'
|
||||
import moment from 'moment'
|
||||
import {
|
||||
CheckSquareOutlined,
|
||||
EditOutlined,
|
||||
@ -9,15 +8,19 @@ import {
|
||||
CloseCircleOutlined,
|
||||
DeleteOutlined
|
||||
} from '@ant-design/icons'
|
||||
import { View } from './View'
|
||||
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { MeasureService } from '../../services/api'
|
||||
import '../../styles/index.css'
|
||||
import '../../styles/measure.css'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
|
||||
const dateFormat = 'YYYY.MM.DD HH:mm'
|
||||
export const formatDate = (date) => date ? moment.utc(date).local().format(dateFormat) : 'Нет данных'
|
||||
import { View } from './View'
|
||||
|
||||
import '../../styles/index.css'
|
||||
import '../../styles/measure.css'
|
||||
import { formatDate } from '../../utils'
|
||||
|
||||
const createEditingColumns = (cols, renderDelegate) =>
|
||||
cols.map(col => ({ render: renderDelegate, ...col }))
|
||||
|
||||
export const MeasureTable = ({idWell, group, updateMeasuresFunc, additionalButtons}) => {
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
@ -29,11 +32,10 @@ export const MeasureTable = ({idWell, group, updateMeasuresFunc, additionalButto
|
||||
|
||||
const [measuresForm] = Form.useForm()
|
||||
|
||||
const createEditingColumns = (cols, renderDelegate) =>
|
||||
cols.map(col => ({ render: renderDelegate, ...col }))
|
||||
|
||||
useEffect(() => {
|
||||
const data = [group.defaultValue].concat(group.values ?? [])
|
||||
let data = [group.defaultValue]
|
||||
if (group?.values?.length > 0)
|
||||
data = group.values
|
||||
setData(data)
|
||||
setDisplayedValues(data.at(-1))
|
||||
}, [group.defaultValue, group.values])
|
||||
@ -118,8 +120,8 @@ export const MeasureTable = ({idWell, group, updateMeasuresFunc, additionalButto
|
||||
<Button key={'edit'} className={'flex-1'} onClick={() => editTable('edit')} disabled={isDataDefault()}>
|
||||
<EditOutlined />
|
||||
</Button>
|
||||
<Popconfirm style={{flex: '1'}} title={'Удалить данные?'} onConfirm={markMeasuresAsDeleted} disabled={isDataDefault()}>
|
||||
<Button key={'delete'} onClick={() => setEditingActionName('delete')} disabled={isDataDefault()} >
|
||||
<Popconfirm style={{ flex: '1' }} title={'Удалить данные?'} onConfirm={markMeasuresAsDeleted} disabled={isDataDefault()}>
|
||||
<Button key={'delete'} onClick={() => setEditingActionName('delete')} disabled={isDataDefault()}>
|
||||
<DeleteOutlined style={{ margin:'auto 28px' }}/>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
@ -136,12 +138,12 @@ export const MeasureTable = ({idWell, group, updateMeasuresFunc, additionalButto
|
||||
key={index}
|
||||
className={'measure-button'}
|
||||
onClick={() => setDisplayedValues(item)}
|
||||
dot={item?.id !== displayedValues?.id ? null :
|
||||
dot={item?.id === displayedValues?.id &&
|
||||
<CheckSquareOutlined className={'timeline-clock-icon'} />
|
||||
}
|
||||
>
|
||||
<span className={item?.id === displayedValues?.id ? 'selected-timeline' : ''}>
|
||||
{formatDate(item.timestamp)}
|
||||
<span className={item?.id === displayedValues?.id ? 'selected-timeline' : undefined}>
|
||||
{formatDate(item.timestamp, true) ?? 'Нет данных'}
|
||||
</span>
|
||||
</Timeline.Item>
|
||||
)}
|
||||
|
@ -1,61 +1,50 @@
|
||||
import React from 'react'
|
||||
import { memo, Fragment } from 'react'
|
||||
import { Empty, Form } from 'antd'
|
||||
|
||||
import { Grid, GridItem } from '../../components/Grid'
|
||||
|
||||
import '../../styles/index.css'
|
||||
import '../../styles/measure.css'
|
||||
|
||||
const colsCount = 3
|
||||
|
||||
const headerCellStyle = {
|
||||
border:'1px solid lightgrey'
|
||||
}
|
||||
export const View = memo(({ columns, item }) => !item || !columns?.length ? (
|
||||
<Empty key={'empty'} image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
) : (
|
||||
<Grid>
|
||||
{columns.map((column, i) => (
|
||||
<Fragment key={i}>
|
||||
<GridItem
|
||||
key={column.dataIndex}
|
||||
row={Math.floor(i / colsCount) + 1}
|
||||
col={(i % colsCount) * 2 + 1}
|
||||
className={'measure-column-header'}
|
||||
>
|
||||
{column.title}
|
||||
</GridItem>
|
||||
|
||||
const valueCellStyle = {
|
||||
border:'1px solid lightgrey',
|
||||
justifyContent:'right',
|
||||
marginRight:'16px',
|
||||
fontWeight:'bold',
|
||||
textAlign:'right',
|
||||
padding: 0
|
||||
}
|
||||
|
||||
export const View = React.memo(({columns, item}) => {
|
||||
if (!item || !columns?.length)
|
||||
return <Empty key='empty' image={Empty.PRESENTED_IMAGE_SIMPLE}/>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
{columns.map((column, i) => (
|
||||
<>
|
||||
<GridItem
|
||||
key={column.dataIndex}
|
||||
row={Math.floor(i / colsCount) + 1}
|
||||
col={(i % colsCount) * 2 + 1}
|
||||
style={headerCellStyle}
|
||||
>
|
||||
{column.title}
|
||||
</GridItem>
|
||||
|
||||
<GridItem
|
||||
key={column.title}
|
||||
row={Math.floor(i / colsCount) + 1}
|
||||
col={(i % colsCount) * 2 + 2}
|
||||
style={valueCellStyle}
|
||||
>
|
||||
{column.render ? (
|
||||
<Form.Item
|
||||
key={column.dataIndex}
|
||||
name={column.dataIndex}
|
||||
style={{ margin: 0 }}
|
||||
//rules={column.formItemRules}
|
||||
>
|
||||
{column.render(item[column.dataIndex])}
|
||||
</Form.Item>
|
||||
) : (
|
||||
<p key={column.title} className='m-5px'>{item[column.dataIndex]}</p>
|
||||
)}
|
||||
</GridItem>
|
||||
</>
|
||||
))}
|
||||
</Grid>
|
||||
)
|
||||
})
|
||||
<GridItem
|
||||
key={column.title}
|
||||
row={Math.floor(i / colsCount) + 1}
|
||||
col={(i % colsCount) * 2 + 2}
|
||||
className={'measure-column-value'}
|
||||
style={{ padding: 0 }}
|
||||
>
|
||||
{column.render ? (
|
||||
<Form.Item
|
||||
key={column.dataIndex}
|
||||
name={column.dataIndex}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
{column.render(item[column.dataIndex])}
|
||||
</Form.Item>
|
||||
) : (
|
||||
<p key={column.title} className={'m-5px'}>
|
||||
{item[column.dataIndex]}
|
||||
</p>
|
||||
)}
|
||||
</GridItem>
|
||||
</Fragment>
|
||||
))}
|
||||
</Grid>
|
||||
))
|
||||
|
@ -1,27 +1,12 @@
|
||||
import React from 'react'
|
||||
import { Input } from 'antd'
|
||||
|
||||
import { RegExpIsFloat } from '../../components/Table'
|
||||
|
||||
const { TextArea } = Input
|
||||
import '../../styles/measure.css'
|
||||
|
||||
export const v = (text) => (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
height: '180px',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<span
|
||||
style={{
|
||||
whiteSpace: 'pre',
|
||||
verticalAlign: 'center',
|
||||
textAlign: 'center',
|
||||
|
||||
WebkitTransform: 'rotate(-90deg)',
|
||||
MozTransform: 'rotate(-90deg)',
|
||||
MsTransform: 'rotate(-90deg)',
|
||||
OTransform: 'rotate(-90deg)',
|
||||
transform: 'rotate(-90deg)',
|
||||
}}>
|
||||
<div className={'v-div'}>
|
||||
<span className={'v-span'}>
|
||||
{text}
|
||||
</span>
|
||||
</div>
|
||||
@ -42,7 +27,7 @@ export const numericColumnOptions = {
|
||||
|
||||
export const textColumnOptions = {
|
||||
editable: true,
|
||||
input: <TextArea/>,
|
||||
input: <Input.TextArea/>,
|
||||
width: '3rem',
|
||||
formItemRules: [{
|
||||
required: true,
|
||||
|
@ -1,36 +1,37 @@
|
||||
import { makeColumn } from '../../components/Table';
|
||||
import { makeColumn } from '../../components/Table'
|
||||
|
||||
import { numericColumnOptions, textColumnOptions } from './columnsCommon'
|
||||
|
||||
export const columnsDrillingFluid = [
|
||||
makeColumn('Наименование', 'name', textColumnOptions),
|
||||
makeColumn('Температура, °C', 'temperature',numericColumnOptions),
|
||||
makeColumn('Плотность, г/см³', 'density',numericColumnOptions),
|
||||
makeColumn('Усл. вязкость, сек', 'conditionalViscosity',numericColumnOptions),
|
||||
makeColumn('R300', 'r300',numericColumnOptions),
|
||||
makeColumn('R600', 'r600',numericColumnOptions),
|
||||
makeColumn('R3/R6', 'r3r6',numericColumnOptions),
|
||||
makeColumn('ДНС, дПа', 'dnsDpa',numericColumnOptions),
|
||||
makeColumn('Пластич. вязкость, сПз', 'plasticViscocity',numericColumnOptions),
|
||||
makeColumn('СНС, дПа', 'snsDpa',numericColumnOptions),
|
||||
makeColumn('R3/R6 49С', 'r3r649С',numericColumnOptions),
|
||||
makeColumn('ДНС 49С, дПа', 'dns49Cdpa',numericColumnOptions),
|
||||
makeColumn('Пластич. вязкость 49С, сПз', 'plasticViscocity49c',numericColumnOptions),
|
||||
makeColumn('СНС 49С, дПа', 'sns49Cdpa',numericColumnOptions),
|
||||
makeColumn('МВТ, кг/м³', 'mbt',numericColumnOptions),
|
||||
makeColumn('Песок, %', 'sand',numericColumnOptions),
|
||||
makeColumn('Фильтрация, см³/30мин', 'filtering',numericColumnOptions),
|
||||
makeColumn('Корка, мм', 'crust',numericColumnOptions),
|
||||
makeColumn('KTK', 'ktk',numericColumnOptions),
|
||||
makeColumn('pH', 'ph',numericColumnOptions),
|
||||
makeColumn('Жесткость, мг/л', 'hardness',numericColumnOptions),
|
||||
makeColumn('Хлориды, мг/л', 'chlorides',numericColumnOptions),
|
||||
makeColumn('PF', 'pf',numericColumnOptions),
|
||||
makeColumn('Mf', 'mf',numericColumnOptions),
|
||||
makeColumn('Pm', 'pm',numericColumnOptions),
|
||||
makeColumn('Твердая фаза раствора, %', 'fluidSolidPhase',numericColumnOptions),
|
||||
makeColumn('Смазка, %', 'grease',numericColumnOptions),
|
||||
makeColumn('Карбонат кальция, кг/м³', 'calciumCarbonate',numericColumnOptions),
|
||||
];
|
||||
makeColumn('Температура, °C', 'temperature', numericColumnOptions),
|
||||
makeColumn('Плотность, г/см³', 'density', numericColumnOptions),
|
||||
makeColumn('Усл. вязкость, сек', 'conditionalViscosity', numericColumnOptions),
|
||||
makeColumn('R300', 'r300', numericColumnOptions),
|
||||
makeColumn('R600', 'r600', numericColumnOptions),
|
||||
makeColumn('R3/R6', 'r3r6', numericColumnOptions),
|
||||
makeColumn('ДНС, дПа', 'dnsDpa', numericColumnOptions),
|
||||
makeColumn('Пластич. вязкость, сПз', 'plasticViscocity', numericColumnOptions),
|
||||
makeColumn('СНС, дПа', 'snsDpa', numericColumnOptions),
|
||||
makeColumn('R3/R6 49С', 'r3r649С', numericColumnOptions),
|
||||
makeColumn('ДНС 49С, дПа', 'dns49Cdpa', numericColumnOptions),
|
||||
makeColumn('Пластич. вязкость 49С, сПз', 'plasticViscocity49c', numericColumnOptions),
|
||||
makeColumn('СНС 49С, дПа', 'sns49Cdpa', numericColumnOptions),
|
||||
makeColumn('МВТ, кг/м³', 'mbt', numericColumnOptions),
|
||||
makeColumn('Песок, %', 'sand', numericColumnOptions),
|
||||
makeColumn('Фильтрация, см³/30мин', 'filtering', numericColumnOptions),
|
||||
makeColumn('Корка, мм', 'crust', numericColumnOptions),
|
||||
makeColumn('KTK', 'ktk', numericColumnOptions),
|
||||
makeColumn('pH', 'ph', numericColumnOptions),
|
||||
makeColumn('Жесткость, мг/л', 'hardness', numericColumnOptions),
|
||||
makeColumn('Хлориды, мг/л', 'chlorides', numericColumnOptions),
|
||||
makeColumn('PF', 'pf', numericColumnOptions),
|
||||
makeColumn('Mf', 'mf', numericColumnOptions),
|
||||
makeColumn('Pm', 'pm', numericColumnOptions),
|
||||
makeColumn('Твердая фаза раствора, %', 'fluidSolidPhase', numericColumnOptions),
|
||||
makeColumn('Смазка, %', 'grease', numericColumnOptions),
|
||||
makeColumn('Карбонат кальция, кг/м³', 'calciumCarbonate', numericColumnOptions),
|
||||
]
|
||||
|
||||
export const drillingFluidDefaultData = {
|
||||
idWell: 0,
|
||||
@ -38,33 +39,33 @@ export const drillingFluidDefaultData = {
|
||||
idCategory: 0,
|
||||
isDefaultData: true,
|
||||
data: {
|
||||
'name': 0,
|
||||
'temperature': 0,
|
||||
'density': 0,
|
||||
'conditionalViscosity': 0,
|
||||
'r300': 0,
|
||||
'r600': 0,
|
||||
'r3r6': 0,
|
||||
'dnsDpa': 0,
|
||||
'plasticViscocity': 0,
|
||||
'snsDpa': 0,
|
||||
'r3r649С': 0,
|
||||
'dns49Cdpa': 0,
|
||||
'plasticViscocity49c': 0,
|
||||
'sns49Cdpa': 0,
|
||||
'mbt': 0,
|
||||
'sand': 0,
|
||||
'filtering': 0,
|
||||
'crust': 0,
|
||||
'ktk': 0,
|
||||
'ph': 0,
|
||||
'hardness': 0,
|
||||
'chlorides': 0,
|
||||
'pf': 0,
|
||||
'mf': 0,
|
||||
'pm': 0,
|
||||
'fluidSolidPhase': 0,
|
||||
'grease': 0,
|
||||
'calciumCarbonate': 0
|
||||
name: 0,
|
||||
temperature: 0,
|
||||
density: 0,
|
||||
conditionalViscosity: 0,
|
||||
r300: 0,
|
||||
r600: 0,
|
||||
r3r6: 0,
|
||||
dnsDpa: 0,
|
||||
plasticViscocity: 0,
|
||||
snsDpa: 0,
|
||||
r3r649С: 0,
|
||||
dns49Cdpa: 0,
|
||||
plasticViscocity49c: 0,
|
||||
sns49Cdpa: 0,
|
||||
mbt: 0,
|
||||
sand: 0,
|
||||
filtering: 0,
|
||||
crust: 0,
|
||||
ktk: 0,
|
||||
ph: 0,
|
||||
hardness: 0,
|
||||
chlorides: 0,
|
||||
pf: 0,
|
||||
mf: 0,
|
||||
pm: 0,
|
||||
fluidSolidPhase: 0,
|
||||
grease: 0,
|
||||
calciumCarbonate: 0
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,55 @@
|
||||
import { Button } from 'antd'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { TableOutlined } from '@ant-design/icons'
|
||||
|
||||
import { MeasureService } from '../../services/api'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
|
||||
import { MeasureTable } from './MeasureTable'
|
||||
import { InclinometryTable } from './InclinometryTable'
|
||||
import { columnsNnb, nnbDefaultData } from './nnbData'
|
||||
import { columnsMudDiagram, mudDiagramDefaultData } from './mudDiagramData'
|
||||
import { columnsDrillingFluid, drillingFluidDefaultData } from './drillingFluidData'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import { MeasureService } from '../../services/api'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { MeasureTable } from './MeasureTable'
|
||||
import InclinometryTable from './InclinometryTable'
|
||||
import { Button } from 'antd'
|
||||
import { TableOutlined } from '@ant-design/icons'
|
||||
|
||||
const defaultData = [
|
||||
{
|
||||
idCategory: 1,
|
||||
title: 'Замер бурового раствора',
|
||||
columns: columnsDrillingFluid,
|
||||
values: [drillingFluidDefaultData],
|
||||
defaultValue: drillingFluidDefaultData,
|
||||
},
|
||||
{
|
||||
}, {
|
||||
idCategory: 2,
|
||||
title: 'Шламограмма',
|
||||
columns: columnsMudDiagram,
|
||||
values: [mudDiagramDefaultData],
|
||||
defaultValue: mudDiagramDefaultData,
|
||||
},
|
||||
{
|
||||
}, {
|
||||
idCategory: 3,
|
||||
title: 'ННБ',
|
||||
columns: columnsNnb,
|
||||
values: [nnbDefaultData],
|
||||
defaultValue: nnbDefaultData,
|
||||
additionalButtons: (group, idx, onClick) => (isEditing) => isEditing ? null : (
|
||||
<Button
|
||||
key={'table'}
|
||||
className={'flex-1'}
|
||||
onClick={() => onClick(idx)}
|
||||
disabled={!group.values || group.values.length <= 0}
|
||||
>
|
||||
<TableOutlined />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
export default function Measure({idWell}){
|
||||
export const Measure = ({ idWell }) => {
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [isMeasuresUpdating, setIsMeasuresUpdating] = useState(false)
|
||||
const [isMeasuresUpdating, setIsMeasuresUpdating] = useState(true)
|
||||
const [data, setData] = useState(defaultData)
|
||||
const [tableIdx, setTableIdx] = useState(-1)
|
||||
|
||||
const updateCurrentValues = () => invokeWebApiWrapperAsync(
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
if (!isMeasuresUpdating) return
|
||||
const measures = await MeasureService.getHisory(idWell)
|
||||
setIsMeasuresUpdating(false)
|
||||
|
||||
@ -57,28 +65,9 @@ export default function Measure({idWell}){
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить последние данные по скважине ${idWell}`
|
||||
)
|
||||
), [idWell, isMeasuresUpdating])
|
||||
|
||||
useEffect(() => {
|
||||
setData(prevData => {
|
||||
prevData[2].additionalButtons = (group, idx) => (isEditing) => isEditing ? null : (
|
||||
<Button
|
||||
key={'table'}
|
||||
className={'flex-1'}
|
||||
onClick={() => setTableIdx(idx)}
|
||||
disabled={!group.values || group.values.length <= 0}
|
||||
>
|
||||
<TableOutlined />
|
||||
</Button>
|
||||
)
|
||||
|
||||
return prevData
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(updateCurrentValues, [idWell, isMeasuresUpdating])
|
||||
|
||||
return <>
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
{data.map((group, idx) => (
|
||||
<MeasureTable
|
||||
@ -86,7 +75,7 @@ export default function Measure({idWell}){
|
||||
idWell={idWell}
|
||||
group={group}
|
||||
updateMeasuresFunc={() => setIsMeasuresUpdating(true)}
|
||||
additionalButtons={group.additionalButtons?.(group, idx)}
|
||||
additionalButtons={group.additionalButtons?.(group, idx, setTableIdx)}
|
||||
/>
|
||||
))}
|
||||
<InclinometryTable
|
||||
@ -95,5 +84,7 @@ export default function Measure({idWell}){
|
||||
group={data[tableIdx]}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Measure
|
||||
|
@ -32,27 +32,27 @@ export const mudDiagramDefaultData = {
|
||||
idCategory: 0,
|
||||
isDefaultData: true,
|
||||
data: {
|
||||
'probeNumber': 0,
|
||||
'probeExtractionDepth': 0,
|
||||
'sandstone': 0,
|
||||
'siltstone': 0,
|
||||
'argillit': 0,
|
||||
'brokenArgillit': 0,
|
||||
'coal': 0,
|
||||
'sand': 0,
|
||||
'clay': 0,
|
||||
'camstone': 0,
|
||||
'cement': 0,
|
||||
'summary': '-',
|
||||
'drillingMud': 0,
|
||||
'sludge': 0,
|
||||
'maxSum': 0,
|
||||
'methane': 0,
|
||||
'ethane': 0,
|
||||
'propane': 0,
|
||||
'butane': 0,
|
||||
'pentane': 0,
|
||||
'mechanicalSpeed': 0,
|
||||
'preliminaryConclusion': '-'
|
||||
probeNumber: 0,
|
||||
probeExtractionDepth: 0,
|
||||
sandstone: 0,
|
||||
siltstone: 0,
|
||||
argillit: 0,
|
||||
brokenArgillit: 0,
|
||||
coal: 0,
|
||||
sand: 0,
|
||||
clay: 0,
|
||||
camstone: 0,
|
||||
cement: 0,
|
||||
summary: '-',
|
||||
drillingMud: 0,
|
||||
sludge: 0,
|
||||
maxSum: 0,
|
||||
methane: 0,
|
||||
ethane: 0,
|
||||
propane: 0,
|
||||
butane: 0,
|
||||
pentane: 0,
|
||||
mechanicalSpeed: 0,
|
||||
preliminaryConclusion: '-'
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export const columnsNnb = [
|
||||
makeColumn('Комментарий', 'comment', numericColumnOptions),
|
||||
makeColumn('Разница вертикальных глубин\nмежду планом и фактом', 'depthPlanFactDifference', numericColumnOptions),
|
||||
makeColumn('Расстояние в пространстве\nмежду планом и фактом', 'distancePlanFactDifference', numericColumnOptions),
|
||||
];
|
||||
]
|
||||
|
||||
export const nnbDefaultData = {
|
||||
idWell: 0,
|
||||
@ -27,22 +27,22 @@ export const nnbDefaultData = {
|
||||
idCategory: 0,
|
||||
isDefaultData: true,
|
||||
data: {
|
||||
'depth': 0,
|
||||
'zenithAngle': 0,
|
||||
'magneticAzimuth': 0,
|
||||
'trueAzimuth': 0,
|
||||
'directAzimuth': 0,
|
||||
'verticalDepth': 0,
|
||||
'absoluteMark': 0,
|
||||
'localNorthOffset': 0,
|
||||
'localEastOffset': 0,
|
||||
'outFallOffset': 0,
|
||||
'offsetAzimuth': 0,
|
||||
'areaIntensity': '-',
|
||||
'offsetStopAngle': 0,
|
||||
'zenithIntensity': 0,
|
||||
'comment': '-',
|
||||
'depthPlanFactDifference': 0,
|
||||
'distancePlanFactDifference': 0
|
||||
depth: 0,
|
||||
zenithAngle: 0,
|
||||
magneticAzimuth: 0,
|
||||
trueAzimuth: 0,
|
||||
directAzimuth: 0,
|
||||
verticalDepth: 0,
|
||||
absoluteMark: 0,
|
||||
localNorthOffset: 0,
|
||||
localEastOffset: 0,
|
||||
outFallOffset: 0,
|
||||
offsetAzimuth: 0,
|
||||
areaIntensity: '-',
|
||||
offsetStopAngle: 0,
|
||||
zenithIntensity: 0,
|
||||
comment: '-',
|
||||
depthPlanFactDifference: 0,
|
||||
distancePlanFactDifference: 0
|
||||
}
|
||||
}
|
||||
|
@ -1,81 +1,83 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { Table, formatDate, makeDateSorter, makeNumericSorter} from "../../components/Table"
|
||||
import { Button, Tooltip } from "antd"
|
||||
import { FilePdfOutlined, FileTextOutlined} from '@ant-design/icons'
|
||||
import { ReportService } from "../../services/api"
|
||||
import { invokeWebApiWrapperAsync, downloadFile, formatTimespan} from "../../components/factory"
|
||||
import LoaderPortal from "../../components/LoaderPortal"
|
||||
import moment from "moment"
|
||||
import { Button, Tooltip } from 'antd'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { FilePdfOutlined, FileTextOutlined } from '@ant-design/icons'
|
||||
|
||||
import { ReportService } from '../../services/api'
|
||||
import { formatDate, periodToString } from '../../utils'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { Table, makeDateSorter, makeNumericSorter } from '../../components/Table'
|
||||
import { invokeWebApiWrapperAsync, downloadFile } from '../../components/factory'
|
||||
|
||||
const imgPaths = {
|
||||
".pdf": <FilePdfOutlined/>,
|
||||
".las": <FileTextOutlined />,
|
||||
'.pdf': <FilePdfOutlined/>,
|
||||
'.las': <FileTextOutlined/>,
|
||||
}
|
||||
|
||||
const columns = [
|
||||
const columns = [
|
||||
{
|
||||
title: "Название",
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
title: 'Название',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (name, report) => (
|
||||
<Button
|
||||
type="link"
|
||||
type={'link'}
|
||||
icon={imgPaths[report.format]}
|
||||
onClick={(_) => downloadFile(report.file)}
|
||||
onClick={() => downloadFile(report.file)}
|
||||
download={name}
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <Tooltip title="Дата формирования">Сформирован</Tooltip>,
|
||||
dataIndex: "date",
|
||||
key: "date",
|
||||
}, {
|
||||
title: <Tooltip title={'Дата формирования'}>Сформирован</Tooltip>,
|
||||
dataIndex: 'date',
|
||||
key: 'date',
|
||||
sorter: makeDateSorter('date'),
|
||||
render: date=>moment(date).format(formatDate),
|
||||
},
|
||||
{
|
||||
title: <Tooltip title="Дата начала периода рапорта">С</Tooltip>,
|
||||
dataIndex: "begin",
|
||||
key: "begin",
|
||||
render: (date) => formatDate(date),
|
||||
}, {
|
||||
title: <Tooltip title={'Дата начала периода рапорта'}>С</Tooltip>,
|
||||
dataIndex: 'begin',
|
||||
key: 'begin',
|
||||
sorter: makeDateSorter('begin'),
|
||||
render: date=>moment(date).format(formatDate),
|
||||
},
|
||||
{
|
||||
title: <Tooltip title="Дата окончания периода рапорта">По</Tooltip>,
|
||||
dataIndex: "end",
|
||||
key: "end",
|
||||
render: (date) => formatDate(date),
|
||||
}, {
|
||||
title: <Tooltip title={'Дата окончания периода рапорта'}>По</Tooltip>,
|
||||
dataIndex: 'end',
|
||||
key: 'end',
|
||||
sorter: makeDateSorter('end'),
|
||||
render: date=>moment(date).format(formatDate),
|
||||
},
|
||||
{
|
||||
title: <Tooltip title="шаг сетки графиков">шаг, сек</Tooltip>,
|
||||
dataIndex: "step",
|
||||
key: "step",
|
||||
|
||||
render: (date) => formatDate(date),
|
||||
}, {
|
||||
title: <Tooltip title={'шаг сетки графиков'}>шаг, сек</Tooltip>,
|
||||
dataIndex: 'step',
|
||||
key: 'step',
|
||||
sorter: makeNumericSorter('step'),
|
||||
|
||||
render: step => formatTimespan(step)
|
||||
render: step => periodToString(step),
|
||||
},
|
||||
]
|
||||
|
||||
export const Reports = ({idWell}) => {
|
||||
export const Reports = ({ idWell }) => {
|
||||
const [reports, setReports] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
useEffect(()=>invokeWebApiWrapperAsync(async()=>{
|
||||
const reportsResponse = await ReportService.getAllReportsNamesByWell(idWell)
|
||||
const reports = reportsResponse.map(r => ({...r, key: r.id?? r.name?? r.date}))
|
||||
setReports(reports)
|
||||
},setShowLoader), [idWell])
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const reportsResponse = await ReportService.getAllReportsNamesByWell(idWell)
|
||||
const reports = reportsResponse.map(r => ({ ...r, key: r.id ?? r.name ?? r.date }))
|
||||
setReports(reports)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список рапортов по скважине "${idWell}"`
|
||||
), [idWell])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<Table
|
||||
size='small' bordered
|
||||
<Table
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={reports}
|
||||
pagination={{pageSize:13}}/>
|
||||
</LoaderPortal>)
|
||||
}
|
||||
pagination={{ pageSize: 13 }}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
||||
|
@ -1,63 +1,55 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { DatePicker, Radio, Button, Select, notification } from "antd"
|
||||
import "moment/locale/ru"
|
||||
import moment from "moment"
|
||||
import { Subscribe } from "../../services/signalr"
|
||||
import LoaderPortal from "../../components/LoaderPortal"
|
||||
import { ReportService } from "../../services/api"
|
||||
import { ReportCreationNotify } from "./ReportCreationNotify"
|
||||
import { invokeWebApiWrapperAsync, notify } from "../../components/factory"
|
||||
import { Reports } from "./Reports"
|
||||
import 'moment/locale/ru'
|
||||
import moment from 'moment'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { DatePicker, Radio, Button, Select, notification } from 'antd'
|
||||
|
||||
const { RangePicker } = DatePicker
|
||||
const { Option } = Select
|
||||
import { ReportService } from '../../services/api'
|
||||
import { Subscribe } from '../../services/signalr'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
|
||||
import { Reports } from './Reports'
|
||||
import { ReportCreationNotify } from './ReportCreationNotify'
|
||||
|
||||
const timePeriodNames = [
|
||||
{ label: "1 секунда", value: 1 },
|
||||
{ label: "10 секунд", value: 10 },
|
||||
{ label: "1 минута", value: 60 },
|
||||
{ label: "5 минут", value: 300 },
|
||||
{ label: "30 минут", value: 1800 },
|
||||
{ label: "1 час", value: 3600 },
|
||||
{ label: "6 часов", value: 21600 },
|
||||
{ label: "12 часов", value: 43200 },
|
||||
{ label: "1 день", value: 86400 },
|
||||
{ label: "1 неделя", value: 604800 },
|
||||
{ label: '1 секунда', value: 1 },
|
||||
{ label: '10 секунд', value: 10 },
|
||||
{ label: '1 минута', value: 60 },
|
||||
{ label: '5 минут', value: 300 },
|
||||
{ label: '30 минут', value: 1800 },
|
||||
{ label: '1 час', value: 3600 },
|
||||
{ label: '6 часов', value: 21600 },
|
||||
{ label: '12 часов', value: 43200 },
|
||||
{ label: '1 день', value: 86400 },
|
||||
{ label: '1 неделя', value: 604800 },
|
||||
]
|
||||
|
||||
const dateTimeFormat = "DD.MM.YYYY hh:mm:ss"
|
||||
const dateTimeFormat = 'DD.MM.YYYY hh:mm:ss'
|
||||
|
||||
const reportFormats = [
|
||||
{ value: 0, label: 'PDF' },
|
||||
{ value: 1, label: 'LAS' },
|
||||
]
|
||||
|
||||
export default function Report({ idWell }) {
|
||||
const [aviableDateRange, setAviableDateRange] = useState([
|
||||
moment(),
|
||||
moment(),
|
||||
])
|
||||
const [aviableDateRange, setAviableDateRange] = useState([moment(), moment()])
|
||||
const [filterDateRange, setFilterDateRange] = useState([
|
||||
moment().subtract(1, "days").startOf("day"),
|
||||
moment().startOf("day"),
|
||||
moment().subtract(1, 'days').startOf('day'),
|
||||
moment().startOf('day'),
|
||||
])
|
||||
const [step, setStep] = useState(timePeriodNames[2].value)
|
||||
const [format, setFormat] = useState(0)
|
||||
const [pagesCount, setPagesCount] = useState(0)
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const periodOptions = timePeriodNames.map((item) => (
|
||||
<Option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</Option>
|
||||
))
|
||||
|
||||
const handleReportCreation = async () => {
|
||||
let begin = filterDateRange[0].toISOString()
|
||||
let end = filterDateRange[1].toISOString()
|
||||
|
||||
try {
|
||||
const handleReportCreation = async () => await invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const taskId = await ReportService.createReport(
|
||||
idWell,
|
||||
step,
|
||||
format,
|
||||
begin,
|
||||
end
|
||||
filterDateRange[0].toISOString(),
|
||||
filterDateRange[1].toISOString()
|
||||
)
|
||||
if (!taskId) return
|
||||
|
||||
@ -65,12 +57,8 @@ export default function Report({ idWell }) {
|
||||
if (progressData) {
|
||||
notification.open({
|
||||
key: taskId,
|
||||
message: "Создание отчета:",
|
||||
description: (
|
||||
<ReportCreationNotify
|
||||
progressData={progressData}
|
||||
></ReportCreationNotify>
|
||||
),
|
||||
message: 'Создание отчета:',
|
||||
description: <ReportCreationNotify progressData={progressData} />,
|
||||
duration: 0,
|
||||
onClose: unSubscribeReportHub,
|
||||
})
|
||||
@ -81,82 +69,68 @@ export default function Report({ idWell }) {
|
||||
}
|
||||
|
||||
const unSubscribeReportHub = Subscribe(
|
||||
"hubs/reports",
|
||||
"GetReportProgress",
|
||||
'hubs/reports',
|
||||
'GetReportProgress',
|
||||
`Report_${taskId}`,
|
||||
handleReportProgress
|
||||
)
|
||||
} catch (error) {
|
||||
notify(
|
||||
`Не удалось создать отчет по скважине (${idWell}) c
|
||||
${filterDateRange[0].format("DD.MM.YYYY hh:mm:ss")} по
|
||||
${filterDateRange[1].format("DD.MM.YYYY hh:mm:ss")}`,
|
||||
"error"
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось создать отчет по скважине (${idWell}) c
|
||||
${filterDateRange[0].format(dateTimeFormat)} по
|
||||
${filterDateRange[1].format(dateTimeFormat)}`
|
||||
)
|
||||
|
||||
const disabledDate = (current) => !current.isBetween(aviableDateRange[0], aviableDateRange[1], 'seconds', '[]')
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const datesRangeResponse = await ReportService.getReportsDateRange(idWell)
|
||||
if (!datesRangeResponse?.from || !datesRangeResponse.to)
|
||||
throw new Error('Формат ответа неверный!')
|
||||
|
||||
const datesRange = [
|
||||
moment(datesRangeResponse.from),
|
||||
moment(datesRangeResponse.to),
|
||||
]
|
||||
setAviableDateRange(datesRange)
|
||||
|
||||
const from = moment.max(moment(datesRange[1]).subtract(1, 'days'), datesRange[0])
|
||||
|
||||
setFilterDateRange([
|
||||
from.startOf('day'),
|
||||
moment(datesRange[1]).startOf('day'),
|
||||
])
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось получить диапозон дат рапортов для скважины "${idWell}"`
|
||||
), [idWell])
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
if (filterDateRange?.length !== 2) return
|
||||
const pagesCount = await ReportService.getReportSize(
|
||||
idWell,
|
||||
step,
|
||||
format,
|
||||
filterDateRange[0].toISOString(),
|
||||
filterDateRange[1].toISOString()
|
||||
)
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
const disabledDate = (current) => {
|
||||
return current < aviableDateRange[0] || current > aviableDateRange[1]
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
invokeWebApiWrapperAsync(async () => {
|
||||
const datesRangeResponse = await ReportService.getReportsDateRange(
|
||||
idWell
|
||||
)
|
||||
const datesRange = [
|
||||
moment(datesRangeResponse.from),
|
||||
moment(datesRangeResponse.to),
|
||||
]
|
||||
setAviableDateRange(datesRange)
|
||||
|
||||
let from = moment(datesRangeResponse.to)
|
||||
from = from.subtract(1, "days")
|
||||
if (from < datesRange[0]) from = datesRange[0]
|
||||
|
||||
const filterDateDefaults = [
|
||||
from.startOf("day"),
|
||||
moment(datesRangeResponse.to).startOf("day"),
|
||||
]
|
||||
setFilterDateRange(filterDateDefaults)
|
||||
}, setShowLoader),
|
||||
[idWell]
|
||||
)
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
if (!filterDateRange || filterDateRange.length < 2) return
|
||||
const begin = filterDateRange[0].toISOString()
|
||||
const end = filterDateRange[1].toISOString()
|
||||
const pagesCount = await ReportService.getReportSize(
|
||||
idWell,
|
||||
step,
|
||||
format,
|
||||
begin,
|
||||
end
|
||||
)
|
||||
setPagesCount(pagesCount)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось получить предварительные параметры отчета c
|
||||
${filterDateRange[0].format(dateTimeFormat)} по
|
||||
${filterDateRange[1].format(dateTimeFormat)}`
|
||||
),
|
||||
[filterDateRange, step, format, idWell]
|
||||
)
|
||||
setPagesCount(pagesCount)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось получить предварительные параметры отчета c
|
||||
${filterDateRange[0].format(dateTimeFormat)} по
|
||||
${filterDateRange[1].format(dateTimeFormat)}`
|
||||
), [filterDateRange, step, format, idWell])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LoaderPortal show={showLoader}>
|
||||
<div className="w-100 mt-20px mb-20px d-flex">
|
||||
<div className={'w-100 mt-20px mb-20px d-flex'}>
|
||||
<div>
|
||||
<div>Диапазон дат отчета</div>
|
||||
<RangePicker
|
||||
<DatePicker.RangePicker
|
||||
disabledDate={disabledDate}
|
||||
allowClear={false}
|
||||
onCalendarChange={setFilterDateRange}
|
||||
@ -164,38 +138,28 @@ export default function Report({ idWell }) {
|
||||
value={filterDateRange}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-30px">
|
||||
<div className={'ml-30px'}>
|
||||
<div>Шаг графиков</div>
|
||||
<Select style={{ width: "8rem" }}
|
||||
onChange={e=>{
|
||||
setStep(e)
|
||||
}}
|
||||
value={step}>
|
||||
{periodOptions}
|
||||
</Select>
|
||||
<Select
|
||||
style={{ width: '8rem' }}
|
||||
onChange={e => setStep(e)}
|
||||
value={step}
|
||||
options={timePeriodNames}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="ml-30px">
|
||||
<div className={'ml-30px'}>
|
||||
<div>Формат отчета</div>
|
||||
<Radio.Group
|
||||
onChange={(e) => setFormat(e.target.value)}
|
||||
options={reportFormats}
|
||||
value={format}
|
||||
>
|
||||
<Radio.Button value={0}>PDF</Radio.Button>
|
||||
<Radio.Button value={1}>LAS</Radio.Button>
|
||||
</Radio.Group>
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-30px">
|
||||
<div className={'ml-30px'}>
|
||||
<div> </div>
|
||||
<Button type="primary" onClick={handleReportCreation}>
|
||||
<span>Получить рапорт</span>
|
||||
<span className={"ml-5px"}>({pagesCount} стр.)</span>
|
||||
<span
|
||||
style={{ display: pagesCount > 100 ? "inline" : "none" }}
|
||||
className={"ml-5px"}
|
||||
>
|
||||
!!!
|
||||
</span>
|
||||
<Button type={'primary'} onClick={handleReportCreation}>
|
||||
Получить рапорт ({pagesCount} стр.) {pagesCount > 100 && '!!!'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,10 +1,10 @@
|
||||
import moment from 'moment'
|
||||
import { memo } from 'react'
|
||||
import { Modal, Input } from 'antd'
|
||||
|
||||
import { Table } from '../../components/Table'
|
||||
import { UserView } from '../../components/Views'
|
||||
import { Grid, GridItem } from '../../components/Grid'
|
||||
import { periodToString } from '../../utils/datetime'
|
||||
import { formatDate, periodToString } from '../../utils'
|
||||
|
||||
export const setpointStatus = {
|
||||
0: 'Неизвестно',
|
||||
@ -16,9 +16,9 @@ export const setpointStatus = {
|
||||
}
|
||||
|
||||
export const getSetpointStatus = (id) => {
|
||||
if (!id || Object.keys(setpointStatus).every((idx) => Number(idx) !== id))
|
||||
return setpointStatus[0]
|
||||
return setpointStatus[id]
|
||||
if (id && Object.keys(setpointStatus).some((idx) => Number(idx) === id))
|
||||
return setpointStatus[id]
|
||||
return setpointStatus[0]
|
||||
}
|
||||
|
||||
const columns = [
|
||||
@ -27,14 +27,11 @@ const columns = [
|
||||
]
|
||||
|
||||
export const SetpointViewer = memo(({ setpoint, visible, onClose, setpointNames }) => {
|
||||
let date = moment(setpoint?.uploadDate).format('DD MMM YYYY, HH:mm:ss')
|
||||
let setpoints = []
|
||||
if (setpoint) {
|
||||
setpoints = Object.keys(setpoint?.setpoints).map((name) => ({
|
||||
name: setpointNames.find(spName => spName.value === name)?.label,
|
||||
value: setpoint.setpoints[name]
|
||||
}))
|
||||
}
|
||||
const date = formatDate(setpoint?.uploadDate)
|
||||
const setpoints = Object.keys(setpoint?.setpoints ?? []).map((name) => ({
|
||||
name: setpointNames.find(spName => spName.value === name)?.label,
|
||||
value: setpoint?.setpoints?.[name]
|
||||
}))
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -55,7 +52,7 @@ export const SetpointViewer = memo(({ setpoint, visible, onClose, setpointNames
|
||||
<GridItem row={3} col={2}>{getSetpointStatus(setpoint?.idState)}</GridItem>
|
||||
|
||||
<GridItem row={4} col={1}>Период актуальности рекомендаций:</GridItem>
|
||||
<GridItem row={4} col={2}>{periodToString(setpoint?.obsolescenceSec)}</GridItem>
|
||||
<GridItem row={4} col={2}>{periodToString(setpoint?.obsolescenceSec * 1000)}</GridItem>
|
||||
|
||||
<GridItem row={5} col={1}>Комментарий:</GridItem>
|
||||
<GridItem row={6} col={1} colSpan={3}>
|
||||
|
@ -122,13 +122,13 @@ export const WellCompositeSections = ({idWell, statsWells, selectedSections}) =>
|
||||
(text, item) => <Link to={`/well/${item?.id}`}>{text ?? '-'}</Link>
|
||||
),
|
||||
makeTextColumn('Секция', 'sectionType', filtersSectionsType, null, (text) => text ?? '-'),
|
||||
makeNumericColumnPlanFact('Глубина', 'sectionWellDepth', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Продолжительность', 'sectionBuildDays', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('МСП', 'sectionRateOfPenetration', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Рейсовая скорость', 'sectionRouteSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Спуск КНБК', 'sectionBhaDownSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Подъем КНБК', 'sectionBhaUpSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Скорость спуска ОК', 'sectionCasingDownSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Глубина, м', 'sectionWellDepth', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Продолжительность, ч', 'sectionBuildDays', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('МСП, м/ч', 'sectionRateOfPenetration', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Рейсовая скорость, м/ч', 'sectionRouteSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Спуск КНБК, м/ч', 'sectionBhaDownSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Подъем КНБК, м/ч', 'sectionBhaUpSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('Скорость спуска ОК, м/ч', 'sectionCasingDownSpeed', filtersMinMax, makeFilterMinMaxFunction),
|
||||
makeNumericColumnPlanFact('НПВ, сут', 'nonProductiveTime', filtersMinMax, makeFilterMinMaxFunction, null, '70px'),
|
||||
{
|
||||
title: 'TVD',
|
||||
|
@ -1,31 +1,28 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
import {
|
||||
makeColumn,
|
||||
makeNumericAvgRange
|
||||
} from '../../components/Table'
|
||||
import { DrillParamsService } from '../../services/api'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import { EditableTable, SelectFromDictionary } from '../../components/Table'
|
||||
import { DrillParamsService } from '../../services/api'
|
||||
import { makeActionHandler, makeColumn, makeNumericAvgRange } from '../../components/Table'
|
||||
|
||||
import { dictionarySectionType, getByKeyOrReturnKey } from './dictionary'
|
||||
|
||||
export const columns = [
|
||||
makeColumn('Конструкция секции','idWellSectionType', {
|
||||
editable:true,
|
||||
input:<SelectFromDictionary dictionary={dictionarySectionType}/>,
|
||||
width:160,
|
||||
render:(_, record)=>getByKeyOrReturnKey(dictionarySectionType, record.idWellSectionType)
|
||||
editable: true,
|
||||
input: <SelectFromDictionary dictionary={dictionarySectionType}/>,
|
||||
width: 160,
|
||||
render: (_, record) => getByKeyOrReturnKey(dictionarySectionType, record.idWellSectionType)
|
||||
}),
|
||||
// makeNumericStartEnd('Глубина', 'depth'),
|
||||
makeNumericAvgRange('Нагрузка', 'axialLoad', 1),
|
||||
makeNumericAvgRange('Давление', 'pressure', 1),
|
||||
makeNumericAvgRange('Момент на ВСП', 'rotorTorque', 1),
|
||||
makeNumericAvgRange('Обороты на ВСП', 'rotorSpeed', 1),
|
||||
makeNumericAvgRange('Расход', 'flow', 1)
|
||||
makeNumericAvgRange('Нагрузка, т', 'axialLoad', 1),
|
||||
makeNumericAvgRange('Давление, атм', 'pressure', 1),
|
||||
makeNumericAvgRange('Момент на ВСП, кН·м', 'rotorTorque', 1),
|
||||
makeNumericAvgRange('Обороты на ВСП, об/мин', 'rotorSpeed', 1),
|
||||
makeNumericAvgRange('Расход, л/с', 'flow', 1)
|
||||
]
|
||||
|
||||
export const WellDrillParams = ({idWell}) => {
|
||||
export const WellDrillParams = ({ idWell }) => {
|
||||
const [params, setParams] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
@ -40,24 +37,15 @@ export const WellDrillParams = ({idWell}) => {
|
||||
|
||||
useEffect(updateParams, [idWell])
|
||||
|
||||
const onAdd = async (param) => {
|
||||
param.idWell = idWell
|
||||
await DrillParamsService.insert(idWell, param)
|
||||
updateParams()
|
||||
const handlerProps = {
|
||||
service: DrillParamsService,
|
||||
setLoader: setShowLoader,
|
||||
errorMsg: `Не удалось выполнить операцию`,
|
||||
onComplete: updateParams,
|
||||
idWell
|
||||
}
|
||||
|
||||
const onEdit = async (param) => {
|
||||
if (!param.id) return
|
||||
param.idWell = idWell
|
||||
await DrillParamsService.update(idWell, param.id, param)
|
||||
updateParams()
|
||||
}
|
||||
|
||||
const onDelete = async (param) => {
|
||||
if (!param.id) return
|
||||
await DrillParamsService.delete(idWell, param.id)
|
||||
updateParams()
|
||||
}
|
||||
const recordParser = (record) => ({ ...record, idWell })
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
@ -66,9 +54,9 @@ export const WellDrillParams = ({idWell}) => {
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={params}
|
||||
onRowAdd={onAdd}
|
||||
onRowEdit={onEdit}
|
||||
onRowDelete={onDelete}
|
||||
onRowAdd={makeActionHandler('insert', handlerProps, recordParser)}
|
||||
onRowEdit={makeActionHandler('update', handlerProps, recordParser)}
|
||||
onRowDelete={makeActionHandler('delete', handlerProps, recordParser)}
|
||||
pagination={false}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
|
@ -1,48 +1,82 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Input } from 'antd'
|
||||
import moment from 'moment'
|
||||
import {
|
||||
EditableTable,
|
||||
DatePickerWrapper,
|
||||
SelectFromDictionary,
|
||||
makeColumn,
|
||||
import { Input } from 'antd'
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
import {
|
||||
EditableTable,
|
||||
DatePickerWrapper,
|
||||
makeColumn,
|
||||
makeDateSorter,
|
||||
makeNumericColumnOptions} from "../../components/Table"
|
||||
makeNumericColumnOptions,
|
||||
makeSelectColumn,
|
||||
makeActionHandler,
|
||||
} from '../../components/Table'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import { WellOperationService} from '../../services/api'
|
||||
import { dictionarySectionType, getByKeyOrReturnKey } from './dictionary'
|
||||
import { formatDate } from '../../utils'
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { TextArea } = Input
|
||||
|
||||
const basePageSize = 160;
|
||||
const format='YYYY.MM.DD HH:mm'
|
||||
const basePageSize = 160
|
||||
|
||||
const defaultColumns = [
|
||||
makeSelectColumn('Конструкция секции', 'idWellSectionType', [], undefined, { editable: true, width: 160 }),
|
||||
makeSelectColumn('Операция', 'idCategory', [], undefined, { editable: true, width: 200 }),
|
||||
makeColumn('Доп. инфо', 'categoryInfo', { editable: true, width: 300, input: <TextArea/> }),
|
||||
makeColumn('Глубина забоя на начало, м', 'depthStart', makeNumericColumnOptions(2, 'depthStart')),
|
||||
makeColumn('Глубина забоя при завершении, м', 'depthEnd', makeNumericColumnOptions(2, 'depthEnd')),
|
||||
makeColumn('Время начала', 'dateStart', {
|
||||
editable: true,
|
||||
width: 200,
|
||||
input: <DatePickerWrapper/>,
|
||||
initialValue: moment().format(),
|
||||
sorter: makeDateSorter('dateStart'),
|
||||
render: (_, record) => (
|
||||
<div className={'text-align-r-container'}>
|
||||
<span>{formatDate(record.dateStart)}</span>
|
||||
</div>
|
||||
)
|
||||
}),
|
||||
makeColumn('Часы', 'durationHours', makeNumericColumnOptions(2, 'durationHours')),
|
||||
makeColumn('Комментарий', 'comment', { editable: true, input: <TextArea/> }),
|
||||
]
|
||||
|
||||
export const WellOperationsEditor = ({idWell, idType}) => {
|
||||
const [pageNumAndPageSize, setPageNumAndPageSize] = useState({current:1, pageSize:basePageSize})
|
||||
const [paginationTotal, setPaginationTotal] = useState(0)
|
||||
const [dictionaryOperationCategory, setDictionaryOperationCategory] = useState(new Map())
|
||||
const [operations, setOperations] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const [columns, setColumns] = useState(defaultColumns)
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const categories = await WellOperationService.getCategories(idWell)
|
||||
const dictCategories = new Map()
|
||||
categories?.forEach((item) => dictCategories.set(item.id, item.name))
|
||||
setDictionaryOperationCategory(dictCategories)
|
||||
}),[idWell])
|
||||
let categories = await WellOperationService.getCategories(idWell) ?? []
|
||||
categories = categories.map((item) => ({ value: item.id, label: item.name }))
|
||||
|
||||
let sectionTypes = await WellOperationService.getSectionTypes(idWell) ?? []
|
||||
sectionTypes = Object.keys(sectionTypes).map((key) => ({ value: parseInt(key), label: sectionTypes[key] }))
|
||||
|
||||
setColumns(preColumns => {
|
||||
preColumns[0] = makeSelectColumn('Конструкция секции', 'idWellSectionType', sectionTypes, undefined, { editable: true, width: 160 })
|
||||
preColumns[1] = makeSelectColumn('Операция', 'idCategory', categories, undefined, { editable: true, width: 200 })
|
||||
return preColumns
|
||||
})
|
||||
},
|
||||
setShowLoader,
|
||||
'Не удалось загрузить список операций по скважине'
|
||||
), [idWell])
|
||||
|
||||
const updateOperations = () => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const skip = ((pageNumAndPageSize.current - 1) * pageNumAndPageSize.pageSize) || 0
|
||||
const take = pageNumAndPageSize.pageSize
|
||||
const paginatedOperations = await WellOperationService.getOperations(idWell,
|
||||
idType, undefined, undefined, undefined, undefined,
|
||||
const paginatedOperations = await WellOperationService.getOperations(idWell,
|
||||
idType, undefined, undefined, undefined, undefined,
|
||||
undefined, undefined, skip, take )
|
||||
const operations = paginatedOperations?.items ?? []
|
||||
setOperations(operations)
|
||||
const total = paginatedOperations.count?? paginatedOperations.items?.length ?? 0
|
||||
const total = paginatedOperations.count?? paginatedOperations.items?.length ?? 0
|
||||
setPaginationTotal(total)
|
||||
},
|
||||
setShowLoader,
|
||||
@ -51,78 +85,39 @@ export const WellOperationsEditor = ({idWell, idType}) => {
|
||||
|
||||
useEffect(updateOperations, [idWell, idType, pageNumAndPageSize])
|
||||
|
||||
const columns = [
|
||||
makeColumn('Конструкция секции','idWellSectionType', {
|
||||
editable:true,
|
||||
input:<SelectFromDictionary dictionary={dictionarySectionType}/>,
|
||||
width:160,
|
||||
render:(_, record)=>getByKeyOrReturnKey(dictionarySectionType, record.idWellSectionType)
|
||||
}),
|
||||
makeColumn('Операция','idCategory', {
|
||||
editable:true,
|
||||
input:<SelectFromDictionary dictionary={dictionaryOperationCategory}/>,
|
||||
width:200,
|
||||
render:(_, record)=>getByKeyOrReturnKey(dictionaryOperationCategory, record.idCategory)
|
||||
}),
|
||||
makeColumn('Доп. инфо','categoryInfo', {editable:true, width:300, input:<TextArea/>}),
|
||||
makeColumn('Глубина забоя на начало','depthStart', makeNumericColumnOptions(2, 'depthStart')),
|
||||
makeColumn('Глубина забоя при завершении','depthEnd', makeNumericColumnOptions(2, 'depthEnd')),
|
||||
makeColumn('Время начала','dateStart', {
|
||||
editable:true,
|
||||
width:200,
|
||||
input:<DatePickerWrapper/>,
|
||||
initialValue:moment().format(),
|
||||
sorter: makeDateSorter('dateStart'),
|
||||
render:(_, record) =>
|
||||
<div className={'text-align-r-container'}>
|
||||
<span>{moment.utc(record.dateStart).local().format(format)}</span>
|
||||
</div>
|
||||
}),
|
||||
makeColumn('Часы','durationHours', makeNumericColumnOptions(2, 'durationHours')),
|
||||
makeColumn('Комментарий','comment', {editable:true, input:<TextArea/>}),
|
||||
]
|
||||
|
||||
const onAdd = async (operation) => {
|
||||
operation.idType = idType
|
||||
operation.wellDepth = +operation.wellDepth
|
||||
operation.durationHours = +operation.durationHours
|
||||
await WellOperationService.insertRange(idWell, [operation])
|
||||
updateOperations()
|
||||
const handlerProps = {
|
||||
service: WellOperationService,
|
||||
setLoader: setShowLoader,
|
||||
errorMsg: `Не удалось выполнить операцию`,
|
||||
onComplete: updateOperations,
|
||||
idWell
|
||||
}
|
||||
|
||||
const onEdit= async (operation) => {
|
||||
if(!operation.id)
|
||||
return
|
||||
operation.idType = idType
|
||||
operation.wellDepth = +operation.wellDepth
|
||||
operation.durationHours = +operation.durationHours
|
||||
await WellOperationService.update(idWell, operation.id, operation)
|
||||
updateOperations()
|
||||
}
|
||||
const recordParser = (record) => ({
|
||||
...record,
|
||||
idType,
|
||||
wellDepth: +record.wellDepth,
|
||||
durationHours: +record.durationHours
|
||||
})
|
||||
|
||||
const onDelete= async (operation) => {
|
||||
if(!operation.id)
|
||||
return
|
||||
await WellOperationService.delete(idWell, operation.id)
|
||||
updateOperations()
|
||||
}
|
||||
|
||||
return <LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size='small'
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={operations}
|
||||
onRowAdd={onAdd}
|
||||
onRowEdit={onEdit}
|
||||
onRowDelete={onDelete}
|
||||
pagination={{
|
||||
current: pageNumAndPageSize.current,
|
||||
pageSize: pageNumAndPageSize.pageSize,
|
||||
showSizeChanger: false,
|
||||
total: paginationTotal,
|
||||
onChange: (page, pageSize) => setPageNumAndPageSize({current: page, pageSize: pageSize})
|
||||
}}
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={operations}
|
||||
onRowAdd={makeActionHandler('insertRange', handlerProps, recordParser)}
|
||||
onRowEdit={makeActionHandler('update', handlerProps, recordParser)}
|
||||
onRowDelete={makeActionHandler('delete', handlerProps)}
|
||||
pagination={{
|
||||
current: pageNumAndPageSize.current,
|
||||
pageSize: pageNumAndPageSize.pageSize,
|
||||
showSizeChanger: false,
|
||||
total: paginationTotal,
|
||||
onChange: (page, pageSize) => setPageNumAndPageSize({ current: page, pageSize })
|
||||
}}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
}
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
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'
|
||||
|
||||
const makeNumberRender = value => {
|
||||
const v = +value
|
||||
@ -13,23 +15,15 @@ const makeNumberRender = value => {
|
||||
|
||||
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: 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 }),
|
||||
]
|
||||
|
||||
const calcDuration = (start, end) => {
|
||||
const msInDay = 1000 * 60 * 60 * 24
|
||||
const startD = new Date(start)
|
||||
const endD = new Date(end)
|
||||
const ms = endD - startD
|
||||
return ms / msInDay
|
||||
}
|
||||
|
||||
export const WellSectionsStat = ({idWell}) => {
|
||||
const [sections, setSections] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
@ -34,3 +34,34 @@ input.measure-input {
|
||||
.ml-10px {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.v-div {
|
||||
display: flex;
|
||||
height: 180px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.v-span {
|
||||
white-space: pre;
|
||||
vertical-align: center;
|
||||
text-align: center;
|
||||
|
||||
-webkit-transform: rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.measure-column-header {
|
||||
border: 1px solid lightgrey;
|
||||
}
|
||||
|
||||
.measure-column-value {
|
||||
border: 1px solid lightgrey;
|
||||
justify-content: right;
|
||||
margin-right: 16px;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
export type RawDate = number | string | Date
|
||||
|
||||
export function isRawDate(value: unknown): value is RawDate {
|
||||
return isNaN(new Date(value as RawDate).getTime())
|
||||
}
|
@ -1,10 +1,44 @@
|
||||
export const periodToString = (time?: number) => {
|
||||
if (!time) return '00:00:00'
|
||||
const hours = Math.floor(time / 3600)
|
||||
const minutes = Math.floor(time / 60 - hours * 60)
|
||||
const seconds = Math.floor(time - hours * 3600 - minutes * 60)
|
||||
import moment from 'moment'
|
||||
|
||||
const toFixed = (num: number) => `${num}`.padStart(2, '0')
|
||||
export type RawDate = number | string | Date
|
||||
|
||||
return `${toFixed(hours)}:${toFixed(minutes)}:${toFixed(seconds)}`
|
||||
export const defaultFormat: string = 'YYYY.MM.DD HH:mm'
|
||||
|
||||
export enum timeInS {
|
||||
millisecond = 0.001,
|
||||
second = 1,
|
||||
minute = second * 60,
|
||||
hour = minute * 60,
|
||||
day = hour * 24,
|
||||
week = day * 7,
|
||||
}
|
||||
|
||||
export function isRawDate(value: unknown): value is RawDate {
|
||||
return !isNaN(Date.parse(String(value)))
|
||||
}
|
||||
|
||||
export const formatDate = (date: unknown, utc = false, format = defaultFormat) => {
|
||||
if (!isRawDate(date)) return null
|
||||
const out = utc ? moment.utc(date).local() : moment(date)
|
||||
return out.format(format)
|
||||
}
|
||||
|
||||
export const periodToString = (time?: number) => {
|
||||
if (!time || time <= 0) return '00:00:00'
|
||||
const days = Math.floor(time / timeInS.day)
|
||||
time %= timeInS.day
|
||||
const hours = Math.floor(time / timeInS.hour)
|
||||
time %= timeInS.hour
|
||||
const minutes = Math.floor(time / timeInS.minute)
|
||||
time %= timeInS.minute
|
||||
const seconds = Math.floor(time / timeInS.second)
|
||||
|
||||
const toFixed = (num: number) => String(num).padStart(2, '0')
|
||||
|
||||
return `${days > 0 ? days : ''} ${toFixed(hours)}:${toFixed(minutes)}:${toFixed(seconds)}`
|
||||
}
|
||||
|
||||
export const calcDuration = (start: RawDate, end: RawDate) => {
|
||||
if (!isRawDate(start) || !isRawDate(end)) return
|
||||
return (+new Date(end) - +new Date(start)) * timeInS.millisecond / timeInS.day
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export type { RawDate } from './DateTimeUtils'
|
||||
export { isRawDate } from './DateTimeUtils'
|
||||
export type { RawDate, timeInS } from './datetime'
|
||||
export { isRawDate, formatDate, defaultFormat, periodToString } from './datetime'
|
||||
|
||||
export const headerHeight = 64
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user