|
|
|
|
import { useContext, useEffect } from 'react';
|
|
|
|
|
import { observer } from 'mobx-react';
|
|
|
|
|
import { stores_Context } from '../config';
|
|
|
|
|
import { Row, Col, Spin, Tabs, Table, Space, Typography, Divider } from 'antd';
|
|
|
|
|
import { RingProgress } from '@ant-design/plots';
|
|
|
|
|
import SearchForm from './../components/search/SearchForm';
|
|
|
|
|
import { empty } from '../utils/commons';
|
|
|
|
|
import { dataFieldAlias } from '../libs/ht';
|
|
|
|
|
import { VSTag, TableExportBtn } from './../components/Data';
|
|
|
|
|
import MixYnQ from './../components/MixYnQ';
|
|
|
|
|
|
|
|
|
|
import './kpi.css';
|
|
|
|
|
|
|
|
|
|
const { Text } = Typography;
|
|
|
|
|
|
|
|
|
|
const apartOptions = [
|
|
|
|
|
{ key: 'tourDays', value: 'tourDays', label: '团天数' },
|
|
|
|
|
{ key: 'PML', value: 'PML', label: '单团毛利' },
|
|
|
|
|
{ key: 'ConfirmDays', value: 'ConfirmDays', label: '成团周期' },
|
|
|
|
|
{ key: 'ApplyDays', value: 'ApplyDays', label: '预定周期' },
|
|
|
|
|
{ key: 'PersonNum', value: 'PersonNum', label: '人等' },
|
|
|
|
|
{ key: 'destination', value: 'destination', label: '国内目的地', },
|
|
|
|
|
{ key: 'GlobalDestination', value: 'GlobalDestination', label: '海外目的地', },
|
|
|
|
|
{ key: 'destinationCountry', value: 'destinationCountry', label: '目的地国籍', },
|
|
|
|
|
{ key: 'guestCountry', value: 'guestCountry', label: '客人国籍', },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 注意TdCell要提到DataTable作用域外声明
|
|
|
|
|
const TdCell = (tdprops) => {
|
|
|
|
|
// onMouseEnter, onMouseLeave在数据量多的时候,会严重阻塞表格单元格渲染,严重影响性能
|
|
|
|
|
const { onMouseEnter, onMouseLeave, ...restProps } = tdprops;
|
|
|
|
|
return <td {...restProps} />;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default observer(() => {
|
|
|
|
|
const { date_picker_store: searchFormStore, DistributionStore } = useContext(stores_Context);
|
|
|
|
|
const { formValues, formValuesToSub, siderBroken } = searchFormStore;
|
|
|
|
|
const { curTab, dateStringQ, dateStringY } = DistributionStore;
|
|
|
|
|
|
|
|
|
|
const pageRefresh = (obj) => {
|
|
|
|
|
DistributionStore.getApartData({
|
|
|
|
|
...(obj || formValuesToSub),
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
DistributionStore.setFormDates(formValuesToSub);
|
|
|
|
|
DistributionStore.resetData();
|
|
|
|
|
return () => {};
|
|
|
|
|
}, [formValuesToSub]);
|
|
|
|
|
|
|
|
|
|
const onTabsChange = (tab) => {
|
|
|
|
|
DistributionStore.setCurTab(tab);
|
|
|
|
|
if (empty(DistributionStore[tab].dataSource)) {
|
|
|
|
|
pageRefresh();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
const RingProgressConfig = {
|
|
|
|
|
height: 60,
|
|
|
|
|
width: 60,
|
|
|
|
|
autoFit: false,
|
|
|
|
|
color: ['#5B8FF9', '#E8EDF3'],
|
|
|
|
|
};
|
|
|
|
|
const RingProgressConfigY = {
|
|
|
|
|
height: 50,
|
|
|
|
|
width: 50,
|
|
|
|
|
autoFit: false,
|
|
|
|
|
// color: ['#f6bd16', '#E8EDF3'],
|
|
|
|
|
color: ['#61ddaa', '#E8EDF3'], // #7cb305
|
|
|
|
|
innerRadius: 0.90,
|
|
|
|
|
};
|
|
|
|
|
const RingProgressConfigQ = {
|
|
|
|
|
height: 50,
|
|
|
|
|
width: 50,
|
|
|
|
|
autoFit: false,
|
|
|
|
|
color: ['#f6bd16', '#E8EDF3'],
|
|
|
|
|
innerRadius: 0.90,
|
|
|
|
|
};
|
|
|
|
|
const columns = [
|
|
|
|
|
{ title: '#', dataIndex: 'label' },
|
|
|
|
|
{
|
|
|
|
|
title: '预定',
|
|
|
|
|
dataIndex: 'SumOrder',
|
|
|
|
|
render: (v, r) => (
|
|
|
|
|
<>
|
|
|
|
|
<Space direction={'vertical'}>
|
|
|
|
|
<Text strong>{v}</Text>
|
|
|
|
|
<span>
|
|
|
|
|
<span>同比: </span> <VSTag diffPercent={r.SumOrderToY} diffData={r.SumOrderDiffY} />
|
|
|
|
|
</span>
|
|
|
|
|
<span>
|
|
|
|
|
<span>环比: </span> <VSTag diffPercent={r.SumOrderToQ} diffData={r.SumOrderDiffQ} />
|
|
|
|
|
</span>
|
|
|
|
|
</Space>
|
|
|
|
|
</>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '团数',
|
|
|
|
|
dataIndex: 'ConfirmOrder',
|
|
|
|
|
render: (v, r) => (
|
|
|
|
|
<>
|
|
|
|
|
<Space direction={'vertical'}>
|
|
|
|
|
<Text strong>{v}</Text>
|
|
|
|
|
<span>
|
|
|
|
|
<span>同比: </span> <VSTag diffPercent={r.ConfirmOrderToY} diffData={r.ConfirmOrderDiffY} />
|
|
|
|
|
</span>
|
|
|
|
|
<span>
|
|
|
|
|
<span>环比: </span> <VSTag diffPercent={r.ConfirmOrderToQ} diffData={r.ConfirmOrderDiffQ} />
|
|
|
|
|
</span>
|
|
|
|
|
</Space>
|
|
|
|
|
</>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '业绩',
|
|
|
|
|
dataIndex: 'SumML',
|
|
|
|
|
render: (v, r) => (
|
|
|
|
|
<>
|
|
|
|
|
<Space direction={'vertical'}>
|
|
|
|
|
<Text strong>{dataFieldAlias.SumML.formatter(v)}</Text>
|
|
|
|
|
<span>
|
|
|
|
|
<span>同比: </span> <VSTag diffPercent={r.SumMLToY} diffData={dataFieldAlias.SumML.formatter(r.SumMLDiffY)} />
|
|
|
|
|
</span>
|
|
|
|
|
<span>
|
|
|
|
|
<span>环比: </span> <VSTag diffPercent={r.SumMLToQ} diffData={dataFieldAlias.SumML.formatter(r.SumMLDiffQ)} />
|
|
|
|
|
</span>
|
|
|
|
|
</Space>
|
|
|
|
|
</>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '团数占比',
|
|
|
|
|
dataIndex: 'ConfirmOrderPercent',
|
|
|
|
|
render: (v, r) => v ? <RingProgress {...RingProgressConfig} percent={v / 100} /> : '-',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '业绩占比',
|
|
|
|
|
dataIndex: 'SumMLPercent',
|
|
|
|
|
render: (v, r) => v ? <RingProgress {...RingProgressConfig} percent={v / 100} /> : '-',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: () => <><div>去年同期</div><div>{dateStringY}</div></>,
|
|
|
|
|
titleX: ['去年同期', dateStringY], // 给导出按钮用
|
|
|
|
|
align: 'center',
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
title: '团数占比',
|
|
|
|
|
titleX: '团数占比',
|
|
|
|
|
width: 90,
|
|
|
|
|
dataIndex: ['resultToY', 'ConfirmOrderPercent'], // 'ConfirmOrderPercent',
|
|
|
|
|
render: (v, r) => r.resultToY.ConfirmOrderPercent ? <RingProgress {...RingProgressConfigY} percent={r.resultToY.ConfirmOrderPercent / 100} /> : '-',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '业绩占比',
|
|
|
|
|
titleX: '业绩占比',
|
|
|
|
|
width: 90,
|
|
|
|
|
dataIndex: ['resultToY', 'SumMLPercent'], // 'SumMLPercent',
|
|
|
|
|
render: (v, r) => r.resultToY.SumMLPercent ? <RingProgress {...RingProgressConfigY} percent={r.resultToY.SumMLPercent / 100} /> : '-',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: () => <><div>上个时间段</div><div>{dateStringQ}</div></>,
|
|
|
|
|
titleX: ['上个时间段', dateStringY], // 给导出按钮用
|
|
|
|
|
align: 'center',
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
title: '团数占比',
|
|
|
|
|
titleX: '团数占比',
|
|
|
|
|
width: 90,
|
|
|
|
|
dataIndex: ['resultToQ', 'ConfirmOrderPercent'], // 'ConfirmOrderPercent',
|
|
|
|
|
render: (v, r) => r.resultToQ.ConfirmOrderPercent ? <RingProgress {...RingProgressConfigQ} percent={r.resultToQ.ConfirmOrderPercent / 100} /> : '-',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '业绩占比',
|
|
|
|
|
titleX: '业绩占比',
|
|
|
|
|
width: 90,
|
|
|
|
|
dataIndex: ['resultToQ', 'SumMLPercent'], // 'SumMLPercent',
|
|
|
|
|
render: (v, r) => r.resultToQ.SumMLPercent ? <RingProgress {...RingProgressConfigQ} percent={r.resultToQ.SumMLPercent / 100} /> : '-',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
const chartsConfig = {
|
|
|
|
|
xField: 'label',
|
|
|
|
|
yFields: ['ConfirmOrder','SumML'],
|
|
|
|
|
seriesField: null,
|
|
|
|
|
};
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<Row gutter={16} className={siderBroken ? "" : "sticky-top"} >
|
|
|
|
|
<Col className="gutter-row" span={24}>
|
|
|
|
|
<SearchForm
|
|
|
|
|
defaultValue={{
|
|
|
|
|
initialValue: {
|
|
|
|
|
...formValues,
|
|
|
|
|
},
|
|
|
|
|
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates', 'operator'],
|
|
|
|
|
fieldProps: {
|
|
|
|
|
DepartmentList: { show_all: true },
|
|
|
|
|
WebCode: { show_all: true },
|
|
|
|
|
dates: { hide_vs: true },
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
onSubmit={(_err, obj) => {
|
|
|
|
|
pageRefresh(obj);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
<Tabs
|
|
|
|
|
onChange={onTabsChange}
|
|
|
|
|
type="card"
|
|
|
|
|
items={apartOptions.map((ele) => {
|
|
|
|
|
return {
|
|
|
|
|
...ele,
|
|
|
|
|
children: (
|
|
|
|
|
<Spin spinning={DistributionStore.pageLoading}>
|
|
|
|
|
<MixYnQ {...chartsConfig} dataSource={DistributionStore[curTab].dataSource} />
|
|
|
|
|
<Divider orientation="right" plain>
|
|
|
|
|
<TableExportBtn label={`统计分布-${ele.label}`} {...{ columns, dataSource: DistributionStore[curTab].dataSource }} />
|
|
|
|
|
</Divider>
|
|
|
|
|
<Table
|
|
|
|
|
id="table_to_xlsx_sale"
|
|
|
|
|
components={{ body: { cell: TdCell } }}
|
|
|
|
|
dataSource={DistributionStore[curTab].dataSource}
|
|
|
|
|
columns={columns}
|
|
|
|
|
size="small"
|
|
|
|
|
rowKey={(record) => record.label}
|
|
|
|
|
loading={DistributionStore[curTab].loading}
|
|
|
|
|
pagination={false}
|
|
|
|
|
scroll={{ x: '100%' }}
|
|
|
|
|
/>
|
|
|
|
|
</Spin>
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
})}
|
|
|
|
|
/>
|
|
|
|
|
</section>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
});
|