|
@@ -1,23 +1,11 @@
|
|
|
-<template>
|
|
|
- <div v-loading="loading">
|
|
|
- <MetricsCards v-model="metrics" :metric-items="metricsItems" @change="changeMetric"></MetricsCards>
|
|
|
- <el-radio-group v-model="statDim" class="chart-button-group" @change="changeStatDim">
|
|
|
- <el-radio-button label="day">日</el-radio-button>
|
|
|
- <el-radio-button :disabled="!props.fetchLineWeek" label="week">周</el-radio-button>
|
|
|
- <el-radio-button :disabled="!props.fetchLineWeek" label="month">月</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- <div ref="chartRef" style="height: 350px"></div>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
<script lang="ts" setup>
|
|
|
import * as echarts from 'echarts';
|
|
|
-import { Ref, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
|
-import { dayMetricsEnum, weekMetricsEnum, monthMetricsEnum } from '/src/views/reportManage/dataCenter/utils/enum';
|
|
|
+import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
|
|
|
+import { dayMetricsEnum, initData } from '/src/views/reportManage/dataCenter/utils/enum';
|
|
|
import XEUtils from 'xe-utils';
|
|
|
import MetricsCards from '../MetricsCards/index.vue';
|
|
|
import emitter from '/src/utils/emitter';
|
|
|
-import { buildChartOpt, parseQueryParams } from '/src/views/reportManage/dataCenter/utils/tools';
|
|
|
+import { parseQueryParams } from '/src/views/reportManage/dataCenter/utils/tools';
|
|
|
|
|
|
defineOptions({
|
|
|
name: 'DataTendencyChart',
|
|
@@ -36,16 +24,11 @@ interface Props {
|
|
|
}
|
|
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
- initMetric: () => [
|
|
|
- {metric: 'sales', color: '#0085ff', label: '销售额'},
|
|
|
- {metric: 'ad_sales', color: '#ff9500', label: '广告销售额'},
|
|
|
- {metric: 'ad_cost', color: '#3fd4cf', label: '广告花费'},
|
|
|
- ],
|
|
|
+ initMetric: () => initData,
|
|
|
metricEnum: () => dayMetricsEnum,
|
|
|
});
|
|
|
|
|
|
const metrics = ref(props.initMetric);
|
|
|
-
|
|
|
const metricsItems: Ref<MetricData[]> = ref([]);
|
|
|
let chartObj: any;
|
|
|
const chartRef = ref();
|
|
@@ -136,7 +119,7 @@ const option: any = {
|
|
|
yAxisIndex: 0,
|
|
|
itemStyle: {
|
|
|
color: '',
|
|
|
- borderRadius: 4,
|
|
|
+ borderRadius: [4, 4, 4, 4],
|
|
|
},
|
|
|
},
|
|
|
{
|
|
@@ -147,11 +130,22 @@ const option: any = {
|
|
|
x: 'data_datetime',
|
|
|
y: '',
|
|
|
},
|
|
|
- barWidth: '18px',
|
|
|
- yAxisIndex: 0,
|
|
|
+ symbolSize: 6,
|
|
|
+ symbol: 'circle',
|
|
|
+ smooth: true,
|
|
|
+ yAxisIndex: 1,
|
|
|
itemStyle: {
|
|
|
- color: '',
|
|
|
- borderRadius: 4,
|
|
|
+ // color: '#ff9500',
|
|
|
+ // borderColor: '#ff9500'
|
|
|
+ },
|
|
|
+ areaStyle: {
|
|
|
+ // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ // { offset: 0, color: '#3fd4cf53' },
|
|
|
+ // { offset: 1, color: '#3fd4cf03' },
|
|
|
+ // ]),
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series',
|
|
|
},
|
|
|
},
|
|
|
{
|
|
@@ -184,6 +178,7 @@ onMounted(() => {
|
|
|
initLine();
|
|
|
}, 0);
|
|
|
});
|
|
|
+
|
|
|
onBeforeUnmount(() => {
|
|
|
if (chartObj) {
|
|
|
chartObj.dispose();
|
|
@@ -192,67 +187,71 @@ onBeforeUnmount(() => {
|
|
|
removeResize();
|
|
|
});
|
|
|
|
|
|
-//初始化图表
|
|
|
-const initLine = async () => {
|
|
|
+async function initLine() {
|
|
|
chartObj = echarts.init(chartRef.value);
|
|
|
- const items = await getDataset();
|
|
|
- option.dataset.source = items;
|
|
|
+ option.dataset.source = await getDataset();
|
|
|
|
|
|
- XEUtils.arrayEach(option.series, (info: any, index) => {
|
|
|
- const color = metrics.value[index].color;
|
|
|
- info.name = metrics.value[index].label;
|
|
|
- info.encode.y = metrics.value[index].metric;
|
|
|
- if (info.type === 'bar') {
|
|
|
- info.itemStyle.color = color;
|
|
|
- } else {
|
|
|
- info.itemStyle = {color: color, borderColor: color};
|
|
|
- info.areaStyle = {
|
|
|
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
- {offset: 0, color: color + '53'},
|
|
|
- {offset: 1, color: color + '03'},
|
|
|
- ]),
|
|
|
- };
|
|
|
- }
|
|
|
- });
|
|
|
- XEUtils.arrayEach(option.yAxis, (info: any, index) => {
|
|
|
- info.name = metrics.value[index].label;
|
|
|
- info.axisLine.lineStyle.color = metrics.value[index].color;
|
|
|
- });
|
|
|
+ // 初始化系列和 y 轴
|
|
|
+ option.series = metrics.value.map((metric, index) => ({
|
|
|
+ id: index,
|
|
|
+ name: metric.label,
|
|
|
+ type: index === 0 ? 'bar' : 'line',
|
|
|
+ encode: {x: 'Name', y: metric.metric},
|
|
|
+ yAxisIndex: index,
|
|
|
+ itemStyle: {color: option.series.type == 'bar' ? '#0085ff' : metric.color, borderRadius: [6, 6, 6, 6]},
|
|
|
+ lineStyle: {color: metric.color},
|
|
|
+ barWidth: '10%',
|
|
|
+ areaStyle:
|
|
|
+ index !== 0
|
|
|
+ ? {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ {offset: 0, color: metric.color + '53'},
|
|
|
+ {offset: 1, color: metric.color + '03'},
|
|
|
+ ]),
|
|
|
+ }
|
|
|
+ : undefined,
|
|
|
+ }));
|
|
|
+
|
|
|
+ option.yAxis = metrics.value.map((metric, index) => ({
|
|
|
+ id: index,
|
|
|
+ name: metric.label,
|
|
|
+ type: 'value',
|
|
|
+ position: index === 0 ? 'left' : 'right',
|
|
|
+ offset: index > 1 ? (index - 1) * 80 : 0,
|
|
|
+ axisLine: {show: true, lineStyle: {color: metric.color}},
|
|
|
+ splitLine: {
|
|
|
+ show: index === 0,
|
|
|
+ },
|
|
|
+ show: true,
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 初始化图例
|
|
|
+ option.legend.selected = props.metricEnum.reduce((acc, metric) => {
|
|
|
+ acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
|
|
|
+ return acc;
|
|
|
+ }, {});
|
|
|
|
|
|
- XEUtils.arrayEach(props.metricEnum, (info) => {
|
|
|
- option.legend.selected[info.label] = false;
|
|
|
- });
|
|
|
- for (const info of metrics.value) {
|
|
|
- option.legend.selected[info.label] = true;
|
|
|
- }
|
|
|
- // console.log(option)
|
|
|
chartObj.setOption(option);
|
|
|
loading.value = false;
|
|
|
-};
|
|
|
+}
|
|
|
|
|
|
-//条形图数据
|
|
|
-const getDataset = async () => {
|
|
|
- // console.log('getDataset,line');
|
|
|
+async function getDataset() {
|
|
|
if (statDim.value === 'week') {
|
|
|
if (props.fetchLineWeek) {
|
|
|
const resp = await props.fetchLineWeek(queryParams.value);
|
|
|
- // console.log('week',resp.data);
|
|
|
return resp.data;
|
|
|
}
|
|
|
} else if (statDim.value === 'month') {
|
|
|
if (props.fetchLineMonth) {
|
|
|
const resp = await props.fetchLineMonth(queryParams.value);
|
|
|
- // console.log('month',resp.data);
|
|
|
return resp.data;
|
|
|
}
|
|
|
} else {
|
|
|
const resp = await props.fetchLine(queryParams.value);
|
|
|
return resp.data;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
-};
|
|
|
-
|
|
|
-//卡片数据
|
|
|
const getMetricsItems = async () => {
|
|
|
let resp;
|
|
|
if (statDim.value === 'week' && props.fetchCardWeek) {
|
|
@@ -278,54 +277,114 @@ const getMetricsItems = async () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-//更新图表的选项
|
|
|
-const changeMetric = () => {
|
|
|
- const opt = buildChartOpt(option, metrics.value);
|
|
|
- chartObj.setOption(opt);
|
|
|
-};
|
|
|
+function changeMetric() {
|
|
|
+ // 更新图例选中状态
|
|
|
+ option.legend.selected = props.metricEnum.reduce((acc, metric) => {
|
|
|
+ acc[metric.label] = metrics.value.some((m) => m.metric === metric.value);
|
|
|
+ return acc;
|
|
|
+ }, {});
|
|
|
+
|
|
|
+ // 重新创建 series 数组,只包含选中的指标
|
|
|
+ option.series = metrics.value.map((metric, index) => {
|
|
|
+ const baseConfig = {
|
|
|
+ id: index,
|
|
|
+ name: metric.label,
|
|
|
+ type: metric.color === '#0085ff' ? 'bar' : 'line',
|
|
|
+ encode: {x: 'Name', y: metric.metric},
|
|
|
+ yAxisIndex: index,
|
|
|
+ itemStyle: {color: metric.color, borderRadius: [6, 6, 6, 6]},
|
|
|
+ lineStyle: {color: metric.color},
|
|
|
+ barWidth: '10%',
|
|
|
+ };
|
|
|
+
|
|
|
+ if (baseConfig != 'bar') {
|
|
|
+ baseConfig.areaStyle = {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ {offset: 0, color: metric.color + '53'},
|
|
|
+ {offset: 1, color: metric.color + '03'},
|
|
|
+ ]),
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return baseConfig;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新 y 轴显示状态
|
|
|
+ option.yAxis = metrics.value.map((metric, index) => ({
|
|
|
+ id: index,
|
|
|
+ name: metric.label,
|
|
|
+ type: 'value',
|
|
|
+ position: index === 0 ? 'left' : 'right',
|
|
|
+ offset: index > 1 ? (index - 1) * 80 : 0,
|
|
|
+ axisLine: {
|
|
|
+ show: true,
|
|
|
+ lineStyle: {color: metric.color},
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ show: index === 0,
|
|
|
+ },
|
|
|
+ show: true,
|
|
|
+ }));
|
|
|
+
|
|
|
+ while (option.yAxis.length > metrics.value.length) {
|
|
|
+ option.yAxis.pop();
|
|
|
+ }
|
|
|
+
|
|
|
+ chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
|
|
|
+}
|
|
|
|
|
|
-//根据日,周,月更新图表
|
|
|
-const emit = defineEmits(['changeStatDim']);
|
|
|
const changeStatDim = async () => {
|
|
|
emitter.emit('DateTendency-dateChange', statDim.value);
|
|
|
- // loading.value = true;
|
|
|
- // // let source = await getDataset();
|
|
|
- // // if (source.length > 0) {
|
|
|
- // // chartObj.setOption({dataset: {source: source}});
|
|
|
- // // }
|
|
|
- // // await getMetricsItems();
|
|
|
- // loading.value = false;
|
|
|
};
|
|
|
|
|
|
-//监测dataRange的变化
|
|
|
let initialized = false;
|
|
|
-
|
|
|
watch(props.query, async () => {
|
|
|
if (!initialized) {
|
|
|
initialized = true;
|
|
|
return;
|
|
|
}
|
|
|
- // emitter.emit('DateTendency-changeStatDim'); // 触发DataTable的loading
|
|
|
loading.value = true;
|
|
|
await getMetricsItems();
|
|
|
- const items = await getDataset();
|
|
|
- // emit('changeStatDim', items); // 向父组件传递数据后再传递给DataTable
|
|
|
- const opt = {dataset: {source: items}};
|
|
|
- chartObj.setOption(opt);
|
|
|
+ option.dataset.source = await getDataset();
|
|
|
+ chartObj.setOption(option);
|
|
|
loading.value = false;
|
|
|
});
|
|
|
|
|
|
+watch(
|
|
|
+ () => props.initMetric,
|
|
|
+ (newVal) => {
|
|
|
+ metrics.value = newVal;
|
|
|
+ initLine(); // 重新初始化图表数据
|
|
|
+ //console.log('initMetric', props.initMetric);
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+);
|
|
|
+
|
|
|
const resizeChart = () => {
|
|
|
chartObj.resize();
|
|
|
};
|
|
|
+
|
|
|
const addResize = () => {
|
|
|
window.addEventListener('resize', resizeChart);
|
|
|
};
|
|
|
+
|
|
|
const removeResize = () => {
|
|
|
window.removeEventListener('resize', resizeChart);
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
+<template>
|
|
|
+ <div v-loading="loading">
|
|
|
+ <MetricsCards v-model="metrics" :metric-items="metricsItems" @change="changeMetric"></MetricsCards>
|
|
|
+ <el-radio-group v-model="statDim" class="chart-button-group" @change="changeStatDim">
|
|
|
+ <el-radio-button label="day">日</el-radio-button>
|
|
|
+ <el-radio-button :disabled="!props.fetchLineWeek" label="week">周</el-radio-button>
|
|
|
+ <el-radio-button :disabled="!props.fetchLineWeek" label="month">月</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <div ref="chartRef" style="height: 350px"></div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
<style scoped>
|
|
|
.metrics-cards {
|
|
|
display: flex;
|