fix(admin): 通知分页、登录过期提示、附件打开反馈与导出统一 loading

- 通知中心改为游标分页 + 加载更多,历史通知不再被 50 条上限截断;
  加载失败显示错误态与重试
- AI 流式请求 401 被登出时,登录页提示"登录已过期",不再无声踢出
- AI 附件/引用来源打开失败时给出明确错误提示,不再"点了没反应"
- 新增 useDownload hook:统一导出/下载的防重复、loading 与成功/失败反馈;
  接入学生/账单/房间/入住/费用五个页面的模板下载与导出按钮
- 学生页移除不检查响应状态的私有下载实现,统一走 downloadBlob
This commit is contained in:
2026-08-07 17:33:58 +08:00
parent 67435e46ca
commit 00e2bc5acf
14 changed files with 320 additions and 153 deletions

View File

@@ -4,10 +4,10 @@ import { useQuery } from '@tanstack/react-query';
import { App, Button, Form, Space, Tabs } from 'antd';
import dayjs from 'dayjs';
import api from '../../api';
import { downloadBlob } from '../../utils/download';
import { message } from '../../ui/app-message';
import { usePermission } from '../../hooks/usePermission';
import { useApiMutation } from '../../hooks/useApiMutation';
import { useDownload } from '../../hooks/useDownload';
import { validateResponse } from '../../utils/validate';
import {
expenseLookupsSchema,
@@ -44,6 +44,12 @@ const ExpensesPage: React.FC = () => {
const [showArchived, setShowArchived] = useState(false);
const expenseViewPolicy = archiveViewPolicy(showArchived ? 'archived' : 'active');
const { downloading: utilityTemplateDownloading, run: runUtilityTemplateDownload } =
useDownload();
const { downloading: personalTemplateDownloading, run: runPersonalTemplateDownload } =
useDownload();
const { downloading: personalExportDownloading, run: runPersonalExportDownload } = useDownload();
const {
data: typeLookups = { typeOptions: [], personalTypeOptions: [], typeMap: {} },
} = useQuery<{
@@ -506,10 +512,12 @@ const ExpensesPage: React.FC = () => {
onPurge={handlePurgeRoom}
onImport={(formData) => mutations.importUtility.mutateAsync(formData)}
onTemplateDownload={() => {
void downloadBlob('/expenses/utility/template', '水电费导入模板.xlsx').catch(
() => message.error('下载失败'),
);
void runUtilityTemplateDownload('/expenses/utility/template', '水电费导入模板.xlsx', {
successMsg: '模板已下载',
errorMsg: '下载失败',
});
}}
templateLoading={utilityTemplateDownloading}
onAddUtility={() => setUtilityModal(true)}
/>
),
@@ -547,15 +555,19 @@ const ExpensesPage: React.FC = () => {
onPurge={handlePurgePersonal}
onImport={(formData) => mutations.importPersonal.mutateAsync(formData)}
onTemplateDownload={() => {
void downloadBlob('/expenses/personal/template', '个人附加费导入模板.xlsx').catch(
() => message.error('下载失败'),
);
void runPersonalTemplateDownload('/expenses/personal/template', '个人附加费导入模板.xlsx', {
successMsg: '模板已下载',
errorMsg: '下载失败',
});
}}
templateLoading={personalTemplateDownloading}
onExport={() => {
downloadBlob('/expenses/personal/export', '个人附加费导出.xlsx').catch(() =>
message.error('导出失败'),
);
void runPersonalExportDownload('/expenses/personal/export', '个人附加费导出.xlsx', {
successMsg: '导出成功',
errorMsg: '导出失败',
});
}}
exportLoading={personalExportDownloading}
/>
),
},