赞
踩
项目最终的效果如图所示,最终效果涉及到6个图表, 5种图表类型,它们分别是折线图,柱状图,地图,散点图,饼图.。每个图表的数据都是从后端推送到前端来的, 不过在项目的初期,我们会先使用 ajax 由前端主动获取数 据, 后续会使用 WebSocket进行改造。整个项目的架构是基于 Vue 的, 所以我们需要创建 Vue项目, 然后在 Vue项目中开发各个图表组件。
在全局环境中安装 vue-cli脚手架
npm install -g @vue/cli
使用命令行执行
vue create 项目名
具体的配置项如下:
手动选择特性
集成 Router , Vuex , CSS Pre-processors
是否选用历史模式的路由
选择 Less作为 CSS 的预处理器
选择 ESLint 的配置
配置选择完之后, 就开始创建项目了, 这个过程需要一些时间:
当项目就创建完成了
- cd vision
- npm run serve
将目录使用 vscode打开
修改 App.vue 中的代码,将布局和样式删除, 变成如下代码:
<template> <div id="app"> <router-view/> </div> </template> <style lang="less"> </style> |
删除 components/HelloWorld.vue 这个文件
删除 views/About.vue 和 views/Home.vue 这两个文件
修改 router/index.js 中的代码,去除路由配置和 Home组件导入的代码
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [] const router = new VueRouter({ routes }) export default router |
// 使用vue-cli创建出来的vue工程 , Webpack的配置是被隐藏起来了的 // 如果想覆盖Webpack中的默认配置 ,需要在项目的根路径下增加vue.config.js文件 module.exports = { devServer: { port: 8999, // 端口号的配置 open: true // 自动打开浏览器 } } |
将资料文件夹中的 static 目录复制到 public 目录之下
在 public/index.html 文件中引入 echarts.min.js 文件
在 src/main.js文件中挂载
由于在 index.html 中已经通过script标签引入了 echarts.js文件夹, 故在 window全局对象中是 存在 echarts全局对象, 将其挂载到 Vue 的原型对象
...... // 将全局echarts对象挂载到Vue的原型对象上 Vue.prototype.$echarts = window.echarts ...... |
this.$echarts |
npm install axios |
...... import axios from 'axios' axios.defaults.baseURL = 'http://127.0.0.1:8888/api/' // 将axios挂载到Vue的原型对象上 Vue.prototype.$http = axios ...... |
在其他组件中使用
this.$http
在项目的初期, 我们会每个图表单独的进行开发, 最后再将所有的图表合并到一个界面中. 在单独开发每个图表的时候, 一个图表会用一个单独的路径进行全屏展示, 他们分别是:
商家销售统计http://127.0.0.1:8999/#/sellerpage
销量趋势分析http://127.0.0.1:8999/#/trendpage
商家地图分布http://127.0.0.1:8999/#/mappage
地区销量排行http://127.0.0.1:8999/#/rankpage
热销商品占比http://127.0.0.1:8999/#/hotpage
库存销量分析http://127.0.0.1:8999/#/stockpage
最终的效果如下图所示:
在 src/components/ 目录下建立 Seller.vue , 这个组件是真实展示图表的组件
给外层div增加类样式 com-container
建立一个显示图表的div元素
给新增的这个div增加类样式 com-chart
在 src/views/ 目录下建立 SellerPage.vue ,这个组件是对应于路由 /seller 而展示的
给外层div元素增加样式 com-page
在 SellerPage 中引入 Seller组件,并且注册和使用
<!-- 这个组件是对应于路由规则中 /seller 这条路径的 在这个组件中 ,需要展示Seller.vue这个组件 Seller.vue才是真正显示图表的组件 --> <template> <div class="com-page"> <seller></seller> </div> </template> <script> import Seller from '@/components/Seller' export default { data () { return {} }, methods: {}, components: { seller:Seller } } </script> <style lang='less' scoped> </style> |
在 src/views/ 目录下建立 SellerPage.vue ,这个组件是对应于路由 /seller 而展示的
给外层div元素增加样式 com-page
在 SellerPage 中引入 Seller组件,并且注册和使用
<!-- 这个组件是对应于路由规则中 /seller 这条路径的 在这个组件中 ,需要展示Seller.vue这个组件 Seller.vue才是真正显示图表的组件 --> <template> <div class="com-page"> <seller></seller> </div> </template> <script> import Seller from '@/components/Seller' export default { data () { return {} }, methods: {}, components: { seller:Seller } } </script> <style lang='less' scoped> </style> |
增加路由规则, 在 src/router/index.js文件中修改
...... import SellerPage from '@/views/SellerPage' ...... const routes = [ { path: '/sellerpage', component: SellerPage } ] |
新建 src/assets/css/global.less 增加宽高样式
原则就是将所有的容器的宽度和高度设置为占满父容器
- html,
- body,
- #app {
- width: 100%;
- height: 100%;
- padding: 0;
- margin: 0;
- overflow: hidden;
- }
- .com-page {
- width: 100%;
- height: 100%;
- overflow: hidden;
- }
- .com-container {
- width: 100%;
- height: 100%;
- overflow: hidden;
- }
- .com-chart {
- width: 100%;
- height: 100%;
- overflow: hidden;
- }
在 main.js 中引入样式
import './assets/css/global.less' |
打开浏览器, 输入 http://127.0.0.1:8999/#/sellerpage 看Seller组件是否能够显示
1.在mounted生命周期中初始化 echartsInstance对象
2.在mounted中获取服务器的数据
3.将获取到的数据设置到图表上
- <script>
- export default {
- data () {
- return {
- chartInstance: null, // echarts实例对象
- allData: [] // 服务器获取的所有数据
- }
- },
- mounted () {
- // 由于初始化echarts实例对象需要使用到dom元素 ,因此必须要放到mounted中 , 而不是created
- this.initChart()
- this.getData()
- },
- methods: {
- initChart () {
- this.chartInstance = this.$echarts.init(this.$refs.seller_ref) // 初始化 echarts实例对象
- },
- async getData () {
- const { data: res } = await this.$http.get('seller') // 获取数据 this.allData = res
- // 对allData进行从大到小的排序
- this.allData.sort((a, b) => {
- return a.value - b.value
- })
- this.updateChart()
- },
- updateChart () {
- // 处理数据并且更新界面图表
- const sellerNames = this.allData.map((item) => {
- return item.name
- })
- const sellerValues = this.allData.map((item) => {
- return item.value
- })
- const option = {
- xAxis: {
- type: 'value'
- },
- yAxis: {
- type: 'category',
- data: sellerNames
- },
- series: [
- {
- type: 'bar',
- data: sellerValues
- }
- ]
- }
- this.chartInstance.setOption(option)
- }
- }
- }
- </script>
4.拆分配置项 option
初始化配置项
拥有数据之后的配置项
数据的处理, 每5个元素显示一页
数据的处理
动画的启动和停止
鼠标事件的处理
public/index.html 中引入
主题的指定,在初始化 echarts实例对象的时候指定
src/components/Seller.vue
边框圆角的设置
src/assets/css/global.less
canvas { border-radius: 20px; } |
其他图表样式的配置
标题的位置和颜色
const initOption = { title: { text: '▎ 商家销量排行', left: 20, top: 20, textStyle: { textStyle: { "color": "#fff" } } }, |
坐标轴的大小
const initOption = { ...... grid: { top: '20%', left: '3%', right: '6%', bottom: '3%', containLabel: true }, |
工具提示和背景
const initOption = { ...... tooltip: { trigger: 'axis', axisPointer: { type: 'line', z: 0, lineStyle: { width: 66, color: '#2D3443' } } }, |
const initOption = { ...... series: [ { ...... label: { show: true, position: 'right', textStyle: { color: '#fff' } }, |
柱宽度和柱圆角的实现
const initOption = { ...... series: [ { ...... barWidth: 66, itemStyle: { barBorderRadius: [0, 33, 33, 0] } } ] } |
柱颜色渐变的实现
线性渐变可以通过 LinearGradient进行实现
LinearGradient需要传递5个参数, 前四个代表两个点的相对位置,第五个参数代表颜色变化 的范围
0, 0, 1, 0 代表的是从左往右的方向
- const initOption = {
- series: [
- {
- ......
- itemStyle: {
- barBorderRadius: [0, 33, 33, 0],
- color: new this.$echarts.graphic.LinearGradient(0, 0, 1,
- 0, [
- {
- offset: 0,
- color: '#5052EE'
- },
- {
- offset: 1,
- color: '#AB6EE5'
- }
- ])
- }
- }
- ]
mounted () { this.initChart() this.getData() window.addEventListener('resize', this.screenAdapter) } |
组件销毁时取消监听
destroyed () { clearInterval(this.timerId) // 在组件销毁的时候 , 需要将监听器取消掉 window.removeEventListener('resize', this.screenAdapter) }, |
获取图表容器的宽度计算字体大小
// 当浏览器的大小发生变化的时候 , 会调用的方法 , 来完成屏幕的适配 screenAdapter () { // console.log(this.$refs.seller_ref.offsetWidth) const titleFontSize = this.$refs.seller_ref.offsetWidth / 100 * 3.6 |
- <!-- 商家销量统计的横向柱状图 -->
- <template>
- <div class="com-container">
- <div class="com-chart" ref="seller_ref"></div>
- </div>
- </template>
-
- <script>
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null, // 服务器返回的数据
- currentPage: 1, // 当前显示的页数
- totalPage: 0, // 一共有多少页
- timerId: null // 定时器的标识
- }
- },
- mounted () {
- this.initChart()
- this.getData()
- window.addEventListener('resize', this.screenAdapter)
- // 在页面加载完成的时候, 主动进行屏幕的适配
- this.screenAdapter()
- },
- destroyed () {
- clearInterval(this.timerId)
- // 在组件销毁的时候, 需要将监听器取消掉
- window.removeEventListener('resize', this.screenAdapter)
- },
- methods: {
- // 初始化echartInstance对象
- initChart () {
- this.chartInstance = this.$echarts.init(this.$refs.seller_ref, 'chalk')
- // 对图表初始化配置的控制
- const initOption = {
- title: {
- text: '▎商家销售统计',
- left: 20,
- top: 20
- },
- grid: {
- top: '20%',
- left: '3%',
- right: '6%',
- bottom: '3%',
- containLabel: true // 距离是包含坐标轴上的文字
- },
- xAxis: {
- type: 'value'
- },
- yAxis: {
- type: 'category'
- },
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'line',
- z: 0,
- lineStyle: {
- color: '#2D3443'
- }
- }
- },
- series: [
- {
- type: 'bar',
- label: {
- show: true,
- position: 'right',
- textStyle: {
- color: 'white'
- }
- },
- itemStyle: {
- // 指明颜色渐变的方向
- // 指明不同百分比之下颜色的值
- color: new this.$echarts.graphic.LinearGradient(0, 0, 1, 0, [
- // 百分之0状态之下的颜色值
- {
- offset: 0,
- color: '#5052EE'
- },
- // 百分之100状态之下的颜色值
- {
- offset: 1,
- color: '#AB6EE5'
- }
- ])
- }
- }
- ]
- }
- this.chartInstance.setOption(initOption)
- // 对图表对象进行鼠标事件的监听
- this.chartInstance.on('mouseover', () => {
- clearInterval(this.timerId)
- })
- this.chartInstance.on('mouseout', () => {
- this.startInterval()
- })
- },
- // 获取服务器的数据
- async getData () {
- // http://127.0.0.1:8888/api/seller
- const { data: ret } = await this.$http.get('seller')
- this.allData = ret
- // 对数据排序
- this.allData.sort((a, b) => {
- return a.value - b.value // 从小到大的排序
- })
- // 每5个元素显示一页
- this.totalPage = this.allData.length % 5 === 0 ? this.allData.length / 5 : this.allData.length / 5 + 1
- this.updateChart()
- // 启动定时器
- this.startInterval()
- },
- // 更新图表
- updateChart () {
- const start = (this.currentPage - 1) * 5
- const end = this.currentPage * 5
- const showData = this.allData.slice(start, end)
- const sellerNames = showData.map((item) => {
- return item.name
- })
- const sellerValues = showData.map((item) => {
- return item.value
- })
- const dataOption = {
- yAxis: {
- data: sellerNames
- },
- series: [
- {
- data: sellerValues
- }
- ]
- }
- this.chartInstance.setOption(dataOption)
- },
- startInterval () {
- if (this.timerId) {
- clearInterval(this.timerId)
- }
- this.timerId = setInterval(() => {
- this.currentPage++
- if (this.currentPage > this.totalPage) {
- this.currentPage = 1
- }
- this.updateChart()
- }, 3000)
- },
- // 当浏览器的大小发生变化的时候, 会调用的方法, 来完成屏幕的适配
- screenAdapter () {
- // console.log(this.$refs.seller_ref.offsetWidth)
- const titleFontSize = this.$refs.seller_ref.offsetWidth / 100 * 3.6
- // 和分辨率大小相关的配置项
- const adapterOption = {
- title: {
- textStyle: {
- fontSize: titleFontSize
- }
- },
- tooltip: {
- axisPointer: {
- lineStyle: {
- width: titleFontSize
- }
- }
- },
- series: [
- {
- barWidth: titleFontSize,
- itemStyle: {
- barBorderRadius: [0, titleFontSize / 2, titleFontSize / 2, 0]
- }
- }
- ]
- }
- this.chartInstance.setOption(adapterOption)
- // 手动的调用图表对象的resize 才能产生效果
- this.chartInstance.resize()
- }
- }
- }
- </script>
-
- <style lang="less" scoped>
- </style>
最终的效果如下:
TrendPage.vue
- <!--
- 针对于 /trendpage 这条路径而显示出来的
- 在这个组件中 , 通过子组件注册的方式 , 要显示出Trend.vue这个组件
- -->
- <template>
- <div class="com-page">
- <trend></trend>
- </div>
- </template>
-
- <script>
- import Trend from '@/components/Trend'
- export default {
- data () {
- return {}
- },
- methods: {},
- components: {
- trend: Trend
- }
- }
- </script>
-
- <style lang="less" scoped>
- </style>
Trend.vue
- <template>
- <div class="com-container">
- <div class="title" :style="comStyle">
- <span>{{ '▎ ' + showTitle }}</span>
- <span class="iconfont title-icon" :style="comStyle" @click="showChoice = !showChoice"></span>
- <div class="select-con" v-show="showChoice" :style="marginStyle">
- <div class="select-item" v-for="item in selectTypes" :key="item.key" @click="handleSelect(item.key)">
- {{ item.text }}
- </div>
- </div>
- </div>
- <div class="com-chart" ref="trend_ref"></div>
- </div>
- </template>
-
- <script>
- export default {
- data () {
- return {
- chartInstane: null,
- allData: null, // 从服务器中获取的所有数据
- showChoice: false, // 是否显示可选项
- choiceType: 'map', // 显示的数据类型
- titleFontSize: 0 // 指明标题的字体大小
- }
- },
- mounted () {
- this.initChart()
- this.getData()
- window.addEventListener('resize', this.screenAdapter)
- this.screenAdapter()
- },
- destroyed () {
- window.removeEventListener('resize', this.screenAdapter)
- },
- computed: {
- selectTypes () {
- if (!this.allData) {
- return []
- } else {
- return this.allData.type.filter(item => {
- return item.key !== this.choiceType
- })
- }
- },
- showTitle () {
- if (!this.allData) {
- return ''
- } else {
- return this.allData[this.choiceType].title
- }
- },
- // 设置给标题的样式
- comStyle () {
- return {
- fontSize: this.titleFontSize + 'px'
- }
- },
- marginStyle () {
- return {
- marginLeft: this.titleFontSize + 'px'
- }
- }
- },
- methods: {
- initChart () {
- this.chartInstane = this.$echarts.init(this.$refs.trend_ref, 'chalk')
- const initOption = {
- grid: {
- left: '3%',
- top: '35%',
- right: '4%',
- bottom: '1%',
- containLabel: true
- },
- tooltip: {
- trigger: 'axis'
- },
- legend: {
- left: 20,
- top: '15%',
- icon: 'circle'
- },
- xAxis: {
- type: 'category',
- boundaryGap: false
- },
- yAxis: {
- type: 'value'
- }
- }
- this.chartInstane.setOption(initOption)
- },
- async getData () {
- // await this.$http.get()
- // 对allData进行赋值
- const { data: ret } = await this.$http.get('trend')
- this.allData = ret
- console.log(this.allData)
- this.updateChart()
- },
- updateChart () {
- // 半透明的颜色值
- const colorArr1 = [
- 'rgba(11, 168, 44, 0.5)',
- 'rgba(44, 110, 255, 0.5)',
- 'rgba(22, 242, 217, 0.5)',
- 'rgba(254, 33, 30, 0.5)',
- 'rgba(250, 105, 0, 0.5)'
- ]
- // 全透明的颜色值
- const colorArr2 = [
- 'rgba(11, 168, 44, 0)',
- 'rgba(44, 110, 255, 0)',
- 'rgba(22, 242, 217, 0)',
- 'rgba(254, 33, 30, 0)',
- 'rgba(250, 105, 0, 0)'
- ]
- // 处理数据
- // 类目轴的数据
- const timeArr = this.allData.common.month
- // y轴的数据 series下的数据
- const valueArr = this.allData[this.choiceType].data
- const seriesArr = valueArr.map((item, index) => {
- return {
- name: item.name,
- type: 'line',
- data: item.data,
- stack: this.choiceType,
- areaStyle: {
- color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
- {
- offset: 0,
- color: colorArr1[index]
- }, // %0的颜色值
- {
- offset: 1,
- color: colorArr2[index]
- } // 100%的颜色值
- ])
- }
- }
- })
- // 图例的数据
- const legendArr = valueArr.map(item => {
- return item.name
- })
- const dataOption = {
- xAxis: {
- data: timeArr
- },
- legend: {
- data: legendArr
- },
- series: seriesArr
- }
- this.chartInstane.setOption(dataOption)
- },
- screenAdapter () {
- this.titleFontSize = this.$refs.trend_ref.offsetWidth / 100 * 3.6
- const adapterOption = {
- legend: {
- itemWidth: this.titleFontSize,
- itemHeight: this.titleFontSize,
- itemGap: this.titleFontSize,
- textStyle: {
- fontSize: this.titleFontSize / 2
- }
- }
- }
- this.chartInstane.setOption(adapterOption)
- this.chartInstane.resize()
- },
- handleSelect (currentType) {
- this.choiceType = currentType
- this.updateChart()
- this.showChoice = false
- }
- }
- }
- </script>
-
- <style lang="less" scoped>
- .title {
- position: absolute;
- left: 20px;
- top: 20px;
- z-index: 10;
- color: white;
- .title-icon {
- margin-left: 10px;
- cursor: pointer;
- }
- .select-con {
- background-color: #222733;
- }
- }
- </style>
router/index.js
- ......
- import TrendPage from '@/views/TrendPage'
- ......
- const routes = [
- ......
- {
- path: '/trendpage',
- component: TrendPage
- }
- ]
- ......
async getData () { // 获取服务器的数据 , 对this.allData进行赋值之后 , 调用updateChart方法更新图表 const { data: ret } = await this.$http.get('trend') this.allData = ret this.updateChart() } |
数据的处理
- updateChart () {
- // x轴的数据
- const timeArrs = this.allData.common.month
- // y轴的数据 , 暂时先取出map这个节点的数据
- // map代表地区销量趋势
- // seller代表商家销量趋势
- // commodity代表商品销量趋势
- const valueArrs = this.allData.map.data
- // 图表数据 , 一个图表中显示5条折线图
- const seriesArr = valueArrs.map((item, index) => {
- return {
- type: 'line', // 折线图
- name: item.name,
- data: item.data,
- }
- })
- const dataOption = {
- xAxis: {
- data: timeArrs
- },
- legend: {
- data: legendArr
- },
- series: seriesArr
- }
- this.chartInstance.setOption(dataOption)
- }
初始化配置
const initOption = { xAxis: { type: 'category', boundaryGap: false }, yAxis: { type: 'value' } } |
堆叠图效果
要实现堆叠图的效果, series下的每个对象都需要配置上相同的stack属性
updateChart () { const timeArrs = this.allData.common.month const valueArrs = this.allData.map.data const seriesArr = valueArrs.map((item, index) => { return { type: 'line', name: item.name, data: item.data, stack: 'map' // stack值相同 , 可以形成堆叠图效果 } }) ...... } |
图例效果
- updateChart () {
- ......
- const valueArrs = this.allData.map.data
- const seriesArr = valueArrs.map((item, index) => {
- return {
- type: 'line',
- name: item.name,
- data: item.data,
- stack: 'map'
- }
- })
- // 准备图例数据 , 它需要和series下的每个对象的name属性保持一致
- const legendArr = valueArrs.map(item => {
- return item.name
- })
- const dataOption = {
- ......
- legend: {
- data: legendArr
- }
- ......
- }
- this.chartInstance.setOption(dataOption) }
initChart () { this.chartInstance = this.$echarts.init(this.$refs.trend_ref, 'chalk') } |
主题使用完之后, 发现折线图都变成了平滑折线图了, 这是因为在 chalk.js主题文件中, 设置了
smooth:true
.
2.2.4.切换图表
布局的实现
增加类样式为 title 的容器
<template> <div class='com-container'> <div class="title"> <span>我是标题</span> <span class="iconfont title-icon"></span> <div class="select-con"> <div class="select-item"> 标题选择1 </div> <div class="select-item"> 标题选择2 </div> <div class="select-item"> 标题选择3 </div> </div> </div> <div class='com-chart' ref='trend_ref'></div> </div> </template> |
字体文件的引入
将资料文件夹下的字体文件夹中的 font复制到 asset 目录下, 然后在 main.js 中引入字体样式文
件
在 Trend.vue 中的style标签中增加一些样式
<style lang='less' scoped> .title { position: absolute; left: 20px; top: 20px; z-index: 10; color: white; .title-icon { margin-left: 10px; cursor: pointer; } .select-item { cursor: pointer; } } </style> |
数据动态渲染
使用计算属性 title控制标题的内容和标题的可选择项
<script> export default { data () { return { chartInstance: null, allData: null, dataType: 'map' // 这项数据代表目前选择的数据类型 , 可选值有map seller commodity } }, computed: { selectTypes () { if ( !this.allData | | ! this.allData.type) { return [] } else { return this.allData.type.filter(item => { return item.key !== this.dataType }) } }, title () { if (!this.allData) { return '' } else { return this.allData[this.dataType].title } } }, ...... |
点击三角控制显示隐藏
增加一项变量控制可选容器的显示与隐藏
export default { data () { return { showChoice: false // 控制可选面板的显示或者隐藏 } }, |
使用指令 v-if和点击事件的监听
<template> <div class='com-container'> <div class="title"> <span>{{ title }}</span> <span class="iconfont title-icon" @click="showChoice = !showChoice"></span> <div class="select-con" v-if="showChoice"> <div class="select-item" v-for="item in selectTypes" :key="item.key"> {{ item.text }} </div> </div> </div> <div class='com-chart' ref='trend_ref'></div> </div> </template> |
点击可选条目的控制
- <template>
- <div class='com-container'>
- <div class="title">
- <span>{{ title }}</span>
- <span class="iconfont title-icon" @click="showChoice =
- !showChoice"></span>
- <div class="select-con" v-if="showChoice">
- <div class="select-item" v-for="item in selectTypes" :key="item.key" @click="handleSelect(item.key)">
- {{ item.text }}
- </div>
- </div>
- </div>
- <div class='com-chart' ref='trend_ref'></div>
- </div>
- </template>
- <script>
- export default {
- ......
- methods: {
- handleSelect (key) {
- this.dataType = key
- this.updateChart()
- this.showChoice = false
- }
- }
- }
- </script>
将 updateChart 中, 之前写死的map变成 dataType
const valueArrs = this.allData[this.dataType].data const seriesArr = valueArrs.map((item, index) => { return { ...... stack: this.dataType } }) |
<script> export default { data () { return { titleFontSize: 0 } }, ...... screenAdapter () { this.titleFontSize = this.$refs.trend_ref.offsetWidth / 100 * 3.6 } |
通过 titleFontSize从而设置给标题文字的大小和图例的大小
标题文字的大小
增加计算属性 comStyle并设置给对应的 div ,如下:
<!-- 销量趋势图表 --> <template> <div class='com-container'> <div class="title" :style="comStyle"> <span>{{ title }}</span> <span class="iconfont title-icon" @click="showChoice = !showChoice" :style="comStyle"></span> ...... <script> export default { ...... computed: { ...... comStyle () { return { fontSize: this.titleFontSize + 'px' } } }, |
图例的大小
- screenAdapter () {
- this.titleFontSize = this.$refs.trend_ref.offsetWidth / 100 * 3.6 const adapterOption = {
- legend: {
- itemWidth: this.titleFontSize,
- itemHeight: this.titleFontSize,
- itemGap: this.titleFontSize,
- textStyle: {
- fontSize: this.titleFontSize / 2
- }
- }
- }
- this.chartInstance.setOption(adapterOption)
- this.chartInstance.resize()
- },
<style lang='less' scoped> .title { ...... .select-con { background-color: #222733; } .select-item { cursor: pointer; } } </style> |
增加标题左侧的小竖杆
<template> <div class='com-container'> <div class="title" :style="comStyle"> <span>{{'▎ ' + title }}</span> <span class="iconfont title-icon" @click="showChoice = !showChoice" :style="comStyle"></span> <div class="select-con" v-if="showChoice" :style="marginStyle"> ...... <script> export default { ...... computed: { marginStyle () { return { marginLeft: this.titleFontSize + 'px' } } }, |
最终的效果如下:
2.3.1.代码环境的准备
MapPage.vue
<!-- 针对于 /mappage 这条路径而显示出来的 在这个组件中 , 通过子组件注册的方式 , 要显示出Map.vue这个组件 --> <template> <div class="com-page"> <single-map></single-map> </div> </template> <script> import Map from '@/components/Map' export default { data () { return {} }, methods: {}, components: { 'single-map': Map } } </script> <style lang="less" scoped> </style> |
Map.vue
- <!-- 商家分布图表 -->
- <template>
- <div class='com-container' @dblclick="revertMap">
- <div class='com-chart' ref='map_ref'></div>
- </div>
- </template>
-
- <script>
- import axios from 'axios'
- import { getProvinceMapInfo } from '@/utils/map_utils'
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null,
- mapData: {} // 所获取的省份的地图矢量数据
- }
- },
- mounted () {
- this.initChart()
- this.getData()
- window.addEventListener('resize', this.screenAdapter)
- this.screenAdapter()
- },
- destroyed () {
- window.removeEventListener('resize', this.screenAdapter)
- },
- methods: {
- async initChart () {
- this.chartInstance = this.$echarts.init(this.$refs.map_ref, 'chalk')
- // 获取中国地图的矢量数据
- // http://localhost:8999/static/map/china.json
- // 由于我们现在获取的地图矢量数据并不是位于KOA2的后台, 所以咱们不能使用this.$http
- const ret = await axios.get('http://localhost:8999/static/map/china.json')
- this.$echarts.registerMap('china', ret.data)
- const initOption = {
- title: {
- text: '▎ 商家分布',
- left: 20,
- top: 20
- },
- geo: {
- type: 'map',
- map: 'china',
- top: '5%',
- bottom: '5%',
- itemStyle: {
- areaColor: '#2E72BF',
- borderColor: '#333'
- }
- },
- legend: {
- left: '5%',
- bottom: '5%',
- orient: 'vertical'
- }
- }
- this.chartInstance.setOption(initOption)
- this.chartInstance.on('click', async arg => {
- // arg.name 得到所点击的省份, 这个省份他是中文
- const provinceInfo = getProvinceMapInfo(arg.name)
- console.log(provinceInfo)
- // 需要获取这个省份的地图矢量数据
- // 判断当前所点击的这个省份的地图矢量数据在mapData中是否存在
- if (!this.mapData[provinceInfo.key]) {
- const ret = await axios.get('http://localhost:8999' + provinceInfo.path)
- this.mapData[provinceInfo.key] = ret.data
- this.$echarts.registerMap(provinceInfo.key, ret.data)
- }
- const changeOption = {
- geo: {
- map: provinceInfo.key
- }
- }
- this.chartInstance.setOption(changeOption)
- })
- },
- async getData () {
- // 获取服务器的数据, 对this.allData进行赋值之后, 调用updateChart方法更新图表
- const { data: ret } = await this.$http.get('map')
- this.allData = ret
- console.log(this.allData)
- this.updateChart()
- },
- updateChart () {
- // 处理图表需要的数据
- // 图例的数据
- const legendArr = this.allData.map(item => {
- return item.name
- })
- const seriesArr = this.allData.map(item => {
- // return的这个对象就代表的是一个类别下的所有散点数据
- // 如果想在地图中显示散点的数据, 我们需要给散点的图表增加一个配置, coordinateSystem:geo
- return {
- type: 'effectScatter',
- rippleEffect: {
- scale: 5,
- brushType: 'stroke'
- },
- name: item.name,
- data: item.children,
- coordinateSystem: 'geo'
- }
- })
- const dataOption = {
- legend: {
- data: legendArr
- },
- series: seriesArr
- }
- this.chartInstance.setOption(dataOption)
- },
- screenAdapter () {
- const titleFontSize = this.$refs.map_ref.offsetWidth / 100 * 3.6
- const adapterOption = {
- title: {
- textStyle: {
- fontSize: titleFontSize
- }
- },
- legend: {
- itemWidth: titleFontSize / 2,
- itemHeight: titleFontSize / 2,
- itemGap: titleFontSize / 2,
- textStyle: {
- fontSize: titleFontSize / 2
- }
- }
- }
- this.chartInstance.setOption(adapterOption)
- this.chartInstance.resize()
- },
- // 回到中国地图
- revertMap () {
- const revertOption = {
- geo: {
- map: 'china'
- }
- }
- this.chartInstance.setOption(revertOption)
- }
- }
- }
- </script>
-
- <style lang='less' scoped>
- </style>
router/index.js
...... import MapPage from '@/views/MapPage' ...... const routes = [ ...... { path: '/mappage', component: MapPage } ] ...... |
获取中国地图矢量数据
注册地图数据到 全局echarts对象 中
配置 geo
<script> // 获取的是Vue环境之下的数据 , 而不是我们后台的数据 import axios from 'axios' export default { ...... methods: { async initChart () { this.chartInstance = this.$echarts.init(this.$refs.map_ref) const { data: mapData } = await axios.get('http://127.0.0.1:8999/static/map/china.json') this.$echarts.registerMap('china', mapData) const initOption = { geo: { type: 'map', map: 'china' } } this.chartInstance.setOption(initOption) }, |
async getScatterData () { // 获取服务器的数据 , 对this.allData进行赋值之后 , 调用updateChart方法更新图表 const { data: ret} = await this.$http.get('map') this.allData = ret this.updateChart() } |
处理数据并且更新图表
- updateChart () {
- // 处理图表需要的数据
- // 图例数据
- const legendData = this.allData.map(item => {
- return item.name
- })
- // 散点数据
- const seriesArr = this.allData.map(item => {
- return {
- type: 'effectScatter',
- coordinateSystem: 'geo',
- name: item.name,
- data: item.children
- }
- })
- const dataOption = {
- legend: {
- data: legendData
- },
- series: seriesArr
- }
- this.chartInstance.setOption(dataOption)
- },
methods: { async initChart () { this.chartInstance = this.$echarts.init(this.$refs.map_ref, 'chalk') |
标题显示
const initOption = { title: { text: '▎ 商家分布', left: 20, top: 20 }, |
地图位置和颜色
const initOption = { ...... geo: { type: 'map', map: 'china', top: '5%', bottom: '5%', itemStyle: { areaColor: '#2E72BF', borderColor: '#333' } } } |
图例控制
const initOption = { ...... legend: { left: '5%', bottom: '5%', orient: 'vertical' } } |
涟漪效果
updateChart () { ...... const seriesArr = this.allData.map(item => { return { type: 'effectScatter', rippleEffect: { scale: 5, brushType: 'stroke' }, ...... } }) |
screenAdapter () { const titleFontSize = this.$refs.map_ref.offsetWidth / 100 * 3.6 const adapterOption = { } this.chartInstance.setOption(adapterOption) this.chartInstance.resize() } |
将 titleFontSize设置给图表的某些区域
标题的大小
图例大小
- screenAdapter () {
- const titleFontSize = this.$refs.map_ref.offsetWidth / 100 * 3.6 const adapterOption = {
- title: {
- textStyle: {
- fontSize: titleFontSize
- }
- },
- legend: {
- itemWidth: titleFontSize / 2,
- itemHeight: titleFontSize / 2,
- itemGap: titleFontSize / 2,
- textStyle: {
- fontSize: titleFontSize / 2 }
- }
- }
- this.chartInstance.setOption(adapterOption) this.chartInstance.resize()
async initChart () { ...... this.chartInstance.on('click', arg => { // arg.name 就是所点击的省份名称 , 是中文 }) |
将资料中的 map_utils.js复制到 src/utils/ 目录之下
得到地图所点击项的拼音和地图矢量数据的路径
<script> // 获取的是Vue环境之下的数据 , 而不是我们后台的数据 import axios from 'axios' import { getProvinceMapInfo } from '@/utils/map_utils' export default { ...... methods: { async initChart () { ...... this.chartInstance.setOption(initOption) this.chartInstance.on('click', async arg => { // arg.name 就是所点击的省份名称 , 是中文 const provinceInfo = getProvinceMapInfo(arg.name) const { data: ret } = await axios.get('http://127.0.0.1:8999' + provinceInfo.path) this.$echarts.registerMap(provinceInfo.key, ret) this.chartInstance.setOption({ geo: { map: provinceInfo.key } }) }) this.getScatterData() } } } </script> |
回到中国地图
- <template>
- <div class='com-container' @dblclick="revertMap">
- <div class='com-chart' ref='map_ref'></div>
- </div>
- </template>
- <script>
- export default {
- ......
- methods: {
- ......
- revertMap () {
- this.chartInstance.setOption({ geo: {
- map: 'china'
- }
- })
- }
- }
- }
- </script>
最终的效果如下:
2.4.1.代码环境的准备
RankPage.vue
- <!--
- 针对于 /rankpage 这条路径而显示出来的
- 在这个组件中 , 通过子组件注册的方式 , 要显示出Rank.vue这个组件 -->
- <template>
- <div class="com-page">
- <rank></rank>
- </div>
- </template>
-
- <script>
- import Rank from '@/components/Rank'
- export default {
- data () {
- return {}
- },
- methods: {},
- components: {
- rank: Rank
- }
- }
- </script>
-
- <style lang="less" scoped>
- </style>
Rank.vue
- <!-- 地区销售排行 -->
- <template>
- <div class='com-container'>
- <div class='com-chart' ref='rank_ref'></div>
- </div>
- </template>
-
- <script>
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null,
- startValue: 0, // 区域缩放的起点值
- endValue: 9, // 区域缩放的终点值
- timerId: null // 定时器的标识
- }
- },
- mounted () {
- this.initChart()
- this.getData()
- window.addEventListener('resize', this.screenAdapter)
- this.screenAdapter()
- },
- destroyed () {
- window.removeEventListener('resize', this.screenAdapter)
- clearInterval(this.timerId)
- },
- methods: {
- initChart () {
- this.chartInstance = this.$echarts.init(this.$refs.rank_ref, 'chalk')
- const initOption = {
- title: {
- text: '▎ 地区销售排行',
- left: 20,
- top: 20
- },
- grid: {
- top: '40%',
- left: '5%',
- right: '5%',
- bottom: '5%',
- containLabel: true
- },
- tooltip: {
- show: true
- },
- xAxis: {
- type: 'category'
- },
- yAxis: {
- type: 'value'
- },
- series: [
- {
- type: 'bar'
- }
- ]
- }
- this.chartInstance.setOption(initOption)
- this.chartInstance.on('mouseover', () => {
- clearInterval(this.timerId)
- })
- this.chartInstance.on('mouseout', () => {
- this.startInterval()
- })
- },
- async getData () {
- // 获取服务器的数据, 对this.allData进行赋值之后, 调用updateChart方法更新图表
- const { data: ret } = await this.$http.get('rank')
- this.allData = ret
- // 对allData里面的每一个元素进行排序, 从大到小进行
- this.allData.sort((a, b) => {
- return b.value - a.value
- })
- console.log(this.allData)
- this.updateChart()
- this.startInterval()
- },
- updateChart () {
- const colorArr = [
- ['#0BA82C', '#4FF778'],
- ['#2E72BF', '#23E5E5'],
- ['#5052EE', '#AB6EE5']
- ]
- // 处理图表需要的数据
- // 所有省份所形成的数组
- const provinceArr = this.allData.map(item => {
- return item.name
- })
- // 所有省份对应的销售金额
- const valueArr = this.allData.map(item => {
- return item.value
- })
- const dataOption = {
- xAxis: {
- data: provinceArr
- },
- dataZoom: {
- show: false,
- startValue: this.startValue,
- endValue: this.endValue
- },
- series: [
- {
- data: valueArr,
- itemStyle: {
- color: arg => {
- let targetColorArr = null
- if (arg.value > 300) {
- targetColorArr = colorArr[0]
- } else if (arg.value > 200) {
- targetColorArr = colorArr[1]
- } else {
- targetColorArr = colorArr[2]
- }
- return new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
- {
- offset: 0,
- color: targetColorArr[0]
- },
- {
- offset: 1,
- color: targetColorArr[1]
- }
- ])
- }
- }
- }
- ]
- }
- this.chartInstance.setOption(dataOption)
- },
- screenAdapter () {
- const titleFontSize = this.$refs.rank_ref.offsetWidth / 100 * 3.6
- const adapterOption = {
- title: {
- textStyle: {
- fontSize: titleFontSize
- }
- },
- series: [
- {
- barWidth: titleFontSize,
- itemStyle: {
- barBorderRadius: [titleFontSize / 2, titleFontSize / 2, 0, 0]
- }
- }
- ]
- }
- this.chartInstance.setOption(adapterOption)
- this.chartInstance.resize()
- },
- startInterval () {
- if (this.timerId) {
- clearInterval(this.timerId)
- }
- this.timerId = setInterval(() => {
- this.startValue++
- this.endValue++
- if (this.endValue > this.allData.length - 1) {
- this.startValue = 0
- this.endValue = 9
- }
- this.updateChart()
- }, 2000)
- }
- }
- }
- </script>
-
- <style lang='less' scoped>
- </style>
router/index.js
...... import RankPage from '@/views/RankPage' ...... const routes = [ ...... { path: '/rankpage', component: RankPage } ] ...... |
async getData () { // 获取服务器的数据 , 对this.allData进行赋值之后 , 调用updateChart方法更新图表 const { data: ret } = await this.$http.get('rank') this.allData = ret // 对数据进行排序 , 从大到小排序 this.allData.sort((a, b) => { return b.value - a.value }) this.updateChart() }, |
数据的处理
- updateChart () {
- // 处理图表需要的数据
- const provinceArr = this.allData.map(item => {
- return item.name
- })
- const valueArr = this.allData.map(item => {
- return item.value
- })
- const dataOption = {
- xAxis: {
- data: provinceArr
- },
- series: [
- {
- data: valueArr
- }
- ]
- }
- this.chartInstance.setOption(dataOption)
- },
初始化配置
initChart () { this.chartInstance = this.$echarts.init(this.$refs.rank_ref) const initOption = { xAxis: { type: 'category' }, yAxis: { type: 'value' }, series: [ { type: 'bar' } ] } this.chartInstance.setOption(initOption) } |
initChart () { this.chartInstance = this.$echarts.init(this.$refs.rank_ref, 'chalk') |
标题的设置
initChart () { this.chartInstance = this.$echarts.init(this.$refs.rank_ref, 'chalk') const initOption = { title: { text: '▎ 地区销售排行', left: 20, top: 20 } |
颜色的设置
不同柱显示不同颜色
渐变的控制
- updateChart () {
- // 处理图表需要的数据
- const colorArr = [
- ['#0BA82C', '#4FF778'],
- ['#2E72BF', '#23E5E5'],
- ['#5052EE', '#AB6EE5']
- ]
- ......
- const dataOption = {
- xAxis: {
- data: provinceArr
- },
- series: [
- {
- data: valueArr,
- itemStyle: {
- color: arg => {
- let targetColorArr = colorArr[0]
- if (arg.vaule >= 300) {
- targetColorArr = colorArr[0]
- } else if (arg.value >= 200) {
- targetColorArr = colorArr[1]
- } else {
- targetColorArr = colorArr[2]
- }
- return new this.$echarts.graphic.LinearGradient(0,
- 1, 0, 0, [
- {
- offset: 0,
- color: targetColorArr[0]
- },
- {
- offset: 1,
- color: targetColorArr[1] }
- ])
- }
- }
- }
- ]
- }
- this.chartInstance.setOption(dataOption)
- },
2.4.4.平移动画的实现
平移动画可以使用 dataZoom 中的 startValue和 endValue来实现
定义数据
<script> export default { data () { return { chartInstance: null, allData: null, startValue: 0, endValue: 9 } }, |
将 startValue 和 endValue 应用在 dataZoom 上, 并隐藏 dataZoom 的显示
updateChart () { ...... const dataOption = { xAxis: { data: provinceArr }, dataZoom: { show: false, startValue: this.startValue, endValue: this.endValue }, |
启动和停止定时器
增加 timerId 的变量, 并且增加一个方法 startInterval ,来控制 startValue和 endValue 的值
- <script>
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null,
- startValue: 0,
- endValue: 9,
- timerId: null
- }
- },
- ......
- methods: {
- ......
- startInterval () {
- if (this.timerId) {
- clearInterval(this.timerId)
- }
- this.timerId = setInterval(() => {
- this.startValue++
- this.endValue++
- if (this.endValue > this.allData.length - 1) {
- this.startValue = 0
- this.endValue = 9
- }
- this.updateChart()
- }, 3000)
- }
- }
- }
获取数据之后启动
async getData () { ...... this.updateChart() this.startInterval() }, |
组件销毁停止
destroyed () { window.removeEventListener('resize', this.screenAdapter) clearInterval(this.timerId) }, |
鼠标移入停止
methods: { initChart () { ...... this.chartInstance.setOption(initOption) this.chartInstance.on('mouseover', () => { clearInterval(this.timerId) }) |
鼠标离开启动
methods: { initChart () { ...... this.chartInstance.on('mouseout', () => { this.startInterval() }) }, |
将 titleFontSize设置给图表的某些区域
screenAdapter () { const titleFontSize = this.$refs.rank_ref.offsetWidth / 100 * 3.6 const adapterOption = { title: { textStyle: { fontSize: titleFontSize } }, series: [ { barWidth: titleFontSize, itemStyle: { barBorderRadius: [0.5 * titleFontSize, 0.5 * titleFontSize, 0, 0] } } ] } this.chartInstance.setOption(adapterOption) this.chartInstance.resize() }, |
最终的效果如下:
HotPage.vue
- <!--
- 针对于 /hotpage 这条路径而显示出来的
- 在这个组件中 , 通过子组件注册的方式 , 要显示出Hot.vue这个组件
- -->
- <template>
- <div class="com-page">
- <hot></hot>
- </div>
- </template>
-
- <script>
- import Hot from '@/components/Hot'
- export default {
- data () {
- return {}
- },
- methods: {},
- components: {
- hot: Hot
- }
- }
- </script>
-
- <style lang="less" scoped>
- </style>
Hot.vue
- <!-- 热销商品图表 -->
- <template>
- <div class='com-container'>
- <div class='com-chart' ref='hot_ref'></div>
- </div>
- </template>
-
- <script>
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null
- }
- },
- mounted () {
- this.initChart()
- this.getData()
- window.addEventListener('resize', this.screenAdapter)
- this.screenAdapter()
- },
- destroyed () {
- window.removeEventListener('resize', this.screenAdapter)
- },
- methods: {
- initChart () {
- this.chartInstance = this.$echarts.init(this.$refs.hot_ref)
- const initOption = {}
- this.chartInstance.setOption(initOption)
- },
- async getData () {
- // 获取服务器的数据 , 对this.allData进行赋值之后 , 调用updateChart方法更新图表
- this.updateChart()
- },
- updateChart () {
- // 处理图表需要的数据
- const dataOption = {}
- this.chartInstance.setOption(dataOption)
- },
- screenAdapter () {
- const adapterOption = {}
- this.chartInstance.setOption(adapterOption)
- this.chartInstance.resize()
- }
- }
- }
- </script>
-
- <style lang='less' scoped>
- </style>
router/index.js
...... import HotPage from '@/views/HotPage' ...... const routes = [ ...... { path: '/hotpage', component: HotPage } ] ...... |
Hot.vue
- <!-- 热销商品图表 -->
- <template>
- <div class='com-container'>
- <div class='com-chart' ref='hot_ref'></div>
- <span class="iconfont arr-left" @click="toLeft" :style="comStyle"></span>
- <span class="iconfont arr-right" @click="toRight" :style="comStyle"></span>
- <span class="cat-name" :style="comStyle">{{ catName }}</span>
- </div>
- </template>
-
- <script>
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null,
- currentIndex: 0, // 当前所展示出的一级分类数据
- titleFontSize: 0
- }
- },
- computed: {
- catName () {
- if (!this.allData) {
- return ''
- } else {
- return this.allData[this.currentIndex].name
- }
- },
- comStyle () {
- return {
- fontSize: this.titleFontSize + 'px'
- }
- }
- },
- mounted () {
- this.initChart()
- this.getData()
- window.addEventListener('resize', this.screenAdapter)
- this.screenAdapter()
- },
- destroyed () {
- window.removeEventListener('resize', this.screenAdapter)
- },
- methods: {
- initChart () {
- this.chartInstance = this.$echarts.init(this.$refs.hot_ref, 'chalk')
- const initOption = {
- title: {
- text: '▎ 热销商品的占比',
- left: 20,
- top: 20
- },
- legend: {
- top: '15%',
- icon: 'circle'
- },
- tooltip: {
- show: true,
- formatter: arg => {
- // console.log(arg)
- const thirdCategory = arg.data.children
- // 计算出所有三级分类的数值总和
- let total = 0
- thirdCategory.forEach(item => {
- total += item.value
- })
- let retStr = ''
- thirdCategory.forEach(item => {
- retStr += `
- ${item.name}:${parseInt(item.value / total * 100) + '%'}
- <br/>
- `
- })
- return retStr
- }
- },
- series: [
- {
- type: 'pie',
- label: {
- show: false
- },
- emphasis: {
- label: {
- show: true
- },
- labelLine: {
- show: false
- }
- }
- }
- ]
- }
- this.chartInstance.setOption(initOption)
- },
- async getData () {
- // 获取服务器的数据, 对this.allData进行赋值之后, 调用updateChart方法更新图表
- const { data: ret } = await this.$http.get('hotproduct')
- this.allData = ret
- console.log(this.allData)
- this.updateChart()
- },
- updateChart () {
- // 处理图表需要的数据
- const legendData = this.allData[this.currentIndex].children.map(item => {
- return item.name
- })
- const seriesData = this.allData[this.currentIndex].children.map(item => {
- return {
- name: item.name,
- value: item.value,
- children: item.children // 新增加children的原因是为了在tooltip中的formatter的回调函数中,来拿到这个二级分类下的三级分类数据
- }
- })
- const dataOption = {
- legend: {
- data: legendData
- },
- series: [
- {
- data: seriesData
- }
- ]
- }
- this.chartInstance.setOption(dataOption)
- },
- screenAdapter () {
- this.titleFontSize = this.$refs.hot_ref.offsetWidth / 100 * 3.6
- const adapterOption = {
- title: {
- textStyle: {
- fontSize: this.titleFontSize
- }
- },
- legend: {
- itemWidth: this.titleFontSize / 2,
- itemHeight: this.titleFontSize / 2,
- itemGap: this.titleFontSize / 2,
- textStyle: {
- fontSize: this.titleFontSize / 2
- }
- },
- series: [
- {
- radius: this.titleFontSize * 4.5,
- center: ['50%', '60%']
- }
- ]
- }
- this.chartInstance.setOption(adapterOption)
- this.chartInstance.resize()
- },
- toLeft () {
- this.currentIndex--
- if (this.currentIndex < 0) {
- this.currentIndex = this.allData.length - 1
- }
- this.updateChart()
- },
- toRight () {
- this.currentIndex++
- if (this.currentIndex > this.allData.length - 1) {
- this.currentIndex = 0
- }
- this.updateChart()
- }
- }
- }
- </script>
-
- <style lang='less' scoped>
- .arr-left {
- position:absolute;
- left: 10%;
- top: 50%;
- transform: translateY(-50%);
- cursor: pointer;
- color: white;
- }
- .arr-right {
- position:absolute;
- right: 10%;
- top: 50%;
- transform: translateY(-50%);
- cursor: pointer;
- color: white;
- }
- .cat-name {
- position:absolute;
- left: 80%;
- bottom: 20px;
- color: white;
- }
- </style>
数据的处理
增加 currentIndex索引代表当前显示的数据索引, 后期通过左右箭头改变 currentIndex 的值
- <script>
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null,
- currentIndex: 0
- }
- },
- ......
- updateChart () {
- // 处理图表需要的数据
- // 饼图数据
- const seriesData = this.allData[this.currentIndex].children.map(item
- => {
- return {
- value: item.value,
- name: item.name
- }
- })
- // 图例数据
- const legendData = this.allData[this.currentIndex].children.map(item
- => {
- return item.name
- })
- const dataOption = {
- legend: {
- data: legendData
- },
- series: [
- {
- data: seriesData
- }
- ]
- }
- this.chartInstance.setOption(dataOption)
- },
初始化配置
methods: { initChart () { this.chartInstance = this.$echarts.init(this.$refs.hot_ref) const initOption = { title: { text: '▎ 热销商品销售金额占比统计', left: 20, top: 20 }, series: [ { type: 'pie' } ] } this.chartInstance.setOption(initOption) }, |
布局
<!-- 热销商品图表 --> <template> <div class='com-container'> <div class='com-chart' ref='hot_ref'></div> <span class="iconfont arr_left"></span> <span class="iconfont arr_right"></span> </div> </template> |
样式
<style lang='less' scoped> .arr_left { position: absolute; left: 10%; top: 50%; transform: translateY(-50%); cursor: pointer; } .arr_right { position: absolute; right: 10%; top: 50%; transform: translateY(-50%); cursor: pointer; } </style> |
点击事件
<span class="iconfont arr_left" @click="toLeft"></span> <span class="iconfont arr_right" @click="toRight"></span> methods: { toLeft () { this.currentIndex-- if (this.currentIndex < 0) { this.currentIndex = this.allData.length - 1 } this.updateChart() }, toRight () { this.currentIndex++ if (this.currentIndex > this.allData.length - 1) { this.currentIndex = 0 } this.updateChart() } } |
分类名称的显示
布局和样式
- <template>
- <div class='com-container'>
- ......
- <span class="cat_name">分类名称</span>
- </div>
- </template>
-
- <style lang='less' scoped>
- .cat_name {
- position: absolute;
- left: 80%;
- bottom: 20px;
- font-weight: bold;
- }
- </style>
名称的改变
增加计算属性 catTitle
<script> export default { ...... computed: { catTitle () { if (!this.allData) { return '' } return this.allData[this.currentIndex].name } }, |
布局中使用计算属性
<!-- 热销商品图表 --> <template> <div class='com-container'> ...... <span class="cat_name">{{ catTitle }}</span> </div> </template> |
2.5.4. UI 效果的调整
主题的使用
methods: { initChart () { this.chartInstance = this.$echarts.init(this.$refs.hot_ref, 'chalk') |
分类名称和箭头的颜色
- <style lang='less' scoped>
- .arr_left {
- ......
- color: white;
- }
- .arr_right {
- ......
- color: white;
- }
- .cat_name {
- .....
- color: white;
- }
- </style>
默认隐藏文字, 高亮显示文字
methods: { initChart () { this.chartInstance = this.$echarts.init(this.$refs.hot_ref, 'chalk') const initOption = { ...... series: [ { type: 'pie', label: { // 隐藏文字 show: false }, labelLine: { // 隐藏线 show: false }, emphasis: { label: { // 高亮显示文字 show: true } } } ] } this.chartInstance.setOption(initOption) }, |
图例形状和位置
methods: { initChart () { this.chartInstance = this.$echarts.init(this.$refs.hot_ref, 'chalk') const initOption = { legend: { top: '5%', icon: 'circle' }, |
工具提示
当鼠标移入某个扇区的时候, 需要将该二级分类之下的三级分类数据进行展示
增加 series下饼图每一个扇区的数据
updateChart () { // 处理图表需要的数据 const seriesData = this.allData[this.currentIndex].children.map(item => { return { ...... children: item.children } }) |
显示 tooltip ,并控制显示内容
methods: { initChart () { this.chartInstance = this.$echarts.init(this.$refs.hot_ref, 'chalk') const initOption = { ...... tooltip: { trigger: 'item', formatter: function(params) { let tipArray = [] params.data.children.forEach(function(item) { let childStr = ` ${item.name} ${parseInt((item.value / params.value) * 100) + '%'} ` tipArray.push(childStr) }) return tipArray.join('<br/>') } } |
<script> export default { data () { return { titleFontSize: 0 } }, ...... screenAdapter () { this.titleFontSize = this.$refs.hot_ref.offsetWidth / 100 * 3.6 } |
最终的效果如下:
2.6.1.代码环境的准备
StockPage.vue
- <!--
- 针对于 /stockpage 这条路径而显示出来的
- 在这个组件中 , 通过子组件注册的方式 , 要显示出Stock.vue这个组件
- -->
- <template>
- <div class="com-page">
- <stock></stock>
- </div>
- </template>
- <script>
- import Stock from '@/components/Stock' export default {
- data () {
- return {}
- },
- methods: {},
- components: {
- stock: Stock
- }
- }
- </script>
-
- <style lang="less" scoped>
- </style>
Stock.vue
- <!-- 库存销量分析 -->
- <template>
- <div class='com-container'>
- <div class='com-chart' ref='stock_ref'></div>
- </div>
- </template>
-
- <script>
- export default {
- data () {
- return {
- chartInstance: null,
- allData: null,
- currentIndex: 0, // 当前显示的数据
- timerId: null // 定时器的标识
- }
- },
- mounted () {
- this.initChart()
- this.getData()
- window.addEventListener('resize', this.screenAdapter)
- this.screenAdapter()
- },
- destroyed () {
- window.removeEventListener('resize', this.screenAdapter)
- clearInterval(this.timerId)
- },
- methods: {
- initChart () {
- this.chartInstance = this.$echarts.init(this.$refs.stock_ref, 'chalk')
- const initOption = {
- title: {
- text: '▎库存和销量分析',
- left: 20,
- top: 20
- }
- }
- this.chartInstance.setOption(initOption)
- this.chartInstance.on('mouseover', () => {
- clearInterval(this.timerId)
- })
- this.chartInstance.on('mouseout', () => {
- this.startInterval()
- })
- },
- async getData () {
- // 获取服务器的数据, 对this.allData进行赋值之后, 调用updateChart方法更新图表
- const { data: ret } = await this.$http.get('stock')
- this.allData = ret
- console.log(this.allData)
- this.updateChart()
- this.startInterval()
- },
- updateChart () {
- const centerArr = [
- ['18%', '40%'],
- ['50%', '40%'],
- ['82%', '40%'],
- ['34%', '75%'],
- ['66%', '75%']
- ]
- const colorArr = [
- ['#4FF778', '#0BA82C'],
- ['#E5DD45', '#E8B11C'],
- ['#E8821C', '#E55445'],
- ['#5052EE', '#AB6EE5'],
- ['#23E5E5', '#2E72BF']
- ]
- // 处理图表需要的数据
- const start = this.currentIndex * 5
- const end = (this.currentIndex + 1) * 5
- const showData = this.allData.slice(start, end)
- const seriesArr = showData.map((item, index) => {
- return {
- type: 'pie',
- radius: [110, 100],
- center: centerArr[index],
- hoverAnimation: false, // 关闭鼠标移入到饼图时的动画效果
- labelLine: {
- show: false // 隐藏指示线
- },
- label: {
- position: 'center',
- color: colorArr[index][0]
- },
- data: [
- {
- name: item.name + '\n' + item.sales,
- value: item.sales,
- itemStyle: {
- color: new this.$echarts.graphic.LinearGradient(0, 1, 0, 0, [
- {
- offset: 0,
- color: colorArr[index][0]
- },
- {
- offset: 1,
- color: colorArr[index][1]
- }
- ])
- }
- },
- {
- value: item.stock,
- itemStyle: {
- color: '#333843'
- }
- }
- ]
- }
- })
- const dataOption = {
- series: seriesArr
- }
- this.chartInstance.setOption(dataOption)
- },
- screenAdapter () {
- const titleFontSize = this.$refs.stock_ref.offsetWidth / 100 * 3.6
- const innerRadius = titleFontSize * 2
- const outterRadius = innerRadius * 1.125
- const adapterOption = {
- title: {
- textStyle: {
- fontSize: titleFontSize
- }
- },
- series: [
- {
- type: 'pie',
- radius: [outterRadius, innerRadius],
- label: {
- fontSize: titleFontSize / 2
- }
- },
- {
- type: 'pie',
- radius: [outterRadius, innerRadius],
- label: {
- fontSize: titleFontSize / 2
- }
- },
- {
- type: 'pie',
- radius: [outterRadius, innerRadius],
- label: {
- fontSize: titleFontSize / 2
- }
- },
- {
- type: 'pie',
- radius: [outterRadius, innerRadius],
- label: {
- fontSize: titleFontSize / 2
- }
- },
- {
- type: 'pie',
- radius: [outterRadius, innerRadius],
- label: {
- fontSize: titleFontSize / 2
- }
- }
- ]
- }
- this.chartInstance.setOption(adapterOption)
- this.chartInstance.resize()
- },
- startInterval () {
- if (this.timerId) {
- clearInterval(this.timerId)
- }
- this.timerId = setInterval(() => {
- this.currentIndex++
- if (this.currentIndex > 1) {
- this.currentIndex = 0
- }
- this.updateChart() // 在更改完currentIndex之后 , 需要更新界面
- }, 5000)
- }
- }
- }
- </script>
-
- <style lang='less' scoped>
- </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。