当前位置: 首页 > news >正文

Uni-app + Vue3+editor富文本编辑器完整实现指南

前言

本文将完整展示如何在Uni-app框架下使用Vue3实现一个功能完善的富文本编辑器。我们将从组件结构、功能实现到样式设计,全面介绍这个编辑器的开发过程。

<template><view class="editor_box"><!-- 操作栏 --><view class="tooltar" v-if="showToolbar"><view v-for="(item, index) in ToolBarList" @tap="item.fnc()" :key="index"><image :src="item.icon" class="img" /></view></view><!-- 编辑器主体 --><editor:id="`${id}`":placeholder="'请输入内容'"@ready="editorReady"@input="handleEditorChange"style="color: black; padding: 12rpx"class="ql-container"/></view>
</template><script setup lang="ts">
import { ref, nextTick, getCurrentInstance } from "vue";// 组件属性定义
interface EditorProps {showToolbar?: boolean;  // 是否显示工具栏defaultContent?: string; // 默认内容id?: string;  // 编辑器ID
}// 格式化类型定义
type formatType = "bold" | "italic" | "header" | "align" | "list";
type formatVal = "h1" | "h2" | "h3" | "left" | "right" | "ordered" | "bullet";// 设置默认属性
const props = withDefaults(defineProps<EditorProps>(), {showToolbar: true,
});// 工具栏按钮配置
const ToolBarList = [{ fnc: () => insertImage(), icon: '图片路径' },{ fnc: () => formatStyle("italic"), icon: '图片路径' },{ fnc: () => formatStyle("bold"), icon: '图片路径' },{ fnc: () => formatStyle("header", "h1"), icon: '图片路径' },{ fnc: () => formatStyle("header", "h2"), icon: '图片路径' },{ fnc: () => formatStyle("header", "h3"), icon: '图片路径' },{ fnc: () => formatStyle("align", "left"), icon: '图片路径' },{ fnc: () => formatStyle("align", "right"), icon: '图片路径' },{ fnc: () => formatStyle("list", "ordered"), icon: '图片路径' },{ fnc: () => formatStyle("list", "bullet"), icon: '图片路径' },{ fnc: () => undo(), icon: '图片路径' },
];// 定义事件
const emits = defineEmits(["editorChange"]);// 响应式变量
const editorCtx = ref<any>(null); // 编辑器上下文
const isEditorReady = ref(false); // 编辑器是否准备就绪
const instance = getCurrentInstance() as any;// 编辑器准备就绪回调
const editorReady = () => {nextTick(() => {const query = uni.createSelectorQuery().in(instance?.proxy);query.select(`#${props?.id}`).context((res: any) => {if (res?.context) {editorCtx.value = res?.context;isEditorReady.value = true;if (props.defaultContent) {setEditorContent(props.defaultContent);}} else {console.error("获取编辑器上下文失败", res);}}).exec();});
};// 设置编辑器内容
const setEditorContent = (html: string) => {return new Promise((resolve) => {if (!editorCtx.value) return resolve(false);editorCtx.value.setContents({html,success: () => {console.log("内容回显成功");resolve(true);// 初始化,向父组件赋值setTimeout(() => {handleEditorChange();}, 500);},fail: (err: any) => {console.error("内容回显失败:", err);resolve(false);},});});
};// 撤销操作
const undo = () => {if (editorCtx.value) {editorCtx.value.undo();}
};// 格式化文本样式
const formatStyle = (type: formatType, val?: formatVal) => {editorCtx.value.format(type, val);
};// 插入图片
const insertImage = () => {uni.chooseImage({count: 1,sizeType: ["compressed"],sourceType: ["album", "camera"],success: (res) => {const filePath = res.tempFilePaths[0];// 方案1:直接使用本地路径(简单测试)if (editorCtx.value) {editorCtx.value.insertImage({src: filePath,width: "100%",success: () => {uni.showToast({ title: "插入成功", icon: "none" });},});}},fail: (err) => {console.log("选择图片失败", err);},});
};// 编辑器内容变化处理
const handleEditorChange = (e?: any) => {// 获取编辑器内容getEditorContent().then((content) => {// 向父组件传递内容emits("editorChange", content);});
};// 获取编辑器内容
const getEditorContent = () => {return new Promise((resolve) => {if (editorCtx.value) {editorCtx.value.getContents({success: (res: any) => {resolve({html: res?.html,text: res?.text,});},});}});
};
</script><style scoped lang="scss">
.editor_box {width: 100%;height: auto;box-sizing: border-box;background: #f7f7f7;border-radius: 12rpx;.ql-container {box-sizing: border-box;padding: 24rpx 32rpx;width: 100%;min-height: 30vh;max-height: 70vh;height: 100%;margin-top: 20rpx;overflow-y: scroll;line-height: 1.5;}
}.tooltar {display: flex;align-items: center;justify-content: space-between;box-sizing: border-box;padding: 10rpx 12rpx;border-bottom: 1rpx solid #ebebeb;view {.img {width: 24rpx;height: 24rpx;}}
}
</style>

关键功能解析

1. 编辑器初始化流程

  1. 组件挂载后触发editorReady事件

  2. 使用uni.createSelectorQuery()获取编辑器实例

  3.  //注意事项这是vue2写法 在vue3中不能哪来使用uni.createSelectorQuery().select('#editor').context((res) => 
    {               this.editorCtx = res.context           }).exec()//因为是组件化封装,为了确保编译到微信小程序可以查询出来实例必须采用下方写法
    const instance = getCurrentInstance() as any; const query = uni.createSelectorQuery().in(instance?.proxy); query.select(`#${props?.id}`).context((res: any) => {  console.log("🚀 ~ editorReady ~ res:", res);  if (res?.context) {     editorCtx.value = res?.context;    } else {     console.error("获取编辑器上下文失败", res); }    }).exec(); });​

  4. 将编辑器上下文保存到editorCtx变量中

  5. 如果提供了默认内容,调用setEditorContent进行内容回显

2. 工具栏功能实现

工具栏通过ToolBarList数组配置,每个按钮包含:

  • 图标资源

  • 点击事件处理函数

支持的功能包括:

  • 文本加粗/斜体

  • 设置标题级别(H1/H2/H3)

  • 文本对齐(左/右)

  • 列表样式(有序/无序)

  • 插入图片

  • 撤销操作

3. 内容同步机制

编辑器内容变化时:

  1. 触发@input事件调用handleEditorChange

  2. 通过getEditorContent获取当前内容

  3. 使用emits将内容传递给父组件

4. 图片插入实现

图片插入流程:

  1. 调用uni.chooseImage选择图片

  2. 获取图片临时路径

  3. 使用editorCtx.value.insertImage插入图片

  4. 设置图片宽度为100%自适应

使用说明

组件引入

<template><Editor :showToolbar="true" :defaultContent="content" @editorChange="onEditorChange"/>
</template><script setup>
import Editor from '@/components/Editor.vue';const content = ref('<p>正文内容</p>');const onEditorChange = (content) => {console.log('编辑器内容变化:', content);
};
</script>

总结

本文完整展示了基于Uni-app和Vue3的富文本编辑器实现方案。该编辑器具有以下特点:

  1. 功能完善:支持常见的文本编辑操作

  2. 响应式设计:适配不同尺寸屏幕

  3. 易于扩展:可以方便地添加新的工具栏功能

  4. 良好的交互体验:提供内容变化实时反馈

  5. 兼容h5和小程序平台(通过uni.createSelectorQuery().in(instance?.proxy)解决vue3版本组件化封装内部拿不到实例)

开发者可以根据实际需求,进一步扩展功能或调整样式,如添加更多格式化选项、实现图片上传到服务器等功能。

http://www.dtcms.com/a/328954.html

相关文章:

  • 学习STM32 脉冲计数实验
  • MySQL相关概念和易错知识点(6)(视图、用户管理)
  • Java 大视界 -- 基于 Java 的大数据可视化在能源互联网全景展示与能源调度决策支持中的应用
  • 深度学习与遥感入门(七)|CNN vs CNN+形态学属性(MP):特征工程到底值不值?
  • 一键自动化:Kickstart无人值守安装指南
  • 【unitrix数间混合计算】2.20 比较计算(cmp.rs)
  • Spring Boot (v3.2.12) + application.yml + jasypt 数据源加密连接设置实例
  • 25个自动化办公脚本合集(覆盖人工智能、数据处理、文档管理、图片处理、文件操作等)
  • 【电气】NPN与PNP
  • [C语言]第二章-从Hello World到头文件
  • 四分位数与箱线图
  • Redis持久化机制详解:RDB与AOF的全面对比与实践指南
  • 动静态库
  • FPGA的PS基础1
  • 【FPGA】初始Verilog HDL
  • c++编程题-笔记
  • kali linux 2025.2安装Matlab的详细教程
  • 通过限制网络访问来降低服务器被攻击风险的方法
  • 服务器如何应对SYN Flood攻击?
  • FluxApi - 使用Spring进行调用Flux接口
  • Gradle(三)创建一个 SpringBoot 项目
  • 深度学习(3):全连接神经网络构建
  • mysql的快照读与当前读的区别
  • 11G RAC数据文件创建到本地如何处理
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day3
  • 《算法导论》第 22 章 - 基本的图算法
  • [AXI5]AXI协议中的Scalar atomic和Vector atomic有什么区别?
  • 【算法】位运算经典例题
  • BM25:概率检索框架下的经典相关性评分算法
  • ADB 无线调试连接(Windows + WSL 环境)