1
This commit is contained in:
parent
df175bbf65
commit
58e4cf2743
114
frontend/src/components/ui/table.tsx
Normal file
114
frontend/src/components/ui/table.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLTableProps<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="relative w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
Table.displayName = "Table"
|
||||
|
||||
const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLTableSectionProps
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
||||
))
|
||||
TableHeader.displayName = "TableHeader"
|
||||
|
||||
const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLTableSectionProps
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableBody.displayName = "TableBody"
|
||||
|
||||
const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLTableSectionProps
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn("bg-primary font-medium text-primary-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableFooter.displayName = "TableFooter"
|
||||
|
||||
const TableRow = React.forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLTableRowProps
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableRow.displayName = "TableRow"
|
||||
|
||||
const TableHead = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...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",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableHead.displayName = "TableHead"
|
||||
|
||||
const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCell.displayName = "TableCell"
|
||||
|
||||
const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCaption.displayName = "TableCaption"
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
@ -14,21 +14,52 @@ import type {ProjectGroup, ProjectGroupQueryParams} from './types';
|
||||
import {ProjectGroupTypeEnum} from './types';
|
||||
import {getProjectTypeInfo} from './utils';
|
||||
import ProjectGroupModal from './components/ProjectGroupModal';
|
||||
import {ProTable} from '@ant-design/pro-components';
|
||||
import type {ProColumns, ActionType} from '@ant-design/pro-components';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
} from "@/components/ui/table";
|
||||
|
||||
interface Column {
|
||||
accessorKey?: keyof ProjectGroup;
|
||||
id?: string;
|
||||
header: string;
|
||||
size: number;
|
||||
cell?: (props: { row: { original: ProjectGroup } }) => React.ReactNode;
|
||||
}
|
||||
|
||||
const ProjectGroupList: React.FC = () => {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentProject, setCurrentProject] = useState<ProjectGroup>();
|
||||
const actionRef = React.useRef<ActionType>();
|
||||
const [list, setList] = useState<ProjectGroup[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchForm] = Form.useForm();
|
||||
const {message: messageApi} = App.useApp();
|
||||
|
||||
const loadData = async (params?: ProjectGroupQueryParams) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await getProjectGroupPage(params);
|
||||
setList(data.content);
|
||||
} catch (error) {
|
||||
messageApi.error('加载数据失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await deleteProjectGroup(id);
|
||||
messageApi.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
loadData(searchForm.getFieldsValue());
|
||||
} catch (error) {
|
||||
messageApi.error('删除失败');
|
||||
}
|
||||
@ -52,45 +83,35 @@ const ProjectGroupList: React.FC = () => {
|
||||
const handleSuccess = () => {
|
||||
setModalVisible(false);
|
||||
setCurrentProject(undefined);
|
||||
actionRef.current?.reload();
|
||||
loadData(searchForm.getFieldsValue());
|
||||
};
|
||||
|
||||
const handleSearch = async (values: any) => {
|
||||
actionRef.current?.reload();
|
||||
loadData(values);
|
||||
};
|
||||
|
||||
const columns: ProColumns<ProjectGroup>[] = [
|
||||
const columns: Column[] = [
|
||||
{
|
||||
title: '项目组编码',
|
||||
dataIndex: 'projectGroupCode',
|
||||
width: 120,
|
||||
copyable: true,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
filters: true,
|
||||
filterSearch: true,
|
||||
onFilter: (value: string, record) => record.projectGroupCode.toLowerCase().includes(value.toLowerCase()),
|
||||
accessorKey: 'projectGroupCode',
|
||||
header: '项目组编码',
|
||||
size: 120,
|
||||
},
|
||||
{
|
||||
title: '项目组名称',
|
||||
dataIndex: 'projectGroupName',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
filters: true,
|
||||
filterSearch: true,
|
||||
onFilter: (value: string, record) => record.projectGroupName.toLowerCase().includes(value.toLowerCase()),
|
||||
accessorKey: 'projectGroupName',
|
||||
header: '项目组名称',
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
title: '项目组描述',
|
||||
dataIndex: 'projectGroupDesc',
|
||||
ellipsis: true,
|
||||
accessorKey: 'projectGroupDesc',
|
||||
header: '项目组描述',
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
title: '项目组类型',
|
||||
dataIndex: 'type',
|
||||
width: 150,
|
||||
render: (type) => {
|
||||
const typeInfo = getProjectTypeInfo(type as ProjectGroupTypeEnum);
|
||||
accessorKey: 'type',
|
||||
header: '项目组类型',
|
||||
size: 150,
|
||||
cell: ({ row }) => {
|
||||
const typeInfo = getProjectTypeInfo(row.original.type);
|
||||
return (
|
||||
<Tag color={typeInfo.color}>
|
||||
<Space>
|
||||
@ -100,84 +121,76 @@ const ProjectGroupList: React.FC = () => {
|
||||
</Tag>
|
||||
);
|
||||
},
|
||||
filters: [
|
||||
{text: '产品型', value: ProjectGroupTypeEnum.PRODUCT},
|
||||
{text: '项目型', value: ProjectGroupTypeEnum.PROJECT},
|
||||
],
|
||||
filterMode: 'menu',
|
||||
filtered: false,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabled',
|
||||
width: 100,
|
||||
valueEnum: {
|
||||
true: {text: '启用', status: 'Success'},
|
||||
false: {text: '禁用', status: 'Default'},
|
||||
},
|
||||
accessorKey: 'enabled',
|
||||
header: '状态',
|
||||
size: 100,
|
||||
cell: ({ row }) => (
|
||||
<Tag color={row.original.enabled ? 'success' : 'default'}>
|
||||
{row.original.enabled ? '启用' : '禁用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '环境数量',
|
||||
dataIndex: 'environments',
|
||||
width: 100,
|
||||
render: (_, record) => (
|
||||
accessorKey: 'totalEnvironments',
|
||||
header: '环境数量',
|
||||
size: 100,
|
||||
cell: ({ row }) => (
|
||||
<Space>
|
||||
<EnvironmentOutlined/>
|
||||
{record?.totalEnvironments || 0}
|
||||
{row.original.totalEnvironments || 0}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '项目数量',
|
||||
dataIndex: 'applications',
|
||||
width: 100,
|
||||
render: (_, record) => (
|
||||
accessorKey: 'totalApplications',
|
||||
header: '项目数量',
|
||||
size: 100,
|
||||
cell: ({ row }) => (
|
||||
<Space>
|
||||
<TeamOutlined/>
|
||||
{record?.totalApplications || 0}
|
||||
{row.original.totalApplications || 0}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width: 80,
|
||||
sorter: true,
|
||||
accessorKey: 'sort',
|
||||
header: '排序',
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
key: 'action',
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
key="edit"
|
||||
type="link"
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
<Space>
|
||||
<EditOutlined/>
|
||||
编辑
|
||||
</Space>
|
||||
</Button>,
|
||||
<Popconfirm
|
||||
key="delete"
|
||||
title="确定要删除该项目组吗?"
|
||||
description="删除后将无法恢复,请谨慎操作"
|
||||
onConfirm={() => handleDelete(record.id)}
|
||||
>
|
||||
id: 'actions',
|
||||
header: '操作',
|
||||
size: 180,
|
||||
cell: ({ row }) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
onClick={() => handleEdit(row.original)}
|
||||
>
|
||||
<Space>
|
||||
<DeleteOutlined/>
|
||||
删除
|
||||
<EditOutlined/>
|
||||
编辑
|
||||
</Space>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
],
|
||||
<Popconfirm
|
||||
title="确定要删除该项目组吗?"
|
||||
description="删除后将无法恢复,请谨慎操作"
|
||||
onConfirm={() => handleDelete(row.original.id)}
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
>
|
||||
<Space>
|
||||
<DeleteOutlined/>
|
||||
删除
|
||||
</Space>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@ -233,75 +246,57 @@ const ProjectGroupList: React.FC = () => {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
<ProTable<ProjectGroup>
|
||||
columns={columns}
|
||||
actionRef={actionRef}
|
||||
scroll={{x: 'max-content'}}
|
||||
cardBordered
|
||||
rowKey="id"
|
||||
search={false}
|
||||
options={{
|
||||
setting: false,
|
||||
density: false,
|
||||
fullScreen: false,
|
||||
reload: false,
|
||||
}}
|
||||
toolbar={{
|
||||
actions: [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
>
|
||||
新建项目组
|
||||
</Button>
|
||||
],
|
||||
}}
|
||||
form={{
|
||||
syncToUrl: true,
|
||||
ignoreRules: false,
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
request={async (params) => {
|
||||
try {
|
||||
const formValues = searchForm.getFieldsValue();
|
||||
const queryParams: ProjectGroupQueryParams = {
|
||||
pageSize: params.pageSize,
|
||||
pageNum: params.current,
|
||||
projectGroupCode: formValues.projectGroupCode?.trim(),
|
||||
projectGroupName: formValues.projectGroupName?.trim(),
|
||||
type: formValues.type,
|
||||
enabled: formValues.enabled,
|
||||
};
|
||||
const data = await getProjectGroupPage(queryParams);
|
||||
return {
|
||||
data: data.content || [],
|
||||
success: true,
|
||||
total: data.totalElements || 0,
|
||||
};
|
||||
} catch (error) {
|
||||
messageApi.error('获取项目组列表失败');
|
||||
return {
|
||||
data: [],
|
||||
success: false,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{modalVisible && (
|
||||
<ProjectGroupModal
|
||||
open={modalVisible}
|
||||
onCancel={handleModalClose}
|
||||
onSuccess={handleSuccess}
|
||||
initialValues={currentProject}
|
||||
/>
|
||||
)}
|
||||
<Card>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined/>}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
新增项目组
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{columns.map((column) => (
|
||||
<TableHead
|
||||
key={column.accessorKey || column.id}
|
||||
style={{ width: column.size }}
|
||||
>
|
||||
{column.header}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{list.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{columns.map((column) => (
|
||||
<TableCell key={column.accessorKey || column.id}>
|
||||
{column.cell
|
||||
? column.cell({ row: { original: row } })
|
||||
: column.accessorKey
|
||||
? String(row[column.accessorKey])
|
||||
: null}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<ProjectGroupModal
|
||||
open={modalVisible}
|
||||
onCancel={handleModalClose}
|
||||
onSuccess={handleSuccess}
|
||||
initialValues={currentProject}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,13 +1,28 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Card, Table, Button, Modal, Form, Input, Space, message, Switch, TreeSelect, Select, Tag, Dropdown } from 'antd';
|
||||
import { Button, Modal, Form, Input, Space, message, Switch, TreeSelect, Select, Tag, Dropdown } from 'antd';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TeamOutlined, MoreOutlined } from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { useTableData } from '@/hooks/useTableData';
|
||||
import * as service from './service';
|
||||
import type { UserResponse, UserRequest, UserQuery, Role } from './types';
|
||||
import type { DepartmentResponse } from '../Department/types';
|
||||
import { getDepartmentTree } from '../Department/service';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
} from "@/components/ui/table";
|
||||
|
||||
interface Column {
|
||||
accessorKey?: keyof UserResponse;
|
||||
id?: string;
|
||||
header: string;
|
||||
size: number;
|
||||
cell?: (props: { row: { original: UserResponse } }) => React.ReactNode;
|
||||
}
|
||||
|
||||
interface TreeNode {
|
||||
title: string;
|
||||
@ -150,75 +165,68 @@ const UserPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const columns: ColumnsType<UserResponse> = [
|
||||
const columns: Column[] = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 60,
|
||||
fixed: 'left',
|
||||
sorter: true
|
||||
accessorKey: 'id',
|
||||
header: 'ID',
|
||||
size: 60,
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'username',
|
||||
width: 100,
|
||||
sorter: true
|
||||
accessorKey: 'username',
|
||||
header: '用户名',
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
dataIndex: 'nickname',
|
||||
width: 100,
|
||||
accessorKey: 'nickname',
|
||||
header: '昵称',
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
dataIndex: 'email',
|
||||
width: 200,
|
||||
accessorKey: 'email',
|
||||
header: '邮箱',
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
dataIndex: 'departmentName',
|
||||
width: 150,
|
||||
accessorKey: 'departmentName',
|
||||
header: '部门',
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabled',
|
||||
width: 100,
|
||||
render: (enabled: boolean) => (
|
||||
<Tag color={enabled ? 'success' : 'error'}>
|
||||
{enabled ? '启用' : '禁用'}
|
||||
accessorKey: 'enabled',
|
||||
header: '状态',
|
||||
size: 100,
|
||||
cell: ({ row }) => (
|
||||
<Tag color={row.original.enabled ? 'success' : 'error'}>
|
||||
{row.original.enabled ? '启用' : '禁用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
width: 120,
|
||||
accessorKey: 'phone',
|
||||
header: '手机号',
|
||||
size: 120,
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
dataIndex: 'roles',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
render: (roles: Role[]) => roles?.map(role => role.name).join(', ') || '-'
|
||||
header: '角色',
|
||||
size: 120,
|
||||
cell: ({ row }) => row.original.roles?.map(role => role.name).join(', ') || '-',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
width: 150,
|
||||
sorter: true,
|
||||
accessorKey: 'createTime',
|
||||
header: '创建时间',
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updateTime',
|
||||
width: 150,
|
||||
sorter: true
|
||||
accessorKey: 'updateTime',
|
||||
header: '更新时间',
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
render: (_, record) => {
|
||||
id: 'actions',
|
||||
header: '操作',
|
||||
size: 180,
|
||||
cell: ({ row }) => {
|
||||
const record = row.original;
|
||||
const items: MenuProps['items'] = [
|
||||
{
|
||||
key: 'resetPassword',
|
||||
@ -235,7 +243,6 @@ const UserPage: React.FC = () => {
|
||||
}
|
||||
];
|
||||
|
||||
// 如果不是 admin 用户,添加删除选项
|
||||
if (record.username !== 'admin') {
|
||||
items.push({
|
||||
key: 'delete',
|
||||
@ -255,40 +262,58 @@ const UserPage: React.FC = () => {
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Dropdown
|
||||
menu={{ items }}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<MoreOutlined />}
|
||||
style={{ padding: '4px 8px' }}
|
||||
/>
|
||||
<Dropdown menu={{ items }} trigger={['click']}>
|
||||
<Button type="link" icon={<MoreOutlined />} />
|
||||
</Dropdown>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
新增用户
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={list}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
scroll={{ x: 1500 }}
|
||||
pagination={pagination}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
<div className="rounded-md border bg-white">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{columns.map((column) => (
|
||||
<TableHead
|
||||
key={column.accessorKey || column.id}
|
||||
style={{ width: column.size }}
|
||||
>
|
||||
{column.header}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{list.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{columns.map((column) => (
|
||||
<TableCell key={column.accessorKey || column.id}>
|
||||
{column.cell
|
||||
? column.cell({ row: { original: row } })
|
||||
: column.accessorKey
|
||||
? String(row[column.accessorKey])
|
||||
: null}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title={editingUser ? '编辑用户' : '新增用户'}
|
||||
@ -436,7 +461,7 @@ const UserPage: React.FC = () => {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,46 +1,47 @@
|
||||
import type { BaseResponse } from '@/types/base/response';
|
||||
import {BaseQuery} from "@/types/base";
|
||||
import type { BaseResponse } from '@/types/base';
|
||||
|
||||
// 用户查询参数
|
||||
export interface UserQuery extends BaseQuery {
|
||||
username?: string;
|
||||
nickname?: string;
|
||||
email?: string;
|
||||
enabled?: boolean;
|
||||
export interface UserQuery {
|
||||
username?: string;
|
||||
nickname?: string;
|
||||
email?: string;
|
||||
enabled?: boolean;
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
sortField?: string;
|
||||
sortOrder?: string;
|
||||
}
|
||||
|
||||
// 用户请求参数
|
||||
export interface UserRequest {
|
||||
username: string;
|
||||
nickname?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
password?: string;
|
||||
enabled?: boolean;
|
||||
departmentId?: number;
|
||||
username: string;
|
||||
nickname?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
password?: string;
|
||||
enabled?: boolean;
|
||||
departmentId?: number;
|
||||
}
|
||||
|
||||
// 角色类型定义
|
||||
export interface Role {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
type: number;
|
||||
sort: number;
|
||||
enabled: boolean;
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
type: number;
|
||||
sort: number;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
// 用户响应类型
|
||||
export interface UserResponse extends BaseResponse {
|
||||
username: string;
|
||||
nickname?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
enabled: boolean;
|
||||
roles: Role[];
|
||||
departmentId?: number;
|
||||
departmentName?: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
username: string;
|
||||
nickname?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
enabled: boolean;
|
||||
roles: Role[];
|
||||
departmentId?: number;
|
||||
departmentName?: string;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user