Добавлена базовая версия страницы

This commit is contained in:
Александр Сироткин 2022-12-06 04:44:03 +05:00
parent 043f73fde3
commit 4b20a44d88
4 changed files with 134 additions and 12 deletions

View File

@ -1,4 +1,4 @@
import { memo } from 'react'
import { DetailedHTMLProps, HTMLAttributes, memo } from 'react'
import { Tooltip, TooltipProps } from 'antd'
import { Grid, GridItem } from '@components/Grid'
@ -15,9 +15,11 @@ const wellState: Record<number, { enum: WellIconState, label: string }> = {
export type WellViewProps = TooltipProps & {
well?: WellDto
iconProps?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
labelProps?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
}
export const WellView = memo<WellViewProps>(({ well, ...other }) => well ? (
export const WellView = memo<WellViewProps>(({ well, iconProps, labelProps, ...other }) => well ? (
<Tooltip {...other} title={(
<Grid style={{ columnGap: '8px' }}>
<GridItem row={1} col={1}>Название:</GridItem>
@ -47,10 +49,12 @@ export const WellView = memo<WellViewProps>(({ well, ...other }) => well ? (
<GridItem row={8} col={2}>{well.id ?? '---'}</GridItem>
</Grid>
)}>
<span role={'img'} style={{ marginRight: 8, lineHeight: 0, verticalAlign: '-0.25em' }}>
<span role={'img'} style={{ marginRight: 8, lineHeight: 0, verticalAlign: '-0.25em' }} {...iconProps}>
<WellIcon state={wellState[well.idState || 0].enum} width={'1em'} height={'1em'} />
</span>
{well.caption}
<span {...labelProps}>
{well.deposit} / {well.cluster} / {well.caption}
</span>
</Tooltip>
) : (
<Tooltip title={'нет скважины'}>-</Tooltip>

View File

@ -1,5 +1,6 @@
import { memo } from 'react'
import {
FundOutlined,
HeatMapOutlined,
} from '@ant-design/icons'
@ -7,6 +8,7 @@ import { makeItem, PrivateMenu } from '@components/PrivateMenu'
export const menuItems = [
makeItem('Карта', 'map', [], <HeatMapOutlined />),
makeItem('Наработка АКБ', 'statistics', [], <FundOutlined />),
]
export const DepositNavigationMenu = memo((props) => (

View File

@ -0,0 +1,104 @@
import { memo, useCallback, useEffect, useState } from 'react'
import { QuestionCircleOutlined } from '@ant-design/icons'
import { Card } from 'antd'
import { WellView } from '@components/views'
import LoaderPortal from '@components/LoaderPortal'
import { DateRangeWrapper, makeNumericColumn, makeNumericRender, makeTextColumn, Table } from '@components/Table'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { arrayOrDefault, withPermissions } from '@utils'
import { SubsystemOperationTimeService } from '@api'
const numericRender = makeNumericRender(2)
const columns = [
makeTextColumn('Подсистема', 'subsystemName', undefined, undefined, (value, row) => value || row.key),
makeNumericColumn('Проходка, м', 'sumDepthInterval'),
makeNumericColumn('Время работы, ч', 'usedTimeHours'),
makeNumericColumn('Кол-во запусков', 'operationCount'),
makeNumericColumn('Коэф. использования, %', 'kUsage', undefined, undefined, (value) => numericRender(value * 100)),
]
const getSubsystemIcon = (subsystem) => {
return <QuestionCircleOutlined />
}
const GeneralSubsystemStatistics = memo(() => {
const [data, setData] = useState([])
const [isLoading, setIsLoading] = useState(false)
const [selected, setSelected] = useState(null)
const [dates, setDates] = useState([null, null])
const onRow = useCallback((record) => {
}, [selected])
useEffect(() => {
invokeWebApiWrapperAsync(
async () => {
const data = await SubsystemOperationTimeService.getStatByWell(dates?.[0]?.toISOString(), dates?.[1]?.toISOString())
const out = arrayOrDefault(data).map(({ well, ...subsystems }) => ({
well,
subsystems: Object.entries(subsystems).map(([key, value]) => ({ key, ...value })),
}))
setData(out)
},
setIsLoading,
'Не удалось загрузить статистику наработки подсистем',
{ actionName: 'Загрузка статистики наработки подсистем' },
)
}, [dates])
const makeOnCardClick = useCallback((row) => () => {
setSelected((prev) => (prev?.well.id === row.well.id) ? null : row)
}, [])
return (
<LoaderPortal show={isLoading} style={{ flex: 1 }}>
<div className={'general-statistics'} style={{ display: 'flex', flexDirection: 'column', alignItems: 'stretch', gap: 20, height: '100%' }}>
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
<span>Диапазон дат:</span>
<DateRangeWrapper allowClear onChange={setDates} value={dates} />
</div>
<div style={{ padding: 15, flex: 1, display: 'flex', flexWrap: 'wrap', alignItems: 'baseline', gap: 15, overflowY: 'auto' }}>
{data.map((row) => {
const cardStyle = {
boxShadow: row.well.id === selected?.well.id ? '0 0 5px 2px gray' : null,
}
return (
<Card key={row.well.id} title={<WellView well={row.well} />} onClick={makeOnCardClick(row)} style={cardStyle}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
{row.subsystems.map((ss) => (
<div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
{getSubsystemIcon(ss)}
<span key={ss.key}>{ss.subsystemName || ss.key}</span>
</div>
))}
</div>
</Card>
)
})}
</div>
{selected && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
<div style={{ display: 'flex', gap: 15, alignItems: 'center', justifyContent: 'center', fontSize: '1.25em', fontWeight: 600 }}>
<span>Детальная информация по скважине</span>
<WellView well={selected.well} />
</div>
<Table
bordered
size={'small'}
pagination={false}
dataSource={selected.subsystems}
columns={columns}
onRow={onRow}
/>
</div>
)}
</div>
</LoaderPortal>
)
})
export default withPermissions(GeneralSubsystemStatistics, [])

View File

@ -3,10 +3,15 @@ import { lazy, memo, useEffect, useMemo } from 'react'
import { DepositContext, RootPathContext, useDepositList, useLayoutProps, useRootPath } from '@asb/context'
import FastRunMenu from '@components/FastRunMenu'
import { makeMenuBreadcrumbItemsRender } from '@components/MenuBreadcrumb'
import { NoAccessComponent, withPermissions } from '@utils'
import { DepositNavigationMenu, menuItems } from './DepositNavigationMenu'
const Map = lazy(() => import('./Map'))
const DepositNavigationMenu = lazy(() => import('./DepositNavigationMenu'))
const GeneralSubsystemStatistics = lazy(() => import('./GeneralSubsystemStatistics'))
const breadcrumb = makeMenuBreadcrumbItemsRender(menuItems, /^\/deposit\/[^\/#?]+\//)
const Deposit = memo(() => {
const { '*': param } = useParams()
@ -17,9 +22,14 @@ const Deposit = memo(() => {
const root = useRootPath()
const rootPath = useMemo(() => `${root}/deposit`, [root])
const idDeposit = useMemo(() => {
const result = /^([^\/#?]+)/.exec(param)
return result && result[1] !== 'null' ? Number(result[1]) : null
const [idDeposit, isMap] = useMemo(() => {
const result = /^([^\/#?]+)(:?\/([^\/#?]+))?/.exec(param)
if (!result) return [null, false]
console.log(result)
return [
result[1] !== 'null' ? Number(result[1]) : null,
result[3] === 'map',
]
}, [param])
const deposit = useMemo(() => deposits.find((deposit) => deposit.id === idDeposit) || null, [deposits, idDeposit])
@ -33,13 +43,14 @@ const Deposit = memo(() => {
}
setLayoutProps({
sheet: false,
sider: <DepositNavigationMenu disabled={idDeposit === null} variables={{ idDeposit: idDeposit }} />,
showSelector: true,
breadcrumb: !isMap && breadcrumb,
sheet: !isMap,
sider: <DepositNavigationMenu variables={{ idDeposit: idDeposit }} />,
showSelector: isMap,
selectorProps,
title: 'Месторождение',
})
}, [setLayoutProps, idDeposit])
}, [setLayoutProps, idDeposit, isMap])
return (
<RootPathContext.Provider value={rootPath}>
@ -53,6 +64,7 @@ const Deposit = memo(() => {
<Route path={'*'} element={<NoAccessComponent />} />
<Route path={'map'} element={<Map />} />
<Route path={'statistics'} element={<GeneralSubsystemStatistics />} />
</Route>
</Routes>
</DepositContext.Provider>