Day46-50:统计图表项目总结

建项

项目需求写法——可视化报表

可视化报表项目效果

开源表格样式库阿帕奇

绘制echarte图标的流程

  1. 在视图中放置一个容器,这个容器需要有一个固定的宽高
  2. 获取容器,调用init方法,初始化echarts实例
let container = document.querySelector('.app')
let myChart = echarte.init(container) // 初始化图表实例
  1. 给实例调用setOption方法传入配置对象,通过这个配置对象确认绘制的图表内容(在官网寻找示例,根据示例
<body>
    <div class="app" style="width: 600px;height: 400px;">

    </div>
    <script src="
https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js
"></script>
    <script>
        console.log(echarts, 123)
        // 绘制echarts图标的流程
        let container = document.querySelector('.app')
        let myChart = echarts.init(container) // 初始化图表实例
        myChart.setOption({
            // 传入配置对象,通过这个配置对象确认绘制的图表内容
            xAxis: {
                // 默认情况下,x粥通常为category类型,y轴一般为value
                type: 'category',
                data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
            },
            yAxis: {
                type: 'value'
            },
            // series决定绘制一个什么类型的图表
            series: [ // 一个框只绘制一个图像时可以不用写成数组
                {
                    data: [820, 932, 901, 934, 1290, 1330, 1320],/* 数据 */
                    type: 'line', /* 数据显示类型 */
                    areaStyle:{
                        color:"purple",
                        opacity:1,/* 颜色透明度 */
                    },
                    smooth: true, /* 光滑 */
                    itemStyle:{
                        opacity:0 // 点透明度
                    },
                }
            ]
        })
    </script>
</body>

修改图表样式

在网站的文档中找到配置项手册,

myChart.setOption({
  // 传入配置对象,通过这个配置对象确认绘制的图表内容
  xAxis: {
    // 默认情况下,x粥通常为category类型,y轴一般为value
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value',
    splitLine:{
      show: false // 去掉柱状图背景的分隔线
    }
  },
  // series决定绘制一个什么类型的图表
  series: [
    {
      data: [820, 932, 901, 934, 1290, 1330, 1320],/* 数据 */
      type: 'bar', /* 数据显示类型 */

    }
  ],
  tooltip:{}, // 鼠标移上去显示标签
})

从 npm 获取 echarts

npm install echarts

Vue2项目

开始前准备

给vue的prototype原型对象挂载的属性,可以在入口中给每个组件实例都注入一些成员。

在入口注册全局组件v-chart

import Vue from 'vue'
import App from './App.vue'
import * as echarts from 'echarts'
import vcharts from 'vue-echarts'
Vue.component('v-chart', vcharts) // 注册全局组件v-chart
Vue.config.productionTip = false
// 给vue的prototype原型对象挂载的属性,可以在入口中给每个组件实例都注入一些成员
Vue.prototype.$echarts = echarts

new Vue({
  render: h => h(App)
}).$mount('#app')

封装组件,初始化图表可以通过封装好的组件来完成。使用封装好的组件可以不再进行原本的初始化内容

改为了

input为v-model绑定的data

在视图中需要写入:option="option"

<div class="wrapper">
  <div class="container" >
    <v-chart :option="option"></v-chart>
  </div>
  <el-button>这是按钮</el-button>
  <el-input v-model="input" placeholder="请输入内容"></el-input>
</div>

封装好的初始化图表组件网站

// 全局注册组件
npm install vue-echarts
// ui组件vue2和vue3
npm i element-ui
npm i element-plus
// reset-css 初始化全局样式组件
npm i reset-css

拆分目录,把不同的功能分散在不同的组件中

  1. 新建plugins文件夹
  2. 在文件夹下新建element-ui与vue-echarts两个js文件
  3. 分别将element引入与echarts引入拆分进这两个文件夹
  4. 把这两个文件夹引入main文件,避免main文件过于杂乱

  5. 其中element-ui文件可以实现组件的按需引入,以免最终项目文件过于庞大
  6. echarts用于注册全局组件Vue.component('v-chart', vcharts)
// main
import Vue from 'vue'
import App from './App.vue'
import * as echarts from 'echarts'
import './plugins/element-ui' // 引入组件库
import './plugins/vue-echarts'
import 'reset-css' // 引入清除默认样式css
Vue.config.productionTip = false
// 给vue的prototype原型对象挂载的属性,可以在入口中给每个组件实例都注入一些成员
Vue.prototype.$echarts = echarts

new Vue({
  render: h => h(App)
}).$mount('#app')

// element-ui 实现组件的按需引入
import Vue from 'vue'
import 'element-ui/lib/theme-chalk/index.css'
import { Button, Input } from 'element-ui' // 按需引入el的组件
Vue.use(Button)
Vue.use(Input) // 组件引入后需要在下面注册一下

// vue-echarts 实现组件的全局注册
import Vue from 'vue'
import vcharts from 'vue-echarts'
Vue.component('v-chart', vcharts) // 注册全局组件v-chart

手动去除大驼峰规则

在.eslintrc.js中的rules里书写一个'vue/multi-word-component-names': 0,将命名必须要大驼峰的规则去掉

Topcomp

公共部分

  1. 把四个组件引入到app,由于是vue2,还需要注册一下,并在视图中写组件出口
<template>
  <div class="app">
    <TopComp/>
    <SecondComp/>
    <ThirdComp/>
    <MapComp/>
  </div>
</template>

<script>
import TopComp from './components/TopComp'
import SecondComp from './components/SecondComp'
import ThirdComp from './components/ThirdComp'
import MapComp from './components/MapComp'
export default {
  components: {
    TopComp,
    SecondComp,
    ThirdComp,
    MapComp
  }
}
  1. 新建main.css,写入公共样式背景颜色,并引入到入口
/* 公共样式 */
html,body {
    background-color: #eee;
}
  1. 在top中新建上栏,写四个卡片输出到top(四个卡片结构类似,通过props传递不同的数据到不同的卡片。只需要封装一个卡片,接收两个props,两个slot。top的index是第一栏的总体入口,在CommonCard中向props中传入每个卡片需要的数据
    1. 组件库中寻找layout布局,按需引入col和row组件到element-ui
    2. 注册card组件,新建vue封装一个带两个插槽的卡片,最后使用插槽进行结构分发
    3. 在chart和footer中写插槽
    4. 写样式
<template>
    <div class="commom-card">
      <div class="title">{{ title }}</div>
      <div class="value">{{ value }}</div>
      <div class="chart">
        <slot></slot>
      </div> 
      <div class="line"></div>
      <div class="footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </template>
<script>
export default {
  props: ['title', 'value']
}
</script>
span.increase{
  display: inline-block;
  width: 0;
  height: 0;
  border-width: 4px;
  border-color: transparent transparent green transparent;
  border-style: solid;
  margin-left: 10px;
  transform: translateY(-50%);
}

TotalSale

新建TotalSale.vue,使用CommonCard组件写卡片中的内容

<template>
  <div class="total-sale">
    <CommonCard title="累计销售额" :value="reportData.salesToday">
      <div>
        <span>日同比</span>
        <span class="css-1">{{reportData.salesGrowLastDay}}%</span>
        <span class="increase"></span>
      </div>
      <div>
        <span>月同比</span>
        <span class="css-1">{{reportData.saleSGrowLastMonth}}%</span>
        <span class="increase"></span>
      </div>
      <template #footer>
        <span>昨日销售额</span>
        <span class="css-1">¥{{reportData.salesLastDay }}</span>
      </template>
                   </CommonCard>
                     </div>
                     </template>

TotalOrder

绘制第二个卡片的图TotalOrder.vue

  1. grid属性,最外层叫容器,内层叫网格,与容器有一些默认边距。在一些宽高很小的容器中绘图时,可以将grid网格和容器靠近,否则图片很容易无法出现——将四周数据都设为0
  2. 通过x,y和series设置图片的样式,在通过接口地址的数据规定图像的数据
  3. 使用areaStyle属性显示折线覆盖的区域,再使用itemStyle去掉折线点,在做一个平滑
  4. 使x轴的boundaryGap为false,去掉坐标轴两边的留白
mixins: [CommonCardMixin],
  data () {
    return {
      option: null
    }
  },
  mounted () {},
  watch: {
    reportData (newValue) {
      this.renderChart(newValue.orderTrend)
    }
  },
  methods: {
    renderChart (data) {
      this.option = {
        xAxis: {
          type: 'category',
          show: false,
          boundaryGap: false
        },
        yAxis: {
          type: 'value',
          show: false
        },
        // 在一些宽高很小的容器上绘图的时候 可以将网格和容器靠近
        grid: {
          left: 0,
          top: 0,
          right: 0,
          bottom: 0
        },
        series: {
          type: 'line',
          data,
          areaStyle: {
            color: 'purple'
          },
          lineStyle: {
            width: 0
          },
          itemStyle: {
            opacity: 0
          },
          smooth: true
        }}}}
  1. 在顶部视图中显示图像和部分文字数据
<template>
    <div class="total-order">
        <CommonCard title="累积订单额" value="13145">
            <v-chart :option="option" />
            <template #footer>
                <span>昨日销售额</span>
                <span class="css-1">¥ 12768</span>
            </template>
        </CommonCard>
    </div>
</template>

TodayUser

绘制第三个卡片的图,创建TodayUser.vue

  1. 显示footer的文字,文字-今日用户交易数
  2. 规定图像为柱状图
  3. grid限定加上,规定series,type为bar,规定name,传入data
<template>
    <div class="today-user">
        <CommonCard title="今日用户交易数" :value="reportData.userToday">
            <v-chart :option="option"/>
            <template #footer>
                <span>退货率</span>
                <span class="css-1">{{reportData.returnRate}}%</span>
            </template>
        </CommonCard>
    </div>
</template>
<script>
import CommonCardMixin from '../../mixins/CommonCardMixin.js'

export default {
  mixins: [CommonCardMixin],
  data () {
    return {
      option: null
    }
  },
  methods: {
    renderChart (data) {
      this.option = {
        xAxis: {
          type: 'category',
          show: false,
          data: [
            '00:00',
            '03:00',
            '05:00',
            '07:00',
            '09:00',
            '11:00',
            '13:00',
            '15:00',
            '17:00',
            '19:00',
            '21:00',
            '23:00'
          ]
        },
        yAxis: {
          type: 'value',
          show: false
        },
        tooltip: {},
        grid: {
          left: 0,
          right: 0,
          top: 0,
          bottom: 0
        },
        series: {
          type: 'bar',
          name: '实时交易量',
          data,
          barWidth: '60%'
        }
      }
    }
  },
  mounted () {},
  watch: {
    reportData (newValue) {
      this.renderChart(newValue.orderUserTrend)
    }
  }
}

ToalUser

  1. 绘制第四个卡片的图ToalUser.vue
    1. 显示footer的文字,三角,上半的文字
    2. 调换x轴和y轴,使柱状图横向排列——x轴为value,y轴为category
    3. grid限定加上,规定series,type为bar,规定name,传入data
    4. 调整barWidth为10,itemStyle的color为green
  1. 绘制第四个卡片的三角形type=custom(配置项文档中找)可以自定义一些形状不同的图表
    1. 在后面再写一个图标项目(此时series为数组类型,该图表为数组中第二个项目)
    2. 由于横向柱状图规定了data为130,则在custom中,data也是130,与柱状图等长。data写在renderItem后面。
    3. renderItem可以自定义渲染逻辑,传入两个参数(文档中规定的)params,api
    4. 绘制三角形(利用svg路径)确定两值:一个是三角形的位置,二是如何绘制三角形本身
    5. api。value为130这个值,其中传入的参数[0],0,是他的位置
    6. return一个对象,这里为要画的图类型,由于需要画两个三角形,所以return一个type:group,并为group写两个children
    7. 为每一个children写类型path和路径,shape中的d为具体的路径,
    8. 第一个三角形的路径为 'M511.744 319.999l-383.744 383.744h767.488l-383.744-383.744z' ,第二个三角形的路径为 'M889.696 320.8H158.848l365.504 365.536 365.344-365.536z'
    9. x为x轴上的偏移量,y为y轴上的偏移量偏移量(第一个为35,第二个为5),其中宽高为10,layout为cover
    10. 给style的fill改为green
series: [
  {
    type: 'bar',
    name: '上月平台用户数',
    data: [data1],
    barWidth: 10,
    itemStyle: {
      color: 'green'
    },
    stack: '1'
  }, {
    type: 'bar',
    name: '本月平台用户数',
    data: [data2],
    itemStyle: {
      color: '#ddd'
    },
    barWidth: 10,
    stack: '1'
  }, {
    type: 'custom',
    renderItem: (params, api) => {
      // 绘制三角形(利用svg路径) 确定2个是: 确定三角形位置 如何绘制2个三角形本身
      const endPoint = api.coord([api.value(0), 0])
      return {
        type: 'group',
        children: [
          {
            type: 'path',
            shape: {
              d: 'M511.744 319.999l-383.744 383.744h767.488l-383.744-383.744z',
              x: endPoint[0] - 5,
              y: 35,
              width: 10,
              height: 10,
              layout: 'cover'
            },
            style: {
              fill: 'green'
            }
          },
          {
            type: 'path',
            shape: {
              d: 'M889.696 320.8H158.848l365.504 365.536 365.344-365.536z',
              x: endPoint[0] - 5,
              y: 5,
              width: 10,
              height: 10,
              layout: 'cover'
            },
            style: {
              fill: 'green'
            }
          }
        ]
      }
    },
    data: [data1]
  }

接口部分

写接口http://project.x-zd.net:3001/apis/reportdata

  1. 新建api文件夹,用来封装请求方法
  2. 新建axios.js,对axios进行封装,使用axios的create方法,提炼出URL,并增加响应时间
  3. 利用axios拦截器处理返回数据,只需要data数据
// 封装发送请求的axios
import axios from 'axios'
const request = axios.create({
  baseURL: 'http://project.x-zd.net:3001/apis',
  timeout: 3000
})
// 利用axios拦截器处理一下返回的数据(只想要返回的data字段)
request.interceptors.response.use((res) => {
  return res.data
}, (err) => { return Promise.reject(err) })
export default request
  1. index.js,中封装请求的具体方法,getRportDat
// 封装请求数据的具体方法
import request from './axios'
export const getReportData = () => request.get('/reportdata')
  1. 传入index中进行调用,存入数据,给四个卡片进行传入
import { getReportData } from '../../api'
export default {
  data () {
    return {
      reportData: {}
    }
  },
  components: { TotalSale, TotalOrder, TodayUser, TotalUser },
  async mounted () {
    const res = await getReportData()
    this.reportData = res
  }
}
<template>
    <div class="top-comp">
      <el-row :gutter="20">
        <el-col :span="6">
          <el-card shadow="hover">
              <TotalSale :reportData="reportData"/> <!-- 示例,分别传给四个组件 -->
  1. 在top的四个组件中接收接口传入的数据,将写死的数据改为接口中的数据,使用接受到的有值的值进行页面内容渲染
<template>
  <div class="total-user">
      <CommonCard title='累计用户数' :value="reportData.totalUser">
          <v-chart :option="option"/>
          <template #footer>
              <div class="wrapper">
                  <div>
                       <span>日同比</span>
                       <span class="css-1">{{reportData.userGrowLastDay}}%</span>
                       <span class="increase"></span>
                  </div>
                  <div>
                       <span>月同比</span>
                       <span class="css-1">{{reportData.userGrowLastMonth}}%</span>
                       <span class="increase"></span>
  1. 在有表格的组件中,给renderChart方法传入一个data参数,将表格中的数据改为传入的data
  2. 加载的时候在mounted中传入参数(是空对象,不传了),并且监听reportData,得到最新值,传入监听的reportData中renderChart方法
export default {
  mixins: [CommonCardMixin],
  data () {
    return {
      option: null
    }
  },
  methods: {
    renderChart (data1, data2) {
      this.option = {
        xAxis: {
          type: 'value',
          show: false
        },
        yAxis: {
          type: 'category',
          show: false
        },
        grid: {...},
        series: [
          {
            type: 'bar',
            name: '上月平台用户数',
            data: [data1],
            barWidth: 10,
            itemStyle: {
              color: 'green'
            },
            stack: '1'
          }, {
            type: 'bar',
            name: '本月平台用户数',
            data: [data2],
            itemStyle: {
              color: '#ddd'
            },
            barWidth: 10,
            stack: '1'
          }, {
            type: 'custom',
            ......
            },
            data: [data1]
          }
        ]
      }
    }
  },
  mounted () {
  // this.renderChart()
  },
  watch: {
    reportData (newValue) {
      this.renderChart(newValue.userLastMonth, newValue.userToday)
    }
  }
}

mixin公共逻辑书写

四个头部卡片中拥有一些相同逻辑,并且都接受了rportData,可以将相同的逻辑使用mixin(混入,用来提取公共逻辑)抽离出来。就不用再在不同的组件中重复书写相同的逻辑。

  1. 新建文件夹mixin文件夹,在其中写一个commoncardMixin.js
  2. 把公共配置放在这里(引入和传参
import CommonCard from '../components/TopComp/CommonCard.vue'
export default {
  props: ['reportData'],
  components: {
    CommonCard
  }
}
  1. 在各个组件中的data上面写,mixins: [CommonCardMixin],即可在每个组件中不写mixin中的内容(数组形式,可以写多个mixin)
import CommonCardMixin from '../../mixins/CommonCardMixin.js'

export default {
  mixins: [CommonCardMixin],
  data () {
    return {
      option: null
    }
  },

SecondComp

效果演示

第二栏接口数据

头部导航栏

  1. 现在index中书写第二栏的显示页面
    1. 在显示页面写头部插槽-card组件(不要用element自带的插槽,会多包一层div,使用template来写不用多包div)
<template>
    <div class="second-comp">
      <el-card class="box-card">
        <template #header>
          <el-menu
            :default-active="activeIndex"
            class="el-menu-demo"
            mode="horizontal"
            @select="handleSelect"
          >
            <el-menu-item index="1">销售额</el-menu-item>
            <el-menu-item index="2">访问量</el-menu-item>
          </el-menu>
  1. 使用NavMenu导航菜单组件,为导航栏写两个标签页切换。
    1. elsumenu为二级菜单,如果需要可以加上
    2. 修改标签页的内容
    3. disabled为禁用,此处不需要禁用
    4. 把activeIndex设定为1,为默认index为1的视图显示
export default {
  data () {
    return {
      activeIndex: '1',
    1. 为menu和menuItem注册按需注册element
import Vue from 'vue'
import 'element-ui/lib/theme-chalk/index.css'
import { Button, Input, Row, Col, Card, Menu, MenuItem, RadioButton, RadioGroup, DatePicker } from 'element-ui' 
Vue.use(Menu)
Vue.use(MenuItem)
Vue.use(RadioButton)
Vue.use(RadioGroup)
Vue.use(DatePicker)
    1. handleSelect绑定切换标签页事件,并修改标签页样式(上边距,去掉header的下边线)。element样式有时需要使用样式穿透
export default {
  data () {...},
  methods: {
    handleSelect (index) {
      this.activeIndex = index// 让选中的菜单激活
...},
  1. 使用radio单选框样式和DatePicker日期选择器,为头部导航栏写时间切换栏。
    1. 使用radio-group进行右侧导航栏书写,给右边导航栏套一个div,方便定位书写(写样式和定位
    2. 在element-ui中注册RadioButton和RadioGroup
    3. DatePicker日期选择器书写右侧组件的右侧部分
...
<template #header>
  ...
  <div class="right">
  <el-radio-group v-model="time">
  <el-radio-button label="今日"></el-radio-button>
  <el-radio-button label="本周"></el-radio-button>
  <el-radio-button label="本月"></el-radio-button>
  <el-radio-button label="今年"></el-radio-button>
  </el-radio-group>
  <el-date-picker
    v-model="pickerTime"
    type="daterange"
    align="right"
    unlink-panels
    range-separator="至"
    start-placeholder="开始日期"
    end-placeholder="结束日期"
    :picker-options="pickerOptions"
  >
  </el-date-picker>
</div>
</template>
    1. 在data中实现一下v-model绑定的pickerTime, PickerOptions则在element中已经写好可以直接使用
export default {
  data () {
    return {
      activeIndex: '1',
      time: '今日',
      pickerTime: '',
      pickerOptions: {
        shortcuts: [
          {
            text: '最近一周',
            onClick (picker) {
              const end = new Date()
              const start = new Date()
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
              picker.$emit('pick', [start, end])
            }
          },
          {
            text: '最近一个月',
            onClick (picker) {
              const end = new Date()
              const start = new Date()
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
              picker.$emit('pick', [start, end])
            }
          },
          {
            text: '最近三个月',
            onClick (picker) {
              const end = new Date()
              const start = new Date()
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
              picker.$emit('pick', [start, end])
            }
          }
        ]
      },

柱状图默认插槽

  1. index中写一个template,写默认插槽#default,下套一个div.content,再下套左右两个div,left和right
<template #default>
  <div class="content">
  	<div class="left-chart">
  		<v-chart :option="option" />
  	</div>
  	<div class="right-list">
  		<div class="list-title">排行榜</div>
  		<div class="list-item" v-for="item in rankData" :key="item.no" >
        <span :class="{'top-3':item.no<=3}">{{ item.no }}</span>
        <span>{{ item.title }}</span>
        <span>{{ item.sales }}</span>
  </div>
  </div>
  </div>
  </template>
  1. 在左边div中嵌套一个v-chart,并为其定义一个动态的:option="option",并在data中定义为一个空对象
export default {
  data () {
    return {...
      option: {},}}
  1. 给left和right写样式,左边:flex:0 0 70%,不拉伸不缩小,占据70%宽度。右边flex:1,占据剩余全部
.content {
  display: flex;
  .left-chart {
    flex: 0 0 70%;
    height: 434px;
  }
  .right-list {...}
}
  1. 在methods中写一个renderChart,其中option为图表方法
    1. 写title样式和文字
    2. 柱状图,不隐藏x轴(show=false不写),x轴为类目轴,需要data值,为1-12月的月份;y轴是value
    3. 由于该图表较大,暂时不写grid,最后视情况调整grid边框(left为40,
    4. 调整柱状图的样式(粗细,颜色,y轴的splitLine-背景轴线样式。
export default {
  data () {
    return {...}
  },
  methods: {
    handleSelect (index) {...},
    renderChart (data1, data2) {
      this.option = {
        title: {
          text: '年度销售额',
          textStyle: {
            fontWeight: 600,
            fontSize: 14
          }
        },
        xAxis: {
          type: 'category',
          data: data1,
          axisTick: {
            alignWithLabel: true
          }
        },
        yAxis: {
          type: 'value',
          splitLine: {
            lineStyle: {
              type: 'dotted'
            }
          }
        },
        grid: {
          left: 40
        },
        series: {
          type: 'bar',
          data: data2,
          barWidth: '40%'
        },
        color: 'skyblue'
      }
    }
  },
  async mounted () {
    const res = await getSaleData()
    this.saleData = res
    this.rankData = res.saleRank
    this.renderChart(this.saleData.saleFulleYearAxis, 
                     this.saleData.saleFulleYear)
  }
}
  1. 在mounted中调用this.renderChart()方法↑

右侧排行榜

  1. 在index中上部div——list-title
  2. 下部div——list-item,静态三个span,分别为排行,名称,数值
<template>
  <div class="second-comp">
    <el-card class="box-card">
      <template #header>....</template>
      <template #default>
        <div class="content">
          <div class="left-chart"><v-chart :option="option" /></div>
          <div class="right-list">
            <div class="list-title">排行榜</div>
            <div class="list-item" 
              v-for="item in rankData" 
              :key="item.no" >
              <span :class="{'top-3':item.no<=3}">{{ item.no }}</span>
              <span>{{ item.title }}</span>
              <span>{{ item.sales }}</span>
            </div>
          </div>
        </div>
      </template>
  1. 写样式
  2. span写弹性盒子flex布局,其中第二个span设置flex:1,占满剩余空间。第一个span有圆形框
.content {
  display: flex;
  .left-chart {...}
  .right-list {
    flex: 1;
    .list-title {
      margin-bottom: 10px;
      font-size: 14px;
      font-weight: 600;
    }
    .list-item {
      margin: 20px 0px;
      display: flex;
      gap: 20px;
      span {
        font-size: 14px;
        color: #464545;
      }
      span:nth-child(2) {
        flex: 1;
      }
      span:nth-child(1) {
        width: 20px;
        height: 20px;
        border-radius: 10px;
        text-align: center;
        line-height: 20px;
      }
      .top-3{
        background-color:#09b3f7 ;
        color:#fff
      }
    }
  }
}

封装接口数据

  1. 在api的index中封装接口数据
// 封装请求数据的具体方法
import request from './axios'
export const getReportData = () => request.get('/reportdata')
export const getSaleData = () => request.get('/saledata')
  1. 并在second中的mounted中调用一下(记得从api中引入
  2. 在data中注册saleData。
import { getSaleData } from '@/api'
export default {
  data () {
    return {
      activeIndex: '1',
      time: '今日',
      pickerTime: '',
      pickerOptions: {...},
      option: {},
      saleData: null,
      rankData: []
    }
  },
  1. 在list-item中遍历rankData(右边排行的data),并将写死的数据改为动态数据
  2. 在第一个span中绑定一个动态类名,在item.no<=3的时候,动态添加圆形框的背景颜色,字体颜色为白。
<div class="right-list">
  <div class="list-title">排行榜</div>
  <div class="list-item" v-for="item in rankData" :key="item.no" >
    <span :class="{'top-3':item.no<=3}">{{ item.no }}</span>
    <span>{{ item.title }}</span>
    <span>{{ item.sales }}</span>
  </div>
</div>
  1. 将图表数据替换为动态的data

点击标签切换图表视图

  1. handleSelect事件中,把index值赋值给activeIndex,让点选的菜单激活
  2. 如果index为1,则激活销售额视图,获取销售额视图的数据saleFulleYearAxis,并同步渲染到右边排行榜rankData
  3. else,获取访问量数据,放在方法中,激活访问量视图visitFullYeadAxis(切换仅仅切换Data,即可渲染不同的数据
export default {
  data () {
    return {
      ...
      saleData: null,
      rankData: []
    }
  },
  methods: {
    handleSelect (index) {
      this.activeIndex = index// 让选中的菜单激活
      if (index === '1') {
        this.rankData = this.saleData.saleRank
        this.renderChart(this.saleData.saleFulleYearAxis,
                         this.saleData.saleFulleYear)
      } else { // 此处yead为接口名写错,是可以使用的
        this.rankData = this.saleData.visitRank
        this.renderChart(this.saleData.visitFullYeadAxis,
                         this.saleData.visitFullYear)
      }
    },

ThirdComp

可以不拆成两个组件,统一写在项目结构中(也可以拆开写成两个组件,主要是看有没有别的地方也用了类似格式

整体布局

  1. 给根div类名third-comp,分两个div,left和right
  2. 先写third-comp基本样式css。css的>表示子代选择器,给左右两个宽度flex:1
.third-comp {
  margin-top: 20px;
  display: flex;
  gap: 20px;
  & > div {
    flex: 1;
  }
  1. 先给左边写el-card,给他添加hover。
  2. 在下面写一个header插槽,下面关键词搜索
  3. 再下一个main部分,写两个charts,一左一右,再一个table
<template>
    <div class="third-comp">
      <div class="left">
        <el-card shadow="hover">
          <template #header>
            <div>关键词搜索</div>
          </template>
          <div class="main">
            <div class="charts">
              <div class="left-chart">...</div>
              <div class="right-chart">...</div>
            </div>
            <div class="table">...</div>
          </div>
        </el-card>
      </div>
      <div class="right"></div>
    </div>
  </template>

左侧图表部分

  1. 先写左边的整体框架
    1. charts下面v-chart :option=option1,在data中空对象一个option1,右边同左,改为option2
<div class="main">
  <div class="charts">
    <div class="left-chart">
      <div class="title">搜索用户量</div>
      <div class="number">{{ totalUser }}</div>
      <v-chart :option="option1" />
    </div>
    <div class="right-chart">
      <div class="title">搜索量</div>
      <div class="number">{{ totalSearch }}</div>
      <v-chart :option="option2" />
    </div>
  </div>
    1. table组件和分页器(Pagination)组件在element中注册一下
import Vue from 'vue'
import 'element-ui/lib/theme-chalk/index.css'
import { ... Table, TableColumn, Pagination } from 'element-ui' 
...
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(Pagination)
    1. el-table,绑定一个:data=tableData,并在data中注册tableData为空数组
<div class="main">
  <div class="charts">
    <div class="left-chart">... </div>
    <div class="right-chart">... </div>
  </div>
  <div class="table">
    <el-table :data="tableData">
      <el-table-column></el-table-column>*4
    </el-table>
    1. 在分页器组件中选择有背景颜色的分页器组件来使用,total为总共的条目数,page-size为每页显示的个数,有多少页得到的是total/page-size的结果
<div class="main">
  <div class="charts">
    <div class="left-chart">... </div>
    <div class="right-chart">... </div>
  </div>
  <div class="table">
    <el-table :data="tableData">...</el-table>
    <el-pagination
      background
      layout="prev, pager, next"
      :total="20"
      :page-size="pageSize"
      @current-change="currentChange"
      >
    </el-pagination>
  </div>
</div>
  1. 获取左侧数据,并升序排序,制成表格
    1. 在api中引入网址,并在第三栏中从api中注册
import request from './axios' ...
export const getKeyWordData = () => request.get('/keyworddata')
    1. async mounted中const一个res,await注册的数据()
    2. 此时不能直接将数据传给tableData,需要注册一个totalData为空对象,并把res赋值给totalData,并做截取,初始状态下的totalData为totalData的前六个数据slice(0, 6)(6改为pageSize
export default {
  data () {...}
  },
  methods: {
    currentChange (page) {...}
  },
  async mounted () {
    const res = await getKeyWordData()
    this.totalData = res
    // 初始状态下,tableData显示前六条数据
    this.tableData = this.totalData.slice(0, this.pageSize)
  }
}
    1. 为视图中的el-table-column绑定不同的prop和label,prop来自于数据相对应的字段
    2. 调整第一个column的宽度,并让所有column居中显示文本
<el-table :data="tableData">
  <el-table-column prop="rank" 
    label="排名" width="60"></el-table-column>
  <el-table-column prop="keyWord" 
    label="关键词" align="center"></el-table-column>
  <el-table-column prop="totalSearch" 
    label='总搜索量' align="center"></el-table-column>
  <el-table-column prop="totalUser" 
    label="搜索用户数" align="center"></el-table-column>
</el-table>
    1. 写table的el-pagination的样式
    2. 在currentChange传入的page可以得到当前选中的页码,在点选后,需要重新修改渲染的tableData。需要提炼分页规律,在第一页时(0+1, 6)2(6+1, 12)3(12+1, 18)4(18+1, 24)
    3. 单独在el-pagination封装一个:pageSize,data中pageSize为6(pageSize在下面写成this调用模式,即可在data中修改pageSize来改变每页显示的条目数
<div class="table">
  <el-table :data="tableData">... </el-table>
  <el-pagination
    background
    layout="prev, pager, next"
    :total="20"
    :page-size="pageSize"
    @current-change="currentChange"
    >
  </el-pagination>
</div>
    1. 在currentChange中书写点选切换页码时列表切换的逻辑。该方法由组件库源码调用,不用自己调用
export default {
  data () {
    return {
      option1: {},
      option2: {},
      tableData: [],
      totalData: {},
      pageSize: 6
    }},
  methods: {
    currentChange (page) {
      // page为当前选中的页码
      this.tableData = this.totalData.slice(this.pageSize * (page - 1),
                                            this.pageSize * page)}},
  async mounted () {...}}
  1. 使用左侧数据,画有面积的折线图
    1. 在methods中写一个renderChart1(data),在其中写图
    2. option1,不要x轴,留白不要,小容器图表grid0,
    3. series中覆盖的区域写颜色,itemStyle透明度0,平滑smooth为true
export default {
  data () {
    return {
      option1: {},
      option2: {},
      tableData: [],
      totalData: {},
      pageSize: 6
    }
  },
  methods: {
    currentChange (page) {
      // page为当前选中的页码
      this.tableData = this.totalData.slice(this.pageSize * (page - 1), this.pageSize * page)
    },
    renderChart1 (data) {
      this.option1 = {
        xAxis: {
          type: 'category',
          show: false,
          boundaryGap: false
        },
        yAxis: {
          type: 'value',
          show: false
        },
        grid: {
          left: 0,
          right: 0,
          top: 0,
          bottom: 0
        },
        series: {
          type: 'line',
          data,
          areaStyle: {
            color: 'skyblue'
          },
          itemStyle: {
            opacity: 0
          },
          smooth: true
        }
      }
    },
    renderChart2 (data) {
      this.option2 = {
        xAxis: {
          type: 'category',
          show: false,
          boundaryGap: false
        },
        yAxis: {
          type: 'value',
          show: false
        },
        grid: {
          left: 0,
          right: 0,
          top: 0,
          bottom: 0
        },
        series: {
          type: 'line',
          data,
          areaStyle: {
            color: 'skyblue'
          },
          itemStyle: {
            opacity: 0
          },
          smooth: true
        }
      }
    }
  },
    1. 在mounted中执行以下renderChart1方法,传参为this.totalData,给数据调map方法,item中只需要totalUser字段,只要10个升序排列的数据(翻转)
async mounted () {
  ...
  this.renderChart1(this.totalData.map(item => item.totalUser).slice(0, 10).reverse())
  this.renderChart2(this.totalData.map(item => item.totalSearch).slice(0, 10).reverse())
}
    1. 在计算属性中写totalUser和totalSearch,使用reduce属性来计算,初始值为0,return(初始值需要和定义的类型相同,定义的是数组,初始值也要写成数组)
computed: {
  totalSearch () {
    return this.totalData.reduce((pre, cur) => {
      return pre + cur.totalSearch
    }, 0)
  },
  totalUser () {
    return this.totalData.reduce((pre, cur) => {
      return pre + cur.totalUser
    }, 0)
  }
},

右侧图表部分

整体框架

  1. right为一个带头部的饼状图
  2. 给template添加header插槽,在right中给右边卡片以及顶部插槽写定位
<template>
  <div class="third-comp">
    <div class="left">...</div>
    <div class="right">
      <el-card>
        <template #header>
    1. right的高度为100%,高度和左边持平
    2. el-card为组件内部样式,需要给它的header和body写样式穿透::v-deep
.right {
  .el-card {
    height: 100%;
    ::v-deep .el-card__body {
      height: 558px;
      .pie-chart {
        height: 100%;
      }
    }
    ::v-deep .el-card__header {
      position: relative;
      .el-radio-group {
        position: absolute;
        right: 2%;
        top: 10%;
      }
    }
  }
}
  1. el-radio-group下面有两个el-radio-button
  2. 在template下面写一个<div class="pie-chart">嵌套带:option的v-chard
<div class="right">
  <el-card>
    <template #header>
      <div class="css-2">分类销售排行</div>
      <el-radio-group v-model="radio" @input="handleRadio">
        <el-radio-button label="品类"></el-radio-button>
        <el-radio-button label="商品"></el-radio-button>
      </el-radio-group>
    </template>
    <div class="pie-chart">
      <v-chart :option="pieChartOption" />
    </div>
  </el-card>
</div>
  1. 在methods中写renderPirChart方法,传一个data
    1. 给一个副标题title(写成数组形式可以写多个),并给副标题一个left和top的偏移量
    2. 再写一个副标题,累计订单量,其中有一个subtext为计算属性,给偏移量
renderPieChart (data) {
  // 需要给data添加上一个name字段 从而可以让legend读到
  data = data.map((item) => {
    item.name = item.title + '|' + item.value
    return item
  })
  const totalSale = data.reduce((pre, cur) => {
    return pre + cur.value
  }, 0)
  this.pieChartOption = {
    title: [
      {
        text: '品类分布',
        textStyle: {
          fontSize: 14,
          color: '#666'
        },
        left: 20,
        top: 20
      },
      {
        text: '累计订单量',
        subtext: totalSale,
        x: '40%',
        y: '45%',
        textAlign: 'center',
        textStyle: {
          fontSize: 14,
          color: '#999'
        },
        subtextStyle: {
          fontSize: 28,
          color: '#333'
        }
      }
    ],
    1. 给series写一个符合当前视图的name,最后会显示在视图上。type为pie,定位为center,给center(环形的中心点位置)设置偏移量
    2. 给环状图设置label设置show,位置为outside,
renderPieChart (data) {
  // 需要给data添加上一个name字段 从而可以让legend读到
  data = data.map((item) => {
    item.name = item.title + '|' + item.value
    return item
  })
  const totalSale = data.reduce((pre, cur) => {
    return pre + cur.value
  }, 0)
  this.pieChartOption = {
    title: [...],
    series: {
      name: '品类分布',
      type: 'pie',
      data,
      radius: ['45%', '60%'],
      center: ['40%', '50%'],
      itemStyle: {
        borderWidth: 8,
        borderColor: '#fff'
      },
      label: {
        show: true,
        position: 'outside',
        formatter: (params) => {
          return params.data.title
        }
      }
    },
    1. title无法显示,由于数据变成了对象的进一步嵌套,为了得到每一个数据中的title,在文档中的lable中找formatter的params,通过回调函数得到params.data.title
    2. 在lable下面写tooltip提示框组件,触发类型中,把tirgger设置为item,再给它每一项根据数据的名字,写字符串拼接和圆点换行符的格式调整
renderPieChart (data) {
  // 需要给data添加上一个name字段 从而可以让legend读到
  data = data.map((item) => {
    item.name = item.title + '|' + item.value
    return item
  })
  const totalSale = data.reduce((pre, cur) => {
    return pre + cur.value
  }, 0)
  this.pieChartOption = {
    title: [...],
    series: {...},
    tooltip: {
      trigger: 'item',
      formatter: (params) => {
        return params.seriesName + '<br/>' + params.marker + params.data.title + '<br/>' + params.marker + '销售额' + params.data.value
      }
    },
    1. 在公共样式中消除边距并box-sizing: border-box;
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
  1. 在api中加载接口数据,引入组件,在async mounted中调用,传入需要数据的部分
async mounted () {
    const _res = await getCategoryData()
    this.categoryData = _res
    this.renderPieChart(this.categoryData.data1)
  }
  1. 在tooltip下面写legend(含二次处理数据
    1. 距离左边80%,改文本样式
    2. legend无法直接读取到data中的name属性(根本没有),所以给data添加一个name字段,从而让legend读取到
    3. 重新使用map遍历data,给item增加name字段,值为title与value的拼接
    4. 返回处理好的item
renderPieChart (data) {
  // 需要给data添加上一个name字段 从而可以让legend读到
  data = data.map((item) => {
    item.name = item.title + '|' + item.value
    return item
  })
  const totalSale = data.reduce((pre, cur) => {
    return pre + cur.value
  }, 0)
  this.pieChartOption = {
    title: [...],
    series: {...},
    tooltip: {...},
    legend: {
      // 会自动读取data数据的 name字段
      left: '80%',
      top: 'top',
      textStyle: {
        color: '#888'
      }
    }
  }
},
    1. let一个totalSale,在计算属性中,对item的value进行累加,return一个累加和
    2. 把subText的值替换为动态累加结果totalSale
computed: {
  totalSearch () {
    return this.totalData.reduce((pre, cur) => {
      return pre + cur.totalSearch
    }, 0)
  },
  totalUser () {
    return this.totalData.reduce((pre, cur) => {
      return pre + cur.totalUser
    }, 0)
  }
},
  1. 做radio-group中品类和商品的切换@input=handleRadio
    1. 在methods中写handleRadio方法,传参label
    2. if-else,选哪个就渲染相应的数据
export default {
  data () {...},
  methods: {
    handleRadio (label) {
      if (label === '品类') {
        this.renderPieChart(this.categoryData.data1)
      } else {
        this.renderPieChart(this.categoryData.data2)
      }
    }
  },

MapComp

创建应用

在百度地图开发者平台中的应用管理中创建应用

拆分为三个组件来写

将三个组件vue引入index,注册,写在视图中,添加样式

在需要绘图的组件上规定宽高,否则无法撑起盒子

地图

  1. 使用百度地图开发者平台,在public中的index里引入百度地图(百度地图引入指南
<head>
  ....
  <script src="https://api.map.baidu.com/api?v=2.0&ak=秘钥"></script>
</head>
  1. 新建BmapScatter.vue
  2. 在data中option:null
  3. 引入echarts中对百度地图的支持
<script>
import 'echarts/extension/bmap/bmap'
export default {
  1. 在methods中定义renderChart方法,初始化。
    1. 在方法中bmap的key中填写秘钥ak。
    2. 设置center,初始化时地图的显示中心(可以设置为咸阳 108.954355, 34.346721
    3. 设置zoom为5,初始缩放
    4. roam,默认是否能缩放,布尔值
    5. mounted中this.renderChart
export default {
  data () {
    return {
      option: null
    }
  },
  methods: {
    renderChart () {
      this.option = {
        bmap: {
          key: '秘钥',
          center: [108.954355, 34.346721],
          zoom: 5,
          roam: false, // 是否可以缩放
          mapStyle: {...}
        }
      }
    }
  },
  mounted () {
    this.renderChart()
  }
    1. 可以通过mapStyle使用JSON对象来设置地图风格(平时不需要加载,太大了
styleJson: [
  {
    featureType: 'water',
    elementType: 'all',
    stylers: {
      color: '#d1d1d1'
    }
  },
  {
    featureType: 'land',
    elementType: 'all',
    stylers: {
      color: '#f3f3f3'
    }
  },
  {
    featureType: 'railway',
    elementType: 'all',
    stylers: {
      visibility: 'off'
    }
  },
  {
    featureType: 'highway',
    elementType: 'all',
    stylers: {
      color: '#fdfdfd'
    }
  },
  {
    featureType: 'highway',
    elementType: 'labels',
    stylers: {
      visibility: 'off'
    }
  },
  {
    featureType: 'arterial',
    elementType: 'geometry',
    stylers: {
      color: '#fefefe'
    }
  },
  {
    featureType: 'arterial',
    elementType: 'geometry.fill',
    stylers: {
      color: '#fefefe'
    }
  },
  {
    featureType: 'poi',
    elementType: 'all',
    stylers: {
      visibility: 'off'
    }
  },
  {
    featureType: 'green',
    elementType: 'all',
    stylers: {
      visibility: 'off'
    }
  },
  {
    featureType: 'subway',
    elementType: 'all',
    stylers: {
      visibility: 'off'
    }
  },
  {
    featureType: 'manmade',
    elementType: 'all',
    stylers: {
      color: '#d1d1d1'
    }
  },
  {
    featureType: 'local',
    elementType: 'all',
    stylers: {
      color: '#d1d1d1'
    }
  },
  {
    featureType: 'arterial',
    elementType: 'labels',
    stylers: {
      visibility: 'off'
    }
  },
  {
    featureType: 'boundary',
    elementType: 'all',
    stylers: {
      color: '#fefefe'
    }
  },
  {
    featureType: 'building',
    elementType: 'all',
    stylers: {
      color: '#d1d1d1'
    }
  },
  {
    featureType: 'label',
    elementType: 'labels.text.fill',
    stylers: {
      color: '#999999'
    }
  }
]
  1. 给地图写title

散点图绘制

在地图上写散点图,title下面写。把series写为数组对象,一组普通散点,一组波纹散点

基础散点图

  1. 在配置选项手册里找coordinateSystem:bmap,意味着绘图的坐标系改为bmap
  2. type为scatter,并从后端传递data
export default {
  data () {
    return {
      option: null
    }
  },
  methods: {
    renderChart (data) {
      this.option = {
        bmap: {...},
        title: {
          text: '新中地网点地图',
          left: 'center'
        },
        series: [
          {
            name: '新中地外卖',
            coordinateSystem: 'bmap',
            type: 'scatter',
            data
          }, {}
        ]
  1. 在api中引入mapdata
import request from './axios' ...
export const getMapData = () => request.get('/mapdata')
  1. mapdata中返回了两组值,为城市和序号(city),城市名称和经纬度(geodata)
  2. 整合这两组数据,生成一个唯一的data。数组的每个对象中,有name:city,value[经度,纬度,销售额],最终将整合后的data传入renderChart,渲染该数据
  3. 在bmap引入getMapData
  4. async mounted中写一个整合数据的方法converData,由于仅在本组件中调用,可以在export前面直接function
import { getMapData } from '@/api'
function converData (city, geodata) {
  // city => [{name:'海门',value:10},{}...]
  // geodata => {'海门':[80,100],....}
  // res => [{name:'海门',value:[80,100,10]},...]
  const res = []
  city.forEach(item => {
    // item.name为城市名称,可以根据城市名称找到经纬度
    const geo = geodata[item.name] 
    if (geo) {
      res.push({
        name: item.name,
        value: geo.concat(item.value) // 拼接数组
      })
    }
  1. 在methods的series中添加encode和symbolSize两个属性共同控制散点图的出现与大小(value[2]/10是控制大小用的,每个大小只有0.2
methods: {
    renderChart (data) {
      this.option = {
        bmap: {...},
        title: {...},
        tooltip: {
          trigger: 'item'
        },
        series: [
          {
            name: '新中地外卖',
            coordinateSystem: 'bmap',
            type: 'scatter',
            data,
            encode: {
              value: 2
            },
            symbolSize (value) {
              return value[2] / 10
            }
          }, {}
        ]
  1. 给图添加tooltip,其中trigger为item,是数据中,对应的属性。

涟漪散点图

  1. 在series的第二个数组元素中写
    1. 起名
    2. 坐标系为bmap
    3. type类型effectScatter
    4. data数据做排序,截取销售额最高的10个点,制作涟漪效果(value[2]为销售额,0,1为经纬度
}, {
  name: '新中地外卖',
    coordinateSystem: 'bmap',
    type: 'effectScatter',
    data: data.sort((a, b) => {
    return b.value[2] - a.value[2]
  }).slice(0, 10),
    encode: {
    value: 2
  },
    1. rippleEffect中的brushType可以更改涟漪效果的样式,将样式更改为波纹的涟漪效果stroke
    2. 散点图的大小更改为[2]/10
    3. 更改波纹涟漪rippleEffect的color
symbolSize (value) {
  return value[2] / 10
},
  rippleEffect: {
    brushTypy: 'storke',
      color: 'purple'
  },
  1. 提示框tooltip
    1. formatter,拼接字符串,得到提示框内容
    2. 更改提示框字体颜色,改为绿色
export default {
  data () {},
  methods: {
    renderChart (data) {
      this.option = {...
        series: [
          {...}, {
            name: '新中地外卖',
            coordinateSystem: 'bmap',
            type: 'effectScatter',
            data: data.sort((a, b) => {
              return b.value[2] - a.value[2]
            }).slice(0, 10),
            encode: {
              value: 2
            },
            symbolSize (value) {
              return value[2] / 10
            },
            rippleEffect: {
              brushTypy: 'storke',
              color: 'purple'
            },
            tooltip: {
              formatter: (params) => {
                return params.data.name + '销售额' 
                  + params.data.value[2]
              },
              textStyle: {
                color: 'green'}}}]}}}}

水滴图

LiquidFill.vue

在npm中搜索npm install echarts-liquidfill

echarts-liquidfill - npm

  1. 安装包,并在liquidfill.vue中引入
<script>
import 'echarts-liquidfill'
export default {...
  1. 视图书写和data
  2. 在methods中封装renderChart方法
    1. this.option中series
    2. type为水球图,水球高度data0.6(60%)
    3. radius字体缩放?
    4. color写成数组,可以给多个波浪添加不同的颜色
    5. 振幅amplitude为4%
    6. 更改outline的样式
export default {
  data () {
    return {
      option: {}
    }
  },
  methods: {
    renderChart (data) {
      this.option = {
        series: {
          type: 'liquidFill',
          data: [data],
          radius: '80%',
          color: ['red'],
          amplitude: '4%',
          outline: {
            borderDistance: 2,
            itemStyle: {
              borderWidth: 2
            }
            ...
  1. 在mounted中调用一下
    1. 在import中调用封装好的data{getReportData}
    2. 写成异步形式
    3. 调用this.renderChart把传入的数据做简单处理并转为数值型(传入时为字符串型
    4. 除以100再保留两位,处理为水滴图可以接收的数值(0.02
<script>
import { getReportData } from '@/api'
export default {
  ...
   async mounted () {
    const res = await getReportData()
    this.renderChart((+res.salesGrowLastDay / 100).toFixed(2))
  }
}

词云图

WordCloud.vue

npm install echarts-wordcloud

echarts-wordcloud

在仓库地址中寻找使用说明

  1. 安装包,并在wordcloud.vue中引入
<script>
import 'echarts-wordcloud'
export default {...
  1. 视图书写和data
<template>
    <v-chart :option="option" />
</template>
    1. 类型为词云图
    2. shape为cardioid(可以根据说明改变
    3. 词云图的数据为搜索量最高的几个数据,可以直接引入
<script>
import 'echarts-wordcloud'
import { getKeyWordData } from '@/api'
export default {
  data () {
    return {
      option: {}
    }
  },
  methods: {
    renderChart (data) {
      this.option = {
        series: {
          type: 'wordCloud',
          shape: 'cardioid',
          data,
          ...
  1. 异步引用方法
  2. 传入数据getKeyWordData
    1. 词云图插件规定传入的data必须为数组,每个数组项目必须拥有一个name字段和value字段
    2. 但传入的数据getKeyWordData中没有这两个字段,需要改传入数据的字段
<script>
import 'echarts-wordcloud'
import { getKeyWordData } from '@/api'
export default {
  data () {...},
  methods: {...},
  async mounted () {
    let res = await getKeyWordData()
    res = res.slice(0, 6).map(item => {
      return {
        name: item.keyWord,
        value: item.totalSearch
      }
    })
    this.renderChart(res)
  }
}
</script>
  1. 修改词云图的样式
    1. 宽度高度
    2. 颜色为随机数写法(随机rgb颜色)
  1. 鼠标移入样式tooltip
  2. 鼠标移入样式其他隐藏的阴影效果
    1. emphasis属性,直接从文档中拷贝
export default {
  data () {...},
  methods: {
    renderChart (data) {
      this.option = {
        series: {
          type: 'wordCloud',
          shape: 'cardioid',
          data,
          width: '100%',
          height: '100%',
          textStyle: {
            // Color can be a callback function or a color string
            color: function () {
              // Random color
              return 'rgb(' + [
                Math.round(Math.random() * 160),
                Math.round(Math.random() * 160),
                Math.round(Math.random() * 160)
              ].join(',') + ')'
            }
          },
          emphasis: {
            focus: 'self',
            textStyle: {
              textShadowBlur: 5,
              textShadowColor: '#333'
            }
          }
        },
        tooltip: {} // 空数组即可,只要出现
      }
    }
  },

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值