import { Notify, platformIsAIO, Toast } from '@/platform/index'; import { useShowScanCode } from '@/composables/useShowScanCode'; export interface DownloadFromUrlOptions { /** 保存时的文件名;不传则从响应头或 URL 路径推断 */ filename?: string; /** 传给 fetch 的额外配置(如 credentials、headers) */ fetchInit?: RequestInit; } function filenameFromContentDisposition(header: string | null): string | undefined { if (!header) return; const utf8 = /filename\*=UTF-8''([^;\s]+)/i.exec(header); if (utf8?.[1]) { try { return decodeURIComponent(utf8[1].replace(/['"]/g, '')); } catch { return utf8[1]; } } const plain = /filename\s*=\s*("?)([^";\n]+)\1/i.exec(header); return plain?.[2]; } function filenameFromUrl(url: string): string { try { const path = new URL(url, typeof location !== 'undefined' ? location.href : undefined).pathname; const seg = path.split('/').filter(Boolean).pop(); return seg || 'download'; } catch { return 'download'; } } /** * 根据文件 URL 下载到本地(通过 fetch 取 Blob 后触发浏览器保存)。 * 跨域资源需服务端允许 CORS,否则 fetch 会失败。 */ export function downloadFromUrl(url: string, options?: DownloadFromUrlOptions): Promise { return (async () => { const res = await fetch(url, options?.fetchInit); if (!res.ok) throw new Error(`下载失败: ${res.status} ${res.statusText}`); const blob = await res.blob(); const filename = options?.filename ?? filenameFromContentDisposition(res.headers.get('content-disposition')) ?? filenameFromUrl(url); const objectUrl = URL.createObjectURL(blob); try { const a = document.createElement('a'); a.href = objectUrl; a.download = filename; a.rel = 'noopener'; document.body.appendChild(a); a.click(); a.remove(); } finally { URL.revokeObjectURL(objectUrl); } })(); } export async function printFromUrl(url: string, options?: { rollback?: boolean; title?: string }) { let closed = false; if (platformIsAIO()) { try { try { await Bridge.print({ url }); } catch { window.AIO?.print?.(url); } } catch (e) { Notify.warning(`打印失败 (${e.message})`, { duration: 1500 }); closed = true; } } else { try { const current = window.open(url, '_blank'); closed = current.closed; current.location.href; } catch (e) { Notify.warning(`无法打开窗口 (${e.message})`, { duration: 1500 }); closed = true; } } if (closed && options?.rollback) { Toast.close(); await useShowScanCode().open({ url, title: options?.title }); } }