diff --git a/src/views/app-manager/upload-package/index.vue b/src/views/app-manager/upload-package/index.vue index 119d19a4..70dddd97 100644 --- a/src/views/app-manager/upload-package/index.vue +++ b/src/views/app-manager/upload-package/index.vue @@ -341,15 +341,39 @@ function openDetail(item: AppPackage) { detailVisible.value = true; } -function download(item: AppPackage) { +async function download(item: AppPackage) { if (!item.fileUrl) { ElMessage.warning("文件地址缺失"); return; } + + const url = withBase(item.fileUrl); + const fileName = item.originalName || `${item.packageName}.${item.packageType}`; + + // 先校验资源是否真实存在,避免后端返回的错误页(如 f.txt)被当作文件下载。 + // 受 CORS 限制时 HEAD 可能失败,此时回退为直接下载( 下载不受 CORS 限制),避免误报。 + try { + const resp = await fetch(url, { method: "HEAD" }); + if (!resp.ok) { + ElMessage.error(`下载失败:资源不存在(${resp.status})`); + return; + } + const contentType = resp.headers.get("Content-Type") || ""; + // 资源存在但返回的是文本/HTML/JSON 错误页,而不是实际安装包 + if (/(text\/html|text\/plain|application\/json)/i.test(contentType)) { + ElMessage.error("下载失败:资源未找到或链接无效"); + return; + } + } catch { + // HEAD 不可达(多为跨域限制),直接走原生下载兜底 + } + const a = document.createElement("a"); - a.href = withBase(item.fileUrl); - a.download = item.originalName || `${item.packageName}.${item.packageType}`; + a.href = url; + a.download = fileName; + document.body.appendChild(a); a.click(); + document.body.removeChild(a); } async function handleDelete(item: AppPackage) {