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.
367 lines
13 KiB
React
367 lines
13 KiB
React
|
3 months ago
|
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;
|