perf: 三峡,酒店: 合计

main
Lei OT 2 months ago
parent 3b51ae8d08
commit f532f138ff

@ -30,20 +30,24 @@ export const VSTag = (props) => {
/** /**
* 导出表格数据存为xlsx * 导出表格数据存为xlsx
* @property label 文件名字
* @property columns 表格列
* @property dataSource 表格数据
* @property btnTxt 按钮文字
*/ */
export const TableExportBtn = (props) => { export const TableExportBtn = ({label, columns, dataSource, btnTxt, ...props}) => {
const output_name = `${props.label}`; const output_name = `${label}`;
const [columnsMap, setColumnsMap] = useState([]); const [columnsMap, setColumnsMap] = useState([]);
const [summaryRow, setSummaryRow] = useState({}); const [summaryRow, setSummaryRow] = useState({});
useEffect(() => { useEffect(() => {
const r1 = props.columns.reduce((r, v) => ({ const r1 = columns.reduce((r, v) => ({
...r, ...r,
...(v.children ? v.children.reduce((rc, vc, ci) => ({ ...(v.children ? v.children.reduce((rc, vc, ci) => ({
...rc, ...rc,
...(vc?.titleX ? {[`${v?.titleX || v.title},${vc.titleX}`]: vc.titleX } : {[(v?.titleX || v.title) + (ci || '')]: `${vc?.titleX || vc?.title || ''}`}), ...(vc?.titleX ? {[`${v?.titleX || v.title},${vc.titleX}`]: vc.titleX } : {[(v?.titleX || v.title) + (ci || '')]: `${vc?.titleX || vc?.title || ''}`}),
}), {}) : {}) }), {}) : {})
}), {}); }), {});
const flatCols = props.columns.flatMap((v, k) => const flatCols = columns.flatMap((v, k) =>
v.children ? v.children.map((vc, ci) => ({ ...vc, title: `${v?.titleX || v.title}` + (vc?.titleX ? `,${vc.titleX}` : (ci || '')) })) : {...v, title: `${v?.titleX || v.title}`} v.children ? v.children.map((vc, ci) => ({ ...vc, title: `${v?.titleX || v.title}` + (vc?.titleX ? `,${vc.titleX}` : (ci || '')) })) : {...v, title: `${v?.titleX || v.title}`}
); );
// .filter((c) => c.dataIndex) // .filter((c) => c.dataIndex)
@ -56,14 +60,14 @@ export const TableExportBtn = (props) => {
// console.log('summaryRow', r1); // console.log('summaryRow', r1);
return () => {}; return () => {};
}, [props.columns]); }, [columns]);
const onExport = () => { const onExport = () => {
if (isEmpty(props.dataSource)) { if (isEmpty(dataSource)) {
message.warning('无结果.'); message.warning('无结果.');
return false; return false;
} }
const data = props.dataSource.map((item) => { const data = dataSource.map((item) => {
const itemMapped = columnsMap.reduce((sv, kset) => { const itemMapped = columnsMap.reduce((sv, kset) => {
const render_val = typeof kset?.render === 'function' ? kset.render('', item) : null; const render_val = typeof kset?.render === 'function' ? kset.render('', item) : null;
const data_val = kset?.dataIndex ? (Array.isArray(kset.dataIndex) ? getNestedValue(item, kset.dataIndex) : item[kset.dataIndex]) : undefined; const data_val = kset?.dataIndex ? (Array.isArray(kset.dataIndex) ? getNestedValue(item, kset.dataIndex) : item[kset.dataIndex]) : undefined;
@ -88,7 +92,7 @@ export const TableExportBtn = (props) => {
disabled={false} disabled={false}
onClick={onExport} onClick={onExport}
> >
{props.btnTxt || '导出excel'} {btnTxt || '导出excel'}
</Button> </Button>
); );
}; };

@ -84,10 +84,7 @@ class HotelCruise {
this.cruise.loading = true; this.cruise.loading = true;
this.cruise.dataSource = []; this.cruise.dataSource = [];
const _queryParam = objectMapper(param, paramKeyMapped); const _queryParam = objectMapper(param, paramKeyMapped);
const queryParam = omit({ ...this.searchValuesToSub, ..._queryParam }, [ const queryParam = omit({ ...this.searchValuesToSub, ..._queryParam }, Object.keys(paramKeyMapped));
'DepartmentList', 'orderStatus', 'keyword', 'Date1', 'Date2', 'DateDiff1', 'DateDiff2',
'cruiseDirection', 'cruiseBookType', 'country', 'agency',
]);
queryParam.Compare = isEmpty(param.DateDiff1) ? '' : '1'; queryParam.Compare = isEmpty(param.DateDiff1) ? '' : '1';
const res = await fetchCruiseData(queryParam); const res = await fetchCruiseData(queryParam);
const resCP = const resCP =
@ -95,13 +92,29 @@ class HotelCruise {
? res ? res
: (res || []).map((ele) => ({ : (res || []).map((ele) => ({
...ele, ...ele,
// 计算 增长率 = (当前值 - 上次值) / 上次值 * 100
TotalNumPercent: ele.CPTotalNum ? fixTo2Decimals(((ele.TotalNum - ele.CPTotalNum) / ele.CPTotalNum) * 100) : '-', TotalNumPercent: ele.CPTotalNum ? fixTo2Decimals(((ele.TotalNum - ele.CPTotalNum) / ele.CPTotalNum) * 100) : '-',
TotalPersonNumPercent: ele.CPTotalPersonNum ? fixTo2Decimals(((ele.TotalPersonNum - ele.CPTotalPersonNum) / ele.CPTotalPersonNum) * 100) : '-', TotalPersonNumPercent: ele.CPTotalPersonNum ? fixTo2Decimals(((ele.TotalPersonNum - ele.CPTotalPersonNum) / ele.CPTotalPersonNum) * 100) : '-',
TotalProfitPercent: ele.CPTotalProfit ? fixTo2Decimals(((ele.TotalProfit - ele.CPTotalProfit) / ele.CPTotalProfit) * 100) : '-', TotalProfitPercent: ele.CPTotalProfit ? fixTo2Decimals(((ele.TotalProfit - ele.CPTotalProfit) / ele.CPTotalProfit) * 100) : '-',
})); }));
const summaryRow = ['TotalNum', 'TotalPersonNum', 'TotalProfit', 'CPTotalNum', 'CPTotalPersonNum', 'CPTotalProfit'].reduce(
(r, skey) => ({
...r,
[skey]: resCP.reduce((a, c) => a + c[skey], 0),
}),
{ ProductName: '合计' }
);
const summaryDelta = ['TotalNum', 'TotalPersonNum', 'TotalProfit'].reduce(
(r, skey) => ({
...r,
[`${skey}Percent`]: queryParam.Compare === '' ? null : fixTo2Decimals(((summaryRow[skey] - summaryRow[`CP${skey}`]) / summaryRow[`CP${skey}`]) * 100),
}),
{}
);
runInAction(() => { runInAction(() => {
this.cruise.loading = false; this.cruise.loading = false;
this.cruise.dataSource = resCP; this.cruise.dataSource = resCP;
this.cruise.summaryRow = { ...summaryRow, ...summaryDelta };
}); });
return this.cruise; return this.cruise;
} }
@ -110,10 +123,7 @@ class HotelCruise {
this.hotel.loading = true; this.hotel.loading = true;
this.hotel.dataSource = []; this.hotel.dataSource = [];
const _queryParam = objectMapper(param, paramKeyMapped); const _queryParam = objectMapper(param, paramKeyMapped);
const queryParam = omit({ ...this.searchValuesToSub, ..._queryParam }, [ const queryParam = omit({ ...this.searchValuesToSub, ..._queryParam }, Object.keys(paramKeyMapped));
'DepartmentList','orderStatus','keyword','Date1','Date2','DateDiff1','DateDiff2','DateType',
'hotelStar','hotelRecommandRate','hotelBookType','countryArea',
]);
queryParam.Compare = isEmpty(param.DateDiff1) ? '0' : '1'; queryParam.Compare = isEmpty(param.DateDiff1) ? '0' : '1';
const _res = await fetchHotelData(queryParam); const _res = await fetchHotelData(queryParam);
const res = (_res || []).map((ele) => ({ ...ele, RecommendRate_100: fixTo2Decimals(ele.RecommendRate * 100) + '%' })); const res = (_res || []).map((ele) => ({ ...ele, RecommendRate_100: fixTo2Decimals(ele.RecommendRate * 100) + '%' }));
@ -127,9 +137,30 @@ class HotelCruise {
RecommendRateDelta: fixTo2Decimals((ele.RecommendRate - (ele.CPRecommendRate || 0)) * 100), RecommendRateDelta: fixTo2Decimals((ele.RecommendRate - (ele.CPRecommendRate || 0)) * 100),
CPRecommendRate_100: fixTo2Decimals(ele.CPRecommendRate * 100) + '%', CPRecommendRate_100: fixTo2Decimals(ele.CPRecommendRate * 100) + '%',
})); }));
const summaryRow = ['TotalNum', 'RecomendNum', ].reduce(
(r, skey) => ({
...r,
[skey]: resCP.reduce((a, c) => a + c[skey], 0),
[`CP${skey}`]: resCP.reduce((a, c) => a + c[`CP${skey}`], 0),
}),
{ CityName: '合计' }
);
summaryRow.RecommendRate = fixTo2Decimals(summaryRow.RecomendNum/summaryRow.TotalNum);
summaryRow.RecommendRate_100 = fixTo2Decimals(summaryRow.RecommendRate * 100) + '%';
summaryRow.CPRecommendRate = fixTo2Decimals(summaryRow.CPRecomendNum/summaryRow.CPTotalNum);
summaryRow.CPRecommendRate_100 = fixTo2Decimals(summaryRow.CPRecommendRate * 100) + '%';
const summaryDelta = ['TotalNum', 'RecomendNum', ].reduce(
(r, skey) => ({
...r,
[`${skey}Percent`]: queryParam.Compare === '0' ? null : fixTo2Decimals(((summaryRow[skey] - summaryRow[`CP${skey}`]) / summaryRow[`CP${skey}`]) * 100),
}),
{}
);
summaryDelta.RecommendRateDelta = queryParam.Compare === '0' ? undefined : fixTo2Decimals((summaryRow.RecommendRate - (summaryRow.CPRecommendRate || 0)) * 100);
runInAction(() => { runInAction(() => {
this.hotel.loading = false; this.hotel.loading = false;
this.hotel.dataSource = resCP; this.hotel.dataSource = resCP;
this.hotel.summaryRow = { ...summaryRow, ...summaryDelta };
}); });
} }
@ -150,8 +181,8 @@ class HotelCruise {
this.searchValuesToSub = obj; this.searchValuesToSub = obj;
} }
cruise = { loading: false, dataSource: [] }; cruise = { loading: false, dataSource: [], summaryRow: {} };
hotel = { loading: false, dataSource: [] }; hotel = { loading: false, dataSource: [], summaryRow: {} };
resetData = () => { resetData = () => {
this.results.loading = false; this.results.loading = false;
for (const key of Object.keys(this.results)) { for (const key of Object.keys(this.results)) {

@ -1,32 +1,45 @@
import React, { Children, useContext, useState } from 'react'; import React, { useContext } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { stores_Context } from '../config'; import { stores_Context } from '../config';
import moment from 'moment'; import { Row, Col, Table, Space, Typography } from 'antd';
import { Row, Col, Table, Select, Space, Typography, Progress, Spin, Divider, Button, Switch } from 'antd';
import SearchForm from './../components/search/SearchForm'; import SearchForm from './../components/search/SearchForm';
import { VSTag, TableExportBtn } from './../components/Data'; import { VSTag } from './../components/Data';
import { CruiseAgency } from './../libs/ht'; import { CruiseAgency } from './../libs/ht';
const {Text} = Typography; const { Text } = Typography;
export default observer((props) => { export default observer((props) => {
const { sale_store, date_picker_store: searchFormStore } = useContext(stores_Context); const { date_picker_store: searchFormStore } = useContext(stores_Context);
const { customerServicesStore, HotelCruiseStore, date_picker_store } = useContext(stores_Context); const { HotelCruiseStore, date_picker_store } = useContext(stores_Context);
const { loading, dataSource } = HotelCruiseStore.cruise; const { loading, dataSource, summaryRow } = HotelCruiseStore.cruise;
const { formValues, siderBroken } = searchFormStore; const { formValues, siderBroken } = searchFormStore;
const tableSorter = (a, b, colName) => (a[colName] - b[colName]); const tableSorter = (a, b, colName) => a[colName] - b[colName];
const tableProps = { const tableProps = {
size: 'small', size: 'small',
bordered: true, pagination: false, bordered: true,
pagination: false,
columns: [ columns: [
{ title: '产品', dataIndex: 'ProductName', key: 'ProductName' }, { title: '产品', children: [{ title: summaryRow.ProductName, dataIndex: 'ProductName', key: 'ProductName' }] },
{ {
title: '房间数', title: '房间数',
sorter: (a, b) => tableSorter(a, b, 'TotalNum'),
children: [
{
title: (
<>
<Space direction={'vertical'}>
<Text strong>
{summaryRow.TotalNum}
{summaryRow.TotalNumPercent ? <Text type="secondary"> VS {summaryRow.CPTotalNum}</Text> : null}
</Text>
{summaryRow.TotalNumPercent && <VSTag diffPercent={summaryRow.TotalNumPercent} />}
</Space>
</>
),
dataIndex: 'TotalNum', dataIndex: 'TotalNum',
key: 'TotalNum', key: 'TotalNum',
sorter: (a, b) => tableSorter(a, b, 'TotalNum'),
render: (v, r) => ( render: (v, r) => (
<> <>
<Space direction={'vertical'}> <Space direction={'vertical'}>
@ -39,8 +52,26 @@ export default observer((props) => {
</> </>
), ),
}, },
{ title: '人数', dataIndex: 'TotalPersonNum', key: 'TotalPersonNum', ],
},
{
title: '人数',
sorter: (a, b) => tableSorter(a, b, 'TotalPersonNum'), sorter: (a, b) => tableSorter(a, b, 'TotalPersonNum'),
children: [
{
title: (
<>
<Space direction={'vertical'}>
<Text strong>
{summaryRow.TotalPersonNum}
{summaryRow.TotalPersonNumPercent ? <Text type="secondary"> VS {summaryRow.CPTotalPersonNum}</Text> : null}
</Text>
{summaryRow.TotalPersonNumPercent && <VSTag diffPercent={summaryRow.TotalPersonNumPercent} />}
</Space>
</>
),
dataIndex: 'TotalPersonNum',
key: 'TotalPersonNum',
render: (v, r) => ( render: (v, r) => (
<> <>
<Space direction={'vertical'}> <Space direction={'vertical'}>
@ -51,9 +82,28 @@ export default observer((props) => {
{r.CPTotalNum && <VSTag diffPercent={r.TotalPersonNumPercent} />} {r.CPTotalNum && <VSTag diffPercent={r.TotalPersonNumPercent} />}
</Space> </Space>
</> </>
),}, ),
{ title: '总利润', dataIndex: 'TotalProfit', key: 'TotalProfit', },
],
},
{
title: '总利润',
sorter: (a, b) => tableSorter(a, b, 'TotalProfit'), sorter: (a, b) => tableSorter(a, b, 'TotalProfit'),
children: [
{
title: (
<>
<Space direction={'vertical'}>
<Text strong>
{summaryRow.TotalProfit}
{summaryRow.TotalProfitPercent ? <Text type="secondary"> VS {summaryRow.CPTotalProfit}</Text> : null}
</Text>
{summaryRow.TotalProfitPercent && <VSTag diffPercent={summaryRow.TotalProfitPercent} />}
</Space>
</>
),
dataIndex: 'TotalProfit',
key: 'TotalProfit',
render: (v, r) => ( render: (v, r) => (
<> <>
<Space direction={'vertical'}> <Space direction={'vertical'}>
@ -64,7 +114,10 @@ export default observer((props) => {
{r.CPTotalNum && <VSTag diffPercent={r.TotalProfitPercent} />} {r.CPTotalNum && <VSTag diffPercent={r.TotalProfitPercent} />}
</Space> </Space>
</> </>
), }, ),
},
],
},
// { title: '', dataIndex: '', key: '' }, // { title: '', dataIndex: '', key: '' },
// { title: '', dataIndex: '', key: '' }, // { title: '', dataIndex: '', key: '' },
// { title: '', dataIndex: '', key: '' }, // { title: '', dataIndex: '', key: '' },
@ -83,8 +136,14 @@ export default observer((props) => {
}, },
// 'countryArea', // 'countryArea',
shows: [ shows: [
'DepartmentList', 'orderStatus', 'dates', 'keyword', 'cruiseDirection', 'agency', 'DepartmentList',
'cruiseBookType', 'country', // 'roomsRange', 'personRange' 'orderStatus',
'dates',
'keyword',
'cruiseDirection',
'agency',
'cruiseBookType',
'country', // 'roomsRange', 'personRange'
], ],
sort: { keyword: 101, agency: 110, cruiseDirection: 102, country: 104 }, sort: { keyword: 101, agency: 110, cruiseDirection: 102, country: 104 },
fieldProps: { fieldProps: {
@ -104,7 +163,7 @@ export default observer((props) => {
</Col> </Col>
</Row> </Row>
<section> <section>
<Table {...tableProps} {...{loading, dataSource}} rowKey={(record) => record.ProductName} /> <Table {...tableProps} {...{ loading, dataSource }} rowKey={(record) => record.ProductName} />
</section> </section>
</> </>
); );

@ -7,25 +7,38 @@ import { VSTag, TableExportBtn } from './../components/Data';
const { Text } = Typography; const { Text } = Typography;
export default observer((props) => { export default observer((props) => {
const { sale_store, date_picker_store: searchFormStore } = useContext(stores_Context); const { date_picker_store: searchFormStore } = useContext(stores_Context);
const { customerServicesStore, date_picker_store, HotelCruiseStore } = useContext(stores_Context); const { date_picker_store, HotelCruiseStore } = useContext(stores_Context);
const { loading, dataSource } = HotelCruiseStore.hotel; const { loading, dataSource, summaryRow } = HotelCruiseStore.hotel;
const { formValues, siderBroken } = searchFormStore; const { formValues, siderBroken } = searchFormStore;
const tableSorter = (a, b, colName) => (a[colName] - b[colName]); const tableSorter = (a, b, colName) => a[colName] - b[colName];
const tableProps = { const tableProps = {
size: 'small', size: 'small',
bordered: true, bordered: true,
pagination: false, pagination: false,
columns: [ columns: [
{ title: '目的地', dataIndex: 'CityName', key: 'CityName' }, { title: '目的地', children: [{ title: summaryRow.CityName, dataIndex: 'CityName', key: 'CityName' }] },
{ {
title: '总间夜', title: '总间夜',
sorter: (a, b) => tableSorter(a, b, 'TotalNum'),
children: [
{
title: (
<>
<Space direction={'vertical'}>
<Text strong>
{summaryRow.TotalNum}
{summaryRow.TotalNumPercent ? <Text type="secondary"> VS {summaryRow.CPTotalNum}</Text> : null}
</Text>
{summaryRow.TotalNumPercent && <VSTag diffPercent={summaryRow.TotalNumPercent} />}
</Space>
</>
),
dataIndex: 'TotalNum', dataIndex: 'TotalNum',
key: 'TotalNum', key: 'TotalNum',
sorter: (a, b) => tableSorter(a, b, 'TotalNum'),
render: (v, r) => ( render: (v, r) => (
<> <>
<Space direction={'vertical'}> <Space direction={'vertical'}>
@ -38,11 +51,26 @@ export default observer((props) => {
</> </>
), ),
}, },
],
},
{ {
title: '主推', title: '主推',
sorter: (a, b) => tableSorter(a, b, 'RecomendNum'),
children: [
{
title: (
<>
<Space direction={'vertical'}>
<Text strong>
{summaryRow.RecomendNum}
{summaryRow.RecomendNumPercent ? <Text type="secondary"> VS {summaryRow.CPRecomendNum}</Text> : null}
</Text>
{summaryRow.RecomendNumPercent && <VSTag diffPercent={summaryRow.RecomendNumPercent} />}
</Space>
</>
),
dataIndex: 'RecomendNum', dataIndex: 'RecomendNum',
key: 'RecomendNum', key: 'RecomendNum',
sorter: (a, b) => tableSorter(a, b, 'RecomendNum'),
render: (v, r) => ( render: (v, r) => (
<> <>
<Space direction={'vertical'}> <Space direction={'vertical'}>
@ -55,8 +83,23 @@ export default observer((props) => {
</> </>
), ),
}, },
],
},
{ {
title: '使用比例', title: '使用比例',
children: [
{
title: (
<>
<Space direction={'vertical'}>
<Text strong>
{summaryRow.RecommendRate_100}
{summaryRow.RecommendRateDelta ? <Text type="secondary"> VS {summaryRow.CPRecommendRate_100}</Text> : null}
</Text>
{summaryRow.RecommendRateDelta && <VSTag diffPercent={summaryRow.RecommendRateDelta} />}
</Space>
</>
),
dataIndex: 'RecommendRate_100', dataIndex: 'RecommendRate_100',
key: 'RecommendRate_100', key: 'RecommendRate_100',
render: (v, r) => ( render: (v, r) => (
@ -72,6 +115,8 @@ export default observer((props) => {
), ),
}, },
], ],
},
],
}; };
return ( return (

Loading…
Cancel
Save