mirror of
https://github.com/Funkoala14/KnowledgeBase_OOIN.git
synced 2025-06-08 07:20:55 +08:00
[dev]permissions page
This commit is contained in:
parent
1ba460b4cf
commit
cc0f56a5d4
12
src/App.jsx
12
src/App.jsx
@ -25,14 +25,14 @@ function App() {
|
|||||||
|
|
||||||
// 如果用户已认证但WebSocket未连接,则初始化连接
|
// 如果用户已认证但WebSocket未连接,则初始化连接
|
||||||
if (user && !isConnected) {
|
if (user && !isConnected) {
|
||||||
initWebSocket()
|
// initWebSocket()
|
||||||
.then(() => {
|
// .then(() => {
|
||||||
// dispatch(setWebSocketConnected(true));
|
// dispatch(setWebSocketConnected(true));
|
||||||
// console.log('WebSocket connection initialized');
|
// console.log('WebSocket connection initialized');
|
||||||
})
|
// })
|
||||||
.catch((error) => {
|
// .catch((error) => {
|
||||||
console.error('Failed to initialize WebSocket connection:', error);
|
// console.error('Failed to initialize WebSocket connection:', error);
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件卸载或用户登出时关闭WebSocket连接
|
// 组件卸载或用户登出时关闭WebSocket连接
|
||||||
|
@ -60,7 +60,7 @@ const CreateKnowledgeBaseModal = ({
|
|||||||
const getAvailableTypes = () => {
|
const getAvailableTypes = () => {
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
return [
|
return [
|
||||||
{ value: 'admin', label: 'Admin 级知识库' },
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'leader', label: 'Leader 级知识库' },
|
{ value: 'leader', label: 'Leader 级知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: 'Member 级知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
@ -68,11 +68,15 @@ const CreateKnowledgeBaseModal = ({
|
|||||||
];
|
];
|
||||||
} else if (isLeader) {
|
} else if (isLeader) {
|
||||||
return [
|
return [
|
||||||
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: 'Member 级知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return [{ value: 'private', label: '私有知识库' }];
|
return [
|
||||||
|
{ value: 'admin', label: '公共知识库' },
|
||||||
|
{ value: 'private', label: '私有知识库' },
|
||||||
|
];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -169,7 +173,7 @@ const CreateKnowledgeBaseModal = ({
|
|||||||
</div>
|
</div>
|
||||||
{!isAdmin && !isLeader && (
|
{!isAdmin && !isLeader && (
|
||||||
<small className='text-muted d-block mt-1'>
|
<small className='text-muted d-block mt-1'>
|
||||||
注意:您当前只能创建私有知识库。其他类型需要更高权限。
|
您可以创建公共知识库(所有人可访问)或私有知识库(仅自己可访问)。
|
||||||
</small>
|
</small>
|
||||||
)}
|
)}
|
||||||
{formErrors.type && <div className='text-danger small mt-1'>{formErrors.type}</div>}
|
{formErrors.type && <div className='text-danger small mt-1'>{formErrors.type}</div>}
|
||||||
|
@ -173,6 +173,17 @@ export default function SettingsTab({ knowledgeBase }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 所有用户都可以修改为admin或private类型,无需额外检查
|
||||||
|
if (newType !== 'admin' && newType !== 'private' && currentUser.role === 'member') {
|
||||||
|
dispatch(
|
||||||
|
showNotification({
|
||||||
|
message: '您只能将知识库修改为公共(admin)或私有(private)类型',
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAdmin && !validateForm()) {
|
if (isAdmin && !validateForm()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ const KnowledgeBaseForm = ({
|
|||||||
const getAvailableTypes = () => {
|
const getAvailableTypes = () => {
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
return [
|
return [
|
||||||
{ value: 'admin', label: 'Admin 级知识库' },
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'leader', label: 'Leader 级知识库' },
|
{ value: 'leader', label: 'Leader 级知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: 'Member 级知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
@ -34,11 +34,15 @@ const KnowledgeBaseForm = ({
|
|||||||
];
|
];
|
||||||
} else if (isLeader) {
|
} else if (isLeader) {
|
||||||
return [
|
return [
|
||||||
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: 'Member 级知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return [{ value: 'private', label: '私有知识库' }];
|
return [
|
||||||
|
{ value: 'admin', label: '公共知识库' },
|
||||||
|
{ value: 'private', label: '私有知识库' },
|
||||||
|
];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,7 +110,7 @@ const KnowledgeBaseForm = ({
|
|||||||
value={type.value}
|
value={type.value}
|
||||||
checked={formData.type === type.value}
|
checked={formData.type === type.value}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
disabled={currentUser?.role === 'member'} // 普通成员无法修改类型
|
disabled={false} // 允许所有用户修改类型
|
||||||
/>
|
/>
|
||||||
<label className='form-check-label' htmlFor={`type${type.value}`}>
|
<label className='form-check-label' htmlFor={`type${type.value}`}>
|
||||||
{type.label}
|
{type.label}
|
||||||
@ -115,7 +119,7 @@ const KnowledgeBaseForm = ({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{currentUser?.role === 'member' && (
|
{currentUser?.role === 'member' && (
|
||||||
<small className='text-muted d-block mt-1'>您当前无权修改知识库类型。</small>
|
<small className='text-muted d-block mt-1'>您可以修改知识库类型为公共或私有。</small>
|
||||||
)}
|
)}
|
||||||
{formErrors.type && <div className='text-danger small mt-1'>{formErrors.type}</div>}
|
{formErrors.type && <div className='text-danger small mt-1'>{formErrors.type}</div>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -437,8 +437,8 @@ export default function KnowledgeBase() {
|
|||||||
const isAdmin = currentUser?.role === 'admin';
|
const isAdmin = currentUser?.role === 'admin';
|
||||||
const isLeader = currentUser?.role === 'leader';
|
const isLeader = currentUser?.role === 'leader';
|
||||||
|
|
||||||
// 默认知识库类型基于用户角色
|
// 默认知识库类型设为公共知识库
|
||||||
let defaultType = 'private';
|
let defaultType = 'admin';
|
||||||
|
|
||||||
// 初始部门和组别
|
// 初始部门和组别
|
||||||
let department = currentUser?.department || '';
|
let department = currentUser?.department || '';
|
||||||
|
@ -12,9 +12,6 @@ import './PendingRequests.css'; // 引入外部CSS文件
|
|||||||
import SvgIcon from '../../../components/SvgIcon';
|
import SvgIcon from '../../../components/SvgIcon';
|
||||||
import RequestDetailSlideOver from './RequestDetailSlideOver';
|
import RequestDetailSlideOver from './RequestDetailSlideOver';
|
||||||
|
|
||||||
// 每页显示的申请数量
|
|
||||||
const PAGE_SIZE = 5;
|
|
||||||
|
|
||||||
export default function PendingRequests() {
|
export default function PendingRequests() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -25,14 +22,18 @@ export default function PendingRequests() {
|
|||||||
const [selectedRequest, setSelectedRequest] = useState(null);
|
const [selectedRequest, setSelectedRequest] = useState(null);
|
||||||
const [showSlideOver, setShowSlideOver] = useState(false);
|
const [showSlideOver, setShowSlideOver] = useState(false);
|
||||||
|
|
||||||
// 使用本地状态管理待处理申请列表
|
// 从Redux store获取权限申请列表状态
|
||||||
const [pendingRequests, setPendingRequests] = useState([]);
|
const {
|
||||||
const [fetchStatus, setFetchStatus] = useState('idle');
|
results: pendingRequests,
|
||||||
const [fetchError, setFetchError] = useState(null);
|
status: fetchStatus,
|
||||||
|
error: fetchError,
|
||||||
|
page: currentPage,
|
||||||
|
page_size: pageSize,
|
||||||
|
total,
|
||||||
|
} = useSelector((state) => state.permissions.pending);
|
||||||
|
|
||||||
// 分页状态
|
// 计算总页数
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const totalPages = Math.ceil(total / pageSize) || 1;
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
|
||||||
|
|
||||||
// 从Redux store获取批准/拒绝操作的状态
|
// 从Redux store获取批准/拒绝操作的状态
|
||||||
const {
|
const {
|
||||||
@ -43,24 +44,8 @@ export default function PendingRequests() {
|
|||||||
|
|
||||||
// 获取待处理申请列表
|
// 获取待处理申请列表
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
dispatch(fetchPermissionsThunk({ page: currentPage, page_size: pageSize }));
|
||||||
try {
|
}, [dispatch, currentPage, pageSize]);
|
||||||
setFetchStatus('loading');
|
|
||||||
const result = await dispatch(fetchPermissionsThunk());
|
|
||||||
if (result.payload) {
|
|
||||||
setPendingRequests(result.payload);
|
|
||||||
setTotalPages(Math.ceil(result.payload.length / PAGE_SIZE));
|
|
||||||
}
|
|
||||||
setFetchStatus('succeeded');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取待处理申请列表失败:', error);
|
|
||||||
setFetchError(error.message || '获取待处理申请列表失败');
|
|
||||||
setFetchStatus('failed');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchData();
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
// 处理从通知中心跳转过来的请求
|
// 处理从通知中心跳转过来的请求
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -88,17 +73,8 @@ export default function PendingRequests() {
|
|||||||
setShowSlideOver(false);
|
setShowSlideOver(false);
|
||||||
setSelectedRequest(null);
|
setSelectedRequest(null);
|
||||||
|
|
||||||
// 从列表中移除已处理的申请
|
// 重新获取申请列表
|
||||||
const updatedRequests = pendingRequests.filter((req) => req.id !== currentRequestId);
|
dispatch(fetchPermissionsThunk({ page: currentPage, page_size: pageSize }));
|
||||||
setPendingRequests(updatedRequests);
|
|
||||||
|
|
||||||
// 更新总页数
|
|
||||||
setTotalPages(Math.ceil(updatedRequests.length / PAGE_SIZE));
|
|
||||||
|
|
||||||
// 如果当前页没有数据了,且不是第一页,则回到上一页
|
|
||||||
if (getCurrentPageData().length === 1 && currentPage > 1) {
|
|
||||||
setCurrentPage(currentPage - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置状态
|
// 重置状态
|
||||||
dispatch(resetOperationStatus());
|
dispatch(resetOperationStatus());
|
||||||
@ -112,15 +88,7 @@ export default function PendingRequests() {
|
|||||||
// 重置状态
|
// 重置状态
|
||||||
dispatch(resetOperationStatus());
|
dispatch(resetOperationStatus());
|
||||||
}
|
}
|
||||||
}, [
|
}, [approveRejectStatus, approveRejectError, dispatch, isApproving, currentPage, pageSize]);
|
||||||
approveRejectStatus,
|
|
||||||
approveRejectError,
|
|
||||||
dispatch,
|
|
||||||
isApproving,
|
|
||||||
currentRequestId,
|
|
||||||
pendingRequests,
|
|
||||||
currentPage,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 打开回复输入框
|
// 打开回复输入框
|
||||||
const handleOpenResponseInput = (requestId, approving) => {
|
const handleOpenResponseInput = (requestId, approving) => {
|
||||||
@ -183,16 +151,9 @@ export default function PendingRequests() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前页的数据
|
|
||||||
const getCurrentPageData = () => {
|
|
||||||
const startIndex = (currentPage - 1) * PAGE_SIZE;
|
|
||||||
const endIndex = startIndex + PAGE_SIZE;
|
|
||||||
return Array.isArray(pendingRequests) ? pendingRequests.slice(startIndex, endIndex) : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理页码变化
|
// 处理页码变化
|
||||||
const handlePageChange = (page) => {
|
const handlePageChange = (page) => {
|
||||||
setCurrentPage(page);
|
dispatch(fetchPermissionsThunk({ page, page_size: pageSize }));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 渲染分页控件
|
// 渲染分页控件
|
||||||
@ -254,29 +215,27 @@ export default function PendingRequests() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前页的数据
|
|
||||||
const currentPageData = getCurrentPageData();
|
|
||||||
|
|
||||||
// 渲染申请列表
|
// 渲染申请列表
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='d-flex justify-content-between align-items-center mb-3'>
|
<div className='d-flex justify-content-between align-items-center mb-3'>
|
||||||
<h5 className='mb-0'>待处理申请</h5>
|
<h5 className='mb-0'>待处理申请</h5>
|
||||||
<div className='badge bg-danger'>{pendingRequests.length}个待处理</div>
|
<div className='badge bg-danger'>{total}个待处理</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='pending-requests-list'>
|
<div className='pending-requests-list'>
|
||||||
{currentPageData.map((request) => (
|
{pendingRequests.map((request) => (
|
||||||
<div key={request.id} className='pending-request-item' onClick={() => handleRowClick(request)}>
|
<div key={request.id} className='pending-request-item' onClick={() => handleRowClick(request)}>
|
||||||
<div className='request-header'>
|
<div className='request-header'>
|
||||||
<div className='user-info'>
|
<div className='user-info'>
|
||||||
<h6 className='mb-0'>{request.applicant}</h6>
|
<h6 className='mb-0'>{request.applicant.name || request.applicant.username}</h6>
|
||||||
|
<small className='text-muted'>{request.applicant.department || '未分配部门'}</small>
|
||||||
</div>
|
</div>
|
||||||
<div className='request-date'>{new Date(request.created_at).toLocaleDateString()}</div>
|
<div className='request-date'>{new Date(request.created_at).toLocaleDateString()}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='request-content'>
|
<div className='request-content'>
|
||||||
<p className='mb-2'>申请访问:{request.knowledge_base}</p>
|
<p className='mb-2'>申请访问:{request.knowledge_base.name}</p>
|
||||||
|
|
||||||
{request.permissions.can_edit ? (
|
{request.permissions.can_edit ? (
|
||||||
<span
|
<span
|
||||||
@ -297,6 +256,14 @@ export default function PendingRequests() {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{request.expires_at && (
|
||||||
|
<div className='mt-2'>
|
||||||
|
<small className='text-muted'>
|
||||||
|
到期时间: {new Date(request.expires_at).toLocaleDateString()}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className='request-actions'>
|
<div className='request-actions'>
|
||||||
<button
|
<button
|
||||||
@ -346,7 +313,7 @@ export default function PendingRequests() {
|
|||||||
{/* 回复输入弹窗 */}
|
{/* 回复输入弹窗 */}
|
||||||
{showResponseInput && (
|
{showResponseInput && (
|
||||||
<div className='modal fade show' style={{ display: 'block' }} tabIndex='-1'>
|
<div className='modal fade show' style={{ display: 'block' }} tabIndex='-1'>
|
||||||
<div className='modal-dialog'>
|
<div className='modal-dialog' style={{ zIndex: 9999 }}>
|
||||||
<div className='modal-content'>
|
<div className='modal-content'>
|
||||||
<div className='modal-header'>
|
<div className='modal-header'>
|
||||||
<h5 className='modal-title'>{isApproving ? '批准' : '拒绝'}申请</h5>
|
<h5 className='modal-title'>{isApproving ? '批准' : '拒绝'}申请</h5>
|
||||||
|
@ -13,9 +13,13 @@ export default function RequestDetailSlideOver({
|
|||||||
}) {
|
}) {
|
||||||
if (!request) return null;
|
if (!request) return null;
|
||||||
|
|
||||||
// 从通知数据中获取申请人信息
|
// 获取申请人信息
|
||||||
const applicant = request.applicant || request.title || '未知用户';
|
const applicantName = request.applicant?.name || request.applicant?.username || '未知用户';
|
||||||
const applicantInitial = applicant.charAt(0);
|
const applicantDept = request.applicant?.department || '未分配部门';
|
||||||
|
const applicantInitial = applicantName.charAt(0);
|
||||||
|
const knowledgeBaseName = request.knowledge_base?.name || '未知知识库';
|
||||||
|
const knowledgeBaseId = request.knowledge_base?.id || '';
|
||||||
|
const knowledgeBaseType = request.knowledge_base?.type || '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -32,7 +36,8 @@ export default function RequestDetailSlideOver({
|
|||||||
<div className='d-flex align-items-center mb-3'>
|
<div className='d-flex align-items-center mb-3'>
|
||||||
<div className='avatar-placeholder me-3 bg-dark'>{applicantInitial}</div>
|
<div className='avatar-placeholder me-3 bg-dark'>{applicantInitial}</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 className='mb-1'>{applicant}</h5>
|
<h5 className='mb-1'>{applicantName}</h5>
|
||||||
|
<div className='text-muted'>{applicantDept}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -40,8 +45,16 @@ export default function RequestDetailSlideOver({
|
|||||||
<div className='mb-4'>
|
<div className='mb-4'>
|
||||||
<h6 className='text-muted mb-2'>知识库信息</h6>
|
<h6 className='text-muted mb-2'>知识库信息</h6>
|
||||||
<p className='mb-1'>
|
<p className='mb-1'>
|
||||||
<strong>ID:</strong> {request.knowledge_base || request.content || '未知知识库'}
|
<strong>名称:</strong> {knowledgeBaseName}
|
||||||
</p>
|
</p>
|
||||||
|
<p className='mb-1'>
|
||||||
|
<strong>ID:</strong> {knowledgeBaseId}
|
||||||
|
</p>
|
||||||
|
{knowledgeBaseType && (
|
||||||
|
<p className='mb-1'>
|
||||||
|
<strong>类型:</strong> {knowledgeBaseType}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mb-4'>
|
<div className='mb-4'>
|
||||||
@ -49,12 +62,20 @@ export default function RequestDetailSlideOver({
|
|||||||
<div className='d-flex flex-wrap gap-2 mb-3'>
|
<div className='d-flex flex-wrap gap-2 mb-3'>
|
||||||
{request.permissions?.can_read && !request.permissions?.can_edit && (
|
{request.permissions?.can_read && !request.permissions?.can_edit && (
|
||||||
<span className='badge bg-warning-subtle text-warning d-flex align-items-center gap-1'>
|
<span className='badge bg-warning-subtle text-warning d-flex align-items-center gap-1'>
|
||||||
|
<SvgIcon className={'eye'} />
|
||||||
只读
|
只读
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{request.permissions?.can_edit && request.permissions?.can_delete && (
|
{request.permissions?.can_edit && (
|
||||||
<span className='badge bg-success-subtle text-success d-flex align-items-center gap-1'>
|
<span className='badge bg-success-subtle text-success d-flex align-items-center gap-1'>
|
||||||
完全访问
|
<SvgIcon className={'pencil'} />
|
||||||
|
编辑
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{request.permissions?.can_delete && (
|
||||||
|
<span className='badge bg-danger-subtle text-danger d-flex align-items-center gap-1'>
|
||||||
|
<SvgIcon className={'trash'} />
|
||||||
|
删除
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -72,51 +93,84 @@ export default function RequestDetailSlideOver({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{request.status && (
|
||||||
|
<div className='mb-4'>
|
||||||
|
<h6 className='text-muted mb-2'>申请状态</h6>
|
||||||
|
<p className='mb-1'>
|
||||||
|
{request.status === 'pending' ? (
|
||||||
|
<span className='badge bg-warning'>待处理</span>
|
||||||
|
) : request.status === 'approved' ? (
|
||||||
|
<span className='badge bg-success'>已批准</span>
|
||||||
|
) : (
|
||||||
|
<span className='badge bg-danger'>已拒绝</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{request.approver && (
|
||||||
|
<div className='mb-4'>
|
||||||
|
<h6 className='text-muted mb-2'>审批人</h6>
|
||||||
|
<p className='mb-1'>
|
||||||
|
{request.approver.name || request.approver.username} ({request.approver.department || '未分配部门'})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{request.response_message && (
|
||||||
|
<div className='mb-4'>
|
||||||
|
<h6 className='text-muted mb-2'>审批意见</h6>
|
||||||
|
<div className='p-3 bg-light rounded'>{request.response_message}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className='mb-4'>
|
<div className='mb-4'>
|
||||||
<h6 className='text-muted mb-2'>申请理由</h6>
|
<h6 className='text-muted mb-2'>申请理由</h6>
|
||||||
<div className='p-3 bg-light rounded'>
|
<div className='p-3 bg-light rounded'>
|
||||||
{request.reason || request.content || '无申请理由'}
|
{request.reason || '无申请理由'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='slide-over-footer'>
|
{request.status === 'pending' && (
|
||||||
<button
|
<div className='slide-over-footer'>
|
||||||
className='btn btn-outline-danger me-2'
|
<button
|
||||||
onClick={() => onReject(request.id)}
|
className='btn btn-outline-danger me-2'
|
||||||
disabled={processingId === request.id && approveRejectStatus === 'loading'}
|
onClick={() => onReject(request.id)}
|
||||||
>
|
disabled={processingId === request.id && approveRejectStatus === 'loading'}
|
||||||
{processingId === request.id && approveRejectStatus === 'loading' && !isApproving ? (
|
>
|
||||||
<>
|
{processingId === request.id && approveRejectStatus === 'loading' && !isApproving ? (
|
||||||
<span
|
<>
|
||||||
className='spinner-border spinner-border-sm me-1'
|
<span
|
||||||
role='status'
|
className='spinner-border spinner-border-sm me-1'
|
||||||
aria-hidden='true'
|
role='status'
|
||||||
></span>
|
aria-hidden='true'
|
||||||
处理中...
|
></span>
|
||||||
</>
|
处理中...
|
||||||
) : (
|
</>
|
||||||
<>拒绝</>
|
) : (
|
||||||
)}
|
<>拒绝</>
|
||||||
</button>
|
)}
|
||||||
<button
|
</button>
|
||||||
className='btn btn-outline-success'
|
<button
|
||||||
onClick={() => onApprove(request.id)}
|
className='btn btn-outline-success'
|
||||||
disabled={processingId === request.id && approveRejectStatus === 'loading'}
|
onClick={() => onApprove(request.id)}
|
||||||
>
|
disabled={processingId === request.id && approveRejectStatus === 'loading'}
|
||||||
{processingId === request.id && approveRejectStatus === 'loading' && isApproving ? (
|
>
|
||||||
<>
|
{processingId === request.id && approveRejectStatus === 'loading' && isApproving ? (
|
||||||
<span
|
<>
|
||||||
className='spinner-border spinner-border-sm me-1'
|
<span
|
||||||
role='status'
|
className='spinner-border spinner-border-sm me-1'
|
||||||
aria-hidden='true'
|
role='status'
|
||||||
></span>
|
aria-hidden='true'
|
||||||
处理中...
|
></span>
|
||||||
</>
|
处理中...
|
||||||
) : (
|
</>
|
||||||
<>批准</>
|
) : (
|
||||||
)}
|
<>批准</>
|
||||||
</button>
|
)}
|
||||||
</div>
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,120 +1,93 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { get } from '../../../services/api';
|
import { get, post } from '../../../services/api';
|
||||||
|
|
||||||
// 模拟用户权限数据
|
|
||||||
const mockUserPermissions = [
|
|
||||||
{
|
|
||||||
knowledge_base: {
|
|
||||||
id: '1',
|
|
||||||
name: '达人直播数据报告',
|
|
||||||
department: '达人组',
|
|
||||||
},
|
|
||||||
permission: {
|
|
||||||
can_read: true,
|
|
||||||
can_edit: true,
|
|
||||||
can_admin: false,
|
|
||||||
},
|
|
||||||
last_access_time: '2024-03-10T14:30:00Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
knowledge_base: {
|
|
||||||
id: '2',
|
|
||||||
name: '人力资源政策文件',
|
|
||||||
department: '人力资源组',
|
|
||||||
},
|
|
||||||
permission: {
|
|
||||||
can_read: true,
|
|
||||||
can_edit: false,
|
|
||||||
can_admin: false,
|
|
||||||
},
|
|
||||||
last_access_time: '2024-03-08T09:15:00Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
knowledge_base: {
|
|
||||||
id: '3',
|
|
||||||
name: '市场分析报告',
|
|
||||||
department: '市场部',
|
|
||||||
},
|
|
||||||
permission: {
|
|
||||||
can_read: true,
|
|
||||||
can_edit: false,
|
|
||||||
can_admin: false,
|
|
||||||
},
|
|
||||||
last_access_time: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
knowledge_base: {
|
|
||||||
id: '4',
|
|
||||||
name: '产品规划文档',
|
|
||||||
department: '产品部',
|
|
||||||
},
|
|
||||||
permission: {
|
|
||||||
can_read: true,
|
|
||||||
can_edit: true,
|
|
||||||
can_admin: true,
|
|
||||||
},
|
|
||||||
last_access_time: '2024-03-15T11:20:00Z',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function UserPermissionDetails({ user, onClose, onSave }) {
|
export default function UserPermissionDetails({ user, onClose, onSave }) {
|
||||||
|
const [updatedPermissions, setUpdatedPermissions] = useState({});
|
||||||
const [userPermissions, setUserPermissions] = useState([]);
|
const [userPermissions, setUserPermissions] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [updatedPermissions, setUpdatedPermissions] = useState({});
|
const [savingPermissions, setSavingPermissions] = useState(false);
|
||||||
|
const [successMessage, setSuccessMessage] = useState(null);
|
||||||
|
|
||||||
// 获取用户权限详情
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUserPermissions = async () => {
|
// If we already have the permissions data in the user object, use it
|
||||||
try {
|
if (user.permissions && Array.isArray(user.permissions)) {
|
||||||
setLoading(true);
|
setUserPermissions(user.permissions);
|
||||||
const response = await get(`/users/${user.user.id}/permissions/`);
|
setLoading(false);
|
||||||
|
} else {
|
||||||
if (response && response.code === 200) {
|
// Otherwise fetch the detailed permissions
|
||||||
// 检查API返回的数据
|
|
||||||
const apiPermissions = response.data.permissions || [];
|
|
||||||
if (apiPermissions.length > 0) {
|
|
||||||
// 使用API返回的数据
|
|
||||||
setUserPermissions(apiPermissions);
|
|
||||||
console.log('使用API返回的用户权限数据');
|
|
||||||
} else {
|
|
||||||
// API返回的数据为空,使用模拟数据
|
|
||||||
console.log('API返回的用户权限数据为空,使用模拟数据');
|
|
||||||
setUserPermissions(mockUserPermissions);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// API请求失败,使用模拟数据
|
|
||||||
console.log('API请求失败,使用模拟数据');
|
|
||||||
setUserPermissions(mockUserPermissions);
|
|
||||||
setError('获取用户权限详情失败,显示模拟数据');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取用户权限详情失败:', error);
|
|
||||||
// API请求出错,使用模拟数据作为后备
|
|
||||||
console.log('API请求出错,使用模拟数据作为后备');
|
|
||||||
setUserPermissions(mockUserPermissions);
|
|
||||||
setError('获取用户权限详情失败,显示模拟数据');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
fetchUserPermissions();
|
fetchUserPermissions();
|
||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
// 处理权限变更
|
const fetchUserPermissions = async () => {
|
||||||
const handlePermissionChange = (knowledgeBaseId, permissionType) => {
|
setLoading(true);
|
||||||
setUpdatedPermissions({
|
try {
|
||||||
...updatedPermissions,
|
const response = await get(`/api/users/${user.user_info.id}/permissions`);
|
||||||
[knowledgeBaseId]: permissionType,
|
setUserPermissions(response.permissions || []);
|
||||||
});
|
setError(null);
|
||||||
|
} catch (err) {
|
||||||
|
setError('获取用户权限详情失败: ' + (err.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理保存
|
const handlePermissionChange = (knowledgeBaseId, newPermissionType) => {
|
||||||
const handleSave = () => {
|
setUpdatedPermissions((prev) => ({
|
||||||
onSave(user.user.id, updatedPermissions);
|
...prev,
|
||||||
|
[knowledgeBaseId]: newPermissionType,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
setSavingPermissions(true);
|
||||||
|
setError(null);
|
||||||
|
setSuccessMessage(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const permissionUpdates = Object.entries(updatedPermissions).map(
|
||||||
|
async ([knowledgeBaseId, permissionType]) => {
|
||||||
|
const permissions = {
|
||||||
|
can_read: permissionType !== 'none',
|
||||||
|
can_edit: ['edit', 'admin'].includes(permissionType),
|
||||||
|
can_delete: permissionType === 'admin',
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestBody = {
|
||||||
|
user_id: user.user_info.id,
|
||||||
|
knowledge_base_id: knowledgeBaseId,
|
||||||
|
permissions: permissions,
|
||||||
|
// Optional expiration date - can be added if needed
|
||||||
|
// expires_at: "2025-12-31T23:59:59Z"
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await post('/permissions/update_permission/', requestBody);
|
||||||
|
return response;
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`更新知识库 ${knowledgeBaseId} 权限失败: ${err.message || '未知错误'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(permissionUpdates);
|
||||||
|
|
||||||
|
setSuccessMessage('权限更新成功');
|
||||||
|
// Refresh permissions list
|
||||||
|
fetchUserPermissions();
|
||||||
|
// Reset updated permissions
|
||||||
|
setUpdatedPermissions({});
|
||||||
|
|
||||||
|
// Notify parent component if needed
|
||||||
|
if (onSave) {
|
||||||
|
onSave(user.user_info.id);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message || '更新权限时发生错误');
|
||||||
|
} finally {
|
||||||
|
setSavingPermissions(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取权限类型的显示文本
|
// 获取权限类型的显示文本
|
||||||
@ -136,7 +109,7 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
|
|||||||
// 获取权限类型的值
|
// 获取权限类型的值
|
||||||
const getPermissionType = (permission) => {
|
const getPermissionType = (permission) => {
|
||||||
if (!permission) return 'none';
|
if (!permission) return 'none';
|
||||||
if (permission.can_admin) return 'admin';
|
if (permission.can_delete) return 'admin';
|
||||||
if (permission.can_edit) return 'edit';
|
if (permission.can_edit) return 'edit';
|
||||||
if (permission.can_read) return 'read';
|
if (permission.can_read) return 'read';
|
||||||
return 'none';
|
return 'none';
|
||||||
@ -147,10 +120,21 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
|
|||||||
<div className='modal-dialog modal-lg modal-dialog-scrollable' style={{ zIndex: 9999 }}>
|
<div className='modal-dialog modal-lg modal-dialog-scrollable' style={{ zIndex: 9999 }}>
|
||||||
<div className='modal-content'>
|
<div className='modal-content'>
|
||||||
<div className='modal-header'>
|
<div className='modal-header'>
|
||||||
<h5 className='modal-title'>{user.user.name} 的权限详情</h5>
|
<h5 className='modal-title'>{user.user_info.name} 的权限详情</h5>
|
||||||
<button type='button' className='btn-close' onClick={onClose}></button>
|
<button type='button' className='btn-close' onClick={onClose}></button>
|
||||||
</div>
|
</div>
|
||||||
<div className='modal-body'>
|
<div className='modal-body'>
|
||||||
|
{successMessage && (
|
||||||
|
<div className='alert alert-success alert-dismissible fade show' role='alert'>
|
||||||
|
{successMessage}
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='btn-close'
|
||||||
|
onClick={() => setSuccessMessage(null)}
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className='text-center py-4'>
|
<div className='text-center py-4'>
|
||||||
<div className='spinner-border' role='status'>
|
<div className='spinner-border' role='status'>
|
||||||
@ -177,7 +161,7 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{userPermissions.map((item) => {
|
{userPermissions.map((item) => {
|
||||||
const currentPermissionType = getPermissionType(item.permission);
|
const currentPermissionType = getPermissionType(item.permissions);
|
||||||
const updatedPermissionType =
|
const updatedPermissionType =
|
||||||
updatedPermissions[item.knowledge_base.id] ||
|
updatedPermissions[item.knowledge_base.id] ||
|
||||||
currentPermissionType;
|
currentPermissionType;
|
||||||
@ -202,8 +186,8 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{item.last_access_time
|
{item.granted_at
|
||||||
? new Date(item.last_access_time).toLocaleString()
|
? new Date(item.granted_at).toLocaleString()
|
||||||
: '从未访问'}
|
: '从未访问'}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -243,13 +227,13 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
|
|||||||
<th>知识库名称</th>
|
<th>知识库名称</th>
|
||||||
<th>所属部门</th>
|
<th>所属部门</th>
|
||||||
<th>当前权限</th>
|
<th>当前权限</th>
|
||||||
<th>最后访问时间</th>
|
<th>授权时间</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{userPermissions.map((item) => {
|
{userPermissions.map((item) => {
|
||||||
const currentPermissionType = getPermissionType(item.permission);
|
const currentPermissionType = getPermissionType(item.permissions);
|
||||||
const updatedPermissionType =
|
const updatedPermissionType =
|
||||||
updatedPermissions[item.knowledge_base.id] || currentPermissionType;
|
updatedPermissions[item.knowledge_base.id] || currentPermissionType;
|
||||||
|
|
||||||
@ -273,9 +257,9 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{item.last_access_time
|
{item.granted_at
|
||||||
? new Date(item.last_access_time).toLocaleString()
|
? new Date(item.granted_at).toLocaleString()
|
||||||
: '从未访问'}
|
: '未记录'}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select
|
<select
|
||||||
@ -310,9 +294,20 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
|
|||||||
type='button'
|
type='button'
|
||||||
className='btn btn-dark'
|
className='btn btn-dark'
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={loading || Object.keys(updatedPermissions).length === 0}
|
disabled={loading || savingPermissions || Object.keys(updatedPermissions).length === 0}
|
||||||
>
|
>
|
||||||
保存更改
|
{savingPermissions ? (
|
||||||
|
<>
|
||||||
|
<span
|
||||||
|
className='spinner-border spinner-border-sm me-2'
|
||||||
|
role='status'
|
||||||
|
aria-hidden='true'
|
||||||
|
></span>
|
||||||
|
保存中...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'保存更改'
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,8 +36,8 @@ export default function UserPermissions() {
|
|||||||
// 计算总页数
|
// 计算总页数
|
||||||
const totalPages = Math.ceil(total / page_size);
|
const totalPages = Math.ceil(total / page_size);
|
||||||
|
|
||||||
const handleOpenDetailsModal = (user) => {
|
const handleOpenDetailsModal = (data) => {
|
||||||
setSelectedUser(user);
|
setSelectedUser(data);
|
||||||
setShowDetailsModal(true);
|
setShowDetailsModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,11 +46,11 @@ export default function UserPermissions() {
|
|||||||
setShowDetailsModal(false);
|
setShowDetailsModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSavePermissions = async (userId, updatedPermissions) => {
|
const handleSavePermissions = async (userId) => {
|
||||||
try {
|
try {
|
||||||
await dispatch(updateUserPermissions({ userId, permissions: updatedPermissions })).unwrap();
|
// Permission updates are now handled directly in the UserPermissionDetails component
|
||||||
|
// Just refresh the users list to reflect the updated permissions
|
||||||
handleCloseDetailsModal();
|
handleCloseDetailsModal();
|
||||||
// 重新获取用户列表以更新权限信息
|
|
||||||
dispatch(fetchAllUserPermissions({ page: currentPage, page_size: pageSize }));
|
dispatch(fetchAllUserPermissions({ page: currentPage, page_size: pageSize }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新权限失败:', error);
|
console.error('更新权限失败:', error);
|
||||||
@ -82,10 +82,10 @@ export default function UserPermissions() {
|
|||||||
|
|
||||||
return users.filter(
|
return users.filter(
|
||||||
(user) =>
|
(user) =>
|
||||||
user.user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
user.user_info?.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
user.user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
user.user_info?.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
user.user.department.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
user.user_info?.department.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
user.user.role.toLowerCase().includes(searchTerm.toLowerCase())
|
user.user_info?.role.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -190,12 +190,13 @@ export default function UserPermissions() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='card'>
|
<>
|
||||||
<div className='card-header'>
|
<div className='d-flex justify-content-between align-items-center mb-3'>
|
||||||
<h5 className='mb-0'>用户权限管理</h5>
|
<h5 className='mb-0'>用户权限管理</h5>
|
||||||
</div>
|
</div>
|
||||||
<div className='card-body'>
|
|
||||||
<div className='mb-3 d-flex justify-content-between align-items-center'>
|
<>
|
||||||
|
{/* <div className='mb-3 d-flex justify-content-between align-items-center'>
|
||||||
<div className='search-bar' style={{ maxWidth: '300px' }}>
|
<div className='search-bar' style={{ maxWidth: '300px' }}>
|
||||||
<div className='input-group'>
|
<div className='input-group'>
|
||||||
<span className='input-group-text bg-light border-end-0'>
|
<span className='input-group-text bg-light border-end-0'>
|
||||||
@ -210,7 +211,7 @@ export default function UserPermissions() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
{loading === 'loading' ? (
|
{loading === 'loading' ? (
|
||||||
<div className='text-center my-5'>
|
<div className='text-center my-5'>
|
||||||
@ -241,58 +242,70 @@ export default function UserPermissions() {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{filteredUsers.length > 0 ? (
|
{filteredUsers.length > 0 ? (
|
||||||
filteredUsers.map((userPermission) => (
|
filteredUsers.map((userPermission) => (
|
||||||
<tr key={userPermission.id}>
|
<tr key={userPermission.user_info?.id}>
|
||||||
<td className='align-middle'>{userPermission.id}</td>
|
<td className='align-middle'>{userPermission.user_info?.id}</td>
|
||||||
<td className='align-middle'>{userPermission.user.username}</td>
|
<td className='align-middle'>
|
||||||
<td className='align-middle'>{userPermission.user.name}</td>
|
{userPermission.user_info?.username || 'N/A'}
|
||||||
<td className='align-middle'>{userPermission.user.department}</td>
|
</td>
|
||||||
|
<td className='align-middle'>
|
||||||
|
{userPermission.user_info?.name || 'N/A'}
|
||||||
|
</td>
|
||||||
|
<td className='align-middle'>
|
||||||
|
{userPermission.user_info?.department || 'N/A'}
|
||||||
|
</td>
|
||||||
<td className='align-middle'>
|
<td className='align-middle'>
|
||||||
<span
|
<span
|
||||||
className={`badge ${
|
className={`badge ${
|
||||||
userPermission.user.role === 'admin'
|
userPermission.user_info?.role === 'admin'
|
||||||
? 'bg-danger-subtle text-danger'
|
? 'bg-danger-subtle text-danger'
|
||||||
: userPermission.user.role === 'leader'
|
: userPermission.user_info?.role === 'leader'
|
||||||
? 'bg-warning-subtle text-warning'
|
? 'bg-warning-subtle text-warning'
|
||||||
: 'bg-info-subtle text-info'
|
: 'bg-info-subtle text-info'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{userPermission.user.role === 'admin'
|
{userPermission.user_info?.role === 'admin'
|
||||||
? '管理员'
|
? '管理员'
|
||||||
: userPermission.user.role === 'leader'
|
: userPermission.user_info?.role === 'leader'
|
||||||
? '组长'
|
? '组长'
|
||||||
: '成员'}
|
: '成员'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className='align-middle'>
|
<td className='align-middle'>
|
||||||
{userPermission.user.permissions_count && (
|
|
||||||
<div className='d-flex flex-wrap gap-1'>
|
<div className='d-flex flex-wrap gap-1'>
|
||||||
{userPermission.user.permissions_count.read > 0 && (
|
{userPermission.stats?.by_permission?.full_access > 0 && (
|
||||||
<span className='badge
|
<span
|
||||||
bg-success-subtle text-success
|
className='badge
|
||||||
d-flex align-items-center gap-1'>
|
bg-success-subtle text-success
|
||||||
完全访问: {userPermission.user.
|
d-flex align-items-center gap-1'
|
||||||
permissions_count.read}
|
>
|
||||||
|
完全访问:{' '}
|
||||||
|
{userPermission.stats?.by_permission?.full_access}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{userPermission.user.permissions_count.edit > 0 && (
|
|
||||||
<span className='badge
|
{userPermission.stats?.by_permission?.read_only > 0 && (
|
||||||
bg-warning-subtle text-warning
|
<span
|
||||||
d-flex align-items-center gap-1'>
|
className='badge
|
||||||
只读访问: {userPermission.user.
|
bg-warning-subtle text-warning
|
||||||
permissions_count.edit}
|
d-flex align-items-center gap-1'
|
||||||
|
>
|
||||||
|
只读访问:{' '}
|
||||||
|
{userPermission.stats?.by_permission?.read_only}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{userPermission.user.permissions_count.admin > 0 && (
|
|
||||||
<span className='badge
|
{userPermission.stats?.by_permission?.read_write > 0 && (
|
||||||
bg-dark-subtle d-flex
|
<span
|
||||||
align-items-center gap-1'>
|
className='badge
|
||||||
无访问权限: {userPermission.user.
|
bg-info-subtle text-info
|
||||||
permissions_count.admin}
|
d-flex align-items-center gap-1'
|
||||||
|
>
|
||||||
|
读写权限:{' '}
|
||||||
|
{userPermission.stats?.by_permission?.read_write}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</td>
|
||||||
</td>
|
|
||||||
<td className='align-middle'>
|
<td className='align-middle'>
|
||||||
<button
|
<button
|
||||||
className='btn btn-sm btn-outline-dark'
|
className='btn btn-sm btn-outline-dark'
|
||||||
@ -305,7 +318,7 @@ export default function UserPermissions() {
|
|||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan='5' className='text-center py-4'>
|
<td colSpan='7' className='text-center py-4'>
|
||||||
无匹配的用户记录
|
无匹配的用户记录
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -325,7 +338,7 @@ export default function UserPermissions() {
|
|||||||
onSave={handleSavePermissions}
|
onSave={handleSavePermissions}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,10 @@ const initialState = {
|
|||||||
error: null,
|
error: null,
|
||||||
},
|
},
|
||||||
pending: {
|
pending: {
|
||||||
items: [],
|
results: [], // 更改为results
|
||||||
|
total: 0, // 添加total
|
||||||
|
page: 1, // 添加page
|
||||||
|
page_size: 10, // 添加page_size
|
||||||
status: 'idle',
|
status: 'idle',
|
||||||
error: null,
|
error: null,
|
||||||
},
|
},
|
||||||
@ -91,7 +94,10 @@ const permissionsSlice = createSlice({
|
|||||||
})
|
})
|
||||||
.addCase(fetchPermissionsThunk.fulfilled, (state, action) => {
|
.addCase(fetchPermissionsThunk.fulfilled, (state, action) => {
|
||||||
state.pending.status = 'succeeded';
|
state.pending.status = 'succeeded';
|
||||||
state.pending.items = action.payload;
|
state.pending.results = action.payload.results || [];
|
||||||
|
state.pending.total = action.payload.total || 0;
|
||||||
|
state.pending.page = action.payload.page || 1;
|
||||||
|
state.pending.page_size = action.payload.page_size || 10;
|
||||||
})
|
})
|
||||||
.addCase(fetchPermissionsThunk.rejected, (state, action) => {
|
.addCase(fetchPermissionsThunk.rejected, (state, action) => {
|
||||||
state.pending.status = 'failed';
|
state.pending.status = 'failed';
|
||||||
|
@ -5,17 +5,22 @@ import { showNotification } from '../notification.slice';
|
|||||||
// 获取权限申请列表
|
// 获取权限申请列表
|
||||||
export const fetchPermissionsThunk = createAsyncThunk(
|
export const fetchPermissionsThunk = createAsyncThunk(
|
||||||
'permissions/fetchPermissions',
|
'permissions/fetchPermissions',
|
||||||
async (_, { rejectWithValue }) => {
|
async (params = {}, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const { data, message, code } = await get('/permissions/');
|
const response = await get('/permissions/', { params });
|
||||||
|
|
||||||
if (code === 200) {
|
if (response && response.code === 200) {
|
||||||
return data.items || [];
|
return {
|
||||||
|
results: response.data.results || [],
|
||||||
|
total: response.data.total || 0,
|
||||||
|
page: response.data.page || 1,
|
||||||
|
page_size: response.data.page_size || 10,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return rejectWithValue('获取权限申请列表失败');
|
return rejectWithValue('获取权限申请列表失败');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取权限申请列表失败:', error);
|
console.error('获取权限申请列表失败:', error);
|
||||||
return rejectWithValue('获取权限申请列表失败');
|
return rejectWithValue(error.response?.data?.message || '获取权限申请列表失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user