KnowledgeBase_frontend/src/pages/Permissions/components/UserPermissions.jsx

472 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}
/>
)}
</>
);
}