记录一次输入框中插入自定义标签的实现

271 阅读1分钟

前言

在项目的开发中产品提出了一个需求 我要实现自定义的告警模板,模板可以输入也可以插入或者拖拽按钮到指定的位置。我着简单我给你搞.....

第一版

这不简单我直接插入不就可以了吗

用textarea 配合焦点的位置 直接插入即可 简单就放了

啥垃圾东西 谁看的懂 我要文本里面展示的按钮的名称

第二版

我这苦想呀 想到了h5中的可编辑属性contenteditable 不就可以实现元素的可编辑 里面的按钮 咱们给他整成标签不就行了 再插入到对应的位置即可。又遇到了 插入元素后焦点丢失了等问题 最后实现了需求。

<!-- eslint-disable no-irregular-whitespace -->
<template>
  <div>
    <el-button v-for="item in optionList" size="small" style="background: rgba(92,133,200,0.08);color:black;font-size: 14px;
      font-family: PingFang SC, PingFang SC-Regular;color: rgba(0,0,0,0.85);" :key="item.value"
      @click="AddExtractedValue(item.label)" :draggable="true" @dragstart.native="handleDragStart(item.label)">
      {{ item.label }}
    </el-button>
    <p contenteditable="true" class="w-textarea_input" id="bjContent" @dragover="handleDragOver" @drop="handleDrop"
      @blur="updateData"></p> 
  </div>
</template>
<script>
export default {
  props: {
    optionList: {
      type: Array,
      default: () => {
        return []
      }
    },
    value: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      bjContent: null,
      idList: [],
      range: null,
    };
  },
  mounted() {
    this.idList = [];
    this.bjContent = document.getElementById("bjContent");

    if (this.value) {
      let text = this.value;
      let idList = [];
      let textDataLength = this.value.split('').filter(item => item == '$').length;
      for (let i = 0; i < textDataLength; i++) {
        this.optionList.forEach((item) => {
          if (text.includes(item.value)) {
            let id = this.guid();
            idList.push(id);
            text = text.replace(
              item.value,
              `<span   contenteditable="false" class="w-textarea_tag" id="${id}" style="position:relative;border:1px dashed #a9d3ff;border-radius:4px;padding:8px 14px;margin:0 4px;background:rgba(121,200,255,0.26) ">
                <span>${item.label}</span>
                <span id="${id}span" style="color:#2E94FF;cursor:pointer;font-size:12px;position:absolute;margin-top:8px;" class='el-icon-close'  ></span>
              </span>
              <span contenteditable=true id='jkq${id}' style="line-height:3"></span>
              `
            );
          }
        });

      }

      let span = document.createElement("span");
      span.innerHTML = text;
      this.bjContent.appendChild(span);
      this.idList = idList;
      this.$nextTick(() => {
        idList.forEach(item => {
          document.querySelector(`#jkq${item}`).innerHTML = "&nbsp";
        })
      })
    }
  },
  watch: {
    idList(val) {

      if (!val) return false
      this.idList.forEach((item) => {
        let ele = document.getElementById(`${item}span`);
        if (!ele) return false
        ele.addEventListener("click", () => {
          document.getElementById(`${item}`).remove()
        });
      });
    },
  },
  methods: {
    AddExtractedValue(insertTxt) {
      let id = this.guid();
      this.idList.push(id);
      let html = `<span   contenteditable="false" class="w-textarea_tag" id="${id}" style="position:relative;border:1px dashed #a9d3ff;border-radius:4px;padding:8px 14px;margin:0 4px;background:rgba(121,200,255,0.26) ">
                    <span>${insertTxt}</span>
                    <span id="${id}span" style="color:#2E94FF;cursor:pointer;font-size:12px;position:absolute;margin-top:8px" class='el-icon-close'  ></span>
                  </span>
                  <span contenteditable=true id='jkq${id}' style="line-height:3"></span>
                  `;
      this.insertNode(html);
      if (!document.querySelector(`#jkq${id}`)) return false
      document.querySelector(`#jkq${id}`).innerHTML = "&nbsp";
    },
    guid() {
      return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
        /[xy]/g,
        function (c) {
          var r = (Math.random() * 16) | 0,
            v = c == "x" ? r : (r & 0x3) | 0x8;
          return v.toString(16);
        }
      );
    },

    insertNode(html) {
      if (this.bjContent != undefined) {
        var sel;
        if (window.getSelection) {
          sel = window.getSelection();
          if (sel.getRangeAt && sel.rangeCount) {
            var tag = document.createElement("span");
            tag.setAttribute("contenteditable", true);
            tag.innerHTML = html;
            var frag = document.createDocumentFragment(),
              node,
              lastNode;
            while ((node = tag.firstChild)) {
              lastNode = frag.appendChild(node);
            }
            this.range.insertNode(frag);
            if (lastNode) {
              this.range.insertNode(lastNode);
              sel.removeAllRanges();
              sel.addRange(this.range);
              sel.collapseToEnd();
            }
            this.bjContent.focus();
          }
        }
      }
    },
    save() {
      let test = this.bjContent.innerText.replace(/\s/g, '')
      this.optionList.forEach(item => {
        test = test.replaceAll(item.label, item.value)
      })
    },
    handleDragStart(label) {
      this.bjContent.focus();
      event.dataTransfer.setData("text/plain", label);
    },
    handleDragOver(event) {
      event.preventDefault();
    },
    updateData() {
      let list = this.bjContent.innerText;
      this.$emit("changeInnerText", list);
      //获取当前的选取
      this.range = window.getSelection().getRangeAt(0);
    },
    handleDrop(event) {
      event.preventDefault();
      const label = event.dataTransfer.getData("text/plain");
      let id = this.guid();
      this.idList.push(id);

      let html = `<span   contenteditable="false" class="w-textarea_tag" id="${id}" style="position:relative;border:1px dashed #a9d3ff;border-radius:4px;padding:8px 14px;margin:0 4px;background:rgba(121,200,255,0.26) ">
                    <span>${label}</span>
                    <span id="${id}span" style="color:#2E94FF;cursor:pointer;font-size:12px;position:absolute;margin-top:8px" class='el-icon-close'  ></span>
                  </span>
                  <span contenteditable=true id='jkq${id}' style="line-height:3"></span>
                  `;
      this.insertNode(html);
      if (!document.querySelector(`#jkq${id}`)) return false
      document.querySelector(`#jkq${id}`).innerHTML = "&nbsp";
      this.updateData();
    },
  },
}
</script>
<style scoped lang="scss">
.w-textarea_input {
  height: 200px;
  width: 100%;
  background: #ffffff;
  border: 1px solid #eaeef3;
  border-radius: 4px;
  padding: 8px;
  overflow: auto;
  text-align: left;
  word-break: keep-all
}
</style>


知识点: 1.contenteditable 2.window.getSelection 3.window.getSelection().getRangeAt(0)