公式规则编辑器,满足业务规则公式编辑场景

本文介绍了在项目开发中如何使用Tinymce富文本库实现一个业务需求的公式规则编辑器,包括需求确认、富文本编辑器组件实现、内容插入方法以及后续待改进的功能如规则子项扩展和函数选择。
摘要由CSDN通过智能技术生成

项目开发又要到特殊的场景,不是函数公式的编辑器,是业务需求的公式规则编辑器,如满足营收计算、项目系统计算
、一些特定场景计算,主要是字段来源于系统、已定义字段,满足基础可配置的场景需求

实现效果

formula-rule-handle.gif

1. 需求确认

基于原型需求,与产品、后端讨论出来了以下需求:

  • 基础编辑框与富文本编辑器接近, 编辑历史记录
  • 实现外部规则子项选择填入,规则子项在编辑区可删除(Delete)
  • 支持加减乘除,与或非,括号的选择填入
  • 支持任意基础中英文、符号的自由编辑
  • 编辑后显示以文本形式展示
  • 实际规则是以字段形式的规则公式提供后端使用
  • 公式校验由接口提供
  • 支持函数选择填入

方案确定

首先明确是以富文本形式实现,富文本可以基于contenteditable实现,原计划是参考钉钉的薪酬计算规则的编辑组件自己用contenteditable实现富文本组件,但是考虑到快照(snapshot),插入处理,回显等一些文字需要耗费不少时间,决定基于现有的第三方富文本库来实现该公式规则编辑器。

项目上的富文本是Tinymce, 查找文档后提供插入方法,直接决定在Tinymce基础上实现。

    // 插入方法
    const editor = window.tinyMCE.editors[curId.value];
    editor.insertContent(value);

代码实现

富文本编辑器组件 tinymce-formula

向富文本插入内容

    const insertContent = ({ value, type, offset, offsetIdx }) => {
      const editor = window.tinyMCE.editors[curId.value];
      if (type === 'operator') { //计算规则
        if (offset) {
          editor.insertContent(value);
          selectionSetRng(offsetIdx);
        } else {
          editor.insertContent(value);
        }
      } else if (type === 'field') { // 规则子项
        editor.insertContent(`<span class="mention-${type}" contenteditable="false">${`#${value}#`}</span>`);
      } else {
        editor.insertContent(value);
        offset && selectionSetRng(offsetIdx);
      }
      initInstanceCallback();
      editor.focus(); // 显示光标
    };

插入文本时改变光标位置

   const selectionSetRng = (offsetIdx) => {
      const editor = window.tinyMCE.editors[curId.value];
      // 获取当前选区的范围对象
      const range = editor.selection.getRng();
      // 获取光标位置的索引
      const caretIndex = range.startOffset;
      // 计算光标位置的偏移量
      const offsetIndex = caretIndex - 1;

      // 设置光标位置
      editor.selection.setRng({
        startContainer: range.startContainer,
        startOffset: offsetIdx ?? offsetIndex,
        endContainer: range.startContainer,
        endOffset: offsetIdx ?? offsetIndex,
      });
    };

处理富文本内容,回调公式节点数组、文本

    const contentChange = (val) => {
      const editor = window.tinyMCE?.editors[curId.value]; // 富文本实例
      if (!editor) return;
      const tempContainer = editor.getBody(); // 富文本节点内容
      const text = tempContainer.textContent;
      if (props.modelValue === text) return; // 内容全等不回调
      const childNodes = tempContainer.childNodes;
      const calcList = [];
      const regexText = props.operatorList.map((operator) => `\\${operator.value ?? operator}`).join('');
      const operatorRegex = new RegExp(`([${regexText}])`);
      const operatorSplitRegex = new RegExp(`([${regexText}])`);

      childNodes.forEach((p) => {
        p.childNodes.forEach((element) => {
          const classText = element.nodeType === 1 && element?.getAttribute ? element?.getAttribute('class') : '';
          const value = element.textContent;
          if (element.nodeType === 3) {
            if (value !== '') {
              calcList.push({
                type: 'text',
                value,
              });
            }
          } else if (classText && classText.indexOf('mention-field') !== -1) {
            if (value !== '') {
              calcList.push({
                type: 'field',
                value,
              });
            }
          } else if (classText && classText.indexOf('mention-operator') !== -1) {
            const operatorSplits = value.split(operatorRegex);
            operatorSplits.forEach((v) => {
              if (v !== '') {
                if (operatorSplitRegex.test(v)) {
                  calcList.push({
                    type: 'operator',
                    value: v,
                  });
                } else {
                  calcList.push({
                    type: 'text',
                    value: v,
                  });
                }
              }
            });
          } else {
            calcList.push({
              type: 'text',
              value,
            });
          }
        });
      });
      emit('changeHtmlContent', val);
      emit('change', calcList);
      emit('input:modelValue', text);
    };
公式规则编辑弹窗组件 formula-dialog
/**
 * @description: 字段-点击插入
 */
const fieldClick = (row) => {
  if (row.code === curRow.value.code) return;
  contendEditRef.value.insertContent({ value: row.name, type: 'field' });
};

/**
 * @description: 操作符-点击插入
 */
const operatorClick = (m) => {
  if (typeof m === 'string') {
    contendEditRef.value.insertContent({ value: m, type: 'text' });
  } else {
    contendEditRef.value.insertContent({ ...m, type: 'text' });
  }
};

/**
 * @description: 函数-点击插入
 */
const functionClick = (m) => {
  contendEditRef.value.insertContent({ ...m, type: 'function' });
};

todo

  • 规则子项在编辑器可输入提出下拉框
  • 函数扩展
  • 规则嵌套
仓库链接
  1. https://github.com/ZTrainWilliams/formula-rule-editor
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MATLAB的实时编辑器(Live Editor)是一种交互式的文档环境,可以在其中编写和运行MATLAB代码,同时还可以添加可视化、文本、图像和其他类型的内容。它提供了一种直观的方式来探索、测试和记录MATLAB代码和分析过程。 使用实时编辑器,你可以创建一个包含代码、文本、公式、图形等内容的单个文档。你可以在编辑器中编写MATLAB代码,并即时运行这些代码,以查看结果和输出。此外,你还可以添加文本段落、标题、图片、表格、交互式控件等来创建丰富的文档。 以下是一些实时编辑器的特点和功能: 1. 即时运行:在编辑器中编写的MATLAB代码可以立即运行,并显示结果和图形。 2. 富文本支持:可以添加富文本内容,例如标题、段落、链接、字体样式等。 3. 图形和绘图:可以在实时编辑器中绘制各种类型的图形,如曲线图、散点图、直方图等。 4. 支持LaTeX和公式:可以使用LaTeX语法添加数学公式和符号。 5. 交互式控件:可以添加交互式控件(如滑块、复选框、下拉菜单等),以便更好地与代码进行交互。 6. 导出和共享:可以将实时编辑器中的内容导出为PDF、HTML或其他格式,并与他人共享。 通过实时编辑器,你可以以一种更具可读性和可交互性的方式编写和展示MATLAB代码和分析过程。它非常适用于教育、演示和报告等场景,同时也是一个方便的工具来记录和分享MATLAB代码和分析结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值