You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dashboard/src/stores/SaleStore.js

580 lines
25 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { makeAutoObservable, runInAction } from 'mobx';
import moment from 'moment';
import * as config from '../config';
import * as comm from '../utils/commons';
import { NavLink } from 'react-router-dom';
import { groupsMappedByCode, dataFieldAlias } from './../libs/ht';
import * as req from '../utils/request';
// 销售数据
class SaleStore {
constructor(rootStore) {
this.rootStore = rootStore;
makeAutoObservable(this);
}
loading = false;
loading_table = false;
spinning = false; // 加载状态
active_tab_key = 'All'; // 当前选择的标签
group_select_mode = false;
groups = [groupsMappedByCode.GH.key]; // 默认GH事业部
filter_country = []; // 筛选国籍
filter_guest_type = []; // 筛选客户关系
site_select_mode = false; // 是否多选站点
include_tickets = '1'; // 是否包含门票1是含有0不含
webcode = 'ALL';
date_type = 'ConfirmDate';
ml_data = []; // 毛利数据
type_data = []; // 类型的数据
type_data_sub = []; // 类型的子维度数据
date_title = 'date_title'; // 日期段,只用于显示,防止日期选择控件的变化导致页面刷新
salesTrade = { groupType: 'dept', loading: false, operator: [], dept: [], overview: [] };
setGroupType(v) {
this.salesTrade.groupType = v;
}
// 是否包含门票
handleChange_include_tickets = (value) => {
this.include_tickets = value;
};
// 选择事业部
group_handleChange(value) {
this.groups = value;
}
// 选择站点
handleChange_webcode = (value) => {
this.webcode = value;
};
// 切换标签页
onChange_Tabs(active_key) {
this.active_tab_key = active_key;
}
// 下单日期或者出发日期
onChange_datetype(value) {
this.date_type = value;
}
// 选择客源国
onChange_Country(value) {
this.filter_country = value;
}
// 选择成员关系
onChange_GuestGroupType(value) {
this.filter_guest_type = value;
}
// 获取业绩信息
get_department_order_ml(date_moment) {
let result = [];
const date1_start = date_moment.start_date.format(config.DATE_FORMAT);
const date1_end = date_moment.end_date.format(config.DATE_FORMAT);
const date2_start = comm.empty(date_moment.start_date_cp) ? '' : date_moment.start_date_cp.format(config.DATE_FORMAT);
const date2_end = comm.empty(date_moment.end_date_cp) ? '' : date_moment.end_date_cp.format(config.DATE_FORMAT);
this.loading = true;
this.date_title = `${date1_start}~${date1_end}`;
let url = '/service-web/QueryData/GetDepartmentOrderML';
url += `?DepartmentList=${this.groups.toString()}&DateType=${this.date_type}&WebCode=${this.webcode}&IncludeTickets=${this.include_tickets}`;
url += `&Date1=${date1_start}&Date2=${date1_end}%2023:59:59`;
if (date2_start && date2_end) {
url += `&OldDate1=${date2_start}&OldDate2=${date2_end}%2023:59:59`;
this.date_title += ` ${date2_start}~${date2_end}`;
}
fetch(config.HT_HOST + url)
.then((response) => response.json())
.then((json) => {
result = json.result1;
// if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) {
// let diff_days = date_moment.start_date.diff(date_moment.start_date_cp, "days");
// for (let item of json.result2) {
// result.push({
// COLI_Date: moment(item.COLI_Date).add(diff_days, "days").format(config.DATE_FORMAT),
// COLI_YJLY: item.COLI_YJLY,
// groups: item.groups,
// key: item.key,
// });
// }
// }
runInAction(() => {
this.ml_data = result;
this.loading = false;
});
})
.catch((error) => {
this.loading = false;
console.log('fetch data failed', error);
});
}
// 按类型获取业绩
get_department_order_ml_by_type(date_moment) {
const result = { dataSource: [], columns: [] };
const date1_start = date_moment.start_date.format(config.DATE_FORMAT);
const date1_end = date_moment.end_date.format(config.DATE_FORMAT);
const date2_start = comm.empty(date_moment.start_date_cp) ? '' : date_moment.start_date_cp.format(config.DATE_FORMAT);
const date2_end = comm.empty(date_moment.end_date_cp) ? '' : date_moment.end_date_cp.format(config.DATE_FORMAT);
this.loading_table = true;
this.date_title = `${date1_start}~${date1_end}`;
let url = '/service-web/QueryData/GetDepartmentOrderMLByType';
url += `?DepartmentList=${this.groups.toString()}&DateType=${this.date_type}&OrderType=${this.active_tab_key}&WebCode=${this.webcode}&IncludeTickets=${this.include_tickets}`;
url += `&Date1=${date1_start}&Date2=${date1_end}%2023:59:59`;
if (date2_start && date2_end) {
url += `&OldDate1=${date2_start}&OldDate2=${date2_end}%2023:59:59`;
this.date_title += ` ${date2_start}~${date2_end}`;
}
fetch(config.HT_HOST + url)
.then((response) => response.json())
.then((json) => {
// if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) {
// eslint-disable-next-line no-constant-condition
if (false) {
} else {
if (this.active_tab_key === 'All') {
result.columns = [
{
title: '',
children: [
{
title: '',
dataIndex: 'OPI_Name',
},
],
},
{
title: '毛利',
children: [{ title: json.result1.reduce((a, b) => a + comm.price_to_number(b.COLI_ML), 0), dataIndex: 'COLI_ML' }],
sorter: (a, b) => comm.price_to_number(b.COLI_ML) - comm.price_to_number(a.COLI_ML),
},
{
title: '成行率',
children: [{ title: comm.formatPercent(json.result1.reduce((a, b) => a + b.COLI_CJCount, 0) / json.result1.reduce((a, b) => a + b.COLI_OrderCount, 0)), dataIndex: 'COLI_CJrate' }],
sorter: (a, b) => parseInt(b.COLI_CJrate) - parseInt(a.COLI_CJrate),
},
{
title: '成团数',
children: [{ title: json.result1.reduce((a, b) => a + b.COLI_CJCount, 0), dataIndex: 'COLI_CJCount' }],
sorter: (a, b) => b.COLI_CJCount - a.COLI_CJCount,
},
{
title: '订单数',
children: [{ title: json.result1.reduce((a, b) => a + b.COLI_OrderCount, 0), dataIndex: 'COLI_OrderCount' }],
sorter: (a, b) => b.COLI_OrderCount - a.COLI_OrderCount,
},
{
title: '订单价值',
children: [{ title: '', dataIndex: 'ordervalue' }],
sorter: (a, b) => b.ordervalue - a.ordervalue,
},
{
title: '单团毛利',
children: [{ title: '', dataIndex: 'COLI_SingleML' }],
sorter: (a, b) => comm.price_to_number(b.COLI_SingleML) - comm.price_to_number(a.COLI_SingleML),
},
{
title: '成团周期',
children: [{ title: '', dataIndex: 'COLI_Cycle' }],
sorter: (a, b) => b.COLI_Cycle - a.COLI_Cycle,
},
];
result.dataSource = json.result1;
} else if (this.active_tab_key === 'ResponseRateByWL') {
result.columns = [
{
title: '',
children: [
{
title: '',
dataIndex: 'OPI_Name',
},
],
},
{
title: '报价次数',
children: [{ title: json.result1.reduce((a, b) => a + b.PriceTime, 0), dataIndex: 'PriceTime' }],
sorter: (a, b) => b.PriceTime - a.PriceTime,
},
{
title: '邮件发送次数',
children: [{ title: json.result1.reduce((a, b) => a + b.mailSendTime, 0), dataIndex: 'mailSendTime' }],
sorter: (a, b) => b.mailSendTime - a.mailSendTime,
},
{
title: 'WhatsApp客人会话次数',
children: [{ title: json.result1.reduce((a, b) => a + b.WhatsAppGuestChatCount, 0), dataIndex: 'WhatsAppGuestChatCount' }],
sorter: (a, b) => b.WhatsAppGuestChatCount - a.WhatsAppGuestChatCount,
},
{
title: 'WhatsApp外联会话次数',
children: [{ title: json.result1.reduce((a, b) => a + b.WhatsAppWLChatCount, 0), dataIndex: 'WhatsAppWLChatCount' }],
sorter: (a, b) => b.WhatsAppWLChatCount - a.WhatsAppWLChatCount,
},
{
title: 'WhatsApp新增客户数',
children: [{ title: json.result1.reduce((a, b) => a + b.WhatsAppNewGuestCount, 0), dataIndex: 'WhatsAppNewGuestCount' }],
sorter: (a, b) => b.WhatsAppNewGuestCount - a.WhatsAppNewGuestCount,
},
{
title: '微信客人会话次数',
children: [{ title: json.result1.reduce((a, b) => a + b.WXGuestChatCount, 0), dataIndex: 'WXGuestChatCount' }],
sorter: (a, b) => b.WXGuestChatCount - a.WXGuestChatCount,
},
{
title: '微信外联会话次数',
children: [{ title: json.result1.reduce((a, b) => a + b.WXWLChatCount, 0), dataIndex: 'WXWLChatCount' }],
sorter: (a, b) => b.WXWLChatCount - a.WXWLChatCount,
},
{
title: '微信新增客户数',
children: [{ title: json.result1.reduce((a, b) => a + b.WXNewGuestCount, 0), dataIndex: 'WXNewGuestCount' }],
sorter: (a, b) => b.WXNewGuestCount - a.WXNewGuestCount,
},
];
result.dataSource = json.result1;
} else if (this.active_tab_key === 'ResponseRateWhatsApp') {
result.columns = [
{
title: '',
children: [
{
title: '',
dataIndex: 'OPI_Name',
},
],
},
{
title: '首次回复率',
children: [{ title: '', dataIndex: 'firstReplayRate', render: (text, record) => <span title={record.firstReplay + '回复数/' + record.OrderCount + '总订单'}>{text}</span> }],
sorter: (a, b) => parseInt(b.firstReplayRate) - parseInt(a.firstReplayRate),
},
{
title: '一次报价率',
children: [{ title: '', dataIndex: 'FirstQuotationRate', render: (text, record) => <span title={record.FirstQuotation + '一次报价数/' + record.OrderCount + '总订单'}>{text}</span> }],
sorter: (a, b) => parseInt(b.FirstQuotationRate) - parseInt(a.FirstQuotationRate),
},
{
title: '一次报价回复率',
children: [
{
title: '',
dataIndex: 'FirstQuotationReplayRate',
render: (text, record) => <span title={record.FirstQuotationReplay + '一次报价回复数/' + record.FirstQuotation + '一次报价数'}>{text}</span>,
},
],
sorter: (a, b) => parseInt(b.FirstQuotationReplayRate) - parseInt(a.FirstQuotationReplayRate),
},
{
title: '一次报价回复周期',
children: [{ title: '', dataIndex: 'FirstQuotationReplaytimeAVG' }],
sorter: (a, b) => b.FirstQuotationReplaytimeAVG - a.FirstQuotationReplaytimeAVG,
},
{
title: '二次报价率',
children: [
{
title: '',
dataIndex: 'SecondQuotationRate',
render: (text, record) => <span title={record.SecondQuotation + '二次报价数/' + record.FirstQuotationReplay + '一次报价回复数'}>{text}</span>,
},
],
sorter: (a, b) => parseInt(b.SecondQuotationRate) - parseInt(a.SecondQuotationRate),
},
{
title: '二次报价回复率',
children: [
{
title: '',
dataIndex: 'SecondQuotationReplayRate',
render: (text, record) => <span title={record.SecondQuotation + '二次报价数/' + record.SecondQuotationReplay + '二次报价回复'}>{text}</span>,
},
],
sorter: (a, b) => parseInt(b.SecondQuotationReplayRate) - parseInt(a.SecondQuotationReplayRate),
},
{
title: '二次报价回复周期',
children: [{ title: '', dataIndex: 'SecondQuotationReplaytimeAVG' }],
sorter: (a, b) => b.SecondQuotationReplaytimeAVG - a.SecondQuotationReplaytimeAVG,
},
{
title: '成团率',
children: [{ title: '', dataIndex: 'COLI_SuccessRate' }],
sorter: (a, b) => parseInt(b.COLI_SuccessRate) - parseInt(a.COLI_SuccessRate),
},
{
title: '成团周期',
children: [{ title: '', dataIndex: 'COLI_ConfirmTimeAVG' }],
sorter: (a, b) => b.COLI_ConfirmTimeAVG - a.COLI_ConfirmTimeAVG,
},
];
result.dataSource = json.result1;
} else {
const diffDateFlagYes = !comm.isEmpty(date_moment.start_date_cp);
// if (this.active_tab_key == "Country")
const mergeDiffData = calcDiff({result1: json.result1, result2: json.result2});
// 获取类型的项目,去掉重复,作为列名
const type_name_arr = [];
mergeDiffData.map((item) => {
type_name_arr[item.SubTypeSN] = { SubTypeSN: item.SubTypeSN, SubTypeName: item.SubTypeName };
return item;
});
const type_data = [];
const type_data_arr = [];
for (const item of mergeDiffData) {
const op_sn = 'OP_' + item.OPI_SN; // 顾问的SN
const items = mergeDiffData.filter((d) => String(d.OPI_SN) === String(item.OPI_SN)); // 筛选出有当前顾问的记录
const total_data_value = items.length ? items.reduce((a, b) => a + b.COLI_YJLY, 0) : ''; // 记录累加
const total_data_value_diff = items.length ? items.reduce((a, b) => a + b.COLI_YJLY2, 0) : ''; // 记录累加
if (comm.empty(type_data[op_sn])) {
type_data[op_sn] = [{ key: item.OPI_SN }, { T_name: item.OPI_Name }, { T_total: total_data_value }, { V_total: total_data_value_diff }];
}
const _diff = comm.objectMapper(comm.pick(item, ['COLI_YJLY_diff', 'COLI_YJLY_vs', 'COLI_YJLY2']), {
COLI_YJLY2: { key: `v_${item.SubTypeSN}` },
COLI_YJLY_diff: { key: `diff_${item.SubTypeSN}` },
COLI_YJLY_vs: { key: `vs_${item.SubTypeSN}` },
});
type_data[op_sn].push({
['T_' + item.SubTypeSN]: diffDateFlagYes
? comm.show_vs_tag(_diff[`vs_${item.SubTypeSN}`]+'%', _diff[`diff_${item.SubTypeSN}`], item.COLI_YJLY, _diff[`v_${item.SubTypeSN}`])
: item.COLI_YJLY,
..._diff,
[`TV_${item.SubTypeSN}`]: item.COLI_YJLY,
});
}
Object.values(type_data).map((item) => {
type_data_arr.push(
Object.assign(
...item.map((it) => {
return it;
})
)
);
return item;
});
const totalDiff = {
diffVal: type_data_arr.reduce((a, b) => a + b.V_total, 0),
val: type_data_arr.reduce((a, b) => a + b.T_total, 0),
};
totalDiff.diff = (totalDiff.val-totalDiff.diffVal);
totalDiff.vs = comm.fixTo2Decimals(((totalDiff.val-totalDiff.diffVal)/totalDiff.diffVal)*100)+'%';
result.columns.push(
{ title: '顾问', children: [{ title: '', dataIndex: 'T_name', render: (text, record) => <NavLink to={`/sale_sub/${this.active_tab_key}`}>{text}</NavLink> }] },
{
title: '合计',
children: [
{
title: diffDateFlagYes
? comm.show_vs_tag(totalDiff.vs, totalDiff.diff, totalDiff.val, totalDiff.diffVal)
: type_data_arr.reduce((a, b) => a + b.T_total, 0),
dataIndex: 'T_total',
},
],
sorter: (a, b) => b.T_total - a.T_total,
}
);
Object.values(type_name_arr).map((item, index) => {
const data_index = 'T_' + item.SubTypeSN;
const items = type_data_arr.filter((d) => d[data_index]); // 筛选出有对应类型的记录
const total_data_value = items.length ? items.reduce((a, b) => a + b[`TV_${item.SubTypeSN}`], 0) : ''; // 记录累加
const total_data_value_diff = items.length ? items.reduce((a, b) => a + b[`v_${item.SubTypeSN}`], 0) : 0; // 记录累加
const columnDiff = {
val: total_data_value_diff,
diff: total_data_value - total_data_value_diff,
vs: comm.fixTo2Decimals(((total_data_value-total_data_value_diff)/total_data_value_diff)*100)+'%',
};
result.columns.push({
title: item.SubTypeName,
children: [{ title: diffDateFlagYes ? comm.show_vs_tag(columnDiff.vs, columnDiff.diff, total_data_value, total_data_value_diff) : total_data_value, dataIndex: data_index }],
sorter: (a, b) => b[`TV_${item.SubTypeSN}`] - a[`TV_${item.SubTypeSN}`],
});
return item;
});
result.dataSource = type_data_arr;
}
}
runInAction(() => {
this.type_data = result;
this.loading_table = false;
});
})
.catch((error) => {
this.loading_table = false;
console.log('fetch data failed', error);
});
}
// 子维度查询
get_department_order_ml_by_type_sub(date_moment, type_sub) {
this.spinning = true;
const result = { dataSource: [], columns: [] };
const date1_start = date_moment.start_date.format(config.DATE_FORMAT);
const date1_end = date_moment.end_date.format(config.DATE_FORMAT);
const date2_start = comm.empty(date_moment.start_date_cp) ? '' : date_moment.start_date_cp.format(config.DATE_FORMAT);
const date2_end = comm.empty(date_moment.end_date_cp) ? '' : date_moment.end_date_cp.format(config.DATE_FORMAT);
let url = '/service-web/QueryData/GetDepartmentOrderMLByType_sub';
url += `?DepartmentList=${this.groups.toString()}&DateType=${this.date_type}&subType=${type_sub}&subTypeVal=-1&WebCode=${this.webcode}&IncludeTickets=${this.include_tickets}`;
url += `&Date1=${date1_start}&Date2=${date1_end}%2023:59:59`;
if (date2_start && date2_end) {
url += `&OldDate1=${date2_start}&OldDate2=${date2_end}%2023:59:59`;
this.date_title += ` ${date2_start}~${date2_end}`;
}
fetch(config.HT_HOST + url)
.then((response) => response.json())
.then((json) => {
// if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) {
// eslint-disable-next-line no-constant-condition
if (false) {
} else {
result.columns = [
{
title: '',
dataIndex: 'OPI_Name',
},
{
title: '毛利',
dataIndex: 'COLI_ML',
sorter: (a, b) => b.COLI_ML - a.COLI_ML,
},
{
title: '成行率',
dataIndex: 'COLI_CJrate',
sorter: (a, b) => parseFloat(b.COLI_CJrate) - parseFloat(a.COLI_CJrate),
},
{
title: '成团数',
dataIndex: 'COLI_CJCount',
},
{
title: '订单数',
dataIndex: 'COLI_OrderCount',
sorter: (a, b) => b.COLI_OrderCount - a.COLI_OrderCount,
},
{
title: '订单价值',
dataIndex: 'ordervalue',
sorter: (a, b) => b.ordervalue - a.ordervalue,
},
{
title: '单团毛利',
dataIndex: 'COLI_SingleML',
},
{
title: '成团周期',
dataIndex: 'COLI_Cycle',
},
];
// 数据处理,把相同类型放入同一个数组
const type_data = [];
!comm.empty(json.result1) &&
Object.values(json.result1).map((item) => {
const subtype_sn = 'type_' + item.subType;
if (comm.empty(type_data[subtype_sn])) {
type_data[subtype_sn] = { subType: item.subType, subType_name: item.subTypeVal, data: [item] };
} else {
type_data[subtype_sn].data.push(item);
}
return item;
});
result.dataSource = Object.values(type_data);
}
runInAction(() => {
this.type_data_sub = result;
this.spinning = false;
});
})
.catch((error) => {
this.spinning = false;
console.log('fetch data failed', error);
});
}
async fetchOperatorTradeData(groupType, queryData) {
this.salesTrade.loading = true;
const param1 = Object.assign(queryData, {groupType, groupDateType: 'year' });
const yearData = await this.fetchTradeData(param1);
const yData = parseSaleData(yearData, ['groupsKey', 'groupDateType']);
const param2 = Object.assign(queryData, {groupType, groupDateType: 'month' });
const monthData = await this.fetchTradeData(param2);
const mData = parseSaleData(monthData, ['groupsKey', 'groupDateType']);
const mergeYearMonth = Object.keys(yData).map(ykey => ({
...yData[ykey],
mData: mData[ykey].data,
yData: Object.values(yData[ykey].data)[0],
data: undefined,
yearML: Object.values(yData[ykey].data)[0]?.SumML || 0, // 整理排序用
})).sort(comm.sortBy('yearML')).reverse();
runInAction(() => {
this.salesTrade.loading = false;
this.salesTrade[groupType] = mergeYearMonth;
});
}
/**
* 获取业绩数据
*/
async fetchTradeData(queryData) {
const json = await req.fetchJSON('/service-Analyse2/GetTradeProcess', queryData);
if (json.errcode === 0) {
return json.result1;
}
return [];
}
}
const calcDiff = ({ result1 , result2 }) => {
const initialDataWithAllKeys = comm.unique([].concat(result1 , result2).map((ele) => `${ele.OPI_SN}@${ele.SubTypeSN}`)).reduce((r, v) => {
const [OPI_SN, SubTypeSN] = String(v).split('@');
r.push({OPI_SN, SubTypeSN, COLI_YJLY: 0, key: `${OPI_SN}@${SubTypeSN}` });
return r;
}, []);
const initialMapped = initialDataWithAllKeys.reduce((r, v) => ({ ...r, [v.key]: v }), {});
const result1Mapped = result1.reduce((r, v) => ({ ...r, [`${v.OPI_SN}@${v.SubTypeSN}`]: v }), comm.cloneDeep(initialMapped));
const result2Mapped = result2.reduce((r, v) => ({ ...r, [`${v.OPI_SN}@${v.SubTypeSN}`]: v }), comm.cloneDeep(initialMapped));
const afterCalc = initialDataWithAllKeys.map((row) => {
const diff = {
COLI_YJLY2: result2Mapped?.[row.key]?.COLI_YJLY || 0,
COLI_YJLY_vs: result2Mapped?.[row.key]?.COLI_YJLY ? comm.fixTo2Decimals(((result1Mapped[row.key].COLI_YJLY - result2Mapped[row.key].COLI_YJLY) / result2Mapped[row.key].COLI_YJLY) * 100) : 0,
COLI_YJLY_diff: result1Mapped[row.key].COLI_YJLY - result2Mapped[row.key].COLI_YJLY,
};
const extField = comm.pick(result2Mapped[row.key], ['OPI_Name', 'SubTypeName', 'groups']);
return { ...result1Mapped[row.key], ...extField, ...diff };
});
return afterCalc;
};
const parseSaleData = (res, keyArr = []) => {
const result = res.map((row) => ({
...row,
yearIndex: moment(row.start_date).year(),
}));
const byYear = comm.groupBy(result, (ele) => ele.yearIndex);
let ret = {};
const [key0, key1] = keyArr;
Object.keys(byYear).map((_yearVal) => {
const _subjectRet = comm.groupBy(byYear[_yearVal], (ele) => `${ele[key0]}`);
const afterGroup = Object.keys(_subjectRet).reduce((r, oID) => {
const isYear = _subjectRet[oID][0].groupDateType === 'year';
const data = (_subjectRet[oID] || []).map((ele) => ({ ...ele, 'dateVal': isYear ? ele.groupDateVal : moment(ele.groupDateVal).format('MM') }));
// const mappedData = comm.groupBy(data, ele => `${ele.groupDateType}_${ele.dateVal}`);
const mappedData = data.reduce((rd, ele) => ({...rd, [`${ele.groupDateType}_${ele.dateVal}`]: ele}), {});
const row = comm.pick(_subjectRet[oID][0], ['groupDateType', 'groupType', 'groupsKey', 'groupsLabel']);
// return {...row, data};
return { ...r, [oID]: { ...row, data: mappedData } };
}, {});
ret = afterGroup;
return afterGroup;
});
return ret;
};
export default SaleStore;