Добавлено отображение секций

This commit is contained in:
goodmice 2021-10-14 11:07:11 +05:00
parent 2f63fd6668
commit c4b3a3f980
2 changed files with 398 additions and 81 deletions

View File

@ -0,0 +1,293 @@
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
import { Table, Tag, Button, Badge, Divider, Modal, Row, Col } from 'antd'
import { Link } from 'react-router-dom'
import { useState, useEffect } from 'react'
import {
makeColumn,
makeTextColumn,
makeNumericColumnPlanFact,
makeNumericAvgRange,
SelectFromDictionary
} from '../../../components/Table'
import {
calcAndUpdateStatsBySections,
makeFilterMinMaxFunction,
getOperations
} from '../../Cluster/functions'
import ChartTvD from '../ChartTvD'
import { DrillParamsService } from '../../../services/api'
import LoaderPortal from '../../../components/LoaderPortal'
import WellOperationsTable from '../../Cluster/WellOperationsTable'
import { invokeWebApiWrapperAsync } from '../../../components/factory'
import { dictionarySectionType, getByKeyOrReturnKey } from '../dictionary'
const filtersMinMax = [
{ text: "min", value: "min" },
{ text: "max", value: "max" },
]
const filtersSectionsType = []
const DAY_IN_MS = 1000 * 60 * 60 * 24
export const WellCompositeSections = ({idWell, statsWells, selectedSections}) => {
const [showLoader, setShowLoader] = useState(false)
const [selectedWellId, setSelectedWellId] = useState(0)
const [selectedWells, setSelectedWells] = useState([])
const [selectedWellsKeys, setSelectedWellsKeys] = useState([])
const [isTVDModalVisible, setIsTVDModalVisible] = useState(false)
const [isOpsModalVisible, setIsOpsModalVisible] = useState(false)
const [isParamsModalVisible, setIsParamsModalVisible] = useState(false)
const [tvdDataPlan, setTvdDataPlan] = useState([])
const [tvdDataFact, setTvdDataFact] = useState([])
const [tvdDataForecast, setTvdDataForecast] = useState([])
const [wellOperations, setWellOperations] = useState([])
const [rows, setRows] = useState([])
const [params, setParams] = useState([])
useEffect(() => {
if (selectedWellId > 0) {
invokeWebApiWrapperAsync(
async () => {
const operations = await getOperations(selectedWellId);
setWellOperations(operations.operations)
setTvdDataPlan(operations.plan)
setTvdDataFact(operations.fact)
setTvdDataForecast(operations.predict)
},
setShowLoader,
`Не удалось загрузить операции по скважине "${selectedWellId}"`,
)
}
}, [selectedWellId])
useEffect(() => {
const rows = []
statsWells?.forEach((well) => {
well.sections?.forEach((section) => {
if (!filtersSectionsType.some((el) => el.text === section.caption))
filtersSectionsType.push({ text: section.caption, value: section.caption })
let row = {
key: well.caption + section.id,
id: well.id,
caption: well.caption,
sectionType: section.caption,
sectionWellDepthPlan: section.plan?.wellDepthEnd,
sectionWellDepthFact: section.fact?.wellDepthEnd,
sectionBuildDaysPlan: (new Date(section.plan?.end) - new Date(section.plan?.start)) / DAY_IN_MS,
sectionBuildDaysFact: (new Date(section.fact?.end) - new Date(section.fact?.start)) / DAY_IN_MS,
sectionRateOfPenetrationPlan: section.plan?.rop,
sectionRateOfPenetrationFact: section.fact?.rop,
sectionRouteSpeedPlan: section.plan?.routeSpeed,
sectionRouteSpeedFact: section.fact?.routeSpeed,
sectionBhaDownSpeedPlan: section.plan?.bhaDownSpeed,
sectionBhaDownSpeedFact: section.fact?.bhaDownSpeed,
sectionBhaUpSpeedPlan: section.plan?.bhaUpSpeed,
sectionBhaUpSpeedFact: section.fact?.bhaUpSpeed,
sectionCasingDownSpeedPlan: section.plan?.casingDownSpeed,
sectionCasingDownSpeedFact: section.fact?.casingDownSpeed,
nonProductiveTimePlan: section.plan?.nonProductiveHours,
nonProductiveTimeFact: section.fact?.nonProductiveHours,
companies: well.companies,
}
rows.push(row)
})
})
calcAndUpdateStatsBySections(rows ?? [], [
"sectionWellDepthPlan",
"sectionWellDepthFact",
"sectionBuildDaysPlan",
"sectionBuildDaysFact",
"sectionRateOfPenetrationPlan",
"sectionRateOfPenetrationFact",
"sectionRouteSpeedPlan",
"sectionRouteSpeedFact",
"sectionBhaDownSpeedPlan",
"sectionBhaDownSpeedFact",
"sectionBhaUpSpeedPlan",
"sectionBhaUpSpeedFact",
"sectionCasingDownSpeedPlan",
"sectionCasingDownSpeedFact",
"nonProductiveTimePlan",
"nonProductiveTimeFact",
])
setRows(rows)
}, [statsWells])
useEffect(() => {
}, [selectedSections])
const columns = [
makeTextColumn("скв №", "caption", null, null,
(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("НПВ, сут", "nonProductiveTime", filtersMinMax, makeFilterMinMaxFunction, null, "70px"),
{
title: "TVD",
render: (value) => <Button onClick={()=> {
setSelectedWellId(value.id)
setIsTVDModalVisible(true)
}}><LineChartOutlined /></Button>,
align: 'center'
},
{
title: "Операции",
render: (value) => <Button onClick={()=> {
setSelectedWellId(value.id)
setIsOpsModalVisible(true)
}}><ProfileOutlined /></Button>,
align: 'center'
},
{
title: "Подрядчики",
dataIndex: "companies",
render: (item) =>
item?.map((company) => <Tag key={company.caption} color="blue">{company.caption}</Tag>),
},
]
const paramsColumns = [
makeColumn('Конструкция секции','idWellSectionType', {
editable:true,
input:<SelectFromDictionary dictionary={dictionarySectionType}/>,
width:160,
render:(_, record)=>getByKeyOrReturnKey(dictionarySectionType, record.idWellSectionType)
}),
// makeNumericStartEnd('Глубина', 'depth'),
makeNumericAvgRange('Нагрузка', 'axialLoad'),
makeNumericAvgRange('Давление', 'pressure'),
makeNumericAvgRange('Момент на ВПС', 'rotorTorque'),
makeNumericAvgRange('Обороты на ВПС', 'rotorSpeed'),
makeNumericAvgRange('Расход', 'flow')
]
const rowSelection = {
selectedRowKeys: selectedWellsKeys,
onChange: (keys, items) => {
setSelectedWells(items)
setSelectedWellsKeys(keys)
},
}
const onParamButtonClick = () => invokeWebApiWrapperAsync(
async () => {
const params = await DrillParamsService.getCompositeAll(idWell)
setParams(params)
setIsParamsModalVisible(true)
},
setShowLoader,
`Не удалось загрузить список режимов для скважины ${idWell}`
)
const onParamsAddClick = () => invokeWebApiWrapperAsync(
async () => {
await DrillParamsService.insertRange(idWell, params)
setIsParamsModalVisible(false)
},
setShowLoader,
`Не удалось добавить режимы в список скважины ${idWell}`
)
return (
<>
<Table
columns={columns}
dataSource={rows}
size={"small"}
bordered
scroll={{ x: true, y: 620 }}
rowSelection={rowSelection}
pagination={false}
/>
<Divider />
<Badge.Ribbon text="комбинированная скважина" color="gray">
<h3>Выбранные секции</h3>
</Badge.Ribbon>
<Table
columns={columns}
dataSource={selectedWells}
rowSelection={rowSelection}
size={"small"}
bordered
scroll={{ x: true }}
pagination={false}
/>
<Row justify={'end'} style={{margin: '1rem 0'}}><Col>
<Button
size={'large'}
disabled={selectedWells.length <= 0}
onClick={onParamButtonClick}
>Режимы</Button>
</Col></Row>
<Modal
title='TVD'
centered
visible={isTVDModalVisible}
onCancel={() => setIsTVDModalVisible(false)}
width={1500}
footer={null}
>
<LoaderPortal show={showLoader}>
<ChartTvD
dataPlan={tvdDataPlan}
dataFact={tvdDataFact}
dataPredict={tvdDataForecast} />
</LoaderPortal>
</Modal>
<Modal
title='Операции'
centered
visible={isOpsModalVisible}
onCancel={() => setIsOpsModalVisible(false)}
width={1500}
footer={null}
>
<LoaderPortal show={showLoader}>
<WellOperationsTable wellOperations={wellOperations} />
</LoaderPortal>
</Modal>
<Modal
title={'Режимы'}
centered
visible={isParamsModalVisible}
onCancel={() => setIsParamsModalVisible(false)}
width={1700}
footer={
<Button
size={'large'}
disabled={params.length <= 0}
onClick={onParamsAddClick}
>Добавить</Button>
}
>
<LoaderPortal show={showLoader}>
<Table
size={'small'}
bordered
columns={paramsColumns}
dataSource={params}
pagination={false}
/>
</LoaderPortal>
</Modal>
</>
);
}

View File

@ -1,12 +1,12 @@
import { Layout, Menu, TreeSelect } from 'antd' import { Col, Layout, Menu, Row, Tag, TreeSelect } from 'antd'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { Redirect, Route, Switch, Link, useParams } from 'react-router-dom' import { Redirect, Route, Switch, Link, useParams } from 'react-router-dom'
import { DepositService } from '../../../services/api' import { DepositService, WellCompositeService } from '../../../services/api'
import { invokeWebApiWrapperAsync } from '../../../components/factory' import { invokeWebApiWrapperAsync } from '../../../components/factory'
import LoaderPortal from '../../../components/LoaderPortal' import LoaderPortal from '../../../components/LoaderPortal'
import { WellCompositeInfo } from './WellCompositeInfo'
import { WellCompositeSections } from './WellCompositeSections' import { WellCompositeSections } from './WellCompositeSections'
import WellOperationsTable from '../../Cluster/WellOperationsTable' import { WellOperationStatService } from '../../../services/api'
import ClusterWells from '../../Cluster/ClusterWells'
const { Content } = Layout const { Content } = Layout
@ -16,62 +16,76 @@ export const WellCompositeEditor = ({idWell}) => {
const [wellsTree, setWellsTree] = useState([]) const [wellsTree, setWellsTree] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [showTabLoader, setShowTabLoader] = useState(false)
const [selectedWells, setSelectedWells] = useState([]) const [selectedWells, setSelectedWells] = useState([])
const [selectedIdWells, setSelectedIdWells] = useState([]) const [selectedIdWells, setSelectedIdWells] = useState([])
const [statsWells, setStatsWells] = useState([])
const [selectedSections, setSelectedSections] = useState([])
const [wellLabels, setWellLabels] = useState([])
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => {
invokeWebApiWrapperAsync(
async () => { async () => {
const deposits = await DepositService.getDeposits() const deposits = await DepositService.getDeposits()
const labels = {}
const wellsTree = deposits.map((deposit, dIdx) => ({ const wellsTree = deposits.map((deposit, dIdx) => ({
title: deposit.caption, title: deposit.caption,
key: `${dIdx}`, key: `0-${dIdx}`,
value: `${dIdx}`, value: `0-${dIdx}`,
children: deposit.clusters.map((cluster, cIdx) => ({ children: deposit.clusters.map((cluster, cIdx) => ({
title: cluster.caption, title: cluster.caption,
key: `${dIdx}-${cIdx}`, key: `0-${dIdx}-${cIdx}`,
value: `${dIdx}-${cIdx}`, value: `0-${dIdx}-${cIdx}`,
children: cluster.wells.map(well => ({ children: cluster.wells.map(well => {
labels[well.id] = `${deposit.caption}.${cluster.caption}.${well.caption}`
return ({
title: well.caption, title: well.caption,
key: `${dIdx}-${cIdx}-${well.id}`, key: well.id,
value: `${dIdx}-${cIdx}-${well.id}`, value: well.id,
})), })
}),
})) }))
})) }))
setWellsTree(wellsTree) setWellsTree(wellsTree)
setWellLabels(labels)
try {
const selected = await WellCompositeService.get(idWell)
setSelectedSections(selected)
} catch(e) {
setSelectedSections([])
}
}, },
setShowLoader, setShowLoader,
'Не удалось загрузить список скважин' 'Не удалось загрузить список скважин'
), [idWell]) )
}, [idWell])
const expandIdx = (idx) => { useEffect(() => invokeWebApiWrapperAsync(
const res = /^(\d+)(?:-(\d+))?(?:-(\d+))?$/g.exec(idx) async () => {
const stats = await WellOperationStatService.getWellsStat(selectedIdWells)
if (res === null) return undefined setStatsWells(stats)
if (res[1] && res[2] && res[3]) },
return parseInt(res[3]) setShowTabLoader,
'Не удалось загрузить статистику по скважинам/секциям'
const depositIdx = parseInt(res[1]) ), [selectedIdWells])
if (res[2]) {
const clusterIdx = parseInt(res[2])
return wellsTree[depositIdx].children[clusterIdx].children.map(well => expandIdx(well.key)).flat()
}
return wellsTree[depositIdx].children.map(cluster => expandIdx(cluster.key)).flat()
}
const onWellChanged = (value) => { const onWellChanged = (value) => {
setSelectedWells(value) setSelectedWells(value)
setSelectedIdWells(value.map(item => expandIdx(item)).flat()) setSelectedIdWells(value)
} }
const tagRender = (props) => <Tag {...props}>{wellLabels[props.value] ?? props.label}</Tag>
return ( return (
<LoaderPortal show={showLoader}> <LoaderPortal show={showLoader}>
<div style={{display: 'flex', width: '100%', alignItems: 'center', justifyContent: 'space-between'}}> <Row align={'middle'} justify={'space-between'} wrap={false}>
<span style={{marginRight: '2'}}>Скважины:</span> <Col span={18}>
<TreeSelect <TreeSelect
multiple multiple
treeCheckable treeCheckable
showCheckedStrategy={TreeSelect.SHOW_PARENT} showCheckedStrategy={TreeSelect.SHOW_CHILD}
treeDefaultExpandAll treeDefaultExpandAll
treeData={wellsTree} treeData={wellsTree}
treeLine={{showLeafIcon: false}} treeLine={{showLeafIcon: false}}
@ -80,7 +94,10 @@ export const WellCompositeEditor = ({idWell}) => {
style={{flexGrow: '1'}} style={{flexGrow: '1'}}
value={selectedWells} value={selectedWells}
placeholder={'Выберите скважины'} placeholder={'Выберите скважины'}
tagRender={tagRender}
/> />
</Col>
<Col span={6}>
<Menu <Menu
mode="horizontal" mode="horizontal"
selectable={true} selectable={true}
@ -88,26 +105,33 @@ export const WellCompositeEditor = ({idWell}) => {
selectedKeys={[tab]} selectedKeys={[tab]}
> >
<Menu.Item key="wells"> <Menu.Item key="wells">
<Link to={`${rootPath}/wells`}>Сводка по скважинам</Link> <Link to={`${rootPath}/wells`}>Статистика по скважинам</Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="sections"> <Menu.Item key="sections">
<Link to={`${rootPath}/sections`}>Сводка по секциям</Link> <Link to={`${rootPath}/sections`}>Статистика по секциям</Link>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</div> </Col>
</Row>
<Layout> <Layout>
<Content className="site-layout-background"> <Content className="site-layout-background">
<LoaderPortal show={showTabLoader}>
<Switch> <Switch>
<Route path={`${rootPath}/wells`}> <Route path={`${rootPath}/wells`}>
<WellCompositeInfo idWell={idWell} selectedIdWells={selectedIdWells}/> <ClusterWells statsWells={statsWells}/>
</Route> </Route>
<Route path={`${rootPath}/sections`}> <Route path={`${rootPath}/sections`}>
<WellCompositeSections idWell={idWell}/> <WellCompositeSections
idWell={idWell}
statsWells={statsWells}
selectedSections={selectedSections}
/>
</Route> </Route>
<Route path={rootPath}> <Route path={rootPath}>
<Redirect to={`${rootPath}/wells`}/> <Redirect to={`${rootPath}/wells`}/>
</Route> </Route>
</Switch> </Switch>
</LoaderPortal>
</Content> </Content>
</Layout> </Layout>
</LoaderPortal> </LoaderPortal>