feat: 分销订单统计

main
Lei OT 3 months ago
parent a2b920b313
commit 559ba14e35

@ -58,6 +58,8 @@ import Hotel from './views/Hotel';
import HostCaseCount from './views/HostCaseCount'; import HostCaseCount from './views/HostCaseCount';
import TrainsUpsell from './views/biz/reports/TrainsUpsell'; import TrainsUpsell from './views/biz/reports/TrainsUpsell';
import HostCaseReport from './views/HostCaseReport'; import HostCaseReport from './views/HostCaseReport';
import ToBOrder from './views/toB/ToBOrder';
import ToBOrderSub from './views/toB/ToBOrderSub';
const App = () => { const App = () => {
const { Content, Footer, Sider, } = Layout; const { Content, Footer, Sider, } = Layout;
@ -93,6 +95,11 @@ const App = () => {
label: <NavLink to="/orders">订单数据</NavLink>, label: <NavLink to="/orders">订单数据</NavLink>,
// icon: <FileProtectOutlined />, // icon: <FileProtectOutlined />,
}, },
{
key: 'tob_orders',
label: <NavLink to="/tob_orders">分销订单</NavLink>,
// icon: <FileProtectOutlined />,
},
{ {
key: 22, key: 22,
label: <NavLink to="/dashboard">仪表盘</NavLink>, label: <NavLink to="/dashboard">仪表盘</NavLink>,
@ -260,6 +267,8 @@ const App = () => {
<Route path="/:page/pivot" element={<DataPivot />} /> <Route path="/:page/pivot" element={<DataPivot />} />
<Route path="/orders/meeting-2024-GH" element={<Meeting2024GH />} /> <Route path="/orders/meeting-2024-GH" element={<Meeting2024GH />} />
<Route path="/orders/meeting-2025-GH" element={<Meeting2025GH />} /> <Route path="/orders/meeting-2025-GH" element={<Meeting2025GH />} />
<Route path="/tob_orders" element={<ToBOrder />} />
<Route path="/tob_orders_sub/:ordertype/:ordertype_sub/:ordertype_title" element={<ToBOrderSub />} />
<Route path="/biz_orders" element={<BizOrder />} /> <Route path="/biz_orders" element={<BizOrder />} />
<Route path="/biz_orders_sub/:ordertype/:ordertype_sub/:ordertype_title" element={<BizOrderSub />} /> <Route path="/biz_orders_sub/:ordertype/:ordertype_sub/:ordertype_title" element={<BizOrderSub />} />
<Route path="/trains" element={<TrainsUpsell />} /> <Route path="/trains" element={<TrainsUpsell />} />

@ -0,0 +1,366 @@
import { useContext } from 'react';
import { Row, Col, Tabs, Table, Divider, Spin, Checkbox, Space } from 'antd';
import { ContainerOutlined, BlockOutlined, SmileOutlined, MobileOutlined, CustomerServiceOutlined, IeOutlined } from '@ant-design/icons';
import { Line, Pie } from '@ant-design/charts';
import { NavLink } from 'react-router-dom';
import * as comm from '@haina/utils-commons';
import DateGroupRadio from '../../components/DateGroupRadio';
import SearchForm from '../../components/search/SearchForm';
import { TableExportBtn } from '../../components/Data';
import { RenderVSDataCell } from './../../components/Data';
import { observer } from 'mobx-react';
import { toJS } from 'mobx';
import { stores_Context } from '../../config';
import { useShallow } from 'zustand/shallow';
import useToBOrderStore, { orderCountDataMapper, orderCountDataFieldMapper } from '../../zustand/ToBOrder';
const ToBOrder = observer(() => {
const { date_picker_store: searchFormStore } = useContext(stores_Context);
const [searchValues, setSearchValues] = useToBOrderStore(useShallow((state) => [state.searchValues, state.setSearchValues]));
const [activeTab, setActiveTab] = useToBOrderStore(useShallow((state) => [state.activeTab, state.setActiveTab]));
const [loading, typeLoading, onTabChange] = useToBOrderStore(useShallow((state) => [state.loading, state.typeLoading,state.onTabChange]));
const orderCountDataRaw = useToBOrderStore((state) => state.orderCountDataRaw);
const [orderCountDataLines, avgLineValue] = useToBOrderStore(useShallow((state) => [state.orderCountDataLines, state.avgLineValue]));
const [onChangeDateGroup, activeDateGroupRadio] = useToBOrderStore(useShallow((state) => [state.onChangeDateGroup, state.activeDateGroupRadio]));
const orderCountDataByType = useToBOrderStore((state) => state.orderCountDataByType);
const result = orderCountDataByType[activeTab] || {};
const getToBOrderCount = useToBOrderStore((state) => state.getToBOrderCount);
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} \t {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={`/tob_orders_sub/${activeTab}/${record.OrderTypeSN}/${encodeURIComponent(record.OrderType)}`}>{text}</NavLink>,
},
],
},
{
title: '数量',
children: [
{
title: (
<RenderVSDataCell
showDiffData={showDiff}
data1={result.ordercountTotal1?.OrderCount}
data2={result.ordercountTotal2?.OrderCount}
diffPercent={result.ordercountTotal1?.OrderCount_vs}
diffData={result.ordercountTotal1?.OrderCount_diff}
/>
),
titleX: [result.ordercountTotal1?.OrderCount, result.ordercountTotal2?.OrderCount].join(' vs '),
dataIndex: 'OrderCount',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.OrderCount} diffPercent={r.OrderCount_vs} diffData={r.OrderCount_diff} />,
},
],
},
{
title: '成交数',
children: [
{
title: (
<RenderVSDataCell
showDiffData={showDiff}
data1={result.ordercountTotal1?.CJCount}
data2={result.ordercountTotal2?.CJCount}
diffPercent={result.ordercountTotal1?.CJCount_vs}
diffData={result.ordercountTotal1?.CJCount_diff}
/>
),
titleX: [result.ordercountTotal1?.CJCount, result.ordercountTotal2?.CJCount].join(' vs '),
dataIndex: 'CJCount',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJCount} diffPercent={r.CJCount_vs} diffData={r.CJCount_diff} />,
},
],
},
{
title: '成交人数',
children: [
{
title: (
<RenderVSDataCell
showDiffData={showDiff}
data1={result.ordercountTotal1?.CJPersonNum}
data2={result.ordercountTotal2?.CJPersonNum}
diffPercent={result.ordercountTotal1?.CJPersonNum_vs}
diffData={result.ordercountTotal1?.CJPersonNum_diff}
/>
),
titleX: [result.ordercountTotal1?.CJPersonNum, result.ordercountTotal2?.CJPersonNum].join(' vs '),
dataIndex: 'CJPersonNum',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJPersonNum} diffPercent={r.CJPersonNum_vs} diffData={r.CJPersonNum_diff} />,
},
],
},
{
title: '成交率',
children: [
{
title: (
<RenderVSDataCell
showDiffData={showDiff}
data1={result.ordercountTotal1?.CJrate}
data2={result.ordercountTotal2?.CJrate}
diffPercent={result.ordercountTotal1?.CJrate_vs}
diffData={result.ordercountTotal1?.CJrate_diff}
/>
),
titleX: [result.ordercountTotal1?.CJrate, result.ordercountTotal2?.CJrate].join(' vs '),
dataIndex: 'CJrate',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJrate} diffPercent={r.CJrate_vs} diffData={r.CJrate_diff} />,
},
],
},
{
title: '成交毛利(预计)',
children: [
{
title: (
<RenderVSDataCell
showDiffData={showDiff}
data1={result.ordercountTotal1?.YJLY}
data2={result.ordercountTotal2?.YJLY}
diffPercent={result.ordercountTotal1?.YJLY_vs}
diffData={result.ordercountTotal1?.YJLY_diff}
/>
),
titleX: [result.ordercountTotal1?.YJLY, result.ordercountTotal2?.YJLY].join(' vs '),
dataIndex: 'YJLY',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.YJLY} diffPercent={r.YJLY_vs} diffData={r.YJLY_diff} />,
},
],
},
{
title: '单个订单价值',
children: [
{
title: (
<RenderVSDataCell
showDiffData={showDiff}
data1={result.ordercountTotal1?.Ordervalue}
data2={result.ordercountTotal2?.Ordervalue}
diffPercent={result.ordercountTotal1?.Ordervalue_vs}
diffData={result.ordercountTotal1?.Ordervalue_diff}
/>
),
titleX: [result.ordercountTotal1?.Ordervalue, result.ordercountTotal2?.Ordervalue].join(' vs '),
dataIndex: 'Ordervalue',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.Ordervalue} diffPercent={r.Ordervalue_vs} diffData={r.Ordervalue_diff} />,
},
],
},
],
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', 'WebCode','IncludeTickets', 'DepartmentList', '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);
getToBOrderCount(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: 'customer_types',
label: (
<span>
<CustomerServiceOutlined />
分销客户
</span>
),
},
].map((ele) => {
return {
...ele,
children: (
<>
<Table sticky key={`table_to_xlsx_${ele.key}`} {...tableProps} loading={typeLoading} />
<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={typeLoading}>
<Row>
<Col sm={24} lg={12}>
<Pie {...pieConfig} data={result?.ordercount1 || []} innerRadius={0.6} statistic={{title: false,content:{content:'数量'}}} />
<Pie {...pieConfig} data={result?.ordercount1 || []} angleField='YJLYx' innerRadius={0.6} statistic={{title: false,content:{content:'预计毛利'}}} />
</Col>
{showDiff && <Col sm={24} lg={12}>
<Pie {...pieConfig} data={result?.ordercount2 || []} innerRadius={0.6} statistic={{title: false,content:{content:'数量'}}} />
<Pie {...pieConfig} data={result?.ordercount2 || []} angleField='YJLYx' innerRadius={0.6} statistic={{title: false,content:{content:'预计毛利'}}} />
</Col>}
</Row>
</Spin>
</div>
</>
);
});
export default ToBOrder;

@ -0,0 +1,280 @@
import { useContext, useEffect } from 'react';
import { Row, Col, Tabs, Table, Divider, Spin, Space } from 'antd';
import { ContainerOutlined, BlockOutlined, SmileOutlined, MobileOutlined } from '@ant-design/icons';
import { Line } from '@ant-design/charts';
import { NavLink, useParams } from 'react-router-dom';
import { getWeek } from '@haina/utils-commons';
import DateGroupRadio from '../../components/DateGroupRadio';
import SearchForm from '../../components/search/SearchForm';
import { TableExportBtn } from '../../components/Data';
import { observer } from 'mobx-react';
import { stores_Context } from '../../config';
import { useShallow } from 'zustand/shallow';
import useToBOrderStore, { orderCountDataMapper, orderCountDataFieldMapper } from '../../zustand/ToBOrder';
//
const addLineBreaksAtCommas = (text) => {
if (!text) return '';
return text.replace(/&amp;/g, '&').replace(//g, '\n').replace(/,/g, ',\n').replace(//g, '\n').replace(/;/g, ';\n');
};
const OrderDetailTable = ({ caption, dataSource, loading, ...props }) => {
const columns = [
{
title: '订单号',
dataIndex: 'COLI_ID',
key: 'COLI_ID',
},
{
title: '网站',
dataIndex: 'COLI_WebCode',
key: 'COLI_WebCode',
},
{
title: '成行',
dataIndex: 'COLI_Success',
key: 'COLI_Success',
render: (text, record) => <span>{text == 1 ? '是' : '否'}</span>,
sorter: (a, b) => b.COLI_Success - a.COLI_Success,
},
// {
// title: "(//)",
// dataIndex: "COLI_PersonNum",
// key: "COLI_PersonNum",
// render: (text, record) => (
// <span>
// {record.COLI_PersonNum}/{record.COLI_ChildNum}/{record.COLI_BabyNum}
// </span>
// ),
// },
{
title: '预计利润',
dataIndex: 'CGI_YJLY',
key: 'CGI_YJLY',
},
{
title: '预定时间',
dataIndex: 'COLI_ApplyDate',
key: 'COLI_ApplyDate',
},
{
title: '出发日期',
dataIndex: 'CGI_ArriveDate',
key: 'CGI_ArriveDate',
},
// {
// title: '',
// dataIndex: 'COLI_CustomerRequest',
// key: 'COLI_CustomerRequest',
// ellipsis: true,
// },
{
title: '订单内容',
dataIndex: 'COLI_OrderDetailText',
key: 'COLI_OrderDetailText',
ellipsis: true,
},
Table.EXPAND_COLUMN,
];
return (
<div>
<Divider orientation="left" plain>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '10px' }}>
<div>{caption}</div>
<TableExportBtn label={caption} columns={columns} dataSource={dataSource} />
</div>
</Divider>
<Table
id="table_to_xlsx_form"
dataSource={dataSource}
columns={columns}
loading={loading}
size="small"
// pagination={false}
rowKey={(record) => record.key}
expandable={{
expandedRowRender: (record) => (
<pre style={{ whiteSpace: 'pre-wrap', wordWrap: 'break-word', fontSize: '16px' }}>
{/* <Divider orientation="left" plain>
客户需求
</Divider>
{record.COLI_CustomerRequest} */}
<Divider orientation="left" plain>
订单内容
</Divider>
{record.COLI_OrderDetailText}
</pre>
),
}}
/>
</div>
);
};
const ToBOrderSub = observer(({ ...props }) => {
const { ordertype, ordertype_sub, ordertype_title } = useParams();
const { date_picker_store: searchFormStore } = useContext(stores_Context);
const [searchValues, setSearchValues] = useToBOrderStore(useShallow((state) => [state.searchValues, state.setSearchValues]));
const [searchValuesToSub] = useToBOrderStore(useShallow((state) => [state.searchValuesToSub]));
const [loading, typeLoading] = useToBOrderStore(useShallow((state) => [state.loading, state.typeLoading]));
const orderCountDataRawSub = useToBOrderStore((state) => state.orderCountDataRawSub);
const [orderCountDataLinesSub, avgLineValueSub] = useToBOrderStore(useShallow((state) => [state.orderCountDataLinesSub, state.avgLineValueSub]));
const [onChangeDateGroupSub, activeDateGroupRadioSub] = useToBOrderStore(useShallow((state) => [state.onChangeDateGroupSub, state.activeDateGroupRadioSub]));
const orderDetails = useToBOrderStore((state) => state.orderDetails);
const getToBOrderCount = useToBOrderStore((state) => state.getToBOrderCount);
const getToBOrderDetailByType = useToBOrderStore((state) => state.getToBOrderDetailByType);
useEffect(() => {
getToBOrderCount(searchValuesToSub, ordertype, ordertype_sub);
getToBOrderDetailByType(searchValuesToSub, ordertype, ordertype_sub);
}, []);
const avg_line_y = Math.round(avgLineValueSub);
const lineConfig = {
data: orderCountDataLinesSub,
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 = orderCountDataLinesSub.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 (activeDateGroupRadioSub) {
case 'day':
ret = `${title} ${getWeek(datum.xField)}`; //
break;
default:
break;
}
return ret;
},
},
smooth: true,
};
const tab_items = [
{
key: 'detail',
label: (
<span>
<ContainerOutlined />
订单内容
</span>
),
title: '订单内容',
children: (
<>
<Space direction="vertical">
<OrderDetailTable caption={orderDetails[0]?.dateRangeStr} dataSource={orderDetails[0]?.data || []} loading={typeLoading} />
<OrderDetailTable caption={orderDetails[1]?.dateRangeStr} dataSource={orderDetails[1]?.data || []} loading={typeLoading} />
</Space>
</>
),
},
];
return (
<div>
<Row gutter={{ sm: 16, lg: 32 }} className={searchFormStore.siderBroken ? '' : 'sticky-top'}>
<Col md={24} lg={12} xxl={14}>
<NavLink to={`/tob_orders`}>返回</NavLink>
</Col>
<Col className="gutter-row" span={24}>
<SearchForm
defaultValue={{
initialValue: {
...searchFormStore.formValues,
...searchValues,
},
//
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates'],
fieldProps: {
DepartmentList: { show_all: false, mode: 'multiple' },
WebCode: { show_all: false, mode: 'multiple' },
// dates: { hide_vs: true },
},
}}
onSubmit={(_err, obj, form, str) => {
setSearchValues(obj, form);
getToBOrderCount(obj, ordertype, ordertype_sub);
getToBOrderDetailByType(obj, ordertype, ordertype_sub);
}}
/>
</Col>
</Row>
<Row gutter={[16, { xs: 8, sm: 16, md: 24, lg: 32 }]}>
<Col span={24} style={{ textAlign: 'right' }}>
<DateGroupRadio
visible={orderCountDataLinesSub.length !== 0}
dataRaw={orderCountDataRawSub}
onChange={onChangeDateGroupSub}
value={activeDateGroupRadioSub}
dataMapper={orderCountDataMapper}
fieldMapper={orderCountDataFieldMapper}
/>
</Col>
<Col className="gutter-row" span={24}>
<Spin spinning={loading}>
<Line {...lineConfig} />
</Spin>
</Col>
<Col className="gutter-row" span={24}>
<Tabs
activeKey={'detail'}
// onChange={onTabsChange}
items={tab_items}
/>
</Col>
</Row>
</div>
);
});
export default ToBOrderSub;

@ -0,0 +1,241 @@
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { groupsMappedByCode } from '../libs/ht';
import { fetchJSON } from '@haina/utils-request';
import { HT_HOST } from '../config';
import { resultDataCb } from '../components/DateGroupRadio/date';
import { isEmpty, price_to_number } from '@haina/utils-commons';
/**
* 分销(ToB)订单
*/
const defaultParams = {};
export const fetchToBOrderCount = async (params, type = '', typeVal = '') => {
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCount_FX', {
...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 fetchToBOrderCountByType = async (type, params) => {
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCountByType_FX', {
...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, YJLYx: price_to_number(row1.YJLY)} }), {});
const rows2Map = res.ordercount2.reduce((a, row2) => ({ ...a, [row2.OrderTypeSN]: {...row2, YJLYx: price_to_number(row2.YJLY)} }), {});
const mixRow1 = res.ordercount1.map((row1) => ({ ...row1, YJLYx: price_to_number(row1.YJLY), 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, ordercount2: res.ordercount2.map((row1) => ({ ...row1, YJLYx: price_to_number(row1.YJLY), })) };
};
const _detailRes = { ordercount1: [], ordercount2: [] };
export const fetchToBOrderDetailByType = async (params, type = '', typeVal = '', orderContent = 'detail') => {
const { errcode, errmsg, ...result } = await fetchJSON(HT_HOST + '/service-web/QueryData/GetOrderCountByType_Sub_FX', {
...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': 'id', _f: 'sum' };
/**
* --------------------------------------------------------------------------------------------------------
*/
const initialState = {
loading: false,
typeLoading: false,
searchValues: {
DateType: { key: 'applyDate', label: '提交日期' },
WebCode: { key: 'all', label: '所有来源' },
IncludeTickets: { key: '1', label: '含门票' },
DepartmentList: groupsMappedByCode.GH, // { key: 'All', label: '所有来源' }, //
},
searchValuesToSub: {
DateType: 'applyDate',
WebCode: 'all',
IncludeTickets: '1',
DepartmentList: -1, // -1: All
},
activeTab: 'customer_types',
activeDateGroupRadio: 'day',
orderCountDataRaw: {},
orderCountDataLines: [],
avgLineValue: 0,
orderCountDataByType: {},
// 二级页面
orderCountDataRawSub: {},
orderCountDataLinesSub: [],
avgLineValueSub: 0,
activeDateGroupRadioSub: 'day',
orderDetails: [],
};
const useToBOrderStore = 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
getToBOrderCount: async (params, type, typeVal) => {
const { setLoading } = get();
setLoading(true);
try {
const res = await fetchToBOrderCount(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) {
console.error(error);
} finally {
setLoading(false);
}
},
getToBOrderCount_type: async (type) => {
const { setTypeLoading, searchValuesToSub, setOrderCountDataByType } = get();
setTypeLoading(true);
try {
const res = await fetchToBOrderCountByType(type, searchValuesToSub);
setOrderCountDataByType(type, res);
} catch (error) {
console.error(error);
} finally {
setTypeLoading(false);
}
},
onTabChange: async (tab) => {
const { setActiveTab, getToBOrderCount_type } = get();
setActiveTab(tab);
await getToBOrderCount_type(tab);
},
// sub
getToBOrderDetailByType: async (params, type, typeVal) => {
const { setTypeLoading } = get();
try {
setTypeLoading(true);
const res = await fetchToBOrderDetailByType(params, type, typeVal, 'detail');
set({ orderDetails: res });
} catch (error) {
console.error(error);
} finally {
setTypeLoading(false);
}
},
})),
{ name: 'ToBOrder' }
)
);
export default useToBOrderStore;
Loading…
Cancel
Save