增加团队管理页面

This commit is contained in:
dengqichen 2025-10-29 12:37:46 +08:00
parent 75d8935ac3
commit 4a4da47253
6 changed files with 127 additions and 68 deletions

View File

@ -2,14 +2,23 @@ import * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
/**
*
* : "1200px" undefined
*/
minWidth?: string;
}
const Table = React.forwardRef< const Table = React.forwardRef<
HTMLTableElement, HTMLTableElement,
React.HTMLTableProps<HTMLTableElement> TableProps
>(({ className, ...props }, ref) => ( >(({ className, minWidth, style, ...props }, ref) => (
<div className="relative w-full overflow-auto"> <div className="relative w-full overflow-auto">
<table <table
ref={ref} ref={ref}
className={cn("w-full caption-bottom text-sm", className)} className={cn("w-full caption-bottom text-sm", className)}
style={{ ...style, ...(minWidth ? { minWidth } : {}) }}
{...props} {...props}
/> />
</div> </div>
@ -18,7 +27,7 @@ Table.displayName = "Table"
const TableHeader = React.forwardRef< const TableHeader = React.forwardRef<
HTMLTableSectionElement, HTMLTableSectionElement,
React.HTMLTableSectionProps React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
)) ))
@ -26,7 +35,7 @@ TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef< const TableBody = React.forwardRef<
HTMLTableSectionElement, HTMLTableSectionElement,
React.HTMLTableSectionProps React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<tbody <tbody
ref={ref} ref={ref}
@ -38,7 +47,7 @@ TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef< const TableFooter = React.forwardRef<
HTMLTableSectionElement, HTMLTableSectionElement,
React.HTMLTableSectionProps React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<tfoot <tfoot
ref={ref} ref={ref}
@ -50,7 +59,7 @@ TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef< const TableRow = React.forwardRef<
HTMLTableRowElement, HTMLTableRowElement,
React.HTMLTableRowProps React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<tr <tr
ref={ref} ref={ref}
@ -63,28 +72,59 @@ const TableRow = React.forwardRef<
)) ))
TableRow.displayName = "TableRow" TableRow.displayName = "TableRow"
interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
/**
*
* 使
*/
sticky?: boolean;
/**
* : "120px", "200px"
*/
width?: string;
}
const TableHead = React.forwardRef< const TableHead = React.forwardRef<
HTMLTableCellElement, HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement> TableHeadProps
>(({ className, ...props }, ref) => ( >(({ className, sticky, width, style, ...props }, ref) => (
<th <th
ref={ref} ref={ref}
className={cn( className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
sticky && "sticky right-0 bg-background shadow-[-2px_0_4px_rgba(0,0,0,0.05)]",
className className
)} )}
style={{ ...style, ...(width ? { width } : {}) }}
{...props} {...props}
/> />
)) ))
TableHead.displayName = "TableHead" TableHead.displayName = "TableHead"
interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
/**
*
* 使
*/
sticky?: boolean;
/**
* : "120px", "200px"
*/
width?: string;
}
const TableCell = React.forwardRef< const TableCell = React.forwardRef<
HTMLTableCellElement, HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement> TableCellProps
>(({ className, ...props }, ref) => ( >(({ className, sticky, width, style, ...props }, ref) => (
<td <td
ref={ref} ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)} className={cn(
"p-4 align-middle [&:has([role=checkbox])]:pr-0",
sticky && "sticky right-0 bg-background shadow-[-2px_0_4px_rgba(0,0,0,0.05)]",
className
)}
style={{ ...style, ...(width ? { width } : {}) }}
{...props} {...props}
/> />
)) ))

View File

@ -263,18 +263,18 @@ const CategoryManageDialog: React.FC<CategoryManageDialogProps> = ({
</div> </div>
{/* 表格 */} {/* 表格 */}
<div className="overflow-x-auto"> <div className="rounded-md border">
<Table> <Table minWidth="890px">
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className="w-[160px]"></TableHead> <TableHead width="160px"></TableHead>
<TableHead className="w-[140px]"></TableHead> <TableHead width="140px"></TableHead>
<TableHead className="w-[60px]"></TableHead> <TableHead width="60px"></TableHead>
<TableHead className="w-[120px]"></TableHead> <TableHead width="120px"></TableHead>
<TableHead className="w-[80px]"></TableHead> <TableHead width="80px"></TableHead>
<TableHead className="w-[80px]"></TableHead> <TableHead width="80px"></TableHead>
<TableHead className="min-w-[150px]"></TableHead> <TableHead width="150px"></TableHead>
<TableHead className="w-[100px] sticky right-0 bg-background shadow-[-2px_0_4px_rgba(0,0,0,0.05)]"></TableHead> <TableHead width="100px" sticky></TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@ -290,15 +290,15 @@ const CategoryManageDialog: React.FC<CategoryManageDialogProps> = ({
) : data?.content && data.content.length > 0 ? ( ) : data?.content && data.content.length > 0 ? (
data.content.map((record) => ( data.content.map((record) => (
<TableRow key={record.id}> <TableRow key={record.id}>
<TableCell className="font-medium"> <TableCell width="160px" className="font-medium">
{record.name} {record.name}
</TableCell> </TableCell>
<TableCell> <TableCell width="140px">
<code className="text-xs bg-muted px-2 py-0.5 rounded whitespace-nowrap"> <code className="text-xs bg-muted px-2 py-0.5 rounded whitespace-nowrap">
{record.code} {record.code}
</code> </code>
</TableCell> </TableCell>
<TableCell> <TableCell width="60px">
{record.icon ? ( {record.icon ? (
<div className="flex items-center justify-center"> <div className="flex items-center justify-center">
<DynamicIcon name={record.icon} className="h-5 w-5" /> <DynamicIcon name={record.icon} className="h-5 w-5" />
@ -307,21 +307,21 @@ const CategoryManageDialog: React.FC<CategoryManageDialogProps> = ({
<span className="text-muted-foreground text-sm">-</span> <span className="text-muted-foreground text-sm">-</span>
)} )}
</TableCell> </TableCell>
<TableCell className="text-center"> <TableCell width="120px" className="text-center">
<span className="text-sm font-medium">{record.applicationCount || 0}</span> <span className="text-sm font-medium">{record.applicationCount || 0}</span>
</TableCell> </TableCell>
<TableCell className="text-center">{record.sort}</TableCell> <TableCell width="80px" className="text-center">{record.sort}</TableCell>
<TableCell> <TableCell width="80px">
{record.enabled ? ( {record.enabled ? (
<CheckCircle2 className="h-4 w-4 text-green-500" /> <CheckCircle2 className="h-4 w-4 text-green-500" />
) : ( ) : (
<XCircle className="h-4 w-4 text-gray-400" /> <XCircle className="h-4 w-4 text-gray-400" />
)} )}
</TableCell> </TableCell>
<TableCell className="max-w-[200px] truncate" title={record.description}> <TableCell width="150px" className="max-w-[150px] truncate" title={record.description}>
{record.description || '-'} {record.description || '-'}
</TableCell> </TableCell>
<TableCell className="sticky right-0 bg-background shadow-[-2px_0_4px_rgba(0,0,0,0.05)]"> <TableCell width="100px" sticky>
<div className="flex items-center justify-end gap-1"> <div className="flex items-center justify-end gap-1">
<Button <Button
variant="ghost" variant="ghost"

View File

@ -459,13 +459,14 @@ const ApplicationList: React.FC = () => {
{/* 数据表格 */} {/* 数据表格 */}
<div className="rounded-md border"> <div className="rounded-md border">
<Table> <Table minWidth="1400px">
<TableHeader> <TableHeader>
<TableRow> <TableRow>
{columns.map((column) => ( {columns.map((column) => (
<TableHead <TableHead
key={column.accessorKey || column.id} key={column.accessorKey || column.id}
style={{ width: column.size }} width={column.size ? `${column.size}px` : undefined}
sticky={column.id === 'actions'}
> >
{column.header} {column.header}
</TableHead> </TableHead>
@ -473,7 +474,16 @@ const ApplicationList: React.FC = () => {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{list.length === 0 ? ( {loading ? (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
<div className="flex items-center justify-center gap-2">
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
<span className="text-muted-foreground">...</span>
</div>
</TableCell>
</TableRow>
) : list.length === 0 ? (
<TableRow> <TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center"> <TableCell colSpan={columns.length} className="h-24 text-center">
@ -493,7 +503,11 @@ const ApplicationList: React.FC = () => {
cellContent = ''; cellContent = '';
} }
return ( return (
<TableCell key={column.accessorKey || column.id}> <TableCell
key={column.accessorKey || column.id}
width={column.size ? `${column.size}px` : undefined}
sticky={column.id === 'actions'}
>
{cellContent} {cellContent}
</TableCell> </TableCell>
); );

View File

@ -319,15 +319,15 @@ const ApplicationManageDialog: React.FC<ApplicationManageDialogProps> = ({
{/* 表格 */} {/* 表格 */}
<div className="border rounded-lg overflow-hidden"> <div className="border rounded-lg overflow-hidden">
<div className="overflow-x-auto max-h-[50vh]"> <div className="max-h-[50vh] overflow-y-auto">
<Table> <Table minWidth="690px">
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className="w-[100px]">ID</TableHead> <TableHead width="100px">ID</TableHead>
<TableHead className="w-[150px]"></TableHead> <TableHead width="150px"></TableHead>
<TableHead className="w-[200px]"></TableHead> <TableHead width="200px"></TableHead>
<TableHead className="w-[160px]"></TableHead> <TableHead width="160px"></TableHead>
<TableHead className="w-[80px] text-right"></TableHead> <TableHead width="80px" sticky></TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@ -343,19 +343,19 @@ const ApplicationManageDialog: React.FC<ApplicationManageDialogProps> = ({
) : data?.content && data.content.length > 0 ? ( ) : data?.content && data.content.length > 0 ? (
data.content.map((record) => ( data.content.map((record) => (
<TableRow key={record.id}> <TableRow key={record.id}>
<TableCell className="font-medium"> <TableCell width="100px" className="font-medium">
{record.applicationId} {record.applicationId}
</TableCell> </TableCell>
<TableCell> <TableCell width="150px">
{record.applicationCode || '-'} {record.applicationCode || '-'}
</TableCell> </TableCell>
<TableCell> <TableCell width="200px">
{record.applicationName || '-'} {record.applicationName || '-'}
</TableCell> </TableCell>
<TableCell> <TableCell width="160px">
{record.createTime || '-'} {record.createTime || '-'}
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell width="80px" sticky>
<div className="flex items-center justify-end gap-1"> <div className="flex items-center justify-end gap-1">
<Button <Button
variant="ghost" variant="ghost"

View File

@ -367,15 +367,15 @@ const MemberManageDialog: React.FC<MemberManageDialogProps> = ({
{/* 表格 */} {/* 表格 */}
<div className="border rounded-lg overflow-hidden"> <div className="border rounded-lg overflow-hidden">
<div className="overflow-x-auto max-h-[50vh]"> <div className="max-h-[50vh] overflow-y-auto">
<Table> <Table minWidth="680px">
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className="w-[100px]">ID</TableHead> <TableHead width="100px">ID</TableHead>
<TableHead className="w-[150px]"></TableHead> <TableHead width="150px"></TableHead>
<TableHead className="w-[150px]"></TableHead> <TableHead width="150px"></TableHead>
<TableHead className="w-[160px]"></TableHead> <TableHead width="160px"></TableHead>
<TableHead className="w-[120px] text-right"></TableHead> <TableHead width="120px" sticky></TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@ -391,19 +391,19 @@ const MemberManageDialog: React.FC<MemberManageDialogProps> = ({
) : data?.content && data.content.length > 0 ? ( ) : data?.content && data.content.length > 0 ? (
data.content.map((record) => ( data.content.map((record) => (
<TableRow key={record.id}> <TableRow key={record.id}>
<TableCell className="font-medium"> <TableCell width="100px" className="font-medium">
{record.userId} {record.userId}
</TableCell> </TableCell>
<TableCell> <TableCell width="150px">
{record.userName || '-'} {record.userName || '-'}
</TableCell> </TableCell>
<TableCell> <TableCell width="150px">
{record.roleInTeam || '-'} {record.roleInTeam || '-'}
</TableCell> </TableCell>
<TableCell> <TableCell width="160px">
{record.joinTime || '-'} {record.joinTime || '-'}
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell width="120px" sticky>
<div className="flex items-center justify-end gap-1"> <div className="flex items-center justify-end gap-1">
<Button <Button
variant="ghost" variant="ghost"

View File

@ -193,28 +193,28 @@ const TeamList: React.FC = () => {
{ {
accessorKey: 'teamCode', accessorKey: 'teamCode',
header: '团队编码', header: '团队编码',
size: 150, size: 180,
}, },
{ {
accessorKey: 'teamName', accessorKey: 'teamName',
header: '团队名称', header: '团队名称',
size: 150, size: 180,
}, },
{ {
accessorKey: 'description', accessorKey: 'description',
header: '团队描述', header: '团队描述',
size: 200, size: 260,
}, },
{ {
accessorKey: 'ownerName', accessorKey: 'ownerName',
header: '负责人', header: '负责人',
size: 120, size: 140,
cell: ({ row }) => row.original.ownerName || '-', cell: ({ row }) => row.original.ownerName || '-',
}, },
{ {
id: 'memberCount', id: 'memberCount',
header: '成员数量', header: '成员数量',
size: 100, size: 120,
cell: ({ row }) => ( cell: ({ row }) => (
<div className="text-center"> <div className="text-center">
<span className="font-medium">{row.original.memberCount || 0}</span> <span className="font-medium">{row.original.memberCount || 0}</span>
@ -224,7 +224,7 @@ const TeamList: React.FC = () => {
{ {
id: 'applicationCount', id: 'applicationCount',
header: '应用数量', header: '应用数量',
size: 100, size: 120,
cell: ({ row }) => ( cell: ({ row }) => (
<div className="text-center"> <div className="text-center">
<span className="font-medium">{row.original.applicationCount || 0}</span> <span className="font-medium">{row.original.applicationCount || 0}</span>
@ -244,12 +244,12 @@ const TeamList: React.FC = () => {
{ {
accessorKey: 'sort', accessorKey: 'sort',
header: '排序', header: '排序',
size: 80, size: 100,
}, },
{ {
id: 'actions', id: 'actions',
header: '操作', header: '操作',
size: 200, size: 260,
cell: ({ row }) => ( cell: ({ row }) => (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button variant="ghost" size="sm" onClick={() => handleManageMembers(row.original)}> <Button variant="ghost" size="sm" onClick={() => handleManageMembers(row.original)}>
@ -368,13 +368,14 @@ const TeamList: React.FC = () => {
{/* 数据表格 */} {/* 数据表格 */}
<div className="rounded-md border"> <div className="rounded-md border">
<Table> <Table minWidth="1460px">
<TableHeader> <TableHeader>
<TableRow> <TableRow>
{columns.map((column) => ( {columns.map((column) => (
<TableHead <TableHead
key={column.accessorKey || column.id} key={column.accessorKey || column.id}
style={{ width: column.size }} width={column.size ? `${column.size}px` : undefined}
sticky={column.id === 'actions'}
> >
{column.header} {column.header}
</TableHead> </TableHead>
@ -402,7 +403,11 @@ const TeamList: React.FC = () => {
cellContent = ''; cellContent = '';
} }
return ( return (
<TableCell key={column.accessorKey || column.id}> <TableCell
key={column.accessorKey || column.id}
width={column.size ? `${column.size}px` : undefined}
sticky={column.id === 'actions'}
>
{cellContent} {cellContent}
</TableCell> </TableCell>
); );