react-monaco-editor编辑器安装使用&自定义提示


2021/02/28更
此次更新主要增加了给编辑器鼠标焦点处设置值方法

  /**
     * 给编辑器设置值
     * @param value 需要设置的值
     */
    const handleSetEditorVal = (value: string): void => {
      if (!value) return;
      // 为所选取的值赋值到编辑器中
      if (editorInstance.current && value) {
        const selection = editorInstance?.current?.getSelection?.();
        const range = new _monaco.Range(
          selection.startLineNumber,
          selection.startColumn,
          selection.endLineNumber,
          selection.endColumn
        );
        const id = { major: 1, minor: 1 };
        const op = { identifier: id, range, text: value, forceMoveMarkers: true };
        editorInstance.current.executeEdits('', [op]);
        editorInstance.current.focus();
      }
    };

开始

最近产品上用到了代码编辑器,因为技术栈使用的是react, 找了许久经过甄选最后决定使用monaco-editor的react封装版本:react-monaco-edotor,下面是他的简单介绍:


image.png

经过很多前辈大佬的努力,react-monaco-edotor横空出世,对于使用react技术栈的人是一个再好不过的好消息了,介绍完毕我们讲讲如何使用吧。

使用方法:

yarn add monaco-editor -D 
yarn add react-monaco-editor -D 
yarn add monaco-editor-webpack-plugin -D 

安装好依赖包再去webpack-config中配置相关依赖:

const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
...
 plugins: [
      new MonacoWebpackPlugin(['apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell', 
'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']),
...
]

注意:自定义提示时一定需设置 language: 'plaintext' 自定义文本, 并且在monaco.languages.registerCompletionItemProvider中使用深拷贝
经过以上步骤 编辑器就能正常应用到项目中去了,使用及自定义提示的使用方法:

/*
 * @filename:
 * @Date: 2020-05-26 15:11:32
 * @Author: 汪渊  
 * @version: 1.0.0
 * @copyright: copyright: HAND ® 2020
 */
import React, { useRef, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Icon, Tooltip } from 'choerodon-ui/pro';
import MonacoEditor from 'react-monaco-editor';
import * as _monaco from 'monaco-editor';

import styles from './index.less';

let provider = {
  dispose: () => {},
};
interface IRightContent {
  currentRecord: any; // fixme
  handleCheck: () => Promise;
  secondRightData: (model.dataSource.BaseDataSourceField | model.dataSource.BaseDataSourceHeader)[];
}
export default forwardRef(
  ({ currentRecord, handleCheck, secondRightData = [] }: IRightContent, ref) => {
    // 编辑器实例
    const editorInstance = useRef();

    // 真实数据
    const code: any = useRef(currentRecord ? currentRecord.formulaContent : '');

    // 缓存数据
    const cache: any = useRef(code.current);

    // 输入引号触发页面刷新
    const [refresh, setRefresh] = React.useState(false);

    const monacoInstance: any = useRef();
    const options = {
      selectOnLineNumbers: true,
      renderSideBySide: false,
    };

    useImperativeHandle(ref, () => ({
      handleSetEditorVal,
      getEditorData: () => cache.current,
    }));

    useEffect(
      () => () => {
        provider.dispose(); // 弹窗关闭后 销毁编辑器实例
      },
      []
    );

    /**
     * 给编辑器设置值
     * @param value 需要设置的值
     */
    const handleSetEditorVal = (value: string): void => {
      if (!value) return;
      // 为所选取的值赋值到编辑器中
      if (editorInstance.current && value) {
        const selection = editorInstance?.current?.getSelection?.();
        const range = new _monaco.Range(
          selection.startLineNumber,
          selection.startColumn,
          selection.endLineNumber,
          selection.endColumn
        );
        const id = { major: 1, minor: 1 };
        const op = { identifier: id, range, text: value, forceMoveMarkers: true };
        editorInstance.current.executeEdits('', [op]);
        editorInstance.current.focus();
      }
    };

    /**
     * 编辑器change回调
     * @param {String} val 当前编辑器的值
     */
    const onChangeHandle = (val: string, event: { changes: { text: any }[] }) => {
      const curWord = event.changes[0].text;
      if (curWord === '"') {
        cache.current = val + curWord;
        setRefresh(!refresh); // 刷新页面
        return;
      }
      cache.current = val;
    };

    /**
     * 初始化编辑器
     * @param {*} editor 编辑器实例
     * @param {*} monaco
     */
    interface ISuggestions {
      label: string;
      kind: string;
      insertText: string;
      detail?: string;
    }
    const editorDidMountHandle = (editor: any, monaco: any) => {
      monacoInstance.current = monaco;
      editorInstance.current = editor;
      const newSecondRightFields: model.dataSource.BaseDataSourceHeader[] = [];
      (secondRightData as model.dataSource.BaseDataSourceHeader[]).forEach((record) => {
        if (record.fields && Array.isArray(record.fields)) {
          record.fields.forEach((item: any) => {
            // fixme
            newSecondRightFields.push(item);
          });
        }
        code.current = newSecondRightFields; // 数组长度永远为1
      });
      // 提示项设值
      provider = monaco.languages.registerCompletionItemProvider('plaintext', {
        provideCompletionItems() {
          const suggestions: ISuggestions[] = [];
          if (code && code.current) {
            code.current.forEach((record) => {
              suggestions.push({
                // label未写错 中间加空格为了隐藏大写字段名称 大写字段名称用于规避自定义提示不匹配小写的bug
                label:
                  record.label ||
                  `${record.displayName} (${
                    record.aliasName
                  })                        ${''}(${record.aliasName.toUpperCase()})`, // 显示名称
                kind: record.kind || monaco.languages.CompletionItemKind.Field, // 这里Function也可以是别的值,主要用来显示不同的图标
                insertText: record.insertText || record.aliasName, // 实际粘贴上的值
                // detail: record.detail || `(property) ${record.aliasName}: String`,
              });
            });
          }
          [
            'CASEWHEN(expression1, value1, expression2, value2, ..., else_value)',
            'CONCAT(str1, str2, ...)',
            'ISNULL (expression, defaultValue)',
            'DATEDIFF_YEAR(startdate,enddate)',
            'DATEDIFF_MONTH(startdate,enddate)',
            'DATEDIFF_DAY(startdate,enddate)',
            'SUM(expression)',
            'AVG(expression)',
            'MAX(expression)',
            'MIN(expression)',
            'COUNT(expression)',
            'DISTINCTCOUNT(expression)',
            'DISTINCTAVG(expression)',
            'DISTINCTSUM(expression)',
            'NOW()',
          ].forEach((item) => {
            suggestions.push(
              // 添加contact()函数
              {
                label: item, // 显示名称
                kind: monaco.languages.CompletionItemKind.Function, // 这里Function也可以是别的值,主要用来显示不同的图标
                insertText: item, // 实际粘贴上的值
              }
            );
          });
          return {
            suggestions, // 必须使用深拷贝
          };
        },
        quickSuggestions: false, // 默认提示关闭
        // triggerCharacters: ['$', '.', '='], // 触发提示的字符,可以写多个
      });
      editor.focus();
    };

    return (
      
        
        
          
        
      
    );
  }
);

页面效果:


image.png

备注:
( 使用react-monaco-editor )还有很多坑,网上的介绍文档也不多,代码全靠摸索,这里我遇到的一个最大的坑就是自定义提示时,不能匹配小写字母,目前已规避这个问题,并且在gitHub上给作者提了issue,多发现问题并分享出来,程序世界会越来越美好。
react-monaco-editor作者地址:https://github.com/jaywcjlove/react-monacoeditor/issues/26

你可能感兴趣的:(react-monaco-editor编辑器安装使用&自定义提示)