fastlion-amis
Version:
一种MIS页面生成工具
195 lines (193 loc) • 7.72 kB
text/typescript
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 ''
}