refactor(property-marketing): 重构投放账号趋势展示功能
-优化 EchartsItem 组件,支持动态数据更新 - 新增多个投放指标的图表展示,包括展示量、点击量、转化率等 - 实现账号类型切换时重新加载对应数据 - 优化数据加载逻辑,增加 loading 状态管理 - 重构 API 接口调用,支持传递参数获取数据
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div >
|
||||
<div>
|
||||
<a-card :bordered="false" class="echart-item-card">
|
||||
<template #title>
|
||||
<span class="a-card-title">{{ title.name }}</span>
|
||||
@ -10,7 +10,7 @@
|
||||
</template>
|
||||
</a-popover>
|
||||
</template>
|
||||
<div id="echarts-line" ref="chart" style="width: 100%; height: 450px"></div>
|
||||
<div ref="chart" style="width: 100%; height: 450px"></div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
@ -19,7 +19,7 @@
|
||||
import { defineProps, onMounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
||||
//子组件参数传递
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: Object,
|
||||
@ -43,11 +43,18 @@ let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
const xAxisData = props.xAxisData;
|
||||
const seriesData = props.seriesData;
|
||||
console.log(seriesData, 'seriesData');
|
||||
|
||||
const initChart = () => {
|
||||
if (!chart.value) return;
|
||||
// 如果已有实例,就不重复初始化
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose(chart.value);
|
||||
}
|
||||
chartInstance = echarts.init(chart.value);
|
||||
|
||||
console.log('init');
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
@ -56,20 +63,6 @@ const initChart = () => {
|
||||
borderColor: '#ccc',
|
||||
borderWidth: 1,
|
||||
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: {
|
||||
type: 'scroll',
|
||||
@ -80,7 +73,7 @@ const initChart = () => {
|
||||
pageButtonItemGap: 5,
|
||||
pageButtonStyle: { color: '#666' },
|
||||
textStyle: { color: '#666' },
|
||||
data: seriesData.map((item) => item.name),
|
||||
data: seriesData,
|
||||
},
|
||||
grid: {
|
||||
top: 80, // 调整图表内容位置,避免与图例重叠
|
||||
@ -104,9 +97,23 @@ const initChart = () => {
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.seriesData,
|
||||
() => {
|
||||
initChart(); //重新渲染的方法
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -36,11 +36,9 @@
|
||||
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">运营人员</span>
|
||||
<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>
|
||||
<a-space class="w-160px">
|
||||
<OperatorSelect v-model="query.operator_id" :options="operators" />
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-row flex">
|
||||
@ -65,24 +63,71 @@
|
||||
</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-col :span="12">
|
||||
<EchartsItem
|
||||
:xAxisData="xhlEcharts.consumption_volume.xAxisData"
|
||||
:seriesData="xhlEcharts.consumption_volume.seriesData"
|
||||
:xAxisData="xhlEcharts?.total_use_amount?.date"
|
||||
:seriesData="xhlEcharts?.total_use_amount?.series_data"
|
||||
:title="{ name: '消耗量', popover: '消耗量' }"
|
||||
></EchartsItem>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<EchartsItem
|
||||
:xAxisData="xhlEcharts.click_count.xAxisData"
|
||||
:seriesData="xhlEcharts.click_count.seriesData"
|
||||
:title="{ name: '消耗量', popover: '消耗量' }"
|
||||
:xAxisData="xhlEcharts?.avg_conversion_cost?.date"
|
||||
:seriesData="xhlEcharts?.avg_conversion_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?.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>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -93,46 +138,55 @@ import {
|
||||
getPlacementAccountsList,
|
||||
getPlacementAccountsTrend,
|
||||
getPlacementAccountProjectsTrend,
|
||||
fetchAccountOperators,
|
||||
} 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({
|
||||
names: '',
|
||||
platform: '',
|
||||
operator_id: '',
|
||||
});
|
||||
const getPlacementAccounts = async () => {
|
||||
const xhlEcharts = reactive({});
|
||||
const getAccountsTrends = async () => {
|
||||
const { code, data } = await getPlacementAccountsTrend(query);
|
||||
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 () => {
|
||||
getPlacementAccounts();
|
||||
};
|
||||
const handleReset = async () => {};
|
||||
// 投放账号计划数据-趋势
|
||||
const getPlacementAccountProjects = async () => {
|
||||
const { code, data } = await getPlacementAccountProjectsTrend(query);
|
||||
if (code === 200) {
|
||||
accountList.value = data;
|
||||
if (accountType.value == 1) {
|
||||
getAccountsTrends();
|
||||
} else if (accountType.value == 2) {
|
||||
getAccountProjectsTrend();
|
||||
}
|
||||
};
|
||||
const handleReset = async () => {};
|
||||
|
||||
const operators = ref([]);
|
||||
const accountList = ref([]);
|
||||
const handleTabClick = (key) => {
|
||||
console.log(key, 'key');
|
||||
if (key == 1) {
|
||||
getAccountList();
|
||||
} else if (key == 2) {
|
||||
getPlacementAccountProjects();
|
||||
}
|
||||
};
|
||||
|
||||
const getOperators = async () => {
|
||||
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
// 获取账户名称
|
||||
@ -144,125 +198,10 @@ const getAccountList = async () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getPlacementAccounts();
|
||||
handleSearch();
|
||||
getAccountList();
|
||||
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>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user