一、前端项目准备

1.vue-cli 搭建项目

npm install @vue/cli -g    (一台电脑只执行一次即可)

vue create 项目名

选 (下键选择 空格确认)

:Manually select features  手动选择某些特性

:Router  Vuex CSS

:2.0 x

Use history mode for router?是否使用路由:    no

CSS预处理语言  :  Less

ESLint 配置模式 - 标准模式:ESLint + Standard config

何时出现ESLint提示- 保存时:Lint on save

配置问件处理:In dedicated config files  单独存放

Save this as a preset for future projects? (y/N)  :no

cd 项目名

npm run serve

2.删除无关代码

①App.vue文件中:

<template><div id="app">   //待编写  </div>
</template>
<style lang="less">
</style>

②components文件夹、 views文件夹   清空

③router/index.js文件中 剩余内容:

import Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [  ]
const router = new VueRouter({routes
})export default router

3.静态资源的引入

图片img、第三方包lib、地图map、主题theme   放在public文件夹下

4.项目的基本配置

根目录下创建vue.config.js文件:

module.exports = {devServer: {port: 8999, //前端 端口号  访问网址(http://localhost:8999/#/具体页面路由)open: true //自动打开浏览器}
};

5.全局echarts对象的挂载

public/index.html文件中:

<script src="echarts.min.js文件的路径地址"></script>

src/main.js文件中:

// 将全局的echarts对象挂载到Vue的原型对象上
// 别的组件中 使用this.$echarts
Vue.prototype.$echarts = window.echarts;

6.axios的封装与挂载

下载axios模块 :npm install axios

src/main.js文件中:

import axios from "axios";   //引入axios
// 请求基准路径的配置  接口前缀
axios.defaults.baseURL = "http://127.0.0.1:8888/api/";
// 将axios挂载到Vue的原型对象上   在别的组件中 使用this.$http发起ajax请求
Vue.prototype.$http = axios;

二、单独图表组件的开发

1.横向柱状图 Seller.vue

①组件结构和布局结构的设计

router/index.js文件中:(定义路由)

import SellerPage from '@/views/SellerPage'
const routes = [{ path: '/sellerpage', component: SellerPage }]  //路由规则

App.vue文件中:

<template><div id="app"><router-view></router-view>  //路由的占位符</div>
</template><style lang="less">
</style>

父- views/SellerPage.vue文件中:(创建SellerPage.vue文件)

<!--针对于 /sellerpage 这条路径而显示出来的   在这个组件中, 通过子组件注册的方式, 要显示出Seller.vue这个组件 -->
<template><div class="com-page"><Seller></Seller>   ③  //显示子组件</div>
</template><script>
import Seller from '@/components/Seller'  ① //引入子组件Seller
export default {data () {return {}},methods: {},components: {Seller: Seller  ②   //注册子组件}
}
</script><style lang="less" scoped>
</style>

子- components/Seller.vue文件中:(创建Seller.vue文件,功能主要写在这个文件里面)

<!-- 商家销量统计的横向柱状图 -->
<template><div class="com-container"><div class="com-chart" > 你好 </div></div>
</template>
<script></script>
<style lang="less" scoped></style>

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: 900px;   //注意overflow: hidden;
}
canvas {border-radius: 20px;  // 全局样式   圆角
}
.com-container {position: relative;
}

main.js文件中:

// 引入全局的样式文件
import "./assets/css/global.less";

public/index.html文件中: 声明主题

 <!-- 引入主题的js文件 --><script src="static/theme/chalk.js"></script><script src="static/theme/vintage.js"></script>

components/Seller.vue文件中:总的代码

<template><div class="com-container"><div class="com-chart" ref="seller_ref"></div>    //ref更好的获取dom</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: {initChart () {this.chartInstance = this.$echarts.init(this.$refs.seller_ref, 'chalk') //chalk使用主题// start// 对图表初始化配置的控制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)// end// 对图表对象进行鼠标事件的监听this.chartInstance.on('mouseover', () => {clearInterval(this.timerId)})this.chartInstance.on('mouseout', () => {this.startInterval()})},async getData () {   //获取后端的数据// http://127.0.0.1:8888/api/sellerconst { data: ret } = await this.$http.get('seller')console.log(ret)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 + 1this.updateChart()this.startInterval()},updateChart () {const start = (this.currentPage - 1) * 5const end = this.currentPage * 5const showData = this.allData.slice(start, end)const sellerNames = showData.map(item => {return item.name})const sellerValues = showData.map(item => {return item.value})// 获取数据之后的配置optionconst 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>
</style>

2.折线图:Trend.vue

main.js文件中:

import './assets/font/iconfont.css'  //引入字体icon的样式

router/index.js文件中:

import MapPage from '@/views/MapPage'const routes = [{ path: '/sellerpage', component: SellerPage },{ path: '/trendpage', component: TrendPage },
]

views/TrendPage.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>

componens/Trend.vue文件中:总的代码

<template><div class="com-container"><div class="title":style="comStyle"><span>{{ '▎ ' + showTitle }}</span><span class="iconfont title-icon"   //使用字体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>
import { mapState } from 'vuex'
// import { getThemeValue } from '@/utils/theme_utils'
export default {data () {return {chartInstane: null,allData: null, // 从服务器中获取的所有数据showChoice: false, // 是否显示可选项choiceType: 'map', // 显示的数据类型titleFontSize: 0 // 指明标题的字体大小}},// created () {//   // 在组件创建完成之后 进行回调函数的注册//   this.$socket.registerCallBack('trendData', this.getData)// },mounted () {this.initChart()this.getData()// 发送数据给服务器, 告诉服务器, 我现在需要数据// this.$socket.send({//   action: 'getData',//   socketType: 'trendData',//   chartName: 'trend',//   value: ''// })window.addEventListener('resize', this.screenAdapter)this.screenAdapter()},destroyed () {window.removeEventListener('resize', this.screenAdapter)// 在组件销毁的时候, 进行回调函数的取消// this.$socket.unRegisterCallBack('trendData')},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'// color: getThemeValue(this.theme).titleColor}},marginStyle () {return {marginLeft: this.titleFontSize + 'px'}},...mapState(['theme'])},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)},// ret 就是服务端发送给客户端的图表的数据async getData () {// await this.$http.get()// 对allData进行赋值const { data: ret } = await this.$http.get('trend')this.allData = retconsole.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].dataconst 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.6const 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 = currentTypethis.updateChart()this.showChoice = false}}// watch: {//   theme () {//     console.log('主题切换了')//     this.chartInstane.dispose() // 销毁当前的图表//     this.initChart() // 重新以最新的主题名称初始化图表对象//     this.screenAdapter() // 完成屏幕的适配//     this.updateChart() // 更新图表的展示//   }// }
}
</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>

3.地图+散点图

router/index.js文件中:

import MapPage from '@/views/MapPage'const routes = [{ path: '/sellerpage', component: SellerPage },{ path: '/trendpage', component: TrendPage },{ path: '/mappage', component: MapPage }
]

views/MapPage.vue文件中:

<!-- 针对于 /mappage 这条路径而显示出来的
在这个组件中, 通过子组件注册的方式, 要显示出Map.vue这个组件 -->
<template><div class="com-page"><Map></Map></div>
</template><script>
import Map from '@/components/Map'
export default {data () {return {}},methods: {},components: {Map: Map}
}
</script><style lang="less" scoped>
</style>

componens/Map.vue文件中:总的代码

<!-- 商家分布图表  地图+散点图-->
<template><div class='com-container'  @dblclick="revertMap"><div class='com-chart'  ref='map_ref'></div></div>
</template><script>
import { mapState } from 'vuex'
import axios from 'axios'
import { getProvinceMapInfo } from '@/utils/map_utils'
export default {data () {return {chartInstance: null,allData: null,mapData: {} // 缓存 所获取的省份的地图矢量数据}},// created () {//   // 在组件创建完成之后 进行回调函数的注册//   this.$socket.registerCallBack('mapData', this.getData)// },mounted () {this.initChart()this.getData()// this.$socket.send({//   action: 'getData',//   socketType: 'mapData',//   chartName: 'map',//   value: ''// })window.addEventListener('resize', this.screenAdapter)this.screenAdapter()},destroyed () {window.removeEventListener('resize', this.screenAdapter)// this.$socket.unRegisterCallBack('mapData')},methods: {async initChart () {this.chartInstance = this.$echarts.init(this.$refs.map_ref, 'chalk')// this.chartInstance = this.$echarts.init(this.$refs.map_ref, this.theme)// 获取中国地图的矢量数据// http://localhost:8999/static/map/china.json// 由于我们现在获取的地图矢量数据并不是位于KOA2的后台, 所以咱们不能使用this.$httpconst 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 => {console.log(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.datathis.$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 => {// console.log('item.name', item.name)return item.name})const seriesArr = this.allData.map(item => {// return的这个对象就代表的是一个类别下的所有散点数据// 如果想在地图中显示散点的数据, 我们需要给散点的图表增加一个配置, coordinateSystem:georeturn {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 () {// console.log('this.$refs.map_ref.offsetWidth', this.$refs.map_ref.offsetWidth)const titleFontSize = this.$refs.map_ref.offsetWidth / 100 * 3.6const 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)}},computed: {...mapState(['theme'])},watch: {theme () {console.log('主题切换了')this.chartInstance.dispose() // 销毁当前的图表this.initChart() // 重新以最新的主题名称初始化图表对象this.screenAdapter() // 完成屏幕的适配this.updateChart() // 更新图表的展示}}
}
</script><style lang='less' scoped>
</style>

4.柱状图

router/index.js文件中:

import Vue from 'vue'
import VueRouter from 'vue-router'import TrendPage from '@/views/TrendPage'
import SellerPage from '@/views/SellerPage'
import MapPage from '@/views/MapPage'
import RankPage from '@/views/RankPage'Vue.use(VueRouter)const routes = [{ path: '/sellerpage', component: SellerPage },{ path: '/trendpage', component: TrendPage },{ path: '/mappage', component: MapPage },{ path: '/rankpage', component: RankPage }]const router = new VueRouter({routes
})export default router

views/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>

componens/Rank.vue文件中:总的代码

<!-- 地区销售排行  柱状图-->
<template><div class='com-container'><div class='com-chart'ref='rank_ref'></div></div>
</template><script>
import { mapState } from 'vuex'
export default {data () {return {chartInstance: null,allData: null,startValue: 0, // 区域缩放的起点值endValue: 9, // 区域缩放的终点值timerId: null // 定时器的标识}},// created () {//   // 在组件创建完成之后 进行回调函数的注册//   this.$socket.registerCallBack('rankData', this.getData)// },mounted () {this.initChart()this.getData()// this.$socket.send({//   action: 'getData',//   socketType: 'rankData',//   chartName: 'rank',//   value: ''// })window.addEventListener('resize', this.screenAdapter)this.screenAdapter()},destroyed () {window.removeEventListener('resize', this.screenAdapter)clearInterval(this.timerId) // 销毁定时器// this.$socket.unRegisterCallBack('rankData')},methods: {initChart () {// this.chartInstance = this.$echarts.init(this.$refs.rank_ref, this.theme)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']]// 处理图表需要的数据// 所有省份所形成的数组  x轴数据const provinceArr = this.allData.map(item => {return item.name})// 所有省份对应的销售金额  y轴数据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 = nullif (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.6const 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 = 0this.endValue = 9}this.updateChart()}, 2000)}},computed: {...mapState(['theme'])},watch: {theme () {console.log('主题切换了')this.chartInstance.dispose() // 销毁当前的图表this.initChart() // 重新以最新的主题名称初始化图表对象this.screenAdapter() // 完成屏幕的适配this.updateChart() // 更新图表的展示}}
}
</script><style lang='less' scoped>
</style>

5.饼图

router/index.js文件中:

import Vue from 'vue'
import VueRouter from 'vue-router'import TrendPage from '@/views/TrendPage'
import SellerPage from '@/views/SellerPage'
import MapPage from '@/views/MapPage'
import RankPage from '@/views/RankPage'
import HotPage from '@/views/HotPage'Vue.use(VueRouter)const routes = [{ path: '/sellerpage', component: SellerPage },{ path: '/trendpage', component: TrendPage },{ path: '/mappage', component: MapPage },{ path: '/rankpage', component: RankPage },{ path: '/hotpage', component: HotPage }]const router = new VueRouter({routes
})export default router

views/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>

componens/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>
import { mapState } from 'vuex'
// import { getThemeValue } from '@/utils/theme_utils'
export default {data () {return {chartInstance: null,allData: null,currentIndex: 0, // 当前所展示出的一级分类数据titleFontSize: 0}},// created () {//   // 在组件创建完成之后 进行回调函数的注册//   this.$socket.registerCallBack('hotData', this.getData)// },computed: {catName () {if (!this.allData) { // 一级标题的显示return ''} else {return this.allData[this.currentIndex].name}},comStyle () { //return {fontSize: this.titleFontSize + 'px'// color: getThemeValue(this.theme).titleColor}},...mapState(['theme'])},mounted () {this.initChart()this.getData()// this.$socket.send({//   action: 'getData',//   socketType: 'hotData',//   chartName: 'hot',//   value: ''// })window.addEventListener('resize', this.screenAdapter)this.screenAdapter()},destroyed () {window.removeEventListener('resize', this.screenAdapter)// this.$socket.unRegisterCallBack('hotData')},methods: {initChart () {// this.chartInstance = this.$echarts.init(this.$refs.hot_ref, this.theme)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 = 0thirdCategory.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 = retconsole.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.6const adapterOption = {title: { // 标题的文字大小textStyle: {fontSize: this.titleFontSize}},legend: { // 图例的大小itemWidth: this.titleFontSize,itemHeight: this.titleFontSize,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()}},watch: {theme () {console.log('主题切换了')this.chartInstance.dispose() // 销毁当前的图表this.initChart() // 重新以最新的主题名称初始化图表对象this.screenAdapter() // 完成屏幕的适配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>

6.循环饼图

router/index.js文件中:

import Vue from 'vue'
import VueRouter from 'vue-router'import TrendPage from '@/views/TrendPage'
import SellerPage from '@/views/SellerPage'
import MapPage from '@/views/MapPage'
import RankPage from '@/views/RankPage'
import HotPage from '@/views/HotPage'
import StockPage from '@/views/StockPage'Vue.use(VueRouter)const routes = [{ path: '/sellerpage', component: SellerPage },{ path: '/trendpage', component: TrendPage },{ path: '/mappage', component: MapPage },{ path: '/rankpage', component: RankPage },{ path: '/hotpage', component: HotPage },{ path: '/stockpage', component: StockPage }]const router = new VueRouter({routes
})export default router

views/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>

componens/Stock.vue文件中:总的代码

<!-- 库存销量分析 -->
<template><div class='com-container'><div class='com-chart'ref='stock_ref'></div></div>
</template><script>
import { mapState } from 'vuex'
export default {data () {return {chartInstance: null,allData: null,currentIndex: 0, // 当前显示的数据timerId: null // 定时器的标识}},created () {// 在组件创建完成之后 进行回调函数的注册this.$socket.registerCallBack('stockData', this.getData)},mounted () {this.initChart()// this.getData()this.$socket.send({action: 'getData',socketType: 'stockData',chartName: 'stock',value: ''})window.addEventListener('resize', this.screenAdapter)this.screenAdapter()},destroyed () {window.removeEventListener('resize', this.screenAdapter)clearInterval(this.timerId)this.$socket.unRegisterCallBack('stockData')},methods: {initChart () {this.chartInstance = this.$echarts.init(this.$refs.stock_ref, this.theme)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()})},getData (ret) {// 获取服务器的数据, 对this.allData进行赋值之后, 调用updateChart方法更新图表// const { data: ret } = await this.$http.get('stock')this.allData = retconsole.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 * 5const end = (this.currentIndex + 1) * 5const showData = this.allData.slice(start, end)const seriesArr = showData.map((item, index) => {return {type: 'pie',center: centerArr[index],hoverAnimation: false, // 关闭鼠标移入到饼图时的动画效果labelLine: {show: false // 隐藏指示线},label: {position: 'center',color: colorArr[index][0]},data: [{name: item.name + '\n\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.6const innerRadius = titleFontSize * 2.8const outterRadius = innerRadius * 1.125const 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)}},computed: {...mapState(['theme'])},watch: {theme () {console.log('主题切换了')this.chartInstance.dispose() // 销毁当前的图表this.initChart() // 重新以最新的主题名称初始化图表对象this.screenAdapter() // 完成屏幕的适配this.updateChart() // 更新图表的展示}}
}
</script><style lang='less' scoped>
</style>

三、WebSocket的引入

1.后端 app.js文件中:

const webSocketService = require('./service/web_socket_service')
// 开启服务端的监听, 监听客户端的连接
// 当某一个客户端连接成功之后, 就会对这个客户端进行message事件的监听
webSocketService.listen()
src/utils/file_utils.js文件中:
// 读取文件的工具方法
const fs = require("fs");
module.exports.getFileJsonData = filePath => {//   return "你好";return new Promise((resolve, reject) => {fs.readFile(filePath, "utf-8", (error, data) => {if (error) {reject(error);} else {resolve(data);}});});
};

src/service/web_socket_service.js文件中:

const path = require('path')
const fileUtils = require('../utils/file_utils')
//WebSocket的引入
//下载插件  npm i ws -S
const WebSocket = require('ws')
// 创建WebSocket服务端的对象, 绑定的端口号是9998
const wss = new WebSocket.Server({port: 9998
})// 服务端开启了监听
module.exports.listen = () => {// 对客户端的连接事件进行connection事件的监听// client:代表的是客户端的连接socket对象wss.on('connection', client => {console.log('有客户端连接成功了...')// 对客户端的连接对象进行message事件的监听// msg: 由客户端发给服务端的数据client.on('message', async msg => {console.log('客户端发送数据给服务端了: ' + msg)let payload = JSON.parse(msg)const action = payload.actionif (action === 'getData') {let filePath = '../data/' + payload.chartName + '.json'// payload.chartName // trend seller map rank hot stockfilePath = path.join(__dirname, filePath)const ret = await fileUtils.getFileJsonData(filePath)// 需要在服务端获取到数据的基础之上, 增加一个data的字段// data所对应的值,就是某个json文件的内容payload.data = retclient.send(JSON.stringify(payload))} else {// 原封不动的将所接收到的数据转发给每一个处于连接状态的客户端// wss.clients // 所有客户端的连接wss.clients.forEach(client => {client.send(msg)})}// 由服务端往客户端发送数据// client.send('hello socket from backend')})})
}

2.前端

src/utils/socket_service.js 文件中:
export default class SocketService {/*** 单例*/static instance = nullstatic get Instance () {if (!this.instance) {this.instance = new SocketService()}return this.instance}// 和服务端连接的socket对象ws = null// 存储回调函数callBackMapping = {}// 标识是否连接成功connected = false// 记录重试的次数sendRetryCount = 0// 重新连接尝试的次数connectRetryCount = 0//  定义连接服务器的方法connect () {// 连接服务器if (!window.WebSocket) {return console.log('您的浏览器不支持WebSocket')}this.ws = new WebSocket('ws://localhost:9998')// 连接成功的事件this.ws.onopen = () => {console.log('连接服务端成功了')this.connected = true// 重置重新连接的次数this.connectRetryCount = 0}// 1.连接服务端失败// 2.当连接成功之后, 服务器关闭的情况this.ws.onclose = () => {console.log('连接服务端失败')this.connected = falsethis.connectRetryCount++setTimeout(() => {this.connect()}, 500 * this.connectRetryCount)}// 得到服务端发送过来的数据this.ws.onmessage = msg => {console.log('从服务端获取到了数据')// 真正服务端发送过来的原始数据时在msg中的data字段// console.log(msg.data)const recvData = JSON.parse(msg.data)const socketType = recvData.socketType// 判断回调函数是否存在if (this.callBackMapping[socketType]) {const action = recvData.actionif (action === 'getData') {const realData = JSON.parse(recvData.data)this.callBackMapping[socketType].call(this, realData)} else if (action === 'fullScreen') {this.callBackMapping[socketType].call(this, recvData)} else if (action === 'themeChange') {this.callBackMapping[socketType].call(this, recvData)}}}}// 回调函数的注册registerCallBack (socketType, callBack) {this.callBackMapping[socketType] = callBack}// 取消某一个回调函数unRegisterCallBack (socketType) {this.callBackMapping[socketType] = null}// 发送数据的方法send (data) {// 判断此时此刻有没有连接成功if (this.connected) {this.sendRetryCount = 0this.ws.send(JSON.stringify(data))} else {this.sendRetryCount++setTimeout(() => {this.send(data)}, this.sendRetryCount * 500)}}
}

src/main.js文件中:

import SocketService from '@/utils/socket_service'
// 对服务端进行websocket的连接
SocketService.Instance.connect()
// 其他的组件  this.$socket
Vue.prototype.$socket = SocketService.Instance

componens/Seller.vue文件中:  其他组件一样

created () {// 在组件创建完成之后 进行回调函数的注册this.$socket.registerCallBack('sellerData', this.getData)
},
mounted () {// this.getData()this.$socket.send({action: 'getData',socketType: 'sellerData',chartName: 'seller',value: ''})
},
destroyed () {this.$socket.unRegisterCallBack('sellerData')
},methods:{// 获取服务器的数据getData (ret) {     // const { data: ret } = await this.$http.get('seller')this.allData = ret },
}

四、细节处置

1.组件合并

src/router/index.js文件中:

import Vue from 'vue'
import VueRouter from 'vue-router'
import ScreenPage from '@/views/ScreenPage'Vue.use(VueRouter)const routes = [{path: '/',redirect: '/screen'},{path: '/screen',component: ScreenPage}
]
const router = new VueRouter({routes
})
export default router

src/views/ScreenPage.vue文件中:组件 -导入注册引用

<template><div class="screen-container":style="containerStyle"><header class="screen-header"><div><img :src="headerSrc"   alt=""></div><span class="title">电商平台实时监控系统</span><div class="title-right"><img :src="themeSrc"   class="qiehuan"  @click="handleChangeTheme"><span class="datetime">{{timeComput}}</span></div></header><div class="screen-body"><section class="screen-left"><div id="left-top" :class="[fullScreenStatus.trend ? 'fullscreen' : '']"><!-- 销量趋势图表 --><Trend ref="trend"></Trend><div class="resize"><span @click="changeSize('trend')":class="['iconfont', fullScreenStatus.trend ? 'icon-compress-alt' : 'icon-expand-alt']"></span></div></div><div id="left-bottom":class="[fullScreenStatus.seller ? 'fullscreen' : '']"><!-- 商家销售金额图表 --><Seller ref="seller"></Seller><div class="resize"><!-- icon-compress-alt --><span @click="changeSize('seller')":class="['iconfont', fullScreenStatus.seller ? 'icon-compress-alt' : 'icon-expand-alt']"></span></div></div></section><section class="screen-middle"><div id="middle-top":class="[fullScreenStatus.map ? 'fullscreen' : '']"><!-- 商家分布图表 --><Map ref="map"></Map><div class="resize"><!-- icon-compress-alt --><span @click="changeSize('map')":class="['iconfont', fullScreenStatus.map ? 'icon-compress-alt' : 'icon-expand-alt']"></span></div></div><div id="middle-bottom":class="[fullScreenStatus.rank ? 'fullscreen' : '']"><!-- 地区销量排行图表 --><Rank ref="rank"></Rank><div class="resize"><!-- icon-compress-alt --><span @click="changeSize('rank')":class="['iconfont', fullScreenStatus.rank ? 'icon-compress-alt' : 'icon-expand-alt']"></span></div></div></section><section class="screen-right"><div id="right-top":class="[fullScreenStatus.hot ? 'fullscreen' : '']"><!-- 热销商品占比图表 --><Hot ref="hot"></Hot><div class="resize"><!-- icon-compress-alt --><span @click="changeSize('hot')":class="['iconfont', fullScreenStatus.hot ? 'icon-compress-alt' : 'icon-expand-alt']"></span></div></div><div id="right-bottom":class="[fullScreenStatus.stock ? 'fullscreen' : '']"><!-- 库存销量分析图表 --><Stock ref="stock"></Stock><div class="resize"><!-- icon-compress-alt --><span @click="changeSize('stock')":class="['iconfont', fullScreenStatus.stock ? 'icon-compress-alt' : 'icon-expand-alt']"></span></div></div></section></div></div>
</template><script>
import Hot from '@/components/Hot.vue'
import Map from '@/components/Map.vue'
import Rank from '@/components/Rank.vue'
import Seller from '@/components/Seller.vue'
import Stock from '@/components/Stock.vue'
import Trend from '@/components/Trend.vue'
import { mapState } from 'vuex'
import { getThemeValue } from '@/utils/theme_utils'
export default {created () {// 注册接收到数据的回调函数this.$socket.registerCallBack('fullScreen', this.recvData)this.$socket.registerCallBack('themeChange', this.recvThemeChange)},mounted () {this.startInterval()},destroyed () {this.$socket.unRegisterCallBack('fullScreen')this.$socket.unRegisterCallBack('themeChange')},data () {return {// 定义每一个图表的全屏状态fullScreenStatus: {trend: false,seller: false,map: false,rank: false,hot: false,stock: false},timeComput: '',timer: ''// 定义一个定时器的变量}},methods: {// 开启定时器startInterval () {if (this.timer) { // 开启定时器之前先取消定时器,节流防抖clearInterval(this.timer)}this.timer = setInterval(() => {this.CurentTime()}, 1000)},CurentTime () {const now = new Date()const year = now.getFullYear() // 年const month = now.getMonth() + 1 // 月const day = now.getDate() // 日const hh = now.getHours() // 时const mm = now.getMinutes() // 分const ss = now.getSeconds() // 秒let clock = year + '-'if (month < 10) { clock += '0' }clock += month + '-'if (day < 10) { clock += '0' }clock += day + ' 'if (hh < 10) { clock += '0' }clock += hh + ':'if (mm < 10) clock += '0'clock += mm + ':'if (ss < 10) clock += '0'clock += ssthis.timeComput = clock},changeSize (chartName) {// 1.改变fullScreenStatus的数据this.fullScreenStatus[chartName] = !this.fullScreenStatus[chartName]// 2.需要调用每一个图表组件的screenAdapter的方法this.$refs[chartName].screenAdapter()this.$nextTick(() => {this.$refs[chartName].screenAdapter()})// // 全屏联动 -- 将数据发送给服务端// const targetValue = !this.fullScreenStatus[chartName]// this.$socket.send({//   action: 'fullScreen',//   socketType: 'fullScreen',//   chartName: chartName,//   value: targetValue// })},// 接收到全屏数据之后的处理recvData (data) {// 取出是哪一个图表需要进行切换const chartName = data.chartName// 取出, 切换成什么状态const targetValue = data.valuethis.fullScreenStatus[chartName] = targetValuethis.$nextTick(() => {this.$refs[chartName].screenAdapter()})},handleChangeTheme () {// 修改VueX中数据this.$store.commit('changeTheme')// this.$socket.send({//   action: 'themeChange',//   socketType: 'themeChange',//   chartName: '',//   value: ''// })},recvThemeChange () {this.$store.commit('changeTheme')}},components: {Hot,Map,Rank,Seller,Stock,Trend},computed: {logoSrc () {return '/static/img/' + getThemeValue(this.theme).logoSrc},headerSrc () {return '/static/img/' + getThemeValue(this.theme).headerBorderSrc},themeSrc () {return '/static/img/' + getThemeValue(this.theme).themeSrc},containerStyle () {return {backgroundColor: getThemeValue(this.theme).backgroundColor,color: getThemeValue(this.theme).titleColor}},...mapState(['theme'])}
}
</script>
<style lang="less" scoped>
// 全屏样式的定义
.fullscreen {position: fixed !important;top: 0 !important;left: 0 !important;width: 100% !important;height: 100% !important;margin: 0 !important;z-index: 100;
}.screen-container {width: 100%;height: 100%;padding: 0 20px;background-color: #161522;color: #fff;box-sizing: border-box;
}
.screen-header {width: 100%;height: 64px;font-size: 20px;position: relative;> div {img {width: 100%;}}.title {position: absolute;left: 50%;top: 50%;font-size: 20px;transform: translate(-50%, -50%);}.title-right {display: flex;align-items: center;position: absolute;right: 0px;top: 50%;transform: translateY(-80%);}.qiehuan {width: 28px;height: 21px;cursor: pointer;}.datetime {font-size: 15px;margin-left: 10px;}.logo {position: absolute;left: 0px;top: 50%;transform: translateY(-80%);img {height: 35px;width: 128px;}}
}
.screen-body {width: 100%;height: 100%;display: flex;margin-top: 10px;.screen-left {height: 100%;width: 27.6%;#left-top {height: 53%;position: relative;}#left-bottom {height: 31%;margin-top: 25px;position: relative;}}.screen-middle {height: 100%;width: 41.5%;margin-left: 1.6%;margin-right: 1.6%;#middle-top {width: 100%;height: 56%;position: relative;}#middle-bottom {margin-top: 25px;width: 100%;height: 28%;position: relative;}}.screen-right {height: 100%;width: 27.6%;#right-top {height: 46%;position: relative;}#right-bottom {height: 38%;margin-top: 25px;position: relative;}}
}
.resize {position: absolute;right: 20px;top: 20px;cursor: pointer;
}
</style>

2.主题切换

①图表主题

src/store/index.js文件中:数据存储

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {theme: 'chalk'},mutations: {changeTheme (state) {if (state.theme === 'chalk') {state.theme = 'vintage'} else {state.theme = 'chalk'}}},actions: {},modules: {}
})

public/static/index.html文件中:

    <!-- 引入主题的js文件 --><script src="static/theme/chalk.js"></script><script src="static/theme/vintage.js"></script>

src/views/ScreenPage.vue文件中:

methods:{handleChangeTheme () {// 修改VueX中数据this.$store.commit('changeTheme')}
}

components/Seller.vue文件中:   其他组件同样

 import { mapState } from 'vuex'computed: {...mapState(['theme'])},watch: {theme () {console.log('主题切换了')this.chartInstance.dispose() // 销毁当前的图表this.initChart() // 重新以最新的主题名称初始化图表对象this.screenAdapter() // 完成屏幕的适配this.updateChart() // 更新图表的展示}},methods:{initChart () {this.chartInstance = this.$echarts.init(this.$refs.hot_ref, this.theme)}}

②特殊html的主题:

src/utils/theme_utils.js文件中:

const theme = {chalk: {backgroundColor: '#161522', // 背景颜色   titleColor: '#ffffff', // 标题的文字颜色   logoSrc: 'logo_dark.png', // 左上角logo的图标路径   themeSrc: 'qiehuan_dark.png', // 切换主题按钮的图片路径    headerBorderSrc: 'header_border_dark.png'// 页面顶部的边框图片},vintage: {backgroundColor: '#eeeeee', // 背景颜色titleColor: '#000000',// 标题的文字颜色logoSrc: 'logo_light2.png',  // 左上角logo的图标路径    themeSrc: 'qiehuan_light.png',// 切换主题按钮的图片路径   headerBorderSrc: 'header_border_light.png' // 页面顶部的边框图片}
}export function getThemeValue (themeName) {return theme[themeName]
}

components/Hot.vue文件中:   其他组件同样

import { getThemeValue } from '@/utils/theme_utils'computed: {comStyle () { return {color: getThemeValue(this.theme).titleColor}},},

src/views/ScreenPage.vue文件中:


<div  :style="containerStyle">
<img :src="headerSrc"  />
<img :src="themeSrc" />
import { mapState } from 'vuex'
import { getThemeValue } from '@/utils/theme_utils'
computed: {headerSrc () {return '/static/img/' + getThemeValue(this.theme).headerBorderSrc},themeSrc () {return '/static/img/' + getThemeValue(this.theme).themeSrc},containerStyle () {return {backgroundColor: getThemeValue(this.theme).backgroundColor,color: getThemeValue(this.theme).titleColor}},...mapState(['theme'])
}

数据可视化大屏-Vue项目相关推荐

  1. 使用Vue实现数据可视化大屏功能(二)

    引入数据大屏相关组件   用Datav插件做大屏可视化的组件,官网地址 http://datav.jiaminghi.com/ ,整个组件库都是基于Vue React版本实现,主要用于构建大屏数据可视 ...

  2. 使用Vue实现数据可视化大屏功能(一)

    导语   现在在很多的工程项目中,都有有关于数据大屏相关的监控内容,这里我们就来看一下如何用Vue来搭建一个数据可视化大屏应用. 创建项目   使用WebStorm工具创建一个Vue的项目.如下图所示 ...

  3. Vue中如何进行数据可视化大屏展示

    Vue中如何进行数据可视化大屏展示 在现代数据驱动的应用程序中,数据可视化大屏已经成为了非常重要的一环.通过对海量数据进行可视化展示,可以帮助用户更好地理解和分析数据,从而做出更加明智的决策.在Vue ...

  4. 基于vue+echarts 数据可视化大屏展示[附源码]

    获取 ECharts 的路径有以下几种,请根据您的情况进行选择: 1) 最直接的方法是在 ECharts 的官方网站中挑选适合您的版本进行下载,不同的打包下载应用于不同的开发者功能与体积的需求,或者您 ...

  5. 【Dash搭建可视化网站】项目10:疫情数据可视化大屏制作步骤详解

    疫情数据可视化大屏制作步骤详解 1 项目效果图 2 项目架构 3 文件介绍和功能完善 3.1 assets文件夹介绍 3.2 app.py和index.py文件完善 3.3 header.py文件完善 ...

  6. 怎么做数据可视化大屏?从设计到上线,一般用这3类工具

    数据可视化大屏成为了这两年很火爆的一个需求. 一方面,不少甲方都想做这么酷炫的大屏,用于公司展厅.日常经营监控,还有些特殊行业如交通.运输.工厂制造,会做更高级的3D建模等. 另一方面,市面上可提供做 ...

  7. YYDatav的数据可视化大屏《精彩案例汇总》(PythonEcharts源码)

    一. 资源下载 [1-10]套Python+Echarts数据可视化大屏案例(共10套)-企业管理文档类资源-CSDN下载第1篇https://yydatav.blog.csdn.net/articl ...

  8. 基于JavaScript+Koa2实现 Echarts 电商平台数据可视化大屏全栈【100010415】

    全新 Echarts 电商平台数据可视化大屏全栈 1. 前言 五一假期重学了新版 Echarts,一个基于 JavaScript 的开源可视化图表库,收集参考了很多网上资料,最终选择电商平台作为练手项 ...

  9. 如何保证两个不同宽高的canvas用同一组坐标正常显示_如何1人5天开发完3D数据可视化大屏 【一】...

    相信从事过数据可视化开发的你对大屏并不陌生,那么开发一个酷炫的大屏一定是很多数据可视化开发者想要做的事情. 我们使用three.js,大约一周的时间开发出了一个酷炫的数据可视化大屏: 1. 前言 由于 ...

  10. Qt编写数据可视化大屏界面电子看板12-数据库采集

    一.前言 数据采集是整个数据可视化大屏界面电子看板系统核心功能,没有数据源,这仅仅是个玩具UI,没啥用,当然默认做了定时器模拟数据,产生随机数据,这个可以直接配置文件修改来选择采用何种数据采集方法,总 ...

最新文章

  1. http://wenku.baidu.com/view/26afdb8371fe910ef12df8ccRevit采用DWG和FBX两种格式导入3D max方法的总结...
  2. 十四、矩阵的快速转置算法
  3. 小余学调度:电气主接线的形式、特点、倒闸操作
  4. 经营为什么需要哲学(学习总结)
  5. 非线性时延系统matlab框图,非线性主-从时延系统的时滞相关有限时间同步控制方法与流程...
  6. 2016年学习Linux决心书(老男孩教育在线课程班第二期)
  7. 容器技术之快速了解K8S各抽象资源及组件架构
  8. 中移4G模块-ML302-OpenCpu开发-服务器搭建
  9. 服务器为什么要装操作系统,服务器需要安装操作系统吗
  10. centos mysql 主从_CentOS 搭建 MySql 主从备份
  11. 洛谷 P1426 小鱼会有危险吗(C语言)
  12. 【预测模型】基于matlab RLS算法预测【含Matlab源码 222期】
  13. Makefile文件生成 GNU Autotools的使用方法
  14. 什么是 GPL GNU,自由软件
  15. mysql修改information_schema表时的错误
  16. 三极管场效应参数全集
  17. uboot中展示gpio接口的驱动
  18. 防火墙阻止应用程序linux,win10系统下如何解除被防火墙阻止运行的应用程序
  19. ubuntu 查看usb设备
  20. 闲人闲谈PS之二十九——关于精确统计工程合同产值问题

热门文章

  1. Python 矩形法求1/x的定积分(完美实现)
  2. 网站服务器怎么查ipv4,服务器的ipv4地址怎么查
  3. html 自动填充 颜色,excel如何设置输入数字后单元格自动填充颜色
  4. 解决Windows Server 2008 System进程占用80端口问题
  5. 1147 Heaps (30分)
  6. python中变量名有哪些
  7. 除了加速上币,OKEx在DeFi热潮中还能做什么?
  8. java 编写序列_java如何编写类似oracle自增序列的算法?
  9. 如何实现Android端的录屏采集
  10. Win7窗口最大化和最小化快捷键