03菜单栏+首页

布局-vuex保存用户信息

目标

能够把登录后返回信息保存到vuex中

讲解

  1. 在src/api/index.js模块中, 定义接口方法, 用于请求用户信息数据

import store from '@/store'

/**
 * 获取-用户信息
 * @returns Promise对象
 */
export const getUserInfoAPI = () => {
  return request({
    url: '/my/userinfo',
    headers: {
      Authorization: store.state.token
    }
  })
}
  1. 在 src/store/index.js 模块中,使用接口方法+actions获取用户的基本信息

action里边可以调用方法 : 要么在逻辑页面直接发;要么在action写方法,在逻辑页面派发

import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
// 导入axios
import { getUserInfoAPI } from '@/api'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    token: '', // 用户token
    userInfo: {} // 定义用户信息对象
  },
  mutations: {
    // 更新 token 的 mutation 函数
    updateToken (state, newToken) {
      state.token = newToken
    },
    // 更新用户的信息
    updateUserInfo (state, info) {
      state.userInfo = info
    }
  },
  actions: {
    // 定义初始化用户基本信息的 action 函数
    async initUserInfo (store) {
      const { data: res } = await getUserInfoAPI()
      if (res.code === 0) {
        store.commit('updateUserInfo', res.data)
      }
    }
  },
  // 配置为 vuex 的插件
  plugins: [createPersistedState()]
})

刷新网页,都会重新调用这个接口

所以,在layout组件中的created里边不合适:

解决方法: 路由守卫

在全局前置守卫, 做判断, 有token但是无userInfo信息, 才发请求拿用户信息, 在src/router/index.js中编写
因为不确定先进入的哪个页面, 但是全局前置路由守卫必执行
import store from '@/store'

router.beforeEach((to, from, next) => {
  const token = store.state.token
  if (token && !store.state.userInfo.username) {
    // 有token但是没有用户信息, 才去请求用户信息保存到vuex里,否则你退出在登陆也会走路由导航
    // 调用actions里方法请求数据
    store.dispatch('initUserInfo')
    // 下次切换页面vuex里有用户信息数据就不会重复请求用户信息
  }
​
  next() // 路由放行
})

  1. 最后效果就是, 登录后, 刷新后, 会在vue调试工具中查看vuex中userInfo有值即可

注意条件:
token && !store.state.userInfo.username

1有token: 避免退出再登录页面的时候,也会去校验
2  !store.state.userInfo.username  如果不加这个,只要路由跳转,都会走路由守卫,重复请求用户
   为了避免有用户信息 还重复调用用户信息

退出登录时候,也要吧用户信息清除

02布局-渲染用户信息

目标

把vuex中用户信息, 渲染到标签上显示

讲解

  1. 在src/views/layout/index.vue, 左侧侧边栏, 渲染用户信息昵称/用户名

<!-- 左侧边栏的用户信息 -->
<el-aside width="200px">
    <div class="user-box">
        <img :src="user_pic" alt="" v-if="user_pic" />
        <img src="../../assets/images/logo.png" alt="" v-else />
        <span>欢迎 {{ nickname || username }}</span>
    </div>
</el-aside>


<style lang="less" scoped>
// 左侧边栏用户信息区域
.user-box {
  height: 70px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-top: 1px solid #000;
  border-bottom: 1px solid #000;
  user-select: none;
  img {
    width: 35px;
    height: 35px;
    border-radius: 50%;
    background-color: #fff;
    margin-right: 15px;
    object-fit: cover;
  }
  span {
    color: white;
    font-size: 12px;
  }
}
</style>
  1. 在vuex中, 定义getters, 定义并导出用户名和昵称

getters: {
    nickname: state => state.userInfo.nickname, // 昵称
    username: state => state.userInfo.username, // 用户名
    user_pic: state => state.userInfo.user_pic // 用户头像
}

3 在src/views/layout/index.vue采用映射getters的方式定义计算属性

import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['nickname', 'username', 'user_pic'])
  }
}

或者:

03 侧边导航准备

目标

使用elementUI组件, 开发左侧导航

讲解

  1. 在 src/views/layout/index.vue组件的 el-aside范围内div标签兄弟关系,定义左侧菜单的 UI 布局结构

<!-- 左侧边栏的用户信息 -->
      <el-aside width="200px">
        <div class="user-box">
          <img :src="user_pic" alt="" v-if="user_pic" />
          <img src="../../assets/images/head.png" alt="" v-else />
          <span>欢迎 {{ nickname || username }}</span>
        </div>
        <!-- 左侧导航菜单 -->
        <el-menu
          default-active="1"
          class="el-menu-vertical-demo"
          background-color="#23262E"
          text-color="#fff"
          active-text-color="#409EFF"
          unique-opened
        >
          <!-- 不包含子菜单的“一级菜单” -->
          <el-menu-item index="1"
            ><i class="el-icon-s-tools"></i>一级菜单</el-menu-item
          >
          <!-- 包含子菜单的“一级菜单” -->
          <el-submenu index="2">
            <template slot="title">
              <i class="el-icon-s-tools"></i>
              <span>一级菜单</span>
            </template>
            <el-menu-item index="2-1"
              ><i class="el-icon-star-on"></i>二级菜单</el-menu-item
            >
            <el-menu-item index="2-2"
              ><i class="el-icon-star-on"></i>二级菜单</el-menu-item
            >
            <el-menu-item index="2-3"
              ><i class="el-icon-star-on"></i>二级菜单</el-menu-item
            >
          </el-submenu>
        </el-menu>
      </el-aside>

  1. 美化 Main.vue 组件的布局结构:

// 侧边栏菜单的样式
.el-aside {
  .el-submenu,
  .el-menu-item {
    width: 200px;
    user-select: none;
  }
}

04 布局-侧边栏导航数据

目标

从后台请求侧边栏标题等数据, 铺设侧边栏导航

讲解

  1. 在src/api/index.js封装请求侧边栏数据的接口方法

/**
 * 获取-侧边栏菜单数据
 * @returns Promise对象
 */
export const getMenusAPI = () => {
  return request({
    url: '/my/menus',
    headers: {
      Authorization: store.state.token
    }
  })
}
  1. 在src/views/layout/index.vue组件中, 引入接口方法, 发请求拿到数据, 保存到data变量中

import { getMenusAPI } from '@/api'
export default {
  // ...其他
  data () {
    return {
      menus: [] // 侧边栏数据
    }
  },
  created () {
    this.getMenusListFn()
  },
  methods: {
    // ...其他
    // 获取侧边栏菜单数据
    async getMenusListFn () {
      const { data: res } = await getMenusAPI()
      console.log(res)
      this.menus = res.data
    }
  }
}
  1. 循环渲染左侧的一级和二级菜单数据铺设

{
    "code":0,
    "message":"获取左侧菜单成功!",
    "data":[
        {
            "indexPath":"/home",
            "title":"首页",
            "icon":"el-icon-s-home",
            "children":null
        },
        {
            "indexPath":"2",
            "title":"文章管理",
            "icon":"el-icon-s-order",
            "children":[
                {
                    "indexPath":"/art-cate",
                    "title":"文章分类",
                    "icon":"el-icon-s-data"
                },
                {
                    "indexPath":"/art-list",
                    "title":"文章列表",
                    "icon":"el-icon-document-copy"
                }
            ]
        },
        {
            "indexPath":"3",
            "title":"个人中心",
            "icon":"el-icon-user-solid",
            "children":[
                {
                    "indexPath":"/user-info",
                    "title":"基本资料",
                    "icon":"el-icon-s-operation"
                },
                {
                    "indexPath":"/user-avatar",
                    "title":"更换头像",
                    "icon":"el-icon-camera"
                },
                {
                    "indexPath":"/user-pwd",
                    "title":"重置密码",
                    "icon":"el-icon-key"
                }
            ]
        }
    ]
}

 <!-- 左侧导航菜单 -->
          <el-menu default-active="1" class="el-menu-vertical-demo" background-color="#23262E" text-color="#fff"
            active-text-color="#409EFF" unique-opened>
            <template v-for="item in menus">
              <!-- 不包含子菜单的“一级菜单” -->
              <el-menu-item :index="item.indexPath" :key="item.indexPath" v-if="!item.children">
                <i :class="item.icon"></i>{{ item.title }}
              </el-menu-item>
              <!-- 包含子菜单的“一级菜单” -->
              <el-submenu :index="item.indexPath" :key="item.indexPath" v-else>
                <template slot="title">
                  <i :class="item.icon"></i>
                  <span>{{ item.title }}</span>
                </template>
                <!-- 循环渲染“二级菜单” -->
                <el-menu-item :index="subItem.indexPath" v-for="subItem in item.children" :key="subItem.indexPath">
                  <i :class="subItem.icon"></i>{{ subItem.title }}
                </el-menu-item>
              </el-submenu>
            </template>
          </el-menu>

  1. 修改 el-menu组件的 default-active属性,可以设置默认激活的左侧菜单, 为首页路由路径

<el-menu
    default-active="/home"
    class="el-menu-vertical-demo"
    background-color="#23262E"
    text-color="#fff"
    active-text-color="#409EFF"
    unique-opened
   ></el-menu>
  1. 为 el-menu 组件添加 router 属性,可以开启左侧菜单的路由模式

<el-menu
    default-active="/home"
    class="el-menu-vertical-demo"
    background-color="#23262E"
    text-color="#fff"
    active-text-color="#409EFF"
    unique-opened
    router
   ></el-menu>
  1. 点击左侧导航, 查看路由地址是否切换

注意:

如果想是header 和footer固定住,只需要给el-container 添加个高度即可

05.axios-请求拦截器

目标

掌握axios请求拦截器使用, 在其中统一携带token

讲解

  1. 正常接口方法里, 在函数里请求headers参数需要写多个, 要修改需要改多处, 所以我们决定在请求拦截器统一携带

  1. 在utils/request.js中, 使用自定义axios函数绑定请求拦截器, 判断有token再携带到请求头上

// 定义请求拦截器
myAxios.interceptors.request.use(function (config) {
  // 为请求头挂载 Authorization 字段
  config.headers.Authorization = store.state.token
  return config
}, function (error) {
  return Promise.reject(error)
})

3 这是一个后台管理类项目, 除了登录和注册接口后端不要求使用token, 其他接口都得携带token

判断请求地址 登录: /api/login 和 注册: /api/reg

// 白名单: 不需要携带token的api地址
const whiteAPIList = ['/api/reg', '/api/login']
// 定义请求拦截器
myAxios.interceptors.request.use(function (config) {
  if (!whiteAPIList.includes(config.url)) {
    // 为请求头挂载 Authorization 字段
    config.headers.Authorization = store.state.token
  }
  return config
}, function (error) {
  return Promise.reject(error)
})

或者:

4 发请求看看登录/获取菜单的network里, 请求头上是否有Authorization和token值携带

06权限-控制访问

目标

当用户未登录的时候, 是无法访问到这个内部项目的正常页面的

讲解

  1. 可以在路由全局前置守卫判断当前vuex里是否有token

有token值证明刚才登录过, 无token值证明未登录

router.beforeEach((to, from, next) => {
  const token = store.state.token
  if (token) {
    // 如果有token, 证明已登录
    if (!store.state.userInfo.username) {
      // 有token但是没有用户信息, 才去请求用户信息保存到vuex里
      // 调用actions里方法请求数据
      store.dispatch('initUserInfo')
      // 下次切换页面vuex里有用户信息数据就不会重复请求用户信息
    }
    next() // 路由放行
  } else {
    next('/login')
  }
})
  1. 在主页删除本地的vuex数据, 刷新页面让vuex取出来空的token, 但是发现递归了

  1. 原因: 因为强制跳转到登录页也会让路由守卫再次触发, 又判断无token, 再次跳转登录页, 就递归了

  1. 解决: 登录页面应该是无需判断token的, 还有注册页面, 所以设置白名单, 无token要去这2个页面直接放行

// 无需验证token的页面
const whiteList = ['/login', '/reg']
router.beforeEach((to, from, next) => {
  const token = store.state.token
  if (token) {
    // 如果有token, 证明已登录
    if (!store.state.userInfo.username) {
      // 有token但是没有用户信息, 才去请求用户信息保存到vuex里
      // 调用actions里方法请求数据
      store.dispatch('initUserInfo')
      // 下次切换页面vuex里有用户信息数据就不会重复请求用户信息
    }
    next() // 路由放行
  } else {
    // 如果无token
    // 如果去的是白名单页面, 则放行
    if (whiteList.includes(to.path)) {
      next()
    } else {
      // 如果其他页面请强制拦截并跳转到登录页面
      next('/login')
    }
  }
})

小结

  1. 无token的时候, 还需要判断什么?

答案

07.axios-响应拦截器


目标

如果token有, 但是过期了怎么办

讲解

  1. 前端是无法判断token是否过期了的, 所以当某次发请求把token带给后台做验证的时候

  1. 后台发现token过期了, 则会返回响应状态码401

  1. 但是你又不确定在哪个请求会401, 所以要用统一的响应拦截器做判断

  1. 在src/utils/request.js中, 给自定义axios函数添加响应拦截器

响应状态码2xx和3xx进入第一个函数, 直接返回响应内容, 如果响应状态码为4xx和5xx则会进入第二个函数, 我们做具体判断和逻辑

return 会返回到本次请求的Promise对象位置

// 定义响应拦截器
myAxios.interceptors.response.use(function (response) {
  // 响应状态码为 2xx 时触发成功的回调,形参中的 response 是“成功的结果”
  return response
}, function (error) {
  // 响应状态码不是 2xx 时触发失败的回调,形参中的 error 是“失败的结果”
  return Promise.reject(error)
})
  1. 在第二个函数, 编写判断401状态码, token过期做出的处理逻辑

// 定义响应拦截器
myAxios.interceptors.response.use(function (response) {
  // 响应状态码为 2xx 时触发成功的回调,形参中的 response 是“成功的结果”
  return response
}, function (error) {
  // 响应状态码不是 2xx 时触发失败的回调,形参中的 error 是“失败的结果”
  if (error.response.status === 401) {
    // 无效的 token
    // 把 Vuex 中的 token 重置为空,并跳转到登录页面
    store.commit('updateToken', '')
    router.push('/login')
  }
  return Promise.reject(error)
})
  1. 可以手动修改本地token值, 乱改一个, 导致它过期, 试试是否能自动回到登录页面

小结

  1. 我们在哪里, 如何判断token过期问题?

答案

08.首页-标签和路由

目标

把首页的组件创建标签和路由配置好

讲解

  1. 在 src/views/目录下,新建 /home/index.vue组件,并初始化如下的组件结构

用了响应式布局: 最小时候占两列,大的时候占4列:sm="6" :xs="12"

使用 el-col 不用指定宽度了,和行边距了,里边的div只需要设置高度即可
 一行24  

第一行  平分四份,每一份占6个。 边距用:gutter
<el-row :gutter="10">
        <el-col :sm="6" :xs="12"> </el-col>
        <el-col :sm="6" :xs="12"></el-col>
        <el-col :sm="6" :xs="12"></el-col>
        <el-col :sm="6" :xs="12"> </el-col>
      </el-row>

第二行
<el-row :gutter="10">
        <el-col :sm="16" :xs="16"> </el-col>
        <el-col :sm="8" :xs="8"></el-col>
 
</el-row>
<template>
  <div>
    <div class="container-fluid">
      <el-row class="spannel_list" :gutter="10">
        <el-col :sm="6" :xs="12">
          <div class="spannel">
            <em>10015</em><span>篇</span>
            <b>总文章数</b>
          </div>
        </el-col>
        <el-col :sm="6" :xs="12">
          <div class="spannel scolor01">
            <em>123</em><span>篇</span>
            <b>日新增文章数</b>
          </div>
        </el-col>
        <el-col :sm="6" :xs="12">
          <div class="spannel scolor02">
            <em>35</em><span>条</span>
            <b>评论总数</b>
          </div>
        </el-col>
        <el-col :sm="6" :xs="12">
          <div class="spannel scolor03">
            <em>123</em><span>条</span>
            <b>日新增评论数</b>
          </div>
        </el-col>
      </el-row>
    </div>
    <div class="container-fluid">
      <el-row class="curve-pie" :gutter="10">
        <el-col :sm="16" :xs="16">
          <div class="gragh_pannel" id="curve_show"></div>
        </el-col>
        <el-col :sm="8" :xs="8">
          <div class="gragh_pannel" id="pie_show"></div>
        </el-col>
      </el-row>
    </div>
    <div class="container-fluid">
      <div class="column_pannel" id="column_show"></div>
    </div>
  </div>
</template>
<script>
import * as echarts from 'echarts'
export default {
  name: 'my-home'
}
</script>
<style lang="less" scoped>
.spannel_list {
  margin-top: 20px;
}
.spannel {
  height: 100px;
  overflow: hidden;
  text-align: center;
  position: relative;
  background-color: #fff;
  border: 1px solid #e7e7e9;
  margin-bottom: 20px;
}
.spannel em {
  font-style: normal;
  font-size: 50px;
  line-height: 50px;
  display: inline-block;
  margin: 10px 0 0 20px;
  font-family: 'Arial';
  color: #83a2ed;
}
.spannel span {
  font-size: 14px;
  display: inline-block;
  color: #83a2ed;
  margin-left: 10px;
}
.spannel b {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  line-height: 24px;
  background: #e5e5e5;
  color: #333;
  font-size: 14px;
  font-weight: normal;
}
.scolor01 em,
.scolor01 span {
  color: #6ac6e2;
}
.scolor02 em,
.scolor02 span {
  color: #5fd9de;
}
.scolor03 em,
.scolor03 span {
  color: #58d88e;
}
.gragh_pannel {
  height: 350px;
  border: 1px solid #e7e7e9;
  background-color: #fff !important;
  margin-bottom: 20px;
}
.column_pannel {
  margin-bottom: 20px;
  height: 400px;
  border: 1px solid #e7e7e9;
  background-color: #fff !important;
}
</style>
  1. 配置路由在src/router/index.js中

 {
    path: '/',
    component: () => import('@/views/layout'),
    redirect: '/home', // 默认显示首页的二级路由
    children: [
      {
        path: 'home',
        component: () => import('@/views/home')
      }
    ]
  },

/匹配了一级路由,/home 匹配了二级路由

设置二级路由挂载点, 在src/views/layout/index.vue的el-main中

小结

  1. 二级路由如何设置?

答案

09首页-echarts使用


目标

在vue项目页面中, 使用echarts完成图表

讲解

  1. 找到echarts文档, 查看如何使用, 根据文档下载echarts包到当前项目中

yarn add echarts
  1. 根据文档初始化echarts, 并完成面积图的使用

这样不起作用: 因为vue文件会被webpack预先执行,执行的时候会把js代码先执行, 执行到document ,getElementByid('curve_show '),真实的dom还没有这个id ,所以获取了个underfined . echarts 的init里边做了判断,发现不是dom标签,所以报错了。

在什么时候会确保在dom上了? 放在mounted 在挂在后进行调用

1 设置饼状图
// 模块内用命名导出的, 我把他们再收集起来形成对象, 放到echrats变量里
import * as echarts from 'echarts'
export default {
  name: 'my-home',
  mounted () {
    this.setCurveChartFn()
  },
  methods: {
    // 设置面积图
    setCurveChartFn () {
      // 基于准备好的dom,初始化echarts实例
      const curveChart = echarts.init(document.getElementById('curve_show'))
      // 绘制面积图表
      // 数据源(模拟后台返回的数据)
      const aListAll = [
        { count: 36, date: '2019-04-13' },
        { count: 52, date: '2019-04-14' },
        { count: 78, date: '2019-04-15' },
        { count: 85, date: '2019-04-16' },
        { count: 65, date: '2019-04-17' },
        { count: 72, date: '2019-04-18' },
        { count: 88, date: '2019-04-19' },
        { count: 64, date: '2019-04-20' },
        { count: 72, date: '2019-04-21' },
        { count: 90, date: '2019-04-22' },
        { count: 96, date: '2019-04-23' },
        { count: 100, date: '2019-04-24' },
        { count: 102, date: '2019-04-25' },
        { count: 110, date: '2019-04-26' },
        { count: 123, date: '2019-04-27' },
        { count: 100, date: '2019-04-28' },
        { count: 132, date: '2019-04-29' },
        { count: 146, date: '2019-04-30' },
        { count: 200, date: '2019-05-01' },
        { count: 180, date: '2019-05-02' },
        { count: 163, date: '2019-05-03' },
        { count: 110, date: '2019-05-04' },
        { count: 80, date: '2019-05-05' },
        { count: 82, date: '2019-05-06' },
        { count: 70, date: '2019-05-07' },
        { count: 65, date: '2019-05-08' },
        { count: 54, date: '2019-05-09' },
        { count: 40, date: '2019-05-10' },
        { count: 45, date: '2019-05-11' },
        { count: 38, date: '2019-05-12' }
      ]
      // 但是图标要求直接给数字的值, 所以要把对象的值取出来形成2个只有值的数组
      const aCount = []
      const aDate = []
      for (let i = 0; i < aListAll.length; i++) {
        aCount.push(aListAll[i].count)
        aDate.push(aListAll[i].date)
      }
      // 面积图的echarts配置项(参考文档复制)
      const chartopt = {
        title: {
          text: '月新增文章数', // 标题
          left: 'center', // 位置居中
          top: '10' // 标题距离容器顶部px
        },
        tooltip: { // 提示框组件
          trigger: 'axis' // 坐标轴触发(鼠标放在坐标轴就能出提示框)
        },
        legend: { // 图例组件(每种颜色的意思)
          data: ['新增文章'], // 图例文字解释(要和series里name对应)
          top: '40' // 距离容器顶部px
        },
        toolbox: { // 工具栏
          show: true, // 要显示
          feature: { // 各工具配置项
            mark: { show: true }, // 辅助线开关
            dataView: { show: true, readOnly: false }, // 数据视图工具,可以展现当前图表所用的数据,编辑后可以动态更新。
            magicType: { show: true, type: ['line', 'bar'] }, // 动态类型切换
            restore: { show: true }, // 配置项还原
            saveAsImage: { show: true } // 点击保存下载图片
          }
        },
        xAxis: [ // 水平轴显示
          {
            name: '日',
            type: 'category',
            boundaryGap: false, // 坐标轴两边留白策略 (不留白)
            data: aDate // 水平轴上的数字数据 (时间)
          }
        ],
        yAxis: [ // 垂直轴显示
          {
            name: '月新增文章数',
            type: 'value' // 以series里的data值做划分段
          }
        ],
        series: [ // 系列(控制图表类型和数据)
          {
            name: '新增文章',
            type: 'line', // 折线图
            smooth: true, // 是否平滑视觉引导线,默认不平滑,可以设置成 true 平滑显示
            areaStyle: { type: 'default' }, // 区域填充样式。设置后显示成区域面积图。
            itemStyle: { color: '#f80', lineStyle: { color: '#f80' } }, // 折线拐点标志的样式。
            data: aCount // 真正数据源(用下标和x轴数组对应)
          }
        ],
        grid: { // 直角坐标系内绘图网格
          show: true,
          x: 50, // grid 组件离容器偏移量, 左侧
          x2: 50, // 右侧
          y: 80, // 上
          height: 220 // 高度
        }
      }
      curveChart.setOption(chartopt)
    }
  }
}
  1. 设置饼状图
// 设置饼状图
    picChartFn () {
      const oPie = echarts.init(document.getElementById('pie_show'))
      const oPieopt = {
        title: {
          top: 10,
          text: '分类文章数量比',
          x: 'center'
        },
        tooltip: {
          trigger: 'item', // 在图形上才会触发提示
          formatter: '{a} <br/>{b} : {c} ({d}%)' // 提示的文字显示的格式
          // a: 系列名
          // b: 数据名
          // c: 数值
          // d: 百分比 (只有饼状图生效)
        },
        color: ['#5885e8', '#13cfd5', '#00ce68', '#ff9565'], // 系列颜色
        legend: { // 图例组件
          x: 'center',
          top: 65,
          data: ['奇趣事', '会生活', '爱旅行', '趣美味'] // 每个部分对应的数据名(要和series里name对应)
        },
        toolbox: { // 工具箱
          show: true,
          x: 'center',
          top: 35,
          feature: {
            mark: { show: true },
            dataView: { show: true, readOnly: false },
            magicType: {
              show: true,
              type: ['pie', 'funnel'],
              option: {
                funnel: {
                  x: '25%',
                  width: '50%',
                  funnelAlign: 'left',
                  max: 1548
                }
              }
            },
            restore: { show: true },
            saveAsImage: { show: true }
          }
        },
        series: [
          {
            name: '访问来源',
            type: 'pie',
            radius: ['45%', '60%'],
            center: ['50%', '65%'],
            data: [
              { value: 300, name: '奇趣事' },
              { value: 100, name: '会生活' },
              { value: 260, name: '爱旅行' },
              { value: 180, name: '趣美味' }
            ]
          }
        ]
      }
      oPie.setOption(oPieopt)
    }
  1. 设置底部柱状图
// 设置底部柱状图
    columnChartFn () {
      const oColumn = echarts.init(document.getElementById('column_show'))
      const oColumnopt = {
        title: {
          text: '文章访问量',
          left: 'center',
          top: '10'
        },
        tooltip: {
          trigger: 'axis'
        },
        legend: {
          data: ['奇趣事', '会生活', '爱旅行', '趣美味'],
          top: '40'
        },
        toolbox: {
          show: true,
          feature: {
            mark: { show: true },
            dataView: { show: true, readOnly: false },
            magicType: { show: true, type: ['line', 'bar'] },
            restore: { show: true },
            saveAsImage: { show: true }
          }
        },
        xAxis: [
          {
            type: 'category',
            data: ['1月', '2月', '3月', '4月', '5月']
          }
        ],
        yAxis: [
          {
            name: '访问量',
            type: 'value'
          }
        ],
        series: [
          {
            name: '奇趣事',
            type: 'bar',
            barWidth: 20,
            areaStyle: { type: 'default' },
            itemStyle: {
              color: '#fd956a'
            },
            data: [800, 708, 920, 1090, 1200]
          },
          {
            name: '会生活',
            type: 'bar',
            barWidth: 20,
            areaStyle: { type: 'default' },
            itemStyle: {
              color: '#2bb6db'
            },
            data: [400, 468, 520, 690, 800]
          },
          {
            name: '爱旅行',
            type: 'bar',
            barWidth: 20,
            areaStyle: { type: 'default' },
            itemStyle: {
              color: '#13cfd5'
            },
            data: [500, 668, 520, 790, 900]
          },
          {
            name: '趣美味',
            type: 'bar',
            barWidth: 20,
            areaStyle: { type: 'default' },
            itemStyle: {
              color: '#00ce68'
            },
            data: [600, 508, 720, 890, 1000]
          }
        ],
        grid: {
          show: true,
          x: 50,
          x2: 30,
          y: 80,
          height: 260
        },
        dataZoom: [ // 给x轴设置滚动条
          {
            start: 0, // 默认为0
            end: 100 - 1000 / 31, // 默认为100
            type: 'slider',
            show: true,
            xAxisIndex: [0],
            handleSize: 0, // 滑动条的 左右2个滑动条的大小
            height: 8, // 组件高度
            left: 45, // 左边的距离
            right: 50, // 右边的距离
            bottom: 26, // 右边的距离
            handleColor: '#ddd', // h滑动图标的颜色
            handleStyle: {
              borderColor: '#cacaca',
              borderWidth: '1',
              shadowBlur: 2,
              background: '#ddd',
              shadowColor: '#ddd'
            }
          }
        ]
      }
      oColumn.setOption(oColumnopt)
    }

小结

  1. 在Vue项目中如何使用图表?

答案

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值