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
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|