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 = { loading: false, operator: [], dept: [], overview: [] }; // 是否包含门票 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) => {text} }], sorter: (a, b) => parseInt(b.firstReplayRate) - parseInt(a.firstReplayRate), }, { title: '一次报价率', children: [{ title: '', dataIndex: 'FirstQuotationRate', render: (text, record) => {text} }], sorter: (a, b) => parseInt(b.FirstQuotationRate) - parseInt(a.FirstQuotationRate), }, { title: '一次报价回复率', children: [ { title: '', dataIndex: 'FirstQuotationReplayRate', render: (text, record) => {text}, }, ], 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) => {text}, }, ], sorter: (a, b) => parseInt(b.SecondQuotationRate) - parseInt(a.SecondQuotationRate), }, { title: '二次报价回复率', children: [ { title: '', dataIndex: 'SecondQuotationReplayRate', render: (text, record) => {text}, }, ], 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) => {text} }] }, { 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, })); console.log(mergeYearMonth, ';;;;;;;;;;'); 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;