Vue el-table 表尾合计行

问题:

前端在处理后端接口返回的列表数据进行表尾合计行统计,由于返回的金额不固定,有小数,整数,大数值等等,会出现累计计算数值精度丢失的问题,导致页面数据看起来是错的。

原因:

在JavaScript中,Number.MAX_SAFE_INTEGER 是一个常量,其值为 9007199254740991(2^53 - 1),代表JavaScript中可以安全表示的最大整数。这意味着任何大于 Number.MAX_SAFE_INTEGER 的整数在进行数学运算时可能会失去精度,因为JavaScript使用IEEE 754标准的64位浮点数表示数字,其中包括一个符号位、11位指数位和52位尾数位。

重写el-table 表尾合计行方法:
复制代码
<template>
  <div class="table-container">
    <el-table
      :data="tableData"
      show-summary
      :summary-method="getSummaries"
      style="width: 100%">
      <el-table-column prop="id" label="ID" width="120"></el-table-column>
      <el-table-column prop="month" label="月份" width="120"></el-table-column>
      <el-table-column prop="name" label="姓名"></el-table-column>
      <el-table-column prop="amount1" label="整数金额" width="120"></el-table-column>
      <el-table-column prop="amount2" label="小数金额" width="120"></el-table-column>
      <el-table-column prop="amount3" label="混合金额" width="200"></el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  name: 'TableComponent',
  data() {
    return {
      tableData: [
        {
          id: '12987122',
          month: '2026-02',
          name: '王小虎',
          amount1: '234',
          amount2: '3.2',
          amount3: '0.11'
        }, {
          id: '12987123',
          month: '2026-01',
          name: '王小虎',
          amount1: '165',
          amount2: '4.43',
          amount3: '0.88'
        }, {
          id: '12987124',
          month: '2025-12',
          name: '王小虎',
          amount1: '324',
          amount2: '1.9',
          amount3: '2'
        }, {
          id: '12987125',
          month: '2025-11',
          name: '王小虎',
          amount1: '621',
          amount2: '2.2',
          amount3: '123456789123456789'
        }, {
          id: '12987126',
          month: '2025-10',
          name: '王小虎',
          amount1: '539',
          amount2: '4.1',
          amount3: '887654321987654321'
        }
      ]
    };
  },
  methods: {
    getSummaries(param) {
      const { columns, data } = param;
      const sums = [];
      columns.forEach((column, index) => {
        if (index === 0) {
          sums[index] = '合计';
          return;
        }
        const values = data.map(item => Number(item[column.property]));
        if (column.property === 'amount3') {
          // 对amount3字段进行高精度计算
          const total = this.calculateHighPrecisionSum(data.map(item => item.amount3));
          sums[index] = total;
        } else if (!values.every(value => isNaN(value))) {
          const precisions = [];
          let notNumber = true;
          values.forEach(value => {
            if (!isNaN(value)) {
              notNumber = false;
              const decimal = value.toString().split('.')[1];
              precisions.push(decimal ? decimal.length : 0);
            }
          });
          if (!notNumber) {
            const precision = Math.max(...precisions);
            sums[index] = values.reduce((prev, curr) => {
              const value = Number(curr);
              if (!isNaN(value)) {
                return parseFloat((prev + value).toFixed(Math.min(precision, 20)));
              } else {
                return prev;
              }
            }, 0);
          } else {
            sums[index] = 'N/A';
          }
        } else {
          sums[index] = 'N/A';
        }
      });

      return sums;
    },
    // 高精度计算方法,处理大数和小数的精确相加
    calculateHighPrecisionSum(amounts) {
      if (!amounts || amounts.length === 0) return '0';
      
      // 使用数组来存储每一位数字,从低位到高位
      let result = [];
      let maxDecimalPlaces = 0;
      
      // 先确定最大的小数位数
      amounts.forEach(amount => {
        const parts = amount.split('.');
        if (parts.length > 1) {
          maxDecimalPlaces = Math.max(maxDecimalPlaces, parts[1].length);
        }
      });
      
      // 将所有数字转换为整数进行计算(乘以10^maxDecimalPlaces)
      const integers = amounts.map(amount => {
        const parts = amount.split('.');
        let integerPart = parts[0];
        let decimalPart = parts[1] || '';
        
        // 补齐小数位
        while (decimalPart.length < maxDecimalPlaces) {
          decimalPart += '0';
        }
        
        return integerPart + decimalPart;
      });
      
      // 从右到左逐位相加
      let carry = 0;
      let maxLength = Math.max(...integers.map(num => num.length));
      
      // 补齐位数
      for (let i = 0; i < integers.length; i++) {
        while (integers[i].length < maxLength) {
          integers[i] = '0' + integers[i];
        }
      }
      
      // 逐位相加
      for (let i = maxLength - 1; i >= 0; i--) {
        let sum = carry;
        for (let j = 0; j < integers.length; j++) {
          sum += parseInt(integers[j][i] || '0');
        }
        result.unshift(sum % 10);
        carry = Math.floor(sum / 10);
      }
      
      // 处理最后的进位
      while (carry > 0) {
        result.unshift(carry % 10);
        carry = Math.floor(carry / 10);
      }
      
      // 转换回小数形式
      let resultStr = result.join('');
      
      // 如果需要小数点,插入小数点
      if (maxDecimalPlaces > 0) {
        if (resultStr.length <= maxDecimalPlaces) {
          // 如果结果长度小于小数位数,前面补0
          while (resultStr.length < maxDecimalPlaces + 1) {
            resultStr = '0' + resultStr;
          }
        }
        const decimalIndex = resultStr.length - maxDecimalPlaces;
        resultStr = resultStr.substring(0, decimalIndex) + '.' + resultStr.substring(decimalIndex);
        
        // 去除末尾多余的0
        resultStr = resultStr.replace(/\.?0+$/, '');
        // 如果小数点在最后,去掉小数点
        resultStr = resultStr.replace(/\.$/, '');
      }
      
      // 去除开头的0(但保留至少一个数字)
      // 页面eslint校验报错可删除
      resultStr = resultStr.replace(/+/, '') || '0';
      
      return resultStr;
    }
  }
};
</script>

<style scoped>
.table-container {
  padding: 20px;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
</style>
代码逻辑说明:
  1. 实现了Vue.js环境下的表格组件,包含表尾合计行功能
  2. 针对混合金额字段amount3,实现了高精度计算方法,支持小数、整数和大数的精确相加
  3. 使用字符串操作和逐位计算的方式,确保大数计算时不会丢失精度
  4. 合计计算从数值的最后面往前一步步计算,保证计算准确性
  5. 返回总金额的字符串格式,保持原始数据的精度特征
  6. 包含完整的样式设计,提供现代化的UI界面

记录一下,有好的方法可以评论区说说~

相关推荐
时光追逐者2 小时前
一个基于 .NET + Vue 实现的通用权限管理平台(RBAC模式),前后端分离模式,开箱即用!
前端·vue.js·c#·.net·.net core
编程猪猪侠2 小时前
Vue3 + Ant Design Vue 实现 Table 表格嵌套 Radio 单选框
javascript·vue.js·anti-design-vue
phltxy2 小时前
Vue3 + Vite:从入门到实战——核心指令全解析
vue.js·vue
静小谢2 小时前
vue3实现语言切换vue-i18n
前端·javascript·vue.js
Highcharts.js2 小时前
如何使用Highcharts Flutter的官方使用文档
javascript·flutter·开发文档·highcharts
东东5162 小时前
资产管理信息系统ssm+vue
前端·javascript·vue.js
森爱。2 小时前
web开发全家桶(django+前端+数据库)
前端·python·django
骆驼爱记录2 小时前
Word侧边页码设置全攻略
前端·自动化·word·excel·wps·新人首发
利刃大大2 小时前
【Vue】声明式导航与传参 && 编程式导航与传参 && 嵌套与守卫
javascript·vue.js·ecmascript