fix(admin): 补全 React hooks 依赖并修复页面稳定性

- 各页面 useCallback/useMemo 依赖补全(modal/mutation/setter 等),避免闭包过期
- ECharts 使用 optionRef、MainLayout 缓存菜单转换函数、main.tsx 增加挂载点校验
- 学生同步结果改用 recordsCount 展示
This commit is contained in:
2026-08-05 21:14:08 +08:00
parent ab4765cee5
commit 1a1e90c72f
21 changed files with 439 additions and 341 deletions

View File

@@ -89,33 +89,6 @@ const ClassesPage: React.FC = () => {
const [saving, setSaving] = useState(false);
const [showArchived, setShowArchived] = useState(false);
const handleArchive = async (id: number, archive: boolean) => {
try {
await archiveMutation.mutateAsync({ id, archive });
message.success(archive ? '已归档' : '已恢复');
} catch {
// 错误提示由 useApiMutation 统一处理
}
};
const handlePurge = (record: ClassItem) => {
modal.confirm({
title: `永久删除班级「${record.name}」?`,
content: '删除后不可恢复,存在学生、教师、排课、考试或考勤关联时将无法删除。确定继续?',
okText: '永久删除',
okButtonProps: { danger: true },
cancelText: '取消',
onOk: async () => {
try {
await purgeMutation.mutateAsync(record.id);
message.success('已永久删除(不可恢复)');
} catch {
// 错误提示由 useApiMutation 统一处理
}
},
});
};
const {
data = [],
isLoading,
@@ -160,6 +133,39 @@ const ClassesPage: React.FC = () => {
{ invalidate: [['classes']] },
);
const handleArchive = useCallback(
async (id: number, archive: boolean) => {
try {
await archiveMutation.mutateAsync({ id, archive });
message.success(archive ? '已归档' : '已恢复');
} catch {
// 错误提示由 useApiMutation 统一处理
}
},
[archiveMutation],
);
const handlePurge = useCallback(
(record: ClassItem) => {
modal.confirm({
title: `永久删除班级「${record.name}」?`,
content: '删除后不可恢复,存在学生、教师、排课、考试或考勤关联时将无法删除。确定继续?',
okText: '永久删除',
okButtonProps: { danger: true },
cancelText: '取消',
onOk: async () => {
try {
await purgeMutation.mutateAsync(record.id);
message.success('已永久删除(不可恢复)');
} catch {
// 错误提示由 useApiMutation 统一处理
}
},
});
},
[modal, purgeMutation],
);
const filtered = useMemo(() => {
if (!searchText) return data;
const q = searchText.toLowerCase();
@@ -174,16 +180,19 @@ const ClassesPage: React.FC = () => {
setModalOpen(true);
};
const handleEdit = (record: ClassItem) => {
setEditing(record);
form.setFieldsValue({
...record,
notes: record.notes ?? undefined,
startDate: record.startDate ? dayjs(record.startDate) : undefined,
endDate: record.endDate ? dayjs(record.endDate) : undefined,
});
setModalOpen(true);
};
const handleEdit = useCallback(
(record: ClassItem) => {
setEditing(record);
form.setFieldsValue({
...record,
notes: record.notes ?? undefined,
startDate: record.startDate ? dayjs(record.startDate) : undefined,
endDate: record.endDate ? dayjs(record.endDate) : undefined,
});
setModalOpen(true);
},
[form],
);
const handleSubmit = async () => {
setSaving(true);
@@ -369,7 +378,7 @@ const ClassesPage: React.FC = () => {
),
},
],
[saveCell, canPurgeClass, handlePurge],
[saveCell, canPurgeClass, handlePurge, navigate, handleEdit, handleArchive],
);
return (