diff --git a/src/components/LineWithKPI.jsx b/src/components/LineWithKPI.jsx
index 0271fae..3eb4294 100644
--- a/src/components/LineWithKPI.jsx
+++ b/src/components/LineWithKPI.jsx
@@ -4,44 +4,90 @@ import { merge, isEmpty, groupBy, sortBy } from '../utils/commons';
import { dataFieldAlias } from '../libs/ht';
export default observer((props) => {
- const { dataSource, ...config } = props;
+ const { dataSource, showKPI, ...config } = props;
const kpiKey = dataFieldAlias[config.yField]?.nestkey?.v;
const seriesData = groupBy(dataSource, ele => ele[config.seriesField]);
- const splitData = dataSource.reduce((r, v) => {
+ const splitData = showKPI ? dataSource.reduce((r, v) => {
r.push(v);
if ( ! isEmpty(v[kpiKey])) { // 有设目标才多显示一条虚线, 颜色和数据线一致
r.push({...v, [config.yField]: v[kpiKey], [config.seriesField]: `${v[config.seriesField]} ${dataFieldAlias[kpiKey].label}`, extraLine: true,});
}
return r;
- }, []).sort(sortBy(config.xField));
+ }, []).sort(sortBy(config.xField)) : dataSource;
const dataColors = [
- "#5B8FF9","#5AD8A6","#5D7092","#F6BD16","#6F5EF9","#6DC8EC","#945FB9","#FF9845","#1E9493",
- "#FF99C3","#FF6B3B","#626681","#FFC100","#9FB40F","#76523B","#DAD5B5","#0E8E89","#E19348",
- "#F383A2","#247FEA",
+ "#5D7092","#F6BD16","#6F5EF9","#6DC8EC","#945FB9","#FF9845","#1E9493",
+ "#FF99C3","#FF6B3B","#626681","#FFC100","#9FB40F","#76523B","#DAD5B5",
+ "#0E8E89","#E19348","#F383A2","#247FEA","#5B8FF9","#5AD8A6",
];
- const colorSets = Object.keys(seriesData).sort().reduce((obj, k, i) => ({...obj, [k]: dataColors[i]}), {});
- const mergeLineConfig = merge({
- color: (item) => {
- const thisSeries = item[config.seriesField]?.split(' ')?.[0];
- return colorSets[thisSeries];
- // return thisSeries.includes('目标') ? '#F4664A' : colorSets[thisSeries];
- },
- lineStyle: (data) => {
- if (data[config.seriesField].includes('目标')) {
+ const colorSets = Object.keys(seriesData)
+ .sort()
+ .filter((ele) => !ele.includes(' '))
+ .reduce((obj, k, i) => ({ ...obj, [k]: dataColors[i] || dataColors[i % 20] }), {});
+ // console.log('colorSets', colorSets);
+ const mergeLineConfig = merge(
+ {
+ color: (item) => {
+ const thisSeries = item[config.seriesField]?.split(' ')?.[0];
+ return colorSets[thisSeries];
+ },
+ lineStyle: (data) => {
+ if (data[config.seriesField].includes('目标')) {
+ return {
+ lineDash: [8, 20],
+ opacity: 0.5,
+ };
+ }
+
+ if (data[config.seriesField].includes('@')) {
+ return {
+ lineDash: [4, 8],
+ opacity: 0.6,
+ lineWidth: 1.5,
+ };
+ }
+
return {
- lineDash: [4, 8],
- opacity: 0.7,
+ opacity: 1,
};
- }
-
- return {
- opacity: 1,
- };
- },
- legend: {
- custom: true,
- items: Object.keys(seriesData).map((ele) => ({ id: ele, name: ele, value: ele, marker: { symbol: 'circle', style: { fill: colorSets[ele], color: colorSets[ele] } } })),
+ },
+ legend: {
+ custom: true,
+ items: Object.keys(seriesData).map((ele) => ({
+ id: ele,
+ name: ele,
+ value: ele,
+ marker: { symbol: ele.includes(' ') ? 'hyphen' : 'circle', style: { fill: colorSets[ele], stroke: colorSets[ele?.split(' ')?.[0]], r: 3, lineWidth: 2, color: colorSets[ele] } },
+ })),
+ },
+ // annotations: [
+ // // 低于 0 颜色变化
+ // {
+ // type: 'regionFilter',
+ // start: ['min', 0],
+ // end: ['max', 0],
+ // color: '#F4664A',
+ // },
+ // {
+ // type: 'text',
+ // position: ['min', 0],
+ // content: '0',
+ // offsetY: -4,
+ // style: {
+ // textBaseline: 'bottom',
+ // },
+ // },
+ // {
+ // type: 'line',
+ // start: ['min', 0],
+ // end: ['max', 0],
+ // style: {
+ // stroke: '#F4664A',
+ // lineDash: [2, 2],
+ // },
+ // },
+ // ],
},
- }, config);
+ config
+ );
return ;
});
diff --git a/src/components/MixTBWithKPI.jsx b/src/components/MixTBWithKPI.jsx
index 37dce81..1737a57 100644
--- a/src/components/MixTBWithKPI.jsx
+++ b/src/components/MixTBWithKPI.jsx
@@ -3,26 +3,6 @@ import { Mix } from '@ant-design/plots';
import { merge, isEmpty, groupBy, 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 { dataSource, summaryData: areaData, ...config } = props;
const kpiKey = dataFieldAlias[config.yField]?.nestkey?.v;
@@ -61,7 +41,7 @@ export default observer((props) => {
meta: {
[yField]: {
sync: true,
- }
+ },
},
// color: ['#598cf3', '#69deae', '#F4664A', '#FAAD14'],
color: (item) => {
@@ -117,22 +97,39 @@ export default observer((props) => {
},
{ [xField]: { sync: true }, [yField]: { sync: true } }
),
- // color: '#b32b19',
color: '#f58269',
- smooth: true,
+ // color: (datum) => {
+ // console.log('color', datum, String(datum[seriesField]).includes('对比'));
+ // return String(datum[seriesField]).includes('对比') ? '#f7a593' : '#f58269';
+ // }, // '#f58269',
+ // smooth: true,
line: {
- size: 0.1,
- },
- areaStyle: () => {
- return {
- fill: 'l(270) 0:#ffffff 0.25:#f8e8e7 0.5:#fac9bd 0.75:#f7a593',
- // lineWidth: 0.1,
- // lineOpacity: 0.5,
- };
+ size: 1,
+ style: (datum) => {
+ return String(datum[seriesField]).includes('对比')
+ ? {
+ // lineWidth: 0.1,
+ lineDash: [4, 5],
+ stroke: '#f7a593',
+ }
+ : {
+ stroke: '#f58269',
+ };
+ },
},
- label: {
- offsetY: -8,
+ areaStyle: (datum) => {
+ // console.log('areaStyle', datum);
+ return String(datum[seriesField]).includes(' ')
+ ? {
+ fill: 'l(270) 0:#ffffff 0.25:#f8e8e7 0.5:#fac9bd 0.75:#fac9bd',
+ }
+ : {
+ fill: 'l(270) 0:#ffffff 0.25:#f8e8e7 0.5:#fac9bd 0.75:#f7a593',
+ // lineWidth: 0.1,
+ // lineOpacity: 0.5,
+ };
},
+ label: (datum) => ({ offsetY: -8 }),
annotations: areaData.map((d) => {
return {
type: 'dataMarker',
diff --git a/src/components/MixYnQ.jsx b/src/components/MixYnQ.jsx
index d990e1d..26c456c 100644
--- a/src/components/MixYnQ.jsx
+++ b/src/components/MixYnQ.jsx
@@ -257,6 +257,13 @@ export default observer((props) => {
legend: false, // {},
color: COLOR_SETS,
annotations: diffLine,
+
+ minColumnWidth: 5,
+ maxColumnWidth: 5,
+ // 分组柱状图 组内柱子间的间距 (像素级别)
+ dodgePadding: 1,
+ // 分组柱状图 组间的间距 (像素级别)
+ // intervalPadding: 20,
},
},
],
diff --git a/src/components/StatisticCard2.jsx b/src/components/StatisticCard2.jsx
index cc764ad..14bc2ad 100644
--- a/src/components/StatisticCard2.jsx
+++ b/src/components/StatisticCard2.jsx
@@ -75,7 +75,7 @@ export default observer((props) => {
description: diff ? (
- {diff.VSrate && 0 ? 'up' : 'down'} />}
+ {diff.VSrate && 0 ? 'up' : 'down'} />}
) : null,
}}
diff --git a/src/components/search/YearPickerCharts.jsx b/src/components/search/YearPickerCharts.jsx
index 1a5a6f7..191afcb 100644
--- a/src/components/search/YearPickerCharts.jsx
+++ b/src/components/search/YearPickerCharts.jsx
@@ -51,7 +51,7 @@ class DatePickerCharts extends Component {
locale={locale}
placeholder={"对比 Year"}
onChange={(value) => {
- const fullYear = [value.clone().set('month', 0).set('date', 1), value.clone().set('month', 11).set('date', 31)];
+ const fullYear = value ? [value.clone().set('month', 0).set('date', 1), value.clone().set('month', 11).set('date', 31)] : undefined;
if (typeof this.props.onChange === 'function') {
this.props.onChange(fullYear);
}
diff --git a/src/stores/Trade.js b/src/stores/Trade.js
index 05e3c17..db58474 100644
--- a/src/stores/Trade.js
+++ b/src/stores/Trade.js
@@ -19,6 +19,10 @@ class Trade {
const curQueryData = cloneDeep(queryData);
curQueryData.groupType = curQueryData?.groupType || 'overview';
curQueryData.groupDateType = 'year';
+ if (isEmpty(curQueryData.DateDiff1)) {
+ curQueryData.DateDiff1 = moment(curQueryData.Date1).subtract(1, 'year').format(DATE_FORMAT);
+ curQueryData.DateDiff2 = moment(curQueryData.Date2).subtract(1, 'year').format(SMALL_DATETIME_FORMAT);
+ }
const multiData = await this.fetchTradeDataAll((curQueryData));
const { summary, traditional, biz } = multiData.result1;
const { summary: summary2, traditional: traditional2, biz: biz2 } = multiData.result2;
@@ -137,16 +141,59 @@ class Trade {
queryData.groupType = queryData?.groupType || 'overview';
Object.assign(queryData, { groupDateType: this.timeLineKey });
const multiData = await this.fetchTradeDataAll(cloneDeep(queryData));
- const { traditional, biz } = multiData.result1;
- const { summary: summary2, traditional: traditional2, biz: biz2 } = multiData.result2;
- console.log(biz, 'mmmmmmmm', queryData, multiData);
+ const { traditional, biz, summaryRows: summaryRows1, } = multiData.result1;
+ // const { summaryRows: summaryRows2, mergeRows: mergeRows2 } = multiData.result2;
+ // console.log(biz, 'mmmmmmmm', queryData, multiData);
const mergeData = [].concat(traditional, biz);
const dateKeyData = groupBy(mergeData, ele => ele.groupDateVal);
const sortByDateKey = Object.values(sortKeys(dateKeyData)).reduce( (a, b) => a.concat(b), []);
+
runInAction(() => {
this.timeData.loading = false;
this.timeData.dataSource = sortByDateKey;
- this.timeData.origin = multiData.result1;
+ this.timeData.origin = { summaryRows: summaryRows1 || [] }; // multiData.result1;
+ });
+ }
+
+ /**
+ * 有对比的时间轴
+ */
+ async fetchTradeDataDiffByDate(queryData = {}) {
+ this.timeDiffData.loading = true;
+ queryData = Object.assign({}, this.searchPayloadHome, queryData); // queryData || this.searchPayloadHome;
+ queryData.groupType = queryData?.groupType || 'overview';
+ Object.assign(queryData, { groupDateType: this.timeLineKey });
+ const multiData = await this.fetchTradeDataAll(cloneDeep(queryData));
+ const { mergeRows: mergeRows1 } = multiData.result1;
+ const { mergeRows: mergeRows2 } = multiData.result2;
+ // console.log(biz, 'mmmmmmmm', queryData, multiData);
+
+ // 为了图表的X轴一致
+ const allDateKey1 = [...new Set(mergeRows1.reduce((rv, vk) => {
+ rv.push(vk.groupDateVal);
+ return rv;
+ }, []))].sort();
+ const allDateKey2 = [...new Set(mergeRows2.reduce((rv, vk) => {
+ rv.push(vk.groupDateVal);
+ return rv;
+ }, []))].sort();
+ const allLabelDateKeyMapped = {
+ ...allDateKey2.reduce((obj, k, i) => ({...obj, [k]: allDateKey1[i] || `_${k}`}), {})
+ };
+ const mergeKeyDateRows = [].concat(
+ mergeRows1 || [],
+ (mergeRows2 || []).map((row, ri) => {
+ return {
+ ...row,
+ groupsLabel: `${row.groupsLabel} @${moment(queryData.DateDiff1).year()}`,
+ groupDateVal: allLabelDateKeyMapped[row.groupDateVal],
+ rawGroupDateVal: row.groupDateVal,
+ };
+ })
+ ).sort(sortBy('groupDateVal'));
+ runInAction(() => {
+ this.timeDiffData.loading = false;
+ this.timeDiffData.dataSource = mergeKeyDateRows;
});
}
@@ -291,18 +338,26 @@ class Trade {
this.targetTableProps.dataSource = [].concat(Object.values(finalTargetData.targetGuest), Object.values(finalTargetData.targetCountry)); // [finalTargetData.targetTotal], // todo: 总数是重复的
};
- setStateSearch(body) {
+ searchValues = {};
+ setSearch(body, form) {
this.searchPayloadHome = body;
+ this.searchValues = form;
}
- timeLineKey = 'week';
+ timeLineKey = 'month';
setTimeLineKey(v) {
this.timeLineKey = v;
}
+ groupKey = 'overview';
+ setGroupKey(v) {
+ this.groupKey = v;
+ }
+
resetData = () => {
this.summaryData = { loading: false, dataSource: [], kpi: {}, };
- this.timeData = { loading: false, dataSource: [], origin: {} };
+ this.timeData = { loading: false, dataSource: [], origin: {}, diff: {} };
+ this.timeDiffData = { loading: false, dataSource: [], origin: {}, };
this.BuData = { loading: false, dataSource: [] };
this.sideData = { loading: false, dataSource: {}, monthData: [], yearData: [] };
this.topData = {};
@@ -312,7 +367,8 @@ class Trade {
searchPayloadHome = {};
summaryData = { loading: false, dataSource: [], kpi: {}, };
- timeData = { loading: false, dataSource: [], origin: {} };
+ timeData = { loading: false, dataSource: [], origin: {}, diff: {} };
+ timeDiffData = { loading: false, dataSource: [], origin: {}, };
BuData = { loading: false, dataSource: [] };
sideData = { loading: false, dataSource: {}, monthData: [], yearData: [] };
topData = {};
diff --git a/src/views/Home.jsx b/src/views/Home.jsx
index f9b3ecb..1ecb837 100644
--- a/src/views/Home.jsx
+++ b/src/views/Home.jsx
@@ -1,6 +1,6 @@
import { useContext, useEffect, useState } from 'react';
import { observer } from 'mobx-react';
-import { Row, Col, Spin, Space, Radio, Table } from 'antd';
+import { Row, Col, Spin, Space, Radio, Table, Button } from 'antd';
import { CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwoTone } from '@ant-design/icons';
import { stores_Context } from '../config';
import StatisticCard2 from '../components/StatisticCard2';
@@ -9,6 +9,7 @@ import Waterfall from '../components/Waterfall';
import MixTBWithKPI from './../components/MixTBWithKPI';
import Donut from './../components/Donut';
import MapCountry from './../components/MapCountry';
+import LineWithKPI from '../components/LineWithKPI';
import DataFieldRadio from '../components/DataFieldRadio';
import { datePartOptions } from './../components/DateGroupRadio/date';
import SearchForm from './../components/search/SearchForm';
@@ -17,11 +18,16 @@ import { dataFieldAlias } from './../libs/ht';
import './home.css';
const topSeries = [
- { key: 'dept', label: '小组', graphVisible: true },
- { key: 'operator', label: '顾问', graphVisible: true },
- { key: 'destination', label: '目的地', graphVisible: true },
- { key: 'GuestGroupType', label: '客群类别', graphVisible: false },
- { key: 'country', label: '国籍', graphVisible: true },
+ { key: 'dept', value: 'dept', label: '小组', graphVisible: true },
+ { key: 'operator', value: 'operator', label: '顾问', graphVisible: true },
+ { key: 'destination', value: 'destination', label: '目的地', graphVisible: true },
+ { key: 'GuestGroupType', value: 'GuestGroupType', label: '客群类别', graphVisible: false },
+ { key: 'country', value: 'country', label: '国籍', graphVisible: true },
+];
+
+const allGroupTypes = [
+ { key: 'overview', value: 'overview', label: '总额' },
+ ...topSeries,
];
// const iconSets = [CheckCircleTwoTone, , , , ,,];
@@ -30,7 +36,7 @@ const iconSets = [CheckCircleTwoTone, MoneyCollectTwoTone, FlagTwoTone, SmileTwo
export default observer(() => {
// const navigate = useNavigate();
const { TradeStore, date_picker_store: searchFormStore } = useContext(stores_Context);
- const { sideData, summaryData, BuData, topData, timeData, timeLineKey, targetTableProps } = TradeStore;
+ const { searchValues, sideData, summaryData, BuData, topData, timeData, timeLineKey, targetTableProps, timeDiffData, groupKey } = TradeStore;
const { formValues } = searchFormStore;
useEffect(() => {
@@ -51,6 +57,7 @@ export default observer(() => {
TradeStore.resetData();
TradeStore.fetchSummaryData(Object.assign({}, queryData, { groupType }));
TradeStore.fetchTradeDataByDate(queryData);
+ TradeStore.fetchTradeDataDiffByDate(queryData);
// // TradeStore.fetchTradeDataByBU(queryData);
TradeStore.fetchTradeDataByMonth(queryData);
const topSeriesF = _overviewFlag ? topSeries : topSeries.filter((ele) => ele.key !== 'dept');
@@ -133,7 +140,6 @@ export default observer(() => {
xAxis: {
type: 'cat',
},
- // smooth: true,
point: {
size: 4,
shape: 'cicle',
@@ -158,26 +164,38 @@ export default observer(() => {
TradeStore.setTimeLineKey(value);
if (!isEmpty(TradeStore.searchPayloadHome)) {
TradeStore.fetchTradeDataByDate({ groupType: groupTypeVal });
+ TradeStore.fetchTradeDataDiffByDate({ groupType: diffGroupKey });
}
};
+ const [diffGroupKey, setDiffGroupKey] = useState(groupKey);
+ const handleChangeDiffType = ({ target: { value } }) => {
+ console.log('diffGroupKey', diffGroupKey, value);
+ setDiffGroupKey(value);
+ TradeStore.setGroupKey(value);
+ if (!isEmpty(TradeStore.searchPayloadHome)) {
+ TradeStore.fetchTradeDataDiffByDate({ groupType: value });
+ }
+ };
+ const [showDiff, setShowDiff] = useState(false);
return (
<>
-
+
{
- TradeStore.setStateSearch(obj);
+ TradeStore.setSearch(obj, form);
pageRefresh(obj);
}}
/>
@@ -185,7 +203,9 @@ export default observer(() => {
- 年度业绩 =传统+商务
+
+ 年度业绩 =传统+商务
+
@@ -195,7 +215,7 @@ export default observer(() => {
))} */}
{summaryData.dataSource.map((item, i) => (
-
+
))}
@@ -203,22 +223,41 @@ export default observer(() => {
-
- 走势
+
+ {showDiff === false ? '走势' : '对比'}
+ {showDiff && }
+ {searchValues.yearDiff && (
+
+ )}
-
- {/* */}
-
-
+ {showDiff === false ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
市场 (仅传统订单)
-
+
- <>>
+ <>
+
+ >
{/* {overviewFlag ? (
<>
@@ -228,12 +267,14 @@ export default observer(() => {
<>>
)} */}
- {Object.keys(sideData.dataSource).sort().map((key) => (
-
-
- {`${key}每月业绩`}
-
- ))}
+ {Object.keys(sideData.dataSource)
+ .sort()
+ .map((key) => (
+
+
+ {`${key}每月业绩`}
+
+ ))}
@@ -265,9 +306,9 @@ export default observer(() => {
)}
-
-
-
+
+
+
diff --git a/src/views/Sale_KPI.jsx b/src/views/Sale_KPI.jsx
index 5e5800b..b57b6e3 100644
--- a/src/views/Sale_KPI.jsx
+++ b/src/views/Sale_KPI.jsx
@@ -212,7 +212,7 @@ const Sale_KPI = () => {
-
+