2.8.42 • Published 2 years ago

ctoweb-engine v2.8.42

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

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 等前端库渲染。复杂架构轻松应对
  • 内置协同编辑方案,轻量配置即可使用
  • 兼容大部分最新移动端浏览器

插件

描述
@aomao/toolbar工具栏, 适用于 React
@aomao/toolbar-vue工具栏, 适用于 Vue3
@aomao/plugin-alignment对齐方式
@aomao/plugin-backcolor背景色
@aomao/plugin-bold加粗
@aomao/plugin-code行内代码
@aomao/plugin-codeblock代码块, 适用于 React
@aomao/plugin-codeblock-vue 代码块, 适用于 Vue3
@aomao/plugin-fontcolor前景色
@aomao/plugin-fontfamily字体
@aomao/plugin-fontsize字体大小
@aomao/plugin-heading标题
@aomao/plugin-hr分割线
@aomao/plugin-indent缩进
@aomao/plugin-italic斜体
@aomao/plugin-link链接, 适用于 React
@aomao/plugin-link-vue链接, 适用于 Vue3
@aomao/plugin-line-height行高
@aomao/plugin-mark标记
@aomao/plugin-mention提及
@aomao/plugin-orderedlist有序列表
@aomao/plugin-paintformat格式刷
@aomao/plugin-quote引用块
@aomao/plugin-redo重做
@aomao/plugin-removeformat移除样式
@aomao/plugin-selectall全选
@aomao/plugin-status状态
@aomao/plugin-strikethrough 删除线
@aomao/plugin-sub下标
@aomao/plugin-sup上标
@aomao/plugin-tasklist任务列表
@aomao/plugin-underline下划线
@aomao/plugin-undo撤销
@aomao/plugin-unorderedlist无序列表
@aomao/plugin-image图片
@aomao/plugin-table表格
@aomao/plugin-file文件
@aomao/plugin-mark-range标记光标, 例如: 批注.
@aomao/plugin-math数学公式
@aomao/plugin-video视频

快速上手

安装

编辑器由 引擎工具栏插件 组成。引擎 为我们提供了核心的编辑能力。

使用 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,具体可以参考 Reactsite-ssr