Compare commits
261 Commits
feature/ba
...
main
Author | SHA1 | Date |
---|---|---|
|
021fb429ce | 5 days ago |
|
a9f45e334d | 1 month ago |
|
488b33ae50 | 1 month ago |
|
6dbf9cfb56 | 1 month ago |
|
108becd2cd | 1 month ago |
|
73e1dcdb4c | 1 month ago |
|
baae505831 | 1 month ago |
|
16b08311fa | 1 month ago |
|
e0e7c3c84f | 2 months ago |
|
87f24c40fa | 2 months ago |
|
bed6b81753 | 2 months ago |
|
171dd7514a | 2 months ago |
|
a0ea6507a3 | 2 months ago |
|
40cd81f9a7 | 2 months ago |
|
876216458e | 2 months ago |
|
8a0f919a42 | 2 months ago |
|
020c9fc3e9 | 2 months ago |
|
b2e539a4d2 | 2 months ago |
|
fee1185406 | 2 months ago |
|
e297d36256 | 2 months ago |
|
667574ec34 | 2 months ago |
|
185db1ec80 | 2 months ago |
|
6dc82a0a01 | 2 months ago |
|
4724b13a2d | 2 months ago |
|
f532f138ff | 2 months ago |
|
3b51ae8d08 | 2 months ago |
|
a6f7884e6e | 2 months ago |
|
928e27913d | 2 months ago |
|
94363df0fc | 2 months ago |
|
621d3a2446 | 2 months ago |
|
8f12fc8747 | 2 months ago |
|
397c886aea | 2 months ago |
|
459db55453 | 2 months ago |
|
6317c24a75 | 2 months ago |
|
c9e13385dc | 2 months ago |
|
a02fa37f17 | 2 months ago |
|
ccbd0be1cd | 2 months ago |
|
c7d3cf5746 | 2 months ago |
|
23b1d2c81b | 2 months ago |
|
7c08e8bfe6 | 2 months ago |
|
1e46cf6e63 | 2 months ago |
|
17cedf14d9 | 2 months ago |
|
2fae92fa2a | 2 months ago |
|
08aa01e33c | 2 months ago |
|
17a85122b1 | 2 months ago |
|
1b86ab1192 | 2 months ago |
|
be31c3983d | 2 months ago |
|
5551b2e362 | 3 months ago |
|
7cfd79a0d2 | 3 months ago |
|
dcb185be5a | 3 months ago |
|
c3547538fd | 3 months ago |
|
cb598ecba1 | 3 months ago |
|
a32943fffb | 3 months ago |
|
a7b24d206d | 3 months ago |
|
f8eef38944 | 3 months ago |
|
89d151f388 | 3 months ago |
|
6ea43a58e2 | 3 months ago |
|
12e99bb816 | 3 months ago |
|
d29dab5824 | 3 months ago |
|
f1e0c1200b | 3 months ago |
|
6f640bac4d | 3 months ago |
|
e822b9bb3a | 3 months ago |
|
ac11babdab | 3 months ago |
|
04431635b5 | 4 months ago |
|
80d4050527 | 4 months ago |
|
6d6a7d98ee | 4 months ago |
|
b583bfcd2f | 4 months ago |
|
539f57e16d | 4 months ago |
|
e5ff51ae63 | 4 months ago |
|
cc3d0c19ad | 4 months ago |
|
00e22a6f08 | 4 months ago |
|
02c09c9821 | 4 months ago |
|
bd67f6ca4e | 4 months ago |
|
04d1cc2b39 | 4 months ago |
|
d1ee551a4e | 4 months ago |
|
4531ce1469 | 4 months ago |
|
d2696d8b01 | 4 months ago |
|
557e4ac104 | 4 months ago |
|
9a4c693c9e | 4 months ago |
|
2be734a591 | 4 months ago |
|
88e09d0bc9 | 5 months ago |
|
ff4852475c | 5 months ago |
|
a0b71dba79 | 5 months ago |
|
b2ce88e8aa | 5 months ago |
|
20e237f75d | 5 months ago |
|
b4a2b02709 | 5 months ago |
|
3890c4700f | 7 months ago |
|
cb211ec207 | 7 months ago |
|
83e570bb49 | 8 months ago |
|
545ad25d05 | 8 months ago |
|
496fbc1ebd | 8 months ago |
|
e53f241d10 | 9 months ago |
|
93766a28b6 | 11 months ago |
|
b5115a8d1e | 11 months ago |
|
322ce021e6 | 11 months ago |
|
55bd0576a8 | 12 months ago |
|
4792291025 | 12 months ago |
|
f2a937cc81 | 12 months ago |
|
d05d15ae09 | 12 months ago |
|
4743a66408 | 1 year ago |
|
49b1657046 | 1 year ago |
|
71b0cce66a | 1 year ago |
|
9e3bb33043 | 1 year ago |
|
c2317d40a7 | 1 year ago |
|
046b7e2988 | 1 year ago |
|
d18c59bb50 | 1 year ago |
|
ab79cdc289 | 1 year ago |
|
345a2b88d8 | 1 year ago |
|
530e558812 | 1 year ago |
|
ebdb7d41b4 | 1 year ago |
|
3c9c9ede8c | 1 year ago |
|
536a8b7f86 | 1 year ago |
|
216bd9c0e6 | 1 year ago |
|
55d205aaaa | 1 year ago |
|
0e76cc1f08 | 1 year ago |
|
854c4a5ebc | 1 year ago |
|
5f55529f68 | 1 year ago |
|
8eb539abb2 | 1 year ago |
|
e42f1f1b6c | 1 year ago |
|
f92f3f53bb | 1 year ago |
|
1025d321ef | 1 year ago |
|
00f0c1a7b1 | 1 year ago |
|
a7280a79b9 | 1 year ago |
|
512b4699ba | 1 year ago |
|
5dcdcd6345 | 1 year ago |
|
ac82432e67 | 1 year ago |
|
35d2731560 | 1 year ago |
|
513b7c7c1c | 1 year ago |
|
401d5720d8 | 1 year ago |
|
e4a63f38f6 | 1 year ago |
|
1a2ee6b637 | 1 year ago |
|
81ee6594c0 | 1 year ago |
|
fe29cb3d1c | 1 year ago |
|
8017b9c2c8 | 1 year ago |
|
5e98893ef0 | 1 year ago |
|
f8820d0eb5 | 1 year ago |
|
1bc85a8b38 | 1 year ago |
|
16fb5b3dfc | 1 year ago |
|
a777eff3bd | 1 year ago |
|
766411cf16 | 1 year ago |
|
956303d9e5 | 1 year ago |
|
606c1e9983 | 1 year ago |
|
9718c023a0 | 2 years ago |
|
8fc609c1c0 | 2 years ago |
|
d272cf6806 | 2 years ago |
|
b4f869453a | 2 years ago |
|
d0d0cb897f | 2 years ago |
|
08b19ee3a9 | 2 years ago |
|
3f62a59648 | 2 years ago |
|
8484beba64 | 2 years ago |
|
528a950b22 | 2 years ago |
|
3ba2b44daa | 2 years ago |
|
8caf1a5fb8 | 2 years ago |
|
18dcfe3759 | 2 years ago |
|
d80ecf398b | 2 years ago |
|
43780b5fc2 | 2 years ago |
|
805804ee88 | 2 years ago |
|
151dcff1a8 | 2 years ago |
|
ea7048e41b | 2 years ago |
|
9695335b30 | 2 years ago |
|
5dafa48ab3 | 2 years ago |
|
e59c1e11bd | 2 years ago |
|
95e799cdae | 2 years ago |
|
d141e0fa7d | 2 years ago |
|
609b6761b3 | 2 years ago |
|
a48bbf35f3 | 2 years ago |
|
6eea280a1d | 2 years ago |
|
cfb4a5f5f7 | 2 years ago |
|
ad91ce73a0 | 2 years ago |
|
f39ed273c3 | 2 years ago |
|
c8e66c12d8 | 2 years ago |
|
95ec1fa822 | 2 years ago |
|
c17f8e8f85 | 2 years ago |
|
e2423c1b85 | 2 years ago |
|
f67fd5da31 | 2 years ago |
|
d90a2cd90d | 2 years ago |
|
a7ecec79d6 | 2 years ago |
|
96b401b8b9 | 2 years ago |
|
8847d03fc1 | 2 years ago |
|
c7e16af0aa | 2 years ago |
|
77f9e67c08 | 2 years ago |
|
72dd9ef590 | 2 years ago |
|
3f35bbfade | 2 years ago |
|
fd57a7c3bd | 2 years ago |
|
9e87278cef | 2 years ago |
|
6ea675f453 | 2 years ago |
|
7926ce3efc | 2 years ago |
|
03655b5e5d | 2 years ago |
|
0d3b170f01 | 2 years ago |
|
aa452b2f26 | 2 years ago |
|
40c8f37ff8 | 2 years ago |
|
edbd4f0dd6 | 2 years ago |
|
01748ecb71 | 2 years ago |
|
30d185eb3c | 2 years ago |
|
c27690cdd1 | 2 years ago |
|
5068f3409b | 2 years ago |
|
230670e43a | 2 years ago |
|
c9c7638cf8 | 2 years ago |
|
7a0b38c8d0 | 2 years ago |
|
bfc953a211 | 2 years ago |
|
24a85d9b38 | 2 years ago |
|
168f63e2d9 | 2 years ago |
|
f6e6dc4c98 | 2 years ago |
|
13fad7dfc5 | 2 years ago |
|
8228e07209 | 2 years ago |
|
42f4cbd134 | 2 years ago |
|
1aa6bfce18 | 2 years ago |
|
e2ae31d339 | 2 years ago |
|
eb686fae86 | 2 years ago |
|
06d1eaa1c9 | 2 years ago |
|
6a7c0a2064 | 2 years ago |
|
cfeecf4af5 | 2 years ago |
|
c878167dc9 | 2 years ago |
|
494dde5276 | 2 years ago |
|
46e6dfcf1e | 2 years ago |
|
7eb57a0ffd | 2 years ago |
|
27d0f48315 | 2 years ago |
|
9593e62573 | 2 years ago |
|
51ae4c9cea | 2 years ago |
|
40b07c503e | 2 years ago |
|
f5dc044469 | 2 years ago |
|
e0f55ddeb8 | 2 years ago |
|
a9c21a54e6 | 2 years ago |
|
e9bccf450d | 2 years ago |
|
2a85a989df | 2 years ago |
|
ede377dcca | 2 years ago |
|
150b42418f | 2 years ago |
|
e34f769723 | 2 years ago |
|
67723c55eb | 2 years ago |
|
43750214be | 2 years ago |
|
31aa3da60b | 2 years ago |
|
633935b5c8 | 2 years ago |
|
b669943f07 | 2 years ago |
|
e8e4efe796 | 2 years ago |
|
077b3b3108 | 2 years ago |
|
fee2a327f8 | 2 years ago |
|
e19733caa9 | 2 years ago |
|
9d1a08f92a | 2 years ago |
|
5f68801a3b | 2 years ago |
|
de6feb85f5 | 2 years ago |
|
619d5c25ed | 2 years ago |
|
1a864c00a8 | 2 years ago |
|
662e5ac2ae | 2 years ago |
|
23de7481bd | 2 years ago |
|
21e70b9dfb | 2 years ago |
|
34080fce8b | 2 years ago |
|
34bb49f487 | 2 years ago |
|
84722e0d0a | 2 years ago |
|
7af50f0e9e | 2 years ago |
|
1c2c7c28c6 | 2 years ago |
|
432d679517 | 2 years ago |
|
479531c82c | 2 years ago |
|
f3b6b1ed5a | 2 years ago |
|
3e3e7f1485 | 2 years ago |
|
2942a61c8e | 2 years ago |
|
c3194869fd | 2 years ago |
|
64898b7533 | 2 years ago |
|
b6df3c17f7 | 2 years ago |
|
9d2d04cdc5 | 2 years ago |
|
3198226430 | 2 years ago |
|
a225c1bbc4 | 2 years ago |
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
process.env.REACT_APP_BUILD_TIME = new Date().getTime()+(5*60*1000);
|
||||||
|
|
||||||
|
require('child_process').execSync(
|
||||||
|
'react-scripts build',
|
||||||
|
{ stdio: 'inherit' }
|
||||||
|
);
|
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
@ -1,41 +1,45 @@
|
|||||||
import React, {Component} from 'react';
|
import React, { Component } from 'react';
|
||||||
import {Table, Button, Space, Radio} from 'antd';
|
import { Table, } from 'antd';
|
||||||
import {SearchOutlined} from '@ant-design/icons';
|
import SearchForm from './../components/search/SearchForm';
|
||||||
import GroupSelect from '../components/search/GroupSelect';
|
import { stores_Context } from '../config';
|
||||||
import DatePickerCharts from "../components/search/DatePickerCharts";
|
import { observer } from 'mobx-react';
|
||||||
import {stores_Context} from "../config";
|
|
||||||
import {observer} from "mobx-react";
|
|
||||||
|
|
||||||
class MobileDeal extends Component {
|
class MobileDeal extends Component {
|
||||||
static contextType = stores_Context;
|
static contextType = stores_Context;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {dashboard_store} = this.context;
|
const { dashboard_store, date_picker_store } = this.context;
|
||||||
const mobile_data = dashboard_store.mobile_data;
|
const mobile_data = dashboard_store.mobile_data;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>移动成交</h2>
|
<h2>移动成交</h2>
|
||||||
<GroupSelect store={mobile_data}/>
|
<SearchForm
|
||||||
<Space size="large">
|
defaultValue={{
|
||||||
<DatePickerCharts hide_vs={true}/>
|
initialValue: {
|
||||||
|
...date_picker_store.formValues,
|
||||||
<Radio.Group value={mobile_data.date_type} onChange={mobile_data.onChange_datetype}>
|
...mobile_data.mobileSearchValues,
|
||||||
<Radio value="applyDate">预定日期</Radio>
|
},
|
||||||
<Radio value="startDate">出发日期</Radio>
|
shows: ['DateType', 'DepartmentList', 'dates'],
|
||||||
</Radio.Group>
|
fieldProps: {
|
||||||
|
DepartmentList: { show_all: false, mode: 'multiple', col: 24 },
|
||||||
<Button type="primary" icon={<SearchOutlined/>} loading={mobile_data.loading} onClick={() => {
|
WebCode: { show_all: true },
|
||||||
mobile_data.asyncFetch();
|
dates: { hide_vs: true, col: 12 },
|
||||||
}}>统计</Button>
|
DateType: { col: 6, disabledKeys: ['confirmDate'] },
|
||||||
</Space>
|
},
|
||||||
<Table dataSource={mobile_data.data} columns={mobile_data.columns} pagination={false} size="small"/>
|
}}
|
||||||
</div>
|
onSubmit={(_err, obj, form, str) => {
|
||||||
);
|
dashboard_store.setMobileSearchValues(obj, form);
|
||||||
}
|
mobile_data.asyncFetch();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Table dataSource={mobile_data.data} columns={mobile_data.columns} pagination={false} size="small" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default observer(MobileDeal);
|
export default observer(MobileDeal);
|
||||||
|
@ -0,0 +1,240 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Row, Col, Spin } from 'antd';
|
||||||
|
import { Line } from '@ant-design/plots';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { dataFieldAlias } from '../libs/ht';
|
||||||
|
import DateGroupRadio from '../components/DateGroupRadio';
|
||||||
|
import { cloneDeep, groupBy, sortBy } from '../utils/commons';
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { dataSource: rawData, showAVG, showCompareSum, loading, solidLineTime,
|
||||||
|
solidLineDash, isCompareLine,solidLineCompareTime, ...config } = props;
|
||||||
|
const { xField, yField, yFieldAlias, seriesField } = config;
|
||||||
|
|
||||||
|
const [dataBeforeXChange, setDataBeforeXChange] = useState([]);
|
||||||
|
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
const [sumSeries, setSumSeries] = useState([]);
|
||||||
|
|
||||||
|
const line_config = {
|
||||||
|
// data: dataSource,
|
||||||
|
padding: 'auto',
|
||||||
|
xField,
|
||||||
|
yField,
|
||||||
|
seriesField,
|
||||||
|
// seriesField: 'rowLabel',
|
||||||
|
// xAxis: {
|
||||||
|
// type: 'timeCat',
|
||||||
|
// },
|
||||||
|
color: isCompareLine?['#17f485', '#1890ff','#17f485', '#1890ff',"#181414","#181414"]:undefined,
|
||||||
|
point: {
|
||||||
|
size: 4,
|
||||||
|
shape: "cicle",
|
||||||
|
},
|
||||||
|
lineStyle: (datum) => {
|
||||||
|
return {
|
||||||
|
stroke: isCompareLine?datum._ylabel.includes("总计")?"#181414":datum._ylabel.includes(solidLineDash) ? '#1890ff':'#17f485' : undefined, // 设置颜色
|
||||||
|
lineDash: isCompareLine?datum._ylabel.includes(solidLineTime) ? undefined:[4, 4] : undefined, // 设置虚线
|
||||||
|
};
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
maxTickInterval: 5,
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
...cloneDeep(dataFieldAlias),
|
||||||
|
},
|
||||||
|
// smooth: true,
|
||||||
|
label: {}, // 显示标签
|
||||||
|
legend: {
|
||||||
|
position: 'right-top',
|
||||||
|
// title: {
|
||||||
|
// text: '总合计 ' + dataSource.reduce((a, b) => a + b.SumOrder, 0),
|
||||||
|
// },
|
||||||
|
itemMarginBottom: 12, // 垂直间距
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
customItems: (originalItems) => {
|
||||||
|
return originalItems
|
||||||
|
.map((ele) => ({ ...ele, valueR: ele.data[yField] }))
|
||||||
|
.sort(sortBy('valueR'))
|
||||||
|
.reverse();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const [lineConfig, setLineConfig] = useState(cloneDeep(line_config));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
resetX();
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [rawData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLineConfig(cloneDeep(line_config));
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [isCompareLine,solidLineTime]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (lineChartX === 'day') {
|
||||||
|
setDataBeforeXChange(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [dataSource]);
|
||||||
|
|
||||||
|
// 日月年切换
|
||||||
|
const [lineChartX, setLineChartX] = useState('day');
|
||||||
|
const orderCountDataMapper = { data1: 'data1', data2: undefined };
|
||||||
|
const orderCountDataFieldMapper = { 'dateKey': xField, 'valueKey': yField, 'seriesKey': seriesField, _f: 'sum' };
|
||||||
|
const resetX = () => {
|
||||||
|
setLineChartX('day');
|
||||||
|
setDataSource(rawData);
|
||||||
|
setDataBeforeXChange(rawData);
|
||||||
|
// 初始化`平均`线, `总计`线
|
||||||
|
const byDays = groupBy(rawData, xField);
|
||||||
|
const sumY = rawData.reduce((a, b) => a + b[yField], 0);
|
||||||
|
// const avgVal = Math.round(sumY / (Object.keys(byDays).length));
|
||||||
|
// const avgLine = [
|
||||||
|
// { type: 'text', position: ['start', avgVal], content: avgVal, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } },
|
||||||
|
// { type: 'line', start: [-10, avgVal], end: ['max', avgVal], style: { stroke: '#F4664A', lineDash: [2, 2] } },
|
||||||
|
// ];
|
||||||
|
// setLineConfig({ ...lineConfig, yField, xField, annotations: avgLine });
|
||||||
|
setLineConfig({ ...lineConfig, yField, xField,});
|
||||||
|
if (showCompareSum) {
|
||||||
|
const _sumLine = Object.keys(byDays).reduce((r, _d) => {
|
||||||
|
const summaryVal = byDays[_d].reduce((rows, row) =>
|
||||||
|
{
|
||||||
|
if (row[seriesField].includes(solidLineTime)){
|
||||||
|
return rows + row[yField];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 0);
|
||||||
|
const summaryCompareVal = byDays[_d].reduce((rows, row) =>
|
||||||
|
{
|
||||||
|
if (row[seriesField].includes(solidLineCompareTime)){
|
||||||
|
return rows + row[yField];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 0);
|
||||||
|
r.push({ ...byDays[_d][0], [yField]: summaryVal, [seriesField]: solidLineTime+'总计' });
|
||||||
|
r.push({ ...byDays[_d][0], [yField]: summaryCompareVal, [seriesField]: solidLineCompareTime+'总计' });
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
setSumSeries(_sumLine);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
const _sumLine = Object.keys(byDays).reduce((r, _d) => {
|
||||||
|
const summaryVal = byDays[_d].reduce((rows, row) => rows + row[yField], 0);
|
||||||
|
r.push({ ...byDays[_d][0], [yField]: summaryVal, [seriesField]: '总计' });
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
// console.log(_sumLine.map((ele) => ele[yField]));
|
||||||
|
setSumSeries(_sumLine);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeXDateFieldGroup = (value, data, avg1) => {
|
||||||
|
// console.log(value, data, avg1);
|
||||||
|
const _sumLine = [];
|
||||||
|
const { xField, yField, seriesField } = lineConfig;
|
||||||
|
const groupByDate = data.reduce((r, v) => {
|
||||||
|
(r[v[xField]] || (r[v[xField]] = [])).push(v);
|
||||||
|
return r;
|
||||||
|
}, {});
|
||||||
|
// console.log(groupByDate);
|
||||||
|
const _data = Object.keys(groupByDate).reduce((r, _d) => {
|
||||||
|
if (showCompareSum) {
|
||||||
|
const summaryVal = groupByDate[_d].reduce((rows, row) =>
|
||||||
|
{
|
||||||
|
if (row[seriesField].includes(solidLineTime)){
|
||||||
|
return rows + row[yField];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 0);
|
||||||
|
const summaryCompareVal = groupByDate[_d].reduce((rows, row) =>
|
||||||
|
{
|
||||||
|
if (row[seriesField].includes(solidLineCompareTime)){
|
||||||
|
return rows + row[yField];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 0);
|
||||||
|
_sumLine.push({ ...groupByDate[_d][0], [yField]: summaryVal, [seriesField]: solidLineTime+'总计' });
|
||||||
|
_sumLine.push({ ...groupByDate[_d][0], [yField]: summaryCompareVal, [seriesField]: solidLineCompareTime+'总计' });
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
const summaryVal = groupByDate[_d].reduce((rows, row) => rows + row[yField], 0);
|
||||||
|
_sumLine.push({ ...groupByDate[_d][0], [yField]: summaryVal, [seriesField]: '总计' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const xAxisGroup = groupByDate[_d].reduce((a, v) => {
|
||||||
|
(a[v[seriesField]] || (a[v[seriesField]] = [])).push(v);
|
||||||
|
return a;
|
||||||
|
}, {});
|
||||||
|
// console.log(xAxisGroup);
|
||||||
|
Object.keys(xAxisGroup).map((_group) => {
|
||||||
|
const summaryVal = xAxisGroup[_group].reduce((rows, row) => rows + row[yField], 0);
|
||||||
|
r.push({ ...xAxisGroup[_group][0], [yField]: summaryVal, });
|
||||||
|
return _group;
|
||||||
|
});
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
// const _sum = Object.values(groupBy(_data, 'dateGroup')).reduce((ac, b) => ({...b, [yField]: 0}), {});
|
||||||
|
// console.log(xField, avg1);
|
||||||
|
// console.log('date source=====', _data);
|
||||||
|
setLineChartX(value);
|
||||||
|
setDataSource(_data);
|
||||||
|
setSumSeries(_sumLine);
|
||||||
|
|
||||||
|
// setAvgLine1(avg1);
|
||||||
|
// const avg1Int = Math.round(avg1);
|
||||||
|
// const mergedConfig = { ...lineConfig,
|
||||||
|
// annotations: [
|
||||||
|
// { type: 'text', position: ['start', avg1Int], content: avg1Int, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } },
|
||||||
|
// { type: 'line', start: [-10, avg1Int], end: ['max', avg1Int], style: { stroke: '#F4664A', lineDash: [2, 2] } },
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
// console.log(mergedConfig);
|
||||||
|
// setLineConfig(mergedConfig);
|
||||||
|
setLineConfig(cloneDeep(line_config));
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<Row gutter={16} justify={'space-between'} className="mb-1">
|
||||||
|
<Col flex={'auto'}>
|
||||||
|
<h3>
|
||||||
|
走势: <span style={{ fontSize: 'smaller' }}>{dataFieldAlias[lineConfig.yField].label}</span>
|
||||||
|
</h3>
|
||||||
|
</Col>
|
||||||
|
<Col style={{ textAlign: 'right' }} align={'end'}>
|
||||||
|
<DateGroupRadio
|
||||||
|
visible={true}
|
||||||
|
dataRaw={{ data1: dataBeforeXChange }}
|
||||||
|
onChange={onChangeXDateFieldGroup}
|
||||||
|
value={lineChartX}
|
||||||
|
dataMapper={orderCountDataMapper}
|
||||||
|
fieldMapper={orderCountDataFieldMapper}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<Line {...lineConfig} data={[].concat(dataSource, sumSeries)} />
|
||||||
|
</Spin>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
});
|
@ -1,60 +1,103 @@
|
|||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Line } from '@ant-design/plots';
|
import { Line } from '@ant-design/plots';
|
||||||
import { merge, isEmpty, groupBy } from '../utils/commons';
|
import { merge, isEmpty, groupBy, sortBy } from '../utils/commons';
|
||||||
import { dataFieldAlias } from '../libs/ht';
|
import { dataFieldAlias } from '../libs/ht';
|
||||||
|
|
||||||
const uniqueByKey = (array, key, pickLast) => {
|
|
||||||
const seen = new Map();
|
|
||||||
const isPickLast = pickLast === true;
|
|
||||||
|
|
||||||
return array.filter(item => {
|
|
||||||
const k = item[key];
|
|
||||||
const storedItem = seen.get(k);
|
|
||||||
|
|
||||||
if(storedItem) {
|
|
||||||
if(isPickLast) {
|
|
||||||
seen.set(k, item); // update with last item
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
seen.set(k, item);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default observer((props) => {
|
export default observer((props) => {
|
||||||
const { dataSource, ...config } = props;
|
const { dataSource, showKPI, ...config } = props;
|
||||||
const kpiKey = dataFieldAlias[config.yField]?.nestkey?.v;
|
const kpiKey = dataFieldAlias[config.yField]?.nestkey?.v;
|
||||||
const seriesData = groupBy(dataSource, ele => ele[config.seriesField]);
|
const seriesData = groupBy(dataSource, ele => ele[config.seriesField]);
|
||||||
const pickKey4KPI = Object.keys(seriesData)[0];
|
const splitData = showKPI ? dataSource.reduce((r, v) => {
|
||||||
const KPIData = (seriesData?.[pickKey4KPI] || []).reduce((r, v) => {
|
r.push(v);
|
||||||
if ( ! isEmpty(v[kpiKey])) { // 有设目标才多显示一条虚线 颜色: #F4664A
|
if ( ! isEmpty(v[kpiKey])) { // 有设目标才多显示一条虚线, 颜色和数据线一致
|
||||||
r.push({...v, [config.yField]: v[kpiKey], [config.seriesField]: dataFieldAlias[kpiKey].label, extraLine: true,});
|
r.push({...v, [config.yField]: v[kpiKey], [config.seriesField]: `${v[config.seriesField]} ${dataFieldAlias[kpiKey].label}`, extraLine: true,});
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}, []);
|
}, []).sort(sortBy(config.xField)) : (dataSource.slice()).sort(sortBy(config.xField));
|
||||||
const dataColors = ['#598cf3', '#69deae', '#FAAD14'];
|
const dataColors = [
|
||||||
const colorSets = Object.keys(seriesData).sort().reduce((obj, k, i) => ({...obj, [k]: dataColors[i]}), {});
|
"#5D7092","#F6BD16","#6F5EF9","#6DC8EC","#945FB9","#FF9845","#1E9493",
|
||||||
const mergeLineConfig = merge({
|
"#FF99C3","#FF6B3B","#626681","#FFC100","#9FB40F","#76523B","#DAD5B5",
|
||||||
// color: ['#598cf3', '#69deae', '#F4664A', '#FAAD14'],
|
"#0E8E89","#E19348","#F383A2","#247FEA","#5B8FF9","#5AD8A6",
|
||||||
color: (item) => {
|
];
|
||||||
const thisSeries = item[config.seriesField];
|
const colorSets = Object.keys(seriesData)
|
||||||
return thisSeries.includes('目标') ? '#F4664A' : colorSets[thisSeries];
|
.sort()
|
||||||
},
|
.filter((ele) => !ele.includes(' '))
|
||||||
lineStyle: (data) => {
|
.reduce((obj, k, i) => ({ ...obj, [k]: dataColors[i] || dataColors[i % 20] }), {});
|
||||||
// console.log(data);
|
// console.log('colorSets', colorSets);
|
||||||
if (data[config.seriesField].includes('目标')) {
|
const mergeLineConfig = merge(
|
||||||
|
{
|
||||||
|
color: (item) => {
|
||||||
|
const thisSeries = item[config.seriesField]?.split(' ')?.[0];
|
||||||
|
return colorSets[thisSeries];
|
||||||
|
},
|
||||||
|
lineStyle: (data) => {
|
||||||
|
if (data[config.seriesField].includes('目标')) {
|
||||||
|
return {
|
||||||
|
lineDash: [8, 20],
|
||||||
|
opacity: 0.5,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[config.seriesField].includes('@')) {
|
||||||
|
return {
|
||||||
|
lineDash: [4, 8],
|
||||||
|
opacity: 0.6,
|
||||||
|
lineWidth: 1.5,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lineDash: [4, 4],
|
opacity: 1,
|
||||||
opacity: 0.5,
|
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
legend: {
|
||||||
return {
|
custom: true,
|
||||||
opacity: 1,
|
items: Object.keys(seriesData)
|
||||||
};
|
.map((ele) => ({
|
||||||
|
id: ele,
|
||||||
|
name: ele,
|
||||||
|
value: ele,
|
||||||
|
marker: {
|
||||||
|
symbol: ele.includes(' ') ? 'hyphen' : 'circle',
|
||||||
|
style: { fill: colorSets[ele], stroke: colorSets[ele?.split(' ')?.[0]] || '#5B8FF9', r: 3, lineWidth: 2, color: colorSets[ele] },
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.sort(sortBy('name')),
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
// title: dataFieldAlias[config.yField]?.alias,
|
||||||
|
showTitle: true,
|
||||||
|
customItems: (items) => items.sort(sortBy('name')).map((ele) => ({ ...ele, title: `${ele.title} ${dataFieldAlias[config.yField]?.alias}` })),
|
||||||
|
},
|
||||||
|
// annotations: [
|
||||||
|
// // 低于 0 颜色变化
|
||||||
|
// {
|
||||||
|
// type: 'regionFilter',
|
||||||
|
// start: ['min', 0],
|
||||||
|
// end: ['max', 0],
|
||||||
|
// color: '#F4664A',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: 'text',
|
||||||
|
// position: ['min', 0],
|
||||||
|
// content: '0',
|
||||||
|
// offsetY: -4,
|
||||||
|
// style: {
|
||||||
|
// textBaseline: 'bottom',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: 'line',
|
||||||
|
// start: ['min', 0],
|
||||||
|
// end: ['max', 0],
|
||||||
|
// style: {
|
||||||
|
// stroke: '#F4664A',
|
||||||
|
// lineDash: [2, 2],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
},
|
},
|
||||||
}, config);
|
config
|
||||||
return <Line {...mergeLineConfig} data={[...dataSource, ...KPIData]} />;
|
);
|
||||||
|
return <Line {...mergeLineConfig} data={splitData} />;
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
import { useContext, useState, useEffect, useMemo } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { ChoroplethMap } from '@ant-design/maps';
|
||||||
|
import { dataFieldAlias } from '../libs/ht';
|
||||||
|
import { cloneDeep } from '../utils/commons';
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { dataSource, sourceField, valueField, containerNode, ...extConfig } = props;
|
||||||
|
|
||||||
|
const [mdataSource, setMdataSource] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
const dataMapped = (cloneDeep(dataSource) || []).reduce((r, v) => ({...r,
|
||||||
|
[(v[sourceField] || '_').replace('(待删除)', '')]: v
|
||||||
|
}), {});
|
||||||
|
if (dataMapped?.['中国']) {
|
||||||
|
dataMapped['中国'][sourceField] = '中华人民共和国';
|
||||||
|
}
|
||||||
|
setMdataSource(Object.values(dataMapped));
|
||||||
|
return () => {};
|
||||||
|
}, [dataSource, valueField]);
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
container: containerNode || '#topC',
|
||||||
|
map: {
|
||||||
|
// type: 'amap',
|
||||||
|
type: 'mapbox',
|
||||||
|
// style: 'blank',
|
||||||
|
center: [120.19382669582967, 30.258134],
|
||||||
|
zoom: 2,
|
||||||
|
pitch: 0,
|
||||||
|
// scrollZoom: false,
|
||||||
|
// dragPan: false,
|
||||||
|
// zoomEnable: false,
|
||||||
|
// token: 'd78b5ba25a4699a1cb567b7a933e630b', // amap
|
||||||
|
},
|
||||||
|
// geoArea: {
|
||||||
|
// url: 'https://gw.alipayobjects.com/os/alisis/geo-data-v0.1.2/choropleth-data',
|
||||||
|
// type: 'topojson',
|
||||||
|
// },
|
||||||
|
source: {
|
||||||
|
data: mdataSource.filter((ele) => ele[sourceField] && ele[valueField] !== 0 ),
|
||||||
|
joinBy: {
|
||||||
|
geoField: 'name',
|
||||||
|
sourceField: sourceField || 'name',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
autoFit: true,
|
||||||
|
color: {
|
||||||
|
field: valueField || 'value',
|
||||||
|
value: [
|
||||||
|
'#820C1B',
|
||||||
|
'#a31022',
|
||||||
|
'#ac2738',
|
||||||
|
'#b53f4e',
|
||||||
|
'#be5764',
|
||||||
|
'#c76f7a',
|
||||||
|
'#d18790',
|
||||||
|
'#da9fa6',
|
||||||
|
'#e3b7bc',
|
||||||
|
'#eccfd2',
|
||||||
|
'#f5e7e8',
|
||||||
|
'#fde7ea',
|
||||||
|
|
||||||
|
// '#8a1313',
|
||||||
|
// '#ad1818',
|
||||||
|
// '#b52f2f',
|
||||||
|
// '#bd4646',
|
||||||
|
// '#c55d5d',
|
||||||
|
// '#cd7474',
|
||||||
|
// '#d68b8b',
|
||||||
|
// '#dea2a2',
|
||||||
|
// '#e6b9b9',
|
||||||
|
// '#eed0d0',
|
||||||
|
// '#f6e7e7',
|
||||||
|
// '#fde7ea',
|
||||||
|
|
||||||
|
// '#001D70',
|
||||||
|
// '#0047A5',
|
||||||
|
// '#1A4397',
|
||||||
|
// '#2555B7',
|
||||||
|
// '#3165D1',
|
||||||
|
// '#3D76DD',
|
||||||
|
// '#467BE8',
|
||||||
|
// '#6296FE',
|
||||||
|
// '#7EA6F9',
|
||||||
|
// '#98B7F7',
|
||||||
|
// '#BDD0F8',
|
||||||
|
// '#DDE6F7',
|
||||||
|
// '#F2F5FC'
|
||||||
|
].reverse(),
|
||||||
|
scale: { type: 'quantile' },
|
||||||
|
},
|
||||||
|
viewLevel: {
|
||||||
|
// level: 'country',
|
||||||
|
// adcode: '100000',
|
||||||
|
// granularity: 'province',
|
||||||
|
level: 'world',
|
||||||
|
adcode: 'all',
|
||||||
|
granularity: 'country',
|
||||||
|
},
|
||||||
|
chinaBorder: false,
|
||||||
|
style: {
|
||||||
|
opacity: 1,
|
||||||
|
stroke: '#fff',
|
||||||
|
lineWidth: 0.6,
|
||||||
|
lineOpacity: 1,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
stroke: 'yellow',
|
||||||
|
lineWidth: 0.6,
|
||||||
|
// lineOpacity: 0.8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
visible: true,
|
||||||
|
field: 'name',
|
||||||
|
style: {
|
||||||
|
fill: '#000',
|
||||||
|
opacity: 0.8,
|
||||||
|
fontSize: 10,
|
||||||
|
stroke: '#fff',
|
||||||
|
strokeWidth: 1.5,
|
||||||
|
textAllowOverlap: false,
|
||||||
|
padding: [8, 8],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
items: ['name', { field: valueField, alias: dataFieldAlias[valueField].alias, customValue: (v) => dataFieldAlias[valueField].formatter(v) }],
|
||||||
|
},
|
||||||
|
zoom: false,
|
||||||
|
legend: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ChoroplethMap {...config} />;
|
||||||
|
});
|
@ -0,0 +1,265 @@
|
|||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { message } from 'antd';
|
||||||
|
import { Mix, getCanvasPattern, } from '@ant-design/plots';
|
||||||
|
import { merge, isEmpty, cloneDeep } from '../utils/commons';
|
||||||
|
import { dataFieldAlias } from '../libs/ht';
|
||||||
|
|
||||||
|
const COLOR_SETS = [
|
||||||
|
"#FF6B3B",
|
||||||
|
"#9FB40F",
|
||||||
|
"#76523B",
|
||||||
|
"#DAD5B5",
|
||||||
|
"#E19348",
|
||||||
|
"#F383A2",
|
||||||
|
];
|
||||||
|
const COLOR_SETS2 = [
|
||||||
|
"#5B8FF9",
|
||||||
|
"#61DDAA",
|
||||||
|
"#65789B",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单数, 团数: 柱形图
|
||||||
|
* 成交率: 折线图
|
||||||
|
*/
|
||||||
|
export default observer((props) => {
|
||||||
|
const { dataSource, summaryData: areaData, ...config } = props;
|
||||||
|
const { xField, yFields, colFields, lineFields, seriesField, tooltip, ...extConfig } = config;
|
||||||
|
const diffData0 = dataSource.reduce((r, row) => {
|
||||||
|
r.push({ ...row, yField: row[colFields[0]], yGroup: dataFieldAlias[colFields[0]].alias });
|
||||||
|
r.push({ ...row, yField: row[colFields[1]], yGroup: dataFieldAlias[colFields[1]].alias });
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
const diffData1 = dataSource.reduce((r, row) => {
|
||||||
|
r.push({ ...row, yField: row[lineFields[1]], yGroup: dataFieldAlias[lineFields[1]].alias });
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
const calcAxis = isEmpty(diffData0) ? 300 : (Math.max(...diffData0.map(ele => ele.yField))) * 3;
|
||||||
|
// const calcAxisC = isEmpty(diffData0) ? 300 : (Math.max(...diffDataPercent.map(ele => ele.yField))) * 3;
|
||||||
|
const diffLine = [
|
||||||
|
// {
|
||||||
|
// type: 'text',
|
||||||
|
// position: ['start', 0],
|
||||||
|
// content: `同比, 环比`,
|
||||||
|
// offsetX: -15,
|
||||||
|
// style: {
|
||||||
|
// fill: COLOR_SETS[0],
|
||||||
|
// textBaseline: 'bottom',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: 'line',
|
||||||
|
// start: [-10, 0],
|
||||||
|
// end: ['max', 0],
|
||||||
|
// style: {
|
||||||
|
// stroke: COLOR_SETS[0],
|
||||||
|
// // lineDash: [2, 2],
|
||||||
|
// lineWidth: 0.5,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
];
|
||||||
|
|
||||||
|
const pattern = (datum, color) => {
|
||||||
|
return getCanvasPattern({
|
||||||
|
type: String(datum.yGroup).includes(' ') ? 'line' : '',
|
||||||
|
cfg: {
|
||||||
|
backgroundColor: color,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const MixConfig = {
|
||||||
|
appendPadding: 15,
|
||||||
|
height: 400,
|
||||||
|
syncViewPadding: true,
|
||||||
|
tooltip: {
|
||||||
|
shared: true,
|
||||||
|
// customItems: (originalItems) => {
|
||||||
|
// // process originalItems,
|
||||||
|
// const items = originalItems.map((ele) => ({ ...ele, name: ele.data?.extraLine ? ele.name : `${ele.name} ${dataFieldAlias[yField]?.alias || yField}` }));
|
||||||
|
// return items;
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
layout: 'horizontal',
|
||||||
|
custom: true,
|
||||||
|
items: [
|
||||||
|
...['团数', '订单数'].map((ele, ei) => ({
|
||||||
|
name: `${ele}`,
|
||||||
|
value: `${ele}`,
|
||||||
|
marker: {
|
||||||
|
symbol: 'square',
|
||||||
|
style: {
|
||||||
|
fill: COLOR_SETS2[ei],
|
||||||
|
r: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
...['', '成行率'].map((ele, ei) => ({ // '业绩',
|
||||||
|
name: `${ele}`,
|
||||||
|
value: `${ele}`,
|
||||||
|
marker: {
|
||||||
|
symbol: 'hyphen',
|
||||||
|
style: {
|
||||||
|
stroke: COLOR_SETS[ei],
|
||||||
|
r: 5,
|
||||||
|
lineWidth: 2
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// event: (chart, e) => {
|
||||||
|
// console.log('mix', chart, e);
|
||||||
|
// if (e.type === 'click') {
|
||||||
|
// props.itemClick(e);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
onReady: (plot) => {
|
||||||
|
plot.on('plot:click', (...args) => {
|
||||||
|
// message.info('请在柱状图上点击, 显示详情');
|
||||||
|
});
|
||||||
|
plot.on('element:click', (e) => {
|
||||||
|
const {
|
||||||
|
data: { data },
|
||||||
|
} = e;
|
||||||
|
// console.log('plot element', data);
|
||||||
|
props.itemClick(data);
|
||||||
|
});
|
||||||
|
// axis-label 添加点击事件
|
||||||
|
plot.on('axis-label:click', (e, ...args) => {
|
||||||
|
const { text } = e.target.attrs;
|
||||||
|
// console.log(text);
|
||||||
|
props.itemClick({ [xField]: text });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
type: 'column',
|
||||||
|
options: {
|
||||||
|
data: diffData0,
|
||||||
|
isGroup: true,
|
||||||
|
xField,
|
||||||
|
yField: 'yField',
|
||||||
|
seriesField: 'yGroup',
|
||||||
|
// xAxis: false,
|
||||||
|
meta: merge({
|
||||||
|
...cloneDeep(dataFieldAlias),
|
||||||
|
}),
|
||||||
|
// color: '#b32b19',
|
||||||
|
// color: '#f58269',
|
||||||
|
legend: false, // {},
|
||||||
|
// smooth: true,
|
||||||
|
yAxis: {
|
||||||
|
type: 'linear',
|
||||||
|
tickCount: 4,
|
||||||
|
min: 0,
|
||||||
|
max: calcAxis,
|
||||||
|
title: { text: '团数', autoRotate: false, position: 'end' },
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
label: {
|
||||||
|
autoHide: false,
|
||||||
|
autoRotate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: false,
|
||||||
|
color: COLOR_SETS2,
|
||||||
|
pattern,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
data: diffData1,
|
||||||
|
isGroup: true,
|
||||||
|
xField,
|
||||||
|
yField: 'yField',
|
||||||
|
seriesField: 'yGroup',
|
||||||
|
xAxis: false,
|
||||||
|
legend: false, // {},
|
||||||
|
meta: merge(
|
||||||
|
{
|
||||||
|
...cloneDeep(dataFieldAlias),
|
||||||
|
},
|
||||||
|
{ yField: dataFieldAlias[lineFields[1]] }
|
||||||
|
),
|
||||||
|
// color: '#1AAF8B',
|
||||||
|
color: COLOR_SETS[1],
|
||||||
|
// smooth: true,
|
||||||
|
point: {
|
||||||
|
size: 4,
|
||||||
|
shape: 'cicle',
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'linear',
|
||||||
|
// tickCount: 4,
|
||||||
|
min: 0,
|
||||||
|
position: 'right',
|
||||||
|
line: null,
|
||||||
|
grid: null,
|
||||||
|
title: { text: dataFieldAlias[lineFields[1]].label, autoRotate: false, position: 'end' },
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
style: {
|
||||||
|
fontWeight: 700,
|
||||||
|
stroke: '#fff',
|
||||||
|
lineWidth: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lineStyle: (datum) => {
|
||||||
|
if (String(datum.yGroup).includes(' ')) {
|
||||||
|
return {
|
||||||
|
lineDash: [4, 4],
|
||||||
|
opacity: 0.75,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
opacity: 1,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// type: 'column',
|
||||||
|
// options: {
|
||||||
|
// data: diffData2,
|
||||||
|
// xField,
|
||||||
|
// yField: 'yField',
|
||||||
|
// seriesField: 'yGroup',
|
||||||
|
// columnWidthRatio: 0.28,
|
||||||
|
// meta: {
|
||||||
|
// // yField: {
|
||||||
|
// // formatter: (v) => `${v}%`,
|
||||||
|
// // },
|
||||||
|
// },
|
||||||
|
// isGroup: true,
|
||||||
|
// xAxis: false,
|
||||||
|
// yAxis: {
|
||||||
|
// line: null,
|
||||||
|
// grid: null,
|
||||||
|
// label: false,
|
||||||
|
// position: 'left',
|
||||||
|
// // min: -calcAxisC,
|
||||||
|
// // max: calcAxisC/4,
|
||||||
|
// min: -3000,
|
||||||
|
// max: 250,
|
||||||
|
// tickCount: 4,
|
||||||
|
// },
|
||||||
|
// legend: false, // {},
|
||||||
|
// color: COLOR_SETS,
|
||||||
|
// // annotations: diffLine,
|
||||||
|
|
||||||
|
// minColumnWidth: 5,
|
||||||
|
// maxColumnWidth: 5,
|
||||||
|
// // 分组柱状图 组内柱子间的间距 (像素级别)
|
||||||
|
// dodgePadding: 1,
|
||||||
|
// // 分组柱状图 组间的间距 (像素级别)
|
||||||
|
// // intervalPadding: 20,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return <Mix {...MixConfig} />;
|
||||||
|
});
|
@ -0,0 +1,272 @@
|
|||||||
|
import { observer } from 'mobx-react';
|
||||||
|
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, seriesField, tooltip, ...extConfig } = config;
|
||||||
|
const diffData0 = dataSource.reduce((r, row) => {
|
||||||
|
r.push({ ...row, yField: row[yFields[0]], yGroup: dataFieldAlias[yFields[0]].alias });
|
||||||
|
r.push({ ...row, yField: row.resultToQ[yFields[0]], yGroup: dataFieldAlias[yFields[0]].alias + ' 上个时间段' });
|
||||||
|
r.push({ ...row, yField: row.resultToY[yFields[0]], yGroup: dataFieldAlias[yFields[0]].alias + ' 去年同期' });
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
const diffData1 = dataSource.reduce((r, row) => {
|
||||||
|
r.push({ ...row, yField: row[yFields[1]], yGroup: dataFieldAlias[yFields[1]].alias });
|
||||||
|
r.push({ ...row, yField: row.resultToQ[yFields[1]], yGroup: dataFieldAlias[yFields[1]].alias + ' 上个时间段' });
|
||||||
|
r.push({ ...row, yField: row.resultToY[yFields[1]], yGroup: dataFieldAlias[yFields[1]].alias + ' 去年同期' });
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
const diffDataPercent = dataSource.reduce((r, row) => {
|
||||||
|
const _key0Q = `${yFields[0]}ToQ`;
|
||||||
|
const _key0Y = `${yFields[0]}ToY`;
|
||||||
|
r.push({ ...row, yField: row[_key0Q], yGroup: dataFieldAlias[yFields[0]].alias + ' 环比' });
|
||||||
|
r.push({ ...row, yField: row[_key0Y], yGroup: dataFieldAlias[yFields[0]].alias + ' 同比' });
|
||||||
|
const _key1Q = `${yFields[1]}ToQ`;
|
||||||
|
const _key1Y = `${yFields[1]}ToY`;
|
||||||
|
r.push({ ...row, yField: row[_key1Q], yGroup: dataFieldAlias[yFields[1]].alias + ' 环比' });
|
||||||
|
r.push({ ...row, yField: row[_key1Y], yGroup: dataFieldAlias[yFields[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: 600,
|
||||||
|
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_SETS2[ei],
|
||||||
|
r: 5,
|
||||||
|
lineWidth: 2
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
...['环比', '同比'].map((ele, ei) => ({
|
||||||
|
name: `团数 ${ele}`,
|
||||||
|
value: `团数 ${ele}`,
|
||||||
|
marker: {
|
||||||
|
symbol: 'square',
|
||||||
|
style: {
|
||||||
|
fill: COLOR_SETS[ei],
|
||||||
|
r: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
...['环比', '同比'].map((ele, ei) => ({
|
||||||
|
name: `业绩 ${ele}`,
|
||||||
|
value: `业绩 ${ele}`,
|
||||||
|
marker: {
|
||||||
|
symbol: 'square',
|
||||||
|
style: {
|
||||||
|
fill: COLOR_SETS[ei+2],
|
||||||
|
r: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
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[yFields[1]] }
|
||||||
|
),
|
||||||
|
// color: '#1AAF8B',
|
||||||
|
// smooth: true,
|
||||||
|
point: {
|
||||||
|
size: 4,
|
||||||
|
shape: 'cicle',
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'linear',
|
||||||
|
// tickCount: 4,
|
||||||
|
min: 0,
|
||||||
|
position: 'right',
|
||||||
|
line: null,
|
||||||
|
grid: null,
|
||||||
|
title: { text: '业绩', 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: diffDataPercent,
|
||||||
|
xField,
|
||||||
|
yField: 'yField',
|
||||||
|
seriesField: 'yGroup',
|
||||||
|
columnWidthRatio: 0.28,
|
||||||
|
meta: {
|
||||||
|
yField: {
|
||||||
|
formatter: (v) => `${v}%`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isGroup: true,
|
||||||
|
xAxis: false,
|
||||||
|
yAxis: {
|
||||||
|
line: null,
|
||||||
|
grid: null,
|
||||||
|
label: false,
|
||||||
|
position: 'left',
|
||||||
|
// min: -calcAxisC,
|
||||||
|
// max: calcAxisC/4,
|
||||||
|
min: -3000,
|
||||||
|
max: 250,
|
||||||
|
tickCount: 4,
|
||||||
|
},
|
||||||
|
legend: false, // {},
|
||||||
|
color: COLOR_SETS,
|
||||||
|
annotations: diffLine,
|
||||||
|
|
||||||
|
minColumnWidth: 5,
|
||||||
|
maxColumnWidth: 5,
|
||||||
|
// 分组柱状图 组内柱子间的间距 (像素级别)
|
||||||
|
dodgePadding: 1,
|
||||||
|
// 分组柱状图 组间的间距 (像素级别)
|
||||||
|
// intervalPadding: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return <Mix {...MixConfig} />;
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
import Icon, {} from '@ant-design/icons';
|
||||||
|
|
||||||
|
const CooperationSvg = () => (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11.8611 2.39057C12.8495 1.73163 14.1336 1.71797 15.1358 2.35573L19.291 4.99994H20.9998C21.5521 4.99994 21.9998 5.44766 21.9998 5.99994V14.9999C21.9998 15.5522 21.5521 15.9999 20.9998 15.9999H19.4801C19.5396 16.9472 19.0933 17.9102 18.1955 18.4489L13.1021 21.505C12.4591 21.8907 11.6609 21.8817 11.0314 21.4974C10.3311 22.1167 9.2531 22.1849 8.47104 21.5704L3.33028 17.5312C2.56387 16.9291 2.37006 15.9003 2.76579 15.0847C2.28248 14.7057 2 14.1254 2 13.5109V6C2 5.44772 2.44772 5 3 5H7.94693L11.8611 2.39057ZM4.17264 13.6452L4.86467 13.0397C6.09488 11.9632 7.96042 12.0698 9.06001 13.2794L11.7622 16.2518C12.6317 17.2083 12.7903 18.6135 12.1579 19.739L17.1665 16.7339C17.4479 16.5651 17.5497 16.2276 17.4448 15.9433L13.0177 9.74551C12.769 9.39736 12.3264 9.24598 11.9166 9.36892L9.43135 10.1145C8.37425 10.4316 7.22838 10.1427 6.44799 9.36235L6.15522 9.06958C5.58721 8.50157 5.44032 7.69318 5.67935 7H4V13.5109L4.17264 13.6452ZM14.0621 4.04306C13.728 3.83047 13.3 3.83502 12.9705 4.05467L7.56943 7.65537L7.8622 7.94814C8.12233 8.20827 8.50429 8.30456 8.85666 8.19885L11.3419 7.45327C12.5713 7.08445 13.8992 7.53859 14.6452 8.58303L18.5144 13.9999H19.9998V6.99994H19.291C18.9106 6.99994 18.5381 6.89148 18.2172 6.68727L14.0621 4.04306ZM6.18168 14.5448L4.56593 15.9586L9.70669 19.9978L10.4106 18.7659C10.6256 18.3897 10.5738 17.9178 10.2823 17.5971L7.58013 14.6247C7.2136 14.2215 6.59175 14.186 6.18168 14.5448Z"></path></svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
const CooperationIcon = (props) => <Icon component={CooperationSvg} {...props} />;
|
||||||
|
|
||||||
|
export default CooperationIcon;
|
@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Select } from 'antd';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { HotelStars as options } from '../../libs/ht';
|
||||||
|
|
||||||
|
const HotelStars = (props) => {
|
||||||
|
const { mode, value, onChange, show_all, ...extProps } = props;
|
||||||
|
const _show_all = ['tags', 'multiple'].includes(mode) ? false : show_all;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
mode={mode || null}
|
||||||
|
allowClear
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder="星级"
|
||||||
|
value={value || undefined}
|
||||||
|
onChange={(value) => {
|
||||||
|
if (typeof onChange === 'function') {
|
||||||
|
onChange(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
labelInValue={false}
|
||||||
|
{...extProps}
|
||||||
|
options={options}
|
||||||
|
>
|
||||||
|
{_show_all ? (
|
||||||
|
<Select.Option key="-1" value="ALL">
|
||||||
|
ALL
|
||||||
|
</Select.Option>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 酒店星级
|
||||||
|
*/
|
||||||
|
export default observer(HotelStars);
|
@ -1,7 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import packageInfo from './../package.json';
|
||||||
|
|
||||||
|
export const APP_VERSION = packageInfo.version;
|
||||||
export const stores_Context = React.createContext();
|
export const stores_Context = React.createContext();
|
||||||
export const DATE_FORMAT = "YYYY-MM-DD";
|
export const DATE_FORMAT = "YYYY-MM-DD";
|
||||||
export const SMALL_DATETIME_FORMAT = 'YYYY-MM-DD 23:59:00';
|
export const SMALL_DATETIME_FORMAT = 'YYYY-MM-DD 23:59:00';
|
||||||
export const DATETIME_FORMAT = 'YYYY-MM-DD 23:59:59';
|
export const DATETIME_FORMAT = 'YYYY-MM-DD 23:59:59';
|
||||||
export const HT_HOST = process.env.NODE_ENV === "production" ? "https://p9axztuwd7x8a7.mycht.cn" : "http://202.103.68.100:890";
|
export const HT_HOST = process.env.NODE_ENV === "production" ? "https://p9axztuwd7x8a7.mycht.cn" : "http://202.103.68.144:890";
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"get|/inbound_person_num/test": {
|
||||||
|
"errcode": 0,
|
||||||
|
"errmsg": "",
|
||||||
|
"data": null,
|
||||||
|
"loading": null,
|
||||||
|
"resultTotal": {
|
||||||
|
"orgz": "@integer(10,99)",
|
||||||
|
"orgzPDays": "@integer(10,99)",
|
||||||
|
"hosts": "@integer(10,99)",
|
||||||
|
"hostsPDays": "@integer(10,99)",
|
||||||
|
"IndividualService": "@integer(10,99)",
|
||||||
|
"groupsKey": "0",
|
||||||
|
"groupsLabel": "总"
|
||||||
|
},
|
||||||
|
"result|10": [
|
||||||
|
{
|
||||||
|
"orgz": "@integer(10,99)",
|
||||||
|
"orgzPDays": "@integer(10,99)",
|
||||||
|
"hosts": "@integer(10,99)",
|
||||||
|
"hostsPDays": "@integer(10,99)",
|
||||||
|
"groupsKey": "@id",
|
||||||
|
"groupsLabel": "@region"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,59 +1,61 @@
|
|||||||
import { makeAutoObservable, runInAction } from "mobx";
|
import { makeAutoObservable, runInAction } from 'mobx';
|
||||||
import * as dd from "dingtalk-jsapi";
|
import * as dd from 'dingtalk-jsapi';
|
||||||
import * as config from "../config";
|
import * as config from '../config';
|
||||||
|
|
||||||
// 权限管理
|
// 权限管理
|
||||||
class AuthStore {
|
class AuthStore {
|
||||||
constructor(rootStore) {
|
constructor(rootStore) {
|
||||||
this.rootStore = rootStore;
|
this.rootStore = rootStore;
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
if (process.env.NODE_ENV == "production") {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
this.get_auth(); // 放到钉钉环境才能开启
|
this.get_auth(); // 放到钉钉环境才能开启
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auth = ["admin"]; // 开发时候用,正式环境留空
|
auth = process.env.NODE_ENV === 'production' ? [] : ['admin']; // 开发时候用,正式环境留空
|
||||||
user = { name: "loading", userid: "..." }; // 开发时候用,正式环境留空
|
user = { name: 'loading', userid: '...' }; // 开发时候用,正式环境留空
|
||||||
|
|
||||||
has_permission(requireds) {
|
has_permission(requireds) {
|
||||||
if (Object.keys(requireds).length == 0) {
|
if (Object.keys(requireds).length === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const has_permission = requireds.filter(item => this.auth.includes(item));
|
const has_permission = requireds.filter((item) => this.auth.includes(item));
|
||||||
if (Object.keys(has_permission).length !== 0) {
|
if (Object.keys(has_permission).length !== 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求权限
|
// 请求权限
|
||||||
get_auth() {
|
get_auth() {
|
||||||
const _this = this;
|
const _this = this;
|
||||||
const CORPID = "ding48bce8fd3957c96b"; // 企业的id
|
const CORPID = 'ding48bce8fd3957c96b'; // 企业的id
|
||||||
dd.runtime.permission.requestAuthCode({
|
dd.runtime.permission.requestAuthCode({
|
||||||
corpId: CORPID,
|
corpId: CORPID,
|
||||||
onSuccess: function (res) {
|
onSuccess: function (res) {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
const code = res.code;
|
const code = res.code;
|
||||||
const url = "/dingtalk/dingtalkwork/Getusers_auth?code=" + code;
|
const url = '/dingtalk/dingtalkwork/Getusers_auth?code=' + code;
|
||||||
// 请求获取HT接口获取用户权限和用户信息
|
// 请求获取HT接口获取用户权限和用户信息
|
||||||
fetch(config.HT_HOST + url)
|
fetch(config.HT_HOST + url)
|
||||||
.then(response => response.json())
|
.then((response) => response.json())
|
||||||
.then(json => {
|
.then((json) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
_this.user = json.result;
|
_this.user = json.result;
|
||||||
_this.auth = json.result.authlist;
|
_this.auth = json.result.authlist;
|
||||||
});
|
window.__spytitle = json.result.name;
|
||||||
})
|
window.initPageSpy();
|
||||||
.catch(error => {
|
});
|
||||||
console.log("fetch data failed", error);
|
})
|
||||||
});
|
.catch((error) => {
|
||||||
},
|
console.log('fetch data failed', error);
|
||||||
onFail: function (err) {
|
});
|
||||||
console.log(err);
|
},
|
||||||
},
|
onFail: function (err) {
|
||||||
});
|
console.log(err);
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AuthStore;
|
export default AuthStore;
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
import { makeAutoObservable, runInAction, toJS } from 'mobx';
|
||||||
|
import { fetchJSON } from '../utils/request';
|
||||||
|
import { isEmpty, sortBy, pick, merge, fixTo2Decimals, groupBy, sortKeys, fixToInt, cloneDeep } from '../utils/commons';
|
||||||
|
import { dataFieldAlias } from './../libs/ht';
|
||||||
|
|
||||||
|
class Trade {
|
||||||
|
constructor(rootStore) {
|
||||||
|
this.rootStore = rootStore;
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 明细
|
||||||
|
*/
|
||||||
|
getDetailData = async (param, page) => {
|
||||||
|
this.detailData[page] = { loading: true, dataSource: [], originData: [] };
|
||||||
|
const json = await fetchJSON('/service-Analyse2/GetTradeApartDetail', param);
|
||||||
|
if (json.errcode === 0) {
|
||||||
|
runInAction(() => {
|
||||||
|
this.detailData[page].loading = false;
|
||||||
|
this.detailData[page].dataSource = json.result;
|
||||||
|
this.detailData[page].originData = json.result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return json.result;
|
||||||
|
};
|
||||||
|
|
||||||
|
setSearchValues(body) {
|
||||||
|
this.searchValues = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeLineKey = 'week';
|
||||||
|
setTimeLineKey(v) {
|
||||||
|
this.timeLineKey = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetData = () => {
|
||||||
|
this.detailData = {
|
||||||
|
orders: { loading: false, dataSource: [], originData: [] },
|
||||||
|
trade: { loading: false, dataSource: [], originData: [] },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
searchValues = {};
|
||||||
|
detailData = {
|
||||||
|
orders: { loading: false, dataSource: [], originData: [] },
|
||||||
|
trade: { loading: false, dataSource: [], originData: [] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Trade;
|
@ -0,0 +1,355 @@
|
|||||||
|
import { makeAutoObservable, runInAction } from 'mobx';
|
||||||
|
import { fetchJSON } from '../utils/request';
|
||||||
|
import { objectMapper, pick, price_to_number, } from '../utils/commons';
|
||||||
|
import { pivotBy } from '../libs/ht';
|
||||||
|
import moment from "moment";
|
||||||
|
import { DATE_FORMAT, DATETIME_FORMAT, SMALL_DATETIME_FORMAT } from '../config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于透视的数据
|
||||||
|
*/
|
||||||
|
const getDetailData = async (param) => {
|
||||||
|
const json = await fetchJSON('/service-Analyse2/GetTradeApartDetail', param);
|
||||||
|
return json.errcode === 0 ? json.result : [];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const getOrderCountByType = async (param) => {
|
||||||
|
const paramBody = objectMapper(param, {
|
||||||
|
WebCode: 'WebCode',
|
||||||
|
OrderType: 'OrderType',
|
||||||
|
IncludeTickets: 'IncludeTickets',
|
||||||
|
DateType: 'DateType',
|
||||||
|
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||||
|
Date1: 'COLI_ApplyDate1',
|
||||||
|
Date2: 'COLI_ApplyDate2',
|
||||||
|
});
|
||||||
|
const url = '/service-web/QueryData/GetOrderCountByType';
|
||||||
|
const json = await fetchJSON(url, paramBody);
|
||||||
|
return json.errcode === 0 ? json : {};
|
||||||
|
};
|
||||||
|
const getAgentGroupInfoALL = async (param) => {
|
||||||
|
const paramBody = objectMapper(param, {
|
||||||
|
DateType: 'DateType',
|
||||||
|
DepartmentList: 'DepList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||||
|
Date1: 'OldDate1',
|
||||||
|
Date2: 'OldDate2',
|
||||||
|
});
|
||||||
|
const url = '/service-web/QueryData/GetAgentGroupInfoALL';
|
||||||
|
const json = await fetchJSON(url, paramBody);
|
||||||
|
return json.errcode === 0 ? json : {};
|
||||||
|
};
|
||||||
|
const getDepartmentOrderMLByType = async (param) => {
|
||||||
|
const paramBody = objectMapper(param, {
|
||||||
|
DateType: 'DateType',
|
||||||
|
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||||
|
OrderType: 'OrderType', // 总览, 产品类型
|
||||||
|
});
|
||||||
|
const url = '/service-web/QueryData/GetDepartmentOrderMLByType';
|
||||||
|
const json = await fetchJSON(url, paramBody);
|
||||||
|
const { result1 } = json.errcode === 0 ? json : { result1: [] };
|
||||||
|
const total1 = ['COLI_CJCount', 'COLI_ML2',].reduce(
|
||||||
|
(r, col) => ({
|
||||||
|
...r,
|
||||||
|
[col]: result1.reduce((rr, row) => rr + row[col], 0),
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
return { total1, result1 };
|
||||||
|
};
|
||||||
|
const GHproductTypeListSetting = {
|
||||||
|
ja: ['日本', '东亚跨国'],
|
||||||
|
se: ['东南亚跨国', '泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝'],
|
||||||
|
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||||
|
};
|
||||||
|
const GHCountryListSetting = {
|
||||||
|
ja: ['日本', ],
|
||||||
|
se: ['泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝', '新加坡', '马来西亚', '菲律宾'],
|
||||||
|
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowItem = (filterData) => {
|
||||||
|
const { data: dataByLineClass, summaryMix: summaryByLineClass } = pivotBy(filterData, [['COLI_LineClass'], [], []]);
|
||||||
|
const LineClass_Origin = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
const LineClass_PPC = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
|
||||||
|
const { data: dataByWebCode, summaryMix: summaryByWebCode } = pivotBy(filterData, [['WebCode'], [], []]);
|
||||||
|
const toB = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
const external = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf("站外渠道") !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
|
||||||
|
const filterIsOldC = filterData.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') === -1);
|
||||||
|
const { data: dataByIsOld, summaryMix: summaryByIsOld } = pivotBy(filterIsOldC, [['IsOld', 'isCusCommend'], [], []]);
|
||||||
|
const isOld1 = dataByIsOld.filter((ele) => ele.rowLabel.indexOf('1') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
|
||||||
|
const total = LineClass_Origin + LineClass_PPC + toB + isOld1 + external;
|
||||||
|
return { LineClass_Origin, LineClass_PPC, toB, external, isOld1, total };
|
||||||
|
};
|
||||||
|
// 日本+: 日本+东亚跨国
|
||||||
|
const dataJA = (rawData, yearData) => {
|
||||||
|
const productTypeList = GHproductTypeListSetting.ja;
|
||||||
|
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 东南亚+: 东南亚跨国+泰国+越南+印尼+水灯节线路
|
||||||
|
const dataSE = (rawData, yearData) => {
|
||||||
|
const productTypeList = GHproductTypeListSetting.se;
|
||||||
|
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 印度+: 印度+次大陆跨国+尼泊尔+不丹+斯里兰卡
|
||||||
|
const dataIN = (rawData, yearData) => {
|
||||||
|
const productTypeList = GHproductTypeListSetting.in;
|
||||||
|
const exceptProduct = ['印度尼西亚'];
|
||||||
|
const filterData = rawData
|
||||||
|
.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||||
|
.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const filterDataYear = yearData
|
||||||
|
.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||||
|
.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 其他GH
|
||||||
|
const dataGHOther = (rawData, yearData) => {
|
||||||
|
const exceptProduct = Object.values(GHproductTypeListSetting).reduce((r, c) => r.concat(c), []);
|
||||||
|
const filterData = rawData.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const filterDataYear = yearData.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataSales = (tKey, rawData, yearData, yearData2) => {
|
||||||
|
const targetList = GHCountryListSetting[tKey];
|
||||||
|
const tIndex = Object.keys(GHCountryListSetting).indexOf(tKey);
|
||||||
|
const exceptTargetList = Object.keys(GHCountryListSetting).reduce((r, c, i) => r.concat(i < tIndex ? GHCountryListSetting[c] : []), []);
|
||||||
|
// console.log(tIndex, tKey, 'exceptTargetList', exceptTargetList, 'targetList', targetList);
|
||||||
|
|
||||||
|
const filterRaw1 = rawData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
// console.log(tKey, 'filterRaw1', filterRaw1);
|
||||||
|
const filterDataC = filterRaw1.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataT = tKey === 'se' ? filterRaw1.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||||
|
const filterData = filterDataC.concat(filterDataT);
|
||||||
|
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||||
|
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||||
|
|
||||||
|
const filterRaw2 = yearData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYearC = filterRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYearT = tKey === 'se' ? filterRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||||
|
const filterDataYear = filterDataYearC.concat(filterDataYearT);
|
||||||
|
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
// console.log(tKey, filterDataYear.map(ee => ee.destinationCountry_AsJOSN), filterDataYear.map(ee => ee.productType), filterDataYear);
|
||||||
|
|
||||||
|
const filterDataYearRaw2 = yearData2.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYear2C = filterDataYearRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYear2T = tKey === 'se' ? filterDataYearRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||||
|
const filterDataYear2 = filterDataYear2C.concat(filterDataYear2T);
|
||||||
|
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
const rowYear = {
|
||||||
|
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||||
|
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cols = ['YJLY', 'CJCount'].reduce((r, key) => ({ ...r, [key]: filterData.reduce((a, c) => a + price_to_number(c[key]), 0) }), {});
|
||||||
|
// console.log(tKey, filterData, filterDataYear, filterDataYear2);
|
||||||
|
return { ...cols, GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||||
|
};
|
||||||
|
const dataSalesGHOther = (rawData, yearData, yearData2) => {
|
||||||
|
const exceptContry = Object.values(GHCountryListSetting).reduce((r, c) => r.concat(c), []);
|
||||||
|
// console.log('exceptContry', exceptContry);
|
||||||
|
// console.log('OOoo rawData', rawData.map(e => e.destinationCountry_AsJOSN));
|
||||||
|
const filterData = rawData
|
||||||
|
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||||
|
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
// console.log('OOoo', filterData.map(e => e.destinationCountry_AsJOSN), filterData.map(e => e.productType));
|
||||||
|
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||||
|
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||||
|
|
||||||
|
const filterDataYear = yearData
|
||||||
|
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||||
|
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
// console.log('OOoo year', filterDataYear.map(e => e.destinationCountry_AsJOSN), filterDataYear.map(e => e.productType));
|
||||||
|
|
||||||
|
const filterDataYear2 = yearData2
|
||||||
|
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||||
|
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
// console.log('Oo', filterDataYear2.map(e => e.destinationCountry_AsJOSN), filterDataYear2);
|
||||||
|
// console.log('Oo row', rowYearData2);
|
||||||
|
|
||||||
|
const rowYear = {
|
||||||
|
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||||
|
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class MeetingData {
|
||||||
|
constructor(rootStore) {
|
||||||
|
this.rootStore = rootStore;
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchValues = {
|
||||||
|
DateType: { key: 'applyDate', value: 'applyDate', label: '提交日期' },
|
||||||
|
};
|
||||||
|
|
||||||
|
setSearchValues(body) {
|
||||||
|
this.searchValues = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHTableData = [];
|
||||||
|
GHTableLoading = false;
|
||||||
|
/**
|
||||||
|
* 获取市场订单数据 ---------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
dataGHOrder = async (param) => {
|
||||||
|
// console.log('dataGH', param);
|
||||||
|
this.GHTableLoading = true;
|
||||||
|
const defaultParam = { DateType: 'applyDate' };
|
||||||
|
// 本周
|
||||||
|
const CHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '1', 'WebCode': 'All' });
|
||||||
|
const exceptCHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '28,33', 'WebCode': 'All' });
|
||||||
|
|
||||||
|
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||||
|
/** 截至今年 - 行 */
|
||||||
|
const { ordercountTotal1: CHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||||
|
const { ordercount1: exceptCHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '28,33', 'WebCode': 'All', OrderType: 'Product' });
|
||||||
|
/** 截至今年 - 列 */
|
||||||
|
const { ordercount1: ColLineClassDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||||
|
const { ordercountTotal1: ColToBDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'GHTOBHW,GHTOBZG', OrderType: 'LineClass' });
|
||||||
|
const { ordercountTotal1: ColExternalDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'ZWQD', OrderType: 'LineClass' });
|
||||||
|
// 老客户
|
||||||
|
const yearDetail = await getDetailData({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All' });
|
||||||
|
const { isOld1: isOld1Year } = rowItem(yearDetail);
|
||||||
|
|
||||||
|
const colYearRow = {
|
||||||
|
LineClass_Origin: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||||
|
LineClass_PPC: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||||
|
toB: ColToBDataYear.OrderCount,
|
||||||
|
isOld1: isOld1Year,
|
||||||
|
external: ColExternalDataYear.OrderCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rows = [
|
||||||
|
{ key: 'ch', label: '中国', ...rowItem(CHData), rowYear: CHDataYear.OrderCount },
|
||||||
|
{ key: 'ja', label: '日本+', ...dataJA(exceptCHData, exceptCHDataYear) },
|
||||||
|
{ key: 'se', label: '东南亚+', ...dataSE(exceptCHData, exceptCHDataYear) },
|
||||||
|
{ key: 'in', label: '印度+', ...dataIN(exceptCHData, exceptCHDataYear) },
|
||||||
|
{ key: 'other', label: '其他GH', ...dataGHOther(exceptCHData, exceptCHDataYear) },
|
||||||
|
];
|
||||||
|
const columnsSum = ['LineClass_Origin', 'LineClass_PPC', 'toB', 'external', 'isOld1', 'total', 'rowYear'].reduce(
|
||||||
|
(r, col) => ({
|
||||||
|
...r,
|
||||||
|
[col]: rows.reduce((rr, row) => rr + row[col], 0),
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
rows.push({ key: 'columnSum', label: '合计', ...columnsSum });
|
||||||
|
rows.push({ key: 'colYearRow', label: '截至', ...colYearRow });
|
||||||
|
runInAction(() => {
|
||||||
|
this.GHTableData = rows;
|
||||||
|
this.GHTableLoading = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
GHSalesTableData = [];
|
||||||
|
GHSalesLoading = false;
|
||||||
|
/**
|
||||||
|
* 获取GH销售数据 ---------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
dataGHSales = async (param) => {
|
||||||
|
this.GHSalesLoading = true;
|
||||||
|
const salesParam = { ...param, DateType: 'confirmDate', 'WebCode': 'All', };
|
||||||
|
// console.log(param);
|
||||||
|
const { total1: CHSalesDataWeek } = await getDepartmentOrderMLByType({...salesParam, DepartmentList: '1', OrderType:'ALL'});
|
||||||
|
const exceptCHSalesDataWeek = await getDetailData({ ...salesParam, 'DepartmentList': '28,33' });
|
||||||
|
const GHDataWeekConfirm = exceptCHSalesDataWeek.filter((ele) => Number(ele.orderState) === 1); // 成交的
|
||||||
|
|
||||||
|
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||||
|
const yearEnd = moment().endOf("year").format(SMALL_DATETIME_FORMAT);
|
||||||
|
/** 截至今年 - 成交 */
|
||||||
|
const { total1: CHDataYear } = await getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '1', OrderType:'ALL' });
|
||||||
|
const GHDataYear = await getDetailData({ ...salesParam, Date1: yearStart, 'DepartmentList': '28,33' });
|
||||||
|
const GHDataYearConfirm = GHDataYear.filter((ele) => Number(ele.orderState) === 1); // 成交的
|
||||||
|
/** 截至今年 - 走团 */
|
||||||
|
const { total1: CHStartDataYear } = await getDepartmentOrderMLByType({...salesParam, Date1: yearStart,Date2:yearEnd,DepartmentList: '1', OrderType:'ALL',DateType: 'startDate' });
|
||||||
|
const GHStartDataYear0 = await getDetailData({ ...salesParam, DateType: 'startDate', 'DepartmentList': '28,33', Date1: yearStart,Date2:yearEnd, });
|
||||||
|
const GHStartDataYear = GHStartDataYear0.filter((ele) => Number(ele.orderState) === 1); // 成交的
|
||||||
|
|
||||||
|
const rows = [
|
||||||
|
{
|
||||||
|
key: 'ch',
|
||||||
|
label: '中国',
|
||||||
|
YJLY: price_to_number(CHSalesDataWeek.COLI_ML2),
|
||||||
|
CJCount: (CHSalesDataWeek.COLI_CJCount),
|
||||||
|
rowYear: { YJLY: price_to_number(CHDataYear.COLI_ML2), CJCount: CHDataYear.COLI_CJCount, YJLY2: price_to_number(CHStartDataYear.COLI_ML2) },
|
||||||
|
},
|
||||||
|
{ key: 'ja', label: '日本+', ...dataSales('ja', GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||||
|
{ key: 'se', label: '东南亚+', ...dataSales('se', GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||||
|
{ key: 'in', label: '印度+', ...dataSales('in', GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||||
|
{ key: 'other', label: '其他GH', ...dataSalesGHOther(GHDataWeekConfirm, GHDataYearConfirm, GHStartDataYear) },
|
||||||
|
];
|
||||||
|
const columnsSum = ['CJCount', 'YJLY'].reduce((r, col) => ({ ...r, [col]: rows.reduce((rr, row) => rr + row[col], 0) }), {});
|
||||||
|
const allYearData = rows.map(row => row.rowYear);
|
||||||
|
const rowYear = ['CJCount', 'YJLY', 'CJCount2', 'YJLY2', ].reduce((r, col) => ({ ...r, [col]: allYearData.reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||||
|
rows.push({ key: 'columnSum', label: '合计', ...columnsSum, rowYear });
|
||||||
|
// console.log(rows, allYearData, rowYear);
|
||||||
|
runInAction(() => {
|
||||||
|
this.GHSalesTableData = rows;
|
||||||
|
this.GHSalesLoading = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GHServiceTableData = [];
|
||||||
|
GHServiceLoading = false;
|
||||||
|
/**
|
||||||
|
* 获取GH服务数据 ---------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
dataGHService = async (param) => {
|
||||||
|
this.GHServiceLoading = true;
|
||||||
|
const serviceParam = { ...param, DateType: 'startDate', 'WebCode': 'All' };
|
||||||
|
// 走团数
|
||||||
|
const { ordercountTotal1: { OrderCount: GroupCount } } = await getOrderCountByType({ ...serviceParam, 'DepartmentList': '1', OrderType: 'Form' });
|
||||||
|
const exceptCHDataWeek = await getDetailData({ ...serviceParam, 'DepartmentList': '28,33' });
|
||||||
|
// 走团数 - 年
|
||||||
|
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||||
|
const { ordercountTotal1: { OrderCount: GroupCountYear } } = await getOrderCountByType({ ...serviceParam, 'DepartmentList': '1', OrderType: 'Form', Date1: yearStart, });
|
||||||
|
const exceptCHDataYear = await getDetailData({ ...serviceParam, Date1: yearStart, 'DepartmentList': '28,33' });
|
||||||
|
// 好评数
|
||||||
|
const { total1: { GoodCount } } = await getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '1', });
|
||||||
|
const { total1: { GoodCount: GoodCountYear } } = await getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '1', });
|
||||||
|
const { total1: { GoodCount: GHGoodCountWeek } } = await getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '28,33', });
|
||||||
|
const { total1: { GoodCount: GHGoodCountYear } } = await getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '28,33', });
|
||||||
|
|
||||||
|
const rows = [
|
||||||
|
{ key: 'ch', label: '中国', ...{GoodCount, GroupCount}, rowYear: { GroupCount: GroupCountYear, GoodCount: GoodCountYear } },
|
||||||
|
{ key: 'ja', label: '日本+', ...dataSales('ja', exceptCHDataWeek, exceptCHDataYear, []) },
|
||||||
|
{ key: 'se', label: '东南亚+', ...dataSales('se', exceptCHDataWeek, exceptCHDataYear, []) },
|
||||||
|
{ key: 'in', label: '印度+', ...dataSales('in', exceptCHDataWeek, exceptCHDataYear, []) },
|
||||||
|
{ key: 'other', label: '其他GH', ...dataSalesGHOther(exceptCHDataWeek, exceptCHDataYear, []) },
|
||||||
|
];
|
||||||
|
const GHRowWeek = { GoodCount: GHGoodCountWeek, GroupCount: 0 };
|
||||||
|
const columnsSum = ['GoodCount', 'GroupCount'].reduce((r, col) => ({ ...r, [col]: [...rows, GHRowWeek].reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||||
|
const allYearData = rows.map((row) => row.rowYear).concat([{ GoodCount: GHGoodCountYear, GroupCount: 0 }]);
|
||||||
|
// console.log(allYearData);
|
||||||
|
const rowYear = ['GoodCount', 'GroupCount', ].reduce((r, col) => ({ ...r, [col]: allYearData.reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||||
|
rows.push({ key: 'columnSum', label: '合计', ...columnsSum, rowYear });
|
||||||
|
// console.log(rows);
|
||||||
|
runInAction(() => {
|
||||||
|
this.GHServiceTableData = rows;
|
||||||
|
this.GHServiceLoading = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default MeetingData;
|
@ -0,0 +1,479 @@
|
|||||||
|
import { makeAutoObservable, runInAction } from 'mobx';
|
||||||
|
import { fetchJSON } from '../utils/request';
|
||||||
|
import { objectMapper, pick, price_to_number, } from '../utils/commons';
|
||||||
|
import { pivotBy } from './../libs/ht';
|
||||||
|
import moment from "moment";
|
||||||
|
import { DATE_FORMAT, DATETIME_FORMAT, SMALL_DATETIME_FORMAT } from '../config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于透视的数据
|
||||||
|
*/
|
||||||
|
const getDetailData = async (param) => {
|
||||||
|
const json = await fetchJSON('/service-Analyse2/GetTradeApartDetail', param);
|
||||||
|
return json.errcode === 0 ? json.result : [];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const getOrderCountByType = async (param) => {
|
||||||
|
const paramBody = objectMapper(param, {
|
||||||
|
WebCode: 'WebCode',
|
||||||
|
OrderType: 'OrderType',
|
||||||
|
IncludeTickets: 'IncludeTickets',
|
||||||
|
DateType: 'DateType',
|
||||||
|
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||||
|
Date1: 'COLI_ApplyDate1',
|
||||||
|
Date2: 'COLI_ApplyDate2',
|
||||||
|
});
|
||||||
|
const url = '/service-web/QueryData/GetOrderCountByType';
|
||||||
|
const json = await fetchJSON(url, paramBody);
|
||||||
|
return json.errcode === 0 ? json : {};
|
||||||
|
};
|
||||||
|
const getAgentGroupInfoALL = async (param) => {
|
||||||
|
const paramBody = objectMapper(param, {
|
||||||
|
DateType: 'DateType',
|
||||||
|
DepartmentList: 'DepList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||||
|
Date1: 'OldDate1',
|
||||||
|
Date2: 'OldDate2',
|
||||||
|
});
|
||||||
|
const url = '/service-web/QueryData/GetAgentGroupInfoALL';
|
||||||
|
const json = await fetchJSON(url, paramBody);
|
||||||
|
return json.errcode === 0 ? json : {};
|
||||||
|
};
|
||||||
|
const getDepartmentOrderMLByType = async (param) => {
|
||||||
|
const paramBody = objectMapper(param, {
|
||||||
|
DateType: 'DateType',
|
||||||
|
DepartmentList: 'DepartmentList', // { key: 'DepartmentList', transform: (v) => v.join(',') },
|
||||||
|
OrderType: 'OrderType', // 总览, 产品类型
|
||||||
|
});
|
||||||
|
const url = '/service-web/QueryData/GetDepartmentOrderMLByType';
|
||||||
|
const json = await fetchJSON(url, paramBody);
|
||||||
|
const { result1 } = json.errcode === 0 ? json : { result1: [] };
|
||||||
|
const total1 = ['COLI_CJCount', 'COLI_ML2',].reduce(
|
||||||
|
(r, col) => ({
|
||||||
|
...r,
|
||||||
|
[col]: result1.reduce((rr, row) => rr + row[col], 0),
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
return { total1, result1 };
|
||||||
|
};
|
||||||
|
const GHproductTypeListSetting = {
|
||||||
|
ja: ['日本', '东亚跨国'],
|
||||||
|
se: ['东南亚跨国', '泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝'],
|
||||||
|
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||||
|
};
|
||||||
|
const GHCountryListSetting = {
|
||||||
|
ja: ['日本', ],
|
||||||
|
se: ['泰国', '越南', '印度尼西亚', '水灯节', '柬埔寨', '老挝', '新加坡', '马来西亚', '菲律宾'],
|
||||||
|
in: ['印度', '印度次大陆跨国', '尼泊尔', '不丹', '斯里兰卡'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowItem = (filterData) => {
|
||||||
|
const { data: dataByLineClass, summaryMix: summaryByLineClass } = pivotBy(filterData, [['COLI_LineClass'], [], []]);
|
||||||
|
const LineClass_Origin = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
const LineClass_PPC = dataByLineClass.filter((ele) => ele.COLI_LineClass.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
|
||||||
|
const { data: dataByWebCode, summaryMix: summaryByWebCode } = pivotBy(filterData, [['WebCode'], [], []]);
|
||||||
|
const toB = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
const external = dataByWebCode.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf("站外渠道") !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
|
||||||
|
const filterIsOldC = filterData.filter((ele) => ele.WebCode.toLocaleLowerCase().indexOf('to b') === -1);
|
||||||
|
const { data: dataByIsOld, summaryMix: summaryByIsOld } = pivotBy(filterIsOldC, [['IsOld', 'isCusCommend'], [], []]);
|
||||||
|
const isOld1 = dataByIsOld.filter((ele) => ele.rowLabel.indexOf('1') !== -1).reduce((r, c) => r + c.SumOrder, 0);
|
||||||
|
|
||||||
|
const total = LineClass_Origin + LineClass_PPC + toB + isOld1 + external;
|
||||||
|
return { LineClass_Origin, LineClass_PPC, toB, external, isOld1, total };
|
||||||
|
};
|
||||||
|
// 日本+: 日本+东亚跨国
|
||||||
|
const dataJA = (rawData, yearData) => {
|
||||||
|
const productTypeList = GHproductTypeListSetting.ja;
|
||||||
|
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 东南亚+: 东南亚跨国+泰国+越南+印尼+水灯节线路
|
||||||
|
const dataSE = (rawData, yearData) => {
|
||||||
|
const productTypeList = GHproductTypeListSetting.se;
|
||||||
|
const filterData = rawData.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const filterDataYear = yearData.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 印度+: 印度+次大陆跨国+尼泊尔+不丹+斯里兰卡
|
||||||
|
const dataIN = (rawData, yearData) => {
|
||||||
|
const productTypeList = GHproductTypeListSetting.in;
|
||||||
|
const exceptProduct = ['印度尼西亚'];
|
||||||
|
const filterData = rawData
|
||||||
|
.filter((ele) => productTypeList.some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||||
|
.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const filterDataYear = yearData
|
||||||
|
.filter((ele) => productTypeList.some((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) !== -1))
|
||||||
|
.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 其他GH
|
||||||
|
const dataGHOther = (rawData, yearData) => {
|
||||||
|
const exceptProduct = Object.values(GHproductTypeListSetting).reduce((r, c) => r.concat(c), []);
|
||||||
|
const filterData = rawData.filter((ele) => exceptProduct.every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const filterDataYear = yearData.filter((ele) => exceptProduct.every((item) => ele.OrderType.toLocaleLowerCase().indexOf(item) === -1));
|
||||||
|
const rowYear = filterDataYear.reduce((r, c) => r + c.OrderCount, 0);
|
||||||
|
return { ...rowItem(filterData), rowYear };
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataSales = (tKey, rawData, yearData, yearData2) => {
|
||||||
|
const targetList = GHCountryListSetting[tKey];
|
||||||
|
const tIndex = Object.keys(GHCountryListSetting).indexOf(tKey);
|
||||||
|
const exceptTargetList = Object.keys(GHCountryListSetting).reduce((r, c, i) => r.concat(i < tIndex ? GHCountryListSetting[c] : []), []);
|
||||||
|
// console.log(tIndex, tKey, 'exceptTargetList', exceptTargetList, 'targetList', targetList);
|
||||||
|
|
||||||
|
const filterRaw1 = rawData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
// console.log(tKey, 'filterRaw1', filterRaw1);
|
||||||
|
const filterDataC = filterRaw1.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataT = tKey === 'se' ? filterRaw1.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||||
|
const filterData = filterDataC.concat(filterDataT);
|
||||||
|
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||||
|
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||||
|
|
||||||
|
const filterRaw2 = yearData.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYearC = filterRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYearT = tKey === 'se' ? filterRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||||
|
const filterDataYear = filterDataYearC.concat(filterDataYearT);
|
||||||
|
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
// console.log(tKey, filterDataYear.map(ee => ee.destinationCountry_AsJOSN), filterDataYear.map(ee => ee.productType), filterDataYear);
|
||||||
|
|
||||||
|
const filterDataYearRaw2 = yearData2.filter((ele) => exceptTargetList.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYear2C = filterDataYearRaw2.filter((ele) => targetList.some((item) => ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const filterDataYear2T = tKey === 'se' ? filterDataYearRaw2.filter((ele) => ['泰国水灯节'].some((item) => ele.productType.toLocaleLowerCase().indexOf(item) !== -1)) : [];
|
||||||
|
const filterDataYear2 = filterDataYear2C.concat(filterDataYear2T);
|
||||||
|
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
const rowYear = {
|
||||||
|
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||||
|
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cols = ['YJLY', 'CJCount'].reduce((r, key) => ({ ...r, [key]: filterData.reduce((a, c) => a + price_to_number(c[key]), 0) }), {});
|
||||||
|
// console.log(tKey, filterData, filterDataYear, filterDataYear2);
|
||||||
|
return { ...cols, GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||||
|
};
|
||||||
|
const dataSalesGHOther = (rawData, yearData, yearData2) => {
|
||||||
|
const exceptContry = Object.values(GHCountryListSetting).reduce((r, c) => r.concat(c), []);
|
||||||
|
// console.log('exceptContry', exceptContry);
|
||||||
|
// console.log('OOoo rawData', rawData.map(e => e.destinationCountry_AsJOSN));
|
||||||
|
const filterData = rawData
|
||||||
|
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||||
|
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
// console.log('OOoo', filterData.map(e => e.destinationCountry_AsJOSN), filterData.map(e => e.productType));
|
||||||
|
const CJCount = filterData.length; // filterData.reduce((r, c) => r + c.CJCount, 0);
|
||||||
|
const YJLY = filterData.reduce((r, c) => r + price_to_number(c.ML), 0);
|
||||||
|
|
||||||
|
const filterDataYear = yearData
|
||||||
|
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||||
|
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const rowYearData = { CJCount: filterDataYear.length, YJLY: filterDataYear.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
// console.log('OOoo year', filterDataYear.map(e => e.destinationCountry_AsJOSN), filterDataYear.map(e => e.productType));
|
||||||
|
|
||||||
|
const filterDataYear2 = yearData2
|
||||||
|
.filter((ele) => ['泰国水灯节'].every((item) => ele.productType.toLocaleLowerCase().indexOf(item) === -1))
|
||||||
|
.filter((ele) => exceptContry.every((item) => !ele.destinationCountry_AsJOSN.includes(item)));
|
||||||
|
const rowYearData2 = { CJCount: filterDataYear2.length, YJLY: filterDataYear2.reduce((r, c) => r + price_to_number(c.ML), 0) };
|
||||||
|
// console.log('Oo', filterDataYear2.map(e => e.destinationCountry_AsJOSN), filterDataYear2);
|
||||||
|
// console.log('Oo row', rowYearData2);
|
||||||
|
|
||||||
|
const rowYear = {
|
||||||
|
YJLY: price_to_number(rowYearData.YJLY), CJCount: rowYearData.CJCount, GroupCount: rowYearData.CJCount,
|
||||||
|
YJLY2: price_to_number(rowYearData2.YJLY), CJCount2: rowYearData2.CJCount, GroupCount2: rowYearData2.CJCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { GroupCount:CJCount, CJCount, YJLY, rowYear, rawData: filterData, rawYearData: filterDataYear, rawYearData2: filterDataYear2 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class MeetingData {
|
||||||
|
constructor(rootStore) {
|
||||||
|
this.rootStore = rootStore;
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchValues = {
|
||||||
|
DateType: { key: 'applyDate', value: 'applyDate', label: '提交日期' },
|
||||||
|
};
|
||||||
|
|
||||||
|
setSearchValues(body) {
|
||||||
|
this.searchValues = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHTableData = [];
|
||||||
|
GHTableLoading = false;
|
||||||
|
/**
|
||||||
|
* 获取市场订单数据 ---------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
dataGHOrder = async (param) => {
|
||||||
|
// console.log('dataGH', param);
|
||||||
|
this.GHTableLoading = true;
|
||||||
|
const defaultParam = { DateType: 'applyDate' };
|
||||||
|
// 本周
|
||||||
|
const CHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '1', 'WebCode': 'All' });
|
||||||
|
const exceptCHData = await getDetailData({ ...param, ...defaultParam, 'DepartmentList': '28,33', 'WebCode': 'All' });
|
||||||
|
|
||||||
|
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||||
|
/** 截至今年 - 行 */
|
||||||
|
const [
|
||||||
|
{ ordercountTotal1: CHDataYear },
|
||||||
|
{ ordercount1: exceptCHDataYear },
|
||||||
|
] = await Promise.all([
|
||||||
|
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1', 'WebCode': 'All', OrderType: 'LineClass' }),
|
||||||
|
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '28,33', 'WebCode': 'All', OrderType: 'Product' }),
|
||||||
|
]);
|
||||||
|
// const { ordercountTotal1: CHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||||
|
// const { ordercount1: exceptCHDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '28,33', 'WebCode': 'All', OrderType: 'Product' });
|
||||||
|
/** 截至今年 - 列 */
|
||||||
|
const [
|
||||||
|
{ ordercount1: ColLineClassDataYear },
|
||||||
|
{ ordercountTotal1: ColToBDataYear },
|
||||||
|
{ ordercountTotal1: ColExternalDataYear },
|
||||||
|
] = await Promise.all([
|
||||||
|
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All', OrderType: 'LineClass' }),
|
||||||
|
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'GHTOBHW,GHTOBZG', OrderType: 'LineClass'}),
|
||||||
|
getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'ZWQD', OrderType: 'LineClass'}),
|
||||||
|
]);
|
||||||
|
// const { ordercount1: ColLineClassDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All', OrderType: 'LineClass' });
|
||||||
|
// const { ordercountTotal1: ColToBDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'GHTOBHW,GHTOBZG', OrderType: 'LineClass' });
|
||||||
|
// const { ordercountTotal1: ColExternalDataYear } = await getOrderCountByType({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'ZWQD', OrderType: 'LineClass' });
|
||||||
|
// 老客户
|
||||||
|
const yearDetail = await getDetailData({ ...param, ...defaultParam, Date1: yearStart, 'DepartmentList': '1,2,28,7,33', 'WebCode': 'All' });
|
||||||
|
const { isOld1: isOld1Year } = rowItem(yearDetail);
|
||||||
|
|
||||||
|
const colYearRow = {
|
||||||
|
LineClass_Origin: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('网前自然订单') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||||
|
LineClass_PPC: ColLineClassDataYear.filter((ele) => ele.OrderType.toLocaleLowerCase().indexOf('ppc') !== -1).reduce((r, c) => r + c.OrderCount, 0),
|
||||||
|
toB: ColToBDataYear.OrderCount,
|
||||||
|
isOld1: isOld1Year,
|
||||||
|
external: ColExternalDataYear.OrderCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rows = [
|
||||||
|
{ key: 'ch', label: '中国', ...rowItem(CHData), rowYear: CHDataYear.OrderCount },
|
||||||
|
{ key: 'ja', label: '日本+', ...dataJA(exceptCHData, exceptCHDataYear) },
|
||||||
|
{ key: 'se', label: '东南亚+', ...dataSE(exceptCHData, exceptCHDataYear) },
|
||||||
|
{ key: 'in', label: '印度+', ...dataIN(exceptCHData, exceptCHDataYear) },
|
||||||
|
{ key: 'other', label: '其他GH', ...dataGHOther(exceptCHData, exceptCHDataYear) },
|
||||||
|
];
|
||||||
|
const columnsSum = ['LineClass_Origin', 'LineClass_PPC', 'toB', 'external', 'isOld1', 'total', 'rowYear'].reduce(
|
||||||
|
(r, col) => ({
|
||||||
|
...r,
|
||||||
|
[col]: rows.reduce((rr, row) => rr + row[col], 0),
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
rows.push({ key: 'columnSum', label: '合计', ...columnsSum });
|
||||||
|
rows.push({ key: 'colYearRow', label: '截至', ...colYearRow });
|
||||||
|
runInAction(() => {
|
||||||
|
this.GHTableData = rows;
|
||||||
|
this.GHTableLoading = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
GHSalesTableData = [];
|
||||||
|
GHSalesLoading = false;
|
||||||
|
/**
|
||||||
|
* 获取GH销售数据 ---------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
dataGHSales = async (param) => {
|
||||||
|
this.GHSalesLoading = true;
|
||||||
|
const salesParam = { ...param, DateType: 'confirmDate', WebCode: 'CHT,AH,GH,GHKYZG,GHKYHW,ZWQD', OrderType:'ALL', }; // WebCode: 不含分销
|
||||||
|
const partnerParam = { WebCode: 'GHTOBHW,GHTOBZG' };
|
||||||
|
const [
|
||||||
|
{ total1: CHSalesDataTotal },
|
||||||
|
{ total1: CHPartnerSalesData },
|
||||||
|
{ total1: AHSalesDataTotal },
|
||||||
|
{ total1: AHpartnerSalesData },
|
||||||
|
{ total1: GHSalesDataTotal },
|
||||||
|
{ total1: GHpartnerSalesData },
|
||||||
|
] = await Promise.all([
|
||||||
|
getDepartmentOrderMLByType({...salesParam, DepartmentList: '1,2', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, DepartmentList: '1,2', ...partnerParam }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, DepartmentList: '28', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, DepartmentList: '28', ...partnerParam }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, DepartmentList: '33', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, DepartmentList: '33', ...partnerParam }),
|
||||||
|
]);
|
||||||
|
// 不含分销 = 总额 - 分销
|
||||||
|
const CHSalesData = {'COLI_CJCount': CHSalesDataTotal.COLI_CJCount-CHPartnerSalesData.COLI_CJCount, 'COLI_ML2': CHSalesDataTotal.COLI_ML2-CHPartnerSalesData.COLI_ML2};
|
||||||
|
const AHSalesData = {'COLI_CJCount': AHSalesDataTotal.COLI_CJCount-AHpartnerSalesData.COLI_CJCount, 'COLI_ML2': AHSalesDataTotal.COLI_ML2-AHpartnerSalesData.COLI_ML2};
|
||||||
|
const GHSalesData = {'COLI_CJCount': GHSalesDataTotal.COLI_CJCount-GHpartnerSalesData.COLI_CJCount, 'COLI_ML2': GHSalesDataTotal.COLI_ML2-GHpartnerSalesData.COLI_ML2};
|
||||||
|
const partnerSalesData = {'COLI_CJCount': CHPartnerSalesData.COLI_CJCount+AHpartnerSalesData.COLI_CJCount, 'COLI_ML2': CHPartnerSalesData.COLI_ML2+AHpartnerSalesData.COLI_ML2};
|
||||||
|
const totalSalesData = {
|
||||||
|
'COLI_CJCount': CHSalesDataTotal.COLI_CJCount + AHSalesDataTotal.COLI_CJCount + GHSalesDataTotal.COLI_CJCount,
|
||||||
|
'COLI_ML2': CHSalesDataTotal.COLI_ML2 + AHSalesDataTotal.COLI_ML2 + GHSalesDataTotal.COLI_ML2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||||
|
const yearEnd = moment().endOf("year").format(SMALL_DATETIME_FORMAT);
|
||||||
|
/** 截至今年 - 成交 */
|
||||||
|
const [
|
||||||
|
{ total1: CHSalesYearTotal },
|
||||||
|
{ total1: CHPartnerSalesYear },
|
||||||
|
{ total1: AHSalesYearTotal },
|
||||||
|
{ total1: AHpartnerSalesYear },
|
||||||
|
{ total1: GHSalesYearTotal },
|
||||||
|
{ total1: GHpartnerSalesYear },
|
||||||
|
] = await Promise.all([
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '1,2', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '1,2', ...partnerParam }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '28', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '28', ...partnerParam }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '33', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, DepartmentList: '33', ...partnerParam }),
|
||||||
|
]);
|
||||||
|
const CHDataYear = {'COLI_CJCount': CHSalesYearTotal.COLI_CJCount-CHPartnerSalesYear.COLI_CJCount, 'COLI_ML2': CHSalesYearTotal.COLI_ML2-CHPartnerSalesYear.COLI_ML2};
|
||||||
|
const AHDataYear = {'COLI_CJCount': AHSalesYearTotal.COLI_CJCount-AHpartnerSalesYear.COLI_CJCount, 'COLI_ML2': AHSalesYearTotal.COLI_ML2-AHpartnerSalesYear.COLI_ML2};
|
||||||
|
const GHDataYear = {'COLI_CJCount': GHSalesYearTotal.COLI_CJCount-GHpartnerSalesYear.COLI_CJCount, 'COLI_ML2': GHSalesYearTotal.COLI_ML2-GHpartnerSalesYear.COLI_ML2};
|
||||||
|
const partnerDataYear = {'COLI_CJCount': CHPartnerSalesYear.COLI_CJCount+AHpartnerSalesYear.COLI_CJCount, 'COLI_ML2': CHPartnerSalesYear.COLI_ML2+AHpartnerSalesYear.COLI_ML2};
|
||||||
|
const totalDataYear = {
|
||||||
|
'COLI_CJCount': CHSalesYearTotal.COLI_CJCount + AHSalesYearTotal.COLI_CJCount + GHSalesYearTotal.COLI_CJCount,
|
||||||
|
'COLI_ML2': CHSalesYearTotal.COLI_ML2 + AHSalesYearTotal.COLI_ML2 + GHSalesYearTotal.COLI_ML2,
|
||||||
|
};
|
||||||
|
/** 截至今年 - 走团 */
|
||||||
|
const [
|
||||||
|
{ total1: CHStartDataYearTotal },
|
||||||
|
{ total1: CHPartnerStartDataYear },
|
||||||
|
{ total1: AHStartDataYearTotal },
|
||||||
|
{ total1: AHpartnerStartDataYear },
|
||||||
|
{ total1: GHStartDataYearTotal },
|
||||||
|
{ total1: GHpartnerStartDataYear },
|
||||||
|
] = await Promise.all([
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '1,2', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '1,2', ...partnerParam }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '28', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '28', ...partnerParam }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '33', WebCode: 'ALL', }),
|
||||||
|
getDepartmentOrderMLByType({...salesParam, Date1: yearStart, Date2:yearEnd, DateType: 'startDate', DepartmentList: '33', ...partnerParam }),
|
||||||
|
]);
|
||||||
|
const CHStartDataYear = {'COLI_CJCount': CHStartDataYearTotal.COLI_CJCount-CHPartnerStartDataYear.COLI_CJCount, 'COLI_ML2': CHStartDataYearTotal.COLI_ML2-CHPartnerStartDataYear.COLI_ML2};
|
||||||
|
const AHStartDataYear = {'COLI_CJCount': AHStartDataYearTotal.COLI_CJCount-AHpartnerStartDataYear.COLI_CJCount, 'COLI_ML2': AHStartDataYearTotal.COLI_ML2-AHpartnerStartDataYear.COLI_ML2};
|
||||||
|
const GHStartDataYear = {'COLI_CJCount': GHStartDataYearTotal.COLI_CJCount-GHpartnerStartDataYear.COLI_CJCount, 'COLI_ML2': GHStartDataYearTotal.COLI_ML2-GHpartnerStartDataYear.COLI_ML2};
|
||||||
|
const partnerStartDataYear = {'COLI_CJCount': CHPartnerStartDataYear.COLI_CJCount+AHpartnerStartDataYear.COLI_CJCount, 'COLI_ML2': CHPartnerStartDataYear.COLI_ML2+AHpartnerStartDataYear.COLI_ML2};
|
||||||
|
const totalStartDataYear = {
|
||||||
|
'COLI_CJCount': CHStartDataYearTotal.COLI_CJCount + AHStartDataYearTotal.COLI_CJCount + GHStartDataYearTotal.COLI_CJCount,
|
||||||
|
'COLI_ML2': CHStartDataYearTotal.COLI_ML2 + AHStartDataYearTotal.COLI_ML2 + GHStartDataYearTotal.COLI_ML2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rows = [
|
||||||
|
{
|
||||||
|
key: 'ch',
|
||||||
|
label: 'CH(不含分销)',
|
||||||
|
YJLY: price_to_number(CHSalesData.COLI_ML2),
|
||||||
|
CJCount: (CHSalesData.COLI_CJCount),
|
||||||
|
rowYear: { YJLY: price_to_number(CHDataYear.COLI_ML2), CJCount: CHDataYear.COLI_CJCount, YJLY2: price_to_number(CHStartDataYear.COLI_ML2) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ah',
|
||||||
|
label: 'AH(不含分销)',
|
||||||
|
YJLY: price_to_number(AHSalesData.COLI_ML2),
|
||||||
|
CJCount: (AHSalesData.COLI_CJCount),
|
||||||
|
rowYear: { YJLY: price_to_number(AHDataYear.COLI_ML2), CJCount: AHDataYear.COLI_CJCount, YJLY2: price_to_number(AHStartDataYear.COLI_ML2) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'partner',
|
||||||
|
label: '分销',
|
||||||
|
YJLY: price_to_number(partnerSalesData.COLI_ML2),
|
||||||
|
CJCount: (partnerSalesData.COLI_CJCount),
|
||||||
|
rowYear: { YJLY: price_to_number(partnerDataYear.COLI_ML2), CJCount: partnerDataYear.COLI_CJCount, YJLY2: price_to_number(partnerStartDataYear.COLI_ML2) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'gh',
|
||||||
|
label: 'GH(不含分销)',
|
||||||
|
YJLY: price_to_number(GHSalesData.COLI_ML2),
|
||||||
|
CJCount: (GHSalesData.COLI_CJCount),
|
||||||
|
rowYear: { YJLY: price_to_number(GHDataYear.COLI_ML2), CJCount: GHDataYear.COLI_CJCount, YJLY2: price_to_number(GHStartDataYear.COLI_ML2) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'total',
|
||||||
|
label: '合计',
|
||||||
|
YJLY: price_to_number(totalSalesData.COLI_ML2),
|
||||||
|
CJCount: (totalSalesData.COLI_CJCount),
|
||||||
|
rowYear: { YJLY: price_to_number(totalDataYear.COLI_ML2), CJCount: totalDataYear.COLI_CJCount, YJLY2: price_to_number(totalStartDataYear.COLI_ML2) },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
runInAction(() => {
|
||||||
|
this.GHSalesTableData = rows;
|
||||||
|
this.GHSalesLoading = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GHServiceTableData = [];
|
||||||
|
GHServiceLoading = false;
|
||||||
|
/**
|
||||||
|
* 获取GH服务数据 ---------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
dataGHService = async (param) => {
|
||||||
|
this.GHServiceLoading = true;
|
||||||
|
const serviceParam = { ...param, DateType: 'startDate', 'WebCode': 'All' };
|
||||||
|
// 走团数
|
||||||
|
const [
|
||||||
|
{ ordercountTotal1: { OrderCount: CHGroupCount } },
|
||||||
|
{ ordercountTotal1: { OrderCount: AHGroupCount } },
|
||||||
|
{ ordercountTotal1: { OrderCount: GHGroupCount } },
|
||||||
|
] = await Promise.all([
|
||||||
|
getOrderCountByType({ ...serviceParam, 'DepartmentList': '1,2', OrderType: 'Form'}),
|
||||||
|
getOrderCountByType({ ...serviceParam, 'DepartmentList': '28', OrderType: 'Form'}),
|
||||||
|
getOrderCountByType({ ...serviceParam, 'DepartmentList': '33', OrderType: 'Form'}),
|
||||||
|
]);
|
||||||
|
// 走团数 - 年
|
||||||
|
// * 走团: 整团数, 而非各地接社/目的地的总和
|
||||||
|
const yearStart = moment().startOf("year").format(DATE_FORMAT);
|
||||||
|
const [
|
||||||
|
{ ordercountTotal1: { OrderCount: CHGroupCountYear } },
|
||||||
|
{ ordercountTotal1: { OrderCount: AHGroupCountYear } },
|
||||||
|
{ ordercountTotal1: { OrderCount: GHGroupCountYear } },
|
||||||
|
] = await Promise.all([
|
||||||
|
getOrderCountByType({ ...serviceParam, 'DepartmentList': '1,2', OrderType: 'Form', Date1: yearStart, }),
|
||||||
|
getOrderCountByType({ ...serviceParam, 'DepartmentList': '28', OrderType: 'Form', Date1: yearStart, }),
|
||||||
|
getOrderCountByType({ ...serviceParam, 'DepartmentList': '33', OrderType: 'Form', Date1: yearStart, }),
|
||||||
|
]);
|
||||||
|
// 好评数
|
||||||
|
const [
|
||||||
|
{ total1: { GoodCount: CHGoodCount } },
|
||||||
|
{ total1: { GoodCount: AHGoodCount } },
|
||||||
|
{ total1: { GoodCount: GHGoodCount } },
|
||||||
|
] = await Promise.all([
|
||||||
|
getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '1,2', }),
|
||||||
|
getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '28', }),
|
||||||
|
getAgentGroupInfoALL({ ...serviceParam, 'DepartmentList': '33', }),
|
||||||
|
]);
|
||||||
|
// 好评数 - 年
|
||||||
|
const [
|
||||||
|
{ total1: { GoodCount: CHGoodCountYear, } },
|
||||||
|
{ total1: { GoodCount: AHGoodCountYear, } },
|
||||||
|
{ total1: { GoodCount: GHGoodCountYear, } },
|
||||||
|
] = await Promise.all([
|
||||||
|
getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '1,2', }),
|
||||||
|
getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '28', }),
|
||||||
|
getAgentGroupInfoALL({ ...serviceParam, Date1: yearStart, 'DepartmentList': '33', }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rows = [
|
||||||
|
{ key: 'ch', label: 'CH', GoodCount: CHGoodCount, GroupCount: CHGroupCount, rowYear: { GroupCount: CHGroupCountYear, GoodCount: CHGoodCountYear } },
|
||||||
|
{ key: 'ah', label: 'AH', GoodCount: AHGoodCount, GroupCount: AHGroupCount, rowYear: { GroupCount: AHGroupCountYear, GoodCount: AHGoodCountYear } },
|
||||||
|
{ key: 'Gh', label: 'GH', GoodCount: GHGoodCount, GroupCount: GHGroupCount, rowYear: { GroupCount: GHGroupCountYear, GoodCount: GHGoodCountYear } },
|
||||||
|
];
|
||||||
|
// const GHRowWeek = { GoodCount: GHGoodCountWeek, GroupCount: 0 };
|
||||||
|
// const columnsSum = ['GoodCount', 'GroupCount'].reduce((r, col) => ({ ...r, [col]: [...rows, GHRowWeek].reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||||
|
// const allYearData = rows.map((row) => row.rowYear).concat([{ GoodCount: GHGoodCountYear, GroupCount: 0 }]);
|
||||||
|
// // console.log(allYearData);
|
||||||
|
// const rowYear = ['GoodCount', 'GroupCount', ].reduce((r, col) => ({ ...r, [col]: allYearData.reduce((rr, row) => rr + (row[col] || 0), 0) }), {});
|
||||||
|
// rows.push({ key: 'columnSum', label: '合计', ...columnsSum, rowYear });
|
||||||
|
// console.log(rows);
|
||||||
|
runInAction(() => {
|
||||||
|
this.GHServiceTableData = rows;
|
||||||
|
this.GHServiceLoading = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default MeetingData;
|
@ -0,0 +1,210 @@
|
|||||||
|
import { makeAutoObservable, runInAction } from 'mobx';
|
||||||
|
import { fetchJSON } from '../utils/request';
|
||||||
|
import { isEmpty, sortDescBy, groupBy, pick, unique } from '../utils/commons';
|
||||||
|
import { groupsMappedByCode } from './../libs/ht';
|
||||||
|
import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from './../config';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const fetchResultsData = async (param) => {
|
||||||
|
const defaultParam = {
|
||||||
|
WebCode: 'All',
|
||||||
|
DepartmentList: '',
|
||||||
|
opisn: -1,
|
||||||
|
Date1: '',
|
||||||
|
Date2: '',
|
||||||
|
groupType: '',
|
||||||
|
groupDateType: '',
|
||||||
|
};
|
||||||
|
const json = await fetchJSON('/service-Analyse2/sales_crm_results', { ...defaultParam, ...param });
|
||||||
|
return json.errcode === 0 ? json.result : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchProcessData = async (param) => {
|
||||||
|
const defaultParam = {
|
||||||
|
WebCode: 'All',
|
||||||
|
DepartmentList: '',
|
||||||
|
opisn: -1,
|
||||||
|
Date1: '',
|
||||||
|
Date2: '',
|
||||||
|
groupType: '',
|
||||||
|
groupDateType: '',
|
||||||
|
};
|
||||||
|
const json = await fetchJSON('/service-Analyse2/sales_crm_process', { ...defaultParam, ...param });
|
||||||
|
return json.errcode === 0 ? json.result : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchRiskDetailData = async (param) => {
|
||||||
|
const defaultParam = {
|
||||||
|
opisn: -1,
|
||||||
|
DateType: '',
|
||||||
|
WebCode: 'All',
|
||||||
|
DepartmentList: '',
|
||||||
|
Date1: '',
|
||||||
|
Date2: '',
|
||||||
|
IncludeTickets: '1',
|
||||||
|
};
|
||||||
|
const json = await fetchJSON('/service-Analyse2/sales_crm_process_detail', {...defaultParam, ...param});
|
||||||
|
return json.errcode === 0 ? json.result : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
class SalesCRMData {
|
||||||
|
constructor(appStore) {
|
||||||
|
this.appStore = appStore;
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async get90n180Data(param = {}) {
|
||||||
|
const retProps = param?.retLabel || '';
|
||||||
|
|
||||||
|
let retKey = param.groupDateType === '' ? (param.groupType === 'overview' ? 'dataSource' : 'details') : 'byDate';
|
||||||
|
retKey = param.opisn ? `operator_${param.opisn}`: retKey;
|
||||||
|
if (param.opisn ) {
|
||||||
|
if (!isEmpty(this.results.details)) {
|
||||||
|
const _this_opi_row = this.results.details.filter(ele => ele.groupsKey === param.opisn);
|
||||||
|
this.results[retKey] = _this_opi_row;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isEmpty(this.results[retKey])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.results.loading = true;
|
||||||
|
const date90=this.searchValues.date90;
|
||||||
|
const date180=this.searchValues.date180;
|
||||||
|
const [result90, result180] = await Promise.all([
|
||||||
|
fetchResultsData({ ...this.searchValuesToSub, ...date90, ...param }),
|
||||||
|
fetchResultsData({ ...this.searchValuesToSub, ...date180, ...param }),
|
||||||
|
]);
|
||||||
|
const _90O = groupBy(result90, 'groupsKey');
|
||||||
|
const _180O = groupBy(result180, 'groupsKey');
|
||||||
|
const result2 = unique(Object.keys(_90O).concat(Object.keys(_180O))).map((key) => {
|
||||||
|
return {
|
||||||
|
...pick(_90O[key]?.[0] || _180O[key][0], ['groupsKey', 'groupsLabel', 'groupType']),
|
||||||
|
...(retProps && retKey === 'dataSource' ? { groupsLabel: retProps, retProps } : { retProps }),
|
||||||
|
key: `${param.groupType}-${key}`,
|
||||||
|
result90: _90O[key]?.[0] || {},
|
||||||
|
result180: _180O[key]?.[0] || {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// console.log(result2, '+++++ +++', retKey);
|
||||||
|
// console.log(this.results[retKey]?.length);
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this.results.loading = false;
|
||||||
|
this.results[retKey] = [].concat((this.results[retKey] || []), result2);
|
||||||
|
});
|
||||||
|
return this.results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getResultData(param = {}) {
|
||||||
|
let retKey = param.groupDateType === '' ? 'byOperator' : 'byDate';
|
||||||
|
retKey = param.opisn ? `operator_byDate_${param.opisn}`: retKey;
|
||||||
|
if (!isEmpty(this.results[retKey])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.results.loading = true;
|
||||||
|
this.results[retKey] = [];
|
||||||
|
const res = await fetchResultsData({ ...this.searchValuesToSub, ...param });
|
||||||
|
runInAction(() => {
|
||||||
|
this.results.loading = false;
|
||||||
|
this.results[retKey] = retKey === 'byOperator' ? res.filter(ele => ele.SumML > 0).sort(sortDescBy('SumML')) : res;
|
||||||
|
});
|
||||||
|
return this.results;
|
||||||
|
};
|
||||||
|
|
||||||
|
async getProcessData(param = {}) {
|
||||||
|
// const retKey = param.groupDateType === '' ? 'byOperator' : 'byDate';
|
||||||
|
let retKey = param.groupDateType === '' ? (param.groupType !== 'operator' ? 'dataSource' : 'details') : 'byDate';
|
||||||
|
retKey = param.opisn ? `operator_${param.opisn}`: retKey;
|
||||||
|
if (param.opisn) {
|
||||||
|
if (!isEmpty(this.process.details)) {
|
||||||
|
const _this_opi_row = this.process.details.filter(ele => ele.groupsKey === param.opisn);
|
||||||
|
this.process[retKey] = _this_opi_row;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isEmpty(this.process[retKey])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.process.loading = true;
|
||||||
|
this.process[retKey] = [];
|
||||||
|
const res = await fetchProcessData({ ...this.searchValuesToSub, ...param });
|
||||||
|
runInAction(() => {
|
||||||
|
this.process.loading = false;
|
||||||
|
this.process[retKey] = [].concat(this.process[retKey], res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRiskDetailData(param = {}) {
|
||||||
|
this.risk.loading = true;
|
||||||
|
this.risk.dataSource = [];
|
||||||
|
const res = await fetchRiskDetailData({ ...this.searchValuesToSub, ...param });
|
||||||
|
runInAction(() => {
|
||||||
|
this.risk.loading = false;
|
||||||
|
this.risk.dataSource = res;
|
||||||
|
this.risk.byLostType = groupBy(res, 'lost_type');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
searchValues = {
|
||||||
|
date: moment(),
|
||||||
|
Date1: moment().startOf("week").subtract(7, "days"),
|
||||||
|
Date2: moment().endOf("week").subtract(7, "days"),
|
||||||
|
DateType: { key: 'applyDate', label: '提交日期'},
|
||||||
|
WebCode: { key: 'All', label: '所有来源' },
|
||||||
|
// IncludeTickets: { key: '1', label: '含门票'},
|
||||||
|
DepartmentList: [groupsMappedByCode.GH],
|
||||||
|
operator: '-1',
|
||||||
|
opisn: '-1',
|
||||||
|
date90: {
|
||||||
|
Date1: moment().subtract(90, 'days').format(DATE_FORMAT),
|
||||||
|
Date2: moment().subtract(30, 'days').format(SMALL_DATETIME_FORMAT),
|
||||||
|
},
|
||||||
|
date180: {
|
||||||
|
Date1: moment().subtract(180, 'days').format(DATE_FORMAT),
|
||||||
|
Date2: moment().subtract(50, 'days').format(SMALL_DATETIME_FORMAT),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
searchValuesToSub = {
|
||||||
|
date: moment().format(DATE_FORMAT),
|
||||||
|
Date1: moment().startOf("week").subtract(7, "days").format(DATE_FORMAT),
|
||||||
|
Date2: moment().endOf("week").subtract(7, "days").format(SMALL_DATETIME_FORMAT),
|
||||||
|
DateType: 'applyDate',
|
||||||
|
DepartmentList: groupsMappedByCode.GH.value,
|
||||||
|
WebCode: 'All',
|
||||||
|
operator: '-1',
|
||||||
|
opisn: '-1',
|
||||||
|
};
|
||||||
|
|
||||||
|
setSearchValues(obj, values) {
|
||||||
|
this.searchValues = { ...this.searchValues, ...values };
|
||||||
|
if (values.date) {
|
||||||
|
this.searchValues.date90 = {
|
||||||
|
Date1: (values.date.clone()).subtract(90, 'days').format(DATE_FORMAT),
|
||||||
|
Date2: (values.date.clone()).subtract(30, 'days').format(SMALL_DATETIME_FORMAT),
|
||||||
|
};
|
||||||
|
this.searchValues.date180 = {
|
||||||
|
Date1: (values.date.clone()).subtract(180, 'days').format(DATE_FORMAT),
|
||||||
|
Date2: (values.date.clone()).subtract(50, 'days').format(SMALL_DATETIME_FORMAT),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.searchValuesToSub = {...this.searchValuesToSub, ...obj};
|
||||||
|
}
|
||||||
|
|
||||||
|
results = { loading: false, dataSource: [], details: [], byDate: [], byOperator: [] };
|
||||||
|
process = { loading: false, dataSource: [], details: [], byDate: [], byOperator: [] };
|
||||||
|
risk = { loading: false, dataSource: [], byLostType: {}, };
|
||||||
|
resetData = (rootKey = '') => {
|
||||||
|
if (rootKey === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this[rootKey].loading = false;
|
||||||
|
for (const key of Object.keys(this[rootKey])) {
|
||||||
|
if (key !== 'loading') {
|
||||||
|
this[rootKey][key] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default SalesCRMData;
|
@ -0,0 +1,181 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { stores_Context } from '../config';
|
||||||
|
import { Row, Col, Table, Space, Typography, Divider } from 'antd';
|
||||||
|
import SearchForm from './../components/search/SearchForm';
|
||||||
|
import { VSTag, TableExportBtn } from './../components/Data';
|
||||||
|
import { CruiseAgency } from './../libs/ht';
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||||
|
const { HotelCruiseStore, date_picker_store } = useContext(stores_Context);
|
||||||
|
const { loading, dataSource, summaryRow } = HotelCruiseStore.cruise;
|
||||||
|
|
||||||
|
const { formValues, siderBroken } = searchFormStore;
|
||||||
|
|
||||||
|
const tableSorter = (a, b, colName) => a[colName] - b[colName];
|
||||||
|
const tableExportDataRow = (col1, col2) => [col1, col2].filter((r) => r).join(' VS ');
|
||||||
|
const tableProps = {
|
||||||
|
size: 'small',
|
||||||
|
bordered: true,
|
||||||
|
pagination: false,
|
||||||
|
columns: [
|
||||||
|
{ title: '产品',
|
||||||
|
sorter: (a, b) => a.ProductName.localeCompare(b.ProductName, 'zh-CN'),children: [{ title: summaryRow.ProductName, dataIndex: 'ProductName', key: 'ProductName' }] },
|
||||||
|
{
|
||||||
|
title: '房间数',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'TotalNum'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{summaryRow.TotalNum}
|
||||||
|
{summaryRow.TotalNumPercent ? <Text type="secondary"> VS {summaryRow.CPTotalNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{summaryRow.TotalNumPercent && <VSTag diffPercent={summaryRow.TotalNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
titleX: tableExportDataRow(summaryRow.TotalNum, summaryRow.CPTotalNum),
|
||||||
|
dataExport: (v, r) => tableExportDataRow(r.TotalNum, r.CPTotalNum),
|
||||||
|
dataIndex: 'TotalNum',
|
||||||
|
key: 'TotalNum',
|
||||||
|
render: (v, r) => (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{v}
|
||||||
|
{r.CPTotalNum ? <Text type="secondary"> VS {r.CPTotalNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{r.CPTotalNum && <VSTag diffPercent={r.TotalNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '人数',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'TotalPersonNum'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{summaryRow.TotalPersonNum}
|
||||||
|
{summaryRow.TotalPersonNumPercent ? <Text type="secondary"> VS {summaryRow.CPTotalPersonNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{summaryRow.TotalPersonNumPercent && <VSTag diffPercent={summaryRow.TotalPersonNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
titleX: tableExportDataRow(summaryRow.TotalPersonNum, summaryRow.CPTotalPersonNum),
|
||||||
|
dataExport: (v, r) => tableExportDataRow(r.TotalPersonNum, r.CPTotalPersonNum),
|
||||||
|
dataIndex: 'TotalPersonNum',
|
||||||
|
key: 'TotalPersonNum',
|
||||||
|
render: (v, r) => (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{v}
|
||||||
|
{r.CPTotalPersonNum ? <Text type="secondary"> VS {r.CPTotalPersonNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{r.CPTotalNum && <VSTag diffPercent={r.TotalPersonNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '总利润',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'TotalProfit'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{summaryRow.TotalProfit}
|
||||||
|
{summaryRow.TotalProfitPercent ? <Text type="secondary"> VS {summaryRow.CPTotalProfit}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{summaryRow.TotalProfitPercent && <VSTag diffPercent={summaryRow.TotalProfitPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
titleX: tableExportDataRow(summaryRow.TotalProfit, summaryRow.CPTotalProfit),
|
||||||
|
dataExport: (v, r) => tableExportDataRow(r.TotalProfit, r.CPTotalProfit),
|
||||||
|
dataIndex: 'TotalProfit',
|
||||||
|
key: 'TotalProfit',
|
||||||
|
render: (v, r) => (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{v}
|
||||||
|
{r.CPTotalProfit ? <Text type="secondary"> VS {r.CPTotalProfit}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{r.CPTotalNum && <VSTag diffPercent={r.TotalProfitPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// { title: '单订船', dataIndex: '', key: '' },
|
||||||
|
// { title: '订单含行程', dataIndex: '', key: '' },
|
||||||
|
// { title: '国籍', dataIndex: '', key: '' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||||
|
<Col md={24} lg={24} xxl={24}>
|
||||||
|
<SearchForm
|
||||||
|
defaultValue={{
|
||||||
|
initialValue: {
|
||||||
|
...date_picker_store.formValues,
|
||||||
|
...HotelCruiseStore.searchValues,
|
||||||
|
},
|
||||||
|
// 'countryArea',
|
||||||
|
shows: [
|
||||||
|
'DepartmentList',
|
||||||
|
'orderStatus',
|
||||||
|
'dates',
|
||||||
|
'keyword',
|
||||||
|
'cruiseDirection',
|
||||||
|
'agency',
|
||||||
|
'cruiseBookType',
|
||||||
|
'country', // 'roomsRange', 'personRange'
|
||||||
|
],
|
||||||
|
sort: { keyword: 101, agency: 110, cruiseDirection: 102, country: 104 },
|
||||||
|
fieldProps: {
|
||||||
|
keyword: { placeholder: '产品名', col: 4 },
|
||||||
|
DepartmentList: { show_all: true, mode: 'multiple' },
|
||||||
|
orderStatus: { show_all: true },
|
||||||
|
cruiseDirection: { show_all: true },
|
||||||
|
agency: { defaultOptions: CruiseAgency, autoGet: false },
|
||||||
|
// years: { hide_vs: false },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onSubmit={(_err, obj, form) => {
|
||||||
|
HotelCruiseStore.setSearchValues(obj, form);
|
||||||
|
HotelCruiseStore.getCruiseData(obj);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<section>
|
||||||
|
<Divider orientation="right" >
|
||||||
|
<TableExportBtn label={'游船'} {...{ columns: tableProps.columns, dataSource }} />
|
||||||
|
</Divider>
|
||||||
|
<Table {...tableProps} {...{ loading, dataSource }} rowKey={(record) => record.ProductName} />
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,147 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { Row, Col, Typography, Space, Table, Divider } from 'antd';
|
||||||
|
import { stores_Context } from '../config';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import 'moment/locale/zh-cn';
|
||||||
|
import SearchForm from '../components/search/SearchForm';
|
||||||
|
import { TableExportBtn } from '../components/Data';
|
||||||
|
import * as comm from '../utils/commons';
|
||||||
|
|
||||||
|
const HostCaseCount = () => {
|
||||||
|
const { customer_store, date_picker_store } = useContext(stores_Context);
|
||||||
|
const host_case_data = customer_store.host_case_data;
|
||||||
|
|
||||||
|
const columnsList = [
|
||||||
|
{
|
||||||
|
title: '团数',
|
||||||
|
dataIndex: 'TotalGroupNum',
|
||||||
|
key: 'TotalGroupNum',
|
||||||
|
sorter: (a, b) => parseInt(a.TotalGroupNum) - parseInt(b.TotalGroupNum),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '人数',
|
||||||
|
dataIndex: 'TotalPersonNum',
|
||||||
|
key: 'TotalPersonNum',
|
||||||
|
sorter: (a, b) => parseInt(a.TotalPersonNum) - parseInt(b.TotalPersonNum),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '计费团天数',
|
||||||
|
dataIndex: 'TotalDays',
|
||||||
|
key: 'TotalDays',
|
||||||
|
sorter: (a, b) => parseInt(a.TotalDays) - parseInt(b.TotalDays),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '交易额',
|
||||||
|
dataIndex: 'TotalPrice',
|
||||||
|
key: 'TotalPrice',
|
||||||
|
sorter: (a, b) => parseInt(a.TotalPrice) - parseInt(b.TotalPrice),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// 筛选函数
|
||||||
|
const getFiltersData=(filterData,filterKey)=>{
|
||||||
|
return comm.uniqWith(filterData.map(rr => ({ text: rr[filterKey], value: rr[filterKey] })),
|
||||||
|
(a, b) => JSON.stringify(a) === JSON.stringify(b)).sort((a, b) => a.text.localeCompare(b.text));
|
||||||
|
};
|
||||||
|
// 小组筛选菜单项
|
||||||
|
const allOPIGroup = getFiltersData(host_case_data.summaryData,"GroupBy");
|
||||||
|
// 顾问筛选菜单项
|
||||||
|
const allOPIConsultant = getFiltersData(host_case_data.counselorData,"GroupBy");
|
||||||
|
// 明细表顾问名筛选菜单项
|
||||||
|
const allOPIDetailConsultant = getFiltersData(host_case_data.singleDetailData,"OPI_Name");
|
||||||
|
const summaryColumnsList = [{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'GroupBy',
|
||||||
|
key: 'GroupBy',
|
||||||
|
}, ...columnsList];
|
||||||
|
const groupColumnsList = [{
|
||||||
|
title: '组名',
|
||||||
|
dataIndex: 'GroupBy',
|
||||||
|
key: 'GroupBy',
|
||||||
|
filters: allOPIGroup,
|
||||||
|
onFilter: (value, record) => record.GroupBy === value,
|
||||||
|
filterSearch: true,
|
||||||
|
}, ...columnsList];
|
||||||
|
const counselorColumnsList = [{
|
||||||
|
title: '顾问名',
|
||||||
|
dataIndex: 'GroupBy',
|
||||||
|
key: 'GroupBy',
|
||||||
|
filters: allOPIConsultant,
|
||||||
|
onFilter: (value, record) => record.GroupBy === value,
|
||||||
|
filterSearch: true,
|
||||||
|
}, ...columnsList];
|
||||||
|
const singleDetailColumnsList = [{
|
||||||
|
title: '团名',
|
||||||
|
dataIndex: 'GroupBy',
|
||||||
|
key: 'GroupBy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '顾问名',
|
||||||
|
dataIndex: 'OPI_Name',
|
||||||
|
key: 'OPI_Name',
|
||||||
|
filters: allOPIDetailConsultant,
|
||||||
|
onFilter: (value, record) => record.OPI_Name === value,
|
||||||
|
filterSearch: true,
|
||||||
|
},
|
||||||
|
...columnsList.slice(1)];
|
||||||
|
|
||||||
|
const renderRow=(rowColumns,rowDataSource,title)=>{
|
||||||
|
return(
|
||||||
|
<Row>
|
||||||
|
<Col span={24}>
|
||||||
|
<Typography.Title level={3}>{title}</Typography.Title>
|
||||||
|
<Divider orientation="right" plain>
|
||||||
|
<TableExportBtn label={title} {...{ columns: rowColumns, dataSource: rowDataSource }} />
|
||||||
|
</Divider>
|
||||||
|
<Table
|
||||||
|
sticky
|
||||||
|
id={`${rowColumns}`}
|
||||||
|
dataSource={rowDataSource}
|
||||||
|
columns={rowColumns}
|
||||||
|
size="small"
|
||||||
|
rowKey={(record) => record.key}
|
||||||
|
loading={host_case_data.loading}
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ x: 1000 }}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
|
<Row gutter={16} className={date_picker_store.siderBroken ? "" : "sticky-top"} >
|
||||||
|
<Col className="gutter-row" span={24}>
|
||||||
|
<SearchForm
|
||||||
|
defaultValue={{
|
||||||
|
initialValue: {
|
||||||
|
...date_picker_store.formValues,
|
||||||
|
...host_case_data.searchValues,
|
||||||
|
},
|
||||||
|
shows: ['DepartmentList', 'dates'],
|
||||||
|
fieldProps: {
|
||||||
|
DepartmentList: { show_all: false, mode: 'multiple' },
|
||||||
|
dates: { hide_vs: true },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onSubmit={(_err, obj, form) => {
|
||||||
|
customer_store.setSearchValues(obj, form,"host_case_data");
|
||||||
|
customer_store.getHostCaseData("1");
|
||||||
|
customer_store.getHostCaseData("2");
|
||||||
|
customer_store.getHostCaseData("3");
|
||||||
|
customer_store.getHostCaseData("4");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{renderRow(summaryColumnsList,host_case_data.summaryData,'东道主项目汇总')}
|
||||||
|
{renderRow(groupColumnsList,host_case_data.groupData,'东道主项目小组统计')}
|
||||||
|
{renderRow(counselorColumnsList,host_case_data.counselorData,'东道主项目顾问统计')}
|
||||||
|
{renderRow(singleDetailColumnsList,host_case_data.singleDetailData,'单团明细')}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default observer(HostCaseCount);
|
@ -0,0 +1,171 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { stores_Context } from '../config';
|
||||||
|
import { Row, Col, Table, Space, Typography, Divider } from 'antd';
|
||||||
|
import SearchForm from './../components/search/SearchForm';
|
||||||
|
import { VSTag, TableExportBtn } from './../components/Data';
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||||
|
const { date_picker_store, HotelCruiseStore } = useContext(stores_Context);
|
||||||
|
const { loading, dataSource, summaryRow } = HotelCruiseStore.hotel;
|
||||||
|
|
||||||
|
const { formValues, siderBroken } = searchFormStore;
|
||||||
|
|
||||||
|
const tableSorter = (a, b, colName) => a[colName] - b[colName];
|
||||||
|
const tableExportDataRow = (col1, col2) => [col1, col2].filter((r) => r).join(' VS ');
|
||||||
|
|
||||||
|
const tableProps = {
|
||||||
|
size: 'small',
|
||||||
|
bordered: true,
|
||||||
|
pagination: false,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '目的地',
|
||||||
|
sorter: (a, b) => a.CityName.localeCompare(b.CityName, 'zh-CN'),
|
||||||
|
children: [{ title: summaryRow.CityName, dataIndex: 'CityName', key: 'CityName' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '总间夜',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'TotalNum'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{summaryRow.TotalNum}
|
||||||
|
{summaryRow.TotalNumPercent ? <Text type="secondary"> VS {summaryRow.CPTotalNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{summaryRow.TotalNumPercent && <VSTag diffPercent={summaryRow.TotalNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
titleX: tableExportDataRow(summaryRow.TotalNum, summaryRow.CPTotalNum),
|
||||||
|
dataIndex: 'TotalNum',
|
||||||
|
key: 'TotalNum',
|
||||||
|
dataExport: (v, r) => tableExportDataRow(r.TotalNum, r.CPTotalNum),
|
||||||
|
render: (v, r) => (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{v}
|
||||||
|
{r.CPTotalNum ? <Text type="secondary"> VS {r.CPTotalNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{r.CPTotalNum && <VSTag diffPercent={r.TotalNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '主推',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'RecomendNum'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{summaryRow.RecomendNum}
|
||||||
|
{summaryRow.RecomendNumPercent ? <Text type="secondary"> VS {summaryRow.CPRecomendNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{summaryRow.RecomendNumPercent && <VSTag diffPercent={summaryRow.RecomendNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
titleX: tableExportDataRow(summaryRow.RecomendNum, summaryRow.CPRecomendNum),
|
||||||
|
dataIndex: 'RecomendNum',
|
||||||
|
key: 'RecomendNum',
|
||||||
|
dataExport: (v, r) => tableExportDataRow(r.RecomendNum, r.CPRecomendNum),
|
||||||
|
render: (v, r) => (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{v}
|
||||||
|
{r.CPRecomendNum ? <Text type="secondary"> VS {r.CPRecomendNum}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{r.CPRecomendNum && <VSTag diffPercent={r.RecomendNumPercent} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '使用比例',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{summaryRow.RecommendRate_100}
|
||||||
|
{summaryRow.RecommendRateDelta ? <Text type="secondary"> VS {summaryRow.CPRecommendRate_100}</Text> : null}
|
||||||
|
</Text>
|
||||||
|
{summaryRow.RecommendRateDelta && <VSTag diffPercent={summaryRow.RecommendRateDelta} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
titleX: tableExportDataRow(summaryRow.RecommendRate_100, summaryRow.CPRecommendRate_100),
|
||||||
|
dataIndex: 'RecommendRate_100',
|
||||||
|
key: 'RecommendRate_100',
|
||||||
|
dataExport: (v, r) => tableExportDataRow(r.RecommendRate_100, r.CPRecommendRate_100),
|
||||||
|
render: (v, r) => (
|
||||||
|
<>
|
||||||
|
<Space direction={'vertical'}>
|
||||||
|
<Text strong>
|
||||||
|
{v}
|
||||||
|
{r.RecommendRateDelta !== undefined && <Text type="secondary"> VS {r.CPRecommendRate_100}</Text>}
|
||||||
|
</Text>
|
||||||
|
{r.RecommendRateDelta !== undefined && <VSTag diffPercent={r.RecommendRateDelta} />}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||||
|
<Col md={24} lg={24} xxl={24}>
|
||||||
|
<SearchForm
|
||||||
|
defaultValue={{
|
||||||
|
initialValue: {
|
||||||
|
...date_picker_store.formValues,
|
||||||
|
...HotelCruiseStore.searchValues,
|
||||||
|
},
|
||||||
|
// 'countryArea', 'DateType', 'dates', 'hotelRecommandRate',
|
||||||
|
shows: ['DepartmentList', 'countryArea', 'orderStatus', 'hotelBookType', 'hotelStar', 'DateType', 'dates'],
|
||||||
|
sort: { DateType: 101, dates: 102 },
|
||||||
|
fieldProps: {
|
||||||
|
DepartmentList: { show_all: true, mode: 'multiple' },
|
||||||
|
countryArea: { show_all: true },
|
||||||
|
orderStatus: { show_all: true },
|
||||||
|
hotelBookType: { show_all: true },
|
||||||
|
hotelRecommandRate: { show_all: true },
|
||||||
|
// years: { hide_vs: false },
|
||||||
|
DateType: { disabledKeys: ['applyDate'] },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onSubmit={(_err, obj, form) => {
|
||||||
|
HotelCruiseStore.setSearchValues(obj, form);
|
||||||
|
HotelCruiseStore.getHotelData(obj);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<section>
|
||||||
|
<Divider orientation="right" >
|
||||||
|
<TableExportBtn label={'酒店'} {...{ columns: tableProps.columns, dataSource }} />
|
||||||
|
</Divider>
|
||||||
|
<Table {...tableProps} bordered {...{ loading, dataSource }} rowKey={(record) => record.CityName} />
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,123 @@
|
|||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { stores_Context } from '../config';
|
||||||
|
import { Spin, Table, Row, Col, Tabs, Switch } from 'antd';
|
||||||
|
import SearchForm from './../components/search/SearchForm';
|
||||||
|
import { TableExportBtn } from './../components/Data';
|
||||||
|
import { empty } from '../utils/commons';
|
||||||
|
import './kpi.css';
|
||||||
|
|
||||||
|
const apartOptions = [
|
||||||
|
{ key: 'inbound', value: 'inbound', label: '入境' },
|
||||||
|
{ key: 'outbound', value: 'outbound', label: '出境' },
|
||||||
|
{ key: 'domestic', value: 'domestic', label: '国内' },
|
||||||
|
];
|
||||||
|
const apartOptionsMapped = apartOptions.reduce((r, v) => ({...r, [v.value]: v}), {});
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { financial_store: financialStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||||
|
const { formValues, formValuesToSub } = searchFormStore;
|
||||||
|
const { servicePersonNum } = financialStore;
|
||||||
|
const { curTab } = servicePersonNum;
|
||||||
|
const [ifNull, setIfNull] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// DistributionStore.setFormDates(formValuesToSub);
|
||||||
|
financialStore.resetPersonNumData();
|
||||||
|
return () => {};
|
||||||
|
}, [formValuesToSub]);
|
||||||
|
|
||||||
|
const pageRefresh = (queryData = formValuesToSub) => {
|
||||||
|
// console.log(queryData, 'qqqq');
|
||||||
|
financialStore.getPersonNum(queryData);
|
||||||
|
|
||||||
|
financialStore.setPersonNumTableDataSource(false);
|
||||||
|
setIfNull(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: apartOptionsMapped[curTab].label, dataIndex: 'groupsLabel', children: [{ title: `${formValuesToSub.Date1}~${formValuesToSub.Date2.substring(0, 10)}`, dataIndex: 'groupsLabel' }] },
|
||||||
|
{
|
||||||
|
title: '人次数',
|
||||||
|
children: [{ title: '组织', dataIndex: 'orgz' }, ...(['inbound', 'domestic'].includes(curTab) ? [{ title: '接待', dataIndex: 'hosts' }] : [])],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '人天',
|
||||||
|
children: [{ title: '组织', dataIndex: 'orgzPDays' }, ...(['inbound', 'domestic'].includes(curTab) ? [{ title: '接待', dataIndex: 'hostsPDays' }] : [])],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={16} style={{ margin: '-16px -8px', position: 'sticky', top: 0, zIndex: 10 }}>
|
||||||
|
<Col className="gutter-row" span={24}>
|
||||||
|
<SearchForm
|
||||||
|
defaultValue={{
|
||||||
|
initialValue: {
|
||||||
|
...formValues,
|
||||||
|
DateType: { key: 'startDate', value: 'startDate', label: '走团日期' },
|
||||||
|
},
|
||||||
|
shows: ['dates'],
|
||||||
|
fieldProps: {
|
||||||
|
dates: { hide_vs: true },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onSubmit={(_err, obj, form, str) => {
|
||||||
|
pageRefresh(obj);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<Tabs
|
||||||
|
activeKey={curTab}
|
||||||
|
onChange={(v) => {
|
||||||
|
financialStore.setCurTab(v);
|
||||||
|
if (empty(servicePersonNum[v].dataSource)) {
|
||||||
|
pageRefresh();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
tabBarExtraContent={{
|
||||||
|
right: (
|
||||||
|
<>
|
||||||
|
<Switch
|
||||||
|
unCheckedChildren="原数据"
|
||||||
|
checkedChildren="去除空"
|
||||||
|
key={'ifNull'}
|
||||||
|
checked={ifNull}
|
||||||
|
onChange={(e) => {
|
||||||
|
financialStore.setPersonNumTableDataSource(e);
|
||||||
|
setIfNull(e);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TableExportBtn label={'服务人数_'+apartOptionsMapped[curTab].label} {...{ columns, dataSource: servicePersonNum[curTab].dataSource }} />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
type="card"
|
||||||
|
items={apartOptions.map((ele) => {
|
||||||
|
return {
|
||||||
|
...ele,
|
||||||
|
children: (
|
||||||
|
<Spin spinning={servicePersonNum.loading}>
|
||||||
|
<Table
|
||||||
|
id="table_to_xlsx_sale"
|
||||||
|
dataSource={servicePersonNum[curTab].dataSource}
|
||||||
|
rowKey="groupsKey"
|
||||||
|
columns={columns}
|
||||||
|
size="small"
|
||||||
|
loading={servicePersonNum[curTab].loading}
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ x: '100%' }}
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,19 @@
|
|||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { APP_VERSION } from '../config';
|
||||||
|
import { SlackOutlined, SketchOutlined, AntCloudOutlined, RedditOutlined, GithubOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const compileTime = moment(Number(process.env.REACT_APP_BUILD_TIME)).format('YYYY-MM-DD ddd HH:mm:ss');
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<SketchOutlined /> <AntCloudOutlined /> <SlackOutlined /> <RedditOutlined /> <GithubOutlined />
|
||||||
|
<div>欢迎! </div>
|
||||||
|
<div>当前版本: v<span>{APP_VERSION}</span></div>
|
||||||
|
<div>编译时间: <span>{compileTime}</span></div>
|
||||||
|
<SketchOutlined /> <AntCloudOutlined /> <SlackOutlined /> <RedditOutlined /> <GithubOutlined />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,334 @@
|
|||||||
|
import React, { useContext, useState, useEffect } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { stores_Context, DATE_FORMAT, SMALL_DATETIME_FORMAT } from '../../config';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Row, Col, Table, Select, Spin, Tag } from 'antd';
|
||||||
|
import SearchForm from '../../components/search/SearchForm';
|
||||||
|
import MixFieldsDetail from '../../components/MixFieldsDetail';
|
||||||
|
import { fixTo2Decimals, isEmpty, pick } from '../../utils/commons';
|
||||||
|
import Column from '../../components/Column';
|
||||||
|
import { groupsMappedByKey } from '../../libs/ht';
|
||||||
|
|
||||||
|
const COLOR_SETS = [
|
||||||
|
"#FFFFFF",
|
||||||
|
// "#5B8FF9",
|
||||||
|
// "#FF6B3B",
|
||||||
|
"#9FB40F",
|
||||||
|
"#76523B",
|
||||||
|
"#DAD5B5",
|
||||||
|
"#E19348",
|
||||||
|
"#F383A2",
|
||||||
|
];
|
||||||
|
|
||||||
|
const transparentHex = '1A';
|
||||||
|
|
||||||
|
const numberConvert10K = (number, scale = 10) => {
|
||||||
|
return fixTo2Decimals((number/(1000*scale)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { SalesCRMDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||||
|
|
||||||
|
const { formValues, formValuesToSub, siderBroken } = searchFormStore;
|
||||||
|
const _formValuesToSub = pick(formValuesToSub, ['DepartmentList', 'WebCode']);
|
||||||
|
|
||||||
|
const { searchValues, resetData, results } = SalesCRMDataStore;
|
||||||
|
const operatorObjects = results.details.map((v) => ({ key: v.groupsKey, value: v.groupsKey, label: v.groupsLabel, text: v.groupsLabel }));
|
||||||
|
// console.log(operatorObjects);
|
||||||
|
|
||||||
|
const pageRefresh = async (obj) => {
|
||||||
|
resetData('results');
|
||||||
|
const deptList = obj.DepartmentList.split(',');
|
||||||
|
const includeCH = ['1', '2', '7'].some(ele => deptList.includes(ele));
|
||||||
|
const includeAH = ['28'].every(ele => deptList.includes(ele));
|
||||||
|
const includeGH = ['33'].every(ele => deptList.includes(ele));
|
||||||
|
const otherDept = deptList.filter(ele => !['1', '2', '7', '28', '33'].includes(ele));
|
||||||
|
const separateParam = [];
|
||||||
|
if (includeCH) {
|
||||||
|
const inCH = deptList.filter(k => ['1', '2', '7'].includes(k)).join(',');
|
||||||
|
separateParam.push({ DepartmentList: inCH, retLabel: 'CH'});
|
||||||
|
}
|
||||||
|
if (includeAH) {
|
||||||
|
separateParam.push({ DepartmentList: '28', retLabel: 'AH'});
|
||||||
|
}
|
||||||
|
if (includeGH) {
|
||||||
|
separateParam.push({ DepartmentList: '33', retLabel: 'GH'});
|
||||||
|
}
|
||||||
|
if (!isEmpty(otherDept) && (!isEmpty(includeAH) || !isEmpty(includeCH) || !isEmpty(includeGH))) {
|
||||||
|
separateParam.push({ DepartmentList: otherDept.join(','), retLabel: otherDept.map(k => groupsMappedByKey[k].label).join(', ') }); // '其它组'
|
||||||
|
}
|
||||||
|
if (!includeAH && !includeCH && !includeGH) {
|
||||||
|
separateParam.push({ DepartmentList: obj.DepartmentList });
|
||||||
|
}
|
||||||
|
// console.log('separateParam', separateParam, otherDept);
|
||||||
|
// console.log('formValuesToSub --- pageRefresh', formValuesToSub.DepartmentList);
|
||||||
|
// return;
|
||||||
|
for await (const subParam of separateParam) {
|
||||||
|
// console.log(subParam);
|
||||||
|
await SalesCRMDataStore.get90n180Data({
|
||||||
|
...(obj || _formValuesToSub),
|
||||||
|
...subParam,
|
||||||
|
groupType: 'overview',
|
||||||
|
// groupType: 'dept', // todo:
|
||||||
|
groupDateType: '',
|
||||||
|
});
|
||||||
|
await SalesCRMDataStore.get90n180Data({
|
||||||
|
...(obj || _formValuesToSub),
|
||||||
|
...subParam,
|
||||||
|
groupType: 'operator',
|
||||||
|
groupDateType: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFullYearDiagramData = async (obj) => {
|
||||||
|
// console.log('invoke --- getFullYearDiagramData');
|
||||||
|
// console.log('formValuesToSub --- getFullYearDiagramData', formValuesToSub.DepartmentList);
|
||||||
|
await SalesCRMDataStore.getResultData({
|
||||||
|
..._formValuesToSub,
|
||||||
|
Date1: moment(obj.date).startOf('year').format(DATE_FORMAT),
|
||||||
|
Date2: moment(obj.date).endOf('year').format(SMALL_DATETIME_FORMAT),
|
||||||
|
groupType: 'overview',
|
||||||
|
// groupType: 'operator',
|
||||||
|
groupDateType: 'month',
|
||||||
|
...(obj),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const getDiagramData = async (obj) => {
|
||||||
|
// console.log('invoke --- getDiagramData');
|
||||||
|
// console.log(_formValuesToSub, SalesCRMDataStore.searchValuesToSub);
|
||||||
|
await SalesCRMDataStore.getResultData({
|
||||||
|
..._formValuesToSub,
|
||||||
|
...SalesCRMDataStore.searchValuesToSub,
|
||||||
|
// Date1: moment().startOf('year').format(DATE_FORMAT),
|
||||||
|
// Date2: moment().endOf('year').format(SMALL_DATETIME_FORMAT),
|
||||||
|
// groupType: 'overview',
|
||||||
|
groupType: 'operator',
|
||||||
|
groupDateType: '',
|
||||||
|
...(obj),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const retPropsMinRatesSet = { 'CH': 12, 'AH': 8, 'default': 10 }; //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业绩数据列
|
||||||
|
* ! 成行率: CH个人成行率<12%, AH个人成行率<8%
|
||||||
|
*/
|
||||||
|
const dataFields = (suffix, colRootIndex) => [
|
||||||
|
{
|
||||||
|
key: 'ConfirmRates' + suffix,
|
||||||
|
title: '成行率',
|
||||||
|
dataIndex: [suffix, 'ConfirmRates'],
|
||||||
|
width: '5em',
|
||||||
|
// CH个人成行率<12%, AH个人成行率<8%刷红
|
||||||
|
render: (val, r) => ({
|
||||||
|
props: { style: { color: val < (retPropsMinRatesSet?.[r?.retProps || 'default'] || retPropsMinRatesSet.default) ? 'red' : 'green', backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: val ? `${val}%` : '',
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ConfirmRates - b[suffix].ConfirmRates),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SumML' + suffix,
|
||||||
|
title: '业绩/万',
|
||||||
|
dataIndex: [suffix, 'SumML'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: numberConvert10K(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].SumML - b[suffix].SumML),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ConfirmOrder' + suffix,
|
||||||
|
title: '团数',
|
||||||
|
dataIndex: [suffix, 'ConfirmOrder'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: _,
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ConfirmOrder - b[suffix].ConfirmOrder),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SumOrder' + suffix,
|
||||||
|
title: '订单数',
|
||||||
|
dataIndex: [suffix, 'SumOrder'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: _,
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].SumOrder - b[suffix].SumOrder),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ResumeOrder' + suffix,
|
||||||
|
title: '老客户团数',
|
||||||
|
dataIndex: [suffix, 'ResumeOrder'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: _,
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ResumeOrder - b[suffix].ResumeOrder),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ResumeRates' + suffix,
|
||||||
|
title: '老客户成行率',
|
||||||
|
dataIndex: [suffix, 'ResumeRates'],
|
||||||
|
width: '5em',
|
||||||
|
render: (val, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: val ? `${val}%` : ''
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ResumeRates - b[suffix].ResumeRates),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const dashboardTableProps = {
|
||||||
|
pagination: false,
|
||||||
|
size: 'small',
|
||||||
|
showSorterTooltip: false,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'groupsLabel',
|
||||||
|
key: 'name',
|
||||||
|
width: '5em',
|
||||||
|
filterSearch: true,
|
||||||
|
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
||||||
|
onFilter: (value, record) => record.groupsKey === value || record.groupType === 'overview',
|
||||||
|
render: (text, record) => (record.groupType !== 'operator' ? text : <Link to={`/op_risk/sales/${record.groupsKey}`}>{text}</Link>),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (<>前90 -30天<br/>{searchValues.date90.Date1} 至 {searchValues.date90.Date2}</>),
|
||||||
|
key: 'date',
|
||||||
|
children: dataFields('result90', 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (<>前180 -50天<br/>{searchValues.date180.Date1} 至 {searchValues.date180.Date2}</>),
|
||||||
|
key: 'department',
|
||||||
|
children: dataFields('result180', 1),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rowClassName: (record, rowIndex) => {
|
||||||
|
return record.groupType === 'overview' ? 'ant-tag-blue' : '';
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnConfig = {
|
||||||
|
xField: 'groupsLabel',
|
||||||
|
yField: 'SumML',
|
||||||
|
label: { position: 'top' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const [clickColumn, setClickColumn] = useState({});
|
||||||
|
const [clickColumnTitle, setClickColumnTitle] = useState('');
|
||||||
|
const onChartItemClick = (colData) => {
|
||||||
|
// console.log('onChartItemClick', colData);
|
||||||
|
if (colData.groupType === 'operator') {
|
||||||
|
// test: 0
|
||||||
|
return false; // 单人趋势数据上的点击, 不做钻取
|
||||||
|
}
|
||||||
|
setClickColumn(colData);
|
||||||
|
setClickColumnTitle(moment(colData.groupDateVal).format('YYYY-MM'));
|
||||||
|
};
|
||||||
|
const chartsConfig = {
|
||||||
|
colFields: ['ConfirmOrder', 'SumOrder'],
|
||||||
|
lineFields: ['SumML', 'ConfirmRates'],
|
||||||
|
seriesField: null,
|
||||||
|
xField: 'groupDateVal',
|
||||||
|
itemClick: onChartItemClick,
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEmpty(clickColumnTitle)) {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
getDiagramData({
|
||||||
|
Date1: moment(clickColumn.groupDateVal).startOf('month').format(DATE_FORMAT),
|
||||||
|
Date2: moment(clickColumn.groupDateVal).endOf('month').format(SMALL_DATETIME_FORMAT),
|
||||||
|
groupType: 'operator', // test: overview
|
||||||
|
groupDateType: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [clickColumnTitle]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||||
|
<Col md={24} lg={24} xxl={24}>
|
||||||
|
<SearchForm
|
||||||
|
defaultValue={{
|
||||||
|
initialValue: {
|
||||||
|
...formValues,
|
||||||
|
...SalesCRMDataStore.searchValues,
|
||||||
|
},
|
||||||
|
shows: ['DepartmentList', 'WebCode', 'date'],
|
||||||
|
fieldProps: {
|
||||||
|
DepartmentList: { show_all: false, mode: 'multiple', col: 5 },
|
||||||
|
WebCode: { show_all: false, mode: 'multiple', col: 5 },
|
||||||
|
dates: { hide_vs: true },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onSubmit={(_err, obj, form, str) => {
|
||||||
|
SalesCRMDataStore.setSearchValues(obj, form);
|
||||||
|
pageRefresh(obj);
|
||||||
|
getFullYearDiagramData({ groupType: 'overview', ...obj });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<section>
|
||||||
|
<Table {...dashboardTableProps} bordered dataSource={[...results.dataSource, ...results.details]} loading={results.loading} sticky />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col flex={'12em'}><h3>每月数据</h3></Col>
|
||||||
|
<Col flex={'auto'}>
|
||||||
|
<Select
|
||||||
|
labelInValue
|
||||||
|
// mode={'multiple'}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder={'选择顾问'}
|
||||||
|
// onChange={sale_store.setPickSales}
|
||||||
|
// onSelect={}
|
||||||
|
onChange={(labelInValue) => labelInValue ? getFullYearDiagramData({ groupType: 'operator', opisn: labelInValue.value, }) : false}
|
||||||
|
onClear={() => getFullYearDiagramData({ groupType: 'overview', opisn: -1, })}
|
||||||
|
// value={sale_store.salesTrade.pickSales}
|
||||||
|
// maxTagCount={1}
|
||||||
|
// maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
|
||||||
|
allowClear={true}
|
||||||
|
options={operatorObjects}
|
||||||
|
/>
|
||||||
|
{/* {operatorObjects.map((ele) => (
|
||||||
|
<Select.Option key={ele.key} value={ele.value}>
|
||||||
|
{ele.label}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select> */}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spin spinning={results.loading}>
|
||||||
|
{/* 小组每月; x轴: 日期; y轴: [订单数, ...] */}
|
||||||
|
<MixFieldsDetail {...chartsConfig} dataSource={results.byDate} />
|
||||||
|
</Spin>
|
||||||
|
<h3>
|
||||||
|
点击上方图表的柱状图, 查看当月 <Tag color='orange'>业绩</Tag>数据: <Tag color='orange'>{clickColumnTitle}</Tag>
|
||||||
|
</h3>
|
||||||
|
{/* 显示小组的详情: 所有顾问? */}
|
||||||
|
<Spin spinning={results.loading}>
|
||||||
|
<Column {...columnConfig} dataSource={results.byOperator} />
|
||||||
|
</Spin>
|
||||||
|
{/* <Table columns={[{ title: '', dataIndex: 'date', key: 'date', width: '5em' }, ...dataFields]} bordered size='small' /> */}
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
{/* 月份×小组的详情; x轴: 顾问; y轴: [订单数, ...] */}
|
||||||
|
{/* <MixFieldsDetail {...chartsConfig} xField={'label'} dataSource={[]} /> */}
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,258 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { stores_Context } from '../../config';
|
||||||
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { Row, Col, Table, Tooltip } from 'antd';
|
||||||
|
import SearchForm from '../../components/search/SearchForm';
|
||||||
|
import { pick } from '../../utils/commons';
|
||||||
|
|
||||||
|
const COLOR_SETS = [
|
||||||
|
"#FFFFFF",
|
||||||
|
// "#5B8FF9",
|
||||||
|
// "#FF6B3B",
|
||||||
|
"#9FB40F",
|
||||||
|
"#76523B",
|
||||||
|
"#DAD5B5",
|
||||||
|
"#E19348",
|
||||||
|
"#F383A2",
|
||||||
|
];
|
||||||
|
|
||||||
|
const transparentHex = '1A';
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { SalesCRMDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||||
|
|
||||||
|
// const { formValues, siderBroken } = searchFormStore;
|
||||||
|
const { formValues, formValuesToSub, siderBroken } = searchFormStore;
|
||||||
|
const _formValuesToSub = pick(formValuesToSub, ['DepartmentList', 'WebCode']);
|
||||||
|
const { resetData, process } = SalesCRMDataStore;
|
||||||
|
const operatorObjects = process.details.map((v) => ({ key: v.groupsKey, value: v.groupsKey, label: v.groupsLabel, text: v.groupsLabel }));
|
||||||
|
|
||||||
|
const pageRefresh = async (obj) => {
|
||||||
|
resetData('process');
|
||||||
|
Promise.allSettled([
|
||||||
|
SalesCRMDataStore.getProcessData({
|
||||||
|
...(obj || _formValuesToSub),
|
||||||
|
// ...subParam,
|
||||||
|
// groupType: 'overview',
|
||||||
|
groupType: 'dept',
|
||||||
|
groupDateType: '',
|
||||||
|
}),
|
||||||
|
SalesCRMDataStore.getProcessData({
|
||||||
|
...(obj || _formValuesToSub),
|
||||||
|
// ...subParam,
|
||||||
|
groupType: 'operator',
|
||||||
|
groupDateType: '',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableSorter = (a, b, colName) => (a.groupType !== 'operator' ? -1 : b.groupType !== 'operator' ? 0 : a[colName] - b[colName]);
|
||||||
|
const percentageRender = val => val ? `${val}%` : '';
|
||||||
|
|
||||||
|
const activityTableProps = {
|
||||||
|
rowKey: 'groupsKey',
|
||||||
|
pagination: { pageSize: 10, showSizeChanger: false },
|
||||||
|
size: 'small',
|
||||||
|
rowClassName: (record, rowIndex) => {
|
||||||
|
return record.groupType === 'operator' ? '': 'ant-tag-blue';
|
||||||
|
},
|
||||||
|
showSorterTooltip: false,
|
||||||
|
columns: [
|
||||||
|
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem',
|
||||||
|
filterSearch: true,
|
||||||
|
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
||||||
|
onFilter: (value, record) => record.groupsKey === value || record.groupType !== 'operator',
|
||||||
|
render: (text, record) => (record.groupType !== 'operator' ? text : <Link to={`/op_risk/sales/${record.groupsKey}`}>{text}</Link>),
|
||||||
|
}, // 维度: 顾问
|
||||||
|
{
|
||||||
|
title: '顾问动作',
|
||||||
|
key: 'date',
|
||||||
|
children: [
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
首次响应率24H{' '}
|
||||||
|
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'firstTouch24', key: 'firstTouch24', render: percentageRender,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'firstTouch24'),
|
||||||
|
},
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
48H内报价率{' '}
|
||||||
|
<Tooltip title="达到48H内报价的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'firstQuote48', key: 'firstQuote48',render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'firstQuote48'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
一次报价率{' '}
|
||||||
|
<Tooltip title="首次报价的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'quote1', key: 'quote1',render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'quote1'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
二次报价率{' '}
|
||||||
|
<Tooltip title="二次报价的订单数/一次报价后回复数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'quote2', key: 'quote2',render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'quote2'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
>50条会话{' '}
|
||||||
|
<Tooltip title=">50条会话的订单数/总订单">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'turnsGT50', key: 'turnsGT50', render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'turnsGT50'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
违规数{' '}
|
||||||
|
<Tooltip title="未遵循24H回复的订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'violations', key: 'violations',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'violations'), },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '客人回复',
|
||||||
|
key: 'department',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
首次回复率24H{' '}
|
||||||
|
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'firstReply24', key: 'firstReply24',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'firstReply24'), },
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
48H内报价回复率{' '}
|
||||||
|
<Tooltip title="48H内报价后的回复数/报价的订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'replyQuote48', key: 'replyQuote48',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'replyQuote48'), },
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
一次报价回复率{' '}
|
||||||
|
<Tooltip title="一次报价后回复率/订单总数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'replyQuote1', key: 'replyQuote1',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'replyQuote1'), },
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
二次报价回复率{' '}
|
||||||
|
<Tooltip title="二次报价后回复数/一次报价后回复数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'replyQuote2', key: 'replyQuote2',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'replyQuote2'), },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const riskTableProps = {
|
||||||
|
rowKey: 'groupsKey',
|
||||||
|
pagination: { pageSize: 10, showSizeChanger: false },
|
||||||
|
size: 'small',
|
||||||
|
rowClassName: (record, rowIndex) => {
|
||||||
|
return record.groupType === 'operator' ? '': 'ant-tag-blue';
|
||||||
|
},
|
||||||
|
showSorterTooltip: false,
|
||||||
|
columns: [
|
||||||
|
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem',
|
||||||
|
filterSearch: true,
|
||||||
|
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
||||||
|
onFilter: (value, record) => record.groupsKey === value || record.groupType !== 'operator',
|
||||||
|
render: (text, record) => (record.groupType !== 'operator' ? text : <Link to={`/op_risk/sales/${record.groupsKey}`}>{text}</Link>),
|
||||||
|
}, // 维度: 顾问
|
||||||
|
{ title: '>24H回复', dataIndex: 'lostTouch24', key: 'lostTouch24',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostTouch24'),
|
||||||
|
},
|
||||||
|
{ title: '首次报价周期>48h', dataIndex: 'lostQuote48', key: 'lostQuote48',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostQuote48'),
|
||||||
|
},
|
||||||
|
{ title: '报价次数<1次', dataIndex: 'lostQuote1', key: 'lostQuote1',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostQuote1'),
|
||||||
|
},
|
||||||
|
{ title: '报价次数<2次', dataIndex: 'lostQuote2', key: 'lostQuote2',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostQuote2'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||||
|
<Col md={24} lg={24} xxl={24}>
|
||||||
|
<SearchForm
|
||||||
|
defaultValue={{
|
||||||
|
initialValue: {
|
||||||
|
...formValues,
|
||||||
|
...SalesCRMDataStore.searchValues,
|
||||||
|
},
|
||||||
|
shows: ['DepartmentList', 'WebCode', 'DateType', 'dates'],
|
||||||
|
fieldProps: {
|
||||||
|
DepartmentList: { show_all: false, mode: 'multiple', col: 5 },
|
||||||
|
WebCode: { show_all: false, mode: 'multiple', col: 5 },
|
||||||
|
dates: { hide_vs: true },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onSubmit={(_err, obj, form, str) => {
|
||||||
|
SalesCRMDataStore.setSearchValues(obj, form);
|
||||||
|
pageRefresh(obj);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<section>
|
||||||
|
<Table {...activityTableProps} bordered dataSource={[...process.dataSource, ...process.details]} loading={process.loading} sticky />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>未成行订单 数量</h3>
|
||||||
|
<Table {...riskTableProps} bordered dataSource={[...process.dataSource, ...process.details]} loading={process.loading} sticky />
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,397 @@
|
|||||||
|
import React, { useContext, useEffect } from 'react';
|
||||||
|
import { NavLink, useParams } from 'react-router-dom';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { stores_Context, DATE_FORMAT, SMALL_DATETIME_FORMAT } from '../../config';
|
||||||
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { Row, Col, Table, Divider, Button, Popover, Tooltip } from 'antd';
|
||||||
|
import { fixTo2Decimals } from '../../utils/commons';
|
||||||
|
import MixFieldsDetail from '../../components/MixFieldsDetail';
|
||||||
|
|
||||||
|
const COLOR_SETS = [
|
||||||
|
'#FFFFFF',
|
||||||
|
// "#5B8FF9",
|
||||||
|
// "#FF6B3B",
|
||||||
|
'#9FB40F',
|
||||||
|
'#76523B',
|
||||||
|
'#DAD5B5',
|
||||||
|
'#E19348',
|
||||||
|
'#F383A2',
|
||||||
|
];
|
||||||
|
const transparentHex = '1A';
|
||||||
|
const percentageRender = (val) => (val ? `${val}%` : '');
|
||||||
|
const numberConvert10K = (number, scale = 10) => {
|
||||||
|
return fixTo2Decimals((number/(1000*scale)));
|
||||||
|
};
|
||||||
|
const retPropsMinRatesSet = { 'CH': 12, 'AH': 8, 'default': 10 }; //
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { opisn, opi_name } = useParams();
|
||||||
|
|
||||||
|
const { sale_store, SalesCRMDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||||
|
|
||||||
|
const { formValues, siderBroken } = searchFormStore;
|
||||||
|
const { searchValues, searchValuesToSub, resetData, results, process, risk } = SalesCRMDataStore;
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Promise.allSettled([
|
||||||
|
// 结果指标
|
||||||
|
SalesCRMDataStore.get90n180Data({ opisn, groupType: 'operator', groupDateType: '' }),
|
||||||
|
// 结果: 全年业绩
|
||||||
|
SalesCRMDataStore.getResultData({
|
||||||
|
opisn,
|
||||||
|
Date1: searchValues.date.clone().startOf('year').format(DATE_FORMAT),
|
||||||
|
Date2: searchValues.date.clone().endOf('year').format(SMALL_DATETIME_FORMAT),
|
||||||
|
groupType: 'overview',
|
||||||
|
// groupType: 'operator',
|
||||||
|
groupDateType: 'month',
|
||||||
|
}),
|
||||||
|
// 过程
|
||||||
|
SalesCRMDataStore.getProcessData({ opisn, groupType: 'operator', groupDateType: '' }),
|
||||||
|
// 违规明细
|
||||||
|
SalesCRMDataStore.getRiskDetailData({ opisn }),
|
||||||
|
]);
|
||||||
|
}, [opisn]);
|
||||||
|
|
||||||
|
const dataFields = (suffix, colRootIndex) => [
|
||||||
|
{
|
||||||
|
key: 'ConfirmRates' + suffix,
|
||||||
|
title: '成行率',
|
||||||
|
dataIndex: [suffix, 'ConfirmRates'],
|
||||||
|
width: '5em',
|
||||||
|
// CH个人成行率<12%, AH个人成行率<8%刷红
|
||||||
|
render: (val, r) => ({
|
||||||
|
props: { style: { color: val < (retPropsMinRatesSet?.[r?.retProps || 'default'] || retPropsMinRatesSet.default) ? 'red' : 'green', backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: val ? `${val}%` : '',
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ConfirmRates - b[suffix].ConfirmRates),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SumML' + suffix,
|
||||||
|
title: '业绩/万',
|
||||||
|
dataIndex: [suffix, 'SumML'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: numberConvert10K(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].SumML - b[suffix].SumML),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ConfirmOrder' + suffix,
|
||||||
|
title: '团数',
|
||||||
|
dataIndex: [suffix, 'ConfirmOrder'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: _,
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ConfirmOrder - b[suffix].ConfirmOrder),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SumOrder' + suffix,
|
||||||
|
title: '订单数',
|
||||||
|
dataIndex: [suffix, 'SumOrder'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: _,
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].SumOrder - b[suffix].SumOrder),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ResumeOrder' + suffix,
|
||||||
|
title: '老客户团数',
|
||||||
|
dataIndex: [suffix, 'ResumeOrder'],
|
||||||
|
width: '5em',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: _,
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ResumeOrder - b[suffix].ResumeOrder),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ResumeRates' + suffix,
|
||||||
|
title: '老客户成行率',
|
||||||
|
dataIndex: [suffix, 'ResumeRates'],
|
||||||
|
width: '5em',
|
||||||
|
render: (val, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[colRootIndex]+transparentHex } },
|
||||||
|
children: val ? `${val}%` : ''
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => (a.groupType === 'overview' ? -1 : b.groupType === 'overview' ? 0 : a[suffix].ResumeRates - b[suffix].ResumeRates),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const dashboardTableProps = {
|
||||||
|
rowKey: 'groupsKey',
|
||||||
|
pagination: false,
|
||||||
|
size: 'small',
|
||||||
|
showSorterTooltip: false,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'groupsLabel',
|
||||||
|
key: 'name',
|
||||||
|
width: '5em',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (<>前90 -30天<br/>{searchValues.date90.Date1} 至 {searchValues.date90.Date2}</>),
|
||||||
|
key: 'date',
|
||||||
|
children: dataFields('result90', 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (<>前180 -50天<br/>{searchValues.date180.Date1} 至 {searchValues.date180.Date2}</>),
|
||||||
|
key: 'department',
|
||||||
|
children: dataFields('result180', 1),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rowClassName: (record, rowIndex) => {
|
||||||
|
return record.groupType === 'overview' ? 'ant-tag-blue' : '';
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const activityTableProps = {
|
||||||
|
rowKey: 'groupsKey',
|
||||||
|
pagination: false,
|
||||||
|
size: 'small',
|
||||||
|
rowClassName: (record, rowIndex) => {
|
||||||
|
return record.groupType === 'operator' ? '' : 'ant-tag-blue';
|
||||||
|
},
|
||||||
|
showSorterTooltip: false,
|
||||||
|
columns: [
|
||||||
|
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem' }, // 维度: 顾问
|
||||||
|
{
|
||||||
|
title: '顾问动作',
|
||||||
|
key: 'date',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
首次响应率24H{' '}
|
||||||
|
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'firstTouch24',
|
||||||
|
key: 'firstTouch24',
|
||||||
|
render: percentageRender,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
48H内报价率{' '}
|
||||||
|
<Tooltip title="达到48H内报价的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'firstQuote48',
|
||||||
|
key: 'firstQuote48',
|
||||||
|
render: percentageRender,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
一次报价率{' '}
|
||||||
|
<Tooltip title="首次报价的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'quote1',
|
||||||
|
key: 'quote1',
|
||||||
|
render: percentageRender,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
二次报价率{' '}
|
||||||
|
<Tooltip title="二次报价的订单数/一次报价后回复数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'quote2',
|
||||||
|
key: 'quote2',
|
||||||
|
render: percentageRender,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
>50条会话{' '}
|
||||||
|
<Tooltip title=">50条会话的订单数/总订单">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'turnsGT50',
|
||||||
|
key: 'turnsGT50',
|
||||||
|
render: percentageRender,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
违规数{' '}
|
||||||
|
<Tooltip title="未遵循24H回复的订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'violations',
|
||||||
|
key: 'violations',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '客人回复',
|
||||||
|
key: 'department',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
首次回复率24H{' '}
|
||||||
|
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'firstReply24',
|
||||||
|
key: 'firstReply24',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
48H内报价回复率{' '}
|
||||||
|
<Tooltip title="48H内报价后的回复数/报价的订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'replyQuote48',
|
||||||
|
key: 'replyQuote48',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
一次报价回复率{' '}
|
||||||
|
<Tooltip title="一次报价后回复率/订单总数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'replyQuote1',
|
||||||
|
key: 'replyQuote1',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
二次报价回复率{' '}
|
||||||
|
<Tooltip title="二次报价后回复数/一次报价后回复数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
dataIndex: 'replyQuote2',
|
||||||
|
key: 'replyQuote2',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1] + transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const riskTableProps = {
|
||||||
|
loading: risk.loading,
|
||||||
|
sticky: true,
|
||||||
|
// scroll: { x: 1000, y: 400 },
|
||||||
|
pagination: false,
|
||||||
|
rowKey: (row) => row.coli_id,
|
||||||
|
columns: [
|
||||||
|
{ title: '客人姓名', dataIndex: 'guest_name', key: 'guest_name', width: '6rem' },
|
||||||
|
{ title: '团号', dataIndex: 'coli_id', key: 'coli_id', width: '6rem' },
|
||||||
|
{
|
||||||
|
title: '表单内容',
|
||||||
|
dataIndex: 'coli_contents',
|
||||||
|
key: 'coli_contents',
|
||||||
|
width: '6rem',
|
||||||
|
render: (text, record) => (
|
||||||
|
<Popover
|
||||||
|
title={`${record.coli_id} ${record.guest_name}`}
|
||||||
|
content={<pre dangerouslySetInnerHTML={{ __html: text }} style={{ whiteSpace: 'pre-wrap', wordWrap: 'break-word' }} />}
|
||||||
|
trigger={['click']}
|
||||||
|
placement="right"
|
||||||
|
overlayStyle={{ width: '500px', maxHeight: '500px' }}
|
||||||
|
autoAdjustOverflow={false}
|
||||||
|
>
|
||||||
|
<Button type="link" size="small">
|
||||||
|
表单内容
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ title: '顾问', dataIndex: 'opi_name', key: 'opi_name', width: '6rem' },
|
||||||
|
{ title: '预定时间', dataIndex: 'coli_applydate', key: 'coli_applydate', width: '6rem' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartsConfig = {
|
||||||
|
colFields: ['ConfirmOrder', 'SumOrder'],
|
||||||
|
lineFields: ['SumML', 'ConfirmRates'],
|
||||||
|
seriesField: null,
|
||||||
|
xField: 'groupDateVal',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||||
|
<Col md={24} lg={12} xxl={14}>
|
||||||
|
<NavLink to={-1}>返回</NavLink>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<section>
|
||||||
|
<h2>结果指标 @ {searchValues.date.format(DATE_FORMAT)}</h2>
|
||||||
|
<Table {...dashboardTableProps} bordered dataSource={results[`operator_${opisn}`]} loading={results.loading} sticky />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>全年每月业绩 @ {searchValues.date.format('YYYY')}</h3>
|
||||||
|
<MixFieldsDetail {...chartsConfig} dataSource={results[`operator_byDate_${opisn}`] || []} />
|
||||||
|
</section>
|
||||||
|
<Divider />
|
||||||
|
<section>
|
||||||
|
<h2>过程指标 @ {searchValuesToSub.Date1} 至 {searchValuesToSub.Date2}</h2>
|
||||||
|
<Table {...activityTableProps} bordered dataSource={process[`operator_${opisn}`]} loading={process.loading} sticky />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>违规明细</h2>
|
||||||
|
<h3>>24H回复</h3>
|
||||||
|
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostTouch24 || []} />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>首次报价周期>48h</h3>
|
||||||
|
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostQuote48 || []} />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>报价次数<1</h3>
|
||||||
|
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostQuote1 || []} />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>报价次数<2</h3>
|
||||||
|
<Table {...riskTableProps} bordered dataSource={risk.byLostType?.lostQuote2 || []} />
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
Loading…
Reference in New Issue