application [cat-admin-web] layout development
This commit is contained in:
@@ -41,7 +41,7 @@ export function AuthRouter({ children }: AuthRouterProps) {
|
||||
redirectToLogin()
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// 获取用户信息失败,跳转到登录页
|
||||
redirectToLogin()
|
||||
return
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
import { useRoutes } from 'react-router-dom'
|
||||
import { useRoutes, useLocation, Link } from 'react-router-dom'
|
||||
import {
|
||||
Home,
|
||||
PawPrint,
|
||||
List,
|
||||
Eye,
|
||||
} from 'lucide-react'
|
||||
|
||||
// vite-plugin-pages 会自动生成路由配置
|
||||
// @ts-expect-error - 动态导入,类型会在构建时生成
|
||||
import autoRoutes from "~react-pages"
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
SidebarHeader,
|
||||
SidebarInset,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarProvider,
|
||||
SidebarTrigger,
|
||||
} from './ui/sidebar'
|
||||
import { Separator } from './ui/separator'
|
||||
|
||||
/**
|
||||
* Layout 组件
|
||||
*
|
||||
*
|
||||
* 这个组件会包裹所有需要 layout 的页面(除了 /user/login 和 /user/register)
|
||||
* 它使用 vite-plugin-pages 生成的路由配置来根据 pathname 动态渲染对应的页面组件
|
||||
*
|
||||
*
|
||||
* 用户可以在生成代码时自定义这个 Layout,添加侧边栏、导航栏等公共布局元素
|
||||
*/
|
||||
const noLayoutRoutes = [
|
||||
@@ -17,21 +38,77 @@ const noLayoutRoutes = [
|
||||
'/user/register',
|
||||
]
|
||||
|
||||
const navigation = [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
url: '/dashboard',
|
||||
icon: Home,
|
||||
},
|
||||
{
|
||||
title: 'Cat Management',
|
||||
url: '/cat-management',
|
||||
icon: PawPrint,
|
||||
},
|
||||
{
|
||||
title: 'Queue Management',
|
||||
url: '/queue-management',
|
||||
icon: List,
|
||||
},
|
||||
{
|
||||
title: 'Reservation View',
|
||||
url: '/reservation-view',
|
||||
icon: Eye,
|
||||
},
|
||||
]
|
||||
|
||||
export function Layout() {
|
||||
const location = useLocation()
|
||||
|
||||
// 过滤掉登录和注册页面,因为它们不应该被 layout 包裹
|
||||
const layoutRoutes = autoRoutes.filter(
|
||||
(route: any) => !noLayoutRoutes.includes(route.path)
|
||||
(route: { path: string }) => !noLayoutRoutes.includes(route.path)
|
||||
)
|
||||
|
||||
|
||||
// 使用 useRoutes 来渲染当前路径对应的组件
|
||||
const element = useRoutes(layoutRoutes)
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* 这里可以添加侧边栏、导航栏等公共布局元素 */}
|
||||
{/* 用户可以在生成代码时自定义这个 Layout */}
|
||||
{element}
|
||||
</div>
|
||||
<SidebarProvider>
|
||||
<Sidebar>
|
||||
<SidebarHeader>
|
||||
<SidebarGroupLabel>Cat Shelter Admin</SidebarGroupLabel>
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Navigation</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{navigation.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton asChild isActive={location.pathname === item.url}>
|
||||
<Link to={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
<SidebarInset>
|
||||
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||
<h1 className="text-lg font-semibold">Cat Shelter Management</h1>
|
||||
</header>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
{element}
|
||||
</div>
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ type User = {
|
||||
name?: string;
|
||||
email?: string;
|
||||
username?: string;
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
type AuthState = {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { Navigate } from 'react-router-dom'
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-bold mb-4">欢迎</h1>
|
||||
<p className="text-muted-foreground">这是首页</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <Navigate to="/dashboard" replace />
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ export default function QueueManagement() {
|
||||
setCats(cats.map(c => c.id === cat.id ? { ...c, is_available: !c.is_available } : c));
|
||||
};
|
||||
|
||||
const updateRules = (key: keyof QueueRules, value: any) => {
|
||||
const updateRules = (key: keyof QueueRules, value: number | string) => {
|
||||
setRules(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { entities } from '@/lib/nvwa';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -35,7 +35,7 @@ const ReservationView: React.FC = () => {
|
||||
const pageSize = 10; // 每页显示数量
|
||||
|
||||
// 获取预约数据
|
||||
const fetchReservations = async () => {
|
||||
const fetchReservations = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 构建查询,使用关联查询获取用户和猫宠信息
|
||||
@@ -91,11 +91,11 @@ const ReservationView: React.FC = () => {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [status, page]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchReservations();
|
||||
}, [status, page]);
|
||||
}, [fetchReservations]);
|
||||
|
||||
// 处理状态筛选
|
||||
const handleStatusChange = (newStatus: string) => {
|
||||
@@ -218,7 +218,7 @@ const ReservationView: React.FC = () => {
|
||||
<TableCell>{reservation.catName || '-'}</TableCell>
|
||||
<TableCell>¥{reservation.deposit.toFixed(2)}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={statusColors[reservation.status] as any}>
|
||||
<Badge variant={statusColors[reservation.status]}>
|
||||
{statusLabels[reservation.status]}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function LoginPage() {
|
||||
const redirectPath = backUrl && backUrl !== "/user/login" ? backUrl : "/";
|
||||
navigate(redirectPath);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// 未登录,继续显示登录页
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user