|
|
|
@ -1,4 +1,4 @@
|
|
|
|
|
import { fixTo4Decimals, fixTo1Decimals, fixToInt, groupBy, sortBy, cloneDeep, pick, unique, flush } from "../utils/commons";
|
|
|
|
|
import { fixTo4Decimals, fixTo1Decimals, fixToInt, groupBy, sortBy, cloneDeep, pick, unique, flush } from '../utils/commons';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 事业部
|
|
|
|
@ -88,7 +88,7 @@ export const sites = [
|
|
|
|
|
{ value: '30', key: '30', label: 'TP', code: 'trippest' },
|
|
|
|
|
{ value: '31', key: '31', label: '花梨鹰', code: 'HLY' },
|
|
|
|
|
];
|
|
|
|
|
export const sitesMappedByCode = sites.reduce((a, c) => ({ ...a, [String(c.code)]: {...c, key: c.code, value: c.code } }), {});
|
|
|
|
|
export const sitesMappedByCode = sites.reduce((a, c) => ({ ...a, [String(c.code)]: { ...c, key: c.code, value: c.code } }), {});
|
|
|
|
|
export const dateTypes = [
|
|
|
|
|
{ key: 'applyDate', value: 'applyDate', label: '提交日期' },
|
|
|
|
|
{ key: 'ConfirmDate', value: 'ConfirmDate', label: '确认日期' },
|
|
|
|
@ -123,11 +123,7 @@ export const dataFieldAlias = dataFieldOptions.reduce(
|
|
|
|
|
* KPI对象
|
|
|
|
|
*/
|
|
|
|
|
export const KPIObjects = [
|
|
|
|
|
{ key: 'overview', value: 'overview', label: '海纳', data: [
|
|
|
|
|
{ key: 'ALL', value: 'ALL', label: '海纳' },
|
|
|
|
|
...overviewGroup
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{ key: 'overview', value: 'overview', label: '海纳', data: [{ key: 'ALL', value: 'ALL', label: '海纳' }, ...overviewGroup] },
|
|
|
|
|
{
|
|
|
|
|
key: 'bizarea',
|
|
|
|
|
value: 'bizarea',
|
|
|
|
@ -174,88 +170,251 @@ export const KPISubjects = [
|
|
|
|
|
// { key: 'sum_person_num', value: 'sum_person_num', label: '人数' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 数据透视计算
|
|
|
|
|
* @param {object[]} data
|
|
|
|
|
* @param {any[]} groupby
|
|
|
|
|
* @param {object[]} keys
|
|
|
|
|
* @param {string} value
|
|
|
|
|
* @param {any[]} groupbyKeys
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
export const pivotBy = (data, [rows, columns, date], keys, value) => {
|
|
|
|
|
export const pivotBy = (data, [rows, columns, date]) => {
|
|
|
|
|
// console.time('pivot----');
|
|
|
|
|
console.log('pivotBy', [rows, columns, date]);
|
|
|
|
|
const groupbyKeys = flush([].concat(rows, columns, [date]));
|
|
|
|
|
console.log('pivotBy', [rows, columns, date], groupbyKeys );
|
|
|
|
|
const uniqueKeys = groupbyKeys.map(keyField => {
|
|
|
|
|
const keyu = [...new Set(data.map(f => f[keyField]))];
|
|
|
|
|
return keyu;
|
|
|
|
|
});
|
|
|
|
|
const pivotResult = []; // new Array(uniqueKeys.reduce((r, v) => r * v.length, 1));
|
|
|
|
|
const groupData = groupBy(data, row => groupbyKeys.map(kk => `${row[kk]}`).join('=@='));
|
|
|
|
|
|
|
|
|
|
Object.keys(groupData).map((group_str) => {
|
|
|
|
|
const _rowKey = groupData[group_str].map((v) => v.key).join('_');
|
|
|
|
|
const _len = groupData[group_str].length;
|
|
|
|
|
const _row = {
|
|
|
|
|
...pick(groupData[group_str][0], groupbyKeys),
|
|
|
|
|
...(groupbyKeys.length < 2 ? { rowKey: '总' } : { rowKey: cloneDeep(groupbyKeys).slice(0, -1).map(_k => groupData[group_str][0][_k]).join("»") }),
|
|
|
|
|
key: _rowKey,
|
|
|
|
|
SumOrder: _len,
|
|
|
|
|
SumPersonNum: groupData[group_str].reduce((r, v) => r + v.personNum, 0),
|
|
|
|
|
ConfirmOrder: groupData[group_str].reduce((r, v) => r + (Number(v.orderState) === 1 ? 1 : 0), 0),
|
|
|
|
|
transactions: groupData[group_str].reduce((r, v) => r + v.transactions, 0),
|
|
|
|
|
SumML: groupData[group_str].reduce((r, v) => r + v.ML, 0),
|
|
|
|
|
quotePrice: groupData[group_str].reduce((r, v) => r + v.quotePrice, 0), // todo: quotePrice
|
|
|
|
|
tourdays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.tourdays, 0) / _len),
|
|
|
|
|
applyDays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.applyDays, 0) / _len),
|
|
|
|
|
confirmDays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.confirmDays, 0) / _len),
|
|
|
|
|
};
|
|
|
|
|
pivotResult.push({
|
|
|
|
|
..._row,
|
|
|
|
|
ConfirmRates: _row.ConfirmOrder ? fixTo4Decimals(_row.ConfirmOrder / _row.SumOrder) : 0,
|
|
|
|
|
OrderValue: _row.SumOrder ? fixToInt(_row.SumML / _row.SumOrder) : 0,
|
|
|
|
|
});
|
|
|
|
|
return group_str;
|
|
|
|
|
});
|
|
|
|
|
// 列转置
|
|
|
|
|
const rowsData = groupBy(data, row => rows.map(kk => `${row[kk]}`).join('=@='));
|
|
|
|
|
const rowsWithColumns = Object.keys(rowsData).map(rowKey => {
|
|
|
|
|
const _colData = groupBy(rowsData[rowKey], crow => columns.map(kk => `${crow[kk]}`).join('=@='));
|
|
|
|
|
const _topRowKey = [];
|
|
|
|
|
return Object.keys(_colData).reduce((r, colKey) => {
|
|
|
|
|
const _len = _colData[colKey].length;
|
|
|
|
|
const _rowKey = _colData[colKey].map((v) => v.key).join('_');
|
|
|
|
|
_topRowKey.push(_rowKey);
|
|
|
|
|
const _row = {
|
|
|
|
|
const getKeys = (keys) => keys.map((keyField) => [...new Set(data.map((f) => f[keyField]))]);
|
|
|
|
|
const [rowsKeys, columnsKeys, dateKeys] = [getKeys(rows), getKeys(columns), getKeys([date])];
|
|
|
|
|
|
|
|
|
|
const calcTradeFields = (dataObj, keepKeys = [], seriesKey = '') => {
|
|
|
|
|
const outerKeys = [];
|
|
|
|
|
const _keepKeys = [...keepKeys, seriesKey];
|
|
|
|
|
const DataGroupByKeys = {};
|
|
|
|
|
|
|
|
|
|
Object.keys(dataObj).forEach((colKey) => {
|
|
|
|
|
const _len = dataObj[colKey].length;
|
|
|
|
|
const _rowKey = dataObj[colKey].map((v) => v.key).join('_');
|
|
|
|
|
outerKeys.push(_rowKey);
|
|
|
|
|
|
|
|
|
|
const initialData = {
|
|
|
|
|
...pick(dataObj[colKey][0], _keepKeys),
|
|
|
|
|
...(keepKeys.length === 0
|
|
|
|
|
? { rowLabel: '总' }
|
|
|
|
|
: {
|
|
|
|
|
rowLabel: cloneDeep(keepKeys)
|
|
|
|
|
// .slice(0, -1)
|
|
|
|
|
.map((_k) => dataObj[colKey][0][_k])
|
|
|
|
|
.join('»'),
|
|
|
|
|
}),
|
|
|
|
|
_label: colKey || '(空)',
|
|
|
|
|
key: _rowKey,
|
|
|
|
|
|
|
|
|
|
SumOrder: _len,
|
|
|
|
|
SumPersonNum: _colData[colKey].reduce((r, v) => r + v.personNum, 0),
|
|
|
|
|
ConfirmOrder: _colData[colKey].reduce((r, v) => r + (Number(v.orderState) === 1 ? 1 : 0), 0),
|
|
|
|
|
transactions: _colData[colKey].reduce((r, v) => r + v.transactions, 0),
|
|
|
|
|
SumML: _colData[colKey].reduce((r, v) => r + v.ML, 0),
|
|
|
|
|
quotePrice: _colData[colKey].reduce((r, v) => r + v.quotePrice, 0), // todo: quotePrice
|
|
|
|
|
tourdays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.tourdays, 0) / _len),
|
|
|
|
|
applyDays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.applyDays, 0) / _len),
|
|
|
|
|
confirmDays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.confirmDays, 0) / _len),
|
|
|
|
|
|
|
|
|
|
SumPersonNum: 0,
|
|
|
|
|
ConfirmOrder: 0,
|
|
|
|
|
transactions: 0,
|
|
|
|
|
SumML: 0,
|
|
|
|
|
quotePrice: 0,
|
|
|
|
|
tourdays: 0,
|
|
|
|
|
applyDays: 0,
|
|
|
|
|
confirmDays: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const calculatedData = dataObj[colKey].reduce((r, v) => {
|
|
|
|
|
r.SumPersonNum += v.personNum;
|
|
|
|
|
r.ConfirmOrder += Number(v.orderState) === 1 ? 1 : 0;
|
|
|
|
|
r.transactions += v.transactions;
|
|
|
|
|
r.SumML += v.ML;
|
|
|
|
|
r.quotePrice += v.quotePrice;
|
|
|
|
|
r.tourdays += v.tourdays;
|
|
|
|
|
r.applyDays += v.applyDays;
|
|
|
|
|
r.confirmDays += v.confirmDays;
|
|
|
|
|
return r;
|
|
|
|
|
}, initialData);
|
|
|
|
|
|
|
|
|
|
// Calculations
|
|
|
|
|
calculatedData.tourdays = Math.ceil(calculatedData.tourdays / _len);
|
|
|
|
|
calculatedData.applyDays = Math.ceil(calculatedData.applyDays / _len);
|
|
|
|
|
calculatedData.confirmDays = Math.ceil(calculatedData.confirmDays / _len);
|
|
|
|
|
const _rowCalc = {
|
|
|
|
|
ConfirmRates: _row.ConfirmOrder ? fixTo4Decimals(_row.ConfirmOrder / _row.SumOrder) : 0,
|
|
|
|
|
OrderValue: _row.SumOrder ? fixToInt(_row.SumML / _row.SumOrder) : 0,
|
|
|
|
|
ConfirmRates: calculatedData.ConfirmOrder ? fixTo4Decimals(calculatedData.ConfirmOrder / calculatedData.SumOrder) : 0,
|
|
|
|
|
OrderValue: calculatedData.SumOrder ? fixToInt(calculatedData.SumML / calculatedData.SumOrder) : 0,
|
|
|
|
|
};
|
|
|
|
|
DataGroupByKeys[colKey] = { ...calculatedData, ..._rowCalc };
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return { groupByKeys: DataGroupByKeys, key: outerKeys.join('_') };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const groupData = groupBy(data, (row) => groupbyKeys.map((kk) => `${row[kk]}`).join('=@='));
|
|
|
|
|
const rowsNcolumnsItems = calcTradeFields(groupData, [...rows, ...columns], date);
|
|
|
|
|
const pivotResult = Object.values(rowsNcolumnsItems.groupByKeys);
|
|
|
|
|
|
|
|
|
|
const transposeData = (keys, dataProp, [dataKey, colKeys]=[]) =>
|
|
|
|
|
Object.keys(dataProp)
|
|
|
|
|
.map((rowKey) => {
|
|
|
|
|
const _colKey = dataKey || 'dataKey';
|
|
|
|
|
const _colData = groupBy(dataProp[rowKey], (crow) => (colKeys || keys).map((kk) => `${crow[kk]}`).join('=@='));
|
|
|
|
|
const _columnsObj = calcTradeFields(_colData);
|
|
|
|
|
return { ...pick(dataProp[rowKey][0], keys), [_colKey]: _columnsObj.groupByKeys, key: _columnsObj.key };
|
|
|
|
|
})
|
|
|
|
|
.map((everyR) => {
|
|
|
|
|
const _colKey = dataKey || 'dataKey';
|
|
|
|
|
const allColumns = Object.values(everyR[_colKey]).reduce((r, c) => r.concat([c]), []);
|
|
|
|
|
const summaryCalc = ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum', 'quotePrice', 'tourdays', 'applyDays', 'confirmDays'].reduce(
|
|
|
|
|
(r, skey) => ({ ...r, [skey]: allColumns.reduce((a, c) => a + c[skey], 0) }),
|
|
|
|
|
everyR
|
|
|
|
|
);
|
|
|
|
|
summaryCalc.tourdays = Math.ceil(summaryCalc.tourdays / allColumns.length);
|
|
|
|
|
summaryCalc.applyDays = Math.ceil(summaryCalc.applyDays / allColumns.length);
|
|
|
|
|
summaryCalc.confirmDays = Math.ceil(summaryCalc.confirmDays / allColumns.length);
|
|
|
|
|
summaryCalc.ConfirmRates = summaryCalc.ConfirmOrder ? fixTo4Decimals(summaryCalc.ConfirmOrder / summaryCalc.SumOrder) : 0;
|
|
|
|
|
summaryCalc.OrderValue = summaryCalc.SumOrder ? fixToInt(summaryCalc.SumML / summaryCalc.SumOrder) : 0;
|
|
|
|
|
|
|
|
|
|
return { ...everyR, ...summaryCalc };
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const rowsData = groupBy(data, (row) => rows.map((kk) => `${row[kk]}`).join('=@='));
|
|
|
|
|
const summaryRows = transposeData(rows, rowsData, ['columns', columns]);
|
|
|
|
|
|
|
|
|
|
const columnsData = groupBy(data, (row) => columns.map((kk) => `${row[kk]}`).join('=@='));
|
|
|
|
|
const summaryColumns = transposeData(columns, columnsData, ['rows', rows]);
|
|
|
|
|
|
|
|
|
|
// console.timeEnd('pivot----');
|
|
|
|
|
return { data: pivotResult, columnValues: [rowsKeys, columnsKeys, dateKeys], summaryRows, summaryColumns };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// todo: 优化 pivotBy 速度
|
|
|
|
|
export const pivotBy3 = (data, [rows, columns, date]) => {
|
|
|
|
|
console.log('pivotBy', [rows, columns, date]);
|
|
|
|
|
console.time('pivot2');
|
|
|
|
|
// const rowKeys = new Set(data.map(row => row[rows[0]]));
|
|
|
|
|
const rowKeys = rows.map((keyField) => {
|
|
|
|
|
const keyu = new Set(data.map((f) => f[keyField]));
|
|
|
|
|
return keyu;
|
|
|
|
|
});
|
|
|
|
|
const colKeys = new Set(data.map(row => row[columns[0]]));
|
|
|
|
|
const dateKeys = new Set(data.map(row => row[date]));
|
|
|
|
|
|
|
|
|
|
const aggregatedData = {};
|
|
|
|
|
|
|
|
|
|
data.forEach(row => {
|
|
|
|
|
const rowKey = row[rows[0]] ?? '__total';
|
|
|
|
|
const colKey = row[columns[0]] ?? '__total';
|
|
|
|
|
const dateKey = row[date];
|
|
|
|
|
|
|
|
|
|
if (!aggregatedData[rowKey]) {
|
|
|
|
|
aggregatedData[rowKey] = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if (!aggregatedData[rowKey][colKey]) {
|
|
|
|
|
// aggregatedData[rowKey][colKey] = {};
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
if (!aggregatedData[rowKey][colKey]) {
|
|
|
|
|
aggregatedData[rowKey][colKey] = {
|
|
|
|
|
SumOrder: 0,
|
|
|
|
|
// other aggregated fields
|
|
|
|
|
SumPersonNum: 0,
|
|
|
|
|
ConfirmOrder: 0,
|
|
|
|
|
transactions: 0,
|
|
|
|
|
SumML: 0,
|
|
|
|
|
// ...
|
|
|
|
|
quotePrice: 0,
|
|
|
|
|
tourdays: 0,
|
|
|
|
|
applyDays: 0,
|
|
|
|
|
confirmDays: 0,
|
|
|
|
|
};
|
|
|
|
|
Object.assign(r.columns, {[colKey]: {..._row, ..._rowCalc}});
|
|
|
|
|
// (r.columns || (r.columns = {})).push({[colKey]: {..._row, ..._rowCalc}});
|
|
|
|
|
return {...r, key: _topRowKey.join('_')};
|
|
|
|
|
}, {...pick(rowsData[rowKey][0], rows), columns: {}});
|
|
|
|
|
}).map(everyR => {
|
|
|
|
|
const allColumns = Object.values(everyR.columns).reduce((r, c) => r.concat([c]), []);
|
|
|
|
|
return ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum'].reduce(
|
|
|
|
|
(r, skey) => ({ ...r,
|
|
|
|
|
[skey]: allColumns.reduce((a, c) => a + c[skey], 0),
|
|
|
|
|
}),
|
|
|
|
|
everyR
|
|
|
|
|
); // todo: 其他数据列计算
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aggregatedData[rowKey][colKey].SumOrder++;
|
|
|
|
|
aggregatedData[rowKey][colKey].SumPersonNum += row.personNum;
|
|
|
|
|
aggregatedData[rowKey][colKey].ConfirmOrder += Number(row.orderState === 1);
|
|
|
|
|
aggregatedData[rowKey][colKey].transactions += row.transactions;
|
|
|
|
|
aggregatedData[rowKey][colKey].SumML += row.ML;
|
|
|
|
|
// aggregate other fields
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// console.log('pivot res', uniqueKeys, rowsWithColumns, pivotResult);
|
|
|
|
|
return { data: pivotResult, columnValues: uniqueKeys, summary: rowsWithColumns };
|
|
|
|
|
const summarizedData = [];
|
|
|
|
|
|
|
|
|
|
// Generate summary rows
|
|
|
|
|
for (const rowKey of rowKeys) {
|
|
|
|
|
|
|
|
|
|
const rowAggregations = {
|
|
|
|
|
SumOrder: 0,
|
|
|
|
|
// other aggregated fields
|
|
|
|
|
SumPersonNum: 0,
|
|
|
|
|
ConfirmOrder: 0,
|
|
|
|
|
transactions: 0,
|
|
|
|
|
SumML: 0,
|
|
|
|
|
// ...
|
|
|
|
|
quotePrice: 0,
|
|
|
|
|
tourdays: 0,
|
|
|
|
|
applyDays: 0,
|
|
|
|
|
confirmDays: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Calculate aggregates over colKey
|
|
|
|
|
for (const colKey in aggregatedData[rowKey]) {
|
|
|
|
|
rowAggregations.SumOrder += aggregatedData[rowKey][colKey].SumOrder;
|
|
|
|
|
rowAggregations.SumPersonNum += aggregatedData[rowKey][colKey].SumPersonNum;
|
|
|
|
|
rowAggregations.ConfirmOrder += aggregatedData[rowKey][colKey].ConfirmOrder;
|
|
|
|
|
rowAggregations.transactions += aggregatedData[rowKey][colKey].transactions;
|
|
|
|
|
rowAggregations.SumML += aggregatedData[rowKey][colKey].SumML;
|
|
|
|
|
// ...aggregate all other fields
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const row = {
|
|
|
|
|
[rows[0]]: rowKey,
|
|
|
|
|
...rowAggregations
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
summarizedData.push(row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate summary columns
|
|
|
|
|
for (const colKey of colKeys) {
|
|
|
|
|
const colAggregations = {
|
|
|
|
|
SumOrder: 0,
|
|
|
|
|
// other aggregated fields
|
|
|
|
|
SumPersonNum: 0,
|
|
|
|
|
ConfirmOrder: 0,
|
|
|
|
|
transactions: 0,
|
|
|
|
|
SumML: 0,
|
|
|
|
|
// ...
|
|
|
|
|
quotePrice: 0,
|
|
|
|
|
tourdays: 0,
|
|
|
|
|
applyDays: 0,
|
|
|
|
|
confirmDays: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Calculate aggregates over rowKey
|
|
|
|
|
for (const rowKey in aggregatedData) {
|
|
|
|
|
if (aggregatedData[rowKey][colKey]) {
|
|
|
|
|
colAggregations.SumOrder += aggregatedData[rowKey][colKey].SumOrder;
|
|
|
|
|
colAggregations.SumPersonNum += aggregatedData[rowKey][colKey].SumPersonNum;
|
|
|
|
|
colAggregations.ConfirmOrder += aggregatedData[rowKey][colKey].ConfirmOrder;
|
|
|
|
|
colAggregations.transactions += aggregatedData[rowKey][colKey].transactions;
|
|
|
|
|
colAggregations.SumML += aggregatedData[rowKey][colKey].SumML;
|
|
|
|
|
// ...aggregate all other fields
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const col = {
|
|
|
|
|
[columns[0]]: colKey,
|
|
|
|
|
...colAggregations
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
summarizedData.push(col);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.timeEnd('pivot2');
|
|
|
|
|
console.log('pivot2 ddd', aggregatedData);
|
|
|
|
|
return {
|
|
|
|
|
data: [], // aggregatedData,
|
|
|
|
|
columnValues: [rowKeys, colKeys, dateKeys],
|
|
|
|
|
summaryRows: summarizedData.filter(r => r[rows[0]]),
|
|
|
|
|
summaryColumns: summarizedData.filter(c => c[columns[0]])
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|