fix(reports): export via authed axios blob download instead of window.open
Some checks failed
CI / test (push) Failing after 2m50s

启用登录鉴权后,导出 HTML/MD/JSON 用 window.open 直连 API 无法携带
X-Auth-Token,服务端返回 401 导致导出失效。改为经 axios 拉取 blob
(拦截器自动附加凭据)后触发浏览器下载。
This commit is contained in:
sinohqb 2026-07-29 15:35:34 +08:00
parent 5dd3c4ff1c
commit cbab55843b
3 changed files with 14 additions and 7 deletions

View File

@ -264,9 +264,16 @@ export interface RunLogsResponse {
export const reportsApi = {
get: (runId: string) => api.get(`/reports/${runId}`),
html: (runId: string) => api.get(`/reports/${runId}/html`),
json: (runId: string) => api.get(`/reports/${runId}/json`),
markdownUrl: (runId: string) => `/api/reports/${runId}/markdown`,
download: async (runId: string, format: 'html' | 'json' | 'markdown') => {
const ext = format === 'markdown' ? 'md' : format
const res = await api.get(`/reports/${runId}/${format}`, { responseType: 'blob' })
const url = URL.createObjectURL(res.data as Blob)
const a = document.createElement('a')
a.href = url
a.download = `report-${runId.slice(0, 8)}.${ext}`
a.click()
URL.revokeObjectURL(url)
},
compare: (run1: string, run2: string) =>
api.get(`/reports/compare`, { params: { run1, run2 } }),
}

View File

@ -325,10 +325,10 @@ export default function ReportsPage() {
{viewMode === 'single' && report && (
<Space style={{ flexShrink: 0 }}>
<Button icon={<DownloadOutlined />} onClick={() => window.open(`/api/reports/${selectedRunId}/html`, '_blank')}>
<Button icon={<DownloadOutlined />} onClick={() => reportsApi.download(selectedRunId, 'html')}>
HTML
</Button>
<Button icon={<FileMarkdownOutlined />} onClick={() => window.open(reportsApi.markdownUrl(selectedRunId), '_blank')}>
<Button icon={<FileMarkdownOutlined />} onClick={() => reportsApi.download(selectedRunId, 'markdown')}>
MD
</Button>
</Space>

View File

@ -6,7 +6,7 @@ import {
import {
FileTextOutlined, PlayCircleOutlined, ReloadOutlined, StopOutlined,
} from '@ant-design/icons'
import { runsApi, scenariosApi, targetsApi, type Run, type Scenario, type Target } from '../api'
import { reportsApi, runsApi, scenariosApi, targetsApi, type Run, type Scenario, type Target } from '../api'
import RunList from '../components/RunList'
import CaseBlock from '../components/CaseBlock'
import RuleOverview from '../components/RuleOverview'
@ -226,7 +226,7 @@ export default function RunsPage() {
}
}}
onOpenReport={handleOpenReport}
onExportJson={(runId) => window.open(`/api/reports/${runId}/json`, '_blank')}
onExportJson={(runId) => reportsApi.download(runId, 'json')}
onRerun={async () => {
if (!session.run) return
await startRun(session.run.target_id, session.run.scenario_id)