"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { SearchIcon } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { Controller, Resolver, useForm } from "react-hook-form"; import { z } from "zod/v4"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Drawer, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, } from "@/components/ui/drawer"; import { Field, FieldContent, FieldError, FieldLabel, } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import type { AdminPermission, AdminRole } from "@/lib/api/admin"; type AssignPermissionsDrawerProps = { open: boolean; saving: boolean; loading: boolean; item: AdminRole | null; permissions: AdminPermission[]; selectedPermissionIds: number[]; onOpenChange: (open: boolean) => void; onSubmit: (permissionIds: number[]) => Promise; }; const assignPermissionsSchema = z.object({ permissionIds: z.array(z.number().int().positive()), }); type AssignPermissionsForm = z.infer; const assignPermissionsResolver = zodResolver( assignPermissionsSchema as never, ) as Resolver< z.input, undefined, z.output >; function buildForm(selectedPermissionIds: number[]): AssignPermissionsForm { return { permissionIds: selectedPermissionIds, }; } export function AssignPermissionsDrawer({ open, saving, loading, item, permissions, selectedPermissionIds, onOpenChange, onSubmit, }: AssignPermissionsDrawerProps) { return ( {open ? ( ) : null} ); } type AssignPermissionsDrawerBodyProps = { saving: boolean; loading: boolean; item: AdminRole | null; permissions: AdminPermission[]; selectedPermissionIds: number[]; onOpenChange: (open: boolean) => void; onSubmit: (permissionIds: number[]) => Promise; }; function AssignPermissionsDrawerBody({ saving, loading, item, permissions, selectedPermissionIds, onOpenChange, onSubmit, }: AssignPermissionsDrawerBodyProps) { const [keyword, setKeyword] = useState(""); const form = useForm< z.input, undefined, z.output >({ resolver: assignPermissionsResolver, defaultValues: buildForm(selectedPermissionIds), }); const { control, handleSubmit, reset, formState: { errors }, } = form; useEffect(() => { reset(buildForm(selectedPermissionIds)); }, [reset, selectedPermissionIds]); const groupedPermissions = useMemo(() => { const output = keyword.trim().toLowerCase(); const filtered = output ? permissions.filter((permission) => `${permission.name} ${permission.code} ${permission.groupName} ${permission.apiPath}` .toLowerCase() .includes(output), ) : permissions; const groups = new Map(); filtered.forEach((permission) => { const groupName = permission.groupName || "default"; const list = groups.get(groupName) ?? []; list.push(permission); groups.set(groupName, list); }); return Array.from(groups.entries()).sort(([left], [right]) => left.localeCompare(right, "zh-CN"), ); }, [keyword, permissions]); async function onFormSubmit(values: AssignPermissionsForm) { await onSubmit(values.permissionIds); } return ( 分配权限 当前角色:{item?.name || "-"} {item?.code ? `(${item.code})` : ""}
权限列表
setKeyword(event.target.value)} placeholder="搜索权限名称、编码、分组或接口" className="pl-9" disabled={loading} />
{ const value = field.value || []; return (
{loading ? (
正在加载权限列表...
) : groupedPermissions.length > 0 ? ( groupedPermissions.map(([groupName, list]) => (
{groupName} {list.length} 项
{list.map((permission) => { const checked = value.includes(permission.id); return ( ); })}
)) ) : (
没有匹配的权限
)}
); }} />
); }