Skip to content

x-editor-u 富文本编辑(单独付费)

多端富文本编辑容器。接口对齐微信 editor / EditorContext,内容走自有受限 HTML(不跟微信 Delta 对齐)。

兼容性

HarmonyIOSAndroidWEB小程序
支持支持支持支持支持

App:系统编辑控件。Web:宿主 DIV 挂 Quill。微信:官方 editor

调用

vue
<x-editor-u
  ref="editorRef"
  placeholder="请输入"
  :height="280"
  @ready="onReady"
  @input="onInput"
  @statuschange="onStatus"
  @linkchange="onLink"
></x-editor-u>
uts
const proxy = getCurrentInstance()!.proxy!

function getEditor() : XEditorUComponentPublicInstance | null {
	return proxy.$refs["editorRef"] as XEditorUComponentPublicInstance | null
}

const editor = getEditor()
if (editor == null) return
editor.format("bold", "true")
const htmlOpts : XEditorSetContentsOptions = { html: "<p>hello</p>" }
editor.setContents(htmlOpts)
const readOpts : XEditorGetContentsOptions = {
	success: (res : XEditorContents) : void => {
		console.log(res.html, res.text)
	}
} as XEditorGetContentsOptions
editor.getContents(readOpts)

参数

对齐微信 <editor> 属性。

字段说明默认
placeholder占位文字请输入
readOnly只读false
dark深色false
height整体高度 px(含内置工具栏)350
html初始 / 受控 HTML""
showToolbar是否显示内置工具栏true
showLinkToolbar光标在链接上时显示编辑条。微信小程序无 insertLink,工具栏链接入口仅该端隐藏true
showImgSize点选图片显示宽高true
showImgToolbar点选图片显示删除true
showImgResize点选图片拖角缩放(鸿蒙为放大/缩小按钮)。App 端点选图片自动收键盘方便拖手柄,已选中的图片再点一下取消选中true
enterkeyhint键盘回车键文案enter
confirmHold回车是否保持键盘true

事件

对齐微信 bindready / bindfocus / bindblur / bindinput / bindstatuschange

名称说明
ready内核就绪
focus / blur{ html, text }
input{ html, text }
statuschange当前光标/选区样式,供工具栏高亮。含 bold italic underline strike link linkText linkIndex linkLength
linkchange进入或离开链接时触发,{ href, text, index, length },方便自绘工具栏填链接
mentionclick点击提及芯片时触发,{ id, name }。微信端提及为纯文本,不触发
tableclick点击表格时触发,{ id, rows, cols, cells }。目前仅 Web 端触发(App 端表格为行内编辑,点击即进入单元格);微信端不支持表格,不触发
chooseimage内置工具栏点插图。宿主选图、上传后调用 insertImage({ src })
error失败

方法

对齐微信 EditorContext

format setContents getContents insertImage insertText insertDivider insertLink updateLink removeLink insertMention insertTable updateTable removeTable undo redo clear removeFormat blur scrollIntoView getSelectionText getHistoryState getSelection setSelection deleteText getBounds

另提供同步读取:getHtml getText

不实现微信 insertCustomBlock(自定义区块仅官方内核支持)。delta 不作为跨端数据。微信端 getSelection / setSelection / deleteText / getBounds 受官方类型限制,能力以各端内核为准。

format

名对齐微信 EditorContext.format

bold italic underline strike ins script color backgroundColor fontSize fontWeight fontStyle align direction header list indent textIndent letterSpacing lineHeight textDecoration margin marginTop marginBottom marginLeft marginRight padding paddingTop paddingBottom paddingLeft paddingRight

另支持历史别名 background(等于 backgroundColor)和 link。kebab-case 写法(如 margin-left)会自动归一。

margin / padding 为段落级样式,再次应用相同值会取消。各端能力:

名称微信WebiOSAndroid鸿蒙
marginLeft / paddingLeft原生支持支持支持(缩进实现)支持(缩进实现)
marginTop / paddingTop原生支持支持(段前距)不支持不支持
marginBottom / paddingBottom原生支持支持(段后距)不支持不支持
margin / padding(四向简写)原生支持不支持不支持不支持

list 取值:ordered / bullet / check(待办)。indent 取值:-1 / +1script 取值:sub / super。再点同一样式会取消(与微信一致)。微信官方只有 check 一种待办,且基础库 3.9.3+ 常画成黑点(与无序相同),小程序工具栏因此只保留有序/无序。

列表按整行加前缀,回车自动续写(有序 1.2.)。空列表项再回车退出列表。

insertMention

插入提及芯片 @名字,整体删除(退格从中间删会整块删掉),点击触发 mentionclick

uts
const opts : XEditorInsertMentionOptions = {
	name: "张三",
	id: "1001",
	color: "#1677ff",        // 可选,文字色
	background: "#e6f3ff",   // 可选,背景色
	radius: "4px"            // 可选,圆角;Android/iOS 绘制为方角,仅参与 HTML 回写
}
editor.insertMention(opts)

HTML 回写为 <span data-mention-id="1001" data-mention-name="张三" style="color:...;background-color:...;border-radius:...">@张三</span>setContents 灌入同构 HTML 会还原成芯片。微信端降级为纯文本 @名字(官方内核无自定义节点),无样式、无点击事件。

表格(insertTable / updateTable / removeTable)

类 iOS 备忘录的表格交互:

  • 工具栏表格按钮直接插入 2x2 空表,不弹输入框。App 端插入后不自动聚焦格子、不弹键盘,用户自行点格子开始输入。
  • 表格在正文里行内直接编辑(全端一致,无弹层):点格子即打字。
  • 光标在某格时,该列顶部、该行左侧出现 ••• 手柄:点一下选中整列/整行(高亮)并收起键盘,选中态再点弹出菜单——删除列/右侧加一列、删除行/下方加一行。
  • 光标在最后一格再按回车,自动在下方续一行。
uts
const opts : XEditorInsertTableOptions = {
	rows: 2,
	cols: 2,
	cells: [["姓名", "分工"], ["张三", "前端"]]  // 可选,缺省为空格子
}
editor.insertTable(opts)
// 之后可用 updateTable({ id, cells }) 改内容,removeTable({ id }) 删除

HTML 回写为 <table class="x-table"><tr><td>…</td></tr></table>setContents 灌入同构 HTML 会还原。上限 20 行 x 8 列,单元格为纯文本。各端形态:

呈现编辑方式
WebQuill 原生表格表格内直接打字 + 行内 ••• 手柄增删行列 + 末格回车加行
Android / iOS行内真实网格(块式内核:正文与表格块混排)格内直接打字 + ••• 手柄增删行列 + 末格回车加行
鸿蒙行内真实网格(块式内核:多 RichEditor 文本块 + 表格块混排)同上
微信不支持(官方内核无表格),工具栏隐藏按钮,API 走 fail(错误码 3006)-

insertImage 对齐微信:src alt width height nowrap extClass data。默认插图后换行,nowrap: true 不换行。

点选图片后出现尺寸、删除和缩放,对齐微信 show-img-size / show-img-toolbar / show-img-resize。App / Web 自绘浮层;微信走官方控件;鸿蒙因无可靠坐标,用编辑区顶栏放大、缩小、删除。设为 false 可关闭。

内置工具栏默认显示,布局、配色和 Remix 图标对齐 tmx-ui x-editorx-icon / x-popover)。字号、颜色、标题用同样的气泡菜单。多出来的缩进、上下标、分割线、插图、链接、撤销重做、去格式沿用同一组按钮样式。微信小程序没有可用的 insertLink,该端隐藏链接按钮和编辑条;App / Web 正常显示。光标落在已有样式上时对应按钮高亮。点进链接会出现编辑条。点插图会触发 chooseimage,由宿主选图、上传后调用 insertImage({ src }),插件不内置上传。:show-toolbar="false" 后只留编辑面。工具栏用 view 点击,避免抢焦点丢掉选区。需要工程里已有 tmx-ui

更新日志

0.1.5(2026-08-28)

  • 修复 APP 端 native-view 拿不到高度导致内容区空白:宿主原本靠 flex: 1 撑开,而原生层拿不到由 flex 分配出来的尺寸,现改为把 props.height 直接写成固定像素挂在 native-view 上。根节点高度已固定,body 是唯一的在流子层,链接条是 position: absolute 不占高,因此两者相等
  • 同一条规则下 flex: 1 隐含的 flex-basis: 0% 会盖掉 height,APP 端补一段条件编译把 flex-grow / flex-shrink / flex-basis 一并退回,确保写上去的像素值生效。本仓其余 native-view 插件(swiper / 天地图 / 相机 / pdf / 人脸 / skia / html / model3d)用的都是固定像素或 100%,只有这里是 flex

0.1.4(2026-08-27)

  • reflowAfterResize 里的强制排版从 containerRef?.layoutIfNeeded() 收窄为 scrollHost?.layoutIfNeeded()。对 bindIOSView 绑定的宿主调 layoutIfNeeded 会把 uni-app x 的页面布局拽进一次计划外的同步排版,与它自己驱动的 frame 布局撞车。宿主直接子层的 frame 已在 layoutHostChildren 中同步摆好,这里只需让滚动容器内部的宽度约束落地

0.1.3(2026-08-27)

  • 修复 iOS 反复进出后整页空白(导航栏还在、内容区永久不恢复):宿主的直接子层(滚动容器、占位 label、图片手柄层、底部静态工具条)全部改回手排 frame,由 XEditorHostView.layoutSubviews 每趟按当前 bounds 重排。bindIOSView 绑定的视图由 uni-app x 的布局引擎按 frame 摆放,在它身上激活 Auto Layout 约束会把宿主拽进约束引擎、与外层 frame 布局打架,整页内容因此塌掉。0.1.1 把这几处从 frame 改成约束正是这个问题的来源,症状也从「重进就好」恶化成「永不恢复」
  • 更深层自成一体的子树(滚动容器内的 UIStackView、工具条内部排布)不碰宿主,保持约束布局不变
  • 宿主补 clipsToBounds

0.1.2(2026-08-27)

  • 修复 iOS 偶发整块空白的主因:文本视图改为在创建时就装配 TextKit 1 排版栈(UITextView(frame:textContainer:) + 显式 NSTextStorage / NSLayoutManager / NSTextContainer)。本实现整体建立在 textStorage / layoutManager / boundingRect(forGlyphRange:in:) 之上,而 iOS 16+ 的 UITextView 默认起 TextKit 2,首次访问这些属性会触发运行时降级(原生日志:UITextView ... is switching to TextKit 1 compatibility mode because its layoutManager was accessed)。降级会把已建好的排版栈整个换掉,落在已上屏、正在布局的视图上就是一块空白;降级时机取决于哪条代码路径先访问,所以表现为偶发

0.1.1(2026-08-27)

  • 修复 iOS 反复进出页面后整块空白:XEditorHostView.layoutSubviewsonLayout 尚未绑定时也会把尺寸记进 lastSizeattach 是在 bindIOSView 之后才挂回调的,中间只要跑过一次布局,这次尺寸就被吞掉,而之后尺寸不再变化就永远不会再回调,正文块一直停在零宽排出来的版上且不可恢复。现在回调未绑定时不记 lastSize,同时把 onLayout 提到 attach 最前面绑定
  • 补齐 attach 时宿主已有尺寸的分支:原来只发 ready 不重排,现在走完整的 reflowAfterResize
  • 修复销毁不彻底:destroy() 原本只清了 9 个回调里的 6 个,漏掉的 onMentionCallback / onTableCallback / onToolbarCallback 会留下 XEditorU → XEditorNative → 闭包 → XEditorU 的循环引用,UTSiOS.destroyInstance 只摘桥接表、断不了这一环
  • destroy() 主动收键盘,避免带焦点离开页面时正文块仍是 first responder;textViewDidEndEditing 增加 destroyed 守卫,不再向已卸载的 uvue 抛 blur
  • destroy() 清空 _options(内含 keepAlive 住的 uvue 回调)
最近更新