feat: 批量下载任务

This commit is contained in:
rd
2025-07-21 15:10:26 +08:00
parent 4800d5fa99
commit e6798bf313
8 changed files with 103 additions and 20 deletions

View File

@ -16,7 +16,7 @@ export function configAutoImport() {
'@vueuse/core', '@vueuse/core',
{ {
dayjs: [['default', 'dayjs']], 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'], '@/hooks': ['useModal'],
}, },
], ],

View File

@ -48,3 +48,13 @@ export const patchTaskRead = (params = {}) => {
export const postRedoTask = (id: string) => { export const postRedoTask = (id: string) => {
return Http.post(`/v1/tasks/${id}/redo`); 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);
};

View File

@ -3,10 +3,10 @@ import { ref, computed } from 'vue';
import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-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 { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
import NoData from '@/components/no-data'; 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 { INITIAL_FORM, TABLE_COLUMNS } from './constants';
import { EXPORT_TASK_STATUS, enumTaskStatus } 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 { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
import { downloadByUrl } from '@/utils/tools'; import { downloadByUrl } from '@/utils/tools';
import DeleteTaskModal from './delete-task-modal.vue'; import DeleteTaskModal from './delete-task-modal.vue';
@ -34,9 +34,11 @@ export default {
getData(); getData();
}, },
}); });
let queryTaskTimer = null;
const query = ref({ ...INITIAL_FORM }); const query = ref(cloneDeep(INITIAL_FORM));
const deleteTaskModalRef = ref(null); const deleteTaskModalRef = ref(null);
const downloadTaskIds = ref([]);
const checkedAll = computed(() => selectedRows.value.length === dataSource.value.length); const checkedAll = computed(() => selectedRows.value.length === dataSource.value.length);
const indeterminate = computed( const indeterminate = computed(
@ -44,11 +46,12 @@ export default {
); );
const reset = () => { const reset = () => {
query.value = { ...INITIAL_FORM }; query.value = cloneDeep(INITIAL_FORM);
pageInfo.value = { ...DEFAULT_PAGE_INFO }; pageInfo.value = cloneDeep(DEFAULT_PAGE_INFO);
selectedRowKeys.value = []; selectedRowKeys.value = [];
selectedRows.value = []; selectedRows.value = [];
dataSource.value = []; dataSource.value = [];
downloadTaskIds.value = [];
}; };
const init = () => { const init = () => {
@ -82,7 +85,7 @@ export default {
reload(); reload();
}; };
const handleCloseTip = () => { const clearSelectedRows = () => {
selectedRows.value = []; selectedRows.value = [];
selectedRowKeys.value = []; selectedRowKeys.value = [];
}; };
@ -98,8 +101,66 @@ export default {
record.file && downloadByUrl(record.file); 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) => { const handleDelete = (record) => {
@ -121,8 +182,14 @@ export default {
selectedRowKeys.value = []; selectedRowKeys.value = [];
getData(); getData();
}; };
const unloadComp = () => {
clearQueryTaskTimer();
};
onUnmounted(() => {
clearQueryTaskTimer;
});
expose({ init, reset }); expose({ init, reset, unloadComp });
return () => ( return () => (
<div class="export-task-wrap"> <div class="export-task-wrap">
@ -187,7 +254,7 @@ export default {
</span> </span>
</div> </div>
</div> </div>
<IconClose size={16} class="cursor-pointer color-#737478" onClick={handleCloseTip} /> <IconClose size={16} class="cursor-pointer color-#737478" onClick={clearSelectedRows} />
</div> </div>
)} )}
@ -252,7 +319,7 @@ export default {
))} ))}
<TableColumn <TableColumn
data-index="operation" data-index="operation"
width={100} width={110}
fixed="right" fixed="right"
title="操作" title="操作"
v-slots={{ v-slots={{

View File

@ -52,6 +52,7 @@ const onClose = () => {
activeTab.value = '0'; activeTab.value = '0';
clearTimer(); clearTimer();
componentRef.value?.unloadComp?.()
visible.value = false; visible.value = false;
}; };
const clearTimer = () => { const clearTimer = () => {

View File

@ -13,7 +13,7 @@ interface RenderNotificationData {
file?: string; file?: string;
} }
export function showExportNotification(label: string, id: '') { export function showExportNotification(label: string, { id = '', duration = 3000}) {
Notification.warning({ Notification.warning({
id, id,
showIcon: false, showIcon: false,
@ -24,7 +24,7 @@ export function showExportNotification(label: string, id: '') {
<p class="text-14px lh-22px font-400 color-#211F24">{label}</p> <p class="text-14px lh-22px font-400 color-#211F24">{label}</p>
</div> </div>
), ),
duration: 3000, duration,
class: 'px-16px py-9px w-400px rounded-2px bg-#F0EDFF', class: 'px-16px py-9px w-400px rounded-2px bg-#F0EDFF',
}); });
} }

View File

@ -107,3 +107,6 @@ export function downloadByUrl(url: string, filename?: string) {
document.body.removeChild(a); document.body.removeChild(a);
} }
export function genRandomId() {
return `id_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
}

View File

@ -28,6 +28,7 @@ import SyncDataModal from '../sync-data-modal';
// import { downloadByUrl } from '@/utils/tools'; // import { downloadByUrl } from '@/utils/tools';
import { showExportNotification } from '@/utils/arcoD'; import { showExportNotification } from '@/utils/arcoD';
import { genRandomId } from '@/utils/tools';
import { import {
fetchAccountTags, fetchAccountTags,
fetchAccountGroups, fetchAccountGroups,
@ -188,9 +189,9 @@ export default {
}, },
}); });
if (code === 200) { if (code === 200) {
const ID = 'IMPORT-ACCOUNT'; const id = genRandomId();
showExportNotification(`正在导入“${file.value.name}”,请稍后...`, ID); showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id });
emit('startQueryTaskStatus', data.id, ID); emit('startQueryTaskStatus', data.id, id);
onClose(); onClose();
} else { } else {
uploadStatus.value = UploadStatus.ERROR; uploadStatus.value = UploadStatus.ERROR;

View File

@ -151,6 +151,7 @@ import StatusBox from '../status-box';
import { PLATFORM_LIST, ENUM_PLATFORM } from '@/views/property-marketing/put-account/common_constants'; import { PLATFORM_LIST, ENUM_PLATFORM } from '@/views/property-marketing/put-account/common_constants';
import { showExportNotification } from '@/utils/arcoD'; import { showExportNotification } from '@/utils/arcoD';
import { genRandomId } from '@/utils/tools';
import { import {
postPlacementAccounts, postPlacementAccounts,
getPlacementAccountsDetail, getPlacementAccountsDetail,
@ -289,9 +290,9 @@ const handleBatchImport = async () => {
}); });
if (code === 200) { if (code === 200) {
const ID = 'IMPORT-PUT-ACCOUNT'; const id = genRandomId();
showExportNotification(`正在导入“${file.value.name}”,请稍后...`, ID); showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id });
emits('startQueryTaskStatus', data.id, ID); emit('startQueryTaskStatus', data.id, id);
onClose(); onClose();
} else { } else {
uploadStatus.value = UploadStatus.ERROR; uploadStatus.value = UploadStatus.ERROR;