|
@@ -0,0 +1,215 @@
|
|
|
+<template>
|
|
|
+ <div ref="chartRef" class="chart"></div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import * as echarts from 'echarts';
|
|
|
+import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ dates: { type: Array, required: true }, // x轴日期
|
|
|
+ natural: { type: Array, required: true }, // 自然流量
|
|
|
+ paid: { type: Array, required: true }, // 付费流量
|
|
|
+ centerText: { type: String, default: '自然流量' } // 环形中间文字
|
|
|
+});
|
|
|
+
|
|
|
+const chartRef = ref(null);
|
|
|
+let chartInstance: any = null;
|
|
|
+
|
|
|
+const initChart = () => {
|
|
|
+ if (!chartRef.value) return;
|
|
|
+ chartInstance = echarts.init(chartRef.value);
|
|
|
+
|
|
|
+ const option = {
|
|
|
+ title: {
|
|
|
+ text: '网站历史数据',
|
|
|
+ left: 18,
|
|
|
+ top: 0,
|
|
|
+ textStyle: { fontSize: 20, fontWeight: 'bold', color: '#282E30' }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['自然', '付费'],
|
|
|
+ right: 'center',
|
|
|
+ itemGap: 30,
|
|
|
+ top: 0,
|
|
|
+ textStyle: {
|
|
|
+ color: '#282E30', // 图例文字颜色
|
|
|
+ fontSize: 24
|
|
|
+ }
|
|
|
+ },
|
|
|
+ graphic: [
|
|
|
+ // {
|
|
|
+ // type: 'text',
|
|
|
+ // right: 17, // 整体靠右
|
|
|
+ // top: 12,
|
|
|
+ // style: {
|
|
|
+ // text: `{a|自}{b|人工}{c|以来的完整历史数据 }`, // 使用富文本标记
|
|
|
+ // rich: {
|
|
|
+ // a: { color: '#000000', font: '12px Arial', padding: [0, 0, 0, 0] },
|
|
|
+ // b: { color: '#036EB8', font: '12px Arial', padding: [0, 0, 0, 0] },
|
|
|
+ // c: { color: '#000000', font: '12px Arial', padding: [0, 0, 0, 0] }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ type: 'text',
|
|
|
+ left: 'center', // 居中
|
|
|
+ bottom: 0, // legend 下方位置,根据 legend.top 调整
|
|
|
+ style: {
|
|
|
+ text: '来自 Google 的流量-自然与付费',
|
|
|
+ fill: 'rgba(40,46,48,0.6)', // 文字颜色
|
|
|
+ font: '16px Arial'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+
|
|
|
+ grid: {
|
|
|
+ left: '9%',
|
|
|
+ right: '3%'
|
|
|
+ // top: '15%',
|
|
|
+ // bottom: '15%'
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: props.dates,
|
|
|
+ // boundaryGap: false,
|
|
|
+ // axisTick: { alignWithLabel: true },
|
|
|
+ splitLine: {
|
|
|
+ // x 轴竖直方向虚线
|
|
|
+ show: true,
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#ccc'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#282E30', // 字体颜色
|
|
|
+ fontSize: 12, // 字体大小
|
|
|
+ fontWeight: '400' // 字体粗细 normal | bold | bolder | lighter
|
|
|
+ },
|
|
|
+ boundaryGap: true // 开启后类目会在刻度中间,而不是在刻度线上
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ min: 0,
|
|
|
+ splitLine: {
|
|
|
+ // y 轴水平方向虚线
|
|
|
+ show: true,
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#ccc'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '自然',
|
|
|
+ type: 'line',
|
|
|
+ data: props.natural,
|
|
|
+ smooth: false,
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 12,
|
|
|
+ lineStyle: {
|
|
|
+ color: '#7ECEF4'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#7ECEF4',
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 2,
|
|
|
+ shadowColor: 'rgba(126, 206, 244, 0.4)',
|
|
|
+ shadowBlur: 10
|
|
|
+ },
|
|
|
+ areaStyle: null,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '付费',
|
|
|
+ type: 'line',
|
|
|
+ data: props.paid,
|
|
|
+ smooth: false,
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 10,
|
|
|
+ lineStyle: {
|
|
|
+ color: '#036EB8'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#036EB8',
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 2,
|
|
|
+ shadowColor: 'rgba(3, 110, 184, 0.4)',
|
|
|
+ shadowBlur: 10
|
|
|
+ },
|
|
|
+ areaStyle: null,
|
|
|
+ },
|
|
|
+ // 白色圆形背景
|
|
|
+ {
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['48%'], // 半径比主环形图小一点或相同
|
|
|
+ center: ['50%', '50%'],
|
|
|
+ data: [{ value: 1, name: 'background' }],
|
|
|
+ silent: true, // 不响应鼠标事件
|
|
|
+ label: { show: false }, // 不显示文字
|
|
|
+ itemStyle: { color: '#fff' }, // 白色
|
|
|
+ z: 8 // 放在折线下方,但环形上方
|
|
|
+ },
|
|
|
+ // 中心环形图
|
|
|
+ {
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['35%', '45%'],
|
|
|
+ center: ['50%', '50%'],
|
|
|
+ data: [{ value: 100, name: props.centerText }],
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'center',
|
|
|
+ formatter: props.centerText,
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#7ECEF4'
|
|
|
+ },
|
|
|
+ z: 10
|
|
|
+ // tooltip: { show: false }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ chartInstance.setOption(option);
|
|
|
+};
|
|
|
+
|
|
|
+// 自动 resize
|
|
|
+const resizeHandler = () => {
|
|
|
+ chartInstance && chartInstance.resize();
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ initChart();
|
|
|
+ window.addEventListener('resize', resizeHandler);
|
|
|
+});
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', resizeHandler);
|
|
|
+ chartInstance && chartInstance.dispose();
|
|
|
+});
|
|
|
+
|
|
|
+// 监听数据变化,实时更新
|
|
|
+watch(
|
|
|
+ () => [props.dates, props.natural, props.paid, props.centerText],
|
|
|
+ () => {
|
|
|
+ initChart();
|
|
|
+ },
|
|
|
+ { deep: true }
|
|
|
+);
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.chart {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ position: relative;
|
|
|
+ // left: 30px;
|
|
|
+
|
|
|
+ :deep(canvas) {
|
|
|
+ // left: 30px !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|