feat(后台/店铺数据): 结果下载增加进度弹窗

批量打包下载与单店下载接入 axios onDownloadProgress,progress 弹窗显示
百分比;后端未回 Content-Length 时退化为 indeterminate 流动条,避免用户
在大 zip 下载期间以为页面无响应。
This commit is contained in:
2026-09-12 11:42:33 +08:00
parent 037a689316
commit 441917bfce
2 changed files with 66 additions and 6 deletions
@@ -27,10 +27,15 @@ export interface ShopDataZipDownloadResult {
errorCount?: number
}
/** 批量下载选中结果文件为 zipPOST /download-zip(blob);从响应头读成功/失败计数。 */
export async function requestShopDataZipDownload(resultIds: readonly (number | string)[]): Promise<ShopDataZipDownloadResult> {
/** 批量下载选中结果文件为 zipPOST /download-zip(blob);从响应头读成功/失败计数。
* onProgress: 可选下载进度回调(0-100,后端未回 Content-Length 时退化为不定进度)。 */
export async function requestShopDataZipDownload(
resultIds: readonly (number | string)[],
onProgress?: (percent: number) => void,
): Promise<ShopDataZipDownloadResult> {
const { data, headers } = await http.post<Blob>(`${SHOP_DATA_CRAWL_TASKS_ENDPOINT}/download-zip`, toShopDataZipRequest(resultIds), {
responseType: 'blob',
...(onProgress ? { onDownloadProgress: toAxiosProgress(onProgress) } : {}),
})
const counts = downloadZipHeaderCounts(headers as Record<string, string>)
return { blob: data, fileCount: counts.fileCount, errorCount: counts.errorCount }
@@ -41,15 +46,31 @@ export interface ShopDataResultDownload {
filename: string
}
/** 下载单店最新采集文件(按每日累计档 id):GET /api/admin/shop-data-crawl-tasks/daily-files/{id}/download。 */
export async function fetchShopDataResultDownload(resultId: number | string): Promise<ShopDataResultDownload> {
/** 下载单店最新采集文件(按每日累计档 id):GET /api/admin/shop-data-crawl-tasks/daily-files/{id}/download。
* onProgress: 可选下载进度回调(0-100)。 */
export async function fetchShopDataResultDownload(
resultId: number | string,
onProgress?: (percent: number) => void,
): Promise<ShopDataResultDownload> {
const { data, headers } = await http.get<Blob>(`${SHOP_DATA_CRAWL_TASKS_ENDPOINT}/daily-files/${resultId}/download`, {
responseType: 'blob',
...(onProgress ? { onDownloadProgress: toAxiosProgress(onProgress) } : {}),
})
const disposition = (headers as Record<string, string> | undefined)?.['content-disposition']
return { blob: data, filename: shopDataFilenameFromDisposition(disposition, `shop-data-task-${resultId}.xlsx`) }
}
/** 将 axios 进度事件转为 0-100 百分比;响应未回 Content-Length 时报 -1,前端显示不定进度。 */
function toAxiosProgress(onProgress: (percent: number) => void) {
return (event: { loaded: number; total?: number }) => {
if (event.total) {
onProgress(Math.min(100, Math.round((event.loaded / event.total) * 100)))
} else {
onProgress(-1)
}
}
}
/** 删除该店这条店铺数据记录(按每日累计档 id,管理端显式操作):DELETE .../daily-files/{id}。 */
export async function deleteShopDataResultHistory(resultId: number | string): Promise<void> {
await http.delete<unknown>(`${SHOP_DATA_CRAWL_TASKS_ENDPOINT}/daily-files/${resultId}`)