forked from ddrilling/asb_cloud_front
Добавлена панель быстрого перехода между страницами
This commit is contained in:
parent
11cb245cf5
commit
e773943b61
144
src/components/FastRunMenu.tsx
Normal file
144
src/components/FastRunMenu.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { DefaultOptionType } from 'antd/lib/select'
|
||||
import { AutoComplete } from 'antd'
|
||||
import { BaseSelectRef } from 'rc-select'
|
||||
import { join } from 'path'
|
||||
|
||||
import { useWell } from '@asb/context'
|
||||
import { makeItem, PrivateWellMenuItem } from './PrivateWellMenu'
|
||||
import { hasPermission, isURLAvailable } from '@utils'
|
||||
|
||||
import { menuItems as adminMenuItems } from '@pages/AdminPanel/AdminNavigationMenu'
|
||||
import { menuItems as wellMenuItems } from '@pages/Well/NavigationMenu'
|
||||
|
||||
import '@styles/fast_run_menu.less'
|
||||
|
||||
const transliterationTable = {
|
||||
'q': 'й', 'w': 'ц', 'e': 'у', 'r': 'к', 't': 'е', 'y': 'н', 'u': 'г', 'i': 'ш', 'o': 'щ', 'p': 'з', '[': 'х', ']': 'ъ', '{': 'х', '}': 'ъ',
|
||||
'a': 'ф', 's': 'ы', 'd': 'в', 'f': 'а', 'g': 'п', 'h': 'р', 'j': 'о', 'k': 'л', 'l': 'д', ';': 'ж', "'": 'э', ':': 'ж', '"': 'э',
|
||||
'z': 'я', 'x': 'ч', 'c': 'с', 'v': 'м', 'b': 'и', 'n': 'т', 'm': 'ь', ',': 'б', '.': 'ю', '<': 'б', '>': 'ю',
|
||||
}
|
||||
|
||||
const transliterateToRu = (text: string): string => {
|
||||
let out = text.toLowerCase()
|
||||
Object.entries(transliterationTable).map(([en, ru]) => out = out.replaceAll(en, ru))
|
||||
return out
|
||||
}
|
||||
|
||||
const transliterateToEn = (text: string): string => {
|
||||
let out = text.toLowerCase()
|
||||
Object.entries(transliterationTable).map(([en, ru]) => out = out.replaceAll(ru, en))
|
||||
return out
|
||||
}
|
||||
|
||||
const applyVars = (route: string, vars?: Record<string, any>): string => {
|
||||
if (!vars) return route
|
||||
let out = route
|
||||
Object.entries(vars).forEach(([key, value]) => {
|
||||
out = out.replaceAll(`{${key}}`, value)
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
const makeOptions = (items: PrivateWellMenuItem[], vars?: Record<string, any>): DefaultOptionType[] => {
|
||||
const out: DefaultOptionType[] = []
|
||||
items.forEach((item) => {
|
||||
if (!hasPermission(item.permissions)) return
|
||||
out.push({
|
||||
label: item.title,
|
||||
value: applyVars(item.route, vars),
|
||||
})
|
||||
if (item.children) {
|
||||
const childrenOptions = makeOptions(item.children).map((child) => ({
|
||||
label: `${item.title} > ${child.label}`,
|
||||
value: applyVars(join(item.route, String(child.value)), vars),
|
||||
}))
|
||||
out.push(...childrenOptions)
|
||||
}
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
export const FastRunMenu = memo(() => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [value, setValue] = useState<string | null>()
|
||||
const ref = useRef<BaseSelectRef | null>(null)
|
||||
|
||||
const [well] = useWell()
|
||||
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
const options = useMemo(() => {
|
||||
const menus = [
|
||||
makeItem('Месторождения', '/deposit', []),
|
||||
]
|
||||
|
||||
if (isURLAvailable('/admin'))
|
||||
menus.push(makeItem('Панель администратора', '/admin', [], undefined, adminMenuItems as PrivateWellMenuItem[]))
|
||||
|
||||
if (well.id)
|
||||
menus.push(
|
||||
makeItem(`Куст (${well.cluster})`, `/cluster/${well.idCluster}`, []),
|
||||
makeItem(`Скважина (${well.caption})`, '/well/{idWell}', [], undefined, wellMenuItems),
|
||||
)
|
||||
|
||||
return makeOptions(menus, { idWell: well.id })
|
||||
}, [well])
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
setIsOpen(false)
|
||||
setValue(null)
|
||||
}, [])
|
||||
|
||||
const onTextChanged = useCallback((value: any) => {
|
||||
navigate(value, { state: { from: location.pathname } })
|
||||
onClose()
|
||||
}, [onClose])
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (event: KeyboardEvent) => {
|
||||
if (event.altKey && event.code === 'KeyA')
|
||||
setIsOpen((prev) => !prev)
|
||||
}
|
||||
|
||||
document.addEventListener('keyup', listener)
|
||||
|
||||
return () => document.removeEventListener('keyup', listener)
|
||||
}, [ref.current])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return
|
||||
ref.current?.focus()
|
||||
ref.current?.scrollTo(0)
|
||||
}, [isOpen])
|
||||
|
||||
const onFilter = (text: string, option: DefaultOptionType | undefined) => {
|
||||
if (!option) return false
|
||||
const search = (String(option.label).replaceAll(' > ', '') + String(option.value).replaceAll('/', '')).toLowerCase()
|
||||
if (search.includes(text.toLowerCase())) return true
|
||||
if (search.includes(transliterateToRu(text))) return true
|
||||
if (search.includes(transliterateToEn(text))) return true
|
||||
return false
|
||||
}
|
||||
|
||||
return isOpen ? (
|
||||
<div className={'fast-run-menu'}>
|
||||
<AutoComplete
|
||||
ref={ref}
|
||||
autoFocus
|
||||
style={{ width: '100%' }}
|
||||
options={options}
|
||||
onBlur={onClose}
|
||||
onChange={setValue}
|
||||
placeholder={'Введите название страницы...'}
|
||||
onSelect={onTextChanged}
|
||||
value={value}
|
||||
filterOption={onFilter}
|
||||
/>
|
||||
</div>
|
||||
) : <></>
|
||||
})
|
||||
|
||||
export default FastRunMenu
|
@ -2,6 +2,7 @@ import { Navigate, Route, Routes } from 'react-router-dom'
|
||||
import { lazy, memo, useMemo } from 'react'
|
||||
|
||||
import { RootPathContext, useLayoutProps, useRootPath } from '@asb/context'
|
||||
import { FastRunMenu } from '@components/FastRunMenu'
|
||||
import { MenuBreadcrumbItems } from '@components/MenuBreadcrumb'
|
||||
import { NoAccessComponent, withPermissions } from '@utils'
|
||||
|
||||
@ -35,6 +36,7 @@ const AdminPanel = memo(() => {
|
||||
|
||||
return (
|
||||
<RootPathContext.Provider value={rootPath}>
|
||||
<FastRunMenu />
|
||||
<Routes>
|
||||
<Route index element={<Navigate to={'visit_log'} replace />} />
|
||||
<Route path={'*'} element={<NoAccessComponent />} />
|
||||
|
@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom'
|
||||
|
||||
import { useLayoutProps } from '@asb/context'
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { FastRunMenu } from '@components/FastRunMenu'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { arrayOrDefault, withPermissions } from '@utils'
|
||||
import { OperationStatService } from '@api'
|
||||
@ -34,9 +35,12 @@ const Cluster = memo(() => {
|
||||
}, [idCluster])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<ClusterWells statsWells={data} />
|
||||
</LoaderPortal>
|
||||
<>
|
||||
<FastRunMenu />
|
||||
<LoaderPortal show={showLoader}>
|
||||
<ClusterWells statsWells={data} />
|
||||
</LoaderPortal>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Map, Overlay } from 'pigeon-maps'
|
||||
import { useState, useEffect, memo, useMemo } 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 { 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'
|
||||
@ -81,35 +82,38 @@ const Deposit = memo(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<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={
|
||||
<div>
|
||||
{deposit.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'}}>
|
||||
<Badge count={deposit.clusters.length}>
|
||||
<PointerIcon state={'active'} width={48} height={59} />
|
||||
</Badge>
|
||||
</div>
|
||||
</Popover>
|
||||
</Overlay>
|
||||
))}
|
||||
</Map>
|
||||
</div>
|
||||
</LoaderPortal>
|
||||
<>
|
||||
<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={
|
||||
<div>
|
||||
{deposit.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'}}>
|
||||
<Badge count={deposit.clusters.length}>
|
||||
<PointerIcon state={'active'} width={48} height={59} />
|
||||
</Badge>
|
||||
</div>
|
||||
</Popover>
|
||||
</Overlay>
|
||||
))}
|
||||
</Map>
|
||||
</div>
|
||||
</LoaderPortal>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { lazy, memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom'
|
||||
|
||||
import { WellContext, RootPathContext, useRootPath, useLayoutProps, TopRightBlockContext } from '@asb/context'
|
||||
import { FastRunMenu } from '@components/FastRunMenu'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { MenuBreadcrumbItems } from '@components/MenuBreadcrumb'
|
||||
import { NoAccessComponent, withPermissions } from '@utils'
|
||||
@ -86,6 +87,7 @@ const Well = memo(() => {
|
||||
return (
|
||||
<RootPathContext.Provider value={rootPath}>
|
||||
<WellContext.Provider value={[well, updateWell]}>
|
||||
<FastRunMenu />
|
||||
<TopRightBlockContext.Provider value={setTopRightBlock}>
|
||||
<Routes>
|
||||
<Route index element={<Navigate to={'telemetry'} replace />} />
|
||||
|
9
src/styles/fast_run_menu.less
Normal file
9
src/styles/fast_run_menu.less
Normal file
@ -0,0 +1,9 @@
|
||||
.fast-run-menu {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
top: 100px;
|
||||
left: calc(25vw - 5px);
|
||||
border: 5px solid #777;
|
||||
border-radius: 5px;
|
||||
width: 50vw;
|
||||
}
|
Loading…
Reference in New Issue
Block a user