自定义公式编辑框,变量高亮展示

143 阅读2分钟

参考文章:blog.csdn.net/C_fashionCa…

目标:实现一个输入框,可以输入变量与常规文字,组成一个计算公式,同时,变量要求高亮直观

HTML:

<div ref="formulaBoxRef" class="formulaBox" contenteditable="true" :v-html="formula" @blur="getFormula">

CSS:

.formulaBox {
  padding: 10px 15px;
  border: 1px solid #DCDFE6;
  border-radius: 4px;
  width: 100%;
  caret-color: #3d7ecf;
  line-height: 18px;
  min-height: 34px;
}

这里只展示核心逻辑,其余代码以伪代码形式展示:

//项目框架:vue2
data(){
    return{
        saveRange:null,
        formula:''
    }
}
​
//在选择变量之前,记录光标之前所处的range
this.saveRange =  range
​
//获取到变量名paraname后,调用addTag方法添加至输入框
this.addTag(paraname)
​
​
addTag(data) {
    //对于有空格的样式字符串,建议直接写到html字符串中,在外面用变量接收的话,在实际使用中,style的双引号遇到空格会直接中断
    let tag = `<input style="display:inline-block;height:18px;line-height:18px;background-color:#004ba6;color:white;padding:0 5px;border:1px solid #DCDFE6;border-radius:4px;text-align:center;margin:0 5px;" type="button" value=${data}>`
    this.pasteHtmlAtCaret(tag)
},
pasteHtmlAtCaret(html) {
  let sel, range
  sel = window.getSelection()
  if (sel && sel.rangeCount === 0 && this.savedRange !== null) sel.addRange(this.savedRange); // 保留光标在文字中间插入的最后位置
  if (sel && sel.rangeCount) range = sel.getRangeAt(0);
  if (['', null, undefined].includes(range)) {
    // 如果div没有光标,则在div内容末尾插入
    range = this.keepCursorEnd(true).getRangeAt(0);
  }else{
    const contentRange = document.createRange()
    contentRange.selectNode(this.$refs.formulaBoxRef)
    // 对比range,检查光标是否在输入范围内
    const compareStart = range.compareBoundaryPoints(Range.START_TO_START, contentRange)
    const compareEnd = range.compareBoundaryPoints(Range.END_TO_END, contentRange)
    const compare = compareStart !== -1 && compareEnd !== 1
    if (!compare) range = this.keepCursorEnd(true).getRangeAt(0);
  }
​
  let input = range.createContextualFragment(html);
​
  // 记录插入input之后的最后节点位置,将光标位置重新定位到插入变量后的位置
  let lastNode = input.lastChild; 
  range.insertNode(input)
  if (lastNode) { // 如果有最后的节点
    range = range.cloneRange();
    range.setStartAfter(lastNode);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
  }
},
/**
* 将光标重新定位到内容最后
* isReturn 是否要将range实例返回
* */
keepCursorEnd(isReturn) {
  this.$refs.formulaBoxRef.focus();
  let sel = window.getSelection(); // 创建range
  sel.selectAllChildren(this.$refs.formulaBoxRef); // range 选择obj下所有子内容
  sel.collapseToEnd(); // 光标移至最后
  if (isReturn) return sel;
},
    
//插入input标签形式的变量后,需要得到只包含文本形式的字符串
//div失焦后,将字符串中的input标签体字符串替换为对应的value
async getFormula(e){
  let innerHTML = e.target.innerHTML
  //匹配input标签形式的变量
  const regex = /<input([^>]+)>/g;
  const matchArr = innerHTML.toString().match(regex)
  //没有变量,说明全是常规文本
  if(!matchArr){
    this.form.evaluationFormula = innerHTML
    return
  }
  let result = innerHTML
  matchArr.forEach(item => {
    //匹配input标签的value值
    const variableName = item.match(/value="([^"]+)"/)[1]
    const variable = result.replace(item, variableName)
    result = variable
  })
  // console.log('result', result);
  this.form.evaluationFormula = result  //这里的result就是我们需要的文本形式的字符串
},