mirror of
https://github.com/Funkoala14/KnowledgeBase_OOIN.git
synced 2025-06-08 07:28:13 +08:00
472 lines
17 KiB
JavaScript
472 lines
17 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
||
import { useDispatch } from 'react-redux';
|
||
import { get, put } from '../../../services/api';
|
||
import { showNotification } from '../../../store/notification.slice';
|
||
import UserPermissionDetails from './UserPermissionDetails';
|
||
import './UserPermissions.css';
|
||
import SvgIcon from '../../../components/SvgIcon';
|
||
|
||
// 模拟数据
|
||
const mockUsers = [
|
||
{
|
||
id: '1',
|
||
username: 'zhangsan',
|
||
name: '张三',
|
||
department: '达人组',
|
||
position: '达人对接',
|
||
permissions_count: {
|
||
read: 1,
|
||
edit: 0,
|
||
admin: 0,
|
||
},
|
||
},
|
||
{
|
||
id: '2',
|
||
username: 'lisi',
|
||
name: '李四',
|
||
department: '人力资源组',
|
||
position: 'HR',
|
||
permissions_count: {
|
||
read: 1,
|
||
edit: 0,
|
||
admin: 2,
|
||
},
|
||
},
|
||
{
|
||
id: '3',
|
||
username: 'wangwu',
|
||
name: '王五',
|
||
department: '市场部',
|
||
position: '市场专员',
|
||
permissions_count: {
|
||
read: 2,
|
||
edit: 1,
|
||
admin: 0,
|
||
},
|
||
},
|
||
{
|
||
id: '4',
|
||
username: 'zhaoliu',
|
||
name: '赵六',
|
||
department: '技术部',
|
||
position: '前端开发',
|
||
permissions_count: {
|
||
read: 3,
|
||
edit: 2,
|
||
admin: 1,
|
||
},
|
||
},
|
||
{
|
||
id: '5',
|
||
username: 'sunqi',
|
||
name: '孙七',
|
||
department: '产品部',
|
||
position: '产品经理',
|
||
permissions_count: {
|
||
read: 4,
|
||
edit: 2,
|
||
admin: 0,
|
||
},
|
||
},
|
||
{
|
||
id: '6',
|
||
username: 'zhouba',
|
||
name: '周八',
|
||
department: '设计部',
|
||
position: 'UI设计师',
|
||
permissions_count: {
|
||
read: 1,
|
||
edit: 1,
|
||
admin: 0,
|
||
},
|
||
},
|
||
{
|
||
id: '7',
|
||
username: 'wujiu',
|
||
name: '吴九',
|
||
department: '财务部',
|
||
position: '财务主管',
|
||
permissions_count: {
|
||
read: 2,
|
||
edit: 0,
|
||
admin: 3,
|
||
},
|
||
},
|
||
{
|
||
id: '8',
|
||
username: 'zhengshi',
|
||
name: '郑十',
|
||
department: '行政部',
|
||
position: '行政专员',
|
||
permissions_count: {
|
||
read: 1,
|
||
edit: 0,
|
||
admin: 0,
|
||
},
|
||
},
|
||
];
|
||
|
||
// 每页显示选项
|
||
const PAGE_SIZE_OPTIONS = [5, 10, 15, 20];
|
||
|
||
export default function UserPermissions() {
|
||
const dispatch = useDispatch();
|
||
const [users, setUsers] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState(null);
|
||
const [selectedUser, setSelectedUser] = useState(null);
|
||
const [showDetailsModal, setShowDetailsModal] = useState(false);
|
||
const [searchTerm, setSearchTerm] = useState('');
|
||
|
||
// 分页状态
|
||
const [currentPage, setCurrentPage] = useState(1);
|
||
const [pageSize, setPageSize] = useState(5);
|
||
const [totalPages, setTotalPages] = useState(1);
|
||
|
||
// 获取用户列表
|
||
useEffect(() => {
|
||
const fetchUsers = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const response = await get('/users/permissions/');
|
||
|
||
if (response && response.code === 200) {
|
||
// 只有当API返回的用户列表为空时才使用模拟数据
|
||
const apiUsers = response.data.users || [];
|
||
if (apiUsers.length > 0) {
|
||
setUsers(apiUsers);
|
||
} else {
|
||
console.log('API返回的用户列表为空,使用模拟数据');
|
||
setUsers(mockUsers);
|
||
}
|
||
} else {
|
||
// API请求失败,使用模拟数据
|
||
console.log('API请求失败,使用模拟数据');
|
||
setUsers(mockUsers);
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户列表失败:', error);
|
||
setError('获取用户列表失败');
|
||
// API请求出错,使用模拟数据作为后备
|
||
console.log('API请求出错,使用模拟数据作为后备');
|
||
setUsers(mockUsers);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
fetchUsers();
|
||
}, []);
|
||
|
||
// 当用户列表或搜索词变化时,更新总页数
|
||
useEffect(() => {
|
||
const filteredUsers = getFilteredUsers();
|
||
setTotalPages(Math.ceil(filteredUsers.length / pageSize));
|
||
// 如果当前页超出了新的总页数,则重置为第一页
|
||
if (currentPage > Math.ceil(filteredUsers.length / pageSize)) {
|
||
setCurrentPage(1);
|
||
}
|
||
}, [users, searchTerm, pageSize]);
|
||
|
||
// 打开用户权限详情弹窗
|
||
const handleOpenDetailsModal = (user) => {
|
||
setSelectedUser(user);
|
||
setShowDetailsModal(true);
|
||
};
|
||
|
||
// 关闭用户权限详情弹窗
|
||
const handleCloseDetailsModal = () => {
|
||
setShowDetailsModal(false);
|
||
setSelectedUser(null);
|
||
};
|
||
|
||
// 保存用户权限更改
|
||
const handleSavePermissions = async (userId, updatedPermissions) => {
|
||
try {
|
||
const response = await put(`/users/${userId}/permissions/`, {
|
||
permissions: updatedPermissions,
|
||
});
|
||
|
||
if (response && response.code === 200) {
|
||
// 更新用户列表中的权限信息
|
||
setUsers(
|
||
users.map((user) => {
|
||
if (user.id === userId) {
|
||
return {
|
||
...user,
|
||
permissions: {
|
||
...user.permissions,
|
||
...response.data.permissions,
|
||
},
|
||
};
|
||
}
|
||
return user;
|
||
})
|
||
);
|
||
|
||
dispatch(
|
||
showNotification({
|
||
message: '权限更新成功',
|
||
type: 'success',
|
||
})
|
||
);
|
||
|
||
// 关闭弹窗
|
||
handleCloseDetailsModal();
|
||
} else {
|
||
dispatch(
|
||
showNotification({
|
||
message: '权限更新失败',
|
||
type: 'danger',
|
||
})
|
||
);
|
||
}
|
||
} catch (error) {
|
||
console.error('权限更新失败:', error);
|
||
dispatch(
|
||
showNotification({
|
||
message: '权限更新失败',
|
||
type: 'danger',
|
||
})
|
||
);
|
||
}
|
||
};
|
||
|
||
// 处理搜索输入变化
|
||
const handleSearchChange = (e) => {
|
||
setSearchTerm(e.target.value);
|
||
setCurrentPage(1); // 重置为第一页
|
||
};
|
||
|
||
// 处理页码变化
|
||
const handlePageChange = (page) => {
|
||
setCurrentPage(page);
|
||
};
|
||
|
||
// 处理每页显示数量变化
|
||
const handlePageSizeChange = (e) => {
|
||
const newPageSize = parseInt(e.target.value);
|
||
setPageSize(newPageSize);
|
||
setCurrentPage(1); // 重置为第一页
|
||
};
|
||
|
||
// 获取过滤后的用户列表
|
||
const getFilteredUsers = () => {
|
||
if (!searchTerm.trim()) return users;
|
||
|
||
return users.filter(
|
||
(user) =>
|
||
user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||
user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||
user.department.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||
user.position.toLowerCase().includes(searchTerm.toLowerCase())
|
||
);
|
||
};
|
||
|
||
// 获取当前页的数据
|
||
const getCurrentPageData = () => {
|
||
const filteredUsers = getFilteredUsers();
|
||
const startIndex = (currentPage - 1) * pageSize;
|
||
const endIndex = startIndex + pageSize;
|
||
return filteredUsers.slice(startIndex, endIndex);
|
||
};
|
||
|
||
// 渲染分页控件
|
||
const renderPagination = () => {
|
||
if (totalPages <= 1) return null;
|
||
|
||
return (
|
||
<div className='d-flex justify-content-center align-items-center mt-4'>
|
||
<nav aria-label='用户权限分页'>
|
||
<ul className='pagination'>
|
||
<li className={`page-item ${currentPage === 1 ? 'disabled' : ''}`}>
|
||
<button
|
||
className='page-link'
|
||
onClick={() => handlePageChange(currentPage - 1)}
|
||
disabled={currentPage === 1}
|
||
>
|
||
上一页
|
||
</button>
|
||
</li>
|
||
<li className='page-item active'>
|
||
<span className='page-link'>
|
||
{currentPage} / {totalPages}
|
||
</span>
|
||
</li>
|
||
<li className={`page-item ${currentPage === totalPages ? 'disabled' : ''}`}>
|
||
<button
|
||
className='page-link'
|
||
onClick={() => handlePageChange(currentPage + 1)}
|
||
disabled={currentPage === totalPages}
|
||
>
|
||
下一页
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
|
||
<div className='ms-3'>
|
||
<select
|
||
className='form-select form-select-sm'
|
||
value={pageSize}
|
||
onChange={handlePageSizeChange}
|
||
aria-label='每页显示数量'
|
||
>
|
||
{PAGE_SIZE_OPTIONS.map((size) => (
|
||
<option key={size} value={size}>
|
||
{size}条/页
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// 渲染加载状态
|
||
if (loading && users.length === 0) {
|
||
return (
|
||
<div className='text-center py-5'>
|
||
<div className='spinner-border' role='status'>
|
||
<span className='visually-hidden'>加载中...</span>
|
||
</div>
|
||
<p className='mt-3'>加载用户列表...</p>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 渲染错误状态
|
||
if (error && users.length === 0) {
|
||
return (
|
||
<div className='alert alert-danger' role='alert'>
|
||
{error}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 渲染空状态
|
||
if (users.length === 0) {
|
||
return (
|
||
<div className='alert alert-info' role='alert'>
|
||
暂无用户数据
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 获取当前页的数据
|
||
const currentPageData = getCurrentPageData();
|
||
|
||
// 获取过滤后的总用户数
|
||
const filteredUsersCount = getFilteredUsers().length;
|
||
|
||
// 渲染用户列表
|
||
return (
|
||
<>
|
||
<div className='d-flex justify-content-between align-items-center mb-3'>
|
||
<h5 className='mb-0'>用户权限管理</h5>
|
||
<div className='input-group' style={{ maxWidth: '250px' }}>
|
||
<input
|
||
type='text'
|
||
className='form-control'
|
||
placeholder='搜索用户...'
|
||
value={searchTerm}
|
||
onChange={handleSearchChange}
|
||
/>
|
||
<span className='input-group-text'>
|
||
<i className='bi bi-search'></i>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{filteredUsersCount > 0 ? (
|
||
<>
|
||
<div className='card'>
|
||
<div className='table-responsive'>
|
||
<table className='table table-hover mb-0'>
|
||
<thead className='table-light'>
|
||
<tr>
|
||
<th scope='col'>用户</th>
|
||
<th scope='col'>部门</th>
|
||
<th scope='col'>职位</th>
|
||
<th scope='col'>数据集权限</th>
|
||
<th scope='col' className='text-end'>
|
||
操作
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{currentPageData.map((user) => (
|
||
<tr key={user.id}>
|
||
<td>
|
||
<div className='d-flex align-items-center'>
|
||
<div
|
||
className='bg-dark rounded-circle text-white d-flex align-items-center justify-content-center me-2'
|
||
style={{ width: '36px', height: '36px' }}
|
||
>
|
||
{user.name.charAt(0)}
|
||
</div>
|
||
<div>
|
||
<div className='fw-medium'>{user.name}</div>
|
||
<div className='text-muted small'>{user.username}</div>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td className='align-middle'>{user.department}</td>
|
||
<td className='align-middle'>{user.position}</td>
|
||
<td className='align-middle'>
|
||
{user.permissions_count && (
|
||
<div className='d-flex flex-wrap gap-1'>
|
||
{user.permissions_count.read > 0 && (
|
||
<span className='badge bg-success-subtle text-success d-flex align-items-center gap-1'>
|
||
完全访问: {user.permissions_count.read}
|
||
</span>
|
||
)}
|
||
{user.permissions_count.edit > 0 && (
|
||
<span className='badge bg-warning-subtle text-warning d-flex align-items-center gap-1'>
|
||
只读访问: {user.permissions_count.edit}
|
||
</span>
|
||
)}
|
||
{user.permissions_count.admin > 0 && (
|
||
<span className='badge bg-danger d-flex align-items-center gap-1'>
|
||
无访问权限: {user.permissions_count.admin}
|
||
</span>
|
||
)}
|
||
</div>
|
||
)}
|
||
</td>
|
||
<td className='text-end align-middle'>
|
||
<button
|
||
className='btn btn-outline-dark btn-sm'
|
||
onClick={() => handleOpenDetailsModal(user)}
|
||
>
|
||
修改权限
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 分页控件 */}
|
||
{renderPagination()}
|
||
</>
|
||
) : (
|
||
<div className='alert alert-info' role='alert'>
|
||
没有找到匹配的用户
|
||
</div>
|
||
)}
|
||
|
||
{/* 用户权限详情弹窗 */}
|
||
{showDetailsModal && selectedUser && (
|
||
<UserPermissionDetails
|
||
user={selectedUser}
|
||
onClose={handleCloseDetailsModal}
|
||
onSave={handleSavePermissions}
|
||
/>
|
||
)}
|
||
</>
|
||
);
|
||
}
|