From 2b279718c05acfb7506317bdfe6584cc678d52fe Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 19 Sep 2023 15:41:41 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=20=E5=B9=B4=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{Bullet.jsx => BulletWithSort.jsx} | 17 ++- src/components/Waterfall.jsx | 15 ++- src/libs/ht.js | 12 +- src/mock/2.0/trade.json | 1 + src/stores/Trade.js | 118 ++++++++++++++---- src/views/Home.jsx | 76 +++++------ 6 files changed, 161 insertions(+), 78 deletions(-) rename src/components/{Bullet.jsx => BulletWithSort.jsx} (79%) diff --git a/src/components/Bullet.jsx b/src/components/BulletWithSort.jsx similarity index 79% rename from src/components/Bullet.jsx rename to src/components/BulletWithSort.jsx index c2e22d7..bd9fb6d 100644 --- a/src/components/Bullet.jsx +++ b/src/components/BulletWithSort.jsx @@ -2,19 +2,24 @@ import { useEffect, useState } from 'react'; import { observer } from 'mobx-react'; import { Bullet } from '@ant-design/plots'; import { sortBy, merge } from '../utils/commons'; -import { dataFieldAlias } from './../libs/ht'; +import { dataFieldAlias } from '../libs/ht'; + +// const layoutLabel = { +// 'vertical': +// }; export default observer((props) => { - const { dataSource, ...extProps } = props; + const { dataSource, itemLength, ...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 sortData = origin.sort(sortBy(measureField)).slice(-itemLength); // 顶格的值定在更远 const _parseData = sortData?.map((ele) => ({ ...ele, [rangeField]: [0, Math.ceil(_max / 0.9)], [measureField]: [ele[measureField]] })); + console.log(_parseData, 'vvvvvvvvvvvvvv'); return _parseData; }; @@ -26,16 +31,16 @@ export default observer((props) => { const config = merge({ color: { - range: ['#FFbcb8', '#FFe0b0', '#bfeec8'], + range: [ '#FFF3E1', '#FFe0b0', '#bfeec8'], // '#FFbcb8', '#FFe0b0', measure: '#5B8FF9', target: '#FF9845', }, label: { // target: true, // measure: { - // position: 'middle', + // position: extProps?.vertical === 'vertical' ? 'top' : 'right', // style: { - // fill: '#fff', + // fill: '#063CAA', // }, // }, }, diff --git a/src/components/Waterfall.jsx b/src/components/Waterfall.jsx index 9fc15f0..8c106ac 100644 --- a/src/components/Waterfall.jsx +++ b/src/components/Waterfall.jsx @@ -5,7 +5,8 @@ import { merge } from '../utils/commons'; export default observer((props) => { const { dataSource, line, title, ...extProps } = props; - const yMax = Math.max(line?.value || 0, ...dataSource.map((ele) => ele[extProps.yField])); + const yMax = (Math.max(line?.value || 0, ...dataSource.map((ele) => ele[extProps.yField])))*10; + console.log(title, 'title waterfall', yMax); const annotationsLine = line ? [ { @@ -36,6 +37,7 @@ export default observer((props) => { /** 展示总计 */ total: { + // label: `${title}总`, label: `${title}总`, style: { fill: '#96a6a6', @@ -57,6 +59,17 @@ export default observer((props) => { formatter: (v) => dataFieldAlias[extProps.yField]?.formatter(v) || v, }, }, + xAxis: { + type: 'cat', + }, + tooltip: { + customItems: (originalItems) => { + // process originalItems, + const items = originalItems.map((ele) => ({ ...ele, title: `${ele.title} ${ele.data.groupsLabel}`, name: dataFieldAlias[ele.name]?.alias || ele.name })); + console.log(originalItems, items, 'llll'); + return items; + }, + }, }, extProps); return ( <> diff --git a/src/libs/ht.js b/src/libs/ht.js index 381fbcc..a68a693 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -82,10 +82,10 @@ export const dateTypes = [ */ export const dataFieldOptions = [ { label: '毛利', value: 'SumML', formatter: (v) => `${v / 1000} K`, nestkey: { p: 'MLKPIrates', v: 'MLKPIvalue' } }, - { label: '订单数', value: 'OrderCount', formatter: (v) => v }, - { label: '成交数', value: 'CJCount', formatter: (v) => v }, - // { label: '成交人数', value: 'CJPersonNum', formatter: (v) => v }, - { label: '成交率', value: 'CJrate', formatter: (v) => v }, + { label: '订单数', value: 'SumOrder', formatter: (v) => v, nestkey: { p: 'OrderKPIrates', v: 'OrderKPIvalue' } }, + { label: '成交数', value: 'ConfirmOrder', formatter: (v) => v, nestkey: { p: 'ConfirmOrderKPIrates', v: 'ConfirmOrderKPIvalue' } }, + { label: '成交率', value: 'ConfirmRates', formatter: (v) => v, nestkey: { p: 'ConfirmRatesKPIrates', v: 'ConfirmRatesKPIvalue' } }, + // { label: '人数', value: 'CJPersonNum', formatter: (v) => v }, // todo: more... ]; /** @@ -94,8 +94,8 @@ export const dataFieldOptions = [ export const dataFieldAlias = dataFieldOptions.reduce( (a, c) => ({ ...a, - [c.value]: { alias: c.label, formatter: (v) => c.formatter(v) }, - [`${c.value}KPI`]: { alias: `${c.label}目标`, formatter: (v) => c.formatter(v) }, + [c.value]: { ...c, alias: c.label, formatter: (v) => c.formatter(v) }, + [c.nestkey.v]: { ...c, alias: `${c.label}目标`, formatter: (v) => c.formatter(v) }, }), {} ); diff --git a/src/mock/2.0/trade.json b/src/mock/2.0/trade.json index 69ceb97..6d65617 100644 --- a/src/mock/2.0/trade.json +++ b/src/mock/2.0/trade.json @@ -87,6 +87,7 @@ "result1|24": [ { "groups|1": "@pick([\"inside\",\"outside\"])", + "groupsLabel|1": "@pick([\"inside\",\"outside\"])", "groupDateVal": "@date('2023-MM')", "SumML": "@increment(1000)", "SumMLVSrate": "@float(0,70,2,2)", diff --git a/src/stores/Trade.js b/src/stores/Trade.js index 52b1343..7837a2c 100644 --- a/src/stores/Trade.js +++ b/src/stores/Trade.js @@ -1,6 +1,7 @@ import { makeAutoObservable, runInAction, toJS } from 'mobx'; import * as req from '../utils/request'; -import { isEmpty, sortBy } from '../utils/commons'; +import { isEmpty, sortBy, pick } from '../utils/commons'; +import { dataFieldAlias } from './../libs/ht'; class Trade { constructor(rootStore) { @@ -8,76 +9,151 @@ class Trade { makeAutoObservable(this); } + /** + * 年度总额 + */ fetchSummaryData(queryData) { this.summaryData.loading = true; queryData.groupType = 'overview'; - queryData.groupDateType = 'year'; - req.fetchJSON('/service-Analyse2/GetTradeProcess/test', queryData).then((json) => { + // queryData.groupDateType = 'year'; + this.fetchTradeData(queryData).then((json) => { if (json.errcode === 0) { runInAction(() => { + const summary = json.result1?.[0] || {}; const summaryData = { loading: false, 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: '成团', + value: summary?.ConfirmOrder, + // VSrate: summary?.ConfirmOrderrate, + KPIrate: summary?.[dataFieldAlias.ConfirmOrder.nestkey.p], + hasKPI: !isEmpty(summary?.[dataFieldAlias.ConfirmOrder.nestkey.p]), + }, + { title: '毛利', value: summary?.SumML, KPIrate: summary?.[dataFieldAlias.SumML.nestkey.p], hasKPI: !isEmpty(summary?.[dataFieldAlias.SumML.nestkey.p]) }, + { title: '完成率', value: `${summary?.[dataFieldAlias.SumML.nestkey.p] || ''}%`, hasKPI: false }, { title: '人数', - value: json.result1?.SumPersonNum, - VSrate: json.result1?.SumPersonNumrate, - KPIrate: json.result1?.SumPersonNumKPIrate, - hasKPI: !isEmpty(json.result1?.SumPersonNumKPIrate), + value: summary?.SumPersonNum, + // VSrate: summary?.SumPersonNumrate, + // KPIrate: summary?.[dataFieldAlias.SumPersonNum.nestkey.p], + hasKPI: false, // // !isEmpty(summary?.[dataFieldAlias.SumPersonNum.nestkey.p]), }, ], }; this.summaryData = summaryData; + const kpi = { label: '', value: summary.MLKPIvalue }; + this.summaryData.kpi = kpi; + }); + } + }); + } + + /** + * 时间轴 + */ + fetchTradeDataByDate(queryData) { + this.timeData.loading = true; + Object.assign(queryData, { groupType: 'overview', groupDateType: 'month' }); + this.fetchTradeData(queryData).then((json) => { + if (json.errcode === 0) { + runInAction(() => { + const data = json.result1; + // 标注KPI + + this.timeData.loading = false; + this.timeData.dataSource = data; }); } }); } + /** + * 事业部年度 + */ + fetchTradeDataByBU(queryData) { + this.BuData.loading = true; + Object.assign(queryData, { groupType: 'bu', groupDateType: 'year' }); + this.fetchTradeData(queryData).then((json) => { + if (json.errcode === 0) { + runInAction(() => { + const data = json.result1; + // 标注KPI + + this.BuData.loading = false; + this.BuData.dataSource = data; + }); + } + }); + } + + /** + * 业务区域, 按月 + */ fetchTradeDataByMonth(queryData) { this.sideData.loading = true; + // Object.assign(queryData, { groupType: 'bizarea', groupDateType: 'month' }); + // todo: groupType: bizarea Object.assign(queryData, { groupType: 'bu', groupDateType: 'month' }); - req.fetchJSON('/service-Analyse2/GetTradeProcess/test', queryData).then((json) => { + this.fetchTradeData(queryData).then((json) => { if (json.errcode === 0) { runInAction(() => { const sortResult = json.result1.sort(sortBy('groupDateVal')); + /** + * test: '91006' + */ const groupsData = sortResult.reduce((r, v) => { - (r[v.groups] || (r[v.groups] = [])).push(v); + if (v.groupsLabel && ['91001', '91006'].includes(v.groupsKey)) { // , '91006' + (r[v.groupsLabel] || (r[v.groupsLabel] = [])).push(v); + } return r; }, {}); - console.log(groupsData, 'groupsData', queryData); - const kpi = { label: '', value: 1200000 }; // todo: 标注KPI this.sideData.loading = false; this.sideData.dataSource = groupsData; this.sideData.monthData = sortResult; - this.sideData.kpi = kpi; + // const kpi = { label: '', value: 1200000 }; // 标注KPI + // this.sideData.kpi = kpi; }); } }); } + /** + * TOP + */ fetchTradeDataByType(orderType, queryData) { - this.topData[orderType] = { loading: true, dataSource: [] }; - Object.assign(queryData, { groupType: 'orderType', groupDateType: 'year' }); - req.fetchJSON('/service-web/QueryData/GetTradeOrderByType', queryData).then((json) => { + this.topData[orderType] = { loading: true, dataSource: [], originData: [] }; + Object.assign(queryData, { groupType: orderType, groupDateType: 'year' }); + this.fetchTradeData(queryData).then((json) => { if (json.errcode === 0) { runInAction(() => { this.topData[orderType].loading = false; this.topData[orderType].dataSource = json.result1; - console.log({ loading: false, ...json }, orderType, 'topData'); }); } }); } + /** + * 获取业绩数据 + */ + async fetchTradeData(queryData) { + const json = await req.fetchJSON('/service-Analyse2/GetTradeProcess', queryData); + if (json.errcode === 0) { + return json; + } + return null; + } + setStateSearch(body) { this.searchPayloadHome = body; } - summaryData = { loading: false, dataSource: [] }; - sideData = { loading: false, dataSource: {}, kpi: {}, monthData: [] }; + summaryData = { loading: false, dataSource: [], kpi: {}, }; + timeData = { loading: false, dataSource: [] }; + BuData = { loading: false, dataSource: [] }; + sideData = { loading: false, dataSource: {}, monthData: [] }; + dataForSort = {}; topData = {}; defaultDataSubject = 'CJCount'; searchPayloadHome = {}; diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 0071d86..98336c1 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -3,27 +3,26 @@ import { observer } from 'mobx-react'; 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 '../components/StatisticCard'; -import Bullet from '../components/Bullet'; +import Bullet from '../components/BulletWithSort'; import Waterfall from '../components/Waterfall'; -import Column from '../components/Column'; import DataFieldRadio from './../components/DateFieldRadio'; import SearchForm from './../components/search/SearchForm'; - import { empty } from './../utils/commons'; +import { dataFieldAlias } from './../libs/ht'; import './home.css'; export default observer(() => { const navigate = useNavigate(); const { TradeStore } = useContext(stores_Context); - const { searchPayloadHome, sideData, summaryData, topData } = TradeStore; + const { searchPayloadHome, sideData, summaryData, BuData, topData } = TradeStore; const topSeries = [ - { key: 'Country', label: '国籍' }, - { key: 'Area', label: '目的地' }, - { key: 'Sales', label: '顾问' }, - { key: 'GuestGroupType', label: '客群类别' }, + { key: 'country', label: '国籍' }, + { key: 'dept', label: '小组' }, + { key: 'operator', label: '顾问' }, + { key: 'destination', label: '目的地' }, + // { key: 'GuestGroupType', label: '客群类别' }, ]; useEffect(() => { @@ -35,6 +34,7 @@ export default observer(() => { const pageRefresh = (queryData) => { TradeStore.fetchSummaryData(queryData); + TradeStore.fetchTradeDataByBU(queryData); TradeStore.fetchTradeDataByMonth(queryData); for (const iterator of topSeries) { TradeStore.fetchTradeDataByType(iterator.key, queryData); @@ -59,55 +59,42 @@ export default observer(() => { const [BulletConfig, setBulletConfig] = useState({ measureField: 'SumML', // rangeField: 'SumMLRange', // - targetField: 'SumMLKPI', // - xField: 'OrderType', + targetField: 'MLKPIvalue', // + xField: 'groupsLabel', }); const handleChangeValueKey = (key) => { setValueKey(key); setBulletConfig({ measureField: key, rangeField: `${key}Range`, - targetField: `${key}KPI`, - xField: 'OrderType', + // targetField: `${key}KPI`, + targetField: dataFieldAlias[key].nestkey.p, + xField: 'groupsLabel', }); }; const WaterfallConfig = { xField: 'groupDateVal', yField: 'SumML', + seriesField: 'groupsLabel', meta: { groupDateVal: { alias: '月份', + // type: 'cat', }, }, label: { - formatter: (v) => ((v.SumML / sideData.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) + '%', }, }; - 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, - // }, - // }, - // }; - // }), + const BUConfig = { + measureField: 'SumML', // + rangeField: 'SumMLRange', // + targetField: 'MLKPIvalue', // + xField: 'groupsLabel', + layout: 'vertical', }; - return ( <> @@ -115,14 +102,14 @@ export default observer(() => { { @@ -148,14 +135,15 @@ export default observer(() => {

市场进度

- + - + {Object.keys(sideData.dataSource).map((key) => ( - + +

{key}

))}
@@ -173,7 +161,7 @@ export default observer(() => {

{item.label}

- +
))}