feat: 数据透视: 增加 人天消费; 酒店星级; perf: 毛利范围

main
Lei OT 3 months ago
parent f1e0c1200b
commit d29dab5824

@ -192,17 +192,70 @@ const calcPPPriceRange = (value) => {
if (value < 0) {
return '--';
}
const step = 30; // step = 30 USD
const start = Math.floor(value / step) * step;
const end = start + step;
if (value >= 301) {
return `301-Infinity`;
return `301`;
}
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
@ -216,19 +269,24 @@ export const pivotBy = (_data, [rows, columns, date]) => {
// 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 => {
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.RTXF_WB_range = findRange(ele.RTXF_WB, RTXF_WB_range);
ele.IsOld_txt = ele.IsOld === '1' ? '老客户' : '否';
ele.isCusCommend_txt = ele.isCusCommend === '1' ? '老客户推荐' : '否';
const hasOld = (ele.IsOld === '1' || ele.isCusCommend === '1') ? 1 : 0;
ele.hasOld = hasOld;
ele.hasOld_txt = hasOld === 1 ? '老客户(推荐)' : '';
ele.SumML_ctxt1 = ele.ML > 10000 ? '1W+' : '1W-';
ele.SumML_ctxt1_5 = ele.ML > 15000 ? '1.5W+' : '1.5W-';
ele.SumML_ctxt2 = ele.ML > 20000 ? '2W+' : '2W-';
ele.SumML_ctxt3 = ele.ML > 30000 ? '3W+' : '3W-';
ele.SumML_ctxt4 = ele.ML > 40000 ? '4W+' : '4W-';
// ele.SumML_ctxt1 = ele.ML > 10000 ? '1W+' : '1W-';
// ele.SumML_ctxt1_5 = ele.ML > 15000 ? '1.5W+' : '1.5W-';
// ele.SumML_ctxt2 = ele.ML > 20000 ? '2W+' : '2W-';
// ele.SumML_ctxt3 = ele.ML > 30000 ? '3W+' : '3W-';
// ele.SumML_ctxt4 = ele.ML > 40000 ? '4W+' : '4W-';
ele.SumML_ctxt = findRange(ele.ML, SumML_range);
return ele;
});
// 数组的字段值, 拆分处理
@ -244,6 +302,18 @@ export const pivotBy = (_data, [rows, columns, date]) => {
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 [rowsKeys, columnsKeys, dateKeys] = [getKeys(rows), getKeys(columns), [getKeys([date])[0].filter(s => s)]];

@ -26,15 +26,18 @@ const filterFields = [
{ key: 'IsOld_txt', label: '是否老客户' },
{ key: 'isCusCommend_txt', label: '是否老客户推荐' },
{ key: 'hasOld_txt', label: '老客户(推荐)' },
{ key: 'HotelStar', label: '酒店星级' },
{ key: 'destinationCountry_AsJOSN', label: '目的地国籍' },
{ key: 'PPPriceRange', label: '人均天/单区间(外币)' },
{ key: 'destinations_AsJOSN', label: '目的地城市' },
{ key: 'RTXF_WB_range', label: '人天消费(外币)' },
{ key: 'PPPriceRange', label: '人均天/单(外币)' },
// { key: 'unitPPPriceRange', label: '()' },
// todo: , ,
{ key: 'SumML_ctxt1', label: '毛利范围[1W]' },
{ key: 'SumML_ctxt1_5', label: '毛利范围[1.5W]' },
{ key: 'SumML_ctxt2', label: '毛利范围[2W]' },
{ key: 'SumML_ctxt3', label: '毛利范围[3W]' },
{ key: 'SumML_ctxt4', label: '毛利范围[4W]' },
// { key: 'SumML_ctxt1', label: '[1W]' },
// { key: 'SumML_ctxt1_5', label: '[1.5W]' },
// { key: 'SumML_ctxt2', label: '[2W]' },
// { key: 'SumML_ctxt3', label: '[3W]' },
// { key: 'SumML_ctxt4', label: '[4W]' },
{ key: 'SumML_ctxt', label: '毛利' },
];
const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {});
@ -706,6 +709,7 @@ export default observer((props) => {
/>
</Spin>
</section>
{ !pivotDateColumns[0].includes('destinationCountry_AsJOSN') && !pivotDateColumns[0].includes('destinations_AsJOSN') &&
<section>
<Spin spinning={loading}>
<h3>
@ -721,6 +725,7 @@ export default observer((props) => {
<Table {...detailsTableProps} dataSource={pivotRowDataSource} components={{ body: { cell: TdCell } }} />
</Spin>
</section>
}
</div>
);
});

Loading…
Cancel
Save