feat: 重构各业务模块管理页面与服务
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useApiMutation } from '../../hooks/useApiMutation';
|
||||
import { validateResponse } from '../../utils/validate';
|
||||
import { teacherListSchema } from '../../api/schemas';
|
||||
import { Table, Input, Modal, Form, Select, DatePicker, Tag, Space } from 'antd';
|
||||
import { EditOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -53,9 +57,6 @@ const DEFAULT_PAGE_SIZE = 20;
|
||||
const TeachersPage: React.FC = () => {
|
||||
const { hasPermission } = usePermission();
|
||||
const canEditTeachers = hasPermission('teacher:edit');
|
||||
const [data, setData] = useState<TeacherRow[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||
const [search, setSearch] = useState('');
|
||||
@@ -63,43 +64,62 @@ const TeachersPage: React.FC = () => {
|
||||
const [form] = Form.useForm<ProfileFormValues>();
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await api.get<TeacherListResponse>('/rbac/teachers', {
|
||||
params: { search: search || undefined, page, pageSize },
|
||||
});
|
||||
setData(res.list);
|
||||
setTotal(res.total);
|
||||
} catch {
|
||||
// silent
|
||||
}
|
||||
setLoading(false);
|
||||
}, [page, pageSize, search]);
|
||||
const {
|
||||
data: fetchResult = { list: [], total: 0 },
|
||||
isLoading,
|
||||
isFetching,
|
||||
} = useQuery<TeacherListResponse>({
|
||||
queryKey: ['rbac', 'teachers', page, pageSize, search],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
return validateResponse<TeacherListResponse>(
|
||||
teacherListSchema,
|
||||
await api.get<TeacherListResponse>('/rbac/teachers', {
|
||||
params: { search: search || undefined, page, pageSize },
|
||||
}),
|
||||
);
|
||||
} catch {
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
},
|
||||
});
|
||||
const data = fetchResult.list;
|
||||
const total = fetchResult.total;
|
||||
const loading = isLoading || isFetching;
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
const saveProfileMutation = useApiMutation(
|
||||
async ({
|
||||
id,
|
||||
values,
|
||||
}: {
|
||||
id: number;
|
||||
values: { subjects: string[]; joinedAt?: string; qualifications?: string };
|
||||
}) => api.put(`/rbac/teachers/${id}/profile`, values),
|
||||
{ invalidate: [['rbac', 'teachers']] },
|
||||
);
|
||||
const saveProfileCellMutation = useApiMutation(
|
||||
async ({ record, field, value }: { record: TeacherRow; field: string; value: unknown }) =>
|
||||
api.put(`/rbac/teachers/${record.id}/profile`, { [field]: value }),
|
||||
{ invalidate: [['rbac', 'teachers']] },
|
||||
);
|
||||
|
||||
const handleSaveProfile = async () => {
|
||||
const values = await form.validateFields();
|
||||
if (!profileModal) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
await api.put(`/rbac/teachers/${profileModal.id}/profile`, {
|
||||
subjects: values.subjects || [],
|
||||
joinedAt: values.joinedAt?.format('YYYY-MM-DD'),
|
||||
qualifications: values.qualifications,
|
||||
await saveProfileMutation.mutateAsync({
|
||||
id: profileModal.id,
|
||||
values: {
|
||||
subjects: values.subjects || [],
|
||||
joinedAt: values.joinedAt?.format('YYYY-MM-DD'),
|
||||
qualifications: values.qualifications,
|
||||
},
|
||||
});
|
||||
message.success('已更新');
|
||||
setProfileModal(null);
|
||||
fetchData();
|
||||
} catch (e: unknown) {
|
||||
let msg = '更新失败';
|
||||
if (e !== null && typeof e === 'object' && 'message' in e) {
|
||||
msg = String(e.message);
|
||||
}
|
||||
message.error(msg);
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -107,11 +127,14 @@ const TeachersPage: React.FC = () => {
|
||||
|
||||
const saveProfileCell = useCallback(
|
||||
async (record: TeacherRow, field: string, value: unknown) => {
|
||||
await api.put(`/rbac/teachers/${record.id}/profile`, { [field]: value });
|
||||
message.success('已保存');
|
||||
await fetchData();
|
||||
try {
|
||||
await saveProfileCellMutation.mutateAsync({ record, field, value });
|
||||
message.success('已保存');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
}
|
||||
},
|
||||
[fetchData],
|
||||
[saveProfileCellMutation],
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
|
||||
Reference in New Issue
Block a user