1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { trimZeros, roundHalfUp } from "./formatNumber.js";
 
/**
 * 通用的表格合计方法
 * @param {Object} param - 包含表格列配置和数据源的对象
 * @param {Array<string>} summaryProps - 需要汇总的字段名数组
 * @param {Object} specialFormat - 特殊格式化规则:字段名 -> 格式化选项(如是否去掉小数)
 *   支持 { noDecimal: true }  取整显示
 *        { trimZeros: true, decimalPlaces: 6 }  最多保留 6 位小数并去掉末尾多余的 0
 *        { roundEach: 2 }  每行先按 2 位小数四舍五入再求和,
 *                          用于金额列:保证合计与列内展示值相加完全一致
 *        { formatter: fn }  自定义格式化函数,优先级最高
 *        { decimalPlaces: 2 }  固定保留 2 位小数(默认)
 * @returns {Array} 合计行数据
 */
const summarizeTable = (param, summaryProps, specialFormat = {}) => {
  const { columns, data } = param;
  const sums = [];
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = "合计";
      return;
    }
    const prop = column.property;
    if (summaryProps.includes(prop)) {
      const rule = specialFormat[prop];
      const values = data.map((item) => {
        const val = Number(item[prop]);
        // NaN 保持 NaN,便于下面判断「整列都不是数字」
        if (Number.isNaN(val)) return NaN;
        return rule && rule.roundEach != null
          ? roundHalfUp(val, rule.roundEach)
          : val;
      });
      // 只对有效数字进行求和
      if (!values.every(isNaN)) {
        const sum = values.reduce(
          (acc, val) => (!isNaN(val) ? acc + val : acc),
          0
        );
        if (specialFormat[prop] && specialFormat[prop].noDecimal) {
          // 如果指定了不需要保留小数,则直接转换为整数
          sums[index] = Math.round(sum).toString();
        } else if (
          specialFormat[prop] &&
          typeof specialFormat[prop].formatter === "function"
        ) {
          // 自定义格式化函数优先
          sums[index] = specialFormat[prop].formatter(sum);
        } else if (specialFormat[prop] && specialFormat[prop].trimZeros) {
          // 指定了去掉末尾多余的 0(最多保留 decimalPlaces 位,默认 6 位)
          sums[index] = trimZeros(sum, specialFormat[prop].decimalPlaces ?? 6);
        } else {
          // 默认保留两位小数
          sums[index] = parseFloat(sum).toFixed(
            specialFormat[prop]?.decimalPlaces ?? 2
          );
        }
      } else {
        sums[index] = "";
      }
    } else {
      sums[index] = "";
    }
  });
  return sums;
};
// 不含税总价计算,decimals 为保留小数位数(销售/采购相关页面传 6)
const calculateTaxExclusiveTotalPrice = (
  taxInclusiveTotalPrice,
  taxRate,
  decimals = 2
) => {
  const taxRateDecimal = taxRate / 100;
  return (taxInclusiveTotalPrice / (1 + taxRateDecimal)).toFixed(decimals);
};
// 含税总价计算,decimals 为保留小数位数(销售/采购相关页面传 6)
const calculateTaxIncludeTotalPrice = (
  taxInclusiveUnitPrice,
  quantity,
  decimals = 2
) => {
  return (taxInclusiveUnitPrice * quantity).toFixed(decimals);
};
// 导出函数供其他文件使用
export {
  summarizeTable,
  calculateTaxExclusiveTotalPrice,
  calculateTaxIncludeTotalPrice,
};