|
@@ -0,0 +1,781 @@
|
|
|
|
+<template>
|
|
|
|
+ <el-container class="portrait">
|
|
|
|
+ <el-header style="height: auto; padding-top: 1em">
|
|
|
|
+ <el-card>
|
|
|
|
+ <el-form ref="form" size="small" :inline="true" label-width="120px">
|
|
|
|
+ <el-form-item label="选择应用">
|
|
|
|
+ <el-select v-model="appSelect" placeholder="请选择">
|
|
|
|
+ <el-option
|
|
|
|
+ v-for="item in appList"
|
|
|
|
+ :key="item.appid"
|
|
|
|
+ :label="item.name"
|
|
|
|
+ :value="item.appid"
|
|
|
|
+ />
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item style="float: right">
|
|
|
|
+ <el-button type="primary" @click="getData"> 查询 </el-button>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-form>
|
|
|
|
+ </el-card>
|
|
|
|
+ </el-header>
|
|
|
|
+ <el-main>
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
+ <el-col :span="12">
|
|
|
|
+ <el-card>
|
|
|
|
+ <template #header>
|
|
|
|
+ <span>用户性别分布</span>
|
|
|
|
+ </template>
|
|
|
|
+ <div ref="sex"></div>
|
|
|
|
+ </el-card>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="12">
|
|
|
|
+ <el-card>
|
|
|
|
+ <template #header>
|
|
|
|
+ <span>用户年龄分布</span>
|
|
|
|
+ </template>
|
|
|
|
+ <div ref="age"></div>
|
|
|
|
+ </el-card>
|
|
|
|
+ </el-col>
|
|
|
|
+ </el-row>
|
|
|
|
+ <br />
|
|
|
|
+ <el-card>
|
|
|
|
+ <template #header>
|
|
|
|
+ <span>用户标签分布</span>
|
|
|
|
+ </template>
|
|
|
|
+ <div ref="tag"></div>
|
|
|
|
+ </el-card>
|
|
|
|
+ <br />
|
|
|
|
+ <el-card>
|
|
|
|
+ <template #header>
|
|
|
|
+ <span>陕西省内用户分布</span>
|
|
|
|
+ </template>
|
|
|
|
+
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
+ <el-col :span="12">
|
|
|
|
+ <div ref="city"></div>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="12">
|
|
|
|
+ <el-table :data="tableProData" style="width: 100%">
|
|
|
|
+ <el-table-column prop="category" label="地名" />
|
|
|
|
+ <el-table-column center prop="ct" label="访问人数" />
|
|
|
|
+ </el-table>
|
|
|
|
+ </el-col>
|
|
|
|
+ </el-row>
|
|
|
|
+ </el-card>
|
|
|
|
+ <br />
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
+ <el-col :span="12">
|
|
|
|
+ <el-card>
|
|
|
|
+ <template #header>
|
|
|
|
+ <span>用户机型分布</span>
|
|
|
|
+ </template>
|
|
|
|
+ <div ref="phone"></div>
|
|
|
|
+ </el-card>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="12">
|
|
|
|
+ <el-card>
|
|
|
|
+ <template #header>
|
|
|
|
+ <span>APP栏目分布</span>
|
|
|
|
+ </template>
|
|
|
|
+ <div ref="category"></div>
|
|
|
|
+ </el-card>
|
|
|
|
+ </el-col>
|
|
|
|
+ </el-row>
|
|
|
|
+ </el-main>
|
|
|
|
+ </el-container>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup>
|
|
|
|
+import {
|
|
|
|
+ getAppList,
|
|
|
|
+ getSexData,
|
|
|
|
+ getAgeData,
|
|
|
|
+ getCityData,
|
|
|
|
+ getTagData,
|
|
|
|
+ getPhoneData,
|
|
|
|
+ getCategoryData
|
|
|
|
+} from '@/api/portrait.js';
|
|
|
|
+import { ref } from 'vue';
|
|
|
|
+
|
|
|
|
+import shanxi from '../../assets/map/610000_shaanxi.json';
|
|
|
|
+import * as echarts from 'echarts/core';
|
|
|
|
+import {
|
|
|
|
+ PieChart,
|
|
|
|
+ BarChart,
|
|
|
|
+ PictorialBarChart,
|
|
|
|
+ MapChart
|
|
|
|
+} from 'echarts/charts';
|
|
|
|
+import {
|
|
|
|
+ TooltipComponent,
|
|
|
|
+ LegendComponent,
|
|
|
|
+ GridComponent,
|
|
|
|
+ TitleComponent,
|
|
|
|
+ VisualMapComponent
|
|
|
|
+} from 'echarts/components';
|
|
|
|
+import { CanvasRenderer } from 'echarts/renderers';
|
|
|
|
+echarts.use([
|
|
|
|
+ PieChart,
|
|
|
|
+ MapChart,
|
|
|
|
+ BarChart,
|
|
|
|
+ PictorialBarChart,
|
|
|
|
+ TooltipComponent,
|
|
|
|
+ CanvasRenderer,
|
|
|
|
+ LegendComponent,
|
|
|
|
+ GridComponent,
|
|
|
|
+ TitleComponent,
|
|
|
|
+ VisualMapComponent
|
|
|
|
+]);
|
|
|
|
+// 陕西地图引入
|
|
|
|
+echarts.registerMap('ShanXi', shanxi);
|
|
|
|
+
|
|
|
|
+const appList = ref([]);
|
|
|
|
+const tableProData = ref([]);
|
|
|
|
+const appSelect = ref('');
|
|
|
|
+const sex = ref(null);
|
|
|
|
+const age = ref(null);
|
|
|
|
+const tag = ref(null);
|
|
|
|
+const city = ref(null);
|
|
|
|
+const phone = ref(null);
|
|
|
|
+const category = ref(null);
|
|
|
|
+
|
|
|
|
+getAppList().then(res => {
|
|
|
|
+ appSelect.value = (res || [])[0].appid;
|
|
|
|
+ appList.value = res || [];
|
|
|
|
+ getData();
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const numform = n => {
|
|
|
|
+ let num = n;
|
|
|
|
+ if (isNaN(n)) num = '0';
|
|
|
|
+ else if (n >= 100000000)
|
|
|
|
+ num =
|
|
|
|
+ ((n / 100000000).toFixed(2) - 0 + '').replace(
|
|
|
|
+ /\B(?=(?:\d{3})+\b)/g,
|
|
|
|
+ ','
|
|
|
|
+ ) + '亿';
|
|
|
|
+ else if (n >= 10000)
|
|
|
|
+ num =
|
|
|
|
+ ((n / 10000).toFixed(2) - 0 + '').replace(/\B(?=(?:\d{3})+\b)/g, ',') +
|
|
|
|
+ '万';
|
|
|
|
+ else num = (num + '').replace(/\B(?=(?:\d{3})+\b)/g, ',');
|
|
|
|
+ return num;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let sexChart = undefined;
|
|
|
|
+const createSex = (list = []) => {
|
|
|
|
+ if (!list || list.length == 0) return;
|
|
|
|
+ sexChart = echarts.init(sex.value);
|
|
|
|
+ sexChart.resize({
|
|
|
|
+ height: sex.value.offsetWidth
|
|
|
|
+ });
|
|
|
|
+ const keys = list.map(v => v.category);
|
|
|
|
+ sexChart.setOption({
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'item',
|
|
|
|
+ formatter: '{b} : {d}%'
|
|
|
|
+ },
|
|
|
|
+ legend: {
|
|
|
|
+ // 图例-图上面的分类
|
|
|
|
+ orient: 'vertical',
|
|
|
|
+ right: 30,
|
|
|
|
+ // icon: 'rect',//长方形
|
|
|
|
+ icon: 'circle',
|
|
|
|
+ bottom: '20%',
|
|
|
|
+ itemWidth: 10,
|
|
|
|
+ itemHeight: 10,
|
|
|
|
+ itemGap: 13,
|
|
|
|
+ data: keys,
|
|
|
|
+ // right: '56%',
|
|
|
|
+ textStyle: {
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ color: '#a6cde8',
|
|
|
|
+ lineHeight: 49
|
|
|
|
+ },
|
|
|
|
+ formatter: function (name) {
|
|
|
|
+ return '' + name + '';
|
|
|
|
+ },
|
|
|
|
+ padding: [2, 2]
|
|
|
|
+ },
|
|
|
|
+ grid: {
|
|
|
|
+ top: '20%',
|
|
|
|
+ left: '53%',
|
|
|
|
+ right: '10%',
|
|
|
|
+ bottom: '6%',
|
|
|
|
+ containLabel: true
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ label: {
|
|
|
|
+ normal: {
|
|
|
|
+ formatter: '{b}\n{d}%'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ type: 'pie',
|
|
|
|
+ radius: '50%',
|
|
|
|
+ center: ['50%', '50%'],
|
|
|
|
+ data: list.map(v => {
|
|
|
|
+ return {
|
|
|
|
+ name: v.category,
|
|
|
|
+ value: v.ct
|
|
|
|
+ };
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let ageChart = undefined;
|
|
|
|
+const createAge = (list = []) => {
|
|
|
|
+ if (!list || list.length == 0) return;
|
|
|
|
+ ageChart = echarts.init(age.value);
|
|
|
|
+ ageChart.resize({
|
|
|
|
+ height: age.value.offsetWidth
|
|
|
|
+ });
|
|
|
|
+ const keys = [],
|
|
|
|
+ values = [];
|
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
|
+ const v = list[i];
|
|
|
|
+ keys.push(v.category);
|
|
|
|
+ values.push(v.ct);
|
|
|
|
+ }
|
|
|
|
+ ageChart.setOption({
|
|
|
|
+ title: {},
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'axis',
|
|
|
|
+ axisPointer: {
|
|
|
|
+ type: 'shadow'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ legend: {
|
|
|
|
+ itemWidth: 10,
|
|
|
|
+ itemHeight: 10,
|
|
|
|
+ itemGap: 30,
|
|
|
|
+ left: '15%',
|
|
|
|
+ top: '3%',
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ grid: {
|
|
|
|
+ left: '10%',
|
|
|
|
+ right: '10%',
|
|
|
|
+ top: '10%',
|
|
|
|
+ bottom: '10%',
|
|
|
|
+ containLabel: true
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ boundaryGap: [0, 0.01],
|
|
|
|
+ splitLine: { show: true },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: keys,
|
|
|
|
+ splitLine: { show: false },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ axisTick: { show: false }
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ type: 'bar',
|
|
|
|
+ data: values,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: 'rgba(255,77,79,0.35)',
|
|
|
|
+ borderColor: '#FF4D4F'
|
|
|
|
+ },
|
|
|
|
+ label: {
|
|
|
|
+ formatter: data => {
|
|
|
|
+ return numform(data.data);
|
|
|
|
+ },
|
|
|
|
+ show: true,
|
|
|
|
+ position: 'right',
|
|
|
|
+ textStyle: {
|
|
|
|
+ fontFamily: 'Noto Sans SC',
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ color: '#000'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let tagChart = undefined;
|
|
|
|
+const createTag = (list = []) => {
|
|
|
|
+ if (!list || list.length == 0) return;
|
|
|
|
+ tagChart = echarts.init(tag.value);
|
|
|
|
+ tagChart.resize({
|
|
|
|
+ height: (tag.value.offsetWidth / 16) * 9
|
|
|
|
+ });
|
|
|
|
+ let category = list.map(v => {
|
|
|
|
+ return {
|
|
|
|
+ name: v.category,
|
|
|
|
+ value: v.ct
|
|
|
|
+ };
|
|
|
|
+ });
|
|
|
|
+ let yName = []; // y轴名称
|
|
|
|
+ let bgData = []; // 最大值用作背景显示的数据
|
|
|
|
+ let maxValue = category[0].value; //最大值
|
|
|
|
+ category = category.reverse();
|
|
|
|
+ category.forEach(element => {
|
|
|
|
+ yName.push(element.name);
|
|
|
|
+ bgData.push({
|
|
|
|
+ name: element.name,
|
|
|
|
+ value: maxValue,
|
|
|
|
+ type: element.type
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+ tagChart.setOption({
|
|
|
|
+ xAxis: {
|
|
|
|
+ max: maxValue,
|
|
|
|
+ splitLine: {
|
|
|
|
+ show: false
|
|
|
|
+ },
|
|
|
|
+ axisLine: {
|
|
|
|
+ show: false
|
|
|
|
+ },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ show: false
|
|
|
|
+ },
|
|
|
|
+ axisTick: {
|
|
|
|
+ show: false
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ grid: {
|
|
|
|
+ left: 50,
|
|
|
|
+ top: 20,
|
|
|
|
+ right: 80,
|
|
|
|
+ bottom: 0
|
|
|
|
+ },
|
|
|
|
+ yAxis: [
|
|
|
|
+ {
|
|
|
|
+ // 每条图形上面的文字
|
|
|
|
+ inverse: false,
|
|
|
|
+ data: yName,
|
|
|
|
+ axisLabel: {
|
|
|
|
+ padding: [0, 0, 45, 0],
|
|
|
|
+ inside: true,
|
|
|
|
+ textStyle: {
|
|
|
|
+ fontSize: 12,
|
|
|
|
+ fontWeight: 400,
|
|
|
|
+ color: '#B1C3DD',
|
|
|
|
+ align: 'left'
|
|
|
|
+ },
|
|
|
|
+ formatter: '{value}',
|
|
|
|
+ rich: {
|
|
|
|
+ a: {
|
|
|
|
+ color: 'transparent',
|
|
|
|
+ lineHeight: 20,
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ shadowColor: 'rgba(255, 0, 0, 1)',
|
|
|
|
+ shadowBlur: 10
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ splitLine: {
|
|
|
|
+ show: false
|
|
|
|
+ },
|
|
|
|
+ axisTick: {
|
|
|
|
+ show: false
|
|
|
|
+ },
|
|
|
|
+ axisLine: {
|
|
|
|
+ show: false
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ // y轴最左侧的文字
|
|
|
|
+ axisTick: 'none',
|
|
|
|
+ axisLine: 'none',
|
|
|
|
+ position: 'left',
|
|
|
|
+ axisLabel: {
|
|
|
|
+ padding: [3, 12, 13, 10], // 调整文字位置
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#000',
|
|
|
|
+ fontSize: '12',
|
|
|
|
+ fontWeight: '400'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ data: Array.from({ length: yName.length }, (_, i) => i + 1).reverse()
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ // y轴最右侧的文字
|
|
|
|
+ axisTick: 'none',
|
|
|
|
+ axisLine: 'none',
|
|
|
|
+ type: 'category',
|
|
|
|
+ axisLabel: {
|
|
|
|
+ margin: 10,
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#FFAC26',
|
|
|
|
+ fontSize: '24'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ data: category
|
|
|
|
+ }
|
|
|
|
+ ],
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ // 背景样式
|
|
|
|
+ name: '背景',
|
|
|
|
+ type: 'bar',
|
|
|
|
+ barWidth: 18,
|
|
|
|
+ stack: '总量',
|
|
|
|
+ barGap: '-100%',
|
|
|
|
+ symbol: 'fixed',
|
|
|
|
+ symbolRepeat: 'repeat',
|
|
|
|
+ legendHoverLink: false,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ normal: {
|
|
|
|
+ color: 'rgba(153, 153, 153, 0)'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ data: bgData,
|
|
|
|
+ animation: false, //关闭动画
|
|
|
|
+ z: 0
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ name: 'info',
|
|
|
|
+ // 内(显示的内容)
|
|
|
|
+ type: 'bar',
|
|
|
|
+ barGap: '-100%',
|
|
|
|
+ barWidth: 18,
|
|
|
|
+ legendHoverLink: false,
|
|
|
|
+ silent: true,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ normal: {
|
|
|
|
+ color: function (params) {
|
|
|
|
+ var color;
|
|
|
|
+ if (params.dataIndex % 2 != 0) {
|
|
|
|
+ color = {
|
|
|
|
+ type: 'linear',
|
|
|
|
+ x: 0,
|
|
|
|
+ y: 0,
|
|
|
|
+ x2: 1,
|
|
|
|
+ y2: 0,
|
|
|
|
+ colorStops: [
|
|
|
|
+ {
|
|
|
|
+ offset: 0,
|
|
|
|
+ color: 'rgba(46,85,185,0)' // 0% 处的颜色
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ offset: 1,
|
|
|
|
+ color: '#317fff' // 100% 处的颜色
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ };
|
|
|
|
+ } else {
|
|
|
|
+ color = {
|
|
|
|
+ type: 'linear',
|
|
|
|
+ x: 0,
|
|
|
|
+ y: 0,
|
|
|
|
+ x2: 1,
|
|
|
|
+ y2: 0,
|
|
|
|
+ colorStops: [
|
|
|
|
+ {
|
|
|
|
+ offset: 0,
|
|
|
|
+ color: 'rgba(46,85,185,0)' // 0% 处的颜色
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ offset: 1,
|
|
|
|
+ color: '#317fff' // 100% 处的颜色
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ return color;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ data: category,
|
|
|
|
+ z: 1,
|
|
|
|
+ animationEasing: 'elasticOut'
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ // 分隔
|
|
|
|
+ type: 'pictorialBar',
|
|
|
|
+ itemStyle: {
|
|
|
|
+ normal: {
|
|
|
|
+ color: 'rgba(255,255,255,1)'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ symbolRepeat: 'fixed',
|
|
|
|
+ barWidth: 18,
|
|
|
|
+ symbolMargin: 4,
|
|
|
|
+ symbol: 'rect',
|
|
|
|
+ symbolClip: true,
|
|
|
|
+ symbolSize: [2, 18],
|
|
|
|
+ symbolPosition: 'start',
|
|
|
|
+ symbolOffset: [0, 0],
|
|
|
|
+ symbolBoundingData: maxValue,
|
|
|
|
+ data: bgData,
|
|
|
|
+ animation: false, //关闭动画
|
|
|
|
+ z: 2
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let cityChart = undefined;
|
|
|
|
+const createCity = (list = []) => {
|
|
|
|
+ if (!list || list.length == 0) return;
|
|
|
|
+ tableProData.value = list;
|
|
|
|
+ let max = -Infinity,
|
|
|
|
+ min = Infinity,
|
|
|
|
+ t = 0;
|
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
|
+ const v = list[i];
|
|
|
|
+ if (v.ct > max) max = v.ct;
|
|
|
|
+ if (v.ct < min) min = v.ct;
|
|
|
|
+ t += v.ct;
|
|
|
|
+ }
|
|
|
|
+ cityChart = echarts.init(city.value);
|
|
|
|
+ cityChart.resize({
|
|
|
|
+ height: city.value.offsetWidth
|
|
|
|
+ });
|
|
|
|
+ cityChart.setOption({
|
|
|
|
+ tooltip: {
|
|
|
|
+ confine: true,
|
|
|
|
+ trigger: 'item',
|
|
|
|
+ formatter: function (e) {
|
|
|
|
+ if (isNaN(e.value)) return;
|
|
|
|
+ return (
|
|
|
|
+ e.name +
|
|
|
|
+ '<br />人数:' +
|
|
|
|
+ (e.value || '') +
|
|
|
|
+ '<br />占比:' +
|
|
|
|
+ e.data.distribution
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ visualMap: {
|
|
|
|
+ min,
|
|
|
|
+ max,
|
|
|
|
+ text: [max, min],
|
|
|
|
+ realtime: false,
|
|
|
|
+ calculable: true,
|
|
|
|
+ inRange: {
|
|
|
|
+ color: ['lightskyblue', 'yellow', 'orangered']
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ type: 'map',
|
|
|
|
+ map: 'ShanXi',
|
|
|
|
+ label: {
|
|
|
|
+ show: true
|
|
|
|
+ },
|
|
|
|
+ data: list.map(v => {
|
|
|
|
+ return {
|
|
|
|
+ name: v.category,
|
|
|
|
+ value: v.ct,
|
|
|
|
+ distribution: ((v.ct / t) * 100).toFixed(2) - 0 + '%'
|
|
|
|
+ };
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let phoneChart = undefined;
|
|
|
|
+const createPhone = (list = []) => {
|
|
|
|
+ if (!list || list.length == 0) return;
|
|
|
|
+ phoneChart = echarts.init(tag.value);
|
|
|
|
+ phoneChart.resize({
|
|
|
|
+ height: (tag.value.offsetWidth / 16) * 9
|
|
|
|
+ });
|
|
|
|
+ const keys = [],
|
|
|
|
+ values = [];
|
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
|
+ const v = list[i];
|
|
|
|
+ keys.push(v.category);
|
|
|
|
+ values.push(v.ct);
|
|
|
|
+ }
|
|
|
|
+ phoneChart.setOption({
|
|
|
|
+ title: {},
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'axis',
|
|
|
|
+ axisPointer: {
|
|
|
|
+ type: 'shadow'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ legend: {
|
|
|
|
+ itemWidth: 10,
|
|
|
|
+ itemHeight: 10,
|
|
|
|
+ itemGap: 30,
|
|
|
|
+ left: '15%',
|
|
|
|
+ top: '3%',
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ grid: {
|
|
|
|
+ left: '10%',
|
|
|
|
+ right: '10%',
|
|
|
|
+ top: '10%',
|
|
|
|
+ bottom: '10%',
|
|
|
|
+ containLabel: true
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ boundaryGap: [0, 0.01],
|
|
|
|
+ splitLine: { show: true },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: keys,
|
|
|
|
+ splitLine: { show: false },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ axisTick: { show: false }
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ type: 'bar',
|
|
|
|
+ data: values,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: 'rgba(255,77,79,0.35)',
|
|
|
|
+ borderColor: '#FF4D4F'
|
|
|
|
+ },
|
|
|
|
+ label: {
|
|
|
|
+ formatter: data => {
|
|
|
|
+ return numform(data.data);
|
|
|
|
+ },
|
|
|
|
+ show: true,
|
|
|
|
+ position: 'right',
|
|
|
|
+ textStyle: {
|
|
|
|
+ fontFamily: 'Noto Sans SC',
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ color: '#000'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let categoryChart = undefined;
|
|
|
|
+const createCategory = (list = []) => {
|
|
|
|
+ if (!list || list.length == 0) return;
|
|
|
|
+ categoryChart = echarts.init(category.value);
|
|
|
|
+ categoryChart.resize({
|
|
|
|
+ height: category.value.offsetWidth
|
|
|
|
+ });
|
|
|
|
+ const keys = [],
|
|
|
|
+ values = [];
|
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
|
+ const v = list[i];
|
|
|
|
+ keys.push(v.category);
|
|
|
|
+ values.push(v.ct);
|
|
|
|
+ }
|
|
|
|
+ categoryChart.setOption({
|
|
|
|
+ title: {},
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'axis',
|
|
|
|
+ axisPointer: {
|
|
|
|
+ type: 'shadow'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ legend: {
|
|
|
|
+ itemWidth: 10,
|
|
|
|
+ itemHeight: 10,
|
|
|
|
+ itemGap: 30,
|
|
|
|
+ left: '15%',
|
|
|
|
+ top: '3%',
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ grid: {
|
|
|
|
+ left: '10%',
|
|
|
|
+ right: '10%',
|
|
|
|
+ top: '10%',
|
|
|
|
+ bottom: '10%',
|
|
|
|
+ containLabel: true
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ boundaryGap: [0, 0.01],
|
|
|
|
+ splitLine: { show: true },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: keys,
|
|
|
|
+ splitLine: { show: false },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ textStyle: {
|
|
|
|
+ color: '#9B9B9B'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ axisTick: { show: false }
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ type: 'bar',
|
|
|
|
+ data: values,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: 'rgba(255,77,79,0.35)',
|
|
|
|
+ borderColor: '#FF4D4F'
|
|
|
|
+ },
|
|
|
|
+ label: {
|
|
|
|
+ formatter: data => {
|
|
|
|
+ return numform(data.data);
|
|
|
|
+ },
|
|
|
|
+ show: true,
|
|
|
|
+ position: 'right',
|
|
|
|
+ textStyle: {
|
|
|
|
+ fontFamily: 'Noto Sans SC',
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ color: '#000'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const getData = () => {
|
|
|
|
+ Promise.all([
|
|
|
|
+ getSexData({ appid: appSelect.value }),
|
|
|
|
+ getAgeData({ appid: appSelect.value }),
|
|
|
|
+ getCityData({ appid: appSelect.value }),
|
|
|
|
+ getTagData({ appid: appSelect.value }),
|
|
|
|
+ getPhoneData({ appid: appSelect.value }),
|
|
|
|
+ getCategoryData({ appid: appSelect.value })
|
|
|
|
+ ]).then(list => {
|
|
|
|
+ /**
|
|
|
|
+ * list[2] 地区分布 地形图
|
|
|
|
+ * list[3] 标签分布 柱状图
|
|
|
|
+ */
|
|
|
|
+ createSex(list[0]);
|
|
|
|
+ createAge(list[1]);
|
|
|
|
+ createCity(list[2]);
|
|
|
|
+ createTag(list[3]);
|
|
|
|
+ createPhone(list[4]);
|
|
|
|
+ createCategory(list[5]);
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style scoped></style>
|