fix: parallel fetches, error handling, extract UserTreeNode component

This commit is contained in:
2026-07-09 11:16:47 +08:00
parent a42039d1df
commit 33e6cb445c

View File

@@ -53,6 +53,33 @@ interface ImportUsersResponse {
skipped: number;
}
const UserTreeNode: React.FC<{
u: { userid: string; name: string; mobile: string };
isTeacher: boolean;
onToggle: () => void;
roleId: number | undefined;
defaultRoleId: number | null;
roles: Array<{ id: number; name: string }>;
onRoleChange: (roleId: number) => void;
}> = React.memo(({ u, isTeacher, onToggle, roleId, defaultRoleId, roles, onRoleChange }) => (
<Space size="small">
<Checkbox checked={isTeacher} onChange={onToggle}></Checkbox>
<span style={{ fontWeight: 500 }}>{u.name}</span>
{u.mobile && <Tag style={{ marginLeft: 4 }}>{u.mobile}</Tag>}
{isTeacher && (
<Select
size="small"
style={{ width: 100, marginLeft: 8 }}
value={roleId || defaultRoleId}
onChange={onRoleChange}
options={roles.map((r) => ({ label: r.name, value: r.id }))}
placeholder="选择角色"
onClick={(e: React.MouseEvent) => e.stopPropagation()}
/>
)}
</Space>
));
const IntegrationConfigPage: React.FC = () => {
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
@@ -91,8 +118,7 @@ const IntegrationConfigPage: React.FC = () => {
};
useEffect(() => {
fetchConfig();
fetchRoles();
void Promise.all([fetchConfig(), fetchRoles()]);
}, []);
const handleSave = async () => {
@@ -137,7 +163,7 @@ const IntegrationConfigPage: React.FC = () => {
const teacherRole = activeRoles.find((r) => r.name === '班主任');
setDefaultTeacherRoleId(teacherRole?.id || activeRoles[0]?.id || null);
} catch {
// ignore
message.error('获取角色列表失败');
}
};
@@ -154,7 +180,7 @@ const IntegrationConfigPage: React.FC = () => {
setDeptPickerTree(toTreeNode(res.data));
}
} catch {
// ignore
message.error('获取部门架构失败');
}
};
@@ -230,43 +256,30 @@ const IntegrationConfigPage: React.FC = () => {
...buildTreeData(node.children),
...node.users.map((u) => ({
title: (
<Space size="small">
<Checkbox
checked={!!teacherChecks[u.userid]}
onChange={(e) => {
setTeacherChecks((prev) => ({
...prev,
[u.userid]: e.target.checked,
}));
if (!e.target.checked) {
setTeacherRoles((prev) => {
const next = { ...prev };
delete next[u.userid];
return next;
});
}
}}
>
</Checkbox>
<span style={{ fontWeight: 500 }}>{u.name}</span>
{u.mobile && (
<Tag style={{ marginLeft: 4 }}>{u.mobile}</Tag>
)}
{teacherChecks[u.userid] && (
<Select
size="small"
style={{ width: 100, marginLeft: 8 }}
value={teacherRoles[u.userid] || defaultTeacherRoleId}
onChange={(roleId: number) =>
setTeacherRoles((prev) => ({ ...prev, [u.userid]: roleId }))
}
options={roles.map((r) => ({ label: r.name, value: r.id }))}
placeholder="选择角色"
onClick={(e: React.MouseEvent) => e.stopPropagation()}
/>
)}
</Space>
<UserTreeNode
key={u.userid}
u={u}
isTeacher={!!teacherChecks[u.userid]}
onToggle={() => {
setTeacherChecks((prev) => ({
...prev,
[u.userid]: !prev[u.userid],
}));
if (teacherChecks[u.userid]) {
setTeacherRoles((prev) => {
const next = { ...prev };
delete next[u.userid];
return next;
});
}
}}
roleId={teacherRoles[u.userid]}
defaultRoleId={defaultTeacherRoleId}
roles={roles}
onRoleChange={(roleId) =>
setTeacherRoles((prev) => ({ ...prev, [u.userid]: roleId }))
}
/>
),
key: `user-${u.userid}`,
selectable: false,