|
|
|
import { useContext, useEffect, useState } from 'react';
|
|
|
|
import { observer } from 'mobx-react';
|
|
|
|
import { Row, Col, Spin, Space, Radio, Table } from 'antd';
|
|
|
|
import { CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone } from '@ant-design/icons';
|
|
|
|
import { stores_Context } from '../config';
|
|
|
|
import StatisticCard2 from '../components/StatisticCard2';
|
|
|
|
import Bullet from '../components/BulletWithSort';
|
|
|
|
import Waterfall from '../components/Waterfall';
|
|
|
|
import MixTBWithKPI from './../components/MixTBWithKPI';
|
|
|
|
import Donut from './../components/Donut';
|
|
|
|
import MapCountry from './../components/MapCountry';
|
|
|
|
import DataFieldRadio from '../components/DataFieldRadio';
|
|
|
|
import { datePartOptions } from './../components/DateGroupRadio/date';
|
|
|
|
import SearchForm from './../components/search/SearchForm';
|
|
|
|
import { empty, cloneDeep, isEmpty } from './../utils/commons';
|
|
|
|
import { dataFieldAlias } from './../libs/ht';
|
|
|
|
import './home.css';
|
|
|
|
|
|
|
|
const topSeries = [
|
|
|
|
{ key: 'dept', label: '小组', graphVisible: true },
|
|
|
|
{ key: 'operator', label: '顾问', graphVisible: true },
|
|
|
|
{ key: 'destination', label: '目的地', graphVisible: true },
|
|
|
|
{ key: 'GuestGroupType', label: '客群类别', graphVisible: false },
|
|
|
|
{ key: 'country', label: '国籍', graphVisible: true },
|
|
|
|
];
|
|
|
|
|
|
|
|
// const iconSets = [CheckCircleTwoTone, <MoneyCollectTwoTone />, <FlagTwoTone />, <ClockCircleTwoTone />, <DashboardTwoTone />,<SmileTwoTone />,];
|
|
|
|
const iconSets = [CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone];
|
|
|
|
|
|
|
|
export default observer(() => {
|
|
|
|
// const navigate = useNavigate();
|
|
|
|
const { TradeStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
|
|
|
const { sideData, summaryData, BuData, topData, timeData, timeLineKey, targetTableProps } = TradeStore;
|
|
|
|
const { formValues } = searchFormStore;
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (empty(summaryData.dataSource)) {
|
|
|
|
// pageRefresh();
|
|
|
|
}
|
|
|
|
return () => {};
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const [topSeriesSet, setTopSeriesSet] = useState(topSeries);
|
|
|
|
const [overviewFlag, setOverviewFlag] = useState(true);
|
|
|
|
const [groupTypeVal, setGroupTypeVal] = useState('overview');
|
|
|
|
const pageRefresh = (queryData) => {
|
|
|
|
const _overviewFlag = queryData.DepartmentList.toLowerCase() === 'all' || queryData.DepartmentList.toLowerCase().includes(',');
|
|
|
|
const groupType = _overviewFlag ? 'overview' : 'dept';
|
|
|
|
queryData.groupType = groupType;
|
|
|
|
setGroupTypeVal(groupType);
|
|
|
|
TradeStore.resetData();
|
|
|
|
TradeStore.fetchSummaryData(Object.assign({}, queryData, { groupType }));
|
|
|
|
TradeStore.fetchTradeDataByDate(queryData);
|
|
|
|
// // TradeStore.fetchTradeDataByBU(queryData);
|
|
|
|
TradeStore.fetchTradeDataByMonth(queryData);
|
|
|
|
const topSeriesF = _overviewFlag ? topSeries : topSeries.filter((ele) => ele.key !== 'dept');
|
|
|
|
setTopSeriesSet(topSeriesF);
|
|
|
|
setOverviewFlag(_overviewFlag);
|
|
|
|
for (const iterator of topSeriesF) {
|
|
|
|
TradeStore.fetchTradeDataByType(iterator.key, queryData);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const layoutProps = {
|
|
|
|
gutter: { xs: 8, sm: 8, lg: 16 },
|
|
|
|
lg: { span: 6 },
|
|
|
|
sm: { span: 12 },
|
|
|
|
xs: { span: 24 },
|
|
|
|
};
|
|
|
|
|
|
|
|
const layoutProps3 = {
|
|
|
|
gutter: { xs: 8, sm: 8, lg: 16 },
|
|
|
|
lg: { span: 8 },
|
|
|
|
sm: { span: 12 },
|
|
|
|
xs: { span: 24 },
|
|
|
|
};
|
|
|
|
|
|
|
|
const [valueKey, setValueKey] = useState('SumML');
|
|
|
|
const [BulletConfig, setBulletConfig] = useState({
|
|
|
|
measureField: 'SumML', //
|
|
|
|
rangeField: 'SumMLRange', //
|
|
|
|
targetField: 'MLKPIvalue', //
|
|
|
|
xField: 'groupsLabel',
|
|
|
|
});
|
|
|
|
const handleChangeValueKey = (key) => {
|
|
|
|
setValueKey(key);
|
|
|
|
setBulletConfig({
|
|
|
|
measureField: key,
|
|
|
|
rangeField: `${key}Range`,
|
|
|
|
// targetField: `${key}KPI`,
|
|
|
|
targetField: dataFieldAlias[key].nestkey.v,
|
|
|
|
xField: 'groupsLabel',
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const WaterfallConfig = {
|
|
|
|
xField: 'groupDateVal',
|
|
|
|
yField: 'SumML',
|
|
|
|
seriesField: 'groupsLabel',
|
|
|
|
meta: {
|
|
|
|
groupDateVal: {
|
|
|
|
alias: '月份',
|
|
|
|
// type: 'cat',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
label: {
|
|
|
|
formatter: (v) => ((summaryData.kpi?.value || 0) === 0 ? dataFieldAlias.SumML?.formatter(v.SumML) || v.SumML : ((v.SumML / summaryData.kpi.value) * 100).toFixed(2) + '%'),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const BUConfig = {
|
|
|
|
measureField: 'SumML', //
|
|
|
|
rangeField: 'SumMLRange', //
|
|
|
|
targetField: 'MLKPIvalue', //
|
|
|
|
xField: 'groupsLabel',
|
|
|
|
layout: 'vertical',
|
|
|
|
xAxis: {
|
|
|
|
line: null,
|
|
|
|
label: {
|
|
|
|
autoHide: true,
|
|
|
|
autoRotate: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
legend: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
const lineConfigSet = {
|
|
|
|
// data: orders_data.data,
|
|
|
|
padding: 'auto',
|
|
|
|
xField: 'groupDateVal',
|
|
|
|
yField: 'SumML',
|
|
|
|
seriesField: 'groupsLabel',
|
|
|
|
xAxis: {
|
|
|
|
type: 'cat',
|
|
|
|
},
|
|
|
|
// smooth: true,
|
|
|
|
point: {
|
|
|
|
size: 4,
|
|
|
|
shape: 'cicle',
|
|
|
|
},
|
|
|
|
legend: false,
|
|
|
|
meta: {
|
|
|
|
...cloneDeep(dataFieldAlias),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const [timeDataField, setTimeDataField] = useState('SumML');
|
|
|
|
const [lineConfig, setLineConfig] = useState(cloneDeep(lineConfigSet));
|
|
|
|
const handleChangetimeDataField = (key) => {
|
|
|
|
setTimeDataField(key);
|
|
|
|
setLineConfig({
|
|
|
|
...cloneDeep(lineConfig),
|
|
|
|
yField: key,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
const [dateField, setDateField] = useState(timeLineKey);
|
|
|
|
const handleChangeDateType = ({ target: { value } }) => {
|
|
|
|
setDateField(value);
|
|
|
|
TradeStore.setTimeLineKey(value);
|
|
|
|
if (!isEmpty(TradeStore.searchPayloadHome)) {
|
|
|
|
TradeStore.fetchTradeDataByDate({ groupType: groupTypeVal });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Row gutter={16} className='sticky-top' >
|
|
|
|
<Col className="gutter-row" span={24}>
|
|
|
|
<SearchForm
|
|
|
|
defaultValue={{
|
|
|
|
initialValue: {
|
|
|
|
...formValues,
|
|
|
|
},
|
|
|
|
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'years'],
|
|
|
|
fieldProps: {
|
|
|
|
DepartmentList: { show_all: true },
|
|
|
|
WebCode: { show_all: true },
|
|
|
|
years: { hide_vs: true },
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
onSubmit={(_err, obj, form, str) => {
|
|
|
|
TradeStore.setStateSearch(obj);
|
|
|
|
pageRefresh(obj);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
<section>
|
|
|
|
<Space>
|
|
|
|
<h2>年度业绩<span style={{fontSize: 'small'}}> =传统+商务</span></h2>
|
|
|
|
</Space>
|
|
|
|
<Spin spinning={summaryData.loading}>
|
|
|
|
<Row gutter={layoutProps.gutter}>
|
|
|
|
{/* {summaryData.dataSource.map((item, i) => (
|
|
|
|
<Col {...layoutProps} key={item.title}>
|
|
|
|
<StatisticCard {...item} showProgress={item.hasKPI} icon={iconSets[i]} />
|
|
|
|
</Col>
|
|
|
|
))} */}
|
|
|
|
{summaryData.dataSource.map((item, i) => (
|
|
|
|
<Col {...layoutProps} key={item.title} lg={{span: ((item?.col || layoutProps.lg.span))}}>
|
|
|
|
<StatisticCard2 {...item} showProgress={item.hasKPI} icon={iconSets[i]} />
|
|
|
|
</Col>
|
|
|
|
))}
|
|
|
|
</Row>
|
|
|
|
</Spin>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<Space gutter={16} size={'large'}>
|
|
|
|
<h3>走势</h3>
|
|
|
|
<DataFieldRadio value={timeDataField} onChange={handleChangetimeDataField} />
|
|
|
|
<Radio.Group options={datePartOptions} optionType="button" onChange={handleChangeDateType} value={dateField} />
|
|
|
|
</Space>
|
|
|
|
<Spin spinning={timeData.loading}>
|
|
|
|
{/* <LineWithKPI dataSource={timeData.dataSource} {...lineConfig} /> */}
|
|
|
|
<MixTBWithKPI dataSource={timeData.dataSource} summaryData={timeData.origin?.summaryRows || []} {...lineConfig} />
|
|
|
|
</Spin>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<h3>市场 (仅传统订单) </h3>
|
|
|
|
<Spin spinning={BuData.loading}>
|
|
|
|
<Row gutter={layoutProps3.gutter}>
|
|
|
|
<Col {...layoutProps3}>
|
|
|
|
<><Donut {...{angleField: 'SumML', colorField: 'groupsLabel'}} title={formValues.DepartmentList?.label} dataSource={sideData.yearData} /></>
|
|
|
|
{/* {overviewFlag ? (
|
|
|
|
<>
|
|
|
|
<Bullet {...BUConfig} dataSource={BuData?.dataSource || []} />
|
|
|
|
<h3 style={{ textAlign: 'center' }}>{`各事业部总业绩`}</h3>
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<><Donut {...{angleField: 'SumML', colorField: 'groupsLabel'}} title={formValues.DepartmentList?.label} dataSource={sideData.yearData} /></>
|
|
|
|
)} */}
|
|
|
|
</Col>
|
|
|
|
{Object.keys(sideData.dataSource).sort().map((key) => (
|
|
|
|
<Col {...layoutProps3} key={key}>
|
|
|
|
<Waterfall key={key} {...WaterfallConfig} title={key} dataSource={sideData.dataSource[key]} line={summaryData.kpi} />
|
|
|
|
<h3 style={{ textAlign: 'center' }}>{`${key}每月业绩`}</h3>
|
|
|
|
</Col>
|
|
|
|
))}
|
|
|
|
</Row>
|
|
|
|
</Spin>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<h3>
|
|
|
|
英语区目标客户
|
|
|
|
<Spin spinning={topData?.GuestGroupType?.loading || false}>
|
|
|
|
<Table {...targetTableProps} pagination={false} />
|
|
|
|
</Spin>
|
|
|
|
</h3>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<Space>
|
|
|
|
<h3>TOP</h3>
|
|
|
|
<div>
|
|
|
|
<DataFieldRadio value={valueKey} onChange={handleChangeValueKey} />
|
|
|
|
</div>
|
|
|
|
</Space>
|
|
|
|
<Row gutter={layoutProps3.gutter}>
|
|
|
|
{topSeriesSet.map((item) =>
|
|
|
|
item.graphVisible ? (
|
|
|
|
<Col {...layoutProps3} key={item.key}>
|
|
|
|
<Spin spinning={topData[item.key]?.loading || false}>
|
|
|
|
<h3 style={{ textAlign: 'center' }}>{item.label}</h3>
|
|
|
|
<Bullet {...BulletConfig} dataSource={topData[item.key]?.dataSource || []} itemLength={10} key={item.key} />
|
|
|
|
</Spin>
|
|
|
|
</Col>
|
|
|
|
) : null
|
|
|
|
)}
|
|
|
|
<Col key={'mapG'} flex={'1 0 auto'}>
|
|
|
|
<Spin spinning={topData?.country?.loading || false}>
|
|
|
|
<div id='topC' style={{height: '700px'}}>
|
|
|
|
<MapCountry sourceField={'groupsLabel'} valueField={BUConfig.measureField} dataSource={topData?.country?.dataSource || []} />
|
|
|
|
</div>
|
|
|
|
</Spin>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
</section>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
});
|