Merge branch 'dev' into feature/documentation

This commit is contained in:
Александр Сироткин 2022-12-19 08:07:37 +05:00
commit 923d469a86
44 changed files with 305 additions and 560 deletions

10
.vscode/settings.json vendored
View File

@ -2,7 +2,15 @@
"cSpell.words": [ "cSpell.words": [
"день", "день",
"спиннера", "спиннера",
"Saub" "Saub",
"КНБК",
"САУБ",
"antd",
"Poprompt",
"saub",
"setpoint",
"Setpoints",
"usehooks"
], ],
"liveServer.settings.port": 5501 "liveServer.settings.port": 5501
} }

View File

@ -40,6 +40,7 @@ export const makeDateColumn = <T extends unknown>(
other?: ColumnProps<T>, other?: ColumnProps<T>,
pickerOther?: DatePickerWrapperProps, pickerOther?: DatePickerWrapperProps,
) => makeColumn<T>(title, key, { ) => makeColumn<T>(title, key, {
editable: true,
...other, ...other,
render: (date) => ( render: (date) => (
<div className={'text-align-r-container'}> <div className={'text-align-r-container'}>

View File

@ -9,7 +9,6 @@ import { OmitExtends } from '@utils/types'
export * from './date' export * from './date'
export * from './time' export * from './time'
export * from './numeric' export * from './numeric'
export * from './plan_fact'
export * from './select' export * from './select'
export * from './tag' export * from './tag'
export * from './text' export * from './text'
@ -45,6 +44,7 @@ export const makeColumn = <T = any>(title: ReactNode, key: Key, other?: ColumnPr
title: title, title: title,
key: key, key: key,
dataIndex: key, dataIndex: key,
render: (value: T) => value,
...other, ...other,
}) })

View File

@ -1,4 +1,3 @@
import { ColumnFilterItem } from 'antd/lib/table/interface'
import { InputNumber } from 'antd' import { InputNumber } from 'antd'
import { Key, ReactNode } from 'react' import { Key, ReactNode } from 'react'
@ -46,18 +45,17 @@ export const makeNumericColumnOptions = <T extends number>(fixed?: number, sorte
export const makeNumericColumn = <T extends number>( export const makeNumericColumn = <T extends number>(
title: ReactNode, title: ReactNode,
key: Key, key: Key,
filters?: ColumnFilterItem[],
filterDelegate?: FilterGenerator<T>,
renderDelegate?: RenderMethod<T>, renderDelegate?: RenderMethod<T>,
filterDelegate?: FilterGenerator<T>,
width?: string | number, width?: string | number,
other?: ColumnProps<T>, other?: ColumnProps<T>,
) => makeColumn(title, key, { ) => makeColumn(title, key, {
filters, editable: true,
onFilter: filterDelegate ? filterDelegate(key) : undefined, onFilter: filterDelegate ? filterDelegate(key) : undefined,
sorter: makeNumericSorter(key), sorter: makeNumericSorter(key),
width, width,
input: <InputNumber style={{ width: '100%' }}/>, input: <InputNumber style={{ width: '100%' }} defaultValue={0} />,
render: renderDelegate ?? makeNumericRender<T>(2), render: renderDelegate || makeNumericRender<T>(2),
align: 'right', align: 'right',
...other ...other
}) })
@ -65,54 +63,78 @@ export const makeNumericColumn = <T extends number>(
export const makeNumericColumnPlanFact = <T extends number>( export const makeNumericColumnPlanFact = <T extends number>(
title: ReactNode, title: ReactNode,
key: Key, key: Key,
filters?: ColumnFilterItem[],
filterDelegate?: FilterGenerator<T>,
renderDelegate?: RenderMethod<T>, renderDelegate?: RenderMethod<T>,
filterDelegate?: FilterGenerator<T>,
width?: string | number,
other?: ColumnProps<T>,
) => {
return {
title,
children: [
makeNumericColumn<T>('План', `${key}.plan`, renderDelegate, filterDelegate, width, other),
makeNumericColumn<T>('Факт', `${key}.fact`, renderDelegate, filterDelegate, width, other),
]
}
}
/**
* @deprecated Для значений типа план/факт появилась модель `PlanFactDto`, использование 2 полей с суффиксами неактуально
* @param title Заголовок столбца
* @param key Ключ столбца
* @param filters Список значений для фильтрации
* @param filterDelegate Метод фильтрации
* @param renderDelegate Render-метод отображения ячейки
* @param width Ширина столбца
* @param other Дополнительные опции
* @returns Объект-столбец для таблицы
*/
export const makeNumericColumnPlanFactOld = <T extends number>(
title: ReactNode,
key: Key,
renderDelegate?: RenderMethod<T>,
filterDelegate?: FilterGenerator<T>,
width?: string | number, width?: string | number,
other?: ColumnProps<T>, other?: ColumnProps<T>,
) => makeGroupColumn(title, [ ) => makeGroupColumn(title, [
makeNumericColumn<T>('п', key + 'Plan', filters, filterDelegate, renderDelegate, width, other), makeNumericColumn<T>('План', key + 'Plan', renderDelegate, filterDelegate, width, other),
makeNumericColumn<T>('ф', key + 'Fact', filters, filterDelegate, renderDelegate, width, other), makeNumericColumn<T>('Факт', key + 'Fact', renderDelegate, filterDelegate, width, other),
]) ])
export const makeNumericStartEnd = <T extends number>( export const makeNumericStartEnd = <T extends number>(
title: ReactNode, title: ReactNode,
key: Key, key: Key,
fixed: number, fixed: number,
filters?: ColumnFilterItem[],
filterDelegate?: FilterGenerator<T>,
renderDelegate?: RenderMethod<T>, renderDelegate?: RenderMethod<T>,
filterDelegate?: FilterGenerator<T>,
width?: string | number, width?: string | number,
) => makeGroupColumn(title, [ ) => makeGroupColumn(title, [
makeNumericColumn<T>('старт', key + 'Start', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, key + 'Start')), makeNumericColumn<T>('старт', key + 'Start', renderDelegate, filterDelegate, width, makeNumericColumnOptions(fixed, key + 'Start')),
makeNumericColumn<T>('конец', key + 'End', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, key + 'End')) makeNumericColumn<T>('конец', key + 'End', renderDelegate, filterDelegate, width, makeNumericColumnOptions(fixed, key + 'End'))
]) ])
export const makeNumericMinMax = <T extends number>( export const makeNumericMinMax = <T extends number>(
title: ReactNode, title: ReactNode,
key: Key, key: Key,
fixed: number, fixed: number,
filters?: ColumnFilterItem[],
filterDelegate?: FilterGenerator<T>,
renderDelegate?: RenderMethod<T>, renderDelegate?: RenderMethod<T>,
filterDelegate?: FilterGenerator<T>,
width?: string | number, width?: string | number,
) => makeGroupColumn(title, [ ) => makeGroupColumn(title, [
makeNumericColumn<T>('мин', key + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, key + 'Min')), makeNumericColumn<T>('мин', key + 'Min', renderDelegate, filterDelegate, width, makeNumericColumnOptions(fixed, key + 'Min')),
makeNumericColumn<T>('макс', key + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, key + 'Max')), makeNumericColumn<T>('макс', key + 'Max', renderDelegate, filterDelegate, width, makeNumericColumnOptions(fixed, key + 'Max')),
]) ])
export const makeNumericAvgRange = <T extends number>( export const makeNumericAvgRange = <T extends number>(
title: ReactNode, title: ReactNode,
key: Key, key: Key,
fixed: number, fixed: number,
filters?: ColumnFilterItem[],
filterDelegate?: FilterGenerator<T>,
renderDelegate?: RenderMethod<T>, renderDelegate?: RenderMethod<T>,
filterDelegate?: FilterGenerator<T>,
width?: string | number, width?: string | number,
) => makeGroupColumn(title, [ ) => makeGroupColumn(title, [
makeNumericColumn<T>('мин', `${key}.min`, filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, `${key}.min`)), makeNumericColumn<T>('мин', `${key}.min`, renderDelegate, filterDelegate, width, makeNumericColumnOptions(fixed, `${key}.min`)),
makeNumericColumn<T>('сред', `${key}.avg`, filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, `${key}.avg`)), makeNumericColumn<T>('сред', `${key}.avg`, renderDelegate, filterDelegate, width, makeNumericColumnOptions(fixed, `${key}.avg`)),
makeNumericColumn<T>('макс', `${key}.max`, filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, `${key}.max`)), makeNumericColumn<T>('макс', `${key}.max`, renderDelegate, filterDelegate, width, makeNumericColumnOptions(fixed, `${key}.max`)),
]) ])
export default makeNumericColumn export default makeNumericColumn

View File

@ -1,22 +0,0 @@
import { Key, ReactNode } from 'react'
import { ColumnProps, makeColumn } from '.'
export const makeColumnsPlanFact = <T,>(
title: string | ReactNode,
key: Key | [Key, Key],
columsOther?: ColumnProps<T> | [ColumnProps<T>, ColumnProps<T>],
) => {
const keys = Array.isArray(key) ? key : [`${key}Plan`, `${key}Fact`]
const others = Array.isArray(columsOther) ? columsOther : [columsOther, columsOther]
return {
title,
children: [
makeColumn<T>('план', keys[0], others[0]),
makeColumn<T>('факт', keys[1], others[1]),
]
}
}
export default makeColumnsPlanFact

View File

@ -1,9 +1,17 @@
import { Select, SelectProps } from 'antd' import { Select, SelectProps } from 'antd'
import { DefaultOptionType, SelectValue } from 'antd/lib/select' import { DefaultOptionType, SelectValue } from 'antd/lib/select'
import { Key, ReactNode } from 'react' import { Key, ReactNode, useMemo } from 'react'
import { ColumnProps, makeColumn } from '.' import { ColumnProps, makeColumn } from '.'
const findOption = <T extends DefaultOptionType>(value: any, options: T[] | undefined) =>
options?.find((option) => String(option?.value) === String(value))
const SelectWrapper = ({ value, options, ...other }: SelectProps) => {
const selectValue = useMemo(() => findOption(value, options)?.label, [value, options])
return <Select value={selectValue} options={options} {...other} />
}
export const makeSelectColumn = <T extends DefaultOptionType>( export const makeSelectColumn = <T extends DefaultOptionType>(
title: ReactNode, title: ReactNode,
key: Key, key: Key,
@ -12,10 +20,11 @@ export const makeSelectColumn = <T extends DefaultOptionType>(
other?: ColumnProps<T>, other?: ColumnProps<T>,
selectOther?: SelectProps<SelectValue> selectOther?: SelectProps<SelectValue>
) => makeColumn(title, key, { ) => makeColumn(title, key, {
editable: true,
...other, ...other,
input: <Select options={options} {...selectOther}/>, input: <SelectWrapper options={options} {...selectOther}/>,
render: (value, dataset, index) => { render: (value, dataset, index) => {
const item = options?.find(option => String(option?.value) === String(value)) const item = findOption(value, options)
return other?.render?.(item, dataset, index) ?? item?.label ?? defaultValue?.label ?? value?.label ?? '--' return other?.render?.(item, dataset, index) ?? item?.label ?? defaultValue?.label ?? value?.label ?? '--'
} }
}) })

View File

@ -65,6 +65,7 @@ export const makeTagColumn = <T extends DataType>(
const InputComponent = makeTagInput<T>(value_key, label_key) const InputComponent = makeTagInput<T>(value_key, label_key)
return makeColumn(title, dataIndex, { return makeColumn(title, dataIndex, {
editable: true,
...other, ...other,
render: (item: T[] | undefined, dataset, index) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm, dataset, index) ?? elm[label_key]}</Tag>) ?? '-', render: (item: T[] | undefined, dataset, index) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm, dataset, index) ?? elm[label_key]}</Tag>) ?? '-',
input: <InputComponent {...tagOther} options={options} />, input: <InputComponent {...tagOther} options={options} />,

View File

@ -1,3 +1,4 @@
import { Tooltip } from 'antd'
import { ColumnFilterItem } from 'antd/lib/table/interface' import { ColumnFilterItem } from 'antd/lib/table/interface'
import { Key, ReactNode } from 'react' import { Key, ReactNode } from 'react'
@ -15,6 +16,18 @@ export const makeStringSorter = <T extends string>(key: Key): SorterMethod<T> =>
return String(vA).localeCompare(String(vB)) return String(vA).localeCompare(String(vB))
} }
export const makeTextRender = <T extends string>(def = '---', stringCutter?: (text: string) => string) => (value: T) => {
if (!value) return def
if (stringCutter) {
return (
<Tooltip title={value}>
{stringCutter(value)}
</Tooltip>
)
}
return value
}
export const makeFilterTextMatch = <T extends unknown>(key: keyof DataType<T>) => export const makeFilterTextMatch = <T extends unknown>(key: keyof DataType<T>) =>
(filterValue: T, dataItem: DataType<T>) => dataItem[key] === filterValue (filterValue: T, dataItem: DataType<T>) => dataItem[key] === filterValue
@ -26,10 +39,11 @@ export const makeTextColumn = <T extends unknown = any>(
render?: RenderMethod<T>, render?: RenderMethod<T>,
other?: ColumnProps other?: ColumnProps
) => makeColumn(title, key, { ) => makeColumn(title, key, {
editable: true,
filters, filters,
onFilter: filters ? makeFilterTextMatch(key) : undefined, onFilter: filters ? makeFilterTextMatch(key) : undefined,
sorter: sorter ?? makeStringSorter(key), sorter: sorter || makeStringSorter(key),
render: render, render: render || makeTextRender(),
...other ...other
}) })

View File

@ -24,6 +24,7 @@ export const makeTimeColumn = <T extends TimeDto>(
other?: ColumnProps, other?: ColumnProps,
pickerOther?: TimePickerWrapperProps, pickerOther?: TimePickerWrapperProps,
) => makeColumn<T>(title, key, { ) => makeColumn<T>(title, key, {
editable: true,
...other, ...other,
render: (time) => ( render: (time) => (
<div className={'text-align-r-container'}> <div className={'text-align-r-container'}>

View File

@ -59,6 +59,7 @@ export const WellSelector = memo(({ value, onChange, treeData, treeLabels, ...ot
<TreeSelect <TreeSelect
multiple multiple
treeCheckable treeCheckable
maxTagCount={'responsive'}
showCheckedStrategy={TreeSelect.SHOW_CHILD} showCheckedStrategy={TreeSelect.SHOW_CHILD}
treeDefaultExpandAll treeDefaultExpandAll
treeData={wellsTree} treeData={wellsTree}

View File

@ -3,11 +3,12 @@ import { Input } from 'antd'
import { import {
EditableTable, EditableTable,
makeColumn,
makeSelectColumn, makeSelectColumn,
makeStringSorter, makeStringSorter,
defaultPagination, defaultPagination,
makeTimezoneColumn makeTimezoneColumn,
makeNumericColumn,
makeTextColumn
} from '@components/Table' } from '@components/Table'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminClusterService, AdminDepositService } from '@api' import { AdminClusterService, AdminDepositService } from '@api'
@ -30,17 +31,11 @@ const ClusterController = memo(() => {
const clusterColumns = useMemo(() => [ const clusterColumns = useMemo(() => [
makeSelectColumn('Месторождение', 'idDeposit', deposits, '--', { makeSelectColumn('Месторождение', 'idDeposit', deposits, '--', {
width: 200, width: 200,
editable: true,
sorter: makeStringSorter('idDeposit') sorter: makeStringSorter('idDeposit')
}), }),
makeColumn('Название', 'caption', { makeTextColumn('Название', 'caption', undefined, undefined, undefined, { width: 200, formItemRules: min1 }),
width: 200, makeNumericColumn('Широта', 'latitude', coordsFormat, undefined, 150),
editable: true, makeNumericColumn('Долгота', 'longitude', coordsFormat, undefined, 150),
sorter: makeStringSorter('caption'),
formItemRules: min1,
}),
makeColumn('Широта', 'latitude', { width: 150, editable: true, render: coordsFormat }),
makeColumn('Долгота', 'longitude', { width: 150, editable: true, render: coordsFormat }),
makeTimezoneColumn('Зона', 'timezone', null, true, { width: 150 }), makeTimezoneColumn('Зона', 'timezone', null, true, { width: 150 }),
], [deposits]) ], [deposits])

View File

@ -3,10 +3,9 @@ import { Input } from 'antd'
import { import {
EditableTable, EditableTable,
makeColumn,
makeStringSorter,
makeSelectColumn, makeSelectColumn,
defaultPagination defaultPagination,
makeTextColumn
} from '@components/Table' } from '@components/Table'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminCompanyService, AdminCompanyTypeService } from '@api' import { AdminCompanyService, AdminCompanyTypeService } from '@api'
@ -37,16 +36,8 @@ const CompanyController = memo(() => {
})) }))
setColumns([ setColumns([
makeColumn('Название', 'caption', { makeTextColumn('Название', 'caption', undefined, undefined, undefined, { width: 200, formItemRules: min1 }),
width: 200, makeSelectColumn('Тип компании', 'idCompanyType', companyTypes, null, { width: 200 }),
editable: true,
sorter: makeStringSorter('caption'),
formItemRules: min1,
}),
makeSelectColumn('Тип компании', 'idCompanyType', companyTypes, null, {
width: 200,
editable: true
}),
]) ])
await updateTable() await updateTable()

View File

@ -1,19 +1,14 @@
import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { Input } from 'antd' import { Input } from 'antd'
import { EditableTable, makeColumn, makeStringSorter, defaultPagination } from '@components/Table' import { EditableTable, defaultPagination, makeTextColumn } from '@components/Table'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { arrayOrDefault, withPermissions } from '@utils' import { arrayOrDefault, withPermissions } from '@utils'
import { min1 } from '@utils/validationRules' import { min1 } from '@utils/validationRules'
import { AdminCompanyTypeService } from '@api' import { AdminCompanyTypeService } from '@api'
const columns = [ const columns = [
makeColumn('Название', 'caption', { makeTextColumn('Название', 'caption', undefined, undefined, undefined, { width: 200, formItemRules: min1 }),
width: 200,
editable: true,
sorter: makeStringSorter('caption'),
formItemRules: min1,
}),
] ]
const CompanyTypeController = memo(() => { const CompanyTypeController = memo(() => {

View File

@ -2,15 +2,15 @@ import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { Input } from 'antd' import { Input } from 'antd'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { EditableTable, makeColumn, defaultPagination, makeTimezoneColumn } from '@components/Table' import { EditableTable, defaultPagination, makeTimezoneColumn, makeTextColumn, makeNumericColumn } from '@components/Table'
import { arrayOrDefault, coordsFormat, withPermissions } from '@utils' import { arrayOrDefault, coordsFormat, withPermissions } from '@utils'
import { min1 } from '@utils/validationRules' import { min1 } from '@utils/validationRules'
import { AdminDepositService } from '@api' import { AdminDepositService } from '@api'
const depositColumns = [ const depositColumns = [
makeColumn('Название', 'caption', { width: 200, editable: true, formItemRules: min1 }), makeTextColumn('Название', 'caption', undefined, undefined, undefined, { width: 200, formItemRules: min1 }),
makeColumn('Широта', 'latitude', { width: 150, editable: true, render: coordsFormat }), makeNumericColumn('Широта', 'latitude', coordsFormat, undefined, 150),
makeColumn('Долгота', 'longitude', { width: 150, editable: true, render: coordsFormat }), makeNumericColumn('Долгота', 'longitude', coordsFormat, undefined, 150),
makeTimezoneColumn('Зона', 'timezone', null, true, { width: 150 }), makeTimezoneColumn('Зона', 'timezone', null, true, { width: 150 }),
] ]

View File

@ -1,23 +1,15 @@
import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { Input } from 'antd' import { Input } from 'antd'
import { EditableTable, makeColumn, makeStringSorter } from '@components/Table' import { EditableTable, makeTextColumn } from '@components/Table'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { arrayOrDefault, withPermissions } from '@utils' import { arrayOrDefault, withPermissions } from '@utils'
import { min1 } from '@utils/validationRules' import { min1 } from '@utils/validationRules'
import { AdminPermissionService } from '@api' import { AdminPermissionService } from '@api'
const columns = [ const columns = [
makeColumn('Название', 'name', { makeTextColumn('Название', 'name', undefined, undefined, undefined, { isRequired: true, formItemRules: min1 }),
editable: true, makeTextColumn('Описание', 'description'),
sorter: makeStringSorter('name'),
isRequired: true,
formItemRules: min1,
}),
makeColumn('Описание', 'description', {
editable: true,
sorter: makeStringSorter('description'),
}),
] ]
const PermissionController = memo(() => { const PermissionController = memo(() => {

View File

@ -19,15 +19,13 @@ const RoleController = memo(() => {
)), [roles, searchValue]) )), [roles, searchValue])
const columns = useMemo(() => [ const columns = useMemo(() => [
makeTextColumn('Название', 'caption', null, null, null, { width: 100, editable: true, formItemRules: min1 }), makeTextColumn('Название', 'caption', null, null, null, { width: 100, formItemRules: min1 }),
makeTagColumn('Включённые роли', 'roles', roles, 'id', 'caption', { makeTagColumn('Включённые роли', 'roles', roles, 'id', 'caption', {
width: 400, width: 400,
editable: true,
render: (role) => <RoleView role={role} /> render: (role) => <RoleView role={role} />
}, { allowClear: true }), }, { allowClear: true }),
makeTagColumn('Разрешения', 'permissions', permissions, 'id', 'name', { makeTagColumn('Разрешения', 'permissions', permissions, 'id', 'name', {
width: 600, width: 600,
editable: true,
render: (permission) => <PermissionView info={permission} />, render: (permission) => <PermissionView info={permission} />,
}), }),
], [roles, permissions]) ], [roles, permissions])

View File

@ -50,7 +50,7 @@ const TelemetryController = memo(() => {
const columns = useMemo(() => [ const columns = useMemo(() => [
makeColumn('', 'hasParent', { render: mergeRender }), makeColumn('', 'hasParent', { render: mergeRender }),
makeNumericColumn('ID', 'id', null, null, makeNumericRender(0)), makeNumericColumn('ID', 'id', makeNumericRender(0)),
makeTextColumn('UID', 'remoteUid'), makeTextColumn('UID', 'remoteUid'),
makeTextColumn('Назначена на скважину', 'realWell'), makeTextColumn('Назначена на скважину', 'realWell'),
makeDateColumn('Дата начала бурения', 'drillingStartDate'), makeDateColumn('Дата начала бурения', 'drillingStartDate'),

View File

@ -115,7 +115,6 @@ const UserController = memo(() => {
setColumns([ setColumns([
makeTextColumn('Логин', 'login', null, null, null, { makeTextColumn('Логин', 'login', null, null, null, {
editable: true,
formItemRules: [ formItemRules: [
{ required: true }, { required: true },
...createLoginRules, ...createLoginRules,
@ -130,41 +129,34 @@ const UserController = memo(() => {
], ],
}), }),
makeTextColumn('Фамилия', 'surname', filters.surname, null, null, { makeTextColumn('Фамилия', 'surname', filters.surname, null, null, {
editable: true,
formItemRules: [{ required: true }, ...nameRules], formItemRules: [{ required: true }, ...nameRules],
filterSearch: true, filterSearch: true,
onFilter: makeTextOnFilter('surname'), onFilter: makeTextOnFilter('surname'),
}), }),
makeTextColumn('Имя', 'name', filters.name, null, null, { makeTextColumn('Имя', 'name', filters.name, null, null, {
editable: true,
formItemRules: nameRules, formItemRules: nameRules,
filterSearch: true, filterSearch: true,
onFilter: makeTextOnFilter('name'), onFilter: makeTextOnFilter('name'),
}), }),
makeTextColumn('Отчество', 'patronymic', filters.partonymic, null, null, { makeTextColumn('Отчество', 'patronymic', filters.partonymic, null, null, {
editable: true,
formItemRules: nameRules, formItemRules: nameRules,
filterSearch: true, filterSearch: true,
onFilter: makeTextOnFilter('patronymic'), onFilter: makeTextOnFilter('patronymic'),
}), }),
makeTextColumn('E-mail', 'email', filters.email, null, null, { makeTextColumn('E-mail', 'email', filters.email, null, null, {
editable: true,
formItemRules: [{ required: true }, ...emailRules], formItemRules: [{ required: true }, ...emailRules],
filterSearch: true, filterSearch: true,
onFilter: makeTextOnFilter('email'), onFilter: makeTextOnFilter('email'),
}), }),
makeTextColumn('Номер телефона', 'phone', null, null, null, { makeTextColumn('Номер телефона', 'phone', null, null, null, {
editable: true,
formItemRules: phoneRules, formItemRules: phoneRules,
}), }),
makeTextColumn('Должность', 'position', null, null, null, { editable: true }), makeTextColumn('Должность', 'position', null, null, null),
makeTextColumn('Роли', 'roleNames', roleFilters, null, rolesRender, { makeTextColumn('Роли', 'roleNames', roleFilters, null, rolesRender, {
editable: true,
input: <RoleTag roles={roles} />, input: <RoleTag roles={roles} />,
onFilter: makeArrayOnFilter('roleNames'), onFilter: makeArrayOnFilter('roleNames'),
}), }),
makeSelectColumn('Компания', 'idCompany', companies, '--', { makeSelectColumn('Компания', 'idCompany', companies, '--', {
editable: true,
sorter: makeNumericSorter('idCompany'), sorter: makeNumericSorter('idCompany'),
}) })
]) ])

View File

@ -12,11 +12,12 @@ import {
EditableTable, EditableTable,
makeColumn, makeColumn,
makeSelectColumn, makeSelectColumn,
makeStringSorter,
makeNumericSorter, makeNumericSorter,
makeTagColumn, makeTagColumn,
defaultPagination, defaultPagination,
makeTimezoneColumn, makeTimezoneColumn,
makeTextColumn,
makeNumericColumn,
} from '@components/Table' } from '@components/Table'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { TelemetryView, CompanyView } from '@components/views' import { TelemetryView, CompanyView } from '@components/views'
@ -81,23 +82,11 @@ const WellController = memo(() => {
})) }))
setColumns([ setColumns([
makeSelectColumn('Куст', 'idCluster', clusters, '--', { makeSelectColumn('Куст', 'idCluster', clusters, '--', { width: '5rem', sorter: makeNumericSorter('idCluster') }),
width: '5rem', makeTextColumn('Название', 'caption', undefined, undefined, undefined, { width: '5rem' }),
editable: true, makeSelectColumn('Тип', 'idWellType', wellTypes, '--', { width: 150, sorter: makeNumericSorter('idWellType') }),
sorter: makeNumericSorter('idCluster'), makeNumericColumn('Широта', 'latitude', coordsFormat, undefined, 150),
}), makeNumericColumn('Долгота', 'longitude', coordsFormat, undefined, 150),
makeColumn('Название', 'caption', {
width: '5rem',
editable: true,
sorter: makeStringSorter('caption'),
}),
makeSelectColumn('Тип', 'idWellType', wellTypes, '--', {
width: 150,
editable: true,
sorter: makeNumericSorter('idWellType'),
}),
makeColumn('Широта', 'latitude', { width: 150, editable: true, render: coordsFormat }),
makeColumn('Долгота', 'longitude', { width: 150, editable: true, render: coordsFormat }),
makeColumn('Телеметрия', 'telemetry', { makeColumn('Телеметрия', 'telemetry', {
editable: true, editable: true,
render: (telemetry) => <TelemetryView telemetry={telemetry} />, render: (telemetry) => <TelemetryView telemetry={telemetry} />,
@ -105,7 +94,6 @@ const WellController = memo(() => {
}, ), }, ),
makeTimezoneColumn('Зона', 'timezone', null, true, { width: 170 }), makeTimezoneColumn('Зона', 'timezone', null, true, { width: 170 }),
makeTagColumn('Компании', 'companies', companies, 'id', 'caption', { makeTagColumn('Компании', 'companies', companies, 'id', 'caption', {
editable: true,
render: (company) => <CompanyView company={company} />, render: (company) => <CompanyView company={company} />,
}), }),
]) ])

View File

@ -7,7 +7,7 @@ import {
makeTextColumn, makeTextColumn,
makeGroupColumn, makeGroupColumn,
makeColumn, makeColumn,
makeNumericColumnPlanFact, makeNumericColumnPlanFactOld,
Table, Table,
makeNumericRender, makeNumericRender,
makeNumericColumn, makeNumericColumn,
@ -117,7 +117,10 @@ const ClusterWells = memo(({ statsWells }) => {
const columns = useMemo(() => [ const columns = useMemo(() => [
makeTextColumn('скв №', 'caption', null, null, makeTextColumn('скв №', 'caption', null, null,
(_, item) => ( (_, item) => (
<Link to={{ pathname: `/well/${item.id}`, state: { from: location.pathname }}} style={{display: 'flex', alignItems: 'center'}}> <Link
to={{ pathname: `/well/${item.id}`, state: { from: location.pathname }}}
style={{ display: 'flex', alignItems: 'center' }}
>
<PointerIcon <PointerIcon
state={item.idState === 1 ? 'active' : 'unknown'} state={item.idState === 1 ? 'active' : 'unknown'}
width={32} width={32}
@ -133,10 +136,10 @@ const ClusterWells = memo(({ statsWells }) => {
makeDateColumn('начало', 'factStart'), makeDateColumn('начало', 'factStart'),
makeDateColumn('окончание', 'factEnd'), makeDateColumn('окончание', 'factEnd'),
]), ]),
makeNumericColumnPlanFact('Продолжительность, сут', 'period', filtersMinMax, makeFilterMinMaxFunction, numericRender), makeNumericColumnPlanFactOld('Продолжительность, сут', 'period', numericRender, makeFilterMinMaxFunction, { filters: filtersMinMax }),
makeNumericColumnPlanFact('МСП, м/ч', 'rateOfPenetration', filtersMinMax, makeFilterMinMaxFunction, numericRender), makeNumericColumnPlanFactOld('МСП, м/ч', 'rateOfPenetration', numericRender, makeFilterMinMaxFunction, { filters: filtersMinMax }),
makeNumericColumnPlanFact('Рейсовая скорость, м/ч', 'routeSpeed', filtersMinMax, makeFilterMinMaxFunction, numericRender), makeNumericColumnPlanFactOld('Рейсовая скорость, м/ч', 'routeSpeed', numericRender, makeFilterMinMaxFunction, { filters: filtersMinMax }),
makeNumericColumn('НПВ, ч', 'notProductiveTimeFact', filtersMinMax, makeFilterMinMaxFunction, numericRender), makeNumericColumn('НПВ, ч', 'notProductiveTimeFact', numericRender, makeFilterMinMaxFunction, { filters: filtersMinMax }),
makeColumn('TVD', 'tvd', { align: 'center', render: (_, value) => ( makeColumn('TVD', 'tvd', { align: 'center', render: (_, value) => (
<Button onClick={() => { <Button onClick={() => {
setSelectedWell(value) setSelectedWell(value)

View File

@ -1,14 +1,14 @@
import { memo, useMemo } from 'react' import { memo, useMemo } from 'react'
import { Table, makeTextColumn, makeNumericColumnPlanFact } from '@components/Table' import { Table, makeTextColumn, makeNumericColumnPlanFactOld } from '@components/Table'
import { getPrecision } from '@utils/functions' import { getPrecision } from '@utils/functions'
const columns = [ const columns = [
makeTextColumn('Конструкция секции', 'sectionType'), makeTextColumn('Конструкция секции', 'sectionType'),
makeTextColumn('Операция', 'operationName'), makeTextColumn('Операция', 'operationName'),
makeNumericColumnPlanFact('Глубина забоя', 'depth', null, null, (number) => getPrecision(number)), makeNumericColumnPlanFactOld('Глубина забоя', 'depth', (number) => getPrecision(number)),
makeNumericColumnPlanFact('Часы', 'durationHours', null, null, (number) => getPrecision(number)), makeNumericColumnPlanFactOld('Часы', 'durationHours', (number) => getPrecision(number)),
makeNumericColumnPlanFact('Комментарий', 'comment', null, null, (text) => text ?? '-') makeNumericColumnPlanFactOld('Комментарий', 'comment', (text) => text ?? '-'),
] ]
export const WellOperationsTable = memo(({ wellOperations }) => { export const WellOperationsTable = memo(({ wellOperations }) => {

View File

@ -18,7 +18,7 @@ const columns = [
makeNumericColumn('Проходка, м', 'sumDepthInterval'), makeNumericColumn('Проходка, м', 'sumDepthInterval'),
makeNumericColumn('Время работы, ч', 'usedTimeHours'), makeNumericColumn('Время работы, ч', 'usedTimeHours'),
makeNumericColumn('Кол-во запусков', 'operationCount'), makeNumericColumn('Кол-во запусков', 'operationCount'),
makeNumericColumn('Коэф. использования, %', 'kUsage', undefined, undefined, (value) => numericRender(value * 100)), makeNumericColumn('Коэф. использования, %', 'kUsage', (value) => numericRender(value * 100)),
] ]
const getSubsystemState = (subsystem) => { const getSubsystemState = (subsystem) => {

View File

@ -21,13 +21,13 @@ const speedNumericRender = (section) => numericRender(section?.speed)
const makeSectionSorter = (key, name) => (a, b) => (a?.[key]?.[name] ?? 0) - (b?.[key]?.[name] ?? 0) const makeSectionSorter = (key, name) => (a, b) => (a?.[key]?.[name] ?? 0) - (b?.[key]?.[name] ?? 0)
export const makeSectionColumn = (title, key, { speedRender } = {}) => makeGroupColumn(title, [ export const makeSectionColumn = (title, key, { speedRender } = {}) => makeGroupColumn(title, [
makeNumericColumn('Проходка', key, null, null, (section => numericRender(section?.depth)), 100, { makeNumericColumn('Проходка', key, (section => numericRender(section?.depth)), undefined, 100, {
sorter: makeSectionSorter(key, 'depth'), sorter: makeSectionSorter(key, 'depth'),
}), }),
makeNumericColumn('Время', key, null, null, (section => numericRender(section?.time)), 100, { makeNumericColumn('Время', key, (section => numericRender(section?.time)), undefined, 100, {
sorter: makeSectionSorter(key, 'time'), sorter: makeSectionSorter(key, 'time'),
}), }),
makeNumericColumn((<>V<sub>рейсовая</sub></>), key, null, null, speedRender ?? speedNumericRender, 100, { makeNumericColumn((<>V<sub>рейсовая</sub></>), key, speedRender ?? speedNumericRender, undefined, 100, {
sorter: makeSectionSorter(key, 'speed'), sorter: makeSectionSorter(key, 'speed'),
}), }),
]) ])
@ -37,7 +37,7 @@ export const defaultColumns = [
makeTextColumn('Скважина', 'caption', null, null, null, { fixed: 'left', width: 100 }), makeTextColumn('Скважина', 'caption', null, null, null, { fixed: 'left', width: 100 }),
] ]
const scrollSettings = { scrollToFirstRowOnChange: true, x: 100, y: 200 } const scrollSettings = { scrollToFirstRowOnChange: true, x: 100, y: '25vh' }
const summaryColSpan = 1 /// TODO: Когда добавится куст изменить на 2 const summaryColSpan = 1 /// TODO: Когда добавится куст изменить на 2
const getWellData = async (wellsList) => { const getWellData = async (wellsList) => {

View File

@ -1,146 +0,0 @@
import { memo, useCallback, useEffect, useState } from 'react'
import { Button, Modal, Popconfirm } from 'antd'
import { useWell } from '@asb/context'
import { makeColumn, makeGroupColumn, makeNumericRender, makeSelectColumn, Table } from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService, WellOperationService } from '@api'
const getDeepValue = (data, key) => {
if (!key || key.trim() === '') return null
const keys = key.split('.')
let out = data
while (keys.length > 0) {
if (!(keys[0] in out)) return null
out = out[keys[0]]
keys.splice(0, 1)
}
return out
}
const makeNumericSorter = (keys) => (a, b) => getDeepValue(a, keys) - getDeepValue(b, keys)
const numericRender = makeNumericRender(1)
const makeNumericColumn = (title, dataIndex, render, other) => makeColumn(title, dataIndex, {
sorter: makeNumericSorter(dataIndex),
render: (_, record, index) => {
const func = render ?? ((value) => <>{value}</>)
const item = getDeepValue(record, dataIndex)
return func(item, record, index)
},
align: 'right',
...other,
})
const makeAvgRender = (dataIndex) => (avg, record) => {
const max = record[dataIndex]?.max
const fillW = (max - avg) / max * 100
return (
<div className={'avg-column'}>
<div className={'avg-fill'} style={{ width: `${fillW}%` }} />
<div className={'avg-value'}>
{numericRender(avg)}
</div>
</div>
)
}
const makeNumericAvgRange = (title, dataIndex, defaultRender = false) => makeGroupColumn(title, [
makeNumericColumn('мин', `${dataIndex}.min`),
makeNumericColumn('сред', `${dataIndex}.avg`, defaultRender ? undefined : makeAvgRender(dataIndex)),
makeNumericColumn('макс', `${dataIndex}.max`),
])
export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
label: value,
value: id,
}))
return [
makeSelectColumn('Конструкция секции','idWellSectionType', sectionTypes, null, {
width: 160,
sorter: makeNumericSorter('idWellSectionType'),
}),
makeNumericAvgRange('Нагрузка, т', 'axialLoad'),
makeNumericAvgRange('Давление, атм', 'pressure'),
makeNumericAvgRange('Момент на ВСП, кН·м', 'rotorTorque', true),
makeNumericAvgRange('Обороты на ВСП, об/мин', 'rotorSpeed'),
makeNumericAvgRange('Расход, л/с', 'flow'),
]
}
export const NewParamsTable = memo(({ selectedWellsKeys }) => {
const [params, setParams] = useState([])
const [paramsColumns, setParamsColumns] = useState([])
const [showParamsLoader, setShowParamsLoader] = useState(false)
const [isParamsModalVisible, setIsParamsModalVisible] = useState(false)
const [well] = useWell()
useEffect(() => {
invokeWebApiWrapperAsync(async () => setParamsColumns(await getColumns(well.id)))
}, [well])
const onParamButtonClick = useCallback(() => invokeWebApiWrapperAsync(
async () => {
setIsParamsModalVisible(true)
const params = await DrillParamsService.getCompositeAll(well.id)
setParams(params)
},
setShowParamsLoader,
`Не удалось загрузить список режимов`,
{ actionName: 'Получение списка режимов скважины', well }
), [well])
const onParamsAddClick = useCallback(() => invokeWebApiWrapperAsync(
async () => {
await DrillParamsService.save(well.id, params)
setIsParamsModalVisible(false)
},
setShowParamsLoader,
`Не удалось добавить режимы в список`,
{ actionName: 'Добавление режима скважины', well }
), [well, params])
return (
<>
<Button
size={'large'}
disabled={selectedWellsKeys.length <= 0}
onClick={onParamButtonClick}
>
Заполнить режимы текущей скважины
</Button>
<Modal
title={'Заполнить режимы текущей скважины'}
centered
open={isParamsModalVisible}
onCancel={() => setIsParamsModalVisible(false)}
width={1700}
footer={(
<Popconfirm title={'Заменить существующие режимы выбранными?'} onConfirm={onParamsAddClick}>
<Button
size={'large'}
disabled={params.length <= 0}
>Сохранить</Button>
</Popconfirm>
)}
>
<LoaderPortal show={showParamsLoader}>
<Table
bordered
size={'small'}
columns={paramsColumns}
dataSource={params}
pagination={false}
/>
</LoaderPortal>
</Modal>
</>
)
})
export default NewParamsTable

View File

@ -1,12 +1,13 @@
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import { useState, useEffect, memo, useMemo, lazy, Suspense } from 'react' import { useState, useEffect, memo, useMemo, lazy, Suspense } from 'react'
import { LineChartOutlined, ProfileOutlined, TeamOutlined } from '@ant-design/icons' import { LineChartOutlined, ProfileOutlined, TeamOutlined } from '@ant-design/icons'
import { Button, Badge, Divider, Modal, Row, Col } from 'antd' import { Button, Badge, Divider, Modal } from 'antd'
import { useWell } from '@asb/context' import { useWell } from '@asb/context'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import SuspenseFallback from '@components/SuspenseFallback'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { Table, makeTextColumn, makeNumericColumnPlanFact, makeNumericColumn } from '@components/Table' import { Table, makeTextColumn, makeNumericColumnPlanFactOld, makeNumericColumn } from '@components/Table'
import { WellCompositeService } from '@api' import { WellCompositeService } from '@api'
import { import {
hasPermission, hasPermission,
@ -16,8 +17,6 @@ import {
getOperations getOperations
} from '@utils' } from '@utils'
import NewParamsTable from './NewParamsTable'
import SuspenseFallback from '@asb/components/SuspenseFallback'
const Tvd = lazy(() => import('@pages/Well/WellOperations/Tvd')) const Tvd = lazy(() => import('@pages/Well/WellOperations/Tvd'))
const CompaniesTable = lazy(() => import('@pages/Cluster/CompaniesTable')) const CompaniesTable = lazy(() => import('@pages/Cluster/CompaniesTable'))
@ -146,15 +145,15 @@ const WellCompositeSections = memo(({ statsWells, selectedSections }) => {
makeTextColumn('скв №', 'caption', null, null, makeTextColumn('скв №', 'caption', null, null,
(text, item) => <Link to={{ pathname: `/well/${item?.id}`, state: { from: location.pathname }}}>{text ?? '-'}</Link> (text, item) => <Link to={{ pathname: `/well/${item?.id}`, state: { from: location.pathname }}}>{text ?? '-'}</Link>
), ),
makeTextColumn('Секция', 'sectionType', filtersSectionsType, sortBySectionId, (text) => text ?? '-'), makeTextColumn('Секция', 'sectionType', filtersSectionsType, sortBySectionId, (text) => text ?? '-', { width: 100 }),
makeNumericColumnPlanFact('Глубина, м', 'sectionWellDepth', filtersMinMax, makeFilterMinMaxFunction), makeNumericColumnPlanFactOld('Глубина, м', 'sectionWellDepth', undefined, makeFilterMinMaxFunction, undefined, { filters: filtersMinMax }),
makeNumericColumnPlanFact('Продолжительность, ч', 'sectionBuildDays', filtersMinMax, makeFilterMinMaxFunction), makeNumericColumnPlanFactOld('Продолжительность, ч', 'sectionBuildDays', undefined, makeFilterMinMaxFunction, undefined, { filters: filtersMinMax }),
makeNumericColumnPlanFact('МСП, м/ч', 'sectionRateOfPenetration', filtersMinMax, makeFilterMinMaxFunction), makeNumericColumnPlanFactOld('МСП, м/ч', 'sectionRateOfPenetration', undefined, makeFilterMinMaxFunction, undefined, { filters: filtersMinMax }),
makeNumericColumnPlanFact('Рейсовая скорость, м/ч', 'sectionRouteSpeed', filtersMinMax, makeFilterMinMaxFunction), makeNumericColumnPlanFactOld('Рейсовая скорость, м/ч', 'sectionRouteSpeed', undefined, makeFilterMinMaxFunction, undefined, { filters: filtersMinMax }),
makeNumericColumnPlanFact('Спуск КНБК, м/ч', 'sectionBhaDownSpeed', filtersMinMax, makeFilterMinMaxFunction), makeNumericColumnPlanFactOld('Спуск КНБК, м/ч', 'sectionBhaDownSpeed', undefined, makeFilterMinMaxFunction, undefined, { filters: filtersMinMax }),
makeNumericColumnPlanFact('Подъем КНБК, м/ч', 'sectionBhaUpSpeed', filtersMinMax, makeFilterMinMaxFunction), makeNumericColumnPlanFactOld('Подъем КНБК, м/ч', 'sectionBhaUpSpeed', undefined, makeFilterMinMaxFunction, undefined, { filters: filtersMinMax }),
makeNumericColumnPlanFact('Скорость спуска ОК, м/ч', 'sectionCasingDownSpeed', filtersMinMax, makeFilterMinMaxFunction), makeNumericColumnPlanFactOld('Скорость спуска ОК, м/ч', 'sectionCasingDownSpeed', undefined, makeFilterMinMaxFunction, undefined, { filters: filtersMinMax }),
makeNumericColumn('НПВ, ч', 'nonProductiveHours', filtersMinMax, makeFilterMinMaxFunction, null, '80px'), makeNumericColumn('НПВ, ч', 'nonProductiveHours', undefined, makeFilterMinMaxFunction, '80px', { filters: filtersMinMax }),
{ {
title: 'TVD', title: 'TVD',
render: (value) => ( render: (value) => (
@ -200,11 +199,11 @@ const WellCompositeSections = memo(({ statsWells, selectedSections }) => {
dataSource={rows} dataSource={rows}
size={'small'} size={'small'}
bordered bordered
scroll={{ x: true, y: 620 }} scroll={{ x: true, y: '30vh' }}
rowSelection={rowSelection} rowSelection={rowSelection}
pagination={false} pagination={false}
/> />
<Divider /> <Divider style={{ marginTop: 0 }} />
<Badge.Ribbon text={'комбинированная скважина'} color={'gray'}> <Badge.Ribbon text={'комбинированная скважина'} color={'gray'}>
<h3>Выбранные секции</h3> <h3>Выбранные секции</h3>
</Badge.Ribbon> </Badge.Ribbon>
@ -214,12 +213,9 @@ const WellCompositeSections = memo(({ statsWells, selectedSections }) => {
rowSelection={rowSelection} rowSelection={rowSelection}
size={'small'} size={'small'}
bordered bordered
scroll={{ x: true }} scroll={{ x: true, y: '30vh' }}
pagination={false} pagination={false}
/> />
<Row justify={'end'} style={{ margin: '1rem 0' }}>
<Col><NewParamsTable selectedWellsKeys={selectedWellsKeys} /></Col>
</Row>
<Modal <Modal
title={'TVD'} title={'TVD'}

View File

@ -40,7 +40,7 @@ export const DocumentsTemplate = ({ idCategory, well: givenWell, mimeTypes, head
), ),
}, },
makeDateColumn('Дата загрузки', 'uploadDate'), makeDateColumn('Дата загрузки', 'uploadDate'),
makeNumericColumn('Размер', 'size', null, null, (value) => formatBytes(value)), makeNumericColumn('Размер', 'size', (value) => formatBytes(value)),
makeColumn('Автор', 'author', { render: item => <UserView user={item}/> }), makeColumn('Автор', 'author', { render: item => <UserView user={item}/> }),
makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> }), makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> }),
...(customColumns ?? []) ...(customColumns ?? [])

View File

@ -29,7 +29,7 @@ const categoryDictionary = {
// Конфигурация таблицы // Конфигурация таблицы
export const makeMessageColumns = (idWell) => [ export const makeMessageColumns = (idWell) => [
makeDateColumn('Дата', 'date', undefined, undefined, { width: '120px' }), makeDateColumn('Дата', 'date', undefined, undefined, { width: '120px' }),
makeNumericColumn('Глубина, м', 'wellDepth', null, null, (depth, item) => ( makeNumericColumn('Глубина, м', 'wellDepth', (depth, item) => (
<Tooltip title={'Нажмите для перехода в архив'}> <Tooltip title={'Нажмите для перехода в архив'}>
<Link <Link
style={{ color: 'inherit'}} style={{ color: 'inherit'}}
@ -40,7 +40,7 @@ export const makeMessageColumns = (idWell) => [
{depth.toFixed(2)} {depth.toFixed(2)}
</Link> </Link>
</Tooltip> </Tooltip>
), '7rem'), ), undefined, '7rem'),
makeColumn('Категория', 'categoryId', { makeColumn('Категория', 'categoryId', {
width: '8rem', width: '8rem',
render: (_, item) => categoryDictionary[item.categoryId].title, render: (_, item) => categoryDictionary[item.categoryId].title,

View File

@ -26,10 +26,10 @@ const tableColumns = [
<div className={'table_color'} style={{ backgroundColor }} /> <div className={'table_color'} style={{ backgroundColor }} />
)}), )}),
makeTextColumn('Подсистема', 'subsystemName'), makeTextColumn('Подсистема', 'subsystemName'),
makeNumericColumn('Использование, %', 'kUsage', undefined, undefined, val => (+val * 100).toFixed(2), 200), makeNumericColumn('Использование, %', 'kUsage', val => (+val * 100).toFixed(2), undefined, 200),
makeNumericColumn('Проходка, м', 'sumDepthInterval', undefined, undefined, undefined, 200), makeNumericColumn('Проходка, м', 'sumDepthInterval', undefined, undefined, 200),
makeNumericColumn('Время работы, ч', 'usedTimeHours', undefined, undefined, undefined, 200), makeNumericColumn('Время работы, ч', 'usedTimeHours', undefined, undefined, 200),
makeNumericColumn('Кол-во запусков', 'operationCount', undefined, undefined, makeNumericRender(0), 200), makeNumericColumn('Кол-во запусков', 'operationCount', makeNumericRender(0), undefined, 200),
] ]
// Выбор доступен только до текущей даты // Выбор доступен только до текущей даты

View File

@ -5,14 +5,13 @@ import { EditableTable, makeTextColumn } from '@components/Table'
import { DrillerService } from '@api' import { DrillerService } from '@api'
const columnOptions = { const columnOptions = {
editable: true,
formItemRules: [{ message: 'Обязательное поле!', required: true }] formItemRules: [{ message: 'Обязательное поле!', required: true }]
} }
const columns = [ const columns = [
makeTextColumn('Фамилия', 'surname', undefined, undefined, undefined, columnOptions), makeTextColumn('Фамилия', 'surname', undefined, undefined, undefined, columnOptions),
makeTextColumn('Имя', 'name', undefined, undefined, undefined, columnOptions), makeTextColumn('Имя', 'name', undefined, undefined, undefined, columnOptions),
makeTextColumn('Отчество', 'patronymic', undefined, undefined, undefined, { editable: true }), makeTextColumn('Отчество', 'patronymic'),
] ]
const rowClassName = (record) => record.has ? 'driller_list_active' : '' const rowClassName = (record) => record.has ? 'driller_list_active' : ''

View File

@ -14,7 +14,6 @@ import { arrayOrDefault } from '@utils'
import { ScheduleService } from '@api' import { ScheduleService } from '@api'
const columnOptions = { const columnOptions = {
editable: true,
formItemRules: [{ message: 'Обязательное поле!', required: true }] formItemRules: [{ message: 'Обязательное поле!', required: true }]
} }

View File

@ -24,11 +24,11 @@ const makeDrillerSorter = (key) => (a, b) => {
export const columns = [ export const columns = [
makeTextColumn('Бурильщик', 'driller', null, makeDrillerSorter('driller'), drillerRender, { width: 200 }), makeTextColumn('Бурильщик', 'driller', null, makeDrillerSorter('driller'), drillerRender, { width: 200 }),
makeNumericColumn('Кол-во операций', 'count', null, null, (value) => parseInt(value), 150), makeNumericColumn('Кол-во операций', 'count', (value) => parseInt(value), undefined, 150),
makeNumericColumn('Среднее по ключевому показателю', 'averageValue', null, null, numericRender, 150), makeNumericColumn('Среднее по ключевому показателю', 'averageValue', numericRender, undefined, 150),
makeNumericColumn('Среднее целевого показателя', 'averageTargetValue', null, null, numericRender, 150), makeNumericColumn('Среднее целевого показателя', 'averageTargetValue', numericRender, undefined, 150),
makeNumericColumn('Эффективность (%)', 'efficiency', null, null, numericRender, 150), makeNumericColumn('Эффективность (%)', 'efficiency', numericRender, undefined, 150),
makeNumericColumn('Коэффициент потерь', 'loss', null, null, numericRender, 100), makeNumericColumn('Коэффициент потерь', 'loss', numericRender, undefined, 100),
] ]
export const OperationsTable = memo(({ data, height, ...other }) => ( export const OperationsTable = memo(({ data, height, ...other }) => (

View File

@ -8,7 +8,6 @@ import { DetectedOperationService, OperationValueService } from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
const columnOptions = { const columnOptions = {
editable: true,
formItemRules: [{ message: 'Обязательное поле!', required: true }] formItemRules: [{ message: 'Обязательное поле!', required: true }]
} }
@ -72,11 +71,11 @@ export const TargetEditor = memo(({ loading, onChange }) => {
filterOption: (input, option) => filterOption: (input, option) =>
String(option?.label ?? '').toLowerCase().indexOf(input.toLowerCase()) >= 0 String(option?.label ?? '').toLowerCase().indexOf(input.toLowerCase()) >= 0
}), }),
makeNumericColumn('Цель', 'targetValue', undefined, undefined, numericRender, 150, columnOptions), makeNumericColumn('Цель', 'targetValue', numericRender, undefined, 150, columnOptions),
makeNumericColumn('Норм.', 'standardValue', undefined, undefined, numericRender, 150, columnOptions), makeNumericColumn('Норм.', 'standardValue', numericRender, undefined, 150, columnOptions),
makeGroupColumn('Глубина, м', [ makeGroupColumn('Глубина, м', [
makeNumericColumn('Начало', 'depthStart', undefined, undefined, numericRender, 150, columnOptions), makeNumericColumn('Начало', 'depthStart', numericRender, undefined, 150, columnOptions),
makeNumericColumn('Окончание', 'depthEnd', undefined, undefined, numericRender, 150, columnOptions), makeNumericColumn('Окончание', 'depthEnd', numericRender, undefined, 150, columnOptions),
]), ]),
]) ])
}, },

View File

@ -1,11 +1,11 @@
import { memo, useCallback, useMemo, useState } from 'react' import { memo, useCallback, useMemo, useState } from 'react'
import { Select, Modal, Input, InputNumber } from 'antd' import { Modal, Input } from 'antd'
import { useWell } from '@asb/context' import { useWell } from '@asb/context'
import { Grid, GridItem } from '@components/Grid' import { Grid, GridItem } from '@components/Grid'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { makeNumericRender, EditableTable } from '@components/Table' import { makeNumericRender, EditableTable, makeSelectColumn, makeNumericColumn } from '@components/Table'
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker' import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
import { SetpointsService } from '@api' import { SetpointsService } from '@api'
@ -19,24 +19,8 @@ export const SetpointSender = memo(({ onClose, visible, setpointNames }) => {
const [well] = useWell() const [well] = useWell()
const addingColumns = useMemo(() => [ const addingColumns = useMemo(() => [
{ makeSelectColumn('Наименование уставки', 'name', setpointNames, undefined, { width: 200, isRequired: true }),
title: 'Наименование уставки', makeNumericColumn('Значение', 'value', makeNumericRender(1), undefined, 125, { isRequired: true, align: 'right' }),
dataIndex: 'name',
editable: true,
isRequired: true,
width: 200,
input: <Select options={setpointNames} />,
render: (val) => setpointNames.find((name) => name.value === val)?.label
}, {
title: 'Значение',
dataIndex: 'value',
editable: true,
isRequired: true,
width: 125,
input: <InputNumber style={{ width: '100%' }} formatter={value => `${value}`.replace(',', '.')}/>,
render: makeNumericRender(1),
align: 'right'
}
], [setpointNames]) ], [setpointNames])
const onAdd = useCallback(async (sp) => setSetpoints((prevSp) => { const onAdd = useCallback(async (sp) => setSetpoints((prevSp) => {

View File

@ -5,9 +5,9 @@ import { Grid, GridItem } from '@components/Grid'
import { getChartIcon, makeDisplayValue } from '@utils' import { getChartIcon, makeDisplayValue } from '@utils'
import moment from 'moment' import moment from 'moment'
const defaultFormater = makeDisplayValue({ def: '---', fixed: 2 }) const defaultFormatter = makeDisplayValue({ def: '---', fixed: 2 })
const defaultValueRender = (v, unit) => ( const defaultValueRender = (v, unit) => (
<>{defaultFormater(v)} {unit ?? ''}</> <>{defaultFormatter(v)} {unit ?? ''}</>
) )
export const cursorRender = (group, data, flowData) => { export const cursorRender = (group, data, flowData) => {
@ -17,7 +17,7 @@ export const cursorRender = (group, data, flowData) => {
const firstChart = group.charts[0] const firstChart = group.charts[0]
const y = firstChart.y(d) const y = firstChart.y(d)
const yDate = moment(y) const yDate = moment(y)
const flow = flowData.filter((row) => yDate.isBetween(row.dateStart, row.dateEnd, 's', '[]')) const flow = flowData?.filter((row) => yDate.isBetween(row.dateStart, row.dateEnd, 's', '[]'))
const yValue = firstChart.yAxis.format?.(y) ?? defaultValueRender(y, firstChart.yAxis.unit) const yValue = firstChart.yAxis.format?.(y) ?? defaultValueRender(y, firstChart.yAxis.unit)
const xFormat = (chart) => { const xFormat = (chart) => {
const v = chart.x(d) const v = chart.x(d)
@ -42,13 +42,13 @@ export const cursorRender = (group, data, flowData) => {
{group.charts.filter((chart) => chart.type === 'rect_area').map((chart, i) => { {group.charts.filter((chart) => chart.type === 'rect_area').map((chart, i) => {
const minX = getByAccessor(chart.minXAccessor) const minX = getByAccessor(chart.minXAccessor)
const maxX = getByAccessor(chart.maxXAccessor) const maxX = getByAccessor(chart.maxXAccessor)
const value = (row) => <span>{defaultFormater(minX(row))} - {defaultFormater(maxX(row))}</span> const value = (row) => <span>{defaultFormatter(minX(row))} - {defaultFormatter(maxX(row))}</span>
return ( return (
<Fragment key={chart.key}> <Fragment key={chart.key}>
<GridItem row={i+2} col={1} style={{ padding: '2px 0' }}>{getChartIcon(chart)}</GridItem> <GridItem row={i+2} col={1} style={{ padding: '2px 0' }}>{getChartIcon(chart)}</GridItem>
<GridItem row={i+2} col={2}>{chart.shortLabel || chart.label}</GridItem> <GridItem row={i+2} col={2}>{chart.shortLabel || chart.label}</GridItem>
{flow.map((row, j) => ( {flow?.map((row, j) => (
<GridItem key={`${j}`} row={i+2+(j++)} col={3} style={{ paddingRight: 0, textAlign: 'end' }}> <GridItem key={`${j}`} row={i+2+(j++)} col={3} style={{ paddingRight: 0, textAlign: 'end' }}>
{chart.xAxis.format?.(row) ?? value(row)} {chart.xAxis.format?.(row) ?? value(row)}
</GridItem> </GridItem>

View File

@ -13,13 +13,12 @@ import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
import { formatDate, hasPermission, isRawDate, withPermissions } from '@utils' import { formatDate, hasPermission, isRawDate, withPermissions } from '@utils'
import { Subscribe } from '@services/signalr' import { Subscribe } from '@services/signalr'
import { import {
DrillFlowChartService,
OperationStatService, OperationStatService,
TelemetryDataSaubService, TelemetryDataSaubService,
TelemetryDataSpinService TelemetryDataSpinService
} from '@api' } from '@api'
import { calcFlowData, makeChartGroups, yAxis } from './dataset' import { makeChartGroups, yAxis } from './dataset'
import { ADDITIVE_PAGES, cutData, DATA_COUNT, getLoadingInterval, makeDateTimeDisabled } from './archive_methods' import { ADDITIVE_PAGES, cutData, DATA_COUNT, getLoadingInterval, makeDateTimeDisabled } from './archive_methods'
import ActiveMessagesOnline from './ActiveMessagesOnline' import ActiveMessagesOnline from './ActiveMessagesOnline'
import TelemetrySummary from './TelemetrySummary' import TelemetrySummary from './TelemetrySummary'
@ -64,12 +63,12 @@ export const normalizeData = (data) => data?.map(item => ({
const dateSorter = makeDateSorter('date') const dateSorter = makeDateSorter('date')
const defaultDate = () => new Date(Date.now() - defaultPeriod * 1000) const defaultDate = () => new Date(Date.now() - defaultPeriod * 1000)
const makeSubjectSubsription = (subject$, handler) => { const makeSubjectSubscription = (subject$, handler) => {
const subscribtion = subject$.pipe( const subscription = subject$.pipe(
buffer(subject$.pipe(throttleTime(700))) buffer(subject$.pipe(throttleTime(700)))
).subscribe((data) => handler(data.flat().filter(Boolean))) ).subscribe((data) => handler(data.flat().filter(Boolean)))
return () => subscribtion.unsubscribe() return () => subscription.unsubscribe()
} }
const getRowDate = (row) => row && isRawDate(row.date) ? new Date(row.date) : null const getRowDate = (row) => row && isRawDate(row.date) ? new Date(row.date) : null
@ -81,7 +80,6 @@ const TelemetryView = memo(() => {
const [dataSaub, setDataSaub] = useState([]) const [dataSaub, setDataSaub] = useState([])
const [dataSpin, setDataSpin] = useState([]) const [dataSpin, setDataSpin] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [flowChartData, setFlowChartData] = useState([])
const [rop, setRop] = useState(null) const [rop, setRop] = useState(null)
const [chartMethods, setChartMethods] = useState() const [chartMethods, setChartMethods] = useState()
@ -147,7 +145,6 @@ const TelemetryView = memo(() => {
const spinSubject$ = useMemo(() => new BehaviorSubject(), []) const spinSubject$ = useMemo(() => new BehaviorSubject(), [])
const filteredData = useMemo(() => cutData(dataSaub, domain.min, domain.max), [dataSaub, domain]) const filteredData = useMemo(() => cutData(dataSaub, domain.min, domain.max), [dataSaub, domain])
const flowData = useMemo(() => calcFlowData(dataSaub, flowChartData), [dataSaub, flowChartData])
const chartGroups = useMemo(() => makeChartGroups(), []) const chartGroups = useMemo(() => makeChartGroups(), [])
useEffect(() => { useEffect(() => {
@ -170,8 +167,8 @@ const TelemetryView = memo(() => {
setSearchParams(params) setSearchParams(params)
}, [archiveMode, endDate, chartInterval]) }, [archiveMode, endDate, chartInterval])
useEffect(() => makeSubjectSubsription(saubSubject$, handleDataSaub), [saubSubject$, handleDataSaub]) useEffect(() => makeSubjectSubscription(saubSubject$, handleDataSaub), [saubSubject$, handleDataSaub])
useEffect(() => makeSubjectSubsription(spinSubject$, handleDataSpin), [spinSubject$, handleDataSpin]) useEffect(() => makeSubjectSubscription(spinSubject$, handleDataSpin), [spinSubject$, handleDataSpin])
useEffect(() => { useEffect(() => {
if (archiveMode) return if (archiveMode) return
@ -233,8 +230,6 @@ const TelemetryView = memo(() => {
useEffect(() => { useEffect(() => {
invokeWebApiWrapperAsync( invokeWebApiWrapperAsync(
async () => { async () => {
const flowChart = await DrillFlowChartService.getByIdWell(well.id)
setFlowChartData(flowChart ?? [])
const rop = await OperationStatService.getClusterRopStatByIdWell(well.id) const rop = await OperationStatService.getClusterRopStatByIdWell(well.id)
setRop(rop) setRop(rop)
let dates = await TelemetryDataSaubService.getDataDatesRange(well.id) let dates = await TelemetryDataSaubService.getDataDatesRange(well.id)
@ -312,7 +307,6 @@ const TelemetryView = memo(() => {
{...chartProps} {...chartProps}
yDomain={domain} yDomain={domain}
data={filteredData} data={filteredData}
flowData={flowData}
methods={setChartMethods} methods={setChartMethods}
datasetGroups={chartGroups} datasetGroups={chartGroups}
onWheel={onWheel} onWheel={onWheel}

View File

@ -40,7 +40,6 @@ export const menuItems = [
makeItem('План', 'plan', [], <TableOutlined />), makeItem('План', 'plan', [], <TableOutlined />),
makeItem('Факт', 'fact', [], <TableOutlined />), makeItem('Факт', 'fact', [], <TableOutlined />),
makeItem('РТК', 'drillProcessFlow', [], <BarChartOutlined />), makeItem('РТК', 'drillProcessFlow', [], <BarChartOutlined />),
makeItem('Режимы', 'params', [], <ControlOutlined />),
]), ]),
makeItem('Документы', 'document', [], <FolderOutlined />, [ makeItem('Документы', 'document', [], <FolderOutlined />, [
makeItem('Растворный сервис', 'fluidService', [], <FolderOutlined />), makeItem('Растворный сервис', 'fluidService', [], <FolderOutlined />),

View File

@ -1,44 +1,83 @@
import { useState, useEffect, memo, useMemo, useCallback } from 'react' import { useState, useEffect, memo, useMemo, useCallback } from 'react'
import { useWell } from '@asb/context' import { useWell } from '@asb/context'
import {
EditableTable,
makeGroupColumn,
makeNumericColumn,
makeNumericColumnPlanFact,
makeNumericRender,
makeNumericSorter,
makeSelectColumn,
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { EditableTable, makeNumericMinMax, makeNumericStartEnd } from '@components/Table' import { ProcessMapService, WellOperationService } from '@api'
import { DrillFlowChartService } from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
const columns = [ const numericRender = makeNumericRender(2)
makeNumericStartEnd('Глубина, м', 'depth'),
makeNumericMinMax('Нагрузка, т', 'axialLoad'), export const getColumns = async (idWell) => {
makeNumericMinMax('Перепад давления, атм', 'pressure'), let sectionTypes = await WellOperationService.getSectionTypes(idWell)
makeNumericMinMax('Момент на ВСП, кН·м', 'rotorTorque'), sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
makeNumericMinMax('Обороты на ВСП, об/мин', 'rotorSpeed'), label: value,
makeNumericMinMax('Расход, л/с', 'flow') value: id,
] }))
return [
makeSelectColumn('Конструкция секции','idWellSectionType', sectionTypes, null, {
width: 160,
sorter: makeNumericSorter('idWellSectionType'),
}),
makeGroupColumn('Интервал бурения, м', [
makeNumericColumn('От', 'depthStart', numericRender),
makeNumericColumn('До', 'depthEnd', numericRender),
]),
makeNumericColumnPlanFact('Перепад давления, атм', 'pressure', numericRender),
makeNumericColumnPlanFact('Нагрузка, т', 'axialLoad', numericRender),
makeNumericColumnPlanFact('Момент на ВСП, кН·м', 'topDriveTorque', numericRender),
makeNumericColumnPlanFact('Обороты на ВСП, об/мин', 'topDriveSpeed', numericRender),
makeNumericColumnPlanFact('Расход, л/с', 'flow', numericRender),
makeNumericColumn('Плановая механическая скорость, м/ч', 'ropPlan', numericRender),
]
}
export const DrillProcessFlow = memo(() => { export const DrillProcessFlow = memo(() => {
const [flows, setFlows] = useState([]) const [flows, setFlows] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([])
const [well] = useWell() const [well] = useWell()
const updateFlows = useCallback(() => invokeWebApiWrapperAsync( const updateFlows = useCallback(() => invokeWebApiWrapperAsync(
async () => { async () => {
const flows = await DrillFlowChartService.getByIdWell(well.id) const flows = await ProcessMapService.getByIdWell(well.id)
setFlows(arrayOrDefault(flows)) setFlows(arrayOrDefault(flows))
}, },
setShowLoader, setShowLoader,
`Не удалось загрузить режимно-технологическую карту`, `Не удалось загрузить режимно-технологическую карту`,
{ actionName: 'Получение режимно-технологической карты', well } { actionName: 'Получение режимно-технологической карты', well },
), [well]) ), [well])
useEffect(() => {
invokeWebApiWrapperAsync(
async () => {
const columns = await getColumns(well.id)
setColumns(columns)
},
setShowLoader,
`Не удалось загрузить список конструкций секций`,
{ actionName: 'Получение списка конструкций секций', well },
)
}, [well])
useEffect(() => { useEffect(() => {
updateFlows() updateFlows()
}, [well]) }, [well])
const tableHandlers = useMemo(() => { const tableHandlers = useMemo(() => {
const handlerProps = { const handlerProps = {
service: DrillFlowChartService, service: ProcessMapService,
setLoader: setShowLoader, setLoader: setShowLoader,
onComplete: updateFlows, onComplete: updateFlows,
permission: 'DrillFlowChart.edit', permission: 'DrillFlowChart.edit',

View File

@ -31,26 +31,20 @@ const dayWithoutNptRender = (_, row) => dayRender((row.day ?? 0) - (row.nptHours
const generateColumns = (showNpt = false, categories = [], sectionTypes = []) => [ const generateColumns = (showNpt = false, categories = [], sectionTypes = []) => [
makeSelectColumn('Конструкция секции', 'idWellSectionType', sectionTypes, undefined, { makeSelectColumn('Конструкция секции', 'idWellSectionType', sectionTypes, undefined, {
sorter: makeNumericSorter('idWellSectionType'), sorter: makeNumericSorter('idWellSectionType'),
editable: true,
width: 160, width: 160,
}), }),
makeSelectColumn('Операция', 'idCategory', categories, undefined, { makeSelectColumn('Операция', 'idCategory', categories, undefined, {
sorter: makeNumericSorter('idCategory'), sorter: makeNumericSorter('idCategory'),
editable: true,
width: 200, width: 200,
}), }),
makeTextColumn('Доп. инфо', 'categoryInfo', null, null, null, { editable: true, width: 300, input: <TextArea/> }), makeTextColumn('Доп. инфо', 'categoryInfo', null, null, null, { width: 300, input: <TextArea/> }),
makeColumn('Глубина забоя на начало, м', 'depthStart', makeNumericColumnOptions(2, 'depthStart')), makeNumericColumn('Глубина забоя на начало, м', 'depthStart'),
makeColumn('Глубина забоя при завершении, м', 'depthEnd', makeNumericColumnOptions(2, 'depthEnd')), makeNumericColumn('Глубина забоя при завершении, м', 'depthEnd'),
makeDateColumn('Время начала', 'dateStart', undefined, undefined, { makeDateColumn('Время начала', 'dateStart', undefined, undefined, { width: 170, initialValue: moment().format() }),
editable: true, makeNumericColumn('День', 'day', dayRender, undefined, 80),
width: 170, showNpt && makeNumericColumn('День без НПВ', 'dayWithoutNpt', dayWithoutNptRender, undefined, 80),
initialValue: moment().format(), makeNumericColumn('Часы', 'durationHours', undefined, undefined, 70),
}), makeTextColumn('Комментарий', 'comment', null, null, null, { input: <TextArea /> }),
makeNumericColumn('День', 'day', null, null, dayRender, 80),
showNpt && makeNumericColumn('День без НПВ', 'dayWithoutNpt', null, null, dayWithoutNptRender, 80),
makeColumn('Часы', 'durationHours', { ...makeNumericColumnOptions(2, 'durationHours'), width: 70 }),
makeTextColumn('Комментарий', 'comment', null, null, null, { editable: true, input: <TextArea/> }),
].filter(Boolean) ].filter(Boolean)
export const WellOperationsEditor = memo(({ idType, showNpt, ...other }) => { export const WellOperationsEditor = memo(({ idType, showNpt, ...other }) => {

View File

@ -4,15 +4,15 @@ import { FilterOutlined } from '@ant-design/icons'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { makeDateColumn, makeNumericColumn, makeNumericRender, makeTextColumn, Table } from '@components/Table' import { makeDateColumn, makeNumericColumn, makeTextColumn, Table } from '@components/Table'
import '@styles/pages/tvd.less' import '@styles/pages/tvd.less'
export const columns = [ export const columns = [
makeTextColumn('Конструкция секции', 'wellSectionTypeName', null, null, null, { width: 140 }), makeTextColumn('Конструкция секции', 'wellSectionTypeName', null, null, null, { width: 140 }),
makeNumericColumn('Глубина', 'depth', null, null, null, 80), makeNumericColumn('Глубина', 'depth', undefined, undefined, 80),
makeDateColumn('Дата начала', 'date', false, undefined, { width: 90 }), makeDateColumn('Дата начала', 'date', false, undefined, { width: 90 }),
makeNumericColumn('Длительность (ч)', 'durationHours', null, null, makeNumericRender(2), 120), makeNumericColumn('Длительность (ч)', 'durationHours', undefined, undefined, 120),
makeTextColumn('Доп. инфо', 'categoryInfo', null, null, null), makeTextColumn('Доп. инфо', 'categoryInfo', null, null, null),
makeTextColumn('Комментарий', 'comment'), makeTextColumn('Комментарий', 'comment'),
] ]

View File

@ -18,9 +18,9 @@ const tableColumns = [
<div style={{ backgroundColor: d, padding: '5px 0' }} /> <div style={{ backgroundColor: d, padding: '5px 0' }} />
) }), ) }),
makeTextColumn('Название', 'category', undefined, undefined, undefined, { width: 300 }), makeTextColumn('Название', 'category', undefined, undefined, undefined, { width: 300 }),
makeNumericColumn('Время, мин', 'minutesTotal', undefined, undefined, undefined, 100), makeNumericColumn('Время, мин', 'minutesTotal', undefined, undefined, 100),
makeNumericColumn('Кол-во', 'count', undefined, undefined, (d) => d ? d.toString() : '---', 100), makeNumericColumn('Кол-во', 'count', (d) => d ? d.toString() : '---', undefined, 100),
makeNumericColumn('Процент, %', 'percent', undefined, undefined, (d) => d ? d.toFixed(2) : '---', 100) makeNumericColumn('Процент, %', 'percent', (d) => d ? d.toFixed(2) : '---', undefined, 100),
] ]
export const TLPie = memo(({ well }) => { export const TLPie = memo(({ well }) => {

View File

@ -1,98 +0,0 @@
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
import { useWell } from '@asb/context'
import {
EditableTable,
makeSelectColumn,
makeNumericSorter,
makeNumericAvgRange,
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService, WellOperationService } from '@api'
import { arrayOrDefault } from '@utils'
export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
label: value,
value: id,
}))
return [
makeSelectColumn('Конструкция секции','idWellSectionType', sectionTypes, null, {
editable: true,
width: 160,
sorter: makeNumericSorter('idWellSectionType'),
}),
makeNumericAvgRange('Нагрузка, т', 'axialLoad', 1),
makeNumericAvgRange('Перепад давления, атм', 'pressure', 1),
makeNumericAvgRange('Момент на ВСП, кН·м', 'rotorTorque', 1),
makeNumericAvgRange('Обороты на ВСП, об/мин', 'rotorSpeed', 1),
makeNumericAvgRange('Расход, л/с', 'flow', 1),
]
}
export const WellDrillParams = memo(() => {
const [params, setParams] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([])
const [well] = useWell()
const updateParams = useCallback(async () => await invokeWebApiWrapperAsync(
async () => {
const params = arrayOrDefault(await DrillParamsService.getAll(well.id))
// Typescript против использования числа в качестве типа значения select
params.forEach((param) => param.idWellSectionType = `${param.idWellSectionType}`)
setParams(params)
},
setShowLoader,
`Не удалось загрузить список режимов бурения`,
{ actionName: 'Получение списка режимов бурения скважины', well }
), [well])
useEffect(() => {
(async () => {
setColumns(await getColumns(well.id))
await updateParams()
})()
}, [well.id, updateParams])
const recordParser = useCallback((record) => ({ ...record, idWell: well.id }), [well.id])
const tableHandlers = useMemo(() => {
const handlerProps = {
service: DrillParamsService,
setLoader: setShowLoader,
onComplete: updateParams,
permission: 'DrillParams.edit',
idWell: well.id,
idRecord: true,
}
return {
add: { ...handlerProps, action: 'insert', actionName: 'Добавление режима бурения', recordParser },
edit: { ...handlerProps, action: 'update', actionName: 'Редактирование режима бурения', recordParser },
delete: { ...handlerProps, action: 'delete', actionName: 'Удаление режима бурения', permission: 'DrillParams.delete' },
}
}, [well.id, updateParams, recordParser])
return (
<LoaderPortal show={showLoader}>
<EditableTable
bordered
size={'small'}
columns={columns}
dataSource={params}
tableName={'well_drill_params'}
onRowAdd={tableHandlers.add}
onRowEdit={tableHandlers.edit}
onRowDelete={tableHandlers.delete}
pagination={false}
/>
</LoaderPortal>
)
})
export default WellDrillParams

View File

@ -3,22 +3,21 @@ import { useState, useEffect, memo } from 'react'
import { useWell } from '@asb/context' import { useWell } from '@asb/context'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table' import { Table, makeColumn, makeNumericRender, makeNumericColumnPlanFactOld } from '@components/Table'
import { calcDuration } from '@utils' import { calcDuration } from '@utils'
import { OperationStatService } from '@api' import { OperationStatService } from '@api'
const numericRender = makeNumericRender(2) const numericRender = makeNumericRender(2)
const columns = [ const columns = [
makeColumn('Тип секции', 'sectionType'), makeColumn('Тип секции', 'sectionType'),
makeColumnsPlanFact('Глубина, м' ,'wellDepth', { render: numericRender }), makeNumericColumnPlanFactOld('Глубина, м', 'wellDepth', numericRender),
makeColumnsPlanFact('Часы' ,'duration', { render: numericRender }), makeNumericColumnPlanFactOld('Часы', 'duration', numericRender),
makeColumnsPlanFact('МСП, м/ч' ,'rop', { render: numericRender }), makeNumericColumnPlanFactOld('МСП, м/ч', 'rop', numericRender),
makeColumnsPlanFact('Рейсовая скорость, м/ч','routeSpeed', { render: numericRender }), makeNumericColumnPlanFactOld('Рейсовая скорость, м/ч', 'routeSpeed', numericRender),
makeColumnsPlanFact('Подъем КНБК, м/ч' ,'bhaUpSpeed', { render: numericRender }), makeNumericColumnPlanFactOld('Подъем КНБК, м/ч', 'bhaUpSpeed', numericRender),
makeColumnsPlanFact('Спуск КНБК, м/ч' ,'bhaDownSpeed', { render: numericRender }), makeNumericColumnPlanFactOld('Спуск КНБК, м/ч', 'bhaDownSpeed', numericRender),
makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: numericRender }), makeNumericColumnPlanFactOld('Спуск ОК, м/ч', 'casingDownSpeed', numericRender),
] ]
export const WellSectionsStat = memo(() => { export const WellSectionsStat = memo(() => {

View File

@ -1,5 +1,5 @@
import { lazy, memo, useCallback, useEffect, useMemo, useState } from 'react' import { lazy, memo, useCallback, useEffect, useMemo, useState } from 'react'
import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom' import { Navigate, Route, Routes, useParams } from 'react-router-dom'
import { WellContext, RootPathContext, useRootPath, useLayoutProps, TopRightBlockContext } from '@asb/context' import { WellContext, RootPathContext, useRootPath, useLayoutProps, TopRightBlockContext } from '@asb/context'
import { FastRunMenu } from '@components/FastRunMenu' import { FastRunMenu } from '@components/FastRunMenu'
@ -22,7 +22,6 @@ const WellOperations = lazy(() => import('./WellOperations'))
const DrillingProgram = lazy(() => import('./DrillingProgram')) const DrillingProgram = lazy(() => import('./DrillingProgram'))
const Tvd = lazy(() => import('./WellOperations/Tvd')) const Tvd = lazy(() => import('./WellOperations/Tvd'))
const WellDrillParams = lazy(() => import('./WellOperations/WellDrillParams'))
const DrillProcessFlow = lazy(() => import('./WellOperations/DrillProcessFlow')) const DrillProcessFlow = lazy(() => import('./WellOperations/DrillProcessFlow'))
const WellSectionsStat = lazy(() => import('./WellOperations/WellSectionsStat')) const WellSectionsStat = lazy(() => import('./WellOperations/WellSectionsStat'))
const WellOperationsEditorFact = lazy(() => import('./WellOperations/OperationEditor/Fact')) const WellOperationsEditorFact = lazy(() => import('./WellOperations/OperationEditor/Fact'))
@ -45,8 +44,6 @@ const breadcrumb = makeMenuBreadcrumbItemsRender(menuItems, /^\/well\/[^\/#?]+\/
const Well = memo(() => { const Well = memo(() => {
const { idWell } = useParams() const { idWell } = useParams()
const location = useLocation()
const [well, setWell] = useState({ id: idWell }) const [well, setWell] = useState({ id: idWell })
const [topRightBlock, setTopRightBlock] = useState() const [topRightBlock, setTopRightBlock] = useState()
@ -130,7 +127,6 @@ const Well = memo(() => {
<Route path={'plan'} element={<WellOperationsEditorPlan />} /> <Route path={'plan'} element={<WellOperationsEditorPlan />} />
<Route path={'fact'} element={<WellOperationsEditorFact />} /> <Route path={'fact'} element={<WellOperationsEditorFact />} />
<Route path={'drillProcessFlow'} element={<DrillProcessFlow />} /> <Route path={'drillProcessFlow'} element={<DrillProcessFlow />} />
<Route path={'params'} element={<WellDrillParams />} />
</Route> </Route>
<Route path={'document/*'} element={<Documents />} /> <Route path={'document/*'} element={<Documents />} />
<Route path={'measure/*'} element={<Measure />} /> <Route path={'measure/*'} element={<Measure />} />

View File

@ -39,6 +39,8 @@
} }
.loader-content{ .loader-content{
width: 100%;
height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
grid-column-start: 1; grid-column-start: 1;