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.
513 lines
19 KiB
JavaScript
513 lines
19 KiB
JavaScript
import React, { Component } from "react";
|
|
import { Row, Col, Tabs, Table, Divider, Spin } from "antd";
|
|
import { ContainerOutlined, BlockOutlined, SmileOutlined, TagsOutlined, GlobalOutlined, FullscreenOutlined, DingtalkOutlined, CarryOutOutlined, CoffeeOutlined, ClockCircleOutlined, HeartOutlined, IdcardOutlined, ContactsOutlined } from "@ant-design/icons";
|
|
import { stores_Context } from "../config";
|
|
import { Line, Pie } from "@ant-design/charts";
|
|
import { observer } from "mobx-react";
|
|
import * as config from "../config";
|
|
import { NavLink } from "react-router-dom";
|
|
import * as comm from "../utils/commons";
|
|
import { utils, writeFileXLSX } from "xlsx";
|
|
import DateGroupRadio from '../components/DateGroupRadio';
|
|
import SearchForm from './../components/search/SearchForm';
|
|
import { TableExportBtn } from './../components/Data';
|
|
|
|
class Orders extends Component {
|
|
static contextType = stores_Context;
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
}
|
|
|
|
format_data(data) {
|
|
const { date_picker_store, orders_store } = this.context;
|
|
const result = { dataSource: [], columns: [] };
|
|
if (!comm.empty(data)) {
|
|
const ordercountTotal1 = data.ordercountTotal1;
|
|
const ordercountTotal2 = data.ordercountTotal2;
|
|
if (date_picker_store.start_date_cp && date_picker_store.end_date_cp) {
|
|
// 有比较的数据
|
|
result.columns = [
|
|
{
|
|
title: '#',
|
|
fixed: 'left',
|
|
children: [
|
|
{
|
|
title: (
|
|
<span>
|
|
<div>
|
|
{date_picker_store.start_date.format(config.DATE_FORMAT)}~{date_picker_store.end_date.format(config.DATE_FORMAT)}
|
|
</div>
|
|
<div>
|
|
{date_picker_store.start_date_cp.format(config.DATE_FORMAT)}~{date_picker_store.end_date_cp.format(config.DATE_FORMAT)}
|
|
</div>
|
|
</span>
|
|
),
|
|
titleX: `${date_picker_store.start_date.format(config.DATE_FORMAT)}~${date_picker_store.end_date.format(config.DATE_FORMAT)} vs ${date_picker_store.start_date_cp.format(
|
|
config.DATE_FORMAT
|
|
)}~${date_picker_store.end_date_cp.format(config.DATE_FORMAT)}`,
|
|
dataIndex: 'OrderType',
|
|
fixed: 'left',
|
|
render: (text, record) => <NavLink to={`/orders_sub/${orders_store.active_tab_key}/${record.OrderTypeSN}/${record.OrderType}`}>{text}</NavLink>,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '数量',
|
|
children: [
|
|
{
|
|
title: comm.show_vs_tag(ordercountTotal1.OrderCount_vs, ordercountTotal1.OrderCount_diff, ordercountTotal1.OrderCount, ordercountTotal2.OrderCount),
|
|
titleX: [ordercountTotal1.OrderCount, ordercountTotal2.OrderCount].join(' vs '),
|
|
dataIndex: 'OrderCount',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '成交数',
|
|
children: [
|
|
{
|
|
title: comm.show_vs_tag(ordercountTotal1.CJCount_vs, ordercountTotal1.CJCount_diff, ordercountTotal1.CJCount, ordercountTotal2.CJCount),
|
|
titleX: [ordercountTotal1.CJCount, ordercountTotal2.CJCount].join(' vs '),
|
|
dataIndex: 'CJCount',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '成交人数',
|
|
children: [
|
|
{
|
|
title: comm.show_vs_tag(ordercountTotal1.CJPersonNum_vs, ordercountTotal1.CJPersonNum_diff, ordercountTotal1.CJPersonNum, ordercountTotal2.CJPersonNum),
|
|
titleX: [ordercountTotal1.CJPersonNum, ordercountTotal2.CJPersonNum].join(' vs '),
|
|
dataIndex: 'CJPersonNum',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '成交率',
|
|
children: [
|
|
{
|
|
title: comm.show_vs_tag(ordercountTotal1.CJrate_vs, ordercountTotal1.CJrate_diff, ordercountTotal1.CJrate, ordercountTotal2.CJrate),
|
|
titleX: [ordercountTotal1.CJrate, ordercountTotal2.CJrate].join(' vs '),
|
|
dataIndex: 'CJrate',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '成交毛利(预计)',
|
|
children: [
|
|
{
|
|
title: comm.show_vs_tag(ordercountTotal1.YJLY_vs, ordercountTotal1.YJLY_diff, ordercountTotal1.YJLY, ordercountTotal2.YJLY),
|
|
titleX: [ordercountTotal1.YJLY, ordercountTotal2.YJLY].join(' vs '),
|
|
dataIndex: 'YJLY',
|
|
},
|
|
],
|
|
},
|
|
|
|
{
|
|
title: '单个订单价值',
|
|
children: [
|
|
{
|
|
title: comm.show_vs_tag(ordercountTotal1.Ordervalue_vs, ordercountTotal1.Ordervalue_diff, ordercountTotal1.Ordervalue, ordercountTotal2.Ordervalue),
|
|
titleX: [ordercountTotal1.Ordervalue, ordercountTotal2.Ordervalue].join(' vs '),
|
|
dataIndex: 'Ordervalue',
|
|
},
|
|
],
|
|
},
|
|
];
|
|
// 1.找出两个数组OrderType相同的数据做比较显示 2.找出两组数据OrderType都不相同的数据做显示
|
|
let has_same_type = false; // 数组1在数组2中是否找到相同的类型
|
|
for (const item of data.ordercount1) {
|
|
has_same_type = false;
|
|
// 数组1在数组2中相同的类型
|
|
for (const item2 of data.ordercount2) {
|
|
if (item.OrderType === item2.OrderType) {
|
|
has_same_type = true;
|
|
result.dataSource.push({
|
|
key: item.key,
|
|
OrderType: item.OrderType,
|
|
OrderTypeSN: item.OrderTypeSN,
|
|
OrderCount: comm.show_vs_tag(item.OrderCount_vs, item.OrderCount_diff, item.OrderCount, item2.OrderCount),
|
|
OrderCount_X: ([item.OrderCount, item2.OrderCount].join(' vs ')),
|
|
CJCount: comm.show_vs_tag(item.CJCount_vs, item.CJCount_diff, item.CJCount, item2.CJCount),
|
|
CJCount_X: ([item.CJCount, item2.CJCount].join(' vs ')),
|
|
CJPersonNum: comm.show_vs_tag(item.CJPersonNum_vs, item.CJPersonNum_diff, item.CJPersonNum, item2.CJPersonNum),
|
|
CJPersonNum_X: ([item.CJPersonNum, item2.CJPersonNum].join(' vs ')),
|
|
CJrate: comm.show_vs_tag(item.CJrate_vs, item.CJrate_diff, item.CJrate, item2.CJrate),
|
|
CJrate_X: ([item.CJrate, item2.CJrate].join(' vs ')),
|
|
YJLY: comm.show_vs_tag(item.YJLY_vs, item.YJLY_diff, item.YJLY, item2.YJLY),
|
|
YJLY_X: ([item.YJLY, item2.YJLY].join(' vs ')),
|
|
Ordervalue: comm.show_vs_tag(item.Ordervalue_vs, item.Ordervalue_diff, item.Ordervalue, item2.Ordervalue),
|
|
Ordervalue_X: ([item.Ordervalue, item2.Ordervalue].join(' vs ')),
|
|
});
|
|
}
|
|
}
|
|
// 数组1中不在数组2的类型
|
|
if (has_same_type === false) {
|
|
result.dataSource.push({
|
|
key: item.key,
|
|
OrderType: item.OrderType,
|
|
OrderTypeSN: item.OrderTypeSN,
|
|
OrderCount: comm.show_vs_tag(comm.formatPercent(item.OrderCount), item.OrderCount, item.OrderCount, 0),
|
|
OrderCount_X: ([item.OrderCount, 0].join(' vs ')),
|
|
CJCount: comm.show_vs_tag(comm.formatPercent(item.CJCount), item.CJCount, item.CJCount, 0),
|
|
CJCount_X: ([item.CJCount, 0].join(' vs ')),
|
|
CJPersonNum: comm.show_vs_tag(comm.formatPercent(item.CJPersonNum), item.CJPersonNum, item.CJPersonNum, 0),
|
|
CJPersonNum_X: ([item.CJPersonNum, 0].join(' vs ')),
|
|
CJrate: comm.show_vs_tag(item.CJrate, item.CJrate, item.CJrate, 0),
|
|
CJrate_X: ([item.CJrate, 0].join(' vs ')),
|
|
YJLY: comm.show_vs_tag(comm.formatPercent(item.YJLY), item.YJLY, item.YJLY, 0),
|
|
YJLY_X: ([item.YJLY, 0].join(' vs ')),
|
|
Ordervalue: comm.show_vs_tag(comm.formatPercent(item.Ordervalue), item.Ordervalue, item.Ordervalue, 0),
|
|
Ordervalue_X: ([item.Ordervalue, 0].join(' vs ')),
|
|
});
|
|
}
|
|
}
|
|
// 数组2中不在数组1的类型
|
|
for (const item2 of data.ordercount2) {
|
|
has_same_type = false;
|
|
for (const item of data.ordercount1) {
|
|
if (item.OrderType === item2.OrderType) {
|
|
has_same_type = true;
|
|
}
|
|
}
|
|
if (has_same_type === false) {
|
|
result.dataSource.push({
|
|
key: item2.key,
|
|
OrderType: item2.OrderType,
|
|
OrderTypeSN: item2.OrderTypeSN,
|
|
OrderCount: comm.show_vs_tag(comm.formatPercent(-item2.OrderCount), -item2.OrderCount, 0, item2.OrderCount),
|
|
OrderCount_X: ([ 0, item2.OrderCount].join(' vs ')),
|
|
CJCount: comm.show_vs_tag(comm.formatPercent(-item2.CJCount), -item2.CJCount, 0, item2.CJCount),
|
|
CJCount_X: ([ 0, item2.CJCount].join(' vs ')),
|
|
CJPersonNum: comm.show_vs_tag(comm.formatPercent(-item2.CJPersonNum), -item2.CJPersonNum, 0, item2.CJPersonNum),
|
|
CJPersonNum_X: ([0, item2.CJPersonNum].join(' vs ')),
|
|
CJrate: comm.show_vs_tag(-item2.CJrate, -item2.CJrate, 0, item2.CJrate),
|
|
CJrate_X: ([ 0, item2.CJrate].join(' vs ')),
|
|
YJLY: comm.show_vs_tag(comm.formatPercent(-item2.YJLY), -item2.YJLY, 0, item2.YJLY),
|
|
YJLY_X: ([0, item2.YJLY].join(' vs ')),
|
|
Ordervalue: comm.show_vs_tag(comm.formatPercent(-item2.Ordervalue), -item2.Ordervalue, 0, item2.Ordervalue),
|
|
Ordervalue_X: ([ 0, item2.Ordervalue].join(' vs ')),
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
result.columns = [
|
|
{
|
|
title: "#",
|
|
fixed: 'left',
|
|
children: [
|
|
{
|
|
title: (
|
|
<span>
|
|
<div>
|
|
{date_picker_store.start_date.format(config.DATE_FORMAT)}~{date_picker_store.end_date.format(config.DATE_FORMAT)}
|
|
</div>
|
|
</span>
|
|
),
|
|
titleX: `${date_picker_store.start_date.format(config.DATE_FORMAT)}~${date_picker_store.end_date.format(config.DATE_FORMAT)}`,
|
|
fixed: 'left',
|
|
dataIndex: "OrderType",
|
|
render: (text, record) => <NavLink to={`/orders_sub/${orders_store.active_tab_key}/${record.OrderTypeSN}/${record.OrderType}`}>{text}</NavLink>,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: "数量",
|
|
children: [{ title: ordercountTotal1.OrderCount, dataIndex: "OrderCount" }],
|
|
sorter: (a, b) => b.OrderCount - a.OrderCount,
|
|
},
|
|
{
|
|
title: "成交数",
|
|
children: [{ title: ordercountTotal1.CJCount, dataIndex: "CJCount" }],
|
|
sorter: (a, b) => b.CJCount - a.CJCount,
|
|
},
|
|
{
|
|
title: "成交人数",
|
|
children: [{ title: ordercountTotal1.CJPersonNum, dataIndex: "CJPersonNum" }],
|
|
sorter: (a, b) => b.CJPersonNum - a.CJPersonNum,
|
|
},
|
|
{
|
|
title: "成交率",
|
|
children: [{ title: ordercountTotal1.CJrate, dataIndex: "CJrate" }],
|
|
sorter: (a, b) => parseInt(b.CJrate) - parseInt(a.CJrate),
|
|
},
|
|
{
|
|
title: "成交毛利(预计)",
|
|
children: [{ title: ordercountTotal1.YJLY, dataIndex: "YJLY" }],
|
|
sorter: (a, b) => parseFloat(b.YJLY.replace(/,/g, "")) - parseFloat(a.YJLY.replace(/,/g, "")),
|
|
},
|
|
|
|
{
|
|
title: "单个订单价值",
|
|
children: [{ title: ordercountTotal1.Ordervalue, dataIndex: "Ordervalue" }],
|
|
sorter: (a, b) => parseFloat(b.Ordervalue.replace(/,/g, "")) - parseFloat(a.Ordervalue.replace(/,/g, "")),
|
|
},
|
|
];
|
|
result.dataSource = data.ordercount1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
render() {
|
|
const { orders_store, date_picker_store } = this.context;
|
|
const table_data = orders_store.orderCountData_Form ? this.format_data(orders_store.orderCountData_Form) : [];
|
|
const data_source = orders_store.orderCountData ? orders_store.orderCountData : [];
|
|
const avg_line_y = Math.round(orders_store.avgLine1);
|
|
const pie_data = comm.empty(orders_store.orderCountData_Form) ? [] : orders_store.orderCountData_Form.ordercount1; // 饼图的显示
|
|
const pie_data2 = comm.empty(orders_store.orderCountData_Form) ? [] : orders_store.orderCountData_Form.ordercount2;
|
|
|
|
const config = {
|
|
data: data_source,
|
|
padding: "auto",
|
|
xField: "xField",
|
|
yField: "yField",
|
|
seriesField: "seriesField",
|
|
// xAxis: {
|
|
// type: "timeCat",
|
|
// },
|
|
point: {
|
|
size: 4,
|
|
shape: "cicle",
|
|
},
|
|
annotations: [
|
|
{
|
|
type: "text",
|
|
position: ["start", avg_line_y],
|
|
content: avg_line_y,
|
|
offsetX: -15,
|
|
style: {
|
|
fill: "#F4664A",
|
|
textBaseline: "bottom",
|
|
},
|
|
},
|
|
{
|
|
type: "line",
|
|
start: [-10, avg_line_y],
|
|
end: ["max", avg_line_y],
|
|
style: {
|
|
stroke: "#F4664A",
|
|
lineDash: [2, 2],
|
|
},
|
|
},
|
|
],
|
|
label: {}, // 显示标签
|
|
legend: {
|
|
itemValue: {
|
|
formatter: (text, item) => {
|
|
const items = data_source.filter(d => d.seriesField === item.value); // 按站点筛选
|
|
return items.length ? items.reduce((a, b) => a + b.yField, 0) : ""; // 计算总数
|
|
},
|
|
},
|
|
},
|
|
tooltip: {
|
|
customItems: (originalItems) => {
|
|
// process originalItems,
|
|
return originalItems.map(ele => ({...ele, name: ele.data?.seriesField || ele.data?.xField}));
|
|
},
|
|
title: (title, datum) => {
|
|
let ret = title;
|
|
switch (orders_store.lineChartXGroup) {
|
|
case 'day':
|
|
ret = `${title} ${comm.getWeek(datum.xField)}`; // 显示周几
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
},
|
|
},
|
|
// smooth: true,
|
|
};
|
|
const pie_config = {
|
|
appendPadding: 10,
|
|
data: [],
|
|
angleField: "OrderCount",
|
|
colorField: "OrderType",
|
|
radius: 0.8,
|
|
label: {
|
|
type: "outer",
|
|
content: "{name} {value} \n {percentage}",
|
|
},
|
|
legend: false, // 不显示图例
|
|
interactions: [
|
|
{
|
|
type: "element-selected",
|
|
},
|
|
{
|
|
type: "element-active",
|
|
},
|
|
],
|
|
};
|
|
|
|
const tableProps = {
|
|
dataSource: table_data.dataSource,
|
|
columns: table_data.columns,
|
|
size: 'small',
|
|
pagination: false,
|
|
scroll: { x: (100*(table_data.columns.length)) },
|
|
loading: orders_store.loading,
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<Row gutter={16} className={date_picker_store.siderBroken ? "" : "sticky-top"} >
|
|
<Col className="gutter-row" span={24}>
|
|
<SearchForm
|
|
defaultValue={{
|
|
initialValue: {
|
|
...date_picker_store.formValues,
|
|
...orders_store.searchValues,
|
|
},
|
|
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates'],
|
|
fieldProps: {
|
|
DepartmentList: { show_all: false, mode: 'multiple' },
|
|
WebCode: { show_all: false, mode: 'multiple' },
|
|
years: { hide_vs: true },
|
|
},
|
|
}}
|
|
onSubmit={(_err, obj, form, str) => {
|
|
orders_store.setSearchValues(obj, form);
|
|
orders_store.getOrderCount();
|
|
orders_store.onChange_Tabs(orders_store.active_tab_key);
|
|
}}
|
|
/>
|
|
</Col>
|
|
</Row>
|
|
<Row gutter={[16, { sm: 16, lg: 32 }]}>
|
|
<Col span={24} style={{ textAlign: 'right' }}>
|
|
<DateGroupRadio
|
|
visible={data_source.length !== 0}
|
|
dataRaw={orders_store.orderCountDataRaw}
|
|
onChange={orders_store.onChangeDateGroup}
|
|
value={orders_store.lineChartXGroup}
|
|
dataMapper={orders_store.orderCountDataMapper}
|
|
fieldMapper={orders_store.orderCountDataFieldMapper}
|
|
/>
|
|
</Col>
|
|
<Col span={24}>
|
|
<Spin spinning={orders_store.loading}>
|
|
<Line {...config} />
|
|
</Spin>
|
|
</Col>
|
|
|
|
<Col span={24}>
|
|
<Tabs
|
|
activeKey={orders_store.active_tab_key}
|
|
onChange={(active_key) => orders_store.onChange_Tabs(active_key)}
|
|
items={[
|
|
{
|
|
key: 'Form',
|
|
label: (
|
|
<span>
|
|
<ContainerOutlined />
|
|
来源类型
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'Product',
|
|
label: (
|
|
<span>
|
|
<CarryOutOutlined />
|
|
产品类型
|
|
</span>),
|
|
},
|
|
{
|
|
key: 'Country',
|
|
label: (
|
|
<span>
|
|
<SmileOutlined />
|
|
国籍
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'line',
|
|
label: (
|
|
<span>
|
|
<TagsOutlined />
|
|
线路
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'city',
|
|
label: (
|
|
<span>
|
|
<GlobalOutlined />
|
|
目的地
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'LineClass',
|
|
label: (
|
|
<span>
|
|
<BlockOutlined />
|
|
页面类型
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'GuestGroupType',
|
|
label: (
|
|
<span>
|
|
<FullscreenOutlined />
|
|
客群类别
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'TravelMotivation',
|
|
label: (
|
|
<span>
|
|
<DingtalkOutlined />
|
|
出行动机
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'ToB',
|
|
label: (
|
|
<span>
|
|
<ContactsOutlined />
|
|
客运类别
|
|
</span>
|
|
),
|
|
},
|
|
{key: 'FoodRequirement',label: (<span><CoffeeOutlined />饮食要求</span>),},
|
|
{key: 'hobbies',label: (<span><HeartOutlined/>兴趣爱好</span>),},
|
|
{key: 'ages',label: (<span><IdcardOutlined/>年龄段</span>),},
|
|
].map((ele) => {
|
|
return {
|
|
...ele,
|
|
children: (
|
|
<>
|
|
<Table sticky id={`table_to_xlsx_${ele.key}`} {...tableProps} />
|
|
<Divider orientation="right" plain>
|
|
<TableExportBtn label={ele.key} {...{ columns: tableProps.columns, dataSource: tableProps.dataSource }} />
|
|
</Divider>
|
|
</>
|
|
),
|
|
};
|
|
})}
|
|
/>
|
|
<Row>
|
|
<Col sm={24} lg={12}>
|
|
<Pie {...pie_config} data={pie_data} />
|
|
</Col>
|
|
<Col sm={24} lg={12}>
|
|
<Pie {...pie_config} data={pie_data2} />
|
|
</Col>
|
|
</Row>
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default observer(Orders);
|