forked from ddrilling/asb_cloud_front
Добавлена возможность отображения крошек
This commit is contained in:
parent
26222104b7
commit
879fea41a5
@ -1,4 +1,4 @@
|
||||
import { Button, Layout, LayoutProps, Menu, SiderProps } from 'antd'
|
||||
import { Breadcrumb, Layout, LayoutProps, Menu, SiderProps } from 'antd'
|
||||
import { Key, memo, ReactNode, Suspense, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { ItemType } from 'antd/lib/menu/hooks/useItems'
|
||||
import { Link, Outlet } from 'react-router-dom'
|
||||
@ -6,19 +6,18 @@ import {
|
||||
ApartmentOutlined,
|
||||
CodeOutlined,
|
||||
HomeOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons'
|
||||
|
||||
import { LayoutPropsContext } from '@asb/context'
|
||||
import PageHeader from '@components/PageHeader'
|
||||
import { UserMenu, UserMenuProps } from '@components/UserMenu'
|
||||
import { WellTreeSelector, WellTreeSelectorProps } from '@components/selectors/WellTreeSelector'
|
||||
import { isURLAvailable, wrapPrivateComponent } from '@utils'
|
||||
|
||||
import SuspenseFallback from './SuspenseFallback'
|
||||
|
||||
import Logo from '@images/Logo'
|
||||
|
||||
import '@styles/layout.less'
|
||||
|
||||
const { Content, Sider } = Layout
|
||||
@ -32,6 +31,8 @@ export type LayoutPortalProps = Omit<LayoutProps, 'children'> & {
|
||||
siderProps?: SiderProps & { userMenuProps?: UserMenuProps }
|
||||
isAdmin?: boolean
|
||||
fallback?: JSX.Element
|
||||
breadcrumb?: boolean | JSX.Element
|
||||
topRightBlock?: JSX.Element
|
||||
}
|
||||
|
||||
const defaultProps: LayoutPortalProps = {
|
||||
@ -49,7 +50,7 @@ const _LayoutPortal = memo(() => {
|
||||
const [currentWell, setCurrentWell] = useState<string>('')
|
||||
const [props, setProps] = useState<LayoutPortalProps>(defaultProps)
|
||||
|
||||
const { isAdmin, title, sheet, showSelector, selectorProps, sider, siderProps, fallback, ...other } = useMemo(() => props, [props])
|
||||
const { isAdmin, title, sheet, showSelector, selectorProps, sider, siderProps, fallback, breadcrumb, topRightBlock, ...other } = useMemo(() => props, [props])
|
||||
|
||||
const setLayoutProps = useCallback((props: LayoutPortalProps) => setProps({ ...defaultProps, ...props}), [])
|
||||
|
||||
@ -71,7 +72,7 @@ const _LayoutPortal = memo(() => {
|
||||
<Sider {...siderProps} collapsedWidth={50} collapsed={menuCollapsed} trigger={null} collapsible className={`menu-sider ${siderProps?.className || ''}`}>
|
||||
<div className={'sider-content'}>
|
||||
<button className={'sider-toogle'} onClick={() => setMenuCollapsed((prev) => !prev)}>
|
||||
{menuCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
<Logo onlyIcon={menuCollapsed} />
|
||||
</button>
|
||||
<div className={'scrollable hide-slider'}>
|
||||
{sider}
|
||||
@ -91,25 +92,34 @@ const _LayoutPortal = memo(() => {
|
||||
</div>
|
||||
</Sider>
|
||||
)}
|
||||
<Layout className={'page-content'}>
|
||||
<PageHeader title={title}>
|
||||
{isAdmin ? (
|
||||
<Button size={'large'}>
|
||||
<Link to={'/'}>Вернуться на сайт</Link>
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
{!isAdmin && (
|
||||
<WellTreeSelector
|
||||
open={wellsTreeOpen}
|
||||
onClose={() => setWellsTreeOpen(false)}
|
||||
{...selectorProps}
|
||||
onChange={(well) => setCurrentWell(well ?? 'Выберите месторождение')}
|
||||
/>
|
||||
<Button onClick={() => setWellsTreeOpen((prev) => !prev)}>{currentWell}</Button>
|
||||
</>
|
||||
)}
|
||||
</PageHeader>
|
||||
<Layout className={'page-content'}>
|
||||
<Content {...other} className={`${sheet ? 'site-layout-background sheet' : ''} ${other.className ?? ''}`}>
|
||||
{(breadcrumb || topRightBlock) && (
|
||||
<div className={'breadcrumb-block'}>
|
||||
{breadcrumb && (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item href={'/'}>
|
||||
<HomeOutlined />
|
||||
</Breadcrumb.Item>
|
||||
{!isAdmin && (
|
||||
<Breadcrumb.Item>
|
||||
<a style={{ userSelect: 'none' }} onClick={() => setWellsTreeOpen((prev) => !prev)}>{currentWell}</a>
|
||||
</Breadcrumb.Item>
|
||||
)}
|
||||
{breadcrumb}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
{topRightBlock}
|
||||
</div>
|
||||
)}
|
||||
<LayoutPropsContext.Provider value={setLayoutProps}>
|
||||
<Suspense fallback={fallback ?? <SuspenseFallback style={{ minHeight: '100%' }} />}>
|
||||
<Outlet />
|
||||
@ -121,8 +131,6 @@ const _LayoutPortal = memo(() => {
|
||||
)
|
||||
})
|
||||
|
||||
export const LayoutPortal = wrapPrivateComponent(_LayoutPortal, {
|
||||
requirements: ['Deposit.get'],
|
||||
})
|
||||
export const LayoutPortal = wrapPrivateComponent(_LayoutPortal, { requirements: ['Deposit.get'] })
|
||||
|
||||
export default LayoutPortal
|
||||
|
56
src/components/MenuBreadcrumb.tsx
Normal file
56
src/components/MenuBreadcrumb.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { Breadcrumb, BreadcrumbItemProps } from 'antd'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { memo, useMemo } from 'react'
|
||||
import { join } from 'path'
|
||||
|
||||
import { PrivateWellMenuItem } from '@components/PrivateWellMenu'
|
||||
import { FunctionalValue, useFunctionalValue } from '@utils'
|
||||
|
||||
export const makeBreadcrumbItems = (items: PrivateWellMenuItem[], pathParts: string[], root: string = '/') => {
|
||||
const out = []
|
||||
const parts = [...pathParts]
|
||||
let route = root
|
||||
let arr: PrivateWellMenuItem[] | undefined = items
|
||||
while (arr && parts.length > 0) {
|
||||
const child: PrivateWellMenuItem | undefined = arr.find(elm => elm.route.toLowerCase() === parts[0].toLowerCase())
|
||||
if (!child) break
|
||||
route = join(route, child.route)
|
||||
out.push({ ...child, route })
|
||||
parts.splice(0, 1)
|
||||
arr = child.children
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
export type MenuBreadcrumbItemsProps = {
|
||||
menuItems: PrivateWellMenuItem[]
|
||||
pathRoot?: RegExp
|
||||
itemsProps?: FunctionalValue<(item: PrivateWellMenuItem) => BreadcrumbItemProps>
|
||||
itemRender?: (item: PrivateWellMenuItem) => JSX.Element
|
||||
}
|
||||
|
||||
export const MenuBreadcrumbItems = memo<MenuBreadcrumbItemsProps>(({ menuItems, pathRoot = /^\//, itemsProps, itemRender }) => {
|
||||
const location = useLocation()
|
||||
const getItemProps = useFunctionalValue(itemsProps)
|
||||
|
||||
const items = useMemo(() => {
|
||||
const path = location.pathname
|
||||
const rootPart = pathRoot.exec(path)
|
||||
if (!rootPart || rootPart.length <= 0) return []
|
||||
const root = rootPart[0]
|
||||
const parts = path.trim().slice(root.length).split('/')
|
||||
return makeBreadcrumbItems(menuItems, parts, root)
|
||||
}, [location, menuItems, pathRoot])
|
||||
|
||||
return (
|
||||
<>
|
||||
{items.map((item) => (
|
||||
<Breadcrumb.Item key={item.route} {...getItemProps(item)}>
|
||||
{itemRender ? itemRender(item) : (
|
||||
<Link to={item.route}>{item.title}</Link>
|
||||
)}
|
||||
</Breadcrumb.Item>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
})
|
@ -1,27 +0,0 @@
|
||||
import { memo } from 'react'
|
||||
import { Layout } from 'antd'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { BasicProps } from 'antd/lib/layout/layout'
|
||||
|
||||
import { headerHeight } from '@utils'
|
||||
|
||||
import Logo from '@images/Logo'
|
||||
|
||||
import '@styles/layout.less'
|
||||
|
||||
export type PageHeaderProps = BasicProps & {
|
||||
title?: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export const PageHeader: React.FC<PageHeaderProps> = memo(({ title, children, ...other }) => (
|
||||
<Layout.Header className={'header'} {...other}>
|
||||
<Link to={'/'} style={{ height: headerHeight }}>
|
||||
<Logo />
|
||||
</Link>
|
||||
<h1 className={'title'}>{title}</h1>
|
||||
{children}
|
||||
</Layout.Header>
|
||||
))
|
||||
|
||||
export default PageHeader
|
@ -72,7 +72,7 @@ body {
|
||||
}
|
||||
|
||||
.h-100vh {
|
||||
height: calc(100vh - 64px);
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.p-10 {
|
||||
|
@ -1,5 +1,7 @@
|
||||
@header-height: 64px;
|
||||
@layout-min-height: calc(100vh - @header-height);
|
||||
@link-color: #FFFC;
|
||||
@active-bg: #c32828;
|
||||
@admin-bg: #900;
|
||||
@admin-active-bg: #413f3d;
|
||||
|
||||
.no-select {
|
||||
-webkit-user-select: none;
|
||||
@ -8,38 +10,6 @@
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
gap: 50px;
|
||||
height: @header-height;
|
||||
|
||||
& .logo {
|
||||
padding: 8px 24px;
|
||||
margin: 0 10px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
& .title {
|
||||
flex-grow: 1;
|
||||
color: #fff;
|
||||
padding-left: calc(100vw / 2 - 400px);
|
||||
.no-select;
|
||||
}
|
||||
|
||||
& button {
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@link-color: #FFFC;
|
||||
@active-bg: #c32828;
|
||||
@admin-bg: #900;
|
||||
@admin-active-bg: #413f3d;
|
||||
|
||||
.ant-menu-submenu-popup .ant-menu-sub {
|
||||
color: @link-color;
|
||||
|
||||
@ -88,12 +58,14 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: calc(@header-height - 4px) !important;
|
||||
// padding: 10px;
|
||||
height: 100px !important;
|
||||
color: @link-color;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
@ -115,6 +87,19 @@
|
||||
& .page-content {
|
||||
overflow-y: auto;
|
||||
background: white;
|
||||
|
||||
& .breadcrumb-block {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 0 15px 15px 15px;
|
||||
|
||||
& .ant-breadcrumb-link, .ant-breadcrumb-separator {
|
||||
.no-select;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
& .sheet{
|
||||
|
Loading…
Reference in New Issue
Block a user