Compare commits

..

No commits in common. "56c4878065fb2ee36e203ce8fb4911e16340e80b" and "1ba460b4cff346107163b3d1b25b9ccabf3d8241" have entirely different histories.

11 changed files with 295 additions and 336 deletions

View File

@ -25,14 +25,14 @@ function App() {
// WebSocket
if (user && !isConnected) {
// initWebSocket()
// .then(() => {
initWebSocket()
.then(() => {
// dispatch(setWebSocketConnected(true));
// console.log('WebSocket connection initialized');
// })
// .catch((error) => {
// console.error('Failed to initialize WebSocket connection:', error);
// });
})
.catch((error) => {
console.error('Failed to initialize WebSocket connection:', error);
});
}
// WebSocket

View File

@ -60,7 +60,7 @@ const CreateKnowledgeBaseModal = ({
const getAvailableTypes = () => {
if (isAdmin) {
return [
{ value: 'admin', label: '公共知识库' },
{ value: 'admin', label: 'Admin 级知识库' },
{ value: 'leader', label: 'Leader 级知识库' },
{ value: 'member', label: 'Member 级知识库' },
{ value: 'private', label: '私有知识库' },
@ -68,15 +68,11 @@ const CreateKnowledgeBaseModal = ({
];
} else if (isLeader) {
return [
{ value: 'admin', label: '公共知识库' },
{ value: 'member', label: 'Member 级知识库' },
{ value: 'private', label: '私有知识库' },
];
} else {
return [
{ value: 'admin', label: '公共知识库' },
{ value: 'private', label: '私有知识库' },
];
return [{ value: 'private', label: '私有知识库' }];
}
};
@ -173,7 +169,7 @@ const CreateKnowledgeBaseModal = ({
</div>
{!isAdmin && !isLeader && (
<small className='text-muted d-block mt-1'>
您可以创建公共知识库所有人可访问或私有知识库仅自己可访问
注意您当前只能创建私有知识库其他类型需要更高权限
</small>
)}
{formErrors.type && <div className='text-danger small mt-1'>{formErrors.type}</div>}

View File

@ -173,17 +173,6 @@ export default function SettingsTab({ knowledgeBase }) {
return;
}
// adminprivate
if (newType !== 'admin' && newType !== 'private' && currentUser.role === 'member') {
dispatch(
showNotification({
message: '您只能将知识库修改为公共(admin)或私有(private)类型',
type: 'warning',
})
);
return;
}
if (isAdmin && !validateForm()) {
return;
}

View File

@ -26,7 +26,7 @@ const KnowledgeBaseForm = ({
const getAvailableTypes = () => {
if (isAdmin) {
return [
{ value: 'admin', label: '公共知识库' },
{ value: 'admin', label: 'Admin 级知识库' },
{ value: 'leader', label: 'Leader 级知识库' },
{ value: 'member', label: 'Member 级知识库' },
{ value: 'private', label: '私有知识库' },
@ -34,15 +34,11 @@ const KnowledgeBaseForm = ({
];
} else if (isLeader) {
return [
{ value: 'admin', label: '公共知识库' },
{ value: 'member', label: 'Member 级知识库' },
{ value: 'private', label: '私有知识库' },
];
} else {
return [
{ value: 'admin', label: '公共知识库' },
{ value: 'private', label: '私有知识库' },
];
return [{ value: 'private', label: '私有知识库' }];
}
};
@ -110,7 +106,7 @@ const KnowledgeBaseForm = ({
value={type.value}
checked={formData.type === type.value}
onChange={onInputChange}
disabled={false} //
disabled={currentUser?.role === 'member'} //
/>
<label className='form-check-label' htmlFor={`type${type.value}`}>
{type.label}
@ -119,7 +115,7 @@ const KnowledgeBaseForm = ({
))}
</div>
{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>}
</div>

View File

@ -437,8 +437,8 @@ export default function KnowledgeBase() {
const isAdmin = currentUser?.role === 'admin';
const isLeader = currentUser?.role === 'leader';
//
let defaultType = 'admin';
//
let defaultType = 'private';
//
let department = currentUser?.department || '';

View File

@ -12,6 +12,9 @@ import './PendingRequests.css'; // 引入外部CSS文件
import SvgIcon from '../../../components/SvgIcon';
import RequestDetailSlideOver from './RequestDetailSlideOver';
//
const PAGE_SIZE = 5;
export default function PendingRequests() {
const dispatch = useDispatch();
const location = useLocation();
@ -22,18 +25,14 @@ export default function PendingRequests() {
const [selectedRequest, setSelectedRequest] = useState(null);
const [showSlideOver, setShowSlideOver] = useState(false);
// Redux store
const {
results: pendingRequests,
status: fetchStatus,
error: fetchError,
page: currentPage,
page_size: pageSize,
total,
} = useSelector((state) => state.permissions.pending);
// 使
const [pendingRequests, setPendingRequests] = useState([]);
const [fetchStatus, setFetchStatus] = useState('idle');
const [fetchError, setFetchError] = useState(null);
//
const totalPages = Math.ceil(total / pageSize) || 1;
//
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
// Redux store/
const {
@ -44,8 +43,24 @@ export default function PendingRequests() {
//
useEffect(() => {
dispatch(fetchPermissionsThunk({ page: currentPage, page_size: pageSize }));
}, [dispatch, currentPage, pageSize]);
const fetchData = async () => {
try {
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(() => {
@ -73,8 +88,17 @@ export default function PendingRequests() {
setShowSlideOver(false);
setSelectedRequest(null);
//
dispatch(fetchPermissionsThunk({ page: currentPage, page_size: pageSize }));
//
const updatedRequests = pendingRequests.filter((req) => req.id !== currentRequestId);
setPendingRequests(updatedRequests);
//
setTotalPages(Math.ceil(updatedRequests.length / PAGE_SIZE));
//
if (getCurrentPageData().length === 1 && currentPage > 1) {
setCurrentPage(currentPage - 1);
}
//
dispatch(resetOperationStatus());
@ -88,7 +112,15 @@ export default function PendingRequests() {
//
dispatch(resetOperationStatus());
}
}, [approveRejectStatus, approveRejectError, dispatch, isApproving, currentPage, pageSize]);
}, [
approveRejectStatus,
approveRejectError,
dispatch,
isApproving,
currentRequestId,
pendingRequests,
currentPage,
]);
//
const handleOpenResponseInput = (requestId, approving) => {
@ -151,9 +183,16 @@ 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) => {
dispatch(fetchPermissionsThunk({ page, page_size: pageSize }));
setCurrentPage(page);
};
//
@ -215,27 +254,29 @@ export default function PendingRequests() {
);
}
//
const currentPageData = getCurrentPageData();
//
return (
<>
<div className='d-flex justify-content-between align-items-center mb-3'>
<h5 className='mb-0'>待处理申请</h5>
<div className='badge bg-danger'>{total}个待处理</div>
<div className='badge bg-danger'>{pendingRequests.length}个待处理</div>
</div>
<div className='pending-requests-list'>
{pendingRequests.map((request) => (
{currentPageData.map((request) => (
<div key={request.id} className='pending-request-item' onClick={() => handleRowClick(request)}>
<div className='request-header'>
<div className='user-info'>
<h6 className='mb-0'>{request.applicant.name || request.applicant.username}</h6>
<small className='text-muted'>{request.applicant.department || '未分配部门'}</small>
<h6 className='mb-0'>{request.applicant}</h6>
</div>
<div className='request-date'>{new Date(request.created_at).toLocaleDateString()}</div>
</div>
<div className='request-content'>
<p className='mb-2'>申请访问{request.knowledge_base.name}</p>
<p className='mb-2'>申请访问{request.knowledge_base}</p>
{request.permissions.can_edit ? (
<span
@ -256,14 +297,6 @@ export default function PendingRequests() {
</span>
)
)}
{request.expires_at && (
<div className='mt-2'>
<small className='text-muted'>
到期时间: {new Date(request.expires_at).toLocaleDateString()}
</small>
</div>
)}
</div>
<div className='request-actions'>
<button
@ -313,7 +346,7 @@ export default function PendingRequests() {
{/* 回复输入弹窗 */}
{showResponseInput && (
<div className='modal fade show' style={{ display: 'block' }} tabIndex='-1'>
<div className='modal-dialog' style={{ zIndex: 9999 }}>
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
<h5 className='modal-title'>{isApproving ? '批准' : '拒绝'}申请</h5>

View File

@ -13,13 +13,9 @@ export default function RequestDetailSlideOver({
}) {
if (!request) return null;
//
const applicantName = request.applicant?.name || request.applicant?.username || '未知用户';
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 || '';
//
const applicant = request.applicant || request.title || '未知用户';
const applicantInitial = applicant.charAt(0);
return (
<>
@ -36,8 +32,7 @@ export default function RequestDetailSlideOver({
<div className='d-flex align-items-center mb-3'>
<div className='avatar-placeholder me-3 bg-dark'>{applicantInitial}</div>
<div>
<h5 className='mb-1'>{applicantName}</h5>
<div className='text-muted'>{applicantDept}</div>
<h5 className='mb-1'>{applicant}</h5>
</div>
</div>
</div>
@ -45,16 +40,8 @@ export default function RequestDetailSlideOver({
<div className='mb-4'>
<h6 className='text-muted mb-2'>知识库信息</h6>
<p className='mb-1'>
<strong>名称</strong> {knowledgeBaseName}
<strong>ID</strong> {request.knowledge_base || request.content || '未知知识库'}
</p>
<p className='mb-1'>
<strong>ID</strong> {knowledgeBaseId}
</p>
{knowledgeBaseType && (
<p className='mb-1'>
<strong>类型</strong> {knowledgeBaseType}
</p>
)}
</div>
<div className='mb-4'>
@ -62,20 +49,12 @@ export default function RequestDetailSlideOver({
<div className='d-flex flex-wrap gap-2 mb-3'>
{request.permissions?.can_read && !request.permissions?.can_edit && (
<span className='badge bg-warning-subtle text-warning d-flex align-items-center gap-1'>
<SvgIcon className={'eye'} />
只读
</span>
)}
{request.permissions?.can_edit && (
{request.permissions?.can_edit && request.permissions?.can_delete && (
<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>
)}
</div>
@ -93,45 +72,13 @@ export default function RequestDetailSlideOver({
</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'>
<h6 className='text-muted mb-2'>申请理由</h6>
<div className='p-3 bg-light rounded'>
{request.reason || '无申请理由'}
{request.reason || request.content || '无申请理由'}
</div>
</div>
</div>
{request.status === 'pending' && (
<div className='slide-over-footer'>
<button
className='btn btn-outline-danger me-2'
@ -170,7 +117,6 @@ export default function RequestDetailSlideOver({
)}
</button>
</div>
)}
</div>
</div>
</>

View File

@ -1,75 +1,120 @@
import React, { useState, useEffect } from 'react';
import { get, post } from '../../../services/api';
import { get } 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 }) {
const [updatedPermissions, setUpdatedPermissions] = useState({});
const [userPermissions, setUserPermissions] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [savingPermissions, setSavingPermissions] = useState(false);
const [successMessage, setSuccessMessage] = useState(null);
const [updatedPermissions, setUpdatedPermissions] = useState({});
//
useEffect(() => {
// If we already have the permissions data in the user object, use it
if (user.permissions && Array.isArray(user.permissions)) {
setUserPermissions(user.permissions);
const fetchUserPermissions = async () => {
try {
setLoading(true);
const response = await get(`/users/${user.user.id}/permissions/`);
if (response && response.code === 200) {
// 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();
}
}, [user]);
const handlePermissionChange = (knowledgeBaseId, newPermissionType) => {
setUpdatedPermissions((prev) => ({
...prev,
[knowledgeBaseId]: newPermissionType,
}));
//
const handlePermissionChange = (knowledgeBaseId, permissionType) => {
setUpdatedPermissions({
...updatedPermissions,
[knowledgeBaseId]: permissionType,
});
};
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('权限更新成功');
// Reset updated permissions
setUpdatedPermissions({});
// Notify parent component if needed
if (onSave) {
onSave(user.user_info.id);
}
} catch (err) {
setError(err.message || '更新权限时发生错误');
} finally {
setSavingPermissions(false);
}
//
const handleSave = () => {
onSave(user.user.id, updatedPermissions);
};
//
@ -91,7 +136,7 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
//
const getPermissionType = (permission) => {
if (!permission) return 'none';
if (permission.can_delete) return 'admin';
if (permission.can_admin) return 'admin';
if (permission.can_edit) return 'edit';
if (permission.can_read) return 'read';
return 'none';
@ -102,21 +147,10 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
<div className='modal-dialog modal-lg modal-dialog-scrollable' style={{ zIndex: 9999 }}>
<div className='modal-content'>
<div className='modal-header'>
<h5 className='modal-title'>{user.user_info.name} 的权限详情</h5>
<h5 className='modal-title'>{user.user.name} 的权限详情</h5>
<button type='button' className='btn-close' onClick={onClose}></button>
</div>
<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 ? (
<div className='text-center py-4'>
<div className='spinner-border' role='status'>
@ -143,7 +177,7 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
</thead>
<tbody>
{userPermissions.map((item) => {
const currentPermissionType = getPermissionType(item.permissions);
const currentPermissionType = getPermissionType(item.permission);
const updatedPermissionType =
updatedPermissions[item.knowledge_base.id] ||
currentPermissionType;
@ -168,8 +202,8 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
</span>
</td>
<td>
{item.granted_at
? new Date(item.granted_at).toLocaleString()
{item.last_access_time
? new Date(item.last_access_time).toLocaleString()
: '从未访问'}
</td>
<td>
@ -209,13 +243,13 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
<th>知识库名称</th>
<th>所属部门</th>
<th>当前权限</th>
<th>授权时间</th>
<th>最后访问时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{userPermissions.map((item) => {
const currentPermissionType = getPermissionType(item.permissions);
const currentPermissionType = getPermissionType(item.permission);
const updatedPermissionType =
updatedPermissions[item.knowledge_base.id] || currentPermissionType;
@ -239,9 +273,9 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
</span>
</td>
<td>
{item.granted_at
? new Date(item.granted_at).toLocaleString()
: '未记录'}
{item.last_access_time
? new Date(item.last_access_time).toLocaleString()
: '从未访问'}
</td>
<td>
<select
@ -276,20 +310,9 @@ export default function UserPermissionDetails({ user, onClose, onSave }) {
type='button'
className='btn btn-dark'
onClick={handleSave}
disabled={loading || savingPermissions || Object.keys(updatedPermissions).length === 0}
disabled={loading || Object.keys(updatedPermissions).length === 0}
>
{savingPermissions ? (
<>
<span
className='spinner-border spinner-border-sm me-2'
role='status'
aria-hidden='true'
></span>
保存中...
</>
) : (
'保存更改'
)}
保存更改
</button>
</div>
</div>

View File

@ -36,8 +36,8 @@ export default function UserPermissions() {
//
const totalPages = Math.ceil(total / page_size);
const handleOpenDetailsModal = (data) => {
setSelectedUser(data);
const handleOpenDetailsModal = (user) => {
setSelectedUser(user);
setShowDetailsModal(true);
};
@ -46,11 +46,11 @@ export default function UserPermissions() {
setShowDetailsModal(false);
};
const handleSavePermissions = async (userId) => {
const handleSavePermissions = async (userId, updatedPermissions) => {
try {
// Permission updates are now handled directly in the UserPermissionDetails component
// Just refresh the users list to reflect the updated permissions
await dispatch(updateUserPermissions({ userId, permissions: updatedPermissions })).unwrap();
handleCloseDetailsModal();
//
dispatch(fetchAllUserPermissions({ page: currentPage, page_size: pageSize }));
} catch (error) {
console.error('更新权限失败:', error);
@ -82,10 +82,10 @@ export default function UserPermissions() {
return users.filter(
(user) =>
user.user_info?.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.user_info?.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.user_info?.department.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.user_info?.role.toLowerCase().includes(searchTerm.toLowerCase())
user.user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.user.department.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.user.role.toLowerCase().includes(searchTerm.toLowerCase())
);
};
@ -190,13 +190,12 @@ export default function UserPermissions() {
};
return (
<>
<div className='d-flex justify-content-between align-items-center mb-3'>
<div className='card'>
<div className='card-header'>
<h5 className='mb-0'>用户权限管理</h5>
</div>
<>
{/* <div className='mb-3 d-flex justify-content-between align-items-center'>
<div className='card-body'>
<div className='mb-3 d-flex justify-content-between align-items-center'>
<div className='search-bar' style={{ maxWidth: '300px' }}>
<div className='input-group'>
<span className='input-group-text bg-light border-end-0'>
@ -211,7 +210,7 @@ export default function UserPermissions() {
/>
</div>
</div>
</div> */}
</div>
{loading === 'loading' ? (
<div className='text-center my-5'>
@ -242,69 +241,57 @@ export default function UserPermissions() {
<tbody>
{filteredUsers.length > 0 ? (
filteredUsers.map((userPermission) => (
<tr key={userPermission.user_info?.id}>
<td className='align-middle'>{userPermission.user_info?.id}</td>
<td className='align-middle'>
{userPermission.user_info?.username || 'N/A'}
</td>
<td className='align-middle'>
{userPermission.user_info?.name || 'N/A'}
</td>
<td className='align-middle'>
{userPermission.user_info?.department || 'N/A'}
</td>
<tr key={userPermission.id}>
<td className='align-middle'>{userPermission.id}</td>
<td className='align-middle'>{userPermission.user.username}</td>
<td className='align-middle'>{userPermission.user.name}</td>
<td className='align-middle'>{userPermission.user.department}</td>
<td className='align-middle'>
<span
className={`badge ${
userPermission.user_info?.role === 'admin'
userPermission.user.role === 'admin'
? 'bg-danger-subtle text-danger'
: userPermission.user_info?.role === 'leader'
: userPermission.user.role === 'leader'
? 'bg-warning-subtle text-warning'
: 'bg-info-subtle text-info'
}`}
>
{userPermission.user_info?.role === 'admin'
{userPermission.user.role === 'admin'
? '管理员'
: userPermission.user_info?.role === 'leader'
: userPermission.user.role === 'leader'
? '组长'
: '成员'}
</span>
</td>
<td className='align-middle'>
{userPermission.user.permissions_count && (
<div className='d-flex flex-wrap gap-1'>
{userPermission.stats?.by_permission?.full_access > 0 && (
<span
className='badge
{userPermission.user.permissions_count.read > 0 && (
<span className='badge
bg-success-subtle text-success
d-flex align-items-center gap-1'
>
完全访问:{' '}
{userPermission.stats?.by_permission?.full_access}
d-flex align-items-center gap-1'>
完全访问: {userPermission.user.
permissions_count.read}
</span>
)}
{userPermission.stats?.by_permission?.read_only > 0 && (
<span
className='badge
{userPermission.user.permissions_count.edit > 0 && (
<span className='badge
bg-warning-subtle text-warning
d-flex align-items-center gap-1'
>
只读访问:{' '}
{userPermission.stats?.by_permission?.read_only}
d-flex align-items-center gap-1'>
只读访问: {userPermission.user.
permissions_count.edit}
</span>
)}
{userPermission.stats?.by_permission?.read_write > 0 && (
<span
className='badge
bg-info-subtle text-info
d-flex align-items-center gap-1'
>
读写权限:{' '}
{userPermission.stats?.by_permission?.read_write}
{userPermission.user.permissions_count.admin > 0 && (
<span className='badge
bg-dark-subtle d-flex
align-items-center gap-1'>
无访问权限: {userPermission.user.
permissions_count.admin}
</span>
)}
</div>
)}
</td>
<td className='align-middle'>
<button
@ -318,7 +305,7 @@ export default function UserPermissions() {
))
) : (
<tr>
<td colSpan='7' className='text-center py-4'>
<td colSpan='5' className='text-center py-4'>
无匹配的用户记录
</td>
</tr>
@ -338,7 +325,7 @@ export default function UserPermissions() {
onSave={handleSavePermissions}
/>
)}
</>
</>
</div>
</div>
);
}

View File

@ -27,10 +27,7 @@ const initialState = {
error: null,
},
pending: {
results: [], // 更改为results
total: 0, // 添加total
page: 1, // 添加page
page_size: 10, // 添加page_size
items: [],
status: 'idle',
error: null,
},
@ -94,10 +91,7 @@ const permissionsSlice = createSlice({
})
.addCase(fetchPermissionsThunk.fulfilled, (state, action) => {
state.pending.status = 'succeeded';
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;
state.pending.items = action.payload;
})
.addCase(fetchPermissionsThunk.rejected, (state, action) => {
state.pending.status = 'failed';

View File

@ -5,22 +5,17 @@ import { showNotification } from '../notification.slice';
// 获取权限申请列表
export const fetchPermissionsThunk = createAsyncThunk(
'permissions/fetchPermissions',
async (params = {}, { rejectWithValue }) => {
async (_, { rejectWithValue }) => {
try {
const response = await get('/permissions/', { params });
const { data, message, code } = await get('/permissions/');
if (response && response.code === 200) {
return {
results: response.data.results || [],
total: response.data.total || 0,
page: response.data.page || 1,
page_size: response.data.page_size || 10,
};
if (code === 200) {
return data.items || [];
}
return rejectWithValue('获取权限申请列表失败');
} catch (error) {
console.error('获取权限申请列表失败:', error);
return rejectWithValue(error.response?.data?.message || '获取权限申请列表失败');
return rejectWithValue('获取权限申请列表失败');
}
}
);