|
|
@@ -77,6 +77,8 @@ const ExportRecordList: React.FC<ExportRecordListProps> = ({
|
|
77
|
77
|
const [loading, setLoading] = useState(true);
|
|
78
|
78
|
const [page, setPage] = useState(1);
|
|
79
|
79
|
const [total, setTotal] = useState(0);
|
|
|
80
|
+ const [downloadingIds, setDownloadingIds] = useState<Set<string>>(new Set());
|
|
|
81
|
+ const [deletingIds, setDeletingIds] = useState<Set<string>>(new Set());
|
|
80
|
82
|
const pageSize = 20;
|
|
81
|
83
|
|
|
82
|
84
|
/**
|
|
|
@@ -132,13 +134,21 @@ const ExportRecordList: React.FC<ExportRecordListProps> = ({
|
|
132
|
134
|
const handleDownload = useCallback(
|
|
133
|
135
|
async (record: ExportRecord) => {
|
|
134
|
136
|
try {
|
|
|
137
|
+ if (downloadingIds.has(record.recordId)) return;
|
|
|
138
|
+ setDownloadingIds((current) => new Set(current).add(record.recordId));
|
|
135
|
139
|
await downloadExportRecord(record.recordId, userId);
|
|
136
|
140
|
message.success('开始下载文档');
|
|
137
|
141
|
} catch {
|
|
138
|
142
|
message.error('下载失败,请重试');
|
|
|
143
|
+ } finally {
|
|
|
144
|
+ setDownloadingIds((current) => {
|
|
|
145
|
+ const next = new Set(current);
|
|
|
146
|
+ next.delete(record.recordId);
|
|
|
147
|
+ return next;
|
|
|
148
|
+ });
|
|
139
|
149
|
}
|
|
140
|
150
|
},
|
|
141
|
|
- [userId]
|
|
|
151
|
+ [downloadingIds, userId]
|
|
142
|
152
|
);
|
|
143
|
153
|
|
|
144
|
154
|
/**
|
|
|
@@ -147,22 +157,35 @@ const ExportRecordList: React.FC<ExportRecordListProps> = ({
|
|
147
|
157
|
const handleDelete = useCallback(
|
|
148
|
158
|
async (record: ExportRecord) => {
|
|
149
|
159
|
try {
|
|
|
160
|
+ if (deletingIds.has(record.recordId)) return;
|
|
|
161
|
+ setDeletingIds((current) => new Set(current).add(record.recordId));
|
|
150
|
162
|
await deleteExportRecord(record.recordId, userId);
|
|
151
|
163
|
message.success('删除成功');
|
|
152
|
164
|
// Refresh the list
|
|
153
|
165
|
setLoading(true);
|
|
154
|
|
- await fetchRecords();
|
|
|
166
|
+ if (page > 1 && records.length === 1) {
|
|
|
167
|
+ setPage((currentPage) => currentPage - 1);
|
|
|
168
|
+ } else {
|
|
|
169
|
+ await fetchRecords();
|
|
|
170
|
+ }
|
|
155
|
171
|
} catch {
|
|
156
|
172
|
message.error('删除失败,请重试');
|
|
|
173
|
+ } finally {
|
|
|
174
|
+ setDeletingIds((current) => {
|
|
|
175
|
+ const next = new Set(current);
|
|
|
176
|
+ next.delete(record.recordId);
|
|
|
177
|
+ return next;
|
|
|
178
|
+ });
|
|
157
|
179
|
}
|
|
158
|
180
|
},
|
|
159
|
|
- [userId, fetchRecords]
|
|
|
181
|
+ [deletingIds, fetchRecords, page, records.length, userId]
|
|
160
|
182
|
);
|
|
161
|
183
|
|
|
162
|
184
|
/**
|
|
163
|
185
|
* Format file size to human-readable string
|
|
164
|
186
|
*/
|
|
165
|
187
|
const formatFileSize = (bytes: number): string => {
|
|
|
188
|
+ if (!Number.isFinite(bytes) || bytes < 0) return '大小未知';
|
|
166
|
189
|
if (bytes < 1024) return `${bytes} B`;
|
|
167
|
190
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
168
|
191
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
|
@@ -173,6 +196,7 @@ const ExportRecordList: React.FC<ExportRecordListProps> = ({
|
|
173
|
196
|
*/
|
|
174
|
197
|
const formatDate = (timestamp: number): string => {
|
|
175
|
198
|
const date = new Date(timestamp);
|
|
|
199
|
+ if (Number.isNaN(date.getTime())) return '时间未知';
|
|
176
|
200
|
return date.toLocaleString('zh-CN', {
|
|
177
|
201
|
year: 'numeric',
|
|
178
|
202
|
month: '2-digit',
|
|
|
@@ -224,6 +248,8 @@ const ExportRecordList: React.FC<ExportRecordListProps> = ({
|
|
224
|
248
|
type="text"
|
|
225
|
249
|
icon={<DownloadOutlined />}
|
|
226
|
250
|
onClick={() => handleDownload(record)}
|
|
|
251
|
+ loading={downloadingIds.has(record.recordId)}
|
|
|
252
|
+ disabled={deletingIds.has(record.recordId)}
|
|
227
|
253
|
data-testid="download-button"
|
|
228
|
254
|
/>
|
|
229
|
255
|
</Tooltip>,
|
|
|
@@ -233,7 +259,10 @@ const ExportRecordList: React.FC<ExportRecordListProps> = ({
|
|
233
|
259
|
onConfirm={() => handleDelete(record)}
|
|
234
|
260
|
okText="删除"
|
|
235
|
261
|
cancelText="取消"
|
|
236
|
|
- okButtonProps={{ danger: true }}
|
|
|
262
|
+ okButtonProps={{
|
|
|
263
|
+ danger: true,
|
|
|
264
|
+ loading: deletingIds.has(record.recordId),
|
|
|
265
|
+ }}
|
|
237
|
266
|
>
|
|
238
|
267
|
<Tooltip title="删除">
|
|
239
|
268
|
<Button
|