feat(account-manage): 添加表格视图并优化现有组件样式和逻辑
This commit is contained in:
@ -3,7 +3,8 @@
|
|||||||
* @Date: 2025-06-25 15:31:15
|
* @Date: 2025-06-25 15:31:15
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="card-container">
|
<NoData v-if="!dataSource.length" />
|
||||||
|
<div v-else class="card-container">
|
||||||
<Spin
|
<Spin
|
||||||
v-for="(item, index) in dataSource"
|
v-for="(item, index) in dataSource"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -14,7 +15,12 @@
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-sync size="24" />
|
<icon-sync size="24" />
|
||||||
</template>
|
</template>
|
||||||
<Checkbox :checked="isSelected(item)" :value="item.id" @change="toggleSelect(item)" class="relative top--2px"></Checkbox>
|
<Checkbox
|
||||||
|
:checked="isSelected(item)"
|
||||||
|
:value="item.id"
|
||||||
|
class="relative top--2px"
|
||||||
|
@change="toggleSelect(item)"
|
||||||
|
></Checkbox>
|
||||||
<div class="ml-8px flex-1">
|
<div class="ml-8px flex-1">
|
||||||
<Tooltip title="点击查看账号详情">
|
<Tooltip title="点击查看账号详情">
|
||||||
<p class="name cursor-pointer hover:!color-#6d4cfe" @click="goDetail(item)">{{ item.name || '-' }}</p>
|
<p class="name cursor-pointer hover:!color-#6d4cfe" @click="goDetail(item)">{{ item.name || '-' }}</p>
|
||||||
@ -47,30 +53,30 @@
|
|||||||
<span class="label">运营人员</span>
|
<span class="label">运营人员</span>
|
||||||
<span class="cts">{{ item.operator?.name || '-' }}</span>
|
<span class="cts">{{ item.operator?.name || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-row">
|
<!-- <div class="field-row">-->
|
||||||
<span class="label">所属项目</span>
|
<!-- <span class="label">所属项目</span>-->
|
||||||
<span v-if="!item.projects.length" class="cts">-</span>
|
<!-- <span v-if="!item.projects.length" class="cts">-</span>-->
|
||||||
<div v-else class="flex items-center">
|
<!-- <div v-else class="flex items-center">-->
|
||||||
<Tooltip
|
<!-- <Tooltip-->
|
||||||
v-if="item.projects.length > 2"
|
<!-- v-if="item.projects.length > 2"-->
|
||||||
placement="bottom"
|
<!-- placement="bottom"-->
|
||||||
:title="
|
<!-- :title="-->
|
||||||
item.projects
|
<!-- item.projects-->
|
||||||
.slice(2)
|
<!-- .slice(2)-->
|
||||||
.map((v) => v.name)
|
<!-- .map((v) => v.name)-->
|
||||||
.join(',')
|
<!-- .join(',')-->
|
||||||
"
|
<!-- "-->
|
||||||
>
|
<!-- >-->
|
||||||
<div class="tag-box">
|
<!-- <div class="tag-box">-->
|
||||||
<span class="text">{{ `+${item.projects.length - 2}` }}</span>
|
<!-- <span class="text">{{ `+${item.projects.length - 2}` }}</span>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</Tooltip>
|
<!-- </Tooltip>-->
|
||||||
|
|
||||||
<div v-for="(project, index) in item.projects.slice(0, 2)" :key="index" class="tag-box">
|
<!-- <div v-for="(project, index) in item.projects.slice(0, 2)" :key="index" class="tag-box">-->
|
||||||
<span class="text">{{ project.name }}</span>
|
<!-- <span class="text">{{ project.name }}</span>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
<div class="field-row">
|
<div class="field-row">
|
||||||
<span class="label">分组</span>
|
<span class="label">分组</span>
|
||||||
<span class="cts">{{ item.group?.name || '-' }}</span>
|
<span class="cts">{{ item.group?.name || '-' }}</span>
|
||||||
@ -123,14 +129,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Spin>
|
</Spin>
|
||||||
<PauseAccountPatchModal ref="pauseAccountPatchModalRef" @success="emits('update')" />
|
|
||||||
<ReauthorizeAccountModal ref="reauthorizeAccountModalRef" @update="emits('update')" />
|
|
||||||
<AuthorizedAccountModal ref="authorizedAccountModalRef" @update="emits('update')" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, ref, computed, inject } from 'vue';
|
import { defineProps, inject } from 'vue';
|
||||||
import { Checkbox, Button, Tooltip, Spin } from 'ant-design-vue';
|
import { Checkbox, Button, Tooltip, Spin } from 'ant-design-vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { deleteSyncStatus } from '@/api/all/propertyMarketing';
|
import { deleteSyncStatus } from '@/api/all/propertyMarketing';
|
||||||
@ -141,10 +144,7 @@ import {
|
|||||||
EnumStatus,
|
EnumStatus,
|
||||||
} from '@/views/property-marketing/media-account/components/status-select/status-box';
|
} from '@/views/property-marketing/media-account/components/status-select/status-box';
|
||||||
|
|
||||||
import PauseAccountPatchModal from './pause-account-patch';
|
|
||||||
import StatusBox from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
|
import StatusBox from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
|
||||||
import ReauthorizeAccountModal from '../reauthorize-account-modal';
|
|
||||||
import AuthorizedAccountModal from '../authorized-account-modal';
|
|
||||||
import FooterBtn from './footer-btn';
|
import FooterBtn from './footer-btn';
|
||||||
|
|
||||||
import icon1 from '@/assets/img/platform/icon-dy.png';
|
import icon1 from '@/assets/img/platform/icon-dy.png';
|
||||||
@ -160,7 +160,7 @@ const props = defineProps({
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
selectedItems: {
|
selectedRows: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
@ -170,28 +170,26 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete', 'updateSyncStatus']);
|
const emits = defineEmits([
|
||||||
|
'openEdit',
|
||||||
|
'update',
|
||||||
|
'delete',
|
||||||
|
'updateSyncStatus',
|
||||||
|
'pause',
|
||||||
|
'reauthorize',
|
||||||
|
'select',
|
||||||
|
'selectAll',
|
||||||
|
]);
|
||||||
const syncData = inject('handleSyncData');
|
const syncData = inject('handleSyncData');
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const pauseAccountPatchModalRef = ref(null);
|
|
||||||
const reauthorizeAccountModalRef = ref(null);
|
|
||||||
const authorizedAccountModalRef = ref(null);
|
|
||||||
|
|
||||||
// 判断当前 item 是否被选中
|
|
||||||
const isSelected = (item) => {
|
const isSelected = (item) => {
|
||||||
return props.selectedItems.some((i) => i.id === item.id);
|
return props.selectedRows.some((i) => i.id === item.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleSelect = (item) => {
|
const toggleSelect = (item) => {
|
||||||
let newSelected;
|
emits('select', item, !isSelected(item));
|
||||||
if (isSelected(item)) {
|
|
||||||
newSelected = props.selectedItems.filter((i) => i.id !== item.id);
|
|
||||||
} else {
|
|
||||||
newSelected = [...props.selectedItems, item];
|
|
||||||
}
|
|
||||||
emits('selectionChange', newSelected);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const openEdit = (item) => {
|
const openEdit = (item) => {
|
||||||
@ -203,21 +201,11 @@ const openDelete = (item) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleReauthorize = (item) => {
|
const handleReauthorize = (item) => {
|
||||||
const { id, platform, error_status } = item;
|
emits('reauthorize', item);
|
||||||
const isUnauthorized = isUnauthorizedStatus(error_status);
|
|
||||||
if (isUnauthorized) {
|
|
||||||
authorizedAccountModalRef.value?.open(id, platform);
|
|
||||||
} else {
|
|
||||||
reauthorizeAccountModalRef.value?.open(id, platform);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePause = (item) => {
|
const handlePause = (item) => {
|
||||||
pauseAccountPatchModalRef.value?.open(item);
|
emits('pause', item);
|
||||||
};
|
|
||||||
|
|
||||||
const isUnauthorizedStatus = (error_status) => {
|
|
||||||
return [EnumErrorStatus.UNAUTHORIZED].includes(error_status);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const goDetail = (item) => {
|
const goDetail = (item) => {
|
||||||
@ -269,6 +257,7 @@ const handleConfirm = (item) => {
|
|||||||
syncData(item);
|
syncData(item);
|
||||||
}
|
}
|
||||||
if (error_status === EnumErrorStatus.LOGIN) {
|
if (error_status === EnumErrorStatus.LOGIN) {
|
||||||
|
emits('');
|
||||||
handleReauthorize(item);
|
handleReauthorize(item);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,9 +3,8 @@ import {
|
|||||||
EnumErrorStatus,
|
EnumErrorStatus,
|
||||||
getStatusInfo,
|
getStatusInfo,
|
||||||
} from '@/views/property-marketing/media-account/components/status-select/status-box';
|
} from '@/views/property-marketing/media-account/components/status-select/status-box';
|
||||||
import { Dropdown, Menu } from 'ant-design-vue';
|
import { Dropdown, Menu, Tooltip, Button } from 'ant-design-vue';
|
||||||
const { Item: MenuItem } = Menu;
|
const { Item: MenuItem } = Menu;
|
||||||
import { Tooltip, Button } from 'ant-design-vue';
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'FooterBtn',
|
name: 'FooterBtn',
|
||||||
props: {
|
props: {
|
||||||
@ -44,7 +43,7 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
const renderUpdateBtn = () => {
|
const renderUpdateBtn = () => {
|
||||||
return (
|
return (
|
||||||
<Button type="primary" ghost size="small" onClick={() => emit('syncData', props.item)}>
|
<Button type="primary" class="!h-24px !px-12px" ghost size="small" onClick={() => emit('syncData', props.item)}>
|
||||||
更新数据
|
更新数据
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@ -63,7 +62,7 @@ export default defineComponent({
|
|||||||
trigger="hover"
|
trigger="hover"
|
||||||
v-slots={{
|
v-slots={{
|
||||||
default: () => (
|
default: () => (
|
||||||
<Button type="primary" ghost class="mr-8px" size="small">
|
<Button type="primary" ghost class="!h-24px !px-12px mr-8px" size="small">
|
||||||
更多
|
更多
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -88,7 +87,7 @@ export default defineComponent({
|
|||||||
trigger="hover"
|
trigger="hover"
|
||||||
v-slots={{
|
v-slots={{
|
||||||
default: () => (
|
default: () => (
|
||||||
<Button type="primary" ghost class="mr-8px" size="small">
|
<Button type="primary" ghost class="mr-8px !h-24px !px-12px" size="small">
|
||||||
更多
|
更多
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -100,7 +99,13 @@ export default defineComponent({
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
></Dropdown>
|
></Dropdown>
|
||||||
<Button type="primary" ghost size="small" onClick={() => emit('handleReauthorize', props.item)}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
size="small"
|
||||||
|
class="!h-24px !px-12px"
|
||||||
|
onClick={() => emit('handleReauthorize', props.item)}
|
||||||
|
>
|
||||||
开启同步
|
开启同步
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
@ -118,14 +123,20 @@ export default defineComponent({
|
|||||||
} else if ([EnumErrorStatus.REQUEST, EnumErrorStatus.FREEZE].includes(error_status)) {
|
} else if ([EnumErrorStatus.REQUEST, EnumErrorStatus.FREEZE].includes(error_status)) {
|
||||||
return (
|
return (
|
||||||
<Tooltip title={statusInfo.value.disabledBtnTooltip}>
|
<Tooltip title={statusInfo.value.disabledBtnTooltip}>
|
||||||
<Button type="primary" ghost size="small" disabled>
|
<Button type="primary" ghost size="small" disabled class="!h-24px !px-12px">
|
||||||
重新授权
|
重新授权
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Button type="primary" ghost size="small" onClick={() => emit('handleReauthorize', props.item)}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
class="!h-24px !px-12px"
|
||||||
|
size="small"
|
||||||
|
onClick={() => emit('handleReauthorize', props.item)}
|
||||||
|
>
|
||||||
{isUnauthorized ? '去授权' : '重新授权'}
|
{isUnauthorized ? '去授权' : '重新授权'}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@ -137,7 +148,7 @@ export default defineComponent({
|
|||||||
trigger="hover"
|
trigger="hover"
|
||||||
v-slots={{
|
v-slots={{
|
||||||
default: () => (
|
default: () => (
|
||||||
<Button type="primary" ghost class="mr-8px" size="small">
|
<Button type="primary" ghost size="small" class="mr-8px !h-24px !px-12px">
|
||||||
更多
|
更多
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
<Table
|
||||||
|
ref="tableRef"
|
||||||
|
:dataSource="dataSource"
|
||||||
|
:pagination="false"
|
||||||
|
:rowSelection="rowSelection"
|
||||||
|
:scroll="{ x: '100%' }"
|
||||||
|
:showSorterTooltip="false"
|
||||||
|
bordered
|
||||||
|
class="flex-1 manuscript-table w-100%"
|
||||||
|
rowKey="id"
|
||||||
|
>
|
||||||
|
<template #emptyText>
|
||||||
|
<NoData text="暂无文件" />
|
||||||
|
</template>
|
||||||
|
<Column
|
||||||
|
v-for="column in tableColumns"
|
||||||
|
:key="column.dataIndex"
|
||||||
|
:align="column.align"
|
||||||
|
:dataIndex="column.dataIndex"
|
||||||
|
:ellipsis="true"
|
||||||
|
:fixed="column.fixed"
|
||||||
|
:minWidth="column.minWidth"
|
||||||
|
:sorter="column.sortable"
|
||||||
|
:width="column.width"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span class="cts mr-4px">{{ column.title }}</span>
|
||||||
|
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
|
||||||
|
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
|
||||||
|
</Tooltip>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'name'" #customRender="{ record }">
|
||||||
|
<TextOverTips
|
||||||
|
:context="record.name || '-'"
|
||||||
|
:line="1"
|
||||||
|
class="name cursor-pointer hover:!color-#6d4cfe"
|
||||||
|
@click="goDetail(record)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'tags'" #customRender="{ record }">
|
||||||
|
<div v-if="record.tags.length > 0" class="flex flex-wrap gap-4px">
|
||||||
|
<Tag v-for="tag in record.tags" :key="tag.id" class="mr-0 rounded-4px bg-#F2F3F5 px-8px">
|
||||||
|
<Tooltip v-if="tag.name.length > 5" :title="tag.name">
|
||||||
|
<span class="cts !color-#55585F !lh-24px !text-14px"> {{ `${tag.name.slice(0, 5)}...` }} </span>
|
||||||
|
</Tooltip>
|
||||||
|
<span v-else class="cts !color-#55585F !lh-24px !text-14px"> {{ tag.name }} </span>
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
<template v-else> -</template>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'status'" #customRender="{ record }">
|
||||||
|
<StatusBox :item="record" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'platform'" #customRender="{ record }">
|
||||||
|
<img :src="record.platform === 0 ? icon2 : icon3" height="16" width="16" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'last_synced_at'" #customRender="{ record }">
|
||||||
|
<span class="cts num">{{ getLastSyncedAt(record) }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'last_authorized_at'" #customRender="{ record }">
|
||||||
|
<span class="cts num">{{ formatTime(record.last_authorized_at) }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<FooterBtn
|
||||||
|
:item="record"
|
||||||
|
@handlePause="handlePause"
|
||||||
|
@handleReauthorize="handleReauthorize"
|
||||||
|
@openDelete="openDelete"
|
||||||
|
@openEdit="openEdit"
|
||||||
|
@syncData="syncData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else #customRender="{ record }">
|
||||||
|
{{ formatTableField(column, record, true) }}
|
||||||
|
</template>
|
||||||
|
</Column>
|
||||||
|
</Table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, inject } from 'vue';
|
||||||
|
import { Tooltip, Table, Tag } from 'ant-design-vue';
|
||||||
|
import StatusBox from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
|
||||||
|
|
||||||
|
const { Column } = Table;
|
||||||
|
|
||||||
|
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
||||||
|
|
||||||
|
import TextOverTips from '@/components/text-over-tips';
|
||||||
|
import FooterBtn from './footer-btn';
|
||||||
|
|
||||||
|
// import icon1 from '@/assets/img/media-account/icon-delete.png';
|
||||||
|
import icon2 from '@/assets/img/platform/icon-dy.png';
|
||||||
|
import icon3 from '@/assets/img/platform/icon-xhs.png';
|
||||||
|
|
||||||
|
const emits = defineEmits([
|
||||||
|
'openEdit',
|
||||||
|
'update',
|
||||||
|
'selectionChange',
|
||||||
|
'delete',
|
||||||
|
'updateSyncStatus',
|
||||||
|
'pause',
|
||||||
|
'reauthorize',
|
||||||
|
'select',
|
||||||
|
'selectAll',
|
||||||
|
]);
|
||||||
|
const syncData = inject('handleSyncData');
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dataSource: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
tableColumns: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
selectedRowKeys: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
syncMediaAccounts: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
isLoadingTaskStatus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableRef = ref(null);
|
||||||
|
|
||||||
|
const goDetail = (item) => {
|
||||||
|
router.push(`/media-account/detail/${item.id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePause = (item) => {
|
||||||
|
emits('pause', item);
|
||||||
|
};
|
||||||
|
const handleReauthorize = (item) => {
|
||||||
|
emits('reauthorize', item);
|
||||||
|
};
|
||||||
|
const openEdit = (item) => {
|
||||||
|
emits('openEdit', item);
|
||||||
|
};
|
||||||
|
const openDelete = (item) => {
|
||||||
|
emits('delete', item);
|
||||||
|
};
|
||||||
|
const formatTime = (time) => {
|
||||||
|
return exactFormatTime(time, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSyncMediaAccount = (item) => {
|
||||||
|
return props.syncMediaAccounts.find((v) => v.id === item.id);
|
||||||
|
};
|
||||||
|
const getLastSyncedAt = (item) => {
|
||||||
|
const target = getSyncMediaAccount(item);
|
||||||
|
if (props.isLoadingTaskStatus && target) {
|
||||||
|
if (target?.status !== 0) {
|
||||||
|
return formatTime(target.last_synced_at);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatTime(item.last_synced_at);
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
selectedRowKeys: computed(() => props.selectedRowKeys),
|
||||||
|
onSelect: (record, selected) => {
|
||||||
|
emits('select', record, selected);
|
||||||
|
},
|
||||||
|
onSelectAll: (selected) => {
|
||||||
|
emits('selectAll', selected);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './style.scss';
|
||||||
|
</style>
|
||||||
@ -41,3 +41,63 @@ export const SHOW_TYPES = [
|
|||||||
svgName: 'svg-list',
|
svgName: 'svg-list',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const TABLE_COLUMNS = [
|
||||||
|
{
|
||||||
|
title: '账号名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 200,
|
||||||
|
fixed: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '数据更新时间',
|
||||||
|
dataIndex: 'last_synced_at',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '最后授权时间',
|
||||||
|
dataIndex: 'last_authorized_at',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '平台',
|
||||||
|
dataIndex: 'platform',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '账号ID',
|
||||||
|
dataIndex: 'account_id',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号码',
|
||||||
|
dataIndex: 'mobile',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '运营人员',
|
||||||
|
dataIndex: 'operator.name',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分组',
|
||||||
|
dataIndex: 'group.name',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标签',
|
||||||
|
dataIndex: 'tags',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
width: 180,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
@ -55,12 +55,12 @@
|
|||||||
:checked="checkedAll"
|
:checked="checkedAll"
|
||||||
:indeterminate="indeterminate"
|
:indeterminate="indeterminate"
|
||||||
class="mr-24px"
|
class="mr-24px"
|
||||||
@change="(e) => handleChangeAll(e.target.checked)"
|
@change="(e) => handleSelectAll(e.target.checked)"
|
||||||
>
|
>
|
||||||
全选
|
全选
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
|
||||||
<template v-if="selectedItems.length">
|
<template v-if="selectedRows.length">
|
||||||
<Button :disabled="isDisabledBatchSyncData" class="!h-24px !px-12px" type="text" @click="handleBatchSyncData">
|
<Button :disabled="isDisabledBatchSyncData" class="!h-24px !px-12px" type="text" @click="handleBatchSyncData">
|
||||||
批量更新数据
|
批量更新数据
|
||||||
</Button>
|
</Button>
|
||||||
@ -81,21 +81,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-wrap">
|
<div class="card-wrap">
|
||||||
<template v-if="showType === 'card'">
|
<component
|
||||||
<AccountTable
|
:is="showType === 'card' ? Card : Table"
|
||||||
v-if="dataSource.length > 0"
|
|
||||||
:dataSource="dataSource"
|
:dataSource="dataSource"
|
||||||
:isLoadingTaskStatus="isLoadingTaskStatus"
|
:isLoadingTaskStatus="isLoadingTaskStatus"
|
||||||
:selectedItems="selectedItems"
|
:selectedItems="selectedItems"
|
||||||
|
:selectedRowKeys="selectedRowKeys"
|
||||||
|
:selectedRows="selectedRows"
|
||||||
:syncMediaAccounts="syncMediaAccounts"
|
:syncMediaAccounts="syncMediaAccounts"
|
||||||
|
:tableColumns="TABLE_COLUMNS"
|
||||||
@delete="handleDelete"
|
@delete="handleDelete"
|
||||||
@openEdit="handleOpenEdit"
|
@openEdit="handleOpenEdit"
|
||||||
@selectionChange="handleSelectionChange"
|
@pause="handlePause"
|
||||||
|
@reauthorize="handleReauthorize"
|
||||||
|
@select="handleSelect"
|
||||||
|
@selectAll="handleSelectAll"
|
||||||
@update="getData"
|
@update="getData"
|
||||||
@updateSyncStatus="handleUpdateSyncStatus"
|
@updateSyncStatus="handleUpdateSyncStatus"
|
||||||
/>
|
/>
|
||||||
<NoData v-else />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div v-if="pageInfo.total > 0" class="pagination-row">
|
<div v-if="pageInfo.total > 0" class="pagination-row">
|
||||||
<Pagination
|
<Pagination
|
||||||
@ -105,7 +108,7 @@
|
|||||||
showSizeChanger
|
showSizeChanger
|
||||||
showQuickJumper
|
showQuickJumper
|
||||||
:current="pageInfo.page"
|
:current="pageInfo.page"
|
||||||
:pageSize="pageInfo.pageSize"
|
:pageSize="pageInfo.page_size"
|
||||||
:pageSizeOptions="['8', '16', '20', '32', '64']"
|
:pageSizeOptions="['8', '16', '20', '32', '64']"
|
||||||
@change="onPageChange"
|
@change="onPageChange"
|
||||||
/>
|
/>
|
||||||
@ -119,6 +122,10 @@
|
|||||||
<DeleteAccountModal ref="deleteAccountRef" @update="getData" @batchUpdate="onBatchSuccess" />
|
<DeleteAccountModal ref="deleteAccountRef" @update="getData" @batchUpdate="onBatchSuccess" />
|
||||||
<BatchTagModal ref="batchTagModalRef" @update="onBatchSuccess" />
|
<BatchTagModal ref="batchTagModalRef" @update="onBatchSuccess" />
|
||||||
<BatchGroupModal ref="batchGroupModalRef" @update="onBatchSuccess" />
|
<BatchGroupModal ref="batchGroupModalRef" @update="onBatchSuccess" />
|
||||||
|
|
||||||
|
<PauseAccountPatchModal ref="pauseAccountPatchModalRef" @success="getData" />
|
||||||
|
<ReauthorizeAccountModal ref="reauthorizeAccountModalRef" @update="getData" />
|
||||||
|
<AuthorizedAccountModal ref="authorizedAccountModalRef" @update="getData" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -127,7 +134,12 @@ import { ref, provide } from 'vue';
|
|||||||
import { Checkbox, Button, Pagination, notification, RadioButton, RadioGroup } from 'ant-design-vue';
|
import { Checkbox, Button, Pagination, notification, RadioButton, RadioGroup } from 'ant-design-vue';
|
||||||
|
|
||||||
import FilterBlock from './components/filter-block';
|
import FilterBlock from './components/filter-block';
|
||||||
import AccountTable from './components/account-table/card.vue';
|
import Card from './components/account-table/card.vue';
|
||||||
|
import Table from './components/account-table/table.vue';
|
||||||
|
import PauseAccountPatchModal from './components/account-table/pause-account-patch.vue';
|
||||||
|
import AuthorizedAccountModal from './components/authorized-account-modal';
|
||||||
|
import ReauthorizeAccountModal from './components/reauthorize-account-modal';
|
||||||
|
|
||||||
import GroupManageModal from './components/group-manage-modal';
|
import GroupManageModal from './components/group-manage-modal';
|
||||||
import TagsManageModal from './components/tags-manage-modal';
|
import TagsManageModal from './components/tags-manage-modal';
|
||||||
import AddAccountModal from './components/add-account-modal';
|
import AddAccountModal from './components/add-account-modal';
|
||||||
@ -135,10 +147,14 @@ import DeleteAccountModal from './components/account-table/delete-account';
|
|||||||
import BatchTagModal from './components/batch-tag-modal';
|
import BatchTagModal from './components/batch-tag-modal';
|
||||||
import BatchGroupModal from './components/batch-group-modal';
|
import BatchGroupModal from './components/batch-group-modal';
|
||||||
|
|
||||||
import { INITIAL_QUERY, INITIAL_PAGE_INFO, SHOW_TYPES } from './constants';
|
import { INITIAL_QUERY, SHOW_TYPES, TABLE_COLUMNS } from './constants';
|
||||||
import { showImportResultNotification } from '@/utils/notification';
|
import { showImportResultNotification } from '@/utils/notification';
|
||||||
import { getTaskStatus } from '@/api/all/common';
|
import { getTaskStatus } from '@/api/all/common';
|
||||||
import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
|
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
|
||||||
|
import {
|
||||||
|
EnumErrorStatus,
|
||||||
|
EnumStatus,
|
||||||
|
} from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
|
||||||
import {
|
import {
|
||||||
getMediaAccounts,
|
getMediaAccounts,
|
||||||
getMediaAccountsHealth,
|
getMediaAccountsHealth,
|
||||||
@ -156,6 +172,21 @@ import icon5 from '@/assets/img/media-account/icon-warn.png';
|
|||||||
let syncDataTimer = null;
|
let syncDataTimer = null;
|
||||||
let queryTaskTimer = null;
|
let queryTaskTimer = null;
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectedRowKeys,
|
||||||
|
selectedRows,
|
||||||
|
dataSource,
|
||||||
|
pageInfo,
|
||||||
|
DEFAULT_PAGE_INFO,
|
||||||
|
onPageChange,
|
||||||
|
handleSelect,
|
||||||
|
handleSelectAll,
|
||||||
|
} = useTableSelectionWithPagination({
|
||||||
|
onPageChange: () => {
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const groupManageModalRef = ref(null);
|
const groupManageModalRef = ref(null);
|
||||||
const tagsManageModalRef = ref(null);
|
const tagsManageModalRef = ref(null);
|
||||||
const addAccountModalRef = ref(null);
|
const addAccountModalRef = ref(null);
|
||||||
@ -163,10 +194,12 @@ const deleteAccountRef = ref(null);
|
|||||||
const batchTagModalRef = ref(null);
|
const batchTagModalRef = ref(null);
|
||||||
const batchGroupModalRef = ref(null);
|
const batchGroupModalRef = ref(null);
|
||||||
const filterBlockRef = ref(null);
|
const filterBlockRef = ref(null);
|
||||||
|
const pauseAccountPatchModalRef = ref(null);
|
||||||
|
const reauthorizeAccountModalRef = ref(null);
|
||||||
|
const authorizedAccountModalRef = ref(null);
|
||||||
|
|
||||||
const pageInfo = ref(cloneDeep(INITIAL_PAGE_INFO));
|
|
||||||
const query = ref(cloneDeep(INITIAL_QUERY));
|
const query = ref(cloneDeep(INITIAL_QUERY));
|
||||||
const dataSource = ref([]);
|
// const dataSource = ref([]);
|
||||||
const selectedItems = ref([]);
|
const selectedItems = ref([]);
|
||||||
const healthData = ref({});
|
const healthData = ref({});
|
||||||
const syncMediaAccounts = ref([]);
|
const syncMediaAccounts = ref([]);
|
||||||
@ -174,13 +207,11 @@ const isLoadingTaskStatus = ref(false); // 正在查询状态中
|
|||||||
const showType = ref('card');
|
const showType = ref('card');
|
||||||
|
|
||||||
const hasAbNormalStatus = computed(() => healthData.value?.abnormal_number > 0);
|
const hasAbNormalStatus = computed(() => healthData.value?.abnormal_number > 0);
|
||||||
const isDisabledBatchSyncData = computed(() => selectedItems.value.some((item) => item.status !== EnumStatus.NORMAL));
|
const isDisabledBatchSyncData = computed(() => selectedRows.value.some((item) => item.status !== EnumStatus.NORMAL));
|
||||||
|
|
||||||
const checkedAll = computed(
|
const checkedAll = computed(() => dataSource.value.length > 0 && selectedRows.value.length === dataSource.value.length);
|
||||||
() => dataSource.value.length > 0 && selectedItems.value.length === dataSource.value.length,
|
|
||||||
);
|
|
||||||
const indeterminate = computed(
|
const indeterminate = computed(
|
||||||
() => selectedItems.value.length > 0 && selectedItems.value.length < dataSource.value.length,
|
() => selectedRows.value.length > 0 && selectedRows.value.length < dataSource.value.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
const tipLabel = computed(() => {
|
const tipLabel = computed(() => {
|
||||||
@ -223,10 +254,10 @@ const getHealthData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const getAccountData = async () => {
|
const getAccountData = async () => {
|
||||||
const { page, pageSize } = pageInfo.value;
|
const { page, page_size } = pageInfo.value;
|
||||||
const { code, data } = await getMediaAccounts({
|
const { code, data } = await getMediaAccounts({
|
||||||
page,
|
page,
|
||||||
page_size: pageSize,
|
page_size,
|
||||||
...query.value,
|
...query.value,
|
||||||
});
|
});
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
@ -242,23 +273,13 @@ const handleSearch = () => {
|
|||||||
reload();
|
reload();
|
||||||
};
|
};
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
pageInfo.value = cloneDeep(INITIAL_PAGE_INFO);
|
pageInfo.value = cloneDeep(DEFAULT_PAGE_INFO);
|
||||||
selectedItems.value = [];
|
selectedRows.value = [];
|
||||||
|
selectedRowKeys.value = [];
|
||||||
query.value = cloneDeep(INITIAL_QUERY);
|
query.value = cloneDeep(INITIAL_QUERY);
|
||||||
reload();
|
reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPageChange = (current, pageSize) => {
|
|
||||||
pageInfo.value.page = current;
|
|
||||||
pageInfo.value.pageSize = pageSize;
|
|
||||||
|
|
||||||
getData();
|
|
||||||
};
|
|
||||||
const onPageSizeChange = (pageSize) => {
|
|
||||||
pageInfo.value.pageSize = pageSize;
|
|
||||||
reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenGroupModal = () => {
|
const handleOpenGroupModal = () => {
|
||||||
groupManageModalRef.value?.open();
|
groupManageModalRef.value?.open();
|
||||||
};
|
};
|
||||||
@ -273,15 +294,9 @@ const handleOpenEdit = (item) => {
|
|||||||
addAccountModalRef.value?.open(item.id);
|
addAccountModalRef.value?.open(item.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectionChange = (val) => {
|
|
||||||
selectedItems.value = val;
|
|
||||||
};
|
|
||||||
const handleChangeAll = (checked) => {
|
|
||||||
selectedItems.value = checked ? cloneDeep(dataSource.value) : [];
|
|
||||||
};
|
|
||||||
const handleBatchDelete = () => {
|
const handleBatchDelete = () => {
|
||||||
const ids = selectedItems.value.map((item) => item.id);
|
const ids = selectedRows.value.map((item) => item.id);
|
||||||
const names = selectedItems.value.map((item) => `"${item.name || '-'}"`).join(',');
|
const names = selectedRows.value.map((item) => `"${item.name || '-'}"`).join(',');
|
||||||
deleteAccountRef.value?.open({ id: ids, name: names });
|
deleteAccountRef.value?.open({ id: ids, name: names });
|
||||||
};
|
};
|
||||||
const handleDelete = (item) => {
|
const handleDelete = (item) => {
|
||||||
@ -330,14 +345,14 @@ const handleSyncData = async (item) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleBatchTag = () => {
|
const handleBatchTag = () => {
|
||||||
batchTagModalRef.value?.open(selectedItems.value);
|
batchTagModalRef.value?.open(selectedRows.value);
|
||||||
};
|
};
|
||||||
const handleBatchSyncData = async () => {
|
const handleBatchSyncData = async () => {
|
||||||
if (isDisabledBatchSyncData.value) {
|
if (isDisabledBatchSyncData.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = selectedItems.value.map((item) => item.id);
|
const ids = selectedRows.value.map((item) => item.id);
|
||||||
const { code } = await postBatchSyncMediaAccountData({ ids });
|
const { code } = await postBatchSyncMediaAccountData({ ids });
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
if (!isLoadingTaskStatus.value) {
|
if (!isLoadingTaskStatus.value) {
|
||||||
@ -346,10 +361,11 @@ const handleBatchSyncData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleBatchGroup = () => {
|
const handleBatchGroup = () => {
|
||||||
batchGroupModalRef.value?.open(selectedItems.value);
|
batchGroupModalRef.value?.open(selectedRows.value);
|
||||||
};
|
};
|
||||||
const onBatchSuccess = () => {
|
const onBatchSuccess = () => {
|
||||||
selectedItems.value = [];
|
selectedRowKeys.value = [];
|
||||||
|
selectedRows.value = [];
|
||||||
getData();
|
getData();
|
||||||
};
|
};
|
||||||
const handleOpenAbnormalAccount = () => {
|
const handleOpenAbnormalAccount = () => {
|
||||||
@ -357,6 +373,23 @@ const handleOpenAbnormalAccount = () => {
|
|||||||
reload();
|
reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isUnauthorizedStatus = (error_status) => {
|
||||||
|
return [EnumErrorStatus.UNAUTHORIZED].includes(error_status);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePause = (item) => {
|
||||||
|
pauseAccountPatchModalRef.value?.open(item);
|
||||||
|
};
|
||||||
|
const handleReauthorize = (item) => {
|
||||||
|
const { id, platform, error_status } = item;
|
||||||
|
const isUnauthorized = isUnauthorizedStatus(error_status);
|
||||||
|
if (isUnauthorized) {
|
||||||
|
authorizedAccountModalRef.value?.open(id, platform);
|
||||||
|
} else {
|
||||||
|
reauthorizeAccountModalRef.value?.open(id, platform);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 查询导入账号任务状态
|
// 查询导入账号任务状态
|
||||||
const getSyncTaskStatus = async (id, notificationId) => {
|
const getSyncTaskStatus = async (id, notificationId) => {
|
||||||
const { code, data } = await getTaskStatus(id);
|
const { code, data } = await getTaskStatus(id);
|
||||||
|
|||||||
Reference in New Issue
Block a user