From 78ebd0b62ee65064be85e61d5ff08a760afaab60 Mon Sep 17 00:00:00 2001 From: msksbr515 Date: Sun, 24 May 2026 20:40:07 +0800 Subject: [PATCH] feat(admin): add book borrowing dialog and root redirect - Add admin book borrowing dialog with user selection - Add useAdminBorrowBook mutation with success/error toasts - Fix sidebar z-index layering on admin layout - Add RootRedirect component that routes based on admin role --- src/components/layout/AdminLayout.tsx | 2 +- src/features/admin/AdminBooksPage.tsx | 50 ++++++++++++++++++++++++++- src/features/admin/hooks.ts | 17 ++++++++- src/router/index.tsx | 8 ++++- 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/components/layout/AdminLayout.tsx b/src/components/layout/AdminLayout.tsx index 6110724..94af0c4 100644 --- a/src/components/layout/AdminLayout.tsx +++ b/src/components/layout/AdminLayout.tsx @@ -31,7 +31,7 @@ export default function AdminLayout() { if (!isMobile) { return (
-
@@ -120,6 +125,9 @@ export default function AdminBooksPage() { + @@ -133,6 +141,7 @@ export default function AdminBooksPage() { {del.book && ( setDel({ open: false, book: null })} /> )} + {borrow && setBorrow(null)} />} ) } @@ -302,6 +311,45 @@ function DeleteBookDialog({ book, open, onClose }: { book: Book; open: boolean; ) } +// ---- Manual Borrow Dialog ---- +function BorrowDialog_({ book, open, onClose }: { book: Book; open: boolean; onClose: () => void }) { + const mutation = useAdminBorrowBook() + const [userId, setUserId] = useState("") + + useEffect(() => { + if (!open) setUserId("") + }, [open]) + + async function submit(e: FormEvent) { + e.preventDefault() + if (!userId.trim()) return + await mutation.mutateAsync({ bookId: book.id!, userId: Number(userId) }) + onClose() + } + + return ( + !o && onClose()}> + + + 手动借阅 — {book.name} + +
+
+ + setUserId(e.target.value)} placeholder="输入用户 ID" /> +
+ + + +
+
+
+ ) +} + // ---- Skeleton ---- function AdminBooksSkeleton({ isMobile }: { isMobile: boolean }) { if (isMobile) { diff --git a/src/features/admin/hooks.ts b/src/features/admin/hooks.ts index 2f0040a..fdf7962 100644 --- a/src/features/admin/hooks.ts +++ b/src/features/admin/hooks.ts @@ -2,7 +2,7 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" import { toast } from "sonner" import { getAllBooks } from "@/api/books" import { addBook, deleteBook, updateBook, updateStock } from "@/api/admin/books" -import { getAllBorrows, searchBorrows, returnBook } from "@/api/admin/borrows" +import { getAllBorrows, searchBorrows, returnBook, borrowBook as adminBorrowBook } from "@/api/admin/borrows" export function useAdminBooks() { return useQuery({ @@ -96,3 +96,18 @@ export function useAdminReturnBook() { }, }) } + +export function useAdminBorrowBook() { + const qc = useQueryClient() + return useMutation({ + mutationFn: ({ bookId, userId }: { bookId: number; userId: number }) => + adminBorrowBook(bookId, userId), + onSuccess: () => { + qc.invalidateQueries({ queryKey: ["admin", "borrows"] }) + toast.success("借阅成功") + }, + onError: (err) => { + toast.error(err instanceof Error ? err.message : "借阅失败") + }, + }) +} diff --git a/src/router/index.tsx b/src/router/index.tsx index ad5b123..b824c17 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -1,5 +1,6 @@ import { createBrowserRouter, Navigate } from "react-router-dom" import { ProtectedRoute, AdminRoute } from "./guards" +import { useAuthStore } from "@/store/authStore" import LoginPage from "@/features/auth/LoginPage" import BooksPage from "@/features/books/BooksPage" import MyBorrowsPage from "@/features/borrows/MyBorrowsPage" @@ -8,6 +9,11 @@ import AdminBorrowsPage from "@/features/admin/AdminBorrowsPage" import AppLayout from "@/components/layout/AppLayout" import AdminLayout from "@/components/layout/AdminLayout" +function RootRedirect() { + const isAdmin = useAuthStore((s) => s.isAdmin()) + return +} + export const router = createBrowserRouter([ { path: "/login", @@ -18,7 +24,7 @@ export const router = createBrowserRouter([ children: [ { path: "/", - element: , + element: , }, { path: "/books",