From 2c7970796fa4088c407905bd9306e3a48dcc89b6 Mon Sep 17 00:00:00 2001 From: luorijun Date: Sat, 28 Mar 2026 17:07:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=9D=83=E9=99=90=E6=A0=91?= =?UTF-8?q?=E7=9A=84=E5=B1=95=E7=A4=BA=E4=B8=8E=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(root)/permissions/page.tsx | 139 +++++++++++++++-- src/app/(root)/roles/assign-permissions.tsx | 160 ++++++++++---------- src/app/(root)/roles/page.tsx | 4 +- src/components/data-table/component.tsx | 1 + src/models/permission.ts | 2 +- 5 files changed, 209 insertions(+), 97 deletions(-) diff --git a/src/app/(root)/permissions/page.tsx b/src/app/(root)/permissions/page.tsx index 96d0d75..414ec87 100644 --- a/src/app/(root)/permissions/page.tsx +++ b/src/app/(root)/permissions/page.tsx @@ -1,19 +1,136 @@ "use client" -import { Suspense } from "react" -import { getPagePermission } from "@/actions/permission" -import { DataTable, useDataTable } from "@/components/data-table" +import { + flexRender, + getCoreRowModel, + getExpandedRowModel, + type Row, + useReactTable, +} from "@tanstack/react-table" +import { Suspense, useCallback, useEffect, useState } from "react" +import { toast } from "sonner" +import { getAllPermissions } from "@/actions/permission" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { cn } from "@/lib/utils" + +type Node = { + id: number + name: string + description: string + children: Node[] +} export default function PermissionsPage() { - const table = useDataTable((page, size) => getPagePermission({ page, size })) return ( - + ) } + +function PermissionTable() { + const [data, setData] = useState([]) + + const table = useReactTable({ + getCoreRowModel: getCoreRowModel(), + getExpandedRowModel: getExpandedRowModel(), + data, + columns: [ + { header: "编码", accessorKey: "name" }, + { header: "描述", accessorKey: "description" }, + ], + getSubRows: row => row.children, + }) + + const refresh = useCallback(async () => { + try { + const resp = await getAllPermissions() + if (!resp.success) { + throw new Error(resp.message) + } + + const map = new Map() + resp.data.forEach(permission => { + map.set(permission.id, { + id: permission.id, + name: permission.name, + description: permission.description, + children: [], + }) + }) + + const roots: Node[] = [] + resp.data.forEach(permission => { + const node = map.get(permission.id) + if (!node) { + throw new Error(`找不到权限节点: ${permission.name}`) + } + + if (!permission.parent_id) { + roots.push(node) + return + } + + const parent = map.get(permission.parent_id) + if (!parent) { + throw new Error(`找不到父权限节点: ${permission.name}`) + } + + parent.children.push(node) + }) + + setData(roots) + console.log(roots) + } catch (e) { + toast.error(e instanceof Error ? e.message : "获取权限列表失败") + } + }, []) + + useEffect(() => { + refresh() + }, [refresh]) + + return ( +
+ + + + 编码 + 描述 + + + + {table.getRowModel().rows.map(row => ( + + ))} + +
+
+ ) +} + +function PermisionRow(props: { row: Row }) { + const row = props.row + return ( + <> + + +
+ + {row.original.name} + +
+ {row.original.description} +
+ {row.subRows.map(subRow => ( + + ))} + + ) +} diff --git a/src/app/(root)/roles/assign-permissions.tsx b/src/app/(root)/roles/assign-permissions.tsx index 26db9e7..c03285d 100644 --- a/src/app/(root)/roles/assign-permissions.tsx +++ b/src/app/(root)/roles/assign-permissions.tsx @@ -13,81 +13,58 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog" -import type { Permission } from "@/models/permission" +import { cn } from "@/lib/utils" import type { Role } from "@/models/role" -function PermissionItem({ - permission, +type TreeNode = { + id: number + name: string + description: string + children: TreeNode[] +} + +function PermissionRow({ + node, + depth, selected, onToggle, }: { - permission: Permission + node: TreeNode + depth: number selected: Set onToggle: (id: number, checked: boolean) => void }) { - const hasChildren = permission.children && permission.children.length > 0 - - const allChildIds = (p: Permission): number[] => { - if (!p.children || p.children.length === 0) return [p.id] - return [p.id, ...p.children.flatMap(allChildIds)] - } - - const childIds = hasChildren ? allChildIds(permission).slice(1) : [] - const allChecked = - hasChildren && childIds.length > 0 - ? childIds.every(id => selected.has(id)) - : selected.has(permission.id) - const someChecked = - hasChildren && childIds.length > 0 - ? childIds.some(id => selected.has(id)) && !allChecked - : false - - const handleChange = (checked: boolean) => { - if (hasChildren) { - const ids = allChildIds(permission) - for (const id of ids) { - onToggle(id, checked) - } - } else { - onToggle(permission.id, checked) - } - } - - const checkboxId = `perm-${permission.id}` + const hasChildren = node.children.length > 0 return ( -
-
+ <> +
+
handleChange(checked === true)} + id={`perm-${node.id}`} + checked={selected.has(node.id)} + onCheckedChange={checked => onToggle(node.id, checked === true)} />
- {hasChildren && ( -
- {permission.children.map(child => ( - - ))} -
- )} -
+ {node.children.map(child => ( + + ))} + ) } @@ -98,33 +75,51 @@ export function AssignPermissions(props: { const [open, setOpen] = useState(false) const [loading, setLoading] = useState(false) const [submitting, setSubmitting] = useState(false) - const [permissions, setPermissions] = useState([]) + const [nodes, setNodes] = useState([]) const [selected, setSelected] = useState>(new Set()) const fetchPermissions = useCallback(async () => { setLoading(true) try { const resp = await getAllPermissions() - if (resp.success) { - setPermissions(resp.data ?? []) - } else { - toast.error(resp.message ?? "获取权限列表失败") - } + if (!resp.success) throw new Error(resp.message) + + const data = resp.data ?? [] + + const map = new Map() + data.forEach(p => { + map.set(p.id, { + id: p.id, + name: p.name, + description: p.description, + children: [], + }) + }) + + const roots: TreeNode[] = [] + data.forEach(p => { + const node = map.get(p.id) + if (!node) return + if (!p.parent_id) { + roots.push(node) + } else { + map.get(p.parent_id)?.children.push(node) + } + }) + + setNodes(roots) } catch (error) { - const message = error instanceof Error ? error.message : error - toast.error(`接口请求错误: ${message}`) + toast.error(error instanceof Error ? error.message : "获取权限列表失败") } finally { setLoading(false) } }, []) useEffect(() => { - if (open) { - fetchPermissions() - } + if (open) fetchPermissions() }, [open, fetchPermissions]) - const handleToggle = (id: number, checked: boolean) => { + const handleToggle = useCallback((id: number, checked: boolean) => { setSelected(prev => { const next = new Set(prev) if (checked) { @@ -134,7 +129,7 @@ export function AssignPermissions(props: { } return next }) - } + }, []) const handleSubmit = async () => { setSubmitting(true) @@ -151,8 +146,7 @@ export function AssignPermissions(props: { toast.error(resp.message ?? "权限分配失败") } } catch (error) { - const message = error instanceof Error ? error.message : error - toast.error(`接口请求错误: ${message}`) + toast.error(error instanceof Error ? error.message : "接口请求错误") } finally { setSubmitting(false) } @@ -160,11 +154,10 @@ export function AssignPermissions(props: { const handleOpenChange = (value: boolean) => { if (value) { - const existingIds = new Set((props.role.permissions ?? []).map(p => p.id)) - setSelected(existingIds) + setSelected(new Set((props.role.permissions ?? []).map(p => p.id))) } else { setSelected(new Set()) - setPermissions([]) + setNodes([]) } setOpen(value) } @@ -187,16 +180,17 @@ export function AssignPermissions(props: {
加载中...
- ) : permissions.length === 0 ? ( + ) : nodes.length === 0 ? (
暂无权限数据
) : ( -
- {permissions.map(permission => ( - + {nodes.map(node => ( + diff --git a/src/app/(root)/roles/page.tsx b/src/app/(root)/roles/page.tsx index ca8606c..0902c11 100644 --- a/src/app/(root)/roles/page.tsx +++ b/src/app/(root)/roles/page.tsx @@ -94,7 +94,7 @@ function PermissionsCell({ permissions }: { permissions: Permission[] }) {
{preview.map(p => ( - {p.name} + {p.description} ))} {rest > 0 && +{rest}} @@ -107,7 +107,7 @@ function PermissionsCell({ permissions }: { permissions: Permission[] }) {
{permissions.map(p => ( - {p.name} + {p.description} ))}
diff --git a/src/components/data-table/component.tsx b/src/components/data-table/component.tsx index 774d29c..640900b 100644 --- a/src/components/data-table/component.tsx +++ b/src/components/data-table/component.tsx @@ -3,6 +3,7 @@ import { type ColumnDef, flexRender, getCoreRowModel, + getExpandedRowModel, useReactTable, } from "@tanstack/react-table" import { Loader } from "lucide-react" diff --git a/src/models/permission.ts b/src/models/permission.ts index e80d2c1..3fdcbba 100644 --- a/src/models/permission.ts +++ b/src/models/permission.ts @@ -4,7 +4,7 @@ export type Permission = { updated_at: Date expired_at: Date - parent_id: number + parent_id?: number name: string description: string parent: number