forked from ddrilling/asb_cloud_front
* WellTreeSelect перенесён в Drawer
* Добавлена сортировка скважин по активности * Подправлена высота подсказки на странице операций
This commit is contained in:
parent
de729dc2ff
commit
702757f9a4
@ -8,12 +8,13 @@ import { wrapPrivateComponent } from '@utils'
|
|||||||
export type LayoutPortalProps = LayoutProps & {
|
export type LayoutPortalProps = LayoutProps & {
|
||||||
title?: ReactNode
|
title?: ReactNode
|
||||||
noSheet?: boolean
|
noSheet?: boolean
|
||||||
|
showSelector?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const _LayoutPortal = memo<LayoutPortalProps>(({ title, noSheet, ...props }) => (
|
const _LayoutPortal = memo<LayoutPortalProps>(({ title, noSheet, showSelector, ...props }) => (
|
||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
<PageHeader title={title}>
|
<PageHeader title={title}>
|
||||||
<WellTreeSelector />
|
<WellTreeSelector show={showSelector} />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<Layout>
|
<Layout>
|
||||||
{noSheet ? props.children : (
|
{noSheet ? props.children : (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
|
||||||
import { Layout } from 'antd'
|
import { Layout } from 'antd'
|
||||||
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
import { BasicProps } from 'antd/lib/layout/layout'
|
import { BasicProps } from 'antd/lib/layout/layout'
|
||||||
|
|
||||||
import { headerHeight } from '@utils'
|
import { headerHeight } from '@utils'
|
||||||
@ -23,8 +23,8 @@ export const PageHeader: React.FC<PageHeaderProps> = memo(({ title = 'Монит
|
|||||||
<Link to={'/'} style={{ height: headerHeight }}>
|
<Link to={'/'} style={{ height: headerHeight }}>
|
||||||
<Logo />
|
<Logo />
|
||||||
</Link>
|
</Link>
|
||||||
{children}
|
|
||||||
<h1 className={'title'}>{title}</h1>
|
<h1 className={'title'}>{title}</h1>
|
||||||
|
{children}
|
||||||
<UserMenu isAdmin={isAdmin} />
|
<UserMenu isAdmin={isAdmin} />
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import { TreeSelect } from 'antd'
|
import { Button, Drawer, Tree, TreeProps, Typography } from 'antd'
|
||||||
import { LabelInValueType } from 'rc-select/lib/Select'
|
|
||||||
import { RawValueType } from 'rc-tree-select/lib/TreeSelect'
|
|
||||||
import { DefaultValueType } from 'rc-tree-select/lib/interface'
|
import { DefaultValueType } from 'rc-tree-select/lib/interface'
|
||||||
import { useState, useEffect, ReactNode, useCallback, memo } from 'react'
|
import { useState, useEffect, ReactNode, useCallback, memo, Key } from 'react'
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
import { useNavigate, useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
import { isRawDate } from '@utils'
|
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
|
||||||
import { WellIcon, WellIconState } from '@components/icons'
|
import { WellIcon, WellIconState } from '@components/icons'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { DepositService, DepositDto } from '@api'
|
import { DepositService, DepositDto, WellDto } from '@api'
|
||||||
|
import { isRawDate } from '@utils'
|
||||||
|
|
||||||
import { ReactComponent as DepositIcon } from '@images/DepositIcon.svg'
|
import { ReactComponent as DepositIcon } from '@images/DepositIcon.svg'
|
||||||
import { ReactComponent as ClusterIcon } from '@images/ClusterIcon.svg'
|
import { ReactComponent as ClusterIcon } from '@images/ClusterIcon.svg'
|
||||||
@ -28,10 +25,15 @@ export type TreeNodeData = {
|
|||||||
children?: TreeNodeData[]
|
children?: TreeNodeData[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getKeyByUrl = (url?: string): [Key | null, string | null] => {
|
||||||
|
const result = url?.match(/^\/([^\/]+)\/([^\/?]+)/) // pattern "/:type/:id"
|
||||||
|
if (!result) return [null, null]
|
||||||
|
return [result[0], result[1]]
|
||||||
|
}
|
||||||
|
|
||||||
const getLabel = (wellsTree: TreeNodeData[], value?: string): string | undefined => {
|
const getLabel = (wellsTree: TreeNodeData[], value?: string): string | undefined => {
|
||||||
const result = value?.match(/^\/([^\/]+)\/([^\/?]+)/) // pattern "/:type/:id"
|
const [url, type] = getKeyByUrl(value)
|
||||||
if (wellsTree.length <= 0 || !result) return
|
if (!url) return
|
||||||
const [url, type] = result
|
|
||||||
let deposit: TreeNodeData | undefined
|
let deposit: TreeNodeData | undefined
|
||||||
let cluster: TreeNodeData | undefined
|
let cluster: TreeNodeData | undefined
|
||||||
let well: TreeNodeData | undefined
|
let well: TreeNodeData | undefined
|
||||||
@ -64,13 +66,47 @@ const getLabel = (wellsTree: TreeNodeData[], value?: string): string | undefined
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WellTreeSelector = memo(({ ...other }) => {
|
const getWellSortScore = (well: WellDto) => {
|
||||||
|
let out = [1, 2, 0][well.idState ?? 2]
|
||||||
|
const timeout = Date.now() - +new Date(well.lastTelemetryDate || 0)
|
||||||
|
if (timeout < 600_000) out += 600_000 - timeout
|
||||||
|
console.log(well, out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortWellsByActive = (a: WellDto, b: WellDto): number => {
|
||||||
|
const score = getWellSortScore(b) - getWellSortScore(a)
|
||||||
|
if (score !== 0) return score
|
||||||
|
return (a.caption || '')?.localeCompare(b.caption || '')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WellTreeSelector = memo(({ show, ...other }: TreeProps<TreeNodeData> & { show?: boolean }) => {
|
||||||
const [wellsTree, setWellsTree] = useState<TreeNodeData[]>([])
|
const [wellsTree, setWellsTree] = useState<TreeNodeData[]>([])
|
||||||
const [showLoader, setShowLoader] = useState<boolean>(false)
|
const [showLoader, setShowLoader] = useState<boolean>(false)
|
||||||
|
const [visible, setVisible] = useState<boolean>(false)
|
||||||
|
const [expanded, setExpanded] = useState<Key[]>([])
|
||||||
|
const [selected, setSelected] = useState<Key[]>([])
|
||||||
const [value, setValue] = useState<string>()
|
const [value, setValue] = useState<string>()
|
||||||
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setVisible((prev) => show ?? prev)
|
||||||
|
setExpanded((prev) => {
|
||||||
|
if (typeof show === 'undefined') return prev
|
||||||
|
if (!show) return []
|
||||||
|
const out: Key[] = []
|
||||||
|
wellsTree.forEach((deposit) => {
|
||||||
|
if (deposit.key) out.push(deposit.key)
|
||||||
|
deposit.children?.forEach((cluster) => {
|
||||||
|
if (cluster.key) out.push(cluster.key)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return out
|
||||||
|
})
|
||||||
|
}, [wellsTree, show])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
invokeWebApiWrapperAsync(
|
invokeWebApiWrapperAsync(
|
||||||
async () => {
|
async () => {
|
||||||
@ -80,23 +116,28 @@ export const WellTreeSelector = memo(({ ...other }) => {
|
|||||||
key: `/deposit/${deposit.id}`,
|
key: `/deposit/${deposit.id}`,
|
||||||
value: `/deposit/${deposit.id}`,
|
value: `/deposit/${deposit.id}`,
|
||||||
icon: <DepositIcon width={24} height={24}/>,
|
icon: <DepositIcon width={24} height={24}/>,
|
||||||
children: deposit.clusters?.map(cluster => ({
|
children: deposit.clusters?.map(cluster => {
|
||||||
title: cluster.caption,
|
const wells = cluster.wells ? cluster.wells.slice() : []
|
||||||
key: `/cluster/${cluster.id}`,
|
wells.sort(sortWellsByActive)
|
||||||
value: `/cluster/${cluster.id}`,
|
|
||||||
icon: <ClusterIcon width={24} height={24}/>,
|
return {
|
||||||
children: cluster.wells?.map(well => ({
|
title: cluster.caption,
|
||||||
title: well.caption,
|
key: `/cluster/${cluster.id}`,
|
||||||
key: `/well/${well.id}`,
|
value: `/cluster/${cluster.id}`,
|
||||||
value: `/well/${well.id}`,
|
icon: <ClusterIcon width={24} height={24}/>,
|
||||||
icon: <WellIcon
|
children: wells.map(well => ({
|
||||||
width={24}
|
title: well.caption,
|
||||||
height={24}
|
key: `/well/${well.id}`,
|
||||||
state={getWellState(well.idState)}
|
value: `/well/${well.id}`,
|
||||||
online={checkIsWellOnline(well.lastTelemetryDate)}
|
icon: <WellIcon
|
||||||
/>
|
width={24}
|
||||||
})),
|
height={24}
|
||||||
})),
|
state={getWellState(well.idState)}
|
||||||
|
online={checkIsWellOnline(well.lastTelemetryDate)}
|
||||||
|
/>
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}),
|
||||||
}))
|
}))
|
||||||
setWellsTree(wellsTree)
|
setWellsTree(wellsTree)
|
||||||
},
|
},
|
||||||
@ -106,32 +147,34 @@ export const WellTreeSelector = memo(({ ...other }) => {
|
|||||||
)
|
)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => setValue(getLabel(wellsTree, location.pathname)), [wellsTree, location])
|
const onChange = useCallback((value?: string): void => {
|
||||||
|
const key = getKeyByUrl(value)[0]
|
||||||
|
setSelected(key ? [key] : [])
|
||||||
|
setValue(getLabel(wellsTree, value))
|
||||||
|
}, [wellsTree])
|
||||||
|
|
||||||
const onChange = useCallback((value?: string): void => setValue(getLabel(wellsTree, value)), [wellsTree])
|
const onSelect = useCallback((value: Key[]): void => {
|
||||||
|
navigate(String(value), { state: { from: location.pathname }})
|
||||||
const onSelect = useCallback((value: RawValueType | LabelInValueType): void => {
|
|
||||||
if (['number', 'string'].includes(typeof value))
|
|
||||||
navigate(String(value), { state: { from: location.pathname }})
|
|
||||||
}, [navigate, location])
|
}, [navigate, location])
|
||||||
|
|
||||||
|
useEffect(() => onChange(location.pathname), [onChange, location])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoaderPortal show={showLoader}>
|
<>
|
||||||
<TreeSelect
|
<Button loading={showLoader} onClick={() => setVisible(true)}>{value ?? 'Выберите месторождение'}</Button>
|
||||||
treeIcon
|
<Drawer visible={visible} mask={false} onClose={() => setVisible(false)}>
|
||||||
className={'header-tree-select'}
|
<Typography.Title level={3}>Список скважин</Typography.Title>
|
||||||
bordered={false}
|
<Tree
|
||||||
dropdownMatchSelectWidth={false}
|
{...other}
|
||||||
placeholder={'Выберите месторождение'}
|
showIcon
|
||||||
treeData={wellsTree}
|
selectedKeys={selected}
|
||||||
treeDefaultExpandAll
|
treeData={wellsTree}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onChange={onChange}
|
onExpand={setExpanded}
|
||||||
value={value}
|
expandedKeys={expanded}
|
||||||
style={{ width: '350px' }}
|
/>
|
||||||
{...other}
|
</Drawer>
|
||||||
/>
|
</>
|
||||||
</LoaderPortal>
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ const Deposit = memo(() => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutPortal noSheet title={'Месторождение'}>
|
<LayoutPortal noSheet showSelector title={'Месторождение'}>
|
||||||
<LoaderPortal show={showLoader}>
|
<LoaderPortal show={showLoader}>
|
||||||
<div className={'h-100vh'}>
|
<div className={'h-100vh'}>
|
||||||
<Map {...viewParams}>
|
<Map {...viewParams}>
|
||||||
|
@ -94,7 +94,7 @@ export const OperationsChart = memo(({ data, yDomain, height, category, onDomain
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
type: 'nearest',
|
type: 'nearest',
|
||||||
height: 200,
|
height: 225,
|
||||||
limit: 4,
|
limit: 4,
|
||||||
render: makeTooltipRender(category),
|
render: makeTooltipRender(category),
|
||||||
},
|
},
|
||||||
|
@ -74,7 +74,7 @@ html {
|
|||||||
.header .title{
|
.header .title{
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding-left: 100px;
|
padding-left: 450px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header button{
|
.header button{
|
||||||
|
Loading…
Reference in New Issue
Block a user