fix: close permission review gaps

fix: harden permission-gated UI — minimum-org endpoint, modal/Popconfirm fail-closed on revocation
This commit is contained in:
2026-07-23 12:23:33 +08:00
parent c98d37307e
commit a7a7af1667
17 changed files with 391 additions and 227 deletions

View File

@@ -1401,9 +1401,12 @@ const StudentProfileContent: React.FC<StudentProfileContentProps> = ({
onClose,
}) => {
const { hasPermission, hasAnyPermission } = usePermission();
const canViewOrganizations = hasPermission('organization:view');
const canChooseOrganization =
canViewOrganizations && hasAnyPermission('student:create', 'student:edit');
const canLoadOrganizations = hasAnyPermission(
'organization:view',
'student:create',
'student:edit',
);
const canChooseOrganization = hasAnyPermission('student:create', 'student:edit');
const [aggregateData, setAggregateData] = useState<StudentProfileAggregate | null>(null);
const [organizations, setOrganizations] = useState<Array<{ id: number; name: string }>>([]);
const [loading, setLoading] = useState(false);
@@ -1426,17 +1429,17 @@ const StudentProfileContent: React.FC<StudentProfileContentProps> = ({
}, [fetchData]);
useEffect(() => {
if (!canViewOrganizations) {
if (!canLoadOrganizations) {
setOrganizations([]);
return;
}
api
.get('/organizations', { params: { includeArchived: 'false' } })
.get('/organizations/options')
.then((res: unknown) => {
setOrganizations(res as Array<{ id: number; name: string }>);
setOrganizations(res as Array<{ id: number; name: string; isHost?: boolean }>);
})
.catch(() => {});
}, [canViewOrganizations]);
}, [canLoadOrganizations]);
const handlePreviewReport = useCallback(async () => {
try {
@@ -1451,7 +1454,11 @@ const StudentProfileContent: React.FC<StudentProfileContentProps> = ({
}
}, [studentId]);
const handleViewSensitive = useViewSensitive(studentId, '学生档案');
const handleViewSensitive = useViewSensitive(
studentId,
'学生档案',
hasPermission('log:create'),
);
const tabItems = useMemo(() => {
if (!aggregateData) return [];