首页 年度 时间轴切换

feature/2.0-sales-trade
Lei OT 2 years ago
parent 33751b3eca
commit 11e0684edf

@ -1,11 +1,11 @@
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Radio, Select } from 'antd'; import { Radio, Select } from 'antd';
import { dataFieldOptions } from './../libs/ht'; import { dataFieldOptions } from '../libs/ht';
export default observer((props) => { export default observer((props) => {
const { visible, dataRaw, dataMapper, fieldMapper, onChange, ...extProps } = props; const { visible, dataRaw, dataMapper, fieldMapper, onChange, ...extProps } = props;
const handleChange = (value) => { const handleChange = (value) => {
console.log('handleChange', value); // console.log('handleChange', value);
if (typeof onChange === 'function') { if (typeof onChange === 'function') {
onChange(value); onChange(value);
} }

@ -4,7 +4,7 @@ export const datePartOptions = [
{ label: '日', value: 'day' }, { label: '日', value: 'day' },
{ label: '周', value: 'week' }, { label: '周', value: 'week' },
{ label: '月', value: 'month' }, { label: '月', value: 'month' },
{ label: '季', value: 'quarter' }, { label: '季', value: 'season' },
{ label: '年', value: 'year' }, { label: '年', value: 'year' },
]; ];
export const datePartMethod = { export const datePartMethod = {
@ -20,7 +20,7 @@ export const datePartMethod = {
const dateKey = `${year}-W${String(week).padStart(2, '0')}`; const dateKey = `${year}-W${String(week).padStart(2, '0')}`;
return { dateKey, 'groupKey': dateKey, date }; return { dateKey, 'groupKey': dateKey, date };
}, },
'quarter': (date) => { 'season': (date) => {
const dateO = moment(date); const dateO = moment(date);
// const key = dateO.format('YYYY-Q'); // const key = dateO.format('YYYY-Q');
const key = `${dateO.year()}-${String(dateO.quarter()).padStart(2, 'Q')}`; const key = `${dateO.year()}-${String(dateO.quarter()).padStart(2, 'Q')}`;

@ -2,6 +2,10 @@ import { Radio } from 'antd';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { datePartOptions, resultDataCb } from './date'; import { datePartOptions, resultDataCb } from './date';
/**
* 仅前端转换数据
* 把按天的数据转换成 按周,,,
*/
export default observer((props) => { export default observer((props) => {
const { visible, dataRaw, dataMapper, fieldMapper, onChange, ...extProps } = props; const { visible, dataRaw, dataMapper, fieldMapper, onChange, ...extProps } = props;
const _dataMapper = dataMapper || { 'data1': null, data2: null }; const _dataMapper = dataMapper || { 'data1': null, data2: null };

@ -1,13 +1,15 @@
import { observer } from "mobx-react"; import { observer } from 'mobx-react';
import { Card, Statistic, Progress } from 'antd'; import { Card, Statistic, Progress } from 'antd';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons'; import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
export default observer((props) => { export default observer((props) => {
const valueStyle = { color: props.VSrate < 0 ? '#3f8600' : '#cf1322' }; const ValueIcon = props.icon;
const VSIcon = () => (props.VSrate < 0 ? <ArrowDownOutlined /> : <ArrowUpOutlined />); const valueStyle = { color: (props?.VSrate || -1) < 0 ? '#3f8600' : '#cf1322' };
const VSIcon = () => ((props?.VSrate || -1) < 0 ? <ArrowDownOutlined /> : <ArrowUpOutlined />);
return ( return (
<Card> <Card>
<Statistic <Statistic
{...props}
className={'__hn-sta-wrapper'} className={'__hn-sta-wrapper'}
valueStyle={valueStyle} valueStyle={valueStyle}
suffix={ suffix={
@ -19,7 +21,8 @@ export default observer((props) => {
</> </>
) )
} }
{...props} prefix={<ValueIcon twoToneColor="#89B67F" />}
// title={<Space><ValueIcon twoToneColor="#89B67F" /><span>{props.title}</span></Space>}
/> />
{props.showProgress !== false && <Progress percent={props.KPIrate} size="small" format={(percent) => `${props.KPIrate}%`} />} {props.showProgress !== false && <Progress percent={props.KPIrate} size="small" format={(percent) => `${props.KPIrate}%`} />}
</Card> </Card>

@ -28,7 +28,8 @@ class Trade {
value: summary?.ConfirmOrder, value: summary?.ConfirmOrder,
// VSrate: summary?.ConfirmOrderrate, // VSrate: summary?.ConfirmOrderrate,
KPIrate: summary?.[dataFieldAlias.ConfirmOrder.nestkey.p], KPIrate: summary?.[dataFieldAlias.ConfirmOrder.nestkey.p],
hasKPI: !isEmpty(summary?.[dataFieldAlias.ConfirmOrder.nestkey.p]), // hasKPI: !isEmpty(summary?.[dataFieldAlias.ConfirmOrder.nestkey.p]),
hasKPI: false
}, },
{ title: '毛利', value: summary?.SumML, KPIrate: summary?.[dataFieldAlias.SumML.nestkey.p], hasKPI: false }, { title: '毛利', value: summary?.SumML, KPIrate: summary?.[dataFieldAlias.SumML.nestkey.p], hasKPI: false },
{ title: '完成率', value: `${summary?.[dataFieldAlias.SumML.nestkey.p] || ''}%`, hasKPI: false }, { title: '完成率', value: `${summary?.[dataFieldAlias.SumML.nestkey.p] || ''}%`, hasKPI: false },
@ -54,7 +55,8 @@ class Trade {
*/ */
fetchTradeDataByDate(queryData) { fetchTradeDataByDate(queryData) {
this.timeData.loading = true; this.timeData.loading = true;
Object.assign(queryData, { groupType: 'overview', groupDateType: 'week' }); queryData = queryData || this.searchPayloadHome;
Object.assign(queryData, { groupType: 'overview', groupDateType: this.timeLineKey });
this.fetchTradeData(queryData).then((json) => { this.fetchTradeData(queryData).then((json) => {
if (json.errcode === 0) { if (json.errcode === 0) {
runInAction(() => { runInAction(() => {
@ -92,16 +94,11 @@ class Trade {
*/ */
fetchTradeDataByMonth(queryData) { fetchTradeDataByMonth(queryData) {
this.sideData.loading = true; this.sideData.loading = true;
// todo: groupType: bizarea
// Object.assign(queryData, { groupType: 'bu', groupDateType: 'month' });
Object.assign(queryData, { groupType: 'bizarea', groupDateType: 'month' }); Object.assign(queryData, { groupType: 'bizarea', groupDateType: 'month' });
this.fetchTradeData(queryData).then((json) => { this.fetchTradeData(queryData).then((json) => {
if (json.errcode === 0) { if (json.errcode === 0) {
runInAction(() => { runInAction(() => {
const sortResult = json.result1.sort(sortBy('groupDateVal')); const sortResult = json.result1.sort(sortBy('groupDateVal'));
/**
* test: '91006'
*/
const groupsData = sortResult.reduce((r, v) => { const groupsData = sortResult.reduce((r, v) => {
if (v.groupsLabel ) { // && ['91001', '91006'].includes(v.groupsKey) if (v.groupsLabel ) { // && ['91001', '91006'].includes(v.groupsKey)
(r[v.groupsLabel] || (r[v.groupsLabel] = [])).push(v); (r[v.groupsLabel] || (r[v.groupsLabel] = [])).push(v);
@ -111,8 +108,6 @@ class Trade {
this.sideData.loading = false; this.sideData.loading = false;
this.sideData.dataSource = groupsData; this.sideData.dataSource = groupsData;
this.sideData.monthData = sortResult; this.sideData.monthData = sortResult;
// const kpi = { label: '', value: 1200000 }; // 标注KPI
// this.sideData.kpi = kpi;
}); });
} }
}); });
@ -149,13 +144,17 @@ class Trade {
this.searchPayloadHome = body; this.searchPayloadHome = body;
} }
timeLineKey = 'week';
setTimeLineKey(v) {
this.timeLineKey = v;
}
summaryData = { loading: false, dataSource: [], kpi: {}, }; summaryData = { loading: false, dataSource: [], kpi: {}, };
timeData = { loading: false, dataSource: [] }; timeData = { loading: false, dataSource: [] };
BuData = { loading: false, dataSource: [] }; BuData = { loading: false, dataSource: [] };
sideData = { loading: false, dataSource: {}, monthData: [] }; sideData = { loading: false, dataSource: {}, monthData: [] };
dataForSort = {}; dataForSort = {};
topData = {}; topData = {};
defaultDataSubject = 'CJCount';
searchPayloadHome = {}; searchPayloadHome = {};
} }

@ -1,30 +1,36 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Row, Col, Spin, Space } from 'antd'; import { Row, Col, Spin, Space, Radio } from 'antd';
import { CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone, } from '@ant-design/icons';
import { stores_Context } from '../config'; import { stores_Context } from '../config';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import StatisticCard from '../components/StatisticCard'; import StatisticCard from '../components/StatisticCard';
import Bullet from '../components/BulletWithSort'; import Bullet from '../components/BulletWithSort';
import Waterfall from '../components/Waterfall'; import Waterfall from '../components/Waterfall';
import DataFieldRadio from './../components/DateFieldRadio'; import DataFieldRadio from '../components/DataFieldRadio';
import { datePartOptions } from './../components/DateGroupRadio/date';
import SearchForm from './../components/search/SearchForm'; import SearchForm from './../components/search/SearchForm';
import { empty, cloneDeep } from './../utils/commons'; import { empty, cloneDeep, isEmpty } from './../utils/commons';
import { dataFieldAlias } from './../libs/ht'; import { dataFieldAlias } from './../libs/ht';
import { Line } from '@ant-design/charts'; import { Line } from '@ant-design/charts';
import './home.css'; import './home.css';
export default observer(() => { const topSeries = [
const navigate = useNavigate(); { key: 'country', label: '国籍' },
const { TradeStore } = useContext(stores_Context); { key: 'dept', label: '小组' },
const { searchPayloadHome, sideData, summaryData, BuData, topData, timeData } = TradeStore; { key: 'operator', label: '顾问' },
{ key: 'destination', label: '目的地' },
// { key: 'GuestGroupType', label: '' },
];
// const iconSets = [CheckCircleTwoTone, <MoneyCollectTwoTone />, <FlagTwoTone />, <ClockCircleTwoTone />, <DashboardTwoTone />,<SmileTwoTone />,];
const iconSets = [CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone];
const topSeries = [ export default observer(() => {
{ key: 'country', label: '国籍' }, // const navigate = useNavigate();
{ key: 'dept', label: '小组' }, const { TradeStore, date_picker_store: searchFormStore } = useContext(stores_Context);
{ key: 'operator', label: '顾问' }, const { sideData, summaryData, BuData, topData, timeData, timeLineKey } = TradeStore;
{ key: 'destination', label: '目的地' }, const { formValues, } = searchFormStore;
// { key: 'GuestGroupType', label: '' },
];
useEffect(() => { useEffect(() => {
if (empty(summaryData.dataSource)) { if (empty(summaryData.dataSource)) {
@ -137,6 +143,14 @@ export default observer(() => {
}, },
}); });
}; };
const [dateField, setDateField] = useState(timeLineKey);
const handleChangeDateType = ({target: {value}}) => {
setDateField(value);
TradeStore.setTimeLineKey(value);
if (!isEmpty(TradeStore.searchPayloadHome)) {
TradeStore.fetchTradeDataByDate();
}
};
return ( return (
<> <>
<Row gutter={16} style={{ margin: '-16px -8px' }}> <Row gutter={16} style={{ margin: '-16px -8px' }}>
@ -145,7 +159,7 @@ export default observer(() => {
<SearchForm <SearchForm
defaultValue={{ defaultValue={{
initialValue: { initialValue: {
...searchPayloadHome, ...formValues,
}, },
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'years'], shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'years'],
fieldProps: { fieldProps: {
@ -155,7 +169,7 @@ export default observer(() => {
}, },
}} }}
onSubmit={(_err, obj, form, str) => { onSubmit={(_err, obj, form, str) => {
TradeStore.setStateSearch(form); TradeStore.setStateSearch(obj);
pageRefresh(obj); pageRefresh(obj);
}} }}
/> />
@ -167,9 +181,9 @@ export default observer(() => {
</Space> </Space>
<Spin spinning={summaryData.loading}> <Spin spinning={summaryData.loading}>
<Row gutter={layoutProps.gutter}> <Row gutter={layoutProps.gutter}>
{summaryData.dataSource.map((item) => ( {summaryData.dataSource.map((item, i) => (
<Col {...layoutProps} key={item.title}> <Col {...layoutProps} key={item.title}>
<StatisticCard {...item} showProgress={item.hasKPI} /> <StatisticCard {...item} showProgress={item.hasKPI} icon={iconSets[i]} />
</Col> </Col>
))} ))}
</Row> </Row>
@ -177,11 +191,13 @@ export default observer(() => {
</section> </section>
<section> <section>
<Space gutter={16} size={'large'}> <Space gutter={16} size={'large'}>
<h3></h3> <h3></h3>
<DataFieldRadio value={timeDataField} onChange={handleChangetimeDataField} /> <DataFieldRadio value={timeDataField} onChange={handleChangetimeDataField} />
<div></div> <Radio.Group options={datePartOptions} optionType="button" onChange={handleChangeDateType} value={dateField} />
</Space> </Space>
<Line {...lineConfig} data={timeData.dataSource} /> <Spin spinning={timeData.loading}>
<Line {...lineConfig} data={timeData.dataSource} />
</Spin>
</section> </section>
<section> <section>
<h3>市场进度</h3> <h3>市场进度</h3>
@ -189,11 +205,12 @@ export default observer(() => {
<Row gutter={layoutProps3.gutter}> <Row gutter={layoutProps3.gutter}>
<Col {...layoutProps3}> <Col {...layoutProps3}>
<Bullet {...BUConfig} dataSource={BuData?.dataSource || []} /> <Bullet {...BUConfig} dataSource={BuData?.dataSource || []} />
<h3 style={{ textAlign: 'center' }}>{`各事业部总业绩`}</h3>
</Col> </Col>
{Object.keys(sideData.dataSource).map((key) => ( {Object.keys(sideData.dataSource).map((key) => (
<Col {...layoutProps3} key={key}> <Col {...layoutProps3} key={key}>
<Waterfall key={key} {...WaterfallConfig} title={key} dataSource={sideData.dataSource[key]} line={summaryData.kpi} /> <Waterfall key={key} {...WaterfallConfig} title={key} dataSource={sideData.dataSource[key]} line={summaryData.kpi} />
<h3 style={{ textAlign: 'center' }}>{key}</h3> <h3 style={{ textAlign: 'center' }}>{`${key}每月业绩`}</h3>
</Col> </Col>
))} ))}
</Row> </Row>

Loading…
Cancel
Save