feat: 导入投放账户修改为异步逻辑

This commit is contained in:
rd
2025-07-21 10:20:07 +08:00
parent c02f3b0479
commit 85fce448ea
6 changed files with 113 additions and 59 deletions

View File

@ -1,6 +1,18 @@
import { Notification } from '@arco-design/web-vue'; import { Notification } from '@arco-design/web-vue';
import { downloadByUrl } from '@/utils/tools';
import { IconLoading } from '@arco-design/web-vue/es/icon'; import { IconLoading } from '@arco-design/web-vue/es/icon';
import icon1 from '@/assets/img/media-account/icon-warn.png';
import icon2 from '@/assets/img/media-account/icon-close.png';
interface RenderNotificationData {
total_number: number;
success_number: number;
fail_number: number;
file?: string;
}
export function showExportNotification(label: string, id: '') { export function showExportNotification(label: string, id: '') {
Notification.warning({ Notification.warning({
id, id,
@ -15,4 +27,43 @@ export function showExportNotification(label: string, id: '') {
duration: 3000, duration: 3000,
class: 'px-16px py-9px w-400px rounded-2px bg-#F0EDFF', class: 'px-16px py-9px w-400px rounded-2px bg-#F0EDFF',
}); });
} }
export const showImportResultNotification = (data: RenderNotificationData) => {
const { total_number, success_number, fail_number, file } = data;
const hasError = fail_number > 0;
const handleDownloadError = (file?: string) => {
file && downloadByUrl(file);
};
Notification.warning({
showIcon: false,
closable: true,
content: () => (
<div>
<div class="flex items-center mb-4px">
<img src={hasError ? icon1 : icon2} width="16" height="16" class="mr-8px" />
<span class="text-16px lh-24px font-400 color-#211F24"></span>
</div>
<p class="text-14px lh-22px font-400 color-#211F24">
{total_number} {success_number}
{hasError && (
<span>
<span class="color-#F64B31">{fail_number}</span>
</span>
)}
</p>
{hasError && (
<div
class="mt-8px text-14px lh-22px font-400 color-#6D4CFE cursor-pointer"
onClick={() => handleDownloadError(file)}
>
</div>
)}
</div>
),
duration: 3000,
class: `px-16px py-16px w-400px rounded-2px ${hasError ? 'bg-#FFF7E5' : 'bg-#EBF7F2'}`,
});
};

View File

@ -42,8 +42,8 @@ import icon1 from '@/assets/img/media-account/icon-download.png';
import icon2 from '@/assets/img/media-account/icon-delete.png'; import icon2 from '@/assets/img/media-account/icon-delete.png';
import icon3 from '@/assets/img/media-account/icon-dy.png'; import icon3 from '@/assets/img/media-account/icon-dy.png';
import icon4 from '@/assets/img/media-account/icon-xhs.png'; import icon4 from '@/assets/img/media-account/icon-xhs.png';
import icon5 from '@/assets/img/media-account/icon-warn-1.png'; // import icon5 from '@/assets/img/media-account/icon-warn-1.png';
import icon6 from '@/assets/img/media-account/icon-success.png'; // import icon6 from '@/assets/img/media-account/icon-success.png';
const UploadStatus = { const UploadStatus = {
DEFAULT: 'default', DEFAULT: 'default',
@ -74,10 +74,10 @@ export default {
const formRef = ref(); const formRef = ref();
const file = ref(null); const file = ref(null);
const authorizedAccountModalRef = ref(null); const authorizedAccountModalRef = ref(null);
const importPromptModalRef = ref(null); // const importPromptModalRef = ref(null);
const uploadRef = ref(null); const uploadRef = ref(null);
const isCustomCookie = ref(false); const isCustomCookie = ref(false);
const form = ref({ ...INITIAL_FORM }); const form = ref(cloneDeep(INITIAL_FORM));
const syncDataModalRef = ref(null); const syncDataModalRef = ref(null);
const importLoading = ref(false); const importLoading = ref(false);
@ -140,7 +140,7 @@ export default {
const reset = () => { const reset = () => {
formRef.value?.resetFields(); formRef.value?.resetFields();
formRef.value?.clearValidate(); formRef.value?.clearValidate();
form.value = { ...INITIAL_FORM }; form.value = cloneDeep(INITIAL_FORM);
fileName.value = ''; fileName.value = '';
file.value = null; file.value = null;
isEdit.value = false; isEdit.value = false;
@ -197,6 +197,8 @@ export default {
} }
} catch (error) { } catch (error) {
uploadStatus.value = UploadStatus.ERROR; uploadStatus.value = UploadStatus.ERROR;
} finally {
importLoading.value = false;
} }
}; };
const handleAddAccount = async () => { const handleAddAccount = async () => {

View File

@ -130,7 +130,7 @@ import BatchTagModal from './components/batch-tag-modal';
import BatchGroupModal from './components/batch-group-modal'; import BatchGroupModal from './components/batch-group-modal';
import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants'; import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
import {renderNotification} from "./renderComp" import { showImportResultNotification } from '@/utils/arcoD';
import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants'; import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
import { getTaskStatus } from '@/api/all/common'; import { getTaskStatus } from '@/api/all/common';
import { import {
@ -350,7 +350,7 @@ const getSyncTaskStatus = async (id, notificationId) => {
if (data?.status !== 0) { if (data?.status !== 0) {
clearQueryTaskTimer(); clearQueryTaskTimer();
notificationId && Notification.remove(notificationId); notificationId && Notification.remove(notificationId);
renderNotification(data) showImportResultNotification(data)
getData(); getData();
} }
} }

View File

@ -1,40 +0,0 @@
import { downloadByUrl } from '@/utils/tools';
const handleDownloadError = (file) => {
file && downloadByUrl(file);
};
export const renderNotification = (data) => {
const { total_number, success_number, fail_number, file } = data;
const hasError = fail_number > 0;
Notification.warning({
id,
showIcon: false,
closable: true,
content: (
<div>
<div class="flex items-center mb-4px">
<img src={hasError ? icon5 : icon6} width="16" height="16" class="mr-8px" />
<span class="text-16px lh-24px font-400 color-#211F24">导入完成</span>
</div>
<p class="text-14px lh-22px font-400 color-#211F24">
共导入 {total_number} 个账号导入成功 {success_number}
{hasError && (
<span>
失败 <span class="color-#F64B31">{fail_number}</span>
</span>
)}
</p>
{hasError && (
<div
class="mt-8px text-14px lh-22px font-400 color-#6D4CFE cursor-pointer"
onClick={() => handleDownloadError(file)}
>
下载问题表格
</div>
)}
</div>
),
duration: 3000,
class: `px-16px py-16px w-400px rounded-2px ${hasError ? 'bg-#FFF7E5' : 'bg-#EBF7F2'}`,
});
};

View File

@ -132,7 +132,7 @@
</a-form> </a-form>
<template #footer> <template #footer>
<a-button size="large" class="cancel-btn" @click="onClose">取消</a-button> <a-button size="large" class="cancel-btn" @click="onClose">取消</a-button>
<a-button type="primary" size="large" @click="onSubmit"> <a-button type="primary" size="large" @click="onSubmit" :loading="importLoading">
{{ confirmBtnText }} {{ confirmBtnText }}
</a-button> </a-button>
</template> </template>
@ -150,6 +150,7 @@ import AuthorizedAccountModal from '../authorized-account-modal';
import StatusBox from '../status-box'; 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 { import {
postPlacementAccounts, postPlacementAccounts,
getPlacementAccountsDetail, getPlacementAccountsDetail,
@ -162,6 +163,7 @@ import icon1 from '@/assets/img/media-account/icon-download.png';
import icon2 from '@/assets/img/media-account/icon-delete.png'; import icon2 from '@/assets/img/media-account/icon-delete.png';
const update = inject('update'); const update = inject('update');
const emits = defineEmits(['startQueryTaskStatus']);
const UploadStatus = { const UploadStatus = {
DEFAULT: 'default', DEFAULT: 'default',
@ -188,6 +190,7 @@ const authorizedAccountModalRef = ref(null);
const uploadRef = ref(null); const uploadRef = ref(null);
const file = ref(null); const file = ref(null);
const form = ref(cloneDeep(INITIAL_FORM)); const form = ref(cloneDeep(INITIAL_FORM));
const importLoading = ref(false);
const rules = { const rules = {
mobile: [ mobile: [
@ -213,7 +216,7 @@ const rules = {
const isBatchImport = computed(() => uploadType.value === 'batch'); const isBatchImport = computed(() => uploadType.value === 'batch');
const confirmBtnText = computed(() => { const confirmBtnText = computed(() => {
if (isBatchImport.value) return '确定导入'; if (isBatchImport.value) return importLoading.value ? '导入中' : '确定导入';
return isEdit.value ? '确定' : '下一步'; return isEdit.value ? '确定' : '下一步';
}); });
@ -229,6 +232,7 @@ const handleUpload = async (option) => {
function removeFile() { function removeFile() {
fileName.value = ''; fileName.value = '';
file.value = null; file.value = null;
importLoading.value = false;
uploadStatus.value = UploadStatus.DEFAULT; uploadStatus.value = UploadStatus.DEFAULT;
} }
@ -240,6 +244,7 @@ const reset = () => {
fileName.value = ''; fileName.value = '';
file.value = null; file.value = null;
isEdit.value = false; isEdit.value = false;
importLoading.value = false;
uploadStatus.value = UploadStatus.DEFAULT; uploadStatus.value = UploadStatus.DEFAULT;
uploadType.value = 'manual'; uploadType.value = 'manual';
}; };
@ -273,23 +278,28 @@ const handleBatchImport = async () => {
return; return;
} }
importLoading.value = true;
const formData = new FormData(); const formData = new FormData();
formData.append('file', file.value); formData.append('file', file.value);
const { code } = await batchPlacementAccounts(formData, { const { code, data } = await batchPlacementAccounts(formData, {
headers: { headers: {
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data',
}, },
}); });
if (code === 200) { if (code === 200) {
AMessage.success('导入成功'); const ID = 'IMPORT-PUT-ACCOUNT';
update(); showExportNotification(`正在导入“${file.value.name}”,请稍后...`, ID);
emits('startQueryTaskStatus', data.id, ID);
onClose(); onClose();
} else { } else {
uploadStatus.value = UploadStatus.ERROR; uploadStatus.value = UploadStatus.ERROR;
} }
} catch (error) { } catch (error) {
uploadStatus.value = UploadStatus.ERROR; uploadStatus.value = UploadStatus.ERROR;
} finally {
importLoading.value = false;
} }
// importPromptModalRef.value.open(); // importPromptModalRef.value.open();

View File

@ -86,7 +86,7 @@
</div> </div>
</div> </div>
<AddAccountModal ref="addAccountModalRef" /> <AddPutAccountModal ref="addAccountModalRef" @startQueryTaskStatus="handleGetImportTaskStatus" />
<DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" /> <DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" />
</div> </div>
</template> </template>
@ -96,11 +96,13 @@ import { ref } from 'vue';
import FilterBlock from './components/filter-block'; import FilterBlock from './components/filter-block';
import AccountTable from './components/account-table'; import AccountTable from './components/account-table';
import AddAccountModal from './components/add-account-modal'; import AddPutAccountModal from './components/add-account-modal';
import DeleteAccountModal from './components/account-table/delete-account'; import DeleteAccountModal from './components/account-table/delete-account';
import { INITIAL_QUERY } from './constants'; import { INITIAL_QUERY } from './constants';
import { getPlacementAccounts, getPlacementAccountsHealth } from '@/api/all/propertyMarketing'; import { getPlacementAccounts, getPlacementAccountsHealth } from '@/api/all/propertyMarketing';
import { getTaskStatus } from '@/api/all/common';
import { showImportResultNotification } from '@/utils/arcoD';
import icon1 from '@/assets/img/media-account/icon-add.png'; import icon1 from '@/assets/img/media-account/icon-add.png';
import icon4 from '@/assets/img/media-account/icon-success.png'; import icon4 from '@/assets/img/media-account/icon-success.png';
@ -111,6 +113,8 @@ const groupManageModalRef = ref(null);
const tagsManageModalRef = ref(null); const tagsManageModalRef = ref(null);
const addAccountModalRef = ref(null); const addAccountModalRef = ref(null);
const deleteAccountRef = ref(null); const deleteAccountRef = ref(null);
let queryTaskTimer = null;
const pageInfo = ref({ const pageInfo = ref({
page: 1, page: 1,
@ -158,10 +162,6 @@ const tipLabel = computed(() => {
return `共有 ${total_abnormal_number} 个账号存在授权异常,其中:${abnormalLabels.join('')}`; return `共有 ${total_abnormal_number} 个账号存在授权异常,其中:${abnormalLabels.join('')}`;
}); });
onMounted(() => {
getData();
});
const getData = () => { const getData = () => {
getHealthData(); getHealthData();
getAccountData(); getAccountData();
@ -257,6 +257,37 @@ const handleOpenAbnormalAccount = () => {
reload(); reload();
}; };
// 查询导入账号任务状态
const getSyncTaskStatus = async (id, notificationId) => {
const { code, data } = await getTaskStatus(id);
if (code === 200) {
if (data?.status !== 0) {
clearQueryTaskTimer();
notificationId && Notification.remove(notificationId);
showImportResultNotification(data);
getData();
}
}
};
const handleGetImportTaskStatus = (id, notificationId) => {
clearQueryTaskTimer();
getSyncTaskStatus(id, notificationId);
queryTaskTimer = setInterval(getSyncTaskStatus, 3000);
};
const clearQueryTaskTimer = () => {
if (queryTaskTimer) {
clearInterval(queryTaskTimer);
queryTaskTimer = null;
}
};
onMounted(() => {
getData();
});
onUnmounted(() => {
clearQueryTaskTimer();
});
provide('update', getData); provide('update', getData);
</script> </script>