| | |
| | | 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 values = data.map(item => Number(item[prop])); |
| | | // 只对有效数字进行求和 |
| | | 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 { |
| | | // 默认保留两位小数 |
| | | sums[index] = parseFloat(sum).toFixed(specialFormat[prop]?.decimalPlaces ?? 2); |
| | | } |
| | | } else { |
| | | sums[index] = ''; |
| | | } |
| | | 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] = ''; |
| | | // 默认保留两位小数 |
| | | sums[index] = parseFloat(sum).toFixed( |
| | | specialFormat[prop]?.decimalPlaces ?? 2 |
| | | ); |
| | | } |
| | | }); |
| | | return sums; |
| | | } |
| | | // 不含税总价计算 |
| | | const calculateTaxExclusiveTotalPrice = (taxInclusiveTotalPrice, taxRate) => { |
| | | const taxRateDecimal = taxRate / 100; |
| | | return (taxInclusiveTotalPrice / (1 + taxRateDecimal)).toFixed(2); |
| | | } |
| | | // 含税总价计算 |
| | | const calculateTaxIncludeTotalPrice = (taxInclusiveUnitPrice, quantity) => { |
| | | return (taxInclusiveUnitPrice * quantity).toFixed(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}; |
| | | export { |
| | | summarizeTable, |
| | | calculateTaxExclusiveTotalPrice, |
| | | calculateTaxIncludeTotalPrice, |
| | | }; |