forked from ddrilling/asb_cloud_front
* Получение списка месторождений вынесено в контекст для сокращения колличества запросов
* Обёртки для страниц вынесены в ленивую загрузку * Страница месторождений и селекторы скважин переписаны с использованием контекста * Улучшена мемоизация и оптимизированы расчёты
This commit is contained in:
parent
ac9b4d6c0d
commit
cc1c6a0661
@ -4,8 +4,6 @@ import locale from 'antd/lib/locale/ru_RU'
|
||||
import { ConfigProvider } from 'antd'
|
||||
|
||||
import { RootPathContext } from '@asb/context'
|
||||
import { UserOutlet } from '@components/outlets'
|
||||
import LayoutPortal from '@components/LayoutPortal'
|
||||
import SuspenseFallback from '@components/SuspenseFallback'
|
||||
import { getUser, NoAccessComponent } from '@utils'
|
||||
import { OpenAPI } from '@api'
|
||||
@ -13,6 +11,10 @@ import { OpenAPI } from '@api'
|
||||
import '@styles/include/antd_theme.less'
|
||||
import '@styles/App.less'
|
||||
|
||||
const UserOutlet = lazy(() => import('@components/outlets/UserOutlet'))
|
||||
const DepositsOutlet = lazy(() => import('@components/outlets/DepositsOutlet'))
|
||||
const LayoutPortal = lazy(() => import('@components/LayoutPortal'))
|
||||
|
||||
const Login = lazy(() => import('@pages/public/Login'))
|
||||
const Register = lazy(() => import('@pages/public/Register'))
|
||||
const FileDownload = lazy(() => import('@pages/FileDownload'))
|
||||
@ -44,6 +46,7 @@ export const App = memo(() => (
|
||||
<Route element={<UserOutlet />}>
|
||||
<Route path={'/file_download/:idFile/*'} element={<FileDownload />} />
|
||||
|
||||
<Route element={<DepositsOutlet />}>
|
||||
<Route element={<LayoutPortal />}>
|
||||
{/* Admin pages */}
|
||||
<Route path={'/admin/*'} element={<AdminPanel />} />
|
||||
@ -54,6 +57,7 @@ export const App = memo(() => (
|
||||
<Route path={'/well/:idWell/*'} element={<Well />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
</Router>
|
||||
</Suspense>
|
||||
|
35
src/components/outlets/DepositsOutlet.tsx
Normal file
35
src/components/outlets/DepositsOutlet.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
|
||||
import { DepositsContext } from '@asb/context'
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { DepositDto, DepositService } from '@api'
|
||||
import { arrayOrDefault } from '@utils'
|
||||
|
||||
export const DepositsOutlet = memo(() => {
|
||||
const [deposits, setDeposits] = useState<DepositDto[]>([])
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const deposits = await DepositService.getDeposits()
|
||||
setDeposits(arrayOrDefault(deposits))
|
||||
},
|
||||
setIsLoading,
|
||||
`Не удалось загрузить список кустов`,
|
||||
{ actionName: 'Получить список кустов' }
|
||||
)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<DepositsContext.Provider value={deposits}>
|
||||
<LoaderPortal show={isLoading}>
|
||||
<Outlet />
|
||||
</LoaderPortal>
|
||||
</DepositsContext.Provider>
|
||||
)
|
||||
})
|
||||
|
||||
export default DepositsOutlet
|
@ -1 +1,2 @@
|
||||
export * from './DepositsOutlet'
|
||||
export * from './UserOutlet'
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { Tag, TreeSelect } from 'antd'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
|
||||
import { useDeposits } from '@asb/context'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { hasPermission } from '@utils'
|
||||
import { DepositService } from '@api'
|
||||
|
||||
export const getTreeData = async () => {
|
||||
const deposits = await DepositService.getDeposits()
|
||||
export const getTreeData = async (deposits) => {
|
||||
const wellsTree = deposits.map((deposit, dIdx) => ({
|
||||
title: deposit.caption,
|
||||
key: `0-${dIdx}`,
|
||||
@ -40,10 +39,12 @@ export const WellSelector = memo(({ value, onChange, treeData, treeLabels, ...ot
|
||||
const [wellsTree, setWellsTree] = useState([])
|
||||
const [wellLabels, setWellLabels] = useState([])
|
||||
|
||||
const deposits = useDeposits()
|
||||
|
||||
useEffect(() => {
|
||||
invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const wellsTree = treeData ?? await getTreeData()
|
||||
const wellsTree = treeData ?? await getTreeData(deposits)
|
||||
const labels = treeLabels ?? getTreeLabels(wellsTree)
|
||||
setWellsTree(wellsTree)
|
||||
setWellLabels(labels)
|
||||
@ -52,7 +53,7 @@ export const WellSelector = memo(({ value, onChange, treeData, treeLabels, ...ot
|
||||
'Не удалось загрузить список скважин',
|
||||
{ actionName: 'Получение списка скважин' }
|
||||
)
|
||||
}, [treeData, treeLabels])
|
||||
}, [deposits, treeData, treeLabels])
|
||||
|
||||
return (
|
||||
<TreeSelect
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Button, Drawer, Skeleton, Tree, TreeDataNode, TreeProps, Typography } from 'antd'
|
||||
import { useState, useEffect, useCallback, memo, Key } from 'react'
|
||||
import { Drawer, Tree, TreeDataNode, TreeProps } from 'antd'
|
||||
import { useState, useEffect, useCallback, memo, Key, useMemo } from 'react'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { useDeposits } from '@asb/context'
|
||||
import { WellIcon, WellIconState } from '@components/icons'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { DepositService, DepositDto, WellDto } from '@api'
|
||||
import { DepositDto, WellDto } from '@api'
|
||||
import { isRawDate } from '@utils'
|
||||
|
||||
import { ReactComponent as DepositIcon } from '@images/DepositIcon.svg'
|
||||
@ -91,28 +91,7 @@ const getExpandKeys = (treeData: TreeDataNode[], depositKeys?: Key[] | boolean):
|
||||
return out
|
||||
}
|
||||
|
||||
export const WellTreeSelector = memo<WellTreeSelectorProps>(({ expand, current, onChange, onClose, open, ...other }) => {
|
||||
const [wellsTree, setWellsTree] = useState<TreeDataNode[]>([])
|
||||
const [showLoader, setShowLoader] = useState<boolean>(false)
|
||||
const [expanded, setExpanded] = useState<Key[]>([])
|
||||
const [selected, setSelected] = useState<Key[]>([])
|
||||
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
useEffect(() => {
|
||||
if (current) setSelected([current])
|
||||
}, [current])
|
||||
|
||||
useEffect(() => {
|
||||
setExpanded((prev) => expand ? getExpandKeys(wellsTree, expand) : prev)
|
||||
}, [wellsTree, expand])
|
||||
|
||||
useEffect(() => {
|
||||
invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const deposits: Array<DepositDto> = await DepositService.getDeposits()
|
||||
const wellsTree: TreeDataNode[] = deposits.map(deposit =>({
|
||||
const makeWellsTreeData = (deposits: DepositDto[]): TreeDataNode[] => deposits.map(deposit =>({
|
||||
title: deposit.caption,
|
||||
key: `/deposit/${deposit.id}`,
|
||||
value: `/deposit/${deposit.id}`,
|
||||
@ -140,13 +119,16 @@ export const WellTreeSelector = memo<WellTreeSelectorProps>(({ expand, current,
|
||||
}
|
||||
}),
|
||||
}))
|
||||
setWellsTree(wellsTree)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список скважин`,
|
||||
{ actionName: 'Получить список скважин' }
|
||||
)
|
||||
}, [])
|
||||
|
||||
export const WellTreeSelector = memo<WellTreeSelectorProps>(({ expand, current, onChange, onClose, open, ...other }) => {
|
||||
const [expanded, setExpanded] = useState<Key[]>([])
|
||||
const [selected, setSelected] = useState<Key[]>([])
|
||||
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const deposits = useDeposits()
|
||||
|
||||
const wellsTree = useMemo(() => makeWellsTreeData(deposits), [deposits])
|
||||
|
||||
const onValueChange = useCallback((value?: string): void => {
|
||||
const key = getKeyByUrl(value)[0]
|
||||
@ -169,11 +151,18 @@ export const WellTreeSelector = memo<WellTreeSelectorProps>(({ expand, current,
|
||||
navigate(newPath, { state: { from: location.pathname }})
|
||||
}, [navigate, location])
|
||||
|
||||
useEffect(() => onValueChange(location.pathname), [onValueChange, location])
|
||||
useEffect(() => {
|
||||
if (current) setSelected([current])
|
||||
}, [current])
|
||||
|
||||
useEffect(() => {
|
||||
setExpanded((prev) => expand ? getExpandKeys(wellsTree, expand) : prev)
|
||||
}, [wellsTree, expand])
|
||||
|
||||
useEffect(() => onValueChange(location.pathname), [onValueChange, location.pathname])
|
||||
|
||||
return (
|
||||
<Drawer open={open} mask={false} onClose={onClose} title={'Список скважин'}>
|
||||
<Skeleton active loading={showLoader}>
|
||||
<Tree
|
||||
{...other}
|
||||
showIcon
|
||||
@ -183,7 +172,6 @@ export const WellTreeSelector = memo<WellTreeSelectorProps>(({ expand, current,
|
||||
onExpand={setExpanded}
|
||||
expandedKeys={expanded}
|
||||
/>
|
||||
</Skeleton>
|
||||
</Drawer>
|
||||
)
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createContext, useContext, useEffect } from 'react'
|
||||
|
||||
import { LayoutPortalProps } from '@components/LayoutPortal'
|
||||
import { UserTokenDto, WellDto } from '@api'
|
||||
import { DepositDto, UserTokenDto, WellDto } from '@api'
|
||||
|
||||
/** Контекст текущей скважины */
|
||||
export const WellContext = createContext<[WellDto, (well: WellDto) => void]>([{}, () => {}])
|
||||
@ -13,6 +13,8 @@ export const UserContext = createContext<UserTokenDto>({})
|
||||
export const LayoutPropsContext = createContext<(props: LayoutPortalProps) => void>(() => {})
|
||||
/** Контекст для блока справа от крошек на страницах скважин и админки */
|
||||
export const TopRightBlockContext = createContext<(block: JSX.Element) => void>(() => {})
|
||||
/** Контекст со списком месторождений */
|
||||
export const DepositsContext = createContext<DepositDto[]>([])
|
||||
|
||||
/**
|
||||
* Получить текущую скважину
|
||||
@ -35,6 +37,18 @@ export const useRootPath = () => useContext(RootPathContext)
|
||||
*/
|
||||
export const useUser = () => useContext(UserContext)
|
||||
|
||||
/**
|
||||
* Получить список скважин
|
||||
*
|
||||
* @returns Список скважин
|
||||
*/
|
||||
export const useDeposits = () => useContext(DepositsContext)
|
||||
|
||||
/**
|
||||
* Получить метод задания элементов справа от крошек
|
||||
*
|
||||
* @returns Метод задания элементов справа от крошек
|
||||
*/
|
||||
export const useTopRightBlock = () => useContext(TopRightBlockContext)
|
||||
|
||||
/**
|
||||
|
@ -1,37 +1,31 @@
|
||||
import { useState, useEffect, memo, useMemo } from 'react'
|
||||
import { useEffect, memo, useMemo, useCallback } from 'react'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { Map, Overlay } from 'pigeon-maps'
|
||||
import { Popover, Badge } from 'antd'
|
||||
|
||||
import { useLayoutProps } from '@asb/context'
|
||||
import { useDeposits, useLayoutProps } from '@asb/context'
|
||||
import { PointerIcon } from '@components/icons'
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { FastRunMenu } from '@components/FastRunMenu'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { arrayOrDefault, limitValue, withPermissions } from '@utils'
|
||||
import { DepositService } from '@api'
|
||||
import { limitValue, withPermissions } from '@utils'
|
||||
|
||||
import '@styles/index.css'
|
||||
|
||||
const defaultViewParams = { center: [60.81226, 70.0562], zoom: 5 }
|
||||
|
||||
const zoomLimit = limitValue(5, 15)
|
||||
|
||||
const calcViewParams = (clusters) => {
|
||||
if ((clusters?.length ?? 0) <= 0)
|
||||
return defaultViewParams
|
||||
return { center: [60.81226, 70.0562], zoom: 5 }
|
||||
|
||||
const center = clusters.reduce((sum, cluster) => {
|
||||
sum[0] += (cluster.latitude / clusters.length)
|
||||
sum[1] += (cluster.longitude / clusters.length)
|
||||
sum[0] += cluster.latitude
|
||||
sum[1] += cluster.longitude
|
||||
return sum
|
||||
}, [0, 0])
|
||||
}, [0, 0]).map((elm) => elm / clusters.length)
|
||||
|
||||
const maxDeg = clusters.reduce((max, cluster) => {
|
||||
const dLatitude = Math.abs(center[0] - cluster.latitude)
|
||||
const dLongitude = Math.abs(center[1] - cluster.longitude)
|
||||
const d = dLatitude > dLongitude ? dLatitude : dLongitude
|
||||
return d > max ? d : max
|
||||
return Math.max(Math.max(dLatitude, dLongitude), max)
|
||||
}, 0)
|
||||
|
||||
// zoom max = 20 (too close)
|
||||
@ -44,75 +38,67 @@ const calcViewParams = (clusters) => {
|
||||
}
|
||||
|
||||
const Deposit = memo(() => {
|
||||
const [depositsData, setDepositsData] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [viewParams, setViewParams] = useState(defaultViewParams)
|
||||
|
||||
const deposits = useDeposits()
|
||||
const setLayoutProps = useLayoutProps()
|
||||
|
||||
const location = useLocation()
|
||||
|
||||
const selectorProps = useMemo(() => {
|
||||
const hasId = location.pathname.length > '/deposit/'.length
|
||||
|
||||
return {
|
||||
expand: hasId ? [location.pathname] : true,
|
||||
current: hasId ? location.pathname : undefined,
|
||||
}
|
||||
}, [location.pathname])
|
||||
|
||||
useEffect(() => setLayoutProps({
|
||||
sheet: false,
|
||||
showSelector: true,
|
||||
selectorProps,
|
||||
title: 'Месторождение',
|
||||
}), [setLayoutProps, selectorProps])
|
||||
|
||||
useEffect(() => {
|
||||
invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const deposits = await DepositService.getDeposits()
|
||||
setDepositsData(arrayOrDefault(deposits))
|
||||
setViewParams(calcViewParams(deposits))
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список кустов`,
|
||||
{ actionName: 'Получить список кустов' }
|
||||
)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<FastRunMenu />
|
||||
<LoaderPortal show={showLoader}>
|
||||
<div className={'h-100vh'} style={{ overflow: 'hidden' }}>
|
||||
<Map {...viewParams}>
|
||||
{depositsData.map(deposit => (
|
||||
<Overlay
|
||||
width={32}
|
||||
anchor={[deposit.latitude, deposit.longitude]}
|
||||
key={`${deposit.latitude} ${deposit.longitude}`}
|
||||
>
|
||||
<Popover content={
|
||||
const makeDepositLinks = useCallback((clusters) => (
|
||||
<div>
|
||||
{deposit.clusters.map(cluster => (
|
||||
<Link key={cluster.id} to={{ pathname: `/cluster/${cluster.id}`, state: { from: location.pathname }}}>
|
||||
{clusters.map(cluster => (
|
||||
<Link
|
||||
key={cluster.id}
|
||||
to={{
|
||||
pathname: `/cluster/${cluster.id}`,
|
||||
state: { from: location.pathname }
|
||||
}}
|
||||
>
|
||||
<div>{cluster.caption}</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
} trigger={['click']} title={deposit.caption}>
|
||||
<div style={{cursor: 'pointer'}}>
|
||||
), [location.pathname])
|
||||
|
||||
const viewParams = useMemo(() => calcViewParams(deposits), [deposits])
|
||||
|
||||
useEffect(() => {
|
||||
const hasId = location.pathname.length > '/deposit/'.length
|
||||
|
||||
const selectorProps = {
|
||||
expand: hasId ? [location.pathname] : true,
|
||||
current: hasId ? location.pathname : undefined,
|
||||
}
|
||||
|
||||
setLayoutProps({
|
||||
sheet: false,
|
||||
showSelector: true,
|
||||
selectorProps,
|
||||
title: 'Месторождение',
|
||||
})
|
||||
}, [setLayoutProps, location.pathname])
|
||||
|
||||
return (
|
||||
<>
|
||||
<FastRunMenu />
|
||||
<div className={'deposit-page'}>
|
||||
<Map {...viewParams}>
|
||||
{deposits.map(deposit => {
|
||||
const anchor = [deposit.latitude, deposit.longitude]
|
||||
const links = makeDepositLinks(deposit.clusters)
|
||||
|
||||
return (
|
||||
<Overlay width={32} anchor={anchor} key={anchor.join(' ')}>
|
||||
<Popover content={links} trigger={['click']} title={deposit.caption}>
|
||||
<div className={'pointer'}>
|
||||
<Badge count={deposit.clusters.length}>
|
||||
<PointerIcon state={'active'} width={48} height={59} />
|
||||
</Badge>
|
||||
</div>
|
||||
</Popover>
|
||||
</Overlay>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</Map>
|
||||
</div>
|
||||
</LoaderPortal>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
@ -158,3 +158,12 @@ code {
|
||||
.asb-grid-item {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.deposit-page {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user