|
|
@ -2,45 +2,37 @@ import { useContext, useEffect, useState } from 'react';
|
|
|
|
import { observer } from 'mobx-react';
|
|
|
|
import { observer } from 'mobx-react';
|
|
|
|
import { useParams } from 'react-router-dom';
|
|
|
|
import { useParams } from 'react-router-dom';
|
|
|
|
import { stores_Context } from '../config';
|
|
|
|
import { stores_Context } from '../config';
|
|
|
|
import { Row, Col, Spin, Table, Select, Typography, Card, Button } from 'antd';
|
|
|
|
import { Row, Col, Spin, Table, Select, Typography, Card, Button, Space, Divider } from 'antd';
|
|
|
|
import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique } from '../utils/commons';
|
|
|
|
import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique, cartesianProductArray } from '../utils/commons';
|
|
|
|
import { dataFieldAlias, pivotBy } from '../libs/ht';
|
|
|
|
import { dataFieldAlias, pivotBy } from '../libs/ht';
|
|
|
|
import SearchForm from '../components/search/SearchForm';
|
|
|
|
import SearchForm from '../components/search/SearchForm';
|
|
|
|
import { Line } from '@ant-design/plots';
|
|
|
|
import { Line } from '@ant-design/plots';
|
|
|
|
import DateGroupRadio from '../components/DateGroupRadio';
|
|
|
|
import DateGroupRadio from '../components/DateGroupRadio';
|
|
|
|
|
|
|
|
import { TableExportBtn } from './../components/Data';
|
|
|
|
|
|
|
|
|
|
|
|
const { Text } = Typography;
|
|
|
|
const { Text } = Typography;
|
|
|
|
|
|
|
|
|
|
|
|
const filterFields = [
|
|
|
|
const filterFields = [
|
|
|
|
{ key: 'country', label: '国籍' },
|
|
|
|
|
|
|
|
{ key: 'SourceType', label: '来源类型' },
|
|
|
|
{ key: 'SourceType', label: '来源类型' },
|
|
|
|
{ key: 'productType', label: '产品类型' },
|
|
|
|
{ key: 'productType', label: '产品类型' },
|
|
|
|
|
|
|
|
{ key: 'country', label: '国籍' },
|
|
|
|
|
|
|
|
{ key: 'CLI_NO', label: '线路' },
|
|
|
|
|
|
|
|
// { key: 'destination', label: '目的地' },
|
|
|
|
|
|
|
|
{ key: 'COLI_LineClass', label: '页面类型' },
|
|
|
|
{ key: 'guestGroupType', label: '客群类别' },
|
|
|
|
{ key: 'guestGroupType', label: '客群类别' },
|
|
|
|
{ key: 'travelMotivation', label: '出行动机' },
|
|
|
|
{ key: 'travelMotivation', label: '出行动机' },
|
|
|
|
{ key: 'operatorName', label: '顾问' },
|
|
|
|
// { key: 'operatorName', label: '顾问' },
|
|
|
|
{ key: 'WebCode', label: '来源站点' },
|
|
|
|
// { key: 'WebCode', label: '来源站点' },
|
|
|
|
// todo: 目的地, 目的地国家, 页面类型[PPC, NL...]
|
|
|
|
// todo: 目的地, 目的地国家, 页面类型LineClass[PPC, NL...], 线路line,
|
|
|
|
];
|
|
|
|
];
|
|
|
|
const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {});
|
|
|
|
const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {});
|
|
|
|
const quickOptions = [
|
|
|
|
const quickOptions = [
|
|
|
|
{ label: '国籍×产品', fields: [['country'], ['productType']] },
|
|
|
|
// { label: '国籍×产品', fields: [['country'], ['productType']] },
|
|
|
|
{ label: '产品×客群', fields: [['productType'], ['guestGroupType']] },
|
|
|
|
{ label: '[ 产品×客群 ]×[ ]', fields: [['productType', 'guestGroupType'], []] },
|
|
|
|
|
|
|
|
{ label: '[ 国籍×客群 ]×[ ]', fields: [['country', 'guestGroupType'], []] },
|
|
|
|
|
|
|
|
// { label: '[ 国籍×客群 ]×[ ]', fields: [['country', '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作用域外声明
|
|
|
|
// 注意TdCell要提到DataTable作用域外声明
|
|
|
|
const TdCell = (tdprops) => {
|
|
|
|
const TdCell = (tdprops) => {
|
|
|
|
// onMouseEnter, onMouseLeave在数据量多的时候,会严重阻塞表格单元格渲染,严重影响性能
|
|
|
|
// onMouseEnter, onMouseLeave在数据量多的时候,会严重阻塞表格单元格渲染,严重影响性能
|
|
|
@ -151,21 +143,36 @@ export default observer((props) => {
|
|
|
|
|
|
|
|
|
|
|
|
// 透视配置:行列选项
|
|
|
|
// 透视配置:行列选项
|
|
|
|
// const [leftFields, setLeftFields] = useState(filterFields);
|
|
|
|
// const [leftFields, setLeftFields] = useState(filterFields);
|
|
|
|
const [rightFields, setRightFields] = useState(filterFields);
|
|
|
|
const [rightFields, setRightFields] = useState(filterFields); // select 的option
|
|
|
|
const [rowFields, setRowFields] = useState([]);
|
|
|
|
const [rowFields, setRowFields] = useState([]);
|
|
|
|
const [columnFields, setColumnFields] = useState([]);
|
|
|
|
const [columnFields, setColumnFields] = useState([]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 预设的选项
|
|
|
|
const [rowSelection, setRowSelection] = useState();
|
|
|
|
const [rowSelection, setRowSelection] = useState();
|
|
|
|
const [columnSelection, setColumnSelection] = useState();
|
|
|
|
const [columnSelection, setColumnSelection] = useState();
|
|
|
|
const quickOpt = (i) => {
|
|
|
|
const quickOpt = (i) => {
|
|
|
|
const { fields: pivotFields } = quickOptions[i];
|
|
|
|
const { fields: pivotFields } = quickOptions[i];
|
|
|
|
const [row, col] = pivotFields;
|
|
|
|
const [row, col] = pivotFields;
|
|
|
|
setRowSelection(Object.values(pick(filterFieldsMapped, row)));
|
|
|
|
setRowSelection(Object.values(pick(filterFieldsMapped, row)));
|
|
|
|
setColumnSelection(filterFieldsMapped[col[0]]);
|
|
|
|
!isEmpty(col) ? setColumnSelection(filterFieldsMapped[col[0]]) : setColumnSelection([]);
|
|
|
|
|
|
|
|
setRowFields(row);
|
|
|
|
|
|
|
|
resetItemFilter();
|
|
|
|
|
|
|
|
|
|
|
|
setPivotDateColumns(pivotFields);
|
|
|
|
setPivotDateColumns(pivotFields);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const resetFields = () => {
|
|
|
|
|
|
|
|
// setRowFields([]);
|
|
|
|
|
|
|
|
// setColumnFields([]);
|
|
|
|
|
|
|
|
setRowSelection([]);
|
|
|
|
|
|
|
|
setColumnSelection([]);
|
|
|
|
|
|
|
|
resetItemFilter();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setPivotDateColumns([[], []]);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleRowsPick = (v) => {
|
|
|
|
const handleRowsPick = (v) => {
|
|
|
|
|
|
|
|
setRowSelection(v);
|
|
|
|
const pickKeys = v.map((ele) => ele.key);
|
|
|
|
const pickKeys = v.map((ele) => ele.key);
|
|
|
|
setRowFields(pickKeys);
|
|
|
|
setRowFields(pickKeys);
|
|
|
|
// const leftFieldsMapped = leftFields.reduce((r, v) => ({ ...r, [v.key]: v }), {});
|
|
|
|
// const leftFieldsMapped = leftFields.reduce((r, v) => ({ ...r, [v.key]: v }), {});
|
|
|
@ -177,6 +184,7 @@ export default observer((props) => {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleColsPick = (val) => {
|
|
|
|
const handleColsPick = (val) => {
|
|
|
|
|
|
|
|
setColumnSelection(val);
|
|
|
|
const pickKeys = isEmpty(val) ? [] : Array.isArray(val) ? val.map((ele) => ele.key) : [val.key];
|
|
|
|
const pickKeys = isEmpty(val) ? [] : Array.isArray(val) ? val.map((ele) => ele.key) : [val.key];
|
|
|
|
setColumnFields(pickKeys);
|
|
|
|
setColumnFields(pickKeys);
|
|
|
|
const afterLeft = Object.values(omit(filterFieldsMapped, rowFields));
|
|
|
|
const afterLeft = Object.values(omit(filterFieldsMapped, rowFields));
|
|
|
@ -323,11 +331,19 @@ export default observer((props) => {
|
|
|
|
<Card
|
|
|
|
<Card
|
|
|
|
size={'small'}
|
|
|
|
size={'small'}
|
|
|
|
title={'透视选项'}
|
|
|
|
title={'透视选项'}
|
|
|
|
// extra={quickOptions.map((ele, elei) => (
|
|
|
|
extra={
|
|
|
|
// <Button key={ele.label} type="link" onClick={() => quickOpt(elei)}>
|
|
|
|
<Space>
|
|
|
|
// {ele.label}
|
|
|
|
<Text type={'secondary'}>预设:</Text>
|
|
|
|
// </Button>
|
|
|
|
{quickOptions.map((ele, elei) => (
|
|
|
|
// ))}
|
|
|
|
<Button key={ele.label} onClick={() => quickOpt(elei)} type={'primary'} ghost size={'small'}>
|
|
|
|
|
|
|
|
{ele.label}
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
<Button key={'reset-quick'} onClick={resetFields} type={'primary'} ghost size={'small'}>
|
|
|
|
|
|
|
|
重置
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
|
|
}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<Row gutter={16}>
|
|
|
|
<Row gutter={16}>
|
|
|
|
<Col span={24}>
|
|
|
|
<Col span={24}>
|
|
|
@ -352,8 +368,7 @@ export default observer((props) => {
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
placeholder={`选择`}
|
|
|
|
placeholder={`选择`}
|
|
|
|
onChange={(v) => handleRowsPick(v)}
|
|
|
|
onChange={(v) => handleRowsPick(v)}
|
|
|
|
// value={sale_store.salesTrade.pickSales}
|
|
|
|
value={rowSelection}
|
|
|
|
// value={rowSelection}
|
|
|
|
|
|
|
|
maxTagCount={2}
|
|
|
|
maxTagCount={2}
|
|
|
|
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
|
|
|
|
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
|
|
|
|
allowClear={true}
|
|
|
|
allowClear={true}
|
|
|
@ -413,8 +428,7 @@ export default observer((props) => {
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
placeholder={`选择`}
|
|
|
|
placeholder={`选择`}
|
|
|
|
onChange={(v) => handleColsPick(v)}
|
|
|
|
onChange={(v) => handleColsPick(v)}
|
|
|
|
// value={sale_store.salesTrade.pickSales}
|
|
|
|
value={columnSelection}
|
|
|
|
// value={columnSelection}
|
|
|
|
|
|
|
|
maxTagCount={2}
|
|
|
|
maxTagCount={2}
|
|
|
|
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
|
|
|
|
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
|
|
|
|
allowClear={true}
|
|
|
|
allowClear={true}
|
|
|
@ -494,6 +508,9 @@ export default observer((props) => {
|
|
|
|
<h3>
|
|
|
|
<h3>
|
|
|
|
透视汇总表: <span style={{ fontSize: 'smaller' }}>{dataFieldAlias[lineConfig.yField].label}</span>
|
|
|
|
透视汇总表: <span style={{ fontSize: 'smaller' }}>{dataFieldAlias[lineConfig.yField].label}</span>
|
|
|
|
</h3>
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
<Divider orientation="right">
|
|
|
|
|
|
|
|
<TableExportBtn label={'pivot'} {...{ columns: targetTableProps.columns, dataSource: pivotTableDataSource }} />
|
|
|
|
|
|
|
|
</Divider>
|
|
|
|
<Table {...targetTableProps} dataSource={pivotTableDataSource} components={{ body: { cell: TdCell } }} />
|
|
|
|
<Table {...targetTableProps} dataSource={pivotTableDataSource} components={{ body: { cell: TdCell } }} />
|
|
|
|
</Spin>
|
|
|
|
</Spin>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|