Add tools menu and submenu. Add Add multiple environments. #28

Merged
FatttSnake merged 4 commits from FatttSnake into dev 2023-10-10 01:35:59 +08:00
12 changed files with 154 additions and 7 deletions

2
.env.development Normal file
View File

@@ -0,0 +1,2 @@
VITE_API_URL=http://localhost:8080
VITE_API_TOKEN_URL=${VITE_API_URL}/token

2
.env.production Normal file
View File

@@ -0,0 +1,2 @@
VITE_API_URL=http://api.fatweb.top
VITE_API_TOKEN_URL=${VITE_API_URL}/token

3
.env.testing Normal file
View File

@@ -0,0 +1,3 @@
NODE_ENV=development
VITE_API_URL=http://api-test.fatweb.top
VITE_API_TOKEN_URL=${VITE_API_URL}/token

View File

@@ -7,6 +7,7 @@
"dev": "vite", "dev": "vite",
"dev-host": "vite --host 0.0.0.0", "dev-host": "vite --host 0.0.0.0",
"build": "vite build", "build": "vite build",
"build-test": "vite build --mode testing",
"clean": "rimraf dist .eslintrc-auto-import.json auto-imports.d.ts", "clean": "rimraf dist .eslintrc-auto-import.json auto-imports.d.ts",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"format": "prettier --write src/", "format": "prettier --write src/",

View File

@@ -50,6 +50,7 @@
.item { .item {
display: inline-block; display: inline-block;
position: relative;
font-size: 1.5em; font-size: 1.5em;
transition: { transition: {
property: all; property: all;
@@ -72,9 +73,58 @@
}; };
} }
:hover { .item:hover {
transform: translateY(-5px); transform: translateY(-5px);
} }
.item:hover .submenu {
display: block;
border: {
width: 1px;
color: constants.$border-color;
style: solid;
};
animation: 0.3s;
@include mixins.unique-keyframes {
0% {
transform: translateY(-10px);
opacity: 0;
}
}
}
.submenu {
display: none;
position: absolute;
width: 100%;
text-align: center;
background-color: white;
overflow: hidden;
.item {
display: block;
font-size: 0.8em;
transition: all 0s;
a {
display: block;
padding: 8px 0;
}
}
.item:hover {
transform: none;
a {
background-color: constants.$focus-color !important;
}
}
.active {
font-weight: bold;
border: none;
}
}
} }
.dropdown-menu-button { .dropdown-menu-button {
@@ -144,7 +194,7 @@
} }
} }
@media screen and (max-width: 900px){ @media screen and (max-width: 900px) {
.dropdown-menu-content.show { .dropdown-menu-content.show {
display: block; display: block;
} }

View File

@@ -4,7 +4,7 @@ import FitFullScreen from '@/components/common/FitFullScreen'
import { MainFrameworkContext } from '@/pages/MainFramework' import { MainFrameworkContext } from '@/pages/MainFramework'
import Slogan from '@/components/home/Slogan' import Slogan from '@/components/home/Slogan'
import OxygenToolbox from '@/components/home/OxygenToolbox' import OxygenToolbox from '@/components/home/OxygenToolbox'
import Indicator from '@/components/common/Indicator.tsx' import Indicator from '@/components/common/Indicator'
import Footer from '@/components/home/Footer' import Footer from '@/components/home/Footer'
const Home: React.FC = () => { const Home: React.FC = () => {

View File

@@ -5,6 +5,7 @@ import LoadingMask from '@/components/common/LoadingMask'
import HideScrollbar, { HideScrollbarElement } from '@/components/common/HideScrollbar' import HideScrollbar, { HideScrollbarElement } from '@/components/common/HideScrollbar'
import Icon from '@ant-design/icons' import Icon from '@ant-design/icons'
import { COLOR_FONT_SECONDARY } from '@/constants/Common.constants.ts' import { COLOR_FONT_SECONDARY } from '@/constants/Common.constants.ts'
import { NavLink } from 'react-router-dom'
export const MainFrameworkContext = createContext<{ export const MainFrameworkContext = createContext<{
navbarHiddenState: { navbarHiddenState: {
@@ -90,7 +91,7 @@ const MainFramework: React.FC = () => {
<nav> <nav>
<ul className={'menu'}> <ul className={'menu'}>
{routeChildren?.map((route) => { {routeChildren?.map((route) => {
return ( return (route.handle as RouteHandle).menu ? (
<li className={'item'} key={route.id}> <li className={'item'} key={route.id}>
<NavLink <NavLink
to={route.path ?? ''} to={route.path ?? ''}
@@ -104,7 +105,50 @@ const MainFramework: React.FC = () => {
> >
{(route.handle as RouteHandle).name} {(route.handle as RouteHandle).name}
</NavLink> </NavLink>
{route.children ? (
<ul className={'submenu'}>
{route.children.map((subRoute) => {
return (subRoute.handle as RouteHandle)
.menu ? (
<li
className={'item'}
key={subRoute.id}
>
<NavLink
to={
(route.path ?? '') +
'/' +
(subRoute.path ?? '')
}
className={({
isActive,
isPending
}) =>
isPending
? 'pending'
: isActive
? 'active'
: ''
}
>
{
(
subRoute.handle as RouteHandle
).name
}
</NavLink>
</li>
) : (
<></>
)
})}
</ul>
) : (
<></>
)}
</li> </li>
) : (
<></>
) )
})} })}
</ul> </ul>

7
src/pages/Tools.tsx Normal file
View File

@@ -0,0 +1,7 @@
import React from 'react'
const Tools: React.FC = () => {
return <></>
}
export default Tools

View File

@@ -0,0 +1,7 @@
import React from 'react'
const Translation: React.FC = () => {
return <></>
}
export default Translation

View File

@@ -23,7 +23,7 @@ const routes: RouteObject[] = [
{ {
path: '', path: '',
id: 'home', id: 'home',
Component: React.lazy(() => import('@/components/home')), Component: React.lazy(() => import('@/pages/Home')),
handle: { handle: {
name: '主页', name: '主页',
menu: true, menu: true,
@@ -38,6 +38,28 @@ const routes: RouteObject[] = [
menu: true, menu: true,
auth: false auth: false
} }
},
{
path: 'tools',
id: 'tools',
Component: React.lazy(() => import('@/pages/Tools')),
children: [
{
path: 'translation',
id: 'tools-translation',
Component: React.lazy(() => import('@/pages/tools/Translation')),
handle: {
name: '翻译',
menu: true,
auth: false
}
}
],
handle: {
name: '工具',
menu: true,
auth: false
}
} }
] ]
}, },

View File

@@ -14,7 +14,7 @@ import {
import { message } from 'antd' import { message } from 'antd'
const service: AxiosInstance = axios.create({ const service: AxiosInstance = axios.create({
baseURL: 'http://localhost:8181', baseURL: import.meta.env.VITE_API_URL,
timeout: 10000, timeout: 10000,
withCredentials: false withCredentials: false
}) })
@@ -44,7 +44,7 @@ service.interceptors.request.use(
jwt.exp * 1000 - new Date().getTime() > 0 jwt.exp * 1000 - new Date().getTime() > 0
) { ) {
await axios await axios
.get('http://localhost:8181/token', { .get(import.meta.env.VITE_API_TOKEN_URL, {
headers: { token } headers: { token }
}) })
.then((value: AxiosResponse<_Response<Token>>) => { .then((value: AxiosResponse<_Response<Token>>) => {

9
src/vite-env.d.ts vendored
View File

@@ -1,5 +1,14 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_API_TOKEN_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
type RouteHandle = { type RouteHandle = {
name?: string name?: string
menu?: boolean menu?: boolean