ctoweb-engine v2.8.42
WuKong
使用浏览器提供的 contenteditable
属性让一个 DOM 节点具有可编辑能力。
引擎接管了浏览器大部分光标、事件等默认行为。
可编辑器区域内的节点通过 schema
规则,制定了 mark
inline
block
card
4 种组合节点,他们由不同的属性、样式或 html
结构组成,并对它们的嵌套进行了一定的约束。
通过 MutationObserver
监听编辑区域内的 DOM
树的改变,并生成 json0
类型的数据格式与 ShareDB 库进行交互,从而达到协同编辑的需要。
特性
- 开箱即用,提供几十种丰富的插件来满足大部分需求
- 高扩展性,除了
mark
inline
block
类型基础插件外,我们还提供card
组件结合React
Vue
等前端库渲染插件 UI - 丰富的多媒体支持,不仅支持图片和音视频,更支持插入嵌入式多媒体内容
- 支持 Markdown 语法
- 支持国际化
- 引擎纯 JavaScript 编写,不依赖任何前端库,插件可以使用
React
Vue
等前端库渲染。复杂架构轻松应对 - 内置协同编辑方案,轻量配置即可使用
- 兼容大部分最新移动端浏览器
插件
快速上手
安装
编辑器由 引擎
、工具栏
、插件
组成。引擎
为我们提供了核心的编辑能力。
使用 npm 或者 yarn 安装引擎包
$ npm install @aomao/engine
# or
$ yarn add @aomao/engine
使用
我们按照惯例先输出一个Hello word!
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
import { Avatar, message, Modal, Space } from 'ant-design-vue';
import Engine, { $, EngineInterface, isMobile } from '@aomao/engine';
import AmToolbar from '@aomao/toolbar-vue';
import AmLoading from './loading.vue';
import { cards, plugins, pluginConfig } from './config';
import OTClient from './ot-client';
import 'ant-design-vue/es/style';
export default defineComponent({
name: 'engine-demo',
components: {
Avatar,
Space,
AmLoading,
AmToolbar,
},
data() {
// toolbar 配置项
return {
items: isMobile
? [
['undo', 'redo'],
{
icon: 'text',
items: [
'bold',
'italic',
'strikethrough',
'underline',
'moremark',
],
},
[
{
type: 'button',
name: 'image-uploader',
icon: 'image',
},
'link',
'tasklist',
'heading',
],
{
icon: 'more',
items: [
{
type: 'button',
name: 'video-uploader',
icon: 'video',
},
{
type: 'button',
name: 'file-uploader',
icon: 'attachment',
},
{
type: 'button',
name: 'table',
icon: 'table',
},
{
type: 'button',
name: 'math',
icon: 'math',
},
{
type: 'button',
name: 'codeblock',
icon: 'codeblock',
},
{
type: 'button',
name: 'orderedlist',
icon: 'orderedlist',
},
{
type: 'button',
name: 'unorderedlist',
icon: 'unorderedlist',
},
{
type: 'button',
name: 'hr',
icon: 'hr',
},
],
},
]
: [
['collapse'],
['undo', 'redo', 'paintformat', 'removeformat'],
['heading', 'fontfamily', 'fontsize'],
[
'bold',
'italic',
'strikethrough',
'underline',
'moremark',
],
['fontcolor', 'backcolor'],
['alignment'],
[
'unorderedlist',
'orderedlist',
'tasklist',
'indent',
'line-height',
],
['link', 'quote', 'hr'],
],
};
},
setup() {
// 编辑器容器
const container = ref<HTMLElement | null>(null);
// 编辑器引擎
const engine = ref<EngineInterface | null>(null);
// 当前所有协作用户
const members = ref([]);
// 默认设置为当前在加载中
const loading = ref(true);
onMounted(() => {
// 容器加载后实例化编辑器引擎
if (container.value) {
//实例化引擎
const engineInstance = new Engine(container.value, {
// 启用的插件
plugins,
// 启用的卡片
cards,
// 所有的卡片配置
config: pluginConfig,
});
// 设置显示成功消息UI,默认使用 console.log
engineInstance.messageSuccess = (msg: string) => {
message.success(msg);
};
// 设置显示错误消息UI,默认使用 console.error
engineInstance.messageError = (error: string) => {
message.error(error);
};
// 设置显示确认消息UI,默认无
engineInstance.messageConfirm = (msg: string) => {
return new Promise<boolean>((resolve, reject) => {
Modal.confirm({
content: msg,
onOk: () => resolve(true),
onCancel: () => reject(),
});
});
};
//卡片最大化时设置编辑页面样式
engineInstance.on('card:maximize', () => {
$('.editor-toolbar')
.css('z-index', '9999')
.css('top', '55px');
});
engineInstance.on('card:minimize', () => {
$('.editor-toolbar').css('z-index', '').css('top', '');
});
// 默认编辑器值,为了演示,这里初始化值写死,正式环境可以请求api加载
const value =
'<strong>Hello</strong>,<span style="color:red">am-editor</span>';
// 使用协同编辑,需要安装 mongodb 数据库,并且配置 ot-server/client 中的数据库连接,最后 yarn start 启动 ot-server 服务
let isOt = false;
if (isOt) {
// 实例化协作编辑客户端
const ot = new OTClient(engineInstance);
// 获取当前用户,正式环境可以传Token到 ot-server 中验证
const memberData = localStorage.getItem('member');
const currentMember = !!memberData
? JSON.parse(memberData)
: null;
// 连接协同服务端,如果服务端没有对应docId的文档,将使用 value 初始化
ot.connect(
`ws://127.0.0.1:8080${
currentMember ? '?uid=' + currentMember.id : ''
}`,
'demo',
value,
);
ot.on('ready', (member) => {
// 为了演示,保存当前会员信息
if (member)
localStorage.setItem(
'member',
JSON.stringify(member),
);
loading.value = false;
});
//用户加入或退出改变
ot.on('membersChange', (currentMembers) => {
members.value = currentMembers;
});
} else {
// 非协同编辑,设置编辑器值,异步渲染后回调
engineInstance.setValue(value, () => {
loading.value = false;
});
}
// 监听编辑器值改变事件
engineInstance.on('change', (value) => {
console.log('value', value);
console.log('html:', engineInstance.getHtml());
});
engine.value = engineInstance;
}
});
onUnmounted(() => {
if (engine.value) engine.value.destroy();
});
return {
loading,
isMobile,
container,
engine,
members,
};
},
});
插件
引入 @aomao/plugin-bold
加粗插件
import Bold from '@aomao/plugin-bold';
把 Bold
插件加入引擎
//实例化引擎
const engine = new Engine(ref.current, {
plugins: [Bold],
});
卡片
卡片是编辑器中单独划分的一个区域,其 UI 以及逻辑在卡片内部可以使用 React、Vue 或其它前端库自定义渲染内容,最后再挂载到编辑器上。
引入 @aomao/plugin-codeblock
代码块插件,这个插件的 语言下拉框
使用 React
渲染,所以有区分。 Vue3
使用 @aomao/plugin-codeblock-vue
import CodeBlock, { CodeBlockComponent } from '@aomao/plugin-codeblock';
把 CodeBlock
插件和 CodeBlockComponent
卡片组件加入引擎
//实例化引擎
const engine = new Engine(ref.current, {
plugins: [CodeBlock],
cards: [CodeBlockComponent],
});
CodeBlock
插件默认支持 markdown
,在编辑器一行开头位置输入代码块语法```javascript
回车后即可触发。
工具栏
引入 @aomao/toolbar
工具栏,工具栏由于交互复杂,基本上都是使用 React
+ Antd
UI 组件渲染,Vue3
使用 @aomao/toolbar-vue
工具栏除了 UI 交互外,大部分工作只是对不同的按钮事件触发后调用了引擎执行对应的插件命令,在需求比较复杂或需要重新定制 UI 的情况下,Fork 后修改起来也比较容易。
import Toolbar, { ToolbarPlugin, ToolbarComponent } from '@aomao/toolbar';
把 ToolbarPlugin
插件和 ToolbarComponent
卡片组件加入引擎,它可以让我们在编辑器中可以使用快捷键 /
唤醒出卡片工具栏
//实例化引擎
const engine = new Engine(ref.current, {
plugins: [ToolbarPlugin],
cards: [ToolbarComponent],
});
渲染工具栏,工具栏已配置好所有插件,这里我们只需要传入插件名称即可
return (
...
{
engine && (
<Toolbar
engine={engine}
items={[
['collapse'],
[
'bold',
],
]}
/>
)
}
...
)
更复杂的工具栏配置请查看文档 https://editor.aomao.com/zh-CN/config/toolbar
开发
React
需要在 am-editor 根目录
site-ssr
ot-server
中分别安装依赖
//依赖安装好后,只需要在根目录执行以下命令
yarn ssr
packages
引擎和工具栏plugins
所有的插件site-ssr
所有的后端 API 和 SSR 配置。使用的 egg 。在 am-editor 根目录下使用 yarn ssr 自动启动site-ssr
ot-server
协同服务端。启动:yarn start
启动后访问 localhost:7001
Vue3
只需要进入 examples/vue 目录安装依赖
//依赖安装好后,在 examples/vue 目录执行以下命令
yarn serve
在 Vue 运行环境中,默认是安装的已发布到 npm 上的代码。如果需要修改引擎或者插件的代码后立即看到效果,我们需要做以下步骤:
- 删除 examples/vue/node_modules/@aomao 文件夹
- 删除 examples/vue/node_modules/vue 文件夹。因为有插件依赖了 Vue,所以 Vue 的包会在项目根目录中安装。如果不删除 examples/vue 中的 Vue 包,和插件的 Vue 包不在一个环境中,就无法加载插件
- 在 am-editor 根目录下执行安装所有依赖命令,例如:
yarn
- 最后在 examples/vue 中重新启动
Vue
案例中没有配置任何后端 API,具体可以参考 React
和 site-ssr
2 years ago