常见操作

发送 响应

editor.fire("tencentDocAfterCreate", { editor: target, doc });

editor.on("tencentDocAfterCreate", function (data) {
  $("body").trigger("tencentDocAfterCreate", data);
});

获取焦点

editor.editorManager.get(editor.settings.id).focus(); // 获取焦点
editor.insertContent(""); // 获取焦点

获取编辑器点击的元素

function composedPath(event) {
  if (event.path) {
    return event.path;
  }
  try {
    return event.composedPath();
  } catch (e) {
    const path = [];
    let el = event.target;
    while (el) {
      path.push(el);
      if (el.tagName === "HTML") {
        path.push(document);
        path.push(window);
        return path;
      }
      el = el.parentElement;
    }
    return path;
  }
}
editor.on("click", function (event) {
  function getCherryModuleEventInfo(event) {
    let eventflag = "";
    const path = composedPath(event);

    for (let index = path.length - 1; index >= 0; index--) {
      const element = path[index];
      if (!element.getAttribute) {
        continue;
      }

      if (element.getAttribute("cm-eventflag")) {
        eventflag = element.getAttribute("cm-eventflag");
      }
    }
    return {
      eventflag,
    };
  }
  const value = getCherryModuleEventInfo(event);
  const targetDom = getCurrentChooseWorkItemTable();
  if (value.eventflag === "refreshWorkItemBlock") {
    editor.fire("refreshWorkItemBlockEvent", {
      editor: editor,
      workitemBlockNode: targetDom,
    });
  }
});

点击的元素是不是含有某个 class 类

const isWorkItemBlock = function (dom) {
  if (
    editor.dom.is(dom, "div") &&
    editor.dom.hasClass(dom, "cherry-choose-workitem")
  ) {
    return true;
  }
  return false;
};

注册悬浮菜单栏

editor.addCommand("changeWorkItemBlock", function (ui, type) {
  if (type === void 0) {
    type = defaultType;
  }
});
editor.addCommand("showWorkItemBlock", function (ui, { key, targetDom }) {
  if (key == "show") {
    editor.fire("createWorkItemShowFileds", {
      editor: editor,
      workitemBlockNode: targetDom,
    });
  } else if (key == "change") {
    editor.fire("changeWorkItemShowFileds", {
      editor: editor,
      workitemBlockNode: targetDom,
    });
  }
});

// 注册bubble menu 按钮组里的具体按钮
Object.keys(defaultList).forEach((key) => {
  editor.ui.registry.addToggleButton(`ch-workitem__${key}`, {
    text: defaultList[key].name,
    tooltip: `${defaultList[key].name}`,
    onAction() {
      const targetDom = getCurrentChooseWorkItemTable();
      editor.execCommand("showWorkItemBlock", true, { key, targetDom });
    },
  });
});
editor.ui.registry.addToggleButton("ch-workitem__delete", {
  icon: workitemList.delete.iconName,
  tooltip: "workitem Block delete",
  onAction: function () {
    editor.execCommand("removeWorkItemBlock");
  },
});

注册 bubble menu 按钮组

editor.ui.registry.addContextToolbar("chooseworkitem_toolbar", {
  predicate: function (node) {
    return isWorkItemBlock(node);
  },
  items: editor.getParam("chooseworkitem_toolbar", defaultBubbleMenu),
  position: "node",
  scope: "node",
});

删除点击的元素

// 获取光标所在的业务对象表格
const getCurrentChooseWorkItemTable = function () {
  const currentNode = editor.selection.getNode();
  let targetDom = null;
  if (
    editor.dom.is(currentNode, "div") &&
    editor.dom.hasClass(currentNode, "cherry-choose-workitem")
  ) {
    targetDom = currentNode;
  } else {
    const parentDom = editor.dom.getParent(
      currentNode,
      "div.cherry-choose-workitem"
    );
    if (parentDom) {
      targetDom = parentDom;
    }
  }
  if (!targetDom || !editor.dom.hasClass(targetDom, "cherry-choose-workitem")) {
    return false;
  }
  return targetDom;
};
// 删除业务对象表格
const remove = function () {
  const targetDom = getCurrentChooseWorkItemTable();
  editor.dom.remove(targetDom);
};

dom 的一些操作

dom.domQuery
var test = tinymce.dom.$("p").attr("attr", "value").addClass("class");

获取当前元素的个数

dom.domUtil
tinymce.activeEditor.dom.select("div.cherry-choose-workitem") &&
  editor.dom.select("div.cherry-choose-workitem").length;

设置某元素的样式

dom.domUtil
setStyle;

tinymce.activeEditor.dom.setStyle(
  tinymce.activeEditor.dom.select("p"),
  "background-color",
  "red"
);

setStyles;
// Sets styles on all paragraphs in the currently active editor
tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select("p"), {
  "background-color": "red",
  color: "green",
});

// Sets styles to an element by id in the current document
tinymce.DOM.setStyles("mydiv", { "background-color": "red", color: "green" });

遇到的问题

为了加快页面载入速度就要首先解决载入文件过多的问题,而大部分时间用户并不需要每次打开页面都先加载一遍 editor 的核心文件,而 editor 本身也要按需加载内容.把源码下载下来,做成异步组件。 导入的过程遇到 theme.js 路径不对的问题。 在默认配置下, tinymce 载入的 theme 的路径居然是这个

Request URL:<http://localhost:8080/themes/modern/theme.js>

改成 cdn 路径

window.tinymce.baseURL = "http://cdn.xxx.com/static/tinymce";

然后把 plugins 包和 tinyMce 主体包在不阻塞页面加载的情况下,做个懒加载提前缓存好文件方便后面使用,而组件本身在挂载前做个监听 window.tinymce 全局变量的方法,然后 cdn 控制下文件的过期时间即可。

本地开发环境,谷歌和火狐浏览器都能完美兼容,打包后谷歌浏览器没问题,火狐浏览器存在问题,报的是 ip+/themes/modern/theme.js 找不见。

解决方法:

在当前组件配置 baseURL:

判断开发环境还是生产环境,改变引用资源的相对路径,保证富文本能加载到所需资源文件.

if (process.env.BASE_API.indexOf(‘openManage’) >= 0) {

window.tinymce.baseURL = ‘./static/js’;

}

难点

  1. 编辑器在弹出框中无法获取焦点:

当将 TinyMCE 嵌入到弹出框(如模态框)中时,可能会出现编辑器无法获取焦点或初始化失败的情况。

解决方案:

延迟初始化: 使用 v-if 或类似的条件渲染指令,确保在弹出框显示时才初始化 TinyMCE 编辑器。

  <TinyMceEditor v-if="isVisible" :content="content" />
  1. 插入视频被图片占位,视频无法编辑播放:

在编辑器中插入视频后,可能会出现视频被图片占位,且无法在编辑模式下播放的问题。

解决方案:

通过自定义 video_template_callback 回调方法,可以解决视频被图片占位的问题。

具体方法是:

  1. 修改 media 插件源码:

找到 tinymce/plugins/media/ 目录下的 plugin.js 文件,定位到 createPreviewIframeNode 函数,将其代码替换为以下内容:

var createPreviewIframeNode = function (editor, node) {
  var previewWrapper;
  var previewNode;
  var shimNode;
  var name = node.name;
  previewWrapper = new global$8("span", 1);
  previewWrapper.attr({
    contentEditable: "false",
    style: node.attr("style"),
    "data-mce-object": name,
    class: "mce-preview-object mce-object-" + name,
  });
  retainAttributesAndInnerHtml(editor, node, previewWrapper);
  previewNode = new global$8(name, 1);
  previewNode.attr({
    src: node.attr("src"),
    controls: "controls", // 新增
    allowfullscreen: node.attr("allowfullscreen"),
    style: node.attr("style"),
    class: node.attr("class"),
    width: node.attr("width"),
    height: node.attr("height"),
    frameborder: "0",
  });
  shimNode = new global$8("span", 1);
  shimNode.attr("class", "mce-shim");
  previewWrapper.append(previewNode);
  previewWrapper.append(shimNode);
  return previewWrapper;
};
  1. 修改判断逻辑: 找到以下代码块并注释掉(大概在 1137-1145 行之间):

/** if (node.name === 'iframe' && Settings.hasLiveEmbeds(editor) && global$1.ceFalse) {
      if (!isWithinEphoxEmbed(node)) {
        node.replace(createPreviewIframeNode(editor, node));
      }
    } else {
      if (!isWithinEphoxEmbed(node)) {
        node.replace(createPlaceholderNode(editor, node));
      }
    } **/

然后修改判断逻辑为:

if (
  (node.name === "iframe" || node.name === "video") &&
  Settings.hasLiveEmbeds(editor) &&
  global$1.ceFalse
) {
  if (!isWithinEphoxEmbed(node)) {
    node.replace(createPreviewIframeNode(editor, node));
  }
} else {
  if (!isWithinEphoxEmbed(node)) {
    node.replace(createPlaceholderNode(editor, node));
  }
}

通过以上修改,可以有效解决在编辑器中插入视频后被图片占位,且无法在编辑模式下播放的问题。

最后更新于

这有帮助吗?