vue3 wangeditor/editor富文本使用和编辑

该教程介绍了如何在Vue项目中安装并使用wangEditor富文本编辑器,包括编辑器和工具栏的安装,内容的绑定,以及自定义图片和视频上传功能的实现。同时,还展示了如何配置和排除不需要的工具栏项。

第一步:安装

yarn add @wangeditor/editor
# 或者 npm install @wangeditor/editor --save


yarn add @wangeditor/editor-for-vue@next
# 或者 npm install @wangeditor/editor-for-vue@next --save

第二步:使用

// html

<div class="editor-box">
    <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef":defaultConfig="toolbarConfig" mode="default" />
    <Editor
        style="height: 440px; overflow-y: hidden"
        v-model="valueHtml"
        :defaultConfig="editorConfig"
        mode="default"
        @onCreated="handleCreated"
    />
</div>
<script setup>
import { getCurrentInstance, ref, onMounted, shallowRef, reactive, onBeforeUnmount } from "vue"
import "@wangeditor/editor/dist/css/style.css"
import { Editor, Toolbar } from "@wangeditor/editor-for-vue"

const { proxy } = getCurrentInstance()

/**
 * 富文本编辑器
 * @param 
<think>我们已经在之前的回答中详细介绍了如何在Vue3使用@wangeditor/editor,包括安装、组件创建、图片上传等。现在根据用户提供的引用内容,用户可能已经在项目中使用了@pureadmin/utils工具库,并且希望将富文本编辑器封装为全局组件。下面将结合用户提供的引用信息,给出更符合实际项目的实现方案。 ### 步骤1:安装依赖 根据引用内容,需要安装以下依赖: ```bash npm install @wangeditor/editor @wangeditor/editor-for-vue @pureadmin/utils # 或者 yarn add @wangeditor/editor @wangeditor/editor-for-vue @pureadmin/utils ``` ### 步骤2:创建编辑器组件 在`src/components/ReEditor/src/Editor.vue`中创建组件(注意路径与引用一致): ```vue <template> <div style="border: 1px solid #ccc"> <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" /> <Editor style="height: 500px; overflow-y: hidden" v-model="valueHtml" :defaultConfig="editorConfig" @onCreated="handleCreated" /> </div> </template> <script setup lang="ts"> import { ref, shallowRef, onBeforeUnmount } from &#39;vue&#39; import { Editor, Toolbar } from &#39;@wangeditor/editor-for-vue&#39; import &#39;@wangeditor/editor/dist/css/style.css&#39; const props = defineProps({ modelValue: { type: String, default: &#39;&#39; } }) const emit = defineEmits([&#39;update:modelValue&#39;]) // 编辑器实例,必须用 shallowRef const editorRef = shallowRef() // 内容 HTML,使用计算属性实现双向绑定 const valueHtml = ref(props.modelValue) // 工具栏配置 const toolbarConfig = { excludeKeys: [&#39;uploadImage&#39;] } // 排除上传图片菜单 // 编辑器配置 const editorConfig = { placeholder: &#39;请输入内容...&#39;, MENU_CONF: { // 配置上传图片(根据实际API调整) uploadImage: { server: &#39;/api/upload&#39;, fieldName: &#39;image&#39; } } } // 组件销毁时,及时销毁编辑器 onBeforeUnmount(() => { const editor = editorRef.value if (editor == null) return editor.destroy() }) const handleCreated = (editor: any) => { editorRef.value = editor // 记录 editor 实例 } // 监听内容变化,更新父组件绑定值 watch(valueHtml, (newVal) => { emit(&#39;update:modelValue&#39;, newVal) }) </script> ``` ### 步骤3:全局注册组件(使用@pureadmin/utils) 在`src/components/ReEditor/index.ts`中: ```typescript import editor from "./src/Editor.vue" import { withInstall } from "@pureadmin/utils" /** 编辑器组件 */ export const RwEditor = withInstall(editor) export default RwEditor ``` ### 步骤4:在main.ts中全局注册(可选) 如果需要在全局使用,可以在`main.ts`中: ```typescript import { createApp } from &#39;vue&#39; import App from &#39;./App.vue&#39; import RwEditor from &#39;@/components/ReEditor&#39; const app = createApp(App) app.component(&#39;RwEditor&#39;, RwEditor) app.mount(&#39;#app&#39;) ``` ### 步骤5:在父组件中使用 ```vue <template> <div> <h1>文章编辑器</h1> <RwEditor v-model="html" /> <button @click="submit">提交</button> </div> </template> <script setup lang="ts"> import { ref } from &#39;vue&#39; const html = ref(&#39;&#39;) const submit = () => { console.log(&#39;提交内容:&#39;, html.value) } </script> ``` ### 图片上传自定义(根据实际API调整) ```javascript // 在editorConfig的uploadImage中配置 uploadImage: { server: &#39;http://your-api-address/upload&#39;, fieldName: &#39;file&#39;, // 自定义上传逻辑 async customUpload(file: File, insertFn: (url: string) => void) { const formData = new FormData() formData.append(&#39;file&#39;, file) try { const { data } = await axios.post(&#39;http://your-api-address/upload&#39;, formData) if (data.code === 0) { insertFn(data.url) } else { console.error(&#39;上传失败&#39;, data.message) } } catch (error) { console.error(&#39;上传失败&#39;, error) } } } ``` ### 注意事项 1. **样式引入**:确保在组件中引入`@wangeditor/editor/dist/css/style.css` 2. **双向绑定**:通过`v-model`实现父子组件数据同步 3. **销毁实例**:在组件销毁前调用`editor.destroy()`防止内存泄漏 4. **TypeScript支持**:wangEditor提供了完整的类型定义,无需额外安装 ### 常见问题解决 - **无法显示工具栏**:检查是否同时引入了`Toolbar``Editor`组件 - **图片上传失败**:检查跨域问题及后端接口格式 - **内容绑定失效**:确保使用`watch`监听变化并触发`update:modelValue`事件 ### 与表单验证集成 ```vue <template> <el-form :model="form" :rules="rules"> <el-form-item label="内容" prop="content"> <RwEditor v-model="form.content" /> </el-form-item> </el-form> </template> <script setup lang="ts"> const form = ref({ content: &#39;&#39; }) const rules = ref({ content: [ { validator: (_, value, callback) => { // 过滤空内容(wangEditor默认空内容是&#39;<p><br></p>&#39;) if (!value || value === &#39;<p><br></p>&#39;) { callback(new Error(&#39;内容不能为空&#39;)) } else { callback() } }, trigger: &#39;blur&#39; } ] }) </script> ``` [^1]: wangEditor官方文档提供了详细的Vue3集成示例 [^2]: @pureadmin/utils的`withInstall`方法可以简化全局组件注册</think>下面我将详细介绍在 Vue3 项目中集成 `@wangeditor/editor` 富文本编辑器的完整步骤,包括组件封装、图片上传、表单验证等实用功能。 ### 一、安装依赖 ```bash npm install @wangeditor/editor @wangeditor/editor-for-vue # 或 yarn add @wangeditor/editor @wangeditor/editor-for-vue ``` ### 二、创建可复用的编辑器组件 `src/components/RwEditor/src/Editor.vue`: ```vue <template> <div class="editor-wrapper" :style="{ border: error ? &#39;1px solid #f56c6c&#39; : &#39;1px solid #dcdfe6&#39; }"> <Toolbar class="editor-toolbar" :editor="editorRef" :defaultConfig="toolbarConfig" /> <Editor class="editor-content" v-model="valueHtml" :defaultConfig="editorConfig" @onCreated="handleCreated" @onChange="handleChange" /> </div> <div v-if="error" class="el-form-item__error">{{ error }}</div> </template> <script setup lang="ts"> import { ref, shallowRef, watch, onBeforeUnmount } from &#39;vue&#39; import { Editor, Toolbar } from &#39;@wangeditor/editor-for-vue&#39; import &#39;@wangeditor/editor/dist/css/style.css&#39; import type { IDomEditor } from &#39;@wangeditor/editor&#39; const props = defineProps({ modelValue: { type: String, default: &#39;&#39; }, placeholder: { type: String, default: &#39;请输入内容...&#39; }, error: String }) const emit = defineEmits([&#39;update:modelValue&#39;, &#39;change&#39;]) // 编辑器实例 const editorRef = shallowRef<IDomEditor | null>(null) // 内容 HTML const valueHtml = ref(props.modelValue) // 工具栏配置 const toolbarConfig = { excludeKeys: [ &#39;uploadVideo&#39;, // 排除上传视频 &#39;fullScreen&#39; // 排除全屏 ] } // 编辑器配置 const editorConfig = ref({ placeholder: props.placeholder, autoFocus: false, MENU_CONF: { uploadImage: { server: &#39;/api/upload/image&#39;, // 图片上传接口 fieldName: &#39;file&#39;, // 表单字段名 maxFileSize: 10 * 1024 * 1024, // 10MB allowedFileTypes: [&#39;image/*&#39;], customInsert(res: any, insertFn: any) { // 处理上传响应 if (res.code === 0) { insertFn(res.data.url) } else { console.error(&#39;图片上传失败&#39;, res.msg) } } } } }) // 创建编辑器实例 const handleCreated = (editor: IDomEditor) => { editorRef.value = editor } // 内容变化处理 const handleChange = (editor: IDomEditor) => { emit(&#39;update:modelValue&#39;, editor.getHtml()) emit(&#39;change&#39;, editor.getHtml()) } // 监听外部值变化 watch(() => props.modelValue, (newVal) => { if (newVal !== valueHtml.value) { valueHtml.value = newVal } }) // 组件销毁时销毁编辑器 onBeforeUnmount(() => { if (editorRef.value) { editorRef.value.destroy() editorRef.value = null } }) </script> <style scoped> .editor-wrapper { border-radius: 4px; overflow: hidden; } .editor-toolbar { border-bottom: 1px solid #eee; } .editor-content { min-height: 300px; overflow-y: auto; } </style> ``` ### 三、全局注册组件 `src/components/RwEditor/index.ts`: ```typescript import editor from "./src/Editor.vue" import { withInstall } from "@pureadmin/utils" /** 富文本编辑器组件 */ export const RwEditor = withInstall(editor) export default RwEditor ``` ### 四、在父组件中使用 ```vue <template> <div class="container"> <el-form :model="form" :rules="rules" ref="formRef" label-width="80px"> <el-form-item label="标题" prop="title"> <el-input v-model="form.title" placeholder="请输入标题" /> </el-form-item> <el-form-item label="内容" prop="content" :error="contentError"> <RwEditor v-model="form.content" :placeholder="请输入文章内容" :error="contentError" /> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm">提交</el-button> <el-button @click="resetForm">重置</el-button> </el-form-item> </el-form> </div> </template> <script setup lang="ts"> import { ref, reactive } from &#39;vue&#39; import RwEditor from &#39;@/components/RwEditor&#39; import type { FormInstance } from &#39;element-plus&#39; const formRef = ref<FormInstance>() const contentError = ref(&#39;&#39;) const form = reactive({ title: &#39;&#39;, content: &#39;&#39; }) // 验证规则 const rules = reactive({ title: [ { required: true, message: &#39;标题不能为空&#39;, trigger: &#39;blur&#39; }, { min: 5, max: 50, message: &#39;长度在 5 到 50 个字符&#39;, trigger: &#39;blur&#39; } ], content: [ { validator: (_, value, callback) => { if (!value || value === &#39;<p><br></p>&#39;) { callback(new Error(&#39;内容不能为空&#39;)) } else if (value.length > 10000) { callback(new Error(&#39;内容长度不能超过10000字符&#39;)) } else { callback() } }, trigger: &#39;blur&#39; } ] }) // 提交表单 const submitForm = async () => { try { await formRef.value?.validate() // 实际提交逻辑 console.log(&#39;提交数据:&#39;, { title: form.title, content: form.content }) // 提交到API // await axios.post(&#39;/api/article&#39;, form) ElMessage.success(&#39;提交成功&#39;) } catch (error) { // 处理内容错误 if (error?.fields?.content) { contentError.value = error.fields.content[0].message } ElMessage.error(&#39;表单验证失败&#39;) } } // 重置表单 const resetForm = () => { formRef.value?.resetFields() contentError.value = &#39;&#39; } </script> ``` ### 五、图片上传处理(后端示例) ```typescript // 服务端上传处理 (Node.js示例) import express from &#39;express&#39; import multer from &#39;multer&#39; import path from &#39;path&#39; const app = express() const upload = multer({ dest: &#39;uploads/&#39; }) app.post(&#39;/api/upload/image&#39;, upload.single(&#39;file&#39;), (req, res) => { if (!req.file) { return res.status(400).json({ code: 400, msg: &#39;未上传文件&#39; }) } // 实际项目中应处理文件重命名、云存储等 const fileUrl = `https://your-domain.com/uploads/${req.file.filename}` res.json({ code: 0, msg: &#39;上传成功&#39;, data: { url: fileUrl, alt: req.file.originalname, href: fileUrl } }) }) ``` ### 六、高级功能扩展 #### 1. 自定义工具栏按钮 ```javascript // 在编辑器配置中添加 toolbarConfig: { insertKeys: { index: 5, // 插入位置 keys: [&#39;uploadImage&#39;, &#39;insertVideo&#39;] // 自定义按钮 } } ``` #### 2. XSS 防护 ```javascript // 安装 xss 库 npm install xss // 在提交前过滤 import xss from &#39;xss&#39; const safeHtml = xss(form.content, { whiteList: { a: [&#39;href&#39;, &#39;title&#39;, &#39;target&#39;], img: [&#39;src&#39;, &#39;alt&#39;], // 其他允许的标签属性 } }) ``` #### 3. 内容字数统计 ```vue <!-- 在编辑器组件中添加 --> <div class="editor-footer"> 字数: {{ wordCount }} | 图片: {{ imageCount }} </div> <script> // 在组件中添加 const wordCount = ref(0) const imageCount = ref(0) watch(valueHtml, (html) => { // 计算纯文字字数 const text = editorRef.value?.getText() || &#39;&#39; wordCount.value = text.trim().length // 计算图片数量 imageCount.value = (html.match(/<img/g) || []).length }) </script> ``` ### 七、常见问题解决 1. **样式冲突问题**: ```css /* 在全局样式中添加 */ .w-e-toolbar { z-index: 2 !important; } .w-e-menu { padding: 0 5px; } ``` 2. **TypeScript 类型支持**: ```bash npm install @types/wangeditor__editor -D ``` 3. **内容初始化问题**: ```javascript // 使用 nextTick 确保编辑器初始化完成 import { nextTick } from &#39;vue&#39; nextTick(() => { if (editorRef.value) { editorRef.value.setHtml(props.modelValue) } }) ``` 4. **只读模式切换**: ```javascript const toggleReadonly = () => { if (editorRef.value) { editorRef.value.disable() // 启用:editorRef.value.enable() } } ``` ### 八、性能优化建议 1. 使用 `shallowRef` 代替 `ref` 存储编辑器实例 2. 大文档使用虚拟滚动(需自定义实现) 3. 避免在 `onChange` 中执行重操作 4. 使用防抖处理自动保存: ```javascript import { debounce } from &#39;lodash-es&#39; const autoSave = debounce((content) => { localStorage.setItem(&#39;draft&#39;, content) }, 1000) watch(() => form.content, autoSave) ``` [^1]: wangEditor 官方文档提供了详细的 Vue3 集成示例 [^2]: @pureadmin/utils 的 `withInstall` 方法简化了全局组件注册 [^3]: Element Plus 表单验证需要特殊处理富文本内容
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余温无痕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值