feat: 帕累托分析

main
Lei OT 5 months ago
parent 5a57c904e3
commit 7f69902d2f

@ -0,0 +1,125 @@
import React from 'react';
import { DualAxes } from '@ant-design/charts';
import { fixTo2Decimals, fixTo4Decimals, fixTo1Decimals, groupBy } from '../utils/commons';
const ParetoChart = ({ data, xField, yField, thresholds = { A: 80, B: 90 }, title = '帕累托分析', showCategory = true, showThresholds = true, yFieldAlias }) => {
// 1.
const cleanedData = data
.filter((d) => d[yField])
.sort((a, b) => b[yField] - a[yField]);
// 2. &
const total = cleanedData.reduce((sum, d) => sum + d[yField], 0);
let cumulative = 0;
const barData = cleanedData.map((d) => {
cumulative += d[yField];
const ratio = (cumulative / total) * 100;
let category = 'C';
if (ratio <= thresholds.A) category = 'A';
else if (ratio <= thresholds.B) category = 'B';
return { [xField]: d[xField], [yField]: d[yField], Category: category };
});
// Fixed the groupBy usage and barColor assignment
const barColor = (() => {
const grouped = groupBy(barData, 'Category');
const ret = new Map();
for (const [category, items] of Object.entries(grouped)) {
ret.set(
category,
items.map((e) => e[xField])
);
}
return ret;
})();
const lineData = barData.map((d, idx) => {
const cumulativeValue = barData.slice(0, idx + 1).reduce((sum, x) => sum + x[yField], 0);
return { [xField]: d[xField], Cumulative: fixTo2Decimals((cumulativeValue / total) * 100 )};
});
// 3. DualAxes
const config = {
data: [barData, lineData],
xField,
yField: [yField, 'Cumulative'],
meta: {
[yField]: {
alias: yFieldAlias || yField,
formatter: (v) => v > 1_0000 ? `${fixTo1Decimals((v || 0) / 10000)}` : v,
},
Cumulative: {
alias: '累计百分比',
formatter: (val) => `${val}%`,
},
},
yAxis: {
//
Cumulative: {
// min: 0,
label: {
formatter: (val) => `${val}%`,
},
},
},
geometryOptions: [
{
geometry: 'column',
colorField: 'Category',
color: showCategory
? (x) => {
const xText = x[xField];
let thisC = null;
for (const [Category, Xs] of barColor) {
if (Xs.includes(xText)) {
thisC = Category;
}
}
if (thisC === 'A') return '#52c41a';
if (thisC === 'B') return '#faad14';
return '#5B8FF9';
}
: '#5B8FF9',
},
{
geometry: 'line',
color: '#ff4d4f',
// smooth: true,
lineStyle: {
lineWidth: 2,
},
},
],
annotations: showThresholds
? {
Cumulative: [
{
type: 'line',
start: ['min', thresholds.A],
end: ['max', thresholds.A],
style: { stroke: '#52c41a', lineDash: [4, 4], lineWidth: 2 },
// text: { content: `${thresholds.A}% `, position: 'end' },
},
{
type: 'line',
start: ['min', thresholds.B],
end: ['max', thresholds.B],
style: { stroke: '#faad14', lineDash: [4, 4], lineWidth: 2 },
// text: { content: `${thresholds.B}% `, position: 'end' },
},
],
}
: {},
tooltip: { shared: true },
title: { visible: true, text: title },
};
return (
<>
<h3>{title}</h3>
<DualAxes {...config} />
</>
);
};
export default ParetoChart;

@ -2,7 +2,7 @@ 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, GoogleOutlined, GooglePlusOutlined } from "@ant-design/icons";
import { stores_Context } from "../config";
import { Line, Pie } from "@ant-design/charts";
import { Line, Pie, } from "@ant-design/charts";
import { observer } from "mobx-react";
import * as config from "../config";
import { NavLink } from "react-router-dom";
@ -11,6 +11,7 @@ import { utils, writeFileXLSX } from "xlsx";
import DateGroupRadio from '../components/DateGroupRadio';
import SearchForm from './../components/search/SearchForm';
import { TableExportBtn } from './../components/Data';
import ParetoChart from "../components/Pareto";
class Orders extends Component {
static contextType = stores_Context;
@ -349,7 +350,7 @@ class Orders extends Component {
dataSource: table_data.dataSource,
columns: table_data.columns,
size: 'small',
pagination: false,
// pagination: false,
scroll: { x: (100*(table_data.columns.length)) },
loading: orders_store.loading,
};
@ -503,10 +504,12 @@ class Orders extends Component {
<Col sm={24} lg={12}>
<Pie {...pie_config} data={pie_data} innerRadius={0.6} statistic={{title: false,content:{content:'数量'}}} />
<Pie {...pie_config} data={pie_data} angleField='YJLYx' innerRadius={0.6} statistic={{title: false,content:{content:'预计毛利'}}} />
{['Form', 'Product', 'Country', 'line'].includes(orders_store.active_tab_key) && <ParetoChart data={pie_data} xField='OrderType' yField='YJLYx' yFieldAlias='毛利' />}
</Col>
<Col sm={24} lg={12}>
<Pie {...pie_config} data={pie_data2} innerRadius={0.6} statistic={{title: false,content:{content:'数量'}}} />
<Pie {...pie_config} data={pie_data2} angleField='YJLYx' innerRadius={0.6} statistic={{title: false,content:{content:'预计毛利'}}} />
{['Form', 'Product', 'Country', 'line'].includes(orders_store.active_tab_key) && <ParetoChart data={pie_data2} xField='OrderType' yField='YJLYx' yFieldAlias='毛利' />}
</Col>
</Row>
</Col>

Loading…
Cancel
Save