/** * 带鉴权下载二进制文件(Excel 等)。 * 为何:window.open 带 token 遇 JSON 错误页难提示;统一走 fetch + blob。 */ import { useGlobSetting } from '/@/hooks/setting'; import { getToken } from '/@/utils/auth'; export type AuthDownloadParams = Record; /** * GET 下载附件并触发浏览器保存。 * @param path 相对 API 路径,如 /inquiry/workOrder/view/export * @param params query 参数(空值跳过) * @param fallbackName 无 Content-Disposition 时的文件名 */ export async function downloadAuthGet( path: string, params: AuthDownloadParams = {}, fallbackName = 'download.bin', ): Promise { const glob = useGlobSetting(); const base = (glob.apiUrl || '').replace(/\/$/, ''); const qs = new URLSearchParams(); Object.keys(params).forEach((k) => { const v = params[k]; if (v === undefined || v === null || String(v).trim() === '') return; qs.set(k, String(v)); }); const url = `${base}${path}${qs.toString() ? `?${qs.toString()}` : ''}`; const token = getToken() || ''; const res = await fetch(url, { method: 'GET', headers: { 'X-Access-Token': String(token), }, }); const ctype = (res.headers.get('content-type') || '').toLowerCase(); if (!res.ok || ctype.includes('application/json')) { let msg = `下载失败 HTTP ${res.status}`; try { const j = await res.json(); if (j?.message) msg = String(j.message); } catch { /* ignore */ } throw new Error(msg); } const blob = await res.blob(); let name = fallbackName; const cd = res.headers.get('content-disposition') || ''; const m = /filename\*=UTF-8''([^;]+)|filename=\"?([^\";]+)\"?/i.exec(cd); if (m) { name = decodeURIComponent((m[1] || m[2] || name).trim()); } const href = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = href; a.download = name; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(href); }