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:
@ -266,9 +266,9 @@ const getProfileInfo = async () => {
|
||||
let enterprises = data['enterprises'];
|
||||
mobileNumber.value = data['mobile'];
|
||||
accounts.value = enterprises;
|
||||
enterpriseStore.setEnterpriseInfo(data);
|
||||
|
||||
if (enterprises.length > 0) {
|
||||
enterpriseStore.setEnterpriseInfo(data.enterprises[0]);
|
||||
if (enterprises.length === 1) {
|
||||
handleUserLogin();
|
||||
} else {
|
||||
|
||||
@ -99,7 +99,9 @@ const addAccountVisible = ref(false);
|
||||
const deleteVisible = ref(false);
|
||||
const deleteTitle = ref('');
|
||||
|
||||
const enterpriseInfo = store.enterpriseInfo;
|
||||
const enterpriseInfo = computed(() => {
|
||||
return store.enterpriseInfo ?? {};
|
||||
});
|
||||
|
||||
const okText = computed(() => {
|
||||
if (!canAddAccount.value) {
|
||||
@ -109,9 +111,9 @@ const okText = computed(() => {
|
||||
});
|
||||
const customerServiceVisible = ref(false);
|
||||
const canAddAccount = computed(() => {
|
||||
if (!enterpriseInfo) return false;
|
||||
if (!enterpriseInfo.value) return false;
|
||||
|
||||
return enterpriseInfo.sub_account_quota > enterpriseInfo.used_sub_account_count;
|
||||
return enterpriseInfo.value.sub_account_quota > enterpriseInfo.value.used_sub_account_count;
|
||||
});
|
||||
|
||||
const currentSelectAccount = ref();
|
||||
|
||||
@ -48,7 +48,9 @@ const form = reactive({
|
||||
name: '',
|
||||
});
|
||||
|
||||
const enterpriseInfo = store.enterpriseInfo;
|
||||
const enterpriseInfo = computed(() => {
|
||||
return store.enterpriseInfo ?? {};
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@ -65,11 +67,13 @@ const infoVisible = ref(false);
|
||||
const customerServiceVisible = ref(false);
|
||||
|
||||
const dataSource = computed(() => {
|
||||
return enterpriseInfo ? [enterpriseInfo] : [];
|
||||
return enterpriseInfo.value ? [enterpriseInfo.value] : [];
|
||||
});
|
||||
|
||||
console.log({ dataSource });
|
||||
const canUpdate = computed(() => {
|
||||
if (!enterpriseInfo) return false;
|
||||
return enterpriseInfo.update_name_quota > enterpriseInfo.used_update_name_count;
|
||||
if (!enterpriseInfo.value) return false;
|
||||
return enterpriseInfo.value.update_name_quota > enterpriseInfo.value.used_update_name_count;
|
||||
});
|
||||
|
||||
const okText = computed(() => {
|
||||
@ -81,7 +85,7 @@ const okText = computed(() => {
|
||||
|
||||
function handleUpdate() {
|
||||
if (!canUpdate.value) {
|
||||
form.name = enterpriseInfo!.name;
|
||||
form.name = enterpriseInfo.value?.name;
|
||||
}
|
||||
infoVisible.value = true;
|
||||
}
|
||||
|
||||
@ -91,7 +91,9 @@ import axios from 'axios';
|
||||
import { useUserStore } from '@/stores';
|
||||
|
||||
const store = useUserStore();
|
||||
const userInfo = store.userInfo;
|
||||
const userInfo = computed(() => {
|
||||
return store.userInfo ?? {};
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@ -115,7 +117,7 @@ const isSendCaptcha = ref(false);
|
||||
const uploadInputRef = ref();
|
||||
|
||||
const dataSource = computed(() => {
|
||||
return userInfo ? [userInfo] : [];
|
||||
return userInfo.value ? [userInfo.value] : [];
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
|
||||
@ -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}”,请稍后...`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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}”,请稍后...`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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: '账号被冻结/封禁',
|
||||
},
|
||||
];
|
||||
@ -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>
|
||||
Reference in New Issue
Block a user