Merge branch 'master' into feature/V2-薪酬统计分析

This commit is contained in:
黎永顺 2023-05-16 14:09:01 +08:00
commit 27d3762efa
12 changed files with 908 additions and 98 deletions

View File

@ -107,6 +107,10 @@ export const saveSysItem = params => {
export const getItemTypeOption = params => { export const getItemTypeOption = params => {
return WeaTools.callApi('/api/bs/hrmsalary/salaryitem/listSalaryItemTypeOption', 'GET', 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 *** // *** 公式 start ***
// 获取公式变量类型 // 获取公式变量类型

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

@ -142,13 +142,13 @@ class AttendanceDataComp extends Component {
} }
}); });
}; };
handleViewAttendanceData = ({ id, salaryCycle }) => { handleViewAttendanceData = ({ id, attendCycle }) => {
const { attendanceViewPayload } = this.state; const { attendanceViewPayload } = this.state;
this.setState({ this.setState({
attendanceViewPayload: { attendanceViewPayload: {
...attendanceViewPayload, ...attendanceViewPayload,
visible: true, attendQuoteId: id, visible: true, attendQuoteId: id,
salaryYearMonth: salaryCycle salaryYearMonth: attendCycle
} }
}); });
}; };

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

@ -28,10 +28,12 @@ export default class LedgerSalaryItemAddModal extends React.Component {
this.setState({ selectedRowKeys: [], dataSourceCopy: [] }, () => { this.setState({ selectedRowKeys: [], dataSourceCopy: [] }, () => {
this.listSalaryItem(); this.listSalaryItem();
}); });
} else {
this.setState({ pageInfo: { current: 1, pageSize: 10, total: 0 } });
} }
} }
listSalaryItem = () => { listSalaryItem = (extra = {}) => {
const { itemGroups } = this.props; const { itemGroups } = this.props;
const { name, pageInfo, loading, dataSourceCopy } = this.state; const { name, pageInfo, loading, dataSourceCopy } = this.state;
let excludeIds = []; let excludeIds = [];
@ -43,17 +45,19 @@ export default class LedgerSalaryItemAddModal extends React.Component {
const payload = { const payload = {
excludeIds, excludeIds,
name, name,
...pageInfo ...pageInfo,
...extra
}; };
this.setState({ loading: { ...loading, query: true } }); this.setState({ loading: { ...loading, query: true } });
listSalaryItem(payload).then(({ status, data }) => { listSalaryItem(payload).then(({ status, data }) => {
this.setState({ loading: { ...loading, query: false } }); this.setState({ loading: { ...loading, query: false } });
if (status) { if (status) {
const { pageNum: current, pageSize, total, columns, list: dataSource } = data; const { pageNum: current, pageSize, total, columns, list: dataSource } = data;
const tmpV = !_.isEmpty(dataSource) ? dataSource : [];
this.setState({ this.setState({
dataSourceCopy: [...dataSourceCopy, ...dataSource], dataSourceCopy: [...dataSourceCopy, ...tmpV],
pageInfo: { ...pageInfo, current, pageSize, total }, pageInfo: { ...pageInfo, current, pageSize, total },
dataSource, dataSource: tmpV,
columns columns
}); });
} }
@ -86,7 +90,7 @@ export default class LedgerSalaryItemAddModal extends React.Component {
const { onAddSalaryItems, id, onCancel, itemGroups } = this.props; const { onAddSalaryItems, id, onCancel, itemGroups } = this.props;
const arrItems = _.find(itemGroups, it => it.uuid === id).items || []; const arrItems = _.find(itemGroups, it => it.uuid === id).items || [];
let selectItems = []; let selectItems = [];
dataSourceCopy.map((item) => { _.uniqWith(dataSourceCopy, _.isEqual).map((item) => {
item = { ...item }; item = { ...item };
selectedRowKeys.map((key, keyIdx) => { selectedRowKeys.map((key, keyIdx) => {
if (item.id === key) { if (item.id === key) {
@ -135,7 +139,7 @@ export default class LedgerSalaryItemAddModal extends React.Component {
placeholder="请输入薪资项目名称" placeholder="请输入薪资项目名称"
value={name} value={name}
onChange={(name) => this.setState({ name })} onChange={(name) => this.setState({ name })}
onSearch={() => this.listSalaryItem()} onSearch={() => this.listSalaryItem({ current: 1 })}
/> />
</div> </div>
<WeaTable <WeaTable

View File

@ -15,7 +15,7 @@ export default class LedgerSalaryItemPreviewModal extends React.Component {
_.map(_.filter(itemGroups, it => !_.isEmpty(it.items)), child => { _.map(_.filter(itemGroups, it => !_.isEmpty(it.items)), child => {
let columnItem = { let columnItem = {
title: child.name, title: child.name,
children: child.items.map(i => { children: _.filter(child.items, t => t.itemHide !== "1").map(i => {
return { return {
title: i.name, title: i.name,
dataIndex: i.id, dataIndex: i.id,

View File

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

View File

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