fix(admin): UX improvements — silent fetch failures, empty states, batch loading guards, dashboard refresh

- Replace console.error-only catches with message.error user-facing notifications
  across Bills, Classes, ClassroomRentals, ClassroomSchedule, Classrooms, Deposits,
  Expenses, OperationLogs, Permissions, Roles, RoomVisual, Rooms, Students,
  Tenants, Users
- Add Empty component via Table locale prop on list pages: Bills, Classes,
  ClassroomRentals, Classrooms, Deposits, Expenses (room+personal), Occupancies,
  Rooms, Students, Tenants, Roles
- Add batchLoading state to batch delete/update operations: Bills (batchDelete,
  batchUpdateStatus), Expenses (batchDeleteRoom, batchDeletePersonal),
  Occupancies (batchCheckOut, batchDelete), Rooms (batchDelete),
  Students (batchDelete)
- Add refreshLoading indicator to Dashboard header when re-fetching data
- Consistent error pattern: catch (e: unknown) { const err = e as { message?: string }; message.error(...); }
This commit is contained in:
2026-07-09 18:03:42 +08:00
parent 6029d8e2fd
commit c59fd6ce92
17 changed files with 141 additions and 59 deletions

View File

@@ -83,13 +83,19 @@ const DashboardPage: React.FC = () => {
const [roomRanking, setRoomRanking] = useState<Array<{ roomNumber: string; total: string }>>([]);
const [classroomUtil, setClassroomUtil] = useState<ClassroomUtilStats | null>(null);
const [loading, setLoading] = useState(true);
const [refreshLoading, setRefreshLoading] = useState(false);
const [period, setPeriod] = useState<[string, string]>([
dayjs().startOf('month').format('YYYY-MM-DD'),
dayjs().endOf('month').format('YYYY-MM-DD'),
]);
const fetchData = useCallback(async () => {
setLoading(true);
const isRefresh = stats !== null;
if (isRefresh) {
setRefreshLoading(true);
} else {
setLoading(true);
}
try {
const [s, rr, cr, g] = await Promise.all([
api.get<DashboardStats>('/dashboard/stats'),
@@ -114,7 +120,8 @@ const DashboardPage: React.FC = () => {
message.error('数据加载失败,请稍后重试');
}
setLoading(false);
}, [period]);
setRefreshLoading(false);
}, [period, stats]);
useEffect(() => {
fetchData();
@@ -321,7 +328,7 @@ const DashboardPage: React.FC = () => {
gap: 12,
}}
>
<h2 style={{ margin: 0 }}></h2>
<h2 style={{ margin: 0 }}>{refreshLoading && <Spin size="small" style={{ marginLeft: 12 }} />}</h2>
<RangePicker
aria-label="选择日期范围"
value={[dayjs(period[0]), dayjs(period[1])]}