From 6ea43a58e28e0b5f5bc3929ec271e35abf6e19ec Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 10 Apr 2025 14:20:51 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E8=80=81=E5=AE=A2=E6=88=B7:=20?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E8=B5=B0=E5=8A=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/charts/Customer_care_regular.jsx | 22 ++-- src/components/LineWithAvg.jsx | 168 +++++++++++++++++++++++++++ src/stores/CustomerStore.js | 35 ++++++ 3 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 src/components/LineWithAvg.jsx diff --git a/src/charts/Customer_care_regular.jsx b/src/charts/Customer_care_regular.jsx index a99c058..75e7ec6 100644 --- a/src/charts/Customer_care_regular.jsx +++ b/src/charts/Customer_care_regular.jsx @@ -1,16 +1,17 @@ -import React, { useContext, useEffect } from 'react'; +import React, { useContext } from 'react'; import { Row, Col, Divider, Table, Tooltip } from 'antd'; import { InfoCircleOutlined } from '@ant-design/icons'; import { utils, writeFileXLSX } from 'xlsx'; import { stores_Context } from '../config'; import { observer } from 'mobx-react'; import SearchForm from './../components/search/SearchForm'; +import LineWithAvg from '../components/LineWithAvg'; const Customer_care_regular = () => { const { orders_store, date_picker_store, customer_store } = useContext(stores_Context); const regular_data = customer_store.regular_data; - useEffect(() => {}, []); + // useEffect(() => {}, []); return (
@@ -66,7 +67,7 @@ const Customer_care_regular = () => { <> {text}   { - {index === 0 && regular_data.total_data_tips!=='' && } + {index === 0 && regular_data.total_data_tips!=='' && } } ), @@ -98,6 +99,11 @@ const Customer_care_regular = () => { rowKey={(record) => record.ItemName} /> + + + + + { { key: 'COLI_ApplyDate', }, { - title: '订单状态', + title: '订单状态',width: '4rem', dataIndex: 'OrderState', key: 'OrderState', render: (text, record) => {text == 1 ? '成行' : '未成行'}, @@ -188,17 +194,17 @@ const Customer_care_regular = () => { dataIndex: 'COLI_LineClass', key: 'COLI_LineClass', }, - { title: '上次 订单号', dataIndex: 'coli_id_Last', key: 'coli_id_Last', width: '2em', + { title: '上次 订单号', dataIndex: 'coli_id_Last', key: 'coli_id_Last', width: '4em', render: (_, r) => ({ props: { style: { backgroundColor: '#5B8FF9'+'1A' } }, children: _, }), }, - { title: '上次 走团日期', dataIndex: 'COLI_OrderStartDate_Last', key: 'COLI_OrderStartDate_Last',width: '2em', + { title: '上次 走团日期', dataIndex: 'COLI_OrderStartDate_Last', key: 'COLI_OrderStartDate_Last',width: '4em', render: (_, r) => ({ props: { style: { backgroundColor: '#5B8FF9'+'1A' } }, children: _, }), }, - { title: '上次 小组', dataIndex: 'Department_Last', key: 'Department_Last',width: '2em', + { title: '上次 小组', dataIndex: 'Department_Last', key: 'Department_Last',width: '4em', render: (_, r) => ({ props: { style: { backgroundColor: '#5B8FF9'+'1A' } }, children: _, diff --git a/src/components/LineWithAvg.jsx b/src/components/LineWithAvg.jsx new file mode 100644 index 0000000..4ca77f1 --- /dev/null +++ b/src/components/LineWithAvg.jsx @@ -0,0 +1,168 @@ +import React, { useEffect, useState } from 'react'; +import { Row, Col, Spin } from 'antd'; +import { Line } from '@ant-design/plots'; +import { observer } from 'mobx-react'; +import { dataFieldAlias } from '../libs/ht'; +import DateGroupRadio from '../components/DateGroupRadio'; +import { cloneDeep, groupBy, sortBy } from '../utils/commons'; + +export default observer((props) => { + const { dataSource: rawData, showAVG, showSUM, loading, ...config } = props; + const { xField, yField, yFieldAlias, seriesField } = config; + + const [dataBeforeXChange, setDataBeforeXChange] = useState([]); + + const [dataSource, setDataSource] = useState([]); + const [sumSeries, setSumSeries] = useState([]); + + const line_config = { + // data: dataSource, + padding: 'auto', + xField, + yField, + seriesField, + // seriesField: 'rowLabel', + // xAxis: { + // type: 'timeCat', + // }, + yAxis: { + min: 0, + maxTickInterval: 5, + }, + meta: { + ...cloneDeep(dataFieldAlias), + }, + // smooth: true, + label: {}, // 显示标签 + legend: { + position: 'right-top', + // title: { + // text: '总合计 ' + dataSource.reduce((a, b) => a + b.SumOrder, 0), + // }, + itemMarginBottom: 12, // 垂直间距 + }, + tooltip: { + customItems: (originalItems) => { + return originalItems + .map((ele) => ({ ...ele, valueR: ele.data[yField] })) + .sort(sortBy('valueR')) + .reverse(); + }, + }, + }; + + const [lineConfig, setLineConfig] = useState(cloneDeep(line_config)); + + useEffect(() => { + resetX(); + + return () => {}; + }, [rawData]); + + + useEffect(() => { + if (lineChartX === 'day') { + setDataBeforeXChange(dataSource); + } + + return () => {}; + }, [dataSource]); + + // 日月年切换 + const [lineChartX, setLineChartX] = useState('day'); + const orderCountDataMapper = { data1: 'data1', data2: undefined }; + const orderCountDataFieldMapper = { 'dateKey': xField, 'valueKey': yField, 'seriesKey': seriesField, _f: 'sum' }; + const resetX = () => { + setLineChartX('day'); + setDataSource(rawData); + setDataBeforeXChange(rawData); + // 初始化`平均`线, `总计`线 + const byDays = groupBy(rawData, xField); + const sumY = rawData.reduce((a, b) => a + b[yField], 0); + const avgVal = Math.round(sumY / (Object.keys(byDays).length)); + const avgLine = [ + { type: 'text', position: ['start', avgVal], content: avgVal, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } }, + { type: 'line', start: [-10, avgVal], end: ['max', avgVal], style: { stroke: '#F4664A', lineDash: [2, 2] } }, + ]; + setLineConfig({ ...lineConfig, yField, xField, annotations: avgLine }); + if (showSUM) { + const _sumLine = Object.keys(byDays).reduce((r, _d) => { + const summaryVal = byDays[_d].reduce((rows, row) => rows + row[yField], 0); + r.push({ ...byDays[_d][0], [yField]: summaryVal, [seriesField]: '总计' }); + return r; + }, []); + // console.log(_sumLine.map((ele) => ele[yField])); + setSumSeries(_sumLine); + } + }; + + const onChangeXDateFieldGroup = (value, data, avg1) => { + // console.log(value, data, avg1); + const _sumLine = []; + const { xField, yField, seriesField } = lineConfig; + const groupByDate = data.reduce((r, v) => { + (r[v[xField]] || (r[v[xField]] = [])).push(v); + return r; + }, {}); + // console.log(groupByDate); + const _data = Object.keys(groupByDate).reduce((r, _d) => { + const summaryVal = groupByDate[_d].reduce((rows, row) => rows + row[yField], 0); + _sumLine.push({ ...groupByDate[_d][0], [yField]: summaryVal, [seriesField]: '总计' }); + + const xAxisGroup = groupByDate[_d].reduce((a, v) => { + (a[v[seriesField]] || (a[v[seriesField]] = [])).push(v); + return a; + }, {}); + // console.log(xAxisGroup); + Object.keys(xAxisGroup).map((_group) => { + const summaryVal = xAxisGroup[_group].reduce((rows, row) => rows + row[yField], 0); + r.push({ ...xAxisGroup[_group][0], [yField]: summaryVal, }); + return _group; + }); + return r; + }, []); + // const _sum = Object.values(groupBy(_data, 'dateGroup')).reduce((ac, b) => ({...b, [yField]: 0}), {}); + // console.log(xField, avg1); + // console.log('date source=====', _data); + setLineChartX(value); + setDataSource(_data); + if (showSUM) { + setSumSeries(_sumLine); + } + + // setAvgLine1(avg1); + const avg1Int = Math.round(avg1); + const mergedConfig = { ...lineConfig, + annotations: [ + { type: 'text', position: ['start', avg1Int], content: avg1Int, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } }, + { type: 'line', start: [-10, avg1Int], end: ['max', avg1Int], style: { stroke: '#F4664A', lineDash: [2, 2] } }, + ], + }; + // console.log(mergedConfig); + setLineConfig(mergedConfig); + }; + return ( +
+ +
+

+ 走势: {dataFieldAlias[lineConfig.yField].label} +

+ + + + + + + + + + ); +}); diff --git a/src/stores/CustomerStore.js b/src/stores/CustomerStore.js index e6686ea..fbda875 100644 --- a/src/stores/CustomerStore.js +++ b/src/stores/CustomerStore.js @@ -2,6 +2,8 @@ import {makeAutoObservable, runInAction} from "mobx"; import { fetchJSON } from '../utils/request'; import * as config from "../config"; import { groupsMappedByKey, sitesMappedByCode, pivotBy } from './../libs/ht'; +import { sortBy } from "../utils/commons"; +import moment from 'moment'; /** * 用于透视的数据 @@ -100,7 +102,10 @@ class CustomerStore { // 老客户 beign regular_customer_order(get_detail = false) { + let pivotByOrder = 'SumOrder'; + let pivotByDate = 'applyDate'; this.regular_data.loading = true; + this.regular_data.detail_loading = get_detail; const date_picker_store = this.rootStore.date_picker_store; let url = '/service-tourdesign/RegularCusOrder'; url += '?Website=' + this.regular_data.webcode.toString() + '&DEI_SNList=' + this.regular_data.groups.toString(); @@ -108,8 +113,12 @@ class CustomerStore { url += '&ApplydateCheck=1&EntrancedateCheck=0&ConfirmDateCheck=0'; } else if(String(this.regular_data.date_type).toLowerCase() === 'confirmdate'){ url += '&ApplydateCheck=0&EntrancedateCheck=0&ConfirmDateCheck=1'; + pivotByOrder = 'ConfirmOrder'; + pivotByDate = 'confirmDate'; }else { url += '&ApplydateCheck=0&EntrancedateCheck=1&ConfirmDateCheck=0'; + pivotByOrder = 'ConfirmOrder'; + pivotByDate = 'startDate'; } url += '&ApplydateStart=' + date_picker_store.start_date.format(config.DATE_FORMAT) + '&ApplydateEnd=' + date_picker_store.end_date.format(config.SMALL_DATETIME_FORMAT); url += '&EntrancedateStart=' + date_picker_store.start_date.format(config.DATE_FORMAT) + '&EntrancedateEnd=' + date_picker_store.end_date.format(config.SMALL_DATETIME_FORMAT); @@ -126,9 +135,30 @@ class CustomerStore { .then((json) => { runInAction(() => { if (get_detail) { + this.regular_data.detail_loading = false; this.regular_data.data_detail = json; const dump_l = (json || []).filter(ele => ele.COLI_IsOld !== '' && ele.COLI_IsCusCommend !== '').length; this.regular_data.total_data_tips = dump_l > 0 ? `包含 ${dump_l} 条同时勾选的数据` : ''; + /** 使用明细数据画图 */ + const data_detail = (json || []).map((ele) => ({ + ...ele, + key: ele.COLI_ID, + orderState: ele.OrderState, + applyDate: moment(ele.COLI_ApplyDate).format('YYYY-MM-DD'), + startDate: ele.COLI_OrderStartDate, + confirmDate: moment(ele.COLI_ConfirmDate).format('YYYY-MM-DD'), + })); + const { data: IsOldData, } = pivotBy(data_detail.filter(ele => ele.COLI_IsOld === '是'), [['COLI_IsOld', ], [], pivotByDate]); + const { data: isCusCommendData, } = pivotBy(data_detail.filter(ele => ele.COLI_IsCusCommend === '是'), [['COLI_IsCusCommend', ], [], pivotByDate]); + // console.log('IsOldData====', IsOldData, '\nisCusCommend', isCusCommendData); + // 合并成两个系列 + const seriesData = [].concat(IsOldData.map(ele => ({...ele, _ylabel: '老客户'})), isCusCommendData.map(ele => ({...ele, _ylabel: '老客户推荐'})),).sort(sortBy(pivotByDate)); + // console.log('seriesData====', seriesData); + + this.regular_data.pivotData = seriesData; // { IsOldData, isCusCommendData, }; + this.regular_data.pivotY = pivotByOrder; + this.regular_data.pivotX = pivotByDate; + } else { this.regular_data.data = json; } @@ -159,6 +189,7 @@ class CustomerStore { regular_data = { loading: false, + detail_loading: false, data: [], data_detail: [], total_data_tips: '', @@ -179,6 +210,10 @@ class CustomerStore { WebCode: undefined, // ['ALL'].map(kk => sitesMappedByCode[kk]), DateType: { key: 'applyDate', label: '提交日期'}, }, + + pivotData: [], + pivotY: 'SumOrder', + pivotX: 'applyDate', }; // 老客户 end