当前位置: 首页 > article >正文

vue移动端统计分析echarts开发小结(多提示框渲染,下载适配,数据量大的时候不分页崩溃等)

前言

前段时间负责开发公司移动端的统计分析模块。其中晒选项包含了季度年度,月度具体还有二级筛选。因此晒选项我封装了一个新的组件。后续我会分享出来。接下来就是柱状图折线图以及表格之间的切换。我把各个图表都独立封装成了一个组件。这样然后一起放在父组件中。图表渲染确实不难很简单。但是在开发途中还是遇到了几个恶心的问题。在此我想分享一下

晒选项

根据ui稿我封装了一个组件

<template>
  <div class="timeSelect">
    <van-dropdown-menu :active-color="'#ff6f2f'">
      <van-dropdown-item
        v-model="selectType"
        @change="typeChange"
        :options="type"
      />
    </van-dropdown-menu>
    <div class="time" @click="showDataCheck">
      <div class="time-title">{{ showTitle }}</div>
    </div>
    <van-popup v-model="dataCheck" round position="bottom" :duration="0">
      <van-datetime-picker
        v-model="currentDate"
        :formatter="formatter"
        type="year-month"
        title=""
        @confirm="confirmDate"
        @cancel="onCancel"
        :min-date="minDate"
        :max-date="maxDate"
    /></van-popup>
    <van-popup v-model="yearCheck" round position="bottom" :duration="0">
      <van-picker
        ref="picker"
        show-toolbar
        :columns="selectType == 2 ? quarterColumns : yearColumns"
        v-model="currentValue"
        @change="yearChange"
        @confirm="onConfirm"
        @cancel="onCancel"
    /></van-popup>
  </div>
</template>

<script>
import dayjs from "dayjs";
export default {
  data() {
    return {
      selectType: 1,
      selectTime: "",
      showTitle: "",
      yearCheck: false,
      type: [
        { text: "月度", value: 1 },
        { text: "季度", value: 2 },
        { text: "年度", value: 3 },
      ],
      yearColumns: [],
      quarterColumns: [
        // 第一列
        {
          values: [],
          defaultIndex: 2,
        },
        // 第二列
        {
          values: ["1季度", "2季度", "3季度", "4季度"],
          defaultIndex: 1,
        },
      ],
      dataCheck: false,
      minDate: new Date(2022, 0, 1),
      maxDate: new Date(),
      currentDate: new Date(),
      currentValue: [],
    };
  },

  mounted() {
    this.onInit();
  },
  methods: {
    updateTime(type, date) {
      console.log(type, date);
    },
    onInit() {
      if (this.$route.query.dateType) {
        this.selectType = Number(this.$route.query.dateType);
        this.selectTime = this.$route.query.date;
        this.showTitle = this.$route.query.date;
      } else {
        this.showTitle = `${dayjs().year()}年${dayjs().month() + 1}月`;
      }
      console.log(this.selectType);

      const years = [];
      for (let year = dayjs().year(); year >= 2022; year--) {
        years.unshift(`${year}年`);
      }
      this.yearColumns = years;
      this.quarterColumns[0].values = years;
    },
    // 选中日期
    confirmDate(value) {
      this.selectTime = `${dayjs(value).year()}年${dayjs(value).month() + 1}月`;
      this.showTitle = `${dayjs(value).year()}年${dayjs(value).month() + 1}月`;
      this.currentDate = new Date(value);
      this.dataCheck = false;
      this.$emit("updateTime", this.selectType, this.selectTime);
      console.log("改变", this.selectType, this.selectTime);
    },
    // 日期格式化
    formatter(type, val) {
      if (type === "year") {
        return `${val}年`;
      } else if (type === "month") {
        return `${Number(val)}月`;
      }
      return val;
    },
    onConfirm(val) {
      if (this.selectType == 3) {
        this.showTitle = val;
        this.selectTime = val;
      } else {
        this.showTitle = `${val[0]}${val[1]}`;
        this.selectTime = `${val[0]}${val[1]}`;
      }
      this.currentValue = val;
      this.$emit("updateTime", this.selectType, this.selectTime);
      console.log("改变", this.selectType, this.selectTime);

      this.yearCheck = false;
    },
    // 年或季度改变
    yearChange(val, time) {
      console.log(111, time);

      if (time[0] == `${dayjs().year()}年`) {
        let vals = JSON.parse(JSON.stringify(this.quarterColumns[1].values));
        vals.length = Math.floor(dayjs().month() / 3) + 1;
        this.$refs["picker"].setColumnValues(1, vals);
      } else {
        this.$refs["picker"].setColumnValues(1, [
          "1季度",
          "2季度",
          "3季度",
          "4季度",
        ]);
      }
    },
    // 改变选择框类型
    typeChange(value) {
      // 选中年份
      if (value == 3) {
        this.showTitle = `${dayjs().year()}年`;
        this.currentValue = [`${dayjs().year()}年`];
        this.selectTime = `${dayjs().year()}年`;
      } else if (value == 2) {
        this.showTitle = `${dayjs().year()}年${
          Math.floor(dayjs().month() / 3) + 1
        }季度`;

        this.selectTime = `${dayjs().year()}年${
          Math.floor(dayjs().month() / 3) + 1
        }季度`;
      } else {
        this.showTitle = `${dayjs().year()}年${dayjs().month() + 1}月`;
        this.selectTime = `${dayjs().year()}年${dayjs().month() + 1}月`;
      }
      this.$emit("updateTime", this.selectType, this.selectTime);
      console.log("改变", this.selectType, this.selectTime);
    },
    // 隐藏弹窗
    onCancel() {
      this.yearCheck = false;
      this.dataCheck = false;
    },
    // 展示弹窗
    showDataCheck() {
      if (this.selectType == 1) {
        this.dataCheck = true;
      } else {
        this.yearCheck = true;
        // 第一次弹窗选中对应
        this.$nextTick(() => {
          if (this.selectType == 3) {
            console.log(this.yearColumns);

            this.$refs["picker"].setColumnIndex(
              0,
              this.yearColumns.indexOf(this.showTitle)
            );
          } else {
            if (this.showTitle.split("年")[0] + "年" == `${dayjs().year()}年`) {
              let vals = JSON.parse(
                JSON.stringify(this.quarterColumns[1].values)
              );
              vals.length = Math.floor(dayjs().month() / 3) + 1;

              this.$refs["picker"].setColumnValues(1, vals);
            }

            this.$refs["picker"].setColumnIndex(
              0,
              this.yearColumns.indexOf(this.showTitle.split("年")[0] + "年")
            );
            this.$refs["picker"].setColumnIndex(
              1,
              this.quarterColumns[1].values.indexOf(
                this.showTitle.split("年")[1]
              )
            );
          }
        });
      }
    },
  },
};
</script>

<style scoped lang="less">
.timeSelect {
  font-size: 0.15rem;
  box-sizing: border-box;
  padding: 0 2%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  height: 9vh;
  background-color: #f5f8fb;
  font-size: 15px;
}

.time {
  width: 48%;
  background-color: #fff;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  margin-right: 4px;
}

.time-title {
  position: relative;
}

.time-title::after {
  position: absolute;
  top: 50%;
  right: -12px;
  margin-top: -5px;
  border: 3px solid;
  border-color: transparent transparent #1f1f1f #1f1f1f;
  -webkit-transform: rotate(-45deg);
  transform: rotate(-45deg);
  opacity: 0.8;
  content: "";
}

/deep/ .van-dropdown-menu {
  width: 48%;
}
/deep/ .van-dropdown-menu__bar {
  border-radius: 4px;
  margin-left: 5px;
}
/deep/ .van-ellipsis {
  color: #1f1f1f;
}
/deep/ .van-dropdown-item {
  margin-top: 10px;
  font-size: 17px;
}

/deep/ .van-dropdown-menu__bar {
}

/deep/ .van-ellipsis {
  font-size: 17px;
}
/deep/ .van-cell__title {
  font-size: 16px;
}

/deep/ .time-title {
  font-size: 17px;
}

/deep/ .van-dropdown-menu__title::after {
  border-color: transparent transparent #1f1f1f #1f1f1f;
}

/deep/ .van-picker__confirm {
  color: #ff6f2f;
}
/deep/ .van-icon-success:before {
  font-family: "iconfont";
  content: "\E625" !important;
}

</style>

直接暴露两个参数type和date就行了,因为我自己封装了转时间戳的函数,上文中也有提到。具体可以去查看。

柱状图

柱状图中后台给我数据之后我处理成我想要的数据格式。因为业务问题我特殊处理X轴和y轴上的数据。另外提示框我自己写了所需要的函数

formatter: function (params) {
            let tooltip = "";
            let xAxisValue = params[0].axisValue;

            // 根据 xAxisValue 的格式进行处理
            if (/^\d{4}$/.test(xAxisValue)) {
              xAxisValue = `${xAxisValue}年`;
            } else if (/^\d{4}-\d{1,2}月$/.test(xAxisValue)) {
              const [year, month] = xAxisValue.split("-");
              xAxisValue = `${year}年${parseInt(month)}月`;
            } else if (/^\d{4}-[1-4]季度$/.test(xAxisValue)) {
              const [year, quarter] = xAxisValue.split("-");
              xAxisValue = `${year}年${parseInt(quarter)}季度`;
            }

            tooltip += `${xAxisValue}<br>`;

            // 添加蓝色小圆圈代表会议量
            tooltip += `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:#396EF3;"></span>${params[0].seriesName} ${params[0].value}<br>`;

            // 添加黄色小圆圈代表会议量排名
            tooltip += `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:#E6BB58;"></span>${params[1].seriesName} ${params[1].value}<br>`;

            return tooltip;
          },

在柱状图中发现了一个问题就是tooltip提示框他会出现一个偶发性问题。如果在一个页面放两个组件图表。当你点击一个组件图表的提示框的时候在点击另一个组件图表的提示框的时候两个提示框会同时出现(在你配置写对的情况下)安卓手机和苹果手机复现的频率还不一样。

第二个问题是:在我们项目开发中有一个需求是下载图表页面。根据ui设计下载好的图片是特定的样式有预览效果。刚开始我的方案是直接使用第三方包html2canvas来截图(上文都有讲过)这种情况下功能可以实现但是时间有点长(数据量100多条的时候安卓手机7秒左右,苹果手机3秒左右这很影响体验使用)下载的图表还需要加特定的样式和字段所以还得用canvs去拼接,所以整体下来时间生成时间就长了。所以很快就放弃这种方法。之后转变思路直接在cavas中加载图表生成图表这样速度很快,但是在调试途中要注意一个问题就是数据量大的情况下并且设计的是不让分页的时候这样加载图表就会出现一个图表崩溃问题,或者像素垮了的问题,或者就是在苹果手机上直接一个问号(崩溃)这都是因为echart图表加载崩溃的原因。

解决方法初始化的时候设置为svg格式(有疑问可以私信我)

this.barChart = echarts.init(this.$refs.bar, null, {
          renderer: "svg",
          devicePixelRatio: window.devicePixelRatio,
          // devicePixelRatio: 1,
        });

不要以为这样就结束了,记得还有适配问题,安卓手机上和苹果手机上出现的问题还是不一样。为综合考虑后在下载的时候根据数据量做了判断。当数据量小于200的时候直接使用图表加载下载图片(很快)数据量大于200的时候使用html2canvas截图来下载记得加一个loading。在这中间我封装了预览图片的函数。截图的函数。如果有需要可以私信我。


http://www.kler.cn/a/510544.html

相关文章:

  • [LeetCode] 哈希表 I — 242#有效的字母异位词 | 349#两个数组的交集 | 202#快乐数 | 1#两数之和
  • ReactiveSwift 简单使用
  • 项目实战--网页五子棋(游戏大厅)(3)
  • Ubuntu 24.04 LTS linux 文件权限
  • Redisson发布订阅学习
  • R数据分析:有调节的中介与有中介的调节的整体介绍
  • 用LSTM模型预测股价的例子(1)
  • Chromium 132 编译指南 Linux 篇 - 安装 Chromium 官方工具(三)
  • 河北省乡镇界面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移内容测评
  • 山西省乡镇界面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移测评
  • HRNet,Deep High-Resolution Representation Learning for Visual Recognition解读
  • 缓存、数据库双写一致性解决方案
  • 计算机毕业设计PySpark+Hadoop+Hive机票预测 飞机票航班数据分析可视化大屏 航班预测系统 机票爬虫 飞机票推荐系统 大数据毕业设计
  • Object常用的方法及开发中的使用场景
  • T-SQL语言的数据库交互
  • MYSQL数据库基础-01.数据库的基本操作
  • Windows图形界面(GUI)-QT-C/C++ - Qt控件与布局系统详解
  • 汇旺财支付PHP代码
  • 服务化架构 IM 系统之应用 MQ
  • 数据库服务体系结构
  • 基于机器学习的用户健康风险分类及预测分析
  • 数据结构 (C语言) 链表
  • C#里await Task.Run死锁的分析与解决
  • 【错误解决方案记录】spine3.8.75导出的数据使用unity-spine3.8插件解析失败报错的解决方案
  • 知识库管理系统的用户体验之道:便捷、高效、智能
  • PyTorch 基础数据集:从理论到实践的深度学习基石