refactor(property-marketing): 重构投放账号趋势展示功能
-优化 EchartsItem 组件,支持动态数据更新 - 新增多个投放指标的图表展示,包括展示量、点击量、转化率等 - 实现账号类型切换时重新加载对应数据 - 优化数据加载逻辑,增加 loading 状态管理 - 重构 API 接口调用,支持传递参数获取数据
This commit is contained in:
@ -282,13 +282,13 @@ export const getMediaAccountsAuthorizedStatus = (id: string) => {
|
|||||||
|
|
||||||
// 投放账号-趋势
|
// 投放账号-趋势
|
||||||
export const getPlacementAccountsTrend = (params = {}) => {
|
export const getPlacementAccountsTrend = (params = {}) => {
|
||||||
return Http.get(`/v1/placement-accounts/trend`);
|
return Http.get(`/v1/placement-accounts/trend`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 投放账号计划数据-趋势
|
// 投放账号计划数据-趋势
|
||||||
export const getPlacementAccountProjectsTrend = (id: string) => {
|
export const getPlacementAccountProjectsTrend = (params = {}) => {
|
||||||
return Http.get(`/v1/placement-account-projects/trend`);
|
return Http.get(`/v1/placement-account-projects/trend`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 投放指南查询
|
// 投放指南查询
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div >
|
<div>
|
||||||
<a-card :bordered="false" class="echart-item-card">
|
<a-card :bordered="false" class="echart-item-card">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span class="a-card-title">{{ title.name }}</span>
|
<span class="a-card-title">{{ title.name }}</span>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
<div id="echarts-line" ref="chart" style="width: 100%; height: 450px"></div>
|
<div ref="chart" style="width: 100%; height: 450px"></div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -19,7 +19,7 @@
|
|||||||
import { defineProps, onMounted } from 'vue';
|
import { defineProps, onMounted } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import { IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
import { IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
||||||
//子组件参数传递
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -43,11 +43,18 @@ let chartInstance: echarts.ECharts | null = null;
|
|||||||
|
|
||||||
const xAxisData = props.xAxisData;
|
const xAxisData = props.xAxisData;
|
||||||
const seriesData = props.seriesData;
|
const seriesData = props.seriesData;
|
||||||
|
console.log(seriesData, 'seriesData');
|
||||||
|
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
if (!chart.value) return;
|
if (!chart.value) return;
|
||||||
|
// 如果已有实例,就不重复初始化
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose(chart.value);
|
||||||
|
}
|
||||||
chartInstance = echarts.init(chart.value);
|
chartInstance = echarts.init(chart.value);
|
||||||
|
|
||||||
|
console.log('init');
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
@ -56,20 +63,6 @@ const initChart = () => {
|
|||||||
borderColor: '#ccc',
|
borderColor: '#ccc',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
textStyle: { color: '#333' },
|
textStyle: { color: '#333' },
|
||||||
formatter: function (params) {
|
|
||||||
let date = params[0].axisValue;
|
|
||||||
let tooltipHtml = `<div style="font-weight:bold;">消耗量 ${date}</div>`;
|
|
||||||
params.forEach((item) => {
|
|
||||||
tooltipHtml += `
|
|
||||||
<div style="display:flex;align-items:center;">
|
|
||||||
<span style="display:inline-block;width:10px;height:10px;border-radius:5px;margin-right:5px;background-color:${item.color}"></span>
|
|
||||||
<span style="margin-right:10px;">${item.seriesName}</span>
|
|
||||||
<span>${item.value}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
return tooltipHtml;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
type: 'scroll',
|
type: 'scroll',
|
||||||
@ -80,7 +73,7 @@ const initChart = () => {
|
|||||||
pageButtonItemGap: 5,
|
pageButtonItemGap: 5,
|
||||||
pageButtonStyle: { color: '#666' },
|
pageButtonStyle: { color: '#666' },
|
||||||
textStyle: { color: '#666' },
|
textStyle: { color: '#666' },
|
||||||
data: seriesData.map((item) => item.name),
|
data: seriesData,
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 80, // 调整图表内容位置,避免与图例重叠
|
top: 80, // 调整图表内容位置,避免与图例重叠
|
||||||
@ -104,9 +97,23 @@ const initChart = () => {
|
|||||||
chartInstance.setOption(option);
|
chartInstance.setOption(option);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.seriesData,
|
||||||
|
() => {
|
||||||
|
initChart(); //重新渲染的方法
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart();
|
initChart();
|
||||||
});
|
});
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose();
|
||||||
|
chartInstance = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -36,11 +36,9 @@
|
|||||||
|
|
||||||
<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-select v-model="query.operator_id" size="medium" placeholder="全部" allow-clear @change="handleSearch">
|
<a-space class="w-160px">
|
||||||
<a-option v-for="(item, index) in operators" :key="index" :value="item.id" :label="item.name"
|
<OperatorSelect v-model="query.operator_id" :options="operators" />
|
||||||
>{{ item.name }}
|
</a-space>
|
||||||
</a-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-row flex">
|
<div class="filter-row flex">
|
||||||
@ -65,24 +63,71 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-wrap rounded-8px py-5px flex-1 flex flex-col">
|
<div class="table-wrap rounded-8px py-5px flex-1 flex flex-col" v-if="onLoading == false">
|
||||||
<a-row class="grid-demo" :gutter="24">
|
<a-row class="grid-demo" :gutter="24">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<EchartsItem
|
<EchartsItem
|
||||||
:xAxisData="xhlEcharts.consumption_volume.xAxisData"
|
:xAxisData="xhlEcharts?.total_use_amount?.date"
|
||||||
:seriesData="xhlEcharts.consumption_volume.seriesData"
|
:seriesData="xhlEcharts?.total_use_amount?.series_data"
|
||||||
:title="{ name: '消耗量', popover: '消耗量' }"
|
:title="{ name: '消耗量', popover: '消耗量' }"
|
||||||
></EchartsItem>
|
></EchartsItem>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<EchartsItem
|
<EchartsItem
|
||||||
:xAxisData="xhlEcharts.click_count.xAxisData"
|
:xAxisData="xhlEcharts?.avg_conversion_cost?.date"
|
||||||
:seriesData="xhlEcharts.click_count.seriesData"
|
:seriesData="xhlEcharts?.avg_conversion_cost?.series_data"
|
||||||
:title="{ name: '消耗量', popover: '消耗量' }"
|
:title="{ name: '展示量', popover: '展示量' }"
|
||||||
|
></EchartsItem>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row class="grid-demo" :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<EchartsItem
|
||||||
|
:xAxisData="xhlEcharts?.click_number?.date"
|
||||||
|
:seriesData="xhlEcharts?.click_number?.series_data"
|
||||||
|
:title="{ name: '点击量', popover: '点击量' }"
|
||||||
|
></EchartsItem>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<EchartsItem
|
||||||
|
:xAxisData="xhlEcharts?.click_rate?.date"
|
||||||
|
:seriesData="xhlEcharts?.click_rate?.series_data"
|
||||||
|
:title="{ name: '点击率', popover: '点击率' }"
|
||||||
|
></EchartsItem>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row class="grid-demo" :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<EchartsItem
|
||||||
|
:xAxisData="xhlEcharts?.avg_click_cost?.date"
|
||||||
|
:seriesData="xhlEcharts?.avg_click_cost?.series_data"
|
||||||
|
:title="{ name: '平均点击成本', popover: '平均点击成本' }"
|
||||||
|
></EchartsItem>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<EchartsItem
|
||||||
|
:xAxisData="xhlEcharts?.thousand_show_cost?.date"
|
||||||
|
:seriesData="xhlEcharts?.thousand_show_cost?.series_data"
|
||||||
|
:title="{ name: '千次展示成本', popover: '千次展示成本' }"
|
||||||
|
></EchartsItem>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row class="grid-demo" :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<EchartsItem
|
||||||
|
:xAxisData="xhlEcharts?.conversion_number?.date"
|
||||||
|
:seriesData="xhlEcharts?.conversion_number?.series_data"
|
||||||
|
:title="{ name: '转化数', popover: '转化数' }"
|
||||||
|
></EchartsItem>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<EchartsItem
|
||||||
|
:xAxisData="xhlEcharts?.conversion_rate?.date"
|
||||||
|
:seriesData="xhlEcharts?.conversion_rate?.series_data"
|
||||||
|
:title="{ name: '转化率', popover: '转化率' }"
|
||||||
></EchartsItem>
|
></EchartsItem>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -93,46 +138,55 @@ import {
|
|||||||
getPlacementAccountsList,
|
getPlacementAccountsList,
|
||||||
getPlacementAccountsTrend,
|
getPlacementAccountsTrend,
|
||||||
getPlacementAccountProjectsTrend,
|
getPlacementAccountProjectsTrend,
|
||||||
|
fetchAccountOperators,
|
||||||
} from '@/api/all/propertyMarketing';
|
} from '@/api/all/propertyMarketing';
|
||||||
|
import OperatorSelect from '@/views/property-marketing/media-account/components/operator-select/index.vue';
|
||||||
|
|
||||||
const accountType = ref('1');
|
const accountType = ref(1);
|
||||||
|
|
||||||
|
const onLoading = ref(true);
|
||||||
|
|
||||||
|
const getOperators = async () => {
|
||||||
|
const { code, data } = await fetchAccountOperators();
|
||||||
|
if (code === 200) {
|
||||||
|
operators.value = data;
|
||||||
|
}
|
||||||
|
};
|
||||||
const query = reactive({
|
const query = reactive({
|
||||||
names: '',
|
names: '',
|
||||||
platform: '',
|
platform: '',
|
||||||
operator_id: '',
|
operator_id: '',
|
||||||
});
|
});
|
||||||
const getPlacementAccounts = async () => {
|
const xhlEcharts = reactive({});
|
||||||
|
const getAccountsTrends = async () => {
|
||||||
const { code, data } = await getPlacementAccountsTrend(query);
|
const { code, data } = await getPlacementAccountsTrend(query);
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
accountList.value = data;
|
Object.assign(xhlEcharts, data);
|
||||||
}
|
}
|
||||||
|
onLoading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAccountProjectsTrend = async () => {
|
||||||
|
const { code, data } = await getPlacementAccountProjectsTrend(query);
|
||||||
|
if (code === 200) {
|
||||||
|
Object.assign(xhlEcharts, data);
|
||||||
|
}
|
||||||
|
onLoading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
getPlacementAccounts();
|
if (accountType.value == 1) {
|
||||||
};
|
getAccountsTrends();
|
||||||
const handleReset = async () => {};
|
} else if (accountType.value == 2) {
|
||||||
// 投放账号计划数据-趋势
|
getAccountProjectsTrend();
|
||||||
const getPlacementAccountProjects = async () => {
|
|
||||||
const { code, data } = await getPlacementAccountProjectsTrend(query);
|
|
||||||
if (code === 200) {
|
|
||||||
accountList.value = data;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleReset = async () => {};
|
||||||
|
|
||||||
const operators = ref([]);
|
const operators = ref([]);
|
||||||
const accountList = ref([]);
|
const accountList = ref([]);
|
||||||
const handleTabClick = (key) => {
|
const handleTabClick = (key) => {
|
||||||
console.log(key, 'key');
|
handleSearch();
|
||||||
if (key == 1) {
|
|
||||||
getAccountList();
|
|
||||||
} else if (key == 2) {
|
|
||||||
getPlacementAccountProjects();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOperators = async () => {
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取账户名称
|
// 获取账户名称
|
||||||
@ -144,125 +198,10 @@ const getAccountList = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getPlacementAccounts();
|
handleSearch();
|
||||||
getAccountList();
|
getAccountList();
|
||||||
getOperators();
|
getOperators();
|
||||||
});
|
});
|
||||||
const xhlEcharts = reactive({
|
|
||||||
consumption_volume: {
|
|
||||||
label: '消耗量',
|
|
||||||
key: 'consumption_volume',
|
|
||||||
xAxisData: ['06-05', '06-06', '06-07', '06-08', '06-09', '06-10', '06-11'],
|
|
||||||
seriesData: [
|
|
||||||
{
|
|
||||||
name: '本地推-小庭院推广计划',
|
|
||||||
type: 'line',
|
|
||||||
data: [30000, 40000, 25000, 20000, 29019, 29019, 29019],
|
|
||||||
color: '#7b68ee',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '全球旅行小助手推广',
|
|
||||||
type: 'line',
|
|
||||||
data: [25000, 27000, 23000, 20000, 29019, 29019, 29019],
|
|
||||||
color: '#32c5ff',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '定向旅居推广顶级套餐',
|
|
||||||
type: 'line',
|
|
||||||
data: [10000, 5000, 12000, 15000, 29019, 29019, 29019],
|
|
||||||
color: '#feb62f',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '城市探索者特惠活动',
|
|
||||||
type: 'line',
|
|
||||||
data: [15000, 18000, 12000, 16000, 20000, 22000, 24000],
|
|
||||||
color: '#ff7d00',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '高端民宿精选推荐',
|
|
||||||
type: 'line',
|
|
||||||
data: [22000, 20000, 25000, 23000, 21000, 19000, 22000],
|
|
||||||
color: '#f5222d',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '美食探店达人计划',
|
|
||||||
type: 'line',
|
|
||||||
data: [18000, 19000, 17000, 16000, 18000, 20000, 21000],
|
|
||||||
color: '#722ed1',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '户外探险装备推荐',
|
|
||||||
type: 'line',
|
|
||||||
data: [12000, 14000, 13000, 15000, 17000, 16000, 18000],
|
|
||||||
color: '#13c2c2',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
},
|
|
||||||
click_count: {
|
|
||||||
label: '点击量',
|
|
||||||
key: 'click_count',
|
|
||||||
xAxisData: ['06-05', '06-06', '06-07', '06-08', '06-09', '06-10', '06-11'],
|
|
||||||
seriesData: [
|
|
||||||
{
|
|
||||||
name: '本地推-小庭院推广计划',
|
|
||||||
type: 'line',
|
|
||||||
data: [30000, 40000, 25000, 20000, 29019, 29019, 29019],
|
|
||||||
color: '#7b68ee',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '全球旅行小助手推广',
|
|
||||||
type: 'line',
|
|
||||||
data: [25000, 27000, 23000, 20000, 29019, 29019, 29019],
|
|
||||||
color: '#32c5ff',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '定向旅居推广顶级套餐',
|
|
||||||
type: 'line',
|
|
||||||
data: [10000, 5000, 12000, 15000, 29019, 29019, 29019],
|
|
||||||
color: '#feb62f',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '城市探索者特惠活动',
|
|
||||||
type: 'line',
|
|
||||||
data: [15000, 18000, 12000, 16000, 20000, 22000, 24000],
|
|
||||||
color: '#ff7d00',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '高端民宿精选推荐',
|
|
||||||
type: 'line',
|
|
||||||
data: [22000, 20000, 25000, 23000, 21000, 19000, 22000],
|
|
||||||
color: '#f5222d',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '美食探店达人计划',
|
|
||||||
type: 'line',
|
|
||||||
data: [18000, 19000, 17000, 16000, 18000, 20000, 21000],
|
|
||||||
color: '#722ed1',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '户外探险装备推荐',
|
|
||||||
type: 'line',
|
|
||||||
data: [12000, 14000, 13000, 15000, 17000, 16000, 18000],
|
|
||||||
color: '#13c2c2',
|
|
||||||
emphasis: { focus: 'series' },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
Reference in New Issue
Block a user