Merge branch 'develop' into hotfix/V2-20230505

This commit is contained in:
黎永顺 2023-05-18 15:23:24 +08:00
commit 29ac83b992
53 changed files with 5241 additions and 410 deletions

View File

@ -107,6 +107,10 @@ export const saveSysItem = params => {
export const getItemTypeOption = params => {
return WeaTools.callApi('/api/bs/hrmsalary/salaryitem/listSalaryItemTypeOption', 'GET', params);
}
//获取公式描述
export const getFormulaDes = params => {
return WeaTools.callApi('/api/bs/hrmsalary/formula/des', 'GET', params);
}
// *** 公式 start ***
// 获取公式变量类型

View File

@ -1,29 +1,36 @@
import { WeaTools } from 'ecCom';
import { WeaTools } from "ecCom";
// 工资单列表
export const mySalaryBillList = params => {
return fetch('/api/bs/hrmsalary/salaryBill/mySalaryBillList', {
method: 'POST',
mode: 'cors',
return fetch("/api/bs/hrmsalary/salaryBill/mySalaryBillList", {
method: "POST",
mode: "cors",
headers: {
'Content-Type': 'application/json'
},
"Content-Type": "application/json"
},
body: JSON.stringify(params)
}).then(res => res.json())
}).then(res => res.json());
};
// 社保福利列表
export const welfareList = params => {
return WeaTools.callApi('/api/bs/hrmsalary/report/welfare/list', 'GET', params);
return WeaTools.callApi("/api/bs/hrmsalary/report/welfare/list", "GET", params);
};
// 调薪记录列表
export const recordList = params => {
return WeaTools.callApi('/api/bs/hrmsalary/report/record/list', 'GET', params);
return WeaTools.callApi("/api/bs/hrmsalary/report/record/list", "GET", params);
};
// 工资查看详情
export const mySalaryBill = params => {
return WeaTools.callApi('/api/bs/hrmsalary/salaryBill/mySalaryBill', 'GET', params);
};
return WeaTools.callApi("/api/bs/hrmsalary/salaryBill/mySalaryBill", "GET", params);
};
export const isNeedSecondPwdVerify = params => {
return WeaTools.callApi("/api/encrypt/secondauthsetting/isNeedSecondAuth", "POST", params);
};
export const doSecondAuth = params => {
return WeaTools.callApi("/api/encrypt/secondauthsetting/doSecondAuth", "POST", params);
};

View File

@ -43,3 +43,11 @@ export const getEncryptProgress = params => {
export const operateTaxDeclarationFunction = (params) => {
return postFetch('/api/bs/hrmsalary/sys/operateTaxDeclarationFunction', params);
}
//保存薪酬统计报表
export const reportStatisticsReportSave = (params) => {
return postFetch('/api/bs/hrmsalary/report/statistics/report/save', params);
}
//薪酬统计维度下拉列表
export const reportGetForm = params => {
return WeaTools.callApi('/api/bs/hrmsalary/report/statistics/report/getForm', 'GET', params);
}

View File

@ -0,0 +1,68 @@
import { WeaTools } from "ecCom";
import { postFetch } from "../util/request";
//薪酬统计维度下拉列表
export const dimensionGetForm = (params) => {
return WeaTools.callApi("/api/bs/hrmsalary/report/statistics/dimension/getForm", "GET", params);
};
//获取自定义统计项目表单
export const statisticsItemGetform = (params) => {
return WeaTools.callApi("/api/bs/hrmsalary/report/statistics/item/getForm", "GET", params);
};
//自定义统计项目列表
export const statisticsItemList = (params) => {
return WeaTools.callApi("/api/bs/hrmsalary/report/statistics/item/list", "GET", 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);
};
//保存薪酬统计报表
export const reportStatisticsReportList = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/report/list", params);
};
//删除薪酬统计报表
export const reportStatisticsReportDelete = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/report/delete", params);
};
//获取薪酬统计报表数据
export const reportStatisticsReportGetData = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/report/getData", params);
};
//保存自定义统计项目
export const reportStatisticsItemSave = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/item/save", params);
};
//保存数据范围及负责设置
export const reportStatisticsSaveSearchCondition = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/report/saveSearchCondition", params);
};
//删除自定义统计项目
export const reportStatisticsItemDelete = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/item/delete", params);
};
//获取薪酬统计报表查询条件
export const reportStatisticsGetSearchCondition = (params) => {
return WeaTools.callApi("/api/bs/hrmsalary/report/statistics/report/getSearchCondition", "GET", params);
};
//分析图数据展示范围设置查询
export const queryRangeSetting = (params) => {
return WeaTools.callApi("/api/bs/hrmsalary/report/statistics/echarts/queryRangeSetting", "GET", params);
};
//分析图数据展示范围设置删除
export const deleteRangeSetting = (params) => {
return WeaTools.callApi("/api/bs/hrmsalary/report/statistics/echarts/deleteRangeSetting", "GET", params);
};
//分析图数据展示范围设置保存
export const saveRangeSetting = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/echarts/saveRangeSetting", params);
};

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="151px" height="150px" viewBox="0 0 151 150" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon-empty-file</title>
<defs>
<polygon id="path-1" points="0 -0.0002 149.9997 -0.0002 149.9997 31.9998 0 31.9998"></polygon>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="知识文档" transform="translate(-112.000000, -299.000000)">
<g id="Group-40" transform="translate(113.000000, 321.000000)">
<g id="Group-3" transform="translate(0.000000, 89.259000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="Clip-2"></g>
<path d="M149.9997,15.9998 C149.9997,24.8368 116.4217,31.9998 74.9997,31.9998 C33.5787,31.9998 -0.0003,24.8368 -0.0003,15.9998 C-0.0003,7.1638 33.5787,-0.0002 74.9997,-0.0002 C116.4217,-0.0002 149.9997,7.1638 149.9997,15.9998" id="Fill-1" fill="#E6EFF0" mask="url(#mask-2)"></path>
</g>
<line x1="116.8337" y1="5.1465" x2="127.1277" y2="5.1465" id="Stroke-4" stroke="#D3D7DC"></line>
<line x1="121.9801" y1="0" x2="121.9801" y2="10.294" id="Stroke-6" stroke="#D3D7DC"></line>
<line x1="127.1276" y1="39.0801" x2="133.1026" y2="39.0801" id="Stroke-8" stroke="#D3D7DC"></line>
<line x1="130.1149" y1="36.0928" x2="130.1149" y2="42.0678" id="Stroke-10" stroke="#D3D7DC"></line>
<line x1="17.9655" y1="38.4541" x2="25.1085" y2="38.4541" id="Stroke-12" stroke="#D3D7DC"></line>
<line x1="21.5368" y1="34.8828" x2="21.5368" y2="42.0248" id="Stroke-14" stroke="#D3D7DC"></line>
<polygon id="Fill-16" fill="#FFFFFF" points="113.029 23.0694 99.885 10.2944 99.091 11.2664 98.32 17.1834 97.716 21.4544 100.529 24.2664 106.842 25.3914 110.675 25.0164 112.842 24.2874"></polygon>
<polygon id="Stroke-18" stroke="#CFD0D6" stroke-linecap="round" stroke-linejoin="round" points="113.029 23.0694 99.885 10.2944 99.091 11.2664 98.32 17.1834 97.716 21.4544 100.529 24.2664 106.842 25.3914 110.675 25.0164 112.842 24.2874"></polygon>
<path d="M96.6842,106.4366 L42.9702,106.4366 C39.6702,106.4366 36.9702,103.7376 36.9702,100.4366 L36.9702,26.3016 C36.9702,23.0006 39.6702,20.3016 42.9702,20.3016 L96.6842,20.3016 C99.9842,20.3016 102.6842,23.0006 102.6842,26.3016 L102.6842,100.4366 C102.6842,103.7376 99.9842,106.4366 96.6842,106.4366" id="Fill-20" fill="#FFFFFF"></path>
<path d="M96.6842,106.4366 L42.9702,106.4366 C39.6702,106.4366 36.9702,103.7376 36.9702,100.4366 L36.9702,26.3016 C36.9702,23.0006 39.6702,20.3016 42.9702,20.3016 L96.6842,20.3016 C99.9842,20.3016 102.6842,23.0006 102.6842,26.3016 L102.6842,100.4366 C102.6842,103.7376 99.9842,106.4366 96.6842,106.4366 Z" id="Stroke-22" stroke="#CFD0D6"></path>
<path d="M105.3727,23.5244 C102.1367,23.5244 99.4877,20.8764 99.4877,17.6394 L99.4877,10.2944 L53.2007,10.2944 C49.9637,10.2944 47.3157,12.9424 47.3157,16.1784 L47.3157,90.5454 C47.3157,93.7814 49.9637,96.4294 53.2007,96.4294 L107.1437,96.4294 C110.3817,96.4294 113.0287,93.7814 113.0287,90.5454 L113.0287,23.5244 L105.3727,23.5244 Z" id="Fill-24" fill="#FFFFFF"></path>
<path d="M105.3727,23.5244 C102.1367,23.5244 99.4877,20.8764 99.4877,17.6394 L99.4877,10.2944 L53.2007,10.2944 C49.9637,10.2944 47.3157,12.9424 47.3157,16.1784 L47.3157,90.5454 C47.3157,93.7814 49.9637,96.4294 53.2007,96.4294 L107.1437,96.4294 C110.3817,96.4294 113.0287,93.7814 113.0287,90.5454 L113.0287,23.5244 L105.3727,23.5244 Z" id="Stroke-26" stroke="#CFD0D6"></path>
<path d="M99.987,36.3116 L60.319,36.3116 C58.944,36.3116 57.819,35.1866 57.819,33.8116 C57.819,32.4366 58.944,31.3116 60.319,31.3116 L99.987,31.3116 C101.362,31.3116 102.487,32.4366 102.487,33.8116 C102.487,35.1866 101.362,36.3116 99.987,36.3116" id="Fill-28" fill="#E9E9E9"></path>
<path d="M99.987,48.2158 L60.319,48.2158 C58.944,48.2158 57.819,47.0908 57.819,45.7158 C57.819,44.3408 58.944,43.2158 60.319,43.2158 L99.987,43.2158 C101.362,43.2158 102.487,44.3408 102.487,45.7158 C102.487,47.0908 101.362,48.2158 99.987,48.2158" id="Fill-30" fill="#E9E9E9"></path>
<path d="M99.987,60.1192 L60.319,60.1192 C58.944,60.1192 57.819,58.9942 57.819,57.6192 C57.819,56.2442 58.944,55.1192 60.319,55.1192 L99.987,55.1192 C101.362,55.1192 102.487,56.2442 102.487,57.6192 C102.487,58.9942 101.362,60.1192 99.987,60.1192" id="Fill-32" fill="#E9E9E9"></path>
<path d="M84.529,72.0235 L60.319,72.0235 C58.944,72.0235 57.819,70.8985 57.819,69.5235 C57.819,68.1485 58.944,67.0235 60.319,67.0235 L84.529,67.0235 C85.904,67.0235 87.029,68.1485 87.029,69.5235 C87.029,70.8985 85.904,72.0235 84.529,72.0235" id="Fill-34" fill="#E9E9E9"></path>
<path d="M49.4841,92.169 L49.4651,12.311 C48.4411,13.349 47.8191,14.441 47.8191,16.439 L47.8191,90.587 C47.8191,92.703 49.0641,94.436 50.7221,95.325 C50.0211,94.344 49.4841,93.459 49.4841,92.169" id="Fill-36" fill="#EBF6FF"></path>
<path d="M39.1335,102.1446 L39.1965,22.3046 C38.1725,23.3426 37.4685,24.4166 37.4685,26.4146 L37.4685,100.5626 C37.4685,102.6786 38.7435,104.4116 40.4025,105.3006 C39.7005,104.3196 39.1335,103.4346 39.1335,102.1446" id="Fill-38" fill="#EBF6FF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,255 @@
/*
* Author: 黎永顺
* name: 公式列表
* Description:
* Date: 2023/4/25
*/
import React, { Component } from "react";
import { WeaInputSearch, WeaLocaleProvider } from "ecCom";
import { Tree } from "antd";
import { formualSearchField, formualSearchGroup, getFormulaDes } from "../../../apis/item";
import "../index.less";
const TreeNode = Tree.TreeNode;
const getLabel = WeaLocaleProvider.getLabel;
class CodeAction extends Component {
constructor(props) {
super(props);
this.state = {
disabled: false,
variableText: "",
funcText: "",
variItemText: "",
variableList: [], //变量列表
variableExpandedKeys: [], //变量展开的节点
funcList: [], //函数列表
funcExpandedKeys: [], //函数展开的节点
funcHoverItem: {} //选中的函数节点
};
}
componentDidMount() {
const { groupParams = {} } = this.props;
this.getFormulaDes();
this.formualSearchGroup(groupParams);
}
componentWillReceiveProps(nextProps, nextContext) {
const { isCustomFunction, isCustomFunctionClick, onChangeCustomFunction } = nextProps;
if (isCustomFunction === "1" && !isCustomFunctionClick) {
this.setState({ disabled: true });
} else {
this.setState({ disabled: false }, () => {
isCustomFunction === "1" && onChangeCustomFunction("0");
});
}
}
getFormulaDes = () => {
getFormulaDes().then(({ data }) => {
if (!_.isEmpty(data)) this.setState({ funcList: data });
});
};
formualSearchGroup = (payload) => {
formualSearchGroup(payload).then(({ status, data }) => {
if (status) this.setState({
variableList: _.map(data, item => ({
...item,
children: [{ name: "", fieldId: "searchInput" }]
}))
});
});
};
formualSearchField = (sourceId) => {
const { groupParams } = this.props;
const { variableList } = this.state;
formualSearchField({ sourceId, extendParam: { ...groupParams } }).then(({ status, data }) => {
if (status) {
this.setState({
variableList: _.map(variableList, it => ({
...it,
children: sourceId === it.key ? [...it.children, ...data] : [...it.children]
}))
});
}
});
};
handleExpandVari = (variableExpandedKeys, { expanded, node }) => {
const { props: { eventKey } } = node;
const { variableList } = this.state;
this.setState({ variableExpandedKeys });
if (expanded) {
this.formualSearchField(eventKey);
} else {
this.setState({
variableList: _.map(variableList, it => ({
...it,
children: eventKey === it.key ? [{ name: "", fieldId: "searchInput" }] : [...it.children]
}))
});
}
};
handleVariNode = (selectedKeys) => {
const { onVariSelect } = this.props;
const { variableList } = this.state;
const parentNode = _.map(variableList, it => it.key);
if (selectedKeys.join() && selectedKeys.join().indexOf("searchInput") === -1 &&
!parentNode.includes(selectedKeys.join())) {
const selectParentNodeKey = selectedKeys.join().split("_")[0];
const convertStr = _.reduce(variableList, (pre, cur) => {
if (cur.key === selectParentNodeKey) {
return pre + cur.value + "." + _.find(cur.children, child => child.fieldId === selectedKeys.join()).name;
}
return pre;
}, "");
onVariSelect(convertStr);
}
};
render() {
const {
variableList, variableExpandedKeys, variableText, variItemText,
funcList, funcText, funcExpandedKeys, funcHoverItem, disabled
} = this.state;
const { groupParams = {} } = this.props;
const { referenceType } = groupParams;
const variableDatalist = _.filter(variableList, it => it.value.indexOf(variableText) !== -1);
const funcDatalist = _.map(funcList, it => ({
...it,
children: _.filter(it.children, child => _.lowerCase(child.name).indexOf(funcText) !== -1)
}));
return (
<div className="excel-codeAction">
<div className="excel-codeAction-item">
<div className="excel-codeAction-header">
<div className="excel-codeAction-header-title">{getLabel(111, "变量")}</div>
</div>
<div className="excel-codeAction-content">
<WeaInputSearch value={variableText} placeholder={getLabel(111, "请输入变量名称")}
className="variableOuterInput"
onChange={variableText => this.setState({ variableText })}/>
<Tree className="variableTree" showLine expandedKeys={variableExpandedKeys}
onExpand={this.handleExpandVari} onSelect={this.handleVariNode}
>
{
_.map(variableDatalist, item => {
const { key, value, children = [] } = item;
const itemChildren = _.filter(children.slice(1), it => it.name.indexOf(variItemText) !== -1);
return <TreeNode title={value} key={key}>
{
_.map([...children.slice(0, 1), ...itemChildren], (child, childIndex) => {
const { name, fieldId } = child;
return (
fieldId === "searchInput" ?
<TreeNode
title={
<WeaInputSearch
value={variItemText}
placeholder={getLabel(111, "请输入变量名称")}
onChange={variItemText => this.setState({ variItemText })}
/>
}
key={fieldId + "_" + childIndex}/> :
<TreeNode title={name} key={fieldId}/>
);
})
}
</TreeNode>;
})
}
</Tree>
</div>
</div>
{
referenceType !== "sql" &&
<React.Fragment>
<div className="excel-codeAction-item">
<div className="excel-codeAction-header">
<div className="excel-codeAction-header-title">{getLabel(111, "函数")}</div>
</div>
<div className="excel-codeAction-content">
<WeaInputSearch value={funcText} placeholder={getLabel(111, "请输入函数名称")}
className="variableOuterInput"
onChange={funcText => this.setState({ funcText })}/>
<Tree className="variableTree" showLine expandedKeys={funcExpandedKeys}
onExpand={funcExpandedKeys => this.setState({ funcExpandedKeys })}
>
{
_.map(funcDatalist, item => {
const { name, dataType, children = [] } = item;
return <TreeNode title={name} disabled={disabled} key={dataType}>
{
_.map(children, (child, childIndex) => {
const { name: childName, chineseName } = child;
return (
<TreeNode
disabled={disabled}
title={
<div className="funcListTitle"
onClick={() => this.props.onFuncSelect(childName)}
onMouseEnter={() => this.setState({ funcHoverItem: child })}>
<span className="functionName" title={childName}>{childName}</span>
<span className="functionDesc" title={chineseName}>{chineseName}</span>
</div>
}
key={childIndex}
/>
);
})
}
</TreeNode>;
})
}
</Tree>
</div>
</div>
<div className="excel-codeAction-item">
<div className="excel-codeAction-header">
<div className="excel-codeAction-header-title">
{!_.isEmpty(funcHoverItem) ? funcHoverItem.name : getLabel(111, "提示")}
</div>
</div>
<div className="excel-codeAction-content"><TipList tips={funcHoverItem}/></div>
</div>
</React.Fragment>
}
</div>
);
}
}
export default CodeAction;
const TipList = (props) => {
const { tips } = props;
const { paramDescs = [], formatString, description, example, result } = tips;
return _.isEmpty(tips) ? <div className="code-action-list">
<div className="code-action-tips">
{/*<div>{getLabel(111, "{C:选项} 用来选择特定选项字段下的备选项")}</div>*/}
{/*<div>{getLabel(111, "{U:姓名} 用来选择工作区成员")}</div>*/}
{/*<div>{getLabel(111, "{D:数据} 用来选择一个部门")}</div>*/}
</div>
</div> : <div className="code-action-list">
<div className="code-action-tips">
<div className="code-action-tips-title">{getLabel(111, "语法")}</div>
<div className="code-action-tips-info">
<div>{formatString}</div>
<div>{description}</div>
</div>
<div className="code-action-tips-title">{getLabel(111, "参数")}</div>
{
_.map(paramDescs, it => {
return <div className="code-action-tips-info">
<span>.</span>
<span>{it}</span>
</div>;
})
}
<div className="code-action-tips-title">{getLabel(111, "示例")}</div>
<span className="code-action-tips-info">{example}</span>
<div className="code-action-tips-title">{getLabel(111, "结果")}</div>
<span className="code-action-tips-info">{result}</span>
</div>
</div>;
};

View File

@ -0,0 +1,16 @@
export const keyboardBaseBtns=[
{ key:"+", label: "+" },
{ key:"-", label: "-" },
{ key:">", label: ">" },
{ key:">=", label: ">=" },
{ key:"=", label: "=" },
{ key:"*", label: "*" },
{ key:"/", label: "/" },
{ key:"<", label: "<" },
{ key:"<=", label: "<=" },
{ key:"!=", label: "!=" },
{ key:"()", label: "(" },
{ key:")", label: ")" },
{ key:"%", label: "%" },
{ key:" ", label: "space" },
]

View File

@ -0,0 +1,112 @@
// extendCodeMirror.js
/* eslint-disable */
import * as CodeMirror from "codemirror";
CodeMirror.extendMode("css", {
commentStart: "/*",
commentEnd: "*/",
newlineAfterToken: function (type, content) {
return /^[;{}]$/.test(content);
}
});
CodeMirror.extendMode("javascript", {
commentStart: "",
commentEnd: "",
// FIXME semicolons inside of for
newlineAfterToken: function (type, content, textAfter, state) {
if (this.jsonMode) {
return /^[\[,{]$/.test(content) || /^}/.test(textAfter) || /^]/.test(textAfter);
} else {
if (content == ";" && state.lexical && state.lexical.type == ")") return false;
return /^[;{}]$/.test(content) && !/^;/.test(textAfter);
}
}
});
CodeMirror.extendMode("xml", {
commentStart: "<!--",
commentEnd: "-->",
newlineAfterToken: function (type, content, textAfter) {
return type == "tag" && />$/.test(content) || /^</.test(textAfter);
}
});
// Comment/uncomment the specified range
CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
var cm = this, curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode;
cm.operation(function () {
if (isComment) { // Comment range
cm.replaceRange(curMode.commentEnd, to);
cm.replaceRange(curMode.commentStart, from);
if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
cm.setCursor(from.line, from.ch + curMode.commentStart.length);
} else { // Uncomment range
var selText = cm.getRange(from, to);
var startIndex = selText.indexOf(curMode.commentStart);
var endIndex = selText.lastIndexOf(curMode.commentEnd);
if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
// Take string till comment start
selText = selText.substr(0, startIndex)
// From comment start till comment end
+ selText.substring(startIndex + curMode.commentStart.length, endIndex)
// From comment end till string end
+ selText.substr(endIndex + curMode.commentEnd.length);
}
cm.replaceRange(selText, from, to);
}
});
});
// Applies automatic mode-aware indentation to the specified range
CodeMirror.defineExtension("autoIndentRange", function (from, to) {
var cmInstance = this;
this.operation(function () {
for (var i = from.line; i <= to.line; i++) {
cmInstance.indentLine(i, "smart");
}
});
});
// Applies automatic formatting to the specified range
CodeMirror.defineExtension("autoFormatRange", function (from, to) {
var cm = this;
var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
var tabSize = cm.getOption("tabSize");
var out = "", lines = 0, atSol = from.ch == 0;
function newline() {
out += "\n";
atSol = true;
++lines;
}
for (var i = 0; i < text.length; ++i) {
var stream = new CodeMirror.StringStream(text[i], tabSize);
while (!stream.eol()) {
var inner = CodeMirror.innerMode(outer, state);
var style = outer.token(stream, state), cur = stream.current();
stream.start = stream.pos;
if (!atSol || /\S/.test(cur)) {
out += cur;
atSol = false;
}
if (!atSol && inner.mode.newlineAfterToken &&
inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i + 1] || "", inner.state))
newline();
}
if (!stream.pos && outer.blankLine) outer.blankLine(state);
if (!atSol) newline();
}
cm.operation(function () {
cm.replaceRange(out, from, to);
for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
cm.indentLine(cur, "smart");
cm.setSelection(from, cm.getCursor(false));
});
});
// export default CodeMirror;
module.exports = { CodeMirror };

View File

@ -0,0 +1,175 @@
import React, { Component } from "react";
import { Button } from "antd";
import { WeaLocaleProvider } from "ecCom";
import { Controlled as CodeMirror } from "react-codemirror2";
import { keyboardBaseBtns } from "./constants";
import CodeAction from "./components/codeAction";
import cs from "classnames";
import "./index.less";
import "codemirror/lib/codemirror.css";
import "codemirror/lib/codemirror.js";
import "codemirror/mode/javascript/javascript.js";
require("./extendCodeMirror");
const getLabel = WeaLocaleProvider.getLabel;
class ExcelEditor extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
isFormter: false,
isCustomFunctionClick: false
};
this.editorRef = null;
this.timer = null;
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.value !== this.props.value && nextProps.value) this.setState({ value: nextProps.value });
}
componentWillUnmount() {
if (this.timer) clearInterval(this.timer);
this.setState({ isCustomFunctionClick: false });
}
autoFormatSelection = () => {
const { isFormter } = this.state;
if (isFormter) {
const script_length = this.editorRef.getValue().length;
const startPos = { line: 0, ch: 0, sticky: null };
const endPos = this.editorRef.doc.posFromIndex(script_length);
this.editorRef.setSelection(startPos, endPos);
this.editorRef.autoFormatRange(startPos, endPos);
this.editorRef.commentRange(true, startPos, endPos);
} else {
this.editorRef.undo();
this.editorRef.undo();
}
};
insertText = text => {
const cursor = this.editorRef.getCursor();
this.editorRef.replaceRange(text, cursor);
this.editorRef.refresh();
this.editorRef.focus();
};
replaceToWidget = (editor) => {
editor.getAllMarks().forEach(m => m.clear());
};
handleVariSelect = str => this.insertText(`{${str}}`);
handleFuncSelect = str => {
const cursor = this.editorRef.getCursor();
this.editorRef.replaceRange(`${str}()`, cursor);
this.timer = setTimeout(() => {
const { line, ch } = this.editorRef.getCursor();
this.editorRef.setCursor({ line, ch: ch - 1 });
this.editorRef.refresh();
this.editorRef.focus();
}, 100);
};
handleEditorRedo = () => {
const { ch, line } = this.editorRef.getCursor();
const delStr = this.editorRef.getRange({ line, ch: ch - 1 }, { line, ch });
const codeValue = this.editorRef.getValue();
if (delStr === "}") {
if (codeValue.slice(0, ch).lastIndexOf("{") === -1) {
this.editorRef.replaceRange("", { line, ch: ch - 1 }, { line, ch });
} else {
this.editorRef.replaceRange("", { line, ch: codeValue.slice(0, ch).lastIndexOf("{") }, { line, ch });
}
} else {
this.editorRef.replaceRange("", { line, ch: ch - 1 }, { line, ch });
}
this.editorRef.refresh();
this.editorRef.focus();
};
handleBackSpaceRedo = () => {
const { ch, line } = this.editorRef.getCursor();
const delStr = this.editorRef.getRange({ line, ch: ch - 1 }, { line, ch });
const codeValue = this.editorRef.getValue();
if (delStr === "}") {
if (codeValue.slice(0, ch).lastIndexOf("{") === -1) {
this.editorRef.replaceRange("", { line, ch: ch - 1 }, { line, ch });
} else {
this.editorRef.replaceRange("", { line, ch: codeValue.slice(0, ch).lastIndexOf("{") + 1 }, { line, ch });
}
}
this.editorRef.refresh();
this.editorRef.focus();
};
render() {
const { isFormter, isCustomFunctionClick } = this.state;
const { groupParams = {}, isCustomFunction, value, onChangeCustomFunction } = this.props;
const { referenceType } = groupParams;
return (
<React.Fragment>
<div className="excel-codeWrap">
<div className="excel-codeBox">
<CodeMirror
editorDidMount={editor => this.editorRef = editor}
value={this.state.value}
onBeforeChange={(editor, data, value) => {
this.setState({ value }, () => this.props.onChange(this.state.value));
}}
onChange={(editor, data, value) => {
this.replaceToWidget(editor, data, value);
}}
options={{
lineNumbers: false,
mode: "javascript",
autofocus: false,
styleActiveLine: true,
lineWrapping: true,
matchBrackets: true,
lint: false,
indentUnit: 2,
cursorHeight: 0.85,
placeholder: "",
showCursorWhenSelecting: true
}}
onKeyDown={(_, { keyCode }) => keyCode === 8 && this.handleBackSpaceRedo()}
/>
</div>
{
referenceType !== "sql" &&
<div className="excel-codeBox-keyboard">
<div className="excel-codeBox-keyboard-operate">
<div className="excel-codeBox-keyboard-operate-content">
{
_.map(keyboardBaseBtns, item => {
const { key, label } = item;
return <Button
key={key} title={label} size="small"
className={cs(key === " " ? "excel-codeBox-keyboard-space" : "excel-codeBox-keyboard-base")}
onClick={() => this.insertText(key)}
>{label}</Button>;
})
}
</div>
<div className="excel-codeBox-keyboard-operate-clear">
<Button title="←" size="small" className="excel-codeBox-keyboard-del"
onClick={this.handleEditorRedo}></Button>
<Button title="C" size="small" className="excel-codeBox-keyboard-clear"
onClick={() => this.setState({ value: "", isCustomFunctionClick: true })}>C</Button>
</div>
</div>
<Button type="ghost"
onClick={() => this.setState({ isFormter: !isFormter }, () => this.autoFormatSelection())}>
{!isFormter ? getLabel(111, "格式美化") : getLabel(111, "格式还原")}
</Button>
</div>
}
</div>
{/*公式参数列表*/}
<CodeAction groupParams={groupParams} isCustomFunction={isCustomFunction} onVariSelect={this.handleVariSelect}
onFuncSelect={this.handleFuncSelect} codeVal={value} isCustomFunctionClick={isCustomFunctionClick}
onChangeCustomFunction={onChangeCustomFunction}
/>
</React.Fragment>
);
}
}
export default ExcelEditor;

View File

@ -0,0 +1,199 @@
.excel-codeWrap {
width: 100%;
display: flex;
justify-content: space-around;
padding: 0 0 8px;
.excel-codeBox {
flex: 1;
overflow: auto;
background: #fff;
box-sizing: content-box;
border: 1px solid #e5e5e5;
span {
font-family: Liberation Mono, LiberationMonoRegular, Courier New, monospace;
}
.CodeMirror-code {
font-size: 16px;
}
.CodeMirror-scroll {
overflow-x: visible !important;
padding: 4px;
}
.CodeMirror-sizer {
margin-left: 0 !important;
}
.CodeMirror-gutters {
border-right: none;
background-color: #f7f7f7;
opacity: 0;
display: none;
}
}
.excel-codeBox-keyboard {
width: 272px;
min-height: 232px;
padding: 20px;
background: #fff;
border: 1px solid #e5e5e5;
border-left: none;
.excel-codeBox-keyboard-operate {
display: flex;
.excel-codeBox-keyboard-operate-content {
display: flex;
flex-wrap: wrap;
flex: 1;
.excel-codeBox-keyboard-base {
width: 30px;
height: 30px;
text-align: center;
margin: 0 10px 10px 0;
}
.excel-codeBox-keyboard-space {
width: 70px;
height: 30px;
}
}
.excel-codeBox-keyboard-operate-clear {
width: 30px;
.excel-codeBox-keyboard-del {
width: 30px;
height: 70px;
}
.excel-codeBox-keyboard-clear {
width: 30px;
height: 30px;
margin-top: 10px;
}
}
}
}
}
.excel-codeAction {
width: 100%;
display: flex;
justify-content: space-between;
.excel-codeAction-item:last-child {
margin-right: 0;
.excel-codeAction-header-title {
color: rgb(217, 82, 189);
}
}
.excel-codeAction-item {
width: 33%;
min-height: 317px;
flex: 1;
background: #fff;
border: 1px solid #e5e5e5;
margin-right: 16px;
display: flex;
flex-direction: column;
.excel-codeAction-header {
display: flex;
padding: 10px 16px;
border-bottom: 1px solid #e5e5e5;
.excel-codeAction-header-title {
flex: 1;
font-weight: 600;
}
}
.excel-codeAction-content {
flex: 1;
overflow: hidden auto;
padding: 0 16px;
max-height: 280px;
position: relative;
.variableOuterInput {
width: 100%;
margin-top: 10px;
position: sticky;
top: 10px;
background-color: #fff;
z-index: 1;
}
.variableTree {
li a:hover, li a.ant-tree-node-selected {
background: transparent;
}
li:first-child {
a {
padding: 2px 5px;
}
}
li a {
width: calc(100% - 16px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.ant-tree-title {
display: inline-block;
width: 100%;
.funcListTitle {
width: 100%;
display: flex;
justify-content: space-between;
& > span {
display: inline-block;
flex: 1 1;
overflow: hidden;
text-overflow: ellipsis;
word-break: keep-all;
white-space: nowrap;
}
.functionName {
max-width: 100px;
}
.functionDesc {
max-width: 100px;
text-align: right;
color: #999;
}
}
}
}
}
.code-action-list {
padding: 10px 0;
.code-action-tips-title{
height: 22px;
line-height: 22px;
}
.code-action-tips-info{
color: #999
}
}
}
}
}

View File

@ -32,6 +32,8 @@ import SysConfig from "./pages/sysConfig";
import RuleConfig from "./pages/ruleConfig";
import Appconfig from "./pages/appConfig";
import FieldManagement from "./pages/fieldManagement";
import AnalysisOfSalaryStatistics from "./pages/analysisOfSalaryStatistics";
import ReportView from "./pages/reportView";
import stores from "./stores";
import "./style/index";
@ -78,6 +80,8 @@ const DataAcquisition = (props) => props.children;
// sysconfig-1 规则配置
// appconfig 应用配置
// fieldManagement 字段管理
// analysisOfSalaryStatistics 薪酬统计分析
// reportView 薪酬报表查看
const Routes = (
<Route
@ -150,6 +154,8 @@ const Routes = (
<Route key="sysconfig-1" path="sysconfig-1" component={RuleConfig}/>
<Route key="appconfig" path="appconfig" component={Appconfig}/>
<Route key="fieldManagement" path="fieldManagement" component={FieldManagement}/>
<Route key="analysisOfSalaryStatistics" path="analysisOfSalaryStatistics" component={AnalysisOfSalaryStatistics}/>
<Route key="reportView" path="reportView" component={ReportView}/>
</Route>
);

View File

@ -0,0 +1,101 @@
import { WeaLocaleProvider } from "ecCom";
const { getLabel } = WeaLocaleProvider;
export const condition = [
{
items: [
{
colSpan: 1,
checkbox: false,
checkboxValue: false,
conditionType: "SELECT",
domkey: ["dimType"],
fieldcol: 14,
label: getLabel(111, "维度类型"),
labelcol: 6,
options: [],
detailtype: 3,
rules: "required|string",
viewAttr: 3
},
{
colSpan: 1,
conditionType: "SELECT",
domkey: ["setting4Qualitative"],
fieldcol: 14,
label: getLabel(111, "统计维度"),
labelcol: 6,
options: [],
rules: "required|string",
viewAttr: 3
},
{
colSpan: 1,
conditionType: "INPUT",
domkey: ["dimName"],
fieldcol: 14,
label: getLabel(111, "统计维度名称"),
labelcol: 6,
value: "",
rules: "required|string",
viewAttr: 3
},
{
colSpan: 1,
conditionType: "SELECT",
domkey: ["dimCode"],
fieldcol: 14,
label: getLabel(111, "分组所属字段"),
labelcol: 6,
options: [],
viewAttr: 2,
helpfulTip: "",
hide: true
},
{
colSpan: 1,
conditionType: "TEXTAREA",
domkey: ["remark"],
fieldcol: 14,
label: getLabel(111, "描述"),
labelcol: 6,
value: "",
viewAttr: 2
}
],
title: getLabel(111, "基础设置"),
defaultshow: true
}
];
export const reportCondition = [
{
items: [
{
colSpan: 1,
conditionType: "INPUT",
domkey: ["reportName"],
fieldcol: 14,
label: getLabel(111, "报表名称"),
labelcol: 6,
value: "",
rules: "required|string",
viewAttr: 3
},
{
colSpan: 1,
conditionType: "SELECT",
domkey: ["dimensionIds"],
fieldcol: 14,
label: getLabel(111, "统计维度"),
labelcol: 6,
options: [],
rules: "required|string",
viewAttr: 3,
helpfulTip: "",
hide: true
}
],
title: "",
defaultshow: true
}
];

View File

@ -0,0 +1,205 @@
/*
* Author: 黎永顺
* name: 新增统计维度弹框
* Description:
* Date: 2023/4/11
*/
import React, { Component } from "react";
import { WeaDialog, WeaLocaleProvider, WeaSearchGroup } from "ecCom";
import { Button, message, Modal } from "antd";
import { dimensionGetForm, dimensionSave } from "../../../apis/statistics";
import { getSearchs } from "../../../util";
import GroupSpacingEditTable from "./groupSpacingEditTable";
import GroupIndividualEditTable from "./groupIndividualEditTable";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
const keyObj = {
"RATION_GROUP_SPACING": "setting4RationGroupSpacing",
"RATION_GROUP_INDIVIDUAL": "setting4RationGroupIndividual"
};
class DimensionSlide extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
dimType: "QUALITATIVE",
setting4RationGroupSpacing: [],
setting4RationGroupIndividual: []
};
}
componentDidMount() {
this.props.initCondition();
}
componentWillReceiveProps(nextProps, nextContext) {
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");
}
if (nextProps.visible !== this.props.visible && !nextProps.visible) {
this.setState({
loading: false,
dimType: "QUALITATIVE",
setting4RationGroupSpacing: [],
setting4RationGroupIndividual: []
});
}
}
dimensionGetForm = (payload) => {
dimensionGetForm(payload).then(({ status, data }) => {
if (status) {
const { baseForm: { data: formData } } = data;
const setting = formData.setting ? JSON.parse(formData.setting) : {};
this.props.onChangeCondition(formData["dimType"], 1);
this.setState({
dimType: formData.dimType,
[keyObj[formData["dimType"]]]: setting
}, () => {
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 if (item === "setting4Qualitative" && formData.statsDim) {
this.props.form.updateFields({
setting4Qualitative: formData.statsDim
});
}
});
});
}
});
};
handleSave = () => {
const { dimType } = this.state;
const { condition, onCancel, formId } = this.props;
const { setting4Qualitative, dimCode, ...extraParams } = this.props.form.getFormParams();
let payload = { id: formId, ...extraParams };
if (dimType === "QUALITATIVE") {
if (!setting4Qualitative || !extraParams.dimName) {
Modal.warning({
title: getLabel(111, "信息确认"),
content: getLabel(111, "必要信息不完整,红色*为必填项!")
});
return;
}
const tjOptions = _.find(condition[0].items, item => item.domkey[0] === "setting4Qualitative").options;
const tjObj = _.find(tjOptions, item => item.key === setting4Qualitative);
payload = { ...payload, setting4Qualitative: { id: tjObj.key, name: tjObj.showname } };
} else {
if (!extraParams.dimName) {
Modal.warning({
title: getLabel(111, "信息确认"),
content: getLabel(111, "必要信息不完整,红色*为必填项!")
});
return;
}
if (dimType === "RATION_GROUP_SPACING") {
const { setting4RationGroupSpacing } = this.state;
const bool = _.every(setting4RationGroupSpacing, it => it.startValue !== "" && it.endValue !== "" && it.startValue <= it.endValue);
if (_.isEmpty(setting4RationGroupSpacing) || !bool) {
message.warning(getLabel(111, "请完善分组设置相关数据!分组设置不能为空,起始值结束值必填,且起始值需小于结束值!"));
return;
} else {
payload = {
...payload, dimCode,
setting4RationGroupSpacing: _.map(setting4RationGroupSpacing, (it, index) => ({
id: index + 1,
endValue: it.endValue,
startValue: it.startValue,
includeEnd: it.includeEnd === "1",
includeStart: it.includeStart === "1"
}))
};
}
} else if (dimType === "RATION_GROUP_INDIVIDUAL") {
const { setting4RationGroupIndividual } = this.state;
const bool = _.every(setting4RationGroupIndividual, it => it.value !== "");
if (_.isEmpty(setting4RationGroupIndividual) || !bool) {
message.warning(getLabel(111, "请完善分组设置相关数据!分组设置不能为空,且数值必填"));
return;
} else {
payload = {
...payload, dimCode,
setting4RationGroupIndividual: _.map(setting4RationGroupIndividual, (it, index) => ({ id: index + 1, ...it }))
};
}
}
}
this.setState({ loading: true });
dimensionSave(payload).then(({ status, errormsg }) => {
this.setState({ loading: false });
if (status) {
message.success(getLabel(111, "保存成功"));
onCancel(true);
this.props.form.resetForm();
} else {
message.error(errormsg || getLabel(111, "保存失败"));
}
}).catch(() => this.setState({ loading: false }));
};
formItemChange = (formObj) => {
const { onChangeCondition } = this.props;
const filedKey = _.keys(formObj)[0];
if (filedKey === "dimType") {
this.setState({
dimType: formObj[filedKey].value,
setting4RationGroupSpacing: [],
setting4RationGroupIndividual: []
}, () => onChangeCondition(formObj[filedKey].value));
}
};
handleConvertGroupDatasource = (data) => {
const { dimType } = this.state;
this.setState({ [keyObj[dimType]]: data });
};
render() {
const { loading, dimType, setting4RationGroupSpacing, setting4RationGroupIndividual } = this.state;
const { form, condition, formId } = this.props;
return (
<WeaDialog
{...this.props}
initLoadCss hasScroll
style={{ width: 900, height: 450 }}
className="dimensionSlideWrapper"
title={
<div className="dimensionTitle">
<span>{formId ? getLabel(111, "编辑统计维度") : getLabel(111, "新建统计维度")}</span>
<Button type="primary" onClick={this.handleSave} loading={loading}>{getLabel(111, "保存")}</Button>
</div>
}
>
{getSearchs(form, condition, 1, false, this.formItemChange)}
{
dimType !== "QUALITATIVE" &&
<WeaSearchGroup title={getLabel(111, "分组设置")} showGroup>
{
dimType === "RATION_GROUP_SPACING" &&
<GroupSpacingEditTable onChange={this.handleConvertGroupDatasource}
setting4RationGroupSpacing={setting4RationGroupSpacing}/>
}
{
dimType === "RATION_GROUP_INDIVIDUAL" &&
<GroupIndividualEditTable onChange={this.handleConvertGroupDatasource}
setting4RationGroupIndividual={setting4RationGroupIndividual}/>
}
</WeaSearchGroup>
}
</WeaDialog>
);
}
}
export default DimensionSlide;

View File

@ -0,0 +1,115 @@
/*
* Author: 黎永顺
* name: 统计维度管理列表
* Description:
* Date: 2023/4/11
*/
import React, { Component } from "react";
import { WeaLocaleProvider, WeaTable } from "ecCom";
import { message, Modal } from "antd";
import { dimensionDelete, dimensionList } from "../../../apis/statistics";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
class DimensionTable extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
dataSource: [],
pageInfo: {
current: 1, pageSize: 10, total: 0
}
};
}
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: current, pageSize, total, list: dataSource } = data;
this.setState({
dataSource,
pageInfo: {
...pageInfo,
current, pageSize, total
}
});
}
}).catch(() => this.setState({ loading: false }));
};
dimensionDelete = (payload) => {
Modal.confirm({
title: getLabel(111, "信息确认"),
content: getLabel(111, "确认要删除吗?"),
onOk: () => {
dimensionDelete(payload).then(({ status, errormsg }) => {
if (status) {
message.success(getLabel(111, "删除成功"));
this.dimensionList();
} else {
message.error(errormsg || getLabel(111, "删除失败"));
}
});
}
});
};
render() {
const { dataSource, loading, pageInfo } = this.state;
const { onEdit } = this.props;
const pagination = {
...pageInfo,
showTotal: total => `${getLabel(111, "共")} ${total} ${getLabel(111, "条")}`,
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: getLabel(111, "统计维度") },
{ dataIndex: "remark", title: getLabel(111, "描述") },
{ dataIndex: "dimType", title: getLabel(111, "维度类型") },
{
dataIndex: "operate", title: getLabel(111, "操作"),
render: (_, record) => {
return (
<span className="space10">
<a href="javascript: void(0);" onClick={() => onEdit(record.id)}>{getLabel(111, "编辑")}</a>
<a href="javascript: void(0);"
onClick={() => this.dimensionDelete([record.id])}>{getLabel(111, "删除")}</a>
</span>
);
}
}
];
return (
<WeaTable
rowKey="id"
className="dimensionTableWrapper"
dataSource={dataSource}
pagination={pagination}
loading={loading}
columns={columns}
/>
);
}
}
export default DimensionTable;

View File

@ -0,0 +1,52 @@
/*
* Author: 黎永顺
* name: 分组设置-定量-单项式分组编辑表格
* Description:
* Date: 2023/4/12
*/
import React, { Component } from "react";
import { WeaLocaleProvider, WeaTableEdit } from "ecCom";
const { getLabel } = WeaLocaleProvider;
class GroupIndividualEditTable extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: []
};
}
componentDidMount() {
const { setting4RationGroupIndividual } = this.props;
this.setState({
dataSource: setting4RationGroupIndividual
});
}
handleChangeTableData = (dataSource) => {
const { onChange } = this.props;
this.setState({ dataSource }, () => onChange(this.state.dataSource));
};
render() {
const { dataSource } = this.state;
const columns = [
{
title: getLabel(111, "分组设置值"),
dataIndex: "value",
key: "value",
com: [
{ label: "", key: "value", type: "INPUTNUMBER" }
]
}
];
return (
<WeaTableEdit
draggable={true} columns={columns} datas={dataSource}
showCopy={false} deleteConfirm onChange={this.handleChangeTableData}
/>
);
}
}
export default GroupIndividualEditTable;

View File

@ -0,0 +1,100 @@
/*
* Author: 黎永顺
* name: 分组设置-定量-组距式分组编辑表格
* Description:
* Date: 2023/4/12
*/
import React, { Component } from "react";
import { WeaLocaleProvider, WeaTableEdit } from "ecCom";
const { getLabel } = WeaLocaleProvider;
class GroupSpacingEditTable extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: []
};
}
componentDidMount() {
const { setting4RationGroupSpacing } = this.props;
this.setState({
dataSource: _.map(setting4RationGroupSpacing, item => {
return {
...item,
includeStart: item.includeStart ? "1" : "0",
includeEnd: item.includeEnd ? "1" : "0"
};
})
});
}
handleChangeTableData = (dataSource) => {
const { onChange } = this.props;
this.setState({ dataSource }, () => onChange(this.state.dataSource));
};
render() {
const { dataSource } = this.state;
const columns = [
{
title: getLabel(111, "起始值"),
dataIndex: "startValue",
key: "startValue",
com: [
{ label: "", key: "startValue", type: "INPUTNUMBER" }
]
},
{
title: getLabel(111, "含"),
dataIndex: "includeStart",
key: "includeStart",
com: [
{
type: "CHECKBOX",
key: "includeStart",
otherParams: { content: getLabel(111, "含") }
}
]
},
{
title: getLabel(111, "至"),
dataIndex: "to",
key: "to",
com: [
{ label: "", type: "TEXT" }
]
},
{
title: getLabel(111, "结束值"),
dataIndex: "endValue",
key: "endValue",
com: [
{ label: "", key: "endValue", type: "INPUTNUMBER" }
]
},
{
title: getLabel(111, "含"),
dataIndex: "includeEnd",
key: "includeEnd",
com: [
{
type: "CHECKBOX",
key: "includeEnd",
otherParams: { content: getLabel(111, "含") }
}
]
}
];
return (
<WeaTableEdit
draggable={true} deleteConfirm columns={columns}
datas={_.map(dataSource, item => ({ ...item, to: getLabel(111, "至") }))}
showCopy={false} onChange={this.handleChangeTableData}
/>
);
}
}
export default GroupSpacingEditTable;

View File

@ -0,0 +1,19 @@
/*
* Author: 黎永顺
* name: 报表表单
* Description:
* Date: 2023/4/17
*/
import React, { Component } from "react";
import { getSearchs } from "../../../util";
class ReportForm extends Component {
render() {
const { form, condition } = this.props;
return (
<React.Fragment>{getSearchs(form, condition, 1, false)}</React.Fragment>
);
}
}
export default ReportForm;

View File

@ -0,0 +1,106 @@
/*
* Author: 黎永顺
* name: 统计表
* Description:
* Date: 2023/4/17
*/
import React, { Component } from "react";
import { WeaLocaleProvider } from "ecCom";
import { Button, Col, Dropdown, Menu, message, Modal, Row } from "antd";
import { reportStatisticsReportDelete, reportStatisticsReportList } from "../../../apis/statistics";
import "../index.less";
const SubMenu = Menu.SubMenu;
const { getLabel } = WeaLocaleProvider;
class ReportList extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: []
};
}
componentDidMount() {
this.reportStatisticsReportList();
}
handleOptsClick = ({ key }, id, dimensionId) => {
if (key === "delete") {
this.reportStatisticsReportDelete(id.split(","));
} else if (key === "edit") {
this.props.onEdit("addReport", id);
}
};
reportStatisticsReportDelete = (payload) => {
Modal.confirm({
title: getLabel(111, "信息确认"),
content: getLabel(111, "确认删除本条数据吗?"),
onOk: () => {
const { reportName = "" } = this.props;
reportStatisticsReportDelete(payload).then(({ status, errormsg }) => {
if (status) {
message.success(getLabel(111, "删除成功"));
this.reportStatisticsReportList({ reportName });
} else {
message.error(errormsg || getLabel(111, "删除失败"));
}
});
}
});
};
reportStatisticsReportList = (payload = {}) => {
reportStatisticsReportList(payload).then(({ status, data: dataSource }) => {
if (status) {
this.setState({ dataSource });
}
});
};
/*
* Author: 黎永顺
* Description: 报表查看
* Params:
* Date: 2023/4/20
*/
handleGoReportView = (id) => {
window.open(`${window.location.origin}/spa/hrmSalary/static/index.html#/main/hrmSalary/reportView?id=${id}`);
};
render() {
const { dataSource } = this.state;
return (
<Row gutter={16} className="reportRow">
{
_.isEmpty(dataSource) ? <div className="empty">{getLabel(111, "暂无数据")}</div> :
_.map(dataSource, it => {
const { reportName, dimension, id, dimensionId } = it;
return <Col className="gutter-row" span={6} onClick={() => this.handleGoReportView(id)}>
<div className="card-item">
<div className="cardLeft"><i className="icon-coms-fa"/></div>
<div className="cardCenter">
<span className="reportName">{reportName}</span>
<div className="dimension">
<div className="label">{getLabel(111, "统计维度")}</div>
<div className="value">{dimension}</div>
</div>
</div>
<div className="cardRight">
<Dropdown overlay={
<Menu onClick={e => this.handleOptsClick(e, id, dimensionId)}>
<Menu.Item key="edit">{getLabel(111, "编辑")}</Menu.Item>
<Menu.Item key="delete">{getLabel(111, "删除")}</Menu.Item>
</Menu>
}>
<Button type="ghost"><i className="icon-coms-more"/></Button>
</Dropdown>
</div>
</div>
</Col>;
})
}
</Row>
);
}
}
export default ReportList;

View File

@ -0,0 +1,70 @@
/*
* Author: 黎永顺
* name: 统计弹框
* Description:
* Date: 2023/4/10
*/
import React, { Component } from "react";
import { Button, message, Modal } from "antd";
import { WeaDialog, WeaLocaleProvider } from "ecCom";
import { reportStatisticsReportSave } from "../../../apis/ruleconfig";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
class StatisticsModal extends Component {
constructor(props) {
super(props);
this.state = {
loading: false
};
}
handleSaveReportList = () => {
const { form, id, onCancel } = this.props;
form.validateForm().then(f => {
if (f.isValid) {
const { dimensionIds, reportName } = form.getFormParams();
const payload = { id, reportName, dimensionIds: dimensionIds.split(",") };
this.setState({ loading: true });
reportStatisticsReportSave(payload).then(({ status, errormsg }) => {
this.setState({ loading: false });
if (status) {
onCancel(true);
message.success(getLabel(111, "保存成功"));
form.resetForm();
} else {
message.error(errormsg || getLabel(111, "保存失败"));
}
}).catch(() => this.setState({ loading: false }));
} else {
Modal.warning({
title: getLabel(111, "信息确认"),
content: getLabel(111, "必要信息不完整,红色*为必填项!")
});
}
});
};
render() {
const { loading } = this.state;
const { typeKey, onCancel } = this.props;
const buttons = typeKey === "addReport" ? [
<Button type="primary" onClick={this.handleSaveReportList} loading={loading}>{getLabel(111, "保存")}</Button>
] : [];
return (
<WeaDialog
{...this.props} hasScroll
style={typeKey === "addReport" ? { width: 600 } : { width: 640, height: 540 }}
buttons={buttons}
onCancel={onCancel}
initLoadCss
className="dimensionModalWrapper"
>
{this.props.children}
</WeaDialog>
);
}
}
export default StatisticsModal;

View File

@ -0,0 +1,297 @@
/*
* Author: 黎永顺
* name: 薪酬统计分析
* Description:
* Date: 2023/4/10
*/
import React, { Component } from "react";
import { inject, observer } from "mobx-react";
import { WeaInputSearch, WeaLocaleProvider, WeaReqTop } from "ecCom";
import { Button } from "antd";
import { condition, reportCondition } from "./components/conditions";
import { commonEnumList, reportGetForm } from "../../apis/ruleconfig";
import { dimensionGetForm } from "../../apis/statistics";
import StatisticsModal from "./components/statisticsModal";
import DimensionSlide from "./components/dimensionSlide";
import DimensionTable from "./components/dimensionTable";
import ReportList from "./components/reportList";
import ReportForm from "./components/reportForm";
import "./index.less";
const { getLabel } = WeaLocaleProvider;
@inject("taxAgentStore", "attendanceStore")
@observer
class Index extends Component {
constructor(props) {
super(props);
this.state = {
conditions: [],
convertConditions: [],
reportConditions: [],
selectedKey: "statistics",
reportName: "",
slideReq: {
visible: false, formId: ""
},
modalReq: {
title: "", visible: false,
typeKey: "", id: ""
}
};
}
componentDidMount() {
this.initReportFormCondition();
}
initReportFormCondition = (payload = {}) => {
const { attendanceStore: { reportForm } } = this.props;
reportGetForm(payload).then(({ status, data }) => {
if (status) {
const { statsDimOptions, data: detailData } = data;
if (_.isEmpty(payload)) {
this.setState({
reportConditions: _.map(reportCondition, item => {
return {
...item,
items: _.map(item.items, child => {
if (child.domkey[0] === "dimensionIds") {
return {
...child,
options: _.map(statsDimOptions, dimTypeItem => ({
key: dimTypeItem.id,
showname: dimTypeItem.content
}))
};
}
return { ...child };
})
};
})
}, () => {
reportForm.initFormFields(this.state.reportConditions);
});
} else {
reportForm.updateFields({
reportName: detailData.reportName,
dimensionIds: detailData.dimension.join(",")
});
}
}
});
};
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 } } = this.props;
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 };
})
};
})
}, () => {
statisticsForm.setCondition(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 };
})
};
})
}, () => {
statisticsForm.setCondition(this.state.conditions);
});
}
};
handleReqBtnsClick = (key, id = "") => {
if (key === "search") {
const { reportName } = this.state;
this.reportListRef.reportStatisticsReportList({ reportName });
} else {
const { modalReq } = this.state;
const title = key === "dimension" ?
<div className="dimensionTitle">
<span>{getLabel(111, "统计维度管理")}</span>
<Button type="primary" onClick={() => this.handleAddDimension()}>{getLabel(111, "新建统计维度")}</Button>
</div>
: getLabel(111, id ? "编辑报表" : "新建报表");
this.setState({
modalReq: {
...modalReq, id, title,
visible: true, typeKey: key
}
}, () => id && this.initReportFormCondition({ id }));
}
};
handleCancel = (refresh = false) => {
const { attendanceStore: { reportForm } } = this.props;
const { modalReq } = this.state;
this.setState({
modalReq: {
...modalReq, visible: false, id: ""
}
}, () => {
const { selectedKey, reportName, modalReq: { typeKey } } = this.state;
selectedKey === "statistics" && reportForm.resetForm();
typeKey === "dimension" && this.initReportFormCondition();
refresh && selectedKey === "statistics" && this.reportListRef.reportStatisticsReportList({ reportName });
});
};
handleAddDimension = (formId = "") => {
const { slideReq } = this.state;
this.setState({
slideReq: {
...slideReq, visible: true,
formId
}
});
};
handleClose = (initTable = false) => {
const { attendanceStore: { statisticsForm } } = this.props;
const { slideReq } = this.state;
this.setState({
slideReq: {
...slideReq, visible: false, formId: ""
}
}, () => {
statisticsForm.resetForm();
initTable && this.dimensionTableRef.dimensionList();
});
};
render() {
const { taxAgentStore: { statisticsReportBtn }, attendanceStore: { statisticsForm, reportForm } } = this.props;
const { selectedKey, modalReq, slideReq, conditions, reportConditions, reportName } = this.state;
const buttons = [
<Button type="primary" onClick={() => this.handleReqBtnsClick("addReport")}>{getLabel(111, "新建报表")}</Button>,
<Button type="ghost"
onClick={() => this.handleReqBtnsClick("dimension")}>{getLabel(111, "维度统计管理")}</Button>,
<WeaInputSearch placeholder={getLabel(111, "请输入报表名称")} className="search"
value={reportName}
onChange={reportName => this.setState({ reportName })}
onSearch={() => this.handleReqBtnsClick("search")}/>
];
const tabs = [
{ key: "statistics", title: getLabel(111, "统计表") }
// { key: "detail", title: getLabel(111, "员工明细") }
];
return (
<WeaReqTop
title={getLabel(111, "薪酬统计报表")} icon={<i className="icon-coms-fa"/>}
iconBgcolor="#F14A2D" buttons={!statisticsReportBtn ? buttons.slice(-1) : buttons} buttonSpace={10}
showDropIcon={false} tabDatas={tabs} className="xc_tj_fx_wrapper"
selectedKey={selectedKey}
onChange={selectedKey => this.setState({ selectedKey }, () => this.state.selectedKey === "statistics" && this.initReportFormCondition())}
>
{
this.state.selectedKey === "statistics" &&
<ReportList
ref={dom => this.reportListRef = dom}
reportName={reportName}
onEdit={this.handleReqBtnsClick}
/>
}
<StatisticsModal {...modalReq} onCancel={this.handleCancel} form={reportForm}>
{
modalReq.typeKey === "dimension" &&
<DimensionTable ref={dom => this.dimensionTableRef = dom}
onEdit={id => this.handleAddDimension(id)}
/>
}
{
modalReq.typeKey === "addReport" &&
<ReportForm form={reportForm} condition={reportConditions}/>
}
</StatisticsModal>
<DimensionSlide
{...slideReq} onCancel={this.handleClose}
form={statisticsForm} condition={conditions}
initCondition={this.initCondition} onChangeCondition={this.handleChangeCondition}
/>
</WeaReqTop>
);
}
}
export default Index;

View File

@ -0,0 +1,141 @@
.xc_tj_fx_wrapper {
.search {
top: -3px;
margin-right: 10px;
width: 220px;
}
.wea-new-top-req-content {
background: #FFF;
.reportRow {
padding: 16px;
.gutter-row {
margin-bottom: 16px;
border-radius: 6px;
.card-item {
border-radius: 6px;
display: flex;
height: 90px;
justify-content: space-between;
padding: 22px 0 22px 16px;
border: 1px solid #e5e5e5;
.cardLeft {
display: flex;
align-items: center;
justify-content: center;
i {
padding: 10px;
color: #FFF;
font-size: 20px;
border-radius: 50%;
background-color: #ff666a;
}
}
.cardCenter {
display: flex;
flex: 1;
flex-direction: column;
justify-content: space-between;
margin-left: 10px;
.reportName {
font-size: 14px;
color: #111;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dimension {
display: flex;
.label {
height: 12px;
font-size: 12px;
color: #999;
line-height: 12px;
font-weight: 400;
}
.value {
height: 12px;
font-size: 12px;
color: #111;
line-height: 12px;
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.cardRight {
display: flex;
align-items: center;
justify-content: center;
.ant-btn-ghost {
color: #999;
border: 1px solid transparent;
}
.ant-btn-ghost:focus, .ant-btn-ghost:hover,
.ant-btn-ghost.active, .ant-btn-ghost:active {
color: #2baee9;
background-color: #FFF;
border: 1px solid #e5e5e5;
}
}
}
}
.card-item:hover {
cursor: pointer;
box-shadow: 0 3px 12px 0 rgba(0, 0, 0, .12);
}
}
.empty {
font-size: 16px;
width: 100%;
text-align: center;
margin-top: 26px;
}
}
}
//统计维度弹框
.dimensionModalWrapper, .dimensionSlideWrapper {
.dimensionTitle {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.dimensionTableWrapper {
.space10 {
a:first-child {
margin-right: 10px;
}
}
}
}
.dimensionSlideWrapper, .dimensionModalWrapper {
.wea-search-group {
.wea-select, .ant-select, .ant-select-selection {
width: 100%;
}
}
}

View File

@ -2,8 +2,8 @@ import React from "react";
import ImportModal from "../../../../components/importModal";
import { Badge, Button, message } from "antd";
import { inject, observer } from "mobx-react";
import SelectFieldModal from "./selectFieldModal";
import { getQueryString } from "../../../../util/url";
import AddHeaderFieldsModal from "./addHeaderFieldsModal";
@inject("calculateStore", "standingBookStore")
@observer
@ -16,7 +16,10 @@ export default class AcctResultImportModal extends React.Component {
salaryItemIds: ""
},
step: 0,
selectFieldVisible: false
selectFieldVisible: false,
addHeadFields: {
visible: false, itemsByGroup: []
}
};
}
@ -24,11 +27,6 @@ export default class AcctResultImportModal extends React.Component {
const { id } = this.props;
if (id) {
this.getImportField();
// let modalParam = { ...this.state.modalParam };
// modalParam.salaryAcctRecordId = id;
// this.setState({
// modalParam
// });
} else {
this.setState({
modalParam: { ...this.state.modalParam, salaryAcctRecordId: "123" }
@ -36,18 +34,31 @@ export default class AcctResultImportModal extends React.Component {
}
}
getImportField=()=>{
getImportField = () => {
const { calculateStore: { getImportField }, id } = this.props;
const { addHeadFields } = this.props;
getImportField(id).then(data => {
this.setState({
modalParam:{
addHeadFields: {
...addHeadFields,
itemsByGroup: _.map(data.itemsByGroup, item => {
return {
...item,
salaryItems: _.map(item.salaryItems, it => ({
...it,
checked: false
}))
};
})
},
modalParam: {
...this.state.modalParam,
salaryAcctRecordId: id,
salaryItemIds: data.checkItems.join(",")
}
});
});
}
};
// 获取模板
handleAccResultTemplateLink() {
@ -98,58 +109,22 @@ export default class AcctResultImportModal extends React.Component {
}
// 渲染第一步表单
renderFormComponent() {
renderFormComponent = () => {
return <Badge
count={!_.isEmpty(this.state.modalParam.salaryItemIds) ? this.state.modalParam.salaryItemIds.split(",").length : 0}>
<Button onClick={() => {
this.handleSelectedField();
}}>请选择表单字段</Button>
<Button onClick={this.handleSelectedField}>请选择表单字段</Button>
</Badge>;
}
};
// 选择表单字段
handleSelectedField() {
handleSelectedField = () => {
this.setState({
selectFieldVisible: true
addHeadFields: {
...this.state.addHeadFields,
visible: true
}
});
}
// 添加表头字段
handleAdd(fieldDate) {
let salaryItemIdsList = [];
if (!_.isEmpty(fieldDate.formulaItems)) {
fieldDate.formulaItems.map(item => {
if (item.checked) {
salaryItemIdsList.push(item.salaryItemId);
}
});
}
if (!_.isEmpty(fieldDate.inputItems)) {
fieldDate.inputItems.map(item => {
if (item.checked) {
salaryItemIdsList.push(item.salaryItemId);
}
});
}
if (!_.isEmpty(fieldDate.sqlItems)) {
fieldDate.sqlItems.map(item => {
if (item.checked) {
salaryItemIdsList.push(item.salaryItemId);
}
});
}
let salaryItemIds = "";
if (salaryItemIdsList.length > 0) {
salaryItemIds = salaryItemIdsList.join(",");
}
let modalParam = { ...this.state.modalParam };
modalParam.salaryItemIds = salaryItemIds;
this.setState({
modalParam
});
this.props.onAdd(fieldDate);
}
};
// 初始化Import数据
handleImportModalInit() {
@ -197,7 +172,7 @@ export default class AcctResultImportModal extends React.Component {
importInsuranceAcctDetail,
importBalanceInsuranceDetail
} = standingBookStore;
const { step, selectFieldVisible, modalParam } = this.state;
const { step, modalParam, addHeadFields } = this.state;
return (
<div>
{
@ -223,7 +198,7 @@ export default class AcctResultImportModal extends React.Component {
!isStandingBook ?
fetchImportAcctResult(params) :
standingBookType === "difference" ?
importBalanceInsuranceDetail({...params, billMonth}) :
importBalanceInsuranceDetail({ ...params, billMonth }) :
importInsuranceAcctDetail(params);
}}
templateLink={() => {
@ -236,23 +211,19 @@ export default class AcctResultImportModal extends React.Component {
}}
/>
}
{
selectFieldVisible && <SelectFieldModal
isStandingBook={isStandingBook}
standingBookType={standingBookType}
id={this.props.id}
visible={selectFieldVisible}
fieldData={this.props.fieldData}
onAdd={(fieldDate) => {
this.handleAdd(fieldDate);
}}
onCancel={() => {
this.setState({
selectFieldVisible: false
});
}}
/>
}
<AddHeaderFieldsModal {...addHeadFields} selectItems={modalParam.salaryItemIds}
onCancel={() => this.setState({ addHeadFields: { ...addHeadFields, visible: false } })}
onAdd={(salaryItemIds) => this.setState({
addHeadFields: {
...addHeadFields,
visible: false
},
modalParam: {
...modalParam,
salaryItemIds: salaryItemIds.join(",")
}
})}
/>
</div>
);
}

View File

@ -0,0 +1,105 @@
/*
* Author: 黎永顺
* name: 表头字段添加
* Description:
* Date: 2023/5/17
*/
import React, { Component } from "react";
import { Button, Col, Row } from "antd";
import { WeaCheckbox, WeaDialog, WeaLocaleProvider, WeaSearchGroup } from "ecCom";
import "./index.less";
const { getLabel } = WeaLocaleProvider;
class AddHeaderFieldsModal extends Component {
constructor(props) {
super(props);
this.state = {
itemsCheckeds: [],
showOnlyChecked: false
};
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.visible !== this.props.visible && nextProps.visible) {
this.setState({
itemsCheckeds: nextProps.selectItems ? _.map(nextProps.selectItems.split(","), it => Number(it)) : []
});
}
}
handleShowOnlyChecked = (showOnlyChecked) => this.setState({ showOnlyChecked: !!Number(showOnlyChecked) });
handleSelectGroupAll = (groupId, checked) => {
const { itemsCheckeds } = this.state;
const { itemsByGroup } = this.props;
_.map(itemsByGroup, item => {
if (item.salarySobItemGroupId === groupId) {
if (!!Number(checked)) {
this.setState({
itemsCheckeds: [...itemsCheckeds, ..._.map(item.salaryItems, child => child.salaryItemId)]
});
} else {
this.setState({
itemsCheckeds: _.differenceWith(itemsCheckeds, _.map(item.salaryItems, child => child.salaryItemId), _.isEqual)
});
}
}
});
};
render() {
const { showOnlyChecked, itemsCheckeds } = this.state;
const { itemsByGroup } = this.props;
let dataSource = _.map(itemsByGroup, item => {
return {
...item,
salaryItems: _.map(item.salaryItems, child => {
return { ...child, checked: itemsCheckeds.includes(child.salaryItemId) };
})
};
});
if (showOnlyChecked) {
dataSource = _.map(dataSource, item => {
return { ...item, salaryItems: _.filter(item.salaryItems, it => !!it.checked) };
});
}
return (
<WeaDialog
{...this.props} hasScroll initLoadCss
scalable title={getLabel(111, "添加表头字段")}
style={{ width: 700, height: 484 }} className="addHeaderFieldsWrapper"
buttons={[
<Button type="primary" onClick={() => this.props.onAdd(itemsCheckeds)}>{getLabel(111, "添加")}</Button>,
<Button type="ghost" onClick={this.props.onCancel}>{getLabel(111, "取消")}</Button>
]}
bottomLeft={<WeaCheckbox content={getLabel(111, "只显示已选中字段")}
onChange={this.handleShowOnlyChecked}/>}
>
{
_.map(dataSource, item => {
const { salarySobItemGroupName, salaryItems, salarySobItemGroupId } = item;
const value = _.every(salaryItems, it => !!it.checked) ? "1" : "0";
return <WeaSearchGroup showGroup needTigger
title={<WeaCheckbox content={salarySobItemGroupName} value={value}
onChange={(val) => this.handleSelectGroupAll(salarySobItemGroupId, val)}/>}>
<Row gutter={16}>
{
!_.isEmpty(salaryItems) ?
_.map(salaryItems, it => {
const { salaryItemId, salaryItemName, checked } = it;
return <Col span={8} style={{ marginBottom: 16 }}>
<WeaCheckbox content={salaryItemName} value={checked ? "1" : "0"}
onChange={() => this.setState({ itemsCheckeds: _.xorWith(itemsCheckeds, [salaryItemId], _.isEqual) })}/>
</Col>;
}) : <Col span={24} style={{ minHeight: 20, padding: "5%", textAlign: "center" }}>暂无数据</Col>
}
</Row>
</WeaSearchGroup>;
})
}
</WeaDialog>
);
}
}
export default AddHeaderFieldsModal;

View File

@ -0,0 +1,11 @@
.addHeaderFieldsWrapper {
.wea-search-group {
.wea-title {
padding-left: 0 !important;
}
.wea-content {
padding: 8px 16px 0;
}
}
}

View File

@ -1,10 +1,9 @@
import React from "react";
import { WeaHelpfulTip, WeaInput, WeaTab } from "ecCom";
import { WeaHelpfulTip, WeaTab } from "ecCom";
import IssuedAndReissueTable from "./issuedAndReissueTable";
import { Col, Row } from "antd";
import PayrollItemsTable from "./payrollItemsTable";
import { inject, observer } from "mobx-react";
import { toJS } from "mobx";
import cs from "classnames";
import "./index.less";
@inject("calculateStore")
@ -22,25 +21,23 @@ export default class EditSalaryDetail extends React.Component {
acctresultDetail(this.props.id);
}
handleItemValueChange = (field, value, isInput) => {
console.log(field, value, isInput);
handleItemValueChange = (field, value, isInput, groupId) => {
const { calculateStore: { acctresultDetailForm, setAcctresultDetailForm } } = this.props;
let form = { ...acctresultDetailForm };
if (isInput === "inputItems") {
form.inputItems = acctresultDetailForm.inputItems.map(item => {
item = { ...item };
if (item.salaryItemName === field) {
item.resultValue = value;
if (isInput === "itemsByGroup") {
form.itemsByGroup = acctresultDetailForm.itemsByGroup.map(item => {
if (item.salarySobItemGroupId === groupId) {
return {
...item,
salaryItems: _.map(item.salaryItems, it => {
if (it.salaryItemId === field) {
return { ...it, resultValue: value };
}
return { ...it };
})
};
}
return item;
});
} else if (isInput === "formulaItems") {
form.formulaItems = acctresultDetailForm.formulaItems.map(item => {
item = { ...item };
if (item.salaryItemName === field) {
item.resultValue = value;
}
return item;
return { ...item };
});
} else if (isInput === "issuedAndReissueItems") {
form.issuedAndReissueItems = acctresultDetailForm.issuedAndReissueItems.map(item => {
@ -53,7 +50,6 @@ export default class EditSalaryDetail extends React.Component {
}
setAcctresultDetailForm(form);
};
renderTableTr = (data, isInput) => {
const tables = [];
const len = data.length;
@ -77,10 +73,10 @@ export default class EditSalaryDetail extends React.Component {
return tables;
};
render() {
const { calculateStore: { acctresultDetailForm } } = this.props;
const { selectedKey } = this.state;
const { itemsByGroup = [] } = toJS(acctresultDetailForm);
const topTab = [
{
title: "正常工资薪金所得",
@ -96,14 +92,8 @@ export default class EditSalaryDetail extends React.Component {
<div className="detailItemWrapper">
<div>
<span className="itemTitle">基本信息</span>
<WeaHelpfulTip
style={{ marginLeft: "10px" }}
width={200}
title="根据账套设置显示"
placement="topLeft"
/>
<WeaHelpfulTip style={{ marginLeft: "10px" }} title="提示:基本信息根据账套设置显示" placement="topLeft"/>
</div>
<div className="itemContent">
{
!_.isEmpty(acctresultDetailForm.employeeInfos) &&
@ -123,69 +113,9 @@ export default class EditSalaryDetail extends React.Component {
/>
}
{
selectedKey === "0" &&
<div>
<div className="detailItemWrapper">
<span className="itemTitle">输入项</span>
<div className="itemContent">
<Row>
{
acctresultDetailForm.inputItems && acctresultDetailForm.inputItems.map((item, index) => {
const len = acctresultDetailForm.inputItems.length;
return (
<Col span={8}>
<Row>
<Col span={12}
className={cs("itemLabel", { "borderB-none": Math.ceil((index + 1) / 3) === len / 3 })}>{item.salaryItemName}</Col>
<Col span={12} className={cs("itemValue", {
"borderB-none": Math.ceil((index + 1) / 3) === len / 3,
"borderR-none": (index + 1) % 3 === 0
})}><WeaInput value={item.resultValue} disabled={!item.canEdit} onChange={(value) => {
this.handleItemValueChange(item.salaryItemName, value, "inputItems");
}}/></Col>
</Row>
</Col>
);
})
}
</Row>
</div>
</div>
<div className="detailItemWrapper">
<span className="itemTitle">
<span>公式项</span>
<WeaHelpfulTip
style={{ marginLeft: "10px" }}
width={200}
title="提示:修改后,若点击核算,将按照公式核算,覆盖修改的数据"
placement="topLeft"
/>
</span>
<div className="itemContent">
<Row>
{
acctresultDetailForm.formulaItems && acctresultDetailForm.formulaItems.map((item, index) => {
const len = acctresultDetailForm.formulaItems.length;
return (
<Col span={8}>
<Row>
<Col span={12}
className={cs("itemLabel", { "borderB-none": Math.ceil((index + 1) / 3) === len / 3 })}>{item.salaryItemName}</Col>
<Col span={12} className={cs("itemValue", {
"borderB-none": Math.ceil((index + 1) / 3) === len / 3,
"borderR-none": (index + 1) % 3 === 0
})}><WeaInput value={item.resultValue} disabled={!item.canEdit} onChange={(value) => {
this.handleItemValueChange(item.salaryItemName, value, "formulaItems");
}}/></Col>
</Row>
</Col>
);
})
}
</Row>
</div>
</div>
</div>
selectedKey === "0" && _.map(itemsByGroup, item => {
return <PayrollItemsTable {...item} onChangeIssueReissueValue={this.handleItemValueChange}/>;
})
}
{
selectedKey === "1" &&

View File

@ -80,8 +80,7 @@
}
.editSalaryDetail {
padding: 20px;
padding-bottom: 40px;
padding: 20px 20px 40px;
.detailItemWrapper {
.itemTitle {
@ -127,43 +126,15 @@
}
}
}
& > .ant-row {
border: 1px solid rgba(0, 0, 0, .06);
}
.itemLabel {
background-color: #fafafa;
padding: 12px 6px;
height: 45px;
display: flex;
align-items: center;
justify-content: flex-start;
border-right: 1px solid rgba(0, 0, 0, .06);
border-bottom: 1px solid rgba(0, 0, 0, .06);
}
.borderB-none {
border-bottom: none !important;
}
.borderR-none {
border-right: none !important;
}
.itemValue {
padding: 12px 6px;
display: flex;
align-items: center;
height: 45px;
border-right: 1px solid rgba(0, 0, 0, .06);
border-bottom: 1px solid rgba(0, 0, 0, .06);
}
}
}
.itemRow {
line-height: 40px;
.wea-search-group {
padding: 0 !important;
.wea-title {
padding: 0 !important;
}
}
}
@ -254,7 +225,6 @@
z-index: 99;
top: 10px !important;
}
}
@media (min-width: 1260px) {

View File

@ -40,7 +40,7 @@ class IssuedAndReissueTable extends Component {
}
},
{
dataIndex: "salaryBackItemFormula",
dataIndex: "itemFormulaContent",
title: <span>
<span style={{ marginRight: 8 }}>核算公式</span>
<WeaHelpfulTip

View File

@ -0,0 +1,83 @@
/*
* Author: 黎永顺
* name: 薪资项目表格
* Description:
* Date: 2023/5/16
*/
import React, { Component } from "react";
import { WeaHelpfulTip, WeaInput, WeaInputNumber, WeaLocaleProvider, WeaSearchGroup, WeaTable } from "ecCom";
const { getLabel } = WeaLocaleProvider;
class PayrollItemsTable extends Component {
render() {
const {
salarySobItemGroupId,
salarySobItemGroupName,
salaryItems: dataSource,
onChangeIssueReissueValue
} = this.props;
const columns = [
{
dataIndex: "salaryItemName",
title: getLabel(111, "薪资项目"),
width: "15%",
render: (text) => {
return <span className="tdEllipsis" title={text}>{text}</span>;
}
},
{
dataIndex: "resultValue",
title: <span>
<span style={{ marginRight: 8 }}>{getLabel(111, "项目值")}</span>
<WeaHelpfulTip
title={getLabel(111, "1.若薪资项目有公式,手动编辑项目值后,则默认将手动编辑的项目值锁定;点击锁定图标,解锁手动编辑的项目值,公式生效,点击保存按照公式重新核算;重新核算后,不显示解锁图标。")}
placement="top" width={250}
/>
</span>,
width: "20%",
render: (text, record) => {
const { canEdit, dataType } = record;
return dataType === "number" ? <WeaInputNumber
disabled={!canEdit}
min={0}
precision={2}
value={text || 0}
onChange={(value) => onChangeIssueReissueValue(record.salaryItemId, value, "itemsByGroup", salarySobItemGroupId)}
/> : <WeaInput
disabled={!canEdit}
value={text}
onChange={(value) => onChangeIssueReissueValue(record.salaryItemId, value, "itemsByGroup", salarySobItemGroupId)}
/>;
}
},
{
dataIndex: "itemFormulaContent",
title: <span>
<span style={{ marginRight: 8 }}>{getLabel(111, "核算公式")}</span>
<WeaHelpfulTip
title={getLabel(111, "若薪资项目有公式,且项目值手动编辑修改过并点击锁定图标,则公式失效;若解除锁定,则项目公式重新生效;")}
placement="top" width={250}
/>
</span>,
width: "65%",
render: (text, record) => {
return <span className="tdEllipsis" title={text}>{_.isNil(text) ? "输入" : text}</span>;
}
}
];
return (
<WeaSearchGroup title={salarySobItemGroupName} showGroup needTigger>
<WeaTable
rowKey="salaryItemId"
dataSource={dataSource}
columns={columns}
bordered
pagination={false}
/>
</WeaSearchGroup>
);
}
}
export default PayrollItemsTable;

View File

@ -0,0 +1,30 @@
import React, { Component } from "react";
import { Button, Modal } from "antd";
import ExcelEditor from "../../components/excelEditor";
class Index extends Component {
constructor(props) {
super(props);
this.state = {
title: "DialogTitle",
visible: false,
lvisible: false
};
}
render() {
return (
<div>
<ExcelEditor onChange={()=>{}}/>
<Button type="primary" onClick={() => this.setState({ visible: true })}>显示对话框</Button>
<Modal title="第一个 Modal" visible={this.state.visible}
onCancel={() => this.setState({ visible: false })}
>
<ExcelEditor onChange={()=>{}}/>
</Modal>
</div>
);
}
}
export default Index;

View File

@ -1,10 +1,14 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { getQueryString } from "../../util/url";
import { WeaDialog, WeaError, WeaInput } from "ecCom";
import { Button, message } from "antd";
import Authority from "../mySalary/authority";
import ComputerTemplate from "../payroll/templatePreview/computerTemplate";
import PhoneTemplate from "../payroll/templatePreview/phoneTemplate";
import "../payroll/templatePreview/index.less";
import * as API from "../../apis/mySalaryBenefits";
import "./index.less";
@inject("mySalaryStore")
@observer
@ -12,6 +16,8 @@ export default class MobilePayroll extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false,
authCode: "",
mySalaryBillData: {
employeeInformation: {},
salaryTemplate: []
@ -24,11 +30,43 @@ export default class MobilePayroll extends React.Component {
const type = getQueryString("type");
this.id = getQueryString("id");
const { mySalaryStore: { init } } = this.props;
// type !== "phone" && init(false);
init(false);
type !== "phone" && init(false);
type === "phone" && this.initMobile();
this.getMySalaryBill(this.id);
}
initMobile = () => {
const { mySalaryStore: { setInitEmVerify } } = this.props;
if (window.em) {
API.isNeedSecondPwdVerify({ mouldCode: "HRM", itemCode: "SALARY" }).then(({ status, isNeedSecondAuth }) => {
if (status && isNeedSecondAuth) {
this.setState({ visible: true });
} else {
setInitEmVerify();
}
});
} else {
setInitEmVerify();
}
};
doSecondAuth = () => {
const { mySalaryStore: { setInitEmVerify } } = this.props;
if (!this.state.authCode) {
this.refs.weaError.showError();
return;
}
API.doSecondAuth({
authCode: this.state.authCode, mouldCode: "HRM", itemCode: "SALARY"
}).then(({ status, checkStatus, checkMsg }) => {
if (status && checkStatus === "1") {
message.success(checkMsg);
setInitEmVerify();
this.setState({ visible: false });
} else {
message.error(checkMsg);
}
});
};
getMySalaryBill = (salaryInfoId) => {
const { mySalaryStore: { getMySalaryBill } } = this.props;
const params = this.getUrlkey();
@ -57,7 +95,8 @@ export default class MobilePayroll extends React.Component {
};
render() {
const { mySalaryBillData } = this.state;
const { mySalaryStore: { clearLoading } } = this.props;
const { mySalaryBillData, visible } = this.state;
const type = getQueryString("type");
const employeeInformation = mySalaryBillData.employeeInformation ? mySalaryBillData.employeeInformation : {};
const salaryGroups = mySalaryBillData.salaryGroups ? mySalaryBillData.salaryGroups : [];
@ -67,18 +106,33 @@ export default class MobilePayroll extends React.Component {
overflowY: "hidden",
paddingBottom: "20px"
}}>
<WeaDialog
onCancel={() => this.setState({ visible: false }, () => clearLoading())}
title="请输入二次验证密码" visible={visible} initLoadCss
className="verifyWrapper"
hasScroll buttons={[
<Button type="primary" size="small" onClick={this.doSecondAuth}>确定</Button>
]}
>
<WeaError tipPosition="bottom" ref="weaError" error="此项必填">
<WeaInput value={this.state.authCode} onChange={authCode => this.setState({ authCode })}/>
</WeaError>
</WeaDialog>
{
type === "phone" ?
<div className="templatePreview">
<div className="contentWrapper">
<PhoneTemplate
isPreview
isMsgPreview
salaryTemplateShowSet={JSON.stringify(mySalaryBillData.salaryTemplate)}
salaryItemSet={!_.isEmpty(salaryGroups) ? JSON.stringify([employeeInformation, ...salaryGroups]) : []}
/>
<Authority ecId={`${this && this.props && this.props.ecId || ""}_Authority@lulowc`}
store={this.props.mySalaryStore}>
<div className="templatePreview">
<div className="contentWrapper">
<PhoneTemplate
isPreview
isMsgPreview
salaryTemplateShowSet={JSON.stringify(mySalaryBillData.salaryTemplate)}
salaryItemSet={!_.isEmpty(salaryGroups) ? JSON.stringify([employeeInformation, ...salaryGroups]) : []}
/>
</div>
</div>
</div>
</Authority>
:
<Authority ecId={`${this && this.props && this.props.ecId || ""}_Authority@lulowc`}
store={this.props.mySalaryStore}>

View File

@ -0,0 +1,17 @@
.verifyWrapper {
.ant-modal-content {
width: 80vw !important;
.ant-modal-body {
padding: 10% !important;
.wea-dialog-body, .wea-new-scroll {
height: 75px !important;
.wea-error {
width: 100%;
}
}
}
}
}

View File

@ -13,17 +13,17 @@ export default class PhoneTemplate extends React.Component {
this.state = {
salaryItemSet: [],
salaryTemplateShowSet: {
theme:'',
background:'',
textContentPosition: '',
textContent: ''
theme: "",
background: "",
textContentPosition: "",
textContent: ""
}
};
}
componentWillMount() {
if(this.props.isPreview) return;
if (this.props.isPreview) return;
let salaryTemplateShowSetStr = window.localStorage.getItem("salary-showset");
let salaryItemSetStr = window.localStorage.getItem("salaryItemSet");
this.setState({
@ -31,18 +31,28 @@ export default class PhoneTemplate extends React.Component {
salaryTemplateShowSet: JSON.parse(salaryTemplateShowSetStr)
});
}
componentDidMount() {
if (this.props.isMsgPreview && this.props.salaryItemSet && window.em) {
this.setState({
salaryItemSet: JSON.parse(this.props.salaryItemSet),
salaryTemplateShowSet: JSON.parse(this.props.salaryTemplateShowSet)
});
}
}
componentWillReceiveProps(nextProps) {
if(nextProps.salaryTemplateShowSet !== this.props.salaryTemplateShowSet){
if (nextProps.salaryTemplateShowSet !== this.props.salaryTemplateShowSet) {
this.setState({
salaryItemSet: JSON.parse(nextProps.salaryItemSet),
salaryTemplateShowSet: JSON.parse(nextProps.salaryTemplateShowSet),
salaryTemplateShowSet: JSON.parse(nextProps.salaryTemplateShowSet)
});
}
}
render() {
const { salaryTemplateShowSet, salaryItemSet }= this.state;
const { salaryTemplateShowSet, salaryItemSet } = this.state;
return (
<div className="computerTemplate phoneTemplate">
<div className="titleWrapper">
@ -57,7 +67,7 @@ export default class PhoneTemplate extends React.Component {
<div className="sobItemDiv" style={{ margin: "20px 10px" }}>
{
salaryTemplateShowSet.textContentPosition == 1 && salaryTemplateShowSet.textContent
salaryTemplateShowSet.textContentPosition == 1 && salaryTemplateShowSet.textContent
}
</div>
@ -73,7 +83,7 @@ export default class PhoneTemplate extends React.Component {
_.map(group.items, item => {
return <tr className="descriptions-row">
<th className="descriptions-item-label">{item.name}</th>
<td className="descriptions-item-content">{item.salaryItemValue || '-'}</td>
<td className="descriptions-item-content">{item.salaryItemValue || "-"}</td>
</tr>;
})
}

View File

@ -0,0 +1,405 @@
/*
* Author: 黎永顺
* name: 分析图标范围数据设置弹框
* Description:
* Date: 2023/5/11
*/
import React, { Component } from "react";
import { Button, Col, message, Row } from "antd";
import {
WeaDialog,
WeaError,
WeaFormItem,
WeaHelpfulTip,
WeaInputNumber,
WeaLocaleProvider,
WeaReqTop,
WeaSearchGroup,
WeaSelect
} from "ecCom";
import { saveRangeSetting } from "../../../apis/statistics";
import "./index.less";
const { getLabel } = WeaLocaleProvider;
class ChartsRangeSettingsModal extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
selectedKey: "1",
rangeSetting: {
itemValues: "",
itemColValue: "",
dimensionRange: "0"
},
rangeSettingExtra: {
itemSortValue: "",
itemColSortValue: "",
sortType: "0",
sortNum: ""
},
statisticalItemsOptions: [],
statisticalColsOptions: [],
rangeSettingExtraOptions: {
sortStatisticsOptions: [],
sortColumnsOptions: []
}
};
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.visible !== this.props.visible && nextProps.visible) {
const { columns } = nextProps;
this.setState({
statisticalItemsOptions: _.map(_.filter(columns, item => !_.isEmpty(item.children)), it => ({
key: it.column, showname: it.text,
children: _.map(it.children, child => ({ key: child.column, showname: child.text }))
}))
});
}
if (nextProps.rangeVal !== this.props.rangeVal && nextProps.rangeVal) {
const { rangeVal } = nextProps;
const { rangeSetting, rangeSettingExtra } = this.state;
this.setState({
rangeSetting: {
...rangeSetting,
..._.reduce(Object.keys(rangeSetting), (pre, cur) => {
return {
...pre,
[cur]: Array.isArray(rangeVal[cur]) ? rangeVal[cur].join(",") :
!_.isNil(rangeVal[cur]) ? rangeVal[cur].toString() : cur === "dimensionRange" ? "0" : ""
};
}, {})
},
rangeSettingExtra: {
...rangeSettingExtra,
..._.reduce(Object.keys(rangeSettingExtra), (pre, cur) => {
return {
...pre,
[cur]: !_.isNil(rangeVal[cur]) ? rangeVal[cur].toString() : cur === "sortType" ? "0" : ""
};
}, {})
}
}, () => {
const { statisticalItemsOptions } = this.state;
const { itemValues, itemSortValue } = rangeVal;
let statisticalColsOptions = [], sortStatisticsOptions = [];
_.forEach(itemValues, item => {
_.forEach(statisticalItemsOptions, child => {
if (item === child.key) {
statisticalColsOptions = [...statisticalColsOptions, ...child.children];
sortStatisticsOptions = [...sortStatisticsOptions, child];
}
});
});
this.setState({
statisticalColsOptions,
rangeSettingExtraOptions: {
sortStatisticsOptions
}
}, () => {
const { rangeSettingExtraOptions } = this.state;
const { sortStatisticsOptions } = rangeSettingExtraOptions;
this.setState({
rangeSettingExtraOptions: {
...rangeSettingExtraOptions,
sortColumnsOptions: !_.isEmpty(_.find(sortStatisticsOptions, it => it.key === itemSortValue)) ? _.find(sortStatisticsOptions, it => it.key === itemSortValue).children : []
}
});
});
});
}
}
handleSaveChartsRangeSettings = () => {
const { reportId, rangeVal } = this.props;
const { rangeSetting, rangeSettingExtra, selectedKey: chartsType } = this.state;
const { itemValues, itemColValue, dimensionRange } = rangeSetting;
if (!itemColValue && !itemValues) {
this.refs.itemValues.showError();
this.refs.itemColValue.showError();
return;
}
if (!itemValues) {
this.refs.itemValues.showError();
return;
}
if (!itemColValue) {
this.refs.itemColValue.showError();
return;
}
const payload = {
reportId, chartsType, id: rangeVal.id,
...rangeSetting,
itemValues: itemValues.split(",")
};
this.setState({ loading: true });
saveRangeSetting(
dimensionRange === "0" ? payload : { ...payload, ...rangeSettingExtra }
).then(({ status, errormsg }) => {
this.setState({ loading: false });
if (status) {
message.success(getLabel(111, "保存成功"));
this.props.onGetData();
this.handleCancel();
} else {
message.error(errormsg || getLabel(111, "保存失败"));
}
}).catch(() => this.setState({ loading: false }));
};
handleCancel = () => {
this.setState({
rangeSetting: {
itemValues: "",
itemColValue: "",
dimensionRange: "0"
},
rangeSettingExtra: {
itemSortValue: "",
itemColSortValue: "",
sortType: "0",
sortNum: ""
},
selectedKey: "1",
statisticalItemsOptions: [],
statisticalColsOptions: []
}, () => this.props.onCancel());
};
handleResetFormVal = () => {
const { selectedKey: chartsType } = this.state;
const { reportId } = this.props;
this.props.onChange({ reportId, chartsType });
};
handleChange = ({ key, val }) => {
const { rangeSetting } = this.state;
this.setState({
rangeSetting: {
...rangeSetting,
[key]: val
}
}, () => {
if (key === "itemValues") {
const { statisticalItemsOptions } = this.state;
let statisticalColsOptions = [], sortStatisticsOptions = [];
_.forEach(val.split(","), item => {
_.forEach(statisticalItemsOptions, child => {
if (item === child.key) {
statisticalColsOptions = [...statisticalColsOptions, ...child.children];
sortStatisticsOptions = [...sortStatisticsOptions, child];
}
});
});
this.setState({
statisticalColsOptions,
rangeSettingExtra: {
itemSortValue: "",
itemColSortValue: "",
sortType: "0",
sortNum: ""
},
rangeSettingExtraOptions: {
sortStatisticsOptions
}
});
}
});
};
handleChangeRange = ({ key, val }) => {
const { rangeSettingExtra } = this.state;
this.setState({
rangeSettingExtra: {
...rangeSettingExtra,
[key]: val
}
}, () => {
if (key === "itemSortValue") {
const { rangeSettingExtraOptions } = this.state;
const { sortStatisticsOptions } = rangeSettingExtraOptions;
this.setState({
rangeSettingExtraOptions: {
...rangeSettingExtraOptions,
sortColumnsOptions: _.find(sortStatisticsOptions, it => it.key === val).children
}
});
}
});
};
render() {
const {
selectedKey,
rangeSetting,
statisticalItemsOptions,
statisticalColsOptions,
rangeSettingExtra,
rangeSettingExtraOptions,
loading
} = this.state;
const dataShowItems = [
{
com: RangSelect({
name: "itemValues",
label: getLabel(111, "统计项"),
onChange: this.handleChange,
value: rangeSetting.itemValues,
options: statisticalItemsOptions,
multiple: selectedKey !== "0"
})
},
{
com: RangSelect({
name: "itemColValue",
label: getLabel(111, "统计列"),
onChange: this.handleChange,
value: rangeSetting.itemColValue,
options: statisticalColsOptions
})
}
];
const rangValItems = [
{
com: RangSelect({
name: "dimensionRange",
label: getLabel(111, "统计维度查看范围"),
onChange: this.handleChange,
value: rangeSetting.dimensionRange,
options: [
{ key: "0", showname: getLabel(111, "全部") },
{ key: "1", showname: getLabel(111, "设置范围") }
],
detailtype: 3,
helpfulTip: getLabel(111, "通过某个统计项统计值的排序,确定统计维度的查看范围。 例查看统计项【薪资成本】的统计值降序为前10的部门的分析图情况")
})
}
];
const rangValExtraItems = [
{
com: RangMultiItem({
label: getLabel(111, "设置范围"),
onChange: this.handleChangeRange,
value: rangeSettingExtra,
options: rangeSettingExtraOptions
})
}
];
const { dimensionRange } = rangeSetting;
return (
<WeaDialog
{...this.props} hasScroll style={{ width: 600 }}
buttons={[]} onCancel={this.handleCancel}
initLoadCss className="rangSettingWrapper"
title={<RangSetingTitle
loading={loading}
selectedKey={selectedKey}
onChange={selectedKey => this.setState({ selectedKey }, () => this.handleResetFormVal())}
onSaveChartsRangeSettings={this.handleSaveChartsRangeSettings}
/>}
>
<WeaSearchGroup
title={
<div className="rangSettingItemTitle">
<span>{getLabel(111, "统计项数据展示范围设置")}</span>
<WeaHelpfulTip
title={getLabel(111, "选择分析图要展示的统计项的统计列\n" +
"例:分析图想查看社保福利成本的合计和税前薪酬成本的合计\n" +
"则:统计项选择【社保福利成本】和【税前薪酬成本】查看列选择【本期合计】")}
placement="bottomLeft"
width={260}
/>
</div>
}
showGroup items={dataShowItems} needTigger col={1}
/>
<WeaSearchGroup
title={getLabel(111, "统计维度值范围设置")}
showGroup items={dimensionRange === "0" ? rangValItems : [...rangValItems, ...rangValExtraItems]}
needTigger col={1}
/>
</WeaDialog>
);
}
}
export default ChartsRangeSettingsModal;
const RangSetingTitle = (props) => {
const { selectedKey, onChange, onSaveChartsRangeSettings, loading } = props;
const btns = [<Button type="primary" loading={loading}
onClick={onSaveChartsRangeSettings}>{getLabel(111, "保存")}</Button>];
return <WeaReqTop
title={getLabel(111, "分析图数据展示范围设置")} icon={<i className="icon-coms-fa"/>}
iconBgcolor="#F14A2D" buttons={btns} showDropIcon={false}
tabDatas={[
{ key: "1", title: "柱状图" },
{ key: "2", title: "折线图" },
{ key: "0", title: "饼图" }
]} selectedKey={selectedKey} onChange={onChange}
/>;
};
const RangSelect = payload => {
const {
label, onChange, value, options = [],
viewAttr = 3, multiple = false, name,
detailtype = 1, helpfulTip
} = payload;
return (
<WeaFormItem label={
<React.Fragment>
<span style={{ marginRight: 8 }}>{label}</span>
{
helpfulTip &&
<WeaHelpfulTip
title={helpfulTip}
placement="bottomLeft"
width={260}
/>
}
</React.Fragment>
} labelCol={{ span: 8 }} wrapperCol={{ span: 12 }}>
<WeaError
tipPosition="bottom" ref={name}
error={getLabel(111, "此项必填")}>
<WeaSelect options={multiple ? options : [{ key: "", showname: "" }, ...options]} viewAttr={viewAttr}
value={value} multiple={multiple} detailtype={detailtype}
onChange={val => onChange({ key: name, val })}/>
</WeaError>
</WeaFormItem>
);
};
const RangMultiItem = payload => {
const { label, onChange, value, options } = payload;
const { itemSortValue, itemColSortValue, sortType, sortNum } = value;
const { sortStatisticsOptions, sortColumnsOptions } = options;
return (
<WeaFormItem label={label} labelCol={{ span: 8 }} wrapperCol={{ span: 12 }}>
<Row style={{ marginBottom: 16 }}>
<Col span={8}>{getLabel(111, "排序统计项:")}</Col>
<Col span={16}><WeaSelect options={sortStatisticsOptions} value={itemSortValue}
onChange={val => onChange({ key: "itemSortValue", val })}/></Col>
</Row>
<Row style={{ marginBottom: 16 }}>
<Col span={8}>{getLabel(111, "排序列:")}</Col>
<Col span={16}><WeaSelect options={sortColumnsOptions} value={itemColSortValue}
onChange={val => onChange({ key: "itemColSortValue", val })}/></Col>
</Row>
<Row style={{ marginBottom: 16 }}>
<Col span={12}>
<WeaSelect
options={
[
{ key: "0", showname: "升序" },
{ key: "1", showname: "降序" }
]
}
detailtype={3} value={sortType} onChange={val => onChange({ key: "sortType", val })}
/>
</Col>
<Col span={12} className="extraCol">
<span style={{ marginRight: 8 }}>{getLabel(111, "前")}</span>
<WeaInputNumber value={sortNum} precision={2} onChange={val => onChange({ key: "sortNum", val })}/>
</Col>
</Row>
</WeaFormItem>
);
};

View File

@ -0,0 +1,422 @@
import { WeaLocaleProvider } from "ecCom";
import { format_with_regex } from "../../../util";
const { getLabel } = WeaLocaleProvider;
export const condition = [
{
items: [
{
colSpan: 2,
checkbox: false,
checkboxValue: false,
conditionType: "SELECT",
domkey: ["taxAgent"],
fieldcol: 18,
label: getLabel(111, "个税扣缴义务人"),
labelcol: 6,
options: [],
multiple: true,
viewAttr: 2
},
{
browserConditionParam: {
completeParams: {},
conditionDataParams: {},
dataParams: {},
destDataParams: {},
hasAddBtn: false,
hasAdvanceSerach: true,
idSeparator: ",",
isAutoComplete: 1,
isDetail: 0,
isMultCheckbox: false,
isSingle: false,
linkUrl: "",
pageSize: 10,
quickSearchName: "",
replaceDatas: [],
title: getLabel(111, "分部"),
type: "164",
viewAttr: 2
},
colSpan: 2,
conditionType: "BROWSER",
domkey: ["subCompany"],
fieldcol: 18,
isQuickSearch: false,
label: getLabel(111, "分部"),
labelcol: 6,
viewAttr: 2
},
{
browserConditionParam: {
completeParams: {},
conditionDataParams: {},
dataParams: {},
destDataParams: {},
hasAddBtn: false,
hasAdvanceSerach: true,
idSeparator: ",",
isAutoComplete: 1,
isDetail: 0,
isMultCheckbox: false,
isSingle: false,
linkUrl: "",
pageSize: 10,
quickSearchName: "",
replaceDatas: [],
title: getLabel(111, "部门"),
type: "57",
viewAttr: 2
},
colSpan: 2,
conditionType: "BROWSER",
domkey: ["department"],
fieldcol: 18,
isQuickSearch: false,
label: getLabel(111, "部门"),
labelcol: 6,
viewAttr: 2
},
// {
// browserConditionParam: {
// completeParams: {},
// conditionDataParams: {},
// dataParams: {},
// destDataParams: {},
// hasAddBtn: false,
// hasAdvanceSerach: true,
// idSeparator: ",",
// isAutoComplete: 1,
// isDetail: 0,
// isMultCheckbox: false,
// isSingle: false,
// linkUrl: "",
// pageSize: 10,
// quickSearchName: "",
// replaceDatas: [],
// title: getLabel(111, "岗位"),
// type: "278",
// viewAttr: 2
// },
// colSpan: 2,
// conditionType: "BROWSER",
// domkey: ["position"],
// fieldcol: 18,
// isQuickSearch: false,
// label: getLabel(111, "岗位"),
// labelcol: 6,
// viewAttr: 2
// },
{
browserConditionParam: {
completeParams: {},
conditionDataParams: {},
dataParams: {},
destDataParams: {},
hasAddBtn: false,
hasAdvanceSerach: true,
idSeparator: ",",
isAutoComplete: 1,
isDetail: 0,
isMultCheckbox: false,
isSingle: false,
linkUrl: "",
pageSize: 10,
quickSearchName: "",
replaceDatas: [],
title: getLabel(111, "人员"),
type: "17",
viewAttr: 2
},
colSpan: 2,
conditionType: "BROWSER",
domkey: ["employee"],
fieldcol: 18,
isQuickSearch: false,
label: getLabel(111, "人员"),
labelcol: 6,
viewAttr: 2
},
{
colSpan: 2,
conditionType: "RANGEPICKER",
domkey: ["hiredate1", "hiredate2"],
fieldcol: 18,
label: getLabel(111, "入职日期"),
labelcol: 6,
viewAttr: 2
}
],
title: "",
defaultshow: true
}
];
const colorList = ["#709DF7", "#73DEB3", "#7585A2", "#F7C739", "#5FC3E3", "#AEE279", "#FF7F81"];
export const mapBarOptions = (params) => ({
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
},
backgroundColor: "#FFF",
borderColor: "#FFF",
borderWidth: "1",
borderRadius: "5",
textStyle: {
color: "#333"
},
formatter: function (params) {
let str = params[0].axisValue + "<br>";
for (let item of params) {
str += `<div>${item.marker}${item.seriesName}<span style='display:inline-block;width: 10px'></span><span style='font-weight: bold'>${format_with_regex(item.value)}</span></div>`;
}
return str;
}
},
legend: {
icon: "rect",
top: "0%",
right: "center",
itemGap: 10,
textStyle: {
fontSize: 12,//字体大小
color: "#B8B8B8"//字体颜色
}
},
grid: {
top: "10%",
right: "0%",
left: "2%",
bottom: "0%",
containLabel: true
},
xAxis: {
type: "category",
axisTick: {
alignWithLabel: true,
show: false
},
data: params.xAxis,
axisLabel: {
interval: 0,
margin: 10,
textStyle: {
fontSize: 11
}
}
},
yAxis: {
name: params.name,
axisLabel: {
padding: [3, 0, 0, 0],
formatter: "{value}",
color: "#666",
textStyle: {
fontSize: 11
}
},
nameTextStyle: {
color: "#787E95",
fontSize: 12
},
axisLine: {
show: true,
lineStyle: {
color: "transparent"
}
},
axisTick: {
show: false // 不显示坐标轴刻度线
},
splitLine: {
show: false,
lineStyle: {
color: "rgba(66, 192, 255, 0.1)"
}
}
},
series: _.map(params.data, (item, index) => {
return {
name: item.name,
barWidth: "32",
data: _.map(item.data, (it) => it.replace(/,/g, "")),
type: "bar",
itemStyle: {
normal: {
color: function (params) {
return colorList[params.seriesIndex] || colorList[Math.floor((Math.random() * colorList.length))];
}
}
},
label: {
show: true,
position: "insideBottom",
distance: 15,
align: "left",
verticalAlign: "middle",
rotate: "90",
formatter: function (params) {
if (parseInt(params.value) === 0) {
return ``;
} else {
return [
`{a|${format_with_regex(params.value)}} {b|${params.seriesName}}`
];
}
},
rich: {
a: {
fontWeight: "bold",
fontSize: 14,
color: "#333",
marginRight: 10
},
b: {
fontSize: 12,
color: "#333"
}
}
}
};
})
});
export const mapLineOptions = (params) => ({
tooltip: {
// 坐标轴指示器,坐标轴触发有效
trigger: "axis",
axisPointer: {
// 默认为直线,可选为:'line' | 'shadow'
type: "line"
}
},
legend: {
icon: "circle",
top: "0%",
right: "center",
itemGap: 20,
textStyle: {
fontSize: 12,//字体大小
color: "#787E95"//字体颜色
}
},
grid: {
top: "10%",
left: "3%",
right: "2%",
bottom: "3%",
containLabel: true
},
xAxis: [
{
type: "category",
boundaryGap: false,
data: params.xAxis,
axisTick: {
alignWithLabel: true,
show: false
},
// 修改坐标值样式
axisLabel: {
color: "#B8B8B8",
fontSize: 12,
show: true
},
axisLine: {
show: false
}
}
],
yAxis: [
{
type: "value",
// 修改坐标值样式
axisLabel: {
color: "#787E95",
fontSize: 14
},
nameTextStyle: {
color: "#787E95",
fontSize: 16
},
// 修改坐标轴线样式
axisLine: {
show: true,
lineStyle: {
color: "transparent"
}
},
axisTick: {
show: false // 不显示坐标轴刻度线
},
splitLine: {
lineStyle: {
color: "rgba(93,126,158,1)"
}
}
}
],
series: _.map(params.data, (item, index) => {
return {
name: item.name,
data: _.map(item.data, (it) => it.replace(/,/g, "")),
type: "line",
itemStyle: {
normal: {
color: function (params) {
return colorList[params.seriesIndex] || colorList[Math.floor((Math.random() * colorList.length))];
},
lineStyle: {
color: colorList[index] || colorList[Math.floor((Math.random() * colorList.length))]
}
}
}
};
})
});
export const mapPieOptions = (params) => ({
tooltip: {
show: true,
formatter: function (params) {
let str = params.seriesName + "<br>";
str += params.marker + params.name + ":" + format_with_regex(params.value) + "(" + params.percent + "%" + ")" + "<br>";
return str;
}
},
legend: {
icon: "rect",
top: "0%",
left: "2%",
orient: "vertical",
itemGap: 10,
textStyle: {
fontSize: 12,//字体大小
color: "#787E95"//字体颜色
}
},
series: _.map(params.data, item => {
return {
name: item.name,
data: _.map(item.data, (it) => ({ ...it, value: it.value.replace(/,/g, "") })),
type: "pie",
radius: "60%",
avoidLabelOverlap: true,
animation: false,
labelLine: {
show: true,
normal: {
length: 5,
align: "center"
}
},
itemStyle: {
normal: {
color: function (colors) {
return colorList[colors.dataIndex] || colorList[Math.floor((Math.random() * colorList.length))];
}
}
}
};
})
});

View File

@ -0,0 +1,416 @@
/*
* Author: 黎永顺
* name: 新建自定义统计项目弹框
* Description:
* Date: 2023/4/10
*/
import React, { Component } from "react";
import { Button, message, Modal } from "antd";
import {
WeaBrowser,
WeaCheckbox,
WeaDialog,
WeaError,
WeaFormItem,
WeaHelpfulTip,
WeaInput,
WeaInputNumber,
WeaLocaleProvider,
WeaTable
} from "ecCom";
import { reportStatisticsItemSave, statisticsItemGetform } from "../../../apis/statistics";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
class CustomStatisticsItemsModal extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
columns: [],
dataSource: [],
formData: {
itemValue: "", itemValueSpan: "",
itemName: ""
}
};
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.visible !== this.props.visible && nextProps.visible) {
this.statisticsItemGetform({ id: nextProps.statisticsItemId });
} else {
this.setState({
columns: [],
dataSource: [],
formData: {
itemValue: "", itemValueSpan: "",
itemName: ""
}
});
}
}
handleSaveStatisticalItems = () => {
const { dataSource, formData } = this.state;
const { id: statReportId, statisticsItemId } = this.props;
const { itemValue, itemName } = formData;
const isNoRules = _.some(dataSource, it => !!it.m2mValue || !!it.ratioValue || !!it.totalValue || !!it.y2yValue);
const isChainRequired = _.some(dataSource, it => !!it.m2mValue && (!it.m2mLowerLimit || !it.m2mUpperLimit));
const isChainValRight = _.some(dataSource, it => !!it.m2mValue && it.m2mLowerLimit !== 0 && it.m2mUpperLimit !== 0 && (Number(it.m2mLowerLimit) > Number(it.m2mUpperLimit)));
const isYoyRequired = _.some(dataSource, it => !!it.y2yValue && (!it.y2yLowerLimit || !it.y2yUpperLimit));
const isYoyValRight = _.some(dataSource, it => !!it.y2yValue && it.y2yLowerLimit !== 0 && it.y2yUpperLimit !== 0 && (Number(it.y2yLowerLimit) > Number(it.y2yUpperLimit)));
if (!itemValue && !itemName) {
this.refs.proError.showError();
this.refs.nameError.showError();
return;
}
if (!itemValue) {
this.refs.proError.showError();
return;
}
if (!itemName) {
this.refs.nameError.showError();
return;
}
if (!isNoRules) {
message.warning(getLabel(111, "请至少设置一个统计规则"));
return;
}
if (isChainRequired) {
message.warning(getLabel(111, "请完善环比增幅正常区间设置上下限"));
return;
}
if (isChainValRight) {
message.warning(getLabel(111, "环比增幅上下限设置错误"));
return;
}
if (isYoyRequired) {
message.warning(getLabel(111, "请完善同比增幅正常区间设置上下限"));
return;
}
if (isYoyValRight) {
message.warning(getLabel(111, "同比增幅上下限设置错误"));
return;
}
let payload = { statReportId, itemValue: itemValue.split(","), itemName };
payload = {
id: statisticsItemId,
...payload,
..._.reduce(dataSource, (pre, cur) => {
if (!!cur.m2mValue || !!cur.ratioValue || !!cur.totalValue || !!cur.y2yValue) {
const { y2yLowerLimit, y2yUpperLimit, m2mLowerLimit, m2mUpperLimit } = cur;
if (!!cur.m2mValue) {
return {
...pre,
[`${cur["id"]}Rule`]: {
m2mValue: cur.m2mValue.toString(),
ratioValue: cur.ratioValue.toString(),
totalValue: cur.totalValue.toString(),
y2yValue: cur.y2yValue.toString(),
m2mLowerLimit: m2mLowerLimit.toString(),
m2mUpperLimit: m2mUpperLimit.toString()
}
};
}
if (!!cur.m2mValue) {
return {
...pre,
[`${cur["id"]}Rule`]: {
m2mValue: cur.m2mValue.toString(),
ratioValue: cur.ratioValue.toString(),
totalValue: cur.totalValue.toString(),
y2yValue: cur.y2yValue.toString(),
y2yLowerLimit: y2yLowerLimit.toString(),
y2yUpperLimit: y2yUpperLimit.toString()
}
};
}
if (!!cur.y2yValue && !!cur.y2yValue) {
return {
...pre,
[`${cur["id"]}Rule`]: {
m2mValue: cur.m2mValue.toString(),
ratioValue: cur.ratioValue.toString(),
totalValue: cur.totalValue.toString(),
y2yValue: cur.y2yValue.toString(),
m2mLowerLimit: m2mLowerLimit.toString(),
m2mUpperLimit: m2mUpperLimit.toString(),
y2yLowerLimit: y2yLowerLimit.toString(),
y2yUpperLimit: y2yUpperLimit.toString()
}
};
}
return {
...pre,
[`${cur["id"]}Rule`]: {
m2mValue: cur.m2mValue.toString(),
ratioValue: cur.ratioValue.toString(),
totalValue: cur.totalValue.toString(),
y2yValue: cur.y2yValue.toString()
}
};
}
return { ...pre };
}, {})
};
if (statisticsItemId) {
Modal.confirm({
title: getLabel(111, "信息确认"),
content: getLabel(111, `确定要编辑统计项吗?编辑后,可能需要重新设置分析图设置。`),
onOk: () => this.reportStatisticsItemSave(payload)
});
} else {
this.reportStatisticsItemSave(payload);
}
};
reportStatisticsItemSave = (payload) => {
this.setState({ loading: true });
reportStatisticsItemSave(payload).then(({ status, errormsg }) => {
this.setState({ loading: false });
if (status) {
this.setState({
formData: {
itemValue: "", itemName: ""
}
}, () => this.props.onCancel(true));
} else {
message.error(errormsg);
}
}).catch(() => this.setState({ loading: false }));
};
statisticsItemGetform = (payload) => {
statisticsItemGetform(payload).then(({ status, data }) => {
if (status) {
const { formData } = this.state;
const { ruleData, baseForm } = data;
const { data: dataDetail } = baseForm;
const { columns, data: dataSource } = ruleData;
this.setState({
columns, dataSource,
formData: {
...formData,
itemName: dataDetail ? dataDetail.itemName : "",
itemValue: dataDetail ? _.map(dataDetail.itemValue, it => it.id).join() : "",
itemValueSpan: dataDetail ? _.map(dataDetail.itemValue, it => it.name).join() : ""
}
});
}
});
};
handleChangeColumnCheckBox = (key, value, id) => {
const { dataSource } = this.state;
this.setState({
dataSource: _.map(dataSource, it => {
if (it.id === id) {
if (key !== "totalValue" && !!value && value !== "0") {
return {
...it,
totalValue: Number(value),
[key]: Number(value)
};
}
return {
...it,
[key]: Number(value)
};
}
return { ...it };
})
});
};
handleChangeColumnAllChecked = (key, val) => {
const { dataSource } = this.state;
this.setState({
dataSource: _.map(dataSource, it => {
if (key !== "totalValue" && !!val && val !== "0") {
return {
...it,
totalValue: Number(val),
[key]: Number(val)
};
}
return {
...it,
[key]: Number(val)
};
})
});
};
handleChangeColumnM2MValue = (key, value, id) => {
const { dataSource } = this.state;
this.setState({
dataSource: _.map(dataSource, it => {
if (it.id === id) {
return {
...it,
[key]: value
};
}
return { ...it };
})
});
};
handleChangeStatisticalItems = (itemValue, _names, datas) => {
const { formData } = this.state;
this.setState({
formData: {
...formData,
itemValue,
itemValueSpan: _.map(datas, it => it.name).join(","),
itemName: datas.length === 1 ? _.map(datas, it => it.names).join(",") : ""
}
});
};
render() {
const { loading, columns, dataSource, formData } = this.state;
const { itemName, itemValue, itemValueSpan } = formData;
const { statisticsItemId } = this.props;
const cols = _.map(columns, it => {
const { text, column } = it;
if (column === "ruleName" || column === "ratio" || column === "m2m" || column === "y2y") {
const key = column === "ruleName" ? "total" : column;
return {
...it,
title: <span>
<WeaCheckbox
value={_.every(dataSource, child => !!child[`${key}Value`])}
onChange={val => this.handleChangeColumnAllChecked(`${key}Value`, val)}
/>
<span style={{ marginLeft: 8 }}>{text}</span>
</span>,
render: (txt, record) => {
return <span>
<WeaCheckbox
value={record[`${key}Value`].toString()}
onChange={val => this.handleChangeColumnCheckBox(`${key}Value`, val, record.id)}
/>
<span style={{ marginLeft: 8 }}>
{column === "ruleName" ? record["ruleName"] : text}
</span>
</span>;
}
};
} else if (column === "m2mLimit") {
return {
...it,
title: <span>
<span style={{ marginRight: 8 }}>{text}</span>
<WeaHelpfulTip title={getLabel(111, "如:增幅>10%,差值和增幅标记为红色,增幅<-10%标记为绿色")}
placement="top" width={200}/>
</span>,
render: (txt, record) => {
return !!record["m2mValue"] && <IntervalSettingsComp
LowerLimit={record[`${column.replace("Limit", "")}LowerLimit`]}
UpperLimit={record[`${column.replace("Limit", "")}UpperLimit`]}
onChange={(type, val) => this.handleChangeColumnM2MValue(`${column.replace("Limit", "")}${type === "min" ? "LowerLimit" : "UpperLimit"}`, val, record.id)}
/>;
}
};
} else if (column === "y2yLimit") {
return {
...it,
title: <span>
<span style={{ marginRight: 8 }}>{text}</span>
<WeaHelpfulTip title={getLabel(111, "如:增幅>10%,差值和增幅标记为红色,增幅<-10%标记为绿色")}
placement="top" width={200}/>
</span>,
render: (txt, record) => {
return !!record["y2yValue"] && <IntervalSettingsComp
LowerLimit={record[`${column.replace("Limit", "")}LowerLimit`]}
UpperLimit={record[`${column.replace("Limit", "")}UpperLimit`]}
onChange={(type, val) => this.handleChangeColumnM2MValue(`${column.replace("Limit", "")}${type === "min" ? "LowerLimit" : "UpperLimit"}`, val, record.id)}
/>;
}
};
}
});
return (
<WeaDialog
{...this.props} hasScroll buttons={[]} initLoadCss
title={
<div className="itemsTitle">
<span>{statisticsItemId ? getLabel(111, "编辑自定义统计项目") : getLabel(111, "新建自定义统计项目")}</span>
<Button type="primary" loading={loading}
onClick={this.handleSaveStatisticalItems}>{getLabel(111, "保存")}</Button>
</div>
}
style={{ width: 900, height: 450 }}
className="statisticItemsWrapper"
>
<div className="statisticItemsBox">
<WeaFormItem label={getLabel(111, "统计项目")} labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
<WeaError tipPosition="bottom" ref="proError" error={getLabel(111, "此项必填")}>
<WeaBrowser
title={getLabel(111, "统计项目")}
type={162}
viewAttr={3}
isSingle={false}
value={itemValue}
replaceDatas={itemValue ? _.map(itemValue.split(","), (it, idx) => ({
id: it,
name: itemValueSpan.split(",")[idx]
})) : []}
completeParams={{
type: 162,
fielddbtype: "browser.salaryItemBrowser",
f_weaver_belongto_usertype: "0"
}}
conditionDataParams={{
type: "browser.salaryItemBrowser",
fielddbtype: "browser.salaryItemBrowser",
f_weaver_belongto_usertype: "0"
}}
dataParams={{
type: "browser.salaryItemBrowser",
f_weaver_belongto_usertype: "0"
}}
destDataParams={{
type: "browser.salaryItemBrowser",
f_weaver_belongto_usertype: "0"
}}
isMultCheckbox
inputStyle={{ width: "100%" }}
onChange={this.handleChangeStatisticalItems}
/>
</WeaError>
</WeaFormItem>
<WeaFormItem label={getLabel(111, "统计项名称")} labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
<WeaError tipPosition="bottom" ref="nameError" error={getLabel(111, "此项必填")}>
<WeaInput value={itemName} viewAttr={3}
onChange={itemName => this.setState({ formData: { ...formData, itemName } })}/>
</WeaError>
</WeaFormItem>
<div className="customRuleTableWrapper">
<WeaTable
dataSource={dataSource}
columns={cols}
pagination={false}
/>
</div>
</div>
</WeaDialog>
);
}
}
export default CustomStatisticsItemsModal;
/*
* Author: 黎永顺
* Description: 区间设置
* Params:
* Date: 2023/4/23
*/
const IntervalSettingsComp = (props) => {
const { LowerLimit, UpperLimit, onChange } = props;
return <div className="intervalSettingsCompWrapper">
<WeaInputNumber value={LowerLimit} precision={2} onChange={val => onChange("min", val)}/>
<span className="increaseTitle">{`% <${getLabel(111, "增幅")}<`}</span>
<WeaInputNumber value={UpperLimit} precision={2} onChange={val => onChange("max", val)}/>
<span className="pecentTitle">%</span>
</div>;
};

View File

@ -0,0 +1,50 @@
.rangSettingWrapper {
.wea-search-group {
.wea-error, .wea-select, .ant-select, .ant-select-selection {
width: 100%;
}
}
.ant-modal-content {
.wea-browser-single-icon-circle {
display: none;
}
.wea-new-top-req-main {
border-radius: 10px;
background: transparent;
}
.wea-new-top-req, .wea-new-top-req-title > div:last-child {
background: transparent;
}
.wea-new-top-req-title {
& > div:first-child {
width: 216px !important;
& > div:first-child {
max-width: 415px !important;
}
}
}
.ant-col-22, .text-elli {
padding-left: 0 !important;
}
}
.rangSettingItemTitle {
display: flex;
align-items: center;
& > span {
margin-right: 8px;
}
}
.extraCol {
display: flex;
align-items: center;
}
}

View File

@ -0,0 +1,83 @@
/*
* Author: 黎永顺
* name: 报表查看-左侧tab标题
* Description:
* Date: 2023/4/20
*/
import React, { Component } from "react";
import { WeaInputSearch, WeaLocaleProvider } from "ecCom";
import { reportStatisticsReportList } from "../../../apis/statistics";
import { Menu } from "antd";
import { getQueryString } from "../../../util/url";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
class LeftTab extends Component {
constructor(props) {
super(props);
this.state = {
reportName: "",
selectedKeys: getQueryString("id").split(","),
reportList: []
};
}
componentDidMount() {
this.reportStatisticsReportList();
}
reportStatisticsReportList = () => {
const { reportName, selectedKeys } = this.state;
const { onChangeTab } = this.props;
reportStatisticsReportList({ reportName }).then(({ status, data: reportList }) => {
if (status) this.setState({ reportList }, () => {
onChangeTab(_.find(this.state.reportList, it => it.id === selectedKeys[0]));
});
});
};
updateReportList = (report) => {
const { reportList } = this.state;
this.setState({
reportList: _.reduce(reportList, (pre, cur) => {
if (report.id === cur.id) {
return [...pre, report];
}
return [...pre, cur];
}, [])
});
};
render() {
const { reportName, selectedKeys, reportList } = this.state;
const { onChangeTab } = this.props;
return (
<div className="leftTabWrapper">
<div className="searchArea">
<WeaInputSearch
value={reportName} onChange={reportName => this.setState({ reportName })}
placeholder={getLabel(111, "请输入报表名称")} onSearch={this.reportStatisticsReportList}
/>
</div>
<Menu
mode="inline" selectedKeys={selectedKeys}
onClick={({ key }) => {
this.setState({ selectedKeys: key.split(",") }, () => {
onChangeTab(_.find(reportList, it => it.id === key));
});
}}
>
{
_.map(reportList, item => {
const { reportName, id } = item;
return <Menu.Item key={id}>{reportName}</Menu.Item>;
})
}
</Menu>
</div>
);
}
}
export default LeftTab;

View File

@ -0,0 +1,256 @@
/*
* Author: 黎永顺
* name: 报表内容区
* Description:
* Date: 2023/4/21
*/
import React, { Component } from "react";
import { Spin } from "antd";
import { WeaEchart } from "ecCom";
import RightOptions from "./rightOptions";
import ChartsRangeSettingsModal from "./chartsRangeSettingsModal";
import { mapBarOptions, mapLineOptions, mapPieOptions } from "./condition";
import { queryRangeSetting, reportStatisticsReportGetData } from "../../../apis/statistics";
import "../index.less";
class ReportContent extends Component {
constructor(props) {
super(props);
this.state = {
columns: [],
dataSource: [],
countResult: {},
loading: false,
viewType: "dataView",
chartsType: "0",
chartsInfo: {},
rangSet: {
visible: false, reportId: "",
rangeVal: {}
}
};
}
componentDidMount() {
window.addEventListener("message", this.handleReceive, false);
if (this.refs.chart) this.refs.chart.paint();
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.report !== this.props.report && nextProps.report.dimensionId) {
this.reportStatisticsReportGetData(nextProps.report.id, nextProps.report.dimensionId);
this.setState({ viewType: "dataView", chartsInfo: {} });
}
}
componentWillUnmount() {
window.removeEventListener("message", this.handleReceive, false);
}
handleReceive = ({ data }) => {
const { type } = data;
if (type === "init") {
const { columns, countResult, dataSource } = this.state;
this.postMessageToChild({
columns, countResult, dataSource,
showSum: !_.isEmpty(countResult)
});
} else if (type === "turn") {
}
};
postMessageToChild = (payload) => {
const childFrameObj = document.getElementById("atdTable");
const { dataSource, columns, showSum, countResult } = payload;
childFrameObj && childFrameObj.contentWindow.postMessage(JSON.stringify({
dataSource, columns, showSum, countResult
}), "*");
};
reportStatisticsReportGetData = (id, dimensionId) => {
const payload = { id, dimensionId };
this.setState({ loading: true });
reportStatisticsReportGetData(payload).then(({ status, data }) => {
this.setState({ loading: false });
if (status) {
const { countResult, columns, pageInfo: { list } } = data;
this.setState({
countResult,
columns: _.map(columns, it => ({
...it,
dataIndex: it.column, width: 150,
title: it.text, align: "center",
children: !_.isNil(it.children) ? _.map(it.children, child => ({
...child,
dataIndex: child.column, width: 150,
title: child.text, align: "center"
})) : []
})),
dataSource: list || []
}, () => {
this.postMessageToChild({
columns: this.state.columns, countResult: this.state.countResult,
dataSource: this.state.dataSource,
showSum: !_.isEmpty(this.state.countResult)
});
});
}
}).catch(() => this.setState({ loading: false }));
};
queryRangeSetting = (payload, isTab) => {
const { dataSource, columns } = this.state;
this.setState({ loading: true });
queryRangeSetting(payload).then(({ status, data }) => {
this.setState({ loading: false });
if (status) {
const { rangSet, viewType } = this.state;
const { itemValues, itemColValue, id } = data;
this.setState({
rangSet: {
...rangSet,
rangeVal: data
}
});
if (id) {
if (!isTab) return;
switch (viewType) {
case "bar":
this.setState({
chartsInfo: {
name: _.find(_.reduce(_.filter(columns, col => !_.isNil(col.children)), (pre, cur) => ([...pre, ...cur.children]), []), it => it.column === itemColValue).text,
xAxis: _.reduce(itemValues, (pre, cur) => ([...pre, _.find(columns, item => item.column === cur).text]), []),
data: _.map(dataSource, item => {
return {
name: item.dimension,
data: _.map(itemValues, child => {
const key = child + itemColValue.slice(_.indexOf(itemColValue, "_"));
return item[key] || "0";
})
};
})
}
});
break;
case "line":
this.setState({
chartsInfo: {
xAxis: _.map(dataSource, it => it.dimension),
data: _.map(itemValues, item => {
return {
name: `${_.find(_.reduce(_.filter(columns, col => !_.isNil(col.children)), (pre, cur) => ([...pre, ...cur.children]), []), it => it.column === itemColValue).text}(${_.find(columns, it => it.column === item).text})`,
data: _.map(dataSource, child => {
const key = item + itemColValue.slice(_.indexOf(itemColValue, "_"));
return child[key] || "0";
})
};
})
}
});
break;
case "pie":
this.setState({
chartsInfo: {
data: _.map(itemValues, item => {
return {
name: _.find(_.reduce(_.filter(columns, col => !_.isNil(col.children)), (pre, cur) => ([...pre, ...cur.children]), []), it => it.column === itemColValue).text,
data: _.map(dataSource, child => {
return {
name: child["dimension"],
value: child[itemColValue]
};
})
};
})
}
});
break;
default:
break;
}
if (this.refs.chart) this.refs.chart.paint();
} else {
this.setState({ chartsInfo: {} });
}
}
}).catch(() => this.setState({ loading: false }));
};
handleChangeChartOpts = (chartsType, viewType) => {
this.setState({ chartsInfo: {} });
if (this.refs.chart && viewType !== "setting" && viewType !== "dataView" && !this.state.rangSet.visible) this.refs.chart.clear();
const { report: { id: reportId, dimensionId } } = this.props;
const { rangSet } = this.state;
viewType !== "setting" && this.setState({ chartsType, viewType }, () => {
const { viewType, chartsType } = this.state;
viewType !== "dataView" ?
this.queryRangeSetting({ reportId, chartsType }, true) :
this.reportStatisticsReportGetData(reportId, dimensionId);
});
viewType === "setting" && this.setState({ rangSet: { ...rangSet, visible: true, reportId } }, () => {
this.queryRangeSetting({ reportId, chartsType });
});
};
handleCancel = () => {
this.setState({
rangSet: {
visible: false, reportId: "",
rangeVal: {}
}
});
};
handleGetData = () => {
const { report } = this.props;
const { id, dimensionId } = report;
this.setState({ viewType: "dataView" }, () => this.reportStatisticsReportGetData(id, dimensionId));
};
renderCharts = () => {
const { chartsInfo, viewType } = this.state;
return _.isEmpty(chartsInfo) ?
<ChartEmptyComp/> : (viewType === "bar" || viewType === "line" || viewType === "pie") ?
<WeaEchart
ref="chart" useDefault={false} className="chart"
option={viewType === "bar" ? mapBarOptions(chartsInfo) : viewType === "line" ? mapLineOptions(chartsInfo) : mapPieOptions(chartsInfo)}
/> :
<div>123</div>;
};
render() {
const { loading, viewType, rangSet, columns } = this.state;
return (
<div className="layoutContent">
<div className="layoutBox">
<Spin spinning={loading}>
{
viewType === "dataView" ?
<iframe
style={{ border: 0, width: "100%", height: "100%" }}
// src="http://localhost:7607/#/reportTable"
src="/spa/hrmSalary/hrmSalaryCalculateDetail/index.html#/reportTable"
id="atdTable"
/> : this.renderCharts()
}
</Spin>
</div>
{/*侧边栏*/}
<RightOptions onChange={this.handleChangeChartOpts}/>
{/* 图表范围数据设置框 */}
<ChartsRangeSettingsModal
{...rangSet}
columns={columns}
onCancel={this.handleCancel}
onChange={this.queryRangeSetting}
onGetData={this.handleGetData}
/>
</div>
);
}
}
export default ReportContent;
const ChartEmptyComp = () => {
return <div className="chartsEmptyWrapper">
<span>
<img src={require("../../../common/Icon-empty-file.svg")} alt=""/>
</span>
<span>暂无数据</span>
<span>您还没有设置报表统计项请先设置一下哦~</span>
</div>;
};

View File

@ -0,0 +1,52 @@
/*
* Author: 黎永顺
* name: 内容操作按钮区
* Description:
* Date: 2023/4/21
*/
import React, { Component } from "react";
import { WeaLocaleProvider } from "ecCom";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
class RightOptions extends Component {
constructor(props) {
super(props);
this.state = {
show: true
};
}
render() {
const { onChange } = this.props;
const { show } = this.state;
return (
<div className="layoutSide" style={{ width: show ? "30px" : 0 }}>
<div className="sideBox">
<div className="sideMain">
<div className="toll-bar-container" style={{ alignItems: show && "center" }}>
<i className="icon-coms-background" title={getLabel(111, "数据视图")}
onClick={() => onChange("", "dataView")}/>
<i className="icon-portal-reportform-o" title={getLabel(111, "柱状图")}
onClick={() => onChange("1", "bar")}/>
<i className="icon-coms-Line" title={getLabel(111, "折线图")} onClick={() => onChange("2", "line")}/>
<i className="icon-coms-Pie" title={getLabel(111, "饼图")} onClick={() => onChange("0", "pie")}/>
<i className="icon-coms-Flow-setting" title={getLabel(111, "设置")}
onClick={() => onChange("1", "setting")}/>
</div>
</div>
<span className="show-btn-right"
onClick={() => this.setState({ show: !show })}>
{
show ? <img src={require("../../../common/leftTree-show.png")} alt=""/> :
<img src={require("../../../common/leftTree-hide.png")} alt=""/>
}
</span>
</div>
</div>
);
}
}
export default RightOptions;

View File

@ -0,0 +1,350 @@
/*
* Author: 黎永顺
* name: 统计数据范围及规则设置
* Description:
* Date: 2023/4/21
*/
import React, { Component } from "react";
import { toJS } from "mobx";
import {
WeaButtonIcon,
WeaDatePicker,
WeaError,
WeaFormItem,
WeaHelpfulTip,
WeaLocaleProvider,
WeaSearchGroup,
WeaSelect,
WeaSlideModal,
WeaTable
} from "ecCom";
import CustomStatisticsItemsModal from "./customStatisticsItemsModal";
import moment from "moment";
import { Button, message, Modal } from "antd";
import { condition } from "./condition";
import { getSearchs } from "../../../util";
import {
deleteRangeSetting,
reportStatisticsGetSearchCondition,
reportStatisticsItemDelete,
reportStatisticsSaveSearchCondition,
statisticsItemList
} from "../../../apis/statistics";
import { commonEnumList } from "../../../apis/ruleconfig";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
class StatisticalMicroSettingsSlide extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
selectedRowKeys: [],
conditions: [],
dataSource: [],
unitTypeList: [],
salaryMonth: [],
statisticalItemPayload: {
visible: false, id: "", dimension: "",
statisticsItemId: ""
}
};
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.taxAgentAdminOption !== this.props.taxAgentAdminOption && !_.isEmpty(nextProps.taxAgentAdminOption)) {
const conditions = _.map(condition, item => {
return {
...item,
items: _.map(item.items, child => {
if (child.domkey[0] === "taxAgent") {
return {
...child,
options: toJS(nextProps.taxAgentAdminOption)
};
}
return { ...child };
})
};
});
this.setState({ conditions });
nextProps.form.initFormFields(condition);
}
if (nextProps.id !== this.props.id && !_.isEmpty(nextProps.id)) {
this.reportStatisticsGetSearchCondition(nextProps.id);
this.statisticsItemList(nextProps.id).then(r => {
});
}
if (nextProps.visible !== this.props.visible && !nextProps.visible) {
nextProps.form.resetForm();
this.setState({ selectedRowKeys: [] });
}
}
reportStatisticsGetSearchCondition = (id) => {
const { conditions } = this.state;
const { form } = this.props;
reportStatisticsGetSearchCondition({ id }).then(({ status, data }) => {
if (status && !_.isEmpty(data)) {
const { salaryEndMonth, salaryStartMonth, ...formData } = data.data;
this.setState({
salaryMonth: [salaryStartMonth || moment().startOf("year").format("YYYY-MM"), salaryEndMonth || moment().format("YYYY-MM")]
});
const fields = _.map(conditions[0].items, it => it.domkey[0]);
fields.map(item => {
const value = item.indexOf("hiredate") !== -1 ? {
value: formData["hiredate"] || []
} : {
value: !_.isNil(formData[item]) ? _.map(formData[item], val => val.id).join(",") : "",
valueSpan: !_.isNil(formData[item]) ? _.map(formData[item], val => val.name).join(",") : "",
valueObj: !_.isNil(formData[item]) ? formData[item] : []
};
const key = item.indexOf("hiredate") !== -1 ? "hiredate1__hiredate2" : item;
form.updateFields({
[key]: value
});
});
}
});
};
reportStatisticsSaveSearchCondition = () => {
const { salaryMonth, dataSource } = this.state;
const { form, id, dimension, onClose } = this.props;
const [salaryStartMonth, salaryEndMonth] = salaryMonth;
const { department, employee, position, subCompany, taxAgent, ...extra } = form.getFormDatas();
const { value, valueSpan } = taxAgent;
if (!salaryEndMonth || !salaryStartMonth) {
this.refs.weaError.showError();
return;
}
const payload = {
dimension, id,
hiredate: extra["hiredate1__hiredate2"].value || [],
department: _.map(department.valueObj, it => ({ id: it.id, name: it.name })),
employee: _.map(employee.valueObj, it => ({ id: it.id, name: it.name })),
// position: _.map(position.valueObj, it => ({ id: it.id, name: it.name })),
subCompany: _.map(subCompany.valueObj, it => ({ id: it.id, name: it.name })),
taxAgent: value ? _.map(value.split(","), (it, idx) => ({ id: it, name: valueSpan.split(",")[idx] })) : [],
items: dataSource,
salaryEndMonth: salaryEndMonth + "-01",
salaryStartMonth: salaryStartMonth + "-01"
};
this.setState({ loading: true });
reportStatisticsSaveSearchCondition(payload).then(({ status, errormsg }) => {
this.setState({ loading: false });
if (status) {
onClose(true);
message.success(getLabel(111, "保存成功"));
} else {
message.error(errormsg);
}
}).catch(() => this.setState({ loading: false }));
};
reportStatisticsItemDelete = () => {
Modal.confirm({
title: getLabel(111, "信息确认"),
content: getLabel(111, "确认要删除吗?"),
onOk: () => {
const { selectedRowKeys } = this.state;
reportStatisticsItemDelete(selectedRowKeys).then(({ status, errormsg }) => {
if (status) {
message.success(getLabel(111, "删除成功"));
this.setState({
selectedRowKeys: []
}, () => {
this.statisticsItemList(this.props.id).then(r => {
deleteRangeSetting({ reportId: this.props.id });
});
});
} else {
message.error(errormsg || getLabel(111, "删除失败"));
}
});
}
});
};
statisticsItemList = async (statisticsReportId = "") => {
const { data: unitTypeList } = await this.commonEnumList();
statisticsItemList({ statisticsReportId }).then(({ status, data }) => {
if (status) {
this.setState({
dataSource: data,
unitTypeList: _.map(unitTypeList, it => ({ key: it.value.toString(), showname: it.defaultLabel }))
});
}
});
};
commonEnumList = () => {
const payload = {
enumClass: "com.engine.salary.report.enums.UnitTypeEnum"
};
return commonEnumList(payload);
};
renderGroupTitle = () => {
return <div className="groupTitleWrapper">
<span>{getLabel(111, "统计数据范围")}</span>
<span>{getLabel(111, "统计满足以下所有条件的人员薪资核算数据,不选则默认为选择全部")}</span>
</div>;
};
renderProjectTitle = () => {
const { selectedRowKeys } = this.state;
const { id, dimension } = this.props;
return <div className="groupPorjectTitleWrapper">
<div>
<span>{getLabel(111, "统计项目")}</span>
<WeaHelpfulTip width={200} placement="topLeft"
title={getLabel(111, "统计列表的统计项排序与本列表统计项的顺序一致")}
/>
</div>
<div>
<WeaButtonIcon
buttonType="del" type="primary" disabled={_.isEmpty(selectedRowKeys)}
onClick={this.reportStatisticsItemDelete}
/>
<WeaButtonIcon
buttonType="add" type="primary"
onClick={() => this.setState({
statisticalItemPayload: {
visible: true, id, dimension,
statisticsItemId: ""
}
})}
/>
</div>
</div>;
};
render() {
const {
salaryMonth, conditions, selectedRowKeys, loading,
statisticalItemPayload, dataSource, unitTypeList
} = this.state;
const { id, dimension } = this.props;
const columns = [
{
title: "统计项名称",
dataIndex: "itemName",
render: (txt, record) => {
return (
<a href="javascript: void(0);" onClick={() => this.setState({
statisticalItemPayload: { visible: true, id, dimension, statisticsItemId: record.id }
})}>{txt}</a>
);
}
},
{
title: "统计单位",
dataIndex: "unitType",
render: (txt, record) => {
return <WeaSelect
value={!_.isNil(txt) ? txt.toString() : ""} options={unitTypeList} style={{ width: 150 }}
onChange={unitType => this.customStatisticsItemsRef.reportStatisticsItemSave({ id: record.id, unitType })}
/>;
}
}
];
const rowSelection = {
selectedRowKeys,
onChange: (selectedRowKeys) => {
this.setState({ selectedRowKeys });
}
};
return (
<WeaSlideModal
className="microSlideWrapper"
{...this.props}
onClose={() => this.props.onClose()}
top={0}
measureT="%"
width={800}
measureX="px"
height={100}
measureY="%"
direction={"right"}
title={<TitleDialog loading={loading} onSave={this.reportStatisticsSaveSearchCondition}/>}
content={
<React.Fragment>
<WeaSearchGroup title={getLabel(111, "统计时间范围")} col={2} showGroup needTigger>
<WeaFormItem label={getLabel(111, "薪资所属月")} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}>
<WeaError tipPosition="bottom" ref="weaError" error={getLabel(111, "此项必填")}>
<MonthRangePicker viewAttr={3} dateRange={salaryMonth}
onChange={v => this.setState({ salaryMonth: v })}/>
</WeaError>
</WeaFormItem>
</WeaSearchGroup>
{
getSearchs(this.props.form, conditions, 2, false, () => {
}, this.renderGroupTitle())
}
<WeaSearchGroup title={this.renderProjectTitle()} showGroup needTigger>
<WeaTable
rowKey="id"
columns={columns}
dataSource={dataSource}
draggable={true}
onDrop={datas => this.setState({
dataSource: _.map(datas, (it, idx) => ({ ...it, indexValue: idx.toString() }))
})}
pagination={false}
rowSelection={rowSelection}
/>
<CustomStatisticsItemsModal
ref={dom => this.customStatisticsItemsRef = dom}
{...statisticalItemPayload}
onCancel={(isRefresh) => this.setState({
statisticalItemPayload: {
visible: false,
id: "",
dimension: "",
statisticsItemId: ""
}
}, () => isRefresh && this.statisticsItemList(this.props.id))}
/>
</WeaSearchGroup>
</React.Fragment>
}
/>
);
}
}
export default StatisticalMicroSettingsSlide;
const TitleDialog = (props) => {
return <div className="titleDialog">
<div className="titleCol">
<div className="titleLeftBox">
<div className="titleIcon"><i className="icon-coms-fa"/></div>
<div className="title">{getLabel(111, "统计数据范围及规则设置")}</div>
</div>
</div>
<div className="titleCol titleRightBox">
<Button type="primary" loading={props.loading} onClick={props.onSave}>{getLabel(111, "保存")}</Button>
</div>
</div>;
};
const MonthRangePicker = (props) => {
const { dateRange, onChange, viewAttr } = props;
const [startDate, endDate] = dateRange || [];
return <div className="rangePickerBox">
<WeaDatePicker
value={startDate}
disabledDate={(current) => {
return current && endDate && current.getTime() > new Date(endDate).getTime();
}}
format="YYYY-MM"
onChange={(val) => onChange([val, endDate])}
viewAttr={viewAttr}
/>
<span className="to" style={{ margin: "0 10px" }}></span>
<WeaDatePicker
value={endDate}
disabledDate={(current) => {
return current && startDate && current.getTime() < new Date(startDate).getTime();
}}
format="YYYY-MM"
viewAttr={viewAttr}
onChange={(val) => onChange([startDate, val])}
/>
</div>;
};

View File

@ -0,0 +1,165 @@
/*
* Author: 黎永顺
* name: 薪酬报表查看
* Description:
* Date: 2023/4/20
*/
import React, { Component } from "react";
import { inject, observer } from "mobx-react";
import { WeaLeftRightLayout, WeaLocaleProvider, WeaSelect, WeaTop } from "ecCom";
import { message, Modal } from "antd";
import LeftTab from "./components/leftTab";
import ReportContent from "./components/reportContent";
import StatisticalMicroSettingsSlide from "./components/statisticalMicroSettingsSlide";
import { reportGetForm, reportStatisticsReportSave } from "../../apis/ruleconfig";
import "./index.less";
const { getLabel } = WeaLocaleProvider;
@inject("taxAgentStore", "attendanceStore")
@observer
class Index extends Component {
constructor(props) {
super(props);
this.state = {
report: {},
dimensionList: [],
statisticalPayload: {
visible: false, id: "", dimension: ""
}
};
}
componentDidMount() {
const { taxAgentStore: { getTaxAgentSelectListAsAdmin } } = this.props;
this.reportGetForm();
getTaxAgentSelectListAsAdmin();
}
reportGetForm = () => {
reportGetForm().then(({ status, data }) => {
if (status) {
const { statsDimOptions } = data;
this.setState({
dimensionList: _.map(statsDimOptions, it => ({ key: it.id, showname: it.content }))
});
}
});
};
/*
* Author: 黎永顺
* Description: 统计维度切换
* Params:
* Date: 2023/4/20
*/
handleChangeDimension = (dimensionId, dimension) => {
const { report } = this.state;
Modal.confirm({
title: getLabel(111, "信息确认"),
content: getLabel(111, "确认要更改统计维度吗?"),
onOk: () => {
const payload = {
id: report.id,
reportName: report.reportName,
dimensionIds: dimensionId.split(",")
};
reportStatisticsReportSave(payload).then(({ status, errormsg }) => {
this.setState({ loading: false });
if (status) {
message.success(getLabel(111, "切换成功"));
this.setState({
report: {
...report, dimensionId,
dimension
}
}, () => this.leftTabRef.updateReportList(this.state.report));
} else {
message.error(errormsg || getLabel(111, "切换失败"));
this.setState({ report: { ...report } });
}
});
},
onCancel: () => {
this.setState({ report: { ...report } });
}
});
};
/*
* Author: 黎永顺
* Description: 导出报表数据
* Params:
* Date: 2023/4/24
*/
exportData = () => {
const { report } = this.state;
const { id, dimensionId } = report;
window.open(`${window.location.origin}/api/bs/hrmsalary/report/statistics/report/exportData?id=${id}&dimensionId=${dimensionId}`, "_self");
};
render() {
const { report, dimensionList, statisticalPayload } = this.state;
const { attendanceStore: { settingForm }, taxAgentStore: { taxAgentAdminOption } } = this.props;
return (
<WeaTop
title={getLabel(111, "报表查看")}
icon={<i className="icon-coms-fa"/>}
iconBgcolor="#F14A2D"
showDropIcon={false}
className="reportViewWrapper"
>
<WeaLeftRightLayout
leftWidth={210}
leftCom={
<LeftTab
ref={dom => this.leftTabRef = dom}
onChangeTab={report =>
this.setState({
report,
statisticalPayload: { visible: false, id: "", dimension: "" }
})}
/>
}
>
<div className="rightLayout">
<div className="layoutHeader">
<div className="layoutRow">
<div className="layoutCol"><span className="leftColTitle">{report.reportName}</span></div>
<div className="layoutCol layoutColRight">
<div className="rightColTitle">
<div className="dimension">
<span>{getLabel(111, "统计维度")}</span>
<WeaSelect value={report.dimensionId} options={dimensionList}
onChange={(key, showname) => this.handleChangeDimension(key, showname)}/>
</div>
<div className="iconWrapper">
<i
className="icon-coms02-currency"
title={getLabel(111, "统计数据范围及规则设置")}
onClick={() => this.setState({
statisticalPayload: { visible: true, id: report.id, dimension: report.dimensionId }
})}
/>
<i className="icon-coms-export" onClick={this.exportData} title={getLabel(111, "导出")}/>
</div>
{/*统计数据范围及规则设置弹框*/}
<StatisticalMicroSettingsSlide
{...statisticalPayload} form={settingForm}
taxAgentAdminOption={taxAgentAdminOption}
onClose={(isRefresh) => this.setState({
statisticalPayload: { visible: false, id: "", dimension: "" }
}, () => isRefresh && this.reportRef.reportStatisticsReportGetData(report.id, report.dimensionId))}
/>
</div>
</div>
</div>
</div>
{/* 内容区 */}
<ReportContent ref={dom => this.reportRef = dom} report={report}/>
</div>
</WeaLeftRightLayout>
</WeaTop>
);
}
}
export default Index;

View File

@ -0,0 +1,340 @@
.reportViewWrapper {
height: 100%;
display: flex;
flex-direction: column;
.wea-new-top-content {
flex: 1;
.leftTabWrapper {
background: #FFF;
height: 100%;
overflow-y: auto;
.searchArea {
width: 100%;
height: 49px;
line-height: 49px;
padding: 0 6px;
.wea-input-focus {
width: 100%;
}
}
.ant-menu {
border-right-color: transparent !important;
.ant-menu-item, .ant-menu-item {
border-left: 4px solid transparent;
border-right: none;
}
.ant-menu-item-selected, .ant-menu-selected {
border-left: 4px solid #2db7f5;
background: #f5f5f5;
border-right: none;
}
.ant-menu-item-active, .ant-menu-item:hover, .ant-menu-submenu-active, .ant-menu-submenu-title:hover {
background: #f5f5f5;
}
}
}
.rightLayout {
display: flex;
flex-direction: column;
height: 100%;
.layoutHeader {
height: 50px;
.layoutRow {
height: 100%;
display: flex;
align-content: center;
padding: 8px 16px;
background-color: #f9f9f9;
border-bottom: 1px solid #e5e5e5;
.layoutCol {
flex: 1;
height: 100%;
display: flex;
align-items: center;
.rightColTitle {
height: 100%;
display: flex;
align-items: center;
white-space: nowrap;
i.icon-coms02-currency, i.icon-coms-export {
font-size: 16px;
color: #2db7f5;
cursor: pointer;
}
i.icon-coms02-currency {
margin: 0 10px;
}
.dimension {
display: flex;
align-items: center;
.wea-select {
width: 150px;
margin-left: 10px;
}
}
.iconWrapper {
display: flex;
align-items: center;
}
}
}
.layoutColRight {
justify-content: flex-end;
}
}
}
.layoutContent {
flex: 1;
display: flex;
justify-content: space-between;
width: 100%;
height: 100%;
.layoutBox {
flex: 1;
padding: 16px;
.ant-spin-nested-loading, .ant-spin-container {
height: 100%;
.chart {
width: 100%;
height: 100%;
}
}
.chartsEmptyWrapper {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
& > span:first-child {
display: inline-block;
text-align: center;
img {
width: 100px;
height: 100px;
}
}
& > span:nth-child(2) {
font-size: 16px;
color: #666;
font-weight: 400;
margin-bottom: 8px;
}
& > span:nth-child(3) {
width: 100%;
font-size: 12px;
color: #999;
text-align: center;
}
}
}
.layoutSide {
position: relative;
transition: width .3s cubic-bezier(.645, .045, .355, 1), height .3s cubic-bezier(.645, .045, .355, 1);
min-height: 1px;
.sideBox {
position: relative;
height: 100%;
width: 100%;
.sideMain {
width: 100%;
.toll-bar-container {
margin-top: 16px;
background-color: #fff;
border: 1px solid #e5e5e5;
height: 180px;
display: flex;
flex-direction: column;
justify-content: space-around;
i {
font-size: 16px;
color: #2db7f5 !important;
cursor: pointer;
}
}
}
.show-btn-right {
position: absolute;
cursor: pointer;
z-index: 100;
top: 60px;
left: -17px;
transform: rotate(180deg);
}
}
}
}
}
}
}
//统计数据范围及规则设置
.microSlideWrapper {
z-index: 999;
.wea-slide-modal-title {
height: auto !important;
line-height: normal !important;
}
.titleDialog {
display: flex;
padding: 10px 16px 10px 0;
position: relative;
background-color: #f6f6f6;
border-bottom: 1px solid #e5e5e5;
.titleCol {
flex: 1;
.titleLeftBox {
display: flex;
height: 100%;
align-items: center;
padding-left: 16px;
.titleIcon {
color: #fff;
margin: 0;
width: 40px;
height: 40px;
line-height: 40px;
font-size: 22px;
display: flex;
align-items: center;
justify-content: center;
background: #F14A2D;
border-radius: 50%;
}
.title {
font-size: 14px;
color: #333;
padding-left: 6px;
}
}
}
.titleRightBox {
display: flex;
justify-content: flex-end;
align-items: center;
padding-right: 30px;
}
}
.groupTitleWrapper {
display: flex;
justify-content: space-between;
& > span:last-child {
color: #666
}
}
.groupPorjectTitleWrapper {
display: flex;
justify-content: space-between;
align-items: center;
& > div:first-child {
display: flex;
align-items: center;
& > span:first-child {
padding-right: 8px;
}
}
& > div:last-child {
display: flex;
align-items: center;
.wea-button-icon:first-child {
margin-right: 10px;
}
}
}
.rangePickerBox {
display: flex;
align-items: center;
}
}
//新建自定义统计项目
.statisticItemsWrapper {
.itemsTitle {
display: flex;
align-items: center;
justify-content: space-between;
}
.statisticItemsBox {
padding: 16px;
.wea-error {
width: 100%;
}
.wea-form-item {
padding: 5px 16px;
border: 1px solid #e5e5e5;
}
.wea-form-item:first-child {
border-bottom: none;
}
}
.customRuleTableWrapper {
margin-top: 16px;
.intervalSettingsCompWrapper {
display: flex;
align-items: center;
span.increaseTitle {
padding: 0 8px;
display: inline-block;
width: 230px;
}
span.pecentTitle {
padding-left: 8px;
}
}
}
}

View File

@ -1,9 +1,10 @@
import React from "react";
import { Button, Col, Icon, message, Modal, Row } from "antd";
import { WeaCheckbox, WeaDialog, WeaFormItem, WeaHelpfulTip, WeaInput, WeaSelect, WeaTextarea } from "ecCom";
import { Button, Col, message, Modal, Row } from "antd";
import { WeaCheckbox, WeaDialog, WeaFormItem, WeaHelpfulTip, WeaInput, WeaSelect } from "ecCom";
import { inject, observer } from "mobx-react";
import { testFormual } from "../../apis/item";
import TestModal from "./testModal";
import ExcelEditor from "../../components/excelEditor";
import "./index.less";
@inject("salaryItemStore")
@ -12,11 +13,11 @@ export default class FormalFormModal extends React.Component {
constructor(props) {
super(props);
this.state = {
formalua: false, //是否删除操作过公式数据
validateType: "",
returnType: "",
value: "",
extendParam: {
isCustomFunction: "0",
sqlReturnKey: "",
openDecrypt: "0",
datasource: {
@ -26,7 +27,8 @@ export default class FormalFormModal extends React.Component {
returnValue: "",
formulaDatasourceList: [],
testVisible: false,
showTestVal: ""
showTestVal: "",
groupParams: {}
};
this.group = {};
this.field = {};
@ -41,11 +43,6 @@ export default class FormalFormModal extends React.Component {
setSearchFields([]);
if (!!this.props.formulaId && this.props.formulaId != 0) {
detailFormual(this.props.formulaId).then(data => {
this.setState({
value: data.formula,
returnType: data.returnType,
validateType: data.validateType
});
this.parameters = data.parameters;
this.referenceType = data.referenceType;
this.extendParam = data.extendParam;
@ -58,6 +55,7 @@ export default class FormalFormModal extends React.Component {
}
this.setState({
extendParam: {
isCustomFunction: extendParam.isCustomFunction || "1",
sqlReturnKey: extendParam.sqlReturnKey,
openDecrypt: extendParam.openDecrypt,
datasource: {
@ -66,14 +64,20 @@ export default class FormalFormModal extends React.Component {
}
});
}
let groupParams = {};
if (this.referenceType == "sql") {
groupParams = { "referenceType": "sql" };
} else {
groupParams = this.props.backCalcType === "issuedItems" ? { "referenceType": "backCalc" } : {};
}
salaryAcctImportTemplateParam(groupParams);
this.setState({
value: data.formula,
returnType: data.returnType,
validateType: data.validateType
});
// salaryAcctImportTemplateParam(groupParams);
});
let groupParams = {};
if (this.props.valueType == "3") {
groupParams = { "referenceType": "sql" };
} else {
groupParams = this.props.backCalcType === "issuedItems" ? { "referenceType": "backCalc" } : {};
}
this.setState({ groupParams });
} else {
let groupParams = {};
if (this.props.valueType == "3") {
@ -85,7 +89,8 @@ export default class FormalFormModal extends React.Component {
value: this.props.formulaContent
});
}
salaryAcctImportTemplateParam(groupParams);
this.setState({ groupParams });
// salaryAcctImportTemplateParam(groupParams);
}
}
@ -108,7 +113,6 @@ export default class FormalFormModal extends React.Component {
const index = value.lastIndexOf("{", end - 1);
const currentValue = value.substring(index, end);
this.setState({
formalua: true,
value: value.replace(currentValue, "")
}, () => {
if (propsTextarea.setSelectionRange) {
@ -138,14 +142,14 @@ export default class FormalFormModal extends React.Component {
};
// 多行文本编辑
handleChange(value) {
handleChange = (value) => {
if (value && value.trim() == "") {
this.parameters = [];
}
this.setState({
value, formalua: true
value
});
}
};
// 获取光标位置
getPositionForTextArea(ctrl) {
@ -206,12 +210,11 @@ export default class FormalFormModal extends React.Component {
returnType: this.props.dataType || this.state.returnType,
validateType: this.props.dataType || this.state.returnType,
extendParam: JSON.stringify(this.state.extendParam),
formula: this.state.value,
formula: this.state.value.replace(/[\r\n]/g, ""),
parameters: this.parameters,
referenceType: this.referenceType == "" ? this.props.valueType == "2" ? "formula" : this.props.valueType == "3" ? "sql" : "" : this.referenceType
};
saveFormual(params).then(data => {
this.setState({ formalua: false });
this.props.onSaveFormal(data);
this.props.onCancel();
});
@ -319,17 +322,26 @@ export default class FormalFormModal extends React.Component {
}
});
};
handleChangeCustomFunction = (isCustomFunction) => {
const { extendParam } = this.state;
this.setState({
extendParam: {
...extendParam,
isCustomFunction
}
});
};
render() {
const { salaryItemStore } = this.props;
const { searchGroup, searchFields } = salaryItemStore;
const { value, formulaDatasourceList, extendParam, testVisible, showTestVal, formalua } = this.state;
const { value, formulaDatasourceList, extendParam, testVisible, showTestVal, groupParams } = this.state;
const title = <div className="formulaTitleWrapper">
<div>{`${(this.props.valueType == 2 || this.props.valueType === "FORMULA") ? "函数" : "SQL"}公式`}</div>
{
value && <Button type="primary" onClick={() => {
const isSaveBool = _.every(this.parameters, it => !!it.id);
if (isSaveBool && !formalua) {
if (isSaveBool) {
this.setState({ testVisible: true });
} else {
message.info("请先保存公式后再进行测试");
@ -346,15 +358,15 @@ export default class FormalFormModal extends React.Component {
return (
<WeaDialog
title={title}
hasScroll
visible={this.props.visible}
style={{ width: 800 }}
style={{ width: 1000 }}
buttons={[
<Button type="primary" onClick={this.handleSave}>保存</Button>
]}
className="formula-wrapper"
initLoadCss
onCancel={() => {
this.setState({ formalua: false });
this.props.onCancel();
}}>
{
@ -427,66 +439,69 @@ export default class FormalFormModal extends React.Component {
</Col>
</Row>
}
<div>
<WeaTextarea
ref={(input) => this.contentProps = input}
minRows={8}
maxRows={8}
value={value}
onChange={(value) => this.handleChange(value)}
noResize={true}
style={{ fontSize: "14px", lineHeight: 1.2 }}
onKeyDown={this.triggerKeyDown}
/>
</div>
<div style={{ display: "flex", height: "300px", marginTop: "10px" }}>
<div style={{
flex: 1,
height: "300px",
overflowY: "scroll",
padding: "10px",
border: "1px solid rgb(217, 217, 217)",
marginRight: "10px"
}}>
<div>
<div style={{ marginBottom: "10px", fontSize: "14px" }}>变量</div>
<div>
{
searchGroup && searchGroup.map(item => {
return <div style={{ height: "25px", lineHeight: "25px", cursor: "pointer", overflow: "hidden" }}
key={item.key} onClick={() => {
this.handleItemClick(item);
}}>
{item.value}
<Icon type="right" style={{ float: "right", marginLeft: "10px", color: "#eee", marginTop: "9px" }}/>
<span style={{ color: "#999", float: "right" }}>{item.value} 的字段</span>
</div>;
})
}
</div>
</div>
</div>
<div style={{
flex: 1,
height: "300px",
overflowY: "scroll",
border: "1px solid rgb(217, 217, 217)",
padding: "10px"
}}>
{
searchFields && searchFields.map(item => {
return (
<div style={{ height: "25px", lineHeight: "25px", cursor: "pointer" }} key={item.fieldId}
onClick={() => {
this.handleFieldClick(item);
}}>
{item.name}
</div>
);
})
}
</div>
</div>
<ExcelEditor value={value} groupParams={groupParams} isCustomFunction={extendParam.isCustomFunction}
onChange={(value) => this.handleChange(value)}
onChangeCustomFunction={this.handleChangeCustomFunction}/>
{/*<div>*/}
{/* <WeaTextarea*/}
{/* ref={(input) => this.contentProps = input}*/}
{/* minRows={8}*/}
{/* maxRows={8}*/}
{/* value={value}*/}
{/* onChange={(value) => this.handleChange(value)}*/}
{/* noResize={true}*/}
{/* style={{ fontSize: "14px", lineHeight: 1.2 }}*/}
{/* onKeyDown={this.triggerKeyDown}*/}
{/* />*/}
{/*</div>*/}
{/*<div style={{ display: "flex", height: "300px", marginTop: "10px" }}>*/}
{/* <div style={{*/}
{/* flex: 1,*/}
{/* height: "300px",*/}
{/* overflowY: "scroll",*/}
{/* padding: "10px",*/}
{/* border: "1px solid rgb(217, 217, 217)",*/}
{/* marginRight: "10px"*/}
{/* }}>*/}
{/* <div>*/}
{/* <div style={{ marginBottom: "10px", fontSize: "14px" }}>变量</div>*/}
{/* <div>*/}
{/* {*/}
{/* searchGroup && searchGroup.map(item => {*/}
{/* return <div style={{ height: "25px", lineHeight: "25px", cursor: "pointer", overflow: "hidden" }}*/}
{/* key={item.key} onClick={() => {*/}
{/* this.handleItemClick(item);*/}
{/* }}>*/}
{/* {item.value}*/}
{/* <Icon type="right" style={{ float: "right", marginLeft: "10px", color: "#eee", marginTop: "9px" }}/>*/}
{/* <span style={{ color: "#999", float: "right" }}>{item.value} 的字段</span>*/}
{/* </div>;*/}
{/* })*/}
{/* }*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div style={{*/}
{/* flex: 1,*/}
{/* height: "300px",*/}
{/* overflowY: "scroll",*/}
{/* border: "1px solid rgb(217, 217, 217)",*/}
{/* padding: "10px"*/}
{/* }}>*/}
{/* {*/}
{/* searchFields && searchFields.map(item => {*/}
{/* return (*/}
{/* <div style={{ height: "25px", lineHeight: "25px", cursor: "pointer" }} key={item.fieldId}*/}
{/* onClick={() => {*/}
{/* this.handleFieldClick(item);*/}
{/* }}>*/}
{/* {item.name}*/}
{/* </div>*/}
{/* );*/}
{/* })*/}
{/* }*/}
{/* </div>*/}
{/*</div>*/}
</WeaDialog>
);

View File

@ -491,7 +491,8 @@ export default class Archives extends React.Component {
break;
case "suspend":
dom = <div>
<div>1数据进入待减员规则①员工的人事状态属性从在职变成非在职且在在缴员工在缴员工里档案维护了缴纳终止月且小于等于当前月③个税扣缴义务人发生调整④某员工分权时在原个税扣缴义务人下处于在缴员工里但又将该员工添加到其他的个税扣缴义务人下的情况</div>
{/*④某员工分权时在原个税扣缴义务人下处于【在缴员工】里但又将该员工添加到其他的个税扣缴义务人下的情况;*/}
<div>1数据进入待减员规则①员工的人事状态属性从在职变成非在职且在在缴员工在缴员工里档案维护了缴纳终止月且小于等于当前月③个税扣缴义务人发生调整</div>
<div>2待减员为是否不再缴纳的待办状态数据是从在缴员工中复制的若不处理列表中的待办数据也不影响社保福利核算待减员维护的数据和在缴员工数据是同步的</div>
<div>3点击减员前先维护最后缴纳月所有有起始缴纳月的福利项的最后缴纳月都小于等于当前月且无未归档的核算数据的档案才能减员成功减员成功后数据进入停缴员工</div>
{/*<div>4、数据进入【待减员】规则的第四种情况下若还需要在当前个税扣缴义务人下进行缴纳的话当前该员工的【待减员】数据进行【删除待办】操作即可若不在该个税扣缴义务人下继续缴纳维护好最后缴纳月后进行【减员】操作员工进入【停缴员工】</div>*/}
@ -500,7 +501,7 @@ export default class Archives extends React.Component {
default:
dom = <div>
<div>1不需要缴纳社保福利的员工保存在停缴员工</div>
<div>2停缴员工点击取消停缴数据会回退到上次的位置待减员减员到停缴员工的点击停缴返回到在缴员工不返回到待减员</div>
{/*<div>2、【停缴员工】点击取消停缴数据会回退到上次的位置从【待减员】减员到停缴员工的点击停缴返回到【在缴员工】不返回到【待减员】</div>*/}
{/*<div>3、若员工的社保福利从一个个税扣缴义务人下转到另一个个税扣缴义务人下去缴纳则在转后的个税扣缴义务人的【待增员】中进行增员操作成功后员工进入待【在缴员工】而在原个税扣缴义务人下的【停缴员工】中该员工数据将会被删除是否开启分权员工的档案数据都只保存一份</div>*/}
</div>;
break;

View File

@ -91,7 +91,7 @@ class RegList extends Component {
const billMonth = getQueryString("billMonth");
const paymentOrganization = getQueryString("paymentOrganization");
const creator = Number(getQueryString("creator"));
const paymentStatus = "3";
const paymentStatus = type === "difference" ? "4" : "3";
const payload = {
billMonth, paymentStatus,
creator, paymentOrganization,

View File

@ -30,8 +30,8 @@ const Home = props => props.children;
class Root extends React.Component {
componentWillMount() {
top.$(".ant-message").remove();
allStore.taxAgentStore.getPermission();
if(window.location.hash.indexOf("payroll")!== -1){
window.location.hash.indexOf("mobilepayroll") === -1 && allStore.taxAgentStore.getPermission();
if (window.location.hash.indexOf("payroll") !== -1) {
window.localStorage.removeItem("template-basedata");
window.localStorage.removeItem("salary-showset");
}

View File

@ -5,4 +5,9 @@ import { WeaForm } from "comsMobx";
export class AttendanceStore {
@observable form = new WeaForm();
@observable refenceform = new WeaForm();
//薪酬统计 新增form
@observable statisticsForm = new WeaForm();
@observable reportForm = new WeaForm();
//报表查看 统计数据范围及规则设置form
@observable settingForm = new WeaForm();
}

View File

@ -3,7 +3,6 @@ import { message } from "antd";
import { WeaForm, WeaTableNew } from "comsMobx";
import * as API from "../apis/calculate";
import { backCalculate } from "../apis/calculate";
const { TableStore } = WeaTableNew;
@ -487,28 +486,26 @@ export class calculateStore {
// 薪资结果-编辑表单保存
@action
saveAcctResult = (recordId) => {
let inputItems = this.acctresultDetailForm.inputItems.map(item => {
const itemsByGroupItems = _.reduce(this.acctresultDetailForm.itemsByGroup, (pre, cur) => {
return [
...pre,
..._.map(cur.salaryItems, it => {
return {
salaryItemId: it.salaryItemId,
resultValue: it.resultValue
};
})
];
}, []);
const issuedAndReissueItems = this.acctresultDetailForm.issuedAndReissueItems.map(item => {
let record = {};
record.salaryItemId = item.salaryItemId;
record.resultValue = item.resultValue;
return record;
});
let formulaItems = this.acctresultDetailForm.formulaItems.map(item => {
let record = {};
record.salaryItemId = item.salaryItemId;
record.resultValue = item.resultValue;
return record;
});
let issuedAndReissueItems = this.acctresultDetailForm.issuedAndReissueItems.map(item => {
let record = {};
record.salaryItemId = item.salaryItemId;
record.resultValue = item.resultValue;
return record;
});
let items = inputItems.concat(formulaItems).concat(issuedAndReissueItems);
let items = itemsByGroupItems.concat(issuedAndReissueItems);
let params = {
salaryAcctEmpId: recordId,
items

View File

@ -4,7 +4,7 @@ import { WeaForm, WeaLogView, WeaTableNew } from "comsMobx";
import { WeaLocaleProvider } from "ecCom";
import moment from "moment";
import * as API from "../apis/mySalaryBenefits"; // 引入API接口文件
import * as API from "../apis/mySalaryBenefits";
const { LogStore } = WeaLogView;
const getLabel = WeaLocaleProvider.getLabel;
@ -37,6 +37,14 @@ export class MySalaryStore {
this.loading = true;
this.hasRight = false;
};
@action clearLoading = () => {
this.loading = false;
this.hasRight = false;
};
@action setInitEmVerify = () => {
this.loading = false;
this.hasRight = true;
};
@action setMySalaryBill = (mySalaryBill) => {
this.mySalaryBill = mySalaryBill;
};

View File

@ -16,6 +16,7 @@ export class TaxAgentStore {
@observable showSearchAd = false; // 高级搜索面板显示
@observable showOperateBtn = false; // 页面操作按钮显示权限
@observable showSalaryItemBtn = false; // 薪资项目管理页面操作按钮显示权限
@observable statisticsReportBtn = false; // 薪酬统计报表页面操作按钮显示权限
@observable loading = false; // 数据加载状态
@observable modalVisiable = false; // EditModal 模态框
@observable columns = [];
@ -37,6 +38,7 @@ export class TaxAgentStore {
@action setShowOperateBtn = bool => (this.showOperateBtn = bool);
@action setSalaryItemBtn = bool => (this.showSalaryItemBtn = bool);//薪资项目权限
@action setStatisticsReportBtn = bool => (this.statisticsReportBtn = bool);//薪酬统计报表权限
// 初始化操作
@action
@ -141,6 +143,8 @@ export class TaxAgentStore {
!isOpenDevolution ? true : isAdminEnable ? true : false
);
this.setSalaryItemBtn((isOpenDevolution && (isChief || isAdminEnable)));
//薪酬统计报表权限
this.setStatisticsReportBtn(!isOpenDevolution ? true : !!(isAdminEnable || isChief));
resolve({ status, data });
} else {
reject();

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), title) => {
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 || 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,25 @@ 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;
}
//金额格式化
export const format_with_regex = (number) => {
return !(number + "").includes(".")
? // 就是说1-3位后面一定要匹配3位
(number + "").replace(/\d{1,3}(?=(\d{3})+$)/g, (match) => {
return match + ",";
})
: (number + "").replace(/\d{1,3}(?=(\d{3})+(\.))/g, (match) => {
return match + ",";
});
};