From e6798bf313c1ffdba8ef2a67da118f7d85d4e334 Mon Sep 17 00:00:00 2001 From: rd <1344903914@qq.com> Date: Mon, 21 Jul 2025 15:10:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=89=B9=E9=87=8F=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/unplugin/auto-import.ts | 2 +- src/api/all/common.ts | 10 +++ .../components/export-task/index.vue | 89 ++++++++++++++++--- .../components/task-center-modal/index.vue | 1 + src/utils/arcoD.tsx | 4 +- src/utils/tools.ts | 3 + .../components/add-account-modal/index.vue | 7 +- .../components/add-account-modal/index.vue | 7 +- 8 files changed, 103 insertions(+), 20 deletions(-) diff --git a/config/unplugin/auto-import.ts b/config/unplugin/auto-import.ts index 5617d5a..7778cc9 100644 --- a/config/unplugin/auto-import.ts +++ b/config/unplugin/auto-import.ts @@ -16,7 +16,7 @@ export function configAutoImport() { '@vueuse/core', { dayjs: [['default', 'dayjs']], - 'lodash-es': ['cloneDeep', 'omit', 'pick', 'union', 'uniq', 'isNumber', 'uniqBy', 'isEmpty', 'merge'], + 'lodash-es': ['cloneDeep', 'omit', 'pick', 'union', 'uniq', 'isNumber', 'uniqBy', 'isEmpty', 'merge', 'debounce'], '@/hooks': ['useModal'], }, ], diff --git a/src/api/all/common.ts b/src/api/all/common.ts index 74d626d..8cc013a 100644 --- a/src/api/all/common.ts +++ b/src/api/all/common.ts @@ -47,4 +47,14 @@ export const patchTaskRead = (params = {}) => { // 任务中心-重做任务 export const postRedoTask = (id: string) => { return Http.post(`/v1/tasks/${id}/redo`); +}; + +// 任务中心-批量下载 +export const postBatchDownload = (params = {}) => { + return Http.post(`/v1/tasks/batch-download`, params); +}; + +// 任务中心-批量查询任务状态 +export const batchQueryTaskStatus = (params = {}) => { + return Http.get(`/v1/tasks/batch-query-status`, params); }; \ No newline at end of file diff --git a/src/components/_base/navbar/components/task-center-modal/components/export-task/index.vue b/src/components/_base/navbar/components/task-center-modal/components/export-task/index.vue index 39124d1..4ee0181 100644 --- a/src/components/_base/navbar/components/task-center-modal/components/export-task/index.vue +++ b/src/components/_base/navbar/components/task-center-modal/components/export-task/index.vue @@ -3,10 +3,10 @@ import { ref, computed } from 'vue'; import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-vue'; import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon'; import NoData from '@/components/no-data'; -import { getTask, postRedoTask } from '@/api/all/common'; +import { getTask, postRedoTask, postBatchDownload, batchQueryTaskStatus } from '@/api/all/common'; import { INITIAL_FORM, TABLE_COLUMNS } from './constants'; import { EXPORT_TASK_STATUS, enumTaskStatus } from '../../constants'; -import { formatTableField, exactFormatTime } from '@/utils/tools'; +import { formatTableField, exactFormatTime, genRandomId } from '@/utils/tools'; import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination'; import { downloadByUrl } from '@/utils/tools'; import DeleteTaskModal from './delete-task-modal.vue'; @@ -34,9 +34,11 @@ export default { getData(); }, }); + let queryTaskTimer = null; - const query = ref({ ...INITIAL_FORM }); + const query = ref(cloneDeep(INITIAL_FORM)); const deleteTaskModalRef = ref(null); + const downloadTaskIds = ref([]); const checkedAll = computed(() => selectedRows.value.length === dataSource.value.length); const indeterminate = computed( @@ -44,11 +46,12 @@ export default { ); const reset = () => { - query.value = { ...INITIAL_FORM }; - pageInfo.value = { ...DEFAULT_PAGE_INFO }; + query.value = cloneDeep(INITIAL_FORM); + pageInfo.value = cloneDeep(DEFAULT_PAGE_INFO); selectedRowKeys.value = []; selectedRows.value = []; dataSource.value = []; + downloadTaskIds.value = []; }; const init = () => { @@ -82,7 +85,7 @@ export default { reload(); }; - const handleCloseTip = () => { + const clearSelectedRows = () => { selectedRows.value = []; selectedRowKeys.value = []; }; @@ -98,8 +101,66 @@ export default { record.file && downloadByUrl(record.file); } }; - const handleBatchDownload = () => { - // 批量下载逻辑 + + const clearQueryTaskTimer = () => { + if (queryTaskTimer) { + clearInterval(queryTaskTimer); + queryTaskTimer = null; + } + }; + const startBatchDownload = (id) => { + const randomId = genRandomId(); + showExportNotification( + `正在批量下载“${selectedRows.value[0]?.name}”等${selectedRows.value.length}个文件,请稍后...`, + { + duration: 0, + id: randomId, + }, + ); + downloadTaskIds.value.push({ + id, + randomId, + }); + + // getSyncTaskStatus(randomId); + if (!queryTaskTimer) { + queryTaskTimer = setInterval(() => getSyncTaskStatus(), 3000); + } + }; + const handleBatchDownload = debounce(async () => { + const { code, data } = await postBatchDownload({ ids: selectedRowKeys.value }); + if (code === 200) { + startBatchDownload(data.id); + } + }, 500); + const getSyncTaskStatus = async () => { + const { code, data } = await batchQueryTaskStatus({ ids: downloadTaskIds.value.map((v) => v.id) }); + if (code === 200) { + let completeTaskNum = 0; + data.forEach((item) => { + const { status, file, id } = item; + if (status !== 0) { + completeTaskNum++; + + const notificationId = downloadTaskIds.value.find((v) => v.id === id)?.randomId; + notificationId && Notification.remove(notificationId); + + if (status === 1) { + AMessage.success('批量下载已完成,正在下载文件...'); + downloadByUrl(file); + } else if (status === 2) { + AMessage.error('批量下载失败,请重新尝试下载'); + } + } + }); + + // 全部完成了 + if (completeTaskNum === data.length) { + clearQueryTaskTimer(); + clearSelectedRows(); + downloadTaskIds.value = []; + } + } }; const handleDelete = (record) => { @@ -121,8 +182,14 @@ export default { selectedRowKeys.value = []; getData(); }; + const unloadComp = () => { + clearQueryTaskTimer(); + }; + onUnmounted(() => { + clearQueryTaskTimer; + }); - expose({ init, reset }); + expose({ init, reset, unloadComp }); return () => (
@@ -187,7 +254,7 @@ export default {
- + )} @@ -252,7 +319,7 @@ export default { ))} { activeTab.value = '0'; clearTimer(); + componentRef.value?.unloadComp?.() visible.value = false; }; const clearTimer = () => { diff --git a/src/utils/arcoD.tsx b/src/utils/arcoD.tsx index a2c6be6..1d8f9ee 100644 --- a/src/utils/arcoD.tsx +++ b/src/utils/arcoD.tsx @@ -13,7 +13,7 @@ interface RenderNotificationData { file?: string; } -export function showExportNotification(label: string, id: '') { +export function showExportNotification(label: string, { id = '', duration = 3000}) { Notification.warning({ id, showIcon: false, @@ -24,7 +24,7 @@ export function showExportNotification(label: string, id: '') {

{label}

), - duration: 3000, + duration, class: 'px-16px py-9px w-400px rounded-2px bg-#F0EDFF', }); } diff --git a/src/utils/tools.ts b/src/utils/tools.ts index f3b6b3b..2635bdc 100644 --- a/src/utils/tools.ts +++ b/src/utils/tools.ts @@ -107,3 +107,6 @@ export function downloadByUrl(url: string, filename?: string) { document.body.removeChild(a); } +export function genRandomId() { + return `id_${Date.now()}_${Math.floor(Math.random() * 10000)}`; +} \ No newline at end of file diff --git a/src/views/property-marketing/media-account/account-manage/components/add-account-modal/index.vue b/src/views/property-marketing/media-account/account-manage/components/add-account-modal/index.vue index c03f1ce..f5fe246 100644 --- a/src/views/property-marketing/media-account/account-manage/components/add-account-modal/index.vue +++ b/src/views/property-marketing/media-account/account-manage/components/add-account-modal/index.vue @@ -28,6 +28,7 @@ import SyncDataModal from '../sync-data-modal'; // import { downloadByUrl } from '@/utils/tools'; import { showExportNotification } from '@/utils/arcoD'; +import { genRandomId } from '@/utils/tools'; import { fetchAccountTags, fetchAccountGroups, @@ -188,9 +189,9 @@ export default { }, }); if (code === 200) { - const ID = 'IMPORT-ACCOUNT'; - showExportNotification(`正在导入“${file.value.name}”,请稍后...`, ID); - emit('startQueryTaskStatus', data.id, ID); + const id = genRandomId(); + showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id }); + emit('startQueryTaskStatus', data.id, id); onClose(); } else { uploadStatus.value = UploadStatus.ERROR; diff --git a/src/views/property-marketing/put-account/account-manage/components/add-account-modal/index.vue b/src/views/property-marketing/put-account/account-manage/components/add-account-modal/index.vue index fd34425..352d66d 100644 --- a/src/views/property-marketing/put-account/account-manage/components/add-account-modal/index.vue +++ b/src/views/property-marketing/put-account/account-manage/components/add-account-modal/index.vue @@ -151,6 +151,7 @@ import StatusBox from '../status-box'; import { PLATFORM_LIST, ENUM_PLATFORM } from '@/views/property-marketing/put-account/common_constants'; import { showExportNotification } from '@/utils/arcoD'; +import { genRandomId } from '@/utils/tools'; import { postPlacementAccounts, getPlacementAccountsDetail, @@ -289,9 +290,9 @@ const handleBatchImport = async () => { }); if (code === 200) { - const ID = 'IMPORT-PUT-ACCOUNT'; - showExportNotification(`正在导入“${file.value.name}”,请稍后...`, ID); - emits('startQueryTaskStatus', data.id, ID); + const id = genRandomId(); + showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id }); + emit('startQueryTaskStatus', data.id, id); onClose(); } else { uploadStatus.value = UploadStatus.ERROR;