Merge branch 'main' of github.com:hainatravel/dashboard

feature/2.0-sales-trade
YCC 2 years ago
commit 564ab5e67c

@ -1,6 +1,12 @@
import { Tag } from 'antd'; import { Tag } from 'antd';
import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons'; import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
/**
* @property diffPercent
* @property diffData
* @property data1
* @property data2
*/
export const VSTag = (props) => { export const VSTag = (props) => {
const { diffPercent, diffData, data1, data2 } = props; const { diffPercent, diffData, data1, data2 } = props;
const CaretIcon = parseInt(diffPercent) < 0 ? CaretDownOutlined : CaretUpOutlined; const CaretIcon = parseInt(diffPercent) < 0 ? CaretDownOutlined : CaretUpOutlined;
@ -9,11 +15,11 @@ export const VSTag = (props) => {
'-' '-'
) : ( ) : (
<span> <span>
<div> {/* <div>
{data1} vs {data2} {data1} vs {data2}
</div> </div> */}
<Tag icon={<CaretIcon />} color={tagColor}> <Tag icon={<CaretIcon />} color={tagColor}>
{diffPercent} {diffData} {diffPercent}<span>%</span>{' '}<span>{diffData}</span>
</Tag> </Tag>
</span> </span>
); );

@ -148,7 +148,7 @@ export default observer((props) => {
}))(dataSource?.[0] || {}); }))(dataSource?.[0] || {});
tableData.unshift(yearRow); tableData.unshift(yearRow);
console.log('sub', tableData, delKpiIds); console.log('sub', tableData, delKpiIds);
// return false; // debug: // return false; // debug:0
KPIStore.onSubmit(tableData, {delQueue: delKpiIds}).then((res) => { KPIStore.onSubmit(tableData, {delQueue: delKpiIds}).then((res) => {
if (res) { if (res) {
message.success('保存成功'); message.success('保存成功');

@ -156,7 +156,7 @@ export default observer((props) => {
return r.concat(allMonth, yearRow); return r.concat(allMonth, yearRow);
}, []); }, []);
// console.log('sub', tableData, 'del:', delKpiIds); // console.log('sub', tableData, 'del:', delKpiIds);
// return false; // debug: // return false; // debug:0
KPIStore.onSubmit(tableData, { delQueue: delKpiIds }).then((res) => { KPIStore.onSubmit(tableData, { delQueue: delKpiIds }).then((res) => {
if (res) { if (res) {
message.success('保存成功'); message.success('保存成功');

@ -156,7 +156,7 @@ export default observer((props) => {
return r.concat(allMonth, yearRow); return r.concat(allMonth, yearRow);
}, []); }, []);
// console.log('sub', tableData, 'del:', delKpiIds); // console.log('sub', tableData, 'del:', delKpiIds);
// return false; // debug: // return false; // debug:0
KPIStore.onSubmit(tableData, { delQueue: delKpiIds }).then((res) => { KPIStore.onSubmit(tableData, { delQueue: delKpiIds }).then((res) => {
if (res) { if (res) {
message.success('保存成功'); message.success('保存成功');

@ -155,7 +155,7 @@ export default observer((props) => {
return r.concat(allMonth, yearRow); return r.concat(allMonth, yearRow);
}, []); }, []);
// console.log('sub', tableData, 'del:', delKpiIds); // console.log('sub', tableData, 'del:', delKpiIds);
// return false; // debug: // return false; // debug:0
KPIStore.onSubmit(tableData, { delQueue: delKpiIds }).then((res) => { KPIStore.onSubmit(tableData, { delQueue: delKpiIds }).then((res) => {
if (res) { if (res) {
message.success('保存成功'); message.success('保存成功');

@ -142,7 +142,7 @@ export const KPIObjects = [
{ {
key: 'guestgrouptype', key: 'guestgrouptype',
value: 'guestgrouptype', value: 'guestgrouptype',
label: '成员关系', label: '客群类别',
data: [ data: [
{ key: '146001', value: '146001', label: '夫妻' }, { key: '146001', value: '146001', label: '夫妻' },
{ key: '146002', value: '146002', label: '家庭' }, { key: '146002', value: '146002', label: '家庭' },

@ -1,6 +1,8 @@
import { makeAutoObservable, runInAction, toJS } from 'mobx'; import { makeAutoObservable, runInAction, toJS } from 'mobx';
import * as req from '../utils/request'; import * as req from '../utils/request';
import { isEmpty, pick, sortBy } from '../utils/commons'; import { DATE_FORMAT } from './../config';
import moment from 'moment';
import { isEmpty, pick, sortBy, fixTo2Decimals, cloneDeep, unique } from '../utils/commons';
const modelMapper = { const modelMapper = {
'tourDays': { url: '/service-Analyse2/GetTradeApartByTourDays' }, 'tourDays': { url: '/service-Analyse2/GetTradeApartByTourDays' },
@ -13,7 +15,7 @@ const modelMapper = {
'destinationCountry': { url: '/service-Analyse2/GetTradeApartByDestinationCountry' }, 'destinationCountry': { url: '/service-Analyse2/GetTradeApartByDestinationCountry' },
}; };
class Distribution { class Distribution {
constructor(appStore){ constructor(appStore) {
this.appStore = appStore; this.appStore = appStore;
makeAutoObservable(this); makeAutoObservable(this);
} }
@ -25,13 +27,26 @@ class Distribution {
this.pageLoading = true; this.pageLoading = true;
const mkey = this.curTab; const mkey = this.curTab;
this[mkey] = { loading: true, dataSource: [] }; this[mkey] = { loading: true, dataSource: [] };
param.operator = param?.operator || -1;
if (isEmpty(param.Date1) || isEmpty(param.Date2)) {
return false;
}
// 环比的参数: 计算上一个时间段
const [DateToQ1, DateToQ2] = [moment(param.Date1).subtract(moment(param.Date2).diff(param.Date1, 'days') + 1, 'days'), moment(param.Date1).subtract(1, 'days')];
// 同比的参数: 去年同期
const [DateToY1, DateToY2] = [moment(param.Date1).subtract(1, 'year'), moment(param.Date2).subtract(1, 'year')];
param.DateToY1 = DateToY1.format(DATE_FORMAT);
param.DateToY2 = DateToY2.format(`${DATE_FORMAT} 23:59:59`);
param.DateToQ1 = DateToQ1.format(DATE_FORMAT);
param.DateToQ2 = DateToQ2.format(`${DATE_FORMAT} 23:59:59`);
const json = await req.fetchJSON(modelMapper[mkey].url, param); const json = await req.fetchJSON(modelMapper[mkey].url, param);
if (json.errcode === 0) { if (json.errcode === 0) {
runInAction(() => { runInAction(() => {
const dataLength = json.result.length; const dataLength = json.result.length;
this[mkey].loading = false; this[mkey].loading = false;
this[mkey].originData = json.result; this[mkey].originData = json.result;
this[mkey].dataSource = dataLength > 20 ? json.result.slice(0, 30) : json.result; const pickResult = dataLength > 20 ? json.result.slice(0, 30) : json.result;
this[mkey].dataSource = calcDiff({ result: pickResult, resultToY: json.resultToY, resultToQ: json.resultToQ });
this.pageLoading = false; this.pageLoading = false;
}); });
} }
@ -48,7 +63,7 @@ class Distribution {
runInAction(() => { runInAction(() => {
this.detailData.loading = false; this.detailData.loading = false;
this.detailData.dataSource = json.result; this.detailData.dataSource = json.result;
const daysData = json.result.filter(ele => ele.confirmDays).map(row => pick(row, ['o_id', 'tourdays', 'applyDays', 'personNum', 'country', 'startDate'])); const daysData = json.result.filter((ele) => ele.confirmDays).map((row) => pick(row, ['o_id', 'tourdays', 'applyDays', 'personNum', 'country', 'startDate']));
this.scatterDays = daysData; this.scatterDays = daysData;
}); });
} }
@ -76,7 +91,7 @@ class Distribution {
pageLoading = false; pageLoading = false;
detailData = { loading: false, dataSource: [], }; detailData = { loading: false, dataSource: [] };
scatterDays = []; scatterDays = [];
tourDays = { loading: false, dataSource: [] }; tourDays = { loading: false, dataSource: [] };
@ -88,4 +103,47 @@ class Distribution {
GlobalDestination = { loading: false, dataSource: [] }; GlobalDestination = { loading: false, dataSource: [] };
destinationCountry = { loading: false, dataSource: [] }; destinationCountry = { loading: false, dataSource: [] };
} }
/**
* 计算 同比, 环比
*/
const calcDiff = ({ result, resultToY, resultToQ }) => {
if (isEmpty(resultToY) || isEmpty(resultToQ)) {
// return result;
}
const initialDataWithAllKeys = unique([].concat(result, resultToY, resultToQ).map((ele) => `${ele.key}@${ele.label}`)).reduce((r, v) => {
const [key, label] = String(v).split('@');
r.push({key: Number(key), label, SumML: 0, ConfirmOrder: 0, SumOrder: 0, SumMLPercent: 0, ConfirmOrderPercent: 0, SumOrderPercent: 0});
return r;
}, []).sort(sortBy('key'));
const initialMapped = initialDataWithAllKeys.reduce((r, v) => ({ ...r, [v.key]: v }), {});
const resultMapped = result.reduce((r, v) => ({ ...r, [v.key]: v }), cloneDeep(initialMapped));
const resultToYMapped = resultToY.reduce((r, v) => ({ ...r, [v.key]: v }), cloneDeep(initialMapped));
const resultToQMapped = resultToQ.reduce((r, v) => ({ ...r, [v.key]: v }), cloneDeep(initialMapped));
const afterCalc = initialDataWithAllKeys.map((row) => {
const diff = {
SumMLY: resultToYMapped?.[row.key]?.SumML || 0,
SumMLToY: resultToYMapped?.[row.key]?.SumML ? fixTo2Decimals(((resultMapped[row.key].SumML - resultToYMapped[row.key].SumML) / resultToYMapped[row.key].SumML) * 100) : 0,
SumMLQ: resultToQMapped?.[row.key]?.SumML || 0,
SumMLToQ: resultToQMapped?.[row.key]?.SumML ? fixTo2Decimals(((resultMapped[row.key].SumML - resultToQMapped[row.key].SumML) / resultToQMapped[row.key].SumML) * 100) : 0,
SumMLDiffY: resultMapped[row.key].SumML - resultToYMapped[row.key].SumML,
SumMLDiffQ: resultMapped[row.key].SumML - resultToQMapped[row.key].SumML,
ConfirmOrderY: resultToYMapped?.[row.key]?.ConfirmOrder || 0,
ConfirmOrderToY: resultToYMapped?.[row.key]?.ConfirmOrder
? fixTo2Decimals(((resultMapped[row.key].ConfirmOrder - resultToYMapped[row.key].ConfirmOrder) / resultToYMapped[row.key].ConfirmOrder) * 100)
: 0,
ConfirmOrderQ: resultToQMapped?.[row.key]?.ConfirmOrder || 0,
ConfirmOrderToQ: resultToQMapped?.[row.key]?.ConfirmOrder
? fixTo2Decimals(((resultMapped[row.key].ConfirmOrder - resultToQMapped[row.key].ConfirmOrder) / resultToQMapped[row.key].ConfirmOrder) * 100)
: 0,
ConfirmOrderDiffY: resultMapped[row.key].ConfirmOrder - resultToYMapped[row.key].ConfirmOrder,
ConfirmOrderDiffQ: resultMapped[row.key].ConfirmOrder - resultToQMapped[row.key].ConfirmOrder,
};
return { ...resultMapped[row.key], ...diff };
});
// console.log(afterCalc, '==================');
return afterCalc;
};
export default Distribution; export default Distribution;

@ -3,7 +3,7 @@ import moment from 'moment';
import * as config from '../config'; import * as config from '../config';
import * as comm from '../utils/commons'; import * as comm from '../utils/commons';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { groupsMappedByCode } from './../libs/ht'; import { groupsMappedByCode, dataFieldAlias } from './../libs/ht';
// 销售数据 // 销售数据
class SaleStore { class SaleStore {
@ -125,7 +125,9 @@ class SaleStore {
fetch(config.HT_HOST + url) fetch(config.HT_HOST + url)
.then((response) => response.json()) .then((response) => response.json())
.then((json) => { .then((json) => {
if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) { // if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) {
// eslint-disable-next-line no-constant-condition
if (false) {
} else { } else {
if (this.active_tab_key === 'All') { if (this.active_tab_key === 'All') {
result.columns = [ result.columns = [
@ -306,10 +308,12 @@ class SaleStore {
]; ];
result.dataSource = json.result1; result.dataSource = json.result1;
} else { } else {
const diffDateFlagYes = !comm.isEmpty(date_moment.start_date_cp);
// if (this.active_tab_key == "Country") // if (this.active_tab_key == "Country")
const mergeDiffData = calcDiff({result1: json.result1, result2: json.result2});
// 获取类型的项目,去掉重复,作为列名 // 获取类型的项目,去掉重复,作为列名
const type_name_arr = []; const type_name_arr = [];
json.result1.map((item) => { mergeDiffData.map((item) => {
type_name_arr[item.SubTypeSN] = { SubTypeSN: item.SubTypeSN, SubTypeName: item.SubTypeName }; type_name_arr[item.SubTypeSN] = { SubTypeSN: item.SubTypeSN, SubTypeName: item.SubTypeName };
return item; return item;
}); });
@ -317,14 +321,26 @@ class SaleStore {
const type_data = []; const type_data = [];
const type_data_arr = []; const type_data_arr = [];
for (const item of json.result1) { for (const item of mergeDiffData) {
const op_sn = 'OP_' + item.OPI_SN; // 顾问的SN const op_sn = 'OP_' + item.OPI_SN; // 顾问的SN
const items = json.result1.filter((d) => String(d.OPI_SN) === String(item.OPI_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 = 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])) { 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 }]; type_data[op_sn] = [{ key: item.OPI_SN }, { T_name: item.OPI_Name }, { T_total: total_data_value }, { V_total: total_data_value_diff }];
} }
type_data[op_sn].push({ ['T_' + item.SubTypeSN]: item.COLI_YJLY }); 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) => { Object.values(type_data).map((item) => {
type_data_arr.push( type_data_arr.push(
@ -336,18 +352,41 @@ class SaleStore {
); );
return item; 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( 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: '', dataIndex: 'T_name', render: (text, record) => <NavLink to={`/sale_sub/${this.active_tab_key}`}>{text}</NavLink> }] },
{ title: '合计', children: [{ title: type_data_arr.reduce((a, b) => a + b.T_total, 0), dataIndex: 'T_total' }], sorter: (a, b) => b.T_total - a.T_total } {
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) => { Object.values(type_name_arr).map((item, index) => {
const data_index = 'T_' + item.SubTypeSN; const data_index = 'T_' + item.SubTypeSN;
const items = type_data_arr.filter((d) => d[data_index]); // 筛选出有对应类型的记录 const items = type_data_arr.filter((d) => d[data_index]); // 筛选出有对应类型的记录
const total_data_value = items.length ? items.reduce((a, b) => a + b[data_index], 0) : ''; // 记录累加 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({ result.columns.push({
title: item.SubTypeName, title: item.SubTypeName,
children: [{ title: total_data_value, dataIndex: data_index }], 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[data_index] - a[data_index], sorter: (a, b) => b[`TV_${item.SubTypeSN}`] - a[`TV_${item.SubTypeSN}`],
}); });
return item; return item;
}); });
@ -383,7 +422,9 @@ class SaleStore {
fetch(config.HT_HOST + url) fetch(config.HT_HOST + url)
.then((response) => response.json()) .then((response) => response.json())
.then((json) => { .then((json) => {
if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) { // if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) {
// eslint-disable-next-line no-constant-condition
if (false) {
} else { } else {
result.columns = [ result.columns = [
{ {
@ -450,4 +491,25 @@ class SaleStore {
} }
} }
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;
};
export default SaleStore; export default SaleStore;

@ -1,6 +1,6 @@
import { makeAutoObservable, runInAction, toJS } from 'mobx'; import { makeAutoObservable, runInAction, toJS } from 'mobx';
import * as req from '../utils/request'; import * as req from '../utils/request';
import { isEmpty, sortBy, pick } from '../utils/commons'; import { isEmpty, sortBy, pick, merge, fixTo2Decimals } from '../utils/commons';
import { dataFieldAlias } from './../libs/ht'; import { dataFieldAlias } from './../libs/ht';
class Trade { class Trade {
@ -144,6 +144,7 @@ class Trade {
runInAction(() => { runInAction(() => {
this.topData[orderType].loading = false; this.topData[orderType].loading = false;
this.topData[orderType].dataSource = json.result1; this.topData[orderType].dataSource = json.result1;
this.getTargetsRes(orderType);
}); });
} }
}); });
@ -163,7 +164,26 @@ class Trade {
/** /**
* 从结果中取出目标客户的数据 * 从结果中取出目标客户的数据
*/ */
getTargetsRes = () => {}; getTargetsRes = (orderType) => {
if ( ! ['GuestGroupType', 'country'].includes(orderType)) {
return false;
}
const countryResMapped = (this.topData[orderType]?.dataSource || []).reduce((a, c) => ({ ...a, [String(c.groupsKey)]: c }), {});
const guestResMapped = (this.topData[orderType]?.dataSource || []).reduce((a, c) => ({ ...a, [String(c.groupsKey)]: c }), {});
const targetCountry = merge(this.targetData.targetCountry, pick(countryResMapped, ['3','5','7','18'])); // 美, 加, 英, 澳
const targetGuest = merge(this.targetData.targetGuest, pick(guestResMapped, ['146001', '146002'])); // 家庭, 夫妻
this.targetData.targetCountry = targetCountry;
this.targetData.targetGuest = targetGuest;
const totalArr = [].concat(Object.values(targetCountry), Object.values(targetGuest));
const targetTotal = ['ConfirmOrder', 'SumOrder', 'SumML'].reduce((r, skey) => ({...r, [skey]: totalArr.reduce((a, c) => a+(c[skey]), 0)}), {});
targetTotal.ConfirmRates = fixTo2Decimals((targetTotal.ConfirmOrder/targetTotal.SumOrder)*100);
targetTotal.groupsLabel = '总数';
targetTotal.groupsKey = 'targetTotal';
const targetData = { targetCountry, targetGuest, targetTotal };
const finalTargetData = merge(this.targetData, targetData);
this.targetData.targetTotal = targetTotal;
this.targetTableProps.dataSource = [].concat(Object.values(finalTargetData.targetGuest), Object.values(finalTargetData.targetCountry)); // [finalTargetData.targetTotal], // todo: 总数是重复的
};
setStateSearch(body) { setStateSearch(body) {
this.searchPayloadHome = body; this.searchPayloadHome = body;
@ -174,13 +194,21 @@ class Trade {
this.timeLineKey = v; this.timeLineKey = v;
} }
searchPayloadHome = {};
summaryData = { loading: false, dataSource: [], kpi: {}, }; summaryData = { loading: false, dataSource: [], kpi: {}, };
timeData = { loading: false, dataSource: [] }; timeData = { loading: false, dataSource: [] };
BuData = { loading: false, dataSource: [] }; BuData = { loading: false, dataSource: [] };
sideData = { loading: false, dataSource: {}, monthData: [] }; sideData = { loading: false, dataSource: {}, monthData: [] };
dataForSort = {}; dataForSort = {};
topData = {}; topData = {};
searchPayloadHome = {}; targetData = { targetTotal: {}, targetCountry: {}, targetGuest: {} };
targetTableProps = { loading: false, columns: [
{key: 'groupsLabel', title: '', dataIndex: 'groupsLabel', },
{key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder'},
{key: 'ConfirmOrder', title: '成行数', dataIndex: 'ConfirmOrder'},
{key: 'ConfirmRates', title: '成行率', dataIndex: 'ConfirmRates'},
{key: 'SumML', title: '毛利', dataIndex: 'SumML', render: (v) => dataFieldAlias.SumML.formatter(v)},
], dataSource: [] };
} }
export default Trade; export default Trade;

@ -1,14 +1,17 @@
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { stores_Context } from '../config'; import { stores_Context } from '../config';
import { Row, Col, Spin, Tabs, Table } from 'antd'; import { Row, Col, Spin, Tabs, Table, Space, Typography } from 'antd';
import { RingProgress } from '@ant-design/plots'; import { RingProgress } from '@ant-design/plots';
import SearchForm from './../components/search/SearchForm'; import SearchForm from './../components/search/SearchForm';
import { empty } from '../utils/commons'; import { empty } from '../utils/commons';
import { dataFieldAlias } from '../libs/ht'; import { dataFieldAlias } from '../libs/ht';
import { VSTag } from './../components/Data';
import './kpi.css'; import './kpi.css';
const { Text } = Typography;
const apartOptions = [ const apartOptions = [
{ key: 'tourDays', value: 'tourDays', label: '团天数' }, { key: 'tourDays', value: 'tourDays', label: '团天数' },
{ key: 'PML', value: 'PML', label: '单团毛利' }, { key: 'PML', value: 'PML', label: '单团毛利' },
@ -53,10 +56,62 @@ export default observer(() => {
}; };
const columns = [ const columns = [
{ title: '', dataIndex: 'label' }, { title: '', dataIndex: 'label' },
{ title: '团数', dataIndex: 'ConfirmOrder' }, {
{ title: '业绩', dataIndex: 'SumML', render: (v) => dataFieldAlias.SumML.formatter(v) }, title: '团数',
{ title: '团数占比', dataIndex: 'ConfirmOrderPercent', render: (v) => <RingProgress {...RingProgressConfig} percent={v / 100} /> }, dataIndex: 'ConfirmOrder',
{ title: '业绩占比', dataIndex: 'SumMLPercent', render: (v) => <RingProgress {...RingProgressConfig} percent={v / 100} /> }, render: (v, r) => (
<>
<Row align={'middle'}>
<Col flex="1 1 100px">
<Text strong>{v}</Text>
</Col>
<Col flex={'auto'}>
<Space direction={'vertical'}>
<span>
<span>同比: </span> <VSTag diffPercent={r.ConfirmOrderToY} diffData={r.ConfirmOrderDiffY} />
</span>
<span>
<span>环比: </span> <VSTag diffPercent={r.ConfirmOrderToQ} diffData={r.ConfirmOrderDiffQ} />
</span>
</Space>
</Col>
</Row>
</>
),
},
{
title: '业绩',
dataIndex: 'SumML',
render: (v, r) => (
<>
<Row align={'middle'}>
<Col flex="1 1 100px">
<Text strong>{dataFieldAlias.SumML.formatter(v)}</Text>
</Col>
<Col flex={'auto'}>
<Space direction={'vertical'}>
<span>
<span>同比: </span> <VSTag diffPercent={r.SumMLToY} diffData={dataFieldAlias.SumML.formatter(r.SumMLDiffY)} />
</span>
<span>
<span>环比: </span> <VSTag diffPercent={r.SumMLToQ} diffData={dataFieldAlias.SumML.formatter(r.SumMLDiffQ)} />
</span>
</Space>
</Col>
</Row>
</>
),
},
{
title: '团数占比',
dataIndex: 'ConfirmOrderPercent',
render: (v, r) => <RingProgress {...RingProgressConfig} percent={v / 100} />,
},
{
title: '业绩占比',
dataIndex: 'SumMLPercent',
render: (v, r) => <RingProgress {...RingProgressConfig} percent={v / 100} />,
},
]; ];
return ( return (
<> <>
@ -67,7 +122,7 @@ export default observer(() => {
initialValue: { initialValue: {
...formValues, ...formValues,
}, },
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates'], shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates', 'operator'],
fieldProps: { fieldProps: {
DepartmentList: { show_all: true }, DepartmentList: { show_all: true },
WebCode: { show_all: true }, WebCode: { show_all: true },

@ -1,6 +1,6 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Row, Col, Spin, Space, Radio } from 'antd'; import { Row, Col, Spin, Space, Radio, Table } from 'antd';
import { CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone } from '@ant-design/icons'; import { CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone } from '@ant-design/icons';
import { stores_Context } from '../config'; import { stores_Context } from '../config';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
@ -20,8 +20,8 @@ const topSeries = [
{ key: 'country', label: '国籍' }, { key: 'country', label: '国籍' },
{ key: 'dept', label: '小组' }, { key: 'dept', label: '小组' },
{ key: 'operator', label: '顾问' }, { key: 'operator', label: '顾问' },
{ key: 'GuestGroupType', label: '客群类别' },
{ key: 'destination', label: '目的地' }, { key: 'destination', label: '目的地' },
// { key: 'GuestGroupType', label: '' },
]; ];
// const iconSets = [CheckCircleTwoTone, <MoneyCollectTwoTone />, <FlagTwoTone />, <ClockCircleTwoTone />, <DashboardTwoTone />,<SmileTwoTone />,]; // const iconSets = [CheckCircleTwoTone, <MoneyCollectTwoTone />, <FlagTwoTone />, <ClockCircleTwoTone />, <DashboardTwoTone />,<SmileTwoTone />,];
@ -30,7 +30,7 @@ const iconSets = [CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwo
export default observer(() => { export default observer(() => {
// const navigate = useNavigate(); // const navigate = useNavigate();
const { TradeStore, date_picker_store: searchFormStore } = useContext(stores_Context); const { TradeStore, date_picker_store: searchFormStore } = useContext(stores_Context);
const { sideData, summaryData, BuData, topData, timeData, timeLineKey } = TradeStore; const { sideData, summaryData, BuData, topData, timeData, timeLineKey, targetTableProps } = TradeStore;
const { formValues } = searchFormStore; const { formValues } = searchFormStore;
useEffect(() => { useEffect(() => {
@ -224,6 +224,13 @@ export default observer(() => {
</Row> </Row>
</Spin> </Spin>
</section> </section>
<section>
<h3>目标客户
<Spin spinning={topData?.GuestGroupType?.loading || false}>
<Table {...targetTableProps} pagination={false} />
</Spin>
</h3>
</section>
<section> <section>
<Space> <Space>
<h3>TOP</h3> <h3>TOP</h3>

@ -42,7 +42,10 @@ const Sale = () => {
average_value = Math.round(config_data.data.reduce((a, b) => a + b.PriceTime, 0) / config_data.data.length); average_value = Math.round(config_data.data.reduce((a, b) => a + b.PriceTime, 0) / config_data.data.length);
break; break;
default: default:
config_data.data = []; // config_data.data = [];
config_data.data = type_data.dataSource;
config_data.xField = 'T_name';
config_data.yField = 'T_total';
break; break;
} }
return { return {

Loading…
Cancel
Save