* Исправлено использование контекста в рапортах

* Готов рабочий образец суточного рапорта
This commit is contained in:
goodmice 2022-04-19 18:57:19 +05:00
parent b2d8b7729c
commit 006ee7878f
3 changed files with 224 additions and 106 deletions

View File

@ -1,58 +1,119 @@
import { useForm } from 'antd/lib/form/Form' import moment from 'moment'
import { ConfigProvider, Descriptions, Divider, Form, Input, InputNumber, Modal, Select, Space, Table, Tag } from 'antd' import { DatePicker, Descriptions, Divider, Form, Input, InputNumber, Modal, Select, Space, Table } from 'antd'
import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { memo, useCallback, useContext, useEffect, useState } from 'react'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { DatePickerWrapper, makeColumn, makeGroupColumn } from '@components/Table' import { makeColumn, makeGroupColumn } from '@components/Table'
import { formatDate } from '@utils' import { DailyReportService } from '@api'
const { Item } = Form import { IdWellContext } from '@pages/Well'
const { Item: RawItem } = Form
const { Summary } = Table const { Summary } = Table
const Item = memo(({ style, ...other }) => <RawItem style={{ margin: 0, ...style }} {...other} />)
const table1Columns = [ const table1Columns = [
makeGroupColumn('Отчётный период', [ makeGroupColumn('Отчётный период', [
makeColumn('От (дата, время)', 'dateStart', { width: 200, render: (date) => formatDate(date) ?? '---' }), makeColumn('От (дата, время)', 'dateStart', { width: 200, render: (disabled) => (
<Item name={'reportDate'}>
<DatePicker disabled={disabled} format={'DD.MM.YYYY'} style={{ width: '100%' }} />
</Item>
)}),
]), ]),
makeGroupColumn('Забой за отчётный период, м', [ makeGroupColumn('Забой за отчётный период, м', [
makeColumn('От', 'depthStart', { width: 200, render: () => ( makeColumn('От', 'depthStart', { width: 200, render: () => (
<Item name={'wellDepthIntervalStartDate'} style={{ margin: 0 }}> <Item name={'wellDepthIntervalStartDate'}>
<InputNumber style={{ width: '100%' }} /> <InputNumber style={{ width: '100%' }} />
</Item> </Item>
)}), )}),
makeColumn('До', 'depthEnd', { width: 200, render: () => ( makeColumn('До', 'depthEnd', { width: 200, render: () => (
<Item name={'wellDepthIntervalEndDate'} style={{ margin: 0 }}> <Item name={'wellDepthIntervalFinishDate'}>
<InputNumber style={{ width: '100%' }} /> <InputNumber style={{ width: '100%' }} />
</Item> </Item>
)}), )}),
]) ])
] ]
const cellData = {
width: 200,
render: (name) => (
<Item name={name}>
<InputNumber style={{ width: '100%' }} />
</Item>
)
}
const table2Columns = [ const table2Columns = [
makeColumn('Работа модулей САУБ', 'label', { width: 500 }), makeColumn('Работа модулей САУБ', 'label', { width: 400 }),
makeColumn('Часов:', 'hours', { width: 200, render: (data) => ( makeColumn('Часов:', 'hours', cellData),
<Item name={data.name} style={{ margin: 0 }}> makeColumn('Метров:', 'meters', cellData),
<InputNumber style={{ width: '100%' }} />
</Item>
)}),
makeColumn('Метров:', 'meters', { width: 200, render: (data) => (
<Item name={data.name} style={{ margin: 0 }}>
<InputNumber style={{ width: '100%' }} />
</Item>
)}),
] ]
const table2Data = [ const table2Data = [
{ label: 'АПД (автоматическая подача долота), ч/м:', hours: 'workTimeSaub', meters: 'drilledMetersSaub' }, { label: 'АПД (автоматическая подача долота), ч/м:', hours: 'workTimeSAUB', meters: 'penetrationSAUB' },
{ label: 'Спин Мастер (осцилляция), ч/м:', hours: 'workTimeSpinMaster', meters: 'drilledMetersSpinMaster' }, { label: 'Спин Мастер (осцилляция), ч/м:', hours: 'workTimeSpinMaster', meters: 'penetrationSpinMaster' },
{ label: 'Торк Мастер (демпфирование), ч/м:', hours: 'workTimeTorkMaster', meters: 'drilledMetersTorkMaster' }, { label: 'Торк Мастер (демпфирование), ч/м:', hours: 'workTimeTorkMaster', meters: 'penetrationTorkMaster' },
] ]
const table3Columns = [
makeGroupColumn('Бурение в роторе (за отчётный период) с использованием САУБ-1', [
makeColumn('Проходка', 'sinking', cellData),
makeColumn('Часы бурения', 'hours', cellData),
makeColumn('Среднее диф. Давление', 'pressure', cellData),
]),
]
const table3Data = [{ sinking: 'penetrationInRotor', hours: 'numberDrillingHours', pressure: 'avgDiffDropRotor' }]
const table4Columns = [
makeGroupColumn('Бурение в слайде (за отчётный период) с использованием САУБ-1', [
makeColumn('Проходка', 'sinking', cellData),
makeColumn('Часы бурения', 'hours', cellData),
makeColumn('Среднее диф. Давление', 'pressure', cellData),
]),
]
const table4Data = [{ sinking: 'penetrationInSlide', hours: 'drillingTimeInRotor', pressure: 'avgDiffPressureSlide' }]
const table6Columns = [
makeGroupColumn('БЕЗМЕТРАЖНЫЕ РАБОТЫ', [
makeColumn('Подготовка ствола скважины к наращиванию', 'l_label', { colSpan: 2, width: 200 }),
makeColumn('', 'l_value', { colSpan: 0, ...cellData }),
makeColumn('Наращивание', 'r_label', { colSpan: 2, width: 200 }),
makeColumn('', 'r_value', { colSpan: 0, ...cellData }),
]),
]
const table6Data = [{
l_label: 'Норматив на одну операцию, (мин):',
r_label: 'Норматив на одну операцию, (мин):',
l_value: 'standardTimeBarrelPreparation',
r_value: 'standardTimeExtension'
}, {
l_label: 'Проработка при бур, факт (ч):',
r_label: 'Наращивание, факт (ч):',
l_value: 'actualTimeBarrelPreparation',
r_value: 'actualTimeExtension'
}]
const table6Summary = () => (
<Summary.Row>
<Summary.Cell colSpan={2}>
Краткие причины: доп проработки при переходе из слайда в ротор, в середине свечи.
</Summary.Cell>
<Summary.Cell colSpan={2}>
Краткие причины: нехватка пальцев на обоих руках у первого помощника бурильщика.
</Summary.Cell>
</Summary.Row>
)
const table2Summary = () => ( const table2Summary = () => (
<Summary.Row> <Summary.Row>
<Summary.Cell>МСЕ, колличество запусков, раз:</Summary.Cell> <Summary.Cell>МСЕ, колличество запусков, раз:</Summary.Cell>
<Summary.Cell colSpan={2}> <Summary.Cell colSpan={2}>
<Item name={'countLaunchesMSE'} style={{ margin: 0 }}> <Item name={'countLaunchesMSE'}>
<InputNumber style={{ width: '100%' }} /> <InputNumber style={{ width: '100%' }} />
</Item> </Item>
</Summary.Cell> </Summary.Cell>
@ -60,27 +121,36 @@ const table2Summary = () => (
) )
export const ReportEditor = memo(({ visible, data, onDone, onCancel }) => { export const ReportEditor = memo(({ visible, data, onDone, onCancel }) => {
const [form] = useForm() const [form] = Form.useForm()
const [isInvalid, setIsInvalid] = useState(false) const [isInvalid, setIsInvalid] = useState(false)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
useEffect(() => form.setFieldsValue(data), [data]) const idWell = useContext(IdWellContext)
useEffect(() => form.setFieldsValue(data ? {
...data,
rotorDrillingModes: Array.isArray(data.rotorDrillingModes) ? data.rotorDrillingModes ?? [] : [],
slideDrillingModes: Array.isArray(data.slideDrillingModes) ? data.slideDrillingModes ?? [] : [],
reportDate: moment.utc(data.reportDate).local(),
} : {}), [data, form])
const onFormChange = useCallback(async () => await form.validateFields() const onFormChange = useCallback(async () => await form.validateFields()
.then(() => setIsInvalid(false)) .then(() => setIsInvalid(false))
.catch(() => setIsInvalid(true)) .catch(() => setIsInvalid(true))
, [form]) , [form])
const onFormFinish = useCallback((data) => invokeWebApiWrapperAsync( const onFormFinish = useCallback((formData) => invokeWebApiWrapperAsync(
async () => { async () => {
onDone?.(data) if (data)
await DailyReportService.update(idWell, data.reportDate, formData)
else
await DailyReportService.add(idWell, formData)
onDone?.(formData)
}, },
setIsLoading, setIsLoading,
'Не удалось сохранить суточный рапорт', 'Не удалось сохранить суточный рапорт',
'Сохранение суточного рапорта', 'Сохранение суточного рапорта',
), [onDone]) ), [data, onDone, idWell])
const table1Data = useMemo(() => [{ dateStart: form.getFieldValue('reportDate') }], [form])
return ( return (
<Modal <Modal
@ -89,7 +159,10 @@ export const ReportEditor = memo(({ visible, data, onDone, onCancel }) => {
visible={visible} visible={visible}
onCancel={onCancel} onCancel={onCancel}
okText={'Сохранить'} okText={'Сохранить'}
title={data ? `Суточная сводка бурения скважины № ${data.wellName} куст № ${data.clusterName}`: 'Новая суточная сводка бурения'} title={data ?
<>Суточная сводка бурения скважины {data?.wellName} куст {data?.clusterName}</> :
<>Новая cуточная сводка бурения скважины</>
}
onOk={form.submit} onOk={form.submit}
okButtonProps={{ okButtonProps={{
disabled: isInvalid, disabled: isInvalid,
@ -103,49 +176,87 @@ export const ReportEditor = memo(({ visible, data, onDone, onCancel }) => {
onChange={onFormChange} onChange={onFormChange}
> >
<Item name={'reportDate'} label={'Дата рапорта'} rules={[{ required: true }]}> <Item name={'reportDate'} label={'Дата рапорта'} rules={[{ required: true }]}>
<DatePickerWrapper showTime={false} disabled={!!data}/> <DatePicker disabled={!!data} format={'DD.MM.YYYY'}/>
</Item> </Item>
<Divider /> <Divider />
<p>Суточная сводка бурения скважины {data?.wellName} куст {data?.clusterName}</p> <Space direction={'vertical'} size={'middle'} style={{ width: '100%' }}>
<Item name={'customer'} label={'Заказчик'}><Input /></Item> <Item name={'customer'} label={'Заказчик'}><Input /></Item>
<Item name={'contractor'} label={'Подрядчик'}><Input /></Item> <Item name={'contractor'} label={'Подрядчик'}><Input /></Item>
<Table <Table
bordered bordered
size={'small'} size={'small'}
columns={table1Columns} columns={table1Columns}
dataSource={table1Data} dataSource={[{ dateStart: !!data }]}
pagination={false} pagination={false}
style={{ marginBottom: '15px' }} />
/> <Item label={'Бурильщик 1 смена'} name={'firstDriller'}><Input /></Item>
<Item label={'Бурильщик 1 смена'} name={'firstDriller'}><Input /></Item> <Item label={'Бурильщик 2 смена'} name={'secondDriller'}><Input /></Item>
<Item label={'Бурильщик 2 смена'} name={'firstDriller'}><Input /></Item>
<Table <Table
bordered bordered
size={'small'} size={'small'}
columns={table2Columns} columns={table2Columns}
dataSource={table2Data} dataSource={table2Data}
summary={table2Summary} summary={table2Summary}
pagination={false} pagination={false}
style={{ marginBottom: '15px' }} />
/>
<Item name={'bhaDescription'}> <Item name={'bhaDescription'} label={'Описание КНБК'}>
<Input.TextArea rows={4} /> <Input.TextArea rows={4} />
</Item> </Item>
<Descriptions column={1} size={'small'} bordered> <Table bordered size={'small'} columns={table6Columns} dataSource={table6Data} pagination={false} summary={table6Summary} />
<Descriptions.Item label={'Бурение в роторе:'}>
<Item name={'rotaryDrillingModes'} style={{ margin: 0 }}> <Descriptions column={1} size={'small'} bordered>
<Select mode={'tags'} style={{ width: '100%' }} /> <Descriptions.Item label={'Бурение в роторе:'}>
</Item> <Item name={'rotorDrillingModes'}>
</Descriptions.Item> <Select mode={'tags'} style={{ width: '100%' }} />
<Descriptions.Item label={'Бурение в слайде:'}> </Item>
<Item name={'slideDrillingModes'} style={{ margin: 0 }}> </Descriptions.Item>
<Select mode={'tags'} style={{ width: '100%' }} /> <Descriptions.Item label={'Бурение в слайде:'}>
</Item> <Item name={'slideDrillingModes'}>
</Descriptions.Item> <Select mode={'tags'} style={{ width: '100%' }} />
</Descriptions> </Item>
</Descriptions.Item>
</Descriptions>
<Table bordered size={'small'} columns={table3Columns} dataSource={table3Data} pagination={false} />
<Table bordered size={'small'} columns={table4Columns} dataSource={table4Data} pagination={false} />
<Item label={'Плановая мех скорость'} name={'sectionROPPlan'}>
<InputNumber style={{ width: '100%' }}/>
</Item>
<Descriptions bordered size={'small'} column={1}>
<Descriptions.Item label={'Время бурения за секцию'}>
<Item name={'sectionDrillingTimeTotal'}>
<InputNumber style={{ width: '100%' }} />
</Item>
</Descriptions.Item>
<Descriptions.Item label={'Проходка за секцию'}>
<Item name={'sectionPenetrationTotal'}>
<InputNumber style={{ width: '100%' }} />
</Item>
</Descriptions.Item>
<Descriptions.Item label={'Кол-во наращиваний'}>
<Item name={'extensionsCount'}>
<InputNumber style={{ width: '100%' }} />
</Item>
</Descriptions.Item>
<Descriptions.Item label={'Отклонение от ГГД +/-, сут'}>
<Item name={'deviationFromTVD'}>
<InputNumber style={{ width: '100%' }} />
</Item>
</Descriptions.Item>
</Descriptions>
<Item name={'declinesReasonsROP'} label={'Примечание:'}>
<Input.TextArea rows={4} />
</Item>
<Item label={'Мастер буровой'} name={'drillingMaster'}><Input /></Item>
<Item label={'Супервайзер'} name={'supervisor'}><Input /></Item>
</Space>
</Form> </Form>
</LoaderPortal> </LoaderPortal>
</Modal> </Modal>

View File

@ -1,26 +1,30 @@
import moment from 'moment' import moment from 'moment'
import { Button } from 'antd' import { Button } from 'antd'
import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { FileExcelOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons' import { FileExcelOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import DateRangeWrapper from '@components/Table/DateRangeWrapper' import DateRangeWrapper from '@components/Table/DateRangeWrapper'
import { Table, makeDateColumn, makeColumn } from '@components/Table' import { Table, makeDateColumn, makeColumn } from '@components/Table'
import { download, invokeWebApiWrapperAsync } from '@components/factory' import { download, invokeWebApiWrapperAsync } from '@components/factory'
import { DailyReportService } from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import ReportEditor from './ReportEditor' import ReportEditor from './ReportEditor'
import { IdWellContext } from '@pages/Well'
export const DailyReport = memo(({ idWell }) => { export const DailyReport = memo(() => {
const [data, setData] = useState([]) const [data, setData] = useState([])
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [searchDate, setSearchDate] = useState(null) const [searchDate, setSearchDate] = useState(null)
const [selectedReport, setSelectedReport] = useState(null) const [selectedReport, setSelectedReport] = useState(null)
const [isEditorVisible, setIsEditorVisible] = useState(true) const [isEditorVisible, setIsEditorVisible] = useState(false)
useEffect(() => invokeWebApiWrapperAsync( const idWell = useContext(IdWellContext)
const updateTable = useCallback(() => invokeWebApiWrapperAsync(
async () => { async () => {
const data = [{ date: '2022-10-01' }] // arrayOrDefault(await DailyreportService.getData(idWell)) const data = arrayOrDefault(await DailyReportService.getList(idWell))
setData(data) setData(data)
}, },
setIsLoading, setIsLoading,
@ -28,14 +32,16 @@ export const DailyReport = memo(({ idWell }) => {
'Получение списка суточных рапортов', 'Получение списка суточных рапортов',
), [idWell]) ), [idWell])
useEffect(updateTable, [updateTable])
const columns = useMemo(() => [ const columns = useMemo(() => [
makeDateColumn('Дата', 'date', undefined, undefined, { width: 300 }), makeDateColumn('Дата', 'reportDate', undefined, undefined, { width: 300 }),
makeColumn('', '', { width: 200, render: (_, report, idx) => ( makeColumn('', '', { width: 200, render: (_, report) => (
<> <>
<Button <Button
type={'link'} type={'link'}
icon={<FileExcelOutlined />} icon={<FileExcelOutlined />}
onClick={async () => await download(`/api/daily_report/${report.id}`)} onClick={async () => await download(`/api/well/${idWell}/DailyReport/${report.reportDate}/excel`)}
> >
Скачать XLSX Скачать XLSX
</Button> </Button>
@ -51,7 +57,7 @@ export const DailyReport = memo(({ idWell }) => {
</Button> </Button>
</> </>
)}), )}),
], []) ], [idWell])
const filteredData = useMemo(() => { const filteredData = useMemo(() => {
if (!searchDate) return data if (!searchDate) return data
@ -89,6 +95,10 @@ export const DailyReport = memo(({ idWell }) => {
data={selectedReport} data={selectedReport}
visible={isEditorVisible} visible={isEditorVisible}
onCancel={() => setIsEditorVisible(false)} onCancel={() => setIsEditorVisible(false)}
onDone={() => {
setIsEditorVisible(false)
updateTable()
}}
/> />
</> </>
) )

View File

@ -1,43 +1,40 @@
import { Switch, useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { memo, useMemo } from 'react' import { memo, useContext, useMemo } from 'react'
import { Layout, Menu } from 'antd'
import { FilePdfOutlined } from '@ant-design/icons' import { FilePdfOutlined } from '@ant-design/icons'
import { Layout } from 'antd'
import { PrivateMenuItemLink, PrivateRoute, PrivateDefaultRoute } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import DailyReport from './DailyReport' import DailyReport from './DailyReport'
import DiagramReport from './DiagramReport' import DiagramReport from './DiagramReport'
import { RootPathContext } from '@pages/Main'
const { Content } = Layout const { Content } = Layout
export const Reports = memo(({ idWell }) => { export const Reports = memo(() => {
const { tab } = useParams() const { tab } = useParams()
const rootPath = useMemo(() => `/well/${idWell}/reports`, [idWell])
const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/reports`, [root])
return ( return (
<Layout> <RootPathContext.Provider value={rootPath}>
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
<PrivateMenuItemLink root={rootPath} key={'diagram_report'} path={'diagram_report'} icon={<FilePdfOutlined />} title={'Диаграмма'}/>
<PrivateMenuItemLink root={rootPath} key={'daily_report'} path={'daily_report'} title={'Суточный рапорт'} />
</Menu>
<Layout> <Layout>
<Content className={'site-layout-background'}> <PrivateMenu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
<Switch> <PrivateMenu.Link key={'diagram_report'} icon={<FilePdfOutlined />} title={'Диаграмма'}/>
<PrivateRoute path={`${rootPath}/diagram_report`}> <PrivateMenu.Link key={'daily_report'} title={'Суточный рапорт'} />
<DiagramReport idWell={idWell} /> </PrivateMenu>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/daily_report`}> <Layout>
<DailyReport idWell={idWell} /> <Content className={'site-layout-background'}>
</PrivateRoute> <PrivateSwitch elseRedirect={['diagram_report', 'daily_report']}>
<PrivateDefaultRoute urls={[ <DiagramReport key={'diagram_report'} />
`${rootPath}/diagram_report`, <DailyReport key={'daily_report'} />
`${rootPath}/daily_report`, </PrivateSwitch>
]}/> </Content>
</Switch> </Layout>
</Content>
</Layout> </Layout>
</Layout> </RootPathContext.Provider>
) )
}) })