{"_id":"l-formula-editor","name":"l-formula-editor","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"type":"module","name":"l-formula-editor","version":"1.0.0","description":"公式编辑器、代码编辑器，支持sql,html,js,json,customScript","main":"dist/index.js","types":"types/index.d.ts","scripts":{"build":"rollup -c"},"keywords":["sql","html","javascript","json","代码编辑器","公式编辑器","codemirror","react","typescript","antd"],"author":{"name":"Lucius"},"license":"ISC","dependencies":{"@babel/core":"^7.13.15","@babel/preset-env":"^7.13.15","@babel/preset-react":"^7.13.13","@codemirror/lang-html":"^6.4.9","@codemirror/lang-javascript":"^6.2.2","@codemirror/lang-json":"^6.0.1","@codemirror/lang-sql":"^6.6.4","@lezer/highlight":"^1.2.0","@rollup/plugin-babel":"^5.3.0","@rollup/plugin-commonjs":"^19.0.0","@rollup/plugin-node-resolve":"^13.0.5","@rollup/plugin-typescript":"^8.2.1","@types/react-dom":"^18.3.0","antd":"3","autoprefixer":"^10.4.19","babel-plugin-import":"^1.13.8","codemirror":"^6.0.1","less":"^4.2.0","less-loader":"^12.2.0","react":"16","react-dom":"16","rollup":"^2.52.8","rollup-plugin-postcss":"^4.0.2","rollup-plugin-terser":"7.0.2","typescript":"^4.2.4"},"devDependencies":{"@rollup/plugin-node-resolve":"^15.2.3","postcss":"^8.4.38"},"publishConfig":{"access":"public"},"gitHead":"73373bec366803dd018ef5c947dda0fbece1640b","_id":"l-formula-editor@1.0.0","_nodeVersion":"14.19.3","_npmVersion":"6.14.17","_npmUser":{"name":"itwarm","email":"1738736002@qq.com"},"dist":{"integrity":"sha512-dLWq/XRyX2LjoYLnTbTIv0CqAG2NobQU7qoFE9X6Aqey/YEnvaQ8wbbOabnFRpDCmFpMB2sz0uMhdSoSB8z0rg==","shasum":"35558e1bd52c3b315868998c50735110e9aafa29","tarball":"https://registry.npmjs.org/l-formula-editor/-/l-formula-editor-1.0.0.tgz","fileCount":61,"unpackedSize":743037,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZHYzgG2W8lvTSIBJngOIC3iZyLy1LerELrHqVP9CoWwIgSS5+2Eub/gP1bXkWKb9grY0eolRpFaBZsFb4nCaCU7E="}]},"directories":{},"maintainers":[{"name":"itwarm","email":"1738736002@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/l-formula-editor_1.0.0_1718069707254_0.12879712733354887"},"_hasShrinkwrap":false}},"time":{"created":"2024-06-11T01:35:07.147Z","1.0.0":"2024-06-11T01:35:07.415Z","modified":"2024-06-11T01:35:07.670Z"},"maintainers":[{"name":"itwarm","email":"1738736002@qq.com"}],"description":"公式编辑器、代码编辑器，支持sql,html,js,json,customScript","keywords":["sql","html","javascript","json","代码编辑器","公式编辑器","codemirror","react","typescript","antd"],"author":{"name":"Lucius"},"license":"ISC","readme":"## 公式编辑器、代码编辑器，支持sql,html,js,json,customScript\r\n\r\n---\r\ntitle: 代码/公式编辑器；\r\norder: 11\r\n\r\nnav:\r\n    title: 组件\r\n    order: 1\r\n    path: /components\r\n\r\ngroup:\r\n    title: 基本元素控件\r\n    order: 1,\r\n    path: \"/basic\"\r\ndemo:\r\n    tocDepth: 4\r\n---\r\n\r\n## 介绍\r\n\r\n-   技术栈：`react:^16.14.0`, `typescript` `less` `codemirror 6.x`\r\n-   目标：实现一个编辑器组件，支持`javaScript`，`json`，`html`，`sql`，以及自定义的公式编辑器`customScript`；\r\n-   实现：基于`codemirror`二次封装实现；[文档地址](https://codemirror.net/)\r\n-   优势：\r\n    Codemirror6 和 Codemirror5 的主要区别体现在以下几个方面：\r\n\r\n    -   架构：CM6 是对 CM5 进行了全面重写和重构的版本，采用了新的架构。CM6 的设计目标是提供更好的可扩展性、模块化和定制性，以适应不同的编辑器需求。\r\n    -   模块化：CM6 引入了模块化的概念，使得编辑器的功能可以以更灵活的方式组织和扩展。它提供了一套核心模块，以及可选的插件和扩展模块，使开发人员能够根据自己的需求选择和组合功能。\r\n    -   插件系统：CM6 的插件系统更加强大和灵活。它采用了新的基于状态和触发器的机制，使得插件能够更好地与编辑器交互并响应变化。这使得开发人员能够创建更复杂和定制化的编辑器功能。\r\n    -   渲染方式：CM6 使用新的渲染引擎，采用了虚拟 DOM 的概念，以提高性能和响应能力。它还支持可选的受限制的线性渲染模式，可以在大型文档上提供更好的性能。\r\n\r\n-   使用的插件集合\r\n    ```json\r\n      \"@codemirror/lang-html\": \"^6.4.9\",\r\n      \"@codemirror/lang-javascript\": \"^6.2.2\",\r\n      \"@codemirror/lang-json\": \"^6.0.1\",\r\n      \"@codemirror/lang-sql\": \"^6.6.4\",\r\n      \"@lezer/highlight\": \"^1.2.0\",\r\n      \"codemirror\": ^6.0.1;\r\n      \"antd\": \"3.x\"\r\n    ```\r\n\r\n## Demo\r\n基础场景下使用\r\n```tsx\r\nimport React from \"react\";\r\nimport { Radio } from \"antd\";\r\nimport { HqCodemirror } from \"hquipro\";\r\nimport { parseToFunction } from \"./utils\";\r\nimport \"./style/index.less\";\r\nimport { variables, functionList, completions, hintPaths } from \"./mockData.ts\";\r\nconst { useState } = React;\r\nexport default () => {\r\n    const [mode, setMode] = useState<\"json\" | \"javascript\" | \"sql\" | \"html\" | \"customScript\">(\"customScript\");\r\n    const placeholderThemeFiled = \"id\";\r\n    const placeholderThemes = {\r\n        [placeholderThemeFiled]: {\r\n            textColor: \"#d46b08\",\r\n            backgroudColor: \"#fff7e6\",\r\n            borderColor: \"#ffd591\"\r\n        }\r\n    };\r\n    const changeRadio = (e: any) => {\r\n        setMode(e.target.value);\r\n    };\r\n    return (\r\n      <div>\r\n        <Radio.Group onChange={changeRadio} value={mode} buttonStyle=\"solid\">\r\n            <Radio.Button value=\"json\">JSON</Radio.Button>\r\n            <Radio.Button value=\"javascript\">JavaScript</Radio.Button>\r\n            <Radio.Button value=\"sql\">SQL</Radio.Button>\r\n            <Radio.Button value=\"html\">HTML</Radio.Button>\r\n            <Radio.Button value=\"customScript\">自定义脚本</Radio.Button>\r\n        </Radio.Group>\r\n        <HqCodemirror\r\n            completions={completions}\r\n            mode={mode}\r\n            variables={variables}\r\n            withSelect={mode === \"customScript\"}\r\n            height={180}\r\n            showTool={true}\r\n            showOutLine={false}\r\n            defaultValue={\"SUM([[id.常量:var.姓名:name]],[[id.常量:var.年龄:age]],[[id.常量:var.年龄2:age2]], [[id.哈哈哈:ha]])\"}\r\n            hintPaths={hintPaths}\r\n            placeholderThemes={placeholderThemes}\r\n            placeholderThemeFiled={placeholderThemeFiled}\r\n            keywordsColor=\"#081cd4\"\r\n            keywords={[\"let\", \"const\", \"wen\", \"if\", \"else\", \"for\", \"while\", \"switch\"]}\r\n            functions={functionList}\r\n            onChange={(data: any) => {\r\n                const res = parseToFunction({\r\n                    codeStr: data,\r\n                    placeholderThemeFiled,\r\n                    functions: functionList,\r\n                    variables\r\n                });\r\n                console.log(\"func\", res);\r\n                if (res) {\r\n                    const { func, funcs, data, funIng } = res;\r\n                    console.log('res',funIng())\r\n                    console.log(\"res\", func(funcs, data));\r\n                }\r\n            }}\r\n        />\r\n      </div>\r\n    );\r\n};\r\n```\r\n\r\n在 sql 中使用\r\n\r\n```tsx\r\nimport React from \"react\";\r\nimport { HqCodemirror } from \"hquipro\";\r\nimport \"./style/index.less\";\r\nimport { variables, functionSqlList, tablesData } from \"./mockData.ts\";\r\nconst { useState } = React;\r\nexport default () => {\r\n    const placeholderThemeFiled = 'sql'\r\n    const placeholderThemes = {\r\n        [placeholderThemeFiled]: {\r\n            textColor: \"#4284D3\",\r\n            backgroudColor: \"rgba(66,132,211,0.1)\",\r\n        }\r\n    };\r\n    return (\r\n        <div>\r\n            <HqCodemirror\r\n                mode={\"sql\"}\r\n                variables={variables}\r\n                withSelect={true}\r\n                showHeader={false}\r\n                actionBoxHeight={\"180px\"}\r\n                height={120}\r\n                defaultValue={\"单行输入框\"}\r\n                placeholderThemeFiled={placeholderThemeFiled}\r\n                placeholderThemes={placeholderThemes}\r\n                showOutLine={false}\r\n                tables={tablesData}\r\n                functions={functionSqlList}\r\n                onChange={(data: any) => {\r\n                    console.log(data);\r\n                }}\r\n            />\r\n        </div>\r\n    );\r\n};\r\n```\r\n\r\n## API\r\n\r\n| 属性　       | 描述　                                           | 　值                                                     | 　备注                     |\r\n| ------------ | ------------------------------------------------ | -------------------------------------------------------- | -------------------------- |\r\n| defaultValue | 文档的默认值  | string        |            |\r\n| mode         | 编辑器支持的语言类型       | \"json\" / \"javascript\" / \"sql\" / \"html\" / \"customScript\"; | 必填      |\r\n| trigger      | 触发长 change 的方式， blur 触发还是 change 触发 | \"blur\"/\"change\" | 默认值 blur |\r\n| editable     | 编辑器是否可编辑    | Boolean    | true       |\r\n| showOutLine | 是否focus聚焦时显示外层的虚线边框 | Boolean    | 默认值true       |\r\n| height       | 编辑器的高度      | Number    |       |\r\n| minHeight    | 编辑器的最小高度          | Number    |         |\r\n| placeholder  | 编辑器的 placeholder    | string / HTMLElement    |       |\r\n| tables       | mode = sql 时，注入表格的一些字段到 sql 语言库中 | ITableSchema   |       |\r\n| extensions   | codemirror 的扩展配置    | Extension[] | 如果现有配置不满足可以覆盖 |\r\n| themeStyle | 对于codemirror View.them的扩展，会覆盖掉hqcodemirror的一些默认配置 | 对象格式{[K: string]: {} } | eg：{\".cm-content\": {minHeight: `80px`} } |\r\n| onChange     | change 回调函数                                  | (value: string, rely?: any) => void                      |                            |\r\n\r\n**自定义的情况下（mode=customScript|sql）,会用到的api**\r\n| 属性 | 描述 | 值 | 备注 |\r\n|-----|-----|-----|-----|\r\n| withSelect|是否展示选择区域|Boolean| 默认值 false |\r\n| actionBoxHeight | 选择区域的最大高度 | String | 200px |\r\n| showHeader | 编译器是否需要展示头部 | Boolean | true |\r\n| showTool | 是否展示工具栏,当且showHeader=true时生效 | Boolean | false |\r\n| customTool | 自定义工具栏，当showHeader=true&showTool=false生效 | React.ReactNode/string | |\r\n| renderHeader | 头部左边区域的内容过可自定义 | React.ReactNode/string | 默认值“公式=” |\r\n| keywordsClassName | 给关键字 Dom 添加的 className | | |\r\n| keywords | 关键字集合，没需要可不传 | String[] | [] |\r\n| keywordsColor | 关键字的颜色值 | 色值 | |\r\n| functions | 自定义函数集合，可用于自动提示，或者操作区域的可选函数 | [FunctionType](####1.函数的定义)[] | |\r\n| variables | 外部的一些变量,可用于自动提示，或者操作区域的可选变量 | [variablesModel](####2.变量)[] | |\r\n| hintPaths | 设置一些含有子集的对象，来激活自动匹配的，例如对象 a:{age:{b:1}}, 输入 a.会弹出可选项 age,输入 a.age.同样弹出 b 可选项； | [HintPathType](####5.Hint链式)[] | |\r\n| completions | 设置自动匹配的值，这里做了优化，变量 variables 和函数 functions 的值会被自动合到这个数组里； | [CompletionsType](####3.completions定义)[] | |\r\n| placeholderThemes | 定义变量的样式 | PlaceholderThemesType | |\r\n| placeholderThemeFiled| 定义变量的 filed, 主要用于最后解析显示用 | String | |\r\n| clickDebug | 调试按钮的回调函数，返回[IParseFunctionResult](####6自定义函数的解析结果) | clickDebug?: (result: IParseFunctionResult) => void; | |\r\n\r\n**组件向外暴露的方法**\r\n\r\n> 通过 ref 获取组件实例，可以调用以下方法\r\n\r\n1. insertText: 插入文本\r\n    - 参数：text: string; isTemplate: boolean;\r\n    - 作用：插入文本到编辑器中，isTemplate 为 true 时，会将 text 包裹在模板字符串中\r\n2. clearText: 清空文本\r\n    - 作用：清空编辑器中的文本\r\n3. setText: 设置文本\r\n    - 参数：text: string;\r\n    - 作用：设置编辑器中的文本\r\n4. getValue: 获取编辑器中的文本内容； - 作用：getValue();\r\n\r\n**解析方法**\r\n> parseToFunction: 用在 customScript 的解析;\r\n```\r\nimport {parseToFunction} from \"hquipro/es/hq-codemirror/utils\";\r\nconst res = parseToFunction({codeStr: data, placeholderThemeFiled, functions: functionList, variables});\r\nconst {func, funcs, data, formatResult} = res;\r\n```\r\n\r\n## 数据说明\r\n\r\n### 数据结构\r\n\r\n#### 1.函数的定义\r\n\r\n```ts\r\ninterface FunctionType {\r\n    template: string;\r\n    label: string;\r\n    detail: string;\r\n    type: string; // 类型 function\r\n    parent?: any;\r\n    handle?: any; // 函数的具体实现方法\r\n    returnType?: string; // 函数的返回值类型\r\n    briefly?: string; // 函数的简要描述\r\n    paramNum?: number; // 函数的参数个数，-1表示多个参数。大于等于1个\r\n    example?: string; // 函数的使用示例\r\n    instruction?: string; // 函数的详细说明\r\n    children?: FunctionType[];\r\n}\r\n\r\n// data\r\nconst functionList: FunctionType[] = [\r\n    {\r\n        label: \"基础函数\",\r\n        detail: \"基础函数\",\r\n        template: \"基础函数\",\r\n        paramNum: 0,\r\n        type: \"groupName\",\r\n        children: [\r\n            {\r\n                label: \"sum1\",\r\n                detail: \"求和函数,求和函数，把所有参数加一起返回\",\r\n                template: \"func.sum1(${1})\",\r\n                paramNum: -1, // 表示多个参数。大于等于1个\r\n                type: \"function\",\r\n                returnType: \"number\",\r\n                instruction: \"该求和方法需要传递数值变量, 参数可以为多个\",\r\n                example: \"sum1(1,2,3)，计算结果为6\",\r\n                briefly: \"求和函数\",\r\n                handle: `(...args) => {\r\n                    return args.reduce((prev, cur) => {\r\n                      return prev + +cur;\r\n                    }, 0);\r\n                }`\r\n            }\r\n        ]\r\n    }\r\n];\r\n```\r\n\r\n#### 2.变量\r\n\r\n```ts\r\ninterface variablesModel {\r\n  code: string;\r\n  name: string;  // 名称\r\n  children?: variablesModel[];\r\n  type: \"model\" | \"field\"; // 区分 分组和字段\r\n  value?: any;\r\n  fieldType?: string;  // 字段的类型\r\n}\r\nconst variables: any[] = [\r\n    {\r\n      code: \"var\",\r\n      name: \"常量\",\r\n      type: \"model\",\r\n      children: [\r\n        {\r\n          name: \"userId\",\r\n          code: \"id\",\r\n          type: \"field\",\r\n          fieldType: \"Number\",\r\n          value: 1,\r\n        },\r\n        ...\r\n      ]\r\n    }\r\n]\r\n```\r\n\r\n#### 3.completions 定义\r\n\r\n> 输入提示的集合；在自定义时生效；上面的 functions 和 variables 在程序内部会自动注入的；\r\n\r\n```ts\r\ninterface CompletionsType {\r\n    template: string;\r\n    label: string;\r\n    detail: string;\r\n    type: string;\r\n    parent?: any;\r\n}\r\nconst completions: CompletionsType[] = [\r\n    {\r\n        label: \"let\",\r\n        type: \"variable\",\r\n        detail: \"定义变量let\",\r\n        template: \"let ${1:variable} = ${2:value};\" // 模板形式注入\r\n    }\r\n];\r\n```\r\n\r\n#### 4.keywords 传入字符串数组\r\n\r\n```ts\r\nconst keywords = [\"a\", \"switch\", \"b\"];\r\n```\r\n\r\n#### 5.Hint 链式\r\n\r\n> 支持输入 a.b.c 的提示输入\r\n\r\n```ts\r\ninterface HintPathType {\r\n    label: string;\r\n    detail: string;\r\n    type: \"function\" | \"keyword\" | \"variable\" | \"text\" | \"property\";\r\n    template: string;\r\n    children?: HintPathType[];\r\n}\r\nconst data = [\r\n    {\r\n        label: \"user\",\r\n        template: \"user\",\r\n        detail: \"用户\",\r\n        type: \"variable\",\r\n        children: [\r\n            {\r\n                label: \"department\",\r\n                template: \"department\",\r\n                detail: \"部门\",\r\n                type: \"property\",\r\n                children: [\r\n                    {\r\n                        label: \"name\",\r\n                        template: \"name\",\r\n                        detail: \"名称\",\r\n                        type: \"property\"\r\n                    }\r\n                ]\r\n            }\r\n        ]\r\n    }\r\n];\r\n```\r\n\r\n#### 6.自定义函数的解析结果\r\n\r\n```ts\r\ninterface IParseFunctionResult {\r\n    funIng: Function; // 可直接用来执行的函数；\r\n    func: Function;\r\n    funcs: any[];\r\n    data: any;\r\n    formatResult: string;\r\n}\r\n```\r\n\r\n#### 变量主题色\r\n\r\n```ts\r\nplaceholderThemeFiled: string;\r\ninterface PlaceholderThemesType {\r\n  [placeholderThemeFiled]: CommonPlaceholderTheme;\r\n}\r\ninterface CommonPlaceholderTheme {\r\n  textColor: string;\r\n  backgroudColor: string;\r\n}\r\n```\r\n\r\n\r\n### 类型Map\r\n```ts\r\nenum typeMap = {\r\n    \"string\": \"字符串\",\r\n    \"number\": \"数字\",\r\n    \"boolean\": \"布尔\",\r\n    \"object\": \"对象\",\r\n    \"array\": \"数组\",\r\n    \"date\": \"日期\",\r\n    \"function\": \"函数\",\r\n    \"null\": \"空\",\r\n    \"undefined\": \"未定义\",\r\n    \"symbol\": \"符号\",\r\n    \"bigInt\": \"大整数\",\r\n    \"regExp\": \"正则表达式\",\r\n    \"error\": \"错误对象\",\r\n    \"promise\": \"Promise对象\",\r\n    \"timeStamp\": \"时间\",\r\n    'decimal': \"数字\",\r\n    'percentage':'数字',\r\n    'integer': \"数字\",\r\n    'multiOption': '字符串',\r\n    'select': \"字符串\"\r\n};\r\n```\r\n\r\n### 输出是怎么样的？\r\n\r\n输出是一个字符串形式，现在的设计是，通过保存输出字符串，在使用的地方在进行解析执行；\r\n\r\n```\r\n// 自动选择的情况下：求和函数sum1，年龄求和\r\nfunc.sum1([[id.常量:var.年龄:age]] ,[[id.常量:var.年龄:age]] )\r\n// 手动输入的情况下：\r\nsum1(1,2,3)\r\n```\r\n\r\n### 怎么解析执行的？\r\n\r\n组件提供了统一的解析方法，只需要传入参数，即可获取返回的函数结果；\r\n\r\n```ts\r\nconst res = parseToFunction({ codeStr: data, placeholderThemeFiled, functions: functionList, variables });\r\nconst { funIng, func, funcs, data } = res;\r\n// 方式一：func是主体函数， funcs是对应的函数集合，data是对应的数据\r\nfunc(funcs, data);\r\n// 方式一：或者直接执行funIng；funIng内部实现了方式一的执行过程；\r\nfunIng();\r\n```\r\n实现：通过 new window.Function('func', 'data', `return ${result}`);\r\nresult即为`FunctionType`中的`handle`；\r\n\r\n### 普通的加减运算也是可以直接执行的\r\n\r\n> 符合基础的 JavaScript 的运算规则；\r\n\r\n```\r\n// 插入的变量\r\n[[id.常量:var.年龄:age]] + [[id.常量:var.年龄:age]]\r\n输出: 36\r\n// 简单的计算:+ - * / %\r\n12 + 34\r\n输出: 46\r\n// 字符串的拼接\r\n\"hello\" + \" world\"\r\n输出: \"hello world\"\r\n// Boolean的运算\r\ntrue && false\r\n输出: false\r\n34>=13 | 34<=32 ...\r\n45!==21 | 23!=122 ...\r\n\r\n```\r\n","readmeFilename":"README.md"}