Optimize submenu in ToolsFramework. Optimize code

This commit is contained in:
2023-10-12 16:18:24 +08:00
parent bc82572fa8
commit 6a7cae8319
11 changed files with 214 additions and 134 deletions

View File

@@ -16,9 +16,9 @@ const AuthRoute = () => {
if (matches.some(({ handle }) => (handle as RouteHandle)?.auth) && !isLogin) { if (matches.some(({ handle }) => (handle as RouteHandle)?.auth) && !isLogin) {
return ( return (
<Navigate <Navigate
to={`/login${ to={`/login?redirect=${encodeURIComponent(
'?redirect=' + encodeURIComponent(lastMatch.pathname + location.search) `${lastMatch.pathname}${location.search}`
}`} )}`}
/> />
) )
} }
@@ -27,13 +27,13 @@ const AuthRoute = () => {
} }
return outlet return outlet
}, [ }, [
handle?.auth,
handle?.title, handle?.title,
handle?.titlePostfix, handle?.titlePostfix,
handle?.titlePrefix, handle?.titlePrefix,
isLogin, isLogin,
lastMatch.pathname, lastMatch.pathname,
location.search, location.search,
matches,
outlet outlet
]) ])
} }

View File

@@ -8,4 +8,9 @@ $font-secondary-color: #9E9E9E;
$focus-color: #DDDDDD; $focus-color: #DDDDDD;
$border-color: rgba(204, 204, 204, 0.33); $border-color: rgba(204, 204, 204, 0.33);
$url-color: rgba(102, 102, 102, .8); $url-color: rgba(102, 102, 102, .8);
$url-active-color: #ccc $url-active-color: #ccc;
$SIZE_ICON_XS: 16px;
$SIZE_ICON_SM: 20px;
$SIZE_ICON_MD: 24px;
$SIZE_ICON_LG: 32px;
$SIZE_ICON_XL: 64px;

View File

@@ -1,5 +1,9 @@
@use "@/assets/css/constants" as constants; @use "@/assets/css/constants" as constants;
body {
background-color: constants.$background-color;
}
.left-panel { .left-panel {
width: 16%; width: 16%;
background-color: constants.$origin-color; background-color: constants.$origin-color;
@@ -17,7 +21,6 @@
.content { .content {
> ul { > ul {
> li { > li {
//background-color: #4E47BB;
margin: 4px 14px; margin: 4px 14px;
&.item { &.item {
@@ -25,28 +28,79 @@
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
a.active { .menu-bt {
color: constants.$origin-color; .icon {
background-color: constants.$main-color; margin-right: 16px;
}
a {
display: flex;
padding: 8px 16px;
height: 100%;
width: 100%;
.text {
flex: 1;
}
&.active {
color: constants.$origin-color;
background-color: constants.$main-color;
}
}
} }
a.pending {
background-color: #123213; &:hover {
background-color: constants.$background-color;
} }
} }
a { &.multiple-item {
display: flex; font-size: 1.4em;
padding: 8px 16px; border-radius: 8px;
height: 100%; overflow: hidden;
width: 100%;
.icon { .menu-bt {
display: flex;
.icon-box {
cursor: pointer;
padding: 8px 16px;
.icon {
transition: all 0.3s ease;
transform: rotate(180deg);
}
}
a {
display: flex;
padding: {
top: 8px;
bottom: 8px;
right: 16px;
};
height: 100%;
width: 100%;
.text {
flex: 1;
}
}
}
.submenu {
} }
.text { &.show {
flex: 1; .menu-bt {
margin-left: 10px; .icon-box {
.icon {
transform: rotate(360deg);
}
}
}
} }
} }
@@ -60,10 +114,6 @@
}; };
opacity: 0.4; opacity: 0.4;
} }
&.item:hover {
background-color: constants.$background-color;
}
} }
} }
} }

View File

@@ -11,7 +11,7 @@ const FitCenter: React.FC<FitCenterProps> = (props) => {
return ( return (
<> <>
<div <div
className={`fit-center${className ? ' ' + className : ''}${ className={`fit-center${className ? ` ${className}` : ''}${
vertical ? ' flex-vertical' : ' flex-horizontal' vertical ? ' flex-vertical' : ' flex-horizontal'
}`} }`}
{..._props} {..._props}

View File

@@ -12,7 +12,7 @@ const FitFullScreen = forwardRef<HTMLDivElement, FitFullscreenProps>((props, ref
return ( return (
<> <>
<div <div
className={`fit-fullscreen${className ? ' ' + className : ''}`} className={`fit-fullscreen${className ? ` ${className}` : ''}`}
style={{ style={{
zIndex, zIndex,
backgroundColor, backgroundColor,

View File

@@ -24,7 +24,7 @@ const Indicator: React.FC<IndicatorProps> = (props) => {
return ( return (
<li <li
key={index} key={index}
className={'item center-box' + (index === current ? ' active' : '')} className={`item center-box${index === current ? ' active' : ''}`}
onClick={handleClick(index)} onClick={handleClick(index)}
> >
<div className={'dot'} /> <div className={'dot'} />

3
src/global.d.ts vendored
View File

@@ -15,6 +15,9 @@ type ToolsJsonObject = {
id: string id: string
component?: React.ComponentType component?: React.ComponentType
name?: string name?: string
titlePrefix?: string
title?: string
titlePostfix?: string
icon?: IconComponent icon?: IconComponent
menu?: boolean menu?: boolean
auth?: boolean auth?: boolean

View File

@@ -84,7 +84,7 @@ const HomeFramework: React.FC = () => {
> >
<div className={'body'}> <div className={'body'}>
<div> <div>
<header className={'nav' + (navbarHidden ? ' hide' : '')}> <header className={`nav${navbarHidden ? ' hide' : ''}`}>
<a className={'logo'} href={'https://fatweb.top'}> <a className={'logo'} href={'https://fatweb.top'}>
<span className={'title'}>FatWeb</span> <span className={'title'}>FatWeb</span>
</a> </a>
@@ -115,11 +115,9 @@ const HomeFramework: React.FC = () => {
key={subRoute.id} key={subRoute.id}
> >
<NavLink <NavLink
to={ to={`${route.path ?? ''}/${
(route.path ?? '') + subRoute.path ?? ''
'/' + }`}
(subRoute.path ?? '')
}
className={({ className={({
isActive, isActive,
isPending isPending
@@ -153,9 +151,9 @@ const HomeFramework: React.FC = () => {
})} })}
</ul> </ul>
<div <div
className={ className={`dropdown-menu-button${
'dropdown-menu-button' + (showDropdownMenu ? ' active' : '') showDropdownMenu ? ' active' : ''
} }`}
> >
<Icon <Icon
component={IconFatwebMenu} component={IconFatwebMenu}
@@ -165,9 +163,7 @@ const HomeFramework: React.FC = () => {
</div> </div>
</nav> </nav>
</header> </header>
<div <div className={`dropdown-menu-content${showDropdownMenu ? ' show' : ''}`}>
className={'dropdown-menu-content' + (showDropdownMenu ? ' show' : '')}
>
<ul> <ul>
{routeChildren?.map((route) => { {routeChildren?.map((route) => {
return ( return (

View File

@@ -1,13 +1,38 @@
import React from 'react' import React from 'react'
import FitFullScreen from '@/components/common/FitFullScreen' import FitFullScreen from '@/components/common/FitFullScreen'
import '@/assets/css/pages/tools-framework.scss' import '@/assets/css/pages/tools-framework.scss'
import router from '@/router'
import Icon from '@ant-design/icons' import Icon from '@ant-design/icons'
import { toolsJsonObjects } from '@/router/tools.tsx'
import _ from 'lodash'
const ToolsFramework: React.FC = () => { const ToolsFramework: React.FC = () => {
const frameworkRoute = useMatches()[1] const location = useLocation()
const routeId = frameworkRoute.id
const routeChildren = router.routes[0].children?.find((value) => value.id === routeId)?.children const [multipleMenuShown, setMultipleMenuShown] = useState(
toolsJsonObjects.map((value) => ({
id: value.id,
path: value.path,
shown: `${location.pathname}/`.startsWith(`/tools/${value.path}/`)
}))
)
useEffect(() => {
const temp = _.clone(multipleMenuShown)
temp.forEach((value) => {
value.shown = `${location.pathname}/`.startsWith(`/tools/${value.path}/`)
})
setMultipleMenuShown(temp)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location])
const switchSubmenu = (menuId: string) => {
return () => {
const temp = _.clone(multipleMenuShown)
const menu = temp.find(({ id }) => id === menuId)
menu && (menu.shown = !menu.shown)
setMultipleMenuShown(temp)
}
}
return ( return (
<> <>
@@ -20,92 +45,93 @@ const ToolsFramework: React.FC = () => {
<div style={{ marginTop: '0' }} className={'separate'} /> <div style={{ marginTop: '0' }} className={'separate'} />
</li> </li>
<li className={'item'}> <li className={'item'}>
<NavLink <div className={'menu-bt'}>
to={''} <NavLink
end to={''}
className={({ isActive, isPending }) => end
isPending ? 'pending' : isActive ? 'active' : '' className={({ isActive, isPending }) =>
} isPending ? 'pending' : isActive ? 'active' : ''
> }
{routeChildren ? ( >
<> <Icon
<Icon className={'icon'}
className={'icon'} component={toolsJsonObjects[0].icon}
component={ />
(routeChildren[0].handle as RouteHandle).icon <span className={'text'}>{toolsJsonObjects[0].name}</span>
} </NavLink>
/> </div>
<span className={'text'}>
{(routeChildren[0].handle as RouteHandle).name}
</span>
</>
) : (
'主页'
)}
</NavLink>
</li> </li>
<li className={'item'}> <li className={'item'}>
<NavLink <div className={'menu-bt'}>
to={'all'} <NavLink
className={({ isActive, isPending }) => to={'all'}
isPending ? ' pending' : isActive ? ' active' : '' className={({ isActive, isPending }) =>
} isPending ? ' pending' : isActive ? ' active' : ''
> }
{routeChildren ? ( >
<> <Icon
<Icon className={'icon'}
className={'icon'} component={toolsJsonObjects[1].icon}
component={ />
(routeChildren[1].handle as RouteHandle).icon <span className={'text'}>{toolsJsonObjects[1].name}</span>
} </NavLink>
/> </div>
<span className={'text'}>
{(routeChildren[1].handle as RouteHandle).name}
</span>
</>
) : (
'全部工具'
)}
</NavLink>
</li> </li>
<li> <li>
<div className={'separate'} /> <div className={'separate'} />
</li> </li>
{routeChildren?.map((route) => { {toolsJsonObjects.map((tool) => {
return (route.handle as RouteHandle).menu && return tool.menu &&
route.id !== 'tools' && tool.id !== 'tools' &&
route.id !== 'tools-all' ? ( tool.id !== 'tools-all' ? (
<li <li
className={route.children ? 'multiple-item' : 'item'} className={
key={route.id} tool.children
? `multiple-item${
multipleMenuShown.find(
({ id }) => id === tool.id
)?.shown ?? false
? ' show'
: ''
}`
: 'item'
}
key={tool.id}
> >
<NavLink <div className={'menu-bt'}>
to={route.path ?? ''} {tool.children ? (
className={({ isActive, isPending }) => <div
isPending ? 'pending' : isActive ? 'active' : '' className={'icon-box'}
} onClick={switchSubmenu(tool.id)}
> >
{(route.handle as RouteHandle).icon ? ( <Icon
<Icon className={'icon'}
className={'icon'} component={IconFatwebDown}
component={(route.handle as RouteHandle).icon} />
/> </div>
) : undefined} ) : undefined}
<span className={'text'}> <NavLink
{(route.handle as RouteHandle).name} to={tool.path}
</span> className={({ isActive, isPending }) =>
</NavLink> isPending ? 'pending' : isActive ? 'active' : ''
{route.children ? ( }
>
{tool.children ? undefined : tool.icon ? (
<Icon
className={'icon'}
component={tool.icon}
/>
) : undefined}
<span className={'text'}>{tool.name}</span>
</NavLink>
</div>
{tool.children ? (
<ul className={'submenu'}> <ul className={'submenu'}>
{route.children.map((subRoute) => { {tool.children.map((subTool) => {
return (subRoute.handle as RouteHandle).menu ? ( return subTool.menu ? (
<li className={'item'} key={subRoute.id}> <li className={'item'} key={subTool.id}>
<NavLink <NavLink
to={ to={`${tool.path}/${subTool.path}`}
(route.path ?? '') +
'/' +
(subRoute.path ?? '')
}
className={({ className={({
isActive, isActive,
isPending isPending
@@ -117,23 +143,14 @@ const ToolsFramework: React.FC = () => {
: '' : ''
} }
> >
{(subRoute.handle as RouteHandle) {subTool.icon ? (
.icon ? (
<Icon <Icon
className={'icon'} className={'icon'}
component={ component={subTool.icon}
(
subRoute.handle as RouteHandle
).icon
}
/> />
) : undefined} ) : undefined}
<span className={'text'}> <span className={'text'}>
{ {subTool.name}
(
subRoute.handle as RouteHandle
).name
}
</span> </span>
</NavLink> </NavLink>
</li> </li>

View File

@@ -1,6 +1,8 @@
import React from 'react' import React from 'react'
const toolsJsonObjects: ToolsJsonObject[] = [ const defaultTitle = '氮工具'
export const toolsJsonObjects: ToolsJsonObject[] = [
{ {
path: '', path: '',
id: 'tools', id: 'tools',
@@ -15,6 +17,7 @@ const toolsJsonObjects: ToolsJsonObject[] = [
id: 'tools-all', id: 'tools-all',
component: React.lazy(() => import('@/pages/tools')), component: React.lazy(() => import('@/pages/tools')),
name: '全部工具', name: '全部工具',
titlePostfix: ' - 全部工具',
icon: React.lazy(() => import('~icons/fatweb/logo.jsx')), icon: React.lazy(() => import('~icons/fatweb/logo.jsx')),
menu: true, menu: true,
auth: false auth: false
@@ -86,6 +89,9 @@ const tools: RouteObject[] = toolsJsonObjects.map((value) => ({
Component: value.component, Component: value.component,
handle: { handle: {
name: value.name, name: value.name,
titlePrefix: value.titlePrefix,
title: value.title ?? defaultTitle,
titlePostfix: value.titlePostfix,
icon: value.icon, icon: value.icon,
menu: value.menu, menu: value.menu,
auth: value.auth auth: value.auth
@@ -96,6 +102,9 @@ const tools: RouteObject[] = toolsJsonObjects.map((value) => ({
Component: value.component, Component: value.component,
handle: { handle: {
name: value.name, name: value.name,
titlePrefix: value.titlePrefix,
title: value.title ?? defaultTitle,
titlePostfix: value.titlePostfix,
icon: value.icon, icon: value.icon,
menu: value.menu, menu: value.menu,
auth: value.auth auth: value.auth

View File

@@ -18,14 +18,14 @@ export function setCookie(
daysToLive: number | null, daysToLive: number | null,
path: string | null path: string | null
): void { ): void {
let cookie = name + '=' + encodeURIComponent(value) let cookie = `${name}=${encodeURIComponent(value)}`
if (typeof daysToLive === 'number') { if (typeof daysToLive === 'number') {
cookie = `${cookie}; max-age=${daysToLive * 24 * 60 * 60}` cookie = `${cookie}; max-age=${daysToLive * 24 * 60 * 60}`
} }
if (typeof path === 'string') { if (typeof path === 'string') {
cookie += '; path=' + path cookie = `${cookie}; path=${path}`
} }
document.cookie = cookie document.cookie = cookie
@@ -61,7 +61,7 @@ export function getToken(): string | null {
} }
export function removeCookie(name: string): void { export function removeCookie(name: string): void {
document.cookie = name + '=; max-age=0' document.cookie = `${name}=; max-age=0`
} }
export function removeLocalStorage(name: string): void { export function removeLocalStorage(name: string): void {