项目踩坑及经验

这是一篇单纯记录项目踩坑及经验的文章

使用codeMirror

引入、定义、添加标签,获取标签、展示标签内容

具体引用和定义可以去 codeMirror 官网查看,这里主要想讲获取标签,因为项目有需求是展示预览,根据标签()括号内的内容展示,我一开始直接使用codeMirror的getAllMarks获取添加的标签来展示预览,但是过了很久才发现,预览顺序和编辑器内的标签的排序不对应。
具体是这样的,如果我正常挨个追加标签的话,getAllMarks获取的标签和编辑框内的标签顺序是一样的。但是,假如说我现已追加两个标签,再在他们中间追加一个,那么就不能对应啦,getAllMarks获取的最后一个标签将是追加在第二个位置的那个,而不是实际显示的顺序啦,我想了一个比较笨的方法,在追加标签的时候,添加了一个隐藏的span标签,再用getElementsByClass获取,这样获取到的是在页面中按顺序来的,与生成的先后顺序无关,如果你用更好的方法,请多指教哦

	 /**
     * 插入标记
     */
    addCodeMirrorTextMarker(startPos, endPos, markData, error, elementType) {
      const self = this;
      const msg = document.createElement('span');
      const dataSpan = document.createElement('span');
      // msg获取指定范围内的editorDoc的内容,指要标记的内容【elementType(elementPattern)】
      msg.innerText = editorDoc.getRange(startPos, endPos);
      // dataSpan的存在以确保预览元素顺序和编辑框内元素顺序一致
      dataSpan.innerText = JSON.stringify(markData);
      dataSpan.style.display = 'none';
      dataSpan.className = 'inner-span-markData';
      msg.appendChild(dataSpan);
      if (error) {
        msg.className = 'outer-span-mark-' + elementType + ' outer-span-mark-margin CodeMirror-lint-mark CodeMirror-lint-mark-error';
      } else {
        msg.className = 'outer-span-mark-' + elementType + ' outer-span-mark-margin CodeMirror-lint-mark';
      }
      // 判断追加得元素是否为第一个,不是第一个需要添加左边距
      if (editorDoc.getAllMarks().length >= 1) {
        msg.classList.add('outer-span-mark-margin-left');
      }
      msg.addEventListener('click', function (e) {
        const elementStr = e.target.children[0].innerHTML;
        const element = JSON.parse(elementStr);
        self.checkedElement = element;
      });
      editorDoc.markText(startPos, endPos, {
        className: msg.className,
        inclusiveLeft: false,
        inclusiveRight: false,
        selectLeft: true,
        selectRight: true,
        atomic: false,
        replacedWith: msg,
        handleMouseEvents: true,
        markData
      });
    },

获取标签:

    /*
    * 获取公式编辑器最后一个追加的元素
    * 以getElementsByClassName获取的元素是在最后面的元素,
    * 以editorDoc.getAllMarks获取的元素是最后追加的元素
    * */
    getLastElementByEditor() {
      const self = this;
      // 获取追加的 CodeMirror-mark
      editorInstance.focus();
      const markSpan = editorDoc.getAllMarks();
      self.lastElementMatch(markSpan);
    },
    getLastElementByClass() {
      const self = this;
      editorInstance.focus();
      const pos = editorDoc.getCursor();
      const marks = editorDoc.findMarksAt(pos);
      self.lastElementMatch(marks);
    },

光标问题

在使用过程中可能出现光标不能好好定位这个事儿,一开始我做了一系列的操作来定位光标,都不能让光标在出现他应在的位置,真是不听话,放个五一假期回来后,居然用一个refresh就解决了,果然还是要放假,工作效率都提高了🤭

// 刷新编辑区,解决光标问题
      editorInstance.refresh();

以下是我得一系列骚操作之后但没有解决问题的代码,仅为了警醒自己以后少走弯路,当然前提是好运。

  elementChange(lastElement) {
      const self = this;
      // 如何将标记内容的改变再体现在markData上
      // 获取光标位置
      const pos = editorDoc.getCursor();
      // 重新定位光标,获得元素开始位置
      oldMarkData = mark[0].markData;
      const oldLength = oldMarkData.replacedStr.length;
      const startPos = { line: pos.line, ch: pos.ch - oldLength };
       // 判断光标所在位置,当前元素的在左右位置
       const oldPos = mark[0].lines[0].markedSpans[0].to;
       let dev;
       const newLength = addString.length;
       if (oldLength < newLength) {
         dev = newLength - oldLength;
         editorDoc.findMarksAt(pos)[0].lines[0].markedSpans[0].to = oldPos + dev;
        } else {
          dev = oldLength - newLength;
          editorDoc.findMarksAt(pos)[0].lines[0].markedSpans[0].to = oldPos - dev;
        }
        // 光标定位
        const endPos = { line: startPos.line, ch: startPos.ch + newLength + 1 };
        editorDoc.setCursor(endPos);
        const newPos = editorDoc.getCursor();
        CodeMirror.commands.goLineRight(editorInstance);
        const marks = editorDoc.getAllMarks();
        // editorInstance.focus();
     

如何设置文本域不能输入,却能删除(不是只读哦)

我也很好奇为什么会有这么无理的要求,不能输入,但能删除!!因为我所做的项目要求可以在外追加标签到公式编辑框内,用户可以删除所追加的标签,但不可以任意输入,所以虽然物理但也可以接受。我想的太简单了,以为添加一个readonly就完事儿了,殊不知加了readonly之后,所有的键盘按键都禁用了,那么既然关于键盘,我想是不是可以拦截一下键盘的按下事件,判断是那个键按下了呢,有了思路问题就可以迎刃而解啦。

每个按键都有他自己的编码keycode,了解这个之后还需要用到键盘事件keydown和keyup,在键盘按下(keydown)的时候拦截判断键盘码keycode,在放开(keyup)的时候决定允许通过还是拦截,代码如下:

 		let k;
        editorInstance.on('keydown', (evt) => {
          // 获取键盘码,delete back,left,right,up,down予以通过
          k = window.event.keyCode;
          if (k !== 8 && k !== 46 && (k < 37 || k > 40)) {
            // 不允许此次键入
            markWhenKeyup = false;
            // 阻止默认事件
            window.event.preventDefault();
          } else {
            markWhenKeyup = true;
            window.event.returnValue = true;
          }
        });
        editorInstance.on('keyup', (evt) => {
          if (markWhenKeyup && (k === 8 || k === 46 || (k > 36 && k < 41))) {
            // 允许删除
            markWhenKeyup = false;
          }
        });


注意:其中最重要的一行是window.event.preventDefalt(),我一开始没加这句导致任意按键都可以通过,但是当我打开F12加debugger走他又可以拦截,关闭F12又不可以拦截,可把我愁得,查询资料后发现只需要加上这一句,哈哈哈代码就是如此,另外还要注意是否需要加window,因为当时一个我学长和我一起解决得这个问题,可能有些情况下不用加。
在这里插入图片描述

最终效果如下:
在这里插入图片描述

$refs未定义问题

这个问题我在前一篇文章中有讲解,查看上一篇

将输入框的原生事件组合到一起

此次项目中对输入框有不同的要求,要求只能输入数字或者只能输入数字和英文,在输入的同时需要将输入的内容展示到公式编辑框中,这些需求都指向input事件,我一开始将过滤输入内容(1)和实时展示(2)分开两个方法写,但是在同一个输入框中又不能出现两个@input,这个确实为难到我了,后来在同事的帮助下,使用$listeners,想了解跟更多看 vue的listeners属性
这里是我的使用情况,你可以将其他原生事件也组合到这儿

inputListeners: function () {
      const self = this;
      // `Object.assign` 将所有的对象合并为一个新对象
      return Object.assign({},
        // 从父级添加所有的监听器
        this.$listeners,
        // 添加自定义监听器,
        {
          input: function (e) {
            if (self.checkedElement.elementType === 'variable') self.checkedElement.variablePattern = e.replace(/[\u4e00-\u9fa5/\s+/]|[^a-zA-Z0-9\u4E00-\u9FA5]/g, '');
            else self.checkedElement.elementPattern = e.replace(/[\u4e00-\u9fa5/\s+/]|[^a-zA-Z0-9\u4E00-\u9FA5]/g, '');
            self.elementChange();
          }
        }
        {
          blur: function (e) {
		// blur事件多用于表单校验	
		  }
        }
      );
    },

html代码片段

  <!-- 使用οnkeyup="value=value.replace(/[^\w\.\/]/ig,'')"有缺陷               -->
                <el-input
                  class="full-width search-select"
                  type="text"
                  v-on="inputListeners"
                  placeholder="请输入"
                  v-model="checkedElement.elementPattern"
                  :maxlength="checkedElement.limitLength"
                  show-word-limit
                />
              </el-form-item>

其实后来我觉得我其实也可以在一个input事件里面完成的,在过滤方法的最后加一个方法调用就行啊!我当时脑子短路了吧·······
这里有提到onkeyup会有缺点,具体可见这篇分享,好吧我找不到那篇分享了,那我自己说一下吧,可看下面两图在这里插入图片描述
注意看,当我在中文输入下敲了“我是大哥”的拼音,我保证我只敲了一遍哈,而输入框内已经出现了三遍,并且回车后长度已经超过了限制。
注意看长度已经超过了限制
综上,使用onkeyup的缺点显而易见了,不必赘述,建议大家还是使用input事件。

一点点心得

最后最后,这次使用codemirror的心得是,因为用到额是一个我并不熟悉的东西,所以写代码的时候总是胆怯,不敢写,生怕出错,但是但是我想告诉自己,你就大胆写,勇敢的写,出错就出错嘛,出错就回退,没错就是发掘新的知识,还有,对于不熟悉的领域,多看文档嘛,多看代码。还有就是,这次的项目要求真的是太严格了,对于有原型的项目,样式严格按照他的要求来,一点细节都不能放过,我看你以后还怎么挑我的样式问题。
还有,感谢我得同事,我的上级吧,给我提出各种问题,其中有我坦然接受的,还有我觉得是无理的,不管怎样,在他的折磨下,我这次真的成长了很多,也学到了很多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值