|
|
|
@ -0,0 +1,589 @@
|
|
|
|
|
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: 'operatorName', label: '顾问' },
|
|
|
|
|
{ key: 'WebCode', label: '来源站点' },
|
|
|
|
|
// todo: 目的地, 目的地国家,
|
|
|
|
|
{ key: 'destinationCountry_AsJOSN', label: '目的地国籍' },
|
|
|
|
|
];
|
|
|
|
|
const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {});
|
|
|
|
|
|
|
|
|
|
/** 预设的选项, 只有行 */
|
|
|
|
|
const quickOptions = [
|
|
|
|
|
{ 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' },
|
|
|
|
|
],
|
|
|
|
|
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: 'SingleML', title: '单团毛利', dataIndex: 'SingleML', width: '5em' },
|
|
|
|
|
{ key: 'OrderValue', title: '单个订单价值', dataIndex: 'OrderValue', 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 [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 } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, [curXfield]));
|
|
|
|
|
// console.log('data====', data, '\ncolumnValues', columnValues, '\nsummaryRows', summaryRows, '\nsummaryColumns', summaryColumns);
|
|
|
|
|
setShowPassCountryTips(pivotKeys.includes('destinationCountry_AsJOSN'));
|
|
|
|
|
setDataBeforePick(data.sort(sortBy(curXfield)));
|
|
|
|
|
// 折线图数据
|
|
|
|
|
setDataSource(data.sort(sortBy(curXfield)));
|
|
|
|
|
// 表格数据
|
|
|
|
|
const sortRowData = cloneDeep(summaryRows).sort(sortBy(defaultValKey)).reverse();
|
|
|
|
|
setPivotTableDataSource(sortRowData);
|
|
|
|
|
// 列汇总
|
|
|
|
|
const sortColData = summaryColumns.sort(sortBy(defaultValKey)).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: 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, // 垂直间距
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
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',
|
|
|
|
|
})),
|
|
|
|
|
})),
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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'} {...{ columns: targetTableProps.columns, dataSource: pivotTableDataSource }} />
|
|
|
|
|
</Divider>
|
|
|
|
|
<Table {...targetTableProps} dataSource={pivotTableDataSource} components={{ body: { cell: TdCell } }} />
|
|
|
|
|
</Spin>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
});
|