合并组件
配置路由规则
创建
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()
})
},