大屏适配解决方案——vw和vh适配方案

整体思路

  • 整体宽高布局100vw,100vh,每个模块的宽高:使用vw,vh
  • 字体大小适配:px转化为vw、vh
  • 处理公共样式:新建一个rem.scss文件
  • 图表根据屏幕尺寸变化自动resize():自定义指令
  • 图表里的文字,例如图例、横坐标等字体大小适配:公共方法
  • 子组件监听数据变化实时刷新图表
  • websocket:递增间隔重连
  • 订单统计列表数据轮播切换

1、按照设计稿的尺寸,将px按比例计算转为vw和vh

util.scss(仅需修改设计稿的默认宽高

//假设设计稿尺寸为1920x*1080px的屏幕分辨率
//1920px = 100vw
//1080px = 100vh

@use "sass:math"; 
//默认设计稿的宽度
$designWidth:1920;
//默认设计稿的高度
$designHeight:1080;

//px转为vw的函数
@function vw($px) {
    
    
  @return math.div($px , $designWidth) * 100vw;
}

//px转为vh的函数
@function vh($px) {
    
      
  @return math.div($px , $designHeight) * 100vh;
}

vue.config.js

// 配置一下utils.scss的路径,就可以全局使用了
module.exports={
    
    
	publicPath: '', 
	configureWebpack: {
    
    
	  name: "app name",
	  resolve: {
    
    
	    alias: {
    
    
	      '@': resolve('src')
	    }
	  }
	},
	css:{
    
     //全局配置utils.scss
         loaderOptions:{
    
     
            sass:{
    
    
                     prependData:`@import "@/styles/utils.scss";`
                }
          }
	}
}

在.vue文件中使用

<style lang="scss" scoped="scoped">
    /* 
     直接使用vw和vh函数,将像素值传进去,得到的就是具体的vw vh单位		 
     */
    .box{
    
    
        width: vw(300);
        height: vh(100);
        font-size: vh(16);
        background-color: black;
        margin-left: vw(10);
        margin-top: vh(10);
        border: vh(2) solid red;
    }
</style>

2、 处理公共样式:新建一个rem.scss文件

== 类名需要与项目公共类名作区分,不然会相互影响 ==

//默认设计稿的宽度
$designWidth:1920;
//默认设计稿的高度
$designHeight:1080;
@for $value from 1 through 100 {
    
    
    .pdRem-#{
    
    $value},
    .ptbRem-#{
    
    $value},
    .ptRem-#{
    
    $value} {
    
    
      padding-top: $value/$designHeight * 1vh;
    }
    .pdRem-#{
    
    $value},
    .ptbRem-#{
    
    $value},
    .pbRem-#{
    
    $value} {
    
    
      padding-bottom: $value/$designHeight * 1vh;
    }
    .pdRem-#{
    
    $value},
    .plrRem-#{
    
    $value},
    .plRem-#{
    
    $value} {
    
    
      padding-left: $value/$designWidth * 1vw;
    }
    .pdRem-#{
    
    $value},
    .plrRem-#{
    
    $value},
    .prRem-#{
    
    $value} {
    
    
      padding-right: $value/$designWidth * 1vw;
    }
    .mgRem-#{
    
    $value},
    .mtbRem-#{
    
    $value},
    .mtRem-#{
    
    $value} {
    
    
      margin-top: $value/$designHeight * 1vh;
    }
    .mgRem-#{
    
    $value},
    .mtbRem-#{
    
    $value},
    .mbRem-#{
    
    $value} {
    
    
      margin-bottom: $value/$designHeight * 1vh;
    }
    .mgRem-#{
    
    $value},
    .mlrRem-#{
    
    $value},
    .mlRem-#{
    
    $value} {
    
    
      margin-left: $value/$designWidth * 1vw;
    }
    .mgRem-#{
    
    $value},
    .mlrRem-#{
    
    $value},
    .mrRem-#{
    
    $value} {
    
    
      margin-right: $value/$designWidth * 1vw;
    }
  }

@for $value from 10 through 40 {
    
    
  .sizeRem-#{
    
    $value} {
    
    
    font-size: $value/$designHeight * 1vh;
  }
}

*{
    
    
  box-sizing: border-box;
}
.s-yellow{
    
    
  color: #FFC300;
}
.s-red{
    
    
  color: #CB272C;
}
.s-blue{
    
    
  color: #00CDCF;
}
.s-gray{
    
    
  color: #979797;
}
.line-1 {
    
    
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

// 引入即可使用
<style lang="scss" scoped>
@import "./components/css/rem.scss";
</style>

3、Echarts图表适配自动resize()

封装指令 directive.js

npm install element-resize-detector --save
// directive.js
import * as ECharts from "echarts";
import elementResizeDetectorMaker from "element-resize-detector";
import Vue from "vue";
const HANDLER = "_vue_resize_handler";
function bind(el, binding) {
    
    
  el[HANDLER] = binding.value
    ? binding.value
    : () => {
    
    
        let chart = ECharts.getInstanceByDom(el);
        if (!chart) {
    
    
          return;
        }
        chart.resize();
      };
  // 监听绑定的div大小变化,更新 echarts 大小
  elementResizeDetectorMaker().listenTo(el, el[HANDLER]);
}
function unbind(el) {
    
    
  // window.removeEventListener("resize", el[HANDLER]);
  elementResizeDetectorMaker().removeListener(el, el[HANDLER]);
  delete el[HANDLER];
}
// 自定义指令:v-chart-resize 示例:v-chart-resize="fn"
Vue.directive("resize", {
    
     bind, unbind });

main.js 中引入即可使用

import '@/directive/directive';

<template>
  <div class="linechart">
    <div ref="chart" v-resize class="chart"></div>
  </div>
</template>

4、图表里的文字,例如图例、横坐标等字体大小适配:公共方法

dataUtil.js

export const fitChartSize = (size,defalteWidth = 1920) => {
    
    
  let clientWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
  if (!clientWidth) return size;
  let scale = (clientWidth / defalteWidth);
  return Number((size*scale).toFixed(3));
}

函数挂载到原型上即可使用

import {
    
    fitChartSize} from '@src/utils/dataUtil.js'
Vue.prototype.fitSize = fitChartSize;

textStyle: {
    
    
    color: "green",
    fontSize: this.fitSize(18) //字体自适应
},

5、子组件监听数据变化实时刷新图表:深度监听+setOption

props: ["list"],
  watch: {
    
    
    list: {
    
    
      handler(val) {
    
    
        // 深度监听没有旧值
        this.month = val.map((item) => {
    
    
          return item.month + "月";
        });
        this.orderNum = val.map((item) => {
    
    
          return item.orderCount;
        });
        this.oldNum = val.map((item) => {
    
    
          return item.servedCount;
        });
        this.goodRate = val.map((item) => {
    
    
          return item.favorableRate;
        });
        let option = {
    
    
          xAxis: [
            {
    
    
              data: this.month,
            },
          ],
          series: [
            {
    
    
              data: this.orderNum, // 订单量
            },
            {
    
    
              data: this.oldNum, // 服务人数
            },
            {
    
    
              data: this.goodRate, //折线图数据
            },
          ],
        };
        this.chart.setOption(option);
      },
      // 这里是关键,代表递归监听的变化
      deep: true,
    },
  },

6、websocket

// index.vue 页面
本地运行时,websocket和接口都用的一个端口号,会导致端口号被占用而运行报错,测试环境没事

<script>
export default {
    
    
  data() {
    
    
    return {
    
    
      lockReconnect: false, //禁止重连
      time: 1, // 断开重连的时间间隔
      websock: null,
      // 对象分析 30秒切换tab,最新订单10条,30秒滚动,更新天气
      timerId: null,
      // websocket
      open: false, //是否真正建立连接
      res: {
    
    }, // 所有数据
      ],
    };
  },
  created() {
    
    
    this.initWebSocket();
  },
  mounted() {
    
    
    this.timerId = setInterval(() => {
    
    
      this.timeOperated();
    }, 30000);
  },
  // 销毁定时器
  beforeDestroy() {
    
    
    clearInterval(this.timerId); // 在Vue实例销毁前,清除时间定时器
    this.timerId = null;
  },

  destroyed() {
    
    
    this.websock.close(); //离开路由之后断开websocket连接
  },
  methods: {
    
    
    // 定时30s操作
    timeOperated() {
    
    
      if (this.open) {
    
    
        this.websock.send("ping");
      }
      // 切换tab
      this.$refs.chart5.tab =
        this.$refs.chart5.tab == 3 ? 1 : this.$refs.chart5.tab + 1;
      // 切换最近订单
      this.$refs.chart7.tab = this.$refs.chart7.tab == 1 ? 2 : 1;
      // TODO更新天气
      this.$refs.header.getWeather();
    },
    //初始化weosocket
    initWebSocket() {
    
    
     const url=window.location.host
    
      this.websock = new WebSocket(
        `ws://${
      
      url}${
      
      process.env.VUE_APP_BASE_API}/report/webSocket/screen/2`
      );

      // 连接建立时触发
      this.websock.onopen = () => {
    
    
        console.log("连接成功");
        this.open = true;
        this.time = 1;
      };

      // 客户端接收服务端数据时触发
      this.websock.onmessage = (e) => {
    
    
        if (e.data != "ping") {
    
    
          console.log(JSON.parse(e.data));
          this.res = JSON.parse(e.data);
        } else {
    
    
          console.log("接收数据:", e.data);
        }
      };

      // 通信发生错误时触发
      this.websock.onerror = () => {
    
    
        console.log("出现错误");
        this.open = false;
        this.reconnect();
      };
      // 连接关闭时触发
      this.websock.onclose = () => {
    
    
        console.log("断开连接");
        this.open = false;
        this.reconnect();
      };
    },
    // 断开重连
    reconnect(url) {
    
    
      // 防止错误和断开重复重连
      if (this.lockReconnect) return;
      this.lockReconnect = true;
      setTimeout(() => {
    
    
        //没连接上会一直重连,设置延迟避免请求过多
        this.initWebSocket();
        this.lockReconnect = false;
        this.time++;
      }, this.time * 1000);
    },
  },
};
</script>

7、订单统计列表数据轮播切换:轮播图10s切换+双层数组(每页4个)

<template>
  <div class="w100 sizeRem-14 white">
    <el-carousel indicator-position="none" height="100%" autoplay interval="10000">
      <el-carousel-item v-for="(items, index) in newList" :key="index">
        <div style="height: 20%" class="flex1 w100" v-for="(item, i) in items" :key="i">
          <div class="flex-2 line-1">{
    
    {
    
     item.providerName }}</div>
          <div class="flex-1 text-right">{
    
    {
    
     item.totalOrderCount }}</div>
        </div>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>

<script>
export default {
    
    
  props: ["list"],
  data() {
    
    
    return {
    
    
      newList: []
    };
  },
  watch: {
    
    
    list: {
    
    
      handler(val) {
    
    
        let arr = []
        for (var i = 0; i < val.length; i += 4) {
    
    
          arr.push(val.slice(i, i + 4));
        }
        this.newList = arr
        console.log(111, this.newList)
      },
      // 这里是关键,代表递归监听的变化
      deep: true,
    },
  },
};
</script>


猜你喜欢

转载自blog.csdn.net/weixin_43848576/article/details/131515200