diff --git a/vite-frontend/public/images/bg-dark.jpg b/vite-frontend/public/images/bg-dark.jpg new file mode 100644 index 0000000..e152a84 Binary files /dev/null and b/vite-frontend/public/images/bg-dark.jpg differ diff --git a/vite-frontend/public/images/bg-light.jpg b/vite-frontend/public/images/bg-light.jpg new file mode 100644 index 0000000..3970aa5 Binary files /dev/null and b/vite-frontend/public/images/bg-light.jpg differ diff --git a/vite-frontend/src/components/animated-page.tsx b/vite-frontend/src/components/animated-page.tsx index 583a730..be9f7f3 100644 --- a/vite-frontend/src/components/animated-page.tsx +++ b/vite-frontend/src/components/animated-page.tsx @@ -13,10 +13,10 @@ export const AnimatedPage = ({ className?: string; }) => ( {children} @@ -28,9 +28,8 @@ export const AnimatedPage = ({ * Children should use `staggerItem` as their `variants` prop. */ export const staggerContainer = { - hidden: { opacity: 0 }, + hidden: {}, show: { - opacity: 1, transition: { staggerChildren: 0.05, delayChildren: 0.05, @@ -42,9 +41,8 @@ export const staggerContainer = { * Individual stagger item variant — fade-in + slide-up. */ export const staggerItem = { - hidden: { opacity: 0, y: 12 }, + hidden: { y: 12 }, show: { - opacity: 1, y: 0, transition: { duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] }, }, diff --git a/vite-frontend/src/layouts/admin.tsx b/vite-frontend/src/layouts/admin.tsx index 75fed00..387fed8 100644 --- a/vite-frontend/src/layouts/admin.tsx +++ b/vite-frontend/src/layouts/admin.tsx @@ -47,7 +47,7 @@ export default function AdminLayout({ const [isCollapsed, setIsCollapsed] = useState( () => localStorage.getItem("sidebar_collapsed") === "true", ); - const [isAdmin, setIsAdmin] = useState(false); + const [isAdmin, setIsAdmin] = useState(() => getAdminFlag()); const [monitorAllowed, setMonitorAllowed] = useState(null); const [monitorAccessReason, setMonitorAccessReason] = useState( null, @@ -433,7 +433,7 @@ export default function AdminLayout({ > {isActive && ( getAdminFlag()); const [monitorAllowed, setMonitorAllowed] = useState(null); const [monitorAccessReason, setMonitorAccessReason] = useState( null, diff --git a/vite-frontend/src/pages/group.tsx b/vite-frontend/src/pages/group.tsx index 112ffa2..53cd00c 100644 --- a/vite-frontend/src/pages/group.tsx +++ b/vite-frontend/src/pages/group.tsx @@ -1,6 +1,8 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import toast from "react-hot-toast"; +import { AnimatedPage } from "@/components/animated-page"; +import { PageLoadingState } from "@/components/page-state"; import { Card, CardBody, CardHeader } from "@/shadcn-bridge/heroui/card"; import { Button } from "@/shadcn-bridge/heroui/button"; import { Input } from "@/shadcn-bridge/heroui/input"; @@ -22,7 +24,7 @@ import { TableRow, } from "@/shadcn-bridge/heroui/table"; import { Chip } from "@/shadcn-bridge/heroui/chip"; -import { Spinner } from "@/shadcn-bridge/heroui/spinner"; + import { assignGroupPermission, assignTunnelsToGroup, @@ -469,14 +471,12 @@ export default function GroupPage() { } return ( -
- {loading && ( -
- -
- )} - - + + {loading ? ( + + ) : ( + <> +

隧道分组

+ + )} + ); } diff --git a/vite-frontend/src/pages/node.tsx b/vite-frontend/src/pages/node.tsx index 7fff46a..40d535e 100644 --- a/vite-frontend/src/pages/node.tsx +++ b/vite-frontend/src/pages/node.tsx @@ -288,7 +288,7 @@ const SortableItem = ({ export default function NodePage() { const [nodeList, setNodeList] = useState([]); const [nodeOrder, setNodeOrder] = useState([]); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [localSearchKeyword, setLocalSearchKeyword] = useLocalStorageState( "node-search-keyword-local", "", diff --git a/vite-frontend/src/pages/panel-sharing.tsx b/vite-frontend/src/pages/panel-sharing.tsx index d1fc03d..4568418 100644 --- a/vite-frontend/src/pages/panel-sharing.tsx +++ b/vite-frontend/src/pages/panel-sharing.tsx @@ -89,7 +89,7 @@ export default function PanelSharingPage() { [], ); const [nodes, setNodes] = useState([]); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [remoteUsageLoading, setRemoteUsageLoading] = useState(false); // Modals diff --git a/vite-frontend/src/pages/user.tsx b/vite-frontend/src/pages/user.tsx index 02bcb22..33929f4 100644 --- a/vite-frontend/src/pages/user.tsx +++ b/vite-frontend/src/pages/user.tsx @@ -185,7 +185,7 @@ const normalizeUserTunnelItem = (item: Partial): UserTunnel => { export default function UserPage() { // 状态管理 const [users, setUsers] = useState([]); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [searchKeyword, setSearchKeyword] = useLocalStorageState( "user-search-keyword", "", @@ -466,10 +466,6 @@ export default function UserPage() { void loadMonitorPermissions(); }, [loadMonitorPermissions, loadSpeedLimits, loadTunnels, loadUserGroups]); - useEffect(() => { - void loadUsers(); - }, [loadUsers]); - useEffect(() => { if (searchDebounceRef.current) { clearTimeout(searchDebounceRef.current); diff --git a/vite-frontend/src/styles/globals.css b/vite-frontend/src/styles/globals.css index 61333fc..bd4e323 100644 --- a/vite-frontend/src/styles/globals.css +++ b/vite-frontend/src/styles/globals.css @@ -241,16 +241,16 @@ body { bottom: 0; z-index: -1; pointer-events: none; - background: url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564&auto=format&fit=crop') no-repeat center center; + background: url('/images/bg-light.jpg') no-repeat center center; background-size: cover; } -:root.dark .bg-mesh-gradient { +.dark .bg-mesh-gradient { background-color: #0b1020; } .dark .bg-mesh-gradient::before { - background: url('https://images.unsplash.com/photo-1557682250-33bd709cbe85?q=80&w=2564&auto=format&fit=crop') no-repeat center center; + background: url('/images/bg-dark.jpg') no-repeat center center; background-size: cover; }