diff --git a/frontend/src/pages/System/Online/List/index.tsx b/frontend/src/pages/System/Online/List/index.tsx index 94bc33fa..01ab7c96 100644 --- a/frontend/src/pages/System/Online/List/index.tsx +++ b/frontend/src/pages/System/Online/List/index.tsx @@ -1,15 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useMemo, useRef } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; +import { Separator } from '@/components/ui/separator'; import { Button } from '@/components/ui/button'; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table'; +import { PaginatedTable, type ColumnDef, type SearchFieldDef, type PaginatedTableRef } from '@/components/ui/paginated-table'; import { AlertDialog, AlertDialogAction, @@ -21,47 +14,18 @@ import { AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { useToast } from '@/components/ui/use-toast'; -import { Search, RefreshCw, Users, TrendingUp, Clock, LogOut } from 'lucide-react'; +import { RefreshCw, Users, TrendingUp, Clock, LogOut } from 'lucide-react'; import { getOnlineUsers, getOnlineStatistics, kickUser } from './service'; -import type { OnlineUserResponse, OnlineStatistics } from './types'; -import type { Page } from '@/types/base'; +import type { OnlineUserResponse, OnlineStatistics, OnlineUserQuery } from './types'; import dayjs from 'dayjs'; const OnlineUserList: React.FC = () => { const { toast } = useToast(); - const [loading, setLoading] = useState(false); - const [data, setData] = useState | null>(null); + const tableRef = useRef>(null); const [statistics, setStatistics] = useState(null); - const [keyword, setKeyword] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - const [pageSize] = useState(10); const [kickDialogOpen, setKickDialogOpen] = useState(false); const [selectedUser, setSelectedUser] = useState(null); - // 加载在线用户列表 - const loadData = async () => { - try { - setLoading(true); - const response = await getOnlineUsers({ - keyword: keyword || undefined, - pageNum: currentPage, - pageSize, - sortField: 'loginTime', - sortOrder: 'desc', - }); - setData(response); - } catch (error) { - console.error('加载失败:', error); - toast({ - title: '加载失败', - description: error instanceof Error ? error.message : '未知错误', - variant: 'destructive', - }); - } finally { - setLoading(false); - } - }; - // 加载统计数据 const loadStatistics = async () => { try { @@ -73,19 +37,22 @@ const OnlineUserList: React.FC = () => { }; useEffect(() => { - loadData(); loadStatistics(); - }, [currentPage, pageSize]); + }, []); - // 搜索 - const handleSearch = () => { - setCurrentPage(1); - loadData(); + // 包装 fetchFn,转换页码(后端从1开始) + const fetchData = async (query: OnlineUserQuery) => { + return await getOnlineUsers({ + ...query, + pageNum: (query.pageNum || 0) + 1, // 转换为从1开始 + sortField: 'loginTime', + sortOrder: 'desc', + }); }; // 刷新 const handleRefresh = () => { - loadData(); + tableRef.current?.refresh(); loadStatistics(); }; @@ -107,7 +74,7 @@ const OnlineUserList: React.FC = () => { }); setKickDialogOpen(false); setSelectedUser(null); - loadData(); + tableRef.current?.refresh(); loadStatistics(); } catch (error) { console.error('强制下线失败:', error); @@ -133,10 +100,64 @@ const OnlineUserList: React.FC = () => { } }; - // 格式化时间 - const formatTime = (time: string): string => { - return dayjs(time).format('YYYY-MM-DD HH:mm:ss'); - }; + // 搜索字段定义 + const searchFields: SearchFieldDef[] = useMemo(() => [ + { key: 'keyword', type: 'input', placeholder: '搜索用户名/昵称', width: 'w-[220px]' }, + ], []); + + // 列定义 + const columns: ColumnDef[] = useMemo(() => [ + { key: 'username', title: '用户名', dataIndex: 'username', width: '120px' }, + { key: 'nickname', title: '昵称', dataIndex: 'nickname', width: '120px' }, + { key: 'departmentName', title: '部门', dataIndex: 'departmentName', width: '120px' }, + { + key: 'loginTime', + title: '登录时间', + width: '180px', + render: (_, record) => dayjs(record.loginTime).format('YYYY-MM-DD HH:mm:ss'), + }, + { + key: 'onlineDuration', + title: '在线时长', + width: '120px', + render: (_, record) => formatDuration(record.onlineDuration), + }, + { + key: 'lastActiveTime', + title: '最后活跃', + width: '180px', + render: (_, record) => dayjs(record.lastActiveTime).format('YYYY-MM-DD HH:mm:ss'), + }, + { key: 'ipAddress', title: 'IP地址', dataIndex: 'ipAddress', width: '140px' }, + { key: 'browser', title: '浏览器', dataIndex: 'browser', width: '120px' }, + { key: 'os', title: '操作系统', dataIndex: 'os', width: '120px' }, + { + key: 'actions', + title: '操作', + width: '120px', + sticky: true, + render: (_, record) => ( + + ), + }, + ], []); + + // 工具栏 + const toolbar = ( + + ); return (
@@ -185,120 +206,19 @@ const OnlineUserList: React.FC = () => { {/* 主内容卡片 */} -
- 在线用户列表 -
-
- setKeyword(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && handleSearch()} - className="w-64" - /> - -
- -
-
+ 在线用户列表
- -
- - - - 用户名 - 昵称 - 部门 - 登录时间 - 在线时长 - 最后活跃 - IP地址 - 浏览器 - 操作系统 - 操作 - - - - {loading ? ( - - - 加载中... - - - ) : data?.content && data.content.length > 0 ? ( - data.content.map((user) => ( - - {user.username} - {user.nickname} - {user.departmentName || '-'} - {formatTime(user.loginTime)} - {formatDuration(user.onlineDuration)} - {formatTime(user.lastActiveTime)} - {user.ipAddress || '-'} - {user.browser || '-'} - {user.os || '-'} - -
- -
-
-
- )) - ) : ( - - - 暂无数据 - - - )} -
-
-
- - {/* 分页 */} - {data && data.totalElements > 0 && ( -
-
- 共 {data.totalElements} 条记录,第 {currentPage} / {data.totalPages} 页 -
-
- - -
-
- )} + + + + ref={tableRef} + fetchFn={fetchData} + columns={columns} + searchFields={searchFields} + toolbar={toolbar} + rowKey="userId" + minWidth="1400px" + />
diff --git a/frontend/src/pages/System/Role/List/index.tsx b/frontend/src/pages/System/Role/List/index.tsx index 42d9e87f..323cda89 100644 --- a/frontend/src/pages/System/Role/List/index.tsx +++ b/frontend/src/pages/System/Role/List/index.tsx @@ -1,19 +1,16 @@ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useMemo, useRef } from 'react'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; -import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table'; +import { Separator } from '@/components/ui/separator'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { DataTablePagination } from '@/components/ui/pagination'; +import { PaginatedTable, type ColumnDef, type SearchFieldDef, type PaginatedTableRef } from '@/components/ui/paginated-table'; import { - Loader2, Plus, Search, Edit, Trash2, KeyRound, Tag as TagIcon, + Plus, Edit, Trash2, KeyRound, Tag as TagIcon, ShieldCheck, Settings, Shield } from 'lucide-react'; import { useToast } from '@/components/ui/use-toast'; import { getRoleList, deleteRole, getRoleMenusAndPermissions, assignMenusAndPermissions } from './service'; import type { RoleResponse, RoleQuery } from './types'; -import type { Page } from '@/types/base'; -import { DEFAULT_PAGE_SIZE, DEFAULT_PAGE_NUM } from '@/utils/page'; import EditDialog from './components/EditDialog'; import DeleteDialog from './components/DeleteDialog'; import PermissionDialog from './components/PermissionDialog'; @@ -26,8 +23,8 @@ import dayjs from 'dayjs'; */ const RolePage: React.FC = () => { const { toast } = useToast(); - const [loading, setLoading] = useState(false); - const [data, setData] = useState | null>(null); + const tableRef = useRef>(null); + const [stats, setStats] = useState({ total: 0 }); const [editDialogOpen, setEditDialogOpen] = useState(false); const [editRecord, setEditRecord] = useState(); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); @@ -38,63 +35,30 @@ const RolePage: React.FC = () => { const [selectedRole, setSelectedRole] = useState(null); const [defaultMenuIds, setDefaultMenuIds] = useState([]); const [defaultPermissionIds, setDefaultPermissionIds] = useState([]); - const [query, setQuery] = useState({ - pageNum: DEFAULT_PAGE_NUM, - pageSize: DEFAULT_PAGE_SIZE, - code: '', - name: '', - }); - // 加载数据 - const loadData = async () => { - setLoading(true); - try { - const result = await getRoleList(query); - setData(result); - } catch (error) { - console.error('加载角色列表失败:', error); - toast({ - title: '加载失败', - description: error instanceof Error ? error.message : '未知错误', - variant: 'destructive' - }); - } finally { - setLoading(false); - } + // 包装 fetchFn,同时更新统计数据 + const fetchData = async (query: RoleQuery) => { + const result = await getRoleList(query); + setStats({ total: result.totalElements || 0 }); + return result; }; - useEffect(() => { - loadData(); - }, [query]); - - // 搜索 - const handleSearch = () => { - setQuery(prev => ({ ...prev, pageNum: 0 })); - }; - - // 重置 - const handleReset = () => { - setQuery({ - pageNum: 0, - pageSize: DEFAULT_PAGE_SIZE, - code: '', - name: '', - }); - }; - - // 新建 const handleCreate = () => { setEditRecord(undefined); setEditDialogOpen(true); }; - // 编辑 const handleEdit = (record: RoleResponse) => { setEditRecord(record); setEditDialogOpen(true); }; - // 删除 + const handleSuccess = () => { + setEditDialogOpen(false); + setEditRecord(undefined); + tableRef.current?.refresh(); + }; + const handleDelete = (record: RoleResponse) => { setDeleteRecord(record); setDeleteDialogOpen(true); @@ -108,7 +72,7 @@ const RolePage: React.FC = () => { title: '删除成功', description: `角色 "${deleteRecord.name}" 已删除`, }); - loadData(); + tableRef.current?.refresh(); setDeleteDialogOpen(false); setDeleteRecord(null); } catch (error) { @@ -148,7 +112,7 @@ const RolePage: React.FC = () => { description: `已为角色 "${selectedRole.name}" 分配权限`, }); setPermissionDialogOpen(false); - loadData(); + tableRef.current?.refresh(); } catch (error) { console.error('分配权限失败:', error); toast({ @@ -165,13 +129,132 @@ const RolePage: React.FC = () => { setAssignTagDialogOpen(true); }; - // 统计数据 - const stats = useMemo(() => { - const total = data?.totalElements || 0; - return { total }; - }, [data]); + // 搜索字段定义 + const searchFields: SearchFieldDef[] = useMemo(() => [ + { key: 'code', type: 'input', placeholder: '搜索角色编码', width: 'w-[180px]' }, + { key: 'name', type: 'input', placeholder: '搜索角色名称', width: 'w-[180px]' }, + ], []); - const pageCount = data?.totalElements ? Math.ceil(data.totalElements / (query.pageSize || DEFAULT_PAGE_SIZE)) : 0; + // 列定义 + const columns: ColumnDef[] = useMemo(() => [ + { key: 'id', title: 'ID', dataIndex: 'id', width: '80px' }, + { + key: 'code', + title: '角色编码', + width: '150px', + render: (_, record) => {record.code}, + }, + { key: 'name', title: '角色名称', dataIndex: 'name', width: '150px' }, + { + key: 'isAdmin', + title: '类型', + width: '120px', + render: (_, record) => record.isAdmin ? ( + + + 管理员 + + ) : ( + 普通角色 + ), + }, + { + key: 'tags', + title: '标签', + width: '200px', + render: (_, record) => ( +
+ {record.tags && record.tags.length > 0 ? ( + record.tags.map(tag => ( + + {tag.name} + + )) + ) : ( + - + )} +
+ ), + }, + { key: 'sort', title: '排序', dataIndex: 'sort', width: '80px' }, + { + key: 'description', + title: '描述', + width: '200px', + render: (_, record) => ( +
+ {record.description || '-'} +
+ ), + }, + { + key: 'createTime', + title: '创建时间', + width: '180px', + render: (_, record) => record.createTime ? dayjs(record.createTime).format('YYYY-MM-DD HH:mm') : '-', + }, + { + key: 'actions', + title: '操作', + width: '200px', + sticky: true, + render: (_, record) => ( +
+ + + + {!record.isAdmin && ( + + )} +
+ ), + }, + ], []); + + // 工具栏 + const toolbar = ( +
+ + +
+ ); return (
@@ -197,188 +280,20 @@ const RolePage: React.FC = () => {
- + 角色列表 -
- - -
- - {/* 搜索栏 */} -
-
- setQuery(prev => ({ ...prev, code: e.target.value }))} - onKeyDown={(e) => e.key === 'Enter' && handleSearch()} - className="h-9" - /> -
-
- setQuery(prev => ({ ...prev, name: e.target.value }))} - onKeyDown={(e) => e.key === 'Enter' && handleSearch()} - className="h-9" - /> -
- - -
- - {/* 表格 */} -
- - - - 角色编码 - 角色名称 - 类型 - 标签 - 排序 - 描述 - 创建时间 - 操作 - - - - {loading ? ( - - -
- - 加载中... -
-
-
- ) : data?.content && data.content.length > 0 ? ( - data.content.map((record) => { - return ( - - - {record.code} - - {record.name} - - {record.isAdmin ? ( - - - 管理员 - - ) : ( - 普通角色 - )} - - -
- {record.tags && record.tags.length > 0 ? ( - record.tags.map(tag => ( - - {tag.name} - - )) - ) : ( - - - )} -
-
- {record.sort} - - {record.description || '-'} - - - {record.createTime ? dayjs(record.createTime).format('YYYY-MM-DD HH:mm') : '-'} - - -
- - - - {!record.isAdmin && ( - - )} -
-
-
- ); - }) - ) : ( - - -
- -
暂无角色数据
-
点击右上角"新建角色"开始创建角色。
-
-
-
- )} -
-
-
- - {/* 分页 */} - {pageCount > 1 && ( - setQuery(prev => ({ - ...prev, - pageNum: page - 1 - }))} - /> - )} + + + + ref={tableRef} + fetchFn={fetchData} + columns={columns} + searchFields={searchFields} + toolbar={toolbar} + rowKey="id" + minWidth="1280px" + />
@@ -387,7 +302,7 @@ const RolePage: React.FC = () => { open={editDialogOpen} record={editRecord} onOpenChange={setEditDialogOpen} - onSuccess={loadData} + onSuccess={handleSuccess} /> {/* 删除确认对话框 */} @@ -414,7 +329,7 @@ const RolePage: React.FC = () => { onOpenChange={setAssignTagDialogOpen} onSuccess={() => { setAssignTagDialogOpen(false); - loadData(); + tableRef.current?.refresh(); }} selectedTags={selectedRole.tags} /> @@ -427,7 +342,7 @@ const RolePage: React.FC = () => { onOpenChange={setTagDialogOpen} onSuccess={() => { setTagDialogOpen(false); - loadData(); + tableRef.current?.refresh(); }} />
diff --git a/frontend/src/pages/System/User/List/index.tsx b/frontend/src/pages/System/User/List/index.tsx index 861d77bb..e5437148 100644 --- a/frontend/src/pages/System/User/List/index.tsx +++ b/frontend/src/pages/System/User/List/index.tsx @@ -1,13 +1,11 @@ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect, useMemo, useRef } from 'react'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; -import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table'; +import { Separator } from '@/components/ui/separator'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { DataTablePagination } from '@/components/ui/pagination'; +import { PaginatedTable, type ColumnDef, type SearchFieldDef, type PaginatedTableRef } from '@/components/ui/paginated-table'; import { - Loader2, Plus, Search, Edit, Trash2, KeyRound, Users as UsersIcon, + Plus, Edit, Trash2, KeyRound, Users as UsersIcon, UserCheck, UserX, Activity } from 'lucide-react'; import { useToast } from '@/components/ui/use-toast'; @@ -15,8 +13,6 @@ import { getUsers, deleteUser, resetPassword, assignRoles, getAllRoles } from '. import { getDepartmentTree } from '../../Department/List/service'; import type { UserResponse, UserQuery, Role } from './types'; import type { DepartmentResponse } from '../../Department/List/types'; -import type { Page } from '@/types/base'; -import { DEFAULT_PAGE_SIZE, DEFAULT_PAGE_NUM } from '@/utils/page'; import EditModal from './components/EditModal'; import ResetPasswordDialog from './components/ResetPasswordDialog'; import AssignRolesDialog from './components/AssignRolesDialog'; @@ -28,8 +24,8 @@ import dayjs from 'dayjs'; */ const UserPage: React.FC = () => { const { toast } = useToast(); - const [loading, setLoading] = useState(false); - const [data, setData] = useState | null>(null); + const tableRef = useRef>(null); + const [stats, setStats] = useState({ total: 0, enabledCount: 0, disabledCount: 0 }); const [departments, setDepartments] = useState([]); const [allRoles, setAllRoles] = useState([]); const [editModalOpen, setEditModalOpen] = useState(false); @@ -40,25 +36,17 @@ const UserPage: React.FC = () => { const [assignRolesRecord, setAssignRolesRecord] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [deleteRecord, setDeleteRecord] = useState(null); - const [query, setQuery] = useState({ - pageNum: DEFAULT_PAGE_NUM, - pageSize: DEFAULT_PAGE_SIZE, - username: '', - email: '', - enabled: undefined, - }); - // 加载数据 - const loadData = async () => { - setLoading(true); - try { - const result = await getUsers(query); - setData(result); - } catch (error) { - console.error('加载用户列表失败:', error); - } finally { - setLoading(false); - } + // 包装 fetchFn,同时更新统计数据 + const fetchData = async (query: UserQuery) => { + const result = await getUsers(query); + const all = result.content || []; + setStats({ + total: result.totalElements || 0, + enabledCount: all.filter(d => d.enabled).length, + disabledCount: all.filter(d => !d.enabled).length, + }); + return result; }; // 加载部门和角色 @@ -79,38 +67,22 @@ const UserPage: React.FC = () => { loadDepartmentsAndRoles(); }, []); - useEffect(() => { - loadData(); - }, [query]); - - // 搜索 - const handleSearch = () => { - setQuery(prev => ({ ...prev, pageNum: 0 })); - }; - - // 重置 - const handleReset = () => { - setQuery({ - pageNum: 0, - pageSize: DEFAULT_PAGE_SIZE, - username: '', - email: '', - enabled: undefined, - }); - }; - - // 新建 const handleCreate = () => { setEditRecord(undefined); setEditModalOpen(true); }; - // 编辑 const handleEdit = (record: UserResponse) => { setEditRecord(record); setEditModalOpen(true); }; + const handleSuccess = () => { + setEditModalOpen(false); + setEditRecord(undefined); + tableRef.current?.refresh(); + }; + // 重置密码 const handleResetPassword = (record: UserResponse) => { setResetPasswordRecord(record); @@ -151,7 +123,7 @@ const UserPage: React.FC = () => { title: '分配成功', description: `已为用户 "${assignRolesRecord.username}" 分配角色`, }); - loadData(); + tableRef.current?.refresh(); setAssignRolesDialogOpen(false); setAssignRolesRecord(null); } catch (error) { @@ -178,7 +150,7 @@ const UserPage: React.FC = () => { title: '删除成功', description: `用户 "${deleteRecord.username}" 已删除`, }); - loadData(); + tableRef.current?.refresh(); setDeleteDialogOpen(false); setDeleteRecord(null); } catch (error) { @@ -191,30 +163,125 @@ const UserPage: React.FC = () => { } }; - // 状态徽章 - const getStatusBadge = (enabled: boolean) => { - return enabled ? ( - - - 启用 - - ) : ( - - - 禁用 - - ); - }; + // 搜索字段定义 + const searchFields: SearchFieldDef[] = useMemo(() => [ + { key: 'username', type: 'input', placeholder: '搜索用户名', width: 'w-[180px]' }, + { key: 'email', type: 'input', placeholder: '搜索邮箱', width: 'w-[180px]' }, + { + key: 'enabled', + type: 'select', + placeholder: '状态', + width: 'w-[120px]', + options: [ + { label: '启用', value: 'true' }, + { label: '禁用', value: 'false' }, + ], + }, + ], []); - // 统计数据 - const stats = useMemo(() => { - const total = data?.totalElements || 0; - const enabledCount = data?.content?.filter(d => d.enabled).length || 0; - const disabledCount = data?.content?.filter(d => !d.enabled).length || 0; - return { total, enabledCount, disabledCount }; - }, [data]); + // 列定义 + const columns: ColumnDef[] = useMemo(() => [ + { key: 'id', title: 'ID', dataIndex: 'id', width: '80px' }, + { key: 'username', title: '用户名', dataIndex: 'username', width: '120px' }, + { key: 'nickname', title: '昵称', dataIndex: 'nickname', width: '120px' }, + { key: 'email', title: '邮箱', dataIndex: 'email', width: '200px' }, + { key: 'phone', title: '手机号', dataIndex: 'phone', width: '120px' }, + { key: 'departmentName', title: '部门', dataIndex: 'departmentName', width: '120px' }, + { + key: 'roles', + title: '角色', + width: '150px', + render: (_, record) => ( +
+ {record.roles && record.roles.length > 0 ? ( + record.roles.map(role => ( + + {role.name} + + )) + ) : ( + - + )} +
+ ), + }, + { + key: 'enabled', + title: '状态', + width: '100px', + render: (_, record) => record.enabled ? ( + + + 启用 + + ) : ( + + + 禁用 + + ), + }, + { + key: 'createTime', + title: '创建时间', + width: '180px', + render: (_, record) => record.createTime ? dayjs(record.createTime).format('YYYY-MM-DD HH:mm') : '-', + }, + { + key: 'actions', + title: '操作', + width: '200px', + sticky: true, + render: (_, record) => { + const isAdmin = record.username === 'admin'; + return ( +
+ + + + {!isAdmin && ( + + )} +
+ ); + }, + }, + ], []); - const pageCount = data?.totalElements ? Math.ceil(data.totalElements / (query.pageSize || DEFAULT_PAGE_SIZE)) : 0; + // 工具栏 + const toolbar = ( + + ); return (
@@ -260,186 +327,20 @@ const UserPage: React.FC = () => {
- + 用户列表 - - - {/* 搜索栏 */} -
-
- setQuery(prev => ({ ...prev, username: e.target.value }))} - onKeyDown={(e) => e.key === 'Enter' && handleSearch()} - className="h-9" - /> -
-
- setQuery(prev => ({ ...prev, email: e.target.value }))} - onKeyDown={(e) => e.key === 'Enter' && handleSearch()} - className="h-9" - /> -
- - - -
- - {/* 表格 */} -
- - - - 用户名 - 昵称 - 邮箱 - 手机号 - 部门 - 角色 - 状态 - 创建时间 - 操作 - - - - {loading ? ( - - -
- - 加载中... -
-
-
- ) : data?.content && data.content.length > 0 ? ( - data.content.map((record) => { - const isAdmin = record.username === 'admin'; - return ( - - - {record.username} - - {record.nickname || '-'} - {record.email || '-'} - {record.phone || '-'} - {record.departmentName || '-'} - -
- {record.roles && record.roles.length > 0 ? ( - record.roles.map(role => ( - - {role.name} - - )) - ) : ( - - - )} -
-
- {getStatusBadge(record.enabled)} - - {record.createTime ? dayjs(record.createTime).format('YYYY-MM-DD HH:mm') : '-'} - - -
- - - - {!isAdmin && ( - - )} -
-
-
- ); - }) - ) : ( - - -
- -
暂无用户数据
-
点击右上角"新增用户"开始创建用户。
-
-
-
- )} -
-
-
- - {/* 分页 */} - {pageCount > 1 && ( - setQuery(prev => ({ - ...prev, - pageNum: page - 1 - }))} - /> - )} + + + + ref={tableRef} + fetchFn={fetchData} + columns={columns} + searchFields={searchFields} + toolbar={toolbar} + rowKey="id" + minWidth="1340px" + />
@@ -449,7 +350,7 @@ const UserPage: React.FC = () => { record={editRecord} departments={departments} onOpenChange={setEditModalOpen} - onSuccess={loadData} + onSuccess={handleSuccess} /> {/* 重置密码对话框 */}