Files
lingji-work-fe/src/views/property-marketing/put-account/account-dashboard/index.vue
2025-09-05 18:09:20 +08:00

257 lines
8.0 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.

<template>
<div class="account-dashboard-wrap">
<div class="filter-wrap bg-#fff rounded-8px pb-24px mb-16px">
<Tabs v-model:activeKey="accountType" @change="handleTabClick" size="large">
<TabPane key="1" tab="账户"></TabPane>
<TabPane key="2" tab="计划"></TabPane>
</Tabs>
<div class="container pt-24px px-24px">
<div class="filter-row flex mb-20px">
<div class="filter-row-item flex items-center" v-if="accountType == 2">
<span class="label">计划名称</span>
<PlanSelect v-model="query.ids" class="w-240px"></PlanSelect>
</div>
<div class="filter-row-item flex items-center">
<span class="label">账号名称</span>
<AccountSelect v-model:value="query.placement_account_id" class="w-240px"></AccountSelect>
</div>
<div class="filter-row-item flex items-center">
<span class="label">平台</span>
<Select v-model:value="query.platform" class="w-150" size="middle" placeholder="全部" allowClear>
<Option v-for="(item, index) in PLATFORM_LIST" :key="index" :value="item.value" :label="item.label"
>{{ item.label }}
</Option>
</Select>
</div>
<div class="filter-row-item flex items-center">
<span class="label">运营人员</span>
<CommonSelect v-model="query.operator_id" class="!w-160px" :multiple="false" :options="operators" />
</div>
</div>
<div class="filter-row flex">
<div class="filter-row-item flex items-center">
<span class="label">时间筛选</span>
<DatePicker.RangePicker v-model="data_time" allowClear format="YYYY-MM-DD" class="!w-240px" @change="onDateChange" />
</div>
<Button type="primary" ghost class="mr-12px" @click="handleSearch">
<template #icon>
<icon-search class="mr-8px" />
</template>
<template #default>搜索</template>
</Button>
<Button @click="handleReset">
<template #icon>
<icon-refresh class="mr-8px" />
</template>
<template #default>重置</template>
</Button>
</div>
</div>
</div>
<div class="table-wrap rounded-8px py-5px flex-1 flex flex-col" v-if="onLoading == false">
<Row :gutter="[24, 24]">
<Col v-for="(chart, key) in chartConfigs" :key="chart.dataKey" :span="12">
<div>
<EchartsItem
:chartData="{ date: chart.date, series_data: chart.series_data }"
:title="{ name: chart.title.name, popover: chart.title.popover }"
/>
</div>
</Col>
</Row>
</div>
</div>
</template>
<script setup lang="ts">
import EchartsItem from './components/echarts-item/index';
import { PLATFORM_LIST } from '@/utils/platform';
import { Button, Select, Tabs, Row, Col, DatePicker } from 'ant-design-vue';
import {
getPlacementAccountsTrend,
getPlacementAccountProjectsTrend,
fetchAccountOperators,
} from '@/api/all/propertyMarketing';
import CommonSelect from '@/components/common-select';
import AccountSelect from '@/views/components/common/AccountSelect.vue';
import PlanSelect from '@/views/components/common/PlanSelect.vue';
const { TabPane } = Tabs;
const { Option } = Select;
const accountType = ref("1");
const data_time = ref([]);
const operators = ref([]);
const onLoading = ref(true);
const getOperators = async () => {
const { code, data } = await fetchAccountOperators();
if (code === 200) {
operators.value = data;
}
};
const query = reactive({
platform: undefined,
operator_id: undefined,
data_time: [],
ids: [],
placement_account_id: [],
});
const getAccountsTrends = async () => {
const { code, data } = await getPlacementAccountsTrend(query);
if (code === 200) {
mergeChartData(data);
}
onLoading.value = false;
};
const handleTabClick = (value) => {
accountType.value = value;
handleSearch();
};
const onDateChange = (date) => {
if (!date) {
query.data_time = [];
handleSearch();
return;
}
const [start, end] = date;
const FORMAT_DATE = 'YYYY-MM-DD';
query.data_time = [dayjs(start).startOf('day').format(FORMAT_DATE), dayjs(end).endOf('day').format(FORMAT_DATE)];
handleSearch();
};
const mergeChartData = (apiResponse) => {
console.log(apiResponse, 'apiResponse');
chartConfigs.value = chartConfigs.value.map((config) => {
const apiItem = apiResponse[config.dataKey] || {};
return {
...config,
date: Array.isArray(apiItem.date) ? [...apiItem.date] : [],
series_data: Array.isArray(apiItem.series_data) ? [...apiItem.series_data] : [],
};
});
};
const getAccountProjectsTrend = async () => {
const { code, data } = await getPlacementAccountProjectsTrend(query);
if (code === 200) {
mergeChartData(data);
}
onLoading.value = false;
};
const handleSearch = async () => {
if (accountType.value == 1) {
getAccountsTrends();
} else if (accountType.value == 2) {
getAccountProjectsTrend();
}
};
const chartConfigs = ref([
{
dataKey: 'total_use_amount',
title: { name: '消耗量', popover: '广告投放期间已使用的预算总额,代表该账户的实际广告花费。' },
date: [],
series_data: [],
},
{
dataKey: 'show_number',
title: { name: '展示量', popover: '广告被用户看到的总次数,是衡量广告曝光覆盖的核心指标。' },
date: [],
series_data: [],
},
{
dataKey: 'click_number',
title: { name: '点击量', popover: '用户点击广告的次数,表示广告对用户产生了实际吸引。' },
date: [],
series_data: [],
},
{
dataKey: 'click_rate',
title: { name: '点击率', popover: '点击率CTR= 点击量 ÷ 展示量,衡量广告吸引力与内容质量。' },
date: [],
series_data: [],
},
{
dataKey: 'avg_click_cost',
title: {
name: '平均点击成本',
popover: '每次点击广告的平均花费CPC= 消耗量 ÷ 点击量。 ',
date: [],
series_data: [],
},
},
{
dataKey: 'thousand_show_cost',
title: { name: '千次展现费用', popover: '每千次展示带来的平均成本CPM= 消耗量 ÷ 展示量 × 1000。' },
date: [],
series_data: [],
},
{
dataKey: 'conversion_number',
title: { name: '转化数', popover: '用户完成设定行为(如注册、下单)的总次数,衡量广告实际效果。' },
date: [],
series_data: [],
},
{
dataKey: 'conversion_rate',
title: { name: '转化率', popover: '转化率CVR= 转化数 ÷ 点击量,代表广告引导行为转化的能力。' },
date: [],
series_data: [],
},
{
dataKey: 'avg_conversion_cost',
title: { name: '平均转化成本', popover: '每次转化所花费的平均广告费用CPA= 消耗量 ÷ 转化数。' },
date: [],
series_data: [],
},
{
dataKey: 'deep_conversion_number',
title: {
name: '深度转化数',
popover: '完成更高价值行为(如支付、留资等)的用户数量,是衡量广告质量的进阶指标。',
},
date: [],
series_data: [],
},
{
dataKey: 'deep_conversion_rate',
title: {
name: '深度转化率',
popover: '深度转化率 = 深度转化数 ÷ 点击量,用于评估优质转化的效率。',
},
date: [],
series_data: [],
},
{
dataKey: 'roi',
title: { name: '投资回报率', popover: 'ROI = 收益 ÷ 投入,衡量广告投放的整体经济回报情况。' },
date: [],
series_data: [],
},
]);
const handleReset = async () => {
data_time.value = [];
query.platform = undefined;
query.operator_id = undefined;
query.data_time = [];
query.ids = [];
query.placement_account_id = [];
handleSearch();
};
onMounted(() => {
handleSearch();
getOperators();
});
</script>
<style scoped lang="scss">
@import './style.scss';
</style>