You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dashboard/src/views/Sale_KPI.jsx

262 lines
9.6 KiB
React

import React, { useContext, useState } from 'react';
import { Row, Col, Table, Select, Space, Typography, Progress, Spin, Divider, Button, Switch } from 'antd';
2 years ago
import { stores_Context } from '../config';
import { observer } from 'mobx-react';
import * as comm from '../utils/commons';
import SearchForm from './../components/search/SearchForm';
import { dataFieldAlias, overviewGroup } from '../libs/ht';
import Donut from './../components/Donut';
import LineWithKPI from '../components/LineWithKPI';
import { TableExportBtn } from './../components/Data';
const { Text } = Typography;
const overviewGroupKeys = overviewGroup.map(item => item.key);
2 years ago
const Sale_KPI = () => {
const { sale_store, date_picker_store: searchFormStore } = useContext(stores_Context);
const { formValues, siderBroken } = searchFormStore;
const { groupType, loading, operator, tableDataSource: dataSource } = sale_store.salesTrade;
const yearData = sale_store.salesTrade[groupType].reduce((r, ele) => r.concat(Object.values(ele.mData)), []);
const operatorObjects = operator.map((v) => ({ key: v.groupsKey, value: v.groupsKey, label: v.groupsLabel }));
const pageRefresh = async (queryData) => {
const overviewFlag = queryData.DepartmentList.toLowerCase() === 'all'
|| overviewGroupKeys.includes(queryData.DepartmentList.toLowerCase()); // queryData.DepartmentList.toLowerCase().includes(',');
const _groupType = overviewFlag ? 'overview' : 'dept';
sale_store.setGroupType(_groupType);
await sale_store.fetchOperatorTradeData(_groupType, { ...queryData, groupDateType: 'year' });
await sale_store.fetchOperatorTradeData('operator', { ...queryData, groupDateType: 'year' });
sale_store.setPickSales([]);
sale_store.setTableDataSource(false);
setIfmerge(false);
2 years ago
};
const [ifmerge, setIfmerge] = useState(false);
const monthCol = new Array(12).fill(1).map((_, index) => {
return {
title: `${index + 1}`,
dataIndex: `M${index + 1}Percent`,
valueType: 'digit',
width: '7.5em',
render: (_, row) => (
<Space direction={'vertical'}>
{/* 目标 */}
<div>
<Text italic type={'secondary'}>
{dataFieldAlias.SumML.formatter(row.mData[`month_${String(index + 1).padStart(2, '0')}`]?.MLKPIvalue || 0)}
</Text>
</div>
{/* 完成 */}
<div>{dataFieldAlias.SumML.formatter(row.mData[`month_${String(index + 1).padStart(2, '0')}`]?.SumML || 0)}</div>
{row.mData[`month_${String(index + 1).padStart(2, '0')}`]?.MLKPIrates || 0 ? (
<Progress
percent={row.mData[`month_${String(index + 1).padStart(2, '0')}`]?.MLKPIrates || 0}
size="small"
format={(percent) => `${row.mData[`month_${String(index + 1).padStart(2, '0')}`]?.MLKPIrates || 0}%`}
status={
row.mData[`month_${String(index + 1).padStart(2, '0')}`].MLKPIrates < 80
? 'exception'
: row.mData[`month_${String(index + 1).padStart(2, '0')}`].MLKPIrates < 100
? 'normal'
: 'success'
}
/>
) : (
'-'
)}
</Space>
),
};
});
const columns = [
{
title: ``,
dataIndex: 'groupsLabel',
editable: false,
width: '7.5em',
2 years ago
fixed: 'left',
},
{
title: '年度',
dataIndex: 'yearValue',
width: '10em',
2 years ago
fixed: 'left',
render: (_, row) => (
<Space direction={'vertical'}>
<div>
<Text italic type={'secondary'}>
<span style={{ marginRight: '.5em' }}>目标</span>
{dataFieldAlias.SumML.formatter(row.yData?.MLKPIvalue || 0)}
</Text>
</div>
<div>
<span style={{ marginRight: '.5em' }}>完成</span>
{dataFieldAlias.SumML.formatter(row.yData?.SumML || 0)}
</div>
{row.yData?.MLKPIrates || 0 ? (
<Progress
percent={row.yData?.MLKPIrates || 0}
size={'small'}
format={(percent) => `${row.yData?.MLKPIrates || 0}%`}
status={row.yData.MLKPIrates < 80 ? 'exception' : row.yData.MLKPIrates < 100 ? 'normal' : 'success'}
/>
) : (
'-'
)}
</Space>
),
},
...monthCol,
];
const columnsForExport = [
{
title: `#`,
dataIndex: 'groupsLabel',
editable: false,
width: '7.5em',
fixed: 'left',
},
{
title: '--',
dataIndex: 'rowLabel',
width: '10em',
fixed: 'left',
},
{
title: '年度',
dataIndex: 'yearML',
width: '10em',
fixed: 'left',
},
...new Array(12).fill(1).map((_, index) => {
return {
title: `${index + 1}`,
dataIndex: ['mData', `month_${String(index + 1).padStart(2, '0')}`], // , 'SumML'
valueType: 'digit',
width: '7.5em',
};
}),
];
const dataForExport = dataSource.reduce((r, ele) => {
const targetRow = {
groupsLabel: ele.groupsLabel,
rowLabel: '目标',
yearML: ele.yData.MLKPIvalue,
mData: Object.values(ele.mData).reduce((rt, et) => ({ ...rt, [`month_${et.dateVal}`]: et.MLKPIvalue }), {}),
};
const valRow = { groupsLabel: ele.groupsLabel, rowLabel: '完成', yearML: ele.yData.SumML, mData: Object.values(ele.mData).reduce((rt, et) => ({ ...rt, [`month_${et.dateVal}`]: et.SumML }), {}) };
const processRow = {
groupsLabel: ele.groupsLabel,
rowLabel: '进度(%)',
yearML: ele.yData.MLKPIrates,
mData: Object.values(ele.mData).reduce((rt, et) => ({ ...rt, [`month_${et.dateVal}`]: et.MLKPIrates }), {}),
};
r.push(targetRow, valRow, processRow);
return r;
}, []);
const lineConfig = { appendPadding: 10, xField: 'groupDateVal', yField: 'SumML', seriesField: 'groupsLabel', isGroup: true, smooth: false, meta: comm.cloneDeep(dataFieldAlias), };
2 years ago
return (
<div>
<Row gutter={16} className={siderBroken ? "" : "sticky-top"}>
2 years ago
<Col md={24} lg={24} xxl={24}>
<SearchForm
defaultValue={{
initialValue: {
...formValues,
...sale_store.searchValues,
2 years ago
},
shows: ['DateType', 'DepartmentList', 'WebCode', 'IncludeTickets', 'years'],
fieldProps: {
DepartmentList: { show_all: false, mode: 'multiple', col: 5 },
WebCode: { show_all: false, mode: 'multiple', col: 5 },
2 years ago
years: { hide_vs: true },
},
}}
onSubmit={(_err, obj, form, str) => {
sale_store.setSearchValues(obj, form);
2 years ago
pageRefresh(obj);
}}
/>
</Col>
</Row>
<Spin spinning={loading}>
<h2 style={{ marginTop: '.5em' }}>年度业绩组成和走势</h2>
<Row>
2 years ago
<Col className="gutter-row" md={8} sm={24} xs={24}>
<Donut
{...{ angleField: 'SumML', colorField: 'groupsLabel', label1: { style: { color: '#000000' }, type: 'spider', content: '{name}\n{percentage}' }, legend: false, label2: false }}
title={formValues.DepartmentList?.label}
dataSource={operator.map((row) => ({ ...row, SumML: row.yearML }))}
/>
</Col>
2 years ago
<Col className="gutter-row" md={16} sm={24} xs={24}>
<LineWithKPI dataSource={yearData} showKPI={true} {...lineConfig} {...{ legend: false }} />
</Col>
<Col className="gutter-row" md={24}>
2 years ago
<Row gutter={16}>
<Col flex={'12em'}><h2>顾问业绩走势</h2></Col>
<Col flex={'auto'}>
<Select
labelInValue
mode={'multiple'}
style={{ width: '100%' }}
placeholder={'选择顾问'}
onChange={sale_store.setPickSales}
value={sale_store.salesTrade.pickSales}
maxTagCount={1}
maxTagPlaceholder={(omittedValues) => ` + ${omittedValues.length} 更多...`}
allowClear={true}
>
{operatorObjects.map((ele) => (
<Select.Option key={ele.key} value={ele.value}>
{ele.label}
</Select.Option>
))}
</Select>
</Col>
</Row>
</Col>
<Col className="gutter-row" span={24}>
<LineWithKPI dataSource={sale_store.salesTrade.pickSalesData} showKPI={true} {...lineConfig} />
</Col>
</Row>
2 years ago
<Row>
<Col className="gutter-row" md={24}>
<Divider orientation="right">
{dataSource.length > 0 && (
<Switch
unCheckedChildren="各账户"
checkedChildren="合并"
key={'openOrMerge'}
checked={ifmerge}
onChange={(e) => {
sale_store.setTableDataSource(e);
setIfmerge(e);
}}
/>
)}
<TableExportBtn label={'sales kpi'} {...{ columns: columnsForExport, dataSource: dataForExport }} />
</Divider>
<Table
sticky={{ offsetHeader: 64 }}
key={`salesTradeTable`}
loading={loading}
columns={columns}
rowKey="groupsKey"
scroll={{
x: 1000,
}}
dataSource={dataSource}
pagination={false}
/>
</Col>
</Row>
</Spin>
2 years ago
</div>
);
};
export default observer(Sale_KPI);