feat: home: 堆积柱状图. 调整组件配置

feature/2.0-sales-trade
Lei OT 2 years ago
parent 230ec4be81
commit 9a5e8c9aac

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Bullet } from '@ant-design/plots'; import { Bullet } from '@ant-design/plots';
import { sortBy } from '../utils/commons'; import { sortBy, merge } from '../utils/commons';
import { dataFieldAlias } from './../libs/ht'; import { dataFieldAlias } from './../libs/ht';
export default observer((props) => { export default observer((props) => {
@ -24,7 +24,7 @@ export default observer((props) => {
return () => {}; return () => {};
}, [extProps.measureField, dataSource]); }, [extProps.measureField, dataSource]);
const config = { const config = merge({
color: { color: {
range: ['#FFbcb8', '#FFe0b0', '#bfeec8'], range: ['#FFbcb8', '#FFe0b0', '#bfeec8'],
measure: '#5B8FF9', measure: '#5B8FF9',
@ -72,17 +72,17 @@ export default observer((props) => {
}, },
], ],
}, },
// alias // ? alias
tooltip: { tooltip: {
customItems: (originalItems) => { customItems: (originalItems) => {
// process originalItems, // process originalItems,
return originalItems.map((ele) => ({ ...ele, name: dataFieldAlias[ele.name]?.alias || ele.name })); return originalItems.map((ele) => ({ ...ele, name: dataFieldAlias[ele.name]?.alias || ele.name }));
}, },
}, },
}; }, extProps);
return ( return (
<> <>
<Bullet {...config} {...extProps} data={parseData} /> <Bullet {...config} data={parseData} />
</> </>
); );
}); });

@ -0,0 +1,66 @@
import { useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import { sortBy, merge } from '../utils/commons';
import { dataFieldAlias } from '../libs/ht';
import { Column } from '@ant-design/plots';
export default observer((props) => {
const { dataSource, line, ...extProps } = props;
const yMax = Math.max(line?.value || 0, ...dataSource.map((ele) => ele[extProps.yField]));
const annotationsLine = line
? [
{
type: 'text',
position: ['start', line.value],
content: `${line.label} ${line.value / 1000} K`,
// offsetX: -15,
style: {
fill: '#F4664A',
textBaseline: 'bottom',
},
},
{
type: 'line',
start: [-10, line.value],
end: ['max', line.value],
style: {
stroke: '#F4664A',
lineDash: [2, 2],
},
},
]
: [];
const config = merge({
isStack: true,
// xField: 'value',
// yField: 'year',
// seriesField: 'type',
label: {
// label
position: 'middle',
// 'left', 'middle', 'right'
//
layout: [
//
{
type: 'interval-adjust-position',
}, //
{
type: 'interval-hide-overlap',
}, //
{
type: 'adjust-color',
},
],
},
meta: {
[extProps.yField]: {
alias: dataFieldAlias[extProps.yField]?.alias || extProps.yField,
formatter: (v) => dataFieldAlias[extProps.yField]?.formatter(v) || v,
max: Math.ceil(yMax / 0.95),
},
},
annotations: [...annotationsLine],
}, extProps);
return <Column {...config} data={dataSource} />;
});

@ -1,6 +1,7 @@
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Waterfall } from '@ant-design/plots'; import { Waterfall } from '@ant-design/plots';
import { dataFieldAlias } from './../libs/ht'; import { dataFieldAlias } from './../libs/ht';
import { merge } from '../utils/commons';
export default observer((props) => { export default observer((props) => {
const { dataSource, line, title, ...extProps } = props; const { dataSource, line, title, ...extProps } = props;
@ -29,7 +30,7 @@ export default observer((props) => {
] ]
: []; : [];
const config = { const config = merge({
padding: 'auto', padding: 'auto',
appendPadding: [20, 0, 0, 0], appendPadding: [20, 0, 0, 0],
@ -50,18 +51,16 @@ export default observer((props) => {
}, },
annotations: [...annotationsLine], annotations: [...annotationsLine],
meta: { meta: {
...extProps.mergeMeta,
[extProps.yField]: { [extProps.yField]: {
...extProps.mergeMeta[extProps.yField],
max: Math.ceil(yMax / 0.95), max: Math.ceil(yMax / 0.95),
alias: dataFieldAlias[extProps.yField]?.alias || extProps.yField, alias: dataFieldAlias[extProps.yField]?.alias || extProps.yField,
formatter: (v) => dataFieldAlias[extProps.yField]?.formatter(v) || v, formatter: (v) => dataFieldAlias[extProps.yField]?.formatter(v) || v,
}, },
}, },
}; }, extProps);
return ( return (
<> <>
<Waterfall {...config} {...extProps} data={dataSource} /> <Waterfall {...config} data={dataSource} />
</> </>
); );
}); });

@ -13,7 +13,7 @@
"SumOrderKPIrate|20-100": 25, "SumOrderKPIrate|20-100": 25,
"SumPersonNum": "@integer(999,9999)", "SumPersonNum": "@integer(999,9999)",
"SumPersonNumrate|-50-1": 33, "SumPersonNumrate|-50-1": 33,
"SumPersonNumKPIrate|20-100": 33, "SumPersonNumKPIrate": 0,
"groups": "2023-05-01~2023-05-31", "groups": "2023-05-01~2023-05-31",
"key": "@increment" "key": "@increment"
}, },
@ -87,11 +87,11 @@
"result1|24": [ "result1|24": [
{ {
"groups|1": "@pick([\"inside\",\"outside\"])", "groups|1": "@pick([\"inside\",\"outside\"])",
"groupDateVal": "@date('2023-MM')",
"SumML": "@increment(1000)", "SumML": "@increment(1000)",
"SumMLVSrate": "@float(0,70,2,2)", "SumMLVSrate": "@float(0,70,2,2)",
"SumMLKPI": "@integer(1000,10000)", "SumMLKPI": "@integer(1000,10000)",
"SumMLKPIrate": "@float(0,70,2,2)", "SumMLKPIrate": "@float(0,70,2,2)",
"month": "@date('2023-MM')",
"SumOrder": "@integer(9,999)", "SumOrder": "@integer(9,999)",
"SumOrderVSrate": "@float(-20,20,0,2)", "SumOrderVSrate": "@float(-20,20,0,2)",
"SumOrderKPIrate": "@float(20,100,0,2)", "SumOrderKPIrate": "@float(20,100,0,2)",
@ -103,5 +103,26 @@
"result2": [ "result2": [
{} {}
] ]
},
"get|/service-web/QueryData/Getkpi": {
"errcode": 0,
"errmsg": "",
"loading": false,
"data": null,
"result1|10": [
{
"object|1": "@pick([\"dept\",\"sales\", \"group\"])",
"subject|1": "@pick([\"OrderCount\",\"SumML\", \"AvgML\", \"SuccessRate\"])",
"object_id": "@integer(10,100)",
"object_name": "@cname",
"date_type": "@pick([\"ConfirmDate\",\"startDate\", \"applyDate\"])",
"start_date": "@date(\"yyyy-MM-dd\")",
"end_date": "@datetime(\"yyyy-MM-dd HH:mm:ss\")",
"value": "@integer(99,9999)",
"key": "@increment"
}],
"result2": [
{}
]
} }
} }

@ -23,6 +23,9 @@ class Trade {
const summaryData = { const summaryData = {
loading: false, loading: false,
dataSource: [ dataSource: [
{ title: '成团', value: json.result1?.SumOrder, VSrate: json.result1?.SumOrderrate, KPIrate: json.result1?.SumOrderKPIrate, hasKPI: !isEmpty(json.result1?.SumOrderKPIrate) },
{ title: '毛利', value: json.result1?.SumML, VSrate: json.result1?.SumMLrate, KPIrate: json.result1?.SumMLKPIrate, hasKPI: !isEmpty(json.result1?.SumMLKPIrate) },
{ title: '完成率', value: `${json.result1?.SumMLKPIrate}%`, hasKPI: false },
{ {
title: '人数', title: '人数',
value: json.result1?.SumPersonNum, value: json.result1?.SumPersonNum,
@ -30,9 +33,6 @@ class Trade {
KPIrate: json.result1?.SumPersonNumKPIrate, KPIrate: json.result1?.SumPersonNumKPIrate,
hasKPI: !isEmpty(json.result1?.SumPersonNumKPIrate), hasKPI: !isEmpty(json.result1?.SumPersonNumKPIrate),
}, },
{ title: '成团', value: json.result1?.SumOrder, VSrate: json.result1?.SumOrderrate, KPIrate: json.result1?.SumOrderKPIrate, hasKPI: !isEmpty(json.result1?.SumOrderKPIrate) },
{ title: '毛利', value: json.result1?.SumML, VSrate: json.result1?.SumMLrate, KPIrate: json.result1?.SumMLKPIrate, hasKPI: !isEmpty(json.result1?.SumMLKPIrate) },
{ title: '完成率', value: `${json.result1?.SumMLKPIrate}%`, hasKPI: false },
], ],
}; };
this.summaryData = summaryData; this.summaryData = summaryData;
@ -47,7 +47,7 @@ class Trade {
// req.fetchJSON('/service-web/QueryData/GetTradeByMonth').then((json) => { // req.fetchJSON('/service-web/QueryData/GetTradeByMonth').then((json) => {
if (json.errcode === 0) { if (json.errcode === 0) {
runInAction(() => { runInAction(() => {
const sortResult = json.result1.sort(sortBy('month')); const sortResult = json.result1.sort(sortBy('groupDateVal'));
const groupsData = sortResult.reduce((r, v) => { const groupsData = sortResult.reduce((r, v) => {
(r[v.groups] || (r[v.groups] = [])).push(v); (r[v.groups] || (r[v.groups] = [])).push(v);
return r; return r;
@ -56,6 +56,7 @@ class Trade {
const kpi = { label: '', value: 1200000 }; // 标注KPI const kpi = { label: '', value: 1200000 }; // 标注KPI
this.sideData.loading = false; this.sideData.loading = false;
this.sideData.dataSource = groupsData; this.sideData.dataSource = groupsData;
this.sideData.monthData = sortResult;
this.sideData.kpi = kpi; this.sideData.kpi = kpi;
}); });
} }
@ -76,7 +77,7 @@ class Trade {
} }
summaryData = { loading: false, dataSource: [] }; summaryData = { loading: false, dataSource: [] };
sideData = { loading: false, dataSource: {}, kpi: {}, }; sideData = { loading: false, dataSource: {}, kpi: {}, monthData: [] };
topData = {}; topData = {};
defaultDataSubject = 'CJCount'; defaultDataSubject = 'CJCount';
} }

@ -282,3 +282,36 @@ export function set_array_index(result) {
export const sortBy = (key) => { export const sortBy = (key) => {
return (a, b) => (a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0); return (a, b) => (a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0);
}; };
/**
* 合并Object, 递归地
*/
export function merge(...objects) {
const isDeep = objects.some(obj => obj !== null && typeof obj === 'object');
const result = objects[0] ?? {};
for (let i = 1; i < objects.length; i++) {
const obj = objects[i];
if (!obj) continue;
Object.keys(obj).forEach(key => {
const val = obj[key];
if (isDeep) {
if (Array.isArray(val)) {
result[key] = [...result[key] || [], ...val];
} else if (typeof val === 'object') {
result[key] = merge(result[key], val);
} else {
result[key] = val;
}
} else {
result[key] = typeof val === 'boolean' ? val : result[key];
}
});
}
return result;
}

@ -3,10 +3,11 @@ import { observer } from 'mobx-react';
import { Row, Col, Spin, Space } from 'antd'; import { Row, Col, Spin, Space } from 'antd';
import { stores_Context } from '../config'; import { stores_Context } from '../config';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { SlackOutlined, SketchOutlined, AntCloudOutlined, RedditOutlined, GithubOutlined, ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons'; // import { SlackOutlined, SketchOutlined, AntCloudOutlined, RedditOutlined, GithubOutlined, ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
import StatisticCard from '../components/StatisticCard'; import StatisticCard from '../components/StatisticCard';
import Bullet from '../components/Bullet'; import Bullet from '../components/Bullet';
import Waterfall from '../components/Waterfall'; import Waterfall from '../components/Waterfall';
import Column from '../components/Column';
import DataFieldRadio from './../components/DateFieldRadio'; import DataFieldRadio from './../components/DateFieldRadio';
import DatePickerCharts from './../components/search/DatePickerCharts'; import DatePickerCharts from './../components/search/DatePickerCharts';
@ -68,10 +69,10 @@ export default observer(() => {
}; };
const WaterfallConfig = { const WaterfallConfig = {
xField: 'month', xField: 'groupDateVal',
yField: 'SumML', yField: 'SumML',
mergeMeta: { meta: {
month: { groupDateVal: {
alias: '月份', alias: '月份',
}, },
}, },
@ -80,6 +81,29 @@ export default observer(() => {
}, },
}; };
const ColumnConfig = {
xField: 'groupDateVal',
yField: 'SumML',
seriesField: 'groups',
label: {
formatter: (v) => ((v.SumML / sideData.kpi.value) * 100).toFixed(2) + '%',
},
legend: false,
// annotations: sideData.monthData.map((d, ...args) => {
// console.log('aaa', d, args);
// return {
// type: 'dataMarker',
// position: d,
// point: {
// style: {
// stroke: '#FF6B3B',
// lineWidth: 1.5,
// },
// },
// };
// }),
};
return ( return (
<> <>
<section> <section>
@ -101,6 +125,9 @@ export default observer(() => {
<h3>市场进度</h3> <h3>市场进度</h3>
<Spin spinning={sideData.loading}> <Spin spinning={sideData.loading}>
<Row gutter={layoutProps3.gutter}> <Row gutter={layoutProps3.gutter}>
<Col {...layoutProps3}>
<Column {...ColumnConfig} dataSource={sideData.monthData} line={sideData.kpi} />
</Col>
{Object.keys(sideData.dataSource).map((key) => ( {Object.keys(sideData.dataSource).map((key) => (
<Col {...layoutProps3} key={key}> <Col {...layoutProps3} key={key}>
<Waterfall {...WaterfallConfig} title={key} dataSource={sideData.dataSource[key]} line={sideData.kpi} /> <Waterfall {...WaterfallConfig} title={key} dataSource={sideData.dataSource[key]} line={sideData.kpi} />

Loading…
Cancel
Save