{
+ makeInitialTable(e);
+ }}
+ />,
+ ,
+ ];
+ }}
+ editable={{
+ type: 'multiple',
+ editableKeys: editableRowsKeys,
+ actionRender: (row, config, defaultDoms) => {
+ 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/components/kpi/SumProfitPanel.jsx b/src/components/kpi/SumProfitPanel.jsx
new file mode 100644
index 0000000..0818a00
--- /dev/null
+++ b/src/components/kpi/SumProfitPanel.jsx
@@ -0,0 +1,57 @@
+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 SearchForm from './../search/SearchForm';
+import { bu, KPIObjects } from './../../libs/ht';
+
+export default observer((props) => {
+ // const { } = useContext(stores_Context);
+ return (
+ <>
+
+
+ {
+ // TradeStore.setStateSearch(form);
+ // pageRefresh(obj);
+ }}
+ />
+
+
+
+
+ {
+ const id = String(i);
+ return {
+ ...ele,
+ children: `Content of tab ${id}`,
+ };
+ })}
+ />
+
+
+ >
+ );
+});
diff --git a/src/components/search/BusinessUnitSelect.jsx b/src/components/search/BusinessUnitSelect.jsx
new file mode 100644
index 0000000..1b0738c
--- /dev/null
+++ b/src/components/search/BusinessUnitSelect.jsx
@@ -0,0 +1,36 @@
+import React, {Component} from 'react';
+import {Select} from 'antd';
+import {observer} from "mobx-react";
+import { biz, bu } from '../../libs/ht';
+
+
+const Business_unit = (props) => {
+ const { store, mode, value, onChange, show_all, ...extProps } = props;
+ const _show_all = ['tags', 'multiple'].includes(mode) ? false : show_all;
+ return (
+
+
+
+ );
+};
+/**
+ * HT的事业部
+ */
+export default observer(Business_unit);
diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx
index 725b5ab..cf63c57 100644
--- a/src/components/search/SearchForm.jsx
+++ b/src/components/search/SearchForm.jsx
@@ -1,7 +1,7 @@
-import { createContext } from 'react';
+import { createContext, useContext } from 'react';
import { toJS } from "mobx";
import { observer } from 'mobx-react';
-import { DATE_FORMAT } from './../../config';
+import { DATE_FORMAT, stores_Context } from './../../config';
import { SearchOutlined, } from "@ant-design/icons";
import { Form, Row, Col, Select, Button, Space, DatePicker } from 'antd';
import moment from 'moment';
@@ -32,6 +32,7 @@ const Option = Select.Option;
* @property onSubmit
*/
export default observer((props) => {
+ const { date_picker_store: searchFormStore } = useContext(stores_Context);
const [form] = Form.useForm();
const { sort, initialValue, hides, shows, fieldProps } = {
sort: '',
@@ -143,6 +144,8 @@ export default observer((props) => {
Object.keys(dest).forEach((key) => (dest[key] == null || dest[key] === '' || dest[key].length === 0) && delete dest[key]);
console.log('form value send to onSubmit:', dest);
const str = new URLSearchParams(dest).toString();
+ searchFormStore.setFormValues(values);
+ searchFormStore.setFormValuesToSub(dest);
if (typeof onSubmit === 'function') {
onSubmit(null, dest, values, str);
}
@@ -156,6 +159,7 @@ export default observer((props) => {
const onValuesChange = (...args) => {
const [changedValues, allValues] = args;
console.log('form onValuesChange', args);
+ searchFormStore.setFormValues(allValues);
};
return (
diff --git a/src/stores/DatePickerStore.js b/src/stores/DatePickerStore.js
index ff7ac74..9735607 100644
--- a/src/stores/DatePickerStore.js
+++ b/src/stores/DatePickerStore.js
@@ -1,6 +1,8 @@
import {makeAutoObservable} from "mobx";
import moment from "moment";
-
+/**
+ * 管理搜索组件的状态
+ */
class DatePickerStore {
constructor(rootStore) {
@@ -8,6 +10,9 @@ class DatePickerStore {
makeAutoObservable(this);
}
+ formValues = {};
+ formValuesToSub = {};
+
start_date = moment().startOf('week').subtract(7, 'days');
end_date = moment().endOf('week').subtract(7, 'days');
start_date_cp = false;
@@ -38,8 +43,14 @@ class DatePickerStore {
return [moment(this.start_date).subtract(1, 'year'), moment(this.end_date).subtract(1, 'year')];
}
-}
+ setFormValues(data){
+ this.formValues = data;
+ }
+ setFormValuesToSub(data){
+ this.formValuesToSub = data;
+ }
+}
export default DatePickerStore;
diff --git a/src/stores/KPI.js b/src/stores/KPI.js
index a1e6296..d31f81b 100644
--- a/src/stores/KPI.js
+++ b/src/stores/KPI.js
@@ -1,61 +1,41 @@
import { makeAutoObservable, runInAction, toJS } from 'mobx';
import * as req from '../utils/request';
-import { isEmpty, sortBy, groupBy, cloneDeep } from '../utils/commons';
+import { isEmpty, sortBy, groupBy, cloneDeep, fixTo4Decimals, flush } from '../utils/commons';
import moment from 'moment';
-const currentYear = moment().year();
-
class KPI {
constructor(appStore) {
this.appStore = appStore;
makeAutoObservable(this);
}
- saveOrUpdate(x) {
- console.log('ssssssssssss', x);
- console.log(toJS(this.pageData));
- const tableData = this.pageData.reduce((r, curObj) => {
- const allMonth = new Array(12).fill(1).map((_, index) => {
- const mIndex = index+1;
- const startM = moment([this.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:ss'),
- value: curObj[`M${mIndex}`],
- // ...(curObj[`M${mIndex}_id`] ? { kpi_id: curObj[`M${mIndex}_id`] } : {}),
- kpi_id: curObj[`M${mIndex}_id`] || undefined,
- key: undefined,
- };
- });
- console.log('cccccccccc', allMonth);
- return r.concat(allMonth);
- }, []);
- console.log('ppppp', tableData);
+ async delByID(ids) {
+ const data = { 'kpi_ids': ids };
+ const json = await req.delJSON('/service-Analyse2/delkpi_multi', data);
+ return json.errcode === 0;
+ }
+
+ async saveOrUpdate(tableData) {
const data = { 'kpis': tableData };
- req.postJSON('/service-Analyse2/setkpi_multi/test', data).then((json) => {
- // req.postJSON('/service-Analyse2/setkpi_multi', data).then((json) => {
- if (json.errcode === 0) {
- runInAction(() => {
- console.log({ loading: false, ...json }, 'post kpi');
- });
- }
- });
+ const json = await req.postJSON('/service-Analyse2/setkpi_multi', data);
+ return json.errcode === 0;
+ }
+
+ async onSubmit(tableData, { delQueue }) {
+ const flushData = tableData.filter(row => !isEmpty(row.value) || !isEmpty(row?.kpi_id));
+ const postRes = isEmpty(flushData) ? true : await this.saveOrUpdate(flushData);
+ const delRes = isEmpty(flush(delQueue)) ? true : await this.delByID(delQueue);
+ return postRes && delRes;
}
- getList() {
- const param = {
+ getList(param = {}) {
+ const _param = {
date_type: 'applyDate',
start_date: '2020-01-01',
end_date: '2024-12-31 23:59:59',
+ ...param,
};
- // return req.fetchJSON('/service-Analyse2/getkpi/test', param).then((json) => {
- return req.fetchJSON('/service-Analyse2/getkpi', param).then((json) => {
+ return req.fetchJSON('/service-Analyse2/getkpi', _param).then((json) => {
if (json.errcode === 0) {
runInAction(() => {
this.originData = json.result;
@@ -68,12 +48,11 @@ class KPI {
});
}
- handleTableEdit(data) {
- console.log('handle change ', data);
- // this.pageData = data;
+ settingYear = moment().year();
+ setSettingYear(v) {
+ this.settingYear = v;
}
- settingYear = 2023;
settingSubject = 'sum_profit';
originData =[];
@@ -115,14 +94,15 @@ export const parseKPI = (kpis, keyArr = []) => {
// r.push({ monthIndex: v.monthIndex, yearIndex: v.yearIndex, value: v.value, kpi_id: v.kpi_id });
return { monthIndex: v.monthIndex, yearIndex: v.yearIndex, value: v.value, kpi_id: v.kpi_id };
}, {});
- const kpiData = _ByFull.false.reduce((r, v) => {
- r.push({ monthIndex: v.monthIndex, yearIndex: v.yearIndex, value: v.value, kpi_id: v.kpi_id, percentVal: (v.value/kpiYear.value*100) });
+ const kpiData = (_ByFull?.false || []).reduce((r, v) => {
+ r.push({ monthIndex: v.monthIndex, yearIndex: v.yearIndex, value: v.value, kpi_id: v.kpi_id, percentVal: (fixTo4Decimals(v.value/kpiYear.value)*100) });
return r;
}, []);
const kpiDataMapped = kpiData.reduce((r, v) => ({...r, [`M${v.monthIndex}`]: v }), {});
const kpiDataFlat = kpiData.reduce((r, v) => ({...r, [`M${v.monthIndex}Val`]: v.value, [`M${v.monthIndex}Percent`]: v.percentVal}), {});
const { start_date, end_date, kpi_id, value, unit, monthIndex, monthRange, ...objectEle } = subjectObject[oID][0];
- return { ...cloneDeep(initialPercentKey), ...objectEle, ...kpiDataFlat, kpiData, kpiDataMapped, kpiYear, yearValue: kpiYear?.value || 0 };
+ const allKey = !isEmpty(kpiData) ? kpiData.map(ek => ek.kpi_id).join('_') : `${Object.values(kpiYear).join('_')}`;
+ return { ...cloneDeep(initialPercentKey), ...objectEle, ...kpiDataFlat, kpiData, kpiDataMapped, kpiYear, yearValue: kpiYear?.value || 0, key: allKey };
});
ret[_subject] = afterGroup;
return 1;
diff --git a/src/utils/request.js b/src/utils/request.js
index e9289eb..d954743 100644
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -78,3 +78,18 @@ export function postStream(url, obj) {
throw error;
});
}
+
+export function delJSON(url, obj) {
+ const host = /^https?:\/\//i.test(url) ? '': HT_HOST;
+ return fetch(`${host}${url}`, {
+ method: 'DELETE',
+ body: JSON.stringify(obj),
+ headers: {
+ 'Content-type': 'application/json; charset=UTF-8'
+ }
+ }).then(checkStatus)
+ .then(response => response.json())
+ .catch(error => {
+ throw error;
+ });
+}
diff --git a/src/views/KPI.jsx b/src/views/KPI.jsx
index 38e4dab..4ecc327 100644
--- a/src/views/KPI.jsx
+++ b/src/views/KPI.jsx
@@ -1,19 +1,12 @@
-import { useContext, useEffect, useState, useRef } from 'react';
-import { observer, useLocalStore } from 'mobx-react';
-import { toJS } from 'mobx';
-// import type { ProColumns } from '@ant-design/pro-components';
-import { EditableProTable, ProCard, ProFormField } from '@ant-design/pro-components';
-import { Button, Table, Switch, Input, Space, Typography, Row, Col, Spin, Radio, Tabs } from 'antd';
-import { stores_Context } from '../config';
-import { isEmpty, fixTo4Decimals, cloneDeep } from './../utils/commons';
-import SearchForm from './../components/search/SearchForm';
+import { useEffect, useState } from 'react';
+import { observer } from 'mobx-react';
+import { Row, Col, Tabs } from 'antd';
+import { KPIObjects } from './../libs/ht';
import BUPanel from './../components/kpi/BUPanel';
import OverviewPanel from './../components/kpi/OverviewPanel';
-import { KPIObjects } from './../libs/ht';
import './kpi.css';
-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 itemComponents = {
+
+const objectComponents = {
'overview': OverviewPanel,
'bu': BUPanel,
'dept': BUPanel,
@@ -22,253 +15,31 @@ const itemComponents = {
'destination': BUPanel,
'country': BUPanel,
};
-const tabsItems = KPIObjects.map((ele) => ({
- ...ele,
- label: ele.label,
- key: ele.value,
- children: BUPanel,
-}));
-console.log(tabsItems);
export default observer((props) => {
- const [dataSource, setDataSource] = useState([]);
- const { KPIStore, DictDataStore } = useContext(stores_Context);
- const { settingYear } = KPIStore;
- const { operator } = DictDataStore;
- useEffect(() => {
- // KPIStore.saveOrUpdate();
- KPIStore.getList().then((data) => {
- setDataSource(data);
- });
- DictDataStore.fetchDictData('operator', { is_assign: 1 });
- DictDataStore.fetchDictData('country');
- return () => {};
- }, []);
- 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 ? 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: 'percent',
- 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: '对象',
- 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: '操作',
- 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]);
- };
- const handleRadioChange = (val) => {
- console.log(val, 'sss');
+ // useEffect(() => {
+ // return () => {};
+ // }, []);
+ const [curObject, setCurObject] = useState('overview');
+ const onObjectChange = (object) => {
+ setCurObject(object);
};
return (
<>
- {/* handleRadioChange(e.target.value)} /> */}
{
- const ItemComponent = itemComponents[ele.key];
+ const ItemComponent = objectComponents[ele.key];
return {
...ele,
- children: ,
+ children: ,
};
})}
/>
-
-
- {
- // TradeStore.setStateSearch(form);
- // pageRefresh(obj);
- }}
- />
-
-
- ({
- key: Date.now().toString(32), // dataSource.length + 1, // Number(Date.now().toString()),
- ...initialRow,
- object_name: '',
- value: 0,
- yearValue: 0,
- ...cloneDeep(initialPercentKey),
- }),
- }
- : false
- }
- toolBarRender={() => {
- 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()
- },
- }}
- />
- {(operator?.dataSource || []).map((ele) => (
-
- {ele.label} {ele.mobile}
-
- ))}
>
);
});
diff --git a/src/views/kpi.css b/src/views/kpi.css
index c652f22..ac1d799 100644
--- a/src/views/kpi.css
+++ b/src/views/kpi.css
@@ -1,3 +1,17 @@
.ant-tabs.ant-tabs-card > .ant-tabs-nav {
margin-bottom: 0;
}
+.ant-tabs.ant-tabs-left .ant-tabs-content-holder{
+ padding: 0;
+}
+.ant-tabs-content.ant-tabs-content-left .ant-tabs-tabpane.ant-tabs-tabpane-active{
+ padding-left: 0;
+}
+.ant-tabs-content.ant-tabs-content-left .ant-tabs-tabpane .ant-pro-table .ant-pro-card-body{
+ padding: 0;
+}
+
+.ant-form-item-control-input .ant-input-affix-wrapper{
+ padding-left: 4px;
+ padding-right: 4px;
+}