| export async function apiFetch<T>(url: string, options?: RequestInit): Promise<T> { |
| const response = await fetch(url, options); |
| const payload = await response.json().catch(() => null); |
| if (!response.ok || !payload || payload.code !== 0) { |
| throw new Error(payload?.msg || `HTTP ${response.status}`); |
| } |
| return payload.data as T; |
| } |
|
|
| export async function downloadBlob(url: string, fallbackName: string): Promise<void> { |
| const response = await fetch(url); |
| if (!response.ok) { |
| const payload = await response.json().catch(() => null); |
| throw new Error(payload?.msg || `HTTP ${response.status}`); |
| } |
| const blob = await response.blob(); |
| const filename = filenameFromDisposition(response.headers.get("Content-Disposition")) || fallbackName; |
| const objectURL = URL.createObjectURL(blob); |
| try { |
| const link = document.createElement("a"); |
| link.href = objectURL; |
| link.download = filename; |
| document.body.appendChild(link); |
| link.click(); |
| link.remove(); |
| } finally { |
| URL.revokeObjectURL(objectURL); |
| } |
| } |
|
|
| export function filenameFromDisposition(disposition: string | null): string { |
| if (!disposition) return ""; |
| const utf8 = /filename\*=UTF-8''([^;]+)/i.exec(disposition); |
| if (utf8) return decodeURIComponent(utf8[1]); |
| const quoted = /filename="([^"]+)"/i.exec(disposition); |
| if (quoted) return quoted[1]; |
| return ""; |
| } |
|
|
| export function downloadText(filename: string, content: string, type: string): void { |
| const blob = new Blob([content], { type }); |
| const objectURL = URL.createObjectURL(blob); |
| try { |
| const link = document.createElement("a"); |
| link.href = objectURL; |
| link.download = filename; |
| document.body.appendChild(link); |
| link.click(); |
| link.remove(); |
| } finally { |
| URL.revokeObjectURL(objectURL); |
| } |
| } |
|
|