vue+echarts项目十二:使用webSocket优化项目:合并图表到一个页面并添加 切换主题和切换全屏功能

合并组件

配置路由规则

创建 Screen.vue 文件 ,
完整的路由表如下  router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
const Seller = () => import('@/views/SellerPage')
const Trend = () => import('@/views/TrendPage')
const Map = () => import('@/views/MapPage')
const Rank = () => import('@/views/RankPage')
const Hot = () => import('@/views/HotPage')
const Stock = () => import('@/views/StockPage')
const Screen = () => import('../Screen')

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect:'/screen'
  },
  {
    path:'/screen',
    component:Screen
  },
  {
    path:'/seller',
    component:Seller
  },
  {
    path:'/trend',
    component:Trend
  },
  {
    path: '/map',
    component:Map
  },
  {
    path: '/rank',
    component:Rank
  },
  {
    path: '/hot',
    component:Hot
  },
  {
    path: '/stock',
    component:Stock
  }
]

const router = new VueRouter({
  routes
})

export default router

准备相关的图片

到public\static\img 下 各两张 配合明暗两种主图

对页面进行分割百分比布局

<style lang="less" scoped> 
 .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;
      }
    }
  }
</style>

 全部的css:


  .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;
    padding-top: 20px;
    > 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 { ... }
  .resize {
    position: absolute;
    right: 20px;
    top: 20px;
    cursor: pointer;
  }

引入组件 和 注册组件 和一些数据的准备

  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'

    components: {
      Hot,
      Map,
      Rank,
      Seller,
      Stock,
      Trend
    },

    data(){
      return {
        // 定义每一个图表的全屏状态
        fullScreenStatus:{
          trend:false,
          seller:false,
          map:false,
          rank:false,
          hot:false,
          stock:false,
        },
        //  右上角显示事件的定时器
        time:'',
        timeID:null
      }
    },

组件template布局

<template>
  <div class="screen-container" :style="containerStyle">
    <header class="screen-header">
      <div>
        <img :src="herderBorderSrc" alt="">
      </div>
      <span class="logo">
        <img :src="logoSrc" alt="" />
      </span>
      <span class="title">中欣网校数据监控系统</span>
      <div class="title-right">
        <img :src="themeSrc" class="qiehuan" @click="handleChangeTheme()">
        <span class="datetime">{
    
    {time}}</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">
            <!-- icon-compress-alt -->
            <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">
            <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">
            <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">
            <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">
            <span
              @click="changeSize('stock')"
              :class="['iconfont',fullScreenStatus.stock ? 'icon-compress-alt' : 'icon-expand-alt']"></span>
          </div>
        </div>
      </section>
    </div>
  </div>
</template>

 切换主题和各个图表全屏切换+显示当前事件的实现

刚进入组件的时候注册事件+运行定时器

    created() {
      this.$socket.registerCallBack('fullScreen',this.recvData)
      this.$socket.registerCallBack('themeChange',this.recvThemeChange)
      this.dateFormat()
    },

组件销毁时与进入组件操作相反

    destroyed() {
      this.$socket.unRegisterCallBack('fullScreen')
      this.$socket.unRegisterCallBack('themeChange')
      clearInterval(this.timerID)
    },

点击时间旁边的图标触发 切换主题事件

<div class="title-right">
  <img :src="themeSrc" class="qiehuan" @click="handleChangeTheme()">
  <span class="datetime">{
    
    {time}}</span>
</div>
handleChangeTheme(){
  this.$socket.send({
    action:'themeChange',
    socketType: 'themeChange',
    chartName: '',
    value:''
  })
}

上篇文章一个路数  handleChangeTheme 发起send 向服务器提交数据 =》action不是getData 服务器就原路返回数据 =》 前端再监听到服务器返回的数据调用进入页面时注册的函数 recvThemeChange =》而这个函数 调用的是Vuex 来改变当前的主题  

因为各个组件注册的时候 都引入 vuex 的state来管理主题

监听数据改变  进行重绘操作

recvThemeChange(){
  // 修改Vuex中的数据
  this.$store.commit('changeTheme')
},

store/index.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    theme:'chalk'
  },
  getters: {
  },
  mutations: {
    changeTheme(state){
      if (state.theme === 'chalk'){
        state.theme = 'vintage'
      }else {
        state.theme = 'chalk'
      }
    }
  },
  actions: {
  },
  modules: {
  }
})

每个组件的公用代码:映射出来 theme 图表初始化的时候 使用  监听theme  改变时进行重绘

  import {mapState} from "vuex";

    computed:{
      ...mapState(['theme'])
    },

watch:{
  theme(){
    this.chartsInstance.dispose() //销毁当前的图表
    this.initChart() //重新以最新的主图渲染图表
    this.screenAdapter() // 完成屏幕的适配
    this.updateChart() // 更新图表的展示
  }
},

this.chartsInstance = this.$echarts.init(this.$refs.rank_1,this.theme)

切换主题时 主页面一些属性也要跟随变化 logo变成反白、字体的颜色、.....

theme_utils.js  

const theme = {
  chalk:{
    // 背景颜色
    backgroundColor:'#161522',
    // 标题的文字颜色
    titleColor:'#ffffff',
    // 左上角logo的图表路径
    logoSrc:'logo_dark.png',
    // 切换主题按钮的图片路径
    themeSrc:'qiehuan_dark.png',
    // 页面顶部的边框图片
    herderBorderSrc:'header_border_dark.png'
  },
  vintage:{
    // 背景颜色
    backgroundColor:'#eee',
    // 标题的文字颜色
    titleColor:'#000000',
    // 左上角logo的图表路径
    logoSrc:'logo_light2.png',
    // 切换主题按钮的图片路径
    themeSrc:'qiehuan_light.png',
    // 页面顶部的边框图片
    herderBorderSrc:'header_border_light.png'
  }
}

export function getThemeValue(themeName) {
  return theme[themeName]
}

主页面的计算属性:

    computed:{
      ...mapState(['theme']),
      logoSrc(){
        return '/static/img/' + getThemeValue(this.theme).logoSrc
      },
      herderBorderSrc(){
        return '/static/img/' + getThemeValue(this.theme).herderBorderSrc
      },
      themeSrc(){
        return '/static/img/' + getThemeValue(this.theme).themeSrc
      },
      containerStyle(){
        return{
          backgroundColor:getThemeValue(this.theme).backgroundColor,
          color:getThemeValue(this.theme).titleColor
        }
      },
    },

实现每个图例的全屏切换效果:

点击 span 触发changeSize  点击过后就是取反的 fullScreenStatus.具体图表的状态

        <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>
      changeSize(chartsName){
        /**
         * 正常事件进行单客户端的全屏事件切换
         取反点击的具体图表 全屏状态的布尔值
        this.fullScreenStatus[chartsName] = !this.fullScreenStatus[chartsName]
         通过 ref 调用每一个图表 组件内内部 刷新分辨率的方法
        this.$nextTick(() =>{
          this.$refs[chartsName].screenAdapter()
        })
        **/

        // 通过webSocket 进行多端联动的效果
        const targetValue = !this.fullScreenStatus[chartsName]
        this.$socket.send({
          action:'fullScreen',
          socketType:'fullScreen',
          chartName:chartsName,
          value:targetValue
        })
      },

走了一圈还是调用 最开始传出去的函数  recvData()

      // 接收到全屏数据之后的处理
      recvData(data){
        // 取出是哪一个图表需要进行切换
        const chartName = data.chartName
        // 取出 切换成什么状态
        const targetValue = data.value
        // 修改数据 根据模板内三元表达式 动态添加 具体图表的全屏事件
        this.fullScreenStatus[chartName] = targetValue
        // 在渲染完毕后重新刷新 具体图表的分辨率事件
        this.$nextTick(() =>{
          this.$refs[chartName].screenAdapter()
        })
      },

上线地址

visionhttp://www.wsg3096.com/vision/#/screen

 完结撒花!!!

猜你喜欢

转载自blog.csdn.net/benlalagang/article/details/127108180