├── src
├── img
│ ├── pic1.png
│ └── pic2.png
├── index.js
├── app.js
└── components
│ ├── FormulaEdit
│ ├── otp.js
│ ├── index.less
│ ├── defineScript.js
│ ├── ScrollContainer.js
│ └── index.js
│ ├── test
│ ├── DropDownWrap.js
│ └── index.js
│ └── Demo
│ └── index.js
├── publish.sh
├── public
└── index.html
├── .babelrc
├── webpack.config.js
├── .gitignore
├── webpack
├── prod.config.js
└── dev.config.js
├── package.json
├── README.md
└── dist
└── bundle.js
/src/img/pic1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tntd/formula-editor/HEAD/src/img/pic1.png
--------------------------------------------------------------------------------
/src/img/pic2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tntd/formula-editor/HEAD/src/img/pic2.png
--------------------------------------------------------------------------------
/publish.sh:
--------------------------------------------------------------------------------
1 | npm config set registry https://registry.npmjs.org && npm publish --access public
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import AutoSuggest from './AutoSuggest';
2 |
3 | export default AutoSuggest;
4 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | "@babel/plugin-proposal-class-properties"
8 | ]
9 | }
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import Demo from './components/Demo';
5 |
6 | ReactDOM.render(
7 | ,
8 | document.getElementById('app')
9 | );
10 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = (env) => {
2 | if (env && env.NODE_ENV === 'production') {
3 | return require('./webpack/prod.config.js');
4 | } else {
5 | return require('./webpack/dev.config.js');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # misc
10 | .DS_Store
11 | .env.local
12 | .env.development.local
13 | .env.test.local
14 | .env.production.local
15 |
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 | .idea
20 |
--------------------------------------------------------------------------------
/webpack/prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const nodeExternals = require('webpack-node-externals');
3 |
4 | module.exports = {
5 | mode: 'production',
6 | entry: './src/components/FormulaEdit/index.js',
7 | output: {
8 | filename: 'bundle.js',
9 | path: path.resolve(__dirname, '../dist'),
10 | libraryTarget: 'commonjs2'
11 | },
12 | // devtool: 'source-map',
13 | module: {
14 | rules: [
15 | {
16 | test: /\.js$/,
17 | use: 'babel-loader',
18 | exclude: /node_modules/
19 | },
20 | {
21 | test: /\.css$/,
22 | use: ['style-loader', 'css-loader']
23 | },
24 | {
25 | test: /\.less$/,
26 | use: ['style-loader', 'css-loader', 'less-loader']
27 | }
28 | ]
29 | },
30 | externals: [nodeExternals()]
31 | };
32 |
--------------------------------------------------------------------------------
/webpack/dev.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const htmlWebpackPlugin = require('html-webpack-plugin');
3 |
4 | module.exports = {
5 | mode: 'development',
6 | entry: './src/app.js',
7 | output: {
8 | filename: 'bundle.js',
9 | path: path.resolve(__dirname, '../dist'),
10 | },
11 | devtool: "eval-source-map",
12 | module: {
13 | rules: [
14 | {
15 | test: /\.js$/,
16 | use: 'babel-loader',
17 | exclude: /node_modules/
18 | },
19 | {
20 | test: /\.css$/,
21 | use: ['style-loader', 'css-loader']
22 | },
23 | {
24 | test: /\.less$/,
25 | use: ['style-loader', 'css-loader', 'less-loader']
26 | }
27 | ]
28 | },
29 | devServer: {
30 | contentBase: './dist',
31 | open: true,
32 | port: 8081
33 | },
34 | plugins: [
35 | new htmlWebpackPlugin({
36 | template: 'public/index.html'
37 | })
38 | ],
39 | };
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tntd/formula-edit",
3 | "version": "2.1.18",
4 | "description": "react计算公式编辑器",
5 | "keywords": [
6 | "formula",
7 | "formula-edit",
8 | "formula-edit-react"
9 | ],
10 | "main": "dist/bundle.js",
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "build": "webpack --env.NODE_ENV=production",
16 | "dev": "PORT=8822 webpack-dev-server --inline --progress --config webpack.config.js"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "git@github.com:tntd/formula-editor.git"
21 | },
22 | "author": "bo.liu@tongdun.cn",
23 | "license": "MIT",
24 | "dependencies": {
25 | "codemirror": "^5.46.0",
26 | "intersection-observer": "^0.12.0",
27 | "js-beautify": "^1.13.13",
28 | "react": "^16.8.6",
29 | "react-dom": "^16.8.6",
30 | "universal-cookie": "^4.0.4"
31 | },
32 | "devDependencies": {
33 | "@babel/core": "7.2.2",
34 | "@babel/plugin-proposal-class-properties": "^7.4.4",
35 | "@babel/preset-env": "7.2.3",
36 | "@babel/preset-react": "7.0.0",
37 | "babel-loader": "^8.0.5",
38 | "css-loader": "0.28.9",
39 | "html-webpack-plugin": "3.2.0",
40 | "less": "^3.9.0",
41 | "less-loader": "^5.0.0",
42 | "prop-types": "15.6.0",
43 | "style-loader": "0.19.1",
44 | "webpack": "4.29.0",
45 | "webpack-cli": "3.2.1",
46 | "webpack-dev-server": "3.1.14",
47 | "webpack-node-externals": "1.6.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/FormulaEdit/otp.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'universal-cookie';
2 |
3 | const cookies = new Cookies();
4 |
5 | export const TypeMap = {
6 | cn: {
7 | INT: '整数',
8 | DOUBLE: '小数',
9 | STRING: '字符串',
10 | ENUM: '枚举',
11 | BOOLEAN: '布尔',
12 | DATETIME: '时间',
13 | ARRAY: '数组',
14 | },
15 |
16 | en: {
17 | INT: 'Integer',
18 | DOUBLE: 'Double',
19 | STRING: 'String',
20 | ENUM: 'Enum',
21 | BOOLEAN: 'Boolean',
22 | DATETIME: 'Datetime',
23 | ARRAY: 'Array',
24 | }
25 | };
26 |
27 | export const getLang = () => {
28 | return cookies.get('lang') || 'cn';
29 | };
30 |
31 |
32 | export const getTypeMap = (typeMap,language) => {
33 | let lang = language || getLang();
34 | let otp = TypeMap[lang];
35 |
36 | const TYPE_MAP = {
37 | 'INT': {
38 | 'displayName': otp.INT,
39 | 'color': '#5262C7'
40 | },
41 | 'LONG': {
42 | 'displayName': otp.INT,
43 | 'color': '#5262C7'
44 | },
45 | 'DOUBLE': {
46 | 'displayName': otp.DOUBLE,
47 | 'color': '#00D2C2'
48 | },
49 | 'STRING': {
50 | 'displayName': otp.STRING,
51 | 'color': '#826AF9'
52 | },
53 | 'ENUM': {
54 | 'displayName': otp.ENUM,
55 | 'color': '#00C5DC'
56 | },
57 | 'BOOLEAN': {
58 | 'displayName': otp.BOOLEAN,
59 | 'color': '#4A9AF7'
60 | },
61 | 'DATETIME': {
62 | 'displayName': otp.DATETIME,
63 | 'color': '#826AF9'
64 | },
65 | 'ARRAY': {
66 | 'displayName': otp.ARRAY,
67 | 'color': '#E5A74F'
68 | }
69 | };
70 |
71 | return TYPE_MAP || {};
72 | };
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/components/FormulaEdit/index.less:
--------------------------------------------------------------------------------
1 | .tnt-codemirror {
2 | position: relative;
3 | overflow: hidden;
4 | border: #d7d7d7 solid 1px;
5 |
6 | .placeholder {
7 | pointer-events: none;
8 | position: absolute;
9 | top: 3px;
10 | left: 35px;
11 | font-size: 12px;
12 | color: #c6d0d3;
13 | opacity: 0.4;
14 | }
15 |
16 | .codemirror-tip-day,
17 | .codemirror-tip-night {
18 | position: fixed;
19 | left: 0;
20 | top: 0;
21 | z-index: 999;
22 | background: #fff;
23 | width: 200px;
24 | height: 200px; //
25 | overflow: auto; //
26 | box-shadow: rgba(119, 119, 119, 0.2) 0px 0px 7px, rgba(0, 0, 0, 0) 1px 1px 0px inset, rgba(0, 0, 0, 0) -1px -1px 0px inset;
27 | font-size: 12px;
28 |
29 | ul {
30 | margin: 0;
31 | padding: 0;
32 | //max-height: 226px; //
33 | overflow: auto;
34 |
35 | li {
36 | list-style: none;
37 | //padding: 5px 10px; //
38 | cursor: pointer;
39 | padding: 0px 10px; //
40 | height: 30px; //
41 | line-height: 30px; //
42 | line-height: 30px;
43 | overflow: hidden;
44 | text-overflow: ellipsis;
45 | white-space: nowrap;
46 |
47 | sup {
48 | padding-right: 5px;
49 | }
50 |
51 | &:hover {
52 | background: #63acff;
53 | color: #fff;
54 | }
55 | }
56 |
57 | .cm-active {
58 | background: #63acff;
59 | color: #fff;
60 | }
61 | }
62 | }
63 |
64 | // 重置codemirror主题颜色
65 | .cm-s-3024-day {
66 | span.cm-field-keyword {
67 | color: #FF9800;
68 | }
69 |
70 | span.cm-function-keyword {
71 | color: #03A9F4;
72 | }
73 |
74 | span.cm-nomal-keyword {
75 | color: #F44336;
76 | border-bottom: #F44336 1px dotted;
77 | }
78 |
79 | span.cm-boolean-keyword {
80 | color: #673AB7;
81 | }
82 |
83 | span.cm-string {
84 | color: #cdab53;
85 | }
86 |
87 | span.cm-comment {
88 | color: #9E9E9E;
89 | }
90 | }
91 |
92 | .cm-s-material {
93 | span.cm-field-keyword {
94 | color: #FF9800;
95 | }
96 |
97 | span.cm-function-keyword {
98 | color: #03A9F4;
99 | }
100 |
101 | span.cm-nomal-keyword {
102 | color: #F44336;
103 | border-bottom: #F44336 1px dotted;
104 | }
105 |
106 | span.cm-boolean-keyword {
107 | color: #7689f3;
108 | }
109 | }
110 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @tntd/formula-edit
2 |
3 | react计算公式编辑器
4 |
5 | ## Usage
6 |
7 | ### @tntd/formula-edit
8 |
9 | 安装
10 |
11 | ```sh
12 | npm install --save-dev @tntd/formula-edit
13 | ```
14 | 使用
15 |
16 | ```
17 | {}} // 回调
25 | // defaultValue={defaultCode} // 初始化值 已去除该属性
26 | // readOnly={false} // 是否只读 默认false
27 | // lineNumber={true} // 是否显示列数 默认true
28 | // theme="night" // 主题 默认night
29 | // height={300} // 高度 默认300
30 | />
31 | ```
32 |
33 | ## fieldList 输入@符号才能调起
34 | ```
35 | fieldList: [
36 | { name: "放款金额", value: "fkje"},
37 | { name: "实际放款金额", value: "sjfkje"},
38 | { name: "借款人姓名", value: "jkrxm"},
39 | { name: "借款人手机号", value: "jkrsjh"},
40 | { name: "借款人身份证", value: "jkrsfz"},
41 | ],
42 | ```
43 | ## methodList 输入#符号才能调起
44 | ```
45 | methodList: [
46 | { name: "平均值", value: "平均值(,)", realValue: "avg" },
47 | { name: "最大值", value: "最大值(,)", realValue: "max" },
48 | { name: "最小值", value: "最小值(,)", realValue: "min" },
49 | { name: "求和", value: "求和(,)", realValue: "sum" }
50 | ],
51 | ```
52 | ## normalList 自定义无需校验关键词
53 | ```
54 | normalList: [
55 | { name: "且", value: "and" },
56 | { name: "或", value: "or" }
57 | ],
58 | ```
59 |
60 | ## 特殊数据场景 @STRINGαC_S_CARDNO
61 | ```
62 | const typeKeyWords = ['αINT', 'αSTRING', 'αDOUBLE', 'αBOOLEAN', 'αDATETIME', 'αLONG', 'αENUM', 'αJSON'];
63 |
64 | const enCodeToCnExtraLogic = (enCode) => {
65 | const regExp = new RegExp(`(${typeKeyWords.join('|')})`, 'g');
66 | const cnCode = enCode.replace(regExp, () => {
67 | return '';
68 | });
69 | return cnCode;
70 | };
71 |
72 | {}} // 回调
80 | cnCodeToEnExtraLogic={(item) => {
81 | return `α${item.type}`;
82 | }}
83 | enCodeToCnExtraLogic={enCodeToCnExtraLogic}
84 | />
85 | ```
86 |
87 | ## 编辑器效果
88 | 
89 | 
90 |
91 | ## props参数:
92 | | 参数 | 类型 | 默认值 | 是否必填 | 说明 |
93 | | :------: | :-----: | :----: | :------: | :----------: |
94 | | defaultValue | string | 无 | 非必填 | 初始化赋值 2.0已废弃 |
95 | | value | string | 无 | 非必填 | 赋值 传入组件自动转化成cnCode |
96 | | height | number | 300 | 非必填 | 高度设置 |
97 | | theme | string | night | 非必填 | 主题: 目前只支持两种:day,night |
98 | | readOnly | boolean | false | 非必填 | 设置只读 |
99 | | lineNumber | boolean | false | 非必填 | 设置行号 |
100 | | typeMap | object | 无 | 非必填 | 自定义变量类型和颜色 |
101 | | isEndMark | boolean | false | 非必填 | 是否需要@结束符 |
102 | | fieldList | array | 无 | 非必填 | 字段列表 |
103 | | methodList | array | 无 | 非必填 | 方法列表 |
104 | | normalList | array | 无 | 非必填 | 自定义关键词列表 |
105 | | onChange | function(enCode, obj)| 无 | 非必填 | enCode, obj(里面有cnCode) |
106 |
107 | ## 支持ref对外暴露方法
108 | | 方法 | 类型 | 说明 |
109 | | :------: | :-----: | :----------: |
110 | | fullScreen() | function | 全屏 |
111 | | exitFullScreen() | function | 退出全屏 |
112 | | insertValue(value) | function | 光标处插入值 |
113 |
114 | ## License
115 | MIT
116 |
--------------------------------------------------------------------------------
/src/components/FormulaEdit/defineScript.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: liubo
3 | * @CreatDate: 2019-02-25 10:59:46
4 | * @Describe: 自定义codemirror mode
5 | */
6 |
7 | import * as CodeMirror from "codemirror/lib/codemirror";
8 |
9 | function defineScript(mode, modeField) {
10 | CodeMirror.defineMode(mode, function () {
11 |
12 | const markList = [">=", "<=", "!=", "=", ">", "<", "+", "-", "*", "/",
13 | "(", ")", ";", ",", ":", "{", "}"];
14 |
15 | return {
16 | startState: function() {
17 | return { inMultilineComment: false };
18 | },
19 | token: function (stream, state) {
20 | // 空白空间
21 | if (stream.eatSpace()) return null;
22 |
23 | if (state.inMultilineComment) {
24 | // 跳过内容直到找到'*'字符
25 | const beforeLength = stream.pos;
26 | stream.skipTo("*");
27 |
28 | // 如果找到'*',消耗它
29 | if (stream.pos !== beforeLength) {
30 | stream.next(); // 消耗 '*'
31 |
32 | // 检查下一个字符是否是'/',如果是则结束注释
33 | if (stream.peek() === "/") {
34 | stream.next(); // 消耗 '/'
35 | state.inMultilineComment = false;
36 | }
37 | } else {
38 | // 到达行尾,继续保持在多行注释状态
39 | stream.skipToEnd();
40 | }
41 | return "comment";
42 | }
43 |
44 | // 处理单行注释
45 | if (stream.match("//")) {
46 | stream.skipToEnd(); // "//"后面全部包进comment
47 | return "comment";
48 | }
49 |
50 | // 处理多行注释 /* */
51 | if (stream.match("/**")) {
52 | state.inMultilineComment = true;
53 |
54 | // 立即检查是否有单行的多行注释(如 /* 注释 */)
55 | const beforeLength = stream.pos;
56 | stream.skipTo("*");
57 |
58 | if (stream.pos !== beforeLength) {
59 | stream.next(); // 消耗 '*'
60 | if (stream.peek() === "/") {
61 | stream.next(); // 消耗 '/'
62 | state.inMultilineComment = false;
63 | }
64 | } else {
65 | stream.skipToEnd();
66 | }
67 | return "comment";
68 | }
69 |
70 | // 处理符号
71 | for (let i = 0; i < markList.length; i++) {
72 | if (stream.match(markList[i])) {
73 | return "mark-keyword";
74 | }
75 | }
76 |
77 | // 处理布尔 true,false
78 | if (stream.match("true") || stream.match("false")) return "boolean-keyword";
79 |
80 | // 处理数字文本
81 | if (stream.match(/^[0-9\.+-]/, false)) {
82 | if (stream.match(/^[+-]?0x[0-9a-fA-F]+/)) { return "number"; }
83 | if (stream.match(/^[+-]?\d*\.\d+([EeDd][+-]?\d+)?/)) { return "number"; }
84 | if (stream.match(/^[+-]?\d+([EeDd][+-]?\d+)?/)) { return "number"; }
85 | }
86 |
87 | // 处理string
88 | if (stream.match(/^"([^"]|(""))*"/)) { return "string"; }
89 | if (stream.match(/^'([^']|(''))*'/)) { return "string"; }
90 |
91 | const {
92 | codemirrorFieldList = [],
93 | codemirrorKeywordList = [],
94 | codemirrorMethodList = [],
95 | codemirrorNormalList = []
96 | } = modeField.current || {}
97 | // 处理@相关内容
98 | for (let i = 0; i < codemirrorFieldList.length; i++) {
99 | if (stream.match(codemirrorFieldList[i])) {
100 | return "field-keyword";
101 | }
102 | }
103 | // 处理@相关内容
104 | for (let i = 0; i < codemirrorKeywordList.length; i++) {
105 | if (stream.match(codemirrorKeywordList[i])) {
106 | return "keyword";
107 | }
108 | }
109 | // if (fieldKeywordArray.length > 0 && stream.match("@")) { return "field-keyword"; }
110 |
111 | // 处理#相关内容
112 | for (let i = 0; i < codemirrorMethodList.length; i++) {
113 | if (stream.match(codemirrorMethodList[i])) {
114 | return "function-keyword";
115 | }
116 | }
117 | // if (keywordFunctionArray.length > 0 && stream.match("#")) { return "function-keyword"; }
118 |
119 | // 处理自定义无需校验的关键词
120 | for (let i = 0; i < codemirrorNormalList.length; i++) {
121 | if (stream.match(codemirrorNormalList[i])) {
122 | return "function-keyword";
123 | }
124 | }
125 |
126 | // 处理未检测到的项目
127 | stream.next();
128 | return "nomal-keyword";
129 | }
130 | };
131 | });
132 |
133 | CodeMirror.defineMIME(`text/x-${mode}`, mode);
134 | }
135 |
136 | export default defineScript;
137 |
--------------------------------------------------------------------------------
/src/components/test/DropDownWrap.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4 |
5 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6 |
7 | Object.defineProperty(exports, "__esModule", {
8 | value: true
9 | });
10 | exports["default"] = void 0;
11 |
12 | var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
13 |
14 | var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
15 |
16 | var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
17 |
18 | var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
19 |
20 | var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
21 |
22 | var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
23 |
24 | var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
25 |
26 | var _react = _interopRequireWildcard(require("react"));
27 |
28 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
29 |
30 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
31 |
32 | var DropDownWrap =
33 | /*#__PURE__*/
34 | function (_PureComponent) {
35 | (0, _inherits2["default"])(DropDownWrap, _PureComponent);
36 |
37 | function DropDownWrap(props) {
38 | var _this;
39 |
40 | (0, _classCallCheck2["default"])(this, DropDownWrap);
41 | _this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(DropDownWrap).call(this, props));
42 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "getItemStyle", function (i) {
43 | var itemHeight = _this.props.itemHeight;
44 | return {
45 | position: "absolute",
46 | top: itemHeight * i,
47 | height: itemHeight,
48 | width: "100%"
49 | };
50 | });
51 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "reactList", function (allHeight, startIndex, endIndex) {
52 | return _this.setState({
53 | allHeight: allHeight,
54 | startIndex: startIndex,
55 | endIndex: endIndex
56 | });
57 | });
58 | var _allHeight = props.allHeight,
59 | _startIndex = props.startIndex,
60 | _endIndex = props.endIndex;
61 | _this.state = {
62 | allHeight: _allHeight,
63 | startIndex: _startIndex,
64 | endIndex: _endIndex
65 | };
66 | return _this;
67 | }
68 |
69 | (0, _createClass2["default"])(DropDownWrap, [{
70 | key: "componentDidUpdate",
71 | value: function componentDidUpdate(prevProps) {
72 | if (this.props.allHeight !== prevProps.allHeight) {
73 | this.setState({
74 | allHeight: this.props.allHeight
75 | });
76 | }
77 | }
78 | }, {
79 | key: "render",
80 | value: function render() {
81 | var _this2 = this;
82 |
83 | var menu = this.props.menu;
84 | var _this$state = this.state,
85 | startIndex = _this$state.startIndex,
86 | endIndex = _this$state.endIndex,
87 | allHeight = _this$state.allHeight; // 截取 Select 下拉列表中需要显示的部分
88 |
89 | var cloneMenu = _react["default"].cloneElement(menu, {
90 | menuItems: menu.props.menuItems.slice(startIndex, endIndex).map(function (item, i) {
91 | var realIndex = (startIndex || 0) + Number(i);
92 |
93 | var style = _this2.getItemStyle(realIndex); // 未搜到数据提示高度使用默认高度
94 |
95 |
96 | if (item.key === "NOT_FOUND") {
97 | delete style.height;
98 | }
99 |
100 | return _react["default"].cloneElement(item, {
101 | style: _objectSpread({}, item.props.style, {}, style)
102 | });
103 | }),
104 | dropdownMenuStyle: _objectSpread({}, menu.props.dropdownMenuStyle, {
105 | height: allHeight,
106 | maxHeight: allHeight,
107 | overflow: "hidden"
108 | })
109 | });
110 |
111 | return cloneMenu;
112 | }
113 | }]);
114 | return DropDownWrap;
115 | }(_react.PureComponent);
116 |
117 | exports["default"] = DropDownWrap;
--------------------------------------------------------------------------------
/src/components/Demo/index.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useRef } from "react";
2 | import FormulaEdit from '../FormulaEdit';
3 |
4 |
5 | // const TYPE_MAP = {
6 | // 'INT': {
7 | // 'displayName': '整数',
8 | // 'color': '#5262C7'
9 | // },
10 | // 'DOUBLE': {
11 | // 'displayName': '小数',
12 | // 'color': '#00D2C2'
13 | // },
14 | // 'STRING': {
15 | // 'displayName': '字符',
16 | // 'color': '#826AF9'
17 | // },
18 | // 'ENUM': {
19 | // 'displayName': '枚举',
20 | // 'color': '#00C5DC'
21 | // },
22 | // 'BOOLEAN': {
23 | // 'displayName': '布尔',
24 | // 'color': '#4A9AF7'
25 | // },
26 | // 'DATETIME': {
27 | // 'displayName': '时间',
28 | // 'color': '#826AF9'
29 | // }
30 | // };
31 |
32 | const testData = [];
33 | for (let i = 0; i < 800; i++) {
34 | testData.push(
35 | { name: `[字段]测试试${i}`, value: `hahahdsdhjbewdbu${i}`, type: 'DOUBLE', prefix: 'sa', }
36 | );
37 | }
38 | testData.push(
39 | { name: `[字段]哈信贷测试`, value: `hahahdsdhjbewdbu$12123`, type: 'ARRAY' }
40 | );
41 | const methodList = [
42 | { name: "求和", value: "求和(,)", realValue: "sum" },
43 | { name: "平均值", value: "平均值(,)", realValue: "avg" },
44 | { name: "最大值", value: "最大值(,)", realValue: "max" },
45 | { name: "最小值", value: "最小值(,)", realValue: "min" },
46 | ];
47 | const methodList2 = [
48 | { name: "求和1", value: "求和1(,)", realValue: "sum" },
49 | { name: "平均值2", value: "平均值2(,)", realValue: "avg" },
50 | { name: "最大值3", value: "最大值3(,)", realValue: "max" },
51 | { name: "最小值4", value: "最小值4(,)", realValue: "min" },
52 | ];
53 |
54 | const normalList = [
55 | { name: "&&", value: "&&" },
56 | { name: "||", value: "||" }
57 | ];
58 |
59 | const fieldList = [
60 | { name: "放款金额", value: "fkje" },
61 | { name: "实际放款金额", value: "sjfkje" },
62 | { name: "借款人姓名", value: "jkrxm" },
63 | { name: "借款人", value: "jkrsjh" },
64 | { name: "借款人身份证", value: "jkrsfz" }
65 | ];
66 |
67 | const typeKeyWords = ['αINT', 'αSTRING', 'αDOUBLE', 'αBOOLEAN', 'αDATETIME', 'αLONG', 'αENUM', 'αJSON'];
68 |
69 | const enCodeToCnExtraLogic = (enCode) => {
70 | const regExp = new RegExp(`(${typeKeyWords.join('|')})`, 'g');
71 | const cnCode = enCode.replace(regExp, () => {
72 | return '';
73 | });
74 | return cnCode;
75 | };
76 |
77 |
78 | export default props => {
79 | const defaultCode = "hhahahah@111";
80 | const [code, setCode] = useState(`int a = 100;
81 | string s = #sum("hello",@hahahdsdhjbewdbu0);
82 | double d=777.324;
83 | boolean x = a >= 0;
84 | list result = [x, d, 333];
85 | "或为空";
86 | 且
87 | 或
88 | @hahahdsdhjbewdbu0 && @hahahdsdhjbewdbu0 || @hahahdsdhjbewdbu2`);
89 | const [height, setHeight] = useState(300);
90 |
91 | const [list, setList] = useState(testData);
92 |
93 | const [list1, setList1] = useState([]);
94 |
95 | const formulaRef = useRef();
96 |
97 | useEffect(() => {
98 | setTimeout(() => {
99 | setList1(methodList)
100 | }, 1000);
101 | return () => {}
102 | }, [])
103 |
104 |
105 | return (
106 | <>
107 | {
115 | console.log(data)
116 | console.log('onChange---------1')
117 | setCode(enCode);
118 | }} // 回调
119 | normalList={normalList}
120 | editorEvent={(event) => {
121 | formulaRef.current = event;
122 | }}
123 |
124 | // cnCodeToEnExtraLogic={(item) => {
125 | // return `α${item.type}`;
126 | // }}
127 | // enCodeToCnExtraLogic={enCodeToCnExtraLogic}
128 |
129 | // defaultValue={defaultCode} // 初始化值 去除该属性
130 | // readOnly={false} // 是否只读 默认false
131 | // lineNumber={true} // 是否显示列数 默认true
132 | // theme="night" // 主题 默认night
133 | height={height} // 高度 默认300
134 | />
135 | {/* {
140 | console.log('onChange---------1')
141 | console.log(data)
142 | console.log('onChange---------2')
143 | setCode(enCode);
144 | }} // 回调
145 | height={height} // 高度 默认300
146 | />
147 | {
154 | console.log('onChange---------1')
155 | console.log(data)
156 | console.log('onChange---------2')
157 | setCode(enCode);
158 | }} // 回调
159 | // theme="day" // 主题 默认night
160 | height={height} // 高度 默认300
161 | /> */}
162 |
166 |
169 |
173 |
180 |
184 | >
185 | );
186 | };
187 |
--------------------------------------------------------------------------------
/src/components/FormulaEdit/ScrollContainer.js:
--------------------------------------------------------------------------------
1 | import 'intersection-observer';
2 | import React, { useEffect } from 'react';
3 | import './index.less';
4 | import ReactDOM from 'react-dom';
5 |
6 | import { getTypeMap } from './otp';
7 |
8 | const firstItemClass = 'li-first',
9 | lastItemClass = 'li-last';
10 | const ScrollContainer = (props) => {
11 | const { style, dropList, theme, selectChange, listLen, listSize, itemHeight, typeMap, lang, domId } = props;
12 | const halfListSize = Math.floor(listSize / 2);
13 | let box = null,
14 | intersectionObserver = null,
15 | lastRenderIndex = 0,
16 | currentIndex = 0,
17 | firstItem = null,
18 | lastItem = null,
19 | lastScrollTop = 0;
20 |
21 | const TYPE_MAP = getTypeMap(lang);
22 |
23 | useEffect(() => {
24 | window.currentIndex = currentIndex;
25 | if (listLen <= listSize) {
26 | renderList(0, listLen);
27 | const container = document.getElementById(domId);
28 | listLen && container && container.children && Array.isArray(container.children) && container.children[0].classList.add('cm-active');
29 | } else {
30 | // 创建 intersectionObserver 对象
31 | intersectionObserver = new IntersectionObserver((entries) => {
32 | for (const entry of entries) {
33 | if (!entry.isIntersecting) return false;
34 | if (currentIndex && entry.target.classList.contains(firstItemClass)) {
35 | // console.log('向上滑动')
36 | handleScroll(false);
37 | } else if (entry.target.classList.contains(lastItemClass)) {
38 | // console.log('向下滑动')
39 | handleScroll(true);
40 | }
41 | }
42 | });
43 | initFirstItems();
44 | // 用户大幅度滚动时则使用scroll兼容方案
45 | polyScroll();
46 | }
47 | return () => {
48 | // console.log('清除')
49 | firstItem && intersectionObserver && intersectionObserver.unobserve(firstItem);
50 | lastItem && intersectionObserver && intersectionObserver.unobserve(lastItem);
51 | intersectionObserver && intersectionObserver.disconnect();
52 | init();
53 | };
54 | }, [dropList]);
55 |
56 | useEffect(() => {
57 | const container = document.getElementById(domId);
58 | container.addEventListener('click', (e) => {
59 | let curr = e.target;
60 | while (!curr.getAttribute('data-value')) {
61 | curr = curr.parentNode;
62 | }
63 | const value = curr.getAttribute('data-value');
64 | const name = curr.getAttribute('data-name');
65 | selectChange({ name, value });
66 | });
67 | return () => {
68 | container.removeEventListener('click', () => { });
69 | };
70 | }, []);
71 |
72 | const init = () => {
73 | const container = document.getElementById(domId);
74 | const scroll = document.querySelector('.scroll-container');
75 | if (container) {
76 | container.style.paddingTop = '0px';
77 | container.style.paddingBottom = '0px';
78 | }
79 | box = null;
80 | intersectionObserver = null;
81 | lastRenderIndex = 0;
82 | currentIndex = 0;
83 | lastScrollTop = 0;
84 | firstItem = null;
85 | lastItem = null;
86 | if (scroll) {
87 | scroll.scrollTop = 0;
88 | }
89 | };
90 |
91 | const polyScroll = () => {
92 | const box = document.querySelector('.scroll-container');
93 | const boxHeight = box && box.offsetHeight;
94 | const baseHeight = itemHeight * halfListSize - itemHeight - boxHeight;
95 | box.onscroll = (e) => {
96 | const currentScrollTop = e.target.scrollTop;
97 | // 处理大幅向下滚动
98 | if (currentScrollTop - lastScrollTop >= itemHeight) {
99 | let firstIndex = Math.floor(Math.abs(currentScrollTop - baseHeight) / (itemHeight * halfListSize)) * halfListSize;
100 | const lastIndex = getContainerLastIndex();
101 | firstIndex = firstIndex > lastIndex ? lastIndex : firstIndex;
102 | if (firstIndex !== window.currentIndex) scrollCallback(firstIndex, true);
103 | lastScrollTop = currentScrollTop;
104 | }
105 | if (currentScrollTop - lastScrollTop <= -itemHeight) {
106 | let firstIndex = Math.floor((currentScrollTop - itemHeight) / (itemHeight * halfListSize)) * halfListSize;
107 | firstIndex = firstIndex <= 0 ? 0 : firstIndex;
108 | scrollCallback(firstIndex, false);
109 | lastScrollTop = currentScrollTop;
110 | }
111 | };
112 | };
113 |
114 | const initFirstItems = () => {
115 | renderList(0, listSize);
116 | adjustPaddings(0);
117 | setTimeout(() => {
118 | bindNewItems();
119 | }, 100);
120 | };
121 |
122 | const renderList = (firstIndex, listSize) => {
123 | const newTypeMap = { ...TYPE_MAP, ...typeMap };
124 | let currentList = JSON.parse(JSON.stringify(dropList));
125 | currentList = currentList.splice(firstIndex, listSize);
126 | const container = document.getElementById(domId);
127 | container.style.display = 'none';
128 | const listItems = [];
129 | for (let i = 0; i < currentList.length; i++) {
130 | let item = currentList[i];
131 | const dataTypeObj = newTypeMap[item.type] ? newTypeMap[item.type] : '';
132 | let listItemContent;
133 | listItemContent = (
134 |
135 | {dataTypeObj && {dataTypeObj.displayName}}
136 | {item.prefix}
137 | {item.name}
138 |
139 | );
140 |
141 | listItems.push(listItemContent); // Push each list item component to the array
142 | }
143 | // eslint-disable-next-line react/no-deprecated
144 | ReactDOM.render(null, container); //
145 | ReactDOM.render(listItems, container); //
146 | container.style.display = 'block';
147 | };
148 |
149 | const bindNewItems = (isScrollDown) => {
150 | const container = document.getElementById(domId);
151 | firstItem && intersectionObserver && intersectionObserver.unobserve(firstItem);
152 | lastItem && intersectionObserver && intersectionObserver.unobserve(lastItem);
153 | if (container && container.children && container.children.length) {
154 | container.children[0].classList.add(firstItemClass);
155 | //start-键盘移动添加active属性 上下移动位置不同
156 | let newInd = 0;
157 | if (isScrollDown && window.currentIndex) {
158 | newInd = (window.currentIndex / 10) % 2 > 0 ? 8 : 7;
159 | }
160 | if (isScrollDown === false) {
161 | newInd = (window.currentIndex / 10) % 2 > 0 ? 13 : 11;
162 | }
163 | container && container.children && container.children[newInd] && container.children[newInd].classList.add('cm-active');
164 | //-end
165 | container.children[container.children.length - 1].classList.add(lastItemClass);
166 | const newFirstItem = container.querySelector(`.${firstItemClass}`);
167 | const newLastItem = container.querySelector(`.${lastItemClass}`);
168 | intersectionObserver && intersectionObserver.observe(newFirstItem);
169 | intersectionObserver && intersectionObserver.observe(newLastItem);
170 | firstItem = newFirstItem;
171 | lastItem = newLastItem;
172 | }
173 | };
174 |
175 | const handleScroll = (isScrollDown) => {
176 | const firstIndex = getContainerFirstIndex(isScrollDown);
177 | scrollCallback(firstIndex, isScrollDown);
178 | };
179 |
180 | const getContainerFirstIndex = (isScrollDown) => {
181 | return isScrollDown ? currentIndex + halfListSize : currentIndex - halfListSize < 0 ? 0 : currentIndex - halfListSize;
182 | };
183 |
184 | const getContainerLastIndex = () => {
185 | const restListSize = listLen % listSize;
186 | let lastIndex;
187 | if (restListSize === 0) {
188 | lastIndex = listLen - listSize;
189 | } else {
190 | lastIndex = restListSize >= halfListSize ? listLen - restListSize : listLen - restListSize - halfListSize;
191 | }
192 | return lastIndex <= 0 ? 0 : lastIndex;
193 | };
194 |
195 | const scrollCallback = (firstIndex, isScrollDown) => {
196 | const lastIndex = getContainerLastIndex();
197 | if (isScrollDown) {
198 | if (firstIndex > lastIndex) return;
199 | } else {
200 | if (lastRenderIndex === firstIndex) return;
201 | }
202 | lastRenderIndex = firstIndex;
203 | if (firstIndex === lastIndex) {
204 | const lastListSize = listLen - lastIndex;
205 | renderList(lastIndex, lastListSize);
206 | } else {
207 | renderList(firstIndex, listSize);
208 | }
209 | currentIndex = firstIndex;
210 | window.currentIndex = currentIndex;
211 | bindNewItems(isScrollDown);
212 | adjustPaddings(firstIndex);
213 | };
214 |
215 | const adjustPaddings = (firstIndex) => {
216 | const container = document.getElementById(domId);
217 | const halfListSizeHeight = halfListSize * itemHeight;
218 | const totalPadding = ((listLen - listSize) / halfListSize) * halfListSizeHeight;
219 | const newCurrentPaddingTop = firstIndex <= 0 ? 0 : (firstIndex / halfListSize) * halfListSizeHeight;
220 | const newCurrentPaddingBottom = totalPadding - newCurrentPaddingTop < 0 ? 0 : totalPadding - newCurrentPaddingTop;
221 | container.style.paddingTop = `${newCurrentPaddingTop}px`;
222 | container.style.paddingBottom = `${newCurrentPaddingBottom}px`;
223 | };
224 | return (
225 |
233 | );
234 | };
235 |
236 | export default ScrollContainer;
237 |
--------------------------------------------------------------------------------
/src/components/test/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4 |
5 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6 |
7 | Object.defineProperty(exports, "__esModule", {
8 | value: true
9 | });
10 | exports["default"] = void 0;
11 |
12 | var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
13 |
14 | require("antd/es/empty/style");
15 |
16 | var _empty = _interopRequireDefault(require("antd/es/empty"));
17 |
18 | var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
19 |
20 | var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
21 |
22 | var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
23 |
24 | var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
25 |
26 | var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
27 |
28 | var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
29 |
30 | var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
31 |
32 | var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
33 |
34 | var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
35 |
36 | require("antd/es/select/style");
37 |
38 | var _select = _interopRequireDefault(require("antd/es/select"));
39 |
40 | var _react = _interopRequireWildcard(require("react"));
41 |
42 | var _DropDownWrap = _interopRequireDefault(require("./DropDownWrap"));
43 |
44 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
45 |
46 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
47 |
48 | // 页面实际渲染的下拉菜单数量,实际为 2 * ITEM_ELEMENT_NUMBER
49 | var ITEM_ELEMENT_NUMBER = 30; // Select size 配置
50 |
51 | var ITEM_HEIGHT_CFG = {
52 | small: 24,
53 | large: 40,
54 | "default": 32
55 | };
56 | var ARROW_CODE = {
57 | 40: "down",
58 | 38: "up"
59 | };
60 | var DROPDOWN_HEIGHT = 224;
61 | var Option = _select["default"].Option;
62 |
63 | var SuperSelect =
64 | /*#__PURE__*/
65 | function (_PureComponent) {
66 | (0, _inherits2["default"])(SuperSelect, _PureComponent);
67 |
68 | function SuperSelect(props) {
69 | var _this;
70 |
71 | (0, _classCallCheck2["default"])(this, SuperSelect);
72 | _this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(SuperSelect).call(this, props));
73 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "turnChildren", function (children) {
74 | if (!children) return [];
75 | var arr = [];
76 |
77 | if (children && children.props) {
78 | arr.push(children);
79 | } else {
80 | children.forEach(function (item) {
81 | if (item) {
82 | if (item instanceof Array) {
83 | arr = arr.concat(item);
84 | } else {
85 | arr.push(item);
86 | }
87 | }
88 | });
89 | }
90 |
91 | return arr;
92 | });
93 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "formulaWidth", function () {
94 | // 获取dom设置宽度
95 | var _this$props = _this.props,
96 | arr2 = _this$props.children,
97 | _this$props$dropdownM = _this$props.dropdownMatchSelectWidth,
98 | dropdownMatchSelectWidth = _this$props$dropdownM === void 0 ? true : _this$props$dropdownM,
99 | maxWidth = _this$props.maxWidth;
100 | var selectDom = document.getElementById(_this.id);
101 | var selectWidth = selectDom.clientWidth || selectDom.offsetWidth;
102 | var arr = [];
103 | var formulaMaxWidth = 10;
104 |
105 | if (!dropdownMatchSelectWidth) {
106 | formulaMaxWidth = maxWidth || 10;
107 | }
108 |
109 | if (!dropdownMatchSelectWidth && !maxWidth) {
110 | var children = _this.turnChildren(arr2);
111 |
112 | for (var i = 0; i < children.length; i++) {
113 | var val = children[i].props.children;
114 |
115 | var textWidth = _this.getTextPixelWith(val);
116 |
117 | arr.push(textWidth.toFixed(2));
118 | }
119 |
120 | if (arr.length > 0) {
121 | formulaMaxWidth = Math.max.apply(Math, arr);
122 | }
123 | }
124 |
125 | _this.setState({
126 | selectWidth: selectWidth,
127 | maxWidth: formulaMaxWidth
128 | });
129 | });
130 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "getTextPixelWith", function (text) {
131 | var fontStyle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "14px";
132 | var canvas = document.createElement("canvas"); // 创建 canvas 画布
133 |
134 | var context = canvas.getContext("2d"); // 获取 canvas 绘图上下文环境
135 |
136 | context.font = fontStyle; // 设置字体样式,使用前设置好对应的 font 样式才能准确获取文字的像素长度
137 |
138 | var dimension = context.measureText(text); // 测量文字
139 |
140 | return dimension.width;
141 | });
142 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "getItemStyle", function (i) {
143 | return {
144 | position: "absolute",
145 | top: _this.ITEM_HEIGHT * i,
146 | width: "100%",
147 | height: _this.ITEM_HEIGHT
148 | };
149 | });
150 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "addEvent", function () {
151 | _this.scrollEle = document.querySelector(".".concat(_this.dropdownClassName)); // 下拉菜单未展开时元素不存在
152 |
153 | if (!_this.scrollEle) return;
154 |
155 | _this.scrollEle.addEventListener("scroll", _this.onScroll, false);
156 |
157 | _this.inputEle = document.querySelector("#".concat(_this.id));
158 | if (!_this.inputEle) return;
159 |
160 | _this.inputEle.addEventListener("keydown", _this.onKeyDown, false);
161 | });
162 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "onKeyDown", function (e) {
163 | var _ref = e || {},
164 | keyCode = _ref.keyCode;
165 |
166 | setTimeout(function () {
167 | var activeItem = document.querySelector(".".concat(_this.dropdownClassName, " .ant-select-dropdown-menu-item-active"));
168 | if (!activeItem) return;
169 | var offsetTop = activeItem.offsetTop;
170 | var isUp = ARROW_CODE[keyCode] === "up";
171 | var isDown = ARROW_CODE[keyCode] === "down"; // 在所有列表第一行按上键
172 |
173 | if (offsetTop - _this.prevTop > DROPDOWN_HEIGHT && isUp) {
174 | _this.scrollEle.scrollTo(0, _this.allHeight - DROPDOWN_HEIGHT);
175 |
176 | _this.prevTop = _this.allHeight;
177 | return;
178 | } // 在所有列表中最后一行按下键
179 |
180 |
181 | if (_this.prevTop > offsetTop + DROPDOWN_HEIGHT && isDown) {
182 | _this.scrollEle.scrollTo(0, 0);
183 |
184 | _this.prevTop = 0;
185 | return;
186 | }
187 |
188 | _this.prevTop = offsetTop; // 向下滚动到下拉框最后一行时,向下滚动一行的高度
189 |
190 | if (offsetTop > _this.scrollEle.scrollTop + DROPDOWN_HEIGHT - _this.ITEM_HEIGHT + 10 && isDown) {
191 | _this.scrollEle.scrollTo(0, _this.scrollTop + _this.ITEM_HEIGHT);
192 |
193 | return;
194 | } // 向上滚动到下拉框第一一行时,向上滚动一行的高度
195 |
196 |
197 | if (offsetTop < _this.scrollEle.scrollTop && isUp) {
198 | _this.scrollEle.scrollTo(0, _this.scrollTop - _this.ITEM_HEIGHT);
199 | }
200 | }, 100);
201 | });
202 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "onScroll", function () {
203 | return _this.throttleByHeight(_this.onScrollReal);
204 | });
205 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "onScrollReal", function () {
206 | _this.allList = _this.getUseChildrenList();
207 |
208 | var _this$getStartAndEndI = _this.getStartAndEndIndex(),
209 | startIndex = _this$getStartAndEndI.startIndex,
210 | endIndex = _this$getStartAndEndI.endIndex;
211 |
212 | _this.prevScrollTop = _this.scrollTop; // 重新渲染列表组件 Wrap
213 |
214 | var allHeight = _this.allList.length * _this.ITEM_HEIGHT || 100;
215 |
216 | _this.wrap.reactList(allHeight, startIndex, endIndex);
217 | });
218 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "throttleByHeight", function () {
219 | _this.scrollTop = _this.scrollEle.scrollTop; // 滚动的高度
220 |
221 | var delta = _this.prevScrollTop - _this.scrollTop;
222 | delta = delta < 0 ? 0 - delta : delta;
223 | delta > _this.reactDelta && _this.onScrollReal();
224 | });
225 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "getUseChildrenList", function () {
226 | return _this.state.filterChildren || _this.state.children;
227 | });
228 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "getStartAndEndIndex", function () {
229 | // 滚动后显示在列表可视区中的第一个 item 的 index
230 | var showIndex = Number((_this.scrollTop / _this.ITEM_HEIGHT).toFixed(0));
231 | var startIndex = showIndex - ITEM_ELEMENT_NUMBER < 0 ? 0 : showIndex - ITEM_ELEMENT_NUMBER / 2;
232 | var endIndex = showIndex + ITEM_ELEMENT_NUMBER;
233 | return {
234 | startIndex: startIndex,
235 | endIndex: endIndex
236 | };
237 | });
238 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "setSuperDrowDownMenu", function (visible) {
239 | if (!visible) return;
240 | _this.allList = _this.getUseChildrenList();
241 |
242 | if (!_this.eventTimer || !_this.scrollEle) {
243 | _this.eventTimer = setTimeout(function () {
244 | return _this.addEvent();
245 | }, 0);
246 | } else {
247 | var allHeight = _this.allList.length * _this.ITEM_HEIGHT || 100; // 下拉列表单独重新渲染
248 |
249 | var _this$getStartAndEndI2 = _this.getStartAndEndIndex(),
250 | startIndex = _this$getStartAndEndI2.startIndex,
251 | endIndex = _this$getStartAndEndI2.endIndex;
252 |
253 | setTimeout(function () {
254 | _this.wrap && _this.wrap.reactList(allHeight, startIndex, endIndex);
255 | }, 0);
256 | }
257 | });
258 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "onDropdownVisibleChange", function (visible) {
259 | var onDropdownVisibleChange = _this.props.onDropdownVisibleChange;
260 | onDropdownVisibleChange && onDropdownVisibleChange(visible);
261 | var _this$state = _this.state,
262 | value = _this$state.value,
263 | children = _this$state.children; // 关闭下拉框前清空筛选条件,防止下次打开任然显示筛选后的 options
264 |
265 | if (!visible) {
266 | // 定时器确保关闭后再设置 filterChildren,防止列表刷新闪烁
267 | setTimeout(function () {
268 | _this.setState({
269 | filterChildren: null
270 | });
271 |
272 | _this.setDefaultScrollTop(value);
273 | }, 100); // this.removeEvent();
274 | } else {
275 | // this.addEvent();
276 | if (value) {
277 | // 如果已有 value, 设置默认滚动位置
278 | _this.setDefaultScrollTop(); // 设置下拉列表显示数据
279 |
280 |
281 | _this.setSuperDrowDownMenu(visible);
282 | } else if (!value && value !== 0 && children && children.length > 0) {
283 | // 无数据时,下拉回归至第一个
284 | var val = children[0].props.value;
285 |
286 | _this.setDefaultScrollTop(val);
287 | }
288 | }
289 | });
290 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "onDeselect", function (value) {
291 | var onDeselect = _this.props.onDeselect;
292 | onDeselect && onDeselect(value);
293 | });
294 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "onChange", function (value, opt) {
295 | var _this$props2 = _this.props,
296 | showSearch = _this$props2.showSearch,
297 | onChange = _this$props2.onChange,
298 | autoClearSearchValue = _this$props2.autoClearSearchValue,
299 | maxCount = _this$props2.maxCount,
300 | mode = _this$props2.mode;
301 |
302 | if (showSearch || _this.isMultiple) {
303 | // 搜索模式下选择后是否需要重置搜索状态
304 | if (autoClearSearchValue !== false) {
305 | _this.setState({
306 | filterChildren: null
307 | }, function () {
308 | // 搜索成功后重新设置列表的总高度
309 | _this.setSuperDrowDownMenu(true);
310 | });
311 | }
312 | }
313 |
314 | if (mode === "multiple") {
315 | if (value.length <= maxCount) {
316 | _this.setState({
317 | value: value
318 | });
319 | }
320 | } else {
321 | _this.setState({
322 | value: value
323 | });
324 | }
325 |
326 | onChange && onChange(value, opt);
327 |
328 | if (mode !== "multiple") {
329 | _this.select && _this.select.blur();
330 | }
331 | });
332 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "onSearch", function (v) {
333 | var _this$props3 = _this.props,
334 | showSearch = _this$props3.showSearch,
335 | onSearch = _this$props3.onSearch,
336 | filterOption = _this$props3.filterOption,
337 | arr = _this$props3.children;
338 |
339 | var children = _this.turnChildren(arr);
340 |
341 | if (showSearch && filterOption !== false) {
342 | // 须根据 filterOption(如有该自定义函数)手动 filter 搜索匹配的列表
343 | var filterChildren = null;
344 |
345 | if (typeof filterOption === "function") {
346 | filterChildren = children.filter(function (item) {
347 | return filterOption(v, item);
348 | });
349 | } else if (filterOption === undefined) {
350 | filterChildren = children.filter(function (item) {
351 | return _this.filterOption(v, item);
352 | });
353 | } // 搜索框有值,去除disabled=true的children
354 |
355 |
356 | var newFilterChild = [];
357 | filterChildren && filterChildren.forEach(function (item, index) {
358 | if (!item.props.disabled) {
359 | newFilterChild.push(item);
360 | }
361 | });
362 | filterChildren = newFilterChild; // 设置下拉列表显示数据
363 |
364 | _this.setState({
365 | filterChildren: v === "" ? null : filterChildren
366 | }, function () {
367 | setTimeout(function () {
368 | // 搜索后需要重置滚动位置到0,防止上次 scrollTop 位置无数据
369 | if (filterChildren) {
370 | _this.scrollTop = 0;
371 |
372 | _this.scrollEle.scrollTo(0, 0);
373 | } // 搜索成功后需要重新设置列表的总高度
374 |
375 |
376 | _this.setSuperDrowDownMenu(true);
377 | }, 0);
378 | });
379 | }
380 |
381 | onSearch && onSearch(v);
382 | });
383 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "filterOption", function (v, option) {
384 | // 自定义过滤对应的 option 属性配置
385 | var filterProps = _this.props.optionFilterProp || "value";
386 | return "".concat(option.props[filterProps]).indexOf(v) >= 0;
387 | });
388 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "setDefaultScrollTop", function (data) {
389 | var value = _this.state.value;
390 | var arr = _this.props.children;
391 |
392 | var children = _this.turnChildren(arr);
393 |
394 | var val = data || data === 0 ? data : value;
395 |
396 | for (var i = 0; i < children.length; i++) {
397 | var item = children[i];
398 | var itemValue = item.props.value;
399 |
400 | if (itemValue === val || Array.isArray(val) && val.includes(itemValue)) {
401 | var _ret = function () {
402 | var targetScrollTop = i * _this.ITEM_HEIGHT;
403 | setTimeout(function () {
404 | if (!_this.scrollEle) {
405 | _this.addEvent();
406 | }
407 |
408 | _this.scrollEle && _this.scrollEle.scrollTo(0, targetScrollTop);
409 | }, 100);
410 | return {
411 | v: void 0
412 | };
413 | }();
414 |
415 | if ((0, _typeof2["default"])(_ret) === "object") return _ret.v;
416 | }
417 | }
418 | });
419 | (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "removeEvent", function () {
420 | if (!_this.scrollEle) return;
421 |
422 | _this.scrollEle.removeEventListener("scroll", _this.onScroll, false);
423 |
424 | if (!_this.inputEle) return;
425 |
426 | _this.inputEle.removeEventListener("keydown", _this.onKeyDown, false);
427 | });
428 | var _mode = props.mode,
429 | defaultValue = props.defaultValue,
430 | _value = props.value,
431 | optionHeight = props.optionHeight,
432 | _arr = props.children;
433 | _this.isMultiple = ["tags", "multiple"].includes(_mode);
434 |
435 | var _children = _this.turnChildren(_arr); // 设置默认 value
436 |
437 |
438 | var defaultV = _this.isMultiple ? [] : "";
439 | defaultV = _value || defaultValue || defaultV;
440 | _this.state = {
441 | children: _children || [],
442 | filterChildren: null,
443 | // 筛选后的 options,优先显示,所以清除筛选后手动设为 null
444 | value: defaultV,
445 | maxWidth: null,
446 | selectWidth: null
447 | }; // 下拉菜单项行高
448 |
449 | _this.ITEM_HEIGHT = optionHeight || ITEM_HEIGHT_CFG[props.size || "default"]; // 可视区 dom 高度
450 |
451 | _this.visibleDomHeight = _this.ITEM_HEIGHT * ITEM_ELEMENT_NUMBER; // 滚动时重新渲染的 scrollTop 判断值,大于 reactDelta 则刷新下拉列表
452 |
453 | _this.reactDelta = _this.visibleDomHeight / 3; // 是否拖动滚动条快速滚动状态
454 |
455 | _this.isStopReact = false; // 上一次滚动的 scrollTop 值
456 |
457 | _this.prevScrollTop = 0; // 上一次按下方向键时 scrollTop 值
458 |
459 | _this.prevTop = 0;
460 | _this.scrollTop = 0; // className
461 |
462 | _this.dropdownClassName = "dc".concat(+new Date());
463 | _this.id = "sid".concat(+new Date());
464 | return _this;
465 | }
466 |
467 | (0, _createClass2["default"])(SuperSelect, [{
468 | key: "componentDidMount",
469 | value: function componentDidMount() {
470 | var _this2 = this;
471 |
472 | // defaultOpens=true 时添加滚动事件
473 | setTimeout(function () {
474 | _this2.addEvent();
475 | }, 500);
476 | var arr = this.props.children;
477 | var children = this.turnChildren(arr);
478 |
479 | if (children && children.length > 0) {
480 | this.formulaWidth();
481 | }
482 | }
483 | }, {
484 | key: "componentDidUpdate",
485 | value: function componentDidUpdate(prevProps) {
486 | var _this$props4 = this.props,
487 | mode = _this$props4.mode,
488 | defaultValue = _this$props4.defaultValue,
489 | value = _this$props4.value,
490 | children = _this$props4.children;
491 | var arr = this.turnChildren(children);
492 |
493 | if (prevProps.children !== children) {
494 | this.isMultiple = ["tags", "multiple"].includes(mode);
495 | this.setState({
496 | children: arr || [],
497 | filterChildren: null
498 | });
499 |
500 | if (arr && arr.length > 0) {
501 | this.formulaWidth();
502 | }
503 | }
504 |
505 | if (prevProps.value !== value) {
506 | // 更新时设置默认 value
507 | var defaultV = this.isMultiple ? [] : "";
508 | defaultV = value || defaultValue || defaultV;
509 | this.setState({
510 | value: defaultV
511 | });
512 | }
513 | }
514 | }, {
515 | key: "componentWillUnmount",
516 | value: function componentWillUnmount() {
517 | this.removeEvent();
518 | }
519 | }, {
520 | key: "render",
521 | value: function render() {
522 | var _this3 = this;
523 |
524 | var _this$state2 = this.state,
525 | maxWidth = _this$state2.maxWidth,
526 | selectWidth = _this$state2.selectWidth;
527 | var _this$props5 = this.props,
528 | dropdownStyle = _this$props5.dropdownStyle,
529 | optionLabelProp = _this$props5.optionLabelProp,
530 | dropdownClassName = _this$props5.dropdownClassName,
531 | props = (0, _objectWithoutProperties2["default"])(_this$props5, ["dropdownStyle", "optionLabelProp", "dropdownClassName"]);
532 | this.allList = this.getUseChildrenList();
533 | this.allHeight = this.allList.length * this.ITEM_HEIGHT || 100;
534 |
535 | var _this$getStartAndEndI3 = this.getStartAndEndIndex(),
536 | startIndex = _this$getStartAndEndI3.startIndex,
537 | endIndex = _this$getStartAndEndI3.endIndex;
538 |
539 | var dynamicWidth = maxWidth;
540 |
541 | if (this.allList.length === 0 || maxWidth < selectWidth) {
542 | dynamicWidth = selectWidth;
543 | }
544 |
545 | dropdownStyle = _objectSpread({
546 | maxHeight: "".concat(DROPDOWN_HEIGHT, "px")
547 | }, dropdownStyle, {
548 | overflow: "auto",
549 | position: "relative",
550 | maxWidth: dynamicWidth
551 | });
552 | var value = this.state.value; // 判断处于 antd Form 中时不自动设置 value
553 |
554 | var _props = _objectSpread({}, props); // 先删除 value,再手动赋值,防止空 value 影响 placeholder
555 |
556 |
557 | delete _props.value; // value 为空字符会隐藏 placeholder,改为 undefined
558 |
559 | if (typeof value === "string" && !value) {
560 | _props.value = undefined;
561 | } else {
562 | _props.value = value;
563 | }
564 |
565 | optionLabelProp = optionLabelProp || "children";
566 | return _react["default"].createElement(_select["default"], (0, _extends2["default"])({}, _props, {
567 | id: this.id,
568 | onSearch: this.onSearch,
569 | onChange: this.onChange,
570 | dropdownClassName: "".concat(this.dropdownClassName, " ").concat(dropdownClassName || ""),
571 | optionLabelProp: optionLabelProp,
572 | dropdownStyle: dropdownStyle,
573 | onDropdownVisibleChange: this.onDropdownVisibleChange,
574 | onDeselect: this.onDeselect,
575 | ref: function ref(ele) {
576 | return _this3.select = ele;
577 | },
578 | dropdownRender: function dropdownRender(menu, props) {
579 | if (_this3.allList.length === 0) {
580 | return _react["default"].createElement("div", {
581 | style: {
582 | padding: "5px 12px"
583 | }
584 | }, _react["default"].createElement(_empty["default"], {
585 | image: _empty["default"].PRESENTED_IMAGE_SIMPLE
586 | }));
587 | }
588 |
589 | return _react["default"].createElement(_DropDownWrap["default"], (0, _extends2["default"])({
590 | startIndex: startIndex,
591 | endIndex: endIndex,
592 | allHeight: _this3.allHeight,
593 | menu: menu,
594 | itemHeight: _this3.ITEM_HEIGHT
595 | }, {
596 | ref: function ref(ele) {
597 | _this3.wrap = ele;
598 | }
599 | }));
600 | }
601 | }), this.allList);
602 | }
603 | }]);
604 | return SuperSelect;
605 | }(_react.PureComponent);
606 |
607 | SuperSelect.Option = Option;
608 | var _default = SuperSelect;
609 | exports["default"] = _default;
--------------------------------------------------------------------------------
/dist/bundle.js:
--------------------------------------------------------------------------------
1 | module.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=18)}([function(e,t){e.exports=require("react")},function(e,t){e.exports=require("codemirror/lib/codemirror")},function(e,t){e.exports=require("react-dom")},function(e,t,n){var r=n(12);"string"==typeof r&&(r=[[e.i,r,""]]);var o={hmr:!0,transform:void 0};n(14)(r,o);r.locals&&(e.exports=r.locals)},function(e,t){e.exports=require("universal-cookie")},function(e,t){e.exports=require("codemirror/mode/groovy/groovy")},function(e,t){e.exports=require("codemirror/mode/sql/sql")},function(e,t){e.exports=require("codemirror/lib/codemirror.css")},function(e,t){e.exports=require("codemirror/theme/3024-day.css")},function(e,t){e.exports=require("codemirror/theme/material.css")},function(e,t){e.exports=require("codemirror/addon/display/fullscreen.css")},function(e,t){e.exports=require("codemirror/addon/display/fullscreen.js")},function(e,t,n){(e.exports=n(13)(!1)).push([e.i,".tnt-codemirror {\n position: relative;\n overflow: hidden;\n border: #d7d7d7 solid 1px;\n}\n.tnt-codemirror .placeholder {\n pointer-events: none;\n position: absolute;\n top: 3px;\n left: 35px;\n font-size: 12px;\n color: #c6d0d3;\n opacity: 0.4;\n}\n.tnt-codemirror .codemirror-tip-day,\n.tnt-codemirror .codemirror-tip-night {\n position: fixed;\n left: 0;\n top: 0;\n z-index: 999;\n background: #fff;\n width: 200px;\n height: 200px;\n overflow: auto;\n box-shadow: rgba(119, 119, 119, 0.2) 0px 0px 7px, rgba(0, 0, 0, 0) 1px 1px 0px inset, rgba(0, 0, 0, 0) -1px -1px 0px inset;\n font-size: 12px;\n}\n.tnt-codemirror .codemirror-tip-day ul,\n.tnt-codemirror .codemirror-tip-night ul {\n margin: 0;\n padding: 0;\n overflow: auto;\n}\n.tnt-codemirror .codemirror-tip-day ul li,\n.tnt-codemirror .codemirror-tip-night ul li {\n list-style: none;\n cursor: pointer;\n padding: 0px 10px;\n height: 30px;\n line-height: 30px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.tnt-codemirror .codemirror-tip-day ul li sup,\n.tnt-codemirror .codemirror-tip-night ul li sup {\n padding-right: 5px;\n}\n.tnt-codemirror .codemirror-tip-day ul li:hover,\n.tnt-codemirror .codemirror-tip-night ul li:hover {\n background: #63acff;\n color: #fff;\n}\n.tnt-codemirror .codemirror-tip-day ul .cm-active,\n.tnt-codemirror .codemirror-tip-night ul .cm-active {\n background: #63acff;\n color: #fff;\n}\n.tnt-codemirror .cm-s-3024-day span.cm-field-keyword {\n color: #FF9800;\n}\n.tnt-codemirror .cm-s-3024-day span.cm-function-keyword {\n color: #03A9F4;\n}\n.tnt-codemirror .cm-s-3024-day span.cm-nomal-keyword {\n color: #F44336;\n border-bottom: #F44336 1px dotted;\n}\n.tnt-codemirror .cm-s-3024-day span.cm-boolean-keyword {\n color: #673AB7;\n}\n.tnt-codemirror .cm-s-3024-day span.cm-string {\n color: #cdab53;\n}\n.tnt-codemirror .cm-s-3024-day span.cm-comment {\n color: #9E9E9E;\n}\n.tnt-codemirror .cm-s-material span.cm-field-keyword {\n color: #FF9800;\n}\n.tnt-codemirror .cm-s-material span.cm-function-keyword {\n color: #03A9F4;\n}\n.tnt-codemirror .cm-s-material span.cm-nomal-keyword {\n color: #F44336;\n border-bottom: #F44336 1px dotted;\n}\n.tnt-codemirror .cm-s-material span.cm-boolean-keyword {\n color: #7689f3;\n}\n",""])},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var o=(c=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(c))))+" */"),i=r.sources.map(function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"});return[n].concat(i).concat([o]).join("\n")}var c;return[n].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},o=0;o=0&&s.splice(t,1)}function v(e){var t=document.createElement("style");return e.attrs.type="text/css",g(t,e.attrs),m(e,t),t}function g(e,t){Object.keys(t).forEach(function(n){e.setAttribute(n,t[n])})}function y(e,t){var n,r,o,i;if(t.transform&&e.css){if(!(i=t.transform(e.css)))return function(){};e.css=i}if(t.singleton){var c=l++;n=u||(u=v(t)),r=x.bind(null,n,c,!1),o=x.bind(null,n,c,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return e.attrs.type="text/css",e.attrs.rel="stylesheet",g(t,e.attrs),m(e,t),t}(t),r=function(e,t,n){var r=n.css,o=n.sourceMap,i=void 0===t.convertToAbsoluteUrls&&o;(t.convertToAbsoluteUrls||i)&&(r=f(r));o&&(r+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var c=new Blob([r],{type:"text/css"}),a=e.href;e.href=URL.createObjectURL(c),a&&URL.revokeObjectURL(a)}.bind(null,n,t),o=function(){h(n),n.href&&URL.revokeObjectURL(n.href)}):(n=v(t),r=function(e,t){var n=t.css,r=t.media;r&&e.setAttribute("media",r);if(e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}.bind(null,n),o=function(){h(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else o()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=c()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=p(e,t);return d(n,t),function(e){for(var r=[],o=0;o=","<=","!=","=",">","<","+","-","*","/","(",")",";",",",":","{","}"];return{startState:function(){return{inMultilineComment:!1}},token:function(n,r){if(n.eatSpace())return null;if(r.inMultilineComment){var o=n.pos;return n.skipTo("*"),n.pos!==o?(n.next(),"/"===n.peek()&&(n.next(),r.inMultilineComment=!1)):n.skipToEnd(),"comment"}if(n.match("//"))return n.skipToEnd(),"comment";if(n.match("/**")){r.inMultilineComment=!0;var i=n.pos;return n.skipTo("*"),n.pos!==i?(n.next(),"/"===n.peek()&&(n.next(),r.inMultilineComment=!1)):n.skipToEnd(),"comment"}for(var c=0;c=s){var r=Math.floor(Math.abs(t-n)/(s*h))*h,o=N();(r=r>o?o:r)!==window.currentIndex&&I(r,!0),x=t}if(t-x<=-s){var i=Math.floor((t-s)/(s*h))*h;I(i=i<=0?0:i,!1),x=t}}},j=function(){L(0,l),R(0),setTimeout(function(){T()},100)},L=function(e,t){var r=p({},O,f),i=JSON.parse(JSON.stringify(n));i=i.splice(e,t);var c=document.getElementById(m);c.style.display="none";for(var a=[],l=0;l0?8:7),!1===e&&(n=window.currentIndex/10%2>0?13:11),t&&t.children&&t.children[n]&&t.children[n].classList.add("cm-active"),t.children[t.children.length-1].classList.add("li-last");var r=t.querySelector(".".concat("li-first")),o=t.querySelector(".".concat("li-last"));v&&v.observe(r),v&&v.observe(o),b=r,w=o}},A=function(e){var t=C(e);I(t,e)},C=function(e){return e?y+h:y-h<0?0:y-h},N=function(){var e,t=a%l;return(e=0===t?a-l:t>=h?a-t:a-t-h)<=0?0:e},I=function(e,t){var n=N();if(t){if(e>n)return}else if(g===e)return;g=e,e===n?L(n,a-n):L(e,l),y=e,window.currentIndex=y,T(t),R(e)},R=function(e){var t=document.getElementById(m),n=h*s,r=(a-l)/h*n,o=e<=0?0:e/h*n,i=r-o<0?0:r-o;t.style.paddingTop="".concat(o,"px"),t.style.paddingBottom="".concat(i,"px")};return o.a.createElement("div",{className:"codemirror-tip-".concat(i," scroll-container"),style:p({},t),id:"scrollDiv"},o.a.createElement("ul",{className:"cm-field-ul box-ul",ref:function(e){return e},id:m}))};function v(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var x=n(16).js_beautify,O=function(e,t){return e.length>t.length?-1:e.length0&&void 0!==arguments[0]?arguments[0]:"formula-edit";return"".concat(e,"-").concat(E++)},j=Object(r.forwardRef)(function(e,t){var n=e.children,a=e.value,u=void 0===a?"":a,l=e.readOnly,s=void 0!==l&&l,f=e.theme,d=void 0===f?"night":f,p=e.mode,m=void 0===p?"defineScript":p,y=e.selectStyle,E=void 0===y?{width:"200px"}:y,j=e.lineNumber,L=void 0===j||j,T=e.indentUnit,A=void 0===T?2:T,C=e.regExp,N=void 0===C?"":C,I=e.isEndMark,R=e.height,k=void 0===R?300:R,M=e.fieldList,U=e.typeMap,B=void 0===U?{}:U,q=e.keyWords,D=void 0===q?["int","double","string","list","boolean","if","else","and","or","return"]:q,F=e.methodList,P=e.normalList,V=e.editorEvent,$=e.placeholder,_=e.cnCodeToEnExtraLogic,z=e.enCodeToCnExtraLogic,G=e.lang,H=e.searchCb,J=w(e,["children","value","readOnly","theme","mode","selectStyle","lineNumber","indentUnit","regExp","isEndMark","height","fieldList","typeMap","keyWords","methodList","normalList","editorEvent","placeholder","cnCodeToEnExtraLogic","enCodeToCnExtraLogic","lang","searchCb"]),K=b(Object(r.useState)({posLeft:0,posTop:0,tipShow:!1,tipShowType:null}),2),Y=K[0],W=K[1],Q=b(Object(r.useState)([]),2),X=Q[0],Z=Q[1],ee=Object(r.useMemo)(function(){if(!N&&(P||[]).length){var e=P.map(function(e){return e.name});return e=(e=e.map(function(e){return e.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&")})).join(""),"@[^\\+\\*\\/#%\\(\\),;\\!\\<\\>\\-=@".concat(e,"]*")}return"@[^\\+\\*\\/#%\\(\\),;\\!\\<\\>\\-=@]*"},[N,P]),te=Object(r.useRef)(),ne=Object(r.useRef)(),re=Object(r.useRef)(""),oe=Object(r.useRef)(""),ie=Object(r.useRef)(""),ce=Object(r.useRef)(""),ae=Object(r.useRef)(S()),ue=Object(r.useRef)(S("defineScript")),le=Object(r.useRef)(),se=Object(r.useRef)(),fe=Object(r.useRef)(),de="defineScript"===m?ue.current:m;Object(r.useEffect)(function(){"defineScript"===m&&c(ue.current,le)},[m]);var pe=Y.posLeft,me=Y.posTop,he=Y.tipShowType,ve=Y.tipShow,ge=function(){te.current.setOption("fullScreen",!te.current.getOption("fullScreen")),te.current.focus()},ye=function(){te.current.getOption("fullScreen")&&te.current.setOption("fullScreen",!1)};Object(r.useEffect)(function(){!function(){le.current={codemirrorFieldList:Oe(M||[],"@"),codemirrorMethodList:Oe(F||[],"#"),codemirrorNormalList:Oe(P||[],""),codemirrorKeywordList:D};var e=(M||[]).map(function(e){return"@".concat(e.name.replace(/\[/g,"\\[").replace(/\]/g,"\\]"))}),t=(F||[]).map(function(e){return"#".concat(e.name)}).sort(O),n=(P||[]).map(function(e){return e.name}).sort(O);re.current=new RegExp("(".concat(e.sort(O).join("|"),")")),oe.current=new RegExp("(".concat(t.join("|"),")")),ie.current=new RegExp("(".concat(t.join("|"),")"),"g");var r=n.map(function(e){return e.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&")});ce.current=new RegExp("(".concat(r.join("|"),")"),"g")}()},[M,F,P]),Object(r.useEffect)(function(){var e,t={};"night"===d&&(e="material"),"day"===d&&(e="3024-day"),"groovy"===de&&(t={mode:"text/x-groovy",indentUnit:A,extraKeys:{Tab:function(e){e.somethingSelected()?e.indentSelection("add"):e.replaceSelection(Array(e.getOption("indentUnit")+1).join(" "),"end","+input")},"Shift-Tab":function(e){if(e.somethingSelected())e.indentSelection("subtract");else{var t=e.getCursor();e.setCursor({line:t.line,ch:t.ch-e.getOption("indentUnit")})}},"Ctrl-F":function(e){var t=x(e.getValue(),{indent_size:A,indent_char:"1"===A?"\t":" "});te.current.setValue(t)}}}),te.current||(te.current=i.fromTextArea(ne.current,g({mode:de,theme:e,lineNumbers:L,lineWrapping:!0,readOnly:!!s&&"nocursor"},t,J)));var n="";return u&&(n=je(u)),"groovy"===de&&te.current.off("changes",we),te.current.setValue(n),te.current.setSize("auto",k),"groovy"===de&&te.current.on("changes",we),V&&V({codeEditor:te.current,fullScreen:ge,exitFullScreen:ye,EnCodeToCn:je,CnCodeToEn:Se}),function(){}},[]),Object(r.useEffect)(function(){te.current&&te.current.addKeyMap({Up:function(e){Ce("up",e)},Down:function(e){Ce("down",e)},Enter:function(e){Ce("enter",e)},Esc:function(){ye()},F9:function(){ge()}})},[ve]),Object(r.useEffect)(function(){if(!s&&te.current){var e=je(u);xe(e)}},[s,u]),Object(r.useEffect)(function(){te.current&&te.current.setSize("auto",k)},[k]);var be=function(t){var n=document.body.querySelector(".cm-nomal-keyword"),r=Se(t),o={cnCode:t,enCode:r,errorMsg:n?"存在错误代码":null};e.onChange(r,o)};se.current=function(t){if(e.onChange){var n=t.getValue();be(n)}};var we=Object(r.useCallback)(function(){if(se.current){for(var e=arguments.length,t=new Array(e),n=0;nt.name.length?-1:e.name.length-1&&(c=r.substring(i,t.ch));var a=n||{},u=a.left,l=a.top;if(E){var f=E.width?Number(E.width.replace("px","")):0,d=fe.current?fe.current.getBoundingClientRect():{},p=d.x,m=d.width;u+f>=p+m&&(u=u-f+(m-u)+p)}var h=fe.current.querySelector("#scrollDiv"),v=200;if(h&&(v=h.getBoundingClientRect().height),document.body.clientHeight-l0&&-1!==o&&o>i){var y=r.substring(o+1,t.ch);if(M.find(function(e){return e.name.includes(y)})){var b=g({},Y,{posLeft:u,posTop:l,tipShow:!0,tipShowType:"@"});W(b),Te(y,"@",c)}else W(g({},Y,{tipShow:!1,tipShowType:null}))}if(F&&F.length>0&&-1!==i&&i>o){var w=r.substring(i+1,t.ch);F.find(function(e){return e.name.includes(w)})?(W(g({},Y,{posLeft:u,posTop:l,tipShow:!0,tipShowType:"#"})),Te(w,"#",c)):W(g({},Y,{tipShow:!1,tipShowType:null}))}r.includes("@")||r.includes("#")||W(g({},Y,{tipShow:!1,tipShowType:null}))}},Te=function(e,t,n){var r=[],o="@"===t?M||[]:F||[];H&&(o=H({field:e,type:t,methodParamsInfo:n,fieldList:M,methodList:F,searchList:o,CnCodeToEn:Se,EnCodeToCn:je})),o&&o.length&&o.forEach(function(t){t.name.includes(e)&&r.push(t)}),Z(r)},Ae=function(e,t){var n=te.current.getCursor(),r=te.current.getLine(n.line),o=r.substring(0,n.ch).lastIndexOf(t,n.ch),i=r.substring(o,r.length),c=n.ch;if("@"===t||"#"===t){var a="@"===t?re.current:oe.current,u=i.match(a);u&&0===u.index&&(c=o+u[0].length)}te.current.setSelection({line:n.line,ch:o+1},{line:n.line,ch:c});var l="",s=0;l="@"===t?e.name+(I?"@":""):"#"===t&&r.length>c?e.name:e.value,"#"===t&&l.includes(",")&&(s=l.indexOf(",")-l.length),te.current.replaceSelection(l),te.current.setCursor(n.line,o+1+l.length+s),te.current.focus(),W(g({},Y,{tipShow:!1,tipShowType:null}))},Ce=function(e,t){if(!ve)return"up"===e?t.execCommand("goLineUp"):"down"===e?t.execCommand("goLineDown"):"enter"===e&&t.execCommand("newlineAndIndent"),!1;for(var n="cm-field-li",r="cm-active",o=document.getElementById(ae.current)||document.body,i=o.querySelectorAll(".".concat(n)),c=i.length,a=0,u=0;u {
22 | if (a.length > b.length) {
23 | return -1;
24 | }
25 | if (a.length < b.length) {
26 | return 1;
27 | }
28 | return 0;
29 | };
30 |
31 | let index = 0;
32 | const getId = (type = 'formula-edit') => `${type}-${index++}`;
33 |
34 | const FormulaEdit = forwardRef((props, ref) => {
35 | const {
36 | children,
37 | value = '',
38 | readOnly = false,
39 | theme = 'night',
40 | mode = 'defineScript',
41 | selectStyle = {
42 | width: '200px'
43 | },
44 | lineNumber = true,
45 | indentUnit = 2,
46 | regExp = '',
47 | isEndMark,
48 | height = 300,
49 | fieldList,
50 | typeMap = {},
51 | keyWords = ['int', 'double', 'string', 'list', 'boolean', 'if', 'else', 'and', 'or', 'return'],
52 | methodList,
53 | normalList,
54 | editorEvent,
55 | placeholder,
56 | cnCodeToEnExtraLogic,
57 | enCodeToCnExtraLogic,
58 | lang,
59 | searchCb,
60 | ...rest
61 | } = props;
62 |
63 | const [curState, setCurState] = useState({
64 | posLeft: 0,
65 | posTop: 0,
66 | tipShow: false,
67 | tipShowType: null
68 | });
69 | const [dropList, setDropList] = useState([]);
70 |
71 | // const [regExpState, setRegExpState] = useState('@[^\\+\\*\\/#%\\(\\),;\\!\\<\\>\\-=@]*');
72 | const regExpState = useMemo(() => {
73 | if (!regExp && (normalList || []).length) {
74 | let temp = normalList.map((res) => res.name);
75 | temp = temp.map((item) => item.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'));
76 | temp = temp.join('');
77 | return `@[^\\+\\*\\/#%\\(\\),;\\!\\<\\>\\-=@${temp}]*`;
78 | }
79 | return '@[^\\+\\*\\/#%\\(\\),;\\!\\<\\>\\-=@]*';
80 | }, [regExp, normalList]);
81 |
82 | const codeMirrorEditor = useRef();
83 | const textareaRef = useRef();
84 | const fieldRegExpRef = useRef('');
85 | const funRegExpRef = useRef('');
86 | const funRegExpGRef = useRef('');
87 | const normalExpGRef = useRef('');
88 | const domId = useRef(getId());
89 | const modeType = useRef(getId('defineScript'));
90 | const modeField = useRef();
91 | const eventRef = useRef();
92 |
93 | const tntCodeMirrorRef = useRef()
94 |
95 |
96 | const _mode = mode === 'defineScript' ? modeType.current : mode;
97 | useEffect(() => {
98 | if (mode === 'defineScript') {
99 | defineScript(modeType.current, modeField);
100 | }
101 | }, [mode]);
102 |
103 | const { posLeft, posTop, tipShowType, tipShow } = curState;
104 |
105 | // 全屏
106 | const fullScreen = () => {
107 | codeMirrorEditor.current.setOption('fullScreen', !codeMirrorEditor.current.getOption('fullScreen'));
108 | codeMirrorEditor.current.focus();
109 | };
110 |
111 | // 退出全屏
112 | const exitFullScreen = () => {
113 | if (codeMirrorEditor.current.getOption('fullScreen')) {
114 | codeMirrorEditor.current.setOption('fullScreen', false);
115 | }
116 | };
117 |
118 | const setLocalStorage = () => {
119 | // 字段存本地,供分词高亮使用
120 | modeField.current = {
121 | codemirrorFieldList: getLocalList(fieldList || [], '@'),
122 | codemirrorMethodList: getLocalList(methodList || [], '#'),
123 | codemirrorNormalList: getLocalList(normalList || [], ''),
124 | codemirrorKeywordList: keyWords
125 | };
126 | const fArr = (fieldList || []).map((item) => `@${item.name.replace(/\[/g, '\\[').replace(/\]/g, '\\]')}`);
127 | const mArr = (methodList || []).map((item) => `#${item.name}`).sort(sortBy);
128 | const nArr = (normalList || []).map((item) => item.name).sort(sortBy);
129 | // const keywords = [...mArr, ...nArr];
130 | fieldRegExpRef.current = new RegExp(`(${fArr.sort(sortBy).join('|')})`);
131 | funRegExpRef.current = new RegExp(`(${mArr.join('|')})`);
132 | funRegExpGRef.current = new RegExp(`(${mArr.join('|')})`, 'g');
133 | // 对特殊字符进行转移转译
134 | const escapedArr = nArr.map((item) => item.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'));
135 | normalExpGRef.current = new RegExp(`(${escapedArr.join('|')})`, 'g'); // normal 不能以引号开头
136 | // normalExpGRef.current = new RegExp(`(? {
140 | setLocalStorage();
141 | }, [fieldList, methodList, normalList]);
142 |
143 | useEffect(() => {
144 | let turnTheme;
145 | let ops = {};
146 | if (theme === 'night') turnTheme = 'material';
147 | if (theme === 'day') turnTheme = '3024-day';
148 | if (_mode === 'groovy') {
149 | ops = {
150 | mode: 'text/x-groovy',
151 | indentUnit,
152 | extraKeys: {
153 | Tab: (cm) => {
154 | if (cm.somethingSelected()) {
155 | // 存在文本选择
156 | cm.indentSelection('add'); // 正向缩进文本
157 | } else {
158 | // 无文本选择
159 | // cm.indentLine(cm.getCursor().line, "add"); // 整行缩进 不符合预期
160 | cm.replaceSelection(Array(cm.getOption('indentUnit') + 1).join(' '), 'end', '+input'); // 光标处插入 indentUnit 个空格
161 | }
162 | },
163 | 'Shift-Tab': (cm) => {
164 | // 反向缩进
165 | if (cm.somethingSelected()) {
166 | cm.indentSelection('subtract'); // 反向缩进
167 | } else {
168 | // cm.indentLine(cm.getCursor().line, "subtract"); // 直接缩进整行
169 | const cursor = cm.getCursor();
170 | cm.setCursor({ line: cursor.line, ch: cursor.ch - cm.getOption('indentUnit') }); // 光标回退 indexUnit 字符
171 | }
172 | },
173 | 'Ctrl-F': (cm) => {
174 | const formatValue = beautify_js(cm.getValue(), {
175 | indent_size: indentUnit,
176 | indent_char: indentUnit === '1' ? '\t' : ' '
177 | });
178 | codeMirrorEditor.current.setValue(formatValue);
179 | }
180 | }
181 | };
182 | }
183 |
184 | if (!codeMirrorEditor.current) {
185 | codeMirrorEditor.current = CodeMirror.fromTextArea(textareaRef.current, {
186 | mode: _mode,
187 | theme: turnTheme,
188 | lineNumbers: lineNumber,
189 | lineWrapping: true,
190 | readOnly: readOnly ? 'nocursor' : false,
191 | ...ops,
192 | ...rest
193 | });
194 | }
195 |
196 | let codeValue = '';
197 | if (value) codeValue = EnCodeToCn(value);
198 | if (_mode === 'groovy') {
199 | codeMirrorEditor.current.off('changes', editorChanges);
200 | }
201 | codeMirrorEditor.current.setValue(codeValue);
202 | codeMirrorEditor.current.setSize('auto', height);
203 | if (_mode === 'groovy') {
204 | codeMirrorEditor.current.on('changes', editorChanges);
205 | }
206 |
207 | editorEvent && editorEvent({ codeEditor: codeMirrorEditor.current, fullScreen, exitFullScreen, EnCodeToCn, CnCodeToEn });
208 |
209 | return () => {};
210 | }, []);
211 |
212 | useEffect(() => {
213 | if (codeMirrorEditor.current) {
214 | codeMirrorEditor.current.addKeyMap({
215 | Up: (cm) => {
216 | enterFuc('up', cm);
217 | },
218 | Down: (cm) => {
219 | enterFuc('down', cm);
220 | },
221 | Enter: (cm) => {
222 | enterFuc('enter', cm);
223 | },
224 | Esc: () => {
225 | exitFullScreen();
226 | },
227 | F9: () => {
228 | fullScreen();
229 | }
230 | });
231 | }
232 | }, [tipShow]);
233 |
234 | useEffect(() => {
235 | if (!readOnly && codeMirrorEditor.current) {
236 | let codeValue = EnCodeToCn(value);
237 | insertValue(codeValue);
238 | }
239 | }, [readOnly, value]);
240 |
241 | useEffect(() => {
242 | if (codeMirrorEditor.current) {
243 | codeMirrorEditor.current.setSize('auto', height);
244 | }
245 | }, [height]);
246 |
247 | const doChange = (cnCode) => {
248 | const errorKeyword = document.body.querySelector('.cm-nomal-keyword');
249 | let enCode = CnCodeToEn(cnCode);
250 | const data = {
251 | cnCode,
252 | enCode,
253 | errorMsg: errorKeyword ? '存在错误代码' : null
254 | };
255 | props.onChange(enCode, data);
256 | };
257 |
258 | eventRef.current = (cm) => {
259 | if (props.onChange) {
260 | const cnCode = cm.getValue();
261 | // 正则替换关键词
262 | doChange(cnCode);
263 | }
264 | };
265 |
266 | const editorChanges = useCallback((...args) => {
267 | if (eventRef.current) {
268 | eventRef.current.apply(null, args);
269 | }
270 | }, []);
271 |
272 | useEffect(() => {
273 | if (codeMirrorEditor.current && _mode !== 'groovy') {
274 | let codeValue = value;
275 | if (codeValue) codeValue = EnCodeToCn(codeValue);
276 | codeMirrorEditor.current.off('changes', editorChanges);
277 | codeMirrorEditor.current.setValue(codeValue);
278 | codeMirrorEditor.current.on('changes', editorChanges);
279 |
280 | codeMirrorEditor.current.on('cursorActivity', (cm) => {
281 | cursorActivity(cm);
282 | });
283 |
284 | codeMirrorEditor.current.on('focus', (cm) => {
285 | cursorActivity(cm);
286 | setCurState({
287 | ...curState
288 | });
289 | });
290 | }
291 | // 这里有异步问题
292 | // if (!regExp && (normalList || []).length) {
293 | // let temp = normalList.map((res) => res.name);
294 | // temp = temp.map((item) => item.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'));
295 | // temp = temp.join('');
296 | // setRegExpState(`@[^\\+\\*\\/#%\\(\\),;\\!\\<\\>\\-=@${temp}]*`);
297 | // }
298 | if ((fieldList || []).length || (methodList || []).length || (normalList || []).length) {
299 | const codeValue = EnCodeToCn(value);
300 | doChange(codeValue);
301 | }
302 | }, [fieldList, methodList, normalList]);
303 |
304 | const insertValue = (value) => {
305 | if (readOnly || !value) return;
306 | const getCursor = codeMirrorEditor.current.getCursor();
307 | const curLine = getCursor.ch;
308 | codeMirrorEditor.current.setCursor(getCursor.line, curLine);
309 | // codeMirrorEditor.current.focus();
310 | };
311 |
312 | const getLocalList = (list, type) => {
313 | const copyList = Object.assign([], list);
314 | // 排序,把长的放前面
315 | copyList.sort((a, b) => {
316 | if (a.name.length > b.name.length) {
317 | return -1;
318 | }
319 | if (a.name.length < b.name.length) {
320 | return 1;
321 | }
322 | return 0;
323 | });
324 | let codemirrorList = [];
325 | for (let i = 0; i < copyList.length; i++) {
326 | codemirrorList.push(`${type}${copyList[i].name}`);
327 | }
328 | return codemirrorList;
329 | };
330 |
331 | const matchLetter = (match) => {
332 | let turnStr = match.replace(/^\s*|\s*$/g, '');
333 |
334 | const leadingSpacesMatch = match.match(/^\s*/);
335 | let leadingSpaces = '';
336 | if (leadingSpacesMatch && leadingSpacesMatch.length) {
337 | leadingSpaces = leadingSpacesMatch[0];
338 | }
339 |
340 | const trailingSpacesMatch = match.match(/\s*$/);
341 | let trailingSpaces = '';
342 | if (trailingSpacesMatch && trailingSpacesMatch.length) {
343 | trailingSpaces = trailingSpacesMatch[0];
344 | }
345 | return {
346 | turnStr,
347 | leadingSpaces,
348 | trailingSpaces
349 | };
350 | };
351 |
352 | const CnCodeToEn = (cnCode) => {
353 | const reg = new RegExp(regExp || regExpState, 'g');
354 | let enCode = cnCode.replace(reg, (match) => {
355 | let { turnStr, leadingSpaces, trailingSpaces } = matchLetter(match);
356 |
357 | const fItem = (fieldList || []).find((item) => `@${item.name}` === turnStr);
358 | if (fItem) {
359 | turnStr = `@${fItem.value}`;
360 |
361 | if (cnCodeToEnExtraLogic) {
362 | const cnCodeTemp = cnCodeToEnExtraLogic(fItem);
363 | if (cnCodeTemp) {
364 | turnStr = cnCodeTemp + turnStr;
365 | }
366 | }
367 | }
368 | return leadingSpaces + turnStr + trailingSpaces;
369 | });
370 | enCode = enCode.replace(funRegExpGRef.current, (match) => {
371 | let turnStr = match;
372 | const mItem = (methodList || []).find((item) => `#${item.name}` === match);
373 | if (mItem) turnStr = `#${mItem.realValue}`;
374 | return turnStr;
375 | });
376 |
377 | enCode = enCode.replace(normalExpGRef.current, (match) => {
378 | let turnStr = match;
379 | const nItem = (normalList || []).find((item) => item.name === match);
380 | if (nItem) turnStr = nItem.value;
381 | return turnStr;
382 | });
383 | return enCode;
384 | };
385 |
386 | const EnCodeToCn = (enCode) => {
387 | const reg = new RegExp(regExp || regExpState, 'g');
388 | const mValueArr = (methodList || []).map((item) => `#${item.realValue}`);
389 | const nValueArr = (normalList || []).map((item) => item.value);
390 | const keywords = [...mValueArr, ...nValueArr].join('|');
391 | const curRegExp = new RegExp(`(${keywords})`, 'g');
392 | let cnCode = enCode.replace(reg, (match) => {
393 | let { turnStr, leadingSpaces, trailingSpaces } = matchLetter(match);
394 | const fItem = (fieldList || []).find((item) => `@${item.value}` === turnStr);
395 | if (fItem) turnStr = `@${fItem.name}`;
396 | return leadingSpaces + turnStr + trailingSpaces;
397 | });
398 |
399 | if (enCodeToCnExtraLogic) {
400 | const cnCodeTemp = enCodeToCnExtraLogic(cnCode);
401 | if (cnCodeTemp) {
402 | cnCode = cnCodeTemp;
403 | }
404 | }
405 |
406 | cnCode = cnCode.replace(curRegExp, (match) => {
407 | let turnStr = match;
408 | const mItem = (methodList || []).find((item) => `#${item.realValue}` === match);
409 | if (mItem) turnStr = `#${mItem.name}`;
410 | const nItem = (normalList || []).find((item) => item.value === match);
411 | if (nItem) turnStr = nItem.name;
412 | return turnStr;
413 | });
414 | return cnCode;
415 | };
416 |
417 | const cursorActivity = (cm) => {
418 | if (readOnly) return;
419 |
420 | const getCursor = cm.getCursor();
421 | const pos = cm.cursorCoords(getCursor);
422 | const getLineInfo = cm.getLine(getCursor.line);
423 | const cursorBeforeOneChar = getLineInfo.substring(0, getCursor.ch);
424 | const lastIndex = cursorBeforeOneChar.lastIndexOf('@', getCursor.ch);
425 | const lastIndex2 = cursorBeforeOneChar.lastIndexOf('#', getCursor.ch);
426 |
427 | let methodParamsInfo = '';
428 | if (lastIndex2 > -1) {
429 | methodParamsInfo = cursorBeforeOneChar.substring(lastIndex2, getCursor.ch);
430 | }
431 |
432 | let { left, top } = pos || {};
433 |
434 | if(selectStyle){
435 | const defaultWidth = selectStyle.width ? Number(selectStyle.width.replace("px",'')) : 0;
436 | const { x, width } = tntCodeMirrorRef.current ? tntCodeMirrorRef.current.getBoundingClientRect() : {};
437 |
438 | if(left + defaultWidth >= x + width){
439 | left = left - defaultWidth + ( width - left ) + x
440 | }
441 | }
442 |
443 |
444 | const scrollDiv = tntCodeMirrorRef.current.querySelector('#scrollDiv');
445 | let scrollDivHeight = 200
446 | if(scrollDiv){
447 | scrollDivHeight = scrollDiv.getBoundingClientRect().height;
448 | }
449 | if(document.body.clientHeight - top < scrollDivHeight){
450 | top = top - scrollDivHeight - 20
451 | }
452 | top = top + 20
453 |
454 |
455 | if (fieldList && fieldList.length > 0 && lastIndex !== -1 && lastIndex > lastIndex2) {
456 | // 监测@
457 | const content = cursorBeforeOneChar.substring(lastIndex + 1, getCursor.ch);
458 | const findObj = fieldList.find((item) => item.name.includes(content));
459 | if (findObj) {
460 | const temp = {
461 | ...curState,
462 | posLeft: left,
463 | posTop: top,
464 | tipShow: true,
465 | tipShowType: '@'
466 | };
467 | setCurState(temp);
468 | search(content, '@', methodParamsInfo);
469 | } else {
470 | setCurState({
471 | ...curState,
472 | tipShow: false,
473 | tipShowType: null
474 | });
475 | }
476 | }
477 | if (methodList && methodList.length > 0 && lastIndex2 !== -1 && lastIndex2 > lastIndex) {
478 | // 监测#
479 | const content = cursorBeforeOneChar.substring(lastIndex2 + 1, getCursor.ch);
480 | const findObj = methodList.find((item) => item.name.includes(content));
481 | if (findObj) {
482 | setCurState({
483 | ...curState,
484 | posLeft: left,
485 | posTop: top,
486 | tipShow: true,
487 | tipShowType: '#'
488 | });
489 | search(content, '#', methodParamsInfo);
490 | } else {
491 | setCurState({
492 | ...curState,
493 | tipShow: false,
494 | tipShowType: null
495 | });
496 | }
497 | }
498 | if (!cursorBeforeOneChar.includes('@') && !cursorBeforeOneChar.includes('#')) {
499 | setCurState({
500 | ...curState,
501 | tipShow: false,
502 | tipShowType: null
503 | });
504 | }
505 | };
506 |
507 | const search = (val, type, methodParamsInfo) => {
508 | let list = [];
509 | let searchList = type === '@' ? fieldList || [] : methodList || [];
510 | if (searchCb) {
511 | searchList = searchCb({ field: val, type, methodParamsInfo, fieldList, methodList, searchList, CnCodeToEn, EnCodeToCn });
512 | }
513 | if (searchList && searchList.length) {
514 | searchList.forEach((item) => {
515 | if (item.name.includes(val)) {
516 | list.push(item);
517 | }
518 | });
519 | }
520 | setDropList(list);
521 | };
522 |
523 | const handleClick = (item, type) => {
524 | const getCursor = codeMirrorEditor.current.getCursor(); // 焦点
525 | const getLineInfo = codeMirrorEditor.current.getLine(getCursor.line); // 当前行数据
526 | const cursorBeforeOneChar = getLineInfo.substring(0, getCursor.ch); // 起始到当前焦点数据
527 | const lastIndex = cursorBeforeOneChar.lastIndexOf(type, getCursor.ch); // 最后一个标记位置
528 | const cursorAfterOneChar = getLineInfo.substring(lastIndex, getLineInfo.length); // 最后一个标记到结尾数据
529 | let endIndex = getCursor.ch;
530 | if (type === '@' || type === '#') {
531 | const regExpRef = type === '@' ? fieldRegExpRef.current : funRegExpRef.current;
532 | const match = cursorAfterOneChar.match(regExpRef);
533 | if (match && match.index === 0) {
534 | // 第一个匹配的
535 | endIndex = lastIndex + match[0].length;
536 | }
537 | }
538 | codeMirrorEditor.current.setSelection({ line: getCursor.line, ch: lastIndex + 1 }, { line: getCursor.line, ch: endIndex });
539 | let content = '';
540 | let offsetIndex = 0;
541 | if (type === '@') {
542 | content = item.name + (isEndMark ? '@' : '');
543 | // 好像不通用,暂时隐藏
544 | // if ([',', ')'].includes(getLineInfo[endIndex])) {
545 | // // 如果后面是逗号或者括号,聚焦到后面
546 | // offsetIndex = 1;
547 | // }
548 | } else if (type === '#' && getLineInfo.length > endIndex) {
549 | content = item.name;
550 | } else {
551 | content = item.value;
552 | }
553 | if (type === '#' && content.includes(',')) {
554 | // 函数聚焦到逗号前面
555 | offsetIndex = content.indexOf(',') - content.length;
556 | }
557 | codeMirrorEditor.current.replaceSelection(content);
558 | codeMirrorEditor.current.setCursor(getCursor.line, lastIndex + 1 + content.length + offsetIndex);
559 | codeMirrorEditor.current.focus();
560 | setCurState({
561 | ...curState,
562 | tipShow: false,
563 | tipShowType: null
564 | });
565 | };
566 |
567 | const enterFuc = (type, cm) => {
568 | if (!tipShow) {
569 | if (type === 'up') {
570 | cm.execCommand('goLineUp');
571 | } else if (type === 'down') {
572 | cm.execCommand('goLineDown');
573 | } else if (type === 'enter') {
574 | cm.execCommand('newlineAndIndent');
575 | }
576 | return false;
577 | }
578 | let findLi = 'cm-field-li';
579 | let active = 'cm-active';
580 | const rootDom = document.getElementById(domId.current) || document.body;
581 | const nodeList = rootDom.querySelectorAll(`.${findLi}`);
582 | const length = nodeList.length;
583 | let index = 0;
584 | let hasActive = false;
585 | for (let i = 0; i < length; i++) {
586 | if (nodeList[i].className.includes(active)) {
587 | index = i;
588 | hasActive = true;
589 | }
590 | }
591 | if (type === 'up') {
592 | nodeList[index].className = findLi;
593 | if (index === 0) {
594 | nodeList[0].className = `${active} ${findLi}`;
595 | } else {
596 | nodeList[index - 1].className = `${active} ${findLi}`;
597 | }
598 | rootDom.querySelector(`.${active}`).scrollIntoViewIfNeeded();
599 | } else if (type === 'down') {
600 | nodeList[index].setAttribute('class', findLi);
601 | if (index === length - 1) {
602 | nodeList[index].setAttribute('class', `${active} ${findLi}`);
603 | } else {
604 | nodeList[index + 1].setAttribute('class', `${active} ${findLi}`);
605 | }
606 | rootDom.querySelector(`.${active}`).scrollIntoViewIfNeeded();
607 | } else if (type === 'enter') {
608 | let node = document.querySelector(`.${active}`);
609 | if (!(node.attributes && node.attributes.data && node.attributes.data.value)) {
610 | node = nodeList[index];
611 | }
612 | handleClick(
613 | {
614 | name: node.title,
615 | value: node.attributes.data.value
616 | },
617 | tipShowType
618 | );
619 | setTimeout(() => {
620 | setCurState({
621 | ...curState,
622 | tipShow: false,
623 | tipShowType: null
624 | });
625 | }, 100);
626 | }
627 | };
628 |
629 | return (
630 |
631 | {children}
632 |
633 | {placeholder && !value &&
{placeholder}}
634 |
635 | {/* @弹框 */}
636 | {tipShow ? (
637 |
{
645 | handleClick(item, tipShowType);
646 | }}
647 | style={{
648 | left: `${posLeft}px`,
649 | top: `${posTop}px`,
650 | ...selectStyle
651 | }}
652 | lang={lang}
653 | domId={domId.current}
654 | />
655 | ) : (
656 | ''
657 | )}
658 |
659 | );
660 | });
661 |
662 | export default FormulaEdit;
663 |
--------------------------------------------------------------------------------