Merge branch 'feature/V2-工资单模板添加新增薪资项目大类的功能' into develop

This commit is contained in:
黎永顺 2023-02-03 10:29:48 +08:00
commit 2a1e152492
7 changed files with 518 additions and 198 deletions

View File

@ -350,3 +350,11 @@ export const sendRangeSave = (params) => {
export const sendRangeDelete = (params) => {
return postFetch('/api/bs/hrmsalary/salaryBill/send/range/delete', params);
}
//工资单模板-获取模板可用的分组
export const getAvailableSalaryGroupSet = (params) => {
return postFetch('/api/bs/hrmsalary/salaryBill/template/getAvailableSalaryGroupSet', params);
}
//工资单模板-获取模板分组下可用的薪资项目
export const getAvailableSalaryItemSet = (params) => {
return postFetch('/api/bs/hrmsalary/salaryBill/template/getAvailableSalaryItemSet', params);
}

View File

@ -101,7 +101,7 @@ export default class Payroll extends React.Component {
// 模板搜索
handleTemplateSearch() {
const { templateSearchValue, templateSelect } = this.state;
let params = { name: templateSearchValue, salarySobId: templateSelect,current: 1 };
let params = { name: templateSearchValue, salarySobId: templateSelect, current: 1 };
const { payrollStore: { getPayrollTemplateList } } = this.props;
getPayrollTemplateList(params);
}
@ -344,8 +344,8 @@ export default class Payroll extends React.Component {
});
return;
}
if(templateBaseData.replenishName === templateBaseData.name){
message.error("工资单模板名称和补发工资单模板名称不可相同")
if (templateBaseData.replenishName === templateBaseData.name) {
message.error("工资单模板名称和补发工资单模板名称不可相同");
return;
}
this.setState({

View File

@ -62,4 +62,143 @@
}
}
}
.salarySetTitle {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
}
//拖拽列表
.salaryItemSettingWrapper {
& > ul {
border: 1px solid #e5e5e5;
.wea-sortable-salary-item {
padding: 0;
margin: 0;
border: none;
cursor: default;
.salaryItemWrapper {
width: 100%;
.salaryItemHeader {
display: flex;
align-items: center;
justify-content: space-between;
height: 32px;
line-height: 32px;
background: #f5f5f5;
border-bottom: 1px solid #f2f2f2;
padding: 0 12px;
.icon-coms-Add-to {
cursor: pointer;
color: #5d9cec;
position: relative;
z-index: 999;
}
.titleWrapper {
display: flex;
align-items: center;
.salaryClassTitle {
font-weight: 400;
font-size: 12px;
color: #111;
vertical-align: middle;
}
.iconWrapper {
display: none;
align-items: center;
i {
padding-left: 8px;
color: #666;
cursor: pointer;
position: relative;
z-index: 999;
}
}
}
}
.salaryItemHeader:hover {
.iconWrapper {
display: flex;
}
}
.salaryItemContent {
padding: 4px 16px;
background: #fff;
min-height: 52px;
& > ul {
display: flex;
width: 100%;
flex-wrap: wrap;
li {
width: 25%;
.salaryItemList {
width: 100%;
.salaryItem:hover {
i.anticon-cross {
display: block;
}
}
.salaryItem {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
min-height: 40px;
color: #000;
font-size: 12px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
.salaryItemName {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
i.anticon-cross {
display: none;
cursor: pointer;
}
}
}
}
}
.empty {
width: 100%;
height: 44px;
line-height: 44px;
text-align: center;
}
}
}
}
}
}
.salaryItemModalWrapper{
.modalContent{
padding: 16px;
}
}

View File

@ -0,0 +1,35 @@
/*
* Author: 黎永顺
* name: 工资单模板-新增编辑薪资项目项
* Description:
* Date: 2023/2/2
*/
import React, { Component } from "react";
import { WeaDialog } from "ecCom";
import { Button } from "antd";
class SalaryItemModal extends Component {
render() {
const { visible, title, onCancel, onConfirm } = this.props;
const buttons = [
<Button type="primary" onClick={onConfirm}>确定</Button>,
<Button type="ghost" onClick={onCancel}>取消</Button>
];
return (
<WeaDialog
onCancel={onCancel}
title={title}
visible={visible}
style={{ width: 500 }}
hasScroll
buttons={buttons}
initLoadCss
className="salaryItemModalWrapper"
>
{this.props.children}
</WeaDialog>
);
}
}
export default SalaryItemModal;

View File

@ -0,0 +1,287 @@
/*
* Author: 黎永顺
* name: 工资单模板-薪资项目设置
* Description:
* Date: 2023/2/2
*/
import React, { Component } from "react";
import { toJS } from "mobx";
import { WeaFormItem, WeaInput, WeaSelect, WeaSortable } from "ecCom";
import { Icon, Modal } from "antd";
import SalaryItemModal from "./salaryItemModal";
import { getAvailableSalaryGroupSet, getAvailableSalaryItemSet } from "../../../apis/payroll";
class SalaryItemSettings extends Component {
constructor(props) {
super(props);
this.state = {
dataList: [],
checkedValue: "",
modalPayload: {
visible: false,
title: "",
groupId: "",
groupName: "",
options: []
}
};
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.dataSource !== this.props.dataSource) {
this.setState({
dataList: _.map(toJS(nextProps.dataSource), item => {
return {
...item,
id: item.groupId
};
})
});
}
}
/*
* Author: 黎永顺
* Description:删除薪资项目大类
* Params:
* Date: 2023/2/2
*/
handleDeleteClick = (group) => {
Modal.confirm({
title: "信息确认",
content: "确认删除",
onOk: () => {
let resultSalaryItemSet = [...this.state.dataList];
resultSalaryItemSet.map((sourceGroup, index) => {
if (sourceGroup.groupId === group.groupId) {
resultSalaryItemSet.splice(index, 1);
}
});
this.setState({
dataList: resultSalaryItemSet
}, () => {
this.props.onChangeSalaryItem(resultSalaryItemSet);
});
}
});
};
/*
* Author: 黎永顺
* Description: 删除薪资项目项
* Params:
* Date: 2023/2/2
*/
handleDeleteSalaryItem = (group, item) => {
let resultSalaryItemSet = [...this.state.dataList];
resultSalaryItemSet.map(sourceGroup => {
if (sourceGroup.groupId === group.groupId) {
sourceGroup.items.map((sourceItem, index) => {
if (sourceItem.id === item.id) {
sourceGroup.items.splice(index, 1);
}
});
}
});
this.setState({
dataList: resultSalaryItemSet
}, () => {
this.props.onChangeSalaryItem(resultSalaryItemSet);
});
};
handleCloseModal = () => {
const { modalPayload } = this.state;
this.setState({
checkedValue: "",
modalPayload: {
...modalPayload,
visible: false,
title: "",
groupId: "",
groupName: "",
options: []
}
});
};
handleOpenModal = (record, title, isItem) => {
if (title === "分类名称编辑") {
const { modalPayload } = this.state;
this.setState({
modalPayload: {
...modalPayload,
visible: true,
title,
groupId: record.groupId,
groupName: record.groupName
}
});
} else {
isItem ? this.getAvailableSalaryItemSet(record, title) : this.getAvailableSalaryGroupSet(record, title);
}
};
getAvailableSalaryItemSet = (record, title) => {
const payload = {
salarySobId: this.props.salarySobId,
existSalaryItemIds: _.map(record.items, it => it.id),
groupId: record.groupId,
isReplenish: this.props.isReplenish
};
getAvailableSalaryItemSet(payload).then(({ status, data }) => {
if (status) {
const { modalPayload } = this.state;
this.setState({
modalPayload: {
...modalPayload,
visible: true,
title,
groupId: record.groupId,
options: _.map(data, it => ({ ...it, showname: it.name, key: it.salaryItemId }))
}
});
}
});
};
getAvailableSalaryGroupSet = (salarySobId, title) => {
const { dataList } = this.state;
const payload = {
salarySobId,
existSalaryGroupIds: _.map(dataList, it => it.groupId),
isReplenish: this.props.isReplenish
};
getAvailableSalaryGroupSet(payload).then(({ status, data }) => {
if (status) {
const { modalPayload } = this.state;
this.setState({
modalPayload: {
...modalPayload,
visible: true,
title,
options: _.map(data, it => ({ ...it, showname: it.groupName, key: it.groupId }))
}
});
}
});
};
handleChangeClassName = (groupName) => {
const { modalPayload } = this.state;
this.setState({
modalPayload: {
...modalPayload,
groupName
}
});
};
/*
* Author: 黎永顺
* Description:添加薪资项目项
* Params:
* Date: 2023/2/2
*/
handleConfirm = () => {
const { modalPayload, checkedValue, dataList } = this.state;
const { options = [], groupId, groupName } = modalPayload;
if (groupName) {
this.setState({
dataList: _.map(dataList, it => {
if (it.groupId === groupId) {
return {
...it,
groupName
};
}
return { ...it };
})
}, () => {
this.props.onChangeSalaryItem(this.state.dataList);
this.handleCloseModal();
});
} else {
this.setState({
dataList: groupId ? _.map(dataList, it => {
if (it.groupId === groupId) {
return {
...it,
items: [...it.items, ..._.filter(options, child => checkedValue.split(",").includes(child.salaryItemId))]
};
}
return { ...it };
}) : [...dataList, ..._.filter(options, child => checkedValue.split(",").includes(child.groupId))]
}, () => {
this.props.onChangeSalaryItem(this.state.dataList);
this.handleCloseModal();
});
}
};
render() {
const { onChangeSalaryItem } = this.props;
const { dataList, modalPayload, checkedValue } = this.state;
return (
<div className="salaryItemSettingWrapper">
<WeaSortable
datas={dataList}
onChange={(list) => onChangeSalaryItem(list)}
renderNodeItem={(item) => {
return <div className="salaryItemWrapper">
<div className="salaryItemHeader">
<span className="titleWrapper">
<span className="salaryClassTitle">{item.groupName}</span>
<span className="iconWrapper">
<i className="icon-coms-edit" onClick={() => this.handleOpenModal(item, "分类名称编辑")}/>
<i className="icon-coms-Delete" onClick={() => this.handleDeleteClick(item)}/>
</span>
</span>
<i className="icon-coms-Add-to" onClick={() => this.handleOpenModal(item, "请选择薪资项目", true)}/>
</div>
<div className="salaryItemContent">
{
!_.isEmpty(item.items) ?
<WeaSortable
datas={item.items}
onChange={(items) => onChangeSalaryItem(
_.map(dataList, child => {
if (child.id === item.id) {
return { ...child, items };
}
return { ...child };
})
)}
renderNodeItem={(filed) => {
return <div className="salaryItemList">
<div className="salaryItem">
<div className="salaryItemName">{filed.name}</div>
<Icon type="cross" onClick={() => this.handleDeleteSalaryItem(item, filed)}/>
</div>
</div>;
}}
className="wea-sortable-salary-item"
/> :
<div className="empty">暂无数据</div>
}
</div>
</div>;
}}
className="wea-sortable-salary-item"
/>
<SalaryItemModal {...modalPayload} onCancel={this.handleCloseModal} onConfirm={this.handleConfirm}>
<div className="modalContent">
{
modalPayload.title === "分类名称编辑" ?
<WeaFormItem label="分类名称" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
<WeaInput value={modalPayload.groupName} onChange={this.handleChangeClassName}/>
</WeaFormItem>
: <WeaSelect
multiple
style={{ width: "100%" }}
value={checkedValue}
options={modalPayload.options}
onChange={v => this.setState({ checkedValue: v })}
/>
}
</div>
</SalaryItemModal>
</div>
);
}
}
export default SalaryItemSettings;

View File

@ -1,8 +1,10 @@
import React from "react";
import { Icon, Modal, Radio, Switch } from "antd";
import { WeaFormItem, WeaInput, WeaSearchGroup } from "ecCom";
import { Radio, Switch } from "antd";
import { WeaButtonIcon, WeaFormItem, WeaInput, WeaSearchGroup } from "ecCom";
import { inject, observer } from "mobx-react";
import { toJS } from "mobx";
import BackgroundUpload from "../components/backgroundUpload";
import SalaryItemSettings from "./salaryItemSettings";
import "./index.less";
@ -21,6 +23,7 @@ export default class ShowSettingForm extends React.Component {
const { payrollStore: { salaryTemplateShowSet, setSalaryTemplateShowSet } } = this.props;
let request = { ...salaryTemplateShowSet, ...params };
setSalaryTemplateShowSet(request);
window.localStorage.setItem("salary-showset", JSON.stringify(request));
};
// 工资单主题 插入变量
@ -30,61 +33,19 @@ export default class ShowSettingForm extends React.Component {
let request = { ...salaryTemplateShowSet };
request.theme = (request.theme ? request.theme : "") + param;
setSalaryTemplateShowSet(request);
window.localStorage.setItem("salary-showset", JSON.stringify(request));
};
handleDownClick = (index) => {
const { payrollStore: { salaryItemSet, setSalaryItemSet } } = this.props;
let downItem = salaryItemSet[index + 1];
let thisItem = salaryItemSet[index];
let resultSet = [...salaryItemSet];
resultSet[index] = downItem;
resultSet[index + 1] = thisItem;
handleChangeSalaryItem = (resultSet) => {
const { payrollStore: { setSalaryItemSet } } = this.props;
setSalaryItemSet(resultSet);
};
handleUpClick = (index) => {
const { payrollStore: { salaryItemSet, setSalaryItemSet } } = this.props;
let upItem = salaryItemSet[index - 1];
let thisItem = salaryItemSet[index];
let resultSet = [...salaryItemSet];
resultSet[index] = upItem;
resultSet[index - 1] = thisItem;
setSalaryItemSet(resultSet);
};
handleDeleteItem = (group, item) => {
const { payrollStore: { salaryItemSet, setSalaryItemSet } } = this.props;
let resultSalaryItemSet = [...salaryItemSet];
resultSalaryItemSet.map(sourceGroup => {
if (sourceGroup.id == group.id) {
sourceGroup.items.map((sourceItem, index) => {
if (sourceItem.id == item.id) {
sourceGroup.items.splice(index, 1);
}
});
}
});
setSalaryItemSet(resultSalaryItemSet);
};
handleDeleteClick = (index) => {
Modal.confirm({
title: "信息确认",
content: "确认删除",
onOk: () => {
const { payrollStore: { salaryItemSet, setSalaryItemSet } } = this.props;
let resultSalaryItemSet = [...salaryItemSet];
resultSalaryItemSet.splice(index, 1);
setSalaryItemSet(resultSalaryItemSet);
}
});
};
render() {
const { payrollStore, id } = this.props;
const salaryTemplateShowSetStorage = id ? "{}" : window.localStorage.getItem("salary-showset") || "{}";
const salaryTemplateShowSetStorage = (id ? "{}" : window.localStorage.getItem("salary-showset") || "{}");
const { salaryTemplateShowSet } = payrollStore;
const { salaryItemSet } = payrollStore;
const { salaryItemSet, templateBaseData } = payrollStore;
const {
theme,
background,
@ -164,54 +125,23 @@ export default class ShowSettingForm extends React.Component {
</WeaFormItem>
</WeaSearchGroup>
<WeaSearchGroup title="薪资项目设置" items={[]} needTigger showGroup>
{
salaryItemSet.map((group, index) => (
<div className="configItemWrapper">
<div className="configTitle">{group.groupName}
{
index < salaryItemSet.length - 1 &&
<Icon
type="caret-down"
style={{ marginLeft: "10px", cursor: "pointer", color: "#666" }}
onClick={() => {
this.handleDownClick(index);
}}
/>
}
{
index > 0 &&
<Icon
type="caret-up"
style={{ marginLeft: "10px", cursor: "pointer", color: "#666" }}
onClick={() => {
this.handleUpClick(index);
}}
/>
}
<i
className="icon-coms-Delete"
style={{ cursor: "pointer", color: "#666", marginLeft: "10px" }}
onClick={() => {
this.handleDeleteClick(index);
}}
/>
</div>
<div className="configContent">
{group.items.map(item => (
<span className="editItem">{item.name}
<Icon
type="cross" style={{ cursor: "pointer" }}
onClick={() => {
this.handleDeleteItem(group, item);
}}
/>
</span>
))}
</div>
</div>
))
<WeaSearchGroup
title={
<div className="salarySetTitle">
<span>薪资项目设置</span>
<WeaButtonIcon buttonType="add" type="primary"
onClick={() => this.salaryItemSettingsRef.handleOpenModal(toJS(templateBaseData).salarySob, "添加分类")}/>
</div>
}
items={[]} needTigger showGroup
>
<SalaryItemSettings
ref={dom => this.salaryItemSettingsRef = dom}
dataSource={salaryItemSet}
onChangeSalaryItem={this.handleChangeSalaryItem}
salarySobId={toJS(templateBaseData).salarySob}
isReplenish={true}
/>
</WeaSearchGroup>
</div>
);

View File

@ -1,119 +1,40 @@
import React from "react";
import { Icon, Modal } from "antd";
import { WeaSearchGroup } from "ecCom";
import { WeaButtonIcon, WeaSearchGroup } from "ecCom";
import { inject, observer } from "mobx-react";
import { toJS } from "mobx";
import "./index.less";
import SalaryItemSettings from "./salaryItemSettings";
@inject("payrollStore")
@observer
export default class TemplateSettingForm extends React.Component {
handleDownClick = (index) => {
const { payrollStore: { replenishSalaryTemplateSalaryItemSet, setReplenishSalaryTemplateSalaryItemSet } } = this.props;
let downItem = replenishSalaryTemplateSalaryItemSet[index + 1];
let thisItem = replenishSalaryTemplateSalaryItemSet[index];
let resultSet = [...replenishSalaryTemplateSalaryItemSet];
resultSet[index] = downItem;
resultSet[index + 1] = thisItem;
handleChangeSalaryItem = (resultSet) => {
const { payrollStore: { setReplenishSalaryTemplateSalaryItemSet } } = this.props;
setReplenishSalaryTemplateSalaryItemSet(resultSet);
};
handleUpClick = (index) => {
const { payrollStore: { replenishSalaryTemplateSalaryItemSet, setReplenishSalaryTemplateSalaryItemSet } } = this.props;
let upItem = replenishSalaryTemplateSalaryItemSet[index - 1];
let thisItem = replenishSalaryTemplateSalaryItemSet[index];
let resultSet = [...replenishSalaryTemplateSalaryItemSet];
resultSet[index] = upItem;
resultSet[index - 1] = thisItem;
setReplenishSalaryTemplateSalaryItemSet(resultSet);
};
handleDeleteItem = (group, item) => {
const { payrollStore: { replenishSalaryTemplateSalaryItemSet, setReplenishSalaryTemplateSalaryItemSet } } = this.props;
let resultSalaryItemSet = [...replenishSalaryTemplateSalaryItemSet];
resultSalaryItemSet.map(sourceGroup => {
if (sourceGroup.id === group.id) {
sourceGroup.items.map((sourceItem, index) => {
if (sourceItem.id === item.id) {
sourceGroup.items.splice(index, 1);
}
});
}
});
setReplenishSalaryTemplateSalaryItemSet(resultSalaryItemSet);
};
handleDeleteClick = (index) => {
Modal.confirm({
title: "信息确认",
content: "确认删除",
onOk: () => {
const { payrollStore: { replenishSalaryTemplateSalaryItemSet, setReplenishSalaryTemplateSalaryItemSet } } = this.props;
let resultSalaryItemSet = [...replenishSalaryTemplateSalaryItemSet];
resultSalaryItemSet.splice(index, 1);
setReplenishSalaryTemplateSalaryItemSet(resultSalaryItemSet);
},
onCancel: () => {
}
});
};
render() {
const { payrollStore } = this.props;
const { replenishSalaryTemplateSalaryItemSet } = payrollStore;
const { replenishSalaryTemplateSalaryItemSet, templateBaseData } = payrollStore;
return (
<div className="showSettingForm">
<WeaSearchGroup title="薪资项目设置" items={[]} needTigger showGroup>
{
!_.isEmpty(toJS(replenishSalaryTemplateSalaryItemSet)) &&
replenishSalaryTemplateSalaryItemSet.map((group, index) => (
<div className="configItemWrapper">
<div className="configTitle">{group.groupName}
{
index < replenishSalaryTemplateSalaryItemSet.length - 1 &&
<Icon
type="caret-down"
style={{ marginLeft: "10px", cursor: "pointer", color: "#666" }}
onClick={() => {
this.handleDownClick(index);
}}
/>
}
{
index > 0 &&
<Icon
type="caret-up"
style={{ marginLeft: "10px", cursor: "pointer", color: "#666" }}
onClick={() => {
this.handleUpClick(index);
}}
/>
}
<i
className="icon-coms-Delete"
style={{ cursor: "pointer", color: "#666", marginLeft: "10px" }}
onClick={() => {
this.handleDeleteClick(index);
}}
/>
</div>
<div className="configContent">
{group.items.map(item => (
<span className="editItem">{item.name}
<Icon
type="cross" style={{ cursor: "pointer" }}
onClick={() => {
this.handleDeleteItem(group, item);
}}
/>
</span>
))}
</div>
</div>
))
<WeaSearchGroup
title={
<div className="salarySetTitle">
<span>薪资项目设置</span>
<WeaButtonIcon buttonType="add" type="primary"
onClick={() => this.salaryItemSettingsRef.handleOpenModal(toJS(templateBaseData).salarySob, "添加分类")}/>
</div>
}
items={[]} needTigger showGroup>
<SalaryItemSettings
ref={dom => this.salaryItemSettingsRef = dom}
dataSource={replenishSalaryTemplateSalaryItemSet}
onChangeSalaryItem={this.handleChangeSalaryItem}
salarySobId={toJS(templateBaseData).salarySob}
isReplenish={true}
/>
</WeaSearchGroup>
</div>
);