wangEditor 5 不支持自定义 HTML 格式图片或文字解析方案

675 阅读4分钟

之前做了个需求,后端会返回别的编译器导出的html格式进入到我们的系统,需要利用# wangEditor 5展示内容。但是wangEditor 5 只会识别自身编辑保存过的。如果没有它自身格式利用sethtml会丢失部分内容。 如:<a href=""><img src=""></a>或者<span><img src=""></span>或者<font></font>; 类似此结构标签在wangEditor 5是会被丢弃没法解析出来。因此需要自己解析替换成别的html标签来包裹展示img。 你也可以改源码根据原来的slate.js来修改源码适配自己的结果。你肯定问为啥过来的数据特么就不能筛选简洁就满足需求?那是因为我们的后端同学懒。又不让你换编辑器组件不想改大量数据。 因此需要如此委婉的方式去解决这些数据T。T这年头做个前端都不容易。 以下是我的处理方式:

    onCreated(editor) {
      try {
        this.editor = Object.seal(editor); // 【注意】一定要用 Object.seal() 否则会报错
        console.log('处理之前的html', this.html);
        let HasImgs = false;
        HasImgs = this.hasValidImageTag(this.html); 判断传入的html是否有图片
        // console.info('HasImgs', HasImgs);
        if (HasImgs) {
          //不是sourceType!=0 内部类型并且有图片的,需要转换
          if (this.sourceType != 0) {
            this.html = this.convert2ValidHtml(this.html);
          }
        }
        console.log('处理之后的html==', this.html);
        this.editor.setHtml(this.html);
      } catch (error) {
        console.log('setHtml error', error);
      }
    },
    hasValidImageTag(str) {
      // 正则表达式匹配img标签
      const imgPattern = /<img[^>]*src=["'](.*?)["']/i;

      // 使用match方法查找匹配项
      const match = str.match(imgPattern);

      // 如果找到匹配项并且src属性不为空,返回true;否则返回false
      return match && match[1];
    },
    convert2ValidHtml(html) {
      if (!html) {
        return '';
      }
      // // 不能正确识别font
      // html = html.replace(/<font/gi, '<div').replace(/<\/font>/gi, '</div>');
      try {
        // 使用DOMParser将HTML字符串解析为DOM
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');

        // 获取所有的img标签
        const imgs = doc.getElementsByTagName('img');

        // 遍历所有的img标签
        for (let i = 0; i < imgs.length; i++) {
          const img = imgs[i];

          // 检查img标签是否存在
          if (!img) {
            continue;
          }

          // 检查src是否以http或https开头
          const src = img.getAttribute('src');
          if (!src || !/^https?:\/\//.test(src)) {
            console.log('src未识别', src);
            continue;
          }

          let imgNode = doc.createElement('img');
          imgNode.src = img.src;
          // 将img标签放入p标签中
          let pNode = doc.createElement('p');

          // 获取img标签的直接父级,即p标签
          let imgFirstParentNode = img.parentNode;
          // 检查img标签的直接父级是否存在,不存在则将该节点包裹一个p
          if (!imgFirstParentNode) {
            pNode.appendChild(imgNode);
            img.replaceWith(pNode);
            continue;
          }

          //找父级标签,直到找到body节点 如div
          let lastParentNode = img.parentNode;
          while (imgFirstParentNode && imgFirstParentNode.tagName.toLowerCase() != 'body') {
            console.log('处理之前imgFirstParentNode==', imgFirstParentNode);
            if (imgFirstParentNode.tagName.toLowerCase() == 'p') {
              // 如果是p标签,直接将img标签替换为p标签,也可以不处理,editor对p可以识别
              pNode.appendChild(imgNode);
              img.replaceWith(pNode);
              imgFirstParentNode = imgFirstParentNode.parentNode;
              continue;
            }
            // 处理wangEditor的标签
            if (imgFirstParentNode.tagName.toLowerCase() == 'div' || imgFirstParentNode.tagName.toLowerCase() == 'a' || imgFirstParentNode.tagName.toLowerCase() == 'span') {
              //如果是有文字的,父级被替换前,将文字放到一个新的p标签
              if (imgFirstParentNode.innerText) {
                let spanNode = doc.createElement('span');
                spanNode.innerText = imgFirstParentNode.innerText;
                pNode.appendChild(spanNode);
              }
            }

            //当前节点的父节点已经是body节点 <body><div><img></div></body>
            if (imgFirstParentNode.parentNode) {
              if (imgFirstParentNode.parentNode.tagName.toLowerCase() == 'body') {
                console.log('当前节点的父节点已经是body节点imgFirstParentNode', imgFirstParentNode);
                lastParentNode = imgFirstParentNode;
                break;
              } else if (imgFirstParentNode.parentNode.tagName.toLowerCase() == 'div' || imgFirstParentNode.parentNode.tagName.toLowerCase() == 'span') {
                //这时想要对嵌套多层div的进行处理,获取包裹img标签的div的兄弟节点,如果兄弟很多,则不方便如此处理,直接正则全局处理,不要样式
                let imgParentBrothers = imgFirstParentNode.parentNode.childNodes;
                if (imgParentBrothers.length > 1) {
                  console.log('img所在div兄弟很多,则不方便如此处理,直接正则全局处理');
                  return html
                    .replace(/<span/gi, '<p')
                    .replace(/<\/span>/gi, '</p>')
                    .replace(/<div/gi, '<p')
                    .replace(/<\/div>/gi, '</p>')
                    .replace(/<a/gi, '<p')
                    .replace(/<\/a>/gi, '</p>')
                    .replace(/<font/gi, '<span')
                    .replace(/<\/font>/gi, '</span>'); //font标签替换成div,有显示问题,再替换为span标签,解决报错问题
                }
              }
            }
            // 检查父节点的父节点 <body><div><div><img></div></div></body>
            imgFirstParentNode = imgFirstParentNode.parentNode;
            if (imgFirstParentNode.parentNode && imgFirstParentNode.parentNode.tagName.toLowerCase() == 'body') {
              console.log('节点的父节点的父节点已经是body节点imgFirstParentNode', imgFirstParentNode);
              lastParentNode = imgFirstParentNode;
            }
            console.log('处理之后imgFirstParentNode==', imgFirstParentNode);
          }
          console.log('lastParentNodee==', imgFirstParentNode);
          pNode.appendChild(imgNode);
          lastParentNode.replaceWith(pNode);
        }
        return doc.documentElement.innerHTML
          .replace(/<head><\/head>/gi, '')
          .replace(/<body>/gi, '')
          .replace(/<\/body>/gi, '')
          .replace(/<p><\/p>/gi, '')
          .replace(/<font/gi, '<span')
          .replace(/<\/font>/gi, '</span>');
      } catch (error) {
        console.log('转换失败:', error);
      }
      // 或者其他适当的默认值
      return html;
   },

以上就是核心逻辑 首先在onCreated 方法传入插入的html判断返回过来的结果是否有图片标签标识或者多层嵌套如果有就走转html结构转完从新调用sethtml方法塞入wangEditor 编辑器中解析。 以上方法能解决80%过来的特殊图片结构体或者是文字文案多层嵌套去展示在编辑框中。如果遇到那种文字解析排序乱目前是没法解析的部分样式也会被丢弃。 因为多层嵌套查找还要判断位置只有用ast语法树才能根本解决或者请教作者把底层改一下。 设置之初也是不支持那种自定义结构体因此就只能用这个方法解决80%问题。附上vue2运行的demo凑合用。