|
@@ -0,0 +1,321 @@
|
|
|
+<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 label="week" :disabled="!props.fetchLineWeek">周</el-radio-button>
|
|
|
+ <el-radio-button label="month" :disabled="!props.fetchLineWeek">月</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <div style="height: 350px;" ref="chartRef"></div>
|
|
|
+</div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { ref,onMounted, onBeforeUnmount, Ref, unref, watch, computed } from 'vue'
|
|
|
+import * as echarts from 'echarts'
|
|
|
+import { spCampaignMetricsEnum } from '/@/views/adManage/utils/enum.js'
|
|
|
+import MetricsCards from '/@/components/MetricsCards/index.vue'
|
|
|
+import XEUtils from 'xe-utils'
|
|
|
+import { buildChartOpt, parseQueryParams } from '/@/views/adManage/utils/tools.js'
|
|
|
+
|
|
|
+// import { useShopInfo } from '/@/stores/shopInfo'
|
|
|
+// import { usePublicData } from '/@/stores/publicData'
|
|
|
+// import { storeToRefs } from 'pinia'
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: "DataTendencyChart"
|
|
|
+})
|
|
|
+
|
|
|
+interface Props {
|
|
|
+ fetchCard: Function,
|
|
|
+ fetchLine: Function,
|
|
|
+ fetchLineMonth?: Function,
|
|
|
+ fetchLineWeek?: Function,
|
|
|
+ query: {[key: string]: any},
|
|
|
+ initMetric?: ShowMetric[],
|
|
|
+ metricEnum?: {[key: string]: string}[]
|
|
|
+}
|
|
|
+
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
+ initMetric: () => [
|
|
|
+ {metric: 'Impression', color: '#0085ff', 'label': '曝光量'},
|
|
|
+ {metric: 'Click', color: '#3fd4cf', 'label': '点击量'},
|
|
|
+ {metric: 'Spend', color: '#ff9500', 'label': '花费'},
|
|
|
+ ],
|
|
|
+ metricEnum: () => spCampaignMetricsEnum
|
|
|
+})
|
|
|
+
|
|
|
+const metrics = ref(props.initMetric)
|
|
|
+// const shopInfo = useShopInfo()
|
|
|
+// const publicData = usePublicData()
|
|
|
+// const { dateRange } = storeToRefs(publicData)
|
|
|
+const metricsItems: Ref<MetricData[]> = ref([])
|
|
|
+let chartObj:any
|
|
|
+const chartRef = ref()
|
|
|
+const statDim = ref('day')
|
|
|
+const option: any = {
|
|
|
+ dataset: {
|
|
|
+ source: []
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ label: {
|
|
|
+ backgroundColor: '#6a7985'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ selected: {}, // 控制显隐
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ top: 50, right: 150, bottom: 30, left: 65,
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category'
|
|
|
+ },
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ id: 0,
|
|
|
+ type: 'value',
|
|
|
+ name: '',
|
|
|
+ splitLine: {
|
|
|
+ show: true // 设置显示分割线
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ show: true,
|
|
|
+ lineStyle: { color: '' }
|
|
|
+ },
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ type: 'value',
|
|
|
+ name: '',
|
|
|
+ position: 'right',
|
|
|
+ splitLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ show: true,
|
|
|
+ lineStyle: {
|
|
|
+ color: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ type: 'value',
|
|
|
+ position: 'right',
|
|
|
+ offset: 90,
|
|
|
+ name: '',
|
|
|
+ splitLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ show: true,
|
|
|
+ lineStyle: {
|
|
|
+ color: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ show: true
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ id: 0,
|
|
|
+ name: '',
|
|
|
+ type: 'bar',
|
|
|
+ encode: {
|
|
|
+ x: 'Name',
|
|
|
+ y: ''
|
|
|
+ },
|
|
|
+ barWidth: '18px',
|
|
|
+ yAxisIndex: 0,
|
|
|
+ itemStyle: {
|
|
|
+ color: '',
|
|
|
+ borderRadius: 4,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ name: '',
|
|
|
+ type: 'line',
|
|
|
+ encode: {
|
|
|
+ x: 'Name',
|
|
|
+ y: ''
|
|
|
+ },
|
|
|
+ symbolSize: 6,
|
|
|
+ symbol: 'circle',
|
|
|
+ smooth: true,
|
|
|
+ yAxisIndex: 1,
|
|
|
+ itemStyle: {
|
|
|
+ // 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'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ name: '',
|
|
|
+ type: 'line',
|
|
|
+ encode: {
|
|
|
+ x: 'Name',
|
|
|
+ y: ''
|
|
|
+ },
|
|
|
+ symbolSize: 6,
|
|
|
+ symbol: 'circle',
|
|
|
+ smooth: true,
|
|
|
+ yAxisIndex: 2,
|
|
|
+ itemStyle: {},
|
|
|
+ areaStyle: {},
|
|
|
+ emphasis: {
|
|
|
+ focus:'series'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+}
|
|
|
+const loading = ref(true)
|
|
|
+const queryParams = computed(() => parseQueryParams(props.query))
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getMetricsItems()
|
|
|
+ addResize()
|
|
|
+ // initLine()
|
|
|
+ setTimeout(() => { initLine() }, 0)
|
|
|
+})
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ if(chartObj) {
|
|
|
+ chartObj.dispose()
|
|
|
+ chartObj = null
|
|
|
+ }
|
|
|
+ removeResize()
|
|
|
+})
|
|
|
+
|
|
|
+const initLine = async () => {
|
|
|
+ chartObj = echarts.init(chartRef.value)
|
|
|
+ const items = await getDataset()
|
|
|
+ option.dataset.source = items
|
|
|
+
|
|
|
+ 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
|
|
|
+ })
|
|
|
+
|
|
|
+ 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 () => {
|
|
|
+ if (statDim.value === 'week') {
|
|
|
+ if (props.fetchLineWeek) {
|
|
|
+ const resp = await props.fetchLineWeek(queryParams.value)
|
|
|
+ return resp.data
|
|
|
+ }
|
|
|
+ } else if (statDim.value === 'month') {
|
|
|
+ if (props.fetchLineMonth) {
|
|
|
+ const resp = await props.fetchLineMonth(queryParams.value)
|
|
|
+ return resp.data
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const resp = await props.fetchLine(queryParams.value)
|
|
|
+ return resp.data
|
|
|
+ }
|
|
|
+}
|
|
|
+const getMetricsItems = async () => {
|
|
|
+ const resp = await props.fetchCard(queryParams.value)
|
|
|
+ const data = resp.data
|
|
|
+ metricsItems.value.length = 0
|
|
|
+ XEUtils.arrayEach(props.metricEnum, info => {
|
|
|
+ const tmp:MetricData = {
|
|
|
+ label: info.label,
|
|
|
+ value: info.value,
|
|
|
+ metricVal: data[info.value],
|
|
|
+ gapVal: data[`gap${info.value}`],
|
|
|
+ preVal: data[`prev${info.value}`],
|
|
|
+ }
|
|
|
+ metricsItems.value.push(tmp)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const changeMetric = () => {
|
|
|
+ const opt = buildChartOpt(option, metrics.value)
|
|
|
+ chartObj.setOption(opt)
|
|
|
+}
|
|
|
+
|
|
|
+const changeStatDim = async () => {
|
|
|
+ loading.value = true
|
|
|
+ let source = await getDataset()
|
|
|
+ if (source.length > 0) {
|
|
|
+ chartObj.setOption({dataset: {source: source}})
|
|
|
+ }
|
|
|
+ loading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+watch(
|
|
|
+ props.query,
|
|
|
+ async () => {
|
|
|
+ // console.log("------watch-----queryParams", props.query)
|
|
|
+ loading.value = true
|
|
|
+ await getMetricsItems()
|
|
|
+ const items = await getDataset()
|
|
|
+ const opt = { dataset: { source: items } }
|
|
|
+ chartObj.setOption(opt)
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
+const resizeChart = () => { chartObj.resize() }
|
|
|
+const addResize = () => { window.addEventListener('resize', resizeChart) }
|
|
|
+const removeResize = () => { window.removeEventListener('resize', resizeChart) }
|
|
|
+
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.metrics-cards {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 12px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-button-group {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-top: 5px;
|
|
|
+}
|
|
|
+</style>
|