增加团队管理页面

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"
interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
/**
*
* : "1200px" undefined
*/
minWidth?: string;
}
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLTableProps<HTMLTableElement>
>(({ className, ...props }, ref) => (
TableProps
>(({ className, minWidth, style, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
style={{ ...style, ...(minWidth ? { minWidth } : {}) }}
{...props}
/>
</div>
@ -18,7 +27,7 @@ Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLTableSectionProps
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
@ -26,7 +35,7 @@ TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLTableSectionProps
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
@ -38,7 +47,7 @@ TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLTableSectionProps
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
@ -50,7 +59,7 @@ TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLTableRowProps
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
@ -63,28 +72,59 @@ const TableRow = React.forwardRef<
))
TableRow.displayName = "TableRow"
interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
/**
*
* 使
*/
sticky?: boolean;
/**
* : "120px", "200px"
*/
width?: string;
}
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
TableHeadProps
>(({ className, sticky, width, style, ...props }, ref) => (
<th
ref={ref}
className={cn(
"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
)}
style={{ ...style, ...(width ? { width } : {}) }}
{...props}
/>
))
TableHead.displayName = "TableHead"
interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
/**
*
* 使
*/
sticky?: boolean;
/**
* : "120px", "200px"
*/
width?: string;
}
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
TableCellProps
>(({ className, sticky, width, style, ...props }, ref) => (
<td
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}
/>
))

View File

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

View File

@ -459,13 +459,14 @@ const ApplicationList: React.FC = () => {
{/* 数据表格 */}
<div className="rounded-md border">
<Table>
<Table minWidth="1400px">
<TableHeader>
<TableRow>
{columns.map((column) => (
<TableHead
key={column.accessorKey || column.id}
style={{ width: column.size }}
width={column.size ? `${column.size}px` : undefined}
sticky={column.id === 'actions'}
>
{column.header}
</TableHead>
@ -473,7 +474,16 @@ const ApplicationList: React.FC = () => {
</TableRow>
</TableHeader>
<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>
<TableCell colSpan={columns.length} className="h-24 text-center">
@ -493,7 +503,11 @@ const ApplicationList: React.FC = () => {
cellContent = '';
}
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}
</TableCell>
);

View File

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

View File

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

View File

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