数据可视化大屏-Vue项目

  一、前端项目准备

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文件中:


②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文件中:

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文件中:



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







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





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文件中: 声明主题

 
    
    

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




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文件中:



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






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文件中:







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







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文件中:







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







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文件中:







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






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文件中:






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






三、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.action
      if (action === 'getData') {
        let filePath = '../data/' + payload.chartName + '.json'
        // payload.chartName // trend seller map rank hot stock
        filePath = path.join(__dirname, filePath)
        const ret = await fileUtils.getFileJsonData(filePath)
        // 需要在服务端获取到数据的基础之上, 增加一个data的字段
        // data所对应的值,就是某个json文件的内容
        payload.data = ret
        client.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 = null
  static 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 = false
      this.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.action
        if (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 = 0
      this.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文件中:组件 -导入注册引用





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文件中:

    
    
    

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文件中:


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.js,前端,javascript)