fix(desktop): 修复导出文件下载
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { beforeEach, expect, it, vi } from 'vitest'
|
||||
|
||||
const { hostInvoke } = vi.hoisted(() => ({ hostInvoke: vi.fn() }))
|
||||
vi.mock('./platform/desktop', () => ({ isDesktop: () => true, hostInvoke }))
|
||||
|
||||
import apiClient from './apiClient'
|
||||
|
||||
beforeEach(() => hostInvoke.mockReset())
|
||||
|
||||
it('restores binary desktop responses as browser-compatible response objects', async () => {
|
||||
hostInvoke.mockResolvedValue({
|
||||
status: 200,
|
||||
content_type: 'application/pdf',
|
||||
body: '',
|
||||
body_base64: 'JVBERi0xLjc=',
|
||||
})
|
||||
|
||||
const response = await apiClient.get<Response>('/api/exports/job/file')
|
||||
|
||||
expect(response).toBeInstanceOf(Response)
|
||||
expect(response.headers.get('content-type')).toBe('application/pdf')
|
||||
expect([...new Uint8Array(await response.arrayBuffer())]).toEqual([37, 80, 68, 70, 45, 49, 46, 55])
|
||||
})
|
||||
|
||||
it('keeps downloadable text responses wrapped in a response object', async () => {
|
||||
hostInvoke.mockResolvedValue({
|
||||
status: 200,
|
||||
content_type: 'text/html; charset=utf-8',
|
||||
body: '',
|
||||
body_base64: 'PGgxPuWvvOWHujwvaDE+',
|
||||
})
|
||||
|
||||
const response = await apiClient.get<Response>('/api/exports/job/file')
|
||||
|
||||
expect(response).toBeInstanceOf(Response)
|
||||
expect(await response.text()).toBe('<h1>导出</h1>')
|
||||
})
|
||||
@@ -4,7 +4,26 @@ import { hostInvoke, isDesktop } from './platform/desktop'
|
||||
// 所有 HTTP 请求都经过此边界,以统一地址、请求追踪和错误契约。
|
||||
const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? import.meta.env.VITE_API_BASE ?? (isDesktop() ? 'http://127.0.0.1:8000' : '')
|
||||
|
||||
interface DesktopCoreResponse { status: number; content_type: string; body: string }
|
||||
interface DesktopCoreResponse {
|
||||
status: number
|
||||
content_type: string
|
||||
body: string
|
||||
body_base64?: string
|
||||
}
|
||||
|
||||
function responseFromDesktopCore(response: DesktopCoreResponse): Response {
|
||||
let body: BodyInit = response.body
|
||||
if (response.body_base64 !== undefined) {
|
||||
const binary = atob(response.body_base64)
|
||||
const bytes = new Uint8Array(binary.length)
|
||||
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index)
|
||||
body = bytes.buffer
|
||||
}
|
||||
return new Response(body, {
|
||||
status: response.status,
|
||||
headers: response.content_type ? { 'Content-Type': response.content_type } : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveApiUrl(path: string): string {
|
||||
if (/^https?:\/\//i.test(path)) return path
|
||||
@@ -72,7 +91,9 @@ async function request<T>(path: string, options: RequestOptions = {}): Promise<T
|
||||
})
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
if (response.status === 204) return undefined as T
|
||||
return (response.content_type.includes('application/json') ? JSON.parse(response.body) : response.body) as T
|
||||
return (response.content_type.includes('application/json')
|
||||
? JSON.parse(response.body)
|
||||
: responseFromDesktopCore(response)) as T
|
||||
}
|
||||
let error: ErrorResponse | null = null
|
||||
try { error = JSON.parse(response.body) as ErrorResponse } catch { /* 非 JSON 错误 */ }
|
||||
|
||||
Reference in New Issue
Block a user