feat: 投放数据表格对接

This commit is contained in:
rd
2025-07-05 15:55:56 +08:00
parent 9b8e96a2e5
commit 5ee30e9146
12 changed files with 514 additions and 434 deletions

View File

@ -279,3 +279,8 @@ export const batchMediaAccounts = (params = {}, config = {}) => {
export const getMediaAccountsAuthorizedStatus = (id: string) => { export const getMediaAccountsAuthorizedStatus = (id: string) => {
return Http.get(`/v1/media-accounts/${id}/status`); return Http.get(`/v1/media-accounts/${id}/status`);
}; };
// 投放账号-列表
export const getPlacementAccountsList = (params = {}) => {
return Http.get('/v1/placement-accounts/list', params);
};

View File

@ -121,12 +121,7 @@
<a-button type="outline" size="small" class="search-btn" @click="handleDetail(record)">详情</a-button> <a-button type="outline" size="small" class="search-btn" @click="handleDetail(record)">详情</a-button>
</template> </template>
<template <template v-else-if="column.isRateField" #cell="{ record }">
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'"> <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-up v-if="record[column.dataIndex] > 0" size="16" />
<icon-arrow-down v-else size="16" /> <icon-arrow-down v-else size="16" />

View File

@ -11,7 +11,7 @@
<div <div
v-for="(row, rowIdx) in getAccountInfoFields(dateType, showMore)" v-for="(row, rowIdx) in getAccountInfoFields(dateType, showMore)"
:key="rowIdx" :key="rowIdx"
class="grid grid-cols-4 gap-6 mb-24px" class="grid grid-cols-4 mb-24px"
> >
<div <div
v-for="(field, colIdx) in row" v-for="(field, colIdx) in row"
@ -73,7 +73,7 @@
}} }}
</template> </template>
<!-- 环比字段特殊渲染 --> <!-- 环比字段特殊渲染 -->
<template v-else-if="field.suffix === '%'"> <template v-else-if="field.isRateField">
<div <div
class="flex items-center" class="flex items-center"
:class="detailData[field.dataIndex] > 0 ? 'color-#F64B31' : 'color-#25C883'" :class="detailData[field.dataIndex] > 0 ? 'color-#F64B31' : 'color-#25C883'"

View File

@ -23,6 +23,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的观看量环比', tooltip: '账号所有内容的观看量环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -43,6 +44,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的主页访客数环比', tooltip: '账号所有内容的主页访客数环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -63,6 +65,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的点赞量环比', tooltip: '账号所有内容的点赞量环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -82,6 +85,7 @@ export const CUSTOM_FIELDS = [
width: 180, width: 180,
tooltip: '账号所有内容的评论数环比', tooltip: '账号所有内容的评论数环比',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -103,6 +107,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的收藏数环比', tooltip: '账号所有内容的收藏数环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -123,6 +128,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的弹幕数环比', tooltip: '账号所有内容的弹幕数环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -143,6 +149,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的笔记涨粉数环比', tooltip: '账号所有内容的笔记涨粉数环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -163,6 +170,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的笔记分享量环比', tooltip: '账号所有内容的笔记分享量环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
@ -184,6 +192,7 @@ export const CUSTOM_FIELDS = [
tooltip: '账号所有内容的笔记平均浏览数环比', tooltip: '账号所有内容的笔记平均浏览数环比',
align: 'right', align: 'right',
suffix: '%', suffix: '%',
isRateField: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },

View File

@ -10,12 +10,6 @@ export const TABLE_COLUMNS = [
width: 180, width: 180,
fixed: 'left', fixed: 'left',
}, },
// {
// title: '项目分组',
// dataIndex: 'group.name',
// width: 180,
// fixed: 'left',
// },
{ {
title: '状态', title: '状态',
dataIndex: 'status', dataIndex: 'status',
@ -25,14 +19,50 @@ export const TABLE_COLUMNS = [
}, },
{ {
title: '运营人员', title: '运营人员',
dataIndex: 'operator.name', dataIndex: 'operator_ame',
prop: 'operator', prop: 'operator_ame',
width: 180,
},
{
title: '最新项目名称',
dataIndex: 'newest_project_name',
prop: 'newest_project_name',
width: 180,
},
{
title: '最新项目展示数',
dataIndex: 'newest_project_show_number',
prop: 'newest_project_show_number',
width: 180,
},
{
title: '最新项目点击数',
dataIndex: 'newest_project_click_number',
prop: 'newest_project_click_number',
width: 180,
},
{
title: '最新项目点击率',
dataIndex: 'newest_project_click_rate',
prop: 'newest_project_click_rate',
width: 180,
},
{
title: '最新项目平均点击成本',
dataIndex: 'newest_project_avg_click_cost',
prop: 'newest_project_avg_click_cost',
width: 180,
},
{
title: '最新项目平均千次展示成本',
dataIndex: 'newest_project_thousand_show_cost',
prop: 'newest_project_thousand_show_cost',
width: 180, width: 180,
}, },
{ {
title: '账户总消耗', title: '账户总消耗',
dataIndex: 'total_consumption', dataIndex: 'total_use_amount',
prop: 'total_consumption', prop: 'total_use_amount',
width: 180, width: 180,
tooltip: '账号总消耗', tooltip: '账号总消耗',
prefix: '¥', prefix: '¥',
@ -41,139 +71,52 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
{ // {
title: '账户余额', // title: '账户余额',
dataIndex: 'balance', // dataIndex: 'balance',
prop: 'balance', // prop: 'balance',
width: 180, // width: 180,
tooltip: '账号余额', // tooltip: '账号余额',
prefix: '¥', // prefix: '¥',
align: 'right', // align: 'right',
sortable: { // sortable: {
sortDirections: ['ascend', 'descend'], // sortDirections: ['ascend', 'descend'],
}, // },
}, // },
{ // {
title: 'AI评价', // title: 'AI评价',
dataIndex: 'ai_evaluation', // dataIndex: 'ai_evaluate',
prop: 'ai_evaluation', // prop: 'ai_evaluate',
width: 260, // width: 260,
}, // },
{ // {
title: 'ROI', // title: '投资回报率',
dataIndex: 'roi', // dataIndex: 'roi',
prop: 'roi', // prop: 'roi',
width: 180, // width: 180,
tooltip: '账号ROI', // tooltip: '投资回报率',
align: 'right', // align: 'right',
sortable: { // sortable: {
sortDirections: ['ascend', 'descend'], // sortDirections: ['ascend', 'descend'],
}, // },
}, // },
{ // {
title: 'ROI环比', // title: '投资回报率环比',
dataIndex: 'roi_chain', // dataIndex: 'roi_chain',
prop: 'roi_chain', // prop: 'roi_chain',
width: 180, // width: 180,
tooltip: '相比上一周期的ROI变化百分比', // tooltip: '相比上一周期的ROI变化百分比',
align: 'right', // align: 'right',
suffix: '%', // suffix: '%',
sortable: { // isRateField: true,
sortDirections: ['ascend', 'descend'], // sortable: {
}, // sortDirections: ['ascend', 'descend'],
}, // },
{ // },
title: 'CPA',
dataIndex: 'cpa',
prop: 'cpa',
width: 180,
tooltip: '账号CPA',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: 'CPA环比',
dataIndex: 'roi_chain',
prop: 'roi_chain',
width: 180,
tooltip: '相比上一周期的CPA变化百分比',
align: 'right',
suffix: '%',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '转化数',
dataIndex: 'conversion_number',
prop: 'conversion_number',
width: 180,
tooltip: '账号转化数',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '转化数环比',
dataIndex: 'conversion_chain',
prop: 'conversion_chain',
width: 180,
tooltip: '相比上一周期的转化数变化百分比',
align: 'right',
suffix: '%',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: 'CVR',
dataIndex: 'conversion_rate',
prop: 'conversion_rate',
width: 180,
tooltip: '账号转化率',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: 'CVR环比',
dataIndex: 'conversion_rate_chain',
prop: 'conversion_rate_chain',
width: 180,
tooltip: '相比上一周期的CVR变化百分比',
align: 'right',
suffix: '%',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '最新投放计划标题/日期',
dataIndex: 'like_chain1',
prop: 'like_chain1',
width: 260,
tooltip: '最新发布内容的标题和发布日期',
},
{
title: '最新投放计划表现',
dataIndex: 'latest_plan_performance',
prop: 'latest_plan_performance',
width: 180,
tooltip: '最新投放计划表现',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{ {
title: '展示量', title: '展示量',
dataIndex: 'view_number', dataIndex: 'show_number',
prop: 'view_number', prop: 'show_number',
width: 180, width: 180,
tooltip: '账号所有内容的总展示次数', tooltip: '账号所有内容的总展示次数',
align: 'right', align: 'right',
@ -181,6 +124,19 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
// {
// title: '展示量环比',
// dataIndex: 'view_number_chain',
// prop: 'view_number_chain',
// width: 180,
// tooltip: '相比上一周期的展示量变化百分比',
// align: 'right',
// suffix: '%',
// isRateField: true,
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
{ {
title: '点击量', title: '点击量',
dataIndex: 'click_number', dataIndex: 'click_number',
@ -192,6 +148,19 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
// {
// title: '点击量环比',
// dataIndex: 'click_number_chain',
// prop: 'click_number_chain',
// width: 180,
// tooltip: '相比上一周期的点击量变化百分比',
// align: 'right',
// suffix: '%',
// isRateField: true,
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
{ {
title: '点击率', title: '点击率',
dataIndex: 'click_rate', dataIndex: 'click_rate',
@ -203,6 +172,19 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
// {
// title: '点击率环比',
// dataIndex: 'click_rate_chain',
// prop: 'click_rate_chain',
// width: 180,
// tooltip: '相比上一周期的CVR变化百分比',
// align: 'right',
// suffix: '%',
// isRateField: true,
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
{ {
title: '平均点击成本', title: '平均点击成本',
dataIndex: 'avg_click_cost', dataIndex: 'avg_click_cost',
@ -216,8 +198,8 @@ export const TABLE_COLUMNS = [
}, },
{ {
title: '千次展现费用', title: '千次展现费用',
dataIndex: 'cost_per_thousand_views', dataIndex: 'thousand_show_cost',
prop: 'cost_per_thousand_views', prop: 'thousand_show_cost',
width: 180, width: 180,
prefix: '¥', prefix: '¥',
tooltip: '账号所有内容的千次展现费用', tooltip: '账号所有内容的千次展现费用',
@ -226,38 +208,104 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
{ // {
title: '平均转化成本', // title: '转化数',
dataIndex: 'avg_conversion_cost', // dataIndex: 'conversion_number',
prop: 'avg_conversion_cost', // prop: 'conversion_number',
width: 180, // width: 180,
prefix: '¥', // tooltip: '账号所有内容的总转化次数',
tooltip: '账号所有内容的平均转化成本', // align: 'right',
align: 'right', // sortable: {
sortable: { // sortDirections: ['ascend', 'descend'],
sortDirections: ['ascend', 'descend'], // },
}, // },
}, // {
{ // title: '转化数环比',
title: '深度转化数', // dataIndex: 'conversion_number_chain',
dataIndex: 'deep_conversion_number', // prop: 'conversion_number_chain',
prop: 'deep_conversion_number', // width: 180,
width: 180, // tooltip: '相比上一周期的CVR变化百分比',
tooltip: '账号所有内容的总深度转化次数', // align: 'right',
align: 'right', // suffix: '%',
sortable: { // isRateField: true,
sortDirections: ['ascend', 'descend'], // sortable: {
}, // sortDirections: ['ascend', 'descend'],
}, // },
{ // },
title: '深度转化率', // {
dataIndex: 'deep_conversion_rate', // title: '转化率',
prop: 'deep_conversion_rate', // dataIndex: 'conversion_rate',
width: 180, // prop: 'conversion_rate',
tooltip: '账号所有内容的总深度转化率', // width: 180,
align: 'right', // tooltip: '账号所有内容的总转化次数',
sortable: { // align: 'right',
sortDirections: ['ascend', 'descend'], // sortable: {
}, // sortDirections: ['ascend', 'descend'],
}, // },
// },
// {
// title: '转化率环比',
// dataIndex: 'conversion_rate_chain',
// prop: 'conversion_rate_chain',
// width: 180,
// tooltip: '相比上一周期的CVR变化百分比',
// align: 'right',
// suffix: '%',
// isRateField: true,
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
// {
// title: '平均转化成本',
// dataIndex: 'avg_conversion_cost',
// prop: 'avg_conversion_cost',
// width: 180,
// prefix: '¥',
// tooltip: '账号所有内容的平均转化成本',
// align: 'right',
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
// {
// title: '深度转化数',
// dataIndex: 'deep_conversion_number',
// prop: 'deep_conversion_number',
// width: 180,
// tooltip: '账号所有内容的总深度转化次数',
// align: 'right',
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
// {
// title: '深度转化率',
// dataIndex: 'deep_conversion_rate',
// prop: 'deep_conversion_rate',
// width: 180,
// tooltip: '账号所有内容的总深度转化率',
// align: 'right',
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
// {
// title: '最新投放计划标题/日期',
// dataIndex: 'newest_work_title',
// prop: 'newest_work_title',
// width: 260,
// tooltip: '最新发布内容的标题和发布日期',
// },
// {
// title: '投放回报率',
// dataIndex: 'roi_chain1',
// prop: 'roi_chain1',
// width: 180,
// tooltip: '账号所有内容的投放回报率',
// align: 'right',
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
]; ];

View File

@ -65,7 +65,7 @@
> >
<template #title> <template #title>
<div class="flex items-center"> <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> <span class="cts mr-4px">{{ column.title }}</span>
<a-tooltip v-if="column.tooltip" :content="column.tooltip" position="top"> <a-tooltip v-if="column.tooltip" :content="column.tooltip" position="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
@ -83,48 +83,43 @@
}}</span> }}</span>
</div> </div>
</template> </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"> <div class="ai-evaluation-row flex">
<img <template v-if="record.ai_evaluate">
width="16" <img
height="16" width="16"
:src="record.ai_evaluation?.status === 1 ? icon4 : record.ai_evaluation?.status === 2 ? icon3 : icon2" height="16"
class="mr-8px icon" :src="record.ai_evaluate?.status === 0 ? icon2 : record.ai_evaluate?.status === 1 ? icon3 : icon4"
/> class="mr-8px icon"
<div> />
<p class="cts">{{ record.ai_evaluation?.text || '-' }}</p> <div>
<p class="cts text-12px lh-20px !color-#939499"> <p class="cts">{{ `${record.ai_evaluate?.level} | ${record.ai_evaluate?.advise}` }}</p>
{{ `ROI: ${record.ai_evaluation?.look_chain}% CVR: ${record.ai_evaluation?.like_chain}%` }} <p class="cts text-12px lh-20px !color-#939499">
</p> {{ `ROI: ${record.roi}% CVR: ${record.conversion_rate}%` }}
</div> </p>
</div>
</template>
<template v-else>
<p class="cts">-</p>
</template>
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'operation'" #cell="{ record }">
<a-button type="outline" size="small" class="search-btn" @click="handleDetail(record)">详情</a-button> <a-button type="outline" size="small" class="search-btn" @click="handleDetail(record)">详情</a-button>
</template> </template>
<template <template v-else-if="column.isRateField" #cell="{ record }">
v-else-if="
[
'view_chain',
'roi_chain',
'like_chain',
'cpa_chain',
'conversion_chain',
'conversion_rate_chain',
].includes(column.dataIndex)
"
#cell="{ record }"
>
<div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'"> <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-up v-if="record[column.dataIndex] > 0" size="16" />
<icon-arrow-down v-else size="16" /> <icon-arrow-down v-else size="16" />
{{ formatTableField(column, record) }} {{ formatTableField(column, record) }}
</div> </div>
</template> </template>
<template v-else-if="['like_chain1', 'like_chain4'].includes(column.dataIndex)" #cell="{ record }"> <template v-else-if="column.dataIndex === 'newest_work_title'" #cell="{ record }">
<p class="cts cursor-pointer hover:!color-#6D4CFE">打工人的环游世界旅行计划(国内版)</p> <p class="cts cursor-pointer hover:!color-#6D4CFe">{{ record.newest_work_title }}</p>
<p class="cts text-12px lh-20px !color-#939499">2025-06-18</p> <p class="cts text-12px lh-20px !color-#939499">
{{ exactFormatTime(record.newest_project_published_at) }}
</p>
</template> </template>
<template v-else #cell="{ record }"> <template v-else #cell="{ record }">
@ -140,7 +135,7 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants'; import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants';
import { formatTableField } from '@/utils/tools'; import { formatTableField, exactFormatTime } from '@/utils/tools';
import { TABLE_COLUMNS } from './constants'; import { TABLE_COLUMNS } from './constants';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import CustomTableColumnModal from '@/components/custom-table-column-modal'; import CustomTableColumnModal from '@/components/custom-table-column-modal';

View File

@ -36,15 +36,22 @@
</div> </div>
</div> </div>
<div class="filter-row flex"> <div class="filter-row flex">
<div v-if="!isAccountTab" class="filter-row-item flex items-center">
<span class="label">关联账户</span>
<a-space class="w-160px">
<AccountSelect v-model="query.placement_account_id" :options="placementAccounts" @change="handleSearch" />
</a-space>
</div>
<div class="filter-row-item flex items-center"> <div class="filter-row-item flex items-center">
<span class="label">时间筛选</span> <span class="label">时间筛选</span>
<a-space class="w-240px"> <a-space class="w-240px">
<a-range-picker <a-range-picker
v-model="query.date_range" v-model="query.data_time"
size="medium" size="medium"
allow-clear allow-clear
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD"
class="w-100%" class="w-100%"
@change="handleSearch"
/> />
</a-space> </a-space>
</div> </div>
@ -66,11 +73,16 @@
<script setup> <script setup>
import { reactive, defineEmits, defineProps } from 'vue'; import { reactive, defineEmits, defineProps } from 'vue';
import { getPlacementAccountProjectGroupsList, getPlacementAccountOperators } from '@/api/all/propertyMarketing'; import {
getPlacementAccountProjectGroupsList,
getPlacementAccountsList,
getPlacementAccountOperators,
} from '@/api/all/propertyMarketing';
import GroupSelect from '../group-select'; import GroupSelect from '../group-select';
import OperatorSelect from '@/views/property-marketing/put-account/components/operator-select'; import OperatorSelect from '@/views/property-marketing/put-account/components/operator-select';
import StatusSelect from '@/views/property-marketing/put-account/components/status-select'; import StatusSelect from '@/views/property-marketing/put-account/components/status-select';
import AccountSelect from '@/views/property-marketing/put-account/components/account-select';
const props = defineProps({ const props = defineProps({
query: { query: {
@ -85,9 +97,11 @@ const props = defineProps({
const emits = defineEmits('onSearch', 'onReset', 'update:query'); const emits = defineEmits('onSearch', 'onReset', 'update:query');
const tags = ref([]); // const tags = ref([]);
const groups = ref([]); const groups = ref([]);
const operators = ref([]); const operators = ref([]);
const placementAccounts = ref([]);
// const dataTime = ref([]);
const handleSearch = () => { const handleSearch = () => {
emits('update:query', props.query); emits('update:query', props.query);
@ -113,9 +127,17 @@ const getOperators = async () => {
} }
}; };
const getAccounts = async () => {
const { code, data } = await getPlacementAccountsList();
if (code === 200) {
placementAccounts.value = data;
}
};
onMounted(() => { onMounted(() => {
getGroups(); getGroups();
getOperators(); getOperators();
getAccounts();
}); });
defineExpose({ defineExpose({

View File

@ -17,6 +17,13 @@ export const TABLE_COLUMNS = [
width: 180, width: 180,
fixed: 'left', fixed: 'left',
}, },
{
title: '关联账户',
dataIndex: 'account_name',
prop: 'account_name',
width: 180,
fixed: 'left',
},
{ {
title: '状态', title: '状态',
dataIndex: 'status', dataIndex: 'status',
@ -26,14 +33,14 @@ export const TABLE_COLUMNS = [
}, },
{ {
title: '运营人员', title: '运营人员',
dataIndex: 'operator.name', dataIndex: 'operator_name',
prop: 'operator', prop: 'operator_name',
width: 180, width: 180,
}, },
{ {
title: '账户总消耗', title: '账户总消耗',
dataIndex: 'total_consumption', dataIndex: 'total_use_amount',
prop: 'total_consumption', prop: 'total_use_amount',
width: 180, width: 180,
tooltip: '账号总消耗', tooltip: '账号总消耗',
prefix: '¥', prefix: '¥',
@ -42,139 +49,39 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
{ // {
title: '账户余额', // title: '账户余额',
dataIndex: 'balance', // dataIndex: 'balance',
prop: 'balance', // prop: 'balance',
width: 180, // width: 180,
tooltip: '账号余额', // tooltip: '账号余额',
prefix: '¥', // prefix: '¥',
align: 'right', // align: 'right',
sortable: { // sortable: {
sortDirections: ['ascend', 'descend'], // sortDirections: ['ascend', 'descend'],
}, // },
}, // },
{ // {
title: 'AI评价', // title: 'AI评价',
dataIndex: 'ai_evaluation', // dataIndex: 'ai_evaluate',
prop: 'ai_evaluation', // prop: 'ai_evaluate',
width: 260, // width: 260,
}, // },
{ // {
title: 'ROI', // title: '投资回报率',
dataIndex: 'roi', // dataIndex: 'roi',
prop: 'roi', // prop: 'roi',
width: 180, // width: 180,
tooltip: '账号ROI', // tooltip: '投资回报率',
align: 'right', // align: 'right',
sortable: { // sortable: {
sortDirections: ['ascend', 'descend'], // sortDirections: ['ascend', 'descend'],
}, // },
}, // },
{
title: 'ROI环比',
dataIndex: 'roi_chain',
prop: 'roi_chain',
width: 180,
tooltip: '相比上一周期的ROI变化百分比',
align: 'right',
suffix: '%',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: 'CPA',
dataIndex: 'cpa',
prop: 'cpa',
width: 180,
tooltip: '账号CPA',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: 'CPA环比',
dataIndex: 'roi_chain',
prop: 'roi_chain',
width: 180,
tooltip: '相比上一周期的CPA变化百分比',
align: 'right',
suffix: '%',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '转化数',
dataIndex: 'conversion_number',
prop: 'conversion_number',
width: 180,
tooltip: '账号转化数',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '转化数环比',
dataIndex: 'conversion_chain',
prop: 'conversion_chain',
width: 180,
tooltip: '相比上一周期的转化数变化百分比',
align: 'right',
suffix: '%',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: 'CVR',
dataIndex: 'conversion_rate',
prop: 'conversion_rate',
width: 180,
tooltip: '账号转化率',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: 'CVR环比',
dataIndex: 'conversion_rate_chain',
prop: 'conversion_rate_chain',
width: 180,
tooltip: '相比上一周期的CVR变化百分比',
align: 'right',
suffix: '%',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '最新投放计划标题/日期',
dataIndex: 'like_chain1',
prop: 'like_chain1',
width: 260,
tooltip: '最新发布内容的标题和发布日期',
},
{
title: '最新投放计划表现',
dataIndex: 'latest_plan_performance',
prop: 'latest_plan_performance',
width: 180,
tooltip: '最新投放计划表现',
align: 'right',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{ {
title: '展示量', title: '展示量',
dataIndex: 'view_number', dataIndex: 'show_number',
prop: 'view_number', prop: 'show_number',
width: 180, width: 180,
tooltip: '账号所有内容的总展示次数', tooltip: '账号所有内容的总展示次数',
align: 'right', align: 'right',
@ -204,6 +111,19 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
// {
// title: '点击率环比',
// dataIndex: 'click_rate_chain',
// prop: 'click_rate_chain',
// width: 180,
// tooltip: '相比上一周期的CVR变化百分比',
// align: 'right',
// suffix: '%',
// isRateField: true,
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
{ {
title: '平均点击成本', title: '平均点击成本',
dataIndex: 'avg_click_cost', dataIndex: 'avg_click_cost',
@ -217,8 +137,8 @@ export const TABLE_COLUMNS = [
}, },
{ {
title: '千次展现费用', title: '千次展现费用',
dataIndex: 'cost_per_thousand_views', dataIndex: 'thousand_show_cost',
prop: 'cost_per_thousand_views', prop: 'thousand_show_cost',
width: 180, width: 180,
prefix: '¥', prefix: '¥',
tooltip: '账号所有内容的千次展现费用', tooltip: '账号所有内容的千次展现费用',
@ -227,38 +147,61 @@ export const TABLE_COLUMNS = [
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
}, },
{
title: '平均转化成本', // {
dataIndex: 'avg_conversion_cost', // title: '转化数',
prop: 'avg_conversion_cost', // dataIndex: 'conversion_number',
width: 180, // prop: 'conversion_number',
prefix: '¥', // width: 180,
tooltip: '账号所有内容的平均转化成本', // tooltip: '账号转化数',
align: 'right', // align: 'right',
sortable: { // sortable: {
sortDirections: ['ascend', 'descend'], // sortDirections: ['ascend', 'descend'],
}, // },
}, // },
{ // {
title: '深度转化数', // title: '转化率',
dataIndex: 'deep_conversion_number', // dataIndex: 'conversion_rate',
prop: 'deep_conversion_number', // prop: 'conversion_rate',
width: 180, // width: 180,
tooltip: '账号所有内容的总深度转化次数', // tooltip: '账号所有内容的总转化率',
align: 'right', // align: 'right',
sortable: { // sortable: {
sortDirections: ['ascend', 'descend'], // sortDirections: ['ascend', 'descend'],
}, // },
}, // },
{ // {
title: '深度转化率', // title: '平均转化成本',
dataIndex: 'deep_conversion_rate', // dataIndex: 'avg_conversion_cost',
prop: 'deep_conversion_rate', // prop: 'avg_conversion_cost',
width: 180, // width: 180,
tooltip: '账号所有内容的总深度转化率', // prefix: '¥',
align: 'right', // tooltip: '账号所有内容的平均转化成本',
sortable: { // align: 'right',
sortDirections: ['ascend', 'descend'], // sortable: {
}, // sortDirections: ['ascend', 'descend'],
}, // },
// },
// {
// title: '深度转化数',
// dataIndex: 'deep_conversion_number',
// prop: 'deep_conversion_number',
// width: 180,
// tooltip: '账号所有内容的总深度转化次数',
// align: 'right',
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
// {
// title: '深度转化率',
// dataIndex: 'deep_conversion_rate',
// prop: 'deep_conversion_rate',
// width: 180,
// tooltip: '账号所有内容的总深度转化率',
// align: 'right',
// sortable: {
// sortDirections: ['ascend', 'descend'],
// },
// },
]; ];

View File

@ -65,7 +65,7 @@
> >
<template #title> <template #title>
<div class="flex items-center"> <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> <span class="cts mr-4px">{{ column.title }}</span>
<a-tooltip v-if="column.tooltip" :content="column.tooltip" position="top"> <a-tooltip v-if="column.tooltip" :content="column.tooltip" position="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
@ -83,49 +83,42 @@
}}</span> }}</span>
</div> </div>
</template> </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"> <div class="ai-evaluation-row flex">
<img <template v-if="record.ai_evaluate">
width="16" <img
height="16" width="16"
:src="record.ai_evaluation?.status === 1 ? icon4 : record.ai_evaluation?.status === 2 ? icon3 : icon2" height="16"
class="mr-8px icon" :src="record.ai_evaluate?.status === 0 ? icon2 : record.ai_evaluate?.status === 1 ? icon3 : icon4"
/> class="mr-8px icon"
<div> />
<p class="cts">{{ record.ai_evaluation?.text || '-' }}</p> <div>
<p class="cts text-12px lh-20px !color-#939499"> <p class="cts">{{ `${record.ai_evaluate?.level} | ${record.ai_evaluate?.advise}` }}</p>
{{ `ROI: ${record.ai_evaluation?.look_chain}% CVR: ${record.ai_evaluation?.like_chain}%` }} <p class="cts text-12px lh-20px !color-#939499">
</p> {{ `ROI: ${record.roi}% CVR: ${record.conversion_rate}%` }}
</div> </p>
</div>
</template>
<template v-else>
<p class="cts">-</p>
</template>
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'operation'" #cell="{ record }">
<a-button type="outline" size="small" class="search-btn" @click="handleDetail(record)">详情</a-button> <a-button type="outline" size="small" class="search-btn" @click="handleDetail(record)">详情</a-button>
</template> </template>
<template <template v-else-if="column.isRateField" #cell="{ record }">
v-else-if="
[
'view_chain',
'roi_chain',
'like_chain',
'cpa_chain',
'conversion_chain',
'conversion_rate_chain',
].includes(column.dataIndex)
"
#cell="{ record }"
>
<div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'"> <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-up v-if="record[column.dataIndex] > 0" size="16" />
<icon-arrow-down v-else size="16" /> <icon-arrow-down v-else size="16" />
{{ formatTableField(column, record) }} {{ formatTableField(column, record) }}
</div> </div>
</template> </template>
<template v-else-if="['like_chain1', 'like_chain4'].includes(column.dataIndex)" #cell="{ record }"> <!-- <template v-else-if="['like_chain1', 'like_chain4'].includes(column.dataIndex)" #cell="{ record }">
<p class="cts cursor-pointer hover:!color-#6D4CFE">打工人的环游世界旅行计划国内版</p> <p class="cts cursor-pointer hover:!color-#6D4CFE">打工人的环游世界旅行计划国内版</p>
<p class="cts text-12px lh-20px !color-#939499">2025-06-18</p> <p class="cts text-12px lh-20px !color-#939499">2025-06-18</p>
</template> </template> -->
<template v-else #cell="{ record }"> <template v-else #cell="{ record }">
{{ formatTableField(column, record, true) }} {{ formatTableField(column, record, true) }}

View File

@ -7,8 +7,15 @@ export const INITIAL_QUERY = {
name: '', name: '',
status: '', status: '',
operator_id: '', operator_id: '',
placement_account_id: '',
group_ids: [], group_ids: [],
date_range: [], data_time: [],
column: '', column: '',
order: '', order: '',
}; };
export const INITIAL_PAGE_INFO = {
page: 1,
page_size: 20,
total: 0,
};

View File

@ -44,7 +44,7 @@
show-jumper show-jumper
show-page-size show-page-size
:current="pageInfo.page" :current="pageInfo.page"
:page-size="pageInfo.pageSize" :page-size="pageInfo.page_size"
@change="onPageChange" @change="onPageChange"
@page-size-change="onPageSizeChange" @page-size-change="onPageSizeChange"
/> />
@ -68,7 +68,7 @@ import {
postPlacementAccountDataListExport, postPlacementAccountDataListExport,
} from '@/api/all/propertyMarketing'; } from '@/api/all/propertyMarketing';
import { INITIAL_QUERY } from './constants'; import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
import { downloadByUrl } from '@/utils/tools'; import { downloadByUrl } from '@/utils/tools';
import icon2 from '@/assets/img/media-account/icon-group.png'; import icon2 from '@/assets/img/media-account/icon-group.png';
@ -78,19 +78,20 @@ const activeTab = ref('1');
const accountTableRef = ref(null); const accountTableRef = ref(null);
const groupManageModalRef = ref(null); const groupManageModalRef = ref(null);
const filterBlockRef = ref(null); const filterBlockRef = ref(null);
const query = ref({}); const query = ref(cloneDeep(INITIAL_QUERY));
const dataSource = ref([]); const dataSource = ref([]);
const pageInfo = ref({ const pageInfo = ref(cloneDeep(INITIAL_PAGE_INFO));
page: 1,
pageSize: 20,
total: 0,
});
const isAccountTab = computed(() => activeTab.value === '1'); const isAccountTab = computed(() => activeTab.value === '1');
const getData = async () => { const getData = async () => {
const _fn = isAccountTab.value ? getPlacementAccountData : getPlacementAccountDataList; const _fn = isAccountTab.value ? getPlacementAccountData : getPlacementAccountDataList;
const { code, data } = await _fn(query.value); const { page, page_size } = pageInfo.value;
const { code, data } = await _fn({
...query.value,
page,
page_size,
});
if (code === 200) { if (code === 200) {
dataSource.value = data?.data ?? []; dataSource.value = data?.data ?? [];
pageInfo.value.total = data.total; pageInfo.value.total = data.total;
@ -103,7 +104,7 @@ const onPageChange = (current) => {
}; };
const onPageSizeChange = (pageSize) => { const onPageSizeChange = (pageSize) => {
pageInfo.value.pageSize = pageSize; pageInfo.value.page_size = pageSize;
reload(); reload();
}; };
@ -113,9 +114,7 @@ const reload = () => {
}; };
const handleReset = () => { const handleReset = () => {
pageInfo.value.page = 1; pageInfo.value = cloneDeep(INITIAL_PAGE_INFO);
pageInfo.value.pageSize = 20;
pageInfo.value.total = 0;
selectedRowKeys.value = []; selectedRowKeys.value = [];
accountTableRef.value?.resetTable(); accountTableRef.value?.resetTable();
query.value = cloneDeep(INITIAL_QUERY); query.value = cloneDeep(INITIAL_QUERY);

View File

@ -0,0 +1,64 @@
<!--
* @Author: RenXiaoDong
* @Date: 2025-06-25 14:02:40
-->
<template>
<a-select
v-model="selectedOperators"
: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: false,
},
placeholder: {
type: String,
default: '全部',
},
options: {
type: Array,
default: () => [],
},
});
const emits = defineEmits(['update:modelValue', 'change']);
const selectedOperators = ref(props.multiple ? [] : '');
// 监听外部传入的值变化
watch(
() => props.modelValue,
(newVal) => {
selectedOperators.value = newVal;
},
{ immediate: true },
);
// 监听内部值变化,向外部发送更新
watch(selectedOperators, (newVal) => {
emits('update:modelValue', newVal);
});
const handleChange = (value) => {
selectedOperators.value = value;
emits('change', value);
};
</script>