hookehuyr

feat(文件下载): 添加下载失败提示弹窗和手动复制链接功能

当文件自动下载失败时,显示包含文件名和链接的弹窗,并提供复制链接功能
改进下载逻辑,先尝试直接下载,失败后使用axios下载并处理各种错误情况
......@@ -305,6 +305,60 @@
</template>
</div>
</van-popup>
<!-- 下载失败提示弹窗 -->
<van-popup
v-model:show="showDownloadFailDialog"
position="center"
round
closeable
:style="{ width: '85%', maxWidth: '400px' }"
>
<div class="p-6">
<div class="text-center mb-4">
<van-icon name="warning-o" size="48" color="#ff6b6b" class="mb-2" />
<h3 class="text-lg font-medium text-gray-800">下载失败</h3>
</div>
<div class="text-center text-gray-600 mb-4">
<p class="mb-2">暂时无法自动下载文件</p>
<p class="text-sm">请复制下方链接手动下载</p>
</div>
<div class="mb-4">
<div class="text-sm text-gray-500 mb-2">文件名:</div>
<div class="bg-gray-50 p-3 rounded-lg text-sm break-all">
{{ downloadFailInfo.fileName }}
</div>
</div>
<div class="mb-6">
<div class="text-sm text-gray-500 mb-2">文件链接:</div>
<div class="bg-gray-50 p-3 rounded-lg text-sm break-all max-h-20 overflow-y-auto">
{{ downloadFailInfo.fileUrl }}
</div>
</div>
<div class="flex gap-3">
<van-button
block
type="default"
@click="showDownloadFailDialog = false"
class="flex-1"
>
关闭
</van-button>
<van-button
block
type="primary"
@click="copyToClipboard(downloadFailInfo.fileUrl)"
class="flex-1"
>
复制链接
</van-button>
</div>
</div>
</van-popup>
</div>
</template>
......@@ -734,6 +788,71 @@ watch(showCommentPopup, async (newVal) => {
}
});
// 下载文件失败提示弹窗状态
const showDownloadFailDialog = ref(false);
const downloadFailInfo = ref({
fileName: '',
fileUrl: ''
});
/**
* 复制文件URL到剪贴板
* @param {string} url - 要复制的URL
*/
const copyToClipboard = async (url) => {
try {
if (navigator.clipboard && window.isSecureContext) {
// 现代浏览器支持的方式
await navigator.clipboard.writeText(url);
showToast('文件链接已复制到剪贴板');
} else {
// 兼容旧浏览器的方式
const textArea = document.createElement('textarea');
textArea.value = url;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showToast('文件链接已复制到剪贴板');
} catch (err) {
console.error('复制失败:', err);
showToast('复制失败,请手动复制链接');
} finally {
document.body.removeChild(textArea);
}
}
} catch (err) {
console.error('复制到剪贴板失败:', err);
showToast('复制失败,请手动复制链接');
}
};
/**
* 尝试直接下载文件(适用于同源或支持CORS的文件)
* @param {string} fileUrl - 文件URL
* @param {string} fileName - 文件名
*/
const tryDirectDownload = (fileUrl, fileName) => {
try {
const a = document.createElement('a');
a.href = fileUrl;
a.download = fileName;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
return true;
} catch (error) {
console.error('直接下载失败:', error);
return false;
}
};
// 下载文件
const downloadFile = ({ title, url, meta_id }) => {
// 获取文件URL和文件名
......@@ -769,33 +888,85 @@ const downloadFile = ({ title, url, meta_id }) => {
return mimeTypes[extension] || 'application/octet-stream';
};
// 首先尝试直接下载(适用于同源文件或支持下载的链接)
const directDownloadSuccess = tryDirectDownload(fileUrl, fileName);
// 如果直接下载可能成功,等待一段时间后检查是否真的成功
if (directDownloadSuccess) {
// 记录下载行为
let paramsObj = {
schedule_id: courseId.value,
meta_id
}
addRecord(paramsObj);
return;
}
// 如果直接下载失败,尝试通过axios下载
axios({
method: 'get',
url: fileUrl,
responseType: 'blob' // 表示返回的数据类型是Blob
responseType: 'blob', // 表示返回的数据类型是Blob
timeout: 30000 // 设置30秒超时
}).then((response) => {
try {
const blob = new Blob([response.data], { type: getMimeType(fileUrl) });
const url = window.URL.createObjectURL(blob);
const blobUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.href = blobUrl;
a.download = fileName;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
// 延迟释放URL,确保下载完成
setTimeout(() => {
window.URL.revokeObjectURL(blobUrl);
}, 1000);
// 新增记录
let paramsObj = {
schedule_id: courseId.value,
meta_id
}
addRecord(paramsObj);
showToast('文件下载已开始');
} catch (blobError) {
console.error('创建下载链接失败:', blobError);
// 显示下载失败提示
downloadFailInfo.value = {
fileName: fileName,
fileUrl: fileUrl
};
showDownloadFailDialog.value = true;
}
}).catch((error) => {
console.error('下载文件出错:', error);
});
// 根据错误类型提供不同的处理方式
let errorMessage = '下载失败';
if (error.code === 'ECONNABORTED' || error.message.includes('timeout')) {
errorMessage = '下载超时';
} else if (error.response && error.response.status === 404) {
errorMessage = '文件不存在';
} else if (error.response && error.response.status === 403) {
errorMessage = '无权限访问文件';
} else if (error.message.includes('CORS') || error.message.includes('cross-origin')) {
errorMessage = '跨域访问限制';
}
console.log(`${errorMessage},显示手动下载提示`);
// 显示下载失败提示弹窗
downloadFailInfo.value = {
fileName: fileName,
fileUrl: fileUrl
};
showDownloadFailDialog.value = true;
});
}
/**
......