Compare commits
No commits in common. 'main' and 'feature/pivot' have entirely different histories.
main
...
feature/pi
@ -1,7 +1,5 @@
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
process.env.REACT_APP_BUILD_TIME = new Date().getTime()+(5*60*1000);
|
||||
|
||||
require('child_process').execSync(
|
||||
'react-scripts build',
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
execSync('npm run build', { stdio: 'inherit' });
|
||||
|
@ -1,240 +0,0 @@
|
||||
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>
|
||||
);
|
||||
});
|
@ -1,265 +0,0 @@
|
||||
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} />;
|
||||
});
|
@ -1,9 +0,0 @@
|
||||
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;
|
@ -1,40 +0,0 @@
|
||||
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);
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,355 +0,0 @@
|
||||
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;
|
@ -1,479 +0,0 @@
|
||||
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;
|
@ -1,210 +0,0 @@
|
||||
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;
|
@ -1,181 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
});
|
@ -1,147 +0,0 @@
|
||||
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);
|
@ -1,171 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
});
|
@ -1,123 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
});
|
@ -1,334 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
});
|
@ -1,258 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
});
|
@ -1,397 +0,0 @@
|
||||
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