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, 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'; import DateGroupRadio from '../components/DateGroupRadio'; const { Text } = Typography; const filterFields = [ { key: 'country', label: '国籍' }, { key: 'SourceType', label: '来源类型' }, { key: 'productType', label: '产品类型' }, { 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 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 result; }; // 注意TdCell要提到DataTable作用域外声明 const TdCell = (tdprops) => { // onMouseEnter, onMouseLeave在数据量多的时候,会严重阻塞表格单元格渲染,严重影响性能 const { onMouseEnter, onMouseLeave, ...restProps } = tdprops; return ; }; export default observer((props) => { const { page } = useParams(); const { date_picker_store: searchFormStore, orders_store, DistributionStore, DataPivotStore } = useContext(stores_Context); const { formValues, formValuesToSub } = searchFormStore; const { originData } = DataPivotStore.detailData[page]; const [loading, setLoading] = useState(false); const [rawData, setRawData] = useState(originData || []); const [dataBeforePick, setDataBeforePick] = useState([]); const [dataBeforeXChange, setDataBeforeXChange] = useState([]); const [dataSource, setDataSource] = useState([]); // const [dataSourceMapped, setDataSourceMapped] = useState({}); const [pivotTableDataSource, setPivotTableDataSource] = useState([]); const [pivotTableColumnSummary, setPivotTableColumnSummary] = useState({}); // 列单选, 只有一组结果 const [pivotDateColumns, setPivotDateColumns] = useState([[], []]); const [pivotDateColumnsValues, setPivotDateColumnsValues] = useState([[], []]); useEffect(() => { calcDataByDate(); resetX(); resetItemFilter(); return () => {}; }, [pivotDateColumns]); useEffect(() => { if (lineChartX === 'day') { setDataBeforeXChange(dataSource); } return () => {}; }, [dataSource]); const detailRefresh = async (obj) => { setLoading(true); DataPivotStore.getDetailData({ ...(obj || formValuesToSub), }, page).then((resData) => { setLoading(false); setRawData(resData); calcDataByDate(resData); resetX(); resetItemFilter(); }); }; const valKey = 'SumOrder'; const timesKey = 'applyDate'; /** * 走势的数据 * 汇总 */ const calcDataByDate = (_rawData) => { // console.log(';;;;;', pivotDateColumns); 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: timesKey, yField: valKey, seriesField: 'rowLabel', // xAxis: { // type: 'timeCat', // }, yAxis: { min: 0, maxTickInterval: 5, }, // smooth: true, label: {}, // 显示标签 legend: { position: 'right-top', // title: { // text: '总合计 ' + dataSource.reduce((a, b) => a + b.SumOrder, 0), // }, itemMarginBottom: 12, // 垂直间距 }, }; const [lineConfig, setLineConfig] = useState(cloneDeep(line_config)); // 透视配置:行列选项 // 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); // const leftFieldsMapped = leftFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); const _left = omit(filterFieldsMapped, pickKeys); setRightFields(isEmpty(v) ? filterFields : Object.values(_left)); // setPivotDateColumns([].concat(pickKeys, columnFields)); setPivotDateColumns([pickKeys, columnFields]); resetItemFilter(); }; const handleColsPick = (val) => { const pickKeys = isEmpty(val) ? [] : Array.isArray(val) ? val.map((ele) => ele.key) : [val.key]; setColumnFields(pickKeys); const afterLeft = Object.values(omit(filterFieldsMapped, rowFields)); setRightFields(afterLeft); // 单选 // const rightFieldsMapped = rightFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); // const _left = omit(rightFieldsMapped, pickKeys); // setRightFields(isEmpty(val) ? afterLeft : Object.values(_left)); // 多选 // setPivotDateColumns([].concat(rowFields, pickKeys)); setPivotDateColumns([rowFields, pickKeys]); resetItemFilter(); }; // 行列的值选项 const [rowsItemValues, setRowsItemValues] = useState(); const [columnsItemValues, setColumnsItemValues] = useState(); const [rowsFilter, setRowsFilter] = useState(); const [columnsFilter, setColumnsFilter] = useState(); const resetItemFilter = () => { setRowsItemValues(null); setColumnsItemValues(null); setRowsFilter(null); setColumnsFilter(null); }; const handleFieldsItemPick = (v, columnsIndex, columnsName, actionSeries) => { const _curFilter = { [columnsName]: actionSeries === 'row' ? v : [v] }; // console.log('handleFieldsItemPick', v, columnsIndex, columnsName, actionSeries, _curFilter); const _rowsF = actionSeries === 'row' ? { ...rowsFilter, ..._curFilter } : rowsFilter; const _columnsF = actionSeries === 'col' ? { ...columnsFilter, ..._curFilter } : columnsFilter; actionSeries === 'row' ? setRowsFilter(_rowsF) : setColumnsFilter(_columnsF); const currentFilterMerge = { rows: _rowsF || {}, columns: _columnsF || {}, }; setRowsItemValues(currentFilterMerge.rows); setColumnsItemValues(currentFilterMerge.columns); const rowsFilterFields = Object.keys(currentFilterMerge.rows).filter((ele) => currentFilterMerge.rows[ele].length); const dataMappedByRows = groupBy(dataBeforePick, (row) => rowsFilterFields.map((kk) => `${row[kk]}`).join('=@=')); const rowsFilterKey = isEmpty(rowsFilterFields) ? [] : 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]), []); const columnsFilterFields = Object.keys(currentFilterMerge.columns).filter((ele) => currentFilterMerge.rows[ele].length); const allFilterValues = [].concat( rowsFilterFields.reduce((r, v) => r.concat(currentFilterMerge.rows[v]), []), columnsFilterFields.reduce((r, v) => r.concat(currentFilterMerge.columns[v]), []) ); 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(timesKey))); resetX(); }; // 日月年切换 const [lineChartX, setLineChartX] = useState('day'); const [avgLine1, setAvgLine1] = useState(0); const orderCountDataMapper = { data1: 'data1', data2: undefined }; const orderCountDataFieldMapper = { 'dateKey': timesKey, 'valueKey': valKey, 'seriesKey': 'rowLabel', _f: 'sum' }; const resetX = () => { setLineChartX('day'); setAvgLine1(0); }; const onChangeXDateFieldGroup = (value, data, avg1) => { const { xField, yField, seriesField } = lineConfig; const groupByDate = data.reduce((r, v) => { (r[v[xField]] || (r[v[xField]] = [])).push(v); return r; }, {}); const _data = Object.keys(groupByDate).reduce((r, _d) => { const xAxisGroup = groupByDate[_d].reduce((a, v) => { (a[v[seriesField]] || (a[v[seriesField]] = [])).push(v); return a; }, {}); Object.keys(xAxisGroup).map((_group) => { const summaryVal = xAxisGroup[_group].reduce((rows, row) => rows + row[yField], 0); r.push({ ...xAxisGroup[_group][0], [yField]: summaryVal }); return _group; }); return r; }, []); // .map((row) => ({ [xField]: row[xField], [yField]: row[yField], [seriesField]: row[seriesField], rowX: row.dateRange[0] })); setLineChartX(value); setDataSource(_data); setAvgLine1(avg1); }; const targetTableProps = { loading: false, // sticky: true, scroll: { x: 1000, y: 400 }, pagination: false, columns: [ ...pivotDateColumns[0].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, dataIndex: ele, width: '6em', fixed: 'left' })), { key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' }, ...pivotDateColumns[1].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, 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', })), })), ], }; return ( <> { detailRefresh(obj); }} /> {/* extra={} */} ( // // ))} > {/* todo: 拖拽的操作 */} {/*
{filterFields.map((tag) => ( -1} onChange={(checked) => handleChange(tag.key, checked)} color={'orange'}> {tag.label} ))}
*/} 行: {rowFields.length > 0 ? cloneDeep(pivotDateColumnsValues)[0] // .slice(0, rowFields.length) .map((_colArr, _colIndex) => ( {filterFieldsMapped[pivotDateColumns[0][_colIndex]]?.label}: )) : null} 列: {/* {columnFields.length > 0 ? cloneDeep(pivotDateColumnsValues) .slice(rowFields.length) .map((_colArr, _colIndex) => ( <> {filterFieldsMapped[pivotDateColumns[_colIndex + rowFields.length]]?.label || _colIndex + rowFields.length}: )) : null} */}

走势: {dataFieldAlias[lineConfig.yField].label}

透视汇总表: {dataFieldAlias[lineConfig.yField].label}

); });