From cfeecf4af58d22a3bcb0e75e71a61f37ae83ddfe Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 13 Nov 2023 11:30:34 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E9=80=8F=E8=A7=86=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.css | 2 +- src/App.jsx | 6 +- src/libs/ht.js | 315 ++++++++++++++----- src/stores/DataPivot.js | 49 +++ src/stores/Index.js | 2 + src/views/{OrdersPivot.jsx => DataPivot.jsx} | 141 +++++---- 6 files changed, 378 insertions(+), 137 deletions(-) create mode 100644 src/stores/DataPivot.js rename src/views/{OrdersPivot.jsx => DataPivot.jsx} (78%) diff --git a/src/App.css b/src/App.css index 1acdfbf..02207ca 100644 --- a/src/App.css +++ b/src/App.css @@ -32,7 +32,7 @@ padding: 0; } .p-s1{ - padding: .5em; + padding: .5rem!important; } .sticky-top{ diff --git a/src/App.jsx b/src/App.jsx index df96778..f76d996 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -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: 数据透视, + label: 数据透视, // icon: , }, ], @@ -196,7 +196,7 @@ const App = () => { } /> } /> } /> - } /> + } /> }> } /> diff --git a/src/libs/ht.js b/src/libs/ht.js index a423627..bcf85dc 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -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]]) + }; + }; diff --git a/src/stores/DataPivot.js b/src/stores/DataPivot.js new file mode 100644 index 0000000..ece3763 --- /dev/null +++ b/src/stores/DataPivot.js @@ -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; diff --git a/src/stores/Index.js b/src/stores/Index.js index 48dfba1..947763c 100644 --- a/src/stores/Index.js +++ b/src/stores/Index.js @@ -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); } diff --git a/src/views/OrdersPivot.jsx b/src/views/DataPivot.jsx similarity index 78% rename from src/views/OrdersPivot.jsx rename to src/views/DataPivot.jsx index 8fe250e..cba838c 100644 --- a/src/views/OrdersPivot.jsx +++ b/src/views/DataPivot.jsx @@ -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; }; // 注意TdCell要提到DataTable作用域外声明 @@ -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) => { {/* extra={} */} - + ( + // + // ))} + > {/* 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) => { {rowFields.length > 0 - ? cloneDeep(pivotDateColumnsValues) - .slice(0, rowFields.length) + ? cloneDeep(pivotDateColumnsValues)[0] + // .slice(0, rowFields.length) .map((_colArr, _colIndex) => ( - {filterFieldsMapped[pivotDateColumns[0][_colIndex]].label}: + {filterFieldsMapped[pivotDateColumns[0][_colIndex]]?.label}: