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


    建项

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

    可视化报表项目效果

    开源表格样式库阿帕奇

    绘制echarte图标的流程

    1. 在视图中放置一个容器,这个容器需要有一个固定的宽高
    2. 获取容器,调用init方法,初始化echarts实例
    1. let container = document.querySelector('.app')
    2. let myChart = echarte.init(container) // 初始化图表实例
    1. 给实例调用setOption方法传入配置对象,通过这个配置对象确认绘制的图表内容(在官网寻找示例,根据示例
    1. <body>
    2. <div class="app" style="width: 600px;height: 400px;">
    3. </div>
    4. <script src="
    5. https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js
    6. "></script>
    7. <script>
    8. console.log(echarts, 123)
    9. // 绘制echarts图标的流程
    10. let container = document.querySelector('.app')
    11. let myChart = echarts.init(container) // 初始化图表实例
    12. myChart.setOption({
    13. // 传入配置对象,通过这个配置对象确认绘制的图表内容
    14. xAxis: {
    15. // 默认情况下,x粥通常为category类型,y轴一般为value
    16. type: 'category',
    17. data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    18. },
    19. yAxis: {
    20. type: 'value'
    21. },
    22. // series决定绘制一个什么类型的图表
    23. series: [ // 一个框只绘制一个图像时可以不用写成数组
    24. {
    25. data: [820, 932, 901, 934, 1290, 1330, 1320],/* 数据 */
    26. type: 'line', /* 数据显示类型 */
    27. areaStyle:{
    28. color:"purple",
    29. opacity:1,/* 颜色透明度 */
    30. },
    31. smooth: true, /* 光滑 */
    32. itemStyle:{
    33. opacity:0 // 点透明度
    34. },
    35. }
    36. ]
    37. })
    38. </script>
    39. </body>

    修改图表样式

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

    1. myChart.setOption({
    2. // 传入配置对象,通过这个配置对象确认绘制的图表内容
    3. xAxis: {
    4. // 默认情况下,x粥通常为category类型,y轴一般为value
    5. type: 'category',
    6. data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    7. },
    8. yAxis: {
    9. type: 'value',
    10. splitLine:{
    11. show: false // 去掉柱状图背景的分隔线
    12. }
    13. },
    14. // series决定绘制一个什么类型的图表
    15. series: [
    16. {
    17. data: [820, 932, 901, 934, 1290, 1330, 1320],/* 数据 */
    18. type: 'bar', /* 数据显示类型 */
    19. }
    20. ],
    21. tooltip:{}, // 鼠标移上去显示标签
    22. })

    从 npm 获取 echarts

    npm install echarts

    Vue2项目

    开始前准备

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

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

    1. import Vue from 'vue'
    2. import App from './App.vue'
    3. import * as echarts from 'echarts'
    4. import vcharts from 'vue-echarts'
    5. Vue.component('v-chart', vcharts) // 注册全局组件v-chart
    6. Vue.config.productionTip = false
    7. // 给vue的prototype原型对象挂载的属性,可以在入口中给每个组件实例都注入一些成员
    8. Vue.prototype.$echarts = echarts
    9. new Vue({
    10. render: h => h(App)
    11. }).$mount('#app')

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

    改为了

    input为v-model绑定的data

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

    1. <div class="wrapper">
    2. <div class="container" >
    3. <v-chart :option="option"></v-chart>
    4. </div>
    5. <el-button>这是按钮</el-button>
    6. <el-input v-model="input" placeholder="请输入内容"></el-input>
    7. </div>

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

    1. // 全局注册组件
    2. npm install vue-echarts
    3. // ui组件vue2和vue3
    4. npm i element-ui
    5. npm i element-plus
    6. // reset-css 初始化全局样式组件
    7. 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)
    1. // main
    2. import Vue from 'vue'
    3. import App from './App.vue'
    4. import * as echarts from 'echarts'
    5. import './plugins/element-ui' // 引入组件库
    6. import './plugins/vue-echarts'
    7. import 'reset-css' // 引入清除默认样式css
    8. Vue.config.productionTip = false
    9. // 给vue的prototype原型对象挂载的属性,可以在入口中给每个组件实例都注入一些成员
    10. Vue.prototype.$echarts = echarts
    11. new Vue({
    12. render: h => h(App)
    13. }).$mount('#app')
    14. // element-ui 实现组件的按需引入
    15. import Vue from 'vue'
    16. import 'element-ui/lib/theme-chalk/index.css'
    17. import { Button, Input } from 'element-ui' // 按需引入el的组件
    18. Vue.use(Button)
    19. Vue.use(Input) // 组件引入后需要在下面注册一下
    20. // vue-echarts 实现组件的全局注册
    21. import Vue from 'vue'
    22. import vcharts from 'vue-echarts'
    23. Vue.component('v-chart', vcharts) // 注册全局组件v-chart

    手动去除大驼峰规则

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

    Topcomp

    公共部分

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

    TotalSale

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

    1. <template>
    2. <div class="total-sale">
    3. <CommonCard title="累计销售额" :value="reportData.salesToday">
    4. <div>
    5. <span>日同比</span>
    6. <span class="css-1">{{reportData.salesGrowLastDay}}%</span>
    7. <span class="increase"></span>
    8. </div>
    9. <div>
    10. <span>月同比</span>
    11. <span class="css-1">{{reportData.saleSGrowLastMonth}}%</span>
    12. <span class="increase"></span>
    13. </div>
    14. <template #footer>
    15. <span>昨日销售额</span>
    16. <span class="css-1">¥{{reportData.salesLastDay }}</span>
    17. </template>
    18. </CommonCard>
    19. </div>
    20. </template>

    TotalOrder

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

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

    TodayUser

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

    1. 显示footer的文字,文字-今日用户交易数
    2. 规定图像为柱状图
    3. grid限定加上,规定series,type为bar,规定name,传入data
    1. <template>
    2. <div class="today-user">
    3. <CommonCard title="今日用户交易数" :value="reportData.userToday">
    4. <v-chart :option="option"/>
    5. <template #footer>
    6. <span>退货率</span>
    7. <span class="css-1">{{reportData.returnRate}}%</span>
    8. </template>
    9. </CommonCard>
    10. </div>
    11. </template>
    12. <script>
    13. import CommonCardMixin from '../../mixins/CommonCardMixin.js'
    14. export default {
    15. mixins: [CommonCardMixin],
    16. data () {
    17. return {
    18. option: null
    19. }
    20. },
    21. methods: {
    22. renderChart (data) {
    23. this.option = {
    24. xAxis: {
    25. type: 'category',
    26. show: false,
    27. data: [
    28. '00:00',
    29. '03:00',
    30. '05:00',
    31. '07:00',
    32. '09:00',
    33. '11:00',
    34. '13:00',
    35. '15:00',
    36. '17:00',
    37. '19:00',
    38. '21:00',
    39. '23:00'
    40. ]
    41. },
    42. yAxis: {
    43. type: 'value',
    44. show: false
    45. },
    46. tooltip: {},
    47. grid: {
    48. left: 0,
    49. right: 0,
    50. top: 0,
    51. bottom: 0
    52. },
    53. series: {
    54. type: 'bar',
    55. name: '实时交易量',
    56. data,
    57. barWidth: '60%'
    58. }
    59. }
    60. }
    61. },
    62. mounted () {},
    63. watch: {
    64. reportData (newValue) {
    65. this.renderChart(newValue.orderUserTrend)
    66. }
    67. }
    68. }

    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
    1. series: [
    2. {
    3. type: 'bar',
    4. name: '上月平台用户数',
    5. data: [data1],
    6. barWidth: 10,
    7. itemStyle: {
    8. color: 'green'
    9. },
    10. stack: '1'
    11. }, {
    12. type: 'bar',
    13. name: '本月平台用户数',
    14. data: [data2],
    15. itemStyle: {
    16. color: '#ddd'
    17. },
    18. barWidth: 10,
    19. stack: '1'
    20. }, {
    21. type: 'custom',
    22. renderItem: (params, api) => {
    23. // 绘制三角形(利用svg路径) 确定2个是: 确定三角形位置 如何绘制2个三角形本身
    24. const endPoint = api.coord([api.value(0), 0])
    25. return {
    26. type: 'group',
    27. children: [
    28. {
    29. type: 'path',
    30. shape: {
    31. d: 'M511.744 319.999l-383.744 383.744h767.488l-383.744-383.744z',
    32. x: endPoint[0] - 5,
    33. y: 35,
    34. width: 10,
    35. height: 10,
    36. layout: 'cover'
    37. },
    38. style: {
    39. fill: 'green'
    40. }
    41. },
    42. {
    43. type: 'path',
    44. shape: {
    45. d: 'M889.696 320.8H158.848l365.504 365.536 365.344-365.536z',
    46. x: endPoint[0] - 5,
    47. y: 5,
    48. width: 10,
    49. height: 10,
    50. layout: 'cover'
    51. },
    52. style: {
    53. fill: 'green'
    54. }
    55. }
    56. ]
    57. }
    58. },
    59. data: [data1]
    60. }

    接口部分

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

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

    mixin公共逻辑书写

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

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

    SecondComp

    效果演示

    第二栏接口数据

    头部导航栏

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

    柱状图默认插槽

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

    右侧排行榜

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

    封装接口数据

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

    点击标签切换图表视图

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

    ThirdComp

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

    整体布局

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

    左侧图表部分

    1. 先写左边的整体框架
      1. charts下面v-chart :option=option1,在data中空对象一个option1,右边同左,改为option2
    1. <div class="main">
    2. <div class="charts">
    3. <div class="left-chart">
    4. <div class="title">搜索用户量</div>
    5. <div class="number">{{ totalUser }}</div>
    6. <v-chart :option="option1" />
    7. </div>
    8. <div class="right-chart">
    9. <div class="title">搜索量</div>
    10. <div class="number">{{ totalSearch }}</div>
    11. <v-chart :option="option2" />
    12. </div>
    13. </div>
      1. table组件和分页器(Pagination)组件在element中注册一下
    1. import Vue from 'vue'
    2. import 'element-ui/lib/theme-chalk/index.css'
    3. import { ... Table, TableColumn, Pagination } from 'element-ui'
    4. ...
    5. Vue.use(Table)
    6. Vue.use(TableColumn)
    7. Vue.use(Pagination)
      1. el-table,绑定一个:data=tableData,并在data中注册tableData为空数组
    1. <div class="main">
    2. <div class="charts">
    3. <div class="left-chart">... </div>
    4. <div class="right-chart">... </div>
    5. </div>
    6. <div class="table">
    7. <el-table :data="tableData">
    8. <el-table-column></el-table-column>*4
    9. </el-table>
      1. 在分页器组件中选择有背景颜色的分页器组件来使用,total为总共的条目数,page-size为每页显示的个数,有多少页得到的是total/page-size的结果
    1. <div class="main">
    2. <div class="charts">
    3. <div class="left-chart">... </div>
    4. <div class="right-chart">... </div>
    5. </div>
    6. <div class="table">
    7. <el-table :data="tableData">...</el-table>
    8. <el-pagination
    9. background
    10. layout="prev, pager, next"
    11. :total="20"
    12. :page-size="pageSize"
    13. @current-change="currentChange"
    14. >
    15. </el-pagination>
    16. </div>
    17. </div>
    1. 获取左侧数据,并升序排序,制成表格
      1. 在api中引入网址,并在第三栏中从api中注册
    1. import request from './axios' ...
    2. export const getKeyWordData = () => request.get('/keyworddata')
      1. async mounted中const一个res,await注册的数据()
      2. 此时不能直接将数据传给tableData,需要注册一个totalData为空对象,并把res赋值给totalData,并做截取,初始状态下的totalData为totalData的前六个数据slice(0, 6)(6改为pageSize
    1. export default {
    2. data () {...}
    3. },
    4. methods: {
    5. currentChange (page) {...}
    6. },
    7. async mounted () {
    8. const res = await getKeyWordData()
    9. this.totalData = res
    10. // 初始状态下,tableData显示前六条数据
    11. this.tableData = this.totalData.slice(0, this.pageSize)
    12. }
    13. }
      1. 为视图中的el-table-column绑定不同的prop和label,prop来自于数据相对应的字段
      2. 调整第一个column的宽度,并让所有column居中显示文本
    1. <el-table :data="tableData">
    2. <el-table-column prop="rank"
    3. label="排名" width="60"></el-table-column>
    4. <el-table-column prop="keyWord"
    5. label="关键词" align="center"></el-table-column>
    6. <el-table-column prop="totalSearch"
    7. label='总搜索量' align="center"></el-table-column>
    8. <el-table-column prop="totalUser"
    9. label="搜索用户数" align="center"></el-table-column>
    10. </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来改变每页显示的条目数
    1. <div class="table">
    2. <el-table :data="tableData">... </el-table>
    3. <el-pagination
    4. background
    5. layout="prev, pager, next"
    6. :total="20"
    7. :page-size="pageSize"
    8. @current-change="currentChange"
    9. >
    10. </el-pagination>
    11. </div>
      1. 在currentChange中书写点选切换页码时列表切换的逻辑。该方法由组件库源码调用,不用自己调用
    1. export default {
    2. data () {
    3. return {
    4. option1: {},
    5. option2: {},
    6. tableData: [],
    7. totalData: {},
    8. pageSize: 6
    9. }},
    10. methods: {
    11. currentChange (page) {
    12. // page为当前选中的页码
    13. this.tableData = this.totalData.slice(this.pageSize * (page - 1),
    14. this.pageSize * page)}},
    15. async mounted () {...}}
    1. 使用左侧数据,画有面积的折线图
      1. 在methods中写一个renderChart1(data),在其中写图
      2. option1,不要x轴,留白不要,小容器图表grid0,
      3. series中覆盖的区域写颜色,itemStyle透明度0,平滑smooth为true
    1. export default {
    2. data () {
    3. return {
    4. option1: {},
    5. option2: {},
    6. tableData: [],
    7. totalData: {},
    8. pageSize: 6
    9. }
    10. },
    11. methods: {
    12. currentChange (page) {
    13. // page为当前选中的页码
    14. this.tableData = this.totalData.slice(this.pageSize * (page - 1), this.pageSize * page)
    15. },
    16. renderChart1 (data) {
    17. this.option1 = {
    18. xAxis: {
    19. type: 'category',
    20. show: false,
    21. boundaryGap: false
    22. },
    23. yAxis: {
    24. type: 'value',
    25. show: false
    26. },
    27. grid: {
    28. left: 0,
    29. right: 0,
    30. top: 0,
    31. bottom: 0
    32. },
    33. series: {
    34. type: 'line',
    35. data,
    36. areaStyle: {
    37. color: 'skyblue'
    38. },
    39. itemStyle: {
    40. opacity: 0
    41. },
    42. smooth: true
    43. }
    44. }
    45. },
    46. renderChart2 (data) {
    47. this.option2 = {
    48. xAxis: {
    49. type: 'category',
    50. show: false,
    51. boundaryGap: false
    52. },
    53. yAxis: {
    54. type: 'value',
    55. show: false
    56. },
    57. grid: {
    58. left: 0,
    59. right: 0,
    60. top: 0,
    61. bottom: 0
    62. },
    63. series: {
    64. type: 'line',
    65. data,
    66. areaStyle: {
    67. color: 'skyblue'
    68. },
    69. itemStyle: {
    70. opacity: 0
    71. },
    72. smooth: true
    73. }
    74. }
    75. }
    76. },
      1. 在mounted中执行以下renderChart1方法,传参为this.totalData,给数据调map方法,item中只需要totalUser字段,只要10个升序排列的数据(翻转)
    1. async mounted () {
    2. ...
    3. this.renderChart1(this.totalData.map(item => item.totalUser).slice(0, 10).reverse())
    4. this.renderChart2(this.totalData.map(item => item.totalSearch).slice(0, 10).reverse())
    5. }
      1. 在计算属性中写totalUser和totalSearch,使用reduce属性来计算,初始值为0,return(初始值需要和定义的类型相同,定义的是数组,初始值也要写成数组)
    1. computed: {
    2. totalSearch () {
    3. return this.totalData.reduce((pre, cur) => {
    4. return pre + cur.totalSearch
    5. }, 0)
    6. },
    7. totalUser () {
    8. return this.totalData.reduce((pre, cur) => {
    9. return pre + cur.totalUser
    10. }, 0)
    11. }
    12. },

    右侧图表部分

    整体框架

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

    MapComp

    创建应用

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

    拆分为三个组件来写

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

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

    地图

    1. 使用百度地图开发者平台,在public中的index里引入百度地图(百度地图引入指南
    1. <head>
    2. ....
    3. <script src="https://api.map.baidu.com/api?v=2.0&ak=秘钥"></script>
    4. </head>
    1. 新建BmapScatter.vue
    2. 在data中option:null
    3. 引入echarts中对百度地图的支持
    1. <script>
    2. import 'echarts/extension/bmap/bmap'
    3. export default {
    1. 在methods中定义renderChart方法,初始化。
      1. 在方法中bmap的key中填写秘钥ak。
      2. 设置center,初始化时地图的显示中心(可以设置为咸阳 108.954355, 34.346721
      3. 设置zoom为5,初始缩放
      4. roam,默认是否能缩放,布尔值
      5. mounted中this.renderChart
    1. export default {
    2. data () {
    3. return {
    4. option: null
    5. }
    6. },
    7. methods: {
    8. renderChart () {
    9. this.option = {
    10. bmap: {
    11. key: '秘钥',
    12. center: [108.954355, 34.346721],
    13. zoom: 5,
    14. roam: false, // 是否可以缩放
    15. mapStyle: {...}
    16. }
    17. }
    18. }
    19. },
    20. mounted () {
    21. this.renderChart()
    22. }
      1. 可以通过mapStyle使用JSON对象来设置地图风格(平时不需要加载,太大了
    1. styleJson: [
    2. {
    3. featureType: 'water',
    4. elementType: 'all',
    5. stylers: {
    6. color: '#d1d1d1'
    7. }
    8. },
    9. {
    10. featureType: 'land',
    11. elementType: 'all',
    12. stylers: {
    13. color: '#f3f3f3'
    14. }
    15. },
    16. {
    17. featureType: 'railway',
    18. elementType: 'all',
    19. stylers: {
    20. visibility: 'off'
    21. }
    22. },
    23. {
    24. featureType: 'highway',
    25. elementType: 'all',
    26. stylers: {
    27. color: '#fdfdfd'
    28. }
    29. },
    30. {
    31. featureType: 'highway',
    32. elementType: 'labels',
    33. stylers: {
    34. visibility: 'off'
    35. }
    36. },
    37. {
    38. featureType: 'arterial',
    39. elementType: 'geometry',
    40. stylers: {
    41. color: '#fefefe'
    42. }
    43. },
    44. {
    45. featureType: 'arterial',
    46. elementType: 'geometry.fill',
    47. stylers: {
    48. color: '#fefefe'
    49. }
    50. },
    51. {
    52. featureType: 'poi',
    53. elementType: 'all',
    54. stylers: {
    55. visibility: 'off'
    56. }
    57. },
    58. {
    59. featureType: 'green',
    60. elementType: 'all',
    61. stylers: {
    62. visibility: 'off'
    63. }
    64. },
    65. {
    66. featureType: 'subway',
    67. elementType: 'all',
    68. stylers: {
    69. visibility: 'off'
    70. }
    71. },
    72. {
    73. featureType: 'manmade',
    74. elementType: 'all',
    75. stylers: {
    76. color: '#d1d1d1'
    77. }
    78. },
    79. {
    80. featureType: 'local',
    81. elementType: 'all',
    82. stylers: {
    83. color: '#d1d1d1'
    84. }
    85. },
    86. {
    87. featureType: 'arterial',
    88. elementType: 'labels',
    89. stylers: {
    90. visibility: 'off'
    91. }
    92. },
    93. {
    94. featureType: 'boundary',
    95. elementType: 'all',
    96. stylers: {
    97. color: '#fefefe'
    98. }
    99. },
    100. {
    101. featureType: 'building',
    102. elementType: 'all',
    103. stylers: {
    104. color: '#d1d1d1'
    105. }
    106. },
    107. {
    108. featureType: 'label',
    109. elementType: 'labels.text.fill',
    110. stylers: {
    111. color: '#999999'
    112. }
    113. }
    114. ]
    1. 给地图写title

    散点图绘制

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

    基础散点图

    1. 在配置选项手册里找coordinateSystem:bmap,意味着绘图的坐标系改为bmap
    2. type为scatter,并从后端传递data
    1. export default {
    2. data () {
    3. return {
    4. option: null
    5. }
    6. },
    7. methods: {
    8. renderChart (data) {
    9. this.option = {
    10. bmap: {...},
    11. title: {
    12. text: '新中地网点地图',
    13. left: 'center'
    14. },
    15. series: [
    16. {
    17. name: '新中地外卖',
    18. coordinateSystem: 'bmap',
    19. type: 'scatter',
    20. data
    21. }, {}
    22. ]
    1. 在api中引入mapdata
    1. import request from './axios' ...
    2. 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
    1. import { getMapData } from '@/api'
    2. function converData (city, geodata) {
    3. // city => [{name:'海门',value:10},{}...]
    4. // geodata => {'海门':[80,100],....}
    5. // res => [{name:'海门',value:[80,100,10]},...]
    6. const res = []
    7. city.forEach(item => {
    8. // item.name为城市名称,可以根据城市名称找到经纬度
    9. const geo = geodata[item.name]
    10. if (geo) {
    11. res.push({
    12. name: item.name,
    13. value: geo.concat(item.value) // 拼接数组
    14. })
    15. }
    1. 在methods的series中添加encode和symbolSize两个属性共同控制散点图的出现与大小(value[2]/10是控制大小用的,每个大小只有0.2
    1. methods: {
    2. renderChart (data) {
    3. this.option = {
    4. bmap: {...},
    5. title: {...},
    6. tooltip: {
    7. trigger: 'item'
    8. },
    9. series: [
    10. {
    11. name: '新中地外卖',
    12. coordinateSystem: 'bmap',
    13. type: 'scatter',
    14. data,
    15. encode: {
    16. value: 2
    17. },
    18. symbolSize (value) {
    19. return value[2] / 10
    20. }
    21. }, {}
    22. ]
    1. 给图添加tooltip,其中trigger为item,是数据中,对应的属性。

    涟漪散点图

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

    水滴图

    LiquidFill.vue

    在npm中搜索npm install echarts-liquidfill

    echarts-liquidfill - npm

    1. 安装包,并在liquidfill.vue中引入
    1. <script>
    2. import 'echarts-liquidfill'
    3. 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的样式
    1. export default {
    2. data () {
    3. return {
    4. option: {}
    5. }
    6. },
    7. methods: {
    8. renderChart (data) {
    9. this.option = {
    10. series: {
    11. type: 'liquidFill',
    12. data: [data],
    13. radius: '80%',
    14. color: ['red'],
    15. amplitude: '4%',
    16. outline: {
    17. borderDistance: 2,
    18. itemStyle: {
    19. borderWidth: 2
    20. }
    21. ...
    1. 在mounted中调用一下
      1. 在import中调用封装好的data{getReportData}
      2. 写成异步形式
      3. 调用this.renderChart把传入的数据做简单处理并转为数值型(传入时为字符串型
      4. 除以100再保留两位,处理为水滴图可以接收的数值(0.02
    1. <script>
    2. import { getReportData } from '@/api'
    3. export default {
    4. ...
    5. async mounted () {
    6. const res = await getReportData()
    7. this.renderChart((+res.salesGrowLastDay / 100).toFixed(2))
    8. }
    9. }

    词云图

    WordCloud.vue

    npm install echarts-wordcloud

    echarts-wordcloud

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

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

  • 相关阅读:
    java学习day31(redis3)通用命令
    速卖通、阿里巴巴国际站测评补单技巧分享,需要哪些技术要求
    GEE开发之Landsat8计算NDWI和数据分析
    代码随想录算法训练营Day35 | 贪心算法(4/6) LeetCode 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球
    y115.第七章 服务网格与治理-Istio从入门到精通 -- 云原生概念(一)
    Python案例|使用Scikit-learn实现客户聚类模型
    薄元近似(TEA)与傅立叶模态方法(FMM)在光栅建模中的对比
    架构核心技术之微服务架构
    9.15c++基础
    JAVA常见基础面试问题汇集
  • 原文地址:https://blog.csdn.net/albedo_102/article/details/132928141