Add tools menu and submenu. Add Add multiple environments. #28
2
.env.development
Normal file
2
.env.development
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_API_URL=http://localhost:8080
|
||||||
|
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
||||||
2
.env.production
Normal file
2
.env.production
Normal 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
3
.env.testing
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NODE_ENV=development
|
||||||
|
VITE_API_URL=http://api-test.fatweb.top
|
||||||
|
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
||||||
@@ -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/",
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 = () => {
|
||||||
@@ -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>
|
</li>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
7
src/pages/Tools.tsx
Normal file
7
src/pages/Tools.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Tools: React.FC = () => {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tools
|
||||||
7
src/pages/tools/Translation.tsx
Normal file
7
src/pages/tools/Translation.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Translation: React.FC = () => {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Translation
|
||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
9
src/vite-env.d.ts
vendored
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user