forked from ddrilling/asb_cloud_front
CF2-55: Added separated wells/well sections table
This commit is contained in:
parent
c13e60d25b
commit
81fcd3c0e5
@ -1,5 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useRouteMatch } from 'react-router-dom'
|
|
||||||
import { DepositService } from '../services/api'
|
import { DepositService } from '../services/api'
|
||||||
import LoaderPortal from './LoaderPortal'
|
import LoaderPortal from './LoaderPortal'
|
||||||
import { TreeSelect } from 'antd'
|
import { TreeSelect } from 'antd'
|
||||||
@ -11,7 +10,6 @@ export default function WellTreeSelector() {
|
|||||||
const [wellsTree, setWellsTree] = useState([])
|
const [wellsTree, setWellsTree] = useState([])
|
||||||
const [showLoader, setShowLoader] = useState(false)
|
const [showLoader, setShowLoader] = useState(false)
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const routeMatch = useRouteMatch('/:route/:id')
|
|
||||||
|
|
||||||
const updateWellsList = async () => {
|
const updateWellsList = async () => {
|
||||||
setShowLoader(true)
|
setShowLoader(true)
|
||||||
@ -23,8 +21,8 @@ export default function WellTreeSelector() {
|
|||||||
value: `/deposit/${deposit.id}`,
|
value: `/deposit/${deposit.id}`,
|
||||||
children: deposit.clusters.map(cluster => ({
|
children: deposit.clusters.map(cluster => ({
|
||||||
title: cluster.caption,
|
title: cluster.caption,
|
||||||
key: `/cluster/${cluster.id}`,
|
key: `/cluster/${cluster.id}/all`,
|
||||||
value: `/cluster/${cluster.id}`,
|
value: `/cluster/${cluster.id}/all`,
|
||||||
children: cluster.wells.map(well => ({
|
children: cluster.wells.map(well => ({
|
||||||
title: well.caption,
|
title: well.caption,
|
||||||
key: `/well/${well.id}`,
|
key: `/well/${well.id}`,
|
||||||
@ -46,7 +44,6 @@ export default function WellTreeSelector() {
|
|||||||
const onSelect = (value, node) => {
|
const onSelect = (value, node) => {
|
||||||
if (value)
|
if (value)
|
||||||
history.push(value);
|
history.push(value);
|
||||||
console.log(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -59,7 +56,6 @@ export default function WellTreeSelector() {
|
|||||||
treeData={wellsTree}
|
treeData={wellsTree}
|
||||||
treeDefaultExpandAll
|
treeDefaultExpandAll
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
value = {routeMatch?.url}
|
|
||||||
/>
|
/>
|
||||||
</LoaderPortal>
|
</LoaderPortal>
|
||||||
)
|
)
|
||||||
|
@ -1,144 +0,0 @@
|
|||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import LoaderPortal from '../components/LoaderPortal'
|
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import { ClusterService } from '../services/api'
|
|
||||||
import { notify } from "../components/factory"
|
|
||||||
import { Table, Tag, Button } from 'antd';
|
|
||||||
|
|
||||||
export default function Cluster() {
|
|
||||||
let { id } = useParams()
|
|
||||||
const [clusterTitle, setClusterTitle] = useState("")
|
|
||||||
const [wellsStat, setWellsStat] = useState(null)
|
|
||||||
const [showLoader, setShowLoader] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const updateWellsStat = async () => {
|
|
||||||
setShowLoader(true)
|
|
||||||
try {
|
|
||||||
const msInDay = 1000 * 60 * 60 * 24
|
|
||||||
const data = await ClusterService.getStat(id)
|
|
||||||
const wellsStat = data.wellsStat.map(w => ({
|
|
||||||
...w,
|
|
||||||
periodPlan: (new Date(w.planEnd) - new Date(w.planStart)) / msInDay,
|
|
||||||
periodFact: (new Date(w.factEnd) - new Date(w.factStart)) / msInDay,
|
|
||||||
}))
|
|
||||||
setWellsStat(wellsStat)
|
|
||||||
setClusterTitle(data.caption)
|
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
notify(`Не удалось загрузить статистику по скважинам куста "${id}"`, 'error')
|
|
||||||
console.log(ex)
|
|
||||||
}
|
|
||||||
setShowLoader(false)
|
|
||||||
}
|
|
||||||
updateWellsStat()
|
|
||||||
}, [id])
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'скв №',
|
|
||||||
key: 'caption',
|
|
||||||
dataIndex: 'caption',
|
|
||||||
render: (_, item) => <Link to={`/well/${item.id}`}>{item.caption}</Link>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Тип скв.',
|
|
||||||
key: 'wellType',
|
|
||||||
dataIndex: 'wellType',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Фактические сроки бурения',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'начало',
|
|
||||||
key: 'factStart',
|
|
||||||
dataIndex: 'factStart',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'окончание',
|
|
||||||
key: 'factEnd',
|
|
||||||
dataIndex: 'factEnd',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Продолжительность бурения',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'план',
|
|
||||||
key: 'periodPlan',
|
|
||||||
dataIndex: 'periodPlan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'факт',
|
|
||||||
key: 'periodFact',
|
|
||||||
dataIndex: 'periodFact',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'МСП за скв',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'план',
|
|
||||||
key: 'rateOfPenetrationPlan',
|
|
||||||
dataIndex: 'rateOfPenetrationPlan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'факт',
|
|
||||||
key: 'rateOfPenetrationFact',
|
|
||||||
dataIndex: 'rateOfPenetrationFact',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Рейсовая скорость за скв',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'план',
|
|
||||||
key: 'routeSpeedPlan',
|
|
||||||
dataIndex: 'routeSpeedPlan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'факт',
|
|
||||||
key: 'routeSpeedFact',
|
|
||||||
dataIndex: 'routeSpeedFact',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Секции',
|
|
||||||
key: 'sections',
|
|
||||||
dataIndex: 'sections',
|
|
||||||
render: (item) => (<span>таблица по секциям</span>)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'График глубина-день',
|
|
||||||
render: (_, item) => (<Button>Открыть</Button>)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Таблица по операциям',
|
|
||||||
render: _ => (<Button>Открыть</Button>)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Подрядчики',
|
|
||||||
key: 'companies',
|
|
||||||
dataIndex: 'companies',
|
|
||||||
render: (item) => item.map(company => <Tag color="blue">{company.caption}</Tag>)
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LoaderPortal show={showLoader}>
|
|
||||||
<h3>{clusterTitle}</h3>
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={wellsStat}
|
|
||||||
size={'small'}
|
|
||||||
bordered
|
|
||||||
pagination={false}
|
|
||||||
rowKey={(record) => record.id}
|
|
||||||
/>
|
|
||||||
</LoaderPortal>)
|
|
||||||
}
|
|
40
src/pages/ClusterInfo.jsx
Normal file
40
src/pages/ClusterInfo.jsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Layout, Menu } from "antd";
|
||||||
|
import { Link, Switch, Route, useParams } from "react-router-dom";
|
||||||
|
import ClusterWells from './ClusterWells'
|
||||||
|
import ClusterSections from './ClusterSections'
|
||||||
|
|
||||||
|
const { Content } = Layout;
|
||||||
|
|
||||||
|
export default function ClusterInfo() {
|
||||||
|
let { id } = useParams();
|
||||||
|
let {tab} = useParams()
|
||||||
|
|
||||||
|
return(<>
|
||||||
|
<Layout>
|
||||||
|
<Menu mode="horizontal"
|
||||||
|
selectable={true}
|
||||||
|
selectedKeys={[tab]}
|
||||||
|
className="well_menu">
|
||||||
|
<Menu.Item key="all">
|
||||||
|
<Link to={`/cluster/${id}/all`}>Скважины</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="sections">
|
||||||
|
<Link to={`/cluster/${id}/sections`}>Секции</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<Content className="site-layout-background">
|
||||||
|
<Switch>
|
||||||
|
<Route path="/cluster/:id/all">
|
||||||
|
<ClusterWells />
|
||||||
|
</Route>
|
||||||
|
<Route path="/cluster/:id/sections">
|
||||||
|
<ClusterSections />
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
|
</>)
|
||||||
|
}
|
264
src/pages/ClusterSections.jsx
Normal file
264
src/pages/ClusterSections.jsx
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
import { Table, Tag, Button, Badge, Divider, Modal} from "antd"
|
||||||
|
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
|
const isMaxPrefix = "isMax"
|
||||||
|
const isMinPrefix = "isMin"
|
||||||
|
|
||||||
|
const filtersMinMax = [
|
||||||
|
{
|
||||||
|
text: "min",
|
||||||
|
value: "min",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "max",
|
||||||
|
value: "max",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const filtersSectionsType = [
|
||||||
|
{
|
||||||
|
text: "Направление",
|
||||||
|
value: "Направление",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Кондуктор",
|
||||||
|
value: "Кондуктор",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Транспорт. ствол",
|
||||||
|
value: "Транспорт. Ствол",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Хвостовик",
|
||||||
|
value: "Хвостовик",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const makeFilterMinMaxFunction = (key) => (filterValue, dataItem) =>
|
||||||
|
filterValue === "max"
|
||||||
|
? dataItem[isMaxPrefix + key]
|
||||||
|
: filterValue === "min"
|
||||||
|
? dataItem[isMinPrefix + key]
|
||||||
|
: false
|
||||||
|
|
||||||
|
const makeFilterTextMatch = (key) => (filterValue, dataItem) =>
|
||||||
|
dataItem[key] === filterValue
|
||||||
|
|
||||||
|
const makeNumericSorter = (key) => (a, b) => a[key] - b[key]
|
||||||
|
|
||||||
|
const makeStringSorter = (key) => (a, b) =>
|
||||||
|
{
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
if (isNaN(b.charCodeAt(i)) || (a.charCodeAt(i) > b.charCodeAt(i)))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if (a.charCodeAt(i) > b.charCodeAt(i))
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeGroupColumn = (title, children) => ({
|
||||||
|
title: title,
|
||||||
|
children: children,
|
||||||
|
})
|
||||||
|
|
||||||
|
const makeTextColumn = (title, dataIndex, filters, sorter, render, other) => ({
|
||||||
|
title: title,
|
||||||
|
dataIndex: dataIndex,
|
||||||
|
key: dataIndex,
|
||||||
|
filters: filters,
|
||||||
|
onFilter: filters ? makeFilterTextMatch(dataIndex) : null,
|
||||||
|
sorter: sorter ? makeStringSorter(dataIndex) : null,
|
||||||
|
render: render,
|
||||||
|
...other
|
||||||
|
})
|
||||||
|
|
||||||
|
const makeNumericColumn = (title, dataIndex, filters) => ({
|
||||||
|
title: title,
|
||||||
|
dataIndex: dataIndex,
|
||||||
|
key: dataIndex,
|
||||||
|
filters: filters,
|
||||||
|
onFilter: makeFilterMinMaxFunction(dataIndex),
|
||||||
|
sorter: makeNumericSorter(dataIndex)
|
||||||
|
})
|
||||||
|
|
||||||
|
const makeNumericColumnPlanFact = (title, dataIndex, filters) =>
|
||||||
|
makeGroupColumn( title, [
|
||||||
|
makeNumericColumn('п', dataIndex + 'Plan', filters),
|
||||||
|
makeNumericColumn('ф', dataIndex + 'Fact', filters),
|
||||||
|
])
|
||||||
|
|
||||||
|
const ModalWindowButton = ({buttonIcon, buttonText, modalTitle, modalContent}) =>{
|
||||||
|
const [isModalVisible, setIsModalVisible] = useState(false)
|
||||||
|
|
||||||
|
let content = (typeof(modalContent)==="string")
|
||||||
|
? <img src={modalContent} alt="" style={{width: "100%"}}/>
|
||||||
|
: modalContent
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
onClick={() => setIsModalVisible(true)}
|
||||||
|
icon={buttonIcon}
|
||||||
|
>{buttonText}</Button>
|
||||||
|
<Modal
|
||||||
|
title={modalTitle}
|
||||||
|
centered
|
||||||
|
visible={isModalVisible}
|
||||||
|
onOk={() => setIsModalVisible(false)}
|
||||||
|
onCancel={() => setIsModalVisible(false)}
|
||||||
|
width={1400}>
|
||||||
|
{content}
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
makeTextColumn('скв №', 'caption'),
|
||||||
|
makeGroupColumn( 'По секциям', [
|
||||||
|
makeTextColumn('Конструкция скважины', 'sectionType', filtersSectionsType),
|
||||||
|
makeNumericColumnPlanFact('Глубина', 'sectionWellDepth', filtersMinMax),
|
||||||
|
makeNumericColumnPlanFact('Продолжительность', 'sectionBuildDays', filtersMinMax), //Цикл строительства
|
||||||
|
makeNumericColumnPlanFact('МСП', 'sectionRateOfPenetration', filtersMinMax),
|
||||||
|
makeNumericColumnPlanFact('Рейсовая скорость', 'sectionRouteSpeed', filtersMinMax),
|
||||||
|
makeNumericColumnPlanFact('Спуск КНБК', 'sectionBhaDownSpeed', filtersMinMax),//Скорость спуска КНБК
|
||||||
|
makeNumericColumnPlanFact('Подъем КНБК', 'sectionBhaUpSpeed', filtersMinMax),//Скорость подъема КНБК
|
||||||
|
makeNumericColumnPlanFact('Скорость спуска ОК', 'sectionCasingDownSpeed', filtersMinMax),
|
||||||
|
]),
|
||||||
|
makeNumericColumn('НПВ, сут', 'notProductiveTime', filtersMinMax),
|
||||||
|
{
|
||||||
|
title: "TVD",
|
||||||
|
render: (_, record) => <ModalWindowButton
|
||||||
|
buttonIcon={<LineChartOutlined/>}
|
||||||
|
modalTitle={`График по скв.:${record.caption}`}/>
|
||||||
|
//modalContent = {resources['Chart' + record.caption]}/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Операции",
|
||||||
|
render: (_, record) => <ModalWindowButton
|
||||||
|
buttonIcon={<ProfileOutlined/>}
|
||||||
|
modalTitle={`Операции по скв.:${record.caption}`}/>
|
||||||
|
//modalContent = {resources['Table' + record.caption]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Подрядчики",
|
||||||
|
dataIndex: "companies",
|
||||||
|
render: (item) =>
|
||||||
|
item.map((company) => <Tag color="blue">{company.caption}</Tag>),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const contractors = [
|
||||||
|
{ type: "Буровой подрядчик", caption: 'ООО "НГ-Бурение"' },
|
||||||
|
// { type: "ННБ", caption: 'ООО НПП "Буринтех"' },
|
||||||
|
// { type: "Растворный сревис", caption: 'ООО НПП "Буринтех"' },
|
||||||
|
// { type: "Цементирование", caption: "Норд-Сервис" },
|
||||||
|
]
|
||||||
|
|
||||||
|
const wellsStat = [
|
||||||
|
{ key:1, caption :'42669', sectionType :'Направление', sectionWellDepthPlan :80.0, sectionWellDepthFact :79.0, sectionBuildDaysPlan :19.0, sectionBuildDaysFact :10.3, sectionRateOfPenetrationPlan :60.0, sectionRateOfPenetrationFact :158.0, rateOfPenetrationPlan :60.0, rateOfPenetrationFact :63.7, sectionRouteSpeedPlan :4.2, sectionRouteSpeedFact :7.7, routeSpeedPlan :9.7, routeSpeedFact :9.2, sectionBhaDownSpeedPlan :1.0, sectionBhaDownSpeedFact :0.3, sectionBhaUpSpeedPlan :1.0, sectionBhaUpSpeedFact :0.3, sectionCasingDownSpeedPlan :2.0, sectionCasingDownSpeedFact :1.0, companies :contractors },
|
||||||
|
{ key:2, caption :'42669', sectionType :'Кондуктор', sectionWellDepthPlan :1280.0, sectionWellDepthFact :1284.0, sectionBuildDaysPlan :90.0, sectionBuildDaysFact :138.3, sectionRateOfPenetrationPlan :60.0, sectionRateOfPenetrationFact :85.5, rateOfPenetrationPlan :60.0, rateOfPenetrationFact :63.7, sectionRouteSpeedPlan :14.2, sectionRouteSpeedFact :9.3, routeSpeedPlan :9.7, routeSpeedFact :9.2, sectionBhaDownSpeedPlan :12.5, sectionBhaDownSpeedFact :5.8, sectionBhaUpSpeedPlan :11.0, sectionBhaUpSpeedFact :10.5, sectionCasingDownSpeedPlan :10.0, sectionCasingDownSpeedFact :14.0, companies :contractors },
|
||||||
|
{ key:3, caption :'42669', sectionType :'Транспорт. Ствол', sectionWellDepthPlan :3615.9, sectionWellDepthFact :3616.0, sectionBuildDaysPlan :406.1, sectionBuildDaysFact :391.0, sectionRateOfPenetrationPlan :60.0, sectionRateOfPenetrationFact :57.7, rateOfPenetrationPlan :60.0, rateOfPenetrationFact :63.7, sectionRouteSpeedPlan :8.9, sectionRouteSpeedFact :9.2, routeSpeedPlan :9.7, routeSpeedFact :9.2, sectionBhaDownSpeedPlan :50.0, sectionBhaDownSpeedFact :40.5, sectionBhaUpSpeedPlan :52.0, sectionBhaUpSpeedFact :57.2, sectionCasingDownSpeedPlan :14.0, sectionCasingDownSpeedFact :16.3, companies :contractors },
|
||||||
|
{ key:4, caption :'16311', sectionType :'Направление', sectionWellDepthPlan :80.0, sectionWellDepthFact :81.0, sectionBuildDaysPlan :10.8, sectionBuildDaysFact :11.0, sectionRateOfPenetrationPlan :60.0, sectionRateOfPenetrationFact :81.0, rateOfPenetrationPlan :60.0, rateOfPenetrationFact :63.7, sectionRouteSpeedPlan :7.4, sectionRouteSpeedFact :7.4, routeSpeedPlan :9.7, routeSpeedFact :9.2, sectionBhaDownSpeedPlan :1.0, sectionBhaDownSpeedFact :0.5, sectionBhaUpSpeedPlan :1.0, sectionBhaUpSpeedFact :0.5, sectionCasingDownSpeedPlan :1.0, sectionCasingDownSpeedFact :2.3, companies :contractors },
|
||||||
|
{ key:5, caption :'16311', sectionType :'Кондуктор', sectionWellDepthPlan :1411.0, sectionWellDepthFact :1412.0, sectionBuildDaysPlan :107.8, sectionBuildDaysFact :99.0, sectionRateOfPenetrationPlan :60.0, sectionRateOfPenetrationFact :96.4, rateOfPenetrationPlan :60.0, rateOfPenetrationFact :63.7, sectionRouteSpeedPlan :13.1, sectionRouteSpeedFact :14.3, routeSpeedPlan :9.7, routeSpeedFact :9.2, sectionBhaDownSpeedPlan :12.5, sectionBhaDownSpeedFact :0.3, sectionBhaUpSpeedPlan :11.0, sectionBhaUpSpeedFact :8.8, sectionCasingDownSpeedPlan :9.0, sectionCasingDownSpeedFact :12.8, companies :contractors },
|
||||||
|
{ key:6, caption :'16311', sectionType :'Транспорт. Ствол', sectionWellDepthPlan :3181.0, sectionWellDepthFact :3181.0, sectionBuildDaysPlan :171.6, sectionBuildDaysFact :171.0, sectionRateOfPenetrationPlan :35.0, sectionRateOfPenetrationFact :80.9, rateOfPenetrationPlan :60.0, rateOfPenetrationFact :63.7, sectionRouteSpeedPlan :18.5, sectionRouteSpeedFact :18.6, routeSpeedPlan :9.7, routeSpeedFact :9.2, sectionBhaDownSpeedPlan :14.0, sectionBhaDownSpeedFact :4.8, sectionBhaUpSpeedPlan :23.0, sectionBhaUpSpeedFact :14.8, sectionCasingDownSpeedPlan :15.0, sectionCasingDownSpeedFact :16.5, companies :contractors },
|
||||||
|
{ key:7, caption :'16311', sectionType :'Хвостовик', sectionWellDepthPlan :4181.0, sectionWellDepthFact :4187.0, sectionBuildDaysPlan :271.9, sectionBuildDaysFact :220.0, sectionRateOfPenetrationPlan :20.0, sectionRateOfPenetrationFact :32.9, rateOfPenetrationPlan :60.0, rateOfPenetrationFact :63.7, sectionRouteSpeedPlan :15.4, sectionRouteSpeedFact :19.0, routeSpeedPlan :9.7, routeSpeedFact :9.2, sectionBhaDownSpeedPlan :61.5, sectionBhaDownSpeedFact :34.0, sectionBhaUpSpeedPlan :40.0, sectionBhaUpSpeedFact :36.5, sectionCasingDownSpeedPlan :24.5, sectionCasingDownSpeedFact :9.0, companies :contractors },
|
||||||
|
{ key:8, caption :'16315', sectionType :'Направление', sectionWellDepthPlan :80.0, sectionWellDepthFact :80.0, sectionBuildDaysPlan :21.1, sectionBuildDaysFact :23.0, sectionRateOfPenetrationPlan :60.0, sectionRateOfPenetrationFact :53.3, rateOfPenetrationPlan :27.2, rateOfPenetrationFact :30.1, sectionRouteSpeedPlan :3.8, sectionRouteSpeedFact :3.5, routeSpeedPlan :14.4, routeSpeedFact :7.5, sectionBhaDownSpeedPlan :1.0, sectionBhaDownSpeedFact :0.5, sectionBhaUpSpeedPlan :1.0, sectionBhaUpSpeedFact :1.0, sectionCasingDownSpeedPlan :2.0, sectionCasingDownSpeedFact :2.0, companies :contractors },
|
||||||
|
{ key:9, caption :'16315', sectionType :'Кондуктор', sectionWellDepthPlan :1500.0, sectionWellDepthFact :1505.0, sectionBuildDaysPlan :130.3, sectionBuildDaysFact :150.5, sectionRateOfPenetrationPlan :60.0, sectionRateOfPenetrationFact :58.2, rateOfPenetrationPlan :27.2, rateOfPenetrationFact :30.1, sectionRouteSpeedPlan :11.5, sectionRouteSpeedFact :10.0, routeSpeedPlan :14.4, routeSpeedFact :7.5, sectionBhaDownSpeedPlan :8.3, sectionBhaDownSpeedFact :0.5, sectionBhaUpSpeedPlan :11.0, sectionBhaUpSpeedFact :6.5, sectionCasingDownSpeedPlan :10.0, sectionCasingDownSpeedFact :11.0, companies :contractors }
|
||||||
|
]
|
||||||
|
|
||||||
|
const calcAndUpdateStats = (data, keys) => {
|
||||||
|
let mins = {}
|
||||||
|
let maxs = {}
|
||||||
|
|
||||||
|
keys.forEach((key) => {
|
||||||
|
maxs[key] = Number.MIN_VALUE
|
||||||
|
mins[key] = Number.MAX_VALUE
|
||||||
|
})
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (mins[key] > item[key]) mins[key] = item[key]
|
||||||
|
|
||||||
|
if (maxs[key] < item[key]) maxs[key] = item[key]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
keys.forEach((key) => {
|
||||||
|
data[i][isMaxPrefix + key] = data[i][key] === maxs[key]
|
||||||
|
data[i][isMinPrefix + key] = data[i][key] === mins[key]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcAndUpdateStatsBySections = (data, keys) => {
|
||||||
|
const sectionTypes = new Set()
|
||||||
|
data.forEach(item => sectionTypes.add(item.sectionType))
|
||||||
|
sectionTypes.forEach(sectionType => {
|
||||||
|
const filteredBySectionData = data.filter(item => item.sectionType === sectionType)
|
||||||
|
calcAndUpdateStats(filteredBySectionData, keys)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
calcAndUpdateStatsBySections(wellsStat, [
|
||||||
|
"sectionWellDepthPlan",
|
||||||
|
"sectionWellDepthFact",
|
||||||
|
"sectionBuildDaysPlan",
|
||||||
|
"sectionBuildDaysFact",
|
||||||
|
"sectionRateOfPenetrationPlan",
|
||||||
|
"sectionRateOfPenetrationFact",
|
||||||
|
"sectionRouteSpeedPlan",
|
||||||
|
"sectionRouteSpeedFact",
|
||||||
|
"sectionBhaDownSpeedPlan",
|
||||||
|
"sectionBhaDownSpeedFact",
|
||||||
|
"sectionBhaUpSpeedPlan",
|
||||||
|
"sectionBhaUpSpeedFact",
|
||||||
|
"sectionCasingDownSpeedPlan",
|
||||||
|
"sectionCasingDownSpeedFact",
|
||||||
|
])
|
||||||
|
|
||||||
|
export default function ClusterStat() {
|
||||||
|
const [selectedWells, setSelectedWells] = useState([])
|
||||||
|
const [selectedWellsKeys, setSelectedWellsKeys] = useState([])
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
selectedRowKeys: selectedWellsKeys,
|
||||||
|
onChange: (keys, items) => {
|
||||||
|
setSelectedWells(items)
|
||||||
|
setSelectedWellsKeys(keys)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h2 className={'mt-20px mb-20px'}>Статистика по секциям</h2>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={wellsStat}
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
156
src/pages/ClusterWells.jsx
Normal file
156
src/pages/ClusterWells.jsx
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import LoaderPortal from '../components/LoaderPortal'
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { ClusterService } from '../services/api'
|
||||||
|
import { notify } from "../components/factory"
|
||||||
|
import { Table, Tag, Button } from 'antd';
|
||||||
|
|
||||||
|
const isMaxPrefix = "isMax"
|
||||||
|
const isMinPrefix = "isMin"
|
||||||
|
|
||||||
|
const filtersMinMax = [
|
||||||
|
{
|
||||||
|
text: "min",
|
||||||
|
value: "min",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "max",
|
||||||
|
value: "max",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const filtersWellsType = [
|
||||||
|
{
|
||||||
|
text: "Наклонно-направленная",
|
||||||
|
value: "sidelong",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Горизонтальная",
|
||||||
|
value: "horizontal",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const makeFilterMinMaxFunction = (key) => (filterValue, dataItem) =>
|
||||||
|
filterValue === "max"
|
||||||
|
? dataItem[isMaxPrefix + key]
|
||||||
|
: filterValue === "min"
|
||||||
|
? dataItem[isMinPrefix + key]
|
||||||
|
: false
|
||||||
|
|
||||||
|
const makeFilterTextMatch = (key) => (filterValue, dataItem) =>
|
||||||
|
dataItem[key] === filterValue
|
||||||
|
|
||||||
|
const makeNumericSorter = (key) => (a, b) => a[key] - b[key]
|
||||||
|
|
||||||
|
const makeStringSorter = (key) => (a, b) =>
|
||||||
|
{
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
if (isNaN(b.charCodeAt(i)) || (a.charCodeAt(i) > b.charCodeAt(i)))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if (a.charCodeAt(i) > b.charCodeAt(i))
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeGroupColumn = (title, children) => ({
|
||||||
|
title: title,
|
||||||
|
children: children,
|
||||||
|
})
|
||||||
|
|
||||||
|
const makeTextColumn = (title, dataIndex, filters, sorter, render, other) => ({
|
||||||
|
title: title,
|
||||||
|
dataIndex: dataIndex,
|
||||||
|
key: dataIndex,
|
||||||
|
filters: filters,
|
||||||
|
onFilter: filters ? makeFilterTextMatch(dataIndex) : null,
|
||||||
|
sorter: sorter ? makeStringSorter(dataIndex) : null,
|
||||||
|
render: render,
|
||||||
|
...other
|
||||||
|
})
|
||||||
|
|
||||||
|
const makeNumericColumn = (title, dataIndex, filters) => ({
|
||||||
|
title: title,
|
||||||
|
dataIndex: dataIndex,
|
||||||
|
key: dataIndex,
|
||||||
|
filters: filters,
|
||||||
|
onFilter: makeFilterMinMaxFunction(dataIndex),
|
||||||
|
sorter: makeNumericSorter(dataIndex)
|
||||||
|
})
|
||||||
|
|
||||||
|
const makeNumericColumnPlanFact = (title, dataIndex, filters) =>
|
||||||
|
makeGroupColumn( title, [
|
||||||
|
makeNumericColumn('п', dataIndex + 'Plan', filters),
|
||||||
|
makeNumericColumn('ф', dataIndex + 'Fact', filters),
|
||||||
|
])
|
||||||
|
|
||||||
|
export default function Cluster() {
|
||||||
|
let { id } = useParams()
|
||||||
|
const [wellsStat, setWellsStat] = useState(null)
|
||||||
|
const [showLoader, setShowLoader] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateWellsStat = async () => {
|
||||||
|
setShowLoader(true)
|
||||||
|
try {
|
||||||
|
const msInDay = 1000 * 60 * 60 * 24
|
||||||
|
const data = await ClusterService.getStat(id)
|
||||||
|
const wellsStat = data.wellsStat.map(w => ({
|
||||||
|
...w,
|
||||||
|
periodPlan: (new Date(w.planEnd) - new Date(w.planStart)) / msInDay,
|
||||||
|
periodFact: (new Date(w.factEnd) - new Date(w.factStart)) / msInDay,
|
||||||
|
}))
|
||||||
|
setWellsStat(wellsStat)
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
notify(`Не удалось загрузить статистику по скважинам куста "${id}"`, 'error')
|
||||||
|
console.log(ex)
|
||||||
|
}
|
||||||
|
setShowLoader(false)
|
||||||
|
}
|
||||||
|
updateWellsStat()
|
||||||
|
}, [id])
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
makeTextColumn('скв №', 'caption', null, null,
|
||||||
|
(_, item) => <Link to={`/well/${item.id}`}>{item.caption}</Link>),
|
||||||
|
makeTextColumn('Тип скв.', 'wellType', filtersWellsType),
|
||||||
|
makeGroupColumn( 'Фактические сроки', [
|
||||||
|
makeNumericColumn('начало', 'factStart', filtersMinMax),
|
||||||
|
makeNumericColumn('окончание', 'factEnd', filtersMinMax),
|
||||||
|
]),
|
||||||
|
makeNumericColumnPlanFact('Продолжительность', 'period', filtersMinMax),
|
||||||
|
makeNumericColumnPlanFact('МСП', 'rateOfPenetration', filtersMinMax),
|
||||||
|
makeNumericColumnPlanFact('Рейсовая скорость', 'routeSpeed', filtersMinMax),
|
||||||
|
makeNumericColumn('НПВ, сут', 'notProductiveTime', filtersMinMax),
|
||||||
|
{
|
||||||
|
title: 'График глубина-день',
|
||||||
|
render: (_, item) => (<Button>Открыть</Button>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Таблица по операциям',
|
||||||
|
render: _ => (<Button>Открыть</Button>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Подрядчики',
|
||||||
|
key: 'companies',
|
||||||
|
dataIndex: 'companies',
|
||||||
|
render: (item) => item.map(company => <Tag color="blue">{company.caption}</Tag>)
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LoaderPortal show={showLoader}>
|
||||||
|
<h2 className={'mt-20px mb-20px'}>Статистика по скважинам</h2>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={wellsStat}
|
||||||
|
size={'small'}
|
||||||
|
bordered
|
||||||
|
pagination={false}
|
||||||
|
rowKey={(record) => record.id}
|
||||||
|
/>
|
||||||
|
</LoaderPortal>)
|
||||||
|
}
|
@ -53,7 +53,7 @@ export default function Deposit() {
|
|||||||
width={32}
|
width={32}
|
||||||
anchor={[cluster.latitude, cluster.longitude]}
|
anchor={[cluster.latitude, cluster.longitude]}
|
||||||
key={`${cluster.latitude} ${cluster.longitude}`}>
|
key={`${cluster.latitude} ${cluster.longitude}`}>
|
||||||
<Link to={`/cluster/${cluster.id}`}>
|
<Link to={`/cluster/${cluster.id}/all`}>
|
||||||
<img width={40} src={pointer} alt="+"/>
|
<img width={40} src={pointer} alt="+"/>
|
||||||
<span>{cluster.caption}</span>
|
<span>{cluster.caption}</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Deposit from './Deposit'
|
import Deposit from './Deposit'
|
||||||
import Cluster from './Cluster'
|
import ClusterInfo from './ClusterInfo'
|
||||||
import Well from "./Well";
|
import Well from "./Well";
|
||||||
import LayoutPortal from '../components/LayoutPortal'
|
import LayoutPortal from '../components/LayoutPortal'
|
||||||
import {Redirect, Route, Switch} from "react-router-dom";
|
import {Redirect, Route, Switch} from "react-router-dom";
|
||||||
@ -18,9 +18,9 @@ export default function Main() {
|
|||||||
<Deposit/>
|
<Deposit/>
|
||||||
</LayoutPortal>
|
</LayoutPortal>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/cluster/:id/">
|
<Route path="/cluster/:id/:tab">
|
||||||
<LayoutPortal>
|
<LayoutPortal>
|
||||||
<Cluster/>
|
<ClusterInfo />
|
||||||
</LayoutPortal>
|
</LayoutPortal>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
|
@ -31,6 +31,10 @@ body {
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mb-20px {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.ml-5px {
|
.ml-5px {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user