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