├── 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 | ![Image text](https://github.com/bruceliu68/formulaEdit-react/blob/master/src/img/pic1.png) 89 | ![Image text](https://github.com/bruceliu68/formulaEdit-react/blob/master/src/img/pic2.png) 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 |
    231 |
      (box = e)} id={domId} /> 232 |
    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 |