fastlion-amis
Version:
一种MIS页面生成工具
290 lines (276 loc) • 10.6 kB
text/typescript
import { IAbcDataParams } from ".";
import { IColumn } from "../../../store/table";
import { getDecimalPlaces } from "../../../utils/utils";
export const activityBasedClassification = (data: number[], areaLimit: { aType: number, bType: number }): ABC => {
data = data.sort((a, b) => (b - a));
const total = data?.length
? data.reduce((prev, curr) => {
return +prev + +curr;
}) : 0;
let A: number[] = [], B: number[] = [], C: number[] = [];
let totalA = 0, totalB = 0, totalC = 0;
// 修复A类分配逻辑:先检查添加后是否满足条件,再决定是否添加
while (data.length > 0) {
const nextItem = data[0];
// 只有当前totalA加上下一项后仍不超过阈值时,才添加到A类
if (totalA + nextItem <= total * areaLimit.aType / 100) {
A.push(...data.splice(0, 1));
totalA += nextItem;
} else {
break;
}
}
// B类分配逻辑:B类的阈值应该是从A类阈值到B类阈值的总和
const bLimit = total * areaLimit.bType / 100;
while (data.length > 0) {
const nextItem = data[0];
// 需要考虑A类已经占用的部分,所以使用totalA + totalB + nextItem来比较
if (totalA + totalB + nextItem <= bLimit) {
B.push(...data.splice(0, 1));
totalB += nextItem;
} else {
break;
}
}
// 剩余数据为C类
C = data;
totalC = data.length > 0 ? C.reduce((prev, curr) => +prev + +curr) : 0;
return {
A, B, C
}
}
export const getTable = (colsName: string[], abcData: ABC, params: IAbcDataParams, originColList: IColumn[], data: any[]) => {
let colList = originColList.filter(col => col.name && colsName.includes(col.name));
const label = colList[0].label;
const yAxisLabel = colList.find(col => col.name === params.statisticsSelects)?.label;
let colsArr = [
{
label: label + '类',
name: 'type',
type: 'plain'
},
{
label: label + '项数',
name: 'sum',
type: 'number',
},
{
label: label + '占比',
name: 'per',
type: 'number',
suffix: '%',
precision: 3,
isPercentage: true
},
{
label: yAxisLabel + '占比',
name: 'sumPer',
type: 'number',
suffix: '%',
precision: 3,
isPercentage: true
},
];
if (params.displayEachColumn) {
colsArr.push({
label: label + '列',
name: 'col',
type: 'plain'
});
}
let tableColList = [];
for (let i = 0; i < colsArr.length; i++) {
const info = colsArr[i];
let col: any = {
name: info.name,
label: info.label,
type: info.type!,
suffix: info.suffix,
precision: info.precision,
isNumerical: true,
sortable: true,
isPercentage: info.isPercentage,
}
tableColList.push(col);
}
// {
// caption: colList[1].attr.caption + '总价均值',
// fieldName: 'sumValue'
// }
let col: IColumn = JSON.parse(JSON.stringify(colList[1]));
col = {
...col.pristine,
...col
}
col.label = yAxisLabel + '均值';
col.name = 'sumValue';
// 江惠需求:希望每一类的那列 在最后一列
// tableColList.push(col)
tableColList.splice(-1, 0, col);
let colData = data
.sort(compare(params.statisticsSelects))
.map(item => {
let val = item[params.sortSelectes]
// 不太明白什么是lookUpData
// let col = colList.find(item => item.name === params.sortSelectes)
// if (col?.elementType === 'lookup' && lookUpData) {
// val = lookUpData?.[col.fieldName]?.find(lookup => lookup.value == item[col?.lookUpKeyField])?.[col.fieldName]
// }
return val
});
function compare(property: string) {
return function (obj1: obj<any>, obj2: obj<any>) {
var value1 = obj1[property];
var value2 = obj2[property];
return value2 - value1; // 升序
}
}
const tableData = Object.keys(abcData).map((key: keyof ABC) => {
const cols = colData.splice(0, abcData[key].length);
const sumValue = cols.length ? (AbcAnalysisMath.sum(abcData[key]) / cols.length).toFixed(2) : 0;
return {
type: key + '类',
sum: abcData[key].length,
per: AbcAnalysisMath.per(key, abcData),
sumPer: AbcAnalysisMath.percent(abcData[key], abcData),
sumValue,
col: cols.join(',')
}
});
return { tableColumns: tableColList, tableData }
}
export const formatAbcTableData = (params: IAbcDataParams, data1: obj<any>[]) => {
const { settingType, aType, bType } = params
let colName = params.statisticsSelects;
let colData: number[] = [];
data1.forEach(dataItem => {
colData.push(+dataItem[colName]);
});
let abType = {
aType: 65,
bType: 85
}
if (settingType === 'manual') {
abType = { aType, bType }
}
return { colData, abType }
}
export function statisticsData(sortName: string | number, compotedNames: string[] | number[], data: obj[], originColList: IColumn[]): obj[] {
if (!sortName || !data.length) {
return [];
}
let _data: obj<any>[] = [];
if (compotedNames && compotedNames.length) {
data.forEach(dataItem => {
if (_data.length === 0) {
return _data.push(dataItem);
}
let ifPush = true;
_data.forEach(_dataItem => {
if (_dataItem[sortName] === dataItem[sortName]) {
ifPush = false;
const compotedArr = Array.isArray(compotedNames) ? compotedNames : [compotedNames]
compotedArr.forEach(name => {
_dataItem[name] = +dataItem[name] + +_dataItem[name];
});
}
});
ifPush && _data.push(dataItem);
});
} else {
// 列统计
let sortNameContents = [];
data.forEach(item => {
const field: any = originColList.find(item => item.name === sortName);
const value = `${item[sortName]}`;
if (value && sortNameContents.indexOf(value) === -1) {
sortNameContents.push(value);
item['_COUNT'] = item['_COUNT'] || 1;
item['_PERCENT'] = item['_PERCENT'] || (item['_COUNT'] / data.length * 100).toFixed(2) + '%';
// _data.push(field.elementType === 'lookup' ?
// {
// ...item,
// [sortName]: item[field.lookUpKeyField].split(field?.flLookup?.valueSep || ',')
// .join(field?.flLookup?.valueSep || ',')
// }
// :
// item
// );
_data.push(item)
} else {
sortNameContents.forEach(content => {
if (content === value) {
_data.forEach((_item, i) => {
const _value = _item[sortName] && `${_item[sortName]}`;
if (_value === value) {
_data[i]['_COUNT'] = _item['_COUNT'] + 1;
_data[i]['_PERCENT'] = (_data[i]['_COUNT'] / data.length * 100).toFixed(2) + '%';
}
})
}
})
}
});
}
return _data;
}
export const getAbcTableData = (params: IAbcDataParams, data: obj<any>[], originColList: IColumn[]) => {
const { sortSelectes, statisticsSelects } = params
// 需要深拷贝data 因为statisticsData内部对引用地址的数据进行了操作
let dealData: obj<any>[] = JSON.parse(JSON.stringify(data)) // 需要处理的数据
let sortName = sortSelectes
const _data = statisticsData(sortName, [statisticsSelects], dealData, originColList);
const { colData, abType } = formatAbcTableData(params, _data)
let abcData = activityBasedClassification(colData, abType);
return getTable([sortSelectes, statisticsSelects], abcData, params, originColList, _data);
}
export type ABC = {
A: number[];
B: number[];
C: number[];
}
export const AbcAnalysisMath = {
sum: (values: number[]): number => {
let numberMaxLen = 2;
return values.length ? values.reduce((total: number, curr: number) => {
numberMaxLen = Math.max(getDecimalPlaces(curr), numberMaxLen)
return +Number(+total) + Number(+curr)
}, 0) : 0
},
colLen: (values: number[]): number => values.length,
avg: (values: number[]): number => AbcAnalysisMath.sum(values) / AbcAnalysisMath.colLen(values),
percent: (v: number[], values: ABC): number => {
let totalA = values.A.length ? values.A.reduce((total, curr) => total + curr) : 0;
let totalB = values.B.length ? values.B.reduce((total, curr) => total + curr) : 0;
let totalC = values.C.length ? values.C.reduce((total, curr) => total + curr) : 0;
let value = v.length ? v.reduce((total, curr) => total + curr) : 0;
// 数据为数字型,才能进行继续统计
if (totalA + totalB + totalC === 0) return 0
return (value / (totalA + totalB + totalC)) * 100;
},
per: (key: keyof ABC, values: ABC): number => {
let len = values.A.length + values.B.length + values.C.length;
// 数据为数字型,才能进行继续统计
if (len === 0) return 0
return (values[key].length / len) * 100;
}
}
// 计算累计占有
export const getBasedCount = (data: obj[], sortName?: string, statisticsSelects?: string) => {
if (!sortName || !data.length || !statisticsSelects) {
return [];
}
let total = 0;
data.forEach(item => {
total += item[statisticsSelects]
})
let sum = 0;
const newArr = data.map(item => {
sum+=item[statisticsSelects]
return {
...item,
countPer: (sum / total * 100).toFixed(3)
}
})
return newArr;
}