todo: 双线图: KPI

feature/2.0-sales-trade
Lei OT 2 years ago
parent e3cbbee6b7
commit 1c78a0c64f

@ -0,0 +1,71 @@
import { observer } from 'mobx-react';
import { DualAxes } from '@ant-design/plots';
import { merge, pick, cloneDeep } from '../utils/commons';
import { dataFieldAlias } from '../libs/ht';
const uniqueByKey = (array, key, pickLast) => {
const seen = new Map();
const isPickLast = pickLast === true;
return array.filter(item => {
const k = item[key];
const storedItem = seen.get(k);
if(storedItem) {
if(isPickLast) {
seen.set(k, item); // update with last item
}
return false;
}
seen.set(k, item);
return true;
});
};
export default observer((props) => {
const { config, dataSource, ...extProps } = props;
const kpiKey = dataFieldAlias[config.yField]?.nestkey?.v;
const _data = uniqueByKey(dataSource, config.xField, true); // debug:
const mergeConfig = merge(
// color: ['#1979C9', '#F4664A', '#FAAD14'],
// padding: 'auto',
// xField: 'groupDateVal',
// yField: 'SumML',
// seriesField: 'groupsLabel',
{
...pick(config, ['padding', 'xField', 'seriesField']),
yField: [config.yField, kpiKey],
legend: false,
xAxis: {
type: 'cat',
},
meta: { ...cloneDeep(dataFieldAlias) },
geometryOptions: [
{
geometry: 'line',
smooth: true,
point: {
size: 4,
shape: 'cicle',
},
},
{
geometry: 'line',
smooth: true,
point: {
size: 4,
shape: 'cicle',
},
lineStyle: {
lineWidth: 1,
lineDash: [5, 5],
},
color: '#F4664A',
},
],
}
);
return <DualAxes {...mergeConfig} data={[_data, _data]} />;
});

@ -107,7 +107,7 @@ export const dataFieldAlias = dataFieldOptions.reduce(
(a, c) => ({ (a, c) => ({
...a, ...a,
[c.value]: { ...c, alias: c.label, formatter: (v) => c.formatter(v) }, [c.value]: { ...c, alias: c.label, formatter: (v) => c.formatter(v) },
[c.nestkey.v]: { ...c, value: c.nestkey.v, alias: `${c.label}目标`, formatter: (v) => c.formatter(v) }, [c.nestkey.v]: { ...c, value: c.nestkey.v, alias: `${c.label}目标`, label: `${c.label}目标`, formatter: (v) => c.formatter(v) },
}), }),
{} {}
); );

@ -289,7 +289,7 @@ export const sortBy = (key) => {
export function merge(...objects) { export function merge(...objects) {
const isDeep = objects.some(obj => obj !== null && typeof obj === 'object'); const isDeep = objects.some(obj => obj !== null && typeof obj === 'object');
const result = objects[0] ?? {}; const result = objects[0] || (isDeep ? {} : objects[0]);
for (let i = 1; i < objects.length; i++) { for (let i = 1; i < objects.length; i++) {
const obj = objects[i]; const obj = objects[i];
@ -301,7 +301,7 @@ export function merge(...objects) {
if (isDeep) { if (isDeep) {
if (Array.isArray(val)) { if (Array.isArray(val)) {
result[key] = [...result[key] || [], ...val]; result[key] = [].concat(Array.isArray(result[key]) ? result[key] : [result[key]], val);
} else if (typeof val === 'object') { } else if (typeof val === 'object') {
result[key] = merge(result[key], val); result[key] = merge(result[key], val);
} else { } else {

@ -1,11 +1,12 @@
import { useContext, useEffect, useMemo } from 'react'; import { useContext, useEffect, useMemo } from 'react';
import { observer } from "mobx-react"; import { observer } from 'mobx-react';
import { stores_Context } from '../config'; import { stores_Context } from '../config';
import { Row, Col, Spin, Space, Radio, Tabs, Table } from 'antd'; import { Row, Col, Spin, Space, Radio, Tabs, Table } from 'antd';
import { empty } from '../utils/commons'; import { empty } from '../utils/commons';
import { dataFieldAlias } from '../libs/ht'; import { dataFieldAlias } from '../libs/ht';
import Scatter from './../components/Scatter'; import Scatter from './../components/Scatter';
import SearchForm from './../components/search/SearchForm'; import SearchForm from './../components/search/SearchForm';
import { Histogram } from '@ant-design/plots';
export default observer((props) => { export default observer((props) => {
const { date_picker_store: searchFormStore, DistributionStore } = useContext(stores_Context); const { date_picker_store: searchFormStore, DistributionStore } = useContext(stores_Context);
@ -46,6 +47,10 @@ export default observer((props) => {
position: 'right-top', position: 'right-top',
}, },
}; };
const HistogramConfig = {
binField: 'personNum',
binWidth: 1,
};
return ( return (
<> <>
<Row gutter={16} style={{ margin: '-16px -8px' }}> <Row gutter={16} style={{ margin: '-16px -8px' }}>
@ -75,6 +80,11 @@ export default observer((props) => {
<Scatter {...ScatterConfig} dataSource={scatterDays} /> <Scatter {...ScatterConfig} dataSource={scatterDays} />
</Spin> </Spin>
</section> </section>
{/* <section>
<Spin spinning={detailData.loading}>
<Histogram {...HistogramConfig} data={scatterDays} />
</Spin>
</section> */}
</> </>
); );
}); });

@ -1,12 +1,13 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Row, Col, Spin, Space, Radio } from 'antd'; import { Row, Col, Spin, Space, Radio } from 'antd';
import { CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone, } from '@ant-design/icons'; import { CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone } from '@ant-design/icons';
import { stores_Context } from '../config'; import { stores_Context } from '../config';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import StatisticCard from '../components/StatisticCard'; import StatisticCard from '../components/StatisticCard';
import Bullet from '../components/BulletWithSort'; import Bullet from '../components/BulletWithSort';
import Waterfall from '../components/Waterfall'; import Waterfall from '../components/Waterfall';
import LineMix from '../components/LineWithKPI';
import DataFieldRadio from '../components/DataFieldRadio'; import DataFieldRadio from '../components/DataFieldRadio';
import { datePartOptions } from './../components/DateGroupRadio/date'; import { datePartOptions } from './../components/DateGroupRadio/date';
import SearchForm from './../components/search/SearchForm'; import SearchForm from './../components/search/SearchForm';
@ -30,7 +31,7 @@ export default observer(() => {
// const navigate = useNavigate(); // const navigate = useNavigate();
const { TradeStore, date_picker_store: searchFormStore } = useContext(stores_Context); const { TradeStore, date_picker_store: searchFormStore } = useContext(stores_Context);
const { sideData, summaryData, BuData, topData, timeData, timeLineKey } = TradeStore; const { sideData, summaryData, BuData, topData, timeData, timeLineKey } = TradeStore;
const { formValues, } = searchFormStore; const { formValues } = searchFormStore;
useEffect(() => { useEffect(() => {
if (empty(summaryData.dataSource)) { if (empty(summaryData.dataSource)) {
@ -92,7 +93,7 @@ export default observer(() => {
}, },
}, },
label: { label: {
formatter: (v) => summaryData.kpi.value === 0 ? (dataFieldAlias.SumML?.formatter(v.SumML) || v.SumML) : ((v.SumML / summaryData.kpi.value) * 100).toFixed(2) + '%', formatter: (v) => (summaryData.kpi.value === 0 ? dataFieldAlias.SumML?.formatter(v.SumML) || v.SumML : ((v.SumML / summaryData.kpi.value) * 100).toFixed(2) + '%'),
}, },
}; };
@ -108,7 +109,8 @@ export default observer(() => {
autoHide: true, autoHide: true,
autoRotate: false, autoRotate: false,
}, },
} },
legend: false,
}; };
const lineConfigSet = { const lineConfigSet = {
@ -123,10 +125,11 @@ export default observer(() => {
smooth: true, smooth: true,
point: { point: {
size: 4, size: 4,
shape: "cicle", shape: 'cicle',
}, },
legend: false, legend: false,
meta: { ...cloneDeep(dataFieldAlias) meta: {
...cloneDeep(dataFieldAlias),
// [extProps.yField]: { // [extProps.yField]: {
// 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,
@ -139,7 +142,7 @@ export default observer(() => {
const handleChangetimeDataField = (key) => { const handleChangetimeDataField = (key) => {
setTimeDataField(key); setTimeDataField(key);
setLineConfig({ setLineConfig({
...lineConfig, ...cloneDeep(lineConfig),
yField: key, yField: key,
tooltip: { tooltip: {
customItems: (originalItems) => { customItems: (originalItems) => {
@ -204,6 +207,7 @@ export default observer(() => {
</Space> </Space>
<Spin spinning={timeData.loading}> <Spin spinning={timeData.loading}>
<Line {...lineConfig} data={timeData.dataSource} /> <Line {...lineConfig} data={timeData.dataSource} />
{/* <LineMix dataSource={timeData.dataSource} config={lineConfig} /> */}
</Spin> </Spin>
</section> </section>
<section> <section>

Loading…
Cancel
Save