Merge remote-tracking branch 'origin/dev' into DrillingModesPage

This commit is contained in:
goodmice 2021-10-12 11:13:55 +05:00
commit f9271b0f46
35 changed files with 757 additions and 285 deletions

View File

@ -32,7 +32,7 @@
"react_test": "react-scripts test", "react_test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"proxy": "http://192.168.1.70:5000", "proxy": "http://192.168.1.58:5000",
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"react-app", "react-app",

View File

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

View File

@ -1,4 +1,6 @@
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { makeNumericSorter, makeStringSorter} from './sorters'
export { makeDateSorter, makeNumericSorter, makeStringSorter} from './sorters'
export { Table } from 'antd' export { Table } from 'antd'
export { EditableTable } from './EditableTable' export { EditableTable } from './EditableTable'
export { DatePickerWrapper } from './DatePickerWrapper' export { DatePickerWrapper } from './DatePickerWrapper'
@ -7,10 +9,28 @@ export { SelectFromDictionary } from './SelectFromDictionary'
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/ export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
export const formatDate='YYYY.MM.DD HH:mm' export const formatDate='YYYY.MM.DD HH:mm'
export const numericColumnOptions = { export const makeNumericRender = (fixed?:number) => (value: any, row: object): ReactNode => {
const placeholder = '-'
let val = placeholder
if((value !== null) ||
(value !== undefined) ||
Number.isNaN(value) ||
!Number.isFinite(value)){
val = !!fixed
? (+value).toFixed(fixed)
: (+value).toPrecision(5)
}
return (<div className='text-align-r-container'>
<span>{val}</span>
</div>)
}
export const makeNumericColumnOptions = (fixed?:number, sorterKey?:string ) => ({
editable: true, editable: true,
initialValue: 0, initialValue: 0,
width:100, width:100,
sorter: sorterKey? makeNumericSorter(sorterKey) : null,
formItemRules: [ formItemRules: [
{ {
required: true, required: true,
@ -18,7 +38,8 @@ export const numericColumnOptions = {
pattern: RegExpIsFloat, pattern: RegExpIsFloat,
}, },
], ],
}; render: makeNumericRender(fixed),
})
/* /*
other - объект с дополнительными свойствами колонки other - объект с дополнительными свойствами колонки
@ -78,41 +99,6 @@ export const makeColumnsPlanFact = (title:string | ReactNode, key:string|string[
export const makeFilterTextMatch = (key: string | number) => (filterValue: string | number, dataItem: any) => export const makeFilterTextMatch = (key: string | number) => (filterValue: string | number, dataItem: any) =>
dataItem[key] === filterValue dataItem[key] === filterValue
export const makeNumericSorter = (key: string) => (a: any, b: any) => a[key] - b[key]
export const makeStringSorter = (key: string) => (a: any, b: any) =>
{
if(a == null && b == null)
return 1
if(a == null)
return 1
if(b == null)
return -1
let aValue = a[key]
let bValue = b[key]
for (let i = 0; i < a.length; i++) {
if (isNaN(aValue.charCodeAt(i)) || (aValue.charCodeAt(i) > bValue.charCodeAt(i)))
return 1
if (aValue.charCodeAt(i) > bValue.charCodeAt(i))
return -1
}
return 0
}
export const makeDateSorter = (key: string) => (a: any, b: any) => {
const date = new Date(a[key])
if(Number.isNaN(date.getTime()))
throw new Error('Date column contains not date formatted string(s)')
return date.getTime() - new Date(b[key]).getTime()
}
export const makeGroupColumn = (title: string, children: object[]) => ({ export const makeGroupColumn = (title: string, children: object[]) => ({
title: title, title: title,
children: children, children: children,
@ -135,17 +121,6 @@ export const makeTextColumn = (
...other ...other
}) })
export const defaultNumericRender = (value: any, row: object) => {
const placeholder = '-'
if((value === null) ||
(value === undefined) ||
Number.isNaN(value) ||
!Number.isFinite(value))
return placeholder
return (+value).toPrecision(5)
}
export const makeNumericColumn = (title: string, dataIndex: string, export const makeNumericColumn = (title: string, dataIndex: string,
filters: object[], filterDelegate: (key: string | number) => any, filters: object[], filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any, width: string, other?: columnPropsOther) => ({ renderDelegate: (_: any, row: object) => any, width: string, other?: columnPropsOther) => ({
@ -156,7 +131,7 @@ export const makeNumericColumn = (title: string, dataIndex: string,
onFilter: filterDelegate ? filterDelegate(dataIndex) : null, onFilter: filterDelegate ? filterDelegate(dataIndex) : null,
sorter: makeNumericSorter(dataIndex), sorter: makeNumericSorter(dataIndex),
width: width, width: width,
render: renderDelegate??defaultNumericRender, render: renderDelegate??makeNumericRender(),
align: 'right', align: 'right',
...other ...other
}) })

View File

@ -0,0 +1,33 @@
export const makeNumericSorter = (key: string) => (a: any, b: any) => a[key] - b[key];
export const makeStringSorter = (key: string) => (a: any, b: any) => {
if (a == null && b == null)
return 1;
if (a == null)
return 1;
if (b == null)
return -1;
let aValue = a[key];
let bValue = b[key];
for (let i = 0; i < a.length; i++) {
if (isNaN(aValue.charCodeAt(i)) || (aValue.charCodeAt(i) > bValue.charCodeAt(i)))
return 1;
if (aValue.charCodeAt(i) > bValue.charCodeAt(i))
return -1;
}
return 0;
};
export const makeDateSorter = (key: string) => (a: any, b: any) => {
const date = new Date(a[key]);
if (Number.isNaN(date.getTime()))
throw new Error('Date column contains not date formatted string(s)');
return date.getTime() - new Date(b[key]).getTime();
};

View File

@ -2,19 +2,35 @@ import { Upload, Button } from 'antd'
import { UploadOutlined } from '@ant-design/icons' import { UploadOutlined } from '@ant-design/icons'
import { useState } from 'react' import { useState } from 'react'
import { upload } from './factory' import { upload } from './factory'
import { ErrorFetch } from './ErrorFetch'
export default function UploadForm({url, accept, onUploadStart, onUploadComplete, onUploadError}) { export const UploadForm = ({url, accept, style, formData, onUploadStart, onUploadSuccess, onUploadComplete, onUploadError}) => {
const [fileList, setfileList] = useState([]) const [fileList, setfileList] = useState([])
const handleFileSend = async (values) => { const handleFileSend = async () => {
if(onUploadStart) if(onUploadStart)
onUploadStart() onUploadStart()
try { try {
const formData = new FormData() const formDataLocal = new FormData()
fileList.forEach((val) => { fileList.forEach((val) => {
formData.append("files", val.originFileObj); formDataLocal.append("files", val.originFileObj)
}); })
await upload(url, formData)
if(formData)
for(var propName in formData)
formDataLocal.append(propName, formData[propName])
const response = await upload(url, formDataLocal)
if(!response.ok)
{
const errorText = await response.text()
const error = new ErrorFetch(response.status, errorText)
throw error
}
else{
if(onUploadSuccess)
onUploadSuccess()
}
} catch(error) { } catch(error) {
if(onUploadError) if(onUploadError)
onUploadError(error) onUploadError(error)
@ -27,7 +43,7 @@ export default function UploadForm({url, accept, onUploadStart, onUploadComplete
const isSendButtonEnabled = fileList.length > 0 const isSendButtonEnabled = fileList.length > 0
return( return(
<div style={{display: 'flex'}}> <div style={{display: 'flex', ...style}}>
<Upload <Upload
name ="file" name ="file"
fileList={fileList} fileList={fileList}

View File

@ -18,7 +18,7 @@ export const UserView = ({user}) => {
<GridItem row={2} col={1}> <GridItem row={2} col={1}>
Фамилия: Фамилия:
</GridItem> </GridItem>
<GridItem row={3} col={2}> <GridItem row={2} col={2}>
{user?.surname} {user?.surname}
</GridItem> </GridItem>
<GridItem row={3} col={1}> <GridItem row={3} col={1}>

View File

@ -81,13 +81,14 @@ export const download = async (url:string, fileName?:string) => {
} }
export const upload = async (url:string, formData: FormData) => { export const upload = async (url:string, formData: FormData) => {
await fetch(url, { let response = await fetch(url, {
headers: { headers: {
Authorization: 'Bearer ' + localStorage['token'] Authorization: 'Bearer ' + localStorage['token']
}, },
method: 'Post', method: 'Post',
body: formData, body: formData,
}) })
return response
} }
export const downloadFile = async (fileInfo: FileInfoDto) => { export const downloadFile = async (fileInfo: FileInfoDto) => {

View File

@ -5,7 +5,7 @@ import {
Row, Row,
Col, Col,
Tooltip} from 'antd' Tooltip} from 'antd'
import { DataService } from '../services/api' import { TelemetryDataSaubService } from '../services/api'
import {generateUUID} from '../services/UidGenerator' import {generateUUID} from '../services/UidGenerator'
import { ArchiveColumn } from '../components/ArchiveColumn' import { ArchiveColumn } from '../components/ArchiveColumn'
import moment from 'moment' import moment from 'moment'
@ -94,7 +94,7 @@ export default function Archive({idWell}) {
let startDate = rangeDate[0].toISOString() let startDate = rangeDate[0].toISOString()
setLoader(true) setLoader(true)
DataService.getData(idWell, startDate, interval, 2048) TelemetryDataSaubService.getData(idWell, startDate, interval, 2048)
.then(handleReceiveDataSaub) .then(handleReceiveDataSaub)
.catch(error => { .catch(error => {
notify(`Не удалось загрузить данные по скважине (${idWell}) c ${rangeDate[0]} по ${rangeDate[1]}`, 'error') notify(`Не удалось загрузить данные по скважине (${idWell}) c ${rangeDate[0]} по ${rangeDate[1]}`, 'error')

View File

@ -7,7 +7,7 @@ import {
makeNumericColumnPlanFact makeNumericColumnPlanFact
} from "../../components/Table"; } from "../../components/Table";
import { invokeWebApiWrapperAsync } from '../../components/factory'; import { invokeWebApiWrapperAsync } from '../../components/factory';
import ChartDepthToDay from '../../components/charts/ChartDepthToDay'; import ChartTvD from '../WellOperations/ChartTvD';
import WellOperationsTable from './WellOperationsTable' import WellOperationsTable from './WellOperationsTable'
import { import {
calcAndUpdateStatsBySections, calcAndUpdateStatsBySections,
@ -249,10 +249,10 @@ export default function ClusterSections({ clusterData }) {
width={1500} width={1500}
footer={null} footer={null}
> >
<ChartDepthToDay <ChartTvD
dataPlan={tvdDataPlan} dataPlan={tvdDataPlan}
dataFact={tvdDataFact} dataFact={tvdDataFact}
dataForecast={tvdDataForecast} /> dataPredict={tvdDataForecast} />
</Modal> </Modal>
<Modal <Modal

View File

@ -6,12 +6,11 @@ import {
makeTextColumn, makeTextColumn,
makeGroupColumn, makeGroupColumn,
makeColumn, makeColumn,
makeNumericColumnPlanFact, makeDateSorter,
makeDateSorter makeNumericColumnPlanFact} from "../../components/Table";
} from "../../components/Table";
import { calcAndUpdateStatsBySections, makeFilterMinMaxFunction } from "./functions"; import { calcAndUpdateStatsBySections, makeFilterMinMaxFunction } from "./functions";
import { invokeWebApiWrapperAsync } from '../../components/factory'; import { invokeWebApiWrapperAsync } from '../../components/factory';
import ChartDepthToDay from '../../components/charts/ChartDepthToDay'; import ChartTvD from '../WellOperations/ChartTvD';
import WellOperationsTable from './WellOperationsTable' import WellOperationsTable from './WellOperationsTable'
import { getOperations } from "./functions"; import { getOperations } from "./functions";
@ -206,10 +205,10 @@ useEffect(() => {
width={1500} width={1500}
footer={null} footer={null}
> >
<ChartDepthToDay <ChartTvD
dataPlan={tvdDataPlan} dataPlan={tvdDataPlan}
dataFact={tvdDataFact} dataFact={tvdDataFact}
dataForecast={tvdDataForecast} dataPredict={tvdDataForecast}
/> />
</Modal> </Modal>

View File

@ -6,17 +6,24 @@ const minPrefix = "isMin"
export const getOperations = async (idWell) => { export const getOperations = async (idWell) => {
const ops = await WellOperationStatService.getTvd(idWell); const ops = await WellOperationStatService.getTvd(idWell);
const planData = ops.map(el => { const convert = wellOperationDto =>
return {key: el.plan?.id, depth: el.plan?.wellDepth, date: el.plan?.startDate} ({
}).filter(el => el.key) key: wellOperationDto?.id,
depth: wellOperationDto?.depthStart,
date: wellOperationDto?.dateStart
})
const factData = ops.map(el => { const planData = ops
return {key: el.fact?.id, depth: el.fact?.wellDepth, date: el.fact?.startDate} .map(item => convert(item.plan))
}).filter(el => el.key) .filter(el => el.key)
const predictData = ops.map(el => { const factData = ops
return {key: el.predict?.id, depth: el.predict?.wellDepth, date: el.predict?.startDate} .map(item => convert(item.fact))
}).filter(el => el.key) .filter(el => el.key)
const predictData = ops
.map(item => convert(item.predict))
.filter(el => el.key)
return { operations: ops, plan: planData, fact: factData, predict: predictData } return { operations: ops, plan: planData, fact: factData, predict: predictData }
} }

View File

@ -8,7 +8,7 @@ import {
formatBytes, formatBytes,
} from "../../components/factory" } from "../../components/factory"
import { EditableTable, makePaginationObject } from "../../components/Table" import { EditableTable, makePaginationObject } from "../../components/Table"
import UploadForm from "../../components/UploadForm" import {UploadForm} from "../../components/UploadForm"
import LoaderPortal from "../../components/LoaderPortal" import LoaderPortal from "../../components/LoaderPortal"
import {UserView} from '../../components/UserView' import {UserView} from '../../components/UserView'
import {CompanyView} from '../../components/CompanyView' import {CompanyView} from '../../components/CompanyView'

View File

@ -1,54 +1,198 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { Table, Button, Modal } from 'antd' import { Button, Form, Input, Popconfirm, Timeline } from 'antd'
import { HourglassOutlined } from '@ant-design/icons' import moment from 'moment'
import { CheckSquareOutlined,
EditOutlined,
SaveOutlined,
PlusOutlined,
CloseCircleOutlined,
DeleteOutlined } from '@ant-design/icons'
import { View } from './View'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { MeasureService } from '../../services/api' import { MeasureService } from '../../services/api'
import { Editor } from './Editor' import '../../styles/index.css'
import '../../styles/measure.css'
export const MeasureTable = ({idWell, idCategory, title, columns}) => { const format='YYYY.MM.DD HH:mm'
const [showLoader, setShowLoader] = useState(false)
const [showEditor, setShowEditor] = useState(false)
const [lastData, setLastData] = useState({})
const update = ()=>invokeWebApiWrapperAsync(async()=>{ export const MeasureTable = ({idWell, idCategory, title, columns, values, updateMeasuresFunc}) => {
const data = await MeasureService.getLast(idWell, idCategory)
setLastData(data) const [showLoader, setShowLoader] = useState(false);
const [displayedValues, setDisplayedValues] = useState({});
const [editingColumns, setEditingColumns] = useState(columns);
const [isTableEditing, setIsTableEditing] = useState(false);
const [editingActionName, setEditingActionName] = useState('');
const [measuresForm] = Form.useForm();
const createEditingColumns = (cols, renderDelegate) =>
cols.map(col =>
({ render: renderDelegate,
...col
})
)
useEffect(() => {
const defaultValuesToDisplay = values[values.length-1]
setDisplayedValues(defaultValuesToDisplay)
}, [values])
useEffect(() => {
let switchableColumns = []
isTableEditing
? switchableColumns = createEditingColumns(columns, () => <Input className='w-100 measure-input' />)
: switchableColumns = createEditingColumns(columns, null)
if(editingActionName === 'edit')
measuresForm.setFieldsValue(displayedValues?.data);
else if(editingActionName === 'add')
measuresForm.resetFields()
setEditingColumns(switchableColumns)
}, [isTableEditing, columns, editingActionName, displayedValues?.data, measuresForm])
const markMeasuresAsDeleted = async () => {
setShowLoader(true)
await MeasureService.markAsDelete(idWell, displayedValues.id)
updateMeasuresFunc()
setShowLoader(false)
} }
, setShowLoader
, "не удалось загрузить")
useEffect(update, [idWell, idCategory]) const checkIsDataDefault = () =>
displayedValues?.isDefaultData ? true : false
const timestamp = lastData ? new Date(lastData?.timestamp).toLocaleString() : '-' const crudButtons =
<div className='w-300px mt-8px'>
return <LoaderPortal show={showLoader}>
<h3>{title}</h3>
<span>Дата: {timestamp}</span>
&nbsp;
<Button <Button
onClick={() => setShowEditor(true)} key='add'
icon={<HourglassOutlined/>}>История</Button> className='w-33'
<Table onClick={() => {
style={{marginTop:16}} setEditingActionName('add')
bordered setIsTableEditing(true)
dataSource = {[lastData?.data]} }}
columns = {columns}
scroll={{ x: 400, y: 600 }}/>
<Modal
title={title}
centered
visible={showEditor}
onOk={() => setShowEditor(false)}
onCancel={() => setShowEditor(false)}
width="95%"
footer={null}
> >
<Editor <PlusOutlined />
idWell={idWell} </Button>
idCategory={idCategory} <Button
columns = {columns} key='edit'
onUpdate={update}/> className='w-33'
</Modal> onClick={() => {
setEditingActionName('edit')
setIsTableEditing(true)
}}
disabled={checkIsDataDefault()}
>
<EditOutlined />
</Button>
<Popconfirm
title="Удалить данные?"
onConfirm={() => markMeasuresAsDeleted()}
disabled={checkIsDataDefault()}
>
<Button
key='delete'
onClick={() => {
setEditingActionName('delete')
}}
disabled={checkIsDataDefault()}
>
<DeleteOutlined style={{margin:'auto 28px'}}/>
</Button>
</Popconfirm>
</div>
const confirmButtons =
<div className='w-300px mt-8px'>
<div className='d-flex'>
<Button
key='confirm'
className='w-50'
onClick={() => { measuresForm.submit() }}
>
<SaveOutlined />
</Button>
<Button
key='decline'
className='w-50'
onClick={()=> setIsTableEditing(false)}
>
<CloseCircleOutlined />
</Button>
</div>
</div>
let handleSubmitMeasuresForm = async (formData) => {
measuresForm.validateFields()
const measureParams = {
idWell: idWell,
idCategory: idCategory,
timestamp: new Date().toISOString(),
data: formData
}
setShowLoader(true)
if(editingActionName === 'add') {
await MeasureService.insert(idWell, measureParams)
} else if (editingActionName === 'edit') {
measureParams.id = displayedValues.id
measureParams.timestamp = displayedValues.timestamp
await MeasureService.update(idWell, measureParams)
}
setIsTableEditing(false)
updateMeasuresFunc()
setShowLoader(false)
}
return <>
&nbsp;
<h2>{title}</h2>
&nbsp;
<div className='d-flex'>
<div className='flex-direction-column'>
<div className='measure-buttons-container'>
{isTableEditing
? confirmButtons
: crudButtons
}
</div>
<div className='measure-dates mt-20px'>
<Timeline className='mt-12px ml-10px'>
{values.map((item, index) =>
<Timeline.Item
key={index}
className='measure-button'
onClick={() => setDisplayedValues(item)}
dot={item?.id === displayedValues?.id
? <CheckSquareOutlined className="timeline-clock-icon" />
: null}
>
<span className={item?.id === displayedValues?.id ? 'selected-timeline' : ''}>
{item.timestamp ? moment.utc(item.timestamp).local().format(format) : 'Нет данных'}
</span>
</Timeline.Item>
)}
</Timeline>
</div>
</div>
<div className='w-100'>
<LoaderPortal show={showLoader}>
<Form
form={measuresForm}
onFinish={handleSubmitMeasuresForm}
>
<View
item={displayedValues?.data ?? {}}
columns={editingColumns}
/>
</Form>
</LoaderPortal> </LoaderPortal>
</div>
</div>
</>
} }

View File

@ -1,48 +1,40 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { Button, Modal, Timeline } from 'antd' import { Table, Button, Modal } from 'antd'
import moment from 'moment'
import { HourglassOutlined } from '@ant-design/icons' import { HourglassOutlined } from '@ant-design/icons'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../components/factory'
import { MeasureService } from '../../services/api' import { MeasureService } from '../../services/api'
import { Editor } from './Editor' import { Editor } from './Editor'
import TimelineItem from 'antd/lib/timeline/TimelineItem'
//import { View } from './View'
const format='YYYY.MM.DD HH:mm' export const MeasureTable = ({idWell, idCategory, title, columns}) => {
export const MeasureTable2 = ({idWell, idCategory, title, columns}) => {
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [showEditor, setShowEditor] = useState(false) const [showEditor, setShowEditor] = useState(false)
const [history, setHistory] = useState([]) const [lastData, setLastData] = useState({})
const update = () => invokeWebApiWrapperAsync(async()=>{ const update = ()=>invokeWebApiWrapperAsync(async()=>{
const data = await MeasureService.getHisory(idWell, idCategory) const data = await MeasureService.getLast(idWell, idCategory)
const story = data?.map( i=> ({ setLastData(data)
id: i.id,
idWell: i.idWell,
idCategory: i.idCategory,
timestamp: moment.utc(i.timestamp).local().format(format),
...i.data}))
setHistory(story??[])
} }
, setShowLoader , setShowLoader
, "не удалось загрузить") , "не удалось загрузить")
useEffect(update, [idWell, idCategory]) useEffect(update, [idWell, idCategory])
const timestamp = lastData ? new Date(lastData?.timestamp).toLocaleString() : '-'
return <LoaderPortal show={showLoader}> return <LoaderPortal show={showLoader}>
<h3>{title}</h3> <h3>{title}</h3>
<span>Дата: {timestamp}</span>
&nbsp; &nbsp;
<Button <Button
onClick={() => setShowEditor(true)} onClick={() => setShowEditor(true)}
icon={<HourglassOutlined/>}>История</Button> icon={<HourglassOutlined/>}>История</Button>
<Timeline> <Table
{history.map(item=><TimelineItem value={item}>{item.timestamp}</TimelineItem>)} style={{marginTop:16}}
</Timeline> bordered
{/* <View dataSource = {[lastData?.data]}
item = {lastData?.data} columns = {columns}
columns = {columns}/> */} scroll={{ x: 400, y: 600 }}/>
<Modal <Modal
title={title} title={title}
centered centered

View File

@ -1,38 +1,62 @@
import { Empty } from 'antd'; import { Empty, Form } from 'antd';
import {Grid, GridItem} from '../../components/Grid' import {Grid, GridItem} from '../../components/Grid'
import '../../styles/index.css'
const renderSwitchableColumn = (column, itm) => {
if(column.render) {
return (
<Form.Item
key={column.dataIndex}
name={column.dataIndex}
style={{margin: 0}}
//rules={column.formItemRules}
>
{column.render(itm[column.dataIndex])}
</Form.Item>
)
}
return <p key={column.title} className='m-5px'>{itm[column.dataIndex]}</p>
}
export const View = ({columns, item}) => { export const View = ({columns, item}) => {
if (!item || !columns?.length) if (!item || !columns?.length)
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/> return <Empty key='empty' image={Empty.PRESENTED_IMAGE_SIMPLE}/>
const colsCount = 3 const colsCount = 3
const viewItems = columns.map( (column, i) => { const viewItems = columns.map( (column, i) => {
const row = Math.floor(i / colsCount) + 1 const row = Math.floor(i / colsCount) + 1
const colb = i % colsCount const colb = i % colsCount
return <> return <>
<GridItem <GridItem
key={column.dataIndex}
row={row} row={row}
col={colb*2 + 1} col={colb*2 + 1}
background='#00000005' style={{border:'1px solid lightgrey'}}
border='1px solid #FFFE'
> >
{column.title} {column.title}
</GridItem> </GridItem>
<GridItem <GridItem
key={column.title}
row={row} row={row}
col={colb*2 + 2} col={colb*2 + 2}
border='1px solid rgba(0, 0, 0, 0.05)' style={{border:'1px solid lightgrey',
justifyContect='right' justifyContent:'right',
marginRight='16px' marginRight:'16px',
fontWeight='bold' fontWeight:'bold',
textAlign='right'> textAlign:'right',
{column.render ? column.render(item[column.dataIndex]) : item[column.dataIndex]} padding: 0}}
>
{renderSwitchableColumn(column, item)}
</GridItem> </GridItem>
</> </>
}) })
return <Grid> return <>
<Grid>
{viewItems} {viewItems}
</Grid> </Grid>
</>
} }

View File

@ -42,4 +42,10 @@ export const numericColumnOptions = {
export const textColumnOptions = { export const textColumnOptions = {
editable:true, editable:true,
input:<TextArea/>, input:<TextArea/>,
width:'20rem'} width:'20rem',
formItemRules: [
{
required: true,
message: `Введите текст`
},
],}

View File

@ -1,39 +0,0 @@
import {makeColumn} from '../../components/Table'
import {v, numericColumnOptions, textColumnOptions} from './columnsCommon'
export const columnsMudDiagram = [
makeColumn(v('N пробы'), 'probeNumber', numericColumnOptions),
makeColumn(v('Глубина отбора пробы'), 'probeExtractionDepth', numericColumnOptions),
{
title: 'Литология',
key: 'lithology',
children: [
makeColumn(v('Песчаник (%)'), 'sandstone', numericColumnOptions),
makeColumn(v('Алевролит (%)'), 'siltstone', numericColumnOptions),
makeColumn(v('Аргиллит (%)'), 'argillit', numericColumnOptions),
makeColumn(v('Аргиллит бит. (%)'), 'brokenArgillit', numericColumnOptions),
makeColumn(v('Уголь (%)'), 'coal', numericColumnOptions),
makeColumn(v('Песок (%)'), 'sand', numericColumnOptions),
makeColumn(v('Глина (%)'), 'clay', numericColumnOptions),
makeColumn(v('Известняк (%)'), 'camstone', numericColumnOptions),
makeColumn(v('Цемент (%)'), 'cement', numericColumnOptions),
]
},
makeColumn('Краткое описание', 'summary', textColumnOptions),
makeColumn(v('ЛБА бурового раствора'), 'drillingMud', numericColumnOptions),
makeColumn(v('ЛБА (шлама)'), 'sludge', numericColumnOptions),
{
title: 'Газопоказания',
key: 'gasIndications',
children: [
makeColumn(v('Сумма УВ мах. (абс%)'), 'maxSum', numericColumnOptions),
makeColumn(v('С1 метан (отн%)'), 'methane', numericColumnOptions),
makeColumn(v('С2 этан (отн%)'), 'ethane', numericColumnOptions),
makeColumn(v('С3 пропан (отн%)'), 'propane', numericColumnOptions),
makeColumn(v('С4 бутан (отн%)'), 'butane', numericColumnOptions),
makeColumn(v('С5 пентан (отн%)'), 'pentane', numericColumnOptions),
]
},
makeColumn(v('Мех. скорость'), 'mechanicalSpeed', numericColumnOptions),
makeColumn('Предварительное заключение о насыщении по ГК', 'preliminaryConclusion', textColumnOptions),
]

View File

@ -31,3 +31,40 @@ export const columnsDrillingFluid = [
makeColumn(v("Смазка, %"), "grease",numericColumnOptions), makeColumn(v("Смазка, %"), "grease",numericColumnOptions),
makeColumn(v("Карбонат кальция, кг/м³"), "calciumCarbonate",numericColumnOptions), makeColumn(v("Карбонат кальция, кг/м³"), "calciumCarbonate",numericColumnOptions),
]; ];
export const drillingFluidDefaultData = {
idWell: 0,
key: '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
}
}

View File

@ -1,25 +1,64 @@
import { columnsMudDiagram} from './columnsMudDiagram' import { useState, useEffect } from 'react'
import { columnsDrillingFluid} from './columnsDrillingFluid' import { columnsMudDiagram} from './mudDiagramData'
import { columnsNnb } from './columnsNnb' import { mudDiagramDefaultData} from './mudDiagramData'
import { columnsDrillingFluid} from './drillingFluidData'
import { drillingFluidDefaultData} from './drillingFluidData'
import { columnsNnb } from './nnbData'
import { nnbDefaultData } from './nnbData'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { MeasureService } from '../../services/api'
import LoaderPortal from '../../components/LoaderPortal'
import { MeasureTable } from './MeasureTable' import { MeasureTable } from './MeasureTable'
//import { MeasureTable2 } from './MeasureTable2'
export default function Measure({idWell}){ export default function Measure({idWell}){
const [showLoader, setShowLoader] = useState(false)
const [fluidValues, setFluidValues] = useState([])
const [mudValues, setMudValues] = useState([])
const [nnbValues, setNnbValues] = useState([])
const [isMeasuresUpdating, setIsMeasuresUpdating] = useState(false)
const updateCurrentValues = () => invokeWebApiWrapperAsync(async()=>{
const measures = await MeasureService.getHisory(idWell)
setIsMeasuresUpdating(false)
const fluids = measures.filter(el => el.idCategory === 1)
setFluidValues(fluids.length ? fluids : [drillingFluidDefaultData])
const muds = measures.filter(el => el.idCategory === 2)
setMudValues(muds.length ? muds : [mudDiagramDefaultData])
const nnbs = measures.filter(el => el.idCategory === 3)
setNnbValues(nnbs.length ? nnbs : [nnbDefaultData])
}
,setShowLoader
,`Не удалось загрузить последние данные по скважине ${idWell}`)
useEffect(updateCurrentValues, [idWell, isMeasuresUpdating])
return <> return <>
<LoaderPortal show={showLoader}>
<MeasureTable <MeasureTable
idWell={idWell} idWell={idWell}
idCategory={1} idCategory={1}
title='Замер бурового раствора' title='Замер бурового раствора'
columns={columnsDrillingFluid}/> columns={columnsDrillingFluid}
values={fluidValues}
updateMeasuresFunc = {() => setIsMeasuresUpdating(true)}
/>
<MeasureTable <MeasureTable
idWell={idWell} idWell={idWell}
idCategory={2} idCategory={2}
title='Шламограмма' title='Шламограмма'
columns={columnsMudDiagram}/> columns={columnsMudDiagram}
values={mudValues}
updateMeasuresFunc = {() => setIsMeasuresUpdating(true)}
/>
<MeasureTable <MeasureTable
idWell={idWell} idWell={idWell}
idCategory={3} idCategory={3}
title='ННБ' title='ННБ'
columns={columnsNnb}/> columns={columnsNnb}
values={nnbValues}
updateMeasuresFunc = {() => setIsMeasuresUpdating(true)}
/>
</LoaderPortal>
</> </>
} }

View File

@ -0,0 +1,58 @@
import {makeColumn} from '../../components/Table'
import {v, numericColumnOptions, textColumnOptions} from './columnsCommon'
export const columnsMudDiagram = [
makeColumn(v('N пробы'), 'probeNumber', numericColumnOptions),
makeColumn(v('Глубина отбора пробы'), 'probeExtractionDepth', numericColumnOptions),
makeColumn(v('Песчаник (%)'), 'sandstone', numericColumnOptions),
makeColumn(v('Алевролит (%)'), 'siltstone', numericColumnOptions),
makeColumn(v('Аргиллит (%)'), 'argillit', numericColumnOptions),
makeColumn(v('Аргиллит бит. (%)'), 'brokenArgillit', numericColumnOptions),
makeColumn(v('Уголь (%)'), 'coal', numericColumnOptions),
makeColumn(v('Песок (%)'), 'sand', numericColumnOptions),
makeColumn(v('Глина (%)'), 'clay', numericColumnOptions),
makeColumn(v('Известняк (%)'), 'camstone', numericColumnOptions),
makeColumn(v('Цемент (%)'), 'cement', numericColumnOptions),
makeColumn('Краткое описание', 'summary', textColumnOptions),
makeColumn(v('ЛБА бурового раствора'), 'drillingMud', numericColumnOptions),
makeColumn(v('ЛБА (шлама)'), 'sludge', numericColumnOptions),
makeColumn(v('Сумма УВ мах. (абс%)'), 'maxSum', numericColumnOptions),
makeColumn(v('С1 метан (отн%)'), 'methane', numericColumnOptions),
makeColumn(v('С2 этан (отн%)'), 'ethane', numericColumnOptions),
makeColumn(v('С3 пропан (отн%)'), 'propane', numericColumnOptions),
makeColumn(v('С4 бутан (отн%)'), 'butane', numericColumnOptions),
makeColumn(v('С5 пентан (отн%)'), 'pentane', numericColumnOptions),
makeColumn(v('Мех. скорость'), 'mechanicalSpeed', numericColumnOptions),
makeColumn('Предварительное заключение о насыщении по ГК', 'preliminaryConclusion', textColumnOptions),
]
export const mudDiagramDefaultData = {
idWell: 0,
key: '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": '-'
}
}

View File

@ -20,3 +20,29 @@ export const columnsNnb = [
makeColumn(v('Разница вертикальных глубин\nмежду планом и фактом'), 'depthPlanFactDifference', numericColumnOptions), makeColumn(v('Разница вертикальных глубин\nмежду планом и фактом'), 'depthPlanFactDifference', numericColumnOptions),
makeColumn(v('Расстояние в пространстве\nмежду планом и фактом'), 'distancePlanFactDifference', numericColumnOptions), makeColumn(v('Расстояние в пространстве\nмежду планом и фактом'), 'distancePlanFactDifference', numericColumnOptions),
]; ];
export const nnbDefaultData = {
idWell: 0,
key: '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
}
}

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import { Table, makeDateSorter, makeNumericSorter, formatDate} from "../../components/Table" import { Table, formatDate, makeDateSorter, makeNumericSorter} from "../../components/Table"
import { Button, Tooltip } from "antd" import { Button, Tooltip } from "antd"
import { FilePdfOutlined, FileTextOutlined} from '@ant-design/icons' import { FilePdfOutlined, FileTextOutlined} from '@ant-design/icons'
import { ReportService } from "../../services/api" import { ReportService } from "../../services/api"

View File

@ -46,7 +46,7 @@ export default function Well() {
<Link to={`${rootPath}/report`}>Рапорт</Link> <Link to={`${rootPath}/report`}>Рапорт</Link>
</PrivateMenuItem> </PrivateMenuItem>
<PrivateMenuItem key="operations" icon={<FolderOutlined />}> <PrivateMenuItem key="operations" icon={<FolderOutlined />}>
<Link to={`${rootPath}/operations/plan`}>Операции по скважине</Link> <Link to={`${rootPath}/operations`}>Операции по скважине</Link>
</PrivateMenuItem> </PrivateMenuItem>
<PrivateMenuItem key="archive" icon={<DatabaseOutlined />}> <PrivateMenuItem key="archive" icon={<DatabaseOutlined />}>
<Link to={`${rootPath}/archive`}>Архив</Link> <Link to={`${rootPath}/archive`}>Архив</Link>
@ -89,7 +89,7 @@ export default function Well() {
<Route path="/well/:idWell/report"> <Route path="/well/:idWell/report">
<Report idWell={idWell} /> <Report idWell={idWell} />
</Route> </Route>
<Route path="/well/:idWell/operations/:tab"> <Route path="/well/:idWell/operations">
<WellOperations idWell={idWell} /> <WellOperations idWell={idWell} />
</Route> </Route>
<Route path="/well/:idWell/archive"> <Route path="/well/:idWell/archive">

View File

@ -97,7 +97,7 @@ const makeDataset = (data, label, color, width=1.5, dash) => ({
borderDash: dash, borderDash: dash,
}) })
export default function ChartDepthToDay({dataPlan, dataFact, dataForecast}) { export default function ChartTvD({dataPlan, dataFact, dataPredict}) {
const chartRef = useRef(null) const chartRef = useRef(null)
const [chart, setChart] = useState() const [chart, setChart] = useState()
@ -105,7 +105,7 @@ export default function ChartDepthToDay({dataPlan, dataFact, dataForecast}) {
let data = { let data = {
datasets: [ datasets: [
makeDataset(dataFact, 'Факт', '#0A0'), makeDataset(dataFact, 'Факт', '#0A0'),
makeDataset(dataForecast, 'Прогноз', 'purple', 1, [7,3]), makeDataset(dataPredict, 'Прогноз', 'purple', 1, [7,3]),
makeDataset(dataPlan, 'План', '#C004', 4), makeDataset(dataPlan, 'План', '#C004', 4),
] ]
} }
@ -127,7 +127,7 @@ export default function ChartDepthToDay({dataPlan, dataFact, dataForecast}) {
chart.data = data chart.data = data
chart.update() chart.update()
} }
}, [chart, dataPlan, dataFact, dataForecast]) }, [chart, dataPlan, dataFact, dataPredict])
return (<canvas ref={chartRef} />) return (<canvas ref={chartRef} />)
} }

View File

@ -0,0 +1,54 @@
import { Button, Tooltip, Modal } from "antd";
import {useState} from 'react'
import { FileOutlined, ImportOutlined, ExportOutlined } from '@ant-design/icons'
import { download } from '../../components/factory'
import { ImportOperations } from './ImportOperations'
const style = {margin:4}
export const ImportExportBar = ({idWell, onImported}) =>{
const [isImportModalVisible, setIsImportModalVisible] = useState(false)
const downloadTemplate = async () => download(`/api/well/${idWell}/wellOperations/tamplate`)
const downloadExport = async () => download(`/api/well/${idWell}/wellOperations/export`)
const onDone = () => {
setIsImportModalVisible(false)
if(onImported)
onImported()
}
return <>
<Tooltip title = "Импорт - загрузить файл с операциями на сервер">
<Button
icon={<ImportOutlined/>}
style={style}
onClick={_=>setIsImportModalVisible(true)}/>
</Tooltip>
<Tooltip title = "Экспорт - скачать файл с операциями по скважине">
<Button
icon={<ExportOutlined/>}
style={style}
onClick={downloadExport}/>
</Tooltip>
<Tooltip title = "Скачать шаблон">
<Button
icon={<FileOutlined/>}
style={style}
onClick={downloadTemplate}/>
</Tooltip>
<Modal
title='Импорт'
visible={isImportModalVisible}
onCancel={_=>setIsImportModalVisible(false)}
footer={null}>
<ImportOperations
idWell={idWell}
onDone={onDone}/>
</Modal >
</>
}

View File

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

View File

@ -1,4 +1,4 @@
import ChartDepthToDay from '../../components/charts/ChartDepthToDay'; import ChartTvD from './ChartTvD';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { invokeWebApiWrapperAsync } from '../../components/factory'; import { invokeWebApiWrapperAsync } from '../../components/factory';
import { getOperations } from '../Cluster/functions'; import { getOperations } from '../Cluster/functions';
@ -7,18 +7,15 @@ import { getOperations } from '../Cluster/functions';
export const Tvd = ({ idWell }) => { export const Tvd = ({ idWell }) => {
const [dataPlan, setDataPlan] = useState([]); const [dataPlan, setDataPlan] = useState([]);
const [dataFact, setDataFact] = useState([]); const [dataFact, setDataFact] = useState([]);
const [dataForecast, setDataForecast] = useState([]); const [dataPredict, setDataPredict] = useState([]);
useEffect(() => { useEffect(() => {
invokeWebApiWrapperAsync( invokeWebApiWrapperAsync(
async () => { async () => {
const operations = await getOperations(idWell); const operations = await getOperations(idWell);
setDataPlan(operations.plan) setDataPlan(operations.plan)
setDataFact(operations.fact) setDataFact(operations.fact)
setDataPredict(operations.predict)
setDataForecast(operations.predict)
}, },
null, null,
`Не удалось загрузить операции по скважине "${idWell}"`, `Не удалось загрузить операции по скважине "${idWell}"`,
@ -29,10 +26,10 @@ export const Tvd = ({ idWell }) => {
<div className="container"> <div className="container">
<div> <div>
<h2 className={'mt-20px'}>График Глубина-день</h2> <h2 className={'mt-20px'}>График Глубина-день</h2>
<ChartDepthToDay <ChartTvD
dataPlan={dataPlan} dataPlan={dataPlan}
dataFact={dataFact} dataFact={dataFact}
dataForecast={dataForecast} /> dataPredict={dataPredict} />
</div> </div>
</div> </div>
); );

View File

@ -6,10 +6,8 @@ import {
DatePickerWrapper, DatePickerWrapper,
SelectFromDictionary, SelectFromDictionary,
makeColumn, makeColumn,
numericColumnOptions,
makeNumericSorter,
makeDateSorter, makeDateSorter,
defaultNumericRender } from "../../components/Table" makeNumericColumnOptions} from "../../components/Table"
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../components/factory'
import { WellOperationService} from '../../services/api' import { WellOperationService} from '../../services/api'
@ -20,20 +18,6 @@ const { TextArea } = Input;
const basePageSize = 160; const basePageSize = 160;
const format='YYYY.MM.DD HH:mm' const format='YYYY.MM.DD HH:mm'
const numericSortColumnOptions = {
...numericColumnOptions,
sorter: makeNumericSorter('wellDepth'),
render:(value) =>
<div className='text-align-r-container'>
<span>{value}</span>
</div>
}
const durationFormattedColumnOptions = {
...numericColumnOptions,
render: defaultNumericRender
}
export const WellOperationsEditor = ({idWell, idType}) => { export const WellOperationsEditor = ({idWell, idType}) => {
const [pageNumAndPageSize, setPageNumAndPageSize] = useState({current:1, pageSize:basePageSize}) const [pageNumAndPageSize, setPageNumAndPageSize] = useState({current:1, pageSize:basePageSize})
const [paginationTotal, setPaginationTotal] = useState(0) const [paginationTotal, setPaginationTotal] = useState(0)
@ -81,19 +65,20 @@ export const WellOperationsEditor = ({idWell, idType}) => {
render:(_, record)=>getByKeyOrReturnKey(dictionaryOperationCategory, record.idCategory) render:(_, record)=>getByKeyOrReturnKey(dictionaryOperationCategory, record.idCategory)
}), }),
makeColumn('Доп. инфо','categoryInfo', {editable:true, width:300, input:<TextArea/>}), makeColumn('Доп. инфо','categoryInfo', {editable:true, width:300, input:<TextArea/>}),
makeColumn('Глубина забоя','wellDepth', numericSortColumnOptions), makeColumn('Глубина забоя на начало','depthStart', makeNumericColumnOptions(2, 'depthStart')),
makeColumn('Время начала','startDate', { makeColumn('Глубина забоя при завершении','depthEnd', makeNumericColumnOptions(2, 'depthEnd')),
makeColumn('Время начала','dateStart', {
editable:true, editable:true,
width:200, width:200,
input:<DatePickerWrapper/>, input:<DatePickerWrapper/>,
initialValue:moment().format(), initialValue:moment().format(),
sorter: makeDateSorter('startDate'), sorter: makeDateSorter('dateStart'),
render:(_, record) => render:(_, record) =>
<div className={'text-align-r-container'}> <div className={'text-align-r-container'}>
<span>{moment.utc(record.startDate).local().format(format)}</span> <span>{moment.utc(record.dateStart).local().format(format)}</span>
</div> </div>
}), }),
makeColumn('Часы','durationHours', durationFormattedColumnOptions), makeColumn('Часы','durationHours', makeNumericColumnOptions(2, 'durationHours')),
makeColumn('Комментарий','comment', {editable:true, input:<TextArea/>}), makeColumn('Комментарий','comment', {editable:true, input:<TextArea/>}),
] ]

View File

@ -1,17 +1,22 @@
import {Layout, Menu} from "antd"; import {Layout, Menu} from "antd";
import {Switch, Link, Route, Redirect, useParams} from "react-router-dom"; import {Switch, Link, Route, Redirect, useParams, useHistory} from "react-router-dom";
import { FolderOutlined } from "@ant-design/icons"; import { FolderOutlined } from "@ant-design/icons";
import { WellOperationsEditor } from './WellOperationsEditor' import { WellOperationsEditor } from './WellOperationsEditor'
import { WellSectionsStat } from './WellSectionsStat' import { WellSectionsStat } from './WellSectionsStat'
import { Tvd } from './Tvd' import { Tvd } from './Tvd'
import { WellOpeationsModes } from './WellOpeationsModes' import { ImportExportBar } from "./ImportExportBar";
const { Content } = Layout const { Content } = Layout
export default function WellOperations({idWell}) { export default function WellOperations({idWell}) {
let {tab} = useParams() let {tab} = useParams()
let history = useHistory()
const rootPath = `/well/${idWell}/operations`; const rootPath = `/well/${idWell}/operations`;
const onImported = () => {
history.push(`${rootPath}`)
}
return(<> return(<>
<Menu <Menu
mode="horizontal" mode="horizontal"
@ -33,6 +38,7 @@ export default function WellOperations({idWell}) {
<Menu.Item key={'modes'} icon={<FolderOutlined />}> <Menu.Item key={'modes'} icon={<FolderOutlined />}>
<Link to={`${rootPath}/modes`}>Режимы</Link> <Link to={`${rootPath}/modes`}>Режимы</Link>
</Menu.Item> </Menu.Item>
<ImportExportBar idWell={idWell} onImported={onImported}/>
</Menu> </Menu>
<Layout> <Layout>
<Content className="site-layout-background"> <Content className="site-layout-background">
@ -53,7 +59,7 @@ export default function WellOperations({idWell}) {
<WellOpeationsModes idWell={idWell}/> <WellOpeationsModes idWell={idWell}/>
</Route> </Route>
<Route path={rootPath}> <Route path={rootPath}>
<Redirect to={`${rootPath}/tvd`}/> <Redirect to={`${rootPath}/plan`}/>
</Route> </Route>
</Switch> </Switch>
</Content> </Content>

View File

@ -20,7 +20,7 @@ export type DrillParamsDto = {
topDriveSpeedMin?: number; topDriveSpeedMin?: number;
topDriveSpeedAvg?: number; topDriveSpeedAvg?: number;
topDriveSpeedMax?: number; topDriveSpeedMax?: number;
flowMin?: number; consumptionMin?: number;
flowAvg?: number; consumptionAvg?: number;
flowMax?: number; consumptionMax?: number;
} }

View File

@ -25,8 +25,8 @@ export class AdminTelemetryService {
* @throws ApiError * @throws ApiError
*/ */
public static async mergeTelemetries( public static async mergeTelemetries(
requestBody?: Array<number>, requestBody?: Array<number>,
): Promise<any> { ): Promise<any> {
const result = await __request({ const result = await __request({
method: 'POST', method: 'POST',
path: `/merge`, path: `/merge`,

View File

@ -15,10 +15,10 @@ export class DrillParamsService {
* @throws ApiError * @throws ApiError
*/ */
public static async getDefaultDrillParams( public static async getDefaultDrillParams(
idWell?: number, idWell?: number,
startDepth?: number, startDepth?: number,
endDepth?: number, endDepth?: number,
): Promise<DrillParamsDto> { ): Promise<DrillParamsDto> {
const result = await __request({ const result = await __request({
method: 'GET', method: 'GET',
path: `/drillParams/idWell/autoParams`, path: `/drillParams/idWell/autoParams`,
@ -39,9 +39,9 @@ export class DrillParamsService {
* @throws ApiError * @throws ApiError
*/ */
public static async saveDrillParams( public static async saveDrillParams(
idWell?: number, idWell?: number,
requestBody?: DrillParamsDto, requestBody?: DrillParamsDto,
): Promise<number> { ): Promise<number> {
const result = await __request({ const result = await __request({
method: 'POST', method: 'POST',
path: `/drillParams/idWell`, path: `/drillParams/idWell`,

View File

@ -154,10 +154,10 @@ export class WellOperationService {
* @throws ApiError * @throws ApiError
*/ */
public static async import( public static async import(
idWell: number, idWell: number,
options: number, options: number,
requestBody?: any, requestBody?: any,
): Promise<any> { ): Promise<any> {
const result = await __request({ const result = await __request({
method: 'POST', method: 'POST',
path: `/api/well/${idWell}/wellOperations/import/${options}`, path: `/api/well/${idWell}/wellOperations/import/${options}`,
@ -173,8 +173,8 @@ export class WellOperationService {
* @throws ApiError * @throws ApiError
*/ */
public static async export( public static async export(
idWell: number, idWell: number,
): Promise<string> { ): Promise<string> {
const result = await __request({ const result = await __request({
method: 'GET', method: 'GET',
path: `/api/well/${idWell}/wellOperations/export`, path: `/api/well/${idWell}/wellOperations/export`,
@ -189,8 +189,8 @@ export class WellOperationService {
* @throws ApiError * @throws ApiError
*/ */
public static async getTamplate( public static async getTamplate(
idWell: string, idWell: string,
): Promise<string> { ): Promise<string> {
const result = await __request({ const result = await __request({
method: 'GET', method: 'GET',
path: `/api/well/${idWell}/wellOperations/tamplate`, path: `/api/well/${idWell}/wellOperations/tamplate`,

View File

@ -11,6 +11,10 @@ body {
display: flex; display: flex;
} }
.flex-direction-column {
flex-direction: column;
}
.d-inline { .d-inline {
display: inline; display: inline;
} }
@ -19,10 +23,30 @@ body {
display: none; display: none;
} }
.w-15 {
width: 15%
}
.w-33 {
width: 33%
}
.w-50 {
width: 50%
}
.w-100 { .w-100 {
width: 100% width: 100%
} }
.m-0 {
margin: 0;
}
.mt-8px {
margin-top: 8px;
}
.mt-20px { .mt-20px {
margin-top: 20px; margin-top: 20px;
} }
@ -35,6 +59,10 @@ body {
margin-left: 5px; margin-left: 5px;
} }
.ml-10px {
margin-left: 10px;
}
.ml-30px { .ml-30px {
margin-left: 30px; margin-left: 30px;
} }

36
src/styles/measure.css Normal file
View File

@ -0,0 +1,36 @@
input.measure-input {
font-weight:bold;
}
.w-300px {
width: 300px;
}
.measure-buttons-container {
height: 30px;
}
.measure-dates {
height: 160px;
overflow-y: scroll;
}
.measure-button:hover {
cursor: pointer;
}
.selected-timeline {
font-weight: bold;
}
.m-5px {
margin: 5px 5px;
}
.mt-12px {
margin-top: 12px;
}
.ml-10px {
margin-left: 10px;
}