Merge branch 'feature/2.0' of github.com:hainatravel/dashboard into feature/2.0
commit
2ce6099b0e
@ -0,0 +1,263 @@
|
||||
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 = 1) => {
|
||||
return fixTo2Decimals(number / (1000 * scale)) + 'K';
|
||||
};
|
||||
|
||||
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);
|
||||
setEditableRowKeys([]);
|
||||
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 (
|
||||
<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: curObjectItem.label,
|
||||
dataIndex: 'object_id',
|
||||
editable: false,
|
||||
render: (_, r) => r.object_name,
|
||||
},
|
||||
{
|
||||
title: '年度目标',
|
||||
dataIndex: 'yearValue',
|
||||
valueType: 'digit',
|
||||
fieldProps: { style: { width: '100%' }, step: 10000 * 1 },
|
||||
formItemProps: {
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
...monthCol,
|
||||
{
|
||||
title: (
|
||||
<Space>
|
||||
<span>操作</span>
|
||||
<Switch
|
||||
unCheckedChildren="查看"
|
||||
checkedChildren="编辑"
|
||||
key={'openEdit'}
|
||||
// defaultChecked={true}
|
||||
checked={editOpen}
|
||||
onChange={(e) => {
|
||||
makeInitialTable(e);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
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,
|
||||
group_date_type: 'month',
|
||||
};
|
||||
});
|
||||
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,
|
||||
group_date_type: 'year',
|
||||
}))(curObj);
|
||||
return r.concat(allMonth, 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 * 1,
|
||||
object: curObject,
|
||||
object_name: '',
|
||||
object_id: -1,
|
||||
subject: KPIStore.settingSubject,
|
||||
date_type: searchFormStore.formValuesToSub.DateType,
|
||||
kpiDataMapped: {},
|
||||
key: Date.now().toString(32),
|
||||
group_date_type: 'month',
|
||||
}),
|
||||
{}
|
||||
); // v.formItemProps.initialValue
|
||||
const makeInitialTable = (e) => {
|
||||
setEditOpen(e);
|
||||
// test: 单独设置之后, 清空筛选会导致无法批量设置新的
|
||||
const _initialRow = Object.assign({}, initialRow, initialPercentKey);
|
||||
const _objects = isEmpty(objects) ? curObjectItem?.data || [] : objects;
|
||||
const _initialTable = _objects.map((obj) => ({
|
||||
...cloneDeep(_initialRow),
|
||||
object_name: obj.label,
|
||||
object_id: obj.value,
|
||||
key: Date.now().toString(32) + obj.value,
|
||||
}));
|
||||
const mergePageData = Object.values(
|
||||
Object.assign(
|
||||
{},
|
||||
_initialTable.reduce((r, v) => ({ ...r, [v.object_name]: v }), {}),
|
||||
dataSource.reduce((r, v) => ({ ...r, [v.object_name]: v }), {})
|
||||
)
|
||||
);
|
||||
if (e && isEmpty(dataSource)) {
|
||||
setDataSource(_initialTable);
|
||||
setEditableRowKeys(_initialTable.map((ele) => ele.key));
|
||||
return false;
|
||||
}
|
||||
setDataSource(mergePageData);
|
||||
setEditableRowKeys(e ? mergePageData.map((ele) => ele.key) : []);
|
||||
};
|
||||
const [delKpiIds, setDelKpiIds] = useState([]);
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} className="mb-1 ">
|
||||
<Col className="gutter-row mb-n1 p-none" span={24}>
|
||||
<EditableProTable
|
||||
key={KPIStore.settingYear}
|
||||
// headerTitle={`毛利`}
|
||||
columns={columns}
|
||||
rowKey="key"
|
||||
scroll={{
|
||||
x: 1000,
|
||||
}}
|
||||
value={dataSource}
|
||||
onChange={onTableChange}
|
||||
recordCreatorProps={false}
|
||||
editable={{
|
||||
type: 'multiple',
|
||||
editableKeys: editableRowsKeys,
|
||||
actionRender: (row, config, defaultDoms) => {
|
||||
return [defaultDoms.delete];
|
||||
},
|
||||
onDelete: (_key, _row) => {
|
||||
const rowKpiIds = (_row?.kpiData || []).map((ele) => ele.kpi_id);
|
||||
rowKpiIds.push(_row?.kpiYear?.kpi_id);
|
||||
setDelKpiIds(rowKpiIds);
|
||||
},
|
||||
onValuesChange: (record, recordList) => {
|
||||
onTableChange(recordList);
|
||||
},
|
||||
onChange: (editableKeys, editableRows) => {
|
||||
setEditableRowKeys(editableKeys);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col className="gutter-row mb-n1 p-none mt-1 align_center" span={24}>
|
||||
<Space size={'large'}>
|
||||
<Button className="mt-1 mb-1 align_center" disabled={!editOpen} type="primary" key="save" onClick={onTableSubmit}>
|
||||
保存数据
|
||||
</Button>
|
||||
{!editOpen && <Button className="mt-1 mb-1 align_center" disabled={false} type={'ghost'} key="initTable" onClick={() => {makeInitialTable(true);}}>
|
||||
编辑设置
|
||||
</Button>}
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,263 @@
|
||||
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}Val`]: [8, 9].includes(i) ? 10 : 8 }), {});
|
||||
const numberConvert10K = (number, scale = 1) => {
|
||||
return fixTo2Decimals(number / (1000 * scale)) + 'K';
|
||||
};
|
||||
|
||||
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(() => {
|
||||
// console.log(KPIStore.pageData);
|
||||
setDataSource(KPIStore.pageData);
|
||||
setEditableRowKeys([]);
|
||||
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);
|
||||
};
|
||||
return (
|
||||
<Input key={'input'} suffix="%" type={'number'} value={inputValue} onChange={handleInputChange} step={0.1} />
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
const RenderMonthCell = (row, mon) => {
|
||||
return (
|
||||
<Space direction={'vertical'}>
|
||||
<div>
|
||||
{fixTo2Decimals(row?.[`M${mon}Val`])}
|
||||
<span>%</span>
|
||||
</div>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
const monthCol = new Array(12).fill(1).map((_, index) => {
|
||||
return {
|
||||
title: `${index + 1}月`,
|
||||
dataIndex: `M${index + 1}Val`,
|
||||
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: curObjectItem.label,
|
||||
dataIndex: 'object_id',
|
||||
editable: false,
|
||||
render: (_, r) => r.object_name,
|
||||
},
|
||||
{
|
||||
title: '年度目标',
|
||||
dataIndex: 'yearValue',
|
||||
valueType: 'percent',
|
||||
formItemProps: {
|
||||
style: { width: '100%' },
|
||||
},
|
||||
renderFormItem: () => <PercentInput />
|
||||
},
|
||||
...monthCol,
|
||||
{
|
||||
title: (
|
||||
<Space>
|
||||
<span>操作</span>
|
||||
<Switch
|
||||
unCheckedChildren="查看"
|
||||
checkedChildren="编辑"
|
||||
key={'openEdit'}
|
||||
// defaultChecked={true}
|
||||
checked={editOpen}
|
||||
onChange={(e) => {
|
||||
makeInitialTable(e);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
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 mVal = Number(curObj[`M${mIndex}Val`]);
|
||||
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,
|
||||
group_date_type: 'month',
|
||||
unit: '%',
|
||||
};
|
||||
});
|
||||
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,
|
||||
group_date_type: 'year',
|
||||
unit: '%',
|
||||
}))(curObj);
|
||||
return r.concat(allMonth, 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: 15,
|
||||
object: curObject,
|
||||
object_name: '',
|
||||
object_id: -1,
|
||||
subject: KPIStore.settingSubject,
|
||||
date_type: searchFormStore.formValuesToSub.DateType,
|
||||
kpiDataMapped: {},
|
||||
key: Date.now().toString(32),
|
||||
group_date_type: 'month',
|
||||
unit: '%',
|
||||
}),
|
||||
{}
|
||||
); // v.formItemProps.initialValue
|
||||
const makeInitialTable = (e) => {
|
||||
setEditOpen(e);
|
||||
// test: 单独设置之后, 清空筛选会导致无法批量设置新的
|
||||
const _initialRow = Object.assign({}, initialRow, initialPercentKey);
|
||||
const _objects = isEmpty(objects) ? curObjectItem?.data || [] : objects;
|
||||
const _initialTable = _objects.map((obj) => ({
|
||||
...cloneDeep(_initialRow),
|
||||
object_name: obj.label,
|
||||
object_id: obj.value,
|
||||
key: Date.now().toString(32) + obj.value,
|
||||
}));
|
||||
const mergePageData = Object.values(
|
||||
Object.assign(
|
||||
{},
|
||||
_initialTable.reduce((r, v) => ({ ...r, [v.object_name]: v }), {}),
|
||||
dataSource.reduce((r, v) => ({ ...r, [v.object_name]: v }), {})
|
||||
)
|
||||
);
|
||||
if (e && isEmpty(dataSource)) {
|
||||
setDataSource(_initialTable);
|
||||
setEditableRowKeys(_initialTable.map((ele) => ele.key));
|
||||
return false;
|
||||
}
|
||||
setDataSource(mergePageData);
|
||||
setEditableRowKeys(e ? mergePageData.map((ele) => ele.key) : []);
|
||||
};
|
||||
const [delKpiIds, setDelKpiIds] = useState([]);
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} className="mb-1 ">
|
||||
<Col className="gutter-row mb-n1 p-none" span={24}>
|
||||
<EditableProTable
|
||||
key={KPIStore.settingYear}
|
||||
// headerTitle={`毛利`}
|
||||
columns={columns}
|
||||
rowKey="key"
|
||||
scroll={{
|
||||
x: 1000,
|
||||
}}
|
||||
value={dataSource}
|
||||
onChange={onTableChange}
|
||||
recordCreatorProps={false}
|
||||
editable={{
|
||||
type: 'multiple',
|
||||
editableKeys: editableRowsKeys,
|
||||
actionRender: (row, config, defaultDoms) => {
|
||||
return [defaultDoms.delete];
|
||||
},
|
||||
onDelete: (_key, _row) => {
|
||||
const rowKpiIds = (_row?.kpiData || []).map((ele) => ele.kpi_id);
|
||||
rowKpiIds.push(_row?.kpiYear?.kpi_id);
|
||||
setDelKpiIds(rowKpiIds);
|
||||
},
|
||||
onValuesChange: (record, recordList) => {
|
||||
onTableChange(recordList);
|
||||
},
|
||||
onChange: (editableKeys, editableRows) => {
|
||||
setEditableRowKeys(editableKeys);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col className="gutter-row mb-n1 p-none mt-1 align_center" span={24}>
|
||||
<Space size={'large'}>
|
||||
<Button className="mt-1 mb-1 align_center" disabled={!editOpen} type="primary" key="save" onClick={onTableSubmit}>
|
||||
保存数据
|
||||
</Button>
|
||||
{!editOpen && <Button className="mt-1 mb-1 align_center" disabled={false} type={'ghost'} key="initTable" onClick={() => {makeInitialTable(true);}}>
|
||||
编辑设置
|
||||
</Button>}
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue