perf: 透视计算速度

feature/pivot
Lei OT 2 years ago
parent 494dde5276
commit cfeecf4af5

@ -32,7 +32,7 @@
padding: 0;
}
.p-s1{
padding: .5em;
padding: .5rem!important;
}
.sticky-top{

@ -42,7 +42,7 @@ import ExchangeRate from './charts/ExchangeRate';
import KPI from './views/KPI';
import Distribution from './views/Distribution';
import Detail from './views/Detail';
import OrderPivot from './views/OrdersPivot';
import DataPivot from './views/DataPivot';
import Welcome from './views/Welcome';
import { stores_Context, APP_VERSION } from './config';
import { WaterMark } from '@ant-design/pro-components';
@ -71,7 +71,7 @@ const App = () => {
},
{
key: 'orders-pivot',
label: <NavLink to="/orders-pivot">数据透视</NavLink>,
label: <NavLink to="/orders/pivot">数据透视</NavLink>,
// icon: <DashboardOutlined />,
},
],
@ -196,7 +196,7 @@ const App = () => {
<Route path="/orders" element={<Orders />} />
<Route path="/orders_sub/:ordertype/:ordertype_sub/:ordertype_title" element={<Orders_sub />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/orders-pivot" element={<OrderPivot />} />
<Route path="/:page/pivot" element={<DataPivot />} />
</Route>
<Route element={<ProtectedRoute auth={['admin', 'director_bu', 'customer_care']} />}>
<Route path="/customer_care_inchina" element={<Customer_care_inchina />} />

@ -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]])
};
};

@ -0,0 +1,49 @@
import { makeAutoObservable, runInAction, toJS } from 'mobx';
import { fetchJSON } from '../utils/request';
import { isEmpty, sortBy, pick, merge, fixTo2Decimals, groupBy, sortKeys, fixToInt, cloneDeep } from '../utils/commons';
import { dataFieldAlias } from './../libs/ht';
class Trade {
constructor(rootStore) {
this.rootStore = rootStore;
makeAutoObservable(this);
}
/**
* 明细
*/
getDetailData = async (param, page) => {
this.detailData[page] = { loading: true, dataSource: [], originData: [] };
const json = await fetchJSON('/service-Analyse2/GetTradeApartDetail', param);
if (json.errcode === 0) {
runInAction(() => {
this.detailData[page].loading = false;
this.detailData[page].dataSource = json.result;
this.detailData[page].originData = json.result;
});
}
return json.result;
};
setSearchValues(body) {
this.searchValues = body;
}
timeLineKey = 'week';
setTimeLineKey(v) {
this.timeLineKey = v;
}
resetData = () => {
this.detailData = {
orders: { loading: false, dataSource: [], originData: [] },
};
};
searchValues = {};
detailData = {
orders: { loading: false, dataSource: [], originData: [] },
};
}
export default Trade;

@ -14,6 +14,7 @@ import TradeStore from "./Trade";
import KPI from "./KPI";
import DictData from "./DictData";
import Distribution from "./Distribution";
import DataPivot from './DataPivot';
class Index {
constructor() {
this.dashboard_store = new DashboardStore(this);
@ -31,6 +32,7 @@ class Index {
this.KPIStore = new KPI(this);
this.DictDataStore = new DictData(this);
this.DistributionStore = new Distribution(this);
this.DataPivotStore = new DataPivot(this);
makeAutoObservable(this);
}

@ -2,8 +2,8 @@ import { useContext, useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import { useParams } from 'react-router-dom';
import { stores_Context } from '../config';
import { Row, Col, Spin, Table, Button, Select, Typography, Card } from 'antd';
import { cloneDeep, groupBy, isEmpty, omit, sortBy } from '../utils/commons';
import { Row, Col, Spin, Table, Select, Typography, Card, Button } from 'antd';
import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique } from '../utils/commons';
import { dataFieldAlias, pivotBy } from '../libs/ht';
import SearchForm from '../components/search/SearchForm';
import { Line } from '@ant-design/plots';
@ -18,26 +18,27 @@ const filterFields = [
{ key: 'guestGroupType', label: '客群类别' },
{ key: 'travelMotivation', label: '出行动机' },
{ key: 'operatorName', label: '顾问' },
{ key: 'WebCode', label: '来源站点' },
// todo: , , [PPC, NL...]
];
const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {});
const quickOptions = [
{ label: '国籍×产品', fields: [['country'], ['productType']] },
{ label: '产品×客群', fields: [['productType'], ['guestGroupType']] },
];
const combineArrays = (arr, sep = '_') => {
// if (arr.length === 0) return [];
if (arr.length === 1) return arr[0].map((item) => item);
const output = [];
const head = arr[0];
const tail = arr.slice(1);
head.forEach((item) => {
const suffixes = combineArrays(tail, sep);
suffixes.forEach((suffix) => {
output.push(`${item}${sep}${suffix}`);
});
/**
* 计算笛卡尔积
*/
const cartesianProductArray = (arr, sep = '_', index = 0, prefix = '') => {
let result = [];
if(index === arr.length){
return [prefix];
}
arr[index].forEach(item => {
result = result.concat(cartesianProductArray(arr, sep, index+1, prefix ? `${prefix}${sep}${item}` : `${item}`));
});
return output;
return result;
};
// TdCellDataTable
@ -48,23 +49,23 @@ const TdCell = (tdprops) => {
};
export default observer((props) => {
const { field } = useParams();
const { date_picker_store: searchFormStore, orders_store, DistributionStore } = useContext(stores_Context);
const { page } = useParams();
const { date_picker_store: searchFormStore, orders_store, DistributionStore, DataPivotStore } = useContext(stores_Context);
const { formValues, formValuesToSub } = searchFormStore;
// const { curTab, scatterDays, detailData } = DistributionStore;
const { originData } = DataPivotStore.detailData[page];
const [loading, setLoading] = useState(false);
const [rawData, setRawData] = useState([]);
const [rawData, setRawData] = useState(originData || []);
const [dataBeforePick, setDataBeforePick] = useState([]);
const [dataBeforeXChange, setDataBeforeXChange] = useState([]);
const [dataSource, setDataSource] = useState([]);
const [dataSourceMapped, setDataSourceMapped] = useState({});
// const [dataSourceMapped, setDataSourceMapped] = useState({});
const [pivotTableDataSource, setPivotTableDataSource] = useState([]);
const [pivotTableColumnSummary, setPivotTableColumnSummary] = useState({}); // ,
const [pivotColumns, setPivotColumns] = useState([]);
const [pivotDateColumns, setPivotDateColumns] = useState([[], []]);
const [pivotDateColumnsValues, setPivotDateColumnsValues] = useState([]);
const [pivotDateColumnsValues, setPivotDateColumnsValues] = useState([[], []]);
useEffect(() => {
calcDataByDate();
@ -84,40 +85,50 @@ export default observer((props) => {
const detailRefresh = async (obj) => {
setLoading(true);
DistributionStore.getDetailData({
DataPivotStore.getDetailData({
...(obj || formValuesToSub),
}).then((resData) => {
}, page).then((resData) => {
setLoading(false);
setRawData(resData);
// const { data, columnValues } = pivotBy(resData, ['country', 'guestGroupType', 'applyDate'], 'personNum');
// setPivotDateColumns(['applyDate']);
calcDataByDate(resData);
// setLineChartX('day');
resetX();
resetItemFilter();
});
};
const valKey = 'SumOrder';
const timesKey = 'applyDate';
/**
* 走势的数据
* 汇总
*/
const calcDataByDate = (_rawData) => {
// console.log(';;;;;', pivotDateColumns);
const { data, columnValues, summary } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, ['applyDate']));
setPivotDateColumnsValues(cloneDeep(columnValues).slice(0, -1));
setDataBeforePick(data.sort(sortBy('applyDate')));
setDataSource(data.sort(sortBy('applyDate')));
setPivotTableDataSource(summary.sort(sortBy('SumOrder')).reverse());
const { data, columnValues, summaryRows, summaryColumns } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, [timesKey]));
// console.log('data====', data, '\ncolumnValues', columnValues, '\nsummaryRows', summaryRows, '\nsummaryColumns', summaryColumns);
setDataBeforePick(data.sort(sortBy(timesKey)));
// 线
setDataSource(data.sort(sortBy(timesKey)));
//
const sortRowData = cloneDeep(summaryRows).sort(sortBy(valKey)).reverse();
setPivotTableDataSource(sortRowData);
//
const sortColData = summaryColumns.sort(sortBy(valKey)).reverse();
const colDataMapped = isEmpty(pivotDateColumns[1]) ? sortColData[0] : sortColData.reduce((r, v) => ({...r, [v[pivotDateColumns[1][0]]]: v}), {});
setPivotTableColumnSummary(colDataMapped);
//
const _r = (pivotDateColumns[0].map(eleR => unique(sortRowData.map(ele => ele[eleR]))));
const _c = (pivotDateColumns[1].map(eleC => unique(sortColData.map(ele => ele[eleC]))));
// console.log('_r', _r, '_c', _c);
setPivotDateColumnsValues([_r, _c, columnValues[2]]);
};
const line_config = {
// data: dataSource,
padding: 'auto',
xField: 'applyDate',
yField: 'SumOrder',
seriesField: 'rowKey',
xField: timesKey,
yField: valKey,
seriesField: 'rowLabel',
// xAxis: {
// type: 'timeCat',
// },
@ -139,11 +150,21 @@ export default observer((props) => {
const [lineConfig, setLineConfig] = useState(cloneDeep(line_config));
//
const [leftFields, setLeftFields] = useState(filterFields);
// const [leftFields, setLeftFields] = useState(filterFields);
const [rightFields, setRightFields] = useState(filterFields);
const [rowFields, setRowFields] = useState([]);
const [columnFields, setColumnFields] = useState([]);
const [rowSelection, setRowSelection] = useState();
const [columnSelection, setColumnSelection] = useState();
const quickOpt = (i) => {
const { fields: pivotFields } = quickOptions[i];
const [row, col] = pivotFields;
setRowSelection(Object.values(pick(filterFieldsMapped, row)));
setColumnSelection(filterFieldsMapped[col[0]]);
setPivotDateColumns(pivotFields);
};
const handleRowsPick = (v) => {
const pickKeys = v.map((ele) => ele.key);
setRowFields(pickKeys);
@ -196,14 +217,13 @@ export default observer((props) => {
const dataMappedByRows = groupBy(dataBeforePick, (row) => rowsFilterFields.map((kk) => `${row[kk]}`).join('=@='));
const rowsFilterKey = isEmpty(rowsFilterFields)
? []
: combineArrays(
: cartesianProductArray(
Object.values(currentFilterMerge.rows)
.map((kv) => kv.map((kf) => kf.key))
.filter((s) => s.length),
'=@='
);
const afterRowsFilter = isEmpty(rowsFilterFields) ? dataBeforePick : rowsFilterKey.reduce((r, _key) => r.concat(dataMappedByRows[_key]), []);
// console.log('afterRowsFilter', rowsFilterFields, afterRowsFilter);
const columnsFilterFields = Object.keys(currentFilterMerge.columns).filter((ele) => currentFilterMerge.rows[ele].length);
const allFilterValues = [].concat(
@ -214,15 +234,15 @@ export default observer((props) => {
const dataMapped = groupBy(afterRowsFilter, (row) => row[columnsName]);
const pickData = isEmpty(v) ? afterRowsFilter : Array.isArray(v) ? v.reduce((r, v) => r.concat(dataMapped[v.value]), []) : dataMapped[v.value];
setDataSource(allFilterValues.length === 0 ? dataBeforePick : pickData.sort(sortBy('applyDate')));
setDataSource(allFilterValues.length === 0 ? dataBeforePick : pickData.sort(sortBy(timesKey)));
resetX();
};
// debug: raw data action
//
const [lineChartX, setLineChartX] = useState('day');
const [avgLine1, setAvgLine1] = useState(0);
const orderCountDataMapper = { data1: 'data1', data2: undefined };
const orderCountDataFieldMapper = { 'dateKey': 'applyDate', 'valueKey': 'SumOrder', 'seriesKey': 'rowKey', _f: 'sum' };
const orderCountDataFieldMapper = { 'dateKey': timesKey, 'valueKey': valKey, 'seriesKey': 'rowLabel', _f: 'sum' };
const resetX = () => {
setLineChartX('day');
setAvgLine1(0);
@ -260,16 +280,17 @@ export default observer((props) => {
pagination: false,
columns: [
...pivotDateColumns[0].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, dataIndex: ele, width: '6em', fixed: 'left' })),
// { key: 'groupsLabel', title: '', dataIndex: 'groupsLabel' },
{ key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' },
...pivotDateColumns[1].map((ele) => ({
key: ele,
title: filterFieldsMapped[ele].label,
align: 'left',
children: cloneDeep(pivotDateColumnsValues)
.slice(-1)[0]
.sort()
.map((col) => ({ key: col, title: col || '(空)', dataIndex: col, width: '6em', render: (_, r) => r.columns[col]?.SumOrder || '' })),
align: 'left', className: 'p-s1',
children: cloneDeep(pivotDateColumnsValues[1][0] || []).map((col) => ({
key: col,
title: `${col || '(空)'}: ${pivotTableColumnSummary[col]?.[valKey]}`,
dataIndex: ['columns', col, valKey],
width: '6em',
})),
})),
],
};
@ -299,7 +320,15 @@ export default observer((props) => {
</Row>
{/* extra={<Button type="link">重置</Button>} */}
<Card size={'small'} title={'透视选项'}>
<Card
size={'small'}
title={'透视选项'}
// extra={quickOptions.map((ele, elei) => (
// <Button key={ele.label} type="link" onClick={() => quickOpt(elei)}>
// {ele.label}
// </Button>
// ))}
>
<Row gutter={16}>
<Col span={24}>
{/* todo: 拖拽的操作 */}
@ -324,6 +353,7 @@ export default observer((props) => {
placeholder={`选择`}
onChange={(v) => handleRowsPick(v)}
// value={sale_store.salesTrade.pickSales}
// value={rowSelection}
maxTagCount={2}
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
@ -337,12 +367,12 @@ export default observer((props) => {
</Col>
<Col span={24}>
{rowFields.length > 0
? cloneDeep(pivotDateColumnsValues)
.slice(0, rowFields.length)
? cloneDeep(pivotDateColumnsValues)[0]
// .slice(0, rowFields.length)
.map((_colArr, _colIndex) => (
<Row gutter={8} key={_colArr.join('_')}>
<Col flex={'5em'} align={'end'}>
<Text strong>{filterFieldsMapped[pivotDateColumns[0][_colIndex]].label}: </Text>
<Text strong>{filterFieldsMapped[pivotDateColumns[0][_colIndex]]?.label}: </Text>
</Col>
<Col flex={'auto'}>
<Select
@ -358,7 +388,7 @@ export default observer((props) => {
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
>
{pivotDateColumnsValues[_colIndex].map((ele) => (
{pivotDateColumnsValues[0][_colIndex].map((ele) => (
<Select.Option key={ele} value={ele}>
{ele}
</Select.Option>
@ -384,6 +414,7 @@ export default observer((props) => {
placeholder={`选择`}
onChange={(v) => handleColsPick(v)}
// value={sale_store.salesTrade.pickSales}
// value={columnSelection}
maxTagCount={2}
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
Loading…
Cancel
Save