|
|
@ -192,17 +192,70 @@ const calcPPPriceRange = (value) => {
|
|
|
|
if (value < 0) {
|
|
|
|
if (value < 0) {
|
|
|
|
return '--';
|
|
|
|
return '--';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const step = 30; // step = 30 USD
|
|
|
|
const step = 30; // step = 30 USD
|
|
|
|
const start = Math.floor(value / step) * step;
|
|
|
|
const start = Math.floor(value / step) * step;
|
|
|
|
const end = start + step;
|
|
|
|
const end = start + step;
|
|
|
|
|
|
|
|
|
|
|
|
if (value >= 301) {
|
|
|
|
if (value >= 301) {
|
|
|
|
return `301-Infinity`;
|
|
|
|
return `≥301`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return `${start === 0 ? start : (start+1)}-${end}`;
|
|
|
|
return `${start === 0 ? start : (start+1)}-${end}`;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function calculateRangeScale(data, numScales = 36) {
|
|
|
|
|
|
|
|
if (!data || data.length === 0 || numScales <= 0) {
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const sortedData = [...data].sort((a, b) => a - b);
|
|
|
|
|
|
|
|
const min = sortedData[0];
|
|
|
|
|
|
|
|
const max = sortedData[sortedData.length - 1];
|
|
|
|
|
|
|
|
if (min === max) {
|
|
|
|
|
|
|
|
return [roundToNice(min), roundToNice(min)];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const scales = [roundToNice(min)];
|
|
|
|
|
|
|
|
const scaleSize = sortedData.length / numScales;
|
|
|
|
|
|
|
|
for (let i = 1; i < numScales; i++) {
|
|
|
|
|
|
|
|
const index = Math.floor(i * scaleSize);
|
|
|
|
|
|
|
|
scales.push(roundToNice(sortedData[Math.min(index, sortedData.length - 1)]));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
scales.push(roundToNice(max));
|
|
|
|
|
|
|
|
return [...new Set(scales)];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function roundToNice(value) {
|
|
|
|
|
|
|
|
if (value === 0) {
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const magnitude = Math.pow(10, Math.floor(Math.log10(Math.abs(value))));
|
|
|
|
|
|
|
|
const normalized = value / magnitude;
|
|
|
|
|
|
|
|
let rounded;
|
|
|
|
|
|
|
|
if (normalized < 1.5) {
|
|
|
|
|
|
|
|
rounded = Math.floor(normalized);
|
|
|
|
|
|
|
|
} else if (normalized < 3) {
|
|
|
|
|
|
|
|
rounded = Math.floor(normalized * 2) / 2; // round to 0.5
|
|
|
|
|
|
|
|
} else if (normalized < 5) {
|
|
|
|
|
|
|
|
rounded = Math.floor(normalized/2) * 2; // round to 2
|
|
|
|
|
|
|
|
} else if (normalized < 7.5) {
|
|
|
|
|
|
|
|
rounded = Math.floor(normalized / 5) * 5;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// rounded = Math.floor(normalized / 5) * 5;
|
|
|
|
|
|
|
|
rounded = Math.floor(normalized / 10) * 10;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return rounded * magnitude;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
const findRange = (value, scale) => {
|
|
|
|
|
|
|
|
if (value < scale[0]) {
|
|
|
|
|
|
|
|
return `0-${scale[0]}`; // `Value ${value} is below the scale range.`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 1; i < scale.length; i++) {
|
|
|
|
|
|
|
|
if (value >= scale[i - 1] && value < scale[i]) {
|
|
|
|
|
|
|
|
return `${scale[i - 1]}-${scale[i]}`; // `Value ${value} is in the range [${scale[i - 1]}, ${scale[i]})`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value >= scale[scale.length - 1]) {
|
|
|
|
|
|
|
|
return `≥${scale[scale.length - 1]}`; // `Value ${value} is in the range [${scale[scale.length - 1]}, Infinity)`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
const SumML_range = [1, 1.5, 2, 3, 4].map(v => v * 10000);
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 数据透视计算
|
|
|
|
* 数据透视计算
|
|
|
|
* @param {object[]} data
|
|
|
|
* @param {object[]} data
|
|
|
@ -216,19 +269,24 @@ export const pivotBy = (_data, [rows, columns, date]) => {
|
|
|
|
// if (groupbyKeys.includes('PPPriceRange')) {
|
|
|
|
// if (groupbyKeys.includes('PPPriceRange')) {
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// 补充计算的字段
|
|
|
|
// 补充计算的字段
|
|
|
|
|
|
|
|
const RTXF_WB_values = cloneDeep(_data).map(ele => ele.RTXF_WB);
|
|
|
|
|
|
|
|
// const max_RTXF_WB = Math.max(...RTXF_WB_values);
|
|
|
|
|
|
|
|
const RTXF_WB_range = calculateRangeScale(RTXF_WB_values);
|
|
|
|
let data = cloneDeep(_data).map(ele => {
|
|
|
|
let data = cloneDeep(_data).map(ele => {
|
|
|
|
ele.PPPrice = (Number(ele.orderState) === 1 && ele.tourdays && ele.personNum) ? fixToInt(ele.quotePrice / ele.tourdays / ele.personNum) : -1;
|
|
|
|
ele.PPPrice = (Number(ele.orderState) === 1 && ele.tourdays && ele.personNum) ? fixToInt(ele.quotePrice / ele.tourdays / ele.personNum) : -1; // 报价: 人均天
|
|
|
|
ele.PPPriceRange = calcPPPriceRange(ele.PPPrice);
|
|
|
|
ele.PPPriceRange = calcPPPriceRange(ele.PPPrice);
|
|
|
|
|
|
|
|
ele.RTXF_WB_range = findRange(ele.RTXF_WB, RTXF_WB_range);
|
|
|
|
ele.IsOld_txt = ele.IsOld === '1' ? '老客户' : '否';
|
|
|
|
ele.IsOld_txt = ele.IsOld === '1' ? '老客户' : '否';
|
|
|
|
ele.isCusCommend_txt = ele.isCusCommend === '1' ? '老客户推荐' : '否';
|
|
|
|
ele.isCusCommend_txt = ele.isCusCommend === '1' ? '老客户推荐' : '否';
|
|
|
|
const hasOld = (ele.IsOld === '1' || ele.isCusCommend === '1') ? 1 : 0;
|
|
|
|
const hasOld = (ele.IsOld === '1' || ele.isCusCommend === '1') ? 1 : 0;
|
|
|
|
ele.hasOld = hasOld;
|
|
|
|
ele.hasOld = hasOld;
|
|
|
|
ele.hasOld_txt = hasOld === 1 ? '老客户(推荐)' : '';
|
|
|
|
ele.hasOld_txt = hasOld === 1 ? '老客户(推荐)' : '';
|
|
|
|
ele.SumML_ctxt1 = ele.ML > 10000 ? '1W+' : '1W-';
|
|
|
|
// ele.SumML_ctxt1 = ele.ML > 10000 ? '1W+' : '1W-';
|
|
|
|
ele.SumML_ctxt1_5 = ele.ML > 15000 ? '1.5W+' : '1.5W-';
|
|
|
|
// ele.SumML_ctxt1_5 = ele.ML > 15000 ? '1.5W+' : '1.5W-';
|
|
|
|
ele.SumML_ctxt2 = ele.ML > 20000 ? '2W+' : '2W-';
|
|
|
|
// ele.SumML_ctxt2 = ele.ML > 20000 ? '2W+' : '2W-';
|
|
|
|
ele.SumML_ctxt3 = ele.ML > 30000 ? '3W+' : '3W-';
|
|
|
|
// ele.SumML_ctxt3 = ele.ML > 30000 ? '3W+' : '3W-';
|
|
|
|
ele.SumML_ctxt4 = ele.ML > 40000 ? '4W+' : '4W-';
|
|
|
|
// ele.SumML_ctxt4 = ele.ML > 40000 ? '4W+' : '4W-';
|
|
|
|
|
|
|
|
ele.SumML_ctxt = findRange(ele.ML, SumML_range);
|
|
|
|
return ele;
|
|
|
|
return ele;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
// 数组的字段值, 拆分处理
|
|
|
|
// 数组的字段值, 拆分处理
|
|
|
@ -244,6 +302,18 @@ export const pivotBy = (_data, [rows, columns, date]) => {
|
|
|
|
return r;
|
|
|
|
return r;
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (groupbyKeys.includes('destinations_AsJOSN')) {
|
|
|
|
|
|
|
|
data = _data.reduce((r, v, i) => {
|
|
|
|
|
|
|
|
const vjson = isEmpty(v.destinations_AsJOSN) ? [] : v.destinations_AsJOSN;
|
|
|
|
|
|
|
|
const xv = (vjson).reduce((rv, cv, vi) => {
|
|
|
|
|
|
|
|
rv.push({...v, destinations_AsJOSN: cv, key: vi === 0 ? v.key : `${v.key}@${cv}`});
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r = r.concat(xv);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getKeys = (keys) => keys.map((keyField) => [...new Set(data.map((f) => f[keyField]))]);
|
|
|
|
const getKeys = (keys) => keys.map((keyField) => [...new Set(data.map((f) => f[keyField]))]);
|
|
|
|
const [rowsKeys, columnsKeys, dateKeys] = [getKeys(rows), getKeys(columns), [getKeys([date])[0].filter(s => s)]];
|
|
|
|
const [rowsKeys, columnsKeys, dateKeys] = [getKeys(rows), getKeys(columns), [getKeys([date])[0].filter(s => s)]];
|
|
|
|