forked from ddrilling/asb_cloud_front
Добавлена таблица собирательной инклинометрии
This commit is contained in:
parent
1ebaf3c185
commit
252cdfe7dd
50
src/pages/Measure/InclinometryTable.jsx
Normal file
50
src/pages/Measure/InclinometryTable.jsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Modal } from 'antd'
|
||||
import { Table } from '../../components/Table'
|
||||
import { formatDate } from './MeasureTable'
|
||||
import { v } from './columnsCommon'
|
||||
|
||||
export const InclinometryTable = React.memo(({ group, visible, onClose }) => {
|
||||
const [tableColumns, setTableColumns] = useState([])
|
||||
const [tableData, setTableData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setTableColumns([{
|
||||
title: 'Дата',
|
||||
key: 'date',
|
||||
dataIndex: 'date',
|
||||
render: (item) => formatDate(item),
|
||||
fixed: 'left',
|
||||
width: '8rem',
|
||||
},
|
||||
...(group?.columns?.map((column) => ({...column, title: v(column.title)})) ?? [])
|
||||
])
|
||||
}, [group?.columns])
|
||||
|
||||
useEffect(() => {
|
||||
setTableData(group?.values?.map(row => ({ date: row.timestamp, ...row.data })))
|
||||
}, [group?.values])
|
||||
|
||||
return !group?.columns ? null : (
|
||||
<Modal
|
||||
title={group?.title}
|
||||
centered
|
||||
visible={visible}
|
||||
onCancel={onClose}
|
||||
width={1900}
|
||||
footer={null}
|
||||
>
|
||||
<Table
|
||||
dataSource={tableData}
|
||||
columns={tableColumns}
|
||||
scroll={{
|
||||
y: 400,
|
||||
x: 1300
|
||||
}}
|
||||
bordered
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
})
|
||||
|
||||
export default InclinometryTable
|
@ -1,140 +1,84 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button, Form, Input, Popconfirm, Timeline } from 'antd'
|
||||
import moment from 'moment'
|
||||
import { CheckSquareOutlined,
|
||||
import {
|
||||
CheckSquareOutlined,
|
||||
EditOutlined,
|
||||
SaveOutlined,
|
||||
PlusOutlined,
|
||||
CloseCircleOutlined,
|
||||
DeleteOutlined } from '@ant-design/icons'
|
||||
DeleteOutlined
|
||||
} from '@ant-design/icons'
|
||||
import { View } from './View'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { MeasureService } from '../../services/api'
|
||||
import '../../styles/index.css'
|
||||
import '../../styles/measure.css'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
|
||||
const format='YYYY.MM.DD HH:mm'
|
||||
const dateFormat = 'YYYY.MM.DD HH:mm'
|
||||
export const formatDate = (date) => date ? moment.utc(date).local().format(dateFormat) : 'Нет данных'
|
||||
|
||||
export const MeasureTable = ({idWell, idCategory, title, columns, values, updateMeasuresFunc}) => {
|
||||
export const MeasureTable = ({idWell, group, updateMeasuresFunc, additionalButtons}) => {
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [displayedValues, setDisplayedValues] = useState({})
|
||||
const [editingColumns, setEditingColumns] = useState(group.columns)
|
||||
const [isTableEditing, setIsTableEditing] = useState(false)
|
||||
const [editingActionName, setEditingActionName] = useState('')
|
||||
const [data, setData] = useState([])
|
||||
|
||||
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 [measuresForm] = Form.useForm()
|
||||
|
||||
const createEditingColumns = (cols, renderDelegate) =>
|
||||
cols.map(col =>
|
||||
({ render: renderDelegate,
|
||||
...col
|
||||
})
|
||||
cols.map(col => ({ render: renderDelegate, ...col }))
|
||||
|
||||
useEffect(() => {
|
||||
const data = [group.defaultValue].concat(group.values ?? [])
|
||||
setData(data)
|
||||
setDisplayedValues(data.at(-1))
|
||||
}, [group.defaultValue, group.values])
|
||||
|
||||
useEffect(() => {
|
||||
const switchableColumns = createEditingColumns(
|
||||
group.columns,
|
||||
isTableEditing ? () => <Input className={'w-100 measure-input'} /> : null
|
||||
)
|
||||
|
||||
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);
|
||||
measuresForm.setFieldsValue(displayedValues?.data)
|
||||
else if(editingActionName === 'add')
|
||||
measuresForm.resetFields()
|
||||
|
||||
setEditingColumns(switchableColumns)
|
||||
}, [isTableEditing, columns, editingActionName, displayedValues?.data, measuresForm])
|
||||
}, [isTableEditing, group.columns, editingActionName, displayedValues?.data, measuresForm])
|
||||
|
||||
const markMeasuresAsDeleted = async () => {
|
||||
setShowLoader(true)
|
||||
const markMeasuresAsDeleted = async () => await invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
await MeasureService.markAsDelete(idWell, displayedValues.id)
|
||||
updateMeasuresFunc()
|
||||
setShowLoader(false)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось удалить запись ${displayedValues.id} для скважины "${idWell}"`
|
||||
)
|
||||
|
||||
const isDataDefault = () => !!displayedValues?.isDefaultData
|
||||
|
||||
const editTable = (action) => {
|
||||
setEditingActionName(action)
|
||||
setIsTableEditing(true)
|
||||
}
|
||||
|
||||
const checkIsDataDefault = () =>
|
||||
displayedValues?.isDefaultData ? true : false
|
||||
|
||||
const crudButtons =
|
||||
<div className='w-300px mt-8px'>
|
||||
<Button
|
||||
key='add'
|
||||
className='w-33'
|
||||
onClick={() => {
|
||||
setEditingActionName('add')
|
||||
setIsTableEditing(true)
|
||||
}}
|
||||
>
|
||||
<PlusOutlined />
|
||||
</Button>
|
||||
<Button
|
||||
key='edit'
|
||||
className='w-33'
|
||||
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) => {
|
||||
const handleSubmitMeasuresForm = async (formData) => await invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
measuresForm.validateFields()
|
||||
|
||||
const measureParams = {
|
||||
idWell: idWell,
|
||||
idCategory: idCategory,
|
||||
idCategory: group.idCategory,
|
||||
timestamp: new Date().toISOString(),
|
||||
data: formData
|
||||
}
|
||||
|
||||
setShowLoader(true)
|
||||
|
||||
if(editingActionName === 'add') {
|
||||
await MeasureService.insert(idWell, measureParams)
|
||||
} else if (editingActionName === 'edit') {
|
||||
@ -145,54 +89,73 @@ export const MeasureTable = ({idWell, idCategory, title, columns, values, update
|
||||
|
||||
setIsTableEditing(false)
|
||||
updateMeasuresFunc()
|
||||
setShowLoader(false)
|
||||
}
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось добавить/изменить запись для скаважины "${idWell}"`
|
||||
)
|
||||
|
||||
return <>
|
||||
|
||||
<h2>{title}</h2>
|
||||
|
||||
<div className='d-flex'>
|
||||
<div className='flex-direction-column'>
|
||||
<div className='measure-buttons-container'>
|
||||
{isTableEditing
|
||||
? confirmButtons
|
||||
: crudButtons
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<h2>{group.title}</h2>
|
||||
<div className={'d-flex'}>
|
||||
<div className={'flex-direction-column'}>
|
||||
<div className={'measure-buttons-container'}>
|
||||
<div className={'w-300px mt-8px d-flex'} style={{ alignItems: 'stretch' }}>
|
||||
{ isTableEditing ? (
|
||||
<>
|
||||
<Button key={'confirm'} className={'flex-1'} onClick={() => measuresForm.submit()}>
|
||||
<SaveOutlined />
|
||||
</Button>
|
||||
<Button key={'decline'} className={'flex-1'} onClick={() => setIsTableEditing(false)}>
|
||||
<CloseCircleOutlined />
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button key={'add'} className={'flex-1'} onClick={() => editTable('add')}>
|
||||
<PlusOutlined />
|
||||
</Button>
|
||||
<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()} >
|
||||
<DeleteOutlined style={{ margin:'auto 28px' }}/>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</>
|
||||
)}
|
||||
{additionalButtons?.(isTableEditing)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='measure-dates mt-20px'>
|
||||
<Timeline className='mt-12px ml-10px'>
|
||||
{values.map((item, index) =>
|
||||
<div className={'measure-dates mt-20px'}>
|
||||
<Timeline className={'mt-12px ml-10px'}>
|
||||
{data.map((item, index) =>
|
||||
<Timeline.Item
|
||||
key={index}
|
||||
className='measure-button'
|
||||
className={'measure-button'}
|
||||
onClick={() => setDisplayedValues(item)}
|
||||
dot={item?.id === displayedValues?.id
|
||||
? <CheckSquareOutlined className="timeline-clock-icon" />
|
||||
: null}
|
||||
dot={item?.id !== displayedValues?.id ? null :
|
||||
<CheckSquareOutlined className={'timeline-clock-icon'} />
|
||||
}
|
||||
>
|
||||
<span className={item?.id === displayedValues?.id ? 'selected-timeline' : ''}>
|
||||
{item.timestamp ? moment.utc(item.timestamp).local().format(format) : 'Нет данных'}
|
||||
{formatDate(item.timestamp)}
|
||||
</span>
|
||||
</Timeline.Item>
|
||||
)}
|
||||
</Timeline>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-100'>
|
||||
<div className={'w-100'}>
|
||||
<LoaderPortal show={showLoader}>
|
||||
<Form
|
||||
form={measuresForm}
|
||||
onFinish={handleSubmitMeasuresForm}
|
||||
>
|
||||
<View
|
||||
item={displayedValues?.data ?? {}}
|
||||
columns={editingColumns}
|
||||
/>
|
||||
<Form form={measuresForm} onFinish={handleSubmitMeasuresForm}>
|
||||
<View item={displayedValues?.data ?? {}} columns={editingColumns} />
|
||||
</Form>
|
||||
</LoaderPortal>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,62 +1,61 @@
|
||||
import { Empty, Form } from 'antd';
|
||||
import React from 'react'
|
||||
import { Empty, Form } from 'antd'
|
||||
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>
|
||||
)
|
||||
const colsCount = 3
|
||||
|
||||
const headerCellStyle = {
|
||||
border:'1px solid lightgrey'
|
||||
}
|
||||
|
||||
return <p key={column.title} className='m-5px'>{itm[column.dataIndex]}</p>
|
||||
const valueCellStyle = {
|
||||
border:'1px solid lightgrey',
|
||||
justifyContent:'right',
|
||||
marginRight:'16px',
|
||||
fontWeight:'bold',
|
||||
textAlign:'right',
|
||||
padding: 0
|
||||
}
|
||||
|
||||
export const View = ({columns, item}) => {
|
||||
export const View = React.memo(({columns, item}) => {
|
||||
if (!item || !columns?.length)
|
||||
return <Empty key='empty' image={Empty.PRESENTED_IMAGE_SIMPLE}/>
|
||||
|
||||
const colsCount = 3
|
||||
const viewItems = columns.map( (column, i) => {
|
||||
const row = Math.floor(i / colsCount) + 1
|
||||
const colb = i % colsCount
|
||||
|
||||
return <>
|
||||
return (
|
||||
<Grid>
|
||||
{columns.map((column, i) => (
|
||||
<>
|
||||
<GridItem
|
||||
key={column.dataIndex}
|
||||
row={row}
|
||||
col={colb*2 + 1}
|
||||
style={{border:'1px solid lightgrey'}}
|
||||
row={Math.floor(i / colsCount) + 1}
|
||||
col={(i % colsCount) * 2 + 1}
|
||||
style={headerCellStyle}
|
||||
>
|
||||
{column.title}
|
||||
</GridItem>
|
||||
|
||||
<GridItem
|
||||
key={column.title}
|
||||
row={row}
|
||||
col={colb*2 + 2}
|
||||
style={{border:'1px solid lightgrey',
|
||||
justifyContent:'right',
|
||||
marginRight:'16px',
|
||||
fontWeight:'bold',
|
||||
textAlign:'right',
|
||||
padding: 0}}
|
||||
row={Math.floor(i / colsCount) + 1}
|
||||
col={(i % colsCount) * 2 + 2}
|
||||
style={valueCellStyle}
|
||||
>
|
||||
{renderSwitchableColumn(column, item)}
|
||||
{column.render ? (
|
||||
<Form.Item
|
||||
key={column.dataIndex}
|
||||
name={column.dataIndex}
|
||||
style={{ margin: 0 }}
|
||||
//rules={column.formItemRules}
|
||||
>
|
||||
{column.render(item[column.dataIndex])}
|
||||
</Form.Item>
|
||||
) : (
|
||||
<p key={column.title} className='m-5px'>{item[column.dataIndex]}</p>
|
||||
)}
|
||||
</GridItem>
|
||||
</>
|
||||
})
|
||||
|
||||
return <>
|
||||
<Grid>
|
||||
{viewItems}
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
}
|
||||
)
|
||||
})
|
@ -1,63 +1,98 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { columnsMudDiagram} from './mudDiagramData'
|
||||
import { mudDiagramDefaultData} from './mudDiagramData'
|
||||
import { columnsDrillingFluid} from './drillingFluidData'
|
||||
import { drillingFluidDefaultData} from './drillingFluidData'
|
||||
import { columnsNnb } from './nnbData'
|
||||
import { nnbDefaultData } from './nnbData'
|
||||
import { columnsNnb, nnbDefaultData } from './nnbData'
|
||||
import { columnsMudDiagram, mudDiagramDefaultData } from './mudDiagramData'
|
||||
import { columnsDrillingFluid, drillingFluidDefaultData } from './drillingFluidData'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import { MeasureService } from '../../services/api'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { MeasureTable } from './MeasureTable'
|
||||
import InclinometryTable from './InclinometryTable'
|
||||
import { Button } from 'antd'
|
||||
import { TableOutlined } from '@ant-design/icons'
|
||||
|
||||
const defaultData = [
|
||||
{
|
||||
idCategory: 1,
|
||||
title: 'Замер бурового раствора',
|
||||
columns: columnsDrillingFluid,
|
||||
values: [drillingFluidDefaultData],
|
||||
defaultValue: drillingFluidDefaultData,
|
||||
},
|
||||
{
|
||||
idCategory: 2,
|
||||
title: 'Шламограмма',
|
||||
columns: columnsMudDiagram,
|
||||
values: [mudDiagramDefaultData],
|
||||
defaultValue: mudDiagramDefaultData,
|
||||
},
|
||||
{
|
||||
idCategory: 3,
|
||||
title: 'ННБ',
|
||||
columns: columnsNnb,
|
||||
values: [nnbDefaultData],
|
||||
defaultValue: nnbDefaultData,
|
||||
}
|
||||
]
|
||||
|
||||
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 [data, setData] = useState(defaultData)
|
||||
const [tableIdx, setTableIdx] = useState(-1)
|
||||
|
||||
const updateCurrentValues = () => invokeWebApiWrapperAsync(async()=>{
|
||||
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}`)
|
||||
setData(prevData => {
|
||||
prevData.forEach(el => el.values = [])
|
||||
measures.forEach(el => {
|
||||
const idx = prevData.findIndex(group => el.idCategory === group.idCategory)
|
||||
if (idx >= 0)
|
||||
prevData[idx].values.push(el)
|
||||
})
|
||||
return prevData
|
||||
})
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить последние данные по скважине ${idWell}`
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setData(prevData => {
|
||||
data[2].additionalButtons = (group, idx) => (isEditing) => isEditing ? null : (
|
||||
<Button
|
||||
key={'table'}
|
||||
className={'flex-1'}
|
||||
onClick={() => setTableIdx(idx)}
|
||||
disabled={!group.values || group.values.length <= 0}
|
||||
>
|
||||
<TableOutlined />
|
||||
</Button>
|
||||
)
|
||||
|
||||
return prevData
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(updateCurrentValues, [idWell, isMeasuresUpdating])
|
||||
|
||||
return <>
|
||||
<LoaderPortal show={showLoader}>
|
||||
{data.map((group, idx) => (
|
||||
<MeasureTable
|
||||
key={idx}
|
||||
idWell={idWell}
|
||||
idCategory={1}
|
||||
title='Замер бурового раствора'
|
||||
columns={columnsDrillingFluid}
|
||||
values={fluidValues}
|
||||
group={group}
|
||||
updateMeasuresFunc={() => setIsMeasuresUpdating(true)}
|
||||
additionalButtons={group.additionalButtons?.(group, idx)}
|
||||
/>
|
||||
<MeasureTable
|
||||
idWell={idWell}
|
||||
idCategory={2}
|
||||
title='Шламограмма'
|
||||
columns={columnsMudDiagram}
|
||||
values={mudValues}
|
||||
updateMeasuresFunc = {() => setIsMeasuresUpdating(true)}
|
||||
/>
|
||||
<MeasureTable
|
||||
idWell={idWell}
|
||||
idCategory={3}
|
||||
title='ННБ'
|
||||
columns={columnsNnb}
|
||||
values={nnbValues}
|
||||
updateMeasuresFunc = {() => setIsMeasuresUpdating(true)}
|
||||
))}
|
||||
<InclinometryTable
|
||||
visible={tableIdx >= 0}
|
||||
onClose={() => setTableIdx(-1)}
|
||||
group={data[tableIdx]}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
</>
|
||||
|
@ -23,6 +23,10 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.w-15 {
|
||||
width: 15%
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user