|
1 | 1 | import { DownloadOutlined, FileTextOutlined } from '@ant-design/icons'; |
2 | | -import { Button, DatePicker, Grid, Table, Typography } from 'antd'; |
| 2 | +import { Button, DatePicker, Grid, message, Table, Typography } from 'antd'; |
3 | 3 | import type { ColumnType } from 'antd/lib/table'; |
4 | 4 | import dayjs, { type Dayjs } from 'dayjs'; |
5 | 5 | import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; |
6 | 6 | import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; |
7 | 7 | import relativeTime from 'dayjs/plugin/relativeTime'; |
8 | 8 | import { useMemo, useState } from 'react'; |
9 | | -import * as XLSX from 'xlsx'; |
10 | 9 | import { useAuditLogs } from '@/utils/hooks'; |
11 | 10 | import 'dayjs/locale/zh-cn'; |
12 | 11 | import { UAParser } from 'ua-parser-js'; |
@@ -324,72 +323,78 @@ export const AuditLogs = () => { |
324 | 323 | }; |
325 | 324 |
|
326 | 325 | // 导出到 Excel |
327 | | - const handleExportToExcel = () => { |
| 326 | + const handleExportToExcel = async () => { |
328 | 327 | if (filteredAuditLogs.length === 0) { |
329 | 328 | return; |
330 | 329 | } |
331 | 330 |
|
332 | | - // 格式化数据 |
333 | | - const excelData = filteredAuditLogs.map((log) => { |
334 | | - const date = dayjs(log.createdAt); |
335 | | - const actionLabel = getActionLabel(log.method, log.path); |
336 | | - |
337 | | - // 解析 UA 信息 |
338 | | - let browserInfo = '-'; |
339 | | - let osInfo = '-'; |
340 | | - if (log.userAgent) { |
341 | | - // 处理特殊的 CLI useragent 格式 |
342 | | - if (log.userAgent.startsWith('react-native-update-cli')) { |
343 | | - const version = log.userAgent.split('/')[1] || ''; |
344 | | - browserInfo = `cli ${version}`.trim(); |
345 | | - osInfo = '-'; |
346 | | - } else { |
347 | | - const { browser, os } = UAParser(log.userAgent); |
348 | | - browserInfo = |
349 | | - `${browser.name || '-'} ${browser.version || ''}`.trim(); |
350 | | - osInfo = `${os.name || '-'} ${os.version || ''}`.trim(); |
| 331 | + try { |
| 332 | + const XLSX = await import('xlsx'); |
| 333 | + |
| 334 | + // 格式化数据 |
| 335 | + const excelData = filteredAuditLogs.map((log) => { |
| 336 | + const date = dayjs(log.createdAt); |
| 337 | + const actionLabel = getActionLabel(log.method, log.path); |
| 338 | + |
| 339 | + // 解析 UA 信息 |
| 340 | + let browserInfo = '-'; |
| 341 | + let osInfo = '-'; |
| 342 | + if (log.userAgent) { |
| 343 | + // 处理特殊的 CLI useragent 格式 |
| 344 | + if (log.userAgent.startsWith('react-native-update-cli')) { |
| 345 | + const version = log.userAgent.split('/')[1] || ''; |
| 346 | + browserInfo = `cli ${version}`.trim(); |
| 347 | + osInfo = '-'; |
| 348 | + } else { |
| 349 | + const { browser, os } = UAParser(log.userAgent); |
| 350 | + browserInfo = |
| 351 | + `${browser.name || '-'} ${browser.version || ''}`.trim(); |
| 352 | + osInfo = `${os.name || '-'} ${os.version || ''}`.trim(); |
| 353 | + } |
351 | 354 | } |
352 | | - } |
353 | 355 |
|
354 | | - return { |
355 | | - 时间: date.format('YYYY-MM-DD HH:mm:ss'), |
356 | | - 操作: actionLabel, |
357 | | - 状态码: log.statusCode, |
358 | | - 提交数据: log.data ? JSON.stringify(log.data) : '-', |
359 | | - 浏览器: browserInfo, |
360 | | - 操作系统: osInfo, |
361 | | - IP地址: log.ip || '-', |
362 | | - 'API Key': log.apiTokens |
363 | | - ? `${log.apiTokens.name}(${log.apiTokens.tokenSuffix})` |
364 | | - : '-', |
365 | | - }; |
366 | | - }); |
367 | | - |
368 | | - // 创建工作簿 |
369 | | - const wb = XLSX.utils.book_new(); |
370 | | - const ws = XLSX.utils.json_to_sheet(excelData); |
371 | | - |
372 | | - // 设置列宽 |
373 | | - const colWidths = [ |
374 | | - { wch: 20 }, // 时间 |
375 | | - { wch: 15 }, // 操作 |
376 | | - { wch: 10 }, // 状态码 |
377 | | - { wch: 40 }, // 提交数据 |
378 | | - { wch: 20 }, // 浏览器 |
379 | | - { wch: 20 }, // 操作系统 |
380 | | - { wch: 15 }, // IP地址 |
381 | | - { wch: 15 }, // 是否使用API |
382 | | - ]; |
383 | | - ws['!cols'] = colWidths; |
384 | | - |
385 | | - // 添加工作表到工作簿 |
386 | | - XLSX.utils.book_append_sheet(wb, ws, '操作日志'); |
387 | | - |
388 | | - // 生成文件名 |
389 | | - const fileName = `操作日志_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`; |
390 | | - |
391 | | - // 导出文件 |
392 | | - XLSX.writeFile(wb, fileName); |
| 356 | + return { |
| 357 | + 时间: date.format('YYYY-MM-DD HH:mm:ss'), |
| 358 | + 操作: actionLabel, |
| 359 | + 状态码: log.statusCode, |
| 360 | + 提交数据: log.data ? JSON.stringify(log.data) : '-', |
| 361 | + 浏览器: browserInfo, |
| 362 | + 操作系统: osInfo, |
| 363 | + IP地址: log.ip || '-', |
| 364 | + 'API Key': log.apiTokens |
| 365 | + ? `${log.apiTokens.name}(${log.apiTokens.tokenSuffix})` |
| 366 | + : '-', |
| 367 | + }; |
| 368 | + }); |
| 369 | + |
| 370 | + // 创建工作簿 |
| 371 | + const wb = XLSX.utils.book_new(); |
| 372 | + const ws = XLSX.utils.json_to_sheet(excelData); |
| 373 | + |
| 374 | + // 设置列宽 |
| 375 | + const colWidths = [ |
| 376 | + { wch: 20 }, // 时间 |
| 377 | + { wch: 15 }, // 操作 |
| 378 | + { wch: 10 }, // 状态码 |
| 379 | + { wch: 40 }, // 提交数据 |
| 380 | + { wch: 20 }, // 浏览器 |
| 381 | + { wch: 20 }, // 操作系统 |
| 382 | + { wch: 15 }, // IP地址 |
| 383 | + { wch: 15 }, // 是否使用API |
| 384 | + ]; |
| 385 | + ws['!cols'] = colWidths; |
| 386 | + |
| 387 | + // 添加工作表到工作簿 |
| 388 | + XLSX.utils.book_append_sheet(wb, ws, '操作日志'); |
| 389 | + |
| 390 | + // 生成文件名 |
| 391 | + const fileName = `操作日志_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`; |
| 392 | + |
| 393 | + // 导出文件 |
| 394 | + XLSX.writeFile(wb, fileName); |
| 395 | + } catch (error) { |
| 396 | + message.error(`导出失败:${(error as Error).message}`); |
| 397 | + } |
393 | 398 | }; |
394 | 399 |
|
395 | 400 | return ( |
|
0 commit comments