/** * 用户 CRUD 演示岛:tRPC + React Query。 * Provider 包在组件内部,便于 Astro 以 client:visible 挂载而不污染全局。 */ import type { RouterOutputs } from "@app/trpc"; import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Label, Separator, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@app/ui"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { useState } from "react"; import { createTRPCReactClient, trpc } from "../lib/trpc"; const queryClient = new QueryClient(); const trpcClient = createTRPCReactClient(); type User = RouterOutputs["user"]["list"]["users"][number]; export default function UserList() { return ( ); } function UserListInner() { const users = trpc.user.list.useQuery({ limit: 20, offset: 0 }); const createUser = trpc.user.create.useMutation({ onSuccess: () => { users.refetch(); toast.success("用户创建成功"); }, onError: (err) => { toast.error(`创建失败: ${err.message}`); }, }); const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name || !email || !password) return; await createUser.mutateAsync({ name, email, password }); setName(""); setEmail(""); setPassword(""); }; return (
用户列表 当前共 {users.data?.total ?? "—"} 位用户 {users.isPending ? (
加载中…
) : users.isError ? (
加载失败: {users.error.message}
) : ( ID 名称 邮箱 状态 {users.data?.users.length === 0 ? ( 暂无数据 ) : ( users.data?.users.map((u: User) => ( {u.id.slice(0, 8)}… {u.name} {u.email} 活跃 )) )}
)}
新建用户
setName(e.target.value)} required />
setEmail(e.target.value)} required />
setPassword(e.target.value)} required />
{createUser.isError && ( {createUser.error.message} )}
); }