diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..3bf4366 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,46 @@ +name: Deploy React App + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + cache-dependency-path: 'src/frontend/pnpm-lock.yaml' + + - name: Install dependencies + working-directory: src/frontend + run: pnpm install + + - name: Build + working-directory: src/frontend + run: pnpm run build + + - name: Copy 404.html + working-directory: src/frontend + run: cp build/index.html build/404.html + + - name: Deploy to GitHub Pages + working-directory: src/frontend + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.name "github-actions" + git config --global user.email "github-actions@users.noreply.github.com" + npx gh-pages -d build -u "github-actions " --repo "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" + # 大抵是没有问题了 diff --git a/.idea/AI-powered-switches.iml b/.idea/AI-powered-switches.iml index 7801d9e..b7e68da 100644 --- a/.idea/AI-powered-switches.iml +++ b/.idea/AI-powered-switches.iml @@ -2,7 +2,7 @@ - + @@ -11,6 +11,5 @@ - \ No newline at end of file diff --git a/README.md b/README.md index dd8d7c9..bfae63b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ - Framer-motion - chakra-ui - HTML5 - ### 项目分工 - **后端api,人工智能算法** : `3`(主要) & `log_out` & `Jerry`(maybe) 使用python - **前端管理后台设计**:`Jerry`使用react @@ -19,7 +18,9 @@ ### 各部分说明 -[网页管理前端](https://github.com/Jerryplusy/AI-powered-switches/blob/main/src/frontend/README.md) +[网页管理前端如下](https://github.com/Jerryplusy/AI-powered-switches/blob/main/src/frontend/README.md) + +![img](./src/frontend/src/resources/img.png) [逻辑处理后端](https://github.com/Jerryplusy/AI-powered-switches/blob/main/src/backend/README.md) diff --git a/src/frontend/README.md b/src/frontend/README.md index 8cf5f11..8964e91 100644 --- a/src/frontend/README.md +++ b/src/frontend/README.md @@ -1,10 +1,27 @@ # Network Admin Web UI -基于 React 和 Chakra UI +### 基于 `React` 和 `Chakra UI` -- 前端框架:React -- UI 组件库:Chakra UI -- 网络图表:Recharts -- 状态管理:React hooks -- 数据通信:Axios -- 页面路由:React Router \ No newline at end of file +#### 架构 +- 前端框架:`React` +- UI 组件库:`Chakra UI` +- 网络图表:`Recharts` +- 状态管理:`React hooks` +- 数据通信:`Axios` +- 页面路由:`React Router` + +#### 部署方法 +- `clone`项目 +- 导航到`/src/frontend/`目录 +- 使用`pnpm`管理软件包:`npm install pnpm -g` +- 安装依赖:`pnpm install` +- 执行`pnpm build`进行`react`服务端构建 +- 运行`pnpm start`启动服务端 +- 服务运行在本地`3000`端口 + +#### 功能(后端) +- []监控网络流量 +- []扫描环境中的交换机 +- []自然语言解析命令 +- []下发配置到交换机 +- []流量预测 \ No newline at end of file diff --git a/src/frontend/package.json b/src/frontend/package.json index 6b16ca8..e31efd3 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -2,6 +2,7 @@ "name": "network-admin-frontend", "version": "0.1.0", "private": false, + "homepage": "https://JerryPlsuy.github.io/AI-powered-switches", "dependencies": { "@chakra-ui/react": "^3.19.1", "@emotion/react": "^11.14.0", @@ -28,6 +29,8 @@ "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-scripts eject", + "predeploy": "pnpm run build", + "deploy": "gh-pages -d build", "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"" }, "eslintConfig": { @@ -54,6 +57,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^4.3.0", + "gh-pages": "^6.3.0", "prettier": "^3.5.3", "react-app-rewired": "^2.2.1" } diff --git a/src/frontend/pnpm-lock.yaml b/src/frontend/pnpm-lock.yaml index 635456d..a970902 100644 --- a/src/frontend/pnpm-lock.yaml +++ b/src/frontend/pnpm-lock.yaml @@ -81,6 +81,9 @@ importers: eslint-plugin-react-hooks: specifier: ^4.3.0 version: 4.6.2(eslint@8.57.0) + gh-pages: + specifier: ^6.3.0 + version: 6.3.0 prettier: specifier: ^3.5.3 version: 3.5.3 @@ -2277,6 +2280,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2757,6 +2764,9 @@ packages: electron-to-chromium@1.5.159: resolution: {integrity: sha512-CEvHptWAMV5p6GJ0Lq8aheyvVbfzVrv5mmidu1D3pidoVNkB3tTBsTMVtPJ+rzRK5oV229mCLz9Zj/hNvU8GBA==} + email-addresses@5.0.0: + resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} + emittery@0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} engines: {node: '>=12'} @@ -3108,6 +3118,14 @@ packages: filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + filename-reserved-regex@2.0.0: + resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} + engines: {node: '>=4'} + + filenamify@4.3.0: + resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==} + engines: {node: '>=8'} + filesize@8.0.7: resolution: {integrity: sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==} engines: {node: '>= 0.4.0'} @@ -3214,6 +3232,10 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} + fs-extra@11.3.0: + resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} + engines: {node: '>=14.14'} + fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -3270,6 +3292,11 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + gh-pages@6.3.0: + resolution: {integrity: sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==} + engines: {node: '>=10'} + hasBin: true + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -5547,6 +5574,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-outer@1.0.1: + resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} + engines: {node: '>=0.10.0'} + style-loader@3.3.4: resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} engines: {node: '>= 12.13.0'} @@ -5705,6 +5736,10 @@ packages: resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} engines: {node: '>=8'} + trim-repeated@1.0.0: + resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} + engines: {node: '>=0.10.0'} + tryer@1.0.1: resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} @@ -9156,6 +9191,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@13.1.0: {} + commander@2.20.3: {} commander@4.1.1: {} @@ -9609,6 +9646,8 @@ snapshots: electron-to-chromium@1.5.159: {} + email-addresses@5.0.0: {} + emittery@0.10.2: {} emittery@0.8.1: {} @@ -10135,6 +10174,14 @@ snapshots: dependencies: minimatch: 5.1.6 + filename-reserved-regex@2.0.0: {} + + filenamify@4.3.0: + dependencies: + filename-reserved-regex: 2.0.0 + strip-outer: 1.0.1 + trim-repeated: 1.0.0 + filesize@8.0.7: {} fill-range@7.1.1: @@ -10250,6 +10297,12 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 + fs-extra@11.3.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -10311,6 +10364,16 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + gh-pages@6.3.0: + dependencies: + async: 3.2.6 + commander: 13.1.0 + email-addresses: 5.0.0 + filenamify: 4.3.0 + find-cache-dir: 3.3.2 + fs-extra: 11.3.0 + globby: 11.1.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -12976,6 +13039,10 @@ snapshots: strip-json-comments@3.1.1: {} + strip-outer@1.0.1: + dependencies: + escape-string-regexp: 1.0.5 + style-loader@3.3.4(webpack@5.99.9): dependencies: webpack: 5.99.9 @@ -13169,6 +13236,10 @@ snapshots: dependencies: punycode: 2.3.1 + trim-repeated@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + tryer@1.0.1: {} ts-interface-checker@0.1.13: {} diff --git a/src/frontend/public/index.html b/src/frontend/public/index.html index 8428f1b..be441da 100644 --- a/src/frontend/public/index.html +++ b/src/frontend/public/index.html @@ -8,4 +8,4 @@
- + \ No newline at end of file diff --git a/src/frontend/src/App.jsx b/src/frontend/src/App.jsx index 5322ded..5140139 100644 --- a/src/frontend/src/App.jsx +++ b/src/frontend/src/App.jsx @@ -1,14 +1,18 @@ -import { Route, Routes } from 'react-router-dom'; -import Welcome from '@/pages/Welcome'; -import Dashboard from '@/pages/Dashboard'; +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import AppShell from '@/components/system/layout/AppShell'; +import buildRoutes from '@/constants/routes/routes'; const App = () => { + const isProd = process.env.NODE_ENV === 'production'; return ( - - } /> - } /> - + + + }> + {buildRoutes()} + + + ); }; -export default App; \ No newline at end of file +export default App; diff --git a/src/frontend/src/components/CommandInput.jsx b/src/frontend/src/components/CommandInput.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/frontend/src/components/ConfigForm.jsx b/src/frontend/src/components/ConfigForm.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/frontend/src/components/Header.jsx b/src/frontend/src/components/Header.jsx deleted file mode 100644 index 985db0c..0000000 --- a/src/frontend/src/components/Header.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { Box, Flex, Heading, Spacer, Button, Card } from '@chakra-ui/react'; -import { useNavigate } from 'react-router-dom'; -import NavButton from '@/components/ui/NavButton'; - -const Header = () => { - const navigate = useNavigate(); - return ( - - - 网络管理后台 - - navigate('/')}> - 返回欢迎页 - - - - ); -}; - -export default Header; diff --git a/src/frontend/src/components/NetworkStatusChart.jsx b/src/frontend/src/components/NetworkStatusChart.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/frontend/src/components/pages/welcome/BackgroundBlur.jsx b/src/frontend/src/components/pages/welcome/BackgroundBlur.jsx new file mode 100644 index 0000000..e2771ad --- /dev/null +++ b/src/frontend/src/components/pages/welcome/BackgroundBlur.jsx @@ -0,0 +1,29 @@ +import { Box, Image } from '@chakra-ui/react'; +import { motion } from 'framer-motion'; +import image from '@/resources/welcome/image/background.png'; + +const MotionBox = motion(Box); + +/** + * 带高斯模糊的背景 + * @returns {JSX.Element} + * @constructor + */ +const BackgroundBlur = () => ( + + + +); + +export default BackgroundBlur; diff --git a/src/frontend/src/components/pages/welcome/DashboardCard.jsx b/src/frontend/src/components/pages/welcome/DashboardCard.jsx new file mode 100644 index 0000000..921e4e6 --- /dev/null +++ b/src/frontend/src/components/pages/welcome/DashboardCard.jsx @@ -0,0 +1,20 @@ +import manageIcon from '@/resources/welcome/image/setting.svg'; +import MotionCard from '@/components/ui/MotionCard'; +import { useNavigate } from 'react-router-dom'; +import FadeInWrapper from '@/components/system/layout/FadeInWrapper'; + +/** + * 进入管理后台按钮组件 + * @returns {JSX.Element} + * @constructor + */ +const DashboardCard = () => { + const navigate = useNavigate(); + return ( + + navigate('/dashboard')} /> + + ); +}; + +export default DashboardCard; diff --git a/src/frontend/src/components/pages/welcome/GithubCard.jsx b/src/frontend/src/components/pages/welcome/GithubCard.jsx new file mode 100644 index 0000000..863cb77 --- /dev/null +++ b/src/frontend/src/components/pages/welcome/GithubCard.jsx @@ -0,0 +1,23 @@ +import githubIcon from '@/resources/welcome/image/github.svg'; +import MotionCard from '@/components/ui/MotionCard'; +import FadeInWrapper from '@/components/system/layout/FadeInWrapper'; + +/** + * GitHub按钮组件 + * @returns {JSX.Element} + * @constructor + */ +const GithubCard = () => { + return ( + + window.open('https://github.com/Jerryplusy/AI-powered-switches', '_blank')} + /> + + ); +}; + +export default GithubCard; diff --git a/src/frontend/src/components/pages/welcome/WelcomeContent.jsx b/src/frontend/src/components/pages/welcome/WelcomeContent.jsx new file mode 100644 index 0000000..b350722 --- /dev/null +++ b/src/frontend/src/components/pages/welcome/WelcomeContent.jsx @@ -0,0 +1,34 @@ +import { Box, Heading, Text, VStack } from '@chakra-ui/react'; +import DashboardCard from '@/components/pages/welcome/DashboardCard'; +import FadeInWrapper from '@/components/system/layout/FadeInWrapper'; + +/** + * 欢迎文字 + * @returns {JSX.Element} + * @constructor + */ +const WelcomeContent = () => { + return ( + + + + + 智能网络交换机 +
+ 管理系统 +
+
+
+ + + + 助力大型网络交换机配置及网络流量管理,方便的管控网络,让网络配置不再困难 + + + + +
+ ); +}; + +export default WelcomeContent; diff --git a/src/frontend/src/components/system/PageContainer.jsx b/src/frontend/src/components/system/PageContainer.jsx new file mode 100644 index 0000000..857c974 --- /dev/null +++ b/src/frontend/src/components/system/PageContainer.jsx @@ -0,0 +1,17 @@ +import { Box } from '@chakra-ui/react'; + +/** + * 解决导航栏占位问题 + * @param children + * @returns {JSX.Element} + * @constructor + */ +const PageContainer = ({ children }) => { + return ( + + {children} + + ); +}; + +export default PageContainer; diff --git a/src/frontend/src/components/system/layout/AppShell.jsx b/src/frontend/src/components/system/layout/AppShell.jsx new file mode 100644 index 0000000..cd50313 --- /dev/null +++ b/src/frontend/src/components/system/layout/AppShell.jsx @@ -0,0 +1,28 @@ +import { Outlet, useLocation } from 'react-router-dom'; +import { Box } from '@chakra-ui/react'; +import { AnimatePresence } from 'framer-motion'; +import PageTransition from './PageTransition'; +import GithubTransitionCard from '@/components/system/layout/github/GithubTransitionCard'; + +/** + * 应用加壳 + * @returns {JSX.Element} + * @constructor + */ +const AppShell = () => { + const location = useLocation(); + return ( + + + + + + + + + + + ); +}; + +export default AppShell; diff --git a/src/frontend/src/components/system/layout/FadeInWrapper.jsx b/src/frontend/src/components/system/layout/FadeInWrapper.jsx new file mode 100644 index 0000000..a568801 --- /dev/null +++ b/src/frontend/src/components/system/layout/FadeInWrapper.jsx @@ -0,0 +1,43 @@ +import { AnimatePresence, motion } from 'framer-motion'; + +/** + * 组件进入 / 离开时的淡入淡出动画 + * @param children 子组件 + * @param delay 延迟 + * @param yOffset y轴偏移量 + * @param duration 动画时间 + * @param className 类名 + * @param props + * @returns {JSX.Element} + * @constructor + */ +const FadeInWrapper = ({ + children, + delay = 0, + yOffset = 10, + duration = 0.6, + className = '', + ...props +}) => { + return ( + + + {children} + + + ); +}; + +export default FadeInWrapper; diff --git a/src/frontend/src/components/system/layout/PageTransition.jsx b/src/frontend/src/components/system/layout/PageTransition.jsx new file mode 100644 index 0000000..e6eaf0f --- /dev/null +++ b/src/frontend/src/components/system/layout/PageTransition.jsx @@ -0,0 +1,16 @@ +import { motion } from 'framer-motion'; + +/** + * 页面动效 + * @param children + * @returns {JSX.Element} + * @constructor + */ +const PageTransition = ({ children }) => {children}; + +export default PageTransition; +/** + * initial={{ opacity: 0, y: 0 }} + * animate={{ opacity: 1, y: 0 }} + * transition={{ duration: 0.2 }} + */ diff --git a/src/frontend/src/components/system/layout/StaggeredFadeIn.jsx b/src/frontend/src/components/system/layout/StaggeredFadeIn.jsx new file mode 100644 index 0000000..9462c71 --- /dev/null +++ b/src/frontend/src/components/system/layout/StaggeredFadeIn.jsx @@ -0,0 +1,24 @@ +import FadeInWrapper from './FadeInWrapper'; + +/** + * 递归为组件及子组件添加载入动效 + * @param children 子组件 + * @param baseDelay 延迟 + * @param increment 增值 + * @param className 类名 + * @returns {JSX.Element} + * @constructor + */ +const StaggeredFadeIn = ({ children, baseDelay = 0.2, increment = 0.1, className = '' }) => { + return ( + <> + {React.Children.map(children, (child, index) => ( + + {child} + + ))} + + ); +}; + +export default StaggeredFadeIn; diff --git a/src/frontend/src/components/system/layout/github/GithubTransitionCard.jsx b/src/frontend/src/components/system/layout/github/GithubTransitionCard.jsx new file mode 100644 index 0000000..fd18391 --- /dev/null +++ b/src/frontend/src/components/system/layout/github/GithubTransitionCard.jsx @@ -0,0 +1,103 @@ +import { useEffect, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { AnimatePresence, motion } from 'framer-motion'; +import { Button, HStack } from '@chakra-ui/react'; +import web from '@/resources/icon/web.svg'; +import githubIcon from '@/resources/welcome/image/github.svg'; +import FadeInWrapper from '@/components/system/layout/FadeInWrapper'; +import MotionCard from '@/components/ui/MotionCard'; + +const navItems = [ + { label: '面板', path: '/dashboard' }, + { label: '网络', path: '/dashboard/network' }, + { label: '交换机', path: '/dashboard/switch' }, +]; + +/** + * 导航栏&github按钮组件 + * @returns {JSX.Element} + * @constructor + */ +const GithubTransitionCard = () => { + const { pathname } = useLocation(); + const navigate = useNavigate(); + const isDashboard = pathname.startsWith('/dashboard'); + const [showNavButtons, setShowNavButtons] = useState(false); + + useEffect(() => { + setShowNavButtons(false); + const timer = setTimeout(() => { + if (isDashboard) setShowNavButtons(true); + }, 400); + return () => clearTimeout(timer); + }, [isDashboard]); + + return ( + + + + { + if (!isDashboard) { + window.open('https://github.com/Jerryplusy/AI-powered-switches', '_blank'); + } + }} + justifyContent={isDashboard ? 'flex-start' : 'center'} + alignItems={'center'} + flexDirection={'row'} + w={'100%'} + px={isDashboard ? 4 : 3} + py={isDashboard ? 3 : 2} + noHover={isDashboard} + > + {isDashboard && showNavButtons && ( + + {navItems.map((item) => ( + + ))} + + )} + + + + + ); +}; + +export default GithubTransitionCard; diff --git a/src/frontend/src/components/system/pages/DashboardBackground.jsx b/src/frontend/src/components/system/pages/DashboardBackground.jsx new file mode 100644 index 0000000..4a3f6e8 --- /dev/null +++ b/src/frontend/src/components/system/pages/DashboardBackground.jsx @@ -0,0 +1,61 @@ +import React, { useState, useEffect } from 'react'; +import { Box } from '@chakra-ui/react'; +import { motion } from 'framer-motion'; + +const MotionBox = motion(Box); + +/** + * 控制台背景 + * @returns {JSX.Element} + * @constructor + */ +const DashboardBackground = () => { + const [mousePos, setMousePos] = useState({ x: 0, y: 0 }); + + useEffect(() => { + const handleMouseMove = (e) => { + setMousePos({ x: e.clientX, y: e.clientY }); + }; + window.addEventListener('mousemove', handleMouseMove); + return () => window.removeEventListener('mousemove', handleMouseMove); + }, []); + + const spotlight = { + background: `radial-gradient( + circle at ${mousePos.x}px ${mousePos.y}px, + rgba(255, 255, 255, 0.05) 0%, + rgba(255, 255, 255, 0.02) 120px, + transparent 240px + )`, + }; + + return ( + + ); +}; + +export default DashboardBackground; diff --git a/src/frontend/src/components/system/pages/DocumentTitle.jsx b/src/frontend/src/components/system/pages/DocumentTitle.jsx new file mode 100644 index 0000000..0c68a6b --- /dev/null +++ b/src/frontend/src/components/system/pages/DocumentTitle.jsx @@ -0,0 +1,10 @@ +import { useEffect } from 'react'; + +const DocumentTitle = ({ title, children }) => { + useEffect(() => { + document.title = title || '网络管理后台'; + }, [title]); + return children; +}; + +export default DocumentTitle; diff --git a/src/frontend/src/components/ui/Card.jsx b/src/frontend/src/components/ui/Card.jsx index 13991aa..ee4f534 100644 --- a/src/frontend/src/components/ui/Card.jsx +++ b/src/frontend/src/components/ui/Card.jsx @@ -1,5 +1,12 @@ import { Box } from '@chakra-ui/react'; +/** + * 卡片组件 + * @param children + * @param props + * @returns {JSX.Element} + * @constructor + */ const Card = ({ children, ...props }) => ( ( + + {hasBlurBackground && ( + + )} + {icon && } + {text && ( + + {text} + + )} + {children} + +); + +export default MotionCard; diff --git a/src/frontend/src/components/ui/color-mode.jsx b/src/frontend/src/components/ui/color-mode.jsx index b2d1cd6..47ba63d 100644 --- a/src/frontend/src/components/ui/color-mode.jsx +++ b/src/frontend/src/components/ui/color-mode.jsx @@ -1,90 +1,86 @@ -'use client' +'use client'; -import { ClientOnly, IconButton, Skeleton, Span } from '@chakra-ui/react' -import { ThemeProvider, useTheme } from 'next-themes' +import { ClientOnly, IconButton, Skeleton, Span } from '@chakra-ui/react'; +import { ThemeProvider, useTheme } from 'next-themes'; -import * as React from 'react' -import { LuMoon, LuSun } from 'react-icons/lu' +import * as React from 'react'; +import { LuMoon, LuSun } from 'react-icons/lu'; export function ColorModeProvider(props) { - return ( - - ) + return ; } export function useColorMode() { - const { resolvedTheme, setTheme, forcedTheme } = useTheme() - const colorMode = forcedTheme || resolvedTheme + const { resolvedTheme, setTheme, forcedTheme } = useTheme(); + const colorMode = forcedTheme || resolvedTheme; const toggleColorMode = () => { - setTheme(resolvedTheme === 'dark' ? 'light' : 'dark') - } + setTheme(resolvedTheme === 'dark' ? 'light' : 'dark'); + }; return { colorMode: colorMode, setColorMode: setTheme, toggleColorMode, - } + }; } export function useColorModeValue(light, dark) { - const { colorMode } = useColorMode() - return colorMode === 'dark' ? dark : light + const { colorMode } = useColorMode(); + return colorMode === 'dark' ? dark : light; } export function ColorModeIcon() { - const { colorMode } = useColorMode() - return colorMode === 'dark' ? : + const { colorMode } = useColorMode(); + return colorMode === 'dark' ? : ; } -export const ColorModeButton = React.forwardRef( - function ColorModeButton(props, ref) { - const { toggleColorMode } = useColorMode() - return ( - }> - - - - - ) - }, -) +export const ColorModeButton = React.forwardRef(function ColorModeButton(props, ref) { + const { toggleColorMode } = useColorMode(); + return ( + }> + + + + + ); +}); export const LightMode = React.forwardRef(function LightMode(props, ref) { return ( - ) -}) + ); +}); export const DarkMode = React.forwardRef(function DarkMode(props, ref) { return ( - ) -}) + ); +}); diff --git a/src/frontend/src/constants/keep b/src/frontend/src/constants/keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/frontend/src/constants/routes/routes.jsx b/src/frontend/src/constants/routes/routes.jsx new file mode 100644 index 0000000..c479820 --- /dev/null +++ b/src/frontend/src/constants/routes/routes.jsx @@ -0,0 +1,17 @@ +import { Route } from 'react-router-dom'; +import Welcome from '@/pages/Welcome'; +import Dashboard from '@/pages/Dashboard'; + +/** + * 路由 + * @type {[{path: string, element: JSX.Element},{path: string, element: JSX.Element}]} + */ +const routeList = [ + { path: '/', element: }, + { path: '/dashboard', element: }, +]; + +const buildRoutes = () => + routeList.map(({ path, element }) => ); + +export default buildRoutes; diff --git a/src/frontend/src/index.js b/src/frontend/src/index.js index aca27d5..d021c8d 100644 --- a/src/frontend/src/index.js +++ b/src/frontend/src/index.js @@ -1,14 +1,11 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from '@/App'; -import { BrowserRouter } from 'react-router-dom'; import { Provider } from '@/components/ui/provider'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - - - + ); diff --git a/src/frontend/src/pages/Dashboard.jsx b/src/frontend/src/pages/Dashboard.jsx index 3a27f2d..964dd61 100644 --- a/src/frontend/src/pages/Dashboard.jsx +++ b/src/frontend/src/pages/Dashboard.jsx @@ -1,15 +1,22 @@ import React from 'react'; import { Box, Text } from '@chakra-ui/react'; -import Header from '../components/Header'; +import DocumentTitle from '@/components/system/pages/DocumentTitle'; +import PageContainer from '@/components/system/PageContainer'; +import DashboardBackground from '@/components/system/pages/DashboardBackground'; +import FadeInWrapper from '@/components/system/layout/FadeInWrapper'; const Dashboard = () => { return ( - <> -
- - 控制台奇怪的功能+1 - - + + + + + + 控制台奇怪的功能+1 + + + + ); }; diff --git a/src/frontend/src/pages/Welcome.jsx b/src/frontend/src/pages/Welcome.jsx index 7b448fe..826e498 100644 --- a/src/frontend/src/pages/Welcome.jsx +++ b/src/frontend/src/pages/Welcome.jsx @@ -1,19 +1,22 @@ -import React from 'react'; -import { Box, Button, Heading, VStack } from '@chakra-ui/react'; -import { useNavigate } from 'react-router-dom'; +import { Box } from '@chakra-ui/react'; +import BackgroundBlur from '@/components/pages/welcome/BackgroundBlur'; +import WelcomeContent from '@/components/pages/welcome/WelcomeContent'; +/** + * 欢迎页 + * @returns {JSX.Element} + * @constructor + */ const Welcome = () => { - const navigate = useNavigate(); return ( - - - - 欢迎使用交换机管理后台 - - - + + + + {/**/} + + + + ); }; diff --git a/src/frontend/src/resources/icon/web.svg b/src/frontend/src/resources/icon/web.svg new file mode 100644 index 0000000..817a917 --- /dev/null +++ b/src/frontend/src/resources/icon/web.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/frontend/src/resources/img.png b/src/frontend/src/resources/img.png new file mode 100644 index 0000000..f9ab032 Binary files /dev/null and b/src/frontend/src/resources/img.png differ diff --git a/src/frontend/src/resources/keep b/src/frontend/src/resources/keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/frontend/src/resources/welcome/image/background.png b/src/frontend/src/resources/welcome/image/background.png new file mode 100644 index 0000000..4e38ccb Binary files /dev/null and b/src/frontend/src/resources/welcome/image/background.png differ diff --git a/src/frontend/src/resources/welcome/image/github.svg b/src/frontend/src/resources/welcome/image/github.svg new file mode 100644 index 0000000..b6a597e --- /dev/null +++ b/src/frontend/src/resources/welcome/image/github.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/frontend/src/resources/welcome/image/setting.svg b/src/frontend/src/resources/welcome/image/setting.svg new file mode 100644 index 0000000..3128ef0 --- /dev/null +++ b/src/frontend/src/resources/welcome/image/setting.svg @@ -0,0 +1,3 @@ + + +