Merge branch 'feature/2.0' of github.com:hainatravel/dashboard into feature/2.0
commit
d4f104a74e
@ -1,11 +1,11 @@
|
||||
import { observer } from 'mobx-react';
|
||||
import { Radio, Select } from 'antd';
|
||||
import { dataFieldOptions } from './../libs/ht';
|
||||
import { dataFieldOptions } from '../libs/ht';
|
||||
|
||||
export default observer((props) => {
|
||||
const { visible, dataRaw, dataMapper, fieldMapper, onChange, ...extProps } = props;
|
||||
const handleChange = (value) => {
|
||||
console.log('handleChange', value);
|
||||
// console.log('handleChange', value);
|
||||
if (typeof onChange === 'function') {
|
||||
onChange(value);
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
import { useContext } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { stores_Context } from './../../config';
|
||||
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, fixToInt, merge } from './../../utils/commons';
|
||||
import ProfitTable from './SubjectTable/Profit';
|
||||
|
||||
const Todo = (props) => {
|
||||
return <h2>TODO</h2>;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
export default observer((props) => {
|
||||
const { KPIStore, DictDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||
const { sort, initialValue, hides, shows, fieldProps: _fieldProps } = {
|
||||
sort: '',
|
||||
initialValue: searchFormStore.formValues,
|
||||
fieldProps: {},
|
||||
hides: [],
|
||||
shows: ['DateType', 'years'],
|
||||
...props.searchProps,
|
||||
};
|
||||
const fieldProps = merge(_fieldProps, { years: { hide_vs: true } });
|
||||
const { curObject, objects, KPISubjects, onSearchSubmit } = props;
|
||||
|
||||
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) => {
|
||||
console.log('invoke kpi setting search');
|
||||
if (typeof onSearchSubmit === 'function') {
|
||||
onSearchSubmit(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'}
|
||||
onChange={(sub) => {
|
||||
KPIStore.setSettingSubject(sub);
|
||||
}}
|
||||
items={KPISubjects.map((ele, i) => {
|
||||
const SubjectTableComponent = subjectComponents[ele.key];
|
||||
return {
|
||||
...ele,
|
||||
children: <SubjectTableComponent title={ele.label} {...{ curObject, objects, onSearchSubmit }} />
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
});
|
@ -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 (
|
||||
<>
|
||||
<KPISettings {...{ searchProps, objects: bu, KPISubjects }} {...props} />
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,263 @@
|
||||
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 { 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} 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': {},
|
||||
shows: ['DateType', 'years'],
|
||||
'fieldProps': {
|
||||
DepartmentList: { show_all: false },
|
||||
WebCode: { show_all: false },
|
||||
years: { hide_vs: true },
|
||||
},
|
||||
}}
|
||||
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,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 (
|
||||
<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*100 },
|
||||
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,
|
||||
};
|
||||
});
|
||||
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 (
|
||||
<>
|
||||
<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) => {
|
||||
// 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);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col className="gutter-row mb-n1 p-none mt-1 align_center" span={24}>
|
||||
<Button className="mt-1 mb-1 align_center" disabled={!editOpen} type="primary" key="save" onClick={onTableSubmit}>
|
||||
保存数据
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
});
|
@ -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);
|
@ -0,0 +1,133 @@
|
||||
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 { objectMapper } from './../../utils/commons';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
let timeout;
|
||||
let currentValue;
|
||||
|
||||
function curl(opts, callback) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
currentValue = opts.value;
|
||||
|
||||
function fake() {
|
||||
// console.log(currentValue, opts.value);
|
||||
if (currentValue === opts.value && opts.value === '空') {
|
||||
const _p = [{ 'key': '0', 'label': '空' }];
|
||||
return callback(_p);
|
||||
}
|
||||
const param = {
|
||||
code: 'utf-8',
|
||||
q: opts.value,
|
||||
};
|
||||
// const str = new URLSearchParams({
|
||||
// code: 'utf-8',
|
||||
// q: opts.value,
|
||||
// }).toString();
|
||||
|
||||
fetchJSON(`${opts.url}`, param)
|
||||
.then(d => {
|
||||
if (currentValue === opts.value) {
|
||||
const result = objectMapper(d.result, opts.map) || [];
|
||||
callback(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
timeout = setTimeout(fake, 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步请求的下拉菜单, 可搜索
|
||||
* @property {array} defaultOptions 默认选项 [{key: '', label: '' }]
|
||||
* @property {string} url
|
||||
* @property {object} map 异步结果的字段转换定义
|
||||
* @property {boolean} autoGet 首次默认请求
|
||||
* @property {string} resultkey 结果的根字段
|
||||
*/
|
||||
class SearchInput extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: this.props.defaultOptions || [],
|
||||
value: undefined,
|
||||
autoData: this.props.defaultOptions || [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autoGet === true) {
|
||||
const { map, resultkey } = this.props;
|
||||
const mapKey = Object.keys(map).reduce((r, v) => ({ ...r, [v]: { key: map[v] } }), {});
|
||||
curl({ value: '', url: this.props.url || '', map: mapKey, resultkey }, (data) =>
|
||||
this.setState({ data, autoData: data }, () => (typeof this.props.onSearchAfter === 'function' ? this.props.onSearchAfter(data, this.state.value) : ''))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.value !== prevProps.value) {
|
||||
this.setState({ value: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
handleClear = () => {
|
||||
this.setState({ data: this.state.autoData });
|
||||
};
|
||||
|
||||
handleSearch = value => {
|
||||
if ( ! this.props.url && this.props.defaultOptions?.length) {
|
||||
const f = this.props.defaultOptions.filter(r => String(r.label).indexOf(value) !== -1);
|
||||
this.setState({ data: f || [] });
|
||||
return false;
|
||||
}
|
||||
const { map, resultkey } = this.props;
|
||||
const mapKey = Object.keys(map).reduce((r, v) => ({ ...r, [v]: { key: map[v] } }), {});
|
||||
if (value) {
|
||||
curl({ value, url: this.props.url || '', map: mapKey, resultkey }, (data) =>
|
||||
this.setState({ data }, () => (typeof this.props.onSearchAfter === 'function' ? this.props.onSearchAfter(data, this.state.value) : ''))
|
||||
);
|
||||
} else {
|
||||
this.setState({ data: this.state.autoData || [] });
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = (value, option) => {
|
||||
this.setState({ value }, () => this.props.onChange(value, option));
|
||||
};
|
||||
|
||||
render() {
|
||||
const options = this.state.data.map(d => <Option key={d.key} extradata={d.options}>{d.label}</Option>);
|
||||
const { onSearchAfter, defaultOptions, autoGet, ...props } = this.props;
|
||||
return (
|
||||
<Select
|
||||
{...props}
|
||||
style={this.props.style || { width: '100%' }}
|
||||
showSearch
|
||||
labelInValue
|
||||
value={this.state.value || this.props.value}
|
||||
placeholder={this.props.placeholder}
|
||||
defaultActiveFirstOption={false}
|
||||
showArrow={false}
|
||||
filterOption={false}
|
||||
onSearch={this.handleSearch}
|
||||
onChange={this.handleChange}
|
||||
notFoundContent={null}
|
||||
allowClear={true}
|
||||
onClear={this.handleClear}
|
||||
>
|
||||
{options}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default observer(SearchInput);
|
@ -0,0 +1,366 @@
|
||||
import { createContext, useContext, useEffect } from 'react';
|
||||
import { toJS } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
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';
|
||||
// import locale from 'antd/es/date-picker/locale/zh_CN';
|
||||
import BusinessSelect from './BusinessSelect';
|
||||
import BusinessUnitSelect from './BusinessUnitSelect';
|
||||
import GroupSelect from './GroupSelect';
|
||||
import SiteSelect from './SiteSelect';
|
||||
import DateTypeSelect from './DataTypeSelect';
|
||||
import DatePickerCharts from './DatePickerCharts';
|
||||
import YearPickerCharts from './YearPickerCharts';
|
||||
import SearchInput from './Input';
|
||||
import { objectMapper, at, empty } from './../../utils/commons';
|
||||
|
||||
import './search.css';
|
||||
|
||||
const EditableContext = createContext();
|
||||
const Option = Select.Option;
|
||||
|
||||
/**
|
||||
* 搜索表单
|
||||
* @property defaultValue { initialValue, fieldProps, hides, shows, sort }
|
||||
* * {object} initialValue 默认值
|
||||
* * {object} fieldProps 表单项属性
|
||||
* * {array} hides 隐藏的表单项
|
||||
* * {array} shows 显示的表单项
|
||||
* * {object} sort 表单项排序
|
||||
* @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: '',
|
||||
initialValue: '',
|
||||
fieldProps: '',
|
||||
hides: [],
|
||||
shows: [],
|
||||
...props.defaultValue,
|
||||
};
|
||||
const { onSubmit, confirmText } = props;
|
||||
|
||||
const formValuesMapper = (values) => {
|
||||
console.log('Received values of form, origin form value: ', values);
|
||||
const destinationObject = {
|
||||
'DateType': {
|
||||
key: 'DateType',
|
||||
transform: (value) => value?.key || '',
|
||||
default: '',
|
||||
},
|
||||
'HTBusinessUnits': {
|
||||
key: 'HTBusinessUnits',
|
||||
transform: (value) => {
|
||||
return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? (!isNaN(parseInt(value.key), 10) ? value.key : '') : '-1';
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
'businessUnits': {
|
||||
key: 'businessUnits',
|
||||
transform: (value) => {
|
||||
return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? (!isNaN(parseInt(value.key), 10) ? value.key : '') : '-1';
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
'DepartmentList': {
|
||||
key: 'DepartmentList',
|
||||
transform: (value) => {
|
||||
return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.key : 'ALL';
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
'WebCode': {
|
||||
key: 'WebCode',
|
||||
transform: (value) => {
|
||||
return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.key : 'ALL';
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
'IncludeTickets': {
|
||||
key: 'IncludeTickets',
|
||||
transform: (value) => value?.key || '',
|
||||
default: '',
|
||||
},
|
||||
'operator': {
|
||||
key: 'operator',
|
||||
transform: (value) => value?.key || '',
|
||||
default: '',
|
||||
},
|
||||
'applyDate': [
|
||||
{
|
||||
key: 'Date1',
|
||||
transform: (value) => (value === '' || !Array.isArray(value) ? undefined : moment(value[0]).format(DATE_FORMAT)),
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
key: 'Date2',
|
||||
transform: (value) => (value === '' || !Array.isArray(value) ? undefined : moment(value[1]).format(`${DATE_FORMAT} 23:59:59`)),
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
'applyDate2': [
|
||||
{
|
||||
key: 'DateDiff1',
|
||||
transform: (value) => (value === '' || !Array.isArray(value) ? undefined : value[0] ? moment(value[0]).format(DATE_FORMAT) : undefined),
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
key: 'DateDiff2',
|
||||
transform: (value) => (value === '' || !Array.isArray(value) ? undefined : value[1] ? moment(value[1]).format(`${DATE_FORMAT} 23:59:59`) : undefined),
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
'year': [
|
||||
{
|
||||
key: 'Date1',
|
||||
transform: (value) => (value ? moment(value).format(`YYYY-01-01`) : undefined),
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
key: 'Date2',
|
||||
transform: (value) => (value ? moment(value).format(`YYYY-12-31 23:59:59`) : undefined),
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
'yearDiff': [
|
||||
{
|
||||
key: 'DateDiff1',
|
||||
transform: (value) => (value ? moment(value).format(`YYYY-01-01`) : undefined),
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
key: 'DateDiff2',
|
||||
transform: (value) => (value ? moment(value).format(`YYYY-12-31 23:59:59`) : undefined),
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
'country': {
|
||||
key: 'country',
|
||||
transform: (value) => value?.key || '',
|
||||
default: '',
|
||||
},
|
||||
};
|
||||
let dest = {};
|
||||
const { applyDate, applyDate2, year, yearDiff, ...omittedValue } = values;
|
||||
dest = { ...omittedValue, ...objectMapper(values, destinationObject) };
|
||||
for (const key in dest) {
|
||||
if (Object.prototype.hasOwnProperty.call(dest, key)) {
|
||||
dest[key] = typeof dest[key] === 'string' ? (dest[key] || '').trim() : dest[key];
|
||||
}
|
||||
}
|
||||
// omit empty
|
||||
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);
|
||||
return dest;
|
||||
};
|
||||
|
||||
const onFinish = (values) => {
|
||||
const dest = formValuesMapper(values);
|
||||
const str = new URLSearchParams(dest).toString();
|
||||
searchFormStore.setFormValues(values);
|
||||
searchFormStore.setFormValuesToSub(dest);
|
||||
if (typeof onSubmit === 'function') {
|
||||
onSubmit(null, dest, values, str);
|
||||
}
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
form.setFieldsValue({
|
||||
// 'DateType': undefined,
|
||||
});
|
||||
};
|
||||
const onValuesChange = (...args) => {
|
||||
const [changedValues, allValues] = args;
|
||||
console.log('form onValuesChange', args);
|
||||
searchFormStore.setFormValues(allValues);
|
||||
};
|
||||
|
||||
return (
|
||||
// layout="inline"
|
||||
<Form form={form} name="advanced_search" className="orders-search-form" onFinish={onFinish} onValuesChange={onValuesChange}>
|
||||
<EditableContext.Provider value={form}>
|
||||
<Row gutter={10} style={{ background: '#f9fafa', margin: '0px 0px 10px 0px', padding: '16px 8px' }}>
|
||||
{getFields({ sort, initialValue, hides, shows, fieldProps, form })}
|
||||
{/* 'textAlign': 'right' */}
|
||||
<Col flex="1 0 120px" style={{ padding: '0px 5px' }}>
|
||||
<Space align="center">
|
||||
<Button size={'middle'} type="primary" icon={<SearchOutlined />} htmlType="submit">
|
||||
{confirmText || '统计'}
|
||||
</Button>
|
||||
{/* <Button size="small" onClick={onReset}>
|
||||
重置
|
||||
</Button> */}
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</EditableContext.Provider>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
function getFields(props) {
|
||||
const { fieldProps, form } = props;
|
||||
const bigCol = 4 * 2;
|
||||
const midCol = 6;
|
||||
const layoutProps = {
|
||||
gutter: { xs: 8, sm: 8, lg: 16 },
|
||||
lg: { span: 4 },
|
||||
sm: { span: 12 },
|
||||
xs: { span: 24 },
|
||||
};
|
||||
const item = (name, sort = 0, render, col) => {
|
||||
const customCol = col || 4;
|
||||
return {
|
||||
'key': '',
|
||||
sort,
|
||||
name,
|
||||
render,
|
||||
'hide': false,
|
||||
'col': { lg: { span: customCol } },
|
||||
};
|
||||
};
|
||||
let baseChildren = [];
|
||||
baseChildren = [
|
||||
item(
|
||||
'HTBusinessUnits',
|
||||
99,
|
||||
<Form.Item name={`HTBusinessUnits`} initialValue={at(props, 'initialValue.HTBusinessUnits')[0] || undefined}>
|
||||
<BusinessUnitSelect {...fieldProps.HTBusinessUnits} />
|
||||
</Form.Item>
|
||||
),
|
||||
item(
|
||||
'businessUnits',
|
||||
99,
|
||||
<Form.Item name={`businessUnits`} initialValue={at(props, 'initialValue.businessUnits')[0] || undefined}>
|
||||
<BusinessSelect {...fieldProps.businessUnits} />
|
||||
</Form.Item>
|
||||
),
|
||||
item(
|
||||
'DepartmentList',
|
||||
99,
|
||||
<Form.Item name={`DepartmentList`} initialValue={at(props, 'initialValue.DepartmentList')[0] || (fieldProps?.DepartmentList?.show_all ? { key: 'ALL', label: '所有小组' } : undefined)}>
|
||||
<GroupSelect {...fieldProps.DepartmentList} />
|
||||
</Form.Item>
|
||||
),
|
||||
item(
|
||||
'WebCode',
|
||||
99,
|
||||
<Form.Item name={`WebCode`} initialValue={at(props, 'initialValue.WebCode')[0] || (fieldProps?.WebCode?.show_all ? { key: 'ALL', label: '所有来源' } : undefined)}>
|
||||
<SiteSelect {...fieldProps.WebCode} />
|
||||
</Form.Item>
|
||||
),
|
||||
item(
|
||||
'IncludeTickets',
|
||||
99,
|
||||
// value={orders_store.include_tickets} onChange={orders_store.handleChange_include_tickets}
|
||||
<Form.Item name={`IncludeTickets`} initialValue={at(props, 'initialValue.IncludeTickets')[0] || { key: '1', label: '含门票' }}>
|
||||
<Select style={{ width: '100%' }} placeholder="是否含门票" labelInValue>
|
||||
<Option key="1" value="1">
|
||||
含门票
|
||||
</Option>
|
||||
<Option key="0" value="0">
|
||||
不含门票
|
||||
</Option>
|
||||
</Select>
|
||||
</Form.Item>,
|
||||
2
|
||||
),
|
||||
//
|
||||
item(
|
||||
'DateType',
|
||||
99,
|
||||
<Form.Item name={`DateType`} initialValue={at(props, 'initialValue.DateType')[0] || { key: 'applyDate', label: '提交日期' }}>
|
||||
<DateTypeSelect />
|
||||
</Form.Item>,
|
||||
2
|
||||
),
|
||||
item(
|
||||
'dates',
|
||||
99,
|
||||
<Form.Item>
|
||||
<DatePickerCharts isform={true} {...fieldProps.dates} form={form} />
|
||||
</Form.Item>,
|
||||
midCol
|
||||
),
|
||||
item(
|
||||
'years',
|
||||
99,
|
||||
<Form.Item>
|
||||
{/* <DatePicker picker="year" placeholder='年份' /> */}
|
||||
<YearPickerCharts {...fieldProps.years} />
|
||||
</Form.Item>,
|
||||
2
|
||||
),
|
||||
item(
|
||||
'months',
|
||||
99,
|
||||
<Form.Item>
|
||||
<DatePicker picker="month" placeholder="月份" />
|
||||
</Form.Item>,
|
||||
2
|
||||
),
|
||||
item(
|
||||
'operator',
|
||||
99,
|
||||
<Form.Item name={'operator'}>
|
||||
<SearchInput autoGet url="/service-Analyse2/GetOperatorInfo" map={{ 'op_id': 'key', 'cn_name': 'label' }} resultkey={'result'} placeholder="输入搜索顾问: 中/英名字" />
|
||||
</Form.Item>
|
||||
),
|
||||
item(
|
||||
'country',
|
||||
99,
|
||||
<Form.Item name={'country'}>
|
||||
<SearchInput autoGet url="/service-Analyse2/GetCountryInfo" map={{ 'c_id': 'key', 'cn_name': 'label' }} resultkey={'result'} placeholder="输入搜索国籍: 中/英名字" />
|
||||
</Form.Item>
|
||||
),
|
||||
];
|
||||
baseChildren = baseChildren
|
||||
.map((x) => {
|
||||
x.hide = false;
|
||||
if (props.sort === undefined) {
|
||||
return x;
|
||||
}
|
||||
const tmpSort = props.sort;
|
||||
for (const key in tmpSort) {
|
||||
if (Object.prototype.hasOwnProperty.call(tmpSort, key)) {
|
||||
if (x.name === key) {
|
||||
x.sort = tmpSort[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return x;
|
||||
})
|
||||
.map((x) => {
|
||||
if (props.hides.length === 0 && props.shows.length === 0) {
|
||||
return x;
|
||||
}
|
||||
if (props.hides.length === 0) {
|
||||
x.hide = !props.shows.includes(x.name);
|
||||
} else if (props.shows.length === 0) {
|
||||
x.hide = props.hides.includes(x.name);
|
||||
}
|
||||
return x;
|
||||
})
|
||||
.filter((x) => !x.hide)
|
||||
.sort((a, b) => {
|
||||
return a.sort < b.sort ? -1 : 1;
|
||||
});
|
||||
const children = [];
|
||||
const leftStyle = {}; // { borderRight: '1px solid #dedede' };
|
||||
for (let i = 0; i < baseChildren.length; i++) {
|
||||
let style = { padding: '0px 2px' };
|
||||
style = i % 2 === 0 && baseChildren[i].col === 12 ? { ...style, ...leftStyle } : style;
|
||||
style = !baseChildren[i].hide ? { ...style, display: 'block' } : { ...style, display: 'none' };
|
||||
const Item = (
|
||||
<Col key={String(i)} style={style} {...layoutProps} {...baseChildren[i].col}>
|
||||
{baseChildren[i].render}
|
||||
</Col>
|
||||
);
|
||||
children.push(Item);
|
||||
}
|
||||
return children;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
.ant-form-item{
|
||||
margin: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"get|/service-web/QueryData/GetOrderCount/test": {
|
||||
"errcode": "",
|
||||
"errmsg": "",
|
||||
"data": null,
|
||||
"ordercount1|10": [
|
||||
{
|
||||
"id|10-99": 1,
|
||||
"key|10-99": 1,
|
||||
"orderCount|1-100": 5,
|
||||
"WebCode|1": ["ah", "cht"],
|
||||
"groups": "groups1",
|
||||
"ApplyDate": "@date(\"2023-08-dd\")"
|
||||
}
|
||||
],
|
||||
"ordercount2|10": [
|
||||
{
|
||||
"id|100-200": 1,
|
||||
"key|100-200": 1,
|
||||
"orderCount|1-100": 5,
|
||||
"WebCode|1": ["ah", "cht"],
|
||||
"groups": "groups2",
|
||||
"ApplyDate": "@date(\"2023-08-dd\")"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"get|/service-web/baseinfo/operator/test": {
|
||||
"errcode": 0,
|
||||
"errmsg": "",
|
||||
"data": null,
|
||||
"loading": null,
|
||||
"result|10": [
|
||||
{
|
||||
"mobile": "13@integer(99999999,999999999)",
|
||||
"op_id": "@integer(10,99)",
|
||||
"cn_name": "@cname",
|
||||
"email": "@email",
|
||||
"en_name": "@first",
|
||||
"code": "@word(2,3)",
|
||||
"key": "@increment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import { makeAutoObservable, runInAction, toJS } from 'mobx';
|
||||
import * as req from '../utils/request';
|
||||
import { isEmpty, sortBy, objectMapper } from '../utils/commons';
|
||||
|
||||
const modelMapper = {
|
||||
'operator': {
|
||||
url: '/service-Analyse2/GetOperatorInfo',
|
||||
mapper: {
|
||||
op_id: [{ key: 'key' }, { key: 'value' }],
|
||||
cn_name: { key: 'label' },
|
||||
en_name: { key: 'label_alias' },
|
||||
},
|
||||
},
|
||||
'country': {
|
||||
url: '/service-Analyse2/GetCountryInfo',
|
||||
mapper: {
|
||||
c_id: [{ key: 'key' }, { key: 'value' }],
|
||||
cn_name: { key: 'label' },
|
||||
en_name: { key: 'label_alias' },
|
||||
},
|
||||
},
|
||||
'vendor': {
|
||||
url: '/service-web/QueryData/GetVEIName',
|
||||
mapper: {
|
||||
CAV_VEI_SN: [{ key: 'key' }, { key: 'value' }],
|
||||
VEI2_CompanyBN: { key: 'label' },
|
||||
},
|
||||
},
|
||||
'creditcardbilltype': {
|
||||
url: '/service-web/QueryData/GetCreditCardBillType',
|
||||
mapper: {
|
||||
cb_billtype: [{ key: 'key' }, { key: 'value' }, { key: 'label' }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class DictData {
|
||||
constructor(appStore) {
|
||||
this.appStore = appStore;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
async fetchDictData(model = '', param={}) {
|
||||
const mkey = model.toLowerCase();
|
||||
this[mkey] = { loading: true, dataSource: [] };
|
||||
const json = await req.fetchJSON(modelMapper[mkey].url, param);
|
||||
if (json.errcode === 0) {
|
||||
runInAction(() => {
|
||||
this[mkey].loading = false;
|
||||
this[mkey].dataSource = objectMapper(json.result, modelMapper[mkey].mapper);
|
||||
console.log({ loading: false, ...json }, model, 'DictData', toJS(this[mkey]));
|
||||
});
|
||||
}
|
||||
return this[mkey];
|
||||
}
|
||||
|
||||
data = {};
|
||||
operator = { loading: false, dataSource: [] };
|
||||
country = { loading: false, dataSource: [] };
|
||||
vendor = { loading: false, dataSource: [] };
|
||||
creditcardbilltype = { loading: false, dataSource: [] };
|
||||
}
|
||||
export default DictData;
|
@ -0,0 +1,61 @@
|
||||
import { makeAutoObservable, runInAction, toJS } from 'mobx';
|
||||
import * as req from '../utils/request';
|
||||
import { isEmpty, sortBy } from '../utils/commons';
|
||||
|
||||
const modelMapper = {
|
||||
'tourDays': { url: '/service-Analyse2/GetTradeApartByTourDays' },
|
||||
'PML': { url: '/service-Analyse2/GetTradeApartByPML' },
|
||||
'ConfirmDays': { url: '/service-Analyse2/GetTradeApartByConfirmDays' },
|
||||
'ApplyDays': { url: '/service-Analyse2/GetTradeApartByApplyDays' },
|
||||
'PersonNum': { url: '/service-Analyse2/GetTradeApartByPersonNum' },
|
||||
'destination': { url: '/service-Analyse2/GetTradeApartByDestination' },
|
||||
'GlobalDestination': { url: '/service-Analyse2/GetTradeApartByGlobalDestination' },
|
||||
};
|
||||
class Distribution {
|
||||
constructor(appStore){
|
||||
this.appStore = appStore;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
async getData(param){
|
||||
const mkey = this.curTab;
|
||||
this[mkey] = { loading: true, dataSource: [] };
|
||||
const json = await req.fetchJSON(modelMapper[mkey].url, param);
|
||||
if (json.errcode === 0) {
|
||||
runInAction(() => {
|
||||
const dataLength = json.result.length;
|
||||
this[mkey].loading = false;
|
||||
this[mkey].originData = json.result;
|
||||
this[mkey].dataSource = dataLength > 20 ? json.result.slice(0, 30) : json.result;
|
||||
});
|
||||
}
|
||||
return this[mkey];
|
||||
|
||||
}
|
||||
|
||||
resetData() {
|
||||
this.tourDays = { loading: false, dataSource: [] };
|
||||
this.PML = { loading: false, dataSource: [] };
|
||||
this.ConfirmDays = { loading: false, dataSource: [] };
|
||||
this.ApplyDays = { loading: false, dataSource: [] };
|
||||
this.PersonNum = { loading: false, dataSource: [] };
|
||||
this.destination = { loading: false, dataSource: [] };
|
||||
this.GlobalDestination = { loading: false, dataSource: [] };
|
||||
}
|
||||
|
||||
curTab = 'tourDays';
|
||||
setCurTab(v) {
|
||||
this.curTab = v;
|
||||
}
|
||||
|
||||
pageLoading = false;
|
||||
|
||||
tourDays = { loading: false, dataSource: [] };
|
||||
PML = { loading: false, dataSource: [] };
|
||||
ConfirmDays = { loading: false, dataSource: [] };
|
||||
ApplyDays = { loading: false, dataSource: [] };
|
||||
PersonNum = { loading: false, dataSource: [] };
|
||||
destination = { loading: false, dataSource: [] };
|
||||
GlobalDestination = { loading: false, dataSource: [] };
|
||||
}
|
||||
export default Distribution;
|
@ -1,106 +1,127 @@
|
||||
import { makeAutoObservable, runInAction, toJS } from 'mobx';
|
||||
import * as req from '../utils/request';
|
||||
import { isEmpty, sortBy, groupBy } 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() {
|
||||
console.log('ssssssssssss');
|
||||
console.log(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);
|
||||
const data = {
|
||||
'kpis': tableData
|
||||
// [
|
||||
// {
|
||||
// 'kpi_id': '1',
|
||||
// 'object': 'sales',
|
||||
// 'subject': 'orderCount',
|
||||
// 'object_name': '刘莎',
|
||||
// 'object_id': '15',
|
||||
// 'date_type': 'confirmDate',
|
||||
// 'start_date': '2024-01-01',
|
||||
// 'end_date': '2024-03-31 23:59',
|
||||
// 'value': '10000',
|
||||
// },
|
||||
// ],
|
||||
};
|
||||
// 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');
|
||||
});
|
||||
}
|
||||
});
|
||||
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 };
|
||||
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 = {
|
||||
date_type: 'confirmDate',
|
||||
start_date: '2024-01-01',
|
||||
end_date: '2024-12-01',
|
||||
getList(param = {}) {
|
||||
const _param = {
|
||||
date_type: 'applyDate',
|
||||
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,
|
||||
};
|
||||
// req.fetchJSON('/service-Analyse2/getkpi/test', param).then((json) => {
|
||||
req.fetchJSON('/service-Analyse2/getkpi', param).then((json) => {
|
||||
this.listLoading = true;
|
||||
this.pageData = [];
|
||||
return req.fetchJSON('/service-Analyse2/getkpi', _param).then((json) => {
|
||||
if (json.errcode === 0) {
|
||||
runInAction(() => {
|
||||
const result = json.result.map((row) => ({ ...row, yearIndex: moment(row.start_date).year(), monthIndex: moment(row.start_date).month() + 1 }));
|
||||
const byYear = groupBy(result, (ele) => ele.yearIndex);
|
||||
const yearData = {};
|
||||
Object.keys(byYear).map((_yearVal) => {
|
||||
const _objRet = groupBy(byYear[_yearVal], (ele) => `${ele.object_id}`);
|
||||
Object.keys(_objRet).map((_obj) => {
|
||||
_objRet[_obj] = _objRet[_obj].reduce((r, v) => ({ ...r, ...v, [`M${v.monthIndex}`]: v.value, [`M${v.monthIndex}_id`]: v.kpi_id }), {});
|
||||
return _obj;
|
||||
});
|
||||
Object.assign(yearData, { [_yearVal]: Object.values(_objRet) });
|
||||
return _yearVal;
|
||||
});
|
||||
console.log(111, yearData);
|
||||
this.pageData = yearData[this.settingYear];
|
||||
this.listLoading = false;
|
||||
this.originData = json.result;
|
||||
const yearData = parseKPI(json.result, ['subject', 'object_id']);
|
||||
console.log(111, yearData, yearData[this.settingYear]);
|
||||
this.pageData = yearData?.[this.settingYear]?.[this.settingSubject] || [];
|
||||
});
|
||||
}
|
||||
return this.pageData;
|
||||
});
|
||||
}
|
||||
|
||||
handleTableEdit(data) {
|
||||
console.log('handle change ', data);
|
||||
this.pageData = data;
|
||||
settingYear = moment().year();
|
||||
setSettingYear(v) {
|
||||
this.settingYear = v;
|
||||
}
|
||||
|
||||
settingYear = 2024;
|
||||
data = [];
|
||||
objectData = [];
|
||||
listLoading = false;
|
||||
setListLoading(v) {
|
||||
this.listLoading = v;
|
||||
}
|
||||
|
||||
settingSubject = 'sum_profit';
|
||||
setSettingSubject(v) {
|
||||
this.settingSubject = v;
|
||||
}
|
||||
|
||||
originData =[];
|
||||
pageData = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 把kpi数据按照对象和类型转换格式
|
||||
* @param {array} keyArr 转换的字段的顺序, 返回结果的key层级
|
||||
* @example
|
||||
* * parseKPI(json.result, ['object_id', 'subject']);
|
||||
* // => { 2023: { 114: [{...}, {...}], 216: [...] } }
|
||||
* * parseKPI(json.result, ['subject', 'object_id']);
|
||||
* // => { 2023: { in_order_count: [{...}, {...}], sum_profit: [...] } }
|
||||
*/
|
||||
export const parseKPI = (kpis, keyArr = []) => {
|
||||
const result = kpis.map((row) => ({
|
||||
...row,
|
||||
yearIndex: moment(row.start_date).year(),
|
||||
monthIndex: moment(row.start_date).month() + 1,
|
||||
monthRange: [moment(row.start_date).month() + 1, moment(row.end_date).month() + 1],
|
||||
fullYear: moment(row.start_date).month() === 0 && moment(row.end_date).month() === 11,
|
||||
}));
|
||||
const byYear = groupBy(result, (ele) => ele.yearIndex);
|
||||
const yearData = {};
|
||||
const initialPercentKey = new Array(12).fill(1).reduce((r, v, i) => ({ ...r, [`M${i+1}Percent`]: 0 }), {});
|
||||
const ret = {};
|
||||
const [key0, key1] = keyArr;
|
||||
Object.keys(byYear).map((_yearVal) => {
|
||||
const _subjectRet = groupBy(byYear[_yearVal], (ele) => `${ele[key0]}`);
|
||||
Object.keys(_subjectRet).map((_subject) => {
|
||||
const subjectObject = groupBy(_subjectRet[_subject], (row) => row[key1]);
|
||||
const afterGroup = Object.keys(subjectObject).map((oID) => {
|
||||
const _ByFull = subjectObject[oID].reduce((r, v) => {
|
||||
(r[String(v.fullYear)] || (r[String(v.fullYear)] = [])).push(v);
|
||||
return r;
|
||||
}, {});
|
||||
const kpiYear = (_ByFull?.true || []).reduce((r, v) => {
|
||||
// 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: (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];
|
||||
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;
|
||||
});
|
||||
Object.assign(yearData, { [_yearVal]: ret });
|
||||
return _yearVal;
|
||||
});
|
||||
return yearData;
|
||||
};
|
||||
export default KPI;
|
||||
|
@ -0,0 +1,118 @@
|
||||
import { useContext, useEffect, useMemo } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { stores_Context } from '../config';
|
||||
import { Row, Col, Spin, Space, Radio, Tabs, Table } from 'antd';
|
||||
import { RingProgress } from '@ant-design/plots';
|
||||
import SearchForm from './../components/search/SearchForm';
|
||||
import "./kpi.css";
|
||||
import { empty } from '../utils/commons';
|
||||
|
||||
const apartOptions = [
|
||||
{ key: 'tourDays', value: 'tourDays', label: '团天数', },
|
||||
{ key: 'PML', value: 'PML', label: '单团毛利', },
|
||||
{ key: 'ConfirmDays', value: 'ConfirmDays', label: '成团周期', },
|
||||
{ key: 'ApplyDays', value: 'ApplyDays', label: '预定周期', },
|
||||
{ key: 'PersonNum', value: 'PersonNum', label: '人等', },
|
||||
{ key: 'destination', value: 'destination', label: '国内目的地', },
|
||||
{ key: 'GlobalDestination', value: 'GlobalDestination', label: '海外目的地', },
|
||||
];
|
||||
|
||||
export default observer((props) => {
|
||||
const { date_picker_store: searchFormStore, DistributionStore } = useContext(stores_Context);
|
||||
const { formValues, formValuesToSub } = searchFormStore;
|
||||
const { curTab } = DistributionStore;
|
||||
const pageRefresh = (obj) => {
|
||||
DistributionStore.getData({
|
||||
DateType: 'applyDate',
|
||||
Date1: searchFormStore.start_date.startOf('year').format('YYYY-MM-DD'),
|
||||
Date2: searchFormStore.end_date.endOf('year').format('YYYY-MM-DD 23:59'),
|
||||
...(obj || formValuesToSub),
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (empty(DistributionStore[curTab].dataSource)) {
|
||||
pageRefresh();
|
||||
}
|
||||
}, [curTab]);
|
||||
|
||||
useEffect(() => {
|
||||
DistributionStore.resetData();
|
||||
return () => {};
|
||||
}, [formValuesToSub]);
|
||||
|
||||
|
||||
const onTabsChange = (tab) => {
|
||||
DistributionStore.setCurTab(tab);
|
||||
};
|
||||
const RingProgressConfig = {
|
||||
height: 60,
|
||||
width: 60,
|
||||
autoFit: false,
|
||||
// percent: Number(_)/100,
|
||||
color: ['#5B8FF9', '#E8EDF3'],
|
||||
};
|
||||
const columns = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'label',
|
||||
},
|
||||
{ title: '团数', dataIndex: 'ConfirmOrder'},
|
||||
{ title: '业绩', dataIndex: 'SumML', render1: (v) => `1`},
|
||||
{ title: '团数占比', dataIndex: 'ConfirmOrderPercent', render: (v) => <RingProgress {...RingProgressConfig} percent={v/100} /> },
|
||||
{ title: '业绩占比', dataIndex: 'SumMLPercent', render: (v) => <RingProgress {...RingProgressConfig} percent={v/100} />},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16} style={{ margin: '-16px -8px' }}>
|
||||
{/* style={{ margin: '-16px -8px', padding: 0 }} */}
|
||||
<Col className="gutter-row" span={24}>
|
||||
<SearchForm
|
||||
defaultValue={{
|
||||
initialValue: {
|
||||
...formValues,
|
||||
},
|
||||
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'years'],
|
||||
fieldProps: {
|
||||
DepartmentList: { show_all: true },
|
||||
WebCode: { show_all: true },
|
||||
years: { hide_vs: true },
|
||||
},
|
||||
}}
|
||||
onSubmit={(_err, obj, form, str) => {
|
||||
pageRefresh(obj);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<section>
|
||||
<Tabs
|
||||
onChange={onTabsChange}
|
||||
type="card"
|
||||
items={apartOptions.map((ele, i) => {
|
||||
// const ObjectItemPanel = objectComponents[ele.key];
|
||||
return {
|
||||
...ele,
|
||||
children: (
|
||||
<Spin spinning={DistributionStore.pageLoading}>
|
||||
{/* <ObjectItemPanel title={ele.label} {...{ curObject, onSearchSubmit, objects: retObjects }} /> */}
|
||||
|
||||
<Table
|
||||
id="table_to_xlsx_sale"
|
||||
dataSource={DistributionStore[curTab].dataSource}
|
||||
columns={columns}
|
||||
size="small"
|
||||
rowKey={(record) => record.label}
|
||||
loading={DistributionStore[curTab].loading}
|
||||
pagination={false}
|
||||
scroll={{ x: '100%' }}
|
||||
/>
|
||||
</Spin>
|
||||
),
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +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…
Reference in New Issue