UNPKG

fastlion-amis

Version:

一种MIS页面生成工具

195 lines (193 loc) 7.72 kB
import { IColumn } from "../../store/table"; import { numberFormatter } from "../../utils/helper"; import { ISelect } from "./DataStatic"; export const Statistics = { // 计算合计sum | 计算个数num sum: (arr: any[], field?: string, type?: 'sum' | 'num', calculatedField?: string) => { if (arr.some(item => typeof item === 'number')) { const res = arr.reduce((total: number, curr: number) => +Number(+total) + Number(+curr), 0); return (res as number).toFixed(2) } else { let sum = 0; if (calculatedField && field) { let numerator = 0;//分子 let denominator = 0;//分母 arr.forEach(item => { numerator += (item[field] || 0) / 100 * (item[calculatedField] || 0); denominator += (item[calculatedField] || 0); }) return denominator === 0 ? 0 : numerator / denominator * 100 } arr.forEach(item => { // sum的时候当数值算, num的时候当1算 默认当数值算 sum += Number(type === 'num' ? 1 : (item && field && item[field] ? item[field] : 0)); }); // 排序string型 会采用unicode排序 所以出参得是数字 return Math.round((+sum || 0) * 100) / 100; } }, max: (values: number[]): number => Math.max(...values), min: (values: number[]): number => Math.min(...values), colLen: (values: number[]): number => values?.length, avg: (values: number[]) => (Number(Statistics.sum(values)) / Statistics.colLen(values)).toFixed(2), std_v: (values: number[]): number => { let length = values?.length; let temp = new Array(length); const avg = Number(Statistics.avg(values)); for (let i = 0; i < length; i++) { let dev = values[i] - avg; temp[i] = Math.pow(dev, 2); } let powSum = Number(Statistics.sum(temp)); return Math.sqrt(powSum / length); }, count_null: (values: number[]): number => values.filter(value => value === null).length, per: (arr: obj[], field: string, data: obj[], type?: 'sum' | 'num') => { let sumAll = 0; let sum = 0; arr.forEach(item => { // sum的时候当数值算, num的时候当1算 默认当数值算 sum += Number(type === 'num' ? 1 : item?.[field] || 0); }); data.forEach(item => { // sum的时候当数值算, num的时候当1算 默认当数值算 sumAll += Number(type === 'num' ? 1 : item?.[field] || 0); }); // 防止报错 if (!+sumAll) return 0 return sum / sumAll * 100; }, g_per: (arr: obj[], field: string, data: obj[], groupList: string[]) => { const groupArr = groupList.slice(0, groupList.length - 1); const groupData = groupList.length < 3 ? data.filter(v => v[groupList[0]] === arr[0][groupList[0]]) : data.filter(v => groupArr.every(val => v[val] === arr[0][val])); let groupSum = 0; let sum = 0; sum += Number(Statistics.sum(arr, field)); groupSum += Number(Statistics.sum(groupData, field)); // 防止报错 if (!+groupSum) return 0 return sum / groupSum * 100; } } //计算分组 function groupBy(array: obj[], conditionFun: (rowData: obj) => any): obj[][] { let groups = {}; array.forEach(function (o) { let group = JSON.stringify(conditionFun(o)); groups[group] = groups[group] || []; groups[group].push(o); }); return Object.keys(groups).map(function (group) { return groups[group]; }); } enum typeName { sum = 'sum', per = 'per', g_per = 'g_per', avg = 'avg', max = 'max', min = 'min', std_v = 'std_v', count_null = 'count_null' } //计算分组统计 export function computed(data: obj[], groudbyList: string[], statisByList: string[], typeList: string[], columns: any) { //计算分组数据 let groudList = groupBy(data, (item) => { let str = ''; groudbyList.forEach(groudStr => { str += item[groudStr] }) return str; }); //含有百分比计算方法的列先拿出来 const percentList = columns.filter((col: any) => (col.isPercentage || col.pristine?.isPercentage) && (col.calculatedField || col.pristine?.calculatedField)); return groudList.map(arr => { let firstRow = arr?.[0] || []; //行数据 let rowData = {}; //提取分组属性数据 groudbyList.forEach(item => { rowData[item] = firstRow[item] }) //提取计算数据属性 statisByList.forEach(item => { const countType = groudbyList.includes(item) ? 'num' : 'sum'; const targetCol = percentList.find((col: any) => col.name === item); typeList.forEach(type => { if (typeName[type] === 'per') { rowData[item + typeName[type]] = Statistics[type](arr, item, data, countType) } else if (typeName[type] === 'g_per') { rowData[item + typeName[type]] = Statistics[type](arr, item, data, groudbyList) } else if (typeName[type] === 'sum') { if (targetCol) { rowData[item + typeName[type]] = Statistics[type](arr, item, countType, targetCol.calculatedField ?? targetCol.pristine?.calculatedField) } else { rowData[item + typeName[type]] = Statistics[type](arr, item, countType) } } else { rowData[item + typeName[type]] = Statistics[type](arr.map(dataItem => dataItem?.[item])) } }) }) return rowData; }) } //计算列统计 export function computedStatic(data: obj[], content: ISelect[], statisByList: string[], originColList: IColumn[]) { let tableData: any = []; //含有百分比计算方法的列先拿出来 const percentList = originColList.filter((col: any) => (col.isPercentage || col.pristine?.isPercentage) && (col.calculatedField || col.pristine?.calculatedField)); statisByList.forEach((info) => { let obj = { statistics: originColList.find(col => col.name === info)?.label || '', } const currCol: any = originColList.find(item => info === item.name); const targetCol: any = percentList.find((col: any) => col.name === info); const formatObj = { suffix: currCol.suffix ?? currCol.pristine?.suffix ?? '', prefix: currCol.prefix ?? currCol.pristine?.prefix ?? '', precision: currCol.precision ?? currCol.pristine?.precision, kilobitSeparator: currCol.kilobitSeparator ?? currCol.pristine?.kilobitSeparator } content.forEach((item) => { if (item.value === 'sum' && targetCol) { obj[item.value] = dealCellValue(Number(Statistics[item.value](data, info, 'sum', targetCol.calculatedField ?? targetCol.pristine?.calculatedField)), formatObj) } else { const values = data.map((dataItem) => dataItem[info]) if (item.value == 'std_v') { obj[item.value] = Number(Statistics[item.value](values)).toFixed(2) } else if (item.value == 'count_null') { obj[item.value] = Statistics[item.value](values) } else { obj[item.value] = dealCellValue(Number(Statistics[item.value](values)), formatObj) } } }) tableData.push(obj) }) return tableData; } const dealCellValue = (originValue: number, formatObj: { suffix?: string, prefix?: string, kilobitSeparator?: boolean, precision?: number }) => { const { suffix, prefix, precision, kilobitSeparator } = formatObj; let value: string | number = originValue; if (kilobitSeparator && originValue != null) { value = numberFormatter(originValue, precision) } else if (originValue != null && typeof originValue == 'number') { value = originValue.toFixed(precision) } if (value != null) { return typeof value == 'object' ? '[文件]' : `${prefix}${typeof value == 'string' ? value.replaceAll('\n', '').replaceAll('\r', '') : value}${suffix}` } return '' }