Files
crawler-plugin/admin-frontend-vue/src/pages/tasks/shop-data-api.ts
T
huangzd1997 441917bfce feat(后台/店铺数据): 结果下载增加进度弹窗
批量打包下载与单店下载接入 axios onDownloadProgress,progress 弹窗显示
百分比;后端未回 Content-Length 时退化为 indeterminate 流动条,避免用户
在大 zip 下载期间以为页面无响应。
2026-09-12 11:42:33 +08:00

89 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 店铺数据任务列表/批量下载适配(任务 110/113)。 */
import { http } from '@/api/http'
import { parseShopDataTaskPage, type ShopDataTaskPageResult } from './shop-data-model.ts'
import { toShopDataQuery, type ShopDataFilter } from './shop-data-filter.ts'
import { shopDataFilenameFromDisposition, toShopDataZipRequest } from './shop-data-download.ts'
import { downloadZipHeaderCounts } from './image-video-download.ts'
import { parseTaskPermissionPayload, toPermissionUserIdsPayload, type TaskPermissionItem } from './task-permission.ts'
export const SHOP_DATA_CRAWL_TASKS_ENDPOINT = '/api/admin/shop-data-crawl-tasks'
export const SHOP_DATA_TASK_PERMISSIONS_ENDPOINT = '/api/admin/shop-data-crawl-task-permissions'
export const SHOP_DATA_SINGLE_ENDPOINT = '/api/admin/shop-data-crawl'
export async function fetchShopDataTaskList(
filter: ShopDataFilter,
page: number,
pageSize: number,
): Promise<ShopDataTaskPageResult> {
const { data } = await http.get<unknown>(SHOP_DATA_CRAWL_TASKS_ENDPOINT, {
params: toShopDataQuery(filter, page, pageSize),
})
return parseShopDataTaskPage(data)
}
export interface ShopDataZipDownloadResult {
blob: Blob
fileCount?: number
errorCount?: number
}
/** 批量下载选中结果文件为 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 }
}
export interface ShopDataResultDownload {
blob: Blob
filename: string
}
/** 下载单店最新采集文件(按每日累计档 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}`)
}
/** 加载店铺数据任务数据范围授权用户:GET /api/admin/shop-data-crawl-task-permissions。 */
export async function fetchShopDataTaskPermissionUsers(): Promise<TaskPermissionItem[]> {
const { data } = await http.get<unknown>(SHOP_DATA_TASK_PERMISSIONS_ENDPOINT)
return parseTaskPermissionPayload(data)
}
/** 保存店铺数据任务数据范围授权:PUT /api/admin/shop-data-crawl-task-permissions { user_ids }。 */
export async function saveShopDataTaskPermissions(userIds: Iterable<number>): Promise<void> {
await http.put<unknown>(SHOP_DATA_TASK_PERMISSIONS_ENDPOINT, toPermissionUserIdsPayload(userIds))
}