mirror of
https://github.com/GSManagerXZ/GameServerManager.git
synced 2026-05-23 03:40:54 +08:00
增加过度动画
This commit is contained in:
49
client/package-lock.json
generated
49
client/package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"antd": "^5.15.0",
|
||||
"axios": "^1.6.8",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^11.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lucide-react": "^0.363.0",
|
||||
"monaco-editor": "^0.47.0",
|
||||
@@ -3213,6 +3214,33 @@
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "11.18.2",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
|
||||
"integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^11.18.1",
|
||||
"motion-utils": "^11.18.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@@ -3970,6 +3998,21 @@
|
||||
"integrity": "sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "11.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
|
||||
"integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^11.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "11.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
|
||||
"integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -5788,6 +5831,12 @@
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"antd": "^5.15.0",
|
||||
"axios": "^1.6.8",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^11.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lucide-react": "^0.363.0",
|
||||
"monaco-editor": "^0.47.0",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ConfigProvider, theme } from 'antd'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
import { useThemeStore } from '@/stores/themeStore'
|
||||
import Layout from '@/components/Layout'
|
||||
import PageTransition from '@/components/PageTransition'
|
||||
import LoginPage from '@/pages/LoginPage'
|
||||
import HomePage from '@/pages/HomePage'
|
||||
import TerminalPage from '@/pages/TerminalPage'
|
||||
@@ -97,14 +98,14 @@ function App() {
|
||||
<ProtectedRoute>
|
||||
<Layout>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/terminal" element={<TerminalPage />} />
|
||||
<Route path="/instances" element={<InstanceManagerPage />} />
|
||||
<Route path="/game-deployment" element={<GameDeploymentPage />} />
|
||||
<Route path="/scheduled-tasks" element={<ScheduledTasksPage />} />
|
||||
<Route path="/files" element={<FileManagerPage />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
<Route path="/about" element={<AboutProjectPage />} />
|
||||
<Route path="/" element={<PageTransition><HomePage /></PageTransition>} />
|
||||
<Route path="/terminal" element={<PageTransition><TerminalPage /></PageTransition>} />
|
||||
<Route path="/instances" element={<PageTransition><InstanceManagerPage /></PageTransition>} />
|
||||
<Route path="/game-deployment" element={<PageTransition><GameDeploymentPage /></PageTransition>} />
|
||||
<Route path="/scheduled-tasks" element={<PageTransition><ScheduledTasksPage /></PageTransition>} />
|
||||
<Route path="/files" element={<PageTransition><FileManagerPage /></PageTransition>} />
|
||||
<Route path="/settings" element={<PageTransition><SettingsPage /></PageTransition>} />
|
||||
<Route path="/about" element={<PageTransition><AboutProjectPage /></PageTransition>} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
|
||||
@@ -78,7 +78,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
</div>
|
||||
|
||||
{/* 导航菜单 */}
|
||||
<nav className="flex-1 px-4 py-6 space-y-2">
|
||||
<nav className="flex-1 px-3 py-5 space-y-1.5">
|
||||
{navigation.map((item) => {
|
||||
const isActive = location.pathname === item.href
|
||||
return (
|
||||
@@ -87,14 +87,15 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
to={item.href}
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
className={`
|
||||
flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200
|
||||
flex items-center space-x-2.5 px-3 py-2.5 rounded-lg transition-all duration-300 ease-in-out transform
|
||||
hover:scale-105 hover:shadow-md active:scale-95
|
||||
${isActive
|
||||
? 'bg-blue-600/20 text-blue-600 dark:text-blue-400 border border-blue-500/30 shadow-lg'
|
||||
? 'bg-blue-600/20 text-blue-600 dark:text-blue-400 border border-blue-500/30 shadow-lg scale-105'
|
||||
: 'text-black dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 hover:text-black dark:hover:text-white'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<item.icon className="w-5 h-5" />
|
||||
<item.icon className="w-4 h-4" />
|
||||
<span className="font-medium">{item.name}</span>
|
||||
</Link>
|
||||
)
|
||||
@@ -106,12 +107,12 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
{/* 主题切换 */}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="flex items-center space-x-3 w-full px-4 py-2 text-black dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 hover:text-black dark:hover:text-white rounded-lg transition-all duration-200"
|
||||
className="flex items-center space-x-2.5 w-full px-3 py-2.5 text-black dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 hover:text-black dark:hover:text-white rounded-lg transition-all duration-300 ease-in-out transform hover:scale-105 hover:shadow-md active:scale-95"
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<Sun className="w-5 h-5" />
|
||||
<Sun className="w-4 h-4" />
|
||||
) : (
|
||||
<Moon className="w-5 h-5" />
|
||||
<Moon className="w-4 h-4" />
|
||||
)}
|
||||
<span className="font-medium">
|
||||
{theme === 'dark' ? '浅色模式' : '深色模式'}
|
||||
@@ -119,9 +120,9 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
</button>
|
||||
|
||||
{/* 用户信息 */}
|
||||
<div className="flex items-center space-x-3 px-4 py-2 bg-gray-100 dark:bg-white/5 rounded-lg">
|
||||
<div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<User className="w-4 h-4 text-white" />
|
||||
<div className="flex items-center space-x-2.5 px-3 py-2.5 bg-gray-100 dark:bg-white/5 rounded-lg transition-all duration-300 ease-in-out transform hover:scale-105 hover:shadow-md">
|
||||
<div className="w-7 h-7 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<User className="w-3.5 h-3.5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-black dark:text-white truncate">
|
||||
@@ -136,9 +137,9 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
{/* 登出按钮 */}
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center space-x-3 w-full px-4 py-2 text-red-400 hover:bg-red-500/10 hover:text-red-300 rounded-lg transition-all duration-200"
|
||||
className="flex items-center space-x-2.5 w-full px-3 py-2.5 text-red-400 hover:bg-red-500/10 hover:text-red-300 rounded-lg transition-all duration-300 ease-in-out transform hover:scale-105 hover:shadow-md active:scale-95"
|
||||
>
|
||||
<LogOut className="w-5 h-5" />
|
||||
<LogOut className="w-4 h-4" />
|
||||
<span className="font-medium">登出</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -172,7 +173,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
</div>
|
||||
|
||||
{/* 页面内容 */}
|
||||
<main className="p-6">
|
||||
<main className="p-6 relative overflow-hidden">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
54
client/src/components/PageTransition.tsx
Normal file
54
client/src/components/PageTransition.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import React from 'react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
interface PageTransitionProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const pageVariants = {
|
||||
initial: {
|
||||
opacity: 0,
|
||||
y: 20,
|
||||
scale: 0.95
|
||||
},
|
||||
in: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
scale: 1
|
||||
},
|
||||
out: {
|
||||
opacity: 0,
|
||||
y: -20,
|
||||
scale: 0.95
|
||||
}
|
||||
}
|
||||
|
||||
const pageTransition = {
|
||||
type: 'tween',
|
||||
ease: [0.25, 0.46, 0.45, 0.94],
|
||||
duration: 0.3
|
||||
}
|
||||
|
||||
const PageTransition: React.FC<PageTransitionProps> = ({ children }) => {
|
||||
const location = useLocation()
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
<motion.div
|
||||
key={location.pathname}
|
||||
initial="initial"
|
||||
animate="in"
|
||||
exit="out"
|
||||
variants={pageVariants}
|
||||
transition={pageTransition}
|
||||
className="w-full h-full min-h-0"
|
||||
style={{ position: 'relative' }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
export default PageTransition
|
||||
Reference in New Issue
Block a user