forked from ddrilling/asb_cloud_front
На вкладку секции, режимы добавлены единицы измерения
Мелкий рефракторинг
This commit is contained in:
parent
528b7e4f7b
commit
c327240ea2
@ -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))
|
||||
}
|
||||
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
|
||||
})
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { WellIcon, WellIconState } from './icons/WellIcon'
|
||||
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/'
|
||||
|
||||
export const getWellState = (idState?: number): WellIconState => idState === 1 ? 'active' : 'unknown'
|
||||
export const checkIsWellOnline = (lastTelemetryDate?: RawDate): boolean => {
|
||||
|
@ -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
|
||||
}
|
@ -119,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',
|
||||
|
@ -2,8 +2,8 @@ import { Modal } from 'antd'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
|
||||
import { Table } from '../../components/Table'
|
||||
import { formatDate } from '../../utils'
|
||||
|
||||
import { formatDate } from './MeasureTable'
|
||||
import { v } from './columnsCommon'
|
||||
|
||||
const tableScroll = { y: 400, x: 1300 }
|
||||
@ -12,7 +12,7 @@ const dateColumn = {
|
||||
title: 'Дата',
|
||||
key: 'date',
|
||||
dataIndex: 'date',
|
||||
render: formatDate,
|
||||
render: (date) => formatDate(date, true) ?? 'Нет данных',
|
||||
fixed: 'left',
|
||||
width: '8rem',
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import moment from 'moment'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button, Form, Input, Popconfirm, Timeline } from 'antd'
|
||||
import {
|
||||
@ -18,9 +17,7 @@ import { View } from './View'
|
||||
|
||||
import '../../styles/index.css'
|
||||
import '../../styles/measure.css'
|
||||
|
||||
export const formatDate = (date) => date ?
|
||||
moment.utc(date).local().format('YYYY.MM.DD HH:mm') : 'Нет данных'
|
||||
import { formatDate } from '../../utils'
|
||||
|
||||
const createEditingColumns = (cols, renderDelegate) =>
|
||||
cols.map(col => ({ render: renderDelegate, ...col }))
|
||||
@ -123,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>
|
||||
@ -145,8 +142,8 @@ export const MeasureTable = ({idWell, group, updateMeasuresFunc, additionalButto
|
||||
<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>
|
||||
)}
|
||||
|
@ -34,7 +34,7 @@ export const View = memo(({ columns, item }) => !item || !columns?.length ? (
|
||||
<Form.Item
|
||||
key={column.dataIndex}
|
||||
name={column.dataIndex}
|
||||
style={{ padding: 0 }}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
{column.render(item[column.dataIndex])}
|
||||
</Form.Item>
|
||||
|
@ -18,14 +18,12 @@ const defaultData = [
|
||||
title: 'Замер бурового раствора',
|
||||
columns: columnsDrillingFluid,
|
||||
defaultValue: drillingFluidDefaultData,
|
||||
},
|
||||
{
|
||||
}, {
|
||||
idCategory: 2,
|
||||
title: 'Шламограмма',
|
||||
columns: columnsMudDiagram,
|
||||
defaultValue: mudDiagramDefaultData,
|
||||
},
|
||||
{
|
||||
}, {
|
||||
idCategory: 3,
|
||||
title: 'ННБ',
|
||||
columns: columnsNnb,
|
||||
@ -45,12 +43,13 @@ const defaultData = [
|
||||
|
||||
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)
|
||||
|
||||
@ -66,9 +65,7 @@ export const Measure = ({ idWell }) => {
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить последние данные по скважине ${idWell}`
|
||||
)
|
||||
|
||||
useEffect(updateCurrentValues, [idWell, isMeasuresUpdating])
|
||||
), [idWell, isMeasuresUpdating])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
|
@ -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 { formatDate, periodToString } from '../../utils'
|
||||
import { ReportService } from '../../services/api'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { Table, makeDateSorter, makeNumericSorter } from '../../components/Table'
|
||||
import { invokeWebApiWrapperAsync, downloadFile, formatTimespan } 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/UserView'
|
||||
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)
|
||||
|
@ -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 } from './datetime'
|
||||
export { isRawDate, formatDate, defaultFormat, periodToString } from './datetime'
|
||||
|
||||
export const headerHeight = 64
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user