salary-management-front/pc4mobx/hrmSalary/components/excelEditor/index.js

255 lines
8.4 KiB
JavaScript
Raw Normal View History

2023-04-13 10:00:24 +08:00
import React, { Component } from "react";
2023-04-13 16:42:57 +08:00
import ReactDOM from "react-dom";
2023-04-13 10:00:24 +08:00
import { Button } from "antd";
2023-04-13 13:26:43 +08:00
import { Controlled as CodeMirror } from "react-codemirror2";
2023-04-13 10:00:24 +08:00
import { keyboardBaseBtns } from "./constants";
2023-04-26 15:08:03 +08:00
import CodeAction from "./components/codeAction";
2023-04-13 10:00:24 +08:00
import cs from "classnames";
import "./index.less";
import "codemirror/lib/codemirror.css";
import "codemirror/lib/codemirror.js";
//代码折叠
import "codemirror/addon/fold/foldgutter.css";
import "codemirror/addon/lint/lint.css";
import "codemirror/addon/fold/foldcode.js";
import "codemirror/addon/fold/foldgutter.js";
import "codemirror/addon/fold/brace-fold.js";
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/lint/lint.js";
import "codemirror/addon/lint/json-lint.js";
import "codemirror/addon/lint/javascript-lint.js";
import "codemirror/addon/display/placeholder.js";
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/sql/sql.js";
// const sqlFormatter = require('sql-formatter');
class ExcelEditor extends Component {
constructor(props) {
super(props);
this.state = {
2023-04-13 13:26:43 +08:00
value: "",
widgets: []
2023-04-13 10:00:24 +08:00
};
2023-04-13 13:26:43 +08:00
this.editorRef = null;
2023-04-13 10:00:24 +08:00
}
2023-04-26 15:08:03 +08:00
// componentDidMount() {
// window.addEventListener("keydown", this.onkeyDown, { passive: false });
// }
//
// componentWillUnmount() {
// window.removeEventListener("keydown", this.onkeyDown);
// }
//
// onkeyDown = (e) => {
// if (e.keyCode === 8) {
// console.log(e);
// e.nativeEvent.stopImmediatePropagation()
// e.preventDefault();
// // this.handleEditorRedo();
// }
// };
2023-04-13 16:42:57 +08:00
/*
* Author: 黎永顺
* Description: 插入字符
* Params:
* Date: 2023/4/13
*/
insertText = text => {
2023-04-26 15:08:03 +08:00
const cursor = this.editorRef.getCursor();
2023-04-13 16:42:57 +08:00
this.editorRef.replaceRange(text, cursor);
};
2023-04-13 13:26:43 +08:00
replaceToWidget = (editor, data, value, inlineWidgetOpts) => {
editor.getAllMarks().forEach(m => m.clear());
2023-04-26 15:08:03 +08:00
editor.refresh();
editor.focus();
2023-04-13 16:42:57 +08:00
// let posInfos = _.flatMap(_.keys(inlineWidgetOpts), widgetName => {
// let { regex, render } = inlineWidgetOpts[widgetName];
// let res = [], newRe = new RegExp(regex, "g"), m;
// do {
// m = newRe.exec(value);
// if (m) {
// const mountToDom = document.createElement("span");
// let text = m[0];
// res.push({
// widgetName,
// text,
// startAt: m.index,
// endAt: m.index + text.length,
// render: () => {
// let x = `((...args) => args)${text.replace(new RegExp(`^${widgetName}`), "")}`;
// let args = eval(x);
// return render(...args);
// },
// mountToDom: mountToDom
// });
// }
// } while (m);
// return res;
// });
// posInfos.forEach(posInfo => {
// let from = { line: 0, ch: posInfo.startAt };
// let to = { line: 0, ch: posInfo.endAt };
// editor.markText(from, to, {
// replacedWith: posInfo.mountToDom,
// clearWhenEmpty: false
// });
// });
// this.setState({
// widgets: posInfos
// }, () => {
// editor.refresh();
// editor.focus();
// });
2023-04-13 13:26:43 +08:00
};
/*
* Author: 黎永顺
* Description:格式化
* Params:
* Date: 2023/4/13
*/
autoFormatSelection = () => {
let editor = this.editorRef.editor;
if (this.props.type != "sql") {
const script_length = editor.getValue().length;
const startPos = { line: 0, ch: 0, sticky: null };
const endPos = editor.doc.posFromIndex(script_length);
// editor.setSelection(startPos, endPos);
// editor.autoFormatRange(startPos, endPos);
// editor.commentRange(false, startPos, endPos);
} else {
let splCont = "";
splCont = editor.getValue();
// editor.setValue(sqlFormatter.format(splCont));
}
};
2023-04-26 15:08:03 +08:00
handleVariSelect = str => this.insertText(`{${str}}`);
handleFuncSelect = str => {
const cursor = this.editorRef.getCursor();
this.editorRef.replaceRange(`${str}()`, cursor);
2023-04-27 10:01:57 +08:00
console.log(this.editorRef);
console.log(this.editorRef.doc);
this.editorRef.doc.goColumnLeft();
2023-04-26 15:08:03 +08:00
};
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 });
}
};
2023-04-27 09:34:19 +08:00
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 {
2023-04-27 10:01:57 +08:00
this.editorRef.replaceRange("", { line, ch: codeValue.slice(0, ch).lastIndexOf("{") + 1 }, { line, ch });
2023-04-27 09:34:19 +08:00
}
}
};
2023-04-13 10:00:24 +08:00
render() {
2023-04-13 13:26:43 +08:00
const inlineWidgetOpts = {
useObject: {
regex: /useObject\("[^)]+"\)/,
render: (objId) => {
return (
2023-04-13 16:42:57 +08:00
<span className="cm-excel-default">{objId}</span>
2023-04-13 13:26:43 +08:00
);
}
}
};
2023-04-13 16:42:57 +08:00
const { widgets } = this.state;
2023-04-13 10:00:24 +08:00
return (
<React.Fragment>
<div className="excel-codeWrap">
<div className="excel-codeBox">
<CodeMirror
2023-04-13 13:26:43 +08:00
editorDidMount={editor => this.editorRef = editor}
2023-04-13 10:00:24 +08:00
value={this.state.value}
onBeforeChange={(editor, data, value) => {
this.setState({ value });
}}
2023-04-13 13:26:43 +08:00
onChange={(editor, data, value) => {
this.replaceToWidget(editor, data, value, inlineWidgetOpts);
2023-04-13 10:00:24 +08:00
}}
options={{
lineNumbers: false,
mode: { name: this.props.type === "sql" ? "text/x-sql" : "application/json" },
autofocus: false,
styleActiveLine: true,
lineWrapping: true,
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
lint: false,
indentUnit: 2,
cursorHeight: 0.85,
2023-04-27 10:01:57 +08:00
placeholder: "",
showCursorWhenSelecting: true
2023-04-13 10:00:24 +08:00
}}
2023-04-27 09:34:19 +08:00
onKeyDown={(_, { keyCode }) => keyCode === 8 && this.handleBackSpaceRedo()}
2023-04-13 10:00:24 +08:00
/>
2023-04-13 16:42:57 +08:00
{widgets.map((w, i) => {
return (
<Widget key={i} info={w}/>
);
})}
2023-04-13 10:00:24 +08:00
</div>
<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"
2023-04-13 16:42:57 +08:00
className={cs(key === " " ? "excel-codeBox-keyboard-space" : "excel-codeBox-keyboard-base")}
onClick={() => this.insertText(key)}
2023-04-13 10:00:24 +08:00
>{label}</Button>;
})
}
</div>
<div className="excel-codeBox-keyboard-operate-clear">
2023-04-13 16:42:57 +08:00
<Button title="←" size="small" className="excel-codeBox-keyboard-del"
2023-04-27 10:01:57 +08:00
onClick={this.handleEditorRedo}></Button>
2023-04-13 16:42:57 +08:00
<Button title="C" size="small" className="excel-codeBox-keyboard-clear"
onClick={() => this.setState({ value: "", widgets: [] })}>C</Button>
2023-04-13 10:00:24 +08:00
</div>
</div>
</div>
</div>
2023-04-26 15:08:03 +08:00
{/*公式参数列表*/}
<CodeAction onVariSelect={this.handleVariSelect} onFuncSelect={this.handleFuncSelect}/>
2023-04-13 10:00:24 +08:00
</React.Fragment>
);
}
}
export default ExcelEditor;
2023-04-13 16:42:57 +08:00
class Widget extends React.Component {
render() {
let { info } = this.props;
return ReactDOM.createPortal(
info.render(),
info.mountToDom
);
}
}