You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dashboard/src/views/DataPivot.jsx

737 lines
31 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { useContext, useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import { useParams, useLocation } from 'react-router-dom';
import { stores_Context } from '../config';
import { Row, Col, Spin, Table, Select, Typography, Card, Button, Space, Divider, Alert } from 'antd';
import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique, cartesianProductArray } 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';
import { TableExportBtn } from './../components/Data';
const { Text } = Typography;
const filterFields = [
{ key: 'SourceType', label: '来源类型' },
{ key: 'productType', label: '产品类型' },
{ key: 'country', label: '国籍' },
{ key: 'CLI_NO', label: '线路' },
// { key: 'destination', label: '目的地' },
{ key: 'COLI_LineClass', label: '页面渠道' },
{ key: 'guestGroupType', label: '客群类别' },
{ key: 'travelMotivation', label: '出行目的' },
{ key: 'startMonth', label: '出行日期-月份' },
{ key: 'startYearMonth', label: '出行日期-年月' },
{ key: 'applyMonth', label: '预订日期-月份' },
{ key: 'applyYearMonth', label: '预订日期-年月' },
{ key: 'operatorName', label: '顾问' },
{ key: 'WebCode', label: '来源站点' },
{ key: 'IsOld_txt', label: '是否老客户' },
{ key: 'isCusCommend_txt', label: '是否老客户推荐' },
{ key: 'hasOld_txt', label: '老客户(推荐)' },
{ key: 'HotelStar', label: '酒店星级' },
{ key: 'destinationCountry_AsJOSN', label: '目的地国籍' },
{ key: 'destinations_AsJOSN', label: '目的地城市' },
{ key: 'RTXF_WB_range', label: '人天消费(外币)' },
{ key: 'PPPriceRange', label: '人均天/单(外币)' },
// { key: 'unitPPPriceRange', label: '人均天(外币)' },
// { 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 }), {});
/** 预设的选项, 只有行 */
const quickOptions = [
{ label: ' 来源站点 ', fields: [['WebCode'], []] },
{ label: '[ 产品×客群 ]', fields: [['productType', 'guestGroupType'], []] },
{ label: '[ 国籍×客群 ]', fields: [['country', 'guestGroupType'], []] },
{ label: '[ 客群×目的地国籍 ]', fields: [['guestGroupType', 'destinationCountry_AsJOSN'], []] },
// { label: '[ 国籍×客群 ]×[ ]', fields: [['country', 'guestGroupType'], []] },
];
// 注意TdCell要提到DataTable作用域外声明
const TdCell = (tdprops) => {
// onMouseEnter, onMouseLeave在数据量多的时候会严重阻塞表格单元格渲染严重影响性能
const { onMouseEnter, onMouseLeave, ...restProps } = tdprops;
return <td {...restProps} />;
};
const pageSetting = {
orders: {
xField: 'applyDate',
yField: 'SumOrder',
tableColumns: [
{ key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' },
// { key: 'ConfirmOrder', title: '成交数', dataIndex: 'ConfirmOrder', width: '5em' },
// { key: 'ConfirmPersonNum', title: '成交人数', dataIndex: 'ConfirmPersonNum', width: '5em' },
// { key: 'ConfirmRates', title: '成交率', dataIndex: 'ConfirmRates_txt', width: '5em' },
// { key: 'SumML', title: '毛利', dataIndex: 'SumML_txt', width: '5em' },
],
childrenColumns: [
{ key: 'ConfirmOrder', title: '成交数', dataIndex: 'ConfirmOrder', width: '5em' },
{ key: 'ConfirmRates', title: '成交率', dataIndex: 'ConfirmRates_txt', width: '5em' },
{ key: 'SumML', title: '毛利', dataIndex: 'SumML', width: '5em' }, // SumML_txt
{ key: 'SingleML', title: '单团毛利', dataIndex: 'SingleML', width: '5em' },
{ key: 'tourdays', title: '团天数', dataIndex: 'tourdays', width: '5em' },
{ key: 'confirmTourdays', title: '✅团天数', dataIndex: 'confirmTourdays', width: '5em' },
{ key: 'SumPersonNum', title: '人数', dataIndex: 'SumPersonNum', width: '5em' },
{ key: 'ConfirmPersonNum', title: '✅人数', dataIndex: 'ConfirmPersonNum', width: '5em' },
],
searchInitial: { DateType: { key: 'applyDate', value: 'applyDate', label: '提交日期' } },
},
trade: {
xField: 'confirmDate',
yField: 'SumML',
yFieldAlias: 'SumML_txt',
tableColumns: [
{ key: 'SumML', title: '毛利', dataIndex: 'SumML', width: '5em' }, // SumML_txt
],
childrenColumns: [
{ key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' },
{ key: 'ConfirmOrder', title: '成交数', dataIndex: 'ConfirmOrder', width: '5em' },
{ key: 'ConfirmRates', title: '成交率', dataIndex: 'ConfirmRates_txt', width: '5em' },
// { key: 'ResumeOrder', title: '老客户订单数', dataIndex: 'ResumeOrder', width: '5em', render: (_, r) => `${r.ResumeConfirmOrder}/${r.ResumeOrder}` },
{ key: 'SingleML', title: '单团毛利', dataIndex: 'SingleML', width: '5em' },
{ key: 'OrderValue', title: '单个订单价值', dataIndex: 'OrderValue', width: '5em' },
{ key: 'PPPriceRange', title: '人均天(外币)', dataIndex: 'PPPriceRange', width: '5em' },
{ key: 'AvgPPPrice', title: '人均天/单(外币)', dataIndex: 'AvgPPPrice', width: '5em' },
// { key: 'unitPPPrice', title: '人均天(外币)', dataIndex: 'unitPPPrice', width: '5em' },
// { key: 'unitPPPriceRange', title: '人均天区间(外币)', dataIndex: 'unitPPPriceRange', width: '5em' },
{ key: 'confirmDays', title: '成团周期(天)', dataIndex: 'confirmDays', width: '5em' },
{ key: 'applyDays', title: '预定周期(天)', dataIndex: 'applyDays', width: '5em' },
{ key: 'tourdays', title: '团天数', dataIndex: 'tourdays', width: '5em' },
],
searchInitial: { DateType: { key: 'confirmDate', value: 'confirmDate', label: '确认日期' } },
},
};
export default observer((props) => {
const { page } = useParams();
const { pathname } = useLocation();
const { date_picker_store: searchFormStore, orders_store, DataPivotStore } = useContext(stores_Context);
const { formValues, formValuesToSub, siderBroken } = searchFormStore;
const { originData } = DataPivotStore.detailData[page];
const { xField: defaultDateType, yField: defaultValKey, yFieldAlias, tableColumns, childrenColumns, searchInitial } = pageSetting[page];
const [curXfield, setCurXfield] = useState(defaultDateType);
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 [pivotRow, setPivotRow] = useState({});
const [pivotRowDataSource, setPivotRowDataSource] = useState([]);
const [pivotDataSource, setPivotDataSource] = useState([]);
const [pivotTableDataSource, setPivotTableDataSource] = useState([]);
const [pivotTableColumnSummary, setPivotTableColumnSummary] = useState({}); // 列单选, 只有一组结果
const [pivotDateColumns, setPivotDateColumns] = useState([[], []]);
const [pivotDateColumnsValues, setPivotDateColumnsValues] = useState([[], []]);
const [showPassCountryTips, setShowPassCountryTips] = useState(false);
useEffect(() => {
calcDataByDate();
resetX();
resetItemFilter();
return () => {};
}, [pivotDateColumns]);
useEffect(() => {
if (lineChartX === 'day') {
setDataBeforeXChange(dataSource);
}
return () => {};
}, [dataSource]);
useEffect(() => {
setCurXfield(formValuesToSub.DateType);
setLineConfig({...lineConfig, xField: formValuesToSub.DateType});
return () => {};
}, [formValues]);
const detailRefresh = async (obj) => {
setLoading(true);
DataPivotStore.getDetailData({
...(obj || formValuesToSub),
}, page).then((resData) => {
setLoading(false);
setRawData(resData);
calcDataByDate(resData);
resetX();
resetItemFilter();
});
};
/**
* 走势的数据
* 汇总
*/
const calcDataByDate = (_rawData) => {
// console.log(';;;;;', pivotDateColumns);
const { data, columnValues, summaryRows, summaryColumns, pivotKeys, summaryMix } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, [curXfield]));
// console.log('data====', data, '\ncolumnValues', columnValues, '\nsummaryRows', summaryRows, '\nsummaryColumns', summaryColumns, '\nsummaryMix', summaryMix);
setShowPassCountryTips(pivotKeys.includes('destinationCountry_AsJOSN'));
setDataBeforePick(data.sort(sortBy(curXfield)));
// 折线图汇总数据, 排序
// const sortMixData = cloneDeep(summaryMix).sort(sortBy(defaultValKey)).reverse();
// setPivotDataSource(sortMixData);
// const sortRank = sortMixData.map(ele => ele.rowLabel);
// 折线图数据
setDataSource(data.sort(sortBy(curXfield)));
// 排名
// .map(ele => ({...ele, rowLabel: `${sortRank.indexOf(ele.rowLabel)+1}).${ele.rowLabel}` }))
// 表格数据
const sortRowData = cloneDeep(summaryRows).sort(sortBy(defaultValKey)).reverse();
setPivotTableDataSource(sortRowData);
setPivotRow({});
setPivotRowDataSource([]);
// 列汇总
const _col1 = pivotDateColumns[1][0] || '';
const _sortByDateOrVal = (_col1.includes('Month') || _col1.includes('Date')) ? _col1 : defaultValKey;
let sortColData = summaryColumns.sort(sortBy(_sortByDateOrVal));
sortColData = _sortByDateOrVal === defaultValKey ? sortColData.reverse() : sortColData;
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: curXfield,
yField: defaultValKey,
seriesField: 'rowLabel',
// xAxis: {
// type: 'timeCat',
// },
yAxis: {
min: 0,
maxTickInterval: 5,
},
meta: {
...cloneDeep(dataFieldAlias),
},
// smooth: true,
label: {}, // 显示标签
legend: {
position: 'right-top',
// title: {
// text: '总合计 ' + dataSource.reduce((a, b) => a + b.SumOrder, 0),
// },
itemMarginBottom: 12, // 垂直间距
},
tooltip: {
customItems: (originalItems) => {
return originalItems.map(ele => ({...ele, valueR: ele.data[defaultValKey]})).sort(sortBy('valueR')).reverse();
},
},
};
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)));
!isEmpty(col) ? setColumnSelection(filterFieldsMapped[col[0]]) : setColumnSelection([]);
setRowFields(row);
setColumnFields(col);
resetItemFilter();
setPivotDateColumns(pivotFields);
};
const resetFields = () => {
setRowFields([]);
setColumnFields([]);
setRowSelection([]);
setColumnSelection([]);
resetItemFilter();
setPivotDateColumns([[], []]);
};
const handleRowsPick = (v) => {
setRowSelection(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) => {
setColumnSelection(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(curXfield)));
resetX();
};
// 日月年切换
const [lineChartX, setLineChartX] = useState('day');
const [avgLine1, setAvgLine1] = useState(0);
const orderCountDataMapper = { data1: 'data1', data2: undefined };
const orderCountDataFieldMapper = { 'dateKey': curXfield, 'valueKey': defaultValKey, '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' })),
...(isEmpty(pivotDateColumns[1]) ? [].concat(cloneDeep(tableColumns), childrenColumns) : tableColumns),
...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]?.[defaultValKey]}`,
dataIndex: ['columns', col, defaultValKey || yFieldAlias],
width: '6em',
})),
})),
],
};
const detailsTableProps = {
loading: false,
// sticky: true,
scroll: { x: 1000, y: 400 },
pagination: false,
columns: [
{
title: '订单号',
dataIndex: 'o_id',
key: 'o_id',
},
{
title: '来源站点',
dataIndex: 'WebCode',
key: 'WebCode',
},
{
title: '页面渠道',
dataIndex: 'COLI_LineClass',
key: 'COLI_LineClass',
},
{
title: '来源类型',
dataIndex: 'SourceType',
key: 'SourceType',
},
{
title: '客群类别',
dataIndex: 'guestGroupType',
key: 'guestGroupType',
},
{
title: '成行',
dataIndex: 'orderState',
key: 'orderState',
render: (text, record) => <span>{text === '1' ? '是' : '否'}</span>,
sorter: (a, b) => b.orderState - a.orderState,
},
// {
// title: "人数(成/童/婴)",
// dataIndex: "COLI_PersonNum",
// key: "COLI_PersonNum",
// render: (text, record) => (
// <span>
// {record.COLI_PersonNum}/{record.COLI_ChildNum}/{record.COLI_BabyNum}
// </span>
// ),
// },
{
title: '预计利润',
dataIndex: 'ML',
key: 'ML',
},
{
title: '预定时间',
dataIndex: 'applyDate',
key: 'applyDate',
},
{
title: '出发日期',
dataIndex: 'startDate',
key: 'startDate',
},
],
};
return (
<div key={pathname}>
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
<Col className="gutter-row" span={24}>
<SearchForm
defaultValue={{
initialValue: {
...formValues,
...orders_store.searchValues,
...searchInitial,
},
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates'], // 'country'
fieldProps: {
DepartmentList: { show_all: false, mode: 'multiple', col: 5 },
WebCode: { show_all: false, mode: 'multiple', col: 5 },
dates: { hide_vs: true },
},
}}
onSubmit={(_err, obj, form, str) => {
detailRefresh(obj);
}}
/>
</Col>
</Row>
{/* extra={<Button type="link">重置</Button>} */}
<Card
size={'small'}
title={'透视选项'}
extra={
<Row gutter={3}>
<Col flex={'0 0 auto'}>
<Text type={'secondary'}>预设:</Text>
</Col>
{quickOptions.map((ele, elei) => (
<Col key={ele.label}>
<Button key={ele.label} onClick={() => quickOpt(elei)} type={'primary'} ghost size={'small'}>
{ele.label}
</Button>
</Col>
))}
<Col>
<Button key={'reset-quick'} onClick={resetFields} type={'primary'} ghost size={'small'}>
重置
</Button>
</Col>
</Row>
}
>
<Row gutter={16}>
<Col span={24}>
{/* todo: 拖拽的操作 */}
{/* <div className='p-s1' style={{border: '1px solid #d9d9d9 '}}>
{filterFields.map((tag) => (
<Tag key={tag.key} checked={selectedTags.indexOf(tag.key) > -1} onChange={(checked) => handleChange(tag.key, checked)} color={'orange'}>
{tag.label}
</Tag>
))}
</div> */}
</Col>
{/* 行: */}
<Col md={12} sm={24} xs={24}>
<Row gutter={8} align={'middle'}>
<Col flex={'4em'} align={'end'}>
<Text strong>: </Text>
</Col>
<Col flex={'auto'}>
<Select
labelInValue
mode={'multiple'}
style={{ width: '100%' }}
placeholder={`选择`}
onChange={(v) => handleRowsPick(v)}
value={rowSelection}
maxTagCount={2}
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
>
{filterFields.map((ele) => (
<Select.Option key={ele.key} value={ele.key}>
{ele.label}
</Select.Option>
))}
</Select>
</Col>
<Col span={24}>
{rowFields.length > 0
? cloneDeep(pivotDateColumnsValues)[0]
// .slice(0, rowFields.length)
.map((_colArr, _colIndex) => (
<Row gutter={8} key={filterFieldsMapped[pivotDateColumns[0][_colIndex]]?.key || _colIndex}>
<Col flex={'5em'} align={'end'}>
<Text strong>{filterFieldsMapped[pivotDateColumns[0][_colIndex]]?.label}: </Text>
</Col>
<Col flex={'auto'}>
<Select
size={'small'}
labelInValue
// mode={_colIndex === 0 ? 'multiple' : null}
mode={'multiple'}
style={{ width: '100%' }}
placeholder={`选择`}
onChange={(v) => handleFieldsItemPick(v, _colIndex, pivotDateColumns[0][_colIndex], 'row')}
value={rowsItemValues?.[pivotDateColumns[0][_colIndex]]}
maxTagCount={1}
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
>
{pivotDateColumnsValues[0][_colIndex].map((ele) => (
<Select.Option key={ele} value={ele}>
{ele}
</Select.Option>
))}
</Select>
</Col>
</Row>
))
: null}
</Col>
</Row>
</Col>
{/* 列: */}
<Col md={12} sm={24} xs={24} style={{ borderLeft: '1px solid #d9d9d9' }}>
<Row gutter={8} align={'middle'}>
<Col flex={'4em'} align={'end'}>
<Text strong>: </Text>
</Col>
<Col flex={'auto'}>
<Select
labelInValue
// mode={'multiple'}
style={{ width: '100%' }}
placeholder={`选择`}
onChange={(v) => handleColsPick(v)}
value={columnSelection}
maxTagCount={2}
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
>
{rightFields.map((ele) => (
<Select.Option key={ele.key} value={ele.key}>
{ele.label}
</Select.Option>
))}
</Select>
</Col>
<Col span={24}>
<Row gutter={16} align={'bottom'} className="mt-1">
<Col span={24}>{showPassCountryTips && <Alert message="途径的国家将会重复计算" type="warning" showIcon />}</Col>
</Row>
{/* {columnFields.length > 0
? cloneDeep(pivotDateColumnsValues)
.slice(rowFields.length)
.map((_colArr, _colIndex) => (
<>
<Row gutter={8} key={_colArr.join('_')}>
<Col flex={'5em'} align={'end'}>
<Text strong>{filterFieldsMapped[pivotDateColumns[_colIndex + rowFields.length]]?.label || _colIndex + rowFields.length}: </Text>
</Col>
<Col flex={'auto'}>
<Select
size={'small'}
labelInValue
mode={_colIndex + rowFields.length === 0 ? 'multiple' : null}
style={{ width: '100%' }}
placeholder={`选择`}
onChange={(v) => handleFieldsItemPick(v, _colIndex + rowFields.length, pivotDateColumns[_colIndex + rowFields.length], 'col')}
// value={sale_store.salesTrade.pickSales}
maxTagCount={1}
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
>
{pivotDateColumnsValues[_colIndex + rowFields.length].map((ele) => (
<Select.Option key={ele} value={ele}>
{ele}
</Select.Option>
))}
</Select>
</Col>
</Row>
</>
))
: null} */}
</Col>
</Row>
</Col>
</Row>
</Card>
<section>
<Row gutter={16} justify={'space-between'} className="mb-1">
<Col flex={'auto'}>
<h3>
走势: <span style={{ fontSize: 'smaller' }}>{dataFieldAlias[lineConfig.yField].label}</span>
</h3>
</Col>
<Col style={{ textAlign: 'right' }} align={'end'}>
<DateGroupRadio
visible={true}
dataRaw={{ data1: dataBeforeXChange }}
onChange={onChangeXDateFieldGroup}
value={lineChartX}
dataMapper={orderCountDataMapper}
fieldMapper={orderCountDataFieldMapper}
/>
</Col>
</Row>
<Spin spinning={loading}>
<Line {...lineConfig} data={dataSource} />
</Spin>
</section>
<section>
<Spin spinning={loading}>
<h3>
透视汇总表: <span style={{ fontSize: 'smaller' }}>{dataFieldAlias[lineConfig.yField].label}</span>
</h3>
<Divider orientation="right">
<TableExportBtn
label={'pivot-' + pivotDateColumns[0].map((ele, ri) => `${filterFieldsMapped[ele].label}`).join('×')}
{...{ columns: targetTableProps.columns, dataSource: pivotTableDataSource }}
/>
</Divider>
<Table
{...targetTableProps}
dataSource={pivotTableDataSource}
components={{ body: { cell: TdCell } }}
onRow={(record) => {
return {
// 点击行
onClick: (event) => {
setPivotRow(record);
const thisDetail = rawData.filter((ele) => {
return record.keys.includes(String(ele.key));
});
setPivotRowDataSource(thisDetail);
},
};
}}
/>
</Spin>
</section>
<section>
<Spin spinning={loading}>
<h3>
点击上方表格行查看单行数据的订单明细:{' '}
<span style={{ fontSize: 'smaller' }}>{pivotDateColumns[0].map((ele, ri) => `${filterFieldsMapped[ele].label}: ${pivotRow[pivotDateColumns[0][ri]] || ''}`).join('; ')}</span>
</h3>
<Divider orientation="right">
<TableExportBtn
label={'pivot-' + pivotDateColumns[0].map((ele, ri) => `${pivotRow[pivotDateColumns[0][ri]] || ''}`).join('-')}
{...{ columns: detailsTableProps.columns, dataSource: pivotRowDataSource }}
/>
</Divider>
<Table {...detailsTableProps} dataSource={pivotRowDataSource} components={{ body: { cell: TdCell } }} />
</Spin>
</section>
</div>
);
});