feature/2.0-sales-trade
Lei OT 2 years ago
parent 81313ec86c
commit 33751b3eca

@ -7,3 +7,16 @@
.align_left{
text-align: left;
}
.mb-1{
margin-bottom: 1em;
}
.mb-n1{
margin-bottom: -1em;
}
.p-none{
padding: 0;
}
.p-s1{
padding: .5em;
}

@ -1,13 +1,18 @@
import { useContext } from 'react';
import { observer } from "mobx-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 (
<>
{props.title}
<KPISettings {...{ searchProps, objects: bu, KPISubjects }} {...props} />
</>
);
});

@ -0,0 +1,256 @@
import { useContext, useState, useEffect } 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 SearchForm from './../search/SearchForm';
import { bu, KPIObjects, KPISubjects } from './../../libs/ht';
import { isEmpty, fixTo2Decimals, fixTo4Decimals, cloneDeep, numberFormatter } from './../../utils/commons';
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 } = {
sort: '',
initialValue: {},
fieldProps: {
years: { hide_vs: true },
},
hides: [],
shows: ['DateType', 'years'],
...props.searchProps,
};
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 (
<Space direction={'vertical'}>
<Input suffix="%" type={'number'} value={value} onChange={onChange} step={0.1} />
{/* onBlur={handleInputConfirm} onPressEnter={handleInputConfirm} */}
{/* <span className='ant-typography ant-typography-secondary'>1</span> */}
<Text type={'secondary'}>{calcV}</Text>
</Space>
);
};
const RenderInput = (row, mon) => {
// console.log(toJS(row), mon);
return (
<Space direction={'vertical'}>
<div>
{row.kpiDataMapped?.[`M${mon}`]?.percentVal}
<span>%</span>
</div>
<div>{row.kpiDataMapped?.[`M${mon}`]?.value}</div>
</Space>
);
};
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 <PercentInput {...{ record }} month={index + 1} />;
},
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: (
<Space>
<span>操作</span>
<Switch
unCheckedChildren="查看"
checkedChildren="编辑"
key={'openEdit'}
// defaultChecked={true}
checked={editOpen}
onChange={(e) => {
setEditOpen(e);
setEditableRowKeys(e ? dataSource.map((ele) => ele.key) : []);
// KPIStore.setEditableRowsKeys(e ? dataSource.map((ele) => ele.key) : []);
}}
/>
</Space>
),
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 (
<>
<Row gutter={16} className="mb-1">
<Col className="gutter-row mb-n1 p-none" span={24}>
<SearchForm
defaultValue={{
initialValue,
shows,
fieldProps,
}}
confirmText="查询"
onSubmit={(_err, obj, form, str) => {
// TradeStore.setStateSearch(form);
// pageRefresh(obj);
}}
/>
</Col>
</Row>
<Row gutter={16} className="mb-1 p-s1">
<Col className="gutter-row mb-n1 p-none" span={24} style={{ backgroundColor: '#ffffff' }}>
<Tabs
tabPosition={'left'}
style={
{
// height: 220,
}
}
items={KPISubjects.map((ele, i) => {
const id = String(i);
return {
...ele,
children: (
<EditableProTable
key={settingYear}
headerTitle={false}
columns={columns}
rowKey="key"
scroll={{
x: 1000,
}}
value={dataSource}
onChange={onTableChange}
recordCreatorProps={false}
// toolBarRender={() => {
// return [
// <Switch
// unCheckedChildren=""
// checkedChildren=""
// key={'openEdit'}
// // defaultChecked={true}
// checked={editOpen}
// onChange={(e) => {
// setEditOpen(e);
// setEditableRowKeys(e ? dataSource.map((ele) => ele.key) : []);
// // KPIStore.setEditableRowsKeys(e ? dataSource.map((ele) => ele.key) : []);
// }}
// />,
// <Button
// disabled={!editOpen}
// type="primary"
// key="save"
// onClick={() => {
// console.log(dataSource);
// // dataSource api
// KPIStore.saveOrUpdate(dataSource);
// }}
// >
//
// </Button>,
// ];
// }}
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()
},
}}
/>
),
};
})}
/>
</Col>
</Row>
</>
);
});

@ -1,37 +1,263 @@
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 { useContext, useState, useEffect, useMemo } from 'react';
import { observer } from 'mobx-react';
import { toJS } from 'mobx';
import moment from 'moment';
import { stores_Context } from './../../config';
import { Button, Table, Switch, Input, Space, Typography, Row, Col, Spin, Radio, Tabs, message } from 'antd';
import { EditableProTable, ProCard, ProFormField } from '@ant-design/pro-components';
import SearchForm from './../search/SearchForm';
import { bu } from './../../libs/ht';
import { isEmpty, objectMapper, fixToInt, fixTo2Decimals, fixTo4Decimals, cloneDeep, numberFormatter } from './../../utils/commons';
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 { } = useContext(stores_Context);
const { KPIStore, date_picker_store: searchFormStore } = useContext(stores_Context);
const { curObject } = props;
const [dataSource, setDataSource] = useState([]);
useEffect(() => {
onSearchSubmit({
object: curObject,
date_type: 'applyDate',
start_date: searchFormStore.start_date.startOf('year').format('YYYY-MM-DD'),
end_date: searchFormStore.end_date.endOf('year').format('YYYY-MM-DD 23:59'),
});
return () => {};
}, []);
const onSearchSubmit = (obj, formVal={}) => {
const getkpiParam = objectMapper(obj, { DateType: { key: 'date_type' }, Date1: { key: 'start_date' }, Date2: { key: 'end_date' } });
Object.assign(getkpiParam, { object: curObject });
KPIStore.setSettingYear(formVal?.year?.year() || KPIStore.settingYear);
KPIStore.getList(getkpiParam).then((data) => {
setDataSource(data);
});
};
const [editOpen, setEditOpen] = useState(false);
const [editableRowsKeys, setEditableRowKeys] = useState([]);
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(fixTo4Decimals((Number(record?.yearValue) * inputValue) / 100)) : 0;
return (
<Space direction={'vertical'}>
<Input key={'input'} suffix="%" type={'number'} value={inputValue} onChange={handleInputChange} step={0.1} />
<Text type={'secondary'}>{calcV}</Text>
</Space>
);
},
[]
);
const RenderMonthCell = (row, mon) => {
return (
<Space direction={'vertical'}>
<div>
{fixTo2Decimals(row?.[`M${mon}Percent`])}
<span>%</span>
</div>
<div>{numberConvert10K(fixTo4Decimals((Number(row?.yearValue) * row?.[`M${mon}Percent`]) / 100))}</div>
</Space>
);
};
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 <PercentInput {...{ record }} month={index + 1} key={`M${index + 1}`} />;
},
render: (_, row) => RenderMonthCell(row, index + 1),
};
});
const columns = [
{
title: '年度目标',
dataIndex: 'yearValue',
valueType: 'digit',
fieldProps: { style: { width: '100%' }, step: 10000 * 100 },
formItemProps: {
style: { width: '100%' },
},
render: (_, row, ...a) => numberConvert10K(row.yearValue),
},
...monthCol,
{
title: '操作',
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, 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);
if (e && isEmpty(dataSource)) {
const _initialRow = Object.assign({}, initialRow, initialPercentKey);
setDataSource([_initialRow]);
setEditableRowKeys([_initialRow.key]);
return false;
}
setEditableRowKeys(e ? dataSource.map((ele) => ele.key) : []);
};
const [delKpiIds, setDelKpiIds] = useState([]);
return (
<>
<Row gutter={16} style={{ margin: '0 0 1em' }}>
<Col className="gutter-row" span={24} style={{ margin: '0 0 -16px 0', padding: 0 }}>
<Row gutter={16} className="mb-1" style1={{ margin: '0 0 1em' }}>
<Col className="gutter-row m-n1 p-none" span={24} style1={{ margin: '0 0 -16px 0', padding: 0 }}>
<SearchForm
defaultValue={{
'initialValue': {
// ...searchPayloadHome,
},
// hides: ['businessUnits', 'months', 'WebCode', 'dates'],
shows: ['DateType', 'DepartmentList', 'WebCode', 'years'],
'initialValue': {},
shows: ['DateType', 'years'],
'fieldProps': {
DepartmentList: { show_all: false },
WebCode: { show_all: false },
years: { hide_vs: true },
},
}}
confirmText='查询'
confirmText="查询"
onSubmit={(_err, obj, form, str) => {
// TradeStore.setStateSearch(form);
// pageRefresh(obj);
onSearchSubmit(obj, form);
setEditOpen(false);
setEditableRowKeys([]);
}}
/>
</Col>
</Row>
<EditableProTable
key={KPIStore.settingYear}
headerTitle={`毛利`}
columns={columns}
rowKey="key"
scroll={{
x: 1000,
}}
value={dataSource}
onChange={onTableChange}
recordCreatorProps={false}
toolBarRender={() => {
return [
<Switch
unCheckedChildren="查看"
checkedChildren="编辑"
key={'openEdit'}
checked={editOpen}
onChange={(e) => {
makeInitialTable(e);
}}
/>,
<Button disabled={!editOpen} type="primary" key="save" onClick={onTableSubmit}>
保存数据
</Button>,
];
}}
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);
},
}}
/>
</>
);
});

@ -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 (
<>
<Row gutter={16} className="mb-1">
<Col className="gutter-row mb-n1 p-none" span={24}>
<SearchForm
defaultValue={{
'initialValue': {
// ...searchPayloadHome,
},
// hides: ['businessUnits', 'months', 'WebCode', 'dates'],
shows: ['HTBusinessUnits', 'DateType', 'DepartmentList', 'years'],
'fieldProps': {
HTBusinessUnits: { show_all: true },
DepartmentList: { show_all: true },
WebCode: { show_all: false },
years: { hide_vs: true },
},
}}
confirmText="查询"
onSubmit={(_err, obj, form, str) => {
// TradeStore.setStateSearch(form);
// pageRefresh(obj);
}}
/>
</Col>
</Row>
<Row gutter={16} className="mb-1 p-s1">
<Col className="gutter-row mb-n1 p-none" span={24} style={{ backgroundColor: '#ffffff' }}>
<Tabs
tabPosition={'left'}
style={
{
// height: 220,
}
}
items={KPIObjects.map((ele, i) => {
const id = String(i);
return {
...ele,
children: `Content of tab ${id}`,
};
})}
/>
</Col>
</Row>
</>
);
});

@ -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 (
<div>
<Select
mode={mode || null}
allowClear
style={{width: '100%',}}
placeholder="选择事业部"
value={value || undefined } // { key: '-1', label: 'ALL ' }
onChange={(value) => {
if (typeof onChange === 'function') {
onChange(value);
}
// store?.bu_handleChange(value);
}}
labelInValue={true}
{...extProps}
>
{_show_all ? <Select.Option key="-1" value="ALL">ALL 事业部</Select.Option> : ''}
{bu.map(ele => <Select.Option key={ele.key} value={ele.label}>{ele.label}</Select.Option>)}
</Select>
</div>
);
};
/**
* HT的事业部
*/
export default observer(Business_unit);

@ -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 (

@ -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;

@ -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;

@ -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;
});
}

@ -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 (
<Space direction={'vertical'}>
<Input suffix="%" type={'number'} value={value} onChange={onChange} />
{/* onBlur={handleInputConfirm} onPressEnter={handleInputConfirm} */}
{/* <span className='ant-typography ant-typography-secondary'>1</span> */}
<Text type={'secondary'}>{calcV}</Text>
</Space>
);
};
const RenderInput = (row, mon) => {
// console.log(toJS(row), mon);
return (
<Space direction={'vertical'}>
<div>
{row.kpiDataMapped?.[`M${mon}`]?.percentVal}
<span>%</span>
</div>
<div>{row.kpiDataMapped?.[`M${mon}`]?.value}</div>
</Space>
);
};
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 <PercentInput {...{ record }} month={index + 1} />;
},
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 (
<>
<Row>
<Col span={24}>
{/* <Radio.Group options={KPIObjects} optionType="button" onChange={(e) => handleRadioChange(e.target.value)} /> */}
<Tabs
// onChange={onChange}
onChange={onObjectChange}
type="card"
items={KPIObjects.map((ele, i) => {
const ItemComponent = itemComponents[ele.key];
const ItemComponent = objectComponents[ele.key];
return {
...ele,
children: <ItemComponent title={ele.label} />,
children: <ItemComponent title={ele.label} {...{ curObject }} />,
};
})}
/>
</Col>
</Row>
<Row gutter={16} style={{ margin: '0 0 1em' }}>
<Col className="gutter-row" span={24} style={{ margin: '0 0 -16px 0', padding: 0 }}>
<SearchForm
defaultValue={{
'initialValue': {
// ...searchPayloadHome,
},
// hides: ['businessUnits', 'months', 'WebCode', 'dates'],
shows: ['DateType', 'DepartmentList', 'WebCode', 'years'],
'fieldProps': {
DepartmentList: { show_all: false },
WebCode: { show_all: false },
years: { hide_vs: true },
},
}}
onSubmit={(_err, obj, form, str) => {
// TradeStore.setStateSearch(form);
// pageRefresh(obj);
}}
/>
</Col>
</Row>
<EditableProTable
key={settingYear}
headerTitle={settingYear}
columns={columns}
rowKey="key"
scroll={{
x: 1000,
}}
value={dataSource}
onChange={onTableChange}
recordCreatorProps={
editOpen
? {
newRecordType: 'dataSource',
record: () => ({
key: Date.now().toString(32), // dataSource.length + 1, // Number(Date.now().toString()),
...initialRow,
object_name: '',
value: 0,
yearValue: 0,
...cloneDeep(initialPercentKey),
}),
}
: false
}
toolBarRender={() => {
return [
<Switch
unCheckedChildren="查看"
checkedChildren="编辑"
key={'openEdit'}
// defaultChecked={true}
checked={editOpen}
onChange={(e) => {
setEditOpen(e);
setEditableRowKeys(e ? dataSource.map((ele) => ele.key) : []);
// KPIStore.setEditableRowsKeys(e ? dataSource.map((ele) => ele.key) : []);
}}
/>,
<Button
disabled={!editOpen}
type="primary"
key="save"
onClick={() => {
console.log(dataSource);
// dataSource api
KPIStore.saveOrUpdate(dataSource);
}}
>
保存数据
</Button>,
];
}}
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) => (
<div key={ele.value}>
{ele.label} <span className="a">{ele.mobile}</span>
</div>
))}
</>
);
});

@ -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;
}

Loading…
Cancel
Save