Compare commits
173 Commits
Author | SHA1 | Date |
---|---|---|
|
021fb429ce | 4 days ago |
|
a9f45e334d | 1 month ago |
|
488b33ae50 | 1 month ago |
|
6dbf9cfb56 | 1 month ago |
|
108becd2cd | 1 month ago |
|
73e1dcdb4c | 1 month ago |
|
baae505831 | 1 month ago |
|
16b08311fa | 1 month ago |
|
e0e7c3c84f | 2 months ago |
|
87f24c40fa | 2 months ago |
|
bed6b81753 | 2 months ago |
|
171dd7514a | 2 months ago |
|
a0ea6507a3 | 2 months ago |
|
40cd81f9a7 | 2 months ago |
|
876216458e | 2 months ago |
|
8a0f919a42 | 2 months ago |
|
020c9fc3e9 | 2 months ago |
|
b2e539a4d2 | 2 months ago |
|
fee1185406 | 2 months ago |
|
e297d36256 | 2 months ago |
|
667574ec34 | 2 months ago |
|
185db1ec80 | 2 months ago |
|
6dc82a0a01 | 2 months ago |
|
4724b13a2d | 2 months ago |
|
f532f138ff | 2 months ago |
|
3b51ae8d08 | 2 months ago |
|
a6f7884e6e | 2 months ago |
|
928e27913d | 2 months ago |
|
94363df0fc | 2 months ago |
|
621d3a2446 | 2 months ago |
|
8f12fc8747 | 2 months ago |
|
397c886aea | 2 months ago |
|
459db55453 | 2 months ago |
|
6317c24a75 | 2 months ago |
|
c9e13385dc | 2 months ago |
|
a02fa37f17 | 2 months ago |
|
ccbd0be1cd | 2 months ago |
|
c7d3cf5746 | 2 months ago |
|
23b1d2c81b | 2 months ago |
|
7c08e8bfe6 | 2 months ago |
|
1e46cf6e63 | 2 months ago |
|
17cedf14d9 | 2 months ago |
|
2fae92fa2a | 2 months ago |
|
08aa01e33c | 2 months ago |
|
17a85122b1 | 2 months ago |
|
1b86ab1192 | 2 months ago |
|
be31c3983d | 2 months ago |
|
5551b2e362 | 3 months ago |
|
7cfd79a0d2 | 3 months ago |
|
dcb185be5a | 3 months ago |
|
c3547538fd | 3 months ago |
|
cb598ecba1 | 3 months ago |
|
a32943fffb | 3 months ago |
|
a7b24d206d | 3 months ago |
|
f8eef38944 | 3 months ago |
|
89d151f388 | 3 months ago |
|
6ea43a58e2 | 3 months ago |
|
12e99bb816 | 3 months ago |
|
d29dab5824 | 3 months ago |
|
f1e0c1200b | 3 months ago |
|
6f640bac4d | 3 months ago |
|
e822b9bb3a | 3 months ago |
|
ac11babdab | 3 months ago |
|
04431635b5 | 4 months ago |
|
80d4050527 | 4 months ago |
|
6d6a7d98ee | 4 months ago |
|
b583bfcd2f | 4 months ago |
|
539f57e16d | 4 months ago |
|
e5ff51ae63 | 4 months ago |
|
cc3d0c19ad | 4 months ago |
|
00e22a6f08 | 4 months ago |
|
02c09c9821 | 4 months ago |
|
bd67f6ca4e | 4 months ago |
|
04d1cc2b39 | 4 months ago |
|
d1ee551a4e | 4 months ago |
|
4531ce1469 | 4 months ago |
|
d2696d8b01 | 4 months ago |
|
557e4ac104 | 4 months ago |
|
9a4c693c9e | 4 months ago |
|
2be734a591 | 4 months ago |
|
88e09d0bc9 | 5 months ago |
|
ff4852475c | 5 months ago |
|
a0b71dba79 | 5 months ago |
|
b2ce88e8aa | 5 months ago |
|
20e237f75d | 5 months ago |
|
b4a2b02709 | 5 months ago |
|
3890c4700f | 7 months ago |
|
cb211ec207 | 7 months ago |
|
83e570bb49 | 8 months ago |
|
545ad25d05 | 8 months ago |
|
496fbc1ebd | 8 months ago |
|
e53f241d10 | 9 months ago |
|
93766a28b6 | 11 months ago |
|
b5115a8d1e | 11 months ago |
|
322ce021e6 | 11 months ago |
|
55bd0576a8 | 12 months ago |
|
4792291025 | 12 months ago |
|
f2a937cc81 | 12 months ago |
|
d05d15ae09 | 12 months ago |
|
4743a66408 | 1 year ago |
|
49b1657046 | 1 year ago |
|
71b0cce66a | 1 year ago |
|
9e3bb33043 | 1 year ago |
|
c2317d40a7 | 1 year ago |
|
046b7e2988 | 1 year ago |
|
d18c59bb50 | 1 year ago |
|
ab79cdc289 | 1 year ago |
|
345a2b88d8 | 1 year ago |
|
530e558812 | 1 year ago |
|
ebdb7d41b4 | 1 year ago |
|
3c9c9ede8c | 1 year ago |
|
536a8b7f86 | 1 year ago |
|
216bd9c0e6 | 1 year ago |
|
55d205aaaa | 1 year ago |
|
0e76cc1f08 | 1 year ago |
|
854c4a5ebc | 1 year ago |
|
5f55529f68 | 1 year ago |
|
8eb539abb2 | 1 year ago |
|
e42f1f1b6c | 1 year ago |
|
f92f3f53bb | 1 year ago |
|
1025d321ef | 1 year ago |
|
00f0c1a7b1 | 1 year ago |
|
a7280a79b9 | 1 year ago |
|
512b4699ba | 1 year ago |
|
5dcdcd6345 | 1 year ago |
|
ac82432e67 | 1 year ago |
|
35d2731560 | 1 year ago |
|
513b7c7c1c | 1 year ago |
|
401d5720d8 | 1 year ago |
|
e4a63f38f6 | 1 year ago |
|
1a2ee6b637 | 1 year ago |
|
81ee6594c0 | 1 year ago |
|
fe29cb3d1c | 1 year ago |
|
8017b9c2c8 | 1 year ago |
|
5e98893ef0 | 1 year ago |
|
f8820d0eb5 | 1 year ago |
|
1bc85a8b38 | 1 year ago |
|
16fb5b3dfc | 1 year ago |
|
a777eff3bd | 1 year ago |
|
766411cf16 | 1 year ago |
|
956303d9e5 | 1 year ago |
|
606c1e9983 | 1 year ago |
|
9718c023a0 | 2 years ago |
|
8fc609c1c0 | 2 years ago |
|
d272cf6806 | 2 years ago |
|
b4f869453a | 2 years ago |
|
d0d0cb897f | 2 years ago |
|
08b19ee3a9 | 2 years ago |
|
3f62a59648 | 2 years ago |
|
8484beba64 | 2 years ago |
|
528a950b22 | 2 years ago |
|
3ba2b44daa | 2 years ago |
|
8caf1a5fb8 | 2 years ago |
|
18dcfe3759 | 2 years ago |
|
d80ecf398b | 2 years ago |
|
43780b5fc2 | 2 years ago |
|
805804ee88 | 2 years ago |
|
151dcff1a8 | 2 years ago |
|
ea7048e41b | 2 years ago |
|
9695335b30 | 2 years ago |
|
5dafa48ab3 | 2 years ago |
|
e59c1e11bd | 2 years ago |
|
95e799cdae | 2 years ago |
|
d141e0fa7d | 2 years ago |
|
609b6761b3 | 2 years ago |
|
a48bbf35f3 | 2 years ago |
|
6eea280a1d | 2 years ago |
|
cfb4a5f5f7 | 2 years ago |
|
ad91ce73a0 | 2 years ago |
|
f39ed273c3 | 2 years ago |
|
c8e66c12d8 | 2 years ago |
|
95ec1fa822 | 2 years ago |
|
7af50f0e9e | 2 years ago |
@ -0,0 +1,240 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Row, Col, Spin } from 'antd';
|
||||
import { Line } from '@ant-design/plots';
|
||||
import { observer } from 'mobx-react';
|
||||
import { dataFieldAlias } from '../libs/ht';
|
||||
import DateGroupRadio from '../components/DateGroupRadio';
|
||||
import { cloneDeep, groupBy, sortBy } from '../utils/commons';
|
||||
|
||||
export default observer((props) => {
|
||||
const { dataSource: rawData, showAVG, showCompareSum, loading, solidLineTime,
|
||||
solidLineDash, isCompareLine,solidLineCompareTime, ...config } = props;
|
||||
const { xField, yField, yFieldAlias, seriesField } = config;
|
||||
|
||||
const [dataBeforeXChange, setDataBeforeXChange] = useState([]);
|
||||
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const [sumSeries, setSumSeries] = useState([]);
|
||||
|
||||
const line_config = {
|
||||
// data: dataSource,
|
||||
padding: 'auto',
|
||||
xField,
|
||||
yField,
|
||||
seriesField,
|
||||
// seriesField: 'rowLabel',
|
||||
// xAxis: {
|
||||
// type: 'timeCat',
|
||||
// },
|
||||
color: isCompareLine?['#17f485', '#1890ff','#17f485', '#1890ff',"#181414","#181414"]:undefined,
|
||||
point: {
|
||||
size: 4,
|
||||
shape: "cicle",
|
||||
},
|
||||
lineStyle: (datum) => {
|
||||
return {
|
||||
stroke: isCompareLine?datum._ylabel.includes("总计")?"#181414":datum._ylabel.includes(solidLineDash) ? '#1890ff':'#17f485' : undefined, // 设置颜色
|
||||
lineDash: isCompareLine?datum._ylabel.includes(solidLineTime) ? undefined:[4, 4] : undefined, // 设置虚线
|
||||
};
|
||||
},
|
||||
yAxis: {
|
||||
min: 0,
|
||||
maxTickInterval: 5,
|
||||
},
|
||||
meta: {
|
||||
...cloneDeep(dataFieldAlias),
|
||||
},
|
||||
// smooth: true,
|
||||
label: {}, // 显示标签
|
||||
legend: {
|
||||
position: 'right-top',
|
||||
// title: {
|
||||
// text: '总合计 ' + dataSource.reduce((a, b) => a + b.SumOrder, 0),
|
||||
// },
|
||||
itemMarginBottom: 12, // 垂直间距
|
||||
},
|
||||
tooltip: {
|
||||
customItems: (originalItems) => {
|
||||
return originalItems
|
||||
.map((ele) => ({ ...ele, valueR: ele.data[yField] }))
|
||||
.sort(sortBy('valueR'))
|
||||
.reverse();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [lineConfig, setLineConfig] = useState(cloneDeep(line_config));
|
||||
|
||||
useEffect(() => {
|
||||
resetX();
|
||||
|
||||
return () => {};
|
||||
}, [rawData]);
|
||||
|
||||
useEffect(() => {
|
||||
setLineConfig(cloneDeep(line_config));
|
||||
|
||||
return () => {};
|
||||
}, [isCompareLine,solidLineTime]);
|
||||
|
||||
useEffect(() => {
|
||||
if (lineChartX === 'day') {
|
||||
setDataBeforeXChange(dataSource);
|
||||
}
|
||||
|
||||
return () => {};
|
||||
}, [dataSource]);
|
||||
|
||||
// 日月年切换
|
||||
const [lineChartX, setLineChartX] = useState('day');
|
||||
const orderCountDataMapper = { data1: 'data1', data2: undefined };
|
||||
const orderCountDataFieldMapper = { 'dateKey': xField, 'valueKey': yField, 'seriesKey': seriesField, _f: 'sum' };
|
||||
const resetX = () => {
|
||||
setLineChartX('day');
|
||||
setDataSource(rawData);
|
||||
setDataBeforeXChange(rawData);
|
||||
// 初始化`平均`线, `总计`线
|
||||
const byDays = groupBy(rawData, xField);
|
||||
const sumY = rawData.reduce((a, b) => a + b[yField], 0);
|
||||
// const avgVal = Math.round(sumY / (Object.keys(byDays).length));
|
||||
// const avgLine = [
|
||||
// { type: 'text', position: ['start', avgVal], content: avgVal, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } },
|
||||
// { type: 'line', start: [-10, avgVal], end: ['max', avgVal], style: { stroke: '#F4664A', lineDash: [2, 2] } },
|
||||
// ];
|
||||
// setLineConfig({ ...lineConfig, yField, xField, annotations: avgLine });
|
||||
setLineConfig({ ...lineConfig, yField, xField,});
|
||||
if (showCompareSum) {
|
||||
const _sumLine = Object.keys(byDays).reduce((r, _d) => {
|
||||
const summaryVal = byDays[_d].reduce((rows, row) =>
|
||||
{
|
||||
if (row[seriesField].includes(solidLineTime)){
|
||||
return rows + row[yField];
|
||||
}
|
||||
else{
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
, 0);
|
||||
const summaryCompareVal = byDays[_d].reduce((rows, row) =>
|
||||
{
|
||||
if (row[seriesField].includes(solidLineCompareTime)){
|
||||
return rows + row[yField];
|
||||
}
|
||||
else{
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
, 0);
|
||||
r.push({ ...byDays[_d][0], [yField]: summaryVal, [seriesField]: solidLineTime+'总计' });
|
||||
r.push({ ...byDays[_d][0], [yField]: summaryCompareVal, [seriesField]: solidLineCompareTime+'总计' });
|
||||
return r;
|
||||
}, []);
|
||||
setSumSeries(_sumLine);
|
||||
}
|
||||
else{
|
||||
const _sumLine = Object.keys(byDays).reduce((r, _d) => {
|
||||
const summaryVal = byDays[_d].reduce((rows, row) => rows + row[yField], 0);
|
||||
r.push({ ...byDays[_d][0], [yField]: summaryVal, [seriesField]: '总计' });
|
||||
return r;
|
||||
}, []);
|
||||
// console.log(_sumLine.map((ele) => ele[yField]));
|
||||
setSumSeries(_sumLine);
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeXDateFieldGroup = (value, data, avg1) => {
|
||||
// console.log(value, data, avg1);
|
||||
const _sumLine = [];
|
||||
const { xField, yField, seriesField } = lineConfig;
|
||||
const groupByDate = data.reduce((r, v) => {
|
||||
(r[v[xField]] || (r[v[xField]] = [])).push(v);
|
||||
return r;
|
||||
}, {});
|
||||
// console.log(groupByDate);
|
||||
const _data = Object.keys(groupByDate).reduce((r, _d) => {
|
||||
if (showCompareSum) {
|
||||
const summaryVal = groupByDate[_d].reduce((rows, row) =>
|
||||
{
|
||||
if (row[seriesField].includes(solidLineTime)){
|
||||
return rows + row[yField];
|
||||
}
|
||||
else{
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
, 0);
|
||||
const summaryCompareVal = groupByDate[_d].reduce((rows, row) =>
|
||||
{
|
||||
if (row[seriesField].includes(solidLineCompareTime)){
|
||||
return rows + row[yField];
|
||||
}
|
||||
else{
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
, 0);
|
||||
_sumLine.push({ ...groupByDate[_d][0], [yField]: summaryVal, [seriesField]: solidLineTime+'总计' });
|
||||
_sumLine.push({ ...groupByDate[_d][0], [yField]: summaryCompareVal, [seriesField]: solidLineCompareTime+'总计' });
|
||||
}
|
||||
else{
|
||||
const summaryVal = groupByDate[_d].reduce((rows, row) => rows + row[yField], 0);
|
||||
_sumLine.push({ ...groupByDate[_d][0], [yField]: summaryVal, [seriesField]: '总计' });
|
||||
}
|
||||
|
||||
|
||||
const xAxisGroup = groupByDate[_d].reduce((a, v) => {
|
||||
(a[v[seriesField]] || (a[v[seriesField]] = [])).push(v);
|
||||
return a;
|
||||
}, {});
|
||||
// console.log(xAxisGroup);
|
||||
Object.keys(xAxisGroup).map((_group) => {
|
||||
const summaryVal = xAxisGroup[_group].reduce((rows, row) => rows + row[yField], 0);
|
||||
r.push({ ...xAxisGroup[_group][0], [yField]: summaryVal, });
|
||||
return _group;
|
||||
});
|
||||
return r;
|
||||
}, []);
|
||||
// const _sum = Object.values(groupBy(_data, 'dateGroup')).reduce((ac, b) => ({...b, [yField]: 0}), {});
|
||||
// console.log(xField, avg1);
|
||||
// console.log('date source=====', _data);
|
||||
setLineChartX(value);
|
||||
setDataSource(_data);
|
||||
setSumSeries(_sumLine);
|
||||
|
||||
// setAvgLine1(avg1);
|
||||
// const avg1Int = Math.round(avg1);
|
||||
// const mergedConfig = { ...lineConfig,
|
||||
// annotations: [
|
||||
// { type: 'text', position: ['start', avg1Int], content: avg1Int, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } },
|
||||
// { type: 'line', start: [-10, avg1Int], end: ['max', avg1Int], style: { stroke: '#F4664A', lineDash: [2, 2] } },
|
||||
// ],
|
||||
// };
|
||||
// console.log(mergedConfig);
|
||||
// setLineConfig(mergedConfig);
|
||||
setLineConfig(cloneDeep(line_config));
|
||||
};
|
||||
return (
|
||||
<section>
|
||||
<Row gutter={16} justify={'space-between'} className="mb-1">
|
||||
<Col flex={'auto'}>
|
||||
<h3>
|
||||
走势: <span style={{ fontSize: 'smaller' }}>{dataFieldAlias[lineConfig.yField].label}</span>
|
||||
</h3>
|
||||
</Col>
|
||||
<Col style={{ textAlign: 'right' }} align={'end'}>
|
||||
<DateGroupRadio
|
||||
visible={true}
|
||||
dataRaw={{ data1: dataBeforeXChange }}
|
||||
onChange={onChangeXDateFieldGroup}
|
||||
value={lineChartX}
|
||||
dataMapper={orderCountDataMapper}
|
||||
fieldMapper={orderCountDataFieldMapper}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spin spinning={loading}>
|
||||
<Line {...lineConfig} data={[].concat(dataSource, sumSeries)} />
|
||||
</Spin>
|
||||
</section>
|
||||
);
|
||||
});
|
@ -0,0 +1,265 @@
|
||||
import { observer } from 'mobx-react';
|
||||
import { message } from 'antd';
|
||||
import { Mix, getCanvasPattern, } from '@ant-design/plots';
|
||||
import { merge, isEmpty, cloneDeep } from '../utils/commons';
|
||||
import { dataFieldAlias } from '../libs/ht';
|
||||
|
||||
const COLOR_SETS = [
|
||||
"#FF6B3B",
|
||||
"#9FB40F",
|
||||
"#76523B",
|
||||
"#DAD5B5",
|
||||
"#E19348",
|
||||
"#F383A2",
|
||||
];
|
||||
const COLOR_SETS2 = [
|
||||
"#5B8FF9",
|
||||
"#61DDAA",
|
||||
"#65789B",
|
||||
];
|
||||
|
||||
/**
|
||||
* 订单数, 团数: 柱形图
|
||||
* 成交率: 折线图
|
||||
*/
|
||||
export default observer((props) => {
|
||||
const { dataSource, summaryData: areaData, ...config } = props;
|
||||
const { xField, yFields, colFields, lineFields, seriesField, tooltip, ...extConfig } = config;
|
||||
const diffData0 = dataSource.reduce((r, row) => {
|
||||
r.push({ ...row, yField: row[colFields[0]], yGroup: dataFieldAlias[colFields[0]].alias });
|
||||
r.push({ ...row, yField: row[colFields[1]], yGroup: dataFieldAlias[colFields[1]].alias });
|
||||
return r;
|
||||
}, []);
|
||||
const diffData1 = dataSource.reduce((r, row) => {
|
||||
r.push({ ...row, yField: row[lineFields[1]], yGroup: dataFieldAlias[lineFields[1]].alias });
|
||||
return r;
|
||||
}, []);
|
||||
const calcAxis = isEmpty(diffData0) ? 300 : (Math.max(...diffData0.map(ele => ele.yField))) * 3;
|
||||
// const calcAxisC = isEmpty(diffData0) ? 300 : (Math.max(...diffDataPercent.map(ele => ele.yField))) * 3;
|
||||
const diffLine = [
|
||||
// {
|
||||
// type: 'text',
|
||||
// position: ['start', 0],
|
||||
// content: `同比, 环比`,
|
||||
// offsetX: -15,
|
||||
// style: {
|
||||
// fill: COLOR_SETS[0],
|
||||
// textBaseline: 'bottom',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// type: 'line',
|
||||
// start: [-10, 0],
|
||||
// end: ['max', 0],
|
||||
// style: {
|
||||
// stroke: COLOR_SETS[0],
|
||||
// // lineDash: [2, 2],
|
||||
// lineWidth: 0.5,
|
||||
// },
|
||||
// },
|
||||
];
|
||||
|
||||
const pattern = (datum, color) => {
|
||||
return getCanvasPattern({
|
||||
type: String(datum.yGroup).includes(' ') ? 'line' : '',
|
||||
cfg: {
|
||||
backgroundColor: color,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const MixConfig = {
|
||||
appendPadding: 15,
|
||||
height: 400,
|
||||
syncViewPadding: true,
|
||||
tooltip: {
|
||||
shared: true,
|
||||
// customItems: (originalItems) => {
|
||||
// // process originalItems,
|
||||
// const items = originalItems.map((ele) => ({ ...ele, name: ele.data?.extraLine ? ele.name : `${ele.name} ${dataFieldAlias[yField]?.alias || yField}` }));
|
||||
// return items;
|
||||
// },
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
layout: 'horizontal',
|
||||
custom: true,
|
||||
items: [
|
||||
...['团数', '订单数'].map((ele, ei) => ({
|
||||
name: `${ele}`,
|
||||
value: `${ele}`,
|
||||
marker: {
|
||||
symbol: 'square',
|
||||
style: {
|
||||
fill: COLOR_SETS2[ei],
|
||||
r: 5,
|
||||
},
|
||||
},
|
||||
})),
|
||||
...['', '成行率'].map((ele, ei) => ({ // '业绩',
|
||||
name: `${ele}`,
|
||||
value: `${ele}`,
|
||||
marker: {
|
||||
symbol: 'hyphen',
|
||||
style: {
|
||||
stroke: COLOR_SETS[ei],
|
||||
r: 5,
|
||||
lineWidth: 2
|
||||
},
|
||||
},
|
||||
})),
|
||||
],
|
||||
},
|
||||
// event: (chart, e) => {
|
||||
// console.log('mix', chart, e);
|
||||
// if (e.type === 'click') {
|
||||
// props.itemClick(e);
|
||||
// }
|
||||
// },
|
||||
onReady: (plot) => {
|
||||
plot.on('plot:click', (...args) => {
|
||||
// message.info('请在柱状图上点击, 显示详情');
|
||||
});
|
||||
plot.on('element:click', (e) => {
|
||||
const {
|
||||
data: { data },
|
||||
} = e;
|
||||
// console.log('plot element', data);
|
||||
props.itemClick(data);
|
||||
});
|
||||
// axis-label 添加点击事件
|
||||
plot.on('axis-label:click', (e, ...args) => {
|
||||
const { text } = e.target.attrs;
|
||||
// console.log(text);
|
||||
props.itemClick({ [xField]: text });
|
||||
});
|
||||
},
|
||||
plots: [
|
||||
{
|
||||
type: 'column',
|
||||
options: {
|
||||
data: diffData0,
|
||||
isGroup: true,
|
||||
xField,
|
||||
yField: 'yField',
|
||||
seriesField: 'yGroup',
|
||||
// xAxis: false,
|
||||
meta: merge({
|
||||
...cloneDeep(dataFieldAlias),
|
||||
}),
|
||||
// color: '#b32b19',
|
||||
// color: '#f58269',
|
||||
legend: false, // {},
|
||||
// smooth: true,
|
||||
yAxis: {
|
||||
type: 'linear',
|
||||
tickCount: 4,
|
||||
min: 0,
|
||||
max: calcAxis,
|
||||
title: { text: '团数', autoRotate: false, position: 'end' },
|
||||
},
|
||||
xAxis: {
|
||||
label: {
|
||||
autoHide: false,
|
||||
autoRotate: true,
|
||||
},
|
||||
},
|
||||
label: false,
|
||||
color: COLOR_SETS2,
|
||||
pattern,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
options: {
|
||||
data: diffData1,
|
||||
isGroup: true,
|
||||
xField,
|
||||
yField: 'yField',
|
||||
seriesField: 'yGroup',
|
||||
xAxis: false,
|
||||
legend: false, // {},
|
||||
meta: merge(
|
||||
{
|
||||
...cloneDeep(dataFieldAlias),
|
||||
},
|
||||
{ yField: dataFieldAlias[lineFields[1]] }
|
||||
),
|
||||
// color: '#1AAF8B',
|
||||
color: COLOR_SETS[1],
|
||||
// smooth: true,
|
||||
point: {
|
||||
size: 4,
|
||||
shape: 'cicle',
|
||||
},
|
||||
yAxis: {
|
||||
type: 'linear',
|
||||
// tickCount: 4,
|
||||
min: 0,
|
||||
position: 'right',
|
||||
line: null,
|
||||
grid: null,
|
||||
title: { text: dataFieldAlias[lineFields[1]].label, autoRotate: false, position: 'end' },
|
||||
},
|
||||
label: {
|
||||
style: {
|
||||
fontWeight: 700,
|
||||
stroke: '#fff',
|
||||
lineWidth: 1,
|
||||
},
|
||||
},
|
||||
lineStyle: (datum) => {
|
||||
if (String(datum.yGroup).includes(' ')) {
|
||||
return {
|
||||
lineDash: [4, 4],
|
||||
opacity: 0.75,
|
||||
};
|
||||
}
|
||||
return {
|
||||
opacity: 1,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
// {
|
||||
// type: 'column',
|
||||
// options: {
|
||||
// data: diffData2,
|
||||
// xField,
|
||||
// yField: 'yField',
|
||||
// seriesField: 'yGroup',
|
||||
// columnWidthRatio: 0.28,
|
||||
// meta: {
|
||||
// // yField: {
|
||||
// // formatter: (v) => `${v}%`,
|
||||
// // },
|
||||
// },
|
||||
// isGroup: true,
|
||||
// xAxis: false,
|
||||
// yAxis: {
|
||||
// line: null,
|
||||
// grid: null,
|
||||
// label: false,
|
||||
// position: 'left',
|
||||
// // min: -calcAxisC,
|
||||
// // max: calcAxisC/4,
|
||||
// min: -3000,
|
||||
// max: 250,
|
||||
// tickCount: 4,
|
||||
// },
|
||||
// legend: false, // {},
|
||||
// color: COLOR_SETS,
|
||||
// // annotations: diffLine,
|
||||
|
||||
// minColumnWidth: 5,
|
||||
// maxColumnWidth: 5,
|
||||
// // 分组柱状图 组内柱子间的间距 (像素级别)
|
||||
// dodgePadding: 1,
|
||||
// // 分组柱状图 组间的间距 (像素级别)
|
||||
// // intervalPadding: 20,
|
||||
// },
|
||||
// },
|
||||
],
|
||||
};
|
||||
return <Mix {...MixConfig} />;
|
||||
});
|
@ -0,0 +1,9 @@
|
||||
import Icon, {} from '@ant-design/icons';
|
||||
|
||||
const CooperationSvg = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11.8611 2.39057C12.8495 1.73163 14.1336 1.71797 15.1358 2.35573L19.291 4.99994H20.9998C21.5521 4.99994 21.9998 5.44766 21.9998 5.99994V14.9999C21.9998 15.5522 21.5521 15.9999 20.9998 15.9999H19.4801C19.5396 16.9472 19.0933 17.9102 18.1955 18.4489L13.1021 21.505C12.4591 21.8907 11.6609 21.8817 11.0314 21.4974C10.3311 22.1167 9.2531 22.1849 8.47104 21.5704L3.33028 17.5312C2.56387 16.9291 2.37006 15.9003 2.76579 15.0847C2.28248 14.7057 2 14.1254 2 13.5109V6C2 5.44772 2.44772 5 3 5H7.94693L11.8611 2.39057ZM4.17264 13.6452L4.86467 13.0397C6.09488 11.9632 7.96042 12.0698 9.06001 13.2794L11.7622 16.2518C12.6317 17.2083 12.7903 18.6135 12.1579 19.739L17.1665 16.7339C17.4479 16.5651 17.5497 16.2276 17.4448 15.9433L13.0177 9.74551C12.769 9.39736 12.3264 9.24598 11.9166 9.36892L9.43135 10.1145C8.37425 10.4316 7.22838 10.1427 6.44799 9.36235L6.15522 9.06958C5.58721 8.50157 5.44032 7.69318 5.67935 7H4V13.5109L4.17264 13.6452ZM14.0621 4.04306C13.728 3.83047 13.3 3.83502 12.9705 4.05467L7.56943 7.65537L7.8622 7.94814C8.12233 8.20827 8.50429 8.30456 8.85666 8.19885L11.3419 7.45327C12.5713 7.08445 13.8992 7.53859 14.6452 8.58303L18.5144 13.9999H19.9998V6.99994H19.291C18.9106 6.99994 18.5381 6.89148 18.2172 6.68727L14.0621 4.04306ZM6.18168 14.5448L4.56593 15.9586L9.70669 19.9978L10.4106 18.7659C10.6256 18.3897 10.5738 17.9178 10.2823 17.5971L7.58013 14.6247C7.2136 14.2215 6.59175 14.186 6.18168 14.5448Z"></path></svg>
|
||||
);
|
||||
|
||||
const CooperationIcon = (props) => <Icon component={CooperationSvg} {...props} />;
|
||||
|
||||
export default CooperationIcon;
|
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { Select } from 'antd';
|
||||
import { observer } from 'mobx-react';
|
||||
import { HotelStars as options } from '../../libs/ht';
|
||||
|
||||
const HotelStars = (props) => {
|
||||
const { mode, value, onChange, show_all, ...extProps } = props;
|
||||
const _show_all = ['tags', 'multiple'].includes(mode) ? false : show_all;
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
mode={mode || null}
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
placeholder="星级"
|
||||
value={value || undefined}
|
||||
onChange={(value) => {
|
||||
if (typeof onChange === 'function') {
|
||||
onChange(value);
|
||||
}
|
||||
}}
|
||||
labelInValue={false}
|
||||
{...extProps}
|
||||
options={options}
|
||||
>
|
||||
{_show_all ? (
|
||||
<Select.Option key="-1" value="ALL">
|
||||
ALL
|
||||
</Select.Option>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
/**
|
||||
* 酒店星级
|
||||
*/
|
||||
export default observer(HotelStars);
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"get|/inbound_person_num/test": {
|
||||
"errcode": 0,
|
||||
"errmsg": "",
|
||||
"data": null,
|
||||
"loading": null,
|
||||
"resultTotal": {
|
||||
"orgz": "@integer(10,99)",
|
||||
"orgzPDays": "@integer(10,99)",
|
||||
"hosts": "@integer(10,99)",
|
||||
"hostsPDays": "@integer(10,99)",
|
||||
"IndividualService": "@integer(10,99)",
|
||||
"groupsKey": "0",
|
||||
"groupsLabel": "总"
|
||||
},
|
||||
"result|10": [
|
||||
{
|
||||
"orgz": "@integer(10,99)",
|
||||
"orgzPDays": "@integer(10,99)",
|
||||
"hosts": "@integer(10,99)",
|
||||
"hostsPDays": "@integer(10,99)",
|
||||
"groupsKey": "@id",
|
||||
"groupsLabel": "@region"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,355 @@
|
||||
import { makeAutoObservable, runInAction } from 'mobx';
|
||||
import { fetchJSON } from '../utils/request';
|
||||
import { objectMapper, pick, price_to_number, } from '../utils/commons';
|
||||
import { pivotBy } from '../libs/ht';
|
||||
import moment from "moment";
|
||||
import { DATE_FORMAT, DATETIME_FORMAT, SMALL_DATETIME_FORMAT } from '../config';
|
||||
|
||||
/**
|
||||
* 用于透视的数据
|
||||
*/
|
||||
const getDetailData = async (param) => {
|
||||
const json = await fetchJSON('/service-Analyse2/GetTradeApartDetail', param);
|
||||
return json.errcode === 0 ? json.result : [];
|
||||
};
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const getOrderCountByType = async (param) => {
|
||||
const paramBody = objectMapper(param, {
|
||||
WebCode: 'WebCode',
|
||||
OrderType: 'OrderType',
|
||||
IncludeTickets: 'IncludeTickets',
|
||||
DateType: 'DateType',
|
||||
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||
Date1: 'COLI_ApplyDate1',
|
||||
Date2: 'COLI_ApplyDate2',
|
||||
});
|
||||
const url = '/service-web/QueryData/GetOrderCountByType';
|
||||
const json = await fetchJSON(url, paramBody);
|
||||
return json.errcode === 0 ? json : {};
|
||||
};
|
||||
const getAgentGroupInfoALL = async (param) => {
|
||||
const paramBody = objectMapper(param, {
|
||||
DateType: 'DateType',
|
||||
DepartmentList: 'DepList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||
Date1: 'OldDate1',
|
||||
Date2: 'OldDate2',
|
||||
});
|
||||
const url = '/service-web/QueryData/GetAgentGroupInfoALL';
|
||||
const json = await fetchJSON(url, paramBody);
|
||||
return json.errcode === 0 ? json : {};
|
||||
};
|
||||
const getDepartmentOrderMLByType = async (param) => {
|
||||
const paramBody = objectMapper(param, {
|
||||
DateType: 'DateType',
|
||||
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||
OrderType: 'OrderType', // 总览, 产品类型
|
||||
});
|
||||
const url = '/service-web/QueryData/GetDepartmentOrderMLByType';
|
||||
const json = await fetchJSON(url, paramBody);
|
||||
const { result1 } = json.errcode === 0 ? json : { result1: [] };
|
||||
const total1 = ['COLI_CJCount', 'COLI_ML2',].reduce(
|
||||
(r, col) => ({
|
||||
...r,
|
||||
[col]: result1.reduce((rr, row) => rr + row[col], 0),
|
||||
}),
|
||||
{}
|
||||
);
|
||||
return { total1, result1 };
|
||||
};
|
||||
const GHproductTypeListSetting = {
|
||||
ja: ['日本', '东亚跨国'],
|
||||
se: ['东南亚跨国', '泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝'],
|
||||
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||
};
|
||||
const GHCountryListSetting = {
|
||||
ja: ['日本', ],
|
||||
se: ['泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝', '新加坡', '马来西亚', '菲律宾'],
|
||||
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||
};
|
||||
|
||||
const rowItem = (filterData) => {
|
||||
const { data: dataByLineClass, summaryMix: summaryByLineClass } = pivotBy(filterData, [['COLI_LineClass'], [], []]);
|
||||
const LineClass_Origin = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
const LineClass_PPC = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
|
||||
const { data: dataByWebCode, summaryMix: summaryByWebCode } = pivotBy(filterData, [['WebCode'], [], []]);
|
||||
const toB = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
const external = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf("站外渠道") !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
|
||||
const filterIsOldC = filterData.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') === -1);
|
||||
const { data: dataByIsOld, summaryMix: summaryByIsOld } = pivotBy(filterIsOldC, [['IsOld', 'isCusCommend'], [], []]);
|
||||
const isOld1 = dataByIsOld.filter((ele) => ele.rowLabel.indexOf('1') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
|
||||
const total = LineClass_Origin + LineClass_PPC + toB + isOld1 + external;
|
||||
return { LineClass_Origin, LineClass_PPC, toB, external, isOld1, total };
|
||||
};
|
||||
// 日本+: 日本+东亚跨国
|
||||
const dataJA = (rawData, yearData) => {
|
||||
const productTypeList = GHproductTypeListSetting.ja;
|
||||
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
// 东南亚+: 东南亚跨国+泰国+越南+印尼+水灯节线路
|
||||
const dataSE = (rawData, yearData) => {
|
||||
const productTypeList = GHproductTypeListSetting.se;
|
||||
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
// 印度+: 印度+次大陆跨国+尼泊尔+不丹+斯里兰卡
|
||||
const dataIN = (rawData, yearData) => {
|
||||
const productTypeList = GHproductTypeListSetting.in;
|
||||
const exceptProduct = ['印度尼西亚'];
|
||||
const filterData = rawData
|
||||
.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||
.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const filterDataYear = yearData
|
||||
.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||
.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
// 其他GH
|
||||
const dataGHOther = (rawData, yearData) => {
|
||||
const exceptProduct = Object.values(GHproductTypeListSetting).reduce((r, c) => r.concat(c), []);
|
||||
const filterData = rawData.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const filterDataYear = yearData.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
const dataSales = (tKey, rawData, yearData, yearData2) => {
|
||||
const targetList = GHCountryListSetting[tKey];
|
||||
const tIndex = Object.keys(GHCountryListSetting).indexOf(tKey);
|
||||
const exceptTargetList = Object.keys(GHCountryListSetting).reduce((r, c, i) => r.concat(i < tIndex ? GHCountryListSetting[c] : []), []);
|
||||
// console.log(tIndex, tKey, 'exceptTargetList', exceptTargetList, 'targetList', targetList);
|
||||
|
||||
const filterRaw1 = rawData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
// console.log(tKey, 'filterRaw1', filterRaw1);
|
||||
const filterDataC = filterRaw1.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataT = tKey === 'se' ? filterRaw1.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||
const filterData = filterDataC.concat(filterDataT);
|
||||
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||
|
||||
const filterRaw2 = yearData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYearC = filterRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYearT = tKey === 'se' ? filterRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||
const filterDataYear = filterDataYearC.concat(filterDataYearT);
|
||||
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
// console.log(tKey, filterDataYear.map(ee => ee.destinationCountry_AsJOSN), filterDataYear.map(ee => ee.productType), filterDataYear);
|
||||
|
||||
const filterDataYearRaw2 = yearData2.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYear2C = filterDataYearRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYear2T = tKey === 'se' ? filterDataYearRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||
const filterDataYear2 = filterDataYear2C.concat(filterDataYear2T);
|
||||
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
const rowYear = {
|
||||
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||
};
|
||||
|
||||
const cols = ['YJLY', 'CJCount'].reduce((r, key) => ({ ...r, [key]: filterData.reduce((a, c) => a + price_to_number(c[key]), 0) }), {});
|
||||
// console.log(tKey, filterData, filterDataYear, filterDataYear2);
|
||||
return { ...cols, GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||
};
|
||||
const dataSalesGHOther = (rawData, yearData, yearData2) => {
|
||||
const exceptContry = Object.values(GHCountryListSetting).reduce((r, c) => r.concat(c), []);
|
||||
// console.log('exceptContry', exceptContry);
|
||||
// console.log('OOoo rawData', rawData.map(e => e.destinationCountry_AsJOSN));
|
||||
const filterData = rawData
|
||||
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
// console.log('OOoo', filterData.map(e => e.destinationCountry_AsJOSN), filterData.map(e => e.productType));
|
||||
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||
|
||||
const filterDataYear = yearData
|
||||
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
// console.log('OOoo year', filterDataYear.map(e => e.destinationCountry_AsJOSN), filterDataYear.map(e => e.productType));
|
||||
|
||||
const filterDataYear2 = yearData2
|
||||
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
// console.log('Oo', filterDataYear2.map(e => e.destinationCountry_AsJOSN), filterDataYear2);
|
||||
// console.log('Oo row', rowYearData2);
|
||||
|
||||
const rowYear = {
|
||||
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||
};
|
||||
|
||||
return { GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||
};
|
||||
|
||||
class MeetingData {
|
||||
constructor(rootStore) {
|
||||
this.rootStore = rootStore;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
searchValues = {
|
||||
DateType: { key: 'applyDate', value: 'applyDate', label: '提交日期' },
|
||||
};
|
||||
|
||||
setSearchValues(body) {
|
||||
this.searchValues = body;
|
||||
}
|
||||
|
||||
GHTableData = [];
|
||||
GHTableLoading = false;
|
||||
/**
|
||||
* 获取市场订单数据 ---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
dataGHOrder = async (param) => {
|
||||
// console.log('dataGH', param);
|
||||
this.GHTableLoading = true;
|
||||
const defaultParam = { DateType: 'applyDate' };
|
||||
// 本周
|
||||
const CHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '1', 'WebCode': 'All' });
|
||||
const exceptCHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '28,33', 'WebCode': 'All' });
|
||||
|
||||
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||
/** 截至今年 - 行 */
|
||||
const { ordercountTotal1: CHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||
const { ordercount1: exceptCHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '28,33', 'WebCode': 'All', OrderType: 'Product' });
|
||||
/** 截至今年 - 列 */
|
||||
const { ordercount1: ColLineClassDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||
const { ordercountTotal1: ColToBDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'GHTOBHW,GHTOBZG', OrderType: 'LineClass' });
|
||||
const { ordercountTotal1: ColExternalDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'ZWQD', OrderType: 'LineClass' });
|
||||
// 老客户
|
||||
const yearDetail = await getDetailData({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All' });
|
||||
const { isOld1: isOld1Year } = rowItem(yearDetail);
|
||||
|
||||
const colYearRow = {
|
||||
LineClass_Origin: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||
LineClass_PPC: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||
toB: ColToBDataYear.OrderCount,
|
||||
isOld1: isOld1Year,
|
||||
external: ColExternalDataYear.OrderCount,
|
||||
};
|
||||
|
||||
const rows = [
|
||||
{ key: 'ch', label: '中国', ...rowItem(CHData), rowYear: CHDataYear.OrderCount },
|
||||
{ key: 'ja', label: '日本+', ...dataJA(exceptCHData, exceptCHDataYear) },
|
||||
{ key: 'se', label: '东南亚+', ...dataSE(exceptCHData, exceptCHDataYear) },
|
||||
{ key: 'in', label: '印度+', ...dataIN(exceptCHData, exceptCHDataYear) },
|
||||
{ key: 'other', label: '其他GH', ...dataGHOther(exceptCHData, exceptCHDataYear) },
|
||||
];
|
||||
const columnsSum = ['LineClass_Origin', 'LineClass_PPC', 'toB', 'external', 'isOld1', 'total', 'rowYear'].reduce(
|
||||
(r, col) => ({
|
||||
...r,
|
||||
[col]: rows.reduce((rr, row) => rr + row[col], 0),
|
||||
}),
|
||||
{}
|
||||
);
|
||||
rows.push({ key: 'columnSum', label: '合计', ...columnsSum });
|
||||
rows.push({ key: 'colYearRow', label: '截至', ...colYearRow });
|
||||
runInAction(() => {
|
||||
this.GHTableData = rows;
|
||||
this.GHTableLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
GHSalesTableData = [];
|
||||
GHSalesLoading = false;
|
||||
/**
|
||||
* 获取GH销售数据 ---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
dataGHSales = async (param) => {
|
||||
this.GHSalesLoading = true;
|
||||
const salesParam = { ...param, DateType: 'confirmDate', 'WebCode': 'All', };
|
||||
// console.log(param);
|
||||
const { total1: CHSalesDataWeek } = await getDepartmentOrderMLByType({...salesParam, DepartmentList: '1', OrderType:'ALL'});
|
||||
const exceptCHSalesDataWeek = await getDetailData({ ...salesParam, 'DepartmentList': '28,33' });
|
||||
const GHDataWeekConfirm = exceptCHSalesDataWeek.filter((ele) => Number(ele.orderState) === 1); // 成交的
|
||||
|
||||
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||
const yearEnd = moment().endOf("year").format(SMALL_DATETIME_FORMAT);
|
||||
/** 截至今年 - 成交 */
|
||||
const { total1: CHDataYear } = await getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '1', OrderType:'ALL' });
|
||||
const GHDataYear = await getDetailData({ ...salesParam, Date1: yearStart, 'DepartmentList': '28,33' });
|
||||
const GHDataYearConfirm = GHDataYear.filter((ele) => Number(ele.orderState) === 1); // 成交的
|
||||
/** 截至今年 - 走团 */
|
||||
const { total1: CHStartDataYear } = await getDepartmentOrderMLByType({...salesParam, Date1: yearStart,Date2:yearEnd,DepartmentList: '1', OrderType:'ALL',DateType: 'startDate' });
|
||||
const GHStartDataYear0 = await getDetailData({ ...salesParam, DateType: 'startDate', 'DepartmentList': '28,33', Date1: yearStart,Date2:yearEnd, });
|
||||
const GHStartDataYear = GHStartDataYear0.filter((ele) => Number(ele.orderState) === 1); // 成交的
|
||||
|
||||
const rows = [
|
||||
{
|
||||
key: 'ch',
|
||||
label: '中国',
|
||||
YJLY: price_to_number(CHSalesDataWeek.COLI_ML2),
|
||||
CJCount: (CHSalesDataWeek.COLI_CJCount),
|
||||
rowYear: { YJLY: price_to_number(CHDataYear.COLI_ML2), CJCount: CHDataYear.COLI_CJCount, YJLY2: price_to_number(CHStartDataYear.COLI_ML2) },
|
||||
},
|
||||
{ key: 'ja', label: '日本+', ...dataSales('ja', GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||
{ key: 'se', label: '东南亚+', ...dataSales('se', GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||
{ key: 'in', label: '印度+', ...dataSales('in', GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||
{ key: 'other', label: '其他GH', ...dataSalesGHOther(GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||
];
|
||||
const columnsSum = ['CJCount', 'YJLY'].reduce((r, col) => ({ ...r, [col]: rows.reduce((rr, row) => rr + row[col], 0) }), {});
|
||||
const allYearData = rows.map(row => row.rowYear);
|
||||
const rowYear = ['CJCount', 'YJLY', 'CJCount2', 'YJLY2', ].reduce((r, col) => ({ ...r, [col]: allYearData.reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||
rows.push({ key: 'columnSum', label: '合计', ...columnsSum, rowYear });
|
||||
// console.log(rows, allYearData, rowYear);
|
||||
runInAction(() => {
|
||||
this.GHSalesTableData = rows;
|
||||
this.GHSalesLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
GHServiceTableData = [];
|
||||
GHServiceLoading = false;
|
||||
/**
|
||||
* 获取GH服务数据 ---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
dataGHService = async (param) => {
|
||||
this.GHServiceLoading = true;
|
||||
const serviceParam = { ...param, DateType: 'startDate', 'WebCode': 'All' };
|
||||
// 走团数
|
||||
const { ordercountTotal1: { OrderCount: GroupCount } } = await getOrderCountByType({ ...serviceParam, 'DepartmentList': '1', OrderType: 'Form' });
|
||||
const exceptCHDataWeek = await getDetailData({ ...serviceParam, 'DepartmentList': '28,33' });
|
||||
// 走团数 - 年
|
||||
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||
const { ordercountTotal1: { OrderCount: GroupCountYear } } = await getOrderCountByType({ ...serviceParam, 'DepartmentList': '1', OrderType: 'Form', Date1: yearStart, });
|
||||
const exceptCHDataYear = await getDetailData({ ...serviceParam, Date1: yearStart, 'DepartmentList': '28,33' });
|
||||
// 好评数
|
||||
const { total1: { GoodCount } } = await getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '1', });
|
||||
const { total1: { GoodCount: GoodCountYear } } = await getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '1', });
|
||||
const { total1: { GoodCount: GHGoodCountWeek } } = await getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '28,33', });
|
||||
const { total1: { GoodCount: GHGoodCountYear } } = await getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '28,33', });
|
||||
|
||||
const rows = [
|
||||
{ key: 'ch', label: '中国', ...{GoodCount, GroupCount}, rowYear: { GroupCount: GroupCountYear, GoodCount: GoodCountYear } },
|
||||
{ key: 'ja', label: '日本+', ...dataSales('ja', exceptCHDataWeek, exceptCHDataYear, []) },
|
||||
{ key: 'se', label: '东南亚+', ...dataSales('se', exceptCHDataWeek, exceptCHDataYear, []) },
|
||||
{ key: 'in', label: '印度+', ...dataSales('in', exceptCHDataWeek, exceptCHDataYear, []) },
|
||||
{ key: 'other', label: '其他GH', ...dataSalesGHOther(exceptCHDataWeek, exceptCHDataYear, []) },
|
||||
];
|
||||
const GHRowWeek = { GoodCount: GHGoodCountWeek, GroupCount: 0 };
|
||||
const columnsSum = ['GoodCount', 'GroupCount'].reduce((r, col) => ({ ...r, [col]: [...rows, GHRowWeek].reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||
const allYearData = rows.map((row) => row.rowYear).concat([{ GoodCount: GHGoodCountYear, GroupCount: 0 }]);
|
||||
// console.log(allYearData);
|
||||
const rowYear = ['GoodCount', 'GroupCount', ].reduce((r, col) => ({ ...r, [col]: allYearData.reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||
rows.push({ key: 'columnSum', label: '合计', ...columnsSum, rowYear });
|
||||
// console.log(rows);
|
||||
runInAction(() => {
|
||||
this.GHServiceTableData = rows;
|
||||
this.GHServiceLoading = false;
|
||||
});
|
||||
};
|
||||
}
|
||||
export default MeetingData;
|
@ -0,0 +1,479 @@
|
||||
import { makeAutoObservable, runInAction } from 'mobx';
|
||||
import { fetchJSON } from '../utils/request';
|
||||
import { objectMapper, pick, price_to_number, } from '../utils/commons';
|
||||
import { pivotBy } from './../libs/ht';
|
||||
import moment from "moment";
|
||||
import { DATE_FORMAT, DATETIME_FORMAT, SMALL_DATETIME_FORMAT } from '../config';
|
||||
|
||||
/**
|
||||
* 用于透视的数据
|
||||
*/
|
||||
const getDetailData = async (param) => {
|
||||
const json = await fetchJSON('/service-Analyse2/GetTradeApartDetail', param);
|
||||
return json.errcode === 0 ? json.result : [];
|
||||
};
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const getOrderCountByType = async (param) => {
|
||||
const paramBody = objectMapper(param, {
|
||||
WebCode: 'WebCode',
|
||||
OrderType: 'OrderType',
|
||||
IncludeTickets: 'IncludeTickets',
|
||||
DateType: 'DateType',
|
||||
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||
Date1: 'COLI_ApplyDate1',
|
||||
Date2: 'COLI_ApplyDate2',
|
||||
});
|
||||
const url = '/service-web/QueryData/GetOrderCountByType';
|
||||
const json = await fetchJSON(url, paramBody);
|
||||
return json.errcode === 0 ? json : {};
|
||||
};
|
||||
const getAgentGroupInfoALL = async (param) => {
|
||||
const paramBody = objectMapper(param, {
|
||||
DateType: 'DateType',
|
||||
DepartmentList: 'DepList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||
Date1: 'OldDate1',
|
||||
Date2: 'OldDate2',
|
||||
});
|
||||
const url = '/service-web/QueryData/GetAgentGroupInfoALL';
|
||||
const json = await fetchJSON(url, paramBody);
|
||||
return json.errcode === 0 ? json : {};
|
||||
};
|
||||
const getDepartmentOrderMLByType = async (param) => {
|
||||
const paramBody = objectMapper(param, {
|
||||
DateType: 'DateType',
|
||||
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||
OrderType: 'OrderType', // 总览, 产品类型
|
||||
});
|
||||
const url = '/service-web/QueryData/GetDepartmentOrderMLByType';
|
||||
const json = await fetchJSON(url, paramBody);
|
||||
const { result1 } = json.errcode === 0 ? json : { result1: [] };
|
||||
const total1 = ['COLI_CJCount', 'COLI_ML2',].reduce(
|
||||
(r, col) => ({
|
||||
...r,
|
||||
[col]: result1.reduce((rr, row) => rr + row[col], 0),
|
||||
}),
|
||||
{}
|
||||
);
|
||||
return { total1, result1 };
|
||||
};
|
||||
const GHproductTypeListSetting = {
|
||||
ja: ['日本', '东亚跨国'],
|
||||
se: ['东南亚跨国', '泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝'],
|
||||
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||
};
|
||||
const GHCountryListSetting = {
|
||||
ja: ['日本', ],
|
||||
se: ['泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝', '新加坡', '马来西亚', '菲律宾'],
|
||||
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||
};
|
||||
|
||||
const rowItem = (filterData) => {
|
||||
const { data: dataByLineClass, summaryMix: summaryByLineClass } = pivotBy(filterData, [['COLI_LineClass'], [], []]);
|
||||
const LineClass_Origin = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
const LineClass_PPC = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
|
||||
const { data: dataByWebCode, summaryMix: summaryByWebCode } = pivotBy(filterData, [['WebCode'], [], []]);
|
||||
const toB = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
const external = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf("站外渠道") !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
|
||||
const filterIsOldC = filterData.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') === -1);
|
||||
const { data: dataByIsOld, summaryMix: summaryByIsOld } = pivotBy(filterIsOldC, [['IsOld', 'isCusCommend'], [], []]);
|
||||
const isOld1 = dataByIsOld.filter((ele) => ele.rowLabel.indexOf('1') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||
|
||||
const total = LineClass_Origin + LineClass_PPC + toB + isOld1 + external;
|
||||
return { LineClass_Origin, LineClass_PPC, toB, external, isOld1, total };
|
||||
};
|
||||
// 日本+: 日本+东亚跨国
|
||||
const dataJA = (rawData, yearData) => {
|
||||
const productTypeList = GHproductTypeListSetting.ja;
|
||||
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
// 东南亚+: 东南亚跨国+泰国+越南+印尼+水灯节线路
|
||||
const dataSE = (rawData, yearData) => {
|
||||
const productTypeList = GHproductTypeListSetting.se;
|
||||
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
// 印度+: 印度+次大陆跨国+尼泊尔+不丹+斯里兰卡
|
||||
const dataIN = (rawData, yearData) => {
|
||||
const productTypeList = GHproductTypeListSetting.in;
|
||||
const exceptProduct = ['印度尼西亚'];
|
||||
const filterData = rawData
|
||||
.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||
.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const filterDataYear = yearData
|
||||
.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||
.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
// 其他GH
|
||||
const dataGHOther = (rawData, yearData) => {
|
||||
const exceptProduct = Object.values(GHproductTypeListSetting).reduce((r, c) => r.concat(c), []);
|
||||
const filterData = rawData.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const filterDataYear = yearData.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||
return { ...rowItem(filterData), rowYear };
|
||||
};
|
||||
|
||||
const dataSales = (tKey, rawData, yearData, yearData2) => {
|
||||
const targetList = GHCountryListSetting[tKey];
|
||||
const tIndex = Object.keys(GHCountryListSetting).indexOf(tKey);
|
||||
const exceptTargetList = Object.keys(GHCountryListSetting).reduce((r, c, i) => r.concat(i < tIndex ? GHCountryListSetting[c] : []), []);
|
||||
// console.log(tIndex, tKey, 'exceptTargetList', exceptTargetList, 'targetList', targetList);
|
||||
|
||||
const filterRaw1 = rawData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
// console.log(tKey, 'filterRaw1', filterRaw1);
|
||||
const filterDataC = filterRaw1.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataT = tKey === 'se' ? filterRaw1.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||
const filterData = filterDataC.concat(filterDataT);
|
||||
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||
|
||||
const filterRaw2 = yearData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYearC = filterRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYearT = tKey === 'se' ? filterRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||
const filterDataYear = filterDataYearC.concat(filterDataYearT);
|
||||
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
// console.log(tKey, filterDataYear.map(ee => ee.destinationCountry_AsJOSN), filterDataYear.map(ee => ee.productType), filterDataYear);
|
||||
|
||||
const filterDataYearRaw2 = yearData2.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYear2C = filterDataYearRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const filterDataYear2T = tKey === 'se' ? filterDataYearRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||
const filterDataYear2 = filterDataYear2C.concat(filterDataYear2T);
|
||||
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
const rowYear = {
|
||||
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||
};
|
||||
|
||||
const cols = ['YJLY', 'CJCount'].reduce((r, key) => ({ ...r, [key]: filterData.reduce((a, c) => a + price_to_number(c[key]), 0) }), {});
|
||||
// console.log(tKey, filterData, filterDataYear, filterDataYear2);
|
||||
return { ...cols, GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||
};
|
||||
const dataSalesGHOther = (rawData, yearData, yearData2) => {
|
||||
const exceptContry = Object.values(GHCountryListSetting).reduce((r, c) => r.concat(c), []);
|
||||
// console.log('exceptContry', exceptContry);
|
||||
// console.log('OOoo rawData', rawData.map(e => e.destinationCountry_AsJOSN));
|
||||
const filterData = rawData
|
||||
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
// console.log('OOoo', filterData.map(e => e.destinationCountry_AsJOSN), filterData.map(e => e.productType));
|
||||
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||
|
||||
const filterDataYear = yearData
|
||||
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
// console.log('OOoo year', filterDataYear.map(e => e.destinationCountry_AsJOSN), filterDataYear.map(e => e.productType));
|
||||
|
||||
const filterDataYear2 = yearData2
|
||||
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||
// console.log('Oo', filterDataYear2.map(e => e.destinationCountry_AsJOSN), filterDataYear2);
|
||||
// console.log('Oo row', rowYearData2);
|
||||
|
||||
const rowYear = {
|
||||
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||
};
|
||||
|
||||
return { GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||
};
|
||||
|
||||
class MeetingData {
|
||||
constructor(rootStore) {
|
||||
this.rootStore = rootStore;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
searchValues = {
|
||||
DateType: { key: 'applyDate', value: 'applyDate', label: '提交日期' },
|
||||
};
|
||||
|
||||
setSearchValues(body) {
|
||||
this.searchValues = body;
|
||||
}
|
||||
|
||||
GHTableData = [];
|
||||
GHTableLoading = false;
|
||||
/**
|
||||
* 获取市场订单数据 ---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
dataGHOrder = async (param) => {
|
||||
// console.log('dataGH', param);
|
||||
this.GHTableLoading = true;
|
||||
const defaultParam = { DateType: 'applyDate' };
|
||||
// 本周
|
||||
const CHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '1', 'WebCode': 'All' });
|
||||
const exceptCHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '28,33', 'WebCode': 'All' });
|
||||
|
||||
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||
/** 截至今年 - 行 */
|
||||
const [
|
||||
{ ordercountTotal1: CHDataYear },
|
||||
{ ordercount1: exceptCHDataYear },
|
||||
] = await Promise.all([
|
||||
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1', 'WebCode': 'All', OrderType: 'LineClass' }),
|
||||
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '28,33', 'WebCode': 'All', OrderType: 'Product' }),
|
||||
]);
|
||||
// const { ordercountTotal1: CHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||
// const { ordercount1: exceptCHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '28,33', 'WebCode': 'All', OrderType: 'Product' });
|
||||
/** 截至今年 - 列 */
|
||||
const [
|
||||
{ ordercount1: ColLineClassDataYear },
|
||||
{ ordercountTotal1: ColToBDataYear },
|
||||
{ ordercountTotal1: ColExternalDataYear },
|
||||
] = await Promise.all([
|
||||
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All', OrderType: 'LineClass' }),
|
||||
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'GHTOBHW,GHTOBZG', OrderType: 'LineClass'}),
|
||||
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'ZWQD', OrderType: 'LineClass'}),
|
||||
]);
|
||||
// const { ordercount1: ColLineClassDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||
// const { ordercountTotal1: ColToBDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'GHTOBHW,GHTOBZG', OrderType: 'LineClass' });
|
||||
// const { ordercountTotal1: ColExternalDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'ZWQD', OrderType: 'LineClass' });
|
||||
// 老客户
|
||||
const yearDetail = await getDetailData({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All' });
|
||||
const { isOld1: isOld1Year } = rowItem(yearDetail);
|
||||
|
||||
const colYearRow = {
|
||||
LineClass_Origin: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||
LineClass_PPC: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||
toB: ColToBDataYear.OrderCount,
|
||||
isOld1: isOld1Year,
|
||||
external: ColExternalDataYear.OrderCount,
|
||||
};
|
||||
|
||||
const rows = [
|
||||
{ key: 'ch', label: '中国', ...rowItem(CHData), rowYear: CHDataYear.OrderCount },
|
||||
{ key: 'ja', label: '日本+', ...dataJA(exceptCHData, exceptCHDataYear) },
|
||||
{ key: 'se', label: '东南亚+', ...dataSE(exceptCHData, exceptCHDataYear) },
|
||||
{ key: 'in', label: '印度+', ...dataIN(exceptCHData, exceptCHDataYear) },
|
||||
{ key: 'other', label: '其他GH', ...dataGHOther(exceptCHData, exceptCHDataYear) },
|
||||
];
|
||||
const columnsSum = ['LineClass_Origin', 'LineClass_PPC', 'toB', 'external', 'isOld1', 'total', 'rowYear'].reduce(
|
||||
(r, col) => ({
|
||||
...r,
|
||||
[col]: rows.reduce((rr, row) => rr + row[col], 0),
|
||||
}),
|
||||
{}
|
||||
);
|
||||
rows.push({ key: 'columnSum', label: '合计', ...columnsSum });
|
||||
rows.push({ key: 'colYearRow', label: '截至', ...colYearRow });
|
||||
runInAction(() => {
|
||||
this.GHTableData = rows;
|
||||
this.GHTableLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
GHSalesTableData = [];
|
||||
GHSalesLoading = false;
|
||||
/**
|
||||
* 获取GH销售数据 ---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
dataGHSales = async (param) => {
|
||||
this.GHSalesLoading = true;
|
||||
const salesParam = { ...param, DateType: 'confirmDate', WebCode: 'CHT,AH,GH,GHKYZG,GHKYHW,ZWQD', OrderType:'ALL', }; // WebCode: 不含分销
|
||||
const partnerParam = { WebCode: 'GHTOBHW,GHTOBZG' };
|
||||
const [
|
||||
{ total1: CHSalesDataTotal },
|
||||
{ total1: CHPartnerSalesData },
|
||||
{ total1: AHSalesDataTotal },
|
||||
{ total1: AHpartnerSalesData },
|
||||
{ total1: GHSalesDataTotal },
|
||||
{ total1: GHpartnerSalesData },
|
||||
] = await Promise.all([
|
||||
getDepartmentOrderMLByType({...salesParam, DepartmentList: '1,2', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, DepartmentList: '1,2', ...partnerParam }),
|
||||
getDepartmentOrderMLByType({...salesParam, DepartmentList: '28', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, DepartmentList: '28', ...partnerParam }),
|
||||
getDepartmentOrderMLByType({...salesParam, DepartmentList: '33', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, DepartmentList: '33', ...partnerParam }),
|
||||
]);
|
||||
// 不含分销 = 总额 - 分销
|
||||
const CHSalesData = {'COLI_CJCount': CHSalesDataTotal.COLI_CJCount-CHPartnerSalesData.COLI_CJCount, 'COLI_ML2': CHSalesDataTotal.COLI_ML2-CHPartnerSalesData.COLI_ML2};
|
||||
const AHSalesData = {'COLI_CJCount': AHSalesDataTotal.COLI_CJCount-AHpartnerSalesData.COLI_CJCount, 'COLI_ML2': AHSalesDataTotal.COLI_ML2-AHpartnerSalesData.COLI_ML2};
|
||||
const GHSalesData = {'COLI_CJCount': GHSalesDataTotal.COLI_CJCount-GHpartnerSalesData.COLI_CJCount, 'COLI_ML2': GHSalesDataTotal.COLI_ML2-GHpartnerSalesData.COLI_ML2};
|
||||
const partnerSalesData = {'COLI_CJCount': CHPartnerSalesData.COLI_CJCount+AHpartnerSalesData.COLI_CJCount, 'COLI_ML2': CHPartnerSalesData.COLI_ML2+AHpartnerSalesData.COLI_ML2};
|
||||
const totalSalesData = {
|
||||
'COLI_CJCount': CHSalesDataTotal.COLI_CJCount + AHSalesDataTotal.COLI_CJCount + GHSalesDataTotal.COLI_CJCount,
|
||||
'COLI_ML2': CHSalesDataTotal.COLI_ML2 + AHSalesDataTotal.COLI_ML2 + GHSalesDataTotal.COLI_ML2,
|
||||
};
|
||||
|
||||
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||
const yearEnd = moment().endOf("year").format(SMALL_DATETIME_FORMAT);
|
||||
/** 截至今年 - 成交 */
|
||||
const [
|
||||
{ total1: CHSalesYearTotal },
|
||||
{ total1: CHPartnerSalesYear },
|
||||
{ total1: AHSalesYearTotal },
|
||||
{ total1: AHpartnerSalesYear },
|
||||
{ total1: GHSalesYearTotal },
|
||||
{ total1: GHpartnerSalesYear },
|
||||
] = await Promise.all([
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '1,2', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '1,2', ...partnerParam }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '28', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '28', ...partnerParam }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '33', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '33', ...partnerParam }),
|
||||
]);
|
||||
const CHDataYear = {'COLI_CJCount': CHSalesYearTotal.COLI_CJCount-CHPartnerSalesYear.COLI_CJCount, 'COLI_ML2': CHSalesYearTotal.COLI_ML2-CHPartnerSalesYear.COLI_ML2};
|
||||
const AHDataYear = {'COLI_CJCount': AHSalesYearTotal.COLI_CJCount-AHpartnerSalesYear.COLI_CJCount, 'COLI_ML2': AHSalesYearTotal.COLI_ML2-AHpartnerSalesYear.COLI_ML2};
|
||||
const GHDataYear = {'COLI_CJCount': GHSalesYearTotal.COLI_CJCount-GHpartnerSalesYear.COLI_CJCount, 'COLI_ML2': GHSalesYearTotal.COLI_ML2-GHpartnerSalesYear.COLI_ML2};
|
||||
const partnerDataYear = {'COLI_CJCount': CHPartnerSalesYear.COLI_CJCount+AHpartnerSalesYear.COLI_CJCount, 'COLI_ML2': CHPartnerSalesYear.COLI_ML2+AHpartnerSalesYear.COLI_ML2};
|
||||
const totalDataYear = {
|
||||
'COLI_CJCount': CHSalesYearTotal.COLI_CJCount + AHSalesYearTotal.COLI_CJCount + GHSalesYearTotal.COLI_CJCount,
|
||||
'COLI_ML2': CHSalesYearTotal.COLI_ML2 + AHSalesYearTotal.COLI_ML2 + GHSalesYearTotal.COLI_ML2,
|
||||
};
|
||||
/** 截至今年 - 走团 */
|
||||
const [
|
||||
{ total1: CHStartDataYearTotal },
|
||||
{ total1: CHPartnerStartDataYear },
|
||||
{ total1: AHStartDataYearTotal },
|
||||
{ total1: AHpartnerStartDataYear },
|
||||
{ total1: GHStartDataYearTotal },
|
||||
{ total1: GHpartnerStartDataYear },
|
||||
] = await Promise.all([
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '1,2', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '1,2', ...partnerParam }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '28', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '28', ...partnerParam }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '33', WebCode: 'ALL', }),
|
||||
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '33', ...partnerParam }),
|
||||
]);
|
||||
const CHStartDataYear = {'COLI_CJCount': CHStartDataYearTotal.COLI_CJCount-CHPartnerStartDataYear.COLI_CJCount, 'COLI_ML2': CHStartDataYearTotal.COLI_ML2-CHPartnerStartDataYear.COLI_ML2};
|
||||
const AHStartDataYear = {'COLI_CJCount': AHStartDataYearTotal.COLI_CJCount-AHpartnerStartDataYear.COLI_CJCount, 'COLI_ML2': AHStartDataYearTotal.COLI_ML2-AHpartnerStartDataYear.COLI_ML2};
|
||||
const GHStartDataYear = {'COLI_CJCount': GHStartDataYearTotal.COLI_CJCount-GHpartnerStartDataYear.COLI_CJCount, 'COLI_ML2': GHStartDataYearTotal.COLI_ML2-GHpartnerStartDataYear.COLI_ML2};
|
||||
const partnerStartDataYear = {'COLI_CJCount': CHPartnerStartDataYear.COLI_CJCount+AHpartnerStartDataYear.COLI_CJCount, 'COLI_ML2': CHPartnerStartDataYear.COLI_ML2+AHpartnerStartDataYear.COLI_ML2};
|
||||
const totalStartDataYear = {
|
||||
'COLI_CJCount': CHStartDataYearTotal.COLI_CJCount + AHStartDataYearTotal.COLI_CJCount + GHStartDataYearTotal.COLI_CJCount,
|
||||
'COLI_ML2': CHStartDataYearTotal.COLI_ML2 + AHStartDataYearTotal.COLI_ML2 + GHStartDataYearTotal.COLI_ML2,
|
||||
};
|
||||
|
||||
const rows = [
|
||||
{
|
||||
key: 'ch',
|
||||
label: 'CH(不含分销)',
|
||||
YJLY: price_to_number(CHSalesData.COLI_ML2),
|
||||
CJCount: (CHSalesData.COLI_CJCount),
|
||||
rowYear: { YJLY: price_to_number(CHDataYear.COLI_ML2), CJCount: CHDataYear.COLI_CJCount, YJLY2: price_to_number(CHStartDataYear.COLI_ML2) },
|
||||
},
|
||||
{
|
||||
key: 'ah',
|
||||
label: 'AH(不含分销)',
|
||||
YJLY: price_to_number(AHSalesData.COLI_ML2),
|
||||
CJCount: (AHSalesData.COLI_CJCount),
|
||||
rowYear: { YJLY: price_to_number(AHDataYear.COLI_ML2), CJCount: AHDataYear.COLI_CJCount, YJLY2: price_to_number(AHStartDataYear.COLI_ML2) },
|
||||
},
|
||||
{
|
||||
key: 'partner',
|
||||
label: '分销',
|
||||
YJLY: price_to_number(partnerSalesData.COLI_ML2),
|
||||
CJCount: (partnerSalesData.COLI_CJCount),
|
||||
rowYear: { YJLY: price_to_number(partnerDataYear.COLI_ML2), CJCount: partnerDataYear.COLI_CJCount, YJLY2: price_to_number(partnerStartDataYear.COLI_ML2) },
|
||||
},
|
||||
{
|
||||
key: 'gh',
|
||||
label: 'GH(不含分销)',
|
||||
YJLY: price_to_number(GHSalesData.COLI_ML2),
|
||||
CJCount: (GHSalesData.COLI_CJCount),
|
||||
rowYear: { YJLY: price_to_number(GHDataYear.COLI_ML2), CJCount: GHDataYear.COLI_CJCount, YJLY2: price_to_number(GHStartDataYear.COLI_ML2) },
|
||||
},
|
||||
{
|
||||
key: 'total',
|
||||
label: '合计',
|
||||
YJLY: price_to_number(totalSalesData.COLI_ML2),
|
||||
CJCount: (totalSalesData.COLI_CJCount),
|
||||
rowYear: { YJLY: price_to_number(totalDataYear.COLI_ML2), CJCount: totalDataYear.COLI_CJCount, YJLY2: price_to_number(totalStartDataYear.COLI_ML2) },
|
||||
},
|
||||
];
|
||||
runInAction(() => {
|
||||
this.GHSalesTableData = rows;
|
||||
this.GHSalesLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
GHServiceTableData = [];
|
||||
GHServiceLoading = false;
|
||||
/**
|
||||
* 获取GH服务数据 ---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
dataGHService = async (param) => {
|
||||
this.GHServiceLoading = true;
|
||||
const serviceParam = { ...param, DateType: 'startDate', 'WebCode': 'All' };
|
||||
// 走团数
|
||||
const [
|
||||
{ ordercountTotal1: { OrderCount: CHGroupCount } },
|
||||
{ ordercountTotal1: { OrderCount: AHGroupCount } },
|
||||
{ ordercountTotal1: { OrderCount: GHGroupCount } },
|
||||
] = await Promise.all([
|
||||
getOrderCountByType({ ...serviceParam, 'DepartmentList': '1,2', OrderType: 'Form'}),
|
||||
getOrderCountByType({ ...serviceParam, 'DepartmentList': '28', OrderType: 'Form'}),
|
||||
getOrderCountByType({ ...serviceParam, 'DepartmentList': '33', OrderType: 'Form'}),
|
||||
]);
|
||||
// 走团数 - 年
|
||||
// * 走团: 整团数, 而非各地接社/目的地的总和
|
||||
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||
const [
|
||||
{ ordercountTotal1: { OrderCount: CHGroupCountYear } },
|
||||
{ ordercountTotal1: { OrderCount: AHGroupCountYear } },
|
||||
{ ordercountTotal1: { OrderCount: GHGroupCountYear } },
|
||||
] = await Promise.all([
|
||||
getOrderCountByType({ ...serviceParam, 'DepartmentList': '1,2', OrderType: 'Form', Date1: yearStart, }),
|
||||
getOrderCountByType({ ...serviceParam, 'DepartmentList': '28', OrderType: 'Form', Date1: yearStart, }),
|
||||
getOrderCountByType({ ...serviceParam, 'DepartmentList': '33', OrderType: 'Form', Date1: yearStart, }),
|
||||
]);
|
||||
// 好评数
|
||||
const [
|
||||
{ total1: { GoodCount: CHGoodCount } },
|
||||
{ total1: { GoodCount: AHGoodCount } },
|
||||
{ total1: { GoodCount: GHGoodCount } },
|
||||
] = await Promise.all([
|
||||
getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '1,2', }),
|
||||
getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '28', }),
|
||||
getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '33', }),
|
||||
]);
|
||||
// 好评数 - 年
|
||||
const [
|
||||
{ total1: { GoodCount: CHGoodCountYear, } },
|
||||
{ total1: { GoodCount: AHGoodCountYear, } },
|
||||
{ total1: { GoodCount: GHGoodCountYear, } },
|
||||
] = await Promise.all([
|
||||
getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '1,2', }),
|
||||
getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '28', }),
|
||||
getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '33', }),
|
||||
]);
|
||||
|
||||
const rows = [
|
||||
{ key: 'ch', label: 'CH', GoodCount: CHGoodCount, GroupCount: CHGroupCount, rowYear: { GroupCount: CHGroupCountYear, GoodCount: CHGoodCountYear } },
|
||||
{ key: 'ah', label: 'AH', GoodCount: AHGoodCount, GroupCount: AHGroupCount, rowYear: { GroupCount: AHGroupCountYear, GoodCount: AHGoodCountYear } },
|
||||
{ key: 'Gh', label: 'GH', GoodCount: GHGoodCount, GroupCount: GHGroupCount, rowYear: { GroupCount: GHGroupCountYear, GoodCount: GHGoodCountYear } },
|
||||
];
|
||||
// const GHRowWeek = { GoodCount: GHGoodCountWeek, GroupCount: 0 };
|
||||
// const columnsSum = ['GoodCount', 'GroupCount'].reduce((r, col) => ({ ...r, [col]: [...rows, GHRowWeek].reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||
// const allYearData = rows.map((row) => row.rowYear).concat([{ GoodCount: GHGoodCountYear, GroupCount: 0 }]);
|
||||
// // console.log(allYearData);
|
||||
// const rowYear = ['GoodCount', 'GroupCount', ].reduce((r, col) => ({ ...r, [col]: allYearData.reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||
// rows.push({ key: 'columnSum', label: '合计', ...columnsSum, rowYear });
|
||||
// console.log(rows);
|
||||
runInAction(() => {
|
||||
this.GHServiceTableData = rows;
|
||||
this.GHServiceLoading = false;
|
||||
});
|
||||
};
|
||||
}
|
||||
export default MeetingData;
|
@ -0,0 +1,210 @@
|
||||
import { makeAutoObservable, runInAction } from 'mobx';
|
||||
import { fetchJSON } from '../utils/request';
|
||||
import { isEmpty, sortDescBy, groupBy, pick, unique } from '../utils/commons';
|
||||
import { groupsMappedByCode } from './../libs/ht';
|
||||
import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from './../config';
|
||||
import moment from 'moment';
|
||||
|
||||
const fetchResultsData = async (param) => {
|
||||
const defaultParam = {
|
||||
WebCode: 'All',
|
||||
DepartmentList: '',
|
||||
opisn: -1,
|
||||
Date1: '',
|
||||
Date2: '',
|
||||
groupType: '',
|
||||
groupDateType: '',
|
||||
};
|
||||
const json = await fetchJSON('/service-Analyse2/sales_crm_results', { ...defaultParam, ...param });
|
||||
return json.errcode === 0 ? json.result : [];
|
||||
};
|
||||
|
||||
const fetchProcessData = async (param) => {
|
||||
const defaultParam = {
|
||||
WebCode: 'All',
|
||||
DepartmentList: '',
|
||||
opisn: -1,
|
||||
Date1: '',
|
||||
Date2: '',
|
||||
groupType: '',
|
||||
groupDateType: '',
|
||||
};
|
||||
const json = await fetchJSON('/service-Analyse2/sales_crm_process', { ...defaultParam, ...param });
|
||||
return json.errcode === 0 ? json.result : [];
|
||||
};
|
||||
|
||||
const fetchRiskDetailData = async (param) => {
|
||||
const defaultParam = {
|
||||
opisn: -1,
|
||||
DateType: '',
|
||||
WebCode: 'All',
|
||||
DepartmentList: '',
|
||||
Date1: '',
|
||||
Date2: '',
|
||||
IncludeTickets: '1',
|
||||
};
|
||||
const json = await fetchJSON('/service-Analyse2/sales_crm_process_detail', {...defaultParam, ...param});
|
||||
return json.errcode === 0 ? json.result : [];
|
||||
};
|
||||
|
||||
class SalesCRMData {
|
||||
constructor(appStore) {
|
||||
this.appStore = appStore;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
async get90n180Data(param = {}) {
|
||||
const retProps = param?.retLabel || '';
|
||||
|
||||
let retKey = param.groupDateType === '' ? (param.groupType === 'overview' ? 'dataSource' : 'details') : 'byDate';
|
||||
retKey = param.opisn ? `operator_${param.opisn}`: retKey;
|
||||
if (param.opisn ) {
|
||||
if (!isEmpty(this.results.details)) {
|
||||
const _this_opi_row = this.results.details.filter(ele => ele.groupsKey === param.opisn);
|
||||
this.results[retKey] = _this_opi_row;
|
||||
return;
|
||||
}
|
||||
if (!isEmpty(this.results[retKey])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.results.loading = true;
|
||||
const date90=this.searchValues.date90;
|
||||
const date180=this.searchValues.date180;
|
||||
const [result90, result180] = await Promise.all([
|
||||
fetchResultsData({ ...this.searchValuesToSub, ...date90, ...param }),
|
||||
fetchResultsData({ ...this.searchValuesToSub, ...date180, ...param }),
|
||||
]);
|
||||
const _90O = groupBy(result90, 'groupsKey');
|
||||
const _180O = groupBy(result180, 'groupsKey');
|
||||
const result2 = unique(Object.keys(_90O).concat(Object.keys(_180O))).map((key) => {
|
||||
return {
|
||||
...pick(_90O[key]?.[0] || _180O[key][0], ['groupsKey', 'groupsLabel', 'groupType']),
|
||||
...(retProps && retKey === 'dataSource' ? { groupsLabel: retProps, retProps } : { retProps }),
|
||||
key: `${param.groupType}-${key}`,
|
||||
result90: _90O[key]?.[0] || {},
|
||||
result180: _180O[key]?.[0] || {},
|
||||
};
|
||||
});
|
||||
// console.log(result2, '+++++ +++', retKey);
|
||||
// console.log(this.results[retKey]?.length);
|
||||
|
||||
runInAction(() => {
|
||||
this.results.loading = false;
|
||||
this.results[retKey] = [].concat((this.results[retKey] || []), result2);
|
||||
});
|
||||
return this.results;
|
||||
}
|
||||
|
||||
async getResultData(param = {}) {
|
||||
let retKey = param.groupDateType === '' ? 'byOperator' : 'byDate';
|
||||
retKey = param.opisn ? `operator_byDate_${param.opisn}`: retKey;
|
||||
if (!isEmpty(this.results[retKey])) {
|
||||
return;
|
||||
}
|
||||
this.results.loading = true;
|
||||
this.results[retKey] = [];
|
||||
const res = await fetchResultsData({ ...this.searchValuesToSub, ...param });
|
||||
runInAction(() => {
|
||||
this.results.loading = false;
|
||||
this.results[retKey] = retKey === 'byOperator' ? res.filter(ele => ele.SumML > 0).sort(sortDescBy('SumML')) : res;
|
||||
});
|
||||
return this.results;
|
||||
};
|
||||
|
||||
async getProcessData(param = {}) {
|
||||
// const retKey = param.groupDateType === '' ? 'byOperator' : 'byDate';
|
||||
let retKey = param.groupDateType === '' ? (param.groupType !== 'operator' ? 'dataSource' : 'details') : 'byDate';
|
||||
retKey = param.opisn ? `operator_${param.opisn}`: retKey;
|
||||
if (param.opisn) {
|
||||
if (!isEmpty(this.process.details)) {
|
||||
const _this_opi_row = this.process.details.filter(ele => ele.groupsKey === param.opisn);
|
||||
this.process[retKey] = _this_opi_row;
|
||||
return;
|
||||
}
|
||||
if (!isEmpty(this.process[retKey])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.process.loading = true;
|
||||
this.process[retKey] = [];
|
||||
const res = await fetchProcessData({ ...this.searchValuesToSub, ...param });
|
||||
runInAction(() => {
|
||||
this.process.loading = false;
|
||||
this.process[retKey] = [].concat(this.process[retKey], res);
|
||||
});
|
||||
}
|
||||
|
||||
async getRiskDetailData(param = {}) {
|
||||
this.risk.loading = true;
|
||||
this.risk.dataSource = [];
|
||||
const res = await fetchRiskDetailData({ ...this.searchValuesToSub, ...param });
|
||||
runInAction(() => {
|
||||
this.risk.loading = false;
|
||||
this.risk.dataSource = res;
|
||||
this.risk.byLostType = groupBy(res, 'lost_type');
|
||||
});
|
||||
}
|
||||
|
||||
searchValues = {
|
||||
date: moment(),
|
||||
Date1: moment().startOf("week").subtract(7, "days"),
|
||||
Date2: moment().endOf("week").subtract(7, "days"),
|
||||
DateType: { key: 'applyDate', label: '提交日期'},
|
||||
WebCode: { key: 'All', label: '所有来源' },
|
||||
// IncludeTickets: { key: '1', label: '含门票'},
|
||||
DepartmentList: [groupsMappedByCode.GH],
|
||||
operator: '-1',
|
||||
opisn: '-1',
|
||||
date90: {
|
||||
Date1: moment().subtract(90, 'days').format(DATE_FORMAT),
|
||||
Date2: moment().subtract(30, 'days').format(SMALL_DATETIME_FORMAT),
|
||||
},
|
||||
date180: {
|
||||
Date1: moment().subtract(180, 'days').format(DATE_FORMAT),
|
||||
Date2: moment().subtract(50, 'days').format(SMALL_DATETIME_FORMAT),
|
||||
}
|
||||
};
|
||||
|
||||
searchValuesToSub = {
|
||||
date: moment().format(DATE_FORMAT),
|
||||
Date1: moment().startOf("week").subtract(7, "days").format(DATE_FORMAT),
|
||||
Date2: moment().endOf("week").subtract(7, "days").format(SMALL_DATETIME_FORMAT),
|
||||
DateType: 'applyDate',
|
||||
DepartmentList: groupsMappedByCode.GH.value,
|
||||
WebCode: 'All',
|
||||
operator: '-1',
|
||||
opisn: '-1',
|
||||
};
|
||||
|
||||
setSearchValues(obj, values) {
|
||||
this.searchValues = { ...this.searchValues, ...values };
|
||||
if (values.date) {
|
||||
this.searchValues.date90 = {
|
||||
Date1: (values.date.clone()).subtract(90, 'days').format(DATE_FORMAT),
|
||||
Date2: (values.date.clone()).subtract(30, 'days').format(SMALL_DATETIME_FORMAT),
|
||||
};
|
||||
this.searchValues.date180 = {
|
||||
Date1: (values.date.clone()).subtract(180, 'days').format(DATE_FORMAT),
|
||||
Date2: (values.date.clone()).subtract(50, 'days').format(SMALL_DATETIME_FORMAT),
|
||||
};
|
||||
}
|
||||
this.searchValuesToSub = {...this.searchValuesToSub, ...obj};
|
||||
}
|
||||
|
||||
results = { loading: false, dataSource: [], details: [], byDate: [], byOperator: [] };
|
||||
process = { loading: false, dataSource: [], details: [], byDate: [], byOperator: [] };
|
||||
risk = { loading: false, dataSource: [], byLostType: {}, };
|
||||
resetData = (rootKey = '') => {
|
||||
if (rootKey === '') {
|
||||
return false;
|
||||
}
|
||||
this[rootKey].loading = false;
|
||||
for (const key of Object.keys(this[rootKey])) {
|
||||
if (key !== 'loading') {
|
||||
this[rootKey][key] = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
export default SalesCRMData;
|
@ -0,0 +1,181 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { stores_Context } from '../config';
|
||||
import { Row, Col, Table, Space, Typography, Divider } from 'antd';
|
||||
import SearchForm from './../components/search/SearchForm';
|
||||
import { VSTag, TableExportBtn } from './../components/Data';
|
||||
import { CruiseAgency } from './../libs/ht';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export default observer((props) => {
|
||||
const { date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
const { HotelCruiseStore, date_picker_store } = useContext(stores_Context);
|
||||
const { loading, dataSource, summaryRow } = HotelCruiseStore.cruise;
|
||||
|
||||
const { formValues, siderBroken } = searchFormStore;
|
||||
|
||||
const tableSorter = (a, b, colName) => a[colName] - b[colName];
|
||||
const tableExportDataRow = (col1, col2) => [col1, col2].filter((r) => r).join(' VS ');
|
||||
const tableProps = {
|
||||
size: 'small',
|
||||
bordered: true,
|
||||
pagination: false,
|
||||
columns: [
|
||||
{ title: '产品',
|
||||
sorter: (a, b) => a.ProductName.localeCompare(b.ProductName, 'zh-CN'),children: [{ title: summaryRow.ProductName, dataIndex: 'ProductName', key: 'ProductName' }] },
|
||||
{
|
||||
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>
|
||||
</>
|
||||
),
|
||||
titleX: tableExportDataRow(summaryRow.TotalNum, summaryRow.CPTotalNum),
|
||||
dataExport: (v, r) => tableExportDataRow(r.TotalNum, r.CPTotalNum),
|
||||
dataIndex: 'TotalNum',
|
||||
key: 'TotalNum',
|
||||
render: (v, r) => (
|
||||
<>
|
||||
<Space direction={'vertical'}>
|
||||
<Text strong>
|
||||
{v}
|
||||
{r.CPTotalNum ? <Text type="secondary"> VS {r.CPTotalNum}</Text> : null}
|
||||
</Text>
|
||||
{r.CPTotalNum && <VSTag diffPercent={r.TotalNumPercent} />}
|
||||
</Space>
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '人数',
|
||||
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>
|
||||
</>
|
||||
),
|
||||
titleX: tableExportDataRow(summaryRow.TotalPersonNum, summaryRow.CPTotalPersonNum),
|
||||
dataExport: (v, r) => tableExportDataRow(r.TotalPersonNum, r.CPTotalPersonNum),
|
||||
dataIndex: 'TotalPersonNum',
|
||||
key: 'TotalPersonNum',
|
||||
render: (v, r) => (
|
||||
<>
|
||||
<Space direction={'vertical'}>
|
||||
<Text strong>
|
||||
{v}
|
||||
{r.CPTotalPersonNum ? <Text type="secondary"> VS {r.CPTotalPersonNum}</Text> : null}
|
||||
</Text>
|
||||
{r.CPTotalNum && <VSTag diffPercent={r.TotalPersonNumPercent} />}
|
||||
</Space>
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '总利润',
|
||||
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>
|
||||
</>
|
||||
),
|
||||
titleX: tableExportDataRow(summaryRow.TotalProfit, summaryRow.CPTotalProfit),
|
||||
dataExport: (v, r) => tableExportDataRow(r.TotalProfit, r.CPTotalProfit),
|
||||
dataIndex: 'TotalProfit',
|
||||
key: 'TotalProfit',
|
||||
render: (v, r) => (
|
||||
<>
|
||||
<Space direction={'vertical'}>
|
||||
<Text strong>
|
||||
{v}
|
||||
{r.CPTotalProfit ? <Text type="secondary"> VS {r.CPTotalProfit}</Text> : null}
|
||||
</Text>
|
||||
{r.CPTotalNum && <VSTag diffPercent={r.TotalProfitPercent} />}
|
||||
</Space>
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
// { title: '单订船', dataIndex: '', key: '' },
|
||||
// { title: '订单含行程', dataIndex: '', key: '' },
|
||||
// { title: '国籍', dataIndex: '', key: '' },
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||
<Col md={24} lg={24} xxl={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...date_picker_store.formValues,
|
||||
...HotelCruiseStore.searchValues,
|
||||
},
|
||||
// 'countryArea',
|
||||
shows: [
|
||||
'DepartmentList',
|
||||
'orderStatus',
|
||||
'dates',
|
||||
'keyword',
|
||||
'cruiseDirection',
|
||||
'agency',
|
||||
'cruiseBookType',
|
||||
'country', // 'roomsRange', 'personRange'
|
||||
],
|
||||
sort: { keyword: 101, agency: 110, cruiseDirection: 102, country: 104 },
|
||||
fieldProps: {
|
||||
keyword: { placeholder: '产品名', col: 4 },
|
||||
DepartmentList: { show_all: true, mode: 'multiple' },
|
||||
orderStatus: { show_all: true },
|
||||
cruiseDirection: { show_all: true },
|
||||
agency: { defaultOptions: CruiseAgency, autoGet: false },
|
||||
// years: { hide_vs: false },
|
||||
},
|
||||
}}
|
||||
onSubmit={(_err, obj, form) => {
|
||||
HotelCruiseStore.setSearchValues(obj, form);
|
||||
HotelCruiseStore.getCruiseData(obj);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<section>
|
||||
<Divider orientation="right" >
|
||||
<TableExportBtn label={'游船'} {...{ columns: tableProps.columns, dataSource }} />
|
||||
</Divider>
|
||||
<Table {...tableProps} {...{ loading, dataSource }} rowKey={(record) => record.ProductName} />
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,147 @@
|
||||
import { useContext } from 'react';
|
||||
import { Row, Col, Typography, Space, Table, Divider } from 'antd';
|
||||
import { stores_Context } from '../config';
|
||||
import { observer } from 'mobx-react';
|
||||
import 'moment/locale/zh-cn';
|
||||
import SearchForm from '../components/search/SearchForm';
|
||||
import { TableExportBtn } from '../components/Data';
|
||||
import * as comm from '../utils/commons';
|
||||
|
||||
const HostCaseCount = () => {
|
||||
const { customer_store, date_picker_store } = useContext(stores_Context);
|
||||
const host_case_data = customer_store.host_case_data;
|
||||
|
||||
const columnsList = [
|
||||
{
|
||||
title: '团数',
|
||||
dataIndex: 'TotalGroupNum',
|
||||
key: 'TotalGroupNum',
|
||||
sorter: (a, b) => parseInt(a.TotalGroupNum) - parseInt(b.TotalGroupNum),
|
||||
},
|
||||
{
|
||||
title: '人数',
|
||||
dataIndex: 'TotalPersonNum',
|
||||
key: 'TotalPersonNum',
|
||||
sorter: (a, b) => parseInt(a.TotalPersonNum) - parseInt(b.TotalPersonNum),
|
||||
},
|
||||
{
|
||||
title: '计费团天数',
|
||||
dataIndex: 'TotalDays',
|
||||
key: 'TotalDays',
|
||||
sorter: (a, b) => parseInt(a.TotalDays) - parseInt(b.TotalDays),
|
||||
},
|
||||
{
|
||||
title: '交易额',
|
||||
dataIndex: 'TotalPrice',
|
||||
key: 'TotalPrice',
|
||||
sorter: (a, b) => parseInt(a.TotalPrice) - parseInt(b.TotalPrice),
|
||||
},
|
||||
];
|
||||
// 筛选函数
|
||||
const getFiltersData=(filterData,filterKey)=>{
|
||||
return comm.uniqWith(filterData.map(rr => ({ text: rr[filterKey], value: rr[filterKey] })),
|
||||
(a, b) => JSON.stringify(a) === JSON.stringify(b)).sort((a, b) => a.text.localeCompare(b.text));
|
||||
};
|
||||
// 小组筛选菜单项
|
||||
const allOPIGroup = getFiltersData(host_case_data.summaryData,"GroupBy");
|
||||
// 顾问筛选菜单项
|
||||
const allOPIConsultant = getFiltersData(host_case_data.counselorData,"GroupBy");
|
||||
// 明细表顾问名筛选菜单项
|
||||
const allOPIDetailConsultant = getFiltersData(host_case_data.singleDetailData,"OPI_Name");
|
||||
const summaryColumnsList = [{
|
||||
title: '',
|
||||
dataIndex: 'GroupBy',
|
||||
key: 'GroupBy',
|
||||
}, ...columnsList];
|
||||
const groupColumnsList = [{
|
||||
title: '组名',
|
||||
dataIndex: 'GroupBy',
|
||||
key: 'GroupBy',
|
||||
filters: allOPIGroup,
|
||||
onFilter: (value, record) => record.GroupBy === value,
|
||||
filterSearch: true,
|
||||
}, ...columnsList];
|
||||
const counselorColumnsList = [{
|
||||
title: '顾问名',
|
||||
dataIndex: 'GroupBy',
|
||||
key: 'GroupBy',
|
||||
filters: allOPIConsultant,
|
||||
onFilter: (value, record) => record.GroupBy === value,
|
||||
filterSearch: true,
|
||||
}, ...columnsList];
|
||||
const singleDetailColumnsList = [{
|
||||
title: '团名',
|
||||
dataIndex: 'GroupBy',
|
||||
key: 'GroupBy',
|
||||
},
|
||||
{
|
||||
title: '顾问名',
|
||||
dataIndex: 'OPI_Name',
|
||||
key: 'OPI_Name',
|
||||
filters: allOPIDetailConsultant,
|
||||
onFilter: (value, record) => record.OPI_Name === value,
|
||||
filterSearch: true,
|
||||
},
|
||||
...columnsList.slice(1)];
|
||||
|
||||
const renderRow=(rowColumns,rowDataSource,title)=>{
|
||||
return(
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Typography.Title level={3}>{title}</Typography.Title>
|
||||
<Divider orientation="right" plain>
|
||||
<TableExportBtn label={title} {...{ columns: rowColumns, dataSource: rowDataSource }} />
|
||||
</Divider>
|
||||
<Table
|
||||
sticky
|
||||
id={`${rowColumns}`}
|
||||
dataSource={rowDataSource}
|
||||
columns={rowColumns}
|
||||
size="small"
|
||||
rowKey={(record) => record.key}
|
||||
loading={host_case_data.loading}
|
||||
pagination={false}
|
||||
scroll={{ x: 1000 }}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Row gutter={16} className={date_picker_store.siderBroken ? "" : "sticky-top"} >
|
||||
<Col className="gutter-row" span={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...date_picker_store.formValues,
|
||||
...host_case_data.searchValues,
|
||||
},
|
||||
shows: ['DepartmentList', 'dates'],
|
||||
fieldProps: {
|
||||
DepartmentList: { show_all: false, mode: 'multiple' },
|
||||
dates: { hide_vs: true },
|
||||
},
|
||||
}}
|
||||
onSubmit={(_err, obj, form) => {
|
||||
customer_store.setSearchValues(obj, form,"host_case_data");
|
||||
customer_store.getHostCaseData("1");
|
||||
customer_store.getHostCaseData("2");
|
||||
customer_store.getHostCaseData("3");
|
||||
customer_store.getHostCaseData("4");
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
{renderRow(summaryColumnsList,host_case_data.summaryData,'东道主项目汇总')}
|
||||
{renderRow(groupColumnsList,host_case_data.groupData,'东道主项目小组统计')}
|
||||
{renderRow(counselorColumnsList,host_case_data.counselorData,'东道主项目顾问统计')}
|
||||
{renderRow(singleDetailColumnsList,host_case_data.singleDetailData,'单团明细')}
|
||||
</Space>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default observer(HostCaseCount);
|
@ -0,0 +1,171 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { stores_Context } from '../config';
|
||||
import { Row, Col, Table, Space, Typography, Divider } from 'antd';
|
||||
import SearchForm from './../components/search/SearchForm';
|
||||
import { VSTag, TableExportBtn } from './../components/Data';
|
||||
const { Text } = Typography;
|
||||
|
||||
export default observer((props) => {
|
||||
const { date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
const { date_picker_store, HotelCruiseStore } = useContext(stores_Context);
|
||||
const { loading, dataSource, summaryRow } = HotelCruiseStore.hotel;
|
||||
|
||||
const { formValues, siderBroken } = searchFormStore;
|
||||
|
||||
const tableSorter = (a, b, colName) => a[colName] - b[colName];
|
||||
const tableExportDataRow = (col1, col2) => [col1, col2].filter((r) => r).join(' VS ');
|
||||
|
||||
const tableProps = {
|
||||
size: 'small',
|
||||
bordered: true,
|
||||
pagination: false,
|
||||
columns: [
|
||||
{
|
||||
title: '目的地',
|
||||
sorter: (a, b) => a.CityName.localeCompare(b.CityName, 'zh-CN'),
|
||||
children: [{ title: summaryRow.CityName, dataIndex: 'CityName', key: 'CityName' }],
|
||||
},
|
||||
{
|
||||
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>
|
||||
</>
|
||||
),
|
||||
titleX: tableExportDataRow(summaryRow.TotalNum, summaryRow.CPTotalNum),
|
||||
dataIndex: 'TotalNum',
|
||||
key: 'TotalNum',
|
||||
dataExport: (v, r) => tableExportDataRow(r.TotalNum, r.CPTotalNum),
|
||||
render: (v, r) => (
|
||||
<>
|
||||
<Space direction={'vertical'}>
|
||||
<Text strong>
|
||||
{v}
|
||||
{r.CPTotalNum ? <Text type="secondary"> VS {r.CPTotalNum}</Text> : null}
|
||||
</Text>
|
||||
{r.CPTotalNum && <VSTag diffPercent={r.TotalNumPercent} />}
|
||||
</Space>
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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>
|
||||
</>
|
||||
),
|
||||
titleX: tableExportDataRow(summaryRow.RecomendNum, summaryRow.CPRecomendNum),
|
||||
dataIndex: 'RecomendNum',
|
||||
key: 'RecomendNum',
|
||||
dataExport: (v, r) => tableExportDataRow(r.RecomendNum, r.CPRecomendNum),
|
||||
render: (v, r) => (
|
||||
<>
|
||||
<Space direction={'vertical'}>
|
||||
<Text strong>
|
||||
{v}
|
||||
{r.CPRecomendNum ? <Text type="secondary"> VS {r.CPRecomendNum}</Text> : null}
|
||||
</Text>
|
||||
{r.CPRecomendNum && <VSTag diffPercent={r.RecomendNumPercent} />}
|
||||
</Space>
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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>
|
||||
</>
|
||||
),
|
||||
titleX: tableExportDataRow(summaryRow.RecommendRate_100, summaryRow.CPRecommendRate_100),
|
||||
dataIndex: 'RecommendRate_100',
|
||||
key: 'RecommendRate_100',
|
||||
dataExport: (v, r) => tableExportDataRow(r.RecommendRate_100, r.CPRecommendRate_100),
|
||||
render: (v, r) => (
|
||||
<>
|
||||
<Space direction={'vertical'}>
|
||||
<Text strong>
|
||||
{v}
|
||||
{r.RecommendRateDelta !== undefined && <Text type="secondary"> VS {r.CPRecommendRate_100}</Text>}
|
||||
</Text>
|
||||
{r.RecommendRateDelta !== undefined && <VSTag diffPercent={r.RecommendRateDelta} />}
|
||||
</Space>
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||
<Col md={24} lg={24} xxl={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...date_picker_store.formValues,
|
||||
...HotelCruiseStore.searchValues,
|
||||
},
|
||||
// 'countryArea', 'DateType', 'dates', 'hotelRecommandRate',
|
||||
shows: ['DepartmentList', 'countryArea', 'orderStatus', 'hotelBookType', 'hotelStar', 'DateType', 'dates'],
|
||||
sort: { DateType: 101, dates: 102 },
|
||||
fieldProps: {
|
||||
DepartmentList: { show_all: true, mode: 'multiple' },
|
||||
countryArea: { show_all: true },
|
||||
orderStatus: { show_all: true },
|
||||
hotelBookType: { show_all: true },
|
||||
hotelRecommandRate: { show_all: true },
|
||||
// years: { hide_vs: false },
|
||||
DateType: { disabledKeys: ['applyDate'] },
|
||||
},
|
||||
}}
|
||||
onSubmit={(_err, obj, form) => {
|
||||
HotelCruiseStore.setSearchValues(obj, form);
|
||||
HotelCruiseStore.getHotelData(obj);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<section>
|
||||
<Divider orientation="right" >
|
||||
<TableExportBtn label={'酒店'} {...{ columns: tableProps.columns, dataSource }} />
|
||||
</Divider>
|
||||
<Table {...tableProps} bordered {...{ loading, dataSource }} rowKey={(record) => record.CityName} />
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,123 @@
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { stores_Context } from '../config';
|
||||
import { Spin, Table, Row, Col, Tabs, Switch } from 'antd';
|
||||
import SearchForm from './../components/search/SearchForm';
|
||||
import { TableExportBtn } from './../components/Data';
|
||||
import { empty } from '../utils/commons';
|
||||
import './kpi.css';
|
||||
|
||||
const apartOptions = [
|
||||
{ key: 'inbound', value: 'inbound', label: '入境' },
|
||||
{ key: 'outbound', value: 'outbound', label: '出境' },
|
||||
{ key: 'domestic', value: 'domestic', label: '国内' },
|
||||
];
|
||||
const apartOptionsMapped = apartOptions.reduce((r, v) => ({...r, [v.value]: v}), {});
|
||||
|
||||
export default observer((props) => {
|
||||
const { financial_store: financialStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
const { formValues, formValuesToSub } = searchFormStore;
|
||||
const { servicePersonNum } = financialStore;
|
||||
const { curTab } = servicePersonNum;
|
||||
const [ifNull, setIfNull] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// DistributionStore.setFormDates(formValuesToSub);
|
||||
financialStore.resetPersonNumData();
|
||||
return () => {};
|
||||
}, [formValuesToSub]);
|
||||
|
||||
const pageRefresh = (queryData = formValuesToSub) => {
|
||||
// console.log(queryData, 'qqqq');
|
||||
financialStore.getPersonNum(queryData);
|
||||
|
||||
financialStore.setPersonNumTableDataSource(false);
|
||||
setIfNull(false);
|
||||
};
|
||||
|
||||
|
||||
const columns = [
|
||||
{ title: apartOptionsMapped[curTab].label, dataIndex: 'groupsLabel', children: [{ title: `${formValuesToSub.Date1}~${formValuesToSub.Date2.substring(0, 10)}`, dataIndex: 'groupsLabel' }] },
|
||||
{
|
||||
title: '人次数',
|
||||
children: [{ title: '组织', dataIndex: 'orgz' }, ...(['inbound', 'domestic'].includes(curTab) ? [{ title: '接待', dataIndex: 'hosts' }] : [])],
|
||||
},
|
||||
{
|
||||
title: '人天',
|
||||
children: [{ title: '组织', dataIndex: 'orgzPDays' }, ...(['inbound', 'domestic'].includes(curTab) ? [{ title: '接待', dataIndex: 'hostsPDays' }] : [])],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} style={{ margin: '-16px -8px', position: 'sticky', top: 0, zIndex: 10 }}>
|
||||
<Col className="gutter-row" span={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...formValues,
|
||||
DateType: { key: 'startDate', value: 'startDate', label: '走团日期' },
|
||||
},
|
||||
shows: ['dates'],
|
||||
fieldProps: {
|
||||
dates: { hide_vs: true },
|
||||
},
|
||||
}}
|
||||
onSubmit={(_err, obj, form, str) => {
|
||||
pageRefresh(obj);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<section>
|
||||
<Tabs
|
||||
activeKey={curTab}
|
||||
onChange={(v) => {
|
||||
financialStore.setCurTab(v);
|
||||
if (empty(servicePersonNum[v].dataSource)) {
|
||||
pageRefresh();
|
||||
}
|
||||
}}
|
||||
tabBarExtraContent={{
|
||||
right: (
|
||||
<>
|
||||
<Switch
|
||||
unCheckedChildren="原数据"
|
||||
checkedChildren="去除空"
|
||||
key={'ifNull'}
|
||||
checked={ifNull}
|
||||
onChange={(e) => {
|
||||
financialStore.setPersonNumTableDataSource(e);
|
||||
setIfNull(e);
|
||||
}}
|
||||
/>
|
||||
<TableExportBtn label={'服务人数_'+apartOptionsMapped[curTab].label} {...{ columns, dataSource: servicePersonNum[curTab].dataSource }} />
|
||||
</>
|
||||
),
|
||||
}}
|
||||
type="card"
|
||||
items={apartOptions.map((ele) => {
|
||||
return {
|
||||
...ele,
|
||||
children: (
|
||||
<Spin spinning={servicePersonNum.loading}>
|
||||
<Table
|
||||
id="table_to_xlsx_sale"
|
||||
dataSource={servicePersonNum[curTab].dataSource}
|
||||
rowKey="groupsKey"
|
||||
columns={columns}
|
||||
size="small"
|
||||
loading={servicePersonNum[curTab].loading}
|
||||
pagination={false}
|
||||
scroll={{ x: '100%' }}
|
||||
/>
|
||||
</Spin>
|
||||
),
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,334 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,258 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { observer } from 'mobx-react';
|
||||
import { stores_Context } from '../../config';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Row, Col, Table, Tooltip } from 'antd';
|
||||
import SearchForm from '../../components/search/SearchForm';
|
||||
import { pick } from '../../utils/commons';
|
||||
|
||||
const COLOR_SETS = [
|
||||
"#FFFFFF",
|
||||
// "#5B8FF9",
|
||||
// "#FF6B3B",
|
||||
"#9FB40F",
|
||||
"#76523B",
|
||||
"#DAD5B5",
|
||||
"#E19348",
|
||||
"#F383A2",
|
||||
];
|
||||
|
||||
const transparentHex = '1A';
|
||||
|
||||
export default observer((props) => {
|
||||
const { SalesCRMDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
|
||||
// const { formValues, siderBroken } = searchFormStore;
|
||||
const { formValues, formValuesToSub, siderBroken } = searchFormStore;
|
||||
const _formValuesToSub = pick(formValuesToSub, ['DepartmentList', 'WebCode']);
|
||||
const { resetData, process } = SalesCRMDataStore;
|
||||
const operatorObjects = process.details.map((v) => ({ key: v.groupsKey, value: v.groupsKey, label: v.groupsLabel, text: v.groupsLabel }));
|
||||
|
||||
const pageRefresh = async (obj) => {
|
||||
resetData('process');
|
||||
Promise.allSettled([
|
||||
SalesCRMDataStore.getProcessData({
|
||||
...(obj || _formValuesToSub),
|
||||
// ...subParam,
|
||||
// groupType: 'overview',
|
||||
groupType: 'dept',
|
||||
groupDateType: '',
|
||||
}),
|
||||
SalesCRMDataStore.getProcessData({
|
||||
...(obj || _formValuesToSub),
|
||||
// ...subParam,
|
||||
groupType: 'operator',
|
||||
groupDateType: '',
|
||||
}),
|
||||
]);
|
||||
};
|
||||
|
||||
const tableSorter = (a, b, colName) => (a.groupType !== 'operator' ? -1 : b.groupType !== 'operator' ? 0 : a[colName] - b[colName]);
|
||||
const percentageRender = val => val ? `${val}%` : '';
|
||||
|
||||
const activityTableProps = {
|
||||
rowKey: 'groupsKey',
|
||||
pagination: { pageSize: 10, showSizeChanger: false },
|
||||
size: 'small',
|
||||
rowClassName: (record, rowIndex) => {
|
||||
return record.groupType === 'operator' ? '': 'ant-tag-blue';
|
||||
},
|
||||
showSorterTooltip: false,
|
||||
columns: [
|
||||
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem',
|
||||
filterSearch: true,
|
||||
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
||||
onFilter: (value, record) => record.groupsKey === value || record.groupType !== 'operator',
|
||||
render: (text, record) => (record.groupType !== 'operator' ? text : <Link to={`/op_risk/sales/${record.groupsKey}`}>{text}</Link>),
|
||||
}, // 维度: 顾问
|
||||
{
|
||||
title: '顾问动作',
|
||||
key: 'date',
|
||||
children: [
|
||||
{ title: () => (
|
||||
<>
|
||||
首次响应率24H{' '}
|
||||
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'firstTouch24', key: 'firstTouch24', render: percentageRender,
|
||||
sorter: (a, b) => tableSorter(a, b, 'firstTouch24'),
|
||||
},
|
||||
{ title: () => (
|
||||
<>
|
||||
48H内报价率{' '}
|
||||
<Tooltip title="达到48H内报价的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'firstQuote48', key: 'firstQuote48',render: percentageRender ,
|
||||
sorter: (a, b) => tableSorter(a, b, 'firstQuote48'), },
|
||||
{ title: () => (
|
||||
<>
|
||||
一次报价率{' '}
|
||||
<Tooltip title="首次报价的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'quote1', key: 'quote1',render: percentageRender ,
|
||||
sorter: (a, b) => tableSorter(a, b, 'quote1'), },
|
||||
{ title: () => (
|
||||
<>
|
||||
二次报价率{' '}
|
||||
<Tooltip title="二次报价的订单数/一次报价后回复数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'quote2', key: 'quote2',render: percentageRender ,
|
||||
sorter: (a, b) => tableSorter(a, b, 'quote2'), },
|
||||
{ title: () => (
|
||||
<>
|
||||
>50条会话{' '}
|
||||
<Tooltip title=">50条会话的订单数/总订单">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'turnsGT50', key: 'turnsGT50', render: percentageRender ,
|
||||
sorter: (a, b) => tableSorter(a, b, 'turnsGT50'), },
|
||||
{ title: () => (
|
||||
<>
|
||||
违规数{' '}
|
||||
<Tooltip title="未遵循24H回复的订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'violations', key: 'violations',
|
||||
sorter: (a, b) => tableSorter(a, b, 'violations'), },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '客人回复',
|
||||
key: 'department',
|
||||
children: [
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
首次回复率24H{' '}
|
||||
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'firstReply24', key: 'firstReply24',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
sorter: (a, b) => tableSorter(a, b, 'firstReply24'), },
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
48H内报价回复率{' '}
|
||||
<Tooltip title="48H内报价后的回复数/报价的订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'replyQuote48', key: 'replyQuote48',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
sorter: (a, b) => tableSorter(a, b, 'replyQuote48'), },
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
一次报价回复率{' '}
|
||||
<Tooltip title="一次报价后回复率/订单总数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'replyQuote1', key: 'replyQuote1',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
sorter: (a, b) => tableSorter(a, b, 'replyQuote1'), },
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
二次报价回复率{' '}
|
||||
<Tooltip title="二次报价后回复数/一次报价后回复数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
), dataIndex: 'replyQuote2', key: 'replyQuote2',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
sorter: (a, b) => tableSorter(a, b, 'replyQuote2'), },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const riskTableProps = {
|
||||
rowKey: 'groupsKey',
|
||||
pagination: { pageSize: 10, showSizeChanger: false },
|
||||
size: 'small',
|
||||
rowClassName: (record, rowIndex) => {
|
||||
return record.groupType === 'operator' ? '': 'ant-tag-blue';
|
||||
},
|
||||
showSorterTooltip: false,
|
||||
columns: [
|
||||
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem',
|
||||
filterSearch: true,
|
||||
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
||||
onFilter: (value, record) => record.groupsKey === value || record.groupType !== 'operator',
|
||||
render: (text, record) => (record.groupType !== 'operator' ? text : <Link to={`/op_risk/sales/${record.groupsKey}`}>{text}</Link>),
|
||||
}, // 维度: 顾问
|
||||
{ title: '>24H回复', dataIndex: 'lostTouch24', key: 'lostTouch24',
|
||||
sorter: (a, b) => tableSorter(a, b, 'lostTouch24'),
|
||||
},
|
||||
{ title: '首次报价周期>48h', dataIndex: 'lostQuote48', key: 'lostQuote48',
|
||||
sorter: (a, b) => tableSorter(a, b, 'lostQuote48'),
|
||||
},
|
||||
{ title: '报价次数<1次', dataIndex: 'lostQuote1', key: 'lostQuote1',
|
||||
sorter: (a, b) => tableSorter(a, b, 'lostQuote1'),
|
||||
},
|
||||
{ title: '报价次数<2次', dataIndex: 'lostQuote2', key: 'lostQuote2',
|
||||
sorter: (a, b) => tableSorter(a, b, 'lostQuote2'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||
<Col md={24} lg={24} xxl={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...formValues,
|
||||
...SalesCRMDataStore.searchValues,
|
||||
},
|
||||
shows: ['DepartmentList', 'WebCode', 'DateType', 'dates'],
|
||||
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);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<section>
|
||||
<Table {...activityTableProps} bordered dataSource={[...process.dataSource, ...process.details]} loading={process.loading} sticky />
|
||||
</section>
|
||||
<section>
|
||||
<h3>未成行订单 数量</h3>
|
||||
<Table {...riskTableProps} bordered dataSource={[...process.dataSource, ...process.details]} loading={process.loading} sticky />
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,397 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { NavLink, useParams } from 'react-router-dom';
|
||||
import { observer } from 'mobx-react';
|
||||
import { stores_Context, DATE_FORMAT, SMALL_DATETIME_FORMAT } from '../../config';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Row, Col, Table, Divider, Button, Popover, Tooltip } from 'antd';
|
||||
import { fixTo2Decimals } from '../../utils/commons';
|
||||
import MixFieldsDetail from '../../components/MixFieldsDetail';
|
||||
|
||||
const COLOR_SETS = [
|
||||
'#FFFFFF',
|
||||
// "#5B8FF9",
|
||||
// "#FF6B3B",
|
||||
'#9FB40F',
|
||||
'#76523B',
|
||||
'#DAD5B5',
|
||||
'#E19348',
|
||||
'#F383A2',
|
||||
];
|
||||
const transparentHex = '1A';
|
||||
const percentageRender = (val) => (val ? `${val}%` : '');
|
||||
const numberConvert10K = (number, scale = 10) => {
|
||||
return fixTo2Decimals((number/(1000*scale)));
|
||||
};
|
||||
const retPropsMinRatesSet = { 'CH': 12, 'AH': 8, 'default': 10 }; //
|
||||
|
||||
export default observer((props) => {
|
||||
const { opisn, opi_name } = useParams();
|
||||
|
||||
const { sale_store, SalesCRMDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
|
||||
const { formValues, siderBroken } = searchFormStore;
|
||||
const { searchValues, searchValuesToSub, resetData, results, process, risk } = SalesCRMDataStore;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
Promise.allSettled([
|
||||
// 结果指标
|
||||
SalesCRMDataStore.get90n180Data({ opisn, groupType: 'operator', groupDateType: '' }),
|
||||
// 结果: 全年业绩
|
||||
SalesCRMDataStore.getResultData({
|
||||
opisn,
|
||||
Date1: searchValues.date.clone().startOf('year').format(DATE_FORMAT),
|
||||
Date2: searchValues.date.clone().endOf('year').format(SMALL_DATETIME_FORMAT),
|
||||
groupType: 'overview',
|
||||
// groupType: 'operator',
|
||||
groupDateType: 'month',
|
||||
}),
|
||||
// 过程
|
||||
SalesCRMDataStore.getProcessData({ opisn, groupType: 'operator', groupDateType: '' }),
|
||||
// 违规明细
|
||||
SalesCRMDataStore.getRiskDetailData({ opisn }),
|
||||
]);
|
||||
}, [opisn]);
|
||||
|
||||
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 = {
|
||||
rowKey: 'groupsKey',
|
||||
pagination: false,
|
||||
size: 'small',
|
||||
showSorterTooltip: false,
|
||||
columns: [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'groupsLabel',
|
||||
key: 'name',
|
||||
width: '5em',
|
||||
},
|
||||
{
|
||||
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 activityTableProps = {
|
||||
rowKey: 'groupsKey',
|
||||
pagination: false,
|
||||
size: 'small',
|
||||
rowClassName: (record, rowIndex) => {
|
||||
return record.groupType === 'operator' ? '' : 'ant-tag-blue';
|
||||
},
|
||||
showSorterTooltip: false,
|
||||
columns: [
|
||||
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem' }, // 维度: 顾问
|
||||
{
|
||||
title: '顾问动作',
|
||||
key: 'date',
|
||||
children: [
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
首次响应率24H{' '}
|
||||
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'firstTouch24',
|
||||
key: 'firstTouch24',
|
||||
render: percentageRender,
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
48H内报价率{' '}
|
||||
<Tooltip title="达到48H内报价的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'firstQuote48',
|
||||
key: 'firstQuote48',
|
||||
render: percentageRender,
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
一次报价率{' '}
|
||||
<Tooltip title="首次报价的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'quote1',
|
||||
key: 'quote1',
|
||||
render: percentageRender,
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
二次报价率{' '}
|
||||
<Tooltip title="二次报价的订单数/一次报价后回复数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'quote2',
|
||||
key: 'quote2',
|
||||
render: percentageRender,
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
>50条会话{' '}
|
||||
<Tooltip title=">50条会话的订单数/总订单">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'turnsGT50',
|
||||
key: 'turnsGT50',
|
||||
render: percentageRender,
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
违规数{' '}
|
||||
<Tooltip title="未遵循24H回复的订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'violations',
|
||||
key: 'violations',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '客人回复',
|
||||
key: 'department',
|
||||
children: [
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
首次回复率24H{' '}
|
||||
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'firstReply24',
|
||||
key: 'firstReply24',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
48H内报价回复率{' '}
|
||||
<Tooltip title="48H内报价后的回复数/报价的订单数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'replyQuote48',
|
||||
key: 'replyQuote48',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
一次报价回复率{' '}
|
||||
<Tooltip title="一次报价后回复率/订单总数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'replyQuote1',
|
||||
key: 'replyQuote1',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<>
|
||||
二次报价回复率{' '}
|
||||
<Tooltip title="二次报价后回复数/一次报价后回复数">
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
dataIndex: 'replyQuote2',
|
||||
key: 'replyQuote2',
|
||||
render: (_, r) => ({
|
||||
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||
children: percentageRender(_),
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const riskTableProps = {
|
||||
loading: risk.loading,
|
||||
sticky: true,
|
||||
// scroll: { x: 1000, y: 400 },
|
||||
pagination: false,
|
||||
rowKey: (row) => row.coli_id,
|
||||
columns: [
|
||||
{ title: '客人姓名', dataIndex: 'guest_name', key: 'guest_name', width: '6rem' },
|
||||
{ title: '团号', dataIndex: 'coli_id', key: 'coli_id', width: '6rem' },
|
||||
{
|
||||
title: '表单内容',
|
||||
dataIndex: 'coli_contents',
|
||||
key: 'coli_contents',
|
||||
width: '6rem',
|
||||
render: (text, record) => (
|
||||
<Popover
|
||||
title={`${record.coli_id} ${record.guest_name}`}
|
||||
content={<pre dangerouslySetInnerHTML={{ __html: text }} style={{ whiteSpace: 'pre-wrap', wordWrap: 'break-word' }} />}
|
||||
trigger={['click']}
|
||||
placement="right"
|
||||
overlayStyle={{ width: '500px', maxHeight: '500px' }}
|
||||
autoAdjustOverflow={false}
|
||||
>
|
||||
<Button type="link" size="small">
|
||||
表单内容
|
||||
</Button>
|
||||
</Popover>
|
||||
),
|
||||
},
|
||||
{ title: '顾问', dataIndex: 'opi_name', key: 'opi_name', width: '6rem' },
|
||||
{ title: '预定时间', dataIndex: 'coli_applydate', key: 'coli_applydate', width: '6rem' },
|
||||
],
|
||||
};
|
||||
|
||||
const chartsConfig = {
|
||||
colFields: ['ConfirmOrder', 'SumOrder'],
|
||||
lineFields: ['SumML', 'ConfirmRates'],
|
||||
seriesField: null,
|
||||
xField: 'groupDateVal',
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||
<Col md={24} lg={12} xxl={14}>
|
||||
<NavLink to={-1}>返回</NavLink>
|
||||
</Col>
|
||||
</Row>
|
||||
<section>
|
||||
<h2>结果指标 @ {searchValues.date.format(DATE_FORMAT)}</h2>
|
||||
<Table {...dashboardTableProps} bordered dataSource={results[`operator_${opisn}`]} loading={results.loading} sticky />
|
||||
</section>
|
||||
<section>
|
||||
<h3>全年每月业绩 @ {searchValues.date.format('YYYY')}</h3>
|
||||
<MixFieldsDetail {...chartsConfig} dataSource={results[`operator_byDate_${opisn}`] || []} />
|
||||
</section>
|
||||
<Divider />
|
||||
<section>
|
||||
<h2>过程指标 @ {searchValuesToSub.Date1} 至 {searchValuesToSub.Date2}</h2>
|
||||
<Table {...activityTableProps} bordered dataSource={process[`operator_${opisn}`]} loading={process.loading} sticky />
|
||||
</section>
|
||||
<section>
|
||||
<h2>违规明细</h2>
|
||||
<h3>>24H回复</h3>
|
||||
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostTouch24 || []} />
|
||||
</section>
|
||||
<section>
|
||||
<h3>首次报价周期>48h</h3>
|
||||
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostQuote48 || []} />
|
||||
</section>
|
||||
<section>
|
||||
<h3>报价次数<1</h3>
|
||||
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostQuote1 || []} />
|
||||
</section>
|
||||
<section>
|
||||
<h3>报价次数<2</h3>
|
||||
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostQuote2 || []} />
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue