Merge remote-tracking branch 'origin/main' into feature/v1.2灵机空间-项目管理_rxd

# Conflicts:
#	src/api/all/common.ts
#	src/components/_base/navbar/index.vue
#	src/hooks/useTableSelectionWithPagination.ts
#	src/router/routes/modules/propertyMarketing.ts
#	src/stores/modules/side-bar/constants.ts
#	src/views/property-marketing/media-account/account-manage/components/account-table/index.vue
#	src/views/property-marketing/media-account/account-manage/components/add-account-modal/index.vue
#	src/views/property-marketing/put-account/account-data/components/filter-block/index.vue
#	src/views/property-marketing/put-account/account-manage/components/account-table/index.vue
#	src/views/property-marketing/put-account/account-manage/components/add-account-modal/index.vue
#	src/views/property-marketing/put-account/account-manage/components/filter-block/index.vue
#	src/views/property-marketing/put-account/components/status-select/constants.ts
This commit is contained in:
rd
2025-07-23 15:22:46 +08:00
66 changed files with 2144 additions and 673 deletions

View File

@ -65,7 +65,8 @@ import AccountTable from './components/account-table';
import { getAccountBoardOverview, getAccountBoardList, postAccountBoardExport } from '@/api/all/propertyMarketing';
import { formatNumberShow } from '@/utils/tools';
import { INITIAL_QUERY, CARD_FIELDS } from './constants';
import { downloadByUrl } from '@/utils/tools';
// import { downloadByUrl } from '@/utils/tools';
import { showExportNotification } from '@/utils/arcoD';
import icon1 from '@/assets/img/icon-question.png';
@ -139,7 +140,7 @@ const handleExport = () => {
}).then((res) => {
const { code, data } = res;
if (code === 200) {
downloadByUrl(data.download_url);
showExportNotification(`正在下载“${data.name}”,请稍后...`);
}
});
};

View File

@ -119,7 +119,7 @@
</a-dropdown>
</div>
</div>
<div v-if="item.status === 2" class="mask">
<div v-if="isSyncFailed(item)" class="mask">
<div class="flex items-center mb-16px box">
<img :src="icon3" width="16" height="16" class="mr-8px" />
<span class="name !mb-0">更新数据失败</span>
@ -172,7 +172,7 @@ const props = defineProps({
},
});
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete']);
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete', 'updateSyncStatus']);
const router = useRouter();
@ -198,9 +198,6 @@ const isSyncing = (item) => {
if (!props.syncMediaAccounts.length) return false;
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
if (target) {
return target?.status === 0;
}
return target?.status === 0;
};
@ -292,7 +289,7 @@ const goDetail = (item) => {
const onDeleteSyncStatus = async (item) => {
await deleteSyncStatus(item.id);
item.status = 1;
emits('updateSyncStatus', item);
};
const formatTime = (time) => {
return exactFormatTime(time, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
@ -306,6 +303,10 @@ const getLastSyncedAt = (item) => {
}
return formatTime(item.last_synced_at);
};
const isSyncFailed = (item) => {
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
return target?.status === 2;
};
</script>
<style scoped lang="scss">

View File

@ -20,17 +20,14 @@ import {
Textarea,
} from '@arco-design/web-vue';
import AuthorizedAccountModal from '../authorized-account-modal';
import ImportPromptModal from '../import-prompt-modal';
// import ImportPromptModal from '../import-prompt-modal';
import StatusBox from '../status-box';
import SyncDataModal from '../sync-data-modal';
import CommonSelect from '@/components/common-select';
import icon1 from '@/assets/img/media-account/icon-download.png';
import icon2 from '@/assets/img/media-account/icon-delete.png';
import icon3 from '@/assets/img/media-account/icon-dy.png';
import icon4 from '@/assets/img/media-account/icon-xhs.png';
import icon5 from '@/assets/img/media-account/icon-warn-1.png';
import icon6 from '@/assets/img/media-account/icon-success.png';
// import { downloadByUrl } from '@/utils/tools';
import { showExportNotification } from '@/utils/arcoD';
import { genRandomId } from '@/utils/tools';
import {
fetchAccountTags,
fetchAccountGroups,
@ -42,6 +39,13 @@ import {
getProjectList,
} from '@/api/all/propertyMarketing';
import icon1 from '@/assets/img/media-account/icon-download.png';
import icon2 from '@/assets/img/media-account/icon-delete.png';
import icon3 from '@/assets/img/media-account/icon-dy.png';
import icon4 from '@/assets/img/media-account/icon-xhs.png';
// import icon5 from '@/assets/img/media-account/icon-warn-1.png';
// import icon6 from '@/assets/img/media-account/icon-success.png';
const UploadStatus = {
DEFAULT: 'default',
WAITING: 'waiting',
@ -73,13 +77,12 @@ export default {
const formRef = ref();
const file = ref(null);
const authorizedAccountModalRef = ref(null);
const importPromptModalRef = ref(null);
// const importPromptModalRef = ref(null);
const uploadRef = ref(null);
const isCustomCookie = ref(false);
const form = ref(cloneDeep(INITIAL_FORM));
const syncDataModalRef = ref(null);
const importLoading = ref(false);
const CustomNotificationVisible = ref(false);
const rules = {
mobile: [
@ -140,6 +143,7 @@ export default {
function removeFile() {
fileName.value = '';
file.value = null;
importLoading.value = false;
uploadStatus.value = UploadStatus.DEFAULT;
}
const reset = () => {
@ -187,27 +191,27 @@ export default {
AMessage.warning('请上传要导入的文件');
return;
}
importLoading.value = true;
const formData = new FormData();
formData.append('file', file.value);
const { code } = await batchMediaAccounts(formData, {
const { code, data } = await batchMediaAccounts(formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
if (code === 200) {
AMessage.success('导入成功');
emit('update');
const id = genRandomId();
showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id });
emit('startQueryTaskStatus', data.id, id);
onClose();
// const ID = 'IMPORT-ACCOUNT';
// renderNotification(ID);
importPromptModalRef.value.open();
} else {
uploadStatus.value = UploadStatus.ERROR;
}
} catch (error) {
uploadStatus.value = UploadStatus.ERROR;
} finally {
importLoading.value = false;
}
};
const handleAddAccount = async () => {
@ -259,9 +263,6 @@ export default {
window.open(data.download_url, '_blank');
}
};
const handleDownloadError = () => {
console.log('handleDownloadError');
};
const renderLabel = (label, tooltipContent) => {
return (
<>
@ -272,31 +273,6 @@ export default {
</>
);
};
const renderNotification = (id, hasError) => {
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">
共导入 37 个账号导入成功 32 {hasError ? `,失败 <span class="color-#F64B31">5</span> 个` : ''}
</p>
{hasError && (
<div class="mt-8px text-14px lh-22px font-400 color-#6D4CFE cursor-pointer" onClick={handleDownloadError}>
下载问题表格
</div>
)}
</div>
),
duration: 3000,
class: `px-16px py-16px w-400px rounded-2px ${hasError ? 'bg-#FFF7E5' : 'bg-#EBF7F2'}`,
});
};
expose({ open });
@ -437,28 +413,24 @@ export default {
auto-size={{ minRows: 3, maxRows: 5 }}
/>
</FormItem>
{!isEdit.value && (
<>
<FormItem
label="Cookie值"
v-slots={{
label: () => renderLabel('Cookie值', 'Cookie无需扫码授权'),
}}
>
<Switch v-model={isCustomCookie.value} size="large" />
</FormItem>
{isCustomCookie.value && (
<FormItem label="" field="cookie">
<Textarea
v-model={form.value.cookie}
placeholder="请输入..."
size="large"
max-length={72}
auto-size={{ minRows: 3, maxRows: 5 }}
/>
</FormItem>
)}
</>
<FormItem
label="Cookie值"
v-slots={{
label: () => renderLabel('Cookie值', 'Cookie无需扫码授权'),
}}
>
<Switch v-model={isCustomCookie.value} size="large" />
</FormItem>
{isCustomCookie.value && (
<FormItem label="" field="cookie">
<Textarea
v-model={form.value.cookie}
placeholder="请输入..."
size="large"
max-length={500}
auto-size={{ minRows: 5, maxRows: 8 }}
/>
</FormItem>
)}
</>
)}
@ -473,7 +445,7 @@ export default {
</div>
<AuthorizedAccountModal ref={authorizedAccountModalRef} onUpdate={() => emit('update')} />
<ImportPromptModal ref={importPromptModalRef} />
{/* <ImportPromptModal ref={importPromptModalRef} /> */}
<SyncDataModal ref={syncDataModalRef} />
</Modal>
);

View File

@ -70,7 +70,7 @@
</template>
<div v-else>
<a-space v-if="isAbNormalStatus" class="flex items-center">
<a-button class="w-96px err-btn" size="mini" @click="handleOpenAbnormalAccount">
<a-button type="primary" status="danger" size="mini" @click="handleOpenAbnormalAccount">
<template #default>查看异常账号</template>
</a-button>
</a-space>
@ -87,6 +87,7 @@
@delete="handleDelete"
@openEdit="handleOpenEdit"
@update="getData"
@updateSyncStatus="handleUpdateSyncStatus"
/>
<NoData v-else />
@ -108,7 +109,7 @@
<GroupManageModal ref="groupManageModalRef" @update="filterBlockRef?.getGroups" />
<TagsManageModal ref="tagsManageModalRef" @update="filterBlockRef?.getTags" />
<AddAccountModal ref="addAccountModalRef" @update="getData" />
<AddAccountModal ref="addAccountModalRef" @update="getData" @startQueryTaskStatus="handleGetImportTaskStatus" />
<DeleteAccountModal ref="deleteAccountRef" @update="getData" @batchUpdate="onBatchSuccess" />
<BatchTagModal ref="batchTagModalRef" @update="onBatchSuccess" />
@ -118,6 +119,7 @@
<script setup>
import { ref, provide } from 'vue';
import { Notification } from '@arco-design/web-vue';
import FilterBlock from './components/filter-block';
import AccountTable from './components/account-table';
@ -127,10 +129,11 @@ import AddAccountModal from './components/add-account-modal';
import DeleteAccountModal from './components/account-table/delete-account';
import BatchTagModal from './components/batch-tag-modal';
import BatchGroupModal from './components/batch-group-modal';
import { Notification } from '@arco-design/web-vue';
import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
import { showImportResultNotification } from '@/utils/arcoD';
import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
import { getTaskStatus } from '@/api/all/common';
import {
getMediaAccounts,
getMediaAccountsHealth,
@ -147,6 +150,7 @@ import icon5 from '@/assets/img/media-account/icon-warn.png';
import icon6 from '@/assets/img/media-account/icon-close.png';
let syncDataTimer = null;
let queryTaskTimer = null;
const groupManageModalRef = ref(null);
const tagsManageModalRef = ref(null);
@ -294,6 +298,13 @@ const getAsyncStatus = async () => {
}
}
};
const handleUpdateSyncStatus = (item) => {
const target = syncMediaAccounts.value.find((v) => v.id === item.id);
// 点击取消,设置状态为成功
if (target) {
target.status = 1;
}
};
const startSyncDataPolling = () => {
isLoadingTaskStatus.value = true;
@ -339,18 +350,43 @@ const handleOpenAbnormalAccount = () => {
query.value.status = 2;
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(id, notificationId), 3000);
};
const clearSyncDataTimer = () => {
if (syncDataTimer) {
clearInterval(syncDataTimer);
syncDataTimer = null;
}
};
const clearQueryTaskTimer = () => {
if (queryTaskTimer) {
clearInterval(queryTaskTimer);
queryTaskTimer = null;
}
};
onMounted(() => {
getData();
});
onUnmounted(() => {
clearSyncDataTimer();
clearQueryTaskTimer();
});
provide('handleSyncData', handleSyncData);

View File

@ -37,15 +37,6 @@
color: #211f24;
}
}
.err-btn {
background-color: #f64b31 !important;
color: var(--BG-white, #fff);
font-family: 'PingFang SC';
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
}
.operation-btn {
padding: 0;
cursor: pointer;

View File

@ -5,8 +5,8 @@
export enum EnumStatus {
UNAUTHORIZED = 0,
NORMAL = 1,
ABNORMAL = 2,
PAUSE = 3,
ABNORMAL = 3,
PAUSE = 2,
ABNORMAL_LOGIN = 4,
ABNORMAL_REQUEST = 5,
ABNORMAL_FREEZE = 6,

View File

@ -19,7 +19,7 @@
<script setup>
import { ref, watch } from 'vue';
import { STATUS_LIST } from './constants';
import { STATUS_LIST } from '@/views/property-marketing/media-account/components/status-select/constants';
const props = defineProps({
modelValue: {

View File

@ -135,7 +135,7 @@
<script setup>
import { ref, computed } from 'vue';
import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants';
import { STATUS_LIST } from '@/views/property-marketing/media-account/components/status-select/constants';
import { formatTableField, exactFormatTime } from '@/utils/tools';
import { TABLE_COLUMNS } from './constants';
import { useRouter } from 'vue-router';

View File

@ -80,7 +80,7 @@ import {
} from '@/api/all/propertyMarketing';
import CommonSelect from '@/components/common-select';
import StatusSelect from '@/views/property-marketing/put-account/components/status-select';
import StatusSelect from '@/views/property-marketing/media-account/components/status-select';
import AccountSelect from '@/views/property-marketing/put-account/components/account-select';
const props = defineProps({

View File

@ -133,7 +133,7 @@
<script setup>
import { ref, computed } from 'vue';
import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants';
import { STATUS_LIST } from '@/views/property-marketing/media-account/components/status-select/constants';
import { formatTableField } from '@/utils/tools';
import { TABLE_COLUMNS } from './constants';
import { useRouter } from 'vue-router';

View File

@ -68,8 +68,9 @@ import {
postPlacementAccountDataListExport,
} from '@/api/all/propertyMarketing';
import { showExportNotification } from '@/utils/arcoD';
import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
import { downloadByUrl } from '@/utils/tools';
// import { downloadByUrl } from '@/utils/tools';
import icon2 from '@/assets/img/media-account/icon-group.png';
@ -151,7 +152,7 @@ const handleExport = () => {
}).then((res) => {
const { code, data } = res;
if (code === 200) {
downloadByUrl(data.download_url);
showExportNotification(`正在下载“${data.name}”,请稍后...`);
}
});
};

View File

@ -102,7 +102,7 @@
<script setup>
import { defineProps, ref, computed } from 'vue';
import { PLATFORM_LIST } from '@/utils/platform';
import { EnumStatus } from '@/views/property-marketing/put-account/components/status-select/constants';
import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
import { formatNumberShow, exactFormatTime } from '@/utils/tools';

View File

@ -129,13 +129,13 @@
<icon-question-circle size="14" class="ml-4px color-#737478" />
</a-tooltip>
</template>
<a-switch v-model="form.is_sync_project" size="medium" :checked-value="1" :un-checked-value="0" />
<a-switch v-model="form.is_sync_project" size="medium" :checked-value="1" :unchecked-value="0" />
</a-form-item>
</template>
</a-form>
<template #footer>
<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 }}
</a-button>
</template>
@ -155,6 +155,8 @@ import CommonSelect from '@/components/common-select';
import { PLATFORM_LIST, ENUM_PUT_ACCOUNT_PLATFORM } from '@/utils/platform';
import { showExportNotification } from '@/utils/arcoD';
import { genRandomId } from '@/utils/tools';
import {
postPlacementAccounts,
getPlacementAccountsDetail,
@ -168,6 +170,7 @@ import icon1 from '@/assets/img/media-account/icon-download.png';
import icon2 from '@/assets/img/media-account/icon-delete.png';
const update = inject('update');
const emits = defineEmits(['startQueryTaskStatus']);
const UploadStatus = {
DEFAULT: 'default',
@ -196,6 +199,7 @@ const uploadRef = ref(null);
const file = ref(null);
const form = ref(cloneDeep(INITIAL_FORM));
const projects = ref([]);
const importLoading = ref(false);
const rules = {
mobile: [
@ -221,7 +225,7 @@ const rules = {
const isBatchImport = computed(() => uploadType.value === 'batch');
const confirmBtnText = computed(() => {
if (isBatchImport.value) return '确定导入';
if (isBatchImport.value) return importLoading.value ? '导入中' : '确定导入';
return isEdit.value ? '确定' : '下一步';
});
@ -237,6 +241,7 @@ const handleUpload = async (option) => {
function removeFile() {
fileName.value = '';
file.value = null;
importLoading.value = false;
uploadStatus.value = UploadStatus.DEFAULT;
}
@ -249,6 +254,7 @@ const reset = () => {
file.value = null;
isEdit.value = false;
projects.value = [];
importLoading.value = false;
uploadStatus.value = UploadStatus.DEFAULT;
uploadType.value = 'manual';
};
@ -284,23 +290,33 @@ const getAccountDetail = async () => {
const handleBatchImport = async () => {
try {
if (!file.value) {
AMessage.warning('请上传要导入的文件');
return;
}
importLoading.value = true;
const formData = new FormData();
formData.append('file', file.value);
const { code } = await batchPlacementAccounts(formData, {
const { code, data } = await batchPlacementAccounts(formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
if (code === 200) {
AMessage.success('导入成功');
update();
const id = genRandomId();
showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id });
emit('startQueryTaskStatus', data.id, id);
onClose();
} else {
uploadStatus.value = UploadStatus.ERROR;
}
} catch (error) {
uploadStatus.value = UploadStatus.ERROR;
} finally {
importLoading.value = false;
}
// importPromptModalRef.value.open();

View File

@ -12,7 +12,7 @@
:footer="!isLoading"
@close="close"
>
<div v-if="showSyncTip">
<!-- <div v-if="showSyncTip">
<div class="flex items-center mb-20px">
<img :src="icon1" width="16" height="16" class="mr-16px" />
<p class="s2">
@ -25,8 +25,8 @@
<a-radio value="sync" class="mb-16px">立即同步遗漏数据 - 获取完整的最新数据 推荐</a-radio>
<a-radio value="no_sync">仅授权不更新 - 继续使用当前不完全的数据</a-radio>
</a-radio-group>
</div>
<div v-else class="flex flex-col items-center">
</div> -->
<div class="flex flex-col items-center">
<template v-if="isLoading">
<a-progress
:percent="progress"
@ -80,7 +80,7 @@ import icon2 from '@/assets/img/media-account/icon-feedback-success.png';
import icon3 from '@/assets/img/media-account/icon-feedback-fail.png';
const update = inject('update');
const INITIAL_SYNC_TYPE = 'sync';
// const INITIAL_SYNC_TYPE = 'sync';
const visible = ref(false);
const isLoading = ref(false);
const isCompleted = ref(false);
@ -91,10 +91,10 @@ const progress = ref(0);
const id = ref('');
const selectSubAccountModalRef = ref(null);
const lastSyncedAt = ref(null); // 上次同步时间戳
const showSyncTip = ref(false);
// const lastSyncedAt = ref(null); // 上次同步时间戳
// const showSyncTip = ref(false);
const shouldSelectSubAccount = ref(false);
const syncType = ref(INITIAL_SYNC_TYPE); // sync no_sync
// const syncType = ref(INITIAL_SYNC_TYPE); // sync no_sync
const addAccountFormData = ref(null); // 添加账户表单数据
const INITIAL_FORM = {
@ -123,20 +123,20 @@ const confirmBtnText = computed(() => {
return isSuccess.value ? '继续添加' : '重试';
});
const getDaysDiffText = (lastSyncedAt) => {
if (!lastSyncedAt) return '0天';
// const getDaysDiffText = (lastSyncedAt) => {
// if (!lastSyncedAt) return '0天';
const daysDiff = dayjs().diff(dayjs(lastSyncedAt * 1000), 'day');
// const daysDiff = dayjs().diff(dayjs(lastSyncedAt * 1000), 'day');
if (daysDiff === 0) return '不到1天';
return `${daysDiff}`;
};
// if (daysDiff === 0) return '不到1天';
// return `${daysDiff}天`;
// };
const open = ({ accountId, last_synced_at = null, form = null, needSelectSubAccount = false }) => {
reset();
id.value = accountId;
lastSyncedAt.value = last_synced_at;
// lastSyncedAt.value = last_synced_at;
addAccountFormData.value = form;
shouldSelectSubAccount.value = needSelectSubAccount;
visible.value = true;
@ -153,9 +153,9 @@ const reset = () => {
failReason.value = '';
progress.value = 0;
id.value = '';
lastSyncedAt.value = null;
syncType.value = INITIAL_SYNC_TYPE;
showSyncTip.value = false;
// lastSyncedAt.value = null;
// syncType.value = INITIAL_SYNC_TYPE;
// showSyncTip.value = false;
shouldSelectSubAccount.value = false;
addAccountFormData.value = null;
clearFakeProgressTimer();
@ -190,6 +190,8 @@ const startStatusPolling = () => {
clearFakeProgressTimer();
clearStatusPollingTimer();
isLoading.value = false;
isSuccess.value && postPlacementAccountsSync(id.value);
}
}
}, 2000);
@ -236,34 +238,34 @@ const clearFakeProgressTimer = () => {
}
};
const handleSyncData = () => {
if (!showSyncTip.value) {
formRef.value.validate(async (errors) => {
if (!errors) {
showSyncTip.value = true;
}
});
return;
}
// const handleSyncData = () => {
// if (!showSyncTip.value) {
// formRef.value.validate(async (errors) => {
// if (!errors) {
// showSyncTip.value = true;
// }
// });
// return;
// }
if (syncType.value === INITIAL_SYNC_TYPE) {
postPlacementAccountsSync(id.value).then((res) => {
if (res.code === 200) {
update();
close();
}
});
} else {
close();
}
};
// if (syncType.value === INITIAL_SYNC_TYPE) {
// postPlacementAccountsSync(id.value).then((res) => {
// if (res.code === 200) {
// update();
// close();
// }
// });
// } else {
// close();
// }
// };
const handleOk = () => {
// n天未同步更新
if (lastSyncedAt.value && lastSyncedAt.value < dayjs().startOf('day').valueOf()) {
handleSyncData();
return;
}
// // n天未同步更新
// if (lastSyncedAt.value && lastSyncedAt.value < dayjs().startOf('day').valueOf()) {
// handleSyncData();
// return;
// }
// 已完成
if (isCompleted.value) {

View File

@ -73,7 +73,7 @@
import { defineEmits, defineProps } from 'vue';
import { getPlacementAccountOperators, getProjectList } from '@/api/all/propertyMarketing';
import { PLATFORM_LIST } from '@/utils/platform';
import StatusSelect from '@/views/property-marketing/put-account/components/status-select';
import StatusSelect from '@/views/property-marketing/media-account/components/status-select';
import CommonSelect from '@/components/common-select';
const props = defineProps({

View File

@ -100,7 +100,7 @@
</div>
<template #footer>
<span class="cts color-#3C4043 s1"
>已选<span class="color-#6D4CFE num mx-3px">{{ selectedAccounts.length }}</span>账户</span
>已选<span class="color-#6D4CFE num mx-3px">{{ selectedRows.length }}</span>账户</span
>
<div class="flex items-center">
<a-button class="cancel-btn" size="large" @click="onClose">取消</a-button>
@ -109,7 +109,7 @@
class="ml-16px"
status="danger"
size="large"
:disabled="!selectedAccounts.length"
:disabled="!selectedRows.length"
@click="onConfirm"
>添加已选账户</a-button
>
@ -136,7 +136,7 @@ const form = ref(null);
const ROW_KEY = 'account_id';
const {
selectedRowKeys,
selectedRows: selectedAccounts,
selectedRows,
dataSource,
pageInfo,
onPageChange,
@ -170,7 +170,8 @@ const open = (formData) => {
const onClose = () => {
form.value = null;
selectedAccounts.value = [];
selectedRows.value = [];
dataSource.value = [];
selectedRowKeys.value = [];
query.value = cloneDeep(INITIAL_FORM);
pageInfo.value = cloneDeep(INITIAL_PAGE_INFO);
@ -178,7 +179,7 @@ const onClose = () => {
};
const onConfirm = async () => {
const { code } = await postAddSubAccount({ ...form.value, subaccounts: selectedAccounts.value });
const { code } = await postAddSubAccount({ ...form.value, subaccounts: selectedRows.value });
if (code === 200) {
visible.value = false;
update();

View File

@ -13,7 +13,7 @@
<script setup>
import { computed } from 'vue';
import { STATUS_LIST, EnumStatus } from '@/views/property-marketing/put-account/components/status-select/constants';
import { STATUS_LIST, EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
import iconWarn1 from '@/assets/img/media-account/icon-warn-1.png';
import iconWarn2 from '@/assets/img/media-account/icon-warn-2.png';

View File

@ -53,7 +53,7 @@
</template>
<div v-else>
<a-space v-if="isAbNormalStatus" class="flex items-center">
<a-button class="w-96px err-btn" size="mini" @click="handleOpenAbnormalAccount">
<a-button type="primary" status="danger" size="mini" @click="handleOpenAbnormalAccount">
<template #default>查看异常账号</template>
</a-button>
</a-space>
@ -86,7 +86,7 @@
</div>
</div>
<AddAccountModal ref="addAccountModalRef" />
<AddPutAccountModal ref="addAccountModalRef" @startQueryTaskStatus="handleGetImportTaskStatus" />
<DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" />
</div>
</template>
@ -96,11 +96,13 @@ import { ref } from 'vue';
import FilterBlock from './components/filter-block';
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 { INITIAL_QUERY } from './constants';
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 icon4 from '@/assets/img/media-account/icon-success.png';
@ -111,6 +113,7 @@ const groupManageModalRef = ref(null);
const tagsManageModalRef = ref(null);
const addAccountModalRef = ref(null);
const deleteAccountRef = ref(null);
let queryTaskTimer = null;
const pageInfo = ref({
page: 1,
@ -158,10 +161,6 @@ const tipLabel = computed(() => {
return `共有 ${total_abnormal_number} 个账号存在授权异常,其中:${abnormalLabels.join('')}`;
});
onMounted(() => {
getData();
});
const getData = () => {
getHealthData();
getAccountData();
@ -257,6 +256,37 @@ const handleOpenAbnormalAccount = () => {
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(id, notificationId), 3000);
};
const clearQueryTaskTimer = () => {
if (queryTaskTimer) {
clearInterval(queryTaskTimer);
queryTaskTimer = null;
}
};
onMounted(() => {
getData();
});
onUnmounted(() => {
clearQueryTaskTimer();
});
provide('update', getData);
</script>

View File

@ -31,15 +31,6 @@
color: #211f24;
}
}
.err-btn {
background-color: #f64b31 !important;
color: var(--BG-white, #fff);
font-family: 'PingFang SC';
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
}
.operation-btn {
padding: 0;
cursor: pointer;

View File

@ -1,60 +0,0 @@
/*
* @Author: RenXiaoDong
* @Date: 2025-07-04 11:18:11
*/
export enum EnumStatus {
UNAUTHORIZED = 0,
NORMAL = 1,
ABNORMAL = 3,
PAUSE = 2,
ABNORMAL_LOGIN = 4,
ABNORMAL_REQUEST = 5,
ABNORMAL_FREEZE = 6,
ABNORMAL_MISSING = 7,
}
export const STATUS_LIST = [
{
text: '正常',
label: '正常',
value: EnumStatus.NORMAL,
},
{
text: '暂停同步',
label: '暂停同步',
value: EnumStatus.PAUSE,
},
{
text: '未授权',
label: '未授权',
value: EnumStatus.UNAUTHORIZED,
},
{
text: '异常',
label: '异常',
value: EnumStatus.ABNORMAL,
},
{
text: '数据缺失',
label: '数据缺失',
value: EnumStatus.ABNORMAL_MISSING,
},
{
text: '异常-登录状态失效',
label: '异常',
value: EnumStatus.ABNORMAL_LOGIN,
tooltip: '登录状态失效,需重新扫码授权',
},
{
text: '异常-请求过于频繁',
label: '异常',
value: EnumStatus.ABNORMAL_REQUEST,
tooltip: '请求过于频繁需等待24小时后重试',
},
{
text: '异常-账号被冻结/封禁',
label: '异常',
value: EnumStatus.ABNORMAL_FREEZE,
tooltip: '账号被冻结/封禁',
},
];

View File

@ -1,60 +0,0 @@
<!--
* @Author: RenXiaoDong
* @Date: 2025-06-25 14:02:40
-->
<template>
<a-select
v-model="selectedStatus"
:multiple="multiple"
size="medium"
:placeholder="placeholder"
allow-clear
@change="handleChange"
>
<a-option v-for="(item, index) in STATUS_LIST" :key="index" :value="item.value" :label="item.text">
{{ item.text }}
</a-option>
</a-select>
</template>
<script setup>
import { ref, watch } from 'vue';
import { STATUS_LIST } from './constants';
const props = defineProps({
modelValue: {
type: [Array, String, Number],
default: () => [],
},
multiple: {
type: Boolean,
default: false,
},
placeholder: {
type: String,
default: '全部',
},
});
const emits = defineEmits(['update:modelValue', 'change']);
const selectedStatus = ref(props.multiple ? [] : '');
// 监听外部传入的值变化
watch(
() => props.modelValue,
(newVal) => {
selectedStatus.value = newVal;
},
{ immediate: true },
);
// 监听内部值变化,向外部发送更新
watch(selectedStatus, (newVal) => {
emits('update:modelValue', newVal);
});
const handleChange = (value) => {
selectedStatus.value = value;
emits('change', value);
};
</script>