JS前端模拟Excel条件格式实现数据条效果

2023-03-02 16:08:39
目录
需求背景需求分析实现逻辑完整代码实现template 部分style 部分script 部分最终实现效果

需求背景

最近业务中遇到一个有意思的需求,是要在现有的表格中实现类似>

先来看下 Excel 的效果

下面记录下实现过程和原理。

需求分析

通过图片可以看出共有几种情况:

    只有正值:数据条全部向右只有负值:数据条全部向左正负值都有:正负值会以一个轴线做分割分布在左右两侧,根据正负值的多少轴线的位置也会相应的偏左或偏右

    实现逻辑

    实现这个效果的前提,我们要知道每列数据的最大值max和最小值min,最大值的数据条宽度就是100%,下面先用伪代码梳理下逻辑。

    全是正值和全是负值的情况,这种情况就比较简单了,就是计算单元格的值占最大值的比例,计算公式是这样:数据条宽度>

    正负值都有的情况多了一个“轴线位置“的计算和”数据条偏移量“计算

    轴线位置 = (Math.abs(min) / (max - min)) * 100
    
    数据条宽度 = (Math.abs(当前值) / (max - min)) * 100
    
    // 当前值 < 0 时数据条在轴线左边,改变 right 值
    // 数据条的总长为100%
    right = 100 - 轴线位置
    // 当前值 > 0 时数据条在轴线右边,改变 left 值
    left = 轴线位置
    

    轴线位置逻辑其实是 "最小值到0的距离在总长度(max-min)之间的占比",我们知道数字与0之间的距离其实就是绝对值的计算,那么转换为公式来表示就是:

    数据条的宽度就是 “当前值到0的距离在总长度(max-min)之间的占比”,公式表示:

      数据条的偏移量,这里需要知道是向左还是向右偏移(最终是通过改变元素CSS的 left、right 属性来实现偏移)

      完整代码实现

      代码使用>

      template>
      <template>
        <el-table :data="tableData" border style="width: 100%">
          <el-table-column
            v-for="item in columns"
            sortable
            :key="item.prop"
            :prop="item.prop"
            :label="item.label"
          >
            <template slot-scope="scope">
              <!-- 数据条 -->
              <div class="data-bar" :style="renderDataBar(item, scope.row)"></div>
              <!-- 轴线 -->
              <div class="data-bar-axis" :style="renderAxis(item, scope.row)"></div>
              <!-- 当前值 -->
              <span class="cell-value">{{ scope.row[item.prop] }}</span>
            </template>
          </el-table-column>
        </el-table>
      </template>
      

      style>

      先放 style 部分是为了让大家对基础样式有个感受,渲染函数中主要就是动态修改元素的 width、left、right 的值

      <style>
          .el-table .cell-value {
            position: relative;
          }
          .data-bar {
            position: absolute;
            top: 0;
            bottom: 0;
            margin: 5px auto;
            transition: width 0.2s;
          }
          .data-bar-axis {
            position: absolute;
            top: -1px;
            bottom: 0;
            border-right: 1px dashed #303133;
          }
      </style>
      

      script>
      <script>
      export default {
        data() {
          return {
            columns: [
              {
                prop: 'positive',
                label: '正值',
                min: 1,
                max: 10
              },
              {
                prop: 'negative',
                label: '负值',
                min: -1,
                max: -12
              },
              {
                prop: 'mixed',
                label: '正负值',
                min: -6,
                max: 5
              }
            ],
            tableData: []
          }
        },
        created() {
          this.initData()
        },
        methods: {
          initData() {
            // mock数据过程,忽略 ...
            this.tableData.push({...})
          },
          renderDataBar(column, row) {
            const { min, max, prop } = column
            // 当前单元格值
            const cellVal = row[prop]
            if (cellVal === 0) return { display: 'none' };
            let style = {
              width: '0',
              background: '#409eff'
            }
            // 全是正值 或 全是负值
            if (min >= 0 || max <= 0) {
              const width = ((cellVal / max) * 100).toFixed(2);
              style.width = `${width}%`
              style = min >= 0 ? { ...style, left: '0' } : { ...style, right: '0' }
            }
            // 正负值都有
            if (min < 0 && max > 0) {
              const range = max - min;
              // 轴线位置
              const leftOffset = Math.abs((min / range) * 100)
              // 数据条宽度
              const width = ((Math.abs(cellVal / range) * 100)).toFixed(2)
              style = cellVal > 0 ? {
                width: `${width}%`,
                left: `${leftOffset.toFixed(2)}%`
              } : {
                width: `${width}%`,
                background: '#F56C6C', // 负值进行颜色区分
                right: `${(100 - leftOffset).toFixed(2)}%`
              }
            }
            return style;
          },
          renderAxis(column) {
            const { min, max } = column
            if (min < 0 && max > 0) {
              // 正负值都有的情况下,显示轴线
              const range = max - min;
              const leftOffset = (((0 - min) / range) * 100).toFixed(2)
              return {
                left: `${leftOffset}%`
              }
            } else {
              return {
                display: 'none'
              }
            }
          }
        }
      }
      </script>
      

      最终实现效果

      以上就是JS前端模拟Excel条件格式实现数据条效果的详细内容,更多关于JS模拟Excel数据条的资料请关注易采站长站其它相关文章!