Files
lingji-work-fe/src/views/property-marketing/put-account/account-manage/index.vue
2025-07-09 13:47:34 +08:00

260 lines
7.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
* @Author: RenXiaoDong
* @Date: 2025-06-25 10:00:50
-->
<template>
<div class="account-manage-wrap">
<div class="filter-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid">
<div class="top flex h-64px px-24px py-10px justify-between items-center">
<p class="text-18px font-400 lh-26px color-#211F24 title">账户管理</p>
<div class="flex items-center">
<a-button type="primary" class="w-112px search-btn" size="medium" @click="handleOpenAccountModal">
<template #icon>
<img :src="icon1" width="16" height="16" />
</template>
<template #default>添加账户</template>
</a-button>
</div>
</div>
<FilterBlock v-model:query="query" @onSearch="handleSearch" @onReset="handleReset" />
</div>
<div
v-if="dataSource.length > 0"
class="tip-row flex justify-between px-16px py-10px w-100% my-12px h-42px"
:class="selectedItems.length > 0 ? 'selected' : isAbNormalStatus ? 'abnormal' : 'normal'"
>
<div class="flex items-center">
<div class="flex items-center">
<template v-if="selectedItems.length > 0">
<a-checkbox
:model-value="checkedAll"
:indeterminate="indeterminate"
class="mr-8px"
@change="handleChangeAll"
/>
<span class="label mr-24px">
已选
<span class="color-#6D4CFE">{{ selectedItems.length }}</span>
个账号
</span>
<span class="operation-btn red" @click="handleBatchDelete"> 批量删除 </span>
</template>
<template v-else>
<img :src="isAbNormalStatus ? icon5 : icon4" width="16" height="16" class="mr-8px" />
<span class="label"> {{ tipLabel }} </span>
</template>
</div>
</div>
<template v-if="selectedItems.length > 0">
<img :src="icon6" width="16" height="16" class="cursor-pointer" @click="handleCloseTip" />
</template>
<div v-else>
<a-space v-if="isAbNormalStatus" class="flex items-center">
<a-button class="w-96px err-btn" size="mini" @click="handleOpenAbnormalAccount">
<template #default>查看异常账号</template>
</a-button>
</a-space>
</div>
</div>
<div class="card-wrap">
<AccountTable
v-if="dataSource.length > 0"
:dataSource="dataSource"
:selectedItems="selectedItems"
@selectionChange="handleSelectionChange"
@delete="handleDelete"
@openEdit="handleOpenEdit"
@update="getData"
/>
<NoData v-else />
<div v-if="pageInfo.total > 0" class="pagination-box">
<a-pagination
:total="pageInfo.total"
size="mini"
show-total
show-jumper
:show-page-size="false"
:current="pageInfo.page"
:page-size="pageInfo.pageSize"
@change="onPageChange"
@page-size-change="onPageSizeChange"
/>
</div>
</div>
<AddAccountModal ref="addAccountModalRef" @update="getData" />
<DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import FilterBlock from './components/filter-block';
import AccountTable from './components/account-table';
import AddAccountModal 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 icon1 from '@/assets/img/media-account/icon-add.png';
import icon4 from '@/assets/img/media-account/icon-success.png';
import icon5 from '@/assets/img/media-account/icon-warn.png';
import icon6 from '@/assets/img/media-account/icon-close.png';
const groupManageModalRef = ref(null);
const tagsManageModalRef = ref(null);
const addAccountModalRef = ref(null);
const deleteAccountRef = ref(null);
const pageInfo = ref({
page: 1,
pageSize: 8,
total: 0,
});
const query = ref(cloneDeep(INITIAL_QUERY));
const dataSource = ref([]);
const selectedItems = ref([]);
const healthData = ref({});
const isAbNormalStatus = computed(() => healthData.value?.abnormal_number > 0);
const checkedAll = computed(() => selectedItems.value.length === dataSource.value.length);
const indeterminate = computed(
() => selectedItems.value.length > 0 && selectedItems.value.length < dataSource.value.length,
);
const tipLabel = computed(() => {
if (!isAbNormalStatus.value) {
return '太棒啦!所有账号都在正常运行。';
}
const {
abnormal_number = 0,
login_invalid_number = 0,
too_many_requests_number = 0,
account_frozen_number = 0,
} = healthData.value;
// 定义异常类型映射
const abnormalTypes = [
{ count: login_invalid_number, label: 'cookie过期' },
{ count: too_many_requests_number, label: '已请求频繁' },
{ count: account_frozen_number, label: '账号被封' },
];
// 过滤出有异常的项并格式化
const abnormalLabels = abnormalTypes.filter(({ count }) => count > 0).map(({ count, label }) => `${count}${label}`);
return `共有 ${abnormal_number} 个账号存在授权异常,其中:${abnormalLabels.join('')}`;
});
onMounted(() => {
getData();
});
const getData = () => {
getHealthData();
getAccountData();
};
const getHealthData = async () => {
const { code, data } = await getPlacementAccountsHealth();
if (code === 200) {
healthData.value = data;
}
};
const getAccountData = async () => {
const { page, pageSize } = pageInfo.value;
const { code, data, total } = await getPlacementAccounts({
page,
page_size: pageSize,
...query.value,
});
if (code === 200) {
dataSource.value = data?.data ?? [];
pageInfo.value.total = data?.total ?? 0;
}
};
const reload = () => {
pageInfo.value.page = 1;
getData();
};
const handleSearch = () => {
getData();
};
const handleReset = () => {
pageInfo.value.page = 1;
pageInfo.value.pageSize = 20;
pageInfo.value.total = 0;
selectedItems.value = [];
query.value = cloneDeep(INITIAL_QUERY);
reload();
};
const onPageChange = (current) => {
pageInfo.value.page = current;
getData();
};
const onPageSizeChange = (pageSize) => {
pageInfo.value.pageSize = pageSize;
reload();
};
const handleOpenGroupModal = () => {
groupManageModalRef.value?.open();
};
const handleOpenAccountModal = () => {
addAccountModalRef.value?.open();
};
const handleOpenEdit = (item) => {
addAccountModalRef.value?.open(item.id);
};
const handleSelectionChange = (val) => {
selectedItems.value = val;
};
const handleChangeAll = (val) => {
if (val) {
selectedItems.value = cloneDeep(dataSource.value);
} else {
selectedItems.value = [];
}
};
const handleBatchDelete = () => {
const ids = selectedItems.value.map((item) => item.id);
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 || '-'}"` });
};
const handleCloseTip = () => {
selectedItems.value = [];
};
// const handleOpenAbnormalAccount = () => {
// query.value.status = 2;
// reload();
// };
const onDeleteSuccess = (ids) => {
selectedItems.value = selectedItems.value.filter((item) => !ids.includes(item.id));
getData();
};
const handleOpenAbnormalAccount = () => {
query.value.status = 2;
reload();
};
</script>
<style scoped lang="scss">
@import './style.scss';
</style>