diff --git a/src/charts/Bullet.jsx b/src/charts/Bullet.jsx
deleted file mode 100644
index 985093c..0000000
--- a/src/charts/Bullet.jsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { observer } from 'mobx-react';
-import { Bullet } from '@ant-design/plots';
-
-export default observer((props) => {
- const { dataSource, ...extProps } = props;
- // todo: 处理进度图的数据格式, number -> array
- const parseData = dataSource?.map(ele => ({...ele, CJCountRange: [0,1000], [extProps.measureField]: [ele[extProps.measureField]]}));
- const config = {
- // data,
- // measureField: 'measures',
- // rangeField: 'ranges',
- // targetField: 'target',
- // xField: 'title',
- color: {
- range: ['#FFbcb8', '#FFe0b0', '#bfeec8'],
- measure: '#5B8FF9',
- target: '#39a3f4',
- },
- label: {
- // target: true,
- // measure: {
- // position: 'middle',
- // style: {
- // fill: '#fff',
- // },
- // },
- },
- xAxis: {
- line: null,
- },
- yAxis: false,
- // 自定义 legend
- legend: {
- custom: true,
- position: 'bottom',
- items: [
- // {
- // value: '差',
- // name: '差',
- // marker: {
- // symbol: 'square',
- // style: {
- // fill: '#FFbcb8',
- // r: 5,
- // },
- // },
- // },
- // {
- // value: '良',
- // name: '良',
- // marker: {
- // symbol: 'square',
- // style: {
- // fill: '#FFe0b0',
- // r: 5,
- // },
- // },
- // },
- // {
- // value: '优',
- // name: '优',
- // marker: {
- // symbol: 'square',
- // style: {
- // fill: '#bfeec8',
- // r: 5,
- // },
- // },
- // },
- {
- value: '实际',
- name: '实际',
- marker: {
- symbol: 'square',
- style: {
- fill: '#5B8FF9',
- r: 5,
- },
- },
- },
- {
- value: '目标',
- name: '目标',
- marker: {
- symbol: 'line',
- style: {
- stroke: '#39a3f4',
- r: 5,
- },
- },
- },
- ],
- },
- };
- return (
- <>
-
- >
- );
-});
diff --git a/src/components/Bullet.jsx b/src/components/Bullet.jsx
new file mode 100644
index 0000000..e2cc0dd
--- /dev/null
+++ b/src/components/Bullet.jsx
@@ -0,0 +1,88 @@
+import { useEffect, useState } from 'react';
+import { observer } from 'mobx-react';
+import { Bullet } from '@ant-design/plots';
+import { sortBy } from '../utils/commons';
+import { dataFieldAlias } from './../libs/ht';
+
+export default observer((props) => {
+ const { dataSource, ...extProps } = props;
+ // 处理进度图的数据格式, number -> array
+ const dataParser = (origin) => {
+ const { measureField, rangeField, targetField } = extProps;
+ const maxKPI = Math.max(...(origin || []).map((ele) => ele[targetField]));
+ const maxValue = Math.max(...(origin || []).map((ele) => ele[measureField]));
+ const _max = Math.max(maxKPI, maxValue);
+ const sortData = origin.sort(sortBy(measureField));
+ // 顶格的值定在更远
+ const _parseData = sortData?.map((ele) => ({ ...ele, [rangeField]: [0, Math.ceil(_max / 0.9)], [measureField]: [ele[measureField]] }));
+ return _parseData;
+ };
+
+ const [parseData, setParseData] = useState([]);
+ useEffect(() => {
+ setParseData(dataParser(dataSource));
+ return () => {};
+ }, [extProps.measureField, dataSource]);
+
+ const config = {
+ color: {
+ range: ['#FFbcb8', '#FFe0b0', '#bfeec8'],
+ measure: '#5B8FF9',
+ target: '#FF9845',
+ },
+ label: {
+ // target: true,
+ // measure: {
+ // position: 'middle',
+ // style: {
+ // fill: '#fff',
+ // },
+ // },
+ },
+ xAxis: {
+ line: null,
+ },
+ yAxis: false,
+ // 自定义 legend
+ legend: {
+ custom: true,
+ position: 'bottom',
+ items: [
+ {
+ value: '实际',
+ name: '实际',
+ marker: {
+ symbol: 'square',
+ style: {
+ fill: '#5B8FF9',
+ r: 5,
+ },
+ },
+ },
+ {
+ value: '目标',
+ name: '目标',
+ marker: {
+ symbol: 'line',
+ style: {
+ stroke: '#FF9845', // '#39a3f4',
+ r: 5,
+ },
+ },
+ },
+ ],
+ },
+ // 全局的alias不起作用
+ tooltip: {
+ customItems: (originalItems) => {
+ // process originalItems,
+ return originalItems.map((ele) => ({ ...ele, name: dataFieldAlias[ele.name]?.alias || ele.name }));
+ },
+ },
+ };
+ return (
+ <>
+
+ >
+ );
+});
diff --git a/src/charts/StatisticCard.jsx b/src/components/StatisticCard.jsx
similarity index 88%
rename from src/charts/StatisticCard.jsx
rename to src/components/StatisticCard.jsx
index 0de1fcc..9a771f8 100644
--- a/src/charts/StatisticCard.jsx
+++ b/src/components/StatisticCard.jsx
@@ -3,7 +3,7 @@ import { Card, Statistic, Progress } from 'antd';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
export default observer((props) => {
- const valueStyle = { color: props.VSrate < 0 ? '#cf1322' : '#3f8600' };
+ const valueStyle = { color: props.VSrate < 0 ? '#3f8600' : '#cf1322' };
const VSIcon = () => (props.VSrate < 0 ? : );
return (
diff --git a/src/components/Waterfall.jsx b/src/components/Waterfall.jsx
new file mode 100644
index 0000000..b23c2d1
--- /dev/null
+++ b/src/components/Waterfall.jsx
@@ -0,0 +1,67 @@
+import { observer } from 'mobx-react';
+import { Waterfall } from '@ant-design/plots';
+import { dataFieldAlias } from './../libs/ht';
+
+export default observer((props) => {
+ const { dataSource, line, title, ...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 = {
+ padding: 'auto',
+ appendPadding: [20, 0, 0, 0],
+
+ /** 展示总计 */
+ total: {
+ label: `${title}总`,
+ style: {
+ fill: '#96a6a6',
+ },
+ },
+ legend: false,
+ /** 数据标签展示模式:绝对值 */
+ labelMode: 'absolute',
+ label: {
+ style: {
+ fontSize: 12,
+ },
+ },
+ annotations: [...annotationsLine],
+ meta: {
+ ...extProps.mergeMeta,
+ [extProps.yField]: {
+ ...extProps.mergeMeta[extProps.yField],
+ max: Math.ceil(yMax / 0.95),
+ alias: dataFieldAlias[extProps.yField]?.alias || extProps.yField,
+ formatter: (v) => dataFieldAlias[extProps.yField]?.formatter(v) || v,
+ },
+ },
+ };
+ return (
+ <>
+
+ >
+ );
+});
diff --git a/src/mock/2.0/trade.json b/src/mock/2.0/trade.json
index 69b9bf8..d78ed3d 100644
--- a/src/mock/2.0/trade.json
+++ b/src/mock/2.0/trade.json
@@ -41,27 +41,9 @@
"month": "@date('MM')",
"key": "@increment"
}
- ],
- "inside|12": [
- {
- "SumML": "@float(999,9999,2,2)",
- "SumMLKPIrate": "@float(0,70,2,2)",
- "COLI_Department": "1,2,28,7",
- "month|+1": -11,
- "key": "@increment"
- }
- ],
- "outside|12": [
- {
- "SumML": "@float(999,9999,2,2)",
- "SumMLKPIrate": "@float(0,70,2,2)",
- "COLI_Department": "1,2,28,7",
- "month|+1": -11,
- "key": "@increment"
- }
]
},
- "get|/service-web/QueryData/GetTradeByType": {
+ "get|/service-web/QueryData/GetTradeOrderByType": {
"errcode": 0,
"errmsg": "",
"loading": false,
@@ -71,18 +53,21 @@
"OrderTypeSN": "@integer(1,10)",
"OrderType": "@cname",
"OrderCount": "@integer(9,999)",
+ "OrderCountKPI": "@integer(100,1000)",
"CJCount": "@integer(9,999)",
"CJCountKPI": "@integer(100,1000)",
"CJPersonNum": "@integer(9,999)",
"YJLY": "@integer(99,9999)",
"CJrate": "@float(1,99,0,2)",
+ "CJrateKPI": "@float(1,99,0,2)",
"Ordervalue": "@integer(9,999)",
"SumML": "@integer(99,9999)",
+ "SumMLKPI": "@integer(99,9999)",
"SumMLrate": "@float(-20,100,0,2)",
"SumMLKPIrate": "@float(20,100,0,2)",
"SumOrder": "@integer(9,999)",
- "SumOrderrate": "@float(-20,20,0,2)",
- "SumOrderKPIrate": "@float(20,100,0,2)",
+ "OrderCountVSrate": "@float(-20,20,0,2)",
+ "OrderCountKPIrate": "@float(20,100,0,2)",
"SumPersonNum": "@integer(99,9999)",
"SumPersonNumrate": "@float(-50,1,0,2)",
"SumPersonNumKPIrate": "@float(20,100,0,2)",
@@ -93,5 +78,30 @@
"result2": [
{}
]
+ },
+ "get|/service-web/QueryData/GetTradeProcess": {
+ "errcode": 0,
+ "errmsg": "",
+ "loading": false,
+ "data": null,
+ "result1|24": [
+ {
+ "groups|1": "@pick([\"inside\",\"outside\"])",
+ "SumML": "@increment(1000)",
+ "SumMLVSrate": "@float(0,70,2,2)",
+ "SumMLKPI": "@integer(1000,10000)",
+ "SumMLKPIrate": "@float(0,70,2,2)",
+ "month": "@date('2023-MM')",
+ "SumOrder": "@integer(9,999)",
+ "SumOrderVSrate": "@float(-20,20,0,2)",
+ "SumOrderKPIrate": "@float(20,100,0,2)",
+ "SumPersonNum": "@integer(99,9999)",
+ "SumPersonNumrate": "@float(-50,1,0,2)",
+ "SumPersonNumKPIrate": "@float(20,100,0,2)",
+ "key": "@increment"
+ }],
+ "result2": [
+ {}
+ ]
}
}
diff --git a/src/stores/Trade.js b/src/stores/Trade.js
index eaffc90..db40f98 100644
--- a/src/stores/Trade.js
+++ b/src/stores/Trade.js
@@ -1,12 +1,12 @@
import { makeAutoObservable, runInAction } from 'mobx';
import * as req from '../utils/request';
-import { isEmpty } from '../utils/commons';
+import { isEmpty, sortBy } from '../utils/commons';
/**
* 计算变化值
*/
const calcRate = (r1, r2) => {
- // 的
+ //
};
class Trade {
@@ -43,15 +43,20 @@ class Trade {
fetchTradeDataByMonth() {
this.sideData.loading = true;
- req.fetchJSON('/service-web/QueryData/GetTradeByMonth').then((json) => {
+ req.fetchJSON('/service-web/QueryData/GetTradeProcess').then((json) => {
+ // req.fetchJSON('/service-web/QueryData/GetTradeByMonth').then((json) => {
if (json.errcode === 0) {
runInAction(() => {
- const _sideData = json.data.reduce((r, v) => {
- (r[v.biz_side] || (r[v.biz_side] = [])).push(v);
+ const sortResult = json.result1.sort(sortBy('month'));
+ const groupsData = sortResult.reduce((r, v) => {
+ (r[v.groups] || (r[v.groups] = [])).push(v);
return r;
}, {});
- console.log(_sideData, '_sideData');
- this.sideData = { loading: false, ...json };
+ console.log(groupsData, 'groupsData');
+ const kpi = { label: '', value: 1200000 }; // 标注KPI
+ this.sideData.loading = false;
+ this.sideData.dataSource = groupsData;
+ this.sideData.kpi = kpi;
});
}
});
@@ -59,7 +64,7 @@ class Trade {
fetchTradeDataByType(orderType) {
this.topData[orderType] = { loading: true, dataSource: [] };
- req.fetchJSON('/service-web/QueryData/GetTradeByType').then((json) => {
+ req.fetchJSON('/service-web/QueryData/GetTradeOrderByType').then((json) => {
if (json.errcode === 0) {
runInAction(() => {
this.topData[orderType].loading = false;
@@ -71,8 +76,9 @@ class Trade {
}
summaryData = { loading: false, dataSource: [] };
- sideData = { loading: false };
- topData = { };
+ sideData = { loading: false, dataSource: {}, kpi: {}, };
+ topData = {};
+ defaultDataSubject = 'CJCount';
}
export default Trade;
diff --git a/src/utils/commons.js b/src/utils/commons.js
index 14c35c5..5cde768 100644
--- a/src/utils/commons.js
+++ b/src/utils/commons.js
@@ -275,3 +275,10 @@ export function set_array_index(result) {
}
return result_array;
}
+
+/**
+ * 排序
+ */
+export const sortBy = (key) => {
+ return (a, b) => (a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0);
+};
diff --git a/src/views/Home.jsx b/src/views/Home.jsx
index 16d138a..09de37a 100644
--- a/src/views/Home.jsx
+++ b/src/views/Home.jsx
@@ -1,12 +1,15 @@
-import { useContext, useEffect } from 'react';
+import { useContext, useEffect, useState } from 'react';
import { observer } from 'mobx-react';
-import { Row, Col, Spin } from 'antd';
+import { Row, Col, Spin, Space } from 'antd';
import { stores_Context } from '../config';
import { useNavigate } from 'react-router-dom';
import { SlackOutlined, SketchOutlined, AntCloudOutlined, RedditOutlined, GithubOutlined, ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
-import StatisticCard from './../charts/StatisticCard';
-import Bullet from './../charts/Bullet';
+import StatisticCard from '../components/StatisticCard';
+import Bullet from '../components/Bullet';
+import Waterfall from '../components/Waterfall';
import DataFieldRadio from './../components/DateFieldRadio';
+import DatePickerCharts from './../components/search/DatePickerCharts';
+
import { empty } from './../utils/commons';
import './home.css';
@@ -16,10 +19,10 @@ export default observer(() => {
const { sideData, summaryData, topData } = TradeStore;
const topSeries = [
- { key: 'Country', label: '国籍'},
- { key: 'Area', label: '目的地'},
- { key: 'Sales', label: '顾问'},
- { key: 'GuestGroupType', label: '客群类别'},
+ { key: 'Country', label: '国籍' },
+ { key: 'Area', label: '目的地' },
+ { key: 'Sales', label: '顾问' },
+ { key: 'GuestGroupType', label: '客群类别' },
];
useEffect(() => {
@@ -28,7 +31,6 @@ export default observer(() => {
TradeStore.fetchTradeDataByMonth();
for (const iterator of topSeries) {
TradeStore.fetchTradeDataByType(iterator.key);
- // TradeStore.fetchTradeDataByType('Area');
}
}
return () => {};
@@ -41,17 +43,50 @@ export default observer(() => {
xs: { span: 24 },
};
- const BulletConfig = {
- measureField: 'CJCount', //
- rangeField: 'CJCountRange', //
- targetField: 'CJCountKPI', //
+ const layoutProps3 = {
+ gutter: { xs: 8, sm: 8, lg: 16 },
+ lg: { span: 8 },
+ sm: { span: 12 },
+ xs: { span: 24 },
+ };
+
+ const [valueKey, setValueKey] = useState('SumML');
+ const [BulletConfig, setBulletConfig] = useState({
+ measureField: 'SumML', //
+ rangeField: 'SumMLRange', //
+ targetField: 'SumMLKPI', //
xField: 'OrderType',
+ });
+ const handleChangeValueKey = (key) => {
+ setValueKey(key);
+ setBulletConfig({
+ measureField: key,
+ rangeField: `${key}Range`,
+ targetField: `${key}KPI`,
+ xField: 'OrderType',
+ });
+ };
+
+ const WaterfallConfig = {
+ xField: 'month',
+ yField: 'SumML',
+ mergeMeta: {
+ month: {
+ alias: '月份',
+ },
+ },
+ label: {
+ formatter: (v) => ((v.SumML / sideData.kpi.value) * 100).toFixed(2) + '%',
+ },
};
return (
<>
- 年度业绩
+
+ 年度业绩
+
+
{summaryData.dataSource.map((item) => (
@@ -62,18 +97,30 @@ export default observer(() => {
- 市场进度
-
- TOP
-
-
-
+ 市场进度
+
+
+ {Object.keys(sideData.dataSource).map((key) => (
+
+
+
+ ))}
+
+
+
+
+
+ TOP
+
+
+
+
{topSeries.map((item) => (
- {item.label}
+ {item.label}