el-table与echarts图形实现滚动联动

发布于:2025-05-12 ⋅ 阅读:(102) ⋅ 点赞:(0)

el-table与echarts图形滚动联动

效果图

在这里插入图片描述

实现思路

设计图滚动条位于表格下方,且echarts滚动不易获取当前展示数据到左侧的距离
故:通过监听表格的滚动实现联动

  1. 为了保持echarts的横坐标和表格的列基本保持对齐,用tdWidth标识单列表格的宽度,showTableTd标识展示了多少列,在mounted计算当前容器宽度能展示多少列,保证是整数列;
  2. echarts设置dataZoom,并且禁用鼠标滚动滑动的功能;
  3. 给表格添加scroll监听,注意添加节流,避免出现滚动时反复计算,无法滚动和浪费资源;
  4. 计算图表dataZoomstartend控制图表展示的区域。

实现代码:
scrollTableLine.vue

<template>
  <div>
    <div class="pdl-42 ">
      <lineChart ref="charts" class="mgt-12" :chartData="chartData" height="90px" :optionOpt="optionOpt" />
    </div>
    <data-table ref="tableRef" :pagination="false" size="mini" class="mgt-12 data-table pdb-0" :head="tableHead"
      :data="tableData">
    </data-table>
  </div>
</template>
<script>
import { throttle } from '@/utils/index.js'

export default {
  name: "scrollTableLine",
  components: {
    lineChart: () => import("@/components/Chart/lineChart.vue"),
  },
  props: {
    data: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      chartData: [
       //...设置数据
      ],
      tableHead: [
        {
          prop: "type",
          label: "",
          width: 70,
          fixed: "left",
        },
      ],
      tableData: [],
      showTableTd: 0,
      tdWidth: 0,
    };
  },
  beforeDestroy() {
    this.removeScrollListener();
  },
  mounted() {
    let width = this.$refs.tableRef.$el.clientWidth;

    //初始按照75的宽度计算
    this.showTableTd = Math.floor((width - 70) / 65);

    //计算每一列的宽度
    this.tdWidth = Math.floor((width - 70) / this.showTableTd);
    let data = {}
    //制造el-table假数据
    for (let i = 0; i < 30; i++) {
      data[`day${i}`] = 10
      this.tableHead.push({
        prop: `day${i}`,
        label: `01-${i + 1 < 10 ? "0" + (i + 1) : i + 1}`,
        width: this.tdWidth,
        align: "center",
      });
    }
    //表格数据
    this.tableData = [
      {
        type: "XX数",
        ...data
      },
      {
        type: "XX数",
        ...data
      }
    ]
    this.addScrollListener();
  },
  methods: {
  //给echarts设置配置项及dataZoom
    optionOpt(option) {
      option.color = ["#1484FA"];
      option.grid.top = 8;
      option.legend.show = false;
      option.yAxis[0].axisLabel.formatter = (value) => {
        return value + "%";
      };
     
      option.tooltip.formatter = (params) => {
        let text = `<div  class="lh-18 color-333 fs-12"><p>${params[0].axisValue}</p> `;
        params.forEach((item) => {
          text += ` <p ><span class="fw-4 color-999">${item.seriesName}:</span> <span  style="color:#333;"></span>${item.value}%</p> `;
        });
        text += "</div>";
        return text;
      };

      option.xAxis[0].axisLabel.interval = 0;
      option.dataZoom = [
        {
          type: "slider",
          show: false,
          zoomLock: false,
          right: "2%",
          start: 0,
          bottom: 2,
          end: 40,
          showDetail: false,
          zoomOnMouseWheel: false,
          backgroundColor: "transparent", //两边未选中的滑动条区域的颜色
          borderColor: "transparent",
          filterMode: "empty",
        }
      ];
    },
//添加滚动监听
    addScrollListener() {
      const tableBodyWrapper = this.$refs.tableRef.$el.querySelector(
        ".el-table__body-wrapper"
      );
      //展示个数
      if (tableBodyWrapper) {
        let handleScroll = throttle(
          this.handleScroll,
          200
        );
        tableBodyWrapper.addEventListener("scroll", handleScroll);
      }

      this.$nextTick(() => {
        setTimeout(() => {
          this.handleScroll();
        }, 600);
      });
    },

//移出监听
    removeScrollListener() {
      const tableBodyWrapper = this.$refs.tableRef.$el.querySelector(
        ".el-table__body-wrapper"
      );
      if (tableBodyWrapper) {
        tableBodyWrapper.removeEventListener("scroll", this.handleScroll);
      }
    },
    //表格滚动的时候计算滚动
    handleScroll(event) {
      const scrollLeft = event?.target?.scrollLeft || 0;

      let leftNum = Math.ceil(scrollLeft / this.tdWidth);
      if (event) event.target.scrollLeft = leftNum * this.tdWidth;

      this.$refs.charts.$refs.chart.setOption({
        dataZoom: [
          {
            start: (leftNum / this.chartData.length) * 100,
            end:
              ((leftNum + this.showTableTd) / this.chartData.length) * 100 -
              (scrollLeft == 0 ? 3.3 : 0),
          },
        ],
      });
    },
  },
};
</script>
<style lang="scss" scoped>
::v-deep .data-table {
  background: #f5f9ff;

  .el-table {
    background: #f5f9ff;
  }

  .el-table__cell {
    padding: 2px 0;
  }

  tr {
    background: #fff;
  }

  .el-table__body-wrapper::-webkit-scrollbar {
    width: 4px;
    height: 4px;
    background-color: #fff;
  }
  .el-table__body-wrapper::-webkit-scrollbar-track {
    box-shadow: 0px 1px 3px #fff inset;
    border-radius: 10px;
    background-color: #fff;
  }


  .el-table__body-wrapper::-webkit-scrollbar-thumb {
    box-shadow: 0px 1px 3px #c3cbd6 inset;
    border-radius: 10px;
    background-color: #c3cbd6;
  }
}

.pdl-42 {
  padding-left: 40px;
}
</style>

lineChart.vue

<template>
    <vue-chart  v-bind="$attrs" v-on="$listeners" ref="chart"
        :style="{ height: height, width: width }" :option="option" />
</template>
<script>
import minix from './minix'
export default {
    name: 'barChart',
    mixins: [minix],
    props: {
        width: {
            type: String,
            default: '100%'
        },
        height: {
            type: String,
            default: '143px'
        },
        chartData: {
            type: Array,
            default: () => [
                { name: '5月', value: 80, value2: 70, value3: 20 },
                { name: '6月', value: 50, value2: 40, value3: 85 },
                { name: '7月', value: 30, value2: 10, value3: 70 },
                { name: '8月', value: 60, value2: 70, value3: 30 },
                { name: '9月', value: 50, value2: 40, value3: 80 },
                { name: '10月', value: 20, value2: 10, value3: 60 },
            ],

        },
        optionOpt: {
            type: Function,
            default: () => () => { }
        },
      
    },
    watch: {
        chartData: {
            handler(val) {
                if (val) {
                    let Xdata = []
                    let Ydata = []
                    val.forEach(item => {
                        Xdata.push(item.name)
                        Ydata.push(item?.value ?? 0)
                    })
                    this.option.xAxis[0].data = Xdata
                    this.option.series[0].data = Ydata

                    this.optionOpt(this.option, this.chartData)

                }
            },
            immediate: true,
            deep: true
        }
    },
    data() {
        return {
            option: {
                color: ['rgba(67, 207, 124, 1)', 'rgba(253, 196, 67, 1)', 'rgba(227, 69, 69, 1)'],
                grid: {
                    left: "0%",
                    right: "1",
                    bottom: "0%",
                    top: "32",
                    containLabel: true,
                },
                tooltip: {
                    trigger: 'axis',
                    confine: true,
                    backgroundColor: 'rgba(255, 255, 255, 0.9)',
                    borderColor: 'rgba(235, 240, 250, 1)',
                    borderWidth: 1,
                    extraCssText: 'box-shadow: 0px 2px 8px  rgba(205, 207, 209, 1);line-height:18px;padding:9px 16px 15px;backdrop-filter: blur(5px);',
                    axisPointer: {
                        type: "shadow",
                        shadowStyle: {
                            color: 'rgba(20, 132, 250, 0)',
                            width: 40,
                        }
                    },
                    textStyle: {
                        color: '#333333',
                        fontSize: 12
                    },
                    formatter: (params) => {
                        let text = `<div  class="lh-18 color-333 fs-12">
                             <p>${params[0].axisValue}</p> `
                        params.forEach(item => {
                            text += ` <p ><span class="fw-4 color-999">${item.seriesName}:</span> <span  style="color:#333;"></span>${item.value}</p> `
                        })
                        text += '</div>'
                        return text
                    },
                },
                legend: {
                    itemGap: 12,
                    itemWidth: 11, // 设置宽度
                    itemHeight: 7,
                    left: 0,
                    top: '0',
                    right: '30',
                    textStyle: {
                        color: '#666666',
                        fontSize: 12
                    },
                },
                xAxis: [{
                    data: [],
                    axisPointer: {
                        "type": "shadow"
                    },
                    axisLine: {
                        "lineStyle": {
                            "color": "rgba(227, 227, 227, 1)"
                        }
                    },
                    splitLine: {
                        show: true,
                        lineStyle: {
                            color: 'rgba(227, 227, 227, 1)',
                            width: 1,
                            type: 'dashed'
                        }
                    },
                    axisTick: {
                        show: false
                    },
                    axisLabel: {
                        show: true,
                        textStyle: {
                            color: "#999999",
                            fontSize: 12
                        }
                    },

                }],
                yAxis: [{
                    type: "value",
                    splitNumber: 2,
                    splitLine: {
                        show: true,
                        lineStyle: {
                            color: 'rgba(227, 227, 227, 1)',
                            width: 1,
                            type: 'dashed'
                        }
                    },
                    name: "",
                    nameTextStyle: {
                        color: "#999",
                        align: 'left'
                    },
                    axisTick: {
                        show: false
                    },
                    axisLine: {
                        show: true,
                        lineStyle: {
                            color: 'rgba(227, 227, 227, 1)'
                        }
                    },
                    axisLabel: {
                        show: true,
                        textStyle: {
                            color: "rgba(153, 153, 153, 1)",
                            fontSize: 12
                        }
                    },

                },
                //右边的虚线
                {
                    type: "value",
                    splitNumber: 5,
                    max: 100,
                    min: 0,
                    yIndex: 1,
                    nameGap: 2,
                    position: 'right',
                    splitLine: {
                        show: false,
                    },
                    name: "%",
                    nameTextStyle: {
                        color: "#999",
                        align: 'left',
                        padding: [0, 0, 10, 10]
                    },
                    axisTick: {
                        show: false
                    },
                    axisLine: {
                        show: true,
                        lineStyle: {
                            color: 'rgba(227, 227, 227, 0)',
                            type: 'dashed',
                            zIndex: 10
                        }
                    },
                    axisLabel: {
                        show: false,
                        textStyle: {
                            color: "rgba(153, 153, 153, 1)",
                            fontSize: 0,
                        }
                    },
                }],
                series: [
                    {
                        name: "日增变化趋势",
                        type: "line",
                        smooth: false,
                        showAllSymbol: true,
                        symbol: "emptyCircle",
                        symbolSize: 6,
                        data: [],
                    },
                    

                ]
            }
        }
    },
    methods: {

    }
}
</script>
<style scoped></style>

网站公告

今日签到

点亮在社区的每一天
去签到