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