From 84722e0d0a19ac86f76eb5f0a789127b2f537da2 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 27 Oct 2023 09:44:12 +0800 Subject: [PATCH 01/86] style: --- src/components/MixTBWithKPI.jsx | 7 ++++++- src/components/kpi/SubjectTable/Profit.jsx | 2 ++ src/stores/Trade.js | 8 ++++---- src/views/Home.jsx | 2 +- src/views/Sale_KPI.jsx | 2 ++ 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/MixTBWithKPI.jsx b/src/components/MixTBWithKPI.jsx index 1500eee..5fd1eef 100644 --- a/src/components/MixTBWithKPI.jsx +++ b/src/components/MixTBWithKPI.jsx @@ -107,9 +107,14 @@ export default observer((props) => { // color: '#b32b19', color: '#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, }; }, label: { @@ -122,7 +127,7 @@ export default observer((props) => { point: { style: { stroke: '#F4664A', - lineWidth: 1.5, + lineWidth: 0.5, }, }, }; diff --git a/src/components/kpi/SubjectTable/Profit.jsx b/src/components/kpi/SubjectTable/Profit.jsx index 93385ee..cf3b75b 100644 --- a/src/components/kpi/SubjectTable/Profit.jsx +++ b/src/components/kpi/SubjectTable/Profit.jsx @@ -84,6 +84,7 @@ export default observer((props) => { dataIndex: 'object_id', editable: false, width: '5em', + fixed: 'left', render: (_, r) => r.object_name, }, { @@ -92,6 +93,7 @@ export default observer((props) => { valueType: 'digit', fieldProps: { style: { width: '100%' }, step: 10000 * 100 }, width: '6em', + fixed: 'left', formItemProps: { style: { width: '100%' }, }, diff --git a/src/stores/Trade.js b/src/stores/Trade.js index 182d411..46108ed 100644 --- a/src/stores/Trade.js +++ b/src/stores/Trade.js @@ -23,7 +23,7 @@ class Trade { loading: false, dataSource: [ { - title: '成团', + title: '成团',col: 6, value: summary?.[0]?.ConfirmOrder, originVal: (summary?.[0]?.ConfirmOrder || 0), valueSuffix: summary?.[0]?.ConfirmRates ? ` / ${summary?.[0]?.ConfirmRates} %` : undefined, @@ -37,7 +37,7 @@ class Trade { biz: { title: '商务', value: biz?.[0]?.ConfirmOrder }, }, { - title: '毛利', + title: '毛利',col: 8, originVal: (summary?.[0]?.SumML || 0), value: dataFieldAlias.SumML.formatter(summary?.[0]?.SumML || 0) + '=' + dataFieldAlias.SumML.formatter((traditional?.[0]?.SumML || 0)) + '+' + dataFieldAlias.SumML.formatter((biz?.[0]?.SumML || 0)), KPIrate: summary?.[0]?.[dataFieldAlias.SumML.nestkey.p], @@ -48,7 +48,7 @@ class Trade { biz: { title: '商务', value: (biz?.[0]?.SumML || 0) }, }, { - title: '完成率', + title: '完成率',col: 5, originVal: (summary?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0), value: `${summary?.[0]?.[dataFieldAlias.SumML.nestkey.p] || ''}%`, hasKPI: false, @@ -58,7 +58,7 @@ class Trade { biz: { title: '商务', value: biz?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0, }, }, { - title: '人数', + title: '人数', col: 5, originVal: (summary?.[0]?.SumPersonNum || 0), value: summary?.[0]?.SumPersonNum, // VSrate: summary?.[0]?.SumPersonNumrate, diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 910b6df..c0ab571 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -195,7 +195,7 @@ export default observer(() => { ))} */} {summaryData.dataSource.map((item, i) => ( - + ))} diff --git a/src/views/Sale_KPI.jsx b/src/views/Sale_KPI.jsx index 54b2379..84caf2f 100644 --- a/src/views/Sale_KPI.jsx +++ b/src/views/Sale_KPI.jsx @@ -68,11 +68,13 @@ const Sale_KPI = () => { dataIndex: 'groupsLabel', editable: false, width: '7.5em', + fixed: 'left', }, { title: '年度', dataIndex: 'yearValue', width: '10em', + fixed: 'left', render: (_, row) => (
From 34bb49f4877a4423443fd63f93c1650020615fec Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 27 Oct 2023 13:47:38 +0800 Subject: [PATCH 02/86] =?UTF-8?q?feat:=20=E9=A6=96=E9=A1=B5:=20=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E6=98=BE=E7=A4=BA=E5=9B=BD=E7=B1=8D=E4=B8=9A=E7=BB=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/MapCountry.jsx | 109 ++++++++++++++++++++++++++++++++++ src/views/Home.jsx | 22 ++++++- 2 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 src/components/MapCountry.jsx diff --git a/src/components/MapCountry.jsx b/src/components/MapCountry.jsx new file mode 100644 index 0000000..e7fdce9 --- /dev/null +++ b/src/components/MapCountry.jsx @@ -0,0 +1,109 @@ +import { useContext, useState, useEffect, useMemo } from 'react'; +import { observer } from 'mobx-react'; +import { ChoroplethMap } from '@ant-design/maps'; +import { dataFieldAlias } from '../libs/ht'; +import { cloneDeep } from '../utils/commons'; + +export default observer((props) => { + const { dataSource, sourceField, valueField, ...extConfig } = props; + + const [mdataSource, setMdataSource] = useState([]); + useEffect(() => { + const dataMapped = (cloneDeep(dataSource) || []).reduce((r, v) => ({...r, + [(v.groupsLabel || '_').replace('(待删除)', '')]: v + }), {}); + if (dataMapped?.['中国']) { + dataMapped['中国'].groupsLabel = '中华人民共和国'; + } + setMdataSource(Object.values(dataMapped)); + return () => {}; + }, [dataSource, valueField]); + + const config = { + container: '#topC', + map: { + // type: 'amap', + type: 'mapbox', + // style: 'blank', + center: [120.19382669582967, 30.258134], + zoom: 3, + pitch: 0, + // scrollZoom: false, + // dragPan: false, + // zoomEnable: false, + // token: 'd78b5ba25a4699a1cb567b7a933e630b', // amap + }, + // geoArea: { + // url: 'https://gw.alipayobjects.com/os/alisis/geo-data-v0.1.2/choropleth-data', + // type: 'topojson', + // }, + source: { + data: mdataSource.filter((ele) => ele[sourceField]), + joinBy: { + geoField: 'name', + sourceField: sourceField || 'name', + }, + }, + autoFit: true, + color: { + field: valueField || 'value', + value: [ + '#001D70', + '#0047A5', + '#1A4397', + '#2555B7', + '#3165D1', + '#3D76DD', + '#467BE8', + '#6296FE', + '#7EA6F9', + '#98B7F7', + '#BDD0F8', + '#DDE6F7', + '#F2F5FC'].reverse(), + scale: { type: 'quantile' }, + }, + viewLevel: { + // level: 'country', + // adcode: '100000', + // granularity: 'province', + level: 'world', + adcode: 'all', + granularity: 'country', + }, + chinaBorder: false, + style: { + opacity: 1, + stroke: '#fff', + lineWidth: 0.6, + lineOpacity: 1, + }, + state: { + active: { + stroke: 'yellow', + lineWidth: 0.6, + // lineOpacity: 0.8, + }, + }, + label: { + visible: true, + field: 'name', + style: { + fill: '#000', + opacity: 0.8, + fontSize: 10, + stroke: '#fff', + strokeWidth: 1.5, + textAllowOverlap: false, + padding: [8, 8], + }, + }, + tooltip: { + items: ['name', { field: valueField, alias: dataFieldAlias[valueField].alias, customValue: (v) => dataFieldAlias[valueField].formatter(v) }], + }, + zoom: false, + legend: false, + }; + + return ; +}); diff --git a/src/views/Home.jsx b/src/views/Home.jsx index c0ab571..1cce101 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -8,6 +8,7 @@ import Bullet from '../components/BulletWithSort'; import Waterfall from '../components/Waterfall'; import MixTBWithKPI from './../components/MixTBWithKPI'; import Donut from './../components/Donut'; +import MapCountry from './../components/MapCountry'; import DataFieldRadio from '../components/DataFieldRadio'; import { datePartOptions } from './../components/DateGroupRadio/date'; import SearchForm from './../components/search/SearchForm'; @@ -18,7 +19,7 @@ import './home.css'; const topSeries = [ { key: 'dept', label: '小组', graphVisible: true }, { key: 'operator', label: '顾问', graphVisible: true }, - { key: 'country', label: '国籍', graphVisible: true }, + { key: 'country', label: '国籍', graphVisible: false }, { key: 'GuestGroupType', label: '客群类别', graphVisible: false }, { key: 'destination', label: '目的地', graphVisible: true }, ]; @@ -252,10 +253,10 @@ export default observer(() => {
- + {topSeriesSet.map((item) => item.graphVisible ? ( - +

{item.label}

@@ -265,6 +266,21 @@ export default observer(() => { )}
+
+ + + +

国籍

+ + + +
+ +
+ +
+
+
); }); From 34080fce8b9b80a767bfc743ef8eb626c801d5bd Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 27 Oct 2023 14:17:02 +0800 Subject: [PATCH 03/86] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5:=20=E8=B5=B0?= =?UTF-8?q?=E5=8A=BF=E4=B8=A4=E4=B8=AAY=E8=BD=B4=E4=B8=8D=E4=B8=80?= =?UTF-8?q?=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/MixTBWithKPI.jsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/MixTBWithKPI.jsx b/src/components/MixTBWithKPI.jsx index 5fd1eef..37dce81 100644 --- a/src/components/MixTBWithKPI.jsx +++ b/src/components/MixTBWithKPI.jsx @@ -51,7 +51,13 @@ export default observer((props) => { shape: 'cicle', }, xAxis: false, - yAxis: false, + yAxis: { + line: null, + grid: null, + label: false, + position: 'left', + min: 0, + }, meta: { [yField]: { sync: true, @@ -98,6 +104,13 @@ export default observer((props) => { yField, seriesField, xAxis: false, + yAxis: { + // line: null, + // grid: null, + // label: false, + position: 'left', + min: 0, + }, meta: merge( { ...cloneDeep(dataFieldAlias), From 21e70b9dfbf845b43d255645ff577287b90d2253 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 27 Oct 2023 15:20:04 +0800 Subject: [PATCH 04/86] =?UTF-8?q?style:=20=E9=A6=96=E9=A1=B5:=20TOP?= =?UTF-8?q?=E6=9D=A1=E5=BD=A2=E5=9B=BE:=20=E8=B4=9F=E6=95=B0=E7=BA=A2?= =?UTF-8?q?=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BulletWithSort.jsx | 25 ++++++++++++++++++++----- src/components/MapCountry.jsx | 2 +- src/views/Home.jsx | 18 +++++------------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/components/BulletWithSort.jsx b/src/components/BulletWithSort.jsx index b106e3a..883cdac 100644 --- a/src/components/BulletWithSort.jsx +++ b/src/components/BulletWithSort.jsx @@ -13,32 +13,47 @@ export default observer((props) => { const maxKPI = Math.max(...(origin || []).map((ele) => (ele?.[targetField] || 0))); const maxValue = Math.max(...(origin || []).map((ele) => ele[measureField])); const _max = Math.max(maxKPI, maxValue); + const minValue = Math.min(...(origin || []).map((ele) => ele[measureField])); + const _min = Math.ceil(Math.min(0, minValue)); const sortData = origin.sort(sortBy(measureField)).slice(-itemLength); // 顶格的值定在更远 const _parseData = sortData?.map((ele) => ({ ...ele, - [rangeField]: [0, Math.ceil(_max / 0.9)], + [rangeField]: [_min, Math.ceil(_max / 0.9)], // [measureField]: [ele[measureField]], [measureField]: ele[measureFieldArrKey] || [ele[measureField]], [targetField]: (ele?.[targetField] || 0) })); - return _parseData; + return { _parseData, _max, _min }; }; const dataMapped = dataSource.reduce((r, v) => ({...r, [v.groupsLabel]: v}), {}); const ifMergeTB = isEmpty(dataSource) ? false : !isEmpty(dataSource[0]?.[`${extProps.measureField}_arr`]); const [parseData, setParseData] = useState([]); + const [maxV, setMaxV] = useState(0); + const [minV, setMinV] = useState(0); useEffect(() => { - setParseData(dataParser(dataSource)); + const _pdata = dataParser(dataSource); + setParseData(_pdata._parseData); + setMaxV(_pdata._max); + setMinV(_pdata._min); return () => {}; }, [extProps.measureField, dataSource]); const config = merge({ color: { - range: [ '#FFF3E1', '#FFF3E1'], - // range: [ '#FFF3E1', '#FFF3E1', '#FFe0b0', '#bfeec8'], // '#FFbcb8', '#FFe0b0', + range: [].concat((minV < 0 ? ['#ffe4e4'] : []), [ '#FFF3E1', '#FFF3E1']), measure: ['#5B8FF9', '#61ddaa'], target: '#FF9845', }, + bulletStyle: { + measure: (item, ...r) => { + if (item[extProps.measureField] < 0) { + return { + fill: '#F4664A', + }; + } + }, + }, label: { target: false, measure: { diff --git a/src/components/MapCountry.jsx b/src/components/MapCountry.jsx index e7fdce9..9e6a444 100644 --- a/src/components/MapCountry.jsx +++ b/src/components/MapCountry.jsx @@ -26,7 +26,7 @@ export default observer((props) => { type: 'mapbox', // style: 'blank', center: [120.19382669582967, 30.258134], - zoom: 3, + zoom: 2, pitch: 0, // scrollZoom: false, // dragPan: false, diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 1cce101..fbf37bc 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -19,9 +19,9 @@ import './home.css'; const topSeries = [ { key: 'dept', label: '小组', graphVisible: true }, { key: 'operator', label: '顾问', graphVisible: true }, - { key: 'country', label: '国籍', graphVisible: false }, - { key: 'GuestGroupType', label: '客群类别', graphVisible: false }, { key: 'destination', label: '目的地', graphVisible: true }, + { key: 'GuestGroupType', label: '客群类别', graphVisible: false }, + { key: 'country', label: '国籍', graphVisible: true }, ]; // const iconSets = [CheckCircleTwoTone, , , , ,,]; @@ -264,22 +264,14 @@ export default observer(() => { ) : null )} -
- -
- - - -

国籍

- - - + +
+
-
); From 23de7481bd721117bd042513f1a43ccbb9987e05 Mon Sep 17 00:00:00 2001 From: YCC Date: Mon, 30 Oct 2023 14:51:51 +0800 Subject: [PATCH 05/86] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E6=9D=83=E9=99=90?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E4=B8=BA=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 12 ++--- src/stores/AuthStore.js | 100 +++++++++++++++++------------------ src/views/ProtectedRoute.jsx | 49 +++++++---------- 3 files changed, 75 insertions(+), 86 deletions(-) diff --git a/package-lock.json b/package-lock.json index eab1659..e1dc4cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7283,9 +7283,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==" + "version": "1.0.30001553", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001553.tgz", + "integrity": "sha512-N0ttd6TrFfuqKNi+pMgWJTb9qrdJu4JSpgPFLe/lrD19ugC6fZgF0pUewRowDwzdDnb9V41mFcdlYgl/PyKf4A==" }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -26806,9 +26806,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==" + "version": "1.0.30001553", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001553.tgz", + "integrity": "sha512-N0ttd6TrFfuqKNi+pMgWJTb9qrdJu4JSpgPFLe/lrD19ugC6fZgF0pUewRowDwzdDnb9V41mFcdlYgl/PyKf4A==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", diff --git a/src/stores/AuthStore.js b/src/stores/AuthStore.js index 5e51f7f..6c2bf36 100644 --- a/src/stores/AuthStore.js +++ b/src/stores/AuthStore.js @@ -1,59 +1,59 @@ -import { makeAutoObservable, runInAction } from "mobx"; -import * as dd from "dingtalk-jsapi"; -import * as config from "../config"; +import { makeAutoObservable, runInAction } from 'mobx'; +import * as dd from 'dingtalk-jsapi'; +import * as config from '../config'; // 权限管理 class AuthStore { - constructor(rootStore) { - this.rootStore = rootStore; - makeAutoObservable(this); - if (process.env.NODE_ENV == "production") { - this.get_auth(); // 放到钉钉环境才能开启 - } - } + constructor(rootStore) { + this.rootStore = rootStore; + makeAutoObservable(this); + if (process.env.NODE_ENV === 'production') { + this.get_auth(); // 放到钉钉环境才能开启 + } + } - auth = ["admin"]; // 开发时候用,正式环境留空 - user = { name: "loading", userid: "..." }; // 开发时候用,正式环境留空 + auth = process.env.NODE_ENV === 'production' ? [] : ['admin']; // 开发时候用,正式环境留空 + user = { name: 'loading', userid: '...' }; // 开发时候用,正式环境留空 - has_permission(requireds) { - if (Object.keys(requireds).length == 0) { - return true; - } - const has_permission = requireds.filter(item => this.auth.includes(item)); - if (Object.keys(has_permission).length !== 0) { - return true; - } - return false; - } + has_permission(requireds) { + if (Object.keys(requireds).length === 0) { + return true; + } + const has_permission = requireds.filter((item) => this.auth.includes(item)); + if (Object.keys(has_permission).length !== 0) { + return true; + } + return false; + } - // 请求权限 - get_auth() { - const _this = this; - const CORPID = "ding48bce8fd3957c96b"; // 企业的id - dd.runtime.permission.requestAuthCode({ - corpId: CORPID, - onSuccess: function (res) { - console.log(res); - const code = res.code; - const url = "/dingtalk/dingtalkwork/Getusers_auth?code=" + code; - // 请求获取HT接口获取用户权限和用户信息 - fetch(config.HT_HOST + url) - .then(response => response.json()) - .then(json => { - runInAction(() => { - _this.user = json.result; - _this.auth = json.result.authlist; - }); - }) - .catch(error => { - console.log("fetch data failed", error); - }); - }, - onFail: function (err) { - console.log(err); - }, - }); - } + // 请求权限 + get_auth() { + const _this = this; + const CORPID = 'ding48bce8fd3957c96b'; // 企业的id + dd.runtime.permission.requestAuthCode({ + corpId: CORPID, + onSuccess: function (res) { + console.log(res); + const code = res.code; + const url = '/dingtalk/dingtalkwork/Getusers_auth?code=' + code; + // 请求获取HT接口获取用户权限和用户信息 + fetch(config.HT_HOST + url) + .then((response) => response.json()) + .then((json) => { + runInAction(() => { + _this.user = json.result; + _this.auth = json.result.authlist; + }); + }) + .catch((error) => { + console.log('fetch data failed', error); + }); + }, + onFail: function (err) { + console.log(err); + }, + }); + } } export default AuthStore; diff --git a/src/views/ProtectedRoute.jsx b/src/views/ProtectedRoute.jsx index 52f00f3..2f58972 100644 --- a/src/views/ProtectedRoute.jsx +++ b/src/views/ProtectedRoute.jsx @@ -1,36 +1,25 @@ -import React, {useContext, useEffect} from 'react'; -import {Row, Col, Button, Tabs, Spin, Result, Space} from 'antd'; -import { - ContainerOutlined, - SearchOutlined, -} from '@ant-design/icons'; -import {stores_Context} from '../config'; -import {Line} from "@ant-design/charts"; -import {observer} from 'mobx-react'; +import React, { useContext, useEffect } from 'react'; +import { Row, Col, Button, Tabs, Spin, Result, Space } from 'antd'; +import { ContainerOutlined, SearchOutlined } from '@ant-design/icons'; +import { stores_Context } from '../config'; +import { Line } from '@ant-design/charts'; +import { observer } from 'mobx-react'; import DatePickerCharts from '../components/search/DatePickerCharts'; -import {NavLink, useParams,Outlet, useOutlet, useLocation, useNavigate} from "react-router-dom"; -import * as comm from "../utils/commons"; -import * as config from "../config"; +import { NavLink, useParams, Outlet, useOutlet, useLocation, useNavigate } from 'react-router-dom'; +import * as comm from '../utils/commons'; +import * as config from '../config'; -const ProtectedRoute = ({auth}) => { - const {auth_store} = useContext(stores_Context); - - if (auth_store.has_permission(auth)) { - return ; - } - - return ( -
- -
- ); +const ProtectedRoute = ({ auth }) => { + const { auth_store } = useContext(stores_Context); + if (auth_store.has_permission(auth)) { + return ; + } + return ( +
+ +
+ ); }; export default observer(ProtectedRoute); - From 662e5ac2ae7537dfd0b6bb7e4d35f3d4cb098b91 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 30 Oct 2023 15:27:51 +0800 Subject: [PATCH 06/86] =?UTF-8?q?conf:=20=E8=B0=83=E6=95=B4=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 15 +++++++++------ src/views/Welcome.jsx | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/views/Welcome.jsx diff --git a/src/App.jsx b/src/App.jsx index 7dd8e9c..7900f94 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,7 +10,7 @@ import { DollarOutlined, AreaChartOutlined, WechatOutlined, - UserOutlined, FlagOutlined, PieChartOutlined, BarChartOutlined + UserOutlined, FlagOutlined, PieChartOutlined, BarChartOutlined, CoffeeOutlined, } from '@ant-design/icons'; import { Layout, Menu, Image, Badge } from 'antd'; import { BrowserRouter, Route, Routes, NavLink } from 'react-router-dom'; @@ -39,13 +39,15 @@ import ExchangeRate from './charts/ExchangeRate'; import KPI from './views/KPI'; import Distribution from './views/Distribution'; import Detail from './views/Detail'; +import Welcome from './views/Welcome'; const App = () => { const { Content, Footer, Sider } = Layout; const { auth_store } = useContext(stores_Context); const menu_items = [ - { key: 1, label: 主页, icon: }, + { key: 1, label: 欢迎, icon: }, + { key: 'annual', label: 综合看板, icon: }, { key: 2, label: '市场', @@ -176,8 +178,12 @@ const App = () => { }} > - } /> + } /> } /> + }> + } /> + } /> + }> } /> } /> @@ -198,9 +204,6 @@ const App = () => { } /> } /> - }> - } /> - }> } /> } /> diff --git a/src/views/Welcome.jsx b/src/views/Welcome.jsx new file mode 100644 index 0000000..19ff3da --- /dev/null +++ b/src/views/Welcome.jsx @@ -0,0 +1,26 @@ +import { useContext } from 'react'; +import { observer } from "mobx-react"; +import { stores_Context } from '../config'; +import { Table } from 'antd'; +import { + SlackOutlined, + SketchOutlined, + AntCloudOutlined, + RedditOutlined, + GithubOutlined +} from '@ant-design/icons'; + +export default observer((props) => { + // const { } = useContext(stores_Context); + return ( + <> +
+ +
+ 欢迎 +
+ +
+ + ); +}); From 619d5c25ed04b0f5780288235b60e0f3c48464ad Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 30 Oct 2023 16:57:00 +0800 Subject: [PATCH 07/86] 2.1.1 --- package-lock.json | 4 ++-- package.json | 2 +- src/App.jsx | 4 ++-- src/config.js | 2 ++ src/views/Welcome.jsx | 7 +++---- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index e1dc4cf..a44e472 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "0.1.0", + "version": "2.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "0.1.0", + "version": "2.1.1", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index d87a310..2eb0eea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "0.1.0", + "version": "2.1.1", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", diff --git a/src/App.jsx b/src/App.jsx index 7900f94..0c6e608 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -33,13 +33,13 @@ import Sale from './views/Sale'; import Sale_sub from './views/Sale_sub'; import Sale_KPI from './views/Sale_KPI'; import Logo from './logo.png'; -import { stores_Context } from './config'; import { observer } from 'mobx-react'; import ExchangeRate from './charts/ExchangeRate'; import KPI from './views/KPI'; import Distribution from './views/Distribution'; import Detail from './views/Detail'; import Welcome from './views/Welcome'; +import { stores_Context, APP_VERSION } from './config'; const App = () => { const { Content, Footer, Sider } = Layout; @@ -219,7 +219,7 @@ const App = () => { > {auth_store.user.name} ({auth_store.user.userid})
- Hainatravel Dashboard ©2022 Created by IT + Hainatravel Dashboard v{APP_VERSION} ©2022 Created by IT diff --git a/src/config.js b/src/config.js index 5fdeb46..b342d42 100644 --- a/src/config.js +++ b/src/config.js @@ -1,5 +1,7 @@ import React from "react"; +import packageInfo from './../package.json'; +export const APP_VERSION = packageInfo.version; export const stores_Context = React.createContext(); export const DATE_FORMAT = "YYYY-MM-DD"; export const SMALL_DATETIME_FORMAT = 'YYYY-MM-DD 23:59:00'; diff --git a/src/views/Welcome.jsx b/src/views/Welcome.jsx index 19ff3da..39f6137 100644 --- a/src/views/Welcome.jsx +++ b/src/views/Welcome.jsx @@ -1,6 +1,6 @@ import { useContext } from 'react'; import { observer } from "mobx-react"; -import { stores_Context } from '../config'; +import { stores_Context, APP_VERSION } from '../config'; import { Table } from 'antd'; import { SlackOutlined, @@ -16,9 +16,8 @@ export default observer((props) => { <>
-
- 欢迎 -
+
欢迎!
+
当前版本: v{APP_VERSION}
From de6feb85f5c51e497f3116e1d331fd8fd69528df Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 31 Oct 2023 11:10:35 +0800 Subject: [PATCH 08/86] =?UTF-8?q?perf:=20=E9=94=80=E5=94=AE=E4=B8=9A?= =?UTF-8?q?=E7=BB=A9:=20=E5=B0=8F=E7=BB=84=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/GroupSelect.jsx | 2 +- src/views/Sale.jsx | 6 +++--- src/views/Welcome.jsx | 28 ++++++++++++--------------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/components/search/GroupSelect.jsx b/src/components/search/GroupSelect.jsx index ba897bc..7622a16 100644 --- a/src/components/search/GroupSelect.jsx +++ b/src/components/search/GroupSelect.jsx @@ -28,7 +28,7 @@ class GroupSelect extends Component { }} labelInValue={false} maxTagCount={1} - maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`} + maxTagPlaceholder={(omittedValues) => `+${omittedValues.length}...`} allowClear={_mode != null} {...extProps} > diff --git a/src/views/Sale.jsx b/src/views/Sale.jsx index a55402f..7f64bc9 100644 --- a/src/views/Sale.jsx +++ b/src/views/Sale.jsx @@ -211,13 +211,13 @@ const Sale = () => { - - + + - + , - 2 + 3 ), // item( @@ -291,7 +293,7 @@ function getFields(props) { , - 2 + 3 ), item( 'years', @@ -300,7 +302,7 @@ function getFields(props) { {/* */} , - 2 + 3 ), item( 'months', @@ -308,7 +310,7 @@ function getFields(props) { , - 2 + 3 ), item( 'dates', diff --git a/src/stores/OrdersStore.js b/src/stores/OrdersStore.js index ba07033..ee8cb5c 100644 --- a/src/stores/OrdersStore.js +++ b/src/stores/OrdersStore.js @@ -73,6 +73,20 @@ class OrdersStore { this.include_tickets = value; }; + searchValues = { + DateType: { key: 'applyDate', label: '提交日期'}, + WebCode: { key: 'All', label: '所有来源'}, + IncludeTickets: { key: '1', label: '含门票'}, + DepartmentList: groupsMappedByCode.GH, + }; + + setSearchValues(obj, values) { + this.groups = obj.DepartmentList; + this.webcode = obj.WebCode; + this.include_tickets = obj.IncludeTickets; + this.date_type = obj.DateType; + } + // 切换标签页 onChange_Tabs_sub(ordertype, ordertype_sub, active_key) { this.active_tab_key_sub = active_key; diff --git a/src/views/Orders.jsx b/src/views/Orders.jsx index 917a8d5..aee44ed 100644 --- a/src/views/Orders.jsx +++ b/src/views/Orders.jsx @@ -1,5 +1,5 @@ import React, { Component } from "react"; -import { Row, Col, Button, Tabs, Table, Divider, Select, Radio } from "antd"; +import { Row, Col, Button, Tabs, Table, Divider, Select, Radio, Spin } from "antd"; import { ContainerOutlined, CarryOutOutlined, BlockOutlined, SmileOutlined, TagsOutlined, GlobalOutlined, SearchOutlined, FullscreenOutlined, DingtalkOutlined } from "@ant-design/icons"; import { stores_Context } from "../config"; import { Line, Pie } from "@ant-design/charts"; @@ -13,6 +13,8 @@ import { NavLink } from "react-router-dom"; import * as comm from "../utils/commons"; import { utils, writeFileXLSX } from "xlsx"; import DateGroupRadio from '../components/DateGroupRadio'; +import SearchForm from './../components/search/SearchForm'; + class Orders extends Component { static contextType = stores_Context; @@ -219,7 +221,7 @@ class Orders extends Component { } render() { - const { orders_store } = this.context; + const { orders_store, date_picker_store } = this.context; const table_data = orders_store.orderCountData_Form ? this.format_data(orders_store.orderCountData_Form) : []; const data_source = orders_store.orderCountData ? orders_store.orderCountData : []; const avg_line_y = Math.round(orders_store.avgLine1); @@ -310,55 +312,42 @@ class Orders extends Component { ], }; + const tableProps = { + dataSource: table_data.dataSource, + columns: table_data.columns, + size: 'small', + pagination: false, + scroll: { x: '100%' }, + loading: orders_store.loading, + }; + return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ + + { + orders_store.setSearchValues(obj, form); + orders_store.getOrderCount(); + orders_store.onChange_Tabs(orders_store.active_tab_key); + }} + /> + + - - - + - - orders_store.onChange_Tabs(active_key)}> - - - 来源类型 - - } - key="Form"> - - - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_form").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "来源类型.xlsx"); - }}> - 导出excel - - - - - - 产品类型 - - } - key="Product"> -
- - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_product").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "产品类型.xlsx"); - }}> - 导出excel - - - - - - 国籍 - - } - key="Country"> -
- - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_country").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "国籍.xlsx"); - }}> - 导出excel - - - - - - 线路 - - } - key="line"> -
- - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_line").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "线路.xlsx"); - }}> - 导出excel - - - - - - 目的地 - - } - key="city"> -
- - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_city").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "目的地.xlsx"); - }}> - 导出excel - - - - - - 页面类型 - - } - key="LineClass"> -
- - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_LineClass").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "页面类型.xlsx"); - }}> - 导出excel - - - - - - 客群类别 - - } - key="GuestGroupType"> -
- - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_GuestGroupType").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "客群类别.xlsx"); - }}> - 导出excel - - - - - - 出行动机 - - } - key="TravelMotivation"> -
- - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_TravelMotivation").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "出行动机.xlsx"); - }}> - 导出excel - - - - - - - - - - - - - - - - ); + + + + + + + + + orders_store.onChange_Tabs(active_key)} + items={[ + { + key: 'Form', + label: ( + + + 来源类型 + + ), + }, + { + key: 'Country', + label: ( + + + 国籍 + + ), + }, + { + key: 'line', + label: ( + + + 线路 + + ), + }, + { + key: 'city', + label: ( + + + 目的地 + + ), + }, + { + key: 'LineClass', + label: ( + + + 页面类型 + + ), + }, + { + key: 'GuestGroupType', + label: ( + + + 客群类别 + + ), + }, + { + key: 'TravelMotivation', + label: ( + + + 出行动机 + + ), + }, + ].map((ele) => { + return { + ...ele, + children: ( + <> +
+ + { + const wb = utils.table_to_book(document.getElementById(`table_to_xlsx_${ele.key}`).getElementsByTagName('table')[0]); + writeFileXLSX(wb, `${ele.key}.xlsx`); + }} + > + 导出excel + + + + ), + }; + })} + /> + + + + + + + + + + + + ); } } From 077b3b310857f4219edd2ece381880cf2720d767 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 3 Nov 2023 11:25:45 +0800 Subject: [PATCH 13/86] =?UTF-8?q?perf:=20=E9=94=80=E5=94=AE>=E4=B8=9A?= =?UTF-8?q?=E7=BB=A9=E6=95=B0=E6=8D=AE.=20=E5=BA=94=E7=94=A8=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/SiteSelect.jsx | 2 +- src/stores/SaleStore.js | 14 ++++++ src/views/Sale.jsx | 72 ++++++++++------------------ 3 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/components/search/SiteSelect.jsx b/src/components/search/SiteSelect.jsx index 2e25905..b5406f7 100644 --- a/src/components/search/SiteSelect.jsx +++ b/src/components/search/SiteSelect.jsx @@ -32,7 +32,7 @@ class SiteSelect extends Component { allowClear={_mode != null} {...extProps} > - {_show_all===true ? 所有来源 : ''} + {_show_all===true ? 所有来源 : ''} {sites.map(ele => {ele.label})} diff --git a/src/stores/SaleStore.js b/src/stores/SaleStore.js index 08eabb8..29a27b9 100644 --- a/src/stores/SaleStore.js +++ b/src/stores/SaleStore.js @@ -32,6 +32,20 @@ class SaleStore { type_data_sub = []; // 类型的子维度数据 date_title = 'date_title'; // 日期段,只用于显示,防止日期选择控件的变化导致页面刷新 + searchValues = { + DateType: { key: 'ConfirmDate', label: '确认日期'}, + WebCode: { key: 'All', label: '所有来源'}, + IncludeTickets: { key: '1', label: '含门票'}, + DepartmentList: [groupsMappedByCode.GH], + }; + + setSearchValues(obj, values) { + this.groups = obj.DepartmentList; + this.webcode = obj.WebCode; + this.include_tickets = obj.IncludeTickets; + this.date_type = obj.DateType; + } + salesTrade = { groupType: 'dept', loading: false, operator: [], dept: [], overview: [], diff --git a/src/views/Sale.jsx b/src/views/Sale.jsx index 7f64bc9..e343529 100644 --- a/src/views/Sale.jsx +++ b/src/views/Sale.jsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect } from 'react'; -import { Row, Col, Button, Tabs, Table, Divider, Radio, Select } from 'antd'; +import { Row, Col, Button, Tabs, Table, Divider, Radio, Select, Spin } from 'antd'; import { ContainerOutlined, SearchOutlined, UserSwitchOutlined } from '@ant-design/icons'; import { stores_Context } from '../config'; import { Column, Pie, Treemap } from '@ant-design/charts'; @@ -12,6 +12,7 @@ import * as config from '../config'; import SiteSelect from '../components/search/SiteSelect'; import GroupSelect from '../components/search/GroupSelect'; import { utils, writeFileXLSX } from 'xlsx'; +import SearchForm from './../components/search/SearchForm'; const Sale = () => { const { sale_store, date_picker_store } = useContext(stores_Context); @@ -207,48 +208,28 @@ const Sale = () => { return (
- -
- - - - - - - - - - - - - - - - - - - - - - - + + + { + sale_store.setSearchValues(obj, form); + sale_store.get_department_order_ml_by_type(date_picker_store); + }} + /> + + {/* @@ -284,12 +265,11 @@ const Sale = () => { */} - - - + + From e8e4efe7967433797506fbfa002e836828cde8f2 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 3 Nov 2023 14:34:18 +0800 Subject: [PATCH 14/86] =?UTF-8?q?perf:=20=E5=BA=94=E7=94=A8=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E7=BB=84=E4=BB=B6:=20=E5=AE=A2=E8=BF=90,=20=E8=AE=A2?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/charts/Customer_care_inchina.jsx | 69 +++++++++-------------- src/charts/Customer_care_potential.jsx | 70 ++++++++++-------------- src/charts/Customer_care_regular.jsx | 70 ++++++++++-------------- src/charts/Orders.jsx | 8 +-- src/components/search/SearchForm.jsx | 4 +- src/components/search/SiteSelect.jsx | 7 ++- src/libs/ht.js | 2 + src/stores/CustomerStore.js | 26 +++++++++ src/views/Orders.jsx | 8 +-- src/views/Orders_sub.jsx | 76 +++++++++----------------- src/views/Sale.jsx | 14 ++--- 11 files changed, 151 insertions(+), 203 deletions(-) diff --git a/src/charts/Customer_care_inchina.jsx b/src/charts/Customer_care_inchina.jsx index 13cd8cb..0a7ba91 100644 --- a/src/charts/Customer_care_inchina.jsx +++ b/src/charts/Customer_care_inchina.jsx @@ -1,19 +1,9 @@ import React, {useContext, useEffect} from 'react'; -import {Row, Col, Button, Divider, Table, Space, Radio, Tooltip} from 'antd'; -import { - ContainerOutlined, - SearchOutlined, -} from '@ant-design/icons'; +import {Row, Col, Divider, Table} from 'antd'; import {stores_Context} from '../config'; -import {Line} from "@ant-design/charts"; import {observer} from 'mobx-react'; -import DatePickerCharts from '../components/search/DatePickerCharts'; -import {NavLink, useParams} from "react-router-dom"; -import * as comm from "../utils/commons"; -import * as config from "../config"; -import SiteSelect from "../components/search/SiteSelect"; -import GroupSelect from "../components/search/GroupSelect"; import {utils, writeFileXLSX} from "xlsx"; +import SearchForm from './../components/search/SearchForm'; const Customer_care_inchina = () => { @@ -27,38 +17,33 @@ const Customer_care_inchina = () => { return (
- -
+ + + { + customer_store.setSearchValues(obj, form, 'inchina_data'); + customer_store.inchina_customer_order(); + customer_store.inchina_customer_order(true); + }} + /> + +

在华客人

- - - - - - - - - - - - 提交日期 - 出发日期 - 确认日期 - - - - - - - -
{ const wb = utils.table_to_book(document.getElementById("table_to_xlsx").getElementsByTagName('table')[0]); writeFileXLSX(wb, "在华客人.xlsx"); }}>导出excel -
{ @@ -21,43 +11,39 @@ const Customer_care_potential = () => { const potential_data = customer_store.potential_data; useEffect(() => { - }, []); return (
- -
+ + + { + customer_store.setSearchValues(obj, form, 'potential_data'); + customer_store.potential_customer_order(); + customer_store.potential_customer_order(true); + }} + /> + +

潜力客户

- - - - - - - - - - - 预定日期 - 出发日期 - 确认日期 - - - - - - - + {/* */} -
{ const wb = utils.table_to_book(document.getElementById("table_to_xlsx").getElementsByTagName('table')[0]); writeFileXLSX(wb, "潜力客户.xlsx"); }}>导出excel -
{ @@ -27,37 +17,33 @@ const Customer_care_regular = () => { return (
- -
+ + + { + customer_store.setSearchValues(obj, form, 'regular_data'); + customer_store.regular_customer_order(); + customer_store.regular_customer_order(true); + }} + /> + +

老客户

- - - - - - - - - - - 预定日期 - 出发日期 - 确认日期 - - - - - - - -
{ const wb = utils.table_to_book(document.getElementById("table_to_xlsx").getElementsByTagName('table')[0]); writeFileXLSX(wb, "老客户.xlsx"); }}>导出excel -
{ 'WebCode': { key: 'WebCode', transform: (value) => { - return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.key : ''; + return isEmpty(value) ? 'ALL': Array.isArray(value) ? value.map((ele) => ele.key).filter(ele => ele !== 'ALL').join(',') : value ? value.key : ''; }, default: '', }, diff --git a/src/components/search/SiteSelect.jsx b/src/components/search/SiteSelect.jsx index b5406f7..832b47e 100644 --- a/src/components/search/SiteSelect.jsx +++ b/src/components/search/SiteSelect.jsx @@ -13,13 +13,14 @@ class SiteSelect extends Component { const { store, mode, value, onChange, show_all, ...extProps } = this.props; const _mode = mode || store?.group_select_mode || null; const _show_all = ['tags', 'multiple'].includes(_mode) ? false : show_all; + const _value = !['tags', 'multiple'].includes(_mode) ? (value || store?.webcode || undefined) : (value || store?.webcode || []).filter(item => item.value !== 'ALL'); return (
); diff --git a/src/libs/ht.js b/src/libs/ht.js index 89eb3ca..0704ebe 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -57,6 +57,7 @@ export const groups = [ { value: '31', key: '31', label: '花梨鹰', code: '', children: [] }, ]; export const groupsMappedByCode = groups.reduce((a, c) => ({ ...a, [String(c.code || c.key)]: c }), {}); +export const groupsMappedByKey = groups.reduce((a, c) => ({ ...a, [String(c.key)]: c }), {}); export const leafGroup = groups.slice(3); export const overviewGroup = groups.slice(0, 3); // todo: 花梨鹰 APP Trippest /** @@ -87,6 +88,7 @@ export const sites = [ { value: '30', key: '30', label: 'TP', code: 'trippest' }, { value: '31', key: '31', label: '花梨鹰', code: 'HLY' }, ]; +export const sitesMappedByCode = sites.reduce((a, c) => ({ ...a, [String(c.code)]: {...c, key: c.code, value: c.code } }), {}); export const dateTypes = [ { key: 'applyDate', value: 'applyDate', label: '提交日期' }, { key: 'ConfirmDate', value: 'ConfirmDate', label: '确认日期' }, diff --git a/src/stores/CustomerStore.js b/src/stores/CustomerStore.js index bfc715a..727b790 100644 --- a/src/stores/CustomerStore.js +++ b/src/stores/CustomerStore.js @@ -1,5 +1,6 @@ import {makeAutoObservable, runInAction} from "mobx"; import * as config from "../config"; +import { groupsMappedByKey, dataFieldAlias, sitesMappedByCode } from './../libs/ht'; class CustomerStore { @@ -79,6 +80,12 @@ class CustomerStore { potential_customer_order: this.potential_customer_order.bind(this), onChange_show_detail_table: this.onChange_show_detail_table.bind(this), handleChange_webcode: this.handleChange_webcode.bind(this), + + searchValues: { + DepartmentList: ['1', '2', '7'].map(kk => groupsMappedByKey[kk]), + WebCode: ['GHKYZG'].map(kk => sitesMappedByCode[kk]), + DateType: { key: 'applyDate', label: '提交日期'}, + }, }; // 潜力客户 end @@ -152,6 +159,12 @@ class CustomerStore { regular_customer_order: this.regular_customer_order.bind(this), onChange_show_detail_table: this.onChange_show_detail_table_regular.bind(this), handleChange_webcode: this.handleChange_webcode_regular.bind(this), + + searchValues: { + DepartmentList: ['1', '2', '28', '7'].map(kk => groupsMappedByKey[kk]), + WebCode: undefined, // ['ALL'].map(kk => sitesMappedByCode[kk]), + DateType: { key: 'applyDate', label: '提交日期'}, + }, }; // 老客户 end @@ -225,9 +238,22 @@ class CustomerStore { inchina_customer_order: this.inchina_customer_order.bind(this), onChange_show_detail_table: this.onChange_show_detail_table_inchina.bind(this), handleChange_webcode: this.handleChange_webcode_inchina.bind(this), + + searchValues: { + DepartmentList: ['1', '2', '28', '7'].map(kk => groupsMappedByKey[kk]), + WebCode: undefined, // ['ALL'].map(kk => sitesMappedByCode[kk]), + DateType: { key: 'applyDate', label: '提交日期'}, + }, }; // 在华客人 end + setSearchValues(obj, values, target) { + this[target].groups = obj.DepartmentList; + this[target].webcode = obj.WebCode; + this[target].include_tickets = obj.IncludeTickets; + this[target].date_type = obj.DateType; + } + } diff --git a/src/views/Orders.jsx b/src/views/Orders.jsx index aee44ed..817a2c8 100644 --- a/src/views/Orders.jsx +++ b/src/views/Orders.jsx @@ -1,13 +1,9 @@ import React, { Component } from "react"; -import { Row, Col, Button, Tabs, Table, Divider, Select, Radio, Spin } from "antd"; -import { ContainerOutlined, CarryOutOutlined, BlockOutlined, SmileOutlined, TagsOutlined, GlobalOutlined, SearchOutlined, FullscreenOutlined, DingtalkOutlined } from "@ant-design/icons"; +import { Row, Col, Tabs, Table, Divider, Spin } from "antd"; +import { ContainerOutlined, BlockOutlined, SmileOutlined, TagsOutlined, GlobalOutlined, FullscreenOutlined, DingtalkOutlined } from "@ant-design/icons"; import { stores_Context } from "../config"; import { Line, Pie } from "@ant-design/charts"; -import SiteSelect from "../components/search/SiteSelect"; -import GroupSelect from "../components/search/GroupSelect"; -import DataTypeSelect from "../components/search/DataTypeSelect"; import { observer } from "mobx-react"; -import DatePickerCharts from "../components/search/DatePickerCharts"; import * as config from "../config"; import { NavLink } from "react-router-dom"; import * as comm from "../utils/commons"; diff --git a/src/views/Orders_sub.jsx b/src/views/Orders_sub.jsx index e8e9bd8..453ec5d 100644 --- a/src/views/Orders_sub.jsx +++ b/src/views/Orders_sub.jsx @@ -1,18 +1,15 @@ import React, { useContext, useEffect } from "react"; -import { Row, Col, Button, Tabs, Table, Select, Divider } from "antd"; -import { ContainerOutlined, SearchOutlined } from "@ant-design/icons"; +import { Row, Col, Tabs, Table, Divider } from "antd"; +import { ContainerOutlined } from "@ant-design/icons"; import { stores_Context } from "../config"; import { Line } from "@ant-design/charts"; import { observer } from "mobx-react"; -import DatePickerCharts from "../components/search/DatePickerCharts"; -import SiteSelect from "../components/search/SiteSelect"; -import GroupSelect from "../components/search/GroupSelect"; -import DataTypeSelect from "../components/search/DataTypeSelect"; import { NavLink, useParams } from "react-router-dom"; import * as comm from "../utils/commons"; import * as config from "../config"; import { utils, writeFileXLSX } from "xlsx"; import DateGroupRadio from '../components/DateGroupRadio'; +import SearchForm from './../components/search/SearchForm'; const Orders_sub = () => { const { ordertype, ordertype_sub, ordertype_title } = useParams(); @@ -184,49 +181,27 @@ const Orders_sub = () => { 返回 - - - - - - - - - - - - - - - - - - - - - - - - - - - + + { + orders_store.setSearchValues(obj, form); + orders_store.getOrderCount_type(ordertype, ordertype_sub); + orders_store.getOrderCountByType_sub(ordertype, ordertype_sub, orders_store.active_tab_key_sub); + }} + /> + { fieldMapper={orders_store.orderCount_type_dateRadio.orderCountDataFieldMapper} /> + + + diff --git a/src/views/Sale.jsx b/src/views/Sale.jsx index e343529..16de43c 100644 --- a/src/views/Sale.jsx +++ b/src/views/Sale.jsx @@ -1,16 +1,10 @@ -import React, { useContext, useEffect } from 'react'; -import { Row, Col, Button, Tabs, Table, Divider, Radio, Select, Spin } from 'antd'; -import { ContainerOutlined, SearchOutlined, UserSwitchOutlined } from '@ant-design/icons'; +import React, { useContext } from 'react'; +import { Row, Col, Tabs, Table, Divider, Spin } from 'antd'; +import { ContainerOutlined, UserSwitchOutlined } from '@ant-design/icons'; import { stores_Context } from '../config'; -import { Column, Pie, Treemap } from '@ant-design/charts'; +import { Column, Pie } from '@ant-design/charts'; import { observer } from 'mobx-react'; -import DatePickerCharts from '../components/search/DatePickerCharts'; -import DataTypeSelect from '../components/search/DataTypeSelect'; -import { NavLink, useParams } from 'react-router-dom'; import * as comm from '../utils/commons'; -import * as config from '../config'; -import SiteSelect from '../components/search/SiteSelect'; -import GroupSelect from '../components/search/GroupSelect'; import { utils, writeFileXLSX } from 'xlsx'; import SearchForm from './../components/search/SearchForm'; From b669943f07cb57bbf3fd985667432ea1f78871c8 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 3 Nov 2023 14:39:30 +0800 Subject: [PATCH 15/86] 2.2.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a44e472..df3c21d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.1.1", + "version": "2.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.1.1", + "version": "2.2.0", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index 2eb0eea..b7d4f8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.1.1", + "version": "2.2.0", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From 633935b5c8db78d52df7037fb4b7530e63ad317d Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 3 Nov 2023 15:01:01 +0800 Subject: [PATCH 16/86] =?UTF-8?q?fix:=20=E9=83=A8=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=B0=B4=E5=8D=B0=E6=97=A0=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index f3ac2e6..cf57d31 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -148,8 +148,8 @@ const App = () => { return ( - - + + Date: Fri, 3 Nov 2023 16:56:59 +0800 Subject: [PATCH 17/86] =?UTF-8?q?conf:=20=E7=BC=96=E8=AF=91=E6=97=B6?= =?UTF-8?q?=E9=97=B4=EF=BC=8C=E5=8A=A0=E7=82=B9=E6=B5=AE=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.js b/build.js index 0ccf591..b75686b 100644 --- a/build.js +++ b/build.js @@ -1,5 +1,5 @@ const { execSync } = require('child_process'); -process.env.REACT_APP_BUILD_TIME = new Date().getTime(); +process.env.REACT_APP_BUILD_TIME = new Date().getTime()+(5*60*1000); execSync('npm run build', { stdio: 'inherit' }); From 43750214be8c11f0d1e02e91bc9d64a2e3928ce4 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 6 Nov 2023 09:45:25 +0800 Subject: [PATCH 18/86] =?UTF-8?q?perf:=20=E5=B8=82=E5=9C=BA>=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E6=95=B0=E6=8D=AE:=20=E5=B0=8F=E7=BB=84=E5=A4=9A?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/SearchForm.jsx | 10 +++++++--- src/components/search/SiteSelect.jsx | 7 +++++-- src/views/Orders.jsx | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index 786770b..47f7439 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -69,7 +69,7 @@ export default observer((props) => { 'DepartmentList': { key: 'DepartmentList', transform: (value) => { - return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.key : ''; + return isEmpty(value) ? 'ALL': Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.key : ''; }, default: '', }, @@ -259,7 +259,11 @@ function getFields(props) { item( 'DepartmentList', 99, - + ), @@ -323,7 +327,7 @@ function getFields(props) { item( 'operator', 99, - + item.value !== 'ALL'); + const __value = ['tags', 'multiple'].includes(_mode) ? (value.constructor === Object ? [value] : value) : undefined; + const _value = !['tags', 'multiple'].includes(_mode) + ? value || store?.webcode || undefined + : (__value || store?.webcode || []).filter((item) => String(item?.value || item.key).toLowerCase() !== 'all'); return (
record.key} + expandable={{ + expandedRowRender: record => ( +
+                  
+                    客户需求
+                  
+                  {record.COLI_CustomerRequest}
+                  
+                    订单内容
+                  
+                  {record.COLI_OrderDetailText}
+                
+ ), + }} + /> + + { + const wb = utils.table_to_book(document.getElementById("table_to_xlsx_form").getElementsByTagName("table")[0]); + writeFileXLSX(wb, "订单列表.xlsx"); + }}> + 导出excel + + + + + + {date_picker_store.start_date_cp ? date_picker_store.start_date_cp.format(config.DATE_FORMAT) + "~" + date_picker_store.end_date_cp.format(config.DATE_FORMAT) : ""} +
record.key} + expandable={{ + expandedRowRender: record =>
{record.COLI_OrderDetailText}
, + }} + /> + + ), + }, + { key: 'page', label: 访问路径, title: '访问路径',children: ( + + {date_picker_store.start_date.format(config.DATE_FORMAT)}~{date_picker_store.end_date.format(config.DATE_FORMAT)} +
record.key} columns={table_data_p.columns} size="small" /> + + + + {date_picker_store.start_date_cp ? date_picker_store.start_date_cp.format(config.DATE_FORMAT) + "~" + date_picker_store.end_date_cp.format(config.DATE_FORMAT) : ""} +
record.key} columns={table_data2_p.columns} size="small" /> + + )}, + { key: 'page_cxstate1', label: 访问路径(成行), title: '访问路径(成行)',children: ( + + {date_picker_store.start_date.format(config.DATE_FORMAT)}~{date_picker_store.end_date.format(config.DATE_FORMAT)} +
record.key} columns={table_data_p.columns} size="small" /> - - - - - - - - + + {date_picker_store.start_date_cp ? date_picker_store.start_date_cp.format(config.DATE_FORMAT) + "~" + date_picker_store.end_date_cp.format(config.DATE_FORMAT) : ""} +
record.key} columns={table_data2_p.columns} size="small" /> + + )}, + ]; - - orders_store.onChange_Tabs_sub(ordertype, ordertype_sub, active_key)}> - - - 订单内容 - - } - key="detail"> - - - {date_picker_store.start_date.format(config.DATE_FORMAT)}~{date_picker_store.end_date.format(config.DATE_FORMAT)} -
record.key} - expandable={{ - expandedRowRender: record => ( -
-													
-														客户需求
-													
-													{record.COLI_CustomerRequest}
-													
-														订单内容
-													
-													{record.COLI_OrderDetailText}
-												
- ), - }} - /> - - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx_form").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "订单列表.xlsx"); - }}> - 导出excel - - - + const [propsForExport, setPropsForExport] = useState(tab_items[0]); + const tabItemsMapped = tab_items.reduce((r, v) => ({...r, [v.key]: v}), {}); + const onTabsChange = (active_key) => { + setPropsForExport(tabItemsMapped[active_key]); + orders_store.onChange_Tabs_sub(ordertype, ordertype_sub, active_key); + }; - - {date_picker_store.start_date_cp ? date_picker_store.start_date_cp.format(config.DATE_FORMAT) + "~" + date_picker_store.end_date_cp.format(config.DATE_FORMAT) : ""} -
record.key} - expandable={{ - expandedRowRender: record =>
{record.COLI_OrderDetailText}
, - }} - /> - - - - - - 访问路径 - - } - key="page"> - - - {date_picker_store.start_date.format(config.DATE_FORMAT)}~{date_picker_store.end_date.format(config.DATE_FORMAT)} -
record.key} columns={table_data_p.columns} size="small" /> - + return ( +
+ +
+ 返回 + + + { + orders_store.setSearchValues(obj, form); + orders_store.getOrderCount_type(ordertype, ordertype_sub); + orders_store.getOrderCountByType_sub(ordertype, ordertype_sub, orders_store.active_tab_key_sub); + }} + /> + + + + + - - {date_picker_store.start_date_cp ? date_picker_store.start_date_cp.format(config.DATE_FORMAT) + "~" + date_picker_store.end_date_cp.format(config.DATE_FORMAT) : ""} -
record.key} columns={table_data2_p.columns} size="small" /> - - - - - - 访问路径(成行) - - } - key="page_cxstate1"> - - - {date_picker_store.start_date.format(config.DATE_FORMAT)}~{date_picker_store.end_date.format(config.DATE_FORMAT)} -
record.key} columns={table_data_p.columns} size="small" /> - + + + + - - {date_picker_store.start_date_cp ? date_picker_store.start_date_cp.format(config.DATE_FORMAT) + "~" + date_picker_store.end_date_cp.format(config.DATE_FORMAT) : ""} -
record.key} columns={table_data2_p.columns} size="small" /> - - - - - - - - ); + + + ), + }} + items={tab_items} + /> + + + + ); }; export default observer(Orders_sub); From ede377dccaa740def538fc3c9cf20742a7e416b9 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 6 Nov 2023 14:50:00 +0800 Subject: [PATCH 22/86] =?UTF-8?q?perf:=20=E8=AE=A2=E5=8D=95>=E5=AD=90?= =?UTF-8?q?=E7=BB=B4=E5=BA=A6:=20=E6=90=9C=E7=B4=A2=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/OrdersStore.js | 1 + src/views/Orders_sub.jsx | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/stores/OrdersStore.js b/src/stores/OrdersStore.js index ee8cb5c..a1fa0c5 100644 --- a/src/stores/OrdersStore.js +++ b/src/stores/OrdersStore.js @@ -85,6 +85,7 @@ class OrdersStore { this.webcode = obj.WebCode; this.include_tickets = obj.IncludeTickets; this.date_type = obj.DateType; + this.searchValues = values; } // 切换标签页 diff --git a/src/views/Orders_sub.jsx b/src/views/Orders_sub.jsx index b4b5489..f911fdd 100644 --- a/src/views/Orders_sub.jsx +++ b/src/views/Orders_sub.jsx @@ -260,7 +260,6 @@ const Orders_sub = () => { setPropsForExport(tabItemsMapped[active_key]); orders_store.onChange_Tabs_sub(ordertype, ordertype_sub, active_key); }; - return (
@@ -272,12 +271,11 @@ const Orders_sub = () => { defaultValue={{ initialValue: { ...date_picker_store.formValues, - ...orders_store.searchValues, }, shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates'], fieldProps: { - DepartmentList: { show_all: false }, - WebCode: { show_all: true }, + DepartmentList: { show_all: false, mode: 'multiple' }, + WebCode: { show_all: false, mode: 'multiple' }, // dates: { hide_vs: true }, }, }} From 2a85a989df0946f1946c486f235717d6c8c400b2 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 6 Nov 2023 16:43:25 +0800 Subject: [PATCH 23/86] =?UTF-8?q?fix:=20=E6=9D=A5=E6=BA=90=E5=A4=9A?= =?UTF-8?q?=E9=80=89=E7=9A=84=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/SiteSelect.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/search/SiteSelect.jsx b/src/components/search/SiteSelect.jsx index d4595ad..fd52f75 100644 --- a/src/components/search/SiteSelect.jsx +++ b/src/components/search/SiteSelect.jsx @@ -13,7 +13,7 @@ class SiteSelect extends Component { const { store, mode, value, onChange, show_all, ...extProps } = this.props; const _mode = mode || store?.group_select_mode || null; const _show_all = ['tags', 'multiple'].includes(_mode) ? false : show_all; - const __value = ['tags', 'multiple'].includes(_mode) ? (value.constructor === Object ? [value] : value) : undefined; + const __value = ['tags', 'multiple'].includes(_mode) ? (value?.constructor === Object ? [value] : value) : undefined; const _value = !['tags', 'multiple'].includes(_mode) ? value || store?.webcode || undefined : (__value || store?.webcode || []).filter((item) => String(item?.value || item.key).toLowerCase() !== 'all'); From e9bccf450dc1024e98a902855d46c27a02048c6c Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 6 Nov 2023 16:43:54 +0800 Subject: [PATCH 24/86] =?UTF-8?q?perf:=20=E8=AE=A2=E5=8D=95>=E4=BB=AA?= =?UTF-8?q?=E8=A1=A8=E7=9B=98:=20=E7=A7=BB=E5=8A=A8=E7=AB=AF=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Dashboard.jsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/views/Dashboard.jsx b/src/views/Dashboard.jsx index c3608fa..9d7f073 100644 --- a/src/views/Dashboard.jsx +++ b/src/views/Dashboard.jsx @@ -22,11 +22,24 @@ class Dashboard extends Component {
- + + - + From a9c21a54e604b5f494b388b65c1e44598029c6c4 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 6 Nov 2023 17:34:01 +0800 Subject: [PATCH 25/86] =?UTF-8?q?perf:=20=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/charts/Customer_care_inchina.jsx | 2 +- src/charts/Customer_care_potential.jsx | 2 +- src/charts/Customer_care_regular.jsx | 2 +- src/stores/CustomerServices.js | 7 +++++-- src/views/AgentGroupCount.jsx | 4 ++-- src/views/AgentGroupList.jsx | 4 ++-- src/views/DestinationGroupCount.jsx | 4 ++-- src/views/DestinationGroupList.jsx | 4 ++-- src/views/Orders.jsx | 10 +++++++--- src/views/Sale.jsx | 9 ++++++--- 10 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/charts/Customer_care_inchina.jsx b/src/charts/Customer_care_inchina.jsx index 0a7ba91..2240454 100644 --- a/src/charts/Customer_care_inchina.jsx +++ b/src/charts/Customer_care_inchina.jsx @@ -86,7 +86,7 @@ const Customer_care_inchina = () => { const wb = utils.table_to_book(document.getElementById("table_to_xlsx").getElementsByTagName('table')[0]); writeFileXLSX(wb, "在华客人.xlsx"); }}>导出excel -
{ const wb = utils.table_to_book(document.getElementById("table_to_xlsx").getElementsByTagName('table')[0]); writeFileXLSX(wb, "潜力客户.xlsx"); }}>导出excel -
{ const wb = utils.table_to_book(document.getElementById("table_to_xlsx").getElementsByTagName('table')[0]); writeFileXLSX(wb, "老客户.xlsx"); }}>导出excel -
{record.COLD_ServiceCityName} } ] @@ -492,7 +494,7 @@ class CustomerServices { inProgress; agentList = []; - + groupList = []; groupListColumns = []; @@ -518,6 +520,7 @@ class CustomerServices { { title: '地接社名称', dataIndex: 'VendorName', + fix: 'left', render: (text, record) => { if (record.EOI_ObjSN === -1) { return text; @@ -569,4 +572,4 @@ class CustomerServices { ]; } -export default CustomerServices; \ No newline at end of file +export default CustomerServices; diff --git a/src/views/AgentGroupCount.jsx b/src/views/AgentGroupCount.jsx index e3ceefd..5a75d0f 100644 --- a/src/views/AgentGroupCount.jsx +++ b/src/views/AgentGroupCount.jsx @@ -136,7 +136,7 @@ const AgentGroupCount = () => { 地接社团信息 -
{ rowKey={(record) => record.key} loading={inProgress} pagination={false} - scroll={{ x: '100%' }} + scroll={{ x: 1000 }} /> { {customerServicesStore.agentCompany} -
record.key} loading={inProgress} pagination={false} - scroll={{ x: "100%" }} + scroll={{ x: 1000 }} expandable={{ expandedRowRender: (record) => ( diff --git a/src/views/DestinationGroupCount.jsx b/src/views/DestinationGroupCount.jsx index decc96b..3890d24 100644 --- a/src/views/DestinationGroupCount.jsx +++ b/src/views/DestinationGroupCount.jsx @@ -103,7 +103,7 @@ const DestinationGroupCount = () => { 目的地团信息 -
{ rowKey={(record) => record.key} loading={inProgress} pagination={false} - scroll={{ x: "100%" }} + scroll={{ x: 1000 }} /> { -
record.key} loading={inProgress} pagination={false} - scroll={{ x: "100%" }} + scroll={{ x: 600 }} expandable={{ expandedRowRender: (record) => ( diff --git a/src/views/Orders.jsx b/src/views/Orders.jsx index 2b87f7e..d6c20e4 100644 --- a/src/views/Orders.jsx +++ b/src/views/Orders.jsx @@ -29,6 +29,7 @@ class Orders extends Component { result.columns = [ { title: "", + fixed: 'left', children: [ { title: ( @@ -42,6 +43,7 @@ class Orders extends Component { ), dataIndex: "OrderType", + fixed: 'left', render: (text, record) => {text}, }, ], @@ -164,6 +166,7 @@ class Orders extends Component { result.columns = [ { title: "", + fixed: 'left', children: [ { title: ( @@ -173,7 +176,8 @@ class Orders extends Component { ), - dataIndex: "OrderType", + fixed: 'left', + dataIndex: "OrderType", render: (text, record) => {text}, }, ], @@ -313,11 +317,11 @@ class Orders extends Component { columns: table_data.columns, size: 'small', pagination: false, - scroll: { x: '100%' }, + scroll: { x: (100*(table_data.columns.length)) }, loading: orders_store.loading, }; - return ( + return (
diff --git a/src/views/Sale.jsx b/src/views/Sale.jsx index 1780d3c..29f9fbc 100644 --- a/src/views/Sale.jsx +++ b/src/views/Sale.jsx @@ -199,6 +199,9 @@ const Sale = () => { ), }, ]; + const tableColumns = type_data.columns.map((ele, i) => + i === 0 ? { ...ele, fix: 'left', children: (ele?.children || []).map((ele_child, i_child) => (i_child === 0 ? { ...ele_child, fix: 'left' } : { ...ele_child })) } : { ...ele } + ); return (
@@ -277,15 +280,15 @@ const Sale = () => { >
-
record.key} loading={sale_store.loading_table} pagination={false} - scroll={{ x: '100%' }} + scroll={{ x: (100*(tableColumns.length)) }} /> Date: Tue, 7 Nov 2023 10:06:12 +0800 Subject: [PATCH 26/86] =?UTF-8?q?perf:=20=E4=BF=A1=E7=94=A8=E5=8D=A1?= =?UTF-8?q?=E8=B4=A6=E5=8D=95:=20=E6=90=9C=E7=B4=A2=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/Input.jsx | 12 +++--- src/components/search/SearchForm.jsx | 21 ++++++++++- src/stores/FinancialStore.js | 10 ++++- src/views/Credit_card_bill.jsx | 55 +++++++++++++--------------- 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/components/search/Input.jsx b/src/components/search/Input.jsx index cbdbaac..d821b7c 100644 --- a/src/components/search/Input.jsx +++ b/src/components/search/Input.jsx @@ -1,7 +1,5 @@ import React from 'react'; import { Select } from 'antd'; -import querystring from 'querystring'; -// import * as oMapper from 'object-mapper'; import { fetchJSON } from './../../utils/request'; import { observer } from 'mobx-react'; import { isEmpty, merge, objectMapper } from './../../utils/commons'; @@ -19,7 +17,6 @@ function curl(opts, callback) { currentValue = opts.value; function fake() { - // console.log(currentValue, opts.value); if (currentValue === opts.value && opts.value === '空') { const _p = [{ 'key': '0', 'label': '空' }]; return callback(_p); @@ -29,11 +26,12 @@ function curl(opts, callback) { // code: 'utf-8', // q: opts.value, // }).toString(); + const resultkey = opts.resultkey || 'result'; fetchJSON(`${opts.url}`, param) .then(d => { if (currentValue === opts.value) { - const result = objectMapper(d.result, opts.map) || []; + const result = objectMapper(d[resultkey], opts.map) || []; callback(result); } }); @@ -65,7 +63,7 @@ class SearchInput extends React.Component { if (this.props.autoGet === true) { const { map, resultkey, dependenciesFun } = this.props; const param = typeof dependenciesFun === 'function' ? dependenciesFun() : {}; - const mapKey = Object.keys(map).reduce((r, v) => ({ ...r, [v]: { key: map[v] } }), {}); + const mapKey = Object.keys(map).reduce((r, v) => ({ ...r, [v]: typeof map[v] === 'string' ? { key: map[v] } : (map[v] || []).map(vi => ({ key: vi})) }), {}); curl({ value: '', url: this.props.url || '', map: mapKey, resultkey, param }, (data) => this.setState({ data, autoData: data }, () => (typeof this.props.onSearchAfter === 'function' ? this.props.onSearchAfter(data, this.state.value) : '')) ); @@ -90,7 +88,9 @@ class SearchInput extends React.Component { } const { map, resultkey, dependenciesFun } = this.props; const param = typeof dependenciesFun === 'function' ? dependenciesFun() : {}; - const mapKey = Object.keys(map).reduce((r, v) => ({ ...r, [v]: { key: map[v] } }), {}); + // const mapKey = Object.keys(map).reduce((r, v) => ({ ...r, [v]: { key: map[v] } }), {}); + const mapKey = Object.keys(map).reduce((r, v) => ({ ...r, [v]: typeof map[v] === 'string' ? { key: map[v] } : (map[v] || []).map(vi => ({ key: vi})) }), {}); + if (value || !isEmpty(param)) { curl({ value, url: this.props.url || '', map: mapKey, resultkey, param }, (data) => this.setState({ data }, () => (typeof this.props.onSearchAfter === 'function' ? this.props.onSearchAfter(data, this.state.value) : '')) diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index 47f7439..4f43365 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -62,7 +62,7 @@ export default observer((props) => { 'businessUnits': { key: 'businessUnits', transform: (value) => { - return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? (!isNaN(parseInt(value.key), 10) ? value.key : '') : ''; + return isEmpty(value) ? 'ALL': Array.isArray(value) ? value.map((ele) => ele.value).join(',') : value ? value.value : ''; }, default: '', }, @@ -148,6 +148,11 @@ export default observer((props) => { transform: (value) => value?.key || '', default: '', }, + 'billtype': { + key: 'billtype', + transform: (value) => isEmpty(value) ? 'ALL' : value?.key || '', + default: '', + }, }; let dest = {}; const { applyDate, applyDate2, year, yearDiff, dates, months, ...omittedValue } = values; @@ -242,6 +247,20 @@ function getFields(props) { }; let baseChildren = []; baseChildren = [ + item( + 'billtype', + 99, + + + + ), item( 'HTBusinessUnits', 99, diff --git a/src/stores/FinancialStore.js b/src/stores/FinancialStore.js index 2557007..d0c3264 100644 --- a/src/stores/FinancialStore.js +++ b/src/stores/FinancialStore.js @@ -85,11 +85,17 @@ class FinancialStore { this.credit_card_data.groups = value; }; + setSearchValues(obj, values) { + this.credit_card_data.business_units = obj.businessUnits; + // this.credit_card_data.groups = obj.businessUnits; + this.bill_type_data.bill_types = obj.billtype; + } + // 请求信用卡账单 get_credit_card_bills() { const date_picker_store = this.rootStore.date_picker_store; let url = '/service-web/QueryData/GetCreditCardBills'; - url += `?business_unit=${this.credit_card_data.business_units.toString()}&groups=${this.credit_card_data.groups.toString()}&billtype=${this.bill_type_data.bill_types.toString()}`; + url += `?business_unit=${this.credit_card_data.business_units}&groups=${this.credit_card_data.groups.toString()}&billtype=${this.bill_type_data.bill_types}`; url += '&billdate1=' + date_picker_store.start_date.format(config.DATE_FORMAT) + '&billdate2=' + date_picker_store.end_date.format(config.DATE_FORMAT) + '%2023:59:59'; if (date_picker_store.start_date_cp && date_picker_store.end_date_cp) { url += '&billdateOld1=' + date_picker_store.start_date_cp.format(config.DATE_FORMAT) + '&billdateOld2=' + date_picker_store.end_date_cp.format(config.DATE_FORMAT) + '%2023:59:59'; @@ -110,7 +116,7 @@ class FinancialStore { get_credit_card_bills_by_type() { const date_picker_store = this.rootStore.date_picker_store; let url = '/service-web/QueryData/GetCreditCardBillsByType'; - url += `?business_unit=${this.credit_card_data.business_units.toString()}&groups=${this.credit_card_data.groups.toString()}`; + url += `?business_unit=${this.credit_card_data.business_units}&groups=${this.credit_card_data.groups.toString()}`; url += '&billdate1=' + date_picker_store.start_date.format(config.DATE_FORMAT) + '&billdate2=' + date_picker_store.end_date.format(config.DATE_FORMAT) + '%2023:59:59'; if (date_picker_store.start_date_cp && date_picker_store.end_date_cp) { url += '&billdateOld1=' + date_picker_store.start_date_cp.format(config.DATE_FORMAT) + '&billdateOld2=' + date_picker_store.end_date_cp.format(config.DATE_FORMAT) + '%2023:59:59'; diff --git a/src/views/Credit_card_bill.jsx b/src/views/Credit_card_bill.jsx index 8b6e6ec..ac5e28b 100644 --- a/src/views/Credit_card_bill.jsx +++ b/src/views/Credit_card_bill.jsx @@ -9,6 +9,7 @@ import BillTypeSelect from "../components/search/BillTypeSelect"; import GroupSelect from "../components/search/GroupSelect"; import Business_unit from "../components/search/BusinessSelect"; import DatePickerCharts from "../components/search/DatePickerCharts"; +import SearchForm from './../components/search/SearchForm'; import { Line, Pie } from "@ant-design/charts"; import * as comm from "../utils/commons"; import * as config from "../config"; @@ -266,36 +267,32 @@ const Credit_card_bill = () => { return (
- + +
+ { + financial_store.setSearchValues(obj, form); + financial_store.get_credit_card_bills(); + financial_store.get_credit_card_bills_by_type(); + financial_store.set_bill_filtered(false); + }} + /> +

信用卡账单

- - - - - - - - - - - - - - - @@ -315,7 +312,7 @@ const Credit_card_bill = () => { } key="summarized_data"> -
+
{ @@ -334,7 +331,7 @@ const Credit_card_bill = () => { } key="detail_data"> -
+
{ From f5dc0444695732695a661eb5d31f99f10c7d9f7b Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 7 Nov 2023 11:14:44 +0800 Subject: [PATCH 27/86] =?UTF-8?q?perf:=20=E5=AE=A2=E6=9C=8D>*:=20=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/SearchForm.jsx | 64 ++++++++++++ src/stores/CustomerServices.js | 22 ++++- src/views/AgentGroupCount.jsx | 143 +++++---------------------- src/views/AgentGroupList.jsx | 126 ++++++++--------------- src/views/DestinationGroupCount.jsx | 123 ++++++----------------- src/views/DestinationGroupList.jsx | 129 +++++++++--------------- src/views/Sale.jsx | 2 +- 7 files changed, 229 insertions(+), 380 deletions(-) diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index 4f43365..464ea44 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -153,6 +153,21 @@ export default observer((props) => { transform: (value) => isEmpty(value) ? 'ALL' : value?.key || '', default: '', }, + 'agency': { + key: 'agency', + transform: (value) => value?.key || '', + default: '', + }, + 'countryArea': { + key: 'countryArea', + transform: (value) => value?.value || value?.key || '', + default: '', + }, + 'orderStatus': { + key: 'orderStatus', + transform: (value) => value?.value || value?.key || '', + default: '', + }, }; let dest = {}; const { applyDate, applyDate2, year, yearDiff, dates, months, ...omittedValue } = values; @@ -247,6 +262,19 @@ function getFields(props) { }; let baseChildren = []; baseChildren = [ + item( + 'agency', + 99, + + + + ), item( 'billtype', 99, @@ -309,6 +337,42 @@ function getFields(props) { , 3 ), + item( + 'countryArea', + 99, + + + , + 3 + ), + item( + 'orderStatus', + 99, + + + , + 3 + ), // item( 'DateType', diff --git a/src/stores/CustomerServices.js b/src/stores/CustomerServices.js index 59e6e57..141a7f4 100644 --- a/src/stores/CustomerServices.js +++ b/src/stores/CustomerServices.js @@ -59,9 +59,11 @@ class CustomerServices { { title: '地接社名称', dataIndex: 'VendorName', + fixed: 'left', children: [{ // title: this.startDate.format(config.DATE_FORMAT) + '~' + this.endDate.format(config.DATE_FORMAT), dataIndex: 'VendorName', + fixed: 'left', render: (text, record) => {record.VendorName} } ] @@ -297,10 +299,10 @@ class CustomerServices { { title: '城市', dataIndex: 'COLD_ServiceCityName', - fix: 'left', + fixed: 'left', children: [{ dataIndex: 'COLD_ServiceCityName', - fix: 'left', + fixed: 'left', render: (text, record) => {record.COLD_ServiceCityName} } ] @@ -455,6 +457,20 @@ class CustomerServices { }); } + searchValues = { + DateType: { key: 'startDate', label: '走团日期'}, + }; + + setSearchValues(obj, values) { + this.dateType = obj.DateType; + this.selectedAgent = obj.agency; + this.startDateString = obj.Date1; + this.endDateString = obj.Date2; + this.selectedCountry = obj.countryArea; + this.selectedTeam = obj.DepartmentList.replace('ALL', ''); + this.selectedOrderStatus = obj.orderStatus; + } + selectDateRange(startDate, endDate) { this.startDate = startDate; this.endDate = endDate; @@ -520,7 +536,7 @@ class CustomerServices { { title: '地接社名称', dataIndex: 'VendorName', - fix: 'left', + fixed: 'left', render: (text, record) => { if (record.EOI_ObjSN === -1) { return text; diff --git a/src/views/AgentGroupCount.jsx b/src/views/AgentGroupCount.jsx index 5a75d0f..f685e2e 100644 --- a/src/views/AgentGroupCount.jsx +++ b/src/views/AgentGroupCount.jsx @@ -1,142 +1,51 @@ -import React, { useContext, useEffect } from 'react'; -import { Row, Col, Typography, Space, DatePicker, Button, Select, Table, Divider } from 'antd'; -import { SearchOutlined } from '@ant-design/icons'; +import { useContext, useEffect } from 'react'; +import { Row, Col, Typography, Space, Table, Divider } from 'antd'; import { stores_Context } from '../config'; -import * as config from '../config'; import { observer } from 'mobx-react'; import 'moment/locale/zh-cn'; -import moment from 'moment'; -import zhCNlocale from 'antd/es/date-picker/locale/zh_CN'; import { utils, writeFileXLSX } from 'xlsx'; -import GroupSelect from './../components/search/GroupSelect'; +import SearchForm from './../components/search/SearchForm'; const AgentGroupCount = () => { - const { customerServicesStore } = useContext(stores_Context); - const agentList = customerServicesStore.agentList; + const { customerServicesStore, date_picker_store } = useContext(stores_Context); const agentGroupList = customerServicesStore.agentGroupList; const agentGroupListColumns = customerServicesStore.agentGroupListColumns; - const { startDate, endDate, dateType, inProgress } = customerServicesStore; + const { inProgress } = customerServicesStore; useEffect(() => { customerServicesStore.fetchAllAgent(); }, []); - const handleSearchClick = () => { - customerServicesStore.fetchAgentGroupCount(); - }; - - const renderAgentItem = (agent) => { - return ( - - {agent.VEI2_CompanyBN} - - ); - }; - return ( <> - - - - - - customerServicesStore.selectTeam(value)} - style={{ width: '95%' }} show_all={true} - /> - - - - - - - - - { - customerServicesStore.selectDateRange(dates[0], dates[1]); + + + { + customerServicesStore.setSearchValues(obj, form); + customerServicesStore.fetchAgentGroupCount(); }} /> - - - 地接社团信息 -
{ { - const wb = utils.table_to_book( - document.getElementById('agentGroupList').getElementsByTagName('table')[0] - ); + const wb = utils.table_to_book(document.getElementById('agentGroupList').getElementsByTagName('table')[0]); writeFileXLSX(wb, '地接社团信息.xlsx'); }} > diff --git a/src/views/AgentGroupList.jsx b/src/views/AgentGroupList.jsx index db1ad79..561dbc9 100644 --- a/src/views/AgentGroupList.jsx +++ b/src/views/AgentGroupList.jsx @@ -1,87 +1,56 @@ -import React, {useContext, useEffect} from 'react'; -import {Row, Col, Typography, Space, DatePicker, Button, Select, Table, List} from 'antd'; -import { - SearchOutlined, -} from '@ant-design/icons'; -import {stores_Context} from '../config'; -import * as config from "../config"; -import {observer} from 'mobx-react'; -import { NavLink, useParams } from "react-router-dom"; +import { useContext, useEffect } from 'react'; +import { Row, Col, Typography, Space, Table, List } from 'antd'; +import { stores_Context } from '../config'; +import { observer } from 'mobx-react'; +import { NavLink, useParams } from 'react-router-dom'; import 'moment/locale/zh-cn'; -import moment from "moment"; -import zhCNlocale from 'antd/es/date-picker/locale/zh_CN'; -import GroupSelect from './../components/search/GroupSelect'; +import SearchForm from './../components/search/SearchForm'; const AgentGroupList = () => { - - const {agentId} = useParams(); - const { customerServicesStore } = useContext(stores_Context); + const { agentId } = useParams(); + const { customerServicesStore, date_picker_store } = useContext(stores_Context); useEffect(() => { - customerServicesStore.fetchGroupListByAgentId(agentId); - }, []); + customerServicesStore.fetchGroupListByAgentId(agentId); + }, []); const groupList = customerServicesStore.groupList; const groupListColumns = customerServicesStore.groupListColumns; - const {startDate, endDate, dateType, inProgress} = customerServicesStore; + const { startDate, endDate, dateType, inProgress } = customerServicesStore; - return ( - <> - - - + return ( + <> + + + 返回 - - customerServicesStore.selectTeam(value)} - style={{ width: '95%' }} show_all={true} + + { + customerServicesStore.setSearchValues(obj, form); + customerServicesStore.fetchGroupListByAgentId(agentId); + }} /> - - - - - - {customerServicesStore.selectDateRange(dates[0], dates[1]);}} - ranges={{ - '本周': [moment().startOf('week'), moment().endOf('week')], - '上周': [moment().startOf('week').subtract(7, 'days'), moment().endOf('week').subtract(7, 'days')], - '本月': [moment().startOf('month'), moment().endOf('month')], - '上个月': [moment().subtract(1, 'months').startOf('month'), moment(new Date()).subtract(1, 'months').endOf('month')], - '近30天': [moment().subtract(30, 'days'), moment()], - '近三个月': [moment().subtract(2, 'month').startOf('month'), moment().endOf('month')], - '今年': [moment().startOf('year').subtract(1, 'month'), moment().endOf('year').subtract(1, 'month')], - '去年': [moment().subtract(1, 'year').startOf('year').subtract(1, 'month'), moment().subtract(1, 'year').endOf('year').subtract(1, 'month')], - }} - /> - - - - + {customerServicesStore.agentCompany} -
{ scroll={{ x: 1000 }} expandable={{ expandedRowRender: (record) => ( - - + - + - + ), @@ -113,9 +73,9 @@ const AgentGroupList = () => { /> - - - ); + + + ); }; export default observer(AgentGroupList); diff --git a/src/views/DestinationGroupCount.jsx b/src/views/DestinationGroupCount.jsx index 3890d24..818266b 100644 --- a/src/views/DestinationGroupCount.jsx +++ b/src/views/DestinationGroupCount.jsx @@ -1,109 +1,49 @@ -import React, {useContext, useEffect} from 'react'; -import { Row, Col, Typography, Space, DatePicker, Button, Select, Table, Divider } from 'antd'; -import { - SearchOutlined, -} from '@ant-design/icons'; +import { useContext, useEffect } from 'react'; +import { Row, Col, Typography, Space, Table, Divider } from 'antd'; import { stores_Context } from '../config'; -import * as config from "../config"; import { observer } from 'mobx-react'; import 'moment/locale/zh-cn'; -import moment from "moment"; -import zhCNlocale from 'antd/es/date-picker/locale/zh_CN'; -import { utils, writeFileXLSX } from "xlsx"; -import GroupSelect from './../components/search/GroupSelect'; +import { utils, writeFileXLSX } from 'xlsx'; +import SearchForm from './../components/search/SearchForm'; const DestinationGroupCount = () => { - - const {customerServicesStore} = useContext(stores_Context); + const { customerServicesStore, date_picker_store } = useContext(stores_Context); const destinationGroupCount = customerServicesStore.destinationGroupCount; const destinationGroupCountColumns = customerServicesStore.destinationGroupCountColumns; - const {startDate, endDate, dateType, inProgress} = customerServicesStore; - - useEffect(() => { - customerServicesStore.selectCountry('china'); - }, []); - - const handleSearchClick = () => { - customerServicesStore.fetchDestinationGroupCount(); - }; + const { startDate, endDate, dateType, inProgress } = customerServicesStore; return ( <> - - - customerServicesStore.selectTeam(value)} - style={{ width: '95%' }} show_all={true} - /> - - - - - - - - - - - - { customerServicesStore.selectDateRange(dates[0], dates[1]); }} - ranges={{ - '本周': [moment().startOf('week'), moment().endOf('week')], - '上周': [moment().startOf('week').subtract(7, 'days'), moment().endOf('week').subtract(7, 'days')], - '本月': [moment().startOf('month'), moment().endOf('month')], - '上个月': [moment().subtract(1, 'months').startOf('month'), moment(new Date()).subtract(1, 'months').endOf('month')], - '近30天': [moment().subtract(30, 'days'), moment()], - '近三个月': [moment().subtract(2, 'month').startOf('month'), moment().endOf('month')], - '今年': [moment().startOf('year').subtract(1, 'month'), moment().endOf('year').subtract(1, 'month')], - '去年': [moment().subtract(1, 'year').startOf('year').subtract(1, 'month'), moment().subtract(1, 'year').endOf('year').subtract(1, 'month')], + + + { + customerServicesStore.setSearchValues(obj, form); + customerServicesStore.fetchDestinationGroupCount(); }} /> - - - 目的地团信息 -
{ { - const wb = utils.table_to_book(document.getElementById("destinationGroupCount").getElementsByTagName("table")[0]); - writeFileXLSX(wb, "目的地团信息.xlsx"); - }}> + const wb = utils.table_to_book(document.getElementById('destinationGroupCount').getElementsByTagName('table')[0]); + writeFileXLSX(wb, '目的地团信息.xlsx'); + }} + > 导出excel diff --git a/src/views/DestinationGroupList.jsx b/src/views/DestinationGroupList.jsx index 0408a97..9174956 100644 --- a/src/views/DestinationGroupList.jsx +++ b/src/views/DestinationGroupList.jsx @@ -1,88 +1,58 @@ -import React, {useContext, useEffect} from 'react'; -import {Row, Col, Typography, Space, DatePicker, Button, Select, Table, List} from 'antd'; -import { - SearchOutlined, -} from '@ant-design/icons'; -import {stores_Context} from '../config'; -import * as config from "../config"; -import {observer} from 'mobx-react'; -import { NavLink, useParams } from "react-router-dom"; +import { useContext, useEffect } from 'react'; +import { Row, Col, Space, Table, List } from 'antd'; +import { stores_Context } from '../config'; +import { observer } from 'mobx-react'; +import { NavLink, useParams } from 'react-router-dom'; import 'moment/locale/zh-cn'; -import moment from "moment"; -import zhCNlocale from 'antd/es/date-picker/locale/zh_CN'; -import GroupSelect from './../components/search/GroupSelect'; +import SearchForm from './../components/search/SearchForm'; const DestinationGroupList = () => { - - const {destinationId} = useParams(); - const { customerServicesStore } = useContext(stores_Context); + const { destinationId } = useParams(); + const { customerServicesStore, date_picker_store } = useContext(stores_Context); useEffect(() => { - customerServicesStore.fetchGroupListByDestinationId(destinationId); - }, []); + customerServicesStore.fetchGroupListByDestinationId(destinationId); + }, []); const destinationGroupList = customerServicesStore.destinationGroupList; const destinationGroupListColumns = customerServicesStore.destinationGroupListColumns; - const {startDate, endDate, dateType, inProgress} = customerServicesStore; + const { inProgress } = customerServicesStore; - return ( - <> - - - + return ( + <> + + + 返回 - - - customerServicesStore.selectTeam(value)} - style={{ width: '95%' }} show_all={true} + + { + customerServicesStore.setSearchValues(obj, form); + customerServicesStore.fetchGroupListByDestinationId(destinationId); + customerServicesStore.fetchDestinationGroupCount(); + }} /> - - - - - {customerServicesStore.selectDateRange(dates[0], dates[1]);}} - ranges={{ - '本周': [moment().startOf('week'), moment().endOf('week')], - '上周': [moment().startOf('week').subtract(7, 'days'), moment().endOf('week').subtract(7, 'days')], - '本月': [moment().startOf('month'), moment().endOf('month')], - '上个月': [moment().subtract(1, 'months').startOf('month'), moment(new Date()).subtract(1, 'months').endOf('month')], - '近30天': [moment().subtract(30, 'days'), moment()], - '近三个月': [moment().subtract(2, 'month').startOf('month'), moment().endOf('month')], - '今年': [moment().startOf('year').subtract(1, 'month'), moment().endOf('year').subtract(1, 'month')], - '去年': [moment().subtract(1, 'year').startOf('year').subtract(1, 'month'), moment().subtract(1, 'year').endOf('year').subtract(1, 'month')], - }} - /> - - - - -
{ scroll={{ x: 600 }} expandable={{ expandedRowRender: (record) => ( - - + - + - + ), @@ -114,9 +75,9 @@ const DestinationGroupList = () => { /> - - - ); + + + ); }; export default observer(DestinationGroupList); diff --git a/src/views/Sale.jsx b/src/views/Sale.jsx index 29f9fbc..4375b67 100644 --- a/src/views/Sale.jsx +++ b/src/views/Sale.jsx @@ -200,7 +200,7 @@ const Sale = () => { }, ]; const tableColumns = type_data.columns.map((ele, i) => - i === 0 ? { ...ele, fix: 'left', children: (ele?.children || []).map((ele_child, i_child) => (i_child === 0 ? { ...ele_child, fix: 'left' } : { ...ele_child })) } : { ...ele } + i === 0 ? { ...ele, fixed: 'left', children: (ele?.children || []).map((ele_child, i_child) => (i_child === 0 ? { ...ele_child, fixed: 'left' } : { ...ele_child })) } : { ...ele } ); return ( From 40b07c503e49663b2df3e1691995ceccb25d1310 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 7 Nov 2023 11:35:32 +0800 Subject: [PATCH 28/86] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E7=AB=AF=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ff1b3f..9735eef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.2.1", + "version": "2.2.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.2.1", + "version": "2.2.2", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index 4569904..332a4f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.2.1", + "version": "2.2.2", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From 51ae4c9cea9052483f818f220fb5d13aee9497b5 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 7 Nov 2023 14:09:50 +0800 Subject: [PATCH 29/86] =?UTF-8?q?perf:=20=E8=AE=A2=E5=8D=95>=E4=BB=AA?= =?UTF-8?q?=E8=A1=A8=E7=9B=98-=E7=A7=BB=E5=8A=A8=E8=AE=A2=E5=8D=95:=20?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/charts/MobileDeal.jsx | 72 +++++++++++++----------- src/components/search/DataTypeSelect.jsx | 2 +- src/components/search/SearchForm.jsx | 10 ++-- src/stores/DashboardStore.js | 11 ++++ 4 files changed, 55 insertions(+), 40 deletions(-) diff --git a/src/charts/MobileDeal.jsx b/src/charts/MobileDeal.jsx index c3c3c18..329adbe 100644 --- a/src/charts/MobileDeal.jsx +++ b/src/charts/MobileDeal.jsx @@ -1,41 +1,45 @@ -import React, {Component} from 'react'; -import {Table, Button, Space, Radio} from 'antd'; -import {SearchOutlined} from '@ant-design/icons'; -import GroupSelect from '../components/search/GroupSelect'; -import DatePickerCharts from "../components/search/DatePickerCharts"; -import {stores_Context} from "../config"; -import {observer} from "mobx-react"; +import React, { Component } from 'react'; +import { Table, } from 'antd'; +import SearchForm from './../components/search/SearchForm'; +import { stores_Context } from '../config'; +import { observer } from 'mobx-react'; class MobileDeal extends Component { - static contextType = stores_Context; + static contextType = stores_Context; - constructor(props) { - super(props); - } + constructor(props) { + super(props); + } - render() { - const {dashboard_store} = this.context; - const mobile_data = dashboard_store.mobile_data; - return ( -
-

移动成交

- - - - - - 预定日期 - 出发日期 - - - - -
- - ); - } + render() { + const { dashboard_store, date_picker_store } = this.context; + const mobile_data = dashboard_store.mobile_data; + return ( +
+

移动成交

+ { + dashboard_store.setMobileSearchValues(obj, form); + mobile_data.asyncFetch(); + }} + /> +
+ + ); + } } export default observer(MobileDeal); diff --git a/src/components/search/DataTypeSelect.jsx b/src/components/search/DataTypeSelect.jsx index 9d199ba..9d30117 100644 --- a/src/components/search/DataTypeSelect.jsx +++ b/src/components/search/DataTypeSelect.jsx @@ -35,7 +35,7 @@ class DataTypeSelect extends Component { {...extProps} > {dateTypes.map((ele) => ( - + {ele.label} ))} diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index 464ea44..e03074c 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -221,7 +221,7 @@ export default observer((props) => { {getFields({ sort, initialValue, hides, shows, fieldProps, form })} {/* 'textAlign': 'right' */} - + +
diff --git a/src/views/Orders.jsx b/src/views/Orders.jsx index d6c20e4..aecbc1b 100644 --- a/src/views/Orders.jsx +++ b/src/views/Orders.jsx @@ -445,7 +445,7 @@ class Orders extends Component { ...ele, children: ( <> -
+
{ From 7eb57a0ffd54989c7b68ca8c2c9b98afad9e10cb Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 8 Nov 2023 11:54:39 +0800 Subject: [PATCH 32/86] =?UTF-8?q?feat:=20=E6=9D=83=E9=99=90=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ProtectedRoute.jsx | 42 ++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/views/ProtectedRoute.jsx b/src/views/ProtectedRoute.jsx index 2f58972..889609e 100644 --- a/src/views/ProtectedRoute.jsx +++ b/src/views/ProtectedRoute.jsx @@ -1,13 +1,8 @@ -import React, { useContext, useEffect } from 'react'; -import { Row, Col, Button, Tabs, Spin, Result, Space } from 'antd'; -import { ContainerOutlined, SearchOutlined } from '@ant-design/icons'; +import { useContext } from 'react'; +import { Button, Result, message } from 'antd'; import { stores_Context } from '../config'; -import { Line } from '@ant-design/charts'; import { observer } from 'mobx-react'; -import DatePickerCharts from '../components/search/DatePickerCharts'; -import { NavLink, useParams, Outlet, useOutlet, useLocation, useNavigate } from 'react-router-dom'; -import * as comm from '../utils/commons'; -import * as config from '../config'; +import { Outlet, useLocation, } from 'react-router-dom'; const ProtectedRoute = ({ auth }) => { const { auth_store } = useContext(stores_Context); @@ -16,9 +11,38 @@ const ProtectedRoute = ({ auth }) => { return ; } + const authApplyLink = + // eslint-disable-next-line max-len + 'dingtalk://dingtalkclient/action/openapp?app_id=-4&container_type=work_platform&corpid=ding48bce8fd3957c96b&ddtab=true&redirect_type=jump&redirect_url=https%3A%2F%2Faflow.dingtalk.com%2Fdingtalk%2Fmobile%2Fhomepage.htm%3Fback_control%3Dfalse%26backcontrol%3Dfalse%26corpid%3Dding48bce8fd3957c96b%26dd_progress%3Dfalse%26dd_share%3Dfalse%26ddtab%3Dtrue%26showmenu%3Dfalse%23%2Fcustom%3Fpcredirect%3Dself%26processCode%3DPROC-C0C2E970-1E4E-44CF-A389-C7D3F10A7885'; + + const copyToClipboard = (text) => { + navigator.clipboard.writeText(text).then(() => { + message.success('已复制到剪贴板'); + }); + }; + + const { pathname } = useLocation(); + const applyInfo = `授权账户: ${auth_store.user.name}(${auth_store.user.userid})\n申请权限: ${auth[auth.length - 1].toString()}\n请求页面: ${pathname}`; + return (
- + {/* '试着联系一下技术,所需权限: ' + auth.toString() */} + +
+ 复制以下信息到OA审批申请 + +
+
+              {applyInfo}
+            
+ + } + extra={} + />
); }; From 46e6dfcf1e7312cc05d5d49623404cb131f5a341 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 9 Nov 2023 14:00:17 +0800 Subject: [PATCH 33/86] =?UTF-8?q?style:=20=E6=90=9C=E7=B4=A2=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=90=B8=E9=A1=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.css | 7 + src/charts/Customer_care_inchina.jsx | 4 +- src/charts/Customer_care_potential.jsx | 6 +- src/charts/Customer_care_regular.jsx | 343 +++++++++++++------------ src/components/DateGroupRadio/date.js | 2 +- src/views/AgentGroupCount.jsx | 2 +- src/views/DestinationGroupCount.jsx | 2 +- src/views/Distribution.jsx | 2 +- src/views/Home.jsx | 3 +- src/views/Orders.jsx | 7 +- src/views/Orders_sub.jsx | 8 +- src/views/Sale.jsx | 2 +- src/views/Sale_KPI.jsx | 2 +- 13 files changed, 204 insertions(+), 186 deletions(-) diff --git a/src/App.css b/src/App.css index e9e982f..1acdfbf 100644 --- a/src/App.css +++ b/src/App.css @@ -34,3 +34,10 @@ .p-s1{ padding: .5em; } + +.sticky-top{ + margin: -16px -8px .5em -8px; + position: sticky; + top: 0; + z-index: 100; +} diff --git a/src/charts/Customer_care_inchina.jsx b/src/charts/Customer_care_inchina.jsx index 2240454..135492c 100644 --- a/src/charts/Customer_care_inchina.jsx +++ b/src/charts/Customer_care_inchina.jsx @@ -17,7 +17,7 @@ const Customer_care_inchina = () => { return (
- +
{ }} /> + +

在华客人

diff --git a/src/charts/Customer_care_potential.jsx b/src/charts/Customer_care_potential.jsx index 48955a8..7a10c4b 100644 --- a/src/charts/Customer_care_potential.jsx +++ b/src/charts/Customer_care_potential.jsx @@ -16,9 +16,9 @@ const Customer_care_potential = () => { return (
- +
- { }} /> + +

潜力客户

diff --git a/src/charts/Customer_care_regular.jsx b/src/charts/Customer_care_regular.jsx index 2f11da0..9032bff 100644 --- a/src/charts/Customer_care_regular.jsx +++ b/src/charts/Customer_care_regular.jsx @@ -1,175 +1,184 @@ -import React, {useContext, useEffect} from 'react'; -import {Row, Col, Divider, Table} from 'antd'; -import {utils, writeFileXLSX} from 'xlsx'; -import {stores_Context} from '../config'; -import {observer} from 'mobx-react'; +import React, { useContext, useEffect } from 'react'; +import { Row, Col, Divider, Table } from 'antd'; +import { utils, writeFileXLSX } from 'xlsx'; +import { stores_Context } from '../config'; +import { observer } from 'mobx-react'; import SearchForm from './../components/search/SearchForm'; const Customer_care_regular = () => { + const { orders_store, date_picker_store, customer_store } = useContext(stores_Context); + const regular_data = customer_store.regular_data; - const {orders_store, date_picker_store, customer_store} = useContext(stores_Context); - const regular_data = customer_store.regular_data; + useEffect(() => {}, []); - useEffect(() => { - - }, []); - - - return ( -
- -
- { - customer_store.setSearchValues(obj, form, 'regular_data'); - customer_store.regular_customer_order(); - customer_store.regular_customer_order(true); + return ( +
+ +
+ { + customer_store.setSearchValues(obj, form, 'regular_data'); + customer_store.regular_customer_order(); + customer_store.regular_customer_order(true); + }} + /> + + + + +

老客户

+ +
+
{Math.round(text * 100)}%, + }, + { + title: '毛利', + dataIndex: 'ML', + key: 'ML', + }, + { + title: '人数(含成人+儿童)', + dataIndex: 'PersonNum', + key: 'PersonNum', + }, + ]} + size="small" + pagination={false} + rowKey={(record) => record.ItemName} + /> + + + + { + const wb = utils.table_to_book(document.getElementById('table_to_xlsx').getElementsByTagName('table')[0]); + writeFileXLSX(wb, '老客户.xlsx'); }} - /> - - -

老客户

- -
-
{Math.round(text * 100)}% - }, - { - title: '毛利', - dataIndex: 'ML', - key: 'ML', - }, - { - title: '人数(含成人+儿童)', - dataIndex: 'PersonNum', - key: 'PersonNum', - }, - ] - } size="small" pagination={false} rowKey={record => record.ItemName} - /> - - - { - const wb = utils.table_to_book(document.getElementById("table_to_xlsx").getElementsByTagName('table')[0]); - writeFileXLSX(wb, "老客户.xlsx"); - }}>导出excel -
{text == 1 ? '成行' : '未成行'}, - sorter: (a, b) => b.OrderState - a.OrderState, - }, - { - title: '毛利', - dataIndex: 'ML', - key: 'ML', - }, - { - title: '人数', - dataIndex: 'PersonNum', - key: 'PersonNum', - }, - { - title: '天数', - dataIndex: 'COLI_Days', - key: 'COLI_Days', - }, - { - title: '人天数', - dataIndex: 'CGI_PersonDays', - key: 'CGI_PersonDays', - }, - { - title: '走团日期', - dataIndex: 'COLI_OrderStartDate', - key: 'COLI_OrderStartDate', - }, - { - title: '小组', - dataIndex: 'Department', - key: 'Department', - }, - { - title: '老客户', - dataIndex: 'COLI_IsOld', - key: 'COLI_IsOld', - }, - { - title: '老客户推荐', - dataIndex: 'COLI_IsCusCommend', - key: 'COLI_IsCusCommend', - }, - { - title: '网站', - dataIndex: 'COLI_WebCode', - key: 'COLI_WebCode', - }, - { - title: '来源', - dataIndex: 'SourceType', - key: 'SourceType', - }, - { - title: '在华', - dataIndex: 'ZH', - key: 'ZH', - }, - ] - } size="small" - rowKey={record => record.COLI_ID} - /> - - - - - ); - + > + 导出excel + + +
{text == 1 ? '成行' : '未成行'}, + sorter: (a, b) => b.OrderState - a.OrderState, + }, + { + title: '毛利', + dataIndex: 'ML', + key: 'ML', + }, + { + title: '人数', + dataIndex: 'PersonNum', + key: 'PersonNum', + }, + { + title: '天数', + dataIndex: 'COLI_Days', + key: 'COLI_Days', + }, + { + title: '人天数', + dataIndex: 'CGI_PersonDays', + key: 'CGI_PersonDays', + }, + { + title: '走团日期', + dataIndex: 'COLI_OrderStartDate', + key: 'COLI_OrderStartDate', + }, + { + title: '小组', + dataIndex: 'Department', + key: 'Department', + }, + { + title: '老客户', + dataIndex: 'COLI_IsOld', + key: 'COLI_IsOld', + }, + { + title: '老客户推荐', + dataIndex: 'COLI_IsCusCommend', + key: 'COLI_IsCusCommend', + }, + { + title: '网站', + dataIndex: 'COLI_WebCode', + key: 'COLI_WebCode', + }, + { + title: '来源', + dataIndex: 'SourceType', + key: 'SourceType', + }, + { + title: '在华', + dataIndex: 'ZH', + key: 'ZH', + }, + ]} + size="small" + rowKey={(record) => record.COLI_ID} + /> + + + + ); }; export default observer(Customer_care_regular); - diff --git a/src/components/DateGroupRadio/date.js b/src/components/DateGroupRadio/date.js index c45e03f..8b249a1 100644 --- a/src/components/DateGroupRadio/date.js +++ b/src/components/DateGroupRadio/date.js @@ -154,5 +154,5 @@ export const resultDataCb = (dataRaw, dateGroup, { data1, data2 }, fieldMapper, const retData = [].concat(parseData1, reindexData2 ).map(ele => ({...ele, [fieldMapper.dateKey]: data1KeyMappedStr[ele[fieldMapper.dateKey]] || data2KeyMappedStr[ele[fieldMapper.dateKey]]})); const avg1 = parse1.avgVal; // console.log('callback', dateGroup, retData, data1KeyMappedStr, data2KeyMappedStr); - cb(dateGroup, retData, avg1); + cb(dateGroup, retData, avg1, parse2.avgVal); }; diff --git a/src/views/AgentGroupCount.jsx b/src/views/AgentGroupCount.jsx index f685e2e..07b6e69 100644 --- a/src/views/AgentGroupCount.jsx +++ b/src/views/AgentGroupCount.jsx @@ -19,7 +19,7 @@ const AgentGroupCount = () => { return ( <> - + { return ( <> - + { }; return ( <> - + { }; return ( <> - - {/* style={{ margin: '-16px -8px', padding: 0 }} */} + - + + + - - - diff --git a/src/views/Orders_sub.jsx b/src/views/Orders_sub.jsx index f911fdd..5f24d28 100644 --- a/src/views/Orders_sub.jsx +++ b/src/views/Orders_sub.jsx @@ -262,7 +262,7 @@ const Orders_sub = () => { }; return (
- +
返回 @@ -286,6 +286,9 @@ const Orders_sub = () => { }} /> + + + { fieldMapper={orders_store.orderCount_type_dateRadio.orderCountDataFieldMapper} /> - - - diff --git a/src/views/Sale.jsx b/src/views/Sale.jsx index 4375b67..8c1c564 100644 --- a/src/views/Sale.jsx +++ b/src/views/Sale.jsx @@ -205,7 +205,7 @@ const Sale = () => { return (
- +
{ const lineConfig = { appendPadding: 10, xField: 'groupDateVal', yField: 'SumML', seriesField: 'groupsLabel', isGroup: true, smooth: true, meta: comm.cloneDeep(dataFieldAlias), }; return (
- +
Date: Tue, 7 Nov 2023 17:16:49 +0800 Subject: [PATCH 34/86] =?UTF-8?q?feat:=20=E9=80=8F=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 透视表字段操作 --- package-lock.json | 361 +++++++++++++++++++- package.json | 4 + src/App.jsx | 13 +- src/components/DateGroupRadio/date.js | 2 +- src/components/search/SearchForm.jsx | 6 +- src/libs/ht.js | 88 ++++- src/stores/Distribution.js | 2 +- src/utils/commons.js | 14 + src/views/OrdersPivot.jsx | 471 ++++++++++++++++++++++++++ 9 files changed, 941 insertions(+), 20 deletions(-) create mode 100644 src/views/OrdersPivot.jsx diff --git a/package-lock.json b/package-lock.json index 9735eef..802efcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,14 @@ "@ant-design/pro-components": "^2.6.16", "antd": "^4.22.6", "dingtalk-jsapi": "^3.0.9", + "insert-css": "^2.0.0", "mobx": "^6.6.1", "mobx-react": "^7.5.2", "react": "^18.2.0", + "react-beautiful-dnd": "^13.1.1", + "react-dnd": "^16.0.1", "react-dom": "^18.2.0", + "react-draggable": "^4.4.6", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", "web-vitals": "^2.1.4", @@ -4826,6 +4830,21 @@ "react-dom": ">=16.9.0" } }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -5395,6 +5414,15 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -5475,8 +5503,7 @@ "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/q": { "version": "1.5.5", @@ -5497,7 +5524,6 @@ "version": "18.2.20", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.20.tgz", "integrity": "sha512-WKNtmsLWJM/3D5mG4U84cysVY31ivmyw85dE84fOCk5Hx78wezB/XEjVPWl2JTZ5FkEeaTJf+VgUAUn3PE7Isw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5513,6 +5539,17 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.30", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.30.tgz", + "integrity": "sha512-i2kqM6YaUwFKduamV6QM/uHbb0eCP8f8ZQ/0yWf+BsAVVsZPRYJ9eeGWZ3uxLfWwwA0SrPRMTPTqsPFkY3HZdA==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/resize-observer-browser": { "version": "0.1.7", "resolved": "https://registry.npmmirror.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", @@ -5534,8 +5571,7 @@ "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/semver": { "version": "7.3.13", @@ -7441,6 +7477,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -7775,6 +7819,14 @@ "postcss": "^8.4" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", @@ -8599,6 +8651,16 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "node_modules/dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -11156,6 +11218,14 @@ "@babel/runtime": "^7.7.6" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -11538,7 +11608,7 @@ }, "node_modules/insert-css": { "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/insert-css/-/insert-css-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz", "integrity": "sha512-xGq5ISgcUP5cvGkS2MMFLtPDBtrtQPSFfC6gA6U8wHKqfjTIMZLZNxOItQnoSjdOzlXOLU/yD32RKC4SvjNbtA==" }, "node_modules/internal-slot": { @@ -14565,6 +14635,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -17049,6 +17124,11 @@ "performance-now": "^2.1.0" } }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -17703,6 +17783,24 @@ "node": ">=14" } }, + "node_modules/react-beautiful-dnd": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", + "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-color": { "version": "2.17.3", "resolved": "https://registry.npmmirror.com/react-color/-/react-color-2.17.3.tgz", @@ -17844,6 +17942,35 @@ "node": ">=8" } }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz", @@ -17856,6 +17983,19 @@ "react": "^18.2.0" } }, + "node_modules/react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/react-error-overlay": { "version": "6.0.11", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", @@ -17871,6 +18011,35 @@ "resolved": "https://registry.npmmirror.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "node_modules/react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -18109,6 +18278,14 @@ "node": ">=6.0.0" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -19746,6 +19923,11 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "node_modules/tinycolor2": { "version": "1.4.2", "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.4.2.tgz", @@ -20231,6 +20413,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -24912,6 +25102,21 @@ "rc-util": "^5.24.4" } }, + "@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -25327,6 +25532,15 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -25407,8 +25621,7 @@ "@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "@types/q": { "version": "1.5.5", @@ -25429,7 +25642,6 @@ "version": "18.2.20", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.20.tgz", "integrity": "sha512-WKNtmsLWJM/3D5mG4U84cysVY31ivmyw85dE84fOCk5Hx78wezB/XEjVPWl2JTZ5FkEeaTJf+VgUAUn3PE7Isw==", - "dev": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -25445,6 +25657,17 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.30", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.30.tgz", + "integrity": "sha512-i2kqM6YaUwFKduamV6QM/uHbb0eCP8f8ZQ/0yWf+BsAVVsZPRYJ9eeGWZ3uxLfWwwA0SrPRMTPTqsPFkY3HZdA==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/resize-observer-browser": { "version": "0.1.7", "resolved": "https://registry.npmmirror.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", @@ -25466,8 +25689,7 @@ "@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "@types/semver": { "version": "7.3.13", @@ -26924,6 +27146,11 @@ "wrap-ansi": "^7.0.0" } }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -27185,6 +27412,14 @@ "postcss-selector-parser": "^6.0.9" } }, + "css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", @@ -27796,6 +28031,16 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "requires": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -29682,6 +29927,14 @@ "@babel/runtime": "^7.7.6" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -29963,7 +30216,7 @@ }, "insert-css": { "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/insert-css/-/insert-css-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz", "integrity": "sha512-xGq5ISgcUP5cvGkS2MMFLtPDBtrtQPSFfC6gA6U8wHKqfjTIMZLZNxOItQnoSjdOzlXOLU/yD32RKC4SvjNbtA==" }, "internal-slot": { @@ -32217,6 +32470,11 @@ "fs-monkey": "^1.0.3" } }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -33847,6 +34105,11 @@ "performance-now": "^2.1.0" } }, + "raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -34308,6 +34571,20 @@ "whatwg-fetch": "^3.6.2" } }, + "react-beautiful-dnd": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", + "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", + "requires": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + } + }, "react-color": { "version": "2.17.3", "resolved": "https://registry.npmmirror.com/react-color/-/react-color-2.17.3.tgz", @@ -34413,6 +34690,18 @@ } } }, + "react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "requires": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + } + }, "react-dom": { "version": "18.2.0", "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz", @@ -34422,6 +34711,15 @@ "scheduler": "^0.23.0" } }, + "react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "requires": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + } + }, "react-error-overlay": { "version": "6.0.11", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", @@ -34437,6 +34735,26 @@ "resolved": "https://registry.npmmirror.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -34623,6 +34941,14 @@ "minimatch": "^3.0.5" } }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -35860,6 +36186,11 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "tinycolor2": { "version": "1.4.2", "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.4.2.tgz", @@ -36208,6 +36539,12 @@ "requires-port": "^1.0.0" } }, + "use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "requires": {} + }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", diff --git a/package.json b/package.json index 332a4f8..5e8f739 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,14 @@ "@ant-design/pro-components": "^2.6.16", "antd": "^4.22.6", "dingtalk-jsapi": "^3.0.9", + "insert-css": "^2.0.0", "mobx": "^6.6.1", "mobx-react": "^7.5.2", "react": "^18.2.0", + "react-beautiful-dnd": "^13.1.1", + "react-dnd": "^16.0.1", "react-dom": "^18.2.0", + "react-draggable": "^4.4.6", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", "web-vitals": "^2.1.4", diff --git a/src/App.jsx b/src/App.jsx index cf57d31..df96778 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -42,6 +42,7 @@ import ExchangeRate from './charts/ExchangeRate'; import KPI from './views/KPI'; import Distribution from './views/Distribution'; import Detail from './views/Detail'; +import OrderPivot from './views/OrdersPivot'; import Welcome from './views/Welcome'; import { stores_Context, APP_VERSION } from './config'; import { WaterMark } from '@ant-design/pro-components'; @@ -52,7 +53,7 @@ const App = () => { const menu_items = [ { key: 1, label: 欢迎, icon: }, - { key: 'annual', label: 综合看板, icon: }, + { key: 'annual', label: 综合看板, icon: }, { key: 2, label: '市场', @@ -61,12 +62,17 @@ const App = () => { { key: 21, label: 订单数据, - icon: , + // icon: , }, { key: 22, label: 仪表盘, - icon: , + // icon: , + }, + { + key: 'orders-pivot', + label: 数据透视, + // icon: , }, ], }, @@ -190,6 +196,7 @@ const App = () => { } /> } /> } /> + } /> }> } /> diff --git a/src/components/DateGroupRadio/date.js b/src/components/DateGroupRadio/date.js index 8b249a1..b413cd2 100644 --- a/src/components/DateGroupRadio/date.js +++ b/src/components/DateGroupRadio/date.js @@ -153,6 +153,6 @@ export const resultDataCb = (dataRaw, dateGroup, { data1, data2 }, fieldMapper, })); const retData = [].concat(parseData1, reindexData2 ).map(ele => ({...ele, [fieldMapper.dateKey]: data1KeyMappedStr[ele[fieldMapper.dateKey]] || data2KeyMappedStr[ele[fieldMapper.dateKey]]})); const avg1 = parse1.avgVal; - // console.log('callback', dateGroup, retData, data1KeyMappedStr, data2KeyMappedStr); + // console.log('callback', dateGroup, retData, avg1, parse2.avgVal, data1KeyMappedStr, data2KeyMappedStr); cb(dateGroup, retData, avg1, parse2.avgVal); }; diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index ccd4d02..d86390e 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -312,14 +312,16 @@ function getFields(props) { rules={[{ required: true, message: '选择小组' }]} > - , fieldProps?.DepartmentList?.col + , + fieldProps?.DepartmentList?.col ), item( 'WebCode', 99, - + , + fieldProps?.WebCode?.col ), item( 'IncludeTickets', diff --git a/src/libs/ht.js b/src/libs/ht.js index 0704ebe..a423627 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -1,4 +1,4 @@ -import { fixTo4Decimals, fixTo1Decimals } from "../utils/commons"; +import { fixTo4Decimals, fixTo1Decimals, fixToInt, groupBy, sortBy, cloneDeep, pick, unique, flush } from "../utils/commons"; /** * 事业部 @@ -173,3 +173,89 @@ export const KPISubjects = [ // { key: 'reply_eff_wa', value: 'reply_eff_wa', label: 'WA回复效率'}, // { key: 'sum_person_num', value: 'sum_person_num', label: '人数' }, ]; + + +/** + * 数据透视计算 + * @param {object[]} data + * @param {any[]} groupby + * @param {object[]} keys + * @param {string} value + * @returns + */ +export const pivotBy = (data, [rows, columns, date], keys, value) => { + const groupbyKeys = flush([].concat(rows, columns, [date])); + console.log('pivotBy', [rows, columns, date], groupbyKeys ); + const uniqueKeys = groupbyKeys.map(keyField => { + const keyu = [...new Set(data.map(f => f[keyField]))]; + return keyu; + }); + const pivotResult = []; // new Array(uniqueKeys.reduce((r, v) => r * v.length, 1)); + const groupData = groupBy(data, row => groupbyKeys.map(kk => `${row[kk]}`).join('=@=')); + + Object.keys(groupData).map((group_str) => { + const _rowKey = groupData[group_str].map((v) => v.key).join('_'); + const _len = groupData[group_str].length; + const _row = { + ...pick(groupData[group_str][0], groupbyKeys), + ...(groupbyKeys.length < 2 ? { rowKey: '总' } : { rowKey: cloneDeep(groupbyKeys).slice(0, -1).map(_k => groupData[group_str][0][_k]).join("»") }), + key: _rowKey, + SumOrder: _len, + SumPersonNum: groupData[group_str].reduce((r, v) => r + v.personNum, 0), + ConfirmOrder: groupData[group_str].reduce((r, v) => r + (Number(v.orderState) === 1 ? 1 : 0), 0), + transactions: groupData[group_str].reduce((r, v) => r + v.transactions, 0), + SumML: groupData[group_str].reduce((r, v) => r + v.ML, 0), + quotePrice: groupData[group_str].reduce((r, v) => r + v.quotePrice, 0), // todo: quotePrice + tourdays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.tourdays, 0) / _len), + applyDays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.applyDays, 0) / _len), + confirmDays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.confirmDays, 0) / _len), + }; + pivotResult.push({ + ..._row, + ConfirmRates: _row.ConfirmOrder ? fixTo4Decimals(_row.ConfirmOrder / _row.SumOrder) : 0, + OrderValue: _row.SumOrder ? fixToInt(_row.SumML / _row.SumOrder) : 0, + }); + return group_str; + }); + // 列转置 + const rowsData = groupBy(data, row => rows.map(kk => `${row[kk]}`).join('=@=')); + const rowsWithColumns = Object.keys(rowsData).map(rowKey => { + const _colData = groupBy(rowsData[rowKey], crow => columns.map(kk => `${crow[kk]}`).join('=@=')); + const _topRowKey = []; + return Object.keys(_colData).reduce((r, colKey) => { + const _len = _colData[colKey].length; + const _rowKey = _colData[colKey].map((v) => v.key).join('_'); + _topRowKey.push(_rowKey); + const _row = { + key: _rowKey, + SumOrder: _len, + SumPersonNum: _colData[colKey].reduce((r, v) => r + v.personNum, 0), + ConfirmOrder: _colData[colKey].reduce((r, v) => r + (Number(v.orderState) === 1 ? 1 : 0), 0), + transactions: _colData[colKey].reduce((r, v) => r + v.transactions, 0), + SumML: _colData[colKey].reduce((r, v) => r + v.ML, 0), + quotePrice: _colData[colKey].reduce((r, v) => r + v.quotePrice, 0), // todo: quotePrice + tourdays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.tourdays, 0) / _len), + applyDays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.applyDays, 0) / _len), + confirmDays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.confirmDays, 0) / _len), + }; + const _rowCalc = { + ConfirmRates: _row.ConfirmOrder ? fixTo4Decimals(_row.ConfirmOrder / _row.SumOrder) : 0, + OrderValue: _row.SumOrder ? fixToInt(_row.SumML / _row.SumOrder) : 0, + }; + Object.assign(r.columns, {[colKey]: {..._row, ..._rowCalc}}); + // (r.columns || (r.columns = {})).push({[colKey]: {..._row, ..._rowCalc}}); + return {...r, key: _topRowKey.join('_')}; + }, {...pick(rowsData[rowKey][0], rows), columns: {}}); + }).map(everyR => { + const allColumns = Object.values(everyR.columns).reduce((r, c) => r.concat([c]), []); + return ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum'].reduce( + (r, skey) => ({ ...r, + [skey]: allColumns.reduce((a, c) => a + c[skey], 0), + }), + everyR + ); // todo: 其他数据列计算 + }); + + // console.log('pivot res', uniqueKeys, rowsWithColumns, pivotResult); + return { data: pivotResult, columnValues: uniqueKeys, summary: rowsWithColumns }; +}; diff --git a/src/stores/Distribution.js b/src/stores/Distribution.js index 0e92697..f21a92b 100644 --- a/src/stores/Distribution.js +++ b/src/stores/Distribution.js @@ -69,7 +69,7 @@ class Distribution { this.scatterDays = daysData; }); } - return this.detailData; + return json.result; }; resetData = () => { diff --git a/src/utils/commons.js b/src/utils/commons.js index c65f069..be8b8b8 100644 --- a/src/utils/commons.js +++ b/src/utils/commons.js @@ -353,6 +353,20 @@ export function pick(object, keys) { }, {}); } +/** + * 返回对象的副本,经过筛选以省略指定的键。 + * @param {*} object + * @param {string[]} keysToOmit + * @returns + */ +export function omit(object, keysToOmit) { + return Object.fromEntries( + Object.entries(object).filter( + ([key]) => !keysToOmit.includes(key) + ) + ); +} + /** * 深拷贝 */ diff --git a/src/views/OrdersPivot.jsx b/src/views/OrdersPivot.jsx new file mode 100644 index 0000000..8fe250e --- /dev/null +++ b/src/views/OrdersPivot.jsx @@ -0,0 +1,471 @@ +import { useContext, useEffect, useState } from 'react'; +import { observer } from 'mobx-react'; +import { useParams } from 'react-router-dom'; +import { stores_Context } from '../config'; +import { Row, Col, Spin, Table, Button, Select, Typography, Card } from 'antd'; +import { cloneDeep, groupBy, isEmpty, omit, sortBy } from '../utils/commons'; +import { dataFieldAlias, pivotBy } from '../libs/ht'; +import SearchForm from '../components/search/SearchForm'; +import { Line } from '@ant-design/plots'; +import DateGroupRadio from '../components/DateGroupRadio'; + +const { Text } = Typography; + +const filterFields = [ + { key: 'country', label: '国籍' }, + { key: 'SourceType', label: '来源类型' }, + { key: 'productType', label: '产品类型' }, + { key: 'guestGroupType', label: '客群类别' }, + { key: 'travelMotivation', label: '出行动机' }, + { key: 'operatorName', label: '顾问' }, + // todo: 目的地, 目的地国家, 页面类型[PPC, NL...] +]; +const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); + +const combineArrays = (arr, sep = '_') => { + // if (arr.length === 0) return []; + if (arr.length === 1) return arr[0].map((item) => item); + + const output = []; + const head = arr[0]; + const tail = arr.slice(1); + + head.forEach((item) => { + const suffixes = combineArrays(tail, sep); + suffixes.forEach((suffix) => { + output.push(`${item}${sep}${suffix}`); + }); + }); + + return output; +}; + +// 注意TdCell要提到DataTable作用域外声明 +const TdCell = (tdprops) => { + // onMouseEnter, onMouseLeave在数据量多的时候,会严重阻塞表格单元格渲染,严重影响性能 + const { onMouseEnter, onMouseLeave, ...restProps } = tdprops; + return + { + detailRefresh(obj); + }} + /> + + + + {/* extra={} */} + + + + {/* todo: 拖拽的操作 */} + {/*
+ {filterFields.map((tag) => ( + -1} onChange={(checked) => handleChange(tag.key, checked)} color={'orange'}> + {tag.label} + + ))} +
*/} + + + + + 行: + + + + + + {rowFields.length > 0 + ? cloneDeep(pivotDateColumnsValues) + .slice(0, rowFields.length) + .map((_colArr, _colIndex) => ( + + + {filterFieldsMapped[pivotDateColumns[0][_colIndex]].label}: + + + + + + )) + : null} + + + + + + + 列: + + + + + + {/* {columnFields.length > 0 + ? cloneDeep(pivotDateColumnsValues) + .slice(rowFields.length) + .map((_colArr, _colIndex) => ( + <> + + + {filterFieldsMapped[pivotDateColumns[_colIndex + rowFields.length]]?.label || _colIndex + rowFields.length}: + + + + + + + )) + : null} */} + + + + + + +
+ +
+

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

+ + + + + + + + + + +
+ +

+ 透视汇总表: {dataFieldAlias[lineConfig.yField].label} +

+
; +}; + +export default observer((props) => { + const { field } = useParams(); + const { date_picker_store: searchFormStore, orders_store, DistributionStore } = useContext(stores_Context); + const { formValues, formValuesToSub } = searchFormStore; + // const { curTab, scatterDays, detailData } = DistributionStore; + + const [loading, setLoading] = useState(false); + const [rawData, setRawData] = useState([]); + const [dataBeforePick, setDataBeforePick] = useState([]); + const [dataBeforeXChange, setDataBeforeXChange] = useState([]); + const [dataSource, setDataSource] = useState([]); + const [dataSourceMapped, setDataSourceMapped] = useState({}); + + const [pivotTableDataSource, setPivotTableDataSource] = useState([]); + + const [pivotColumns, setPivotColumns] = useState([]); + const [pivotDateColumns, setPivotDateColumns] = useState([[], []]); + const [pivotDateColumnsValues, setPivotDateColumnsValues] = useState([]); + + useEffect(() => { + calcDataByDate(); + resetX(); + resetItemFilter(); + + return () => {}; + }, [pivotDateColumns]); + + useEffect(() => { + if (lineChartX === 'day') { + setDataBeforeXChange(dataSource); + } + + return () => {}; + }, [dataSource]); + + const detailRefresh = async (obj) => { + setLoading(true); + DistributionStore.getDetailData({ + ...(obj || formValuesToSub), + }).then((resData) => { + setLoading(false); + setRawData(resData); + // const { data, columnValues } = pivotBy(resData, ['country', 'guestGroupType', 'applyDate'], 'personNum'); + // setPivotDateColumns(['applyDate']); + calcDataByDate(resData); + // setLineChartX('day'); + resetX(); + resetItemFilter(); + }); + }; + + /** + * 走势的数据 + * 汇总 + */ + const calcDataByDate = (_rawData) => { + // console.log(';;;;;', pivotDateColumns); + const { data, columnValues, summary } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, ['applyDate'])); + setPivotDateColumnsValues(cloneDeep(columnValues).slice(0, -1)); + setDataBeforePick(data.sort(sortBy('applyDate'))); + setDataSource(data.sort(sortBy('applyDate'))); + + setPivotTableDataSource(summary.sort(sortBy('SumOrder')).reverse()); + }; + + const line_config = { + // data: dataSource, + padding: 'auto', + xField: 'applyDate', + yField: 'SumOrder', + seriesField: 'rowKey', + // xAxis: { + // type: 'timeCat', + // }, + yAxis: { + min: 0, + maxTickInterval: 5, + }, + // smooth: true, + label: {}, // 显示标签 + legend: { + position: 'right-top', + // title: { + // text: '总合计 ' + dataSource.reduce((a, b) => a + b.SumOrder, 0), + // }, + itemMarginBottom: 12, // 垂直间距 + }, + }; + + const [lineConfig, setLineConfig] = useState(cloneDeep(line_config)); + + // 透视配置:行列选项 + const [leftFields, setLeftFields] = useState(filterFields); + const [rightFields, setRightFields] = useState(filterFields); + const [rowFields, setRowFields] = useState([]); + const [columnFields, setColumnFields] = useState([]); + + const handleRowsPick = (v) => { + const pickKeys = v.map((ele) => ele.key); + setRowFields(pickKeys); + // const leftFieldsMapped = leftFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); + const _left = omit(filterFieldsMapped, pickKeys); + setRightFields(isEmpty(v) ? filterFields : Object.values(_left)); + // setPivotDateColumns([].concat(pickKeys, columnFields)); + setPivotDateColumns([pickKeys, columnFields]); + resetItemFilter(); + }; + + const handleColsPick = (val) => { + const pickKeys = isEmpty(val) ? [] : Array.isArray(val) ? val.map((ele) => ele.key) : [val.key]; + setColumnFields(pickKeys); + const afterLeft = Object.values(omit(filterFieldsMapped, rowFields)); + setRightFields(afterLeft); // 单选 + // const rightFieldsMapped = rightFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); + // const _left = omit(rightFieldsMapped, pickKeys); + // setRightFields(isEmpty(val) ? afterLeft : Object.values(_left)); // 多选 + // setPivotDateColumns([].concat(rowFields, pickKeys)); + setPivotDateColumns([rowFields, pickKeys]); + resetItemFilter(); + }; + + // 行列的值选项 + const [rowsItemValues, setRowsItemValues] = useState(); + const [columnsItemValues, setColumnsItemValues] = useState(); + const [rowsFilter, setRowsFilter] = useState(); + const [columnsFilter, setColumnsFilter] = useState(); + const resetItemFilter = () => { + setRowsItemValues(null); + setColumnsItemValues(null); + setRowsFilter(null); + setColumnsFilter(null); + }; + const handleFieldsItemPick = (v, columnsIndex, columnsName, actionSeries) => { + const _curFilter = { [columnsName]: actionSeries === 'row' ? v : [v] }; + // console.log('handleFieldsItemPick', v, columnsIndex, columnsName, actionSeries, _curFilter); + const _rowsF = actionSeries === 'row' ? { ...rowsFilter, ..._curFilter } : rowsFilter; + const _columnsF = actionSeries === 'col' ? { ...columnsFilter, ..._curFilter } : columnsFilter; + actionSeries === 'row' ? setRowsFilter(_rowsF) : setColumnsFilter(_columnsF); + const currentFilterMerge = { + rows: _rowsF || {}, + columns: _columnsF || {}, + }; + setRowsItemValues(currentFilterMerge.rows); + setColumnsItemValues(currentFilterMerge.columns); + + const rowsFilterFields = Object.keys(currentFilterMerge.rows).filter((ele) => currentFilterMerge.rows[ele].length); + const dataMappedByRows = groupBy(dataBeforePick, (row) => rowsFilterFields.map((kk) => `${row[kk]}`).join('=@=')); + const rowsFilterKey = isEmpty(rowsFilterFields) + ? [] + : combineArrays( + Object.values(currentFilterMerge.rows) + .map((kv) => kv.map((kf) => kf.key)) + .filter((s) => s.length), + '=@=' + ); + const afterRowsFilter = isEmpty(rowsFilterFields) ? dataBeforePick : rowsFilterKey.reduce((r, _key) => r.concat(dataMappedByRows[_key]), []); + // console.log('afterRowsFilter', rowsFilterFields, afterRowsFilter); + + const columnsFilterFields = Object.keys(currentFilterMerge.columns).filter((ele) => currentFilterMerge.rows[ele].length); + const allFilterValues = [].concat( + rowsFilterFields.reduce((r, v) => r.concat(currentFilterMerge.rows[v]), []), + columnsFilterFields.reduce((r, v) => r.concat(currentFilterMerge.columns[v]), []) + ); + + const dataMapped = groupBy(afterRowsFilter, (row) => row[columnsName]); + const pickData = isEmpty(v) ? afterRowsFilter : Array.isArray(v) ? v.reduce((r, v) => r.concat(dataMapped[v.value]), []) : dataMapped[v.value]; + + setDataSource(allFilterValues.length === 0 ? dataBeforePick : pickData.sort(sortBy('applyDate'))); + resetX(); + }; + + // 日月年切换 debug: raw data action + const [lineChartX, setLineChartX] = useState('day'); + const [avgLine1, setAvgLine1] = useState(0); + const orderCountDataMapper = { data1: 'data1', data2: undefined }; + const orderCountDataFieldMapper = { 'dateKey': 'applyDate', 'valueKey': 'SumOrder', 'seriesKey': 'rowKey', _f: 'sum' }; + const resetX = () => { + setLineChartX('day'); + setAvgLine1(0); + }; + + const onChangeXDateFieldGroup = (value, data, avg1) => { + const { xField, yField, seriesField } = lineConfig; + const groupByDate = data.reduce((r, v) => { + (r[v[xField]] || (r[v[xField]] = [])).push(v); + return r; + }, {}); + const _data = Object.keys(groupByDate).reduce((r, _d) => { + const xAxisGroup = groupByDate[_d].reduce((a, v) => { + (a[v[seriesField]] || (a[v[seriesField]] = [])).push(v); + return a; + }, {}); + 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; + }, []); + // .map((row) => ({ [xField]: row[xField], [yField]: row[yField], [seriesField]: row[seriesField], rowX: row.dateRange[0] })); + + setLineChartX(value); + setDataSource(_data); + setAvgLine1(avg1); + }; + + const targetTableProps = { + loading: false, + // sticky: true, + scroll: { x: 1000, y: 400 }, + pagination: false, + columns: [ + ...pivotDateColumns[0].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, dataIndex: ele, width: '6em', fixed: 'left' })), + // { key: 'groupsLabel', title: '', dataIndex: 'groupsLabel' }, + { key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' }, + ...pivotDateColumns[1].map((ele) => ({ + key: ele, + title: filterFieldsMapped[ele].label, + align: 'left', + children: cloneDeep(pivotDateColumnsValues) + .slice(-1)[0] + .sort() + .map((col) => ({ key: col, title: col || '(空)', dataIndex: col, width: '6em', render: (_, r) => r.columns[col]?.SumOrder || '' })), + })), + ], + }; + + return ( + <> + +
+ + + + ); +}); From c878167dc9b740b1163d4f1080ab7d930239330c Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 10 Nov 2023 23:16:05 +0800 Subject: [PATCH 35/86] =?UTF-8?q?perf:=20=E9=94=80=E5=94=AE>=E4=B8=9A?= =?UTF-8?q?=E7=BB=A9=E6=95=B0=E6=8D=AE:=20=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/SaleStore.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/stores/SaleStore.js b/src/stores/SaleStore.js index 29a27b9..bba182c 100644 --- a/src/stores/SaleStore.js +++ b/src/stores/SaleStore.js @@ -265,7 +265,7 @@ class SaleStore { sorter: (a, b) => b.WXNewGuestCount - a.WXNewGuestCount, }, ]; - result.dataSource = json.result1; + result.dataSource = json.result1.sort(comm.sortBy('PriceTime')).reverse(); } else if (this.active_tab_key === 'ResponseRateWhatsApp') { result.columns = [ { @@ -341,7 +341,7 @@ class SaleStore { sorter: (a, b) => b.COLI_ConfirmTimeAVG - a.COLI_ConfirmTimeAVG, }, ]; - result.dataSource = json.result1; + result.dataSource = json.result1.sort(comm.sortBy('COLI_ConfirmTimeAVG')).reverse(); } else { const diffDateFlagYes = !comm.isEmpty(date_moment.start_date_cp); // if (this.active_tab_key == "Country") @@ -419,13 +419,17 @@ class SaleStore { vs: comm.fixTo2Decimals(((total_data_value-total_data_value_diff)/total_data_value_diff)*100)+'%', }; result.columns.push({ - title: item.SubTypeName, + title: item.SubTypeName, _val: total_data_value, children: [{ title: diffDateFlagYes ? comm.show_vs_tag(columnDiff.vs, columnDiff.diff, total_data_value, total_data_value_diff) : total_data_value, dataIndex: data_index }], sorter: (a, b) => b[`TV_${item.SubTypeSN}`] - a[`TV_${item.SubTypeSN}`], }); return item; }); - result.dataSource = type_data_arr; + result.columns = [].concat( + comm.cloneDeep(result.columns).slice(0, 2), + comm.cloneDeep(result.columns).slice(2).sort(comm.sortBy('_val')).reverse() + ); + result.dataSource = type_data_arr.sort(comm.sortBy('T_total')).reverse(); } } runInAction(() => { From cfeecf4af58d22a3bcb0e75e71a61f37ae83ddfe Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 13 Nov 2023 11:30:34 +0800 Subject: [PATCH 36/86] =?UTF-8?q?perf:=20=E9=80=8F=E8=A7=86=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.css | 2 +- src/App.jsx | 6 +- src/libs/ht.js | 315 ++++++++++++++----- src/stores/DataPivot.js | 49 +++ src/stores/Index.js | 2 + src/views/{OrdersPivot.jsx => DataPivot.jsx} | 141 +++++---- 6 files changed, 378 insertions(+), 137 deletions(-) create mode 100644 src/stores/DataPivot.js rename src/views/{OrdersPivot.jsx => DataPivot.jsx} (78%) diff --git a/src/App.css b/src/App.css index 1acdfbf..02207ca 100644 --- a/src/App.css +++ b/src/App.css @@ -32,7 +32,7 @@ padding: 0; } .p-s1{ - padding: .5em; + padding: .5rem!important; } .sticky-top{ diff --git a/src/App.jsx b/src/App.jsx index df96778..f76d996 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -42,7 +42,7 @@ import ExchangeRate from './charts/ExchangeRate'; import KPI from './views/KPI'; import Distribution from './views/Distribution'; import Detail from './views/Detail'; -import OrderPivot from './views/OrdersPivot'; +import DataPivot from './views/DataPivot'; import Welcome from './views/Welcome'; import { stores_Context, APP_VERSION } from './config'; import { WaterMark } from '@ant-design/pro-components'; @@ -71,7 +71,7 @@ const App = () => { }, { key: 'orders-pivot', - label: 数据透视, + label: 数据透视, // icon: , }, ], @@ -196,7 +196,7 @@ const App = () => { } /> } /> } /> - } /> + } /> }> } /> diff --git a/src/libs/ht.js b/src/libs/ht.js index a423627..bcf85dc 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -1,4 +1,4 @@ -import { fixTo4Decimals, fixTo1Decimals, fixToInt, groupBy, sortBy, cloneDeep, pick, unique, flush } from "../utils/commons"; +import { fixTo4Decimals, fixTo1Decimals, fixToInt, groupBy, sortBy, cloneDeep, pick, unique, flush } from '../utils/commons'; /** * 事业部 @@ -88,7 +88,7 @@ export const sites = [ { value: '30', key: '30', label: 'TP', code: 'trippest' }, { value: '31', key: '31', label: '花梨鹰', code: 'HLY' }, ]; -export const sitesMappedByCode = sites.reduce((a, c) => ({ ...a, [String(c.code)]: {...c, key: c.code, value: c.code } }), {}); +export const sitesMappedByCode = sites.reduce((a, c) => ({ ...a, [String(c.code)]: { ...c, key: c.code, value: c.code } }), {}); export const dateTypes = [ { key: 'applyDate', value: 'applyDate', label: '提交日期' }, { key: 'ConfirmDate', value: 'ConfirmDate', label: '确认日期' }, @@ -123,11 +123,7 @@ export const dataFieldAlias = dataFieldOptions.reduce( * KPI对象 */ export const KPIObjects = [ - { key: 'overview', value: 'overview', label: '海纳', data: [ - { key: 'ALL', value: 'ALL', label: '海纳' }, - ...overviewGroup - ] - }, + { key: 'overview', value: 'overview', label: '海纳', data: [{ key: 'ALL', value: 'ALL', label: '海纳' }, ...overviewGroup] }, { key: 'bizarea', value: 'bizarea', @@ -174,88 +170,251 @@ export const KPISubjects = [ // { key: 'sum_person_num', value: 'sum_person_num', label: '人数' }, ]; - /** * 数据透视计算 * @param {object[]} data - * @param {any[]} groupby - * @param {object[]} keys - * @param {string} value + * @param {any[]} groupbyKeys * @returns */ -export const pivotBy = (data, [rows, columns, date], keys, value) => { +export const pivotBy = (data, [rows, columns, date]) => { + // console.time('pivot----'); + console.log('pivotBy', [rows, columns, date]); const groupbyKeys = flush([].concat(rows, columns, [date])); - console.log('pivotBy', [rows, columns, date], groupbyKeys ); - const uniqueKeys = groupbyKeys.map(keyField => { - const keyu = [...new Set(data.map(f => f[keyField]))]; - return keyu; - }); - const pivotResult = []; // new Array(uniqueKeys.reduce((r, v) => r * v.length, 1)); - const groupData = groupBy(data, row => groupbyKeys.map(kk => `${row[kk]}`).join('=@=')); - - Object.keys(groupData).map((group_str) => { - const _rowKey = groupData[group_str].map((v) => v.key).join('_'); - const _len = groupData[group_str].length; - const _row = { - ...pick(groupData[group_str][0], groupbyKeys), - ...(groupbyKeys.length < 2 ? { rowKey: '总' } : { rowKey: cloneDeep(groupbyKeys).slice(0, -1).map(_k => groupData[group_str][0][_k]).join("»") }), - key: _rowKey, - SumOrder: _len, - SumPersonNum: groupData[group_str].reduce((r, v) => r + v.personNum, 0), - ConfirmOrder: groupData[group_str].reduce((r, v) => r + (Number(v.orderState) === 1 ? 1 : 0), 0), - transactions: groupData[group_str].reduce((r, v) => r + v.transactions, 0), - SumML: groupData[group_str].reduce((r, v) => r + v.ML, 0), - quotePrice: groupData[group_str].reduce((r, v) => r + v.quotePrice, 0), // todo: quotePrice - tourdays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.tourdays, 0) / _len), - applyDays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.applyDays, 0) / _len), - confirmDays: Math.ceil(groupData[group_str].reduce((r, v) => r + v.confirmDays, 0) / _len), - }; - pivotResult.push({ - ..._row, - ConfirmRates: _row.ConfirmOrder ? fixTo4Decimals(_row.ConfirmOrder / _row.SumOrder) : 0, - OrderValue: _row.SumOrder ? fixToInt(_row.SumML / _row.SumOrder) : 0, - }); - return group_str; - }); - // 列转置 - const rowsData = groupBy(data, row => rows.map(kk => `${row[kk]}`).join('=@=')); - const rowsWithColumns = Object.keys(rowsData).map(rowKey => { - const _colData = groupBy(rowsData[rowKey], crow => columns.map(kk => `${crow[kk]}`).join('=@=')); - const _topRowKey = []; - return Object.keys(_colData).reduce((r, colKey) => { - const _len = _colData[colKey].length; - const _rowKey = _colData[colKey].map((v) => v.key).join('_'); - _topRowKey.push(_rowKey); - const _row = { + const getKeys = (keys) => keys.map((keyField) => [...new Set(data.map((f) => f[keyField]))]); + const [rowsKeys, columnsKeys, dateKeys] = [getKeys(rows), getKeys(columns), getKeys([date])]; + + const calcTradeFields = (dataObj, keepKeys = [], seriesKey = '') => { + const outerKeys = []; + const _keepKeys = [...keepKeys, seriesKey]; + const DataGroupByKeys = {}; + + Object.keys(dataObj).forEach((colKey) => { + const _len = dataObj[colKey].length; + const _rowKey = dataObj[colKey].map((v) => v.key).join('_'); + outerKeys.push(_rowKey); + + const initialData = { + ...pick(dataObj[colKey][0], _keepKeys), + ...(keepKeys.length === 0 + ? { rowLabel: '总' } + : { + rowLabel: cloneDeep(keepKeys) + // .slice(0, -1) + .map((_k) => dataObj[colKey][0][_k]) + .join('»'), + }), + _label: colKey || '(空)', key: _rowKey, + SumOrder: _len, - SumPersonNum: _colData[colKey].reduce((r, v) => r + v.personNum, 0), - ConfirmOrder: _colData[colKey].reduce((r, v) => r + (Number(v.orderState) === 1 ? 1 : 0), 0), - transactions: _colData[colKey].reduce((r, v) => r + v.transactions, 0), - SumML: _colData[colKey].reduce((r, v) => r + v.ML, 0), - quotePrice: _colData[colKey].reduce((r, v) => r + v.quotePrice, 0), // todo: quotePrice - tourdays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.tourdays, 0) / _len), - applyDays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.applyDays, 0) / _len), - confirmDays: Math.ceil(_colData[colKey].reduce((r, v) => r + v.confirmDays, 0) / _len), + + SumPersonNum: 0, + ConfirmOrder: 0, + transactions: 0, + SumML: 0, + quotePrice: 0, + tourdays: 0, + applyDays: 0, + confirmDays: 0, }; + + const calculatedData = dataObj[colKey].reduce((r, v) => { + r.SumPersonNum += v.personNum; + r.ConfirmOrder += Number(v.orderState) === 1 ? 1 : 0; + r.transactions += v.transactions; + r.SumML += v.ML; + r.quotePrice += v.quotePrice; + r.tourdays += v.tourdays; + r.applyDays += v.applyDays; + r.confirmDays += v.confirmDays; + return r; + }, initialData); + + // Calculations + calculatedData.tourdays = Math.ceil(calculatedData.tourdays / _len); + calculatedData.applyDays = Math.ceil(calculatedData.applyDays / _len); + calculatedData.confirmDays = Math.ceil(calculatedData.confirmDays / _len); const _rowCalc = { - ConfirmRates: _row.ConfirmOrder ? fixTo4Decimals(_row.ConfirmOrder / _row.SumOrder) : 0, - OrderValue: _row.SumOrder ? fixToInt(_row.SumML / _row.SumOrder) : 0, + ConfirmRates: calculatedData.ConfirmOrder ? fixTo4Decimals(calculatedData.ConfirmOrder / calculatedData.SumOrder) : 0, + OrderValue: calculatedData.SumOrder ? fixToInt(calculatedData.SumML / calculatedData.SumOrder) : 0, + }; + DataGroupByKeys[colKey] = { ...calculatedData, ..._rowCalc }; + }); + + return { groupByKeys: DataGroupByKeys, key: outerKeys.join('_') }; + }; + + const groupData = groupBy(data, (row) => groupbyKeys.map((kk) => `${row[kk]}`).join('=@=')); + const rowsNcolumnsItems = calcTradeFields(groupData, [...rows, ...columns], date); + const pivotResult = Object.values(rowsNcolumnsItems.groupByKeys); + + const transposeData = (keys, dataProp, [dataKey, colKeys]=[]) => + Object.keys(dataProp) + .map((rowKey) => { + const _colKey = dataKey || 'dataKey'; + const _colData = groupBy(dataProp[rowKey], (crow) => (colKeys || keys).map((kk) => `${crow[kk]}`).join('=@=')); + const _columnsObj = calcTradeFields(_colData); + return { ...pick(dataProp[rowKey][0], keys), [_colKey]: _columnsObj.groupByKeys, key: _columnsObj.key }; + }) + .map((everyR) => { + const _colKey = dataKey || 'dataKey'; + const allColumns = Object.values(everyR[_colKey]).reduce((r, c) => r.concat([c]), []); + const summaryCalc = ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum', 'quotePrice', 'tourdays', 'applyDays', 'confirmDays'].reduce( + (r, skey) => ({ ...r, [skey]: allColumns.reduce((a, c) => a + c[skey], 0) }), + everyR + ); + summaryCalc.tourdays = Math.ceil(summaryCalc.tourdays / allColumns.length); + summaryCalc.applyDays = Math.ceil(summaryCalc.applyDays / allColumns.length); + summaryCalc.confirmDays = Math.ceil(summaryCalc.confirmDays / allColumns.length); + summaryCalc.ConfirmRates = summaryCalc.ConfirmOrder ? fixTo4Decimals(summaryCalc.ConfirmOrder / summaryCalc.SumOrder) : 0; + summaryCalc.OrderValue = summaryCalc.SumOrder ? fixToInt(summaryCalc.SumML / summaryCalc.SumOrder) : 0; + + return { ...everyR, ...summaryCalc }; + }); + + const rowsData = groupBy(data, (row) => rows.map((kk) => `${row[kk]}`).join('=@=')); + const summaryRows = transposeData(rows, rowsData, ['columns', columns]); + + const columnsData = groupBy(data, (row) => columns.map((kk) => `${row[kk]}`).join('=@=')); + const summaryColumns = transposeData(columns, columnsData, ['rows', rows]); + + // console.timeEnd('pivot----'); + return { data: pivotResult, columnValues: [rowsKeys, columnsKeys, dateKeys], summaryRows, summaryColumns }; +}; + +// todo: 优化 pivotBy 速度 +export const pivotBy3 = (data, [rows, columns, date]) => { + console.log('pivotBy', [rows, columns, date]); + console.time('pivot2'); + // const rowKeys = new Set(data.map(row => row[rows[0]])); + const rowKeys = rows.map((keyField) => { + const keyu = new Set(data.map((f) => f[keyField])); + return keyu; + }); + const colKeys = new Set(data.map(row => row[columns[0]])); + const dateKeys = new Set(data.map(row => row[date])); + + const aggregatedData = {}; + + data.forEach(row => { + const rowKey = row[rows[0]] ?? '__total'; + const colKey = row[columns[0]] ?? '__total'; + const dateKey = row[date]; + + if (!aggregatedData[rowKey]) { + aggregatedData[rowKey] = {}; + } + + // if (!aggregatedData[rowKey][colKey]) { + // aggregatedData[rowKey][colKey] = {}; + // } + + if (!aggregatedData[rowKey][colKey]) { + aggregatedData[rowKey][colKey] = { + SumOrder: 0, + // other aggregated fields + SumPersonNum: 0, + ConfirmOrder: 0, + transactions: 0, + SumML: 0, + // ... + quotePrice: 0, + tourdays: 0, + applyDays: 0, + confirmDays: 0, }; - Object.assign(r.columns, {[colKey]: {..._row, ..._rowCalc}}); - // (r.columns || (r.columns = {})).push({[colKey]: {..._row, ..._rowCalc}}); - return {...r, key: _topRowKey.join('_')}; - }, {...pick(rowsData[rowKey][0], rows), columns: {}}); - }).map(everyR => { - const allColumns = Object.values(everyR.columns).reduce((r, c) => r.concat([c]), []); - return ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum'].reduce( - (r, skey) => ({ ...r, - [skey]: allColumns.reduce((a, c) => a + c[skey], 0), - }), - everyR - ); // todo: 其他数据列计算 + + } + + aggregatedData[rowKey][colKey].SumOrder++; + aggregatedData[rowKey][colKey].SumPersonNum += row.personNum; + aggregatedData[rowKey][colKey].ConfirmOrder += Number(row.orderState === 1); + aggregatedData[rowKey][colKey].transactions += row.transactions; + aggregatedData[rowKey][colKey].SumML += row.ML; + // aggregate other fields + }); - // console.log('pivot res', uniqueKeys, rowsWithColumns, pivotResult); - return { data: pivotResult, columnValues: uniqueKeys, summary: rowsWithColumns }; + const summarizedData = []; + + // Generate summary rows + for (const rowKey of rowKeys) { + + const rowAggregations = { + SumOrder: 0, + // other aggregated fields + SumPersonNum: 0, + ConfirmOrder: 0, + transactions: 0, + SumML: 0, + // ... + quotePrice: 0, + tourdays: 0, + applyDays: 0, + confirmDays: 0, + }; + + // Calculate aggregates over colKey + for (const colKey in aggregatedData[rowKey]) { + rowAggregations.SumOrder += aggregatedData[rowKey][colKey].SumOrder; + rowAggregations.SumPersonNum += aggregatedData[rowKey][colKey].SumPersonNum; + rowAggregations.ConfirmOrder += aggregatedData[rowKey][colKey].ConfirmOrder; + rowAggregations.transactions += aggregatedData[rowKey][colKey].transactions; + rowAggregations.SumML += aggregatedData[rowKey][colKey].SumML; + // ...aggregate all other fields + } + + const row = { + [rows[0]]: rowKey, + ...rowAggregations + }; + + summarizedData.push(row); + } + + // Generate summary columns + for (const colKey of colKeys) { + const colAggregations = { + SumOrder: 0, + // other aggregated fields + SumPersonNum: 0, + ConfirmOrder: 0, + transactions: 0, + SumML: 0, + // ... + quotePrice: 0, + tourdays: 0, + applyDays: 0, + confirmDays: 0, + }; + + // Calculate aggregates over rowKey + for (const rowKey in aggregatedData) { + if (aggregatedData[rowKey][colKey]) { + colAggregations.SumOrder += aggregatedData[rowKey][colKey].SumOrder; + colAggregations.SumPersonNum += aggregatedData[rowKey][colKey].SumPersonNum; + colAggregations.ConfirmOrder += aggregatedData[rowKey][colKey].ConfirmOrder; + colAggregations.transactions += aggregatedData[rowKey][colKey].transactions; + colAggregations.SumML += aggregatedData[rowKey][colKey].SumML; + // ...aggregate all other fields + } + } + + const col = { + [columns[0]]: colKey, + ...colAggregations + }; + + summarizedData.push(col); + } + + console.timeEnd('pivot2'); + console.log('pivot2 ddd', aggregatedData); + return { + data: [], // aggregatedData, + columnValues: [rowKeys, colKeys, dateKeys], + summaryRows: summarizedData.filter(r => r[rows[0]]), + summaryColumns: summarizedData.filter(c => c[columns[0]]) + }; + }; diff --git a/src/stores/DataPivot.js b/src/stores/DataPivot.js new file mode 100644 index 0000000..ece3763 --- /dev/null +++ b/src/stores/DataPivot.js @@ -0,0 +1,49 @@ +import { makeAutoObservable, runInAction, toJS } from 'mobx'; +import { fetchJSON } from '../utils/request'; +import { isEmpty, sortBy, pick, merge, fixTo2Decimals, groupBy, sortKeys, fixToInt, cloneDeep } from '../utils/commons'; +import { dataFieldAlias } from './../libs/ht'; + +class Trade { + constructor(rootStore) { + this.rootStore = rootStore; + makeAutoObservable(this); + } + + /** + * 明细 + */ + getDetailData = async (param, page) => { + this.detailData[page] = { loading: true, dataSource: [], originData: [] }; + const json = await fetchJSON('/service-Analyse2/GetTradeApartDetail', param); + if (json.errcode === 0) { + runInAction(() => { + this.detailData[page].loading = false; + this.detailData[page].dataSource = json.result; + this.detailData[page].originData = json.result; + }); + } + return json.result; + }; + + setSearchValues(body) { + this.searchValues = body; + } + + timeLineKey = 'week'; + setTimeLineKey(v) { + this.timeLineKey = v; + } + + resetData = () => { + this.detailData = { + orders: { loading: false, dataSource: [], originData: [] }, + }; + }; + + searchValues = {}; + detailData = { + orders: { loading: false, dataSource: [], originData: [] }, + }; +} + +export default Trade; diff --git a/src/stores/Index.js b/src/stores/Index.js index 48dfba1..947763c 100644 --- a/src/stores/Index.js +++ b/src/stores/Index.js @@ -14,6 +14,7 @@ import TradeStore from "./Trade"; import KPI from "./KPI"; import DictData from "./DictData"; import Distribution from "./Distribution"; +import DataPivot from './DataPivot'; class Index { constructor() { this.dashboard_store = new DashboardStore(this); @@ -31,6 +32,7 @@ class Index { this.KPIStore = new KPI(this); this.DictDataStore = new DictData(this); this.DistributionStore = new Distribution(this); + this.DataPivotStore = new DataPivot(this); makeAutoObservable(this); } diff --git a/src/views/OrdersPivot.jsx b/src/views/DataPivot.jsx similarity index 78% rename from src/views/OrdersPivot.jsx rename to src/views/DataPivot.jsx index 8fe250e..cba838c 100644 --- a/src/views/OrdersPivot.jsx +++ b/src/views/DataPivot.jsx @@ -2,8 +2,8 @@ import { useContext, useEffect, useState } from 'react'; import { observer } from 'mobx-react'; import { useParams } from 'react-router-dom'; import { stores_Context } from '../config'; -import { Row, Col, Spin, Table, Button, Select, Typography, Card } from 'antd'; -import { cloneDeep, groupBy, isEmpty, omit, sortBy } from '../utils/commons'; +import { Row, Col, Spin, Table, Select, Typography, Card, Button } from 'antd'; +import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique } from '../utils/commons'; import { dataFieldAlias, pivotBy } from '../libs/ht'; import SearchForm from '../components/search/SearchForm'; import { Line } from '@ant-design/plots'; @@ -18,26 +18,27 @@ const filterFields = [ { key: 'guestGroupType', label: '客群类别' }, { key: 'travelMotivation', label: '出行动机' }, { key: 'operatorName', label: '顾问' }, + { key: 'WebCode', label: '来源站点' }, // todo: 目的地, 目的地国家, 页面类型[PPC, NL...] ]; const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); +const quickOptions = [ + { label: '国籍×产品', fields: [['country'], ['productType']] }, + { label: '产品×客群', fields: [['productType'], ['guestGroupType']] }, +]; -const combineArrays = (arr, sep = '_') => { - // if (arr.length === 0) return []; - if (arr.length === 1) return arr[0].map((item) => item); - - const output = []; - const head = arr[0]; - const tail = arr.slice(1); - - head.forEach((item) => { - const suffixes = combineArrays(tail, sep); - suffixes.forEach((suffix) => { - output.push(`${item}${sep}${suffix}`); - }); +/** + * 计算笛卡尔积 + */ +const cartesianProductArray = (arr, sep = '_', index = 0, prefix = '') => { + let result = []; + if(index === arr.length){ + return [prefix]; + } + arr[index].forEach(item => { + result = result.concat(cartesianProductArray(arr, sep, index+1, prefix ? `${prefix}${sep}${item}` : `${item}`)); }); - - return output; + return result; }; // 注意TdCell要提到DataTable作用域外声明 @@ -48,23 +49,23 @@ const TdCell = (tdprops) => { }; export default observer((props) => { - const { field } = useParams(); - const { date_picker_store: searchFormStore, orders_store, DistributionStore } = useContext(stores_Context); + const { page } = useParams(); + const { date_picker_store: searchFormStore, orders_store, DistributionStore, DataPivotStore } = useContext(stores_Context); const { formValues, formValuesToSub } = searchFormStore; - // const { curTab, scatterDays, detailData } = DistributionStore; + const { originData } = DataPivotStore.detailData[page]; const [loading, setLoading] = useState(false); - const [rawData, setRawData] = useState([]); + const [rawData, setRawData] = useState(originData || []); const [dataBeforePick, setDataBeforePick] = useState([]); const [dataBeforeXChange, setDataBeforeXChange] = useState([]); const [dataSource, setDataSource] = useState([]); - const [dataSourceMapped, setDataSourceMapped] = useState({}); + // const [dataSourceMapped, setDataSourceMapped] = useState({}); const [pivotTableDataSource, setPivotTableDataSource] = useState([]); + const [pivotTableColumnSummary, setPivotTableColumnSummary] = useState({}); // 列单选, 只有一组结果 - const [pivotColumns, setPivotColumns] = useState([]); const [pivotDateColumns, setPivotDateColumns] = useState([[], []]); - const [pivotDateColumnsValues, setPivotDateColumnsValues] = useState([]); + const [pivotDateColumnsValues, setPivotDateColumnsValues] = useState([[], []]); useEffect(() => { calcDataByDate(); @@ -84,40 +85,50 @@ export default observer((props) => { const detailRefresh = async (obj) => { setLoading(true); - DistributionStore.getDetailData({ + DataPivotStore.getDetailData({ ...(obj || formValuesToSub), - }).then((resData) => { + }, page).then((resData) => { setLoading(false); setRawData(resData); - // const { data, columnValues } = pivotBy(resData, ['country', 'guestGroupType', 'applyDate'], 'personNum'); - // setPivotDateColumns(['applyDate']); calcDataByDate(resData); - // setLineChartX('day'); resetX(); resetItemFilter(); }); }; + const valKey = 'SumOrder'; + const timesKey = 'applyDate'; /** * 走势的数据 * 汇总 */ const calcDataByDate = (_rawData) => { // console.log(';;;;;', pivotDateColumns); - const { data, columnValues, summary } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, ['applyDate'])); - setPivotDateColumnsValues(cloneDeep(columnValues).slice(0, -1)); - setDataBeforePick(data.sort(sortBy('applyDate'))); - setDataSource(data.sort(sortBy('applyDate'))); - - setPivotTableDataSource(summary.sort(sortBy('SumOrder')).reverse()); + const { data, columnValues, summaryRows, summaryColumns } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, [timesKey])); + // console.log('data====', data, '\ncolumnValues', columnValues, '\nsummaryRows', summaryRows, '\nsummaryColumns', summaryColumns); + setDataBeforePick(data.sort(sortBy(timesKey))); + // 折线图数据 + setDataSource(data.sort(sortBy(timesKey))); + // 表格数据 + const sortRowData = cloneDeep(summaryRows).sort(sortBy(valKey)).reverse(); + setPivotTableDataSource(sortRowData); + // 列汇总 + const sortColData = summaryColumns.sort(sortBy(valKey)).reverse(); + const colDataMapped = isEmpty(pivotDateColumns[1]) ? sortColData[0] : sortColData.reduce((r, v) => ({...r, [v[pivotDateColumns[1][0]]]: v}), {}); + setPivotTableColumnSummary(colDataMapped); + // 行列的选项值 + const _r = (pivotDateColumns[0].map(eleR => unique(sortRowData.map(ele => ele[eleR])))); + const _c = (pivotDateColumns[1].map(eleC => unique(sortColData.map(ele => ele[eleC])))); + // console.log('_r', _r, '_c', _c); + setPivotDateColumnsValues([_r, _c, columnValues[2]]); }; const line_config = { // data: dataSource, padding: 'auto', - xField: 'applyDate', - yField: 'SumOrder', - seriesField: 'rowKey', + xField: timesKey, + yField: valKey, + seriesField: 'rowLabel', // xAxis: { // type: 'timeCat', // }, @@ -139,11 +150,21 @@ export default observer((props) => { const [lineConfig, setLineConfig] = useState(cloneDeep(line_config)); // 透视配置:行列选项 - const [leftFields, setLeftFields] = useState(filterFields); + // const [leftFields, setLeftFields] = useState(filterFields); const [rightFields, setRightFields] = useState(filterFields); const [rowFields, setRowFields] = useState([]); const [columnFields, setColumnFields] = useState([]); + const [rowSelection, setRowSelection] = useState(); + const [columnSelection, setColumnSelection] = useState(); + const quickOpt = (i) => { + const { fields: pivotFields } = quickOptions[i]; + const [row, col] = pivotFields; + setRowSelection(Object.values(pick(filterFieldsMapped, row))); + setColumnSelection(filterFieldsMapped[col[0]]); + setPivotDateColumns(pivotFields); + }; + const handleRowsPick = (v) => { const pickKeys = v.map((ele) => ele.key); setRowFields(pickKeys); @@ -196,14 +217,13 @@ export default observer((props) => { const dataMappedByRows = groupBy(dataBeforePick, (row) => rowsFilterFields.map((kk) => `${row[kk]}`).join('=@=')); const rowsFilterKey = isEmpty(rowsFilterFields) ? [] - : combineArrays( + : cartesianProductArray( Object.values(currentFilterMerge.rows) .map((kv) => kv.map((kf) => kf.key)) .filter((s) => s.length), '=@=' ); const afterRowsFilter = isEmpty(rowsFilterFields) ? dataBeforePick : rowsFilterKey.reduce((r, _key) => r.concat(dataMappedByRows[_key]), []); - // console.log('afterRowsFilter', rowsFilterFields, afterRowsFilter); const columnsFilterFields = Object.keys(currentFilterMerge.columns).filter((ele) => currentFilterMerge.rows[ele].length); const allFilterValues = [].concat( @@ -214,15 +234,15 @@ export default observer((props) => { const dataMapped = groupBy(afterRowsFilter, (row) => row[columnsName]); const pickData = isEmpty(v) ? afterRowsFilter : Array.isArray(v) ? v.reduce((r, v) => r.concat(dataMapped[v.value]), []) : dataMapped[v.value]; - setDataSource(allFilterValues.length === 0 ? dataBeforePick : pickData.sort(sortBy('applyDate'))); + setDataSource(allFilterValues.length === 0 ? dataBeforePick : pickData.sort(sortBy(timesKey))); resetX(); }; - // 日月年切换 debug: raw data action + // 日月年切换 const [lineChartX, setLineChartX] = useState('day'); const [avgLine1, setAvgLine1] = useState(0); const orderCountDataMapper = { data1: 'data1', data2: undefined }; - const orderCountDataFieldMapper = { 'dateKey': 'applyDate', 'valueKey': 'SumOrder', 'seriesKey': 'rowKey', _f: 'sum' }; + const orderCountDataFieldMapper = { 'dateKey': timesKey, 'valueKey': valKey, 'seriesKey': 'rowLabel', _f: 'sum' }; const resetX = () => { setLineChartX('day'); setAvgLine1(0); @@ -260,16 +280,17 @@ export default observer((props) => { pagination: false, columns: [ ...pivotDateColumns[0].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, dataIndex: ele, width: '6em', fixed: 'left' })), - // { key: 'groupsLabel', title: '', dataIndex: 'groupsLabel' }, { key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' }, ...pivotDateColumns[1].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, - align: 'left', - children: cloneDeep(pivotDateColumnsValues) - .slice(-1)[0] - .sort() - .map((col) => ({ key: col, title: col || '(空)', dataIndex: col, width: '6em', render: (_, r) => r.columns[col]?.SumOrder || '' })), + align: 'left', className: 'p-s1', + children: cloneDeep(pivotDateColumnsValues[1][0] || []).map((col) => ({ + key: col, + title: `${col || '(空)'}: ${pivotTableColumnSummary[col]?.[valKey]}`, + dataIndex: ['columns', col, valKey], + width: '6em', + })), })), ], }; @@ -299,7 +320,15 @@ export default observer((props) => { {/* extra={} */} - + ( + // + // ))} + > {/* todo: 拖拽的操作 */} @@ -324,6 +353,7 @@ export default observer((props) => { placeholder={`选择`} onChange={(v) => handleRowsPick(v)} // value={sale_store.salesTrade.pickSales} + // value={rowSelection} maxTagCount={2} maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`} allowClear={true} @@ -337,12 +367,12 @@ export default observer((props) => { {rowFields.length > 0 - ? cloneDeep(pivotDateColumnsValues) - .slice(0, rowFields.length) + ? cloneDeep(pivotDateColumnsValues)[0] + // .slice(0, rowFields.length) .map((_colArr, _colIndex) => ( - {filterFieldsMapped[pivotDateColumns[0][_colIndex]].label}: + {filterFieldsMapped[pivotDateColumns[0][_colIndex]]?.label}:
Date: Tue, 14 Nov 2023 10:35:28 +0800 Subject: [PATCH 42/86] =?UTF-8?q?perf:=20=E9=A2=84=E8=AE=BE;=20=E5=AF=BC?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/ht.js | 4 +- src/utils/commons.js | 14 +++++++ src/views/DataPivot.jsx | 83 +++++++++++++++++++++++++---------------- 3 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/libs/ht.js b/src/libs/ht.js index bcf85dc..b79c1c8 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -177,7 +177,7 @@ export const KPISubjects = [ * @returns */ export const pivotBy = (data, [rows, columns, date]) => { - // console.time('pivot----'); + console.time('pivot----'); console.log('pivotBy', [rows, columns, date]); const groupbyKeys = flush([].concat(rows, columns, [date])); const getKeys = (keys) => keys.map((keyField) => [...new Set(data.map((f) => f[keyField]))]); @@ -278,7 +278,7 @@ export const pivotBy = (data, [rows, columns, date]) => { const columnsData = groupBy(data, (row) => columns.map((kk) => `${row[kk]}`).join('=@=')); const summaryColumns = transposeData(columns, columnsData, ['rows', rows]); - // console.timeEnd('pivot----'); + console.timeEnd('pivot----'); return { data: pivotResult, columnValues: [rowsKeys, columnsKeys, dateKeys], summaryRows, summaryColumns }; }; diff --git a/src/utils/commons.js b/src/utils/commons.js index 0073b1c..a2b7c27 100644 --- a/src/utils/commons.js +++ b/src/utils/commons.js @@ -533,3 +533,17 @@ export const getNestedValue = (obj, keyArr) => { return acc && acc[curr]; }, obj); }; + +/** + * 计算笛卡尔积 + */ +export const cartesianProductArray = (arr, sep = '_', index = 0, prefix = '') => { + let result = []; + if(index === arr.length){ + return [prefix]; + } + arr[index].forEach(item => { + result = result.concat(cartesianProductArray(arr, sep, index+1, prefix ? `${prefix}${sep}${item}` : `${item}`)); + }); + return result; +}; diff --git a/src/views/DataPivot.jsx b/src/views/DataPivot.jsx index cba838c..dde0831 100644 --- a/src/views/DataPivot.jsx +++ b/src/views/DataPivot.jsx @@ -2,45 +2,37 @@ import { useContext, useEffect, useState } from 'react'; import { observer } from 'mobx-react'; import { useParams } from 'react-router-dom'; import { stores_Context } from '../config'; -import { Row, Col, Spin, Table, Select, Typography, Card, Button } from 'antd'; -import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique } from '../utils/commons'; +import { Row, Col, Spin, Table, Select, Typography, Card, Button, Space, Divider } from 'antd'; +import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique, cartesianProductArray } from '../utils/commons'; import { dataFieldAlias, pivotBy } from '../libs/ht'; import SearchForm from '../components/search/SearchForm'; import { Line } from '@ant-design/plots'; import DateGroupRadio from '../components/DateGroupRadio'; +import { TableExportBtn } from './../components/Data'; const { Text } = Typography; const filterFields = [ - { key: 'country', label: '国籍' }, { key: 'SourceType', label: '来源类型' }, { key: 'productType', label: '产品类型' }, + { key: 'country', label: '国籍' }, + { key: 'CLI_NO', label: '线路' }, + // { key: 'destination', label: '目的地' }, + { key: 'COLI_LineClass', label: '页面类型' }, { key: 'guestGroupType', label: '客群类别' }, { key: 'travelMotivation', label: '出行动机' }, - { key: 'operatorName', label: '顾问' }, - { key: 'WebCode', label: '来源站点' }, - // todo: 目的地, 目的地国家, 页面类型[PPC, NL...] + // { key: 'operatorName', label: '顾问' }, + // { key: 'WebCode', label: '来源站点' }, + // todo: 目的地, 目的地国家, 页面类型LineClass[PPC, NL...], 线路line, ]; const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); const quickOptions = [ - { label: '国籍×产品', fields: [['country'], ['productType']] }, - { label: '产品×客群', fields: [['productType'], ['guestGroupType']] }, + // { label: '国籍×产品', fields: [['country'], ['productType']] }, + { label: '[ 产品×客群 ]×[ ]', fields: [['productType', 'guestGroupType'], []] }, + { label: '[ 国籍×客群 ]×[ ]', fields: [['country', 'guestGroupType'], []] }, + // { label: '[ 国籍×客群 ]×[ ]', fields: [['country', 'guestGroupType'], []] }, ]; -/** - * 计算笛卡尔积 - */ -const cartesianProductArray = (arr, sep = '_', index = 0, prefix = '') => { - let result = []; - if(index === arr.length){ - return [prefix]; - } - arr[index].forEach(item => { - result = result.concat(cartesianProductArray(arr, sep, index+1, prefix ? `${prefix}${sep}${item}` : `${item}`)); - }); - return result; -}; - // 注意TdCell要提到DataTable作用域外声明 const TdCell = (tdprops) => { // onMouseEnter, onMouseLeave在数据量多的时候,会严重阻塞表格单元格渲染,严重影响性能 @@ -151,21 +143,36 @@ export default observer((props) => { // 透视配置:行列选项 // const [leftFields, setLeftFields] = useState(filterFields); - const [rightFields, setRightFields] = useState(filterFields); + const [rightFields, setRightFields] = useState(filterFields); // select 的option const [rowFields, setRowFields] = useState([]); const [columnFields, setColumnFields] = useState([]); + // 预设的选项 const [rowSelection, setRowSelection] = useState(); const [columnSelection, setColumnSelection] = useState(); const quickOpt = (i) => { const { fields: pivotFields } = quickOptions[i]; const [row, col] = pivotFields; setRowSelection(Object.values(pick(filterFieldsMapped, row))); - setColumnSelection(filterFieldsMapped[col[0]]); + !isEmpty(col) ? setColumnSelection(filterFieldsMapped[col[0]]) : setColumnSelection([]); + setRowFields(row); + resetItemFilter(); + setPivotDateColumns(pivotFields); }; + const resetFields = () => { + // setRowFields([]); + // setColumnFields([]); + setRowSelection([]); + setColumnSelection([]); + resetItemFilter(); + + setPivotDateColumns([[], []]); + }; + const handleRowsPick = (v) => { + setRowSelection(v); const pickKeys = v.map((ele) => ele.key); setRowFields(pickKeys); // const leftFieldsMapped = leftFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); @@ -177,6 +184,7 @@ export default observer((props) => { }; const handleColsPick = (val) => { + setColumnSelection(val); const pickKeys = isEmpty(val) ? [] : Array.isArray(val) ? val.map((ele) => ele.key) : [val.key]; setColumnFields(pickKeys); const afterLeft = Object.values(omit(filterFieldsMapped, rowFields)); @@ -323,11 +331,19 @@ export default observer((props) => { ( - // - // ))} + extra={ + + 预设: + {quickOptions.map((ele, elei) => ( + + ))} + + + } > @@ -352,8 +368,7 @@ export default observer((props) => { style={{ width: '100%' }} placeholder={`选择`} onChange={(v) => handleRowsPick(v)} - // value={sale_store.salesTrade.pickSales} - // value={rowSelection} + value={rowSelection} maxTagCount={2} maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`} allowClear={true} @@ -413,8 +428,7 @@ export default observer((props) => { style={{ width: '100%' }} placeholder={`选择`} onChange={(v) => handleColsPick(v)} - // value={sale_store.salesTrade.pickSales} - // value={columnSelection} + value={columnSelection} maxTagCount={2} maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`} allowClear={true} @@ -494,6 +508,9 @@ export default observer((props) => {

透视汇总表: {dataFieldAlias[lineConfig.yField].label}

+ + +
From 13fad7dfc51caf68b25aac110476178b64909406 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 14 Nov 2023 11:43:59 +0800 Subject: [PATCH 43/86] =?UTF-8?q?fix:=20=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Data.jsx | 6 ++++-- src/views/AgentGroupCount.jsx | 10 ++-------- src/views/DestinationGroupCount.jsx | 2 ++ src/views/Orders.jsx | 10 ++-------- src/views/Sale.jsx | 10 ++-------- 5 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/components/Data.jsx b/src/components/Data.jsx index 6aa12bd..35106ff 100644 --- a/src/components/Data.jsx +++ b/src/components/Data.jsx @@ -35,7 +35,8 @@ export const TableExportBtn = (props) => { const output_name = `${props.label}`; const [columnsMap, setColumnsMap] = useState([]); useEffect(() => { - const flatCols = props.columns.flatMap((v, k) => (v.children ? v.children.map((vc) => ({ ...vc, title: `${v.title}-${vc.title}` })) : v)).filter(c => c.title); + const flatCols = props.columns.flatMap((v, k) => (v.children ? v.children.map((vc) => ({ ...vc, title: `${v.title}-${vc.title || ''}` })) : v)).filter(c => c.title); + // console.log('flatCols', flatCols); setColumnsMap(flatCols); return () => {}; @@ -50,11 +51,12 @@ export const TableExportBtn = (props) => { const itemMapped = columnsMap.reduce((sv, kset) => { const render_val = typeof kset?.render === 'function' ? kset.render('', item) : null; const data_val = kset?.dataIndex ? (Array.isArray(kset.dataIndex) ? getNestedValue(item, kset.dataIndex) : item[kset.dataIndex]) : undefined; - const v = { [kset.title]: render_val || data_val }; + const v = { [kset.title]: data_val || render_val }; return { ...sv, ...v }; }, {}); return itemMapped; }); + // console.log('data', data); const ws = utils.json_to_sheet(data, { header: columnsMap.filter((r) => r.dataIndex).map((r) => r.title) }); const wb = utils.book_new(); utils.book_append_sheet(wb, ws, 'sheet'); diff --git a/src/views/AgentGroupCount.jsx b/src/views/AgentGroupCount.jsx index 07b6e69..3aea2a4 100644 --- a/src/views/AgentGroupCount.jsx +++ b/src/views/AgentGroupCount.jsx @@ -5,6 +5,7 @@ import { observer } from 'mobx-react'; import 'moment/locale/zh-cn'; import { utils, writeFileXLSX } from 'xlsx'; import SearchForm from './../components/search/SearchForm'; +import { TableExportBtn } from './../components/Data'; const AgentGroupCount = () => { const { customerServicesStore, date_picker_store } = useContext(stores_Context); @@ -56,14 +57,7 @@ const AgentGroupCount = () => { scroll={{ x: 1000 }} /> - { - const wb = utils.table_to_book(document.getElementById('agentGroupList').getElementsByTagName('table')[0]); - writeFileXLSX(wb, '地接社团信息.xlsx'); - }} - > - 导出excel - + diff --git a/src/views/DestinationGroupCount.jsx b/src/views/DestinationGroupCount.jsx index 4688ab9..bfda615 100644 --- a/src/views/DestinationGroupCount.jsx +++ b/src/views/DestinationGroupCount.jsx @@ -5,6 +5,7 @@ import { observer } from 'mobx-react'; import 'moment/locale/zh-cn'; import { utils, writeFileXLSX } from 'xlsx'; import SearchForm from './../components/search/SearchForm'; +import { TableExportBtn } from './../components/Data'; const DestinationGroupCount = () => { const { customerServicesStore, date_picker_store } = useContext(stores_Context); @@ -62,6 +63,7 @@ const DestinationGroupCount = () => { > 导出excel + diff --git a/src/views/Orders.jsx b/src/views/Orders.jsx index 01b3f32..d9d8241 100644 --- a/src/views/Orders.jsx +++ b/src/views/Orders.jsx @@ -10,6 +10,7 @@ import * as comm from "../utils/commons"; import { utils, writeFileXLSX } from "xlsx"; import DateGroupRadio from '../components/DateGroupRadio'; import SearchForm from './../components/search/SearchForm'; +import { TableExportBtn } from './../components/Data'; class Orders extends Component { static contextType = stores_Context; @@ -446,14 +447,7 @@ class Orders extends Component { <>
- { - const wb = utils.table_to_book(document.getElementById(`table_to_xlsx_${ele.key}`).getElementsByTagName('table')[0]); - writeFileXLSX(wb, `${ele.key}.xlsx`); - }} - > - 导出excel - + ), diff --git a/src/views/Sale.jsx b/src/views/Sale.jsx index 8c1c564..ed83323 100644 --- a/src/views/Sale.jsx +++ b/src/views/Sale.jsx @@ -7,6 +7,7 @@ import { observer } from 'mobx-react'; import * as comm from '../utils/commons'; import { utils, writeFileXLSX } from 'xlsx'; import SearchForm from './../components/search/SearchForm'; +import { TableExportBtn } from './../components/Data'; const Sale = () => { const { sale_store, date_picker_store } = useContext(stores_Context); @@ -291,14 +292,7 @@ const Sale = () => { scroll={{ x: (100*(tableColumns.length)) }} /> - { - const wb = utils.table_to_book(document.getElementById('table_to_xlsx_sale').getElementsByTagName('table')[0]); - writeFileXLSX(wb, 'sale.xlsx'); - }} - > - 导出excel - + From f6e6dc4c987d4ae9b364d04a175feaae7702cf95 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 14 Nov 2023 11:53:32 +0800 Subject: [PATCH 44/86] 2.3.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1c4a9e1..2cfd363 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.3.1", + "version": "2.3.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.3.1", + "version": "2.3.2", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index ed74cc7..b7f6dc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.3.1", + "version": "2.3.2", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From 168f63e2d9f2064ce70067738114ef3225f30cbf Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 14 Nov 2023 16:58:12 +0800 Subject: [PATCH 45/86] =?UTF-8?q?perf:=20=E8=AE=A2=E5=8D=95=E9=80=8F?= =?UTF-8?q?=E8=A7=86:=20=E5=A2=9E=E5=8A=A0:=20=E6=B8=A0=E9=81=93=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B,=E7=BA=BF=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/DataPivot.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/views/DataPivot.jsx b/src/views/DataPivot.jsx index cba838c..71f04c5 100644 --- a/src/views/DataPivot.jsx +++ b/src/views/DataPivot.jsx @@ -12,14 +12,17 @@ import DateGroupRadio from '../components/DateGroupRadio'; const { Text } = Typography; const filterFields = [ - { key: 'country', label: '国籍' }, { key: 'SourceType', label: '来源类型' }, { key: 'productType', label: '产品类型' }, + { key: 'country', label: '国籍' }, + { key: 'CLI_NO', label: '线路' }, + // { key: 'destination', label: '目的地' }, + { key: 'COLI_LineClass', label: '页面渠道' }, { key: 'guestGroupType', label: '客群类别' }, - { key: 'travelMotivation', label: '出行动机' }, - { key: 'operatorName', label: '顾问' }, - { key: 'WebCode', label: '来源站点' }, - // todo: 目的地, 目的地国家, 页面类型[PPC, NL...] + { key: 'travelMotivation', label: '出行目的' }, + // { key: 'operatorName', label: '顾问' }, + // { key: 'WebCode', label: '来源站点' }, + // todo: 目的地, 目的地国家, 页面类型LineClass[PPC, NL...], 线路line, ]; const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); const quickOptions = [ From 24a85d9b386504afa086f6547609be75474c79f7 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 14 Nov 2023 11:39:57 +0800 Subject: [PATCH 46/86] =?UTF-8?q?feat:=20=E4=B8=9A=E7=BB=A9=E9=80=8F?= =?UTF-8?q?=E8=A7=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 2 + src/components/Data.jsx | 4 +- src/components/DateGroupRadio/date.js | 19 ++++-- src/libs/ht.js | 32 +++++++--- src/stores/DataPivot.js | 2 + src/stores/DatePickerStore.js | 4 +- src/stores/SaleStore.js | 2 +- src/views/DataPivot.jsx | 90 +++++++++++++++++++-------- 8 files changed, 109 insertions(+), 46 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index f76d996..08d1f63 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -84,6 +84,7 @@ const App = () => { { key: 51, label: 业绩数据 }, { key: 52, label: 销售进度 }, { key: 'distribution', label: 统计分布 }, + { key: 'trade-pivot', label: 数据透视 }, ], }, { @@ -218,6 +219,7 @@ const App = () => { } /> } /> } /> + } /> diff --git a/src/components/Data.jsx b/src/components/Data.jsx index 6aa12bd..ed5e596 100644 --- a/src/components/Data.jsx +++ b/src/components/Data.jsx @@ -36,6 +36,7 @@ export const TableExportBtn = (props) => { const [columnsMap, setColumnsMap] = useState([]); useEffect(() => { const flatCols = props.columns.flatMap((v, k) => (v.children ? v.children.map((vc) => ({ ...vc, title: `${v.title}-${vc.title}` })) : v)).filter(c => c.title); + // console.log('flatCols', flatCols); setColumnsMap(flatCols); return () => {}; @@ -50,11 +51,12 @@ export const TableExportBtn = (props) => { const itemMapped = columnsMap.reduce((sv, kset) => { const render_val = typeof kset?.render === 'function' ? kset.render('', item) : null; const data_val = kset?.dataIndex ? (Array.isArray(kset.dataIndex) ? getNestedValue(item, kset.dataIndex) : item[kset.dataIndex]) : undefined; - const v = { [kset.title]: render_val || data_val }; + const v = { [kset.title]: data_val || render_val }; return { ...sv, ...v }; }, {}); return itemMapped; }); + // console.log('data', data); const ws = utils.json_to_sheet(data, { header: columnsMap.filter((r) => r.dataIndex).map((r) => r.title) }); const wb = utils.book_new(); utils.book_append_sheet(wb, ws, 'sheet'); diff --git a/src/components/DateGroupRadio/date.js b/src/components/DateGroupRadio/date.js index b413cd2..9bd9d15 100644 --- a/src/components/DateGroupRadio/date.js +++ b/src/components/DateGroupRadio/date.js @@ -1,5 +1,5 @@ import moment from 'moment'; -import { groupBy } from '../../utils/commons'; +import { fixTo2Decimals, groupBy } from '../../utils/commons'; export const datePartOptions = [ { label: '日', value: 'day' }, @@ -83,7 +83,7 @@ export const parseDateType = (data, dateType = 'day', { dateKey, valueKey, serie const dateRange = [min, max]; const summaryVal = everySeries[key].reduce((rows, row) => rows + row[valueKey], 0); const retValue = _f === 'sum' ? summaryVal : _calcF(summaryVal, everySeries[key].length); - a.push({ groupKey: key, value: retValue, dateKey: dateRangeStr, dateRange, containDate, [seriesKey]: _seriesKey, [dateKey]: _dateKey }); + a.push({ groupKey: key, value: fixTo2Decimals(retValue), dateKey: dateRangeStr, dateRange, containDate, [seriesKey]: _seriesKey, [dateKey]: _dateKey }); return a; }, []); const avgDiv = [...new Set(dateArr)].length; @@ -108,7 +108,7 @@ export const resultDataCb = (dataRaw, dateGroup, { data1, data2 }, fieldMapper, const _data2 = data2 ? dataRaw[data2] : []; const parse1 = parseDateType(_data1, dateGroup, fieldMapper); const parseData1 = parse1.data.map((ele) => ({ - [fieldMapper.dateKey]: ele[fieldMapper.dateKey], + [fieldMapper.dateKey]: ele[fieldMapper.dateKey] === 'Invalid date' ? '空日期' : ele[fieldMapper.dateKey], [fieldMapper.valueKey]: ele.value, [fieldMapper.seriesKey]: ele[fieldMapper.seriesKey], groups: _data1[0].groups, @@ -118,7 +118,7 @@ export const resultDataCb = (dataRaw, dateGroup, { data1, data2 }, fieldMapper, })); const parse2 = parseDateType(_data2, dateGroup, fieldMapper); const parseData2 = parse2.data.map((ele) => ({ - [fieldMapper.dateKey]: ele[fieldMapper.dateKey], + [fieldMapper.dateKey]: ele[fieldMapper.dateKey] === 'Invalid date' ? '空日期' : ele[fieldMapper.dateKey], // [fieldMapper.dateKey]: ele.groupKey, [fieldMapper.valueKey]: ele.value, [fieldMapper.seriesKey]: ele[fieldMapper.seriesKey], @@ -151,8 +151,15 @@ export const resultDataCb = (dataRaw, dateGroup, { data1, data2 }, fieldMapper, [fieldMapper.dateKey]: keyMapped[ele[fieldMapper.dateKey]], dateKey: ele.dateKey, })); - const retData = [].concat(parseData1, reindexData2 ).map(ele => ({...ele, [fieldMapper.dateKey]: data1KeyMappedStr[ele[fieldMapper.dateKey]] || data2KeyMappedStr[ele[fieldMapper.dateKey]]})); + const retData = [].concat(parseData1, reindexData2 ).map(ele => ({...ele, + [fieldMapper.dateKey]: data1KeyMappedStr[ele[fieldMapper.dateKey]] === 'Invalid date' ? '空日期' : (data1KeyMappedStr[ele[fieldMapper.dateKey]] || data2KeyMappedStr[ele[fieldMapper.dateKey]])})); const avg1 = parse1.avgVal; - // console.log('callback', dateGroup, retData, avg1, parse2.avgVal, data1KeyMappedStr, data2KeyMappedStr); + // console.log('callback','\ndateGroup', dateGroup, + // '\nretData', retData, + // '\navg1', avg1, + // '\nparse2', parse2.avgVal, + // '\ndata1KeyMappedStr', data1KeyMappedStr, + // '\ndata2KeyMappedStr', data2KeyMappedStr + // ); cb(dateGroup, retData, avg1, parse2.avgVal); }; diff --git a/src/libs/ht.js b/src/libs/ht.js index b79c1c8..5f64cfd 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -1,4 +1,4 @@ -import { fixTo4Decimals, fixTo1Decimals, fixToInt, groupBy, sortBy, cloneDeep, pick, unique, flush } from '../utils/commons'; +import { fixTo4Decimals, fixTo1Decimals, fixToInt, groupBy, sortBy, cloneDeep, pick, unique, flush, fixTo2Decimals } from '../utils/commons'; /** * 事业部 @@ -91,7 +91,7 @@ export const sites = [ export const sitesMappedByCode = sites.reduce((a, c) => ({ ...a, [String(c.code)]: { ...c, key: c.code, value: c.code } }), {}); export const dateTypes = [ { key: 'applyDate', value: 'applyDate', label: '提交日期' }, - { key: 'ConfirmDate', value: 'ConfirmDate', label: '确认日期' }, + { key: 'confirmDate', value: 'confirmDate', label: '确认日期' }, { key: 'startDate', value: 'startDate', label: '走团日期' }, ]; @@ -177,11 +177,12 @@ export const KPISubjects = [ * @returns */ export const pivotBy = (data, [rows, columns, date]) => { - console.time('pivot----'); + console.time('pivot3----'); console.log('pivotBy', [rows, columns, date]); const groupbyKeys = flush([].concat(rows, columns, [date])); const getKeys = (keys) => keys.map((keyField) => [...new Set(data.map((f) => f[keyField]))]); - const [rowsKeys, columnsKeys, dateKeys] = [getKeys(rows), getKeys(columns), getKeys([date])]; + const [rowsKeys, columnsKeys, dateKeys] = [getKeys(rows), getKeys(columns), [getKeys([date])[0].filter(s => s)]]; + // console.log('rowsKeys', rowsKeys, 'columnsKeys', columnsKeys, 'dateKeys', dateKeys); const calcTradeFields = (dataObj, keepKeys = [], seriesKey = '') => { const outerKeys = []; @@ -209,9 +210,11 @@ export const pivotBy = (data, [rows, columns, date]) => { SumOrder: _len, SumPersonNum: 0, + ConfirmPersonNum: 0, ConfirmOrder: 0, transactions: 0, SumML: 0, + SumML_txt: '', quotePrice: 0, tourdays: 0, applyDays: 0, @@ -220,9 +223,10 @@ export const pivotBy = (data, [rows, columns, date]) => { const calculatedData = dataObj[colKey].reduce((r, v) => { r.SumPersonNum += v.personNum; + r.ConfirmPersonNum += Number(v.orderState) === 1 ? v.personNum : 0; r.ConfirmOrder += Number(v.orderState) === 1 ? 1 : 0; r.transactions += v.transactions; - r.SumML += v.ML; + r.SumML += Number(v.orderState) === 1 ? v.ML : 0; r.quotePrice += v.quotePrice; r.tourdays += v.tourdays; r.applyDays += v.applyDays; @@ -238,6 +242,13 @@ export const pivotBy = (data, [rows, columns, date]) => { ConfirmRates: calculatedData.ConfirmOrder ? fixTo4Decimals(calculatedData.ConfirmOrder / calculatedData.SumOrder) : 0, OrderValue: calculatedData.SumOrder ? fixToInt(calculatedData.SumML / calculatedData.SumOrder) : 0, }; + // Formatter + calculatedData.transactions = fixTo2Decimals(calculatedData.transactions); + calculatedData.SumML = fixTo2Decimals(calculatedData.SumML); + calculatedData.SumML_txt = dataFieldAlias.SumML.formatter(calculatedData.SumML); + calculatedData.quotePrice = fixTo2Decimals(calculatedData.quotePrice); + calculatedData.ConfirmRates_txt = dataFieldAlias.ConfirmRates.formatter(_rowCalc.ConfirmRates); + DataGroupByKeys[colKey] = { ...calculatedData, ..._rowCalc }; }); @@ -259,16 +270,19 @@ export const pivotBy = (data, [rows, columns, date]) => { .map((everyR) => { const _colKey = dataKey || 'dataKey'; const allColumns = Object.values(everyR[_colKey]).reduce((r, c) => r.concat([c]), []); - const summaryCalc = ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum', 'quotePrice', 'tourdays', 'applyDays', 'confirmDays'].reduce( - (r, skey) => ({ ...r, [skey]: allColumns.reduce((a, c) => a + c[skey], 0) }), + const summaryCalc = ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum', 'ConfirmPersonNum', 'quotePrice', 'tourdays', 'applyDays', 'confirmDays'].reduce( + (r, skey) => ({ ...r, [skey]: allColumns.reduce((a, c) => Number(c.orderState) === 1 ? a : fixTo2Decimals(a + c[skey]), 0) }), everyR ); summaryCalc.tourdays = Math.ceil(summaryCalc.tourdays / allColumns.length); summaryCalc.applyDays = Math.ceil(summaryCalc.applyDays / allColumns.length); summaryCalc.confirmDays = Math.ceil(summaryCalc.confirmDays / allColumns.length); - summaryCalc.ConfirmRates = summaryCalc.ConfirmOrder ? fixTo4Decimals(summaryCalc.ConfirmOrder / summaryCalc.SumOrder) : 0; + summaryCalc.ConfirmRates = summaryCalc.ConfirmOrder ? fixTo2Decimals(summaryCalc.ConfirmOrder / summaryCalc.SumOrder*100) : 0; summaryCalc.OrderValue = summaryCalc.SumOrder ? fixToInt(summaryCalc.SumML / summaryCalc.SumOrder) : 0; + summaryCalc.SumML_txt = dataFieldAlias.SumML.formatter(summaryCalc.SumML); + summaryCalc.ConfirmRates_txt = dataFieldAlias.ConfirmRates.formatter(summaryCalc.ConfirmRates); + return { ...everyR, ...summaryCalc }; }); @@ -278,7 +292,7 @@ export const pivotBy = (data, [rows, columns, date]) => { const columnsData = groupBy(data, (row) => columns.map((kk) => `${row[kk]}`).join('=@=')); const summaryColumns = transposeData(columns, columnsData, ['rows', rows]); - console.timeEnd('pivot----'); + console.timeEnd('pivot3----'); return { data: pivotResult, columnValues: [rowsKeys, columnsKeys, dateKeys], summaryRows, summaryColumns }; }; diff --git a/src/stores/DataPivot.js b/src/stores/DataPivot.js index ece3763..b75ee38 100644 --- a/src/stores/DataPivot.js +++ b/src/stores/DataPivot.js @@ -37,12 +37,14 @@ class Trade { resetData = () => { this.detailData = { orders: { loading: false, dataSource: [], originData: [] }, + trade: { loading: false, dataSource: [], originData: [] }, }; }; searchValues = {}; detailData = { orders: { loading: false, dataSource: [], originData: [] }, + trade: { loading: false, dataSource: [], originData: [] }, }; } diff --git a/src/stores/DatePickerStore.js b/src/stores/DatePickerStore.js index c49ac73..3f11ec1 100644 --- a/src/stores/DatePickerStore.js +++ b/src/stores/DatePickerStore.js @@ -45,7 +45,7 @@ class DatePickerStore { 'DepartmentList': { 'key': 'ALL', 'label': '所有小组' }, 'WebCode': { 'key': 'ALL', 'label': '所有来源' }, 'IncludeTickets': { 'key': '1', 'label': '含门票' }, - 'DateType': { 'key': 'ConfirmDate', 'label': '确认日期' }, + 'DateType': { 'key': 'confirmDate', 'label': '确认日期' }, 'year': this.start_date, // 'months': [moment(), moment()], 'dates': [this.start_date, this.end_date], @@ -55,7 +55,7 @@ class DatePickerStore { DepartmentList: 'ALL', WebCode: 'ALL', IncludeTickets: '1', - DateType: 'ConfirmDate', + DateType: 'confirmDate', Date1: this.start_date.format('YYYY-MM-DD'), Date2: this.end_date.format('YYYY-MM-DD 23:59:59'), }; diff --git a/src/stores/SaleStore.js b/src/stores/SaleStore.js index bba182c..6b6a521 100644 --- a/src/stores/SaleStore.js +++ b/src/stores/SaleStore.js @@ -33,7 +33,7 @@ class SaleStore { date_title = 'date_title'; // 日期段,只用于显示,防止日期选择控件的变化导致页面刷新 searchValues = { - DateType: { key: 'ConfirmDate', label: '确认日期'}, + DateType: { key: 'confirmDate', label: '确认日期'}, WebCode: { key: 'All', label: '所有来源'}, IncludeTickets: { key: '1', label: '含门票'}, DepartmentList: [groupsMappedByCode.GH], diff --git a/src/views/DataPivot.jsx b/src/views/DataPivot.jsx index dde0831..3ef1fae 100644 --- a/src/views/DataPivot.jsx +++ b/src/views/DataPivot.jsx @@ -18,18 +18,20 @@ const filterFields = [ { key: 'country', label: '国籍' }, { key: 'CLI_NO', label: '线路' }, // { key: 'destination', label: '目的地' }, - { key: 'COLI_LineClass', label: '页面类型' }, + { key: 'COLI_LineClass', label: '页面渠道' }, { key: 'guestGroupType', label: '客群类别' }, - { key: 'travelMotivation', label: '出行动机' }, + { key: 'travelMotivation', label: '出行目的' }, // { key: 'operatorName', label: '顾问' }, // { key: 'WebCode', label: '来源站点' }, - // todo: 目的地, 目的地国家, 页面类型LineClass[PPC, NL...], 线路line, + // todo: 目的地, 目的地国家, ]; const filterFieldsMapped = filterFields.reduce((r, v) => ({ ...r, [v.key]: v }), {}); + +/** 预设的选项, 只有行 */ const quickOptions = [ // { label: '国籍×产品', fields: [['country'], ['productType']] }, - { label: '[ 产品×客群 ]×[ ]', fields: [['productType', 'guestGroupType'], []] }, - { label: '[ 国籍×客群 ]×[ ]', fields: [['country', 'guestGroupType'], []] }, + { label: '[ 产品×客群 ]', fields: [['productType', 'guestGroupType'], []] }, + { label: '[ 国籍×客群 ]', fields: [['country', 'guestGroupType'], []] }, // { label: '[ 国籍×客群 ]×[ ]', fields: [['country', 'guestGroupType'], []] }, ]; @@ -40,12 +42,37 @@ const TdCell = (tdprops) => { return {filterFieldsMapped[pivotDateColumns[0][_colIndex]]?.label}: From 7a0b38c8d0f935c6770abba736cd1326276a7ac2 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 15 Nov 2023 13:43:27 +0800 Subject: [PATCH 47/86] =?UTF-8?q?fix:=20pivot=E9=A1=B5=E9=9D=A2=E7=9A=84ke?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/DataPivot.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/views/DataPivot.jsx b/src/views/DataPivot.jsx index 3ef1fae..2d1ed9c 100644 --- a/src/views/DataPivot.jsx +++ b/src/views/DataPivot.jsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useState } from 'react'; import { observer } from 'mobx-react'; -import { useParams } from 'react-router-dom'; +import { useParams, useLocation } from 'react-router-dom'; import { stores_Context } from '../config'; import { Row, Col, Spin, Table, Select, Typography, Card, Button, Space, Divider } from 'antd'; import { cloneDeep, groupBy, isEmpty, omit, pick, sortBy, unique, cartesianProductArray } from '../utils/commons'; @@ -66,6 +66,7 @@ const pageSetting = { export default observer((props) => { const { page } = useParams(); + const { pathname } = useLocation(); const { date_picker_store: searchFormStore, orders_store, DataPivotStore } = useContext(stores_Context); const { formValues, formValuesToSub } = searchFormStore; const { originData } = DataPivotStore.detailData[page]; @@ -339,7 +340,7 @@ export default observer((props) => { }; return ( - <> +
{
; }; +const pageSetting = { + orders: { + xField: 'applyDate', + yField: 'SumOrder', + tableColumns: [ + { key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' }, + // { key: 'ConfirmOrder', title: '成交数', dataIndex: 'ConfirmOrder', width: '5em' }, + // { key: 'ConfirmPersonNum', title: '成交人数', dataIndex: 'ConfirmPersonNum', width: '5em' }, + // { key: 'ConfirmRates', title: '成交率', dataIndex: 'ConfirmRates_txt', width: '5em' }, + // { key: 'SumML', title: '毛利', dataIndex: 'SumML_txt', width: '5em' }, + ], + searchInitial: { DateType: { key: 'applyDate', value: 'applyDate', label: '提交日期' } }, + }, + trade: { + xField: 'confirmDate', + yField: 'SumML', + yFieldAlias: 'SumML_txt', + tableColumns: [{ key: 'SumML', title: '毛利', dataIndex: 'SumML', width: '5em' }], // SumML_txt + searchInitial: { DateType: { key: 'confirmDate', value: 'confirmDate', label: '确认日期' } }, + }, +}; + export default observer((props) => { const { page } = useParams(); - const { date_picker_store: searchFormStore, orders_store, DistributionStore, DataPivotStore } = useContext(stores_Context); + const { date_picker_store: searchFormStore, orders_store, DataPivotStore } = useContext(stores_Context); const { formValues, formValuesToSub } = searchFormStore; const { originData } = DataPivotStore.detailData[page]; + const { xField: defaultDateType, yField: defaultValKey, yFieldAlias, tableColumns, searchInitial } = pageSetting[page]; + const [curXfield, setCurXfield] = useState(defaultDateType); + const [loading, setLoading] = useState(false); const [rawData, setRawData] = useState(originData || []); const [dataBeforePick, setDataBeforePick] = useState([]); @@ -75,6 +102,14 @@ export default observer((props) => { return () => {}; }, [dataSource]); + useEffect(() => { + setCurXfield(formValuesToSub.DateType); + setLineConfig({...lineConfig, xField: formValuesToSub.DateType}); + + return () => {}; + }, [formValues]); + + const detailRefresh = async (obj) => { setLoading(true); DataPivotStore.getDetailData({ @@ -88,24 +123,22 @@ export default observer((props) => { }); }; - const valKey = 'SumOrder'; - const timesKey = 'applyDate'; /** * 走势的数据 * 汇总 */ const calcDataByDate = (_rawData) => { // console.log(';;;;;', pivotDateColumns); - const { data, columnValues, summaryRows, summaryColumns } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, [timesKey])); + const { data, columnValues, summaryRows, summaryColumns } = pivotBy(_rawData || rawData, [].concat(pivotDateColumns, [curXfield])); // console.log('data====', data, '\ncolumnValues', columnValues, '\nsummaryRows', summaryRows, '\nsummaryColumns', summaryColumns); - setDataBeforePick(data.sort(sortBy(timesKey))); + setDataBeforePick(data.sort(sortBy(curXfield))); // 折线图数据 - setDataSource(data.sort(sortBy(timesKey))); + setDataSource(data.sort(sortBy(curXfield))); // 表格数据 - const sortRowData = cloneDeep(summaryRows).sort(sortBy(valKey)).reverse(); + const sortRowData = cloneDeep(summaryRows).sort(sortBy(defaultValKey)).reverse(); setPivotTableDataSource(sortRowData); // 列汇总 - const sortColData = summaryColumns.sort(sortBy(valKey)).reverse(); + const sortColData = summaryColumns.sort(sortBy(defaultValKey)).reverse(); const colDataMapped = isEmpty(pivotDateColumns[1]) ? sortColData[0] : sortColData.reduce((r, v) => ({...r, [v[pivotDateColumns[1][0]]]: v}), {}); setPivotTableColumnSummary(colDataMapped); // 行列的选项值 @@ -118,8 +151,8 @@ export default observer((props) => { const line_config = { // data: dataSource, padding: 'auto', - xField: timesKey, - yField: valKey, + xField: curXfield, + yField: defaultValKey, seriesField: 'rowLabel', // xAxis: { // type: 'timeCat', @@ -128,6 +161,9 @@ export default observer((props) => { min: 0, maxTickInterval: 5, }, + meta: { + ...cloneDeep(dataFieldAlias), + }, // smooth: true, label: {}, // 显示标签 legend: { @@ -138,32 +174,31 @@ export default observer((props) => { itemMarginBottom: 12, // 垂直间距 }, }; - const [lineConfig, setLineConfig] = useState(cloneDeep(line_config)); // 透视配置:行列选项 // const [leftFields, setLeftFields] = useState(filterFields); - const [rightFields, setRightFields] = useState(filterFields); // select 的option + const [rightFields, setRightFields] = useState(filterFields); const [rowFields, setRowFields] = useState([]); const [columnFields, setColumnFields] = useState([]); - - // 预设的选项 const [rowSelection, setRowSelection] = useState(); const [columnSelection, setColumnSelection] = useState(); + // 预设的选项 const quickOpt = (i) => { const { fields: pivotFields } = quickOptions[i]; const [row, col] = pivotFields; setRowSelection(Object.values(pick(filterFieldsMapped, row))); !isEmpty(col) ? setColumnSelection(filterFieldsMapped[col[0]]) : setColumnSelection([]); setRowFields(row); + setColumnFields(col); resetItemFilter(); setPivotDateColumns(pivotFields); }; const resetFields = () => { - // setRowFields([]); - // setColumnFields([]); + setRowFields([]); + setColumnFields([]); setRowSelection([]); setColumnSelection([]); resetItemFilter(); @@ -242,7 +277,7 @@ export default observer((props) => { const dataMapped = groupBy(afterRowsFilter, (row) => row[columnsName]); const pickData = isEmpty(v) ? afterRowsFilter : Array.isArray(v) ? v.reduce((r, v) => r.concat(dataMapped[v.value]), []) : dataMapped[v.value]; - setDataSource(allFilterValues.length === 0 ? dataBeforePick : pickData.sort(sortBy(timesKey))); + setDataSource(allFilterValues.length === 0 ? dataBeforePick : pickData.sort(sortBy(curXfield))); resetX(); }; @@ -250,7 +285,7 @@ export default observer((props) => { const [lineChartX, setLineChartX] = useState('day'); const [avgLine1, setAvgLine1] = useState(0); const orderCountDataMapper = { data1: 'data1', data2: undefined }; - const orderCountDataFieldMapper = { 'dateKey': timesKey, 'valueKey': valKey, 'seriesKey': 'rowLabel', _f: 'sum' }; + const orderCountDataFieldMapper = { 'dateKey': curXfield, 'valueKey': defaultValKey, 'seriesKey': 'rowLabel', _f: 'sum' }; const resetX = () => { setLineChartX('day'); setAvgLine1(0); @@ -288,15 +323,15 @@ export default observer((props) => { pagination: false, columns: [ ...pivotDateColumns[0].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, dataIndex: ele, width: '6em', fixed: 'left' })), - { key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' }, + ...tableColumns, ...pivotDateColumns[1].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, align: 'left', className: 'p-s1', children: cloneDeep(pivotDateColumnsValues[1][0] || []).map((col) => ({ key: col, - title: `${col || '(空)'}: ${pivotTableColumnSummary[col]?.[valKey]}`, - dataIndex: ['columns', col, valKey], + title: `${col || '(空)'}: ${pivotTableColumnSummary[col]?.[defaultValKey]}`, + dataIndex: ['columns', col, defaultValKey || yFieldAlias], width: '6em', })), })), @@ -312,6 +347,7 @@ export default observer((props) => { initialValue: { ...formValues, ...orders_store.searchValues, + ...searchInitial, }, shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'dates'], // 'country' fieldProps: { @@ -385,7 +421,7 @@ export default observer((props) => { ? cloneDeep(pivotDateColumnsValues)[0] // .slice(0, rowFields.length) .map((_colArr, _colIndex) => ( - +
- + ); }); From c9c7638cf807f845e52e27cc030fb4949fab73d6 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 16 Nov 2023 10:23:37 +0800 Subject: [PATCH 48/86] =?UTF-8?q?perf:=20=E7=BB=9F=E8=AE=A1=E5=88=86?= =?UTF-8?q?=E5=B8=83:=20=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Data.jsx | 2 +- src/views/Distribution.jsx | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/components/Data.jsx b/src/components/Data.jsx index 35106ff..5d1904d 100644 --- a/src/components/Data.jsx +++ b/src/components/Data.jsx @@ -35,7 +35,7 @@ export const TableExportBtn = (props) => { const output_name = `${props.label}`; const [columnsMap, setColumnsMap] = useState([]); useEffect(() => { - const flatCols = props.columns.flatMap((v, k) => (v.children ? v.children.map((vc) => ({ ...vc, title: `${v.title}-${vc.title || ''}` })) : v)).filter(c => c.title); + const flatCols = props.columns.flatMap((v, k) => (v.children ? v.children.map((vc) => ({ ...vc, title: `${v?.titleX || v.title}-${vc.title || ''}` })) : v)).filter(c => c.title); // console.log('flatCols', flatCols); setColumnsMap(flatCols); diff --git a/src/views/Distribution.jsx b/src/views/Distribution.jsx index 44dddc6..84d7ec3 100644 --- a/src/views/Distribution.jsx +++ b/src/views/Distribution.jsx @@ -1,12 +1,12 @@ import { useContext, useEffect } from 'react'; import { observer } from 'mobx-react'; import { stores_Context } from '../config'; -import { Row, Col, Spin, Tabs, Table, Space, Typography } from 'antd'; +import { Row, Col, Spin, Tabs, Table, Space, Typography, Divider } from 'antd'; import { RingProgress } from '@ant-design/plots'; import SearchForm from './../components/search/SearchForm'; import { empty } from '../utils/commons'; import { dataFieldAlias } from '../libs/ht'; -import { VSTag } from './../components/Data'; +import { VSTag, TableExportBtn } from './../components/Data'; import MixYnQ from './../components/MixYnQ'; import './kpi.css'; @@ -73,7 +73,7 @@ export default observer(() => { innerRadius: 0.90, }; const columns = [ - { title: '', dataIndex: 'label' }, + { title: '#', dataIndex: 'label' }, { title: '团数', dataIndex: 'ConfirmOrder', @@ -132,36 +132,38 @@ export default observer(() => { }, { title: () => <>
去年同期
{dateStringY}
, + titleX: ['去年同期', dateStringY], // 给导出按钮用 align: 'center', children: [ { title: '团数占比', width: 90, - dataIndex: 'ConfirmOrderPercent', + dataIndex: ['resultToY', 'ConfirmOrderPercent'], // 'ConfirmOrderPercent', render: (v, r) => r.resultToY.ConfirmOrderPercent ? : '-', }, { title: '业绩占比', width: 90, - dataIndex: 'SumMLPercent', + dataIndex: ['resultToY', 'SumMLPercent'], // 'SumMLPercent', render: (v, r) => r.resultToY.SumMLPercent ? : '-', }, ], }, { title: () => <>
上个时间段
{dateStringQ}
, + titleX: ['上个时间段', dateStringY], // 给导出按钮用 align: 'center', children: [ { title: '团数占比', width: 90, - dataIndex: 'ConfirmOrderPercent', + dataIndex: ['resultToQ', 'ConfirmOrderPercent'], // 'ConfirmOrderPercent', render: (v, r) => r.resultToQ.ConfirmOrderPercent ? : '-', }, { title: '业绩占比', width: 90, - dataIndex: 'SumMLPercent', + dataIndex: ['resultToQ', 'SumMLPercent'], // 'SumMLPercent', render: (v, r) => r.resultToQ.SumMLPercent ? : '-', }, ], @@ -205,6 +207,9 @@ export default observer(() => { children: ( + + +
Date: Thu, 16 Nov 2023 11:41:45 +0800 Subject: [PATCH 49/86] =?UTF-8?q?perf:=20=E7=BB=9F=E8=AE=A1=E5=88=86?= =?UTF-8?q?=E5=B8=83:=20=E5=9B=BE=E8=A1=A8=E7=9A=84=E5=9B=BE=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/MixYnQ.jsx | 63 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/src/components/MixYnQ.jsx b/src/components/MixYnQ.jsx index 953d8a0..1a24c28 100644 --- a/src/components/MixYnQ.jsx +++ b/src/components/MixYnQ.jsx @@ -11,6 +11,10 @@ const COLOR_SETS = [ "#E19348", "#F383A2", ]; +const COLOR_SETS2 = [ + "#5B8FF9", + "#61DDAA", + "#65789B",]; /** * 当期数据; 同比; 环比 */ @@ -75,7 +79,58 @@ export default observer((props) => { // return items; // }, }, - legend: {position: 'top',layout: 'horizontal' }, + legend: { + position: 'top', + layout: 'horizontal', + custom: true, + items: [ + ...['当期', '上期', '去年同期'].map((ele, ei) => ({ + name: `${ele} 团数`, + value: `${ele} 团数`, + marker: { + symbol: 'square', + style: { + fill: COLOR_SETS2[ei], + r: 5, + }, + }, + })), + ...['当期', '上期', '去年同期'].map((ele, ei) => ({ + name: `${ele} 业绩`, + value: `${ele} 业绩`, + marker: { + symbol: 'hyphen', + style: { + stroke: COLOR_SETS2[ei], + r: 5, + lineWidth: 2 + }, + }, + })), + ...['环比', '同比'].map((ele, ei) => ({ + name: `团数 ${ele}`, + value: `团数 ${ele}`, + marker: { + symbol: 'square', + style: { + fill: COLOR_SETS[ei], + r: 5, + }, + }, + })), + ...['环比', '同比'].map((ele, ei) => ({ + name: `业绩 ${ele}`, + value: `业绩 ${ele}`, + marker: { + symbol: 'square', + style: { + fill: COLOR_SETS[ei+2], + r: 5, + }, + }, + })), + ], + }, plots: [ { type: 'column', @@ -91,7 +146,7 @@ export default observer((props) => { }), // color: '#b32b19', // color: '#f58269', - legend: {}, + legend: false, // {}, smooth: true, yAxis: { type: 'linear', @@ -118,7 +173,7 @@ export default observer((props) => { yField: 'yField', seriesField: 'yGroup', xAxis: false, - legend: {}, + legend: false, // {}, meta: merge( { ...cloneDeep(dataFieldAlias), @@ -175,7 +230,7 @@ export default observer((props) => { max: 250, tickCount: 4, }, - legend: {}, + legend: false, // {}, color: COLOR_SETS, annotations: diffLine, }, From 5068f3409b44c38e7b09d62186eba8554115527c Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 16 Nov 2023 16:11:54 +0800 Subject: [PATCH 50/86] =?UTF-8?q?feat:=20=E7=BB=BC=E5=90=88=E7=9C=8B?= =?UTF-8?q?=E6=9D=BF:=20=E5=8E=BB=E5=B9=B4=E6=80=BB=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=AF=B9=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/StatisticCard2.jsx | 18 +++++++++++---- src/stores/Trade.js | 38 +++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/components/StatisticCard2.jsx b/src/components/StatisticCard2.jsx index 1216832..7ef05ba 100644 --- a/src/components/StatisticCard2.jsx +++ b/src/components/StatisticCard2.jsx @@ -5,15 +5,16 @@ import { RingProgress, Progress, Bullet } from '@ant-design/plots'; import RcResizeObserver from 'rc-resize-observer'; import { stores_Context } from '../config'; import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons'; -import { Table } from 'antd'; +import { Table, Space } from 'antd'; const { Statistic, Divider } = StatisticCard; export default observer((props) => { - const { icon, traditional, biz, kpiVal, originVal, ...extProps } = props; + const { icon, traditional, biz, kpiVal, originVal, diff, ...extProps } = props; const ValueIcon = props.icon; - const valueStyle = { color: (props?.VSrate || -1) < 0 ? '#3f8600' : '#cf1322' }; - const VSIcon = () => ((props?.VSrate || -1) < 0 ? : ); + const valueStyle = { color: '#3f8600' }; + // const valueStyle = { color: (props?.VSrate || -1) < 0 ? '#3f8600' : '#cf1322' }; + // const VSIcon = () => ((props?.VSrate || -1) < 0 ? : ); // console.log(props, ';;;;'); const [responsive, setResponsive] = useState(false); const showMulti = traditional.value && biz.value; @@ -70,9 +71,16 @@ export default observer((props) => { valueStyle, ...extProps, value: props.valueSuffix ? `${props.value} ${props.valueSuffix}` : props.value, - prefix: , + prefix: , + description: ( + + + {diff.VSrate && 0 ? 'up' : 'down'} />} + + ), }} chart={showMulti ? : false} + footer={null} /> diff --git a/src/stores/Trade.js b/src/stores/Trade.js index 46108ed..48f5c96 100644 --- a/src/stores/Trade.js +++ b/src/stores/Trade.js @@ -1,7 +1,9 @@ import { makeAutoObservable, runInAction, toJS } from 'mobx'; import * as req from '../utils/request'; +import moment from 'moment'; import { isEmpty, sortBy, pick, merge, fixTo2Decimals, groupBy, sortKeys, fixToInt, cloneDeep } from '../utils/commons'; import { dataFieldAlias } from './../libs/ht'; +import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from './../config'; class Trade { constructor(rootStore) { @@ -14,11 +16,15 @@ class Trade { */ async fetchSummaryData(queryData) { this.summaryData.loading = true; - queryData.groupType = queryData?.groupType || 'overview'; - queryData.groupDateType = 'year'; - const multiData = await this.fetchTradeDataAll(cloneDeep(queryData)); + const curQueryData = cloneDeep(queryData); + curQueryData.groupType = curQueryData?.groupType || 'overview'; + curQueryData.groupDateType = 'year'; + 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; - // console.log(JSON.stringify(summary), 'mmmmmmmmmmm'); + const { summary: summary2, traditional: traditional2, biz: biz2 } = multiData.result2; + // console.log(JSON.stringify(summary), 'mmmmmmmmmmm', multiData); const summaryData = { loading: false, dataSource: [ @@ -26,7 +32,8 @@ class Trade { title: '成团',col: 6, value: summary?.[0]?.ConfirmOrder, originVal: (summary?.[0]?.ConfirmOrder || 0), - valueSuffix: summary?.[0]?.ConfirmRates ? ` / ${summary?.[0]?.ConfirmRates} %` : undefined, + valueSuffix: undefined, + // valueSuffix: summary?.[0]?.ConfirmRates ? ` / ${summary?.[0]?.ConfirmRates} %` : undefined, // VSrate: summary?.[0]?.ConfirmOrderrate, KPIrate: summary?.[0]?.[dataFieldAlias.ConfirmOrder.nestkey.p], // hasKPI: !isEmpty(summary?.[0]?.[dataFieldAlias.ConfirmOrder.nestkey.p]), @@ -35,6 +42,11 @@ class Trade { kpiVal: summary?.[0]?.[dataFieldAlias.ConfirmOrder.nestkey.v], traditional: { title: '传统', value: traditional?.[0]?.ConfirmOrder }, biz: { title: '商务', value: biz?.[0]?.ConfirmOrder }, + diff: { + label: summary2?.[0]?.groupDateVal || '对比', + value: summary2?.[0]?.ConfirmOrder || 0, + VSrate: summary2?.[0]?.ConfirmOrder ? fixTo2Decimals((summary?.[0]?.ConfirmOrder - summary2?.[0]?.ConfirmOrder)/summary2?.[0]?.ConfirmOrder*100) : null , + }, }, { title: '毛利',col: 8, @@ -46,6 +58,11 @@ class Trade { kpiVal: summary?.[0]?.[dataFieldAlias.SumML.nestkey.v], traditional: { title: '传统', value: (traditional?.[0]?.SumML || 0) }, biz: { title: '商务', value: (biz?.[0]?.SumML || 0) }, + diff: { + label: summary2?.[0]?.groupDateVal || '对比', + value: dataFieldAlias.SumML.formatter(summary2?.[0]?.SumML || 0), + VSrate: summary2?.[0]?.SumML ? fixTo2Decimals((summary?.[0]?.SumML - summary2?.[0]?.SumML)/summary2?.[0]?.SumML*100) : null , + }, }, { title: '完成率',col: 5, @@ -56,6 +73,11 @@ class Trade { kpiVal: 0 , // summary?.[0]?.[dataFieldAlias.SumML.nestkey.p], traditional: { title: '传统', value: traditional?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0, }, biz: { title: '商务', value: biz?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0, }, + diff: { + label: summary2?.[0]?.groupDateVal || '对比', + value: `${summary2?.[0]?.[dataFieldAlias.SumML.nestkey.p] || '-'}%`, + VSrate: null , + }, }, { title: '人数', col: 5, @@ -68,6 +90,11 @@ class Trade { // kpiVal: summary?.[0]?.[dataFieldAlias.SumPersonNum.nestkey.v], traditional: { title: '传统', value: traditional?.[0]?.SumPersonNum, }, biz: { title: '商务', value: biz?.[0]?.SumPersonNum, }, + diff: { + label: summary2?.[0]?.groupDateVal || '对比', + value: summary2?.[0]?.SumPersonNum, + VSrate: summary2?.[0]?.SumPersonNum ? fixTo2Decimals((summary?.[0]?.SumPersonNum - summary2?.[0]?.SumPersonNum)/summary2?.[0]?.SumPersonNum*100) : null , + }, }, ], }; @@ -275,6 +302,7 @@ class Trade { {key: 'transactions', title: '营收', dataIndex: 'transactions', render: (v) => dataFieldAlias.transactions.formatter(v)}, ], dataSource: [] }; } + export const parseMergeItem = ({traditional, biz}) => { return ['result1', 'result2'].reduce((res, resKey) => { const mergeItem = { From c27690cdd143f47fb58739e326f7c91d9393d7c0 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 16 Nov 2023 15:54:00 +0800 Subject: [PATCH 51/86] =?UTF-8?q?style:=20=E6=9D=83=E9=99=90=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E6=8C=87=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ProtectedRoute.jsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/views/ProtectedRoute.jsx b/src/views/ProtectedRoute.jsx index 76a6813..7641648 100644 --- a/src/views/ProtectedRoute.jsx +++ b/src/views/ProtectedRoute.jsx @@ -41,20 +41,22 @@ const ProtectedRoute = ({ auth }) => { 申请步骤: -
    +
    1. 复制以下信息 {/* */}
    2. -
    3. + {/*
    4. */}
                           {applyInfo}
                         
      -
    5. + {/* */}
    6. - 打开OA审批, +
      • 输入申请信息(姓名等)
      • @@ -64,9 +66,6 @@ const ProtectedRoute = ({ auth }) => { 填入上述复制的信息到 权限内容
      -
    From 30d185eb3ca526adfe654b65e7f192c74a9dd0d0 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 16 Nov 2023 16:18:51 +0800 Subject: [PATCH 52/86] 2.3.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2cfd363..1ecb7cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.3.2", + "version": "2.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.3.2", + "version": "2.3.3", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index b7f6dc2..59cc79e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.3.2", + "version": "2.3.3", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From 01748ecb71c2b258ede7994bbbb5460c230897fa Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 16 Nov 2023 16:47:06 +0800 Subject: [PATCH 53/86] =?UTF-8?q?fix:=20=E5=AE=A2=E8=BF=90=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2:=20=E6=9F=A5=E8=AF=A2=E7=9A=84=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E7=9A=84=E8=B5=8B=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/CustomerStore.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stores/CustomerStore.js b/src/stores/CustomerStore.js index 727b790..2e9be7f 100644 --- a/src/stores/CustomerStore.js +++ b/src/stores/CustomerStore.js @@ -17,9 +17,9 @@ class CustomerStore { const date_picker_store = this.rootStore.date_picker_store; let url = '/service-tourdesign/PotentialCusOrder'; url += '?Website=' + this.potential_data.webcode.toString() + '&DEI_SNList=' + this.potential_data.groups.toString(); - if (this.potential_data.date_type == 'applyDate') { + if (String(this.potential_data.date_type).toLowerCase() === 'applydate') { url += '&ApplydateCheck=1&EntrancedateCheck=0&ConfirmDateCheck=0'; - } else if(this.potential_data.date_type == 'ConfirmDate'){ + } else if(String(this.potential_data.date_type).toLowerCase() === 'confirmdate'){ url += '&ApplydateCheck=0&EntrancedateCheck=0&ConfirmDateCheck=1'; }else { url += '&ApplydateCheck=0&EntrancedateCheck=1&ConfirmDateCheck=0'; @@ -96,9 +96,9 @@ class CustomerStore { 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(); - if (this.regular_data.date_type == 'applyDate') { + if (String(this.regular_data.date_type).toLowerCase() === 'applydate') { url += '&ApplydateCheck=1&EntrancedateCheck=0&ConfirmDateCheck=0'; - } else if(this.regular_data.date_type == 'ConfirmDate'){ + } else if(String(this.regular_data.date_type).toLowerCase() === 'confirmdate'){ url += '&ApplydateCheck=0&EntrancedateCheck=0&ConfirmDateCheck=1'; }else { url += '&ApplydateCheck=0&EntrancedateCheck=1&ConfirmDateCheck=0'; @@ -175,9 +175,9 @@ class CustomerStore { const date_picker_store = this.rootStore.date_picker_store; let url = '/service-tourdesign/RegularCusInChinaOrder'; url += '?Website=' + this.inchina_data.webcode.toString() + '&DEI_SNList=' + this.inchina_data.groups.toString(); - if (this.inchina_data.date_type == 'applyDate') { + if (String(this.inchina_data.date_type).toLowerCase() === 'applydate') { url += '&ApplydateCheck=1&EntrancedateCheck=0&ConfirmDateCheck=0'; - } else if(this.inchina_data.date_type == 'ConfirmDate'){ + } else if(String(this.inchina_data.date_type).toLowerCase() === 'confirmdate'){ url += '&ApplydateCheck=0&EntrancedateCheck=0&ConfirmDateCheck=1'; }else { url += '&ApplydateCheck=0&EntrancedateCheck=1&ConfirmDateCheck=0'; From edbd4f0dd60cb7b285bd34daa69a219c2ef0d961 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 16 Nov 2023 16:51:06 +0800 Subject: [PATCH 54/86] =?UTF-8?q?fix:=20=E7=A7=BB=E5=8A=A8=E6=88=90?= =?UTF-8?q?=E4=BA=A4:=20=E4=B8=8D=E6=9F=A5=E8=AF=A2"=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E6=97=A5=E6=9C=9F"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/charts/MobileDeal.jsx | 2 +- src/components/search/SearchForm.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/charts/MobileDeal.jsx b/src/charts/MobileDeal.jsx index 329adbe..65d440b 100644 --- a/src/charts/MobileDeal.jsx +++ b/src/charts/MobileDeal.jsx @@ -28,7 +28,7 @@ class MobileDeal extends Component { DepartmentList: { show_all: false, mode: 'multiple', col: 24 }, WebCode: { show_all: true }, dates: { hide_vs: true, col: 12 }, - DateType: { col: 6, disabledKeys: ['ConfirmDate'] }, + DateType: { col: 6, disabledKeys: ['confirmDate'] }, }, }} onSubmit={(_err, obj, form, str) => { diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index d86390e..fb212c9 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -380,7 +380,7 @@ function getFields(props) { 'DateType', 99, - + , fieldProps?.DateType?.col || 3 ), From 40c8f37ff8a7aa8af888aedf066f2c53a1536a56 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 16 Nov 2023 16:51:20 +0800 Subject: [PATCH 55/86] 2.3.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ecb7cb..6a99c03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.3.3", + "version": "2.3.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.3.3", + "version": "2.3.4", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index 59cc79e..6345eb8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.3.3", + "version": "2.3.4", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From aa452b2f26fddc6cc8e5ca71da226536ad7f5252 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 17 Nov 2023 10:30:27 +0800 Subject: [PATCH 56/86] =?UTF-8?q?perf:=20=E5=AE=A2=E6=9C=8D>=E5=9C=B0?= =?UTF-8?q?=E6=8E=A5:=20=E9=9A=90=E8=97=8F=E6=8F=90=E4=BA=A4=E6=97=A5?= =?UTF-8?q?=E6=9C=9F;=20fix:=20=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/AgentGroupCount.jsx | 1 + src/views/AgentGroupList.jsx | 1 + src/views/DestinationGroupCount.jsx | 9 +-------- src/views/DestinationGroupList.jsx | 1 + 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/views/AgentGroupCount.jsx b/src/views/AgentGroupCount.jsx index 3aea2a4..b447fd9 100644 --- a/src/views/AgentGroupCount.jsx +++ b/src/views/AgentGroupCount.jsx @@ -33,6 +33,7 @@ const AgentGroupCount = () => { DepartmentList: { show_all: true }, WebCode: { show_all: false, mode: 'multiple' }, dates: { hide_vs: true }, + DateType: { disabledKeys: ['applyDate'] }, }, }} onSubmit={(_err, obj, form) => { diff --git a/src/views/AgentGroupList.jsx b/src/views/AgentGroupList.jsx index 561dbc9..1a23268 100644 --- a/src/views/AgentGroupList.jsx +++ b/src/views/AgentGroupList.jsx @@ -37,6 +37,7 @@ const AgentGroupList = () => { DepartmentList: { show_all: true }, WebCode: { show_all: false, mode: 'multiple' }, dates: { hide_vs: true }, + DateType: { disabledKeys: ['applyDate'] }, }, }} onSubmit={(_err, obj, form, str) => { diff --git a/src/views/DestinationGroupCount.jsx b/src/views/DestinationGroupCount.jsx index bfda615..f9ad7b7 100644 --- a/src/views/DestinationGroupCount.jsx +++ b/src/views/DestinationGroupCount.jsx @@ -31,6 +31,7 @@ const DestinationGroupCount = () => { orderStatus: { show_all: true }, countryArea: { show_all: false }, dates: { hide_vs: true }, + DateType: { disabledKeys: ['applyDate'] }, }, }} onSubmit={(_err, obj, form) => { @@ -55,14 +56,6 @@ const DestinationGroupCount = () => { scroll={{ x: 1000 }} /> - { - const wb = utils.table_to_book(document.getElementById('destinationGroupCount').getElementsByTagName('table')[0]); - writeFileXLSX(wb, '目的地团信息.xlsx'); - }} - > - 导出excel - diff --git a/src/views/DestinationGroupList.jsx b/src/views/DestinationGroupList.jsx index 9174956..7dbf37b 100644 --- a/src/views/DestinationGroupList.jsx +++ b/src/views/DestinationGroupList.jsx @@ -39,6 +39,7 @@ const DestinationGroupList = () => { orderStatus: { show_all: true }, countryArea: { show_all: false }, dates: { hide_vs: true }, + DateType: { disabledKeys: ['applyDate'] }, }, }} onSubmit={(_err, obj, form) => { From 0d3b170f01d4ebf5da633869c59734ccac8f10c5 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 17 Nov 2023 15:09:50 +0800 Subject: [PATCH 57/86] =?UTF-8?q?perf:=20=E6=95=B0=E6=8D=AE=E9=80=8F?= =?UTF-8?q?=E8=A7=86:=20=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE=E9=A1=B9?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/ht.js | 5 +++++ src/views/DataPivot.jsx | 22 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libs/ht.js b/src/libs/ht.js index 5f64cfd..68fb22d 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -219,6 +219,8 @@ export const pivotBy = (data, [rows, columns, date]) => { tourdays: 0, applyDays: 0, confirmDays: 0, + SingleML: 0, + OrderValue: 0, }; const calculatedData = dataObj[colKey].reduce((r, v) => { @@ -241,6 +243,7 @@ export const pivotBy = (data, [rows, columns, date]) => { const _rowCalc = { ConfirmRates: calculatedData.ConfirmOrder ? fixTo4Decimals(calculatedData.ConfirmOrder / calculatedData.SumOrder) : 0, OrderValue: calculatedData.SumOrder ? fixToInt(calculatedData.SumML / calculatedData.SumOrder) : 0, + SingleML: calculatedData.ConfirmOrder ? fixToInt(calculatedData.SumML / calculatedData.ConfirmOrder) : 0, }; // Formatter calculatedData.transactions = fixTo2Decimals(calculatedData.transactions); @@ -248,6 +251,7 @@ export const pivotBy = (data, [rows, columns, date]) => { calculatedData.SumML_txt = dataFieldAlias.SumML.formatter(calculatedData.SumML); calculatedData.quotePrice = fixTo2Decimals(calculatedData.quotePrice); calculatedData.ConfirmRates_txt = dataFieldAlias.ConfirmRates.formatter(_rowCalc.ConfirmRates); + calculatedData.SingleML = fixTo2Decimals(calculatedData.SingleML); DataGroupByKeys[colKey] = { ...calculatedData, ..._rowCalc }; }); @@ -279,6 +283,7 @@ export const pivotBy = (data, [rows, columns, date]) => { summaryCalc.confirmDays = Math.ceil(summaryCalc.confirmDays / allColumns.length); summaryCalc.ConfirmRates = summaryCalc.ConfirmOrder ? fixTo2Decimals(summaryCalc.ConfirmOrder / summaryCalc.SumOrder*100) : 0; summaryCalc.OrderValue = summaryCalc.SumOrder ? fixToInt(summaryCalc.SumML / summaryCalc.SumOrder) : 0; + summaryCalc.SingleML = summaryCalc.ConfirmOrder ? fixTo2Decimals(summaryCalc.SumML / summaryCalc.ConfirmOrder) : 0; summaryCalc.SumML_txt = dataFieldAlias.SumML.formatter(summaryCalc.SumML); summaryCalc.ConfirmRates_txt = dataFieldAlias.ConfirmRates.formatter(summaryCalc.ConfirmRates); diff --git a/src/views/DataPivot.jsx b/src/views/DataPivot.jsx index 2d1ed9c..456dce7 100644 --- a/src/views/DataPivot.jsx +++ b/src/views/DataPivot.jsx @@ -53,13 +53,26 @@ const pageSetting = { // { key: 'ConfirmRates', title: '成交率', dataIndex: 'ConfirmRates_txt', width: '5em' }, // { key: 'SumML', title: '毛利', dataIndex: 'SumML_txt', width: '5em' }, ], + childrenColumns: [ + { key: 'ConfirmOrder', title: '成交数', dataIndex: 'ConfirmOrder', width: '5em' }, + { key: 'ConfirmRates', title: '成交率', dataIndex: 'ConfirmRates_txt', width: '5em' }, + ], searchInitial: { DateType: { key: 'applyDate', value: 'applyDate', label: '提交日期' } }, }, trade: { xField: 'confirmDate', yField: 'SumML', yFieldAlias: 'SumML_txt', - tableColumns: [{ key: 'SumML', title: '毛利', dataIndex: 'SumML', width: '5em' }], // SumML_txt + tableColumns: [ + { key: 'SumML', title: '毛利', dataIndex: 'SumML', width: '5em' }, // SumML_txt + ], + childrenColumns: [ + { key: 'SumOrder', title: '订单数', dataIndex: 'SumOrder', width: '5em' }, + { key: 'ConfirmOrder', title: '成交数', dataIndex: 'ConfirmOrder', width: '5em' }, + { key: 'ConfirmRates', title: '成交率', dataIndex: 'ConfirmRates_txt', width: '5em' }, + { key: 'SingleML', title: '单团毛利', dataIndex: 'SingleML', width: '5em' }, + { key: 'OrderValue', title: '单个订单价值', dataIndex: 'OrderValue', width: '5em' }, + ], searchInitial: { DateType: { key: 'confirmDate', value: 'confirmDate', label: '确认日期' } }, }, }; @@ -71,7 +84,7 @@ export default observer((props) => { const { formValues, formValuesToSub } = searchFormStore; const { originData } = DataPivotStore.detailData[page]; - const { xField: defaultDateType, yField: defaultValKey, yFieldAlias, tableColumns, searchInitial } = pageSetting[page]; + const { xField: defaultDateType, yField: defaultValKey, yFieldAlias, tableColumns, childrenColumns, searchInitial } = pageSetting[page]; const [curXfield, setCurXfield] = useState(defaultDateType); const [loading, setLoading] = useState(false); @@ -324,11 +337,12 @@ export default observer((props) => { pagination: false, columns: [ ...pivotDateColumns[0].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, dataIndex: ele, width: '6em', fixed: 'left' })), - ...tableColumns, + ...(isEmpty(pivotDateColumns[1]) ? [].concat(cloneDeep(tableColumns), childrenColumns) : tableColumns), ...pivotDateColumns[1].map((ele) => ({ key: ele, title: filterFieldsMapped[ele].label, - align: 'left', className: 'p-s1', + align: 'left', + className: 'p-s1', children: cloneDeep(pivotDateColumnsValues[1][0] || []).map((col) => ({ key: col, title: `${col || '(空)'}: ${pivotTableColumnSummary[col]?.[defaultValKey]}`, From 03655b5e5deae59d01d92e7cc3e1f6384b9f6034 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 17 Nov 2023 15:42:58 +0800 Subject: [PATCH 58/86] =?UTF-8?q?style:=20=E6=8A=98=E7=BA=BF=E5=9B=BE:=20?= =?UTF-8?q?=E4=B8=8D=E8=A6=81=E5=B9=B3=E6=BB=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/charts/Orders.jsx | 2 +- src/components/MixYnQ.jsx | 32 ++++++++++++++++++++++++++++---- src/views/Home.jsx | 2 +- src/views/Orders.jsx | 2 +- src/views/Sale_KPI.jsx | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/charts/Orders.jsx b/src/charts/Orders.jsx index 55e6b29..eabb648 100644 --- a/src/charts/Orders.jsx +++ b/src/charts/Orders.jsx @@ -34,7 +34,7 @@ class Orders extends Component { // xAxis: { // type: 'timeCat', // }, - smooth: true, + // smooth: true, legend: { position: 'right-top', title: { diff --git a/src/components/MixYnQ.jsx b/src/components/MixYnQ.jsx index 1a24c28..d990e1d 100644 --- a/src/components/MixYnQ.jsx +++ b/src/components/MixYnQ.jsx @@ -1,5 +1,5 @@ import { observer } from 'mobx-react'; -import { Mix } from '@ant-design/plots'; +import { Mix, getCanvasPattern } from '@ant-design/plots'; import { merge, isEmpty, cloneDeep } from '../utils/commons'; import { dataFieldAlias } from '../libs/ht'; @@ -66,7 +66,18 @@ export default observer((props) => { // lineDash: [2, 2], lineWidth: 0.5, }, - },]; + }, + ]; + + const pattern = (datum, color) => { + return getCanvasPattern({ + type: String(datum.yGroup).includes(' ') ? 'line' : '', + cfg: { + backgroundColor: color, + }, + }); + }; + const MixConfig = { appendPadding: 15, height: 600, @@ -147,7 +158,7 @@ export default observer((props) => { // color: '#b32b19', // color: '#f58269', legend: false, // {}, - smooth: true, + // smooth: true, yAxis: { type: 'linear', tickCount: 4, @@ -162,6 +173,8 @@ export default observer((props) => { }, }, label: false, + color: COLOR_SETS2, + pattern, }, }, { @@ -181,7 +194,7 @@ export default observer((props) => { { yField: dataFieldAlias[yFields[1]] } ), // color: '#1AAF8B', - smooth: true, + // smooth: true, point: { size: 4, shape: 'cicle', @@ -202,6 +215,17 @@ export default observer((props) => { lineWidth: 1, }, }, + lineStyle: (datum) => { + if (String(datum.yGroup).includes(' ')) { + return { + lineDash: [4, 4], + opacity: 0.75, + }; + } + return { + opacity: 1, + }; + }, }, }, { diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 22d87b2..f9b3ecb 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -133,7 +133,7 @@ export default observer(() => { xAxis: { type: 'cat', }, - smooth: true, + // smooth: true, point: { size: 4, shape: 'cicle', diff --git a/src/views/Orders.jsx b/src/views/Orders.jsx index d9d8241..12bae62 100644 --- a/src/views/Orders.jsx +++ b/src/views/Orders.jsx @@ -290,7 +290,7 @@ class Orders extends Component { return ret; }, }, - smooth: true, + // smooth: true, }; const pie_config = { appendPadding: 10, diff --git a/src/views/Sale_KPI.jsx b/src/views/Sale_KPI.jsx index 0c83bb2..5e5800b 100644 --- a/src/views/Sale_KPI.jsx +++ b/src/views/Sale_KPI.jsx @@ -151,7 +151,7 @@ const Sale_KPI = () => { r.push(targetRow, valRow, processRow); return r; }, []); - const lineConfig = { appendPadding: 10, xField: 'groupDateVal', yField: 'SumML', seriesField: 'groupsLabel', isGroup: true, smooth: true, meta: comm.cloneDeep(dataFieldAlias), }; + const lineConfig = { appendPadding: 10, xField: 'groupDateVal', yField: 'SumML', seriesField: 'groupsLabel', isGroup: true, smooth: false, meta: comm.cloneDeep(dataFieldAlias), }; return (
    From 7926ce3efcace9c3c87cb51166130e0bdc51d185 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 22 Nov 2023 09:22:16 +0800 Subject: [PATCH 59/86] 2.3.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a99c03..54c2a25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.3.4", + "version": "2.3.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.3.4", + "version": "2.3.5", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index 6345eb8..b6caa90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.3.4", + "version": "2.3.5", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From 6ea675f4533d6978947fecd2b78ed90fe8b05c1e Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 17 Nov 2023 11:27:27 +0800 Subject: [PATCH 60/86] =?UTF-8?q?feat:=20=E7=9C=8B=E6=9D=BF:=20=E6=80=BB?= =?UTF-8?q?=E9=A2=9D=E5=B9=B4=E4=BB=BD=E5=AF=B9=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/StatisticCard2.jsx | 4 +- src/stores/Trade.js | 102 ++++++++++++++++++------------ 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/components/StatisticCard2.jsx b/src/components/StatisticCard2.jsx index 7ef05ba..cc764ad 100644 --- a/src/components/StatisticCard2.jsx +++ b/src/components/StatisticCard2.jsx @@ -72,12 +72,12 @@ export default observer((props) => { ...extProps, value: props.valueSuffix ? `${props.value} ${props.valueSuffix}` : props.value, prefix: , - description: ( + description: diff ? ( {diff.VSrate && 0 ? 'up' : 'down'} />} - ), + ) : null, }} chart={showMulti ? : false} footer={null} diff --git a/src/stores/Trade.js b/src/stores/Trade.js index 48f5c96..05e3c17 100644 --- a/src/stores/Trade.js +++ b/src/stores/Trade.js @@ -19,8 +19,6 @@ class Trade { const curQueryData = cloneDeep(queryData); curQueryData.groupType = curQueryData?.groupType || 'overview'; curQueryData.groupDateType = 'year'; - 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; @@ -29,9 +27,10 @@ class Trade { loading: false, dataSource: [ { - title: '成团',col: 6, + title: '成团', + col: 6, value: summary?.[0]?.ConfirmOrder, - originVal: (summary?.[0]?.ConfirmOrder || 0), + originVal: summary?.[0]?.ConfirmOrder || 0, valueSuffix: undefined, // valueSuffix: summary?.[0]?.ConfirmRates ? ` / ${summary?.[0]?.ConfirmRates} %` : undefined, // VSrate: summary?.[0]?.ConfirmOrderrate, @@ -42,59 +41,83 @@ class Trade { kpiVal: summary?.[0]?.[dataFieldAlias.ConfirmOrder.nestkey.v], traditional: { title: '传统', value: traditional?.[0]?.ConfirmOrder }, biz: { title: '商务', value: biz?.[0]?.ConfirmOrder }, - diff: { - label: summary2?.[0]?.groupDateVal || '对比', - value: summary2?.[0]?.ConfirmOrder || 0, - VSrate: summary2?.[0]?.ConfirmOrder ? fixTo2Decimals((summary?.[0]?.ConfirmOrder - summary2?.[0]?.ConfirmOrder)/summary2?.[0]?.ConfirmOrder*100) : null , - }, + ...(summary2?.[0] + ? { + diff: { + label: summary2[0]?.groupDateVal || '对比', + value: summary2[0]?.ConfirmOrder || 0, + VSrate: summary2[0]?.ConfirmOrder ? fixTo2Decimals(((summary[0]?.ConfirmOrder - summary2[0]?.ConfirmOrder) / summary2[0]?.ConfirmOrder) * 100) : null, + }, + } + : {}), }, { - title: '毛利',col: 8, - originVal: (summary?.[0]?.SumML || 0), - value: dataFieldAlias.SumML.formatter(summary?.[0]?.SumML || 0) + '=' + dataFieldAlias.SumML.formatter((traditional?.[0]?.SumML || 0)) + '+' + dataFieldAlias.SumML.formatter((biz?.[0]?.SumML || 0)), + title: '毛利', + col: 8, + originVal: summary?.[0]?.SumML || 0, + value: + dataFieldAlias.SumML.formatter(summary?.[0]?.SumML || 0) + + '=' + + dataFieldAlias.SumML.formatter(traditional?.[0]?.SumML || 0) + + '+' + + dataFieldAlias.SumML.formatter(biz?.[0]?.SumML || 0), KPIrate: summary?.[0]?.[dataFieldAlias.SumML.nestkey.p], hasKPI: false, childrenVisible: true, kpiVal: summary?.[0]?.[dataFieldAlias.SumML.nestkey.v], - traditional: { title: '传统', value: (traditional?.[0]?.SumML || 0) }, - biz: { title: '商务', value: (biz?.[0]?.SumML || 0) }, - diff: { - label: summary2?.[0]?.groupDateVal || '对比', - value: dataFieldAlias.SumML.formatter(summary2?.[0]?.SumML || 0), - VSrate: summary2?.[0]?.SumML ? fixTo2Decimals((summary?.[0]?.SumML - summary2?.[0]?.SumML)/summary2?.[0]?.SumML*100) : null , - }, + traditional: { title: '传统', value: traditional?.[0]?.SumML || 0 }, + biz: { title: '商务', value: biz?.[0]?.SumML || 0 }, + ...(summary2?.[0] + ? { + diff: { + label: summary2[0]?.groupDateVal || '对比', + value: dataFieldAlias.SumML.formatter(summary2[0]?.SumML || 0), + VSrate: summary2[0]?.SumML ? fixTo2Decimals(((summary?.[0]?.SumML - summary2[0]?.SumML) / summary2[0]?.SumML) * 100) : null, + }, + } + : {}), }, { - title: '完成率',col: 5, - originVal: (summary?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0), + title: '完成率', + col: 5, + originVal: summary?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0, value: `${summary?.[0]?.[dataFieldAlias.SumML.nestkey.p] || ''}%`, hasKPI: false, childrenVisible: false, - kpiVal: 0 , // summary?.[0]?.[dataFieldAlias.SumML.nestkey.p], - traditional: { title: '传统', value: traditional?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0, }, - biz: { title: '商务', value: biz?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0, }, - diff: { - label: summary2?.[0]?.groupDateVal || '对比', - value: `${summary2?.[0]?.[dataFieldAlias.SumML.nestkey.p] || '-'}%`, - VSrate: null , - }, + kpiVal: 0, // summary?.[0]?.[dataFieldAlias.SumML.nestkey.p], + traditional: { title: '传统', value: traditional?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0 }, + biz: { title: '商务', value: biz?.[0]?.[dataFieldAlias.SumML.nestkey.p] || 0 }, + ...(summary2?.[0] + ? { + diff: { + label: summary2[0]?.groupDateVal || '对比', + value: `${summary2[0]?.[dataFieldAlias.SumML.nestkey.p] || '-'}%`, + VSrate: null, + }, + } + : {}), }, { - title: '人数', col: 5, - originVal: (summary?.[0]?.SumPersonNum || 0), + title: '人数', + col: 5, + originVal: summary?.[0]?.SumPersonNum || 0, value: summary?.[0]?.SumPersonNum, // VSrate: summary?.[0]?.SumPersonNumrate, // KPIrate: summary?.[0]?.[dataFieldAlias.SumPersonNum.nestkey.p], hasKPI: false, // // !isEmpty(summary?.[0]?.[dataFieldAlias.SumPersonNum.nestkey.p]),, childrenVisible: true, // kpiVal: summary?.[0]?.[dataFieldAlias.SumPersonNum.nestkey.v], - traditional: { title: '传统', value: traditional?.[0]?.SumPersonNum, }, - biz: { title: '商务', value: biz?.[0]?.SumPersonNum, }, - diff: { - label: summary2?.[0]?.groupDateVal || '对比', - value: summary2?.[0]?.SumPersonNum, - VSrate: summary2?.[0]?.SumPersonNum ? fixTo2Decimals((summary?.[0]?.SumPersonNum - summary2?.[0]?.SumPersonNum)/summary2?.[0]?.SumPersonNum*100) : null , - }, + traditional: { title: '传统', value: traditional?.[0]?.SumPersonNum }, + biz: { title: '商务', value: biz?.[0]?.SumPersonNum }, + ...(summary2?.[0] + ? { + diff: { + label: summary2[0]?.groupDateVal || '对比', + value: summary2[0]?.SumPersonNum || '', + VSrate: summary2[0]?.SumPersonNum ? fixTo2Decimals(((summary?.[0]?.SumPersonNum - summary2[0]?.SumPersonNum) / summary2[0]?.SumPersonNum) * 100) : null, + }, + } + : {}), }, ], }; @@ -115,7 +138,8 @@ class Trade { Object.assign(queryData, { groupDateType: this.timeLineKey }); const multiData = await this.fetchTradeDataAll(cloneDeep(queryData)); const { traditional, biz } = multiData.result1; - // console.log(biz, 'mmmmmmmm', queryData, multiData); + const { summary: summary2, traditional: traditional2, biz: biz2 } = 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), []); From 9e87278cef771ea243e59f81247d52d3a9db89e1 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 24 Nov 2023 10:23:58 +0800 Subject: [PATCH 61/86] =?UTF-8?q?feat:=20=E7=9C=8B=E6=9D=BF:=20=E5=B9=B4?= =?UTF-8?q?=E5=BA=A6=E5=AF=B9=E6=AF=94;=20=E5=88=87=E6=8D=A2groupType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/LineWithKPI.jsx | 100 ++++++++++++++------ src/components/MixTBWithKPI.jsx | 63 ++++++------- src/components/MixYnQ.jsx | 7 ++ src/components/StatisticCard2.jsx | 2 +- src/components/search/YearPickerCharts.jsx | 2 +- src/stores/Trade.js | 72 +++++++++++++-- src/views/Home.jsx | 101 +++++++++++++++------ src/views/Sale_KPI.jsx | 2 +- 8 files changed, 248 insertions(+), 101 deletions(-) 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 = () => { - + From fd57a7c3bd1fc9513ba95af5ebc3fa741e3e6bdc Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 24 Nov 2023 11:08:37 +0800 Subject: [PATCH 62/86] =?UTF-8?q?=E5=B9=B4=E5=BA=A6=E5=AF=B9=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 54c2a25..b4b6d5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.3.5", + "version": "2.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.3.5", + "version": "2.4.0", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index b6caa90..a986613 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.3.5", + "version": "2.4.0", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From 3f35bbfade27765feb2270f0b1938d8ac2c56daf Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 24 Nov 2023 11:41:53 +0800 Subject: [PATCH 63/86] =?UTF-8?q?perf:=20=E4=B8=9A=E7=BB=A9KPI=E6=8A=98?= =?UTF-8?q?=E7=8E=B0=E5=9B=BE=E7=9A=84=E5=9B=BE=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/LineWithKPI.jsx | 5 ++++- src/views/Home.jsx | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/LineWithKPI.jsx b/src/components/LineWithKPI.jsx index 3eb4294..77191bf 100644 --- a/src/components/LineWithKPI.jsx +++ b/src/components/LineWithKPI.jsx @@ -57,7 +57,10 @@ export default observer((props) => { 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] } }, - })), + })).sort(sortBy('name')), + }, + tooltip: { + customItems: (items) => items.sort(sortBy('name')), }, // annotations: [ // // 低于 0 颜色变化 diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 1ecb837..65a5105 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -54,6 +54,7 @@ export default observer(() => { const groupType = _overviewFlag ? 'overview' : 'dept'; queryData.groupType = groupType; setGroupTypeVal(groupType); + setDiffGroupKey('overview'); TradeStore.resetData(); TradeStore.fetchSummaryData(Object.assign({}, queryData, { groupType })); TradeStore.fetchTradeDataByDate(queryData); @@ -169,7 +170,7 @@ export default observer(() => { }; const [diffGroupKey, setDiffGroupKey] = useState(groupKey); const handleChangeDiffType = ({ target: { value } }) => { - console.log('diffGroupKey', diffGroupKey, value); + // console.log('diffGroupKey', diffGroupKey, value); setDiffGroupKey(value); TradeStore.setGroupKey(value); if (!isEmpty(TradeStore.searchPayloadHome)) { From 72dd9ef590b13aee8ef6a3f6b9fffe0a1507e6cb Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 24 Nov 2023 15:29:50 +0800 Subject: [PATCH 64/86] =?UTF-8?q?style:=20=E5=B9=B4=E5=BA=A6=E5=AF=B9?= =?UTF-8?q?=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/LineWithKPI.jsx | 21 ++++++++++++++------- src/views/Home.jsx | 10 +++++++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/components/LineWithKPI.jsx b/src/components/LineWithKPI.jsx index 77191bf..0a77acc 100644 --- a/src/components/LineWithKPI.jsx +++ b/src/components/LineWithKPI.jsx @@ -52,15 +52,22 @@ export default observer((props) => { }, 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] } }, - })).sort(sortBy('name')), + 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]] || '#5B8FF9', r: 3, lineWidth: 2, color: colorSets[ele] }, + }, + })) + .sort(sortBy('name')), }, tooltip: { - customItems: (items) => items.sort(sortBy('name')), + // title: dataFieldAlias[config.yField]?.alias, + showTitle: true, + customItems: (items) => items.sort(sortBy('name')).map((ele) => ({ ...ele, title: `${ele.title} ${dataFieldAlias[config.yField]?.alias}` })), }, // annotations: [ // // 低于 0 颜色变化 diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 65a5105..3b21614 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -23,6 +23,7 @@ const topSeries = [ { key: 'destination', value: 'destination', label: '目的地', graphVisible: true }, { key: 'GuestGroupType', value: 'GuestGroupType', label: '客群类别', graphVisible: false }, { key: 'country', value: 'country', label: '国籍', graphVisible: true }, + { key: 'webcode', value: 'webcode', label: '站点', graphVisible: false }, ]; const allGroupTypes = [ @@ -228,7 +229,6 @@ export default observer(() => {

{showDiff === false ? '走势' : '对比'}

- {showDiff && } {searchValues.yearDiff && (
+
@@ -305,7 +309,7 @@ export default observer(() => { ) : null )} - +
From 77f9e67c0832c3557acf1acf24d75db8a918cb70 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 24 Nov 2023 15:38:26 +0800 Subject: [PATCH 65/86] 2.4.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4b6d5e..5405e76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.4.0", + "version": "2.4.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.4.0", + "version": "2.4.1", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index a986613..5c7e72e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.4.0", + "version": "2.4.1", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From c7e16af0aa0681a462eac86322d2ecaa0ef70340 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 24 Nov 2023 15:55:57 +0800 Subject: [PATCH 66/86] =?UTF-8?q?style:=20=E5=B9=B4=E5=BA=A6=E5=AF=B9?= =?UTF-8?q?=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Home.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 3b21614..de02378 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -24,6 +24,7 @@ const topSeries = [ { key: 'GuestGroupType', value: 'GuestGroupType', label: '客群类别', graphVisible: false }, { key: 'country', value: 'country', label: '国籍', graphVisible: true }, { key: 'webcode', value: 'webcode', label: '站点', graphVisible: false }, + { key: 'bizarea', value: 'bizarea', label: '国境', graphVisible: false }, ]; const allGroupTypes = [ @@ -231,7 +232,7 @@ export default observer(() => { {searchValues.yearDiff && ( )} From 8847d03fc17d0fb8946095dd4ab84f4153850ef8 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Sun, 26 Nov 2023 09:47:57 +0800 Subject: [PATCH 67/86] =?UTF-8?q?test:=20=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 49 +++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/App.jsx | 10 +++++++-- src/stores/AuthStore.js | 2 +- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5405e76..3837a11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", + "vconsole": "^3.15.1", "web-vitals": "^2.1.4", "xlsx": "https://cdn.sheetjs.com/xlsx-0.18.11/xlsx-0.18.11.tgz" }, @@ -7678,6 +7679,17 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/copy-text-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", + "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/copy-to-clipboard": { "version": "3.3.2", "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", @@ -14865,6 +14877,11 @@ "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" }, + "node_modules/mutation-observer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", + "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==" + }, "node_modules/nano-css": { "version": "5.3.5", "resolved": "https://registry.npmmirror.com/nano-css/-/nano-css-5.3.5.tgz", @@ -20317,6 +20334,17 @@ "node": ">= 0.8" } }, + "node_modules/vconsole": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", + "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "copy-text-to-clipboard": "^3.0.1", + "core-js": "^3.11.0", + "mutation-observer": "^1.0.3" + } + }, "node_modules/viewport-mercator-project": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/viewport-mercator-project/-/viewport-mercator-project-6.2.3.tgz", @@ -27120,6 +27148,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "copy-text-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", + "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==" + }, "copy-to-clipboard": { "version": "3.3.2", "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", @@ -32433,6 +32466,11 @@ "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" }, + "mutation-observer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", + "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==" + }, "nano-css": { "version": "5.3.5", "resolved": "https://registry.npmmirror.com/nano-css/-/nano-css-5.3.5.tgz", @@ -36273,6 +36311,17 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "vconsole": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", + "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", + "requires": { + "@babel/runtime": "^7.17.2", + "copy-text-to-clipboard": "^3.0.1", + "core-js": "^3.11.0", + "mutation-observer": "^1.0.3" + } + }, "viewport-mercator-project": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/viewport-mercator-project/-/viewport-mercator-project-6.2.3.tgz", diff --git a/package.json b/package.json index 5c7e72e..fd3311b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", + "vconsole": "^3.15.1", "web-vitals": "^2.1.4", "xlsx": "https://cdn.sheetjs.com/xlsx-0.18.11/xlsx-0.18.11.tgz" }, diff --git a/src/App.jsx b/src/App.jsx index 08d1f63..e701ee3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -16,7 +16,7 @@ import { BarChartOutlined, CoffeeOutlined, } from '@ant-design/icons'; -import { Layout, Menu, Image, Badge } from 'antd'; +import { Layout, Menu, Image, Badge, } from 'antd'; import { BrowserRouter, Route, Routes, NavLink } from 'react-router-dom'; import Home from './views/Home'; import Dashboard from './views/Dashboard'; @@ -47,6 +47,8 @@ import Welcome from './views/Welcome'; import { stores_Context, APP_VERSION } from './config'; import { WaterMark } from '@ant-design/pro-components'; +import VConsole from 'vconsole'; + const App = () => { const { Content, Footer, Sider } = Layout; const { auth_store } = useContext(stores_Context); @@ -153,6 +155,10 @@ const App = () => { // }, ]; + const callDebug = () => { + const vConsole = new VConsole({ theme: 'dark' }); + auth_store.get_auth(); + }; return ( @@ -234,7 +240,7 @@ const App = () => { v{APP_VERSION}{' '} {' '} - ©2022 Created by IT + ©2022 Created by IT diff --git a/src/stores/AuthStore.js b/src/stores/AuthStore.js index 6c2bf36..a9cfa10 100644 --- a/src/stores/AuthStore.js +++ b/src/stores/AuthStore.js @@ -12,7 +12,7 @@ class AuthStore { } } - auth = process.env.NODE_ENV === 'production' ? [] : ['admin']; // 开发时候用,正式环境留空 + auth = process.env.NODE_ENV === 'production' ? ['admin'] : ['admin']; // 开发时候用,正式环境留空 user = { name: 'loading', userid: '...' }; // 开发时候用,正式环境留空 has_permission(requireds) { From 96b401b8b938be8f5805716f553a92d93e80a326 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Sun, 26 Nov 2023 09:49:46 +0800 Subject: [PATCH 68/86] 2.4.2-beta.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3837a11..590f304 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.4.1", + "version": "2.4.2-beta.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.4.1", + "version": "2.4.2-beta.0", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index fd3311b..d0cc61f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.4.1", + "version": "2.4.2-beta.0", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From a7ecec79d673a41affdd6c2a00335fd0d43cc11b Mon Sep 17 00:00:00 2001 From: Lei OT Date: Sun, 26 Nov 2023 10:08:57 +0800 Subject: [PATCH 69/86] =?UTF-8?q?=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 ++-- package.json | 2 +- src/stores/AuthStore.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 590f304..249e1dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.4.2-beta.0", + "version": "2.4.2-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.4.2-beta.0", + "version": "2.4.2-beta.1", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index d0cc61f..3ca4e7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.4.2-beta.0", + "version": "2.4.2-beta.1", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", diff --git a/src/stores/AuthStore.js b/src/stores/AuthStore.js index a9cfa10..6c2bf36 100644 --- a/src/stores/AuthStore.js +++ b/src/stores/AuthStore.js @@ -12,7 +12,7 @@ class AuthStore { } } - auth = process.env.NODE_ENV === 'production' ? ['admin'] : ['admin']; // 开发时候用,正式环境留空 + auth = process.env.NODE_ENV === 'production' ? [] : ['admin']; // 开发时候用,正式环境留空 user = { name: 'loading', userid: '...' }; // 开发时候用,正式环境留空 has_permission(requireds) { From d90a2cd90d1facf1d8b2da80c9c70f0c71c47c23 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Sun, 26 Nov 2023 10:10:40 +0800 Subject: [PATCH 70/86] 2.4.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 249e1dc..c113897 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "haina-dashboard", - "version": "2.4.2-beta.1", + "version": "2.4.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "haina-dashboard", - "version": "2.4.2-beta.1", + "version": "2.4.2", "dependencies": { "@ant-design/charts": "^1.4.2", "@ant-design/pro-components": "^2.6.16", diff --git a/package.json b/package.json index 3ca4e7d..780bac4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haina-dashboard", - "version": "2.4.2-beta.1", + "version": "2.4.2", "private": true, "dependencies": { "@ant-design/charts": "^1.4.2", From f67fd5da3130eedc47f8f33e48401a965d0e8ab3 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Sun, 26 Nov 2023 10:32:34 +0800 Subject: [PATCH 71/86] =?UTF-8?q?fix:=20=E9=94=80=E5=94=AE=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6:=20=E5=B0=8F=E7=BB=84=E7=9A=84KPI=E6=9B=B2=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Sale_KPI.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/Sale_KPI.jsx b/src/views/Sale_KPI.jsx index b57b6e3..8b29762 100644 --- a/src/views/Sale_KPI.jsx +++ b/src/views/Sale_KPI.jsx @@ -187,7 +187,7 @@ const Sale_KPI = () => { />
- + From e2423c1b85b9504587a59d4db07dda297011bc5b Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 27 Nov 2023 10:52:20 +0800 Subject: [PATCH 72/86] conf: build.js --- build.bat | 2 +- build.js | 6 ++++-- package-lock.json | 49 ----------------------------------------------- package.json | 3 +-- public/index.html | 1 + src/App.jsx | 4 +--- 6 files changed, 8 insertions(+), 57 deletions(-) diff --git a/build.bat b/build.bat index 7a5c155..da7be55 100644 --- a/build.bat +++ b/build.bat @@ -1 +1 @@ -node build.js +npm run build diff --git a/build.js b/build.js index b75686b..eaf88ee 100644 --- a/build.js +++ b/build.js @@ -1,5 +1,7 @@ -const { execSync } = require('child_process'); process.env.REACT_APP_BUILD_TIME = new Date().getTime()+(5*60*1000); -execSync('npm run build', { stdio: 'inherit' }); +require('child_process').execSync( + 'react-scripts build', + { stdio: 'inherit' } +); diff --git a/package-lock.json b/package-lock.json index c113897..cfa5971 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,6 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", - "vconsole": "^3.15.1", "web-vitals": "^2.1.4", "xlsx": "https://cdn.sheetjs.com/xlsx-0.18.11/xlsx-0.18.11.tgz" }, @@ -7679,17 +7678,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/copy-text-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", - "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/copy-to-clipboard": { "version": "3.3.2", "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", @@ -14877,11 +14865,6 @@ "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" }, - "node_modules/mutation-observer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", - "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==" - }, "node_modules/nano-css": { "version": "5.3.5", "resolved": "https://registry.npmmirror.com/nano-css/-/nano-css-5.3.5.tgz", @@ -20334,17 +20317,6 @@ "node": ">= 0.8" } }, - "node_modules/vconsole": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", - "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", - "dependencies": { - "@babel/runtime": "^7.17.2", - "copy-text-to-clipboard": "^3.0.1", - "core-js": "^3.11.0", - "mutation-observer": "^1.0.3" - } - }, "node_modules/viewport-mercator-project": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/viewport-mercator-project/-/viewport-mercator-project-6.2.3.tgz", @@ -27148,11 +27120,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "copy-text-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", - "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==" - }, "copy-to-clipboard": { "version": "3.3.2", "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", @@ -32466,11 +32433,6 @@ "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" }, - "mutation-observer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", - "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==" - }, "nano-css": { "version": "5.3.5", "resolved": "https://registry.npmmirror.com/nano-css/-/nano-css-5.3.5.tgz", @@ -36311,17 +36273,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, - "vconsole": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", - "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", - "requires": { - "@babel/runtime": "^7.17.2", - "copy-text-to-clipboard": "^3.0.1", - "core-js": "^3.11.0", - "mutation-observer": "^1.0.3" - } - }, "viewport-mercator-project": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/viewport-mercator-project/-/viewport-mercator-project-6.2.3.tgz", diff --git a/package.json b/package.json index 780bac4..3d97e98 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,12 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", - "vconsole": "^3.15.1", "web-vitals": "^2.1.4", "xlsx": "https://cdn.sheetjs.com/xlsx-0.18.11/xlsx-0.18.11.tgz" }, "scripts": { "start": "react-scripts start", - "build": "react-scripts build", + "build": "node build.js", "test": "react-scripts test", "eject": "react-scripts eject", "lint": "eslint ./src", diff --git a/public/index.html b/public/index.html index 637312a..76305dc 100644 --- a/public/index.html +++ b/public/index.html @@ -28,6 +28,7 @@ +