feat: 账号健康状态、统一组件逻辑
This commit is contained in:
@ -2,152 +2,177 @@
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-28 10:33:06
|
||||
*/
|
||||
export const TABLE_COLUMNS = [
|
||||
{
|
||||
title: '账号名称',
|
||||
dataIndex: 'name',
|
||||
width: 180,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '项目分组',
|
||||
dataIndex: 'group.name',
|
||||
width: 180,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 180,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '运营人员',
|
||||
dataIndex: 'operator.name',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: 'AI评价',
|
||||
dataIndex: 'ai_evaluation',
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
title: '粉丝量',
|
||||
dataIndex: 'fans_number',
|
||||
width: 180,
|
||||
tooltip: '账号当前粉丝总数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
export const getDefaultColumns = (type = 'week') => {
|
||||
const isWeek = type === 'week';
|
||||
const viewChain = isWeek ? 'week_view_chain' : 'month_view_chain';
|
||||
const likeChain = isWeek ? 'week_like_chain' : 'month_like_chain';
|
||||
const viewChainText = isWeek ? '近7天观看量环比' : '近30天观看量环比';
|
||||
const likeChainText = isWeek ? '近7天点赞量环比' : '近30天点赞量环比';
|
||||
|
||||
return [
|
||||
{
|
||||
title: '账号名称',
|
||||
dataIndex: 'name',
|
||||
prop: 'name',
|
||||
width: 180,
|
||||
fixed: 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '总赞藏数',
|
||||
dataIndex: 'like_collect_number',
|
||||
width: 180,
|
||||
tooltip: '账号所有内容的点赞和收藏总数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: '项目分组',
|
||||
dataIndex: 'group.name',
|
||||
prop: 'group',
|
||||
width: 180,
|
||||
fixed: 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '观看量',
|
||||
dataIndex: 'view_number',
|
||||
width: 180,
|
||||
tooltip: '账号所有内容的总观看次数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
prop: 'status',
|
||||
width: 180,
|
||||
fixed: 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '观看量环比',
|
||||
dataIndex: 'view_chain',
|
||||
width: 180,
|
||||
tooltip: '相比上一周期的观看量变化百分比',
|
||||
align: 'right',
|
||||
suffix: '%',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: '运营人员',
|
||||
dataIndex: 'operator.name',
|
||||
prop: 'operator',
|
||||
width: 180,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '点赞量',
|
||||
dataIndex: 'like_number',
|
||||
width: 180,
|
||||
tooltip: '账号所有内容的总点赞数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: 'AI评价',
|
||||
dataIndex: 'ai_evaluate',
|
||||
prop: 'ai_evaluate',
|
||||
width: 260,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '点赞量环比',
|
||||
dataIndex: 'like_chain',
|
||||
width: 180,
|
||||
tooltip: '相比上一周期的点赞量变化百分比',
|
||||
align: 'right',
|
||||
suffix: '%',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: '粉丝量',
|
||||
dataIndex: 'fans_number',
|
||||
prop: 'fans_number',
|
||||
width: 180,
|
||||
tooltip: '账号当前粉丝总数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '最新内容标题/日期',
|
||||
dataIndex: 'like_chain1',
|
||||
width: 240,
|
||||
tooltip: '最新发布内容的标题和发布日期',
|
||||
},
|
||||
{
|
||||
title: '最新作品观看数',
|
||||
dataIndex: 'latest_view_number',
|
||||
width: 180,
|
||||
tooltip: '最新发布内容的观看次数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: '总赞藏数',
|
||||
dataIndex: 'like_collect_number',
|
||||
prop: 'like_collect_number',
|
||||
width: 180,
|
||||
tooltip: '账号所有内容的点赞和收藏总数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '最新作品日增长',
|
||||
dataIndex: 'latest_daily_growth',
|
||||
width: 180,
|
||||
tooltip: '最新作品每日观看量的增长情况',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: '观看量',
|
||||
dataIndex: 'view_number',
|
||||
prop: 'view_number',
|
||||
width: 180,
|
||||
tooltip: '账号所有内容的总观看次数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '次新内容标题/日期',
|
||||
dataIndex: 'like_chain4',
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
title: '次新作品观看数',
|
||||
dataIndex: 'second_latest_view_number',
|
||||
width: 180,
|
||||
tooltip: '倒数第二个发布内容的观看次数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: viewChainText,
|
||||
dataIndex: viewChain,
|
||||
prop: viewChain,
|
||||
width: 180,
|
||||
tooltip: '相比上一周期的观看量变化百分比',
|
||||
align: 'right',
|
||||
suffix: '%',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '次新作品日增长',
|
||||
dataIndex: 'second_latest_daily_growth',
|
||||
width: 180,
|
||||
tooltip: '倒数第二个作品每日观看量的增长情况',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
{
|
||||
title: '点赞量',
|
||||
dataIndex: 'like_number',
|
||||
prop: 'like_number',
|
||||
width: 180,
|
||||
tooltip: '账号所有内容的总点赞数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
},
|
||||
];
|
||||
{
|
||||
title: likeChainText,
|
||||
dataIndex: likeChain,
|
||||
prop: likeChain,
|
||||
width: 180,
|
||||
tooltip: '相比上一周期的点赞量变化百分比',
|
||||
align: 'right',
|
||||
suffix: '%',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '最新内容标题/日期',
|
||||
dataIndex: 'newest_work_title_and_publish_time',
|
||||
prop: 'newest_work_title_and_publish_time',
|
||||
width: 240,
|
||||
tooltip: '最新发布内容的标题和发布日期',
|
||||
},
|
||||
{
|
||||
title: '最新作品观看数',
|
||||
dataIndex: 'newest_work_view_number',
|
||||
prop: 'newest_work_view_number',
|
||||
width: 180,
|
||||
tooltip: '最新发布内容的观看次数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '最新作品日增长',
|
||||
dataIndex: 'newest_work_view_grow_number',
|
||||
prop: 'newest_work_view_grow_number',
|
||||
width: 180,
|
||||
tooltip: '最新作品每日观看量的增长情况',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '次新内容标题/日期',
|
||||
dataIndex: 'second_new_work_title_and_publish_time',
|
||||
prop: 'second_new_work_title_and_publish_time',
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
title: '次新作品观看数',
|
||||
dataIndex: 'second_new_work_view_number',
|
||||
prop: 'second_new_work_view_number',
|
||||
width: 180,
|
||||
tooltip: '倒数第二个发布内容的观看次数',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '次新作品日增长',
|
||||
dataIndex: 'second_new_work_view_grow_number',
|
||||
prop: 'second_new_work_view_grow_number',
|
||||
width: 180,
|
||||
tooltip: '倒数第二个作品每日观看量的增长情况',
|
||||
align: 'right',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
</template>
|
||||
<template #columns>
|
||||
<a-table-column
|
||||
v-for="column in TABLE_COLUMNS"
|
||||
v-for="column in tableColumns"
|
||||
:key="column.dataIndex"
|
||||
:data-index="column.dataIndex"
|
||||
:fixed="column.fixed"
|
||||
@ -60,7 +60,7 @@
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<img v-if="column.dataIndex === 'ai_evaluation'" width="16" height="16" :src="icon5" class="mr-4px" />
|
||||
<img v-if="column.dataIndex === 'ai_evaluate'" width="16" height="16" :src="icon5" class="mr-4px" />
|
||||
<span class="cts mr-4px">{{ column.title }}</span>
|
||||
<a-tooltip v-if="column.tooltip" :content="column.tooltip" position="top">
|
||||
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
|
||||
@ -78,18 +78,18 @@
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'ai_evaluation'" #cell="{ record }">
|
||||
<template v-else-if="column.dataIndex === 'ai_evaluate'" #cell="{ record }">
|
||||
<div class="ai-evaluation-row flex">
|
||||
<img
|
||||
width="16"
|
||||
height="16"
|
||||
:src="record.ai_evaluation?.status === 1 ? icon4 : record.ai_evaluation?.status === 2 ? icon3 : icon2"
|
||||
:src="record.ai_evaluate?.status === 1 ? icon4 : record.ai_evaluate?.status === 2 ? icon3 : icon2"
|
||||
class="mr-8px icon"
|
||||
/>
|
||||
<div>
|
||||
<p class="cts">{{ record.ai_evaluation?.text }}</p>
|
||||
<p class="cts">{{ record.ai_evaluate?.advise }}</p>
|
||||
<p class="cts text-12px lh-20px !color-#939499">
|
||||
{{ `观看: ${record.ai_evaluation?.look_chain}% 点赞: ${record.ai_evaluation?.like_chain}%` }}
|
||||
{{ `观看: ${record.week_view_chain}% 点赞: ${record.week_like_chain}%` }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -101,14 +101,24 @@
|
||||
<a-button type="outline" size="small" class="search-btn" @click="handleDetail(record)">详情</a-button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="['view_chain', 'like_chain'].includes(column.dataIndex)" #cell="{ record }">
|
||||
<template
|
||||
v-else-if="
|
||||
['week_view_chain', 'month_view_chain', 'week_like_chain', 'month_like_chain'].includes(column.dataIndex)
|
||||
"
|
||||
#cell="{ record }"
|
||||
>
|
||||
<div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'">
|
||||
<icon-arrow-up v-if="record[column.dataIndex] > 0" size="16" />
|
||||
<icon-arrow-down v-else size="16" />
|
||||
{{ formatTableField(column, record) }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="['like_chain1', 'like_chain4'].includes(column.dataIndex)" #cell="{ record }">
|
||||
<template
|
||||
v-else-if="
|
||||
['newest_work_title_and_publish_time', 'second_new_work_title_and_publish_time'].includes(column.dataIndex)
|
||||
"
|
||||
#cell="{ record }"
|
||||
>
|
||||
<p class="cts cursor-pointer hover:!color-#6D4CFE">打工人的环游世界旅行计划(国内版)</p>
|
||||
<p class="cts text-12px lh-20px !color-#939499">2025-06-18</p>
|
||||
</template>
|
||||
@ -120,16 +130,17 @@
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<CustomTableColumnModal ref="customTableColumnModalRef" type="media_account" @success="onCustomColumnSuccess" />
|
||||
<CustomTableColumnModal ref="customTableColumnModalRef" :type="CUSTOM_COLUMN_TYPE" @success="onCustomColumnSuccess" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { STATUS_LIST } from '../../constants';
|
||||
import { getCustomColumns } from '@/api/all/common';
|
||||
import { STATUS_LIST } from '@/views/property-marketing/media-account/components/status-select/constants';
|
||||
import { formatTableField, formatNumberShow } from '@/utils/tools';
|
||||
import { TABLE_COLUMNS } from './constants';
|
||||
import { getDefaultColumns } from './constants';
|
||||
import CustomTableColumnModal from '@/components/custom-table-column-modal';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-custom.png';
|
||||
@ -147,11 +158,14 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['selectionChange', 'sorterChange', 'export']);
|
||||
|
||||
const CUSTOM_COLUMN_TYPE = 'media_account';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const selectedItems = ref([]);
|
||||
const tableRef = ref(null);
|
||||
const customTableColumnModalRef = ref(null);
|
||||
const selectedColumns = ref([]);
|
||||
|
||||
const checkedAll = computed(
|
||||
() => selectedItems.value.length > 0 && selectedItems.value.length === props.dataSource.length,
|
||||
@ -165,6 +179,18 @@ const rowSelection = computed(() => ({
|
||||
showCheckedAll: true,
|
||||
}));
|
||||
|
||||
const tableColumns = computed(() => {
|
||||
const _result = [];
|
||||
const _columns = getDefaultColumns();
|
||||
selectedColumns.value.forEach((item) => {
|
||||
const _column = _columns.find((_item) => _item.prop === item);
|
||||
if (_column) {
|
||||
_result.push(_column);
|
||||
}
|
||||
});
|
||||
return _result;
|
||||
});
|
||||
|
||||
const handleSelectAll = (checked) => {
|
||||
if (checked) {
|
||||
selectedItems.value = props.dataSource.map((item) => item.id);
|
||||
@ -201,13 +227,26 @@ const openCustomColumn = () => {
|
||||
customTableColumnModalRef.value.open();
|
||||
};
|
||||
|
||||
const onCustomColumnSuccess = (selectedColumns) => {
|
||||
console.log(selectedColumns);
|
||||
const onCustomColumnSuccess = (columns) => {
|
||||
selectedColumns.value = columns;
|
||||
};
|
||||
|
||||
const getSelectedColumns = () => {
|
||||
getCustomColumns({ type: CUSTOM_COLUMN_TYPE }).then((res) => {
|
||||
const { code, data } = res;
|
||||
if (code === 200) {
|
||||
selectedColumns.value = data.selected_columns;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
resetTable,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getSelectedColumns();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -19,27 +19,19 @@
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">分组</span>
|
||||
<a-space class="w-200px">
|
||||
<group-select v-model="query.group_ids" multiple :options="groups" @change="handleSearch" />
|
||||
<GroupSelect v-model="query.group_ids" multiple :options="groups" @change="handleSearch" />
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">状态</span>
|
||||
<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.text">{{
|
||||
item.text
|
||||
}}</a-option>
|
||||
</a-select>
|
||||
<StatusSelect v-model="query.status" @change="handleSearch" />
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">运营人员</span>
|
||||
<a-space class="w-160px">
|
||||
<a-select v-model="query.operator_id" size="medium" placeholder="全部" allow-clear @change="handleSearch">
|
||||
<a-option v-for="(item, index) in operators" :key="index" :value="item.id" :label="item.name">{{
|
||||
item.name
|
||||
}}</a-option>
|
||||
</a-select>
|
||||
<OperatorSelect v-model="query.operator_id" :options="operators" @change="handleSearch" />
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
@ -62,8 +54,9 @@
|
||||
<script setup>
|
||||
import { reactive, defineEmits, defineProps } from 'vue';
|
||||
import { fetchAccountGroups, fetchAccountOperators } from '@/api/all/propertyMarketing';
|
||||
import GroupSelect from '../group-select/index.vue';
|
||||
import { STATUS_LIST } from '@/views/property-marketing/media-account/account-dashboard/constants';
|
||||
import GroupSelect from '@/views/property-marketing/media-account/components/group-select';
|
||||
import OperatorSelect from '@/views/property-marketing/media-account/components/operator-select';
|
||||
import StatusSelect from '@/views/property-marketing/media-account/components/status-select';
|
||||
|
||||
const props = defineProps({
|
||||
query: {
|
||||
@ -100,8 +93,8 @@ const getOperators = async () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// getGroups();
|
||||
// getOperators();
|
||||
getGroups();
|
||||
getOperators();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 14:02:40
|
||||
-->
|
||||
<template>
|
||||
<a-select
|
||||
v-model="selectedGroups"
|
||||
:multiple="multiple"
|
||||
size="medium"
|
||||
:placeholder="placeholder"
|
||||
allow-clear
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
||||
{{ item.name }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Array, String, Number],
|
||||
default: () => [],
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '全部',
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
const selectedGroups = ref(props.multiple ? [] : '');
|
||||
|
||||
// 监听外部传入的值变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
selectedGroups.value = newVal;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 监听内部值变化,向外部发送更新
|
||||
watch(selectedGroups, (newVal) => {
|
||||
emits('update:modelValue', newVal);
|
||||
});
|
||||
|
||||
const handleChange = (value) => {
|
||||
selectedGroups.value = value;
|
||||
emits('change', value);
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user