Merge branch 'feature/v1.3_营销资产中台' of http://gogs.lvfunai.com:444/ai-team/lingji-work-fe into feature/v1.3_营销资产中台
This commit is contained in:
@ -35,8 +35,9 @@ export const getMediaAccountsDetail = (id: string) => {
|
||||
};
|
||||
|
||||
// 媒体账号-修改
|
||||
export const putMediaAccounts = (id: string) => {
|
||||
return Http.put(`/v1/media-accounts/${id}`);
|
||||
export const putMediaAccounts = (params = {}) => {
|
||||
const { id, ...rest } = params as { id: string; [key: string]: any };
|
||||
return Http.put(`/v1/media-accounts/${id}`, rest);
|
||||
};
|
||||
|
||||
// 媒体账号-删除
|
||||
@ -105,3 +106,18 @@ export const batchPutTag = (params = {}) => {
|
||||
export const batchPutGroup = (params = {}) => {
|
||||
return Http.put(`/v1/media-accounts/batch-group`, params);
|
||||
};
|
||||
|
||||
// 媒体账号-暂停爬取
|
||||
export const pausePatchAccount = (id: string) => {
|
||||
return Http.patch(`/v1/media-accounts/${id}/pause`);
|
||||
};
|
||||
|
||||
// 媒体账号-开始爬取
|
||||
export const startPatchAccount = (id: string) => {
|
||||
return Http.patch(`/v1/media-accounts/${id}/start`);
|
||||
};
|
||||
|
||||
// 媒体账号-获取授权图片
|
||||
export const getAuthorizedImage = (id: string) => {
|
||||
return Http.get(`/v1/media-accounts/${id}/authorized-image`);
|
||||
};
|
||||
|
||||
BIN
src/assets/img/media-account/icon-feedback-fail.png
Normal file
BIN
src/assets/img/media-account/icon-feedback-fail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
BIN
src/assets/img/media-account/icon-feedback-success.png
Normal file
BIN
src/assets/img/media-account/icon-feedback-success.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/img/media-account/icon-warn-2.png
Normal file
BIN
src/assets/img/media-account/icon-warn-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 490 B |
@ -10,9 +10,7 @@
|
||||
<p class="name">{{ item.name }}</p>
|
||||
<div class="field-row">
|
||||
<span class="label">状态</span>
|
||||
<div class="status-box" :class="`status-box-${item.status}`">
|
||||
<span class="text">{{ STATUS_LIST.find((v) => v.value === item.status)?.label ?? '-' }}</span>
|
||||
</div>
|
||||
<StatusBox :status="item.status" />
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<span class="label">平台</span>
|
||||
@ -37,31 +35,66 @@
|
||||
<div class="field-row">
|
||||
<span class="label">标签</span>
|
||||
<div class="flex items-center">
|
||||
<div v-for="(tag, index) in item.tags" :key="index" class="tag-box">
|
||||
<a-tooltip
|
||||
v-if="item.tags.length > 2"
|
||||
position="bottom"
|
||||
:content="
|
||||
item.tags
|
||||
.slice(2)
|
||||
.map((v) => v.name)
|
||||
.join(',')
|
||||
"
|
||||
>
|
||||
<div class="tag-box">
|
||||
<span class="text">{{ `+${item.tags.length - 2}` }}</span>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
|
||||
<div v-for="(tag, index) in item.tags.slice(0, 2).reverse()" :key="index" class="tag-box">
|
||||
<span class="text">{{ tag.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operate-row">
|
||||
<img :src="icon3" width="16" height="16" class="mr-8px cursor-pointer" @click="openDelete(item)" />
|
||||
<a-button class="w-64px search-btn mr-8px" size="mini">
|
||||
<a-button
|
||||
v-if="showPauseButton(item.status)"
|
||||
class="w-64px search-btn mr-8px"
|
||||
size="mini"
|
||||
@click="handlePause(item)"
|
||||
>
|
||||
<template #default>暂停同步</template>
|
||||
</a-button>
|
||||
<a-button class="w-64px search-btn mr-8px" size="mini">
|
||||
<a-tooltip v-if="isDisabledReauthorize(item.status)" :content="getTooltipText(item.status)">
|
||||
<a-button class="w-64px search-btn mr-8px" size="mini" @click="handleReauthorize(item)">
|
||||
<template #default>重新授权</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template v-else>
|
||||
<a-button class="w-64px search-btn mr-8px" size="mini" @click="handleReauthorize(item)">
|
||||
<template #default>{{ isUnauthorizedStatus(item.status) ? '去授权' : '重新授权' }}</template>
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-button class="w-40px search-btn" size="mini" @click="openEdit(item)">
|
||||
<template #default>编辑</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PauseAccountPatchModal ref="pauseAccountPatchModalRef" @success="emits('update')" />
|
||||
<ReauthorizeAccountModal ref="reauthorizeAccountModalRef" @success="emits('update')" />
|
||||
<AuthorizedAccountModal ref="authorizedAccountModalRef" @success="emits('update')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, ref, computed } from 'vue';
|
||||
import { STATUS_LIST } from '../../constants';
|
||||
import { STATUS_LIST, EnumStatus } from '../../constants';
|
||||
import PauseAccountPatchModal from './pause-account-patch';
|
||||
import StatusBox from '../status-box';
|
||||
import ReauthorizeAccountModal from '../reauthorize-account-modal';
|
||||
import AuthorizedAccountModal from '../authorized-account-modal';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-dy.png';
|
||||
import icon2 from '@/assets/img/media-account/icon-xhs.png';
|
||||
@ -80,6 +113,10 @@ const props = defineProps({
|
||||
|
||||
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete']);
|
||||
|
||||
const pauseAccountPatchModalRef = ref(null);
|
||||
const reauthorizeAccountModalRef = ref(null);
|
||||
const authorizedAccountModalRef = ref(null);
|
||||
|
||||
// 判断当前 item 是否被选中
|
||||
const isSelected = (item) => {
|
||||
return props.selectedItems.some((i) => i.id === item.id);
|
||||
@ -96,12 +133,41 @@ const toggleSelect = (item) => {
|
||||
};
|
||||
|
||||
const openEdit = (item) => {
|
||||
emits('openEdit', item.id);
|
||||
emits('openEdit', item);
|
||||
};
|
||||
|
||||
const openDelete = (item) => {
|
||||
emits('delete', item);
|
||||
};
|
||||
|
||||
const handleReauthorize = (item) => {
|
||||
const isUnauthorized = isUnauthorizedStatus(item.status);
|
||||
if (isUnauthorized) {
|
||||
authorizedAccountModalRef.value?.open(item.id);
|
||||
} else {
|
||||
reauthorizeAccountModalRef.value?.open(item.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePause = (item) => {
|
||||
pauseAccountPatchModalRef.value?.open(item);
|
||||
};
|
||||
|
||||
const showPauseButton = (status) => {
|
||||
return ![EnumStatus.PAUSE, EnumStatus.UNAUTHORIZED].includes(status);
|
||||
};
|
||||
const isUnauthorizedStatus = (status) => {
|
||||
return [EnumStatus.UNAUTHORIZED].includes(status);
|
||||
};
|
||||
|
||||
// 三种异常情况
|
||||
const isDisabledReauthorize = (status) => {
|
||||
return [EnumStatus.ABNORMAL_LOGIN, EnumStatus.ABNORMAL_REQUEST, EnumStatus.ABNORMAL_FREEZE].includes(status);
|
||||
};
|
||||
|
||||
const getTooltipText = (status) => {
|
||||
return STATUS_LIST.find((v) => v.value === status)?.tooltip ?? '-';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-27 14:41:20
|
||||
-->
|
||||
<template>
|
||||
<a-modal v-model:visible="visible" title="暂停同步" width="400px" modal-class="account-manage-modal" @close="onClose">
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
<span>确认暂停同步 “{{ accountName }}” 这个账号的数据吗?</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button class="cancel-btn" size="large" @click="onClose">取消</a-button>
|
||||
<a-button type="primary" class="ml-16px" size="large" @click="onConfirm">确定</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { pausePatchAccount } from '@/api/all/propertyMarketing';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
|
||||
const emits = defineEmits(['success', 'close']);
|
||||
|
||||
const visible = ref(false);
|
||||
const accountId = ref(null);
|
||||
const accountName = ref('');
|
||||
|
||||
function onClose() {
|
||||
visible.value = false;
|
||||
accountId.value = null;
|
||||
accountName.value = '';
|
||||
emits('close');
|
||||
}
|
||||
|
||||
const open = (record) => {
|
||||
const { id = null, name = '' } = record;
|
||||
accountId.value = id;
|
||||
accountName.value = name;
|
||||
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
async function onConfirm() {
|
||||
const { code } = await pausePatchAccount(accountId.value);
|
||||
if (code === 200) {
|
||||
AMessage.success('暂停成功');
|
||||
emits('success');
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
@ -1,3 +1,9 @@
|
||||
@mixin ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
@ -21,6 +27,8 @@
|
||||
// line-height: 22px; /* 157.143% */
|
||||
}
|
||||
.label {
|
||||
margin-right: 20px;
|
||||
flex-shrink: 0;
|
||||
color: var(--Text-3, #737478);
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 12px;
|
||||
@ -34,6 +42,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.cts {
|
||||
@include ellipsis;
|
||||
color: var(--Text-2, #3c4043);
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 12px;
|
||||
@ -41,42 +50,16 @@
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
.status-box {
|
||||
display: flex;
|
||||
padding: 0px 8px;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
background: #f2f3f5;
|
||||
.text {
|
||||
color: var(--BG-700, #737478);
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
|
||||
&-1 {
|
||||
background: #ebf7f2;
|
||||
.text {
|
||||
color: #25c883;
|
||||
}
|
||||
}
|
||||
&-2 {
|
||||
background: #ffe7e4;
|
||||
.text {
|
||||
color: #f64b31;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tag-box {
|
||||
display: flex;
|
||||
height: 16px;
|
||||
height: 20px;
|
||||
padding: 0px 4px;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
background: var(--BG-200, #f2f3f5);
|
||||
max-width: 100px;
|
||||
.text {
|
||||
@include ellipsis();
|
||||
color: var(--Text-2, #3c4043);
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 10px;
|
||||
|
||||
@ -9,9 +9,10 @@
|
||||
modal-class="add-account-modal"
|
||||
width="500px"
|
||||
:mask-closable="false"
|
||||
@close="onClose"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" layout="horizontal" auto-label-width>
|
||||
<a-form-item label="上传方式" required>
|
||||
<a-form-item v-if="!isEdit" label="上传方式" required>
|
||||
<a-radio-group v-model="uploadType">
|
||||
<a-radio value="manual">手动添加账号</a-radio>
|
||||
<a-radio value="batch">批量导入账号</a-radio>
|
||||
@ -53,6 +54,18 @@
|
||||
|
||||
<!-- 手动添加账号 -->
|
||||
<template v-else>
|
||||
<template v-if="isEdit">
|
||||
<a-form-item label="账号名称" field="name">
|
||||
<a-input v-model="form.name" placeholder="请输入..." size="large" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item label="账号ID" field="account_id">
|
||||
<a-input v-model="form.account_id" placeholder="请输入..." size="large" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item label="状态" field="status">
|
||||
<StatusBox :status="form.status" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<a-form-item label="手机号" field="mobile" required>
|
||||
<a-input v-model="form.mobile" placeholder="请输入..." size="large" />
|
||||
</a-form-item>
|
||||
@ -62,8 +75,9 @@
|
||||
<a-form-item label="号码持有人" field="holder_name" required>
|
||||
<a-input v-model="form.holder_name" placeholder="请输入..." class="w-240px" size="large" />
|
||||
</a-form-item>
|
||||
<a-form-item label="运营平台" required>
|
||||
<a-radio-group v-model="form.platform">
|
||||
<a-form-item label="运营平台" :required="!isEdit">
|
||||
<img v-if="isEdit" :src="form.platform === 0 ? icon3 : icon4" width="24" height="24" />
|
||||
<a-radio-group v-else v-model="form.platform">
|
||||
<a-radio :value="0">抖音</a-radio>
|
||||
<a-radio :value="1">小红书</a-radio>
|
||||
</a-radio-group>
|
||||
@ -83,13 +97,13 @@
|
||||
</template>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button size="large" class="mr-16px 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">
|
||||
{{ isBatchImport ? '确定导入' : '生成授权码' }}
|
||||
{{ confirmBtnText }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<QrCodeModal ref="qrCodeModalRef" />
|
||||
<AuthorizedAccountModal ref="authorizedAccountModalRef" />
|
||||
<ImportPromptModal ref="importPromptModalRef" />
|
||||
</a-modal>
|
||||
</template>
|
||||
@ -98,8 +112,9 @@
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import TagSelect from '../tag-select';
|
||||
import GroupSelect from '../group-select';
|
||||
import QrCodeModal from '../qrCode-modal';
|
||||
import AuthorizedAccountModal from '../authorized-account-modal';
|
||||
import ImportPromptModal from '../import-prompt-modal';
|
||||
import StatusBox from '../status-box';
|
||||
|
||||
import {
|
||||
fetchAccountTags,
|
||||
@ -112,6 +127,8 @@ import {
|
||||
|
||||
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';
|
||||
|
||||
const groupOptions = ref([]);
|
||||
const tagOptions = ref([]);
|
||||
@ -122,7 +139,7 @@ const id = ref('');
|
||||
const isEdit = ref(false);
|
||||
const fileName = ref('账号导入模板.xlsx');
|
||||
const formRef = ref();
|
||||
const qrCodeModalRef = ref(null);
|
||||
const authorizedAccountModalRef = ref(null);
|
||||
const importPromptModalRef = ref(null);
|
||||
const form = reactive({
|
||||
mobile: '',
|
||||
@ -204,6 +221,7 @@ const reset = () => {
|
||||
formRef.value.clearValidate();
|
||||
|
||||
fileName.value = '';
|
||||
isEdit.value = false;
|
||||
uploadStatus.value = 'default';
|
||||
uploadType.value = 'manual';
|
||||
};
|
||||
@ -215,6 +233,8 @@ function onClose() {
|
||||
const open = (accountId = '') => {
|
||||
id.value = accountId;
|
||||
isEdit.value = !!accountId;
|
||||
|
||||
console.log({ accountId });
|
||||
if (accountId) {
|
||||
getAccountDetail();
|
||||
}
|
||||
@ -242,26 +262,28 @@ async function onSubmit() {
|
||||
return;
|
||||
}
|
||||
|
||||
handleSuccess();
|
||||
|
||||
formRef.value.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
const _fn = id.value ? putMediaAccounts : postMediaAccounts;
|
||||
const _params = id.value ? { id: id.value, ...form.value } : form;
|
||||
const { code } = await _fn(_params);
|
||||
const { code, data } = await _fn(_params);
|
||||
if (code === 200) {
|
||||
AMessage.success(isEdit.value ? '修改成功' : '生成授权码成功');
|
||||
|
||||
if (isEdit.value) {
|
||||
visible.value = false;
|
||||
} else {
|
||||
handleSuccess();
|
||||
handleSuccess(data?.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleSuccess = () => {
|
||||
qrCodeModalRef.value.open();
|
||||
const handleSuccess = (id) => {
|
||||
authorizedAccountModalRef.value.open(id);
|
||||
};
|
||||
|
||||
const handleDownloadTemplate = async () => {
|
||||
|
||||
@ -0,0 +1,158 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 17:51:46
|
||||
-->
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
width="480px"
|
||||
title="授权账号"
|
||||
modal-class="authorized-account-modal"
|
||||
:mask-closable="false"
|
||||
:footer="!isLoading"
|
||||
@close="close"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<template v-if="isLoading">
|
||||
<a-progress
|
||||
:percent="progress"
|
||||
color="#6D4CFE"
|
||||
trackColor="#E6E6E8"
|
||||
size="large"
|
||||
:stroke-width="4"
|
||||
type="circle"
|
||||
/>
|
||||
<p class="s2 mt-16px">数据同步和初始化中,请勿关闭窗口。</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="isCompleted">
|
||||
<img :src="isSuccess ? icon2 : icon3" width="80" height="80" class="mb-16px" />
|
||||
<p class="s2">{{ `数据初始化${isSuccess ? '成功' : '失败'}。` }}</p>
|
||||
<p v-if="!isSuccess" class="red-text">失败原因:{{ failReason || '-' }}</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="flex items-center mb-16px">
|
||||
<img :src="icon1" width="16" height="16" />
|
||||
<span class="ml-8px red-text">未识别到有效二维码。</span>
|
||||
</div>
|
||||
<div class="img-box">
|
||||
<img :src="imgUrl" width="160" height="160" class="mb-16px" />
|
||||
<div v-if="isOverdue" class="mask">
|
||||
<icon-refresh size="24" class="mb-13px" />
|
||||
<p class="s1">二维码失效</p>
|
||||
<p class="s1">请点击刷新</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="mt-16px"> 请使用抖音扫码,将公司账号绑定至灵机平台。 </span>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button v-if="isCompleted" size="large" class="cancel-btn" @click="close">取消</a-button>
|
||||
<a-button type="primary" size="large" @click="handleOk">{{ confirmBtnText }} </a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineExpose, ref, onUnmounted } from 'vue';
|
||||
import { startPatchAccount, getAuthorizedImage } from '@/api/all/propertyMarketing';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-warn.png';
|
||||
import icon2 from '@/assets/img/media-account/icon-feedback-success.png';
|
||||
import icon3 from '@/assets/img/media-account/icon-feedback-fail.png';
|
||||
|
||||
const visible = ref(false);
|
||||
const isOverdue = ref(false);
|
||||
const isLoading = ref(false);
|
||||
const isCompleted = ref(false);
|
||||
const isSuccess = ref(false);
|
||||
const failReason = ref('');
|
||||
const progress = ref(0);
|
||||
const id = ref('');
|
||||
const imgUrl = ref('');
|
||||
|
||||
let progressTimer = null;
|
||||
|
||||
const notCompleted = computed(() => {
|
||||
return !isCompleted.value;
|
||||
});
|
||||
const confirmBtnText = computed(() => {
|
||||
if (notCompleted.value) return '完成扫码';
|
||||
return isSuccess.value ? '继续添加' : '重新扫码';
|
||||
});
|
||||
|
||||
const open = (accountId) => {
|
||||
id.value = accountId;
|
||||
handleAuthorizedImage();
|
||||
visible.value = true;
|
||||
};
|
||||
const close = () => {
|
||||
isOverdue.value = false;
|
||||
isLoading.value = false;
|
||||
isCompleted.value = false;
|
||||
isSuccess.value = false;
|
||||
failReason.value = '';
|
||||
progress.value = 0;
|
||||
id.value = '';
|
||||
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const handleAuthorizedImage = async () => {
|
||||
const { code, data } = await getAuthorizedImage(id.value);
|
||||
if (code === 200) {
|
||||
imgUrl.value = data.url;
|
||||
}
|
||||
};
|
||||
const startLoading = async () => {
|
||||
isLoading.value = true;
|
||||
progress.value = 0;
|
||||
startFakeProgressPolling();
|
||||
// const { code } = await startPatchAccount(id.value);
|
||||
// if (code === 200) {
|
||||
// isLoading.value = true;
|
||||
// progress.value = 0;
|
||||
// startFakeProgressPolling();
|
||||
// }
|
||||
};
|
||||
|
||||
const startFakeProgressPolling = () => {
|
||||
clearFakeProgressTimer();
|
||||
progressTimer = setInterval(() => {
|
||||
if (progress.value < 0.99) {
|
||||
const step = Math.random() * 0.04 + 0.01;
|
||||
progress.value = Math.min(progress.value + step, 0.99);
|
||||
progress.value = Number(progress.value.toFixed(2));
|
||||
} else {
|
||||
clearFakeProgressTimer();
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const clearFakeProgressTimer = () => {
|
||||
if (progressTimer) {
|
||||
clearInterval(progressTimer);
|
||||
progressTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
if (notCompleted.value) {
|
||||
startLoading();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
clearFakeProgressTimer();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -0,0 +1,49 @@
|
||||
@import '@/views/property-marketing/component.scss';
|
||||
|
||||
.authorized-account-modal {
|
||||
border-radius: 8px;
|
||||
.img-box {
|
||||
position: relative;
|
||||
.mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.8;
|
||||
background: #000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
.s1 {
|
||||
color: var(--BG-White, #fff);
|
||||
text-align: center;
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.s2 {
|
||||
color: var(--Text-1, #211f24);
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
.red-text {
|
||||
overflow: hidden;
|
||||
color: var(--Functional-Red-6, #f64b31);
|
||||
text-overflow: ellipsis;
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
@ -75,14 +75,13 @@ const form = reactive({
|
||||
group_id: null,
|
||||
});
|
||||
|
||||
const accounts = ref([]); // [{id, name}]
|
||||
const accountGroupList = ref([]); // [{id, name, group_id: null}]
|
||||
const isAllEdit = computed(() => editType.value === 'all');
|
||||
|
||||
const open = (accountList = []) => {
|
||||
editType.value = 'all';
|
||||
groupOptions.value = [];
|
||||
form.group_id = null;
|
||||
accounts.value = accountList;
|
||||
accountGroupList.value = accountList.map((acc) => ({
|
||||
...acc,
|
||||
group_id: null,
|
||||
@ -114,21 +113,31 @@ const getTags = async () => {
|
||||
|
||||
const onClose = () => {
|
||||
visible.value = false;
|
||||
form.tag_ids = [];
|
||||
form.group_id = null;
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
// 这里处理批量标签的提交逻辑
|
||||
const _params =
|
||||
editType.value === 'all'
|
||||
? { id: -1, group_id: form.group_id }
|
||||
: accountGroupList.value.map((item) => ({
|
||||
if (isAllEdit.value) {
|
||||
if (form.group_id === null) {
|
||||
AMessage.error('请选择分组');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (accountGroupList.value.some((item) => item.group_id === null)) {
|
||||
AMessage.error('请选择分组');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const media_accounts = accountGroupList.value.map((item) => ({
|
||||
id: item.id,
|
||||
group_id: item.group_id,
|
||||
group_id: isAllEdit.value ? form.group_id : item.group_id,
|
||||
}));
|
||||
const { code } = await batchPutGroup({ media_accounts: _params });
|
||||
|
||||
// 这里处理批量标签的提交逻辑
|
||||
const { code } = await batchPutGroup({ media_accounts });
|
||||
if (code === 200) {
|
||||
AMessage.success('批量分组成功');
|
||||
AMessage.success('设置分组成功');
|
||||
emits('update');
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
<template v-if="editType === 'all'">
|
||||
<div class="flex items-center w-100%">
|
||||
<a-select
|
||||
v-model="form.tag_ids"
|
||||
v-model="form.tags"
|
||||
:options="tagOptions"
|
||||
multiple
|
||||
allow-create
|
||||
@ -40,7 +40,7 @@
|
||||
class="flex-1"
|
||||
@create="handleCreateTag"
|
||||
/>
|
||||
<span class="ml-12px">{{ `${form.tag_ids.length}/5` }}</span>
|
||||
<span class="ml-12px">{{ `${form.tags.length}/5` }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-form-item>
|
||||
@ -90,18 +90,17 @@ const tagOptions = ref([]);
|
||||
const formRef = ref();
|
||||
const editType = ref('all');
|
||||
const form = reactive({
|
||||
tag_ids: [],
|
||||
tags: [],
|
||||
});
|
||||
|
||||
const accounts = ref([]); // [{id, name}]
|
||||
const accountTagList = ref([]); // [{id, name, tags: []}]
|
||||
const isAllEdit = computed(() => editType.value === 'all');
|
||||
|
||||
// 打开弹窗时请求标签数据
|
||||
const open = (accountList = []) => {
|
||||
editType.value = 'all';
|
||||
tagOptions.value = [];
|
||||
form.tag_ids = [];
|
||||
accounts.value = accountList;
|
||||
form.tags = [];
|
||||
accountTagList.value = accountList.map((acc) => ({
|
||||
...acc,
|
||||
tags: [],
|
||||
@ -114,19 +113,19 @@ const open = (accountList = []) => {
|
||||
const getTags = async () => {
|
||||
tagOptions.value = [
|
||||
{
|
||||
label: '测试',
|
||||
value: '1',
|
||||
label: '测试1',
|
||||
value: '测试1',
|
||||
},
|
||||
{
|
||||
label: 'ceshi2',
|
||||
value: 2,
|
||||
label: '测试2',
|
||||
value: '测试2',
|
||||
},
|
||||
];
|
||||
const { code, data } = await fetchAccountTags();
|
||||
if (code === 200) {
|
||||
tagOptions.value = data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
value: item.name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
@ -138,26 +137,36 @@ const handleCreateTag = (inputValue, idx) => {
|
||||
if (typeof idx === 'number') {
|
||||
accountTagList.value[idx].tags.push(inputValue);
|
||||
} else {
|
||||
form.tag_ids.push(inputValue);
|
||||
form.tags.push(inputValue);
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
visible.value = false;
|
||||
form.tag_ids = [];
|
||||
form.tags = [];
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
const _params =
|
||||
editType.value === 'all'
|
||||
? { tag_ids: form.tag_ids }
|
||||
: accountTagList.value.map((item) => ({
|
||||
if (isAllEdit.value) {
|
||||
if (form.tags.length === 0) {
|
||||
AMessage.error('请输入标签');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (accountTagList.value.some((item) => item.tags.length === 0)) {
|
||||
AMessage.error('请输入标签');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const media_accounts = accountTagList.value.map((item) => ({
|
||||
id: item.id,
|
||||
tag_ids: item.tags,
|
||||
tags: isAllEdit.value ? form.tags : item.tags,
|
||||
}));
|
||||
const { code } = await batchPutTag({ media_accounts: _params });
|
||||
console.log({ media_accounts });
|
||||
const { code } = await batchPutTag({ media_accounts });
|
||||
if (code === 200) {
|
||||
AMessage.success('批量标签成功');
|
||||
AMessage.success('设置标签成功');
|
||||
emits('update');
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
<!-- eslint-disable vue/no-mutating-props -->
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 14:02:40
|
||||
@ -24,7 +25,7 @@
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">状态</span>
|
||||
<a-space class="w-160px">
|
||||
<a-space class="w-180px">
|
||||
<a-select v-model="query.status" size="medium" placeholder="全部" allow-clear @change="handleSearch">
|
||||
<a-option v-for="(item, index) in STATUS_LIST" :key="index" :value="item.value" :label="item.label">{{
|
||||
item.label
|
||||
@ -66,9 +67,7 @@
|
||||
<tag-select v-model="query.tag_ids" :options="tags" @change="handleSearch" />
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
<a-space class="flex items-center">
|
||||
<a-button class="w-84px search-btn" size="medium" @click="handleSearch">
|
||||
<a-button class="w-84px search-btn mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
@ -80,7 +79,7 @@
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -95,18 +94,25 @@ import {
|
||||
STATUS_LIST,
|
||||
} from '@/views/property-marketing/media-account/account-manage/constants';
|
||||
|
||||
const emits = defineEmits('onSearch', 'onReset');
|
||||
const props = defineProps({
|
||||
query: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits('onSearch', 'onReset', 'update:query');
|
||||
|
||||
const query = ref(cloneDeep(INITIAL_QUERY));
|
||||
const tags = ref([]);
|
||||
const groups = ref([]);
|
||||
const operators = ref([]);
|
||||
|
||||
const handleSearch = () => {
|
||||
emits('onSearch', query);
|
||||
emits('onSearch', props.query);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
query.value = cloneDeep(INITIAL_QUERY);
|
||||
emits('update:query', cloneDeep(INITIAL_QUERY));
|
||||
emits('onReset');
|
||||
};
|
||||
|
||||
@ -130,9 +136,9 @@ const getOperators = async () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getTags();
|
||||
getGroups();
|
||||
getOperators();
|
||||
// getTags();
|
||||
// getGroups();
|
||||
// getOperators();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 17:51:46
|
||||
-->
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
width="480px"
|
||||
title="添加账号"
|
||||
modal-class="qrCode-modal"
|
||||
:footer="false"
|
||||
:mask-closable="false"
|
||||
@close="close"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<img src="" width="160" height="160" class="mb-16px" />
|
||||
<span> 请使用抖音扫码,将公司账号绑定至灵机平台。 </span>
|
||||
|
||||
<div class="mt-24px text-center">
|
||||
<a-button type="primary" size="large" @click="handleOk"> 完成扫码 </a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineExpose } from 'vue';
|
||||
|
||||
const visible = ref(false);
|
||||
const open = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
const close = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
const handleOk = () => {
|
||||
console.log('handleOk');
|
||||
};
|
||||
defineExpose({
|
||||
open,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -1,19 +0,0 @@
|
||||
.qrCode-modal {
|
||||
border-radius: 8px;
|
||||
|
||||
.arco-modal-header {
|
||||
border-bottom: none;
|
||||
height: 56px;
|
||||
padding: 22px 24px 16px 24px;
|
||||
.arco-modal-title {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
.arco-modal-body {
|
||||
padding: 20px 24px 20px;
|
||||
}
|
||||
.arco-modal-footer {
|
||||
border-top: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,201 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 17:51:46
|
||||
-->
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
width="500px"
|
||||
title="重新授权"
|
||||
modal-class="reauthorize-account-modal"
|
||||
:mask-closable="false"
|
||||
:footer="!isLoading"
|
||||
@close="close"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<template v-if="isLoading">
|
||||
<a-progress
|
||||
:percent="progress"
|
||||
color="#6D4CFE"
|
||||
trackColor="#E6E6E8"
|
||||
size="large"
|
||||
:stroke-width="4"
|
||||
type="circle"
|
||||
/>
|
||||
<p class="s2 mt-16px">数据同步和初始化中,请勿关闭窗口。</p>
|
||||
</template>
|
||||
<template v-else-if="loadingStep === 1">
|
||||
<div class="flex items-center mb-20px">
|
||||
<img :src="icon4" width="16" height="16" class="mr-16px" />
|
||||
<span class="s2 color-#3C4043">检测到该账号最后更新日期为x月x日,已有x天未同步最新数据。</span>
|
||||
</div>
|
||||
<a-radio-group v-model="actionType" class="ml-32px">
|
||||
<a-radio :value="1" class="mb-16px"
|
||||
><span class="s2"
|
||||
>立即同步遗漏数据 - 获取完整的最新数据 <span class="color-#6D4CFE">(推荐)</span></span
|
||||
></a-radio
|
||||
>
|
||||
<a-radio :value="2" class="mb-16px"><span class="s2">仅授权不更新 - 继续使用当前不完全的数据</span></a-radio>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
<template v-else-if="loadingStep === 2">
|
||||
<div class="flex items-center mb-20px">
|
||||
<img :src="icon4" width="16" height="16" class="mr-16px" />
|
||||
<span class="s2 color-#3C4043">当前绑定的账号与之前的昵称不一致,请确认是否覆盖原账号信息。</span>
|
||||
</div>
|
||||
<div class="w-100% pl-32px">
|
||||
<div class="account-tip-box">
|
||||
<p class="mb-4px s2"><span class="color-#3C4043">原账号:</span>全球旅行小助手</p>
|
||||
<p class="s2"><span class="color-#3C4043">当前账号:</span>环球旅行官</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="isCompleted">
|
||||
<img :src="isSuccess ? icon2 : icon3" width="80" height="80" class="mb-16px" />
|
||||
<p class="s2">{{ `数据初始化${isSuccess ? '成功' : '失败'}。` }}</p>
|
||||
<p v-if="!isSuccess" class="red-text">失败原因:{{ failReason || '-' }}</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="flex items-center mb-16px">
|
||||
<img :src="icon1" width="16" height="16" />
|
||||
<span class="ml-8px red-text">未识别到有效二维码。</span>
|
||||
</div>
|
||||
<div class="img-box">
|
||||
<img :src="imgUrl" width="160" height="160" class="mb-16px" />
|
||||
<div v-if="isOverdue" class="mask">
|
||||
<icon-refresh size="24" class="mb-13px" />
|
||||
<p class="s1">二维码失效</p>
|
||||
<p class="s1">请点击刷新</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="mt-16px"> 请使用抖音扫码,将公司账号绑定至灵机平台。 </span>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button v-if="isCompleted || [1, 3].includes(actionType)" size="large" class="cancel-btn" @click="close"
|
||||
>取消</a-button
|
||||
>
|
||||
<a-button type="primary" size="large" @click="handleOk">{{ confirmBtnText }} </a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineExpose, ref, onUnmounted } from 'vue';
|
||||
import { startPatchAccount, getAuthorizedImage } from '@/api/all/propertyMarketing';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-warn.png';
|
||||
import icon2 from '@/assets/img/media-account/icon-feedback-success.png';
|
||||
import icon3 from '@/assets/img/media-account/icon-feedback-fail.png';
|
||||
import icon4 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
|
||||
const visible = ref(false);
|
||||
const isOverdue = ref(false);
|
||||
const isLoading = ref(false);
|
||||
const isCompleted = ref(false);
|
||||
const isSuccess = ref(false);
|
||||
const failReason = ref('');
|
||||
const progress = ref(0);
|
||||
const id = ref('');
|
||||
const imgUrl = ref('');
|
||||
const loadingStep = ref(0); // 1 | 2
|
||||
const actionType = ref(1);
|
||||
|
||||
let progressTimer = null;
|
||||
|
||||
const notCompleted = computed(() => {
|
||||
return !isCompleted.value;
|
||||
});
|
||||
const confirmBtnText = computed(() => {
|
||||
if (loadingStep.value === 1) return '确定';
|
||||
if (loadingStep.value === 2) return '确定覆盖';
|
||||
|
||||
if (notCompleted.value) return '完成扫码';
|
||||
|
||||
return isSuccess.value ? '继续添加' : '重新扫码';
|
||||
});
|
||||
|
||||
const open = (accountId) => {
|
||||
id.value = accountId;
|
||||
handleAuthorizedImage();
|
||||
visible.value = true;
|
||||
};
|
||||
const close = () => {
|
||||
isOverdue.value = false;
|
||||
isLoading.value = false;
|
||||
isCompleted.value = false;
|
||||
isSuccess.value = false;
|
||||
failReason.value = '';
|
||||
progress.value = 0;
|
||||
loadingStep.value = 0;
|
||||
actionType.value = 1;
|
||||
id.value = '';
|
||||
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const handleAuthorizedImage = async () => {
|
||||
const { code, data } = await getAuthorizedImage(id.value);
|
||||
if (code === 200) {
|
||||
imgUrl.value = data.url;
|
||||
}
|
||||
};
|
||||
const startLoading = async () => {
|
||||
isLoading.value = true;
|
||||
progress.value = 0;
|
||||
startFakeProgressPolling();
|
||||
// const { code } = await startPatchAccount(id.value);
|
||||
// if (code === 200) {
|
||||
// isLoading.value = true;
|
||||
// progress.value = 0;
|
||||
// startFakeProgressPolling();
|
||||
// }
|
||||
};
|
||||
|
||||
const startFakeProgressPolling = () => {
|
||||
clearFakeProgressTimer();
|
||||
progressTimer = setInterval(() => {
|
||||
if (progress.value < 0.99) {
|
||||
const step = Math.random() * 0.04 + 0.01;
|
||||
progress.value = Math.min(progress.value + step, 0.99);
|
||||
progress.value = Number(progress.value.toFixed(2));
|
||||
} else {
|
||||
loadingStep.value = 2;
|
||||
clearFakeProgressTimer();
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const clearFakeProgressTimer = () => {
|
||||
if (progressTimer) {
|
||||
clearInterval(progressTimer);
|
||||
progressTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
if (loadingStep.value === 1) {
|
||||
startLoading();
|
||||
return;
|
||||
}
|
||||
|
||||
if (notCompleted.value) {
|
||||
loadingStep.value = 1;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
clearFakeProgressTimer();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -0,0 +1,57 @@
|
||||
@import '@/views/property-marketing/component.scss';
|
||||
|
||||
.reauthorize-account-modal {
|
||||
border-radius: 8px;
|
||||
.img-box {
|
||||
position: relative;
|
||||
.mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.8;
|
||||
background: #000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
.s1 {
|
||||
color: var(--BG-White, #fff);
|
||||
text-align: center;
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.s2 {
|
||||
color: var(--Text-1, #211f24);
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
.red-text {
|
||||
overflow: hidden;
|
||||
color: var(--Functional-Red-6, #f64b31);
|
||||
text-overflow: ellipsis;
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
.account-tip-box {
|
||||
// margin-left: 32px;
|
||||
width: 100%;
|
||||
padding: 8px 0px 8px 12px;
|
||||
|
||||
border-radius: 4px;
|
||||
background: var(--BG-200, #F2F3F5);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 15:31:15
|
||||
-->
|
||||
<template>
|
||||
<div class="status-box" :class="`status-box-${status}`">
|
||||
<span class="text">{{ statusText }}</span>
|
||||
<a-tooltip v-if="showTooltip" :content="tooltipText">
|
||||
<img v-if="showIcon" :src="iconSrc" width="12" height="12" class="ml-4px" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { STATUS_LIST, EnumStatus } from '../../constants';
|
||||
|
||||
import iconWarn1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
import iconWarn2 from '@/assets/img/media-account/icon-warn-2.png';
|
||||
|
||||
const props = defineProps({
|
||||
status: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const statusText = computed(() => {
|
||||
return STATUS_LIST.find((v) => v.value === props.status)?.label ?? '-';
|
||||
});
|
||||
|
||||
const showTooltip = computed(() => {
|
||||
return isDisabledReauthorize(props.status);
|
||||
});
|
||||
|
||||
const tooltipText = computed(() => {
|
||||
return STATUS_LIST.find((v) => v.value === props.status)?.tooltip ?? '-';
|
||||
});
|
||||
|
||||
const showIcon = computed(() => {
|
||||
return ![EnumStatus.NORMAL, EnumStatus.UNAUTHORIZED].includes(props.status);
|
||||
});
|
||||
|
||||
const iconSrc = computed(() => {
|
||||
return props.status === EnumStatus.PAUSE ? iconWarn1 : iconWarn2;
|
||||
});
|
||||
|
||||
// 判断是否为禁用重新授权的状态
|
||||
const isDisabledReauthorize = (status) => {
|
||||
return [EnumStatus.ABNORMAL_LOGIN, EnumStatus.ABNORMAL_REQUEST, EnumStatus.ABNORMAL_FREEZE].includes(status);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.status-box {
|
||||
display: flex;
|
||||
padding: 0px 8px;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
background: #f2f3f5;
|
||||
|
||||
.text {
|
||||
color: var(--BG-700, #737478);
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
|
||||
&-1 {
|
||||
background: #ebf7f2;
|
||||
.text {
|
||||
color: #25c883;
|
||||
}
|
||||
}
|
||||
|
||||
&-2,
|
||||
&-4,
|
||||
&-5,
|
||||
&-6 {
|
||||
background: #ffe7e4;
|
||||
.text {
|
||||
color: #f64b31;
|
||||
}
|
||||
}
|
||||
|
||||
&-3 {
|
||||
background: #fff7e5;
|
||||
color: #ffae00;
|
||||
.text {
|
||||
color: #ffae00;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -22,17 +22,46 @@ export const PLATFORM_LIST = [
|
||||
},
|
||||
];
|
||||
|
||||
export enum EnumStatus {
|
||||
NORMAL = 1,
|
||||
PAUSE = 3,
|
||||
UNAUTHORIZED = 0,
|
||||
ABNORMAL = 2,
|
||||
ABNORMAL_LOGIN = 4,
|
||||
ABNORMAL_REQUEST = 5,
|
||||
ABNORMAL_FREEZE = 6,
|
||||
}
|
||||
|
||||
export const STATUS_LIST = [
|
||||
{
|
||||
label: '未授权',
|
||||
value: 0,
|
||||
label: '正常',
|
||||
value: EnumStatus.NORMAL,
|
||||
},
|
||||
{
|
||||
label: '正常',
|
||||
value: 1,
|
||||
label: '暂停同步',
|
||||
value: EnumStatus.PAUSE,
|
||||
},
|
||||
{
|
||||
label: '未授权',
|
||||
value: EnumStatus.UNAUTHORIZED,
|
||||
},
|
||||
{
|
||||
label: '异常',
|
||||
value: 2,
|
||||
value: EnumStatus.ABNORMAL,
|
||||
},
|
||||
{
|
||||
label: '异常',
|
||||
value: EnumStatus.ABNORMAL_LOGIN,
|
||||
tooltip: '登录状态失效,需重新扫码授权',
|
||||
},
|
||||
{
|
||||
label: '异常',
|
||||
value: EnumStatus.ABNORMAL_REQUEST,
|
||||
tooltip: '请求过于频繁,需等待24小时后重试',
|
||||
},
|
||||
{
|
||||
label: '异常',
|
||||
value: EnumStatus.ABNORMAL_FREEZE,
|
||||
tooltip: '账号被冻结/封禁',
|
||||
},
|
||||
];
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<FilterBlock @onSearch="handleSearch" @onReset="handleReset" />
|
||||
<FilterBlock v-model:query="query" @onSearch="handleSearch" @onReset="handleReset" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -72,7 +72,7 @@
|
||||
</template>
|
||||
<div v-else>
|
||||
<a-space v-if="isAbnormalStatus" class="flex items-center">
|
||||
<a-button class="w-96px err-btn" size="mini">
|
||||
<a-button class="w-96px err-btn" size="mini" @click="handleOpenAbnormalAccount">
|
||||
<template #default>查看异常账号</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
@ -84,6 +84,7 @@
|
||||
:selectedItems="selectedItems"
|
||||
@selectionChange="handleSelectionChange"
|
||||
@delete="handleDelete"
|
||||
@openEdit="handleOpenEdit"
|
||||
/>
|
||||
<div class="pagination-box">
|
||||
<a-pagination
|
||||
@ -160,6 +161,7 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const getData = async () => {
|
||||
console.log('getData');
|
||||
// const { page, pageSize } = pageInfo;
|
||||
// const { code, data, total } = await getMediaAccounts({
|
||||
// page,
|
||||
@ -176,7 +178,7 @@ const getData = async () => {
|
||||
name: '全球',
|
||||
account_id: 1,
|
||||
mobile: 1777777,
|
||||
status: 1,
|
||||
status: 0,
|
||||
platform: 0,
|
||||
operator: {
|
||||
name: '小周',
|
||||
@ -191,6 +193,15 @@ const getData = async () => {
|
||||
{
|
||||
name: '标签2',
|
||||
},
|
||||
{
|
||||
name: '标签3',
|
||||
},
|
||||
{
|
||||
name: '标签4',
|
||||
},
|
||||
{
|
||||
name: '标签5',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -198,7 +209,7 @@ const getData = async () => {
|
||||
name: '全球2',
|
||||
account_id: 1,
|
||||
mobile: 1777777,
|
||||
status: 1,
|
||||
status: 4,
|
||||
platform: 0,
|
||||
operator: {
|
||||
name: '小周',
|
||||
@ -217,13 +228,16 @@ const getData = async () => {
|
||||
},
|
||||
];
|
||||
};
|
||||
const handleSearch = (newQuery) => {
|
||||
query.value = { ...newQuery };
|
||||
const reload = () => {
|
||||
pageInfo.page = 1;
|
||||
getData();
|
||||
};
|
||||
const handleSearch = () => {
|
||||
getData();
|
||||
};
|
||||
const handleReset = () => {
|
||||
query.value = cloneDeep(INITIAL_QUERY);
|
||||
getData();
|
||||
reload();
|
||||
};
|
||||
|
||||
const onPageChange = (current) => {
|
||||
@ -232,8 +246,7 @@ const onPageChange = (current) => {
|
||||
};
|
||||
const onPageSizeChange = (pageSize) => {
|
||||
pageInfo.pageSize = pageSize;
|
||||
pageInfo.page = 1;
|
||||
getData();
|
||||
reload();
|
||||
};
|
||||
|
||||
const handleOpenGroupModal = () => {
|
||||
@ -262,12 +275,12 @@ const handleChangeAll = (val) => {
|
||||
};
|
||||
const handleBatchDelete = () => {
|
||||
const ids = selectedItems.value.map((item) => item.id);
|
||||
const names = selectedItems.value.map((item) => `“${item.name}”`).join(',');
|
||||
const names = selectedItems.value.map((item) => `"${item.name}"`).join(',');
|
||||
deleteAccountRef.value?.open({ id: ids, name: names });
|
||||
};
|
||||
const handleDelete = (item) => {
|
||||
const { id, name } = item;
|
||||
deleteAccountRef.value?.open({ id, name: `“${name}”` });
|
||||
deleteAccountRef.value?.open({ id, name: `"${name}"` });
|
||||
};
|
||||
const handleCloseTip = () => {
|
||||
selectedItems.value = [];
|
||||
@ -279,6 +292,10 @@ const handleBatchTag = () => {
|
||||
const handleBatchGroup = () => {
|
||||
batchGroupModalRef.value?.open(selectedItems.value);
|
||||
};
|
||||
const handleOpenAbnormalAccount = () => {
|
||||
query.value.status = 2;
|
||||
reload();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user