Merge branch 'dev' into feature/CF2-117-New-drilling-program

This commit is contained in:
Александр Сироткин 2022-02-14 20:41:29 +05:00
commit a95df07ac1
27 changed files with 300 additions and 23291 deletions

23270
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@
"react_test": "react-scripts test", "react_test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"proxy": "http://192.168.1.70:5000", "proxy": "http://46.146.209.148:89",
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"react-app", "react-app",

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import { import {
EditableTable, EditableTable,
@ -17,7 +17,7 @@ import { hasPermission } from '@utils/permissions'
import { coordsFixed } from './DepositController' import { coordsFixed } from './DepositController'
export const ClusterController = () => { export const ClusterController = memo(() => {
const [deposits, setDeposits] = useState([]) const [deposits, setDeposits] = useState([])
const [clusters, setClusters] = useState([]) const [clusters, setClusters] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -82,6 +82,6 @@ export const ClusterController = () => {
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default ClusterController export default ClusterController

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import { import {
EditableTable, EditableTable,
@ -16,7 +16,7 @@ import { min1 } from '@utils/validationRules'
import { hasPermission } from '@utils/permissions' import { hasPermission } from '@utils/permissions'
export const CompanyController = () => { export const CompanyController = memo(() => {
const [columns, setColumns] = useState([]) const [columns, setColumns] = useState([])
const [companies, setCompanies] = useState([]) const [companies, setCompanies] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -79,6 +79,6 @@ export const CompanyController = () => {
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default CompanyController export default CompanyController

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import { import {
EditableTable, EditableTable,
@ -12,7 +12,7 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminCompanyTypeService } from '@api' import { AdminCompanyTypeService } from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import { min1 } from '@utils/validationRules' import { min1 } from '@utils/validationRules'
import { hasPermission } from '@asb/utils/permissions' import { hasPermission } from '@utils/permissions'
const columns = [ const columns = [
makeColumn('Название', 'caption', { makeColumn('Название', 'caption', {
@ -23,7 +23,7 @@ const columns = [
}), }),
] ]
export const CompanyTypeController = () => { export const CompanyTypeController = memo(() => {
const [companyTypes, setCompanyTypes] = useState([]) const [companyTypes, setCompanyTypes] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -60,6 +60,6 @@ export const CompanyTypeController = () => {
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default CompanyTypeController export default CompanyTypeController

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
@ -16,7 +16,7 @@ const depositColumns = [
makeColumn('Долгота', 'longitude', { width: 150, editable: true, render: coordsFixed }) makeColumn('Долгота', 'longitude', { width: 150, editable: true, render: coordsFixed })
] ]
export const DepositController = () => { export const DepositController = memo(() => {
const [deposits, setDeposits] = useState([]) const [deposits, setDeposits] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -53,6 +53,6 @@ export const DepositController = () => {
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default DepositController export default DepositController

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import { import {
EditableTable, EditableTable,
@ -26,7 +26,7 @@ const columns = [
}), }),
] ]
export const PermissionController = () => { export const PermissionController = memo(() => {
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [permissions, setPermissions] = useState([]) const [permissions, setPermissions] = useState([])
@ -63,6 +63,6 @@ export const PermissionController = () => {
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default PermissionController export default PermissionController

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import { import {
defaultPagination, defaultPagination,
@ -29,7 +29,7 @@ const columns = [
makeTextColumn('Версия Спин Мастер', 'spinPlcVersion'), makeTextColumn('Версия Спин Мастер', 'spinPlcVersion'),
] ]
export const TelemetryController = () => { export const TelemetryController = memo(() => {
const [telemetryData, setTelemetryData] = useState([]) const [telemetryData, setTelemetryData] = useState([])
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
@ -59,6 +59,6 @@ export const TelemetryController = () => {
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default TelemetryController export default TelemetryController

View File

@ -1,6 +1,6 @@
import { Button, Tag } from 'antd' import { Button, Tag } from 'antd'
import { UserSwitchOutlined } from '@ant-design/icons' import { UserSwitchOutlined } from '@ant-design/icons'
import { useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import { import {
EditableTable, EditableTable,
@ -17,14 +17,14 @@ import { ChangePassword } from '@components/ChangePassword'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '@api' import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '@api'
import { createLoginRules, nameRules, phoneRules, emailRules } from '@utils/validationRules' import { createLoginRules, nameRules, phoneRules, emailRules } from '@utils/validationRules'
import { makeTextOnFilter, makeTextFilters } from '@utils/table' import { makeTextOnFilter, makeTextFilters, makeArrayOnFilter } from '@utils/table'
import { hasPermission } from '@utils/permissions' import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import RoleTag from './RoleTag' import RoleTag from './RoleTag'
export const UserController = () => { export const UserController = memo(() => {
const [users, setUsers] = useState([]) const [users, setUsers] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([]) const [columns, setColumns] = useState([])
@ -61,7 +61,7 @@ export const UserController = () => {
setUsers(users) setUsers(users)
const filters = makeTextFilters(users, ['surname', 'name', 'patronymic', 'email']) const filters = makeTextFilters(users, ['surname', 'name', 'patronymic', 'email'])
const roleFilters = [{ text: 'Без роли', value: null }, ...roles.map((role) => ({ text: role.caption, value: role.caption }))]
setColumns([ setColumns([
makeColumn('Логин', 'login', { makeColumn('Логин', 'login', {
@ -124,6 +124,8 @@ export const UserController = () => {
makeColumn('Роли', 'roleNames', { makeColumn('Роли', 'roleNames', {
editable: true, editable: true,
input: <RoleTag roles={roles} />, input: <RoleTag roles={roles} />,
filters: roleFilters,
onFilter: makeArrayOnFilter('roleNames'),
render: (item) => item?.map((elm) => ( render: (item) => item?.map((elm) => (
<Tag key={elm} color={'blue'}> <Tag key={elm} color={'blue'}>
<RoleView role={roles.find((role) => role.caption === elm)} /> <RoleView role={roles.find((role) => role.caption === elm)} />
@ -172,6 +174,6 @@ export const UserController = () => {
/> />
</> </>
) )
} })
export default UserController export default UserController

View File

@ -3,7 +3,7 @@ import { memo, 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 { defaultPagination, makeColumn, makeDateSorter, makeStringSorter, Table } from '@components/Table' import { defaultPagination, makeColumn, makeDateSorter, makeStringSorter, Table } from '@components/Table'
import { RequerstTrackerService } from '@api' import { RequestTrackerService } from '@api'
import { arrayOrDefault, formatDate } from '@utils' import { arrayOrDefault, formatDate } from '@utils'
const logRecordCount = 1000 const logRecordCount = 1000
@ -23,7 +23,7 @@ export const VisitLog = memo(() => {
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const logData = arrayOrDefault(await RequerstTrackerService.getUsersStat(logRecordCount)) const logData = arrayOrDefault(await RequestTrackerService.getUsersStat(logRecordCount))
logData.forEach((log) => log.key = `${log.login}${log.ip}`) logData.forEach((log) => log.key = `${log.login}${log.ip}`)
setLogData(logData) setLogData(logData)
}, },

View File

@ -5,6 +5,7 @@ import { getTelemetryLabel } from '@components/views'
export const TelemetrySelect = memo(({ telemetry, value, onChange }) => ( export const TelemetrySelect = memo(({ telemetry, value, onChange }) => (
<Select <Select
allowClear
value={value?.id} value={value?.id}
onChange={(id) => onChange?.(telemetry.find((row) => row.id === id))} onChange={(id) => onChange?.(telemetry.find((row) => row.id === id))}
dropdownClassName={'telemetry_select'} dropdownClassName={'telemetry_select'}

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'
import { Button } from 'antd' import { Button } from 'antd'
import { CopyOutlined } from '@ant-design/icons' import { CopyOutlined } from '@ant-design/icons'
import { memo, useEffect, useState } from 'react'
import { import {
AdminClusterService, AdminClusterService,
@ -21,20 +21,20 @@ import {
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { TelemetryView, CompanyView } from '@components/views' import { TelemetryView, CompanyView } from '@components/views'
import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import { coordsFixed } from '../DepositController' import { coordsFixed } from '../DepositController'
import TelemetrySelect from './TelemetrySelect' import TelemetrySelect from './TelemetrySelect'
import '@styles/admin.css' import '@styles/admin.css'
import { hasPermission } from '@asb/utils/permissions'
const wellTypes = [ const wellTypes = [
{ value: 1, label: 'Наклонно-направленная' }, { value: 1, label: 'Наклонно-направленная' },
{ value: 2, label: 'Горизонтальная' }, { value: 2, label: 'Горизонтальная' },
] ]
export const WellController = () => { export const WellController = memo(() => {
const [columns, setColumns] = useState([]) const [columns, setColumns] = useState([])
const [wells, setWells] = useState([]) const [wells, setWells] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -128,13 +128,13 @@ export const WellController = () => {
dataSource={wells} dataSource={wells}
pagination={defaultPagination} pagination={defaultPagination}
onRowAdd={hasPermission('AdminWell.edit') && makeActionHandler('insert', handlerProps, recordParser, 'Добавление скважины')} onRowAdd={hasPermission('AdminWell.edit') && makeActionHandler('insert', handlerProps, recordParser, 'Добавление скважины')}
onRowEdit={hasPermission('AdminWell.edit') && makeActionHandler('put', handlerProps, recordParser, 'Редактирование скважины')} onRowEdit={hasPermission('AdminWell.edit') && makeActionHandler('update', handlerProps, recordParser, 'Редактирование скважины')}
onRowDelete={hasPermission('AdminWell.delete') && makeActionHandler('delete', handlerProps, null, 'Удаление скважины')} onRowDelete={hasPermission('AdminWell.delete') && makeActionHandler('delete', handlerProps, null, 'Удаление скважины')}
//additionalButtons={addititonalButtons} //additionalButtons={addititonalButtons}
buttonsWidth={95} buttonsWidth={95}
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default WellController export default WellController

View File

@ -16,13 +16,14 @@ import { CompanyView } from '@components/views'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import PointerIcon from '@components/icons/PointerIcon' import PointerIcon from '@components/icons/PointerIcon'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { Tvd } from '@pages/WellOperations/Tvd'
import { import {
getOperations, getOperations,
calcAndUpdateStatsBySections, calcAndUpdateStatsBySections,
makeFilterMinMaxFunction makeFilterMinMaxFunction
} from './functions' } from '@utils/functions'
import { isRawDate } from '@utils'
import { Tvd } from '@pages/WellOperations/Tvd'
import WellOperationsTable from './WellOperationsTable' import WellOperationsTable from './WellOperationsTable'
const filtersMinMax = [ const filtersMinMax = [
@ -61,14 +62,11 @@ export const ClusterWells = ({ statsWells }) => {
useEffect(() => { useEffect(() => {
let data = statsWells?.map((well) => { let data = statsWells?.map((well) => {
if (!filtersWellsType.some((el) => el.text === well.wellType)) if (!filtersWellsType.some((el) => el.text === well.wellType))
filtersWellsType.push({ text: well.wellType, value: well.wellType,}) filtersWellsType.push({ text: well.wellType, value: well.wellType })
let periodPlanValue = well.total?.plan?.start && well.total?.plan?.end const dateOrM = (a, b) => a && b ? (new Date(b) - new Date(a)) / DAY_IN_MS : '-'
? (new Date(well.total?.plan?.end) - new Date(well.total?.plan?.start)) / DAY_IN_MS const periodPlanValue = dateOrM(well.total?.plan?.start, well.total?.plan?.end)
: '-' const periodFactValue = dateOrM(well.total?.fact?.start, well.total?.fact?.end)
let periodFactValue = well.total?.fact?.start && well.total?.fact?.end
? (new Date(well.total?.fact?.end) - new Date(well.total?.fact?.start)) / DAY_IN_MS
: '-'
return { return {
key: well.caption, key: well.caption,
@ -106,9 +104,7 @@ export const ClusterWells = ({ statsWells }) => {
setTableData(data) setTableData(data)
}, [statsWells]) }, [statsWells])
const getDate = (str) => Number.isNaN(+new Date(str)) || +new Date(str) === 0 const getDate = (str) => isRawDate(str) ? new Date(str).toLocaleString() : '-'
? '-'
: new Date(str).toLocaleString()
const columns = [ const columns = [
makeTextColumn('скв №', 'caption', null, null, makeTextColumn('скв №', 'caption', null, null,

View File

@ -1,9 +1,8 @@
import { Table } from 'antd' import { Table } from 'antd'
import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table' import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table'
import { getPrecision } from './functions' import { getPrecision } from '@utils/functions'
export const WellOperationsTable = ({ wellOperations }) => { export const WellOperationsTable = ({ wellOperations }) => {
const columns = [ const columns = [
@ -27,7 +26,7 @@ export const WellOperationsTable = ({ wellOperations }) => {
commentFact: el.fact?.comment ?? '-' commentFact: el.fact?.comment ?? '-'
})) }))
return( return (
<Table <Table
bordered bordered
size={'small'} size={'small'}

View File

@ -1,74 +0,0 @@
import { OperationStatService } from '@api'
const maxPrefix = 'isMax'
const minPrefix = 'isMin'
export const getPrecision = (number) => Number.isFinite(number) ? number.toFixed(2) : '-'
export const getOperations = async (idWell) => {
const ops = await OperationStatService.getTvd(idWell)
if (!ops) return []
const convert = wellOperationDto => ({
key: wellOperationDto?.id,
depth: wellOperationDto?.depthStart,
date: wellOperationDto?.dateStart,
day: wellOperationDto?.day,
})
const planData = ops
.map(item => convert(item.plan))
.filter(el => el.key)
const factData = ops
.map(item => convert(item.fact))
.filter(el => el.key)
const predictData = ops
.map(item => convert(item.predict))
.filter(el => el.key)
return { operations: ops, plan: planData, fact: factData, predict: predictData }
}
export const makeFilterMinMaxFunction = (key) => (filterValue,
dataItem) =>
filterValue === 'max'
? dataItem[maxPrefix + key]
: filterValue === 'min'
? dataItem[minPrefix + key]
: false
export const calcAndUpdateStats = (data, keys) => {
const mins = {}
const 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][maxPrefix + key] = data[i][key] === maxs[key]
data[i][minPrefix + key] = data[i][key] === mins[key]
})
}
}
export 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)
})
}

View File

@ -1,4 +1,3 @@
import moment from 'moment'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { DatePicker, Button, Input } from 'antd' import { DatePicker, Button, Input } from 'antd'
@ -9,6 +8,7 @@ import { UploadForm } from '@components/UploadForm'
import { CompanyView, UserView } from '@components/views' import { CompanyView, UserView } from '@components/views'
import { EditableTable, makePaginationObject } from '@components/Table' import { EditableTable, makePaginationObject } from '@components/Table'
import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory' import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory'
import { formatDate } from '@utils'
const pageSize = 12 const pageSize = 12
const { RangePicker } = DatePicker const { RangePicker } = DatePicker
@ -28,7 +28,7 @@ const columns = [
title: 'Дата загрузки', title: 'Дата загрузки',
key: 'uploadDate', key: 'uploadDate',
dataIndex: 'uploadDate', dataIndex: 'uploadDate',
render: item => moment.utc(item).local().format('DD MMM YYYY, HH:mm:ss'), render: item => formatDate(item, false, 'DD MMM YYYY, HH:mm:ss'),
}, { }, {
title: 'Размер', title: 'Размер',
key: 'size', key: 'size',

View File

@ -138,20 +138,20 @@ export const MeasureTable = memo(({ idWell, group, updateMeasuresFunc, additiona
<div className={'measure-dates mt-20px'}> <div className={'measure-dates mt-20px'}>
<Timeline className={'mt-12px ml-10px'}> <Timeline className={'mt-12px ml-10px'}>
{data.map((item, index) => {data.map((item, index) => (
<Timeline.Item <Timeline.Item
key={index} key={index}
className={'measure-button'} className={'measure-button'}
onClick={() => setDisplayedValues(item)} onClick={() => setDisplayedValues(item)}
dot={item?.id === displayedValues?.id && dot={item?.id === displayedValues?.id && (
<CheckSquareOutlined className={'timeline-clock-icon'} /> <CheckSquareOutlined className={'timeline-clock-icon'} />
} )}
> >
<span className={item?.id === displayedValues?.id ? 'selected-timeline' : undefined}> <span className={item?.id === displayedValues?.id ? 'selected-timeline' : undefined}>
{formatDate(item.timestamp, true) ?? 'Нет данных'} {formatDate(item.timestamp) ?? 'Нет данных'}
</span> </span>
</Timeline.Item> </Timeline.Item>
)} ))}
</Timeline> </Timeline>
</div> </div>
</div> </div>

View File

@ -82,7 +82,7 @@ export const Setpoints = ({ idWell, ...other }) => {
visible={isModalVisible} visible={isModalVisible}
onCancel={() => setIsModalVisible(false)} onCancel={() => setIsModalVisible(false)}
footer={( footer={(
<Button onClick={() => setIsSenderVisible(true)}> <Button onClick={() => setIsSenderVisible(true)} disabled={!hasPermission('Setpoints.edit')}>
Рекомендовать Рекомендовать
</Button> </Button>
)} )}

View File

@ -6,7 +6,7 @@ import {
FilePdfOutlined, FilePdfOutlined,
DatabaseOutlined, DatabaseOutlined,
ExperimentOutlined, ExperimentOutlined,
FundProjectionScreenOutlined, DeploymentUnitOutlined,
} from '@ant-design/icons' } from '@ant-design/icons'
import { Layout, Menu } from 'antd' import { Layout, Menu } from 'antd'
import { Switch, useParams } from 'react-router-dom' import { Switch, useParams } from 'react-router-dom'
@ -22,6 +22,7 @@ import TelemetryView from './TelemetryView'
import WellOperations from './WellOperations' import WellOperations from './WellOperations'
import DrillingProgram from './DrillingProgram' import DrillingProgram from './DrillingProgram'
import TelemetryAnalysis from './TelemetryAnalysis' import TelemetryAnalysis from './TelemetryAnalysis'
import WellCompositeEditor from './WellCompositeEditor'
const { Content } = Layout const { Content } = Layout
@ -35,9 +36,10 @@ export const Well = memo(() => {
<PrivateMenuItem.Link root={rootPath} key={'telemetry'} path={'telemetry'} icon={<FundViewOutlined />} title={'Мониторинг'}/> <PrivateMenuItem.Link root={rootPath} key={'telemetry'} path={'telemetry'} icon={<FundViewOutlined />} title={'Мониторинг'}/>
<PrivateMenuItem.Link root={rootPath} key={'message'} path={'message'} icon={<AlertOutlined/>} title={'Сообщения'} /> <PrivateMenuItem.Link root={rootPath} key={'message'} path={'message'} icon={<AlertOutlined/>} title={'Сообщения'} />
<PrivateMenuItem.Link root={rootPath} key={'report'} path={'report'} icon={<FilePdfOutlined />} title={'Рапорт'} /> <PrivateMenuItem.Link root={rootPath} key={'report'} path={'report'} icon={<FilePdfOutlined />} title={'Рапорт'} />
<PrivateMenuItem.Link root={rootPath} key={'composite'} path={'composite'} icon={<DeploymentUnitOutlined />} title={'Аналитика'} />
<PrivateMenuItem.Link root={rootPath} key={'operations'} path={'operations'} icon={<FolderOutlined />} title={'Операции по скважине'} /> <PrivateMenuItem.Link root={rootPath} key={'operations'} path={'operations'} icon={<FolderOutlined />} title={'Операции по скважине'} />
<PrivateMenuItem.Link root={rootPath} key={'archive'} path={'archive'} icon={<DatabaseOutlined />} title={'Архив'} /> <PrivateMenuItem.Link root={rootPath} key={'archive'} path={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
<PrivateMenuItem.Link root={rootPath} key={'telemetryAnalysis'} path={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} title={'Операции по телеметрии'} /> {/* <PrivateMenuItem.Link root={rootPath} key={'telemetryAnalysis'} path={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} title={'Операции по телеметрии'} /> */}
<PrivateMenuItem.Link root={rootPath} key={'document'} path={'document'} icon={<FolderOutlined />} title={'Документы'} /> <PrivateMenuItem.Link root={rootPath} key={'document'} path={'document'} icon={<FolderOutlined />} title={'Документы'} />
<PrivateMenuItem.Link root={rootPath} key={'measure'} path={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} /> <PrivateMenuItem.Link root={rootPath} key={'measure'} path={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} />
<PrivateMenuItem.Link root={rootPath} key={'drillingProgram'} path={'drillingProgram'} icon={<FolderOutlined />} title={'Программа бурения'} /> <PrivateMenuItem.Link root={rootPath} key={'drillingProgram'} path={'drillingProgram'} icon={<FolderOutlined />} title={'Программа бурения'} />
@ -55,6 +57,9 @@ export const Well = memo(() => {
<PrivateRoute path={`${rootPath}/report`}> <PrivateRoute path={`${rootPath}/report`}>
<Report idWell={idWell} /> <Report idWell={idWell} />
</PrivateRoute> </PrivateRoute>
<PrivateRoute path={`${rootPath}/composite/:tab?`}>
<WellCompositeEditor idWell={idWell} rootPath={`${rootPath}/composite`}/>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/operations/:tab?`}> <PrivateRoute path={`${rootPath}/operations/:tab?`}>
<WellOperations idWell={idWell} /> <WellOperations idWell={idWell} />
</PrivateRoute> </PrivateRoute>
@ -77,6 +82,7 @@ export const Well = memo(() => {
`${rootPath}/telemetry`, `${rootPath}/telemetry`,
`${rootPath}/message`, `${rootPath}/message`,
`${rootPath}/report`, `${rootPath}/report`,
`${rootPath}/composite`,
`${rootPath}/operations`, `${rootPath}/operations`,
`${rootPath}/archive`, `${rootPath}/archive`,
`${rootPath}/telemetryAnalysis`, `${rootPath}/telemetryAnalysis`,

View File

@ -9,15 +9,15 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table' import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table'
import { DrillParamsService, WellCompositeService } from '@api' import { DrillParamsService, WellCompositeService } from '@api'
import { hasPermission } from '@utils/permissions' import { hasPermission } from '@utils/permissions'
import { import {
calcAndUpdateStatsBySections, calcAndUpdateStatsBySections,
makeFilterMinMaxFunction, makeFilterMinMaxFunction,
getOperations getOperations
} from '@pages/Cluster/functions' } from '@utils/functions'
import { Tvd } from '@pages/WellOperations/Tvd'
import { getColumns } from '@pages/WellOperations/WellDrillParams'
import WellOperationsTable from '@pages/Cluster/WellOperationsTable' import WellOperationsTable from '@pages/Cluster/WellOperationsTable'
import { Tvd } from '../Tvd'
import { getColumns } from '../WellDrillParams'
const filtersMinMax = [ const filtersMinMax = [

View File

@ -18,8 +18,7 @@ import { WellCompositeSections } from './WellCompositeSections'
const { Content } = Layout const { Content } = Layout
export const WellCompositeEditor = memo(({ idWell }) => { export const WellCompositeEditor = memo(({ idWell, rootPath }) => {
const rootPath = `/well/${idWell}/operations/composite`
const { tab } = useParams() const { tab } = useParams()
const [wellsTree, setWellsTree] = useState([]) const [wellsTree, setWellsTree] = useState([])

View File

@ -16,8 +16,7 @@ import ChartDataLabels from 'chartjs-plugin-datalabels'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { getOperations } from '@utils/functions'
import { getOperations } from '@pages/Cluster/functions'
Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin) Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin)

View File

@ -5,7 +5,6 @@ import {
BarChartOutlined, BarChartOutlined,
BuildOutlined, BuildOutlined,
ControlOutlined, ControlOutlined,
DeploymentUnitOutlined,
LineChartOutlined, LineChartOutlined,
TableOutlined, TableOutlined,
} from '@ant-design/icons' } from '@ant-design/icons'
@ -17,7 +16,6 @@ import { ImportExportBar } from './ImportExportBar'
import { WellDrillParams } from './WellDrillParams' import { WellDrillParams } from './WellDrillParams'
import { DrillProcessFlow } from './DrillProcessFlow' import { DrillProcessFlow } from './DrillProcessFlow'
import { WellSectionsStat } from './WellSectionsStat' import { WellSectionsStat } from './WellSectionsStat'
import { WellCompositeEditor } from './WellCompositeEditor'
import { WellOperationsEditor } from './WellOperationsEditor' import { WellOperationsEditor } from './WellOperationsEditor'
const { Content } = Layout const { Content } = Layout
@ -40,7 +38,6 @@ export const WellOperations = memo(({ idWell }) => {
<PrivateMenuItemLink root={rootPath} icon={<TableOutlined />} key={'fact'} path={'fact'} title={'Факт'} /> <PrivateMenuItemLink root={rootPath} icon={<TableOutlined />} key={'fact'} path={'fact'} title={'Факт'} />
<PrivateMenuItemLink root={rootPath} icon={<BarChartOutlined />} key={'drillProcessFlow'} path={'drillProcessFlow'} title={'РТК'} /> <PrivateMenuItemLink root={rootPath} icon={<BarChartOutlined />} key={'drillProcessFlow'} path={'drillProcessFlow'} title={'РТК'} />
<PrivateMenuItemLink root={rootPath} icon={<ControlOutlined />} key={'params'} path={'params'} title={'Режимы'} /> <PrivateMenuItemLink root={rootPath} icon={<ControlOutlined />} key={'params'} path={'params'} title={'Режимы'} />
<PrivateMenuItemLink root={rootPath} icon={<DeploymentUnitOutlined />} key={'composite'} path={'composite'} title={'Аналитика'} />
<ImportExportBar idWell={idWell} disabled={isIEBarDisabled} onImported={onImported}/> <ImportExportBar idWell={idWell} disabled={isIEBarDisabled} onImported={onImported}/>
</Menu> </Menu>
<Layout> <Layout>
@ -64,17 +61,13 @@ export const WellOperations = memo(({ idWell }) => {
<PrivateRoute path={`${rootPath}/params`}> <PrivateRoute path={`${rootPath}/params`}>
<WellDrillParams idWell={idWell}/> <WellDrillParams idWell={idWell}/>
</PrivateRoute> </PrivateRoute>
<PrivateRoute path={`${rootPath}/composite/:tab?`}>
<WellCompositeEditor idWell={idWell}/>
</PrivateRoute>
<PrivateDefaultRoute urls={[ <PrivateDefaultRoute urls={[
`${rootPath}/plan`, `${rootPath}/plan`,
`${rootPath}/fact`, `${rootPath}/fact`,
`${rootPath}/tvd`, `${rootPath}/tvd`,
`${rootPath}/sections`, `${rootPath}/sections`,
`${rootPath}/drillProcessFlow`, `${rootPath}/drillProcessFlow`,
`${rootPath}/params`, `${rootPath}/params`
`${rootPath}/composite`
]}/> ]}/>
</Switch> </Switch>
</Content> </Content>

View File

@ -1 +1 @@
npx openapi -i http://192.168.1.70:5000/swagger/v1/swagger.json -o src/services/api npx openapi -i http://46.146.209.148/swagger/v1/swagger.json -o src/services/api

86
src/utils/functions.tsx Normal file
View File

@ -0,0 +1,86 @@
import { OperationStatService, WellOperationDto, WellOperationDtoPlanFactPredictBase } from '@api'
const maxPrefix = 'isMax'
const minPrefix = 'isMin'
export const getPrecision = (number: number) => Number.isFinite(number) ? number.toFixed(2) : '-'
export type KeyType = number | string
export type SaubData = {
key?: number
depth?: number
date?: string
day?: number
}
export const getOperations = async (idWell: number): Promise<{
operations?: WellOperationDtoPlanFactPredictBase[],
plan?: SaubData[]
fact?: SaubData[]
predict?: SaubData[]
}> => {
const ops = await OperationStatService.getTvd(idWell)
if (!ops) return {}
const convert = (operation?: WellOperationDto): SaubData => ({
key: operation?.id,
depth: operation?.depthStart,
date: operation?.dateStart,
day: operation?.day,
})
const planData = ops
.map(item => convert(item.plan))
.filter(el => el.key)
const factData = ops
.map(item => convert(item.fact))
.filter(el => el.key)
const predictData = ops
.map(item => convert(item.predict))
.filter(el => el.key)
return { operations: ops, plan: planData, fact: factData, predict: predictData }
}
export const makeFilterMinMaxFunction = <T extends unknown>(key: KeyType) => (filterValue: string, dataItem: Record<KeyType, T>) => {
if (filterValue === 'max') return dataItem[maxPrefix + key]
if (filterValue === 'min') return dataItem[minPrefix + key]
return false
}
export const calcAndUpdateStats = (data: Record<KeyType, number | boolean>[], keys: KeyType[]) => {
const mins: Record<KeyType, number | boolean> = {}
const maxs: Record<KeyType, number | boolean> = {}
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][maxPrefix + key] = data[i][key] === maxs[key]
data[i][minPrefix + key] = data[i][key] === mins[key]
})
}
}
export const calcAndUpdateStatsBySections = (data: { sectionType: any }[], keys: KeyType[]) => {
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)
})
}

View File

@ -93,6 +93,10 @@ export const requirements: PermissionRecord = {
telemetry: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'], telemetry: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'],
message: ['Deposit.get', 'TelemetryDataSaub.get'], message: ['Deposit.get', 'TelemetryDataSaub.get'],
report: ['Deposit.get', 'Report.get'], report: ['Deposit.get', 'Report.get'],
composite: {
wells: ['Deposit.get', 'OperationStat.get', 'WellComposite.get'],
sections: ['Deposit.get', 'OperationStat.get', 'WellComposite.get', 'DrillParams.get'],
},
operations: { operations: {
tvd: ['Deposit.get', 'OperationStat.get'], tvd: ['Deposit.get', 'OperationStat.get'],
sections: ['Deposit.get', 'OperationStat.get'], sections: ['Deposit.get', 'OperationStat.get'],
@ -100,10 +104,6 @@ export const requirements: PermissionRecord = {
fact: ['Deposit.get', 'WellOperation.get'], fact: ['Deposit.get', 'WellOperation.get'],
drillProcessFlow: ['Deposit.get', 'DrillFlowChart.get'], drillProcessFlow: ['Deposit.get', 'DrillFlowChart.get'],
params: ['Deposit.get', 'WellOperation.get', 'DrillParams.get'], params: ['Deposit.get', 'WellOperation.get', 'DrillParams.get'],
composite: {
wells: ['Deposit.get', 'OperationStat.get', 'WellComposite.get'],
sections: ['Deposit.get', 'OperationStat.get', 'WellComposite.get', 'DrillParams.get'],
}
}, },
telemetryAnalysis: { telemetryAnalysis: {
depthToDay: ['Deposit.get', 'TelemetryAnalytics.get'], depthToDay: ['Deposit.get', 'TelemetryAnalytics.get'],

View File

@ -1,5 +1,11 @@
export const makeTextOnFilter = (key: string) => export const makeTextOnFilter = (key: string) =>
(value: string, record?: Record<string, string>) => record?.[key]?.startsWith(value) (value: string, record?: Record<string, unknown>) => String(record?.[key]).startsWith(value)
export const makeArrayOnFilter = (key: string) =>
(value: string, record?: Record<string, string[]>) => (!value && (record?.[key]?.length ?? 0) <= 0) || record?.[key]?.includes(value)
export const makeObjectOnFilter = (field: string, key: string) =>
(value: string, record?: Record<string, Record<string, unknown>>) => String(record?.[field]?.[key]).startsWith(value)
export const makeTextFilters = (array: Record<string, unknown>[], keys: string[]) => { export const makeTextFilters = (array: Record<string, unknown>[], keys: string[]) => {
const filters: string[][] = Array(keys.length) const filters: string[][] = Array(keys.length)