更新记录
1.0.0(2026-07-06) 下载此版本
核心能力:
- ✨ @提及:输入
@自动唤起用户选择面板,选择后插入不可编辑的 @原子标签,支持远程搜索过滤 - ✨ #话题:输入
#自动唤起话题选择面板,选择后插入不可编辑的 #原子标签,支持远程搜索过滤 - ✨ 图片插入:通过
insertImage()API 插入图片,支持 contenteditable 内渲染和反显 - ✨ 表情面板:通过
#emoji插槽自定义表情面板,与编辑器完全解耦 - ✨ 结构化消息输出:send 事件产出的 message 使用不可见字符编码,后端可直接存储;同时返回结构化 model,前端可直接反显渲染
- ✨ v-model 双向绑定:外部可随时读写编辑器内容,setValue 自动还原 @/#/图片
- ✨ 字数统计:实时显示当前字数 / 上限,超限时数字变红并阻止发送
- ✨ 发送防抖:loading prop 控制发送中状态 + 300ms 内置防抖,防止重复提交
- ✨ CSS 变量定制:11 个 CSS 变量全覆盖,业务方可按需覆写适配主题色
- ✨ 三端统一 API:props / events / slots / ref 方法在 H5、App、小程序三端行为一致
- ✨ 小程序 @/# 自动检测:diff 算法实时追踪 textarea 输入,支持末尾/中间/粘贴场景的 @/# 触发
- ✨ 空内容/超限 toast 提示:内容为空或超出字数限制时自动提示用户
- ✨ 多实例安全:data-uid 属性选择器 + class 兜底,多个编辑器实例互不干扰
平台兼容性
uni-app(3.7.13)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | √ | √ | - | √ | × | 4.4 | 12 | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | - | - | - | - | - | - | - | - | - | - | - |
l-editor 富文本评论编辑器
基于 uni-app (Vue 3) 的富文本输入组件,renderjs 驱动,支持 H5 和 App 端。 提供 @提及、#话题、图片插入、结构化消息输出、反显解析。
平台支持
| 平台 | 支持 |
|---|---|
| H5 (Web) | ✅ 完整支持 (renderjs) |
| App (Android/iOS/Harmony) | ✅ 完整支持 (renderjs) |
| 小程序 | ✅ 降级支持 (textarea + 自动 @/# 检测) |
小程序降级说明:组件内部通过
#ifdef MP条件编译自动切换为<textarea>实现。用户在 textarea 中输入@或#时会自动通过 diff 检测唤起对应候选面板,选择后自动替换为原子标签。图片暂不支持(转为[图片]占位文),对外 API(props / events / expose)与 H5/App 端保持一致。发送产出的message编码格式完全兼容。
安装
将 uni_modules/l-editor 放入项目的 uni_modules 目录,通过 easycom 自动引入。
快速开始
<template>
<l-editor v-model="content" @send="handleSend" @search-change="onSearch">
<!-- @ 面板 -->
<template #at="{ show, keyword, , close }">
<view v-show="show">
<view v-for="u in filteredUsers" :key="u.id" @touchend.prevent="(u)" @click.stop="(u)">
{{ u.name }}
</view>
<button @click="close">关闭</button>
</view>
</template>
<!-- # 话题面板 -->
<template #hot="{ show, keyword, , close }">
<view v-show="show">
<view v-for="t in filteredTags" :key="t.id" @touchend.prevent="(t)" @click.stop="(t)">
#{{ t.name }}
</view>
<button @click="close">关闭</button>
</view>
</template>
</l-editor>
</template>
<script setup>
const onSearch = ({ type, keyword }) => {
// type: 'ate' | 'tag' — 根据 keyword 远程搜索并更新面板列表
};
const handleSend = (data) => {
console.log(data.message); // '你好{at_0}' — 编码后的下发格式
console.log(data.model); // ParsedModel[] — 结构化模型
console.log(data.ateUsers); // [{ value:'@张三', id:1 }]
console.log(data.tags); // [{ value:'#前端开发', id:1 }]
};
</script>
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| modelValue | ParsedModel[] | undefined | v-model 绑定值 |
| placeholder | string | "说点什么..." | 占位文本 |
| maxNum | number | 300 | 最大字数 |
| loading | boolean | false | 发送中状态 |
| disabled | boolean | false | 禁用状态 |
| clearOnSend | boolean | true | 发送后自动清空 |
| sendText | string | "发送" | 发送按钮文字 |
Events
| 事件 | 参数 |
|---|---|
| send | { model, message, num, ateUsers, tags } |
| change | { model, message, num } |
| update:modelValue | ParsedModel[] |
| input | { model, message, num } |
| search-change | { type, trigger, keyword, content } |
Slots
| 插槽 | 作用域参数 |
|---|---|
| #at | { show, keyword, content, , close } |
| #hot | { show, keyword, content, , close } |
| #emoji | { show, , close } |
暴露方法
| 方法 | 说明 |
|---|---|
| focus() | 编辑器获取焦点 |
| clear() | 清空编辑器和面板 |
| getValue() | 获取当前值快照 |
| setValue(model) | 回填结构化内容 |
| insertAtom(atom) | 插入内容 { type, value, id? } |
| insertText(text) | 插入纯文本 |
| insertImage(url, id?) | 插入图片 |
图片上传集成示例
<script setup>
const uploadAndInsert = async () => {
const tempFile = await uni.chooseImage({ count: 1, sourceType: ["album", "camera"] });
const filePath = tempFile.tempFilePaths[0];
// 上传到服务器
const uploadRes = await uni.uploadFile({
url: "https://your-api.com/upload",
filePath,
name: "file",
});
const data = JSON.parse(uploadRes.data);
// 将返回的 CDN URL 插入编辑器
refEditor.value?.insertImage(data.url, data.id || Date.now());
};
</script>
CSS 变量
组件颜色使用 CSS 变量,方便业务方按需覆写以适配自己的主题色:
| CSS 变量 | 默认值 | 说明 |
|---|---|---|
--l-editor-bg |
#fff |
编辑器背景 |
--l-editor-text |
#333 |
正文颜色 |
--l-editor-text-muted |
#999 |
辅助文字 |
--l-editor-placeholder |
#808080 |
占位文字 |
--l-editor-link |
#09408e |
@提及颜色 |
--l-editor-primary |
#07c160 |
主题色/#话题 |
--l-editor-danger |
#f56c6c |
警告/超限色 |
--l-editor-disabled-bg |
#f5f5f5 |
禁用态背景 |
--l-editor-btn-disabled-bg |
#e0e0e0 |
按钮禁用背景 |
--l-editor-btn-disabled-color |
#999 |
按钮禁用文字 |
--l-editor-btn-active-color |
#fff |
按钮激活文字 |
覆写示例:
:root {
--l-editor-primary: #ff6b35;
--l-editor-link: #1a73e8;
}
类型
interface ParsedModel {
type: "text" | "ate" | "tag" | "image";
id?: number | string;
value: string;
}
interface AtomSelectItem {
name: string;
id: number | string;
}
消息编码格式
发送时 message 字段使用不可见分隔符 包裹占位符:
"你好{at_0} 欢迎加入{tag_1}"
反显时用正则 /\{(at|tag|image)(?:_(\d+))?\}/g 解析还原。
APP 端注意事项
manifest.json需配置softinputMode: "adjustResize"- 面板选择项需同时绑定
@touchend.prevent和@click.stop

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(0)
下载 12
赞赏 0
下载 12395609
赞赏 1930
赞赏
京公网安备:11010802035340号