薪酬統計

This commit is contained in:
黎永顺 2023-04-12 10:47:51 +08:00
parent 6846e81498
commit 8fe5f201c6
9 changed files with 386 additions and 112 deletions

View File

@ -9,3 +9,11 @@ export const dimensionGetForm = (params) => {
export const dimensionSave = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/dimension/save", params);
};
// 薪酬统计维度列表
export const dimensionList = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/dimension/list", params);
};
// 删除薪酬统计维度
export const dimensionDelete = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/dimension/delete", params);
};

View File

@ -18,7 +18,7 @@ export const condition = [
{
colSpan: 1,
conditionType: "SELECT",
domkey: ["dimCode"],
domkey: ["setting4Qualitative"],
fieldcol: 14,
label: "统计维度",
labelcol: 6,
@ -35,7 +35,19 @@ export const condition = [
labelcol: 6,
value: "",
rules: "required|string",
viewAttr: 3,
viewAttr: 3
},
{
colSpan: 1,
conditionType: "SELECT",
domkey: ["dimCode"],
fieldcol: 14,
label: "分组所属字段",
labelcol: 6,
options: [],
viewAttr: 2,
helpfulTip: "",
hide: true
},
{
colSpan: 1,
@ -45,10 +57,10 @@ export const condition = [
label: "描述",
labelcol: 6,
value: "",
viewAttr: 2,
},
viewAttr: 2
}
],
title: "基础设置",
defaultshow: true,
},
defaultshow: true
}
];

View File

@ -6,10 +6,8 @@
*/
import React, { Component } from "react";
import { WeaDialog } from "ecCom";
import { Button } from "antd";
import { condition } from "./conditions";
import { commonEnumList } from "../../../apis/ruleconfig";
import { dimensionGetForm } from "../../../apis/statistics";
import { Button, message } from "antd";
import { dimensionGetForm, dimensionSave } from "../../../apis/statistics";
import { getSearchs } from "../../../util";
import "../index.less";
@ -17,46 +15,90 @@ class DimensionSlide extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
date: ""
};
}
componentDidMount() {
this.props.initCondition();
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.visible !== this.props.visible && nextProps.visible) {
const promise = this.initCondition();
nextProps.form.initFormFields(condition);
if (nextProps.visible !== this.props.visible && nextProps.formId) this.dimensionGetForm({ id: nextProps.formId });
if (nextProps.visible !== this.props.visible && !nextProps.formId) {
nextProps.form.updateFields({
dimType: "QUALITATIVE"
});
nextProps.onChangeCondition("QUALITATIVE");
}
}
initCondition = async () => {
const [dimTypeEnum, dimCodeList] = await Promise.all([this.commonEnumList(), this.dimensionGetForm()]);
const { data: dimTypeData } = dimTypeEnum, { data: dimCodeData } = dimCodeList;
console.log(dimTypeEnum, dimCodeList, condition);
};
commonEnumList = () => {
const payload = {
enumClass: "com.engine.salary.report.enums.SalaryStatisticsDimensionTypeEnum"
};
return commonEnumList(payload);
};
dimensionGetForm = () => {
return dimensionGetForm();
dimensionGetForm = (payload) => {
dimensionGetForm(payload).then(({ status, data }) => {
if (status) {
const { baseForm: { data: formData } } = data;
const fields = _.map(this.props.condition[0].items, it => {
return it.domkey[0];
});
fields.map(item => {
if (item !== "setting4Qualitative") {
this.props.form.updateFields({
[item]: formData[item] || ""
});
} else {
this.props.form.updateFields({
setting4Qualitative: formData.statsDim
});
}
});
this.props.onChangeCondition(formData["dimType"], 1);
}
});
};
handleSave = () => {
const { condition, onCancel, formId } = this.props;
this.props.form.validateForm().then(f => {
console.log(f);
console.log(this.props.form.getFormParams());
if (f.isValid) {
console.log(this.props.form.getFormParams());
return;
const { setting4Qualitative, ...extraParams } = this.props.form.getFormParams();
const options = _.find(condition[0].items, item => item.domkey[0] === "setting4Qualitative").options;
const obj = _.find(options, item => item.key === setting4Qualitative);
const payload = {
id: formId,
...extraParams,
setting4Qualitative: { id: obj.key, name: obj.showname }
};
this.setState({ loading: true });
dimensionSave(payload).then(({ status, errormsg }) => {
this.setState({ loading: false });
if (status) {
message.success("保存成功");
onCancel(true);
this.props.form.resetForm();
} else {
message.error(errormsg || "保存失败");
}
}).catch(() => this.setState({ loading: false }));
} else {
f.showErrors();
this.setState({ date: new Date() });
}
});
};
formItemChange = (formObj) => {
const { onChangeCondition } = this.props;
const filedKey = _.keys(formObj)[0];
if (filedKey === "dimType") onChangeCondition(formObj[filedKey].value);
};
render() {
const { form } = this.props;
const { loading } = this.state;
const { form, condition } = this.props;
return (
<WeaDialog
{...this.props}
@ -66,11 +108,11 @@ class DimensionSlide extends Component {
title={
<div className="dimensionTitle">
<span>新建统计维度</span>
<Button type="primary" onClick={this.handleSave}>保存</Button>
<Button type="primary" onClick={this.handleSave} loading={loading}>保存</Button>
</div>
}
>
{getSearchs(form, condition, 1)}
{getSearchs(form, condition, 1, false, this.formItemChange)}
</WeaDialog>
);
}

View File

@ -5,13 +5,107 @@
* Date: 2023/4/11
*/
import React, { Component } from "react";
import { WeaTable } from "ecCom";
import { message, Modal } from "antd";
import { dimensionDelete, dimensionList } from "../../../apis/statistics";
import "../index.less";
class DimensionTable extends Component {
render() {
return (
<div>
constructor(props) {
super(props);
this.state = {
loading: false,
dataSource: [],
pageInfo: {
current: 0, pageSize: 10, total: 0
}
};
}
</div>
componentDidMount() {
this.dimensionList();
}
dimensionList = (extra = {}) => {
const { pageInfo } = this.state;
this.setState({ loading: true });
dimensionList({ ...pageInfo, ...extra }).then(({ status, data }) => {
this.setState({ loading: false });
if (status) {
const { pageNum: curren, pageSize, total, list: dataSource } = data;
this.setState({
dataSource,
pageInfo: {
...pageInfo,
curren, pageSize, total
}
});
}
}).catch(() => this.setState({ loading: false }));
};
dimensionDelete = (payload) => {
Modal.confirm({
title: "信息确认",
content: "确认要删除吗?",
onOk: () => {
dimensionDelete(payload).then(({ status, errormsg }) => {
if (status) {
message.success("删除成功");
this.dimensionList();
} else {
message.error(errormsg || "删除失败");
}
});
}
});
};
render() {
const { dataSource, loading, pageInfo } = this.state;
const { onEdit } = this.props;
const pagination = {
...pageInfo,
showTotal: total => `${total}`,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ["10", "20", "50", "100"],
onShowSizeChange: (current, pageSize) => {
this.setState({
pageInfo: { ...pageInfo, current, pageSize }
}, () => this.dimensionList());
},
onChange: current => {
this.setState({
pageInfo: { ...pageInfo, current }
}, () => this.dimensionList());
}
};
const columns = [
{ dataIndex: "dimName", title: "统计维度" },
{ dataIndex: "remark", title: "描述" },
{ dataIndex: "dimType", title: "维度类型" },
{
dataIndex: "operate", title: "操作",
render: (_, record) => {
return (
<span className="space10">
<a href="javascript: void(0);" onClick={() => onEdit(record.id)}>编辑</a>
<a href="javascript: void(0);" onClick={() => this.dimensionDelete([record.id])}>删除</a>
</span>
);
}
}
];
return (
<WeaTable
rowKey="id"
className="dimensionTableWrapper"
dataSource={dataSource}
pagination={pagination}
loading={loading}
columns={columns}
/>
);
}
}

View File

@ -17,8 +17,8 @@ class StatisticsModal extends Component {
] : [];
return (
<WeaDialog
{...this.props}
style={{ width: 640 }}
{...this.props} hasScroll
style={{ width: 640, height: 350 }}
buttons={buttons}
onCancel={onCancel}
initLoadCss

View File

@ -8,8 +8,12 @@ import React, { Component } from "react";
import { inject, observer } from "mobx-react";
import { WeaInputSearch, WeaReqTop } from "ecCom";
import { Button } from "antd";
import { condition } from "./components/conditions";
import { commonEnumList } from "../../apis/ruleconfig";
import { dimensionGetForm } from "../../apis/statistics";
import StatisticsModal from "./components/statisticsModal";
import DimensionSlide from "./components/dimensionSlide";
import DimensionTable from "./components/dimensionTable";
import "./index.less";
@inject("taxAgentStore", "attendanceStore")
@ -18,9 +22,11 @@ class Index extends Component {
constructor(props) {
super(props);
this.state = {
conditions: [],
convertConditions: [],
selectedKey: "statistics",
slideReq: {
visible: false
visible: false, formId: ""
},
modalReq: {
title: "", visible: false,
@ -29,6 +35,111 @@ class Index extends Component {
};
}
initCondition = async () => {
const { attendanceStore: { statisticsForm } } = this.props;
const [dimTypeEnum, dimCodeList] = await Promise.all([this.commonEnumList(), this.dimensionGetForm()]);
const { data: dimTypeData } = dimTypeEnum, { data: dimCodeData } = dimCodeList;
const { baseForm: { statsDimOptions, groupDimOptions, data: dimTypeValue } } = dimCodeData;
this.setState({
conditions: _.map(condition, item => {
return {
...item,
items: _.map(item.items, child => {
if (child.domkey[0] === "dimType") {
return {
...child,
value: dimTypeValue.dimType,
options: _.map(dimTypeData, dimTypeItem => ({
key: dimTypeItem.value,
showname: dimTypeItem.defaultLabel
}))
};
}
if (child.domkey[0] === "setting4Qualitative") {
return {
...child,
options: _.map(statsDimOptions, dimCodeItem => ({
key: dimCodeItem.id,
showname: dimCodeItem.content
}))
};
}
if (child.domkey[0] === "dimCode") {
return {
...child,
options: _.map(groupDimOptions, dimCodeItem => ({
key: dimCodeItem.id,
showname: dimCodeItem.content
}))
};
}
return { ...child };
})
};
})
}, () => {
this.setState({ convertConditions: this.state.conditions });
statisticsForm.initFormFields(this.state.conditions);
});
};
commonEnumList = () => {
const payload = {
enumClass: "com.engine.salary.report.enums.SalaryStatisticsDimensionTypeEnum"
};
return commonEnumList(payload);
};
dimensionGetForm = () => {
return dimensionGetForm();
};
handleChangeCondition = (val, viewAttr) => {
const { attendanceStore: { statisticsForm, initStatisticsForm } } = this.props;
console.log(val);
const helpfulTitle = val === "RATION_GROUP_SPACING" ?
"例:\n" +
" 若所属字段为【工龄】分组设置为【0-5】【5-10】统计项为【税前薪资】对应的统计规则为【求和】 则统计结果为【工龄】为【0-5】的所有人的【税前薪资】求和【工龄】为【5-10】的所有人的【税前薪资】求和\n" +
"若未选择所属字段分组设置为【0-10,000.00】【10,000.00-20,000.00】;若统计项为【税前薪资】,对应的统计规则为【计数】; 则统计结果为【税前薪资】为【0-10,000.00】有多少人【税前薪资】为【10,000.00-20,000.00】有多少人;" :
val === "RATION_GROUP_INDIVIDUAL" ?
"例:\n" +
" 若所属字段为【职级】分组设置为【1】【2】【3】统计项为【税前薪资】对应的统计规则为【平均值】 则统计结果为【职级】为【1】的所有人的【税前薪资】的平均值【职级】为【2】的所有人的【税前薪资】的平均值【职级】为【3】的所有人的【税前薪资】的平均值\n" +
"若未选择所属字段分组设置为【1】【2】【3】若统计项为【绩效】对应的统计规则为【计数】 则统计结果为【绩效】为【1】有多少人绩效为【2】有多少人绩效为【3】有多少人" : "";
if (val === "QUALITATIVE") {
this.setState({
conditions: _.map(this.state.convertConditions, item => {
return {
...item,
items: _.map(_.filter(item.items, child => child.domkey[0] !== "dimCode"), it => {
if (it.domkey[0] === "dimType") {
return { ...it, value: val, viewAttr: viewAttr ? viewAttr : it.viewAttr };
}
return { ...it };
})
};
})
}, () => {
initStatisticsForm(this.state.conditions);
});
} else {
this.setState({
conditions: _.map(this.state.convertConditions, item => {
return {
...item,
items: _.map(_.filter(item.items, child => child.domkey[0] !== "setting4Qualitative"), it => {
if (it.domkey[0] === "dimType") {
return { ...it, value: val, viewAttr: viewAttr ? viewAttr : it.viewAttr };
} else if (it.domkey[0] === "dimCode") {
return { ...it, helpfulTitle };
}
return { ...it };
})
};
})
}, () => {
initStatisticsForm(this.state.conditions);
// statisticsForm.initFormFields(this.state.conditions);
});
}
};
handleReqBtnsClick = (key) => {
if (key === "search") {
@ -37,7 +148,7 @@ class Index extends Component {
const title = key === "dimension" ?
<div className="dimensionTitle">
<span>统计维度管理</span>
<Button type="primary" onClick={this.handleAddDimension}>新建统计维度</Button>
<Button type="primary" onClick={() => this.handleAddDimension()}>新建统计维度</Button>
</div>
: "新建报表";
this.setState({
@ -56,26 +167,31 @@ class Index extends Component {
}
});
};
handleAddDimension = () => {
handleAddDimension = (formId = "") => {
const { slideReq } = this.state;
this.setState({
slideReq: {
...slideReq, visible: true
...slideReq, visible: true,
formId
}
});
};
handleClose = () => {
handleClose = (initTable = false) => {
const { attendanceStore: { statisticsForm } } = this.props;
const { slideReq } = this.state;
this.setState({
slideReq: {
...slideReq, visible: false
...slideReq, visible: false, formId: ""
}
}, () => {
statisticsForm.resetForm();
initTable && this.dimensionTableRef.dimensionList();
});
};
render() {
const { taxAgentStore: { showOperateBtn }, attendanceStore: { statisticsForm } } = this.props;
const { selectedKey, modalReq, slideReq } = this.state;
const { selectedKey, modalReq, slideReq, conditions } = this.state;
const buttons = [
<Button type="primary" onClick={() => this.handleReqBtnsClick("addReport")}>新建报表</Button>,
<Button type="ghost" onClick={() => this.handleReqBtnsClick("dimension")}>维度统计管理</Button>,
@ -95,9 +211,18 @@ class Index extends Component {
>
<div style={{ height: 2000, background: "#fff" }}>
<StatisticsModal {...modalReq} onCancel={this.handleCancel}>
<div>123</div>
{
modalReq.typeKey === "dimension" &&
<DimensionTable ref={dom => this.dimensionTableRef = dom}
onEdit={id => this.handleAddDimension(id)}
/>
}
</StatisticsModal>
<DimensionSlide {...slideReq} onCancel={this.handleClose} form={statisticsForm}/>
<DimensionSlide
{...slideReq} onCancel={this.handleClose}
form={statisticsForm} condition={conditions}
initCondition={this.initCondition} onChangeCondition={this.handleChangeCondition}
/>
</div>
</WeaReqTop>
);

View File

@ -15,6 +15,17 @@
justify-content: space-between;
}
.dimensionTableWrapper {
.space10 {
a:first-child {
margin-right: 10px;
}
}
}
}
.dimensionSlideWrapper {
.wea-select, .ant-select, .ant-select-selection {
width: 100%;
}

View File

@ -1,4 +1,4 @@
import { observable } from "mobx";
import { action, observable } from "mobx";
import { WeaForm } from "comsMobx";
@ -7,4 +7,11 @@ export class AttendanceStore {
@observable refenceform = new WeaForm();
//薪酬统计 新增form
@observable statisticsForm = new WeaForm();
@action
initStatisticsForm = (condition) => {
this.statisticsForm && this.statisticsForm.updateIsFormInit(false);
this.statisticsForm = new WeaForm();
this.statisticsForm.setCondition(condition, true);
};
}

View File

@ -1,65 +1,35 @@
import { Spin } from 'antd';
import { WeaSwitch } from 'comsMobx';
import { WeaLocaleProvider, WeaAlertPage, WeaSearchGroup, WeaFormItem } from 'ecCom';
import { Spin } from "antd";
import { WeaSwitch } from "comsMobx";
import { WeaAlertPage, WeaFormItem, WeaHelpfulTip, WeaLocaleProvider, WeaSearchGroup } from "ecCom";
const getLabel = WeaLocaleProvider.getLabel;
// 渲染form表单: 一般对form的渲染都统一使用该方法
export const getCustomSearchs = (form, condition, col, isCenter) => {
const { isFormInit } = form;
const formParams = form.getFormParams();
let items = [];
let group = [];
isFormInit && condition &&
condition.map(c =>{
c.items.map(fields => {
items.push({
com:(
<WeaFormItem
label={`${fields.label}`} // label 标签的文本
labelCol={{span: `${fields.labelcol}`}} // label标签占一行比例
wrapperCol={{span: `${fields.fieldcol}`}} // 右侧控件占一行比例
error={form.getError(fields)} // 错误提示: 处理表单中有必填项,保存的校验
tipPosition="bottom" // 错误提示的显示位置: top/bottom
>
<WeaSwitch
fieldConfig={fields}
form={form}
formParams={formParams}
/>
</WeaFormItem>),
colSpan:1,
})
});
// 获取condition的domKey值
export const getConditionDomkeys = (condition) => {
let domkeyList = [];
_.forEach(condition, item => {
const tmpV = _.reduce(item.items, (pre, cur) => {
return [...pre, cur["domkey"][0]];
}, []);
domkeyList = domkeyList.concat(tmpV);
});
if(items.length > 0) {
group.push(
<WeaSearchGroup
col={col || 1} // 高级搜索列布局列数
needTigger={true} // 是否开启收缩
title={''} // 高级搜索标题
showGroup={true} // 是否开启面板
items={items} // 条目数组数据
center={isCenter || false} // 内容是否居中:一般弹框需要
/>)
return group;
}
}
return domkeyList;
};
// 渲染form表单: 一般对form的渲染都统一使用该方法
export const getSearchs = (form, condition, col, isCenter) => {
export const getSearchs = (form, condition, col, isCenter, onChange = () => void (0)) => {
const { isFormInit } = form;
const formParams = form.getFormParams();
let group = [];
isFormInit && condition && condition.map(c =>{
isFormInit && condition && condition.map(c => {
let items = [];
c.items.map(fields => {
items.push({
com:(
com: (
<WeaFormItem
label={`${fields.label}`} // label 标签的文本
labelCol={{span: `${fields.labelcol}`}} // label标签占一行比例
wrapperCol={{span: `${fields.fieldcol}`}} // 右侧控件占一行比例
labelCol={{ span: `${fields.labelcol}` }} // label标签占一行比例
wrapperCol={{ span: `${fields.fieldcol}` }} // 右侧控件占一行比例
error={form.getError(fields)} // 错误提示: 处理表单中有必填项,保存的校验
tipPosition="bottom" // 错误提示的显示位置: top/bottom
>
@ -67,39 +37,44 @@ export const getSearchs = (form, condition, col, isCenter) => {
fieldConfig={fields}
form={form}
formParams={formParams}
onChange={onChange}
/>
{
fields.helpfulTitle &&
<WeaHelpfulTip title={fields.helpfulTitle} style={{ position: "absolute", right: "-20px", top: "25%" }}/>
}
</WeaFormItem>),
colSpan:1,
})
colSpan: 1
});
});
group.push(
<WeaSearchGroup
col={col || 1} // 高级搜索列布局列数
needTigger={true} // 是否开启收缩
title={c.title || ''} // 高级搜索标题
title={c.title || ""} // 高级搜索标题
showGroup={c.defaultshow} // 是否开启面板
items={items} // 条目数组数据
center={isCenter || false} // 内容是否居中:一般弹框需要
/>)
/>);
});
return group;
}
};
// 页面加载中效果处理
export const renderLoading = (loading) => (
<div className="wea-demo-loading">
<Spin spinning={loading} />
<Spin spinning={loading}/>
</div>
)
);
// 无权限处理
export const renderNoright = () => (
<WeaAlertPage>
<div>
{getLabel(2012,'对不起,您暂时没有权限!')}
{getLabel(2012, "对不起,您暂时没有权限!")}
</div>
</WeaAlertPage>
)
);
// 暂无数据处理
export const renderNoData = () => (
@ -108,13 +83,13 @@ export const renderNoData = () => (
暂无数据
</div>
</WeaAlertPage>
)
);
//分页计算
export function calcPageNo (total, pageNo = 1, pageSize = 10, selectDelDataLen = 1) {
const totalPage = Math.ceil((total - selectDelDataLen) / pageSize) // 总页数
pageNo = pageNo > totalPage ? totalPage : pageNo
pageNo = pageNo < 1 ? 1 : pageNo
return pageNo
export function calcPageNo(total, pageNo = 1, pageSize = 10, selectDelDataLen = 1) {
const totalPage = Math.ceil((total - selectDelDataLen) / pageSize); // 总页数
pageNo = pageNo > totalPage ? totalPage : pageNo;
pageNo = pageNo < 1 ? 1 : pageNo;
return pageNo;
}