Merge branch 'feature/biz'
commit
d7cbad1d02
@ -0,0 +1,442 @@
|
||||
import { useContext } from 'react';
|
||||
import { Row, Col, Tabs, Table, Divider, Spin, Checkbox, Space } from 'antd';
|
||||
import { ContainerOutlined, BlockOutlined, SmileOutlined, MobileOutlined, CustomerServiceOutlined } from '@ant-design/icons';
|
||||
import { Line, Pie } from '@ant-design/charts';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import * as comm from '../../utils/commons';
|
||||
import DateGroupRadio from '../../components/DateGroupRadio';
|
||||
import SearchForm from '../../components/search/SearchForm';
|
||||
import { TableExportBtn } from '../../components/Data';
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import { toJS } from 'mobx';
|
||||
import { stores_Context } from '../../config';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import useBizOrderStore, { orderCountDataMapper, orderCountDataFieldMapper } from '../../zustand/BizOrder';
|
||||
|
||||
const BizOrder = observer(() => {
|
||||
const { date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
|
||||
const [searchValues, setSearchValues] = useBizOrderStore(useShallow((state) => [state.searchValues, state.setSearchValues]));
|
||||
const [activeTab, setActiveTab] = useBizOrderStore(useShallow((state) => [state.activeTab, state.setActiveTab]));
|
||||
const [loading, onTabChange] = useBizOrderStore(useShallow((state) => [state.loading, state.onTabChange]));
|
||||
|
||||
const orderCountDataRaw = useBizOrderStore((state) => state.orderCountDataRaw);
|
||||
const [orderCountDataLines, avgLineValue] = useBizOrderStore(useShallow((state) => [state.orderCountDataLines, state.avgLineValue]));
|
||||
const [onChangeDateGroup, activeDateGroupRadio] = useBizOrderStore(useShallow((state) => [state.onChangeDateGroup, state.activeDateGroupRadio]));
|
||||
|
||||
const orderCountDataByType = useBizOrderStore((state) => state.orderCountDataByType);
|
||||
const result = orderCountDataByType[activeTab] || {};
|
||||
|
||||
const getBizOrderCount = useBizOrderStore((state) => state.getBizOrderCount);
|
||||
|
||||
const showDiff = !comm.isEmpty(searchFormStore.start_date_cp);
|
||||
|
||||
const avg_line_y = Math.round(avgLineValue);
|
||||
const lineConfig = {
|
||||
data: orderCountDataLines,
|
||||
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 = orderCountDataLines.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 (activeDateGroupRadio) {
|
||||
case 'day':
|
||||
ret = `${title} ${comm.getWeek(datum.xField)}`; // 显示周几
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
},
|
||||
// smooth: true,
|
||||
};
|
||||
const pieConfig = {
|
||||
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: result?.ordercount1 || [], // table_data.dataSource,
|
||||
columns: [
|
||||
{
|
||||
title: '#',
|
||||
fixed: 'left',
|
||||
children: [
|
||||
{
|
||||
title: (
|
||||
<span>
|
||||
<div>{result.ordercountTotal1?.groups}</div>
|
||||
{showDiff ? <div>{result.ordercountTotal2?.groups}</div> : null}
|
||||
</span>
|
||||
),
|
||||
titleX: `${result.ordercountTotal1?.groups}` + (showDiff ? ` vs ${result.ordercountTotal2?.groups}` : ''),
|
||||
dataIndex: 'OrderType',
|
||||
fixed: 'left',
|
||||
render: (text, record) => <NavLink to={`/biz_orders_sub/${activeTab}/${record.OrderTypeSN}/${encodeURIComponent(record.OrderType)}`}>{text}</NavLink>,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
children: [
|
||||
{
|
||||
title: !showDiff
|
||||
? result.ordercountTotal1?.OrderCount
|
||||
: comm.show_vs_tag(result.ordercountTotal1?.OrderCount_vs, result.ordercountTotal1?.OrderCount_diff, result.ordercountTotal1?.OrderCount, result.ordercountTotal2?.OrderCount),
|
||||
titleX: [result.ordercountTotal1?.OrderCount, result.ordercountTotal2?.OrderCount].join(' vs '),
|
||||
dataIndex: 'OrderCount',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.OrderCount_vs, r.OrderCount_diff, r.OrderCount, r.diff?.OrderCount)),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '成交数',
|
||||
children: [
|
||||
{
|
||||
title: !showDiff
|
||||
? result.ordercountTotal1?.CJCount
|
||||
: comm.show_vs_tag(result.ordercountTotal1?.CJCount_vs, result.ordercountTotal1?.CJCount_diff, result.ordercountTotal1?.CJCount, result.ordercountTotal2?.CJCount),
|
||||
titleX: [result.ordercountTotal1?.CJCount, result.ordercountTotal2?.CJCount].join(' vs '),
|
||||
dataIndex: 'CJCount',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.CJCount_vs, r.CJCount_diff, r.CJCount, r.diff?.CJCount)),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '成交人数',
|
||||
children: [
|
||||
{
|
||||
title: !showDiff
|
||||
? result.ordercountTotal1?.CJPersonNum
|
||||
: comm.show_vs_tag(result.ordercountTotal1?.CJPersonNum_vs, result.ordercountTotal1?.CJPersonNum_diff, result.ordercountTotal1?.CJPersonNum, result.ordercountTotal2?.CJPersonNum),
|
||||
titleX: [result.ordercountTotal1?.CJPersonNum, result.ordercountTotal2?.CJPersonNum].join(' vs '),
|
||||
dataIndex: 'CJPersonNum',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.CJPersonNum_vs, r.CJPersonNum_diff, r.CJPersonNum, r.diff?.CJPersonNum)),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '成交率',
|
||||
children: [
|
||||
{
|
||||
title: !showDiff
|
||||
? result.ordercountTotal1?.CJrate
|
||||
: comm.show_vs_tag(result.ordercountTotal1?.CJrate_vs, result.ordercountTotal1?.CJrate_diff, result.ordercountTotal1?.CJrate, result.ordercountTotal2?.CJrate),
|
||||
titleX: [result.ordercountTotal1?.CJrate, result.ordercountTotal2?.CJrate].join(' vs '),
|
||||
dataIndex: 'CJrate',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.CJrate_vs, r.CJrate_diff, r.CJrate, r.diff?.CJrate)),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '成交毛利(预计)',
|
||||
children: [
|
||||
{
|
||||
title: !showDiff
|
||||
? result.ordercountTotal1?.YJLY
|
||||
: comm.show_vs_tag(result.ordercountTotal1?.YJLY_vs, result.ordercountTotal1?.YJLY_diff, result.ordercountTotal1?.YJLY, result.ordercountTotal2?.YJLY),
|
||||
titleX: [result.ordercountTotal1?.YJLY, result.ordercountTotal2?.YJLY].join(' vs '),
|
||||
dataIndex: 'YJLY',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.YJLY_vs, r.YJLY_diff, r.YJLY, r.diff?.YJLY)),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
title: '单个订单价值',
|
||||
children: [
|
||||
{
|
||||
title: !showDiff
|
||||
? result.ordercountTotal1?.Ordervalue
|
||||
: comm.show_vs_tag(result.ordercountTotal1?.Ordervalue_vs, result.ordercountTotal1?.Ordervalue_diff, result.ordercountTotal1?.Ordervalue, result.ordercountTotal2?.Ordervalue),
|
||||
titleX: [result.ordercountTotal1?.Ordervalue, result.ordercountTotal2?.Ordervalue].join(' vs '),
|
||||
dataIndex: 'Ordervalue',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.Ordervalue_vs, r.Ordervalue_diff, r.Ordervalue, r.diff?.Ordervalue)),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
size: 'small',
|
||||
pagination: false,
|
||||
scroll: { x: 100 * 7 },
|
||||
loading,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Row gutter={16} className={toJS(searchFormStore.siderBroken) ? '' : 'sticky-top'}>
|
||||
<Col className="gutter-row" span={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...toJS(searchFormStore.formValues),
|
||||
...searchValues,
|
||||
},
|
||||
// 'WebCode','IncludeTickets',
|
||||
shows: ['DateType', 'DepartmentList', 'IncludeInternal', 'dates'],
|
||||
fieldProps: {
|
||||
DepartmentList: { show_all: false, mode: 'multiple' },
|
||||
WebCode: { show_all: false, mode: 'multiple' },
|
||||
years: { hide_vs: true },
|
||||
},
|
||||
}}
|
||||
onSubmit={(_err, obj, form, str) => {
|
||||
setSearchValues(obj, form);
|
||||
getBizOrderCount(obj);
|
||||
onTabChange(activeTab);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={[16, { sm: 16, lg: 32 }]}>
|
||||
<Col span={24} style={{ textAlign: 'right' }}>
|
||||
<DateGroupRadio
|
||||
visible={orderCountDataLines.length !== 0}
|
||||
dataRaw={orderCountDataRaw}
|
||||
onChange={onChangeDateGroup}
|
||||
value={activeDateGroupRadio}
|
||||
dataMapper={orderCountDataMapper}
|
||||
fieldMapper={orderCountDataFieldMapper}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Spin spinning={loading}>
|
||||
<Line {...lineConfig} />
|
||||
</Spin>
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={(active_key) => onTabChange(active_key)}
|
||||
items={[
|
||||
{
|
||||
key: 'servicetype',
|
||||
label: (
|
||||
<span>
|
||||
<CustomerServiceOutlined />
|
||||
服务类型
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
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: 'ordersource',
|
||||
label: (
|
||||
<span>
|
||||
<MobileOutlined />
|
||||
来源设备
|
||||
</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 key={`table_to_xlsx_${ele.key}`} {...tableProps} />
|
||||
<Divider orientation="right" plain>
|
||||
<TableExportBtn label={ele.key} {...{ columns: tableProps.columns, dataSource: tableProps.dataSource }} />
|
||||
</Divider>
|
||||
</>
|
||||
),
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div>
|
||||
<h3>各项占比</h3>
|
||||
{/* <Checkbox
|
||||
checked={true}
|
||||
// onChange={(e) => setIsShowEmpty(e.target.checked)}
|
||||
>
|
||||
包含空值
|
||||
</Checkbox> */}
|
||||
</div>
|
||||
<Spin spinning={loading}>
|
||||
<Row>
|
||||
<Col sm={24} lg={12}>
|
||||
<Pie {...pieConfig} data={result?.ordercount1 || []} />
|
||||
</Col>
|
||||
<Col sm={24} lg={12}>
|
||||
<Pie {...pieConfig} data={result?.ordercount2 || []} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
export default BizOrder;
|
||||
@ -0,0 +1,238 @@
|
||||
import { useContext } from 'react';
|
||||
import { Row, Col, Table, Spin, Space, Divider } from 'antd';
|
||||
import { Funnel, Pie } from '@ant-design/charts';
|
||||
|
||||
import * as comm from '../../../utils/commons';
|
||||
import SearchForm from '../../../components/search/SearchForm';
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import { toJS } from 'mobx';
|
||||
import { stores_Context } from '../../../config';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import useTrainsStore from '../../../zustand/Trains';
|
||||
|
||||
const buildFunnelData = (data1, data2) => {
|
||||
// const key = 'OrderCount';
|
||||
const data01 = [
|
||||
{ stage: '火车票服务', number: data1?.data?.[0]?.OrderCount, dateRange: data1?.dateRangeStr },
|
||||
{ stage: '成行出票', number: data1?.data?.[0]?.CJCount, dateRange: data1?.dateRangeStr },
|
||||
{ stage: '火车票Upsell', number: (data1?.data?.[1]?.OrderCount || 0) + 0, dateRange: data1?.dateRangeStr },
|
||||
{ stage: 'Upsell成行', number: data1?.data?.[1]?.CJCount, dateRange: data1?.dateRangeStr },
|
||||
];
|
||||
const data02 = !comm.isEmpty(data2)
|
||||
? [
|
||||
{ stage: '火车票服务', number: data2?.data?.[0]?.OrderCount, dateRange: data2?.dateRangeStr },
|
||||
{ stage: '成行出票', number: data2?.data?.[0]?.CJCount, dateRange: data2?.dateRangeStr },
|
||||
{ stage: '火车票Upsell', number: data2?.data?.[1]?.OrderCount, dateRange: data2?.dateRangeStr },
|
||||
{ stage: 'Upsell成行', number: data2?.data?.[1]?.CJCount, dateRange: data2?.dateRangeStr },
|
||||
]
|
||||
: [];
|
||||
return data01.concat(data02);
|
||||
// return data02.concat(data01);
|
||||
};
|
||||
|
||||
const buildPieData = (data1, data2) => {
|
||||
const data01 = !comm.isEmpty(data1)
|
||||
? [
|
||||
{ stage: '火车票出票', number: Number((data1?.data?.[0]?.YJLY || '0').replaceAll(',', '')), dateRange: data1?.dateRangeStr },
|
||||
{ stage: 'Upsell成行', number: Number((data1?.data?.[1]?.YJLY || '0').replaceAll(',', '')), dateRange: data1?.dateRangeStr },
|
||||
]
|
||||
: [];
|
||||
const data02 = !comm.isEmpty(data2)
|
||||
? [
|
||||
{ stage: '火车票出票', number: Number((data2?.data?.[0]?.YJLY || '0').replaceAll(',', '')), dateRange: data2?.dateRangeStr },
|
||||
{ stage: 'Upsell成行', number: Number((data2?.data?.[1]?.YJLY || '0').replaceAll(',', '')), dateRange: data2?.dateRangeStr },
|
||||
]
|
||||
: [];
|
||||
return data01.concat(data02);
|
||||
};
|
||||
|
||||
const TrainsUpsell = observer(({ ...props }) => {
|
||||
const { date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
const [searchValues, setSearchValues] = useTrainsStore(useShallow((state) => [state.searchValues, state.setSearchValues]));
|
||||
|
||||
const [loading] = useTrainsStore(useShallow((state) => [state.loading]));
|
||||
const [trainsOrdersSummary, compareData] = useTrainsStore((state) => state.trainsOrdersSummary);
|
||||
// const showDiff = !comm.isEmpty(searchValuesToSub.DateDiff2);
|
||||
const showDiff = !comm.isEmpty(compareData);
|
||||
|
||||
const getTrainsWithUpsell = useTrainsStore((state) => state.getTrainsWithUpsell);
|
||||
|
||||
const data0 = buildFunnelData(trainsOrdersSummary, compareData);
|
||||
const chartConfig = {
|
||||
data: data0,
|
||||
xField: 'stage',
|
||||
yField: 'number',
|
||||
compareField: showDiff ? 'dateRange' : false,
|
||||
// isTransposed: true,
|
||||
legend: false,
|
||||
// tooltip: {
|
||||
// shared: true,
|
||||
// showMarkers: false,
|
||||
// showTitle: false,
|
||||
// },
|
||||
label: {
|
||||
position: 'right',
|
||||
// offsetX: 10,
|
||||
style: {
|
||||
fill: '#002c45',
|
||||
},
|
||||
},
|
||||
shape: 'pyramid',
|
||||
theme: {
|
||||
colors10: [
|
||||
// '#b5d8f3',
|
||||
'#c4e1ff',
|
||||
// '#739ec1',
|
||||
'#7ebdff',
|
||||
// '#3d759b',
|
||||
'#5b90f9',
|
||||
// '#1f5373',
|
||||
'#526bd1',
|
||||
'#002c45',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const pieData = buildPieData(trainsOrdersSummary);
|
||||
const pieData2 = buildPieData(compareData);
|
||||
// console.log('🟠', pieData, pieData2);
|
||||
const pieConfig = {
|
||||
appendPadding: 0,
|
||||
// data: pieData,
|
||||
angleField: 'number',
|
||||
colorField: 'stage',
|
||||
radius: 0.8,
|
||||
innerRadius: 0.6,
|
||||
// startAngle: Math.PI ,
|
||||
// endAngle: Math.PI * 1.5,
|
||||
label: {
|
||||
type: 'spider',
|
||||
// content: '{name} {value} \n {percentage}',
|
||||
content: '{name}\n{percentage}',
|
||||
},
|
||||
statistic: false,
|
||||
legend: false, // 不显示图例
|
||||
interactions: [
|
||||
{
|
||||
type: 'element-selected',
|
||||
},
|
||||
{
|
||||
type: 'element-active',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const tableProps = {
|
||||
title: () => `${trainsOrdersSummary?.dateRangeStr || ''}` + (showDiff ? ` vs ${compareData?.dateRangeStr}` : ''),
|
||||
dataSource: trainsOrdersSummary?.data || [],
|
||||
columns: [
|
||||
{
|
||||
title: '#',
|
||||
fixed: 'left',
|
||||
dataIndex: 'OrderType',
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'OrderCount',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.OrderCount_vs, r.OrderCount_diff, r.OrderCount, r.diff?.OrderCount)),
|
||||
},
|
||||
{
|
||||
title: '成交数',
|
||||
dataIndex: 'CJCount',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.CJCount_vs, r.CJCount_diff, r.CJCount, r.diff?.CJCount)),
|
||||
},
|
||||
{
|
||||
title: '成交人数',
|
||||
dataIndex: 'CJPersonNum',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.CJPersonNum_vs, r.CJPersonNum_diff, r.CJPersonNum, r.diff?.CJPersonNum)),
|
||||
},
|
||||
{
|
||||
title: '成交率',
|
||||
dataIndex: 'CJrate',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.CJrate_vs, r.CJrate_diff, r.CJrate, r.diff?.CJrate)),
|
||||
},
|
||||
{
|
||||
title: '成交毛利(预计)',
|
||||
dataIndex: 'YJLY',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.YJLY_vs, r.YJLY_diff, r.YJLY, r.diff?.YJLY)),
|
||||
},
|
||||
|
||||
{
|
||||
title: '单个订单价值',
|
||||
dataIndex: 'Ordervalue',
|
||||
render: (text, r) => (!showDiff ? text : comm.show_vs_tag(r.Ordervalue_vs, r.Ordervalue_diff, r.Ordervalue, r.diff?.Ordervalue)),
|
||||
},
|
||||
],
|
||||
size: 'small',
|
||||
pagination: false,
|
||||
scroll: { x: 100 * 7 },
|
||||
loading,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Row gutter={16} className={toJS(searchFormStore.siderBroken) ? '' : 'sticky-top'}>
|
||||
<Col className="gutter-row" span={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...toJS(searchFormStore.formValues),
|
||||
...searchValues,
|
||||
},
|
||||
shows: ['DateType', 'DepartmentList', 'IncludeInternal', 'dates'],
|
||||
fieldProps: {
|
||||
DepartmentList: { show_all: false, mode: 'multiple' },
|
||||
WebCode: { show_all: false, mode: 'multiple' },
|
||||
years: { hide_vs: true },
|
||||
},
|
||||
}}
|
||||
onSubmit={(_err, obj, form, str) => {
|
||||
setSearchValues(obj, form);
|
||||
getTrainsWithUpsell(obj);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Space direction={'vertical'}>
|
||||
<Table sticky key={`table_1`} {...tableProps} />
|
||||
|
||||
<Spin spinning={loading}>
|
||||
<Row gutter={[16, { sm: 16, lg: 32 }]}>
|
||||
<Col {...{ xs: 24, md: 12, lg: 12 }}>
|
||||
<h3>订单数量</h3>
|
||||
<Funnel {...chartConfig} key={showDiff ? 'funnel_2' : 'funnel_1'} />
|
||||
</Col>
|
||||
<Col {...{ xs: 24, md: 12, lg: 12 }}>
|
||||
<h3>预计利润</h3>
|
||||
<div style={{ display: showDiff ? 'flex' : 'block' }}>
|
||||
<Pie
|
||||
key={showDiff ? 'pie1' : 'pie0'}
|
||||
style={{ display: showDiff ? 'inline' : 'block' }}
|
||||
{...pieConfig}
|
||||
startAngle={Math.PI}
|
||||
endAngle={Math.PI * (showDiff ? 1.5 : 2)}
|
||||
data={pieData}
|
||||
/>
|
||||
{showDiff && (
|
||||
<Pie
|
||||
key={'pie2'}
|
||||
style={{ display: 'inline' }}
|
||||
{...pieConfig}
|
||||
startAngle={Math.PI * 1.5}
|
||||
endAngle={Math.PI * 2}
|
||||
data={pieData2.reverse()}
|
||||
color={['#61DDAA', '#5B8FF9']}
|
||||
pattern={{ type: 'line' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</Space>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
export default TrainsUpsell;
|
||||
@ -0,0 +1,234 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { groupsMappedByCode } from '../libs/ht';
|
||||
import { fetchJSON } from '../utils/request';
|
||||
import { HT_HOST } from '../config';
|
||||
import { resultDataCb } from '../components/DateGroupRadio/date';
|
||||
import { isEmpty } from '../utils/commons';
|
||||
|
||||
const defaultParams = { WebCode: 'all', IncludeTickets: 1, IncludeInternal: 1 };
|
||||
|
||||
export const fetchBizOrderCount = async (params, type = '', typeVal = '') => {
|
||||
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCount_biz', {
|
||||
...defaultParams,
|
||||
...params,
|
||||
COLI_ApplyDate1: params.Date1,
|
||||
COLI_ApplyDate2: params.Date2,
|
||||
COLI_ApplyDateOld1: params.DateDiff1 || '',
|
||||
COLI_ApplyDateOld2: params.DateDiff2 || '',
|
||||
OrderType: type,
|
||||
OrderType_val: typeVal,
|
||||
});
|
||||
return errcode !== 0 ? {} : (result || {});
|
||||
};
|
||||
|
||||
const _typeRes = {
|
||||
'ordercount1': [],
|
||||
'ordercount2': [],
|
||||
'ordercountTotal1': {
|
||||
'OrderType': '合计',
|
||||
'OrderCount': 0,
|
||||
'CJCount': 0,
|
||||
'CJPersonNum': 0,
|
||||
'YJLY': '',
|
||||
'CJrate': '0%',
|
||||
'Ordervalue': '',
|
||||
'groups': '',
|
||||
'key': 1,
|
||||
},
|
||||
'ordercountTotal2': {},
|
||||
};
|
||||
export const fetchBizOrderCountByType = async (type, params) => {
|
||||
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCountByType_biz', {
|
||||
...defaultParams,
|
||||
...params,
|
||||
COLI_ApplyDate1: params.Date1,
|
||||
COLI_ApplyDate2: params.Date2,
|
||||
COLI_ApplyDateOld1: params.DateDiff1 || '',
|
||||
COLI_ApplyDateOld2: params.DateDiff2 || '',
|
||||
OrderType: type,
|
||||
});
|
||||
const res = errcode !== 0 ? _typeRes : (result || _typeRes);
|
||||
const rows1Map = res.ordercount1.reduce((a, row1) => ({ ...a, [row1.OrderTypeSN]: row1 }), {});
|
||||
const rows2Map = res.ordercount2.reduce((a, row2) => ({ ...a, [row2.OrderTypeSN]: row2 }), {});
|
||||
|
||||
const mixRow1 = res.ordercount1.map((row1) => ({ ...row1, diff: rows2Map[row1.OrderTypeSN] || {} }));
|
||||
// Diff: elements in rows2 but not in rows1
|
||||
const diff = [...new Set(Object.keys(rows2Map).filter((x) => !new Set(Object.keys(rows1Map)).has(x)))];
|
||||
mixRow1.push(...diff.map((sn) => ({ diff: rows2Map[sn], OrderType: rows2Map[sn].OrderType, OrderTypeSN: rows2Map[sn].OrderTypeSN })));
|
||||
|
||||
return { ...res, ordercount1: mixRow1 };
|
||||
};
|
||||
|
||||
const _detailRes = { ordercount1: [], ordercount2: [] };
|
||||
export const fetchBizOrderDetailByType = async (params, type = '', typeVal = '', orderContent = 'detail') => {
|
||||
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCountByType_Sub_biz', {
|
||||
...defaultParams,
|
||||
SubOrderType: orderContent,
|
||||
...params,
|
||||
COLI_ApplyDate1: params.Date1,
|
||||
COLI_ApplyDate2: params.Date2,
|
||||
COLI_ApplyDateOld1: params.DateDiff1 || '',
|
||||
COLI_ApplyDateOld2: params.DateDiff2 || '',
|
||||
OrderType: type,
|
||||
OrderType_val: typeVal,
|
||||
});
|
||||
const res = errcode !== 0 ? _detailRes : (result || _detailRes);
|
||||
const dateStr = [params.Date1, params.Date2].map((d) => d.substring(0, 10)).join('~');
|
||||
const dateDiffStr = isEmpty(params.DateDiff1) ? '' : [params.DateDiff1, params.DateDiff2].map((d) => d.substring(0, 10)).join('~');
|
||||
const ret = [
|
||||
{ dateRangeStr: dateStr, data: res.ordercount1 },
|
||||
{ dateRangeStr: dateDiffStr, data: res.ordercount2 },
|
||||
];
|
||||
return ret;
|
||||
};
|
||||
|
||||
const calculateLineData = (value, data, avg1) => {
|
||||
const groupByDate = data.reduce((r, v) => {
|
||||
(r[v.ApplyDate] || (r[v.ApplyDate] = [])).push(v);
|
||||
return r;
|
||||
}, {});
|
||||
const _data = Object.keys(groupByDate)
|
||||
.reduce((r, _d) => {
|
||||
const xAxisGroup = groupByDate[_d].reduce((a, v) => {
|
||||
(a[v.groups] || (a[v.groups] = [])).push(v);
|
||||
return a;
|
||||
}, {});
|
||||
Object.keys(xAxisGroup).map((_group) => {
|
||||
const summaryVal = xAxisGroup[_group].reduce((rows, row) => rows + row.orderCount, 0);
|
||||
r.push({ ...xAxisGroup[_group][0], orderCount: summaryVal });
|
||||
return _group;
|
||||
});
|
||||
return r;
|
||||
}, [])
|
||||
.map((row) => ({ xField: row.ApplyDate, yField: row.orderCount, seriesField: row.groups }));
|
||||
return { lines: _data, dateRadioValue: value, avgLineValue: avg1 };
|
||||
};
|
||||
|
||||
export const orderCountDataMapper = { 'data1': 'ordercount1', data2: 'ordercount2' };
|
||||
export const orderCountDataFieldMapper = { 'dateKey': 'ApplyDate', 'valueKey': 'orderCount', 'seriesKey': 'WebCode', _f: 'sum' };
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
const initialState = {
|
||||
loading: false,
|
||||
typeLoading: false,
|
||||
searchValues: {
|
||||
DateType: { key: 'applyDate', label: '提交日期' },
|
||||
WebCode: { key: 'all', label: '所有来源' },
|
||||
IncludeTickets: { key: '1', label: '含门票' },
|
||||
DepartmentList: groupsMappedByCode.CTX, // { key: 'All', label: '所有来源' }, //
|
||||
},
|
||||
searchValuesToSub: {
|
||||
DateType: 'applyDate',
|
||||
WebCode: 'all',
|
||||
IncludeTickets: '1',
|
||||
DepartmentList: -1, // -1: All
|
||||
},
|
||||
|
||||
activeTab: 'servicetype',
|
||||
activeDateGroupRadio: 'day',
|
||||
|
||||
orderCountDataRaw: {},
|
||||
orderCountDataLines: [],
|
||||
avgLineValue: 0,
|
||||
|
||||
orderCountDataByType: {},
|
||||
|
||||
// 二级页面
|
||||
orderCountDataRawSub: {},
|
||||
orderCountDataLinesSub: [],
|
||||
avgLineValueSub: 0,
|
||||
activeDateGroupRadioSub: 'day',
|
||||
orderDetails: [],
|
||||
};
|
||||
|
||||
const useBizOrderStore = create(
|
||||
devtools(
|
||||
immer((set, get) => ({
|
||||
...initialState,
|
||||
reset: () => set(initialState),
|
||||
|
||||
setLoading: (loading) => set({ loading }),
|
||||
setTypeLoading: (typeLoading) => set({ typeLoading }),
|
||||
|
||||
setSearchValues: (obj, values) => set((state) => ({ searchValues: values, searchValuesToSub: obj })),
|
||||
setSearchValuesToSub: (values) => set((state) => ({ searchValuesToSub: values })),
|
||||
setActiveTab: (tab) => set({ activeTab: tab }),
|
||||
|
||||
setOrderCountDataLines: (data) => set({ orderCountDataLines: data }),
|
||||
setOrderCountDataByType: (type, data) =>
|
||||
set((state) => {
|
||||
state.orderCountDataByType[type] = data;
|
||||
}),
|
||||
|
||||
// data ----
|
||||
onChangeDateGroup: (value, data, avg1) => {
|
||||
const { lines, dateRadioValue, avgLineValue } = calculateLineData(value, data, avg1);
|
||||
set({ orderCountDataLines: lines, avgLineValue, activeDateGroupRadio: dateRadioValue });
|
||||
},
|
||||
onChangeDateGroupSub: (value, data, avg1) => {
|
||||
const { lines, dateRadioValue, avgLineValue } = calculateLineData(value, data, avg1);
|
||||
set({ orderCountDataLinesSub: lines, avgLineValueSub: avgLineValue, activeDateGroupRadioSub: dateRadioValue });
|
||||
},
|
||||
|
||||
// site effects
|
||||
getBizOrderCount: async (params, type, typeVal) => {
|
||||
const { setLoading } = get();
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await fetchBizOrderCount(params, type, typeVal);
|
||||
// 第一次得到数据
|
||||
const { lines, dateRadioValue, avgLineValue } = resultDataCb(res, 'day', orderCountDataMapper, orderCountDataFieldMapper, calculateLineData);
|
||||
if (isEmpty(type)) {
|
||||
// index page
|
||||
set({ orderCountDataRaw: res });
|
||||
set({ orderCountDataLines: lines, avgLineValue, activeDateGroupRadio: dateRadioValue });
|
||||
} else {
|
||||
// sub page
|
||||
set({ orderCountDataRawSub: res });
|
||||
set({ orderCountDataLinesSub: lines, avgLineValueSub: avgLineValue, activeDateGroupRadioSub: dateRadioValue });
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
|
||||
getBizOrderCount_type: async (type) => {
|
||||
const { setTypeLoading, searchValuesToSub, setOrderCountDataByType } = get();
|
||||
setTypeLoading(true);
|
||||
try {
|
||||
const res = await fetchBizOrderCountByType(type, searchValuesToSub);
|
||||
setOrderCountDataByType(type, res);
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setTypeLoading(false);
|
||||
}
|
||||
},
|
||||
|
||||
onTabChange: async (tab) => {
|
||||
const { setActiveTab, getBizOrderCount_type } = get();
|
||||
setActiveTab(tab);
|
||||
await getBizOrderCount_type(tab);
|
||||
},
|
||||
|
||||
// sub
|
||||
getBizOrderDetailByType: async (params, type, typeVal) => {
|
||||
const { setTypeLoading } = get();
|
||||
try {
|
||||
setTypeLoading(true);
|
||||
const res = await fetchBizOrderDetailByType(params, type, typeVal, 'detail');
|
||||
set({ orderDetails: res });
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setTypeLoading(false);
|
||||
}
|
||||
},
|
||||
})),
|
||||
{ name: 'bizOrder' }
|
||||
)
|
||||
);
|
||||
export default useBizOrderStore;
|
||||
@ -0,0 +1,178 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { groupsCTplus, groupsMappedByCode } from '../libs/ht';
|
||||
import { fetchJSON } from '../utils/request';
|
||||
import { HT_HOST } from '../config';
|
||||
import { resultDataCb } from '../components/DateGroupRadio/date';
|
||||
import { groupBy, isEmpty } from '../utils/commons';
|
||||
|
||||
const SERVICETYPE_TRAINSBOOKING = '2'; // 火车票服务
|
||||
const FORM_TRAINSBOOKING = 32024; // 火车票预定
|
||||
const FORM_TRAINSUPSELL = 32214; // 火车票Upsell
|
||||
|
||||
const defaultParams = { WebCode: 'all', IncludeTickets: 1, IncludeInternal: 1 };
|
||||
const _res = { ordercount1: [], ordercount2: [] };
|
||||
|
||||
export const fetchBizTrainsOrderSummaryByType = async (params, type = 'Form', typeVal = '') => {
|
||||
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCountByType_biz', {
|
||||
...defaultParams,
|
||||
...params,
|
||||
COLI_ApplyDate1: params.Date1,
|
||||
COLI_ApplyDate2: params.Date2,
|
||||
COLI_ApplyDateOld1: params.DateDiff1 || '',
|
||||
COLI_ApplyDateOld2: params.DateDiff2 || '',
|
||||
OrderType: type,
|
||||
});
|
||||
const res = errcode !== 0 ? _res : result || _res;
|
||||
const dateStr = [params.Date1, params.Date2].map((d) => d.substring(0, 10)).join('~');
|
||||
const ret = [
|
||||
{ dateRangeStr: dateStr, data: res.ordercount1.find((row) => row.OrderTypeSN === typeVal) },
|
||||
// { dateRangeStr: dateDiffStr, data: res.ordercount2.find((row) => row.OrderTypeSN === typeVal) },
|
||||
];
|
||||
const dateDiffStr = isEmpty(params.DateDiff1) ? '' : [params.DateDiff1, params.DateDiff2].map((d) => d.substring(0, 10)).join('~');
|
||||
if (!isEmpty(dateDiffStr)) {
|
||||
ret.push({ dateRangeStr: dateDiffStr, data: res.ordercount2.find((row) => row.OrderTypeSN === typeVal) });
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* 从传统订单中获取upsell数据
|
||||
*/
|
||||
export const fetchTrainsUpsellTSummaryByType = async (params, type = 'Form', typeVal = '') => {
|
||||
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCountByType', {
|
||||
...defaultParams,
|
||||
...params,
|
||||
COLI_ApplyDate1: params.Date1,
|
||||
COLI_ApplyDate2: params.Date2,
|
||||
COLI_ApplyDateOld1: params.DateDiff1 || '',
|
||||
COLI_ApplyDateOld2: params.DateDiff2 || '',
|
||||
OrderType: type,
|
||||
});
|
||||
const res = errcode !== 0 ? _res : result || _res;
|
||||
const dateStr = [params.Date1, params.Date2].map((d) => d.substring(0, 10)).join('~');
|
||||
const ret = [
|
||||
{ dateRangeStr: dateStr, data: res.ordercount1.find((row) => row.OrderTypeSN === typeVal) },
|
||||
// { dateRangeStr: dateDiffStr, data: res.ordercount2.find((row) => row.OrderTypeSN === typeVal) },
|
||||
];
|
||||
const dateDiffStr = isEmpty(params.DateDiff1) ? '' : [params.DateDiff1, params.DateDiff2].map((d) => d.substring(0, 10)).join('~');
|
||||
if (!isEmpty(dateDiffStr)) {
|
||||
ret.push({ dateRangeStr: dateDiffStr, data: res.ordercount2.find((row) => row.OrderTypeSN === typeVal) });
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
export const fetchTrainsWithUpsellSummary = async (params) => {
|
||||
const [trains, upsell] = await Promise.all([
|
||||
// fetchBizTrainsOrderSummaryByType(params, 'Form', FORM_TRAINSBOOKING), // todo: 换成servicetype=2
|
||||
fetchBizTrainsOrderSummaryByType(params, 'servicetype', SERVICETYPE_TRAINSBOOKING),
|
||||
fetchTrainsUpsellTSummaryByType(params, 'Form', FORM_TRAINSUPSELL),
|
||||
]);
|
||||
if (!isEmpty(trains[1])) {
|
||||
trains[0].data.diff = trains[1].data;
|
||||
upsell[0].data.diff = upsell[1].data;
|
||||
}
|
||||
const mergedMap = [...trains, ...upsell].reduce((acc, currentItem, currentIndex) => {
|
||||
const dateRange = currentItem.dateRangeStr;
|
||||
|
||||
if (acc[dateRange]) {
|
||||
acc[dateRange].data.push(currentItem.data);
|
||||
|
||||
for (const key in currentItem) {
|
||||
if (key !== 'dateRange' && key !== 'data') {
|
||||
acc[dateRange][key] = currentItem[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
acc[dateRange] = {
|
||||
...currentItem,
|
||||
data: [currentItem.data],
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
return Object.values(mergedMap);
|
||||
// return groupBy([...trains, ...upsell], 'dateRangeStr');
|
||||
};
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
const initialState = {
|
||||
loading: false,
|
||||
typeLoading: false,
|
||||
searchValues: {
|
||||
DateType: { key: 'applyDate', label: '提交日期' },
|
||||
WebCode: { key: 'all', label: '所有来源' },
|
||||
IncludeTickets: { key: '1', label: '含门票' },
|
||||
DepartmentList: groupsCTplus, // groupsMappedByCode.CTX, // { key: 'All', label: '所有来源' }, //
|
||||
},
|
||||
searchValuesToSub: {
|
||||
DateType: 'applyDate',
|
||||
WebCode: 'all',
|
||||
IncludeTickets: '1',
|
||||
DepartmentList: -1, // -1: All
|
||||
},
|
||||
|
||||
trainsOrdersSummary: [],
|
||||
|
||||
activeTab: 'Form',
|
||||
activeDateGroupRadio: 'day',
|
||||
|
||||
orderCountDataRaw: {},
|
||||
orderCountDataLines: [],
|
||||
avgLineValue: 0,
|
||||
|
||||
orderCountDataByType: {},
|
||||
|
||||
// 二级页面
|
||||
orderCountDataRawSub: {},
|
||||
orderCountDataLinesSub: [],
|
||||
avgLineValueSub: 0,
|
||||
activeDateGroupRadioSub: 'day',
|
||||
orderDetails: [],
|
||||
};
|
||||
|
||||
const useTrainsStore = create(
|
||||
devtools(
|
||||
immer((set, get) => ({
|
||||
...initialState,
|
||||
reset: () => set(initialState),
|
||||
|
||||
setLoading: (loading) => set({ loading }),
|
||||
setTypeLoading: (typeLoading) => set({ typeLoading }),
|
||||
|
||||
setSearchValues: (obj, values) => set((state) => ({ searchValues: values, searchValuesToSub: obj })),
|
||||
setSearchValuesToSub: (values) => set((state) => ({ searchValuesToSub: values })),
|
||||
setActiveTab: (tab) => set({ activeTab: tab }),
|
||||
|
||||
setOrderCountDataLines: (data) => set({ orderCountDataLines: data }),
|
||||
setOrderCountDataByType: (type, data) =>
|
||||
set((state) => {
|
||||
state.orderCountDataByType[type] = data;
|
||||
}),
|
||||
|
||||
// data ----
|
||||
|
||||
// site effects
|
||||
getTrainsWithUpsell: async (params, type, typeVal) => {
|
||||
const { setLoading } = get();
|
||||
setLoading(true);
|
||||
set({ trainsOrdersSummary: [] });
|
||||
try {
|
||||
const res = await fetchTrainsWithUpsellSummary(params);
|
||||
set({ trainsOrdersSummary: res });
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
|
||||
// sub
|
||||
})),
|
||||
{ name: 'trains' }
|
||||
)
|
||||
);
|
||||
export default useTrainsStore;
|
||||
Loading…
Reference in New Issue