diff --git a/src/App.css b/src/App.css
index 678262a..e9e982f 100644
--- a/src/App.css
+++ b/src/App.css
@@ -7,10 +7,24 @@
.align_left{
text-align: left;
}
-
+.align_center{
+ text-align: center;
+}
+.m-1{
+ margin: 1em;
+}
+.mt-1{
+ margin-top: 1em;
+}
.mb-1{
margin-bottom: 1em;
}
+.ml-1{
+ margin-left: 1em;
+}
+.mr-1{
+ margin-right: 1em;
+}
.mb-n1{
margin-bottom: -1em;
}
diff --git a/src/components/kpi/BUPanel.jsx b/src/components/kpi/BUPanel.jsx
deleted file mode 100644
index 0ebdd84..0000000
--- a/src/components/kpi/BUPanel.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useContext } from 'react';
-import { observer } from 'mobx-react';
-// import { stores_Context } from '../config';
-import { Table } from 'antd';
-import KPISettings from './KPISettings';
-import { bu, KPISubjects } from './../../libs/ht';
-
-export default observer((props) => {
- // const { } = useContext(stores_Context);
- const searchProps = {
- // shows: ['DateType', 'years', 'HTBusinessUnits'],
- };
- return (
- <>
-
- >
- );
-});
diff --git a/src/components/kpi/KPISettings.jsx b/src/components/kpi/KPISettings.jsx
index f4c9e65..ecec5d4 100644
--- a/src/components/kpi/KPISettings.jsx
+++ b/src/components/kpi/KPISettings.jsx
@@ -1,161 +1,42 @@
-import { useContext, useState, useEffect } from 'react';
+import { useContext } from 'react';
import { observer } from 'mobx-react';
import { stores_Context } from './../../config';
-import { Button, Table, Switch, Input, Space, Typography, Row, Col, Spin, Radio, Tabs } from 'antd';
-import { EditableProTable, ProCard, ProFormField } from '@ant-design/pro-components';
+import { Typography, Row, Col, Tabs, } from 'antd';
import SearchForm from './../search/SearchForm';
import { bu, KPIObjects, KPISubjects } from './../../libs/ht';
-import { isEmpty, fixTo2Decimals, fixTo4Decimals, cloneDeep, numberFormatter } from './../../utils/commons';
+import { isEmpty, fixTo2Decimals, fixTo4Decimals, cloneDeep, numberFormatter, fixToInt, merge } from './../../utils/commons';
+import ProfitTable from './SubjectTable/Profit';
+
+const Todo = (props) => {
+ return
TODO
;
+};
+
+const subjectComponents = {
+ 'sum_profit': ProfitTable,
+ 'in_order_count': Todo,
+ 'confirm_order_count': Todo,
+ 'depart_order_count': Todo,
+ 'confirm_rates': Todo,
+ 'praise_rates': Todo,
+ 'sum_person_num': Todo,
+};
export const KPIObjectsMapped = KPIObjects.reduce((a, c) => ({ ...a, [String(c.key)]: c }), {});
const { Text } = Typography;
-const numberConvert10K = (number, scale = 10) => {
- return fixTo2Decimals(number / (1000 * scale)) + '万';
-};
export default observer((props) => {
- const { KPIStore, DictDataStore } = useContext(stores_Context);
- const { sort, initialValue, hides, shows, fieldProps } = {
+ const { KPIStore, DictDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
+ const { sort, initialValue, hides, shows, fieldProps: _fieldProps } = {
sort: '',
- initialValue: {},
- fieldProps: {
- years: { hide_vs: true },
- },
+ initialValue: searchFormStore.formValues,
+ fieldProps: {},
hides: [],
shows: ['DateType', 'years'],
...props.searchProps,
};
+ const fieldProps = merge(_fieldProps, { years: { hide_vs: true } });
const { curObject, objects, KPISubjects, onSearchSubmit } = props;
- const curObjectItem = KPIObjectsMapped[curObject];
-console.log(curObjectItem, KPIObjectsMapped, curObject, 'cocococo');
- const [dataSource, setDataSource] = useState([]);
- const { settingYear } = KPIStore;
-
- const [editOpen, setEditOpen] = useState(false); // test:
- const [editableRowsKeys, setEditableRowKeys] = useState([]);
- // console.log(toJS(KPIStore.pageData ), dataSource, '00000');
-
- const PercentInput = ({ value, onChange, record, ...extProps }) => {
- // console.log(extProps, '22222222');
- const initialPercent = record.kpiDataMapped?.[`M${extProps.month}`]?.percentVal;
- const [inputVal, setInputVal] = useState(value);
- const calcV = inputVal ? numberConvert10K(fixTo4Decimals((Number(record?.yearValue) * inputVal) / 100)) : 0;
-
- const handleInputChange = ({ target: { value } }) => {
- setInputVal(value);
- };
- const handleInputConfirm = () => {
- // onChange?.(inputVal);
- // setInputVal('');
- };
- return (
-
-
- {/* onBlur={handleInputConfirm} onPressEnter={handleInputConfirm} */}
- {/* 1 */}
- {calcV}
-
- );
- };
- const RenderInput = (row, mon) => {
- // console.log(toJS(row), mon);
- return (
-
-
- {row.kpiDataMapped?.[`M${mon}`]?.percentVal}
- %
-
- {row.kpiDataMapped?.[`M${mon}`]?.value}
-
- );
- };
- const monthCol = new Array(12).fill(1).map((_, index) => {
- return {
- title: `${index + 1}月`,
- dataIndex: `M${index + 1}Percent`,
- valueType: 'digit',
- width: '6.5em',
- fieldProps: { min: 0, max: 100, style: { width: '4em' } },
- renderFormItem: ({ dataIndex, ...item }, { record, isEditable, ...e }, form) => {
- return ;
- },
- render: (_, row) => RenderInput(row, index + 1),
- };
- });
- const initialRow = monthCol.reduce((r, v) => ({ ...r, [v.dataIndex]: 0 }), {}); // v.formItemProps.initialValue
- const columns = [
- {
- title: curObjectItem.label,
- dataIndex: 'object_id',
- valueType: 'select',
- // ...valueEnum
- // fieldProps: { labelInValue: true },
- render: (_, r) => r.object_name,
- },
- // {
- // title: 'Name',
- // dataIndex: 'title',
- // //...form rules
- // formItemProps: {
- // rules: [
- // {
- // required: true,
- // whitespace: true,
- // message: '此项是必填项',
- // },
- // ],
- // },
- // },
- {
- title: '年度目标',
- dataIndex: 'yearValue',
- valueType: 'digit',
- fieldProps: { style: { width: '100%' } },
- formItemProps: {
- style: { width: '100%' },
- },
- },
- ...monthCol,
- // {
- // title: '完成进度',
- // dataIndex: 'place',
- // valueType: 'percent',
- // editable: false,
- // width: '6em',
- // },
- {
- title: (
-
- 操作
- {
- setEditOpen(e);
- setEditableRowKeys(e ? dataSource.map((ele) => ele.key) : []);
- // KPIStore.setEditableRowsKeys(e ? dataSource.map((ele) => ele.key) : []);
- }}
- />
-
- ),
- valueType: 'option',
- // width: 250,
- render: () => {
- return null;
- },
- },
- ];
- const onTableChange = (...argrs) => {
- console.log(argrs[0], 'who who who');
- setEditableRowKeys(argrs[0].map((ele) => ele.key));
- // KPIStore.setEditableRowsKeys(argrs[0].map((ele) => ele.key));
- setDataSource(argrs[0]);
- // KPIStore.handleTableEdit(argrs[0]);
- };
return (
<>
@@ -168,8 +49,10 @@ console.log(curObjectItem, KPIObjectsMapped, curObject, 'cocococo');
}}
confirmText="查询"
onSubmit={(_err, obj, form, str) => {
- // TradeStore.setStateSearch(form);
- // pageRefresh(obj);
+ console.log('invoke kpi setting search');
+ if (typeof onSearchSubmit === 'function') {
+ onSearchSubmit(obj);
+ }
}}
/>
@@ -178,74 +61,14 @@ console.log(curObjectItem, KPIObjectsMapped, curObject, 'cocococo');
{
+ KPIStore.setSettingSubject(sub);
+ }}
items={KPISubjects.map((ele, i) => {
- const id = String(i);
+ const SubjectTableComponent = subjectComponents[ele.key];
return {
...ele,
- children: (
- {
- // return [
- // {
- // setEditOpen(e);
- // setEditableRowKeys(e ? dataSource.map((ele) => ele.key) : []);
- // // KPIStore.setEditableRowsKeys(e ? dataSource.map((ele) => ele.key) : []);
- // }}
- // />,
- // ,
- // ];
- // }}
- editable={{
- type: 'multiple',
- editableKeys: editableRowsKeys,
- actionRender: (row, config, defaultDoms) => {
- // console.log(row, config, defaultDoms);
- return [defaultDoms.delete];
- },
- onValuesChange: (record, recordList) => {
- console.log('on edit, onValuesChange');
- onTableChange(recordList);
- },
- onChange: (editableKeys, editableRows) => {
- console.log('editable onValuesChange');
- onTableChange(editableRows);
- // KPIStore.setEditableRowsKeys()
- },
- }}
- />
- ),
+ children:
};
})}
/>
diff --git a/src/components/kpi/ObjectPanel.jsx b/src/components/kpi/ObjectPanel.jsx
new file mode 100644
index 0000000..a78c231
--- /dev/null
+++ b/src/components/kpi/ObjectPanel.jsx
@@ -0,0 +1,23 @@
+import { useContext } from 'react';
+import { observer } from 'mobx-react';
+// import { stores_Context } from '../config';
+import { Table } from 'antd';
+import KPISettings from './KPISettings';
+import { bu, KPISubjects } from '../../libs/ht';
+
+const searchFormItemSet = {
+ 'bu': { shows: ['DateType', 'years', 'HTBusinessUnits'] },
+ 'dept': { shows: ['DateType', 'years', 'DepartmentList'], fieldProps: { DepartmentList: { allowClear: true } } },
+ 'operator': { shows: ['DateType', 'years', 'DepartmentList'] }, // , 'operator'
+ 'destination': { shows: ['DateType', 'years', 'destination'] },
+ 'country': { shows: ['DateType', 'years', 'country'] },
+};
+
+export default observer((props) => {
+ const searchProps = searchFormItemSet?.[props.curObject] || {};
+ return (
+ <>
+
+ >
+ );
+});
diff --git a/src/components/kpi/SubjectTable/Profit.jsx b/src/components/kpi/SubjectTable/Profit.jsx
new file mode 100644
index 0000000..777adb4
--- /dev/null
+++ b/src/components/kpi/SubjectTable/Profit.jsx
@@ -0,0 +1,252 @@
+import { useContext, useState, useEffect, useMemo } from 'react';
+import { observer } from 'mobx-react';
+import moment from 'moment';
+import { stores_Context } from './../../../config';
+import { Button, Switch, Input, Space, Typography, Row, Col, message } from 'antd';
+import { EditableProTable } from '@ant-design/pro-components';
+import { KPIObjects } from './../../../libs/ht';
+import { isEmpty, fixTo2Decimals, fixTo4Decimals, cloneDeep, numberFormatter, fixToInt } from './../../../utils/commons';
+
+export const KPIObjectsMapped = KPIObjects.reduce((a, c) => ({ ...a, [String(c.key)]: c }), {});
+const { Text } = Typography;
+const initialPercentKey = new Array(12).fill(1).reduce((r, v, i) => ({ ...r, [`M${i + 1}Percent`]: [8, 9].includes(i) ? 10 : 8 }), {});
+const numberConvert10K = (number, scale = 10) => {
+ return fixTo2Decimals(number / (1000 * scale)) + '万';
+};
+
+export default observer((props) => {
+ const { KPIStore, date_picker_store: searchFormStore } = useContext(stores_Context);
+ const { curObject, objects, onSearchSubmit } = props;
+ const curObjectItem = KPIObjectsMapped[curObject];
+
+ const [dataSource, setDataSource] = useState(KPIStore.pageData);
+ const [editOpen, setEditOpen] = useState(false);
+ const [editableRowsKeys, setEditableRowKeys] = useState([]);
+ useEffect(() => {
+ setDataSource(KPIStore.pageData);
+ setEditOpen(false);
+ return () => {};
+ }, [KPIStore.pageData]);
+
+
+ const PercentInput = useMemo(
+ () =>
+ // eslint-disable-next-line react/display-name
+ ({ value, onChange, record, ...extProps }) => {
+ // // eslint-disable-next-line react-hooks/rules-of-hooks
+ const [inputValue, setInputValue] = useState(value);
+ const handleInputChange = (e) => {
+ setInputValue(e.target.value);
+ onChange?.(e.target.value);
+ };
+ const calcV = inputValue ? numberConvert10K(fixToInt((Number(record?.yearValue) * inputValue) / 100)) : 0;
+ return (
+
+
+ {calcV}
+
+ );
+ },
+ []
+ );
+ const RenderMonthCell = (row, mon) => {
+ return (
+
+
+ {fixTo2Decimals(row?.[`M${mon}Percent`])}
+ %
+
+ {numberConvert10K(fixTo4Decimals((Number(row?.yearValue) * row?.[`M${mon}Percent`]) / 100))}
+
+ );
+ };
+ const monthCol = new Array(12).fill(1).map((_, index) => {
+ return {
+ title: `${index + 1}月`,
+ dataIndex: `M${index + 1}Percent`,
+ valueType: 'digit',
+ width: '6.5em',
+ // fieldProps: { min: 0, max: 100, style: { width: '4em' } },
+ renderFormItem: ({ dataIndex, ...item }, { record, isEditable, ...e }, form) => {
+ return ;
+ },
+ render: (_, row) => RenderMonthCell(row, index + 1),
+ };
+ });
+ const columns = [
+ {
+ title: curObjectItem.label,
+ dataIndex: 'object_id',
+ editable: false,
+ render: (_, r) => r.object_name,
+ },
+ {
+ title: '年度目标',
+ dataIndex: 'yearValue',
+ valueType: 'digit',
+ fieldProps: { style: { width: '100%' }, step: 10000*100 },
+ formItemProps: {
+ style: { width: '100%' },
+ },
+ },
+ ...monthCol,
+ {
+ title: (
+
+ 操作
+ {
+ makeInitialTable(e);
+ }}
+ />
+
+ ),
+ valueType: 'option',
+ // width: 250,
+ render: () => {
+ return null;
+ },
+ },
+ ];
+
+ const onTableChange = (...argrs) => {
+ setEditableRowKeys(argrs[0].map((ele) => ele.key));
+ setDataSource(argrs[0]);
+ };
+ const onTableSubmit = () => {
+ const tableData = dataSource.reduce((r, curObj) => {
+ const allMonth = new Array(12).fill(1).map((_, index) => {
+ const mIndex = index + 1;
+ const mVal = (Number(curObj.yearValue) * Number(curObj[`M${mIndex}Percent`])) / 100;
+ const startM = moment([KPIStore.settingYear, index, 1]);
+ const pick = (({ object, object_name, object_id, subject, date_type }) => ({
+ object,
+ object_name,
+ object_id,
+ subject,
+ date_type,
+ }))(curObj);
+ return {
+ ...pick,
+ start_date: startM.format('YYYY-MM-DD'),
+ end_date: startM.endOf('M').format('YYYY-MM-DD HH:mm'),
+ value: mVal,
+ kpi_id: curObj.kpiDataMapped?.[`M${mIndex}`]?.kpi_id || undefined,
+ key: undefined,
+ };
+ });
+ return r.concat(allMonth);
+ }, []);
+ const yearRow = (({ object, object_name, object_id, subject, date_type, yearValue, kpiYear }) => ({
+ object,
+ object_name,
+ object_id,
+ subject,
+ date_type,
+ value: yearValue,
+ start_date: moment([KPIStore.settingYear, 0, 1]).format('YYYY-MM-DD'),
+ end_date: moment([KPIStore.settingYear, 11, 1]).endOf('M').format('YYYY-MM-DD HH:mm'),
+ kpi_id: kpiYear?.kpi_id || undefined,
+ }))(dataSource?.[0] || {});
+ tableData.unshift(yearRow);
+ console.log('sub', tableData, 'del:', delKpiIds);
+ // return false; // debug:
+ KPIStore.onSubmit(tableData, { delQueue: delKpiIds }).then((res) => {
+ if (res) {
+ message.success('保存成功');
+ setEditOpen(false);
+ setEditableRowKeys([]);
+ setDelKpiIds([]);
+ onSearchSubmit(searchFormStore.formValuesToSub);
+ return false;
+ }
+ message.error('失败, 请重试');
+ });
+ };
+ const initialRow = monthCol.reduce(
+ (r, v) => ({
+ ...r,
+ [v.dataIndex]: 0,
+ yearValue: 10000 * 100,
+ object: curObject,
+ object_name: '',
+ object_id: -1,
+ subject: 'sum_profit',
+ date_type: searchFormStore.formValuesToSub.DateType,
+ kpiDataMapped: {},
+ key: Date.now().toString(32),
+ }),
+ {}
+ ); // v.formItemProps.initialValue
+ const makeInitialTable = (e) => {
+ setEditOpen(e);
+ // todo: 单独设置之后, 清空筛选会导致无法批量设置新的
+ if (e && isEmpty(dataSource)) {
+ const _initialRow = Object.assign({}, initialRow, initialPercentKey);
+ const _objects = isEmpty(objects) ? (curObjectItem?.data || []) : objects;
+ console.log('ooo', objects, isEmpty(objects), curObjectItem?.data || []);
+ const _initialTable = _objects.map((obj) => ({
+ ...cloneDeep(_initialRow),
+ object_name: obj.label,
+ object_id: obj.value,
+ key: Date.now().toString(32) + obj.value,
+ }));
+ console.log(_initialRow, 'iiiii');
+ setDataSource(_initialTable);
+ setEditableRowKeys(_initialTable.map((ele) => ele.key));
+ return false;
+ }
+ setEditableRowKeys(e ? dataSource.map((ele) => ele.key) : []);
+ };
+ const [delKpiIds, setDelKpiIds] = useState([]);
+ return (
+ <>
+
+
+ {
+ return [defaultDoms.delete];
+ },
+ onDelete: (_key, _row) => {
+ // console.log('del', _key, _row);
+ const rowKpiIds = (_row?.kpiData || []).map((ele) => ele.kpi_id);
+ rowKpiIds.push(_row?.kpiYear?.kpi_id);
+ setDelKpiIds(rowKpiIds);
+ },
+ onValuesChange: (record, recordList) => {
+ // console.log('on edit, onValuesChange',record, recordList);
+ onTableChange(recordList);
+ },
+ onChange: (editableKeys, editableRows) => {
+ setEditableRowKeys(editableKeys);
+ },
+ }}
+ />
+
+
+
+
+
+ >
+ );
+});
diff --git a/src/stores/KPI.js b/src/stores/KPI.js
index d31f81b..aa3b922 100644
--- a/src/stores/KPI.js
+++ b/src/stores/KPI.js
@@ -34,10 +34,14 @@ class KPI {
start_date: '2020-01-01',
end_date: '2024-12-31 23:59:59',
...param,
+ object_id: [0, 1, 'ALL'].includes(param?.object_id || 0) ? '' : param.object_id,
};
+ this.listLoading = true;
+ this.pageData = [];
return req.fetchJSON('/service-Analyse2/getkpi', _param).then((json) => {
if (json.errcode === 0) {
runInAction(() => {
+ this.listLoading = false;
this.originData = json.result;
const yearData = parseKPI(json.result, ['subject', 'object_id']);
console.log(111, yearData, yearData[this.settingYear]);
@@ -53,7 +57,15 @@ class KPI {
this.settingYear = v;
}
+ listLoading = false;
+ setListLoading(v) {
+ this.listLoading = v;
+ }
+
settingSubject = 'sum_profit';
+ setSettingSubject(v) {
+ this.settingSubject = v;
+ }
originData =[];
pageData = [];
diff --git a/src/views/KPI.jsx b/src/views/KPI.jsx
index 4ecc327..288e041 100644
--- a/src/views/KPI.jsx
+++ b/src/views/KPI.jsx
@@ -1,27 +1,72 @@
-import { useEffect, useState } from 'react';
+import { useContext, useEffect, useState } from 'react';
+import { stores_Context } from './../config';
import { observer } from 'mobx-react';
-import { Row, Col, Tabs } from 'antd';
+import { Row, Col, Tabs, Spin } from 'antd';
+import { flush, objectMapper } from './../utils/commons';
import { KPIObjects } from './../libs/ht';
-import BUPanel from './../components/kpi/BUPanel';
+import ObjectPanel from '../components/kpi/ObjectPanel';
import OverviewPanel from './../components/kpi/OverviewPanel';
import './kpi.css';
const objectComponents = {
'overview': OverviewPanel,
- 'bu': BUPanel,
- 'dept': BUPanel,
- 'du': BUPanel,
- 'operator': BUPanel,
- 'destination': BUPanel,
- 'country': BUPanel,
+ 'bu': ObjectPanel,
+ 'dept': ObjectPanel,
+ 'du': ObjectPanel,
+ 'operator': ObjectPanel,
+ 'destination': ObjectPanel,
+ 'country': ObjectPanel,
+};
+const objectFilterKey = {
+ 'bu': 'HTBusinessUnits',
+ 'dept': 'DepartmentList',
+ // 'du': 'du',
+ // 'operator': 'operator',
+ // 'destination': 'destination',
+ // 'country': 'country',
};
export default observer((props) => {
+ const { KPIStore, DictDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
// useEffect(() => {
// return () => {};
// }, []);
const [curObject, setCurObject] = useState('overview');
const onObjectChange = (object) => {
setCurObject(object);
+ setRetObjects([]);
+ };
+ useEffect(() => {
+ onSearchSubmit(searchFormStore.formValuesToSub);
+
+ return () => {};
+ }, [curObject]);
+
+ const [retObjects, setRetObjects] = useState([]);
+ const onSearchSubmit = (obj, formVal={}) => {
+ const getkpiParam = objectMapper(obj, {
+ DateType: { key: 'date_type' },
+ Date1: { key: 'start_date' },
+ Date2: { key: 'end_date' },
+ HTBusinessUnits: { key: 'object_id' },
+ DepartmentList: { key: 'object_id' },
+ businessUnits: { key: 'object_id' },
+ WebCode: { key: 'object_id' },
+ operator: { key: 'object_id' },
+ country: { key: 'object_id' },
+ });
+ Object.assign(getkpiParam, { object: curObject });
+ KPIStore.setSettingYear(formVal?.year?.year() || KPIStore.settingYear);
+ console.log('invoke on search', obj, formVal, getkpiParam);
+ KPIStore.getList(getkpiParam).then((data) => {
+ // setDataSource(data);
+ if (objectFilterKey?.[curObject]) {
+ const selectItem = searchFormStore.formValues[objectFilterKey[curObject]];
+ if (selectItem) {
+ selectItem.value = selectItem.key;
+ }
+ setRetObjects(flush([selectItem]));
+ }
+ });
};
return (
<>
@@ -31,10 +76,14 @@ export default observer((props) => {
onChange={onObjectChange}
type="card"
items={KPIObjects.map((ele, i) => {
- const ItemComponent = objectComponents[ele.key];
+ const ObjectItemPanel = objectComponents[ele.key];
return {
...ele,
- children: ,
+ children: (
+
+
+
+ ),
};
})}
/>