基于Vue+wangeditor实现富文本编辑

这篇具有很好参考价值的文章主要介绍了基于Vue+wangeditor实现富文本编辑。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基于Vue+wangeditor实现富文本编辑,单应用多租户SaaS平台实践,Vue,vue.js,前端,javascript,编辑器


前言

一个网站需要富文本编辑器功能的原因有很多,以下是一些常见的原因:

  • 方便用户编辑内容:富文本编辑器提供了类似于Office Word的编辑功能,使得那些不太懂HTML的用户也能够方便地编辑网站内容。
  • 提高用户体验:富文本编辑器注重用户体验,具有轻量、可定制等特点,使得用户能够更加方便地编辑和发布内容。
  • 增加网站交互性:富文本编辑器可以让用户在网站上进行实时编辑和协同编辑,增加了网站的交互性和社交性。
  • 提高网站SEO:富文本编辑器可以让用户更加方便地添加关键词、标签等元素,从而提高网站的SEO效果。
  • 提高网站可维护性:富文本编辑器可以让网站管理员更加方便地对网站内容进行维护和更新,从而提高网站的可维护性。

综上所述,富文本编辑器是一个非常重要的网站功能,它可以提高用户体验、增加网站交互性、提高网站SEO、提高网站可维护性等。

分析

以下是几款Vue网站主流的富文本编辑器:

  • wangEditor:wangEditor是一款国产的富文本编辑器,开源免费,支持Vue、React等框架。
  • TinyMCE:TinyMCE是一款功能丰富的富文本编辑器,支持Vue、React等框架。
  • Quill:Quill是一款易于扩展、轻量级的富文本编辑器,支持Vue、React等框架。
  • CKEditor 5:CKEditor 5是一款开源免费可商用的富文本编辑器,支持Vue、React等框架。
  • tiptap:tiptap是一款支持多人在线实时协同编辑的富文本编辑器,支持Vue、React等框架。

这里我们采用了wangEditor实现富文本编辑,并且在插件的基础上做了一些适配优化。

实现

具体解决的问题有

  1. 弹出层遮挡调整(可以使操作栏中下拉展示信息(通过设置属性值toolbarDirection设置为true)向上展示)
  2. 其他解决方案都是采用官方提供的,我只是进行了搬砖

具体代码实现如下

<script lang="ts" setup>
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import {onBeforeUnmount, ref, shallowRef, watch} from 'vue'
import {i18nChangeLanguage, IDomEditor, IEditorConfig, SlateElement} from '@wangeditor/editor'
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
import {useI18n} from 'vue-i18n'
import {store} from '@/vuex/store'
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
import {resData} from '@/entity/res'
import {ElMessage} from 'element-plus'

const {t} = useI18n()

const props = defineProps({
  // 占位信息
  placeholder: {
    type: String,
    required: false,
    default: () => ''
  },
  // 高度
  editorHeight: {
    type: Number,
    required: false,
    default: () => 500
  },
  // 文本
  content: {
    type: String,
    required: false,
    default: () => ''
  },
  // 工具栏方向,默认为下方展示,top为上方展示
  toolbarDirection: {
    type: String,
    required: false,
    default: () => ''
  },
  // 完整版或者十精简版
  mode: {
    type: String,
    required: false,
    default: () => 'default'
  },
  // 是否展示操作栏
  toolbarShow: {
    type: Boolean,
    required: false,
    default: () => true
  }
})

//图片参数
type ImageElement = SlateElement & {
  src: string
  alt: string
  url: string
  href: string
}

type VideoElement = SlateElement & {
  src: string
  poster?: string
}

type InsertFnType = (url: string, alt: string, href: string) => void
type InsertFnType1 = (url: string, poster: string) => void

//解决国际化切换问题
const loading = ref(false)

// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()

// 内容 HTML
const valueHtml = ref(props.content)

//内容变化回调
const emits = defineEmits(['handleChange'])
const handleChange = (editor: IDomEditor) => {
  emits('handleChange', editor.getHtml())
}

//工具栏自定义
const toolbarConfig = {}

// 编辑器自定义
const editorConfig: Partial<IEditorConfig> = {  // TS 语法
  MENU_CONF: {},
  placeholder: props.placeholder
}

//上传图片
editorConfig.MENU_CONF['uploadImage'] = {
  //@ts-ignore
  server: import.meta.env.VITE_UPLOAD_LOCAL_IMAGE_URL,
  fieldName: 'file',
  headers: {},
  // 自定义插入图片
  customInsert(res: resData, insertFn: InsertFnType) {  // TS 语法
    // res 即服务端的返回结果
    if (res.ok) {
      // 从 res 中找到 url alt href ,然后插入图片
      insertFn(res.msg, res.msg, res.msg)
    } else {
      ElMessage.error(res.msg)
    }
  },
  // 上传错误,或者触发 timeout 超时
  onError(file: File, err: any, res: any) {  // TS 语法
    ElMessage.error(err.message)
  },
}
// 插入链接
editorConfig.MENU_CONF['insertLink'] = {
  parseLinkUrl: customParseLinkUrl, // 补全链接
}
// 更新链接
editorConfig.MENU_CONF['editLink'] = {
  parseLinkUrl: customParseLinkUrl, // 补全链接
}
// 插入图片连接
editorConfig.MENU_CONF['insertImage'] = {
  onInsertedImage(imageNode: ImageElement | null) {
    if (imageNode == null) return
    const {src, alt, url, href} = imageNode
    console.log('inserted image', src, alt, url, href)
  },
  parseImageSrc: customParseLinkUrl, // 补全链接
}
// 编辑图片连接
editorConfig.MENU_CONF['editImage'] = {
  onUpdatedImage(imageNode: ImageElement | null) {
    if (imageNode == null) return
    const {src, alt, url} = imageNode
    console.log('updated image', src, alt, url)
	},
	parseImageSrc: customParseLinkUrl, // 补全链接
}
// 编辑图片连接
editorConfig.MENU_CONF['editImage'] = {
  onUpdatedImage(imageNode: ImageElement | null) {
    if (imageNode == null) return
    const {src, alt, url} = imageNode
    console.log('updated image', src, alt, url)
  },
  parseImageSrc: customParseLinkUrl, // 补全链接
}
//新增视频连接
editorConfig.MENU_CONF['insertVideo'] = {
  onInsertedVideo(videoNode: VideoElement | null) {  // TS 语法
    if (videoNode == null) return
    const {src} = videoNode
    console.log('inserted video', src)
  },
  parseVideoSrc: customParseLinkUrl, // 也支持 async 函数
}
//上传视频
editorConfig.MENU_CONF['uploadVideo'] = {
  //@ts-ignore
  server: import.meta.env.VITE_UPLOAD_LOCAL__VIDEO_URL,
  fieldName: 'file',
  headers: {},
  // 自定义插入视频
  customInsert(res: resData, insertFn: InsertFnType1) {  // TS 语法
    // res 即服务端的返回结果
    if (res.ok) {
      // 从 res 中找到 url poster ,然后插入视频
      insertFn(res.data.url, res.data.poster)
    } else {
      ElMessage.error(res.msg)
    }
  },
  // 上传错误,或者触发 timeout 超时
  onError(file: File, err: any, res: any) {  // TS 语法
    ElMessage.error(err.message)
  },
}

//兼容国际化
i18nChangeLanguage(store.state.internationalization === 'zhCn' ? 'zh-CN' : 'en')
//监听国际化变化做出切换动作
watch(() => store.state.internationalization, () => {
  loading.value = true
  setTimeout(() => {
    loading.value = false
    editorConfig.placeholder = props.placeholder
    i18nChangeLanguage(store.state.internationalization === 'zhCn' ? 'zh-CN' : 'en')
  }, 800)
})

//初始化回调
const handleCreated = (editor: any) => {
  editorRef.value = editor // 记录 editor 实例,重要!
}

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value
  if (editor === null) return
  editor.destroy()
})

// 自定义转换链接 url
function customParseLinkUrl(url: string): string {
  if (url.indexOf('http') === -1) {
    return `https://${url}`
  }
  return url
}

//兼容element-plus表单提交验证边框状态
const validateFlag = ref(true)
//验证是否有内容
const validate = () => {
  const flag = valueHtml.value !== '' && valueHtml.value !== '<p><br></p>'
  setTimeout(() => {
    validateFlag.value = flag
  }, 1)
  return flag
}

//清空内容
const validateClear = () => {
  validateFlag.value = true
  valueHtml.value = ''
}

const setHtml = (val: any) => {
  valueHtml.value = val
}
const setDisable = () => {
  editorRef.value.disable()
}
defineExpose({
  validate,
  validateClear,
  setHtml,
  setDisable
})
</script>
<template>
  <div :class="!validateFlag? `${toolbarDirection} wang-editor-error`:`${toolbarDirection}`" class="ve-wang-editor">
    <Toolbar
        v-show="toolbarShow"
        :default-config="toolbarConfig"
        :editor="editorRef"
        :mode="mode"
        class="wang-editor-tools"
    />
    <Editor
        v-model="valueHtml"
        :default-config="editorConfig"
        :mode="mode"
        :style="{height: `${editorHeight}px`}"
        class="wang-editor-editor"
        @onChange="handleChange"
        @onCreated="handleCreated"
    />
  </div>
</template>
<style lang="less" scoped>
.ve-wang-editor {
  width: 100%;
  padding-top: 1px;
  border: var(--el-border);
  border-radius: 4px;
  z-index: 99;

  .wang-editor-tools {
    border-bottom: var(--el-border)
  }

  .wang-editor-editor {
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    overflow-y: hidden;

    ::v-deep(.w-e-text-placeholder) {
      top: 10px;
      line-height: 34px;
    }
  }

  .w-e-bar-item button {
    border-radius: 4px;
  }

  .w-e-bar {
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
  }
}

.wang-editor-error {
  border: 1px solid var(--el-color-danger);
}

.wang-editor-text-error {
  color: var(--el-color-danger);
  margin-left: 2px;
}

::v-deep(.w-e-modal) {
  position: fixed;
  z-index: 100;
  margin: 15vh calc(50% - 150px);
}


.top:not(.w-e-full-screen-container) {
  :has([data-menu-key=headerSelect]) > ::v-deep(.w-e-select-list) {
    top: -300px;
  }

  :has([data-menu-key=group-more-style]) > ::v-deep(.w-e-bar-item-menus-container) {
    top: -240px;
  }

  :has([data-menu-key=color]) > ::v-deep(.w-e-drop-panel) {
    top: -295px;
  }

  :has([data-menu-key=bgColor]) > ::v-deep(.w-e-drop-panel) {
    top: -295px;
  }

  :has([data-menu-key=fontSize]) > ::v-deep(.w-e-select-list) {
    top: -388px;
  }

  :has([data-menu-key=fontFamily]) > ::v-deep(.w-e-select-list) {
    top: -388px;
  }

  :has([data-menu-key=lineHeight]) > ::v-deep(.w-e-select-list) {
    top: -285px;
  }

  :has([data-menu-key=group-justify]) > ::v-deep(.w-e-bar-item-menus-container) {
    top: -200px;
  }

  :has([data-menu-key=group-indent]) > ::v-deep(.w-e-bar-item-menus-container) {
    top: -120px;
  }

  :has([data-menu-key=emotion]) > ::v-deep(.w-e-drop-panel) {
    top: -420px;
  }

  :has([data-menu-key=group-image]) > ::v-deep(.w-e-bar-item-menus-container) {
    top: -120px;
  }

  :has([data-menu-key=group-video]) > ::v-deep(.w-e-bar-item-menus-container) {
    top: -120px;
  }

  :has([data-menu-key=insertTable]) > ::v-deep(.w-e-drop-panel) {
    top: -232px;
  }
}

</style>

效果图

image.png

总结

富文本编辑器是一种用于在Web应用程序中创建、编辑和格式化文本的工具。以下是关于富文本编辑器的一些总结:

  • 常用的富文本编辑器:常用的富文本编辑器包括wangEditor、TinyMCE、Quill、CKEditor 5和tiptap等。
  • 优点:富文本编辑器可以提高用户体验、增加网站交互性、提高网站SEO、提高网站可维护性等。
  • 缺点:一些富文本编辑器更新不及时,可能存在一些安全问题。
  • 应用场景:富文本编辑器广泛应用于博客、论坛、电商、在线教育等Web应用程序中。

综上所述,富文本编辑器是一种非常有用的Web工具,它可以提高用户体验、增加网站交互性、提高网站SEO、提高网站可维护性等。常用的富文本编辑器包括wangEditor、TinyMCE、Quill、CKEditor 5和tiptap等。文章来源地址https://www.toymoban.com/news/detail-629218.html


到了这里,关于基于Vue+wangeditor实现富文本编辑的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue3项目使用富文本编辑器-wangeditor

    1.下载依赖 2.插件版本  3.使用 引入css和组件 配置方法 模板(标签)中插入 效果  

    2024年02月09日
    浏览(22)
  • vue3 wangeditor/editor富文本使用和编辑

    第一步:安装 第二步:使用 最终效果图: 第四:工具栏配置 进入官方demo:https://www.wangeditor.com/demo/index.html 打开之后,按F12在控制台输入 toolbar.getConfig()  查看工具栏的默认配置。  如果有不需要的工具栏项,可以在  toolbarConfig.excludeKeys 中配置

    2024年01月21日
    浏览(22)
  • vue使用富文本编辑器 Wangeditor 可显示编辑新增回显禁用

    npm install wangeditor import editorBar from \\\"@/components/ editor/ editor.vue\\\"; Vue.component(\\\'editorBar\\\', editorBar)  editor-bar v-model=\\\"form.nr\\\" :flag=\\\"false\\\" @change=\\\"getcontent\\\" / mothods:{      //获取富文本内容     getcontent (content) {        this.form.nr = content;     }, } editor-bar v-model=\\\"form.nr\\\" :flag=\\\"false\\\" @change=\\\"getc

    2024年02月13日
    浏览(48)
  • uniapp富文本编辑-editor-vue2-vue3-wangeditor

    不管vue2还是vue3,都推荐官方的editor组件, 官方手册 https://uniapp.dcloud.net.cn/component/editor.html 除了“ 微信小程序 ”,其他小程序想要使用editor组件实现富文本编辑,很难 ​​​​​​​ 第三方组件wangeditor在vue2,vue3在H5表现都很好,但是app环境下直接报错 reportJSException excep

    2024年03月10日
    浏览(32)
  • HTML——实现富文本编辑器wangEditor的使用

    背景:最近在写小说项目,关于发布文章需要用到富文本编辑器,由于还没学到Vue,最实用的还是用wangEditor富文本编辑器。 官方文档:http://www.wangeditor.com/ 使用手册:创建一个编辑器 · wangEditor3使用手册 · 看云 (kancloud.cn) 至于实现的方式有三种: 一.导入wangEditor.JS 使用方法

    2024年02月11日
    浏览(18)
  • Vue3 代码块高亮显示并可使用富文本编辑器编辑(highlight.js + wangEditor)

    在Vue项目中实现以下功能:   功能1. 在页面中显示代码,并将其中的高亮显示。   功能2. 允许对代码块进行编辑,编辑时代码也高亮显示。   功能3. 可在编辑器中添加多个代码块,动态渲染代码高亮。   Step1: 安装所需插件(本文使用npm安装,若需

    2023年04月21日
    浏览(20)
  • vue2+wangEditor5富文本编辑器(图片视频上传)并加锚链接

    官网:https://www.wangeditor.com/v5/installation.html#npm 1、安装使用 安装 在main.js中引入样式 在使用编辑器的页面引入js 模板 js 到这一步编辑完就可以正常显示了 2、上传图片、视频 1)上传到后台接口的可直接按照文档这个配置就行接口返回格式也要可文档上一致 2)自定义上传(一

    2024年02月12日
    浏览(15)
  • vue2+wangEditor5富文本编辑器(图片视频自定义上传七牛云/服务器)

    1、安装使用 安装 在main.js中引入样式 在使用编辑器的页面引入js 模板 js  到这一步编辑器就可以正常显示了 2、上传图片、视频 上传到后台接口的可直接按照文档这个配置就行接口返回格式也要可文档上一致    2)自定义上传(一般上传到别的服务器上,我这边是上传到七

    2024年02月11日
    浏览(17)
  • Vue组件封装:基于Vue3+wangeditor富文本组件二次封装

    1.简介         开源 Web 富文本编辑器,开箱即用,配置简单。一个产品的价值,就在于解决用户的问题,提高效率、降低成本、增加稳定性和扩展性。wangEditor 不是为了做而做,也不是单纯的模仿谁,而是经过上述问题分析之后,给出一个系统的解决方案。旨在真正去解决用

    2024年04月08日
    浏览(13)
  • wangEditor富文本编辑器图片/视频上传

    wangEditor 有丰富的 API 和足够的扩展性,允许我们自定义开发菜单、模块、插件等。在 Vue、React 中运用也很方便。因此本文介绍下vue中富文本上传图片和视频。 安装引入后富文本有显示上传图片按钮,点击上传后会报 没有配置上传地址 的错误,如下图所示: 所以自定义上传

    2024年02月15日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包