You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
12 KiB
JavaScript
335 lines
12 KiB
JavaScript
import React, { useContext, useState, useEffect } from 'react';
|
|
import { observer } from 'mobx-react';
|
|
import { Link } from 'react-router-dom';
|
|
import { stores_Context, DATE_FORMAT, SMALL_DATETIME_FORMAT } from '../../config';
|
|
import moment from 'moment';
|
|
import { Row, Col, Table, Select, Spin, Tag } from 'antd';
|
|
import SearchForm from '../../components/search/SearchForm';
|
|
import MixFieldsDetail from '../../components/MixFieldsDetail';
|
|
import { fixTo2Decimals, isEmpty, pick } from '../../utils/commons';
|
|
import Column from '../../components/Column';
|
|
import { groupsMappedByKey } from '../../libs/ht';
|
|
|
|
const COLOR_SETS = [
|
|
"#FFFFFF",
|
|
// "#5B8FF9",
|
|
// "#FF6B3B",
|
|
"#9FB40F",
|
|
"#76523B",
|
|
"#DAD5B5",
|
|
"#E19348",
|
|
"#F383A2",
|
|
];
|
|
|
|
const transparentHex = '1A';
|
|
|
|
const numberConvert10K = (number, scale = 10) => {
|
|
return fixTo2Decimals((number/(1000*scale)));
|
|
};
|
|
|
|
export default observer((props) => {
|
|
const { SalesCRMDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
|
|
|
const { formValues, formValuesToSub, siderBroken } = searchFormStore;
|
|
const _formValuesToSub = pick(formValuesToSub, ['DepartmentList', 'WebCode']);
|
|
|
|
const { searchValues, resetData, results } = SalesCRMDataStore;
|
|
const operatorObjects = results.details.map((v) => ({ key: v.groupsKey, value: v.groupsKey, label: v.groupsLabel, text: v.groupsLabel }));
|
|
// console.log(operatorObjects);
|
|
|
|
const pageRefresh = async (obj) => {
|
|
resetData('results');
|
|
const deptList = obj.DepartmentList.split(',');
|
|
const includeCH = ['1', '2', '7'].some(ele => deptList.includes(ele));
|
|
const includeAH = ['28'].every(ele => deptList.includes(ele));
|
|
const includeGH = ['33'].every(ele => deptList.includes(ele));
|
|
const otherDept = deptList.filter(ele => !['1', '2', '7', '28', '33'].includes(ele));
|
|
const separateParam = [];
|
|
if (includeCH) {
|
|
const inCH = deptList.filter(k => ['1', '2', '7'].includes(k)).join(',');
|
|
separateParam.push({ DepartmentList: inCH, retLabel: 'CH'});
|
|
}
|
|
if (includeAH) {
|
|
separateParam.push({ DepartmentList: '28', retLabel: 'AH'});
|
|
}
|
|
if (includeGH) {
|
|
separateParam.push({ DepartmentList: '33', retLabel: 'GH'});
|
|
}
|
|
if (!isEmpty(otherDept) && (!isEmpty(includeAH) || !isEmpty(includeCH) || !isEmpty(includeGH))) {
|
|
separateParam.push({ DepartmentList: otherDept.join(','), retLabel: otherDept.map(k => groupsMappedByKey[k].label).join(', ') }); // '其它组'
|
|
}
|
|
if (!includeAH && !includeCH && !includeGH) {
|
|
separateParam.push({ DepartmentList: obj.DepartmentList });
|
|
}
|
|
// console.log('separateParam', separateParam, otherDept);
|
|
// console.log('formValuesToSub --- pageRefresh', formValuesToSub.DepartmentList);
|
|
// return;
|
|
for await (const subParam of separateParam) {
|
|
// console.log(subParam);
|
|
await SalesCRMDataStore.get90n180Data({
|
|
...(obj || _formValuesToSub),
|
|
...subParam,
|
|
groupType: 'overview',
|
|
// groupType: 'dept', // todo:
|
|
groupDateType: '',
|
|
});
|
|
await SalesCRMDataStore.get90n180Data({
|
|
...(obj || _formValuesToSub),
|
|
...subParam,
|
|
groupType: 'operator',
|
|
groupDateType: '',
|
|
});
|
|
}
|
|
};
|
|
|
|
const getFullYearDiagramData = async (obj) => {
|
|
// console.log('invoke --- getFullYearDiagramData');
|
|
// console.log('formValuesToSub --- getFullYearDiagramData', formValuesToSub.DepartmentList);
|
|
await SalesCRMDataStore.getResultData({
|
|
..._formValuesToSub,
|
|
Date1: moment(obj.date).startOf('year').format(DATE_FORMAT),
|
|
Date2: moment(obj.date).endOf('year').format(SMALL_DATETIME_FORMAT),
|
|
groupType: 'overview',
|
|
// groupType: 'operator',
|
|
groupDateType: 'month',
|
|
...(obj),
|
|
});
|
|
};
|
|
const getDiagramData = async (obj) => {
|
|
// console.log('invoke --- getDiagramData');
|
|
// console.log(_formValuesToSub, SalesCRMDataStore.searchValuesToSub);
|
|
await SalesCRMDataStore.getResultData({
|
|
..._formValuesToSub,
|
|
...SalesCRMDataStore.searchValuesToSub,
|
|
// Date1: moment().startOf('year').format(DATE_FORMAT),
|
|
// Date2: moment().endOf('year').format(SMALL_DATETIME_FORMAT),
|
|
// groupType: 'overview',
|
|
groupType: 'operator',
|
|
groupDateType: '',
|
|
...(obj),
|
|
});
|
|
};
|
|
|
|
const retPropsMinRatesSet = { 'CH': 12, 'AH': 8, 'default': 10 }; //
|
|
|
|
/**
|
|
* 业绩数据列
|
|
* ! 成行率: CH个人成行率<12%, AH个人成行率<8%
|
|
*/
|
|
const dataFields = (suffix, colRootIndex) => [
|
|
{
|
|
key: 'ConfirmRates' + suffix,
|
|
title: '成行率',
|
|
dataIndex: [suffix, 'ConfirmRates'],
|
|
width: '5em',
|
|
// CH个人成行率<12%, AH个人成行率<8%刷红
|
|
render: (val, r) => ({
|
|
props: { style: { color: val < (retPropsMinRatesSet?.[r?.retProps || 'default'] || retPropsMinRatesSet.default) ? 'red' : 'green', backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
|
children: val ? `${val}%` : '',
|
|
}),
|
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ConfirmRates - b[suffix].ConfirmRates),
|
|
},
|
|
{
|
|
key: 'SumML' + suffix,
|
|
title: '业绩/万',
|
|
dataIndex: [suffix, 'SumML'],
|
|
width: '5em',
|
|
render: (_, r) => ({
|
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
|
children: numberConvert10K(_),
|
|
}),
|
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].SumML - b[suffix].SumML),
|
|
},
|
|
{
|
|
key: 'ConfirmOrder' + suffix,
|
|
title: '团数',
|
|
dataIndex: [suffix, 'ConfirmOrder'],
|
|
width: '5em',
|
|
render: (_, r) => ({
|
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
|
children: _,
|
|
}),
|
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ConfirmOrder - b[suffix].ConfirmOrder),
|
|
},
|
|
{
|
|
key: 'SumOrder' + suffix,
|
|
title: '订单数',
|
|
dataIndex: [suffix, 'SumOrder'],
|
|
width: '5em',
|
|
render: (_, r) => ({
|
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
|
children: _,
|
|
}),
|
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].SumOrder - b[suffix].SumOrder),
|
|
},
|
|
{
|
|
key: 'ResumeOrder' + suffix,
|
|
title: '老客户团数',
|
|
dataIndex: [suffix, 'ResumeOrder'],
|
|
width: '5em',
|
|
render: (_, r) => ({
|
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
|
children: _,
|
|
}),
|
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ResumeOrder - b[suffix].ResumeOrder),
|
|
},
|
|
{
|
|
key: 'ResumeRates' + suffix,
|
|
title: '老客户成行率',
|
|
dataIndex: [suffix, 'ResumeRates'],
|
|
width: '5em',
|
|
render: (val, r) => ({
|
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
|
children: val ? `${val}%` : ''
|
|
}),
|
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ResumeRates - b[suffix].ResumeRates),
|
|
},
|
|
];
|
|
|
|
const dashboardTableProps = {
|
|
pagination: false,
|
|
size: 'small',
|
|
showSorterTooltip: false,
|
|
columns: [
|
|
{
|
|
title: '',
|
|
dataIndex: 'groupsLabel',
|
|
key: 'name',
|
|
width: '5em',
|
|
filterSearch: true,
|
|
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
|
onFilter: (value, record) => record.groupsKey === value || record.groupType === 'overview',
|
|
render: (text, record) => (record.groupType !== 'operator' ? text : <Link to={`/op_risk/sales/${record.groupsKey}`}>{text}</Link>),
|
|
},
|
|
{
|
|
title: () => (<>前90 -30天<br/>{searchValues.date90.Date1} 至 {searchValues.date90.Date2}</>),
|
|
key: 'date',
|
|
children: dataFields('result90', 0),
|
|
},
|
|
{
|
|
title: () => (<>前180 -50天<br/>{searchValues.date180.Date1} 至 {searchValues.date180.Date2}</>),
|
|
key: 'department',
|
|
children: dataFields('result180', 1),
|
|
},
|
|
],
|
|
rowClassName: (record, rowIndex) => {
|
|
return record.groupType === 'overview' ? 'ant-tag-blue' : '';
|
|
},
|
|
};
|
|
|
|
const columnConfig = {
|
|
xField: 'groupsLabel',
|
|
yField: 'SumML',
|
|
label: { position: 'top' },
|
|
};
|
|
|
|
const [clickColumn, setClickColumn] = useState({});
|
|
const [clickColumnTitle, setClickColumnTitle] = useState('');
|
|
const onChartItemClick = (colData) => {
|
|
// console.log('onChartItemClick', colData);
|
|
if (colData.groupType === 'operator') {
|
|
// test: 0
|
|
return false; // 单人趋势数据上的点击, 不做钻取
|
|
}
|
|
setClickColumn(colData);
|
|
setClickColumnTitle(moment(colData.groupDateVal).format('YYYY-MM'));
|
|
};
|
|
const chartsConfig = {
|
|
colFields: ['ConfirmOrder', 'SumOrder'],
|
|
lineFields: ['SumML', 'ConfirmRates'],
|
|
seriesField: null,
|
|
xField: 'groupDateVal',
|
|
itemClick: onChartItemClick,
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (isEmpty(clickColumnTitle)) {
|
|
return () => {};
|
|
}
|
|
getDiagramData({
|
|
Date1: moment(clickColumn.groupDateVal).startOf('month').format(DATE_FORMAT),
|
|
Date2: moment(clickColumn.groupDateVal).endOf('month').format(SMALL_DATETIME_FORMAT),
|
|
groupType: 'operator', // test: overview
|
|
groupDateType: '',
|
|
});
|
|
|
|
return () => {};
|
|
}, [clickColumnTitle]);
|
|
|
|
|
|
return (
|
|
<>
|
|
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
|
<Col md={24} lg={24} xxl={24}>
|
|
<SearchForm
|
|
defaultValue={{
|
|
initialValue: {
|
|
...formValues,
|
|
...SalesCRMDataStore.searchValues,
|
|
},
|
|
shows: ['DepartmentList', 'WebCode', 'date'],
|
|
fieldProps: {
|
|
DepartmentList: { show_all: false, mode: 'multiple', col: 5 },
|
|
WebCode: { show_all: false, mode: 'multiple', col: 5 },
|
|
dates: { hide_vs: true },
|
|
},
|
|
}}
|
|
onSubmit={(_err, obj, form, str) => {
|
|
SalesCRMDataStore.setSearchValues(obj, form);
|
|
pageRefresh(obj);
|
|
getFullYearDiagramData({ groupType: 'overview', ...obj });
|
|
}}
|
|
/>
|
|
</Col>
|
|
</Row>
|
|
<section>
|
|
<Table {...dashboardTableProps} bordered dataSource={[...results.dataSource, ...results.details]} loading={results.loading} sticky />
|
|
</section>
|
|
<section>
|
|
<Row gutter={16}>
|
|
<Col flex={'12em'}><h3>每月数据</h3></Col>
|
|
<Col flex={'auto'}>
|
|
<Select
|
|
labelInValue
|
|
// mode={'multiple'}
|
|
style={{ width: '100%' }}
|
|
placeholder={'选择顾问'}
|
|
// onChange={sale_store.setPickSales}
|
|
// onSelect={}
|
|
onChange={(labelInValue) => labelInValue ? getFullYearDiagramData({ groupType: 'operator', opisn: labelInValue.value, }) : false}
|
|
onClear={() => getFullYearDiagramData({ groupType: 'overview', opisn: -1, })}
|
|
// value={sale_store.salesTrade.pickSales}
|
|
// maxTagCount={1}
|
|
// maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
|
|
allowClear={true}
|
|
options={operatorObjects}
|
|
/>
|
|
{/* {operatorObjects.map((ele) => (
|
|
<Select.Option key={ele.key} value={ele.value}>
|
|
{ele.label}
|
|
</Select.Option>
|
|
))}
|
|
</Select> */}
|
|
</Col>
|
|
</Row>
|
|
<Spin spinning={results.loading}>
|
|
{/* 小组每月; x轴: 日期; y轴: [订单数, ...] */}
|
|
<MixFieldsDetail {...chartsConfig} dataSource={results.byDate} />
|
|
</Spin>
|
|
<h3>
|
|
点击上方图表的柱状图, 查看当月 <Tag color='orange'>业绩</Tag>数据: <Tag color='orange'>{clickColumnTitle}</Tag>
|
|
</h3>
|
|
{/* 显示小组的详情: 所有顾问? */}
|
|
<Spin spinning={results.loading}>
|
|
<Column {...columnConfig} dataSource={results.byOperator} />
|
|
</Spin>
|
|
{/* <Table columns={[{ title: '', dataIndex: 'date', key: 'date', width: '5em' }, ...dataFields]} bordered size='small' /> */}
|
|
</section>
|
|
<section>
|
|
{/* 月份×小组的详情; x轴: 顾问; y轴: [订单数, ...] */}
|
|
{/* <MixFieldsDetail {...chartsConfig} xField={'label'} dataSource={[]} /> */}
|
|
</section>
|
|
</>
|
|
);
|
|
});
|